[{"categories":["technology"],"content":"曾老师日常工作中，常常混用多个 AI 工具和大模型，哪个好用就用哪个。\n这样不但方便了解每个模型和工具的特点，也容易找到最出活、效率最高的那一个。\n这次这个项目，就是用多个模型解决一个相对具体的任务。这样的生产项目测试，远比写一个 demo 进行测试要靠谱。如果文章对你有帮助，欢迎关注点赞转发三联。\n任务定义 在海外漫剧制作的过程中，经常需要翻译多种语言。正常情况下，使用剪映或者 Premiere 重新输出另一种语言的字幕视频，效率是非常低下的（因为需要人类参与）。\n更高效的方式是这样：\n抽离英文字幕。 抽离人声。 英文字幕翻译成多种语言（对齐时间轴）。 人声翻译成多种语言（对齐时间轴）。 封面翻译成多种语言。 将多种语言的字幕、人声、封面首帧、无人声无字幕的视频，重新烧录成一个单语言版本。 曾老师使用下面的几个模型完成了这个任务：\nCodex 5.5 DeepSeek V4 Gemini 3 使用的 Agent：\nClaude Code Codex Gemini CLI 其中 DeepSeek V4 是通过 Claude Code 调用的，走的是 DeepSeek 官方 API 服务。\n任务分解 曾老师把这个任务沉淀成三个 skill：\n漫剧字幕翻译 manju-srt 这个 skill 负责将剪映导出的英文 SRT 格式字幕翻译成下面三种语言：\n日文 印尼语 巴西葡萄牙语 SKILL.md（部分）\n1--- 2name: manju-srt 3description: | 4 多语种英文 SRT 翻译与字幕可读性优化专家。直接读取英文 SRT 文件，只以英文 SRT 为输入，不依赖外部文本；保持原 SRT 的字幕序号、时间轴和条目数量，在同一字幕条目内优化换行与长度，同步翻译并输出巴西葡萄牙语、日语和印尼语三种语言的 SRT 文件。 5 当用户提到\u0026#34;字幕翻译\u0026#34;、\u0026#34;SRT 翻译\u0026#34;、\u0026#34;英文 SRT 翻译\u0026#34;、\u0026#34;多语种字幕\u0026#34;、\u0026#34;漫剧字幕\u0026#34;、\u0026#34;巴西葡萄牙语字幕\u0026#34;、\u0026#34;日语字幕\u0026#34;、\u0026#34;印尼语字幕\u0026#34;时使用此技能。 6--- 7 8# Manju SRT - 多语种英文 SRT 翻译与可读性优化专家 9 10你是一个专业的视频字幕工程师与多语种本地化翻译专家。 11 12## 核心能力 13 14- 直接读取英文 SRT 文件，将字幕文本翻译为巴西葡萄牙语、日语和印尼语 15- 保持原英文 SRT 的字幕序号、时间轴和条目数量完全一致 16- 在同一个字幕条目内部优化译文长度、自然换行和阅读节奏 17- 不读取、不匹配、不引用任何外部文本来源 18- 不重构英文字幕，只翻译原文件已有内容，不新增、删除、合并或拆分字幕条目 19 20## 严格遵守的核心规则 21 22### 1. 源文本规则 23 24- 英文 SRT 是唯一源文本。所有译文都必须从英文 SRT 的字幕文本直接翻译。 25- 不使用其它辅助文本来改写源字幕。 26- 不主动修复英文源字幕的拼写、大小写、标点或断句；如果源字幕明显有识别错误，只在译文中按可判断的语义自然表达，不能确定时按原文保守翻译。 27 28### 2. 结构与时间轴规则 29 30- 每个输出语言文件必须与英文 SRT 保持相同的字幕序号、时间轴和条目数量。 31- 时间轴行必须逐字复制，格式保持为 `00:00:01,000 --\u0026gt; 00:00:05,500`。 32- 不合并字幕条目，不拆分字幕条目，不重新计算时间轴，不为疑似缺失音频补时间。 33- 如果源 SRT 时间轴明显异常，只在完成报告中提示风险；除非用户明确要求修复，否则不要自动改时间轴。 34- 只翻译字幕文本行；序号行、时间轴行和空行结构保持标准 SRT 格式。 35- 允许在同一个字幕条目内部调整译文换行，但不能因此新增字幕序号或改变时间轴。 36 37### 3. 翻译规则 38 39- 输出三种语言： 40 - 巴西葡萄牙语：`PT-BR` 41 - 日语：`JA` 42 - 印尼语：`ID` 43- 翻译要符合目标语言的自然口语习惯，优先保证观众能快速读懂。 44- 人名、地名、品牌名和专有名词在没有明确通行译名时保留英文。 45- 同一字幕条目内有多行英文时，将这些行作为一个语义单元理解，再输出适合目标语言阅读的字幕文本。 46- 保留必要的字幕格式标记，例如 `\u0026lt;i\u0026gt;...\u0026lt;/i\u0026gt;`、`{\\an8}`、音乐符号或说话人标识；只翻译其中可见字幕文本。 47- 避免把解释、注释、括号说明或翻译备注写进 SRT。 48 49### 4. 字幕长度与换行控制 50 51- PT-BR 和 ID 的单行译文尽量不超过 60 个字符；超过时优先在逗号、句号、问号、感叹号、连接词或自然语义停顿处换行。 52- 每个字幕条目通常最多两行。只有在源条目本身极长且无法自然压缩时，才允许保留较长译文，但仍不得拆成新的 SRT 条目。 53- JA 译文优先保持短句和自然语气，避免冗长说明；需要换行时，在助词、标点或自然语义停顿处断开。 54- 如果译文过长，优先通过自然本地化压缩表达，而不是机械逐词直译。 55- 长度控制只作用于译文文本和同条目内换行，不得改变字幕序号、条目数量或时间轴。 56 57### 5. 质量检查规则 58 59生成每个语言的 SRT 后，必须检查： 60 61- 字幕条目数量是否与英文源 SRT 完全一致 62- 每条字幕序号是否连续且与源文件一致 63- 每条时间轴是否与源文件完全一致 64- PT-BR 和 ID 是否尽量满足单行 60 字符限制，超长处是否已在同条目内自然换行 65- 是否存在未翻译的英文整句 66- 是否存在多余说明、Markdown、代码块围栏或非 SRT 内容 67 68质量检查必须分为两层执行： 69 701. **翻译质量自审**：逐条复核译文是否有错译、漏译、时态误判、宾语错位、角色称号不一致、直译腔、不自然表达、过长影响阅读等问题。发现问题必须先修改译文，再进入输出步骤。 712. **机器结构校验**：用脚本或等价逻辑检查 SRT 是否为标准格式。发现可机械修复的问题必须自动修复；无法可靠修复时必须停止上传并在报告中列出。 72 73翻译质量自审必须覆盖以下高风险项： 74 75- 不把英文假设句误译为已发生事件，例如 `We do ... and ...` 不能误译成“已经做了”。 76- `I got you`、`let go`、`shoot me up there`、`strip mine`、`supercharge`、`bond`、`guardian` 等短句/术语必须按上下文翻译，不能机械直译。 77- PT-BR 避免 `Me dê ela`、`Uma mais rachadura`、`Quebrar!`、`nos vinculamos` 等不自然或语法错误表达。 78- JA 避免 `もっと最悪`、`ひどく欲しがる`、`残り物`、`今週期`、`破滅させる` 等不自然表达；不得在对白字幕中添加 `絆（きずな）` 这类假名括注；不得混入中文简体字，例如 `强`。 79- ID 避免 `Sangat terlalu`、`Apa itu sakit`、`menahan ia`、`Tembakkan aku`、`Aku adalah apa yang tersisa` 等直译腔或语法不自然表达。 80 81机器结构校验必须覆盖以下问题： 82 83- 文件中不得出现 multipart/form-data 包装内容，例如 `--xxxx`、`Content-Disposition`、`Content-Type`。 84- 时间码必须严格为 `HH:MM:SS,mmm --\u0026gt; HH:MM:SS,mmm`，不得出现 `00:16,933`、`01:00,216` 这类缺小时字段的时间码。 85- 不得有无法解析的字幕块、Markdown、代码块围栏、上传日志、完成提示或其它非 SRT 内容。 86- 条目数量必须与英文源 SRT 完全一致；不得缺条、重复条、合并条或新增条。 87- 序号和时间轴必须按英文源 SRT 的条目顺序逐条复制。即使英文源本身存在段落顺序与时间顺序不一致，也不能自动排序目标语言文件；目标语言必须保持源文件的原始结构。 88- 每条字幕结束时间必须大于开始时间。若目标文件出现负时长或明显误写时间码，优先用英文源对应条目的时间轴覆盖目标时间轴。 89 90可自动修复的格式问题及处理方式： 91 92- `multipart/form-data` 包装：删除所有边界行和表单头，只保留标准 SRT 字幕块。 93- 缺小时字段的时间码：如果能确定是 `MM:SS,mmm`，补成 `00:MM:SS,mmm`；最终仍以英文源对应条目的完整时间轴为准。 94- 序号、时间轴不一致：只有在可以确认目标译文条目顺序没有改变时，才保留译文文本并按英文源逐条重写序号和时间轴，例如缺小时字段、单条时间码笔误、负时长等机械错误。 95- 时间码集合相同但顺序不同：这通常说明目标文件被按时间排序过，不能直接重写时间轴，否则可能造成译文和画面错配；必须重新从英文源生成该目标文件，或在报告中列为未解决问题。 96- 连续重复条目：如果目标文件比英文源多 1 条，且存在连续两条时间轴和文本完全相同的字幕，删除重复条并重排序号。 97- 条目缺失、合并或无法判断对应关系：不要猜测补译；必须重新从英文源生成该目标文件，或在报告中列为未解决问题。 98 99## 工作流说明 100 101（更多内容略过……）需要详细内容可进入胡扯漫剧群获取。 还记得做游戏出海那会儿，我们有一款游戏，翻译一门语言需要上万元人民币。每个版本都要更新翻译，而且翻译得特别烂。\n德语区的玩家发邮件要求免费帮我们翻译，甚至要求我们允许德语手机系统显示英文界面，因为我们翻译的德语，连德国人都看不懂……\n而现在，只需要一个 skill……\n漫剧字幕 + 首帧烧录 manju-burn （下篇介绍）\nfilebrowser 这个 skill 是可选的。为了方便文件共享，制作过程中所有中间文件都会上传到局域网服务器上的 filebrowser 文件服务器，最终生成的文件也会自动上传到这里存储。\n本文会略过这个 skill 的介绍，但曾老师十分推荐这个项目：\nhttps://github.com/gtsteffaniak/filebrowser\n三大模型 + 工具大斗法 DeepSeek V4 的翻译效果如何？ 翻译工作是使用 DeepSeek V4，在 Claude Code 中调用 manju-srt skill 实现的。在曾老师明确提出 subagent 使用要求时，DeepSeek V4 Pro 能正确创建并行任务进行翻译，翻译完成后可以自动汇报。\nGemini 3 检查翻译质量 为了确认 DeepSeek 对字幕的翻译质量，我启用 Gemini CLI，使用 Gemini 3 模型进行检查。\n总的来说，Gemini 老师对于 D 老师的翻译质量评价还是颇高的。\n由于 Gemini 见多识广（Google 是全球公司，训练语料库应该更丰富），因此用 Gemini 作为翻译的检查者应该是相对合适的选择。\n然而，检查过程中，Gemini 也出现了一些错误。例如将时间轴与编号顺序的错位认定为严重错误。而实际上 SRT 是允许时间轴不遵循编号顺序出现的。\nCodex 5.5 检查格式错误 在使用 Gemini 修改了翻译语气问题后，曾老师让 Codex 5.5 再次做了检查。不查不知道，Codex 真是夯爆了！\n由于主要功能已经使用 skill 部署，提示词就变得非常简单：\n使用 filebrowser 技能，获取 /虎澈漫剧/C01/字幕/英文SRT/ 以及同级目录下的另外 3 种语言，检测翻译质量和时间轴的正确程度，给我一个报告。\n注意使用 subagent 并行处理，避免上下文腐烂。\nCodex 启动了 3 个 subagent 进行抽查（一门语言一个，不得不说这个选择非常聪明）。给出的报告更加结构化，把格式问题和翻译问题分开来报告，让人一目了然。\n而且，Codex 明确说清楚了段落错位的问题，没有像 Gemini 那样用「严重错误」来进行语焉不详的说明。\n经过比对，我发现原始的英文 SRT 也有相同的段落错位，这说明从剪映导出 SRT 的时候，就存在错位。错位的 SRT 在字幕编辑器中打开也正常，说明这是较为通用的现象。\n确认完段落错位之后，Codex 又分别解释了格式错误问题和时间码缺失问题。\n最后问题解决，也是干净利落：\n按照你提供的表格，首先解决翻译质量问题\n解决完毕后，再尝试解决你提到的格式问题，并保证修复后的文件是正常的\n如果你解决不了，记录下来最后给我报告。\n费用 曾老师的 ChatGPT 是 Plus 订阅，但高频使用时 Codex 额度还是不够用。达到 5 小时使用额度后，曾老师也会停下来换模型。\nGemini 是 Pro 订阅，基本上用不完。\n最让我感动的是 DeepSeek V4，不但速度贼快、不需要魔法，还巨便宜。翻译 3 种语言花了不到人民币 2 块钱，和之前翻译出海游戏找翻译公司花的钱比起来，降了 1 万倍以上……真的是九牛一毛了。\n有人问，为什么没有 Claude？因为被封麻了……\n","date":"2026-05-01","description":"","lastmod":"2026-05-03T04:52:57Z","slug":"manju-auto-subtitle-translation-skill","tags":["ai","ai-skill","ai-agent"],"title":"漫剧全自动字幕翻译 Skill：Codex 5.5/DeepSeek V4/Gemini 3 混战","url":"https://blog.zengrong.net/post/manju-auto-subtitle-translation-skill/"},{"categories":["technology"],"content":"经常有群友问我：Coding 过程中，怎么把测试和编码都自动化起来？\n看看你的 Coding 流程，是不是还在用下面这种方式和 AI 沟通：\nCoding 之后，手动调试。 把后端 log 发给 Agent。 在前端控制台里复制 log 内容发给 Agent。 Agent 调整完成后，再重复上面的 1 到 3 步。 这个流程其实挺低效。\n让 Agent 读取后端 log 其实很方便。你只要在 AGENTS.md 或 CLAUDE.md 里把后端 log 的获取方式写清楚，Agent 就会自己去拿。比如下面这样：\n当然，后台管理不是本文重点。本文着重讨论曾老师日常在 Web 前端开发里使用的浏览器自动化工具。有了这些工具，你就能把 Agent 真正接进完整的自动化 Coding 流程里。\n下面按我的使用频率，来介绍这 3 个主力工具，最后再补一个常备的 Playwright。\nAgent Browser 这是曾老师用得最多的 skill。GitHub 上已经有 30k star，主要用 Rust 编写，项目地址：\nhttps://github.com/vercel-labs/agent-browser\n建议安装到全局（macOS）：\n1brew install agent-browser 2agent-browser install # 下载 Chrome for Testing，只需要执行一次 将 Agent 使用的 skill 安装到全局：\n1npx skills add vercel-labs/agent-browser@agent-browser -g Agent Browser 可以读取 DOM 和控制台信息，在开发过程中模拟开发者执行点击、拖动等操作。\n有了上面的能力，Agent 基本就能代替你去做调试了。只要把通过条件设清楚，理论上它会一直干到功能完成。\nAgent Browser 相对于其它工具最大的优势，就是快。后面我讲其它工具时还会再提这点。\nchrome-devtools-mcp 这是 Google 官方提供的 Chrome MCP 工具，主要用 TypeScript 编写，GitHub 上已经有 36.4k star，项目地址：\nhttps://github.com/ChromeDevTools/chrome-devtools-mcp\n由于这是 Google 官方提供的工具，理论上你可以用它来做任何事。\n但有一些限制让我很不爽：\n必须开一个临时的 profile。 如果退出了某个 Agent session，这个 session 里打开的 Chrome 进程也会被 kill。 默认情况下，只有一个 session 能控制一个 Chrome 进程。 默认情况下，无法连接已经存在的 Chrome 进程。 例如，在 macOS 上，需要先创建一个新的 Chrome 实例，再连接到临时 profile：\n1/Applications/Google\\ Chrome.app/Contents/MacOS/Google\\ Chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-profile-stable 另一个让我挺不爽的问题是：速度太慢了。\n不知道是不是因为用了 MCP 协议。跟 Agent Browser 对比着用的时候，曾老师总觉得 chrome-devtools-mcp 慢了半拍。当然，也可能只是因为 Claude Code 被限速了……\nBrowser Use 早期曾老师挺常用这个工具。它主要用 Python 编写，GitHub 上已经有 88.9k star，项目地址：\nhttps://github.com/browser-use/browser-use\n建议安装到全局环境中（uv 是 Python 的现代包管理器）：\n1uv tool add browser-use 2browser-use install # 安装 Chromium 浏览器（只需一次） 安装 Agent 使用的 skill 到全局：\n1npx skills add browser-use/browser-use@browser-use -g 和 Agent Browser 类似，Browser Use 本身也是个独立的工具，可以在命令行中独立运行。\n你可以借助大模型的力量，让 Browser Use 帮你做各种事……当然，也可以在 Claude Code、Codex 等任意 Agent 里使用。\n在运行过程中，Browser Use 会贴心地把浏览器 DOM 里的内容标注清楚，方便你在和 Agent 对话时描述位置。（虽然曾老师觉得这件事没那么必要。）\n注意，在日常开发里，你其实不用提这么具体的要求。我这里写得细一点，只是为了行文方便。你只要给出一个需要验证的结果，大模型会自己去想该怎么点击。\n当然，Browser Use 能做的远不止前端开发。你也可以用它来自动化日常工作，比如登录带验证码的网站、买机票之类的事情……\nPlaywright Playwright 是微软开源的一套 Web 自动化工具，做的事情和前面几个工具类似。GitHub 上已经有 86.9k star。\nhttps://github.com/microsoft/playwright\n曾老师日常会用 Playwright 做完整的自动化测试。但只要涉及模拟操作网页，还是更常用上面那 3 个。严格说，Playwright 更像自动化测试基座，不完全算前面那类给 Agent 用的浏览器操作工具，所以我把它放在最后单独说。\nWeb 自动化小结 下面几张图是 Codex 帮曾老师画的。讲真，跟曾老师自己画的比，还是差那么一点点。算了，凑合用吧……\n使用场景 flowchart TD Start[\"我现在要做什么？\"] Debug[\"前端调试看 Console / Network / Performance\"] Test[\"写稳定测试 / CI / 跨浏览器自动化\"] FastOps[\"让 Agent 快速打开网页、点击、填表、截图\"] Platform[\"做更完整的 Browser Agent 平台能力Cloud / Profile / Remote Browser / Agent SDK\"] CDM[\"chrome-devtools-mcp\"] PW[\"Playwright\"] AB[\"agent-browser\"] BU[\"browser-use\"] Start --\u003e Debug Start --\u003e Test Start --\u003e FastOps Start --\u003e Platform Debug --\u003e CDM Test --\u003e PW FastOps --\u003e AB Platform --\u003e BU AB -. \"也能做部分调试，但不是强项\" .-\u003e Debug BU -. \"也能做网页操作，但更偏平台层\" .-\u003e FastOps PW -. \"也能直接脚本化调试页面\" .-\u003e Debug CDM -. \"可做基础自动化，但核心价值是调试观察\" .-\u003e FastOps 选型表格 block-beta columns 5 h0[\"能力\"] h1[\"Playwright\"] h2[\"chrome-devtools-mcp\"] h3[\"agent-browser\"] h4[\"browser-use\"] r1[\"调试 / 可观测性\"] r1a[\"中\\n可脚本调试，但不是主场\"] r1b[\"强\\nConsole / Network / Performance\"] r1c[\"中\\n有 console/errors/snapshot\"] r1d[\"中\\n可观察，但不如 DevTools 专精\"] r2[\"测试 / CI\"] r2a[\"强\\n最成熟的测试框架\"] r2b[\"弱\\n不是测试框架\"] r2c[\"弱\\n偏操作型 CLI\"] r2d[\"中\\n能自动化，但非传统测试主力\"] r3[\"跨浏览器\"] r3a[\"强\\nChromium / Firefox / WebKit\"] r3b[\"弱\\n主要面向 Chrome\"] r3c[\"弱\\n主要走 Chrome/CDP\"] r3d[\"弱\\n重点不在跨浏览器\"] r4[\"Agent 友好度\"] r4a[\"中\\n可用但更偏工程脚本\"] r4b[\"中\\n通过 MCP 接给 Agent\"] r4c[\"强\\nsnapshot + @ref 很适合 Agent\"] r4d[\"强\\nCLI / SDK / Agent workflow\"] r5[\"云 / 远程浏览器\"] r5a[\"中\\n可 connect_over_cdp\"] r5b[\"弱\\n核心不是浏览器基础设施\"] r5c[\"中\\n支持 provider / CDP 连接\"] r5d[\"强\\nCloud / Remote Browser 是强项\"] r6[\"登录态 / Profile 复用\"] r6a[\"中\\n可保存 storage state\"] r6b[\"弱\\n不以此为主\"] r6c[\"中\\nprofile / session-name / auto-connect\"] r6d[\"强\\nprofile / auth / sync 体系更完整\"] classDef header fill:#1f2937,stroke:#111827,color:#fff; classDef rowhead fill:#e5e7eb,stroke:#9ca3af,color:#111827; classDef strong fill:#dcfce7,stroke:#22c55e,color:#14532d; classDef medium fill:#fef3c7,stroke:#f59e0b,color:#92400e; classDef weak fill:#fee2e2,stroke:#ef4444,color:#991b1b; class h0,h1,h2,h3,h4 header class r1,r2,r3,r4,r5,r6 rowhead class r1b,r2a,r3a,r4c,r4d,r5d,r6d strong class r1a,r1c,r1d,r2d,r4a,r4b,r5a,r5c,r6a,r6c medium class r2b,r2c,r3b,r3c,r3d,r5b,r6b weak 关系 graph TD CDP[\"CDP / Chrome DevTools Protocol\"] PW[\"Playwright\"] CDM[\"chrome-devtools-mcp\"] BU[\"browser-use\"] AB[\"agent-browser\"] Agent[\"AI Agent / Codex / Claude / Cursor\"] Chrome[\"Chrome / Chromium\"] Cloud[\"Cloud Browser / Remote Browser\"] CDP --\u003e Chrome PW --\u003e Chrome PW -. \"可通过 connect_over_cdp 接入\" .-\u003e CDP CDM --\u003e CDP CDM --\u003e Chrome CDM -. \"把 DevTools 能力暴露为 MCP tools\" .-\u003e Agent AB --\u003e CDP AB --\u003e Chrome AB -. \"CLI + snapshot/@eN refs\" .-\u003e Agent BU --\u003e CDP BU --\u003e Chrome BU --\u003e Cloud BU -. \"CLI / SDK / Agent Platform\" .-\u003e Agent PW -. \"可接入 browser-use 提供的云浏览器\" .-\u003e BU classDef proto fill:#f5f5f5,stroke:#666,color:#111; classDef tool fill:#e8f1ff,stroke:#4a78c2,color:#111; classDef platform fill:#eaf7ea,stroke:#4f8a4f,color:#111; class CDP proto; class PW,CDM,AB tool; class BU platform; 最重要的总结 什么？你说曾老师数学不行，介绍了四个工具？\n怎么，买三送一。 你还不乐意了？\n","date":"2026-04-20","description":"","lastmod":"2026-04-21T12:49:47Z","slug":"browser-automation-tools-for-ai-coding","tags":["ai","ai-skill","ai-agent"],"title":"3个最好的浏览器自动化Skill，让AI全自动修复WEB前端BUG","url":"https://blog.zengrong.net/post/browser-automation-tools-for-ai-coding/"},{"categories":["technology"],"content":"曾老师昨天买了小米的 Xiaomi MMO Token Plan（100 元档），改了两个 BUG 就剩 80 块了……\n问题是，这还卡着，一个问题改 40 分钟没改完。\n曾老师这是第一次希望 token 快点花完，因为 token 不动，证明模型被限流了。\n小米的 mimo-v2-pro 编程能力还是不错的，曾老师觉得差不多达到了 GLM 5.1 的水准。当然有可能是 GLM 5.1 太卡，水准发挥不正常？\n一到下午，对于我这种每个模型都开一个窗口，同时干活的人来说，也无法逃过被卡住的命运，因为：\n！所！有！模！型！都！卡！！！\n智谱的 GLM 5.1 超卖就不说了，一到下午就 429。\n速度发挥最稳定的莫过于 MiniMax-M2.7（官方 Coding Plan），贼快贼快的，虽然有时候不太聪明：\n火山方舟 Coding Plan 中的 Kimi-k2.5 慢到没法用！有网友说可能在整个池子里有人传图吧。\n那么，这么多模型一起用，到底哪个编程能力更强点呢？分三个层次吧：\n在不卡的情况下，自然是 Claude Opus 4.6 断层领先。 GLM 5.1 和 Mimo v2 Pro 大约在一个级别。Gemini 3.1 也差不多在这个级别。 MiniMax M2.7 和 Kimi k2.5 稍弱。 这只是每个模型都烧了十几亿 Token 之后的一个整体体验，曾老师不对上面的排行结果负责。\n最近曾老师的更新可能不太正常。没办法，每天和 AI 缠斗太久，被蒸馏了不少技能。\n愿世界和平，愿模型不卡，愿霍尔木兹海峡早日重新开放。\n","date":"2026-04-09","description":"","lastmod":"2026-04-09T09:36:00Z","slug":"models-are-slow-in-the-afternoon","tags":["ai"],"title":"一到下午，所有的模型都在卡……","url":"https://blog.zengrong.net/post/models-are-slow-in-the-afternoon/"},{"categories":["technology"],"content":"为持续鼓励开发者进行优质内容创作，微信小游戏宣布再次升级2026年IAP激励计划。此次新政通过两阶段的大幅度让利，支持优质游戏在微信平台首发与长线运营，新游首发最高5000万不分成，可获2000万元激励金。\n此次政策调整最大的升级点在于大幅拉高了首发新游的激励天花板。属于首发新游的游戏可享受两阶段奖励。阶段一，游戏在累计流水超10万起的12个月内，可享受首1000万流水不分成，单款最高可获得400万激励金。\n若首发新游在新游期月流水突破1000万，即可解锁阶段二的叠加激励。在该阶段，若累计流水达到4000万，开发者可一次性获得1600万激励金。结合阶段一，单款首发新游最高可享5000万流水不分成，获得2000万激励金总额。\n同时，平台也兼顾了非首发新游的起步成长，非首发新游可享受首100万流水不分成，拿到最高40万的激励金。\n在大幅释放激励金红利之余，新政特别针对长线运营设置了现金扶持。针对符合首发期条件的游戏，近30天内流水每满200万元，平台即激励5%现金。首发成长激励金达标后可按月结算，为开发者提供最直接的资金流回补。\n此外，微信小游戏将延续PC激励政策，平台在2026年上半年还额外叠加定向激励（其他激励政策同时享受），针对IAP小游戏的PC流水返回10% PC广告金，以推动PC端的IAP小游戏持续健康成长。\n官方文档链接：\nhttps://developers.weixin.qq.com/minigame/introduction/commercialization/guide/virtual-payment.html\nQ \u0026amp; A Q1：对于开发者来说，这次激励政策再次升级调整有哪些利好？\nA：首发新游不仅享受首1000万流水不分成，一旦月流水突破千万，还能进一步叠加4000万流水不分成，享受最高1600万激励金。单款游戏总计可获高达2000万的激励支持。\n同时，处在首发新游期的游戏还能获得首发成长激励金，每满200万元平台将额外给予流水5%的现金激励，真金白银支持开发者做长线优质经营。\nQ2：如果我的游戏是非首发新游，能享受到这次的新政吗？\nA：为了普惠更多开发者，非首发新游可以享受首100万流水不分成的激励政策，单款激励上限为40万。\nQ3：如果不选择参与微信小游戏新游首发，还能享受哪些激励政策？\n所有符合平台规范的微信小游戏只要符合规范，均可持续享受现金分成、基础激励金和成长激励金。\n","date":"2026-04-03","description":"","lastmod":"2026-04-03T08:37:12Z","slug":"wechat-minigame-2026-iap-incentive-policy","tags":[],"title":"微信小游戏2026超值激励落地，首发新游最高5000万流水不分成","url":"https://blog.zengrong.net/post/wechat-minigame-2026-iap-incentive-policy/"},{"categories":["technology"],"content":"\n你的 API 号池环境里，可能躺着一个定时炸弹。\n2026 年 3 月 24 日，PyPI 官方紧急下架了 LiteLLM 的两个版本——1.82.7 和 1.82.8。\n不是 bug，而是这两个版本被植入了窃取凭证的木马。Wiz、Datadog Security Labs、Kaspersky、Snyk 等主流安全厂商全部在 48 小时内发布了警报。\n这件事值得认真对待，因为它暴露了高价值 API 代理服务的安全脆弱性。\nLiteLLM 是什么，和 New API 有什么区别 Claude Code 频繁被封，逼着大家不得不在各大模型间来回切换。我们接触到的付费 API 系统，大多采用 New API 项目搭建。\nNew API 是一个开源项目，截至 2026-03-29 在 GitHub 上 Star 23.7k。\nhttps://github.com/QuantumNous/new-api\nNew API 的典型用户是搭建 AI 中转站的人，提供多渠道管理、余额统计、自动签到、额度监控这类运营能力，本质是一个分发系统而不是调用封装。\nLiteLLM 是一个 LLM 调用封装库，核心价值是用同一套代码调用 100+ 种 LLM 提供商的 API——OpenAI、Claude、Gemini、Azure OpenAI、本地 vLLM、Ollama……它既可以作为 Python SDK 在代码里调用，也可以部署为 proxy 服务充当 AI Gateway。\nhttps://github.com/BerriAI/litellm\n截至 2026-03-29，LiteLLM 在 GitHub 上的 Star 为 41.4k。\nLiteLLM = 你写的代码去调各种 API 的胶水层 New API = 你搭一个平台让别人来调你的接口 两者定位不同，但都涉及 API key 的管理。\nLiteLLM 事件也格外值得 New API 用户警惕：你管理的 key 越多，攻击者越感兴趣。\n发生了什么 LiteLLM 是一个流行的 Python 库，被大量企业用作 AI Gateway 的核心组件。攻击者获取了维护者的 PyPI 账号后，在 3 月 24 日上传了两个恶意版本：\nv1.82.7：在 proxy_server.py 中植入了恶意 payload v1.82.8：引入了 litellm_init.pth 文件，利用 Python 的 .pth 机制实现持久化和隐蔽执行 这些版本没有在 GitHub 上留下痕迹。恶意代码只存在于 PyPI 分发的包中。很多人即使查看了源码仓库，也不会发现异常。\n恶意代码会窃取环境中几乎所有值钱的东西：\n云服务商凭证（AWS、GCP、Azure） CI/CD secrets（GitHub Actions、GitLab CI 环境变量） API keys（OpenAI、Anthropic、各种 LLM 渠道） Slack、Discord 等通信平台 token 数据会被发送到攻击者控制的域名。整个过程静默运行，除非主动抓包检查出站流量，否则几乎无感知。\nLiteLLM 的主要用户是企业，它是部署 AI Gateway 的首选工具之一。那些部署了 LiteLLM proxy 的服务器，天然持有 大量 API key 和云凭证。 这正是攻击者最想要的东西。\n攻击者是谁 这轮攻击被归属到 TeamPCP——一个专门针对开源供应链的攻击组织。他们此前已经用类似手法攻击过 Trivy（容器安全扫描工具）和 Checkmarx 等项目。具体到 LiteLLM，入口是通过 Trivy 的代码扫描服务被攻破，进而拿到了 LiteLLM 维护者的 PyPI 凭证。\n这是一个教科书级别的供应链攻击链：\n攻击一个安全工具 → 获取通往主项目的凭证 → 在 PyPI 投毒 → 影响所有下游用户。\n你需要做什么 如果你用过 LiteLLM 1.82.7 或 1.82.8：\n立即卸载：pip uninstall litellm 升级到安全版本：pip install litellm\u0026gt;=1.82.9 轮换所有密钥：你在运行 LiteLLM 的环境中所接触过的所有 API key、云凭证，全部当作已泄露处理。 检查网络流量：看有没有发往陌生域名的出站请求。 Docker 部署的 LiteLLM 受影响吗？ 曾老师让龙虾帮忙查了下服务器上的 LiteLLM Docker 容器：\n龙虾说，曾老师部署的官方 Docker 镜像 不受影响。\n官方 LiteLLM Proxy Docker 镜像走的是独立构建路径，不依赖那两个被污染的 PyPI 包版本。这是官方在安全通告中明确说明的。\n龙虾发现曾老师使用的是 latest ，顺便批评了曾老师，让曾老师改为固定版本，龙虾的原话如下。\n以 /data/docker/compose/litellm 的配置为例：\n1image: litellm/litellm:latest latest 意味着每次拉取都会拿到最新镜像，版本完全不可控。虽然这次 Docker 镜像本身没问题，但未来任何新的恶意版本如果针对 Docker 构建路径，latest 就会自动中招。正确做法是锁定到一个固定的版本标签或 digest，例如：\n1image: litellm/litellm@sha256:9a03bac8b54f6e8b25359e713740d40fe5caf5ca88b5baa0dfcd78f95a95eeb7 或者用语义化版本：\n1image: litellm/litellm:1.82.9 总结一句话： 官方 Docker 镜像路线在这次事件中是安全的，但曾老师你用了 latest 就等于把这个安全边界交了出去。趁这次机会改成固定版本，是值得做的改进。\n真正值得想的 这次事件最让人不安的地方，不是攻击有多高明，而是我们默认信任了 pip install 的产出物。\n开源生态建立在 「源码公开、人人可审查」 的假设上，但 PyPI 分发的包和 GitHub 上的源码完全可以是两回事。只要维护者账号被拿下，这个假设就不成立了。\nLiteLLM 的 GitHub 仓库里没有恶意代码，但 47,000+ 次下载量的恶意版本已经在 PyPI 上躺了好几天。\n你的 CI/CD 流水线里有多少依赖？每次 pip install 的时候，你真的验证过装的是什么吗？\n参考来源 LiteLLM 官方安全公告：https://docs.litellm.ai/blog/security-update-march-2026 Wiz 安全分析：https://www.wiz.io/blog/threes-a-crowd-teampcp-trojanizes-litellm-in-continuation-of-campaign GitHub Issue #24518：https://github.com/BerriAI/litellm/issues/24518 Endor Labs 分析：https://securityaffairs.com/189948/hacking/malicious-litellm-versions-linked-to-teampcp-supply-chain-attack.html Orca Security：https://orca.security/resources/blog/litellm-supply-chain-attack-malware/ Snyk：https://snyk.io/articles/poisoned-security-scanner-backdooring-litellm/ Datadog Security Labs：https://securitylabs.datadoghq.com/articles/litellm-compromised-pypi-teampcp-supply-chain-campaign/ Kaspersky：https://www.kaspersky.com/blog/critical-supply-chain-attack-trivy-litellm-checkmarx-teampcp/55510/ FutureSearch：https://futuresearch.ai/blog/litellm-pypi-supply-chain-attack/ ","date":"2026-03-29","description":"","lastmod":"2026-03-29T14:41:19Z","slug":"litellm-supply-chain-attack-182","tags":["ai","python"],"title":"危险！你的号池在将所有 API KEY 上传！LiteLLM 1.82.7/1.82.8 供应链攻击详解","url":"https://blog.zengrong.net/post/litellm-supply-chain-attack-182/"},{"categories":["technology"],"content":"nanobot 是一个只有 4000 行代码，Python 实现的类 OpenClaw 智能体。曾老师在 Windows 系统中使用它，有两个益处：\n规避 OpenClaw 的安装和运行环境问题 规避安全问题，4000 行代码很容易阅读和修改 使用 nanobot 的时候，我碰到一个很实际的问题：我希望它能访问局域网中的服务。\n例如，我有一个 indexTTS2 语音生成服务搭建在局域网服务器上（需要使用高配显卡集群），这个服务不对公网提供访问，但我希望能在外网通过龙虾随时调用它。\n但 nanobot 默认做不到。\n无论是 web_fetch，还是 exec 里的 curl，只要目标是局域网地址（例如 192.168.x.x、10.x.x.x），都会被它的安全策略拦下来。\n这其实不是什么 bug，而是一个很标准的 SSRF / 私网 访问防护设计。默认拒绝私网地址，这件事本身是对的。\n问题在于，我的需求也是真的。\n所以这篇文章要解决的，不是「如何粗暴地关掉 nanobot 的安全机制」，而是另一个问题：\n如何在保留默认安全边界的前提下，让 nanobot 有控制地访问局域网资源。\n我的最终方案是做一个基于白名单的私网访问策略：\n默认仍然拒绝私网访问； 只有命中指定的 CIDR 或 Host 才允许放行； 改动尽量小，方便后续维护； 同时覆盖 web_fetch、exec 和 subagent 的工具链路。 这件事说起来不复杂，真做起来，坑还不少。\n尤其是在 Windows 服务部署环境下，很多问题并不是改完代码就结束了。\n曾老师修改过的项目在此，使用 private_network 分支：\nhttps://github.com/zrong/nanobot.git\n下面我从问题定位、方案设计、代码实现和部署验证四个方面，完整记录一下这次改造过程。\n1. nanobot 为什么默认不能访问局域网 先说结论：nanobot 不能访问局域网，并不是某一个工具单独做了限制，而是平台层面做了多层防护。\n我这次排查时，主要看到三个关键位置：\nnanobot/security/network.py nanobot/agent/tools/web.py nanobot/agent/tools/shell.py 其中 network.py 是核心。它会识别常见的私有地址范围，例如：\n10.0.0.0/8 127.0.0.0/8 172.16.0.0/12 192.168.0.0/16 以及 localhost、link-local 和部分 IPv6 范围。\n也就是说，只要 URL 目标落在这些地址范围里，就会被当作潜在的内部网络访问目标处理。\nweb_fetch 在真正请求 URL 之前会做一次安全校验。\nexec 也不例外，它会直接检查命令字符串里是否包含私网 URL。\n所以你看到的表象虽然是「agent 不能抓某个网页」，但本质上其实是平台级网络安全策略在生效。\n2. 我的目标不是关闭安全 碰到这种需求，最简单的做法当然是：\n注释掉私网检测； 或者把「检测到私网就报错」的逻辑改成直接放行。 这么看似既快又危险，实际上一点儿也不安全。\n如果一个 agent 平台默认允许访问内网资源，那它的安全边界就会立刻变得非常模糊。\n尤其是带有 web_fetch、exec、subagent 这类能力的时候，一旦把私网访问彻底放开，平台就有了探测和操作内网服务的潜力。例如局域网上或者本机有另一个 OpenClaw 进程在运行，那就能直接操作它的工作台。\n所以我这次的目标从一开始就很明确：\n不是全开，而是白名单放行。\n换句话说：\n默认行为不能变，还是拦； 用户必须显式配置，才允许访问特定内网目标； 白名单最好同时支持网段和主机名； 代码改动尽量收敛，不搞大重构。 很多时候，技术实现不是最难的。\n如何以最小的改动，把正确的事情做进去，才是难点。\n3. 白名单方案 既然目标是「默认拦截 + 显式放行」，那配置就应该足够直白。\n我最后采用的配置结构是：\n1{ 2 \u0026#34;tools\u0026#34;: { 3 \u0026#34;networkSecurity\u0026#34;: { 4 \u0026#34;allowPrivateNetwork\u0026#34;: false, 5 \u0026#34;allowedPrivateCidrs\u0026#34;: [\u0026#34;192.168.31.0/24\u0026#34;], 6 \u0026#34;allowedPrivateHosts\u0026#34;: [] 7 } 8 } 9} 它的语义很简单：\nallowPrivateNetwork：是否启用私网白名单能力； allowedPrivateCidrs：允许访问的私网网段； allowedPrivateHosts：允许访问的私网主机名。 例如：\n1{ 2 \u0026#34;tools\u0026#34;: { 3 \u0026#34;networkSecurity\u0026#34;: { 4 \u0026#34;allowPrivateNetwork\u0026#34;: false, 5 \u0026#34;allowedPrivateCidrs\u0026#34;: [\u0026#34;192.168.31.0/24\u0026#34;], 6 \u0026#34;allowedPrivateHosts\u0026#34;: [\u0026#34;nas.local\u0026#34;] 7 } 8 } 9} 这样做的好处是，用户不需要理解 nanobot 的内部实现，也能一眼看懂这几个字段是做什么的。\n我不太喜欢那种「为了显得优雅，把简单事情搞得很抽象」的配置设计。\n一个配置项如果要靠半天文档才能看懂，那它多半就不算是一个好的配置项。\n4. 代码实现改了哪些地方 这次最终改动涉及 7 个文件：\nnanobot/security/network.py nanobot/config/schema.py nanobot/agent/tools/shell.py nanobot/agent/tools/web.py nanobot/agent/loop.py nanobot/agent/subagent.py nanobot/cli/commands.py 看起来文件不少，但职责其实很清楚：\nschema.py 定义配置结构； network.py 统一处理私网判断和白名单放行； web.py 负责 web_fetch 的 URL 校验与重定向校验； shell.py 负责 exec 中私网 URL 的拦截与放行； cli/commands.py -\u0026gt; loop.py -\u0026gt; subagent.py 负责把配置一路传进具体工具。 核心判断逻辑可以概括成这样：\n1def _private_addr_allowed(addr, network_security_config) -\u0026gt; bool: 2 # addr 是私网地址时： 3 # 1. 判断当前是否启用私网白名单能力 4 # 2. 检查是否命中 allowedPrivateCidrs / allowedPrivateHosts 然后让下面这些入口统一走这套规则：\nvalidate_url_target(...) validate_resolved_url(...) contains_internal_url(...) 这里有个很重要的点：\n白名单判断应该尽量集中在 network 层，而不是散落在 web.py 和 shell.py 里各写一套。\n否则：\n你今天修了 web_fetch，明天漏了 exec； 今天 IPv4 正常，明天 Hostname 解析又不一致。 安全相关逻辑如果分散，后面一定会出问题。 5. 为什么我最后放弃了「三参数一路传递」 一开始我也想过最直接的办法：把三个参数平铺着一路往下传：\nallow_private_network allowed_private_cidrs allowed_private_hosts 这办法能不能用？能用。\n但它会把很多函数签名都改脏。\n原本一个函数参数很干净，现在突然多出三项； 原本只是构造一个 tool，现在又要同步这三项； 一路从 cli -\u0026gt; loop -\u0026gt; subagent -\u0026gt; tool -\u0026gt; network 传下去，扩散面会很大。 所以我后来把它收敛成了单对象传递：统一传一个 network_security_config。\n也就是说：\nCLI 把 config.tools.network_security 传给 AgentLoop AgentLoop 再传给 SubagentManager ExecTool 和 WebFetchTool 都持有同一个配置对象 到 network.py 再从对象里读取 allow_private_network、allowed_private_cidrs、allowed_private_hosts 调用链大致如下：\nflowchart LR A[config.jsontools.networkSecurity] --\u003e B[cli/commands.py] B --\u003e C[AgentLoop] C --\u003e D[SubagentManager] C --\u003e E[WebFetchTool] C --\u003e F[ExecTool] D --\u003e G[Subagent WebFetchTool] D --\u003e H[Subagent ExecTool] E --\u003e I[security/network.py] F --\u003e I G --\u003e I H --\u003e I 这里真正重要的不是「对象传递比较优雅」，而是：\n维护成本高不高，不取决于它看起来扁平还是优雅，而取决于改动是不是集中、签名是不是扩散、冲突面是不是可控。\n对这次补丁来说，单对象传递明显更克制，也更容易继续维护。\n6. Windows 服务部署：Shawl、登录身份与路径问题 如果你以为代码改完就结束了，那就太乐观了。\n我这里的 nanobot 跑在 Windows 上，而且是作为服务启动的。\n我最终使用的是 Shawl，它是一个很好用的 Windows 服务包装器，适合把普通命令包装成 Windows 服务来运行。\n这类工具的好处很直接：\n不需要自己写服务宿主程序； 可以把任意命令包装成服务； 支持重启、日志、超时等常见能力； 对命令行程序非常友好。 我这里就是用 Shawl 包装 nanobot gateway 来跑。\n6.1 路径问题不是 nanobot 的 bug，而是登录身份不同 这个坑很典型。\n手工在命令行里运行 nanobot gateway 的时候，一切正常。\n但当它作为 Windows 服务启动后，却报了一些非常迷惑的问题，例如配置找不到、API Key 缺失之类。\n最后排查下来，原因不是配置文件坏了，而是：\n服务运行时使用的登录身份，与我平时手工登录 Windows 的身份不是同一个。\n而在 Windows 下，不同登录身份对应的用户目录也不同。\n这就导致服务进程解析出来的 ~/.nanobot 根本不是我平时使用的那个：\nC:\\Users\\admin\\.nanobot\n而可能变成类似下面这种路径：\nC:\\Windows\\System32\\config\\systemprofile\\.nanobot\n于是问题就出现了：\n你明明已经在自己的用户目录下配好了 config.json，但服务进程根本没读到它。\n所以这个问题的本质不是「路径错了」，而是运行服务的登录身份变了，用户主目录也跟着变了。\n6.2 解决方法：显式指定配置路径 解决办法并不复杂：不要依赖 ~/.nanobot，而是显式指定配置文件路径。\n例如：\n1nanobot gateway --config C:\\Users\\admin\\.nanobot\\config.json 如果你用 Shawl 包装服务，就把这个参数直接写进服务命令行里。\n6.3 Windows 服务「登录身份」设置截图 看这个设置，比盯着报错日志瞎猜要有效得多。\n6.4 本地源码安装不一定可靠，wheel 反而更稳 我这次还碰到另一个问题。\n理论上，直接执行：\n1uv tool install D:\\storage\\nanobot 看起来很方便。\n但实际验证时发现，这种基于本地目录的安装方式，在某些情况下会让工具环境里残留旧包内容，导致你明明已经改了代码，运行结果却像没改一样。\n最后验证下来，更稳的办法反而是：\n先 build wheel； 卸载旧工具安装； 从新生成的 wheel 安装。 虽然步骤多一点，但结果更可控，也更容易确认「当前运行的到底是哪份代码」。\n很多时候，工程上真正靠谱的方案，未必是最省命令的那个。\n7. 最终验证 代码写完、服务跑起来之后，最重要的还是验证。\n我的测试目标是一个局域网服务：\nhttp://192.168.31.88:8888\n配置中允许的网段是：\n[\u0026quot;192.168.31.0/24\u0026quot;]\n也就是说，如果实现正确，那么这个地址应该可以通过白名单被访问。\n7.1 web_fetch 验证通过 直接通过主工具链执行 web_fetch，成功获取到了页面内容。\n目标站点返回了正常的 HTTP 200，并且内容提取也成功完成。\n这里还有一个细节：\n在抓取过程中，Jina Reader 对这个私网 URL 返回过 451 Unavailable For Legal Reasons，但工具随后正确回退到了本地可读性提取逻辑，最终依然拿到了页面正文。\n这说明白名单放行不仅通过了前置校验，也在实际抓取链路里工作正常。\n7.2 exec 验证通过 我还验证了下面这条命令：\n1curl http://192.168.31.88:8888 在原本的逻辑里，这类命令会因为包含私网 URL 被直接拦截。而在加入白名单策略之后，这条命令已经可以正常通过 guard 检查。\n这一步很关键。\n因为如果只修了 web_fetch，没有修 exec，那整个平台的 LAN 能力其实还是不完整的。\n8. 这次改造真正重要的，不是「能访问 LAN」 如果只是为了访问内网，我完全可以写一个外部脚本，或者单独做一个 LAN skill，然后绕过主工具链去抓数据。\n但我最后还是选择改 nanobot 本体，是因为我更关心另一件事：\n如何把一个真实需求，变成平台层面可维护、可配置、可验证的能力。\n「能不能访问 LAN」只是表象。 真正重要的是下面这些事情有没有一起成立：\n默认是否仍然安全； 放行是否足够精确； 实现是否覆盖完整工具链； 改动是否尽量小； 后续是否容易继续维护。 如果这些问题没有同时解决，那么「能访问 LAN」这件事本身其实没多大价值。\n在 agent 时代，这类问题以后只会越来越多。\n因为 agent 的能力边界越来越像「一个会看、会想、会执行命令的操作员」，而不是一个简单的 API 调用器。当能力变强之后，安全边界、配置边界和工程边界就都不能靠拍脑袋决定了。\n9. 总结 这次给 nanobot 增加局域网访问能力，我最终采用的是一个白名单式私网访问方案：\n默认仍然拒绝访问内网； 通过 tools.networkSecurity 显式配置允许的 CIDR 和 Host； 同时覆盖 web_fetch、exec 和 subagent 工具链； 在实现上采用 network_security_config 单对象传递，减少签名扩散； 在部署上通过 Shawl 包装 Windows 服务，并显式指定 --config 保证配置路径稳定。 回头看，这次工作里最值得记录的，不是某一行代码怎么改，而是这几个判断：\n不要为了满足需求，顺手破坏默认安全边界； 不要为了写得「优雅」，把一个小功能改成大手术； 不要以为代码能跑就算完成，部署环境和验证链路同样重要。 对于一个本地自用的 agent 来说，访问局域网资源是很自然的诉求。 但「自然的诉求」，并不意味着可以「随便地实现」。 技术方案真正成熟的标志，从来都不是它「终于能用了」，而是它在能用之外，还尽量保持了边界、克制和可维护性。\n","date":"2026-03-28","description":"","lastmod":"2026-03-28T13:19:32Z","slug":"nanobot-lan-whitelist","tags":["ai","ai-agent"],"title":"给龙虾 nanobot 增加局域网访问能力：一次最小改动的白名单改造","url":"https://blog.zengrong.net/post/nanobot-lan-whitelist/"},{"categories":["technology"],"content":"目前有两种主要的方法，可以让微信直接和 OpenClaw 通信。\n但你们不要想多了。让微信官方的机器人，做到和飞书中的机器人一样，在群里作为小助手回复信息，是不可能的。\n它只能作为你的私聊对象。\n接入已有的 OpenClaw 实例 要确保手机上的微信是最新版本。\n对于 iOS 手机，升级到最新的 8.0.70。 对于 Android 手机，如果已经是 8.0.69，建议访问官方网站下载安装包，再更新一次： https://weixin.qq.com\n点击微信中的插件进行连接：\n进入微信的 我 -\u0026gt; 设置 -\u0026gt; 插件 配置，找到 微信 ClawBot 插件。\n这里的体验其实很不好，它直接给了一段命令让你执行：\n1npx -y @tencent-weixin/openclaw-weixin-cli@latest install 这段代码，必须在安装了 OpenClaw 的机器上 手动执行，不能让龙虾代为执行。\n因为安装后会出现一个二维码。如果让龙虾代为执行，这个码你就没法看到。\n在插件界面中点击扫码，扫码二维码进行连接。\n连接成功后，微信的聊天列表中会出现一个名为 微信ClawBot 的联系人。这个联系人可以置顶，但不能被拉入群聊。\n这有点像那个啥也干不了的元宝，但好处是 微信 ClawBot 机器人能直接识别语音指令：\n接入QClaw 对于懒得折腾的同学，可以直接安装 QClaw v0.1.16 以上的版本。\n因为这个版本可以直接使用上面刚刚推出的 微信 ClawBot。在上个版本里，QClaw 还需要通过客服会话才能进行通信。\n但坏处是，一个微信只能连接一个龙虾。如果你连接了 OpenClaw，再跑来连接 QClaw，那么之前的 OpenClaw 就断联了。\n建议目前还 没有折腾本地龙虾 的同学，可以安装 QClaw 试试。这是当前成本和效率都比较平衡的方案了。每天 4000 万 Token，至少能聊天 120～200 次了。\nhttps://qclaw.qq.com/\n","date":"2026-03-22","description":"","lastmod":"2026-03-23T11:28:39Z","slug":"wechat-openclaw-access","tags":["ai","ai-agent"],"title":"沉不住气了！微信终于接入龙虾 OpenClaw","url":"https://blog.zengrong.net/post/wechat-openclaw-access/"},{"categories":["news"],"content":"\n3月18日，腾讯发布2025年第四季度及全年业绩报告。财报显示，腾讯全年营收7517.7亿元，其中，营销服务业务2025年度的收入同比增长19%至人民币1,450亿元，主要得益于广告单价及广告曝光量增长。\n广告单价受益于AI驱动的广告精准定向、广告主使用AI制作更多广告，以及闭环广告（用户点击后可直达小程序、微信小店或小游戏等原生交易场景）的占比持续提升。曝光量增长主要得益于用户对包括视频号及微信搜一搜在内的产品参与度增加，以及广告加载率的小幅提升。年内大多数主要行业的广告主投放均有所增长。\n同时，增长也得益于AI技术提升了广告定向能力。 腾讯升级了广告技术的基础模型，并推出了智能投放产品矩阵腾讯广告AIM+，保持广告加载率远低于同业的同时，亦取得高于行业的收入增长。\n营销服务业务2025年第四季度的收入为人民币411亿元，同比增长17%。 对AI驱动的广告精准定向能力的优化，以及对微信生态系统内闭环营销能力的扩展，推动了广告表现改善和广告单价提升，构成收入增长的主要驱动力。由于用户参与度提高及广告加载率的小幅提升，广告曝光也略有增长。\n回顾这一年，腾讯广告是如何围绕AI+生态，为广告主开拓商业增量的呢？\nAI技术方面，腾讯广告2025年发布了智能投放产品矩阵AIM+，它由「场景化产品」、「单点能力产品」和底层「AI决策」构成，实现了从创意生产、定向出价到版位执行的全流程自动化，助力广告主的智能投放从工具赋能迈向「自动驾驶」阶段。\n第四季度，AIM+在场景拓展、链路深化与创意自动化方面实现了进一步升级。在既有的小店艾米、内容艾米、线索艾米基础上，新上线「应用艾米」和「商品艾米」，支持含游戏、阅读、AI 应用在内的App投放场景，以及微信小程序直购链路的商品投放场景。腾讯广告也持续基于AI为创意提效，广告主可一键开启「创意全库智选」功能，AI能自动筛选优质创意并投放，相比手工挑选提升5倍创意供给。\nAI技术的突破离不开优质人才的持续引入，最新财报中也提到了这一点。至今已成功举办六届的腾讯广告算法大赛，始终聚焦业界最前沿的技术挑战。2025年大赛以「全模态生成式推荐」为赛题，吸引了全球近30个国家超2800支团队、8400余名选手报名参与。\n3月17日，刚刚官宣启动的2026算法大赛规格全面升级，腾讯广告与国际顶级会议KDD联合办赛，聚焦业界下一代推荐架构的核心未解难题——面向大规模推荐的序列建模与特征交互的统一，并提供高达88.5 万美元（超 600 万元人民币）的总奖池。\n除了技术，微信生态也进一步在全域经营方面协同发力。 视频号、小程序、微信小店及搜一搜等核心场景，为广告主构建了多触点、闭环式的营销阵地，有效承接了强劲的广告投放需求。\n视频号广告投放需求持续增加。过去一年围绕素材供给、行业化能力及经营工具等维度持续迭代。\n素材供给方面，现支持广告主选择授权视频号账号中已发表的自有内容、互选商单内容及合作达人内容等进行投放推广，以获得更多用户触达及转化机会，放大优质内容传播优势。\n短剧行业能力方面，新增「短剧半屏衍生」等能力，广告主可根据推广剧目自动生成半屏页素材并跳转短剧小程序，提升短剧行业广告创建与投放效率。经营工具层面，「评论管理助手」全新升级，引入AI进行正负向情绪分析与智能回复，大幅提升了内容运营与转化效率。\n视频号内容生态也在快速发展，数据显示，2025年1-8月，腾讯广告互选平台变现创作者数量同比提升209%，收入规模同比增长205%。\n在小程序场景：\n小游戏商业化能力成绩优异。微信广告生态合作伙伴大会数据显示，2025年1-5月IAA 广告主数量增长190%创新高。纯IAA小游戏的收入占小游戏流量主总广告规模的比例已达70%，成为变现引擎。第四季度，小游戏「商业化工具箱Pro」上线，提供营收ROI 便捷计算、品类标杆对比洞察、投放90日ROI 预估和多日变现指标分析等能力，助力打造更多爆款。同时，体验感拉满的「试玩广告」形态成为转化利器，激励版位渗透率大幅增至44%，试玩时长突破30秒。\n面对持续升温的漫剧赛道，腾讯广告也在加快布局，不仅推出AI 漫剧创作平台「妙创」，更联合领先高校发起AI 漫剧创意大赛，通过「技术赋能+人才挖掘」双向发力，推动漫剧产业生态的高质量繁荣。\n快速迭代的微信小店正在迸发全域经营的新活力。\n去年10月，专为小店商家打造的轻量化广告投放工具腾讯广告（小店版）上线，商家可以在微信小店管理后台一键开通，实现更简单、更高效的生意经营。\n越来越多商家正以「微信小店+微信小程序」为枢纽，在用户的多触点联通、会员体系及资产、自营分销等经营模式实现「公私域融合经营」，叠加广告投放，实现商家生意规模的快速扩展。雅诗兰黛就在双旦期间，聚焦「送礼」需求，通过小程序「送礼专区」内嵌商品卡及「晒单有礼」等互动玩法，成功拉动微信小店 GMV 同比增长 145%，有效验证了将流量转化为长期用户资产的全链路运营价值。\n随着搜索融入微信用户的日常，搜一搜已成为品牌连接用户的高效入口。尤其在520、七夕、双旦等节点，商家通过重点布局微信搜一搜，借搜一搜会场、超级品牌专区等展示产品和品牌形象，整合其在微信全域的内容和服务，能在精准满足高意向用户需求的同时，也大大提升了营销转化。\n在快速变化的市场环境中，腾讯广告基于「技术赋能+全域协同」的双轮驱动，为广告主持续提供更具确定性的生意增量，实现长效经营与稳健增长。\n","date":"2026-03-19","description":"","lastmod":"2026-03-19T09:52:23Z","slug":"tencent-2025-financial-report","tags":[],"title":"腾讯2025财报：营销服务营收达1450亿元，「AI赋能+全域协同」筑牢增长底盘","url":"https://blog.zengrong.net/post/tencent-2025-financial-report/"},{"categories":["technology"],"content":"我们都知道，腾讯文档的体验有点差。\n尤其最近上线的那个 AI 模式，不仅 UI 设计糟糕，交互体验混乱，甚至都无法找到需要的文件。\n昨天，腾讯文档官方公众号发布了一篇文章，介绍了使用 OpenClaw 调用腾讯文档 Skill，今天曾老师就来讲讲如何使用。\n本质上，这个 Skill 是通过 MCP 调用腾讯文档的功能的。我这里的范例使用的是 Claude Code，但使用 OpenClaw 或者其他 Agent 也是没问题的。\n安装 Skill 在 Skill 是 Agent 的核心：让龙虾 OpenClaw 给飞书发电脑桌面截图 一文中，我提到了曾老师的 Skill 仓库，也介绍了如何安装 Skill，欢迎阅读。\n使用下面的命令安装 skill：\n1npx skills add zrong/skills --skill tencent-docs --agent claude-code --agent openclaw 当然，也可以直接告诉龙虾 OpenClaw 或者 Claude Code：\n帮我安装 https://github.com/zrong/skills 中的 tencent-docs\n配置 MCP Skill 安装后，问一下是否能用：\n再要求操作腾讯文档：\n帮我操作下腾讯文档\n此时 Agent 会要求提供 token。\n在腾讯文档中，「空间」的右上角三道杠菜单获取 MCP token，将其提供给龙虾 OpenClaw。\n把得到的 MCP token 大概龙虾就可以了：\n分析合同 然后可以直接和龙虾说要什么：\n我让龙虾帮我分析了一个合同：\nxxxxx 找到这个合同，分析其中的条款。我是乙方。\n计算 PDF 文档字数 腾讯文档自带的 AI 问答，搜索能力很弱，所以用 Skill 的搜索能力，下面这个问题，它就搞不定：\n2603制作 这个文件夹中，有 5 个 PDF 文件，你统计一下这些 PDF 文件的字数。注意要写个 Python 脚本来统计，不要乱猜。\n需要注意的是，即使文档是 PDF 格式，在腾讯文档内部，也并不是这样定义的。\n对于腾讯文档来说，它会认为这些 PDF 是 Word 格式。\n找到后，龙虾就开始自己写脚本。\n为什么一定要使用 Python 统计呢？因为基于大模型的特点，字数统计功能是不太准的，使用代码才能准确获取数据。\n至于如何读取文件，如何检测字数，大模型自然知道调用什么函数，你自己不用操心。\n统计小说转漫剧后的时长 根据之前做的小说字数和漫剧时长关系，计算 5 本小说生成的漫剧的预估时长。\n我提供了 3 本小说字数和时长的关系，你写 Python 代码帮我计算一下， 新的五本小说，时长大概是多少？给我个统计表格。\n结语 这些事情，之前可以交给实习生去做，现在可以交给 AI 去做。\n重要的是，要掌握合适的 AI 工具，以及知道 AI 的边界在哪里。\n","date":"2026-03-11","description":"","lastmod":"2026-03-11T07:47:29Z","slug":"openclaw-tencent-docs","tags":["openclaw","llm","ai","ai-agent"],"title":"使用OpenClaw龙虾操作腾讯文档，分析合同+计算小说转漫剧的时长","url":"https://blog.zengrong.net/post/openclaw-tencent-docs/"},{"categories":["technology"],"content":"昨天腾讯云线下免费安装 OpenClaw 的新闻刷爆了行业群，真的是一代人有一代人的鸡蛋……\n今天换电脑找 API-KEY 的时候，我发现火山引擎的火山方舟 Coding Plan 居然自带了一个免费的 ArkClaw 助手，后面是一套火山引擎的 ECS 主机，配置 2C4G40GB 硬盘 5MB 带宽。\n重点是： 免费！\n真的送了一台免费主机 只要你的 Coding Plan 在付费状态，这台主机就能持续运行。\n如果购买的话，一年 99 元。2C4G40GB 5MB 的云主机这个价格，还挺赚的。\n于是我顺手开通了这台主机，发现它就是一台预装了 OpenClaw 的标准 ECS，预装 Ubuntu24.04。\n既然都免费了，不养龙虾多可惜，开始！\n曾老师目前已经有三只龙虾，这个是第四只。每只龙虾我会至少配置一个飞书机器人方便远程管理。\n所以对于这台免费的龙虾主机，我也要新建一个飞书机器人。\n绑定飞书机器人的文章满天飞了，我推荐下面两篇安装教程（3 月 5 日发布）：\nhttps://www.feishu.cn/content/article/7613711414611463386 https://cloud.tencent.com/developer/article/2626151 火山引擎也提供了 一键接入 ArkClaw 这篇教程：\nhttps://www.volcengine.com/docs/6396/2227963?lang=zh 曾老师讲一些配置机器人的过程中容易让人抓狂的问题，主要是上面教程的查漏补缺。\n创建一个新的机器人 https://open.feishu.cn/\n主要有三步：\n创建一个企业自建应用。 「应用能力 \u0026gt; 添加应用能力」中给应用添加「机器人」能力卡片。 「权限管理 \u0026gt; 批量导入/导出权限」，复制下面的 JSON 代码确认新增。 1{ 2 \u0026#34;scopes\u0026#34;: { 3 \u0026#34;tenant\u0026#34;: [ 4 \u0026#34;contact:contact.base:readonly\u0026#34;, 5 \u0026#34;docx:document:readonly\u0026#34;, 6 \u0026#34;im:chat:read\u0026#34;, 7 \u0026#34;im:chat:update\u0026#34;, 8 \u0026#34;im:message.group_at_msg:readonly\u0026#34;, 9 \u0026#34;im:message.p2p_msg:readonly\u0026#34;, 10 \u0026#34;im:message.pins:read\u0026#34;, 11 \u0026#34;im:message.pins:write_only\u0026#34;, 12 \u0026#34;im:message.reactions:read\u0026#34;, 13 \u0026#34;im:message.reactions:write_only\u0026#34;, 14 \u0026#34;im:message:readonly\u0026#34;, 15 \u0026#34;im:message:recall\u0026#34;, 16 \u0026#34;im:message:send_as_bot\u0026#34;, 17 \u0026#34;im:message:send_multi_users\u0026#34;, 18 \u0026#34;im:message:send_sys_msg\u0026#34;, 19 \u0026#34;im:message:update\u0026#34;, 20 \u0026#34;im:resource\u0026#34;, 21 \u0026#34;application:application:self_manage\u0026#34;, 22 \u0026#34;cardkit:card:write\u0026#34;, 23 \u0026#34;cardkit:card:read\u0026#34; 24 ], 25 \u0026#34;user\u0026#34;: [ 26 \u0026#34;contact:user.employee_id:readonly\u0026#34;, 27 \u0026#34;offline_access\u0026#34;,\u0026#34;base:app:copy\u0026#34;, 28 \u0026#34;base:field:create\u0026#34;, 29 \u0026#34;base:field:delete\u0026#34;, 30 \u0026#34;base:field:read\u0026#34;, 31 \u0026#34;base:field:update\u0026#34;, 32 \u0026#34;base:record:create\u0026#34;, 33 \u0026#34;base:record:delete\u0026#34;, 34 \u0026#34;base:record:retrieve\u0026#34;, 35 \u0026#34;base:record:update\u0026#34;, 36 \u0026#34;base:table:create\u0026#34;, 37 \u0026#34;base:table:delete\u0026#34;, 38 \u0026#34;base:table:read\u0026#34;, 39 \u0026#34;base:table:update\u0026#34;, 40 \u0026#34;base:view:read\u0026#34;, 41 \u0026#34;base:view:write_only\u0026#34;, 42 \u0026#34;base:app:create\u0026#34;, 43 \u0026#34;base:app:update\u0026#34;, 44 \u0026#34;base:app:read\u0026#34;, 45 \u0026#34;board:whiteboard:node:create\u0026#34;, 46 \u0026#34;board:whiteboard:node:read\u0026#34;, 47 \u0026#34;calendar:calendar:read\u0026#34;, 48 \u0026#34;calendar:calendar.event:create\u0026#34;, 49 \u0026#34;calendar:calendar.event:delete\u0026#34;, 50 \u0026#34;calendar:calendar.event:read\u0026#34;, 51 \u0026#34;calendar:calendar.event:reply\u0026#34;, 52 \u0026#34;calendar:calendar.event:update\u0026#34;, 53 \u0026#34;calendar:calendar.free_busy:read\u0026#34;, 54 \u0026#34;contact:contact.base:readonly\u0026#34;, 55 \u0026#34;contact:user.base:readonly\u0026#34;, 56 \u0026#34;contact:user:search\u0026#34;, 57 \u0026#34;docs:document.comment:create\u0026#34;, 58 \u0026#34;docs:document.comment:read\u0026#34;, 59 \u0026#34;docs:document.comment:update\u0026#34;, 60 \u0026#34;docs:document.media:download\u0026#34;, 61 \u0026#34;docs:document:copy\u0026#34;, 62 \u0026#34;docx:document:create\u0026#34;, 63 \u0026#34;docx:document:readonly\u0026#34;, 64 \u0026#34;docx:document:write_only\u0026#34;, 65 \u0026#34;drive:drive.metadata:readonly\u0026#34;, 66 \u0026#34;drive:file:download\u0026#34;, 67 \u0026#34;drive:file:upload\u0026#34;, 68 \u0026#34;im:chat.members:read\u0026#34;, 69 \u0026#34;im:chat:read\u0026#34;, 70 \u0026#34;im:message\u0026#34;, 71 \u0026#34;im:message.group_msg:get_as_user\u0026#34;, 72 \u0026#34;im:message.p2p_msg:get_as_user\u0026#34;, 73 \u0026#34;im:message.send_as_user\u0026#34;, 74 \u0026#34;im:message:readonly\u0026#34;, 75 \u0026#34;search:docs:read\u0026#34;, 76 \u0026#34;search:message\u0026#34;, 77 \u0026#34;space:document:delete\u0026#34;, 78 \u0026#34;space:document:move\u0026#34;, 79 \u0026#34;space:document:retrieve\u0026#34;, 80 \u0026#34;task:comment:read\u0026#34;, 81 \u0026#34;task:comment:write\u0026#34;, 82 \u0026#34;task:task:read\u0026#34;, 83 \u0026#34;task:task:write\u0026#34;, 84 \u0026#34;task:task:writeonly\u0026#34;, 85 \u0026#34;task:tasklist:read\u0026#34;, 86 \u0026#34;task:tasklist:write\u0026#34;, 87 \u0026#34;wiki:node:copy\u0026#34;, 88 \u0026#34;wiki:node:create\u0026#34;, 89 \u0026#34;wiki:node:move\u0026#34;, 90 \u0026#34;wiki:node:read\u0026#34;, 91 \u0026#34;wiki:node:retrieve\u0026#34;, 92 \u0026#34;wiki:space:read\u0026#34;, 93 \u0026#34;wiki:space:retrieve\u0026#34;, 94 \u0026#34;wiki:space:write_only\u0026#34; 95 ] 96 } 97} 为什么飞书机器人没有输入框 飞书机器人发布后，如果找不到输入框（类似下图），先别着急，需要继续进行配置。\n接收消息事件\n必须设置飞书应用的 App ID 和 App Secret 之后，OpenClaw 才会和应用进行一次握手通信。\n此时才可以进行事件配置中的「订阅方式」配置。否则，这里的配置是无法保存的。\n保存订阅方式为「使用长连接」之后，添加「接收消息」事件。\n然后，机器人的输入框就会出现。和机器人进行任意对话，得到配对码，填入 OpenClaw 进行配对。\n和飞书机器人的对话，与 ArkClaw 中显示的是同一个 Session，可以同时看到对话信息。\n建议买了 Coding Plan 的同学去领一下 整体测试下来，ArkClaw 的优化还挺不错的，文件传输这些细节问题也都考虑到了。\n同样需要注意的是，为了试用这些功能，火山引擎会自动开通一系列服务。尽管不用不收费，但也要自行关注账单。\n如果还没有注册 Coding Plan，可以看上一篇文章： OpenClaw 选什么模型？GLM-5，MiniMax-M2.5，Kimi 2.5，火山方舟 Coding Plan 真实评测，里面有邀请码，看上哪个扫哪个就行。\n","date":"2026-03-07","description":"","lastmod":"2026-03-07T15:29:04Z","slug":"free-2c4g-host-openclaw-lobster","tags":["openclaw","llm","ai","ai-agent"],"title":"居然可以免费领一台 2C4G 主机安装 OpenClaw 养龙虾……免费！","url":"https://blog.zengrong.net/post/free-2c4g-host-openclaw-lobster/"},{"categories":["technology"],"content":"昨天腾讯云线下免费安装OpenClaw的新闻刷爆了行业群，真的是一代人有一代人的鸡蛋……\n今天换电脑找API-KEY的时候，我发现火山引擎的火山方舟 Coding Plan 居然自带了一个免费的 ArkClaw 助手，后面是一套火山引擎的ECS主机，配置2C4G40GB硬盘5MB带宽。\n重点是： 免费！\n真的送了一台免费主机 只要你的 Coding Plan 在付费状态，这台主机就能持续运行。\n如果购买的话，一年99元。2C4G40GB5MB的云主机这个价格，还挺赚的。\n于是我顺手开通了这台主机，发现它就是一台预装了 OpenClaw 的标准 ECS，预装 Ubuntu24.04。\n既然都免费了，不养龙虾多可惜，开始！\n曾老师目前已经有三只龙虾，这个是第四只。每只龙虾我会至少配置一个飞书机器人方便远程管理。\n所以对于这台免费的龙虾主机，我也要新建一个飞书机器人。\n绑定飞书机器人的文章满天飞了，我推荐下面两篇安装教程（3月5日发布）：\nhttps://www.feishu.cn/content/article/7613711414611463386 https://cloud.tencent.com/developer/article/2626151 火山引擎也提供了 一键接入ArkClaw 这篇教程：\nhttps://www.volcengine.com/docs/6396/2227963?lang=zh 曾老师讲一些配置机器人的过程中容易让人抓狂的问题，主要是上面教程的查漏补缺。\n创建一个新的机器人 https://open.feishu.cn/\n主要有三步：\n创建一个企业自建应用。 「应用能力 \u0026gt; 添加应用能力」中给应用添加「机器人」能力卡片。 「权限管理 \u0026gt; 批量导入/导出权限」，复制下面的JSON代码确认新增。 1{ 2 \u0026#34;scopes\u0026#34;: { 3 \u0026#34;tenant\u0026#34;: [ 4 \u0026#34;contact:contact.base:readonly\u0026#34;, 5 \u0026#34;docx:document:readonly\u0026#34;, 6 \u0026#34;im:chat:read\u0026#34;, 7 \u0026#34;im:chat:update\u0026#34;, 8 \u0026#34;im:message.group_at_msg:readonly\u0026#34;, 9 \u0026#34;im:message.p2p_msg:readonly\u0026#34;, 10 \u0026#34;im:message.pins:read\u0026#34;, 11 \u0026#34;im:message.pins:write_only\u0026#34;, 12 \u0026#34;im:message.reactions:read\u0026#34;, 13 \u0026#34;im:message.reactions:write_only\u0026#34;, 14 \u0026#34;im:message:readonly\u0026#34;, 15 \u0026#34;im:message:recall\u0026#34;, 16 \u0026#34;im:message:send_as_bot\u0026#34;, 17 \u0026#34;im:message:send_multi_users\u0026#34;, 18 \u0026#34;im:message:send_sys_msg\u0026#34;, 19 \u0026#34;im:message:update\u0026#34;, 20 \u0026#34;im:resource\u0026#34;, 21 \u0026#34;application:application:self_manage\u0026#34;, 22 \u0026#34;cardkit:card:write\u0026#34;, 23 \u0026#34;cardkit:card:read\u0026#34; 24 ], 25 \u0026#34;user\u0026#34;: [ 26 \u0026#34;contact:user.employee_id:readonly\u0026#34;, 27 \u0026#34;offline_access\u0026#34;,\u0026#34;base:app:copy\u0026#34;, 28 \u0026#34;base:field:create\u0026#34;, 29 \u0026#34;base:field:delete\u0026#34;, 30 \u0026#34;base:field:read\u0026#34;, 31 \u0026#34;base:field:update\u0026#34;, 32 \u0026#34;base:record:create\u0026#34;, 33 \u0026#34;base:record:delete\u0026#34;, 34 \u0026#34;base:record:retrieve\u0026#34;, 35 \u0026#34;base:record:update\u0026#34;, 36 \u0026#34;base:table:create\u0026#34;, 37 \u0026#34;base:table:delete\u0026#34;, 38 \u0026#34;base:table:read\u0026#34;, 39 \u0026#34;base:table:update\u0026#34;, 40 \u0026#34;base:view:read\u0026#34;, 41 \u0026#34;base:view:write_only\u0026#34;, 42 \u0026#34;base:app:create\u0026#34;, 43 \u0026#34;base:app:update\u0026#34;, 44 \u0026#34;base:app:read\u0026#34;, 45 \u0026#34;board:whiteboard:node:create\u0026#34;, 46 \u0026#34;board:whiteboard:node:read\u0026#34;, 47 \u0026#34;calendar:calendar:read\u0026#34;, 48 \u0026#34;calendar:calendar.event:create\u0026#34;, 49 \u0026#34;calendar:calendar.event:delete\u0026#34;, 50 \u0026#34;calendar:calendar.event:read\u0026#34;, 51 \u0026#34;calendar:calendar.event:reply\u0026#34;, 52 \u0026#34;calendar:calendar.event:update\u0026#34;, 53 \u0026#34;calendar:calendar.free_busy:read\u0026#34;, 54 \u0026#34;contact:contact.base:readonly\u0026#34;, 55 \u0026#34;contact:user.base:readonly\u0026#34;, 56 \u0026#34;contact:user:search\u0026#34;, 57 \u0026#34;docs:document.comment:create\u0026#34;, 58 \u0026#34;docs:document.comment:read\u0026#34;, 59 \u0026#34;docs:document.comment:update\u0026#34;, 60 \u0026#34;docs:document.media:download\u0026#34;, 61 \u0026#34;docs:document:copy\u0026#34;, 62 \u0026#34;docx:document:create\u0026#34;, 63 \u0026#34;docx:document:readonly\u0026#34;, 64 \u0026#34;docx:document:write_only\u0026#34;, 65 \u0026#34;drive:drive.metadata:readonly\u0026#34;, 66 \u0026#34;drive:file:download\u0026#34;, 67 \u0026#34;drive:file:upload\u0026#34;, 68 \u0026#34;im:chat.members:read\u0026#34;, 69 \u0026#34;im:chat:read\u0026#34;, 70 \u0026#34;im:message\u0026#34;, 71 \u0026#34;im:message.group_msg:get_as_user\u0026#34;, 72 \u0026#34;im:message.p2p_msg:get_as_user\u0026#34;, 73 \u0026#34;im:message.send_as_user\u0026#34;, 74 \u0026#34;im:message:readonly\u0026#34;, 75 \u0026#34;search:docs:read\u0026#34;, 76 \u0026#34;search:message\u0026#34;, 77 \u0026#34;space:document:delete\u0026#34;, 78 \u0026#34;space:document:move\u0026#34;, 79 \u0026#34;space:document:retrieve\u0026#34;, 80 \u0026#34;task:comment:read\u0026#34;, 81 \u0026#34;task:comment:write\u0026#34;, 82 \u0026#34;task:task:read\u0026#34;, 83 \u0026#34;task:task:write\u0026#34;, 84 \u0026#34;task:task:writeonly\u0026#34;, 85 \u0026#34;task:tasklist:read\u0026#34;, 86 \u0026#34;task:tasklist:write\u0026#34;, 87 \u0026#34;wiki:node:copy\u0026#34;, 88 \u0026#34;wiki:node:create\u0026#34;, 89 \u0026#34;wiki:node:move\u0026#34;, 90 \u0026#34;wiki:node:read\u0026#34;, 91 \u0026#34;wiki:node:retrieve\u0026#34;, 92 \u0026#34;wiki:space:read\u0026#34;, 93 \u0026#34;wiki:space:retrieve\u0026#34;, 94 \u0026#34;wiki:space:write_only\u0026#34; 95 ] 96 } 97} 为什么飞书机器人没有输入框 飞书机器人发布后，如果找不到输入框（类似下图），先别着急，需要继续进行配置。\n接收消息事件\n必须设置飞书应用的 App ID 和 App Secret 之后，OpenClaw 才会和应用进行一次握手通信。\n此时才可以进行事件配置中的「订阅方式」配置。否则，这里的配置是无法保存的。\n保存订阅方式为「使用长连接」之后，添加「接收消息」事件。\n然后，机器人的输入框就会出现。和机器人进行任意对话，得到配对码，填入OpenClaw 进行配对。\n和飞书机器人的对话，与 ArkClaw 中显示的是同一个Session，可以同时看到对话信息。\n建议买了Coding Plan的同学去领一下 整体测试下来，ArkClaw的优化还挺不错的，文件传输这些细节问题也都考虑到了。\n同样需要注意的是，为了试用这些功能，火山引擎会自动开通一系列服务。尽管不用不收费，但也要自行关注账单。\n如果还没有注册 Coding Plan，可以看上一篇文章： OpenClaw 选什么模型？GLM-5，MiniMax-M2.5，Kimi 2.5，火山方舟 Coding Plan 真实评测，里面有邀请码，看上哪个扫哪个就行。\n","date":"2026-03-07","description":"","lastmod":"2026-03-07T15:20:14Z","slug":"free-2c4g-host-openclaw-lobster","tags":["openclaw","llm","ai","ai-agent"],"title":"居然可以免费领一台2C4G主机安装OpenClaw养龙虾……免费！","url":"https://blog.zengrong.net/post/free-2c4g-host-openclaw-lobster/"},{"categories":["technology"],"content":"对于一个 AI Agent 来说，Skill 能力是核心。\nAgent Skills 是 Anthropic 牵头的一套开源标准，几乎所有的 Agent 都支持：https://agentskills.io\nOpenClaw 可以通过自行创建 Skill，不断扩展自己的能力。\n建议安装 Anthropic 提供的 skill-creator，方便龙虾自行创建 Skill。\n下面介绍 Skill 相关的工具和一个基于龙虾的 Skill 创建案例。\nnpx skills 工具 Skills 工具是 Vercel Labs 开源的，用于管理通用 Skill。它可以从 GitHub 或者其他仓库下载 Skills，保存到 ~/.agents/skills，然后通过符号链接绑定到其他 Agent。使用这种方式，让 Skills 易于维护。\nhttps://skills.sh/\n使用 npx 安装 skills：\n1# 标准命令 2npx skills add \u0026lt;owner/repo\u0026gt; 3 4# 安装曾老师写的 mcp-deploy 5npx skills add zrong/skills --skill mcp-deploy 6 7# 使用完整 github url 安装 find-skills 8npx skills add https://github.com/vercel-labs/skills --skill find-skills Skills 仓库 通过指定不同的 Skill 仓库名称，就能从不同的仓库中获取 Skill。\nClaude 官方： https://github.com/anthropics/skills Awesome OpenClaw: https://github.com/VoltAgent/awesome-openclaw-skills OpenClaw 官方： https://github.com/openclaw/skills 让墨汁发桌面截图给我 我的个人助理叫做「墨汁」，让她发一张桌面截图给我，但 OpenClaw 自带的飞书插件，不支持发图片。\n于是，我让墨汁给我写一个 Skill：\n前面你提到了 OpenClaw 的 Feishu 插件不支持上传图片。那么你是不是可以写一个飞书专用的 skill，用于支持上传图片？这样你就可以给我发图片了。\n我新建的所有 skill 都应该位于 ~/storage/ai_agent/skills/ 项目中。当我需要创建 skills 的时候，需要你在这里创建，并提交到 GitHub 仓库。然后再使用 npx skills 来安装这个 skill。记住这点。\n墨汁自动把 Skill 完成，并更新到我在 GitHub 上的 Skill 项目中了。\n曾老师会同时使用多个 Agent（不限于 OpenClaw），将 Skill 保存在 GitHub，方便在多个电脑的不同 Agent 中共享功能，也方便一次性部署。\n可以看看曾老师的 Skill 仓库，会不断更新：\nhttps://github.com/zrong/skills\n使用上面学到的内容安装这个 Skill：\n1npx skills add zrong/skills --skill feishu-image ","date":"2026-03-05","description":"","lastmod":"2026-03-05T15:16:05Z","slug":"openclaw-skill-feishu-screenshot","tags":["ai-product","llm","ai"],"title":"Skill 是 Agent 的核心：让龙虾 OpenClaw 给飞书发电脑桌面截图","url":"https://blog.zengrong.net/post/openclaw-skill-feishu-screenshot/"},{"categories":["technology"],"content":"我在 阿里云也推出了 39 元 Coding Plan 套餐，但我还是建议你买……（OpenClaw 的 GLM-5，MiniMax-M2.5，Kimi 2.5，火山方舟真实评测） 一文中提到过模型的选择。\n一个模型打天下，也不是不行。 但在模型高负载的时候，OpenClaw 可能会卡住很长时间，导致 AI 打工人效率不高。\n既然目前国内大模型的 Coding Plan 套餐不太贵，我们可以多购买几个套餐，让多个模型轮转，进一步压榨 AI 牛马的能量。\n下面看看如何在已经配置了单个智谱模型的前提下，新增火山方舟中的 Kimi-K2.5 作为轮转模型。\n如果需要购买火山方舟 Coding Plan，可以看上面的文章。\n[toc]\n配置火山方舟 Coding Plan 进入火山方舟 Coding Plan 的「开通管理」界面，选择启用 Kimi-K2.5：\n选择模型 1openclaw configure --section model 一路选择后，确认使用 volcengine-plan/kimi-k2.5：\n火山方舟的限制 如果不使用 Auto 模式，火山方舟 Coding Plan 一次只能使用一个模型，而且必须在火山引擎的后台手动切换。\n也就是说，如果在火山引擎后台切换了 Kimi-K2.5 模型（就像我上面截图所做的那样），OpenClaw 继续使用 volcengine-plan/ark-code-latest 就不会起作用。\n这是一个容易令人困惑的地方。\n切换模型 使用命令行，在 tui 界面中，@小助手，都可以切换模型。\n在 tui 界面中可以使用斜杠命令 /model 或者 /models 来切换模型。\n如果配置了 Channel（飞书或者其他），直接 @助手之后输入斜杠命令也可以生效。\n使用命令行，可以查看模型的配置情况：\n1openclaw models list 每次切换模型之后，为了保证立即生效，最好重启一次 gateway：\n1openclaw gateway restart ","date":"2026-03-04","description":"","lastmod":"2026-03-04T13:20:53Z","slug":"openclaw-model-fallback-volcengine","tags":["llm","coding","ai"],"title":"OpenClaw 的模型轮转配置（fallback）：以火山方舟Coding Plan为例","url":"https://blog.zengrong.net/post/openclaw-model-fallback-volcengine/"},{"categories":["use"],"content":"今天不得不用我的游戏机（Windows 11）工作一天，然而我已经无法习惯没有触控板的工作。\n所以，让我的 Magic Trackpad 3 工作在 Windows 11 上就至关重要。\n事实证明，这体验和 macOS 几乎一模一样。\n安装 mac-precision-touchpad https://github.com/imbushuo/mac-precision-touchpad\n按文档建议，使用 choco 安装：\n1choco install mac-precision-touchpad 由于我的电脑是 Windows 11，一定会遇到这个问题：\nmac-precision-touchpad v0.2105.3979 [Approved] mac-precision-touchpad package files install completed. Performing other installation steps. The package mac-precision-touchpad wants to run 'chocolateyInstall.ps1'. Note: If you don't run this script, the installation will fail. Note: To confirm automatically next time, use '-y' or consider: choco feature enable -n allowGlobalConfirmation Do you want to run the script?([Y]es/[A]ll - yes to all/[N]o/[P]rint): y\nERROR: Cannot be installed on this version of Windows. Requires Windows 10 x64. The install of mac-precision-touchpad was NOT successful. Error while running 'C:\\ProgramData\\chocolatey\\lib\\mac-precision-touchpad\\tools\\chocolateyInstall.ps1'.\n解决问题 编辑这个文件：\n1C:\\ProgramData\\chocolatey\\lib\\mac-precision-touchpad\\tools\\chocolateyInstall.ps1 注释或者删除这一段：\n1# Check OS version - only works on Windows 10 2Write-Debug \u0026#34;OS Name: $($env:OS_NAME)\u0026#34; 3if ($env:OS_NAME -ne \u0026#34;Windows 11\u0026#34;) { 4 throw \u0026#34;Cannot be installed on this version of Windows. Requires Windows 10 x64.\u0026#34; 5} 使用管理员权限运行 Windows 终端，使用 PowerShell 执行：\n1cd C:\\ProgramData\\chocolatey\\lib-bad\\mac-precision-touchpad\\0.2105.3979\\tools 2chocolateyInstall.ps1 开启蓝牙，搜索妙控板并连接 Magic Trackpad 3 那个鼠标设备就是妙控板。\n然后就可以在游戏机上愉快地工作了。\n顺便说一句，Lightning 接口，送编织线的深空灰 Magic Trackpad 在 macOS 上也被识别为 3 代。但它的型号 A1535 好像是和 2 代的芯片一样，外观变成了磨砂效果。\n","date":"2026-03-01","description":"","lastmod":"2026-03-01T14:21:28Z","slug":"smooth-magic-trackpad-3-on-windows11","tags":["macos","hardware"],"title":"无比顺滑：在 Windows 11 中使用 Magic Trackpad 3","url":"https://blog.zengrong.net/post/smooth-magic-trackpad-3-on-windows11/"},{"categories":["technology"],"content":"春节前，我写过一篇 Kimi 2.5，GLM 4.7，MiniMax 2.1 几个国内大模型 Coding 套餐真实对比 的文章，比较了这些套餐的价格和特点。\n春节过去了，MiniMax 升级了 M2.5，GLM 升级到了 5，Kimi 也推出了 Kimi Claw 抢占龙虾热度。不得不说，所有的厂商都很拼。\n狂飙的 MiniMax 2.5 MiniMax 2.5 的 Ultra 极速版本，价格快赶上 Claude Max 套餐了（当然还比不上 Claude 的 5x 模式）。\n从我上次 MiniMax m2.1 + GLM 4.7 + Kimi 2.5 的测试来看，它的表现还真不错。\n实际使用中，MiniMax 还是挺贵的，下面也只是和 OpenClaw 聊了十几分钟而已：\nToken 用起来挺贵，所以购买 Coding Plan 是必要的。49 元的 Plus 套餐用来测试足够了，119 元的 Max 套餐也挺良心，用量相当于其他平台的 Pro 级别，足够满足日常需求了。\n要体验 MiniMax-M2.5 可以用我的链接注册：\n🎁 MiniMax 跨年福利来袭！邀好友享 Coding Plan 双重好礼，助力开发体验！ 好友立享 9 折专属优惠 + Builder 权益，你赢返利 + 社区特权！ 👉 立即参与：https://platform.minimaxi.com/subscribe/coding-plan?code=HrjTu8vtQU\u0026amp;source=link\n智谱 Coding Plan 套餐的变化 智谱 GLM Coding Plan 套餐在之前只有 5 小时限额，并无周限额。但现在也加入了周限额。为了避免老用户的不满，特地设置了老用户回归窗口期。\n以每个 Prompt 对应 15~20 次调用的标准，智谱新版 Pro 套餐量级与火山方舟和阿里云基本相同。\n有趣的是，这个套餐里还提供了 视觉理解 MCP Server、网络搜索 MCP Server、网页读取 MCP Server、开源仓库 MCP Server，可以直接配置在 OpenClaw 中使用，为你的小龙虾提供网络搜索和图像识别功能。\nOpenClaw 默认使用 Brave、Perplexity 或 Gemini 这些付费工具来搜索。我也可以让 OpenClaw 使用智谱 GLM Coding Plan 提供的网络搜索工具：\n要体验 GLM-5 可以通过我的链接注册：\n🚀 速来拼好模，智谱 GLM Coding 超值订阅，邀你一起薅羊毛！Claude Code、Cline 等 20+ 大编程工具无缝支持，“码力”全开，越拼越爽！立即开拼，享限时惊喜价！ 链接：https://www.bigmodel.cn/glm-coding?ic=1LH1BCP5SZ\n阿里云百炼和火山方舟 Coding Plan 的比较 平台 模型 价格 阿里云百炼 Qwen3.5-Plus、Qwen3-Max、Qwen3-Coder-Next、Qwen3-Coder-Plus、GLM-5、Kimi-k2.5、GLM-4.7 39 首月，100 次月，200 三月，首季度合计 339 元 火山方舟 Doubao-Seed-2.0-Code、Doubao-Seed-Code、Kimi-K2.5、Kimi-K2、GLM-4.7、Deepseek-V3.2 首季度 270 元 抛开两家自己的模型 Doubao 和 Qwen，模型上的最大的区别，就是火山方舟 没有 GLM-5 模型。\n量级区别呢？不能说差别不大，只能说 一模一样：见下图，左边火山方舟，右边阿里云百炼。\n有趣的是，火山方舟的 Coding Plan 支持一个 Auto 模式，还是挺方便的。在我的测试过程中，大多数情况下，Auto 模式都在调用 Doubao-Seed-2.0-Code 模型。\n体验火山方舟可以使用我的链接注册：\n方舟 Coding Plan 支持 Doubao、GLM、DeepSeek、Kimi 等模型，工具不限，现在订阅折上 9 折，低至 8.9 元，订阅越多越划算！立即订阅：https://volcengine.com/L/gKwQecTsvhc/ 邀请码：SCPTJYMH\n要体验阿里云百炼 Coding Plan 可以通过我的链接注册：\n大模型 4.5 折起，最高得 3000 元。 https://www.aliyun.com/benefit/ai/aistar?clubBiz=subTask..12410168..10263..\nOpenClaw 的选择 优秀的小龙虾不卡壳，自然是全都要了。\n在曾老师的使用过程中，发现北京时间下午 GLM-5 模型经常会卡壳数分钟之久，配置轮转（follback）模型是非常必要的。因为 MiniMax 太贵，而我的 GLM 套餐是老版本量大管饱，所以用 GLM-5 做主力，火山方舟的自动模型（一般是 Doubao-Seed-2.0-Code，当然也可以放弃 Auto 模式，手动指派 Kimi 2.5），使用 MiniMax-M2.5 来兜底。\n","date":"2026-02-26","description":"","lastmod":"2026-03-04T13:15:53Z","slug":"aliyun-coding-plan-recommendation","tags":["llm","coding","ai"],"title":"OpenClaw 选什么模型？GLM-5，MiniMax-M2.5，Kimi 2.5，火山方舟 Coding Plan 真实评测","url":"https://blog.zengrong.net/post/aliyun-coding-plan-recommendation/"},{"categories":["media"],"content":"大年初七，春节档激战正酣。在红果漫剧榜上，佳作频出，而稳居 TOP1 的《西游，错把玉帝当亲爹》更是现象级存在。今天，曾老师就来深度拆解这部作品，看看它究竟凭何实力“一剧独大”。\n[toc]\n概览 目标受众：聚焦年轻男性群体。 表现形式：横屏 2D 漫剧。 核心基调：基于西游 IP 进行大胆二创，风格轻松诙谐。 视觉亮点：在处理主角内心独白与深度思考时，切换至 Q 版画风。这种“打破第四面墙”的视觉转换，如今已成为精品漫剧的标配，有效调整叙事节奏，增加了趣味性。 剧情拆解 第一部分：钩子 启动钩子是「给五指山贴瓷砖」，足够吸引人看 1 分钟。 给猴哥送饭，赚 80% 好感度。利用观众对于西游角色的熟悉感，拉高后续结论的期待值。 捡到便宜老爹玉帝。 系统给新手奖励。 创意开局：“给五指山贴瓷砖”的荒诞设定，瞬间抓住观众眼球，留存率高。 情感铺垫：通过给猴哥送饭赚取好感度，利用观众对西游角色的固有认知建立情感连接，为后续反转蓄势。 核心冲突：捡到“便宜老爹”玉帝，系统新手奖励随即到账。 启动即高潮，系统与现实的巨大反差成功勾起观众对后续剧情的强烈期待。\n第二部分：天庭风云（反腐篇·第 4 集起） 玉帝与猴哥的正面对峙，张力拉满。 天庭版《仙民的名义》，天庭反腐失败， 张之秋给出反腐新方案。 第三部分：情感线与爽点（老婆篇·第 11 集起） 给主角送了一个老婆（第 11 集），首席家畜牛魔王出场。\n首席家畜牛魔王亮相，玉面狐狸招赘婿引发家庭伦理大戏。 牛魔王和铁扇公主对峙。 一箭干掉太乙金仙巅峰的黄风怪，坐实猪吃老虎。 老婆主动上门。 第四部分：光环加身（成长篇·第 16 集起） 开始强化主角光环，渲染佛帝之争。\n强悍宠妻，还有黑丝…… 奇葩设定：睡觉涨修为、黑丝元素点缀、厨艺可增仙人修为。 战力热点：收服牛魔王，秒杀灵吉菩萨，玉帝更是突破至准圣巅峰。 第五部分：新冲突（王母·第 33 集） 坐实玉帝“无辜受害者”人设。虽看似支线繁杂，但每个小故事都精彩绝伦，插科打诨中引人会心一笑。\n王母娘娘得知玉帝的私生子。 收编首席家禽陆压。 妖孽……饶命（现在的秃驴还真会审时度势） 粉碎李天王之塔，哪吒反水。 王母八卦玉如意。 第六部分：新女主（杨婵 第 49 集） 哮天犬爱白骨精的老梗。玉帝拜杨戬的反差。嫦娥演讲引发打工人共鸣。\n首席吃瓜官下凡。 玉帝下象棋。 搬家：留下六耳猕猴的线索。 玉帝拜杨戬。 喜欢白骨精的杨戬朋友。 嫦娥的打工人独白。 第一季终章 ：金蝉子（第 59 集） 人设颠覆：油腻版金蝉子登场，打破传统印象。 精彩的独立 Q 版故事，结束第一季 。 金蝉子被沙和尚干掉 9 次。脑补猴子、杨戬、哪吒取经组合。 音乐 剧集的音乐选择全程在线，音乐音效与场景的配合完美。\n牛魔王铁扇公主对峙的二胡、古筝，天庭侍卫八卦配乐妙趣横生，嫦娥演讲段落，琵琶、竹笛与小提琴、钢琴的中西合璧，将情绪烘托至顶点，极大提升了观剧沉浸感。\n融梗和反串 剧集在“玩梗”与“反串”上拿捏得恰到好处，既熟悉又陌生：\n审美解构：审美什么时候这么抽象了？好好一座山贴得和天庭的公共茅厕一样。 YY： 黑丝…… 仙级普通人：不许悔棋的玉帝，财色兼得的入赘牛魔王 打工人： 刚到天庭斩杀线的沙和尚，说四川话的海贼王的信鸽 社会事件：现在的秃驴还真会审时度势 跨界乱入：乱入忍辱负重的下蛋公鸡，杨戬的朋友喜欢白骨精 好在哪里？ 单论画面精细度，《西游，错把玉帝当亲爹》或许并非春节档最顶尖之作，甚至偶有画风不统一、人物前后不一致的瑕疵。然而，它的综合完成度远超同类竞品。\n制作成熟度：镜头语言老练，音乐制作精良，整体质感比榜单大多数作品高出一个维度。 剧本还原与创新：在优秀本体剧本的基础上，制作团队展现了极高的还原度与巧思。如对如来佛狡诈神情的微表情刻画、人物“石化”效果的运用等传统动画技法，在此剧中显得游刃有余。 社会情绪的出口：弹幕中高频出现的“求第二季”与剧情讨论，证明了其用户粘性。特别是嫦娥那段 2 分钟的演讲，关于体制、腐败、关系网与公平期待的探讨，精准击中了当代年轻人的心声，引发了广泛共鸣。 选择西游题材，在春节档推出一部轻松诙谐的漫剧，并在嬉笑怒骂中影射时代症结，是本作的风格特点。它没有《斩仙台》那般苦大仇深，却巧妙贴合了当下的文化符号——从《黑神话：悟空》的叛逆猴哥和真诚杨戬，到《哪吒之魔童闹海》的正面魔丸，再到热爱白骨精的杨戬小弟。这一切元素的有机融合，造就了这部春节霸榜爆款。\n当下的漫剧市场处于“战国时代”，每日数百部新作上线，供给远超需求。尽管粗制滥造的解说剧仍占据榜单一席之地，但《玉帝爹》也证明了，用心打磨剧本、精准捕捉情绪、巧妙融合经典与创新的作品一定能持续霸榜。\n","date":"2026-02-23","description":"","lastmod":"2026-02-23T06:12:01Z","slug":"daily-review-journey-west-jade-emperor","tags":[],"title":"每日拉片 | 春节霸榜剧《西游，错把玉帝当亲爹》","url":"https://blog.zengrong.net/post/daily-review-journey-west-jade-emperor/"},{"categories":["technology"],"content":"近期国产 Coding 大模型开启了促销模式，曾老师在实际开发的项目中进行了尝试，下面说说体验。\n顺便说一句：只有在真实项目中使用这些模型，才能感觉到具体的差异。 仅仅靠一句提示词或者一个 DEMO 来判断模型能力（你知道我在说什么），是非常不靠谱的行为。\n所有测试均使用 Claude Code 进行，使用 CC Switch 切换。\nMiniMax m2.1 Plus 会员，49元每月，100 prompts 每 5 小时。\n基本能满足一个 真实的摸鱼打工人单 Agent 对话。如果勤奋一点，或者并行多开 1 个 Agent 的话，这个量级是不够的。\n要体验可以通过我的链接注册。\n🎁 MiniMax 跨年福利来袭！邀好友享 Coding Plan 双重好礼，助力开发体验！ 好友立享 9折 专属优惠 + Builder 权益，你赢返利 + 社区特权！ 👉 立即参与：https://platform.minimaxi.com/subscribe/coding-plan?code=HrjTu8vtQU\u0026amp;source=link\nGLM 4.7 Coding Pro 会员，100 元每月，600 prompts 每5小时。\n基本用不完。 曾老师开着 3 个 Agent 并行，也用不完。\n要体验可以通过我的链接注册。\n🚀 速来拼好模，智谱 GLM Coding 超值订阅，邀你一起薅羊毛！Claude Code、Cline 等 20+ 大编程工具无缝支持，“码力”全开，越拼越爽！立即开拼，享限时惊喜价！ 链接：https://www.bigmodel.cn/glm-coding?ic=1LH1BCP5SZ\nKimi Code K2.5 Andante 会员，49每月，官网说明每 5 小时能支持 300-1200 次 API 请求。\n完全不够用。 单 Agent 下，20分钟就触发了 5 小时限额。不到 2 天就干完了一周的额度。\n顺便说一句，Kimi 开源的 Kimi Code CLI / Kimi Code for VS Code 编程工具，我测试无法正常使用（网络RETRY），但通过 Claude Code + API Key 是可以正常使用的。\n火山方舟 Pro Plan， 首月 49.9，6000次请求每 5 小时。相当于 GLM 的 10 倍。\n量大管饱。 重点是能同时用 Kimi-K2.5/ GLM-4.7 /DeepSeek v3.2。买 300 的包季套餐，不但能 Coding，接入 Dify 也能随便用了。\n要体验可以通过我的链接注册。\n方舟 Coding Plan 支持 Doubao、GLM、DeepSeek、Kimi 等模型，工具不限，现在订阅折上9折，低至8.9元，订阅越多越划算！立即订阅：https://volcengine.com/L/gKwQecTsvhc/ 邀请码：SCPTJYMH\n总结 注意， Andante 是 Kimi Code 的初级套餐，其余均为中级套餐。\n模型 套餐 价格 每 5 小时请求 主观判断 MiniMax M2.1 Plus 49 100 轻量够用 GLM 4.7 Coding Pro 99 600 量大管饱 Kimi K2.1 Andante 49 300-1200 次 轻量不够用，周月限额 火山方舟 Pro 49.9 首月 6000 次 多模型切换，总量限额 ","date":"2026-02-03","description":"","lastmod":"2026-03-04T13:19:26Z","slug":"compare-kimi-glm-minimax-coding-plan","tags":["llm","coding","ai"],"title":"Kimi 2.5，GLM4.7，MiniMax 2.1 几个国内大模型 Coding 套餐真实对比","url":"https://blog.zengrong.net/post/compare-kimi-glm-minimax-coding-plan/"},{"categories":["technology"],"content":"当然还是标题党，其实也没有（狡辩）。省略号的部分应该是「历史沿革图」。\n早上起来打开 Gemini 发现 Nano Banana Pro 更新了，那当然是 Try now 啊！\n[toc]\n来一个全球小游戏市场的历史沿革图 DUANG 的一下就完成了。\n你信不信？反正我不信。\n其实曾老师的提示词是这样的：\n1创建一个充满未来科技感的信息图表，主题为全球小游戏市场的发展历史。 2 3**整体风格：** 4背景是深蓝色的电路板和数据流图案，带有发光的节点和全息投影效果。所有的图标、箭头和文本框都应具有现代化的3D渲染效果，边缘带有蓝色或金色的光晕，看起来像是悬浮在屏幕上的全息界面。 5 6**标题：** 7在顶部中央位置，用发光的蓝色中文字体显示标题：“全球小游戏市场：多并行时间线历史沿革图 (中文版)”。 8 9**主要结构：** 10图表分为三条平行的横向时间线，从左向右延伸，末端有发光的箭头指示方向。最右侧有一个独立的“未来展望”区域。 11 12**第一条时间线（顶部）：国内主流平台** 13* 左侧标签：“国内主流平台”。 14* **节点1 (2017-18 起源/爆发):** 上方是“2017-18 (起源/爆发)”。下方有两个3D图标：一个是发光的绿色微信气泡，旁边是一个正在跳跃的3D像素小人（跳一跳）。下方文字：“微信：跳一跳开启元年，H5爆发。” 15* **节点2 (2019-20 超休闲/算法):** 上方是“2019-20 (超休闲/算法)”。下方有两个3D图标：一个带有数据柱状图的字节跳动标志，旁边是一枚红白相间的火箭（Ohayoo）。下方文字：“字节：算法推荐+Ohayoo，超休闲统治。” 16* **节点3 (2021-22 重度化/双雄):** 上方是“2021-22 (重度化/双雄)”。下方有两个3D图标：交叉的剑盾徽章，旁边是一块发光的蓝色芯片（ENGINE）。下方文字：“双雄并进：重度化IAP，引擎升级，内卷。” 17* **节点4 (2023-24 生态融合/精品):** 上方是“2023-24 (生态融合/精品)”。下方有两个3D图标：带有播放按钮的直播图标，旁边是一座发光的3D城堡。下方文字：“生态融合：直播带玩，精品化，存量博弈。” 18 19**第二条时间线（中间）：国内其他尝试** 20* 左侧标签：“国内其他尝试”。 21* **节点1 (2017-18):** 上方是“2017-18”。下方是一个3D的QQ企鹅图标。下方文字：“QQ：空间引流，休闲起步。” 22* **节点2 (2019):** 上方是“2019”。下方是一个带有“du”字样的百度熊掌图标，旁边是一个搜索框。下方文字：“百度：搜索入口尝试发现。” 23* **节点3 (2020-24 工具属性/稳定):** 上方是“2020-24 (工具属性/稳定)”。下方是一组图标：一个看起来生锈或老旧的QQ企鹅，紧接着是小米(mi)、OPPO、vivo的Logo组合，最后是一个蓝色的支付宝“支”字图标。下方文字：“QQ苟延残喘。快游戏/支付宝：硬件/工具属性，场景融合。” 24 25**第三条时间线（底部）：海外市场与新兴平台** 26* 左侧标签：“海外市场与新兴平台”。 27* **节点1 (2017-18 海外先驱):** 上方是“2017-18 (海外先驱)”。下方是一个3D蓝色的Facebook Messenger图标。下方文字：“FB Instant Games：海外H5先驱，社交裂变。” 28* **节点2 (2019-22 广告/AR尝试):** 上方是“2019-22 (广告/AR尝试)”。下方有两个3D图标：彩色的Google Play三角形图标，旁边是黄色的Snapchat幽灵图标。下方文字：“Google/Snap：试玩广告，AR小游戏尝试。” 29* **节点3 (2023-24 社区浪潮):** 上方是“2023-24 (社区浪潮)”。下方有三个3D图标：紫色的Discord图标，蓝色的Telegram纸飞机图标，红色的Reddit外星人图标。下方文字：“新兴平台：社区驱动新浪潮。” 30 31**右侧区域：未来展望** 32* 在三条时间线的尽头右侧，有一个从底座向上投射的全息显示区域，标题为“未来展望”。 33* 内部有两个发光的3D立方体图标：左侧是一个发光的大脑形状（代表AI），下方文字“AIGC”；右侧是一个连接着网络的立方体结构，下方文字“Web3”。 34 35**左下角图例：** 36带有两个小图标和文字：“关键里程碑”（对应三角形图标）和“游戏平台/类型”（对应立方体图标）。 如果你觉得上面的提示词太复杂，也可以用这个简化版 ：\n1创建一个科幻风格的3D信息图表，标题为“全球小游戏市场：多并行时间线历史沿革图 (中文版)”。背景是发光的蓝色电路和数据流。 2 3图表包含三条平行的、带有发光箭头的时间线，从左到右展示历史进程。 4 5**第一条时间线（顶部）：国内主流平台** 6* 展示从2017年到2024年的四个阶段。 7* 阶段1：微信和跳一跳的3D发光图标，主题是“起源/爆发”。 8* 阶段2：字节跳动和火箭的3D图标，主题是“超休闲/算法”。 9* 阶段3：剑盾和芯片的3D图标，主题是“重度化/双雄”。 10* 阶段4：直播和城堡的3D图标，主题是“生态融合/精品”。 11* 每个阶段下方都有详细的中文说明文字，描述该阶段的市场特征。 12 13**第二条时间线（中间）：国内其他尝试** 14* 展示QQ（早期）、百度（搜索尝试失败），以及后期QQ（衰落）、手机厂商（小米/OV）和支付宝的工具化发展路径。 15* 图标要反映其状态，例如后期的QQ图标应显得老旧。 16 17**第三条时间线（底部）：海外市场与新兴平台** 18* 展示Facebook（早期H5先驱）、Google/Snapchat（广告/AR尝试），以及Discord/Telegram/Reddit（社区浪潮）的发展。 19* 使用各自平台极具辨识度的3D现代化Logo。 20 21**右侧：未来展望** 22* 在时间线终点，有一个全息投影台，展示关于“未来展望”的两个核心3D图标：AIGC（大脑）和Web3（网络立方体）。 23 24确保所有元素都具有高质量的3D渲染、发光和全息效果，整体色调为科技蓝和金色。 如果你不相信上面的提示词是曾老师写的， 继续往下看：\n来一个微信小游戏历史沿革图 DUANG 的一下就完成了。\n你还别不信，我给你看记录：\n当然，在没有看到的部分，Gemini 给了我一段 Mermaid.js 的源码，我就不贴出来了，直到我给了它一张参考图：\n来一个字节跳动（抖音）小游戏历史沿革图 接下来 DUANG 的一下生成字节跳动（抖音）小游戏的图。\nGemini 聊天记录： 生成字节小游戏历史沿革图\n来一个全球小游戏市场历史沿革图 这个提示词稍微复杂点：\n同样的，我需要整个小游戏市场发展的历史沿革图。\n你需要考虑刚才已经完成的字节跳动小游戏、微信小游戏、以及苟延残喘的 QQ 小游戏（多个阶段）、昙花一现的百度小游戏、手机厂商小米、OPPO、VIVO 的快游戏、支付宝小游戏等等尝试，再结合海外 Facebook 、Reddit、Discord、Youtube、Google、Telegram 等超级 App 中小游戏的发展，给我一个完整的小游戏市场历史沿革图。\n然而，Gemini 没有理解「并行时间线」的含义，直到我给了它一张塞尔达的图作为参考。\n果不其然，Gemini 使用了哈拉路大路地图作为背景，图标也改成了塞尔达风。\n但我不需要这些 。经过两轮迭代之后，Nano Banana Pro 给出了比较满意的效果。最终效果图就是文章开头的那张。\n聊天的过程很顺畅，能感觉到 Gemini 对于语义理解的增强。\n开头的提示词怎么来的？ 只需要再次问 Gemin 下面这句话就行：\n如果直接要求你生成上面那张图，在一个全新的上下文中，要使用哪些提示词来询问你呢？\n重开一个新的上下文窗口，看看生成的效果，还挺不错：\n附赠网友的优秀实践 AI 漫剧和短剧制作设施\n原神游戏新生态\n我的世界历史沿革图\n塞尔达传说历史沿革图\n指环王人物世界树\n全文完 ","date":"2025-11-21","description":"能感觉到 Gemini 对于语义理解的增强。","lastmod":"2025-11-21T02:17:24Z","slug":"nano-banana-pro-minigame","tags":["AI"],"title":"Nano Banana Pro 一句话，小游戏生成了","url":"https://blog.zengrong.net/post/nano-banana-pro-minigame/"},{"categories":["technology"],"content":"昨天的文章 小游戏裂变：1 人分享 100 次是怎么做到的？ 中，谈到了微信小游戏的兜底分享机制。\n取消微信默认提供的兜底分享机制 文章发出后，胡扯群友们进行了激烈的讨论，不止一位群友给出了微信官方提供的兜底方案取消方法：\n在广告创建阶段，针对广告实例禁用即可：\n1rewardedVideoAd = wx.createRewardedVideoAd({ 2 adUnitId: \u0026#39;xxxx\u0026#39;, 3 disableFallbackSharePage: true 4}) 禁用兜底分享之后，就可以使用 RewardedVideoAd.onError() 来获取拉取广告失败的事件，然后给出游戏自己的处理逻辑。\n取消默认兜底机制之后，自己应该做什么呢？毕竟不能看广告，游戏就没有收益，开发者也没钱赚，玩家可能会转向其他的游戏。总不能不让玩家玩游戏吧？\n不让玩家玩游戏，为什就不是一个选择呢？欢迎在评论或者文章底部加入群聊讨论。\n官方文档在这里：\nhttps://developers.weixin.qq.com/minigame/dev/guide/open-ability/ad/rewarded-video-ad.html\n99% 的分享是假分享 在 2019 年上半年以前，微信小游戏和小程序，是支持分享回调的。\n也就是说，开发者可以知道，一次分享是否成功到一个账号或者微信群。我记得当时有很多群的建立就是为了分享得到收益。\n甚至，还能检测到玩家是否分享到了同一个群。（过去多么美好呢……）\n但 2019 年 9 月以后（如果我记错了欢迎指正），微信关闭了这个功能。\n这也就意味着，大家玩到的大部分小游戏提供的「分享获得收益」功能，都是假的。\n判断真假很简单，分享给「文件传输助手」，或者在分享的过程中取消分享，看能不能得到收益。如果一次不行，就多试几次（注意调整分享或取消的节奏） 😄\n什么是真正有效的分享 真正有效的分享，是「分享出去的数量」还是「让真实的新用户注册」呢？\n相信昨天的文章 小游戏裂变：1 人分享 100 次是怎么做到的？ 已经提供了答案。\n有效分享的目标：让真实的新用户注册。 因为这些新增能弥补无法看广告而流失的收益（用赚到的 CPI 填补 ARPU 的损失）。\n有效真分享可以有。这需要服务器的支持：\n开发者以在分享出去的卡片中，带上玩家的一次性 ID 和小游戏 ID 信息，甚至还能包含场景信息。 点击卡片的玩家，在登录服务器的时候，将这些卡片中包含的信息传递到服务器。 服务器通过登录玩家信息（老玩家+新玩家），建立起分享玩家信息与被分享玩家信息之间的联系。 游戏根据新增玩家的数量，给分享玩家发奖励。 下面就是个不错例子：\n小游戏的服务器模块 我之前写过一个游戏服务器模块太监系列，可以参考：\n玩家抱怨丢失进度怎么办？解决小游戏小团队服务器进度存储和外挂问题 小游戏客户端和策划需要了解的服务器模块：静默登录与绑定账号 配置+公告+白名单+邮件：小游戏开发者需要了解的服务器模块 游戏版本管理：小游戏开发者需要了解的服务器模块之三 全文完 ","date":"2025-11-19","description":"真分享是可以有的。","lastmod":"2025-11-19T03:48:07Z","slug":"weixin-share-true","tags":["gamenote"],"title":"小游戏裂变：假分享骗不到玩家了，这次来真的！","url":"https://blog.zengrong.net/post/weixin-share-true/"},{"categories":["technology"],"content":"你如果真的是进来看怎么做到的，那么很可能会失望了。\n当然，本文并没有标题党， 从数据看，的确是能做到，但我们应该尽量避免。\n因为这是一个被动数据。\n下面进入正文。\n1275次分享 曾老师刷数据的时候发现了一个分享次数尖峰，百思不得其解，这不科学啊……\n然后看了一眼分享用户数： 23 人！\n也就是说，平均每人有几十次分享。这当然不是一个好现象。\n这种超高频率的分享，大部分是无效的。这也带来了次留的变化（注意，这天并不是周日）：\n如何做到的？ 我在 eCPM必然衰减！IAA与混合变现游戏调优的底层认知 一文中提到过：\n你的游戏超级牛逼，让玩家欲罢不能，今天看10个广告，明天看20个广告，后天看30个广告。这不会让ARPU更高， 广告平台不喜欢这样的「看广告专业户」，这类用户的 eCPM 会逐日衰减。 玩家每天都有流失，这些流失的玩家数量，与你留存玩家增加的广告次数形成对冲，导致你产品的总广告次数不一定是增长的。 这就是为什么 IAA 产品相对于 IAP 产品，对 首日回收 要求更高的原因。\nIAA 游戏后期，用户价值必然衰减。但如果产品中包含IAP，利用玩家付费后对游戏的依赖，以及中后期的产品内容对于免费玩家的转化和对于付费玩家的吸引，游戏就有了足够持久的生命力。\n平台不愿意没有价值的用户持续看广告，因为他们看再多广告也带不来有效的转化（点击）。\n所以平台会采用这样的策略：不给你展示广告，自动转换成分享。 就像下面这样。\n在以前，我们还需要程序员判断广告拉取是否成功，自行转换成广告。\n但现在，平台已经自动做了这件事。\n这就是上面提到现象的真正原因了。\n我们怎么做？ 广告之间的 CD。许多 IAA 游戏都会在看广告之间给出 CD，拉长游玩时长的同时也限制了广告频率。 限制广告次数。对于一些重要的广告点，限制每天能使用广告的次数。 使用体力类限制。对于平台来说，游戏时长自然是越长越好。但对于流量主来说，过长的，没有广告转化的游戏时长，不但没有经济价值，也会让玩家对游戏的兴趣迅速降低。可以使用体力限制来控制玩家在游戏中的停留时长，让他们有兴趣第二天再进入游戏。 全文完 ","date":"2025-11-18","description":"这是一个被动数据。","lastmod":"2025-11-18T04:07:21Z","slug":"minigame-auto-share","tags":["gamenote"],"title":"小游戏裂变：1 人分享 100 次是怎么做到的？","url":"https://blog.zengrong.net/post/minigame-auto-share/"},{"categories":["impressions"],"content":"本文翻译自：When Stick Figures Fought: A story about Flash, plus news。\n原文名称：When Stick Figures Fought: A story about Flash, plus news 原文地址： https://animationobsessive.substack.com/p/when-stick-figures-fought 原文作者：Animation Obsessive 译文名称：当火柴人停止战斗 中文翻译已获得原作者授权。若需要转载本文（中文版本），请先向原作者获取授权。\n胡扯游戏的老读者都知道，曾老师也曾经是个闪客。Flash 伴随了我最能熬夜的那段时间…… 1997~2015。\n博客早期 就有一篇 Good Bye Flash 抒发我对 Adobe 的恨意。\n在「曾嵘胡扯的地方」公众号 中我也发过： Good Bye Flash。\n胡扯社群的朋友大多在游戏行业从业，我们知道，火柴人可是游戏行业中的大 IP。\n火柴人与我而言，是记忆也是情感，是过去也是现在。所以，当我看到这篇原文的时候，迫不及待就找原作者拿到了翻译授权。\n文章较长，我也需要一篇篇整理和阅读外链。花了许多碎片时间，终于整理完毕。\n以下是正文。\n顺便说一句，Github 上有个用 Rust 写的开源 Flash 模拟器。如果你有一些保存的 swf 文件，欢迎发给我：\nhttps://github.com/ruffle-rs/ruffle\n1. Flash和闪客 在 YouTube、TikTok 或任何社交媒体出现之前，Flash 是主流。\n它改变了互联网。Shockwave Flash (.SWF) 文件格式使得动画和游戏即使在拨号连接（56kb/s）上也能流畅运行。虽然现在这个格式已经不再使用了，但在它最辉煌的时期，全球的网页设计师都用它来让网站显得更加现代化和流畅。\n曾老师：记住我在文章开头提到的 Ruffle，如果你有 swf 文件，欢迎发给我。\n另外还有一个群体也转向了 Flash，他们是业余动画师。这个软件非常容易上手，用它创作的内容也方便分享，从而引发了第一次在线动画热潮。一个只有 PC 的独立艺术家就能触达数百万观众。\n这在美国是个大事，但在某些方面，在中国，Flash 的影响更为深远。Flash 在 90 年代末传入中国，成为了一个时代的标志。所谓的「Flasher」（闪客）用年轻一代的全新声音发声，他们的作品同时吸引了媒体和公众的关注。\n「中国地下电影和摇滚音乐曾经是新的文化维度追求......，」学者吴伟华曾经写道，「但今天这些激情是通过 Flash 的渠道表达的。」 1\n21 世纪初，中国的网吧里挤满了观看 Flash 动画的人。2 其中最受欢迎的是《小小》（2000-2002），一部关于火柴人的暴力动作系列。它风格简单，极具时代特色，并被无数次模仿。很快，它就超越了中国，成为风靡全球的现象级作品。\n上图为小小 5 号作品片段\n对于特定年龄段、在网络时代成长起来的人来说， 《小小系列》 及其衍生作品是他们生活的一部分。这部动画系列并不深刻，也没有什么内涵：只有功夫、枪战、血腥和混乱。但它对当时的（大多是年轻）观众来说，却是酷炫的代名词。它也是许多人接触 Flash 动画的入门之作。\n无论在中国还是国外，情况都是如此 。《小小系列》 在 Newgrounds 和 Albino Blacksheep 等托管 Flash 文件的西方网站上大受欢迎。它在台湾、韩国、日本以及其他地区也拥有极高的人气。这些凶悍的小火柴人——以及他们让人联想到香港动作片和 《黑客帝国》的动作 ——风靡全球。\n2. 小小是谁 然而，这个系列是中文作品。它的创作者朱志强当时只有二十多岁。而且，他的走红也并非易事。\n朱志强并没有接受过动画方面的专业训练。在发现 Flash 之前，他靠做平面设计师勉强糊口。他对技术也并不精通。直到 1997 年，他从东北的吉林市搬到北京后，才第一次接触到电脑。\n那几年很艰难。朱志强初中辍学，在北京的第一份工作月薪 800 元人民币——按通货膨胀率计算，大约相当于 181 美元。他不停地加班来补贴家用。事实上，对他来说，能找到工作有时甚至需要撒谎。他曾谎称自己毕业于一所「技校」，但没带毕业证， 才找到一份工作。3\n曾老师： 那时候的 800人民币肯定可换不到 181 美元。\n尽管如此，小朱依然充满热情。他痴迷于火柴人。1989 年，年仅 14 岁，他就开始在书本的角落里绘制火柴人动画，通过翻动书页让角色动起来。他的灵感一部分来自成龙的电影，另一部分则源于他童年对《龙珠》的热爱。\n世纪之交，小朱通过软件重拾了这份热情。\n2000 年 4 月，小朱制作了他的第一部虚拟火柴人格斗动画《独孤求败》 ，当时正值 Flash 动画风靡中国之际。Flash 动画在中国通过闪客帝国等网站迅速发展。几个月前，一部名为《强盗的天堂》4 的暴力 Flash 动画走红，开启了 Flash 动画制作的新时代。该动画的创作者成为了他的「偶像」。\n上图为老蒋2000年2月制作的《强盗的天堂》\n小朱最初使用 Corel Painter 制作了动画《独孤求败》，但很快便转向了 Flash。他几乎是白手起家：「我毫无电脑技能，英语也不好，不会编程，绘画能力也只是平平。」他基本上是自学 Flash，用鼠标逐帧绘制。这是他的创作方式。2000 年年中，小朱制作了《独孤求败》的续集《小小2号》。5\n这些早期项目非常受欢迎，朱志强因此在北京的搜狐公司（该公司曾发布他的作品）获得了一份网页设计工作。进入 2000 年，媒体开始关注中国的 Flash 动画圈，朱志强也是他们报道的艺术家之一。6 但所有这些成就都只是为《小小 3 号》项目做了铺垫——这个项目让朱志强创作的火柴人享誉全球。\n3. 小小3号 上图为《小小 3 号》作品片段\n朱志强花了七个月的时间制作了《小小 3 号 。这完全是他利用业余时间制作的业余项目。这部动画画面简洁，没有情节和表演：只有一个火柴人孤身一人击败一支小部队，展开一场惊天动地的混战。然而，在当时的 Flash 动画中，这种酷炫程度却是前所未有的。这一点意义非凡。即使在今天，朱志强笔下火柴人的动作依然令人着迷。\n2001 年 4 月，《小小3号》一经推出，便立即引起了轰动，甚至在美国也引起了广泛关注。论坛用户纷纷称赞其「令人叹为观止」，相关帖子也因讨论人数过多而被锁定。一家任天堂粉丝网站甚至打破常规，向读者展示了这部「超棒的动画」。同年晚些时候，《 底特律自由报》 也对此进行了报道 。\n当你以为已经见识过网络上最精彩的 Flash 动画时， 《小小3号》 横空出世 。简单来说，这是一部火柴人版的成龙式群殴打单人的武打片。\n这听起来有些滑稽，但这套编排确实充满了陈氏风格：人物在墙上奔跑，做出 360 度旋转踢腿，充分展示了武术的最高境界。... 甚至镜头也非常令人振奋：3D 镜头、慢动作以及类似《黑客帝国》的环绕镜头，绝对会让你意犹未尽。7\n朱志强意识到自己找到了突破口。他把自己的邮箱留在了视频里，观众们通过这个邮箱联系他。「最让我印象深刻的是，」他回忆说，「第三天，我一天就收到了1200封邮件。这些邮件没有垃圾邮件，而且其中80%都不是中国人写的，而是来自世界各地的人。」\n尽管当时的互联网规模还很小，《小小3号》 的成绩依然令人印象深刻。到 2001 年底，它在闪客帝国上的浏览量超过 80 万，在 Newgrounds 上的浏览量超过 50 万，这还不包括其他众多托管该网站的平台。多年来，仅 Newgrounds 的上传量就超过了 500 万。闪客帝国的一位撰稿人评论道：「我不知道作者是如何创作出如此优秀的作品的。 」8\n朱志强毫无预兆地成为了早期的网络红人。《小小系列》动画爆红之际，正值 Flash 在中国大陆风靡之时。有人称 2001 年为「中国 Flash 元年」，甚至有杂志将他评为「年度网络人物」。\n听到风声，朱的父亲去了网吧亲眼看看《小小系列》，作品让他大吃一惊。他意识到，那和朱志强在书角画的东西一模一样。\n4. 成名之后 朱志强很快成为了一名自由动画师，并开始赚钱。一些知名品牌（例如香港的太古城中心）都想在广告中使用他绘制的简笔画人物。在 《小小3号》播出仅仅三个月后 ，他就与巴伦森公司达成协议，在韩国主持该系列动画的制作 。9\n到了 2001 年底，朱志强和他的闪客同伴一起上了电视，媒体也纷纷前来探访他的住所。然而，他不像一个成为明星的人。记者发现他睡在沙发上，很少出门，常常到下午才起床。他作的拿手菜仅限于简单的土豆和炒蛋，因此经常在外面吃廉价快餐。当被问及每天工作多长时间时，他说：「超过 8 小时。」除此之外，他主要就是看电视。10\n据 《北京今日报》 2002 年初报道：\n……他是个安静的年轻人，性格随和，略带腼腆；他说话很慢，表达想法也比别人慢一些。但一旦涉及到设计和 Flash 动画，他就会坚定地坚持自己的想法。……他大部分时间都待在家里，很少出门，除了参加一些 Flash 动画制作者的聚会和活动。他的所有时间都投入到了动画创作中。\n北京日报提供照片，拍摄于 2001 年左右。左上：朱志强和他的父母。右上：朱志强和他画的一位明星画像。左下：朱志强和他的动作电影收藏。右下：朱志强在制作 《小小8号》 。\n朱并非暴力卡通人动画的发明者。90 年代，西方网站「Stick Figure Death Theatre」就提供了其名字所暗示的内容。但小小融合了成龙、李连杰和《黑客帝国》的元素，将这一概念推向了完美。（此外，朱否认之前看过国外的火柴人作品——毕竟，他自 14 岁起就一直在创作这类动画。）\n无论如何，都是小小让「火柴人战斗」火遍网络。克隆作品泛滥成灾——就连 Stick Figure Death Theatre 也不例外。正如 2002 年的一份报告所记载的那样：\n网络上无数的兼职 Flash 动画师开始制作他们自己的《小小系列》仿作——数量实在太多，甚至出现了一个专门介绍这些仿作的网站。Stick Figure Death Theatre 里也有大量的火柴人山寨版，让人不禁想问，为什么朱志强不干脆放弃呢？11\n竞争并没有削弱 《小小系列》的影响力。朱志强在 2001 年和 2002 年制作了新的系列作品（一个射击游戏，一个功夫电影），他的作品依然傲视群雄。而且他的野心也越来越大。他的代码更加精良，小小 7 号和 8 号更是运用 3ds Max 制作了大量的 3D 镜头。《小小系列》依然是形式大于内容，但这种风格却持续在互联网上引起轰动。\n闪客们心中萦绕着一个问题：这一切究竟会走向何方？\n5. 商业化之殇 中国的闪客文化以自我表达为核心。正如朱志强所说，「电视是给所有人看的，Flash 是给自己看的。」12 这种理念让闪客文化不断发展壮大——到 2003 年，《闪客帝国》甚至拥有了自己的电视节目。然而 ，这些创作者也需要赚钱 。否则，Flash 就只能是一种爱好。\n金钱是关键难题。找到一份 Flash 动画的工作并不能保证高收入，而且这份工作的报酬也并不总是理想。朱曾指出，「大多数 Flash 动画师的生活十分艰难。」13 此外，版权问题也令人头疼。许多 Flash 动画师随意盗用受版权保护的声音、音乐和角色。他们又该如何收费呢？\n朱志强试图回答这些问题。他创作的「火柴人」——那个带领小晓的、全黑的火柴小人——是一个广为人知的角色。许多品牌希望与之联名，朱也希望借此获利。他努力保持作品的原创性，为自己的电影申请了版权，并在 2003 年正式提交了火柴人的商标申请。\n但那时，耐克的广告已经在海外播出了。\n上图为《小小 7 号》作品片段\n2003 年初，耐克推出了「火柴人」系列广告。 14 该广告的主角是一个动画火柴人 ，与 《小小系列》 中的形象非常相似 ，其炫酷的动作有时与朱志强的创意惊人地相似。当该广告活动在中国推出时，朱志强就此事联系了耐克公司。但耐克公司并未对此作出回应，于是朱志强提起了诉讼。\n当这个故事被报道出来时，耐克公司态度强硬。耐克的一位律师表示：「很明显，原告企图通过指控一家知名跨国公司来推销自己和他的 Flash 作品。」\n与此同时，朱志强表示，这个火柴人角色与他自己的非常相似，人们误以为他会自己制作这些广告。他还称这类火柴人非常适合体育广告——这很麻烦。「一旦耐克使用这些角色，还有谁会来找我做它们呢？」他问道。他还声称，已经有一个出版商因为耐克的广告而退出了他的图书项目。15\n尽管朱志强一直都在制作一些《小小》伟明的品牌广告作品，但在 2003 年并没有推出新的系列剧集。他说：「诉讼结束后我会继续。」16 这起案件持续了数年，变成了漫长的煎熬。他认为案件的结果对他的未来至关重要。\n2004 年初，有中国媒体报道称：\n朱志强向记者透露，他为 「火柴人」规划了光明的 「职业蓝图」，但眼下最关键的是打赢官司。毕竟，这些美好的前景都建立在「火柴人」版权得到确认的基础上。\n朱志强最初在法庭上获胜。随后，上诉过程一直持续到 2006 年，最终他败诉。法官认为这些火柴人差异显著，且图像过于简单，不构成版权保护。耐克公司因此无需承担法律责任。朱志强需支付数千美元的诉讼费用。17\n从此之后，他几乎不再从事动画制作。2007 年，摩托罗拉聘请他创作 另一部类似《小小》的广告，但该系列最终未能回归。到了 2008 年，朱志强某转行成为程序员。他和《闪亮帝国》的创建者 Jams Gao 一同加入了创意代理公司 VML，开始参与手机游戏开发。就这样，《小小系列》宣告终结。18\n这是朱志强在 2001 年左右为香港太古城购物中心制作的一则广告的一部分\n6. 继往开来 从很多方面来看，朱志强的艺术生涯都是早期互联网的牺牲品。如果说如今从爆红到获得稳定收入的路径尚不清晰，那么在 2003 年，这条路根本就不存在。如今，如果能像 《小小系列》 那样爆红，很可能就意味着有机会创业。而他当时却没有任何必要的资源。\n「火柴人」诉讼事件只是问题的又一层。在那个年代，大公司可以轻易摆脱抄袭知名网络艺术家作品所带来的负面公关影响。耐克广告的制作人员真的都没看过 《小小系列》吗？不太可能——它在全球年轻人中非常流行。然而，朱志强在传统媒体主导的时代，却身处新媒体领域 。他又能怎么办呢？\n尽管如此，首先，《小小系列》的故事并非失败之作。必须强调的是：这部动画系列非常成功 ，它激励了世界各地的人们。这种情况不仅发生在中国（ 《猫》 的作者卜桦是 《小小系列》的忠实粉丝），也发生在海外 。19\n《小小系列》 确实比较肤浅，画面也谈不上新颖。但正是这种简单让它变得平易近人。任何年轻人都能看懂、 理解并模仿。其他一些中国 Flash 动画的代表作——比如 《大鱼海棠》、《黑鸟》 等等——可能因为过于复杂或过于中国化，难以被世界模仿。 《小小系列》 则不然。在那个年代，任何人都能看懂火柴人打斗的画面。\n朱志强本人可能并未从这一系列中直接获益，但他为后来的闪客奠定了基础，从而为闪客后续的发展铺平了道路。\n朱志强参与开创的中国闪客群体，培养了一批新的动画师和电影制作人。许多关键人物都从这里起步，包括章平（木头），他执导的《 罗小黑战记2》 今年票房大卖。他的作品作品也对海外的 Flash 艺术家产生了同样的影响。\n几年前，一位名叫本·拉蒂莫尔的档案管理员出版了一本电子书。自 2017 年 Adobe 开始逐步淘汰 Flash 以来，他一直致力于保存.SWF 文件及其相关历史。他的书是对 Flash 时代的编年史，他认为那是一个逝去的黄金时代。在书的最后一页，他这样写道，关于那个时代：\n……强烈的创造力、易于使用的软件、显著但不致命的局限性、几乎与当时整个技术领域的通用兼容性、鼓励免费消费和分享的广泛普及（在那个「病毒式传播」真正具有意义的时代），所有这些因素结合起来，以一次又一次的冲击影响了整个娱乐产业？这是我们永远无法复制的，只能永远怀念。而这一切，都源于一群窝在卧室里，看了太多 《小小系列》的家伙 。\n全文完 摘自吴的著作 《中国动画、创意产业和数字文化》。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n正如这篇 中国互联网简史 中所述 。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n关于朱的这些细节信息来自 《北京日报 》（2002 年1月18日）、 央视的采访 、 新浪的问答 、 台湾：《今日商业》小小功夫一閃一閃賺大錢以及 搜狐：阿贵作者与小小将做客搜狐聊天实录 。我们在文章中多次引用了所有这些资料。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n他使用 Painter 这个细节来自他在 TOM 上的旧个人资料 。想了解更多关于 《黑帮天堂》的信息 ，请参阅蓝色理想：闪光的里程——Flash 的这五年 。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n闪客小小诉耐克侵权获赔30万。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n正如吴在其书中解释的那样，Flash 动画首次在主流媒体上得到报道是在 《三联生活周刊 》（2000 年 9 月）： 大话“闪客”。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n论坛详情请参见 Ars Technica 2001 年 4 月的Stick figure martial arts (shockwave)。 粉丝网站的引述出自同月的 《任天堂世界报道》Stick Figure Fighting! 。另请参见 《 底特律自由报》 （ 2001 年 8 月 19 日 ）。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n数据来自 闪客帝国 和 Newgrounds 的存档截图 。引文出自 《闪客帝国精彩flash赏析》（2001.1—2001.6） 。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n摘自 2006 年一篇关于此案的文章 中国闪客第一案 ，其中包含有关朱的职业生涯的许多重要细节。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n有关朱志强生活方式的详细信息来自《北京晚报》 和 《北京青年报》 的文章 ，这两份报纸在当今都具有重要意义。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n参见 《南华早报》 （ 2002 年 6 月 24 日 ）。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n有关该电视节目的详细信息来自 2003 年的一份新闻稿: 闪客帝国推出中国第一档Flash动画专题电视节目 。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n摘自 PConline 2004 年的一次采访专访中国第一闪客小小: FLASH市场还差中介 。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n请参阅这篇关于 火柴人诉讼 的文章。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n关于耐克此次活动的来回争论，请参阅 《新华网》: Xiaoxiao sues Nike over copyright 、 CCTV《第一时间》：耐克“火柴人”广告惹官司 和 《新民周刊》：谁偷了我的火柴人？ 的文章 。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n请参阅闪客帝国 在 2004 年的访谈：“运气也很重要”——闪客小小帝国聊天实录。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n摘自中国网 Designer Meets His Match in Nike。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nTrendhunter 曾在 2007 年报道过 小小为摩托罗拉拍摄的广告。关于朱在诉讼后放弃 Flash 的说法来自这里：看不到钱途的中国“闪客”，关于他加入 VML 的细节来自这里： 为没有历史的互联网留下历史 闪客帝国回忆录。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n卜桦在 2003 年的一次采访中谈到了她对小小的喜爱： 卜桦：爱是唯一不受理性控制的事情 。\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025-11-17","description":"火柴人与我而言，是记忆也是情感，是过去也是现在。","lastmod":"2025-11-17T08:18:43Z","slug":"xiaoxiao-fought","tags":["flash"],"title":"当火柴人停止战斗","url":"https://blog.zengrong.net/post/xiaoxiao-fought/"},{"categories":["impressions"],"content":"这篇文章是我在2021年3月挖的一个坑。我一直想写一篇几万字的大作来分析游戏行业的商业模式，三年多过去了，因为精(neng)力有限，这坑没填。\n最近出现了一些有趣的事情，如果再不填，某些内容就该成为历史了。为了见证历史，开始填坑！\n下面是计划的目录，我估计五万字起。看看我的 TODO LIST（叹口气），五千字起吧😊。\n商业游戏的商业模式 史前时代 不要拿过去的眼光看现在 互联网时代 渠道为王时代 广告变现：新时代的开启 变现是一门技术活 买量时代 白piao渠道 前辈、大佬、著名企业的商业模式 小团队的决策 这两天颈椎犯了，不能低头，上半身无法旋转，坐着敲字的感觉极似著名的已故物理学家史蒂芬·霍金。脑补一下画面感，都不用配图。\n（配图已删）\n商业游戏的商业模式 讲挣钱离不开商业模式讨论。讲挣钱也就意味着本文不会将公益游戏和个人作品纳入讨论范畴。\n作为游戏行业从业者，我们应该大胆并毫不避讳地说，商业游戏的核心目标是盈利。\n和电影、流行音乐、畅销书一样，游戏本质上是文化娱乐产品，这类产品从生产者到消费者，少不了这四个流程：\n研发：制作出游戏的企业或个人 渠道：消费者可以直接在这里体验或购买 宣传：在消费者获取游戏之前，提供游戏展示，让消费者知道如何获得 运营：在消费者获取到游戏之后，持续影响消费者的体验和付费行为 游戏人一眼就可以看出，第一个流程是开发商的活儿，第二三四一般是发行商的活儿。\n以海外手游为例，主流渠道是 AppStore + GooglePlay，宣传是 Meta/Tiktok/各种买量渠道，运营一般在社交平台或者游戏内进行。\n我们用电影行业做类比：\n制片人：资方或资源方 导演+编剧+演员组成研发团队研制产品（电影） 院线是主要的渠道和宣传，现在还有线上渠道 一个更有趣的类比思考：\n为什么小众的艺术电影一直不赚钱，但一直存在？\n这是一种导演修炼途径 你怎么知道不赚钱？ 电影节是有奖金的，拿奖金也是一种商业模式 如果有一个游戏制作人，立志要做一个不赚钱但大家都热爱的游戏，他自己投资，或者找到愿意投资的人，把游戏做出来了，一分钱没赚，大家都爱玩，这是商业模式的成功。\n还是这个例子，如果游戏做出来，一分钱没赚，但大家不爱玩，这算是产品失败了。但不影响该商业模式在这个游戏上的成功。\n这种商业模式，不是我们要讨论的商业模式。\n所以，这个制作人还能不能做下一个不赚钱但好玩的游戏，就不是我们有资格讨论的事情了。\n史前时代 用时间收费 90年代风靡大江南北的街机游戏厅，采用的是投币模式。游戏厂商是内容提供者，游戏厅就是 宣传+渠道+运营 一体化发行商。这种商业模式是时间收费：\n游戏厂商把游戏主板硬件卖给游戏厅 玩家买币租赁游戏机的时间 游戏厅选择最能赚钱的游戏类型 由于厂商卖出主板硬件后，就无法再通过直接手段影响游戏厅的运营，因此游戏厅需要部署最能赚钱的游戏类型：\n想赚钱，一局不能太快：玩家输不起； 要赚钱，一局不能太慢：游戏厅时间等不起。 很长的剧情铺垫的，很多选项要斟酌思考的，这些当今 3A 必备的设定，街机都不应该有。游戏厂商要充分给玩家游戏时间的爽感和刺激感，让难度从中等逐渐加大到只有10%玩家可通关。\n这就决定了街机游戏的典型最佳选择：横版动作闯关、格斗、PK。 用一个游戏币买回来几十分钟的爽感。\n当然游戏厅老板也喜欢格斗产品对抗（输上头的会买很多币）。甚至游戏厅老板会在拳皇和街霸的机子上作为擂主打对抗，放话打赢老板送10个币…… 这就是最直接有效的游戏厅运营活动了。\n点卡模式，也是一种时间收费。 当时欧美互联网开始兴起， 多人文字冒险地牢游戏（Multi-User Dungeon， MUD，跑团）在欧美流行。由于服务器需要成本，参加游戏的玩家按照游戏时间支付服务器成本，点卡模式逐渐成为主流。\n从2001年的《热血传奇》到2005年的《征途》再到今年6月国服回归的《魔兽世界》，点卡始终伴随游戏玩家不眠不休地在游戏世界中战斗。\n买断制 买断制是长盛不衰的商业模式，从买卡带，买光盘到付费下载，形式变了N种，本质一直不变。买断制也是更适合头铁的原教旨主义游戏人的商业模式。\n原因很简单，既然您已经拥有了这个游戏，那么设计者就不必在「如何挖付费坑」上绞尽脑汁，而把所有的脑汁用在「如何让你觉觉得好玩」上了。\n买断制没有时间限制，不用买币，玩家玩一分钟和几十个小时花的钱都一样，这就催生了更多游戏类型，RPG/SLG/ACT/AVG/RTS/FPS 都是在买断制的时代诞生的。\nSteam 上大部分的独立游戏都采取买断制，三大主机Switch、PlayStation、Xbox中的产品也主要采用买断制。\n这是最好的模式么？\n下面两件事都是在买断制模式下出现的：\n1982年起始于《E.T.》的雅达利大崩溃 1997年《血狮》造成的国产游戏信任危机 摘自百度百科：\n烈火文明的市场销售部经理吴刚曾经写过一篇文章，开头是：“这些年来，两件事使我印象深刻：有几个湖北的大学生为了在首发式上买到《血狮》，凑钱来京买走一套。走的时候自知有愧的销售人员一再地对那个学生说，有什么问题一定要来电话，我们给你们解决。但是直到今天，那几个学生没有来过电话，也没有来信，我知道他们真的被深深地伤害了。是的，那种骤然而来的打击使他们的内心有强烈地被欺骗的感觉。我意识到，我们失去了他们，这是我们最大的悲哀。\n“另外一件事是我曾经看过这样一封信，有一个人在《血狮》失败后写信明确地讲，他不退货，尽管他非常愤怒，他说就算是为中国软件业做贡献了。不过，以后他可能不会再买国产游戏了，他说他很抱歉。”\n不要拿过去的眼光看现在 对产品的静态了解只能通过宣传渠道获得，市场宣发决定一切，渠道决定市场。这是 史前时代 商业模式的弊端。\n这样的时代，已经一去不复返了。\n很多 游戏人/制作人 都深受史前时代优秀产品的影响，希望在当代复刻那个时代的辉煌。这可能是个错误的决定。\n不能拿过去的眼光看现在，也不能拿过去的眼光看未来。\n但更要注意的是不能拿现在的眼光看未来！\n参考文章 百度百科：血狮 知乎@Taoist Punk 游戏付费制度的由来与过往 未完待续 敬请期待下一篇：\n互联网时代 渠道为王时代 广告变现：新时代的开启 变现是一门技术活 买量时代 白piao渠道 前辈、大佬、著名企业的商业模式 小团队的决策 未完待续 ","date":"2024-06-30","description":"作为游戏行业从业者，我们应该大胆并毫不避讳地说，商业游戏的核心目标是盈利。","lastmod":"2024-06-30T15:07:53Z","slug":"how-to-video-games-make-money-1","tags":["gamenote"],"title":"电子游戏是怎么挣钱的？史前时代","url":"https://blog.zengrong.net/post/how-to-video-games-make-money-1/"},{"categories":["tutorial"],"content":"会议刚刚结束，还热乎着。\n今年4月，巨量引擎和微信小游戏团队分别在长沙和武汉举办了各自平台的IAA生态会议。今天（2024年6月20日），广州的抖音小游戏行业峰会又来了。\n点击下面的链接，观看之前的两次会议实录。\n智游破浪启迪未来：2024巨量引擎抖音IAA小游戏生态大会实录 让创造产生价值：2024（武汉）微信小游戏公开课IAA专场实录 我观看了部分直播，对绝大部分的现场简报做了截图。但文字比蚊子还要小并不是我截图压缩的原因，而是直播本就如此。\n如果希望逐字了解大会中讲了什么，可以扫码查看胡扯游戏群中 Online 同学的 AI 听录。\n新域崛起，引领未来：聚焦小游戏行业新超势，激活未来增长引擎 宏观经济利好小游戏，文娱支出年增速大于10%，与发达国家相比有增长空间。 84% 的用户选择更加轻量化和碎片化的移动端娱乐方式。 流水规模的大幅增长。 用户付费意愿增长向好。 研发更快，成本优势，链路更短，激活和付费优势。 融合创新，进无止境：抖音游戏玩法新篇章 经营有道，无限增长：用全域营销思维解锁抖音小游戏爆款密码 赛道分析：消耗、数量大幅增长。 抖音小游戏是闭环体系，生态是有变化的。 55% 的收入是来自于混合变现的产品。消耗排名前五的产品中90%是混变产品。广告变现比例达到了3成。 前15天30%，终生20%，内购做到80%就能收回投放成本。 大盘数据可以看到，内部付费后置，广告付费前期。前期需要友好的获取环境，免广告成为很好的付费转化突破点。 泛用户能保持游戏中友好的粘性。 闭环体系拥有更强的转化链路，抖小的激活成本是其他产品的一半。抖小能拿到丰富的自然流量，最高自然量占比70%，通常自然量新增贡献30-40%。 短周期回收成本，长线经营实现盈利。头部产品90天持续增长，终身倍率45+倍。 快回收不等于割韭菜。关注局外的养成线。 具备和抖音生态匹配的题材。短线和中长线的差异化。增强用户的留存率和参与度。 广告、内容、阵地，实现全域经营。 ROI7 单项目跑量 143%，首日ROI 36%，7日ROI 105%。 直播小玩法和小游戏用户的结合。 生态壁垒，整合内容流和游戏的界限。 更好的激励政策。 分享经验，启迪未来：深挖融合创新，激发小游戏新活力（百炼英雄） 适合小游戏的融合玩法：\n玩法表现和包装轻量化，解决买量问题。 符合玩家认知直觉和好奇心，解决留存问题。 简单上手，快速反馈，难于精通，解决付费问题。 分享经验，启迪未来：聚焦混变玩法 把握小游戏新机遇（贪吃蛇无尽大作战） 还好吧，这次贪吃蛇无尽大作战没有用上次微信公开课的PPT。\n全文完 ","date":"2024-06-20","description":"政策倾斜，多重激励，闭环经营，抢占先机","lastmod":"2024-06-20T11:14:07Z","slug":"2024-guangzhou-douyin-microgame","tags":["gamenote"],"title":"游无界创未来：2024(广州)巨量引擎抖音小游戏行业峰会","url":"https://blog.zengrong.net/post/2024-guangzhou-douyin-microgame/"},{"categories":["technology"],"content":"Q：立项的时候，你会优先考虑用3D来表现吗？\nA：我不认为3D和2D是立项的重点。\n封面图 由 AI生成，参数如下：\n1RAW photo, (masterpiece:1.3), subsurface scattering, heavy shadow, (high quality:1.4), (intricate, high detail:1.2), professional photography, HDR, High Dynamic Range, realistic, ultra realistic, photorealistic, high resolution, film photography, ambient lighting, atmospheric effects, \u0026lt;lora:detail_slider_v4:2\u0026gt;, OverallDetail, smoldering ruins in a fiery landscape, fiery rain, \u0026lt;lora:RealmsOfFire-10:1\u0026gt;, epiCPhoto 2Negative prompt: UltraBeta-neg, (UnrealisticDream:1.25), BadDream, Beyondv4-neg, ng_deepnegative_v1_75t, (HandNeg-neg), blurry, noisy, cowboy hat, (face out of frame), bad anatomy, watermark, wrong number of fingers, username, moles, epiCPhoto-neg 3Steps: 28, CFG scale: 12, Sampler: DPM++ 2M, Seed: 802237468, Size: 1016x896, Model: Absolute (Un)Real Fantasy, Refiner: Real Dreamshape [571d4333e1], Version: v1.9.3, TI hashes: [object Object], Model hash: 953cf6b0c6, Schedule type: Karras, Refiner switch at: 0.7, Denoising strength: 0.48, Clip skip: 2 技术人的误区 （下面提到的几个游戏，均为微信小游戏）。\n3D和2D只是表现形式。具体怎么选择，要根据 团队能力，开发周期，产品选型 来决定。\n我们做技术的人，很容易陷入认为技术能影响商业化的判断中。我现在的观点是：有影响，但影响不大。\n超级解压馆的美术，借用了一部分3D表现，本质是2D，能发起来还是整体的体验好，用户的目标感强。\n现在小游戏榜单上有不少产品是3D的，比如抓大鹅，我最爱消除（LionStudio 的国内正版），这些产品都是Unity做的。\n这也说明了，对 Unity 技术方案比较熟悉的海外团队，或者在技术上沉淀了很久的国内团队（例如抓大鹅的研发），选择使用 3D 来表现产品，是因为能更高效地实现出产品需要的效果。\n注意，技术的核心是 高效地实现产品需要的效果。套用一句老话：技术终究是为产品服务的。\n我们在做出决策的时候，要看团队的技术能力，产品的研发周期，要在立项时，就判断这个产品适合用什么技术来做。\n注意，是 先决定做什么，再选择用什么技术来做，而不是 先选择用什么技术来做，再决定做什么。\n制作人和技术人的目标不同 同样需要注意的是，制作人的目标，与技术人（包括美术和程序）的目标，往往是不一致的。\n技术人员更关注较新的，较难的，能提升自己技术能力的目标，而制作人往往更关心产品的实现质量和工期。\n这是矛盾的。\n例如，一个优秀的程序在实现一个需求时候，会优先考虑 性能、吞吐量优化 等等指标，而制作人更多考虑 完成度、效果。\n例如，技术人常常会要求更多的时间去打磨产品质量，而且常常无法给出明确的截止时间点。\n在这种情况下，不能相信技术人员的判断。因为制作人对最终结果负责。\n如果制作人对技术能增加一些理解，就更容易做出 平衡 的判断了。\n所以我总是不止一次地说，制作人 非常有必要 学习程序和美术，并持续关注主流技术方案和美术实践的变化。\n小游戏的立项优先级 List 国内小游戏立项，我觉得可以考虑这个 List，优先级从高到低：\n选择平台： 微信、抖音直投、抖音发行人、抖音直播 核心玩法 Game Play 题材 -\u0026gt; 美术风格 -\u0026gt; 特效表现（前者决定后者） 技术方案 对于休闲游戏来说，技术方案看似是最低优先级的，\n但如果技术能力很菜，上面3个做得再好也没用。\n全文完 ","date":"2024-06-02","description":"但如果技术能力很菜，上面3个做得再好也没用。","lastmod":"2024-06-02T06:55:16Z","slug":"game-setup-2d-or-3d","tags":["gamenote","game"],"title":"休闲游戏立项：2D还是3D","url":"https://blog.zengrong.net/post/game-setup-2d-or-3d/"},{"categories":["technology"],"content":"\n前几天莉莉丝的《海滨消消乐》上线的时候，我发现这个游戏启动界面中居然没有 Unity Logo。\n胡扯游戏群友们聊着聊着，就有技术大佬给出了去掉 Unity Logo 的方法。\n下面的方法我也没有试过，请有条件的同学尝试后给我回复下：\n方法一 by @猫小姐（搬砖ing）\n专业版就可以把那个 Power by Unity 标记去掉。\n看得出猫小姐是个有钱人。\n方法二 by @Bacoo\ngame.js 里面：\n1barConfig.style.width = 1 2barConfig.style.height = 1 3textConfig.style.fontSize = 0 iconImage注释掉，那个最开始的进度条就看不见，首包加载的 Logo 也不显示了。\n如果还要去掉启动的 Logo，可以修改小游戏配置：hideAfterCallmain 这个选项的勾。\n如果还要支持 H5 版本中去掉 Unity Logo，可以直接改 unity_default_resources 包里面的那个 Splash，替换一下。\n全文完 ","date":"2024-05-30","description":"给钱就可以","lastmod":"2024-05-30T12:19:35Z","slug":"remove-unity-logo-in-minigame","tags":["unity"],"title":"去掉微信小游戏启动界面中 Unity Logo","url":"https://blog.zengrong.net/post/remove-unity-logo-in-minigame/"},{"categories":["impressions"],"content":"作为休闲游戏的制作者，研发团队的成员，在制作游戏的过程中，会形成对自己游戏的理解和认知。\n无论对于研发中的哪个岗位，明确「自己的认知」不一定等于「玩家的认知」，是做好休闲游戏的前提。\n封面图 由 AI生成，参数如下：\n1score_9, score_8_up, score_7_up, rating_safe, dark background 21 girl, solo, cute, (short:1.2), small body, (shortstack:1.1), d.va \\(overwatch\\), headshot portrait, side view, close-up, looking at viewer, bodysuit, (head tilt:1.2), white gloves, slight smile, headphones, dutch angle, v sign 3Negative prompt: score_4, score_5, score_6, signature, patreon, censored, deformed fingers, fused fingers, long fingers, ponytail, white background, lipstick, nipples 4Steps: 40, VAE: sdxl-vae-fp16-fix.safetensors, Size: 896x1152, Seed: 2189159887, Model: ponyFaetality_v11, Version: f0.0.17v1.8.0rc-latest-276-g29be1da7, Sampler: Euler a, Emphasis: No norm, CFG scale: 7, Clip skip: 2, Model hash: 499513276a, Hires steps: 15, Hires upscale: 2, Hires upscaler: 4x_NMKD-Siax_200k, Denoising strength: 0.2 我们在立项的过程中，会进行认真的市场和竞品分析。但我们在做游戏的过程中，很难重回客观角度，很容易陷入自己的信息茧房中。找不回小白玩家的感觉。缺少变回小白玩家的能力，是很难做好休闲游戏的。\n我把这个能力标签化为 「一秒变小白」 ：\n做游戏的时候站在专业人士的视角，分析各种数据啊体验啊系统啊；\n测游戏的时候站在小白玩家的视角，不分析，只感受，努力还原自己的「第一感觉」。\n开发者当然能代表游戏的一部分玩家。根据我的观察，大部分休闲游戏的开发者，主要是下面三类人：\n硬核游戏玩家： 更多关注主机平台、PC平台，独立游戏，少玩或者不玩手机游戏，除了测试自己制作的游戏，基本不玩自己制作的同类竞品。 只是一个玩游戏的人： 经常或者偶尔玩玩大众化的手游（王者、阴阳师、原神之类），偶尔也玩玩大众化的小游戏（抓大鹅、跳一跳之类），一般不会去主动寻找游戏来玩。 只是一个人： 如果不是被广告弹脸，基本不玩游戏。 休闲游戏的策划，可能属于前两种人的概率更高。和独立游戏制作者不同，自己制作的游戏，与自己在玩的游戏，有巨大的差别。这认知偏差，影响了休闲游戏的质量。\n第一种人，必须弄明白，自己代表的玩家，不是休闲游戏面向的最主要的那批玩家。 第二种人，代表的休闲玩家群体更大，但需要主动大量玩不同品类的游戏，否则很难在产品上有所创新。 作为创作产品的人，很容易在错误的认知偏差上越走越远。\n比如，格力空调的智能App整体体验为什么那么差，感觉是PC时代的设计师做出来的？ 比如，女性向游戏团队里策划没有女性，美术没有女性，很容易做出「男性玩家眼中的女性向游戏」； 比如，产品出海欧美，美术风格看起来是「中国美术认为的欧美玩家会喜欢的美术」。 玩家有很多种，需要思考，从大数据的角度，我们自己这种玩家，在所有玩家里，占比是多少。\n最简单易行的办法：找一个不玩游戏的人，站在TA背后，看着TA玩你的游戏，不要出声，不要指导，把TA所有的行为记录下来。\n测试结束后，这样问：\n你喜欢玩这个游戏吗？ 为什么这个按钮这么好看，你刚才就是不点？ 刚才已经输了，你没发现嘛？ 哦，如果下次你还想找人家测游戏，这么问就是错的。😄\n应该这么问：\n我发现你刚才没有升级就下一关了，为什么呢？\n得到的答案大多是： 哦！还能升级？ 或者：什么是升级？\n别指望专职的测试岗位帮你测出问题，QA只能保证质量，无法保证体验。 成本最低的方式是给家人玩我们做的游戏。如果不怕被打，对颜值有自信，也可以在地铁上，马路上找人玩游戏发红包…… 如果他们在完全不被指导的情况下，顺利玩了10分钟以上，且在不被提醒的情况下，还会有空拿出来玩。\n那这个游戏想不爆都难。\n开发者作品 下面这个老牌横版动作射击游戏是胡扯游戏三群一个哥们单人肝出来的，大家可以体验一下。\n微信小游戏搜索 爆裂兄弟 。\n群友们认为：\n局内高手操的游戏，商业化更难做。因为你不能在局内用户爽感最强的情况下打断用户心流。\n来到局外，用户的付费意愿，就取决于游戏的核心价值。用户在局内爽完了，为什么出来要付费？\n总之，动作游戏成本更高，IAA的商业化对于动作游戏，要求更高。\n全文完 ","date":"2024-05-19","description":"作为创作产品的人，很容易在错误的认知偏差上越走越远。","lastmod":"2024-05-19T06:04:42Z","slug":"become-a-rookie","tags":["gamenote"],"title":"休闲游戏开发者需要刻意锻炼一秒变小白的能力","url":"https://blog.zengrong.net/post/become-a-rookie/"},{"categories":["tutorial"],"content":"上周，巨量引擎和微信分别在长沙和武汉召开了与IAA小游戏相关的会议。这两次会议相隔时间1天，都是针对IAA游戏。这个行为从侧面证明了两家最大的小游戏渠道，对IAA小游戏的重视。\nIAA 小游戏的活跃用户占抖音小游戏大盘的 80%，成了名副其实的行业增长新热点。微信公开课提到自己拥有 40 万开发者，其中超过 8 成是 30 人以下的中小团队。\n封面图 由 AI生成，参数如下：\n1beautiful city in the night sky with Eiffel tower in her center, stars, realistic style, dreamy, fantasy. The photo depicts a beautiful city under the night sky filled with stars. The scene seems dreamy and fantastical, 8K, cinematic photo \u0026lt;lora:juggernaut_cinematic:1.00\u0026gt; 2Negative prompt: score_6, score_5, score_4, ugly face, mutated hands, low res, blurry face, pubes, monochrome, furry, bad eyes, dot eyes, huge breasts, blue skin, bad teeth,(( teeth, adult, old, adult female, old woman, pubic hair, medium breasts, large breasts, huge breasts, Asian, Chinese, mature woman, milf, more than 16yo, futa, trans, gay)), bad arms, missing legs, missing arms, poorly drawn face, bad face, fused face, cloned face, worst face, three crus, extra crus, fused crus, worst feet, three feet, fused feet, fused thigh, three thigh, fused thigh, extra thigh, worst thigh, deformed pupils, missing fingers, extra fingers, ugly fingers, long fingers, horn, extra eyes, huge eyes, , amputation, mutilated, deformed, disconnected limbs, , , , worst quality, low quality, jpeg artifacts 3Steps: 40, Size: 1216x832, Seed: 504095531, Sampler: DPM++ 2M Karras, CFG scale: 7, Clip skip: 2 我阅读了 智游破浪启迪未来：2024抖音IAA小游戏生态大会实录 中提供的 2024巨量引擎抖音IAA小游戏用户研究报告 （下称「报告」，访问前述会议实录可下载），同时对比 让创造产生价值：2024（武汉）微信小游戏公开课IAA专场实录 会议上的公开课讲师PPT，讲讲我关于立项思路的分析。\n这些理解，仅限于人数少于10人，或月均成本低于10万元的纯研发团队。\n立项之前先分类 微信小游戏公开课把开发者分成四类：\n内容创作者 热度引领者 投放深耕者 IP所有者 在我看来，先知道自己属于什么分类，非常重要。\n看看微信公开课对3类和4类开发者的定义：\n投放深耕者： 核心指标 ROI 90%。面向具有成熟发行能力的团队。 IP所有者：核心指标 14 留 70%。面向自由IP或者获取IP的团队。 很明显，绝大多数10人以下的纯研发团队不可能是 投放深耕者 和 IP所有者 。\n所以本文仅针对 内容创作者 和 热度引领者 做分析。\n当然，如果团队创始人本身就是成功的发行人士，不需要看这篇文章。\n抖音IAA小游戏的立项思路 从用户游戏频率、年龄、性别维度来看，年长用户和男性用户的游戏频率更高、时长更长、数量更少。\n如果团队更愿意面向男性用户做产品，就可以更多考虑男性的品类偏好： 动作冒险、角色扮演、塔防、策略卡牌。\n而且，男性用户是抖小的核心付费人群（男性38%付费渗透，女性仅12%）。我在 混合变现从入门到放弃（上）——混变产品的误区 一文中提到过，广告和内购之间不会互相影响，只会互相促进。付费玩家更容易接受看广告这种（付费）行为。\n接近50%的用户会看10次以上的广告。 从品类看，解谜、塔防、经营养成、合成消除、休闲类 的广告次数都超过了10次。\n这就意味着，内容创作者 应该更多选择广告次数较多的品类做立项。\n女性玩家看广告的意愿更高（86%），集中在 18-24 岁者占比 52%，游戏时长在 30分钟-1小时的占比 72%。\n这就意味着，内容创作者 面向女性用户做游戏的时候，需要多考虑 18-24 岁年轻女性的喜好和品味，在美术品质和产品特点上做好迎合。\n上面这张图能表现出男性用户的社交性更强，而女性用户的分享意愿更强。这酒意味着，内容创作者 在面向女性用户做游戏的时候，应该更多思考如何利用平台提供的分享机制，并做好产品自身的分享打点分析。\n从抖音小游戏提供的大盘新增数据来看，脑洞类 的产品新增量级占据50%。这个品类很适合 热度引领者 来做创新。\n新鲜感和喜好题材 是用户首次游玩最重要的原因。这对于 内容创作者 的创新能力有较高的要求。难度不合理与玩法重复 （被更好的同类玩法游戏截胡）是弃玩的主要原因，这对于 内容创作者 的研发能力提出了更高的要求。简单来说，要做新颖的玩法，提高产品质量，使之不易被模仿和超越。\n从上图可以看出，大量的用户是被有趣的游戏视频吸引到游戏中来的。他们又容易被同样有趣的游戏视频和直播召回。游戏的难度如果设置合理，用户更愿意持续在游戏中玩更长时间。\n抖音IAA小游戏立项小总结 对于 内容创作者 ，根据自己的喜好和擅长的方向，要确定是面向男性用户还是面向女性用户制作游戏（抖音的男性用户已经超过女性，详见 智游破浪启迪未来：2024抖音IAA小游戏生态大会实录 ），在广告次数更多的游戏品类中寻找创新。 在发行端，要关注买量的游戏视频，并善于使用直播来召回用户。 做好游戏的难度控制，过高的难度会让用户更容易弃游，合适的难度能增加用户持续游玩的动机。 对于 热度引领者 ，考虑抖音大盘占比50%的品类，热度就是一切创新。 微信IAA小游戏的立项思路 如果经常关注微信和抖音小游戏排行榜，会发现其中的品类非常不同。\n先挖个坑放在这里，写累了，睡觉去。\n全文完 ","date":"2024-04-28","description":"这些理解，仅限于人数少于10人，或月均成本低于10万元的纯研发团队。","lastmod":"2024-04-28T14:26:55Z","slug":"build-a-new-iaa-project-of-douyin","tags":["gamenote"],"title":"IAA异军突起，基于抖小IAA用户研究报告的立项思路分析","url":"https://blog.zengrong.net/post/build-a-new-iaa-project-of-douyin/"},{"categories":["tutorial"],"content":"会议刚刚结束，还热乎着。\n和昨天的 智游破浪启迪未来：2024巨量引擎抖音IAA小游戏生态大会实录 类似，微信小游戏公开课IAA专场（武汉）分为官方演讲和开发者演讲两个内容。\n不同的是，武汉的公开课有光谷开发区的支持，公开课官方演讲的整体的内容更加偏「虚」一些，许多演讲都有「科普」性质，对资深开发团队来说，有价值的内容不多。\n当然，或许这也是微信公开课追求的价值吧。\n下面的标题，是我为了方便理解而杜撰的。\n高等级部分 微信小游戏专属MAU为5亿，昨天抖音会议，说到抖音的MAU是12亿。 24年前3个月高增长，但现场没有说具体的数字。 40万开发者。 中小团队（30人以下）超8成。 独特的创意，是创作者的才华体现，也是赢得用户的钥匙。创意是来源于生活的。\n下面这部分内容是「胡扯游戏3群」的车尔斯基光速手打出来的，无删改，大家凑合着看：\n现在讲案例了\n北京大厂出来创业，4个人，小游戏赛道做了卡牌类的IAA游戏，平均每天10W广告流水 微信社交小游戏，超乎想象的效果，22年行业爆款三消产品，也是北京大厂出来 十几个人 长沙团队，擅长美术，都是女孩子，从来没做过游戏，23年来微信尝试，女生做女生擅长玩的，融合长沙文化，模拟经营游戏 3个例子表达是微信小游戏生态容纳了这些团队，希望每个有才华的团队来微信，期待大家一起探讨，把生态做好，做大，小团队要擅长发挥自己的才华到作品，我们18年向开发者发过一份信，讲我们的理念，小游戏的本质是创意，小游戏平台是围绕创意开展，好的创意来自生活，小游戏创造力增加更多烟火气。\n举例 找茬这类玩法 长线不足，但是受玩家喜欢，容易亏损，但是去年开始，这类游戏开始成为热门的视频话题，用户就会在这的游戏看到 找茬的游戏还能这样玩，还有原创的剧情，所以我们在这个赛道上有长足的进步，另外一个品类就是解谜类，很好玩的解谜的内容用户是喜欢的，我们发现开发者把用户喜欢的变成游戏，比如（我没听清），品质表现特别好活跃次留40%\n小结一下，什么是创意呢？创意来着生活，是创作者用自己的才华把生活融入产品 获得用户认可，有了创意是不是能转化成产品？答案，不是的，怎么转化有竞争力的产品?小游戏平台希望能帮助开发者拥有无限的可能，我们从开放到现在秉承的就是高效率低成本长期稳定有商业回报的理念，在技术上迭代，我可以打下广告\n曾嵘：下面这段我也不知道怎么编了，是真的领导讲话稿。\n小游戏可以传承文化，发挥新力量。\n生态内容观察 用户多元，内容繁荣\n起步 2018：《跳一跳》，《海盗来了》等。 沉淀2019-2021：《消灭病毒》，《成语小秀才》等。 再出发 2022-今：《羊了个羊》，《超级解压馆》，《鱼吃鱼》等。 人均时长 60分钟。 活跃七留 60%。 女性：三消、益智（解谜、找茬）、模拟经营merge。 男性：策略塔防自走棋、io、益智、动作设计、模拟经营（点击经营）、答题。 赋予玩法「烟火气」，强化沉浸感体验\n玩法：强脑力，强解压。 题材：生活化、意义化。 表达：重情绪价值。 用户发现游戏行为多元\n35% 分享。抓大鹅的分享获得用户可以达到 50%以上。（曾嵘：抓大鹅是我猜的，现场可没说） 25% 搜索获量。 40% 广告获量。 四类开发者\n内容创作者 热度引领者 投放深耕者 IP所有者 内容创作者建议的品类，善用社交能力\n模拟经营 创意三消 策略塔防 动作射击 45分钟时长，分享裂变50%。 3D 消除类游戏获得了非常好的成绩。随着规模增长，分享率会不断增加。\n热度引领者，融合长线设计，挖掘泛娱乐热点，发挥新媒体优势\n模拟经营 解谜 找茬 io 30分钟时长，终身ROI 150% 投放深耕者和IP所有者，低门槛试水，快速积累经验\n投放：答题、传统消除，首日 ROI 90%。 IP：动作、超休闲、桌游社交，14留70%。 创意来源于生活\n曾嵘：这张图我实在编不出来内容，因为现场讲的内容也没有啥内容。\n实战指引 曾嵘：这部分没记，因为直接读开发者文档就可以了。感觉是在做科普，难度很低，不知道当时现场的同学是不是心里五味杂陈。\n下面这部分内容是「胡扯游戏3群」的车尔斯基光速手打出来的，无删改，大家凑合着看：\n我改提炼法吧，当前这个实战运营经验总结 提炼如下\n请认真阅读接入文档， 不要做伤害用户的行为 加载很重要，请用我们的插件和测试，让加载速度提升起来，具体详细看我们后台技术文档 分享是微信生态的重要一部分（如何重要，用户心态），请大家认真研究做好（但是微信关了分享回调接口……） 大盘数据和广告投放 用户ARPU增长25%，只有比例，没有数据。 男性45%，女性55%。 消除和塔防品类提升。 这说的啥，初学者看了也不知道怎么做变现，老鸟看了也不知道怎么做变现。 先提升广告渗透，再提升广告频次（这是不是很初级）。 买量是最重要的增长方式（下面又是科普部分，我就就不详细记了）。 账户搭建时，以素材分类，优质素材在A广告，测试素材在B广告。 图片和视频分开。 系统是以24小时ROI作为预估优化，要给时间。 单广告难以跑量。 直播实现流水数倍增长。 外部渠道实时看板，实现外部渠道增长。 选择微信优选服务，进行合作。 上面这部分非常有价值的内容，「胡扯游戏群」里的讨论更激烈：\n《贪吃蛇无尽大作战》分享 产品部门\n产品 用户运营 数据分析 QA 增长\n核心玩法优化带来的数据增长 中长期用户目标建立-双S蛇设计带长线增长 中长线商业化，广告点对生命周期和用户分层的数据影响 《超级解压馆》分享 使用硬币作为题材，使用水排序解决商业化变现。 光影效果细节。 尽量降低用户的上手门槛 加强用户爽感，获取更久的用户时长 品质和新鲜感是吸量的本质 模拟筹码音效 合成的强反馈 数字唯一目标性 增加用户长期的目标。 外围系统。 扭蛋机的金币消耗。 活跃次留35%。 人均停留时长45分钟。 挑战玩法比较鸡肋，只有3%数据变化。 每个数字精准打点。 1-5 用户不用看视频。 发牌数量降低50%。 提升11%的广告渗透率。 音效：灵感最优 手感：细节打磨 内容：生命周期增长 变现：爽之后的视频卡点 品质和新鲜感是吸量的本质 电视和短剧对用户碎片化需求的迎合 爆款是让用户眼前一亮的创意 这个分享内容，群友们一致认为是整个会议中最有价值的。 以下是「胡扯游戏3群」的车尔斯基光速手打出来的，无删改，大家凑合着看：\n玩法单一 做高策略难度玩法 效果很差 很鸡肋 数据结构看 挑战模式 没有超过5%的用户体验 数据埋点上，如图，1-5的时候用户是不看视频的，6以后可以看视频，精准打点是可以做商业化调优的 用户初始难度调低，对广告次数提升是有帮助 美术上要打磨的更细，愉悦感要做出来，操作上要给用户流畅，要给用户一个长期追求目标， 创意来自生活，视频短剧的爆火，碎片化的需求多了，用户习惯改变了，微信小程序改变了用户玩游戏的习惯，生活节奏快，碎片化时间非常符合（超级解压馆）小游戏场景 团队介绍 主创，清华美院毕业 学交互设计 能把产品做好是有原因的。\n对小游戏看法，从18年到现在都是微信小游戏生根，经历了市场的变化，不是没有市场，而是需要更好的产品，只要有更好的产品，就能拿到最好的结果，如果希望交流产品，加我，会后多探讨，希望能和更多开发者一起探讨微信生态里如何做更好的产品\n全文完 ","date":"2024-04-24","description":"3D 消除类游戏获得了非常好的成绩。随着规模增长，分享率会不断增加。","lastmod":"2024-04-24T10:31:08Z","slug":"2024-wuhan-wechat-microgame","tags":["gamenote"],"title":"让创造产生价值：2024（武汉）微信小游戏公开课IAA专场实录","url":"https://blog.zengrong.net/post/2024-wuhan-wechat-microgame/"},{"categories":["tutorial"],"content":"会议刚刚结束，还热乎着。\n这次会议，官方的4个演讲从IAA赛道成长、产品运营、立项选择、投放方法上做了详细的阐述。两位企业负责人基于各自公司的爆款产品做了案例分享，详细介绍了产品数据和投放经验。整个会议干货满满，看下来全程无尿点。\n参会的过程中我全神贯注用颤抖的手充当了人肉PPT转印机，现在把这些照片中价值较高的找出来，总结其中的核心数据要点，但并没有严格遵守拍摄顺序。\n标题是我杜撰的分类，方便大家理解。\n通用数据 抖音 MAU 已经达到10亿，渗透率 81%。 抖音小程序日均使用时长已经达到了43分钟。 互联网广告大盘增速高于社会零售总额，IAA上游拥有充足预算。 84% 的用户选择更加轻量化的游戏方式。 小游戏DAU波动中稳步提升。 24Q1塔防品类增长758%（胡扯游戏群友：因为靶心like吧算到塔防了吧） 明星品类的用户数据 年龄层上移，24-40和50+用户增加，大盘用户男性用户超女性数量。 游戏品类更加丰富。 大盘新增和消耗占比：脑洞类 \u0026gt; 逆袭类 \u0026gt; 模拟类 。 IAA3.0 时代，更多中度玩法，益智模拟塔防肉鸽齐上阵，短视频、直投、直播间一起推。 脑洞类的进化：费眼找茬 \u0026gt; 交互解谜 \u0026gt; 剧情解谜 。 脑洞类产品 CPA 1-2，首日ROI \u0026gt; 80%，终身ROI \u0026gt; 130%，男女比例5:5，游戏时长 15 分钟。 逆袭类的进化： 故事性，游戏场景，更加丰富的小玩法。 逆袭类产品 CPA 1-2，首日 ROI \u0026gt; 90%，终身ROI \u0026gt; 150%，男女比例6:4，相比脑洞类用户更年轻，游戏时长 20 分钟。 策略类产品，建议策略塔防，策略对冲，策略占领。 流量的来源 侧边栏接入的必要性（曾嵘：实际上这个接入是强制的）。 向僵尸开炮，抓大鹅的侧边栏用户数据。 小游戏在抖音IM的渗透率20%，单产品利用IM免费获量月均500万。 8成游戏使用发行人计划的素材用于广告投放。 直播间筛选更高价值的用户。 投放和预算 短回收：首日ROI \u0026gt; 85%，7日ROI \u0026gt; 110%，解谜占比46%，模拟经营占比19%，超休闲占比 12%，合成占比12%。 中长回收：首日ROI \u0026lt; 60%，30日ROI \u0026gt; 110%，解谜占比44%，模拟经营占比38%，休闲益智占比 13%。 IPU 至少6+次，成本控制 1-3元。 eCPM 高的原因是 70% 的预算上游是游戏。 用户回流比例： 首页侧边栏占比 41%。 抖音 lite 增量10%。 星广联投。 图文广告更有优势：CVR 4倍，ARPU 3倍。素材制作成本更低。 消耗和规模 平均在投生命周期43天，超过2个月的占比42%，预算充盈时增长倍率500%。 单品消耗峰值 100万每天，头部游戏日耗 5-10万每天。 流量存在品类分布，模拟养成、塔防、生存、沙盒分布更高。 游戏eCPM 200-300，电商 eCPM 60-100。 产品是偏向于游戏流量型的，建议关注上游游戏赛道的素材方向，去做更轻度的素材，吸引上游产品的的泛用户群体。 针对电商类型的品类，例如三消、IO等，女性用户占比更高。建议重点关注电商和女性游戏的爆发节点。 探索高埋点提收入，IPU稳定3-5过滤低广告意愿。 测试期、攀升期、成熟期的三步投放策略 测试期，发行人小单快跑验证数值，日耗 \u0026lt; 1000。 攀升期，自动化产品追求高渗透，中高埋点。 拉回收，埋点探索高价值群体。 上游行业的高价值人群，他们的行为数量会比我们普通的广告人群要更低，所以我们可以适当的去探索高一天低打点的模式，去实现高净值用户的流转，实现回收的提升。 攀升期案例：当确认了自己游戏的费率15日能达到3倍以后，实现消耗的快速攀升客户在调整买点之后，虽然首日ROI有所下降，但总体的用户价值并没有发生变化，同时消耗规模得到巨大的增长。 中台带来的巨大提升。 成熟期，直播协同增长实现收益最大化，放联运，分包撬动二次增长，抓流量热度。 素材，素材，素材 IAA 游戏的优势素材总时长 15-45秒，大部分用户观看 6-8 秒。 做素材无非面向两个对象，一是系统，一是用户。 需要从系统的角度去思考什么是优质素材。 从用户的视角看素材，内容-巨量创意，热点-巨量算数，灵感-巨量云图。 从素材内容拆解素材结构，明确作用方向。 使用热门话题、快节奏录屏、难点口播确定新游素材方向。 使用基准、优势、融合探索老游戏的进阶素材。 时间维度：3秒吸睛，10秒展示，20秒情绪升华。 立项和效率 玩法越重，用户越垂直，价值越高，但买量成本越高。 题材越轻，买量成本越低，用户越笨。 投放团队的配置：老板不一定要自己上，但一定要懂投放。 启动阶段的发行人素材，扩量阶段的录屏混剪素材，进阶的创意素材。 货不对版能跑的原因是利用用户标签，击穿算法模型。 多品类挖掘和全域营销。 救救这只猪：eCPM 300-400，IPU 3次，首日ROI 75%。 注重达人需求，关注达人的短视频制作，玩法迭代以团队效率为核心。 两个高价值的手册 畅游无界，轻启未来：2024巨量引擎｜抖音IAA小游戏用户研究报告\nhttps://bytedance.larkoffice.com/file/CnnybGpMTovXf9xKNrccP3gBnig\n抖小 IAA 五一冲量手册：\nhttps://bytedance.larkoffice.com/docx/Dvgsd6GOEo216LxuAIIc0Hy4ndf\n全文完 ","date":"2024-04-23","description":"年龄层上移，24-40和50+用户增加，大盘用户男性用户超女性数量。","lastmod":"2024-04-23T13:35:04Z","slug":"2024-changesha-douyin-microapp","tags":["gamenote"],"title":"智游破浪启迪未来：2024巨量引擎抖音IAA小游戏生态大会实录","url":"https://blog.zengrong.net/post/2024-changesha-douyin-microapp/"},{"categories":["tutorial"],"content":"这篇有点乱，边玩游戏边写出来的。做个快速记录避免遗忘，想到什么就写什么。\n见微知著，我们可以从产品局内是否设置了清晰的目标，看出这个设计者面对用户时的思路，包括体验思路和变现思路。\n封面图 由 AI生成，参数如下：\n1woman in a (yellow startrektos dress:1.1) admiring a flower in a garden,star trek badge, 2masterpiece, best quality, soft light, bokeh, real shadow, cinematic, subsurface scuttering \u0026lt;lora:TOSXL-000040:0.8\u0026gt; 3Negative prompt: low quality,film grain 4Steps: 50, RNG: CPU, VAE: sdxl_vae.safetensors, Size: 896x1344, Seed: 2196466465, Model: ghostbustersxlMix_v10, Version: v1.8.0, Sampler: DPM++ SDE Karras, CFG scale: 4.5, Model hash: dfbfe02e08, Hires steps: 14, Hires upscale: 1.5, Hires upscaler: 4x_NMKD-Siax_200k, Denoising strength: 0.25, Downcast alphas_cumprod: True, Discard penultimate sigma: True 「抓大鹅」，除了倒计时，就没有明显的局内目标了。在使用道具、复活界面，它使用了非常明显的字体来提醒用户的完成进度。用户很难不使用道具，也很难在倒计时内完成一局。此时显示局内目标，是一个必然被用户关注的场景。 在常驻界面中，已经有倒计时在不断提醒用户了，增加一个局内百分比目标就变得无关紧要。而用户在使用道具时，往往是为了扳回一局，此时强调百分比目标，再次增强提醒，对于控制用户体验是很有必要的。\n「抓大鹅」的难度，是润物细无声的。由于物理特性带来的随机性，玩家总认为自己可以找到需要的合成零件。而实际上可用的合成零件远比玩家感觉到的要少。也是因为物理随机性的存在，玩家不会认为游戏在「硬卡」进度，更多是认为「自己眼神不好没找到」。之前我在 不是玩家变了，而是游戏变了 中提到过「抓到真分享，抓不到假分享」，也在一定基础上印证了，玩家认为抓到鹅是真的强。\n「我最爱解压」，是在 消除品类的商业化调优（一） 中提到过的 Hexa Sort 的小游戏版本。局内目标、局外目标都做得相当「标准」和「完美」，小游戏插屏广告效果不佳，改用了锁格子的方法来做激励视频变现。这会降低用户体验。玩家本来会觉得，「我放错了地方堵了路，这是我的问题」，现在变成了「你策划故意锁个格子恶心我，逼我看广告」。\n能不能使用时间限制来解决这个问题呢？我认为不行。\n「我最爱解压」的体验感和「抓大鹅」有很大不同。前者的心流来自于观赏不同颜色面片翻转带来的流动感，后者的心流来自于悠闲的选择三个相关的物品，最终把一堆杂乱的物品清理干净。如果前者限制了时间，就会让玩家变得追求放置速度，降低了游戏的爽感。后者限制时间，就是为了在临近倒计时结束的时候，刺激玩家更快选择，更容易出错。因为核心体验不同，所以不能硬融。\n「超级解压馆」本质上是无尽玩法。它将可以无尽合成的数字，拆分成了多个数字范围段。和 2048 类似，当合成来到更大的数字时，在发牌的时候不再提供小的数字，此时产品的难度一直在设计者可以控制的范围内。局外目标部分，肯定是起量后增加上去的，因为这个局外目标设计质量看起来挺粗糙。\n手动发牌和手动合成， 是这个玩法的特点，但不能算劣势。尽管需要多点击两次，但给了玩家更多虚假的「掌控感」。游戏的发牌和合成特效做得也足够精致，降低了玩家手动点击时的无聊感。\n很明显，「爱上叠叠叠」的局外成长是预先设计的，整体效果非常统一。花朵-花束-花架-花房 这个系列合理且完整。\n有趣的是，「爱上叠叠叠」的前期难度很低，闭眼选择，都很难在前期失败。「我最爱解压」也有类似的设计。根据消除游戏的通常表现，我们知道 难度、用户时长、广告次数、留存 是互相影响的。难度低，留存更好，但付费（广告次数）也会相应降低。\n在设计关卡的时候，需要将乐趣与难度分开，并建立模型来判断玩家的进度和流失率。相信这两个游戏选择了最适合用户的模型。\n在 消除品类的商业化调优（三）失败的失败反馈 一文中，《糖果传奇》的策划总结了消除游戏关卡设计的 9 条经验，可以看看。\n这几个游戏中，除了「抓大鹅」之外，都允许无限复活（当然需要看广告）。「抓大鹅」只允许一次复活，是因为它的核心商业化是体力限制，每局无论成败必须消耗体力。若「抓大鹅」允许无限复活，体力这个用来控制裂变或者商业化的核心点就无效了。\n我们在体验「抓大鹅」这样注重裂变的产品时，会觉得它比较「克制」。这样的「克制」，是为了吸引更多的用户，不让人认为「这就是个骗我点广告」的游戏。另一个「克制」的原因还是来源于体力限制。体力是玩家主动的一种选择（因为完全可以不付费明天再来），那么游戏就不能做得让人讨厌（逼点广告骗点广告）。\n如果想学习「抓大鹅」，要消耗大量的时间调整产品，建立起买量成本、裂变利润之间的比例平衡，算好广告金价值和回收周期，做好因人而异的用户行为控制，并接受早期的无利润甚至负利润。\n想想，这样的消耗，是值得的吗？是你能接受的吧？\n下面是几个游戏的分类记录，我就不整理了，也不勘误了，大家随便看看就好。\n抓大鹅 玩法：无尽玩法，实际上只有一关，每局20+分钟。 局内目标：倒计时，使用道具时的进度展示。 局外目标：装修，通过抓到的鹅兑换。 暂存区：7个格子。 发牌机制：堆叠形式发牌，尽管看起来有很多合成零件，实际上能使用的有限。 卡点：倒计时限制。格子数量限制。 挑战次数限制（体力）。 道具：移出-将三个物体放到暂存栏，凑齐-自动凑齐一样，打乱-刷新物品堆。 裂变：第一次通过分享获取道具，后续改为看广告。 商业化：清仓甩卖，随机出现的道具获取。 失败：复活并移出所有格子物品到暂存栏。只能复活一次。 超级解压馆 玩法：本质上是无尽玩法，数字可以持续增长，数字派发的下限跟随总体进度增长。但会给一个较短的目标。平均每局30+分钟。 发牌机制：手动派发。 暂存区：无暂存区。 合成区：合成区数量与合成到的数字相同。 局外目标：装饰房间。 局内目标：数字合成的增长，有进度条。 卡点：看广告临时增加槽位，合成高一级数字自动解锁槽位。 失败：没有失败弹窗，失败可自主选择无限次洗牌。 难度：解锁的槽位越多，难度越大。 难度增加：只有最近的一个代币展示颜色，移走后才显示后面代币的颜色。 我最爱解压 玩法：关卡玩法。 发牌机制： 三个一组，消耗完毕后自动发牌。 局外目标： 过关搜集碎片，建造。 局内目标：数字，进度条。 卡点：卡格子数量，局内永久解锁格子格子。 道具：锤子-消除一堆，交换-移动一堆，刷新-重新发牌。 失败：第一次分享复活，第二次看广告复活，每次复活清空两个格子。无限复活。 其他体验：界面可手动旋转，复活失败界面可分享，复活失败界面可查看棋盘。 裂变：第一次可选择分享，后续改为看广告，首页分享拉新有奖励。 其他：转盘获取道具，皮肤商店 爱上叠叠叠 玩法：关卡玩法。 发牌机制：堆叠形式发牌，一次显示8个合成零件。 暂存区：6个格子。 合成区：2个花盆，可广告解锁2个额外花盆。 局外目标：花店，局内获取花束，装饰花店。 局内目标：花束的完成进度。 卡点：卡花盆数量，局内永久解锁。 道具：花朵调整-随机刷新剩余，打包一盆-随机打包一盆，清空格子-清空格子内的花朵。 失败：复活并解锁一个花盆，解锁两个花盆后变成清空格子，无限复活。 其他：局内展示总局数进度，未知局目标花束，用户好奇心。 裂变：首页分享，无奖励。 全文完 ","date":"2024-04-17","description":"我们可以从产品局内是否设置了清晰的目标，看出这个设计者面对用户时的思路，包括体验思路和变现思路。","lastmod":"2024-04-17T14:05:22Z","slug":"commercial-breaks-on-the-charts-eliminate-several-games","tags":["gamenote"],"title":"榜单上几个消除游戏的商业化分析","url":"https://blog.zengrong.net/post/commercial-breaks-on-the-charts-eliminate-several-games/"},{"categories":["tutorial"],"content":"短期（2-4周）项目要保证保证进度和质量，保证上线版本与设计一致，最有效的方法就是进行定期验收。\n下面给出我的建议方案：\n封面图 由 AI生成，参数如下：\n1tracer (overwatch), (visor, googles),brown eyes, short brown hair, cropped jacket, orange leggings, looking at viewer, smiling, teeth, standing, from_behind, upper body shot, outside, city alleyway, graffiti, blue sky, high quality, masterpiece, \u0026lt;lora:OverWatch_Tracer-DEF:.7\u0026gt; 2Negative prompt: fcNeg-neg, EasyNegativeV2, verybadimagenegative_v1.3 3Steps: 30, VAE: vae-ft-mse-840000-ema-pruned.ckpt, Size: 1024x1536, Seed: 1428282915, Model: realcartoonPixar_v10, Version: v1.7.0, Sampler: DPM++ 2M SDE Karras, CFG scale: 6, Clip skip: 2, TI hashes: [object Object], Model hash: 0f1bab0873, ControlNet 0: [object Object], Denoising strength: 0.22, Ultimate SD upscale padding: 32, Ultimate SD upscale upscaler: 4xUltrasharp, Ultimate SD upscale mask_blur: 8, Ultimate SD upscale tile_width: 512, Ultimate SD upscale tile_height: 512 项目上线前，必须包含以下三个版本。\nA 核心玩法DEMO（会议验收：项目组全体） B 主要玩法实现60%（会议验收：项目组全体） R 提审前完整版（会议验收：项目组+发行） 版本A：核心玩法DEMO 完成内容：\n仅包含核心玩法界面，没有其他界面； 核心玩法的UI界面完整； 所有与核心玩法相关的素材完整； 如有动画、特效，需要实现1-2个，要求能表现出核心玩法的基础体验； 不必包含音效、音乐； 验收内容：\n美术是否符合设计方案； 动画、特效是否符合预期； 核心玩法体验是否符合预期； 产品运行性能是否合理。 版本B：主要玩法实现60% 完成内容：\n主要界面和逻辑均已实现； 游戏内容完成度超过60%； 音乐音效均已加入； 允许缺少部分内容，允许锁定部分交互按钮。 验收内容：\n美术是否是符合设计方案； 动画、特效完整、合理、可用； 音乐和音效与游戏本体风格匹配； UE合理； 数值体验合理； 商业化逻辑合理。 版本R：提审前完整版 所有内容均已完成，待提审。\n验收内容：\n数值完整体验； 商业化完整体验； 项目团队与发行团队对齐产品预期。 更多版本 如果需要更细节的版本控制，可以阅读我这篇10年前的文章：项目版本描述规则\n全文完 ","date":"2024-04-14","description":"保证上线版本与设计一致，最有效的方法就是进行定期验收。","lastmod":"2024-04-14T14:24:53Z","slug":"three-inspections-before-going-live","tags":["gamenote"],"title":"快速项目质量保证：上线前的三次验收","url":"https://blog.zengrong.net/post/three-inspections-before-going-live/"},{"categories":["tutorial"],"content":"本篇不是评测，而是介绍一些使用思路，让小曾同学熟练使用AI工具解决生活问题。\n封面图 由 AI生成，参数如下：\n1score_9, score_8_up, score_7_up, solo, 1girl, pokemonkahili, expressionless, looking at viewer, squatting, ahoge, visor cap, striped shirt, white gloves, single glove, blue skirt, outdoors, tennis court \u0026lt;lora:style_tarakanovich_ponyXL:1\u0026gt; \u0026lt;lora:pokemon_kahili_ponyXL:1\u0026gt; 2Negative prompt: monochrome, simple background 3Steps: 20, RNG: CPU, VAE: sdxl_vae.safetensors, Size: 832x1216, Seed: 1621551946, Model: autismmixSDXL_autismmixPony, Version: f0.0.17v1.8.0rc-latest-276-g29be1da7, Sampler: Euler A SGMUniform, CFG scale: 6, Clip skip: 2, Mask blur: 4, Model hash: 821aa5537f, Inpaint area: Only masked, ADetailer model: face_yolov8m.pt, ADetailer version: 24.3.1, Denoising strength: 0.4, ADetailer mask blur: 4, Masked area padding: 32, ADetailer confidence: 0.3, ADetailer dilate erode: 4, ADetailer inpaint padding: 32, ADetailer denoising strength: 0.4, ADetailer inpaint only masked: True 这也是2024计划 曾老师的AI课 的第一篇。\n小曾同学发了个任务过来，让我给选个电动牙刷。\n这是一个测试AI大模型能力的机会。\n选择标准 要在两个消费产品中做选择，主要标准一般逃不出这三条：\n颜值 功能与特点匹配 价格，或者说性能/质量价格比例 其中1是主观的，3价格是基本确定的。大模型解决的主要是2的问题。\n我们使用了两个海外，两个国内大模型：\nGoogle Gemini ChatGPT 4 文心一言 Kimi 五个问题 我们设计了5个问题，依次检测大模型的能力：\n请推荐电动牙刷。（测试盲选能力） 请推荐国产电动牙刷。（测试对国产设备的理解） 国产电动牙刷中，usmile 和 laifen 这两个品牌，你看好哪一个？（强迫大模型给出二选一建议） 请在「什么值得买」网站中，查找这两个品牌的相关介绍，根据信息推荐一款牙刷。（测试联网能力） 在京东热榜中，搜索这两个品牌的购买热度数据。（联网数据分析能力） 推荐电动牙刷 Gemini 采用分类给出建议的方式，对于普通用户是更有有价值的。\n比较两个海外大模型，都给出了具体的品牌和型号。两个国内大模型在回答的时候，更多是罗列产品数据。\n推荐国产电动牙刷 Gemini 依然是同样的味道。GPT4 表现也不错。\n文心一言推荐相对简洁。Kimi 给出了搜索 9 篇网络资料后的分析结果。\n国产电动牙刷中，usmile 和 laifen 这两个品牌，你看好哪一个？ Gemini 是唯一给出了偏好的大模型，优劣的说明也很明确，其他的大模型都在打太极。\n文心一言的分析足够全面和具体，最终还是打太极。\n请在「什么值得买」网站中，查找这两个品牌的相关介绍，根据信息推荐一款牙刷 GPT4 是表现最好的，它真的去访问了网站，也给出了明确的选项。感觉Gemini在胡说八道。\nKimi 的5篇资料中只有2篇来自于张大妈，并没有啥新的内容。\n在京东热榜中，搜索这两个品牌的购买热度数据 Gemini表现明显超越其他几个模型了。\nGPT4无法搜到 laifen 这个品牌，所以我换了一个问题，似乎又被转换成了英文。下图中2、3两张图都是 GPT4。\n文心一言直接放弃说臣妾我做不到啊。Kimi 倒是同样的讨论，说臣妾做不到然后分析参考文章。\n结论 上面对于各模型的比较并不严谨，但有普通用户的代表性。\n从分析方式看，Gemini是胜出的，也是唯一一个获取到京东热榜数据的大模型（尽管我也不知道是不是它编的😄）。\nGPT4 访问网络资源的能力十分出色。\n文心一言中规中矩。\nKimi 的特点是实时搜索资料并立即分析。\n人类工作量 人类问题的终极答案，当然还是要上一点人类工作量。所以我人肉比较了一下京东热卖和官网信息：\n京东热卖榜中，usmile 的热卖榜搜索优化相当好，前列的全是官方产品。而徕芬的热卖榜前面不知道是些啥，翻了一页才看到官方产品信息。 显然 usmile 的营销做得更专业。 两者的单品最高销售量都能达到20万+。 usmile 专做口腔相关产品，有冲牙器、电动牙刷、牙膏、牙线。徕芬做电动牙刷之前，只做高速吹风机，貌似还做的不错。 官方牙刷头，徕芬价格合理，usmile 过高。 核心选择点 最终小曾同学选择的是徕芬，理由如下：\n高速吹风机和电动牙刷的核心部件都是电机。 usmile 的宣传做得太好了，不知道是否能在产品上下同样的功夫。 usmile 的原装刷头过于昂贵，很难不联想到买刀片送刮胡刀的吉列，以及买墨盒送打印机的爱普生。 扫振一体这个宣传点已将小曾同学洗脑。 全文完 ","date":"2024-04-06","description":"人类问题的终极答案，当然还是要上一点人类工作量。","lastmod":"2024-04-06T06:48:41Z","slug":"gemini-gpt4-yiyan-kimi-electric-toothbrush","tags":["aicourse","ai"],"title":"使用AI大模型(Gemini/GPT4/文心一言/Kimi)选择电动牙刷","url":"https://blog.zengrong.net/post/gemini-gpt4-yiyan-kimi-electric-toothbrush/"},{"categories":["impressions"],"content":"下面的视频剪辑了三个不同的消除游戏对失败反馈的处理。\n三个游戏的失败反馈处理\n第一个游戏反馈较差，在可用槽位填满之后，以迅雷不及掩耳盗铃之势，直接弹出了复活界面，快得让用户都不知道发生了什么事。\n第二个游戏和第一个游戏类似，优点在于，后续又弹出了一个未能上榜的反馈，并给出了地区排名和订单目标，算是挽回了一点点用户感受。\n第三个游戏反馈较好，在最后一个槽即将填满的时候，给出了明确的声音反馈和图像反馈。\n之后，在用完槽位导致失败的界面清晰表达三件事：\n失败原因 失败的位置 复活的表现：「复活并移出3道菜」 再之后，用户选择不复活，界面回到复活前的状态，用户仍然可以选择看广告挽救，或者手动退回首页。\n用户厌恶损失，更厌恶的是不知道为何而损失。\n用户讨厌看广告，但用户也愿意为了沉没成本而看广告。\n我们在设计游戏的时候，总会认为，玩家会有和自己一样的表现。然而真实的情况是，消除游戏的玩家可能是某个人的奶奶，也可能是从没有在手机上玩过这类游戏的人。\n你大叫不可能！不可能有没玩过消除小游戏的人！\n可中国还有10亿人没坐过飞机呢，找出几亿没玩过消除游戏的人还是很轻松的。\n小游戏为大多数人的体验来设计，但又不能只针对平均水平的玩家进行设计。\n我们不能不考虑最差的用户，又必须在表现最好的用户身上赚钱，同时考虑把表现最差的用户转换成表现最好的用户。\n这不能拍脑袋，不能想当然，如果不信，那就看数据。\n数据分析专治嘴硬，数据分析会打肿你的脸。\n消除游戏《糖果传奇》的策划在 GDC：1.5万三消关卡的经验 中提到了消除游戏设计的一些关键点，可以读读，我整理了部分关键内容：\n玩家是不同的，每关体验也不同。需要用模型判断玩家进度和流失率。 解耦难度和乐趣，将放弃时间和通关时间结合，以此作为趣味性指标。 困难的关卡可能有趣，简单的关卡可能无趣。 将乐趣与难度区分开。 关卡越长，乐趣越少。谨慎建立长而有趣的关卡，这很难成功。 如果关卡难度很高，它就应该很短。 记录玩家失败尝试次数，判断玩家水平，解决大多数人的体验。 超级困难的关卡不会有回报，但也不能为平均水平玩家设计。 留存率最重要。 全文完 ","date":"2024-04-02","description":"用户厌恶损失，更厌恶的是不知道为何而损失。","lastmod":"2024-04-02T12:30:29Z","slug":"failure-feedback-failure","tags":["gamenote"],"title":"消除品类的商业化调优（三）失败的失败反馈","url":"https://blog.zengrong.net/post/failure-feedback-failure/"},{"categories":["impressions"],"content":"今天和一个年轻策划聊天，谈到平时玩的小游戏，她列举出了每天都玩的抓大鹅和玩了一年的肥鹅健身房。\n让我感到意外的是，作为一个动辄3年开发周期的重度游戏策划，她却觉得小游戏非常上头。抓大鹅每天6局，每局3-4次广告，3次分享。\n当然，作为一个专业策划，抓到鹅了就真分享，没抓到就假分享。\n聊到为什么愿意放弃玩重度游戏的时间去玩小游戏，我得到一个意料之中的回答：\n因为我妈也在玩，我妹把游戏分享给我，我发现也挺好玩。我爱的游戏他们玩不了，但这个游戏大家可以一起玩，互相分享装X挺开心。\n我们认为小游戏的优点是不用下载，随时分享。之前小游戏还是真分享的时候，有专门建「小游戏分享群」以获取分享收益的。\n分享越来越多，越来越滥，点击却越来越少。\n现在事情起了变化，小游戏的分享似乎不再是弱关系，而是一种强关系。\n弱关系和强关系 按照社会学的定义，可以从4个维度来测量关系的强弱：\n互动率 信任度 亲密程度 互惠交换 互动次数多，信任度高、关系密切，经常有利益往来，就是强关系。\n弱关系是烟花模型，强关系是渔网模型。强关系最大的力量是 冗余效应。 强关系带来的分享更多私发而不群发，强关系带来的信任感让人不假思索地点击分享卡片。\n这也是为什么 抓到鹅就真分享，抓不到就假分享 的原因。强关系已经做好了筛选，保证每次分享都是真实有效的。\n用户不是玩家 用广告变现，赚钱需要非常大的量级。买量成本很高，利润很少。因此广告变现的产品更加看重数据驱动。\n但我们并不能说，只靠数据驱动就能做好这样的游戏。\n上图来自于：用“数据驱动”干掉游戏策划？\n目前微信小游戏排行榜前几位的产品，许多都把分享作为获量手段，能更多通过自然量获取利润。\n不那么依赖买量的力量，需要产品的体验足够好，也就是更容易被普通用户所接受。\n这部分能力，才是策划的真实能力。 是数据驱动之外的不可替代的能力，是核心竞争力。\n在红利里拿不到核心竞争力 数据驱动，加上信息认知壁垒，就是行业红利，就是竞争优势，就是竞争力。\n现在手游团队越来越多地进驻小游戏，数据驱动你会我会他也会，认知壁垒不存在了。\n被你知道了，你也干不了的事，才是核心竞争力。\n核心竞争力有三个特点：\n第一，差异化。 第二，它真的是能带来优势。 第三，不可复制性。 小游戏市场，借鉴的情况很多。因为产品体量小，研发难度低。借鉴的流量，来得快，去得也快。但有的产品，既有长期的价值，也借鉴不了。「抓大鹅」「羊了个羊」，它们总是能排在榜单前列。一直被模仿，从未被超越。\n因为这样的产品，是具有核心竞争力的。这是建立在理性数据驱动之上，将策划的 感性理解力与理性分析力相结合的产物。 这是策划的天赋能力，是 「感性价值的理性表达」。\n用户的竞争还是注意力的竞争 越来越多的产品，已经融入了游戏设计思路。在支付宝里，拼多多里，美团里，我们都能看到披着游戏外衣的抢优惠券抢积分，微信开放平台（公众号小程序小游戏），本质也是为了让用户更多留在一个超级APP中，以更长时间地抢夺用户的注意力。\n游戏的方法论，一次又一次改变着大众的习惯，游戏逐渐展现出不同的样子。\n互联网世界从来没有如此游戏化，互联网产品经理想着做游戏，游戏策划发现自己变成了互联网产品经理。\n当更多人熟悉了小游戏，更多策划接受了小游戏之后，硬核游戏领域会变成什么样子？\n质疑小游戏，拥抱小游戏，成为小游戏。\n全文完 ","date":"2024-04-01","description":"互联网世界从来没有如此游戏化，互联网产品经理想着做游戏，游戏策划发现自己变成了互联网产品经理。","lastmod":"2024-04-01T12:26:20Z","slug":"player-and-game-changed","tags":["gamenote"],"title":"不是玩家变了，而是游戏变了","url":"https://blog.zengrong.net/post/player-and-game-changed/"},{"categories":["impressions"],"content":"(本文封面图由 Midjourney 生成)\n我们有时候会碰到产品基础数据还可以，但商业化数据调不起来的情况。此时去增加系统不一定是个好选择。\n如果没有预料到这种情况，或者没有通过打点数据分析找到解决方案，说明在初期思考的时候就不成熟。\n下面的思路不能说不对，但不一定有用。\n时长不够就加系统 打开次数不够就加离线奖励 变现次数不够就加广告点 社交功能不够就加排行榜 举两个例子：\n当给产品加上皮肤系统的时候，我们的思考是：加上皮肤系统之后，用户会为了获取到这个皮肤，增加付费行为。\n实际上要思考的是：\n这个产品为什么要加入皮肤系统？ 用户购买皮肤的目的是什么？ 怎样设计的皮肤系统才更能与产品匹配？ 当给产品加上排行榜的时候，我们的思考是：加上排行榜之后，用户会为了争夺排名，更多地留在游戏中。\n实际上要思考的是：\n这个产品需要排行榜吗？ 用户在乎这个排行榜吗？ 什么样的排行榜设计更能激发玩家之间的攀比心理？ 排行榜如何刷新才能不给用户带来心理落差？ 目标没想明白，基础数据没有调好，怎么加系统都没用。\n菜鸟产品做加法，高级产品做减法，顶级产品做乘法。`\n全文完 ","date":"2024-04-01","description":"菜鸟产品做加法，高级产品做减法，顶级产品做乘法。","lastmod":"2024-04-01T03:00:55Z","slug":"optimized-addition-subtraction-multiplication","tags":["gamenote"],"title":"调优的加法、减法和乘法","url":"https://blog.zengrong.net/post/optimized-addition-subtraction-multiplication/"},{"categories":["impressions"],"content":"上一篇： 消除品类的商业化调优（一）\n榜单一直都是我们关注和选择立项的重点，今天（2024-03-28）我们来看看，微信畅玩榜+抖音小游戏 Top100 中的消除品类。\n微信小游戏畅玩榜 Top100 中，有29款消除类产品。\n抖音小游戏 Top100 中，有21款消除类产品。\n是不是可以说，微信用户更爱消除？😄\n品类分析 把榜单上的所有游戏都玩过几遍之后，我将消除这个品类细分成为下面六种玩法：\n堆叠：占比 46%，榜单最大品类。无论是「羊了个羊」这种顺序堆叠，还是「抓大鹅」这种随机堆叠，都在这类。 逃脱：占比16%，「救救这只猪」、「开心挪挪车」、「打螺丝达人」。 合成：「改装大作战」、「开局托儿所」。 排序：「超级解压馆」、「你有我快吗」。 消除：「挪对对」、「做个拿手菜」。 数独：「弱虫」。 分平台看，堆叠玩法在微信 Top100 的分布是抖音的2倍还要多。其他品类微信和抖音都差不多。\n数独产品比较，微信上的「数独趣味闯关」从 2023 年开始就一直在榜单 20 名上下卡位，抖音上火爆的「弱虫」在微信上短暂进过榜，但最近没有出现。\n数独品类的一点点思考 春节前我关注到「数独趣味闯关」一直在榜，就去仔细研究了数独这个小众品类，把海外市场下载量靠前的数独产品都玩了一遍，思考了这么三个问题：\n为什么数独只在微信火了这一个产品？ 为什么没有同类产品出现？ 为什么在抖音上关于这个产品的视频很少？ 当时没想明白，就作罢了。\n「数独趣味闯关」海外手游平台数据也是相当爆炸的，比不上超休闲的爆发力，但胜在长线。\n春节过后，「弱虫」在抖音爆发了。观察「弱虫」的推广轨迹，我想明白了这几点：\n尽管数独偏难，可能导致用户量有限。但在益智产品中，它一直都有稳定的拥趸。 数独这个品类的用户，可能量少，但粘性极高，付费能力也较强。只要突破了上手难这个门槛，就会突然拥有一大批高时长用户。 我认为数独直播和视频切片效果不好，但「弱虫」做了示范。 我认为数独游戏只有男性用户感兴趣，但「弱虫」的设计风格和宣传都是针对年轻女性用户。 在「弱虫」爆火之前，上面四个结论可能都是负面的，但「弱虫」出现后，上面四个结论都是正向的。\n「弱虫」发展了数独（看似）不可能关注的女性用户，用女性喜爱的美术风格，简洁明快的特效，设计巧妙的新手引导，匹配的投放策略，开辟了一个新的市场，创造出了新的用户价值。想清楚这一点，在更优秀的数独创新者出现之前，这一批用户的价值就是该产品独有的。\n弱虫的原型在2023年5月短暂加量推广过，但在我调研数独产品时的下载量特别小，我没有玩到，感觉错过了一个亿。\n顺便说一句，抖音小游戏上的「无尽的关卡」是「弱虫」的马甲包。目前为止，抖音小游戏和微信小游戏平台上，都只有一款数独产品持续留在榜单上。\n仔细去玩玩这两款产品，或许能找到抖音和微信用户对于同一个品类的不同喜好，或许能在小游戏上再做一个爆款数独？\n题材分析 同样把榜单上的所有游戏玩过几遍之后，我将消除题材细分成为下面几种：\n实物：占比30%。这个是最多的，是不容易分类的杂货铺。「货柜趣消消」的饮料，「爱上叠叠叠」的花朵，「开心挪挪车」的车辆，都算是实物。 麻将：占比20%。毋庸置疑的真实占比第一大类。 食物：占比10%。「抓大鹅」、「做个拿手菜」，食物、水果一直都是很受欢迎的选择。 数字+文字：合并占比 15%。成语、汉字拼拆、数独。 动物：阿猫阿狗，猪猪和咸鱼，一样受欢迎。 分平台看，实物题材和麻将题材在微信平台的占比非常高。\n实物题材平台比较 有趣的是，在微信平台中，实物题材的种类繁多。花朵、钻石、货柜等题材均有较高的排名。\n抖音平台中，在榜的实物题材就比较少了。「玩了个锤子」和「打螺丝达人」的玩法是相同的。\n有一种可能，是打螺丝这种玩法，更有话题性，在抖音平台更容易爆量？\n麻将题材平台比较 从数据看麻将题材在微信有7款产品在榜，实际上有4款产品是同一个产品的马甲包。\n这样看来，抖音平台的麻将题材，可以说和微信平台数量基本一致了。\n这也充分说明了，麻将这个单一题材，在微信和抖音，都是非常受欢迎的。\n立项做一个比榜上所有麻将消除质量更高的产品，是不是一个好选择？\n全文完 ","date":"2024-03-28","description":"立项做一个比榜上所有麻将消除质量更高的产品，是不是一个好选择？","lastmod":"2024-03-28T10:52:17Z","slug":"unite-business-2","tags":["gamenote"],"title":"消除品类的商业化调优（二）小游戏榜单分析","url":"https://blog.zengrong.net/post/unite-business-2/"},{"categories":["impressions"],"content":"基于海外休闲游戏做本土化改造，一直都是小游戏平台中的产品创新思路。\n2023年开始，大量手游团队进入小游戏领域，排行榜上的游戏质量大幅提升。加上 Unity 在小游戏平台的表现越来越成熟，手游团队能使用自己更顺手的工具进行游戏研发，小游戏榜单上的产品就越来越卷。\n我最近在持续关注小游戏榜单上的消除品类，希望学习国内小游戏平台上的优秀团队，怎样把海外的优秀创意搬运到国内，并做出更符合国内市场的商业化决策。\n今天先做一点非常粗浅的体验，随着理解深入，可能会有总结性输出。\nZen Blossom 这款 2022 年 7 月上线的产品，上线半年后开始大推，至今每月仍有百万级别的下载量。\n2815b.mp4\n看看位于小游戏榜单前列的麻将消除产品，是不是有点像呢？\n2815a.mp4\n这款消除的爽感来自于两点：\n全部完成的满足感 连击爽感 由于追求爽感和完成度，在游戏过程中打断玩家体验就比较犯忌了。因此游戏中采用 体力 和 时间限制 的方式进行商业化。\n在海外平台可以进行插屏广告变现，但国内小游戏插屏价值过低，所以在小游戏中采用的是局内分享获取道具，或者局末激励视频双倍领取等常规方式进行商业化。\n是选择裂变还是选择激励视频，在微信小游戏中需要仔细思考。我们如果持续跟进单个产品的更新，也会发现位于排行榜前列的产品，对于裂变和激励视频的比例，总是处于调整中。根据流量的变化，发行方也在持续寻找平衡点。\nHexa Sort 再来看看这款 Lion Studio 发行的游戏，2023年11月上线，目前全球累计下载量接近 900 万。\n2815d.mp4\nLion 近期也在微信小游戏平台上线了移植版本《我最爱解压》，且已经上榜。\n我们注意到，在微信小游戏平台，无论是 Lion 的官方版本，还是其他借鉴版本，都无一例外采用了 封锁格子 的商业化模式。\n2815e.mp4\n不得不说，锁格子的方式丑是丑了点，但胜在简单粗暴有效。\n全文完 ","date":"2024-03-26","description":"不得不说，锁格子的方式丑是丑了点，但胜在简单粗暴有效。","lastmod":"2024-03-26T12:47:40Z","slug":"unite-business-1","tags":["gamenote"],"title":"消除品类的商业化调优（一）","url":"https://blog.zengrong.net/post/unite-business-1/"},{"categories":["impressions"],"content":"产品列表 Google Play/AppStore： We Are Warriors!（有内购） 微信：抓鸡大师（有内购） 抖音：鹅鹅出击（无内购） 类似产品： The Army（有内购） 产品数据 双平台每日下载量10万+，目前DAU百万左右，日内购收入$4-5万。按 10% 内购比例计算，每日收入为 $40万左右。\n总新增中，买量为自然量的2倍。最近两个月买量占比逐渐增加。\n会话次数平均 5.14，时长 2304 秒。每天超过1小时的人占 20%。次留 56%，7留15%，60留5%。\n从我的个人体验来看，每天至少 10+ 广告，时长超过 1 小时。\n系统分析 Warriors 产品本质是一个数值放置游戏。它加入了卡牌和单局策略玩法，将数值类游戏特有的数值爆炸体验加入到了单局卡牌成长中。\n游戏的系统非常简单，我将其归纳为：三种货币，两个循环，一次暴涨。\n图中红色部分，是给玩家带来 情绪 的重点， 通过 时代跃迁后形成的数值暴涨和跨时代碾压，给玩家带来极度舒爽的体验。\n蓝色部分为核心玩法层面，是玩家 持续体验 的核心， 时代跃迁后对升级循环的重置，以及战斗循环中的不同配比带来的随机结果（巨大的收益差）， 持续推动玩家进行体验。\n黄色部分是游戏的货币系统和商业化内容。这部分相当简洁，也没有做太多的关联。次要货币宝石只用来抽卡做永久性升级，局内货币（食物）的实时增长只能通过局内广告点获得，金币只能通过战斗循环获取。\n至于跨越时间线之后增加的地牢玩法，主要是为了增加长留和视频点，并不会给游戏体验带来根本变化。\n后期增加的技能支持，增加了一种新的货币，需要使用推关来解锁，核心目的依然是增加长留。但技能的有效使用，增加了战斗微操变化，对战斗体验还是有影响的。\n循环 在如此清晰的目标中，玩家只需要不断进行战斗和升级，在简单调整策略后获得巨大的收益差，在两三分钟内就能获取一次调整反馈的机会，在获得胜利（推基地成功）后得到巨大的快乐奖赏，对方基地升级后的时代落差又重新将玩家拉入 升级循环。\n战斗策略 战斗循环中的策略简化为两个维度：\n选择兵种 选择出兵节奏 基地只有血量没有攻击力，但依然可以依赖基地做一些附加策略，例如：\n在敌人到达基地门口的时候制造远程兵种 怼门攻击让敌方失去远程攻击力 估计制造势均力敌的场面，以增加杀死更多士兵的收益 战斗策略选择乐趣 尽管战斗策略简单，但在 战斗循环 过程中，不同的策略选择，或者一个微小的疏忽，可能导致失去之前的巨大优势。\n例如下面这段视频：\n肉盾优势\n由于缺少肉盾角色，远程攻击过于靠前，导致攻击力落在对方的基地上。前期建立起来的人数优势，被对方的肉盾+远程迅速瓦解。第二波组织起来的进攻被对方小兵迅速消耗。第三波的远程就沦为了炮灰。\n只需要稍微调整一下策略，在第一波远程前加上几个肉盾，战斗的结果就会反转。\n这就是战斗循环中策略选择的核心乐趣。\n升级中的新手引导策略 由于早期解锁兵种，升级基地血量对金币的要求巨大，玩家只能选择升级食物增长的速度。这在一定程度上起到了新手引导的作用。\n早期升级基地能带来的收益是极低的，在经历过几轮失败后，玩家能自然感觉到兵种配比对战局的决定性作用。在这里，玩家会进入到第一个选择决策： 是升级食物生产速度，还是解锁新的兵种。\n当食物生产速度升级到 0.76 之后，解锁新兵种的消耗会低于持续升级食物的消耗。此时玩家也会感觉到，在这个食物生产速度上，影响战斗的因素更多来自于兵种配比和出兵节奏，玩家此时会更倾向于解锁新的兵种。这个决策是顺理成章的，不需要选择。\n游戏让玩家在战斗循环的过程中自主学会了升级决策，这个新手引导是非常自然的。\n在玩家完成了一次推倒对方基地的操作后，立刻会进入到对方高于自己一个文明等级的状态。此时玩家会意识到，如果不进行跃升，在食物产出速度较低的情况下，是很难打败高于自己一个等级的文明的，此时跃升就是最好的选择。这个决策也是顺理成章的，不需要选择。\n跃升之后，金币和食物产生速度都会归零，玩家又无法打败自己同等级文明的敌人了。好在此时战斗中能获取到的金币数量也有了大幅的暴涨，在战斗循环中又可以快速获得金币资源，进行升级。\n在这个升级循环中，玩家在不知不觉中完成了新手引导的过程。由于数值暴涨，又不会纠结于跃升归零的现状，从此乐此不疲进入 战斗 -\u0026gt; 跃升 循环。\n世界观包装 界面是简化过的，尽量让玩家做最少的选择，甚至不需要新手引导。无论是战斗过程中的界面，还是系统界面（例如升级、卡牌抽取等等），都紧紧围绕简化选择这个目标。\n在早期升级基地血量是没有意义的，基地升级消耗远超食物生产速度的特点，也明确告知了玩家需要放弃这个选择。\n商业化 金币是游戏中的主要货币，为了在战斗中获取金币，需要使用不同的战斗策略。\n在石器时代中，敌方总共只会出六波兵，出完之后就不会出兵。要想获得最高的收益，就不能提前推倒基地，需要先将这六波兵全部消灭。在石器时代中，推倒基地加消灭所有士兵，可以获得最高1K金币，加上广告翻倍就是2K金币，对于早期的玩家，这个翻倍几乎是无法拒绝的。\n国内小游戏 小游戏平台上已经出现了大量直接搬运的产品。许多产品都通过大量投放上过排行榜。\n我仔细比较过这些产品的数值，很多数值是直接还原 Warriors 原作，也有一些数值做了些许调整以匹配纯 IAA 商业化形式。\n数值有巨大差异的是最后一款产品《猫狗家园争霸》，它完全替换了原作的结构，将产品改得更像是一个超休闲游戏，快速获得原版数倍的爽感。但我已经被 Warriors 虐到变态看广告了，对这个修改方式反而找不到任何感觉。\nThe Army 与 Warriors 类似玩法的还有一款 The Army，这款产品很明显是给数值控核心玩法准备的。\n同时需要考虑的内容更多，资源有限，实时调配好数量、攻击力、血量、速度之间的平衡，配合不同的兵种，能打出更多有趣的策略，这些策略是普通玩家（非核心玩家）所无法感受到的乐趣。\nThe Army 这种玩法是比较挑剔受众的，不经过修改，很难在国内小游戏平台有所作为。\n全文完 ","date":"2024-03-25","description":"双平台每日下载量10万+，目前DAU百万左右，日内购收入$4-5万。按 10% 内购比例计算，每日收入为 $40万左右。","lastmod":"2024-03-25T12:30:00Z","slug":"warriors-like","tags":["gamenote"],"title":"产品分析：Warriors like","url":"https://blog.zengrong.net/post/warriors-like/"},{"categories":["impressions"],"content":"在深入跟进产品技术方案的过程中，我发现技术生产力对于产品的影响是不足的。这体现在两个方面。\n一是产品追求低质量的快速上线 流量化产品，追求 快速上线、快速试错、快速放弃 是正确的选择。制订一个严格的计划是有必要的。\n严格的计划需要严格的执行，需要训练有素的执行者，需要经验丰富的领导者。\n上面的要素缺一不可，否则就会返工。返工是不可能实现快速上线的。\n经验丰富的领导者， 需要提前预判大部分开发过程中的坑，提前部署好资源，调配生产力使之不出现互相等待。能预料到坑，要先踩过类似的坑。\n训练有素的执行者， 需要有较高的专业素养，技术和美术实现尽量达到一次成功，对于领导者没有提出的可能性，要预判并提前做好部署。对于领导者提出的不必要的复杂性，要有能力做出简化。\n二是技术对产品的支持有限 大多数情况下，我们只是在完成策划给出的目标，对这个目标应该如何 SMART 实现，缺乏足够的思考。\n许多目标，由于策划的经验不够丰富，需求不够具体，会导致返工，返工会影响产品上线的进度。\n返工又会导致技术人抱怨给的时间不够。\n失败产品循环 需求理解不全面 -\u0026gt; 排期不合理/技术实现不合理 -\u0026gt; 返工 -\u0026gt; 上线延迟 -\u0026gt; 抱怨时间不足 -\u0026gt; 全面影响产品质量 -\u0026gt; 产品失败 -\u0026gt; 新立项\n能不能既快又好呢？ 我们经常会听到这样的信息：\n这个产品是一个人做的，赚了一千万 这个产品是十天做的，赚了八百万 这个产品只有三个人，维护一年多了，赚了两千万 这种结果论的暴富故事没有价值。他们要么是通过长期积累解决了我们存在的问题，要么是运气好踩到风口（注意：运气也是实力的一部分），也可能是两者均有。\n有三条路，解决问题，提升气运：\n锻炼/寻找训练有素的执行者 确保每个产品参与者的能力（尤其是策划）螺旋上升 提升下限，沉淀最佳实践，实现生产力流水线 全文完 ","date":"2024-03-24","description":"确保每个产品参与者的能力（尤其是策划）螺旋上升","lastmod":"2024-03-24T03:29:21Z","slug":"impact-of-technology-on-products","tags":["gamenote"],"title":"技术对流量产品的影响力","url":"https://blog.zengrong.net/post/impact-of-technology-on-products/"},{"categories":["impressions"],"content":"周读这个系列记录我读过的一些重要文字，略作整理和评论。\n哇哈哈集团创始人董事长宗庆后 来源：微博\n中国共产党党员，全国劳动模范，全国五一劳动奖章获得者，优秀中国特色社会主义事业建设者，改革开放40年百名杰出民营企业家，第十、十一、十二届全国人大代表，中国共产党浙江省第十二、十三、十四届代表大会代表，娃哈哈集团创始人、董事长宗庆后同志，因病医治无效，于2024年2月25日10时30分逝世,享年79岁。\n朋友圈里的评论： 那代人的经历真的是跌宕起伏，时代的洪流冲击过的生命，不枉此生。\n巴菲特在佛罗里达大学商学院的演讲 来源： 巴菲特一生最经典的演讲\n为方便阅读，我进行了一些内容修改：\n你会选班里的哪位同学，买入他今后一生之内 10% 的收入。你还要同时做空另一位同学今后一生 10% 的收入。\n你会选择买入谁？你未必会选智商最高的，你也未必会选考试成绩最好的，你甚至不会选最努力的那个人。也许你会选你最有认同感的那个人，那个拥有领导能力，能把别人组织起来的人。 这样的人应该是慷慨大方的、诚实正直的，他们自己做了贡献，却说是别人的功劳。\n你会选择做空谁？你不会选智商最低的。你会想到那些招人烦的人，他们可能学习成绩优秀，但你就是不想和他们打交道，不但你烦他们，别人也烦他们。 这样的人可能自私自利、贪得无厌、投机取巧或者弄虚作假。\n比较两种人的品质，对于你想要做多的人的品质，这些是关于行为、脾气和性格的品质，是能培养出来的。想拥有的话，你可以得到。\n对于那些令人生厌的品质，没一个是你非有不可的，你身上要是有，想改的话，可以改掉。\n这篇文章中还有许多内容，例如巴菲特说到想写一本《聪明人怎么做蠢事》，例如他提到的关于「娘胎彩票」的幸福观点。\n游戏行业面临30年来最大放缓……吗？ 来源：\n外媒：外媒：游戏行业面临30年来最大放缓 2023年Steam年度最畅销 最热玩 等多个榜单公布！ TikTok报告：市场和玩家都变了、全球超20亿人都是手游玩家 《金融时报》发文：\n“2000 亿美元的视频游戏产业正在面临 30年来最大的放缓，由智能手机游戏和最新一代游戏机驱动的巨大增长已经达到极限。”\n手机方面：\n全球智能手机销量下降意味着新玩家减少，而手游新玩家已成为近年来游戏行业最赚钱的部分。去年移动游戏消费支出下降了 2%，至 1073 亿美元。\n主机方面：\nPlayStation 5 于 12 月销量突破 5000万台后，索尼集团总裁十时裕树，公司「已进入主机生命周期的后半程，销量将逐步下降」 任天堂的Switch2网传定档2025略晚于市场预期，微软没准备为Xbox进行中期迭代。\n游戏行业主机难题：\n没有人购买 Xbox，PS5在大幅降价后也达到峰值，每个人都在等待 Switch2.0。\nXbox 的策略：\n在斥资750亿美元收购动视暴雪后，公司希望在一个日益饱和的市场中挖掘新的增长来源，因此寻求在竞争对手的游戏机上销售更多自有游戏。\n重启IP：\n每个月都有数千个游戏上市，成功率非常低。风险和竞争变得更加激烈，也推动了索尼、微软、EA和其他大型游戏公司对好莱坞模式的依赖，即重启相同的大型IP。\n如果说上面的分析给我们泼了一盆冷水的话，Steam 官方公布的数据又给我们点了一把火。\nSteam 官方数据：\n季节性特卖活动期间销售额大幅增长。Steam每年会对应四个季节举行四次大型特卖活动，期间大部分游戏都会参与并进行额度较高的打折。Valve以秋季特卖为例，指出在不同收入阶层的游戏在特卖期间的表现都远超前一年同期。\n2023年有1180万玩家参与了Steam上的游戏测试，同比增长41%。\nTiktok 的报告让我们看到了不一样的方向：\n定义「现代玩家」：\n每周至少在手游上投入7个小时； 成为特定品牌或IP忠实粉丝的概率是普通玩家的1.6倍； 有三分之一的机会成为新游戏和平台的早期尝试者； 与朋友分享游戏想法的可能是普通用户的两倍； 每周观看20小时直播的概率高84%； 追随流行文化的可能是其他玩家的五倍。 三个关键总结：\n手游市场的重大变化意味着营销者们必须调整他们的策略优先关注长期玩家忠实度； 在这个新的手游市场成功围绕三个核心支柱：建立文化关联、与玩家建立连接，并形成社区的意识； 手游市场的进化带来了一种新型玩家，了解现代玩家对于希望在这个新环境与玩家达成互信的所有发行商都至关重要。 手游玩家总数未有下降迹象：\n在2024年，全球手游玩家数量即将超过20亿，意味着地球上四分之一的人都是手游玩家。这个增长主要来自于亚洲和拉丁美洲，如韩国、巴西、土耳其和墨西哥过去两年的玩家消费都持续增长， 发行商们也在将这些区域作为未来继续增长的下一个前线。\n长期成功的关键性支柱：\n发行商们需要拥抱三个支柱：文化关联性、连接和社区。 文化关联性， 指的是要创造挖掘时代精神的游戏。 连接， 指的是与玩家形成一种持续的关系。71%的现代手游玩家想要体验一款来自他们信任的发行商的游戏，73%的现代玩家想要玩一款理解玩家的手游。 社区主要的目的， 就是将玩家围绕共同的热情和体验聚集起来。 61%的现代玩家认为「与志同道合的人连接」很重要，如果是好友讨论的游戏，现代玩家推荐它的概率比普通玩家高27%。 拉黑目标客户也可以降低转化率 来源： “李一舟何许人也” 昨天我在朋友圈看到一条吐槽“不买课也被坑”，大意是只因为在李一舟直播间说了句，“逼单技巧不错”，就被火速拉黑，进而导致后面玩小程序游戏，关卡出现李一舟的直播推广又进不去，游戏也玩不成。但偏偏她还挺认可李一舟的营销话术，因为可以“最大程度筛选出目标客户，降低后续的转化成本”。就挺荒诞的：你可以讨厌他，但不能不认可他。 商业贩卖需要的是激情或者欲望，而不是知识，所以，在我们如今这样一个知识堪称遍地可得的年代，如何混淆知识，让人们忘记知识，回复原始激情欲望的状态，便是商业输赢的关键。\n曾嵘：这部分我真的没法评论……\nAI课程销售和广告投放已经受到李一舟事件的影响了。是因为之前的投放量太大，被不该看到的人看到了？AI是目前举球关注的大事，导致李一舟被神秘力量研究了？\nSam Altman：如何提高工作效率 来源： 炸裂AI技术Sora背后：奥特曼清单法\n下面是曾嵘的总结，为方便阅读，做了一些修改：\n复合增长也适用于职业， 如果你每天比别人多做 10%，进步 1%，复合（增长）的差异是巨大的； 对世界有着强烈的信念，培养坚持自己信念的信心； 尽量不做不关心或不喜欢的事情上； 出色的工作通常需要某种同事。 试着和聪明、多产、快乐、积极的人在一起，他们不会贬低你的抱负，和那些推动我并激励我变得更好的人在一起；在力所能及的范围内，避开相反类型的人； 确保完成重要的事情；不要把时间浪费在愚蠢的事情上；列很多清单； 拥有一个开放的网络，不要拒绝开会； 了解自己最高效的时间段，分配最重要的事； 正确的目标是最佳地分配你的一年，而不是你的一天； 睡眠是影响生产力的最重要的物理因素；其次是锻炼；第三是营养； 为经常做的重复事情写定制软件； 不要为了生产力而忽视你的家人和朋友。 全文完 ","date":"2024-02-26","description":"2000 亿美元的视频游戏产业正在面临 30年来最大的放缓，由智能手机游戏和最新一代游戏机驱动的巨大增长已经达到极限。","lastmod":"2024-02-26T10:03:16Z","slug":"gamenote202401","tags":["gamenote"],"title":"周读202401：游戏行业面临30年来最大放缓……吗？","url":"https://blog.zengrong.net/post/gamenote202401/"},{"categories":["impressions"],"content":"听说最近最火爆的流量密码是在视频中标注： 该作品由人工智能Sora生成。\n所以我也试一下这个标题能不能火🔥吧哈哈哈。\n接着昨天那篇 Sora 文章汇总 ，这是我今天读过的 AI（主要是Sora） 文章汇总。\nSora 学习手册（复制下面的链接访问）\nhttps://yunyinghui.feishu.cn/wiki/BaCEwe3AliqYERkc9dVcfW0BnXg\n硅星人Pro：Sora带来的四点启发\n其实从宫斗的处理，到今天Sora发布，OpenAI的每一次重要的动作和发布，都配合着一场场精巧设计的叙事和campaign，它的设置议题的能力，节奏控制，公众引导熟练自如。哪怕你只想喊两句“中美差距拉大”吃一吃情绪馒头，也至少该知道这一切。\nAI前线：Sora生成的视频太真实？那是你遇到造假了\n“本次拍摄中没有动物受伤。”\n幼儿园毕业才能看懂的……哆啦A梦：与 Sora 同行 中学生就能看懂的……Sora 原理解读 硅星GenAI：GPT-4 与国产模型，大！横！评！\n综合来看，GLM-4表现可圈可点，全面对标GPT的功能布局，可以用六边形战士来形容；豆包语义理解最强，更适用在生活化问答场景；文心虽然在代码生成解释环节输掉，整体实力不容小觑；GPT4的整体实力非常强，但应付国内的生产生活场景，还是有点吃力。\nGLM-4的新功能中，让我印象深刻、帮助最大的，当属“数据分析”，对比同功能的GPT分析效果一致；还可以调教智能体，不会编程也能轻松拥有专属大模型；联网查询帮助也很大，实际体验效果不凡。对比智谱AI上一代模型，GLM-4的各项基础模型能力做到了全面强化，进步明显，在“数理计算”和“代码生成”有质的提升。\n硅基立场：AI Agent的任务，是让我们每天最多工作四个小时\n“Agent”第一次作为人工智能术语的出现，是1995年出版的经典人工智能教科书《人工智能：一种现代方法》（Artificial Intelligence: A Modern Approach）。这本书对人工智能的定义是：“智能代理的研究和设计”（study and design of intelligent agents）。这么看，“Agent”被视作人工智能发展的终极目标，至少也是快30年前的事了。它折射了人类发展人工智能的初衷，即寻找人类的一切行为的“代理人”。\nOpenAI 估值或跃升至 1000 亿美金，被曝光正在秘密开发两种代理软件\nOpenAI 内部正在进行初步讨论，计划筹集新一轮资金，这笔最新融资将使 OpenAI 的估值跃升至 1000 亿美元或以上。\n给真实视频标注“该作品由人工智能Sora生成”，成了当下的流量密码\n威尔史密斯本尊整活Sora\n他还假装很震惊，配上“形势已经失控了！”的文字，结结实实骗过了不少网友。最令人毛骨悚然的是，你很难判断这是真实表演还是AI生成的\nYann LeCun：OpenAI的Sora注定死路一条！\n所以 Yann LeCun 认为实现“世界模型”的方式，应该是让机器智能像人类般学习、建立起周遭世界的内部模型，从而高效学习、适应并制定计划以完成种种复杂的任务。\n这也是他提出的 JEPA（Joint Embedding Predictive Architecture，联合嵌入预测架构）的核心特点所在：它并不是在“生成”，而是在表示空间中进行预测。\n在软件测试中使用 ChatGPT\n使用 ChatGPT 进行许多操作，缩短自动化项目和 CI/CD 管道的创建和执行时间，比如：\n准备测试计划和测试场景——在创建基于 Web、移动或 Cucumber 测试用例的测试场景时大模型非常有用。\n用不同的语言创建脚本——大模型所掌握的每一种编程语言的知识令人惊叹。\n设计测试用例——它可以根据通用模型为特定业务逻辑创建特定的设计模式。\n创建电子邮件模板——生成测试数据是模型的最大优势之一。\n全文完 ","date":"2024-02-20","description":"该作品由人工智能Sora生成。","lastmod":"2024-02-20T09:54:39Z","slug":"sora-messi","tags":["ai"],"title":"热搜第一的梅西回应视频是 Sora 生成的吗？","url":"https://blog.zengrong.net/post/sora-messi/"},{"categories":["impressions"],"content":"这两天 Sora 的消息满天飞了，我找了十几篇文章读了读，总结了一下要点给大家，方便进行快速理解。\n技术向 OpenAI 的 Sora 技术报告详解\nOpenAI 的研究论文 Video generation models as world simulators\n论文关键点（来自 知白 白话AI编程）：\n统一的视觉数据表示：研究者们将所有类型的视觉数据转换为统一的表示，以便进行大规模的生成模型训练。Sora 使用视觉补丁（patches）作为其表示方式，类似于大型语言模型（LLM）中的文本标记。 视频压缩网络：研究者们训练了一个网络，将原始视频压缩到一个低维潜在空间，并将其表示分解为时空补丁。Sora 在这个压缩的潜在空间中进行训练，并生成视频。 扩散模型：Sora 是一个扩散模型，它通过预测原始“干净”的补丁来从输入的噪声补丁中生成视频。扩散模型在语言建模、计算机视觉和图像生成等领域已经显示出了显著的扩展性。 视频生成的可扩展性：Sora 能够生成不同分辨率、时长和宽高比的视频，包括全高清视频。这种灵活性使得 Sora 能够直接为不同设备生成内容，或者在生成全分辨率视频之前快速原型化内容。 语言理解：为了训练文本到视频生成系统，需要大量的视频和相应的文本标题。研究者们应用了在 DALL·E 3 中引入的重新描述技术，首先训练一个高度描述性的标题生成器，然后为训练集中的所有视频生成文本标题。 图像和视频编辑：Sora 不仅能够基于文本提示生成视频，还可以基于现有图像或视频进行提示。这使得 Sora 能够执行广泛的图像和视频编辑任务，如创建完美循环的视频、动画静态图像、向前或向后扩展视频等。 模拟能力：当视频模型在大规模训练时，它们展现出了一些有趣的新兴能力，使得 Sora 能够模拟物理世界中的某些方面，如动态相机运动、长期一致性和对象持久性等。 量子位：爆火Sora参数规模仅30亿？\n英伟达AI科学家Jim Fan认为：Sora应该是一个数据驱动的物理引擎。 谢赛宁认为： Sora是视频生成的GPT-3时刻。 信息平权：继续解读Sora、超微SMCI暴跌\n这次创新的关键，可能在于找到了表达视频信息最合适的representation，即spaceTime latent patch，进而可以用OpenAI最擅长的“大力出奇迹”去scale up数据和参数规模，不仅实现了更高精度的扩散模型，甚至涌现出了对物理世界和因果关系的理解\n差评：OpenAI今天刷屏的视频模型，是如何做到这么强的？\nOpenAI 在训练上的路线选择也稍有不同。他们选择了 “ 原始尺寸、时长 ” 训练，而非业内常用的 “ 把视频截取成预设标准尺寸、时长 ” 后再训练。\n格隆：OpenAI炸裂升级！又一个行业被干掉了\nSora主要采用了两种技术。\n一个是扩散模型（diffusion model），原本是用于文字转图片的。Sora的团队使用了DALL-E 3背后的技术，即扩散模型。扩散模型经过训练后可以将模糊的随机像素变成图片。 另一项技术是Transformer的神经网络，就是GPT（Generative Pre-Trained Transformer）中的T。Transformer 架构中，全注意力机制的内存需求会随着输入序列长度而二次方增长，计算成本太高了。所以他们开发了一个视频压缩网络，先把视频数据降维到latent（潜空间），再将压缩过的数据生成 Patche，这样就能使输入的信息变少，有效减小计算量压力。然后，为了让大模型更好理解用户的意思，OpenAI 直接把文生视频模型套进已经得到市场认可的GPT模型范式中，这就是它独有的优势了。 AIGC开放社区：OpenAI公布Sora技术报告：模拟世界、视频扩展等，强的离谱！\n为什么其他模型，很难生成4秒以上的高质量视频？ 一个重要原因就是缺少——高质量训练数据。\nSora在经过大规模训练后，会表现出许多有趣的新能力，能够模拟物理世界中的人、动物和环境的某些方面。\nSora 拥有视频连接功能，扩展生成视频功能，视频剪辑能力。\n量子位：Sora背后团队：应届博士带队，00后入列，还专门招了艺术生\n应届博士带队，多名 DALL-E 的创作者。\n内容影响 互联网怪盗团：Sora会对视频内容创作产生什么样的影响？\n裴团长的观点总结：对于热点话题的“时效性覆盖”将主要是AI的任务，这方面很难玩出花来。优质的垂类创作者更容易从AI中找到自己的优势。\n果壳：关于 Sora，我有十个小白问题\nSora 在日语中是“天空”的意思，引申含义还有“自由”。\n虽然 Sora 都还没公测呢，但已经有人开始卖付费教程了。\n腾讯科技：Sora“碾压”一众模型，Pika等创业公司再无活路？\n从ChatGPT、DALL-E3，再到Sora，如果用一句话来总结OpenAI的与众不同之处，那就是：技术想象力和工程能力，要远比技术路线或者黑科技重要。\nSora爆发的当下，普通人要做的事情可以归结为三件事：\n应用到你熟悉的场景中 探索属于你的新模式 成为规则的“领航员” 字母榜：Sora会“杀死“剪映吗？\n张楠卸任抖音CEO，投入全部精力用AI改造剪映，被外界视为是字节内部希望提速AI发展的一个信号。\n全文完 ","date":"2024-02-19","description":"十几篇文章读了读，总结了一下要点给大家，方便进行快速理解。","lastmod":"2024-02-19T09:48:55Z","slug":"sora-articles","tags":["ai"],"title":"Sora 文章汇总","url":"https://blog.zengrong.net/post/sora-articles/"},{"categories":["impressions"],"content":"高铁上读到一本《过程经济》，结合游戏付费有点思考。\n营销的四次升级 书中介绍到营销的四次升级：\n1.0 产品为中心，强调产品的功能价值 2.0 消费者中心，满足消费者的差异化价值 3.0 提高消费者的独特参与价值 4.0 消费者参与产品共创 游戏营销 游戏作为一个文化产品也需要营销升级，这十几年的游戏从千人千面到 UGC 共创，走的几乎是一模一样的路子。所不同的是，产品营销是为了提升销量，游戏内营销是为了……好吧，也是为了提升销量。\n远古单机和未联网主机时代，消费者反馈只能借助游戏销量套数和专业杂志的读者反馈，这些反馈不是实时的，也不一定准确。在互联网的出现后，游戏市场无论是发行方式还是付费方式都在发生变化。\n单机买断制 单机买断制 市场开发出 DLC（Downloadable Content）这种模式，和光盘没啥不同，无非是可以根据用户反馈来确定是不是发 DLC，或者发布多少 DLC。有些单机游戏把 DLC 当成大版本升级来做，或者当成首发时开发时间不足的补丁之作。还有的游戏一口气能出几十上百个 DLC，直接把 DLC 当成功能开发来做了……无论如何做，单机买断制游戏都没有脱离以产品为中心的营销本质。\n免费网络游戏 免费网络游戏 （下载免费内购付费）的营销方式则完全是消费者中心的。从王者荣耀到原神，从 Minecraft 我的世界、Roblex 罗布乐思到蛋仔派对、元梦之星，从追逐消费者差异化体验，提升消费者参与价值，到 PGC+UGC 的内容共创，都表现出消费者在文化产品营销中的主动参与性越来越强的现状。\n娱乐圈粉丝后援会模式，应该是当下社会环境中，用户参与模式最高阶的表现…… 吧？\n长线广告付费游戏 广告游戏与上面两者明显不同。\n我把广告游戏分成两种，一种需要进行适度的用户运营，关注用户的长期体验，在意用户的行为数据，需要持续根据用户的行为数据改善产品付费表现。这种产品中的广告是主要或者次要的商业化方式，和内购游戏充值没有区别。设计者需要考虑的在付费手段上，怎样将用户付费心理中对看广告的理解转换成对付费的理解，将用户看广告消耗的时间价值转换为心理账户上的付费价值。\n这类游戏，我暂且称之为 长线广告付费游戏。无论是纯广告变现，还是混合变现，都可以参考（在一定程度上）上面提到的免费网游来思考用户行为。理解用户对看广告行为价值本身的理解， 是不得已而为之（例如政策原因），还是因为产品本身在广告变现上有优势，或是要兼顾免费用户的价值，抑或兼而有之？ 要在产品立项前必须想清楚这个核心点。\n流量广告游戏 另一种广告游戏，我暂且称之为 流量广告游戏。这是纯粹使用广告变现（无内购），使用大规模原生素材或第三方素材，一波流投放，只关注转化率（投放端起量）和短期 ROI 的产品。 我们可以把这类产品理解为 带有游戏玩法的流量整合产品 。在立项和产品研发的过程中，不要将传统游戏产品中关注的参与感、归属感、价值感代入到这类产品当中，而要更多思考是广告主的价值，推广算法的运行方式，整合社会热点的可能性。\n流量广告游戏 可以长线运营么？我认为 可以长线，但不能运营。 这类产品的受众是大众而非游戏玩家，需要通过长期叠加大众喜闻乐见的内容来保持热度，可以蹭热点，做擦边，和现在抖音起号，微博涨粉的操作类似。\n只要能保持 CPI 稳定，或者能持续找到免费或者低价的流量来源，这类产品就能持续获取利润。这类产品的核心目标不是人而是流量算法，因此不适用针对人的运营方式。\n广告游戏的运营团队建设 如果一家发行公司建设运营团队，要对 长线广告付费游戏 和 流量广告游戏 厚此薄彼吗？要建设不同的运营团队分别管理这两种类型的游戏发行吗？\n不妨换一个角度思考这个问题，先看一个不太恰当的例子。\n我昨天接到某国内知名的养老社区机构打来的电话，邀请我去他们的养老社区参观。对于这个营销行为，我面对的是「养老市场」，还是「银发经济」？\n引用蔡钰老师的话来说：\n养老市场是购消分离的老人客场，由「年轻人来判断老人需要什么」，服务目标是「帮老人与失能相处」； 而银发经济是购消一体的老人主场，是「老人来判断自己需要什么」，服务目标是「帮老人享受生活」。\n类比一下：\n流量广告游戏 是流量与体验分离的玩家客场，由「投放算法来判断玩家需要什么」，服务目标是「更高的CTR、更低的CPI、更高的eCPM」，产品本质是「让算法发现更匹配的高用户价值」；\n而 长线广告付费游戏 是流量与体验一体的玩家主场，由「玩家更主动判断自己需要的体验」，产品本质是「让玩家发现更匹配的高用户体验」。\n如今的游戏发行，产品运营，立项和研发，无论如何都无法避开被流量掌控。既然如此，可以换一个方向思考，基于流量和产品的本质，找到更切合实际的方法，提升产品本身的价值。\n产品是有价值的，流量也是有价值的。\n要结合它们创造出新的价值。\n全文完 ","date":"2024-02-05","description":"将用户付费心理中对看广告的理解转换成对付费的理解，将用户看广告消耗的时间价值转换为心理账户上的付费价值。","lastmod":"2024-02-05T09:41:49Z","slug":"process-economy","tags":["readingnote","reading","gamenote"],"title":"《过程经济》、游戏付费与运营团队建设","url":"https://blog.zengrong.net/post/process-economy/"},{"categories":["impressions"],"content":"2024 已经过去十二分之一，是时候简单总结我的 2023 了。\n这是一篇个人记录流水账，没有任何价值。\n阅读 2023 年我在得到 App 学习了 33 门课，读了 23 本电子书，299 本听书解读，关注了 21 个领域的知识，写了 639 条笔记。主要的学习是通过听来进行的，语音播放速度为 2-2.5 倍速。\n这数据我不太满意的。本来读书计划应该是 50 本，实际完成不到一半。\n纸质书读的比较少了，应该不到 10 本，没有做记录。\n碎片信息的获取主要在公众号。公众号阅读我没有进行仔细统计，印象中大约每天10篇的样子，全年应该有 3000+ 篇。微信出 AI 听全文功能之前，主要用讯飞有声来听，微信 AI 听功能稳定之后就只用微信听公众号文章了。AI合成语音比真人语音更好理解，一般用 3 倍速来听。\n运动 Keep 是我最常用的健身 App，基本上每次健身的时长我都会使用 Keep 来记录。\n健身 136 天，平均 3 天一次，每次 45 分钟。由于大部分的健身活动都使用 10 分钟跳绳来热身，所以跳绳这个课程使用得会比较多。\n输出 根据微信公众号的统计，2023 年我一共发布了 28 篇内容，其中 18 篇原创，共计 4.9 万字。12 月输出最多。\n由于我为博客定制的 AID 系统还没有开发完成，所以疏于打理的博客暂时停更了。这个月（202402）已经立了 Flag 把博客更新起来。\n找到了一个不错的插件，可以把公众号内容同步到知乎B站简书，这可方便了。\n公众号和博客之后会写什么？我想主要是：\n对玩过的游戏基于个人认知的感受。 对行业和各种乱七八糟的知识的阅读和理解。 个人思考。 思考 创业是最能教育人的。那些玻璃心、那些自尊自傲、那些清高不凡、那些自以为是，在「九死一生」的创业面前，啥也不是。\n人少的时候，一起往前冲。人多的时候，有的人蒙眼狂奔，有的人乱跑一气，有的人只是搭便车。\n不要怀着「拯救别人」的心态做事，到最后往往需要你来「拯救自己」。\n打工和合伙是两个概念。真正的合伙人，不需要你推动。\n「你的动机并不总像你想的那样无私」，「不要过高估计你的价值」，「不要指望别人使用和你一样的方式思考」，「不要指望别人也像你一样那么看重你」。\n不能拿过去的眼光看现在，也不能拿过去的眼光看未来。更重要的是 不能拿现在的眼光看未来。\n放弃成就完美的自己，放弃不可能征服的事，放弃「我能做好所有事」的执念。 把更多时间消耗在对客观事物的理解上。\n分辨我们用时间和钱换来的东西哪些是资产，那些是费用。加深护城河的是资产，随时间流逝的不利是费用。资产只会买贵，不会买错。费用不但可恶，还会加速浪费。\n靠文化凝聚起来的人是资产。能用钱购买来的劳动力和技能是费用。\n不想卖，就不要出价。人品，感受、文化不搭，第一天就不要合作。\n个人认知取决于出身、幼年教育和利益角色。因此要结合背景和相关利益角色去判断一个人的观点和看法。这种判断需要的往往不是睿智，而是坚持理性的勇气。\n以前写代码的时候，我总是很奇怪，为什么明明可以把代码写得像诗一样，偏偏有人就要写得像屎一样，而且还是一坨可以运行的屎。但只要屎可以产生价值，它就是黄金。当黄金没有了价值，它就是屎。\n小曾同学经常挂在嘴上一句话，这不公平！ 我总会很认真地告诉她： 这世界没有公平，只有平衡。 想想又觉得不妥：我告诉她自以为的这世界的真实，能轻易塑造她心中的真实世界吗？\n强行煽情（价值提升） 时间长留，而我们离去，一年又一年。\n过去不留，当下不杂，未来不念。 风景不再，来客稀疏，只有自己。 最爱的人不在对面，最好的朋友不在身边。\n2023 年过去了，我很怀念她。\n2024 年，愿我们都能以离开的方式，善待自己。\n全文完 ","date":"2024-02-03","description":"时间长留，而我们离去，一年又一年。","lastmod":"2024-02-03T09:29:19Z","slug":"endless-2023","tags":["gamenote"],"title":"2023 年过去了，我很怀念她","url":"https://blog.zengrong.net/post/endless-2023/"},{"categories":["impressions"],"content":"昨天读了万维钢关于项目人的文章，挺有感触。我结合自己管理项目、公司和人的经验，做了一些小思考，记录如下。\n一个组织只有两种事： 项目和运营 一个公司只有两种事： 管理和经营 项目需要管理，保持稳定的现金流需要经营。\n经营型公司和研发型公司，需要采用不同的管理方式。\n运营现金流产品和构建创新性产品，是完全不同的经营方式。\n个人成长、公司管理，都需要使用项目管理能力，有一部分经验是共通的。\n什么是项目 项目就是为创建某一独特产品、服务或成果而临时进行的一次性努力。项目必须有三个特点：临时、独特、渐进明细。\n一位滴滴司机每天工作很久，他做的不是项目。只是用劳动换钱。 一家奶茶店老板管销售管员工管进货，奶茶店开业和布局那几天是个项目，剩下的是运营。 一个游戏，上线的研发期是项目，上线的推广期是项目，上线后的持续活动和研发活动是运营。 1. 临时 「临时」就是有明确的开始和关闭时间。 需要立项，需要人和钱，需要开工时刻，需要交付日期。\n我要创业！我要成为全栈程序员！我要把 UE 研究透彻！ 这是口号，不是项目。\n公司今年转向做中度混变游戏！自建发行团队来自己发行产品！这是关键战略选择，这不是项目。\n给一个梦想制定边界，将想法变成蓝图，提出具体目标，确定交付时间，这才是「项目化」。\n2. 独特 「独特」就是指没有人做过，或者每次的变量都不一样。\n公司年会，是一个项目。 虽然每年一次，但地点不同，局面不同，人不同，组织方式与公司日常工作非常不同，无法例行公事，需要进行专门的目标设计，对人有专门的安排。\n前几天看过一篇挺火的文章 打工人，烦死年会了，其中有评论提到自己的公司从来都是请外部人员来表演，然后吃吃饭发发红包就好。这种年会就不是项目，而是运营。\n公司这么操作，可能的原因是避免年会这个项目，在年底这个时间点给公司带来的工作负荷与不确定性。或者单纯就是老板不想做。\n和许多打工人想的不一样，采用何种年会方式，只是管理者的思路区别，孰优孰劣，要看管理者的选择以及对公司的实际影响。\n3. 渐进明细 「渐进明细」，是因为之前没做过，细节问题随着项目的展开才知道。\n项目是有风险的，有些项目做起来之后才知道做不成。 成功是非稳态，失败是常态。\n二十世纪建筑史上的杰作，建成于 1973 年的悉尼歌剧院，和长城、泰姬陵并列，是唯一一个评选时建筑师还在世的世界文化遗产。\n但悉尼歌剧院的建造是一个巨大的失败。它原计划 5 年建成，实际上用了 14 年，最终花费比预算多了 1400%。它的设计师约恩·乌松在开建 7 年后离开澳大利亚，直到 2008 年去世再也没有亲眼见过自己的作品。\n在我们游戏行业里，经常有研发（美术+程序）的同学抱怨说，策划给的案子太粗糙，只有自己上手做了之后才发现问题，再回去找策划讨论，发现策划根本没想过。\n我说这很正常。策划案作为一个项目的计划，或者说「项目管理方案」，不可能在初期做到事无巨细。\n「项目管理」是一个很重要的学科，它考虑 计划、执行、利益相关方的关系、预算、风险、交付 等各种套路，而且有一些通用的、但不一定广为人知的原则。\n游戏策划案只是项目管理中的很小一环，但需要策划案的制订者通晓所有岗位的特点，或者至少知道每个项目参与人的能力特点。遗憾的是，基于时间、经验、策划人员的能力，这一点很难实现。\n你不能要求每一个策划都会画图，懂 UE，同时还能写代码。\n同样的，策划岗位也经常吐槽美术实现不了想要的效果，程序做不出来想要的功能。你看，菜鸡互啄开始了。\n实际上，在项目进行当中，不断完善方案，是一个正常且必要的流程。策划、程序、美术这些研发人员，更应该思考的是，如何在开始之前，通过充分沟通，互相了解，多方求证，形成一个「当下最优」的执行方案， 并持续合作让这个方案越来越完善，让项目越来越倾向于成功。\n对于管理者来说，要弄明白：\n哪些「详细」是必要的？ 哪些「详细」是为了追求效率和流量周期可以省略的？ 哪些「详细」是由于项目管理者或者执行者能力不足而想不到的？ 哪些「详细」是由于主观想偷懒而故意舍弃的？ 上面第 1/2 项是必须做到的，第 3 项是通过培训可以做到的，第 4 项是需要严格避免的。\n把成长看成一个项目 和打工人聊工作，我一定会问这样的问题：你这半年的成长计划是什么样的？两年呢？\n除了已经躺平不思考的人，大家都会提到学习，成长，成为更高职位的人，或者学会一个什么样的技术、工具。\n例如一个美术同学和我讲的是：在美术基础方面获得持续的成长。一个游戏前端程序员和我说的是：在游戏前端特效上有所精进。\n而当我追问下面这些问题，少有人能答得上来：需要多长时间学会这个技能？怎么检验学习的成果？产出的是什么内容？\n成长不是一个运营活动，而是一个项目。它需要有明确的时间表，需要确定的反馈，也需要定期的检验。 成长有阶段，成长必须实现螺旋上升。技能的成长是线性渐变，认知的成长是量级跳变。\n滴滴专车司机的算法训练项目 上面提到开滴滴不是项目，但我读过一个有趣的滴滴专车司机故事，主人公靠着项目思路，把滴滴系统训练成了自己的接单工具，用别人一半的单量完成收入目标。他是这么干的：\n蹲点特定的时间段； 只跑萧山机场到自己家方向的长途单，送到回程机场空跑，不接顺路短途； 对长途客户非常热情，维护好客户评分。 这三点做完，他就完成了对滴滴排单系统的训练，让系统知道他是一个住在市区的专车司机，服务质量优秀，会更多给他派相关路线的长途单。\n对于这个司机师傅来说，跑高架开车更轻松，长途收益更高，一单抵好几单短途，跑完单回家还能睡个好觉，把更多的时间分配给家庭。\n这就是一个有明确目标的项目，而且可以通过分析滴滴派单逻辑，不断调优这个流程。把这个派单算法训练项目，变成一个可持续日常运营的工作。\n每个项目都是打怪升级的小确幸 既然是项目，就必须有目标。\n下面是我常听到的游戏人制订的目标：\n我要成为主程！ 我要成为主美！ 我要成为制作人！ 当我问主程主美制作人是什么的时候，几乎没有人能答得出来。\n这不是目标，这是口号，这是欺骗自己的座右铭。\n要实现大目标，你需要小确幸。\n一个美术的升级目标 有个美术同学的目标很清晰：\n在自己的产品中，找到需要可以优化的特效，寻找市场上游戏中适用的，比自己项目更好的特效，将其移植到自己的产品中。 研究引擎源码，将默认配置不能实现的特效，通过修改源码实现。 学习编程，做一些一些小的游戏 DEMO。 重点是，上面三个小目标，是可以螺旋上升的。螺旋上升对我们能力的成长起到持续的支持作用。也同时提升我们的项目管理技能。\n项目经理，复盘不甩锅 在自我成长这个项目中，你是自己的项目经理，也是项目执行者，你可以借鉴项目管理的常用办法。\n项目经理常用的方法就是把大项目分拆成很多个小目标，多设置节点，每走到一个里程碑都确保所有人都走到，坚定地逼近最终目标。\n自我成长也相同，每个节点，就是成长目标的反馈。必须有这个反馈，你的大脑才能活跃起来，你的内啡肽被激活，你的血清素会积聚，你的学习行为价值得到了固化。\n即使你的学习计划失败了，也没啥好焦虑的。复盘并制订下一个计划就好。这里的复盘不必担责，不用甩锅，不可自欺欺人。 个人任务失败逃不开 时间不合理，任务太困难，临时起变化 等因素。你需要思考的是，为什么在制订任务的时候，没有想到这些问题。\n项目经理的自我修炼中有一条是「事不成，人成」。通过不断失败，你也可以不断复盘积累经验，让你制订的新任务越来越靠谱，越来越容易成功。你的经验和能力也就会越来丰富，你能制订出在时间和目标上更加可控的小任务，你会在自我成长的项目管理中，越来越如鱼得水，越来越容易成功。\n参考 万维钢精英日课 5：项目人和编制人 打工人，烦死年会了 全文完 ","date":"2024-01-09","description":"项目需要管理，保持稳定的现金流需要经营。","lastmod":"2024-01-09T09:18:19Z","slug":"more-project-more-grow-up","tags":["gamenote","management"],"title":"个人成长是无数项目的集合","url":"https://blog.zengrong.net/post/more-project-more-grow-up/"},{"categories":["impressions"],"content":"关于最新的《网络游戏管理办法草案（征求意见稿）》还是触乐这篇写得最好（第一篇），建议阅读。最后一篇是草案全文，也建议阅读（不要只看二手信息）。\n大多数的媒体报道都在渲染紧张气氛，行业内的媒体反而会更客观思考，非行业媒体就比较负面吧，怎么惊悚怎么来，一如今日游戏股，从业者情绪的无从安放，资本的身体无限诚实，在这个冬至给我们带来无限惊！喜！\n《网络游戏管理办法草案（征求意见稿）》与惊弓之鸟丨触乐 逐条解读最新《网络游戏管理办法》 网游新规征求意见稿出台，游戏公司的商业模式要变了吗？ 游戏圈惊弓一日 今天，腾讯跌掉一个京东 《网络游戏管理办法》(草案征求意见稿) 下面是我自己作为游戏行业从业者的非专业仅供参考的解读：\n第十二条：不能简单说版号有效期一年。这里留了个口子给出版社操作。以目前的版号业务来看，对出版社是利好。出版社会增加不少收益空间。（向省级主管部门书面说明的理由，应该是要通过出版社的）\n第十七条：除了 SLG，对其它类型的游戏，影响不大。\n第十八条：限制游戏运营策略了，新手运营得想新点子，老手运营应该已经积累了无数新点子。\n第二十一条：对正经做游戏的公司影响不大。目前利用公测内测挣钱的，一般是个人开发者和非常小的团队，本来就是不合规的。\n第二十四条：对休闲游戏没有影响。但需要考虑如何打击 X 宝 X 鱼上的破解服务和代充服务（它们不需要用数字人民币）。只打击官方，不打击黑产，会让黑产更加猖狂。\n第二十五，第二十七条：定义挺模糊的，如果出不了详细办法，裁量权又下放给地方，那对于游戏公司就是个灾难。\n第六十三条：真实的意思是（小游戏，我知道你们很赚钱！等着哈，我会收拾的）\n我的简评：\n整个草案，对休闲游戏是利好，对重度游戏有更多限制，但也并没有超出之前的规则太多。规则是明确了一些，但扔有大量细节有待明确。\n关于小游戏，需要平台去做一些博弈。平台的利益比开发者更大，肯定不愿意放弃（除非像百度这种不会做的以及之前一群不下功夫只想用流量想来圈钱的平台）。小游戏给平台带来的整体活跃的提升，增强左手倒右手流量的机会，哪一个成熟平台都不会坐视。\n游戏这东西有文化和传播属性，管理者自然希望所有的剧都是正剧，所有的游戏都是保卫萝卜和跳一跳。有可能因为他们只玩保卫萝卜和跳一跳。\n全文完 ","date":"2023-12-22","description":"管理者自然希望所有的剧都是正剧，所有的游戏都是保卫萝卜和跳一跳。有可能因为他们只玩保卫萝卜和跳一跳。","lastmod":"2023-12-22T06:48:38Z","slug":"gamenote2824","tags":["gamenote"],"title":"啥叫惊弓之鸟？","url":"https://blog.zengrong.net/post/gamenote2824/"},{"categories":["impressions"],"content":"这是 2024：游戏创业者能重回初心吗？ 一篇的后续思考。\n今天和一个策划朋友聊天，谈到酣畅处，TA 说了句许多真正游戏人的心里话：\n不想成为制作人的美术不是一个好程序。\n你必须是一个全能选手 我们不得不承认，游戏人想做出心中的那个游戏，必须得是一个全能选手：\n自己写代码，自己做美术，自己编文案，自己配数值。\n什么，要策划？你才是策划，你全组都是策划！\n能做成游戏的只有制作人，能把自己的点子 101.83% 还原的，也只有制作人。\n为什么当代游戏公司，还需要孜孜不倦招聘：\n系统策划 关卡策划 文案策划 数值策划 前端程序 UI 动作 特效 地编 技美 …… 因为：\n它们做的不是独立游戏。 它们要保证项目成功。 实践复杂的好东西 丹麦经济学家，牛津大学教授Bent Flyvbjerg 在他 2023 年 2 月出版的新书《怎样做成大事 How Big Things Get Done》 中说：\n所有足够复杂的好东西都是迭代出来的，而大项目的迭代必须发生在计划阶段。\n解释深度错觉让我们认为自己知道是怎么回事，其实自己并不知道是怎么回事。举个例子：\n你知道抽水马桶是怎么工作的吗？ 你能说清楚自行车的链条系统是怎么工作的吗？ 如果说不清楚上面两个每天都能接触到的物品/现象的工作原理，怎么保证一个投入了 几十万/几百万/上千万/过亿元 的游戏，能在完成的时候 100% 达到你设计时候的效果呢？\n这几年游戏行业里都在讲工业化，国内莉莉丝、米哈游、腾讯、网易，都在实践工业化，我们看看游戏行业的老大哥和先行者，电影行业的皮克斯是如何实现工业化的。\n一个皮克斯的动画片要经过六个阶段：\n形成创意。「一只法国老鼠喜欢做饭」「一个脾气暴躁的老人」「一个女孩头脑内部」 写 12 页摘要，把创意变成简单的故事，找皮克斯的任何人随意批评。 写 120 页剧本，每页 1 分钟。 花三到四个月把剧本拍成完整的草稿电影，画出所有分镜头（2秒/分镜），找人念对白，配置音效。 重拍，出第二版草稿电影。 迭代八次。 开始拍正片。 在开始拍正片之前，所有的影片细节，所有的台词都已经确定。所以皮克斯才敢于动用几百个工程师，找顶级明星配音，让著名作曲家配乐。\n这才是真正的制作人应该做的事：让最终结果可控。\n而我们这些自封的游戏制作人，都知道这个段子：我有一个点子，就差一个程序员了。\n很多自称的游戏人其实只是玩游戏的人 参与到游戏行业中，自认为自己是游戏人的人，大多数都只是「玩游戏的人」。\nTA 们面对各个平台的游戏如数家珍，分析游戏系统和代际差异头头是道，玩起游戏来毁天灭地，买起游戏来绝不肉痛，聊起天来感觉小岛秀夫 beta v0.1非 TA 莫属。\n但做起游戏来就是那么菜。\n偏安一隅，不愿意了解最新的技术发展，不熟悉团队合作方法，不屑于掌握基本的沟通技巧，不学习行业沉淀下来的最佳实践。会 SUMIF 函数就敢说精通 Excel，会配表就敢接手数值策划，会用 Python 解析 XLSX 就敢说掌握了编程，会 PS 画界面就觉得已经精通 UE……\n这能做好游戏？这连独立游戏都做不好。\n我应该去做独立游戏吗？ 沃顿商学院的赛斯在他的《别相信直觉》一书中认为不存在创业神话：\n不是越多失败记录的人越容易重新创业成功。 不是越年轻的创业者月容易成功。270万个初创企业家的平均年龄是 42 岁。至少在 60 岁以前，企业创始人年龄越大，越容易成功。 企业创始人在创业前的收入进入行业的前 0.1% 时，创业成功率最高。 换个角度说，容易创业成功的人，就是我们印象中的典型「中年精英」：在自己的行业中深耕多年，积累了很多经验和人脉，爬到山顶之后出来创业，靠着自己的过往积累把企业做起来。\n所以，如果希望失败很多次就能获得很大的成功，概率并不大。\n互联网行业有个 721 格局。老大 70%，老二 20%，其他所有抢占剩余 10%。\n看看国内的游戏市场，近六年来，腾讯、网易、三七互娱、吉比特、恺英网络五家公司的市场份额，从60%左右，增加到2022年的69%。\n我们常说，老大和老二打架，死的是老三。 腾讯和网易两强相争的格局已经形成，最难受的应该是腰部厂商。\n独游的比例？可能是这 10% 的 10% 的 10%……放心，至少，目前，在内容行业，这 10% 抢不走。\n在开始独立游戏之前，你是不是已经做好了所有的准备？比如说：\n你得是个真正的游戏人，而且经过真正的制作人训练（仔细看上面两段）； 准备好了足够支撑到你游戏上线（可能还包含基础的宣发）的经费； 找到了足够经验丰富且和你一样认可这件事的合作者。合作者不要超过 2 个，且不能有生活压力（例如没有女盆友蓝盆友甚至不养狗）； 得到了家人的全面支持； 为了疯狂加班最好戒烟否则会猝死的； 调配了一瓶药水，用于治疗很长一段时间（很可能比你想象的时间还要长2.5倍）内没有收入，没有反馈，充满煎熬和精神内耗的脆弱小心脏。 …… 更多扎心的就不说了。 还有挺重要的一点，我在 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 一文中提到过：\n独立游戏大多是较小的团队，但独立游戏团队在选择游戏类型上并不会刻意避开难度大的类型，也更愿意尝试创新类型。这就是独立游戏的特点： 我可以接受长周期，但我不能接受低标准。\n产品专家梁宁在读完《埃隆马斯克传》后写到：\n人做一件事一般会有三个目标，为了「做成」，为了「赚钱」，为了「让自己的体验最大化」。\n做游戏，一定要想清楚自己的目标是什么。\n有人说，挣钱要站着挣，要做对的事情，挣钱就会在做事成功后自动成为结果。\n它们骗你的。\n如果只是想挣钱，最好先做点挣钱的事情。 毕竟做游戏也是能挣钱的。\n如果只是想做成，那就先把做成的钱攒好了，并做好做不成也不后悔的准备。\n毕竟，想要成为 Terraria 这样的，12 年持续挣钱又开心的独立游戏，你不能只靠想。\n思考链接 12年不出续作，竟是玩家们的错！？ 梁宁：马斯克，一个拒绝内耗的行动力狂 游戏行业卷在2023：四大趋势，两强争霸 《别相信直觉》 万维钢精英日课5《怎样做成大事》3：创意的工业化 全文完 ","date":"2023-12-20","description":"毕竟做游戏也是能挣钱的。","lastmod":"2023-12-20T03:39:06Z","slug":"2024-should-i-go-indie","tags":["gamenote"],"title":"2024：我应该去做独立游戏吗？","url":"https://blog.zengrong.net/post/2024-should-i-go-indie/"},{"categories":["impressions"],"content":"这篇数据详实，分析客观，值得细读。\n今年最巅峰对决！《元梦》VS《蛋仔》独家数据公布！全网最详细！\n感觉目前社交 Party 这个领域，小投入已经没法打了。元梦和蛋仔，从代言人，玩法内容，投放（一个月投了一年的量），游戏内撒钱（充 6 块返 6 块），直播（大主播集中播），预热（确定代言人预热，代言人到位预热，上线预热），UGC 生态创作鼓励金， 做的是全方位的竞争。\n一个狙击，一个守成，后面还有 UGC 内容沉淀的竞争。蛋仔有丁三石亲自督战，坐拥五亿用户，元梦是目前腾讯主推的 party 合家欢游戏，你甚至能在游戏里看腾讯视频这才有了一点点元宇宙的影子嘛。\n蛋仔的 5 亿用户有多少水分？未成年人轻易就能被几个 Q 币挖走。元梦的 UGC 何时能达到一亿张地图？长线留存和长线付费怎么做？热度下降之后的付费如何保持？大 DAU 如何持续？元梦和蛋仔，注定还有很长的拉锯战要打。\n猛兽派对，人类一败涂地，鹅鸭杀，太空杀，这些优秀的产品，和蛋仔元梦已经不在一个量级，它们提供给行业的经验，在这个级别的竞争中，又有多少能被中小厂商采纳呢？\n全文完 ","date":"2023-12-18","description":"大 DAU 如何持续？","lastmod":"2023-12-18T03:33:07Z","slug":"gamenote2822","tags":["gamenote"],"title":"元梦和蛋仔，中小厂无法企及的狂欢","url":"https://blog.zengrong.net/post/gamenote2822/"},{"categories":["impressions"],"content":"俞敏洪知道怎么选 从小作文，到东方甄选粉丝逼宫，到高途佳品的泼天富贵，到东方甄选主播间怒怼粉丝疯狂掉粉……\n小孙下课，东方甄选直播间停播一天，董宇辉和俞敏洪在 12 月 16 日晚上回归，这几天的瓜吃得一愣一愣地，周末都不让人休息啊！\n董宇辉说今天下午沟通过薪酬，有非常大的提升。\n短期来看，这对董宇辉来说是好事，对东方甄选也是好事。\n从长期来看，无论董宇辉还是东方甄选，下一步都要走得非常谨慎。毕竟，不单之前董老师被高薪挖角，现在连京东、高途和罗永浩都虎视眈眈望着这个顶流搞事情呢！\n东方甄选的小编，一而再，再而三地置顶消息寻找存在感，说好听叫不专业，说难听叫越权。情商高的董老师说「胡回复」，这台阶还没铺起来就被小编直接踩碎了。\n就好像游戏上线大推，程序员在登录界面里面埋了个公告「这个界面是我做的，请点击下一步登录」。\n能写代码的人多了去了，能把游戏发上线还能赚钱的有几个？那是代码敲的好么？那是游戏牛逼！\n董宇辉重要，还是小编重要？没有小编，董宇辉也能写出小作文，但没有董宇辉，小编的 128 分小作文也没人看啊。\n俞敏洪当然知道怎么选，而且很快就做了选择。\n董小姐的选择却不太妥当 对于一个已经离职的年轻人，持续批评，不是一个世界 500 强的老大应该做出的决策。何况，还是在格力电器 2023 届大学生入职仪式上。\n哦，格力在 2023 年已经不是世界五百强了。\n为什么被裁后都想着去开滴滴和送外卖？ 为什么年轻人热衷于直播带货成为网红，而 35 岁大厂被裁人热衷于开滴滴和送外卖？\n因为滴滴和外卖能提供稳定的收入预期，这与大厂打工人在大厂工作时的预期是一致的。\n年轻一点追求一夜暴富，年长一点追求均值回归，大家都有光明的未来。\n2023 年小游戏数据爆炸了 让我们看看几个数据的对比：\n主机游戏 29.93 亿（增长22.93%） 网页游戏 47.50 亿（就算降 10%，也依然高于主机游戏） 休闲游戏内购 200.87 亿（增长 109.7%），广告 117.54 亿（下降 52.7%） 小游戏 200 亿（增长 300%） 我在 2024：游戏创业者能重回初心吗？ 一文中写到：\n但「玩游戏的人」只有那么多。\n如果只面向玩游戏的人做游戏，就是把自己游戏的目标受众限制在一个很小的范围内，就会错过大量不一样的平台和更新的流量。\n你看，从上面的规模就能看出来了吧。\n《元梦之星》真好玩，小游戏版本使用的云游戏技术，没有 WIFI 不要玩！一个月 50G 流量也撑不住！\n思考链接 东方甄选的小编，怕不是高途派来的？ 东方甄选不想变成董方甄选 《2023年中国游戏产业报告》 九成主播收入不如骑手？是时候褪去“流量造富”光环了 董小姐这次分手，不够体面 俞敏洪赢了，东方甄选输了 全文完 ","date":"2023-12-16","description":"能写代码的人多了去了，能把游戏发上线还能赚钱的有几个？","lastmod":"2023-12-16T03:11:15Z","slug":"gamenote2821","tags":["gamenote"],"title":"董宇辉和孟羽童，中间差一个元梦之星","url":"https://blog.zengrong.net/post/gamenote2821/"},{"categories":["impressions"],"content":"即将过去的 2023 年，身在行业中的游戏创业者，见证了太多 「活久见」 的大事。\n蛋仔派对收获了五亿注册用户。 字节游戏五光年太久，不争朝夕。 博德之门3大卖，黑神话悟空终于定档。 Party 品类爆炸，逆水寒手游霸榜，开箱子满天飞。 然而，上面的消息，和本文定义的「游戏创业者」关系不大。\n哦不，最后一个开箱子还是 可以有 关系的。\n规模和品质 游戏研发的工业化时代，规模和品质，已经深度绑定在一起了。拿逆水寒举例来说，号称不卖数值，量大管饱的开放式武侠 MMO，创业者做不来。就算不是MMO，几百人，数年的开发投入，创业者也做不来。\n游戏行业，在卷过了「消失的那三年」的红利之后，似乎已经进入了一个「大者恒大」的局面。\n《永劫无间》免费后人气持续冲高，12月 14 日网易港股市值达到 5306 亿港元，超过美团的 5192亿港元，成为中国市值第四大互联网公司。\n面对比你懂游戏还比你有钱的大厂，创业者怎么卷规模和品质？\n卷不过大厂，不做需要大投入的商业游戏，保持一个小团队做独立游戏是否可行？\n2016年，国产独立游戏大概只有那么十几款，2020年700多款，2021 年过千款，到了 2023 年，我们去 Steam 看看，每个月有大量的新奇游戏发布，销量却起不来。\n可不是每个热爱游戏的人都有 600 万。\n资本的目标 规模并不等同于品质。但在资本的加持下，游戏研发和宣发的规模，是越来越大了。\n我们知道，如果有选择，资本总是喜欢赚最容易赚的钱。我们在嘴上诉苦监管越来越严，但很多人心里清楚，国内的市场环境和互联网政策弹性为他们的快速崛起提供了多少支撑。\n如果能一直野蛮生长，谁来付出后果和代价呢？\n老老实实在行业中做事的人，最终承担了代价。\n先上岸的人可以跑步进入下一个新的，规则不完善的领域，但热爱这个行业，愿意付出一生的人必须留下来面对全面的监管。\n软著、版号、游戏备案、越来越多的政策保障了游戏玩家的利益，保证了行业合规性。同时，这些流程也增加了行业成本，给追求灵活快速的小创业者带来了更大的工作量。\n因为各种原因不得不出海的小创业者，迎头赶上 Google Play 要求 DNUS，以及面对中国网络 IP 更加严厉和无厘头的封号。这是之前的出海者挖的坑。\n为什么对游戏理解不足的资本，要大踏步进入这个领域呢？\n因为游戏一度被认为是自有流量变现最高效的阵地。\n可这话只说对了一半，尤其是在字节这个大资本放弃游戏之后。让我们简单聊几点：\n互联网资本需要的明确的投资回报率。而真正的游戏恰好是成功随机性极大，成功概率极低的产品。 不存在「全品类研发」以增强成功概率的情况。游戏产品稳定的成功率，是基于对一个品类或者一个赛道全面透彻的理解下实现的。 我们觉得爆品的成功概率是 5%，就立项 100 个产品认为有 5 个能爆是不对的。爆品概率永远都是基于单个产品品类。 跨行业流动来的很多「高级人才」，很少为了游戏本身才进入这个行业。即使技术再好，也只是大干快上地提升了游戏工程管理能力。做得越快，可能是死的越快。 游戏基因是一种传承，体现在创业者的个人魅力和共同价值观，需要创业者积累的一群人来凝练和放大这些精神。 铺大摊子可能导致统一作战性差，可能导致游戏本身的信息传递差。 只有游戏基因，没有商业基因，不懂人性管理，也难以做好游戏。热爱游戏是一回事，管理人是另一回事，赚钱是最重要的事。 在游戏上，成功的资本会更成功。\n员工的需求 去年有个公司要同时开十个千万成本的游戏项目（这不门外汉么），结果项目一个都没做出来，挖了不少人全裁掉，整个市场薪资结构乱掉了。\n创业者会认为，我们能在一起，把心中的那个想要的游戏做出来，是一件多么开心的事。 有薪资，有环境，有同事，有点子，爱游戏，全力以赴干就行了！\n但员工不是这么想问题的。\n大家都是普通人。 薪资一直涨，公司有方向，付出精力做的产品能成功，身边的饭搭子不被裁，上班还能摸摸鱼， 这才是普通人的日常。至于公司怎么样，游戏能不能做出来，那不是太重要的事儿，至少没有生活质量重要。\n创业者和员工，是站在对立面的。而且，团队越大，对立越强。管理越合规，就会有越多的人找到规则的漏洞。\n晚餐后晃悠到 9点半再去公司打卡，这是大厂打工人都懂的规则。\n游戏项目需要多少人，应该考虑创作者/创业者的管理半径，不可轻信「大力出奇迹」。\n只有足够「可控」，才会更少受到市场的影响， 才能更加聚焦产品本身。\n游戏人的初心 经常有同学和我说：我要做个牛逼的游戏！我现在做的这些都是个啥？我啥时候才能做出心目中的那个游戏？我要回归初心！\n游戏人的初心是什么。我们来捋一捋。\n游戏创业者，一定得是个「玩游戏的人」。想做出自己心目中的游戏，显然面对的也是玩游戏的人。\n但「玩游戏的人」只有那么多。\n如果只面向玩游戏的人做游戏，就是把自己游戏的目标受众限制在一个很小的范围内，就会错过大量不一样的平台和更新的流量。\n毕竟，你做游戏，是希望被更多人玩到，不是只希望自己开心吧？\n对于游戏这种文化产品来说，商业成功与受众规模息息相关。\n在流量为王的手游时代，快速上线，快速盈利，一直是市场的主流做法。大家更愿意选择一个明确的数值模型，通过调整得到更确定，更长久，成本更低的成功。\n想要回归初心的成功，需要两个降低：\n降低收益预期。 降低成本支出。 也就是说，要接受产品不成功的可能性，要抛弃自己的游戏宣发后一夜暴富的戏剧性，要明确认识到自己的能力边界和行业的残酷性。\n做游戏，得分清楚用户阶层，要挣谁的钱？得知道自己面对什么用户，针对性采用策略。\n宣传方式，买量方式，游戏内运营怎么做，私域怎么做，长留怎么做，首日怎么做，和面向的用户都息息相关。先想清楚用户，再关注类别，最后才是变现，运营，获量。\n当然，面向的用户和产品类别，大多息息相关。平台，渠道和获量品质也紧密相关。\n就好像你不应该去 TapTap 买广告用户，也不能期待在俄罗斯市场的超休闲品类里批量抓到百万大 R。用户还是那群用户，为什么他们在不同的平台表现出的行为不同呢？\n人是会改变的。人在社会中的行为依赖环境，人在平台中的行为依赖平台调性。\n毕竟微信小游戏都做中重度了。普通自媒体都在写小游戏狠赚钱了。\n把上面的问题都想清楚了，游戏人的初心还在吗？\n第68届柏林电影节最佳处女作奖《大象席地而坐》的导演胡波说：\n公司鼓励我发朋友圈免费招美术和其他组员，导致该项目在电影学院美术系毕业生中成为笑柄……绝大部份主创几乎以零片酬来参与这部电影的制作，我靠去年夏天得到的奖金撑过下半年，但因筹备期无法进行其他工作，之后也没有任何收入。但整部电影的流程不是独立电影制作体系，从融资到宣传均属于商业行为……\n游戏人，你总得先活下来，才有空去想初心。\n参考链接 又苦又累 为什么游戏主播做独立游戏会这么困难？ 我看到了中国游戏的另一种结局 复盘小红书：拥有最高消费能力的用户，为什么却“不挣钱”？ 中国游戏业，等待一个黑神话 字节游戏这五年：300亿买教训 GQ报道 | 胡波：一个自杀者的传说 全文完 ","date":"2023-12-14","description":"游戏人，你总得先活下来，才有空去想初心。","lastmod":"2023-12-14T00:33:34Z","slug":"original-aspiration-of-gamer","tags":["gamenote"],"title":"2024：游戏创业者能重回初心吗？","url":"https://blog.zengrong.net/post/original-aspiration-of-gamer/"},{"categories":["impressions"],"content":"今天早上看游研社的 这篇文章，在「胡扯游戏」群里发了一点感想，晚上有点时间把白天的思考汇总，零零碎碎写在这里。\n文化是分层面的，能被大众广为接受的，是整个人类社会文化情感的公约数。把你没有的带给你，并得到了你的广泛接受，才是输出。\n变形金刚是输出（输入？），二次元文化也应该算是输出（毕竟原神都破圈儿了）。西游记传播了这么多年，但懂的都懂，还没有火到输出的程度。\n短剧这种爆火，是形式上的创新，说是形式输出也可以。本质是基于人类的文化情感公约数，用国内验证了的形式在海外做一遍。需要调整文化基因，不然就很难被大众广泛认可。但所有的套路都是通用的，地球村儿嘛╮(╯▽╰)╭\n2018 年开始，超休闲游戏在全球爆火，但从 2019 年开始，就不断有人鼓吹 超休闲已死（2020-06-27：超休闲/小游戏是否还有机会），直到 2023 年，VOODOO 的发行主管亲自说出这样的论调（2023-07-03： 混合变现从入门到放弃（上）——混变产品的误区），但我认为，超休闲这种游戏形态是不会死的，就好像短剧永远不会死一样。\n我们讲超休闲游戏已死，指的是以下定义的超休闲游戏：\n广告变现为主要的盈利模式； 以垄断包场冲榜投放为主要的推广方式； 以批量寻找超低CPI（T1国家$0.1，T3国家$0.03以下）价格标的的研发模式。 这当然会死，因为现在的广告平台已经不会再给你更便宜的量。如果没有签订框架协议，没有投放保底，没有广告算法池延转移，就不会有低CPI。\n毕竟，连字节这么有钱的主儿都不看好游戏了。\n然而短剧出现了，和超休闲游戏一样，短剧起手就是大规模投放。啊，也不一样，至少短剧没办法冲榜实现榜单带来的 1:3 新增。看自媒体宣传似乎短剧人均一周破亿，可毛利 10% 也就不错了。这种模式为什么能跑通？和超休闲游戏有什么区别？\n最大的区别，就是付费模式从广告变现（羊毛出在猪身上）变成了用户直接付费（羊毛还是出在 羊了个羊 身上）。收益更高，也更少受到广告市场 eCPM 变化的影响。玩家/用户：我开心，我充值还不行么？\n回头看看2022 年和 2021 年的风口，无论是 NFT 还是元宇宙，都不是面向大众的（或者无法在短时间内面向大众），它们充其量是小部分从业者的狂欢（击鼓传花，业内自己割自己）。但短剧、超休闲游戏，近期火热的社交游戏，还有字节不愿意放弃的 UGC，它们都面向大众的。大众接收它们，没有高昂的理解成本，只是缺乏一个快速触达的途径。\n买量平台解决了快速触达的问题。看着电梯里随时可见的 XX到家 服务，我和小曾同学说：哪个产品在疯狂投放广告，很可能说明这个产品在疯狂赚钱。\n年初马化腾说他不相信买量，到年底连张一鸣也不信了。\n短剧，也可以把它视作为内容持续创新的超休闲游戏。它们简单易懂，不需要学习也不用制作新手引导，不必考虑游戏人所熟知的游戏四要素（机制/故事/美感/技术）。这样的产品，核心是「爽感」。层出不穷的创意和持续的研发，就是内容的填充。不用考虑支持长线运营的内容研发，再做一个新的产品，很可能 一定效率更高。\n短剧，和超休闲游戏类似。更具戏剧冲突的形式，更容易被普通用户接受。到这里，我觉得可以祭出 2016 年就放在我笔记本里的这张图：\n注意，这张图是纯粹中性的，没有任何贬低的含义，也没有预设谁是「高阶」。我在练习演讲技巧的时候存下来的这张图，用来时刻提醒自己，让自己能把话讲得更清楚明白。\n正如游研社 这篇文章 所说：\n最土的，往往就是最为“世界”的。\n除了短剧和超休闲，发生在近几天的这些事情也特别值得铭记（我搞不清楚为啥这两天发生这么多大事）：\n99岁的芒格老爷子仙逝，他说选大道，因为大道人少。 雷军在武大 130 周年校庆捐赠中国高校最大单笔个人捐款 13 亿人民币现金，他说不要攀比。 滴滴崩了一夜，不知道要开除几个人。 拼多多市值 $1855 亿，只差阿里 $80 亿，马云说谁都牛 X 过。 龙芯 3A6000 发布（牛逼！）。 华为车 BU 分拆。 蔚来与长安、吉利合作换电模式。 全文完 ","date":"2023-11-29","description":"最土的，往往就是最为“世界”的。","lastmod":"2023-11-29T00:51:20Z","slug":"short-form-drama","tags":["gamenote"],"title":"短剧出海并不是一种文化输出","url":"https://blog.zengrong.net/post/short-form-drama/"},{"categories":["impressions"],"content":"昨天被群消息刷屏，今天又被媒体刷屏。到了下午，连圈外的朋友都知道字节不做游戏了。\n字节买沐瞳花了 40 亿刀，现在 20 亿刀和腾讯谈收购，可不就是五折么。之前收购时腾讯做了搅局者，现在要不要接盘呢？\n2020 卷口罩，2021 卷NFT，2022 卷元宇宙，2023 卷大模型。接着 Ohayoo 调整，有爱调整，PICO 调整，光年调整。\n“现在很多事情第一年起来，第二年风口，到第三年没了。”\n字节游戏做了六年。\n朝夕光年的游戏人开始回档重练。\n触乐问了十几个行业人，如果离开游戏行业可能去做什么？ 我翻了翻答案，果然宇宙的尽头是考公。\n11月26日，2024年度国考公共科目笔试正式开考。今年招收 3.96 万人，共有303.3万人通过用人单位的资格审查，77 人竞争一个岗位。\n如果没有没有硕士和博士学历，也不想去西藏日喀则卷 2179:1 的公务员岗位。那么还可以关注一下短剧。\n横店变竖店，群演变导演。 王晶开拍短剧《亿万傻王子》，万达电影也筹备了两部小程序短剧。\n短剧的风口，很像 18 年的小游戏风口。上来就是一堆抢速度的产品。相对品质高一些的就能赚到钱。但短剧一上来就投流，18 年的小游戏则更关注自然量。\n从自然裂变完全转变到到投流，小游戏走了四、五年。现在不投流，已经很难有大增长了，即使在小游戏上也是如此。《羊了个羊》这样的主要依靠裂变的爆品，很难再出现。\n短剧这波，看起来也是类似的剧本走向。开始卷时间，后面卷内容。\n早期做内容会比做流量吃亏，扛得住时间磨砺（没死），积累下来的经验才能在后期慢慢回收。\n如果死了呢？那就回档重练。\n毕竟——\n没有人愿意慢慢变富。\n参考链接 平均约77人竞争一个岗位！国考报名人数首破300万 今日开考！报名人数首破300万 假如离开游戏行业，你的下一份工作会是什么？ 横店短剧江湖：擦边、暴富和群演飞升 互联网行业最大的幻觉：有流量就能做好游戏 败局 | 字节只争朝夕，游戏再无光年 收缩业务？字节跳动再曝大消息！ 字节游戏大撤退，波及700人，保留2个在研项目 | 新知独家 全文完 ","date":"2023-11-27","description":"没有人愿意慢慢变富。","lastmod":"2023-11-27T06:15:49Z","slug":"the-next-game-job","tags":["gamenote"],"title":"离开游戏行业，下一份工作是什么？","url":"https://blog.zengrong.net/post/the-next-game-job/"},{"categories":["impressions"],"content":"今天中午骑了一辆带有手动锁的美团单车去健身房，在关锁的时候，发现锁会自动弹开导致无法关闭，必须在美团 APP 上点击「还车」成功之后，才能手动关锁。\n关于这个显然会影响终端用户体验的变化，产品经理和运营是怎么思考的？\n我们知道，美团单车（现在基本上看不到老的摩拜单车了）有两种车型，老的车型带有手动锁（如下图），新的车型则改为了无锁（APP 锁车，如上图）。\n为什么要改为无锁？我觉得从运营方的角度，有这样几个好处：\n提升APP 活跃。 使用手动锁单车的流程是： APP开锁 -\u0026gt; 骑行 -\u0026gt; 关锁 。\n若使用的是无锁单车，流程变成了：APP开锁 -\u0026gt; 骑行 -\u0026gt; APP关锁 。\n你看，在一次骑行的过程中，是不是增加了一次打开 APP 的机会？\n避免在非停放区域关锁。 若停放区域位于运营区域外，无锁车辆在关锁时可以禁止关锁。这增加了用户的工作量（必须把车移动到允许停止的区域），减少了单车运营方的工作量。\n使用手机获取更加精准的位置信息。 使用 APP关锁，基于手机网络和 GPS，获取更加精准的位置信息，与车辆的位置信息进行比对，进行车辆位置和用户行为追踪。\n如果只是对于新型的无锁车进行 APP 还车操作，还可以理解。为什么今天碰到的有锁车，强行要求 APP 还车，不用 APP 还车就无法关锁了呢？\n我猜想，美团的产品/运营在这个功能点上，或许有如下思考：\n统一流程。 这是许多产品人和程序员都想做的事情（我自己也经常这么想）。如果新旧流程统一，在资源配置，数据分析上，能节省时间和成本。这个流程不一定对于用户有利，但一定对于设计者和运营者有利。\n主管单位对运营区域提出了更高要求。 主管部门下了死命令？必须在停靠点停车？不准乱停乱放？\n帮助用户减少忘了锁车的损失。 我强行想出这个原因，但可能性不大。确实有忘了锁车的情况。但在美团单车如此大的保有量下，忘记锁车的比例应该不会超过 5%。\n无锁单车，可以通过远程锁车。有锁的单车，就算远程还车，依然是无法进行手动锁车的。\n不得不说下我的态度：禁止关锁自动还车的体验，非常，非常，非常糟糕。\n如果骑行途中手机没电了，这个车怎么还呢？下次一定要试一试。😄\n全文完 ","date":"2023-11-21","description":"如果骑行途中手机没电了，这个车怎么还呢？","lastmod":"2023-11-21T06:01:26Z","slug":"meituan-bike-lock","tags":["product"],"title":"产品思考：美团单车为啥禁止关锁自动还车","url":"https://blog.zengrong.net/post/meituan-bike-lock/"},{"categories":["impressions"],"content":"本文的讨论局限于 15 人以内的纯游戏研发团队。\n日常游戏研发中，会出现研发结果与原始设计不一致的情况。尤其是对于休闲游戏「小步快跑」的情况，1-2 天出一个小的功能版本，紧赶慢赶做出来发现实现的效果居然不一样，返工就浪费时间了。\n从这个意义上说：不返工，一次实现设计，就是效率提升。\n撇开人员能力胜任不谈 ，本文讨论在沟通上如何提升效率。\n遥远的偏差 在一个标准的功能开发循环中，流程如下图所示：\n这个流程可能导致如下问题：\n美术、程序对文档产生不同的解读。 测试岗距离文档遥远，信息不足。 策划在产品最终输出后才看到成品。 验收距离文档生成时间久，文档的解读在四个不同岗位发生偏差的可能性大。 策划总是希望写出完美的文档。实际工作中，无论时间的要求，还是精力和能力的限制，都让文档不可能做到事无巨细。更何况，每个岗位都可能根据自己的岗位特点和经验先入为主，造成文档解读不力或者过度解读的现象。\n当策划最终看到成品（或者半成品）的那一刻，再回炉重造已经晚了。消耗的时间可能只有一两天（或者一两周），但面对不可变化的版本发布时间，团队只能继续加班加点，试图通过增加工作时间来弥补由于沟通不利犯下的错误。\n我们需要增加内审流程。\n两次内审 Boehm在 1988 年就提出了螺旋式软件开发模型，这个模型坐标系的第一象限，是 识别和解决风险。\n上图来自 Wikipedia: 软件开发的螺旋模型\n下面看看加入内审后的功能开发循环。\n我们在美术动工之前，以及美术完工之后，加入两次内审，来降低研发工作中的风险性。\n第一次内审的目的是同步意图，需要策划、美术、程序、测试四个岗位的全部人员参加。\n对于美术，需要确立功能的质量标准和工期，避免在艺术上过度追求完美。 对于程序，需要确定研发风险，判断功能难度，避免过度开发不必要的功能，也需要在这个阶段确定后续功能的延展性，让程序在系统研发上留好扩展位。 对于测试，需要了解整个设计和岗位合作的全貌，以便在功能完成后，进行针对性且不过度的测试。 第一次内审的这个过程，一定要 当面拉会。\n根据我的经验，所有的研发团队都讨厌开会，但这个会是 一定要开的会。如果不开这个当面会议，各岗位就会按照自己对文档的理解直接开始工作，最终必然出现文章开头讲到的「遥远的偏差」。\n第二次内审的目的是确定 美术的技术标准，需要该功能的策划、美术、程序的主要岗位参加，再由主要岗位传递给所有参与功能研发的岗位。如果传达错误或者传递不到位，就说明第二次内审是失败的。\n同步美术的技术标准，实际要做的是下面几点：\n确保美术按照策划需求完成了程序需要的所有资源； 确保这些资源都能按程序要求的格式和标准提供； 确保资源和技术能在文件大小，表现性能上达到最优； 重新梳理美术和程序的分工职责，保证工作效率最高。 程序岗位是最终产品搭建的执行者，但程序搭建使用的材料是其他岗位提供的。由于每个岗位对实现标准，最终效果都有自己的理解，本次内审就是要在程序动手干活之前，把这些标准和规则同步到一个最佳的状态。注意，不要最完美的状态，而要最合适的状态。\n这次内审需要主要岗位参加，内审中要充分发挥主要职责人员的主观能动性，充分听取各方面的意见，策划也需要坚持自己的标准，并能适时根据开发周期和市场需求做出平衡调整。这对于策划的能力与经验的全面性要求很高，更考验的是产品策划人员的沟通能力。\n三阶意向性 内审，本质上是一次多方沟通。多方沟通，尤其是不同岗位的多方沟通，涉及到唇枪舌战，涉及到意向性理解问题。\n邓巴教授，也就是发现邓巴数150的那位人类学家，曾经提出过一个「意向性理论」，用来判断你猜测他人意图的能力。\n一阶意向性：你能读懂自己的意图。比如，我知道自己喜欢吃苹果。 二阶意向性：你能读懂他人的意图。比如，我知道你也喜欢吃苹果。 三阶意向性：你能感受到他人对你的感知。比如，我知道你知道我喜欢吃苹果。 四阶意向性：你能感受到他人感受到的对你的感知。比如，我知道你知道我知道你喜欢吃苹果。 我们需要一种沟通方法，来解决研发团队沟通中的 三阶意向性 问题，也就是解决 「我以为你以为的就是我以为的」 问题。\nX-Y 问题 多方沟通可能出现信息不对称，把手段当目的，舍本逐末等问题。这些其实都是 X-Y 问题 的变体。\nX-Y Problem\nvia CoolShell\n有人想解决问题X 他觉得Y可能是解决X问题的方法 但是他不知道Y应该怎么做 于是他去问别人Y应该怎么做？ 简而言之，没有去问怎么解决问题X，而是去问解决方案Y应该怎么去实现和操作。于是乎：\n热心的人们帮助并告诉这个人Y应该怎么搞，但是大家都觉得Y这个方案有点怪异。 在经过大量地讨论和浪费了大量的时间后，热心的人终于明白了原始的问题X是怎么一回事。 于是大家都发现，Y根本就不是用来解决X的合适的方案。 X-Y Problem最大的严重的问题就是：在一个根本错误的方向上浪费他人大量的时间和精力！\n有个介绍 X-Y Problem 的专门网站，可以去看一看。\n三步沟通法 沟通，尤其是多方沟通，就是保持大家的想法一致。\n抛开通用的日常沟通技巧不谈，三步沟通法 就是要解决对方案理解不统一的现象，同时避免 X-Y Problem。\n流程上也就是简单三步：\n产品策划岗在准备充分，文档资料完整的前提下，详细阐述自己的设计思路和期待的结果。 程序岗和美术岗完整复述对于策划文档的理解，并分解成自己岗位的工作，进行详细阐述。 产品策划岗位再次根据第二步的结果进行补充。 说是三步沟通，实际上不止三步。因为第二步可能出现理解偏差，第三步又可能出现新的信息。第二步和第三步是一个无限循环，在持续沟通中，不断出现在文档中没有出现的新信息，产生新的需求，又由于资源和时间的限制不断调整，直到参加会议的多方都能达成一致为止。\n研发团队是一个组合 我知道，在创意研发团队中，保证大家的想法一致非常难。优秀的研发团队是一个组合而不是一个圈子。组合就允许每个人能够保留不同的思想而不强求唯一。\n研发团队应该允许个体保留个性、观点和做事风格，而且也只有这样，每个人才能对团队有独特的贡献。每个人有自己的特点，但是所有人又有一个共同点。\n正所谓 「君子和而不同，小人同而不和。君子周而不比，小人比而不周」。\n我们很难要求大家想法一致，但为了保持最终的产品质量，我们至少要保证团队能完整理解，并严格执行内审后的方案。\n重复沟通，认真内审，这就是小微研发团队提升效率的方法。\n全文完 ","date":"2023-09-24","description":"我们很难要求大家想法一致，但为了保持最终的产品质量，我们至少要保证团队能完整理解，并严格执行内审后的方案。","lastmod":"2023-09-24T09:57:40Z","slug":"communication-cycling","tags":["management"],"title":"重复沟通与内审：提升小微研发团队效率的方法","url":"https://blog.zengrong.net/post/communication-cycling/"},{"categories":["impressions"],"content":"2023年 ChinaJoy 可算是回归了，没有了疫情的羁绊，游戏行业的热情被展会和面基点燃。\n每个人都有属于自己的一片森林，也许我们从来不曾走过，但它一直在那里，总会在那里。迷失的人迷失了，相逢的人会再相逢。\n——《挪威的森林》\n版号看似解禁，但海外 eCPM 持续拉垮，买量成本节节攀升，游戏人的森林在何处？\n我记录一下 ChinaJoy 期间的信息和思考，应该无价值，纯属碎碎念。\n挖掘新的增长点 在 27 日下午 Enjoy 出海圆桌上，有这样一个问题： 谈谈未来，游戏行业的增长点还能如何挖掘？\n我觉得对于 SAGI 这样的自研自发小厂来说，可以有这三个方向：\n立项没有涉足过的品类，这是品类增长点。目标是触达不同种类的玩家。 上架没有接触过的渠道，这是用户增长点。目标是获取更多的玩家增量。 在团队熟悉的品类和渠道上做创意深挖与精细运营。目标是吃透品类玩家，精通渠道特性，在看似老旧的平台中获取全新的增长。 受限于资源，上面三个方向很难同时进行。在我看来，若有成功品类，可以选 3；若有稳定现金流，可以选 2；若有大把存款，可以选 1。\n你钱多，你精力无限，你当然可以全都要咯。\n我选 3。\n红海和蓝海的本质区别 不存在什么真正的蓝海。游戏行业卷到现在，所有的方向都是血海 （说不定还有深仇😝）。\n产品只要上了顶级渠道（AppStore/GooglePlay），数据就是透明的。如果只是上了内部渠道（例如国内的小游戏渠道、硬核渠道），也有足够多的途径拿到八九不离十的数据。当然，国内数据来源有点非常规，你得有能力区分真假。\n蓝海，在没有数据支撑的前提下，培育市场和用户，可能要花一些时间、精力和经费。没有经验的支撑，我们怎能知道这个领域是有大增长的呢？会不会已经被别人趟过然后放弃了？\n红海，是被验证过的领域。无数优秀的产品在其中竞争，有大量的数据和模型可以参考。不是不能做，而是要想清楚怎么做。在游戏行业中，或许只需要比别人好一点点，就能找到机会。怎么比别人好一点点呢？需要创新和经验，需要花一些时间、精力和经费。\n红海还是蓝海，本质上没有区别。立项的选择，依赖的仍然是 经验、精力、时间、经费。\n休闲游戏要保证白嫖用户的体验 许多混合变现游戏 IAA 占比都很低，在某些渠道上甚至连 10%都不到，但仍然需要考虑免费玩家的价值。我们看 Habby 的产品，免费玩家的体验是能够得到保证的。\n在重度付费游戏中，策划可以忽略不付费的用户（逼氪），只为付费用户服务（做付费深度），这是一种主动筛选： 既然不付费，就不要占用资源。\n在休闲游戏中，策略需要调整。有两个原因：\n休闲游戏的 LTV 大幅低于重度游戏。 休闲游戏面向的就是大部分普通人（很可能不是游戏玩家）。 休闲游戏需要保证长线收入，要拉长游戏的生命周期。如果降低游戏可玩性，面对这些不付费的普通用户，游戏的生命周期和口碑都会被摊薄。\n这些免费玩家，不会给游戏带来 更多的收益，但能产生 一定的收益。只要给免费玩家提供「平等的」服务，让免费玩家长期留在游戏中，就可能产生广告甚至是付费价值，并带来一定的自然量。\n这些价值不可预估，无法代入 ROI 公式进行计算，但这是「价值」，可以将这部分价值直接计入「利润」。\n我在 混合变现从入门到放弃（中）——混变产品的运营和商业化设计 中提到过，根据用户分群，在不同群体中的运营成本也是不同的。对于这群白嫖用户，消耗的成本是可控的。\n关于AIGC 经过了几个月的讨论，AIGC 在游戏行业中的应用讨论终于接上地气了，再没有自媒体帮别人家公司发布裁员计划，企业内的 AI 应用部门纷纷发声，不认为 AI 近期能替代人类员工。\n只有人类才能驾驭魔法。AI 的探索，无论是文案还是美术，都不可能由一个普通基层员工完成。\n我对许多应用 AI 的企业一把手提出过这个问题：\n在您的企业中，AI 能够带来多大的工作效能提升 / 代替多少之前的工作量？\n答案中最高的是 50%，最低的是 10%。\n而且，所有企业的 AI 工作流都是老板亲自抓，或者是合伙人在推进。\n只有行业专家，懂产品，懂流程，懂工程化痛点，有推动能力，有改变意愿，说话算数的人，才可能推动工作流中的 AI 变革。\n也就是说，关于 AIGC，不是应该裁人，而是应该换人。 对于企业来说，最先考虑的不是应用，而是培训。\n把 AI 加入到企业原有的工作流中，且不影响既有效能，才算是成功应用了 AI。至于 50%~2000% 的效能提升，那要先成功应用了再说。\nAI 不能替换最差的人，AI 始终消耗最好的人。\n那时我们还年轻。 穿过残垣断壁苍松古柏， 我们来到山崖上。 沐浴着夕阳，心静如水，我们向云雾飘荡的远方眺望。\n——《青灯》\n历届 CJ 碎碎念 2020 年开始，每次从 CJ 回来我都会写点东西做记录，下面是列表：\n2021 CJ五日-更严监管之下，休闲游戏小CP的破局思考 2020 CJ五日-Unity能做小游戏？ 2020 CJ五日-腾讯爸爸抱紧发行爸爸，CP爸爸在数钱 2020 CJ五日-殊途同归：Habby、凉屋和青瓷的游戏之道 查看所有 ChinaJoy 相关文章：\nhttps://blog.zengrong.net/tag/chinajoy/\n全文完 ","date":"2023-08-03","description":"我在 2023 China Joy 的见闻，所思，所想","lastmod":"2023-08-03T23:59:22Z","slug":"2023cj","tags":["gamenote","chinajoy"],"title":"CJ五日-2023 ChinaJoy 回归新增长","url":"https://blog.zengrong.net/post/2023cj/"},{"categories":["impressions"],"content":"6 月 16 日，我在广州参加了第二届中国游戏出海峰会。在上午完成了 《混合变现从入门到放弃》——中度休闲+混合变现游戏调优经验谈 的分享，并负责了下午场的主持工作。\n俗话说得好：没有 DEADLINE 就没有输出。 借助这次峰会上的分享， 我把这半年积累的经验与去年形成的 休闲游戏立项方法论 进行了一次整合更新，并强迫自己做一次相对完整的输出。\n完整分享一共有五个部分，本文包含最后部分 PART05。\n中度休闲+混合变现游戏调优经验谈 PART01 为什么要做混合变现产品 PART02 混合变现产品常见误区 PART03 IAA和混合变现产品的运营 PART04 中度休闲混变产品的商业化设计 PART05 我的产品立项思考 PART 05 我的产品立项思考 在 休闲游戏立项方法论 一文中，我比较详细地阐述了这几年创业的立项失败经历。本篇谈谈我在 2023 年的立项思考。\n如果没有看过 「休闲游戏立项方法论」 这篇文章，我墙裂建议去读一读，不然可能看不懂下面在忽悠啥。\n也建议读完下面几篇，方便理解我对休闲游戏的定义：\n我理解的休闲游戏（上）——平台如何定义休闲游戏 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 我理解的休闲游戏（下）——超休闲和小游戏交给 SAGI 的知识 我在「休闲游戏立项方法论」中提出了针对休闲游戏的「换核方法论」：\n「换核」与「换皮」两个方法论本质没有区别，重度游戏换的是表象，休闲游戏换的是核心玩法。\n我们总在说 立项定生死，说的是什么？定的是谁的生死？\n我们说「立项」， 本质是在寻找一个大概率收回研发成本，小概率成为爆款的研发方向。\n但这两个目标是相悖的，想收回研发成本，追求的是 确定性，只能在别人成功的基础之上小修小补，或者干脆照抄。想成为爆款，追求的是 可能性，敢于进行更大的创新，就必须面对大概率失败的结果。\n在立项之时，就得想清楚目标是啥。确定性与可能性不可兼得，作为成年人你不能全都要。\n天演论：「物竞天择，适者生存」 立项这事儿，可以和进化论联系起来说一说。\n我们知道，进化论的核心思想是基因随机突变，环境自然选择。一个生物的下一代是什么样子，和「努力」没有关系，基因变异不存在主观能动性。\n「基因漂变」是比「自然选择」更普遍的现象：生物在各个方向上发生各种演化，并不是 为了 适应自然选择——它们只是 可以 演变而已。\n举个例子，在基因的限制下，我们不能「努力」长高（锯腿那个不算哈），但现代年轻人的确越来越高了。这是整个社会经济环境变好，孩子们在发育阶段的营养更好造成的。社会经济发展也不是靠个人努力能做到的，这是时代红利。\n反过来看，是不是家庭经济环境变好，孩子就一定能长高呢？不一定。因为有基因的限制。\n放大到整个社会范围来看，人群的平均身高又的确是在向上变化的。\n如果把「立项」看做确定「下一代」产品基因的可行性动作，我们就必须想清楚：我们的基因是什么？是否可以演变？演变的方向是什么？这个方向适应市场选择吗？\n请注意自然选择的规律：并不是自然选择 推动 了生物的多样性，而是自然选择 允许 了生物的多样性。我们的主动行为是否有意义，大自然也不知道。\n市场遵循大自然的逻辑 市场和自然有许多相似之处。一个产品炸裂或者平庸，市场都可以接受。平庸的产品并非不能赚钱，只是不能赚到我们期望之外的钱罢了。\n企业可以通过 砸钱、造势、引流、集中买量、垄断 等等行为，在一定周期内影响市场的变化。但拉长时间，总会出现 均值回归 的情况。\n控制下的市场行为犹如「寒武纪生命大爆发」。生物种类在寒武纪突然呈爆炸式的增加意味着，生物进化除了缓慢渐变，还可能以跳跃的方式进行。爆发这个行为就注定了不可持续，爆发之后，一切还是会归于平庸。\n行业群里经常会有人聊到「现在什么火啊？不知道怎么立项啊！」。\n在立项的时候，并不存在「现在什么比较火」这种概念。现在火的东西，是已经经过验证的东西。既然经过了验证，就只能获得 确定性。 基于「现在火」的东西做立项，不可能「继续火」，最多也就是收回成本和小赚一笔罢了。\n灵游坊的梁其伟在谈到立项时说过一段话我很认同：\n我们现在的创作环境是一样的，游戏就是提供乐趣，什么东西提供了乐趣什么东西就能满足需求，原本只是这样一个常识性问题。《黑神话》也好《流浪地球》也好，他们都是回归常识的作品。但现在我们的游戏行业，多数产品的立项，都不遵循创作者的常识性思维。\n它们不是什么好做什么，而是什么火做什么，但他们怎么知道别的没做过的东西火不火？比如武侠火不火？这个问题放在真正立项的对话当中，一定会有更严谨的自证逻辑去说明，哎，你看现在武侠果然不火！\n没有创作者的情况下，所有的人做决策会过度依赖于一些表面现象。比如市场上流行什么，影视领域什么火，古偶的粉丝更多等等，然后新作就被带偏，失去了武侠的味道。这种思维惯性，是源于厂商对市场的保守预测，是基于大数据和资本化的求稳策略产生的，很容易进入一个越来越跑偏的自循环。\n先预设了某种题材不火，然后采集了数据证明它不火，接着做PPT包装好找投资人强化了不火的概念，最后大家都觉得市场就这样，确实不火。但事实上，一通操作下来，受众也没有多高兴，需求还是得不到满足。\n如果我们始终盯着「现在火」的爆款做模仿和微创新，就是虎口夺食的竞争逻辑。我们试图从现有产品的受众中「挖」出一部分用户注意力，结果可能是和其他类似的产品一起推高买量 CPI。\n「竞争意识削弱竞争力」，创造更多地是来自自由，而不是来自竞争。\n游戏是内容产业，是文化产品，很难像平台一样做到赢者通吃，遵循创作者的常识性思维，弱者总会有机会。弱者不一定非得挨打，自然界也并非都是弱肉强食，还有很多——也许更多——「共生关系」。\n我们要知道自己适合做什么（基因）。我们要学习市场需要什么。\n市场足够大，市场允许多样性的存在。\n爆款在未爆之前，不会成为模仿的对象。创作者要做的，就是尊重创作者的常识性思维，努力磨练商业技能，支撑到自己的创作成为爆款的那一刻。\n基于模拟退火算法的立项思路 一次我的合伙人问我，现在 A 类型的游戏在市场上的占有率最高，为什么我们不做 A 类型的游戏而要做 B 类型的游戏？\n我说是因为我们已经试过太多类型，现在必须选择一个方向深挖下去，等待我们的爆款。B 这个方向我们有积累，有成功案例，做得比同类产品好那么一点点，可能说明我们有 B 类型的基因。\n寻找立项方向的过程有点像兔子爬山：\n有一只兔子要爬山，它朝着比现在高的地方跳去，找到了不远处的最高山峰。但是这座山不一定是珠穆朗玛峰。这就是 爬山算法。 爬山算法的问题在于 无法确定局部最优解就是全局最优解。\n一个更好的算法是 模拟退火算法 ：\n这只兔子喝醉了，所以它随机地跳向不同的方向。这期间，它可能跳向高处，也可能踏入平地。后面它渐渐清醒了并朝最高方向跳去。这就是模拟退火算法。\n模拟退火算法的核心思想是：从某一较高初温出发，伴随温度参数的不断下降,结合一定的概率突跳特性在解空间中随机寻找目标函数的全局最优解，即 在局部最优解能概率性地跳出并最终趋于全局最优。\n如果这个兔子比喻不够形象，我们可以想象一下跳槽：\n进入职场早期，对行业还不太了解，有差不多的机会就可以跳； 如果已经很了解这个行业了，那就必须看到更高的工资才跳。 回到立项思路上来：\n对于技术好的团队来说，当然是啥类型都可以做。同时开多个方向的项目，一个个扫描，是非常低效且费钱的方式； 主动加入随机性，能更迅速找到更好的立项方向，或者更迅速地失败（看你怎么理解失败）； 控制随机性：早期可以多一点，在积累了经验后减少一点，到了后期就越来越明确。 在立项过程中应用模拟退火算法，是要把立项当做一个连续的行为。每个成功或者失败的项目，都会影响下一个产品的立项选择概率。\n在《津巴多普通心理学》中，介绍了一种常用的启发式决策法：拆分问题。这种方法也是模拟退火算法的应用。我们这次不用「跳槽」思路，换成「规划最优的职业发展路线」的思路看看拆分问题的启发式决策如何做：\n用几年的时间，在不同行业，不同岗位上做做看； 根据这几年的经验确定自己擅长和喜欢的方向； 瞄准一个方向持续做下去。 每一次失败的结果，都能增加对你基因的理解，都在为下次成功积累经验。\n模拟退火算法详解 为了避免打破阅读的连贯性，对于模拟退火算法，我并未做出足够的解释。本文末的参考资料中提供了更学术的解释。本文并非讨论算法的文章，因此我直接引用万维钢老师提供的非常精彩的，通俗易懂的解释：\n先看一道算法题。想象你在探测一条山路，有上坡有下坡，有山峰有低谷。你的任务是在最少的步骤内找到路上尽可能高的一座山并且停留在那里。这个游戏的特点是你不用走：每一步，你或者选择从地图上的一个点跳跃到另一个点；或者选择随便报一个点，让系统告诉你那个点的高度是多少。请问你会用什么样的方式跳跃呢？\n一个符合直觉的更快的算法是先随便选一个点，以这个点为基础，往左和往右各移动一步，看看哪边会升高，然后往升高的方向移动，继续探测下一个点。如果你发现两边都比这里低，那你就找到了一个高点。这个方法的问题在于你很容易被卡在一个局部的山峰上，会错过更高的山。\n有个特别好的算法叫「模拟退火算法（Simulated Annealing）」。设想你在地图中位于A点，你知道A点的高度。现在随机选择一个之前没去过的B点，如果B点高于A点，你就跳到B点；如果B点低于A点，那么你有两个选择——\n如果现在还处在探测的早期，那你就以一个比较高的概率跳到B点。这是因为地图对你来说还有很多不确定性，你随便跳一下将来碰到更高点的概率很大； 如果已经在探测晚期，那就降低跳跃的概率。 参考资料 万维钢：理解进化论的新视角 专访梁其伟：全北京的人才凑一起，可能都凑不出一个3A项目的人 Simulated Annealing 模拟退火算法详解 人工智能中的爬山算法 继续阅读 混合变现从入门到放弃（上）——混变产品的误区 混合变现从入门到放弃（中）——混变产品的运营和商业化设计 混合变现从入门到放弃（下）——我的产品立项思考 全系列完结 ","date":"2023-07-02","description":"市场足够大，市场允许多样性的存在。","lastmod":"2023-07-02T03:16:04Z","slug":"hybrid-game-optimization-3","tags":["gamenote","presentation","sagiteam"],"title":"混合变现从入门到放弃（下）——我的产品立项思考","url":"https://blog.zengrong.net/post/hybrid-game-optimization-3/"},{"categories":["impressions"],"content":"6 月 16 日，我在广州参加了第二届中国游戏出海峰会。在上午完成了 《混合变现从入门到放弃》——中度休闲+混合变现游戏调优经验谈 的分享，并负责了下午场的主持工作。\n俗话说得好：没有 DEADLINE 就没有输出。 借助这次峰会上的分享， 我把这半年积累的经验与去年形成的 休闲游戏立项方法论 进行了一次整合更新，并强迫自己做一次相对完整的输出。\n完整分享一共有五个部分，本文包含 PART03/PART04 两个部分。\n中度休闲+混合变现游戏调优经验谈 PART01 为什么要做混合变现产品 PART02 混合变现产品常见误区 PART03 IAA和混合变现产品的运营 PART04 中度休闲混变产品的商业化设计 PART05 我的产品立项思考 PART 03 IAA 和混合变现产品的运营 从纯 IAA 游戏转到混合变现游戏，要面对之前没有接触过的产品运营。\n超休闲游戏不需要运营 超休闲游戏使用广告变现，追求更高的广告次数和更高的 eCPM。\n超休闲游戏大多追求 7 日回收，甚至部分游戏仅关注 3 日数据。这意味着 超休闲游戏更多将用户当作一批「数据」来看待。\n超休闲游戏更多采用买量冲榜模式的原因。它追求极低的 CPI，是为了能快速将产品推导榜单顶部以带来巨大的自然量，从而获取更高的收益。\n既然用户只是一批「数据」，既然只关注用户几天内的留存，那就不需要和用户产生关系，也就不需要运营。\n长线 IAA 产品的运营目标是促活 长线 IAA 产品尽管使用广告变现，但拥有更长的用户时长，更高的用户黏度。\n因此，长线 IAA 产品的运营的目标应该是保持用户的活跃，让用户有机会看到更多广告。\n基于产品内容做运营，是符合长线 IAA 产品特征的。我认为长线 IAA 产品的运营精力应主要放在游戏内容上，通过大数据分析用户行为，调整产品变现点增收，优化产品内容增时，推出运营活动保活。\n强运营：内购占优，追求 LTV 增长 由于不面对「真正的人类」，轻度游戏的运营相对简单。一旦产品的充值增多，内购提升到一定比例，就必须要面对实际的用户，这可是个活儿。\n我这里提到的「内购占优」，并非内购比例一定要高于 50%，而是说 「内购收入占据了无法忽视的比例」 。按照 SAGI GAMES 的经验，我认为这个比例应该是 30+%。\n运营的目标有不同，手段有差异，无非是下面几种的组合：\n促进用户活跃 提升 LV 老用户回流 产品、品牌宣传 触达新用户 协助解决产品问题 运营的三驾马车「渠道运营、用户运营、活动运营」，包含大量需要学习的知识，需要有经验的团队。仅 用户运营 就包含日常咨询、引导充值、退款对接、渠道维护等等工作。良好的运营能大幅提升产品的活跃程度，为产品带来收益和生命周期的增长。重度游戏常用的 GS 团队，甚至能为产品带来 30% 以上的流水提升。\n对于重度游戏转来混变的团队，也要注意不要简单代入之前重度游戏的思路。这毕竟是个混变休闲游戏，运营上可以做得更轻一点。\n运营工作的复杂度值得单独开一场分享来讲，本篇不做深入。\nPART 04 中度休闲混变产品的商业化设计 调优实例：广告内购之关系 重度游戏改变为混变游戏时，我们很担心 加入广告会影响付费用户的充值行为，导致想充钱的人不愿意充钱而看广告。 这是不是个伪命题呢？\n下面的实例讲述了 SAGI GAMES 如何将一款线上运营的纯内购游戏改为混合变现游戏。希望看完这个实例，大家不会再有类似的疑惑。\n上图展示的是该游戏的广告收入部分。在调整前，这款游戏的广告收入比例在 3% 以下，是可以忽略不计的。从 3 月开始，游戏不断更新版本，加入广告点，目前的广告收入已经可以占到整体收入的 30~40%。\n上图展示的是游戏混变调整期间内购收入的变化。在 3~4 月 DAU 不变的的这段时间里，内购收入的总量并没有显著的的变化。\n但 3-4 月包含一个趋势上的变化。在广告加入后，突然出现的大 R 变少了（体现在图中的大幅度折线变少），游戏整体的收入更加平均。\n黄色马克笔的部分展示了游戏买入广告用户期间，内购收入的增长。我们发现，即使定向的是广告用户，内购也会有一定程度的增加。\n但我们并不能简单下结论说广告不会影响付费。\n上图展示了混变调整期间，该游戏的平均付费用户收益，也就是付费用户的平均价值。在使用黄色马克笔分开的三个时间段内，平均付费用户收益一直在降低。这说明付费用户的确「更不愿意付费了」，这可能是因为他们去看广告了。\n最右边的马克笔部分的数据降低，是在增加了小额付费点之后发生的。这意味着在增加了小额付费点之后，之前的大 R 可能降级成了中小 R，之前的小 R 可能降级成了纯广告用户。\n我们分析，出现这种情况，是由于 广告价值、小额付费价值和原始付费点的价值相冲突 导致。后续调整的思路就是提升标准付费价值，分群处理小额付费，真正 让小额付费成为将广告用户转为付费用户的转化点，而非付费用户薅羊毛的降级点。\n这是不是说明，前面的调整是失败的？\n也不尽然。\n该游戏做混变调整的目标是为了 让更多广告用户愿意付费，让纯白嫖用户愿意看广告。 这需要关注用户付费渗透率的变化。\n上图为游戏混变调整期间付费用户渗透率的变化。我们发现在增加了小额付费点之后，付费渗透率得到了明显的提升。这代表有更多白嫖用户加入到付费大军中来了。根据前面收入图的展示，在调整期间，整体产品的收入也增加了。\n这是一次成功的调整。但还有很多更加细节的优化工作要完成。现在我们可以得出结论，对重度纯内购游戏加入广告进行合理的混合变现改造：\n会影响原本付费用户的充值行为。 需要重新设计广告价值、内购点价值。 广告点和内购点价值要区别对待，不给玩家比较的机会（价值不可比较）。 要综合关注最终目标的实现情况，以整体产品收益的增长为标准。 要怎么做到更好呢？ 这需要对用户分群。\n基于分群的商业化设计 上面讲到的案例数据，仅来自于单一渠道。为方便叙述。我们将其命名为 A 渠道。\n我们同时也在关注其他渠道的该产品数据。有趣的是，在另一个付费玩家占优的 B 渠道中，经过混变调整后，我们发现产品整体付费率、人均付费次数、ARPU、ARPPU 均有降低。但广告渗透率增长了 4 倍。付费数据降低了 30%。\nB 渠道的零氪玩家、小R/大R 玩家的广告次数均增长 3 倍以上。付费越多的玩家，广告绝对次数越多。\n这是个挺有趣的现象，我们分析 B 渠道数据之后，得出以下的结论：\n由于增加了较多的广告点，且一直在提升广告收益刺激白嫖用户看广告，导致相当一部分的玩家从付费充值获得资源转为通过看广告来获取资源。 玩家习惯通过广告获取资源后，付费道具价值处于一个贬值的状态，导致购买次数也会降低。 付费用户看广告次数更多了，因为他们认为看广告获得的资源足够了，造成了充值意愿和充值额度双低。 要解决 B 渠道中出现的这些问题，需要将 A 渠道和 B 渠道的商业化思路做不同的调整，这就是基于渠道的用户分群。\nB 渠道玩家更愿意付费，应该降低付费玩家的广告曝光，调整广告价值。\n对于长线运营的游戏，分群是必选项。除了刚才提到的基于渠道的用户分群之外，还有下面多种分群方式：\n基于玩家游玩时长条件分群。 基于玩家角色等级条件分群。 基于玩家购买历史条件分群。 基于玩家充值金额条件分群。 基于运营活动条件分群。 ……等等等等…… 和运营工作类似，分群设计的复杂度也值得单独开一场分享来讲，本篇不做深入。\n同款混变产品在国内与海外的区别 无论是出海，还是出口转内销，都要注意到一个事实： 中国已经在 2022 年超越日本成为全球第二大游戏市场。\n也请注意： 拥有相同的文化，超大的国土面积，近似收入比例的高速发展中的市场，太阳系中也只有中国这一个。\n为什么要强调这一点呢？因为我们人类习惯于把自己的成功经验原封不动搬到另一个市场做尝试。这是要亏钱的。\n上图展示的是某款产品在全球不同国家地区的留存数据。在新增数量近似的前提下，你能看到完全不同的留存表现。\n留存表现和经济发达程度（网络质量和手机性能）、文化、付费习惯息息相关，商业模式不能简单进行移植。\n大部分超休闲产品全球都能发得不错，但中度混变产品就需要根据国家和地区做出差异化。\n幸运的是，中国是最大的单体市场。不幸的是，中国的发行渠道分散的比较严重。每个渠道之间用户的区别，甚至比海外地区之间的区别还要大。\n从前面提出的渠道 A 和渠道 B 也可以看出，渠道差异和地区差异，是需要深入研究的，是值得为之投入大量精力的。\n混合变现的商业化调优，任重而道远。\n继续阅读 混合变现从入门到放弃（上）——混变产品的误区 混合变现从入门到放弃（中）——混变产品的运营和商业化设计 混合变现从入门到放弃（下）——我的产品立项思考 继续阅读：混合变现从入门到放弃（下）——我的产品立项思考 ","date":"2023-07-01","description":"让小额付费成为将广告用户转为付费用户的转化点，而非付费用户薅羊毛的降级点。","lastmod":"2023-07-01T01:08:18Z","slug":"hybrid-game-optimization-2","tags":["gamenote","presentation","sagiteam"],"title":"混合变现从入门到放弃（中）——混变产品的运营和商业化设计","url":"https://blog.zengrong.net/post/hybrid-game-optimization-2/"},{"categories":["impressions"],"content":"整个上半年，博客(blog.zengrong.net)都没有产出比较干的内容，主要是因为写稿需要消耗大量的精力，而我把大部分的精力都投入到了产品调优和写代码中了。\n6 月 16 日，我在广州参加了第二届中国游戏出海峰会。在上午完成了 《混合变现从入门到放弃》——中度休闲+混合变现游戏调优经验谈 的分享，并负责了下午场的主持工作。\n俗话说得好：没有 DEADLINE 就没有输出。 借助这次峰会上的分享， 我把这半年积累的经验与去年形成的 休闲游戏立项方法论 进行了一次整合更新，并强迫自己做一次相对完整的输出。\n完整分享一共有五个部分，本文包含 PART01/PART02 两个部分。\n中度休闲+混合变现游戏调优经验谈 PART01 为什么要做混合变现产品 PART02 混合变现产品常见误区 PART03 IAA和混合变现产品的运营 PART04 中度休闲混变产品的商业化设计 PART05 我的产品立项思考 PART 01 为什么要做混合变现产品 在经历了前几年的爆炸性增长之后，超休闲游戏的下载量在 2022 年第一季度开始呈下降趋势。2022 年第四季度，超休闲游戏下载量同比下降 24%，至 27 亿次。\n超休闲产品模式的盈利能力与用户获取成本，和使用视频广告获利直接相关。两者都在 2022 年受到多种因素的影响。 超休闲游戏是疫情期间下载量增长最快的类型。\n以下数据来自 Sensor Tower。\n让我们看看超休闲游戏下载量同比变化情况：\n2020 年：60% 2021 年：15% 2022 年： -24% 2023 年：持续下降 在同等时间段内，我们看到混合变现休闲产品在 2022 年的下载量增长了 13%，并一度超越了超休闲产品。\n在北美市场，2022 年底，混合变现产品下载出现了明显的增长势头。\n混合变现拥有更长的游玩时长，更长的局内时间，当然就带来了更强的变现能力。\n同样的，混合变现产品在 Google Play 和 App Store 上都表现出更高的收入增长。就连「超休闲之王 Voodoo」的发行主管 Alexander Shea 都在演讲中公开宣称：超休闲游戏已死。据他观察，Voodoo 在 2022 年发行并取得成功的超休闲游戏都是现有产品而非新游戏。\n以下数据来自 Sunday Games\n2022 年第一季度，超休闲 TOP100 中新游戏数量下降了 36%，总下载量下降了 57%，每个新游戏的平均下载量下降了 33%。\n对比前一年，过去12个月头部超休闲厂商中唯一出现增长的是 Supersonic，下载量几乎涨了 10%；而 Crazy Labs 和Rollic 则是持平；Azur 和 Say Games 则下滑近 20%。最为严重的是 Lion Studios 和 Voodoo，二者下载量均下降了 40%。\n以下内容来自 Game Look\nVoodoo 发布公告称，自 2022 年以来，整个组织和工作方式从超休闲游戏转变为混合游戏，且专注于战斗和解谜玩法，以实现更高的游戏时间和留存率。\nVoodoo 目前已推出10款混合休闲游戏，每款大约能够创造 2000 万至 1 亿美元的年收入。\nVoodoo 同时强调，将在未来几年打造年收入超 1 亿美元的产品。\n转型后的Voodoo实现了近 3000 万的月流水。从旗下内购收入 TOP10 来看，千万月流水、月下载 700 万的《Mob Control》，正是一款典型的混合变现游戏。其付费点除了超休闲游戏常见的「去广告」外，主要来自赛季通行证、不同类型道具和卡包售卖等方面，商业机制愈发丰富、且中度化。\n海外尚且如此，更别提国内的《咸鱼之王》《疯狂骑士团》这些打着休闲旗号的重度游戏了吧😄\n无论是行业风向，大数据，头部企业，都在转型或者深耕中度混变游戏，我们还有什么理由不开始呢？\nPART 02 混合变现产品的常见误区 SAGI GAMES 从 2019 年就开始转型制作中度休闲混变产品。彼时混变不是主流，行业媒体也少有提及。\n这几年中，我们和许多开发者与发行商交流过，发现无论是从超休闲、轻度游戏「升级」转向中度混变，还是从重度游戏「降级」转向混变游戏，大家都会出现「路径依赖」的误区。也就是说，人们习惯于把之前积累的经验拿到混变产品中来。\n这个部分就来讲讲这些误区，为什么我能讲呢？并非是 SAGI GAMES 做得有多好，而是因为这些坑，SAGI GAMES 一个不少全都踩过。\n误区一：混合变现就是在内购游戏里面加个广告，或反之。 广告变现的概念，很多从内购游戏过来的设计者都不太能理解。\neCPM/CTR/Show Rate/点击率/广告次数/广告类型/聚合/瀑布流/Bidding 这些概念容易理解，但将其和游戏的商业化融合起来，尤其是还要和内购系统有机融合起来，并达到互相促进的作用，就没有那么轻松了。\n从纯 IAA 转到混变游戏的设计者也会突然发现，之前依赖大数据模型的手段不太管用了。在付费设计的过程中，必须更多考虑玩家的付费体验，同时还要经常面对来自重氪玩家的礼貌问候。\n误区二：我可以先做一个内购版本，在上架的时候改成广告版本。 这是个非常幼稚的想法，但我至少被问过 100 遍。乍一看还挺合理的，但付费和广告的心流体验完全不同啊兄弟！\n付费顺畅的情况下，一次付费只需要几秒钟；看一个激励视频，需要至少 30 秒（少部分平台有 15 秒的选择），对玩家的心流体验是个毁灭性打击。\n价值体系在内购改成广告的时候也需要做重构，重构不全面的情况下甚至可能影响经济体系。\n你还需要考虑 eCPM 在广告次数的影响下可能产生的变化，这个变化能直接导致游戏的收入变化。\n内购点更多是集中在商城中呈现的，而广告点要更多分布在游戏的各个角落，让广告有更多的展示可能性，让玩家在「最期待」的那个时刻主动点击广告播放按钮。\n误区三：我可以先做一个海外版本，在拿到版号之后改成国内版本。 海外并不是一个国家，但中国是全球最大的单体游戏市场。海外用户和国内用户的付费习惯、付费能力、沟通方式都完全不同。简单粗暴的策略只能得到简单失败的结果。\n海外的广告体系，和国内的广告体系也是不同的。国内的广告渠道更加复杂和多元，不同渠道之间的广告价值和用户习惯天差地别，接入方式不尽相同，需要根据产品特点做好甄别，避免做无用功。\n很多从海外转到国内的产品人不能理解，为什么国内的聚合平台发展偏慢，而且有那么多的专属广告平台。我认为这和国内渠道垄断的前提分不开。海外市场尽管也被 Google Play 和 App Store 两大渠道垄断，然而经过这么长时间的发展，监管体系逐渐形成，厂商+GOV也在和平台不断角力。因为这样我们才等到了苹果税和 Google Play 分成降低新政并因此收益。\n反观国内，以硬件厂商为主的硬核平台，以广告发行为主的腾讯（广点通）、字节（巨量）、快手系，瓜分了几乎所有的渠道市场。虎扑、B站这类垂直平台由于量级和受众限制，不可能提供完善的基于游戏的广告曝光，更加无法在算法和价值上与前述平台竞争。这些平台的规则不尽相同，落后平台的潜规则更是层出不穷。这个现状在短时期内不会有太大变化。\n误区四：我想要做一个内购:广告收入 5:5 的游戏。 从前 …… SAGI GAMES 也想要做一个内购:广告收入 5:5 的游戏。直到 …… 我们上线了这个游戏。\n上图为这个游戏的真实收入比例数据。绿色代表广告收入，蓝色代表内购收入。\n收入比例从 6:4 至 8:2 变化的因素很多。有渠道的不均等，有节假日的变化，有玩家质量的提升，也有游戏内活动的影响。\n总之，对于这个产品来说，内购占比比我们想象中更大。但对于我们同在运营的另一个产品，数据则是完全相反的。\n走出误区的六种思维方式 1. 用户是不同的。\n有的用户更愿意付费，有的用户更愿意看广告。有的用户只想白嫖。游戏中需要对付费做分群设计。\n2. 流量是不同的。\n一部分流量中的用户很可能比另一部分流量中的用户更有价值。流量代表来源渠道、来源平台、变现平台、以及时间周期差异。\n3. 产品是不同的。\n有的产品更适合做内购，有的产品更适合做广告变现（例如网赚就是为广告而生），还有的产品更适合做买断制。\n我个人更喜欢买断制（更贴近游戏本质的乐趣）。然而同等投入下，买断制游戏带来的收入规模一般低于内购，不搞钱还怎么做下一款游戏？\n4. 付费架构是一个复杂系统。\n应该像设计 Game Play 一样去设计付费架构，在游戏初期就要将付费方式思考清晰。即使在 DEMO 版本中没有付费，你也必须清楚，付费点会加在哪里。\n5. 内购是游戏内货币的收入来源，广告也是。\n广告作为主要来源，内购就是补充来源。反之亦然。\n不要把广告价值与内购价值做简单的平均化。 （例如用 eCPM 和广告次数去对等计算付费点的价值）\n广告和内购之间不会互相影响，只会互相促进。除非 …… 你的设计有问题。\n6. 赚钱是最重要的事。\n先学会赚钱，再学会站着赚钱。\n参考资料 Habby刺激下，Voodoo宣布全面转型混合变现，目标单款创收1亿美元 Sensor Tower: The State of Mobile Gaming 2023 继续阅读 混合变现从入门到放弃（上）——混变产品的误区 混合变现从入门到放弃（中）——混变产品的运营和商业化设计 混合变现从入门到放弃（下）——我的产品立项思考 继续阅读：混合变现从入门到放弃（中）——混变产品的运营和商业化设计 ","date":"2023-06-30","description":"先学会赚钱，再学会站着赚钱。","lastmod":"2023-06-30T09:56:03Z","slug":"hybrid-game-optimization-1","tags":["gamenote","presentation","sagiteam"],"title":"混合变现从入门到放弃（上）——混变产品的误区","url":"https://blog.zengrong.net/post/hybrid-game-optimization-1/"},{"categories":["impressions"],"content":"本文作者： 格正大王\n本篇是格正大王进行一次儿童科普讲座的PPT，其中所有插图均为格正大王手绘。本篇对成瘾机制的讲解深入浅出，建议搭配下面几本书一起学习效果更佳：\n《成瘾：在放纵中寻找平衡》 《根本停不下来：用心理学戒瘾，做一个自律的人》 《欲罢不能》 《我们为什么会上瘾》 本篇转载已获格正大王授权。\n全文完 ","date":"2023-05-16","description":"格正大王进行一次儿童科普讲座的PPT，其中所有插图均为格正大王手绘。","lastmod":"2023-05-16T10:31:32Z","slug":"how-is-the-game-addictive","tags":["gamenote","presentation"],"title":"游戏怎么让人上瘾的？","url":"https://blog.zengrong.net/post/how-is-the-game-addictive/"},{"categories":["impressions"],"content":"经济增长的三个阶段 第一阶段叫「马尔萨斯阶段」 ，或简单工业化阶段，增长由人力资源的配置效率驱动。\n在这个阶段里的主要增长方式是农民进城当工人，搞劳动密集型产业。\n第二阶段叫「索洛阶段」 ， 或中等收入阶段，增长由投资驱动。\n在这个阶段中，升级工厂，引进先进的设备和技术，生产附加值更高的产品，才能负担较高的工资。资本密集型的发展方式，需要很多钱。\n第三阶段是「创新驱动」阶段 ， 这是通往发达经济体之路，增长是由创新驱动的。\n这个阶段要打造自己的护城河和技术壁垒，不再搞「内卷」式恶性竞争，踏踏实实赚钱。\n2023-04-16 长江边烤串\n游戏行业的三种风格 游戏行业属于互联网行业的一部分，但由于游戏提供的是情绪价值，同时又遵循服务业和文化娱乐产品的基本规律。\n类比经济增长的三个阶段，我认为游戏行业的发展，有三种风格。\n第一种风格我称之为「热点风格」 ，这有点类似于「马尔萨斯阶段」，不追求质量和专业度，而追求产品的数量、热点和速度。\n这个热点可以是社会热点，也可以是某个突然发生的事件，甚至还可以自己制造出一种风格上的热点。\n2018 年的微信小游戏，2020 年的抖音小游戏，以及疫情期间的超休闲游戏市场，都基于这种风格创造出了新的行业高度。这种风格，起点较低，成本不高，拥有极强的生命力。\n第二种风格我称之为「内卷风格」， 类似于「索洛阶段」。就是花最多的钱，请最贵的人，搞最强的工业化，卷最赚钱的项目。\n例如 MMORPG 热，SLG 热，二次元热，女性向热。进入这种风格的有的是游戏大厂，有的是互联网大厂，有的是游戏和互联网之外的资本。\n然而游戏是情绪产品，无论花多少钱，不好玩就是不好玩。和「索洛阶段」一样，这个风格最大的影响是调整行业整体薪资水平，需要「更卷」的方式或者「更大」的资本来消化影响。\n第三种风格我称之为「小而美」风格， 类似于「创新阶段」。这是游戏人心心念念期望达到的风格，也是绝难达到的风格。\n这需要高级的人才，需要宽松的文化，需要保护创新，鼓励颠覆式创新。需要行业的开放，需要政治和文化环境的友好和宽容。\n最怕的是，没有高级人才的能力，却想拥有「小而美」的风格。没有创新的命，却得了友好包容的病。\n外包是什么风格？ 宏基电脑的创始人施振荣提出过一个「微笑曲线」理论，认为代工厂的商业模式，利润低，体量小，很难搞工业化升级，是「茅山道士（毛利3%到%4）」。\n游戏制作流程工业化，有很长的路要走。但游戏产品提供的情绪价值，又让标准化这个过程变得极难。\n也许工业化和情绪化是不可调和的矛盾，也许这也是行业需要寻找的创新机会。\n继续阅读 我理解的休闲游戏（下）——超休闲和小游戏教给 SAGI 的知识 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 我理解的休闲游戏（上）——平台如何定义休闲游戏 休闲游戏立项方法论 全文完 ","date":"2023-04-18","description":"最怕的是，没有高级人才的能力，却想拥有「小而美」的风格。没有创新的命，却得了友好包容的病。","lastmod":"2023-04-18T01:23:16Z","slug":"three-styles-of-the-gaming-industry","tags":["gamenote","management"],"title":"游戏行业的三种风格","url":"https://blog.zengrong.net/post/three-styles-of-the-gaming-industry/"},{"categories":["technology"],"content":"本文作者： Lee\nAI 成图使用地址 本文介绍的绘图 AI 为 Stable Diffusion，一个文本到图像的潜在扩散模型。它使用来自 LAION-5B 数据库子集的 512x512 图像进行训练，可以生成包括人脸在内的任何图像。\n网址：https://github.com/AUTOMATIC1111\nStable Diffusion 界面认知 Stable Diffusion 是全英文界面，使用频率最高的地方已标注中文，如下图：\n常用功能 Stable Diffusion 最常用的生成图片的方式为文字生图、图生图。\n一、文字生图步骤 1、打开 Stable Diffusion 后，在页面左上角选择 txt2img。\n2、选择 Checkpiont，也就是我们常说的模型。\n不同的模型会出来不同的效果，大家可以自己多多尝试。\n3、输入正向和负向的关键词。\n注意，所有的关键词最好是英文，关键词之间需用逗号隔开。\n解释一下\nPrompt（正向关键词） 可以简单的理解为“我希望画面呈现的”，例如1 boy。\nNegative Prompt（负向关键词） 可以理解为“我不希望在画面里呈现的”，例如 missing fingers。\n所以，如果你想要生成一张美女图，关键词可以按照以下内容设置。\nPrompt：a beautiful girl, blonde hair, perfect face（一个漂亮的女孩，金发，完美的脸——来自Google翻译）\nNegative Prompt：ugly, tiling, poorly drawn hands, poorly drawn feet, poorly drawn face, out of frame, mutation, mutated, extra limbs, extra legs, extra arms, disfigured, deformed, cross-eye, body out of frame, blurry, bad art, bad anatomy, 3d render（丑陋，平铺，画得不好的手，画得不好的脚，画得不好的脸，框架外，突变，突变，额外的肢体，额外的腿，额外的手臂，毁容，变形，斜视，身体出框架，模糊，糟糕的艺术, 坏解剖学, 3d 渲染——来自Google翻译）\n注意：关键词越详细，AI越能生成你想要的内容。关键词写的越简略，AI就会更多的是自由发挥。\n4、选择参数。\n参数可以直接参考下面的截图。可以根据自己的需求做微调。\n5、prompt 和参数都写好后，可以直接点击 Generate 生成。\n恭喜你成功生成了图片！\n二、图生图步骤 1、点击 img2img，选择 Checkpoint。\n2、上传照片。\n3、描述一下原图中想要的部分，再描述你想添加的内容。\n4、点击 Generate 生成，就可以得到你想要的图片了。\n概念详解 一、如何写出好的 prompt？ 在正向关键词里，如果不刻意增加/删减权重的话，我们输入的内容是从左往右依次权重从高到低。\n例如下面的3个关键词里，a beautiful girl 权重最高，perfect face 权重最低。如果对调位置，那么 AI 会优先处理perfect face。\na beautiful girl, blonde hair, perfect face\n要想出好图，上面简单的 prompt 是不行的，对于 AI 来说太抽象了。为了获得更好的画面效果，prompt 可以分成三段：风格走向 + 背景 + 人物。\n举个例子\nultra-detailed, detailed light, background detailed, ultrahigh res, (best quality), ((masterpiece)), (highres), originalextremely detailed 8K wallpaper, (an extremely delicateand beautiful), ultra-detailed, detailed light, an extremeldelicate and beautiful, background detailed,(photorealistic:1.2), professionallighting, (blur light:1.3), extremely beautiful detailedanime face, soft light, perfect lighting,\nbuilding, indoors, night, gloomy, gorgeous, Western-stylebuilding, sconcestairway, column, portrait, red wall, redcarpet, palace, resplendent,\nfull body, solo1girl, (izayoi sakuya:1.3), sakuya, maidblack legwear, blue dress, hair between eyes, looking atviewer, maid apron, medium breasts, pantyhose, puffyshort sleeves, puffy sleeves, short sleeves, thigh holsterthigh strap, thighband pantyhose, white apron,(silverhair:1.3), twin braids, maid headdress, short hair, blueeyes, floating hair,gravel, black footwear, thighstrap, green bow, standing, from below, tsurime,lookingdown, cold and eleaant\nGoogle翻译：\n超详细，详细的光，详细的背景，超高分辨率，（最佳质量），（（杰作）），（高分辨率），原始非常详细的 8K 壁纸，（非常精致和美丽），超详细，详细的光线，非常精致和 美丽，背景详细，（真实感：1.2），专业照明，（模糊光：1.3），非常漂亮的详细动漫脸，柔和的光线，完美的灯光，\n建筑，室内，夜晚，阴沉，华丽，西式建筑，灯塔，柱子，肖像，红墙，红地毯，宫殿，金碧辉煌，\n全身，单人，1girl，(izayoi sakuya:1.3), sakuya,女仆，黑色紧身裤，蓝色连衣裙，双眼之间的头发，看着观众，女仆围裙，中胸，连裤袜，蓬松短袖，蓬松袖，短袖，大腿皮套，大腿带大腿连裤袜，白色围裙，(银色 头发: 1.3)双辫子，女仆头饰，短发蓝眼睛，飘逸的头发，碎石，黑鞋，大腿带，绿色蝴蝶结，站立自下而上,吊眼角,俯视,冷艳\n二、关于权重 AI 生图中，某个关键词权重越高，成图会越往这个词上体现。那么如何表示某个关键词的权重高呢？\n在 Stable Diffusion 中，调整权重用的是小括号 () 和中括号 [] 。\n1、() 是增加权重。\n(关键词) 表示将该关键字的强度增加 1.1 倍，相当于 (关键词:1.1) 。\n具体权重值：\n(关键词) 权重是1.1\n((关键字)) 权重是1.21\n(((关键字))) 权重是1.33\n举个例子：\n小红同学想要生成一张主人和狗的图，输入了关键词 man, dog。加关键词权重的方法就是打小括号，注意是英文的小括号，写出来就是 (dog)，权重可以在 dog 后打冒号和一个数值来修改。不同权重出来的结果不一样。\n(dog:0.5)\n(dog)\n(dog:1.5)\n2、[] 是减少权重。\n[关键词] 表示将该关键词强度降低 0.9 倍，相当于 (关键词:0.9)。\n具体权重值：\n[关键字] 权重是0.9\n[[关键字]] 权重是0.81\n[[[关键字]]] 权重是0.73\n如果想把两个角色混合生成一张图，则关键词可以按照这个格式写：[关键词1:关键词2:0.5]\n比如说把男女融合一下，那么关键词的写法就是：[man:women:0.5] 或者 [male:female:0.5]\n甚至你还可以把川普和拜登结合一下，关键词写法是：[Joe Biden: Donald Trump: 0.5]\n小贴士\n在 AI 生图过程中，使用增加权重 () 更方便，如果某个关键词需要减少权重，只需注明0.x。\n例如，需要降低关键词“狗”的权重，只需写(dog:0.5)，表示“狗”的权重只有0.5，不必再写个[]\n三、引用Lora 如果你很喜欢美少女模型，但是又想让美少女是某种特定的风格怎么办？那就在关键词里加上 Lora 吧！\nLora 是特殊定制的画风的插件，使用方式是在正向关键词里用\u0026lt;\u0026gt;引用。\n例如你训练好了一个单挑篮球的 lora 模型，想要在美少女的基础上生成一个篮球风格的。那么在正向关键词里，需要写下：\u0026lt;lora:dtlq:0.7\u0026gt; ，后面的0.7是指的单挑篮球 lora 在整个关键词里的权重。\n案例中输入0.7是N个参数值里最接近理想状态的，在实际使用中可以按需调整。\n如何指定人物动作？ 若想要人物做出特定的动作，需要用到 ControlNet。\n以 txt2img 文字成图为例，在 seed 下方有一个 posex，点开可以看到一个火柴人。\n第一步：尽情摆弄这个火柴人吧！然后点击 Download image，把你摆好的姿势保存下来。\n第二步：在主页下方找到 Controlnet，上传摆好的火柴人模型，选择对应选项。\n第三步：返回到网页的右上角，点击 Generate 生成，就可以获得姿势一样的图片。\n小贴士\n用了火柴人，也需要描述正向和负向关键词。\n不会摆动火柴人怎么办？那就直接用图生图也可以生成同样的动作。就比如说上面用彭于晏生成了同一个动作的亨利卡维尔。\n参考网站 AI成图有点像炼丹药，自己写的咒语炼出来的丹药奇形怪状怎么办？\n那下面这个网站就不得不收藏！\nhttps://civitai.com\n在这里简单介绍一下参考网站的使用方法。\n一、如何利用参考网站 1、选择一张你喜欢的图。\n2、点开图片找到右下角的参数，点击 Copy Generation Data（复制生成参数）\n3、粘贴到 Stable Diffusion 里面，然后点击右边的小箭头，一键填充文字和参数。调整时只需要改 正向关键词 里的词语就行。例如 brown hair 改成 pink hair，girl 改成 man。\n二、其它参考网站 除了 Civitai 这样良心的网站以外，还有一些网站也值得一看。\n【openai】\nhttps://openart.ai/discovery\n【最全的使用指南】\nhttps://stable-diffusion-art.com/beginners-guide/\n【Lora大全】\nhttps://huggingface.co/lora-library\n全文完 ","date":"2023-03-29","description":"使用 Stable Diffusion，ControlNet 和 Lora。","lastmod":"2023-03-29T11:19:48Z","slug":"stable-diffusion-manual","tags":["AI","stable-diffusion"],"title":"据说 AI 已经学会用手吃热干面了","url":"https://blog.zengrong.net/post/stable-diffusion-manual/"},{"categories":["impressions"],"content":"这是我在 2021年4月准备的一个内部培训。其中的经验均为我在实际的面试过程中使用过的，部分内容给出了出处。\n做这个培训的主要目的是给高级面试官一个上手实操的指南，避免看了很多书但不知道如何问问题的情况。\n经验不一定正确， 但保真，保熟。\nQ：如何问一个好问题？ 参照记者提问的方式：who、when、how、why、where。\n如果你询问一个复杂问题，你会得到一个简单的回答。人们会参照你问句中的那个最有力量的词给出回答。你问「你熟悉这个技术吗？」他会说「是的，我很熟悉。」然后你不得不想出更多问题来询问他。\n可以这样问：「你觉得这个技术怎么样？」\n扩展阅读： 高情商聊天的十个技巧\nQ：什么时候应该说，什么时候应该听？ 面试的前期，应该尽量倾听。\n你可以从候选人的状态看出候选人对他说的内容是否认同，以及他是否愿意说下去，是否希望深入聊当前的话题。如果你发现他不愿意说下去了，作为面试官，你应该换一个话题。\n如果他不愿意说的话题对于面试来说很重要（例如能判断他能力的技术细节，例如经历上的迷惑点）那么，应该对这个话题持续表达关注，要求他给出更多细节。\n对于候选人答不出来的问题，可以适当给出提醒，并说出问这个问题的目的。例如：我希望用这个问题来了解对于该技术的掌握程度。\n当你已经了解了你期望了解内容的 80% 以上的时候，你就应该从「听」转向「说」。\n切忌滔滔不绝地说，你的「说」是为了能了解下面 20%的内容。你要关注在你「说」的时候候选人的表情和情绪，你要是不是让候选人能发表观点。例如：你觉得我刚才谈到的开发流程有没有不合理的地方？你觉得刚才介绍的产品还有什么地方可以改进？\n注意，你的「说」是为了得到余下 20%，甚至超出你预期的信息。\n史蒂芬柯维：\n我们大多数人都不是为了理解而倾听，我们为了回应而听。\nQ：如何节省面试官的时间？ 用五六个基础的技术问题快速过滤掉技术不过关的人。理想情况下这一步可以电话进行，被滤掉的人数会多到令你吃惊。 与应聘者面对面（不在本地也可通过电话）交流一下他们曾经遇到的技术问题、用过的工具、做过的决策等。Google的数据证明这种“行为面试”是最有用的。但讨论技术概念时，不要用一些必须准确回答的问题来拷问对方。 最重要的，讨论一下应聘者之前做过的项目。或者如果对方在Github上有代码，让他讲一讲。 尝试建立“文化切合度”。注意，这里仅指工作文化。有的人只是跟你不太像，不要下意识地把他们拒之门外。不要把公司搞成兄弟会。 最后，就安排一个“试用项目”。找一个大小合适、自包含、非关键，但成功后将会发布的真实项目，付薪水让应聘者做上一周，密切关注进程。 扩展阅读：技术面试已死\nQ：如何管理候选人的预期？ 老板： 高薪，那么就要高成就，你要对的起你的薪水。 员工： 高薪，是我应得的。你既然挖我来，你就要持续付出。 HR 和职能线负责人，在面试期间和员工入职后都要做好预期管控。\n在面试期间，要让候选人明白高薪意味着高投入和高预期，让员工对来到公司后，不会有过大的落差。\n员工入职后，既然已经在面试期间进行了预期管理，候选人也选择了我司，候选人已经经过了心理建设和全面考量。那就可以在安排工作时提出你期望的要求。\n提出预期的时候注意两点：\n高要求 不是 无法达到的要求，它至少是你（面试官 ）可能做到的。 高要求 是你在面试期间就已经根据他的能力确定过的，认为他有能力或者有潜力可以做到的，位于员工 学习区 的要求。 高薪招的这么多人，要么被裁，要么停止涨薪。现在失衡（如果算的话）的市场价格，又会回到平衡的状态去，人该回哪回哪了。但如果真成功了，那就叫洗牌。\n扩展阅读： 关于高薪招人\nQ：表现得平易近人还是高冷牛逼？ 看人。对于大多数求职者来说，表现得平易近人会有优势。\n有时候候选人会比较高调（骄傲），也没有必要表现高冷。这里需要一些技巧，不露声色展现自己的实力。\n对于装逼的人，他一定会露馅的。\n切忌和候选人争吵。如果候选人提及面试官不清楚的知识和技能，可以诚恳地请教他具体的细节。注意态度是：诚恳。\n对于真正牛逼的人，你的诚恳也会让他感觉到诚意。这是加分项，不是减分项。\n切忌在面试过程中「好为人师」，因为这会浪费时间。如果候选人表现出求知欲，只需要适当提出一些思维线索即可，让候选人可以顺着你提供的线索想下去。「更多的内容可以等你来了之后再全面交流。」\nQ：如何在面试中表达疑惑？ 直接说出来。\n对于简历中觉得不合理的地方，可以直接问。\n例如：我看到简历中有一段时期的空白，我想了解一下这段时间你是怎么安排的？\nQ：如何在面试中表达不满？ 直接说出来。\n如果候选人的简历很糟糕，例如跳槽过于频繁等，可以直接表达出来。\n例如：我看到你前面几家公司待的时间都很短，能告诉我是什么原因么？\nQ：如何提前了解候选人？ 必须要提前看简历。 简历上有个人博客的，要去看博客，有 github 的，要去 github 浏览，有作品集的，要去看作品。 简历上有个人读过的书籍的，去翻一下书籍的目录和书评。 去翻一下简历中候选人呆过的公司，对于你感兴趣的公司，访问一下他们的主页，或者找熟人了解该公司的信息。这不是背调，这是让你在与候选人的沟通中有更多的共同话题，也方便在聊天的过程中得到更多信息。 扩展阅读： 客户接待也是生产力\nQ：如何分级面试？ 技术面 成长面 性格面 压力面 家庭面 Q：如何吸引候选人？ 真诚、真实。不要给不切实际的承诺，不要让他对来公司之后的生活产生过热的想象。\n说公司曾经发生过的事。\n说公司价值观范围中的好现象。\n扩展阅读： 苏世民面经\nQ：如何判断候选人是否在说谎？ 问细节。真实信息和虚假的信息，候选人在对于信息披露的细节方面是不对称的。细节越多，越详细，就越能判断这个人是不是在说谎。\n例如有人在大厂工作过，可以通过询问他们平时的工作方式、沟通方式、工作内容等进行了解。\n针对你刚才提到过的这件事,如果你有机会能重新做一遍,会有哪些地方不一样?\n每个人做事的颗粒度不一样，做事颗粒度越细，就越能做成一件事。这个问题不仅能看到一个人的反思深度，还能根据他的答案看到做事的颗粒度。\n如果他只是蹭车的，那么，他所提出的改善，不是颗粒度很粗，就是只集中在个别方面。\n注意，即使你判断出候选人在说谎，也不必明示。\n扩展阅读： 非对称信息操作技术\nQ：如何在面试中学习？ 面试的过程其实是学习的过程。我在面试者身上主要学习到下面这些知识：\n候选人所在的城市、公司、团队的氛围，开展工作方式，组织方式，文化。 侯选人所在项目的数据、规模、成本、工作方式。 候选人重新原则的原因、思维方式。 Q：如何判断候选人的内驱力 如果你突然有半个月的带薪休假，但是必须研究一个事儿，你会研究什么?\n“既然时间限定是半个月，只有这个量级的课题能够有实质性的进展和成果，所以不怕小。”\n作为面试官，你当然立即能够感受到这是一个比较务实、对具体执行和操作有手感的人。了解一个人的内驱力高低，基本上就能判断出来这个人未来应对变化的能力。\nGoogle 的两条经验：\n关注世界级的编程大赛，发现这样进来的顶级程序员在谷歌过得都不怎么好。因为赢得比赛需要外驱力，而进行创新需要内驱力。 相比于复杂的面试题，一个最简单的面试题的效果最好：你几岁开始拥有自己的电脑？拥有电脑越早的人在谷歌发展越好。 扩展阅读： 得到CEO脱不花：面试一个人，问这4个问题就够了！\nQ：如何判断候选人的学习能力 你正在做的事，行业里最顶尖的人或者公司是谁，他们是怎么做的?\n他的眼界怎么样? 作为一名从业者，是不是对这个行业有足够开放的视野? 知不知道这个行业里有多少顶级高手? 定义这个“最顶尖高手”的标准是什么? 运营什么方式进行研究？ 通过人脉说明他在业界的真实地位。 怎么看待自己和标杆之间的差距？ 如何缩短这个差距？ 真正的顶尖高手心中一定会有另一个高手。 Q：如何判断候选人的人际关系 你在此之前的人生经历中，做过什么重要的取舍?\n当你问这个问题的时候，具体事件不重要，决策机制才重要。看一个人做取舍的方式，能够了解他的进退感和分寸感，进而了解他是否是一个有清晰边界意识的人。\n为什么做出这种选择？ 出发点是什么？ 为什么是这个时候做？ 你能不能清晰地界定选择的代价是什么？ 在你做出这些选择的前后都发生了什么，分别怎么解决的？ 这个问题很像是要去判断一个人的决策能力。 大部分候选人也是这么理解的，所以大家会努力重现处境和场景，试图讲一番自己的道理。 而事实上，问这一切都不是目的，目的是看他在回答这些问题的过程中，有没有提及其他相关人，怎么提及； 特别是怎么描述其他人在这个选择和决策中的角色。 每个人生重大取舍都定会有一两个关键人施加过影响。对此完全避而不谈的创业者，通常都无法从商业计划书走到实际起步阶段。因为这种人的组织能力很弱，也无法从原有的关系网络中为自己吸引到追随者。而那些啰哩啰嗦、提到人数过多的，多愁善感和老好人都无法带大队伍。 全文完 ","date":"2023-03-06","description":"做这个培训的主要目的是给高级面试官一个上手实操的指南，避免看了很多书但不知道如何问问题的情况。","lastmod":"2023-03-06T12:21:11Z","slug":"interview-training-of-teacher-zeng","tags":["management"],"title":"曾老师的面试培训","url":"https://blog.zengrong.net/post/interview-training-of-teacher-zeng/"},{"categories":["impressions"],"content":"用ChatGPT搞钱，“赚”不下去了\n谈谈我对这段时间铺天盖地 AI 替代人类的观点的看法。\nAI 一定会替代人类的某些工作，这是人类世界进化的必然过程。就像家庭主燃料从木柴和煤炭转换到天然气，通讯方式从书信到无线通讯系统，全球卫星定位系统替换了纸质地图，机动车替换了马一样。人类总会追求更好用，更高效，更全面的工具。\n我们能不能，会不会被替换，取决于我们对工具的看法。你是那个用工具的人？还是那个被使用的工具？\n孔子说「君子不器」，荀子说「君子性非异也，善假于物也」，这些老先生们几千年前就想得很通透。\nSAGI 是一个自研自发的游戏公司。岗位类型很多，每个岗位该如何面对 AI 呢？\n美术同学要把 AI 当成工具。就像大家求学的时候把画笔当成工具，工作的时候把 PS 当成工具一样，AI 只是个更高级的工具。\n程序同学最重要的能力是工程能力，是一个人完成框架设计，编码构建，质量测试，产品发布的能力。不要担心 AI 会写斐波那契数列和做网站，这解决不了工程问题。\n产品（策划）同学的的重要能力包括协调与沟通能力（领导力），在复杂信息中找出问题根源的能力（统计分析），以及丰富的想象力（AI 是很好的想象力训练工具）。\n优化师和素材同学要意识到，投放本身就是一套实时变化算法，而素材优化是在寻找这套算法的风格和漏洞。要研究趋势，分析市场策略，借助 AI 的力量洞察方向。\n渠道和商务同学，HR 和 GR 同学，你们暂时是安全的。但如果你们能更多了解和使用 AI，就能快速获得超越同行的能力。\n请大家记住，时代抛弃我们的时候，不会对我们说抱歉。\n当汽车代替马成为出行工具时，马有意见吗？\n全文完 ","date":"2023-02-16","description":"当汽车代替马成为出行工具时，马有意见吗？","lastmod":"2023-02-16T00:17:01Z","slug":"2800","tags":["ai","chatgpt"],"title":"ChatGPT：马有意见吗？","url":"https://blog.zengrong.net/post/2800/"},{"categories":["impressions"],"content":" 《三体》没能狂飙 空手套一切，幸好流浪地球导演没想过干传销 高启强背后的男人，更狠 聊聊这两年游戏行业最大的认知陷阱 早上读了这几篇文章，随便写点感慨：\n《三体》剧集的主创全是三体迷。他们在创作上明确认为：「首先要捕获原著粉，在这个前提之下再尽量普及给没看过书的普通观众。」\n从观众反馈来看，主创们的确做到了捕获原著粉，但同时这让《三体》的受众受到局限，一定程度上影响着《三体》的叙事。\n改编原著，创作者必须思考是服务书迷还是服务普通观众。剧版《三体》选择了前者，动画版《三体》选择了后者。\n但动画版《三体》塌房了。 塌房的原因并不是因为选择了服务普通观众，而是因为不上心。\n《狂飙》的编剧朱俊懿说：\n你白天对着剧本干了一天，然后你可能到晚上结尾的时候发现这全是一坨屎，全在删，就会睡不着，很痛苦，躺在床上，人物在脑子里情不自禁的自己跑，一跑能跑半晚上，特别的耗精力，那段时间有时候整个人是恍惚的，下来吃饭的时候跟鬼一样。\n不疯魔不成活。\n我必须说句实话：很多人都是看不懂《三体》的，甚至看懂《流浪地球》都不容易。什么元宇宙，什么 ChatGPT，什么 AI，在没有良好的商业化实现之前，无法普及给大众。最近尬吹ChatGPT的自媒体，主要还是蹭流量居多（看本篇的标题就懂了吧图片）。\nChatGPT 进入了 Bing，距离商业化（赚钱）更进一步，才算是得到了大众的认可。微软狂飙的股价说明了一切。\n游戏行业外的人，对于羊了个羊和塞尔达的理解可能是类似的：它们都是游戏。 只有我们这些业内人，才会去做分类。\n我经常苦口婆心地劝团队说， 做游戏要理解人性，理解创作，理解商业。\n你是要小破球，还是要小时代？\n我们在立项讨论时的「面向硬核玩家和休闲玩家」，也是这个道理。 确立了受众，才有不同的内容偏好，不同的运营思路，不同的商业化逻辑。\n中国游戏行业依托于这个全球最大的单体游戏市场，凭借对人性的把握和开脑洞的商业创新，实现了在全球游戏行业范围的资本超越。体量的超越又带来了对优质内容的渴求。这渴求接着造成了巨大的资源浪费。\n我们要清醒。要客观面对自己的短板和长板，用创作和商业的双重逻辑看待游戏。依托有限的资源，有的放矢地生成产品。\n全文完 ","date":"2023-02-09","description":"你是要小破球，还是要小时代？","lastmod":"2023-02-09T12:09:14Z","slug":"chatgpt-threebody-game","tags":["gamenote","chatgpt"],"title":"创作和商业的双重逻辑：ChatGPT、三体、狂飙和游戏","url":"https://blog.zengrong.net/post/chatgpt-threebody-game/"},{"categories":["impressions"],"content":"书籍信息 书名：关系飞轮 作者: 徐志斌 出版社: 中信出版社 副标题: 用户亲密关系如何左右私域及未来增长浪潮 出版年月: 2022-07 页数: 280 ISBN: 9787521743609 阅读地址： 得到电子书 超级用户与亲密关系 本书一直围绕超级用户与亲密关系做讨论。亲密关系与超级用户的形成密不可分。书中使用了 「可信、可亲、可爱」 三个形容词，包含详实的一手数据，讲述了企业与用户之间建立的 「平辈、晚辈、长辈」 的三种亲密关系模型。\n超级用户典型的「四高」特征 转化率高，用户掏出真金白银来支持； 复购率高，持续黏着，持续复购； 分享率高，绝大部分用户都积极分享并协助组织活动； 转介绍率高，用户持续影响亲友的购买决策。 「用户-企业」亲密关系的三种类型 形容词 企业扮演的亲密关系类型 要点 关键现象 企业合拍 我很可信 平辈 强调用户投入产出比，即用户以最低的成本获得最佳收益；品牌必须靠谱且能够承载信任，能让用户在向好友推荐时提升自己的可信度 前置运营（在重信赖下，用户愿意提前支付费用）、躺赢时代（用户希望用最低的成本获取最好的服务）、价值观制胜 适合绝大部分企业，尤其是重视技术和服务的企业 我很可爱 晚辈 鼓励用户参与“养成”企业，相比产品的完美程度，用户更在意关系的亲密度、产品的迭代速度 及自己的参与程度 品牌特权（用户容忍品牌犯的小错）、时时成长（要让用戶感受到企 业的成长速度） 特别适合新品牌 我很可亲 长辈 人们天然亲近于帮助自己的人（时时从用户角度出发满足用户需求，就像长辈关爱晚辈一样） 荣光时刻等各种时刻（在和用户接触的过程中，照顾好用户）、时时陪伴（建立起情感连接） 适合领先企业和知名品牌，或利润率较高、重服务的企业 分享疲倦和圈层陷阱 用户越来越不愿意分享。\n用户正在从曾经的频繁分享变成担心分享会影响个人形象，担心无聊信息会骚扰到朋友。这是分享疲倦。\n用户依然在分享，但分享带来的浏览和转化越来越低。这是圈层陷阱。\n下表为阿拉丁提供的微信小程序不同行业分享比率。\n氪金玩家就是超级用户 《征途2》夫妻系统促进付费 《征途2》按年付费金额区分玩家：\n中R：5000～30000元 大R：3万元以上 超R：10万元以上 下表为 2018 年的用户构成及付费比例：\n付费占比 用户占比 大R+超R 32.87% 0.47% 中R 34.43% 3.16% 小R 32.7% 96.37% 2015年 夫妻系统的上线直接拉动游戏的营业收入增长20%～30%。《征途》团队观察这些亲密关系上线后普通用户付费情况的变化，发现：\n中R中，8%上升为大R（含超级大R）； 普通用户中，5%上升为付费用户，1.3%上升为中R，还有0.92%上升到大R（含超R）。 用户类型 付费率 30留 贡献率 周数据 超R 90% 89% 60% 普通用户 4.3% 48% 9% 月数据 超R 95% 97% 65% 普通用户 7.06% 61% 7% 《枪神》恋爱系统促进付费 《枪神》游戏通过分析2017年4月在游戏中处于恋爱状态的4169名玩家，发现游戏中恋爱对用户付费没有太大影响。 在游戏中50%的恋爱玩家的付费额提升，47%的付费额反而下降，3%的消费保持不变。\n在将用户区分为小R、中R、大R和巨R（超R）后，数据立刻反映出不一样的结果（如下图），亲密关系对拉升用户进入中R、大R和巨R等级的效果显著。\n由此可见，亲密关系为用户持续活跃、提升新用户获取速度和获取能力、增强付费意愿等方面都提供了非常大的帮助。\n华为与「花粉」的长辈关系 为举办2018年“花粉”年会，华为不仅承担了受邀参加年会的1000位“花粉”的住宿费用，还在伴手礼中放入了一部时价1799元的荣耀手机，仅这两项支出总计就超过200万元。\n曾嵘说：\n套用上面的亲密关系，尽管在「年会」上华为扮演的是「长辈」关系，但就如 MIUI 社区一样，粉丝对于品牌依然有「养成」心结。\n蔚来与用户的晚辈关系 2019年，蔚来汽车新车销售量的45%来自老客户转介绍，其中转介绍数量最多的一个车主介绍了50位好友购买蔚来汽车； 2020年新冠肺炎大流行期间，用户转介绍率一度升至70%； 2021年转介绍数量排名第一的车主一人转介绍成功160辆。 车主中，深度参与蔚来汽车社区活动和日常运营的比率达30%。在蔚来社区，浅度、中度和深度参与社区运营的用户比例为1∶6∶3。 2018年6月，蔚来在微信上试点，为每位车主构建一个专属服务群，每个群包括用户、销售、客服（2人）、交付、城市总经理、售后（2人）、加电，群内8个工作人员都围绕用户提供服务。\n曾嵘说：\n保姆关系并不是「晚辈」关系。任何一个企业，都不能在商业活动中仅维持同一种关系。企业与用户之间会模拟不同的亲密关系，模拟关系为企业带来用户活跃和转化。不同的亲密关系导致不同的用户宽容度与用户参与度。\n新创品牌更容易构建这种模拟关系。\n关于模拟关系，在《小群效应》中也有提及。详见读书笔记：《小群效应》（下）——时间货币与社群类型 。\n老年人与保健品的长辈关系 灰色保健品套路 免费三部曲\n免费诊疗：获取好感，联系方式。 免费讲座：神话功效，推销铺垫。 免费吃饭：增进感情，推销产品。 两个套路\n上门收钱。 定期回访，跟踪服务。 顾客一旦购买了保健品，就意味着和卖保健品的人建立了一种长期关系。\n正规保健品套路 进入用户熟悉的场合。公园、菜市场等等。 在恰当的时间点建立感动、惊喜的瞬间。设计和创造「美好的事情」。 时时陪伴。售后服务是一个能够建立长期关系的重要环节和关键出口。 让客户有成就感。销售人员刚入行时不懂销售技巧，青涩的模样反而很容易使顾客产生安全感，让老年人产生指导欲，愿意提供帮助，就像长辈在关心晚辈成长一样。 发起一个大事件，让用户知道公司在做的事。亲密关系是起点，而不是终点。亲密关系一旦确立，就不能停歇。 拿来就用 金句 让关系崩塌的两个主要因素分别是期望值和价值观。 用户投入的时间越短，收获越令人愉悦，就越容易引爆。 为什么爽文流那么成功： 用户要求企业和品牌在越发简便的使用体验和低廉的费用背后提供更超值的服务。让用户用最低的成本就能获得自己想要的服务。 腾讯对私域的定义是「品牌和用户之间长远而忠诚的关系」。 拥有几百万甚至几千万用户的大型企业很难简单地使用私域能量，因为设备及账号、品牌管理、内容服务和活动设计、销售管理、售后服务等一系列关键环节都需要更庞大的系统支撑，他们显然更关心会员管理、围绕提升用户复购和用户生命周期的运营等事项。 蘑菇分体系 曾嵘：这个体系可以用来做会员体系的参考。\n蘑菇分=（经营规模+系统管理+在线收租）×系统活跃度\n2021年2月蘑菇租房倒下，没能最终坚持下来。\n百合网亲密关系 3T公式 亲密关系（Intimacy）=双向自我表露（Talk）+多样的相处经历（Togetherness）+相处的时间（Time）\n颜值是让人产生接触和了解欲望的前提，要缔结亲密关系依靠外在和内在。\n让关系崩塌的两个主要因素分别是期望值和价值观。\n关系开始的六大因素 随时随地可触发关系的事件。 随时随地可互动的场景。 让用户明白自己是不可或缺的角色。 充分运用地域和兴趣要素。 让关系持续进阶的方式。 品牌和用户要形成一致的价值观。 全文完 ","date":"2022-12-19","description":"亲密关系与超级用户的形成密不可分。书中使用了「可信、可亲、可爱」三个形容词，包含详实的一手数据，讲述了企业与用户之间建立的「平辈、晚辈、长辈」的三种亲密关系模型。","lastmod":"2022-12-19T09:37:18Z","slug":"relationship-wheel","tags":["readingnote","reading"],"title":"读书笔记：《关系飞轮》","url":"https://blog.zengrong.net/post/relationship-wheel/"},{"categories":["use"],"content":"自 2015 年购买后，这块 HHKB Pro 2 键盘 已经陪伴我 7 年了。依然和购买的第一天一样好用。美中不足的是：不支持无线。\n好在 HHKB 的定制化已经很成熟了，我直接下单购买了 YDKB静电容改无线蓝牙BLE BT双模主控，半小时改造和测试成功。\n改造过程 电路板、电池、发光二极管、双面胶。\n拆掉三颗螺丝，拆掉排线和原装电路板，装入购买的电路板，接上电池。\n左下图为原装电路板和定制化电路板的比较。\n搞定了，下面是全家福。\n购买建议 建议购买 MiniUSB 口的版本。因为 Type-C 口也是通过拆除 MiniUSB 之后加上增高垫改装的。MiniUSB 口更结实，而且也更加美观，能和原始外壳匹配。\n下图左边为改造后的 MiniUSB 充电口，右边为改造后的 Type-C 充电口。\n电池上方务必要垫上购买时候的包装泡泡塑料以隔绝静电效应。\n可淘宝搜索「YDKB HHKB」 或者 「YDKB客制化」获取购买链接。（纯推荐，非广告）\n参考 HHKB Pro2 简单评测\n全文完 ","date":"2022-12-18","description":"HHKB 的定制化已经很成熟了。","lastmod":"2022-12-18T08:10:31Z","slug":"hhkb-pro2-wireless","tags":["hardware"],"title":"HHKB Pro 2 蓝牙无线改造","url":"https://blog.zengrong.net/post/hhkb-pro2-wireless/"},{"categories":["impressions"],"content":"48 小时内核酸绿码登机，6 个小时不喝水，不吃东西，不摘 N95 口罩。测了 6 次体温，下飞机后做一次核酸，全程闭环转运，一路大白护送，直接给你怼到酒店房间门口。 喜提 5+3！\n是的，上面就是我昨天晚上的经历了。\n不得不夸一句，国家的入境隔离防疫做得很好， 非常完美地保护了我不受境内新冠病毒侵害。\n之前每次走过境外隔离酒店周边时，我都提心吊胆的。生怕有海外变异病毒不小心通过气溶胶感染到我。但自己经过了这么一套流程，我才发现境外回来隔离的流程 很严格！很谨慎！很安全！\n尤其是和 2022 年 12 月 12 日的境内疫情现状相比。\n48 小时绿码\n入境健康申报码\n大白帮忙卸下行李\n颇有点生化危机既视感的酒店\n5+3 隔离告知\n荤素搭配的盒饭\n大白每天上门做核酸\n杭州到武汉一千多公里呢，开私家车吗？\n红码能不能上飞机？我明天要联系一下\n一切似乎都很好。大白工作很努力，隔离酒店条件不错，甚至提前打开了空调，迎接我们这些从 26 摄氏度南国归来的人们。\n但感觉总有哪里不对？\n因为，这一切的工作，意义都不大了。无论隔离多么严谨，都不能改变我们始终会羊的事实。\n因为，已经放开了。\n12 月 13 日，「通信行程卡」下线。今天 SAGI 的神雕大侠人数比例已经达到 20%，明天很可能就会超过 50%。\n5+3 的隔离（对外省的我来说只能选择 8 天），自费 3840 元，已经无法实现它的价值，是一笔极大的浪费。\n转运的大巴车，大白的劳动，酒店的折扣，是浪费。\n为什么入境隔离政策不能与时俱进，快速跟上放开的步伐？\n也许是要阻断境外的「变异毒株」？也许是还在研究最新政策？也许是因为目前防疫形势过于严峻，还没来得及关注到入境的群体？\n政策的制定有其滞后性。机构越庞大，效率越低下。 但基层的执行者真的辛苦，入境的人们也真的配合。大家全力给一台已经报废的汽车装上崭新的轮胎，每天再精心擦拭一遍。\n关注过程，不在乎意义。\n每个人都很努力，没有人质疑。在新的政策没有公布的前提下，质疑无效。这是官僚机构运转的基础规则。\n期待着 8 天内能有新的政策变化。\n否则，我就只能如朋友圈里猫姐所说的：\n经过严格的5+3流程，阴性入境的你可以安全地到达阳性的同事朋友们身边。。。。\n猫姐\n全文完 ","date":"2022-12-12","description":"政策的制定有其滞后性。机构越庞大，效率越低下。","lastmod":"2022-12-12T13:13:22Z","slug":"5+3","tags":["COVOD-19"],"title":"双十二：通信行程卡都下线了，入境5+3还有意义吗？","url":"https://blog.zengrong.net/post/5+3/"},{"categories":["impressions"],"content":"三体是2006年开始写的，那个时候数学证明三体问题没有通解。这也是刘慈欣写三体恒纪元和乱纪元的科学基础。\n但三体问题有稳定特解。\n2010年，3族特解被找到。 2013年，新发现了13族解。 2014年，新发现了14族解。 2017年，新发现669个解。（不知道是不是不好找规律，这以后的记录都是N个解，而非N族解） 2018年，新发现1223个解。 2021年，基于机器学习搞出了135445个解，而且称数值精度比普朗克尺度还小。\n​在我看来，以三体人远超地球人的科技，以及小说中行星级别的超级计算机，三体人是可以算出更多特解的。\n毕竟三体是2.0级宇宙文明，但2022年的地球文明才0.7级。马斯克成功了才能升级到0.9吧 😅😅😅\n现在你可以说大刘错了。\n硬科幻总会错的，因为科学的发展是总是超出人类的想象。\n你只能站在现有信息的基础上进行预言。\n三体的核心是社会学。三体里写社会舆论的部分，这三年已经被验证了(闭嘴！）。\n但是，领先于这个时代就可以成功吗？\n先驱和先烈差距只有一个字。 天才和疯子往往都是一个人。 执着或执拗决定项目的成败。 梵高和波洛克都开创了时代，都英年早逝，但一个死后成名，一个在世辉煌。 三体不稳定，为什么太阳系稳如狗呢？\n太阳系实际是二体问题。地球和木星的引力相对于太阳来说，都太小了。 幸存者偏差。 我们总说幸存者偏差。 但你是那个幸存者，还是那个偏差？\n参考链接\n既然三体运动非常不稳定，那太阳系为什么能稳定存在？ 三体开播了，帮三体人找找稳定解 全文完 ","date":"2022-12-11","description":"我们总说幸存者偏差。但你是那个幸存者，还是那个偏差？","lastmod":"2022-12-11T00:58:55Z","slug":"tree-body-survivorship-bias","tags":["reading"],"title":"三体问题的幸存者偏差","url":"https://blog.zengrong.net/post/tree-body-survivorship-bias/"},{"categories":["impressions"],"content":"国务院副总理孙春兰在11月30日的座谈会上指出： 要以人民为中心，防控工作稳中求进，防控政策持续优化，走小步不停步，不断完善诊断、检测、收治、隔离等措施，加强全人群特别是老年人免疫接种，加快治疗药物和医疗资源准备，落实好 “疫情要防住、经济要稳住、发展要安全” 的要求。\nhttps://www.yicai.com/news/101612991.html\n现状 决策在拉锯； 物业公司、社区、街道、卫健委、核酸检测公司、人民政府不是互联网/游戏公司，执行能力有强弱，完成标准不统一，沟通和权限存在一定的障碍。它们要满足诉求不同，理解力也不同的群众们的需求，需要时间； 疫情管控花了三年时间「进化」到现在这个样子，不能期待三天就恢复正常秩序； 耐心等待，曙光已现。 我们需要做什么 加强体育锻炼，保持身体健康，增强抵抗力； 注意日常补充维生素和蔬菜，增强抵抗力； 全身心投入生活和工作，保持心理健康； 戴好口罩，并存储一些 N95 口罩（价格大约在RMB1.0-1.5每个，柳叶形比鱼嘴型好看）； 存储必须的药品，做好共存准备； 为自己负责，不要把自己的健康寄托在虚无缥缈的公告承诺上。 新冠备药建议 布洛芬、复方氨酚、氯雷他定、连花清瘟、抗原检测盒\n昨日(2022-12-03)在武汉亲测，以下药物线下药店均可买到，且没有做限制。\n也可以尝试阿里大药房线上、京东美团线上买药。\n下图来自微博：\n四季抗病毒合剂 是一种中成药，具有疏风清热解毒作用。新冠患者出现咽喉痛等上火症状，可以考虑使用。\n风寒感冒颗粒 是疏风解表药，一般得了流感，或者出现了鼻塞、流清涕、没有喉咙痛时，可以考虑服用。\n复方氨酚烷胺片、布洛芬缓释胶囊 主要是解热镇痛。复方氨酚烷胺片是一种复方制剂，里面含有对甲基氨基酚成分，当出现发热伴鼻塞、流鼻涕、全身酸痛的情况下，可以考虑使用；布洛芬缓释胶囊也是常用的解热镇痛药，一般用于发烧伴鼻塞、流鼻涕、头痛、全身酸痛等情况。\n连花清瘟胶囊 是目前非常火的一个药，主要起到清热解毒、宣肺泄热作用。那它一般在何时使用，效果好一些？如果出现鼻塞、流黄色鼻涕、喉咙痛，可以考虑使用连花清瘟胶囊。\n蒲地蓝消炎片 主要在清热解毒方面，力度会更强一点。如果出现喉咙痛、流黄鼻涕、咳黄痰等上火情况，才考虑使用。\n果维康主要 含有维生素C，在治疗新冠患者当中不是一个常规药。对于新冠十大症状，它没有明确的改善症状作用。\n面这一段解释来自湖北发布： 【关注】朋友圈疯传的“新冠吃药顺序图”可靠吗？\n下图来自 香港醫院藥劑師學會新冠患者用藥建議：\n请注意这个建议是2022年3月发布的，请自行分辨。\n症状 常用药 儿童 孕妇 哺乳期妇女 发烧 扑热息痛 OK OK OK 布洛芬 OK 不建议 OK 阿司匹林 不建议 不建议 不建议 喉咙痛 地喹氯铵 / 克菌定 OK（限10岁以上） 咨询医生 咨询医生 咳痰 溴己新 OK 不建议 不建议 氨溴索 OK 不建议 不建议 愈创甘油醚 OK OK 咨询医生 乙酰半胱氨酸 OK OK 咨询医生 干咳 福尔可定 OK 咨询医生 咨询医生 右美沙芬 OK 咨询医生 咨询医生 流鼻涕 氯苯那敏 / 扑尔敏 OK OK OK 氯雷他定 OK OK OK 西替利嗪 OK OK OK 鼻塞 赛洛唑啉滴鼻剂 OK 不建议 咨询医生 恶心呕吐 桂利嗪 OK 不建议 不建议 新冠亲历者讲述 8 天症状 这个视频建议看完，一个阳性亲历者讲述自己 8 天时间的感受（2022-12-02）：\n微博@钟文泽： https://m.weibo.cn/status/4842331268717934\n封控期间发在大群的话2022-11-25 @所有人 疫情反复，昨天光谷的商业体、写字楼也被通知静默 3 天。SAGI 此次在线办公也已经持续一周，大家辛苦乐！疫情中最重要的是保持乐观向上的心态。下面是曾老师的一点点建议。\n请少看或者不看各微信群里的负面消息，很多消息真真假假，时效性也有限，剪接和 PS 痕迹严重，我今天还看到把防控 1 号文的落款改成 2022 年拿出来发的，而且还有人相信。\n疫情的治理情况，疫区管理情况，社会舆论混合在一起，各说各话。游戏公司追求效率，大家对低效深恶痛绝。 但我们能很明显地能感觉到各级政府和基层的治理水平，管理人员的工作能力，人民群众的理解水平都是不一致的。这是我们无法改变的，多看这些负面消息只会增加焦虑，没有实际价值。\n古罗马最著名的斯多葛学派哲学家爱比克泰德说过：「对于不可控的事，我们保持乐观；对于可控的事情，我们保持谨慎。」\n如果能出门，每天尽量在做好防护的前提下去散散步，放松一下心情，放空一下自己。如果有运动的习惯，要坚持运动，即使被封在户内，运动条件也是很容易创造的。\n如果和家人一起住，就多关心家人健康，每天找个时间聊聊天；如果自己一个人住，就和家人朋友约好一个固定的时间段视频聊聊天。\n关注家人和自己的同时，也要关注好自己手头的工作。\n疫情很可能会长期持续，但生活必须继续。\n赚钱很重要！兄弟姐妹们冲鸭！\n《一只孤独的船》\n莱蒙托夫\n一只船孤独地航行在海上，\n它既不寻求幸福，\n也不逃避幸福，\n它只是向前航行，\n底下是沉静碧蓝的大海，\n而头顶是金色的太阳。\n将要直面的，\n与已成过往的，\n较之深埋于它内心的 皆为微沫。\n全文完 ","date":"2022-12-04","description":"疫情管控花了三年时间「进化」到现在这个样子，不能期待三天就恢复正常秩序","lastmod":"2022-12-04T04:09:33Z","slug":"covid19-coexist","tags":["sagiteam","COVOD-19"],"title":"准备好共存","url":"https://blog.zengrong.net/post/covid19-coexist/"},{"categories":["impressions"],"content":"这是 《小群效应》读书笔记 的第四篇，也是最后一篇。\n本篇的内容主要来源于原书中第八、九、十章。\n《小群效应》读书笔记 列表 读书笔记：《小群效应》（上) 读书笔记：《小群效应》——关于退群，瑞幸与疯狂星期四 读书笔记：《小群效应》——曾老师失败的Python课 读书笔记：《小群效应》（下）——时间货币与社群类型 七种社群 初始小群在 PC 时代是一种「不稳定态」，有生命力的小群会快速变大，否则就会迅速消亡。但到了手机时代，小群反而成为了稳定持久互动频繁的状态。\n社群的核心指标 成员之间互动是否频繁； 社群内主流群体的规模大小。 七种社群类型 社群分类法：七种场景类型\n街边集市： 58 同城是这类社群形态的代表。用户只是将社群作为一个发布信息的平台，不会有后续讨论，成员之间是陌生人关系。也不存在意见领袖。 社团圈子： 关系驱动，核心人员稳定，往往基于距离或者兴趣等构建。过强的互动，反而会导致参与人数越来越少。强运营会导致更多用户选择沉默。社团圈子是大部分行业社群的典型形态。 俱乐部： 地域驱动带来的用户互动强度，要远远超过兴趣驱动。“江西人在北京”“武汉人奋斗在北京”这样的群，其活跃度也远超其他群。 社区公园： 兴趣驱动，比俱乐部更大的兴趣圈，主流人群互动越频繁，社群规模就越大。KOL 是整个社区公园活跃的核心。 大型游乐场： 兴趣驱动，KOL 对整个社群的影响减弱。成员之间结成了各种新的小圈子，圈子之间的沟通较少，主流人群自己也形成了一个小圈子。一般是由于社群中涌入大量新人之后，KOL 未进行引导，导致新人组成了更多新圈子。这种变化不可逆。 商场： 人群极度分散，没有主流人群和KOL。商场和街边集市都以信息交换和解决问题为目的。街边集市在定位明确的情况下会成为商场。 大型商业中心： 分散，拥有商业价值，小圈子互动增强。拥有社群文化，例如「景涛咆哮组」。成员发声遵循默契。 社群洞察 不存在互动频繁但连接分散的社群。互动频繁必然导致连接紧密。 真实的社交网络在互动上是冷淡的，只有中小社群才可能互动频繁。 互动频繁的社群会分裂成小圈子，核心人群对社群影响大。 大规模的社群会分裂成小圈子。小社群若互动冷淡，会继续分裂。 兴趣社交互动热度的上限，是熟人社交的下限。兴趣社交反对无意义闲聊，熟人社交中的闲聊反而是联络感情。 社群规模越大，成员之间的对话会增加。 大型社群越大，KOL 的影响力越弱。 影响社群类型转化的四种力量：互动频率、KOL量级、商业化、外部力量。 团队的效率与内部沟通成正比，团队的创新能力与外部探索成正比。 时间货币 在用户打法（或用户池打法）中，用户的持续活跃、进阶、转化等，最终都会落到“时间货币”上来，也就是用户愿意活跃多长时间，并最终帮助社群获得多少收入。\n社群成员分类法 社群成员角色金字塔： 内容创造者、内容传播者、内容消费者。\n强运营 强运营就是运营者在努力消耗用户的时间。强运营的社群规模取决于运营团队能照顾到的最大成员数量。\n案例：“碳9学社”的磨课模型 交作业。从业经历撰文，或者读书笔记。 同伴阅读。共同阅读一本书，每天完成 1-2 章内容阅读，提交 300 字读书笔记。 磨课。小组成员多次深度讨论，形成一定结论。 正课。基于磨课过程形成的结论在全体成员面前做深度分享。 体验课和公开课。邀请外部嘉宾进行系列主题分享。 复盘作业。 强运营洞察 在所有人的互动频率平均的前提下，组织成员互动越频繁，交往时的轻松感就越强烈。\n让所有人都有表达，这很重要。\n强运营的成本 寻找成本：找到违反规则成员并剔除出去所需的时间。 情感成本：面对被剔除成员可能产生的抱怨和怒火。 运营成本：维护社群、发起各种活动、提供服务等各项实际成本。 潜在损失：移除高阶或活跃该成员对社群造成的影响。 噪声产生：默许成员破坏规则导致更多噪声产生，驱赶遵守规则的成员。 曾嵘说： 在当前的微信群规则下，更多人采用的方式是创建新群而非将成员移除群。这可能是个折衷的办法，但这也提升了成员的参与成本（会出现旧群信息无法同步，并保留大量死群的情况）。\n强运营的潜规则 不要骚扰成员。 对他人信息进行互动、评论、肯定、赞扬。 推荐自己喜欢的商品和信息。 不泄露群内的私密讨论。 贡献自己的优势，弥补他人的不足。 弱运营 弱运营的实质是充分运用各种特性和各种机制、基础需求，将并将之融入产品的各项功能设计中去，让用户自发地行动起来，自发地去寻找结果。\n每个小圈子都有自己的生命周期，如果能够在一个月内超过一定的阈值，即使群主已经离开，圈子也会持续活跃。\n阈值计算：在线时间、30 天和 7 天发帖回帖数、30 天和 7 天活跃用户等等。\n基于机制的弱运营 将用户人群细分。引导进入兴趣相似人群，有效提升互动率。 对产生的内容实时分类。将其分为阅读性和讨论性，配备不同的推广资源。 内容去中心化。所有内容提供基础曝光，根据内容属性利用关系链和细分人群做曝光推荐。 游戏设计中的核心需求 安全需求 成长需求 探索需求 交互需求 审美需求 个人实现 弱运营下的社群成员角色分类法 弱运营需要激发更多用户从非人类型向人类型转化。\nPlant 植物型：低频浏览。Animal 动物型：高频浏览。这两种成员是「沉默的大多数」。 Human 人类型：产出行为和动作。 God 大神型： KOL。产生内容，辐射影响，收获肯定和崇拜。 强运营和弱运营的策略 强运营 弱运营 按组建目的 事件驱动型 关系驱动型 按动作 浏览型 互动型 按关系 陌生人型 熟人型 按成员关系 关系不平等型 关系平等型 按场景 街边集市、社团圈子、俱乐部 社区公园、商场、大型游乐场、大型商业中心 按时间货币 运营者努力消耗用户时间 让人们愉快消耗对方时间 模拟关系与事件驱动 我将你的目标视为我的目标，并自觉分配时间、精力和资源去帮助你努力实现这个目标，这就是事件驱动。\n模拟关系是事件驱动的表现。各种不同的事件促成的仪式感，粉丝的称谓形成的文化，造就了社群用户之间的模拟关系，消除陌生感，增加亲密感。\n关系驱动和地域驱动是社交驱动力中的基础力量。兴趣驱动和事件驱动是内部的强作用力，利益驱动和荣誉驱动是最大的推动力。\n《小群效应》全部读书笔记\n全文完 ","date":"2022-11-28","description":"在用户打法（或用户池打法）中，用户的持续活跃、进阶、转化等，最终都会落到“时间货币”上来，也就是用户愿意活跃多长时间，并最终帮助社群获得多少收入。","lastmod":"2022-11-28T05:44:59Z","slug":"little-group-effect-4","tags":["readingnote","reading","little-group-effect"],"title":"读书笔记：《小群效应》（下）——时间货币与社群类型","url":"https://blog.zengrong.net/post/little-group-effect-4/"},{"categories":["technology"],"content":"现象 同样一篇文章，在 macOS 操作系统的浏览器中能正常显示文字的加粗效果，在 Windows 操作系统中无论如何都不显示加粗效果。 显示粗体的文字位于 \u0026lt;strong\u0026gt; 标签中。 浏览器无关。 解决 blog.zengrong.net 在 CSS 中设置了 font-weight（SASS语法）：\n1b, strong, em 2 font-weight: 500 将 font-weight 的值改为 bold 解决了问题。\n1b, strong, em 2 font-weight: bold 原因 根据 CSS 定义，font-weight 可以使用短语或者数字格式表示。\n短语的有效值为： nomral bold lighter bolder。\n数字有效值为 100-900。\n100 到 900 之间的数值大致对应如下的常见粗细值名称。lighter 和 bolder 会基于父元素继承，这里不讨论。\n短语 数字（权重值） 说明 100 Thin (Hairline) 200 Extra Light (Ultra Light) 300 Light normal 400 Normal 500 Medium 600 Semi Bold (Demi Bold) bold 700 Bold 800 Extra Bold (Ultra Bold) 900 Black (Heavy) 从上表可以看出，400 对应 normal，700 对应 bold。\n浏览器在渲染字体时，会从字体中找到对应尺寸（权重级别）的矢量进行渲染。\n但不是每个字体都有 100-900 这九级矢量级别。\nmacOS 系统默认的华文黑体(STHeiti) 有七个矢量级别：Heavy/Bold/MediumP4/Regular/Thin/Light/UltraLightP2 ，它包含上面 CSS 中设定的 500 这个精度。\nWindows 系统默认的宋体(simsun) 显然没有那么多级别。在缺少级别支持的前提下，CSS 会根据扩展阅读中所述的「回退机制」寻找一个类似的精度。我相信它找到的是 400 (Normal)。\n这就导致了这个现象：虽然浏览器知道要显示的是 \u0026lt;strong\u0026gt;，但字体渲染出来是 normal ，即加粗效果显示不出来。\n扩展阅读 Mozilla: font-weight W3C: CSS3字体模块 回退机制\n如果指定的权重值不可用，则使用以下规则来确定实际呈现的权重：\n如果指定的权重值在 400和 500之间（包括400和500）： 按升序查找指定值与500之间的可用权重； 如果未找到匹配项，按降序查找小于指定值的可用权重； 如果未找到匹配项，按升序查找大于500的可用权重。 如果指定值小于400，按降序查找小于指定值的可用权重。如果未找到匹配项，按升序查找大于指定值的可用权重（先尽可能的小，再尽可能的大）。 如果指定值大于500，按升序查找大于指定值的可用权重。如果未找到匹配项，按降序查找小于指定值的可用权重（先尽可能的大，再尽可能的小）。 以上策略意味着，如果一个字体只有 normal 和 bold 两种粗细值选择，指定粗细值为 100-500 时，实际渲染时将使用 normal，指定粗细值为 501-900 时，实际渲染时将使用 bold 。\n全文完 ","date":"2022-11-24","description":"如果一个字体只有 normal 和 bold 两种粗细值选择，指定粗细值为 100-500 时，实际渲染时将使用 normal，指定粗细值为 501-900 时，实际渲染时将使用 bold。","lastmod":"2022-11-24T06:40:24Z","slug":"font-weight-500","tags":["web","font"],"title":"font-weight 设置导致在 Windows 上网页不显示加粗效果","url":"https://blog.zengrong.net/post/font-weight-500/"},{"categories":["impressions"],"content":"这是 《小群效应》读书笔记 的第三篇。\n本篇的内容主要来源于原书中第七章：三人成虎。\n《小群效应》读书笔记 列表 读书笔记：《小群效应》（上) 读书笔记：《小群效应》——关于退群，瑞幸与疯狂星期四 读书笔记：《小群效应》——曾老师失败的Python课 读书笔记：《小群效应》（下）——时间货币与社群类型 邀请码的作用 社交蒸发冷却 是垂直社群中经常出现的情况。\n一个推广中的新社群，大量新用户涌入，带来了许多基础而重复的问题，导致高阶用户最终离去。\n高阶用户希望在一个专业圈子中进行更深入的探讨。新用户也会因为没人回答问题而离开，结果导致整个社群衰退。\n在问答社区中，启用邀请码制度，获赞 100 的用户可以获得一个邀请码，没有邀请码的用户只能以游客身份浏览，无权发言和提问。严苛的邀请码发放固然屏蔽了部分基础用户，但同时也阻碍了其他优质用户加入。\n当现有社群准备好之后，就可以放开更多的邀请码，降低限制，吸引更多新人。\n曾嵘胡扯：\nGmail 和知乎早期的邀请码甚至能成为社交货币，长期一码难求。B 站的 100 道题机制，筛选出真正的二次元用户（当然可以花钱跳过就对了）。\n魔法数字 捞月狗： 话题的 25%和女性、约会相关，20%和游戏组队／约会相关，25%为游戏内容交流，剩下的30%和用户生活状态有关。「妹子，约吗？」和「兄弟，玩不？」占有搭讪第一句话的 28%，排名第三的是「老哥，这波很稳」。\n如果用户能添加 3 个以上的好友，可以维持 6~9 个月的活跃；如果好友数量增至 7 个，这些人就会变成铁杆用户；好友数量没有达到 3 个的用户会很快流失。\n物理世界中，两点间理想的距离是直线距离。社交世界中，两个人之间最理想的距离是折线距离。要影响某个人的决策，最好的方式不是冲上去直接推销，而是通过 2~3 位好友进行影响。\nGrowingIO 中的“朋友印象”：用户好友数量对点赞、发表主页印象数及阅读时长的影响。\n用户好友数 平均点赞数 人均主页印象数 阅读时长（分钟） 1～10 37.07 3.71 7.89 11～20 67.32 5.21 20.15 21～40 79.86 6 62.34 41～100 105.1 7.14 43.7 借贷宝： 借贷宝似乎恰巧提供了一个平台，方便好友之间假装“匿名”地借进钱或借出钱，以解决不好催债、不好意思说自己有钱这个问题。通过手机号搜索、通信录匹配、好友引荐三种方式建立的关系，发生借贷的可能性也比其他方式要大，分别占到所有交易的60%、15%、10%左右。好友越多，借到钱或者借出钱的概率就越大。 告诉用户有多少好友在用，比告诉用户有多少额度更加有吸引力。\n信息茧房和认同茧房 过去，当粉丝定义自己的身份时，会采用关注、点赞、转发、分享等方式。现在，粉丝定义自己身份的方式直接变成了“买买买”。\n网红经济、直播经济、社群经济、内容付费等新名词和新现象纷纷出现，究其根本，是由粉丝在“信任”和“时间货币”背景下做出的“买买买”行为所致。\n在社交网络中，关系链构建了壁垒，好友具备整个世界的特性，可以保护人们免受不相关信息的骚扰，但当人们进入几乎全部错误的关系链时，这个壁垒也会牢牢地将人们扯入“泥潭”。偶尔的劝诫根本起不了作用，人们会再次被相似的人同化——从众心理可以帮助决策，既可以带来好的影响，也可以带来坏的影响。\n对于陷入认同茧房的人们来说，不但要删除负面好友，还要抛弃整个圈子。 只有这样，正确的信息才会出现在你面前。\n曾嵘的思考：曾老师失败的Python课 2021 年 1 月我做了一个私域尝试，开设了一门教非程序员用 Python 抢茅台的课程 「曾老师的 Python 课」。课程以日课+视频的方式连载，对我个人的精力占用大约为每天 2 小时。\n一个编过中小学信息技术教材，年读书 200+ ，一直在一线写代码的美术老师，对于课程内容是很有信心的。开课前我做了详细的调查和设计：曾老师的Python课（第1课）。\n基于我自己的程序员经历，开源项目经验，丰富的一线教师经历和管理经验，加上合理的资源配置，我自认为这门课可以成功。 但课程大约坚持了 20 天就停止了。\n群里经常会有各种编程课、PS 课甚至会计课程换不同的小号进来发 1 分钱的红包往外洗人。我加了几个课程顾问后，发现他们采用的是类似于游戏公会的分包模式。线上卖课授课拉客流程已经如此成熟，任何机会流量都不会被放过。\n拉人骚扰是小问题，很容易通过技术手段解决。但持续观察群中的反馈，和几个群友深度沟通后，我停止了课程，原因如下：\n关注课程的人大部分并不是真的想学 Python，只是想凑个热闹。 即使是真心想学 Python 的人，也低估了无基础学一门编程语言的难度。 没有奖励机制，正反馈的周期过长。 消耗我的个人精力，远超过能带来的收益（影响群体和个体数量）。 上一篇 关于退群，瑞幸与疯狂星期四 中提到滴滴红包设计的六个互惠原则，我基于这六个原则分析一下 「曾老师的 Python 课」 失败的原因。\n个性化： 不相关。 参与简单： 参与非常复杂。学习编程语言需要极大的精力付出，即使不需要搭建编程环境，使用浏览器编程都是不可逾越的鸿沟。 零成本： 高成本。课程设计、教案、课件、录制、文章都需要我的大量精力付出，学习者也有需要大量的精力和时间付出。 对社群和企业利好： 正相关。但利益不足，影响力小，退出率高。 利益可分享： 学习结果不可分享。 可累积： 缺少短期正反馈，很难累积。 由以上分析可知，六个原则除了第一条不相关，第四条正相关外，其余都是负相关。这注定是一次失败的尝试。\n如果希望课程成功，应该做什么改进？\n扩大宣传面，严格筛选课程参与者。用付费筛选可能是最简单的方式。 降低学习门槛，搭建基于浏览器的代码调试平台。 缩短反馈时间，建立速胜项目。 选择容易被普通人群认可（能看懂）并能带来炫耀感的项目。 提供更易用的分享机制。 课程资料\n曾老师的Python课第1课（B站视频） 曾老师的Python课（第1课） 曾嵘胡扯的地方#曾老师的Python课 博客 Python 课合辑 《小群效应》全部读书笔记\n请继续关注读书笔记：《小群效应》 ","date":"2022-11-23","description":"物理世界中，两点间理想的距离是直线距离。社交世界中，两个人之间最理想的距离是折线距离。","lastmod":"2022-11-23T02:08:17Z","slug":"little-group-effect-3","tags":["readingnote","reading","little-group-effect"],"title":"读书笔记：《小群效应》——曾老师失败的Python课","url":"https://blog.zengrong.net/post/little-group-effect-3/"},{"categories":["impressions"],"content":"这是 《小群效应》读书笔记 的第二篇。写第一篇的时候，我觉得用（上下）就能把整本书整理完成，但写着写着就有了许多思考。\n所以我决定停止编号，写过瘾。\n本篇的内容主要来源于原书中第六章：互惠接口。\n《小群效应》读书笔记 列表 读书笔记：《小群效应》（上) 读书笔记：《小群效应》——关于退群，瑞幸与疯狂星期四 读书笔记：《小群效应》——曾老师失败的Python课 读书笔记：《小群效应》（下）——时间货币与社群类型 长尾世界和头部世界 长尾世界\n小众的，容易被忽略的商品或者信息，累积成巨大的数量和流量。\n头部世界\n极小的话题、人物，吸引了极大的量级。\n在长尾世界中，受益最大的正是平台和消费者。所有在长尾世界中的企业都在为平台贡献价值。\n在头部世界中，平台最好的用户、流量和资源，不断滋养头部区域的合作伙伴。平台在为头部的「超级个体」打工。\n长尾世界利好于平台，头部世界利好于合作伙伴。\n病毒式扩散 长尾世界为进入头部世界的尝试提供了「创新池」。用户获得的收益和预约体验促使信息的分享和扩散。企业的推广总是期待降低扩散成本。\n病毒式扩散指数=用户收益／用户支出成本\n用户收益：货币收益、愉悦感、成就感、认同感、个人形象塑造、有价值的资讯、解决特定问题。 用户成本：时间成本、理解成本、思考成本、信任成本、货币成本。 案例：红包玩法 （某产品）直投带来 72.9% 的新用户，分享带来 27% 的新用户，80% 以上的参与用户会再玩一次。红包活动获取的用户成本低至正常投放的 5%。\n滴滴和快滴的补贴让用户快速接受了打车软件，但用户习惯没有完全形成。补贴停止后，滴滴的日订单量从 530 万回落到 300 万，快滴的日订单量从 600 万降低到 360 万。\n马化腾建议借鉴游戏玩法，将滴滴固定金额补贴改为 12-20元之间随机分配，极大刺激了滴滴补贴效果。2015年初，滴滴红包每天打开的次数高达 3000 万。随机红包推出后，滴滴和快递的下载比例变成了 3:1。2015年2月14日，两家公司宣布合并。\n滴滴红包的分享去向，从朋友圈到微信群，从小群到私聊好友。分享去向的变化反映了用户在社交网络中状态的变化。开始时大家对新鲜事物好奇，随后是依靠群内关系维系，认为自己能帮助到更多好友。当红包普及后，再分享到微信群和朋友圈反而会对好友造成困扰，影响自己的形象，这时就变成了家人之间的强关系分享。\n互惠的价值 社群分类法：关系平等型和关系不平等型\n平等互惠的典型方式：我帮助你，你也帮助我。人们进入社群的目的更多是为了利益。用好互惠的价值，需要运营好「利益」的力量。\n增强自己的独一无二和不可替代性，就能吸引更多的社群主动前来合作。\n互惠原则 进化新心理学中的合作联盟规则\n增强将来交往的迹象； 引导他人的互惠行为； 坚持公平原则； 迅速对挑衅做出反应； 建立一种诚信互惠者的个人声誉。 滴滴红包设计中的接口规则\n个性化：发与不发，发给谁，金额都由用户决定。 参与简单：享受服务后发起。 零成本：企业无需配备大量人力对接。 对社群和企业利好。 利益可分享。 可累积：吸引用户持续投入时间。 利益驱动轴线 flowchart TD; 想要获取利益 --\u003e 设置互惠接口 --\u003e 分享协助好友获益 --\u003e 持续投入持续获益; 冗余驱动轴线 曾嵘的思考：红包和礼物 SAGI 创业早期推广游戏时做过红包的尝试。但我们发现红包带来的用户价值很低， 几乎所有的用户都只领红包，不玩游戏。\n微信小游戏从立项到上线！谈谈《猎头专家》的开发历程 上面滴滴红包的例子也说明：红包活动与用户利益相关，停止红包活动会降低日单量。和上面的游戏的例子不同，用户在滴滴中抢到红包带来的是产品内消费，这与游戏拉新抢到红包直接提现思路完全不同。\n一个更好的例子是当前依然火爆的红包/网赚类提现游戏，要完成提现就必须在游戏中持续活跃。红包的本质是促活，促活必须要和拉新结合起来做。\n上面 SAGI 的红包尝试就是一个反面例子，只拉新不促活，不与产品结合的红包是无效的。\n书中提到另一个关于抽奖送礼物，却被中奖者认为是骗子的经历，SAGI 在游戏活动中遇到过，我们的工作人员也是哭笑不得。\n用户当然喜欢利益，对于利益趋之若鹜。但我们始终要谨记，用户更接受用自己喜欢的方式获取利益。因此，运营者需要花更多的精力思考如何设计「喜欢的方式」。 时代变了，简单粗暴的方案行不通了。\n曾嵘的思考：关于退群，瑞幸与疯狂星期四 当社群中有价值的信息越来越少（收益降低），用户会选择通过减少支出成本的方式来平衡收益。这个行为可能是屏蔽群消息（免打扰），也可能是退群。\n综合现实体验，我发现退群的情况鲜有发生。原因无外乎两点：\n忘了退群。 潜意识里面认为这个群可能还有价值，虽然价值降低，但我投入的成本也降低了（从每天看几次变成每周看几次），若退群就完全抛弃了这部分可能的价值。 退群成功的情况也有两种：\n愤而退群（冲动型）。例如在群中吵架，或者遇到恶/坏/小人。 认为群已经没有价值。例如加入营销群领完红包就退群。 这里不得不夸一下瑞幸咖啡的群营销。\n和肯德基的私域群比起来，瑞幸的群更有价值，因为总能在里面领到大额优惠券。优惠券在瑞幸咖啡整个生态中的重要性不言而喻，和星巴克不同，大量瑞幸的用户在没有优惠券或者折扣的前提下，是不会产生购买行为的，因为瑞幸从出生起就一直伴随着优惠券和折扣。但我们不能简单把瑞幸私域营销归功于瑞幸的折扣思路。\n疯狂星期四的梗已经在互联网上玩的飞起。\n但很多年轻的朋友可能不知道，从上世纪开始，肯德基、麦当劳两家在优惠券的使用上就已经冲上巅峰。\n但在私域运营领域，肯德基的运营并不占优。我认为在肯德基私域不如瑞幸的主要原因，还是两点：\n肯德基的产品线和优惠活动过于复杂，内部大神卡、疯狂星期四、V 金折扣、咖啡卡等等折扣让人难以理解。很多折扣互斥，很多折扣无法执行。而瑞幸这边就简单粗暴很多，直接打折一看就明白（虽然也有变复杂的趋势）。 瑞幸的活动更年轻，更出圈，更匹配新一代的生活方式，比如前段时间联名的JoJo石之海，瑞幸每次出爆款新品，都是私域和新媒体的联动的一次狂欢。 没有用过纸质优惠券的可以看看这篇：我无比怀念肯德基和麦当劳的纸质优惠券。\n《小群效应》全部读书笔记\n请继续关注读书笔记：《小群效应》 ","date":"2022-11-22","description":"长尾世界利好于平台，头部世界利好于合作伙伴。","lastmod":"2022-11-22T02:23:14Z","slug":"little-group-effect-2","tags":["readingnote","reading","little-group-effect"],"title":"读书笔记：《小群效应》——关于退群，瑞幸与疯狂星期四","url":"https://blog.zengrong.net/post/little-group-effect-2/"},{"categories":["impressions"],"content":"书籍信息 书名：小群效应 作者: 徐志斌 出版社: 中信出版社 副标题: 席卷海量用户的隐性力量 出版年月: 2017-11 页数: 350 丛书: 社交红利 ISBN: 9787508681467 阅读地址： 得到电子书 《小群效应》读书笔记 列表 读书笔记：《小群效应》（上) 读书笔记：《小群效应》——关于退群，瑞幸与疯狂星期四 读书笔记：《小群效应》——曾老师失败的Python课 读书笔记：《小群效应》（下）——时间货币与社群类型 全书写作主辅线 主线 1：工具性、病毒性、长连接 主线 2：社交六大驱动力案例 辅线 1：社群优秀的三个标准 辅线 2：划分社区的方式，区分社群成员角色的方式 案例：微信读书 三种用户月登录天数占比 骨灰用户：\u0026gt;20天/月，18% 铁杆用户：10~20天/月，20% 迁都用户：\u0026lt;10天/月，62% 微信读书分享功能上线早期数据 分享至 微信群/好友 ，在当天分享人数和次数中，占比均超过85%。\n案例：全民 K 歌 分享去向 人数占比（%） 次数占比（%） 人均分享次数（次） 微信好友和群 49 54 2.13 微信朋友圈 23 20 1.67 QQ好友和群 20 20 1.91 QQ空间 7 5 1.34 新浪微博 1 1 1.38 用户更愿意分享给强关系和小圈子。 「赠一得一」活动，2.1 万名用户送出 4.6 万本书。1.8万名新用户领走 3.1 万本书。 标准与概念 社交网络的六大驱动力 荣誉驱动 利益驱动 关系驱动 事件驱动 地域驱动 兴趣驱动 优秀社群的三个标准 成员互相认识 成员互相信赖 成员之间频繁互动 衡量社群质量的三个标准 成员之间互相结识人数 成员之间的互动频次 相互之间的信任程度 人人都想进大群，人人活跃在小群 超过 30 人可以称为大群。 希望加入 200 人以上群的比例为 42%。 26%的人喜欢 20 人以下的小群。 20%的人认为只要超过 50 人就算大群。 三近一反 相近地域 用户对 1000 米内的信息最为敏感。\n陌陌群最早采用的是多人对话的模式，最终发展出了基于地理位置的群功能，允许用户在“小区”“学校”“写字楼”三个地点创建自己的群。这三个地点能够表明用户的社会地位（或者说，用户具有相似的社会地位），而其他地点（如公园、地铁站等）则不具备社交筛选这一功能。\n用户只是需要找一个理由聚在一起，不管主题是“某某小区狼人杀”还是“宝马群”，用户基本不会只围绕设立的主题进行沟通，而是围绕地点本身以聊天和聚会的形式交流。在由社会地位相当的基础成员构成的用户群中，陌陌每月活跃的100多万个群中至少有1%会发起线下聚会。数据也显示出，在线下发起过聚会的群会维持非常高的活跃度（70%～80%）。\n相近兴趣 用户只关注和自己相关的问题。\n将社群按照“地域”“行业”“兴趣”三个属性进行分类，地域属性的社群在前100名中占了48个。其他社群中，行业属性的占29个，兴趣属性的占23个。可见兴趣、行业、地域都是强大的社群策源地。\n不同类型的问题比较：\n公共问题：“情人节怎么过”“养狗的好处和坏处”“旅游对生活有什么帮助” 客观问题：“如何看待互联网的快速发展” 私人问题：“你养狗时有什么有趣的经历” 主观问题：“你旅游时曾有过什么难忘的故事” 私人问题的浏览和回答概率最高。 主观问题容易吸引用户参与。 私人问题容易在小圈子引发讨论。 公共问题，旁观者效应。 客观问题不受欢迎。 在讨论时，应该进行引导，让参与者提出基于主观角度的私人问题。\n相近年龄 2013年，快手从GIF（图像文件格式）工具转型为短视频分享社区。这次转型让活跃用户流失了90%以上，迅速跌至1万左右。直到2014年7月，日活跃用户数才再次超过百万。到2015年1月，日活跃用户数超过千万。\n快手刚转型时，原有的成熟用户不太习惯，反而是一些年轻、爱分享的用户更愿意尝鲜，愿意拍摄和发表视频。 快手也将自己的目标人群定义为年轻、热爱分享的普通人。这个人群为快手贡献了人均日消费60分钟、每5位活跃用户中就有1人上传原创视频的数据。\n性别相反 曾嵘：男女搭配，干活不累\n更多的三近一反 相似的背景和需求：\n拥有相似的个人资料 发布相似的消息 拥有相近的财富 相同的社会地位 相似的履历 在某一游戏中拥有相似的等级和战斗力 某个时刻都在减肥 都喜欢某个明星 都喜欢看某部电视剧 都想辞职去某地旅游 都在为家里的小朋友挑选幼儿园 相互帮助却又存在冲突和协作的两方：\n甲方乙方 供需双方 竞争与合作 企业与社群 社交网络的能力 如何获得大量流量的用户 如何获得爆发性增长 如何保持爆发性力量的延续使商业模式更稳固 私域的两高一低 高活跃 高转化 低门槛 高效率的社群组织 大群的骚扰和噪声大，遴选有价值信息的成本巨大 用户逃离和沉默比例急剧上升 关系链是企业的围墙 信息如何穿透多个小群 单个用户价值 公式： 社交网络市值（market cap）除以月活跃用户数（monthly active users）。\n并不是社交网络规模越大，单个用户价值就越高。用户在该社交网络的关系强度、活跃性，也会对社交网络价值产生重要影响。\n并不是市盈率越高的社交网络，单个用户价值就越高。网聚效应会帮助平台获得更大的收入空间。\n连接者 商品的分享 来自好友和“相似”人群的信息，对购买决策影响巨大。商品的分享欲望低，互动低，但点击转化倍数高。\n爆发的核心是连接者 46.25%的参与量是由0.8%的人产生的。\n普通人引发“蝴蝶效应”的背后是一个个“连接者”。\n实操：发现连接者 寻找 500~5000 真实用户加为好友。 阅读用户的朋友圈或者微博，留意以下细节： 关注的账号和 KOL 被关注的情况 经常讨论的话题 曾经分享的链接 链接的来源账号 链接和信息的语言风格 信息的类型 信息的标题 发布时间段 参加的线上线下活动 活动举办的企业 查看活动的新闻稿了解活动的原因和效果 了解举办活动的企业处于什么发展阶段 制作表格整合上述信息。 人工分析，辅助大数据。 过去大家总习惯性地认为，珠宝行业的“连接者”是具有一定经济基础的人。在用上述方法对用户进行观察分析后发现，原来扮演关键连接者角色的是那些喜欢茶艺的人。喝茶时手腕和颈部佩戴的珠宝首饰，是这个人群经常交流的话题之一，他们将信息引入了一个个谈话时的小圈子。\n面向用户 人们组建的大部分社群，都是被浪费的、迅速消亡的。\nDiscuz! 的数据：\n每日发帖 100 以上的论坛有 3 万个，占比 1.5%。 持续3天都有用户使用的论坛为15万～16万个，占比7%～8%。 活跃论坛大约有4 000多，占论坛总数的0.2%。 微信的数据：\n微信上每天会建立200万～300万个群（含3人及以上对话群），其中40%以上的群在7天内直接死掉，只有30%左右的群能够艰难地活过一个月。\n人们愿意停留在什么样的社群中？人们加入社群的目的是什么？\n由事件驱动组件的群生命周期短，多为 3-7天。 由关系驱动组件的群生命周期长，至少 1 个月。 用户加入社区的诉求 问答求助。 炫耀。在同类和懂行的人面前炫耀会有成就感。 寻找共鸣。报团取暖，通过互动甚至争吵，来表达自己的诉求。 分享知识和咨询。利他行为，积累影响力、获得荣誉。 管理和储备潜在关系、维系关系。在群中潜水也不退出，主要是为了随时找到某些关系、储备潜在的关系链，维系和某个人群间的熟悉感。 共同认可的长期目标和价值观。 社群分类法 组建目的：事件驱动型，观察项驱动型\n社群成员关系：熟人型，陌生人型\n曾嵘：微信群大多为熟人型社群。当微信成为工作工具之后，就包含了更多的陌生人。传统的 BBS 属于陌生人社群，Github/知乎/豆瓣这种也是陌生人社群。\n用户核心动：浏览型，互动型\n浏览型高度依赖优质内容，核心用户贡献内容，绝大部分用户消费。 互动型高度依赖承运之间的互动、讨论交流，互动型讨论的内容对于浏览型社群来说就是垃圾。 好社群的出发点 工具性 解决根本性问题。用户第一眼就决定加入。\nKOL：管理粉丝与变现 粉丝：获取资讯 共鸣、好奇、想学 病毒性 不但用户自己加入，还能促使用户将亲朋好友都拉入。\n同类需求，用户沟通 持续获得优质内容和服务 维系关系、表达诉求、塑造形象 长连接 吸引用户及其好友天天用，天天买。KOL 为了获取更高收入，持续邀请粉丝进入。\n社交网络基础模型 外部性：建立外部用户池 外部性依赖用户的分享实现。\n超过 10 万之后，退订率开始增加。提升到 200 万粉丝后，退订率开始保持稳定。\n整个社群输出的价值就是社群的外部性。\n用户过滤器 在大型社区中可以建立互不打扰的活跃小群。\n曾嵘：例如游戏行业有商务群、CP 群、发行群、源码买卖群等等。这些群中的人是完全不同的人，有的是浏览型社群，有的是互动性社群，其中的人互相认为对方的讨论没有价值。\n破冰的最好方式是运营情感关注点：\n声讨负心男 讨论婆媳关系 讨论夫妻感情 讨论社会热点 用户一旦开始「写」东西，长期留存和活跃度会更好。\n望远镜 每过三年，社交网络就会呈现出截然不同的用户行为习惯和与之对应的新浪潮。\n2009-2012：微博改变世界 2012-2015：围观中国 2015-2017：一睁眼到一闭眼都在看微信 曾嵘胡扯一下：\n2018-2020：在抖音上看小姐姐跳舞 2020-2023：在抖音上卖货/买货 2023-2026：每个人都在元宇宙里买房？ 案例：知识星球的四次调整 第一次调整：“小圈子，更亲密”，重点解决的问题是微信群中的优质信息如何沉淀。 第二次调整：“移动协作利器”，重点解决团队成员的移动协作需求。 第三次调整：“开心工作，安心分享”，重点解决小团队分享，让全体成员聚焦社群创建者、组织者这一需求。 第四次调整：“连接一千位铁杆粉丝”，重点解决KOL的粉丝管理、付费渠道的需求。 前两次定位完全错误，当时迭代修改的大部分功能都被抛弃了。直到第四次调整时，运营团队增加了付费功能，KOL可以在这里管理自己的粉丝并获得收入。从第四次调整开始，每一个开通知识星球的KOL都成为一个连接者。\n荣誉驱动 让用户和他们相似的人在一起，让他们互相比拼。\n我们将用户在小圈子内互相比拼、竞争以凸显自己某种形象和地位的现象，称为“小池塘里的大鱼”。“大鱼”代表用户想要塑造的形象和地位，而在一起互相比较的人群就是“小池塘”。\n小池塘中的人，要知道比较的结果，要知道谁在和他们比。小池塘是动态的，超越和被超越一直在进行。\n用户希望不断获得进步，希望能和比自己能力更强或地位更高的人在一起。\n玩家的愉悦度在能力略低于游戏所需能力时达到最高。过高的难度会提升玩家的紧张感，导致愉悦度下降。\n心理感觉难度总是大幅超越实际难度。\n比较的力量 比较方式 和别人比：能与更高级别的人沟通，并随时保证获得略低阶层的人求教。 和自己比：「精华」次数带来荣誉感。 和陌生人比较带来的愉悦感和成就感，远不如强关系和熟人、特定人群带给自己的感受。 比较范围 和全部用户比较（没有关系链或者陌生人为主） 和好友比较（关系链是天然的小池塘） 和「三近」用户比较（动态进阶） 同兴趣的更小范围好友（缩小差距） 和自己的过往比较（超神、精华数量） 曾嵘： KEEP中不同级别用户差距是巨大的，大社群和专业社群，需要适当缩小差距（创造小群中的小群）。\n荣誉驱动轴线 flowchart TD; 期望塑造形象 --\u003e 小池塘里的大鱼 --\u003e 比较; 比较 --\u003e 自动进阶机制 --\u003e 黏着和转化 --\u003e 明确商业模式; 冗余驱动轴线 荣誉集体的作用 人们之所以如此投入，是希望自己心仪的球队获胜，或者追随的领袖拥有更高的社会地位。社群中人们的投入是为了推动团队获胜。\n曾嵘： 为什么人们不求个人利益也要追求集体荣誉感？这有可能和人类几百万年的生存方式相关。几万年前，离开部落就是死路一条，进入一个强大的集体就意味着总是有食物。\n现代社会也类似。追求「大公司」、「稳定的汇报」、「确定性的涨薪」几乎就是人类基因中唯一正确的选择。「创业」本身就是反人性的。\n所以当公司变大、活得足够长，就一定会「国企化」，一定会让员工追求稳定性，因为这是绝大多数的选择。\n实操：动态小池塘技巧 设定一个小池塘； 让用户比较，告诉他比较大结果； 让用户知道有人在和他比较； 避免与能力过高或过低的人比较。 用户乐意做小池塘里的大鱼，不愿意做大池塘里的小鱼。\n曾嵘：鸡头凤尾原则。\n请继续关注读书笔记：《小群效应》 ","date":"2022-11-13","description":"人们组建的大部分社群，都是被浪费的、迅速消亡的。","lastmod":"2022-11-13T11:40:03Z","slug":"little-group-effect-1","tags":["readingnote","reading","little-group-effect"],"title":"读书笔记：《小群效应》（上）","url":"https://blog.zengrong.net/post/little-group-effect-1/"},{"categories":["impressions"],"content":" 题图：万科未来主场\n工程师的效率，经常是老板们讨论的重点。\nNetflix 的黑斯廷斯说：\n“多年以来，我发现最好的程序员创造的价值并非一般程序员的10倍。他或她增加的效益大约是普通人的100倍。”\n比尔盖茨也曾经说过：\n“一位出色的车床操作员，薪酬可以达到普通车床操作员的数倍；但 一位出色软件开发者的价值，则可以达到普通软件开发者的 10000 倍。”\n注意：上述引言并未经过验证。仅作为情感支持，不作为论据支撑。\n十倍工程师的心态 我一直认为， **10 倍指的是最好的开发人员和最差开发人员之间的差异，而不是最好的开发人员和一般开发人员的差异。 **\n直到我看到这篇文章：\n我放弃了年薪200万的岗位，因为“复制粘贴”的技术活让人厌恶\nMichael 的心态值得我们分析，我会思考下面几点：\n为什么一开始认为柰飞是自己的终生公司，后面又要求公司主动开除自己给遣散费？ 奈飞这样透明文化的公司为什么转岗这么难？ 转岗问题是员工自己的问题还是公司的问题？ 工程化在大公司是效率最高的方式么？当于人才发展意愿冲突时应该怎么做？ 「十倍工程师」如何教会「初级工程师」？ 稍微关注一下，就能发现在互联网上充斥着关于十倍工程师的讨论。例如这篇：\n“10 倍工程师”，以一当十的程序员真的存在吗？\n关于十倍工程师，比较详细的概念解释在这里： Individual Productivity Variation in Software Development)\n作为一个沦为程序员的美术老师，我认同文中的这个观点： 10 倍只针对完成实际任务的开发人员，它不能体现一个工程师对一个组织的价值。\nMichael 提到了初级开发人员的三个问题：\n对工具钻研不精。 不愿意寻求帮助。 提供不了商业价值。 我在第二次创业的这几年，亲身经历过多次关于上面三个问题的实例。\n对于声称自己「愿意学习」的初级技术人员，我会付出许多精力去培训，但最终均以失败告终。 总结下来，这些初级技术人员主要表现为：\n不看文档，不做设计，上来就敲代码。 想得太多，做的太少。 对于我提供的资料和文档，永远看不完，或者根本不看。 畏难。 不主动反馈，不主动寻求帮助。 用百度搜索技术问题。 不熟悉正在使用的工具、平台、框架，而且没有主动钻研的意愿。 不求甚解，或者钻牛角尖。 回想起来，我第一次创业的时候，对于「提供不了商业价值」这点的思考并不多。\n正如 Michael 文中的例子一样，做一个网站，老手用 1 天，新手用两周。这的确是 10 倍差距。\n一个从未做过网站的熟练的程序员，我相信他能在两天内完成一个 Michael 提到的网站。这是方法论层面的较量。软件工程比的是工程能力，工程能力是解决问题的能力。而解决问题的能力，是可以跨行业迁移的。\n很幸运，我又看到这篇文章，或许能解释 Michael 的困惑，为什么在文化如此透明的奈飞，转岗如此难？\n有一种尴尬，叫大厂中层\n中层保护伞 Michael 提到自己是如何做出主动努力的：\n每次申请新职务，我都会重申自己总结出的项目管理优先事项：客户服务、开发者生产力、工作室、合作伙伴关系与通知。我希望能在自己的团队中设置新的职能角色，专门管理不断增长的基础设施。我还提议其他项目经理把更多工作委托给我，腾出更多时间发展自己的组织。但这些提议最终都没有下文。\n为什么会没有下文呢？因为公司有自己的运作方式，业务有固有的流程，每个位置上的人都有自己的利益。Michael 的努力，就是动了别人的蛋糕。\n一部运转良好的机器，不需要巨大的变革。\n奈飞已经不是一个创业公司了，别人的蛋糕也是不能随便动的。\n在大厂，中层充当的是保护伞、润滑油和翻译器。当没有业务需要保护（裁员）,伞会被收起来。\n小厂也有保护伞。小厂的中层都是要干活的，在具体干活的时候，强大的执行力（十倍）和永远完不成的任务，让中层同学更愿意自己干而不是把事情交给自己的下属干，避免干得不行自己回来擦屁股浪费时间。\n这就是小厂保护伞。 这种保护伞限制了基层员工的成长，限制了他们成为新的中层。从短期看似是提升了生产力，从长期看是扼杀了公司的发展力。\n小厂最大的保护伞，就是 CEO/创始人自己。\n掌握了信息，就掌握了权力。 CEO 是创业公司所有信息的汇总者，很容易因此就认为自己无所不能。当本属于别人的事情搞不定的时候，自己冲上去堵枪眼，享受这种解决问题的感觉，消耗自己本就不多的精力和时间，这是愚蠢和不负责任的行为。\nSAGI 的中层，是比对 CEO 的职责做要求的。中层必须背负足够的压力和责任。在信息全面的前提下，若没有能力或胆量做全局决策，不能推动创新，管不住自己的下属，就没有中层的价值，应该被换掉。\n这种要求，对于 CEO 是极大的挑战，我称之为十倍 CEO。\n十倍 CEO 有个持续了 10 年以上的 CEO基因组计划， 评估了所有主要的行业，从财富 100 强到千万美金的企业。覆盖了17000 多名企业高管和 2000 名 CEO。这个研究发现，我们日常对高效能 CEO 的认知有很多是错误的：\n想成为 CEO，要在早期有高管的视野。事实：有 70% 优秀的 CEO 在职业生涯早期没有啥过高的规划。 CEO必须是名校毕业。 事实：只有 7% 的 CEO 毕业于藤校，有 8% 的 CEO 甚至大学没毕业。 CEO 必须有完美的履历。 事实：超过 45%的 CEO 在职业生涯中有过重大失误。 源自： The CEO Next Door: The 4 Behaviors That Transform Ordinary People into World-Class Leaders\nThose who become chief executives set their sights on the C-suite at an early age. In fact, over 70 percent of the CEOs didn’t have designs on the corner office until later in their careers.\nYou must graduate from an elite college. Only 7 percent of CEOs in the data set are Ivy League graduates—and 8 percent didn’t graduate from college at all.\nTo become a CEO you need a flawless résumé. The reality: 45 percent of CEO candidates had at least one major career blowup.\n每个人都可以成为 CEO，每个人都是自己职业生涯和生活上的 CEO。\n那么，要将中层锻炼成为十倍 CEO，需要怎么做？\n结合「CEO 基因组计划」的研究结论，以及我自己的阅读和实践，我将方法整理如下：\n1. 果断、坚定、快速判断 老话说「聪明反被聪明误」。聪明人往往考虑因素过多，瞻前顾后。聪明人的决定可能更正确，但贻误时机就是最大的罪过。\n十倍 CEO 应该尽可能快的，非常坚定地做出决策。\n把做出 70% 把握决定的机会留给中层，把做出 90% 把握决定的机会留给执行层。\nCEO 应该去做 60% 甚至只有 50% 把握的决策。\n2. 持续可靠 这个「持续」意味着不仅是某件事上可靠，也不仅是某一次应对可靠。可靠代表着时时可靠，事事可靠。\n可靠意味着十倍 CEO 提供的预期是合理的，可完成的。\n可靠还意味着十倍 CEO 的表现是可以达到或者超出预期的。\n3. 高度适应性 普通人认为失败的事情，十倍 CEO 应该将其当作公司学习成长的机会。\n当市场快速变化的时候，十倍 CEO 应该积极引领公司做出快速改变。\n十倍 CEO 应该把 50% 的时间用在考虑长远发展的战略问题上。\n4. 争取各方面支持，不回避冲突 十倍 CEO 需要员工的支持，但不需要所有的员工都喜欢你。\n不应该害怕公司里有冲突，应该积极应对冲突，充分听取相关人员的意见，并重视这些意见。\n十倍 CEO 要善于沟通。沟通就是说和听。领导者需要持续激励别人，必须要练好说和听。耐心倾听是一种重要的激励。\n要与公司中的人取得共识。 取得共识是为了执行决定，但不是必须有共识才能做决定。\n公司不需要「民主决策」。\n5. 保持良好的身体和精神状态 十倍 CEO，在身体和精神状态上，要做到以下几点：\n进行持续的身体锻炼和饮食、精力控制； 能连续工作较长时间； 有较强适应能力，可以迅速转换时差、环境甚至饮食习惯； 无论顺境逆境，都能以积极心态，从容面对。 6. 做好园丁的工作 十倍 CEO 应该坚持不懈地提升团队。 要把同员工的每一次会面，都作为评估、指导、帮助他们树立自信心的机会。\n做好下面三件事，CEO 就变成了好园丁，一手提洒水壶，一手提肥料桶，偶尔除除草，多数时候只需要浇水施肥、细心呵护。\n识人之能，用人之长。做好评估，让合适的人干合适的事，支持提拔出色的，调开那些不适合的。 授权不是放权，无为而治是懒惰的借口。必须提供指导，给方向、给支持，并及时批评，及时纠偏。 树立员工的自信心，激励员工向前冲，关心、赏识他们，鼓励他们敢于尝试、敢于创新、敢于冒险。 zrong's blog 中关于领导力的文章 领导力就是承担责任 推向绝望之谷 杰出并非不可取代 自主思考是对员工的过分要求 去上游 聪明的中层管理者 更多曾嵘的领导力文章： MANAGEMENT。\n参考 我放弃了年薪200万的岗位，因为“复制粘贴”的技术活让人厌恶 “10 倍工程师”，以一当十的程序员真的存在吗？ CEO基因组计划 有一种尴尬，叫大厂中层 Individual Productivity Variation in Software Development) The CEO Next Door: The 4 Behaviors That Transform Ordinary People into World-Class Leaders 宁向东的管理学课 刘澜：领导力 50 讲 何刚财经大课-《赢》2：什么人可以带你赢？ 万维钢精英日课 全文完 ","date":"2022-11-03","description":"SAGI 的中层，是比对 CEO 的职责做要求的。中层必须背负足够的压力和责任。在信息全面的前提下，若没有能力或胆量做全局决策，不能推动创新，管不住自己的下属，就没有中层的价值，应该被换掉。.","lastmod":"2022-11-03T09:20:45Z","slug":"decuple-ceo","tags":["sagiteam","management"],"title":"十倍工程师和十倍CEO","url":"https://blog.zengrong.net/post/decuple-ceo/"},{"categories":["technology"],"content":"PYAPE [paɪp] = a Application Programming Environment of Python.\nPyape 是我在开发 Flask 应用程序过程中积累的一个开发框架。准确的说，这不算一个框架，而是一组集合。 我将开发 Web 以及 API 应用程序过程中积累的一些好用的工具和常用功能进行了简单的封装，整合在一起， 方便快速启动一个新项目。\nPYAPE 的完整文档： https://pyape.rtfd.io/\nPYAPE 的 github 主页： https://github.com/zrong/pyape\nPYAPE 我的博客上的页面： https://blog.zengrong.net/pyape/\nPyape 的特点如下：\n集成命令行 通过对 Fabric 的集成，使用统一的命令行工具来实现如下功能：\n生成配置文件 将程序部署到远程服务器 控制远程服务器的运行 详细说明请阅读：命令行。\n多开发环境支持 可配置多套开发环境，方便同时支持本地开发、局域网开发、互联网测试和正式服部署。\n在开发环境配置中提供的配置，将被 合并 进入默认的配置。 合并规则如下：\n开发环境配置会 覆盖 默认配置中的同名参数。 开发环境中的新配置，会 增加 到默认配置中。 若希望在开发环境中 删除 某个默认配置，可以将开发环境中的同名变量设置为空值。 详细说明请阅读： 多开发环境支持\n模版支持与配置合并 pyape 的命令行工具支持多级配置合并，方便在多个配置中共用数据，不必重复输入配置。\npyape 允许自定义配置生成模版。\n环境变量替换支持 pyape 的配置文件模版机制支持从环境变量中获取实际值，这样可以避免将敏感信息写入配置文件提交到 CVS 造成安全隐患。\n详细说明请阅读：替换变量\nSQLAlchemy 支持 Pyape 集成了 SQLAlchemy 支持。与 Flask-SQLAlchemy 不同，Pyape 直接使用标准的 SQLAlchemy 语法。\n这更加方便升级到未来的 SQLAlchemy 2.0 版本。\nUse Flask and SQLalchemy, not Flask-SQLAlchemy\n这篇文章的观点，我也是赞同的。\nRedis 支持 基于 flask-redis 修改，使其支持多个 Redis 数据库。\nLogging 集成 支持 ZeroMQHandler、RedisHandler，提供 get_logger 和 get_logging_handler 方便从配置中直接生成 Logger 和 Handler 对象。\n关于 logging，我在博客上讨论过多次，详见： TAG: logging。\n详情参见 pyape.logging 包。\n与 PYAPE 相关 下面这些文章与开发 PYAPE 相关。有些是灵感，有些是实现。\nTAG: flask TAG: uwsgi TAG: sqlalchemy TAG: server TAG: python 部署Flask + uWSGI + Nginx 在 Python+uWSGI 应用中使用缓存 uWSGI+rsyslog 实现 rotating logging Flask+uWSGI 的 Logging 支持 Flask+uWSGI Logging rotate：重要补充 pyzog：uWSGI logging rotate 的终极方案 uWSGI 的 log 参数详解 全文完 ","date":"2022-10-02","description":"a Application Programming Environment of Python.","lastmod":"2022-10-02T07:56:39Z","slug":"pyape","tags":["python","flask","sqlalchemy"],"title":"PYAPE","url":"https://blog.zengrong.net/pyape/"},{"categories":["impressions"],"content":"心动这一年\n复盘字节游戏：氪了几百亿元，没算出人性\n这两篇文章，放在一起看，还蛮有意思的。\n心动证明了算法基建带来的变化比编辑推荐更有效率(保留了编辑权重)，在字节游戏似乎又发现算法对于游戏研发和发行并没有那么大的影响力。\n游戏研发的世界不是可以信奉「大力出奇迹」的地方。要大力，得准备好团队和弹药。心动在经济下行时也忍不住砍掉了百人研发的《萃星》。\n正如字节游戏前期打法，挖人建团队挖不来一整套流水线。但心动上百人研发的《萃星》在停止开发后，价值最大的遗产，只是核心成员的认知。\n在大力出奇迹的口号下，绿洲工作室出现「人池」这种现象也就不奇怪了。江南工作室立项的3个项目，有两个制作人只做过策划而没有管理过完整项目。\n字节至少证明了， 大中台小前台的模式，并不适合游戏研发。 (SAGI在与字节合作时也踩过这个坑)\n所以字节游戏改用收购公司的方法来完善游戏业务。\n被高薪聚集起来的人很有热情 (不一定抱有对游戏的热情） ，这就很难做好游戏。\n不理解游戏行业的人，低估了「长期有耐心」的长期含义，也高估了自己的金钱和耐心。\n游戏行业的研发经验和普遍认知，研发管线和人才积累，立项模式和团队基因，不是两三年就能建立起来的。\n做游戏，要坚持长期主义，要回归初心。\n全文完 ","date":"2022-09-27","description":"坚持长期主义，回归初心","lastmod":"2022-09-27T09:31:40Z","slug":"essay-ong-termism","tags":["game"],"title":"随笔：坚持长期主义，回归初心","url":"https://blog.zengrong.net/post/essay-ong-termism/"},{"categories":["impressions"],"content":"所有我写的「方法论」文章，都是标题党！\n那为什么我还要起这样的名字呢？因为有流量啊！能把读者骗进来啊！\n产品立项没有万无一失的方法论，但有具体的避免失败，或者说提前止损的方法。\n大多数人都是普通人，而普通人犯的错误都差不多。\n游戏创业者也是普通人，不不不，无论你多么热爱游戏，无论你玩过多少游戏，无论你怎样对当前的游戏行业现状嗤之以鼻，期望以一己之力改变游戏行业，你都是个普通人。\n天才都要讲概率，普通人也要讲概率。\n大的成功是非常稀少的。无论天才还是普通人，无论做过什么、多么聪明、资源多么好，做成一件事的成功概率就是很低的。不信的话，你去微博看看王思聪就知道了。\n所以一旦我们排除了那些最主要的错误，成功的概率就会一下提高很多。\n（嗯，提高 1‰ 吧……注意，是 ‰）\nGood Judgment Comes with Experience, But Experience Comes from Bad Judgment.\n上周羊了个羊爆火的时候，一堆文章宣称「找到了休闲游戏的爆款方法论」。我是不屑于和这样蹭热度的文章为伍的。找到方法论了你干嘛不去做个爆款出来啊，靠写小作文能吃饱饭么？\n所以，虽然我前天写了一篇 羊了个羊：我没有，不是我，你别瞎说啊 来蹭流量（文中没有废话，欢迎阅读），但我绝对不会写「XX 方法论」这样的文章。话题太沉重了。\n就从失败讲起吧。\n基于热情和无知的失败 2018 年 2 月，微信小游戏平台尚未发布。SAGI 立项了一款小游戏产品「天才射手」（曾用名猎头专家）。一群硬核游戏爱好者，做一款休闲手游，借用投资圈盛行的一句话，在我们看来就是「高维打低维」。我们提出一个口号是「做被 AppStore 推荐的小游戏」。\n这里是 产品研发日志记录 。\n现实狠狠打脸。无论我们做了多少个角色，每个角色有多少个不同算的动作。也不管我们加入了终结技、必杀技、针对每个角色的特点设计的技能展示和系统，产品就是：不……赚……钱。\n是的，留存和用户时长都挺不错的，但就是不赚钱。\n你只能赚你认知范围内的钱。\n我在 猎头专家产品立项 中的描述展现了我当时的无知和愚蠢：\n与现在充斥微信小游戏平台上的大部分小游戏不同，在《猎头专家》中，我们瞄准的主要玩法是在线对战。这是猎头专家第一个版本就包含的功能，也是游戏最核心，最主要的功能。这让《猎头专家》的开发过程变得不那么简单。玩家习惯和在线人数都会影响到对战游戏的表现。射手座团队认为，和真实的玩家对战，可以促进玩家真正主动地分享游戏。这是《猎头专家》用户增长的个重要途径之一。\n四年后再来看，这段话是对的。只是不适合那个时候的市场。\n2018 年的微信小游戏平台自身，并没有想清楚如何从商业化的角度驱动游戏。平台一直在观察平台内开发者的玩法。微信自己也推出了「跳一跳」这种官方产品下场，用来指导开发者，以及演示功能。但跳一跳也误导了不少开发者。大家以为做出这样质量的小游戏就可以爆量了。等下场后才知道，这种游戏只能自娱自乐。\n尽管许多文章吹爆「跳一跳」，但我从不认为「跳一跳」是个爆款。它只是一个承载了微信推广小游戏任务的落地页，它的量级是由于微信的宣传以及官方盖戳的「第一款」小游戏名头，还有张小龙不遗余力的演示。\n2018 年真正的爆款是「海盗来了」，是一个沉淀了很久的 H5 游戏。没错，最近爆火的 羊了个羊 的制作人也曾是海盗来了的制作人之一。\nP.S. 关于海盗来了，我在 我理解的休闲游戏（下）——超休闲和小游戏教给 SAGI 的知识 中做过介绍。\n在平台初创，规则还不明确的时候（尤其是微信这么大流量的平台）是有红利的，但这些红利的方向并不是由官方给出的。甚至相反，跟着官方的要求走，一定会吃大亏的。\n下面的引用描述了微信小游戏在 2018 年 4 月发布后，为了整治流量乱象，发布的一系列官方政策。引用的内容来自于我在 2018 年 10 月写的一篇文章： 微信小游戏：风口还是泡沫，\n小游戏正式发布一个多月后的 5 月 17 日，为了解决愈演愈烈的诱导分享问题，微信对分享功能进行了调整，取消获取分享成功消息，和获取群 ID 的功能。不过这个功能直到 10 月 10 日才会被完全禁止掉，而且不会影响这之前已经发布的小游戏。\n接着，微信对授权接口进行了限制，开发者需要使用组件方式唤起登录授权弹窗，这意味着必须让玩家交互一次才允许获得登录授权。\n8 月 17 日，微信发布了 《致小游戏开发者的一封信》，强调小游戏的本质是创意，确定小游戏平台的目标，是成为一个汇聚创意并且让创意体现价值的平台。\n接下来，小游戏增强了投诉功能，公布了 《小游戏发布内容规范》，接连发布了两辑《小游戏“免坑手册”》，封禁了一批踩线的小游戏，下架了一批涉嫌抄袭的小游戏。\n就在国庆长假前一天，微信发布了 《“小程序跳转小程序”功能调整》，限制跳转功能必须由用户触发，并要求在小程序发布时设置跳转白名单，白名单的上限为 10 个，且不允许动态更新白名单。这是在将大刀挥向小游戏盒子们了。\n微信的这些措施当然是有效的。现在微信群里分享小游戏卡片的情况已经越来越少。这些措施增强了微信用户的体验，却无疑让小游戏的开发者们过得愈发艰难了。\n不幸的是，SAGI 当时严格执行了微信小游戏的官方政策。\n市场并不会因为热情就多给你资源。市场永远是无情的，市场抛弃你的时候，都不会多看你一眼。\n基于错配方法论的失败 超休闲游戏测试 尝试做海外超休闲游戏的时候，我们掌握了超休闲游戏的测试方法：\nDEMO 阶段 2 天，使用视频 + ICON 进行 CTR 测试； DEMO 阶段 4 天，使用上架包进行 CPI 测试和次留测试； DEMO 阶段 7 天，ROI 回收拟合； 深入研发阶段 15 天，完善内容量。 P.S. 关于超休闲游戏分类，可以看这一篇文章： 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 。\n使用这套方法，我们用了 2.5 个人在 3 个月时间内做出了 12 个创休闲游戏 DEMO。有几个游戏达到了部分测试指标。然而，三个月的时间也消耗完了创作人员所有的激情和创意。\n我意识到这种模式更适合个人团队和松散团队，或者也可以扩大团队进行批量工厂管理以提升成功概率。SAGI 这样的团队并不适合持续采用工厂模式进行生产。\n创意点子库 SAGI 的许多员工都是资深硬核玩家，平时聊天也会聊到游戏创意。2020 年 9 月，我们开启了 全员点子库，期望用这个形式来促进员工之间的交流，也期望点子库中能迸发出让人耳目一新的点子，成为游戏立项创新的思考源泉。\n大家对点子库非常感兴趣，一周时间内就收集了几十个点子，我花了一下午时间对这些点子进行了分类评价和思考：\n1. 有亮点的休闲游戏\n此类点子在设计上有一定的亮点，有类似的竞品但差别较大，或许可以做出较大的创新。\n2. 无甚新意的休闲游戏\n此类点子竞品不少，点子中体现出来的也没有太大创新。\n3. 想法老旧\n此类点子竞品过多，或者属于烂大街的想法，如果不做出特别出彩的质量，完全没有出头的可能性。\n4. 思考比较全面\n此类点子思考比较全面，需要进一步深入讨论。\n5. 信息太少，无法判断\n此类点子给出的内容太少，或者支离破碎不完整。\n从案子的质量看，创意爆棚但思考不足者有之，心有余而力不足者有之，陷在自己的桎梏中无法自拔者有之。\n我们要把「创意」分为两个部分： 一是能够维持商业游戏模式运转的创意，一是能对产品进行创新方向推进的创意。 当前的所有创意，都应该是围绕商业目标而非个人目标运转的。\n有同事在某个点子下面评价道：「基于只想让自己爽的歪歪产物」。\n第 5 类点子表现出提供者的强烈的个人喜好，关注点和世界观，描述了大量产品细节，但没有进行同类可行性分析，缺少一根思考主线，始终无法逃开「我就是要做一个最牛逼的游戏！」的内心呐喊。\n当然，要求非策划岗位的同学提供数据分析和商业化思考，并不符合创建全员点子库的初衷。\n一些点子能体现出员工的个人喜好。但他们对「休闲」没有足够的理解。过于老旧的思路在当前这个市场上不会表现出价值，投入无法在短期内做出比大量竞品都好的质量。休闲游戏的点子创意在于推陈出新，找出有亮点创意快速实现，或者在已有的创意上做出新的亮点。\n让全员理解「休闲游戏」的正确的打开方式，任重而道远。\n这次尝试是以失败告终的，我进一步认识到，选品是商业盈利的关键点，将其放在灵光一闪、天马行空、缺少深度思考的创意之上是危险的。\n点子库应该作为打开思路的一把钥匙，或者作为思维火花催化剂。应该对点子中有趣的部分进行嫁接，产生出可以进行商业化操作的新点子。\n基于项目管理的失败 掌握了测试流程之后，SAGI 制定了一套基于自己测试的立项流程，配合研发和选品方向，命名为「SAGI 模式」。\n这套模式中最重要的是「自研」这个分支中的策划案和多轮测试。\n有几款产品按这个方式进行了孵化，严格走完测试流程后，进行了半年以上的开发，结果并不好。\n这其中既有项目管理的问题，也有当数据变化之后的方向调整问题。综合来说，都是人的问题，是人没有想清楚方向，最终导致了项目管理上的错乱。\n一个产品在立项前期得到的 CTR 和 CPI 数据都相当漂亮，在产品研发走入深水区之后，数据发生变化，产品无法根据数据的变化来判定产品的调整方向，决策反复横跳，不断增加的新模块导致项目流程不堪重负。\n另一个产品在测试中得到 40+ 的次留，但对于提升三日和七日留存找不到更好的方案，项目组成员机械工作，为了完成任务而持续开发。\n合格的制作人，必须对项目的方向、产品的意识、设计的细节有足够深入的理解，对程序、美术能提出建设性的指导意见，要拥有足够的团队掌控力。如果不合格，就不能带项目。\n休闲游戏立项思考 那么，休闲游戏的立项，应该使用什么样的方法，才能提升产品的成功概率呢？\n2022 年 9 月 21 日，我参加了 Adjust、TracePlus、广大大组织的主题为 「游戏出海如何破局--休闹游戏的全球化增长与变现」 会议的圆桌讨论部分。\n会议参与者多在休闲游戏行业，也有重度游戏发行参与。基于会上讨论和会下交流，以及我这些年的思考，我观察到，这两年间休闲游戏主要面临下面两个现状：\n超休闲、轻度休闲游戏尝试中度化和内购向。 重度游戏尝试利用轻度和休闲玩法来减低买量成本。 P.S. 我的文章 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 对超休闲、轻中重度游戏做过定义，可参考。\n重度游戏尝试休闲玩法，主要有两个方向。一是在买量素材上做文章，使用轻度玩法的视频素材吸引休闲玩家或者普通用户进入重度游戏，再进行转化。二是直接将休闲玩法整合进入重度游戏中，让轻度玩法的产出进入（或粗暴结合）重度游戏的数值体系。\n也有重度游戏发行或者研发直接下场做休闲游戏，但成功案例极少。\n轻度游戏中度化，是 SAGI 一直在践行的道路。从 2019 年立项第一个中度游戏开始，我们已经走过了 3 年。\nSAGI 经过上面谈到的那些失败的尝试，也一直在思考：在休闲游戏立项这件事上，还能如何提升成功概率呢？\n下面两种方法或许可以试试。\n找到一个大厂看不上，小厂做不了的垂直品类，在品类中尚无头部产品的前提下，迅速、大量推出产品成为这个品类的头部，让这个品类成为你专有的品类。这有点类似于「包场式」买量打法，占住品类后能享受更多的流量红利。 基于一个成功产品，固化产品的商业线、成长线和体验线，同步不断找到更吸量（CPI 更低）的核心玩法来降低研发成本，提升开发效率。 我基于第 2 点展开说一说。这个方法论我暂且把它命名为「换核」。\n换核方法论 抛开大厂的「全力创新」打法不谈，重度游戏「换皮」是常见的商业化思路。在换皮的过程中，最难的部分总是不变的，而最有确定性的部分是可以变化的。\n描述 重度游戏 不变 成长线、充值体验 不变 数值 变化 世界观、美术 变化目标 调整用户画像 这个表当然很粗糙，并未包含所有的因素，简化也是为了说明我想表达的含义。\n因为重度游戏的研发成本和周期，对商业化影响最大的成长线、数值是不应该被改变的。重度游戏的换皮主要集中在美术和世界观上，换皮的目的主要是为了触达更多不同类的用户。\n重度游戏已经成熟使用的「换皮」方法论能否用在休闲游戏上呢？\nHabby 给出了答案。\n上图为 Habby 的产品 弓箭传说+砰砰法师+酷喵跑酷+蛋壳特攻队 的英雄装备界面。\n蛋壳特攻队和弓箭传说，都是流水过亿的产品。但它们的天赋技能、升级方式、道具装备、商业化逻辑、充值界面、首充体验、过关奖励、技能获取（三选一）、UI 布局和 UE 体验等几乎完全一致。\n只有什么不一样？核心玩法不一样。\nHabby 的这套逻辑，本质上和重度游戏的换皮思路是一致的。既然已经在弓箭传说上验证了商业化逻辑和用户体验这套最难的部分，就将其固化不变，通过测试不同的核心玩法来获取更低的 CPI。\n问题的核心是，如何找到合适的核心玩法？\n《蛋壳特攻队》就是最近的一次成功尝试。Vampire Suvivors 的核心玩法，借用 GameLook 的说法：\n既可以算作动作类 Roguelike，也可以算作弹幕射击，甚至还能算作无双割草的游戏，对玩家而言，最大的卖点无疑还是一个“爽”字。因此，这种小体量、画风复古的产品反而更能匹配他们“让肾上腺素飙升半小时”的朴素需求。\n「换核」与「换皮」两个方法论本质没有区别，重度游戏换的是表象，休闲游戏换的是核心玩法。\n为什么重度游戏不能换核？因为成本过高，换掉核心之后就成为另一个游戏。\n为什么休闲游戏不能换皮？当然能。换皮的休闲游戏太多了，但无论从买量的角度还是从用户群体的角度来看，换皮的休闲游戏都难以成为爆款。\n描述 休闲游戏（换核） 重度游戏（换皮） 不变 变现、商业化 成长线、充值体验 不变 UI、UE、新手引导 数值 变化 核心玩法 世界观、美术 变化目标 降低CPI 调整用户画像 继续上面的简陋表格，我发现，在休闲游戏和重度游戏上，居然适用同样的方法论。\n商业逻辑从未改变，遵循相似的方法论，只不过是为了在不确定性中寻找确定性和可能性。\n失败的经验教我们怎么样避开那些明显错误；成功的经验教给我们敬畏之心。 希望本文，对你有所启发。\n引用 半年诞生多个爆款，手游月收入破亿，这个奇葩玩法还有多大潜力？ 游戏出海如何破局--休闹游戏的全球化增长与变现 全文完 ","date":"2022-09-24","description":"产品立项没有万无一失的方法论，但有具体的避免失败，或者说提前止损的方法。","lastmod":"2022-09-24T01:08:03Z","slug":"start-a-casual-game","tags":["game","sagiteam"],"title":"休闲游戏立项方法论","url":"https://blog.zengrong.net/post/start-a-casual-game/"},{"categories":["impressions"],"content":"最近微信小游戏羊了个羊火爆天际，不少朋友问我怎么看。我总觉得在游戏最火的时候写一篇文章就是为了蹭流量，就一直拖着没写。\n一看日期，博客已经一个月没更新有价值的内容了，那还是写(ceng)点东(liu)西(liang)吧。\n本文不讨论已经辟谣过的内容。\n先来个耸人听闻的结论： 目前分析羊了个羊的大部分文章，都是错的。\n抄袭玩法？ 三消麻将是个很经典的玩法，换算到游戏发展史可以说是「古已有之」，说抄袭 3 Tiles，那 3 Tiles 抄了谁呢？真没必要。\n创始人到底有没有买房 这才几天啊，修 BUG 和架设新的服务器都来不及呢，还有空去买房？\n广告的结算怎么都要等一个月啊！ 不是实时入账！不是实时入账！不是实时入账！\n服务器是故意挂的？ 拜托！这游戏可是全中国人都在玩！\n春晚抢个红包，AppStore 的服务器就挂了。2021 年春节抖音为了抢红包新增了 12 万台服务器。2022 年春节抢红包，京东花了上万名专业人员维护。\n羊了个羊只是一个小游戏，资源、人员都不足，巴不得服务器好好的啊！能多赚点钱不好么？干嘛要故意让它挂？\n地图炮？社交？游戏圈？裂变？ 这些都是小游戏行业的标准玩法，是标配。\n我在 2018 年小游戏刚刚兴起的时候写过一篇文章 微信小游戏：风口还是泡沫，可以点进去看一下（图很辣眼睛我不好意思放），其中的裂变传播图比羊了个羊要离谱多了。\n至于说地图炮的，VOODOO 2018 年的超休闲游戏就在全面使用更高级的「国别炮」了。\n既然大家都有，为啥只有羊火了呢？\n羊了个羊这种小游戏，在微信上怕不是有上万个，为啥只有羊火了呢？\n要知道真正的原因，墙裂建议读龙虾这一篇： 羊了个羊赛前赛后，我眼中的羊 。 这是目前关于羊的文章中写得最人间清醒的，龙虾对于小游戏行业和人性的理解，对开发团队的熟悉，对流量的认知，对心理学的喜爱，对羊的研究，对整个事件解读之全面，超越了 99.9999% 蹭流量自媒体（虽然他也在蹭😝）。\n想要了解更多休闲游戏和小游戏，可以阅读下面这个系列：\n我理解的休闲游戏（上）——平台如何定义休闲游戏 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 我理解的休闲游戏（下）——超休闲和小游戏教给 SAGI 的知识 星图 500W 投放预算？ 小游戏在抖音跑的是「联运」模式，产品的收益每月结算。如果要花钱投放，投的钱也是从产品的收入中扣除。所以预算那里填写 300W 还是 500W 都是可以的，你不用真的充那么多钱。 只有你的账户中赚了那么多钱，投放的时候才可能会花那么多钱。结算给达人的费用，也是有账期的。\n随便找一个小游戏（某鱼）的推广预算，是 500W 哦！\n这是另一个小游戏（某果）的推广预算，是 300W+ 哦！\n相比而言，爆火的「暗黑破坏神：不朽」的 35W 预算就很寒酸了，网易好穷！\n最后是羊了个羊手游自己的投放预算，你没有看错！只有 3W！\n手游投放需要发行方把钱打入抖音账户中才能消耗的。500W 存在抖音账户里面可没有利息的。\n有兴趣可以在抖音自己验证哦！\n心理学分析？ 读了某个心理学公众号发出来的心理学分析，对文章内容比较失望，没什么新东西。游戏本来就是心理学。可你的评论到底是咋回事啊！\n版号？ 这个问题比较敏感。不讨论了。\n结论是什么？ 羊了个羊能爆量，可能有推广的因素。但羊了个羊能够出圈，绝对是广大网友的功劳。\n每一次游戏转发，每一篇貌似有道理的分析（包括本篇），每一个蹭流量的内容都是推手。\n绝大部分内容都是在蹭羊的流量罢了。\n李易峰应该感谢羊。\n全文完 ","date":"2022-09-20","description":"李易峰应该感谢羊。","lastmod":"2022-09-20T12:04:15Z","slug":"yang","tags":["game"],"title":"羊了个羊：我没有，不是我，你别瞎说啊","url":"https://blog.zengrong.net/post/yang/"},{"categories":["tutorial"],"content":"SAGI GAMES 成立于 2017 年。经历五年时间，SAGI 从一个纯粹的休闲游戏研发团队，发展到一个集研发、运营、国内外发行为一体的游戏公司。这中间踩了许多坑，交了许多学费，也学到了许多经验。\n以下是 SAGI GAMES 的简历：\n2017-2018：H5、小游戏研发 2019-2020：小游戏发行，超休闲研发，出海 2020-2022：中度休闲游戏研发发行 分享内容如下：\nPART1 ABOUT SAGI GAMES\nSAGI简历 SAGI出海史 PART2 混合变现\n海内外混合变现误区 如何选择聚合 变现渠道优先级 避开误区的思维方式 PART3 海外运营\n什么样的产品需要运营 全面运营 运营的目标和手段 基于邮件的海外用户运营 PART4 TapTap 渠道运营\n为什么要做TapTap 如何在TapTap获量 用户运营和评分相辅相成 高频优质的活动 精细化运营的优势 PART5 用户运营：处理退款\n对接客服 充值申请退款 应对平台投诉 本次分享包含 SAGI GAMES 这五年来积累的运营和变现经验，都是一手信息哦！\n感谢AppFlyer，Twitter和数数科技的邀请！\n8 月 25 日下午，不见不散。\n演讲内容目录请看下文，报名请跳转到最后。\n全文完 ","date":"2022-08-23","description":"本次分享包含 SAGI GAMES 这五年来积累的运营和变现经验，都是一手信息哦！","lastmod":"2022-08-23T02:37:06Z","slug":"sagi-publishing-and-operation","tags":["sagiteam"],"title":"SAGI 的不传之密：休闲游戏运营及变现案例分享","url":"https://blog.zengrong.net/post/sagi-publishing-and-operation/"},{"categories":["technology"],"content":"六月下旬，我沿着北纬 30° 跑了一个来回。6 天去了 5 个城市，拜访了 14 家优秀的公司。我把拜访过程中讨论得到的灵感，以及这半年自己在管理上的思考整理出来，逐篇成文，作为思想记录。\n在这十几年的管理和创业生涯中，我不止一次地碰到技术同学和我说： 让我做什么都可以，就是别让我做管理！\n这样的思路很难有大成就。\n听我慢慢分析。\n就是不想做管理 把业务能力强的同学提拔成 Leader，这是普通公司的正常操作。此时拒绝，有两个可能性：\n第一个可能性。如果你的老板是个明白人（比如像曾老师这样的），我相信他对这个提议做过缜密的思考。他可能考虑过你的拒绝或者你的想法，但仍然选择了你，这说明在当时的情况下，你就是他最好的选择。放心，老板总会有方法让你就范的。\n如果老板生性豁达，知难而退从了你（比如像曾老师这样的），他一定会找一个替代人选（因为事情总得有人做），这个替代人选和你之间的关系就很尴尬了。\n而且，你错过了一个增加自己影响力的绝好机会。也错过了一个和这家公司一起成长，带领自己的团队获取更大成（li）就（yi）的可能性。\n在游戏行业，在拿（mei）工（you）资（kuang）的前提下，想要单打独斗做出一款挣钱的产品，靠一个人的力量是极难的。相信我，你搞不定。\n换个角度，就算你家里有矿，或者你是老板的亲戚，你也搞不定。因为现代游戏的复杂性决定了你不可能一个人搞定这么大的工程问题。你说拉一个团队起来？那谁来做管理呢？\n所以，最终你的结果大概率是——离职。\n第二个可能性。如果你的老板属于强力型的领导，你的结果直接就是——离职。\n不要认为自己是百年一遇的人才。如果真的是，你早就找到一个更好的栖身之所的了。现在这个机会，你应该抓住！\n也许你会说，不是有公司有技术专家么？总有技术需要我去深挖的！我练好了技术哪里找不到一个更高薪的工作！\n大厂有技术专家岗位，但你现在并不在大厂。如果你的目标是在小公司练手之后去大厂做技术专家，这个难度只怕比在管理岗上做出技术成就更大。有人的地方就有斗争，大厂的人事斗争更加复杂，你确定不在小公司补点儿经验值再去么？\n小公司和创业企业，每天想着的问题就是「活下来」。老板给你管理的机会，是认为你的能力能帮助公司运转得更好。因为不能成就业务的技术，是没有价值的。\n技术专家要成就业务 最好解决的问题，是技术问题。技术问题是一个单纯问题。\n最难的解决的问题，是新建一个组织，实现业务技术的一体化。这是一个棘手问题。\n1973 年，加州大学伯克利分校的两个公共政策专家，霍斯特·里特尔（Horst W.J. Rittel）和梅尔文·韦伯（Melvin M. Webber），提出一个新概念，叫“wicked problem”。我们可以翻译成「棘手问题」。里特尔和韦伯提出，如果一个问题具备下面这十个特征中的几个，就是棘手问题：\n1.这个问题没有清晰的定义。它不像高考数学题那样给你写好了条件让你证明。 2.它没有终极的答案。你永远都别想彻底解决它，它会一直存在。 3.你的解决方法不分对和错，只有好和坏，而什么是好什么是坏，只能你自己判断。 4.你采取一个什么应对措施，不会立即看到结果。你也许根本不知道你做的有没有用，也许还出现了意想不到的结果。 5.没有专门给你做试错练习的地方，你的每一个动作都会有影响，你一上来就是实操。 6.连有什么选项，都不清楚。 7.没有先例可循。前人的经验不会对你有太多帮助。 8.这个问题很可能只是一个更深的问题的症状，但是它背后不只有一个问题，盘根错节，可能根本就没有根本性根源。 9.有很多利益相关方对这个问题有自己的看法，他们想要的解决方向各自不一样。 10.如果你上手，那将来不论是什么结果，你都得负责。\n不懂业务的技术专家，无法将业务推进到成就阶段，在小公司无法发挥出最大价值。这样的专家存在于创业小公司中，是在浪费为数不多的资源，一个脑子正常的老板，不会允许这样的「专家」存在。\n不要被「技术专家」这个名头给吓到，觉得只有牛逼哄哄的公司才有？错！在你的团队里很可能就有一两个这样的人。\n例如，是不是有个程序员在下一个里程碑开干前说：我觉得可以在下一个里程碑中加上 PBR 管线的渲染支持啊！PBR采用了 Microfacet Model 材质模型，用不同大小和方向的微表面在对入射光线进行反射时产生了不同的反射效果，从而……（此处省略一万字）\n然后 TA 拍胸脯说：虽然里程碑已经确定了，但我一定能弄出来！只需要再给我一些资源……\n这种就是「技术没有成就业务」的典型表现，因为这里面有三个典型特征：\n里程碑已经确定，但要临时调整技术方案。 需要增加资源。 绝对承诺。 做技术时间久了，我知道，程序员的时间承诺不可信。 如果失败了，给你承诺的人最多拍屁股走人，而老板才是最终承担责任和收拾烂摊子的人。\n如果是懂业务的技术专家，面对在项目中应用新技术这件事会如何决策呢？\n考虑新技术对产品在业务上的帮助，判断新技术是否需要加入当前产品。 考虑里程碑和时间点，确定此时是不是加入新技术的最好时机。 考虑资源规划，确定增加的资源是否能带来业务的提升。 许多技术人「为了技术而技术」，要警惕这一点。公司大了之后，可以安排专门的人员来转化这些技术，以及给技术专家定任务，将技术转化为生产力。但创业小公司不可能有这样的专职人员，因此小公司需要技术专家理解业务。\n如果你是技术人，要时刻谨记不要成为「不懂业务的技术专家」。如果你是老板，要时刻推动你的技术专家去理解业务。\nLeader 要成就业务 公司创始人必须懂业务。只有公司创始人才知道做什么对公司有价值。除了产品架构，创始人还必须做企业架构，对业务深刻理解，能高度抽象，并推动组织不断变革。\nLeader 必须要懂业务，不但要懂业务，还必须懂隔壁的业务。在 Leader 这个层面，若出现「鸡同鸭讲」的现象，对公司的整体损害是巨大的。\n我有个做技术负责人的朋友，有一天跟我吐槽所在公司的 IT 负责人：「让他想办法提供一个 WIFI 和有线网络互通方案，结果他给我拉来一个没有方案能力的手下，说他自己不太懂技术。我怀疑他是不是老板的亲戚？我是该上报呢还是自己解决算了？」\n再说个技术上的例子。PM 和程序需要有共同的语言，才能让交付流程变得顺畅，保证成品质量。针对 CI/CD 流程，开发与测试工作的解耦，需要一整套的支持体系，如果 PM 不懂技术，就无法构建起无依赖的快速交付流，这导致研发给出的全是半成品。\n最后说个美术和程序的例子。如果美术负责人不懂 SVN、Git，没有确定做 UI 切片分层的方案，没有和程序一起制订出明确的命名规则，可能会导致美术切出来的图，程序无法使用。甚至可能会出现素材错误，直到产品正式上线才被发现的情况。如果美术掌握更先进的工具，让程序能直接使用美术拼出来的 UI 成品，就会大幅提升整个项目的开发质量。这里的很多工作是穿插的，既可以美术来做，也可以程序来做，但若有一方完全了解对方的工作流，就能针对整个工作流制订出更加优化的方案。如果双方的 Leader 互相非常了解隔壁的工作，整个项目一定能事半功倍。少吵架，多出活，开心不开心？\nTitle 没有用 有些同学很喜欢 Title，认为给了 Title 自己就达到人生巅峰。实际上 Title 真的只是一个名词而已，在创业公司没有半点用处。\n有个朋友给我讲了一个他的亲身经历。大公司要成立游戏部门，找制作人的过程非常曲折。许多人来面试都期待一个「更大的 Title」，这样即使离职也能找到一个好下家。朋友后来在招人的时候不得不祭出撒手锏：「我们虽然是大公司，但这个部门只是一个很小的部门，没钱也没人，你来不来？」\n我面试开发岗经常会问这个诱导性问题：「你最近 5 年的目标是什么？」大部分面试者的回答都是：「我要当主程。」于是我会接着问：「什么是主程呢？」然后，就没有然后了。\n当然也会有一些人回答：「我就是想好好做技术。」于是我会接着问：「你准备怎么把技术能力向下深挖呢？」「你准备怎么吧 C++学到专业十级呢？」「在技术和产品冲突的时候你会怎么选择呢？」我有一百种方法能知道候选人是只能做技术，还是可以被发掘成一个管理者。此处可参考上文：就是不想做管理。\n和开发岗类似，其它岗位的回答可能是：「我要当总监……我要当经理……我要走上管理岗位」。只要是这样回答的，我敢肯定候选人一定是没有想清楚，Title 的作用是什么。\n因为如果想清楚了 Title 的作用，以及理解了「领导需要承担责任」这个重点，TA 就一定不会做这么简单的回答。\n所以，如果想成为主程、主美、主策，或者嗯……制作人，先想想要承担多少责任。责、权、利，必须是一体的。\n压力很重要 管理大师彼得·德鲁克说，他自己一生都在追寻一个话题：\n如何让组织变得即有效率，又仁慈？\n一个不成熟的组织，不应该追求仁慈。仁慈是对组织中所有人最大的伤害。\n创业早期，我非常在意公司的整体氛围。SAGI 的文化很强，所有来 SAGI 的小伙伴都会提到公司的氛围很好，我一度认为这是管理成功的表现。\n但现在我不这么认为。\n创业公司的目标首先是活下去，然后是发展。好的企业氛围若不能帮助企业增长，就没有意义。如果因为追求「仁慈」本身而让企业失去了增长的可能，就是对整个组织中所有人的伤害。\n老话说的好，压力就是动力。没有压力，人类与生俱来的懒惰会占据主导，组织中优秀的人会主动离开。这样的「仁慈」并不是创业企业所追求的。\n有没有企业追求仁慈成功呢？我引用一段胖东来创始人于东来的话：\n我们从小就被灌输要听老师的话、听妈妈的话、听爸爸的话、听领导的话、听老板的话，从来就不问要不要听自己的话，去尊重自己，知道自己的人格。\n这样的状态导致产生了自卑的生活状态，但世界是多彩的，每个人都希望自己是个性的。\n做企业也是一样，先让员工得到尊重、温暖，再引导员工怎样去工作、生活。所以要真正的培养独立、健全的人格，成就阳光、个性的生命。\n拥有懂得尊重、健全的人格，慢慢人就变得自信了。\n但在这种合作的关系、成长的过程当中，因为人的本性而相互的认识，又因为真诚、信任、温暖而有了责任，不想让对方失望，所以我们对自己的要求就会更高，一点都不想让自己不开心！\n上文摘自： 胖东来创始人：真心成就别人，结果一定会好\n我认为胖东来的思路实践起来很难，要处理好 私心，公心，贪婪，嫉妒，分配，成长 的关系，认同这个观点的员工或许只在胖东来才能得到认同。\n行业和城市也是一个很大的区别，胖东来一直在三线城市做服务业，游戏公司要参考胖东来的思路，还要做很大的消化吸收才行。\n胖东来没有给员工压力吗？我不信。\n那这样的企业管理者和「烂好人」有什么区别呢？我谨记这三点：\n1 严格的复盘机制。 2 谨慎的惩罚机制。 3 把精力分配给值得尊重的人。\n在创业公司中，压力必须存在，而且必须足够大。不然我们创业干什么？\n如果把工作上升到「生涯」的高度来，对压力就有了另一个解释。这个以后再聊吧。\n附录：超市坟场胖东来 摘录自：《蔡钰商业参考 2》\n河南当地有一个玩笑，说以胖东来超市为圆心，方圆2公里叫「超市坟场」，意思是，只要胖东来开了店，别的超市就别想活了。它2005年刚进新乡的时候，周围本来有三个大超市，一个台湾的丹尼斯，当时是河南第一超市品牌；一个世纪联华，还有一个筹备中的沃尔玛。结果胖东来的超市开起来以后，丹尼斯关门了；世纪联华把门店卖给了胖东来；沃尔玛犹豫了6年才开业，开了4年就又关门停业了。\n公司每年的净利润有一半拿出来跟员工们分，还规定每人每周工作不能超过40个小时，加班要罚钱。老板天天催着员工按时休息，还设计了制度强迫员工休年假，必须去长途旅行。\n员工天天盯着顾客提供贴心服务，什么购物车给老人配凳子，配放大镜，冷冻食品货架配保暖手套，用色卡告诉你怎么挑香蕉，就连你在它的电玩城玩抓娃娃机，你都可以跟工作人员说最想抓哪个玩具，工作人员就会替你把柜子打开，把娃娃摆成最好抓的样子，让你来抓。在胖东来遇到了不满意的服务，打投诉电话你还可以领到500块奖金，因为胖东来认为你投诉是在帮他们改进服务。\n2016年出过一件事，胖东来在新乡的超市因为房租涨价计划撤出新乡，这事一度惊动了新乡市民和当地副市长来出面挽留。后来胖东来换了个地址再度开业，开业那天人满为患，以至于惊动了交警来维持秩序。\n全文完 ","date":"2022-07-11","description":"把业务能力强的同学提拔成 Leader，这是普通公司的正常操作。","lastmod":"2022-07-11T12:33:56Z","slug":"leadership-and-duty","tags":["management"],"title":"领导力就是承担责任","url":"https://blog.zengrong.net/post/leadership-and-duty/"},{"categories":["impressions"],"content":"回顾系列文章回顾：\n我理解的休闲游戏（上）——平台如何定义休闲游戏 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 我的休闲游戏定义：没有标准 前两篇文章一直在讨论休闲游戏的定义。讨论得越多，我越发现难以对休闲游戏做一个明确的定义。\n本节并不是为了否定中篇第五节的标题，而是为了提供另一个角度。\n从 我理解的休闲游戏（上）——平台如何定义休闲游戏 一文中的讨论可以看出，平台对于休闲游戏的定义模糊且分类混乱。\n我在 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 一文中，试图用中度、轻度、超休闲对休闲游戏进行分类，但我们也不能排除重度甚至 3A 游戏中的休闲玩法。\n昆特牌算休闲玩法吗？这或许是另一个问题了。\n打地鼠总该算休闲玩法了吧？\n从前面的讨论看来，休闲游戏并不局限于平台：主机游戏平台、页游平台、手游平台、小游戏（此处定义见 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏）平台都有大量优秀的休闲游戏存在。\n休闲游戏也不局限于游戏精神：无论是商业游戏还是独立游戏，同样拥有大量优秀（且赚钱）的休闲游戏。\n所以，休闲游戏是一种信仰？\n我觉得我又开始胡扯了。\n休闲游戏是一种玩法表现形式。游戏开发者既可以将其融入「更重」的游戏中，也可以基于这种玩法单独开发一款游戏。\n我在 我理解的休闲游戏（上）——平台如何定义休闲游戏 中提到：游戏是体验的载体。\n大型/重度游戏是更长期，更连续，更强烈的体验载体。休闲游戏 关注更轻度，更随意，更放松的短期独立体验。\n这也就解释了，为什么罕有成功的「重度休闲」，为什么超休闲游戏绝大多数采用 IAA 付费，即使海外发行不需要版号。\n因为玩家不认为这样的体验值得为之付费。\n休闲玩法经常被嵌入到大型游戏之中，冲淡玩家在强注意力长期体验下产生的困乏感，玩家「休闲」后能更好地关注主线情节。\n玩家在休闲游戏中获得了简单的愉悦和放松，但如果要玩家掏钱购买这个体验，则需要更强的刺激。\n有人评论国产造车新势力蔚小理三家，认为小鹏靠技术，理想靠产品，蔚来靠用户。蔚来的用户运营把每个蔚来车主都变成了野生代言人，不但自己消费买车，还拿出自己的时间和精力为蔚来宣传增值。\n人类追求的体验价值，蔚来实现了。\n当有人指着你的越野车（无论什么牌子）问，这个是 Jeep 吧？你就知道 Jeep 成功了。\n当人指着你的 Bloons TD 问你，这个就是保卫萝卜吧？你就是知道萝卜已经统治了小白塔防世界。\n闲扯至此，聊正题。\nSAGI GAMES 的员工以硬核玩家为主，但一直在做休闲游戏。在我们的成长过程中，超休闲游戏和小游戏（此处定义见 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏）教给我们许多知识。我们把这些经验与自己对游戏的理解融合在一起，加上全体的不懈努力，才取得了目前的一点点小成果。下面就聊一聊这些经验。\n流量、CPI 与价值 2018 年，小游戏市场方兴未艾的时候，我写过一篇 微信小游戏：风口还是泡沫，其中解释了「裂变」这个词：\n和小游戏圈子里的人聊天，不出十句话，你一定能听到这个词：“裂变”。“裂变”这个词其实是唬人的说法，说平凡点就是“用户主动分享”罢了。上面谈到的微信政策，有些就是针对无节制的诱导分享来制订的。除了政策上的限制，微信在审核这一关也对诱导分享进行了特别关照，违背微信的分享规则的产品，无法通过审核。“裂变”是越来越难做了。\n裂变的目标是“廉价的流量”，让用户能主动分享为小游戏带来新的用户，在不花或者少花买量费用的情况下实现游戏的用户增长。微信虽然限制了“诱导分享”，但并没有限制“分享”行为本身。通过对不同年龄阶段和产品目标用户属性的分析，经过精心的设计，还是能得到不错的效果的。\n具体「效果还不错」的图因为过于超纲我就不放了，可以阅读原文去看。\n当时小游戏的流量大的吓人，几百万上千万 DAU 的游戏比比皆是。2018 一年就上线了 7000 款产品，市场规模达到 60 亿。你的游戏要是只有几十万 DAU，都不好意思和人家打招呼。\n当时 SAGI 铁了心的要做感动自己的游戏，完全错过了这个流量风口。彼时我自己对流量理解不够，写下了这么一段内容：\n有人把优秀的工具和简单的玩法造成的开发时间大幅缩短作为了宣传手段。“3人小团队，3天开发，日活过百万”，仿佛一个游戏开发时间超过3天就很 low ，就拿不出手，仿佛 3 天做出一个小游戏是很值得吹捧，是超一流牛逼似的。拜托，你几个月做了 200 个小游戏，有啥用呢？数量要是能取胜，为啥市场要期待精品？\n我们团队内部把交互简单，开发快速的小游戏统称为“七天小游戏”，意味着这类游戏能用 2 个人（包括程序、美术、策划）在 1 周时间内开发测试完成并上线发布。目前微信平台上的绝大部分小游戏都是七天小游戏。\n“七天小游戏”这个词本身并没有贬义，用心做，七天小游戏也做出精品。通过研究游戏数据并发现用户痛点，持续调优，看着一款游戏的数据不断增长至爆款，需要花的时间远远不止七天。但目前大多数的微信小游戏并不是这个思路。纯单机，开了流量主之后就不再维护，粗制滥造，疯狂换皮，只注重广告不注重体验，这样的做法只会留下一堆堆小游戏垃圾。\n是的，我们仍然放下执念组建了一个「Seven Day」团队专门进行快速开发（而非换皮）的产品。用纯粹的游戏开发者心态做流量向的事情，意味着我们几乎不可能成功。\n你只能赚你认知范围内的钱。\n当时我们关注的这些「小游戏」，与 SAGI 内心想做的游戏，不是同一个物种。虽然也带有一个「游戏」的名字，但它在当时是「导流和洗流量」的工具。制造这些流量工具的人，并不是游戏行业的开发者，而是一群「流量追逐者」。就好像 1848 年美国旧金山开始的淘金热一样，黄金在哪里，流量追逐者就会去那里。\n流量追逐者不会在乎这是个什么行业，做的是工具、游戏、小说还是男士植发，追逐者只是在不断寻找流量洼地，复制已经磨练得炉火纯青的流量技巧，使用低价甚至免费的流量变现。\n追逐者给一个新的行业带来了一定的价值。微信自己都不知道小游戏/小程序这个新兴的渠道应该如何做，但追逐者知道。\n开发者们天真地认为这是一个「游戏」平台，殊不知追逐者将其定义成了「流量」平台。\n主流价值是成功者定义的。在 2018 年那段时间里，流量追逐者无疑就是成功者。\n直到「消灭病毒」的出现。\n消灭病毒是 2019 年初的爆款，它借鉴了某个超休闲游戏的玩法，是一个成熟游戏团队的作品，这样质量的作品在当时的小游戏行业中，当然是吊打七天流量小游戏。\n其实在消灭病毒之前，已经有成功的优质产品了。「海盗来了」和「损友圈」，在 2018 年都是千万级别 DAU 的产品。\n与「消灭病毒」这种「原生小游戏」不同，「海盗来了」和「损友圈」这两款产品在小游戏平台开放之前已经是很成熟的 H5 游戏（H5 定义见 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 ）了，运营了很长时间，不温不火。小游戏这个新平台的到来，似乎就是为它们度身定做的一样，老游戏迎来了新生。\n机会总是留给准备好的游戏。\n在小游戏平台中的历练带给 SAGI 非常深刻的思考。我们在 2018 年十脸懵逼地尝试小游戏研发，2019 年开除尝试超休闲。此时我们突然发现能看懂为什么超休闲的发行模式是那样的。我们也理解了为什么超休闲发行要追求低到似乎不可能完成的 CPI。因为超休闲不能像小游戏一样做「裂变」，就只能通过超低的 CPI 和更高的 CTR 来赚取更多的利润。\n流量游戏追求的是「更多的广告点击次数」和「更高的eCPM」，而游戏开发者理解的游戏追求的是「更强的情感体验」和「更流畅爽快的解压体验」。这两者是相悖的。一次在出差的火车车厢里，我亲眼看到一个「消灭病毒」的玩家为了升级，找遍了她前后左右的所有人帮忙游戏做裂变。当时我就在想，如果可以充值的话，她会毫不犹豫为一次升级付费么？\n她一定会。但悲哀的是，并不是所有人都会。一遍遍看激励视频广告，一次次打断游戏体验，对于硬核玩家来说，是不可接受的；对于休闲玩家来说，是不够舒适的；对于免费玩家来说，是完全可行的；对于流量追逐者来说，是容易计算的。\n经历了 2018 年的爆发，手 Q、OV、字节等渠道也开始了小游戏平台之旅。流量追逐者不断移动到新的有价格优势的平台。它们逐渐完成了对这个行业的改造，平台也因为流量追逐者的尝试摸清了自己的商业逻辑（尽管每个平台都不尽相同），追逐流量逐渐变得无利可图。\n2021 年的最强实名验证政策，一度让 9 月的流量暴跌 90%。当这个平台无利可图之后，流量追逐者就会离开，寻找下一个流量洼地，一如资本在全球的流动和收割。\n在这个过程中，真正的游戏开发者学到了什么呢？但愿我们没有学坏，初心不改。\n以夷攻夷，以夷款夷，师夷长技以制夷\n摘录一段美国淘金潮时期的描述：\n几乎所有的企业停止了营业，海员把船只抛弃在了圣弗朗西斯科湾，士兵离开了营房，仆人离开了主人，涌向金矿发源地，农民典押田宅，拓荒者开垦荒地，工人扔下工具，公务员离开写字台，甚至连传教士也离开了布道所。\n潮水褪去，无人裸泳，留下的都是真爱。流量追逐者可以离开，但游戏开发者必须坚守。\n然而新的流量追逐者已经跑步入场。重度游戏发行商们发现，在成熟平台上几百上千的 CPI，在这个平台上只需要几块钱。即使用户质量挺差，付费率也低，但差距是上百倍啊！而且量级还这么大！为什么不试试呢？\n这是我总结的一个毫无科学依据的简单公式：\n1(1- CPI) x 流量 = 价值 对于 SAGI 来说，学到的知识就是：\n很X很暴力，很傻很天真，这并不是一道单选题。\n如何看待 eCPM 我在 广告收入的分解 一文中比较详细地写过 eCPM。文中有公式也有范例，读起来应该是清晰的。\n本节只聊一聊 SAGI 如何「看待」eCPM。既然是「看待」，就说明这里有很多的主观因素，也说明我的说法很可能就是胡扯。\n我经常在群里看到有人晒小游戏平台的 eCPM，成千上万的都有。这毫无意义。晒 eCPM 的朋友，为什么要把新增和日活打上马赛克呢？\n我们都知道，在小量级下，eCPM 是没有意义的。多大的量级才有意义呢？套用最低最低最低的标准，小游戏怎么也得 DAU 过万吧？App 怎么也得 DAU 过千吧？\n游戏不赚钱不能怪 eCPM 低，要看是不是选错了平台，是不是这个平台的广告模式真的跑通了。我们曾经在某个非洲平台上线了产品，eCPM 长期低于 $0.1。非洲兄弟网络尚且不稳定，游戏可能还在 loading 呢，哪有流量看广告啊？就算广告看完了也不一定能点得开链接啊。\n可是如果平台没有游戏，也没有玩家去这个平台了。这就是典型的「先有鸡还是先有蛋」的问题。游戏开发者一定要认清事实，不要把有限的精力和金钱花在无限的为平台服务上去。\n考察广告平台，首先要考察填充率。广告和变现市场必须要有买方，有卖方，双方各有需求才能共同繁荣。\n一个没有数据后台可以查看，SDK 都做得不完整，用户来源复杂的平台。如果我们相信它有「初期红利」，大多数情况会被割韭菜。\nIAA 变现的游戏如何做 eCPM 调优？\n抛开整个市场的走向谈 eCPM 的提升是没有意义的。只有在「规模化的平台」上，在游戏中加入「套路」去优化 eCPM 才是有效的。\n双十一、618 eCPM 当然高。但能高几天呢？活动一过，必然是长期低位。\n我在 计算广告：在线广告是如何生效的？ 一文中提到：\nSAGI GAMES 开发的小游戏在双十一、春节期间的广告 eCPM 能瞬间（几个小时以内）爬升到日常值的数十倍，但这并不代表游戏本身受到用户欢迎。\n等假期过去，在用户质量和量级不变的前提下，eCPM 突降到正常水平之下，持续数日才会缓慢提升。\n什么是「规模化的平台」呢？\nSAGI 会碰到在同一个平台，不同的游戏，eCPM 天壤之别的情况。这个平台有高 eCPM 的产品，能说明这个平台是健康的。如果我们自己的产品 eCPM 低，就应该从产品选型和受众上找方向了。\n一个产品 eCPM 低，不要总想着在方法的层面上挽救。要调查这个品类的真实受众，有没有高 eCPM 的可能性。如果一开始基础就选错了，那还不如节省一下精力和金钱，从头再来。\n混合变现的误区 我接触过一些从重度开发转来做广告变现的朋友，发现大家对于混合变现的理解是有误区的。 下面的误区 SAGI 也经历过：\n误区一：混合变现就是我在内购游戏里面加个广告\n这种误区是最常见的。加入广告影响留存。加入广告当然也会影响付费。要加几个广告点？加在什么地方？使用什么类型的广告点？不同位置，不同类型的广告点的 eCPM 有什么区别？这都是需要考虑的。\n误区二：我可以先做一个内购版本，在上架的时候就改成广告版本\n这是不可能的。内购和广告，带给玩家的体验不同，对心流的打断形式不同，付费心理不同，价值感不同。游戏提供的是情感价值，看一次广告和充值 6 元的心理账户是不一致的。\n误区三：我想要做一个内购：广告收入5：5 的游戏\n基本上不可能做到。SAGI 有款产品，在 iOS 比例是 8:2，在 Tap 9:1，在安卓平台 7:3，在快手平台5:5。 广告的收入和 eCPM、活动、渠道、平台、买量情况息息相关，难以固定。\n有一些通用的思维方式，可以避免在实际的开发工作中陷入这些误区：\n用户是不同的。有的用户更愿意付费，有的用户更愿意看广告。有的用户只想白嫖。我们最好能在游戏中对这些用户进行分类。 流量是不同的。一部分流量中的用户很可能比另一部分流量中的用户更有价值。更深入地说，流量代表来源渠道、来源平台、以及时间周期差异。 产品是不同的。有的产品更适合做内购变现，有的产品更适合做广告变现（例如网赚就是为广告而生），还有的产品更适合做买断制。（当然，我个人当然更喜欢买断制，然而买断制游戏带来的收入规模远远低于内购） 付费架构，是一个复杂的系统。应该像设计游戏一样去设计付费架构，而且在游戏初期就将付费方式思考清晰。即使在 DEMO 版本中没有付费，你也必须清楚，你的付费点会加在哪里。 广告是游戏内货币的收入来源。内购也是游戏内货币的收入来源。广告应该作为补充来源，要让玩家能够理解到，是充值划算还是看广告划算。这是心理学范畴，要向电商学习。 赚钱是最重要的事。先学会赚钱，再学会站着赚钱。 一个结尾 啊！这真是很长的一个系列了。\n更多的内容不适合用文字的方式表达出来，还有一些内容不适合放在这个系列中。我的不少思考，在连续几年的测试中已经形成闭环。我会抽出一些时间来把这些闭环的思考整理成文。\n欢迎关注后续文章，欢迎加入「胡扯游戏」微信群，和游戏行业的朋友们天南海北地胡扯一番。\n关注公众号回复「进群」即可。\n系列文章回顾 我理解的休闲游戏（上）——平台如何定义休闲游戏 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 我理解的休闲游戏（下）——超休闲和小游戏交给 SAGI 的知识 全系列完结 ","date":"2022-06-09","description":"在我们的成长过程中，超休闲游戏和小游戏。教给我们许多知识。我们把这些经验与自己对游戏的理解融合在一起，加上全体的不懈努力，才取得了目前的一点点小成果。","lastmod":"2022-06-09T00:55:49Z","slug":"casual-game-3","tags":["game"],"title":"我理解的休闲游戏（下）——超休闲和小游戏教给 SAGI 的知识","url":"https://blog.zengrong.net/post/casual-game-3/"},{"categories":["impressions"],"content":"在上一篇 我理解的休闲游戏（上）——平台如何定义休闲游戏 中，我们站在平台的肩上讨论了休闲游戏分类学，又站在玩家的肩上讨论了休闲游戏的「体验」。\n这些讨论让定义休闲游戏变得更加复杂和难以完成。在本篇中我试图从一个简单直接的角度来聊一聊我理解的休闲游戏。\n行业中我们经常会碰到这些术语：「超休闲游戏、小游戏、H5，中度、重度、独立游戏」。我们先把这些概念梳理清楚，才可以接着聊。\n小游戏/H5 市场 SAGI GAMES 在 2018 年微信小游戏发布之前立项了第一款小游戏「天才射手」，而后一直在持续创作小游戏，已经积累了超过一亿用户。这四年来，小游戏从最初的流量换皮洗量玩法到到近期有版号的重度游戏反攻入场，整个市场已经发生了翻天覆地的变化。\n小游戏就是「比较小的游戏」？ 「小游戏」是不是 比较小的游戏 ？小游戏是否就是 H5 游戏？下面将给出我的答案。\nPS. 「天才射手」曾用名「猎头专家」，这篇是当年我写的软文，发在 Cocos 论坛：微信小游戏从立项到上线！谈谈《猎头专家》的开发历程 我在 2018 年写过一篇文章：微信小游戏：风口还是泡沫，其中很提到了小游戏市场的早期历史，也明确了小游戏的平台属性。\n如果要向上溯源，基于互联网平台的小游戏形式的爆发可以追溯到 Flash 时代（2002-2015）。由于 Flash 这个优秀工具的出现，诞生了 Armor Games 等一批优质的在线 Flash 游戏平台。就和现在不少开发者喜欢去 Steam 上搬运游戏一样，那时的游戏开发者也很喜欢去 Armor Games 上「搬运」创意。\nPS. 我在 2014 年写过一篇文章： flash动画是如何兴起和衰退的？ 介绍了这段历史，有兴趣可以参考。上图也来出自该文。 当时已经有了「Flash 小游戏」的名称，但更多的称呼就是简单粗暴的「Flash」。这个词指代了「为了休闲目的而制作的有交互功能的视频和游戏」。\n当时的对话大多是这种风格：「你昨天玩的是哪个 Flash？」\n本文并不想将「小游戏」的概念扩大到上古时代，在这个系列文章中讨论的小游戏 特指 微信发布「小程序/小游戏」平台之后，其他厂商跟进而创立出的一个全新的渠道，本文中的 「小游戏」 是一个专有概念。\n小游戏是一个渠道品类 下面这段话是我对「小游戏」的定义：\n小游戏，是位于超级 App 或平台内部（例如微信、手Q、VIVO 手机）中的游戏，大部分为流式下载，在手机桌面中没有独立的图标，能方便利用宿主 App 或平台进行传播，主要作用是帮宿主 App 或平台提升整体用户活跃。\n几个主要的小游戏平台：\n微信：小游戏平台老大哥。 手Q：曾经出现过厘米秀（早期）和QQ小游戏（当前）两个平台。大厂就是有钱，够浪费。 OPPO小游戏/VIVO轻游戏/小米快应用：集成于手机平台内部。 字节小游戏：内嵌于抖音、西瓜视频等平台。 Tiktok 小游戏：推广了一段时间，但一直不温不火，他们和字节小游戏是不同的团队。 Facebook Instant Game：量级一直不大，对国内开发者有限制。 百度/360/B站等其他小游戏平台……。 很自(keng)豪(die)地说，上面的平台 SAGI 全部都深入了解过。B站的第一个小游戏就是 SAGI 制作的。\n当然还有各种量级较小的平台，海外的居多，这些血泪史就不说了。\n需要注意的是，国内的 「摸摸鱼/233」 等盒子平台，使用的是独立包技术，本质是一个安卓系统上的 App 应用，但使用 Hacker 方法隐藏了桌面图标（SAGI 有款游戏在接入摸摸鱼时，由于 Android 更新，摸摸鱼无法隐藏游戏图标，不知道这问题现在解决没有）。Google Play Instant 主要基于自有平台的试玩体验，并未大幅推广，技术上也使用的是原生技术。我认为这几种形式在技术上并不属于小游戏范畴。但如果行业一定要从形式上把它们叫做小游戏，我也没意见。\n从上面的定义可以看出，小游戏并不是一个游戏品类，而是一个渠道品类。就好像最早的游戏渠道只有主机游戏和 PC 游戏，后面又出现了页游、手游一样。\n小游戏不是 H5 许多引入商务上来就问：你这个游戏是网游么？\n这种问题我都不知道怎么回答，挺尴尬的。拜托，网游是 PC 时代的说法。这都 2022 年了，无论是强联网还是弱联网，就算你的游戏是单机玩耍不联网的，你接入的 SDK 也总要联网的啊。是不是网游，这我怎么回答呢？总不能先开课给你普及一下网络知识吧？\n更专业一点的问法是：我观察到你这个游戏里面有对战内容，这个 PvP 是实时的么？有没有 AI 啊？\n问出这种问题的商务，才是真正尊重了游戏的商务。你尊重游戏，开发者就愿意尊重你。\n除了小游戏概念的混乱，另一个容易混淆的概念就是 H5 游戏。\n我常常碰到这样的对话：\n引入商务： 把你们的 H5 游戏给我几个呗？ 我： 我们没有 H5 啊，那些都是小游戏。 引入商务： 小游戏不就是 H5 么？\n小游戏还真不是 H5。\n下面这段话是我对「H5 游戏」的定义：\nH5游戏，是指使用 HTML5 + Javascript 技术，采用 DOM 或 Canvas 方式渲染，在手机浏览器、PC 浏览器或软件/App内嵌浏览器中直接运行的游戏形式。H5 游戏为流式下载，没有「包」的概念，通过一个 URL 链接就可以体验它。\n从上面的定义可以看出，「H5游戏」关注的是技术实现和运营方式。「小游戏」关注的是宿主渠道。\n这两者当然有一定的联系。由于使用了相同的技术开发，一个 H5 游戏稍作修改就可以在小游戏平台上架。而一个小游戏也很容易实现在网页中运行。实际上，绝大多数的小游戏在开发过程中，程序员都是在浏览器中对游戏进行调试的。\n但你不能说它们就是一种类型的游戏，因为这样——不科学，不准确，不完美。\n如果一定要混为一谈的话，小游戏和 H5 游戏倒是有一个非常相似的特点：他们的流量来源都可以很廉价，而且很大量。是的，轻松几百万 DAU 那种，手游看了沉默，端游看了流泪。关于这点下篇再说。\n小游戏/H5 和休闲游戏有什么关系？ 没什么关系。\n上面定义小游戏是渠道品类，所以小游戏中当然就可以有轻度、中度和重度，也可以拥有超休闲。\n那为什么经常有人把小游戏和休闲类混淆，直接认为小游戏就一定是休闲游戏呢？主要有下面几个原因，导致了大众认知上的小游戏成了休闲游戏的代名词：\n小游戏和 H5 技术实现上相对容易，渠道平台多，用户受众广，常常被用于作为一些轻量级玩法的测试平台，或者一些重度游戏的预热平台。 小游戏平台本身性能有限（位于宿主平台内部），限制颇多（例如平台的包体大小限制），不适合开发大型游戏，因此开发者和发行商更多选择休闲游戏品类。 休闲游戏本身不容易变现。但小游戏和 H5 平台量级价格较低，即使商业化做得较弱，（做得好的）产品也能收回研发和投放成本。 开发者更愿意在这样的平台上使用 IAA 变现，快速制作，快速起量，快速变现，快速回本。 虽然随着这几年技术的发展，已经出现将 Unity 开发的游戏转换后发布到小游戏平台上的工具，甚至 Unity 自己也下场认真开发了类似工具（中间放弃了一次），但这种形式目前限制较多，尚未成为主流。转换到小游戏平台的 Unity 产品也必须经过适当的优化，这降低了原生平台产品的游戏体验。\n技术的发展一日千里，我们可以拭目以待。\n接下来聊聊超休闲游戏。\n超休闲游戏的特点 SAGI 的超休闲开发史 SAGI GAMES 在 2020 年做过一段时间超休闲游戏。3个月时间做了 12 款，没有一款达到发行商的数据要求。有的游戏过了留存关和 CPI，但又折在了变现上。\n这些玩法还挺有意思的。比如当时正在播放诺兰的《信条》，Space Shooter 中做了「逆转时间」的设定。再比如疫情期间严重依赖快递，我们又做了 Package Master，用胶带打包快递的玩法，真的很解压啊！\n基于电影《信条》灵感的 Space Shooter\n基于疫情期间快递打包灵感的 Package Master\nPS. 为了看《信条》，我研究了一番武汉电影院对杜比和 IMAX 的支持情况： 看诺兰的《信条》，不要随便找影院！——武汉杜比影院和 IMAX 影院简选 在这三个月的时间里，我深深感觉到，超休闲是很「纯粹」的游戏方式。它在 DEMO 阶段将一个简单的玩法打磨到极致，就是要满足人类对有趣、好玩和解压的原始追求。\n超休闲的困境 超休闲游戏面对的是「非游戏玩家」，对于普通手机用户来说，不需要新手引导，快速上手，快速获得解压体验，意味着游戏的核心玩法必须非常简单，但又非常耐玩。面对「什么都见过了的」用户，策划岗位的压力可想而知。\n简单的玩法意味着极低的抄袭成本，如果游戏玩法在测试过程中曝光了，你不可能按部就班慢慢更新。你必须要比你的抄袭者更快达到目标。你面对的是全世界超休闲游戏开发者的竞争，有一两个人的小团队，也有上百人批量制作的大公司，你永远不能停下来休息。\n总结几点，超休闲游戏要实现好玩（感动自己）是容易的，要实现赚钱（感动世界）是困难的：\n不断创造新点子，这是个非常烧脑和掉发的过程，十几个游戏在三个月内完成，头发都不够用了。 快速完成，快速测试，快速放弃，以量取胜。这种方式一直在考验团队的情感底线。他们会在加班到吐的凌晨自问：我 TMD 的做了个啥？ 发行方认为不赚钱的游戏，自己发行不一定不赚钱。因为发行方追求的是「超量放大」。然而开发者发行的门槛比较高，一两个人的团队如何能把产品推到TOP1 呢？申请投放账户就够麻烦了吧。 体验好和能赚钱，是两件事，但开发者往往只看到了前者。 不断创造新的玩法，快速跟进测试，抓住玩法的时间窗口，不能休息。 很多有趣的独特的玩法是需要技术攻关的，一个能力普通的程序员无法胜任。但能力高的程序员为什么要做超休闲呢？ 超休闲的发行方式 超休闲的发行方式是比较独特的。发行依赖超低的 CPI，通过短期内饱和买量将产品冲上榜单头部，继而形成规模效应，获取一部分自然量冲抵买量成本，并通过强行弹出的插屏广告，实现对投放成本的进一步收割。\n超休闲游戏不需要运营，不考虑长线，不必花费精力维护玩家关系，不用关注单个用户的生命周期。超休闲游戏甚至不会单独考虑游戏的生命周期，因为游戏的生命周期就是投放的生命周期。一旦投放回收下滑，经过计算和尝试无法挽回，这个产品就会被放弃。\n超休闲游戏绝大多数都是单机形式的，即使游戏中有多人战斗的表现，这些对手角色也一定是 AI。因为研发团队没有时间和精力来做更加细致的网络交互，游戏的生命周期和开发周期也不允许他们这样做。\n那么，一个只有游戏机制，缺少故事设定，没有玩家交互的超休闲游戏，到底是不是一个游戏？即使这个游戏有极致的心流体验，但缺少用户交流，不包含开发者与玩家之间的情感联系，这是否违背了我们制作游戏的初衷？\n说起来有些矛盾，在超休闲游戏中，开发者为了追求极致的体验而设计的游戏机制，可以随时被插屏广告打断，完全不在意玩家在被打断的过程中产生的负面情感。一个合格的游戏是否应该这样做？是否必须这样做，才能实现商业上的成功？\n我无法给出上面问题的明确答案，让我们来关注结果。\n超休闲游戏之所以发展成现在这样，是由于上面讨论过的自身特点决定的。超休闲游戏发行追求的看似不可能达到的数据导致了爆款的诞生。一个爆款的诞生是通过全球数百个研发团队的上千个游戏测试达成的。一个很小的研发团队，不可能有这么高的产出，也无法长期嵌入到这个循环。\n超休闲持续增长 这两年业内一直在唱衰超休闲游戏，但市场表现却正好相反。\n随着新冠疫情对移动市场影响的减弱，2021年整体手游下载量较2020年略有下降。然而全球超休闲手游累计下载量同比仍提高15%，达到137亿。\n亚洲成为超休闲游戏最大市场，贡献36%下载量份额，其次为欧洲与拉丁美洲。\n以巴西、墨西哥为代表的拉美地区年度下载量增幅高达27%，成为增长速度最快的市场。此外，亚洲市场增长同样明显，其下载量主要来自印度、印尼等东南亚国家。\nSensor Tower：《2021 年超休闲游戏市场洞察》\n需要注意的是，报告中提到的超休闲游戏的增长区域。拉美地区、印尼和印度地区的 eCPM 是远低于 T1 地区的。\n给超休闲团队的建议 根据上面的讨论，想在超休闲游戏上获得成功，对于小团队（十人以内）来说，我认为有两个方向：\n保持旺盛的精力，在一年内出 100 款以上产品，可能有 1-2 款成功（$100万流水以上）。与大的超休闲发行合作，保证现金流无忧，但切忌不可做外包（没有积累）。若有一些合作发行看不上但数据不错的产品，可以尝试自发。 在有现金流养团队的前提下，在一年内慢慢打磨 10 款产品，在其中找出 2-3 款精品持续打磨。 既然都自己养团队了，为什么不做轻度或者中度休闲呢？\n轻度、中度、重度游戏 上面主要是从发行和研发的角度来分析超休闲游戏。我们也经常听到「中度休闲」和「重度游戏」这些概念。这是如何定义的呢？\n四个判断依据 我一般采用「开发周期」、「产品生命周期」、「玩家在线时长」、「LTV」等四个数据来综合判断。\n例如在 我理解的休闲游戏（上）——平台如何定义休闲游戏 中提到的《割草的 100 种方式》，我认为它应该属于「轻度休闲」而非「超休闲」，就是基于这种方式来判断的。\n首先，我认为该游戏的开发周期应该超过半年，而超休闲游戏从开发到上线，一般在三个月以内。有没有超休闲游戏开发周期超过半年的呢？当然有的。上面也提到过，超休闲游戏为了抢热点和避免抄袭，大多是「边发行边完善」，把未完成品推上 TOP1 然后再打磨是正常现象。\nSAGI 在开发超休闲产品时，第三天就会进行第一次吸量测试，一个月内就会开始商业化测试。\n经典的超休闲游戏在上线开始大推的时候，甚至连成长线和音效都没有，这是轻度和中度休闲游戏绝对不可接受的。我了解到的超休闲游戏开发超过半年还没有规模化上线测试的，基本上都发不起来。\n其次，《割草》这个游戏，产品生命周期足够长，内容足够多，第一天的内容至少也可以玩 2 小时以上。而绝大部分超休闲游戏，在上线大推（可能已经到 Top10 级别）的时候，产品的内容都只有半小时左右。仅包含一个核心玩法，玩来玩去就是那么几关。\n第三，我没有《割草》的玩家在线时长和 LTV 的数据，但从产品较为克制的广告形式（主要是激励视频）以及较为复杂的成长消耗循环来看，策划在游戏设计上是足够长线的。\n我采访了《割草的 100 种方式》的策划同学，下面是来自于他的一手信息：\n游戏的数值设计的复杂程度也不亚于一款小型的经营游戏了。玩家在游戏过程中的持续追求感的建立费了极大的心思，为了保证长留，副本和宠物的玩法再一次加深了游戏的深度。在等级和成长设计上，玩家的基本游戏时长也需要近100个小时左右，用户时长得到极大的支持。\n割草主要是以数值为驱动，有自己的成长体系和玩法深度，相比于其他超休闲类的游戏，玩家的自主行为较多。\n但为什么我认为《割草》是轻度休闲而非中度休闲呢？因为它不需要「用户联系」。\n没有「用户联系」和「玩家交互」，游戏就不可能做得更长线。是的，连「中度」都很难。中度游戏必须要考虑「人」的因素。\n看起来像轻度的重度游戏 说完了「轻度休闲」，我们来聊聊「重度游戏」。\n等等，既然你都决定做「重度游戏」了，为什么要考虑休闲品类？既然你决定投入数千万的成本，超过五十人的研发团队以及两年以上的研发周期，为什么还要做休闲呢？\n是的，我看似简单粗暴地逃避了这个问题，但行业内真的就是这么思考的。\n我们都知道，超休闲和轻度休闲买量价格低，重度氪金效果好。如果花很多钱做游戏，那么一定要做重度才有可能收回研发成本。\n等等，如果做一个披着休闲外衣的重度，是不是就可以双杀了呢？\n曾经风靡一时的「弓箭传说」就是这么个产品。依靠前期极低的CPI ，和游戏中后期的深氪，「弓箭传说」获取了和它表现形式不相称的收入。这数据看起来已经像一个「重度游戏」了。\n和经典的从 Flash 时代移植过来的塔防休闲游戏「Kindom Rush」比较，这成绩「很骄傲」。\n讲到这里，似乎已经不用解释「中度休闲」是什么了。\n我并不是在偷懒，因为本文最后我会给出一个更明确的标准定义。\n当然，现在我就是在偷懒。\n独立游戏 独立游戏当然也可以是休闲游戏。独立游戏中的「独立」，更多指的是游戏在开发过程中的「独立精神」。至于游戏做什么，并不重要。\n独立游戏大多是较小的团队，但独立游戏团队在选择游戏类型上并不会刻意避开难度大的类型，也更愿意尝试创新类型。这就是独立游戏的特点：我可以接受长周期，但我不能接受低标准。\n我自己是个程序员，我更愿意把「独立精神」与软件世界的「开源精神」放在同一个高度来讨论。很多人把「开源软件 Open Source Software」理解为「免费软件」，这是错误的。也有些人把「Free Software」翻译成「免费软件」，这也是错误的。Free 指的是自由精神，OpenSource 指的是开源精神。免费只是体现精神过程中的一个表象而已。\n所以，讨论独立游戏和休闲游戏之间的关系是没有意义的。甚至讨论独立游戏和商业游戏的关系也是没有意义的。独立游戏并不排斥商业。就好像开源软件和自由软件在实践程序员精神的过程中顺便普及了免费软件概念一样，独立游戏开发者也应该在表达自我的同时顺便把商业化（qian）做（zhuan）了。\n至于赚不赚钱，那就只是个过程。\n我的休闲游戏定义标准 讨论了这么多，回到上一篇给自己挖的坑：如何定义休闲游戏？\n我在下文确定了几个数据标准。纯粹是建立在我的个人经验上，仅供参考。\n根据上面的讨论，我们可以确定本文讨论的「小游戏」是一种渠道品类，而「独立游戏」是按团队精神划分的品类，这两个概念都不能用具体的数据标准来衡量。\n而日常讨论中的「超休闲」、「轻度」、「中度」和「重度」则较容易使用数据的方式来定义。下面是我常用的一些数据：\n单局时长 超休闲游戏的单局时长一般在一分钟以内。中度的单局时长可以在 5-30 分钟之间。\n对于重度游戏谈单局时长似乎不是很科学。因为有些重度游戏（例如卡牌）的战斗是连续性的，单局可能只有十几分钟，但游戏的节奏中必须有多个连续单局才可以获得一个完整的体验。重度游戏使用「单日在线时长」衡量会更合理。\n在线时长 休闲游戏的特点就是在玩家打开游戏时没有心理负担。因此休闲游戏单日的打开次数一定高于重度游戏。比如在地铁上你可能不会 打开《王者荣耀》，但很有可能打开《天才枪手》。\n以《天才枪手》为例，玩家的单局时长在 10-20 分钟之间，单日时长 40 分钟以上。这是我理解的「中度休闲」游戏的正常表现。\n基于玩法不同，即使是轻度休闲游戏，单日在线时长也有很可能超过中度休闲。所以我们还要考察游戏的生命周期。\n生命周期 我认为对于一个中度休闲来说，玩家的生命周期应该超过 15 天，能达到 30 天更佳。换个表述方式，30 日留存率在 5%以上，我会认为这是一个合格的中度游戏。超过 10% 就很棒啦。\n相比而言，超休闲游戏更关注次留、三留和七留。\n变现方式 超休闲游戏的变现方式大多以 IAA 为主，大多数产品只有 IAA 变现方式。即使是加入了 IAP，它们也只是用来做一下去广告之类的轻度模式，充值在整体收入中占比较低（一般低于 15%）。\n而中度游戏则应以 IAP 变现为主，充值一般要占到总体收入的五成以上。\n用户规模 超休闲游戏由于上面提到的流量价格的优势，用户规模会比中度休闲大很多。但由于长留低，玩家很难留下来。即使是推高了 DAU 也无法保持。\n而中度休闲有更多的方式能留住用户，即使是推广前期 DAU 较小，也能借助积累效应与玩家召回机制，保持不错的用户规模。\n用户规模是变现的核心。\n运营方式 中度休闲必须搭建专门的运营团队，解决与玩家沟通的问题。超休闲则不需要专门的运营团队。\n推广方式 超休闲主要以买量方式推广。因为 CPI 极低，在极短的时间内将产品推高，能够得到较好的效果。中度休闲的 CPI 可能不便宜，而且长线运营，长期买量的中度产品上不可能保持极低的 CPI。\n中度休闲产品需要更多拓展获量渠道，寻找价格更低的流量来补贴较高的买量成本。组织玩家活动、产品运营活动、线上线下联动也是获取流量的好方式。但买量不能停，稳定的买量团队是游戏推广的基础配置。\nPS. 关于买量为什么越来越贵，这篇文章可以解释：面向CP的发行知识：关于流量的理解 下一篇 我理解的休闲游戏（下）——超休闲和小游戏教给 SAGI 的知识 会谈一谈在 SAGI 的成长过程中，超休闲和小游戏如何对 SAGI 的研发和发行思路产生的影响，敬请期待。\n参考资料 Sensor Tower：《2021 年超休闲游戏市场洞察》 感谢 vino 对本文的贡献，欢迎关注公众号：龙虾游戏推荐 感谢策划郭同学对本文的贡献 系列文章回顾 我理解的休闲游戏（上）——平台如何定义休闲游戏 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 我理解的休闲游戏（下）——超休闲和小游戏交给 SAGI 的知识 继续阅读：我理解的休闲游戏（下）——超休闲和小游戏交给 SAGI 的知识 ","date":"2022-05-30","description":"行业中我们经常会碰到这些术语：「超休闲游戏、小游戏、H5，中度、重度、独立游戏」 等等，让我们先把这些概念梳理清楚。","lastmod":"2022-05-30T22:14:48Z","slug":"casual-game-2","tags":["game"],"title":"我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏","url":"https://blog.zengrong.net/post/casual-game-2/"},{"categories":["impressions"],"content":"从 2018 年起， SAGI GAMES 一直在休闲游戏领域深耕。作为一个员工以硬核玩家为主的游戏公司，怎样才能做好休闲游戏呢？今天简单聊一聊。\n想做好休闲游戏，首先要定义什么是休闲游戏。只有获得了清晰的定义，我们在讨论休闲游戏时，才不至于出现「鸡同鸭讲」的情况。这会浪费大量时间，无助于提升效率。\n我们先来看看，Steam、AppStore 和 GooglePlay 是如何定义休闲游戏的。\nSteam 下图展示了 Steam 的游戏分类。\nSteam 将「冒险和休闲」作为一个大类，「休闲」是其中的一个小类。除了不同的游戏类型，Steam 还提供了「主题」和「玩家支持」两种不同的分类方法。「主题」可以理解为游戏的风格、受众方向，而「玩家支持」则体现了游戏的社交支持程度。\n进入 Steam 「休闲」子类下的「热门游戏」列表，我们可以看到，无论是 Sims 这种模拟类产品，还是 Bloons TD 这种经典的塔防类产品，或者动作类、沙盒类等等，都被归于休闲类。这些游戏之间的类别、风格、规模都有较大的区别。\nAppStore AppStore 如何呢？来看看 Sensor Tower 统计的 AppStore 北美市场 5 月 28 日休闲游戏排行榜：\n从榜单可以看出，免费游戏中的 Subway Surfers、Candy Crush Saga 和 Magic Tiles 都是老牌休闲游戏。前十中带有 IAP 的游戏占了八成，超休闲游戏占了五成。\nGooglePlay GooglePlay 的情况也差不多，似乎纯广告变现的产品要多一点点。\nSensor Tower 游戏标签 与应用市场的分类不同，Sensor Tower 也提供了自行分类的游戏标签。这些游戏标签是 Sensor Tower 的员工根据试玩游戏的特点进行手动分类的。\nSensor Tower 把游戏被分成「休闲」、「中度」、「体育\u0026amp;竞赛」、「博彩」几个大类，而「超休闲」则属于「休闲」大类下的一个子类。\n解谜、街机、模拟作为「休闲」的子类，其下方有更加详细的分类。RPG 被分到了「中度」标签。\ndata.ai Game IQ AppAnnie(data.ai) 的 Game IQ 也是采用人工分类的方式给游戏打标签的。从下图中可以看到，GameIQ大类中并没有「休闲」这个标签，但拥有「超休闲」标签。\n这是否意味着「休闲」相对于「超休闲」来讲，是更难定义的呢？进入「超休闲」这个大类，我们能看到其子类是酱紫：\n有点奇怪对不对？运动游戏、球类、动作这三个标签在同一个级别！它们其实更像 Steam 中的「主题」。\n「益智」这个大类更让我感到奇怪。「华容道、数独、扫雷」这些小的游戏类型已经强大到足以与「迷宫、脑力游戏」抗衡了吗？\n休闲游戏国区 让我们来到中文世界看一下，下图是 AppAnnie 统计的 AppStore 中国市场 5 月 28 日休闲游戏排行榜：\n最近有很多文章将免费排行中位于榜一的「割草的 100 种方式」归为「超休闲游戏」。但我认为，以该游戏的体量，不应该被称为「超休闲游戏」，而应该算作「轻度休闲游戏」。\n至于中国区的畅销排行…… 嗐，不看也罢\n休闲游戏是一种超类型 分类是复杂的，因此人类专门发明出 「分类学」 来解决这个问题。\n分类学要求分类程序具有唯一性、特属性和逻辑性，但在应用价值上又必须具有直观性、形象性和象征性。\n从上面几个平台的表现看，休闲游戏的定义是模糊的、交叉的、跨品类的。从其包含的游戏的玩法来看，似乎用 Casual 的本意就能概括。\n牛津英语词典第二版（Oxford Dictionary of English 2nd）对 Casual 的形容词形式解释为：\nrelaxed and unconcerned not regular or permanent, in particular happening by chance; accidental without formality of style or manner, in particular (of clothing) suitable for everyday wear rather than formal occasions 我试着用单词的本意解释休闲游戏的特点：\n放松和漫不经心：你可以在无聊、无趣的时候来上一局休闲游戏。 临时的，非正式的，短期的：你甚至都不用等这一局游戏结束，随时可以放弃（地铁到站了？）。 偶发的，碰巧的：一个有趣的小点子就可以做一个休闲游戏。 随意的穿着，非正式场合：风格多样的休闲游戏。 呕，上面的定义有点刻意了，进入偷(chao)懒(xi)模式。\n我个人比较认同下面这段定义。这段定义同时也解释了，为何休闲游戏的体量可以做到非常庞大。因为体验轻量，可以吸引大量的「非游戏用户」，可以将普通人转化成「初级」游戏玩家。这种转化在「超休闲游戏」中更加普遍。\n休闲游戏是涵盖玩法原型最为复杂和宽泛的赛道，包括益智解谜、消除、跑酷，乃至街机动作、竞速等等。只要体验足够轻度，均可算作休闲游戏的范围之内。这种特性也使其可以在性别、年龄等维度上覆盖更为广阔的潜在用户群体。\n《2021 中国游戏出海白皮书》 P128\n正因为用户群体的庞大，《地铁跑酷》这类休闲游戏产品的下载量才能轻松超过国民游戏《王者荣耀》：\n2015年《Subway Surfers》宣布下载量破10亿。 2019年《Subway Surfers》下载量突破了25亿次。 2020年《Subway Surfers》下载量突破30亿。 也正因为用户群体的极度庞大和计算广告网络的发展，才能让 IAA 这种商业化变现方式成为可能。\n游戏是体验的载体 上文中的定义中提到，「体验足够轻度」是休闲游戏的一个重要特点。我们对于 Casual 这个单词的分析也主要集中在「体验」上。\n但游戏本身并不是一种体验，游戏只是体验的载体。\n我们来复习一下《全景探秘-游戏设计艺术》中的游戏定义：\n有趣就是伴随惊奇的开心。 玩是一种放纵好奇心的操控。 玩具就是你可以玩耍的物体。 一个好玩具就是玩起来有趣的物体。 游戏是一种以好玩的态度来对待的问题解决活动。 聊起体验来，没有谁能比 Steam 中的小作文作者更厉害了。下面我精心选择了一些字数较少，但情感强烈的评论。\n水枪很解压对不对？简单的玩法也能给你整花活儿，重点是解压啊！这也是休闲游戏的精髓所在。\n能玩一天的休闲游戏算是休闲游戏么？是手停不下来？还是进入了心流状态？说好的轻度体验呢？\n保卫萝卜出圈，也算是对 TD 这个古老类型的支持吧！普通人对于手机游戏的理解只有两大类：王者荣耀和其它。每次我和亲戚朋友说我的工作，他们都认为我做了个王者荣耀。\n所有的小学生最终都会成为大学生。休闲游戏也能承载历史。\n讨论了这么多，似乎让定义休闲游戏这个任务变得更抽象和难以完成，我心中增加了许多问号，仿佛面前多了一个更深的大坑，而且铁锹还在我自己手里：\n「不是玩家的用户」为什么要一遍遍刷新游戏中的分数呢？ 用户追求的是体验本身，还是仅仅为了消磨时间？ 许多 2018-2020年间的「超休闲游戏」，没有故事设定和成熟的成长线，但依然受欢迎。这些产品可以算作游戏么？ 「超休闲」和「轻度休闲」、「中度休闲」是什么关系？ 有没有「重度休闲」？ ……救命…… 这需要一篇新的文章来讨论： 我理解的休闲游戏（中）——超休闲、轻中度、独立游戏。\n参考资料 《全景探秘-游戏设计艺术》 《2021 中国游戏出海白皮书》 《地铁跑酷》为什么能火十年？ 分类学 https://en.wikipedia.org/wiki/Taxonomy 系列文章回顾 我理解的休闲游戏（上）——平台如何定义休闲游戏 我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 我理解的休闲游戏（下）——超休闲和小游戏交给 SAGI 的知识 继续阅读：我理解的休闲游戏（中）——超休闲、小游戏、轻中重度、独立游戏 ","date":"2022-05-29","description":"想做好休闲游戏，首先要定义什么是休闲游戏。","lastmod":"2022-05-29T01:12:40Z","slug":"casual-game-1","tags":["game"],"title":"我理解的休闲游戏（上）——平台如何定义休闲游戏","url":"https://blog.zengrong.net/post/casual-game-1/"},{"categories":["impressions"],"content":"这几天被大厂裁员的新闻刷屏了，正好今天下午把工作都处理完了，有时间梳理一下信息。\n大厂裁员 腾讯、阿里巴巴是上市公司，三月爆出裁员消息的有赞也是在港股借壳上市的。经营状态暴露聚光灯下，裁员当然是来自于财报的压力。\n腾讯控股发布2022年度第一季度财报显示：该公司第一季度营收1355亿元，同比持平；净利润234亿元，同比下滑51%，非国际财务报告准则下，净利润255亿元，同比下滑23%。 财报中提到，网络广告“市场低迷，环境艰难”，云与其他企业服务正在“主动缩减亏损业务”。\n腾讯裁员的目标是降低成本，多位腾讯人士提到，这次裁员没有非常明确的标准，有一些是因为业绩较低被裁，还有一些绩效不算低的也被裁了。“优先裁可替代性高同时收入又比较高的人”。\n和腾讯批量裁员不同，阿里巴巴是滚动式裁员。\n阿里巴巴的人员一直跟随着业务的发展稳步前进，2019年财年阿里巴巴的全职员工数是10万多人，到2020财年增加了1.56万名员工至11.76万人，2021年突破25万人大关，至25.14万人。然而，从2021年下半年开始，阿里巴巴的总员工增速在明显放缓。2021年第二季度、第三季度阿里巴巴单季新增人数分别为3240人、3876人，第四季度新增人数则锐减至738人。\n与此同时，造车新势力毁约应届生的消息最近也在广泛流传。「毕业即失业」可不仅仅是一句俏皮话。\n小米第一季度财报表示，本季度小米收入为734亿元，同比下降4.6%。\n为什么互联网、手机、汽车企业的业绩同时下滑这么严重？\n主要是来自三个方面的影响：新冠疫情、核心零部件短缺、宏观经济环境 周五我和团队一起在外团建，皮划艇教练说他们前几个月就没怎么干活，平时搞户外运动团队，每天关在办公室里写方案。我问他今年到现在能保本么？他停顿了一下说：能……吧！\n教练问我游戏行业不受影响吧？我在心里默默叹气。新冠疫情导致的居家办公，人效降低，怎么可能不受影响？ 2020 年已经折腾过一次了，有了一点经验。现在每到放假我们都如临大敌，全体随时准备居家办公，做好临时封控准备。 谁也经不住持续这么折腾啊。\n最近政府做了什么动作呢？\n稳经济，先从稳房地产开始 最近，各地房贷利率下调，开放三胎购房，二套房取消限购等政策扑面而来。中国的经济有相当一部分是建立在房地产市场之上的。内循环要形成，房地产不能垮。\n5月18日，高层在云南召开座谈会，研究部署进一步稳增长稳市场主体保就业。这场重磅座谈会上，有一段话非常值得关注：\n“中央经济工作会议和政府工作报告确定的政策上半年基本实施完毕；各地各部门要增强紧迫感，看得准的新举措能用尽用，5月份能出尽出，确保上半年和全年经济运行在合理区间，努力使经济较快回归正常轨道。”\n这可能说明中央该做的都做了，没有藏着掖着的了。\n全国楼市在短暂企稳后再现下跌势头。4月房价下跌的城市数量环比增加，整体新房房价自2015年12月以来首次出现同比下降。\n4月份的社会零售总额，同比下降11.1%。\n根据 Counterpoint Research 的中国周度智能手机销量追踪报告显示，与 2021 年同期相比，自 2022 年的第六周以来至今，中国智能手机销量已连续下降 10 周。\n不仅上面提到的小米在下降，其他手机厂商的销售额也在下降。\nA 股的状态，大家都是知道的。\n年轻人怎么办？ 失业率都高达18.2%了，年轻人还在想着骂资本家……\n看完这篇文章，尤其是文章下面的评论，感觉不是太好。\n有无良企业，有勤恳的员工，有偏颇的媒体。大家自说自话，每个人拿着自己的一点信息就认为那是全世界，这无法解决问题。\n当然，勤恳的员工和守法的企业仍然占大多数，但年轻人已经有了自己的选择。\n复旦大学社会治理研究中心、上海开放大学信息安全与社会管理创新实验室与上海市青少年研究中心联合开展的延续五年的大学生社会心态调查结果显示，将“舒适”和“稳定”作为评价工作好坏首要标准的大学生比例都有所提高，选择“舒适”的大学生比例从2015年的5.4%上升到2020年的8.8%，选择“稳定”的大学生比例从2015年的5.9%上升到2020年的8.0%。\n调查结果显示，相比于2015年，2020年选择50018000元的大学生比例从33.2%上升到40.7%，选择800110000元的比例从5.7%上升到17.9%，选择10000元以上的比例从6.1%上升到16.0%。\n本次调查考察了大学生在找工作过程中是否能接受在行业、岗位、工作地点、薪酬待遇、企业性质五个方面的妥协。结果显示，大学生接受妥协的占比在行业（59.1%）、岗位（70.2%）、工作地点（60.7%）、企业性质（67.1%）四个方面颇高；但值得注意的是，在薪酬待遇上，选择“不妥协”（50.4%）的大学生比例却略高于“妥协”（49.6%）的比例。\n买过股票的人都知道，稳健增长和高回报是成反比关系的。\n在大厂批量裁员的当下，找到同时符合「稳定」和「高薪」的工作机会，太难了。\n这半年里我碰到过几个非常出色的走上管理岗的年轻人，向我展示过这样的思维方式： 创业公司很辛苦，老板要我管人很艰难，工作累心也累，不想努力了，大不了我去找个大厂呆着呗？薪资高又轻松。 当我询问大厂裁员怎么办的时候，得到的反应一般是这样的： 应该不会裁到我吧？\n创业者怎么办？ SAGI GAMES 创业走到了第五年。作为游戏行业的创业者，4月下发版号的消息稍稍提振了一点我们的信心，但不能解决任何实际问题。\n企业不要总想着别人来救，能拯救你的人都只有你自己。\n我们会持续：\n关注行业发展，关注社会现状。 不纠结，不迷茫，不骂ZF不骂娘。 努力挣钱，稳定就业，为稳定社会稳定员工家庭做贡献。 在力所能及的情况下，根据企业自身的承受力和发展状态，尽量创造就业机会。 追求自身价值，奉行长期主义。 创造长期伟大的游戏，创造最佳的环境让团队成功。 部分参考文章 腾讯裁员范围扩大 阿里“滚动式”裁员 20余名应届生从延期入职到解约赔偿：等待小鹏汽车的60天 \u0026quot;失业率超18%, 年轻人的反应耐人寻味\u0026quot;： 追踪5年的真实调查 小米：一季度营收734亿元，净利润下滑52.9% 非常时期，高层召集十大经济强省开会！透露了什么信号？ 失业率都高达18.2%了，年轻人还在想着骂资本家…… 全文完 ","date":"2022-05-22","description":"并非独立资本行为，事关整体经济发展。","lastmod":"2022-05-22T07:59:05Z","slug":"job-cuts","tags":[],"title":"裁员！裁员！","url":"https://blog.zengrong.net/post/job-cuts/"},{"categories":["use"],"content":"本篇讲述我选择一款激光投影的过程。\n此投影满足会议室白天不拉窗帘使用条件。 研究时间 2 小时。 关于激光投影技术 光峰科技是ALPD®技术发明者，使用荧光激光技术，基于激光激发荧光材料、混合多色激光的技术路线用于图像显示。这种技术大幅降低了激光投影仪的成本。\n光峰科技的产品偏向高端，价格多在 3 万以上。其下有一个品牌光峰小明面向中低端。\n峰米科技是光峰科技与小米科技的合资公司。峰米科技价格偏中高端，1-3 万的产品不少。1 万以下的产品也多有布局。\n米家激光电视和米家激光影院应该是峰米科技代工，面向米家品牌单独设计，价格会比峰米科技的产品更低（性价比更高）。\n激光投影光源的寿命一般都在 20000 小时以上，基本上与投影仪本身的寿命相同，不存在之前 LCD 或者 DLP 投影换灯泡的问题。\n关于屏幕 激光投影必须配备专用的抗光屏，才能发挥出激光投影的最大效果。\n几个重要名词 增益：代表屏幕对亮度的增益。白墙增益为 1.0，低于 1.0 属于低增益，高于 1.0 属于高增益。 可视角度：最佳可视角度外，观看质量会有较大的衰减（亮度降低或者梯形失真）。 亮度：一般单位为 ANSI 流明（lm，美国标准）或者 ISO 流明（国际标准）。也有品牌标注尼特（nit）的。一般激光投影的亮度在 1000-3000，亮度与价格成正比。ANSI 与 ISO 流明的换算比例约为 1:0.8。 黒栅屏幕 黑栅幕，对某一特定角度的环境光具有较好的抗光效果，它的设计加工较为简单，幕面尺寸也几乎不影响其光学结构，可选尺寸多。\n黒栅结构吸收来自屏幕上方的光源（一般是环境光、日光、照明灯光），而反射来自屏幕下方的光源。 由此可见，它对来自于其他方向的环境光抗干扰能力较弱。\n黒栅屏幕的锯齿结构位于屏幕的表面，没有保护，幕面不能够被剐蹭，一旦受损不能够修复，安装及使用时需要小心维护。由于容易被划伤和剐蹭，黒栅屏幕的使用寿命一般为 5 年。\n黒栅屏幕的左右可视角度较大，最佳可视角度 160 度。\n黒栅屏幕属于低增益屏幕，一般增益为 0.4-0.8。\n菲涅尔屏幕 菲涅尔屏幕表面布满了从大到小的半圆型扇形螺纹结构，这种半圆结构只对圆心下方的投影光线敏感，也就是它只反射投影机位置投射出来的光线，所以菲涅尔屏幕要想取得理想的效果，在设计结构时一定要尽量让圆心点对准投影机的镜头，这样才可以将反射出的光线聚焦在一定的范围内，从而提升画面亮度。\n菲涅尔结构属于扇形抗光结构，可以将上面，左右两侧的光线进行了吸收反射处理，所以其可视角度相对于黑栅幕的视角要小。\n88、100英寸的菲涅尔屏幕可视角度160度，但最佳可视角度45度。120英寸的菲涅尔屏幕可视角度120度，最佳60度。\n菲涅尔屏幕表面有树脂材料保护，使用寿命大大高于黒栅屏幕。\n菲涅尔屏幕可以达到 1.2 增益。\n选择 我希望达到的效果，是在 中午不拉窗帘的情况下开会，可以轻松看清屏幕文字。\n在会议室，对于可视角度要求高，因此优先选择黒栅屏幕。菲涅尔屏幕的反光也是一个比较大的问题，会影响阅读。\n黒栅屏幕比菲涅尔的增益要低，因此亮度上会有折扣，我选择亮度为 4500ANSI 的峰米 4K MAX 来补充亮度。\n据说沙特王子卧室用的就是这款哦……\n下面是 2200 ANSI 和 4500 ANSI 的区别。左边是峰米 Cinema 2，右边是峰米 4K MAX。\n我在 T1 和 4K MAX 这两款型号中纠结了一阵子。T1 的 CPU 明显更好一些：MT9669 是A73 内核，而 T972-H 是 A52 内核。三色激光和 BT.2020 色域真的让人很激动！而且这明显是峰米的最新一代 ALPD 4.0 技术啊！\n但是，作为放在会议室的投影， 「亮瞎眼」才是最重要的因素！\n部分参考 持续创新的ALPD 五点让你明白菲涅尔与黑栅的区别 菲涅尔与黒栅屏幕比较https://zhuanlan.zhihu.com/p/93779094) 會議室、客廳用的雷射投影機黑柵抗光幕與 Feinieer 菲涅爾抗光硬屏該如何挑選？兩者之間又有什麼差異？ 从原理到参数，手把手教你激光电视怎么选 投影仪幕布应该怎么选择？你需不需要买幕布？ 全文完 ","date":"2022-05-12","description":"选择一款白天不拉窗帘使用的会议室激光投影。","lastmod":"2022-05-12T12:45:43Z","slug":"select-formovie-max4k-max","tags":[],"title":"激光投影与幕布选则","url":"https://blog.zengrong.net/post/select-formovie-max4k-max/"},{"categories":["technology"],"content":"升级 macOS Monterey 12.3 之后，我并没有感觉到有什么异样。\n直到今天，我想在命令行中打开 vimr：\n1zrong@zrong-mbp16$ vimr 2zsh: /usr/local/bin/vimr: bad interpreter: /usr/bin/python: no such file or directory python 没了 什么？python 没了？\n1zrong@zrong-mbp16$ python 2zsh: command not found: python 3zrong@zrong-mbp16$ /usr/bin/python 4zsh: no such file or directory: /usr/bin/python 真没了。\n果然，nvim 是使用 /usr/bin/python 启动的：\n1zrong@zrong-mbp16$ nvim $(which vimr) 2 3#!/usr/bin/python 4 5import urllib 6import subprocess 7import argparse 8import os 9import uuid 10import json 11....................... Sunset Python 2 此事早有端倪。\nPython 官方早在 2020年 1 月 1 日 就宣布了停止维护 Python 2：\nSunsetting Python 2\nWe are volunteers who make and take care of the Python programming language. We have decided that January 1, 2020, was the day that we sunset Python 2. That means that we will not improve it anymore after that day, even if someone finds a security problem in it. You should upgrade to Python 3 as soon as you can.\nApple 在 macOS Catalina 10.15 Release Notes 中也提到了 macOS 将不会包含 Python 2.7：\nScripting Language Runtimes\nDeprecations\nScripting language runtimes such as Python, Ruby, and Perl are included in macOS for compatibility with legacy software. Future versions of macOS won’t include scripting language runtimes by default, and might require you to install additional packages. If your software depends on scripting languages, it’s recommended that you bundle the runtime within the app. (49764202)\nUse of Python 2.7 isn’t recommended as this version is included in macOS for compatibility with legacy software. Future versions of macOS won’t include Python 2.7. Instead, it’s recommended that you run python3 from within Terminal. (51097165)\n现在，Apple 在 macOS Monterey 12.3 Release Notes 中直接明确干掉了 Python 2.7 :\nPython\nDeprecations Python 2.7 was removed from macOS in this update. Developers should use Python 3 or an alternative language instead. (39795874)\npyenv 只要思想不滑坡，办法总比困难多。\n无论是使用 conda ，还是 brew ，都可以简单解决这个问题。\n然而我还是想用 pyenv 试一试，艺（工具）多不压身嘛对不对？\n1brew install pyenv 2curl https://pyenv.run | bash 3brew install openssl readline sqlite3 xz zlib 4pyenv install 3.10.4 5python-build: use openssl@1.1 from homebrew 6python-build: use readline from homebrew 7Downloading Python-3.10.4.tar.xz... 8-\u0026gt; https://www.python.org/ftp/python/3.10.4/Python-3.10.4.tar.xz 9python-build: use openssl@1.1 from homebrew 10python-build: use readline from homebrew 11Installing Python-3.10.4... 12python-build: use tcl-tk from homebrew 13python-build: use readline from homebrew 14python-build: use zlib from xcode sdk 15Installed Python-3.10.4 to /Users/zrong/.pyenv/versions/3.10.4 如果碰到下载太慢无法安装，可以采用下面的流程：\n访问 Python source 网站下载对应的源码包。 将下载的源码移动到 ~/.pyenv/cache/ 文件夹（若无则手动创建）。 重新执行安装命令 pyenv install 3.10.4。 设定必要的环境变量，设置默认的 python 版本为刚刚安装的 3.10.4。\nshims 可以帮助我们解决文章开始提到的没有 python 命令行的问题。\n1# 执行更新 2pyenv rehash 3# 写入 shell，这里我使用的是 zsh 4echo \u0026#39;eval \u0026#34;$(pyenv init --path)\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.zprofile 5echo \u0026#39;eval \u0026#34;$(pyenv init -)\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.zshrc 6# shims 让我们可以使用 python/python3/pip 等等命令 7export PATH=\u0026#34;$(pyenv root)/shims:$PATH\u0026#34; 8# 设定全局默认 python 版本为 3.10.4 9pyenv global 3.10.4 新开一个 shell，看看 python 的情况：\n1zrong@zrong-mbp16$ which python 2/Users/zrong/.pyenv/shims/python 3zrong@zrong-mbp16$ which python3 4/Users/zrong/.pyenv/shims/python3 实际上 python 是什么呢？\n1zrong@zrong-mbp16$ cat $(which python) 2#!/usr/bin/env bash 3set -e 4[ -n \u0026#34;$PYENV_DEBUG\u0026#34; ] \u0026amp;\u0026amp; set -x 5 6program=\u0026#34;${0##*/}\u0026#34; 7 8export PYENV_ROOT=\u0026#34;/Users/zrong/.pyenv\u0026#34; 9exec \u0026#34;/usr/local/opt/pyenv/bin/pyenv\u0026#34; exec \u0026#34;$program\u0026#34; \u0026#34;$@\u0026#34; pyenv 与 conda 的共存 尽管 pyenv 可以管理 conda 的版本，但我并不建议这么做。\n因为 pyenv 中安装的 conda 只能为 python 解释器服务。如果在 pyenv 创建的 conda 环境中使用虚拟环境，将依然使用 pyenv virtualenv 而不是使用 conda env create。\n所以，我建议用这样一套规则来让 pyenv 和 conda 共存。\n规则 1： 禁用默认的 conda 环境。\n1conda config --set auto_activate_base false 2# 或者在 .condarc 中写入 3auto_activate_base: false 规则 2： 独立使用 pyenv 和 conda 管理自己的环境。\npyenv 与 brew 安装的 python 共存 使用 pyenv 安装了 Python3.9.12 和 3.10.4 之后，我想删除 brew 安装的 python。看了一下 brew 下的 python 版本：\n1brew ls | grep python@ 2python@3.10 3python@3.9 4python@3.8 但这是不可能的，因为有大量使用 brew 安装的包依赖特定的 brew Python 版本。\n1zrong@zrong-mbp16$ brew uninstall python@3.9 2Error: Refusing to uninstall /usr/local/Cellar/python@3.9/3.9.12 3because it is required by cairo, ffmpeg, gdk-pixbuf, glib, gobject-introspection, graphviz, gts, harfbuzz, libass, librsvg, pango and python-tk@3.9, which are currently installed. 4You can override this and force removal with: 5 brew uninstall --ignore-dependencies python@3.9 所以呢，我们要做的，就是什么也不做。\n全文完 ","date":"2022-03-29","description":"Python2 真的成了过去时，苹果总是快人一步。","lastmod":"2022-03-29T03:15:17Z","slug":"sunset-python2-in-macos","tags":["python","macos"],"title":"macOS移除Python2支持","url":"https://blog.zengrong.net/post/sunset-python2-in-macos/"},{"categories":["impressions"],"content":"在上周写的 给 SAGI 新员工的一封信 中，我提到 SAGI 是一个终生学习的组织，乐于打破常规，挑战极限。SAGI 的每个人都是自己的领导者。\nSAGI 的每个人都是领导者。你是自己的领导者，是团队的推动者，是公司的检验者。SAGI 关注你的每一条建议，每一个意见，只要你提出来，都会获得反馈。你应该开诚布公，直接且明确地向你的 Leader 说出你的想法，如果没有得到合适的反馈，你知道在哪里能找到我。但请你注意，SAGI 不欢迎无意义的吐槽，提出意见的同时，也要提出你的解决方案。\n学习型组织面临的问题 有学习，就有成长。终生学习的组织会面临下面这些问题：\n如何快速培训新同学？ 如何让团队成员有「成长的感觉」？ 如何根据成长的阶段调岗或转岗？ 如何让成长优秀的员工接受「更大的挑战」？ SAGI 在尝试解决这些问题。\n周报治公司 我每周都会阅读 SAGI GAMES 所有人的周报，大约需要使用 2-3 小时。摘录和回复周报是我周末最重要的工作，这个过程给了我一个整体的视角，让我对每个岗位上的具体细节有了全面梳理的机会。\n如果说 川建国 是 Twitter 治国的话，我可能就是周报治公司了。😝\n本周有比较严重的问题，也有一些针对问题的解决方案。SAGI 的同学们都在周报中毫无保留地将它们展示出来了。有趣的是，某个岗位的问题，在另一个岗位的周报中似乎就给出了解决方案。也不知道大家写周报的时候有没有互相通过气（SAGI 的周报是全员公开的，只要愿意，每个人可以阅读每个人的周报）。\n我把上周周报中的一些有趣的相互印证的点拿出来说一说。\n新同学吐槽：为什么流程这么乱 从结果上虽然没有出现重大的用户故障，但是问题依然存在，都有补充热更的情况出现；而问题的原因却生动的佐证了质量管理的各个方面，比如有临期做功能，测试不够、有“顺手”改代码、有了解不足的；这些问题有流程上的，有风险管理、还有合作部门的规范。\n有新同学也提到，为什么我们的流程这么混乱。 在版本之间连续两天会有热更，仿佛每天都在搞热更。质量管理肯定不是单纯的”点点点“三个字可以解释，要想简单做测试，也可以，无知无畏，出了问题就甩锅，要么就说能力不行，那确实很容易做；但是要想精细的做到方方面面，用科学的方式方法，基于理解的前提下做事，那肯定也需要付出心力。\n看得出来，上面这段周报是质量管理部门同学写的。「新同学认为新东家的流程乱」是一个很普遍的现象，大厂也不能免俗。知乎上有个热帖讲大厂新程序员吐槽老代码质量差的，引起了很多讨论，可惜我一时找不到地址了。\n类似的 老流程问题的吐槽，我作为新同学的时候也干过，但没好意思说出口，默默在心里吐一下，然后就去理解流程，推动优化了。阻力固然大，但我就是来干活的，不是来交朋友的。 只要不让我走人，我就能推下去。\n吐槽的反面，是坚决执行。我想起 《授权》 一书中的段子：\n作者大卫是圣塔菲号核动力潜艇的指挥官，他和轮机长商定了 「推进到 2/3 转速」 的指令，轮机长也严格传达了这个指令。然而推进装置上并没有 2/3 转速。下面是作者和轮机长的对话：\n我问他是否知道圣塔菲号的电力推进装置上没有2/3转速。\n“是的，指挥官，我知道。”\n“那你为什么还下达这个命令？”我震惊地问。\n“因为你命令我这样做的。”\n“什么？”\n“我以为你掌握了一些我们不知晓的军事机密。”\n读到这一段的时候，我笑得停不下来。转念又倒吸一口凉气。在 SAGI 有出现过这样的事么？由于我的坚持和独断，是否也有同学因为我「可能掌握了一些商业机密」而严格执行指令，甚至都没有发出一声质疑？\n对于新同学来说，敢于质疑一切，挑战权威是相当好的习惯。 可想要成长，只有习惯是不够的。Leader 应该主动面对质疑，做好新同学培训，梳理清楚质疑对象的前世今生。新同学则应该站在历史的角度全面理解问题，直面挑战，推动流程向好的方向发展。\n新同学对于团队流程优化，是有推动作用的。老同学可能已经习惯了流程，不容易发现流程中的问题。也可能是尽管发现了更好的工具和方法，但没有意愿或者时间推动整个团队调整。新同学是很好的催化剂。Leader 可能会想，为什么房间里的大象我一直没看到？为什么我早就想到这个解决方案却一直没有推进？为什么我需要一个新同学来推进执行工作？有反思，就有进步。\n当然，也有新同学在没有全面理解现状的前提下就希望按照自己的想法和习惯推进工作。这不可取。所有的修改，都应该在全面同步信息之后进行，要得到团队大部分成员（尤其是 Leader）的信任，可是不那么容易的事！\n以前从来没听说，就不该做吗？ 另一位新同学在周报中进行了吐槽。然而，周一周二是吐槽，周五就变成了点赞。\n其实刚开始进来的前两天不太适应每天都有表格要做挺不喜欢的，感觉以前从来没听说主播需要做表格啥的有点吐槽，T导让我做各种分析和表格其实一开始有点不是很愿意，感觉每个人的风格都是不一样的，没必要做的太框架，后边做了后感觉还是有用的，起码整体看起来更清晰明了，到最后我试播了第一次后发现很多问题，我自己会去自己去分析，到周5慢慢的好像习惯了这种有问题就做思路表格的这种方式，能够帮我更快明了的解决分析问题。\n「以前从来没听说」，只能说明以前没有碰到过这样的要求，不代表以前的方式就是正确的。我们在做创新的时候，经常会面对这样的「灵魂拷问」：为什么别人不这样做？这涉及到两个前提：\n别人是否在全面思考和仔细求证之后才决定不这样做的？ 别人是不是因为没有别人这样做才不这样做的？ 为什么不和其他人一样正常做事情呢？如果你的新计划顺利实行，那样最好；但如果事情不顺利，将会有很多人排着队奚落你：「就因为他追求与众不同」。\n不，SAGI 不会奚落你。与众不同不是原因，而是结果。 SAGI 会愿意去尝试不同。如果我们本就知道这个尝试不合理，SAGI 就会向你解释不合理的原因。 如果是 SAGI 认定正确的事情，即使新同学有多么不情愿，SAGI 也会坚持这个做法。\n自己发现问题，自己解决问题 这位开发同学总能挑战自己的极限，把解决方案做到终极。\n本周研究了一下如何提高启动性能，所有小游戏进入之前会有一个几秒的下载代码包的过程，会导致启动时黑屏几秒流失一部分用户，之前做XXX项目的时候就用民间的办法优化过，启动的时候先用webgl渲染一张图片挡着以至于不会有黑屏的过程，通过这一方法启动分数首次提高到了80分以上。最近发现这一方法在安卓的最新版本微信上会有问题，渲染的图片会显示不正常，黑白、或者是各种拉伸，闪屏，具体原因不知道，论坛有人碰到了一样的情况但是也没人知道。\n后来尝试用别的办法，发现官方也在想办法解决这个黑屏过程的问题，出了一个内测功能叫封面图插件，加了客服微信申请才能用。原理都一样，使用上之后也能到80分以上，双端显示都很正常，再加上主包被我缩减到30K，引擎也丢到分包，首次冲到了93分，剩下7分是在低端机上加载大量3D模型占用内存过多扣的，如果改成在游戏过程中分批加载，分数还能再冲一冲，目前进入游戏已经完全没有黑屏的过程了，我觉得这一套就是终极解决方案。\n这位运营同学自己发现问题，自己解决问题，还顺便更新了部门的工作流，可以说是很 SAGI 了。\n虽然看似很小，但仔细想想，其实是个很严重的问题：帖子发布前的审核标准。\n按照当前的发布帖子，均为写完帖子之后，直接甩到群里，佛系的让大家检查有没有问题。这样其实很危险。\n于是今天做了一个标准模板，在发布之前，先自查进行5个选项的自查，避免检查帖子的时候，不知道该检查些什么，就只是单纯的重复看一遍。\n此外一定要和同事多多沟通，发布前专门拿出3分钟时间，面对面认真的去读一下帖子内容，讨论一下规则是否会挖坑。\n方法就在那里，用不用是你的选择，但展现出来的效果就是云泥之别。\n上面质量管理同学提出的问题，有开发同学直接给出了解决方案：\n任何线上数据的修复，特别是影响大面积玩家的数据，必须在处理前备份数据库。如果redis在处理数据前备份，就不会使用时间跨度十几个小时的备份数据。 代码质量一定要有保障，提交之前要确保每一行代码的修改都能知道目的与结果，如果不清楚的可以问，清楚但是没把握的需要通知相关代码提交者，“手贱”“顺手修改”一定一定不允许出现。 对于线上问题的修复，要做到最小修改，一个步骤能解决问题的，两个步骤就算写出花来也不考虑。仅仅显示的问题，就只修改显示，不对存储做操作，存储出问题，就仅修改出问题的玩家。 代码review的习惯要保持，对每一次提交的代码要进行审核。一些很明显的问题其实在审核阶段是可以解决掉的，比如这次删除存储代码的问题。 开发同学的方案规范了开发行为，产品同学则给出了流程思考：\n提升团队“认知共识”“行为共识”！\n研发过程中，策划要主动推动信息同步，推动有效工作沟通。特别是设计案评审过后，在具体制作环节。策划要不断的，定期的询问是否哪里有问题，是否有险，时程是否有调整…。 不要认为是怎样就怎样，要明确核对，实时同步。\n评审，能解决 大多数的信息对齐。要做到位，技术评审也要更准确细致。并且要在平时工作中不断的交流，强化各种认知和默契，不断巩固团队的 认知共识，行为共识。\n简单的方法，但凡有疑问的，可问可不问的，全都问。 具体工作上，可说可不说的，说。 再简单点， 多唠叨，多唠叨…。 形成共识后，很多工作效率和准确性可以有效提升。\n以上不是单方面哪个人或哪个职能的问题，而是研发全局。 出现的问题，就全员同步。碰到认知不对齐 就立马抹平。\n你甚至还能学到心理学 在 SAGI 的周报里，你甚至还能学到心理学和社会学。\n玩家总是吐槽我们暗改、更新慢，其实很大的一个原因是因为每次发热更没有安排更新公告。当玩家打开游戏，发现一个下载的进度条，然后更改了什么内容也不知道，这时就会很敏感：“官方又在搞什么，肯定又是暗改了！”。其实开发策划美术等同事每天都有做很多事情，比如修 bug，优化等等，但是这些花时间讨论优化的内容玩家并不知道，只有一句 “又暗改“。作为一个小运营，以后也会不断和策划一起做好这一块的工作吧。\n玩家暗改那个事情，充分说明了透明度的重要性。是不是真透明不重要，重要的是给人以「透明」的感觉。\nApple Music 刚上线的时候（当时乔布斯还在世），「随机播放」使用的是「真随机」。但用户上来就投诉说这个「不随机」，说重复的歌太多。最后乔布斯给改成了「假随机」，用户才满意了。\n如果这位运营同学能直接去推动产品增加这个更新公告，那么整个团队都会获得成长。\n我相信 TA 一定会这么做。\n转岗是基础操作 沟通后确认了转岗相关的内容，我个人是觉得很具有挑战性工作，加上我自己对于直播是有一定了解并且感兴趣的，所以在知道了工作调整之后是毫不犹豫就答应了。虽然增长运营对于我来说是一个全新的模块，我觉得工作就是需要不断学习不断拓宽自己的，我也很期待之后的工作中能学习到更多，希望能尽快上手增长相关的工作为T多分担一些。\n在 SAGI ，部门内部按需转岗是个普遍操作。跨部门转岗也是常见的。SAGI 作为终生学习的组织，支持转岗，也欢迎转岗。要成为 π 型人才，转岗是最快的方式。\n新同学成长：自下而上 我们总是希望接受更大的挑战。在 SAGI，我们的成长是自发的，是受到尊重的，是有人帮助的，是自下而上的。\n如果有人在 SAGI 的成长超过了 SAGI 的发展速度，我们应该怎样让 TA 接受「更大的挑战」？\n无论是调岗，还是负责难度更高的工作，都会立即进行。\n终生学习，终身成就。\n全文完 ","date":"2022-03-13","description":"有学习，就有成长。终生学习的组织会面临下面这些问题。","lastmod":"2022-03-13T12:11:57Z","slug":"rookie-in-sagigames","tags":["sagiteam","management"],"title":"新同学在SAGI的成长：自下而上","url":"https://blog.zengrong.net/post/rookie-in-sagigames/"},{"categories":["impressions"],"content":"你好，新同学：\n欢迎加入 SAGI GAMES。\n你要做好准备，SAGI GAMES 可能拥有你从来没有体验过的 Style。你既然已经看到了这封信，说明你已经得到了 SAGI 的认可，你应该和我们是一样的人。事已至此，你只有两条路可以选：融入我们，或者离开我们。\nSAGI GAMES 是一家集创新游戏研发、发行、运营于一体的游戏公司，由一群十年以上的硬核玩家组成。我们聚集在这里，强调休闲、简单、快乐的企业文化，坚持超越自己、全力以赴的游戏精神，立志打造长期伟大的游戏，愿意创造最佳的环境让团队成功。\nSAGI 没有部门墙。现代企业的分部门制度，给了每个部门一个不同的聚焦点。这么做的好处是能够提高效率，每个部门都非常清楚自己的任务是什么，只要全力以赴完成分内任务就行了。但坏处在于大家都变得近视，只专注于自己部门的事，没有全局思维。SAGI 不喜欢这种「领导者-跟随者」的工作模式。\n游戏是内容产业，SAGI 喜欢有全局思维的你。我们不怕你出错，只怕你不创新。如果无法不断打破常规，突破自我，不可能做出优秀的游戏。把每天的任务完成就行了吗？解决现在的问题就行了吗？你有没有挑战过自己的极限？你有没有试图改变过自己多年的习惯？你有没有不怕别人笑话提出一个看似傻 X 的问题？你有没有勇敢面对一个全新的领域？\n「木桶理论」在 SAGI 不适用。你不需要成为「没有短板」的人，你应该成为「有好几条长板」的人。SAGI 需要 「T」 型人才，我们希望你的那一横足够宽，宽到你有能力「管得宽」。我们也需要你那一竖足够长，长到超越所有小伙伴。我们更希望你在 SAGI 发展成 「π」 型人才，两条腿跨越学科和职业，分得越开，站得越稳。 放心，你的短板，其 TA 小伙伴会用 TA 们的长板帮你补齐。\nSAGI 的每个人都是领导者。你是自己的领导者，是团队的推动者，是公司的检验者。SAGI 关注你的每一条建议，每一个意见，只要你提出来，都会获得反馈。你应该开诚布公，直接且明确地向你的 Leader 说出你的想法，如果没有得到合适的反馈，你知道在哪里能找到我。但请你注意，SAGI 不欢迎无意义的吐槽，提出意见的同时，也要提出你的解决方案。\nSAGI 是一所学校，有很多优秀的人，但你不必羡慕 TA。你应该学习 TA，成长为和 TA 并肩战斗的人。SAGI 的所有人都乐于分享，愿意把 TA 掌握的一切知识倾囊相授。你一定会成为和 TA 一样的人，心中有火，眼里有光，脚踏七色祥云…… 「大鹏一日同风起，扶摇直上九万里」！\n人生如戏，公司亦如戏。 SAGI GAMES 是一个游戏，我们都是她的创造者和运营者。愿你在这群真心热爱游戏的人中找到归属感，愿你将自己的能力、经验、创意和资源融入到 SAGI GAMES 的创作中，做出你最爱的游戏，改变自己的生活！\nSAGI GAMES 创始人： 曾老师\n2022-02-22\n全文完 ","date":"2022-03-05","description":"人生如戏，公司亦如戏。SAGI GAMES 是一个游戏，我们都是她的创造者和运营者。愿你在这群真心热爱游戏的人中找到归属感，愿你将自己的能力、经验、创意和资源融入到 SAGI GAMES 的创作中，做出你最爱的游戏，改变自己的生活！","lastmod":"2022-03-05T02:38:45Z","slug":"a-letter-for-new-classmate-in-sagi-games","tags":["sagiteam","management"],"title":"给 SAGI 新同学的一封信","url":"https://blog.zengrong.net/post/a-letter-for-new-classmate-in-sagi-games/"},{"categories":["impressions"],"content":" 有担当的管理者一个重要的责任，就是把下属从愚昧之巅推向绝望之谷。\n这是我在 2019 年 1 月 20 日写的一篇随笔，距离今天已经整整三年了。早上在回复 SAGI GAMES 同学周报的时候翻了出来。我这两天一直在回顾去年的工作，将文章与这三年我所做的决策对比思考，感触良多。\n我将文章稍作修改发出来，就是 2022 年的第一篇文章了。所以，这是一篇「崭新的旧文章」。😄文章中的一些观点需要结合当时（2019 年1 月）的现状才能讲得通。\n文章标题和最开始的一句话来自于这篇采访： 美团王慧文：大部分人不知道自己在愚昧之巅。\n这篇文章说了三个部分内容：\n美团对上市这件事的认识； 管理和组织架构； 对于企业基因的认识。 有几句话对我触动比较大：\n第一句话 自然界前面，人还是非常渺小的，我们要在变化之前提前做一点准备，确保企业在任何一个时间点上，让经营理念、资源配置、节奏符合大势。\n我觉得我们做游戏，不能跟风。比如业界流行 IO 我们就去做 IO，业界流行 MOBA 我们就做 MOBA，业界流行吃鸡玩法我们就做吃鸡玩法。玩家也是跟风的，他们跟着游戏从一个风口跑向另一个风口。你如果在风口看媒体，会发现好像全世界都在玩同一个游戏同一个类别，没有人关注其他类别。这个是假象。即使全世界都在吃鸡，还是有非常多的人在玩其他类型的游戏。玩家并不会永远只玩一款或者一种游戏。不能因为媒体没有报道它们就认为它们不存在。如果去看年底的游戏收入榜，会发现风口游戏并不处于垄断地位的。\n所以呢，除了变化，还要积累。游戏类别和流行趋势一样，都是几年一个轮回。我们得做好积累，看清游戏市场的发展节奏，再稳定好自己的步调。\n第二句话 虽然大部分人不知道自己在愚昧之巅，但是大部分人都知道别人在愚昧之巅。谁在愚昧之巅人们看得非常清楚，但是没有人知道自己在愚昧之巅，这就产生了非常大的信息不对称。\n说说我的想法 以上是展示达克效应的两张图。\n达克效应：\nD-K effect 邓宁-克鲁格效应 Dunning-Kruger effect 昨天看了 《空雨衣》，里面提到了 S 型曲线和达克效应有异曲同工之妙。看来大师们触摸到顶端之后，得到的本质理论是相同的。\nS 曲线是把 S 躺下来看，大概的意思是，业务发展一定有增长期和衰退期，企业要在增长期达到顶峰的时候开启第二条 S 曲线，在不至于让企业衰落。\n我做管理方面的工作时，会有两个极端思维：\n1.觉得别人在 D-K PEAK，为啥别人不能快点成长和我保持思路一致啊； 2.觉得自己在绝望之谷，似乎做什么都不对，很痛苦，不知道方向在哪里。\n《空雨衣》里面讲的 S曲线也有一个悖论。企业如何知道自己的业务是否处于向顶峰攀爬的过程中，还是已经处于顶峰？何时开辟第二条曲线才是最好的时机？毋庸置疑，新的曲线开始的时候大概率会处于”绝望之谷“，而且会消耗大量的人力物力财力。\n回到实际问题 当前业务发展不理想的时候。是新开一个项目，还是努力调整现有项目让其不断增长？对于本来就资源紧张的小团队，新开项目是会消耗人力的。如果新开的项目不成功，还有可能会影响团队情绪。\n即使当前业务发展不错，如何更进一步？当前业务的顶峰在哪里？成熟点在哪里？我们是处于一个长期的上升期呢？还是正在顶峰平缓期？\n不要用基因自我设限\n专业是必须要专业的。对于游戏行业的从业者来说，除了专业还需要全面。\n我一直这样认为：\n非全栈的程序员，不是称职的程序员。 不懂一点美术、设计、音乐、程序、写作、管理的游戏策划，不是合格的策划，不可能成长一个合格的游戏制作人。 不懂商务和产品的运营不是好运营。 不玩自己做的游戏的人，不配做游戏 。 用基因自我设限，更多的可能是给自己的懒惰找台阶罢了。 下面的内容来自未来（2022-01-23） 我还是觉得三年前的思考有那么一点极端。后面我又写了几篇文章来平复这种思维：\n读书笔记：《俞军产品方法论》 聪明的中层管理者 去上游 自主思考是对员工的过分要求 杰出并非不可取代 更多关于管理的文章可以看这个 Tag ：MANAGEMENT 。\n全文完 ","date":"2022-01-22","description":"有担当的管理者一个重要的责任，就是把下属从愚昧之巅推向绝望之谷。","lastmod":"2022-01-22T23:55:33Z","slug":"peak-to-trough","tags":["reading","management"],"title":"推向绝望之谷","url":"https://blog.zengrong.net/post/peak-to-trough/"},{"categories":["impressions"],"content":"CP 不一定要成为发行，但 CP 应该了解足够多的发行知识。本文谈谈我对流量的理解。\n我在 小CP如何搭建发行团队 中提到过，优秀的发行最重要的能力可以用一个词来概括：放大。\nCP 把产品交给发行，要判断产品能不能发好，推广之后的表现算不算好，需要了解「流量」。\nCP 如果决定自己发产品，想让产品赚到更多，更要了解「流量」。\n流量的理解 流量转换是所有销售的基础，也是游戏推广的基础。 发行工作的灵魂拷问：量从哪里来？\n流量从渠道中来 流量从广告中来 流量从品牌中来 不能孤立考虑营销传播和获客的问题，而是要综合考虑公司整体运营和成本。\n游戏行业的获量渠道 手机厂商渠道（传统的硬核联盟），例如小米市场、App Store、TapTap、网站、公众号、媒体的自然曝光； 移动设备预装； 渠道中的推荐位。 渠道中的推荐位是有限的，需要游戏本身的资源足够好才能争取到。目前是精品稀缺的时代，好的产品就能拿到足够多的资源位，渠道与产品才能产生共赢关系。\n需要注意的是，各渠道获取推荐位的方式是不同的。AppStore 需要自己做曝光，产品符合苹果小编口味的才有推荐位的可能。硬核渠道则需要做封测和评级。\n游戏广告，就是精准买量 百货商店之父约翰·沃纳梅克说过一句很著名的话：「我知道我的广告费浪费了一半，但是我不知道是哪一半」。互联网计算广告出现后，根据大数据和关键词匹配，我们似乎可以实现广告费 100% 精准不浪费。但对于广告主来说，不浪费就是最完美的广告投放模式么？\n如果广告成本不断升高，高到和收益相同，那么精准得到的量还是优质的量么？\n如今的买量已经从买激活、买付费逐渐过渡到买 ROI，产品的所有数据都要回传给广告商，广告平台需要这些数据来「匹配精准用户」。如果产品的 LTV 是 100 元，你的买量成本一定会到逐渐上涨且稳定到 80 元。广告平台知道你的所有数据，在竞价的前提下，它会怎么做？它当然会始终给你留一个「薄利」空间，让你永远为平台打工。不要骂广告平台，换成你也会这么做的。\n在「广告主」、「广告平台」、「流量主」三家之中，广告平台是稳赚不赔的。\n广告主不是「主」，而是「佃户」；我们不是在「买量」，而是在「打工」；广告平台不是「流量平台」，而是「流量监狱」。\n投资就是风险，凡是投入产出可控、可计算的，就是所有人都有的技能。买量平台恰好就是这样一个可控的计算平台。即使构建再大的团队，雇佣更多的优化师和素材剪辑师，对买量素材进行更精准的优化，在成本上也不会持续保持优势。\n在商业社会中，承担更大的风险，就可能得到更大的收益。如果一味追求「精准买量」，代表你不想承担风险，想挣大钱就难了。\n上面的部分有点妖魔化广告平台了。在游戏发行的过程中，利用广告平台买量依然是主流并且容易操作的获量方式。写这一段的目的是提醒 CP 理解买量方式的弊端，不能把「发行」和「买量」画上等号，而要不断学习新的获量方法。\n基于品牌，获取免费流量 关于品牌，SuperCell 是一个很好的例子。\n每次 SuperCell 有新品测试，所有的主流游戏媒体都会免费帮它做宣传，甚至教它做游戏。这样得到的流量曝光当然是免费的。\n同样的，CDPR 和 Rockstar 的拥趸会持续关注公司的新产品和明星产品的动向，迫不及待地排队买首发，这是品牌的力量。\n在国内，一些老牌的游戏公司如椰岛、凉屋等会持续打造有自己特色的产品，玩家也对它们的品味产生了较为明确的认知。在新品上线的时候，会在第一时间积累更多的用户，这就是品牌免费流量。\n所以，获取流量的过程，就是不断摈弃流量佃户思维，打破流量监狱，获取流量主权的过程。\n私域流量内卷 昨天晚上我正吃饭，接到一个自称是小区开发商车位销售处的电话，在我表明不买车位之后，一定要加我的个人微信。\n我：你为啥一定要加我微信呢？我不可能被你转化啊。 销售：加微信是我们的工作要求，加微信数量是我们的考核标准。而且我们是用工作微信加，不会对你有什么打扰的。 培训班的电话能报出我孩子同班同学的名字，要加微信。点个肯德基要加群，喝个咖啡要加群，收个外卖要加群，点个菜还要关注公众号。\n当你的微信里已经有超过三个企业微信群（其实就是私域流量群）的时候，你已经被（有组织的）私域流量裹挟了。\n社区团购私域 来看看现在私域流量玩得最 6 的社区团购是怎么玩转私域流量的：\n个人微信流量主（社区、渠道），小区群； 分销工具，裂变工具，平台分销渠道； 代购、小红书种草、个人朋友圈经营； 微商、家庭主妇、人类高质量群体 KOL； 淘宝店主，美团商家群。 私域本来是为了降低获量成本而存在的，可是当私域逐渐壮大之后，公域和私域流量就会互相倾轧。 品牌方锁定独家可以理解，如果连私域也要锁定的时候，私域的内卷就开始了。\n卷到最后，大家又会回到起点，做 IP（品牌），做小众和非标品，提高客单价。\n游戏的私域流量在哪里？ 公司官网（少）； 老玩家群（取决于产品的运营周期），包括但不限于 QQ 群、微信群、FacebookGroup、抖音群、抖音号、视频号、YouTube 频道，群和频道之间可以而且必须互相关联才能达到最好的效果； 公会； 基于游戏社交玩法的裂变和拉新； 游戏社交形成的社区，包括游戏内和游戏外（线上）； 渠道的公司专属页面。例如 TapTap、Steam、AppStore 的公司专区（少）； 试玩、积分墙等渠道。 这些流量渠道中，有质量高的（例如老玩家群），也有质量低的（例如公会和试玩）。游戏并不是一味追求高质量用户。根据发行目的的不同，各种类型的流量都可以进行尝试，然后选择最适合产品的流量合作。\n渠道的 55 分成能做吗？ 前段时间苹果税降低，让硬核渠道过高的 55 分成比例暴露了出来。「原神」在小米市场上架，应该是调整了分成比例。但我们不是米哈游，也没有做出「原神」这种级别的产品，是不是还需要把自己的产品上渠道呢？这可是 55 分成啊，再加上乱七八糟的渠道费和优惠可能有 40% 就不错啦。\n做发行的过程主要是算账。从纯粹算账的角度来讲，渠道的流量可以理解为是「免费」的。\n抛开 55 分成的心理门槛，渠道提供的流量能让产品在扣除所有成本之后赚到钱，就是可以做的。\n在渠道上做流量获取，比在广告平台买量还是要复杂很多，不在此处讨论。我仅仅是提出一种思路，一种从流量获取和算账角度来做决策的思路。我觉得作为 CP 需要学习和理解这种思路。\n做品牌和做 IP 为啥游戏厂商喜欢买 IP？为啥大厂喜欢炒冷饭？为什么超过 10 年的游戏公司都活得还不错？\n买 IP 可以降低获量成本啊！炒冷饭可以降低获量成本啊！超过 10 年的公司已经形成了属于自己的品牌价值和规模效应了，也能降低获量成本啊！\n根据《2021 年中国游戏产业报告》，在收入前 100 移动游戏产品 IP 类型收入占比中，收入占比最高的是自创 IP，占比为 42.46%。另有 32%、8.33%和 8.27%的 IP 类型收入分 别由客户端游戏、小说和主机/单机游戏改编而来 的产品提供。总收入与 IP 的总数量基本呈正比。\n自创 IP，比例是 42%哦！\n与社区电商不同的是，每个游戏都是非标品。每个游戏都有机会形成自己的 IP。社区电商或许也要进入到使用 IP+非标品，通过情感体验来提高单价的方式赚钱的阶段了。而这是游戏赖以生存多年的原生发展模式。\n对于发行来说，把游戏产品卖出去，是一门生意。而对于 CP 来说，把自己的产品卖出去，却关乎着情感。\n所以说 CP 更适合长线发展，做品牌，打造自己的 IP。\n全文完 ","date":"2021-12-22","description":"对于发行来说，把游戏产品卖出去，是一门生意。而对于 CP 来说，把自己的产品卖出去，却关乎着情感。所以说 CP 更适合长线发展，做品牌，打造自己的 IP。","lastmod":"2021-12-22T10:07:17Z","slug":"to-cp-about-flux","tags":["gamenote"],"title":"面向CP的发行知识：关于流量的理解","url":"https://blog.zengrong.net/post/to-cp-about-flux/"},{"categories":["impressions"],"content":"从 2021 年 7 月 22 日开始计算，版号已经停发 4 个月了。这段时间，游戏圈里大家都喊着要「出海」。但我觉得，出海不是银弹，也不是口号。如果没有想清楚出海做什么和为什么出海，还不如不出海。\n今天早上读完了巨量\u0026amp;Ohayoo的《2021休闲游戏研究报告》+音数协游戏工委出品的《2021年中国游戏产业报告》，得到了一些更确切的数据。我结合近期的思考做了一点文字整理，仅用于指导 SAGI GAMES 的发展，欢迎交流。\n数据告诉我什么 整个国内游戏销售收入是 2965.13 亿元，其中自主研发游戏收入为 2558.19 亿元，自主研发游戏在海外的收入为 180.13 亿美元（按今日汇率换算为 1147.63 亿元）。国内同比增长 6.40%，海外同比增长 16.59%。\n这是不是说明中国的游戏开发者更多转向海外了？从趋势来看，中国自研游戏的海外收入从 2017 年开始每年增长 超过 15%，2020 年达到 33%。而中国自研游戏在国内的收入从2016年开始小幅下降，从 19.85% 降到 2019 年的 15.28%。\n年份 版号发放数量 2016 4050 2017 9369 2018 1923 2019 1385 2020 1219 2021 679 这个趋势，似乎和版号收紧政策正好对得上。但从下面的两个数据来看，无论是国内还是海外，收入都是持续增长的。并未出现国内游戏的收入增长「断崖式下跌」，和海外收入「大幅增长」的情况。\n2021 年的国内和海外收入增长率都出现了两位数的下跌，可见国内游戏收入增长率下跌并非完全由于版号收紧（比 2020 少发 50%）造成，而是受到了 2020 年新冠疫情下全球宅经济的激增效应消退的影响。\n根据 AppAnnie 和 Newzoo 的数据，2020 年全球移动应用收入为 1430 亿美元，其中游戏为 907 亿美元（根据今日汇率换算为 5780.04 亿元），可见中国市场在全球游戏市场中的影响力。\n我们可以更「一般」地讲：优秀的游戏总是可以盈利的，但没有版号，就少了一个重点盈利市场。\n需要注意的是，上面的收入数据，并不包含广告收入（IAA）。\n我们自己适合做什么 泛泛地谈「出海」没有意义。我们必须找到适合团队基因的品类，深挖下去。\n「弓箭传说」的原班人马（包括研发和发行 Habby）近期正在推广新品砰砰法师（PunBall），作为一个资深休闲游戏玩家，我也在深度体验，自己挺喜欢的。在 PunBall 中，开发团队很取巧地使用了广大玩家喜闻乐见的「弹一弹」玩法包装（买量可能会便宜吧？），游戏中有很多「弓箭传说」的影子。两者对比，PunBall 的设计和系统更加成熟。从 AppAnnie 和目前的宣传来看，产品的发行效果还是挺不错的。\n抛开独立游戏不谈，我认为商业游戏更愿意复用之前的成功。下面三种重复成功的思路都很优秀：\n重度游戏使用轻度素材买量，或者做混合玩法（例如插销的广告都看过吧，现在已经独立做成了游戏）来降低买量成本。 Habby 和青瓷的产品，休闲轻度包装，结合自己积累的一整套经验，在一条路上走到底。看起来是轻度，其实是中重度。 SuperCell 的产品，轻度包装，实际上粘性极强，更追求创新玩法。 很难说哪种思路更好，但我们可以分辨出哪种方法最难。思路 3 难度最大，一旦成功就是极大成就，而且是名利双收的大成就。\n思路 2 可以获得足够好的商业回报，当然也需要长期的积累。青瓷从起步到上市积累了近 10 年。在 PunBall 上我们也能看到「弓箭传说」的经验以及 Habby 多年积累的成功发行套路。\n可以说思路 2 和思路 3 都有足够的壁垒，持续复用的成功概率更高。\n思路 1 是相对好操作的，但在开始之前，我们要明确团队能 hold 住的类型。\nSAGI GAMES 怎么做 SAGI GAMES 从超休闲到轻度休闲，到现在的中度休闲，一直在做休闲游戏，在「2021休闲游戏研究报告」中，我也找到了许多有用的洞察。\n全年龄向用户都认可休闲游戏的「解压」和「放松」的功能。 年轻人更爱休闲模拟和休闲竞技，年长者更爱益智解谜和棋牌桌游。 80后付费意愿更高，有 63% 的用户曾为休闲游戏买单。 0-200元是主要选择，16% 的用户能接受为休闲游戏付费千元。 年轻人偏爱装饰，年长者更爱通关和免广告；男性关注加成和身份，女性关注皮肤和额外游戏机会。 年轻人更在意「精妙好玩」，年长者更在意「获得感和智力的成就感」。 近五成用户通过短视频获得休闲游戏，「应用商店」、「朋友推荐」、「互联网广告」各占三成。 76% 的用户通过应用商店下载游戏，官网和运营平台也占有接近三成。 有四成用户不喜欢过于复杂的游戏，有三成用户接受激励广告。 从上面的洞察来看，SAGI GAMES 一直坚持的 休闲竞技，简化操作，混合变现 的思路，是符合休闲游戏的发展方向的。\n我之前在 更严监管之下，休闲游戏小CP的破局思考 一文中聊过 SAGI GAMES 的路线：\n找到适合团队基因的「蓝海」品类。 不扎堆，选择「大厂」们或许看不上，或许认为赚钱效率不高的赛道。 建立自有流量池。 精品和流量都要做。学着理解流量的运作方式，找到获取低价甚至免费流量的方法。 出海会遇到什么 SAGI GAMES 在 2019 年 开始尝试出海，作为一个之前没有任何海外经验的团队，我们遇到了下面这些问题：\n本地化很贵！很贵！很贵！每次更新都有翻译费！当你有大量小语种时，玩家会因为翻译不好或者没有本地翻译给你打差评。 不同市场对于同一个产品的反映截然不同，不同到让人无地自容。 海外社区的玩法与国内差异巨大； 不同国家不同地区的人们有不同习惯； 海外主体，海外银行账户，收款； 面对 FB、Google 等公司面向中国开发者的「恶意」，尽管这些恶意可能是我们「自食其果」。 上面的每一条都可以单独拉出来写篇文章，限于篇幅就不展开了。\n出海不是银弹，也不是口号。想清楚我们适合做什么，想清楚玩家需要什么，想清楚怎么获取收益，再决定如何出海。\n出海当然和停发版号有相关性，但没有因果关系。\n参考资料 迎风使帆，2021休闲游戏研究报告： 巨量算数 \u0026amp; Ohayoo 2021中国游戏产业报告： 中国音数协游戏工委 2021 年移动市场报告： AppAnnie 聚焦游戏领域，2021 年回顾：AppAnnie \u0026amp; IDC 光明网：监管部门发声 杜绝游戏擅自变更内容等违法行为 新时代证券：复盘2019 年版号审批：控制总量强调精品，维持长期看好游戏行业发展 2021全球移动市场报告：Newzoo 全文完 ","date":"2021-12-17","description":"优秀的游戏总是可以盈利的，但没有版号，就少了一个重点盈利市场。出海当然和停发版号有相关性，但没有因果关系。","lastmod":"2021-12-17T07:11:11Z","slug":"no-isbn-and-set-sail","tags":["gamenote","sagiteam"],"title":"因为停发版号就出海是不是一个伪命题？","url":"https://blog.zengrong.net/post/no-isbn-and-set-sail/"},{"categories":["use"],"content":"上一篇 在 macOS 上使用手柄玩 Steam 游戏 之后，我终于可以在 macOS 上使用手柄愉快玩耍了。但升级了 macOS Monterey 之后，Switch Pro 手柄 无法识别 了！\n我试了无数种方法，当把手柄换成 Xbox 配套手柄之后，一切又恢复正常了。经过真机测试，Xbox Series X 和 Xbox One 的手柄都可以正常在 macOS Monterey 上使用。\n但是，我又遇到了新的问题：\n我发现有较大几率，Steam 会把使用蓝牙接入的 Xbox 手柄识别为多个，这导致在游戏中频繁出现「双击」的现象，正常的游戏完全无法进行。如下图所示：\n在中文世界中，我没有找到这个问题的解决方案，可能因为中文世界像我这样用 Macbook Pro 玩 Steam 游戏的人太少吧。^_^\n在 Steam 论坛上有网友给出了解决方案：Xbox one controller recognized as 2 controllers by steam. Causing double inputs。\n经过实验，在 macOS 的 Steam 中，如果出现一个 Xbox 手柄被识别为多个，可以进行下面的操作：\n关闭手柄：长按 xbox 手柄上的「西瓜」按钮 5 秒以上； 关闭 macOS 中的蓝牙功能； 退出 Steam； 启动 Steam； 开启手柄； 打开 macOS 中的蓝牙功能。 此时应该能正常识别。\n如果依然不正常，建议删除手柄的蓝牙连接，重新配对。\n需要注意的要点，是不要在 Steam 启动前开启手柄。应该在 Steam 完全启动之后，再开启手柄。\n全文完 ","date":"2021-12-11","description":"解决 macOS 上 Steam 将一个 Xbox 手柄识别为多个导致的双击问题","lastmod":"2021-12-11T12:01:04Z","slug":"xbox-controller-in-macos-steam","tags":["game"],"title":"解决 macOS 上 Steam 将一个 Xbox 手柄识别为多个导致的双击问题","url":"https://blog.zengrong.net/post/xbox-controller-in-macos-steam/"},{"categories":["use"],"content":"我的设备是 MacBook Pro (16-inch, 2019)，Intel 8 核 i9 处理器，内存 32 GB 2667 MHz DDR4，显卡 AMD Radeon Pro 5500M 8 GB。\n操作系统是 macOS Big Sur 11.6。\nSteam 上支持 macOS 的游戏越来越多了，所以我把 Switch Pro 手柄用蓝牙连上了电脑。\n问题出现 整个的识别过程还是挺顺利的，在 Steam 的控制器设置界面，可以正常识别出 Switch 手柄，也可以进行键位设置。\n但进入游戏后，手柄是无法工作的，所有按键无效。\n我让 Steam 进入大屏幕模式（Big Picture Mode），Steam 提醒我要安装驱动。\n一路 NEXT 进入到安装界面：\n最后一步安装失败。\n这个流程我做了数次都是同样的结果，因此求助于网络。看了各种不靠谱的解决方案，在 Steam 社区 找到了亲测有效的最终解。\n解决过程 下面把解决过程梳理一下：\n按住 ⌘-R 快捷键重启到恢复模式。 打开终端。 输入下面的代码并回车： 1spctl kext-consent add MXGJJ98X76 输入下面的代码并回车： 1kmutil trigger-panic-medic --volume-root \u0026#34;/Volumes/Macintosh HD\u0026#34; 重启。 重启之后，已有的系统扩展弹出了信息更新提示。\n重新进行安装操作，安装成功。\n在系统的「安全性与隐私」中也能看到来自 Valve 的扩展被批准了。\n这个问题解决之后，Steam 中的游戏都可以支持手柄了，除了人类一败涂地（Human: Fall Flat）。\n此方法仅适用于 Intel 平台的 Mac，苹果自研处理器 M1 平台目前依然无解。\n问题原因 根源是 Valve 不作为。Steam 开发团队的 TEAM ID 不在苹果的安全白名单里，其实只需要 Valve 去申请一下就好，但 Steam 没这么做。\n上面的流程所做的工作就是将 Steam 的 TEAM ID 加入到了设备白名单中，这会触发系统对所有的系统扩展重新鉴权。\n至于特定的不支持手柄的游戏，就只能去找开发者了。\n全文完 ","date":"2021-09-28","description":"连接 Switch Pro 手柄到 macOS Steam","lastmod":"2021-09-28T07:32:17Z","slug":"use-controller-in-macos-steam","tags":["game"],"title":"在 macOS 上使用手柄玩 Steam 游戏","url":"https://blog.zengrong.net/post/use-controller-in-macos-steam/"},{"categories":["news"],"content":"本文标题党，看新闻一定要找出处和官方途径，要有自己的思考。\n昨天的朋友圈被刷屏了，大家认为 AppStore 已经开放第三方支付了，奔走相告，很开心。\n搜索引擎里也充斥着这样的文章：\n但实际上呢？\n并不是。\n错在哪里？ 看原文：\nApple, US developers agree to App Store updates that will support businesses and maintain a great experience for users 原文关于「开放第三方支付」有这么一段描述：\nTo give developers even more flexibility to reach their customers, Apple is also clarifying that developers can use communications, such as email, to share information about payment methods outside of their iOS app. As always, developers will not pay Apple a commission on any purchases taking place outside of their app or the App Store. Users must consent to the communication and have the right to opt out.\n这里提到的信息是：\n开发者可以用 email 等途径告知用户第三方支付的信息； 苹果不会对第三方支付的部分征收「苹果税」； 用户有权退出第三方支付。 所以，如果你希望在你的 iOS 应用中接入支付宝或者微信支付，用于购买 App 中的虚拟物品，那是不行的。\n这和大众理解的「开放第三方支付」，是完全不一样的场景。\n为什么出错？ 原文 Summary 中有这么一段话，如果不看细节就很容易理解为开放了第三方支付：\nThe agreement clarifies that developers can share purchase options with users outside of their iOS app\n当然，更大的可能是大部分媒体都是转载消息上瘾，甚至没时间（lan）看原文吧。\n几点消息 这次苹果政策修改并不是源于 Epic Games 的诉讼，而是源自 2019 年 一批小型开发商提起的诉讼。\n这个政策的修改何时生效还未确定，需要等待法官审批。\n不允许在应用内通知用户。\n开发者可以怎么做？ 我来开一点脑洞，如果苹果的这个关于第三方支付的新政策生效，开发者可以如何利用呢？\n以游戏开发者为例，在接入了微信登录的游戏中：\n在游戏中引导玩家关注公众号； 在公众号中引导玩家使用微信登录游戏（若提供了多种登录方式，则必须使用微信登录）； 在微信公众号中提供充值入口，对苹果玩家提供折扣或者礼包赠送。（小心安卓玩家可能会喷死你：安卓就不配领礼包么？垃圾游戏！煞笔策划！） 对于海外玩家：\n在游戏中引导玩家关注 Facebook 群组或者提供官网链接； 在官网提供充值入口。 以上的行为，在新政策出台之前，都是违反苹果政策的。但也不是不能用，对吧？\n其实什么也没有改变。\n参考文档 Apple, US developers agree to App Store updates that will support businesses and maintain a great experience for users Apple 宣布 App Store 变化：新的沟通规则、小型开发者援助基金等 苹果将允许第三方支付！App Store规则更新，但网友却不买账 全文完 ","date":"2021-08-29","description":"大家认为 AppStore 已经开放第三方支付了，奔走相告，很开心。但实际上呢？并不是。","lastmod":"2021-08-29T02:53:35Z","slug":"ios-payment-methods-outside","tags":["gamenote","reading"],"title":"App Store 开放第三方支付？假消息","url":"https://blog.zengrong.net/post/ios-payment-methods-outside/"},{"categories":["use"],"content":"今天在算账的的时候，发现有个字段始终对不上。找了半天才发现，原来财务给我的表里面用的是「账号」，我的统计程序里用的是「帐号」。\n这两个词当然是有区别的，我来咬文嚼字一下。\n帐号 根据 新华字典 的解释，「帐」的含义是：\n帐 帳 zhàng〈名〉\n(形声。从巾,长声。巾,麻丝织品。本义:篷帐,有顶的篷帐)\n同本义 [canopy;curtain]。一种张挂或支架起来作为遮蔽用的器物。通常用布帛毡革制成\n帐,帱也。——《说文》 帱谓之帐。——《尔雅》 在上曰帐,在旁曰帷,禅帐曰帱。——《何承天纂要》 项羽晨朝上将军 宋义,即其帐中斩 宋义头。——《史记·项羽本纪》 战士军前半死生,美人帐下犹歌舞。——唐· 高适《燕歌行》 又如:帷帐(帷是四周相围而无顶的篷帐;帐是有顶的篷帐);帐具(陈列帷帐入筵。指备膳、帷帐和膳具);帐门(篷帐的出入口);帐下(营帐中;将帅的部下);帐内(军幕中的将佐);帐天(布篷);帐落(游牧部落聚居之处);帐殿(作为行宫的帐幕);帐幄(帐御。帷帐衣服等);帐饮(在郊野张设篷帐,宴饮送别);帐饯(设帐置酒饯行)\n蚊帐,床帐 [mosquito net]\n帐,张也,张施于床上也。——《释名》 偷夜解齐将军之帱帐而献之。——《淮南子》 又如:帐檐(帐荫子。帐子前幅上端下垂如檐,用作装饰的横幅);帐钩(床帐的金属框架)\n古代游牧民族计算人户的单位 [household]。因他们逐水草而居,每户住一顶帐篷,故按帐计人户数\n帐者,犹中国之户数也。——《后汉书》 从上面的最后一个解释可以看出，「帐」的本义，可以指帐篷的数量，也就是户数。在产品中要表达用户的时候，应该使用「帐号」。\n这个本义在《康熙字典》中也有说明：\n《史記·高帝紀》復留止張，飮三日。《註》張，幃帳也。　又計簿也。《前漢·武帝紀》明堂朝諸侯，受郡國計。《註》計，若今之諸州計帳也。\n微信的「设置」界面使用的就是「帐号」：\n网易邮箱也使用了「帐号」：\n账号 新华字典 对于「账」的解释如下：\n账 賬 zhàng〈名〉\n(古作“帐”。形声。从贝,长声。从贝,表明与财富有关。本义:账目,关于银钱财物出入的记载) 同本义 [account] - 每年造僧账二本,其一本奏闻,一本申祠部。——《旧五代史》 又如:账略(简要的账目) 债务,欠别人的东西(如金钱、货物等) [credit;debt]。如:还账;欠账;拉账;折账;冲账;赊账 账簿 [account book]。如:账箱(专为放置账簿、银票、单据等物品的箱子) 从上面最后一个解释可以看出，「账」应该是指和财产相关的账目。在银行和金融相关的产品中要表达与钱相关的户头时，应该使用「账号」。\n古时候，是用「帐」来代表账簿的，现在则更多用账来表示和财富相关的内容。\n招商银行APP中使用的就是「账号」：\n支付宝的「设置」界面使用的也是「账号」：\n或许这就是支付宝做不好社交的原因吧（笑）？ 😃\n多说两句 所以「帐号」和「账号」不应该通用，两个词有较为明确的含义。但日常生活中，有很多人不知道两个词的精确区别，经常性使用「账号」去替代「帐号」，以讹传讹导致了大众认知的变化。\n比如现在大家经常使用「阀值（错误用法 ）」来替代「阈值（正确用法）」，挺让人难受的。每次在公司开会的时候，我只要听到有人用「阀值」，都会立刻跳出来纠正。\n但是，有些字的用法已经永远回不去了。作为一个师范生，我当年学习《语文基础知识》和考普通话的时候，老师会严格纠正我们的发音。\n比如「呆板」，之前在新华字典中的发音是 áibǎn。「确凿」，之前在新华字典中的发音是 quèzuò。可现在这两种古老的读法都已经是错误的了。\n下面是搜狗输入法的错误提示（我用的是双拼）：\n文化和语言，要符合普通大众的认知。并不是所有人都想知道茴香豆的茴字有四种写法。\n虽然我并不期待这样，但或许以后「阀值」也会进入新华字典？\n毕竟连 guanxi 都被收录进入牛津词典了啊！\n如果在英文中找对应关系，「帐号」应该对应 ID(identification)，「账号」应该对应 Accounts。对吧？\n全文完 ","date":"2021-08-14","description":"Article description.","lastmod":"2021-08-14T09:02:01Z","slug":"id-and-account","tags":["reading"],"title":"帐号和账号有啥区别？你肯定不知道！","url":"https://blog.zengrong.net/post/id-and-account/"},{"categories":["impressions"],"content":"本文题图为陆家嘴外滩观光隧道。一生一次的体验 😂。\n7 月 28 日到 8 月 2 日，因为 China Joy 召开，我去上海转了一圈，见了许多新老朋友。\n每一年 CJ，都是一次大型的网友（基友）见面会，大量的信息汇聚在一起，脑袋都要爆炸。每次 CJ 回来，我会花大量时间整理谈话内容。这些优秀同行们的想法和经验，总能让我得到认知上的提升。\n从上海回到武汉，已经过去了一周。武汉防疫进入了新的阶段。继去年的 在线办公 之后，SAGI GAMES 开启了第二次居家办公。 现在，是时候整理一下，把我近期的一些思考写出来了。\n做精品还是做流量，并不是选择题 精品有精品的方式，流量有流量的玩法。\n就小游戏而言，有把误触做成艺术的方案，也有蹭热点的方案。是看到奥运会蓝色小人儿很火做一个全民运动会吸一波奥运会流量更好，还是去买一个奥特曼 IP 做奥特曼超人机甲更好？全看自己的选择。\n你也可以默默长线维护一个冷门方向，让收入慢慢起飞。最好抛弃一夜暴富这种不切实际的理想，踏踏实实赚钱发工资。说不定哪天就暴富了呢？\n精品和流量，都要做。\n做精品，不应该对流量表现出不屑。我在 CJ 参加了一些独立向的会议，在制作人这个级别，大家依然对游戏流量来源表现出强烈的渴望。在 Steam 和 TapTap 上，吸引流量的手段和商业游戏在 AppStore、Google Play 上并无二致。面向海外的独立游戏发行，总是绕不开 Facebook 群组，Reddit 论坛，Youtube 频道和落地页素材更新。\n流量不是原罪，只要想把产品推向市场，想让更多的人知道，你就必须知道如何用高效的方式，更低的价格获取足够的流量。\n买量只是获取流量的一种方式，它可能是比较容易的方式，或者比较工业化的方式，但肯定不是最高效和最经济的方式。买量这种流量获取方式会越来越贵，会逐渐吃掉产品的所有利润。\n解决这个困局的唯一方案，就是理解流量的运作方式，找到获取低价甚至免费流量的方法。\n纯 IAA 变现的游戏还能做吗？ 总有人问我，为什么不做网赚？我的回答是，我们没有那个能力。\n到了 2021，纯广告变现游戏（IAA，App 平台），好像只有网赚在大把赚钱了？买量成本的不断高企，让次留 50+%，平均广告次数 25+ 的精品游戏都能亏掉发行老本，可想而知 CPI 有多高了。\n好多网赚的朋友说，你接入我们这个网赚 SDK，就能让 30% 次留的产品瞬间提升到 50+%，然后就只管分钱就可以了！可我总是固执地认为，网赚是另一条赛道，想要做好，要仔细研究用户心理和产品呈现方式，网赚玩家的行为方式和游戏玩家可是完全不同的。\n就像画马一样，显然不是接入一个 SDK 就能解决所有的细节问题，如果不知道具体是怎么挣钱的，我又怎么可能挣到钱呢？\n如果没有顶尖的买量技能（在T1 国家买到 $0.3 以下，在国内买到 RMB2以下），我觉得在近期，纯 IAA 游戏可以选择下面几个方向：\nFacebook 小游戏、Google Play Instant 和海外 HTML5。 国内小游戏的非中心化平台（例如字节系）。中心化分发的平台流量平均化和极端化严重，对于小 CP 可能会有点儿难做。 字节的摸摸鱼平台和 233 乐园。 你肯定已经注意到了，上面几个平台都具有相同的特点：低 CPI。当然，低 CPI 也可能会导致低 eCPM。但相信我，都有机会的。\n注意，这里谈到的游戏，主要是指「流量型」游戏，如果是那种次留 50+% 广告次数 25+ 的精品，还是应该去标准平台试试的。如果产品风格合适，海外也是不错的选择。\n除了想方设法降低 CPI，IAA 游戏要考虑的另一个重要因素就是 eCPM。对于 IAA 变现，我们应该全力面向 eCPM 和用户画像做游戏，死去活来地寻找提升 eCPM 的方法，跟随流量潮汐的规律，寻找让产品破圈的方案。\n休闲游戏混合变现之难 混合变现当然需要版号。比较普遍的做法是这样：\n确定 IAA 和 IAP 的比例； 按确定好的比例设计游戏； 在海外进行测试和上线，同时申请版号； 版号下发，回国内发行。 要实现上面的流程，需要 CP 能给产品提供稳定的测试流量，最简单的方案就是建立自己的买量团队。可以参考这篇文章： 小CP如何搭建发行团队。\n但有的 CP 没有自己的买量团队，而且只做面向国内的产品，流程就变成这酱紫：\n如果要在国内测试，就要版号； 如果没有版号，就只能做纯广告变现（IAA）的游戏； 如果做 IAA 游戏，产品设计i和经济体系就是面向广告设计； 如果申请的版号下来了，要做混合变现，就要对游戏进行全面的修改； 之前的测试数据没用了。 都 2021 年了，为什么还有产品因为没有版号而暂停开发，有团队因为等不到版号而解散？真的挺可惜的，自建买量团队并没有那么难，招不到人的话老板可以自己上嘛。\n随着 苹果取消 IDFA 支持，以及 计算广告 行业的发展，买量成本一定会越来越高。混合变现游戏能够支撑更高的 CPI，为开发者带来更高的收益。如果没有 IAP，CPI $150 怎么赚得回来呢？。\n自有流量池的重要性 最近郭嘉针对在线教育进行了调控，对在线广告行业也在进行整顿。现在又开始策划三胎政策，所有影响生娃，制造内卷的行业都可能受到影响。8 月 3 日央媒发表了游戏「精神鸦片」论，8 月 6 日北京海淀检察院对微信青少年模式提起民事诉讼。\n对游戏行业的调控或将到来。\n游戏行业需要依法合规，版号的重要性不言自明。在这样的前提下，买量的成本会更高。\n休闲游戏 CP 需要构建自己的流量池，有提供低价甚至免费的流量的能力，对自己的产品进行灵活测试，快速决策，保持掌控感很重要。\n找到适合团队基因的「蓝海」品类 前几天看到这篇文章，内心很震撼：Zynga 5.25亿美元收购中国手游公司StarLark：代表作Golf Rival。\n《Golf Rival》是一款休闲PvP游戏，有非常好的成长空间和高度社交功能，Zynga 花 $3.15 亿现金 + $2.1 亿股份收购了其开发商 StarLark，且对这个价格感到满意。\n坦白地讲，我刚玩到 Golf Rival 的时候，并没有想到这是一家中国公司的产品。游戏质量当然非常好，但因为是很冷门的赛道，我没有想到这个产品的收益如此高，甚至都没有仔细去研究这个产品。\n即使是在动不动就谈 3A、工业化、元宇宙，仿佛没有一个小目标就不能立项的 2021 年，依然有蓝海赛道。StarLark 就是一个很好的例子。坚守 TD 赛道的 IRONHIDE 也一直在自己的蓝海深耕。在他们的大海里，没有太多竞争。\n这些赛道，「大厂」们或许看不上，或许认为赚钱效率不高。毕竟手握大把 IP，成功概率更高。\n但对于小 CP 而言，这样的赛道能带给我们足够的弹药，更充足的时间，以及更冷静的思考。\n不扎堆，不聚集，好好做游戏。\n2020 年 CJ 相关文章 CJ五日-Unity能做小游戏？ CJ五日-腾讯爸爸抱紧发行爸爸，CP爸爸在数钱 CJ五日-殊途同归：Habby、凉屋和青瓷的游戏之道 相关文章阅读 时间有限，本文大量细节不便展开，欢迎讨论。关注公众号「曾嵘胡扯的地方」进入菜单可加我微信。\n以下是我从微信小游戏开始创业之后，所撰写的与行业相关的文章。一路走来，文如本人，真情流露，仅供参考。\n小CP如何搭建发行团队 广告归因：苹果ATT对归因的影响 休闲游戏的机会 赌一个未来：从《糖豆人》看小游戏CP的出路 中小 CP 的游戏梦 面对苹果爸爸的版号暴击，2020 小 CP 怎么活？ 超休闲/小游戏是否还有机会？ Supercell 十年，中度休闲游戏增长 做个靠谱的微信小游戏 CP 微信小游戏：风口还是泡沫 全文完 ","date":"2021-08-11","description":"我在 2021 China Joy 的见闻，所思，所想","lastmod":"2021-08-11T08:20:37Z","slug":"2021cj","tags":["gamenote","chinajoy"],"title":"CJ五日-更严监管之下，休闲游戏小CP的破局思考","url":"https://blog.zengrong.net/post/2021cj/"},{"categories":["technology"],"content":"今天写分析系统的时候看到一段关于 sum 的用法，有点意思：\n1fruits = [\u0026#39;Apples\u0026#39;, \u0026#39;Pears\u0026#39;, \u0026#39;Nectarines\u0026#39;, \u0026#39;Plums\u0026#39;, \u0026#39;Grapes\u0026#39;, \u0026#39;Strawberries\u0026#39;] 2 3data = {\u0026#39;fruits\u0026#39; : fruits, 4 \u0026#39;2015\u0026#39; : [2, 1, 4, 3, 2, 4], 5 \u0026#39;2016\u0026#39; : [5, 3, 3, 2, 4, 6], 6 \u0026#39;2017\u0026#39; : [3, 2, 4, 4, 5, 3]} 7 8counts = sum(zip(data[\u0026#39;2015\u0026#39;], data[\u0026#39;2016\u0026#39;], data[\u0026#39;2017\u0026#39;]), ()) 这段代码来自于： bar_nested_colormapped.py，有兴趣的同学可以去看看完整代码。\n我对 sum(zip(data['2015'], data['2016'], data['2017']), ()) 这段代码产生了好奇，我自己是从来没有这么用过，一下没看出来这段代码产生的结果是啥。\n跑一下程序，发现 counts 的值为：\n1In [29]: sum(zip(data[\u0026#39;2015\u0026#39;], data[\u0026#39;2016\u0026#39;], data[\u0026#39;2017\u0026#39;]), ()) 2Out[29]: (2, 5, 3, 1, 3, 2, 4, 3, 4, 3, 2, 4, 2, 4, 5, 4, 6, 3) 好吧，这段代码的作用就是把一堆 tuple 拼接起来。\n根据 sum 的 官方文档，我们知道，sum 的作用是对一个 iterable 进行求和操作。\n求和怎么就变成拼接了呢？拆开来一步步看。\nzip 将 3 个等长的 list 分成 6 个元组，每组 3 个元素。\n1In [58]: print(*zip(data[\u0026#39;2015\u0026#39;], data[\u0026#39;2016\u0026#39;], data[\u0026#39;2017\u0026#39;])) 2(2, 5, 3) (1, 3, 2) (4, 3, 4) (3, 2, 4) (2, 4, 5) (4, 6, 3) 这里 sum 的含义误导我了，因为这些元组中的元素正好都是数字，让我一直思考它们为啥不加起来反而拼起来了。\n因为正常情况下，sum 是这么用的：\n1In [59]: sum((1,2,3)) 2Out[59]: 6 但当我去掉 sum 的第二个参数——一个空元组的时候，代码会报错：\n1In [60]: sum(zip(data[\u0026#39;2015\u0026#39;], data[\u0026#39;2016\u0026#39;], data[\u0026#39;2017\u0026#39;])) 2--------------------------------------------------------------------------- 3TypeError Traceback (most recent call last) 4\u0026lt;ipython-input-60-aaab535addac\u0026gt; in \u0026lt;module\u0026gt; 5----\u0026gt; 1 sum(zip(data[\u0026#39;2015\u0026#39;], data[\u0026#39;2016\u0026#39;], data[\u0026#39;2017\u0026#39;])) 6 7TypeError: unsupported operand type(s) for +: \u0026#39;int\u0026#39; and \u0026#39;tuple\u0026#39; 看到这个报错，我才明白过来，这里「求和」的含义是广义的，它不仅仅针对数字，也针对所有实现了求和运算符 + 的情况。\nsum 的第二个参数必须和 iterable 中的元素类型相同，且只要实现了求和运算符 + 就可以被 sum 使用。\n我们可以将 sum 用在数字上：\n1In [62]: sum((1,2,3), 4) 2Out[62]: 10 也可以用在元组中，因为元组也实现了求和运算符：\n1In [74]: sum(((2,3), (4, 5)), (0,1)) 2Out[74]: (0, 1, 2, 3, 4, 5) 用在 datetime 对象中也是可以的：\n1from datetime import date, timedelta 2In [86]: sum((date.today(),timedelta(days=30)), timedelta(days=2)) 3Out[86]: datetime.date(2021, 8, 5) 但有趣的是，Python 不希望我们把它用在字符串上，尽管字符串也是 iterable。这应该是出于性能考虑：\n1In [76]: sum(\u0026#39;love U\u0026#39;, \u0026#39;I \u0026#39;) 2--------------------------------------------------------------------------- 3TypeError Traceback (most recent call last) 4\u0026lt;ipython-input-76-c45d4933da5d\u0026gt; in \u0026lt;module\u0026gt; 5----\u0026gt; 1 sum(\u0026#39;love U\u0026#39;, \u0026#39;I \u0026#39;) 6 7TypeError: sum() can\u0026#39;t sum strings [use \u0026#39;\u0026#39;.join(seq) instead] Python 官方文档也提到，如果要连接 iterable，建议使用 itertools.chain() ：\nTo concatenate a series of iterables, consider using itertools.chain().\n所以我也试了试（由于返回的是生成器，我用 tuple 将其转换为元组方便查看）：\n1import itertools 2In [79]: tuple(itertools.chain(*zip(data[\u0026#39;2015\u0026#39;], data[\u0026#39;2016\u0026#39;], data[\u0026#39;2017\u0026#39;]))) 3Out[79]: (2, 5, 3, 1, 3, 2, 4, 3, 4, 3, 2, 4, 2, 4, 5, 4, 6, 3) 上面的过程告诉我：\n脑子笨就是反应慢。 先入为主的思维方式要不得。 老代码害死人。 要经常看文档。 新写法能提升性能。 你甚至啥也不用干。这就叫躺赢。 欧耶！ 全文完 ","date":"2021-07-04","description":"Python 中的 sum","lastmod":"2021-07-04T08:21:24Z","slug":"sum-in-python","tags":["python"],"title":"老法新用：Python 中的 sum ","url":"https://blog.zengrong.net/post/sum-in-python/"},{"categories":["technology"],"content":"前几天一个创业朋友 Q 同学咨询我如何学习做游戏。这是个很普遍的问题，经常会有其他行业的程序员向我咨询转到游戏行业应该做什么准备，所以我把回答整理了一下，希望对大家有用。\nQ 同学问 小弟最近有一个想法，想要做一个网页联网的2D（或者伪3D）大富翁游戏。主要目的是：\n（1）增进一下技术，了解一下相关的技术栈。 （2）未来有在这个领域创业的想法。不想把技术钻的太深，未来产品可能会找外包团队，但是自己要懂一些，可以做技术管理。 我想要通过做这个网页联网2D（或者伪3D）大富翁游戏把下面的技术串一串，实践一下：\n（1）网页游戏开发，框架，引擎。 （2）联网游戏的服务器同步机理。 （3）微服务，Docker（当然我不知道游戏服务器是不是在做这种微服务和伸缩设计）。 （4）即使不做游戏，想要复习一下快速构建网络·服务的过程，以及相关的工具链（以 NodeJs 工具链为主）。 我现在的技术栈大概是：python写得多，java，c++复习一下会写，Js曾经比较熟，sql二把刀。网站的话，自己带几个人开发过最普通的低并发业务网站，用的 Flask，Jinjia，Js，Reddit，阿里云，了解服务器端的简单开发（不涉及到大量中间件，复杂数据结构，对数据实效性基本上没有要求），运维了解一些。也用 Vue，Express，webpack 等做过业务网站。\n由于我是第一次做游戏，所以我不敢自己贸然行动。我想要请大大帮我梳理一下整个学习，工程的行进路线，包括资料（书，网页，教程，文档），大概的技术方向（框架的选择，技术的选择，架构的选择等），每一步学习的大方向，每一步开发的大方向。有了这份指导，我再去学习相关的知识，我想一定可以事半功倍！\n作为 SAGI GAMES 的创始人，我想先声明，如果你不爱游戏，不玩游戏，那么最好不要转行做游戏。是的，游戏行业钱多薪资高，但游戏行业也出了名的极难成功。你看到的成功项目，背后可能是上百次失败。如果没有对游戏本身的热爱，你很难坚持下来，更无法承受游戏开发过程中的不确定性，直到最终等到一个成功。\n我的回答 Q，先聊一下你的问题吧。\n以你目前的积累和学习能力，我认为自学游戏开发是肯定没有问题的。你希望得到一些指导，可能是因为这个领域从来没有接触过，自己心里有点虚（很正常的心理）。另外也如你所说，有一些指导可以少走弯路事半功倍。\n但我相信你心中一定有自己的方法。\n我这些年积累了一些学习的方法论，但并没有非常系统地写出来，现在正好是个机会，我来做一个简单的梳理。\n渔 下面是我写过的一些文章，可以先读一遍：\n如何学习一门新技术： 这篇比较新，很全面的讲解了我自己学习技术的方法论，可以仔细读一下。 移动游戏客户端技能树： 这篇是我在带技术团队的的时候写的，主要针对的是游戏客户端技术，服务端技术有一些，但没有作为重点。 Python 入门建议： 这是我学习 Python 的时候写的一篇，你主要关注思想和方法论层面就好。 如何学习 cocos2d-x ？： 我在学习 Cocos2d-x 时写的一篇，Cocos2d-x 是 Cocos Creator 之前的引擎，使用 C++ 开发。所以我当时把 C++ 重学了一遍，但主要还是写 Lua。 这篇文章中提到了 OpenGL 的部分，是游戏客户端的核心（当然不学OpenGL也可以做出好游戏的）。 真正的程序员应该能自己解决问题： 这篇是我之前在一个论坛中的讨论记录，能代表我在技术学习上的思维方式。 你可能注意到了，上面我提供的都是「渔」而非「鱼」。因为我认为，到了你这种高度的学习者，提供「渔」的效率更高。你有自己的学习方法论，直接给你「鱼」，反而会影响你的学习效率。\n我在 如何学习一门新技术 一文中提到技术的定义： 可以通过现有资料进行全面掌握的解决实际问题的知识与能力。\n从上面的定义可以看出，由于技术解决的是 实际问题，不可避免地，技术会根据现状的变化出现过时的情况。由于可以 通过现有资料全面掌握， 时效性就显得尤为重要。\n所以，我给你太多「鱼」，就是太多余。\n鱼 为了让你能减少试错成本，下面就是必要的「鱼」的内容。\n首先是开发引擎选择。\nPython 目前你很熟悉，基于 Python 的游戏引擎是有的，但我不推荐。基于 C++/JAVA 的我更不推荐。\n我推荐两个引擎： Cocos Createor (CCC）和 Unity。\nCCC 是国产软件，国内三大 H5 引擎之一。CCC 可以开发 2D 和 3D（目前较弱） 游戏，使用 JS 或者 TS 开发，产品可以发布到微信等小游戏平台，可以作为 H5 页面发布，也可以打包成安卓或者 iOS 原生应用程序。\nUnity 是市场占有率超过 50% 的游戏引擎。可以开发 3D 和 2D 游戏，使用 C# 开发，也有社区支持的 Lua 版本。面向安卓/iOS/PC/主机平台。\n至于你应该选什么，主要应该基于市场和平台考虑。我的建议如下：\n如果你想快速试错，前期希望用 2D 游戏练手，建议先用 CCC。平台建议选择微信小游戏平台，可快速上架面向用户，流程简单顺畅。当然你也可以直接基于 H5 来发布。 如果你想做 3D 游戏，面向 AppStore 或者安卓市场，就应该从 Unity 上手。 其他的引擎，无论如何都不建议你用。等把这两个引擎吃透了再去看其他引擎的源码就行了。 Unreal 更是千万不要碰。你现在需要的是 DEMO。 根据你的前面的描述，2D 或者伪 3D，网页，相信你已经有选择了。\n其次是书籍的选择。\n引擎学习的部分，直接看官方文档就好了。不需要买什么书。买了也是浪费钱。\n如果想深度钻研一下技术，可以看这几本：\n《游戏引擎架构》 https://book.douban.com/subject/34864920/ 《OpenGL编程指南(原书第8版)》 https://book.douban.com/subject/26220248/ 《OpenGL ES 3.0编程指南》 https://book.douban.com/subject/26414014/ 设计的部分，看这几本：\n《全景探秘游戏设计艺术》 https://book.douban.com/subject/4837351/ 《游戏设计艺术（第2版）》 https://book.douban.com/subject/26791007/ 《通关！游戏设计之道（第二版）》 https://book.douban.com/subject/26905728/ 《游戏设计的236个技巧》 https://book.douban.com/subject/26653173/ 《游戏人工智能编程案例精粹》 https://book.douban.com/subject/19930152/ 《游戏编程中的人工智能技术》 https://book.douban.com/subject/1831698/ 需要注意的是，对于游戏开发初学者来说，设计的部分应该远远比技术的部分重要。 但遗憾的是绝大部分初学者往往认为是反过来的。\n代码再漂亮，也拼不出一个好游戏。\n另外，你并非一定要看完上面的书才能开始动手写游戏。实际上，我建议你看完引擎文档就开始动手设计游戏。\n上面的书，只是告诉你，当你期望向上一部分的时候，方向就在那里， 不要走错路。\n最后，讲一下我认同的个人游戏开发方法论。\nDEMO 阶段，奉行拿来主义，能去买的素材就不要自己画，能拿来主义的地方就不要自己造轮子。\n最小化原则作品，先把作品 run 起来，比优雅的代码，整洁的界面，完善的设计带给你的信心要大很多很多很多很多倍。\nDEMO 出来了之后就要立即找到受众测试。游戏是做给别人玩的，不是做给你自己欣赏的。只有别人（除你之外的所有人）喜欢玩的游戏才是好游戏。\n游戏不是艺术，游戏是娱乐。\n等你做出一个成功的娱乐游戏之后，再来考虑艺术的事情。\n全文完 ","date":"2021-06-15","description":"一个普通程序员如何成为一个游戏程序员？","lastmod":"2021-06-15T06:54:24Z","slug":"how-to-build-a-game","tags":["howto","study","choice"],"title":"程序员如何学习做游戏？","url":"https://blog.zengrong.net/post/how-to-build-a-game/"},{"categories":["impressions"],"content":"本文是我在 5 月 13 日在独立出海联合体武汉分享活动中的演讲内容。\n演讲 35 分钟，分享 SAGI GAMES 构建发行团队，帮助 B 级产品变现，提升与优秀发行合作能力的经验和过程。\n经验有限，错误不少，请多指教。\n以下内容并非逐字稿，而是对演讲内容的简化和要点提炼。\n1. SAGI GAMES 简介 参照下文：\n来 SAGI GAMES，一起创造长期伟大的游戏\n2. CP 的痛点 CP 的主要工作是产出产品，对于产品来说，最重要的三板斧是 选品、研发、发行。\n选品是解决「做什么」的问题； 研发是解决「怎么做」的问题，在真正的研发过程中，CP 更纠结于「怎么做才好」的问题； 发行是解决「怎么卖」的问题，我们今天的内容就讨论这个问题。 CP 在和发行商合作的过程中，经常是「双眼一抹黑」的。主要表现在三个方面：\n不知道产品的具体数据，也不知道如何对产品进行调优。 当发行说「这个产品不行」的时候，CP 很多时候只能放弃产品。CP 认为每个产品都是自己的孩子，不懂发行思维，连孩子怎么死掉的都不知道。 如果 CP 做出 A 级产品，但碰到了能力不匹配的发行，会导致 A 级产品也发不起来，好好的 A 级产品砸到别人手里。如果 CP 做出的是 B 级产品（绝大部分属于这种），大概率找不到发行，这就把产品砸在自己手里了。 3. 为什么要自建发行团队 掌握自己产品的核心数据 上面提到过 CP 的痛点之一是「不知道产品的具体数据」。SAGI GAMES 的产品数据是由策划负责梳理的，有了这些实时数据，我们就能掌握自己的产品的核心机密。\n学会基于数据调产品 拿到产品的核心数据之后，产品团队通过详细分析，调整产品，增加投放，得到产品的正循环。\n利用数据协调各部门之间的融合 上面的三张报表分别是 SAGI GAMES 某项目的运营数据报表、海外投放数据报表、国内投放数据报表。这些报表每日会在项目群中同步，项目所有成员都可以看到。\n运营和投放关注的数据点是不同的。例如投放同学更关注 CPI 和 7 日 ROAS，但运营同学更关注付费率和留存。海外投放同学还会将多个国家的数据做分群计算。\n以下是在 SAGI 发生的真实场景：\n晨会，一位美术同学问，为啥最近 CPI 涨了？投放的同学站出来说，是因为最近处于国外的假期，买量成本普遍上升……\n在这个过程中，所有项目组成员能深入理解他人的工作方式，基于数据的决策更加准确，减少了沟通中的信息流失。\nB 级产品变现 CP 和发行，达成双赢，发行才愿意代理 CP 的产品。而 B 级的产品可能无法让 CP 和发行同时赚到钱，此时由 CP 自己变现可能更划算。\n前几天看到这篇文章 被头部发行打回6款产品，自研自发下载量过亿的武汉团队 中提到 CP 自发成功的例子，单款产品在海外达到 1 亿下载量，是一个武汉 CP 自己发行的。他们在和发行合作的过程中，发行认为这个产品是不错的产品，但发行不会代理，建议 CP 自己尝试发行，CP 尝试后也获得了成功。\n虽然产品不满足他们的数据标准，他们不会发行。但这依旧是一款好游戏，建议我们尝试自己发行，也可以获得理想的收益。 “直到今天，我都很感谢那位对接人，如果没有他的认可，也许就不会有现在的成绩，团队的信心更会遭到严重打击，可能今天的采访也就不会存在了”\n对于 CP 来说，信心无比重要，一款产品只要能覆盖成本，就是不错的成功，能给 CP 带来继续开发下一款产品的信心。\n用发行的思维方式，与优秀的发行合作 理想中 CP 和发行的合作是亲密无间的。\n但现实中 CP 和发行的关系就像武汉四月的天气，穿羽绒服的同学和穿短袖的同学互看一眼，心中默默说了一句煞笔然后决然离去。\nCP 和发行合作的目标是一起赚钱，但由于思维方式的不同导致处在各自的平行宇宙，是时候回归核心目标了。\n发行最重要的能力 什么时候可以投：时间、时机、量级。 什么时候应该停：慢就是快，少就是多。 如何实现利益最大化：避开锋芒，省到即赚到。 如何得到更便宜（或者免费）的量？ 如何理解用户：地域差异，消费差异，喜好差异。 怎样调整特定产品：基于用户特点和 CP 特点，考虑时间成本。 …… 发行是个技术活，经验丰富的发行要理解和整合大量的信息，并非有钱就可以做好。\n如果 CP 自己有足够的资本，也掌握一定的发行技巧，是否就可以不必和发行合作了呢？这里要根据一个优秀发行最重要的能力来判断，我认为，优秀的发行最重要的能力可以用一个词来概括：放大。\n如果 CP 有一个优秀的产品，希望将该产品做到其可以承受的最大量级，那么就应该寻找一个有同类产品发行经验的优秀发行来合作。因为放大不仅仅是钱的问题，还涉及到效率问题和节奏问题。这些经验是多年积攒起来的，是一个优秀发行的核心竞争力。\n至于普通产品（B 级产品），CP 可以用自己的团队尝试投放，这样能取得更大的收益，同时起到锻炼团队的目的。\n4. 怎样搭建发行团队 发行团队应该由下面几个部分组成：\n懂产品的商务团队 懂优化的投放团队 懂数据的运营团队 懂合作的产品团队 懂产品的商务团队 SDK跟进 最新政策跟进 渠道门儿清 和优秀发行建立良好关系 与CP 建立信息共享通道 商务团队必须专业，要懂自己的产品，也要懂行业的产品。要了解产品接入的所有 SDK 的大概情况，坑在哪里。\n商务团队要了解最新政策，例如知道苹果最新版本的 ATT 政策，Google Play 的 85% 分成与苹果的 85%有啥区别，如何申请等等。\n商务团队要知道，有哪些与渠道合作的方法，每个渠道的大概分成比例和合作方式，渠道自然量如何，哪些渠道质量比较高。\n商务团队要知道哪些大的发行和渠道有比较好的活动和计划，各家发行喜欢什么样的产品，他们现在需要什么样的产品。\n商务要和本地 CP 联系，取长补短，互通有无。\n懂优化的投放团队 优化师（技术岗） 广告平台特点 产品数据特点 突发事件处理 素材剪辑师 素材归类 质量归因 投放团队除了会花钱，还要会优化。优化师是技术岗，要知道不同广告平台的 eCPM、CPI、用户质量，不同产品在不同平台的数据表现。\n优化师还需要具有处理突发事件的能力。例如，BM 账户被封如何处理，Facebook 目前对投放有何限制。\n素材剪辑师除了需要了解到目前的投放素材风格的变化趋势之外，还需要根据自己的产品特点提前完善素材库，要知道一个优秀素材背后的原因是什么。\n懂数据的运营团队 产品活动设计 投放数据分析 产品数据分析 用户数据分析 游戏日常运营 运营团队是产品数据系统的中心。无论是产品数据、用户行为数据还是投放数据，运营团队都必须综合起来进行分析，才会有准确的判断。\n懂合作的产品团队 做好内容设计和开发 控制产品节奏 配合投放、商务、运营团队的工作 其他团队提出的优化方案，需要产品团队一一实现。「懂合作」对于产品团队至关重要，完全理解其他团队的工作，是合作顺畅的基础。\n感觉合理的团队关系 我们可能觉得这四个团队之间的关系是平行关系，大家井水不犯河水，各做各的事情，到了需要沟通的时候开个会说一下。\n真实高效的合作关系 但实际上，一个高效的团队，各部门之间的关系应该是重叠关系。衡量一个团队工作的高效程度，就是观察在重叠部分有没有人主动站出来承担责任。\n5. SAGI HOW TO 下面简单聊一下 SAGI 是怎么做的。\n定性：找到 SAGI 的基因 在做小游戏时，SAGI 经历了下面几个历程：\n研发……开心被嫖 自发……懵懂无知 发行……一地鸡毛 离场……遭遇碾压式的降维竞争 随后，SAGI 尝试了超休闲游戏，与优秀的发行合作，熟悉了超休闲游戏的创意和测试的全套流程，学到的最重要的经验是：知道什么时候放弃。\n2019年，SAGI 开始尝试中度休闲游戏，确定了 IAP+IAA 混合变现的中度休闲游戏的开发方向。版号是限制也是机遇。产品先选择出海，待版号通过后出口转内销。这样的节奏给产品带来了更多的调优时间，但国内和海外市场的不同也给产品团队带来更大的挑战。\n定量：SAGI方法论 下面的方法论，SAGI 也在学习中，想要结构化的实现自己的发行方法论，在做好产品本行的同时，还要需要努力学习 广告 知识，构建自己的 BI 系统。单纯的买量已经不能满足游戏发行的需求了，优秀的游戏公司会把内容营销、直播、KOL、KOC以及自传播这几个手段融合在一起，建立一个复杂的营销模型。\n产品中心……选品方法论，确定适合自己基因的产品 运营中心……产品 BI，产品人是个好运营 投放中心……投放 BI，优化师是技术岗 流量中心……私域流量、ASO、KOL、KOC、社区运营 全文完 ","date":"2021-05-18","description":"本文是我在 5 月 13 日在独立出海联合体武汉分享活动中的演讲内容。演讲 35 分钟，分享 SAGI GAMES 构建发行团队，帮助 B 级产品变现，提升与优秀发行合作能力的经验和过程。","lastmod":"2021-05-18T02:31:15Z","slug":"cp-and-publication","tags":["sagiteam","presentation"],"title":"小CP如何搭建发行团队","url":"https://blog.zengrong.net/post/cp-and-publication/"},{"categories":["impressions"],"content":"本文是阅读 《计算广告 第2版》 的第 3 篇读书笔记。所有读书笔记请访问这个 TAG： 《计算广告》。\neCPM 是什么 eCPM 可谓大名鼎鼎，它是计算广告的核心量化指标。《计算广告》这本书大部分计算都围绕它展开，纯广告变现（IAA）游戏最关注这个指标。\neCPM(expected Cost Per Mille) 代表每千次展示期望收入。\n曾嵘：\n这里介绍一下 mille 这个词，我觉得有点意思。\nmille 不单独使用，它会和 per 一同使用组成 per mille ，来自拉丁文 per mīlle ，代表 每千次，有一个专有的关联符号 ‰，注意，这个符号 不是 百分号 % 。\n还有一些变体拼写：per mil, per mill, permil, permill, permille，它们都代表相同的意思。\n这个词组在英文中非常少见，不会出现在大部分的主要英文字典中。因为这些正统字典不承认它的拼写。我专门查了一下 Collins、Macmillan 和 Oxford 词典，都无法找到它的解释。\n但这个词组在欧洲语言中很常用，一般用于表达 小于 1% 的数字，例如血液中的酒精浓度。但在英语国家中，一般使用百分比来表达同样的数字。\neCPM 意味着在我们的游戏中，展示一千次广告，我们将获得多少收入。\n例如，在微信小游戏平台中，一般的 eCPM 是 RMB1030。而在 iOS 平台的休闲游戏中，一般的 eCPM 是 RMB200300。\n注意：上面的数据仅用于举例，随时可能根据时间和市场状况发生变化，没有参考价值。\n流量主和广告主 在微信公众平台中，将在产品中展示广告以获取收益的一方称为 流量主，将投放广告获取流量的一方称为 广告主。\n为了方便后面的叙事，我们也使用微信公众平台的这种叫法。\nCPM 和 eCPM CPM(Cost Per Mille) 代表每千次展示成本。\n有了前面的名称统一，这里就很好解释 CPM 和 eCPM 的区别了：\nCPM 与广告主的投入相关。 eCPM 和流量主的收入相关。 例如，SAGI GAMES 的某个游戏在微信广告平台投放广告，当时 CPM 若为 RMB80，那么若广告获得了一万次展示，SAGI GAMES 需要支付广告费 RMB800： $800 = 80 \\times (10000 \\div 1000)$。SAGI GAMES 的身份是 广告主。\n再如，公众号 曾嵘胡扯的地方 展示了微信公众号的广告，若微信公众号广告的 eCPM 为 RMB10，某篇文章获得了十万+访问，其中广告的有效展示次数是八万，那么该文章可以获取收益 RMB800： $800 = 10 \\times (80000 \\div 1000)$。此时曾嵘的身份是 流量主。\n当然，同一个主体可以即是 广告主 也是 流量主。这也是游戏行业的普遍情况。\n曾嵘：\n有时候人们也会将 RPM(Revenue Per Mille)和 eCPM 混用。大多数情况下，它们的含义都是很相近的。\neCPM 是如何计算出来的 对于 流量主 来说，eCPM 当然是越高越好。那么是什么影响了 eCPM 值？让我们来简单分解一下。\n首先要了解几个概念，下面的概念均以游戏行业为例：\n落地页 Landing Page：广告主用来展示自己的游戏产品，提供下载链接的网页。 点击率 CTR(Click Through Rate)：广告点击与广告展现的比率。即用户点击广告的次数，与广告展现次数的比值。在下面的公式中，我们使用 $u'$ 来表示点击率。 到达率：点击行为成功以后，用户被导向到 落地页，落地页成功打开的次数与广告点击次数的比率就是到达率。 转化率 CVR(Conversion Rate)：用户从落地页开始，进一步下载游戏并安装成功，被称为转化。转化次数与到达次数的比率就是转化率。 点击价值 Click Value：单次点击为广告主带来的收益。在下面的公式中，我们使用 $v$ 来表示点击价值。 上图展示了从 广告页 -\u0026gt; 落地页 -\u0026gt; 转化页 的过程。\n需要注意的是，为了提升转化率，绝大多数 iOS 平台上的广告落地页均为 AppStore 下载页面。上图中加入的落地页概念是为了方便理解更一般的流程。\n在游戏行业，一般会更关注安装游戏后的首次打开操作。与网页访问即算作转化不同，下载游戏后的第一次打开才是真正的转化。\n根据上面的讨论，有下面的公式：\n$$ eCPM = u' \\cdot v $$\n注意：此处使用点乘而非叉乘，因为要将 $u'$ 和 $v$ 考虑成多次行为。\n再靠近一点 广告的展示，对于流量主来说是收益，对于广告主来说是成本。我们将其简化为 $r$ 。\n$r$ 与广告本身，广告用户，广告展示的阶段上下文均有关系。\n曾嵘： 要了解广告展示的六个阶段，请阅读 计算广告：在线广告是如何生效的？ 一文。 用 $a$ 表示广告本身，用 $u$ 表示广告用户，用 $c$ 表示广告展示阶段的上下文。$r$ 是 $a, u, c$ 的函数。于是有下面的公式：\n$$ eCPM = r(a, u, c) $$\n合并上面的两个公式：\n$$ eCPM = r(a, u, c) = u'(a,u,c) \\cdot v(a,u,c) $$\n计算广告的核心问题 计算广告的核心问题，是为一系列用户与上下文的组合找到最合适的广告投放策略，以优化整体广告活动的利润。\n如果用 $r$ 表示 总收入，用 $q$ 表示广告主付出的 总成本，用 $T$ 表示广告的 总展示次数，用 $i$ 表示 某一次展示。那么有下面的公式：\n$$ max \\sum^T_{i=1}(r_i-q_i) $$\n从公式中可以看出，我们作为一个合格的广告主，永远应该追求 收入与成本之差（利润） 的最大值。\n关于数学公式 写本篇之前，我一直在考虑是不是应该在文中包含数学公式。\n最后，我认为学习的原始动力来自于好奇心，对于不熟悉的范畴要尽量去学习和理解。旺盛的好奇心，终生学习 也是 SAGI GAMES 的价值观之二。\n而且，《计算广告》 这本书本就包含大量公式。放弃介绍公式，也无法讲清楚这本书的精髓。\n数学公式是描述计算问题最准确的表达方式，用语言和文字描述会失去精准度。我也会尽量对原文中的公式进行精简和筛选，力求准确和易懂。\n下面的提供了一些学习数学公式的连接，希望能帮助大家熟悉这些 科学的咒语。\n参考文章 数学符号表 咒语入门（数学符号入门）神经网络篇 咒语入门（数学符号入门） Reddit 热门话题：如何阅读并理解论文中的数学内容？ How do you read math-heavy machine learning papers? Per mille 全文完 ","date":"2021-04-07","description":"《计算广告》读书笔记：广告收入的分解，eCPM 的本质","lastmod":"2021-04-07T06:10:54Z","slug":"computad3","tags":["reading","readingnote","ad","computational-advertising"],"title":"计算广告：广告收入的分解","url":"https://blog.zengrong.net/post/computad3/"},{"categories":["impressions"],"content":"本文是阅读 《计算广告 第2版》 的第 2 篇读书笔记。所有读书笔记请访问这个 TAG： 《计算广告》。\n从信息接收阶段理解广告生效方式 广告的信息接收过程分为三个大的阶段：\n选择 Selection 解释 Interpretation 态度 Attitude 在这三个大的阶段中，还有六个小的阶段：\n曝光 Exposure 关注 Attention 理解 Comprehension 接受 Accptence 保持 Retention 决策 Decision 所有阶段的关系如下图所示：\n1. 曝光 「位置为王」是曝光阶段的最好写照。为啥大家都想去抢纽约时代广场大屏发朋友圈呢？因为那儿位置好啊！\n当然，咱宇宙中心五道口也不差对不对？\n曾嵘：\n对于互联网广告来说，不一定位置最好点击率就一定高。例如开屏广告的效果是否比首页顶部 Banner 效果更好？这需要考虑载体匹配度、内容匹配度和设计匹配度。\n2. 关注 行为定向原理会考虑以下三点来提高关注效率：\n尽量不要打断用户的任务。当用户明确辨识出固定广告位时，会下意识屏蔽其内容（房间里的大象）； 明确传达向用户推送此广告的原因； 内容符合用户的兴趣或需求。 以手机游戏中三种常见广告为例，以上面三点作为标准来评价广告效果：\n广告类型 1 2 3 Banner 不打断，固定位置 莫名其妙 不一定 插屏 打断，全屏 不解释原因，不确定时机，强迫 不一定 激励视频 用户触发打断，全屏 解释原因：主动获取收益 不一定 从上表可以看出，激励视频 的广告效果是最好的。实际上，以上三种广告中，激励视频的 eCPM 也是最高的。\n3. 理解 即使已经通过大数据定位到某个用户的喜好（例如该用户属于游戏玩家），在推送广告时，如果该用户不是此种游戏类型的玩家，广告依然不会取得良好的效果。\n在理解这个阶段要注意：\n保持广告内容在用户能理解的兴趣范围内； 注意理解门槛； 强调一个主要诉求来吸引用户的注意力。 如上图中微信公众号正文中嵌入的广告，我（曾嵘）一直不认为是一种好的体验模式。我对该广告的体验如下：\n文案和图片都在打擦边球（定位男性游戏玩家），具有不错的吸引力； 此类游戏不在我的兴趣范围内，无法吸引点击； 广告在文章中心，我不愿意停止阅读的连贯性去点击广告。 4. 接受 广告上下文环境对广告的接受程度影响很大。上图中的广告若出现在游戏社区、或者霸道总裁类小说中，效果就会出现在经济观察文章中好很多。因为兴趣定位更准，理解也变简单了。\n5. 保持 长期效果类广告会在创意上花更多功夫，以期传递鲜明的记忆，追求长期影响。\n6. 决策 决策代表转化行为。\n以上六个阶段中，越靠前的阶段，效果对于 点击率 的影响越大；越靠后的阶段，效果对于 转化率 的影响越大。\n我上个月撰写的 归因系列 讲述了如何判断最终 决策 与前面哪个阶段的关系最为紧密，欢迎前往阅读。\n从技术角度看互联网广告特点 互联网广告和传统广告相比，有不少专有的特点。下面从技术角度说明一下：\n1. 技术和计算导向 我早期使用信用卡时，每月都会收到银行寄来的纸质账单。对于这些账单，银行可以根据信用卡用户的年龄、性别、消费能力（通过信用卡账单判断）做定制化。这就是定向广告的传统表现形式。 理论上，银行可以完全掌握用户的所有特点进行针对定制。但实际上这样做的成本相当高，因为对纸质账单分类寄送需要消耗大量人力，难以规模化。\n而对于在线广告来说，做到计算导向就相当容易了。\n2. 效果的可衡量性 点击率是最能衡量广告效果的指标吗？从 1998 年到今天，Banner 广告的评论点击率从 10% 一路降低到 0.1%，我们能说 Banner 广告的效果下降了两个数量级吗？\n3. 创意和投放方式的标准化 受众定向和程序化交易要求标准化。创意尺寸统一化和关键接口标准化是广告标准产生的基础。目前国内的广告平台在标准化上做的 非常之不友好。\n4. 媒体概念的多样化 越接近转化的媒体上的广告，带来的流量就可以达到越高的 ROI，但距离「引导潜在用户」的目的也越远。\n举例来说： 淘宝与淘宝客网站做现金返利形式的广告推广，ROI 非常高。但淘宝在 2012 年底终止了这种广告形式。\n一味追求高 ROI 在电商行业未必是一件好事，在游戏行业就是好事吗？\n曾嵘：\nSAGI GAMES 开发的小游戏在双十一、春节期间的广告 eCPM 能瞬间（几个小时以内）爬升到日常值的数十倍，但这并不代表游戏本身受到用户欢迎。\n等假期过去，在用户质量和量级不变的前提下，eCPM 突降到正常水平之下，持续数日才会缓慢提升。\n5. 数据驱动的投放策略 现代在线广告系统是一个大数据处理平台，它根据数据在多个广告竞争同一次展示时做出决策，再将投放结果统计数据反馈给广告操作人员以调整投放策略。\n数据就是在线广告系统的根本驱动力，正如煤炭是第一次工业革命的驱动力，电力是第二次工业革命的驱动力一样。\n全文完 ","date":"2021-04-04","description":"《计算广告》读书笔记：从信息接收阶段理解广告生效方式，从技术角度看在线广告特点。","lastmod":"2021-04-04T14:07:43Z","slug":"computad2","tags":["reading","readingnote","ad","computational-advertising"],"title":"计算广告：在线广告是如何生效的？","url":"https://blog.zengrong.net/post/computad2/"},{"categories":["technology"],"content":"BI 是一个成熟的概念。\n根据 前面的讨论，我们知道 BI 系统是一套综合了 数据仓库、查询引擎和前端展示 的复杂系统，是企业进行互联网营销的必备工具。有大量的公司和团体进入这个领域，开发出了优秀的整体解决方案。\n我们今天就来聊聊已经存在的 BI 系统解决方案。\n我正在构建一个用于游戏运营的 BI(Busniness Intelligence)系统，该系统用于指导 SAGI GAMES 游戏的投放，把运营和投放岗位从重复繁重的数据分析工作中解放出来。\n我会用文字把整个构建中的学习、思考、决策的流程记录下来，形成这个系列： 构建BI。\n由于时间仓促，能力有限，这个系列中一定有许多疏漏错误之处，欢迎指出和讨论。\n商业方案 阿里云 Quick BI https://www.aliyun.com/product/bigdata/bi\n阿里云的智能分析套件 Quick BI 是一个专为云上用户量身打造的新一代智能BI服务平台。Quick BI可以提供海量数据实时在线分析服务，支持拖拽式操作和丰富的可视化效果，帮助您轻松自如地完成数据分析、业务数据探查、报表制作等工作。\nQuick BI 的核心流程\nQuick BI 的产品架构\nQuick BI 的优势在于它可以集成阿里云几乎所有的数据库产品。权限管理模块和数据展示模块也相当成熟。\n还有一个不能忽视的优势，就是：真心不贵。\n腾讯云 BI https://cloud.tencent.com/product/bi\n商业智能分析（Business Intelligence，BI）支持自服务数据准备、探索式分析和企业级管控，是新一代的敏捷自助型 BI 服务平台。只需几分钟，您就可以在云端轻松自如地完成数据分析、业务数据探查、报表制作等一系列数据可视化操作。便捷的拖拉拽式交互操作方式，让您无需依赖 IT 人员，无需担心试错成本，快速洞察数据背后的关联、趋势和逻辑。\n阿里有的，腾讯当然也会有啦。虽然刊例价比较贵，但目前三折中。\nTableau https://www.tableau.com/zh-cn#products\nTableau 高度专注地致力于开发强大的可扩展平台，借以帮助人们更好地查看和理解数据；即使是全球最大的组织，Tableau 的平台也足以满足其需求。\n相当成熟的产品，价格也很成熟。\nGoogle Data Studio https://datastudio.google.com/\n可以方便连接到 Google Firebase 和 BigQuery，Google 全家桶你值得拥有。\n看起来很好用但实际上很难用。\nPower BI https://powerbi.microsoft.com/\nPower BI 让你能使用统一和可扩展的平台，连接到任何数据并对数据进行可视化。通过自助服务分析企业数据，帮助企业获取更深入的数据见解。\n巨硬出品，必属精品。\ndeltaDNA https://unity.com/cn/products/deltadna\n使用 deltaDNA 需要接入 SDK，以便您的游戏可以将信息发送到 deltaDNA 平台进行可视化、分析和游戏玩法个性化。\ndeltaDNA 是专门为游戏设计的，目前已经开始在国内推广。\n开源方案 上面提到的全部是商业产品。开源世界也有不少优秀的方案可供选择，下面介绍两个 SAGI GAMES 使用过的方案。\n开源方案在易用性上无法与商业方案比肩。我们需要至少安排一名可以进行 SQL 或者代码编写的人员帮助使用者生成报表。\nApache Zeppelin https://zeppelin.apache.org/\nZeppelin 是一套基于 web 的 notebook 方案。她支持 20 多种语言解释器，你可以使用自己熟悉的语言对数据进行分析，如果用过 Jupyter，你就会对 Zeppelin 的界面感到友好。\n可以把 Python、JAVA 和 SQL 混在一个界面中使用和进行数据分析，的确是很惬意的事情。\n在 SAGI GAMES 使用 Zeppelin 的过程中，体验不是太好。主要的原因是系统不太稳定，而且占用资源过高。\nredash https://github.com/getredash/redash\nredash 支持多种数据源的整合，也支持通过查询自动生成图表，是一个简洁和快速的可视化工具。\nSAGI GAMES 觉得挺好用的，界面简洁，速度飞快。\n选择方案 在这么多方案中选择是一件挺困难的事儿。我在考虑 BI 方案的时候会关注下面几个重点：\n使用成本。 易用性。易用性决定了是否需要安排独立为 BI 服务的人员。 数据集成。也就是 ETL 部分，必须易于与现存的海量数据进行连接。 私有部署。私有部署有时候是必备的选项。 下一篇，我将介绍一下 SAGI BI 解决方案。\n全文完 ","date":"2021-04-01","description":"BI 系统是一套综合了数据仓库、查询引擎和前端展示的复杂系统，是企业进行互联网营销的必备工具。","lastmod":"2021-04-01T09:08:18Z","slug":"build-bi-3","tags":["ad","bi","build-bi"],"title":"构建BI：BI系统解决方案","url":"https://blog.zengrong.net/post/build-bi-3/"},{"categories":["technology"],"content":"在 构建BI：大数据系统概念 一文中，我们提到过 OLAP 系统的特点：\nOLAP 强调 SQL 执行时长，强调磁盘 IO、磁盘分区和磁盘子系统吞吐量。OLAP 同时处理的数据量大，DML 少，更多的执行是报表作业和聚合类操作。\n这些特点让 OLAP 对于存储和查询的要求都很高。业界也有大量成熟的技术解决方案来处理这两类问题。\n本文从个人喜好的角度来简单讨论查询引擎 Presto 和数据仓库 ClickHouse。\n注意：本篇为技术讨论，对技术不感兴趣的读者可以跳过不看。\n我正在构建一个用于游戏运营的 BI(Busniness Intelligence)系统，该系统用于指导 SAGI GAMES 游戏的投放，把运营和投放岗位从重复繁重的数据分析工作中解放出来。\n我会用文字把整个构建中的学习、思考、决策的流程记录下来，形成这个系列： 构建BI。\n由于时间仓促，能力有限，这个系列中一定有许多疏漏错误之处，欢迎指出和讨论。\nPresto Presto 是一个分布式内存化 SQL 引擎，只负责计算，不负责存储数据。Presto 不会将中间结果写入磁盘，能显着提高查询速度。\nFacebook 和社区 PrestoDB 是 2013 年 Facebook 的三个核心工程师创造并开源的产品。在 Facebook 内部，这个应用规模很庞大（总结点 10000+）。2018 年，Facebook 决定对 Presto 进行更严格的管控。为了自己创立项目的更好发展，这三个工程师离职创建了 Presto 软件基金会，fork 了 PrestoDB 的源码，创建了 PrestoSQL。\nFacebook 随后在 LinuxFoundation® 上创建了 Presto 基金会，并申请了 Presto® 商标。\n因为 Presto 已经是注册商标，三个创始人只能放弃这个由自己创建的名称，将 PrestoSQL 改名为 Trino。\n因此，你可以在互联网上找到两个 Presto 版本：\nFacebook 主导版本 PrestoDB 社区版本 Trino（原 PrestoSQL） 曾嵘在这里建议选择 Trino。\n详见： We’re rebranding PrestoSQL as Trino\nPresto 的优势 多数据源、混合计算支持：支持众多常见的数据源，并且可以进行混合计算分析； 大数据：完全的内存计算，支持的数据量完全取决于集群内存大小。他不像SparkSQL可以配置把溢出的数据持久化到磁盘，Presto是完完全全的内存计算； 高性能：低延迟高并发的内存计算引擎，相比Hive（无论MR、Tez、Spark执行引擎）、Impala 执行效率要高很多。根据Facebook和京东的测试报告，至少提升10倍以上； 支持ANSI SQL：这点不像Hive、SparkSQL都是以HQL为基础（方言），Presto是标准的SQL。用户可以使用标准SQL进行数据查询和分析计算； 扩展性：有众多 SPI 扩展点支持，开发人员可编写UDF、UDTF。甚至可以实现自定义的Connector，实现索引下推，借助外置的索引能力，实现特殊场景下的 MPP； 流水线：Presto 是基于PipeLine进行设计，在大量数据计算过程中，终端用户(Driver)无需等到所有数据计算完成才能看到结果。一旦开始计算就可立即产生一部分结果返回，后续的计算结果会以多个Page返回给终端用户（Driver） Presto 的 Connector 借用 Connector 机制，Presto 可以将来自一切数据源的数据计算 SQL 化。\n本地文件(File Connector) 内存（Memory Connector） HTTP API（Http Connector） Hive（Hive Connector） HBase（HBase Connector） MySQL（MySQL Connector） ClickHouse ClickHouse 由俄罗斯 Yandex 公司开源的数据仓库(Data Warehouse)，专为 OLAP 而设计。\nYandex 是俄罗斯最大的搜索引擎公司，官方宣称 ClickHouse 中有超过 13 万亿条记录，每天超过 200 多亿个事件被处理。\nClickHouse 是 DBMS(database management system)，有自己的数据存储机制：列式存储。\nClickHouse 的特性 真正的列式数据库管理系统 数据压缩 数据的磁盘存储 多核心并行处理 多服务器分布式处理 支持SQL 向量引擎 实时的数据更新 索引 适合在线查询 支持近似计算 Adaptive Join Algorithm 支持数据复制和数据完整性 角色的访问控制 列式存储的优势 OLAP场景与其他通常业务场景(OLTP或K/V)有很大的不同，因此想要使用 OLTP 或 Key-Value 数据库去高效的处理分析查询场景，并不是非常完美的适用方案。\n列式数据库更适合于OLAP场景(对于大多数查询而言，处理速度至少提高了100倍)\n行式检索示意图\n列式检索示意图\nI/O\n针对分析类查询，通常只需要读取表的一小部分列。在列式数据库中你可以只读取你需要的数据。例如，如果只需要读取100列中的5列，这将帮助你最少减少20倍的I/O消耗。 由于数据总是打包成批量读取的，所以压缩是非常容易的。同时数据按列分别存储这也更容易压缩。这进一步降低了I/O的体积。 由于I/O的降低，这将帮助更多的数据被系统缓存。 CPU\n由于执行一个查询需要处理大量的行，因此在整个向量上执行所有操作将比在每一行上执行所有操作更加高效。同时这将有助于实现一个几乎没有调用成本的查询引擎。如果你不这样做，使用任何一个机械硬盘，查询引擎都不可避免的停止CPU进行等待。\nMPP 架构和分布式架构 MPP 架构 MPP 架构是传统数据仓库中的常见技术架构，将单机数据库节点组成集群，从而提升处理性能。\nClickHouse 采用的是 MPP 架构。\nMPP 架构在集群中使用非共享形式，每个节点拥有独立的磁盘存储和内存系统，计算过程独立，单个节点不关心整个集群的状态。各节点之间的数据传输使用网络进行。\nMPP 集群作为一个整体向外提供服务，单个节点无法独立运行局部应用。数据存储位置不透明，并行计算时，单节点瓶颈会成为整个系统的短板，扩展性较差。\n分布式架构 分布式架构组成的集群中，各节点实现场地自治，可以独立运行局部应用。\nHive 采用的是分布式架构。\n在分布式架构中，数据在集群中是全局透明共享的。节点在进行运算时，可以访问公共数据存储系统。\n分布式架构对于网络的要求不高，但节点间的通信开销比较大，数据移动不方便。\n主流的大数据仓库和查询引擎 下面都是些耳熟能详，大名鼎鼎、如雷贯耳、非常成熟的解决方案。仅列出简单介绍。\nApache Hadoop： Hadoop 是一款支持数据密集型分布式应用程序的开源软件框架。用于高效存储和处理从 GB 级到 PB 级的大型数据集。利用 Hadoop，您可以将多台计算机组成集群以便更快地并行分析海量数据集，而不是使用一台大型计算机来存储和处理数据。Hadoop 是根据谷歌公司发表的 MapReduce 和 Google 文件系统的论文自行实现而成。它包含 HDFS、YARN、MapReduce 和一套 JAVA 库。 Apache HBase：HBase 是一种开源 NoSQL 分布式大数据存储。它可以实现对 PB 级数据的随机、严格一致的实时访问。HBase 可非常高效地处理大型稀疏数据集。可以在 Hadoop 分布式文件系统 (HDFS) 顶部运行。 Apache Spark：Spark 是一种用于大数据工作负载的分布式开源处理系统。它使用内存中缓存和优化的查询执行方式，可针对任何规模的数据进行快速分析查询。它提供使用 Java、Scala、Python 和 R 语言的开发 API，支持跨多个工作负载重用代码—批处理、交互式查询、实时分析、机器学习和图形处理等。Spark 和 Presto 类似，没有自己的存储系统。 Apache Hive： Hive 是可实现大规模分析的分布式容错数据仓库系统。该数据仓库集中存储信息，您可以轻松对此类信息进行分析，从而做出明智的数据驱动决策。Hive 让用户可以利用 SQL 读取、写入和管理 PB 级数据。Hive 建立在 Apache Hadoop 基础之上。 Apache Kafka：Kafka 是一种分布式数据存储，经过优化以实时提取和处理流数据。流数据是指由数千个数据源持续生成的数据，通常可同时发送数据记录。流平台需要处理这些持续流入的数据，按照顺序逐步处理。Kafka 适合在 ETL 系统中发挥作用。 参考文章 开源OLAP引擎综评：HAWQ、Presto、ClickHouse 第1章02节 | Presto概述：特性、原理、架构 Presto: Interacting with petabytes of data at Facebook Presto 分布式SQL查询引擎及原理分析 greenplum,teradata,presto,clickhouse四种分布式数据库的对比 ClickHouse拆解 【简介】MPP\u0026amp;分布式架构 We’re rebranding PrestoSQL as Trino 什么是ClickHouse 全文完 ","date":"2021-03-30","description":"Presto 和 ClickHouse，大数据基础设施简介。","lastmod":"2021-03-30T14:46:34Z","slug":"build-bi-2","tags":["ad","bi","build-bi"],"title":"构建BI：查询引擎和数据仓库","url":"https://blog.zengrong.net/post/build-bi-2/"},{"categories":["technology"],"content":"我正在构建一个用于游戏运营的 BI(Busniness Intelligence)系统，该系统用于指导 SAGI GAMES 游戏的投放，把运营和投放岗位从重复繁重的数据分析工作中解放出来。\n我会用文字把整个构建中的学习、思考、决策的流程记录下来，形成这个系列： 构建BI。\n由于时间仓促，能力有限，这个系列中一定有许多疏漏错误之处，欢迎指出和讨论。\n本文的目标是理解大数据系统的相关概念。\n生产-存储-消费 计算广告是唯一得到充分商业化的规模化的大数据应用。BI(Business Intelligence)系统属于一种大数据洞察应用。它处在 生产、存储、消费 流程中的消费环节。\n参见：计算广告：免费模式的本质\n图中，数据源 处于生产环节，数据仓库 处于存储环节，数据应用 属于消费环节。\nBI 系统，在商业上属于 DDS（Decision support system，决策支持系统)，作用是将源数据整理到数据仓库(Data Warehouse) 中，然后根据业务模型对仓库中的数据进行分析、聚合，最终在数据应用层面展示出来。\n源数据 在数据生产环节，源数据可能有多种来源，例如来自于 OLTP 的交易数据和行为数据，来自于文档的结构化数据，和来自于图像、声音等非结构化数据。\n数据仓库 数据仓库（Data Warehouse）处于存储环节。我们根据业务模型对数据源中的数据进行抽取、清洗、转换、聚合，将处理过的源数据存储进入数据仓库备用。整个处理的过程被称为 ETL。\n曾嵘：我后面会单独写一篇文章来介绍数据仓库。 数据应用 在 计算广告：免费模式的本质 一文中，我们提到了两种大数据应用：\n洞察(insight) 自动化(automation) BI 属于一种洞察应用。营销信息系统属于一种自动化应用。\n重要概念介绍 OLTP OLTP 是 On-line Transaction Processing 的简称，中文一般译为 联机事务处理。传统银行交易就是典型的 OLTP 应用。\n技术上，OLTP 强调内存各种指标的命中率，强调查询效率，强调并发，高可用。OLTP 评估 Transaction 和 Execute SQL 的数量，对 IOPS 敏感，在硬件上对 CPU 和磁盘子系统依赖极高。\n从数据的角度看，OLTP 同时处理的数据量少，DML(Data Manipulation Language) 频繁，并行事务处理多，但是一般都很短。\nOLAP OLAP 是 On-line Analytical Processing 的简称，中文一般译为 联机分析处理。BI 应用也可以说是一种 OLAP。\n技术上，OLAP 强调 SQL 执行时长，强调磁盘 IO、磁盘分区和磁盘子系统吞吐量。Cache 在 OLAP 系统中的作用有限，因为要并行在多个数据源读取海量的数据，OLAP 在硬件上对于带宽和分布式磁盘个数要求高。\n从数据的角度看，OLAP 同时处理的数据量大，DML 少，更多的执行是报表作业和聚合类操作。典型的操作是全表扫描，长查询，一般事务的个数很少，往往是一个事务独占系统。\n需要注意的是，OLAP 是一个同时存在于存储环节和消费环节的概念。\nOLTP 和 OLAP 的比较 项目 OLTP OLAP 使用场景 在线业务 数据分析，挖掘、机器学习 主要用户 操作人员 决策人员 作用 日常操作 分析决策 实现 增删改查 复杂聚合与多数据源关联 数据新鲜度 当前的业务数据 历史的存档数据 数据维度 细节的，二维分立的 聚集的，多维统一的 数据量级 百MB~GB 百GB~TB 存取 读/写 数百条 读 百万条 并发 高 低 可用性 非常高 中 时间维度 毫秒 分钟、小时、天 技术方案 事务、索引、存储计算整合 大量 SCAN、列式存储、存储计算分离 数据模型 关系模型、3NF范式 维度模型、关系模型、范式要求低 典型技术 MySQL/Oracle Hadoop/Presto/ClickHouse 典型应用 银行事务 BI 上面的表格主要讲区别，下面这张图可以体现 OLTP 和 OLAP 的联系：\nOLTP 系统基于商业逻辑运行，生成的操作数据通过 ETL 进入数据仓库，OLAP 系统基于数据仓库进行分析，形成洞察和决策，反过来影响和改善商业逻辑。\nOLAP 场景的关键特征 大多数是读请求 数据总是以相当大的批(\u0026gt; 1000 rows)进行写入 不修改已添加的数据 每次查询都从数据库中读取大量的行，但是同时又仅需要少量的列 宽表，即每个表包含着大量的列 较少的查询(通常每台服务器每秒数百个查询或更少) 对于简单查询，允许数十毫秒的延迟 列中的数据相对较小： 数字和短字符串(例如，每个URL 60个字节) 处理单个查询时需要高吞吐量（每个服务器每秒高达数十亿行） 事务不是必须的 对数据一致性要求低 每一个查询除了一个大表外都很小 查询结果明显小于源数据，换句话说，数据被过滤或聚合后能够被盛放在单台服务器的内存中 ETL ETL 是 Extract-Transform-Load 的缩写，中文一般译为 抽取、清洗转换、加载。\nETL 是数据整合方案，也是数据整理工具，ETL 处理的数据量巨大，它不是一次性的活动，而是一个周期性的活动。\n在数据抽取阶段，需要考虑同源数据，不同源数据，文件型数据（txt、xls）和增量更新。\n在数据清洗阶段，需要考虑不完整数据、错误数据和重复数据。\n在数据转换阶段，需要考虑的内容更多：\n不一致的数据转换（例如用户唯一 id 的不同表现形式） 数据的粒度分析 理解商务规则。 简化概念 让我们把概念简化一下。将生产环节的数据源简化为 OLTP，将存储环节的数据仓库简化为 OLAP，那么 OLTP/ETL/OLAP 三者间的关系就是：\n交易生成数据 多渠道抽取数据 清洗和转换数据 加载数据 分析数据 参考文章 OLAP、OLTP的介绍和比较 第1章04节 | 常见开源OLAP技术架构对比 ETL讲解（很详细！！！） 如何理解关系型数据库的常见设计范式？ greenplum,teradata,presto,clickhouse四种分布式数据库的对比 数据仓库的基本架构 数据仓库的多维数据模型 全文完 ","date":"2021-03-29","description":"生产-存储-消费环节的数据处理。OLTP OLAP 和 ETL。","lastmod":"2021-03-29T09:15:03Z","slug":"build-bi-1","tags":["ad","bi","build-bi"],"title":"构建BI：大数据系统概念","url":"https://blog.zengrong.net/post/build-bi-1/"},{"categories":["impressions"],"content":"本文是阅读 《计算广告 第2版》 的第一篇读书笔记。\n所有读书笔记请访问这个 TAG： 《计算广告》\n在线广告的目的和实现 定义和分类 广告在《当代广告学》中的定义：\n广告是由已确定的出资人通过各种媒介进行的有关产品（商品、服务和观点）的，通常是有偿的、有组织的、综合的、劝服性的非人员的信息传播活动。\n广告的主动参与方：\n出资人-需求方-广告主/代理方 媒体-供给方 广告的被动参与方：受众。\n广告是出资人、媒体和受众三者的利益博弈。\n广告的根本目的是广告主通过媒体达到低成本的用户接触。\n品牌广告 的作用是宣传品牌形象，提升中长期购买率和利润空间。 直接效果广告 的作用是带来大量的购买或者其他转化行为。\n在线广告优势 在线广告与传统媒体广告相比，主要优势体现在下面两个方面：\n变现流量：低成本投送个性化广告 变现数据：搜索和电子商务可以更清楚了解用户意图 在广告业务中，数据变现 是附着在 流量变现 的基础上的。\n对于互联网广告来说：\n一切付费的信息、产品或服务的传播渠道，都是广告。\n定向广告系统 定向广告(targeted advertising)系统对计算机技术提出两个需求：\n受众定向(audience targeting) 实时广告投放(ad serving) 担保式投放 (Guaranteed Delivery)的计算问题：\n有效将流量分配到各个合约互相交叉的人品覆盖上。 在线环境下实时完成每一次展示决策。 竞价广告 (auction-based advertising)不再给广告出保证量，而转为保证质（单位流量成本）。对于每一次展示，按照收益最高的简单原则来决策。\n广义第二高价（Generalized Second Price） 是重要的竞价广告理论。\n实时竞价（Real Time Bidding）为预先出价，完成每次展示时实时出价。\n免费模式的本质 免费模式的本质是将那些能够规模化、个性化的传播信息的商品，以边际成本的价格出售。\n这里有四个重点：\n规模化。没有规模化，获利太少没有商业价值。 个性化。没有个性化则不易于传播。 传播。无法传播的商品无法进行免费模式改造。 边际成本。边际成本就是量产成本。 免费模式的利润从何而来？有三个重点：\n流量。在免费内容中夹带付费内容。 数据。个性化数据是有价值的，依赖个性化数据调整投放策略，让投放更有效。 影响力。具备影响力的产品会获得更高溢价。 影响力变现的方式，不容易被在线广告体系处理。\n曾嵘：\n网红和自媒体主要是靠影响力变现，而非流量变现。\n薇娅和李佳琦远高于普通主播的溢价更多来自于影响力。\n公众号软文属于影响力变现，它的价格就远高于文末展示广告的价值。\n曾嵘：顺便聊聊微信小游戏 微信小游戏 是免费模式的典型应用。让我们一条条拆解一下：\n规模化： 大量的小游戏 ARPU 只有几分钱，想要赚钱必须上大流量。 个性化： 玩家对于小游戏的买账，就会产生自然分享。自然分享的目的是标榜自己的独特性（角色、皮肤、战绩）。 传播： 「裂变」是小游戏低价流量的主要来源。 边际成本： 由于互联网产品的特点，每个玩家所玩游戏的边际成本接近 0。 我在 2018 年微信小游戏启动时写过了一篇文章：微信小游戏：风口还是泡沫，从文字中可以看出，当时的微信小游戏市场就已经在弱监管下疯狂地在免费模式的经典之路上冲刺了：\n裂变的目标是“廉价的流量”，让用户能主动分享为小游戏带来新的用户，在不花或者少花买量费用的情况下实现游戏的用户增长。微信虽然限制了“诱导分享”，但并没有限制“分享”行为本身。通过对不同年龄阶段和产品目标用户属性的分析，经过精心的设计，还是能得到不错的效果的。\n但目前大多数的微信小游戏并不是这个思路。纯单机，开了流量主之后就不再维护，粗制滥造，疯狂换皮，只注重广告不注重体验，这样的做法只会留下一堆堆小游戏垃圾。\n什么样的团队容易产出小游戏垃圾呢？主要是这样三种：\n以流量为主的公司，做的是流量生意，没有自己的技术团队，通过为已上架的小游戏导量分成的方式运营。 看到小游戏红利一头扎进来的公司，需要快速大量出产品，快速组建开发团队，快速上线小游戏。 个人开发者和小规模团队，抱着试水的想法快速发布产品。 以上团队没有能够做出精品游戏的基因。贪多求快，原封不动地抄袭，占山头卖流量拿红利，不会做出好产品。还是那句话，如果快有用，为啥市场要期待精品？\n到了 2019 年，我又写了一篇文章 做个靠谱的微信小游戏 CP 表达对这种变现模式的担忧，决定坚持做有价值的游戏：\nSAGITEAM 经常打趣说这一届“玩家质量堪忧”。这不是玩家的问题，而是我们的问题，是我们思维方式的大问题。我们在技术和美术上专业度足够了，在“好玩”这件事情上还相当嫩。\n到了现在，我们已经知道：在不同平台，玩家的需求是不一致的，不能用相同的游戏设计思路去面对不同的玩家。玩家会用脚投票，不好玩的游戏，玩家离开你甚至都不会挥挥衣袖，更别说带走一片云彩。\n做好微信小游戏这个平台，需要深挖玩家的需求，做贴合玩家需要的产品。做一些“真正的小游戏”。早点放下自己“专业”的架子，进入“接地气”的阶段，才能做出爆品。我们需要把心思放在如何“更好玩”，而不是“更专业”上。\n这对 SAGITEAM 的未来当然是一个好的选择。\n如果站在 技术的角度 来讨论， 在微信小游戏的互联网免费模式的大潮洗礼之下，在监管羸弱的前提下，在稍纵即逝的市场机会前，关注极低成本的规模化传播，对小游戏做疯狂裂变或许才是更客观的选择。\n在线广告是典型的大数据应用 大数据的特点(4V) 规模 Volume 多样性 Variety 高速 Velocity 价值 Value 数据规模 从 数据来源 看，传统数据处理的主要是 交易数据，大数据主要处理的是 行为数据。\n交易数据是商业活动中的必须数据，它对一致性和实时性的要求非常高。例如通话记录、充值、存取款等都是交易数据。\n交易中产生的其他非必须数据是 行为数据 。行为数据有两个特点：\n规模巨大。 对于数据一致性的要求较低。 加工方式 如果只采样一部分数据，但最终解决问题的效果没有太大下降，这就是 A 类问题 ，A 类问题不需要全量处理，不是大数据问题。\n个性化推荐和计算广告等问题就不能通过对部分数据的分析来达到高收益，这就是 B 类问题。 B 类问题要得到最高的收益，需要对 全量数据 进行分析。这是典型的大数据问题。\n大数据应用方向 洞察(insight)\n洞察应用需要人的参与，报表规模不能太大，因此会造成一定的信息量损失。对于洞察报表的解读和决策，需要相当专业的训练。\n企业的财务报表，销售月度总结就是传统的洞察应用。\nBI(Business Intellgence) 是洞察应用的典型表现。\n自动化(automation)\n自动化营销系统(Marketing Information System)，由机器进行决策，数据可以在个体颗粒度上进行处理。\n计算广告是唯一得到充分商业化的规模化的大数据应用 计算广告为规模化变现流量和数据提供了完整产品和解决方案，创造了互联网大部分利润。 在线广告孵化了成熟的数据加工和交易产业链。 计算广告产品和技术比推荐系统更加复杂。理解它对于设计高效的商业产品大有益处。 全文完 ","date":"2021-03-28","description":"《计算广告》读书笔记：免费模式的本质是将那些能够规模化、个性化的传播信息的商品，以边际成本的价格出售。","lastmod":"2021-03-28T06:06:30Z","slug":"computad1","tags":["reading","readingnote","ad","computational-advertising"],"title":"计算广告：免费模式的本质","url":"https://blog.zengrong.net/post/computad1/"},{"categories":["tutorial"],"content":"上一篇 广告归因：广告ID与个人隐私保护 一文中，我们介绍了广告 ID 的出现，以及个人隐私保护。\n本篇是归因系列五篇文章中的最终篇。我们来聊聊苹果的 ATT 政策对广告行业的巨大影响，以及广告行业是如何应对的。\n阅读归因系列的所有文章： 归因系列 。\n苹果的隐私保护政策 苹果一直以来都非常尊重用户隐私。也一直在以雷霆手段不断限制 APP 对于用户隐私的获取。在 广告ID与个人隐私保护 中，我们提到，苹果在 iOS 5 禁止获取 IMEI，在 iOS 6 禁止获取 UDID，在 iOS 7 禁止获取 Mac Address。\n2020 年 9 月，iOS 14 发布，苹果开始对自己一手创造的，已经正常运行了 8 年之久，成为了事实上 iOS 广告 ID 标准的 IDFA 下手了。\n实际上还有一种「绕过限制」的方法：\n开发者「发明」了一种通过将用户数据存在 iCloud 钥匙串中的方法来实现跟踪。苹果对这种方法进行过限制，设计了当用户删除 App 的时候也自动删除钥匙串中的数据的流程。\n但许多 App 的正常使用数据也存储在钥匙串中。苹果对此功能进行限制后，收到大量开发者的反对，因此回滚了这个功能。\nApp Tracking Transparency(ATT) iOS 14 以来，苹果出台了强制执行的 App Tracking Transparency(ATT)政策，要求开发者必须明确征得用户的许可，才能使用用户的 IDFA 信息。在这之前，iOS 是默认同意 APP 使用 IDFA 信息的。\n苹果官网上的说明如下：\nApp Store 旨在提供一个安全可信的平台，让用户发现由全球才华横溢的开发者所打造的各种 app。App Store 上的 app 在隐私、安全性和内容方面都必须遵循严格标准，因为维护用户的信任至关重要。要提交新 app 和 app 更新，您需要在产品页面上提供有关您 app 的某些数据收集做法的信息。从 iOS 14.5, iPadOS 14.5 和 Apple tvOS 14.5 开始，您必须先征得用户的许可，才能在其他公司拥有的 app 和网站内对其进行跟踪。\n公布了ATT后，苹果公司并没有立刻启用它，但 2020 年末苹果高管发言称，在2021年春季，苹果公司将会全面推行ATT，如果应用开发者拒绝接入该功能，则会被从 App Store 中强制删除对应 App。\n根据我的几个开发者朋友的反馈，他们的 APP 在 2021 年 2 月提审到 AppStore 时被拒审，原因是 APP 中包含的 SDK 有将 IDFA 上传到第三方服务器。从这一点看来，苹果对 IDFA 的管控会越来越严。\nATT 焦虑 ATT 的强制推行对于基于 IDFA 进行归因的移动广告行业带来了下面的焦虑：\n第三方调研机构数据显示，一旦需要通过弹窗来获取权限，就只有 20%~30% 用户会愿意授权。 苹果对于「诱导」用户授权跟踪的行为会进行及其严厉的打击。 按照以往数据预测，最新的强制 ATT 的 iOS 版本 14.5，很可能会在更新之后 40 天内被 65% 的苹果手机用户安装。 没有 IDFA，广告平台就无法精准定位 唯一用户，从而不可能对广告价值进行准确的归因。\n即使苹果没有明说，这也很可能意味着，IDFA 的时代要结束了。\nSKAdNetwork 解决方案 苹果毕竟是一家公司，不可能放弃广告市场。苹果的方案是提供另一个工具 SKAdNetwork 来对 App 进行有限归因：\nSKAdNetwork 允许已注册的广告网络通过接收来自 Apple 的签名信号，将 app 安装归因于特定营销活动。这能帮助开发者在保护用户隐私的同时，验证某个广告带来了多少安装次数，并衡量哪些营销活动最为有效。\n使用 SKAdNetwork 2.0 或更高版本的广告网络还能访问“来源 App”的信息，该信息会标识具体是由哪个 app 带来了安装。这样有助于在第三方 app 上投放广告的广告网络确定触发用户下载的来源 app。SKAdNetwork 2.0 还能识别重新下载，从而帮助广告网络衡量再互动广告系列的效果。\n使用 SKAdNetwork 进行归因的执行方式如下：\n广告平台通过 SKAdNetwork 对广告签名； 用户点击广告下载； 用户在设备中启动 App（激活），调用 SKAdNetwork Register API，记录一次应用激活； 若需要记录用户行为，调用 SKAdNetwork Update Conversion Value API，记录注册、购买等事件； 调用 SKAdNetwork API 启用倒计时，每次调用 API(激活或者用户事件) 都会让倒计时重新开始； 24 小时倒计时结束，计时器会再加上一个 0-24 小时的 随机延迟， 然后将转换的值发给广告平台； 转换值可以设定 64 个事件，0 代表 install，其他的可以自行设定； 广告平台得到转换值之后，再回传给归因平台。 sequenceDiagram autonumber iOS设备 -\u003e\u003e+ 用户: 展示SKAdNetwork签名广告 用户 -\u003e\u003e- iOS设备: 点击广告 iOS设备 -\u003e\u003e 广告平台: 跳转到广告平台 Note left of AppStore: 广告平台跳转到 AppStore 广告平台 -\u003e\u003e+ AppStore: 下载 AppStore -\u003e\u003e- iOS设备: 安装APP iOS设备 -\u003e\u003e 广告平台: 激活 loop 24小时倒计时，新事件则重启倒计时 iOS设备 --\u003e\u003e AppStore: registerAppForAdNetworkAttribution() iOS设备 --\u003e\u003e AppStore: updateConversionValue() iOS设备 -\u003e\u003e+ AppStore: 没有新的事件 AppStore --\u003e\u003e- iOS设备: 0-24小时随机倒计时 end iOS设备 -\u003e\u003e 广告平台: 归因结果数据 广告平台 --\u003e\u003e 归因平台: 归因结果数据 SKAdNetwork下的归因流程 这个解决方案与现有的归因流程有很大不同。广告平台和归因平台需要同时面临下面的问题：\n没有实时回传。新的归因方式意味着回传最快也要发生在安装之后的 24 小时，如果运气不好（例如经常重置倒计时），回传可能会发生在数月之后…… 匿名数据问题。SKAdNetwork 的数据采用聚合形式，不提供用户层级的精细数据，难以进行精细的 ROI 优化； 数据易用性问题。SKAdNetwork 提供的是 6 bit 事件识别码，识别码只能单向变化和向上递进。 数据量不足。SKAdNetwork 对每个渠道仅显示 100 个不同的推广活动。在对多个国家/地区进行多素材测试的时候是不够用的。这会减少投放上 Campaign 的数量设置； 回传浪费。SKAdNetwork 先把数据传回广告平台，广告平台再将数据传给归因平台。这意味着归因平台（无论是自建还是第三方）需要和每个广告主进行对接，这是巨大的技术资源浪费。 要了解现有的归因流程，可以阅读 广告归因：归因方法。 关于「单向变化和向上递进」，Adjust 提供了一个很好的例子进行解释：\n例如，用户在游戏内达成了等级 1，应用为 \u0026quot;等级 1\u0026quot; 创建的事件识别码为 000001；然后用户购买了游戏内货币，这一事件的识别码为 000011。如果用户随后达成了 \u0026quot;等级 2\u0026quot;，比特值不会变为 000010，因为变化是单向的。要避免这个问题，开发者需要为排列组合中所有的可能性分配不同的比特值，而不是为每种事件分配比特值。\n广告和归因平台的应对 对于 ATT 带来的巨大改变，广告平台和归因平台都推出的自己的应对方案。\n归因平台 AppsFlyer 推出了 SK360，涵盖转化值配置、防作弊和 LTV 预测分析等功能。 广告平台 Facebook 针对 SKAdNetwork 推出了一系列新的工具和限制：Apple iOS 14 发布对您的广告和报告有何影响。 归因平台 Adjust 与 众多合作伙伴进行了集成，减少开发者的工作量。 广告平台 Google 发布了详细的更新说明： 帮助应用开发者为 Apple 的 iOS 14 ATT 政策更新做好准备。 另外，在投放的时候使用 In-app Bidding（应用内竞价）方案，也能一定程度上避免 SKAdNetwork 的影响。\nATT 已成事实，对于广告主来说，只能停下抱怨，快速学习如何在新的框架下得到最好的投放效果。如果小扎和库克掰手腕有了效果，我们也乐见其成。\n参考链接 ATT“来袭”之际，行业应该如何面对这种变化 后IDFA时代：买量投放、广告变现、归因分析的营销策略 苹果官网: 用户隐私和数据使用 Adjust: SKAdNetwork 会为移动监测带来哪些挑战？ Adjust: SKAdNetwork 合作伙伴集成 Google： 帮助应用开发者为 Apple 的 iOS 14 ATT 政策更新做好准备 Facebook：Apple iOS 14 发布对您的广告和报告有何影响 AppsFlyer：面向 SKAdNetwork 的一站式解决方案 SK360 结语：我为什么要写这个系列 归因系列一共有五篇文章，全部都在这里了：归因系列 。\n这个系列文章，是我在系统学习互联网商业变现市场与技术的一点点成果。一直以来，我都更习惯用输出的方式来学习较为系统化的知识，这样会让我的学习速度更快，记得更牢。这方法也是 费曼技巧 所推崇的。\n对于混合变现的游戏，我们要实现最好的投放效果，必须搭建自己的工具平台。整合多个广告平台和归因平台的数据，最终实现智能投放，让人的力量去做更重要的事。\n一个优秀的程序员，应该致力于用自动化的方式去解决所有可以自动化的问题。要以人肉方式为耻，要全力对世界进行自动化改造。优秀的程序员，也必须同时是一个优秀的产品经理。\n由于我也在学习中，这个系列文章很可能有错误和疏漏的地方，欢迎提出来，一起讨论，一起成长。\n全文完 ","date":"2021-03-11","description":"苹果的ATT政策对广告归因的影响","lastmod":"2021-03-11T07:29:11Z","slug":"attribution-att","tags":["feynmantech","attribution","ad"],"title":"广告归因：苹果ATT对归因的影响","url":"https://blog.zengrong.net/post/attribution-att/"},{"categories":["tutorial"],"content":"上一篇 广告归因：归因模型 一文中，我们介绍了归因系列中最重要的概念。\n本篇是归因系列的第四篇。我们来聊聊广告ID的前世今生，政府出台的法案对个人隐私保护的意义。\n阅读归因系列的所有文章： 归因系列 。\n广告 ID 是什么 在 广告归因：归因方法 一文中，我们接触到了广告 ID 的概念，也知道了 广告 ID 是归因实现中最重要的证据。\n现在我们做一点进一步的解释。\n广告 ID 一直都不是一个固定的概念。只要能代表用户唯一性的证据，都可以作为广告 ID 使用。\n在网页广告中，这个广告 ID 一般就是网站 Cookie，网站开发者通过向用户的浏览器 Cookie 中写入一个允许过期的字符串来作为记录用户唯一性的凭证。虽然 Cookie 是可以被用户清除的，但大多数用户都不知道怎么清除它，也不知道清除它有什么用。\n最近大家可能经常会在海外网站上看到同意你使用 Cookie 的显著标志，如果你不同意它就会一直显示，这是为了符合欧盟的 GDPR。\n在移动设备上，对于 APP 的广告追踪无法使用 Cookie。开发者「发明」了很多方法来确定用户设备的唯一性，下面分类聊一下。\n通用 ID Mac Address 网络设备包括 WIFI、蓝牙设备的机器码都是全球唯一的，得到这个数据当然就定位到了唯一的设备。 IMEI 国际移动设备识别码，International Mobile Station Equipment Identity，用于 GSM、WCDMA、LTE 制式的移动电话和卫星电话。 MEID 移动设备识别码，Mobile Station Equipment Identifier，用于 CDMA 手机。 不幸的是，iOS 5 开始禁止获取 IMEI，iOS 7 开始禁止获取 Mac Address。\n在 Android 这边，一直没有禁止获取这些信息，而是采用权限分配的方式，APP 必须明确请求用户同意这些权限才能获取手机设备和网络设备信息。然而这个限制并没有什么luan用，有不少 APP 很霸王，你不给权限它就不让你用……\niOS 专用 ID UDID Unique Device Identifier，iOS 专用，与 iOS 设备绑定，用来标示设备的唯一性 。iOS 6 之后被禁止获取。 OpenUDID 苹果禁止获取 UDID 之后，开发者社区自己搞出来的方法。iOS 7 后被禁止获取。 IDFA Identifier For Advertising，iOS 6 开始提供，用于给开发者跟踪广告效果使用。用户可以在手机设置中重置 IDFA。和 Cookie 一样，大多数用户都不知道怎么清除它，也不知道清除它有什么用。 IDFV Identifier For Vendor，应用开发商标识符。同设备上不同开发商获得的 IDFV 不同。对于广告商来说，作用显然不如 IDFA。 Android 专用 ID ADID Google Advertising ID，Android 设备提供的广告 ID，类似于 iOS 的 IDFA。Android 4 开始提供， 由于 Android 设备对于 IMEI 等通用 ID 的管控没有 iOS 那么严格，广告厂商还是更愿意使用能获得 真正唯一性 的通用广告 ID。\ngantt title iOS 广告 ID 演化史 dateFormat YYYY-MM section iOS IMEI(iOS5禁止获取) :imei, 2010-06, 480d UDID(2010-06~2013-05,iOS6禁止获取) :udid, 2010-06,1050d MAC Address(2010-06~2013-09,iOS7禁止获取) :after udid, 2010-06, 1170d OpenUDID(2013-06~2013-09,iOS7禁止获取) :after udid, 2013-06, 90d IDFA(2012-09~2021-03,iOS14开始限制) :after udid, 2012-09, 3110d iOS6-iOS14(2012-09,2020-09) :2012-09, 2920d section Android IMEI,Device ID(read_phone_state权限) :imei, 2010-06,3930d MAC Address(网络权限): mac, 2010-06, 3930d ADID(2011-10~) :adid, 2011-10,3450d section 政府隐私法案 GDPR(2018-05) :2018-05, 1040d CCPA(2020-01) :2020-01, 430d 广告 ID 演化史 政府出台隐私法案 GDPR GDPR 全称是 General Data Protection Regulation，通用数据保护条例，是欧盟议会和欧盟理事会在 2016 年 4 月通过，在 2018 年 5 月开始强制实施的规定。GDPR 规定了所有欧盟的公民所享有的数字生活中的权利。\nGDPR 要求企业在收集用户的个人信息之前，必须以「简洁、透明且易懂的形式，清晰和平白的语言」向用户说明：\n将收集用户的哪些信息； 收集到的信息将如何进行存储； 存储的信息会如如何使用； 企业的联系方式。 作为用户，享有以下权利：\n数据访问权； 被遗忘权； 限制处理权； 数据携带权。 更详细的信息可以参考少数派的这篇文章：关于 GDPR 你需要了解的一切。\nCCPA CCPA 全称是 California Consumer Privacy Act，加州消费者隐私法案，是美国的第一个全面的隐私法。 它向加利福尼亚的消费者提供各种隐私权利。CCPA 向加利福尼亚人提供的许多权利都类似于 GDPR 提供的权利，包括披露数据主体权利 (DSR) 请求，如访问、删除和可移植性。\nCCPA 于 2020 年 1 月 1 日生效。 但是，由加利福尼亚首席律师 (AG) 确定于 2020 年 7 月 1 日正式执行。\n简言之，CCPA为加利福尼亚消费者的个人数据提供以下保护：\n所有权：保护消费者有权告诉企业不要共享或出售个人信息的权利 控制：提供消费者对收集到的有关他们的个人信息的控制权 安全：要求企业负责保护个人信息 网络安全法和个人信息保护法 我国在隐私保护上也是不遗余力的：\n《中华人民共和国网络安全法》 在2016年11月7日第十二届全国人民代表大会常务委员会第二十四次会议通过发布。 《个人信息保护法》草案已经在 2021年3月提请全国人大常委会审议。 隐私保护法案对广告行业的影响 这些法案对广告行业的影响是极其深远的。\n《个人信息保护法（草案）》规定对违法部门处以五千万人民币或上一年营业额百分之五以下的罚款。GDPR 对于严重违反者处以两千万欧元或者企业上一年度全球营业收入的百分之四。 这些罚款可以令企业伤筋动骨，个人倾家荡产。\n我们应该敬佩尊重个人隐私的企业。从上面聊到的广告 ID 变迁史可以看出。苹果一直以来都非常尊重用户的个人隐私。也一直在主动控制用户的个人隐私泄露。iOS 14 以来，苹果出台了强制执行的 ATT（App Tracking Transparency）政策，要求开发者必须明确征得用户的许可，才能使用用户的 IDFA 信息。\n从上面关于广告 ID 的分析可以看出，在 iOS 设备上硕果仅存的广告 ID，仅剩 IDFA 可用。如果苹果再限制获取 IDFA，广告行业应该如何应对呢？广告行业会就此消失吗？\n请等待 归因系列 的最后一篇：ATT 时代的广告归因。\n参考链接 Google 获取用户可重置的广告ID iOS 是不是能比安卓更能保护隐私？ 关于 GDPR 你需要了解的一切 用户隐私和数据使用 全文完 ","date":"2021-03-10","description":"广告 ID 的前世今生，GDPR 对个人隐私的影响","lastmod":"2021-03-10T03:43:41Z","slug":"attribution-adid","tags":["feynmantech","attribution","ad"],"title":"广告归因：广告ID与个人隐私保护","url":"https://blog.zengrong.net/post/attribution-adid/"},{"categories":["impressions"],"content":"金三银四招聘季，这是一篇 SAGI GAMES 的招聘软文。它应该是你最近朋友圈铺天盖地的招聘海报中最特别的内一份。\n感谢您的阅读，先行谢过。\n一、我是谁 曾嵘，业内荣誉称号「曾老师」。SAGI GAMES 创始人。我是：\n教美术的体育老师 一个沦为程序员的美术老师 八年的游戏行业创业经验 十年的游戏行业老兵 二十年的老年程序员 乐于在公司大群里自黑的游戏公司老板 想要知道曾老师是个什么样的人，可以阅读我写了 16 年的 博客 和写了 5 年的公众号「曾嵘胡扯的地方」。保证真情实感，绝无杜撰。\n二、SAGI GAMES 是什么 SAGI GAMES 成立于 2018 年 2 月，位于传说中的武汉，是一家专注于中度休闲游戏创作的公司。\n是的，我们是游戏公司，但我们不仅仅只爱做游戏。\n1. SAGI GAMES 是一家为了做好游戏，自行打通研发运营发行通路的公司 我们在 3 年多时间里，走了一条极其充(jian)实(nan)的路，用不到 30 人的体量，完成了游戏研发、流量变现、买量投放和用户运营的全路径打通。\n2. SAGI GAMES 是以研发为主，专注创新实现的团队 我们梳理了全体成员的特点，制定了一条属于我们自己的发展路线。\n我们专注于 休闲竞技、动作、射击、模拟经营、卡牌放置策略融合 方向，做能发挥出团队最高实力的产品。\n我们创造了 SAGI 风格的 吸量-最小化MVP-数据测试-立项 模式，坚持做既好玩又赚钱的产品。\n3. SAGI GAMES 是一群骨灰级游戏玩家组成的，爱玩会玩想玩的团伙 SAGI 人都是游戏玩家，让一群站在「游戏鄙视链顶端」的主机玩家来做手机游戏，需要特殊的方法。\nSAGI 人会在任何时候玩游戏（包括上班时间），SAGI 人把生活变成了游戏。团建、节日活动、体育课、读书会、吃鸡、奶茶惩罚 都是 SAGI 的游戏。\n4. SAGI GAMES 是唯一位于武汉内环，月湖风景区内的游戏公司 很可能是武汉环境最好的游戏公司。\n三、SAGI GAMES 的价值观 SAGI GAMES 的使命： 用游戏改变生活\nSAGI GAMES 的愿景： 创造长期伟大的游戏，创造最佳的环境让团队成功\n在这里，我想让大家知道，SAGI GAMES 是为什么而存在，以及她的存在对于 SAGI 人有什么意义。\n1. SAGI 要改变 SAGI 人的生活 热爱游戏的人们，有天生的纯真理想。我们希望自己做的游戏能被人喜爱，也期待着游戏大卖给我们带来世俗上的成功。\n这带来了矛盾。\n我们既希望努力工作，又很想快乐生活。我们既反对 996，又为了产品进度而努力加班。我们既讨厌实时在线的沟通方式，又要在工作时实现秒回消息。\n在 SAGI GAMES，我们会自由很多。在 SAGI GAMES，我们是矛盾的统一体。为了改变生活，我们学会了专注自由，小心平衡。\n2. SAGI 要创造 SAGI 人的成功 创造长期伟大的游戏，需要大量的积累和持续的投入。游戏公司的成功，必须建立在团队成功的基础之上。\n在 SAGI，所有的改变都是为了一个目的，让 SAGI 人能够释放出全部的潜力。\n我们有从策划转岗到开发并且发量茂盛的善良程序员； 我们有一人单挑运营和投放岗位的帅气产品人； 我们有测而优则策：从测试岗转行产品岗的美丽小姐姐。 在 SAGI GAMES，公司层面的所有努力，都是为了创造最好的环境，让团队成功。\n四、加入 SAGI GAMES SAGI GAMES 仍在创业阶段，你可能会有一些疑问，我提前作答：\n1. 加入 SAGI GAMES 的缺点 曾老师是个细节控，偶尔会发现你刚好疏忽的那个点。曾老师能看懂的细节有点儿多，不好忽悠。 创业当然辛苦，我们都需要努力奔跑，才不至于留在原地。 创业小破司经常有各种岗位和流程上的缺失和疏漏，你要随时准备在没有相关知识的情况下冲上去补(tiao)位(keng)，学习压力巨大。 下面就是全是优点啦！加入 SAGI GAMES，你会得到：\n2. 充分的自由和主动权 SAGI GAMES 坚决推崇自由主动的的文化。\n我建议你在公司大会上直接怼老板，我建议你在群里直接 AT 制作人、职能线负责人发表你的意见。我们推荐群聊，拒绝私聊。我们提供各种渠道（实名或匿名）搜集与公司所有业务和管理相关的意见建议。\n我们的管理非常扁平，堪比飞机场。\n我郑重承诺，坚决捍卫所有 SAGI 人平等发声的权利。\n不自由，毋宁死！\n3. 关注新的技术和设计 SAGI GAMES 持续在成长中拥抱新的技术和设计。\n创业小破司的特点就是「没有历史包袱」。优秀的程序员的特点就是「不断关注新的技术趋势」。设计需要不断迭代才能更加精巧，技术需要持续更新才有活力。\n我希望你大胆尝试新的技术和新的流程，公司会为你在新的尝试中出现的错误和过失买单。\n我郑重承诺，坚决拥护主动创新，全面支持 SAGI 人在新的项目中启用能提升生产力的新架构新方法。\n不创新，毋宁死！\n4. 全透明的文化 产品的运营数据在项目组内部是公开的。项目的运营数据如何，赚了多少钱，团队成本如何，项目成员一清二楚。\n正因为如此，SAGI 的美术同学会催着策划调留存，SAGI 的程序员会在客服群里回答玩家的问题。\n也正因为如此，才能实现下面这一条：\n5. 真的分钱 除了年终奖，还分钱，而且是立刻分钱。\n项目立项时，开全员大会公布分成方法以及各项目组的分成比例。项目盈利后立即开分钱大会，全公司参加，每个人分多少一清二楚全公布，精确到小数点后两位。\n正如 SAGI 的价值观之一： 让努力的人有尊严。\n6. 轻松活泼的氛围 所有的程序员都闷骚，所有的美术都明骚，所有的策划都不得不骚，所有的妹子颜值都极高。\n五、HR 小姐姐在这里 SAGI GAMES 位于武汉内环，武汉长江大桥和江汉桥交汇处，龟山公园脚下，高山流水觅知音的古琴台旁，月湖风景区内，距离地铁 6 号线「琴台」站 C 出口 30 米，距汉江江滩 200 米。\n坐在你的工位上就可以俯瞰月湖全景，中午吃饭时就能听蝉声蛙鸣。春望一江汉水，夏看荷塘月色。\n我们需要：\n技术：高级 Unity 开发工程师 技术：Unity 主程 技术：高级服务器开发工程师（Golang，欢迎Python/Node/Java/C++转岗） 技术：服务器主程（Golang，欢迎Python/Node/Java/C++转岗） 技术：游戏测试工程师 产品：高级游戏策划 产品：制作人 美术：高级 UI 设计 我认为，以上岗位最重要的技能是：\n能准确描述自己的想法，有顺畅的沟通能力 热爱游戏事业，旺盛的好奇心 愿意不断尝试新的事物 如果你对 SAGI GAMES 有兴趣，欢迎给我们发邮件： hr@sagigames.cn\n你可以可以直接加我们 HR 小姐姐的微信。\n当然，你也可以加曾老师的微信：\n请一定要大胆加 V\n请一定要大胆加 V\n请一定要大胆加 V\n你做三四月的事，在八九月自有答案 全文完 ","date":"2021-03-07","description":"SAGI GAMES 2021 春季招聘","lastmod":"2021-03-07T07:22:49Z","slug":"hire2021","tags":["entrepreneurship","sagiteam"],"title":"来 SAGI GAMES，一起创造长期伟大的游戏","url":"https://blog.zengrong.net/post/hire2021/"},{"categories":["tutorial"],"content":"上一篇 广告归因：归因方法 一文中，我们讲解了归因的具体流程、归因窗口和自归因平台的概念。\n本篇来讲讲常用的归因模型。归因模型的选择对归因效果和数据分析会产生决定性的影响。\n阅读归因系列的所有文章： 归因系列 。\n归因效果 上篇我们讲过，对于自归因平台来说，归因可以让我们得到如下效果：\n是哪一部手机（可以等同于哪一个用户）点击了广告； 点击的是哪一条广告。 这条广告的效果怎么样？（用户有没有看完广告？看到广告的第几秒点击的广告？看完广告后下载的用户比例？等等……） 这条广告花了多少钱？（业内一般会按照千条计算） 更一般的说，归因让 广告主 得到这样的信息：\n哪些渠道/哪一条广告转化效果最好（更多广告点击），哪些渠道的用户质量更高（更多充值），哪些渠道的用户的用户更加活跃（更多时长）。\n要准确分析这些信息，可不那么容易。\n归因窗口的 BUG 在 广告归因：归因方法 讲解 归因窗口 的时候，我们提到了一个 BUG：\n用户点击广告，下载 APP，启动 APP，这三者之间是有时间差的。用户可能在下载 APP 的时候碰到网络失败，也可能下载 APP 之后没有立刻打开。\n解决这个 BUG 的方法就是设置 归因窗口。\n然而还有 BUG。\n假设我们投放了 3 个视频广告，有一个用户 A，在不同的地方观看了这 3 个广告，在看完广告 3 的时候，点击下载链接下载了 APP。这是否就说明第 3 个广告的效果最好呢？如果是这个用户在看完广告 2 就想下载，只是由于正好有事忘记了（例如接了个电话），过两天看到广告 3 的时候才最终完成下载，这不能说明广告 3 的效果最好啊！\n假设另一个用户 B，在看到广告 1 的时候就点击了下载，但由于各种原因，一直没有在设备上启动 APP。也就是说，没有上一篇文章中第 8 步的匹配过程，归因无法完成。直到有一天，用户 B 观看了广告 3，点击下载（已经下载过），然后启动 APP 完成了归因。这能说明广告 3 的效果是最好的么？\n这两个例子，都是 归因模型 要解决的问题。\n基于点击的归因模型 首次点击归因模型\n首次点击或访问归因模型会将 100% 的转化功劳归于转化路径中发生的首次点击或访问。\n以上面提到的 3 个视频广告为例，广告 1 承载了所有的归因功劳。\ngraph LR p1[\"100%\"] p2[\"0%\"] p3[\"0%\"] subgraph 广告1 p1 end subgraph 广告2 p2 end subgraph 广告3 p3 end p1 --\u003e p2 --\u003e p3 --\u003e 转化 首次点击归因模型 从效果上看，首次点击归因模型割裂了归因与最终转化的联系，忽视了其他因素的影响。\n最终点击归因模型\n最终点击或访问归因模型会将 100% 的转化功劳归于转化路径中发生的最后一次点击或访问。\n以上面提到的 3 个视频广告为例，广告 3 承载了所有的归因功劳。\ngraph LR p1[\"0%\"] p2[\"0%\"] p3[\"100%\"] subgraph 广告1 p1 end subgraph 广告2 p2 end subgraph 广告3 p3 end p1 --\u003e p2 --\u003e p3 --\u003e 转化 最终点击归因模型 从效果上看，最终点击归因模型没有考虑产品宣传的认知度和兴趣作用的过程，忽略了其他广告的促进作用。\n可以看到，上面的两个归因模型都不完美。事实上，有更多的归因模型可以选择。在选择模型的时候，重点并非 事实是什么，而是 你想要得到怎样的事实 。\n在了解更多的归因模型之前，我们要先了解一个新的概念： 触点。\n触点 在之前的 归因系列文章 中，我们仅讨论了一种触点： 点击 。\n点击是重要且主要的触点，因为点击说明了用户对广告感兴趣，点击是完全主动的行为，能够反映用户的自由意志。\n但 展示 也是一个重要的触点。\n还是以上面的 3 个广告为例。用户 C 在看了广告 1 和广告 2 之后，没有进行点击的操作。当看完了广告 3 之后点击了下载。我们可以说广告 1 和广告 2 的 展示 就没有作用吗？如果没有广告 1 和广告 2 的 展示，用户在首次碰到广告 3 的时候，可能也不会进行 点击 操作。\n除了 基于点击的归因，还有 基于展示的归因。\n现在来定义 触点：\n触点就是转化前发生的展示、点击和访问总量。\n通过对触点比重的分配，我们可以构建更多的归因模型。\n更多归因模型 线性归因/功劳均等归因模型\n虽然名称不同，但都采用相同的方式计算所有触点的功劳。\ngraph LR p1[\"33.3%\"] p2[\"33.3%\"] p3[\"33.3%\"] subgraph 展示广告1 p1 end subgraph 展示广告2 p2 end subgraph 点击广告3 p3 end p1 --\u003e p2 --\u003e p3 --\u003e 转化 线性归因模型 时间衰减归因模型\n触点越越接近转化发生时间，分配的功劳就越多。以 Google Ads 为例：\n假设归因窗口为 28 天，那么点击每相隔 7 天，所分配的功劳就会相差一半。换言之，转化发生 8 天前的触点所获功劳是转化发生 1 天前的触点所获功劳的一半。\n位置归因模型\nGoogle Ads 会进行下面的分配：\n为客户首个触点以及最终触点分别分配 40% 的功劳，将其余 20% 的功劳平均分配给其他触点。\n以数据为依据的归因模型\n根据转化操作的历史数据来分配转化功劳。该归因模型与其他归因模型的区别在于，它使用帐号数据计算每个触点在转化路径中的实际功劳。\n参考链接 Facebook： 触点 程序化广告归因模型知多少 Google：归因模型简介 Facebook： 归因模型简介 全文完 ","date":"2021-03-06","description":"归因模型","lastmod":"2021-03-06T01:56:00Z","slug":"attribution-model","tags":["feynmantech","attribution","ad"],"title":"广告归因：归因模型","url":"https://blog.zengrong.net/post/attribution-model/"},{"categories":["tutorial"],"content":"书接上回。\n在 广告归因：是什么和为什么 一文中，我们从归因作用的角度对其进行了定义：\n归因的目的是判断用户的来源，确定广告的效果，从而指导后期投放。\n本回书说一下归因的方法和常用模型。\n阅读归因系列的所有文章： 归因系列 。\n从证明你妈是你妈开始 先说个 老段子：\n2015 年 5 月 6 日，李总理在国务院常务会议上，讨论进一步简政放权时说“我看到有家媒体报道，一个公民要出国旅游，需要填写”紧急联系人“，他写了他母亲的名字，结果有关部门要求他提供材料，证明”你妈是你妈“。这怎么证明呢？简直是天大的笑话！人家本来是想出去旅游，放松放松，结果呢？这些办事机构到底是出于对老百姓负责的态度，还是在故意给老百姓设置障碍？”\n归因方法的核心，和证明你妈是你妈是一样的道理，就是 让归因平台知道，是一个特定的设备点击了一个特定的广告。\n要证明我们和亲爱的妈妈就是我们的妈妈，我们可以用户口本啊出生证明啊公证啊 DNA 啊之类的证据，而在归因的世界里，证据就是 广告 ID。\n观察下面的时序图，抛开「用户」和「下载渠道」这两个参与者，归因的过程，就是广告 ID 匹配的过程。最重要过程是第 4 步和第 8 步：\n第 4 步，用户设备在点击广告时，将广告 ID 发送给归因平台。 第 8 步，新装的 APP 启动后，自动将广告 ID 发送给归因平台。 如果这两步中的广告 ID 是匹配的，此次归因成功。\nsequenceDiagram autonumber 用户设备-\u003e\u003e+用户: 展示广告（曝光） 用户-\u003e\u003e-用户设备: 点击广告 用户设备 --\u003e\u003e 用户设备: 获取设备广告ID Note right of 用户设备: 展示广告时，归因平台的SDK获取到设备的广告ID 用户设备 -\u003e\u003e 归因平台: 发送广告ID 归因平台 --\u003e\u003e 归因平台: 记录广告ID Note left of 归因平台: 归因平台记录这一次广告点击 归因平台 -\u003e\u003e+ 下载渠道: 下载 Note left of 下载渠道: 归因平台跳转到下载渠道 下载渠道 -\u003e\u003e- 用户设备: 安装APP 用户设备 -\u003e\u003e 归因平台: 发送广告ID Note right of 用户设备: APP内嵌的归因平台SDK上报广告ID 归因平台 --\u003e\u003e 归因平台: 广告ID匹配，归因成功 归因就是广告ID匹配的过程 归因让我们得到了什么？ 上面的归因流程完成之后，归因平台还要在内部进行一系列的匹配和计算，得到下面这些信息：\n是哪一部手机（可以等同于哪一个用户）点击了广告； 点击的是哪一条广告。 如果配合广告投放平台，还可以得到更多的数据：\n这条广告的效果怎么样？（用户有没有看完广告？看到广告的第几秒点击的广告？看完广告后下载的用户比例？等等……） 这条广告花了多少钱？（业内一般会按照千条计算） 归因窗口 观察上面的时序图，我们可以发现一个 BUG：\n用户点击广告，下载 APP，启动 APP，这三者之间是有时间差的。用户可能在下载 APP 的时候碰到网络失败，也可能下载 APP 之后没有立刻打开。\n归因平台解决这个问题的方法就是： 设置归因时间窗口。\n例如，Facebook 目前的默认时间窗口是 点击后 7 天。这意味着无论用户在下载 APP 后发生了什么问题，7 天内只要启动了下载的 APP，这次下载就被归因为这一次广告点击。\n在 iOS 14 之前，Facebook 默认采用的是 点击后 28 天 内归因。\n自归因平台 大家应该注意到了，上面的时序图中，没有出现「广告投放平台」的角色。\n这是因为，上图中的「归因平台」，也同时是「广告投放平台」。\n这种结合了「归因平台」和「广告投放平台」能力的广告平台，就被称为 自归因平台 。\n实际上，也只有结合了「广告投放平台」和「归因平台」两者的数据之后，才能得到 归因让我们得到了什么？ 一节时提到的所有数据。有了这些数据，才能指导我们的投放。\n下面这些耳熟能详的大型的广告平台都是自归因平台：\nApple Search Ads Facebook Google Ads 腾讯 参考链接 如何证明你妈是你妈？ Google Ads：转化时间范围介绍 Facebook：广告管理工具中的统计时间窗简介 全文完 ","date":"2021-03-04","description":"归因方法、归因窗口和自归因平台","lastmod":"2021-03-04T08:38:34Z","slug":"attribution-how","tags":["feynmantech","attribution","ad"],"title":"广告归因：归因方法","url":"https://blog.zengrong.net/post/attribution-how/"},{"categories":["tutorial"],"content":"归因是我们在进行投放的过程中，经常用到的广告术语。归因是什么？我们为什么要研究它？下面就来简单地说一说。\n本文是 费曼系列 的第一篇。在 费曼系列 中，我会使用费曼技巧来讲解我学到的知识。 阅读归因系列的所有文章： 归因系列 。\n归因的作用 广告归因经常和广告联系起来，让我们产生一个误解：不做 IAA 游戏，就不用理解归因。\n这不对。\n先不忙着定义什么是归因，我们用一个例子看看归因有啥用。\nJacky 在电梯里看到了「专门做拖把做得更专业」的黄教主宣传自家拖把的广告，去京东下单买了一个黄教主的拖把，黄教主把快递发出来之后，就开始琢磨：\nJacky 这哥们是怎么知道咱家的拖把好的呢？他肯定是看到了我们的广告。我在京东、淘宝、小红书都投了广告，不知道他是通过哪个广告来的呢？\n让我看看他下单过来的链接…… 原来他是通过扫描我教投放在框架传媒电梯广告中的二维码进入的。电梯广告的效果不错哦！再投俩月！\n从上面那个例子看，归因的作用，就是要知道 Jacky（消费者）是被哪条广告吸引以产生购买行为，从而让黄教主（广告主）知道哪个广告渠道的效果更好。\n所以说，无论做的是何种类型的产品，只要使用广告投放的形式来获取用户，就需要了解归因。\n归因原理 电梯广告归因 让我们看看从黄教主投放广告到决定增加投放这中间发生了什么。\nsequenceDiagram autonumber 黄教主-\u003e\u003e+广告投放平台: 购买广告 广告投放平台-\u003e\u003eJacky: 在电梯中展示广告（曝光） Jacky-\u003e\u003e京东: 扫码购买（二维码中包含广告ID） 京东-\u003e\u003e京东: 归因分析 京东-\u003e\u003e广告投放平台: 回调归因数据 广告投放平台-\u003e\u003e-黄教主: 增加投放 黄教主的归因时序图 在上面这张图里，扮演归因平台的是京东。得益于二维码的普及，每个广告的二维码中，都带有黄教主投放商品的广告 ID，京东通过这个广告 ID，就能知道了是哪个广告转化了 Jacky 成为购买者。\n曝光和转化 这里有两个词需要简单解释一下：曝光 和 转化。\n曝光： 把广告显示在用户面前。\n如何计算曝光呢？\n对于电梯广告，可以根据平均人流量来算出大致的曝光量（要区分平峰和高峰）。 对于电视广告，可以根据收视率来计算。 对于网络广告，可以根据素材下载完成（图像广告）或者广告播放到一定比例（视频广告），对于互动广告可以计算互动广告的互动次数和关键埋点。 转化： 观看广告后产生购买、付费、下载等产生价值的行为。\n黄教主发现电梯广告效果好，根据曝光量计算出这条广告的转化率高，就决定增加电梯广告的投放了。\n当然，实际的决策不会这么容易做出。黄教主至少要分析出不同时段的转换率，并比较周边几个小区、写字楼的转化率，同时结合自己在其他渠道的投放，综合比较，计算投资回报率，选出最优的策略。\n除了电梯广告，黄教主还在抖音、小红书、Google、Facebook 等多个平台投放了网络广告。在前面的例子中，归因分析是由产生购买的平台京东完成的。当有多个平台的时候，除了要对比平台内不同广告位的转化效果，还要评价平台之间的效果，应该怎么做呢？\n现在可以聊一聊专业归因监控平台了。\n归因监控平台 我们把注意力从专门做拖把的黄教主那里转到移动互联网领域。对于 APP 和游戏的开发者而言，绝大部分的广告投放都位于各大掌握了财富密码的平台爸爸手中。苹果、字节跳动、Facebook、Google、腾讯都拥有强大的广告投放服务能力。它们也都拥有自己的归因监控平台和广告算法。但对于广告主来说，需要一个中立的数据平台进行客观的数据分析。这就是第三方归因监控平台的作用。\n下面这张时序图相比黄教主的那张要复杂了些。因为 APP 的推广不可避免地增加了「下载」、「启动」等等流程。但如果把「用户」换成「Jacky」，把「归因监控平台、下载渠道」换成「京东」，会发现两张图没有本质区别。\nsequenceDiagram autonumber 广告投放平台-\u003e\u003e+用户: 展示广告（曝光） 用户-\u003e\u003e-广告投放平台: 点击广告 广告投放平台-\u003e\u003e+归因监控平台: 上报用户点击 rect rgb(0, 128, 255, 0.1) 用户--\u003e\u003e下载渠道: 下载 APP 下载渠道--\u003e\u003eAPP: 安装 APP 用户--\u003e\u003eAPP: 启动 APP Note over 用户,APP: 用户自主行为 end APP-\u003e\u003e归因监控平台: 上报信息 归因监控平台-\u003e\u003e归因监控平台: 归因分析 归因监控平台-\u003e\u003e-广告投放平台: 回调归因数据 APP推广归因时序图 专注于移动的归因监控平台有 Adjust、AppsFlyer、DataEye、TalkingData、热云数据等等。\n再谈归因的作用 有了上面的基础之后，可以稍微深入一点谈谈归因的作用了。\n百货商店之父约翰.沃纳梅克（John Wanamaker，1838~1922，就是上面这位大兄弟）说过：\n“我在广告上的投资有一半是无用的，但是问题是我不知道是哪一半。”\n这句话当然已经过时了。\n有了准确的广告归因，约翰大叔当然可以明确知道每一条广告的作用，并依据数据做出决策。\n归因的作用主要是：\n了解价值： 了解广告能为业务带来多大价值（例如销量或操作）。 选择策略： 决定采取在何处投放广告（转化路径），如何投放广告，投放广告的目标（客户群选择），投放广告的预算（规模）。 采取操作： 根据广告效果的详细数据优化广告，更合理地进行出价。 什么是归因 各大广告平台和归因监控平台对归因都有定义，比如下面这些：\n用户如何来到您的应用，他们如何与应用进行交互，以及这些行为和模式在不同同期群之间的对比情况。\n归因就是把转化分配给消费者转换途径中的各个触点，通过了解消费者采取您希望的动作时该归功于哪个广告，您可以妥善衡量广告成效，并针对未来的销售规则制定出最佳决策。\n归因是匹配两个数据点的科学。将您的应用用户与推动其安装的来源进行匹配，来监测推广效果、开展高效再营销推广以及优化广告素材等。\n根据 费曼技巧，我需要给出一个简单的定义，和本文开头一样，通过解释归因的作用来定义：\n归因的目的是判断用户的来源，确定广告的效果，指导后期投放。 参考文章 Apple Ads 归因 API Adjust： 归因 Facebook 归因工具 Google 归因报告和归因效果 全文完 ","date":"2021-02-28","description":"归因的目的是判断用户的来源，确定广告的效果，从而指导后期投放。","lastmod":"2021-02-28T09:19:56Z","slug":"attribution-model-what-why","tags":["feynmantech","attribution","ad"],"title":"广告归因：是什么和为什么","url":"https://blog.zengrong.net/post/attribution-model-what-why/"},{"categories":["technology"],"content":"从 WizNote 为知笔记到 Joplin（上） 一文中讲到了我为什么要从为知笔记转到 Joplin。本文讲一讲其中的技术细节。\nwiz2joplin 项目是开源的，我在源码中写的注释也很详细，所以本文就不列举所有实现，而是主要讲一下设计思路和需要关注的问题。文中标注了报名和函数名称，方便大家在 wiz2joplin 项目中寻找对应源码查看。\n要理解下面讲述的细节，请先阅读：WizNote 为知笔记 macOS 版本本地文件夹分析 。\n读取为知笔记 从为知笔记本地数据库中读取为知笔记。 读取为知笔记的目录信息，在为知笔记中称为 location。 读取为知笔记的 TAG 信息。 解压缩为知笔记每个文档的压缩包到临时文件夹： w2j.wiz.WizDocument._extract_zip 解析为知笔记文档源码中的内嵌的图像资源、内链和附件： w2j.parser.parse_wiz_html 整理数据 为知笔记和 Joplin 中有一些相同的部分，也有一些不同的部分。我们在整理数据的时候，需要将它们进行一一对应。\n1. 为知笔记的 document 有自己的 guid，Joplin 也使用同样的 guid，两者都是 32 个字符，但为知笔记采用了标准的 8-4-4-4-12 格式，而 Joplin 去掉了分隔符。只需要写两个简单的函数进行转换即可：\n1def towizid(id: str) -\u0026gt; str: 2 \u0026#34;\u0026#34;\u0026#34; 从 joplin 的 id 格式转为 wiz 的 guid 格式 3 \u0026#34;\u0026#34;\u0026#34; 4 one = id[:8] 5 two = id[8:12] 6 three = id[12:16] 7 four = id[16:20] 8 five = id[20:] 9 return \u0026#39;-\u0026#39;.join([one, two, three, four, five]) 10 11 12def tojoplinid(guid: str) -\u0026gt; str: 13 \u0026#34;\u0026#34;\u0026#34; 从 wiz 的 guid 格式转为 joplin 的 id 格式 14 \u0026#34;\u0026#34;\u0026#34; 15 return \u0026#39;\u0026#39;.join(guid.split(\u0026#39;-\u0026#39;)) 2. 为知笔记的 TAG 和附件都拥有自己的 GUID，这与 Joplin 的 resource 的 GUID 可以进行一一对应。\n3. 为知笔记的文档中的内嵌图像没有 GUID，为知笔记的目录也没有 GUID，但 Joplin 中的内嵌图像属于标准资源，有自己的 GUID，Joplin 中的 notebook/folder 也拥有自己的 GUID。\n4. 为知笔记的内链有附件内链和文档内链两种格式，使用正则表达式来提取其中的 GUID 部分：\n1RE_A_START = r\u0026#39;\u0026lt;a href=\u0026#34;\u0026#39; 2RE_A_END = r\u0026#39;\u0026#34;\u0026gt;([^\u0026lt;]+)\u0026lt;/a\u0026gt;\u0026#39; 3 4# 附件内链 5# 早期的链接没有双斜杠 6# wiz:open_attachment?guid=8337764c-f89d-4267-bdf2-2e26ff156098 7# 后期的链接有双斜杠 8# wiz://open_attachment?guid=52935f17-c1bb-45b7-b443-b7ba1b6f854e 9RE_OPEN_ATTACHMENT_HREF = r\u0026#39;wiz:/{0,2}(open_\\w+)\\?guid=([a-z0-9\\-]{36})\u0026#39; 10RE_OPEN_ATTACHMENT_OUTERHTML = RE_A_START + RE_OPEN_ATTACHMENT_HREF + RE_A_END 11 12# 文档内链，只需要提取 guid 后面的部分即可 13# wiz://open_document?guid=c6204f26-f966-4626-ad41-1b5fbdb6829e\u0026amp;amp;kbguid=\u0026amp;amp;private_kbguid=69899a48-dc52-11e0-892c-00237def97cc 14RE_OPEN_DOCUMENT_HREF = r\u0026#39;wiz:/{0,2}(open_\\w+)\\?guid=([a-z0-9\\-]{36})\u0026amp;amp;kbguid=\u0026amp;amp;private_kbguid=([a-z0-9\\-]{36})\u0026#39; 15RE_OPEN_DOCUMENT_OUTERHTML = RE_A_START + RE_OPEN_DOCUMENT_HREF + RE_A_END 在读取为知笔记文档源码内容的时候还碰到一个问题，就是早期的为知笔记版本采用了 UTF16 编码。如果使用默认的 UTF8 来读取就会报错。此时应该先检测笔记源码的编码再读取。这里的检测使用第三方库 chardet 完成。\n1index_html = note_extract_dir.joinpath(\u0026#39;index.html\u0026#39;) 2if not index_html.is_file: 3 raise FileNotFoundError(f\u0026#39;主文档文件不存在！ {index_html} |{title}|\u0026#39;) 4html_body_bytes = index_html.read_bytes() 5# 早期版本的 html 文件使用的是 UTF-16 LE(BOM) 编码保存。最新的文件是使用 UTF-8(BOM) 编码保存。要判断编码进行解析 6enc = chardet.detect(html_body_bytes) 7html_body = html_body_bytes.decode(encoding=enc[\u0026#39;encoding\u0026#39;]) 8 9# 去掉换行符，早期版本的 html 文件使用了 \\r\\n 换行符，而且会切断 html 标记。替换掉换行符方便正则 10html_body = html_body.replace(\u0026#39;\\r\\n\u0026#39;, \u0026#39;\u0026#39;) 11html_body = html_body.replace(\u0026#39;\\n\u0026#39;, \u0026#39;\u0026#39;) 5. 为知笔记中的图片在文档源码中使用的是 img 标签，使用正则表达式提取：\n1# 图像文件在 body 中存在的形式，即使是在 .md 文件中，也依然使用这种形式存在 2RE_IMAGE_OUTERHTML = r\u0026#39;\u0026lt;img .*?src=\u0026#34;(index_files/[^\u0026#34;]+)\u0026#34;[^\u0026gt;]*\u0026gt;\u0026#39; 6. 上面解析出来的内链资源和附件资源，都会在 Joplin 中转换成同一种形式： [Title](:/GUID)，image 资源则会转换成 ![Title](:/GUID) 形式。\n临时数据库 由于部分的为知笔记资源在 Joplin 中没有对应的 GUID，必须将这些资源上传到 Joplin 才能取得 GUID，为了避免整个转换过程的中断导致重头来过（毕竟有 3000 篇），我在转换过程中建立了一个临时数据库，将转换过程写入到数据库中，下次中断的时候，就可以从数据库中取得转换状态了。\n下面是数据库的定义：\n1CREATE_SQL: dict[str, str] = { 2 # 保存 Location 和 Folder 的关系 3 \u0026#39;l2f\u0026#39;: \u0026#34;\u0026#34;\u0026#34;CREATE TABLE l2f ( 4 location TEXT NOT NULL, 5 id TEXT, 6 title TEXT NOT NULL, 7 parent_location TEXT, 8 parent_id TEXT, 9 level INTEGER NOT NULL, 10 PRIMARY KEY (location) 11 );\u0026#34;\u0026#34;\u0026#34;, 12 # 处理过的文档会保存在这里，在这个表中能找到的文档说明已经转换成功了 13 \u0026#39;note\u0026#39;: \u0026#34;\u0026#34;\u0026#34;CREATE TABLE note ( 14 note_id TEXT not NULL, 15 title TEXT not NULL, 16 joplin_folder TEXT NOT NULL, 17 markup_language INTEGER NOT NULL, 18 wiz_location TEXT NOT NULL, 19 PRIMARY KEY (note_id) 20 );\u0026#34;\u0026#34;\u0026#34;, 21 # 处理过的资源保存在这里，包括 image 和 attachment 资源 22 \u0026#39;resource\u0026#39;: \u0026#34;\u0026#34;\u0026#34;CREATE TABLE resource ( 23 resource_id TEXT not NULL, 24 title TEXT NOT NULL, 25 filename TEXT NOT NULL, 26 created_time INTEGER not NULL, 27 resource_type INTEGER NOT NULL, 28 PRIMARY KEY (resource_id) 29 );\u0026#34;\u0026#34;\u0026#34;, 30 # 保存为知笔记中的内链，也就是 resource 与 note 的关系，使用 文档 guid 和 连接目标 guid 同时作为主键。链接目标 guid 为 joplin 格式 31 \u0026#39;internal_link\u0026#39;: \u0026#34;\u0026#34;\u0026#34; 32 CREATE TABLE internal_link ( 33 note_id TEXT not NULL, 34 resource_id TEXT not NULL, 35 title TEXT not NULL, 36 link_type TEXT NOT NULL, 37 PRIMARY KEY (note_id, resource_id) 38 ); 39 CREATE INDEX idx_link_type ON internal_link (link_type); 40 CREATE INDEX idx_resource_id ON internal_link (resource_id); 41 \u0026#34;\u0026#34;\u0026#34;, 42 # 保存为知笔记中的 tag 43 \u0026#39;tag\u0026#39;: \u0026#34;\u0026#34;\u0026#34; 44 CREATE TABLE tag ( 45 tag_id TEXT not NULL, 46 title TEXT not NULL, 47 created_time INTEGER not NULL, 48 updated_time INTEGER not NULL, 49 PRIMARY KEY (tag_id) 50 ); 51 CREATE UNIQUE INDEX idx_title ON tag (title); 52 \u0026#34;\u0026#34;\u0026#34;, 53 # 保存tag 与note 的关系 54 \u0026#39;note_tag\u0026#39;: \u0026#34;\u0026#34;\u0026#34;CREATE TABLE note_tag ( 55 note_id TEXT not NULL, 56 tag_id TEXT not NULL, 57 title TEXT not NULL, 58 created_time INTEGER not NULL, 59 PRIMARY KEY (note_id, tag_id) 60 );\u0026#34;\u0026#34;\u0026#34;, 61} 使用 Python 自带的 sqlite3 来创建临时数据库。\n上传到 Jopin 1. 同步为知笔记的目录到 Joplin： w2j.adapter.Adapter.sync_folders 以及 w2j.joplin.JoplinDataAPI.post_folder。\n2. 同步为知笔记的附件和内嵌图像： w2j.adapter.Adapter._upload_wiz_attachment 以及 w2j.adapter.Adapter._upload_wiz_image。\n3. 同步笔记正文内容到 Joplin： w2j.adapter.Adapter.sync_all 以及 w2j.adapter.Adapter._sync_note。\n为知笔记的文档有两种，一种标题以 .md 结尾的，为知笔记会将其作为 Markdown 格式来渲染，另一种不带 .md 后缀的就作为 HTML 来渲染。\n这里说点题外话：\n使用 .md 作为标题后缀，我不知道老魏是处于一个什么样的考量，但我肯定这不是一个优雅的解决方案。\n尽管 Markdown 是在为知笔记出现之后才流行起来的，尽管为知笔记运行这么多年可能有一些历史包袱，但面对一个已经如此流行的技术，采用了这样一种「近乎于无厘头」的解决方案，反映出为知笔记团队「懒于深入思考」的现状。\n在我分析为知笔记本地数据的时候，经常会碰到这种「无厘头」的折衷方案。 例如：\n前后不一的笔记文本编码，之前用 UTF16，后面改为 UTF8. 设计混乱的内链方式， wiz:open_attachment 和 wiz://open_attachment。 拼写错误的数据库列名。 其实只要多花一些思考的时间，这些问题都很容易被优雅地解决。\n在同步到 Joplin 的时候，需要区分这两种情况。为知笔记中保存的 .md 文章是一种很奇怪的格式：既不是纯 Markdown，也不是纯 HTML，而是使用 HTML 作为排版，包含纯 Markdown 内容。\n需要调用 HTML 渲染引擎来处理，将其中用于格式分隔（一般是 div/p/br）等等渲染成实际在 HTML 中的表现，但保持 Markdown 源码不变。\n我找到的最好的 Python 渲染引擎 ：inscriptis 。\n下面的 get_text 方法就是这套渲染引擎中提供的。\n1def gen_ilstr(is_markdown: bool, jil: JoplinInternalLink) -\u0026gt; str: 2 \u0026#34;\u0026#34;\u0026#34; 返回被替换的内链 3 ilstr = internal link str 4 \u0026#34;\u0026#34;\u0026#34; 5 if is_markdown: 6 body = f\u0026#39;[{jil.title}](:/{jil.resource_id})\u0026#39; 7 if jil.link_type == \u0026#39;image\u0026#39;: 8 return \u0026#39;!\u0026#39; + body 9 return body 10 if jil.link_type == \u0026#39;image\u0026#39;: 11 return f\u0026#39;\u0026lt;img src=\u0026#34;:/{jil.resource_id}\u0026#34; alt=\u0026#34;{jil.title}\u0026#34;\u0026gt;\u0026#39; 12 return f\u0026#39;\u0026lt;a href=\u0026#34;:/{jil.resource_id}\u0026#34;\u0026gt;{jil.title}\u0026lt;/a\u0026gt;\u0026#39; 13 14 15def gen_end_ilstr(is_markdown: bool, jils: list[JoplinInternalLink]): 16 \u0026#34;\u0026#34;\u0026#34; 返回 body 底部要加入的内容 17 ilstr = internal link str 18 \u0026#34;\u0026#34;\u0026#34; 19 if is_markdown: 20 return \u0026#39;\\n\\n# 附件链接\\n\\n\u0026#39; + \u0026#39;\\n\u0026#39;.join([ \u0026#39;- \u0026#39; + gen_ilstr(is_markdown, jil) for jil in jils]) 21 body = \u0026#39;\u0026#39;.join([ f\u0026#39;\u0026lt;li\u0026gt;{gen_ilstr(is_markdown, jil)}\u0026lt;/li\u0026gt;\u0026#39; for jil in jils]) 22 return f\u0026#39;\u0026lt;br\u0026gt;\u0026lt;br\u0026gt;\u0026lt;h1\u0026gt;附件链接\u0026lt;/h1\u0026gt;\u0026lt;ul\u0026gt;{body}\u0026lt;/ul\u0026gt;\u0026#39; 23 24 25def convert_joplin_body(body: str, is_markdown: bool, internal_links: list[JoplinInternalLink]) -\u0026gt; str: 26 \u0026#34;\u0026#34;\u0026#34; 将为知笔记中的 body 转换成 Joplin 内链 27 \u0026#34;\u0026#34;\u0026#34; 28 insert_to_end: list[JoplinInternalLink] = [] 29 for jil in internal_links: 30 # 替换链接 31 if jil.outertext: 32 body = body.replace(jil.outertext, gen_ilstr(is_markdown, jil)) 33 # 所有的附件，需要在body 底部加入链接 34 if jil.link_type == \u0026#39;open_attachment\u0026#39;: 35 insert_to_end.append(jil) 36 # 处理 markdown 转换 37 if is_markdown: 38 body = get_text(body) 39 if insert_to_end: 40 body += gen_end_ilstr(is_markdown, insert_to_end) 41 return body 最后，关于同步到 JoplinDataAPI 的正文内容，Joplin 文档讲解得并不详细。我通过抓包 Joplin WebClipper 得到了隐藏的参数。\n在将正文提交到 Joplin 的时候，通过这样的参数配置，就能让 Joplin 自动转换 HTML 到 Markdown。效果还挺不错的。\nbody_html 正文内容。 convert_to 若值为 markdown 代表将 HTML 转换成 Markdown，若值为 html 则不转换。 source_command 若值为 {'name': 'simplifiedPageHtml'} 则设置成简单转换。 下面是更详细的说明。\n1def post_note(self, id: str, title: str, body: str, 2 is_markdown: bool, parent_id: str, source_url: str) -\u0026gt; JoplinNote: 3 \u0026#34;\u0026#34;\u0026#34; 创建一个新的 Note 4 隐藏的 Joplin 参数：通过抓包 Joplin WebClipper 5 6 complete Page Html 7 source_command 8 { 9 \u0026#39;name\u0026#39;: \u0026#39;completePageHtml\u0026#39;, 10 \u0026#39;preProcessFor\u0026#39;: \u0026#39;html\u0026#39; 11 } 12 convert_to = html 13 14 simplified Page Html 15 source_command 16 { 17 \u0026#39;name\u0026#39;: \u0026#39;simplifiedPageHtml\u0026#39;, 18 } 19 convert_to = markdown 20 21 complete page 22 source_command = markdown 23 { 24 \u0026#39;name\u0026#39;: \u0026#39;completePageHtml\u0026#39;, 25 \u0026#39;preProcessFor\u0026#39;: \u0026#39;markdown\u0026#39; 26 } 27 convert_to = markdown 28 \u0026#34;\u0026#34;\u0026#34; 29 kwargs = { 30 \u0026#39;id\u0026#39;: id, 31 \u0026#39;title\u0026#39;: title, 32 \u0026#39;parent_id\u0026#39;: parent_id, 33 \u0026#39;markup_language\u0026#39;: 1, 34 } 35 if source_url: 36 kwargs[\u0026#39;source_url\u0026#39;] = source_url 37 if is_markdown: 38 kwargs[\u0026#39;body\u0026#39;] = body 39 else: 40 # 使用 joplin 的功能将所有的 html 都转换成 markdown 41 kwargs[\u0026#39;body_html\u0026#39;] = body 42 kwargs[\u0026#39;convert_to\u0026#39;] = \u0026#39;markdown\u0026#39; 43 kwargs[\u0026#39;source_command\u0026#39;] = { 44 \u0026#39;name\u0026#39;: \u0026#39;simplifiedPageHtml\u0026#39;, 45 } 46 47 query = self._build_query() 48 logger.info(f\u0026#39;向 Joplin 增加 note {kwargs}\u0026#39;) 49 resp = self.client.post(\u0026#39;/notes\u0026#39;, params=query, json=kwargs) 50 data = resp.json() 51 if data.get(\u0026#39;error\u0026#39;): 52 logger.error(data[\u0026#39;error\u0026#39;]) 53 raise ValueError(data[\u0026#39;error\u0026#39;]) 54 return JoplinNote(**data) 全部的重点就在这里了，希望对你有所帮助。\n更多细节在源码中，欢迎访问 wiz2joplin 项目以了解更多信息。\n设置 Joplin 同步 下面两篇文章详细介绍了 Joplin 同步配置。有了同步功能，笔记软件才完整。建议非程序员使用腾讯云 COS 同步的方式，配置简单，稳定性更有保证。\n配置 Joplin Server 实现同步 使用腾讯云对象存储(COS)实现 Joplin 同步 引用 WizNote 为知笔记 macOS 版本本地文件夹分析 从 WizNote 为知笔记到 Joplin（上） wiz2joplin 转换 WinzNote 到 Joplin 的开源工具 inscriptis Python 下的 HTML 渲染引擎 全文完 ","date":"2021-02-25","description":"转换十年的为知笔记到 Joplin，工具设计","lastmod":"2021-02-25T09:40:58Z","slug":"wiznote2joplin2","tags":["fromto","joplin"],"title":"从 WizNote 为知笔记到 Joplin（下）","url":"https://blog.zengrong.net/post/wiznote2joplin2/"},{"categories":["technology"],"content":"我在 配置 Joplin Server 一文中谈到了如何配置 Joplin Server 以实现 Jopin 笔记同步。对于普通使用者来说，这显然是相当麻烦了，恐怕只有程序员愿意去折腾一下。\n根据该文评论中的提示，我找到了使用腾讯云对象存储(COS)实现同步的方法。配置更少，而且免费。\n腾讯云 COS 官方介绍：\n对象存储（Cloud Object Storage，COS）是由腾讯云推出的无目录层次结构、无数据格式限制，可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限，无需分区管理，适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。COS 提供网页端管理界面、多种主流开发语言的 SDK、API 以及命令行和图形化工具，并且兼容 S3 的 API 接口，方便用户直接使用社区工具和插件。\nJoplin 能采用 COS 同步的核心原因是，COS 提供了 AWS S3 兼容的 API，而 Joplin 又正好支持 AWS S3 同步。\n创建腾讯云帐号\n这个流程很简单，略过了。\n创建 API 用户\n创建一个专用于「编程访问」的用户 joplin，授予 QcloudCOSFullAccess 权限。\n创建存储桶\n在对象存储中创建一个存储桶，选择一个距离你的居住地域较近的地域。可以开启服务端加密。\n授予存储桶权限\n设置存储桶的访问权限，为刚才新创建的用户 joplin 提供数据读取、数据写入的权限。\n设置 Joplin 同步\n在 Joplin 中设置同步目标为 AWS S3，填写 URL 和存储桶、密钥和密码，检查同步配置即可。\nURL 可以在 地域和访问域名 中查到。主要不要使用存储桶页面给你的域名，应该将其中域名前缀去掉。\n例如我的存储桶访问域名是 https://joplin-123456.cos.ap-guangzhou.myqcloud.com，实际填写到配置中的 URL 应该是 https://cos.ap-guangzhou.myqcloud.com。\n实际使用效果\n我在实际使用中碰到了读取超时的问题：\n已新建远程项目: 1。\n已完成: 2021-02-25 12:32\n最后错误: NetworkingError: read ETIMEDOUT\n这个问题在使用 Joplin Server 同步的时候也出现过，但通过修改 nginx 设置 client_max_body_size 解决了。\n目前在 COS 中碰到，我无法判断是客户端的问题（毕竟还是 Beta 阶段）还是 COS 设置问题。\n如果没有很大的资源同步，使用上还是很顺畅的。或许我需要删除一些超大的附件。\n全文完 ","date":"2021-02-25","description":"使用腾讯云对象存储（COS）来进行 Joplin 笔记同步。","lastmod":"2026-03-19T09:50:49Z","slug":"joplin-sync-use-cos","tags":["joplin","fromto"],"title":"使用腾讯云对象存储(COS)实现Joplin同步","url":"https://blog.zengrong.net/post/joplin-sync-use-cos/"},{"categories":["technology"],"content":"在 从 WizNote 为知笔记到 Joplin（上） 一文中，我介绍了把自己积累十年的笔记从 WizNote 搬家到 Joplin 的心路历程。\nJoplin 是一个开源的笔记工具，拥有 Windows/macOS/Linux/iOS/Android/Terminal 版本的客户端，关于它的优势可以看 这里。\n我们知道，多端同步功能是笔记工具最重要的功能。只有实现了多端同步，我们才能在工作电脑和手机之间无缝切换笔记体验。Joplin 在同步上做得不错，支持 Dropbox、OneDrive 和 AWS s3（当然国内都没法用），支持 WebDAV 协议，也支持自家的 Joplin Server。\n本文介绍如何在自己的服务器上搭建 Joplin Server，并配置好 Joplin Desktop 的同步功能。\n安装 Docker 以 Ubuntu 18.04 为例：\n1# 更新包 2sudo apt update 3 4# 安装包管理 https 支持 5sudo apt install apt-transport-https ca-certificates curl software-properties-common 6 7# 添加 docker 官方包的 GPG key 8curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 9 10# 添加 docker 官方源 11sudo add-apt-repository \u0026#34;deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable\u0026#34; 12 13# 再次更新包 14sudo apt update 15 16# 安装 docker 17sudo apt install docker-ce 18 19# 查看 docker 服务状态 20sudo systemctl status docker 如果此时显示 docker 服务一切正常，接着设置 docker 的执行用户组。我用来运行 Joplin Server 的用户名称为 app，进行下面的设置：\n1sudo usermod -aG docker app 创建 Joplin Server 配置文件 创建配置文件，/home/app/joplin/.env：\n1APP_BASE_URL=https://your-sample-url 2APP_PORT=22300 Joplin Server 推荐使用 PostgreSQL 数据库，但我的烂服务器装个数据库很难受，所以上面的配置就是直接用 SQLite 数据库了。 O(∩_∩)O哈哈~\n我的近三千篇文章在同步之后，数据库大小仅为 600MB，这个大小对于 SQLite 还是能承受的。\n创建 Docker Volumes 由于采用了 SQLite 数据库，而 Docker 默认每次运行都会使用隔离的资源，会导致之前的同步内容丢失。\n我们需要考虑数据持久化的问题，创建一个 Volume：\n1# 创建名为 joplin 的 volume 2docker volume create joplin 下面的命令查看 volume 是否创建成功，可以看到，volumns 文件夹位于 /var/lib/docker 中。\n1$ docker volume inspect joplin 2[ 3 { 4 \u0026#34;CreatedAt\u0026#34;: \u0026#34;2021-02-20T12:09:19+08:00\u0026#34;, 5 \u0026#34;Driver\u0026#34;: \u0026#34;local\u0026#34;, 6 \u0026#34;Labels\u0026#34;: {}, 7 \u0026#34;Mountpoint\u0026#34;: \u0026#34;/var/lib/docker/volumes/joplin/_data\u0026#34;, 8 \u0026#34;Name\u0026#34;: \u0026#34;joplin\u0026#34;, 9 \u0026#34;Options\u0026#34;: {}, 10 \u0026#34;Scope\u0026#34;: \u0026#34;local\u0026#34; 11 } 12] 运行 Joplin Server 下面的命令在后台运行 Joplin Server，名称为 joplin_server，绑定端口为 22300，并把容器的 /home/joplin 绑定到 joplin 这个 volume：\n1docker run -d --name joplin_server -v joplin:/home/joplin --env-file /home/app/joplin/.env -p 22300:22300 joplin/server:latest 使用下面的命令查看运行状态：\n1$ docker ps -a 2 3CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 46074fa330192 joplin/server:latest \u0026#34;docker-entrypoint.s…\u0026#34; 3 days ago Up 3 days 0.0.0.0:22300-\u0026gt;22300/tcp joplin_server 使用下面的命令重启容器：\n1docker container restart joplin_server 配置 Nginx nginx 配置服务器\n1server { 2 server_tokens off; 3 root /srv/www/html; 4 index index.html index.htm; 5 server_name your-domain 6 client_max_body_size 100m; 7 8 location / { try_files $uri $uri/ @joplin; } 9 location @joplin { 10 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 11 proxy_set_header X-Forwarded-Proto $scheme; 12 proxy_set_header Host $http_host; 13 proxy_redirect off; 14 proxy_pass http://localhost:22300; 15 } 16} 建议开启 HTTPS 支持，可以参考我之前写的文章： 使用 Let's Encrypt 加入全站 HTTPS 支持。\n配置 Joplin Desktop 同步目标选择 Joplin Server。\n服务器 URL 保持和 .env 配置文件中的 APP_BASE_URL 配置相同。\nJoplin Server 支持多个客户端同步，可以把每个客户端的内容同步到不同的服务器目录。不同的目录可以在 Joplin Server 后台看到。如果你只有一个客户端，那么随便填写一个英文字符串就好。\n登录进入 https://your-sample-url/login ，使用默认的账户名 admin@localhost 和密码 admin 登录，然后修改账号和密码即可。\n在移动设备上也可以进行相同的设置。移动设备有个「同步状态」功能可以看到当前的同步状态：\n全文完 ","date":"2021-02-23","description":"配置 Joplin Server 以实现笔记同步。","lastmod":"2021-02-23T08:42:32Z","slug":"joplin-server-config","tags":["joplin","fromto"],"title":"配置 Joplin Server 实现同步","url":"https://blog.zengrong.net/post/joplin-server-config/"},{"categories":["technology"],"content":"我是 为知笔记 WizNote 的忠实+重度用户。使用 WizNote 接近 10 年，笔记 3000 多篇。日常工作和个人记事，读书笔记和备忘均在为知笔记上，每天都要使用它。\n在 2021 牛年春节假期中，我下定决心转向 Joplin 了。我花了几天时间开发了一个工具 wiz2joplin，目前已经成功将我的所有笔记完美转换到 Joplin 。\n这是一个怎样的故事？十年使用，有什么样的爱恨情仇？请读下去。\n我的知识管理工具尝试 myBase -\u0026gt; CyberArticle -\u0026gt; WizNote 我最早购买的知识管理工具是 myBase ，目前软件还在更新，而且出了 macOS 和 Linux 版本。\n由于 myBase 对于网页抓取处理不是太好，我切换到了网文快捕 CyberArticle。这个软件之前叫 WebCatcher，后来由于和某个国外软件重名，改成了 CyberArticle。\n后来网文快捕的作者魏时俊开发了 WizKnowledge，也就是为知笔记的前身。我切换到为知笔记 WizNote，一直用到现在。\n网文快捕吸引我的地方是网页抓取的强大。IE 时代，网页抓取功能是内置在网文快捕软件中的。后来转到 WizNote 之后，由于现代浏览器的发展，为知笔记为多个浏览器提供了以插件形式存在的抓取器，但在我看来，这些抓取器都不如网文快捕的抓取功能那么好用。\n为知笔记也推出过公众号文章保存功能，后来不知为什么停止支持了。\n为知笔记的全平台支持做的是很不错的，同步速度也非常快，这十年里我换了无数的设备，都能毫无困扰地同步旧的知识。点赞！\n切换到 macOS 2015 年我把主要工作环境切换到了 macOS(那时名叫 OS X)，为知笔记的 macOS 版本表现并不好，和 Windows 版本看起来像是两个不同的软件。但它一直在持续更新和优化，我也就一直在使用为知笔记来记录自己的工作、生活和思想。\n​这期间发生了两件对于为知笔记比较重要的事。\n一是 WizNote 开始收费。\n之前 WizNote 一直是免费使用状态，每个月只对上传和下载的流量做限制，但老魏给的限制奇大无比（我记得好像是 600MB？），基本上你很难把这个限制给用完。后来可能是有一些资金压力，为知笔记开始收费，价格也是很良心，我就一口气买到了 2024 年。\n二是 WizNote 改为使用网络搜索。\n这点对于 macOS 用户极端不友好。当网络在线的时候，搜索是通过 wiz.cn 的 API 进行的。我思考老魏做这个调整应该是希望和为知笔记的网页版做架构上的同步。虽然我从来不使用为知笔记的分享功能，但为知笔记网页版功能上做得还是挺不错的。遗憾的是，这个网络搜索功能并不好用，经常卡住不说，还会搜不到内容。我在 WizNote 为知笔记 macOS 版本本地文件夹分析 中分析了为知笔记文件夹后发现，为知笔记本地有一个数据库 wizthumb.db 专门用于离线搜索，但其中并不包含全文内容。也就是说，无论是网络搜索还是离线搜索，我都无法得到准确的搜索信息。这对一个拥有大量历史笔记的我来说是不可接受的。实际上，这也是我下决心转换到 Joplin 的最主要的原因。\n最后的坚持 WizNote lite WizNote 后来推出了 lite 版本，我试用时感觉很惊艳，但深入使用后，发现这是一个未完成的版本。lite 版本和 WizNote 之间无法实现双向同步，WizNote 也无法修改 lite 创建的文档。这对于老为知笔记用户来说是硬伤。\n印象笔记、OneNote、有道云笔记和其他 印象笔记、OneNote 和有道云笔记，是最常见的为知笔记的同类产品。我全部都试用过，没有选择它们的原因很简单： 没有一款软件能解决完美转换老笔记的到新平台的问题。\n印象笔记(Evernote) 和 OneNote 在我试用的时候似乎都有搜索性能问题。而 Ulysses、Bear 这类 macOS Only 的产品，我是注定不会选择的。\n感觉这两年为知笔记团队有点不思进取，或许是老魏精力太分散了？或许是为知笔记在企业服务上更容易赚钱所以忽视了个人用户？也可能是 macOS 用户太小众了，毕竟为知笔记 Windows 版本支持各种扩展，体验也相当不错。\n这十年来，非常感谢为知笔记一路陪伴，是时候改变了。\nJoplin 我关注了 Joplin 很久。直到它推出自己的同步服务器 joplin-server 之后，我觉得是时候转换了。\nJoplin 的优点不少，列一点在下面：\n开源 多平台支持： Windows/macOS/Linux/Android/iOS/Terminal 强大的全文搜索功能（后面提到） 优秀的快捷键支持，Goto Anything（键盘使用者会知道这个真心好用） 编辑器支持 Vim 绑定（我可是个重度 Vim 使用者） Web Clipper 支持，可以将网页抓取为 markdown 本地支持 API 调用，可以使用 API 来自动化管理文章（无限可能） 支持 Nextcloud, Dropbox, OneDrive, WebDAV 或者本地文件系统同步 支持 Joplin Server 同步 插件支持 顺便多说一点我最在意的 搜索功能 吧：\ndog cat 搜索包含 dog 和 cat 的文档 swim* 通配符支持 /\u0026quot;- [ ]\u0026quot; 源码搜索 any:1 cat dot 返回包含 dog 或 cat 的文档 title:office -body:world 搜索标题中包含 hello 但内容中不包含 world 的文档 tag:office tag:important 搜索同时包含 office 和 important 标签的文档 created:20201218 搜索所有 2020 年 12 月 18 日之后创建的文档 WizNote to Joplin 我为知笔记的 本地文件夹和数据库进行了分析，发现利用 Joplin Data API ，可以将为知笔记中的文档是完美转换到 Joplin。\n我花了一些时间对这十年的文档进行了整理，把 3000 多篇精简到 2000 多篇，在 2021 牛年春节期间，写了一个转换工具 wiz2joplin，完成了十年的转换。\n关于这个工具的设计和编码，就是另一篇文章了： 从 WizNote 为知笔记到 Joplin（下） 。\n引用 WizNote 为知笔记 macOS 版本本地文件夹分析 Joplin Joplin Data API Joplin Searching wiz2joplin 转换 WinzNote 到 Joplin 的开源工具 全文完 ","date":"2021-02-20","description":"转换十年的为知笔记到 Joplin，工具设计","lastmod":"2021-02-20T09:42:43Z","slug":"wiznote2joplin1","tags":["fromto","joplin"],"title":"从 WizNote 为知笔记到 Joplin（上）","url":"https://blog.zengrong.net/post/wiznote2joplin1/"},{"categories":["technology"],"content":"本文分析为知笔记 WizNote for Mac 2.8.7(2020.8.20 10:28) 本地文件夹结构以及数据库结构。\n分析这些信息，目的是为了从为知笔记转向其他的笔记系统。我写了一个 WizNote 向 Joplin 转换的工具，详见： WizNote to Joplin 。\n文件夹结构 ~/.wiznote/ 我使用的Wiznote 版本不是在 AppStore 下载的，因此软件文件夹位于 ~/.wiznote/。\n~/.wiznote/wiznote.ini 为知笔记的配置文件，其中包含用户 GID 等信息。\n~/.wiznote/cache/ avatar/ 不重要，略过 editor/ 不重要，略过 plugins/ 包含 markdown 插件。大量为知笔记的插件在 macOS 下无法使用。这里我只看到了 markdown 一个插件。应该是官方提供。 ~/.wiznote/log/ 为知笔记本地日志，以及同步日志。\n~/.wiznote/templates/ 为知笔记的模版，虽然我从来不用。\n其中的 .wiz 文件是一个 zip 压缩包，解压后可以得到 index.html 和 index_files/ 文件夹，其中包含模版使用的图像文件等。\n~/.wiznote/登录邮箱文件夹/ 这个文件夹中是特定用户的数据。包括对应用户的所有本地文章等等。这是最重要的数据。例如我的为知账户文件夹大小为 815.2MB 。\n以下所有文件或文件夹均以 ~/wiznote/登录邮箱文件夹/ 为父文件夹。\nwiznote.ini 这个配置文件就比上级文件夹下的同名文件内容要复杂多了。\n[FolderPosition] 存储的是所有目录的排序。键名是目录的名称。碰到中文就用 URL 转义了。 [TreeState] 存储的是目录树状态。奇怪的是，这里没有像上面一样使用目录名称做键名，而是改用了 GID。 true 代表有子目录，false 代表没有子目录。 SelectedItemID 代表当前选择的目录是哪个。每次选择目录后，这个 ini 文件会被更新。 另外两个不重要的配置 [Theme] 和 [MarkdownTemplate] 就不强行解释了。 analyzer.ini/analyzerEx.ini 应该不重要，不强行解释。\ndata/ 最重要的文件夹。 所有本地内容均在此。\ndata/attachmets/ 文档附件。文件名为 {GUID}原始文件名.原始扩展名，中文也未做转义。\ndata/index.db 主数据库，SQLite 格式。\ndata/wizthumb.db 文章的摘要数据库，SQLite格式，或许用于搜索？\ndata/notes/ 所有文章压缩包。每个文件名为 {GUID}，没有扩展名。解压后包含 index.html 和 index_files/ 文件夹，文件夹中包含所有文档使用的图片文件。\ngroup/ 团队笔记。其中的文件夹为用户的 GUID，每个用户的子文件夹结构与 data/ 文件夹相同。\n数据库结构 index.db WIZ_DELETED_GUID 无内容，不解释。\n1CREATE TABLE WIZ_DELETED_GUID 2( 3 DELETED_GUID char(36) not null, 4 GUID_TYPE int not null, 5 DT_DELETED char(19), 6 primary key (DELETED_GUID) 7) WIZ_DOCUMENT 最重要的表。\n1CREATE TABLE WIZ_DOCUMENT 2( 3\t# 文档的 GUID 4 DOCUMENT_GUID char(36) not null, 5\t# 文档标题 6 DOCUMENT_TITLE varchar(768) not null, 7\t# 文档位于为知目录树中的路径 8 DOCUMENT_LOCATION varchar(768), 9\t# 文档的标题文件名，ziw 格式，标题中的空格被替换成 _，估计这个字段没有使用了，因为 notes 文件夹中的文件名均为 GUID。也有可能这个字段是在 Windows 中使用的。 10 DOCUMENT_NAME varchar(300), 11\t# 大部分没有值，有些值为 GUID，不明其意 12 DOCUMENT_SEO varchar(300), 13\t# 如果是采集的文章，那么就包含采集的 URL，否则为 NULL 14 DOCUMENT_URL varchar(2048), 15\t# 大部分文章为 NULL，可能是采集时的作者标题 16 DOCUMENT_AUTHOR varchar(150), 17\t# 大部分文章为 NULL 18 DOCUMENT_KEYWORDS varchar(300), 19\t# 部分文章为 document，猜测与内链有关。包含内链或者被内链的文章为 document，直接采集的文章为 webclip/webnote ，编辑过的文章的 document。 20 DOCUMENT_TYPE varchar(20), 21\t# 值为用户邮箱 22 DOCUMENT_OWNER varchar(150), 23\t# att/html 或者扩展名格式，例如 .xls/.doc/.jpg/.html 24 DOCUMENT_FILE_TYPE varchar(20), 25 STYLE_GUID char(38), 26 DT_CREATED char(19), 27 DT_MODIFIED char(19), 28 DT_ACCESSED char(19), 29 DOCUMENT_ICON_INDEX int, 30 DOCUMENT_SYNC int, 31 DOCUMENT_PROTECT int, 32 DOCUMENT_READ_COUNT int, 33\t# 附件数量，默认为 0 34 DOCUMENT_ATTACHEMENT_COUNT int, 35 DOCUMENT_INDEXED int, 36 DT_INFO_MODIFIED char(19), 37 DOCUMENT_INFO_MD5 char(32), 38 DT_DATA_MODIFIED char(19), 39 DOCUMENT_DATA_MD5 char(32), 40 DT_PARAM_MODIFIED char(19), 41 DOCUMENT_PARAM_MD5 char(32), 42 WIZ_VERSION int64, 43\t# 下面两个 CHANGED 是同时改变的 44 INFO_CHANGED int default 1, 45 DATA_CHANGED int default 1, 46 primary key (DOCUMENT_GUID) 47) WIZ_DOCUMENT_ATTACHMENT 附件表。\n1CREATE TABLE WIZ_DOCUMENT_ATTACHMENT 2( 3\t# 附件的 GUID 4 ATTACHMENT_GUID char(36) not null, 5\t# 附件所属文档的 GUID 6 DOCUMENT_GUID varchar(36) not null, 7\t# 附件加入时的文件名和扩展名 8 ATTACHMENT_NAME varchar(768) not null, 9\t# 若不为 NULL，则为附件在硬盘上的完整路径（加入时） 10 ATTACHMENT_URL varchar(2048), 11 ATTACHMENT_DESCRIPTION varchar(600), 12 DT_INFO_MODIFIED char(19), 13 ATTACHMENT_INFO_MD5 char(32), 14 DT_DATA_MODIFIED char(19), 15 ATTACHMENT_DATA_MD5 char(32), 16 WIZ_VERSION int64, 17 primary key (ATTACHMENT_GUID) 18) WIZ_TAG 所有 TAG 的定义，不需要解释。\n1CREATE TABLE WIZ_TAG 2( 3 TAG_GUID char(36) not null, 4 TAG_GROUP_GUID char(36), 5 TAG_NAME varchar(150), 6 TAG_DESCRIPTION varchar(600), 7 DT_MODIFIED char(19), 8 WIZ_VERSION int64, 9 TAG_POS\tint64, 10 primary key (TAG_GUID) 11) WIZ_DOCUMENT_TAG TAG 与文档的对应关系表。不需要解释。\n1CREATE TABLE WIZ_DOCUMENT_TAG 2( 3 DOCUMENT_GUID char(36) not null, 4 TAG_GUID char(36) not null, 5 primary key (DOCUMENT_GUID, TAG_GUID) 6) WIZ_META 部分配置文件，不需要解释。\n1CREATE TABLE WIZ_META 2( 3 META_NAME varchar(50), 4 META_KEY varchar(50), 5 META_VALUE varchar(3000), 6 DT_MODIFIED char(19), 7 primary key (META_NAME, META_KEY) 8) WIZ_OBJECT_EX 所有的对象的 GUID 表。其中 OBJECT_TYPE 值为 document/attachment，应该是方便寻找所有对象的 GUID。\n1CREATE TABLE WIZ_OBJECT_EX 2( 3 OBJECT_GUID char(36) not null, 4 OBJECT_TYPE char(20) not null, 5 OBJECT_RESERVED1\tint, 6 OBJECT_RESERVED2\tint, 7 OBJECT_RESERVED3\tint, 8 OBJECT_RESERVED4\tint, 9 OBJECT_RESERVED5\tvarchar(200), 10 OBJECT_RESERVED6\tvarchar(500), 11 OBJECT_RESERVED7\tvarchar(1000), 12 OBJECT_RESERVED8\tvarchar(5000), 13 14 primary key (OBJECT_GUID, OBJECT_TYPE) 15) wizthumb.db 仅包含一个表。\nWIZ_ABSTRACT ABSTRACT_GUID 文章的 GUID ABSTRACT_TYPE 数据库中的固定值为 PAD，不知道是否有其他值 ABSTRACT_TEXT 文章的摘要内容，最长 3000 字节 全文完 ","date":"2021-02-20","description":"分析为知笔记的 macOS 版本本地文件夹和数据库结构，方便转换到其他笔记软件。","lastmod":"2021-02-20T08:22:24Z","slug":"analysis-of-wiznote","tags":["sql"],"title":"WizNote 为知笔记 macOS 本地文件夹分析","url":"https://blog.zengrong.net/post/analysis-of-wiznote/"},{"categories":["tutorial"],"content":"Python 日课 2021-01-21：Python2 和 Python3 的区别。\n曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 各位 Python 同学大家晚上好啊。本周的内容有点难哦，今天不讲新课，聊聊 Python 小知识： Python2 和 Python3 的区别。\n要强调的是， 不要学习 Python2！不要学习 Python2！不要学习 Python2！\n重要的事情说三遍。\nPython2 已经在 2020年 1 月 1 日停止维护。2020 年 4 月 20 日发布的 Python 2.7.18 是 Python2 的最后一个版本。\n官方说明在此：\nhttps://www.python.org/dev/peps/pep-0373/\n那么，Python2 和 Python3 有哪些区别呢？\n首先，最常用的 print 在 python2 中是一条语句，在 Python3 中则是函数：\n1# python2: 2print \u0026#34;hello world\u0026#34; 3 4# python3: 5print(\u0026#34;hello world\u0026#34;) 其次，整数相除的结果不同。\n在 Python2 中，整数相除的结果是整数： 3 / 2 = 1 。\n在 Python3 中，相同除法的结果是浮点数： 3 / 2 = 1.5 。\n第三，Unicode 支持不同。\nPython3 的源码中原生支持 Unicode 编码，而Python2 必须在源文件中指明 coding 作为编码。\n我们在 Python日课-2.3-要不要coding声明 中讲过这一点。\n当然，Python2 与 3 的区别远不止上面这三条，例如还有异常处理的不同，range 用法不同，八进制字面量区别，不等运算符，repr 表达式，内部模块改名等等区别。\n但由于我们不用再学习这些区别，曾老师这里就不一一叙述了。\n我在 2015 年刚开始学习 Python 的时候，写过一篇文章：Python 入门建议\n用这篇文章中的一段话来作为今天的结尾：\nPython2 还是 Python3 ？\n毫无疑问，Python2 是辉煌，Python3 是未来。不要相信网上那些大牛吐槽 Python3 速度慢、兼容性差、库少等等言论了，仔细看看那些言论的发表时间。现在都特么已经2015了好么。\n如果某个库到现在还没有兼容 Python3，那么放弃它就是了，因为它的作者已经抛弃了它。\n使用 Python3 ，你不会再碰到恶心的 i18n 问题，也不会再面对那些奇怪的2包名了。\n选 Python3 吧，因为你属于未来。\n2015-07-03\n问答 学习群中的 给你一颗小土豆 同学的提问：\n虽然第三点还有点看不懂\n曾老师的回答：\nUnicode 就是「万国码」\n支持使用一种编码方式，实现世界上所有的语言。\n在Unicode 出现之前，各种不同的语言会有不同的编码。\n比如中文的 GB2312/GBK，中国台湾的 BIG5，英文的 Latin1。\n在读取文本的时候，如果选错了编码，就会出现「乱码」。\n如早期 Win98 时代，在简体中文系统中打开繁体中文的文本，就会显示乱码，因为不同编码之间无法转换。\nUnicode 就解决了这个问题。用同一种编码，编码全世界所有的文字。\n你可以在一个文本文件中同时显示中文、英文、日文、俄文、西欧语系等等。\n更详细的内容，你可以阅读阮一峰的这篇文章：\nhttp://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html\n这篇文章深入浅出，写得很好。对于非计算机专业的同学来说，很好懂。\nUnicode 机构的官网：\nhttps://home.unicode.org/\n对了，顺便提一下，我们现在已经非常熟悉的 emoji ，就是输入法中的哪些表情符号，也是 unicode 的一部分哦。例如下面这个：\n🐂🍺\n全文完 ","date":"2021-01-22","description":"曾老师的 Python 日课：Python 版本选择","lastmod":"2021-01-22T04:27:58Z","slug":"python-daily-python23","tags":["pythoncourse","python"],"title":"Python日课-3.3-python 2 和 3 的区别","url":"https://blog.zengrong.net/post/python-daily-python23/"},{"categories":["tutorial"],"content":"Python 日课 2021-01-19：定义函数与 pillow 图像处理。\n曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 各位 Python 同学大家好啊，今天的课程来啦。\n在昨天的 Python日课-3.1-Pillow滤镜初识 中，我们学习了 Pillow 高斯模糊和模式滤镜的使用。今天我们继续讲 Pillow，同时学习一下拆分字符串、定义函数。\n我们经常需要对图像进行旋转和放大缩小的操作，这可以使用 Pillow 中的 resize 和 rotate 方法来进行。\nresize 文档： https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.resize\nrotate 文档： https://pillow.readthedocs.io/en/latest/reference/Image.html#PIL.Image.Image.rotate\n在昨天的课程中，我们直接提供了要被处理的图像文件名，以及处理后的图像文件应该存放的文件名。\n但在实际的工作中，我们往往需要指定不同的文件名，获取文件名是一个会被多次调用的重复操作。在编程世界中，我们一般将重复的操作定义成一个函数。\n下面就是这个函数的内容：\n1def get_file(name, suffix): 2 \u0026#34;\u0026#34;\u0026#34; 返回 assets 文件夹下的原始文件和目标文件 3 4 :param name: 原始文件名 5 :param suffix: 需要被增加到目标文件名中的后缀 6 :returns: 原始文件，目标文件 7 \u0026#34;\u0026#34;\u0026#34; 8 9 sfile = basedir.joinpath(\u0026#39;assets\u0026#39;, name) 10 # 假设 name 的值为 \u0026#39;前台.jpg\u0026#39; ，我们需要分别得到 \u0026#39;前台\u0026#39; 和 \u0026#39;jpg\u0026#39; 两个字符串，此时需要使用 split 方法，基于英文句号将字符串拆成两个 11 # sfile.name.split(\u0026#39;.\u0026#39;) 会返回一个 list，包含两个字符串。fname 和 fext 中分别包含 \u0026#39;前台\u0026#39; 和 \u0026#39;jpg\u0026#39; 这两个值 12 fname, fext = sfile.name.split(\u0026#39;.\u0026#39;) 13 14 # 使用 f 开头的字符串支持替换，下面的 suffix 等花括号中的变量会自动被替换为变量中的值 15 # 假设 suffix 的值为 \u0026#39;rotate90\u0026#39;，那么 tfilename 的值为 \u0026#39;前台_rotate90.jpg\u0026#39; 16 tfilename = f\u0026#39;{fname}_{suffix}.{fext}\u0026#39; 17 tfile = basedir.joinpath(\u0026#39;assets\u0026#39;, tfilename) 18 19 # 返回原始文件和目标文件 20 return sfile, tfile 这里出现了一些新的东西，我来介绍下。\ndef 是 python 中定义函数的语句。一个函数包含名称、参数和返回值。\nget_file 是这个函数的名称。这个函数支持两个参数 name 和 suffix ，包含在函数名称后面的括号中，最后需要用一个英文冒号声明函数体的内容。\n下面包含在三个双引号之间的内容叫做 DocString ，你可以把它理解成一种注释，用来说明这个函数怎么运作。\n这个函数要做的事情，就是通过提供的 name 参数，以及 suffix 参数，返回两个文件，前面一个文件是原始文件，后面一个文件是目标文件。\n通过这个函数，我们只需要提供一个原始文件名，就能直接生成目标文件，在使用的时候会方便不少了。\n具体的实现，大家可以看其中的注释内容，已经很详细啦，不明白的同学，可以直接在群里提问。\n下面我们要来使用 get_file 这个函数。\n1def rotate(source_name, angle): 2 \u0026#34;\u0026#34;\u0026#34; 旋转 source 图像文件到 angle 角度，并保存到新文件 3 4 :param source_name: 原始文件名 5 :param angle: 浮点数，旋转的角度。90 代表顺时针旋转 90 度 6 \u0026#34;\u0026#34;\u0026#34; 7 # 获得原始文件和目标文件，其中 f 字符串的用法在 get_file 中介绍了 8 sfile, tfile = get_file(source_name, f\u0026#39;rotate{angle}\u0026#39;) 9 10 simg = Image.open(sfile) 11 timg = simg.rotate(angle) 12 timg.save(tfile) 我们定义了另一个函数 rotate。rotate 函数提供旋转一张图片的功能，里面的代码没有新知识，在上节课都已经介绍了。唯一的区别就是在 rotate 函数中调用 get_file 来获取原始文件和目标文件。\n除了 rotate 功能外，我们还做了一个 scale 函数，用来缩小或者放大图像。\n1def scale(source_name, ratio): 2 \u0026#34;\u0026#34;\u0026#34; 改变 source 图像大小，使用 ratio 作为比例，保存到新文件 3 4 :param source_name: 原始文件名 5 :param ratio: 浮点数，代表调整的比例。 0.5 代表调整为原始大小的 50% 6 \u0026#34;\u0026#34;\u0026#34; 7 8 # 获得原始文件和目标文件，其中 f 字符串的用法在 get_file 中介绍了 9 sfile, tfile = get_file(source_name, f\u0026#39;rotate{ratio}\u0026#39;) 10 11 simg = Image.open(sfile) 12 13 # 获取图像调整后的大小，使用 int 来将浮点数转换成整数 14 width = int(simg.width * ratio) 15 height = int(simg.height * ratio) 16 17 # (width, height) 是一个参数，同时提供两个值 18 timg = simg.resize((width, height)) 19 timg.save(tfile) 接下来，我们可以调用 rotate 和 scale 函数，看看效果如何：\n1# 把 前台.jpg 这个文件旋转 90 度 2rotate(\u0026#39;前台.jpg\u0026#39;, 90) 3 4# 把 前台.jpg 这个文件缩小 50% 5scale(\u0026#39;前台.jpg\u0026#39;, 0.5) 6 7# 把 餐厅.jpg 这个文件旋转 180 度 8rotate(\u0026#39;餐厅.jpg\u0026#39;, 180) 上面所有的内容保存为 pillow3.py，调用它：\n1python pillow3.py 今天的课程就到这里啦，欢迎大家到群里讨论。\n下载源码附件：\n1 文件 全文完 ","date":"2021-01-19","description":"曾老师的 Python 日课：定义函数与 pillow 图像处理","lastmod":"2021-01-19T00:44:20Z","slug":"python-daily-pillow2","tags":["pythoncourse","python"],"title":"Python日课-3.2-定义函数与 pillow 图像处理","url":"https://blog.zengrong.net/post/python-daily-pillow2/"},{"categories":["tutorial"],"content":"Python 日课 2021-01-18：Pillow 滤镜初识。\n曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 各位同学大家好哇，今天周一，我们的 Python 课堂又来啦。\n在上周三的课程 Python日课-2.4-pip的使用 中，我们安装了 pillow 库，今天我们就来看看 pillow 能干点啥。\npillow 是一个开源的图像处理库，虽然是一个外部库，但它已经成为了 Python 事实上的图像处理标准。Pillow 的官网在这里：\nhttps://pillow.readthedocs.io/en/stable/\n如果你更喜欢看中文文档的话，可以参考这里：\nhttps://pillow-cn.readthedocs.io/zh_CN/latest/\n我们使用最新的 Pillow 8.1 来做讲解，中文文档是基于 Pillow 2.4 翻译的。\n完成今天的课程，曾老师准备了两张图片：\n没错，这两张图片就是 SAGI GAMES 现场用手机拍摄的。\n为了方便讲述，我们所有的代码均位于 pycourse 文件夹中，这两张照片位于 pycourse/assets/ 文件夹中，结构如下：\npillow1.py 文件的作用是将 「餐厅.jpg」进行高斯模糊处理，处理后的文件名为「餐厅_高斯模糊_10.jpg」。\npillow2.py 文件的作用是将「前台.jpg」进行模式滤镜处理，处理后的文件名为「前台_模式_10.jpg」。\npillow1.py 的内容如下：\n1# 曾老师的 Python 课 2# 课程地址：https://blog.zengrong.net/tag/pythoncouse/ 3# 课程内容：2021-01-18 pillow 高斯模糊滤镜 4# 高斯模糊滤镜文档： https://pillow.readthedocs.io/en/4.1.x/reference/ImageFilter.html#PIL.ImageFilter.GaussianBlur 5 6from pathlib import Path 7from PIL import Image 8from PIL.ImageFilter import GaussianBlur 9 10# 当前文件所在文件夹 11basedir = Path(__file__).parent 12 13# 原始图像文件 14sfile = basedir.joinpath(\u0026#39;assets/餐厅.jpg\u0026#39;) 15# 目标图像文件 16tfile = basedir.joinpath(\u0026#39;assets/餐厅_高斯模糊_10.jpg\u0026#39;) 17 18# 创建一个高斯模糊滤镜对象，模糊半径为 10 19gb = GaussianBlur(radius=10) 20 21# 打开原始图像 22simg = Image.open(sfile) 23# 对原始图像文件使用滤镜，返回一个目标图像 24timg = simg.filter(gb) 25 26# 将应用过滤镜的图像文件保存到目标图像文件中 27timg.save(tfile) pillow2.py 的内容如下：\n1# 曾老师的 Python 课 2# 课程地址：https://blog.zengrong.net/tag/pythoncouse/ 3# 课程内容：2021-01-18 pillow 模式滤镜 4# 模式滤镜文档： https://pillow.readthedocs.io/en/4.1.x/reference/ImageFilter.html#PIL.ImageFilter.ModeFilter 5 6from pathlib import Path 7from PIL import Image 8from PIL.ImageFilter import ModeFilter 9 10# 当前文件所在文件夹 11basedir = Path(__file__).parent 12 13# 原始图像文件 14sfile = basedir.joinpath(\u0026#39;assets/前台.jpg\u0026#39;) 15# 目标图像文件 16tfile = basedir.joinpath(\u0026#39;assets/前台_模式_10.jpg\u0026#39;) 17 18# 创建一个模式滤镜对象，size 为 10 19gb = ModeFilter(size=10) 20 21# 打开原始图像 22simg = Image.open(sfile) 23# 对原始图像文件使用滤镜，返回一个目标图像 24timg = simg.filter(gb) 25 26# 将应用过滤镜的图像文件保存到目标图像文件中 27timg.save(tfile) 分别执行：\n1python pillow1.py 2python pillow2.py 就可以使用滤镜生成新的图像啦！生成的图像位于 pycourse/assets/ 文件夹中。\n今天的课程就到这里啦，欢迎大家到群里讨论。\n下载源码附件：\n1 文件 全文完 ","date":"2021-01-18","description":"曾老师的 Python 日课：pillow滤镜初始","lastmod":"2021-01-18T03:00:44Z","slug":"python-daily-pillow1","tags":["pythoncourse","python"],"title":"Python日课-3.1-pillow滤镜初识","url":"https://blog.zengrong.net/post/python-daily-pillow1/"},{"categories":["tutorial"],"content":"Python 日课 2021-01-14： Python语言的不同实现。\n曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 各位同学，今天来说说Python语言的种类吧。\nCPython https://python.org\n我们平时说的Python，更多的是指（Python的C语言实现）。它的全称是CPython。\nPython是一门解释型语言，它的底层是用C语言来实现的，这也是最常用一种Python实现。如果不加解释的话，CPython就是Python的代表了。\n既然用C语言能实现Python，那么Java行不行呢？\nJython 当然行，所以有Jython。\nhttps://www.jython.org/\nJython 是 Python 语言的 Java 实现。它将代码编译为 Java 字节代码，开发人员在 Python 模块中可以无缝使用 Java 类。\nIronPython 微软当然要来凑热闹的。\nhttps://ironpython.net/\n微软的一群人把Python引入了微软的 .net 框架，所以有了 IronPython。\n这个项目受到微软的支持，因为 IronPython 的主要开发人员都在微软工作。\nPyPy 另一个非常著名的Python实现是PyPy。\nhttps://www.pypy.org/\nPyPy 可能是最令人兴奋的 Python 实现，因为其目标就是将 Python 重写为 Python。在 PyPy 中，Python 解释器本身是用 Python 编写的。而且，PyPy 的运行速度是 CPython 的4倍。\n当然 PyPy 也有缺点：许多用C语言实现的模块在 PyPy 中无法使用。\nCython 最后说一个容易和CPython混淆的实现：Cython。\nhttps://cython.org/\nCython是让Python脚本支持C语言扩展的编译器，Cython能够将Python+C混合编码的.pyx脚本转换为C代码，主要用于优化Python脚本性能或Python调用C函数库。\n当需要高性能的库时，程序员可以选择使用Cython来编写CPython可以调用的模块。\n上节课我们讲过Python模块，有很多对性能要求高的数据结构，列表等内置模块，就是Cython编写的。\nCython编写的代码，在极端情况下，可以比 CPython编写的代码运行速度快上几十倍。实际上Cython的速度就是C语言的速度。\n当然，不同的要选择需要不同的环境。极端的速度比较没有意义。\n如何选择 对于曾老师的Python课上的同学，大家学好CPython就好啦！\n刚才我们讲到了几种Python：\nCPython 最常用的 Python PyPy 用 Python 实现的 Python，支持 JIT Jython 用Java 实现的 Python IronPython .NET 平台下的 Python Cython Python 和 C/C++ 语言语法的混合体，一般用来扩展 Python ，实现更高性能的 Python 模块。 ​​为了让 Python 更快更方便，程序员们也是操碎了心。其实 Python 语言的实现远不止上面这五个，你还能找出更多么？\n问答 学习群中的 Aaron 同学的提问：\n曾老师 下午好呀\n我想问下 那些做数据监测的第三方服务商比如app annie/sensor tower 主要依靠的是什么技术呢？\n曾老师的回答：\n如果是说数据获取方式，Annie 的主要来源有三个：\n一是与他们自有的 App/SDK 以及与他们合作的 App/SDK，这些 App 会将数据回传到 Annie 二是开发者自己的上报，在 Annie 后台可以填写 Itunes Connect 帐号，Annie 拿到帐号当然是可以为所欲为 三应该是爬虫。Annie 肯定有爬虫。 Annie 的数据主要还是通过大数据加上 AI 做预测，数据越多预测越准。根据我们的比较，当数据量没有那么大的时候，很多数据是不准确的，主要看趋势吧。\n全文完 ","date":"2021-01-14","description":"曾老师的 Python 日课：python语言的不同实现","lastmod":"2021-01-14T00:34:22Z","slug":"python-daily-type-of-python","tags":["pythoncourse","python"],"title":"Python日课-2.5-python语言的不同实现","url":"https://blog.zengrong.net/post/python-daily-type-of-python/"},{"categories":["tutorial"],"content":"Python 日课 2021-01-13：pip 的使用。\n曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 今天的Python日课，我们来讲讲学习 Python 过程中最重要的一个工具：pip 。\npip 的全称是 Package Installer for Python，对于我们这些普通的（不以成为程序员为目的）Python 的使用者来说，pip 是仅次于 Python 解释器本身的最重要的工具，没有之一。\nPython 的那些强大和易用的功能，有许多不是 Python 本身所具备的，而是来自于大量的库（Library/Module）。库分为两种，一种是标准库，一种是外部库（第三方库）。\n库和模块 这里的库（LIbrary）和模块（MOdule）是同义语。Python 中的标准叫法是模块（Module），但业界有一种更通用的叫法是库（Library）。这种通用的叫法可能主要来自于 C/C++ 语言的标准库的概念。\nC 语言标准库： https://en.cppreference.com/w/c/header C++ 语言标准库： https://en.cppreference.com/w/cpp/header 标准库/内置模块 Python 安装的过程中自带的，由 Python 官方社区所支持和提供维护的库叫 标准库 或者 内置模块。例如我们在 Python日课-2.1-Path模块的使用 中讲到的 Path 模块，就是 Python 自带的内置模块。\n我们可以在这里找到所有的 Python 内置模块： https://docs.python.org/zh-cn/3/py-modindex.html\n标准库/内置模块 默认存在，不需要安装就可以在 Python 中使用。\n外部库/外部模块 外部库是由 Python 的使用者（广大的程序员们）开发的库，他们遵循 Python 官方社区提供的标准，提供 Python 内置模块没有的，或者内置模块做得不够好的功能。在使用方法上，Python 的外部模块和内置模块没有什么不同。\n安装外部库/外部模块 外部模块需要安装才可以使用。pip 就是安装外部模块的工具。\npip 自己也是是个外部模块，在安装 Python 的时候会自动安装。\n下面以在 Python3.9.1 中安装外部模块 pillow 为例，讲解一下 pip 的用法：\n1# 进入命令行，输入下面的命令，安装 pillow 模块。 2python -m pip install pillow 上面的命令中，python -m pip 告知 python 要使用 pip 这个模块来搞事情。install pillow 则是 pip 这个模块提供的子命令，意为安装 pillow 这个模块。\n1# 安装成功后，通过 show 子命令查看已经安装的模块信息 2python -m pip show pillow 1# 通过 list 子命令查看所有已经安装的模块列表 2python -m pip list 扩展阅读 pip 模块有许多功能，可以使用 --help 参数来查看用法。\n如果在安装外部库的时候速度比较慢，原因是因为 pip 的安装源在国外，可以将其换成国内源。本课程不详述了，想了解详细可以参考我博客上的这篇文章：常用开源镜像站整理 。\n如果想了解更多关于 pip 的细节，可以参考我博客上这这篇文章： Python 包管理工具解惑 。\n全文完 ","date":"2021-01-13","description":"曾老师的 Python 日课，pip 的使用","lastmod":"2021-01-13T08:52:29Z","slug":"python-daily-pip","tags":["pythoncourse","python"],"title":"Python日课-2.4-pip的使用","url":"https://blog.zengrong.net/post/python-daily-pip/"},{"categories":["tutorial"],"content":"本文是 Python 日课 2021-01-12 的内容整理。\n曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 今天 SAGITEAM 的毕老板在公司群里面发了一段 Python 代码：\n1# -*- coding: UTF-8 -*- 2 3# Filename : helloworld.py 4# author by : caibi 5 6# 该实例输出嘲讽! 7print(\u0026#39;不要这样，我们程序员不喜欢\u0026#39;) 然后被程序员吐槽了：特别讨厌这种头部加 UTF-8 的东西。\n如果大家经常阅读 Python 源码，可能也会看到类似的源码头部声明。那么问题来了：Python 源代码头部类似于 coding: utf-8 这样的声明是否需要呢？\n来看看 PEP 263 的内容： https://www.python.org/dev/peps/pep-0263/\n在 Python2 中，默认的 Python 源码是使用 ASCII 编码保存的。但 ASCII 编码是不支持中文、日文、韩文字符的。因此我们需要将 Python 源码使用 UTF-8 编码来保存。这可能导致 Python 解密源码的时候出错。\n因此， PEP 263 定义了在源码开头加入声明的方式来让 Python 解析器能识别 UTF-8 编码的源文件。\n把下面内容放在源码开头就可以了 \u0026lt;encoding name\u0026gt; 代表字符编码。比如 utf-8，latin-1。\n1# coding=\u0026lt;encoding name\u0026gt; 实际上，并不是必须遵循上面的格式，只要遵循下面的正则表达式，Python 就可以识别这行代码。\n1^[ \\t\\f]*#.*?coding[:=][ \\t]*([-_.a-zA-Z0-9]+) 但是 Python3 已经默认使用 UTF-8 编码来保存源码，因此在 Python3 中不需要加入编码声明。\n详见：PEP 3120 -- Using UTF-8 as the default source encoding ：https://www.python.org/dev/peps/pep-3120/\n曾老师的 Python 课全部课程使用 Python3 来讲解，而且代码仅支持 Python3，我们不需要这个编码声明。\n全文完 ","date":"2021-01-12","description":"曾老师的 Python 日课，是否需要源码 coding 声明","lastmod":"2021-01-12T07:03:08Z","slug":"python-daily-coding","tags":["pythoncourse","python"],"title":"Python日课-2.3-要不要coding声明","url":"https://blog.zengrong.net/post/python-daily-coding/"},{"categories":["tutorial"],"content":"本文是 Python 日课 2021-01-08 的内容整理。\n曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 各位同学大家好哇，今天在群里有同学问了 IDE 的问题，那我们就讲讲本地 IDE 的选择吧。\n2021-01-04 的课程 中我们讲解了几款 Web IDE。Web IDE 适合做在线测试，但我们有许多学习工作需要在本地电脑上进行，所以我们需要安装一个本地 IDE。 许多教程会推荐你使用 PyCharm （https://www.jetbrains.com/pycharm/），PyCharm 当然好，但它是商业软件，价格不菲，我并不推荐。\nVisual Studio Code 有一个功能同样强大的 IDE，免费且有大厂持续支持，这就是 Visual Studio Code (https://code.visualstudio.com/) 。\nVisual Studio Code 经常被简称为 VSCode，是微软（没错，就是你们知道的那个微软）开发的一款 IDE ，支持多种编程语言，当然也包括 Python。\n访问 https://code.visualstudio.com/ ，根据你的设备下载对应版本即可。Windows 用户建议下载 User Installer 64bit。\n安装成功后，访问这个地址： https://marketplace.visualstudio.com/items?itemName=ms-python.python ，继续安装对 Python 语言的支持：\n如果你的 VSCode 安装正常，那么系统会自动调用 VSCode 来安装这个插件。\n安装成功之后，你就可以使用 VSCode 来编辑 Python 文件啦！打开 VSCode，熟悉一下 IDE 的界面，尝试创建一个项目试试看！\n替代品 如果你觉得 VSCode 安装起来比较麻烦，可以换几个更简单的替代品。\n在 Windows 上，我建议你用 Notepad++： https://notepad-plus-plus.org ，这是一款老牌的开源的编辑器，支持多种语言。\n在 MacOS 上，我建议你用 Geany： https://www.geany.org，支持多种语言，启动速度飞快。 Geany 也同样支持 Windows 系统。\n在 Linux 上，我没啥建议。毕竟你都已经用 Linux 了，你能自己找到喜欢的编辑器的。\n小知识 今天有同学在群里问 Python 是前端还是后端。回答如下：\nPython 是一个前后端通吃的语言。\nPython 做前端功能的时候，就是前端。例如 Python 可以开发游戏，游戏面向用户的部分，通过 Python 来编码、渲染、展示，此时 Python 就在「前端」工作。\nPython 做后端功能的时候，就是后端。例如 Python 可以开发网站后端支持，与数据库通信，响应用户的请求，返回服务器中的资源，此时 Python 就在「后端」工作。\n许多编程语言都是前后端通吃的。例如鼎鼎有名的 JavaScript，曾经是一门地地道道的前端语言，但现在也经常用来写服务器程序。如果你对「成为一个程序员」感兴趣，就不要纠结于前端还是后端。一个优秀的程序员，必须是前后端通吃的。否则你的发展就会很受限。\n全文完 ","date":"2021-01-08","description":"曾老师的 Python 日课，IDE的选择","lastmod":"2021-01-08T07:03:08Z","slug":"python-daily-install-ide","tags":["pythoncourse","python"],"title":"Python日课-2.2-IDE的选择","url":"https://blog.zengrong.net/post/python-daily-install-ide/"},{"categories":["tutorial"],"content":"本文是 Python 日课 2021-01-07 的内容整理。\n曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 各位同学大家好哇，今天的课程，我们来学一下 Path 这个模块的基本用法。\nPath 模块的作用是获取本地文件路径。所以，今天的课程内容必须在本地环境中执行。 进入 Python 交互环境，输入下面的代码：\n1from pathlib import Path 2Path().home() 输出如下：\n介绍一下上面的两行代码：\n第一行代码从 pathlib 模块中导入了 Path 这个子模块。 第二行代码创建了一个 Path 对象，然后调用它的 home 方法。 其中的英文句号 . 用来调用方法，注意在调用方法的时候，需要在方法名称后面加上英文半角的括号： () 。\n执行的结果就是我的 home 目录的路径。\n下面再多做一点。\n在我的 home 目录下面有个文本文件叫做 a.txt，我要用 Python 显示其中的文本内容：\n1Path().home().joinpath(\u0026#39;a.txt\u0026#39;).read_text() 结果如下：\n介绍一下上面这行看起来比较复杂的代码。\n这里并没有太多新东西。使用 . 和 () 来调用方法是我们在上面已经学会的。我们用 . 来持续调用 Path 对象的方法，这种调用方式就像一个链条一样把所有的方法串起来，我们给它起个名字叫 链式调用 。\njoinpath 是一个方法，在这行代码中，它的作用是从 home 文件夹中找到 a.txt 这个文件。需要用英文半角的单引号 ' 把文件名包裹起来。\nread_text 是一个方法，用来读取这个文件的内容。\n今天的内容就到这里啦。有不懂的请提问哦！\n全文完 ","date":"2021-01-07","description":"曾老师的 Python 日课，Path 模块使用","lastmod":"2021-01-07T06:55:45Z","slug":"python-daily-use-path","tags":["pythoncourse","python"],"title":"Python日课-2.1-Path模块的使用","url":"https://blog.zengrong.net/post/python-daily-use-path/"},{"categories":["tutorial"],"content":"曾老师的 Python 课 第一课的所有内容已经全部发布，包括课程 PPT、课程视频都可以免费获取，可以使用微信扫描下面的二维码进群获取。\n这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 本文是 Python 课开启 第一周（2020-12-31至 20201-01-05） 每日 Python 知识点的整理。\nPython 小史（2020-12-31） 各位Python同学早上好啊，今天的Python小知识来啦。\nPython 由 Guido van Rossum 于 1989 年底发明，第一个公开发行版发行于 1991 年。\n像 Perl 语言一样, Python 源代码同样遵循 GPL(GNU General Public License) 协议。\n从上面的信息可以看出，Python是个接近三十年的「老」语言了。另外几个著名的大家经常谈论的「老」语言是哪些呢？\n1964 – BASIC 1970 – Pascal 1972 – C 1980 – C++ 1995 – Java 1995 – PHP 1995 – JavaScript 2009 – Go 上面的语言大家知道哪些呢？\nPython的发明者 Guido van Rossum 是荷兰人，所以著名的Python之禅中有一句：\nAlthough that way may not be obvious at first unless you're Dutch.\nPython之禅的作者 Tim Peters 解释说这里的荷兰人指的是吹捧 Python 的作者 Guido van Rossum 的彩虹屁：等同于“你个荷兰佬他娘的还真是个天才”。\n今天有两个小知识，\nPython和其他语言的发明年份。 Python的作者是个荷兰人。 你记住了吗？\n完毕（曾老师地铁上手打 [微笑] ）\nPython 之禅（2021-01-01） 各位Python同学新年好啊！2021年的第一天的 Python 小知识来啦。\n昨天的小知识里面提到Python之禅，感觉很高深的样子。今天我们继续聊聊这个禅。\n你在Python环境中输入\n1import this 就能得到Python之禅。\nThe Zen of Python\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren't special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is better than never.\nAlthough never is often better than right now.\nIf the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea -- let's do more of those!\nPython之禅就是上面这段文本，它描述了Python的设计思想，和写 Python 代码的时候秉承的理念。我在 知乎 上找到了一篇较好的译文。\n优美 \u0026gt; 丑陋\n明确 \u0026gt; 隐晦 （1）\n简单 \u0026gt; 复杂\n复杂 \u0026gt; 繁复 （2）\n扁平 \u0026gt; 嵌套\n稀疏 \u0026gt; 拥挤（3）\n可读性很重要（4）\n固然代码的实用性比洁癖更重要，\n所谓的“特例”也往往没有特殊到必须违背上述规则的程度\n除非必要，否则不要无故忽视异常（5）\n如果遇到模棱两可的逻辑，请不要自作聪明地瞎猜。\n应该提供一种，且最好只提供一种，一目了然的途径\n当然这是没法一蹴而就的，除非你是荷兰人（6）\n固然，立刻着手 好过 永远不做。\n然而，永远不做 也好过 闷头蛮干\n倘若你的实现很难解释，它一定不是个好主意\n倘若你的实现一目了然，它可能是个好主意（7）\n命名空间大法好，同志们要多多搞！\n考据：\n该引入的包显式地一条条罗列出来，不要合并；不要用星号；不要在方法里藏意想不到的的副作用，等等等等。还一个例子，有一种著名的软件设计原则 Convention over Configuration（约定优于配置）如果做得不谨慎，比如你约定的规则并不是真的业界惯例，就会违背这条。 StackOverflow上针对这句话的提问: 必要的复杂总是难免的，繁复啰嗦的代码却是不可接受的。你可以做很多事，很复杂的事，但是不能啰嗦，更不能难以理解。复杂不是罪，但是代码需要更有逻辑、更有机的组织。简而言之，Simple \u0026gt; Complex \u0026gt; Complicated \u0026gt; Chaotic。（另外，以上内容仅限Python语境，不同语境下对Complex和Complicated的定义可能会有所不同） 有人喜欢写很长的one-liner 比如：lambda L: [] if L==[] else qsort([x for x in L[1:] if x\u0026lt; L[0]]) + L[0:1] + qsort([x for x in L[1:] if x\u0026gt;=L[0]]) # 一行流快速排序 这样固然可以炫技，但是也很难懂啊。让其他人读不懂的代码不是优雅的代码 写这篇文章的动机之一就是看到有人把 Readability counts 翻译成可读性计数 实操中很多人不注意 catch 完就 log 一下就不管了，很快啊，这样好么？这样不好。软件界一般都讲 Let it fail，学名为 Fail-fast 法则。简而言之就是整个项目周期中越早暴露的问题，其修复成本越低。等到你的项目上线了结果出来各种诡异的bug你会毫无头绪，结果只能去翻长长的日志。所以我劝各位，不要再犯这样的聪明，小聪明。 本文作者 Tim Peters 解释说这里的荷兰人指的是 Python 的作者 Guido van Rossum 吹捧 gvanrossum 的彩虹屁：等同于“你个荷兰佬他娘的还真是个天才” 贯穿整个 PEP 20 的核心就是一句话“你的代码是给别人读的！”。从这个角度而言，难以理解、难以维护的代码，即便是“高性能”，也肯定不是好代码；但是反过来，一目了然的逻辑也不代表就一定是好代码。编程可太难了 今天的Python小知识就到这里啦，新年新气象，2021加油，你能掌握Python！\n以上翻译来自于： https://zhuanlan.zhihu.com/p/40950546 PEP 20： https://www.python.org/dev/peps/pep-0020/ Python发明历史（2021-01-02） 各位Python同学上午好啊！经过昨天的修整，熬夜跨年的疲惫一扫而空！\n昨天曾老师二倍速拉完B站 《2020最美的夜》，三倍速拉完得到《时间的朋友》，我感觉有了新的目标。爷青回，从未来看现在，做太阳影响别人，做火炬指引方向。从Python开始，改变我们的 2021！\n今天的 Python 小知识，聊一下 Python 的发明历史吧。\n下面的内容来自于知乎专栏：https://zhuanlan.zhihu.com/p/67469889\n1982年，Guido从阿姆斯特丹大学获得了数学和计算机硕士学位。然而，尽管他算得上是一位数学家（学术），但他更加享受计算机带来的乐趣（工程）。用他的话说，尽管拥有数学和计算机双料资质，他总趋向于做计算机相关的工作，并热衷于做任何和编程相关的活儿。\nGuido接触并使用过诸如Pascal、C、Fortran等语言。这些语言的基本设计原则是让机器能更快运行。在80年代，虽然IBM和苹果已经掀起了个人电脑浪潮，但这些个人电脑的配置很低。\n所有的编译器的核心是做优化，以便让程序能够运行。这种编程方式让Guido感到苦恼。Guido希望有一种语言，这种语言既能够像C语言那样，能够全面调用计算机的功能接口，又可以像shell那样，可以轻松的编程。\n于是Guido在1989年，那个无聊的圣诞节假期，开始编写Python语言的编译器。他希望这个新的叫做Python的语言，能符合他的理想：创造一种C和shell之间，功能全面，易学易用，可拓展的语言。\n1991年，第一个Python编译器诞生。它是用C语言实现的，并能够调用C语言的库文件。从一出生，Python已经具有了 ：类，函数，异常处理，包含表和词典在内的核心数据类型，以及模块为基础的拓展系统。\n最初的Python完全由Guido本人开发。但随后Python得到Guido同事的欢迎。他们迅速的反馈使用意见，并参与到了Python的改进。\n后来，Python拓展到研究所之外。Python将许多机器层面上的细节隐藏，交给编译器处理，并凸显出逻辑层面的编程思考。Python程序员可以花更多的时间用于思考程序的逻辑，而不是具体的实现细节。这一特征吸引了广大的程序员，Python开始流行。\n1991年，Linus在comp.os.minix新闻组上发布了Linux内核源代码，吸引大批hacker的加入。Linux和GNU相互合作，最终构成了一个充满活力的开源平台。 硬件性能不是瓶颈，Python又容易使用，所以许多人开始转向Python。\nPython发展至今，无论是从入门级选手（爬虫、前端、后端、自动化运维）到专业级数据挖掘、科学计算、图像处理、人工智能，Python 都可以胜任。\n曾老师评论：\nPython 的流行，离不开Guido的热情和计算机世界发展的「大势」。上面的故事可以看出， Guide 首先是个「数学和计算机双料硕士」，其次他喜欢「工程」，这两个条件对于 Python 的诞生都「缺一不可」。\n1991 年之后，计算机硬件的逐渐普及（在中国，还要等到1995年之后，从 DOS 到 Win95 的转换，曾老师是在 1995 年第一次接触计算机的）让「更容易学习」的 Python 走上了历史舞台。\n1991年又正好发布了 Linux 内核源码，Python 在 Linux 上基本上处于「基础设施」的地位，许多 Linux 的管理工具都是使用 Python 开发的。\n到了 2018年，大数据、AI 的发展，更多非专业人员需要使用一门入门容易的编程语言，这 Python 的发展走上了快车道。继而出圈。\n可见，一个事物的发展，和(1)自身的特点，(2)持续的努力，(3)主流趋势密不可分，且缺一不可。对于我来来说，(3)不可掌控，(1)不可快速构建，能掌控的只有(2)：持续的努力。\n所以，2021 努力吧，少年！\nPython 安装包下载（2021-01-03） 各位Python同学上午好啊！今天开始我们来学点实际的 Python 技能吧。就从最难的「安装 Python 环境」讲起。\n为啥说这个「最难」呢？因为 90% 的编程语言自学者可能都是从这里放弃的。\n如果你看过 《21天精通 C++》或者《七天学会网站制作》这类书籍，你就可能知道，21 天你或许还没有搞清楚 C++编译器是什么，7 天你甚至都没有弄清楚 HTML 和 Dreamweaver 的关系。\n这并不是因为这些技能很难，而是因为他们很杂，需要有大量的「前置」经验。例如知道怎么阅读计算机给你展示的报错信息，知道如何正确使用搜索引擎来查找资料，知道在碰到模棱两可或者前后矛盾的信息时应该如何选择。\n当然，曾老师的课程中不会讲太多「前置」经验，因为能进入曾老师的 Python 课的同学都是「超出平均水平的」。从今天开始，我会讲讲如何从头配置本机 Python 开发环境。\n曾老师假设你有一台电脑（废话），安装了 Windows 10 操作系统。当然，如果你使用 macOS 的话可能会更简单一点。\n首先，访问 Python 的官方网站： https://www.python.org，目前最新的 Python 版本是 3.9.1 ，点击 「Downloads -\u0026gt; Windows」下载。\nPython.org 官方网站访问比较慢，也可能连接不上，我建议你通过「科学上网」来下载。\n你也可以直接访问 Python3.9.1 的下载地址：https://www.python.org/downloads/release/python-391/\n大部分使用 Windows 系统的同学选择 Windows installer (64-bit）下载即可。\nmacOS 的同学建议选择 macOS 64-bit intel installer。如果你的 Mac 使用最新的 苹果 M1 处理器，那么需要选择 macOS 64-bit universal2 installer 。\nPython 在线编辑器（2021-01-04） 各位Python同学，2021 第一天上班好啊！\n昨天我们讲了 Python 安装包的下载。至于安装过程我就不讲了，相信各位都有能力搞定。说实在的，如果你没有能力做完安装这步的话，我建议你也不要学 Python 了，至少这节课你是跟不下去了。\n今天聊一些 Python 的在线 IDE。使用在线 IDE，你可以不必安装本地环境，打开网页就能进行 Python 代码的编写和测试。\n什么是 IDE 集成开发环境（IDE，Integrated Development Environment ）是用于提供程序开发环境的应用程序，一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。\n本地搭建 IDE 会消耗一些时间，再加上在线教育的普及，很多网站提供了「在线 IDE」。经过昨天晚上的测试，我提供一些可以使用的在线 IDE 给大家，大家可以直接访问下面的网站进行 Python 代码测试。\nw3cschool 访问地址： https://www.w3cschool.cn/tryrun/runcode?lang=python3 Python 环境：python3.6 是否需要科学上网：否 repl.it 访问地址： https://repl.it/languages/python3?v2=1 Python 环境： python3.8 是否需要科学上网：可能 coding.net 访问地址： https://coding.net Python 环境： Python 3.6 是否需要科学上网： 否 特点：提供虚拟机环境，提供 VSCode 编程环境，但运行速度较慢 colab 访问地址： https://colab.research.google.com/ Python 环境： Python 3.9 是否需要科学上网： 是 特点：类 Jupyter 环境，无敌强大，可调用 GPU print 函数的使用（2021-01-05） 各位Python同学大家晚上好哇！今天的Python小知识又来啦。\n昨天我们聊一些 Python 的在线 IDE，不知道大家有没有尝试呢？从今天开始，我们讲的内容就基于大家已经搭建起了自己的本地 Python 环境这个前提啦。（请参考昨天和前天的 Python 小知识）\n什么？你没有本地 Python 环境？那就去装一个吧。我知道你能搞定的。\n再次推荐一下，在本地环境还没有搭建好的时候，你可以用下面这两个环境来做 Python 的测试：\n无需科学上网： https://www.w3cschool.cn/tryrun/runcode?lang=python3 必须科学上网： https://colab.research.google.com/ 今天讲讲 print 的使用。\n初学者一般会使用 print 函数来输出信息和调试程序。print 的语法是 print('Hello world')。\n被输出的信息包含在英文括号内，使用单引号或者双引号包裹即可。\n请切换到英文输入法！所有标点符号必须使用英文输入\n如果你在本机环境中，首先输入 python3 进入 python 交互环境，按下图展示输入代码，然后按下回车键即可输出信息。\n如果你使用在线编辑器（以w3cschool 为例），按下图操作输入代码，单击「运行代码」按钮即可查看输出。\n今天的内容就到这里啦！请注意，曾老师的 python 课第一课 PPT、视频已经全面上线。请扫描加入课程交流群。\n全文完 ","date":"2021-01-07","description":"曾老师的 Python 课，每日教学第一周，每周更新。","lastmod":"2021-01-07T06:13:07Z","slug":"python-course-of-tearcher-zeng-daily-1","tags":["pythoncourse","python"],"title":"Python日课-第1周","url":"https://blog.zengrong.net/post/python-course-of-tearcher-zeng-daily-1/"},{"categories":["tutorial"],"content":"因为 SAGI 读书会 和 SAGITEAM 同学的需要，我设计了一套 Python 课程，该课程与市面上所有课程都不同，有以下几个特点：\n不以 成为程序员 为目的； 学完之后 不会 立刻拿到 50 万年薪； 适合所有渴望锻炼编程思维的打工人； 强调自学能力，强调应用； 关注你实际能在工作中使用 Python 作为工具； 提供实际生活或者工作中的案例； 完全免费，学不会免费自学。 课程介绍 这套课程有两条教学线：\n现场教学，约两周一次，每次一小时，主要为实际案例的讲解。 微信群教学，每日一次，由易到难讲解 Python 知识点，Python 相关的小故事，业界八卦，IT 行业最新信息。 两条教学线的教学内容整理后会发布到群中，请扫描加入群索取。\n曾老师是谁 以下内容均含有吹牛成分，请谨慎对待。\n教美术的体育老师 一个沦为程序员的美术老师 十年一线教学经验 六年组织培训经验 八年创业经验 二十年程序员经验 SAGI GAMES 创始人 \u0026amp; CEO 出书的曾老师——出版物 以下出版物均为 2006 年前的古董书，不一一列举。\n主编：《Flash MX互动教学课件制作实例教程》（人民邮电出版社） 参编：《用多媒体学 SQL Server 2000》（北京中电电子出版社） 参编：湖北省中小学信息技术教材 《信息技术与网络（小学版）》《网络技术应用（初高中版）》（华中科技大学出版社） …… 讲课的曾老师——教学经验 全国教学艺术大赛讲课二等奖 新东方 IT 教育讲师、湖北总部教学主管 连续五年每年组织300人以上培训两次 写博客的曾老师——持续输出 一个写了十五年的博客： https://blog.zengrong.net 一个做了十八年的网站： https://zengrong.net 一个办了五年的公众号： 曾嵘胡扯的地方 敲代码的曾老师——开发经验 中老年野生程序员 精通用十八种语言写 Hello World 页游/手游/网站前后端/APP/服务器 SAGITEAM 的IT+运维 千万日活，分离架构，在线部署的小游戏服务器 MJP (write by python)维护者 课程目标 建立编程自信 培养编程思维 课程结束后拥有持续自学能力 日常工作中持续使用 Python 这是一套教你学 Python 的课，不是教你 Python 的课。\n授人以鱼不如授人以渔，自学能力是当今社会最重要的能力。\n曾老师的 Python 调查 为了摸清楚 SAGITEAM 这群人到底是不是真想学 Python，曾老师设计了一个调查，内容如下：\n可以看出，一部分是是做梦都想学，还有一部分人是不要钱就学。\n所以我觉得这个课要收费。当然，只对现场学习者收费。\n通过统计这个细节表，我发现 SAGITEAM 中报名的学生有这样几个问题：\n学习诉求不一 学习基础不同 曾老师秉承「有教无类，因材施教」的教学理念。但对于这样的学生群体，没有一套现有的课程能解决问题，所以只能这样做：\n从头设计一套市场上没有的课程。 课程设计 1. 研究线上 Python 课程 研究千X教育、财X Python、达X教育的 Python 课程。\n2. 研究儿童 Python 课程 教小白和教儿童有一定的共通之处。所以曾老师研究了 VipXXXX、极X晨星的编程课程。\n3.阅读创新 Python 书籍 《父与子的编程之旅》 豆瓣 8.5\n研究了上面的优秀课程之后，曾老师明白，在课程设计时必须遵循下面的限制：\n设计限制 简化操作，简化概念 确保所有的人都能轻松上手 不需要了解的概念一定不讲 给出线索、资源和工具，学生根据自我需求自主深挖 课程内容 图形图像处理 面向目标：全员 Sample：批量图像处理：尺寸、裁剪、风格化、滤镜 涉及技术：Wand(imagemagic)、Pillow 数据可视化 面向目标： 财务、行政、人事岗、策划岗、运营岗 Sample： 产品数据分析、基金投资分析 涉及技术：pyecharts、bokeh 爬虫 面向目标：策划岗、运营岗、玩票者 Sample：爬取豆瓣创建一个自己的读书数据库 涉及技术：Selenium、Scrapy 教学方式 SAGI 读书会现场教学 全程视频 B 站可回看 PPT 课后在群中发布 课后作业在线提交 授课群中每日更新 Python 小知识，回答 Python 疑问 毕业设计辅导 课程收费 现场付费，网课免费\n坚持到最后的可能不到 30%，现场听课人员低于 10 人不开课\n课后作业 欢迎进群索取课程 PPT、视频播放地址、学习 Python 小知识、与 Python 学习者交流。\n全文完 ","date":"2021-01-04","description":"曾老师的 Python 课第1课","lastmod":"2021-01-04T10:40:13Z","slug":"python-course-of-teacher-zeng-1","tags":["pythoncourse","python"],"title":"曾老师的Python课（第1课）","url":"https://blog.zengrong.net/post/python-course-of-teacher-zeng-1/"},{"categories":["technology"],"content":"因为在 SAGI 读书会 上给自己挖了个大坑，给公司同事设计了一套 Python 课程，所以要准备一下授课环境了。\n目前最好的授课环境当然是 Jupyter，下面讲讲它的配置。\n我一直是一个 选择综合症+洁癖患者，尽管我已经不断强迫自己降低要求了。这次的目标是这样的：\n最小化。 不影响当前的系统环境。 依赖 操作系统 macOS Big Sur 11.1 conda 4.9.2 （Miniconda） JupyterLab 3.0.0 JupyterLab 包含 Jupyter+notebook+iPython ，是 Jupyter 项目的下一代用户界面。界面上更友好。网上大量的中文资料都是介绍 Jupyter + notebook 的，比较老了，参考起来可能会有些差异，建议直接看原版文档：JupyterLab。\n为什么选择 conda 在 Jupyter 里面，你迟早是要跑 Matplotlib 的，为了避免不必要的麻烦，应该选择 conda 而非 pip 。\n我在使用 pip 安装 Jupyter 的时候，的确遇到了一些麻烦，主要表现为 matplotlib 安装成功但无法正确导入，以及 numpy 安装报错。报错信息大致为：\n1 raceback (most recent call last): 2 3 File \u0026#34;/Users/paul/python/test/t.py\u0026#34;, line 2, in \u0026lt;module\u0026gt; 4 import pandas as pd 5 File \u0026#34;/Users/paul/python/test/venv/lib/python3.9/site-packages/pandas/__init__.py\u0026#34;, line 11, in \u0026lt;module\u0026gt; 6 __import__(dependency) 7 File \u0026#34;/Users/paul/python/test/venv/lib/python3.9/site-packages/numpy/__init__.py\u0026#34;, line 286, in \u0026lt;module\u0026gt; 8 raise RuntimeError(msg) 9 RuntimeError: Polyfit sanity test emitted a warning, most likely due to using a buggy Accelerate backend. If you compiled yourself, see site.cfg.example for information. Otherwise report this to the vendor that provided NumPy. 10 RankWarning: Polyfit may be poorly conditioned 如果一定要在 pip 中解决，可以这么做：\n1$ pip cache remove numpy 2$ brew install openblas 3$ OPENBLAS=\u0026#34;$(brew --prefix openblas)\u0026#34; pip install numpy 所以，直接用 conda 比较省事，且不会影响本地原有的 python 环境。\n至于 conda/Anaconda/Miniconda 的区别和联系，看官方文档介绍：Conda Doc Home\n我做了一些简单的翻译：\nMiniconda: 最小化的 conda 安装器，仅仅包含 Python，conda 以及 conda 必须的核心最小包，例如 pip/zlib 等等。安装文件有数十 MB。 Anaconda: 大而全的 conda 安装器，包含几百个用于科学计算和分析的包，例如 SciPy, NumPy 等等。安装文件有数百 MB。 conda：上面两者的包管理器。 我个人建议不要用 brew 安装 conda，直接用 官网提供的方式 来安装会比较容易。\n当然，也可以去 清华大学的镜像站 下载，速度会比较快。\n禁止 conda 默认进入 base 环境 conda 安装成功后，每次启动命令行工具，都会自动进入 (base) 环境。这会对我造成困扰。因为我的本机还有其他 Python 虚拟环境存在，进入 conda 环境后会导致这些虚拟环境无效。\n有两个方法可以解决这个问题：\n默认进入 (base) 环境后，输入代码 conda deactivate 来退出 conda 环境。 设置 auto_activate_base 配置为 false，让 conda 不自动进入 (base) 环境： conda config --set auto_activate_base false 。 我当然会选择第二种。\n安装和使用 JupyterLab 安装 JupyterLab 安装完毕 Miniconda 之后，创建一个虚拟环境，使用 python3.9，安装 jupyterlab 库。\n1# 创建一个名称为 jupyter 的虚拟环境 2conda create -n jupyter python=3.9 3 4# 激活这个环境 5conda activate jupyter 6 7# 安装 jupyterlab 8conda install -c conda-forge jupyterlab 9 10# 启动 jupyterlab 11jupyter lab 解决 BUG 在 notebook 中使用 tab 做自动补全的时候，会报错如下：\n1 File \u0026#34;../venv/lib/python3.8/site-packages/IPython/core/completer.py\u0026#34;, line 2029, in _complete 2 completions = self._jedi_matches( 3 File \u0026#34;../venv/lib/python3.8/site-packages/IPython/core/completer.py\u0026#34;, line 1373, in _jedi_matches 4 interpreter = jedi.Interpreter( 5 File \u0026#34;../venv/lib/python3.8/site-packages/jedi/api/__init__.py\u0026#34;, line 725, in __init__ 6 super().__init__(code, environment=environment, 7TypeError: __init__() got an unexpected keyword argument \u0026#39;column\u0026#39; 这是因为 jedi 0.18.0 不支持 iPython 7.19，解决方案：将 jedi 0.18.0 降为 0.17.2。不能使用 conda 安装，因为 conda 中找不到 jedi 这个包。要进入 conda 环境使用 pip 安装。\n1# 确保自己在 conda 的 jupyter 环境中，用 pip 安装 1.17.2 版本的 jedi 2pip install jedi==0.17.2 设定启动目录 需要指定 jupyter 中的 notebook 保存的文件夹，否则 JupyterLab 会自动指定 / 为文件夹，这是很不安全的操作。\n1# 编辑文件 2vim ~/.jupyter/jupyter_lab_config.py 3 4# 加入下面的行 5c.ServerApp.root_dir = \u0026#39;/Users/zrong/study/jupyter/\u0026#39; 6 7# 重新启动 JupyterLab 8jupyter lab 换源 为了让下载和安装更快速，换用 清华大学提供的镜像源。编辑 ~/.condarc 文件，加入下面的内容：\n1channels: 2 - defaults 3show_channel_urls: true 4channel_alias: https://mirrors.tuna.tsinghua.edu.cn/anaconda 5default_channels: 6 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main 7 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free 8 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r 9 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro 10 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2 11custom_channels: 12 conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 13 msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 14 bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 15 menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 16 pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 17 simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud Have Fun 来玩点花样吧。先在 jupyter 环境中安装 matplotlib 和 numpy 这两个库：\n1conda install matplotlib numpy 然后在 notebook 中键入如下代码：\n1import matplotlib.pyplot as plt 2import numpy as np 3 4plt.plot(np.random.randn(50).cumsum()) 效果如下：\n参考 BUG: Python 3.9 Polyfit sanity test emitted a warning Last jedi release (0.18.0) is incompatible with ipython (7.19 and 7.18 tested); reason - column arg was deprecated, and now removed #12740 清华大学 Anaconda 镜像使用帮助 JupyterLab Doc Home 清华大学的镜像站 Miniconda 下载 安装 conda 全文完 ","date":"2020-12-29","description":"一个洁癖患者的 conda4.9.2 + JupyterLab 3.0.0 版本的安装和使用。","lastmod":"2020-12-29T14:47:43Z","slug":"conda-and-jupyter-lab","tags":["python","sagibookclub"],"title":"conda 和 JupyterLab 干净安装与最小使用","url":"https://blog.zengrong.net/post/conda-and-jupyter-lab/"},{"categories":["impressions"],"content":"读完迪士尼传奇 CEO 罗伯特·艾格的自传 《一生的旅程》 之后，挺受感动。读到佳作时候，书中的内容往往会给我带来极大的震撼与共鸣，会使我对整本书的价值和意义产生不真实的判断。因此我把这本书放了一个月之后重新拾起来翻了一遍，去掉思维泡沫，写一点感想。\n重读这本书的同时，我也在读小米官方授权的传记 《一往无前》。从写作手法上来看，两本传记还是有一定区别的。《一生的旅程》即使是翻译版，也显得更加弘大和流畅，《一往无前》中的人物塑造就刻意了许多。当然，两本书都很棒。\n艾格是一个领导者，从 ABC 一路走来，取得这样的成绩，与个人习惯息息相关，也离不开管理技巧、重要机会和关键人物的帮助。下面的文章会分开讲解这些特点，然后讲讲我在阅读时发现的一些写作方面的小细节，最后列出文中的金句。\n个人光环 艾格是个极其自律的人，虽然书中没有花很大的篇幅讲解，但从字里行间可以感觉到。他习惯清晨4:15早起（P30）和素食，能参加铁人三项（P160），长期健身。他重视家庭，在选择新工作的时候会把家庭放在第一位并考虑家庭成员的感受（P55）。他认为管理自己的时间和尊重别人的时间同样重要。这是作为一名管理者应该具备的最重要的特质。原书中有一段话非常适合描述这一点，这是在讲书中唯一一个作为反面人物的上司迈克尔·奥维茨的时候出现的（P107）：\n你必须做到专心致志；往往遇到不得不坚持开完的会议，虽然你很想中途离场；\n你要学会学习和吸收；你也要倾听别人的困难，并帮助他们寻找解决办法。\n这些都是一名杰出管理者的必备特质。\n艾格也是一个非常能抓住机会的人，他一生中最重要的机会，就是在迪士尼前 CEO 迈克尔被免除职务时，在所有人都不看好的前提下，抓住机会并且创造机会勇敢向前（P152）。\n我问他，大家打算怎么宣布寻找人选的消息。\n「就说我们打算从内部和外部寻找人选。」乔治说。\n「除了我之外，还有什么内部人选？」\n「没有了。」他说，「你是唯一一位。」\n「那你就必须这样写，」我说，「我是首席运营官，从今天开始，你们把迈克尔当成一只跛脚鸭来耍。这事我必须介入，也要行使更多的权利才行。」\n我觉得，那是至关重要的一刻。如果公司的其他人不相信我是一个严肃的候选人，我就不会拥有实质的权利，也就会跟迈克尔一样转眼沦为跛脚鸭。\n上面这样抓住机会的例子，书中还有很多，我再简单介绍几个：\n实例 艾格在 1979 年为 ABC 争取国际乒乓球锦标赛转播许可证的时候，飞到北京与朝鲜的代表商谈。在协议就要达成的节骨眼上，他收到美国国务院的电话，告知和朝鲜进行的所有交涉均为非法。其实当时国务院只是不允许付钱给朝鲜人购买许可或者签订协议，并非要阻止 ABC 进入朝鲜。但当时的国务院代表并没有讲明。朝鲜代表怒不可遏，但艾格想到了新的办法，绕过主办国，通过国际乒乓球联合会获取拍摄的许可。朝鲜政府虽然没有拿到 ABC 的经费，但还是同意让 ABC 入境。这让 ABC 的转播团队成为了几十年来第一支踏入朝鲜的美国媒体队伍。（P45）\n曾嵘的思考：在 SAGITEAM 创业的过程中，我们会经常碰到不可能完成的任务。创业的特点就是变不可能为可能。工作的重点是把事情做成，而不是找借口说这事儿做不成。对于创业者来说，需要始终保持 把事情做成，不要找借口 的状态。但对于团队来说，领导者必须在 要求团队做出成绩 和 避免给他们灌输对于失败的恐惧 之间找到平衡点。 讲到电视剧《双峰镇》的失败时，艾格批评制片人大卫「是一位杰出的电影人，却没有电视剧制片人的头脑」。他认为大卫缺乏对于电视剧的敏感。然而大卫却在公开场合把收视率下滑的责任归咎到艾格头上。（P76）这是从不同的角度对同一个作品进行的不同理解。创意不是科学，绝大多数创意人士在其概念或者具体执行遭到质疑时会表现出敏感。人们往往会把注意力放在细枝末节上，以此来掩盖他们对清晰连贯的大格局缺乏把握。如果着眼小处，你的格局就会显得狭隘。\n管理人员要对任何创意作品的效益负责，但也不要对创意过程造成任何相反的伤害。作为管理者，需要把握两者的平衡。合理地管理创意，同理心是前提条件，而尊敬心则不可或缺。（P78）\n曾嵘的思考：跨界就要跨出去，而不是留一半。上面的大卫就是这样。我在看许多程序员学习一门新的编程语言的时候，往往能从源码看出他最熟悉或者最擅长的编程语言是什么。如果这个程序员是从 JAVA 入门的，那么他写的 C++ 也会带着 JAVA 的味道。我在看阿里云 SDK 源码的时候，心中不知道有多少头 CNM 飞过。把 Python 写得和 JAVA 一样？CIAO。 在准备迪士尼 CEO 选举的时候，艾格急于展示自己有解决迪士尼所有问题和应对所有困难的策略。但最终他只列出了三个选项的内容，不再纠结于过去，完全放眼未来。这种策略清楚和重复地传达了他的优先事项，也将他完全和前任 CEO 迈克尔分开，让他从十年迈克尔副手的影子中走了出来。（P157）\n曾嵘的思考：在做出变革的时候，清晰的使命愿景远比复杂的手段更容易说服别人。 CEO 竞选的 15 次面谈中的最后一次，艾格终于发了火（P167）：\n一直撺掇我贬低迈克尔的董事会成员加里·威尔森又问了我一次：「告诉我，我们为什么影响相信你与他人不同？你觉得迈克尔做错了什么？你会拿出什么不同的措施？」\n这句话终于把我激怒了：「同样的问题，你已经问过我三次了，」我一边说一遍努力克制自己不要怒吼，「我觉得这是对我的侮辱，我是不会回答你的问题的。」\n曾嵘的思考：正确地表达愤怒，也是一种重要的沟通方式。\n管理技巧 艾格认为，作为一名领导者，需要管理好自己的抱负，也需要管理好你所管理的人员的抱负。这需要一个平衡点：抱负不能太过超前眼前的机遇。当人们对于某个遥远的目标过分执着的时候，对现在的环境就会越发厌倦。他们没有足够的注意力放在自己的实际责任上，抱负起到了反作用。对于被管理者，需要成为一个一旦机会出现就会让上司把机会交给你的人；对于管理者，要培养这种每天在证明自己不可取代的人。（P111）\n有的时候，需要把员工的疑惑拿来探讨，对其顾虑进行耐心回应，这有助于提高士气和获得最高的收效；有时候你要足够直接，直截了当让员工知道你才是老板，没有商量的余地，任务就需要按你的方式完成。使用何种方式，重点在于你觉得哪种方式更适用于当下。（P117）\n艾格之前的迪士尼 CEO 迈克尔是一位「令人窒息的完美主义者和微观管理者」，并这样解释自己的观点「人们低估了微观管理的价值。」很多事情都是靠细节定成败的，所谓「伟大」，往往就是由一些微小细节拼凑而成的。但凡事要有度。你不用让所有人都知道公司的台灯是 CEO 挑选的。（P133）\n曾嵘：何时进行完美主义和微观管理也需要管理者的智慧。在我的工作中，更多碰到的是具体执行者由于能力限制，考虑不到细节完美的地步，而且在拥有足够时间的前提下，无法把产品的质量再提高一个级别。在这种情况下，完美主义和微观管理是必要的。反之，在时间紧迫或者执行者有足够的质量控制的前提下，管理者需要进行「全局性折衷」并回到对大局的控制上来，将细节交给执行人。 对于一位领导者而言，乐观精神是必不可少的一项特质，在困难时期更是如此。悲观主义会导致妄想偏执，进而发展成为戒备多疑，再进一步则会导致对风险的全盘规避。乐观不意味着要向大家传递「船到桥头自然直」的盲目信息，你要相信自己和身边的人有能力朝着最好的结果前进，如果不是真的遇到重大的挫折，就不要传达满盘皆输的信息。作为领导，你所设下的基调对于身边的人而言有巨大的影响，没有人想要成为悲观者的信众。（P138）\n在处理战略规划部问题的时候，艾格写到：彼得及其团队都是善于分析且咄咄逼人的人，而他们这种招牌式的能力和态度是当时公司许多人都不具备的。尽管如此，把蔑视写在脸上 也是不对的。人们往往会因为恐惧感而变得屈从，要么因为挫败感而变得漠然。无论怎样，你都是在挫败他们带到工作中的自尊心。久而久之，所有人都会把自己的工作职责拱手让给战规部。（P188）后来艾格解散了战规部。\n电影是一个激动人心又让人抓狂的行业，其运作机制依赖的是空凭直觉所下的一个接一个的赌注。每一件事都是一次冒险。即使你觉得某个构想很棒而且团队也无可挑剔，事情也可能因为一系列列往往在你的掌控之外的原因而脱轨。影视公司希望在以创意制片人为首的影视高管身上 施加压力，期待用 更少的预算 做出 更好的影片，但也必须关注电影行业经济的现状：制片成本有时候会严重超支；合同谈判时偶尔要采取强硬态度；为防止电影亏本，需要处理没完没了的财务决定。商业团队有时会对创意团队提出太多要求，对于电影创作者所承担的压力却无动于衷。（P250）\n曾嵘：游戏又何尝不是如此呢？ 艾格说服 J.J 艾布拉姆斯执导第一部没有出自乔治·卢卡斯之手的《星球大战》电影时，努力向 JJ 表明他是这个项目中的合作伙伴，而不只是一个敦促他交出质量上乘、票房大卖的影片的 CEO。面对一个牵涉巨大利益的项目，往项目工作人员身上施加压力是收效甚微的。要表达领导者与团队分担压力，和大家共同奋战，需要团队拿出更好的结果来减轻领导的压力。（P277）\n2016年10月，迪士尼准备收购 Twitter。在董事会刚刚批准收购后，艾格给所有的董事会成员发了一封短信，停止了对 Twitter 的收购，并打电话给 Twitter CEO 解释了返回的缘由。「如果什么事情让你感觉不对，那么这件事情可能就不适合你。」（P286）\n曾嵘：这就是领导者的直觉。这种直觉是在多年的管理经验中建立起来的，往往非常准确。做出这种决策，需要敢于在最后关头放弃的勇气。 随着权利的积累，CEO 的自信很容易越界成为自大。CEO 或许会觉得没有什么提议是自己没有听过的，因此对他人的开发变得缺少耐心或者不屑一顾。这是 CEO 这个职位的必然产物。CEO 必须去刻意努力倾听，关注各种不同的意见。艾格对工作关系中最紧密的高管提出：「如果发现我表现出不屑或者不耐烦，那就一定要告诉我。」（P328）\n重要机会 有几个重要机会对艾格人生旅程的影响是决定性的。艾格从 ABC 到迪士尼，收购皮克斯，继而收购漫威和卢卡斯影业，最后从默多克手中买下福克斯，都和这些重要的机会息息相关。\n在 ABC 制作对《双峰镇》并铤而走险决定播出后，消息在好莱坞疯传，一夜之间，艾格就接到了史蒂文·斯皮尔伯格和乔治·卢卡斯的电话。艾格和他当时的老板汤姆·墨菲说：「我们的这次冒险，在创意圈迎来了盛赞。」的确， 创意是得到更好合作的敲门砖。 这段历史让艾格在卢卡斯心中留下记忆，也为后面迪士尼收购卢卡斯影业打下了基础。（P73）\n艾格经过六个月的挑选刚刚获得迪士尼 CEO 职位时，经历了一个相当大的挑战，这个挑战来自于迪士尼家族成员罗伊·迪士尼。艾格解决问题的方法就是「尊重的力量」和「直接对话」，他发现：「迪士尼里没有人对于罗伊抱有他自认为应得的尊重，他只是一个渴望被人尊重的人，对他的尊重触及了私人感情、自尊心和自我价值。」在这件事情上他也收获了对于人性最重要的理解：「不要让你的自尊心阻碍你做出最佳的选择。」这件事的完美处理，是艾格后面人生旅程的基础。（P181）收购福克斯的过程很有戏剧性，尤其是和默多克的谈判以及出价的拉锯战，值得去读一下原书。\n关键人物 本书中提到了众多人物，大多是正面形象的。在艾格的旅程中，篇幅较大的几个人是苹果公司创始人乔布斯、迪士尼前 CEO 迈克尔和在 ABC 时候的老板汤姆·墨菲和丹·伯克。\n书中对乔布斯的很多描写都改变了我对乔布斯的刻板印象。艾格在第一次打电话给乔布斯提出收购皮克斯邀约的时候描写到：\n我等着他挂掉电话或者放声大笑。他回应之前的安静，漫长得仿佛无穷无尽。\n没想到他回话说：「你知道吗，这不算是世界上最离谱的想法。」\n在与乔布斯讨论迪士尼收购皮克斯的优缺点两个小时之后，优点一栏仍显稀薄，缺点一栏中则浩浩荡荡。但乔布斯说：「几个实打实的优点要比一堆缺点更有力。所以说，我们接下来怎么办？」这是乔布斯的突出特质，不允许优点被缺点掩盖住。\n在皮克斯的收购达成，准备举行媒体发布会前的 30 分钟时间里，乔布斯找到艾格，告知他的癌症复发：「我就要成为你们最大的股东和董事会的一员了。现在你已经知道了我的情况，你可以选择退出，我觉得我欠你这个权利。」还说艾格可以把责任推到他身上。\n短短几句话，竟让我听出苦涩之意，让我（曾嵘）想起另一个关于乔布斯的小故事。\n乔布斯曾经和《成为乔布斯》的作者布伦特·施兰德一起组织了一次个人电脑行业最重要的四人采访，同时采访史蒂夫·乔布斯、比尔·盖茨、迈克尔·戴尔、安迪·格鲁夫，采访由乔布斯牵头。但2008年12月采访前一周，乔布斯电话施兰德说无法参加采访：「我现在瘦的不行，狂吃东西但体重仍然一直下降。没人想看到我现在的样子，我连下个月的苹果大会都无法参加……」施兰德问：「那我应该怎么和盖茨、戴尔以及格鲁夫解释呢？」\n乔布斯在电话那头停顿了几秒钟，然后说：「你就告诉他们我是个混蛋吧。可能他们心里就是这么想的，你就帮他们说出来吧。」\n从传记中可以看出，乔布斯和艾格之后成了好友，即使乔布斯已经离世，艾格也一直在他之后的成功时刻提起乔布斯对他的帮助。无论是收购漫威之后《黑豹》的成功（P262），还是考虑新的收购对象时的「精神探讨」（P285），都能显示出乔布斯在艾格一生旅程中的重要性。乔布斯不仅是迪士尼最大的股东，也是对艾格和迪士尼影响最大的人。没有他的背书，对漫威和卢卡斯影响的收购都不会那么容易成功。\n而传记中另一个关键人物迈克尔就没有得到这样高的评价了。我不能说整本专辑中迈克尔的形象是负面的，但至少不是积极的。无论是迈克尔与罗伊·迪士尼的矛盾，迈克尔在任期间与皮克斯的矛盾，还是迈克尔在用人方面的失误，以及迈克尔处理与艾格关系时的若即若离，还有迈克尔离开公司之后还在反对迪士尼收购皮克斯的行为让艾格感到的愤怒，都表现出了艾格对迈克尔的复杂性格的不满。\n至于 ABC 时的老板汤姆和丹，艾格一直充满感激。\n写作手法 这本传记涉及到众多的人物，但人物并没有脸谱化，即使是匆匆出场的人物也有其个性。书中并不会对人进行过多的批评，更多的时候是描述事实，艾格加上一两句中肯的评价。\n例如，在谈到皮克斯员工对约翰·拉赛特的不当身体接触控诉时描述：「人人都知道约翰是个爱拥抱的人，虽然许多人将此看做无伤大雅之举。」（P304）\n再例如，即使是《双峰镇》的制片人大卫已经在公开场合把责任归咎到艾格头上，他还是在传记中描述：「我不确定当时是否做出了正确判断。我运用的是较为传统的电视剧制作方式，而大卫或许已经超越了时代。从内心来说，我认为是大卫挫伤了观众的积极性，但也很有可能，是我的要求把剧集抛进了混沌中。或许，大卫从一开始就是对的。」\n这种写作方式，表明了艾格是一个谨慎和富有同理心的人，树立了艾格的形象。\n一些细节也反映的作者的缜密心思。\n例如，在大都会 1986 年 1 月并购 ABC 的时候，传记中提到了「我的级别不够，因此没有被邀请」（P50），在 1988 年艾格的导师鲁尼将体育部门管理权交出来时提到「我还甚至想过这个人也许是我」（P53），在迈克尔离职的时候主动要求机会：「那你就必须这样写，我是首席运营官。」（P152）这一系列递进式的表现，是艾格的主动精神和自信心越来越强的衬托。\n乔布斯的素材在本书中是最浓墨重彩的一笔。传记中铺陈了不少鲜为人知的事件，艾格在乔布斯葬礼上的发言，通过乔布斯遗孀之口说出的乔布斯对艾格的认可，以及乔布斯离世后，每到重大事件提到乔布斯可能会对这些事件的看法，都让艾格的形象显得更加高大。\n金句 最后，用一些文中的金句来结束这一篇已经很长的读书笔记吧。\n苏珊（艾格的前妻）：生活就是一场冒险。如果不选择冒险的那条路，那就没有真正地活过。 （P66）\n杰出的领导力的重点并不在于你的不可取代，而是要帮助他人做好有天继承你的职位的准备——也就是给予他人与你一起制定决策的机会；辨识出他们需要开发的能力并帮助他们实现进步；有的时候，如果对方还没有做好迎接下一步的准备，你也要开诚布公地指出来。（P336）（本文标题出于此句）\n竞技场上的人（P227），是西奥多·罗斯福（美国第二十六任总统）1910 年 4 月 23 日在巴黎索邦大学发表的演讲共和国公民（Citizenship In A Republic）的一部分。 这段文字能激发创业者，不理会那些毫无价值的攻击和批评，努力拼死创新。我（曾嵘）在互联网上找到了原文。\nIt is not the critic who counts; not the man who points out how the strong man stumbles, or where the doer of deeds could have done them better.\n荣誉不属于批评家，不是那个指出强者是如何跌倒的，或者是行事者可以在哪里做得更好的人。\nThe credit belongs to the man who is actually in the arena, whose face is marred by dust and sweat and blood; who strives valiantly; who errs, who comes short again and again, because there is no effort without error and shortcoming;\n荣誉属于真正站在竞技场上拼搏的人，属于脸庞沾满灰尘、汗水和鲜血的人，属于顽强奋斗的人，属于犯错，一次又一次失败的人，因为没有不伴随错误或缺陷的成就。\nbut who does actually strive to do the deeds; who knows great enthusiasms, the great devotions; who spends himself in a worthy cause; who at the best knows in the end the triumph of high achievement, and who at the worst, if he fails, at least fails while daring greatly, so that his place shall never be with those cold and timid souls who neither know victory nor defeat.\n然而实际拼搏的人，懂得极大热情和投入的人，投身于有价值的事业的人，最终或如愿取得伟大成就，即使遭遇失败也不乏胆量，因此其所处将与冷漠且胆小、未经胜败洗礼者永不相同。\n全文完 ","date":"2020-11-29","description":"","lastmod":"2020-11-29T05:54:32Z","slug":"the-ride-of-a-lifetime","tags":["management","reading","readingnote"],"title":"杰出并非不可取代","url":"https://blog.zengrong.net/post/the-ride-of-a-lifetime/"},{"categories":["impressions"],"content":"最近在读管理学大师们的书籍，综合 SAGITEAM 的情况，得到一点点启示，本文做一个粗浅的记录。\n也许我读完上面的书目之后，会有更多或颠覆性的想法。那么这篇就算一个不完美的开头了。\n找到适合创业团队的人 在 SAGITEAM 创业初期，我常常做这样的思考：我们需要什么样的人？ 这个问题很容易回答，有这样几个方向：\n和团队创始人类似的人。在创业期，创始人就代表了整个团队。和创始人类似的人，更容易理解创始人的思维方式，更能高效执行创业团队的意图。 有明确的目标，有踏实愿景或疯狂愿望的人。有明确的目标意味着缜密的思辨力，有踏实的愿景意味着准确的自我判断力，有疯狂的愿望意味着无限的精力。 有创业经历，且经历过失败的人。经历过创业的人更容易开始另一段创业，大多数失败的创业在于坑太多，踩着踩着就掉进去了。经历过创业失败的人更容易脚踏实地避开大坑，眼光也更长远， 有复杂经验，和丰富兴趣的人。和经验单一的人相比，这样的人更容易看出项目中的问题，也更容易迸发出奇思妙想。 创业，就是小错不断，大错不犯。我一直很庆幸的是，SAGITEAM 在早期就能碰到几位符合上面条件的同事。\n回忆起来，我已经面试过 500 多人了，和他们的谈话过程中，我也学到了大量的知识、经验和行业趋势。创始人在「找人」这件事上一定不能犯懒，团队初期的人和重要岗位上的人必须亲力亲为，这是团队发展的基石。\n替换 谈替换是个伤感的事情。但不替换对团队来说就是生死选择了。\n创业团队不允许不合适的人存在太久。放一个不合适的人在重要岗位上，不如找一个勇敢的新人冲上去弄懂它。绝大部分事务性的工作，没有那么难，对经验的要求也远比你想象中要少。\n在替换之前，要想明白的两点：\n员工是否在正确的位置上？ 管理者是否已经做了所有的必须的事？ 彼得德鲁克在《卓有成就的管理者》一书中谈到：\n年轻的知识工作者的职位涵盖范围太窄，不足以向他的能力挑战，其结果不是他自请离职，便是很快地变成了“老油条”。我们常听到许多主管感慨地说：想不到满怀壮志的年轻人，会一个接一个消沉下去。其实这不怪别人，只能怪这些主管，是他们自己冻结了年轻人的热情，他们将职位设计得涵盖范围太窄了。\n日本有一种“终身雇用”的制度。一个人进了一家公司，他就会逐年升迁，平均每15年薪水增加一倍。他不会随便辞职，公司也不能把他开除。除非年龄到了45岁，或者位置升到了顶点，才显出事业生涯的分歧：其中少数能力特强的人，可以继续升到高级主管的位置。日本这套制度，与日本今天取得的巨大发展有什么关联呢？答案很简单：由于日本有这套制度，所以他们可以闭上眼睛，不看人的缺点。尤其是因为日本的管理者不能开除人，所以他们就只有从下属中去发掘能做事的人了。他们看人，只看人之所长。\n我们做不到「终身雇佣」，但我们能做到「看人所长」。对于管理者来说，员工正在经历的那些磕磕绊绊都是自己经历过的，管理者很容易忽略自己在经历这些磕绊时的心理活动，而把快速解决这些问题作为理所当然的要求。这是不公正的。员工需要犯错的空间，优秀管理者要做的，就是根据每位员工的特点，动态调整这个空间的大小。\n如果一个员工犯了过多的错且没有得到预期的成长，管理者就要思考，招聘出了什么问题？\n如果没有替换人选该怎么办？创始人顶上。 如果创始人不会，那就必须要学会，而且要学得足够快，做得比市场上同岗位的普通人员更好。作为创始人，这是必须做到的一点，而且并不难做到。\n对于游戏创业团队来说，创始人必须亲自抓的事情包括：\n人事。人才的重要性，团队的稳定性，团队士气的推动，团队文化的塑造，团队风格的一致性。 产品。选型、立项、产品细节、产品优化。没有人比创始人更在意了。一个选型错误就可能消耗浪费大量的时间和金钱。 运营。这里指的是广义的运营。所有与用户获取、用户研究与产品动态优化相关的内容，都是运营。包括买量、获客、推广、数据分析、用户调研、搜索引擎优化、应用市场优化，都应该是运营的范畴。 上面就是最重要的了。美术和开发当然重要，但不好意思，这类技术岗位并不是能很快学会而且能达到普通水平的。创始人要找到靠谱的合伙人再开始创业历程，或者你自己就是牛逼的技术人员。创始人也应具备一定的技术知识，以便在全面思考时不至于受到限制。\n《硅谷钢铁侠》中说到过一个例子，埃隆马斯克常常会直接当面开除人且立即生效（懂王附体啊^_^），在新人没有到岗的时候，他就自己扑上去干，而且干得更快更好更出活儿。我不能判断这个例子的真伪（毕竟传记大多美化，且有光环效应的存在），但这种精神是创始人必须具备的。\n推进员工自主思考 优秀的管理者并不愿意把所有事情抓着不放。对于脑力劳动者，放权是让团队更有效运作的好方法。但若你真的尝试过「放权」，你一定会像我一样，碰到「一管就死，一放就乱」的情况。造成这个情况的原因如要有这样几种：\n1. 授权走样 授权走样有多种表现形式。\n一种是「甩手掌柜」式的授权。「所有事的决策都交给你来决定，没有大事儿别找我」。员工接到这种授权的时候心里是犯嘀咕的：「你啥都让我决定，是为了让我背锅么？」\n一种是「用力过猛」式的授权。口头上说都让你做主，实际上管得很细，天天盯着具体细节。\n授权行为，并不会直接导致好的绩效结果。行为和结果之间还隔着一层被授权者的心理体验，这就是「心理授权」，也就是个体感受到的被赋予权力的多少。\n以一己之力搞垮了拥有两百多年历史的巴林银行（Barings Bank）的 Nick Leeson 对讲述「心理授权」很有帮助，具体事件请自行搜索，这里借用他来讲讲心理授权的三个维度：\n目标的内化。被授权者应该能感到清晰的边界。Leeson 一直没有搞明白自己的责任到底是清算还是交易，他对目标感知是混乱的。 控制感。Lesson 心理上有极大的恐惧，搞不清楚失败了之后应该由谁来承担责任。在「怎么干我不管，但一定要干成」的压力下，他丢失了对自我的掌控感。 效能感。Lesson 没有经过任何的培训，因为对自己通过正常手段弥补亏损能力的不自信，导致了他的隐瞒行为，最终在 1995 年搞垮了 1763 年成立的巴林银行。 员工在「被授权」时最容易表现出来下面两种情形：\n热情高涨 -\u0026gt; 遭遇挫折 -\u0026gt; 自我怀疑 -\u0026gt; 退缩、放手\n将信将疑 -\u0026gt; 小幅尝试或保持现状 -\u0026gt; 管理介入，要求绩效 -\u0026gt; 恍然大悟（原来你们在找背锅侠） -\u0026gt; 退缩、放手\n而 Leeson 是第三种情况：\n热情高涨 -\u0026gt; 遭遇成功 -\u0026gt; 将信将疑 -\u0026gt; 极度恐惧 -\u0026gt; 放手一搏 -\u0026gt; 大溃败\nLeeson 在《我如何弄垮巴林银行》一书中写到：\n我为变成一个骗子感到羞愧，但我不知道他们的疏忽与我的犯罪之间，界限何在。我不清楚他们是否对我负有责任。他们的做法不可思议，而我则惶恐至极。\n无论是「甩手掌柜」式的授权，还是「用力过猛」型的授权，都缺失了对上面三个维度的思考。\n「授权」这个名词，通常都被人误解了，甚至是被人曲解了。这个名词的意义，是 把可由别人做的事情交付给别人， 这样才能做真正应由自己做的事。\n2. 打工人心态 这几天「打工人」的梗火遍网络，创业团队需要「打工人心态」的员工么？\n我的答案是需要。这涉及到两个问题：\n打工人是不是真的在打工？ 打工人心态是不是由于管理不到位导致的？ 彼得德鲁克在《卓有成效的管理者》一书中说：\n有效的管理者从来不问：「他能跟我合得来吗？」他们问的是：「他贡献了什么？」\n他们从来不问：「他不能做什么？」他们问的是：「他能做些什么？」\n所以在用人时，他们用的都是在某一方面有所长的人，而不是在各方面都过得去的人。\n管理者最喜欢的当然是「什么都能干」但只拿一份工资的人，而员工最怕的就是「我因为能干所以要干更多的活儿」。在社会中，「越能干，越多干」成为一个普遍现象。这样的现象也会促进「打工人心态」的产生。社会认知既如此，那么久而久之，无论员工是否真的符合「能干」的定义，TA 都会认为自己「能干」且应该「只为薪水干活」。这就是「打工人心态」。\n管理者需要知道的是，每个人都有创新的动力。人的内心不满现状，希望改变，只是缺乏自信，缺乏方法，缺乏指引，缺乏助力。于是，这些内心中的火苗泯灭了，大家会继续走在老路上，成为「打工人」。\n解决「打工人心态」，激发员工的创新动力，不是员工的责任。这是管理者的责任。\n3. 凝聚力和战斗力 解决「打工人心态」，要依靠「团队」的力量，要把原子化的个人，捏合成一个高度互信的超级小团队，让员工既能充分发挥各自的主观能动性，又能作为一个整体来协同作战。\n《赋能》一书中提到了海豹突击队的魔鬼式训练，例如长达 6 个月的超高强度体能训练，各种各样的体罚措施，5 天加起来只能睡 4 小时的「地狱周」等等，目标都是打造一个超级团队。训练中精心设计的环节，用来加强队员之间的互信与默契。比如，潜水训练要求两个人合用一根氧气管去完成任务，「冲浪受虐」的时候，队员们在大浪中必须手臂挽在一起，互相获取体温才能坚持更久；甚至在训练之外，队员们也不允许单独行动，连去餐厅都必须和同伴一道，否则就要受罚。\n通过这样的训练，坚持下来的队员彼此之间非常熟悉，建立起了高度的相互认同的相互信任，愿意用自己的生命托付给队友。在执行任务时，所有成员像一个有机整体一样去思考、去行动，就是达到了一种 「集体心流」 的状态，所有人都能快速理解彼此的行动意图，自动分工，自动补位，随时切换指挥官，谁觉得那一刻情况下他对局势最有把握，他就自动成为临时指挥，所有人都听他的。\n要实现这样的战斗力，还要剔除掉那些以个人为中心，没有集体荣誉感的人。一位资深教官说，有 50% 的训练者无法坚持到最后。在这些人中只有 10% 是因为体能够不上，其他 90% 的人就是因为吃不了苦。学员在面试的时候说过原因是因为「挑战自我」或者「让自己变得更强大」，他就很难坚持下来。而那些因为「我想加入海豹突击队」、「我要去海外作战」原因的人会坚持到底。\n一个群体共同智力的强弱，很大程度上取决于成员之间互动的强度。那些内部交互和外部交互水平都更高的团队，能够产生出更多的创意。\n形成群体智慧需要 凝聚力， 凝聚力来自于成员之间的绝对信任和全面沟通。形成了群体智慧的团队有超强的 战斗力， 战斗力来源于团队成员的自主推动。\n凝聚力和战斗力都不是天生的，需要管理的能力让它们在团队中爆发出来。\n4. 团队规模和团队文化 亚马逊CEO贝索斯提出过一个 Two-Pizza teams 原则，他认为如果两个披萨不足以喂饱一个项目团队，那么这个团队可能就显得太大了。因为小团队的每个成员都能做出显著的贡献（并被他人看到），相互依赖，有共同目标和统一的成功标准。\n这里说的小团队，指 3-15 人。这也是 SAGITEAM 从 2017 年到 2019 年的人员数量。团队不能扩大吗？当然可以，事情多了需要人手。但问题是，扩大了之后怎么办？\nSAGITEAM 扩大成为 SAGI GAMES 之后，很多老同事说，团队之间没有那么有趣了，找不到之前的那个调调了。\n我认为这很正常。\n团队的秩序、文化、风格，在从小到大的过程中，需要一个「重建」的过程。这个重建不是要打破或者放弃原来的风格，而是在团队成员的感受上，展现团队风格和文化的方式发生了变化。\n团队用两个 Pizza 就能喂饱的时候，出了问题喊一嗓子，整个团队都知道了，了解信息的人会迅速反馈形成合力。在几十人大团队里你喊一嗓子有人答应你吗？\n十几人的团队出门团建，大家都互相认识，知道各自的喜好，各种插科打诨好不快活，吃饭两张桌子就能坐满。几十人大团队里名字都认不全，讲个段子有人接吗？\nSAGITEAM 从创建开始就有「吃鸡文化」。\n这个文化是从一次「上线事故」开始的，「事故责任人」自掏腰包请大家吃炸鸡。从那以后，新品上线请吃鸡、有问题赔罪请吃鸡、立项确定请吃鸡、项目失败请吃鸡…… 这一系列文化活动（几百只/杯烤鸡奶茶）把团队风格深深烙印在大家的心里。但是当吃一次鸡的费用超过 500 块时，我发现这个文化要改变了。\n改变的方式就是类似于 Supercell 的 「Cell」。让每个项目组都成为自洽的「超级项目组」，让每个项目组都能复制「SAGITEAM」的文化。\n要这样做不容易，这需要更强的管理能力。\n高阶理论和四阶理论 一个企业的变化和转型能否成功，最重要的因素不是员工被激发，也不是逼着大家拼命，而是整个组织是否坚决执行了 CEO 的个人想法。\n这个发现被命名为「高阶理论」。其中的「阶」有两层含义：\n组织处理重大决策时，高阶层的人影响更大。 成功的变革大概率是自上而下。CEO 要影响高管团队，高管影响中层，中层影响普通员工，普通员工收到后才会坚决执行。 我认为，无论是扁平化管理的互联网企业，还是层级化管理的传统企业，都能从高阶理论中受益。无论管理方式为何，「执行不走样」都是最基本和最核心的要求。\n企业管理四阶理论将国家，行业，历史变量移除，将管理状态水平分为四个层级：\n随意，感觉管理型：基本规章制度，管理思想和个体行为，企业工作程序都呈现极大的随意性。个体文化水平偏低，工作效率低下，错误频繁，企业核心竞争力极弱。 一般制度管理型：大部分规章制度已建立，但缺乏科学性系统。缺乏具体职责和程序类制度，规章制度大多为纪律性要求，个体的制度执行自觉性不足，管理的制度贯彻监控不足。 规范化、信息化管理型：各领域都已建立系统化、规范化现代企业制度，战略目标透彻，组织结构合理，流程清晰，职责明确。且对现代人力资源管理体系，现代生产管理技术，IT技术有足够应用。 优秀企业文化导向型：具备规范化、信息化管理型企业的全部优点，且在此基础上建立了优秀，独特的企业文化。由于企业文化的不可复制性，优秀的企业文化是变化莫测环境中的最大竞争力。 SAGITEAM 走在第 2 层向第 3 层迈进的阶梯上。但值得庆幸的是，我们已经提前构建了第 4 层的文化。\nSAGITEAM 的使命： 用游戏改变生活。 SAGITEAM 的愿景： 创造长期伟大的游戏，创造最佳的环境让团队成功。 SAGITEAM 的价值观： 热爱游戏事业，旺盛的好奇心，终生学习，团队合作，勇于承担，做内心快乐的自己，让努力的人有尊严。 更大的视角 最后，用《蓝图》中的一段话来结束吧（有改动）：\n试着想象一下站在高达 3000 米的山峰上观察两座小山时的情形吧。一个高一点，一个矮一点，你会关注山之间的植被不同和雨水侵蚀的细节情况。你能观察到的东西取决于你所站的位置。在人类社会中，许多人就是站在这种海拔 3000 米的高低上，观察到不同社会之间的差异，却掩盖了更为巨大的相似性。如果你在太空俯视，你会发现山与山之间的区别变成了阴影的深浅而已。你会被深深震撼：「我们所有人都是地球的孩子。」\n我们的思考格局，取决于我们发问的时间尺度。\n有些问题每天都需要回答：今天中午吃什么？天气怎么样？ 这叫信息。\n有些问题每隔几年就要回答：苹果的股票怎么还在涨？现在学计算机专业还那么赚钱么？ 这叫趋势。\n最大的问题需要回顾几千年的历史才能做出回答：这个世界会变好吗？人类需要什么样的游戏？ 这是未来。\n相关书籍 《卓有成效的管理者》 《赋能：打造应对不确定性的敏捷团队》 《我是如何弄垮巴林银行的》 《蓝图》 《硅谷钢铁侠》 全文完 ","date":"2020-11-12","description":"","lastmod":"2020-11-12T02:28:52Z","slug":"independent-thinking","tags":["management","reading","sagibookclub"],"title":"自主思考是对员工的过分要求","url":"https://blog.zengrong.net/post/independent-thinking/"},{"categories":["impressions"],"content":"昨天，Ohayoo 的第一届开发者大会在上海召开。作为热情的云参会成员，我梳理了一点内容。\n信息获取（内容来源于会议实录） 去年 Ohayoo 一共发行了 60 款游戏，今年发行了接近 100 款。今年发行的游戏下载量超过5亿。其中最高单款游戏流水超过6亿，流水过亿的游戏有9款，流水千万的有39款。\n从数据来看，过往月流水千万的项目中，63%的团队不超过10人，86%的项目的研发成本低于100W。\nOhayoo 与大厂体量合作的有5-7家了，目前的合作进展相对缓慢。主要是想解决思路上的不一致，就只能求同存异互相理解，或者找到一些新的方法去尝试。我们还是希望他们进来的，所以说只能在我们帮他们赚到钱之后，我们才能慢慢磨合出更好的产品，这是一个比较慢的过程。\nOhayoo 下半年的游戏储备有一、二十款。而且我们对挂品牌的要求比较高一些，很多流水大几千万的我们也不一定会挂品牌，所以你们可能看不到，或者并不知道是 Ohayoo 发的。\nOhayoo 次留 50 七留 20 的筛选标准导致很多开发者的产品被直接淘汰，对于头部产品的要求则更高。这导致开发者难以入局，粗暴的筛选错失了一些底子很好的游戏，整体的产能也出现了不足。\n休闲游戏生态最终的买单方是用户，用户爱玩你游戏才行，所以如果只做一些偏短期的游戏，用户迟早会疲惫，那就需要有人做长线游戏。长线游戏就需要它的三十留，六十留，甚至九十留特别好。在 Ohayoo 看来，这才是头部开发者最终的存在价值。\nOhayoo 要覆盖最广大的用户群体。全球 80% 的游戏下载来自于休闲游戏品类。Ohayoo 从去年 7 月开始到现在海外发行的产品已经获得接近一亿的下载量。 休闲游戏对比19年下半年上涨将近50%，是手游整体大盘上涨速度的近两倍。\n对于 CP 来说，数据能力非常重要，因为数据是一切能力的基础。在这个基础之上。我们可能有了很多的工程能力，有了很多的版本管理能力，我们去年年底到今年，我们维持了周更新的速度。不单单是说我的研发速度更快，而是整个的数据获取、版本决策、版本质量、版本发布，这是一整套的研发、工程效率的提升。\n在《消灭病毒》之前，蓝飞总想着游戏怎么能赚钱，因为我们做了十年的广告变现，但是规模一直没有做大，消灭病毒的商业化成绩出来了以后，我们发现纯的广告变现，也是能支撑一个好产品的特别重要的一个方式。它给了我们信心，可以投入到AI游戏的里面去，而不是一边考虑，一边做付费，一边做广告。最后做了一个四不象，就是吃相也不怎么好看，又要保持傲娇，这个是不好的状态。\n所有人都说立项很难，立项对市场对于数据对于经验，都可以概括为你的创造力。但是以往的经验当中，很多同事会依赖于经验。只是拿数据做参考。现在我们团队内部要求的是说，我们要依赖于数据，而拿经验和喜好去做参考。\n如果有人想去立一个项，我通常都会问他两个问题。第一，你想立项的东西，你能在市场上找到跟它类似的，不同平台的，不管是主机还是Steam游戏，相像的游戏，能不能找到参考。第二，你的工程效率能有多快，如果你这个idea没有人做，你三天能做出来，能推到市场去测试也可以，所以我认为创造力加工程力一定要并重。\n信息解读 休闲游戏与短视频平台是天生融合的。 更多的「大厂」在进入休闲游戏的领域。 对于休闲游戏，立项是第一重要的事情。 大家都缺少好的产品。 数据能力和工程能力，是休闲游戏团队的基础能力。 先入局，再成长，再扩大。强势入场不一定会一炮而红。 Ohayoo 也在采用类似于 VOODOO 这样的方式，通过小的投入来拿到更多的创意产品，用 AppStore 下载量来计算分成。 Ohayoo 也无法平衡数据驱动与传统制作人之间的矛盾，只能尽量找一些年轻的有出海手游经验的制作人。 会议实录 Ohayoo、Kunpo、妙聚、Peakx、谷得，他们认为今天入局休闲游戏有何门槛？ 专访Ohayoo总经理徐培翔：经历半年阵痛期，我们想清楚休闲游戏生态要怎么做了 Ohayoo开发者大会（全程记录）：全面扶持休闲游戏开发者及产品出海 全文完 ","date":"2020-09-22","description":"","lastmod":"2020-09-22T02:40:57Z","slug":"challenges-for-casual-game","tags":["gamenote"],"title":"休闲游戏的机会","url":"https://blog.zengrong.net/post/challenges-for-casual-game/"},{"categories":["use"],"content":"诺兰又出手了。\n看过《星际穿越》和《盗梦空间》，怎么能不去看看《信条》呢？\n对于实拍狂魔和技术狂魔诺兰的电影来说，不应该随便找一块 2D 小屏幕来看。武汉那么多电影院，如何选择？\n先说结论 《信条》本身是采用 IMAX 摄影机拍摄的，当然应该首选 IMAX 影院。\n首选影院：\n武汉武商摩尔 IMAX 影城（IMAX激光）江汉区解放大道690号武汉国际广场C座7楼 次选影院：\n武汉万达影城汉街万达广场店（11号厅杜比影院） 武昌区楚河汉街1号万达广场5楼（地铁4号线楚河汉街站） 武汉金逸影城王家湾店（杜比影院厅）汉阳区龙阳大道特6号摩尔城C区5楼 看到这里就可以了，下面都是些废话。\n不过如果你不在武汉，或者你想知道选择的过程和逻辑，可以接着看下去。\nIMAX 和杜比 目前影院技术最好的两家就是 杜比 和 IMAX，其他的如 RealD/LUXE/4DX/ScreenX/中国巨幕 暂时还没办法超越这两家。所以我只在 IMAX 和杜比两家之间选择。\nIMAX 发展很早，在武汉目前（2020年9月）至少有 18 块屏幕（来自 IMAX 官网）。目前 IMAX 最新的放映系统是 IMAX 二代激光播放系统，武汉满足这一条件的只有 武汉武商摩尔 IMAX 影城（IMAX 激光） 一家。\n武汉武商摩尔国际电影城的 IMAX 二代激光播放系统在2019年12月22日进行的启幕仪式，屏幕尺寸 21.34x11.10 米，面积 236.874 平方米，有 294 个座位。\n杜比影院 由 杜比视界、杜比全景声以及一套标准的影院设计 构成。也就是说，杜比影院不能从现有的播放厅通过简单更改屏幕和音响设备改造而成，而是必须从影厅装修时就开始设计，对于座位个数和位置，整体识别设计，杜比影院都有统一要求。杜比影院的的亮度高于 IMAX 激光放映系统（参见下方亮度比较表）。\n根据 杜比官方公众号 提供的信息，在武汉仅有三家 杜比影院。分别为： 武汉万达影城江汉路悦荟广场店、武汉万达影城汉街万达广场店、武汉金逸影城王家湾店 。但由于我没有在万达影城江汉路悦荟广场店的播放单中找到 杜比影院 字样的放映厅，因此没有在上文中进行推荐。待我进行过实地考察后再来更新本文。\n需要注意的是，武汉有四十多个 【杜比全景声】放映厅，这些仅仅是采用了「杜比全景声」技术的普通放映厅，并非「杜比影院」！\n如果你是技术控，上面的介绍显然不能满足你，请继续看下面的数据分析。\n更进一步 IMAX 技术众多，技术演进的顺序如下：\nIMAX 70mm胶片 \u0026gt; IMAX 数字 \u0026gt; IMAX 激光一代 \u0026gt; IMAX 激光二代\nIMAX 70mm 胶片是宇宙无敌的电影放映系统，也是激光系统出来之前大屏幕的绝对统治者。其 70mm 胶片放映系统重达 2 吨，放映时需要独立的水冷系统。70mm 胶片格式包含四个投影机类型：GT（大剧院），GT 3D（双转子），SR（小转子）和 MPX（专为改装剧院）。当然了，MPX 的效果是四者中最差的。\n华中地区第一块 IMAX 屏幕（全国第六块），于 2007 年 2 月 1 日开幕的 武汉环艺新民众乐园影城（现已更名为武汉UA新民众乐园电影城），采用的就是 MPX 放映系统。该影城已经在 2010 年更换为 IMAX 数字放映系统。\n目前中国大陆已经没有使用 IMAX 胶片播放系统的商业影院，但科技馆、少年宫、博物馆等机构依然还保留有不少使用 GT/SR 播放系统的放映厅。\n目前在 IMAX 技术中占有统治地位的是 2008 年发布的 IMAX 数字，武汉的 IMAX 影院绝大多数采用这这种系统，只有 武商摩尔 IMAX 影城（IMAX 激光） 一家例外。现在 IMAX 胶片放映系统已经官宣停止支持了，但 IMAX 摄像机依然还在使用胶片。注意：摄像机和放映系统是两个不同的概念。\n2014 年，IMAX 推出第一代激光放映系统。这套系统目前在中国大陆有五家：\n东莞万达影城（华南MALL店，亚洲首个 IMAX 激光影城） 哈尔滨泰莱时代影城 昆明大都LCC光魔激光影院 贵阳越界影城（未来方舟店） 中国电影博物馆 由于价格昂贵（据说成本 10 倍于 IMAX 数字），IMAX 一代激光系统没有在全球推广开。\n2018 年 IMAX 缩减了一代 IMAX 规格之后，推出了 IMAX 二代激光系统，用来取代除了 IMAX 专属画幅和画面之外一点优势都没有的 IMAX 数字。二代激光系统成本大概是一代激光系统的五分之一，目前在中国大陆有 50 块以上的屏幕使用了 IMAX 二代激光放映系统。武汉的 武商摩尔 IMAX 影城（IMAX 激光） 采用的就是 IMAX 二代激光系统。\n杜比数字技术也经常出现在家庭影院中，因此我们对杜比实验室的标志并不陌生。但杜比影院还是近几年才进入中国的电影市场的。武汉的第一家杜比影院 武汉万达影城江汉路悦荟广场店 于 2018 年 1 月才开业。直到目前（2020年 9 月），武汉也只有三家杜比影院。\n杜比影院（Dolby Cinma）融合了三套技术：\n杜比视界 (Dolby Vision™) 技术的令人叹为观止的亮度、深邃的暗部细节和鲜亮的颜色可对每个片段渲染出夺人心魄的感染力。 杜比全景声 (Dolby Atmos®) 采用数十个扬声器（最多可以有 64 个）和最多 128 音轨的音床封装，让的震撼音效遍布整个房间，始终萦绕在您的周围。 获得红点设计大奖的影院设计，理想的环境和高档的座位吸引着您的到来，让您畅享其中。 从技术的表现来看，杜比的技术更新，杜比全景声效果更好，杜比影院屏幕的亮度也更高。可能无法与 IMAX 激光比肩的就只有 2K 的分辨率和屏幕尺寸了。\n但如果你去 IMAX 数字影厅观影后就会知道，大量的 IMAX 数字 影厅的观影效果都很差，屏幕小不说，亮度还非常暗。看完之后总会感觉「我是不是看了一个假的 IMAX」。杜比影院的体验必须是完爆 IMAX 数字的。\n下表给出了 IMAX/杜比影院/Cinity 这三种目前地球上最好的电影播放技术的数据比较。其中数据综合了 杜比影院对飙IMAX，谁才是极致视听体验的未来？ 和 备战《双子杀手》之谁是最强影厅——主流影厅介绍( Cinity，杜比影院，IMAX等) 两篇文章。\nIMAX胶片/数字/激光、杜比影院、Cinity参数对比\n影厅名 发布年份 视频放映技术 放映机/投影系统 色彩空间 对比度 分辨率 HFR帧率 亮度 音频技术 3D 眼镜 IMAX 胶片 70mm IMAX 18K（根据胶片） 不支持 20fL 3D IMAX 数字 2008 IMAX/Barco/科视 双氙气 2K DCI-P3 2800:1@2D 2K 不支持 14fL 2D/6fL 3D 5.1声道传统 线偏振 IMAX 激光一代 2014 伊士曼柯达/IMAX/Barco Barco 4K双机 Rec.2020 8000:1@2D 4K 2K@60/4K@60 22fL 2D/12-14fL 3D 11.1 声道传统 杜比3D 分色 IMAX 激光二代 2018 伊士曼柯达/IMAX/Barco Barco 4K单机双镜头 Rec.2020 8000:1@2D 4K 2K@60/4K@60 18fL 2D/10fL 3D 11.1声道传统 圆偏振 杜比影院 Dolby Cinema 2016 杜比视界 科视 Solaris CP42LH 6P 双头 Rec.2020 1000000:1 2K 2K@120/4K@60 28-31fL 2D/14fL 3D 杜比全景声 杜比3D 分色 Cinity 2019 科视Real Laser 科视Cinelife CP4450-RGB 双机 4K 120 28fL 2D 杜比全景声或传统音效 RealD XL 圆偏振 光通量、照度和亮度 上表中的亮度单位 fL 代表英尺伯朗（footlambert），是电影行业用于衡量每单位面积在某一个方向上所发出或者反射的光强度。介绍如下：\n先说光通量：\n光通量单位是流明(ml)，它指光源发出的光的总量。投影设备的最大光通量一般是固定的。 再说光强：\n光源在给定方向上，每单位立体角内发出的光通量。 光强的单位是坎德拉(cd)，也称烛光。 1 cd = lm / sr（球面度） 再说照度：\n光落在物体表面的密度就是照度(illuminance) 照度计算公式： 照度(E) ＝ 光通量 / 面积 ＝ F / A 照度的公制单位(lux，lx, 勒克斯，1lm 的光通量均匀分布在 1 平方米的表面)： lm / m2 ＝ lux (lx) 照度的英制单位(footcandle, fc，英尺烛光，1lm 的光通量均匀分布在一平方英尺的表面)： lm / ft2 ＝ footcandle (fc) 1 fc ＝ 10.76 lx 1 lx ＝ 0.093 fc 再说亮度：\n亮度表示人眼直接看到的发光体或者反光体，也就是单位面积在在某一个方向上的发光强度。 亮度计算公式： 亮度(L) ＝ 发光强度 / 面积 ＝ I / AP 亮度与光通量： 亮度 = 光通量 x 屏幕增益系数 /（ 投影面积 * π ） 亮度的公制单位（尼特，坎德拉）： nt = cd / m2 亮度的英制单位（英尺伯朗）： footlambert(fL) ＝ cd / ft2 1 nt = 0.2919 fL 1 fL = 3.426 nt 看了下面这个参考表，就知道杜比影院的亮度参数有多么恐怖了。同时它还有 一百万比一 的对比度！\n名称 激光电视 会议室 2D 影院 3D 影院 微型投影 光通量 3000 1500 视屏幕大小定 视屏幕大小定 \u0026gt;350 尼特(nt) 300 150 48 20.5 48 英尺伯朗(fL) 87 44 14 6 14 更多故事 还记得 2009 年《阿凡达》上映的时候，整个华中地区只有 环艺新民众乐园（中国第六家 IMAX 影院） 一块 IMAX 屏幕。当年我凌晨一点来到民众乐园，发现一楼已经被来自华中地区的各位影友或座或躺全部挤满，有些影友等看完电影立刻就搭乘第一班高铁回去。这个体验远超电影本身给我带来的震撼。\n上面数据表中提到的 Cinity 在这里解释一下。2019 年李安的《双子杀手》宣传采用 4K 在 120 帧下观看效果最佳，这就是 Cinity 的优势所在了。从上面的介绍中可以看出杜比影院支持 120 帧率但不支持 4K，IMAX 激光支持 4K 但不支持 120 帧。Cinity 则正好同时填补了两家空白，支持 4K+120 帧。\n通用选择方案 最后给一个通用选择方案吧，方便我这种技术纠结症患者康复：\nIMAX 摄像机拍摄的影片，理所当然选择 IMAX 激光，一代二代无所谓，激光就是了。反正你在中国碰到 IMAX 一代激光也是小概率事件。 非 IMAX 专属的影片，优先选择 杜比影院 。 专属 4K+120 帧片源，选择 Cinity，若所在城市没有 Cinity，则选择 杜比影院。因为 120 帧带来的震撼会超过 4K 带来的。 如果不在武汉也没关系，通过 IMAX 影院（官网），杜比影院介绍（官网） 可以找到自己城市的 IMAX 激光厅和杜比影院。通过 IMAX 中国（维基百科） 可以找到自己城市所在 IMAX 影院的屏幕尺寸、座位数。杜比影院的详细数据我还没有找到，如果找到了再更新。\n参考资料 IMAX 影院（官网） IMAX 放映机（维基百科） IMAX 中国（维基百科） 杜比影院介绍（官网） 【杜比影院】放映厅列表（官方公众号） 【杜比全景声】放映厅列表（官网公众号） 备战《双子杀手》之谁是最强影厅——主流影厅介绍( Cinity，杜比影院，IMAX等) 杜比影院的误区：杜比影院＝杜比音效？ 武汉首家杜比影院万达影城江汉路店1月20日盛大开幕 杜比影院对飙IMAX，谁才是极致视听体验的未来？ 照度（维基百科） 照度、辉度和亮度 我来讲一下“激光电视”的亮度 啥是IMAX？看这一篇就够了 全文完 ","date":"2020-09-10","description":"","lastmod":"2020-09-10T15:36:19Z","slug":"watch-tenet-in-cinema","tags":["film"],"title":"看诺兰的《信条》，不要随便找影院！——武汉杜比影院和 IMAX 影院简选","url":"https://blog.zengrong.net/post/watch-tenet-in-cinema/"},{"categories":["impressions"],"content":"《糖豆人：终极淘汰赛》火了，游戏开发商 Mediatonic 和发行商 Devolver 显然没有做好准备，断线，外挂等现象让玩家们意见炒鸡大。SAGITEAM 在中午组织了一次团队赛，也以失败告终：\n这么糟糕的应对方式，说明 Mediatonic 是一个走了狗屎运的小公司？\n显然不是。\nMediatonic 2005 年成立，总部在伦敦，是英国最大的独立游戏工作室之一。人员 240+，在英国和欧洲多个城市都有分部，支持远程办公。\n这似乎说明，Mediatonic 对游戏的爆红没有做好准备，他们也不知道，游戏能突然火成这个样子。导致服务器安排一时没有跟上。\n话说回来，无处不在的可能性不就是游戏的乐趣吗？\n游戏是怎么爆火的 我见过的游戏大火有两种不同的方式。\n第一种方式 ，一个优质的游戏，通过多轮测试和优化，准备好 Money，突击投放，将游戏快速推到榜单前列，使其成为一段时间的现象级产品。这个「现象级」的时间周期，取决于花钱的数量，后续运营方案，以及游戏本身的质量。\n从 2018 年开始爆火的超休闲游戏，以及大部分商业化游戏，都是这个模式。以莉莉丝的《剑与远征 AFK Arena》 为例，使用的就是「针对目标用户的包场」思路。可以参考这篇文章： 莉莉丝发行负责人张子龙复盘《剑与远征》：做发行没有秘诀 。\n我在疫情期间基于公开信息对莉莉丝的发行策略做了一些简单的总结，一起放在这里。\n第二种方式，一个特别的游戏，通过多轮测试和优化，由于产品本身的特点，在第一次曝光的时候就吸引了大量自然传播。或者不温不火一段时间之后，因为某个特定的事件爆红。这个「爆红」的时间周期，取决于游戏本身的内容和质量，以及后续的运营方案。\n例如去年在小游戏/超休闲领域爆火的《我飞刀玩得贼6》就是这种方式的代表。睡神飞工作室六年做废了 50 多个游戏，飞刀在 2019 年春节期间上传到 AppStore 后突然爆火。爆火带来暴抄，微信小游戏版本上的李鬼版本甚至比原版手感更好，真是原创的无奈啊。\n在第一种方式中，游戏的火爆是被发行商计算好的。媒体造势，花钱砸榜，平台曝光，视频广告……只要产品本身 OK，游戏是一定会爆火的。这种推广方式好像是一次经过精细计算和缜密指挥的战役，部队装备精良，士兵斗志昂扬，战则必胜无疑。\n第二种方式似乎有一点随机性。游戏本身的绝对质量的要求可能不如商业游戏那么高，但游戏本身有特点，有传播性，能得到玩家和大众的自发分享传播。一旦传播的范围从普通玩家转移到普通手机用户，游戏的爆红就不可避免。各类媒体会蜂拥开始免费介绍，游戏的曝光会指数级增长。年初爆火的老牌游戏《集合啦！動物森友會》就是这么出圈的。现在的《糖豆人：终极淘汰赛》也是同样的情况。\n我的第一篇 游记 就是写动森： 游记：我换皮《动森》只要三个月！\n无论是哪一种方式，爆火的游戏本身的质量都是上乘的。一个垃圾游戏火不起来，一个抄袭游戏或许可以赚到些钱，但不可能爆火。\n小团队怎么做出爆火的游戏 睡神飞工作室的创始人有一个分享： 睡神飞创始人：失败50多次后，我们做出了“飞刀”和“大刀” ，在这篇分享里面有他的心路历程。他的原话我摘录一下：\n根据我们做游戏的思路，反正就是不停地试，我也不知道我要做什么，我就不停地试，我不知道接下来的游戏会做成什么样，但我肯定会不停地去折腾，至于折腾出来是什么东西我也不知道，我就喜欢试，我就是这样做游戏的。但是大家也不要学我，因为代价比较大，我去年做废了22个，我现在已经做游戏6年了，已经试废了50多个，我相信大家没有这个耐心去试废50多个，因为创新是很难的事情。\n缺人手，缺时间，不断加班，不断测试，面对比你更快的抄袭…… 这些是大部分小 CP 都会碰到的事。但睡神飞工作室为什么能支撑下来，而没有像其他小 CP 一样默默地灰飞烟灭呢？我觉得有两点：一是始终坚持创新，不轻易言弃；二是坚持中遇到了 TapTap 和小游戏红利。\n如同睡神所说：\n你必须要对创新本能地渴望，不创新，毋宁死，并不断死磕。\n而且，你还需要正好在死磕中，钱还没花完的时候，遇到红利。\n大团队怎么做出爆火的游戏 以上面提到的 Mediatonic 为例，让我们看看大团队是怎么做的。下面这段是 Mediatonic 官网的介绍，说明他们对人员的要求。\nMediatonic is a team of over 240 exceptionally talented and seriously hardworking people who devote their careers to crafting awesome original games.\n下面是更详细的介绍：\n从招聘信息可以看出其对人员的要求：\n前端程序员\n2D高级技美\n对于人员的高要求，以及欧洲更全面的游戏人才，都是做出爆款游戏的条件。但可惜的是，要做出爆款，不但需要人才和经验的积累，还需要一些运气。\n如果活得足够长，那么运气应该会好一些。毕竟运气也要遵循概率法则的嘛！\nMediatonic 从创始到现在已经 15 年了。\n小 CP 的出路在哪里？ 如果没有像睡神飞那样的死磕精神，也不像 Mediatonic 那样位于游戏人才丰富的欧洲，更没有十几年的游戏开发经验，应该怎么做才能保证生存，一直存活到产品和运气一起来临的那一天呢？\n今年 CJ 期间我请教了很多 CP 和发行朋友，向他们学习了各自活得更好的方式。简单总结一下：\n控制团队规模，提升开发速度，快速为发行商定制大量产品。 有国内成功 App 发行经验，期望尝试小游戏或出海。 用超休闲的方式测试中度休闲游戏，面向 CPI 开发游戏。 小游戏的高品质化，基于已有玩法的创新，用 10 人以上的团队和 2 个月以上的开发周期构筑护城河，高质量的游戏可以支持更高的买量成本。 基于之前的成功经验死磕产品，等待一个爆款的可能性。 追随新渠道的红利，基于新渠道的用户特点开发有针对性的产品。 学习发行的思路，搭建小游戏发行团队，尝试跑通新渠道的流量玩法。 这些公司这么优秀，可不可以不动脑子直接抄作业呢？\n当然不可以。\n世界是变化的。从小游戏渠道的发展历程来说，我们经历了 微信 -\u0026gt; 手Q玩一玩 -\u0026gt; 手Q -\u0026gt; OV -\u0026gt; 抖音 数个渠道红利期，去年的打法到了今年就不能再用了。上面提到的这些优秀公司，也是发现了自身的瓶颈，才在不断求新求变的路上找到了新的成功点。\n巴菲特、芒格和埃隆马斯克不是很对付。马斯克说巴菲特和芒格的护城河理论过时了，并认为他们的工作很无聊。而芒格评价马斯克是：一个智商180的人，但认为自己的智商有260。\n他们说的都是对的，因为他们观点的隐含假设不一样。\n马斯克作为创业者，他的隐含价值是寻求创新的可能性。寻求确定性的学习方法和寻求创新性的学习方法必然会出现巨大差异。所以，马斯克是在不确定性中寻求可能性，芒格是在不确定性中寻求确定性。他们在人类贡献中角色完全不同。没有像马斯克这种疯子，人类会少了很多创新进步的可能。而没有像芒格和巴菲特这种投资人去识别好企业，坚定长期支持，可能市场的资源配置机制也不能真正发挥。\n怎么在变化的世界中找到长期确定性的东西呢？\n巴菲特和芒格的投资思路给了我们启发：\n找到一个简单的、基本的道理 不管别人怎么说，都非常严格地按照这个道理去行事 只在自己的能力圈里出击 所以，抄作业是没用的，如果想游戏大卖，想做出爆款，就要明白自己的能力圈在哪里，在自己的能力圈的范围内严格、持续地执行，做有长期价值的事。\n怎样判断一个事情在不在自己的能力圈之内呢？芒格给出了一个判断标准：\n「如果我要拥有一个观点，而我不能够比全世界最聪明、最有能力、最有资格反驳这个观点的人更能够证否自己，我就不配拥有这个观点。」\nSAGITEAM 准备怎么做？ 根据上面的讨论，我认为 SAGITEAM 的能力圈边界是：\n优秀的执行力 磨合顺畅的团队 在轻度休闲游戏上的成功经验 理解游戏的研发、发行、运营的全套流程 根据这些能力，以及我们对市场方向的思考，我认为 SAGITEAM 的发展方向应该是：\n聚焦出海与混合变现 聪明地借鉴，有限度的创新 长期主义 想清楚所有事 每一次失败都是经验，每一次成功就是积累 朝气蓬勃，持续推进团队新能力 游戏全生命周期流程的学习与掌控 未来是不能靠赌的。未来靠我们自己。\n全文完 ","date":"2020-08-16","description":"","lastmod":"2020-08-16T12:36:30Z","slug":"bet-on-a-future","tags":["management","sagiteam","gamenote"],"title":"赌一个未来：从《糖豆人》看小游戏CP的出路","url":"https://blog.zengrong.net/post/bet-on-a-future/"},{"categories":["impressions"],"content":"想法来自： 得到万维钢精英日课 4：「上工」如何「治未病」\n讲一个扁鹊的故事。《战国·鹖冠子》有个典故说，魏文王问扁鹊，你们三兄弟的医术谁最高明啊？扁鹊说，我大哥的医术其实是最高的。他总是在别人还没发病的时候就把病治好，所以他的名气只限于我们家里。我二哥的医术次之，他能在症状初发的时候遏制住病情，所以他只在我们村里有些名气。我的医术其实最差，我都是看人病情很严重了才开始治，动不动就做大手术，一天天的双手沾满鲜血，用的都是猛药……结果我名气反而特别大，诸侯都知道我。\n这大约就是《黄帝内经》中说的「上工治未病」，在你还没病的时候就把你给治好了，够牛逼吧？\n公司管理中也要将上工治未病。Expedia 是一个在线订机票、订旅馆、租车的公司，大约相当于「携程」。它在2012年发现，客服电话中，每年有两千万个，都是关于顾客订了票之后找不到行程单的。考虑到客服的薪资，每个电话的成本是 5 美元，两千万个电话就是一亿美元。\n为了找行程单要多花一亿美元，怎么省下这笔钱呢？公司成立了一个多部门联合的作战室，调查发现很多人找不到行程单是因为留了错误的邮箱地址，或者是行程单被发到垃圾邮箱中去了。于是技术团队在网页上做了调整，包括让顾客填写两遍邮箱地址，提醒顾客去找找垃圾邮箱，并且设计一个按钮，让人能一键调出行程单……这次联合行动把打电话的比例从 58% 降低到了 15%，公司就此节省了每年 7400 万美元的支出。\n但帐不是这么算的。为什么我们要在 2000 万个电话的时候才发现问题，而没有在 10万个电话的时候就发现问题呢？\n假设 Expedia 有三个部门：营销部负责吸引客户，技术部负责网站建设和维护，客服部负责电话客服。你说其中哪个部门应该关注电话太多的问题呢？客服部肯定不会。电话越多，客服部就越重要，就越需要更多的人力和财力，权力就越大，客服部喜欢电话多。技术部只要保证网站不崩溃、网页没问题就行了。市场部门更不在乎。\n以前我在一家公司管大技术团队，分为三个大的技术组：客户端、服务端、通用服务组。前两者很容易理解，通用服务组则包含了运维和基础服务（中台）。一旦游戏服务器出了问题，那个组背锅最多呢？\n当然是通用服务组。因为通用服务不出问题才是常态，一旦出了问题，就是大问题。大问题当然是底层技术背锅。通用服务组有没有做努力呢？他们非常努力，每天都在想尽办法节约开支，提升服务的稳定性，进行各种创新。但创新需要试错，创新也需要成本，一旦由于创新导致出错，就会出现灵魂拷问：「为什么花了钱还是不稳定？」\n你看，未病怎么治，或许是个悖论。或许唯一的选择就是「不出错」。「不创新」就意味着不出错，「多做多错，少做少错，不做无错。」\n现代企业的分部门制度，给了每个部门一个不同的聚焦点。这么做的好处是能够提高效率，每个部门都非常清楚自己的任务是什么，只要全力以赴好好完成分内的任务就行了。但坏处在于大家都变得短视了，只专注于自己部门的事，没有全局思维。\nSAGI 需要全局思维。我们不怕你出错，只怕你不创新。游戏不是让你顺其自然做出成绩的，游戏需要你不断打破常规，寻找自己的极限。把每天的任务完成就行了吗？解决现在的问题就行了吗？你有没有挑战过自己的极限呢？你有没有试图改变过自己多年的惯性思路呢？你要有没有不怕别人笑话当面提出一个看似傻 X 的问题呢？你有没有勇敢面对一个全新的领域呢？\n当人们无法保持个体的独特性时，就会实现平庸的统一性。\n做游戏需要全局思维。SAGI 一直在做的事情——信息透明，内部和外部培训，「侵入式工作」，「T 型人才」，「决策下放」……都是为了提升大家的全局思维。\n我们讨厌自以为是的傻X，我们尊重敢于突破的沙雕。\n产品和运营是密不可分的工作，开发和美术都应该懂一点基础数据知识，投放岗位必须要懂一点产品。这是我们的立身之本，是我们能更快走向成功的核心机密。这也是公开的秘密，是谁都抄袭不了的原则，是哪家公司都无法跨越的护城河。\n我们不要平庸，我们宁可像烟花一样绽放。\n佛教有一句禅语：「凡夫畏果，菩萨畏因」。这里的「畏」不是「敬畏」之意，而是「谨慎、探究」之意。凡夫俗子并不知道自己为什么做，欲望来了，就跟随欲望；情绪来了，就跟随情绪；世界流行什么，就改变自己去迎合世界。而菩萨只关注内在变化的源头，结果是什么并不重要，菩萨关注原因，所以会平静地接受结果。\n你是凡夫，还是菩萨？你想做凡夫，还是想做菩萨？\n在 SAGI，你有选择。\n有个故事这么说的：有两个人在河边散步，发现河里有个孩子，他们合力救下这个孩子，发现又漂来一个孩子。孩子越救越多，一个人就和另一个人说，我不能在这里救孩子了，我要去上游，看看谁在往河里扔孩子。\n我们要做那个去上游的人。我们必须去上游。\n全文完 ","date":"2020-08-11","description":"","lastmod":"2020-08-11T15:42:23Z","slug":"upstream","tags":["management","sagiteam"],"title":"去上游","url":"https://blog.zengrong.net/post/upstream/"},{"categories":["impressions"],"content":"从 ChinaJoy 回来之后真的是忙翻了，这两天遇到一些问题，综合前段时间的思考，触发了一些想法，用故事的形式记下来。\n小 A 的故事 小 A，不知道手头的工作怎么做，来问他的主管。主管指点了方向，小 A 还是没做好。主管叹了口气说： 嗨，我来做吧。\n下次碰到重点工作，主管不敢再安排给小 A。我自己做多快啊，使命秒达。你看我多高效，心中的满足感和自我成就感提升了，美滋滋。回头看看自己堆积如山的 list，默默加班。\n到了评绩效的时候，主管给小 A 打了个 C。\n小 A 很委屈：我做事情都很努力啊，也很认真地在请教你啊！你给我的事情我都做完了啊！不会的事情我也请教你了啊，为什么你说我表现不行？\n主管也很委屈：我交给你的事情你都搞不定啊，我怎么敢继续把事情交给你呢？很多你的事情都是我帮你做了啊！\n小 A 一气之下，辞职！什么小破公司，我干得这么优秀，哪里还找不到好工作？心情有点小激动，到处找人吐槽，这公司不行，那个主管不行，这个老板不行，整个就烂透了嘛！\n小 B 的故事 主管意识到自己的管理失误，开始改变管理方式。\n小 B 做事儿挺靠谱，交代的事情都能按时完成，对本职工作还会做一些深入研究，偶尔能提出一些不错的改进意见。\n主管找到小 B 说：你的主观能动性不错，这摊子事情就交给你了！你有拍板的权利！说完主管就跑去忙 list 上小 A 离职剩下的事情了，心说终于可以少加一点班了。\n月底考核的时候，主管发现小 B 管理的项目出现了很大的问题，小 B 没有及时上报损失。主管给小 B 打了个 C。\n小 B 很委屈：你不是说让我来决策的嘛？我有拍板的权利！出了错可以改正的啊！\n主管也很委屈：我说把事情交给你，但你出了这么大问题为啥不来问问我啊？最后还不是我帮你擦屁股！\n经过此役，小 B 虽然没有辞职，但做事情总是畏首畏尾，再也没有碰到他主动提出改进意见的情况了。\n主管的故事 主管真的是个很 nice 的人，但也是个很苦逼的存在。小 A 做不了的事情，主管自己扛了。小 B 的锅，主管默默背着。\n主管不想放弃任何一个员工，又不愿意降低自己部门的工作质量，所以眼瞅着自己的 list 越来越长，加班越来越多，发际线越来越高。\n主管觉得，你干不了的事情，我来手把手教你一次。如果还干不好，我就帮你做了。你出错了，我来背锅还要填坑，没有比我更难的主管了。 为什么大家都没办法成长起来？\n主管自己非常认同一句话：\n当领导者表现出自我牺牲的行为时，我们会认为他们具有更高的道德感，是一个更好的榜样。\n我努力了，也牺牲了，说好的榜样的力量呢？\n也许，应该找老板谈谈了。\n老板的故事 老板想做甩手掌柜。\n他常把这句话挂在嘴边：「我把放权做好，你们最好都不要来找我。你们可能不知道我给了你们多少权限，所以你们要大胆尝试。」主管也是因为受了老板的影响，才敢于放手给小 B。\n老板理解主管的苦恼，因为老板之前也是主管。老板说， 你看似做了正确的事情，但没有把握好边界，所以没能正确地做事。\n要解决小 A 和小 B 的问题，一是要推动员工「自驱力」的形成，二是要理解员工的「个人工作匹配度」，三是要转换自己的心智模型。\n自驱力是自驱型团队的核心 只有让员工的个人目标与团队目标、公司目标达成一致或者逐渐趋同，自驱型团队才可能逐渐形成。\n在小 B 的例子里，主管虽然放权放的快，但没有做好顾问。主管对小 B 使用的是「挑战-努力」模型，但自驱力的建设需要「压力-控制感」模型。\n「放权」不是简单说出「这事儿你说了算」就够了的。要把「放权」做好，起码要做到下面三点：\n1. 解决小 B 的后顾之忧\n后顾之忧的一个方面对小 B 还没有准备好的事做好培训。若小 B 没有项目管理能力，主管就需要帮助他规划好项目团队成员数量和配比；若小 B 对主流市场方向理解不够准确，主管就需要对其理解进行修正。\n后顾之忧的另一个方面是要帮助小 B 解决他无法承担的后果。项目的资金投入、整体思路，都需要有经验的主管和小 B 一起讨论决定，用「这事儿你说了算」把所有责任都扔给小 B，是愚蠢和不负责的行为。\n2. 在小 B 有能力做出决定的事情上闭嘴\n主管要分辨出那些并非最好选择，但并不离谱的决策。此类决策可以有选择性地让小 B 自行决定。有些决策在短期内没有立竿见影的效果，但长期运行下去可能给整个项目带来惊喜。不要对项目抓得太死，该闭嘴的时候就要闭嘴。\n3. 小 B 和主管都要对自己的选择承担后果\n小 B 和主管，都知道自己应该在选择中承担的后果。主管知道放权的后果，也知道那些决定必须要由自己来做（参见第 2 条）。小 B 知道主管对自己的信任，重视自己的每一个决定。虽然失败最终都是主管背锅，但小 B 已经理清了自己的责任，在选择中获得了成长。\n员工与工作匹配度 工作匹配度是组织心理学中的一个现象：它衡量的是员工的态度、价值观、能力和性格与工作、职责和组织特征之间的匹配程度。如果说每个候选人心中都有一个雇主优势清单，那么排在大多数候选人清单前列的，并不是有风格的团队、有梦想的项目、有热情的工作。 虽然候选人会把梦想挂在嘴上，但当遇到假想的困难或者需要承担责任的时候，少有候选人会眼前一亮热血沸腾，而是退而要求更稳定的生活，更高的薪资和福利水平。\n重点不是「我给你权限，你随便绽放」，而是要找到高匹配度的员工，用他们的主动性来解决管理者的问题。主动沟通的员工能解决好糟糕的管理者的问题。如果无法找到更优秀的管理者，就应该通过培训和企业文化让员工知道，主动沟通是非常安全的，不要等到事情完全失控或者准备离职的时候再来沟通。\n从小 B 的例子来看，小 B 已经是一个匹配度很高的员工了，主管要做好的，是让自己成为更优秀的管理者。只有优秀的管理者才能带好优秀的员工。\n转换心智模型 团队能力不提高，主管永远不会解放，这是作为主管应该具备的意识。主管应该扮演好教练的角色，而不是救火队长。你要做的仅仅是观察、给一些指点、适当给予时间上的支持。\n员工思维 主管思维 我行我上 教会其他人，追求团队成长 出问题责怪、抱怨 事前做好提醒，事中做好支持和跟进，事后做好复盘和分享 成长上主要考虑自己会不会，有没有提升 通盘考虑对团队整体的配合度提升、方法复用性、业务影响 归功于自己 弱化自己，归功于团队中表现优秀的人 小 A 的例子中，主管没有做好教练工作。教练工作就是事前提醒、事中支持、事后跟进。 即使是花费的时间比自己做更长，也是值得的。 如果小 A 没有在经过两轮之后成长起来，这只能说明他与工作配合度不够。解决方案也很简单：调岗换人。\n最后，老板语重心长地说：\n你是个愿意放权的好主管，但你要记住，无论给予什么，重要的是将牺牲与愿景、使命和组织的价值观联系起来，以便员工能够理解你为什么要这样做。如果这个目的不明确，可能会产生反作用。人类的特性是保持怀疑态度，当领导者做出自我牺牲时，人类的大脑会试图推断为什么领导者会这样做（无故利他），或者关注他们没有做什么（比如领导者是否保留了奖金）。\n老板还说，最简单的方案是从招聘源头上解决问题。例如字节跳动、腾讯、华为这样的企业，会给出业界顶配的薪水，对人员进行冗余招聘。企业内部使用赛马机制，驱动员工进行饥饿游戏，推进公司增长。当你的员工足够好的时候，不用小心翼翼地呵护部分员工脆弱的自尊心。他们可能因为更关注「成长」而时刻保持开放的心态。然而，初创企业不可能做到这一点。\n《娇惯的心灵》 主管似有所悟，转身离开。老板却打开了话匣子，拉住他开始闲聊。\n老板表现出对新一代人才的担忧。在《娇惯的心灵》一书中，美国当代大学生在心理状况上出现了三种错误的认知模式：\n凡是伤害你的，只能让你更脆弱。 永远相信你的感觉。 生活是好人和恶人之间的战斗。 美国社会中出现的大量「政治正确」现象和过激社会行为，都能在这些认知模式中找到原型。美国大学甚至制造出「微侵犯」的概念，提倡每个人「发现可疑即举报」，甚至不用等到下课。你可能会因为在上课路上碰到一个有色人种同学，顺手紧了一下书包肩带而受到举报。教授们提心吊胆地搞教学，再重要的内容，只要可能引发争议，他们就会舍弃不讲。\n教授群体似乎也出现了这种苗头，之前一个学者反对论文的观点，会写一篇论文去驳斥这个观点；而现在，学者们动不动就联名发表公开信，要求对这篇论文进行撤稿，要把这篇文章从学术记录中彻底抹去。这种处理方式，过去只是在学术不端或者剽窃的情况下才适用。\n「真是世风日下，人心不古啊！」老板边说便摇头。\n主管实在没空陪老板唠叨，准备遁走，可无奈老板就是不停嘴。情急之下，主管想到自己碰到的一个棘手问题，就「请教」了老板：\n「如果一个天才员工觉得自己的主管是个傻 X，每天找同事抱怨说这个傻 X 主管不给机会让自己做不出成绩，应该怎么办？」\n老板说：「那你让他来找我。」\n主管说：「他经常说老板也是傻 X。」\n老板推了推眼镜，镜片下的瞳孔闪闪发光，他直视主管的双眼，淡淡的说：\n「他为啥还没辞职？」\n参考 工程师如何从技术转型做管理？ 作为领导的觉悟 《自驱型成长》 全文完 ","date":"2020-08-07","description":"","lastmod":"2020-08-07T04:11:09Z","slug":"smart-middle-manager","tags":["management"],"title":"聪明的中层管理者","url":"https://blog.zengrong.net/post/smart-middle-manager/"},{"categories":["impressions"],"content":"来上海的第五天，参加了数数科技主办的分享会「数据驱动，数说变革」。数数科技邀请了Habby、凉屋游戏和青瓷数码讲述各自的产品和运营理念，我听得非常过瘾。趁遗忘曲线还没有发挥更大作用，赶紧记下来。\n当然本文标题是有点夸张了，一篇短短的游记不可能摸到「道」这个境界。CJ 前我给自己定了计划：每天至少参加一次业界会议和一个交流Party，并且至少和一个CP或者发行进行一小时以上的深度交流。现在看来，我是超额两倍完成了这个计划。但交流的工作压缩了归纳写作和深度思考的时间，在会议期间我就只能抓住零碎时间写一点比较肤浅的分析了。这篇文章的真实 Title 原本是《关于 Habby、凉屋和青瓷的肤浅分析》，但这样一写又有不尊敬的嫌疑，这三家公司都是非常优秀的游戏开发/发行商，怎么都不可能和「肤浅」挂钩。经过慎重思考，我又把标题改成了《关于 Habby、凉屋和青瓷这三家优秀企业的肤浅分析》，读起来也是不顺口的。正好行李中背了一本SAGI读书会的必读书目《程序员修炼之道：从小工到专家》，就启发了我想出了这么个非常标题党的标题了。\nHabby：弓箭传说抄起来只要3个月 Habby CEO王嗣恩说，两三年前做钢琴块、跳舞的线，做一款成一款，动不动就一亿下载。但去年产品变化就很大了，如果认为自己的产品是情怀优先的良心之作，市场是不一定认的。Habby 去年也躺了好几款产品。\n因此，团队立项一定要学会控制时间成本的快速验证方法。想要快速迭代和验证核心玩法，工具要先行。很多海外的几人小团队，在验证产品的时候，都会先做编辑器。这样就可以实现策划直接来调整游戏的表现。如果每次调整产品都需要程序员去动手，可能就会错失机会了。\n快速验证的引擎选择也很重要。早期 Habby 做音游的时候，使用 Cocos2d-X，但 C++ 程序员不愿意学五线谱，学了两周都没有学会，而一个 H5 程序员愿意学，所以他们就采用了奇葩的开发方式：先用 H5 做一遍游戏，调整手感后，再用 C++ 翻译一遍。当然，后面 Unity 越来越成熟了，也就不用这么麻烦了。\n弓箭传说在立项阶段只有 2 人，制作人兼任策划，再加上 1 个程序员。 两个人做了一关，内部试玩的时候大家都觉得很好玩。在这个阶段不需要美术参与，可以购买一些便宜素材，目的是快速出 demo。demo 验证成功之后，团队才增加到 10 人，并开始找外包团队进行正式美术制作。弓箭传说的美术外包量比较大，大约是 500 万成本。\n在弓箭传说产品的测试过程中，团队对游戏进行了多次的调整。比如之前用推图的形式呈现，后面又改成章节的形式呈现，这些测试都会消耗成本。现在随便找一个团队，把弓箭传说的玩法复刻出来，只需要三到五个月，是因为在成品出来之前，Habby 已经进行了大量的测试和验证，复刻是可以完全规避高昂的验证成本的。\nHabby 在计算了弓箭传说的投放和回收周期之后，认为需要 1000 万美金来进行投放，但此时账上只有 100 万，这就是另一个故事了。\nHabby 认为休闲游戏成功的不确定性太大，整个发行市场已经是买量为主，硬拼 ROI 的状态，因此一定要先把帐算清楚，并且要学会通过快速验证来降低失败概率。如果一个玩法已经很成熟，那么买量肯定会比较贵，弓箭传说第二年的收入数据就降低到上一年的 50%。\n凉屋游戏：感性创作，理性设计 凉屋游戏 CEO 李泽阳的讲了凉屋的游戏设计理念，同时也详细介绍了一款「正常一点」的像素动作游戏的制作思路。\n这款动作游戏选择像素方向，可以大幅降低成本。 一开始用 2 个人做了 2 年，完成了核心机制和一半内容，后续又增加到 6 个人，两年半做完。 整个的过程非常顺，几乎没有遇到实际制作的问题。游戏中使用了 5000+ 帧逐帧手绘动画，40+武器、80+ 技能风格和近百个道具。\n凉屋在设计游戏时会反复考虑「媒介即信息、认知负载、廉价维度」等抽象概念。\n青瓷数码：最强蜗牛背后的数据体系 青瓷数码的 COO 曾祥硕在演讲过程中，一直强调青瓷从成立初始都是坚决走自研自发路线的。青瓷的员工 300 人，从 2013 年的《格子 RPG》和 2015 年的《三国志大战》开始就积累的大量的卡牌游戏经验，2016 年的《愚公移山》和 2019 年的《无尽大冒险》都是放置挂机类的产品，因此2020 年的《最强蜗牛》是在有大量的积累下的一次成功尝试。\n曾总开玩笑说青瓷是「厦门美术最差的团队」，所以蜗牛就采用了绘本风格，以内容为主。 青瓷每年出一到两款产品，一款产品会做两到三年。\n青瓷一直认为研发和运营不是两个团队，而应该合二为一。《最强蜗牛》开始开发不到两周，《不思议迷宫》不到一个月就开始测试了。青瓷的测试不是买量测试，而是依托 QQ 群和公众号沉淀下来的几十万核心用户群体，这样就可以随时d得到几百到上千量级的新增用户。 由于青瓷的测试大多是在很核心的玩法层面，面向的也是核心自有用户而非买量来的泛用户，所以多烂的美术用户都可以接受。也就不必在核心玩法定型初期就考虑成本高昂的美术问题。\n青瓷要求员工有复合能力。一条视频的文案、剪辑可以由策划来做，美术辅助。商务必须懂产品和运营，这样才更容易达成合作。如果策划对客户端 UI 现状不满，可以自行拼客户端的产品界面。青瓷的最佳业务实践就是：\n研运一体的组织架构+复合型的员工+数据支撑\n数据支撑在运营过程中非常重要。例如，在运营过程中经常会碰到在游戏下面留言，在群里喷策划的玩家，这部分玩家能代表全部用户吗？\n不能。 只有大数据能代表全部用户。\n通过对用户关键行为的跟踪，运营团队发现《最强蜗牛》中大R最反感是充值时间太久，以及充值资源到账了之后花不掉。大R要找那种花了钱马上就全部花完的感觉。而零氪（非R）用户则会认为，在游戏中看广告是在使用自己的努力来换取资源， 你的游戏中不增加广告，或者广告位不够，是对他们的不尊重。\n《最强蜗牛》中很多竞技场打不过的玩家去哪里了呢？这群人跑到飞船电脑哪里去玩小游戏了。主线打不过的玩家是需要被关注的，因为这可能与引导相关。每日任务完成后的玩家， 会跑到物品下面去留言和吵架，还会去世界频道坑萌新。 这些对于用户关键行为的分析都不是运营人员能猜到的，而是通过数据分析得到的。\n《最强蜗牛》的新手流程被很多人喷，因为跑得快也要跑 20 分钟。也有很多业内朋友苦口婆心劝说蜗牛团队不要这么做新手引导。但运营需要靠数据说话，新手引导的数据表现是很好的，不需要调整。\n除了运营，数据的支撑还表现在对客服、QA、财务上，青瓷的财务是按照 A 股上市公司的方式在做，游戏中的道具如果没有小号，核算方式就会不同。客服掌握了数据之后就可以对高价值用户进行分层维护。\n我的感受 三个分享连着听下来，我有点醍醐灌顶的感觉。分享内容上，Habby 偏向于产品立项和，凉屋偏向于产品设计，青瓷偏向于运营。从分享内容和这三家公司的产品风格，也能看出三家公司的不同管理风格。果然，没有哪一个成功是完全相同的，每个优秀的公司都有自己的特色和优势。这些特色只可学习，不可抄袭，要根据自己所在企业的特点将经验内化。齐白石教育他的学生许麟庐说「学我者生，似我者亡」，就是这个道理。\nSAGITEAM 一直都在强调复合型人才的重要性，要求各岗位进行「侵入式」工作。我们要求运营和产品必须互相理解，甚至要求开发和美术理解运营数据。为了这个奇葩的要求，我被同事吐槽过多次。「为什么我要管这个？做好自己的事情不就好了么？」我也碰到过非暴力不合作的情况。但更多的人是不知道应该怎么合理进行「侵入式」工作。从青瓷数码的做法以及他们取得的成就来看，这样的要求对于自研自发的团队来说是很正确和高效的。\n在我看来，要解决复合型人才的问题，需要注意三个方面：\n从源头上下功夫，在招聘这个阶段就区分好「专才」和「潜力」两个维度。 做好内部流程培训，只提要求，不讲流程是达不到预期效果的。 做好岗位间的「信任管理」，保持企业文化的一致性。 「CJ 五日」系列 殊途同归：Habby、凉屋和青瓷的游戏之道 腾讯爸爸抱紧发行爸爸，CP爸爸在数钱 Unity能做小游戏？ 关于游记 游记，不是旅游日记，而是 游戏札记。 聊一下近期的游戏行业相关话题，加一点不成熟的思考。\n全文完 ","date":"2020-08-01","description":"","lastmod":"2020-08-01T03:20:34Z","slug":"gamenote20200801","tags":["gamenote","chinajoy"],"title":"游记：CJ五日-殊途同归：Habby、凉屋和青瓷的游戏之道","url":"https://blog.zengrong.net/post/gamenote20200801/"},{"categories":["impressions"],"content":"来上海的第三天，参加了「小游戏 大空间——腾讯广告IAA 游戏生态发展大会」。到了才发现，原来外滩 W 酒店就在昨天 Unity 大中华区总部白玉兰广场的旁边。\nIAA（In-App Ad）是相对于 IAP（In-App Purchase）来说的。前者是广告变现，后者是内购变现。目前休闲游戏的发展趋势是混合变现（IAA+IAP），但在天朝版号政策的严格执行下（见 游记：面对苹果爸爸的版号暴击，2020 小 CP 怎么活？ ），IAA 或许更现实一点。\n腾讯广告在这次 IAA 大会中主要讲了三件事。\n第一：IAA 游戏的整个流程是如何运转的。 第二：IAA 打造的智能平台帮助游戏实现专业投放和变现。 第三：寻找优秀的产品进入优选计划合作，寻找优秀的发行进入犀牛鸟计划合作。 IAA 游戏的生命周期流程 IAA 游戏的上线运营流程，发行爸爸们是很熟悉的，但 CP 可能并不清楚，这次 IAA 大会把整个流程讲了个透彻：\n立项评估 -\u0026gt; 测试调优 -\u0026gt; 推广测试 -\u0026gt; 规模大推 -\u0026gt; 平稳迭代\nIAA 游戏全生命周期流程图\n接着，给出了一个 IAA 游戏的收益计算公式：\nIAA 游戏核心收益公式\nIAA 游戏全流程量级\n再给一个全流程的量级图，对于游戏推广小白来说是很贴心啦！\n腾讯广告的智能系统 接着，腾讯爸爸的利器来了。为了让你能毫不犹豫而且非常顺利地花钱，腾讯广告的这一套智能投放系统提供了智能提价、自动跟盘、精准分析、ABTest 等各种工具，看得我真是眼花缭乱，目不暇接。\n所有你想要的功能都有了，海外投放回来耻笑国内广告平台工具太 low 的同学们，钱准备好了么？\n优选计划和犀牛鸟计划 这两个计划才是腾讯爸爸的杀招。一石二鸟，一箭双雕，不但搞定了发行爸爸，还同时搞定了CP爸爸。\n优选计划，CP 能分 20% 到 25% 的流水，还不用花自己的钱，天底下哪有这样的好事？我偷偷问了问《班主任模拟器》的 CP 刘总，流水多少能不能透露下？没关系这里就咱们俩，我绝对不会告诉别人的。刘总腼腆一笑，悠悠地说：很赚钱……。 狠……赚……钱…… 这句话应该怎么解毒？\n犀牛鸟计划绑定的是发行。如果你有下面这些经验，那么你可以成为腾讯广告的「官方发行商」哦！是 官方 哦！还不抱紧一点！\n官方资料 如果想参加优选计划和犀牛鸟计划，可以关注「腾讯广告游戏营销顾问」公众号。\n由于微信公众号的限制，要下载 「腾讯广告 IAA 游戏全周期能力矩阵.pdf」，需要点击「阅读原文」，然后点击前面的链接或下方图片。\n乱七八糟 这两天我见人就问人家游戏的 CPI，但因为脸皮厚的关系，从来没有感觉到对方幽怨的眼神。这些大神CP/发行爸爸们的数据总是能成功让我的小心脏一紧。国内 RMB 0.5 北美 $0.5 是怎么做到的？这可是不是超休闲啊！回头看看我们的 CPI，不由得老脸一红，顾盼间用手遮住屏幕，悄悄关闭了页面，在心中对着大神们无声竖起了 中 大拇指……\n学习之路，永无止境啊。\n招优化师，素材。 有意者请点下方广告后留言。\n「CJ 五日」系列 殊途同归：Habby、凉屋和青瓷的游戏之道 腾讯爸爸抱紧发行爸爸，CP爸爸在数钱 Unity能做小游戏？ 关于游记 游记，不是旅游日记，而是 游戏札记。 聊一下近期的游戏行业相关话题，加一点不成熟的思考。\n全文完 ","date":"2020-07-30","description":"","lastmod":"2020-07-30T11:07:33Z","slug":"gamenote20200730","tags":["gamenote","chinajoy"],"title":"游记：CJ五日-腾讯爸爸抱紧发行爸爸，CP爸爸在数钱","url":"https://blog.zengrong.net/post/gamenote20200730/"},{"categories":["impressions"],"content":"来上海的第二天，参加了罗斯基组织的 Unity 引擎技术沙龙。\nUnity 能不能做小游戏呢？看完这篇就知道啦！我先说一个段子吧。\n2018 年我刚开始做小游戏的时候，有个游戏创业的哥们过来问我，小游戏开发平台咋样？\n我说还挺好的啊，中国的 H5 开发引擎是全世界最棒的。要不你也来做小游戏呗？\n他坚定地说：不！我要等 Tiny Project。\n2019年我们又聊起小游戏，我说小游戏还不错啊你也来做小游戏吧。\n他坚定的说：不！我要等 Tiny Project。\n今年 4 月武汉刚解封的时候，他问我：兄弟，你知道抖音短视频怎么变现么？\n是的，Tiny Project 这个让众多开发者苦等两年多，并多次推倒重来的项目，终于要发布了。\nUnity 大中华区总裁张俊波先生在沙龙现场说，Tiny 会在今年 11 月发布。\n我把这个消息推给了从游戏创业转做抖音创业的哥们儿，不知道他的心中会不会泛起涟漪？\nTiny Project 下面有一些技术性的东西，如果引起读者不适，可跳过。我直接说结论：\nUnity 可以直接对接超级 App 平台，实现在开发环境不变，开发习惯不变的前提下，一键发布成小游戏。\n嗯，然后下面的就可以略过不看了，本文结束。 ^_^\nTiny 自动实现了模块化控制，能够在游戏首屏运行之后再加载后续资源（Lazy Loading），实现首包最小化。在打包的时候也可以选择不同的模块以进一步减少最终包体大小。\nTiny 也同样支持 DOTS 技术栈。\n支持一键打包，我在现场演示视频中看到了导出菜单中的某信、某宝的 target。\n版本优先支持 2019.4，用 C# 开发，你说它香不香？\n基于相同的技术架构，插件通用。\n下面是一些 DEMO 资源。\n使用Tiny 开发的小游戏 Tiny Racing：\nhttps://connect-cdn-public-prd.unitychina.cn/h1/instant-gaming/web/racing/27ceea11fc945d1edf5526c998588cf8/TinyRacing.html https://github.com/Unity-Technologies/ProjectTinySamples/ 风景欣赏 凑篇幅放几张图吧。Unity 办公室真的风景很好啊。\nUnity 的会客室，沙龙结束后我蹭这个地方做了一场视频面试。\n窗外的黄浦江\n「CJ 五日」系列 殊途同归：Habby、凉屋和青瓷的游戏之道 腾讯爸爸抱紧发行爸爸，CP爸爸在数钱 Unity能做小游戏？ 关于游记 游记，不是旅游日记，而是 游戏札记。 聊一下近期的游戏行业相关话题，加一点不成熟的思考。\n全文完 ","date":"2020-07-29","description":"","lastmod":"2020-07-29T15:50:27Z","slug":"gamenote20200729","tags":["gamenote","unity","chinajoy"],"title":"游记：CJ五日-Unity能做小游戏？","url":"https://blog.zengrong.net/post/gamenote20200729/"},{"categories":["impressions"],"content":"中国游戏的未来在哪里 - 游戏行业20年历史观察及趋势分析\n这是篇旧文。写在2018年1月 SAGITEAM 转型小游戏之前。用其中的分析思路来印证小游戏市场的发展与风向，几乎全中。\n我个人的工作和创业过程，经历了端游的盗版年代，页游的爆发和沉寂，手游的红利年代，小游戏的红利期和稳定期。整个过程，真的应了那句话「一场游戏一场梦」。\n专注一个细分领域，不放弃对市场机会的迅速跟进，做有价值的事。\n下面整合文中有价值的观点，加一点我的思考：\n1. 海外游戏团队比你工资底，还比你起点高\n海外二线国家的开发实力正在快速成长。尤其是那些语言障碍相对较小，教育实力并不弱，人力资本又相对比较便宜的国家，例如东欧的某些国家：乌克兰、波兰、匈牙利、捷克、塞尔维亚、克罗地亚、罗马尼亚 ，东南亚的马来西亚，印尼等等。\n波兰首都华沙也就2000多美金一个月，到波兰的二线城市，更是要便宜一半都不止。克罗地亚、乌克兰、塞尔维亚这些国家都有大量的游戏从业者，由于语言和文化与西方更加接近，他们进行原创和承接 3A 大作的外包工作更多，多年积淀形成了强大的生产力，他们就是 3A 大作的后花园。\n2. 教育和社会的因素\n中国的游戏开发者往往都比较苦逼，很少有人能掏自己的钱投资，按照自己的梦想干，不拿投资人的钱谁给发工资呢？没工资拿什么钱买房子养家呢？相比之下，老外游戏圈里有很多人虽然不是富二代，但他们也并不太发愁日常生活，整个社会的福利水平比较高，社会理念压力也没那么大，没有丈母娘逼着他们必须买房子才能娶他们家女儿，他们经常觉得在一个小城市租房子住就挺好，孩子上学、医疗之类的都没什么大压力，生活也没什么不方便，在这种情况下，他们就可以非常低成本的坚持宅在家里搞自己喜欢的东西，他们有足够的资本可以把兴趣放在第一位，喜欢游戏就做游戏，能做自己喜欢的游戏，赚点钱够活就行，就算不赚钱，有时候也可能不一定那么严重，而且老外的市场比较成熟，只要你的游戏是认真做的，你不收钱都有人主动给你捐钱，有时候靠玩家好心捐的钱都够养活一个 indie 工作室的。\n小游戏行业中的「龙虾游戏推荐」就是这样一个例子。龙虾是个认真做游戏推荐号的人，每篇文章都写得很用心。有一批业内的优秀发行在不断支持他，也让他可以把全部的精力放在找游戏，推荐游戏这件事上。这就是一个正向循环。\n3. 找志同道合的高水平合作者\n一定要找一群志同道合的人，最好人数少一点，但不要有想法不一致的人，家境都还过得去的最好，这样就不会争管理权。如果找投资，以不受投资者左右为首要前提。\n一定要找水平高的，对你们想做的游戏理解足够深刻一致的，大家能认同一个方向，这样就可以避免对产品方向产生巨大分歧，或者半路有人掉队。\n4. 坚持一个细分品类\n用搞科研的方法来做一个细分品类。坚持 10 年不换，不断磨练对这个品类的理解，不断精研，追求极致，把全世界所有在这个品类里出现的微创新全部研究透，用合理的方式加入自己的产品，再自己在所有巨人肩膀上引领更多的微创新，力求超过全世界做这个细分品类95%以上的团队。\n5. 小游戏就是下沉玩法\n这篇文章成文（2018年 1 月）之后的几个月里，小游戏开始飞速狂奔。SAGITEAM 就在此时开始创业。小游戏细分市场属于文中提到的「下沉」市场，SAGITEAM 也遇到了「往下走」还是「往上走」的问题。\nSAGITEAM 走了一个曲线： 先往上走，然后往下走，转头再往上走。 小游戏第一批下沉红利的时候，我们做了一个小游戏市场中的「独立游戏」质量的产品，结果第一批最大的红利没有吃到。痛定思痛，我们开始「往下走」，尝试小游戏市场的流量运作和变现。和本文作者的想法类似，我们发现行业内的高手太多，而且这件事似乎不适合 SAGITEAM 的基因。在 2019 年中，我们定下基调开始转头往上，做中度休闲产品，优先面向海外玩家。\n市场瞬息万变，不会有一个一直正确的方向。小 CP 更应该跟随趋势，找深层价值，做有意义的事。\n关于游记 游记，不是旅游日记，而是 游戏札记。 聊一下近期的游戏行业相关话题，加一点不成熟的思考。\n全文完 ","date":"2020-07-27","description":"","lastmod":"2020-07-27T06:42:05Z","slug":"gamenote20200727","tags":["gamenote","entrepreneurship","sagiteam"],"title":"游记：中小 CP 的游戏梦","url":"https://blog.zengrong.net/post/gamenote20200727/"},{"categories":["use"],"content":"一直以来我都是个米粉，家中和公司都使用了大量的小米生态链的智能设备。大部分设备的安装和使用都相当简单，也有些需要一点点技巧。这篇就谈谈怎么实现智能灯具的单开双控。\n注意，本篇不是广告，我没有收小米一分钱。但其中涉及到的所有产品均来自小米生态链。\n实现智能灯具控制，要解决的核心问题是保证灯具始终在线，这样才允许同时使用开关、手机、或者语音助手等对灯具进行唤醒。\n智能灯具是如何工作的 智能灯具自身包含网关和照明两个模块，网关部分的功能是保持与 WIFI 的连接，接收互联网发来的开关、调整色温和光强等信号。照明模块则与普通灯具的照明部分一致。\n因此，智能灯具的网关模块是不允许断电的，否则就无法与互联网连接，照明模块的电源由网关模块来控制。\n问题来了，智能灯具中的网关会 24 小时耗电？\n是的。\n好在网关的耗电量很小。或许你一次忘记关灯后消耗的电量，都会大于网关几个月消耗的电量。\n浪费和便利性，你选哪个？\n不同的智能灯具方案 有几套方案可以选择：\n方案一：智能灯具+传统墙壁开关 方案二：智能灯具+传统墙壁开关+智能无线开关 方案三：普通灯具+智能墙壁开关 方案四：智能灯具+智能墙壁开关 方案五：智能灯具+米家墙壁开关（支持灵动开关功能） 方案一 适用于要传统灯具损坏，直接更换成智能灯具，但不希望更换传统墙壁开关的情况。这种方案的问题在于，传统墙壁开关关闭时会对灯具进行断电，导致智能灯具离线，此时就无法使用手机或者语音助手对灯具进行控制了。\n方案二 是方案一的变体。如果锁死传统墙壁开关的开状态，直接用手机、语音助手、无线开关（一般是 Zigbee 协议，例如绿米Aqara无线开关 D1 贴墙式、绿米 Aqara 无线开关升级版）控制智能灯具的开关，是可以解决智能灯具离线问题的。但这样势必会修改旧的使用习惯，而且也需要另外购置 Zigbee 网关和无线开关。\n方案三 适用于保留传统灯具，将传统墙壁开关换成智能墙壁开关的情况。可以节省购买灯具的费用（毕竟主灯总比开关贵）。这看起来是很完美的方案，但根据我的经验，问题很多：\n智能墙壁开关需要零线，而一般的装修布线不会为灯具单独铺设零线。 智能墙壁开关有单火版本（不需要零线），但单火版本的故障率较高（或者说兼容性较差）。我购买过绿米 Aqara 墙壁开关（单火版），配合普通灯具使用时，凌晨时分灯具经常会无故闪亮，还蛮恐怖的。 智能墙壁开关的价格较高，大概是传统墙壁开关的 10 倍以上。 绿米的 Aqara 智能墙壁开关使用的是 ZigBee 协议，需要配合 ZigBee 网关使用，无疑又增加了费用。 方案四 有点浪费，因为智能灯具和智能墙壁开关都比传统设备贵。如果是新装修，选择方案三（预留零线）或者方案五都比较好。\n方案五 是我目前使用的方案。这个方案有如下优势：\n米家的许多智能灯具都自带蓝牙 Mesh 网关，这样在每个房间里安装蓝牙 Mesh 设备（米家蓝牙湿温度计，米家保险箱，米家LED灯泡蓝牙 Mesh 版，米家智能插座蓝牙网关版）的时候，就不需要再单独购买网关了。因为蓝牙协议的通信距离有限，把蓝牙 Mesh 网关做在灯具里是最合适的选择，每个房间都可以覆盖蓝牙信号。 传统灯具和智能灯具可以共存。 米家墙壁开关不是智能墙壁开关，而是传统墙壁开关。它可以用于控制传统灯具，也可以控制支持「灵动开关」功能的智能灯具。如果只希望将部分传统灯具换成智能灯具，这个特性就很有用。例如我的客厅门灯就是传统灯具，客厅主灯为智能灯具，这两个灯具的原始布线是接在一个「传统二开墙壁开关」上的。我只需要购买一个二开米家墙壁开关，其中一个设置为传统跷板模式接传统灯具，另一个设置为自回弹模式接智能灯具，就可以在一个墙壁开关上同时完美兼容传统灯具和智能灯具了。 米家墙壁开关的价格不算贵，大约是传统墙壁开关的两到三倍，做工真心不错。 SAGITEAM 的智能设备\n智能灯具带蓝牙网关\n对智能灯具实现单开双控 下面的这个例子，是将单开双控的普通顶灯，切换为米家吸顶灯 450，并使用米家墙壁开关实现单开双控。\n单开双控，就是使用两个单独的开关（单开），控制一盏灯。其实现原理图和电路图如下：\n由于之前的传统开关是单开，我购买的米家墙壁开关也是单开。与传统墙壁开关区分单控和双控不同，米家墙壁开关同时支持单控和双控。\n传统开关的接线如下图：\n上图中，上下两根黄色线分别为 L1/L2，中间的绿色线为 L。根据双控原理图，我们知道在两个传统开关中，有一个开关的绿线是输入火线，另一个开关的绿色线是接灯输入火线。可以使用试电笔将它们找出来。方法是在灯具处于关闭状态的时候，始终有电的那根绿色线就是输入火线，而另一个开关的绿色线就是接灯输入火线。\n根据米家墙壁开关说明书中的「双控」接线法，我们可以知道这个方法的核心是保证灯具永远处于通电状态，这样一来智能灯具就不会离线了。\n具体的接线如下图所示，这是其中一个开关的接线方式，多出的那一根 L2 没有用处了，用电工胶带包好即可。\n注意：如果第一个开关接入的是 L1，在另一个开关上，一般情况下要接入 L2。因为传统开关要保证两个开关的状态是互斥的，所以 L1 和 L2 是交叉联通状态。具体那根线是连通的，可以在通电后使用试电笔测试。一定要注意绿色线不要接错，否则可能会出现安全问题。\n灵动开关 使用「米家」连接智能灯具之后，一定要将灯具的「灵动开关」置为开启状态，这样灯具才能识别米家墙壁开关的「自回弹」模式的信号。\n我没有在互联网上找到关于智能灯具中「灵动开关」模式的过多资料，通过实验我对「灵动开关」有了一些理解，胡乱说一下。\n米家墙壁开关开启了「自回弹」模式后，每次按下的时候，作用是短暂切断电源之后重开电源。如果智能灯具支持「灵动开关」并开启该功能，那么网关模块会将这次短暂断电的行为理解为要求关闭或者打开照明模块的电源。由于网关内有电容器，短暂切断电源的行为并不会让网关模块丧失功能。\n后备式 UPS 也是利用这种机制工作的。在市电断开的时候，后备式 UPS 会在 10ms 内开始对设备供电。由于后备式 UPS 的供电一般面向的是家用 PC 机这类自带交换式电源，且对用电安全要求不高的设备，10ms 足够了。\n最后，再重复一遍，上面的关于「灵动开关」的原理都是我瞎猜的。\n张大妈有一篇 0成本把普通开关改造成灵动开关，用普通开关加弹簧的方式模拟小米墙壁开关的「自回弹」模式。注意，这只是解决了墙壁开关的问题，智能灯中的灵动开关支持依然是必须的。\n全文完 ","date":"2020-07-26","description":"","lastmod":"2020-07-26T09:27:21Z","slug":"dual-control-intelligent-lamps","tags":["live","xiaomi"],"title":"双控智能灯具的实现","url":"https://blog.zengrong.net/post/dual-control-intelligent-lamps/"},{"categories":["impressions"],"content":" 深度工作 作者：卡尔·纽波特 原作名： Deep Work: Rules for Focused Success in a Distracted World 出版日期：2017-7 关键词：心流，最小阻力，引领性目标，工作模式\n「深度工作」是指在没有干扰的专注下进行的工作，它会将你的认知能力推到极限，最终得到创造性和高价值的工作结果。「深度工作」之外的都是「浮浅工作」。\n最小阻力原则带来的效率降低 这是大脑的对于耗能的考虑，会本能遵循一个「最小阻力路径」，在所有的事情中选择一个最容易的。即使是你正在用整块的时间来处理一件重要的事情，当收到较容易的事情提醒的时候，你也会停下手头的工作，去处理那件更容易的事。\n例如我在撰写一个报告的时候，突然收到了企业微信提醒，而且还是 @我 的提醒，是看还是不看呢？此时我会有两个心理状态：\n一是认为看消息这么简单的事情，消耗不了我的太多精力，看一看如果是简单的事情就立刻回复，如果是复杂的事情就等手头的事情忙完了再回复。\n二是在心中压制看消息的欲望，强行忽视消息，继续手上的工作。\n这两种方式都会消耗注意力和大脑能量。对于不同难度和类型的任务，人脑内有多种不同的区域和机制进行处理。但无论哪种机制，在不停切换目标的时候，大脑的处理能力都会降低。\n在工作中，忙碌并不是全是好事。要分清「显得忙碌」和「真的忙碌」之间的区别，要摈弃「忙碌代表生产力」的思维定式和关键偏差。\n要解决上面谈到的最小阻力原则造成的切换成本，就要学会使用适合自己的工作模式。\n工作模式 只有「记者模式」才适合上面谈到的随时切换的工作方式。这种模式是可以在日程中随时插入深度工作，一有空闲时间，就能立刻进入深度工作模式。遗憾的是，这种能力这需要大量的练习，而且很可能需要一些生理机制的配合。\n大部分的人适合「节奏模式」。就是把自己的工作时间分成多个时间段，在不同的时间段采用不同的节奏。比如村上春树会要求自己每天都写 4000 字，达尔文在起草《物种起源》的时候，会每天早上 7 点准时起床散步，8 点半到10 点半拆书信，10 点半之后继续工作，然后就是散步和思考。这样的习惯能让大脑的神经细胞髓鞘化，让我们在熟悉的路径上越走越快。\n「双峰模式」适合有固定休息时间的人，在这种模式中，你可以把时间分成两块，一块完成浮浅工作，一块追求高强度，无干扰的专注。我在做教师的时候就采用这种模式工作。在下班之后，我会在 20:00~1:00 安排出 5 个小时的整块时间，进行技术学习和写作。在 25 岁的时候，我已经出版了《Flash MX互动教学课件制作实例教程》、《用多媒体学Visual Basic.NET 2003》等多本技术书籍，并参加编写了湖北省中小学信息技术地方教材《网络技术应用》，同时还持续维护自己的个人网站和博客。那段时间的「双峰模式」工作效率和产出都非常棒。\n「禁欲模式」很好理解，就是在工作的时候隔绝一切外界的联系。显然，这种模式不适合大多数人，但可以把这种模式作为其他工作模式的一个补充。例如上面提到的例子，我最终选择禁止企业微信「在菜单图标上显示未读信息数」，但保留了「在 Dock 图标上显示未读信息数」的功能。因为我的 Dock 是隐藏的，我可以自行选择查看未读信息的时机，而不是被信息绑架。这就是「禁欲模式」在「节奏模式」中的一种应用了。\n像经商一样执行 确定了工作模式之后，要像经商一样去给自己设定一个让你燃起欲望的结果，把所有的关注点都关注到这一个目标结果上，然后用足够的精力去完成这个目标。\n在设定具体目标的时候，要抓住引领性目标，而不是滞后性目标。\n「滞后性目标」仅描述结果，「引领性目标」则关注过程。\n例如我给 HR 团队制定的目标是「8 月招聘 12 人」。这就是一个滞后性目标。如果只关注具体人数，就无法在过程中改善行为策略。\nHR 团队收到这个目标之后，会将其分解为「引领性目标」，将最终目标分成多个阶段，并设置计分板，每周都在计分板上做简单的标记，记录当周的深度工作时间，每周回顾自己的计分板，分析没有完成深度工作的原因，随时调整自己的工作状态和工作方法。\n在整个的执行阶段，以 HR 团队的这个例子来看，有三个步骤：\n制订关注点：配合业务的高增长 设定目标：8 月招聘 12 人 执行：分解引领性目标，积分板回顾，调整工作状态和方法。 全文完 ","date":"2020-07-24","description":"","lastmod":"2020-07-24T06:28:36Z","slug":"deep-work","tags":["reading"],"title":"读书笔记：《深度工作》","url":"https://blog.zengrong.net/post/deep-work/"},{"categories":["impressions"],"content":"这是曾嵘的第 11 篇周读。\n今天说的有点儿杂。\n顽皮狗的加班文化 《最后的生还者 2》发售后的巨大争议牵扯出了顽皮狗的加班文化和人才流失问题。我读了几篇相关报道，发现「加班」这件事似乎是由开发者们自行推动的：\n比如《最后生还者》中，那个标志性的“长颈鹿场景”并不是一开始就存在的。很多游戏机制和设计可能理论上很美好，但是玩起来却体验极差。这种情况发生的时候，就需要开发者额外花费几个月的时间去重新制作和修改。同样地，你也很难拒绝那些在开发过程中想出来的好点子。所以开发人员往往需要花费更多的时间去完善游戏。\n这似乎变成了一个加班推动链条：完美游戏 + 自我管理 + 极高要求 = 无限制地加班。顽皮狗的公司文化总结起来有这么几点：\n1. 完美主义情节\n顽皮狗工作室多年以来建立的完美主义情结：不计代价地做出伟大的游戏。 在顽皮狗，开发团队不是被命令去加班的。这不需要有人命令，因为他们天生就是会那样工作。 2. 自治管理\n最难受的莫过于自己负责的部分在不知情的情况下被删除，这是沟通造成的问题。 顽皮狗工作室雇佣过几个制作人，来帮助规划日程并完成一些其他的任务，但工作室大的核心理念一直都是“自己做自己的制作人”，自己对自己的工作负责。 如果开发团队单纯地为了改变游戏而改变，也没有人能去制约。没人能告诉开发团队沟通的重要性。更没人去告诉他们不要通宵工作。 “这里有很棒的创作环境，但你就是不能回家。” 3. 高要求和负面反馈\n顽皮狗的另外一种公司文化，就是来自上级的反馈通常都是批评性质的，而如果什么反馈都没有，就意味着你做的不错。对很多新员工来说，刚开始工作的这段时间就好比是“盲人领着盲人走路”。 顽皮狗在过去很少雇佣没有经验员工。工作室的标准太高，导致新人很难一下就达到他们的标准；这样一来，他们就需要花更多的时间去返工、加班。 顽皮狗资深的设计师对所有人的要求都是一样的高，不管是入门级别的临时工还是公司骨干。一名开发者表示这种要求很离谱，给了很多新人无意义的压力和挫败感。 无限制加班会影响个人健康和情绪状态，最终会影响到产品的质量。或许《最后的生还者 2》的口碑争议，在发布之前就已经注定。\n什么是奋斗逼？ 谈到加班，就不可避免地要聊一聊「奋斗逼」这个概念。不知道什么是「奋斗逼」的可以先去补一下知乎的热门问题「奋斗逼到底做错了什么？」\n需要注意的是，不要把「奋斗逼」和「奋斗者」混为一谈， 奋斗者的目标永远是星辰大海，只有奋斗逼才成天想着压同事一头。\n群响刘老板有段话说的特别好：\n资本的剥削是真实存在的，人与人的差距是真实存在的，这是客观因素导致的，不以人的意志为转移。\n国家、家庭、教育、恋爱、子女、就业、城市，都时刻在影响你的阶层。\n认识到自己的阶层之后，大喊王侯将相宁有种乎，然后去做更多的尝试，一步一步地去和你不能够得着的人去平行喝茶，才是真汉子。\n《清酒之魂》 可并非所有的人都想做真汉子。农口尚彦当上杜氏（酒庄中的品控负责人）一年以后，终于发现了这个道理：\n我以为当了杜氏就是指挥别人像我自己一样干活。可刚过了一年，我就知道这样的方法是行不通的。无论哪个工种，都有为了自己有朝一日能成为杜氏而拼命努力的人，也有根本无所谓，没什么追求的人。我总是想当然地认为别人的想法都会自己一样。\n农口毫无疑问是一个有远大目标的「奋斗者」，然而他必须面对这样的问题：\n对于敷衍的工人所做的工作，很难判断出具体的问题到底是酒米不好，还是工人没有按照指令去做。查不出原因，就无法立刻做出调整。\n做游戏和酿酒一样，有着复杂的工序，需要多个工种配合，而且产出的产品也和酒一样，需要交给「人」来评判。让「种子用户」和「品酒官」来评判一款游戏和一种酒的好坏，我认为是以偏概全的。因为「酒是让人喝的，不是含在嘴里品的，只有酒真正划过喉咙饮下的哪一刻，才能完整体会到酒体清冽纯净的美妙」。同样的，游戏是让人玩的，少量玩家的意见有一定的参考性，但不能代替大数据测试。\n怎么才能保证游戏中的「匠人精神」？农口的办法就是「我自己来吧」！游戏人的方法应该是「把握核心，快速推进」。\n游戏开发中的核心方向，必须是由经验丰富的制作人「自己来」，具体的执行层面，则需要给值得培养的新人机会。\n项目的决策者即不能当「甩手掌柜」，也不能「事必亲躬」。管理的作用，是发现整个产品开发的过程中「人的问题」。「人人都有主动性」是一个好现象，但主动性到了前面顽皮狗的那种状态，会把项目拖进深渊。\n项目的决策者需要在整个前进的方向中，不断学习和调整，在流程上始终掌控这个项目，发现「有问题的人」，及时清除出队伍；发现「有问题的流程」，及时进行纠正；发现「有潜力的人」，及时给予信任。这一切，都是建立在「匠人精神」的基础之上，建立在决策者对项目和人员的理解之上。\n刚才有点儿偏题，扯远了。回到原题： 践行「匠人精神」就是「奋斗逼」么？\n当然不是。\n「匠人精神」是一种自我实现，是个人追求。在我的理想中，SAGITEAM 要做成一个追寻生活质量，活得有尊严 的团队。\n简单点说，就是干活是为了得到自己的认可，休闲的时候有志趣相投的玩伴。我们可以不使这个世界变得更好，但我们要努力做到不让自己身边的人过得更糟。\n这样才有意思嘛。\n相关参考 《最后生还者2》开发者谈加班：一边通宵，一边等着游戏翻车 奋斗逼到底做错了什么？ 996 不是福报，是真的会死人的哈 《清酒之魂》 全文完 ","date":"2020-07-18","description":"","lastmod":"2020-07-18T11:04:49Z","slug":"weekread202029","tags":["reading","weekread"],"title":"周读：202029 「努力」就是「奋斗逼」吗？","url":"https://blog.zengrong.net/post/weekread202029/"},{"categories":["impressions"],"content":"游记，不是旅游日记，而是 游戏札记。 聊一下近期的游戏行业相关话题，加一点不成熟的思考。\n今天早上被 2020游戏发行大困局 这篇文章刷屏了。文中提到的发行困局在 5 月份的这篇文章 2020年的游戏圈，“拿产品”到底有多难？ 一文中也有体现。文章观点很清晰，也列举了不少实例，但评论更精彩。\n精彩的评论区 评论区仿佛一个辩论场，除了讨论文章，也会时不时出现一些新的观点，打广告的，求曝光的，各方激战好不热闹。来看看站在 CP 的不满情绪：\n我留个言，我猜你不敢放出来：这篇文章标题有问题，应该是：2020年的游戏圈，想“白嫖拿产品”到底有多难\n有时候你会发现，只要有“嘴”，就可以是“所谓的发行”。这个比例还是太高。\n花了几十万做了一个产品，结果分成就10%，没版金，没预付，还要等半年账期，你让CP吃土？\n现在很多长尾吵着求着闹着要产品，真的产品给了吧，又没量，很多产品引入应该换个名字叫“产品（带有自有量）引入”，都希望有量的产品，哪那么多好事呀，我有自有量还接你干嘛。\n不爱游戏而做游戏的人太多了！\nCP 的自救：\n还不如做做超休闲，挣点广告钱\n在cp的立场想说一下，完全自研的游戏，做一半找投资机会被嫌内容少，做完的话哪还需要找机会…全部从0开始做，不知道推翻了多少系统和设计，没有那么简单…最终结果导致小cp的研发方向要么是成本低的休闲游戏，要么是中重度换皮游戏。活下来的全部都是做这些游戏的，也就导致这些游戏成了市场刚需，那我们完全自研中重度游戏的出路在哪里…只能随着市场去做休闲小游戏吗？2d武侠非换皮非金古背景cp，目标香港、台湾市场，寻找能坚持下去的机会\n优秀发行：\n感觉写的比较片面！下半年我们拿到5款产品了！\n看来还没到寒冬啊，还得再死一批才好。不热爱游戏而只想在这个行业里赚钱的人还是太多了。什么时候cp真成老大了才算好时候。目前来看大多数cp仍然只是工具人而已。\n写出这样评论的肯定是神仙：\n乐观点，凡事利弊共存，越难的时候或许又是另一种成功的开始\n这么有情怀和决心。去做3A，拯救国产单机游戏啊。个个都在做手游无非也是想着赚快钱。包括腾讯网易也在做换皮，快销品。说什么好好做游戏，不爱游戏离开游戏行业都是屁话。赚钱才是初心，只不过大家都没有骨气站着把钱挣。\n作者立场有点歪 站在发行角度，被中小cp坑的多如牛毛 游戏行业的难是进入门槛低，只要几台电脑上网就能搞，几个人能骗cp骗cp能骗渠道骗渠道反正搞黄只是他人买单\n悲天悯人：\n渠道不让发行赚钱 发行不让研发赚钱 中国式大爷渠道是限制发展的核心毒瘤\n我觉得还是分配问题 ，cp利润太薄弱了 不够支撑创新产品 没钱最为致命 找产品南 做产品更难 .一个主程一个服务端 那是真的贵 .当然发行也难.2020是淘汰出局的一年.\n个人觉得内容太片面了，立场战队很明显。把CP说的好像高高在上、不食人间烟火一样。但其实CP才是跪舔的那一个好吗？君不见CP老板跑断腿，只为一口活命粮。多少有情怀的CP倒在路上，历经多少磨难才拿出一款发行眼中“合格”的产品？ 希望发行与CP同气连枝，而不是互相伤害。CP转型自研自发背后的缘由难道仅仅是利益驱使吗？ 文章也说啦，现在还存活的CP已经凤毛麟角了，珍惜吧！遇到能够同舟共济的发行，CP们都会感激涕零！ 愿得一人心，白首不分离！\n分析世界大势：\n行业壁垒只会越来越高，而最终能做出高品质内容的团队，将引领游戏新世纪。\n游戏行业高利润自然的结果就是泥沙俱下、乌烟瘴气，这两年游戏行业政策也在不断趋于严格，也算是一直在赶公司和人离开，逐渐逐渐大概率行业的平均利润率也会回归正常，那时候自然会重新进入良性的发展阶段吧。\n需求决定存在，不太赞同买量能力可简单复制的论调，买量的玩法会变得越来越快，在资本投入之外，将来更核心的会是素材，或者说是传播学心理学这种靠人靠团队的不可定量的能力，创意无价。 这是机会。\n已退出游戏行业，国内做游戏只是为了流水，并不对品质负责，大厂都恬不知耻的换皮，小厂竞争压力大，更是无所不用其极，国产游戏崛起-难\n现在还有不搞自研的发行？ 现在还有只考虑把产品带出去的研发？ 什么年代了？还靠信息不对称来做事吗？\n“热爱游戏”才配做游戏是个伪命题，各位游戏人你们有没有其他行业混的好的同学朋友？他们热爱各自行业的比例有多高？用爱发电永远比不上金钱驱动，这个行业只要还能赚钱就不缺人才，就能扩大规模影响力赚更多的钱。很多付费能力强的玩家真的是因为喜爱游戏花钱的吗？不，首先是因为他们有很多钱可以消费。做游戏就是开饭店，菜好吃能吸引到人，服务好也能吸引到人，便宜大碗可能也能吸引到人，先找好自己的切入点比单纯喊“要热爱游戏”价值大多了，我觉得自己做不出好产品的人才会老把“你们都不热爱游戏所以做不出好产品”挂嘴上，好像热爱就能做出好游戏似的，这种复杂原因归一化的谬论本质还是懒得思考。\n正反方：\n买量让游戏变得功利，但是当买量这事变得标准化了之后，最终拼的还是产品的实力，赚快钱不能走远，修炼内功才是根本！感谢几大运营商整合了流量入口，消除了流量方面的信息不对称，玩家需要好游戏，而不是渣渣辉！\n从商业角度看，渣渣辉依然是玩家商家双满足的顶级优秀产品\n怎么样，是不是很欢乐？\n难是双向的。现在不但发行拿产品难，产品上架也更难了，大家肯定都知道，苹果爸爸对版号动真格了：\n8月1日起，App Store中国区无版号游戏将全部下架！ 没有版号，PS一个？这次苹果动真格的了！ 那么，面对版号暴击，小 CP 们应该怎么做呢？我结合 SAGITEAM 的经验瞎聊一下。\n买量的智慧 再次强调，下面说的纯属瞎聊和胡扯，看到的同学不要胡乱尝试。\n我在上一期 游记：超休闲/小游戏是否还有机会？ 中谈到了超休闲和小游戏的机会。CP 要掌握一些发行的基本技能，实现「自研自测」（详见 游戏茶馆对 SAGITEAM 的采访）：\n建立懂产品的运营团队，持续关注产品的运营数据。我们的运营负责人是从产品转岗过来，对产品有足够的深入的理解； 培养策划的「运营感」和「数据感」，要求制作人和策划能做到「半个运营」； 建立懂产品的投放团队。至少需要一名投放优化师和一名素材制作师。极端情况下，投放优化师可以让运营兼任，素材制作可以让项目美术兼任。投放优化师需要理解游戏产品和游戏运营。 但 CP 也不要把买量这件事想得过于简单。买量并不是一个可以简单复制的能力。要买到更低价格的量，需要对下面几点非常熟悉：\n各大广告平台的特点（Facebook 和 Admob 各有什么不同？我要买 1000 个量跑测试，应该怎么选择？） 研究素材。什么时候用图片素材，什么时候用视频素材，什么时候用真素材，什么时候用加素材，都要门儿清。 了解产品。产品到什么时候可以开始起量？什么时候可以买价格高的量？什么时候什么时候放开KPI？这都需要尝试。 1948年1月18日，毛泽东在为中共中央起草的决议草案《关于目前党的政策中的几个重要问题》中又说：“当着我们正确地指出在全体上，在战略上，应当轻视敌人的时候，却决不可在每一个局部上，在每一个具体问题上，也轻视敌人。”\n可是发行并不是 CP 的敌人。自我懈怠才是。\n作为 CP，对于发行的整体技能，要在战略上藐视它（认为自己也是可以学会的）；对于发行的具体技巧，要重视它（认为这是需要大量经验和技巧的）。\n我一直认为，买量的人必须懂游戏、玩游戏、要研究游戏和数据。买量操作的过程要配合产品的运营和功能开发一起进行。 这对买量团队是非常高的要求。如果没有和发行之间的相互信任，几乎不可能做到如此默契。所以，前期的买量测试，CP 完全可以自己做。\n但 CP 不要认为学会了买量就是真的会买量，就好像能做出产品不代表能做出好产品，一周做出一个不错的小游戏（做不出来才是不合格），就是一个优秀 CP 了？\n为啥别人买 3 毛，你买就是 3 块？为什么花同样的钱，别人的付费就是比你高？\n你要尽可能地去了解所有的事。\n但你不需要在真正合作的时候，所有的事情都自己做。你懂，不一定非要你做。但你不懂，你就不知道应该如何与优秀的合作方配合。\n游戏进入正常起量阶段时，可以启用代投，此时不能要求代投者对游戏有相同的理解。\n简单的说，最核心的事情（例如测试数据，调试产品，获得用户反馈）这些相关工作需要懂产品的人来做。边缘化的工作就不必这么高要求了。\n再说一次：并不是会买量就可以做发行了，这就好像并不是会编程就能做游戏了。「案子都写好了，就缺一个程序员了」这个心态，和「素材都做好了，就缺 100 万刀了」有啥区别吗？\n你懂得越多，你就更容易找到靠谱优秀的发行。你懂得越多，你就能具备更强的「掌控感」，除了游戏本身，你能掌控更多信息，这些信息会反哺你对游戏的理解。\nCP 活下来的锦囊 可以选择性考虑下面五个聚焦点：\n尽快申请版号。在游戏 DEMO 测试通过后就开始申请。 如果无法申请版号，可以考虑小游戏平台，但不要只关注一个渠道（例如死守微信），要了解目前主流的渠道（微信、手Q、OPPO、头条），关注他们的最新策略和动向，看能不能抓到一点新政策的红利。例如最近 VIVO 关于买量的政策，OPPO 也在频繁动作。 出海。申请海外帐号、4.3、帐号被调查、科学上网 这些事情当然很烦人，但聪明的 CP 同学，你觉得这些比 C++ 还难么？游戏在自己的账号下面它不香么？ 买量。如果人多，找 2 个专人负责。如果人少，找 1 个兼任做就行了，真的没有你想想的那么难。聪明的 CP 同学，你觉得 申请广告账号、做素材、花钱， 比 C++ 还难么？ 懂发行，不是说一定要做发行。选择和优秀的头部发行合作，不要浪费时间。你掌握的所有买量技能，都是谈判的筹码。如果你要自己买量，那么这些技能就是产品调优的推动力。 一定要利用这波动荡。限制版号，对 CP 来说是机遇也是挑战。这取决于你站在那个角度理解。玩家是愿意为好游戏买单的。CP 要积淀自己的核心能力。不要一味跟风。网赚火了就去做个网赚，小游戏看起来不错就去花一个月做 4 个，这样跟着跑很难积累起自己的核心能力。CP 的核心能力永远都是也只能是好产品。\n喧嚣，就让它去喧嚣吧。活着比什么都好。\n全文完 ","date":"2020-07-09","description":"","lastmod":"2020-07-09T08:50:48Z","slug":"gamenote20200707","tags":["gamenote","reading"],"title":"游记：面对苹果爸爸的版号暴击，2020 小 CP 怎么活？","url":"https://blog.zengrong.net/post/gamenote20200707/"},{"categories":["impressions"],"content":"周读，就是把每周我读过的部分有意思的内容记录下来。有时包含我的评论。这些内容来自我订阅的公众号，读过的书，或者在各新闻 App 读过的文章和报道。\n下面是 2020 年第 28 周的周读内容。\n想当个优秀的员工，光有才华是不够的 批判性思考之难 读书笔记：《自驱型成长》 想当个优秀的员工，光有才华是不够的 想当个优秀的员工，光有才华是不够的\n除非我们考虑到了一个人的情绪构成、偏好和性格，否则我们就不可能充分了解到这个人的整体能力。不管你多么聪明、多么博学、多么有经验，你能做到的事情和你平时做的事情之间是有差异的。\n招聘的时候，雇主往往更关注候选人的潜力，但候选人入职后的真正表现（平时表现）才更加重要。 高效的人会更苛刻地评估自己的表现，低效的人却认为他们为公司做出了重大的贡献。 从这个角度上说，自知之明似乎是才华的一个重要部分。\n多数人在入职超过六个月之后，就不会再尽自己最大的能力去工作。这半年的时间叫做「蜜月期」。要完全避免蜜月期现很难，可以从四个方面来应对： 个人工作不匹配、领导者能力欠佳、组织政治、个人（私人）情况。\n才华的展现主要取决于工作环境是否与员工的性格相匹配。组织心理学将这一现象称为「个人工作匹配度」：它衡量的是员工的态度、价值观、能力和性格与工作、职责和组织特征之间的匹配程度。 即使在入职的时候对候选人的评估完全正确，双方也难以完全准确评估候选人的工作职责和组织本身的文化是否匹配。组织往往一厢情愿地认为自己比实际情况更具包容性、更多样化、更具创新性，更有利于社会。候选人也必须加入组织后花上一段时间才能体会到组织的文化，并真正了解到自己的工作内容和要求。\n曾嵘：SAGITEAM 是一个风格极强的的团队，我也曾经有上文中一厢情愿的感觉。当我们增加了面试次数后，我们发现排在大多数候选人心中的雇主优势清单前列的并不是有风格的团队、有梦想的项目、有热情的工作。虽然候选人会把梦想挂在嘴上，但当遇到假想的困难或者需要承担责任的时候，少有候选人会眼前一亮热血沸腾，而是退而要求更稳定的生活，更高的薪资和福利水平。 用情怀吸引人才是耍流氓。只谈梦想也是耍流氓。 给够钱，让优秀的人才加入，看团队和人才是否适应，这是高效之道。\n糟糕的领导者会导致员工工作表现欠佳，还可能会导致有才华的明星员工离职。但曾嵘认为，主动沟通的员工能解决好糟糕的管理者的问题。如果无法找到更优秀的管理者，就应该通过培训和企业文化让员工知道，主动沟通是非常安全的，不要等到事情完全失控或者准备离职的时候再来沟通。\n你越有才华，你树立的敌人就会越多，特别是在工作文化有毒、政治氛围浓厚的组织中。 如果情况难以改变，最好的解决方法或许就是换个组织，或者至少换个单位。值得注意的是，虽然所有的组织都带有政治氛围，但是有些组织的政治氛围远比其他组织宽松。\n曾嵘：金玉良言啊。圈子不同，不必强融。有才华也不必让所有人都知道。在多个平行的机构中，表现出才华就意味着受到打压、排挤或者妒忌。一切应该以效率最高的方式来完成，表现才华若会影响效率，就不要表现。\n个人的家庭情况，身体健康等都会影响工作安排，此类影响出现的随机性很高，而且可能对工作有决定性的影响，如果一个重要岗位因为家庭原因离职，对初创企业会带来很大的打击。企业要注意不把所有的可能性放在一个员工身上，要安排人员冗余，分散风险。\n批判性思考之难 批判性思考之难：阻碍我们思考的8种认知偏差\n文中谈到了 8 种不同的认知偏差，这里只说 2 个吧：\n国王晚上做了一个梦，醒来之后很是担忧。于是找来了国内最好的两名解梦师。\n第一位解梦师听国王讲完梦境之后，说道：“陛下，您这个梦是预言之梦。它预示着您的亲人都会先您一步离世。”\n国王听后，大怒，命人将第一位解梦师重打一顿。\n随后国王召来了第二位解梦师，他听国王讲完梦境后，说道：“陛下，您这个梦是预言之梦。它预示着陛下您会比您的亲人都长寿，恭喜陛下。”\n国王听后，大喜，重重赏赐了第二位解梦师。\n由此来看，说话真的是一门艺术啊。\n框架效应 是说一个客观上相同的问题的不同描述导致人们做出了不同的决策判断。一般来说人们更喜欢「获得」，厌恶「损失」。\n锚定效应 是指个体的判断是以一个初始值或者「锚」为依据的，然后再进行不充分的上下调整。\n曾嵘： 买东西还价的时候（尤其是买房这种大宗商品），不要受到原价的限制，应该直接根据市场调研和自我心理判断价格的初始值。给候选人定薪也一样，不要看原来的薪水，直接根据面试和笔试的情况判断薪水。做判断或者做选择是不要局限于别人给出的选择，要自己创造出选择。\n读书笔记：《自驱型成长》 这本书虽然是讲述培养孩子自驱力的，但曾嵘觉得其中提到的思路在企业管理上也可以得到不错的应用。注意，本篇笔记中的主要谈到的是曾嵘的想法而原书表述的思想。\n书中有一句很深刻的话：「你是孩子的顾问，而不是老板」。在一个团队中，如果期望团队成员能够自驱型成长，可以把这句话改一改：「你是员工的老板，同时也是顾问」。要形成自驱型的团队，核心要点是让 员工的个人目标与团队目标、公司目标达成一致或者逐渐趋同。 这需要转变老板心态为顾问心态。\n中国式家长的特点是在孩子的各个人生阶段设定终点，以「冲过终点」为目标。例如孩子学习阶段以「高考」为目标，成年阶段以「结婚」为目标，结婚之后以「抱孙」为目标。这种模型叫做「挑战-努力」模型。\n企业在「挑战-努力」模型中的不同阶段的目标也是不同的。企业初创阶段的目标是「生存」，现金流稳定后的目标是「发展」，形成完善的产品线之后的目标是「第二曲线」，规模壮大之后的目标是「社会责任」。企业目标和孩子的人生阶段所不同的是，它们可能没有明确的时间点，也不是线性的，企业的发展压力不像学习目标那样能够通过一个时间点来释放。\n人类大脑的压力反应系统会通过两个激素来影响人类的行为。一个是肾上腺素，一个是皮质醇。前者是激发潜力的因素，后者的主要作用是为人体在面对压力时提供能量。生活紧张、节食和高强度训练都会增加皮质醇。人体的自我保护机制可以通过释放胰岛素来抑制皮质醇的数量并帮助身体管理压力。由于上面提到的企业发展的特点，企业的压力持续存在，企业的皮质醇难以被抑制，这需要企业的管理者「发明」出自己的胰岛素。\n「压力-控制感」模型就是企业的胰岛素。「一管就死，一放就乱」是在儿童教育中经常发生的问题，打造一个「自驱型团队」也会碰到类似的问题。以 SAGITEAM 为例，我们在项目上对制作人足够放权，但这个「放权」不是简单说出「这事儿你说了算」就够了的。在我看来，要把「放权」做好，起码要做到下面三点：\n1. 解决制作人的后顾之忧。 后顾之忧的一个方面是制作人还没有准备好的事，例如有的制作人团队管理能力较弱，此时要帮助制作人规划好团队成员数量和配比；有的制作人对于产品的投放流程不够了解，就要快速组织投放团队和制作人的全面沟通；有的制作人对于目前主流市场和企业专注的市场方向理解不够清晰，就要迅速纠正。\n后顾之忧的另一个方面是要帮助制作人解决到他们无法承担后果。项目的资金投入、整体推广思路，都需要有经验的执行团队和项目制作人一起讨论决定，用「这事儿你说了算」把所有责任都扔给项目负责人，对项目和企业都是不负责任的行为。\n2. 在项目组有能力自己做出决定的事情上闭嘴。企业要分辨出那些并非最好选择，但并不离谱的决策。此类决策可以有选择性地让项目组自行决定。有些决策在短期内没有立竿见影的效果，但长期运行下去可能给整个项目带来惊喜。不要对项目抓得太死，该闭嘴的时候就要闭嘴。\n3. 项目组要对自己的选择承担后果。同样的，企业也要为对项目组的干预承担后果。如同 游记：Supercell 十年，中度休闲游戏增长-用信任代替控制 中提到的《海岛奇兵》项目的例子所展现的，后果也可能是惊喜。\n在帮助孩子控制压力的时候，家长要做的不是放弃干预，而是要管理好自己，当孩子做出不完美决定的时候不要焦虑，管理好自己的情绪。\n自驱力的建立需要 内在动机 ，建立内在动机需要 渴望机制。培养孩子的内在动机有三个重要的内在需求：「自主需求、胜任需求、归属需求」。\n在企业中建立起「归属需求」难度巨大。孩子和家长之间天生的血缘关系让孩子更容易理解到家长发自内心的关爱。企业要建立「归属需求」，可以学习教师的办法。孩子喜欢某门课程往往不是因为课程内容吸引自己，而是该课程的老师关心自己。企业对员工的关爱应该是发自内心，不计回报以及尽量一视同仁的。\n全文完 ","date":"2020-07-05","description":"","lastmod":"2020-07-05T12:30:56Z","slug":"weekread202028","tags":["reading","weekread","management"],"title":"周读：202028 有才华就够优秀吗？","url":"https://blog.zengrong.net/post/weekread202028/"},{"categories":["impressions"],"content":"游记，不是旅游日记，而是 游戏札记。 我把游戏相关的内容记录下来，加一点不成熟的想法。\n之前的游记和 周读 以摘录为主。本期游记开始，我将增加​原创内容的篇幅。本篇前半部分为个人感悟。​\n本期游记内容：\n曾嵘感悟：超休闲/小游戏是否还有机会？ 全球超休闲游戏数据报告：CPI、IPM、ARPU指标 超休闲/小游戏是否还有机会？ 上一篇游记 里面，AppsFlyer 的报告谈到重度游戏比重上升的趋势：疫情期间，全球三分之二的应用类型内购收入是增加的。中度游戏的量级在上升，总收入有波动；重度游戏保持平稳；休闲游戏处于上升状态。\n但超休闲游戏将如何发展呢？从 2019 年开始，就有人鼓吹「超休闲已死」，我们也能看到 VOODOO 等「老牌」厂商逐渐被 SayGame 等新秀超越。两年过去了，国内的小游戏市场也发生了翻天覆地的变化，似乎整个市场越来越难懂了。超休闲/小游戏是否还有机会呢？\n先说结论，我的答案是：有。\n对于超休闲游戏，有个极端的说法是这样的：\n超休闲游戏并不是游戏。他们是披着游戏外衣的广告播放器。\n但下文中的描述更加准确：\n超休闲游戏是一种商业模式，它将简单和富有吸引力的游戏机制与广告结合起来，获得广泛的非游戏玩家的喜爱。\n看看游戏玩家中的鄙视链： 主机 \u0026gt; PC \u0026gt; 手机 \u0026gt; 页游 \u0026gt; 传奇 \u0026gt; 小游戏，我们就知道，非游戏玩家的绝对数量是大幅超越典型游戏玩家的。鄙视链里的玩家，除了最后一个，都不是超休闲游戏的用户。正因为如此，超休闲游戏可以颠覆之前游戏行业建立起来的基本规则：\n不过分担心影响用户留存和体验，强插广告就为赚钱。 不在乎用户是否能再回来，广告看一个算一个。 强依赖买量和冲榜带来的自然量，不在意用户口碑。 不在意 7 天以上的留存。 没有用户社区。 充分交叉推广。 这些做法是完全围绕广告产业建立起来的，所以说超休闲游戏是广告播放器也不为过。仔细想想，不少做法是不是和电梯广告挺像的？\n超休闲游戏不能算内容产业，而应该归为广告产业。它提供的内容没有稀缺性，不会引起长期的情感共鸣，它们提供的是一种「用完即走，玩完就扔」的体验，它们存在的唯一目的就是为了占用碎片化的娱乐时间并提供一些迅速消退的快感。因为已经受过足够多的训练，这种快感很难被『小众』的游戏玩家感觉到，但容易被没有经过游戏洗礼的『大众』Get 到。\n超休闲游戏的高收益从何而来？\n疯狂投放视频广告会消耗大量的成本，但所有的商业模式都要求收益大于成本。我们能够发现，超休闲游戏中的广告大部分都是其他的超休闲游戏，这种自给自足的方式能够产生新的利润么？\n能。\n超休闲游戏的新利润主要来自于：\n广告主在不同时期投放产生的溢价行为（例如黑五和圣诞季，eCPM 和 CPI 都特别高）。 中度休闲和其他类型游戏产生的内购溢价。 超休闲游戏的内部跳转对用户注意力时间产生的叠加效用能贡献更多的广告展示和点击。 篇幅有限，不展开说，下面聊聊国内的小游戏平台。\n看不懂的小游戏平台\n最近看小游戏，我发现开发成本 50 万往上走的越来越多了，不少游戏估计是百万以上的成本。以小游戏平台的盈利能力，我不相信这些游戏能赚到钱。小游戏平台中的赚钱方式已经变换了好几个版本，但目前活跃的依然是流量为主，自有流量池的平台。发行与 CP 的分成模式变了，但核心玩法没变。小游戏平台的利润越来越薄，平台对流量作弊的打击越来越严格，但小游戏平台依然有机会。\n国内小游戏平台的机会主要来自于：\n新的平台（相对于微信小游戏）的流量红利。新平台总是有红利的。之前是微信、后来是手 Q、再后来是 OV、再后来是头条，接盘侠永远在后面。 各平台内部的激励计划：微信优选、手 Q 主推、OV 萤火虫……这可是拿到几十到几百万量的好机会。 自由拉新（裂变）能力的极致应用。与社交平台强绑定且无缝衔接，这是超休闲游戏所并不具备的特性。 视频内容的拉新（头条系起了个头，微信也在跟进）。 买量能力。 把上面的方式用好的团队，在小游戏平台上的机会很多。对于 CP 来说，小团队（10 人以下）可入坑，再大一点就不适合了。\n为什么大团队不适合呢？因为在没有抓住红利的前提下，小游戏的微薄利润很难养活团队。要保持正向的现金流，需要有买量变现能力以及充足的后备资金，你有了买量、运营团队，你就不是一个单纯的 CP 了。\n注意，小游戏和超休闲游戏并不是一回事，如果复制超休闲游戏的玩法来做小游戏，会死得很惨。\n从内容产业的角度来讲，我不认为小游戏/超休闲是一个对内容友好的市场。 这两个类别都非常「吃创意」，中国的开发者没办法也没有必要和海外那群人竞争。这并不是说中国的开发者没有创意或者只会抄袭，这是教育方式、成长文化、社会福利等等几十年的差异决定的。就像中国留学生数学无敌一样，在超休闲游戏创意上，我们的大部分开发者真的不适应。\n我们适合的是在一个自己认可的品类上深耕。\n但我也认为几十人的团队做独立游戏不适合。独立游戏需要时间，如果没有跑通盈利流程，团队很难坚持到最后。\n稀缺与红利\n既然小游戏赚钱是因为红利，推而广之，之前类似形式的 4399、17173 等渠道为什么活下来了？\n因为他们一直在 转型。我在 周读：应用商店十年神话终落幕 中提到过：\n买量方式兴起之后，硬核渠道也会逐渐成为次要渠道。优秀的游戏会优先使用 iOS 渠道买量分发，然后才会选择在其他渠道发行。这对于开发者来说，是好事。话语权将逐渐向优秀的内容倾斜。\n这类平台死得也不少，只是市场上活下来的被我们看到了而已，这是幸存者偏差。它们在不断转型，从而吃到了新的红利。\n而 CP 的内容红利，永远都不会消失。\n前几天读了一篇媒体圈的文章，有些自媒体注定是要被淘汰的，感觉其中涉及「稀缺内容」的说法和游戏圈也挺相似的，借用一下：\n第一种，抓住渠道红利的内容。比如说最近一些比较火的视频内容，走红的原因更多在于抓住了视频渠道红利，抢先把图文类内容进行了视频化，从而获得流量。但问题在于，仅仅靠抓住红利并不足以构建内容竞争力，随着新团队的进场，这种渠道红利将会被抹平。\n第二种，抓住认知红利的内容。比如说各种运营培训类的内容，走红的原因更多在于部分团队拥有某些运营方法的认知红利，率先通过内容的方式进行呈现，从而获得关注。但问题在于，这种认知红利难以保持，且几乎没有人能够始终获得认知红利，优势终将消失。\n第三种，资料整合类的内容。资料整合类的内容能够帮助读者节约时间，我们常见的各种“简史类文章”、“盘点类文章”都是如此，尽管这类内容颇受欢迎，但问题在于，这类内容几乎没有独特的竞争力，也就没有了读者的忠诚度。\n追求稀缺的『爆款』需要时间积累，更需要运气。但抓住『认知红利』和『渠道红利』，是可以通过经验做到的。\n在追求『稀缺内容』的道路上，孤独是常态。\n「天下熙熙，皆为利来；天下攘攘，皆为利往。」屏蔽熙攘，找到适合自己的路，看清方向，等待下一个红利的到来。\n创造出「稀缺内容」，就是创造出了红利。\n追风口的人，追到了才是风口。做内容的团队，做出了就是红利。\n全球超休闲游戏数据报告 全球超休闲游戏数据报告：CPI、IPM、ARPU指标\n到 2022 年，手机游戏有望产生高达 954 亿美元的收入，占 1960 亿美元全球游戏市场总价值的 41%。\n超休闲游戏其实是一种商业模式，将最佳广告体验与最具吸引力的游戏机制巧妙结合起来，博得最广泛的玩家青睐。\n“超休闲类应用无疑改写了应用营销的剧本，其崛起的背后，是自动化在提供推力，自动化能实时解答对于营销人员最重要的问题：哪些办法有效，哪些是徒劳？”\n下面谈到的增长率，均指从 2019Q4 到 2020Q1 的增长率。\n新冠疫情的影响\n2019年12月至2020年3月间，全球安装量翻了一倍还多(增长了103%) 。最大的增幅发生在中国—4个月间(12月至3月)安装量翻 了3.5倍。\n2019年10月，来自付费营销推广的应用安装量占80%，2020年3月下降至 59%，降幅达到26%。相反，自然安装成了赢家，这说明隔离在家的人们更愿意去浏览和尝试各种游戏。\n在新冠疫情的影响下，大量用户首次尝试超休闲类应用。这一现象可能会下压关键数据，导致生命周期价值 (LTV) 的计算出现显著波动。营销人员要想保证自己的计算模型干净准确，就必须与之前的应用表现作比较，将自然来源的新用户和额外安装划分出来。\n不同地区的 CPI\nAPAC（亚太地区）的成本环比从 2019Q4 的 $0.31 下降到 2020Q1 的 $0.20，EMEA（欧非中东）则为 $0.19 。全球范围在 2020Q1 的平均值为 $0.17。这可能是由于拉美地区（巴西、墨西哥）的 CPI 大跌导致。\n不同地区的 IPM\n所有地区的 IPM（千次展示安装）均有所增长，美国市场的平均值为 30，ECMA 平均值为 27。全球平均为 26，全球的数据增长了 19.2%。\nCTR/CVR/IR\nCTR（广告展示点击率）APAC 24% 显著高于其他地区（广告效果最好），EMEA 的 CTR 最低。美国地区的 CTR 只有 18%，但 IR（广告点击安装率）达到了 17%。EMEA 的 IR 也是 17%。这些数据有点意思，APAC 虽然喜欢广告，但行动上却不安装。美国和 EMEA 用户虽然不欢迎广告，但看了广告之后更愿意安装。\n不同类型游戏的CPI/CVR\n指标 动作 冒险 休闲 超休闲 角色扮演 安卓全球CPI $0.25 $0.53 $0.40 $0.16 $1.89 安卓全球CVR 2.18% 1.20% 1.18% 3.34% 0.58% iOS全球CPI $1.14 $1.65 N/A $0.46 $0.78 iOS全球CVR 1.21% 0.74% N/A 2.72% 1.66% 安装后表现\n超休闲用户的明天平均会话数量为 1.56，在线时长为 2分 39 秒。相比而言，其他游戏这两个数据为 2.47 次和 19 分 52 秒。\n超休闲游戏的用户粘度（日活除以月活）只有 11%，比其他游戏的平均数据低 2 倍多。这是超休闲游戏的设计初衷，超休闲游戏的商业模式早已考虑了游戏的高流失率，因此才有产品组合（矩阵）的出现。\n广告展示量、留存率和收入\n超休闲游戏的平均次留为 30.4%，但平均 7 留只有 8.7%。相比而言，中度游戏这两个值为 31.7% 和 13.7%。但两种游戏 30 留的数据都差不多：1%。\n平均每分钟低于广告量 次留 4.45 20-10% 3.4 30-20% 3 40-30% 2.3 50-40% 广告展示量和每月收入并没有什么明确的关系。比较极端的情况是在一分钟内展示超过游戏时长的广告。每分钟展示 4 次以上广告的超休闲游戏智能产出中等收入，每个月的收入上限为 35000 美元。如果把每分钟的广告展示量限制在 2-3 之间，每个月能增加 10% 的收入。每分钟低于 1 则广告的游戏很难获得超过 20000 每月的月收入，但 1-2 则广告的游戏有 640 款达到了 20000 每月的月收入，有 338 款游戏在展示 2-3 则广告的情况下达到了 20000 每月月收入。\n营销商不仅要打造数百种不同的素材，还要能测试这些素材的效果。如果无法实现 A/B 测试自动化，也就不可能获得有实用价值的结论。成功是多种因素共同作用的结果，并非一次优化就能达到。优秀的营销人员应该了解哪些素材平台与哪种推广活动和渠道搭配效果最佳，同时在不断的反复操作中快速更新素材\nARPU 与 LTV\n2020Q1 不同水平的 ARPU，前 10% 的游戏为 $0.3042，前 25% 的游戏为 $0.2345，中位数为 $0.1300，底部 25% 为 $0.0685。\n超休闲游戏营销经理需要将注意力集中在三个核心指标上：CPI、留存率和LTV。\n全文完 ","date":"2020-06-27","description":"","lastmod":"2020-06-27T01:59:38Z","slug":"gamenote20200627","tags":["gamenote","reading"],"title":"游记：超休闲/小游戏是否还有机会？","url":"https://blog.zengrong.net/post/gamenote20200627/"},{"categories":["impressions"],"content":"周读，就是把每周我读过的部分有意思的内容记录下来。有时包含我的评论。这些内容来自我订阅的公众号，读过的书，或者在各新闻 App 读过的文章和报道。\n找适合的人，找喜欢的人，多招聘年轻人，加速淘汰，持续招聘，追求人效，警惕规模，真心换真心。\n下面是 2020 年第 22 周的周读内容。\n读书笔记：《千亿个太阳》 读书笔记：《学校如何运转》 读书笔记：《奈飞文化手册》 小公司招聘和管理 95 后员工 行业的三个周期 领导的自我牺牲精神 读书笔记：《千亿个太阳》 关键词： 能量，核聚变\n恒星的三个特点：\n辐射出巨大的能量。 能够长时间稳定地输出能量。 在上面两个条件下，自身的质量没有改变太多。 地球会把来自太阳的能量大部分都辐射释放出去，地球接收到的能量和释放掉的能量大致是相等的。但接收到的能量会被地球存下来一点点。这一点点能量有的储存在了植物中，作为动物的能量来源；有的以煤炭或者石油的方式存储下来。风能、水能、潮汐能都来自于太阳。\n整个地球表面每小时接收到的太阳的能量，相当于 200 万亿度电，也就是 1000 个三峡大坝一年的发电量。但这相对于太阳的全部能量来说是微不足道的。太阳一小时释放出的能量足够全世界用 4000 年，需要写下一个 24 位的数字。\n超新星爆发和黑洞的合并都会释放大量的能量，但那个时间很短暂。恒星可以在长达几亿年甚至几十亿年的时间跨度上一直稳定地输出能量。\n经过长时间的研究，天文学家逐渐理解了太阳的工作机制。这分为三个阶段：\n如果太阳是由煤炭和石油组成，以化学燃烧来提供能量的话，太阳大概可以维持 5000 年的时间，这显然是不可能的。 如果太阳的能量来自于彗星和小行星撞击的话，要达到太阳现在释放的能量程度，需要每隔 100 年就有一个地球大小的天体撞击太阳。把太阳系中所有的天体都计算进去，也只能让太阳维持 4 万多年。 核聚变反应才是太阳的能量来源。1 克氢原子在太阳内部 1500 万摄氏度，3000 多亿个大气压的条件下，聚合形成氦原子核，并释放出 6300 亿焦耳的能量。这相当于同重量煤炭燃烧释放出能量的 2000 万倍。 恒星一生的四个阶段：\n婴幼儿阶段：滚雪球 青壮年阶段：稳定释放能量，从发生核聚变开始到核聚变减弱 老年阶段：核聚变减弱，体积膨胀 死亡阶段：核聚变停止，内核收缩 恒星的诞生是一个引力主导的滚雪球的过程。这个过程可能持续几十万年，直到雪球把周围的气体和尘埃全部吸进去为止。在滚雪球的过程中，恒星的中心会越来越热，密度越来越高，压力越来越大。当温度、密度和压力增加到一个临界状态的时候，就会达到核聚变反应发生的条件，触发核聚变反应。\n核聚变一旦被点燃，就无法停止，直到核反应的原材料被消耗完才能结束。\n恒星是一个气体球，为什么气体不会逃散？为什么气体不会被自身的强大的引力压向内核？这是因为太阳不断向外以光的方式释放能量，这些能量叫做光压或者辐射压。光压有推动力，太阳对地球的光压大约产生 6 亿牛的推力，相当于 20 枚长征九号火箭的推力。光压抵消了太阳向内的引力，让这个巨大的气体球保持稳定。\n恒星一生的大部分时间都会保持稳定状态，直到核聚变燃亮消耗殆尽，恒星的核心就只剩下氦原子，不能产生足够的光压。恒星的核心气体球会迅速收缩变小。为了寻找新的平衡点，恒星的外壳会迅速膨胀到之前的几百倍，以这种增加面积的方式来继续释放能量，和内部的引力取得新的平衡。由于温度变低，这样的恒星看起来是橙红色的，这就是红巨星，也是恒星生命临终前的状态。\n一旦恒星内部的核聚变反应彻底停止，恒星向内的引力会重新成为主导的力量，恒星会进一步收缩，压缩成密度很大的结构。根据恒星的质量不同，会形成白矮星、中子星或者黑洞。\n恒星是一种无法直接进行观测，寿命超出人类生命极限的物体。描述恒星的参数有温度、质量、半径、平均密度、年龄、运动速度、颜色等等，这么复杂和难以接触的系统如何研究呢？天文学家采用了下面的方法：\n寻找参照物。恒星的参照物就是太阳。 把参数分成两类，第一类能靠望远镜直接观测；第二类则可以在第一类参数的基础上结合物理理论推断的得到。 第一类参数需要细致、准确的观测；第二类参数需要严密的逻辑和成熟的思维模型。 形成完备底层数据库，作为分析基础，一步步拓展对恒星的认知。 读书笔记：《学校如何运转》 《学校如何运转》\n读书笔记：《奈飞文化手册》 《奈飞文化手册》\n小公司招聘和管理 95 后员工 小公司招聘和管理 95 后的 10 个技巧\n提高招聘自信。找到最合适的人而非最好的人。庙小就不要供佛祖。不要妄自菲薄，要有严肃的反馈和 Offer 流程。 重要岗位招聘喜欢的人，保证稳定性。创始人的性格就是公司的性格。在第一个月需要有人照料，没有懵逼。 基础业务人员招聘 95 后。90 后还到小公司应聘基础人员，几乎等同于业务能力很差。 合伙人是以前囤的。 加速淘汰，勇敢开除，持续招聘。 创始人把 30% 的时间放在招聘上，开拓多个渠道，扎实做好招聘渠道流量漏斗管理。 创始人在一线，明确完整流程，精确复制成功经验。 真心换真心。 追求人效，警惕规模。 建立知识库、资产清单，掌控财权和人事权。 行业的三个周期 如何让企业死得慢一点？\n无论上市公司还是非上市公司，企业总是会死的。\n当行业遭遇价值转移的时候，头部企业也无法幸免。技术的非连续性之就是一种价值转移。诺基亚被 iPhone 打败，燃油车积累上百年的引擎优势被电动车瞬间超过，柯达胶卷在被数码相机踢出市场，数码相机又被智能手机踢出市场。「能打败微信的，一定不是下一个微信」。\n任何行业都会有三个周期，风口红利期，技术红利期，管理红利期。\n第一个赚钱的进入了风口红利期，很快蓝海变成红海，技术差异化造就了技术红利。大家的技术都上来之后，就进入了管理红利期。别人花三天干的事情，你两天就干完了，别人花 10 块钱做的事情，你用 8 块钱就做成了，这就是管理红利期。管理红利期的特征是：双巨头或者三巨头垄断市场 50%以上的利润。\n用成本和效率去竞争。第一条曲线和第二条曲线之间的断点，一定要用创新跨过去。\n领导的自我牺牲精神 没点自我牺牲的精神，好意思当领导吗？\n当领导者表现出自我牺牲的行为时，我们会认为他们具有更高的道德感，是一个更好的榜样。 疫情期间的领导者所做的有效措施：做出自我牺牲；有目的地付出；即使困难重重也保持高度透明。\n没有人喜欢那些自己做在办公室里，却把团队推到前线的领导者。如果你要求你的员工做出牺牲，你也需要做出牺牲。如果你希望你的团队安全审慎地行事，你作为一个领导者也应该安全行事。如果你希望你的团队在战壕里，你也应该和他们一起战斗。与员工一起牺牲可能意味着他们将在危机期间和危机后更加坚定地留在公司。\n但是，无论给予什么，重要的是将牺牲与愿景、使命和组织的价值观联系起来，以便人们能够理解你为什么要这样做。如果这个目的不明确，可能会产生反作用。我们是一群经常持怀疑态度的人，当领导者做出自我牺牲时，我们会试图推断为什么领导者会这样做，或者关注他们没有做什么（比如领导者是否保留了奖金）。\n84% 的员工认为，组织在提高透明度方面做得不够。这对组织来说是有代价的。那些透明度高的组织比那些不透明的组织有更高的生产力、创新能力和员工保留率。相比之下，增加透明度实际上可以建立起信任。\n全文完 ","date":"2020-06-01","description":"","lastmod":"2020-06-01T00:52:01Z","slug":"weekread202022","tags":["reading","weekread","management"],"title":"周读：202022 作为领导的觉悟","url":"https://blog.zengrong.net/post/weekread202022/"},{"categories":["impressions"],"content":"书籍信息 奈飞文化手册 作者：帕蒂·麦考德 原作名： Powerful: Building a Culture of Freedom and Responsibility 出版社: 浙江教育出版社 出版日期：2018-10 关于作者 帕蒂·麦考德是流媒体巨头奈飞的前CHO（人力资源总监），担任该职位14年，奈飞文化平台的创建者之一。她是被誉为“硅谷重要文件”的《奈飞文化集》PPT的主要撰写者。\n读书感受 这本书非常有名，去年我就接触过书中的许多概念。被大家提到最多的就是那句著名的宣言：「我们这只招成年人」。某天我在外办事的间歇，用手机把它读完了，由于环境原因不少感想没有记录下来，所以我在写读书笔记的时候又读了一遍。\n这本书促进我对企业文化进行了更深入的思考。让我感到惊讶的是，我在工作中也形成的关于企业管理方面的部分观点似乎和书中提到的很一致。我感觉自己的这些懵懵懂懂，还没有形成系列方法的观点，被另一个人以条理清晰的方法梳理了出来，同时仔细回答了这些观点形成的原因。我有一种豁然开朗的感觉，很多之前想不明白的事情都有了答案。\n答案不等于方法。思路不等于实践。要推行优秀的理念，需要优秀的团队。不同的社会有不同的特性，要形成优秀的团队文化，还有更长的路要走。\n「尽信书不如无书」。书中的很多方法是不能直接拿来用的。对于公司文化，必须考虑公司的地理位置，社会特点，人才数量和民族文化。但无论如何，只要你对企业文化和管理有所思考和经验，在阅读此书时都会在不同角度得到足够的碰撞和共鸣。\n员工和 HR 的权利 不断提醒你自己，员工都是有权力的。你的工作不是要交给他们权力，而是要欣赏他们的权力，并将之从繁文缛节中解放出来。\n作为管理者，应该希望所有的员工都能来激烈地挑战自己，要让员工从踏入公司大门第一天起，就提醒他们拥有权利，为他们创造各种条件来行使权力。\n管理者的本职是建立伟大的团队，按时完成那些让人觉得不可思议的工作。\n要确保 HR 了解公司的业务运作原理，要让他们成为公司的业务人员。\n奈飞的核心原则 我们要求开放、清晰和持续地沟通工作任务以及面临的挑战，这个要求不是针对某个管理者的团队的，而是针对整个公司的。 我们要求大家做到绝对坦诚：同事之间能够及时地据实以告，最理想的方式是当面沟通。 我们要求大家都有充分的、以事实为依据的观点，并激烈辩论和严格检验这些观点。 我们要求大家做出的任何举动，出发点都是以对客户和公司最有利为基本点的，而不是试图证明自己正确。 我们要求人力资源管理者在团队建立方面发挥主导作用，立足未来，确保团队中每个岗位上都有技能匹配的高绩效者。 我们要求所有管理者，从最顶层的高管团队开始，能够在上述行为方面起到示范作用，以此向各自团队的所有人展示应该如何接纳这些行为。 奈飞的文化准则1：我们只招成年人 HIRE, REWARD, AND TOLERATE ONLY FULLY-FORMED ADULTS 只雇用、奖励和容忍完全成熟的成年人 如何做：\n只雇用、奖励和容忍完全成熟的成年人。 让员工加入到让他信任和钦佩的同事团队中。 打造尽可能简洁的工作流程和强大的纪律文化。 不要让规章和制度限制了高绩效者。 成年人最渴望的奖励是成功。 每个人都渴望与高绩效者合作。人们最希望得到的东西，就是加入到他们信任和钦佩的团队中，专注完成一项伟大的任务。\n过去十年最成功的的公司都采用协作和有机的工作方式。这些团队的目标和分配时间与资源的方式，以及它们所专注的问题与解决这些问题的方法，都在不断适应商业变化和客户需求。它们就像不断成长和变化的有机体，没有僵化的结构，也没有受制于既定的目标、人员或预算。他们保持着尽可能简洁的工作流程和强大的纪律文化。 简洁的流程和强大的文化远比发展速度重要。\n管理者能做的最好的事情，就是只招聘哪些高绩效的员工来和现有的员工一起工作。优秀的同事、清晰的目标和明确的结果，远比免费食堂、公司健身房或者股票期权更有吸引力。\n我们都喜欢搞 A/B 测试，把产品中没用的东西去掉，这个原则也同样可以用来管人。奈飞做了这样几件让传统公司大跌眼镜的事情：\n奈飞取消了休假制度，让员工自己决定何时可以休假； 奈飞取消了报销政策和差旅政策，让员工运用自己的判断来决定如何花公司的钱； 奈飞挑战了传统的招聘方法，在公司内部建立了一家猎头机构，培养内部到招聘能力； 奈飞取消了年度计划制订，把更多的时间拿来做季度计划。 奈飞创建了一种“自由与责任的文化”，这种文化把高绩效的员工从各种审批和监督的限制中解放出来，给最好的、最具执行力的创作者更多的自由空间，让他们全面实现自己的理想。\n曾嵘：这就像软件工程中的“敏捷开发”，要敏捷，需要员工有极强的个人能力、责任感、热情。既然大家都是成年人，就不需要有太多的监控和审批。奈飞的这个策略对于招聘的要求相当高，如果员工达不到“成年人”的要求，这种自由化的管理会成为一种灾难。\n奈飞的文化准则2：要让每个人都理解公司业务 HIGH PERFORMANCE PEOPLE WILL DO BETTER WORK IF THEY UNDERSTAND THE BUSINESS 如果能够很好地理解公司的业务，高绩效者就能够更好地工作 如何做：\n建立新员工大学，保持沟通的强节奏。 双向沟通，为员工提供向所有管理者提问的机会。 让每一位员工了解，他为客户带来的体验是如何直接影响公司利润的。 如果只选择一门课程面向公司全员开授，请选择公司业务运作和客户服务的基本知识。 最好的福利，是让员工有机会去更好地了解业务和客户。 管理者越是花更多的时间去详尽、透彻地沟通亟待完成的工作任务、面临的挑战以及竞争环境，那些政策、审批和激励措施就越不重要。\n作者在 SUN 工作时，公司里有370名人力资源专员。这些人基本上都与业务脱节，无法告诉别人公司是做什么的。主要工作是搞一些庆祝活动。\n曾嵘：脱离业务的人力资源，就是浪费资源。人力资源必须深入理解业务模式，了解公司如何赚钱。只有这样，HR 才能帮助公司招到合适的人，并能正确地培训和影响这些人。\n员工需要以高层管理者的视角看事物，以便感受到自己与所有层级、所有部门都必须解决的问题有真正的联系，这样公司才能发现每个环节上的问题和机会，并采取有效行动。\n双向沟通和好奇文化至关重要。员工必须能够提出问题、批评和其他意见。在理想的的情况下，员工应该可以对上至 CEO 在内的所有管理者提问。员工的无知是管理者的失职。\n有些管理者会说：我试着和员工解释过了，但是他们太笨了，听不懂。管理者应该记住一条规则：就像跟你妈妈解释一件事情一样去解释给员工听。最好不要去臆想员工很笨，而是要考虑：如果员工做了愚蠢的事情，要么是未被告知相关的信息，要么是被告知了错误的信息。\n曾嵘： 人类的知识是高度组块化的，管理者和员工掌握的信息量和前置知识是不对等的。在解释问题的时候必须保证接收知识的人有组块密码，否则他就无法理解。提供密码的责任在管理者，而非员工。\n78%的消费者会因为一次糟糕的客服体验而无法完成购买或其他交易，美国企业每年因此受到的损失总计高达620亿美元。研究也显示，一次糟糕的客服体验通过口耳相传造成的影响是一次良好的客服体验的影响的两倍。任何一家拥有客服部门的公司如果希望该团队高度敬业，第一步就是教客服人员阅读公司的损益表。\n如果你不把信息告诉员工，那么他们就很有可能会从其他地方获得这些信息。如果你不告诉他们公司的业务表现如何、战略是什么、面临的挑战是什么以及业绩如何，他们就会从别处获得这类信息，要么从同样信息不足的同事那里，要么从网上，而网络可是最喜欢传播末日谣言或添油加醋的阴谋论的地方。\n如何判断公司在信息透明和沟通方面做得足够好了？可以在休息室或电梯里随便叫住一名员工，任何一个级别的都行，然后问他公司在未来6个月里要做的最重要的5件事是什么。该员工应该能够飞快地把一二三四五都答出来，而且会使用你在员工沟通中用过的字眼。如果他们真的厉害，还能以同样的顺序说出来。假如他们说不出来，就说明沟通的节奏还不够强。\n奈飞的文化准则3：绝对坦承，才能获得真正高效的反馈 RADICAL HONESTY HELPS PEOPLE TO GROW 绝对坦诚，帮助人们成长 如何做：\n人前人后要做到言行一致。 公开批评，面对面沟通是解决问题的最有效方法。 只有当管理者能够坦承错误时，员工才能够畅所欲言。 公司要有一套透明的反馈系统。 人力资源部门副总裁会把我叫到他的办公室，大声质问：“你是不是开工程师的玩笑了？”我说：“是的，不过，有没有搞错！他们抱怨浴缸里的水不够热，毛巾不够软，还有游泳池里的水太凉了。”副总裁就会责备我：“工程师是我们最重要的资源，你必须给予他们特殊对待！”\n曾嵘： 哈哈哈，这些人是来公司洗澡的么？我之前也看到过这样的意见回复，抱怨公司零食种类不够多，或者希望晚餐里面有自己爱吃的类型。这真的是搞错了重点。\n奈飞的文化支柱之一是如果对某人有意见，或者对某人的工作方式有意见，他们就应该和当事人开诚布公地沟通，最好是当面沟通。不应该在背后批评别人。开诚布公可以帮助员工成长，还能消除员工藏在心里的意见和分歧。\n不给严格反馈，会给管理者带来不必要的压力，他们不得不掩盖事实并欺骗员工，进而导致员工丧失做出改变的机会。\n给予反馈要针对行为，而不是给一个人笼统定性。要有具体的表述。例如不能说“你不够专心”，“你做的不错，但是还不够”。而应该说：“我能看到你的工作非常努力，我也很欣赏这一点。但是，我也注意到，你在某些事情上花了太多时间，而这些时间本应花在更重要的事情上。”。大部分人都希望有机会可以更好地了解自己的行为方式以及别人会怎么看待自己，只要我们表达的预期不是充满恶意或者盛气凌人的就行。\n奈飞会在团队会议上做一个名叫“开始、停止和继续”的练习。在这个练习中，每个人都要告诉一名同事一件他应该开始做的事、一件他应该停止做的事，以及一件他做得非常好且应该继续保持的事。奈飞给团队设立了明确标准：禁止在背后议论别人，或者是在同事面前抱怨其他同事，除非事件涉及伦理道德等需要保密处理的问题。奈飞建立了一个反馈系统，每年向公司全员发送一次“开始、停止和继续”反馈。一开始这个系统是匿名的，后来遭到员工的一致反对，系统变成了公开透明的。\n对于公司面临的挑战，奈飞也做到了绝对坦承。奈飞向全员分享公司早期遇到的困难，清楚阐述时间期限、业绩指标以及该如何实现目标。管理者以为分享业务所遇到的麻烦会加剧员工的焦虑感，但其实更让人焦虑的是对信息一无所知。向员工隐瞒真相或半真半假只会引来他们的鄙视。信任是建立在坦诚沟通的基础之上的，当员工听到半真半假的话时，就会开始冷嘲热讽。\n坦诚必须是双向的。员工应该了解，永远不要向上级主管隐瞒问题或信息。作为领导者，你应该身先士卒，用行动而不是用语言来表明你希望员工可以畅所欲言，他们可以直接告诉你业务相关信息，哪怕是坏消息或是你不赞同的消息。否则，大部分人永远都无法真正向你敞开心扉。德勤有限公司（Deloitte）发布的一项研究结果表明，来自众多行业领域的70%的员工承认，他们在可能危及自己绩效的问题上会保持沉默。\n传统观点认为，如果允许人们匿名，他们就会表现得更坦诚。作者认为情况并非如此。坦诚的人会坦诚地对待任何事情。匿名反馈最大的问题就在于它传递出这样一个信息：人们只有在对方不知道自己是谁的时候才是最坦诚的。\n奈飞的文化准则4：只有事实才能捍卫观点 PEOPLE'S OPINIONS SHOULD ALWAYS BE FACT BASED 人们的意见，应始终以事实为依据 如何做：\n鼓励以事实为依据的公开辩论。 不过分依赖数据，但会使用来自数据分析的洞察对团队决策进行补充。 牢记基于事实≠真实，不断地对观点进行再次审视和讨论。 辩论陷入僵局时，尝试站在对方的立场上辩论，找到自己立场中的漏洞 在进行沟通时，提问的态度必须真诚，以真正感兴趣的态度来询问别人正在面临的问题，可以在双方之间建立起一座牢固的“理解之桥”。在提问题的时候，应该询问别人正在解决的问题的本质，而不是假设自己已经理解了这个问题。\n在工作中应该有自己的意见，坚持自己的立场，在大多数时候让自己意见正确。商业上的一个巨大危险是，有人因为自己强大的说服力赢得争论，而不是依靠观点本身。在奈飞，人们必须通过探求事实来完善自己的观点，并且以开放的心态去倾听他们那些并不认同，但以事实为依据的辩论。\n奈飞重视“事实驱动”，而不是“数据驱动”。近年出现了一些数据被神化的现象，仿佛数据本身就是答案，是最后的真相，认为数据构成了经营公司所需要的事实，这是一个危险的谬误。\n在奈飞的辩论非常激烈，因此制定了一个标准用于控制辩论的边界，那就是所有的辩论必须从本质上服务于业务和客户的需要。奈飞的辩论会叫做“客户科学会议”。在辩论会上，经常有人在本能的驱动下陷入为了争论而争论的局面之中，此时用“这样做对于客户来到底有什么好处？”这个问句，就能避免焦点的转移。\n最具有说服力而且基于事实的观点也有可能出错，“基于事实”并不等于“真实”。结论必须能够再次进行审视和讨论。\n奈飞会在辩论无法达成结论的时候，安排辩论双方在讲台上站在对方的观点进行辩论，这需要换位思考。奈飞还会在大型会议上把团队拆成三四人小组进行分别辩论，以免群体性思维，或者过度左右人的观点导致人们缄口不言。\n把辩论放在一群人面前，能够帮助人们在辩论中保持文明。高管们的分歧更可能是基层员工需要理解和权衡的重要参考内容。\n奈飞聘用和晋升人员时要求他们具备的核心素质之一，就是拥有良好的判断能力，从本质上讲就是能在复杂环境下做出正确决策、深挖问题的根源，以及拥有战略性思维并可以清晰地表达出来的能力。\n奈飞的文化准则5：现在就开始组建你未来需要的团队 DON'T ASSUME THAT CURRENT EMPLOYEES WILL BE ABLE TO GROW INTO THE RESPONSIBILITIES OF THE FUTURE 不要以为你现在的员工能够成长到承担将来的责任 如何做：\n面向未来去思考你需要什么样的团队。 站在6个月后的未来审视你现在的团队，了解团队对即将到来的变化是否已准备就绪。 让每个人都理解团队需要持续“进化”。 有些人永远无法成长为未来组织中的高绩效者，主动让他们离开。 持续不断地搜寻人才，招入谁和解聘谁的决定必须完全建立在团队绩效的基础上。 「我们部门需要增加 15 个人，否则工作无法完成」。「你确定你不是只需要 3 个人，但却想申请 15 个人？你可以付给这些员工双倍的薪水，因为他们有双倍的经验，而且绩效会更好」。\n业务领导者应该经常对自己提一个重要问题：「我们是否因为现在拥有的团队不是我们应该拥有的团队而受到了限制」？\n要做到提升绩效，可以尝试这个方法：写下团队今天没有完成但 6 个月之后可以完成的事情，在脑子里想象你走在公司里，环顾四周，看到了这支优秀的团队在 6 个月之后做到的结果，思考 6 个月后的团队和今天的团队做的有哪些不一样。思考下面这些问题：\n会议更多了还是更少了？ 大家是否在大声辩论？ 大家的决策速度更快了吗？ 谁在做决定？谁没有做决定？ 更多人埋头默默工作，还是有更多人组成小组在白板前激烈讨论？ 跨部门合作是不是更多了？ 员工有没有通过更多的合作去解决问题？ 如果公司业务明年要扩大十倍，但你的团队只能循序增长，那么你应该从未来规划出发，建立一支理想的团队：确认要解决的问题、确认解决问题的时间时限、确认能够成功解决这些问题的人选、确认要解决这些问题他们会怎么做。然后问自己：「我需要做哪些准备？我需要招什么人样的人？」\n公司是一个团队，不是一个家庭。伟大的团队总在寻找新选手并精挑细选。招人和解聘的决定必须完全建立在团队绩效的基础上，目的是确保公司成功。管理者的最佳选择包括招入新的高绩效者，这意味着现有团队成员不得不从公司离开。\n究竟是从公司内部提拔还是从外部招聘一名高绩效者，可以看这项有待完成的工作是否需要内部人员所不具备的专业技能，或者我们自己是否处于这个领域的创新最前沿。\n管理者应该确保公司能产出好的产品，可以及时的服务客户。管理者并不亏欠员工一个机会让其从事自己没有准备好也没有天赋完成的工作；管理者也不亏欠员工一个新设的岗位来奖励员工付出；管理者也不必为了员工而在那些可以让公司发展的人员变动上退缩。 员工的成长，只能由自己负责。\n晋升员工并指导他们扮演新角色，对于团队领导者来说是一件相当有满足感的事情，对团队绩效也是好事。但对整体绩效来说，这通常不是最理想的方式。管理者不应该期望自己成为员工的职业规划者。在今天快速发展的商业环境中，试图扮演这种角色并沉浸其中，对公司来说是很危险的。\n奈飞在面试时会直截了当地告知应聘者，这里不是一家职业生涯管理公司，员工应该自己管理自己的职业发展。奈飞也会建议员工经常去其他公司面试，让他们自己判断市场上的机会，让奈飞更好地给员工支付薪酬。 建立更具流动性的团队带来的好处是双向的。\n作者认为，对于今天职场人士的最佳建议就是： 保持灵活，不断学习新技能，不断考虑新机会，经常接受挑战，保持工作的新鲜感和延展性。\n在开发产品和市场早期所需的员工，与企业发展壮大阶段所需的员工是完全不同的。初创阶段，创业者需要聪明的人，这些人努力工作并坚信公司的愿景可以实现。创业早期会犯各种错误，还要疯狂工作、努力尝试、突破极限，直到推出一个可行的、能被市场接受的产品。答案是未知的，大部分工作是即兴的。但当公司开始快速发展，面临的问题就不是试错能够解决的，而是需要大量经验。面对规模和复杂性带来的问题，创始员工可以通过提升自己的技能来处理，但很多员工无法做到。\n奈飞的文化准则6：员工与岗位的关系，不是匹配而是高度匹配 NOT EVERY POSITION NEEDS TO BE FILLED WITH EINSTEIN, BUT THEY NEED TO BE VERY GOOD AT WHAT THEY DO 不是每个岗位都需要爱因斯坦，但每个岗位都需要最适合的员工 如何做：\n用人经理是首席招聘人员，招聘高绩效者是他最重要的工作。 招聘优秀人才不是招聘“一流选手”，而是为岗位需求找到最佳匹配人员。 确保每一个对业务至关重要的岗位上，都有一位一流人才。 主动让那些已经很出色的人离开，以便给顶尖人才腾位置。 奈飞人才管理的三条基本原则：\n第一，招聘优秀人才以及决定员工是否应该从现有岗位离开的责任，主要在管理者身上。 第二，每一个岗位都要招聘一个高度匹配的人，而不仅仅是一个匹配的人。 第三，如果一个人的技能与岗位要求不再匹配，即便是非常优秀的人才，也要跟他说再见。 人才保留不是团队建设的目标。最好的员工总是在寻找有挑战的机会，你永远都不会知道他们何时决定离开，通常你也没有办法阻止他们。\n伟大的工作和福利无关。让员工感到幸福当然是一件很好的事情，但这个幸福感应该来源于员工在和优秀的人一起做伟大的事情，而不是因为公司提供了美味的午餐和漂亮的游泳池。\n在候选人接受奈飞的工作邀请之前，奈飞只会谈论他们的薪酬理念，但不会讨论具体的数字。如果候选人在第一轮面试就开始讨论薪水，要么是因为他在原来的岗位上薪水偏低；要么是薪水优厚担心你不会加薪；要么就是只对钱感兴趣，对工作没有激情。奈飞会让员工决定薪资中有多少期权，用部分期权去替换工资，而非在工资的基础上增加期权。奈飞不会把期权当作「金手铐」来使用。期权按月发放，可以在十年内行权。\n做好招聘工作就是做好完美的匹配，一家公司的一流选手可能是另一家公司的二流选手。文化契合并不是两个人在一起喝啤酒很开心，而是应该在工作上合作无间。\n「我的团队」，就是由我创建的足够好的团队，好到即使没有「我」，团队也能持续前行。\n当用人经理深入介入招聘工作的时候，将会促使所有招聘人员变得更加积极主动。奈飞要求任何招聘人员看到有陌生人在等待面试，必须过去和他说「嗨，我叫 XXX，您是要面试吗？您在等谁？让我来帮您安排一下。」作者的团队在执行这点要求上非常到位，以至于作者在有一次面试迟到时听到应聘者说：「已经有 6 个人和我聊过了。」\n候选人在评估公司，就像公司在评估候选人一样。\n招聘人员的目标是让每个参加面试的人在离开的时候都希望得到这份工作。要让应聘人员认为我们高效准时、问题合理、面试官聪明、对面试者尊重。即使面试者不是合适的人，有可能他的邻居是合适的人。\n最后的决定权是用人经理，招聘团队会提出权衡意见，一旦做出决定，不需要多层审批。招聘团队和用人经理直接决定薪酬、头衔和工作细节。速度和效率让奈飞可以找到同时面试多家公司的卓越候选人。\n建立团队是项目负责人和用人经理的工作，不是 HR 的。要雇佣聪明的人，就要雇佣聪明的 HR，HR 需要了解业务，在开业务会议的时候让 HR 参加，要让 HR 表现得像业务人员。\n奈飞的文化准则7：按照员工带来的价值付薪 WHETHER NETFLIX IS PROSPERING OR FLOUNDERING, WE PAY AT THE TOP OF THE MARKET 无论奈飞的业绩表现是好是坏，我们都按照市场顶级水平付薪 如何做：\n不要依赖于薪酬调研，因为薪酬调研的信息总是滞后的。 薪酬与年度绩效评估流程无关，只与员工的绩效相关。 如果不能针对所有的岗位支付市场最高水平的薪水，优先考虑对公司的业务增长最为重要的岗位。 资历相当的应聘者应该获得同样的薪酬，跟他们之前的薪酬、他们的性别都无关。 建立薪酬透明制度，让大家对薪酬有更好的判断。 不要让员工在不得不离开的时候才获得应得的薪水。按市场定薪不应该是将薪酬水平定在整体市场范围的某个固定水平上，而应该是在你要求的工作时间范围内，估算一个人给公司带来的整体市场价值。这样的最佳人选给业务带来的增长价值会大大超过薪水本身。\n应该识别出最有潜力提升公司业绩的岗位，尽可能招来能够招到的最好人才，给他们支付市场最高水平的薪水。你招到的人可能做两个人的工作，而且给公司带来更大的价值。80/20 原则在哪里都适用。最好的公司总在刻意制造不平等，这些企业把明星员工集中对业务至关重要的领域。而在其他公司，明星员工被分散安排在各个部门之中。\n在企业中，薪酬应该是透明的，公司要针对薪酬背后的理念和员工展开公开对话。让员工知道他自己薪酬的构成，这种对话应该非常小心，需要充分沟通这些数据被分享的原因，以及薪酬背后的依据。薪酬应该和绩效挂钩，但不应该和绩效考核流程挂钩。\n奈飞的文化准则8：离开时好好说再见 THE IDEAL COMPANY WOULD BE ONE THAT WAS A GREAT PLACE TO BE FROM 理想的公司，就是那种离开之后仍然觉得它很伟大的公司 如何做：\n1．如果员工的表现不够好，及时告诉他们要么纠正过来，要么去一家新公司。 2．不要把与工作不再匹配的员工归结为失败者。 3．不要给员工无法实现的承诺，这只会让他们感觉自己被背叛了。 4．积极地帮助离职员工找到新的好机会。\n我们都应该做好准备，时不时地换一份工作，不论是在公司内部还是去一家新公司，目的就是以我们喜爱的方式工作，做那些让我们充满激情的事情。同时，假如我们的表现不够好，也应该有人告诉我们，要么快速纠正过来，要么去一家新公司。\n作为管理者，应该和员工进行频繁的一对一会谈，这种做法比年度绩效评估更有效率，也更加人性化。如果无法找到可靠的数据来证明评估流程有助于实现一些重要的业务指标，就应该废除绩效评估。\n有些公司会将绩效不好的员工放入「绩效提升计划」中，然后和他们说再见。但员工无法胜任工作不是员工的问题，而是招聘的问题。在进行绩效提升计划的时候，应该用现实的眼光来看待提升有多大可能实现，要确保提升员工绩效是真正的目标，而不是让员工离开的理由和手段。\nHR 需要辅导管理者和所有员工，让他们知道必须公开讨论相互之间的问题。\n高绩效者通常会对自己团队的绩效表现感到有些沮丧，他们大都追求卓越的结果，实现这类目标通常伴随一些痛苦和一定程度的不满足。这种对成就的执着才是我们想要培养的，不要让员工认为只要工作努力，公司就会支持你的所有期望。\n员工评估算法：这个人喜欢做的、极其擅长的事情，是不是公司需要有人擅长做的事情？\n终身雇佣制已经消失，不要等到最后一刻才让员工离开。也不应该将员工分流到他们并不适合或者公司并不需要的岗位上。应该开诚布公支持员工找到新的机会。\n正如 Supercell CEO 在 Supercell 十周年 中所说：\n“生命是短暂的，享受这个过程。找到激励你的人，让你做的更好，让自己成为更好的人。我们不知道自己在做的是什么，所以专注于打造一个优秀的团队，和尽可能创造最佳的环境让团队成功”。\n曾嵘： 坚定地打造一个具有优秀文化和强大执行力的团队，奉行长期主义，让团队中的每个人都能有尊严地生活，快乐地工作，就是我这十年的目标。\n全文完 ","date":"2020-05-19","description":"","lastmod":"2020-05-19T03:12:10Z","slug":"powerful-building-a-culture-of-freedom-and-responsibility","tags":["reading","sagibookclub"],"title":"读书笔记：《奈飞文化手册》","url":"https://blog.zengrong.net/post/powerful-building-a-culture-of-freedom-and-responsibility/"},{"categories":["impressions"],"content":"游记，不是旅游日记，而是 游戏札记。 我把游戏相关的内容记录下来，加一点不成熟的想法。\n本期游记内容：\n2020 全球游戏市场趋向 Supercell 十年感悟 2020 全球游戏市场趋向 AppsFlyer：2020全球游戏市场新趋向 中度游戏比重上升\n超休闲游戏的非自然用户(NOI)占比最高，休闲和超休闲游戏的非自然用户增长最快。\n在中度游戏中，小型游戏和大型游戏差距是非常大的，达到43%。而规模最大的中度游戏，买量占比只有32%，大部分量来自品牌和知名度等自然量。\n休闲游戏方面，iOS次留为35%左右，头部20%在43%左右；安卓32%左右，头部20%为41%。重度游戏方面，iOS次留34%，头部20%为44%；安卓次留29%，头部20%为36%。\n安装30天内，游戏比非游戏应用卸载率高34%，可见游戏间竞争非常激烈。安卓卸载率高出iOS卸载率125%。\n安装 90 天内的用户付费比率，中重度游戏在下降：\n游戏类型 2019Q1 2019Q2 2019Q3 休闲 5.9% 6.0% 6.3% 重度 5.0% 4.9% 4.4% 中度 4.3% 3.2% 2.8% 社交菠菜 4.8% 4.9% 5.0% 游戏行业使用再营销广告的比例远低于电商、旅游等行业。整个游戏行业使用再营销广告的比例是最低的，虽然近几年有所增长，但目前仅为11%。\n再营销广告：拉动活跃或者重新激活已流失用户的广告。\n行业 2017 2018 2019 购物 36% 43% 53% 食品饮料 32% 33% 39% 旅游 16% 26% 41% 金融 10% 13% 18% 游戏 7% 9% 11% 疫情期间，教育(66%)、餐饮(13%)、娱乐(7%)产品的用户活跃度增长。旅行(-30%)、约会(-21%)、社交(-17%)产品的用户活跃度降低。教育(45%)、音乐(29%)、视频(23%)的营收提升。旅行(-29%)、购物(-23%)、生活方式(-16%)的营收下降。\n疫情期间，全球三分之二的应用类型内购收入是增加的。中度游戏的量级在上升，总收入有波动；重度游戏保持平稳；休闲游戏处于上升状态。\n2019年全球游戏类应用广告的花费为220亿美元。预测2022年达到485亿美元。\n亚太区增长高于其他区域，这是因为日韩地区是重度游戏的重点市场，重度游戏的收入更高，所以占比也会更大。俄罗斯和巴西是增长最快的市场，亚太区中印尼和泰国也是增速最快的区域。中东的沙特，超休闲的增速非常快。埃及的中重度游戏表现非常好。\n从每个付费用户带来的平均收益角度来看，使用再营销广告的广告主，每个付费用户的收益可以提高50%，而50%的增幅是其他行业数据的两倍。从留存角度看，使用再营销的游戏的留存率，无论是短期留存还是长期留存的数据，都大幅领先较少使用产品。在30天留存数据中，差距已经扩大到两倍\n再营销也有难点，一是从业人员的注意力大多在拉新上，并未考虑到再营销广告的投放。二是游戏再营销的使用比较复杂，需要和深究用户行为，和运营联合起来一起做。\n例如一款产品品质不错，用户愿意小额付费。但是用户付费之后发现还是过不了xx关卡，所以就流失了。这时营销人员可以使用“受众共享功能”，可以设置最近30天充值大于5美金，关卡设置为（xx-1），最近x天卸载等。通过这个设置可以将用户进行细分，自动同步给再营销平台。通过再营销平台，可以给用户一个通过攻略，或者更简单的版本。这样可以把用户切分和深度链接相结合，达到更好的效果。从而提升游戏的整体LTV。\n支持用户级别颗粒度的对接平台：MOPUB/IRONSOURCE/APPODEAL/MAX。\nSupercell 十年感悟 Supercell CEO亲笔信：十年学到的 10 条经验 10 LEARNINGS FROM 10 YEARS Supercell 的梦想：创造被玩家体验很多年、并且被永远记住的游戏。\n1、始终坚持“无限游戏” “留存、留存、留存，在给游戏研发新功能的时候，不只是早期留存（当然也需要），还要始终关注长期留存”\n有必要还上因时间而积累的游戏设计问题和技术问题的债，优先为基础问题寻找解决方案，而不是寻求短期影响力。\n曾嵘： 追求短期目标的企业不可能按照上述要求运转。不同时期的企业要辩证地来看待长留的问题。\n2、优秀的团队打造伟大的游戏，但出色的个人并不一定能组建出好团队 游戏成功需要万事俱备：\n你的游戏需要与它发布时候的用户兴趣匹配； 你需要不同能力和不同思维方式的超级优秀人才； 最重要的是，在一个心理安全的环境中，这些人才需要紧密合作。 首先打造一个紧凑和功能完善的核心团队，比如2-4人。SuperCell 经常从“神奇双人组”开始打造团队。这需要完全的信任，他们知道彼此的想法，与此同时又有着不同的看法。一旦有了牢固的核心团队，增加人手和增长团队就变得容易很多，若没有坚实的核心，不要轻易给团队增加新人。\n3、慢招人，始终提高标准 你拥有的员工数量并不一定直接与实现更好的结果相关，很多时候可能恰恰相反。新的人员应该能够提升公司的平均水准。\n要雇佣以质量为导向的人，在公司层面上把品质标准设得尽可能高。当人们日复一日地看到他们的同事真正关心他们正在做的事情时，这是有感染力的。这只会创造一种真正积极的动力，让人们从他人那里获得灵感，并最大限度地发挥自己的潜能。\n单个员工对项目的归属感极其重要，他们不仅会关心游戏的整体质量，甚至会关心每一个小细节，与优秀的人共事可以让所有事变得更好，对项目的热情会传递给团队的每一个人。\n曾嵘： 这是文化的力量。当一个团队产生了高质量的文化，管理上就会更加轻松，所有的人都会在同一个文化的认同下直达目标。如我在读书笔记：《学校如何运转》 最后写到的那样：做一个在管理上用力很轻的企业。\n需要在招聘侧就严格把关。需要知道什么样的人能接受高质量的团队文化。没有目标，仅仅为了生存和收入而工作的人会缺少足够的热情，不应该进入你的团队。否则就会变成一种消耗，消耗团队的所有人，尤其是你。\n4、保持尽可能小的规模 更大的团队通常只能‘发明’一些看似有用的工作”。小团队的专注力是打造优秀事物的关键。团队的成长会过早增加团队的复杂性，这会使得改变方向的时候会更难。如果没有“杀手级的核心玩法”，就没有必要专注于花哨的系统、工具或技术，无效的反复修改会杀死项目，专注力的丧失更不会实现游戏成功的梦想。\n5、文化是所有人行为的总和，而不是PPT上的装饰或写在墙上的标语 文化由最艰难的决策定义，绝大多数下面这些决策都是团队自己做出的，团队以外的任何人(包括我)都没有参与其中：\n砍掉不符合我们“可以玩很多年”梦想的游戏； 对有趣的想法说“不”，因为我们要集中精力聚焦； 如果团队没有达到预期的效果，一定要对其做出改变。 What you do is who you are.\n6、用信任代替控制 团队最了解自己做的事情，因此他们才是做出最佳决策的最优人选；如果团队可以自己做出决定、而不需要寻求其他人的批准，他们执行起来会更快、更开心。\n在理想情况下，团队可以做所有的决策，这样我（CEO）就不用做决策。\n“相比绝大多数其他公司和他们的企业文化、Supercell人被允许拥有更大的权限。大多数的公司都是基于等级制度、流程和文化来管理员工，但并不让他们贡献自己最大的能力。Supercell向人们展示的是，当企业去掉了审批、流程、等级和官僚主义之后，我们的人才可以带来不可思议的成就。这也是Supercell拥有5款大作并且获得十多亿用户、创造玩家难忘的长时间优秀游戏体验的根本原因”。\n当所有事情很顺利或者所有人都赞同做某些事的时候，做团队决定很容易，但如果不是这样、情况就会变得艰难很多。\n当《海岛奇兵》公司内部试玩版做出来之后，很多Supercell的共同创始人和游戏主管开会都认为这款游戏应该被砍掉。在一个关键游戏主管会议上，十分之九的人都认为这个项目应该被放弃。在《皇室战争》和《荒野乱斗》项目上，这两个项目开发团队之外的Supercell同事都曾质疑过这两款游戏。SuperCell 的公司文化让这些团队不顾其他人的反对发布了这些游戏。\n7、不要让对失败的恐惧指引你做事，坚强并尝试新事物！ “没有人知道任何事。玩家们在变，公司在变、我们也在变，生活就像是一个持续移动的标靶”。\n打造新事物和创新是困难的，它需要承担风险。顾名思义，冒更大的风险意味着失败的次数比成功的次数多。如果人们害怕失败，他们就不会冒这些风险，因此就没有创新，也就没有大作游戏。为了鼓励我们的员工勇于冒险和失败，我们努力创造一种不仅可以接受失败，而且预料会失败的文化。事实上，我们尽量不把它们看作是失败，而是学习机会。\n8、抵制住创作流程和规则的冲动，哪怕你犯了错 人们总是倾向于努力控制局面，我们大多数人在自己能控制的时候都更有安全感。这可能有问题。\n首先，在当时起作用的规则往往现在是没有意义的，因为规则创造之后，一些事情发生了变化；其次，有时候人们倾向于遵守流程做事情，但却不一定是有意义的。\n很多年前，我们制定了一个规则，所有新游戏有三个月时间做出可玩版本，因为我们想让新游戏更快速获得概念验证。开发人员随后开始更早地启动项目，而且不告诉任何人，确保他们可以在这个时间段内做出可玩版本。这创造了一个不健康的环境，开发人员试图玩转规则，而不是专注于尽可能做最好的游戏。而且，把一个规则运用到所有游戏里是没有意义的，因为会有些游戏六周就可以做出可玩版本，但还有些游戏需要六个月。\n9、传统的目标设定方式在我们的独立细胞里行不通 公布了公司目标六个月之后，我曾问一个同事他们对于按照目标做事情是什么看法。在随后的讨论中，我们发现：1）大多数人都不记得目标是什么，因为有 2）这些目标与他们特定的团队无关，或者只是与所有人有关。\n随后有些人问到：“我们的目标不是创造让很多人玩很长时间的伟大游戏吗？”我回答：“没错，是的”。这个人又问：“我们不是要通过最佳的团队和最佳的公司文化来实现吗？”我说：“完全正确”。他又追问：“所以其他目标还有什么？”我：“不知道”。\n我们真正的目标是打造伟大的游戏，让尽可能多的人玩很多年、并被永远记住，这是我们的长期目标。每一年我们都在短期目标/预算的限制之下离这个梦想更进一步，如今我们只是让团队在这个框架内设定自己的目标，让他们告诉所有人他们的目标是什么。去年，我们在两场每次1小时的全体员工会议上呈现了各个团队的目标，我们认为这样做最大的价值在于，它可以让团队停下来思考中长期目标。\n曾嵘：目标确定了之后就要被不断传达。独立团队文化会导致团队不会关注具体的公司目标。这里 Supercell 的长期目标不能算是目标，而是愿景。\n10、写下你的价值观并定义你的公司文化 Supercell有六名创始人，用了2年的时间，等到公司增长到40人的时候才写下公司文化。这不合理，因为过晚定义价值观，会让团队中人员对于公司价值观的理解不一致。\n制定价值观时，与所有的Supercell同事单独谈话，找到他们认为的价值观。然后总结，每隔一两年重新审视。\n文化不应过度简化。过度简单的词语会让人们的理解变的不同。Supercell 为公司文化写了一个详细的备忘录，而且在每个文化中加入真实的 Supercell 故事，用行动展现文化。\n“生命是短暂的，享受这个过程。找到激励你的人，让你做的更好，让自己成为更好的人。我们不知道自己在做的是什么，所以专注于打造一个优秀的团队，和尽可能创造最佳的环境让团队成功”。\n全文完 ","date":"2020-05-16","description":"","lastmod":"2020-05-16T01:08:51Z","slug":"gamenote20200516","tags":["gamenote","reading"],"title":"游记：Supercell 十年，中度休闲游戏增长","url":"https://blog.zengrong.net/post/gamenote20200516/"},{"categories":["impressions"],"content":"本书为北京十一学校联盟总校校长李希贵结合30年教育管理经验和教训所作。\n曾嵘的感受：\n我在教师一线岗位上呆了 3 年，在教育主管部门干了 6 年。时至今日，我对教育系统的现状依然非常不满。离开教育系统十几年了，我依然深爱教育行业，持续关注她的发展。教育系统和教育人的整体能力提升都非常缓慢。有点像早期的PC 堆料：不断堆砌更高的硬件设施，但教学方法和教学理念依然陈旧不变。\n教育系统的变革，应该是自下而上的，而非自上而下。但这个“下”不能下到老师，而应该下到校长。一个优秀的校长可以给整个学校带来翻天覆地的变化，可以推动所有老师迅速提升个人能力，这是我亲身的经历和体验。如果没有强大的师资力量，没有校长的有效推动和管理，任何来自国家、省、市、区教育主管部门的理念都不可能推进落实。优秀的师资力量会面临枯竭，优秀的学生会面临更大的课业负担和选择考验。\n教育的目标不是补齐所有短板，而是挖掘潜能，发现学生的长板。 这句话看起来很有力，但要把它实现，需要家庭、社会、学校做深入的工作。书中怀着崇高的理想，认为基础教育的真正作用是“试错”。在当前的教育现状下，我认为这还只是“理想”。大学教育的作用是“选择”，而基础教育越来越变成了“选拔”：“选拔”一批可供“选择”的材料。作为一名家长，我是能深刻地体验到这种“选拔”的力量。从社会，到学校，到家庭，无人不在拼尽全力。\n做公司和做学校是一样的，都是做企业。公司以盈利为目标，以社会责任为最终使命。学校以课程为产品，以学生为成长为信念，以社会责任为最终使命。两者没有本质不同，都在为社会创造价值。在公司，价值以产品的方式体现，在学校，价值以对学生的行为影响的方式体现。\n这篇读书笔记并不全面，是对书籍的一次快速浏览后所写，主要偏向于对学生和教育的理解。如有时间，我会回来补齐管理方面的理解。\n基础教育的实质 中小学是孩子们进入社会之前的小社会，目的是让他们“先活一遍”。\n基础教育的真正作用，是在这个教育者守护的边界中，让学生尽情选择，充分试错，亲身体验成功的喜悦、挫败的迷茫。这个成本比先进入社会再犯错要小得多。\n几句名言 曾国藩：行霹雳手段，怀菩萨心肠\n李希贵：非常理想，特别现实。 无论我们希望赋予学校多少使命，都没有理由剥夺孩子们的童年。\n李希贵：在管理工作中，能用结构解决的问题，就不用制度。能用制度解决的问题，就不靠开会。\n李希贵：可以越级检查，但不可以越级指挥；可以越级汇报，但不可以越级请示。\n戴明：一个组织中的员工问题，80%都是组织和系统自身的问题。\n好学校的三个表现 好学校中的师生关系是良好和融洽的。 教育是人对人的影响，教育学首先是关系学。学生的成长、教学的效益，是从师生管理里来的。每个学生都是一个独立的教育成长中心。十一学校赋予了所有教职员工服务学生成长的育人职责，无论科任老师还是后勤人员，都以自己的方法教育学生。十一学校实行类似大学的选课制度，它没有班主任，上课的老师就自动成为了这门课的班主任。传统班主任的德育功能由导师替代，导师每天在固定的时间和学生见面，制定阶段目标和学习规划。负责纪律的老师专门“唱黑脸”，他们时刻关注学生易犯的错误，用公正的态度给予处分或者反馈给导师和学科教师。但这些反馈不会计入学生评价。十一学校还有专门的咨询师，他们是“老师的老师”，“学生的导航者”，负责解决学生和教师在沟通中的困惑，也帮助学生解决心理问题、升学与职业规划问题，家长亲子关系问题。\n十一学校的一个学生体验是这样的：在中学 6 年的时间里，他平均和 100 多位老师打交道，这些老师各司其事，又都负责他的成长，随时回应他的需要，他有足够的犯错空间，不担心自己会被班主任针对，能更放松主动地和老师们交流。\n好学校需要为学生提供多元化的，更自由的成长空间。 传统校园的所有空间都有明确的功能，这是过度管理的表现。学生需要能挥洒自我、暴露自我的公共空间，他们可以在那里完成自选动作，也包括犯错误。十一学校的学生可以在学校录音棚沉浸在自己的爱好中，也能在学校花园里面摆摊卖东西，还可以让学生在选修的戏剧课中找到自己的灵魂方向。\n书里讲到一个例子。有那么所国际学校，董事会成员几乎都是在校学生家长。董事会做的决定，经常让校长头疼不已。比如：任何家长都可以在任何时候来学校，可以到任何一间教室推门就进，听听老师在讲些什么。然后，家长可以跑到校长那儿去提意见，要求马上落实，虽然他们的意见都是互相冲突的。另外，董事会迟迟不批准一个对学校来说很重要的建筑：艺术馆。原因能猜出来：艺术馆要建好几年，他们家的孩子是在校生，赶不上了。\n审美能力和艺术感悟能滋养人的一生。艺术、哲学是幸福的来源，不是光有钱就能买到的。这些内在的东西在青春期不培养，以后就没有机会了。好学校的决策要更多考虑学生的长远发展。\n在好的学校中，学生拥有自己的成长路径。 学校最重要的产品不是学生，而是课程。教育是一种服务，它服务于学生的成长。十一学校有 300 门课可供学生选择，同样的一门数学、物理有五个级别可选。一二级满足高考需求，五级面向未来有志于从事这个专业的学生。很多老师和家长，都在习惯性地为孩子补短板。这种补课会让孩子逐渐丧失信心。 教育的目标不是补齐所有短板，而是挖掘潜能，发现学生的长板。 潜能是观察不出来的，它埋藏得很深，只有让学生什么都尝试，找到之后，就能长期专注投入。所谓“差生”，是还没有找到长板的学生。\n有个学生，在班上一大半的同学数学都考满分时，她只考90分，所以觉得自己数学不好。当她进入十一学校高中部以后，参加了选课前的测试，结果水平处于全年级前10%。老师建议她选最难的数学五级。在这个课程里，同学只能拿到60分、40分，而她还是能拿90分。她这才知道，原来自己有过人的数学天赋。\n书中对于不同年级学生心理的准确而细腻，丝丝入扣。下面是初二学生容易出现的问题。对比小曾的同学的表现，几乎全中。\n学习上两极分化开始明显。 初步适应了初中生活，但离中考还有一段距离，暂时出现目标不明确、纪律松懈的现象。 部分学生一两个学段成绩提升不明显，出现动力不足、懈怠现象。 初一时较松散，到了初二，学习难度加大后，缺乏耐心和毅力。 有学习潜力但不用功的学生（男生为主），成绩出现较大滑坡。 摆不正兴趣与学业之间的关系，为了兴趣暂时放弃学习。 没有完全养成良好行为习惯，对学校规则有些淡漠。 参加的课外班过多，影响课内学习任务的完成。 手机管理：有些学生仍然存在侥幸心理，或者意欲挑战权威和规则。 叛逆意识增强，不想跟老师说心里话，听不进老师的建议。 思想日渐成熟，对父母的反应与批评变得敏感，与父母的冲突增多。 比较敏感，一言不合便容易引发肢体冲突。 随着同学之间彼此熟悉，个别学生出现男女生交往现象，但尚处于萌芽阶段。 个别学生错误地认为语言不文明是成熟的表现。 怎样办一所好学校 做管理工作中，能用结构解决的问题，就不用制度。能用制度解决的问题，就不靠开会。\n李校长按照明茨伯格的理论，从功能出发，把学校的结构重新划分成为五个部分的矩阵结构：战略高层、教育教学一线、中层管理者、支持人员、研发平台。\n校务委员会的搭建原则是：吸收离学生最近的人，让“能听到炮声的人指挥打仗。”校长在财权上只负责审批年度预算，具体的报销需要财务总监签字才有效：“有权的人不理睬，理财的人没有权。”在人事聘任权上，校长无权干涉年级主任聘任教师。\n曾嵘： 在公司管理中也需要有这样的安排。让职能部门来决定招聘人员的能力和薪酬和变动（人事权），让业务部门来决定员工的奖金和福利（奖励权）。\n只有真正处于战略高层和教学一线中的少数中层人员，才是中层管理者。其他中层干部属于服务支持人员。不会再有一堆“婆婆”来瞎指挥，只有对教学最内行的管理者在给老师提供专业的指导。在一个健康的组织里，可以越级检查，但不可以越级指挥；可以越级汇报，但不可以越级请示。\n扁平化的层级当然更加高效，减少管理效率的浪费，降低信息传递的偏差。但扁平化对管理者的要求更高。这种管理方式要求每个管理者有更全面的思辨能力和更顺畅的沟通表达技巧。\n十一学校300 门课程的研发平台是最值得学习的。学校采用招投标方式，从战略高层到中层管理部门都可以根据需求向全校发布项目。每名教职员工都可以根据自己的专业和特长参与竞标，拿到启动经费。在项目通过评估、答辩之后，课程投入运营，再根据成果质量给予奖励。\n以学校组织郊游远足为例，经过研发平台，形成分发到每个班级的操作标准，包括：家长书面同意、详细远足计划、出行期间时间安排、教师行为准则、标准应急工具箱。十一学校的每门选修课背后，都有课程手册、学习资源、教学工具等等。电影课、大学先修课、残疾人护理课、旅行管理课、理财课等都是十一学校选修课的内容。\n十一学校还采用了互联网企业常用的 OKR 来推进课程质量。下面是“人工智能领域课程”的 OKR。\n美国管理大师戴明说：“一个组织中的员工问题，80%都是组织和系统自身的问题。”只要能成功建立结构良好、机制健康的组织，领导者的用力就会很轻。\n“一个组织的衰败常常从支持人员的内部混乱开始。”\n曾嵘：互联网公司的支持人员就是行政，财务，人力，IT，运维，后勤。在中小企业，这些任务可以专人专岗，也可以多人兼任。根据李校长的启发和我的个人经验，我认为互联网中小企业在支持管理上的重点是：\n不能没有，必须有序，实时响应，稳定输出。\n希望我们能做成一个在管理上用力很轻的企业。\n全文完 ","date":"2020-05-15","description":"","lastmod":"2020-05-15T01:04:55Z","slug":"how-a-school-works","tags":["reading","readingnote"],"title":"读书笔记：《学校如何运转》","url":"https://blog.zengrong.net/post/how-a-school-works/"},{"categories":["technology"],"content":"前几天有位同事问我，学习 Vue 应该看哪些书。作为一个从 2000 年开始就用记事本手写 CSS 的野生程序员，我的确是存了不少关于 Web 前端的书籍，但对于团队成长来说，总是发书似乎解决不了未来的问题。所以我觉得有必要写一篇 授人以渔 的文章，把我自己快速掌握一门新技术的方法说道说道。\n1. 定义技术 首先来定义一下 技术 的概念。技术是 知识 的一种，如果按照弗里茨-马克卢普的分类法，技术应该属于 实用知识。如果按照 Towers 的分类法，技术应该属于 实践性知识。如果按照波兰尼的分类法，技术应该属于 关于技能和诀窍方面的知识。\n弗里茨-马克卢普根据知识的实用价值把知识分为五类:实用知识、学术知识、闲谈与消遣知识、精神知识、不需要的知识。\n一九六六年,美国俄亥俄州立大学教授陶尔士 (Towers)、陆克斯 (Lux)和雷(Ray)等人,受Maccia 系统化知识思辨理论的影响,将人类知识分类成「描述性的知识」、「规范性的知识」、「实践性的知识」、与「形式性的知识」四大领域.\n经济合作与发展组织(OECD)在1996年发表的《以知识为基础的经济》报告中,以波兰尼的知识分类为基础,进而把人类的知识分为四大类:关于事实和现实的知识、关于自然规律和原理方面的知识、关于技能和诀窍方面的知识、关于人力资源方面的知识。前两类为可编码的显性知识,后两类为不能明言的意会知识。\n2 月份 SAGI读书会 选书 《俞军产品方法论》 中也提到了关于企业知识传承的问题，在这段话中可以看出，技术属于可以用语言文字符号传递的 显性知识。\n生产效率高于市场平均水平的企业才能得以延续。企业使用 权威 的力量来提高效率。利用各岗位的专家表现出超出市场平均水准的更优决策来战胜市场。企业的核心竞争力是专业知识，专业知识中的 显性知识 可用语言文字符号传递，但 默会知识 只可意会不可言传。企业管理者通过设计内部惯例和制度，将一部分专有 默会知识 转换成 企业共同知识，从而提升提升组织效率。\n在这篇文章中，我把 技术 定义为： 可以通过现有资料进行全面掌握的解决实际问题的知识与能力。\n从上面的定义可以看出，由于技术解决的是 实际问题，不可避免地，技术会根据现状的变化出现过时的情况。由于可以 通过现有资料全面掌握，时效性就显得尤为重要。\n2. 锚 找到正确的资料，才能准确掌握技术。当今的互联网上，优质和劣质的资料混杂在一起，我们必须寻找到特定的锚才能判断资料的质量。\n对于程序开发技术，我主要用下面几个锚：\n该技术的官方网站/官方文档。 社区。 问答网站。 经典书籍。 相关联的技术。 下面展开讲一下。\n2.1 官方文档 官方网站/官方文档永远是你学习这门技术时首先阅读的。一个值得学习的技术，应该是相对优秀的。如果它的官方文档不能让你满意，那就只能说明两件事：\n该技术还不够有名，可能属于项目早期。 该技术由于维护人员太少而有维护风险。 在技术百花齐放的今天，对于一门技术，你往往能找到大量的替代者。此时需要睁大眼睛挑选，如果在选择上出了问题，可能会浪费大量的学习时间。如果官方文档很不完善，可以观察项目是否处于早期阶段。早期阶段的技术需要谨慎使用，如果学有余力，可以为早期技术贡献代码，当然这就是另一个故事了。\n在文档不完善的情况下，你可能需要大量阅读源码才能完成对资料的理解，孰优孰劣，要看个人。\n下面是我之前做技术选型时候的一些选择，供参考：\n桌面软件GUI开发框架 SaltStack/Ansible/Fabric 的选择 Python web框架的选择 Golang Resources 2.2 技术社区 Github 是目前最大的社区，大量的开源项目会在 Github 上托管自己的仓库，并建立互动社区。你需要关注该技术的 Issue 和版本发布信息。有些技术依然在使用古老但有效的邮件列表，例如 uWSGI。入乡随俗使用它们，观察参与社区讨论的热度和话题的回复速度，能得到正确的锚。\n对一些热门技术，社区提供了集合性的资源，例如针对 Vue 的 Awesome Vue.js 。\n2.3 问答网站 全球知名的 Stackoverflow 是程序员必上的网站。你可以查看一个话题的热度和回复速度，这能够体现该技术的热门程度。热门的技术总是相对更好的选择，这意味着你可以更容易找到一起讨论的人，你的问题能更容易得到解决。\n国内的 V2EX 和 SegmentFault 也是相对靠谱的社区。而对于 知(B)乎，需要谨慎分辨。\n总的来说，不建议在知乎提问技术问题。不要使用百度搜索技术问题，尽量少看 简书 和 CSDN博客 的文章。并非前面的社区不好（都有不少隐藏大牛），而是因为你碰到质量很差的文章的概率更高。在不知道如何快速分辨这些文章质量之前，你会在这些平台上浪费大量时间。\n重要的事情说三遍：\n不要用百度搜索技术问题！\n不要用百度搜索技术问题！\n不要用百度搜索技术问题！\n2.4 经典书籍 成熟技术都有经典书籍，例如著名的 《UNIX环境高级编程》，《JavaScript高级程序设计》 等等。 最简单的找到经典书籍的办法是在 豆瓣图书 搜索后查看评分。如果一本技术书籍（非小众技术）有 500 人以上的评价 和 7.5 以上的评分，我们就可以认为这本书是经典之作。\n挑选出版社也是个好办法。 O'REILLY 策划了一系列优秀的技术书籍，它的书籍装帧使用相同的风格，是的，这个风格你肯定见过。\n在国内，电子工业出版社、人民邮电出版社、清华大学出版社 在技术书籍方面的质量是比较有保证的，买书时可以优先选择它们。\n2.5 相关联的技术 要完全掌握一门技术，需要学习其相关联的技术。以 Vue 为例，相关的基础架构有这么多：\nHTML4/HTML5 CSS3 Node Javascript/Typescript/ES2015/ES2016 NPM/Yarn 前端生态系统依赖：\nBabel webpack axios 相关插件和工具链：\nVue Router Vuex Vue Loader Vue Cli 这么多相关联的技术，一次性全部掌握是不可能的。最好的方法是保证学习的主线，在主线周边逐渐展开技术广度，不要追求单个技术的深度，一切以实践中能满足需求为目标。\n在需要了解某一个关联技术的时候，也可以继续采用上面的 5 个方法找到该技术的锚，基于优质资料持续深入学习。\n3. 我是怎么学习 Golang 的 去年底，我花了 1 个月的时间学习 Golang，并写了一个在生产中使用的小项目。学习的过程如下：\n找到官方和民间最主流的入门资料，阅读完毕。 选择一个 Web 框架，选择的方法可以参考 SaltStack/Ansible/Fabric 的选择。 找一个即将投入生产的项目，使用 Golang 将其实现出来并部署上线。 强制输出：把整个学习过程记录在 Golang Resources 和 从Flask 到 Gin 这一系列文章中。 下面是学习输出文章的一段摘录，现在看来，描写得很准确：\n我学习新技术的时候有个不好的习惯，就是除了新技术本身，还会第一时间找一堆与这个技术相关的信息，了解一下其主流应用，把今后可能用到的库啊开源项目啊都找出来。这就导致我在学习 Golang 的同时又跑去看了大量的开源库和 Golang 主流应用的资料。另一个不太好的习惯就是，我很有点选择困难综合症，总执着于在一堆技术里面选择出来一个自己看起来顺眼的，相对主流，用起来不错，源码足够优雅的那个。这也就直接导致我足不出户的这两天阅读量巨大，眼都快瞎了。除了选择起来很痛苦，寻找佐证和写垃圾代码测试的过程也是难以描述啊……\n4. 我是怎么学习 OKR 的 今年 2 月，为了启动团队 OKR，我全面学习了 OKR 知识，并在新冠疫情期间团队在线办公时，在部分项目中进行了试验，效果还不错。学习过程如下：\n在豆瓣读书中找到评分最高的关于 OKR 的数据，从图中可以看出，《OKR 工作法》与《这就是 OKR》是最好的书（综合评分和评价数量）。 深度阅读，做读书笔记。 读书笔记：《这就是 OKR》， 读书笔记《OKR 工作法》。 阅读一本包含多种绩效管理方法的书，深度阅读，比较所有绩效管理方法与 OKR 的区别。我选择的这本书评价不佳，读起来也的确很乏味。但它对我要达到的目的来说，是一个合适的选择。读书笔记：《绩效管理全流程实战方案》 在团队中实践。选择一个项目，在制作人完善了解 OKR 的前提下（可以通过读书笔记），与制作人逐条全面讨论目标和关键结果的制订。讨论期间，不断重新理解书籍中的内容，学习书籍中的实例，直至制订出大家都认同的 OKR。然后每周对 OKR 进行同步、调整、打分和反馈。在 OKR 结束时进行总结，并制定下一个阶段的 OKR。 5. 形成默会知识 授人以渔 的重点，是形成 默会知识。 有着不同经验的人，在学习时花费的时间是不同的。经验更丰富的学习者在学习新知识的时候，会从之前形成的知识网络节点中找到类似的经验，与新知识关联验证。如果找到相似的关联，这部分的知识就会立刻被学习者的知识网络接受，从而节省大量的时间。\n形成的默会知识越多，个人知识网络 越清晰，经验关联越丰富，学习新技能和知识的速度就会越快。我在学习 Vue 的时候，已经有大约 18 年没有写 Web 前端，个人知识网络中的前端知识点还是 XHTML/CSS2/Dreamweaver，那时 jQuery 还没有被发明，Node.js 还没有出现，Div 布局还没有成为标准，Web 前端还在使用各种奇技淫巧来解决老不死的 IE 兼容问题。但即使如此，十几年前积累下来的关于浏览器渲染、HTTP 协议以及 HTML 技术栈知识依然快速与新的 Web 标准形成了关联，极大缩短了我的学习时间。\n对于技术人员来说，需要不断拓宽个人知识网络的边界，大量阅读和编码，正所谓：\n学得越多，就学得越快。\n这套方法论并非只能用于程序员。我们在工作中需要掌握的大部分技能，都属于“技术”的范畴。希望读到文章的同学能触类旁通，举一反三。\n学习之路，没有尽头。与君共勉。\n全文完 ","date":"2020-05-12","description":"","lastmod":"2020-05-12T07:27:30Z","slug":"how-to-study-a-new-technology","tags":["howto","study","choice"],"title":"如何学习一门新技术","url":"https://blog.zengrong.net/post/how-to-study-a-new-technology/"},{"categories":["impressions"],"content":"\n亲爱的淇淇：\n今天是母亲节，我们就来聊聊关于母亲的事儿。\n精子和卵子结合之后，我们的女儿“你”就出现了。你会找到子宫这个安静的小床，在上面舒服地躺下来，这就是“着床”。接着，妈妈的身体为了你的出现，开始发生巨大的变化。首先，她的体温会升高0.5-1 摄氏度。然后，为了维持子宫内膜的稳定，让你的小床更加舒适，她的身体会分泌大量的黄体素。黄体素的增多，会让身体有疲倦乏力感，妈妈会很想睡觉，但她依然还要努力工作。\n由于身体里多了一个“你”，妈妈需要吃更多的东西来满足两个人的需要。她匀称的身材因为存储了太多能量变得胖嘟嘟的。你的逐渐长大，让妈妈每天都要抱着肚子走路，睡觉的时候不能随便摊大字了。这是什么感觉呢？你可以试试每天抱着一个大西瓜刷牙、学习和睡觉试试看。当然啦，和你抱的大西瓜不同，妈妈深深地爱着这个大西瓜。\n你在妈妈的身体里，和妈妈同呼吸，心连心。妈妈能听到你的心跳，感受到你的情绪和状态。妈妈还可以和你对话，仔细聆听你的反馈。妈妈的身体因为承受了两个人有了很大的负担，她会整夜睡不着觉，也会因为你的烦躁而难过，因为你的快乐而兴奋。你为什么这么喜欢吃 Pizza 呢？那是因为妈妈怀孕七个月的时候正好在必胜客门口排队，你闻到了 Pizza 的香气，告诉妈妈你要吃的缘故啦。\n在你出生的那一天，如果是自然生产，妈妈会在产房里待 8~48 小时的时间，承受巨大的子宫收缩疼痛，你从产道出生的时候会撕裂妈妈的阴道口。如果是剖宫产，妈妈的腹部会留下一道永久的疤痕，还要在病床上躺上 3 天才能出院。出院后，妈妈需要 3 个月以上的时间才能逐渐恢复好身材和身体的激素水平。然而，这一切付出都是值得的，因为我们有了你这个小宝贝。\n母亲是伟大的，为了你的出生，她克服了很多从未经历过的困难。为了你的成长，她更付出了不懈的努力。\n进入初中之后，你的课业负担加重，妈妈的负担也随之加重了。她为你寻找合适的辅导班，在碰到不懂的难题时帮你寻找解法和答案，仔细研究做出你喜爱的美食，刮风下雨也不间断送你到校，早上充当闹钟，睡前充当按摩师，陪你聊学校的趣事，一起吐槽不开心，在疫情期间帮你上传作业和录制课程……妈妈不仅是你的母亲，也是你的良师，你的闺蜜，你的诤友。\n说说“诤（zhèng）友”这个职责吧。诤友是能用直言相规劝的益友，出自汉书《白虎通．卷四．谏诤》：「士有诤友，则身不离于令名。」在生活和学习中碰到你因为经验做得不够好的地方，妈妈都会直言不讳地指出来。但良药苦口，你和妈妈之间也会因为这些“直言”而发生不快。每当这时，我就是最好的调停人。你要相信，我永远是站在公正的角度来处理这些小“冲突”的。当然了，Old Daddy 会更多地偏袒女儿一点（这显然是不能直说的啦）。\n平心而论，妈妈的建议大多数都是合理的。那为什么有时会出现争吵呢？我觉得主要的问题在于“情绪”的控制。情绪是我们看不见的敌人，它们在有分歧的时候出现在我们的大脑中，借助肾上腺素和偏见的力量迅速壮大，如果不对它们进行理性地遏制，就不会有理性的讨论。一次基于学习方法或者题目解法的讨论，因为情绪的失控，会变成一场孰是孰非的争吵，最终浪费大量的时间，末了，还需要更多的时间来弥补双方心中的不快。作为聪明的你，一定会愿意主动避免发生这种情况。\n要怎么避免呢？Old Daddy 给你三个锦囊：\n锦囊一：面对看不见的情绪，寻找看得见的边界\n当你意识到争吵发生时，要寻找一个边界。例如，你可以指着时钟说：“妈妈，我们也别争了，等到时钟上的分针走到 9 的时候，我们就和好了行么？”或者如果在路上，可以说“等到我们走到那棵树的时候，我们就停止争论了好么？”使用这种方法，能加强你对自己情绪的掌控感。\n锦囊二：只谈论事实，不评价对错\n争吵往往是从“我觉得你这样做不对”开始的。每个人的思维方式都不同，要完全站在对方的角度思考问题是很难很难的。随意对别人进行评价，很容易引发对方的反感，造成双方争论的焦点从理性的事实转移到感性的对错之上。 如果碰到对方用“我觉得你这样做不对”开头的话语，可以先耐心听完陈述，然后平静地说：“妈妈，我知道你的想法了，现在让我们继续讨论一下这道题的错误原因好吗？我的想法是这样的……”把讨论聚焦到事实上，对有标准的事实进行评价，才能让讨论更有效率。 要做到这点，需要你对自己的情绪有足够的掌控。你可能需要不断练习才能做到，在练习的过程中也会碰到失败，可不要气馁哦。\n锦囊三：真诚地赞扬，用肯定代替否定\n我们人类是社会化的动物，所有的人都希望得到别人的认同。你、我和妈妈也不例外。在与人沟通的过程中，要注意对方的优点，真诚地赞美这些优点。即使是在这些点上你做得比对方更好也应不吝赞美。例如，碰到同学的解题方法比你好，你可以说：“这道题的解题思路真棒！那个公式是怎么得到的？”碰到同学做实验很快的时候你可以问：“你刚才的实验做得真好，液体倒入量杯一次到位是怎么做到的？”碰到妈妈帮你解出了一道难题时可以说：“妈妈好厉害！这道题我想了好久都没想出来呢！谢谢妈妈！” 多真诚地使用具体细节赞美别人，一定能得到别人的回馈。\n锦囊传授完毕，老爸这里还有不少锦囊，欢迎随时来取。\n再过十几年，你也会成为母亲，我们找个母亲节再来聊聊关于母亲的话题，也是件很好玩儿的事情呢。\n爱你的 老爸\n二〇二〇年〇五月十日\n全文完 ","date":"2020-05-10","description":"","lastmod":"2020-05-10T02:47:49Z","slug":"a-letter-from-old-daddy-to-daughter","tags":["life","qiqi"],"title":"Old Daddy 在母亲节给女儿的信","url":"https://blog.zengrong.net/post/a-letter-from-old-daddy-to-daughter/"},{"categories":["impressions"],"content":"周读，就是把每周我读过的部分有意思的内容记录下来。有时包含我的评论。这些内容来自我订阅的公众号，读过的书，或者在各新闻 App 读过的文章和报道。\n好的谈判者总是能达成交易，因为他的目的不是为了赢，而是为了促成交易。\n世界上有两种人，一种想赢，一种想赢得辩论。\n积累性格信用的方法只有一个，就是赢得尊重。\n我们要追求高级的幸福：共同体感觉。\n世界上的问题可以分为两种，第一种就是像乱麻一样的问题，看起来很难解决，但只要有耐心，总有解开的那天。另一种问题是像水一样的问题，你越急着把水流干净，反而会越来越脏，只能等它慢慢沉淀。\n下面是 2020 年第 18 周的周读内容。\n读书笔记：《被讨厌的勇气》 火神山医院“中国速度”的背后 信息的结构化处理 舒适圈理论的误区 读书笔记：《一生的旅程》 读书笔记：《被讨厌的勇气》 阿德勒的思想会告诉你，你的成功和幸福，与任何人无关，所有的一切都取决于你自己。\n阿德勒的核心思想是：你可以自己做选择。\n阿德勒说：你之所以不幸，是因为你自己选择了不幸。你之所以无法改变，是因为自己下了“不改变”的决心。\n“自卑感”是人不断追求卓越的一个动力源。人人都有自卑感，自卑感让人时刻感到自己的不足，从而不断努力进步。\n生活中有些人因为自卑而特别消极，宣扬“我因为长得不好看而找不到男女朋友，我因为学历低而找不到好工作”，这不是自卑感，而是“自卑情结”。自卑情结的格式是“因为有 A，所以才做不到 B”。自卑情结也是自我的选择。\n人际关系是一种烦恼和不良情绪的源头。决定我们目前状况的，不是过去的经历，而是我们自己赋予经历的意义。\n人际关系造成的烦恼，源于对别人的课题的妄加干涉，或者自己的课题被别人干涉。因此，必须要把自己的课题和别人的课题区分开来。人类作为一种社会性的动物，天然就喜欢得到认可，没有人喜欢被别人指责和被别人讨厌的感觉。\n曾嵘： 在工作中怎样处理干涉的边界呢？要时刻准备为同事提供充分的支持，但在TA们没有向你求助的时候，不要去指手画脚。但当同事花费的精力超出你的阈值时，就需要果断干涉。这个阈值取决于团队人才的能力，也取决于管理者的经验。A 类人才不需要你在工作中做主动干涉，TA 们会自己找到方法。你唯一需要做的是提供支持。\n真正的自由，就是有勇气被别人讨厌。这并不是意味着我们要追求被别人讨厌。阿德勒想要告诉我们，“被讨厌”是自由生活的一种证据，是你按照自己方式生活的表现。\n人际关系是幸福之源。低级的幸福， 比如获取别人的承认，收入比同龄人高等等，这是很容易实现的，但这建立在别人某种不幸福之上。\n我们要追求高级的幸福：共同体感觉。 学校、家庭和公司都可以是共同体。更高级的幸福来源于 被家人需要，或者帮公司创造价值。 有些人退休就没有了精神，是由于高级幸福感的缺失。有些富人已经拥有巨额财富还要不断工作，有些公司追求实现公司的社会责任，就是在追求这样的高级幸福。\n共同体感觉 是一种有持续价值的，对他人的贡献感，这依然与他人无关，阿德勒也反对为共同体牺牲自己。\n火神山医院“中国速度”的背后 关键词：火神山，中国速度，隔离\n火神山医院「中国速度」背后的门道 我们都知道 10 天建成火神山医院非常牛逼，但到底有多牛逼，看了这篇才知道。\n传染病医院的分区\n传染病医院并不能征用酒店和体育场直接改建，因为这些场所往往都是“单廊”的布置，无法起到医患分离的作用。\n火神山医院以50个床位为一个护理单元，也就是下图中一个绿色的单元。每4个护理单元形成一个H型的治疗区域，医院在这个区域配备特定数量的医护人员和医疗器械，为200个病床的病人服务。\n这个 H 型治疗区域，很容易复制。\n传染病医院平面布局的要求是「三区两通道」，三区是指清洁区、半污染区和污染区，清洁区与污染区之间要有过渡区域。正中央竖直的通道是清洁医护通道。两侧横向的通道是一般医护通道。内部红色的箭头是医护人员的流线，外部紫色的箭头是病人的流线，二者没有交叉。\n医护人员的生活办公区位于洁净区，通过「更衣-卫生通过-二次更衣」后，进入到半污染区的走廊，随后进入污染区病房对住院病人进行医治护理。返回洁净区的时候，需要走一个「更衣-淋浴-二次更衣」的流程。而患者的路线是不会进入到清洁区的。\n快快快\n火神山医院使用的方式，是集装箱式箱体活动板房进行模块化拼接。施工上以一个房间为基本单元，每个集装箱单元彼此分开。但由于不能全使用吊车，基本单元必须设计成人力可以搬动的构件，到了现场再组装起来。\n这样的设计，才能满足大面积同时作业，而不用等所有柱子都装好再铺楼板。\n单元里四根柱子承担所有梁板的重量，新增的房间需要有单独的柱子，而不是传统方式里一群柱子共同承担建筑的所有重量。这样的设计，才能满足大面积同时作业，而不用等所有柱子都装好再铺楼板。\n在直播里看到平整场地、钢筋混凝土地面铺设之后，大面积铺的那层「防护衣」，叫HDPE防渗土工膜，主要原料是聚乙烯原生树脂，高抗拉耐酸碱，2毫米的厚度就可以达到防潮防渗水的要求。\n白房子\n武汉气候湿润，防水防潮必须解决，单元格不能直接在地面组装。下面图中大面积铺的「防护衣」，叫HDPE防渗土工膜，主要原料是聚乙烯原生树脂，高抗拉耐酸碱，2毫米的厚度就可以达到防潮防渗水的要求。\n单元房和防水地面之间，垫着方钢管，这样不仅可以进一步起到防潮的作用，也给后续的管道和电器等专业安装预留出空间。\n除了HDPE防水膜，还需要建设配套的雨水调蓄池、化粪池、接触消毒池等污水处理设施和管道，最终要达到每小时80吨污水处理能力，保证医院污水没有一滴进入地下和湖泊。\n暖通专业的特殊之处，在于实现各个区域不同的空气压力梯度，清洁区压力最大，半污染区次之，污染区压力最小，在这样的气压环境下，才能保证室内的空气永远保持从清洁区向污染区流动，减少感染。\n这也是为什么不能征用体育馆和酒店的原因之一。\n最难的是调度和管理\n在科技发达的今天，技术不是最难的，最难的在于资源调度和管理。\n5小时出方案，24小时出施工图，边建设边修改方案；在7万平米的施工现场，近千名管理人员、4000多名工人和几百台机械设备，24小时轮班作业；各类分包单位有上百家，更有几百家不同地区、不同行业的厂家参与到建设当中。\n在这么短的时间里，顶着全国瞩目的重大压力，还是在春节假期缺工人、人员密集风险高的大环境下，要调配大量的人员和物资，部门之间要默契配合，共同完成一项零失误、零感染的任务，这件事放眼世界，只有中国能做到。\n信息的结构化处理 关键词：生命力，构建，筛选\n慢一点，不要急\n曾嵘：在信息爆炸的今天，如果能够坚持阅读，每天会接触到大量信息。如何让这些信息沉淀下来为我所用？这篇文章介绍了一些方法。\n筛选信息\n根据价值进行排序：\n系统的方法或思维方式； 关注的前沿知识点； 备用的资料或素材； 噪音。 噪音没有价值，无论它们如何耸人听闻，如何吸引注意力，不要想，不要看，不要讨论，不要产生情绪。\n主动探索\n对于感兴趣的信息要试着主动探索和挖掘，而不是等待别人咀嚼之后再传播给你。这需要：\n有一套自己的信息管理渠道。对内，可以关注一些深度媒体，比如：财新，三联，界面，澎湃。对外，可以关注一些国家级的媒体。 养成主动检索信息的习惯，而不是被动接收信息的轰炸。你需要什么，关注什么，就去寻找什么，不要等着别人来喂饱自己。 整理消化\n只 囤积 信息，不整理消化，会产生一种虚假的充实感，没有任何实际作用。\n可以把阅读时间分为两块，一块用来闲逛、收集，一块用来消化。\n例如读到一篇关于阿兹海默症最新研究的文章，就可以提炼出文章的核心信息，将其归纳到「阿兹海默症」笔记主题，进一步思考：这个研究可以如何跟其他信息整合起来？可以如何丰富和完善这个主题？\n读到一个新的软件，准备使用，可以将这个计划安排到 TODO LIST 中，找时间尝试一下，把使用结果形成记录。\n探询底层\n生活中大部分信息都是和语境高度绑定的。要思考信息脱离了语境会留下什么。要把信息中的规律、理论、现象和方法论抽象出来，迁移到其他场景和语境，在“流变”的事务中去寻找“不变”的东西。\n舒适圈理论的误区 关键词：舒适圈，学习力\n你陷入「舒适圈理论」的误区了吗？\n曾嵘：舒适圈是一个很老的概念，本文的作者写出了新意。\n舒适圈\n所谓舒适圈，代表着一个人对自己工作、对自己正在做的事情非常熟悉，不需要花费什么力气就可以做好。\n跳出舒适圈并不需要真正意义的 “跳出”，而是要不断“扩大”，是在自己擅长的领域里、能力范围内，不断挑战自己、不断学习，让自己不断进步，保持持续的竞争力。\nVUCA 是volatility（易变性）、uncertainty（不确定性）、complexity（复杂性）、ambiguity（模糊性）的缩写。在 VUCA 时代，必须找到高效学习的方法。\n学习力\n学习力是一个人把知识资源转化为知识资本的能力，即把可学转化为可用，是一个人的学习动力、学习毅力和学习能力的综合体现。学习力的两大特征：\n追问本质，找到边界。即在学的过程中，真正知道自己是否知道，是否知其然知其所以然。 举一反三，跨界应用。即能灵活运用所学知识，举一反三，触类旁通。 学习力=学习动力+学习毅力+学习能力， 三者合一才能发挥最大的效用。尤其是学习动力，我们内心因为对现状不满，我们期望去改变，所以往往是想得很美好，但始终没有真正迈出第一步，结果学习效果一切归零；亦或者是我们想去改变，我们计划得都很好，也开始行动，但总是阻力重重，最终也是达不成满意的学习效果。\n理查德·贝克哈德（Richard Beckhard）与大卫·格莱西（David Gleicher）提出了一个变革公式：\n1D*V*FS\u0026gt;RC 2 3Dissatisfaction* Vision * First Step \u0026gt; Resistance to Change 4 5对现状的不满 * 对未来的愿景 * 第一步实践\u0026gt; 变革阻力 D（dissatisfaction，不满）代表对当前状况的不满。\n曾嵘： 用上面《被讨厌的勇气》中的理论来说，这个不满来自于“自卑感”。\nV（vision，愿景）代表对未来状态的期望，或对行动背后的结果的期待。\nFS（first steps，第一步）代表迈向愿景的积极行动步骤，以及采取行动的意愿。\nRC（Resistance to change）对当下变化的抗拒程度，或者阻力。\n以读书为例，为什么很多人无法坚持阅读？用变革公示分析一下：\nD： 对于现状没有特别的不满，貌似不读书也没有什么问题，内心期望值不高。 V： 读了书之后也没有什么结果，找不到人讨论。 FS： 迈出了第一步，但由于没时间或者各种”其他“原因，书没有读完，放下了。 高效学习\n由简入难： 例如练习英文阅读，可以从简单的英语国家儿童读物或者少年文学读起，找到自信心。\n以始为终： 设定清晰的学习目标，对目标进行分解，完成阶段之后 Review，利用各种机会检验。学完该阶段之后重复这个过程。\n系统学习： 构建学习全景图，化碎片化为系统化，构建个人知识体系\n构建个人知识体系，需要建立知识之间的联系，构思知识的多种用法，探索知识背后的原因。\n化碎片化为系统化，需要不断提炼和整理信息。\n曾嵘：在上面“信息的结构化处理”一文中也有类似的方法。\n有效练习\n有输出的学习就是把所获得的信息和内容进行整理、吸收和传授成为个人认知体系中的一部分，并能通过应用不断反思学习的过程。\n可以使用 说和写 来不断练习有效的输出。对于学过的技能，要创造条件 使用 它们，如果能找到机会 教 别人，则会不断加深对掌握知识的理解。\n曾嵘： 费曼学习法 就是利用教来帮助学习。\n读书笔记：《一生的旅程》 2004年 3 月，罗伯特·艾格在一片质疑声中接手了艾斯纳的烂摊子，成为了迪士尼的 CEO。到现在，他是迪士尼任期时间最长的 CEO。\n艾格作为 COO 和艾斯纳一起管理了迪士尼 5 年，他和艾斯纳的关系在这次 CEO 任命中都成了他的负债。\n艾格采取了 面向未来 的方法来获取董事会的信任，他提出三点策略：\n未来优势内容会越来越珍贵，迪士尼要全力打造优质内容。 未来迪士尼要全面拥抱科技。 把消费群体铺开，在国际市场扎根。 董事会问了很多艾格关于过去的问题，艾格的回答都是：“我无法改变过去，大家想要知道的，是我将带领公司踏上的道路，而不是公司走过的老路。”\n艾格在几个月里进行了 15 轮面谈，遭遇了很多质疑和贬低，最后阶段还被猎头毫无尊重的品头论足。他的优势是 不容易焦虑。第一轮面谈的早上，他可以先参加一个 30 公里的自行车比赛，然后才来董事会接受拷问。\n艾格的这段经历给我们的启示是： 世界上的问题可以分为两种，第一种就是像乱麻一样的问题，看起来很难解决，但只要有耐心，总有解开的那天。另一种问题是像水一样的问题，你越急着把水流干净，反而会越来越脏，只能等它慢慢沉淀。\n艾格重振迪士尼的方法，就是购买超级 IP，然后打造优质内容。皮克斯是艾格第一个想收购的公司，漫威的IP 库足够大，而卢卡斯影业拥有“星球大战”这个超级 IP。这三家公司就是艾格收购清单上的主要对象。\n迪士尼和皮克斯合作过 5 部动画电影，但最后的合作因为高层不合而破裂。乔布斯的态度对于收购至关重要，艾格给乔布斯打电话，采用了主动示弱的方法，这反而获得了乔布斯的好感。\n乔布斯答应见面，并列出了一大堆迪士尼收购皮克斯的缺点，但艾格直接亮出底牌：迪士尼能够通过拥有皮克斯而起死回生。这是谈判者的大忌，但这些大胆、真诚和鲁莽的行动赢得了乔布斯的信任。\n乔布斯提出的要求就是迪士尼要保护好皮克斯的传统和文化。2006 年，迪士尼用 74 亿美元收购了皮克斯。乔布斯加入了迪士尼董事会。\n亚当·格兰特在《离经叛道》中说，一个创新者在选择盟友的时候，应该选择说话带刺并且自己也是创新者的人，这些人更容易站在反对他人和反对传统的立场上，他们对创新者的支持也更加坚定。\n在收购漫威影业和卢卡斯影业的时候，乔布斯都出面为迪士尼做了背书。让这两家公司都相信迪士尼可以保持原始的文化和传统。\n这些收购帮助艾格积累了 性格信用：一个人做事经常偏离大众的预期，那么他做事的时候，就享有更多的自由度。积累性格信用的方法只有一个，就是赢得尊重。\n性格信用积累到了一定程度时，为迪士尼主动吸引了更多收购对象。2017 年，迪士尼以 713 亿美元收购 20 世纪福克斯，默多克主动找到艾格要求收购，条件是艾格必须延长 CEO 任期。\n艾格本来准备在 2015 年卸任，结果一直干到今天，在 2020 年 2 月，艾格宣布会在 2021 年卸任。\n艾格说： 好的谈判者总是能达成交易，因为他的目的不是为了赢，而是为了促成交易。\n塔勒布说： 世界上有两种人，一种想赢，一种想赢得辩论。\n全文完 ","date":"2020-05-03","description":"","lastmod":"2020-05-03T07:34:26Z","slug":"weekread202018","tags":["reading","weekread"],"title":"周读：202018 被讨厌的勇气","url":"https://blog.zengrong.net/post/weekread202018/"},{"categories":["impressions"],"content":"周读，就是把每周我读过的部分有意思的内容记录下来。有时包含我的评论。这些内容来自我订阅的公众号，读过的书，或者在各新闻 App 读过的文章和报道。\n下面是 2020 年第 17 周的周读内容。\n什么样的人会越来越优秀 工程师如何从技术转型做管理？ 用上帝思维来解释裁员 苏世民和沈南鹏的对话 读书笔记：《从 0 到 ZARA》 读书笔记：《风格感觉：21世纪写作指南》 什么样的人会越来越优秀 什么样的人，会越来越优秀？\n关键词：固定性思维，成长性思维\n曾嵘：讲 思维模式 的鸡汤文非常多，而且多是 只给汤不给勺子 的那种。然而鸡汤文并非总是不好的，许多鸡汤文的观点挺能发人深省。另外，鸡汤文都是“金句集中营”，能提供不少写作素材。\n金句集中\n人生最大的遗憾不是“我不行”，而是“我本可以”。 约翰·伍登：在开始责怪他人之前，你并不算一个失败者。 迈克尔·乔丹：我有超过9000次投篮没有命中。曾经输掉300场比赛。有26次，人们相信我会投出决胜的一球，但是我没有。 卡罗尔·德韦克：真正的自信，是有勇气敞开心扉去欢迎新的变化和想法，不管它们来自何方。真正的自信来源于你的思维模式：你已经做好了成长的准备。 阿尔弗雷德·比奈：一开始聪明的人不一定到最后还是聪明的。 曾嵘：我又想起乔丹告别赛的那个闭眼罚篮。牛逼的人果然心理承受力超强。\n固定型思维和成长型思维\n通用电气集团CEO杰克·韦尔奇，是公认的具有成长型思维模式的CEO。1986年，在韦尔奇上任CEO一职的第六年，通用电气收购了华尔街的一家投资金融公司。收购完成后，该公司被爆出非法交易丑闻。几年后，公司的一名证券交易人为了提高自己的收入，故意制造了涉案金额高达上亿美元的虚假交易。对此，韦尔奇采取的做法是，亲自给通用电气的14名高级管理人员打电话，告诉他们这个坏消息，并向他们道歉——“对于这场灾难，我非常自责。”他没有因为自己坐在CEO的位置上时出现这样的丑闻感到愤怒，也没有因为担心此次丑闻影响大家对他领导能力的判断而选择去隐瞒、去压制负面消息，更没有将责任推给任何人。\n这次新冠疫情中，美团新吸纳了7.5万个快递小哥入职，没有按照餐馆商户的诉求，降低平台费，而是坚持将80%的平台费结算给了快递小哥。按理说，应该降低平台费，让餐馆多点喘息，毕竟这次疫情带来的伤害值，餐饮业位居前列。但是，根据梁宁的看法，降低平台费这个动作，看似很对，不过没有产生行业增量。美团把有限的资源，用来强化枢纽（外卖小哥），导入更多的流量，让整个生态有更强的、对抗漫长寒冬的能力，具有长期价值。\n做一个成长型思维的人\n接受当下的自己，不要否认自己的固定型思维模式。你只有正视它，才可能去解决它。要接受每个人都有一部分固定型思维模式。每个人的思维模式都是成长型和固定型的混合物，这没什么可羞愧的。 凡事试一试，不给自己设限，总是愿意尝试新事物，不怕犯错，只怕原地打转。 坦然接受失败，从中学习。成长型思维者的眼中，失败是一记警钟，是成功路上的垫脚石，可以给人无穷的动力。 随时做好成长的准备，随时意识到自己是可以改变的。成长型思维并不会强迫任何人去追求某件事，它只是告诉我们可以拓展自己的能力。 工程师如何从技术转型做管理？ 关键词：包容心，自我控制，时间管理，教练思维\n工程师如何从技术转型做管理？\n曾嵘： 很巧的是，我也写过类似的文章，但上文作者在文中体现出来归纳能力比我的更好。\n在技术和管理中选择 怎么做好互联网公司的技术负责人？ 工作选择与职业规划 程序员35岁的退休年龄虽然只是贩卖焦虑的一种说法，但是整个行业对人的要求越来越高是不争的事实，要求我们的成长速度必须跟上。\n什么样的工程师会被提拔做管理？\n技术能力强。常用技术的深度和宽度缺一不可，架构能力非常关键。否则技术方向都把握不好，技术决策也容易出问题。\n不仅要熟悉业务，而且应该具备比较强的业务意识，能从技术维度提出好想法，帮助业务拿到更好的结果。\n软性素质达标，核心的两点： 沟通协调能力强，做事靠谱。\n做管理的初衷\n以下几种常见的初衷是不靠谱的。“外部因素驱动”是很难在管理的路上走远的。\n技术不能做一辈子，很多前辈在能力达到一定水平后都转管理了，自己也这么想 在技术路线上遇到了晋升瓶颈，想尝试下管理方向，看自己是否合适 公司发展太快了，老板让我带团队，自己也没办法 管理者工资高，在别人眼中是优秀的代表 指挥做事即可，可以脱离执行层面，越往上走越轻松 真正想在管理岗上走远，需要认可技术管理岗位的价值所在，并且能激发个人的投入意愿。考虑以下目标：\n对于公司：能带领技术团队支撑好业务，帮助业务实现公司定的战略目标。 对于团队：规划好方向，别让组员瞎忙，同时能帮助他们成长。 对于个人：提升自身的技术和管理能力。 同时思考，你是否对管理工作充满热情并且享受这个过程？比如项目协调、制定流程并推动落地执行、招聘。如果你只喜欢做技术相关的工作（比如架构设计、技术评审等），那么还是走技术路线比较好。\n困惑和挑战\n时间不够用：各种会议和日常事务，时间被碎片化，还要分精力在一线 coding。 嫌组员效率低：任务交给组员花的时间超出你的预期，开始自己着手处理，久而久之习惯在一线。 恨人际关系复杂：每天和不同职位，不同 Level 的人打交道，推动很难，感觉情商不够用。 成就感不强：有时会收到上下平级的负面反馈，开始质疑自己的管理能力，有较大落差。 不敢放弃一线：担心自己的能力会停滞不前而不敢放弃一线工作，导致精力跟不上。 转型心智模型\n学会从团队的角度考虑问题\n团队能力不提高，leader 永远不会解放，这是作为 leader 应该具备的意识。leader 应该扮演好教练的角色，而不是救火队长。你要做的仅仅是观察、给一些指点、适当给予时间上的支持。\n员工思维 Leader 思维 我行我上 教会其他人，追求团队成长 出问题责怪、抱怨 事前做好提醒，事中做好支持和跟进，事后做好复盘和分享 技术规则主要考虑自己会不会，有没有技术提升 技术规则通盘考虑对团队整体的技术提升、复用性、业务影响 归功于自己 弱化自己，归功于团队中表现优秀的人 注重执行细节\n切忌被放权式的管理方式洗脑。放权式管理对团队整体能力要求很高，它适用于工作流程清晰，团队骨干目标认知以及自驱力很强的团队。在团队成长初期，一定要注意细节，手把手带员工，要员工知道标准如何，慢慢培养出团队骨干。在这个过程中积累管理经验。需要清楚了解每一个人的特点，用 Leader 思维和他人反馈来促进自己的成长。\n学会用人所长，具备包容心\n有些leader在技术上非常强势，技术权威不容有任何挑战，当组员提出更合理的技术方案时，他会用职级强制要求按自己说的执行，根本不做任何解释。这会打击组员的积极性，消磨组员的创造力。如果组员和leader的技术方案都可行，应该将选择权交给组员，给组员足够的自由发挥空间。\n重视情商，做好自我情绪控制\n面对不同职位、不同level、不同性格的人，你要反复琢磨采取何种沟通方式和沟通技巧。情绪很容易传递，如果leader碰到不爽，不能把组员当做出气筒，这是不专业的表现。有三个建议：\n保持积极乐观的心态，提高自己面对问题时的承受能力，情绪化是解决不了问题的，只会加大解决问题的难度。 能够自我反省并吸收别人的反馈，做得不好的地方要勇于正视并且持续改进。 培养亲和力，不要觉得自己是leader就带着架子，要有一种 鞠着 的姿态，能够尊重人并且真诚待人。 曾嵘：对外不卑不亢，对内就要“鞠着”\n做好时间管理\n把技术 Leader 做的所有事情分类：需求评审，资源规划和项目排期，技术评审，团队周例会，研发规范制定和落地，项目管理，技术调研，架构设计，coding，紧急任务协调和处理，业务以及新技术充电等。把上面这些内容按照优先级授权给团队骨干。\n大部分时间 leader 是不需要亲自写代码的，但是如果有需要，leader要能够随时顶上，不能长期远离一线，纸上谈兵，导致技术决策失误。可以将重点放在架构设计、代码审查、技术调研、以及一些框架性的代码开发上，这些事情对于维持技术优势是足够的。\n曾嵘：对于技术 Leader 而言，我的经验是，管理维度的时间可以安排在 30%~50% 之间。\n用上帝思维来解释裁员 关键词：经济反弹，资源分配，降薪，裁员\n常人不解朱啸虎，读懂已是被裁人\n朱啸虎：“一定要迅速坚决地降成本，越早砍成本，对公司和对员工都越负责任、越有利。对公司不用多说，对员工，越早砍掉员工，他们就能在市场上可能更容易找到工作。越到后面，找工作的机会是越来越难的。”\n朱啸虎：“砍成本一定要一次性砍到骨头里面去，最忌讳的是慢刀割肉，这样员工他不知道你什么时候能砍（成本）到头，他们都没有信心在公司工作。”\n疫情对经济的影响可能会超过 1 年，经济反弹的曲线可能是 U 型而不是 V 型。\n国内前8大省份在今年1月到2月的企业注销数量，都超过了1万家。\n忘掉过去取得的业绩，假定今天开始是重新创业。思考“什么事情是必须做的，什么人是必须留下的”。\n三种降低成本的方式：\n一种是直接裁员，务必一步裁到位，这样尽可能降低对团队人心的影响； 第二种是从高管到基层员工的全员降薪或延迟发放部分工资的方法； 第三种则是让员工停薪留职，只为员工缴纳基本的社保。 朱啸虎回忆自己17年前创业遇到SARS时，公司选择全员只发基本的生活费，直到年底，公司的现金流都缓过来之后，才补发全额工资。说起来，日本“经营四圣”之一的稻盛和夫也曾在公司危难的时候用过这个方法。\n有网友喊出了“企业赚钱时，怎么不给员工多分点？”，这个其实是一种错误的逻辑，或者叫一种“奢望”。\n毕竟员工选择了安稳，选择了固定工资，选择了为别人打工，就已经决定了他的工作、他的个人命运被别人所左右，既然创业的风险已经被创始团队承担了，那普通员工凭什么还要工资以外更多的回报？除非普通员工也与公司共患难，进阶成为核心员工甚至是合伙人，才会享受更高的回报。\n携程创始人梁建章、理想汽车CEO李想、锤子科技创始人罗永浩这些大佬都已经开始通过直播带货的方式为自己的公司创收了。\n新浪副总裁邓庆旭感叹到：“网红主播们可能从没想过，2020年和他们抢直播卖货蛋糕的，是一群曾经只出现在商业杂志封面上的董事长和CEO们。”\n曾嵘：出现在商业杂志封面上的并不是只能有董事长和 CEO 们，网红们也可以啊。\n苏世民和沈南鹏的对话 关键词：黑石，苏世民，十倍速思维，旁观者，比较优势，连接器，结构洞\n文章内容均来自网络公开资料，大多出自新书《我的经验和教训》，以及苏世民与沈南鹏的对话。\n聪明的有钱人怎么想？\n一、如何看中国？加倍下注。 二、疫情之后世界会怎样？世界总会复苏。 三、年轻人如何成长？运用比较优势。 四、如何成功创业？企业家也许是“天生的”。 五、厉害商业的本质是什么？成为连接器。 六、个人如何成功？追寻你的幸福。 七、如何赚到钱？先于大趋势而动。 八、如何管好公司？帮别人实现梦想。 九、如何做交易？胆大、心细、脸皮厚。 十、如何看教育？永不需要退出的投资。 对于疫情中的企业家而言，有三个建议：\n第一，必须要确保有足够的资金，自身不能够很懈怠，要随时做好打攻坚战的准备。 第二，要确保员工能够得到很好的照顾，因为员工是企业家的资产。 第三，必须能够观察到全球未来的趋势。 对于年轻人来说，不要因为薪水高、福利好、位置好、办公室大而被工作牵着鼻子走，你需要关注你是否能在工作中运用你的优势，尤其是你的 比较优势。 能够定义你个人品质的，永远是你在逆境中展现的百折不回的精神和永不言弃的态度，而不是逆境本身。 把时间和精力投入到自己热爱的事物上。热情所至，卓越必成，单纯为了他人的敬仰和尊重做事，则很少能带来成功。\n创业者要有点儿野心。做大事和做小事的难易程度是一样的。所以要选择一个值得追求的宏伟目标，让回报与你的努力相匹配。\n创办黑石时，搭档彼得森打算募集5000万美金，而苏世民则提出要10亿美金。后来筹到了8.5亿。这就是所谓“十倍速思维”。\n如果你看到一个巨大的变革性机会，不要疑虑其他人为什么没有采取行动。你可能看到了他人没有看到的东西。问题越严峻，竞争就越有限，对问题解决者的回报就越大。\n创业者要能讲一个吸引很多人愿意参与的故事，要有说服力，能够说服客户，说服供应商，说服他人加入自己的团队。一个创业者单枪匹马是很难成功的，必须要能让所有这些相关方形成一个体系，要明白与他人打交道的方式方法。\n创业者要有敬畏心。畏惧既能让你加倍努力，也能帮助你规避一些可能犯的错。\n青年的苏世民 想成为一个像电话交换机一样的人，从无数的电话线路中收集信息，对信息进行分类，然后将它们传递给世界。\n掌握得越多，拥有的视角就越多，在竞争对手面前就越有可能发现常规模式和异常现象。所以要始终对进入企业的新鲜事物保持开放的态度，无论是新的人、新的经验，还是新的知识。\n曾嵘： 阿里和腾讯，本质上都是“连接器”。这个“连接器”还有个更唬人的说法，可以在吹牛逼或者故作高深的时候用——“结构洞”。\n谨慎选择你的奋斗领域。有一个原则特别简单，就是跟随自己的兴趣。或者像哲学家约瑟夫坎贝尔说过的：“追寻你的幸福”。坚持强烈的信念，一个人的信念必须超越自我和个人需求。\n人们总觉得最有意思的话题就是与自己相关的话题。所以，要善于分析他人的问题所在，并尝试提出办法来帮助他人。\n永远要黑白分明、百折不回。你的诚信必须要毋庸置疑。当一个人不需要付出代价或承担后果的时候，坚持做正确的事情并非难事。但当必须得放弃一些东西时，你就很难保持信用记录。\n选择进入成长性的行业，着眼于长期利益，理解经济周期，谨慎抄底。反弹需要时间，没有真正意义上的抄底。应尽量挑在资产的价值从低谷至少弹回 10% 的时候才进场，避免“群羊效应”。\n每个企业都是一个封闭的集成系统，内部各个组成部分性能独特却又相互关联。优秀的管理者既洞悉每个部分如何独立运行，也熟知各部分之间如何相互协作。\n尽可能雇用10分人才，因为他们会积极主动地感知问题、设计解决方案，并朝着新方向开展业务。他们还会吸引和雇用其他10分人才。10分人才做什么事都会得心应手。\n1988 年，苏世民从报纸上得知第一波士顿的拉里·芬克（Laurence D. Fink）从银行离职，后者 20 多岁就参与设计了抵押贷款证券化的方法。苏世民马上找到对方，拉里后来进入黑石金融管理公司，苏世民将拉里称为 11 分天才。\n在和帝杰的一次合作中，苏世民又将目光锁定在托尼·詹姆斯身上，后者是谈判过程中唯一力挺公司立场的人。托尼·詹姆斯最终于 2002 年进入黑石，并成为合伙人和 CEO。任职期间，托尼·詹姆斯推动了公司的组织、企业文化改革，并建立书面反馈和公开评论为核心的薪酬管理制度，他被苏世民看作是一个天生的管理者。如今，黑石已经在第三代领导人的带领下继续前进。\n黑石的文化是 不要有旁观者。 如果你在场，你必须发言。你必须说你自己的想法，而不是别人的想法。\n处于困境中的人往往只关注自己的问题，而解决问题的途径通常在于你如何解决别人的问题。\n成功的企业家是具有志在必得的气魄和一往无前的精神的人。他们会在恰当的时刻当仁不让。当其他人谨小慎微时，他们会接受风险；当其他人瞻前顾后时，他们会采取行动，但他们会选择明智的做法。 这种特质是领导者的标志。\n每个人都有梦想，尽你所能帮助别人实现他们的目标。如果你认为一个人的本质是好的，就要随时为这个人提供帮助，即使其他人都离他而去。在别人需要的时候，一个偶然的善意行为就会改变他的生命轨迹，造就意想不到的友谊或忠诚。\n销售其实就是敢于去追求一些“赢了很厉害，败了也没啥”的机会。这类事儿真的是越练越“厚”：\n苏世民读书时让当时受学生欢迎的乐队来自己学校演出，在打了很多电话，动用了很多同学和家人的关系之后，这件事成了现实。 为了读哈佛大学，他想通过电话说服院长录取自己。他失败了，去了耶鲁。 请著名的芭蕾舞演员来给全是男生的耶鲁表演，且不收取任何费用。 在大学的最后一年苏世民决定和有着268年历史的校规做斗争，用调查问卷联合媒体的方式废除了“禁止女性在宿舍过夜”的规定。 极少有人能在首次推介中完成销售。仅仅因为你对一些事物有信念，并不意味着其他人也愿意接受。你需要能够一次又一次坚定地推销你的愿景。大多数人不喜欢改变，所以你需要说服他们为什么要接受改变。不要因为畏惧而不去争取自己想得到的东西。\n交易要果断。时间会对所有交易造成负面影响，有时甚至产生致命影响。一般情况下，等待的时间越久，意料之外的事情就越多。特别是在艰难的谈判中，要让所有人都在谈判桌上协商足够长的时间，以此达成协议。\n1987年9月，苏世民看到当时的股市已经创下历史新高，决定尽快关闭基金。他在10 月15号拿到了所有的钱，4天之后，就是史称“黑色星期一”的美国股灾。 类似的事情还发生在 2007 年，那一年黑石以每股价格 31 美元的价格上市，公开募资超过 40 亿元，成功在金融危机爆发的前夜上岸。 曾嵘：相比而言，老虎基金就没有那么幸运了。\n要在准备好时，而非在压力之下做出决策，或为了达到个人目的，或因为内部斗争，或因为一些外部需求，其他人总会催促你做出决策。但几乎每次你都可以这么说：“我需要更多的时间来考虑这个问题。我想清楚了再回复你。”即使是在最艰难、最令人不快的情况下，这种策略也非常有效。\n忧虑心理不可怕。忧虑是一种积极的心理活动，可以开阔人的思路。如果能正确引导这一情绪，你就可以洞察任何形势下的负面风险，并采取行动规避这些风险。\n读书笔记：《从 0 到 ZARA》 关键词：苟且红利，速度，准确\n苟且红利：只要你比别人多付出一点儿努力，就会轻松地淘汰掉数不清的苟且行事的同行，赢得属于自己的市场机会。\n在创业初期，阿曼西奥的成功之处就是对顾客的反应更快，总是想得比别人多一点。阿曼西奥的叔叔和哥哥白天在商场做销售，搜集市场行情，家里的女人们根据市场行情在车间快速缝纫家居服。男人们一边上班一边推荐自家的家居服。由于他们的产品掌握了市场最新情报，理解了顾客需求，所以卖的很好。\n曾嵘： 这就和游戏行业的“研运一体”类似。如果闷头只开发产品，不了解发行和投放，就很难做到“真正理解玩家需求”。\nZara 的整个产品定位和定价策略：“用大众可以接受的价格销售中高级质量的流行服装。”\n曾嵘：这也是优衣库、小米的思路。现在并非“消费降级”时代，而是“消费降级中的升级”时代。高端方便面卖得非常好，而 MUJI 现在就必须要降价。小米对标戴森吸尘器和吹风机的产品都卖得不错。\n家居服的价格控制，靠的是当时大家觉得奇怪的一条规则：不是根据成本定价，而是根据预期销量控制成本。这样的好处是：家庭小作坊的产能有限，少量生产，没有库存压力，也不担心赔钱甩货的损失。因此可以用适中的价格销售商品。\nZara 的成功在于速度，热门电影女主角穿了一条大牌迷彩裤，Zara 可以在 15 天就上架近似款式。H\u0026amp;M最快都要 20 天。这是因为 Zara 自建了服装供应链上的每个环节。从策划、设计、生产，再到运输和销售，整条供应链都自己掌控。这中间的 5 天，成为了无法逾越的护城河。\n曾嵘：护城河不是天堑，但往往只有一点点区别，就能带来压倒性的优势。和“苟且红利”类似，这看似不起眼的 5 天，实际上区别巨大。这看似多付出的一点儿努力，实际上很多。\nZara 追求的其实不是速度，而是产品的准确。阿曼西奥思考的不是“彻彻底底卖完产品”，而是“怎么生产卖得出去的衣服”。\nZara 卖的主要是时装，时装就像生鲜有保质期，大部分企业都会在换季的第一周上新。但 Zara 每周都会有两次上新。在 Zara 逛的时候，买衣服贡献的是销售数据，试穿衣服贡献的是试穿数据。一件衣服试穿了多少次，Zara 门店每天必须早晚两次上报手机到的信息。有了这些信息，Zara 会对卖的好的产品做出改款，追加生产。\nZara 做设计的出发点不是原创，而是搭配。他们从搭配的角度出发做设计，给顾客提供的解决方案不是从头到脚全部换新，而是满足一个更深层次的需求： 用新衣服搭配好衣柜里的旧衣服。\n读书笔记：《风格感觉——21世纪写作指南》 关键词：组块化，功能固着，知识的诅咒，僵尸名词\n写作之难，在于将网状的思想，通过树状的句法，用线性的文字展开。\n为什么科普书是最难写的？为什么听专业人士讲话总是听不懂？为什么看论文会出现“每个字都认识”但就是看不懂的情况？\n知识是 组块化 的。CPA、IMP、ARPU、eCPM 这些都是在沟通中的“组块”，对于接受者来说，它是一个压缩包，如果接受者没有能力解压缩，就会导致听不懂。\n曾嵘：在面试中，商业谈判中，沟通交流中，可以通过对方对知识组块的理解程度，来判断对方的专业性，避免浪费过多时间。\n功能固着 实验：老师给学生发一盒图钉和一根蜡烛。要求大家把蜡烛固定在墙上而且蜡油不能滴到地上。大家都在想办法用图钉把蜡烛固定在墙上，没有人把盒子固定在墙上。出现这种情况的原因是大家认为盒子是用来装图钉的而没有想到盒子是一个独立物品。这种对事物功能的刻板印象就是 “功能固着”\n知识的诅咒 ：你掌握了一种知识，你就不能理解那些没有掌握这种知识的人是怎么想的。\n知识像一条大河，已经渡过河的人回头看看，感觉不难。但他们无法想象渡河的人所看到的困难。打破知识的诅咒的的唯一方法，就是从读者的世界中获得反馈信号。白居易的这一点做得最好，他写一首诗之后会念给一位大娘听，如果大娘听不懂，白居易就会改写使诗更易懂。\n曾嵘：费曼学习法 就是一种利用 知识的诅咒 特点，反方向验证自己对知识的掌握能力的方法。\n由于知识的 组块化 ，知识传播者会大量使术语、缩写、抽象，大大提高自己学习和同行交流的效率。在领域越久，他们对于术语的 功能固着也越强，离开了术语就无法交流。因为 知识的诅咒 ，他们也意识不到这些属于和抽象概念，大众是无法理解的。\n古典风格 ，就是作者在写文章的时候，要假设自己在和读者聊天，模仿对话的姿态，就像博物馆解说员向游客介绍藏品那样。\n古典风格的作者必须模拟两种体验：和读者对话，向读者展示世界。写作是反本能的，古典风格把写作转换成了自然的两件事：说与看。\n读者在阅读时，文字在他们脑中形成一个个单独的组块，每弄清一些关联，读者脑中就会形成更大的组块。句子变成段落，段落变成章节，如果处理不好，大脑就会调用更多资源来处理，阅读就会更累。\n用相同的主语形成主题链，他们使得读者从一个句子读到下一个句子时将注意力集中在同一个主题。好的文章不只是好的表达，更是好的思维和好的逻辑。在古典风格中要用同一个视角观察物体，用同一个主题思考问题。\n不要在文章中滥用路标。一篇文章就像一个公园，路标多了，读者想要看懂地图就比走路还费劲，失去了路标的效果。路标也不能太少，否则读者会茫然不知向哪里去。\n不要用模糊的语言做缓冲。几乎、比较、大概、一定程度上、某种意义上。这些语言看似客观，其实是免责条款，读者会认为作者不够自信。\n不要滥用强调词。非常、十分、特别这样的词也可能会起到相反的效果。“不可能，小明是个诚实的人”和“不可能，小明是个特别诚实的人”哪句更可信呢？是前者。因为第一种说法是二元对立，第二种说法就有了“刻度”，既然有“特别诚实”，就有“一般诚实”、“偶尔诚实”、“不特别诚实”。强调词加入思考和质疑的维度，削弱话语的力量。\n不要使用僵尸名词。僵尸名词就是把本可以用具体语言描写的内容用抽象的词来概括。不说让别人更信任自己，非要说提升自己的“可信度”。不说让自己更擅长演讲，非要说提升自己的“演讲力”。不说让一辆车变得更安全，非要说提升车子的“安全性”。“没有任何迹象表明将会有取消情况的预期”，这样的说法让原本简介的语言变得冗长，让原本鲜活的文字变得冰冷。\n全文完 ","date":"2020-04-26","description":"","lastmod":"2020-04-26T06:12:50Z","slug":"weekread202017","tags":["reading","weekread","management"],"title":"周读：202017 如何从技术转型做管理","url":"https://blog.zengrong.net/post/weekread202017/"},{"categories":["impressions"],"content":"周读，就是把每周我读过的部分有意思的内容记录下来。有时包含我的评论。这些内容来自我订阅的公众号，读过的书，或者在各新闻 App 读过的文章和报道。\n本周我增加了一个新的栏目 游记，和 周读不同，游记 没有固定的发布频率。\n下面是 2020 年第 16 周的周读内容。\n什么是西方 IgG 抗体阳性就能横着走？！ 读书笔记：《韩剧如何讲故事》 游记：我换皮《动森》只要三个月！ 什么是西方 得到 App：李筠 西方史纲 50 讲\n日本在东方，为什么通常被当做西方国家？ 俄罗斯的文化传统和英美法有一点相似，为什么不是西方国家？ 俄国和英国法国之间的东欧、南欧国家，是不是西方国家？ 亨廷顿总结前人的研究，列出了西方文明的八大特征：\n古典遗产，也就是古希腊罗马文化； 天主教和新教； 欧洲语言； 精神权威与世俗权威的分离； 法治； 社会多元主义； 代议制； 个人主义。 沿着西方独特成长路径成长起来的，就是西方，偏离了这个路径就不是西方。 要站在文明的角度去看西方，要通过理解西方来理解中国。\nIgG 抗体阳性就能横着走？ 抗体检测保证安全返工？WHO警告有抗体不等于能免疫\n轻度暴露于细菌或病毒环境会促使身体启动免疫应答（通过巨噬细胞、B淋巴细胞、T淋巴细胞等）。这一套应答过程会被免疫系统记住，并在下一次遭受同种病毒攻击时迅速调兵遣将，实现对这一病毒的免疫。\n人体接触病原体后免疫系统产生的第一种抗体称为免疫球蛋白M（Immunoglobulins M，IgM），IgM寿命短，仅能在血液中保留数周。但随后免疫系统会继续加工，产生特异性更高的免疫球蛋白G（Immunoglobulins G，IgG）和A（Immunoglobulins A，IgA）。IgG在血液里的停留时间更长，并根据其所预防的疾病赋予人体免疫力，它带来的免疫力可以长达数月、数年甚至一生。\n一些国家正在认真考虑免疫通行证的策略，检测出“有抗体的人”，发放“免疫通行证”，加速复工复产。\n判断“免疫通行证”能否有效应对疫情，可以从下面三个问题入手：\n检测抗体试剂盒的准确性如何衡量？ 试剂盒检测显示有抗体，但是这个抗体是能够中和病毒的有效抗体么？ 体内要有多少抗体才能有效对抗病毒？ 评估抗体检测的准确性，需要考量两个关键指标：灵敏度与特异性。\n灵敏度：能否在体内抗体水平较低的情况下检测到抗体的存在？ 特异性：能否不被其他抗体干扰，检测到特定抗体的存在？ 目前，市面上几家抗体检测试剂盒公布的灵敏度和特异性的数据是：Innovita，灵敏度达到87.3％，特异性达到100％；Cellex，灵敏度为93.8％，特异性为95.6％；Beroni，灵敏度为88.57％，特异性为100％。Biomerica 的测试灵敏度超过90％。\n灵敏度与特异性如同拔河的双方，提升灵敏度就会降低特异性。因为要检测到较低水平的抗体，就需要试剂盒更好地与样品中的所有抗体发生反应。同理，增加特异性就会降低灵敏度，因为抗体分子结构的细微差异可能会阻止我们检测出特定靶标。实际上，灵敏度和特异性均达到95％或更高是一个很难实现的标准。\n抗体检测如果不够准确，带来的后果可能是灾难性的，一个微小的错误遇上一个足够大的样本量时，产生的不良效应是惊人的。若大批不具有新冠病毒免疫力的人被错误识别成阳性结果，获得政府颁发的“免疫通行证”重返街头，就有可能感染、传播病毒，引发难以想象的后果。\n我们还需要考虑一个更为基本的问题：试剂盒检测显示有抗体，但它是能够中和病毒的有效抗体么？抗体产生的机制十分复杂，当遭遇新冠病毒时，人体内可以产生多种针对病毒蛋白质的不同抗体，但其实大多数抗体并没有抗病毒的作用。只有能识别病毒颗粒表面蛋白质的抗体，才可能有抗病毒作用——这种抗体称为中和性抗体，它通过阻止病毒入侵细胞来发挥保护作用。\n我们还需要考虑更多问题：人体内要有多少抗体才能有效对抗病毒？这些抗体又能提供多久的免疫力？抗体的效果是否存在个体差异？\n《韩剧如何讲故事》 编剧不是一门心思创作故事的人，而是 用故事解决问题的人。编辑有三个层次：\n第一个层次，是解决 商业问题。 编剧的剧本必须有商业价值，必须懂得什么题材流行，什么故事好卖，以及什么会被观众反感。编剧要着力于 写一个不赔钱的故事。 第二个层次，是解决 人的问题。 不但要根据某个演员的“人设”设计专门的故事，还要让粉丝满意。 第三个层次，是解决 协作问题。 投资方不满意你的故事，明星不满意你的故事，导演不满意你的故事，你的故事可能被改得面目全非。顶尖的编剧高手要能解决协作问题，让各方面都满意，又能写出一个精彩的故事。同时为了配合资金的使用程度（可能没钱了要烂尾），还能让故事无论发展到什么程度，随时有个合理的结尾。 麦克·卢汉说过，媒介就是信息，内容存在的方式，就决定了内容本身的特点。\n电视要面向普罗大众和全家老小，就必须老少皆宜，不能剑走偏锋。社会上每个时期流行什么题材，和电视行业本身没有关系，但编剧必须抓住重点。可以在经济条件好的时候忆苦思甜，也可以在政治动荡的时候写历史隐喻。\n电视的收看场景是高度开放性的。因此电视的节奏不能太快，要保证观看者走神、接电话、吃饭回来还能接上情节。\n编剧的第一步是塑造角色，一般会设计一个足够细腻，且自带矛盾的人物。细腻不仅表现在人物上，也表现在场景上。例如军营的人物，因为背景可能不够有视觉美感，就可以把大量的配角士兵换成六块腹肌的小鲜肉。\n要解决情节抓人但又不能节奏太快的问题，可以设置过山车式的情节。起点和终点其实是一个地方，坐完车之后，情节并没有往前走一步，但这个过程本身很刺激。例如宫斗戏的女一号和女二号争宠情节，为了不让剧情发展太快，或者由于某个配角表现亮眼让观众很喜欢导致需要给她加戏，就可以设定一个女三号来争宠的情节。由于女三号实力强大，女一和女二必须携起手来对付女三。等女三退场之后，故事情节又会回到女一和女儿争宠的原点。\n广告植入也要足够细腻。例如植入红酒广告，可以安排男主和女主在酒吧相遇聊天。男主说喝完这杯就回去吧。女主拿起来喝了一小口。男主问酒很难喝吗？女主说很好喝，我只是想喝得更慢一点。\n好莱坞编剧教父罗伯特·麦基说： 好故事没有固定的公式，只有必须遵守的原则。\n游记：我换皮《动森》只要三个月！ 点击链接阅读： 游记：我换皮《动森》只要三个月！\n全文完 ","date":"2020-04-19","description":"","lastmod":"2020-04-19T15:28:12Z","slug":"weekread202016","tags":["reading","weekread","COVOD-19"],"title":"周读：202016 IgG抗体阳性就能横着走？","url":"https://blog.zengrong.net/post/weekread202016/"},{"categories":["impressions"],"content":"游记，不是旅游日记，而是 游戏札记。 我把游戏相关的内容记录下来，加一点不成熟的想法。\n和 周读不同，游记 没有固定的发布频率。\nOhayoo 的成功之道 坐拥天时地利人和 多角度解析Ohayoo的成功之道\nOhayoo 是字节跳动的休闲游戏发行品牌。在短短一年的时间里接连打造了《消灭病毒》、《全民漂移》、《我功夫特牛》等十余个个休闲爆款，走出了一条有中国特色的休闲游戏发行道路。\nVoodoo、Say Games、Lion Studios 等驰骋海外的头部超休闲厂商进入中国市场后表现不佳。主要原因是它们的产品框架大多以益智或创作类为主，充分秉承了“超休闲”的极简要义，点开即玩，几乎无引导无养成无目标。这样的设计倾向符合西方玩家的“自由”主义心理诉求。中国玩家则更倾向于有清晰目标感的做事情，热衷于带有阶段性目标的游戏体验。\n另外，海外超休闲游戏的变现主要利用强制的插屏广告实现，一部分原因是为了让那一群 “可能在第一次游戏就流失的用户” 产生部分价值，但中国玩家会认为这是而言这就是“强奸用户”，这是很难被接受的。\nOhayoo 发行的中度休闲类产品都内嵌有养成系统，许多产品都被赋予了足够有特色的题材，更贴合中国玩家的喜好。这些产品既有“超休闲”所具备的单局短、强快感的特质，又有一定的数值增长设计，能拔高产品的中长线乐趣。\nOhayoo 的产品不使用强制插屏广告，主要使用需要玩家主动触发的激励视频广告，这提高了产品的商业变现能力和口碑。对于《我功夫特牛》和《小兵别嚣张》这类中度休闲产品，Ohayoo 采用的策略是：“核心用户聚集到泛用户扩散的分层触达”。\n以《我功夫特牛》为例，Ohayoo 在项目立项早期就确定了结合 ACT、Rogulike、RPG 三大元素进行设计，预埋了打动不同玩家的分层卖点。在第一阶段瞄准“核心用户”，形成攻略。第二阶段推广到次核心和潜在用户，展示武侠剧情元素。第三阶段当产品有了足够的热度后，用 CG 混剪、情境短剧、动画连载、IP 孵化等方式来吸引泛用户。\n在研发立项阶段，Ohayoo 会通过时下热点、行业趋势等维度整合用户群体画像，确定产品核心基调，通过 Demo 测试验证吸量能力。产品调优方面，Ohyaoo 会以数据为核心导向，分阶段对产品的吸量、留存、变现能力展开调优。整个发行的核心就是“数据驱动”与“用户分层”。\n中国厂商被人家换皮了 复刻6年前中国游戏套路，它在海外月收入1.7亿\n塞浦路斯的团队 NEXTERS 凭借一个类《刀塔传奇》的换皮产品《Hero Wars》获得了月收入 2400 万美元的成就，成为了海外市场 TOP5 的卡牌手游。这让擅长卡牌 RPG 的中国发行商惊掉了下巴。\n《Hero Wars》2400万美元（折合人民币约1.7亿元）收入的 TOP3 市场分别是美国、俄罗斯和德国，三大市场收入占比分别为43%、8.6%和6.8%。\n欧美市场从用户基础来讲，即便是2020年，海外玩家仍然以休闲用户居多，且占比远高于亚洲市场。《Hero Wars》的玩家大部分都是之前的休闲玩家甚至是非游戏玩家，通过“广撒网”式的广告覆盖，Nexters在对的时间找到了对的玩家。\n中国游戏出海发行商早在五六年前就转向了 SLG，现在的热点是吃鸡、MMO、二次元。这类产品的研发难度远远高于卡牌 RPG。对于国人来说，看 Nexters 的成功，心里会很不是滋味。这个机会本应该被中国游戏企业瓜分。从另一个角度讲，这也体现了中国出海发行商对全球市场认知上的不足。Nexters 已经被俄罗斯手游发行商 Playrix 收购，该发行商“五年内的目标是成为欧洲的动视暴雪或者网易”。\n实况足球这 6 年 网易这款游戏的表现，或许意味着我们小看了这个品类\n网易和科乐美共同推出的《实况俱乐部》在巴西世界杯（2014）之后，运营了国服近 6 年的时间，依然稳定位于体育类畅销榜 Top30。2018 年网易代理了《实况足球》，该产品经常能进入畅销榜 Top3，这说明这款细分产品的盈利能力和大众品类没有很大差距。\n《实况足球》去年将 havok 引擎替换成虚幻4，成为市面上首款搭载虚幻4引擎的足球手游。随着引擎更新，产品优化了人物模型、球场细节和光影的效果，并将支持的最高画面帧数提升到60帧。产品还支持球员的花式动作，优化了 AI 跑位表现，2020 版本更新了精细盘球和启发机制，让球员模拟出盘带技巧。\n《实况足球》不断尝试打通与真实足球的连接，通过多种形式渗透球迷群体。它在懂球帝、虎扑足球版建立专区，安排官方运营入驻，鼓励玩家产出攻略、测评，手机玩家素材做成游戏集锦，组织圈子内部的挑战赛。通过持续有效的运营，懂球帝圈子的实况足球专区长期排名 Top3，热度甚至高于拜仁、巴萨等老牌足球俱乐部。在线下，《实况足球》参与了曼联的中国行活动，曾举办“实况 719 足球节”，在 32 个城市包场免费请球迷踢球，在广州活动请到前国足成员区楚良现场指导，活动还获得了央视的关注和报道。这些活动在球迷中建立起专业、真实的品牌印象：游戏项目组成员都很懂足球，游戏非常专业，玩家交流都用专业足球术语，和国内外主流赛事都有合作，连知名的球星都在玩这款游戏。\n《实况足球》基于国内玩家的认知，在内容更新、玩法方面定制了本地化的方案。例如和国际服不同的赛季结束数据继承制度，还有绿茵赛、战队赛等玩法。国服开发了《易球成名Club》App，一上线就升至 iOS 免费榜 66 名。\n逼真的足球游戏并不好做。手游要尝试追赶主机游戏的表现力，需要很强的技术实力。真实的联赛和球员需要流程复杂的 IP 授权合作，游戏推广需要线上线下多维度与真实运动捆绑。这些对团队运营能力有很高的要求。\n换皮《动物森友会》要几个月？ 任天堂是怎么做出《动物森友会》的？ 为什么中国没人做得出《动森》？ 《动物森友会》在日本三天 188 万实体版销量，超过了《精灵宝可梦：剑/盾》首周 136 万的成绩。2001 年 N64 平台的《动物森友会》就达到 232 万份，2005 年 NDS 上的《欢迎来到动物森友会》销量达到 1175 万份，2008 年 Wii 上发售的《动物森友会 城市大家庭》销量 338 万份，2012 年 3DS 上发售的《来吧！动物森友会》创下 1221 万份销量。\n《动森》最早设计的时候考虑的是迷宫探险RPG，准备在 N64DD 上推出。后来改为使用传统卡带推出，N64 支持 64MB 闪存，但传统卡带仅支持 1MB 闪存，因此游戏被逼大幅删减内容，仅留下了“场地构造”和“玩家之间的交互”。从立项开始，初代《动森》绕了一大圈弯路。来回推翻重做，折腾得开发成员们都快挂了，才摸清楚了制作方向。\n由于设计《动森》的是一帮“社畜”，每天工作太忙，希望能回家不继续肝，能做一款和家人在 不同时间一起玩 的游戏，导致《动森》被做成了一个“过日子的游戏”。\n野上说了一个场景：“晚回家的父亲打开《动森》，发现孩子留下的消息，然后给孩子准备想要的东西，再留言给孩子‘这些事搞定了’。”带着这种心情，他们在游戏里加入了留言板功能，说这就好像对孩子的赎罪：“抱歉，总是回家太晚。”\n岩田聪评价说：《动森》的特别之处在于，它是一款让大家想拉朋友、家人一起来玩的游戏，而不仅仅是想要推荐给别人。\n一名同事一家三口都特别喜欢玩NDS上的《欢迎来到 动物森友会》，其中，儿子的母亲非常喜欢钓鱼，以至于钓得太多，被儿子抱怨“妈，快别钓了”。于是，她在儿子面前就忍着不钓，但是背后还是一个人偷偷钓鱼。但是有一天，儿子在玩的时候，动物跑过来找他聊天，刚好说到他的母亲“那家伙，整天都在钓鱼啊”。千算万算，没想到动物的闲聊，让他的母亲露了馅儿。\n2012年底《来吧！动物森友会》发售之后，最热衷这款游戏的，竟然是19~24岁的女性群体，这个年龄层原本并不是任天堂用户中最大的群体。因为这款游戏的畅销，3DS在当地销量闯过了1000万大关。3DS原本的硬件销量中，男女比例是69%对31%。但在购买《来吧！动物森友会》的人群中，男性只占44%，女性则占到了56%。\n这一款游戏，火了 20 年。“开发组的成员，没人会考虑游戏能卖多少分。完全凭借着让客人满足的执念，以及自己也喜欢游戏的纯粹想法，在制作游戏。”《动森》的制作者们一直在构想着，玩家看到这些东西后会有怎样的反应。这种思路，是宫本茂的团队在任天堂做游戏的一贯策略：尽可能地想象玩家玩的场景，什么环境、什么情形、和什么人一起，在什么时间玩。\n这 20 年里，中国的开发者在干什么呢？\n我们会说：这有何难，我换皮最多三个月！\n不管是端游、页游还是手游，在中国似乎都是休闲开局，重度收尾。错过了流量红利期的休闲品类基本就不会再出现了。游戏立项大部分也都是先定付费模型保证LTV，再思考怎么包装吸量就完事儿，推广基本以流量获取为主，而流量又是价高者得。曾经的游戏行业，不管是渠道还是广告平台，用户都是混着卖，随着市场竞争越发激烈，只要LTV不够高，管你什么品类都拿不到流量。所以游戏只能越来越重，LTV越炸越高。\n2018 年的时候，做微信小游戏，大家见面都是这么开场： 你抄这款要几天？\n现在，微信小游戏上的重度产品已经越来越多了，以我的经验看，做一款至少也要三个月吧？\n全文完 ","date":"2020-04-16","description":"","lastmod":"2020-04-16T12:44:27Z","slug":"gamenote20200416","tags":["gamenote","reading"],"title":"游记：我换皮《动森》只要三个月！","url":"https://blog.zengrong.net/post/gamenote20200416/"},{"categories":["impressions"],"content":"周读，就是把每周我读过的部分有意思的内容记录下来。有时包含我的评论。这些内容来自我订阅的公众号，读过的书，或者在各新闻 App 读过的文章和报道。\n这次的周读是两周的内容，这两周读了三本长篇科幻《世界的误算》——完美缺陷/生而为人/永生之惑，疫情期间紧绷的神经也稍稍放松了点。果然读小说的酣畅淋漓感是最好的休息。\n另外，我发现算周数算错了，4 月 11 日（周六）应该是2020 年第 15 周才对。这次就将错就错两周合一周了。\n很多朋友问我武汉终于解封是什么感觉。嗯……其实没有什么感觉。媒体宣传的“武汉解封”对于武汉市民的象征意义大于实际意义。小区依然是封闭状态，复工依然没有全面开展，所以我们也只能继续居家在线办公。\n下面是 2020 年第 15 周的周读内容。\nVLCC和国内石油地板价的争议 高效在线办公的核心实现升 读书笔记：《黑匣子思维》 学习笔记：疫情中眉州东坡怎么做到在武汉不关店的？ VLCC 和国内石油地板价的争议 国际油价大跌，国内油价为什么不降？ 派84艘巨轮抄底石油，中国成为“石油大战”最大赢家？ 关键词： 成品油，调价窗口，地板价，VLCC\n中国的成品油定价是与国际原油接轨的。2019 年，中国对外的石油依存度高达 70%，任何一个产油国都不能忽视中国这个市场。\n国内成品油有天花板价和地板价，天花板价格为 130 美元每桶，地板价为 40 美元每桶。3 月 17 日，国际油价已经跌到 40 美元以下，因此国内的成品油价不再下调。\n我国是贫油国，本土石油的生产成本较高。如果不限价，进口石油会冲击甚至摧毁本土石油产业，这会导致国家能源安全受制于人。\n成品油地板价和粮食托市收购制度类似。粮食收购制度获得了一边倒的好评，但成品板地板价却受到了批评。主要原因是粮价的背后是无数农民，油价的背后是三个石油寡头。\nVLCC， 全称为：Very Large Crude Carrier，中文名为超大型油轮，或者巨型油轮。其长度超过300米，宽度可达60米，载重容积34万立方米，载重吨位在30万吨左右，折合成油桶为 200 万桶。\nVLCC 造价高，折旧费贵。资讯网站 Seaway 海事新闻3月17日消息：一艘VLCC的日平均收入从2月中旬的每天30000美元上升到上周的每天210000美元，增长了600%。\n20年前，中国还造不出 VLCC，近 5 年，中国建造的 VLCC 占世界份额三分之一以上。现在，中国共有大约 120 艘 VLCC。\n2019年，中国原油进口额为16627亿元，占当年货物进口总额143162亿元的11.61%。\n中国的石油战略储备，始于 2003 年。2006年10月，中国首个国家石油储备基地——镇海基地建成交付使用。据公开资料，中国正在规划建设战略石油储备三期工程。整个项目一旦完成，总规模将达到5.03亿桶。\n高效在线办公的核心实现 这是一篇单独的博客：高效在线办公的核心实现\n读书笔记：《黑匣子思维：我们如何更理性地犯错》 《黑匣子思维：我们如何更理性地犯错》\n关键词： 闭路循环、认知失调、叙述谬误、过强自尊\n黑匣子是飞机上的一种记录设备，会记录飞行过程中驾驶舱的所有声音以及飞机上所有电子系统的指令。黑匣子非常坚固，如果飞机发生了事故，黑匣子一般不会被损坏，其中的数据可以帮助人们找到事故原因。\n闭路循环和开路循环 黑匣子的这种从错误中学习的方法帮助航空业成为了世界上安全系数最高的行业之一。黑匣子能让人们可以从过去的教训中迅速汲取经验来快速进步，我们可以把这种方式叫做 开路循环 。\n下面来看一下 闭路循环 的反面例子，说说被讲烂了的 放血疗法。这种疗法从公元二世纪开始流行了好几百年。流行的原因是没有人质疑它，一旦一个人因为放血疗法康复了，那么功劳就归功于它；若没有康复，可以解释为该病人的病重到放血疗法也无法治愈。\n直到医学领域发明了大样本随机双盲对照实验，放血疗法才被彻底抛弃。这就是医学领域的 闭路循环。令人惋惜的是，直到现在，医学领域中依然有这样的现象：医生对自己的错误行为做出各种看似合理的解释，例如技术性错误、事故无法避免、患者不必知道等等。这种现象可能来自两个原因：\n一个原因是 认知失调。一个人一直相信的东西受到外部事实挑战的时候，为了维护自己的形象，就会选择寻找借口、忽略证据的方式替自己辩护。比如许多误入邪教的人为自己找的借口，医生也是人，碰到这种情况也一样。\n另一个原因是人们认为犯错就等于无能。很多人期待医生做到“尽善尽美”，医生也一直相信自己应该做到尽善尽美。外界的巨大期望压力，加上对自己不切实际的高标准要求，让医疗工作者非常害怕失败而尽量采取回避的态度。\n传统上我们对错误的态度很苛刻， 对错误的指责导致了人们对失败的恐惧和抗拒。 失败的人们会寻求自我安慰，搭建了一堵阻碍进步的墙。\n用科学的方法面对失败 联合利华在生产洗衣粉的时候经常碰到喷头堵塞的问题，数学家团队经过了长时间的研究，都没有解决。生物学家用简单粗暴的试错办法，测试了 450 种喷嘴，解决了堵塞问题。最终设计出来的喷嘴，是所有数学家都想象不到的。\n2007 年卡佩罗做了英格兰足球队的主教练，球队非常严明，球员玩什么吃什么都要严格掌控。英格兰队成绩也不错，人们都称赞教练的风格。但世界杯赛场上英格兰队失败了，于是舆论开始批评主教练的训练太严格导致球员不敢发挥。\n这就是 叙述谬误。 它让我们编造故事来描述自己所见的行为，让我们太想对过去的事情做出解释，以至于会用同样的原因去解相反的事实。\n一个球队是否能赢得比赛有太多不可控的的因素叠加。长期看来，结果是随机出现的，但人类的大脑不适合处理复杂结果，人类了解世界的过程偏向于简单，几百万年来的进化让我们更相信直觉，甚至在面对问题的时候给自己讲故事。\n从失败中学习 不要被过强的自尊心干扰。 作者在牛津大学发现一些好学生会在考试前夜故意醉酒或者去很远的地方来逃避考试结果。因为他们对于考试结果太过担忧，因此蓄意毁掉考试机会以给自己找到 不失败的借口。\n要减少对于他人错误的谴责，创造允许失败的文化氛围。 2007 年伦敦有一个婴儿被母亲虐待致死。当地舆论更多地批评了当地的社工和儿童服务中心。这非但没有改进工作，反而导致没人愿意从事这方面的工作，孩子们的安全更加没办法保证。\n伦敦有所中学有个“失败周”的年度活动，他们会邀请父母、老师和各个领域的意见领袖去分享自己的失败经历，展示他们在成功之前是如何与失败相处的。\n用以失败为基础的方式提前做预演。 “死前验尸” 法：在项目开始前告诉所有人，项目已经失败，要求所有人做一个思想实验以推演项目失败的原因，让自己找到的理由尽可能的合理化。这可以找到新项目的盲区，帮助团队更好的成功。\n学习笔记：疫情中眉州东坡怎么做到在武汉不关店的？ 这是一篇单独的博客：疫情中眉州东坡怎么做到在武汉不关店的？\n全文完 ","date":"2020-04-13","description":"","lastmod":"2020-04-13T03:00:10Z","slug":"weekread202015","tags":["reading","weekread"],"title":"周读：202015 在线办公效率和国际油价的黑匣子","url":"https://blog.zengrong.net/post/weekread202015/"},{"categories":["impressions"],"content":"本文是 得到：跟眉州东坡学危机管理 的学习笔记。\n听这门课的时候，我正在瑜伽垫上做俯卧撑。听到眉州东坡在凯德 1818 的店在疫情期间是该商场唯一开门营业的店，立刻就停下来买了课。等整组练习完成的时候，所有课程也听完了，挺受用的。\n众所周知，武汉是这次新冠疫情的中心点，而黄冈的疫情确诊数量一度仅次于武汉。眉州东坡在武汉和黄冈各有两家店，这些店在疫情期间都没有关门。这让我这个武汉人感觉匪夷所思。\n眉州东坡怎样做到这一点的？在这中间碰到了哪些问题？它的创始人兼总裁梁棣做了说明，我将其总结成下面几点。\n良好的基础 这是一家 24 年历史，100多家店的企业。 这家企业经历了非典事件和汶川地震。 眉州东坡是 2008 北京奥运的服务商。 思考关店要面对的问题 关店后员工会被推到社会上，回家后会强制隔离 14 天，路上有感染风险，心情也不会好。 把员工放回去，会导致复工时无法及时回来，回来后的隔离也会影响复工，造成开工难的情况。 餐饮人要尊重自己的职业道德，做非常时期的战地厨房。 现金为王 眉州东坡在春节期间一共退掉了客人提前订好的 11144 桌餐，涉及的金额是 1700 万。 所有店每月正常人工支出 5000 万，门店每月房租 1116 万，员工宿舍每月房租 295 万。 大年三十卖了 900 多万，二月大约收入 3000 万，三月收入 5000 万，年前拿了一笔 2000 万的贷款。 员工吃住每月 1000 万，工资全额计算，分期发放。 要求所有的款项，没有总裁签字，不允许付钱。 供应链有准备 5 天准备了 20 万个口罩 外卖从不缺餐盒 自建的从农田到餐桌的食材供应链。 员工凝聚力 严格培训，Word/PPT 抗疫指南，每天两次量体温，宿舍消毒，门店隔离室。 《新闻联播》和《东方时空》采访湖北黄冈的店，2 月 7 日电视台播出。 很多员工看哭了，员工家长和家属看了也很自豪。这是最正能量的广告。 业务发展 堂食转外卖，主动联系押金转成储值卡，或直接退押金。 免费口罩手套，桌子拉开 1.5 米，专属包房分餐。按奥运会送餐标准处理 小程序转成菜站销库存，进而转成专门的线上销售平台。 半成品熟食，让中央厨房有活干，和物美超市合作卖货，群拼单打折卖货。 高标准，严执行 定出详细标准，进入 BI 系统。 每日巡店。 每天晚上视频例会。 严格管理，警告、降职、降级立即执行。 主动沟通，坚持沟通 因为武汉政府的统一要求，武汉凯德 1818 商场要求关店。 店长主动沟通，坚持开店是做公益，免费给医护人员送餐，做战地食堂。 最终店没有关，还得到了武昌区区委书记带队拜访。 曾嵘： 要做成事情，需要坚持。需要想尽一切办法去做成它，而不是听之任之。\n眉州东坡能做到这些，和它们在疫情期间坚持不关店，保持团队正常运营有很大的关系。我在 高效在线办公的核心实现 一文中谈到过，在疫情期间能感觉到 SAGITEAM 的运转更高效，大家对工作的热情也更加高涨。\n有时候 “被逼无奈” 也是一种动力，视角转换也能促进创新。当危机来临，团队要去接受挑战的哪一刻，团队所有力量都会爆发出来。\n全文完 ","date":"2020-04-12","description":"","lastmod":"2020-04-12T16:04:51Z","slug":"open-in-wuhan-with-covid-19","tags":["life","management"],"title":"疫情中眉州东坡怎么做到在武汉不关店的？","url":"https://blog.zengrong.net/post/open-in-wuhan-with-covid-19/"},{"categories":["impressions"],"content":"从上次写 SAGITEAM 的在线办公经验 一文开始算起，时间又过去了 1 个月。\n从 2 月 3 日开始，SAGITEAM 已经在线办公了 2 个月。我们依然可以再次自豪的说，我们还原了集中办公 85% 以上的生产力。在特定的岗位和时机，在线办公的生产力甚至超过了集中办公。\n这篇文章不再调侃，我会基于 SAGITEAM 这两个月的经验，认真分析一下实现高效在线办公的关键点。\n1. 我们做到了什么？ 1.1 项目进度控制 在线办公当然给我们造成了影响。我在 SAGITEAM 的在线办公经验 一文中进行过详细介绍。在开始在线办公的两周里，大家花了不少时间熟悉在线办公的节奏，形成办公习惯，效率会偏低。在线办公三周之后，武汉疫情趋于相对稳定，物流状态可以期待，我们使用各种方式解决了最棘手的设备问题，办公效率逐渐回到了正常状态。\n在线办公期间，我们并行了 4 个项目：B、G、S、T。其中 2 个项目(B、S)发布版本频率为每周 1 个。G 项目在这 2 个月里共发了 3 个版本，其 OKR 接近达成。\n在线办公期间，项目没有出现大幅、持续 delay 情况。\n1.2 会议的力量 在线办公期间，我们一共开了 2 次全员在线会议。所有项目组都有每日晨会（15 分钟），T 项目每日上下班都有工作会。掐指一算，这两个月组织了工作会议 240 次，会议记录 200 多条。日常工作中，为了保证沟通而临时拉起了比工作会更多的语音会议，有些同学在办公期间直接就不关麦，实时连麦工作。\n尽管所有人都讨厌开会，但在这样特殊的时刻，大家都发现频繁主动的沟通，是高效工作的基础。\n1.3 工作时间的延长 不得不提的一点是，大家主动延长了自己的工作时长。这是我最不愿意看到的一点。正如一位同事所说：“终于过上了下班就睡觉，起床就开工的梦想生活了。”\n在线办公的便利，以及疫情期间不能出门的特殊氛围，让大家进入了“不工作似乎也没有其他事情好做”的状态，这或许是工作时间延长的一个原因。\n2. 提升效率的关键点是什么？ 2.1 汇聚对的人 找到认同团队价值观，并能一起接受挑战的团队成员，这是高效率团队打造的最关键因素。\n一个团队要拥有强烈的风格，才能吸引到足够好的人。也只有自驱力强，目标明确的人，在面对在线办公这种较为极端的工作条件时，才能迸发出更大的生产力。\n团队中的每个人都需要有归属感。有了归属感，才会有更好的责任心，才会把这个团队当成自己的团队，才会守护这个团队，而不是只把这当成一份挣薪水的工作。管理者要有能力在招聘过程中分辨出有潜力的人。并能不遗余力地把所有的新员工打造成有归属感的团队成员。\n例如，在线办公需要“文档驱动”。在一个”文档驱动“的团队中，如果有员工不愿意读文档，不愿意写文档，就无法融入团队，也无法获得团队其他成员的认可，更无法产生“归属感”。汇聚对的人，就是找到“臭味相投”的人，这需要团队形成自己的“味道”和“高标准”。\nAutomattic 是 Wordpress 的母公司，它在 75 个国家拥有 1000 多名雇员的，完全使用远程办公。Automattic 认为：员工的自我激励能力、是否擅长书面交流能力是远程工作中最重要的能力。形成最具生产力的关系需要经过数年，而不是几个月。如果公司把所有人都视为短期员工，那么在员工的投入上，管理者就会基于短期化形成截然不同的做法和态度。因此，忠诚应该是相互的。\nMegaEase 的创始人陈皓认为：我们并不是在找技术最好的人，而是在找有自驱力、有热情、学习能力强、基础不错、有冒险精神的人。这样的人会比单纯技术很好的人更适合团队。因为技术是死的，只要你好学，就一定能学会，而个人素质和做事方式是很难改变的。\n2.2 每个人都有自己的目标 需要把目标公布出来。\n团队的目标必须由团队创始人来确定，并不断强调，让团队所有人都能随时知道团队要完成的是什么。这个目标可以写在团队内网首页，可以在每次团队会议都公布一遍，也可以在日常谈话中不断重复，甚至可以把这些目标挂在洗手间镜子边。管理者需要明确的一点是：只有当你把目标讲到所有人都听烦了的时候，人们才可能记住这个目标。\n当团队成员记住了团队目标，他们才会在心中制定自己的目标。团队中的每个人，都需要有自己的目标，这些目标应该和团队目标统一，它们应该是团队目标的一部分。\n更好的方案是明确要求所有人制定自己的目标。可以使用 OKR 这个工具来进行目标管理和同步。\n2.3 严格的规则 人类天生懒惰。\n懒惰来自大脑，终结于理性。规则就是理性在团队中的表现。\n大多数时候，人们是不愿意偷懒的。但如果没有合理的规则进行约束，人们就可以“合理地”偷懒。 这是管理者的问题，不是团队成员的问题。\n在 SAGITEAM 在线办公期间，我们制定了 每日四次的考勤打卡、每日晨会、会议记录 等规则，这些规则在两个月时间内得到了严格的执行。从结果来看，这些规则达到了意料之中的效果。\n2.4 找到好的人 有个段子是这么说的：“我们也想招好的人，可是并没有什么好的人”。\n的确，在团队的不同发展阶段，可能因为 薪酬条件、办公条件、团队规模 等等各种原因找不到 好的人 。但所有的大公司都是由小公司发展而来，为什么别人能做到呢？\n我认为，无论公司处于什么阶段，依然要坚持足够高的面试标准。对于 不够好 的人，若符合当前需求，可以进入团队后严格要求，与团队一起成长；对于 足够好 的人，可以进入团队后负责重要项目和工作，使其发挥出全部能量。\n对于初创团队，在人才上最重要的一点是： 不要停止招聘。 团队负责人必须亲自参与招聘过程，不仅是参与面试，更要参与人员选拔，人才发现和人才关系维持。\n就像 汇聚对的人 中谈到的那样，好的人除了个人能力和发展潜力，还要符合团队风格，形成团队归属感。团队应该有清晰明确的选拔和淘汰机制，保证团队不断更新和成长。“淘汰”听上去有些不近人情，但这就是世界的运作规律。团队和人体一样，需要新陈代谢。团队不断成长，在不同的阶段需要不同的人才，如果成员不能和团队一起成长，就只能被淘汰。公司的人才更新、团队的不断整形是一个长期、持续性的工作，“团队成型”并不是一个最终概念，只是团队成长的一个阶段。\n3. 怎样做到更好？ 团队自身必须足够强大。有足够好的产品，就能吸引更好的人才。\n团队自身要持续成长，做学习型团队。团队中的管理者需要保持旺盛的精力，不断学习和成长。学习型团队的形成需要团队管理者的细致规划，也需要团队全体成员的共同努力。\n团队要保持开放的心态。不可固步自封，不可满足于当前的成就，要掌握行业内主流动向，快速调整方向，积极拥抱变化。\n4. 相关阅读 估值 30 亿美元，连续 15 年纯远程办公，这家公司做对了什么？ 为什么招不到最好的程序员？StackOverflow 创始人有些建议 想提高远程办公效率？先学习这些“军规” 能力不错的大厂高P，为什么过不了小厂的试用期？ 远程办公监控软件卖爆了：5分钟一拍照、10分钟一截屏，这不是第一次了 别被“远程办公”吓住，要善于抓住管理的本质问题 疫情下的远程办公，聊聊我的经验和实践 全文完 ","date":"2020-04-05","description":"","lastmod":"2020-04-05T09:59:45Z","slug":"efficient-online-office","tags":["life","management","sagiteam","COVOD-19"],"title":"高效在线办公的核心实现","url":"https://blog.zengrong.net/post/efficient-online-office/"},{"categories":["impressions"],"content":"周读，就是把每周我读过的部分有意思的内容记录下来。有时包含我的评论。这些内容来自我订阅的公众号，读过的书，或者在各新闻 App 读过的文章和报道。\n下面是 2020 年第 14 周的周读内容。\n韩国式抗疫 GitHub不断封禁开源项目 读书笔记：《冥王星沉浮记》 回形针为啥被喷 治疗新冠肺炎已经花了多少钱? 读书笔记：《绩效管理全流程实战方案》 韩国式抗疫 财新：韩国做对了什么\n韩国从 1 月 8 日发现首例“不明原因肺炎”疑似病例后，到 2 月 17 日，境内也只有 30 例确诊病例。文在寅甚至预言疫情不久就会消失。但“新天地教会”教众的集中爆发让韩国的提前布防带来的效果全部失效。2 月 29 日，韩国新冠肺炎确诊人数单日新增 909 例。\n随后，韩国对超过 20 万名“新天地”教会信众开展全面排查，并在 2月 26 日通过了《传染病防治管理法》《检疫法》《医疗法》，统称“新冠三法”，对违反新冠三法的人员进行 300 万韩元到 1000 万韩元的罚款。\n韩国也“抢跑”了病毒检测试剂生产。在 2 月 4 日，韩国公司 Kogene Biotech 就率先获得韩国食品医药品安全处（MFDS） 的“紧急使用许可”，上市了第一批新冠病毒检测试剂。以往 MFDS 的审批流程耗时长达一年。到 3月 16 日，韩国有 5 家试剂盒企业获得 MFDS 生产许可，4 家实现量产，韩国日军检测能力在 1.2 万人左右，最高时可达 2 万人。\n在韩国，有50个“免下车”检测点。开车10分钟就可以完成检测，有 600 个监测点和 110 个实验室，在疫情爆发的两周内检测了超过 12 万人。核酸检测费用不超过 16 万韩元，密切接触者和明显症状者都可以免费检测。截止 3 月 20 日零时，韩国共有超过 31 万人进行了核酸检测，累计确诊患者 8652 人，死亡率约为 1.1%，比意大利、伊朗、中国低。\n韩国大邱碰到的医疗资源短缺现象和武汉爆发期类似，但与武汉的“方舱医院”不同，韩国式的“方舱医院”选在郊区，以酒店为基础进行集中医学隔离。\n拐点来得很快。3月 6 日后，韩国境内单日新增确诊病例数开始下降，曾一度回落到 2 位数增长。\n韩国自始至终没有禁止他国人员入境或者本国“封城”的举措，也没有限制居民的活动自由，只是要求入境人员严格监控 14 天。疫情重灾区大邱市保持各项生意开放，不限制居民行动，但政府强烈要求居民小心谨慎。在这样的措施下，韩国能控制住疫情非常不容易。\n然而胜利时刻还未到来。3 月 19 日，韩国日新增病例数在时隔五天后又上升到了 100 例以上。\nGitHub不断封禁开源项目 AI前线：怀疑开发者在“造核弹”？GitHub不断封禁开源项目\nGitHub 去年封禁了伊朗等地区的账号，本周封禁了一个属于微软的前端开源项目 Aurelia，理由是项目中有两名来自伊朗的外部贡献者。\n一位勤奋的俄罗斯小哥 Nikolay 去年有 3,236 个 contributions，但 GitHub 账号被封了。经过长时间的申诉，他发现被封的原因是因为他在 GitHub 上开玩笑地称一个家伙为 prick。对方因为感觉被侮辱而申诉，GitHub 没有给 Nikolay 任何申诉的机会，也没有给他任何通知，直接就封了他的帐户。\n伊朗的开发者更倒霉。整个伊朗地区的 GitHub 帐号均无法使用，而且这些开发者并没有得到备份的机会。克里米亚、古巴、朝鲜、伊朗和叙利亚也有同样的情况。\nAurelia 项目最终得以解封。该事件在 HackerNews 上成了头版新闻，GitHub 的 CEO 也对此表示了歉意。\n本篇报道结合 周读：202013 被下架到底是谁的错？ 一起读，味道更好。\n曾嵘：GitHub 在成为了全世界最大的同性交友平台，被微软收购，程序员找工作都要在简历里面贴出自己的 Github 地址之后…… GitHub 已经不可能独善其身，标榜自己是一个非盈利组织了。\nGitHub 首先是一家美国公司，其次是一个社交媒体，再次才是一个代码托管网站。当它背负了更多的社会责任和国家意志后，是时候改变初心了。\n对了，作为一个 2008 年就开始开源事业的码农，这是我的 GitHub 主页：\nhttps://github.com/zrong\n《冥王星沉浮记》 曾嵘：这是篇读书笔记。因为是杂书，就没有单独成文，放在周读里面就好。\n在那个时代，发现太阳系新的大行星能给发现者带来巨大荣誉。天王星被发现后，其发现者米歇尔被英国国王授予了贵族身份，从业余天文爱好者一举成为皇家天文学家。海王星的发现者勒维耶，也从巴黎天文台的普通工作人员成为了巴黎天文台台长。\n海王星的发现，是由于天文学家经过计算发现天王星根据牛顿定律计算出来的轨道与实际观测情况不符，大家认为还有一颗新的行星在影响天王星的轨道。勒维耶计算出了海王星的位置并经观测证实了这个发现。\n曾嵘：勒维耶在 1859 年认为在水星轨道内也有一颗这样影响了水星轨道的行星存在。并把这颗行星命名为Vulcan（祝融星）。1915 年，爱因斯坦广义相对论完成，解释了超大质量天体（太阳）对水星轨道的扰动，人们才放弃了寻找祝融星。详细过程可以读一下这本书 《追捕祝融星》\n美国富商阿西瓦尔·罗威尔把自己的所有财富投身于天文学研究，创建了罗威尔天文台。1930年，罗威尔天文台的观测助理汤博（24 岁）发现了冥王星的身影。英国牛津的一位 11 岁小女孩为它命名 Pluto（冥王星），自此太阳系拥有了 9 大行星。这一事件也标志着天文学的中心从欧洲转到了美国。\n然而，到了 2006 年，在有 2500 人参加的第 26 届国际天文学联合大会上，400 多位天文学家投票把冥王星被开除出了太阳系的大行星系统。这中间发生了什么？下面用个列表来说明：\n1978 年，天文学家发现冥王星有卫星，根据卫星计算出冥王星的质量不到地球的 1%。 1993 年，伽利略号发现小行星艾达也有卫星，其后发现小行星拥有卫星是普遍现象。 1992 年 8 月，天文学家发现海王星轨道外的另一个天地，直径超过 100 公里。而冥王星的直径是 2000 公里。 1994 年，冥王星的发现者汤博建议把海王星外的小天体分类为柯伊伯带天体。 2005 年，科学家发现了Eris（阋神星），它也属于柯伊伯带天体，但它的质量超过了冥王星。之后一连串柯伊伯带天体被发现，质量都和冥王星差不多。 由于上面这些问题，冥王星的身份到了不得不重新审视的阶段。如果它可以叫做大行星，那么阋神星和其他被发现的柯伊伯带天体天体算什么呢？\n第 26 届国际天文学联合大会解决了这个问题。会议关于冥王星的讨论投票很有戏剧性。由于希望发言的人太多，出现了排队等候发言的人比台下坐着的人都多的情况。天文学家们最终通过决议，想要被称为太阳系的行星，必须满足三个条件：\n围绕太阳运动 质量足够大，使自己呈现圆球形 清空了自身运动的轨道（冥王星不能满足这一点，因为它不是自己所在轨道的主要天体） 这次大会创建了新的“矮行星”分类，冥王星和阋神星等天体都被归入这个类型。这是一次负责任的讨论，会议不仅解决了冥王星的争议，还提出了一个严谨的规则用于对太阳系行星进行分类，为未来的新发现提前留出了可以讨论的空间。\n冥王星被降级事件带来了巨大的影响。不仅所有的教科书需要重印，天文台、科技馆也需要修改自己的资料。美国新墨西哥州第 48 届议会甚至通过决议立法规定冥王星在新墨西哥州永远是一颗大行星（汤博长期居住在该州，且该州拥有大量世界级的天文台和望远镜）。\n回形针为啥被喷 曾嵘：这篇我写了很长的评论，想想还是不发了，直接发几篇观点不同的文章链接吧。\n意识形态这东西，谁都不可能绝对正确。\n文化输出对一个民族来说肯定是好事。希望能有 SAGITEAM 的功劳。\n两面针躺枪真的是惨。\n回形针辱华了吗 回形针不是问题的问题 因为撞名而被迫躺枪，两面针求生欲太强 治疗新冠肺炎要花多少钱？ 澎湃新闻：权威数据丨新冠肺炎患者人均治疗费用1.7万，这笔钱谁出？\n根据国家医保局服务管理司司长熊先军的采访，我们可以了解到最权威的数据。\n医保部门要求对于确诊和疑似患者全部实行先救治、后结算。在基本医保、大病保险、医疗救助等按规定支付后，个人负担部分由财政给予补助。医保部门及时调整定点收治医疗机构的总额预算指标，对相关医疗费用单列预算，不占用当年总额预算指标。\n体外膜肺氧合（ECMO）费用较为昂贵，之前许多省份未纳入支付范围，在肺炎疫情中医保也予以支付。\n无论是本地或异地患者，都实行先救治、后结算。个人负担部分由就医地财政给予补助，异地就医医保支付的费用由就医地医保部门先行垫付，疫情结束后全国统一组织清算。\n截至3月15日，31个省（区、市）和新疆生产建设兵团报告，全国新冠肺炎确诊和疑似患者发生医保结算93238人次（包括门诊患者多次就诊结算），涉及总费用103960万元，医保系统共支付67734万元。\n截至3月19日，各地医保部门累计拨付资金193亿元，其中湖北省37亿元。\n全国确诊患者结算人数为44189人，涉及总费用75248万元，人均费用1.7万元，其中医保支付比例约为65%（剩余部分由财政进行补助）。\n读书笔记：《绩效管理全流程实战方案》 本周精读了一本书： 《绩效管理全流程实战方案》\n历史周读 周读：202013 被下架到底是谁的错？ 周读：202012 能解释为愚蠢的，就不要解释为恶意 周读：202011 长得帅的人脾气都不好，长得漂亮的人收入相对高 全部周读 全文完 ","date":"2020-03-29","description":"","lastmod":"2020-03-29T11:34:06Z","slug":"weekread202014","tags":["reading","weekread","COVOD-19"],"title":"周读：202014 治疗新冠肺炎已经花了多少钱？","url":"https://blog.zengrong.net/post/weekread202014/"},{"categories":["impressions"],"content":"书籍信息 绩效管理全流程实战方案 作者：白睿 出版社: 中国法制出版社 出版日期：2019-01 读书感受 这本书给我的感觉就是一本“教科书式的词典”，书中介绍了现存的多种绩效管理方法，包括我知道的方法和我不知道的方法。每种方法都有详细的案例、表格设计可供参考。因为其词典特性，加上各种不知道从哪里找来的案例和图表，整本书读起来相当蓝瘦。\n我们知道，每家公司的绩效管理方法是不可能完全一致的。绩效管理和企业文化、产品、生产方式都息息相关。没有一种方法可以满足所有企业的需求。既然如此，这本词典的作用是什么呢？是写给谁看的呢？\n我想，应该是写给我这种什么都要拿过来看一看的人看的吧 😄\n因为本书实在是很像一部词典，在读书笔记中再把书中内容列举出来就显得毫无意义，我会采用一种不同的方式来做笔记：仅记录重点感受。记录中有些是书中原话，有些是我的演绎。为了让行文更加流畅，就不将它们强行分开了。\n思维导图 绩效管理和绩效考核 绩效管理和绩效考核是有区别的。现代企业中，只讲考核而不讲管理，无法获得好的绩效。两者的主要区别如下：\n人性观不同。传统绩效考核的假设是性恶论，即通过不断考核来防止员工的懒惰和懈怠。绩效管理则遵循以人为本的原则，相信每个人都有自我完善和自我实现的潜能。 侧重点不同。绩效考核侧重于过程的执行和考核结果的判断，是单向命令式的。绩效管理则侧重于双向互动沟通和及时反馈。 参与方式不同。 员工认为绩效考核是人力资源部门的工作， 自己只是被动参与者。现代绩效管理的过程则需要员工参与其中，充分显示员工的主动性。 主要目的不同。绩效考核通过考核得到员工工作情况和效果，主要用于薪资水平上的奖惩。绩效管理主要的目的是完善员工的 绩效改进计划。 绩效考核的结果是复杂的，考虑下面几点：\n如有的员工工作很努力，但基础不是很好，工作效果一般； 有的员工在业务方面大胆开创，但有时细节工作不到位； 有的员工工作成绩平平，但在某些方面有特长。 要在上面的员工中选出一位最优秀的员工的确非常困难。因为绩效管理的目的是提升绩效，所以 绩效考核要体现战略导向，在一定期间内符合公司发展战略导向的行为就该受到奖励。 如果公司本期对业务开拓创新有更大的要求，那么开拓创新的行为就该受到鼓励；如果公司业务发展压力较大，那么业务出色的员工更该受到激励。\n企业不同战略阶段的选择 企业所处的战略发展阶段为初创期、发展期、成长期、衰退期、改革期共五个阶段。在这五个阶段中，由于战略重点不同，绩效诊断重点各异，因此企业所选择的绩效诊断方法也有所不同。\n初创期，企业承担高风险的项目，没有太多的政策和制度，注重短期结果和目标完成。 发展期，企业承担有适度风险的项目，权衡眼前和长远利益，员工对绩效管理也有了一定的认知和理解。可以使用短期结果和长期标准相结合，个人目标和团队目标相结合的方案。 成长期，企业需要维持现有利润状态，适度削减投资并适当裁员，管理制度趋近完善。应着重对发展期的绩效方案进行小幅调整。 衰退期，企业利润下降，会选择变卖资产维持运营，不再投资，有时会大规模裁员。应该采取注重行为和过程的全面评估系统，例如 360 度评估。 改革期，企业要盘活资产，削减投资，短期内大规模裁员，员工士气低落。应该采用更注重结果的评估方式，例如 KPI。 关于绩效沟通 绩效计划沟通的目标是达成共识。沟通应该根据企业文化和企业氛围、员工个性特点、工作目标难易程度等来选择绩效沟通的方式。绩效计划沟通是让员工了解绩效管理的目的，了解其对自己的好处，营造合作氛围，同步对绩效管理的理解和期望，避免员工将关注焦点集中在考评上，避免产生担忧和敌对情绪。\n绩效沟通是整个绩效管理的重中之重。可以说沟通是绩效管理成功的关键。在沟通之前，员工和经理都应该对以下几个问题达成共识：\n经理和员工在沟通中是一种相对平等的关系，他们共同为了业务单元的成功而做计划； 我们有理由承认员工是真正最了解自己所从事的工作的人，员工本人是自己的工作领域的专家，因此在制定工作的衡量标准时应更多地发挥员工的主动性，更注重听取员工的意见； 经理主要影响员工的领域是在如何使员工个人工作目标与整体业务单元乃至整个组织的目标结合在一起，以及员工如何在组织内部与其他人员或其他业务单位中的人进行协调配合； 经理应该与员工一起做决定，而不是代替员工做决定，员工自己做决定的成分越多，绩效管理就越容易成功。 制订绩效计划的过程中，对计划的审定和确认是最后一个步骤。在这个过程中要注意以下两点：\n一是在绩效计划过程结束时，管理人员和员工应该能以同样的答案回答以下几个问题，以确认双方是否达成了共识。这些问题是：\n员工在本绩效期内的工作职责是什么？ 员工在本绩效期内所要完成的工作目标是什么？ 如何判断员工的工作目标完成得怎么样？ 员工应该在什么时候完成这些工作目标？ 各项工作职责以及工作目标的权重如何？ 哪些是最重要的，哪些是次重要的，哪些是次要的？ 员工的工作绩效好坏对整个企业或特定的部门有什么影响？ 员工在完成工作时可以拥有哪些权限？可以得到哪些资源？ 员工在达到目标的过程中会遇到哪些困难和障碍？ 经理会为员工提供哪些支持和帮助？ 员工在绩效期内会得到哪些培训？ 员工在完成工作的过程中，如何去获得有关他们工作情况的信息？ 在绩效期间内，经理如何与员工进行沟通？ 二是当绩效计划结束时，应达到以下结果：\n员工的工作目标与企业的总体目标紧密相连，并且员工清楚地知道自己的工作目标与企业的整体目标之间的关系； 员工的工作职责的描述已经按照现有的企业环境进行了修改，可以反映本绩效期内主要的工作内容； 经理和员工对员工的主要工作任务、各项工作任务的重要程度、完成任务的标准、员工在完成任务过程中享有的权限都已经达成了共识； 经理和员工都十分清楚在完成工作目标的过程中可能遇到的困难和障碍，并且明确经理所能提供的支持和帮助； 要形成一个经过双方协商讨论的文档，该文档中包括员工的工作目标，实现工作目标的主要工作结果，衡量工作结果的指标和标准，各项工作所占的权重。 领导者类型 没有管理者的支持和重视，绩效管理也不可能成功。领导有两类： 交易型领导和变革型领导。\n所谓交易型领导，是指领导与下属之间的关系以一系列的交易和隐含的契约为基础。该类型的领导以奖赏的方式领导下属，当下属完成特定的任务后，便给予承诺和奖赏，整个过程就像一项交易。\n所谓变革型领导，是指领导者通过改变下属的动机与价值观，如提升需要层次、超越自我兴趣等来促进绩效的提高和整个组织的变革。变革型领导涉及四个维度：领袖魅力、鼓舞动机、个别体贴和智力刺激。\n研究表明，领导方式与组织绩效具有密切的联系。在银行业，变革型的管理者比事务型的管理者会获得更多的顾客市场份额。具有领袖魅力的变革型领导行为，不仅可以提高员工满意度和生产率，也能提高组织的有效性，加强组织成员间的沟通，激发员工的创新意愿，从而使他们有更强的责任感，努力提高工作质量，促进组织财务绩效的提高。\n关于 OKR 我前段时间一直在研究 OKR。写了两本 OKR 相关书籍的读书笔记：\n读书笔记：《OKR 工作法：谷歌、领英等公司的高绩效秘籍》 读书笔记：《这就是 OKR：让谷歌、亚马逊实现爆炸性增长的工作法》 所以在本书中讲到 OKR 的部分，我做的笔记就更加全面一些。\n对于 OKR 来说，目标管理并不仅仅是一种方法，更是一种 管理哲学，它特别适用于对高级管理人员或者创意人员的管理，所以，它也是“管理中的管理”。目标管理的理论哲学精髓就是它的主动性、创造性和条理性。目标管理优点集中于“计划的有效工具”“使组织明确”“诱导工作执行”“控制的最佳指南”“使管理绩效的评价成为可能”“个人承诺”“展开有效的控制工作”等方面。\nOKR一般被认为能为企业带来以下几点价值：\n采用受过训练、逻辑严密的思考方式，让主要目标浮出水面； 告知每一个人什么是重要的； 使沟通传达更加准确； 建立衡量进展的指标体系； 集中精力并确保跟组织目标对齐； 因为目标对每个人都是透明的，所以OKR确保每个人的工作都朝着同一个目标和结果。 OKR 有 团队效应。 每一个经理都应该清楚如何让 多代劳动力 一起团结协作。OKR正是这样一个比较理想的方法，它帮助组织、团结不同年代的员工朝着共同的目标努力。通过设置明确的目标，并且保证目标在全公司透明。\nOKR 适合不同大小的组织。Google 通过OKR的成功应用，人员规模从40人发展到40000人。从这个意义上说，OKR还是一个保留公司核心人才的有效工具。\n对于100人以下的初创期企业，OKR可帮助企业分解战略，培养OKR的意识和企业文化； 对于100—1000人之间的上升期企业，可通过OKR实现系统化落地，待发展成熟后形成相对成熟的OKR管理文化，类似 Google 模式； 对于1000人以上规模较大的企业，可引入OKR做常规性目标管理，解决层级多、目标背离、激励创新的问题。 OKR 是一种企业、团队、员工个人目标设定与沟通的 最佳实践与工具， 是通过结果去衡量过程的方法与实践。同时，OKR还是一种能够促进员工与团队协同工作的 思维模式。 OKR是一种 目标管理工具 ，是 战略管理工具。\n在介绍 OKR 方法的优势时，KPI 往往是作为反面典型出现的。但这样评价 KPI 并不公平。KPI 的要素独立，适用的应该是财务指标与任务指标，对于能力指标、学习指标，是很难用 KPI 来分解的。下面从几个不同方面来做一下比较：\nKPI 一般是一个单一的数字，背后的执行逻辑没有限制，最终考核也是依照数字的完成情况，且与奖金挂钩；OKR 更多是对目标执行的过程分解，是一个目标执行过程和方法的跟踪，使执行者更有方向感。 本质上比较：OKR 不以考核为目标，它是目标管理工具；KPI 则认定 “你选择衡量什么，你就得做到什么”。 关注点上比较：OKR 时刻提醒每一个人当前的任务是什么；KPI 则关注财务和非财务指标，认为工作完成的情况对财务结果有直接的影响。 考核标准比较：OKR 分数不是越高越好，有愿景型 OKR 的存在；KPI 则是分数越高越好。 OKR 在于“监控我要做的事”，KPI强调的是“要我做的事”。 我制作了一个表格，按我（曾嵘）自己的理解用不同的条件来表述 KPI 与 OKR 标准上的不同。表格中的分数最高为 5 分，最低为 1 分。\n标准 OKR KPI 可量化(SMART) 5 5 公司、个人、团队目标要一致 4 5 目标难度 5 3 目标公开透明 5 1 自下而上（考核指标的制订） 5 2 标准唯一性（结果不是考核的唯一标准） 3 5 作为绩效工具，KPI 和 OKR 并无孰优孰劣之分，只要业务形态适合，就可以使用。就某一具体岗位而言，其 OKR 与 KPI 最终呈现出来的差异并不明显，具体的 KR 与指标没有太大差异，指标设定的过程与 KPI 也十分类似。人们虽然能够罗列出很多 KPI 与 OKR 的不同，但差异其实并没有想象得那么大。\n人力资源经理素质要素 人力资源部经理岗位素质模型\n人力资源部经理胜任要素分级定义\n杂项 职位价值通常是指某个岗位在组织体系中因其所承担的责任和对组织的贡献所形成的价值的大小。这种价值是一种相对价值，是与其他岗位在同一评价标准下比较所得到的排序。对职位价值的这种评价是建立公平的薪酬绩效制度的基础。绩效的评价指标依托职位价值评估方法和维度进行提炼。\n满意度模型可分为三个层次：\n第一个层次是满足，对于消费者而言，这一层次是满意度的基础，如果做不到，消费者会迅速产生不满。 第二个层次是渴望，对于消费者而言，他们非常渴望这一方面的需求得到满足，如果做到，会带来消费者的赞赏；相反的，如果做不到，则会降低消费者的满意程度。 第三个层次是惊喜，这一层次并不是消费者所要求的，因此，做不到消费者并不会因此不满，但一旦做到，他们会格外惊喜，并成为忠诚的用户。 全文完 ","date":"2020-03-27","description":"","lastmod":"2020-03-27T09:35:31Z","slug":"reading-notes-jxglqlc","tags":["reading","management","readingnote"],"title":"读书笔记：《绩效管理全流程实战方案》","url":"https://blog.zengrong.net/post/reading-notes-jxglqlc/"},{"categories":["impressions"],"content":"2020 年第 13 周我读过的部分有价值的文章之内容摘要。\n猎豹旗下 App 被下架到底是谁的错？ 移动办公软件规模 Google 和 Facebook 的大锅饭 应用商店十年神话终落幕 如何才能以一种既公正又得体的方式来解雇一个人 《OKR 工作法》 猎豹旗下 App 被下架到底是谁的错 本节内容基于下面三篇文章重新组织而成：\n被谷歌掐住咽喉：中国科技公司的出海苦旅 傅盛：悬崖边的反思 被谷歌剪掉命根子的出海应用，没几个冤枉的 2020年2月20日，猎豹旗下的 45 款 App 被谷歌下架，其广告账户也被终止。\n猎豹移动于2014年5月8日在美国上市，2015年5月底，猎豹移动的股价一度触摸到35美元以上，市值接近50亿美元。如今公司市值在4亿美元上下徘徊。\n猎豹在被下架之前就被 Google 冻结过广告收益，据此一直在和 Google 中国和美国总部积极沟通，该沟通已经持续三个月之久。2月20日，就在猎豹准备年度财报之前，他们终于等来了谷歌美国的回信，结果是被冻结的资金里有大部分可以返还给猎豹。但 2 小时之后，猎豹的所有 App 产品（包括每年被 Google 应用商店推荐的游戏产品和非猎豹控股的，不带广告 SDK 的海外直播平台 LiveMe）被 Google Play 下架。\n在猎豹之前，被谷歌下架或封杀的中国出海企业名单还有一长串，包括iHandy、小熊博望、触宝等，他们都已积累了数亿用户规模。\n张哲制图\n有人认为，许多出海厂商使用“恐吓式获客”、“饱和式变现”、“批发式生产”等手段劫持广告和流量，不符合“大厂风范”，被下架了也不冤。他们被下架之后绑架民意，把此事与围堵中国联系起来，声泪俱下地控诉 Google 的霸权行径，这并不合适。\n那么 Google 等厂商有哪些霸权行径呢？\n傅盛认为，如果一家出海公司 DAU 发展到 1 亿，靠广告来挣钱，就会遇到来自Google、Facebook激烈竞争。在2016年3月，Facebook宣布把广告平台向更多第三方广告平台开放，这让猎豹却多了大量竞争对手，收入也被分流。\n2018年11月，一家美国第三方移动应用分析公司Kochava指控猎豹移动存在广告欺诈行为。Facebook 终止与猎豹合作。在猎豹不断的询问之下，Facebook 表态说，猎豹可以请审计公司对内部数据安全和政策遵守情况进行审计，但审计公司必须由 Facebook 指定，猎豹承担所有费用。于是猎豹从美国请来流量审查公司 Alix Partners 耗时一个月对自身数据进行审查，结论为猎豹本身的数据安全符合 Facebook 的要求，数据使用符合规范。这个结论没有什么 luan 用，Facebook 的回复依然是继续终止合作，并且是最终决定。\n即使早就退出了中国，Google 仍然每年都在中国举办开发者大会（去年我也去参加了），Google 中国在北京和上海都有办公室，员工有几百人。但是 Google 中国办公室在话语权和信息传递方面做得并不好，主要是起到传话的作用，很难在最后决策上发表意见。\n美国福布斯网站 2020年3月3日 发布的一篇报道中，与 Google、Facebook 长期合作的流量审查公司 White Ops 披露了他们认为猎豹旗下所有产品被下架的原因：猎豹移动的公司总部在北京，即使数据跑在AWS的服务器上，仍会被发送回中国本地。\n对 Google 提起诉讼是否有用呢？有两个例子。\nAptoide 是一家做应用市场的公司，2014年遭到谷歌打压。这家企业向欧盟反垄断机构举报，欧盟给谷歌开出了数亿美元罚单。谷歌负责整个安卓的VP主动找上门来，给他们退钱，并为 Aptoide 解禁了谷歌广告分发广告 AdMob 的账号。\nunlockd 是一家做锁屏应用的澳大利亚企业，2018年被谷歌下架，并被封禁了AdMob账号。unlockd 选择起诉谷歌，Google 的策略是打持久战。到了2019年中，unlockd 因为没有收入破产，不得不放弃对谷歌的诉讼。\n中国企业普遍的担忧包括：一是对美国法律不了解，“我们也不知道该向谁起诉，怎么去投递起诉书，怎么走流程”；二是谷歌有庞大的法务和律师团队，打官司都要耗时数年。更多企业担心被谷歌报复。“我告赢了谷歌，之后还得跟它合作。”\n记者问，“当时没想到钱来这么容易，有可能是不持续的？”傅盛反问，“要是你，你会这么想吗？”\n曾嵘：我会这么想。\n孩子才谈论对错，成人只谈利益。猎豹要利益，Google 一样也要利益。从不同的角度能看到不同的信息。就像下图一样：\n移动办公软件规模 财新周刊：移动办公极速狂奔\n钉钉在疫情期间，吸引14万所学校的290万个班级开课，覆盖1.2亿名学生，350万名老师在钉钉做起了主播。 失去假期的学生们掀起了一轮又一轮的“一星差评”潮。腾讯会议、钉钉等在苹果应用商店中的评价皆为两极分布的C字形，一星最多、五星第二。\n早在2月3日上午，钉钉、企业微信、华为WeLink等因近2亿人在家办公，在春节假期后首日接连倒下。而到2月10日，据极光大数据，钉钉的日活跃用户从之前的数千万升至1.22亿，是春节的7倍；企业微信为1282万，是春节的近3倍；基数更小的腾讯会议几乎完成了从零到约600万的跳跃。\n腾讯会议在除夕夜里将免费视频的门槛从25人提高至100人，两天之后再提至300人，又在此后增加了直播的功能。1月29日到2月6日，腾讯会议八天扩容超过10万台云主机。\n字节跳动内部从2012年起，曾使用过Skype、微信与微信企业号、Slack和钉钉等国内外办公应用。在2016年底，决定启动自研，2018 年飞书开始在国内内测推广。\n到了 3 月中旬，钉钉日活超过2亿；腾讯会议升至1000多万；企业微信升至1500万。飞书和 WeLink 则仍在几十万量级。金山文档的月活跃用户在 2 月 4 日过亿，2 月 20 日左右就超过了 2 亿。\n企业微信团队至今仅有 300 多人，钉钉团队已有一两千人规模，业务规模小得多的飞书也有上千人，华为WeLink团队也有五六百人。\n“疫情把在线办公提前了五年。”\nGoogle 和 Facebook 的大锅饭 面对疫情，Google 和 Facebook 为什么要给员工开「大锅饭」？ 远程办公期间，Facebook 和 Google 的绩效怎么算？\nFacebook：扎克伯格决定给 45000 名全职员工发放 1000 美元补助，不仅如此，取消 2020年上半年业绩考核，并且以Exceeds Expectation（超出预期）评级给每位员工发放绩效奖金。Facebook 在过去 16 年中从未这样做过。\n有人认为 Facebook 的全员 EE 的方案这为许多人的懒惰找了借口，对于该被奖励的员工是不公平的，「我认为『至少 EE』而不是『全员EE』（才是合理的）」。\nGoogle：推迟年中（2019年10月-2020年3月）考核，取而代之的是整年度（2019年10月- 2020年9月）绩效考核。2020年11月发布审核结果时，公司将提供两倍的晋升机会（考虑到是两个周期的结合），而晋升员工的工资将追溯到 8 月发放。按照以往的考核流程，两次考核结果分别在 5 月和 11 月开始实施。\n有人认为这样调整是在疫情之下对现金流的缓冲（其实对于 Google 这种体量的工资，这样做没有必要）。「上半年的考核其实已经有结果了，现在等于把结果抹掉了。」\n曾嵘：全员评优和没有评优，员工肯定都会不高兴。作为一个大公司，一刀切是最容易的做法，如果要做到个性化，需要消耗更高的成本。企业一直都在寻找平衡。\n应用商店十年神话终落幕 小败局 | 阿里撤退百度放弃，应用商店十年神话终落幕\n2013年 8 月，百度以 19 亿美元收购 91 无线。将 91 助手，安卓市场和百度手机助手三个产品合并成百度移动开放平台。2017 年底百度关闭福州研发中心，从实体上宣布了 91 退出历史舞台。现在百度移动的战略已经调整为搜索+信息流双引擎，19 亿美元交了学费。\n豌豆荚曾经收到过阿里开出的 15 亿美元条件，2014 年它拒绝了收购意向，接收了 1.2 亿美元融资，公司估值达到 10 亿美元。2016 年阿里巴巴用了 2 亿美元就买到了豌豆荚。彼时豌豆荚的在第三方机构的应用商店排名中已经掉出 10 名开外。\n移动互联网早期，用户没有太多自主选择意识，应用商店占强势地位，产品比较弱势。而十多年发展过后，用户心智愈发成熟，AppStore 更加稳定，用户付费习惯已经养成。当一款产品能够满足刚需时，应用商店就处于弱势地位，很难凭一己之力控制优秀App的生死。\n本质上，是手机厂商的觉醒把应用商店洗出了市场。小米、华为、Vivo、OPPO 每年累计出货上亿台智能手机，用户的换机期一到，原来的应用商店用户就会流失。手机厂商也不约而同不再选择与应用商店续约。腾讯系渠道也不再是应用宝唱主角，还有更为重要的微信（小程序）以及QQ等入口。未来几年，应用分发市场基本上是三足鼎立格局，苹果垄断iOS市场，安卓市场由腾讯系渠道和头部手机厂商瓜分。\n2019 年，App Store的总销售额超过540亿美元，而 Google Play的销售额为293亿美元。App Store自从2008年上线以来，已经向开发者分成超过1550亿美元。\n曾嵘： 买量方式兴起之后，硬核渠道也会逐渐成为次要渠道。优秀的游戏会优先使用 iOS 渠道买量分发，然后才会选择在其他渠道发行。这对于开发者来说，是好事。话语权将逐渐向优秀的内容倾斜。\n如何才能以一种既公正又得体的方式来解雇一个人 哈佛商业评论：要想公正又得体地裁员，哪些禁忌不能踩？\n最好的领导者不仅要善于将后起之秀安排在合适的岗位上，还要善于让不合适的人离开。招聘过程中要做到完全不犯错误并不现实——即使能够做到毫无差错，但组织会变化，角色会发生调整，最终你可能会发现，哪怕是技能高超的员工也有可能无法适应新的变化。\n1. 不要拖延到迫不得已的那一刻\n打造顶级团队，需要不断对组织及其成员进行评估——发现谁能够承担起更重要的责任，以及谁的技能发展跟不上平均水平。将那些不那么重大、不那么突出的绩效不佳时刻记录下来，构建一条趋势线。尝试通过指导、培训和其他方式来解决问题。指导和培训都无法提升的人，应让其尽快离开。\n2. 要敢于解雇朋友或家人\n3. 不要让对方感到意外\n从最高层的管理者到级别较低的员工，都应该得到频繁的反馈。绝大多数解职应该只发生在老板与员工进行过多次讨论之后，或许还应该执行一次绩效改进计划（PIP），明确记录问题所在，并对绩效不佳者发出警告。\n4. 做好准备，事先练习\n调整心态，庄重而优雅，高敏感度。提醒自己作为管理者，对这个人的失败负有一定责任。选人不当和指导欠佳。把重点放在最佳结果上：找到这个人能够最大化发挥潜力的地方。\n5. 不要把讨厌的工作推给别人\n没有人喜欢开除别人。也没有人喜欢被开除。但比起被自己的老板开除，只有一件事更招人厌烦，那就是被人力资源部主管或者老板雇来的人开除。不亲自解雇自己的员工，就是无法“自己收拾残局”。\n6. 立即并明确传达讯息\n在坐下来后的前30秒钟内将讯息传达出去：“我们已经决定要调整/解除你的职位/替换你。” 拖延会引起误会和尴尬，幽默或者展示怜悯可能引起反感或者误会。\n7. 不要对决定做过多解释\n解职谈话是传达决定，不是讨论、辩护或者谈判。被解雇的人会不断用各种方式问”为什么“。你只需要给出简单解释：可能是因为绩效，也可能是因为撤销岗位或者职能。这次谈话重点不是讲道理，而是后勤安排。避免与辩护者交锋的冲动。\n8. 要人性化\n好老板不是机器人。应该意识到被解雇的员工将会产生各种不愉快的情绪。应该耐心倾听员工的一切反应，并小心调整自己的回应。\n裁人从来都不容易，管理者在应付自身情绪的同时，也必须认识到，同情心和同理心与怜悯或悲伤之间的区别。\n9. 不要推卸责任\n不要暗示：“是恶人让我来这么做的”或“我只是来传达消息”。传达决定的人应该表达出个人对此的责任。\n10. 要慷慨\n按照法律规定给出补偿。要意识到：若新员工未能达到绩效标准，招聘负责人应当承担一部分责任；这应该会让公司更愿意承担部分被解职者的经济损失。\n《OKR 工作法》 本周精读了一本书： 《OKR 工作法：谷歌、领英等公司的高绩效秘籍》\n阅读列表 被谷歌掐住咽喉：中国科技公司的出海苦旅 傅盛：悬崖边的反思 被谷歌剪掉命根子的出海应用，没几个冤枉的 财新周刊：移动办公极速狂奔 小败局 | 阿里撤退百度放弃，应用商店十年神话终落幕 面对疫情，Google 和 Facebook 为什么要给员工开「大锅饭」？ 哈佛商业评论：要想公正又得体地裁员，哪些禁忌不能踩？ 全文完 ","date":"2020-03-22","description":"","lastmod":"2020-03-22T11:38:36Z","slug":"weekread202013","tags":["reading","weekread"],"title":"周读：202013 被下架到底是谁的错？","url":"https://blog.zengrong.net/post/weekread202013/"},{"categories":["impressions"],"content":"书籍信息 OKR 工作法：谷歌、领英等公司的高绩效秘籍 作者：克里斯蒂娜•沃特克 (Christina Wodtke) 原作名： Radical Focus: Achieving Your Most Important Goals with Objectives and Key Results 出版社: 中信出版社 出版日期：2017-09-01 关于作者 克里斯蒂娜•沃特克是硅谷的著名产品专家，她曾经在 Myspace 和 Zynga 负责过重要产品的设计和管理工作，也是《锦绣蓝图：怎样规划令人流连忘返的网站》一书的作者。\n本书由国内著名的 PaaS 平台明道云团队组织翻译。\n读书感受 本书读起来非常轻松，也许是因为我已经阅读过 《这就是 OKR：让谷歌、亚马逊实现爆炸性增长的工作法》 的原因，也许是因为本书独特的写作手法。\n本书前三分之二的篇幅是一部中篇小说。小说中讲述了汉娜和杰克两个合伙人因为对茶叶高品质茶叶的热爱而一起创业的故事。故事中的汉娜坚强感性，杰克理想化且充满热情，天使投资人吉姆经验丰富，CTO 拉斐尔敏锐又有趣。他们在创业中遇到了各种困难，也会经常吵架，被服务商解除合同，还辜负了供应商的期望。最终他们使用 OKR 方法让公司成功走出困境，获得了 A 轮融资。\n整个故事一气呵成，我也是一口气读完。其中的案例让我出了一身冷汗。几乎所有的事例，所有的状态，在我的工作和创业过程中都遇到过，作者丰富的经历和敏锐的感觉可见一斑。\n书的后三分之一部分介绍了 OKR 的实践过程和使用场景。与 《这就是 OKR：让谷歌、亚马逊实现爆炸性增长的工作法》 一书只讲案例不讲操作不同，这本书较为详细地讲解了具体操作和重要建议。\n点子不值钱，成熟的团队才值钱 尼尔·盖曼在《你的灵感来自哪里？》一书中讲了个段子：\n每一个出版过作品的作家都有这样的体验：有人找到你，说他有一个极妙的想法，并迫不及待地想和你一起实现这个想法；结局也总是差不多，他们艰难地完成了灵感部分，而你只需要简单地把它写成小说，收益则需要五五分成。\n在游戏创业世界里面也有个段子：\n策划案都想好了，就缺个程序员了。\n可是策划案不值钱。想出一个创意太容易了，在把创意变成现实并让玩家为之付费的过程中，要能保持创意不走形，你需要组建合适的团队，招聘不同的人，还要让他们聚焦到具体的事情上，确保方向不错。\n曾嵘：两年多来，我一直都在致力于把 SAGITEAM 打造成一个“完美团队”。这个团队必须有极强的执行力，永远保持旺盛的斗志和明确的目标。团队成员之间要正面直言，团队内部能形成足够的“默会知识”和“共同知识”，做到“可以用眼神来确认需求”。团队越大，做这件事就越难，所以我们需要 OKR。\n要做到保护创意变为现实的过程，有三个步骤：\n首先，设置有挑战、可衡量的阶段性目标。 其次，确保你和你的团队一直朝着这个目标前进，不要被其他事情干扰。 最后，把握节奏，所有成员一直明确需要努力达成的目标，并相互支持、相互鼓励。 两个小故事 阿塔兰忒和金苹果（来自百度百科）\n阿塔兰忒在当时几乎是全希腊跑的最快的人，除了赫拉克勒斯几乎没有人跑的过她。她不愿结婚，但迫于父亲的要求只得同意参加赛跑，跑赢她的求婚者可以和她结婚，输了就没命。许多年轻人都挂了，直到希波墨涅斯出现。希波墨涅斯向爱神阿佛洛狄忒求助，她便给他三颗金苹果。当阿塔兰忒在比赛中超过希波墨涅斯，他就丢下一个金苹果，阿塔兰忒就会去捡苹果，导致速度减慢。墨拉尼昂就这样赢了比赛。\n如果阿塔兰忒当时知道 OKR，能保持对目标的聚焦的话，估计就不用被逼结婚了。\n投资人不是你的妈妈\n小说中汉娜在最糟糕的时候去找她的天使投资人吉姆，吉姆说：“你刚刚告诉我你快崩溃了，但你要知道，我是你的投资人，不是你妈妈，你自己要想方设法让你们都振作起来，搞不定就让他离开团队。你们当下要专注在业务上，要不然我只能另找他人把公司业务做起来。换几个人很简单。”\n无法完成目标的关键因素 因素 1：没有给目标设置优先级\n如果所有的事情都同等重要，那么就意味着它们也同等不重要。\n因素 2：缺乏充分沟通，导致没能准确理解目标\n每周 OKR 和周五活动不断重复阶段性目标，保证目标时刻在大家心中。目标必须非常明确，反复传达给所有人，直到所有人都步调一致。\n因素 3：没有做好计划\n需要一个目标管理系统来保证你在疲劳的时候也能保持在正确的轨道上。\n因素 4：没有把时间花在重要的事情上\n重要的事情不紧急，紧急的事情不重要。\n因素 5：轻易放弃\n有的目标故意放水，有的目标设定过高导致没有能力实现，有的目标设定完之后就没有再跟进。成功实施 OKR 的企业都有相同的特质： 失败后不断尝试。\n企业使命、目标和关键结果 首先必须明确企业的使命。所有的公司无论大小都有自己的使命，即使没有被写下来。可以使用这个简单的格式来描述公司的使命：\n我们通过（什么样的价值主张）在（什么领域或行业）（改善人们的生活或减少人们的痛苦）\n使命和目标在 OKR 模型里面有很多共通之处，区别在于时间的跨度：目标对应一年或者一个季度，使命对应数年或者更长时间。 使命让团队保持正确的方向，OKR 给出明确的里程碑。\n目标用来明确方向，关键结果用来量化目标。设定目标要遵循三个原则：\n原则 1：目标要明确方向并且鼓舞人心 原则 2：目标要有时间期限 原则 3：要由独立的团队来执行目标（目标必须真正属于你） 关键结果要使用振奋人心的语言，并且需要被量化。每个目标通常指定 3 个关键结果，可以包括：用户增长、用户激活、收入增长、产品性能、产品质量等等一切可以量化的条件。产品质量可以使用 NPS 工具来量化。\n可以给 OKR 设定一个信心指数，代表完成目标的把握。5/10 代表有 50% 的把握完成目标，10/10 代表能搞定，1/10 代表没希望。\n曾嵘：在 Google 和 Intel 的实践中，使用 0.0~1.0 的小数来表示目标的完成度。\n在制关键结果的时候，心理状态与难度的关系：\n如果你内心觉得它们很有趣，想着“我们要把所有力量都花在上面”，那么你可能就正确设定了关键结果。 如果想着“我们死定了”，就说明关键结果设定太难。 如果想着“我只要稍稍努力就可以完成”，说明关键结果太简单。 OKR很大一部分的价值就是沟通，沟通哪些事情是重要的、沟通我们能做到什么程度，以及和已经偏离公司目标的执行团队沟通应该做哪些对的事情。每个人都应设定单独的OKR，以反映个人成长以及明确如何支持公司目标。个人 OKR 的沟通可以方便管理团队全面了解员工的想法。\n应该在每周分享 OKR，每周调整信心指数，讨论该指数上升或者下降的原因。不要在季度中途更改 OKR，目标要么成功要么失败，汲取经验，下次就会设定得更好。OKR 的意义不仅在于完成目标，更重要的是能挖掘团队的真正能力。\n曾嵘：调整 OKR 的部分和《这就是 OKR》一书中说的不一样。具体的方案我们还需要尝试。\n在产品团队中，每个人的注意力应该是产品目标。职能部门的目标与产品团队冲突的时候，需要排好优先级顺序，将职能部门的目标融入产品团队的目标中。允许职能部门的经理建立和组织相关的个人目标（不一定与产品有紧密关系）。对于个人也允许建立少量的成长目标（不一定与产品有紧密关系，但与组织有关系）。产品组织执行 OKR 的关键是，OKR 需要从跨部门的产品团队层面上升到公司业务层面。\n曾嵘：这一段说的有点晦涩难懂，需要有产品团队和职能团队交叉工作经验的才能较好地理解。其中的重点就是：将职能团队的目标并入产品目标。\nOKR 的每周流程 周一确定职责\n周一盘点 OKR 进程，使用四象限展示形式：\n本周任务：列出3~4件最重要的事情；明确这些事情的优先级。 未来四周计划：有哪些事情需要其他团队成员做好准备或支持。 OKR当前的状态：调整目标完成的信心指数，团队一起讨论一下信心变化的原因。 状态指标：挑出两个影响目标达成的其他因素，团队需要额外关注，比如客户关系、团队状态、系统状况等。当这些地方发生意外时，马上讨论找出应对方案，确保OKR不受影响。 在使用上面的四象限讨论 OKR 的时候，可以使用下面的问题：\n这个优先级列表能确保我们的OKR完成吗？ 团队的能力可以完成OKR吗？谁能帮助我们？ 我们准备好新一轮的发力了吗？市场部知道产品部马上要做什么吗？ 我们的团队已经筋疲力尽了吗？我们的产品是否存在什么隐患？ 会议要简短，不要让会议变成团队成员刷存在感的现场。把会议的基调定在大家为了共同目标相互支持与配合上。会议时间的1/4用来讲述进展，其余时间一起讨论下一步计划。\n要先从公司 OKR 开始，然后沟通部门级别的 OKR。不需要在会议上沟通个人 OKR，个人 OKR 只需要一对一交流。\n曾嵘：这不是每日晨会中梳理每个人的工作的会议，也不需要所有人都发言。我认为对于 10 人左右的项目团队，这个会议应该在 20 分钟内结束。\n周五放飞自我\n周五安排庆祝环节。准备好啤酒、饮料和点心，工程师展示项目代码，设计师展示原型。销售部分享最近的订单。每个团队分享自己的成果。公司欣赏每个人的努力。\nOKR 会议 OKR 会议步骤\n团队在实施下面的步骤的时候，应该已经研究过 OKR 并做过专门训练，所有人都准确理解 OKR 的含义。\n所有员工提交他们认为这个季度公司应该实现的目标。员工直接参与公司目标制定，这是企业文化的一部分。 CEO 组织 OKR 季度会议， 管理层用半天时间讨论 OKR。下面有详细的季度会议流程。 管理层作业：向各自主管部门介绍公司的季度 OKR，并完成各个部门的 OKR 设置。部门也要有 部门 OKR 会议，使用的方法与 OKR 季度会议类似： 自由列举-归类分组-投票排序-最终选择。 CEO 确认部门 OKR，准备一天的时间和所有部门确认。 自上而下关联 OKR。部门-子部门。 个人 OKR。个人 OKR 是可选的，它是经理和个人一对一沟通的极好方式，必须当面沟通确定。 全体会议，CEO 向全员解释季度 OKR 是什么，为什么这样设置，对其中几个进行示范性的任务拆解。解释的时候要涵盖上个季度的 OKR 总结。 整个公司的 OKR 必须在两周内确定完毕，确定 OKR 是公司最高优先级的事情。\nOKR 通过实践、总结、不断发现和挑战团队的潜力。不要把这个过程当成汇报、考核结果。如果没有完成目标，要敢于承认，吸取教训，做好下次的设置。如果目标达成，要设置更有挑战的目标。把精力聚焦在 学习总结、挖掘潜力和高效执行 上。\nOKR 季度会议\n季度会议的目的是搜集和整理意见，制定下一个季度的战略战术。\n季度会议是在管理层，由 CEO 发起，所有的高层管理人员都必须参加。会议开始前几天就需要开始向公司所有成员收集意见，让他们思考公司近期应该聚焦的目标。每个高管要准备一到两个目标。\n准备 4.5 小时来开会。每两个小时一个环节，中间留半小时休息。熟练掌握方法后可以取消休息以便聚焦。\n会议的流程：\n使用便利贴将员工和高管的目标都贴在墙上。 将所有的目标内容集体过一遍，删除重复，将类似目标合并，通过投票将目标减少到三个。 讨论-辩论-争论-投票排序-做决策。 自由列举所有能衡量目标的指标。 归类分组，确定关键结果，可以用变量来代替关键结果中的具体数值。 关键结果应该覆盖用户指标、收入指标、满意度指标。如果三项关键结果中有两项都是收入指标，意味着团队在追求成功之路上失衡了。 给每个关键结果设定信心值，确保每个 OKR 都是有挑战的目标。 5 分钟确定最终 OKR：这是令人鼓舞并有灵感的目标吗？设置的关键结果有意义吗？它们很难实现吗？团队能和 OKR 一起顺利度过这个季度吗？ OKR 建议 第一次实践 OKR\n第一次实践的时候，可以考虑以下三个建议让 OKR 更容易成功。\n全公司只需要设置一个 OKR，让所有人能看到 OKR 的效果。 先用一个团队去尝试 OKR。选择一个相对独立的团队以保证 OKR 依赖的正常进行。 可以尝试用 OKR 做项目管理，目的是让员工理解这个方法。 周报和 OKR\n试着用 OKR 来改进周报。\n把团队 OKR 作为开始，标注关键结果的信心指数。 列出上周的优先任务，标注完成情况。 列出下周的优先实现。 列出风险或阻碍。 备注。 年度评估和 OKR\n在工作中，2/3 的人认为除了工资，他们和公司几乎没有什么关系。当员工签了有保障的劳动合同后，目标绩效注定是较低的。随着业务的增长，员工的年度目标和业务现实脱节会更加严重，水分会越来越大。\n目标应该着眼于鼓舞和激发成员。这意味着要改变团队制定目标的方法、推动的节奏和表现的形式。OKR 把有挑战、鼓舞人心的想法和量化的关键结果结合在一起，让员工都清楚阶段性的目标挑战。团队中的每个人都能清晰知道努力的方向，明白如何分配自己的时间。这是制胜关键。\n管理者经常会高估成员理解目标的能力，实际上只有 7% 的人能真正理解目标。管理者需要保持目标实时的追踪和持续的透明沟通，这能帮助团队聚焦在目标上，让预测的结果页更加靠谱，每个人都能自我驱动。\n管理者要考虑自上而下和自下而上目标的比例，不能为了掌控一切就限制团队意志，每个人都应该有自由发挥的余地。\n不要一次性完成评估，要持续沟通，不断加以指导和校正。每个月至少需要两次的一对一交流和校正，内容涵盖员工的投入度，绩效水平和协同能力。一年内员工就有 24 次交谈的机会。这样做，一方面可以帮助他们提升自己的能力，另一方面也可以方便管理者识别他们是否真正有所提高。年终评估就简单了，因为事实大家已经知道了，没有意外，只是多了一次年终交流。\nOKR 常见坑\n设置了多个目标。除非公司有多个不同的方向，否则只需要设定一个目标。 设置的 OKR 目标的时间过短。一周或者一个月的时间都不够。 用绩效指标来驱动目标的完成。目标应该是鼓舞人心的，而不是与绩效挂钩。 没有设置信心指数。团队可能会隐藏 20% 的实力导致无法完成目标。 没有追踪指数的变化。要经常主动询问团队成员，多沟通。 周一的会议做成了汇报会。周一的会议不是汇报会，而是谈话会。只讨论需要讨论的事情。 周五的聚会过于严肃。周五是用来放飞自我，增强团队信心的。 全文完 ","date":"2020-03-17","description":"","lastmod":"2020-03-17T09:16:51Z","slug":"reading-notes-radical-focus","tags":["reading","readingnote","management","okr"],"title":"读书笔记：《OKR 工作法：谷歌、领英等公司的高绩效秘籍》","url":"https://blog.zengrong.net/post/reading-notes-radical-focus/"},{"categories":["impressions"],"content":"2020 年第 12 周我读过的部分有价值的文章之内容摘要。\n敢不敢共享你的员工？ 汉隆剃刀原则（Hanlon's razor） 人际容纳度 《这就是 OKR》 雷军：疫情拐点之际，谈谈企业如何度过难 敢不敢共享你的员工？ 受疫情影响，以西贝餐饮集团为代表的传统餐饮业陷入收入减少和现金流不足等困境，董事长贾国龙公开喊话“撑不过3个月”，就此阿里巴巴集团旗下盒马鲜生超市做出响应，提出了“共享员工”概念。\n盒马即从32家企业“借兵”1800余人，开启了“共享员工”模式。继盒马之后，沃尔玛、生鲜传奇、苏宁、联想等企业相继跟进，京东旗下7FRESH也接纳10余家餐饮企业员工到到岗工作。 横店东磁与横店影视城达成“共享员工”合作意愿，首批借用近千名影视城员工。2月11日上午，横店影视城首批180多名员工在得邦照明参加为期一天的培训，次日上岗作业，为得邦照明提供相应复工支持。 海信视像于2月11日正式推出“共享员工”计划，邀请临时歇业的酒店、商超、餐饮员工以短期工的方式加盟公司。一周时间共有60名“共享员工”到岗，解决了企业复工复产的燃眉之急。 共享员工有其合法性，可适用《劳动合同法》中的劳务派遣条款，服务外包的合同法律关系，也可以适用短期的人员借调法律关系。\n“共享员工”模式在海外也早有案例。\n2002 年瑞士的家族企业“维氏”就出租过员工。维氏的代表产品是瑞士军刀。2001 年的 911 事件，让美国开始禁止随身携带道具上飞机，也禁止免税店销售刀具。维氏的销售额一夜之间下滑的 30%。 维氏没有裁员，他们取消了加班，轮班时间减少了 15 分钟，鼓励员工多休假和提前休假。一些有潜力成为管理者的员工，被送到学校深造，公司负担学费。同时借调了大约 90 名员工到其他企业。 维氏借调员工考虑了几个重要的点：\n租借出去的都是高素质的工人，是租借企业急需的。 出租员工的公司都是周边做刀叉餐具或者铁艺的中小企业，这些企业规模小不会给维氏造成威胁。 出借 3-6 个月期满之后允许员工自主选择。这给租借的公司也吃了定心丸，让他们可以放心来培训租借到的员工。 最终租借的员工都回到了维氏。这是因为维氏足够强大，而且企业注重文化管理，和员工建立了深厚的信任基础。\n汉隆剃刀原则（Hanlon's razor） 能解释为愚蠢的，就不要解释为恶意\n恶意行为是小概率事件，愚蠢的行为（广义的愚蠢：包括忘了、错了、漏了、累了、耽误了、不知情、懒惰、想不到、能力不足）则是大概率事件。在路上被其他车别了一下，大概率它不是故意的。\n在生活和工作当中，应该尽量做到 只处理事实，不猜测动机。这样生活会更加幸福。这不是阿 Q 的精神胜利法。很多时候我们认为某个组织或者某个人在刻意做坏事，更有可能是因为它没有能力杜绝坏事。\n曾嵘：如果在工作中碰到无法正常配合的同事或合作伙伴，先不要揣测其意图。这大概率是由于 愚蠢 造成的。不要生气，要平静地尝试让对方正确理解你的要求，或者选择另一种方案以避免时间和机会的浪费。\n我在 2020 年2 月的武汉看到了太多的阴谋论和太多的不如意。有些动机是很容易猜测的，有些是真的很难做到。想把 1000 万人的生活安排得滴水不漏么？神仙都做不到，为什么一定要强求政府做到？加上很多官员能力一般，胆子也小，秉承“多做多错，不做不错”的原则。他们不是不想做到，而是没有能力做到。\n一些演绎\n能解释为无知的，就不要解释为愚蠢 能解释为可原谅的错误的，就不要解释为无知 能用未知的其他原因解释的，就不要解释为错误 金融作家道格拉斯·哈伯德：能用一群人在复杂系统中的互动解释的，就不要解释为恶意或者愚蠢 政治专栏作家拉梅什·莲努尔：能解释为情绪的，就不要解释为策略 人际容纳度 创始人的组织能力第一条就是人际容纳度。你能和多少人进入深度关系，能容纳什么样的人，决定你的组织是丰盛的有弹性的，还是单薄脆弱的。\n关系的四个阶段： 理想期、冲突期、整合期、协同创作期。\n理想期\n让人看到你好的那一面，因为欣赏优秀的人，大家开始走近，甚至进入契约。但体验来自于预期，因为预期一致才觉得好。好的预期可能是角色扮演造成的，也可能是强烈的愿望扭曲了事实（例如恋爱前期）。一旦因为某些原因让预期变得不一致，理想期就会被打破。\n冲突期\n真实世界中，一个人不可能完美符合另一个人的预期，这会导致冲突。只要与人协作，就会面临冲突。\n冲突会导致指责，指责是当对方没有满足预期的时候，你觉得失望甚至愤怒。\n冲突也会导致自责或者内疚。自责带来对自己没有达成预期的心理惩罚，内疚带来辜负感。人际控制，有时就是从让对方内疚开始的。\n冲突非常消耗能量，人们会使用逃避的方式来面对冲突。\n第一种逃避方式是冷漠。冷漠可以降低能耗。工作中的拖延和没有热情的服从，就是冷漠的的表现。 第二种逃避方式是超越。在冲突中退让，看似避免了冲突，但其实是不愿意投入能量，不愿意完整地理解对方。 第三种逃避方式是分离。辞职、散伙、分手，都是分离的形式。 整合期\n正确处理好冲突，才能进入整合期。把沟通中的 “问题” 替换成 “差距”，是一个不错的技巧。如果说“问题”，代表了指责，但说“和预期有差距”就代表同时也承认了预期是有问题的。对别人说问题，代表自己是一个评判者。对别人说差距，代表大家站在一起。\n整合期需要技巧，但不能只靠技巧。整合期依靠的是管理者的容纳程度，这需要锻炼。\n情感来自共同记忆，在整合的过程中，我们会产生强烈的共鸣和 情绪共振。 很多时候我们不愿意分离，不是因为规则，也不是因为利益，而是因为强烈的情感共振的存在。\n协同创作阶段\n一起冒险，一起拥抱不确定，互相接受对方的短板。一起创新和创业。\n《这就是 OKR》 本周精读了一本书： 《这就是 OKR：让谷歌、亚马逊实现爆炸性增长的工作法》\n雷军：疫情拐点之际，谈谈企业如何度过难关 以四季常态过冬天，则冬天亡；以冬天态度过四季，则四季存。\n危机下要以现金为王，做好成本控制。现金充沛的企业，在冬天要大胆。别人恐惧的时候，各种运作成本都很低，这时大胆扩张，可以用较低成本建立较高的竞争门槛。现金严重不足的企业，每项固定成本都要问一下是否可以节约，变动成本尽量取消。 尽量只做六个月以内能产生收益的项目。\n要穿越经济周期，企业还应该具备持续创新的能力。创新既包括产品的创新，也包括商业模式的创新。企业创新的本质就是发现问题、解决问题，并从中找到发展机会。危机是一场考验，是推动反思的终极命题，是企业价值、模式和生长潜力的试金石。\n面对危机的五个建议\n建议一：建立全员成本意识\n从公司创建开始建立成本意识，从老板开始建立成本意识，反复强调，直至建立起全员成本意识。“省钱就是赚钱，每省一块钱至少相当于赚三块钱”。这三块钱分别是：销售成本、生产成本和销售税。\n建议二：严格把控成本\n该花的钱一定要花，不该花的钱一分钱都不能花。\n省钱不是不花钱，不花钱可能造成更大的浪费，比如，可以租用便宜的办公室，但不意味着办公室非常拥挤混乱不舒服，这样员工的工作可能没有效率，造成更大的浪费。\n不该花的钱一分钱不能花，要从每件小事做起。创业前期电话费、出租车费和请客吃饭费等地方花的钱虽然不多，但风气一旦养成，很难改善。人一多，再遇到个别不自觉的人，这几项成本就是天文数字。要回报员工，可以用更好的方式，例如给员工更好的报酬或者更多的股票。\n严控成本的本质是提高效率。片面地以低成本为目标，可能熬过一时的危机，但无法在未来竞争中立足。\n建议三：把费用分成固定费用和变动费用两块\n固定费用是每月必须支付的，如人员费用、房租水电宽带、办公设备、服务器等等。变动费用比如差旅费、电话费、招待费、市场费用等等。\n固定费用非常可怕，一旦开始花，每个月都必须花，很难终止。要在每年、每季度预算会上重点分析固定费用。变动费用累积起来的总数不小，这项成本是每月财务分析会的重点。\n这样分类的好处在于，企业必须花的钱相对可控。一旦遇到危机，先停掉所有的变动费用，然后分析固定成本，逐项定计划消减，整个成本就一步一步控制下来了。\n建议四：严管应收款和库存\n应付款是一定要付的，应收款是一定收不回来的。\n小企业要关心现金存量（够发几个月工资），大企业要关心现金流。现金流的问题一般在应收、库存、固定资产采购上。要严格管理应收款，修改销售政策，尽量现款销售；成立专门小组负责催收应收款。注意好库存管理，所有业务主管定期到库房现场办公，解决相关问题，加大处理速度和力度，保持周转效率。\n建议五：省钱有技巧\n台湾智冠老总王俊博讲控制请客成本的经验： “在最贵的地方点最便宜的菜，在便宜的地方点最贵的菜”。 有创业者通过购买破产网游企业的服务器降低成本，还有创业者直接使用 PC 作为服务器来降低成本。金山软件做广告的时候使用在多版做小广告的技巧来降本增效。\n阅读列表 得到 App 邵恒头条 2020-03-10 得到 App 万维钢精英日课 4 2020-03-10 得到 App 梁宁 增长思维30讲 民建中央网站：疫情之下“共享员工”模式的法律分析和建议 雷军：疫情拐点之际，谈谈企业如何度过难关 全文完 ","date":"2020-03-15","description":"","lastmod":"2020-03-15T12:24:58Z","slug":"weekread202012","tags":["reading","weekread"],"title":"周读：202012 能解释为愚蠢的，就不要解释为恶意","url":"https://blog.zengrong.net/post/weekread202012/"},{"categories":["impressions"],"content":"书籍信息 这就是 OKR：让谷歌、亚马逊实现爆炸性增长的工作法 作者：约翰·杜尔（John Doerr） 原作名： Measure What Matters 出版社: 中信出版社 出版日期：2018-12 关于作者 约翰·杜尔是全世界最具传奇色彩的风险投资家之一，他被誉为“风险投资之王”，参与了 Compaq, Netscape, Symantec, Sun Microsystems, Amazon.com, Intuit, Macromedia 和 Google 等一系列著名公司的早期风险投资，单笔投资收益率达到80倍、44倍、87倍、72倍、352倍。2013 年他被福布斯选为全球第 527 位最富有的人，总财富为 27 亿美元。2019 年他在福布斯美国 400 富豪榜位列第 67 名，总财富为 75 亿美元。\n约翰·杜尔在1974年加入了早期的Intel公司，当时的工作涉及 8080 处理器的产品开发、市场与销售。进入投资界的早期他创建过一家 CAD 软件公司，还共同创立了全球第一家宽带运营公司 @Home。\n读书感受 如果只看中文书名，你可能会误以为这本书中会详细讲解 OKR 的实现，但事实并非如此。英文书名 Measure What Matters 道出了本书的实质：用实例说明如何衡量事件。整本书中使用了十几个例子来说明 OKR 是什么。这十几个不同体量，不同风格，不同行业的企业和机构都使用 OKR 获得了成功。\n如果你希望书中告诉你 OKR 具体应该如何一步步执行，那么也会失望。书中讲解 OKR 执行策略的，只有寥寥数个章节加上附录资源，翻遍全书你都找不到详细的执行手册。但从另一个角度看，整本书都在讲解策略执行。这种讲述方式能够让阅读者在实际案例中对 OKR 产生更全面的理解。正如 20 章中提到的，OKR 是一种思维方式。思维是如此不同，它和企业文化结合在一起，不可能有一种标准的方法去执行它。要解释清楚应该如何部署 OKR，最好的方式就是提供不同的执行经验，由执行者亲自去理解和尝试。\nOKR 小历史 OKR 是 Objectives and Key Results 的简写，中文一般译为 目标与关键结果法。\n20世纪初期的管理理论先驱，弗雷德里克·温斯洛·泰勒和亨利·福特认为：最富有效率、最有利可图的组织，应该是独裁式的。在半个世纪之后，著名的管理学家彼得·德鲁克彻底否定了泰勒–福特模型，构想出一种新的管理理念：具有人文主义的结果驱动型管理。他认为公司应该“建立在对员工信任和尊重的基础上——而不仅仅是作为获得利润的机器”，他发现人性的一个基本特点，即当人们为行动路线的选择做出了贡献时，就更希望看到它顺利实现。德鲁克的目标是制定出“充分发挥个人能力和责任感的管理原则，同时树立共同的愿景和努力方向，建立团队合作精神，协调个人和共同目标的和谐一致”。在 1954 年出版的名著《管理的实践》中，他将这个原则定义为“目标管理和自我控制”。这就是 OKR 的起源。\n20 世纪 60 年代，以惠普为代表的许多前瞻性思维公司都采用了目标管理 MBO（Management by Objectives）。Intel 公司的 CEO 安迪·格鲁夫援引德鲁克的目标管理，在 MBO 的基础上创建了 iMBOs（Intel 公司的目标管理系统），本书作者将格鲁夫的方法命名为 OKR。\nMBO 和 OKR 的区别\nMBO 英特尔的OKR “目标是什么” “目标是什么”以及“如何实现” 年度 季度和月度 不公开，不透明 公开、透明 自上而下 自下而上或者团队协商（50%） 与薪酬体系挂钩 大部分与薪酬体系无关 规避风险 进取精神 理解目标 盖洛普调查证实了世界范围的员工敬业度危机：只有不到1/3的美国员工对自己的工作是投入、充满热情和全身心付出的。而剩下的数百万员工中，超过一半的人会为了不到20%的加薪而离开公司。在科技行业，有2/3的员工认为他们能够在两个月内找到一份更好的工作。\n曾嵘：这段话描述的情况和我在管理工作中得到的经验是相匹配的。这个结果似乎会让管理者非常气馁，但仔细想来，问题是出现在管理方而不是出在员工方。员工在工作关系中碰到冲突时，大部分会采取逃避的方式。第一种逃避方式是冷漠，表现为工作中的拖延和没有热情的服从。第二种逃避方式是超越，在冲突中退让，看似避免了冲突，但实际情况是不愿意投入能量，不愿意完整地理解对方。第三种逃避方式是分离，辞职、散伙、分手，都是分离的形式。想要在管理中塑造员工的敬业度，需要管理层的智慧和合理的方法。\n德勤的一项调查结果显示：“留住员工和提升员工敬业度是公司领导者第二关心的问题，其重要性仅次于如何迎接构建全球领导力的挑战。”解决员工的敬业度危机，可以从两个方面着手：\n企业需要一个 “明确定义的、被记录下来而且能够分享的目标”， 这个目标能大幅提升员工的工作满意度。这个目标不仅要为员工所知，也要被制度化。OKR 是能 将公司创始人的“往大处想”加以制度化的简单工具。\n企业需要提升一线员工的自主权，构建更适于培育重大突破的创新土壤。许多公司都有所谓的“7人原则”，即向上级直接汇报的下级人数最多不超过7人。在某些情况下，Google 公司将这一规则变为向上级直接汇报的下级最少为7人。当乔纳森·罗森伯格领导 Google 的产品团队时，直接向他汇报的下级多达20位。这个数字越高，组织结构就越扁平。这意味着自上而下的监督层级会更少，一线员工的自主权更大。\n安迪·格鲁夫的 OKR 思想核心 下面几点是 Intel CEO 安迪·格鲁夫的主要 OKR 思想的核心：\n少即是多。 目标应该是精心选定的，每个周期最多只需要制定 3 到 5 个OKR，每个目标都应该与 5 个或者更少的关键结果相对应。 自下而上设定目标。 管理团队和个人之间协商制定的 OKR 应该占到所有 OKR 的一半左右。 共同参与。 集体达成一致，对于最大限度地实现目标来讲是至关重要的。 保持灵活。 因为大环境的变化，可以在执行期间修改甚至放弃某些关键结果。 敢于失败。 如果每个人都把目标定得比轻而易举就能完成高一点，那么结果往往会更好。挑战性的目标能将组织推向新的高度。 OKR是工具，而非武器。 为了鼓励员工承担风险，防止消极参与，最好将 OKR 和奖金激励分离开来。 耐心、坚定。 一个组织需要 4 到 5 个季度才能完全适应 OKR 系统，构建成熟的目标往往需要更长时间。 OKR 的四大利器 OKR 的四大利器是：聚焦、协同、追踪、延展。\n利器一：对优先事项的聚焦和承诺 OKR 需要领导者在言行上做出公开承诺才能艰难实现。当有些 CEO 说“我所有的目标都是团队目标”时，是在释放一个十分危险的信号。领导者必须说清楚为什么做某件事，以及怎样做。员工不仅仅需要通过里程碑式的成功来获得动力，他们还渴望理解辛勤工作的意义，同时了解自己的目标和公司使命之间的关联。领英 CEO 杰夫·韦纳说：“当你不厌其烦地多次强调的时候，团队成员可能才真正开始听你说话。”\n必须在评估活动发生后立刻给予反馈，反馈才会有效果。应该在相对较短的时间内设定 OKR 目标。如果计划是以年度为单位的话，就应该设置季度或者月度的 OKR 目标。OKR 目标时间设定没有明确的标准，各团队应该根据自己所处的行业和企业文化节奏来决定自己的 OKR 节奏。\n目标越弘大，相匹配的关键结果就应该越具体。我们强调在关键结果中包含数字，但数字应该与质量相匹配。下面是一个匹配的例子：\n目标的设定必须符合 SMART原则，因此目标的设定必须谨慎。但法国哲学家伏尔泰说：不要让完美成为优秀的敌人。 因此，在一个目标上纠结数天也是没有必要的。OKR 就是工作的一部分，它并不是一成不变的。OKR 的理想数量往往介于 3 个到 5 个之间。设立过多的目标容易模糊我们对重要事务的关注。\n在实施 OKR 的时候，管理者必须公开对其目标做出承诺，并一以贯之。\nIntel 公司 1980 年第二季度推出的“粉碎行动（与摩托罗拉 68000CPU 的战争）”ORK 以及相关工程部门的 OKR\nRemind 公司招聘 OKR\nNuna 公司高绩效管理 OKR\n利器二：团队工作的协同和联系 乔布斯说： 不要雇用聪明人，然后告诉他们去做什么；而是要让他们告诉我们，应该做什么。\n有 92% 的人表示：如果同事能够看到他们的进度，他们会更有达成目标的动力。但在实际的运营过程中，只有 7% 的员工完全理解企业的经营战略，以及企业为了实现共同目标期待他们做什么。OKR 可以聚焦在重点上，并提升透明度，将每个人的工作与团队工作、部门项目和整体组织使命联系起来。\n适度的层级和关联能加强组织协同。但当所有的目标都沿着组织层级过度关联的时候，组织协同功能就会退化，带来四个方面的不利影响：\n丧失敏捷性。层层的会议，紧密联系的组织和繁琐的实施过程会导致季度 OKR 无法实现。 缺乏灵活性。人们不愿意在周期内对其做出修改。 员工被边缘化。严格的层级系统对一线员工的投入和努力视而不见。 单一维度的联系。垂直纵向会影响水平横向的联系效果。 顶层的 OKR 是众所周知，团队中每个人的 OKR 也是公开可见的，整个组织都能看到正在发生的事情。随着时间的推移，团队所有人的目标也会自然趋于协同一致。与团队整体目标不一致的情况会很明显地自动显现。\n员工需要严格的绩效标准和高层次的目标，但如何开展自己的工作应该始终由员工自己来负责和决决定，这样的做法能激活基层的力量。如果管理者不断对员工决策进行干预，那么员工会对上司给予的期望持保留态度，在解决问题的时候变得不够主动，转而把问题推到上司那里。\n理想的 OKR 系统往往允许员工自主设置目标及大部分或者全部的关键结果。OKR 能引导组织成员向着更高、更远的范围拓展，设定更加宏伟的目标并为之付出努力：“目标越高，绩效越高”。\n在制定 OKR 的过程中，不应该出现 官僚式的服从，而应该是员工发自内心的遵从，即 “热爱式的服从”。 OKR 的透明化会让员工了解团队的优先级目标和约束条件。当有些事情发生偏离的时候，他们会更倾向于相信你。CEO 也必须拥有自己的 OKR，否则员工会问：“如果 CEO 的目标一直不变，那么他在做什么？”\nOKR 系统应更加注重横向连接而非纵向连接。如果有员工制定的 OKR 保持与经理的 OKR 完全一致，这是有问题的。OKR 应该更多地让员工彼此横向相互连接。\n财捷集团的 OKR\n利器三：责任追踪 OKR 系统不需要进行每日追踪，但需要定期检查。一般是每周一次跟进进展、识别障碍、改进关键结果。 当 OKR 目标处于黄色区域时，需要及时调整它使其符合工作流程或者环境变化。只要有需要，随时都可以重新启动一个新的 OKR。如果目标处于红色区域，就要提示达成目标已经有巨大的“风险”，最好的解决方案就是放弃。如果一个承诺型的 OKR 失败，团队必须指定一个补救计划。\n下面是 Intel 公司和 Google 都遵循的 OKR 分数计量标准：\n0.7-1.0 绿色，目标完成 0.4-0.6 黄色，目标取得了进展，但没有完成 0-0.3 红色，目标失败 Intel公司 OKR 评分范例\n对关键结果进行评分不能只看数量完成度。例如一条关键结果是打 50 个营销电话。最终你给 35 个人打了电话，按照数量可以获得 70% 的得分。但这个数字无法确认这条关键结果完成的数量。如果这些电话带来了 10 个新客户，那么可以说这条关键结果的质量是 1.0。如果你只花了 1 个小时就打完了这些电话，只带来了 1 个新客户，那么这条关键结果就只能评为 0.2 分了。\n评分和评估的变量\n自我评估效果是因人而异的，有些人对自己要求严格，有些人却刚好相反。无论哪种情况，都需要一个机敏的调解人或者团队领导参与并帮助重新校准。和客观数字相比，基于情景的反馈和团队内部的广泛讨论更重要。\nOKR是以行动为导向的，但是，如果只是不懈努力而没有偶尔停下来反思，这跟永不停止的“仓鼠轮”没有什么分别。反思可以考虑以下几个点：\n我是否完成了所有的目标？如果是，是什么促成了我的成功？如果没有，我遇到了什么障碍？ 如果我要重新写一个完整的目标，需要做什么改变？ 我学到了哪些经验，可以帮助我更有效地制定下一个周期的OKR？ 盖茨基金会的 OKR\n利器四：充分延展而挑战不可能 创业者就是那些不仅仅思考各种可能性，而且将各种可能性付诸实践的人。 创业者应该敢于设定“胆大包天的目标（Big Hairy Audacious Goals）”。这种目标是一座等待被征服的巨大山峰。它清晰可见，又极具诱惑力，是人们渴望即刻能够“到达”的地方。这个宏伟的目标聚焦了所有人的目光与努力，造就了团队精神，并激发全体员工为了一致的目标而奋勇向前的信念。\nGoogle 将他们的 OKR 分为两类，一类称为承诺型目标，另一类称为愿景型目标，二者有着本质的不同。承诺型目标与 Google 的日常考核指标紧密相连，它是必须完成的。愿景型目标反映了更宏伟的蓝图，更高的风险，侧重于未来导向，它是极难实现的（平均失败率为40%）。Gmail 邮箱就是一个愿景型“10 倍速”目标的产物。在谷歌的 OKR 管理氛围中，70% 的完成率（平均而言）就被认为是成功的了。你不需要将你设定的每一个OKR都实现，那样反而不会起到激励团队的作用。但是，这并不意味着在谷歌没有压力，事实上，谷歌的员工承担着很大的压力，因为，如果你不能驱动自己完成目标，你就会被解雇。拉里佩奇希望谷歌的员工保持“不舒服的兴奋”，他希望员工能“正视不可能”。\n在 YouTube 刚开始使用 OKR 工作时，有 800 多名员工。他们会为每十个人设定三四十条目标，但目标完成率达不到一半。有两个原因影响了工程师们设定目标的准确性，一方面他们讨厌放弃自己认为的好想法，另一方面他们习惯性地低估了完成任务所需要花费的时间。YouTube 管理层后来依靠明确的规则迫使人们将其团队目标清单缩减到3至4个。\n在设定目标的时候可以遵循是由史蒂芬·柯维提出的 “巨石理论“： 假设你有几块石头，一堆鹅卵石和一些沙子，你的任务是尽可能地把所有东西都装进一个广口瓶中。如果你先放沙子，再放入鹅卵石，那么再想放石头时你会发现瓶子已经没有空间留给它们了。然而，当你先放石头，再放鹅卵石，最后放沙子，你会发现一切如你所愿——沙子将石头之间的缝隙填满了。这就告诉我们要善于抓住主要矛盾，重要的事情要先做，否则有可能永远都没有机会去做了。若人们各自忙于自己的工作，看起来好像是“百花齐放”，但是，没有人关注公司最高级、最重要的OKR。从巨石理论的角度，员工在开展工作之前，必须明白自己的“石头”在哪里。\nYouTube 也使用 OKR 来激发挑战。他们在公司的目标旁边分别用绿色、黄色和红色的彩色条来标记目标进度，并在彩色条的前方写上每个人的名字，看起来这个目标是这个人必须完成的。有挑战性的目标可以促使整个组织进行再设计。它激发了整个 YouTube 公司最根本的创造性和主动性。\n如果人们不相信挑战性目标是可以实现的，那么它就真的无法实现， 这就是设定目标的艺术所在。在工作中，我们必须为自己设定让自己感到不适的极具挑战性的目标，然后去实现它。随后，我们必须在短暂的庆祝后为自己制定另一套非常难以达到的目标，然后又必须实现它。而实现这些具有挑战性的目标的奖励之一便是，你能够不断获得晋级的机会。\nYouTube 的 10 亿小时 OKR\n持续性绩效管理 现有的年终考核的弊端 大部分公司都在使用年度绩效考核的方式来评价员工。每个绩效考核要消耗每位管理者约 7.5 小时的时间。仅有 12% 的人力资源主管认为这个过程在推动商业价值方面“非常有效”。只有 6% 的人认为值得花时间去做这件事。受近因效应、员工排名，以及正态分布曲线的约束和影响，这些年终评估报告无法公平公正地衡量员工的绩效。爱因斯坦说过： 并非所有可以用数字计算的东西都是有价值的，也并非所有有价值的东西都可以用数字来衡量。 我们管理的是活的员工，不能将人数字化。管理者需要和他人建立有效的人际关系，激发共同的信心，构建命运共同体。\n在世界 500 强中已经有 10% 的公司放弃了一年一度的绩效评估体系，采用了另一种灵活的、持续沟通和实时反馈方式来取而代之。这种方式被称为“持续性绩效管理”。\n年度绩效考核与持续性绩效管理\nOKR 和绩效评估 要实施持续性绩效管理，第一步要做的事情就是把薪酬体系与 OKR 分开。不管事加薪还是奖金都不能和 OKR 直接相关，因为这是两种不同的对话机制，有各自的周期和节奏。年度绩效管理是回顾性的评估，通常在年底举行。而持续性绩效管理是领导同员工之间持续跟进的前瞻性对话，通常以五个问题为中心：\n你正在做什么？ 你做得怎么样？你的OKR进展如何？ 你的工作有什么阻碍吗？ 你需要我提供什么来帮助你实现目标？ 你需要什么帮助来实现你的职业目标？ 曾嵘： 和员工 ONEONE，员工通常会认为自己处于弱势的地位。如果在提问题的时候，能以聊天对象为中心（例如上面的问题），就可以让整个聊天处于一个平等的氛围中，这会取得出其不意的效果。\n个人报酬的增加可能会消除一个人对目标完成得分的偏见。OKR 能够恰当反映一个人的工作价值，是一个可靠的周期性反馈来源。但是当目标直接与薪酬挂钩时，员工就会更在意得分而不是真正的目标。让我们来比较下面两个员工 A 和 B：\n员工 A 设定了一个很困难的挑战性目标，她完成了设定目标的 75%，那么她的优异表现是否值得获得 100% 或是 120% 的奖金呢？ 员工 B 达到了他的关键业绩的 90%，但是他的经理知道他并没有竭尽全力，更重要的是，他还缺席了好几个重要的团队会议，那么他应该获得比员工 A 更多的奖金吗？ 很显然，员工 A 的 OKR 得分虽然不如员工 B，但应该得到比员工 B 更高的报酬。\nGoogle 的 OKR 只占绩效考核比重的 1/3 甚至更低。他们还可以从跨职能团队的反馈中获得员工的综合信息，对反馈的结果，都会结合具体情况而加以综合考虑。\nOKR 和薪酬是 \u0026quot;好朋友“，但不必生活在一起\nCFR 的概念 在启用 OKR 的过程中，应该同时深入使用 CFR，它是有效沟通的刺激物，能激发 OKR，将其送入正确的轨道。CFR 是指：\n对话（Conversation）：经理与员工之间真实的、高质量的交流，旨在对绩效提升起到驱动作用。 反馈（Feedback）：同事之间面对面进行双向沟通或通过网络进行交流，以评估工作进展情况并探讨未来的改进方向。 认可（Recognition）：根据个体所做贡献的大小施以对等的表彰。 CFR——对话 安迪·格鲁夫认为，管理者与下属的谈话“将提升下属的工作质量，90分钟的谈话可以影响下属两周的工作效率”。在 CFR 的执行理念下，经理们的角色由监工转化为指导者、辅导者或是引领者。经理和员工之间的 一对一谈话的关键是以员工作为主导。 主管向下属传授他所掌握的技能和知识，提出解决事情的具体方法。同时，下属也应向主管详细说明他正在做什么事情，以及他所担心的问题。会议的内容和整个基调是由员工决定的，而主管的作用是倾听并做出指导。\n管理者和员工之间的对话通常出现在五个关键领域：\n目标设定和反思：员工的OKR计划是为即将到来的周期设定的，讨论的重点应当是如何将个人目标和关键结果与组织的当务之急进行最有效的结合。 持续进度更新：依托数据对员工工作的实时进度进行快捷监督，并随时待命准备解决问题。 双向辅导：帮助员工发挥他们的潜能，并帮助管理者更上一层楼。 职业发展：帮助员工提升技能，发现成长的机会，并让其看到未来在公司的升职空间。 轻量级的绩效评估：这是一种以组织需求为基准，将上次会议以来的组织投入与员工产出进行总结比较的反馈机制。这一谈话与员工年度薪酬和奖金无关。 绩效沟通要点（书附录资源2）\n目标规划和反馈 过程升级 来自管理者的直接指导 向上反馈 理解和关注员工的职业发展 管理者和员工的提前准备 CFR——反馈 反馈必须足够具体。下面是反馈的两个具体例子：\n负面反馈：“由于你上周的会议组织晚了，才导致了这种一团糟的结果。” 正面反馈：“你的演讲真是太棒了！先是通过开场白吸引了全场的注意力，又用我最喜爱的极具指导性的‘下一步规划’来完美收尾。” 双向（或360度）反馈是持续性绩效管理的一个附加手段，随着OKR与360度反馈的完美结合，传统的绩效考核方式将很快成为历史。\nCFR——认可 文化认可度高的公司的员工自愿离职率比文化认可度低的公司低 31%。\n认可需要建立明确的标准。可以在一个确定的时间（例如每周五）组织对杰出贡献成员给以主动、真诚的称赞。要正确识别员工目前进行的是行动还是结果：完成特殊项目、实现公司目标，还是展示公司价值。同时用“当月业绩”代替“当月最佳员工”。提高认同发生的频率和可获得性。即便是很小的成就，也应当予以赞扬。\n曾嵘：在认可的过程中，要展示员工在公司中的价值，而非凸显个人价值。这种认可才能提升员工对公司文化的认同。在对员工的赞扬中，管理者往往会忽视一些小的成就，将其作为理所当然。应该从员工的角度来考虑这些成就。\n一些企业的持续性绩效管理实践 Pact 的持续性绩效管理推进四要素\n雇员与经理每月进行一次关于事情进展的一对一谈话。 对OKR进行季度审查。我们会一同坐下来进行沟通：本季度的计划是什么，哪些能做，哪些不能做，为什么会出现这样的状况，以及我们可以做出哪些改变？ 我们还会进行一个半年度职业发展座谈。员工们可以陈述他们的职业规划：他们从事过什么岗位的工作，现在在什么岗位，将来想要去什么岗位，以及他们的工作经历和需要组织对他们的规划提供什么支持。 持续而自我驱动的洞察力。 Adobe 的签到模式\n在 Adobe，每年 2 月都会有大批员工因对考核结果感到失望而自愿离职，到其他公司去施展他们的才华与抱负。2012 年，Adobe 开始推行新的签到模式（Adboe 的持续绩效管理方案）。管理层之间、管理层和员工之间、员工和员工之间每年都要进行多次会谈。在整个组织中，管理层也不再仅仅依靠人力资源团队进行员工管理，而是主动投身于整个过程，进行掌控。原来强制的等级排名也被年度签到奖励所取代。管理人员也逐渐学会 根据员工的表现、对业务的影响力、技能的相对稀缺性及市场状况来分配薪酬， 不再墨守成规。在推进签到模式时，领导者的角色至关重要。领导必须要 树立标杆、以身作则。 他们需要表现出自己 乐于接受反馈，并且欢迎别人对自己的想法提出质疑的态度。\n根据 Adobe 的经验，想要运作好一个持续性绩效管理系统，需要做到如下三点：\n管理层的支持； 明确的公司目标，以及它们如何与个人目标相匹配； 针对提高管理者和领导者的效率进行适当培训和投资。 Adobe新旧绩效管理方法对比\n没有什么比员工更有价值，特别是那些充满自信、相信自己能够创造价值并愿意为公司长期效力的员工。人员流动的代价是昂贵的。最好的人员流动方式是内部流动，即员工在你的公司中实现职业发展，而不是跳槽去别的企业。人们天生不愿成为流浪者，他们只是想找到一个觉得可以体现自身价值的地方。\nZume 公司的案例\n在 Zume，每一位新入职的员工都要进行使命和价值观的培训才可以正式上岗。每两周，Zume 的员工需要选择一个人与之进行一对一谈话，谈话的时间为一个小时，谈话的对象根据自己的意愿来选择。谈话不能迟到，不能取消。对话只有一条规则：不能聊工作。聊天内容围绕着你未来两三年想要达成的个人心愿进行，员工需要在说明如何把它们分解成两周的计划。亚里克斯喜欢以三个问题开启这场对话：什么事情让你感到高兴？什么事情会耗费你的精力？你的理想职业是什么样的？\n完成谈话之后，管理者就能感知到何时需要拨出一些时间为员工充电。例如可以在团队全面完成了季度目标后，在下一个季度拨出一些时间给员工去实现他们的个人发展目标。管理者可以拿出5%到15%，甚至20%的时间给员工去做他自己的事情。这听起来好像是一笔巨大的开支，但它却可以为公司带来未来两三个季度的高效执行力。\n当员工在公开的讨论中质疑管理者时，管理者需要停下来思考一下这个人说的话是否有价值。管理者需要努力做到这一点，致力于为员工创造一个言论自由、兼容并蓄的工作环境。\n管理者也需要关注结构化的目标设定。这是一种思维习惯，必须在企业初期养成。否则，企业的扩张规模往往会超过管理团队的能力，这就会导致两种结果：公司倒闭，或者管理团队被取代。一开始就要培养员工的管理意识，即使该部门只有一个人。\n提升绩效最简单的方法就是强制人们对业务进行深刻、透彻和关联性的思考，这对绩效的提升具有很好的促进作用。整个 OKR 流程迫使分工必须明确。可以用接球的例子来明确分工的重要性： 当一个球飞到两个球员之间时，必须有一个人去接住它，否则球就会坠落；如果两个人同时扑向它，又会导致两个人撞向对方。问题在于，球应该由谁来接呢？\nZume 公司将市场营销部和产品部是两个“球员”，两个部门的领导已经共事一个多月，但他们无法制定出合理的 OKR 目标。通过谈话和讨论，最终他们将收益目标拆分为两个子目标： 新增收入和重复收入。 第一个目标由市场营销部承担，第二个目标则由产品部承担。于是两个部门领导便从自己的目标着手设定 OKR。这次谈话不仅仅让两个部门共同支持了公司整体的目标，更让公司管理者和部门管理者加强了沟通，提高了目标的透明度。\n一个基于实际的 OKR\n一个基于使命的 OKR\n文化的重要性 坚持公司文化价值观的员工将在各种相似的条件下表现出一致的行为，这就意味着管理者无须担心由常规制度、程序和规章等带来的低效率。管理需要发展和培养共同的价值观、目标和构建信任的方法。那么我们该如何做呢？ 一种方式是以口头或文字的形式传达，另外一种更重要的方式则是树立标杆。\nIntel 的文化价值体系\n以人为本： 重视承诺；尊重每一位成员的工作；愿意迎接挑战和机遇 公开：重视已出现的或可能出现的问题 解决方案：简洁而干脆；冲突必须是有建设性的 结果：所有工作以结果为导向；一知半解是不被尊重的；以积极的回馈来奖励成功 原则：在一个高度竞争、复杂的环境中想要表现良好需要坚守原则 风险承担：高科技导向必然伴随着高风险；不害怕失败和自我揭露；防范风险，捍卫利益 信任和诚实 集体责任感、无畏的风险承担精神和可量化的成果是 Intel 和 Google 公司都高度推崇的。在 Google 公司的一项大型研究表明，团队的绩效表现与下面五个问题息息相关：\n结构和清晰度：我们团队的目标、角色和执行计划是清晰而明确的吗？ 心理安全：我们能够感到安全而且从容地在这个团队中承担风险吗？ 工作的意义：我们是否在做一些对我们每个人都很重要的事情？ 可靠性：我们能彼此信赖并按时完成高质量的工作吗？ 工作的影响：我们是否发自内心地认为我们做的工作是真正有意义的？ OKR文化是一种强调责任的文化。你不会仅仅因为老板给你下了命令就应付了事，而是会竭尽全力认真地完成每个目标，不只是因为它们对公司至关重要，更是因为你不能让信任你的团队成员失望。没有人想成为拖后腿的那个人，每个人都会由衷地为团队的进步而感到自豪。这虽说是一个社会契约，但也可以说是一种自我约束契约。\n在透明度高的文化中，人们更加开放，愿意分享真理，愿意接纳他人，并且更加敏捷。OKR/CFR 所倡导的文化就是透明文化。组织结构越扁平，组织就越敏捷。当绩效管理成为一个网络化和双向的系统时，员工的个人表现也会越来越好。\n文化也是一种筛选机制，它决定企业招聘什么样的人，选拔秉持什么价值观的员工。在 Lumeris 公司，为了对抗第一次 OKR 实验的失败，管理层在不到18个月的时间里，替换了公司中 85% 的人力资源管理人员，花了 3 年的时间建立了新的公司文化。\n与其说目标设定是一门科学，还不如说它是一门艺术。Lumeris 公司通过引导员工深入思考下面的问题来完成文化重建工作：\n为什么透明度非常重要？为什么你想让其他部门的同事知道你的目标？为什么我们现在做的工作很重要？ 什么是真正的责任感？“尊重型问责”（对于别人的失败）和“脆弱型问责”（对于我们自己的失败）的差异是什么？ OKR 如何帮助经理借助他人完成工作？我们如何吸引其他团队将我们的目标作为优先级目标，并且帮助我们完成目标？ 什么时候是增加或者减少团队工作量的合适时间？什么时候将目标转移至其他的团队成员？什么时候重新设定目标让其更加清晰？什么时候完全改变目标？在培养团队自信心的时候，时机非常重要。 Lumeris 公司吸引新人才的 OKR\nGoogle 公司的 OKR 模版 用色彩来打分 参见 利器三：责任追踪 一节。\n设计 OKR 目标是什么\n明确目标和意图 有进取心，但又认清事实 有形的，客观的，明确的 目标成功能给公司带来明确价值 关键结果怎么做\n描述结果而非行为：如果包含“咨询”、“帮助”、“分析”、“参与”等词汇，这是在描述行为，是不合适的。应该描述这些行为对终端用户产生的影响。 必须包含完整的证据 曾嵘：雅虎的 CEO（也是 Google 前员工）Marissa Mayer 说过：It's not a key result unless it has a number.\n承诺型OKR\n一定要实现的目标 1.0分 达不到就必须重新评估 需要解释未完成的原因，这说明制订 OKR 的时候有失误 无法达到 1.0 的团队需要立刻升级 愿景型OKR\n完成承诺型之后的空余时间 平均得分 0.7，可能有 40% 的 OKR 无法完成 可延展到下个周期 不允许删除 承诺型OKR应该会消耗掉一个团队大部分资源，但不是全部资源，而承诺型OKR再加上愿景型OKR所消耗的资源，应该会超过企业的全部资源。如果一个团队不需要利用全部团队成员或资源就可以满足所有OKR，那只能说明他们要么是囤积了资源，要么是没有设定具有挑战性的目标，或两者兼而有之。\n典型的 OKR 周期 周期开始前4-6 周：公司制定年度 OKR 周期开始前 2 周：全公司范围内讨论 OKR 设定 开始：讨论设定团队第一季度 OKR 开始 1 周：分享员工第一季度 OKR 开始后的整个周期：员工进度追踪与签到 接近结束：员工反馈和为第一季度 OKR 打分 典型 OKR 周期\n全文完 ","date":"2020-03-12","description":"","lastmod":"2020-03-12T07:19:40Z","slug":"reading-notes-measure-what-matters","tags":["reading","readingnote","management","okr"],"title":"读书笔记：《这就是 OKR：让谷歌、亚马逊实现爆炸性增长的工作法》","url":"https://blog.zengrong.net/post/reading-notes-measure-what-matters/"},{"categories":["impressions"],"content":"2020 年第 11 周我读过的部分有价值的文章之内容摘要。\n贝佐斯的管理风格 新加坡的疫情防控 伯格森悖论 国际法的窘境 《南极洲》 杰克·韦尔奇2001卸任演讲 贝佐斯的管理风格 贝佐斯会使用“下水道盖子为什么是圆的”这问题来试探人的逻辑敏锐性。他在招人上奉行达尔文主义：每雇一个人，这个人就要提升下一次雇人的标准，这样整个人才库水平才能持续提高。\n贝佐斯常用的训人语句：\n你是懒呢还是笨呢？ 这文件显然是二级团队写的，一级团队的文件呢？ 你为啥要毁了我的生活？ 贝佐斯对细节专注到连文件的脚注和备注都会盯住不放。一位前高管说，“如果你要和贝佐斯一起开会，你得做好世界末日来临的准备。你寻思，‘我已经准备了三个礼拜啦，我已经叫每个该死的熟人提了所有他们能想出来的问题。’ 可是贝佐斯就能提出那个你没想到的问题。”\n贝佐斯创建了一个新职位：“技术顾问”，这些技术顾问要跟着贝佐斯学习至少一年，然后变成“杰夫版机器人”。贝佐斯的高度个人化管理风格被编撰成各种系统和规程。这些技术顾问能保证就算他不在场，他的“势场”也在。\n贝佐斯的最高团队是 S 团队（The Senior Team）。一共十七位高管有家人般的情感，定期开会。这些人已经吃透了贝佐斯的方法，并将其运用到公司每一个角落。领导层受到的训练就是在数据中找到漏洞。当亚马逊的高层听到“多元化”这个词的时候，他们觉得这是在“降低标准”。\n为了避免各组的膨胀，贝佐斯打造了“两个披萨饼团队”的传统。按照他的理论，理想的亚马逊团队应该小到两个披萨饼就能喂饱。小团队能增长项目主人翁意识，团队成员对失败的恐惧感会更强——因为出错不能像在大团队中那样被隐藏，或者由更多人分担责任。\n曾嵘：SAGITEAM 的光荣吃鸡传统一次只要两三百块，现在逐渐增长到五六百块，这也增长了请吃鸡团队成员的心理负担。\n贝佐斯坚持所有计划必须做成六页长的备忘录，以”叙事“的方式用完整的句子写出。他认为 PPT 已经成为掩盖模糊思考的工具，而写作需要更线性的推理逻辑。如果写不出来，那么肯定无法为它辩护。会议开始的时候，与会者需要在“自习室”的气氛中阅读这六页备忘录之后才能开始提问。这保证了所有人都能全面消化会议内容，而备忘录作者也会在这段时间内如坐针毡。\n一旦六页备忘录的计划得到批准，执行人在实施该计划时就有很大的自由度，基本上不再需要批准层层闯关。\n新加坡的疫情防控 新加坡号召国民要求国民不要佩戴口罩，学校和小区也没有封闭式管理，是真的“佛系”么？\n截至3月5日，新加坡确诊新冠肺炎117例，治愈出院81例，治愈率高达68%。\n新加坡进行了提前布局，基于 DORSCON 预警机制和全国警察系统进行确诊患者的全传播链条分析，在医疗保障上将口罩留给最需要的医务人员，拨付资金支持看病以及隔离企业，制订铁腕政策惩罚不服从隔离令的人员，建立官方打假渠道要求所有媒体链接。这些措施有力保证了新加坡的疫情始终在政府的监控之中。\n提前布局 1 月 2 日，新加坡卫生部发公告要求留意疑似肺炎感染者，彼时武汉卫健委还在通报“未发现明显人传人证据”。1 月 29 日，禁止 14 天内前往湖北的旅客和持有湖北护照的旅客入境或者转机。1 月 31 日，禁令延伸至 14 天内去过大陆的旅客。对于新加坡籍入境者，启动强制休假。\n疫情分级和全链条分析 基于 SARS 和 H1N1 的防控经验构建的 DORSCON 系统，分为绿黄橙红四个疫情程度。SARS/H1N1/COVID-19 都是橙色。\n用警察来追踪传染病，每个病例都做了传播链条分析。\n医疗和保障 2 月初就生产了 1.5 万的试剂盒，自己留下 5000 个，10000 个捐给了武汉。政府补贴，鼓励呼吸道疾病去看病，固定费用 10 新元（50人民币），老人 5 新元。\n购买 520 万个口罩发给 130 万个家庭。2 月 8 日后返回新加坡的人，强制隔离 14 天，带薪休假，不算年假，政府补助每人每日 100 新元。\n口罩匮乏后制定以医务人员为优先的原则：\n健康人不需要戴口罩 N95 留给医护人员，普通人不要戴 咳嗽和喷嚏戴普通医用口罩 保持个人卫生和常洗手优于戴口罩 铁腕政策 违反隔离令罚款 1 万新元或最高 6 个月监禁。有 1 名 45 岁男性因此丧失永久居民身份并禁止再次入境（新加坡的永久居留权非常难以获得）。\n舆论监控 根据 POFMA 《放置网络假讯息和网络操纵法案》，建立政府打假页面，所有社交媒体信息链接到政府打假专题页面。\n伯克森悖论 伯克森悖论（Berkson's Paradox）是一种统计学现象。它的常见形式是：如果你对两个特性有一个总体的阈值要求（这两个特性哪怕没有关系，甚至原本可能是正相关），在你考察的那个范围内，也能让你感觉到它们有负相关性。\n伯克森悖论的例子 上面这么说可能还是不容易懂，下面的几个例子展现了伯克森悖论。\n长得帅的人性格都不好；长得漂亮的人收入会更高。 有名的作家的小说不深刻；有学问的人大多不善言辞。 因为车祸被送进医院急诊室的人摩托车手，戴头盔的比不带头盔的受伤更重。 银行贷款数据中，民企获得贷款的效率比国企高。 Google 的机器学习系统发现自己员工在编程比赛中获奖的人能力相对低。 寒门出贵子；为富不仁；仗义每从屠狗辈，负心多是读书人；杀人放火金腰带，修桥铺路无尸骸。 来说明一下。\n长得帅和赚钱多 大家认为被选择的样本（长得帅又性格好的人）在下面的区间：\n但实际上，选择的样本在这个右上角三角区间：\n这是因为我们对于样本选择的时候是有阈值要求的，长得帅+性格好 \u0026gt; 某个阈值 才是样本区间。这就是整个分布的右上角。在这个区间中，长得越帅且性格不好的人比长得帅但性格好的人要多。\n真正的选择不应该放在帅的极值上，而应该放在不那么帅的人身上（如红色矩形部分），此处的选择会多很多。\n长得漂亮的人收入相对高也是一个道理，因为长得丑但收入高的人被排除在样本选择范围之外了。毕竟马云只有一个对不对？\n深刻的作家和有学问的人 作家要有名，要么作品很深刻而受到评论家的推崇，要么作品很通俗受到大众喜爱。但流行不代表没有深度，也不是只要没有深度就能流行。\n有学问的人可能善言辞也可能不善。没学问的人也一样。有这种印象的原因是选择样本中“有学问的人”居多。\n戴头盔的摩托车手，民企和 Google 戴头盔的摩托车手更不容易受重伤，可能不会来医院。这部分的样本丢失了。\n民企如果不能更高效，其实是很难获得银行贷款的。国企即使低调一点，但有国家兜底，和民企的竞争不在一个层面。\nGoogle 招人如果只看实际能力，那么招到的人是这样的：\n而面试的过程不可能完全考察实际能力，因此比赛成绩会作为一个很重要的考察项，那么招到的人是这样的：\n这就会出现一些样本变化，有一部分考试成绩高，但实际能力较低的人也通过了招聘。而一部分比赛成绩低但能力高的人被剔除了。\n总结 平庸寒门，守法富人，不仗义屠狗辈，真诚的读书人并没有什么可以在社会中表现出来的亮点，也不会有人宣扬他们的事迹。伯克森悖论和人们熟悉的“幸存者偏差”一样都属于选择偏差，出错的根本原因是样本不全。\n国际法的窘境 有个段子是这么说的：联合国调节小国与小国的矛盾，矛盾没了；联合国调节大国和小国的矛盾，小国没了；联合国调解大国与大国的矛盾，联合国没了。\n这个段子充分说明了联合国以及国际法在国际纷争中的窘境。\n国际法是较弱的法律，是不完善的法律，它仍然处于进化中。国际法没有强制管辖权，力量是有限的。\n遵循国际法给我们带来益处 1884 年国际经度会议决定全球时间的换算标准。 1878 年《万国邮政公约》规定国际邮件转运自由原则，让我们可以海淘成功。 《国际民用航空公约（芝加哥公约）》决定了民用飞机可以在国家领空畅通无阻。 1865 年的《国际电信公约》让我们今天可以越洋通话。 《联合国国际贸易货物销售合同公约》保证了我们能享受世界各国的产品。 如果两个企业约定使用仲裁解决纠纷，依据《纽约公约》，各国法院无权审理该争议。 1953 年英法两国围绕英吉利海峡的两个小岛归属地的争端，就是由国际法院审理和判定的。 违反国际法的案例 美国在 1946 年就表示接受国际法院的强制管辖权。1984 年，尼加拉瓜在国际法院诉美国要对其在该国境内的军事行动负责。美国先是表明当时的声明不含拉美国家，然后直接撤出了诉讼。再也没有重返国际法院。 1928 年签订的《巴黎非战公约》在 1931 年就被日本侵略中国、1939 年德国侵略波兰所违反。 2003 年美国违反《联合国宪章》入侵伊拉克。 2017 年特朗普违反《联合国宪章》下令对叙利亚进行空袭。 国际法对大国的作用 当今国际社会依然是弱肉强食的现状，但各大国在遇到国际法问题的时候，在采取军事行动的时候，依然要寻找国际法的依据。\n《南极洲》 南极洲作为一个有着一千多平方公里土地，占有地球上 75% 的淡水资源，拥有巨大的煤矿、铁矿是有和有色金属资源的无主大陆，为什么到现在还没有一个国家拥有主权，甚至联合国也无法管辖它？\n曾嵘：《南极洲》这本书中的信息是上面 国际法的窘境 一文很好的注脚。\n先占原则 英国的库克船长在 1773 年首度横跨南极圈但没有登陆。1820 年登陆南极洲的沙皇俄国的海军军官别林斯高晋被认为是发现南极大陆的第一人。在那之后，挪威的探险家阿蒙森和英国的探险家斯科特还有一场抢登南极点的竞赛。\n宣誓主权的世界惯例是：1. 探险家登陆宣誓主权。2. 政府公告，殖民，派军保护殖民并占领。但南极洲由于气候条件恶劣无法驻军以及永久殖民占领，导致了各国无法真正拥有南极洲的主权。\n共管原则 美国在二战后建立了一个联盟期望一起共管南极洲。但阿根廷、智利和澳大利亚坚决反对，挪威和法国也不同意。1950 年刚发射了世界第一颗人造卫星的苏联也入局争夺，发布了声明：如果南极洲事务不让苏联参与那么无论讨论出什么结果，苏联都拒不接受。\n南极条约 在 1957 年（国际地球物理年）科学家们为了解决地球气候问题，举办了一次跨国科学活动。这是冷战以来第一次有东西方科学家共同参加的活动，南极洲正好是这次活动的讨论话题。因此有国家提出了和平处理南极事务的提议，在 1959 年这些国家一起签订了《南极条约》，南极从此进入和平开发的科学时代。\n根据 国际法的窘境 中谈到的，南极条约也是一部国际法。\n南极条约第一条就强调：“南极应该只用于和平的目的，一切有军事性质的措施都是被禁止的。”\n接受《南极条约》的国家有“协约国”和“缔约国”之分，只有“协约国”才能协商修订条约的权利，这和联合国所有成员国都有投票权是不同的。\n1956 年印度提出把南极交给联合国管理，但有几个国家坚决反对，此事就无疾而终了。\n杰克·韦尔奇 2001 卸任演讲稿 主要成绩 杰克·韦尔奇于 2020 年 3 月 2 日去世，他是二十世纪最佳经理人，全球第一 CEO，美国当代最成功的最伟大的企业家。\n1981年至2001年，韦尔奇担任通用电气CEO。通用电器市值从120亿美元成长到 4100 亿美元。\n争议 一味迎合对华尔街对股价的期待。 选了一个失败的继承者。 如何评价韦尔奇 评价 CEO 的三立模型：立功、立言、立框架 韦尔奇高度重视人力资源管理，他认为在任何企业里，人力资源负责人都应该是二把手。 活力曲线，末尾淘汰法则。 每一代 CEO 都有自己的责任和使命，做好了自己应该做的，剩下的应该交给下一代企业家。 公司这项制度本身就有缺陷，而且一直在改进。对韦尔奇的质疑，是对“完美人”的期待。世界上没有完美人，韦尔奇完成了他的使命。 韦尔奇 2001 年卸任演讲稿 1. 诚信是最重要的价值观。\n2. 勇敢做出变革，拥抱新的机会，不要因为害怕人心惶惶而退缩。\n3. 真正的顾客导向精神是伟大企业的特征。\n4. 用好大规模优势，憎恶官僚主义，嘲笑官老爷作风和臃肿的机构，减少管理层次。\n5. 让员工有机会尝试和冒险，利用小成功不断给他们注入自信。自信的人言简意赅，注重速度。\n6. 领导力\n领导力的四个标准：\n1. 应对快速全球化的精力 2. 调动组织积极性的号召力 3. 做出艰难抉择的魄力 4. 贯彻到底的执行力 四类经理人：\n1. 即认同企业价值观又有业绩：升职 2. 既不认同企业价值观有没有业绩：炒掉 3. 认同企业价值观，但还没有业绩：再给一次机会 4. 不认同企业价值观，就有业绩：零容忍。他们会吸干任何组织的活力。**自私人的业绩以牺牲他人为代价，** 将自己永远排在第一位。 7. 六西格玛：学习型组织。每个部门确保前 20%的人都接受过 2 年的黑带任务。\n8. 吸纳最优秀的人才，营造激动人心的工作氛围。失去最优秀的前 20%人才是领导的失败。留住最差的 10%也是过错。 在人们年轻的时候就告诉他们自身的缺陷， 不要让你的继任者来做这件事。\n9. 非正式，非官僚化。互相信任，平等称呼，每个人都有话语权。不管头衔是什么，都能直接处理问题。 有经理，但没有“经理派头”。\n10. 互相学习，向其他公司学习，公司内部外部互相学习。每天起床都要找到提高的方法，不要习惯性出现错误。\n参考文章 贝佐斯的大棋 新加坡\u0026quot;佛系防疫\u0026quot;，为什么能控住疫情？ 不戴口罩不封城的新加坡，是如何从重灾区变成抗疫“优等生”的？ 得到 万维钢精英日课 4 得到 刘晗法律思维 30 讲 杰克·韦尔奇的最后一次管理层会议 全文完 ","date":"2020-03-08","description":"","lastmod":"2020-03-08T12:05:39Z","slug":"weekread202011","tags":["reading","weekread"],"title":"周读：202011 长得帅的人脾气都不好，长得漂亮的人收入相对高","url":"https://blog.zengrong.net/post/weekread202011/"},{"categories":["impressions"],"content":"2020 年 2 月 4 日，云开工的第二天，我写了这篇：云开工！疫情挡不住武汉人的工作热情！。大量企业都在那天云开工，导致钉钉、企业微信和腾讯会议不堪重负，服务器同时挂掉。到今天（2020 年 3 月 6 日），SAGITEAM 已经在线办公整整五周了。\n我们可以自豪的说，SAGITEAM 的在线办公还原了集中办公 85% 以上的生产力。在特定的岗位和时机，在线办公的生产力甚至超过了集中办公。\n毕竟……终于可以把每天通勤消耗的 2 个小时用来加班了 😄\n下面我就讲讲 SAGITEAM 在线办公经验。\n1. 先讲困难 影响效率的最大的问题依然是设备\n在文章 云开工！疫情挡不住武汉人的工作热情！ 中，我介绍了 SAGITEAM 是怎么八仙过海各显神通弄到办公设备的。云办公的前三周，湖北的物流处于停滞状态，在京东上购买的设备一直推迟发货，部分美术同学只能 鼠标作画，有个策划同学用 纸笔写案子，还好开发同学在云开工前都弄到了电脑，最终也没有实现 手机编程。\n恶劣天气和停电\n天冷加上停电，有的同学手机被冻关机了。\n人间自有真情在，居委会送爱心菜\n拿了爱心菜还搭上个口罩。\n家里有地的男人真是让人羡慕啊！\n2. 解决重点问题 设备问题是必须解决的，湖北境内的同事们想方设法，通过下面几个渠道，最终把设备全部配齐：\n出口转内销：因为湖北境内快递停运，美术同学辗转找到北京的经销商，发 EMS 解决了绘图板问题； 以租代买：租电脑并不贵，可是押金真心贵； 内部解决：有同事找村里的黄牛买了台电脑，黄牛还是很敬业的，并没有收太高的溢价。 3. 制度决定效率 效率不高不能只怪干活的人，这很可能是管理的锅。效率问题都可以用制度来解决。\n3.1 考勤、打卡和每日总结 我们规定了上午上下班和下午上下班的明确时间。上午/下午 的 上班/下班 时间点，SAGITEAM 成员需要在各部门群中 @部门负责人 进行打卡。迟到、请假、加班 与日常办公的标准相同，由部门负责人核实。\n集中办公时，SAGITEAM 早晚打卡也是采用这一套机制进行的，不需要花时间熟悉。增加两次打卡（上午下班和下午上班）的目的是保持在线办公的仪式感，让大家对时间有足够明确的概念。\n在 每日结束工作 时，每人需要在 自己所在的项目群 中汇报当天的工作内容。工作内容汇报给制作人和部门负责人。同时参加多个项目的同事，需要在多个项目群中汇报。\n每日总结是非常好的习惯，它不仅能强制执行人反思每天的工作，也能让项目所有成员加深对彼此工作的了解。\n3.2 每日晨会和会议记录 各项目制作人 自行以群语音会议的形式安排项目晨会。在家办公期间，晨会 必须 每日举行，每次晨会时间原则上不超过 15 分钟。有些同事同时参加多个项目，因此 SAGITEAM 所有项目的晨会的时间是错开的。晨会结束之后，项目制作人 必须 提供会议记录。有些项目每天上班和下班都有碰头会。\n这个要求刚开始执行的时候，也有制作人会质疑这个要求的可行性，认为整理会议记录会花掉大量时间。执行一个月一来，大家已经习惯了这项制度，一般能在会议结束后 5 分钟内发布会议记录。\n3.3 周报 项目制作人和部门负责人 必须 提供周报。周报的重要性毋庸置疑。写好周报是项目或部门的负责人的基本功。所有需要写周报的人也能看到其他人的周报，这让部门和项目之间的信息交流更加通畅。\n4. 日常节点控制与关键会议 不能定好规范后就当甩手掌柜。规范的执行和落地，需要全体人员的主动性和多次循环。管理者需要对所有项目的进度有全局把握，在关键节点及时组织会议讨论。管理者还要在每天的晨会中找到产品运行和开发过程中的蛛丝马迹，把问题放大到让项目成员重视的程度，把隐患消除在还没发生之前。\nSAGITEAM 把这种会议叫做 关键会议。问题会全方位出现，不论是项目组还是职能部门中都会出现。任何人都可以发现问题，提出问题，要求组织关键会议。部门负责人和制作人必须找到问题的解决方案，并及时进行反馈。\n开发负责人收到制作人吐槽后，调查过后在某项目群中的反馈：\n产品负责人在产品群中关于沟通的要求：\n5. 有没有人偷懒？ 是时候提出这个灵魂拷问了：在没有空间监督的情况下，有没有人偷懒呢？\n我的回答是： “绝大多数人都是非常勤奋和努力的。”\n尽管这套流程的目的是高效在线办公。但会产生一个副作用：偷懒的人很容易被发现。正常的工作绩效都体现在了每日的总结和晨会中了，每个人的心中自有评价。和集中办公一样，如果大家都在努力工作，划水的人会自动浮现出来。\n此时我们更应该反思招聘流程的问题：为什么能让这样的人进入团队？\n每个人都有自己的追求。让追求一致的人聚在一起，做点有意思的事情，这就够了。\n道不同不相为谋。\n6. 在线办公的缺点 大家都知道在线办公的缺点，我随便举几个：\n开会的时候经常会有人因为网络和设备原因无法发言或发言不清晰，影响效率； 讨论的时候看不到对方表情，信息理解有疏漏； 某些必须依赖实物的事情无法完成（例如合同、发票等等——再次吐槽纸质发票制度——想到一次就骂一次：技术条件早已达到了，你们这群人能不能少薅一点？） 受限于网速，一些应用并不方便。 可是，我们没有选择对不对？\n我们只能选择在当前的环境下做到最好。\n在武汉，能健康地工作，已经很幸运了。\n祝我们健康！\n生命不是永恒的瞬间，而是充满玄机的历程，我们既要享受生命的蓬勃生机，又要接纳她的顿挫、煎熬和凋敝。\n你做三四月份的事，在八九月份自有答案。\n全文完 ","date":"2020-03-06","description":"","lastmod":"2020-03-06T09:40:13Z","slug":"online-office-in-sagiteam","tags":["sagiteam","life","management"],"title":"SAGITEAM 的在线办公经验","url":"https://blog.zengrong.net/post/online-office-in-sagiteam/"},{"categories":["impressions"],"content":"《俞军产品方法论》是 SAGI读书会第一期(2020年1月) 的选择书籍。读书会讨论本应在1月底进行，可疫情突如其来，所有的节奏都乱了。\n疫情期间的情况，我写过两篇文章，大家可以读读看：\n坚守武汉，我们应该做什么 云开工！疫情挡不住武汉人的工作热情！ 现在，我们已经云开工三周，一切逐渐走上正轨。是时候静下心来读书了。\n2020年1月选书 俞军产品方法论 作者: 俞军 出版社: 中信出版集团 出版日期: 2019-12-1 新写法的尝试 我尝试过很多读书笔记的写法：\n【读书笔记】海狸的记号 【读书笔记】聘谁 【读书笔记】我的创业-读《从0到1》有感 【读书笔记】掌控力——让所有人对你讲真话 2019 年我大概听了 400 多本书，因为听书类产品的影响，我想尝试一种新的写法，用类似得到 App 每天听本书栏目的解读方法来写这篇读书笔记。\n关于作者 俞军是百度的第一个产品经理，是百度搜索体验和百度贴吧的创建者，是和马化腾、张小龙齐名的传奇人物。但和两位已经封神的人物不同，俞军似乎更多一些烟火气，也许是他的文字更多流于世间的原因吧！张小龙“我是故意不来现场”和”我坚决不参加任何外部会议“在不提供语境的前提下会误导不少粉丝，俞军的 产品经理十二条 和这本十几年后的《俞军产品方法论》却能更丰满地解释他的完整思想。\n读书感受 作为一个沦为程序员的美术老师，我虽然不是产品经理，但从 0 到 1 打造产品（设计、美术、开发）并持续维护的事情，也做过不少。在这些产品的开发过程中，我会尽量在程序开发中贯彻产品思维。我现在所做的工作，相当于是 SAGITEAM 的“产品经理”。或许因为有这些经验，我在读书的过程中有不少共鸣。\n这本书并不是一本工具书，也不是俞军一人写成，而是滴滴出行产品团队从俞军在滴滴几年的内部讨论中整理出来的言论汇集。因此，整本书的内容比较散乱，读起来不够通畅，不适合产品新人阅读。有足够的产品经验或者复杂的阅历，在企业中遇到资源掣肘，碰到过团队矛盾，感受过纠结和选择的人，在本书中得到的体会将更加深刻。\n豆瓣中出现了很多低分评论，也是由于上面的原因。根据 邓宁-克鲁格效应（达克效应），没有类似经历的人会认为这本书是俞军在吹牛逼或故弄玄虚。\n俞军在书的自序中提到了：\n本书适合的读者为：在产品工作中遇到过需求冲突或者资源冲突，有过权衡取舍困难的人；或在创业和业务发展中思考过用户价值和商业价值的人；……以及对产品工作感兴趣的其他互联网从业者。\n这本书给我的启发挺多的。阅读的时候感觉像一个行业经验丰富的专家在耳边循循善诱，又像一位老友在讲述过往故事。书中得到的共鸣也不少，我都在读书笔记中做了记录。\n产品经理是一个人，TA 需要对产品（事）负责，在产品形成的过程中，TA 的执行力（术）决定了产品质量，TA 的方法论（道）决定了产品的成功概率。\n下面我就从 道、术、人、事 这四个方面来讲讲我对这本书的一部分理解。\n道（方法论） 产品经理的工作职责 产品经理是干什么的？不同时代的产品经理职责不同。消费品时代的产品经理注重品牌建设；软件时代的产品经理分为市场经理（研究目标用户，对用户负责）、产品经理（负责产品功能，对市场经理负责）、项目经理（项目进度管理，对交付负责）；互联网产品经理是本书主要讨论的对象。\n互联网时代与传统行业、软件行业相比，有几个明显的特征：\n信息复制分发的边际成本低 用户量巨大 迭代快速 依赖数据和 AB 测试 体验设计的价值增大 传统产品分发到用户手中的路径是比较复杂的。例如光盘时代的软件产品送达用户需要较长的时间，这就导致用户反馈不及时，产品设计即使不合理也无法快速进行迭代。因此传统行业需要经验丰富的人做产品经理，降低迭代成本。互联网产品的快速迭代特性降低了试错成本，也降低了对产品经理的经验需求。\n互联网产品的设计依赖数据和 AB 测试，产品体验对产品本身的价值也提升到了一个不能忽视的地步。这部分会在 术 的部分进行详述。\n产品经理的工作职能有四个部分：需求、生产、销售和协调。错误判断和决策权衡，是产品经理在需求阶段最有价值的能力。到了生产部分，产品经理要负责文档、原型、交互、策略优化和功能演进的设计。销售部分的技能则包含市场宣传、运营活动、用户增长和售后服务。协调部分则属于项目经理的职责。产品经理在任何时候都要搞清楚，当前的阶段下，侧重哪一个部分更能创造价值。\n产品工作属于强实践性的社会科学，其研究对象不可重复（人类意识多样，行为动机无法预测），需要大量的经验和数据做出决策，这些决策无法真正重复验证，最终结果有不确定性。由于这些原因，产品经理需要学习经济学和心理学，既要从用户模型的宏观角度思考群体的行为和结果，又要从微观场景下分析用户个体的心理和行为。经济学追求的是 约束条件下的利益最大化 ，产品经理要学会从经济学中的这四个概念分析产品：效用、成本、边际、供需定律。\n一个好产品需要有三个属性：对用户有效，对企业有益，可持续。从交易模型的角度来看，用户更关注的产品给 TA 带来的价值，产品本身对于用户并没有那么重要，产品的使用过程对于用户来说是一个交易过程，企业和用户交换的不是产品，而是价值。\n曾嵘：为什么 iPhone 手机套要设计成 Logo 镂空的形态呢？因为 iPhone 不但有应用价值还有标签价值。手机套的价值不应覆盖 iPhone Logo 的标签价值。\n曾嵘：公司的领导者就是公司的产品经理，负责”公司“这个产品的生产、销售和协调。”公司“这个产品的用户是公司员工。领导者需要基于公司员工的特点和公司生产的产品特点，构建出合理的公司文化，最终目标是公司利益的最大化，为公司的“产品（员工）”创造价值。从交易模型的角度看，公司员工在公司工作是一种”交易过程“，公司员工需要的是交易过程产生的价值，而不需要公司这个“产品”本身。因此，领导者应该通过合理的公司文化和符合公司现状的分配体系，保证公司员工在这个“交易过程”中得到不愿更换“产品”的收益，同时帮助公司创造足够的价值。\n企业、用户和产品的关系 企业以产品为媒介，与用户进行价值交换，达成创造商业价值的目的。价值是被产品经理“创造”出来的，产品是交互用户价值的媒介。\n用户不是自然人，而是需求的集合\n曾嵘：在游戏运营领域经常谈到“洗用户”的概念，我结合上面俞军的用户模型来聊一下这个概念。 某款小游戏，已经累计了 2000 万用户。该游戏所在平台的日活总量也是 2000 万。我们是不是可以说，这款游戏就到了生命末期了？ 如果从“需求的集合”的角度来看，当该游戏能满足用户玩这个游戏的心理需求，就可以让这个用户为游戏贡献一次活跃。这种心理需求，可以被游戏的新内容、新的用户活动来满足，老用户也可能在休息一段时间后，看到该游戏的名字或图标，想找回旧有感觉而重新体验。 从小游戏平台的角度来看，2000 万日活并不意味着只能有 20 个 200 万日活的游戏。而是 2000 万个玩家的 N 个需求。同一个玩家在一天内可能会尝试多款游戏。游戏开发商应该掌握平台的用户属性，开发出适合该平台用户的游戏，让自己的产品被 不同的需求 关注。\n产品经理要从四个维度去研究人：生物的人、社会的人、认知的人、情境的人。从这几个不同的属性来分析用户会更加全面：\n异质性：用户千差万别，需要建立用户画像进行区分 情境性：打车时，有钱没钱，晴天下雨，高峰平峰，选择完全不同 可塑性：用户善变，也容易被改变 有限理性：用户总是追求理性，但能力有限，经常出错 理解用户的行为 用户根据自己的偏好和认知，在情境刺激下，根据系统一做出感知和判断。产品通过使用体验来影响用户的行为，塑造用户的期望效应，最终形成用户的实际体验。这个过程会修正用户的主观偏好和认知。\n曾嵘：我用自己的个亲身体验来解释一下这个流程。我原本不喜欢放置类游戏（认知偏好），最近由于立项的原因开始大量尝试放置类游戏（情境），在体验一些优秀的放置类游戏的过程中（使用体验），感受到这些游戏的设计精妙之处，它们与其他类型游戏的融合以及它们对用户心理的控制方法（感知、解读、选择、推演、价值判断），从而感受到了游戏的快乐（期望效用），最终影响了我对该类游戏的感受（实际体验修正主观偏好和认知）。\n要促进用户的行为，可以选择最合适的用户（例如选择本来就热爱放置类游戏的用户），对这类用户来说，不需要特殊的情境设计就能达成效果。\n曾嵘：超休闲游戏和国内大部分小游戏就是使用这种思路来进行设计。这类游戏面向的是非游戏用户，因此在设计上没有任何玩家预设，创意上也会寻找和线下体验类似或者用户熟悉的玩法。用户拿到游戏后能无成本上手。\n理解用户价值 幸福公式：\n幸福 = 效用 ÷ 欲望\n根据诺贝尔经济学奖得主萨缪尔森的幸福公式，主观感受到的效用（欲望满足度）越大，幸福度越高；效用不变的情况下降低欲望，也能提升幸福感。宗教或者心灵鸡汤就是通过降低欲望的方式来提升幸福感的。欲望具有无限性和约束性，效用具有主观性，用户价值是用户的主观认知。用户感受到的价值才是用户价值。\n用户价值 = 新体验 - 旧体验 - 替换成本\n用户价值越大越好，因此提升用户价值，就是要让新体验最大化，旧体验最小化，降低替换成本。\n曾嵘：感觉 P45 的海底捞的例子是不合适的。首先海底捞的价格并不便宜，否则海底捞就不会专门制订针对大学生的折扣来降低平峰空台率；其次海底捞也在主动尝试各种方法来降低用户的机会成本，例如网上排号机制，过号不作废而是等三桌等等。海底捞的用户价值有很大一部分体现在其服务质量带来的心理溢价，针对于这部分溢价，用户是心甘情愿承受的，他们也有能力承受。无力承受的用户自然会去选择其他火锅店，并不会来海底捞排队。从用户属性的角度来看，这个就是用户的异质性。海底捞的用户画像是很清晰的。\n企业生存的本质问题 企业要生存下来，需要解决两个本质问题：\n发现市场的获利机会。 生产效率高于市场平均水平。 发现市场获利机会的途径主要有三种：洞察、试错和偶然性。其中洞察一般不会独立出现，它要求企业对新技术、新方法和新渠道有足够的了解，还要了解约束条件的变化。重点在于这些信息，你知道而别人不知道。因此洞察往往是和试错相关联的，试错越多，洞察出现的可能性就越大。试错是产品开发过程中必然出现的部分，信息是不完备的，产品经理无论经过多么审慎的思考，也无法穷尽所有变化的可能性，因此错误是必然出现的。至于机会的偶然性，只能交给运气来决定了。\n曾嵘：在后面的 产品经理自身需要注意什么 章节部分，提到了评价决策的标准，可以对照阅读。\n这里讨论一下”你知道而别人不知道的信息“。内部信息也属于这一类信息，如果有可能得到，应该尽量去得到，但不能只寄希望于内部信息。你知道而别人不知道 是一个相对的概念，要做长期的企业，只依靠内部信息是不行的，依然要磨练企业内力。\n阿罗信息悖论：在买方得到信息之前，并不了解信息对他的价值。一旦他了解了信息的价值，实际上已经无成本地获得了该信息。 因为发现信息后的实现是一个长链过程，影响因素众多，成功后无法有效证明成功的原因是来自于该发现，甚至无法证明该发现是成功者的发现。这也就是 “我的成功你无法复制；你成功了你说什么都对；我成功了就是运气好” 等说法出现的原因。\n生产效率高于市场平均水平的企业才能得以延续。企业使用 权威 的力量来提高效率。利用各岗位的专家表现出超出市场平均水准的更优决策来战胜市场。企业的核心竞争力是专业知识，专业知识中的 显性知识 可用语言文字符号传递，但 默会知识 只可意会不可言传。企业管理者通过设计内部惯例和制度，将一部分专有 默会知识 转换成 企业共同知识，从而提升提升组织效率。\n随着企业的成长，企业层级制度会带来信息损耗，抵消企业共同知识带来的效率提升。因此，企业边界只能扩大到因企业内部层级增加而增大的组织成本等于外部市场交易成本之时。\n在企业的运营过程中，每一个离职员工都会带走个人专有知识（默会知识），新入职员工会导致企业 共同知识 的损耗，因此要建立稳定的团队，慎重对待关键员工的离职。企业关键岗位掌握的专业知识和企业共同知识超过市场平均水平越多，企业的效率优势就越大。\n企业的组织效率也会影响生产效率。组织效率的四个条件互相影响：共同目标、共同理念、共同知识、运行机制。共同目标和共同理念就是企业的使命、愿景和价值观，在招聘的时候企业根据这些因素选择志同道合的人。运行机制关系到企业的激励和约束制度设计。共同知识在上面已经讨论过了。\n罗宾斯在《组织行为学》中提到，决定组织文化的是三点： 第一是创始人的人格特性；第二是高级管理者们的真实行为；第三是员工的社会化，通过宣传、制度和日常工作让员工融入企业文化。 在这三点中，创始人的性格特点对于组织文化的影响最大。企业的经营时间越长，组织文化和价值观就越取决于创始人的性格特征和自我进化能力。除了抓住大机遇的运气之外， 企业创始人最重要的能力就是自我进化能力。\n企业的生存、发展和产出 发展是创造用户价值的游戏，评估企业的发展状况，只需要看做成了什么，不用看错过了什么，做错了什么。 生存是不能有短板的游戏，任何一个短板都可能带来致命伤害。企业能否生存，不看做好了哪些方面和做对了多少次，只看哪个关键问题没有处理好。\n生存的关键问题有下面几个：\n环境选择：制度、文化、时代、地域、关键人才 替代成本：时间、品牌、入场门槛、规模效应和网络效应、政治/生产/财务/信息安全 效率：组织建设、激励与约束、技术应用、成本管理、变现能力、稀缺资源配置 在企业运营的过程中，除了产品之外，还会产生下面的价值：\n财务绩效：如果做投资，那么就只能带来财务绩效 认知：领域内的认知（know how 技术秘密），自身能力认知，技术积累 团队：磨合过的，有平台匹配度的团队 无形资产：产业链的了解和信任积累，提前获得竞争优势 何时是冒险的最佳时机？ 詹森生产函数：\nQ = Fr(L, K, M, C:T)\nQ Quantity 产量 r Rules 外部规则 T Technology 技术 L Labor 劳动力 K Capital 资本 C 内部规则：产权、组织形式、激励和分配 M Material 原材料 产量是外部规则的函数，外部规则约定了产量的边界。外部规则可能是法律、社会、政治、地理、版号（纳尼？）等条件。因此当外部规则变化时，就是企业冒险的时机。\n产量是技术的函数。新技术进入应用阶段时，会显著提升产量边界。这是企业冒险的时机。\n核心产品决定了企业的主要收入，因此在一段时间内，企业的制度建设都要围绕企业当前的核心产品去设计。\n价值的创造和交换 产品最核心的两个价值，使用价值和交换价值（价格）。交换价值有三个属性： 有效用、被认知、稀缺性。 任一个属性的变化都会影响交换价值（价格）。企业创造的产品承载使用价值，然后用它跟用户交换钱、时间、承诺等企业需要的交换价值。\n劳动可以创造价值，但没有对应需求的劳动没有价值。社会分工、新技术、新制度也能创造价值。从詹森生产函数的角度来看，其他因素给定的情况下，影响企业产出的最大因素就是企业内部制度。\n对于消费市场上的用户来说，只有被应用和普及的技术才能创造大量价值。技术本身（无论多高端）并不创造价值，技术必须被应用于产品，但产品本身也不创造价值——只有产品被交易（购买和使用）后才实现了价值创造，只有交易量大才能创造大量新价值。\n曾嵘：上面这段话很想说给各位程序员听一下。我也作为程序员在第一次创业的时候，一度认为自己的技术能力很强，做出来的东西一定很值钱。这个想法被现实证明是错误的。程序员在当今的时代，更像一个匠人的角色。我们把自己的技能磨练得炉火纯青，是为了做出优秀的作品创造价值。我们的手艺本身并不值钱，只有做出被市场认可的产品才值钱。我碰到过很多程序员，对于技术非常执着，对于商业规律一窍不通，对学习商业运营之道不屑一顾。这样的人适合去稳定的大企业和成熟的产品中打拼，但不适合创业，更不适合带领创业团队。\n人类享受的绝大部分美好事物，是由赚钱的企业创造的。产品经理的工作是为用户创造价值，用户价值和商业价值是一个硬币的两面。如果企业不能有长期的商业价值，无法持续经营，就无法给用户创造长期的用户价值。\n从 价值交换 的角度来看，应该 将产品看做交易，产品是为了在企业和用户之间交换价值而存在，所以产品就是交易本身。 我们也不能将交易狭义的理解为货币交换，任何有预期代价的主动行为都可以作为交易的收益。\n在交易的过程中，用户付出了直接成本（购买）和交易成本（选择、学习、保障），企业付出了生产成本（制作、薪酬）和交易成本（付费宣传、优惠、相关习惯养成）。对于两者来说，仅当 效用 - 成本 \u0026gt; 0 的情况下，交易才可能持续发生。一个交易能同时满足企业和用户两方，且两方都认为得到了收益，由此我们可以判断：不存在等价交换。\n曾嵘：需要注意的是，企业付出的成本，不一定给自己带来全部的收益。例如易到用车培养了一部分打车用户的习惯，但收到用户红利的是滴滴出行。支付宝培养了用户使用手机付费的习惯，但微信通过春节红包打开市场后，也收到了部分支付红利。\n交易和交易模型 交易是一种非等价的交换行为。用户选择主动交易，是因为 TA 认为得到的收益高于代价和机会成本（效用 - 成本 \u0026gt; 0）。用户付出代价可以是多方面的，比如时间、货币、身体、心理等等。\n曾嵘：交易创造价值。交易让不同的人得到需要的“效用”，从而创造更多价值。很多策划说要做一个“自己喜欢的游戏”，这个游戏当然有价值，但它的价值仅限于满足自己心理需求（和创作者自身交易）。不要说“独立游戏”不是商业游戏了。所有的游戏都必须创造商业价值才能持续存在。上面我也谈到过技术的价值。如果技术人员只会 炫技 而不追求商业价值，不考虑“约束条件下的最优解”，就会导致产品成本居高不下，最终无法持续。\n交易模型是产品经理发现和设计的合理机制，该机制促成用户主动交易且可持续。交易模型也是商业模式，是利益创造和利益分配模式。产品经理的工作是不断发现市场上高于用户支付代价的用户价值，设计合适的交易模型将其固化为产品，并持续降低产品生产成本和交易成本。\n曾嵘：前面提到过我对海底捞的用户体验看法，下面讲讲西贝。在西贝就餐时，我碰到过多次店员主动赠送打折活动，也碰到过不好吃退款的情况。西贝的产品经理发现了这类在意服务质量用户的价值并对其进行了满足。滴滴顺风车为了体现其社交属性，有段时间的广告带有性暗示色彩，这就属于没有找准用户价值，产品被“带歪了”。\n可以从损失厌恶、选择增加福利、激励相容这几个切入点来理解交易模型。诺贝尔奖获得者赫维茨对激励相容的解释是：在市场经济中，每个参与者都会按照实现个人利益的最大化的策略行动，如果有一种制度安排，使行为人追求个人利益的行为正好与企业实现整体价值最大化的目标相吻合，这一制度安排就是 激励相容 。\n产品的效用和成本 效用是对欲望的满足程度。由于人类欲望的无限性，个人的决策总是为了获得最大期望的效用值，而非最大期望的金额值（有限理性）。效用是多样的，货币、时间、心理生理、情绪认知、信念等等都是效用。效用往往又是互相冲突的，例如社会取向和自尊取向的冲突：一个人正确认识世界的过程中，发现自己正在做的事情是错误的。这种冲突下，只有少数人能进行自我否定和自我改变，多数人的习惯做法是在心理上寻找 自我安慰 甚至扭曲认知，营造正确的感觉，将错误 归因于别人的偏见和无知，或者归因于他人和环境。\n产品效用 对用户欲望的满足程度可以分为四类： 不能低于底线需求、够用就好的基本需求、多多益善的需求、超过预期或参照系的惊喜。 产品经理不能把产品当作一个整体来看待，而应该将其作为 一组约束条件下的各种效用的组合， 分析各种产品属性的迭代对一个（或一组）效用可能的影响。\n边际成本 的存在，意味着企业无法无限度地扩大生产规模。游戏行业做产品推广的时候有个“洗量”的说法，在产品推广初期，获得用户的价格较低，当游戏总用户量达到较大数量级（例如千万或者亿级别），获取用户的边际成本就会逐渐增高。不同的经营方式有着不同的边际利润。网约车的边际成本和边际利润都相对固定，100 万司机和 10 万司机，网约车公司都需要支付高昂的边际成本，订单数量与司机数量的比例是相对固定的。饭店行业的边际成本高，但每增加一个订单的边际成本低（食材成本和部分人工），因此饭店增加订单的 边际利润 就高。\n机会成本 是企业因为从事某项经营活动而放弃另一项经营活动的机会，或者利用某一项资源获得收入而放弃另一种收入，放弃的收益就是机会成本。\n交易成本 是指在完成一笔交易时，交易双方在交换前后产生的各种相关成本。诺贝尔经济学奖得主阿罗定义：交易费用是经济系统的运行费用。 巴泽尔认为 交易费用为与权力的转让、获取和保护相关的费用。 张五常认为交易费用是制度费用。交易成本的本质就是用户付出但企业没有收到的价值，以及企业付出但用户没有收到的价值。零交易成本在真实的世界中是不存在的。\n交易成本来源于 有限理性、投机主义、不确定性与复杂性、少数交易、信息不对称和交易气氛。 从产品的角度来看，交易成本来源与 信息不对称、信息不确定、信息不完全和信息有成本。 交易成本主要包括 搜寻成本和度量成本、询价成本和决策成本、实施成本和保障成本。\n相对价格 = （直接成本 + 交易成本）÷ 效用组合\n当其他条件不变的时候，相对价格降低，需求量上升。越发达的社会，交易成本就越低。但发达社会中交易成本占交易总额的比例越高（理解：物流成本占鲜果产品的比例）。因为发达社会的新增交易在落后的社会中无法达成（理解：一骑红尘妃子笑）。在直接成本无法降低的情况下，可以通过追加效用组合，改变效用组合，或者降低各类交易成本的方法，来降低产品的相对价格，让需求量上升。\n理性决策 人类的决策天生是不理性的，主要体现在：人脑的计算力有限；部分记忆存在偏差；认知存在缺陷，本能算法和经验算法（系统一和系统二）会导致各种偏误。要实现理性决策，需要考虑三要素：理性的信念 \u0026gt; 理性的目标 \u0026gt; 理性的行动。 理性的信念就是对自我的认知、怀疑和反思。理性的目标是 约束条件下的价值最大化。 理性的行动是在给定目标下寻找最优的解决方案。没有经过训练的产品经理会着重在 理性的行动 上，但前两项更重要。错误目标下的最优解，只会导致行动越快，错得越多。\n产品经理要追求决策价值最大化，就是要在决策过程中追求新体验（叠加效应）最大化，旧体验（参照系、完美新用户、垄断、先发优势）最小化，替换成本（认知成本、获取成本、使用成本）最小化。产品经理也要充分使用元认知能力，使用权衡的视角判断用户价值。\n决策方法 数据决策是有效的，但其中的误区是需要避免的。 AB 测试并不适合所有决策。卢卡斯批判：利用加总的历史数据来给出政策建议和经济预测，其结论值得怀疑。 经济学家的决策策略一般为：从一些公认的微观主体假设出发，建立模型并进行推导，反过来将实际数据代入其中。\n逻辑决策的成本远高于数据决策。因为在阐述决策时，需要保证所有人的知识结构和认知偏好是一致的。鸡同鸭讲的痛苦大家都明白的。\n主观判断决策是最难的决策。没有数据或者逻辑的支撑，用关键变量（关键约束条件），用户模型和交易模型来模拟市场做出判断。此类决策还需要强大的个人影响力来支持落地。\n决策偏误 常见的决策认知偏误非常多：归因偏误、锚定效应、选择性注意、刻板印象、自我中心偏误、信念偏误、决策类偏误。\n组织不做决策，只有真实的人才能做决策。每个人均有不同的偏好、目标、目的和想法。“社会”、“国家”、“企业”、“团体”、“群体”等等不应该被理解为犹如个体行为人那样的集体。世界上不存在一个叫用户的人，也不存在一个叫用户的群体，产品经理研究的用户，是从无数情绪环境下去除了差异化的抽象“用户模型”。针对这样的用户模型做决策，才能最大程度避免决策偏误。\n术（执行方法） AB测试的弊端 AB 测试对大规模快速迭代的互联网产品带来的设计和决策意义都是巨大的，但 AB 测试也有不少弊端。\nAB 测试是作弊，它跳过了“推理”这个阶段直接得到最优结果。Facebook 前员工们会在内部鄙视那些只会做两件事（能做 AB 测试的事和小扎关心的事）的产品经理。并非所有的内容都可以做 AB 测试。如果一个项目已经找到方向开始驶入快车道，以数据驱动是可以的。如果市场不稳定，需要找到新方向，一味强调数据驱动就不合适。有些事情用数据驱动成本会很高，有时候数据只能作为部分的参考，硬件产品（成本过高）和线下产品这类产品也无法做 AB 测试。而且，AB 测试也促成了部分不求上进的产品经理思维上的懒惰。\n研究供需定律不要忽略条件变化 供需定律中提到 价格下降，需求上升 是市场规律，但我们不能忽略“其他条件不变”这个前提条件。例如一个螺蛳粉小店的免费试吃活动能否吸引所有用户，还取决于活动信息的获得性、店面交通便利性、用户是否喜欢螺蛳粉等等因素。\n产品经理是做权衡的，不是做“设计”或“创意”的 任何产品，如果把”设计“狭义定义成好创意，好审美，就只能满足一小部分用户诉求。互联网产品做大之后都是平台，要引入经济学思维，习惯权衡取舍，追求整个系统的效率。有些现象级产品宣称遵从狭义的设计理念，一般来说是因为产品遇到了巨大的新要素红利。本身创造了巨大的用户价值，其他因素的影响权重都会降低，随便怎么做都能锦上添花。这种情况是无法证伪的，因为历史不能重演。\n曾嵘：知乎之前是小众社区，追求用户质量的时候使用邀请制。后期的开放增加用户数量，让知乎变成了“逼乎”，一部分优秀老用户流失，但也带来了大量新用户。B 站原本是个二次元小众社区，但 2020 年春节晚会成功”出圈“，是因为它从二次元社区用户中摸索到了通用于 90 后年轻用户的用户价值。\n完美新用户 假设产品 A 与 B 竞争，某月 A 增长了 200 万新用户，其中 100 万是用过 B 产品的，另外 100 万是从未用过 A 与 B 的同类产品的。那么从未用过同类产品的用户就是完美新用户。完美新用户感受到的新体验和旧体验差值巨大，对产品和公司品牌有光环效应，能高概率成为忠诚用户，是公司品牌价值和长期市场竞争力的重要来源。用户第一个接触到的产品往往就是其心中的标准，大众对于产品的理解是基于市场体验构建的，不遵从工程师标准或者媒体专家的标准。\n曾嵘：我的同事在使用 Google Firebase 的时候，会大吐其槽其难用的点。Google 的产品大多是工程师主导，展现方式和用户体验与大多数产品不同。但更重要的原因，是大家已经先入为主熟悉了其他的产品体验，认为之前的体验才是标准体验。从完美新用户的角度，先发优势是极大的产品优势，但有瑕疵的产品由于新体验提升的难度小，替换成本低，也极容易成为后来者的超越对象。\n权衡视角 产品经理对用户价值进行判断时，有几个常用的视角可以提升决策质量：\n对自我认知的认知（元认知） 对给定目标的批判性思考 参照系 成本 世界的不确定性 概率风险 非货币价值 外部性和宽阔视野 考虑时间的跨期决策 主观决策的思考习惯 有一个想法后将其记下来，用过往所有的经验去证伪，如果无法证伪，就假设它成立，存储起来。以后有新信息或新思考的时候，随时提取出来匹配，或有所得，产生新的结论。\n三维归因 要将三维归因方法锻炼成为系统一的一部分：行为主体（人或者组织）、关联方（关联方也要进行三维归因）、行为情境对结果的影响。\n情境特殊性：行为人在不同情境中是否表现不同。 他人一致性：其他行为人在相同环境下是否表现相同。 个体一贯性：行为人在不同时间是否表现相同。 常见的归因偏误如下：\n自利偏误：成功归因于自己，失败归因于环境。 正面效应与负面效应：好事归因于内在本质，坏事归因于环境因素。(朋友圈的照片自己拍的好是因为技术高，别人拍的好是因为手机好) 群体归因偏误：个人特质当作其所属群体的特质（武汉人都爱吃蝙蝠） 终极归因偏误：把问题归因于整个群体，而非群体中的个体。 基本归因偏误：解释他人行为的时候，过度归因于内在特质，而忽略了外在环境因素。 决策落地的技巧 常见的推动方法为：\n合法性（下属和同事）：依靠职权 理性说服（老板、同事和下属）：符合逻辑的观点和事实依据 鼓舞式诉求（下属）：目标的价值观、需求、希望造成的情感认同 商议（同事和下属）：他人参与决定你的计划以增强他人对你的支持 交换（同事和下属）：提供利益或者好处以换取支持 个人式诉求（同事）：友谊和忠诚度 逢迎（同事或下属）：吹捧、赞扬或友善以换取支持 施压（下属）：警告、威胁、反复要求 联盟（同事）：寻求他人帮助或支持 一个人对另一个人的影响力，主要来自于一个人对另一个人创造价值或者造成伤害的能力。要提高自己的推动能力关键是提供自己为他人创造价值的能力，充分分析诉求，寻找有较多共同利益的方向和视角，让对方理解推动这件事情能给 TA 带来的价值，或者无法推动这件事情给 TA 带来的伤害。\n议程设置也会影响决策落地。议程设置是一个由三部分组成的线性过程：首先必须设定将要被讨论的问题的轻重缓急（媒体议程）；其次，媒体议程影响公众观念（公众议程）；最后，公众议程影响政策制定者所重视的事物（政策议程）。这就是舆论导向的重要性。\n人（选人、用人、育人、留人） 对于公司，需要处理好两点： 1. 怎么选拔一个有潜力的产品经理；2. 怎么培养一个优秀的产品经理。\n产品经理的技能无法标准化，怎么选拔一个有潜力的产品经理？ 空降高级产品经理的成功率较低。因为 TA 需要付出大量的熟悉成本、团队磨合成本与团队达成共识。俞军把产品经理分成 ABC 三类：\nA类：要有深度思考能力和超常的同理心。大约占 10%。 B类：逻辑清晰和产品心。大部分合格的产品经理属于这一类。 C类：不适合从事产品经理：期望生活休闲，不爱加班，自我认知过高，缺乏好奇心，思考过于表面化，点子多爱思考但缺乏实践精神。 在选拔产品经理的时候，需要考察的是能力的绝对值，优秀的产品经理，其洞察力需要高于平均水平。不能单一地看重候选人经历的企业、职级和毕业学校，以及负责过的项目，这些都是能力绝对值的分母。分母越大，分子就应该越大。在同等成就的条件下，年龄小、经验少、学校差可能是优势，因为分子足够大。一个合格的产品经理，应该会对每个求职岗位针对性地设计个人简历，这至少能反映出其同理心、勤奋程度和对被招聘企业的兴趣。\n面试产品经理的时候，可以深入地问他参与过的某件事，主要关注他在这件事中的思考和认知变化，关注他的每一次选择和决策方式。除了过往事件，也可以问一件面试官熟悉但候选人不熟悉的事，考察候选人面对陌生问题的切入角度和方法。优秀的产品经理需要有良好的思维框架，对社会和用户有良好的认知，会发散，会权衡，会收敛。选拔 B 类的产品经理上面的问题已经足够。如果要选择 A 类的产品经理，因为其离职替换成本太高，还需要考虑性格、志趣、三观以及入职两年后的成长性和机会成本。职位或者级别越高，发现和承认自己不足的能力 就越重要。\n对于应届生招聘，由于其没有什么工作经验，问题设计上就应该以着重考察超过平均水平的思维洞察力为主。问一下他做过的几件事，参与过的活动、学习情况和个人爱好即可。\n在选择人才的时候，要考虑好“当前好”还是“未来好”的问题。如果仅针对“当前好”来招人，会缩小筛选范围。“未来好”的人培养时间较长，忠诚度和磨合度也会比较高。上面已经谈到过，不要只看重“最好的人”，而要看重“最合适的人”。A 类产品经理如果与当前企业方向和产品类型不匹配，就算招到了也无法稳定下来。\n培养一个优秀的产品经理，需要下面三步： 找到一个有潜力的人。(40%) 将其放到一个有成长性的产品岗位上。(40%) 培养其对约束条件的权衡取舍和识人用人的能力。(20%) 产品经理是必须需要其他人协作才能完成目标的岗位。对于产品经理，需要充分授权，用目标做管理，让产品经理积累错事经验。俞军提出了从三个不同的视角阐述了产品经理的成长的关键标准。\n第一个视角是用户模型和交易模型。优秀的产品经理要理解用户，需要从 0 到 1 地建立用户模型。很多其他岗位转岗产品经理不成功的原因就是没有精力和时间重走用户模型构建道路。要赚足够的利润，需要服务最多的用户，才能创造出最多的价值。\n第二个视角包含科学方法、人文关怀和实践精神。优秀的产品经理既要掌握科学的方法，了解逻辑、概率、控制实验、理性决策和经济学；也要考虑用户样板量，拥有同理心、学习心理学、进化论和博弈论；还要从尊重事实和数据的角度观察细节，亲身体验，不断观察、实践、权衡、反馈、迭代和自省。产品经理要了解不同用户的需求冲突甚至同一个用户自身的需求冲突，要知道用户、企业和产业链上下游之间的利益冲突，要尊重社会的复杂性，愿意多维度权衡决策，要有自我怀疑精神和期待被现实的反馈打脸的无畏精神。产品是一个不断迭代的过程，产品经理需要不断自我否定。\n第三个视角是批判性思维。哈佛大学前校长博克总结了本科生的思维模式的三个阶段：\n无知的确定性阶段（大学新生） 有知的混乱性阶段（大部分学生） 批判性思维阶段（少数学生） 在书的 222 页，俞军列出了美国教育资助委员会评估批判性思维的技能，相当详细和中肯，可以去读一下原书。\n俞军给产品经理的成长制订了两个五年计划 前五年，产品经理需要从大量产品的迭代中验证自己的思考。如果进入大公司，不要去螺丝钉的岗位和部门，要充分积累实战经验。如果去中小公司，要挑选一个有产品心的小团队加入，公司和大老板都不重要，和你一起工作的伙伴的能力最重要。这五年的产品经理需要六个方面的能力：\n从 0 到 1 最深做透 做宽做杂。可以做增长、测试、客服、销售、功能、项目管理，来者不拒。 大用户量。微小的升级在小用户量上的产品得到的反馈少，数据噪音大。无法得出准确的判断。 商业闭环。大部分美好的事物都是由赚钱的企业创造的。 团队建设。产品经理的产出靠的是关键决策的质量。 对于有经验的产品经理，要带前五年的新人，需要进入到项目中，和他们一起讨论同一个产品问题。如果要培养新人的管理能力，就要和他们一起判断所有的候选人。\n后五年，产品经理进入到发展停滞期，需要根据自己的特点选择不同的路线：\n专业路线。学习权衡决策和交易模型。 管理路线：计划、组织、领导、协调、识人用人、优先级区分。 业务路线：优先进入大平台稳扎稳打，选择 toB 的业务，沉淀与平台匹配的经验。 创新创业：天赋优秀的产品经理适合去稳定的产品中创新；资质普通的产品经理适合去创业公司或者早期业务，一旦成功可以获得超额的成长和回报。 公司要为产品经理的选拔和成长提供哪些环境呢？ 公司要吸引优秀的产品经理，应该提供吸引人的美好愿景，充分的发展空间和良好的薪资待遇。\n企业能提供的产品机会和产品文化对产品经理的长期成长至关重要。俞军讲了一个例子：有一个企业家谈到用户体验每次都很重视，甚至将其写到企业文化中。提交的方案中他会期望看到提升用户体验的内容，但他又谈到升级产品不能降低企业收入。如果不能为用户体验降低任何收入，则说明用户体验在这个企业是没有价值的，即使将其写在企业文化中也没有用。\n有下面特点的企业更适合让产品经理和企业一同成长：\n有经验丰富的导师 着眼于中长期的利益导向 产品快速迭代 构架简单扁平 高强度的竞争压力 大量的用户 较为粗放的分工 产品经理自身需要注意什么？ 在实践中的权衡取舍、持续迭代，追求价值最大化，是产品经理的关键能力。掌握每一个约束条件的属性和弹性，是产品经理需要长期修炼的能力。优秀的产品经理，要能够通过理性的决策来得出正确的个人选择。在信息不全未来不可知的情况下，这个决策的正确性很难把握。俞军给出了一个 正确决策的评价标准 ：在多年以后，人们未能发现更优的选择，且当时的逻辑判断已经包含了做这个决策所需要的所有关键变量。\n上面俞军给产品经理制定的前五年计划中谈到了六点，其中第一点从 0 到 1 和第二点最深做透是所有产品经理都很容易理解的，也会努力去做的。但做宽做杂、大用户量和商业闭环则不是所有产品经理都能想到的。天赋高的产品经理，就要多换业务，不仅要把一个业务做深做透，还应该多了解其他岗位和其他产品的。\n曾嵘：对我们游戏行业来说，一个游戏制作人（产品主策划居多），必须能胜任一个大体量的产品（50万 以上 DAU）从产品设计到运营活动的全链路工作，了解团队中美术和开发人员的短板和长板，控制好流量与内容的节奏，才能把产品带向成功。不能完成前述所有目标的，只能算策划，不能算制作人。从俞军给出的产品经理十年规划来看，只有达到后五年并有一定团队建设能力的人，才能胜任游戏制作人这个角色。\n和 做宽做杂 相悖的，产品经理若切换产品方向会导致自我价值下降。俞军提供了一个公式：\n产品经理的价值 = 经验等级 x 平台匹配度 x 智慧等级\n越高阶的产品经理，越要看重自己的智慧等级。智慧等级的高低决定了产品经理的移情能力，也决定了自己在全新品类上成功的概率。因此产品经理必须对自己的特点有非常明确的了解和科学的认知。这些认知决定了你去什么样的公司和从事什么样的岗位。\n产品经理如何选公司和岗位？ 阿尔钦在《不确定性、演化和经济理论》中写道：世界的不确定性越大，利润由那些风险型、幸运型的个人获得的可能性就越大，往往成功者不是那些逻辑型的，细心的，务实的个人。\n越优秀的产品经理，由于试错成本高，就应该倾向于去有稳定业务和成熟产品的大企业，在现有产品中实现高增长。普通资质的产品经理则可以较多考虑创业公司和初期产品，因其可能有潜在的巨大收益。早期业务一旦试错成功，它所带来的的高速增长也会给其中的人带来超额历练和超额成长。\n高级产品经理也要明白自己是属于 开创型、增长型还是协作型 的人才。开创型的产品经理适合初创团队或者转型期。增长型的产品经理是产品成型后的中坚力量。协作型的产品经理则更偏重于在稳定的产品中发挥协作能力。当然也有产品经理同时具备两个或者三个以上的特性，此时就需要产品经理通过对自身禀赋和目标的分析，来决定进行什么样的企业。\n高级产品经理在选择适合自己的岗位时，要选择人才质量长期处于正向上升的企业或者部门。根据自身的不同时间目标分类，短期规划（一到两年）选增长，中期规划（三到五年）选行业，长期规划（十年以上）选老板。 为什么要选老板呢？因为长期看来，只要企业继续成长，机遇和危机都是少不了的。此时老板的能力、性格和价值观就是主要决定因素。\n一些重要的句子（原文略有修改） 想赚很多钱的企业家或产品经理，也最应该致力于服务最多的用户。为每个用户创造最多的他愿意为之付费的用户价值。这就是交易模型的意义。\n产品经理的产出不是靠人多，而是靠决策质量。无论团队大小，一个领导者和几个骨干的产出就决定了团队的产出能力。能招到几个高潜力的人才，主要问题就解决了。\n管理上，不要总想着“提升自己”。提升自己固然重要，招到高潜力的人才更重要，把高潜力、高能力的人才聚集在一起并使其高效协同才是管理的成功.\n事（案例） 关于异见 每个公司的文化中都包含了平等概念，如果一个领导提出了一个观点，大家只是提出一堆补充性意见，看上去很民主，很平等，是不够的。否定性意见才是真正的异见。足够多的异见才能提升决策的质量。\n互联网产品经理会回归传统行业是否有优势？ 互联网产品经理回归传统行业，借互联网经验打败竞争对手，这种观点是不正确的。大部分充分竞争的传统行业，要么利润很薄，要么领先的幸存者已经是一个优秀的“产品经理”，他们长期处于被互联网改造的影响中，互联网产品经理掌握的新工具和方法很少有机会抵消领域知识积累的差距。在传统企业中，推动旧流程使用新方法也是一件极难的事。\n下属做什么都不如自己？ 俞军对管理者成长初期 “下属干什么都不如自己，还是撸起袖子自己干” 的思维方式，从经济学“比较优势”的概念上做了解答。下属干活的产出或许只能达到 Leader 的 70%，但他节省了 Leader 的时间，Leader 可以使用节省下来的时间干更有价值的事。两份工作的产出是大于一份的。\n产品经理是否需要懂技术 对于 “产品经理是否需要懂技术” 这个经常被讨论的问题，一直并没有确定性答案。俞军给出的方案是权衡机会成本和约束条件之间的收益。若增加“懂技术、懂数据、懂战略、懂营销……”这些约束条件，就必须缩小候选人的选择范围，放弃没有这些条件的优秀产品经理。 在不同的业务阶段，不同的团队文化，不同的岗位和不同的大环境下，产品经理的 一般能力 就是懂用户，会权衡。其他的条件是否必须，要具体分析。\n用机会成本的思路来分析网约车司机的心理成本 职业司机对收入的诉求比兼职司机更高。 顺风车司机在顺路的情况下（机会成本为零）接收的交易价格会非常低。 降低交易成本的例子 标准化产品带来的确定性能大幅降低交易成本。 智能手机的普及减少了排队带来的交易成本。 链家的真房源价格比假房源要贵，但降低了消费者一方的 搜寻成本、度量成本和询价成本 。 交易全链路线上化降低了 搜寻成本、询价成本和实施成本。 网约车的抽成制，降低了工资制和买断制的 实施成本和保障成本。 快车动态调价模式与排队模式的权衡 最初的动态调价模式会导致极端场景下超过感性人接受范围的倍数，造成了一些社会问题。将调价限制在 2 倍以内又会导致大量人竞争但无法出行。用户的真实诉求分三层：打到车的预期，预期的准确性，预期是否能更快。根据特定环境下对公平和效率的权衡，滴滴出行选择了排队方案作为主要解决方案。为了处理紧急需求，又增加了会员优先特权（每月几次并清零）和积分兑换特权。\n网约车醉酒乘客打车的问题权衡 醉酒乘车有安全风险和服务难点。女性醉酒乘客的安全问题在于乘客本身，男性醉酒成功的安全问题多在于司机。严重醉酒乘客无法叫醒，醉酒乘客呕吐都会影响司机工作。\n有两个著名的三角理论：\n犯罪铁三角理论：潜在犯罪者 + 合适受害人 + 合适犯罪场景 = 有概率发生犯罪 权利三角（巴泽尔）：任何个人的任何一项权利的有效性都来自于三点：这个人为保护该项权利所做的努力，他人企图分享这项权利的努力；任何“第三方”所做的保护这项权利的努力。 保护用户权利（司机的和乘客的）的一部分工作是教育用户保护自己的权利。\n网约车拼车价值分配问题的权衡 滴滴拼车经历了两个阶段：第一种供大于求，缺乘客，价值分配向乘客倾斜。第二种供小于求，缺司机，价值分配向司机倾斜。在向司机倾斜的过程中，要考虑司机对收入的心理认同，考虑快车价格实时计价和共乘收益的混合计算。\n网约车乘客物品遗失在车上问题的权衡 汉德公式：\nB \u0026lt; PL：当事人避免意外所付的成本 B 低于意外发生的概率 P 乘以以外发生以后的预计损失 L 时，当事人应该承担责任。\n建立物品遗失专项小组后，乘客的遗失物品找回率从 38% 提升到 68%。半年找回率较高，更长时间则难以提升。更复杂的情况，可以用运营手段（人工客服）来解决，可能比线上产品的成本更低。\n网约车乘客中途修改目的地问题的权衡 从司机的角度看，司机来接顾客有成本，顾客临时将目的地改短会影响司机收益。若司机知道顾客的真实目的地，则不会接单，同样的机会成本可以换来另一笔订单的收益。从乘客的角度看，需求发生变更是很常见的事情，结束订单重新叫车的体验是很糟糕的。如果对标出租车的处理方案，将责任交给司机一方，会造成司机的流失。所以具体处理方案要根据各个地方司机资源的差异进行区别对待。\n网约车司乘纠纷判责价值观的权衡 由于复杂的路况导致司乘双方经过努力都找不到对方，很难判责。平台通过新增“判责不清”这种情况，承担一定的补偿责任，优化了该问题的解决体验。\n订单结束后乘客没有即时付款导致司机的焦虑感增强，平台引入了优先垫付的功能提升司机端体验。对于风控刷单的账号平台不进行垫付，而一旦账号通过了风控系统的检查，即使最终账号依然没有付款，平台也必须承受坏账风险（这是风控系统的责任）。这种体验增加了成本，但从长期看减少了司机的流失，提升了效率和经济收益（由于司机流失的新司机招募成本）。\n全文完 ","date":"2020-02-22","description":"","lastmod":"2020-02-27T09:51:26Z","slug":"reading-notes-yujun","tags":["reading","readingnote","sagiteam","sagibookclub"],"title":"读书笔记：《俞军产品方法论》","url":"https://blog.zengrong.net/post/reading-notes-yujun/"},{"categories":["technology"],"content":"关于 uWSGI 的 log，我已经写过好几篇文章了。本篇是一个总结，希望把我这几年使用 uWSGI 中积累的关于 log 选项的经验做一个汇总介绍。\n有兴趣可以先读一读这几篇文章，更多细节在这里：\nFlask+uWSGI 的 Logging 支持 Flask+uWSGI logging rotate：重要补充 uWSGI+rsyslog 实现 rotating logging pyzog：logging rotate 的终极方案 1. 混乱的 uWSGI 文档 uWSGI 的功能强大，参数众多。要了解它的全部功能，看文档是最有效的办法。但 uWSGI 的文档一直广被诟病。文档中描述不清或者过时的地方很多。\n例如，文档中提到了支持 ZeroMQ logger，但官方的 PyPI 版本中却并没有提供这个功能。\n要弄清楚要使用的功能，在文档记录错误的时候，可以去查看源码或者翻找 StackOverflow。\nuWSGI 的 Logging 文档\n2. uWSGI 中与 log 相关的选项 2.1 前置选项 以下几个选项是必须要配置的：\n1[uwsgi] 2; 启用 master 进程，管理 worker，提供大量高级功能 3master = true 4; log 在 master 中处理 5log-master = true 6; 使用单独的线程处理插件化 logger 7threaded-logger = true 2.2 daemonize daemonize 保证进程自守护。在这个选项中通常会配置一个文件地址，日志会写入这个地址。\n如果不配置 daemonize，uWSGI 会在前台运行，日志输入到 STDOUT。这种情况下，建议用 Supervisor 来管理 uWSGI 进程。 因为 Sueprvisor 要求被管理的程序 必须运行在非守护模式。\n当使用了 Supervisor 来管理进程后，uWSGI 输入到 STDOUT 的日志会被 Supervisor 的日志系统接管。\n2.3 logto 如果使用 Supervisor 来管理 uWSGI，又不希望 Supervisor 来接管日志，logto 就能排上用场了。\n配置 logto 写入一个文件，此时日志就输入到 logto 配置的文件中了。\nlogto 是支持 logreopen 参数的。这一点我在 Flask+uWSGI logging rotate：重要补充 一文中有详细介绍。\n2.4 logger 和 req-logger logger 是插件化的日志处理器。和 logto 是完全不同的两套机制。\n如果希望输出到其他目标，例如 redis、ZeroMQ、MySQL。就可以使用插件化的 logger 配置，此时 logto 和 daemonize 都不会输出。\nreq-logger 也是插件化的日志处理器。所不同的是它仅仅处理请求类的日志。如果不配置 req-logger，请求类的日志会和其他日志一起写入同一个目标。为了方便处理，我建议必须分离请求类的之日。\nreq-logger 的具体的使用方法，在 pyzog：logging rotate 的终极方案 中就有介绍。\n使用了 logger 配置之后，logto 和 daemonize 都会不会有日志输出。\n3. 相关阅读 部署Flask + uWSGI + Nginx uWSGI+rsyslog 实现 rotating logging Flask+uWSGI 的 Logging 支持 pyzog：logging rotate 的终极方案 Flask+uWSGI logging rotate：重要补充 全文完 ","date":"2020-02-13","description":"","lastmod":"2020-02-13T00:55:45Z","slug":"uwsgi-log","tags":["uwsgi","flask","python","server","logging"],"title":"uWSGI 的 log 参数详解","url":"https://blog.zengrong.net/post/uwsgi-log/"},{"categories":["technology"],"content":"在上一篇文章 Flask+uWSGI logging rotate：重要补充 中，我仔细分析了 uWSGI 中处理 logging rotate 的各种限制和解决方案，在文章最后，我提到：\n因此，我们需要一个终极解决方案来解决这个问题。这需要新开一篇文章来说明。\n现在，终极解决方案 来了。\n1. 测试支持的 logger 插件 上篇文章我们讨论过，uWGI 的 req-logger 和 logger 两个参数是插件化的 logger，它们不支持 logreopen 功能。但也正是因为它们的插件化功能，让它们可以支持多种 logger 数据目标。\n在 官方文档 中，介绍了多种 logger 支持插件。例如 socket/rsyslog/mongodblog/zeromq 等等。\n可以使用 uwsgi --logger-list 来查看当前的 uWSGI 版本到底支持哪些 logger 插件：\n1uwsgi --logger-list 2*** uWSGI loaded loggers *** 3python 4syslog 5rsyslog 6socket 7redislog 8mongodblog 9file 10fd 11stdio 12--- end of loggers list --- 我对几个插件进行了测试。结果如下：\nsyslog 工作正常。 python 插件可以让 uWSGI 使用一个来自于 python 标准库的 logger。但这个插件没有文档，阅读源码后进行相关配置，实例会崩溃且没有可读的错误日志。 文档中提到的 zeromq 插件在 PyPI 最新 uWSGI 版本中没有提供，因此不可用。 我认为比较高效的方式，是使用 redislog 来分发日志。需要一个独立的日志接收服务，对发来的日志进行分类存储和集中化管理。\n这个日志服务就是 pyzog 。\n2. pyzog pyzog 是一个独立的服务，用来支持从 Redis 或者 ZeroMQ 发来的日志信息。它的特点如下：\n使用 python 的 logging 标准库来处理日志，方便扩展。 支持使用 Redis 的 Pub/Sub 机制来接收日志。 支持使用 ZeroMQ 的 PUB-SUB/DEALER-ROUTER 模式来接收日志。 使用 Python 标准库模块 logging.handlers.WatchedFileHandler 来支持文件 inode 改变的时候自动打开新的流，以实现自动 logrotate。 根据发来的信息提供的数据自动分类写入 log 文件。 提供命令行来启动服务。 使用 Supervisor 守护 pyzog 服务。 提供 systemd 配置文件的生成功能。 提供 program 配置文件的生成功能。 使用 pyzog 命令行可以查看其支持的子命令：\n1$ pyzog --help 2 3Usage: pyzog [OPTIONS] COMMAND [ARGS]... 4 5 执行 pyzog 命令 6 7Options: 8 --help Show this message and exit. 9 10Commands: 11 genprog 生成 supervisord 的 program 配置文件 12 genpyzog 在当前文件夹下生成 pyzog.conf 配置文件 13 gensupe 在当前文件夹下生成 supervisord.conf 配置文件 14 gensys 在当前文件夹下生成 systemd 需要的 supervisord.service 配置文件 15 start 启动 pyzog。请先使用 pyzog genpyzog 生成配置文件。 3. 部署 pyzog 接下来讲述将 pyzog 部署到服务器的流程。\n3.1 安装 pyzog 到虚拟环境 pyzog 没有提交的 PyPI。PyPI 上已有的那个 PyZog 与本项目无关。\n可以直接使用 pip 从 github 上安装 pyzog。创建虚拟环境并使用 pip 安装：\n1$ python3 -m venv ~/pyzog/venv 2$ source ~/pyzog/venv/bin/activate 3$ (venv) pip install git+https://github.com/zrong/pyzog 3.2 配置 Supervisor pyzog 需要一个守护程序，保证进程 crash 的时候自动拉起。我选择使用 Supervisor。它已经和 pyzog 一起被安装了。\npyzog 可以生成 Supervisor 的配置文件。\n1$ cd ~/pyzog 2$ (venv) pyzog gensupe --help 3 4 在当前文件夹下生成 supervisord.conf 配置文件 5 6Options: 7 -p, --path PATH 提供一个路径，配置中和路径相关的内容都放在这个路径下 8 --unix-http-server-file TEXT 9 --supervisord-logfile TEXT 10 --supervisord-pidfile TEXT 11 --supervisord-user TEXT 12 --supervisord-directory TEXT 13 --supervisorctl-serverurl TEXT 14 --include-files TEXT 15 --help Show this message and exit. 提供 --path 和 --superversord-user 参数，其他的参数会自动根据 --path 设置：\n1# 这里采用 app 这个普通用户来执行 supervisord。如果不提供这个参数，那么将使用 root 来运行 supervisord 2$ (venv) pyzog gensupe --path ~/pyzog --supervisord-user app 3# 根据 --path 的设定，创建集中放置 program 配置的文件夹 4mkdir ~/pyzog/conf.d 命令执行成功之后，会在当前文件夹下生成一个 supervisord.conf 配置文件。\n3.3 使用 systempd 守护 Supervisor Supervisor 是一个进程管理器，需要在服务器操作系统启动的时候自动启动。在 Supervisor 的文档 Running supervisord automatically on startup 中可以找到对应操作系统的 initscripts。\n目前，许多 Linux 发行版都已经使用 systemd 替换了标准的 initscript。我们这里使用 systemd 来代替老旧的 initscript。\npyzog 可以创建 systemd 配置文件。确保当前位于 ~/pyzog 文件夹中，虚拟环境激活状态：\n1$ (venv) pyzog gensys --help 2Usage: pyzog gensys [OPTIONS] 3 4 在当前文件夹下生成 systemd 需要的 supervisord.service 配置文件 5 6Options: 7 --supervisord-exec TEXT 8 --supervisorctl-exec TEXT 9 --supervisord-conf TEXT 10 --help Show this message and exit. 提供以上三个参数的值，：\n1$ (venv) pyzog gensys --supervisord-exec ~/pyzog/venv/bin/supervisord --supervisorctl-exec ~/pyzog/venv/binsupervisorctl --supervisord-conf ~/pyzog/supervisord.conf 命令执行成功之后，会在当前文件夹下生成一个 supervisord.service 配置文件。\n继续配置 systemd：\n1$ mkdir -p ~/.config/systemd/user 2$ mv ~/pyzog/supervisord.service ~/.config/systemd/user/ 3$ systemctl --user enable supervisord 4$ systemctl --user start supervisord 至此， supervisord 已经被正常启动，可以通过 systemctl --user status supervisord 查看其状态。\n3.4 创建 pyzog 运行需要的配置文件 pyzog 运行需要配置文件，pyzog 提供了子命令来生成它们：\n1$ (venv) pyzog genpyzog --help 2Usage: pyzog genpyzog [OPTIONS] 3 4 在当前文件夹下生成 pyzog.conf 配置文件 5 6Options: 7 -n, --name TEXT pyzog 实例的名称 [required] 8 -l, --logpath PATH 提供一个路径，pyzog 接收到的日志将放在这里 [required] 9 -t, --type [redis|zmq] 指定服务器类型，可选值 redis/zmq [required] 10 -a, --addr TEXT 服务器地址，形如 tcp://127.0.0.1:5011 或者 11 password@127.0.0.1:6379/0 [required] 12 -m, --get-message-type [thread|block|listen] 13 -s, --sleep-time FLOAT 调用 redis.pubsub.get_message 时提供的 sleep_time 14 参数 15 -c, --channel TEXT 仅当 type 为 redis 的时候提供，允许指定多个 channel 名称 16 --help Show this message and exit. 创建一个名称为 pyzog1.conf 的配置。使用 redis 接收日志，支持两个 channel req.* 和 app.*，log 文件写入 ~/pyzog/logs 目录。然后创建一个名为 conf 的文件夹，将所有的运行配置放入其中：\n1$ (venv) pyzog genpyzog -n pyzog1 -l ~/pyzog/logs -t redis -a \u0026#39;127.0.0.1:6372\u0026#39; -c \u0026#39;app.*\u0026#39; -c \u0026#39;req.*\u0026#39; 2$ mkdir ~/pyzog/conf 3$ mv ~/pyzog/pyzog1.conf ~/pyzog/conf 3.5 配置 Supervisor 中的 program 上面的配置指定了 Supervisor 管理的 program 配置文件位于 ~/pyzog/conf.d 中。让我们来生成一个配置：\n1$ (venv) pyzog genprog --help 2Usage: pyzog genprog [OPTIONS] 3 4 生成 supervisord 的 program 配置文件 5 6Options: 7 -n, --name TEXT Supervisor program 名称 [required] 8 -c, --config_file PATH [required] 9 -u, --user TEXT Supervisor program 的 user 10 --help Show this message and exit. 创建一个名称为 p1 的配置。使用刚才生成的 pyzog1.conf 配置文件运行，并将生成的 p1.conf 文件夹移动到 supervisord 的 program 配置文件夹中：\n1$ (venv) pyzog genprog -n p1 -c ~/pyzog/conf/pyzog1.conf -u app 2$ mv ~/pyzog/p1.conf ~/pyzog/conf.d 3.6 使用 supervisor 启动 pyzog 进程 启动 uspervisorctl，调用 reread 命令读取配置文件：\n1$ (venv) supervisorctl -c ~/pyzog/supervisord.conf 2supervisor\u0026gt; reread 3p1: available 启动这个 program：\n1supervisor\u0026gt; start p1 2p1: started 查看状态：\n1supervisor\u0026gt; status 2p1 RUNNING pid 12848, uptime 0:00:01 3.7 为什么不使用 systemd 直接管理 pyzog？ 因为 Supervisor 在管理上更加直观，在分组管理 program 的时候更加方便。\n4. 发送日志给 pyzog program 4.1 uWSGI 发送请求日志给 pyzog 在 Flask+uWSGI logging rotate：重要补充 中提到的 req-logger 无法进行 logrotate 的问题，可以使用 pyzog 解决了。只需要配置 uwsgi.ini：\n1[uwsgi] 2daemonize = true 3log-master = true 4req-logger = redislog:127.0.0.1:6379,publish req.mjp2401 uWSGI 会自动将 request log 内容使用 publish 命令发送给指定的 redis， 使用的 channel 名称为 req.mjp2401。\n上面配置了名为 p1 的 pyzog 进程用来接收 req.* 这个 channel 名称，当 pyzog 接收到 uWSGI 传来的 log 时，会自动创建 ~/pyzog/logs/req.mjp2401.log 这个日志文件。\n如果采用不同的 channel(req 前缀)，pyzog 会创建不同的日志文件。这就解决了数十个 uWSGI 实例分开处理日志的问题。\n4.2 python logging 发送日志给 pyzog 在 Flask+uWSGI logging rotate：重要补充 中的 5. Flask 中的日志处理 这个章节，我封装的 get_logger 方法支持 stream/file/zmq 三种不同类型的 logger。\npyzog 提供了 RedisHandler，扩展了 get_logger 方法，增加了对 redis 的支持，详情参考 pyzog.logging 源码。\n5. 设置 logrotate 增加一个 logrotate 配置文件 /etc/logrotate.d/pyzog，内容如下：\n1/home/app/pyzog/logs/*.log { 2 su app app 3 # WatchedFileHandler 会自动创建新文件 4 nocreate 5 daily 6 rotate 5 7 missingok 8 notifempty 9 compress 10 dateext 11 dateyesterday 12} 重启 logrotate 服务，终极方案完成。\n完美！\n6. 相关阅读 部署Flask + uWSGI + Nginx uWSGI+rsyslog 实现 rotating logging Flask+uWSGI 的 Logging 支持 Flask+uWSGI logging rotate：重要补充 全文完 ","date":"2020-02-10","description":"","lastmod":"2020-02-17T07:40:24Z","slug":"pyzog","tags":["uwsgi","flask","python","server","logging"],"title":"pyzog：uWSGI logging rotate 的终极方案","url":"https://blog.zengrong.net/post/pyzog/"},{"categories":["impressions"],"content":"全国人民都在云开工，但武汉特别难。\n1月 25 日，我写了这篇：坚守武汉，我们应该做什么。10 天过去了，到了开工的日子，SAGITEAM 似乎已经做好了万全的准备。\n\u0026lt;!--more--\n我们知道，困难总比办法多办法总比困难多！下面就把 SAGITEAM 开工碰到的困难来说一说。\n大家要知道的是，湖北是一个特殊的省，“鄂”在车牌上是一个有魔力的文字，4201 在身份证上也是一个极具力量的数字。武汉处在疫情中心，碰到的情况复杂且魔幻。\n以下内容绝对真实，全部来自于 SAGITEAM 的朋友圈和群。如有雷同，我们握个爪🤝！\n先要解决的是办公设备问题： 由于武汉宣布封城的时候公司已经放假，很多同事没有机会准备工作电脑。武汉目前是全面禁行状态，无法去公司搬运设备。\n为了避免被隔离，我们也应该尽量呆在家里。\n有些同事在武汉周边城市过年（比如黄冈啦孝感啦宜昌啦），过年期间每天锻炼身体就为了回来搬电脑。\n然鹅，这些城市和武汉之间的交通已经被切断，现在回武汉挺麻烦的，似乎近期是不能打办公电脑的主意了。\n于是大家就开始八仙过海各显神通了。求助万圈：\n准备…… 走路…… 4.5 公里，配上接头暗号！\n大家动用一切力量从各个角落里找到老爷机操练起来：\n这个貌似还好\n下面这个就有点惨不忍睹了\n超豪华的装机设备，美术同学自己动手了。\n还有直接抢哥哥的高级电脑的，这个就有点过分了啊！\n但这个电脑真的很酸啊！不酸不行啊！！！🍋🍋🍋🍋🍋🍋\n但就算你有个拥有超级电脑的哥哥，你也得去拿过来对不对？SAGITEAM 的策划同学准备去邻村拿高级电脑的，出门发现路被挖断了……挖……断……了……\n还有同学动用了自己价值几百万的关系网。平时都是出了事儿才动用的，现在做正经事也要动用了：\n动用国家力量。这样真的好么？这不是走后门么？\n成功弄到设备的同学，也有各种不安定因素：比如这个 1 和 3 不能按，需要从其他地方复制过来 1 和 3……\n比如 Unity 启动要 30 秒的……\n比如没有手绘板，比如碰到家里领导也要上班的电脑不够，总不能强抢领导的电脑对不对？\n设备总算大部分搞定，开会又碰到问题： 企业微信挡不住大家一起开会，罢工了……\n接下来的时间里，我们更换了 YY、微信、钉钉等多种聊天工具，总算把大会小会项目会给开完啦！\n开工后碰到的问题，是没有测试设备 作为手机游戏公司，当然可以用自己的手机来测试。可是OPPO、VIVO 这种平台独占的游戏，是不能用普通手机来测试的！\n有老设备经不起折腾烧了，哥们真勇士，准备横穿武汉去公司扛电脑。看来我们要🔥了。\n买设备？又碰到物流停摆的问题。\n有钱买不到东西这还真的是挺难办呢。\n无论多难，SAGITEAM 还是开工了对不对！\n无论多难，我们总能同舟共济的对不对！\n火神山医院今天都交付使用了，对不对！\nSAGITEAM 加油！ 武汉加油！ 全文完 ","date":"2020-02-04","description":"","lastmod":"2020-02-04T00:56:00Z","slug":"work-in-wuhan-with-2019-ncov","tags":["life","sagiteam","COVOD-19"],"title":"云开工！疫情挡不住武汉人的工作热情！","url":"https://blog.zengrong.net/post/work-in-wuhan-with-2019-ncov/"},{"categories":["technology"],"content":"我在 Flask+uWSGI 的 Logging 支持 一文中详细讲解过关于 Flask+uWSGI 中的 logging 支持情况。但还不够，这篇文章里面做一些重要补充。\n1. copytruncate 的不足之处 在 Flask+uWSGI 的 Logging 支持 一文的最后，我对于 logging rotate 有这样一段描述：\nuWSGI 还可以使用 touch-logrotate 和 touch-logreopen 来实现 logging rotate，但为了让系统更加简单的独立，我建议使用 logrotate 来实现 logging rotate，并已经在 uWSGI+rsyslog 实现 rotating logging 一文中介绍过具体做法。\n需要注意的是，我在 uWSGI+rsyslog 实现 rotating logging 一文的 单独使用 logrotate 小节中提到的使用 copytruncate 选项替换 create 选项，是因为没有通知 uWSGI 重新打开 log 文件。要做到这一点非常简单，除了使用刚才提到的 touch-logreopen 之外，还可以使用 Master FIFO 中的 l 命令。\n如果不使用 rsyslog 这类系统工具，而只用 logrotate 来实现 logging rotate，我们有两种选择，使用 copytruncate 或者使用 create 来 rotate 日志文件。\n在使用 copytruncate 的时候，如果日志文件很小（例如小于 100MB），那么这样做问题不大。如果日志文件很大呢？例如 MJP 的日志文件是几十个 GB 的大小。这就出现了两个问题：\n即使是使用高速 SSD 硬盘，复制 GB 级别的日志文件也成了一个非常耗时的工作。 每天制造几十个 GB 日志的服务是非常繁忙的服务，在 copy 的过程中，一定会丢失一部分日志无法写入。 那么使用 create 是否可行呢？\n摘录一段 uWSGI+rsyslog 实现 rotating logging 中的原文：\n这是因为在 linux 系统下，一个进程打开文件时使用的是文件系统的 inode 编号而非文件名。移动或者重命名一个文件，并不会修改它的 inode 编号。因此需要在进行 rotate 之后，通知 uwsgi 重新打开 log 文件。\n或者，可以将 create 创建方式修改为 copytruncate 创建方式，后者的特点是复制一份现有的 log 为新文件，然后清空旧文件。这样就不需要通知 uwsgi 重新打开 log 文件了。\n所以，我们只需要解决 重新打开 log 文件 的问题，就可以解决 copytruncata 带来的不足。\n2. uwsgi 重新打开 log 文件 成功触发 logreopen 时，uWSGI 日志会给出下面的消息：\n1[1580052181] logsize: 13574, triggering log-reopen... 2[1580052181] /srv/app/mjpadm/logs/uwsgi.log reopened. 关于重新打开 log 文件，uWSGI 中有两种方法可以实现：\n2.1 方法一 首先加入 touch-logreopen 配置：\n1[uwsgi] 2# 必须配置 log-master 才支持 logreopen 3log-master = true 4touch-logreopen = /srv/app/mjpadm/mjp-touch-logreopen.touch 改名旧 log 文件，创建新 log 文件之后，touch 一下 touch-logreopen 选项指定的文件，即可通知 uWSGI 重新打开新的 log 文件。\n这个文件就是一个普通的 0 字节文件，但这个文件需要预先创建，否则 uWSGI 启动的时会报 warning。\n重新打开文件，可以直接执行命令:\n1touch /srv/app/mjpadm/mjp-touch-logreopen.touch 2.2 方法二 启用 The Master FIFO，配置如下：\n1[uwsgi] 2# 必须配置 log-master 才支持 logreopen 3log-master = true 4master-fifo = /srv/app/mjpadm/uwsgi.fifo 改名旧 log 文件，创建新 log 文件之后，执行下面的命令向 fifo 文件写入 l 指令，即可通知 uWSGI 重新打开新的 log 文件。\n1echo l \u0026gt; /srv/app/mjpadm/uwsgi.fifo 3. 哪个 logger 选项支持 logreopen 你以为像上面那样配置好就结束了？错！\nuWSGI Logging 文档中提到，有好几个选项可以实现 logging，但这些选项的作用，文档中却没有仔细介绍。这是 uWSGI 文档的特点。但我也不能过多吐槽，毕竟它还是有文档的对不对？总比直接翻源码要好一点对不对？\n你要记住的是： 并不是所有的 logger 选项都支持 logreopen！！！\n根据实验，我把 uWSGI 的所有 logger 的支持列在下面。uWSGI 的 logger 选项分为 基本 logger 和 插件化 logger 两种。\n3.1 基本 logger logto/logto2 指定一个文件，将日志内容写入文件。支持 logreopen daemonize 指定一个文件，将日志内容写入文件。也可以不指定文件，仅提供参数，此时需要配合其他 logger 指定日志写入到哪里。 不支持 logreopen 3.2 插件化 logger logger 可以支持多种日志写入目标，当使用 file:/path/to/foo.log 语法的时候支持写入文件。不支持 logreopen req-logger 仅处理请求日志，用于将请求日志单独分离。可以支持多种日志写入目标，当使用 file:/path/to/bar.log 语法的时候支持写入文件。 不支持 logreopen 插件化 logger 是支持多种写入目标的，通过配置 file:/socket:/syslog:/redislog:zeromq: 等等前缀来执行，具体可以查看 uWSGI Logging 文档，这里仅仅讨论 file: 的情况。\n3.3 实战 uWSGI 配置 根据我的测试，只有这样的配置才能支持 logreopen：\n1[uwsgi] 2# 不要给 daemonize 指定文件，简单设置为 true 即可 3daemonize = true 4# 必须配置 log-master 才支持 logreopen 5log-master = true 6logto = /srv/app/mjpadm/logs/uwsgi.log 7# master-fifo 和 touch-logreopen 任选其一 8master-fifo = /srv/app/mjpadm/uwsgi.fifo 9; touch-logreopen = /srv/app/mjpadm/mjp-touch-logreopen.touch 4. logrotate 相关配置 我们讨论不使用 logrotate 中的 copytruncate 配置，看看对应 logreopen 的相关 logrotate 配置：\n1/srv/app/mjpadm/logs/uwsgi.log { 2 su app app 3 create 0644 app app 4 daily 5 rotate 10 6 missingok 7 notifempty 8 sharedscripts 9 # 执行完毕 rotate 之后，通知 uWSGI 重新打开日志，以下两种方法任选其一 10 postrotate 11 # 方法一 12 touch /srv/app/mjpadm/mjp-touch-logreopen.touch 13 # 方法二 14 echo l \u0026gt; /srv/app/mjpadm/uwsgi.fifo 15 endscript 16} 5. Flask 中的日志处理 uWSGI 相关的部分讨论完了，接着来看看 Flask 中的日志处理。\n我建议使用 Python 自带的 logging 模块来处理 Flask 的日志，这样会更加灵活也更容易控制。我在 MJP 项目中封装了两个方法 get_logger/get_logging_handler 供使用：\n1# -*- coding: utf-8 -*- 2__version__ = \u0026#39;1.3.1\u0026#39; 3 4import logging 5from logging.handlers import WatchedFileHandler 6from pathlib import Path 7 8import zmq 9from pythonjsonlogger import jsonlogger 10 11 12TEXT_LOG_FORMAT = \u0026#34;\u0026#34;\u0026#34; 13[%(asctime)s] %(levelname)s in %(module)s.%(funcName)s [%(pathname)s:%(lineno)d]: 14%(message)s\u0026#34;\u0026#34;\u0026#34; 15JSON_LOG_FORMAT = r\u0026#39;%(levelname)s %(module)s %(funcName)s %(pathname)s %(lineno) %(threadName) %(processName)\u0026#39; 16 17def _create_file_handler(target, filename): 18 \u0026#34;\u0026#34;\u0026#34; 创建一个基于文件的 logging handler 19 :param target: 一个 Path 对象，或者一个 path 字符串 20 \u0026#34;\u0026#34;\u0026#34; 21 logsdir = None 22 if isinstance(target, Path): 23 logsdir = target.joinpath(\u0026#39;logs\u0026#39;) 24 else: 25 logsdir = Path(target).joinpath(\u0026#39;logs\u0026#39;) 26 # 创建或者设置 logs 文件夹的权限，让其他 user 也可以写入（例如nginx） 27 # 注意，要设置 777 权限，需要使用 0o40777 或者先设置 os.umask(0) 28 # 0o40777 是根据 os.stat() 获取到的 st_mode 得到的 29 if logsdir.exists(): 30 logsdir.chmod(0o40777) 31 else: 32 logsdir.mkdir(mode=0o40777) 33 logfile = logsdir.joinpath(filename + \u0026#39;.log\u0026#39;) 34 if not logfile.exists(): 35 logfile.touch() 36 # 使用 WatchedFileHandler 在文件改变的时候自动打开新的流，配合 logrotate 使用 37 return WatchedFileHandler(logfile, encoding=\u0026#39;utf8\u0026#39;) 38 39 40def _create_zmq_handler(target): 41 \u0026#34;\u0026#34;\u0026#34; 创建一个基于 zeromq 的 logging handler 42 :param target: 一个字符串，形如： tcp://127.0.0.1:8334 43 \u0026#34;\u0026#34;\u0026#34; 44 ctx = zmq.Context() 45 pub = ctx.socket(zmq.PUB) 46 pub.connect(target) 47 return zmq.log.handlers.PUBHandler(pub) 48 49 50def get_logging_handler(type_, fmt, level, target=None, name=None) : 51 \u0026#34;\u0026#34;\u0026#34; 获取一个 logger handler 52 53 :param type_: stream/file/zmq 54 :param fmt: text/json 55 :param level: logging 的 level 级别 56 :param target: 项目主目录的的 path 字符串或者 Path 对象，也可以是 tcp://127.0.0.1:8334 这样的地址 57 :param name: logger 的名称，不要带扩展名 58 \u0026#34;\u0026#34;\u0026#34; 59 handler = None 60 if type_ == \u0026#39;zmq\u0026#39;: 61 if target is None: 62 raise TypeError(\u0026#39;target is necessary if type is zmq!\u0026#39;) 63 handler = _create_zmq_handler(target) 64 elif type_ == \u0026#39;file\u0026#39;: 65 if target is None or name is None: 66 raise TypeError(\u0026#39;target and name is necessary if type is file!\u0026#39;) 67 handler = _create_file_handler(target, name) 68 else: 69 handler = logging.StreamHandler() 70 if fmt == \u0026#39;text\u0026#39;: 71 formatter = logging.Formatter(TEXT_LOG_FORMAT) 72 else: 73 formatter = jsonlogger.JsonFormatter(JSON_LOG_FORMAT, timestamp=True) 74 handler.setLevel(level) 75 handler.setFormatter(formatter) 76 return handler 77 78 79def get_logger(name, target, type_=\u0026#39;file\u0026#39;, fmt=\u0026#39;text\u0026#39;, level=logging.INFO): 80 \u0026#34;\u0026#34;\u0026#34; 基于 target 创建一个 logger 81 82 :param name: logger 的名称，不要带扩展名 83 :param target: 项目主目录的的 path 字符串或者 Path 对象，也可以是 tcp://127.0.0.1:8334 这样的地址 84 :param type_: stream/file/zmq 85 :param fmt: text/json 86 :param level: logging 的 level 级别 87 \u0026#34;\u0026#34;\u0026#34; 88 hdr = get_logging_handler(type_, fmt, level, target, name) 89 90 log = logging.getLogger(name) 91 log.addHandler(hdr) 92 log.setLevel(level) 93 return log 注意，上面的方法依赖两个外部模块 pyzmq 和 python-json-logger，请先使用 pip 安装。这两个外部模块提供 ZeroMQ 发送日志，并同时支持 JSON 格式的日志格式。\nget_logger 和 get_logging_handler 可以单独使用。注释很详细，不再赘述。\n5.1 WatchedFileHandler 支持自动 reopen Python 标准库模块 logging.handlers.WatchedFileHandler 支持在文件改变的时候自动打开新的流，和 logrotate 是绝配了。有了这个我们甚至不需要在创建了新的 log 之后通知 uWSGI 重新打开 log 文件。\n5.2 _set_logger 升级版 我在 Flask+uWSGI 的 Logging 支持 中展示了 _set_logger 方法，现在有了上面的 get_logging_handler 封装，可以让 _set_logger 升个级。\n1def _set_logger(mjpapp): 2 \u0026#34;\u0026#34;\u0026#34; 设置 Flask app 和 sqlalchemy 的logger 3 \u0026#34;\u0026#34;\u0026#34; 4 # 获取 flask 和 sqlalchemy 的 logger 5 flasklogger = mjpapp.logger 6 sqlalchemylogger = logging.getLogger(\u0026#39;sqlalchemy\u0026#39;) 7 # 删除 Flask 的默认 Handler 8 del flasklogger.handlers[:] 9 handler = None 10 # 默认为 DEBUG 级别 11 level = logging.DEBUG 12 # 在 DEBUG 状态下，使用 stream handler 13 if mjpapp.config.get(\u0026#39;DEBUG\u0026#39;): 14 handler = get_logging_handler(\u0026#39;stream\u0026#39;, \u0026#39;text\u0026#39;, level) 15 else: 16 # 切换为 INFO 级别，使用 json 格式的日志 17 level = logging.INFO 18 handler = get_logging_handler(\u0026#39;file\u0026#39;, \u0026#39;json\u0026#39;, level, target=config.getdir(), name=\u0026#39;app\u0026#39;) 19 flasklogger.setLevel(level) 20 sqlalchemylogger.setLevel(logging.WARNING) 21 for log in (flasklogger, sqlalchemylogger): 22 log.addHandler(handler) 6. 终极方案 你以为像上面那样处理好就结束了？错！！\n还有一个 小问题 没有解决。\n上面提到 req-logger 是不支持 logreopen 的，而且 uWSGI 的请求日志，也没办法让 Python 的 WatchedFileHandler 代劳。前面也讨论过，使用 copytruncate 处理几十个 GB 的请求日志，会丢失一部分数据。\nuWSGI 的主要作者 unbit 也讨论过，req-logger 这个插件化的 logger 不支持 logreopen。\nrequest logging only uses pluggable loggers, so \u0026quot;basic file-logging\u0026quot; procedure cannot work. Each plugin has its features, and, as an example, \u0026quot;reopening\u0026quot; is not meaningful for the vast majority of them. You could improve the logfile plugin to support reopening, or you can use the logpipe plugin to use (again as an example) a tool like this: http://httpd.apache.org/docs/2.2/programs/rotatelogs.html\n这里不讨论 log-maxsize 这样的配置来实现基于文件大小的 rotate 的情况。毕竟大多数情况下我们需要的是 daily rotate。\n而且，当你的 uWSGI 实例达到数十个的时候，使用 touch-logreopen 或者 Master FIFO 是个很麻烦的事情。因为 logrotate 的配置文件中，尽管对于路径支持通配符，但在 postrotate script 中不支持魔术变量，这会导致我们无法去 touch 数十个不同的文件。\nMJP 就有近百个实例部署在多台服务器上，要处理它们的 logreopen 就是一件挺棘手的问题。\n因此，我们需要一个终极解决方案来解决这个问题。这需要新开一篇文章来说明。请看下篇：\npyzog：uWSGI logging rotate 的终极方案\n7. 相关阅读 部署Flask + uWSGI + Nginx Flask+uWSGI 的 Logging 支持 uWSGI+rsyslog 实现 rotating logging pyzog：uWSGI logging rotate 的终极方案 全文完 ","date":"2020-01-27","description":"","lastmod":"2020-08-15T03:33:57Z","slug":"flask-uwsgi-logging-rotate","tags":["uwsgi","flask","python","server","logging"],"title":"Flask+uWSGI logging rotate：重要补充","url":"https://blog.zengrong.net/post/flask-uwsgi-logging-rotate/"},{"categories":["impressions"],"content":"作为春节期间的武汉人，作为一个处于 2019 新型冠状病毒（2019-nCoV）爆发中心的人，我的心情一直是复杂的。各种不实的信息满天飞，ZF的辟谣和官宣总是姗姗来迟。\n我仔细梳理了一遍 湖北省人民政府网站专题页面 提供的信息，把一些重要的列在下面。\n官方信息 如果想看武汉市新型肺炎防控指挥部的所有通告，可以看这个页面：最新通报\n2020-01-25 武汉外环（福银、沪蓉、京港澳、沪渝四条高速内的空间）处于禁行状态。\n网约车停运，出租车单双号限行。两条长江隧道关闭，桥梁通行实施体温检测。\n自2020年1月26日0时始，除经许可的保供运输车、免费交通车、公务用车外，中心城区区域实行机动车禁行管理。\n来源：湖北省人民政府网站\n2020-01-24 全市紧急征集6000台出租车，分配给中心城区\n为解决市民居家出行不便等问题，全市紧急征集6000台出租车，分配给中心城区。每个社区3-5台，由社区居委会统一调度使用，自2020年1月25日中午始，为辖区居民出行提供免费服务。社区居委会为生活不便居民上门免费提供送餐、送菜、送药等居家服务。\n来源：湖北省人民政府网站\n2020-01-25 武汉火神山医院（武汉的“小汤山”）2 月 1 日建成\n2020-01-25 武汉市征用24家医院万张床位收治发热病人\n武汉市在早期两家定点医院和61家发热门诊的基础上，第一批、第二批、第三批分别征用7家、3家、14家综合医院，参照感染性疾病防治机构的基本要求，临时改造成为发热病人收治医院，目前收治疑似和确诊病例的床位达4000余张，另6000余张床位将于本月底前提供使用。\n来源：湖北省人民政府网站\n从上面的信息来看，ZF做了很多事情，但统筹不力，宣传不够。人类更容易被负面信息所影响，这是天性。ZF的宣传速度，跟不上互联网信息传播的节奏。\n关于公布的确诊人数，的确需要试剂才能确认。这会导致我们心理感受的数字和实际数字有出入。就好像民众大多认为被警察叔叔铐上的都是“罪犯”，但法律上经过法院审理之前都只能叫做“犯罪嫌疑人”。\n我们要保持镇定 情势危急，作为普通人，我们更应该在大灾面前保持镇定。\n我们要做的是：\n不随意外出（封城禁行也出不去）。保持自我隔离，阻断传染路径，还能节省口罩。 早睡早起，定点吃饭，每日家中锻炼半小时（无氧或者瑜伽）。家中若没有相应条件，可以下载 KEEP 做 K1 级别的自重锻炼。 定时刷手机，不要沉浸在负面信息和慌乱情绪中无法自拔，也不要漏掉重要信息。 锻炼判断信息真实性的能力。 判断信息的真实性 我们在微信群，微信朋友圈和微信看一看中会得到大量的信息。这些信息如果不加甄别照单全收，会给我们的心理带来大量的负担。这些信息的来源不一，信息编译时作者的心态不一，信息不够全面透明，很多信息只是只言片语和一转再转，甚至有些信息的目的并不单纯。\n学会如何鉴别这些信息的真实性，有几个简单易行的办法：\n判断信息是不是来源于可信源。\n优先选择相信官方媒体和主流媒体的报道。对公众号、头条、和非官方网站的信息应该首先持商讨态度，不要立刻全盘相信。对于微信群中的小视频、录屏和各种转述的信息优先持怀疑态度。总结就是：\n官方媒体 \u0026gt; 公众号 \u0026gt; 微信群\n武汉户籍人口 1108 万，加上常住人口 500 万。三甲医院 24 所，总医院数量 256 所，病床数量 9.2 万张。牢记这些数据，当得到信息的时候与之对比，可以发现信息是否存在真实性问题。\n来源：湖北省卫健委-湖北省医院等级情况查询 来源：武汉市卫健委-砥砺前行四十年 改革铺就健康路 从公开信息中推演 这是我在 1 月 22 日的一个推演。作为普通人我们无法及时得到内部信息，但这些内部信息一定会以某种方式表现出来。通过分析这些表现，我们就能得到一些未公开的信息。\n上面三张图是湖北省博物馆公众号推送的信息。一个是上午11点，一个是晚上10点。 这11个小时里面，ZF的管控等级是提升了不少的。省博作为文化和旅游的省级直管单位，一定会快速严格执行ZF给出的方案。\n上午说的方案还是标准方案，三十和初一闭关，停止了编钟演奏。晚上就直接闭馆两周了。从这一点上看，事态是很严重的。\n下面是江腾广场接连三天的推送。可以看出，23日是广场停止营业，但肯德基还在营业；24日肯德基停止营业；25日强调沃尔玛超市持续营业，保证民生基础供应。\n让我们保持充沛的体力和精力，维持平和的心态，相信白衣天使，相信国家力量，等待春天来临。\n全文完 ","date":"2020-01-25","description":"","lastmod":"2020-01-25T06:43:14Z","slug":"stay-in-wuhan-with-2019-ncov","tags":["life","COVOD-19"],"title":"坚守武汉，我们应该做什么","url":"https://blog.zengrong.net/post/stay-in-wuhan-with-2019-ncov/"},{"categories":["impressions"],"content":"阅读是很重要的能力。我从 2015 年开始刻意练习阅读能力，现在基本形成了自己的读书风格。\n我的读书历史在这里： https://zengrong.net/read/。\nSAGI读书会成立的目的，是培养 SAGITEAM 成员的阅读习惯。\n我会把 SAGI 读书会的成果持续记录在博客和公众号上，这既是承诺，也是监督。\n读书会选书 每月一本书，由参加读书会的同学推荐。\n推荐的书必须是公认的经典，或者对参加读书会的所有人都有重要的借鉴和参考意义。\n推荐的书涉及面应该足够广泛。\n读书会目标 每本书至少读 2 遍以上：略读一遍，精读一遍。可参考《如何阅读一本书》。\n每本书要写读书笔记。可以参考《如何有效阅读一本书》。\n关注如何提高阅读速度和效率。可以参考《脑与阅读》。\n我之前写过一些读书笔记，仅供参考：reading | zrong' Blog\n下面是几篇我写的风格完全不同的读书笔记，我在这些笔记中不断尝试适合不同书籍的笔记风格，也在尝试不同的笔记方法：\n【读书笔记】海狸的记号 【读书笔记】聘谁 【读书笔记】我的创业-读《从0到1》有感 【读书笔记】掌控力——让所有人对你讲真话 读书会成果 提供读书笔记，读书笔记可以写在腾讯文档或者云笔记之类的地方。\n每月初组织一次讨论会，交流上月读书心得并记录，确定当月选书。\n2020年1月选书 俞军产品方法论 作者: 俞军 出版社: 中信出版集团 出版年: 2019-12-1 全文完 ","date":"2020-01-11","description":"","lastmod":"2020-01-11T10:33:15Z","slug":"sagi-reading-202001","tags":["sagiteam","reading","sagibookclub"],"title":"SAGI读书会第一期(2020年1月)：《俞军产品方法论》","url":"https://blog.zengrong.net/post/sagi-reading-202001/"},{"categories":["technology"],"content":"本文是 从 Flask 到 Gin 系列的第 6 篇，也是最后一篇。\n到这里，重点的内容就讲得差不多了。似乎就剩下怎么部署和维护服务器了。\n本文主要介绍如何部署 App，以及实现优雅重启。\n部署代码 Fabric 是我一直都在使用的部署工具。它是很成熟的工具，网上能找到大量的介绍文章。需要注意的是两点：\n使用最新的 2.x 版本（支持 Python3）。 早期一点的版本（2019Q4 之前）由于依赖库 Paramiko 不支持 MacOS 生成的 BEGIN OPENSSH PRIVATE KEY 开头的私钥格式，导致在 MacOS 上使用新的 OPENSSH 私钥会报错，现在这个 BUG 已经修复，确保 Paramiko 库使用 2.7.1 版本以上即可。 Flask App 的部署，直接把 Python 源码用 Frbric transfers.rsync 上传到服务器上就可以了，当然你也可以直接自己写个 shell 脚本调用 rsync 来解决。\nGin 的部署，将编译后的可执行文件使用 Fabric.connection.Connection.put 上传到服务器即可。\nFlask App 的启动 三年前，我写过一篇 部署Flask + uWSGI + Nginx 完整地介绍过如何部署 Flask 实现的服务，而且后续的一年一直在更新这篇文章。这几年我用 Flask 和 uWSGI 写了很多服务，采用的都是文中提到的方法。\n因此，Flask App 部署和启动，可以直接参考上文来处理。本文就不再花费笔墨介绍了。\nGin App 的启动 Gin 生成的 App 就是一个可执行文件而已，如果是临时使用，可以用 nohup/tmux/screen 保证后台启动；如果是正式使用，可以用 systemd 或者 Supervisor。\nuWSGI + Flask 优雅重启 优雅重启，也叫热更新、平滑升级等等，表达的都是同一个意思，英文一般使用 Graceful upgrade 或者 Zero Downtime。\nuWSGI + Flask 的优雅重启很简单，使用 rsync 更新 Python 源码，然后向 uWSGI 的 master 进程发送 SIGHUP 即可实现优雅重启。\n要发送信号，可以使用 Fabric.connection.Connection.run 。\n近期我部署的大量的 Flask APP 实例用了另外一种方式：负载均衡 + uWSGI http 服务。这套方案不再需要 Nginx 支持，而是直接用负载均衡服务将请求转发到 uWSGI 的 http 端口。如果实例规模进一步扩大，使用这种方式也容易移植到 API Gateway。\n在这种新的部署方式下，我采用了 uWSGI 的 Maser FIFO 来实现优雅重启。从发送控制信号到 uWSGI 进程，改为了直接写入 FIFO 文件来实现优雅重启。这样就不需要关注 master 进程 pid 的变化，或许这样更“优雅”一点？\nGin App 优雅重启 对于 Gin 的 App，优雅重启需要自己来实现。Golang 1.8 新增了 Server.Shutdown 之后，要实现优雅重启已经相当简单了。\n我直接拿来主义使用 zerdown 这个 package，main.go 的内容：\n1package main 2 3import ( 4\t\u0026#34;fmt\u0026#34; 5 \u0026#34;github.com/douglarek/zerodown\u0026#34; 6\t\u0026#34;mjp/models\u0026#34; 7\t\u0026#34;mjp/routers\u0026#34; 8\t\u0026#34;mjp/util\u0026#34; 9 10\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 11) 12 13func main() { 14\tdefer func() { 15\tif e := recover(); e != nil { 16\tfmt.Printf(\u0026#34;Panicing in main: %s\\n\u0026#34;, e) 17\t} 18\t}() 19\tmodels.AutoMigrate() 20\tr := gin.Default() 21\trouters.Init(r) 22\tconf := util.ConfigInstance() 23\tzerodown.ListenAndServe(conf.Address, r) 24} 上面的代码只是提供了优雅重启的功能，要实现优雅重启，还需要知道进程的 pid，向这个进程发送 USR2 信号。\n在 Fabric 中获得进程 PID 和发送信号 依然是依赖 Fabric.connection.Connection.run 的远程执行能力，下面的代码片段位于 fabfile.py 中：\n1import re 2from fabric import task, Connection 3from invoke.exceptions import Exit 4 5def get_pid(conn): 6 address = \u0026#39;127.0.0.1:5005\u0026#39; 7 command = \u0026#39;lsof -i @%s | tail -1\u0026#39; % address 8 result = conn.run(command, warn=False, hide=True) 9 if result.stdout == \u0026#39;\u0026#39;: 10 return None 11 return re.split(r\u0026#39;\\s+\u0026#39;, result.stdout)[1] 12 13@task 14def reload(c): 15 \u0026#34;\u0026#34;\u0026#34; 重载服务 16 \u0026#34;\u0026#34;\u0026#34; 17 if not isinstance(c, Connection): 18 raise Exit(\u0026#39;Use -H to provide a host!\u0026#39;) 19 pid = get_pid(c) 20 if pid is None: 21 raise Exit(\u0026#39;进程不存在，不能重载。\u0026#39;) 22 result = c.run(\u0026#39;kill -USR2 %s\u0026#39; % pid, warn=False) 23 if result.ok: 24 new_pid = get_pid(c) 25 print(\u0026#39;重载进程 %s 成功，新的 pid 为 %s。\u0026#39; % (pid, new_pid)) 参考 部署Flask + uWSGI + Nginx The Master FIFO Signals for controlling uWSGI Graceful upgrades in Go Go 服务优雅重启 源码分析golang http shutdown优雅退出的原理 Server.Shutdown zerodown 本系列到此结束，阅读系列所有文章：从 Flask 到 Gin。\n全文完 ","date":"2020-01-11","description":"","lastmod":"2020-01-11T07:37:39Z","slug":"flask-to-gin-deploy-and-graceful-upgrade","tags":["fromto","flask","golang","python","gin","mjp"],"title":"从 Flask 到 Gin —— 部署和优雅重启","url":"https://blog.zengrong.net/post/flask-to-gin-deploy-and-graceful-upgrade/"},{"categories":["technology"],"content":"本文是 从 Flask 到 Gin 系列的第 5 篇。\nPython 提供了一个有趣且有用的语法糖：装饰器。使用装饰器，我使用装饰器在 Flask 中实现了鉴权：用户进行数据提交的时候，需要提供一个 TOKEN，这个 TOKEN 如果解密成功，就正常进行 response 相关的逻辑，反之则直接返回 403 错误。\n鉴权逻辑就是使用 Python 的装饰器实现的。但 Golang 没有装饰器特性，我会使用 Gin 的中间件机制来代替它。\n实现 mjst_checker 装饰器 mjst_checker 是一个装饰器，经过它装饰的路由方法，会自动处理请求中带来的名为 mjst 的 token，token 中包含用户的权限信息，还有 token 有效期等等信息，如果信息正常，那么路由会继续正常处理，否则会得到 403 错误。\n来看看 mjst_checker 装饰器的定义：\n1from functools import wraps 2 3class PYConf(dict): 4 \u0026#34;\u0026#34;\u0026#34;基于 Python dict 的配置文件。 5 6 dict 默认不适合当作配置文件对象使用。如要有下面几点不便： 7 8 #. 对于不存在的 key，会 raise KeyError 错误； 9 #. dict不能使用 ``.`` 语法访问。 10 11 :class:`PYConf` 解决了这些问题，还另外提供了一些方法在使用上更加方便。 12 13 \u0026#34;\u0026#34;\u0026#34; 14 15 def __missing__(self, key): 16 return None 17 18 def __getattr__(self, name): 19 return self[name] 20 21 def __setattr__(self, name, value): 22 self[name] = value 23 24 def __delattr__(self, name): 25 del self[name] 26 27 28def mjst_checker(*typeids): 29 \u0026#34;\u0026#34;\u0026#34; MJST 的检测器，检测 HEADER 中传来的 MJST 是否正常 30 :param typeids: typeid 数组 31 \u0026#34;\u0026#34;\u0026#34; 32 33 def decorator(f): 34 @wraps(f) 35 def decorated_fun(*args, **kwargs): 36 # _get_mjst 从请求中获取 mjst 的值 37 mjst = _get_mjst() 38 # 将要加入 kwargs 中的mjst解析后的值，默认是一个错误，代表没有解析成功 39 mjstarg = PYConf({\u0026#39;error\u0026#39;: True, \u0026#39;code\u0026#39;: 403, \u0026#39;message\u0026#39;: \u0026#39;MJST DECODE ERROR!\u0026#39;}) 40 try: 41 # _decode_mjst 为具体的解密方法 42 mjstobj = _decode_mjst(typeids, mjst) 43 if mjstobj is not None: 44 mjstarg = mjstobj 45 except Exception as e: 46 logger.error(\u0026#39;@mjst_checker decode_mjst:%s error:%s\u0026#39;, mjst, str(e)) 47 # 此处的 mjstarg 一定为非 None，至少包含上面提供的 403 错误。这里不使用 HTTP 403，让客户端知道出了什么错 48 kwargs[\u0026#39;mjst\u0026#39;] = mjstarg 49 return f(*args, **kwargs) 50 51 return decorated_fun 52 53 return decorator 其中涉及的 _get_mjst 和 _decode_mjst 方法的作用已经在注释中解释过了，具体实现就不提供了。让我们看看 mjst_checker 这个装饰器应该怎么使用。\nmjst_checker 的用法 在 从 Flask 到 Gin —— SQLAlchemy 和 gorm 中我们定义了两个路由方法，register 和 active 是没有增加鉴权功能的，现在可以补全了：\n1@audible.route(\u0026#39;/register/\u0026#39;, methods=[\u0026#39;GET\u0026#39;]) 2@mjst_checker(51, 55) 3def register(mjst): 4 \u0026#34;\u0026#34;\u0026#34; 获取注册数据 5 \u0026#34;\u0026#34;\u0026#34; 6 if mjst.error: 7 return responseto(data=mjst) 8 return _response_register_or_active(LogRegister) 9 10 11@audible.route(\u0026#39;/active/\u0026#39;, methods=[\u0026#39;GET\u0026#39;]) 12@mjst_checker(51, 55) 13def active(mjst): 14 \u0026#34;\u0026#34;\u0026#34; 获取活跃数据 15 \u0026#34;\u0026#34;\u0026#34; 16 if mjst.error: 17 return responseto(data=mjst) 18 return _response_register_or_active(LogActive) 如果 mjst 解析失败，得到的响应是一个 http code 200，但返回的 json 中的 code 值为 403。responseto 的具体定义见 从 Flask 到 Gin —— 处理 JSON。\n实现 MjstChecker 中间件 Gin 为我们提供了中间件支持，让我们可以在请求收到以及响应之间做许多事。看看 MjstChecker 这个中间件的定义：\n1package middlewares 2 3import ( 4\t\u0026#34;net/http\u0026#34; 5\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 6) 7// MjstChecker 检查 mjst 的权限是否匹配 8// typeids 支持的 usertype 9func MjstChecker(typeids []int) gin.HandlerFunc { 10\treturn func(c *gin.Context) { 11 // getMjst 从请求中获取 mjst 的值 12\tmjstObj := getMjst(typeids, c) 13\tif mjstObj == nil { 14\tc.AbortWithStatusJSON(http.StatusOK, gin.H{ 15\t\u0026#34;error\u0026#34;: true, 16\t\u0026#34;code\u0026#34;: 403, 17\t\u0026#34;message\u0026#34;: \u0026#34;MJST DECODE ERROR!\u0026#34;, 18\t}) 19\treturn 20\t} 21\tc.Set(\u0026#34;mjst\u0026#34;, mjstObj) 22\tc.Next() 23\t} 24} getMjst 在注释中已经说明了用途，也就不提供具体定义了。和 Python 版本不同的是，这里把获取 mjst 请求参数以及解密 token 写到了一个方法中。如果解析失败的话，会直接返回一个 code 403 的 JSON 响应。\nMjstChecker 的用法 在 从 Flask 到 Gin —— SQLAlchemy 和 gorm 中，我们定义了初始化路由的方法 InitAudible。现在进行一点点修改，将 MjstChecker 这个中间件串联到路由方法前面，增加鉴权功能：\n1package routers 2 3import ( 4\t\u0026#34;mjp/middlewares\u0026#34; 5\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 6) 7// InitAudible register the audible api 8func InitAudible(router *gin.RouterGroup) { 9 router.GET(\u0026#34;/register\u0026#34;, middlewares.MjstChecker([]int{51, 55}), AudibleRegister) 10\trouter.GET(\u0026#34;/active\u0026#34;, middlewares.MjstChecker([]int{51, 55}), AudibleActive) 11} 如果鉴权失败，客户端收到的结果为：\n1{ 2 \u0026#34;code\u0026#34;: 403, 3 \u0026#34;error\u0026#34;: true, 4 \u0026#34;message\u0026#34;: \u0026#34;MJST DECODE ERROR!\u0026#34; 5} 参考 从 Flask 到 Gin —— 处理 JSON 从 Flask 到 Gin —— SQLAlchemy 和 gorm PEP 318 -- Decorators for Functions and Methods Custom Middleware Go Web轻量级框架Gin学习系列：中间件使用详解 阅读系列所有文章：从 Flask 到 Gin。\n全文完 ","date":"2020-01-11","description":"","lastmod":"2020-01-11T03:14:07Z","slug":"flask-to-gin-decorator-and-middleware","tags":["fromto","flask","golang","python","gin","mjp"],"title":"从 Flask 到 Gin —— 装饰器和中间件","url":"https://blog.zengrong.net/post/flask-to-gin-decorator-and-middleware/"},{"categories":["technology"],"content":"本文是 从 Flask 到 Gin 系列的第 4 篇。\n本篇讲解在 Flask 和 Gin 中使用 MySQL 数据库的相关问题。在 Flask 中，我使用的是 Python 世界中最强大的 ORM 库： SQLAlchemy。在 Gin 的实现中，我选择了 gorm。\nGolang 中的 ORM 也是百花齐放的状态，如果你不喜欢 gorm ，也可以参考 awsome-go 来选择。\nSQLAlchemy 的表定义 我博客中写过几篇关于 SQLAlchemy 的文章。在 Flask 的项目中，我使用的是 FlaskSQLAlchemy 这个插件，它对 SQLAlchemy 进行了简单的封装。\n在我的项目中，连接了两个数据库：\ndata1 数据库包含 active 表 data2 数据库包含 register 表 下方的代码展示了这两个表在 SQLAlchemy 中的定义：\n1# -*- coding: utf-8 -*- 2\u0026#34;\u0026#34;\u0026#34; 3app.models.audible 4~~~~~~~~~~~~~~~~~~~ 5 6data1.active 7data2.register 8\u0026#34;\u0026#34;\u0026#34; 9 10# db 就是 flask_sqlalchemy.SQLAlchemy 的实例 11from mjp.app import db 12 13 14class LogActive(db.Model): 15 \u0026#34;\u0026#34;\u0026#34; Log 中的活跃数据 16 \u0026#34;\u0026#34;\u0026#34; 17 __tablename__ = \u0026#39;active\u0026#39; 18 __bind_key__ = \u0026#39;data1\u0026#39; 19 20 # 就是 regional 21 gid = db.Column(db.INT, primary_key=True, index=True) 22 23 # 代表日期 24 date = db.Column(db.INT, primary_key=True, index=True) 25 26 # 渠道 ID 27 channel = db.Column(db.INT, primary_key=True, index=True) 28 29 # 数量 30 num = db.Column(db.INT, nullable=True) 31 32 33class LogRegister(db.Model): 34 \u0026#34;\u0026#34;\u0026#34; Log 中的注册数据 35 \u0026#34;\u0026#34;\u0026#34; 36 __tablename__ = \u0026#39;register\u0026#39; 37 __bind_key__ = \u0026#39;data2\u0026#39; 38 39 # 就是 regional 40 gid = db.Column(db.INT, primary_key=True, index=True) 41 42 # 代表日期 43 date = db.Column(db.INT, primary_key=True, index=True) 44 45 # 渠道 ID 46 channel = db.Column(db.INT, primary_key=True, index=True) 47 48 # 数量 49 num = db.Column(db.INT, nullable=True) 在 SQLAlchemy 中，可以很灵活的使用参数语法来制定表字段的定义。也可以使用 __tablename__ 来指定表名，使用 __bind_key__ 来指定这个表对应的数据库。\n上面两个表的结构是一致的，它们的内容如下所示：\n对应的多数据库配置，默认的数据库指向 data1。这意味着如果不在 class 定义中指定 __bind_key__，则会认为这个表是位于 data1：\n1{ 2 \u0026#34;SECRET_KEY\u0026#34;: \u0026#34;YutjgVSPDERGyPayXrXbwsuF_SZWiVmUw3mD4YYD_kY=\u0026#34;, 3 \u0026#34;SQLALCHEMY_DATABASE_URI\u0026#34;: \u0026#34;mysql+pymysql://zrong:123456@127.0.0.1/data1\u0026#34;, 4 \u0026#34;SQLALCHEMY_BINDS\u0026#34;: { 5 \u0026#34;data1\u0026#34;: \u0026#34;mysql+pymysql://zrong:123456@127.0.0.1/data1\u0026#34;, 6 \u0026#34;data2\u0026#34;: \u0026#34;mysql+pymysql://zrong:123456@127.0.0.1/data2\u0026#34; 7 } 8} SQLAlchemy 中的多数据库查询 由于 data1.active 和 data2.register 这两个表的结构是一样的，我将其封装成同一个方法： _response_register_or_active，通过传入不同的表定义来实现查询。下面代码中的 responseto 方法的定义已经在 从 Flask 到 Gin —— 处理 JSON 一文中介绍过了。\n1def _parse_date(datestr): 2 if datestr is None: 3 return None 4 return int(dt.strftime(\u0026#39;%Y%m%d\u0026#39;)) 5 6 7def _response_register_or_active(DataTable): 8 \u0026#34;\u0026#34;\u0026#34; 提供一个数据表作为参数，数据表的字段名称必须相同 9 返回一个响应对象 10 :param DataTable: 数据表 Class 11 12 :arg from_date: 起始日期 13 :arg to_date: 终止日期 14 \u0026#34;\u0026#34;\u0026#34; 15 # get_request_values 是一个获取查询的封装，等同于在 request.args 中查询，很容易实现，在此就不列出定义了 16 gids, from_date, to_date = get_request_values(\u0026#39;gids\u0026#39;, \u0026#39;from_date\u0026#39;, \u0026#39;to_date\u0026#39;, request_key=\u0026#39;args\u0026#39;) 17 from_date = _parse_date(from_date) 18 to_date = _parse_date(to_date) 19 if from_date is None or to_date is None or gids is None: 20 return responseto(\u0026#39;请提供 from_date/to_date/gids!\u0026#39;, code=401) 21 try: 22 gids = json.loads(gids) 23 if not isinstance(gids, list) or len(gids) == 0: 24 return responseto(\u0026#39;gids 必须是一个列表！\u0026#39;, code=401) 25 except Exception as e: 26 return responseto(\u0026#39;gids 解析错误！\u0026#39;, code=401) 27 28 gids = [item.r for item in rall] 29 results = DataTable.query.\\ 30 filter(DataTable.gid.in_(gids)).\\ 31 filter(and_(DataTable.date \u0026gt;= from_date, DataTable.date \u0026lt;= to_date)).\\ 32 with_entities(DataTable.gid, DataTable.date, func.sum(DataTable.num).label(\u0026#39;num\u0026#39;)).\\ 33 group_by(DataTable.gid, DataTable.date).\\ 34 all() 35 stat = [{ 36 \u0026#39;gid\u0026#39;: item.gid, 37 \u0026#39;num\u0026#39;: int(item.num), 38 \u0026#39;date\u0026#39;: item.date 39 } for item in results] 40 return responseto(stat=stat, gids=gids) 定义两个不同的路由，直接调用上面的 _response_register_or_active 就可以实现根据提供的日期分组返回 MySQL 中的数据：\n1@audible.route(\u0026#39;/register/\u0026#39;, methods=[\u0026#39;GET\u0026#39;]) 2def register(): 3 \u0026#34;\u0026#34;\u0026#34; 获取注册数据 4 \u0026#34;\u0026#34;\u0026#34; 5 return _response_register_or_active(LogRegister) 6 7 8@audible.route(\u0026#39;/active/\u0026#39;, methods=[\u0026#39;GET\u0026#39;]) 9def active(): 10 \u0026#34;\u0026#34;\u0026#34; 获取活跃数据 11 \u0026#34;\u0026#34;\u0026#34; 12 return _response_register_or_active(LogActive) 在对 LogRegister 和 LogActive 这两个表进行定义的时候，我们已经通过 __bind_key__ 指定了数据库，执行查询的时候，SQLAlchemy 会自行切换数据库进行查询。\n让我们来测试一下：\n1curl --request GET \\ 2 --url \u0026#39;http://127.0.0.1:5001/audible/register/?from_date=20180801\u0026amp;to_date=20180802\u0026amp;gids=%5B1%2C53%5D\u0026#39; 结果为：\n1{ 2 \u0026#34;code\u0026#34;: 200, 3 \u0026#34;error\u0026#34;: false, 4 \u0026#34;gids\u0026#34;: [ 5 1, 6 53 7 ], 8 \u0026#34;stat\u0026#34;: [ 9 { 10 \u0026#34;date\u0026#34;: 20180801, 11 \u0026#34;gid\u0026#34;: 1, 12 \u0026#34;num\u0026#34;: 819 13 }, 14 { 15 \u0026#34;date\u0026#34;: 20180802, 16 \u0026#34;gid\u0026#34;: 1, 17 \u0026#34;num\u0026#34;: 840 18 }, 19 { 20 \u0026#34;date\u0026#34;: 20180801, 21 \u0026#34;gid\u0026#34;: 53, 22 \u0026#34;num\u0026#34;: 680 23 }, 24 { 25 \u0026#34;date\u0026#34;: 20180802, 26 \u0026#34;gid\u0026#34;: 53, 27 \u0026#34;num\u0026#34;: 624 28 } 29 ] 30} gorm 的表定义 active 和 register 表的定义如下：\n1package models 2 3import ( 4\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 5) 6 7// ActiveModel is a table for active users 8type ActiveModel struct { 9\tGid int `gorm:\u0026#34;PRIMARY_KEY,AUTO_INCREMENT\u0026#34;` 10\tDate int `gorm:\u0026#34;PRIMARY_KEY,INDEX\u0026#34;` 11\tChannel int `gorm:\u0026#34;PRIMARY_KEY,INDEX\u0026#34;` 12\tNum int `gorm:\u0026#34;NOT NULL\u0026#34;` 13} 14 15// TableName for ActiveModel 16func (ActiveModel) TableName() string { 17\treturn \u0026#34;active\u0026#34; 18} 19 20// RegisterModel is a table for registered users 21type RegisterModel struct { 22\tGid int `gorm:\u0026#34;PRIMARY_KEY,AUTO_INCREMENT\u0026#34;` 23\tDate int `gorm:\u0026#34;PRIMARY_KEY,INDEX\u0026#34;` 24\tChannel int `gorm:\u0026#34;PRIMARY_KEY,INDEX\u0026#34;` 25\tNum int `gorm:\u0026#34;NOT NULL\u0026#34;` 26} 27 28// TableName for RegisterModel 29func (RegisterModel) TableName() string { 30\treturn \u0026#34;register\u0026#34; 31} 需要注意的是，在 gorm 中需要使用 Struct Tags 来定义字段属性，使用 TableName 方法来定义表的名称。\n文档 gorm models 定义 中解释得不够清晰，几条常用的我总结一下：\n使用 column:列名 这样的语法来精确指定列名。 使用 type:SQL 类型 这样的语法来精确指定列字段类型。 Struct Tags 中具体的定义之间可以使用分号或者逗号隔开。 Struct Tags 中不区分大小写。 下面是一个混合的范例，很清晰，不解释了：\n1// RegionalModel is a table for regional 2type RegionalModel struct { 3\tR int `gorm:\u0026#34;type:smallint;PRIMARY_KEY,AUTO_INCREMENT\u0026#34;` 4\tName string `gorm:\u0026#34;type:varchar(100);NOT NULL,INDEX\u0026#34;` 5\tValue string `gorm:\u0026#34;type:text;INDEX\u0026#34;` 6\tStatus int `gorm:\u0026#34;type:smallint;NOT NULL\u0026#34;` 7\tCreateTime *time.Time `gorm:\u0026#34;type:smallint;column:createtime\u0026#34;` 8\tUpdateTime *time.Time `gorm:\u0026#34;type:smallint;column:updatetime\u0026#34;` 9} 上面列出的 ActiveModel 和 RegisterModel 是具体的数据表定义，要将数据库中查询到的数据进行 JSON 序列化，还需要一些代码配合。请参考：从 Flask 到 Gin —— 处理 JSON -\u0026gt; 序列化。\ngorm 中的多数据库初始化 在 database.go 中初始化多个数据库，以供使用：\n1package models 2 3import ( 4\t\u0026#34;fmt\u0026#34; 5\t\u0026#34;mjp/util\u0026#34; 6 7\t\u0026#34;github.com/jinzhu/gorm\u0026#34; 8\t_ \u0026#34;github.com/jinzhu/gorm/dialects/mysql\u0026#34; // mysql drive 9) 10 11// DefaultDB is a global object for MySQL call. 12var DefaultDB *gorm.DB 13 14// Data1DB is a global object for MySQL bind with name \u0026#34;data1\u0026#34; 15var Data1DB *gorm.DB 16 17// Data2DB is a global object for MySQL bind with name \u0026#34;data2\u0026#34; 18var Data2DB *gorm.DB 19 20func init() { 21\tconf := util.ConfigInstance() 22\tDefaultDB = initDB(conf.Databases.URI) 23\tData1DB = initDB(conf.DatabaseBind(\u0026#34;data1\u0026#34;)) 24\tData2DB = initDB(conf.DatabaseBind(\u0026#34;data2\u0026#34;)) 25\t// defer DefaultDB.Close() 26} 27 28func initDB(uri string) *gorm.DB { 29\tdb, err := gorm.Open(\u0026#34;mysql\u0026#34;, uri) 30\tif err != nil { 31\terrmsg := fmt.Errorf(\u0026#34;Connect %s error: %s\u0026#34;, uri, err) 32\tpanic(errmsg) 33\t} 34\tdb.DB().SetMaxIdleConns(10) 35\tdb.LogMode(true) 36\tdb.SingularTable(true) 37\treturn db 38} 要了解 util.ConfigInstance 的具体实现，请参考：Flask 到 Gin —— 读取配置文件。\ngorm 中的多数据库查询 和 SQLAlchemy 类似，由于 data1.active 和 data2.register 这两个表的结构是一样的，我将其封装成同一个方法： registerOrActive，通过传入不同的表定义来实现查询。下面代码中的 re2q.Responseto 方法的定义已经在 从 Flask 到 Gin —— 处理 JSON 一文中介绍过了。\n1package routers 2 3import ( 4\t\u0026#34;fmt\u0026#34; 5\t\u0026#34;mjp/models\u0026#34; 6\t\u0026#34;mjp/re2q\u0026#34; 7\t\u0026#34;mjp/util\u0026#34; 8\t\u0026#34;strconv\u0026#34; 9\t\u0026#34;strings\u0026#34; 10 11\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 12\t\u0026#34;github.com/jinzhu/gorm\u0026#34; 13) 14 15// 返回一个处理过的 gorm 查询，以便进一步处理 16func registerOrActive(c *gin.Context, DataTable *gorm.DB) (defaultDBQry *gorm.DB) { 17\tgids, gok := c.GetQuery(\u0026#34;gids\u0026#34;) 18\tvar gidsInt []int 19\tif gok { 20\tgidsString := strings.Split(gids, \u0026#34;,\u0026#34;) 21\tgidsInt = make([]int, len(gidsString)) 22\tfor i, value := range gidsString { 23\tgidInt, gidErr := strconv.Atoi(value) 24\tif gidErr != nil { 25\tre2q.Responseto(c, fmt.Sprintf(\u0026#34;Convert gid %s got a error!\u0026#34;, value), 401) 26\treturn 27\t} 28\tgidsInt[i] = gidInt 29\t} 30\t} 31 32\tfromDate, fromDateErr := strconv.Atoi(c.Query(\u0026#34;from_date\u0026#34;)) 33\ttoDate, toDateErr := strconv.Atoi(c.Query(\u0026#34;to_date\u0026#34;)) 34\tif fromDateErr != nil || toDateErr != nil { 35\tre2q.Responseto(c, \u0026#34;Give from_date and to_date please!\u0026#34;, 401) 36\treturn 37\t} 38\t// gids 必须存在 39\tif gidsInt == nil { 40\tre2q.Responseto(c, \u0026#34;gids please!\u0026#34;, 401) 41\treturn 42\t} 43 44\tdefaultDBQry = DataTable.Where(\u0026#34;date BETWEEN ? AND ?\u0026#34;, fromDate, toDate).Where(\u0026#34;gid IN (?)\u0026#34;, gidsInt) 45\treturn 46} 和 Flask 中类似，定义两个不同的路由，直接调用上面的 registerOrActive 就可以实现根据提供的日期分组返回 MySQL 中的数据：\n1package routers 2 3import ( 4\t\u0026#34;fmt\u0026#34; 5\t\u0026#34;mjp/models\u0026#34; 6\t\u0026#34;mjp/re2q\u0026#34; 7\t\u0026#34;mjp/util\u0026#34; 8\t\u0026#34;strconv\u0026#34; 9\t\u0026#34;strings\u0026#34; 10 11\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 12\t\u0026#34;github.com/jinzhu/gorm\u0026#34; 13) 14 15// InitAudible register the audible api 16func InitAudible(router *gin.RouterGroup) { 17\trouter.GET(\u0026#34;/register\u0026#34;, AudibleRegister) 18\trouter.GET(\u0026#34;/active\u0026#34;, AudibleActive) 19} 20 21// AudibleActive return active users list 22func AudibleActive(c *gin.Context) { 23 // 使用 Data1 数据库 24\tdefaultDBQry := registerOrActive(c, models.Data1DB) 25 // 查找活跃数据 26 actives := []models.ActiveModel{} 27 findError := defaultDBQry.Find(\u0026amp;actives).Error 28 if findError != nil { 29 re2q.Responseto(c, findError.Error(), 503) 30 return 31 } 32 activesSerializer := models.ActivesSerializer{c, actives} 33 re2q.ResponsetoWithData(c, gin.H{ 34 \u0026#34;stat\u0026#34;: activesSerializer.Response(), 35 \u0026#34;code\u0026#34;: 200, 36 }) 37} 38 39// AudibleRegister return registred user list 40func AudibleRegister(c *gin.Context) { 41 // 使用 Data2 数据库 42\tdefaultDBQry := registerOrActive(c, models.Data2DB) 43 // 查找注册数据 44 registers := []models.RegisterModel{} 45 findError := defaultDBQry.Find(\u0026amp;registers).Error 46 if findError != nil { 47 re2q.Responseto(c, findError.Error(), 503) 48 return 49 } 50 registersSerializer := models.RegistersSerializer{c, registers} 51 re2q.ResponsetoWithData(c, gin.H{ 52 \u0026#34;stat\u0026#34;: registersSerializer.Response(), 53 \u0026#34;code\u0026#34;: 200, 54 }) 55} 这里的测试效果和 Python 版本完全相同。\n参考 Object-relational mapping The Python SQL Toolkit and Object Relational Mapper The fantastic ORM library for Golang Flask-SQLAlchemy 从 Flask 到 Gin —— 处理 JSON 阅读系列所有文章：从 Flask 到 Gin。\n全文完 ","date":"2020-01-05","description":"","lastmod":"2020-01-05T03:24:14Z","slug":"flask-to-gin-sqlalchemy-gorm","tags":["fromto","flask","golang","python","gin","mjp","sqlalchemy"],"title":"从 Flask 到 Gin —— SQLAlchemy 和 gorm","url":"https://blog.zengrong.net/post/flask-to-gin-sqlalchemy-gorm/"},{"categories":["technology"],"content":"本文是 从 Flask 到 Gin 系列的第 3 篇。\n配置文件是一个项目不可或缺的内容。在 MJP 的 Flask 版本中，我采用 JSON 格式的配置文件。在 Gin 的实现中，我决定保持这种格式不变。下面是一个简化了的配置文件内容。\n配置文件范例 1{ 2 \u0026#34;GIN_MODE\u0026#34;: \u0026#34;debug\u0026#34;, 3 \u0026#34;PATH\u0026#34;: \u0026#34;\u0026#34;, 4 \u0026#34;ADDRESS\u0026#34;: \u0026#34;127.0.0.1:5005\u0026#34;, 5 \u0026#34;SECRET_KEY\u0026#34;: \u0026#34;YutjgVSPDERGyPayXrXbwsuF_SZWiVmUw3mD4YYD_kY=\u0026#34;, 6 \u0026#34;DATABASES\u0026#34;: { 7 \u0026#34;DATABASE_URI\u0026#34;: \u0026#34;zrong:123456@(127.0.0.1)/data1?charset=utf8mb4\u0026amp;parseTime=True\u0026amp;loc=Local\u0026#34;, 8 \u0026#34;DATABASE_BINDS\u0026#34;: { 9 \u0026#34;data1\u0026#34;: \u0026#34;zrong:123456@(127.0.0.1)/data1?charset=utf8mb4\u0026amp;parseTime=True\u0026amp;loc=Local\u0026#34;, 10 \u0026#34;data2\u0026#34;: \u0026#34;zrong:123456@(127.0.0.1)/data2?charset=utf8mb4\u0026amp;parseTime=True\u0026amp;loc=Local\u0026#34; 11 } 12 }, 13 \u0026#34;REGIONALS\u0026#34;: [ 14 { 15 \u0026#34;name\u0026#34;: \u0026#34;测试服1001\u0026#34;, 16 \u0026#34;r\u0026#34;: 1001, 17 \u0026#34;bind_key_db\u0026#34;: \u0026#34;mjptest\u0026#34; 18 }, 19 { 20 \u0026#34;name\u0026#34;: \u0026#34;测试服1023\u0026#34;, 21 \u0026#34;r\u0026#34;: 1023, 22 \u0026#34;bind_key_db\u0026#34;: \u0026#34;mjptest\u0026#34; 23 } 24 ] 25} 上面的配置文件命名为 config.json，保存在项目根目录下。\n配置模块封装：config.py 下面是 Flask 项目中，对 config.json 这个配置文件进行处理的模块。模块名称是 config.py。这个模块的主要功能有下面几个：\n提供一个 getdir 模块方法，用于返回当前文件夹下相对路径的 Path 对象。 解析 config.json，将其保存到模块全局变量中，便于随时使用。这里使用标准库的 json 包完成，并封装了一个 readjson 方法。 封装了一个 getcfg 模块方法，从配置文件中获取变量。 封装了一个 getregional 模块方法，根据 REGIONAL 中的 r 值，获取这个 r 对应的配置。 1# -*- coding: utf-8 -*- 2\u0026#34;\u0026#34;\u0026#34; 3config.py 4~~~~~~~~~~~~~~~~~~~ 5 6初始化 app.config 中的配置 7解析 config.json 配置文件 8提供配置文件相关的读取和写入方法 9\u0026#34;\u0026#34;\u0026#34; 10 11import os 12from pathlib import Path 13import json 14 15 16# getdir 使用 17__basedir = None 18 19# 全局变量，用于保存 config.json 载入的配置 20cfg_json = None 21 22regional_list = None 23regional_dict = {} 24regional_ids = [] 25 26 27def readjson(filename, basedir=None, throw_error=False): 28 \u0026#34;\u0026#34;\u0026#34; 读取一个 json 格式的配置文件 29 30 :param filename: 文件名 31 :param basedir: str 32 :param throw_error: boolean 若值为 True，则当文件不存在的时候抛出异常 33 :returns: 解析后的 dict 34 :rtype: dict 35 \u0026#34;\u0026#34;\u0026#34; 36 jsonf = getdir(filename, basedir=basedir) 37 if jsonf.exists(): 38 return json.loads(jsonf.read_text(encoding=\u0026#39;utf-8\u0026#39;), encoding=\u0026#39;utf-8\u0026#39;) 39 if throw_error: 40 raise FileNotFoundError(\u0026#39;%s is not found!\u0026#39; % jsonf.resolve()) 41 return {} 42 43 44def writejson(data_dict, filename, basedir=None): 45 \u0026#34;\u0026#34;\u0026#34; 将一个 dict 写入成为 json 文件 46 47 :param data_dict: 要写入的配置信息 48 :type data_dict: dict 49 \u0026#34;\u0026#34;\u0026#34; 50 jsonf = getdir(filename, basedir=basedir) 51 jsonf.write_text(json.dumps(data_dict, ensure_ascii=False, indent=2)) 52 53 54def getdir(*args, basedir=None): 55 \u0026#34;\u0026#34;\u0026#34; 基于当前项目的运行文件夹，返回一个 pathlib.Path 对象 56 如果传递 basedir，就基于这个 basedir 创建路径 57 \u0026#34;\u0026#34;\u0026#34; 58 if basedir is not None: 59 return Path(basedir, *args) 60 if __basedir is None: 61 raise ValueError(\u0026#39;please set basedir first!\u0026#39;) 62 return Path(__basedir, *args) 63 64 65def getcfg(*args, default_value=None, data=\u0026#39;cfg_json\u0026#39;): 66 \u0026#34;\u0026#34;\u0026#34; 67 递归获取 dict 中的值 68 如果不提供 data，默认使用 cfg 中的值 69 注意，getcfg 不仅可用于读取 config.yaml 的值，还可以通过传递 data 用于读取任何字典的值 70 :param args: 71 :param data: 72 :return: 73 \u0026#34;\u0026#34;\u0026#34; 74 if data is None: 75 return None 76 elif data == \u0026#39;cfg_json\u0026#39;: 77 data = cfg_json 78 if args: 79 if isinstance(data, dict): 80 return getcfg(*args[1:], data=data.get(args[0], default_value)) 81 return data 82 return data 83 84 85def init_regionals(): 86 global regional_list 87 # 从配置文件中读取 regional 的配置，存储到一个 list 和一个 dict 中 88 regional_list = getcfg(\u0026#39;REGIONALS\u0026#39;) 89 if not isinstance(regional_list, list) or len(regional_list) == 0: 90 raise ValueError(\u0026#39;REGIONAL is unavailable!\u0026#39;) 91 for regional in regional_list: 92 r = regional.get(\u0026#39;r\u0026#39;) 93 if r is None: 94 raise KeyError(\u0026#39;REGIONALS 配置必须包含 r key!\u0026#39;) 95 regional_ids.append(r) 96 regional_dict[r] = regional 97 98 99def getregional(r): 100 return regional_dict.get(r) 101 102 103def init(basedir=None, initr=True): 104 \u0026#34;\u0026#34;\u0026#34; 初始化配置文件 105 :param initr: 是否初始化配置文件中的 initregionals 106 \u0026#34;\u0026#34;\u0026#34; 107 global __basedir, cfg_json 108 if basedir is None: 109 __basedir = os.getcwd() 110 else: 111 __basedir = basedir 112 cfg_json = readjson(\u0026#39;config.json\u0026#39;) 113 if initr: 114 init_regionals() 使用方法很简单，导入全局模块 config，调用其封装的方法即可：\n1from mjp import config 2 3address = config.getcfg(\u0026#39;ADDRESS\u0026#39;) 显然，由于 Python 的动态特性，将 config.json 解析后变成一个 dict，然后处理它，是一件很灵活和轻松的事情。但 Golang 是静态语言，处理起来并没有这么方便。这里我们可以复习一下 从 Flask 到 Gin —— 处理 JSON，config.json 需要映射成 Struct 才更容易使用。\n接下来看看怎样在 Golang 中实现和 config.py 模块类似的效果。\n使用 viper 读取配置 第一次看到 viper，是因为 Hugo。去年 8 月，我把博客从 Hexo 转到了 Hugo，接着坚定了学习 Golang 的念头。Hugo 使用 viper 也很正常，因为 viper 的作者 spf13 也是 Hugo 的主要作者之一。\n我刚使用 Hugo 的时候，就因为它同时支持多种配置文件格式 config.yaml/config.toml/config.json 而感到惊讶（当然，uWSGI 也是支持多种配置文件格式的，所以我可能也并没有那么惊讶😄），我也是在这里第一次接触到了 TOML 格式。\nviper 的优势很多（via）：\n为各种配置项设置默认值 加载并解析JSON、TOML、YAML 或 Java properties 格式的配置文件 可以监视配置文件的变动、重新读取配置文件 从环境变量中读取配置数据 从远端配置系统中读取数据，并监视它们 从命令参数中读取配置 从 buffer 中读取 调用函数设置配置信息 因为这些优势，我选择了 viper 作为配置文件的读取和解析库。\n配置模块封装：config.go 下面是 Gin 项目中，对 config.json 这个配置文件进行处理的模块。模块名称是 config.go。这个模块的主要功能有下面几个：\n提供一个 GetDir 函数，用于返回当前文件夹下的相对路径。 解析 config.json，将其内容映射到 Config 这个 Struct 中，便于随时使用。 封装了 DatabaseBind 函数，从配置文件中获取数据库绑定情况。 封装了一个 Regional 函数，根据 REGIONAL 中的 r 值，获取这个 r 对应的 Regional Struct。 1package util 2 3import ( 4\t\u0026#34;encoding/json\u0026#34; 5\t\u0026#34;fmt\u0026#34; 6\t\u0026#34;os\u0026#34; 7\t\u0026#34;path\u0026#34; 8\t\u0026#34;path/filepath\u0026#34; 9\t\u0026#34;sync\u0026#34; 10 11\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 12\t\u0026#34;github.com/spf13/viper\u0026#34; 13) 14 15// Databases 定义配置中的 DATABASES 字段 16type Databases struct { 17\tURI string `mapstructure:\u0026#34;DATABASE_URI\u0026#34;` 18\tBinds map[string]string `mapstructure:\u0026#34;DATABASE_BINDS\u0026#34;` 19} 20 21// Regional 定义一个区服的配置 22type Regional struct { 23\tName string `mapstructure:\u0026#34;name\u0026#34;` 24\tR int `mapstructure:\u0026#34;r\u0026#34;` 25\tBindKeyDb string `mapstructure:\u0026#34;bind_key_db\u0026#34;` 26} 27 28// String return a string about regional 29func (regional *Regional) String() string { 30\treturn regional.Name 31} 32 33/* 34Config parse form config.json 35*/ 36type Config struct { 37\tGinMode string `mapstructure:\u0026#34;GIN_MODE\u0026#34;` 38\tPath string `mapstructure:\u0026#34;PATH\u0026#34;` 39\tAddress string `mapstructure:\u0026#34;ADDRESS\u0026#34;` 40\tSecretKey string `mapstructure:\u0026#34;SECRET_KEY\u0026#34;` 41\tDatabases *Databases `mapstructure:\u0026#34;DATABASES\u0026#34;` 42\tRegionals []*Regional `mapstructure:\u0026#34;REGIONALS\u0026#34;` 43} 44 45// String return the config as string 46func (conf *Config) String() string { 47\tc := viper.AllSettings() 48\tbs, err := json.Marshal(c) 49\tif err != nil { 50\tfmt.Println(err) 51\t} 52\treturn string(bs) 53} 54 55// Regional return a regional in REGIONALS 56func (conf *Config) Regional(r int) *Regional { 57\treturn regionals[r] 58} 59 60// DatabaseBind return a database_uri in DATABASES 61func (conf *Config) DatabaseBind(bindKey string) string { 62\treturn conf.Databases.Binds[bindKey] 63} 64 65var regionals map[int]*Regional 66var conf *Config 67var configOnce sync.Once 68 69// load the config.json and return it 70func load(content string) (*Config, error) { 71\tconfig := \u0026amp;Config{} 72 73\tvar err error 74 75\tviper.SetConfigFile(content) 76\terr = viper.ReadInConfig() 77\tif err != nil { 78\treturn nil, err 79\t} 80 81\terr = viper.Unmarshal(\u0026amp;config) 82\tif err != nil { 83\treturn nil, err 84\t} 85\tregionals = make(map[int]*Regional) 86\tfor _, value := range config.Regionals { 87\tregionals[value.R] = value 88\t} 89 90\treturn config, nil 91} 92 93// ConfigInstance is a signal Config 94func ConfigInstance() *Config { 95\tconfigOnce.Do(func() { 96\tconfigFile := GetDir(\u0026#34;config.json\u0026#34;) 97\tinst, err := load(configFile) 98\tif err != nil { 99\tpanic(err) 100\t} 101\tconf = inst 102\tif conf.GinMode != \u0026#34;\u0026#34; { 103\tgin.SetMode(conf.GinMode) 104\t} 105\t}) 106\treturn conf 107} 108 109// GetDir return a path in pwd 110func GetDir(elem ...string) string { 111\tcurPath, _ := filepath.Abs(filepath.Dir(os.Args[0])) 112\targs := append([]string{curPath}, elem...) 113\treturn path.Join(args...) 114} 上面的处理和 Python 有很大的不同。在 config.py 中，所有读入内存的配置文件数据作为一个 dict 存在，要获取其中的值，只需要使用 dict.get 并提供值的名称即可。但是在 config.go 中，我们必须把所有的值以 Struct 的形式进行预先定义和绑定。\n看看怎么使用 config.go：\n1package middlewares 2 3import ( 4\t\u0026#34;mjp/util\u0026#34; 5\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 6) 7var config *util.Config 8var logger *util.Logger 9 10func init() { 11 config = util.ConfigInstance() 12 logger = util.GetAppLogger() 13} 14 15func printAddress() { 16 logger.Infof(\u0026#34;Addrss: %s\u0026#34;, config.Address) 17} 参考 在 Go 中使用 Viper 加载配置文件 阅读系列所有文章：从 Flask 到 Gin。\n全文完 ","date":"2020-01-01","description":"","lastmod":"2020-01-01T12:59:52Z","slug":"flask-to-gin-read-config-file","tags":["fromto","flask","golang","python","gin","mjp"],"title":"从 Flask 到 Gin —— 读取配置文件","url":"https://blog.zengrong.net/post/flask-to-gin-read-config-file/"},{"categories":["technology"],"content":"本文是 从 Flask 到 Gin 系列的第 2 篇。\n在 MJP 项目中，我使用的是 Python 标准库中的 logging 模块。在 Flask 项目启动的时候，创建一个全局的 logger 对象，对其进行基本的设置。\n在下面的代码中，_set_logger 被 create_app 调用，初始化整个 MJP 系统的全局 logger 对象。这个 logger 对象与 Flask 中的 app.logger 是等同的。\nFlask 中的全局 logger 实现 1# package mjp.app 2 3import logging 4from mjp import config 5 6# 就是 flas.app.logger https://flask.palletsprojects.com/en/1.1.x/logging/，放在这里不必引用 current_app 7logger = logging.getLogger(__name__) 8 9def _set_logger(mjpapp): 10 \u0026#34;\u0026#34;\u0026#34; 11 设置 Flask app 的logger 12 \u0026#34;\u0026#34;\u0026#34; 13 # logger = mjpapp.logger 14 # 删除 Flask 的默认 Handler 15 del logger.handlers[:] 16 if mjpapp.config.get(\u0026#39;DEBUG\u0026#39;): 17 hdr = logging.StreamHandler() 18 hdr.setLevel(logging.DEBUG) 19 logger.setLevel(logging.DEBUG) 20 else: 21 # logsdir 是一个 Path 实例 22 logsdir = config.getdir(\u0026#39;logs\u0026#39;) 23 # 创建或者设置 logs 文件夹的权限，让其他 user 也可以写入（例如nginx） 24 # 注意，要设置 777 权限，需要使用 0o40777 或者先设置 os.umask(0) 25 # 0o40777 是根据 os.stat() 获取到的 st_mode 得到的 26 if logsdir.exists(): 27 logsdir.chmod(0o40777) 28 else: 29 logsdir.mkdir(mode=0o40777) 30 applog = logsdir.joinpath(\u0026#39;app.log\u0026#39;) 31 if not applog.exists(): 32 applog.touch() 33 # python 3.6 的 FileHandler 才支持 Path 实例。因此这里要做处理 34 hdr = logging.FileHandler(str(applog.resolve()), encoding=\u0026#39;utf8\u0026#39;) 35 hdr.setLevel(logging.INFO) 36 logger.setLevel(logging.INFO) 37 38 LOG_FORMAT = \u0026#34;\u0026#34;\u0026#34; 39[%(asctime)s] %(levelname)s in %(module)s.%(funcName)s [%(pathname)s:%(lineno)d]: 40%(message)s\u0026#34;\u0026#34;\u0026#34; 41 hdr.setFormatter(logging.Formatter(LOG_FORMAT)) 42 43 for log in (logger, logging.getLogger(\u0026#39;sqlalchemy\u0026#39;)): 44 log.addHandler(hdr) 45 46def create_app(FlaskClass=MJPFlask, ResponseClass=MJPResponse, ConfigClass=FlaskConfig): 47 \u0026#34;\u0026#34;\u0026#34; 48 根据不同的配置创建 app 49 \u0026#34;\u0026#34;\u0026#34; 50 mjpapp = FlaskClass(__name__, static_url_path=config.getcfg(\u0026#39;PATH\u0026#39;, \u0026#39;STATIC_URL_PATH\u0026#39;)) 51 mjpapp.response_class = ResponseClass 52 mjpapp.config.from_object(ConfigClass(config.getcfg(\u0026#39;FLASK\u0026#39;))) 53 _set_logger(mjpapp) 54 return mjpapp 要使用这个全局 logger 对象，只需要进行下面的操作就可以了：\n1 2from mjp.app import logger 3 4logger.info(\u0026#39;Answer to Life, the Universe, and Everything: %s\u0026#39;, 42) 我们看到，由于 Python 的动态特性和全局模块特性，这个使用方法是相当优雅和 Pythonic 的。\nGolang 中的 log 包选择 在 Golang 实现中，我没有选择标准库中的 log 包，而是使用了 Logrus 这个 Golang 世界中被 Star 最多的 log 库，也是 docker 这个 Golang 明星项目使用的 logger 库。至于原因，看了下面的特性就清楚了 (via)：\n完全兼容golang标准库日志模块。logrus拥有六种日志级别：debug、info、warn、error、fatal和panic，这是golang标准库日志模块的API的超集。如果你的项目使用标准库日志模块，完全可以用最低的代价迁移到logrus上。 可扩展的Hook机制。允许使用者通过hook方式，将日志分发到任意地方，如本地文件系统、标准输出、logstash、elasticsearch或者mq等，或者通过hook定义日志内容和格式等。 可选的日志输出格式。logrus内置了两种日志格式，JSONFormatter和TextFormatter。 如果这两个格式不满足需求，可以自己动手实现接口Formatter，来定义自己的日志格式。 Field机制。logrus鼓励通过Field机制进行精细化、结构化的日志记录，而不是通过冗长的消息来记录日志。 logrus是一个可插拔的、结构化的日志框架。 可能是由于 Golang 标准库中的 log 包功能太弱，加上 Golang 社区的年轻活力，导致 Golang 世界中没有出现一个类似于 Java 世界的 log4j 这样一个具有统治力的库。这导致我们有大量优秀的 log 库可供选择，较真的话可能会挑花眼。有兴(Shi)趣(Jian)可以在 awesome-go 翻一下。\nLogrus 已经帮我们处理的所有的事情，我要做的就是在项目中进行一下封装，把它做成一个单例：\n1package util 2 3import ( 4\t\u0026#34;fmt\u0026#34; 5\t\u0026#34;os\u0026#34; 6\t\u0026#34;path\u0026#34; 7\t\u0026#34;sync\u0026#34; 8 9\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 10\t\u0026#34;github.com/sirupsen/logrus\u0026#34; 11) 12 13func init() { 14\tlogsDir = GetDir(\u0026#34;logs\u0026#34;) 15\tos.Mkdir(logsDir, os.ModePerm) 16} 17 18// Logger is a global logger 19type Logger struct { 20\tfilename string 21\t*logrus.Logger 22} 23 24var logsDir string 25var appLogger *Logger 26var appLoggerOnce sync.Once 27 28// GetAppLogger will start a global logger use in app 29func GetAppLogger() *Logger { 30\tappLoggerOnce.Do(func() { 31\tappLogger = createLogger() 32\t}) 33\treturn appLogger 34} 35 36func makeFile() (*os.File, string) { 37 // 在调试状态的时候输出到 stdout 38\tif gin.IsDebugging() { 39\treturn os.Stdout, \u0026#34;os.Stdout\u0026#34; 40 } 41 // 非调试状态的时候输出的库 app.log 42\tappLogFile := path.Join(logsDir, \u0026#34;app.log\u0026#34;) 43\tfile, err := os.OpenFile(appLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 44\tif err != nil { 45\tpanic(err) 46\t} 47\treturn file, appLogFile 48} 49 50func createLogger() *Logger { 51\tfile, name := makeFile() 52\tfmt.Printf(\u0026#34;createLogger %s\\n\u0026#34;, name) 53\tlog := \u0026amp;logrus.Logger{ 54\tOut: file, 55\tFormatter: new(logrus.JSONFormatter), 56\tLevel: logrus.DebugLevel, 57\t} 58\treturn \u0026amp;Logger{name, log} 59} 在使用的时候，只需要这样处理：\n1import \u0026#34;mjp/util\u0026#34; 2 3logger = util.GetAppLogger() 4logger.Infof(\u0026#34;Answer to Life, the Universe, and Everything: %d\u0026#34;, 42) 如果要频繁使用的话，可以把 logger 对象的赋值放到 init 中：\n1package middlewares 2 3import ( 4 \u0026#34;mjp/models\u0026#34; 5\t\u0026#34;mjp/util\u0026#34; 6) 7 8var logger *util.Logger 9 10func init() { 11\tlogger = util.GetAppLogger() 12} 13 14// 使用 mjstObj 中的数据获取一个 admin 15func getAdmin(mjstObj *app.MJST) *models.AdminModel { 16\tadmin := \u0026amp;models.AdminModel{} 17\tadminFindErr := models.AdminDB.First(admin, mjstObj.Uid).Error 18\tif adminFindErr != nil { 19\tlogger.Errorf(\u0026#34;getAdmin Error %s\u0026#34;, adminFindErr) 20\treturn nil 21\t} 22\treturn admin 23} 阅读系列所有文章：从 Flask 到 Gin。\n全文完 ","date":"2019-12-31","description":"","lastmod":"2019-12-31T23:44:13Z","slug":"flask-to-gin-logging","tags":["fromto","flask","golang","python","gin","mjp"],"title":"从 Flask 到 Gin —— Logging","url":"https://blog.zengrong.net/post/flask-to-gin-logging/"},{"categories":["technology"],"content":"本文是 从 Flask 到 Gin 系列的第 1 篇。\n如果写过 C++ 或者 Java，你会觉得在 Golang 中处理 JSON 比前两者要简单很多。但作为习惯了在 Python 中偷懒的我来说，Golang 中的 JSON 用法还是挺难受的。\nMJP 是一个 RESTful API 服务，绝大多数 API 返回的都是 JSON 格式。由于 Python 的灵活性和 Flask 的良好封装，编写 MJP 服务时，我在 Flask 中使用 JSON 没有遇到什么困难。MJP 中有一个名为 responseto 的封装，我使用它来统一输出 JSON Response。本文介绍将这个方法移植到 Gin 的过程。\nresponseto 的 Python 实现 responseto 是一个 Python 中的模块方法，它的定义如下：\n1from flask import jsonify 2from mjp.app import db 3 4def responseto(message=None, error=None, code=None, data=None, replaceobj=None, replaceobj_key_only=False, **kwargs): 5 \u0026#34;\u0026#34;\u0026#34; 6 封装 json 响应 7 :param message: 错误消息，若提供则默认 error 为 True 8 :param error: 是否包含错误 9 :param code: 错误代码，若不提供则值可能为 200 error=False/444 error=True 10 :param data: 若提供了 data，则 data 中应该包含 error/message/code 11 :param replaceobj: 替换响应中的键名。 {\u0026#39;被替换\u0026#39;: \u0026#39;替换值\u0026#39;} 12 :param kwargs: 要加入响应的其他对象，可以是 model 也可以是 dict 13 :return: 一个 Response 对象 14 \u0026#34;\u0026#34;\u0026#34; 15 16 # 如果提供了 data，那么不理任何其他参数，直接响应 data 17 if not data: 18 data = kwargs 19 for k, v in kwargs.items(): 20 # 不处理空对象 21 if not v: 22 continue 23 data[k] = db.to_response_data(v, replaceobj, replaceobj_key_only) 24 data[\u0026#39;error\u0026#39;] = error 25 data[\u0026#39;code\u0026#39;] = code 26 if message: 27 # 除非显示提供 error 的值，否则默认为 True 28 # 意思是提供了 message 就代表有 error 29 data[\u0026#39;message\u0026#39;] = message 30 if error is None: 31 data[\u0026#39;error\u0026#39;] = True 32 if not data.get(\u0026#39;code\u0026#39;): 33 data[\u0026#39;code\u0026#39;] = 444 34 else: 35 # 除非显示提供 error 的值，否则默认为 False 36 # 意思是没有提供 message 就代表没有 error 37 if error is None: 38 data[\u0026#39;error\u0026#39;] = False 39 if not data.get(\u0026#39;code\u0026#39;): 40 data[\u0026#39;code\u0026#39;] = 200 41 if not isinstance(data, dict): 42 # 444 为不合法操作 43 data = {\u0026#39;error\u0026#39;: True, \u0026#39;code\u0026#39;: 444, \u0026#39;message\u0026#39;: \u0026#39;data 必须是一个 dict！\u0026#39;} 44 if not data.get(\u0026#39;code\u0026#39;): 45 if data.get(\u0026#39;error\u0026#39;): 46 data[\u0026#39;code\u0026#39;] = 444 47 else: 48 data[\u0026#39;code\u0026#39;] = 200 49 resp = jsonify(data) 50 return resp 上面的源码注释很清晰，不用多言。jsonify 是 Flask 提供的一个 JSON Response 封装，它会返回一个 JSON 响应。\n有必要多说一句的是 db.to_response_data。这个方法把 SQLAlchemy 的 Model 对象（一般是数据库查询的结果）转换成为一个 dict，接着使用 jsonify 将其转换成为 JSON Response。因为与本文关系不大，这里就不贴 db.to_response_data 的源码了。\nresponseto 方法在路由中使用。下面是一个获取奖品信息的路由方法示例：\n1@reward.route(\u0026#39;/get/\u0026#39;, methods=[\u0026#39;GET\u0026#39;]) 2@login_token_checker() 3def reward_status_get(r, loginobj): 4 \u0026#34;\u0026#34;\u0026#34; 获取邀请码对应的奖励信息 5 :param invitecode: 奖励邀请码 6 \u0026#34;\u0026#34;\u0026#34; 7 invitecode = parse_int(request.args.get(\u0026#39;invitecode\u0026#39;)) 8 if invitecode is None: 9 return responseto(\u0026#39;需要 invitecode!\u0026#39;, code=401) 10 11 # 保存奖励的 Table 12 Reward = get_reward_table(r) 13 reward_result = Reward.query.filter(Reward.invitecode == invitecode).all() 14 if reward_result is None: 15 reward_result = [] 16 else: 17 rewards = [] 18 # item 是 Reward Table 中的一行数据 19 for item in reward_result: 20 # rvalue 是一个保存在数据库中的 JSON 字符串 21 result = json.loads(item.rvalue) 22 # 当前奖励行的状态 23 result[\u0026#39;status\u0026#39;] = item.status 24 rewards.append(result) 25 reward_result = rewards 26 27 return responseto(result=reward_result) 在上面的路由方法中，当没有提供 invitecode 导致请求失败的时候，responseto 返回的内容为：\n1{ 2 \u0026#34;error\u0026#34;: true, 3 \u0026#34;code\u0026#34;: 401, 4 \u0026#34;message\u0026#34;: \u0026#34;需要 invitecode!\u0026#34; 5} 当请求成功的时候，JSON 内容可能为：\n1{ 2 \u0026#34;error\u0026#34;: false, 3 \u0026#34;code\u0026#34;: 200, 4 \u0026#34;result\u0026#34;: [ 5 { 6 \u0026#34;gold\u0026#34;: 10, 7 \u0026#34;status\u0026#34;: 1 8 }, 9 { 10 \u0026#34;money\u0026#34;: 3000, 11 \u0026#34;status\u0026#34;: 2 12 } 13 ] 14} 从上面的例子可以看出，得益于 Pyhon 方法的 kwargs 参数机制，responseto 的使用可以非常灵活。使用中既可以省略许多参数，也可以动态调整返回的 JSON 内容的键名。\n这些灵活的用法，在 Golang 中会遇到挑战。\nresponseto 在 Golang 中的挑战 在 Golang/gin 中实现 responseto 方法，至少会碰到 3 个问题。\nJSON 对应的 Struct 问题。 Golang 不支持关键字参数的问题。 动态 JSON 结构的问题。 让我们来解决这些问题。\ngin.H 和 Context.JSON 查看 gin 的源码，可以找到一些良好的封装。gin.H(utils.go) 提供了一个类似于 Python dict 的结构。Context.JSON(context.go) 提供了类似于 Flask jsonify 的方法。\n1// utils.go 2// H is a shortcut for map[string]interface{} 3type H map[string]interface{} 4 5// context.go 6// JSON serializes the given struct as JSON into the response body. 7// It also sets the Content-Type as \u0026#34;application/json\u0026#34;. 8func (c *Context) JSON(code int, obj interface{}) { 9\tc.Render(code, render.JSON{Data: obj}) 10} Responseto/ResponsetoWithData Golang 不提供方法重载和关键字参数，因此我创建了 3 个方法来替代 Flask 版本的 responseto：\n1package re2q 2 3import ( 4\t\u0026#34;net/http\u0026#34; 5 6\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 7) 8 9// Responseto 使用 gin.Context 创建一个响应，提供 message/code 10func Responseto(c *gin.Context, message string, code int) { 11\tResponsetoWithAllData(c, gin.H{ 12\t\u0026#34;error\u0026#34;: code != 200, 13\t\u0026#34;code\u0026#34;: code, 14\t\u0026#34;message\u0026#34;: message, 15\t}) 16} 17 18// ResponsetoWithData 使用 gin.Context 创建一个响应，自动填充 error/code/message 19func ResponsetoWithData(c *gin.Context, data gin.H) { 20\tmessage := data[\u0026#34;message\u0026#34;] 21\tif message != nil { 22\tdata[\u0026#34;message\u0026#34;] = message 23\t} 24\tcode, ok := data[\u0026#34;code\u0026#34;] 25\tif !ok { 26\tif message == nil { 27\tcode = 200 28\t} else { 29\tcode = 444 30\t} 31\t} 32\tif err, ok := data[\u0026#34;error\u0026#34;]; ok { 33\tdata[\u0026#34;error\u0026#34;] = err 34\t} else { 35\tdata[\u0026#34;error\u0026#34;] = code != 200 36\t} 37\tdata[\u0026#34;code\u0026#34;] = code 38\tResponsetoWithAllData(c, data) 39} 40 41// ResponsetoWithAllData 使用 gin.Context 创建一个响应，直接使用包含了 message/code/error 的 data 42func ResponsetoWithAllData(c *gin.Context, data gin.H) { 43\tc.JSON(http.StatusOK, data) 44} 上面的三个方法层层递进，提供了 Flask 版本 responseto 能提供的 大部分 功能。想完整实现 Flask 版本 responseto 的功能，需要定义更多的方法，或者采用 Golang 中的变长参数。这样会让方法变得更加负责，让方法的使用者产生困扰。\n在跨语言移植功能的时候，”保持绝对完全一致“ 是没有必要的。每个语言都有自己独特的特性，我们作为架构设计者，需要在不同语言中进行折衷。我认为上面 3 个方法既保证了简洁的抽象，也保持了一定的使用灵活性。这是一个合理的移植决策。\n下面是 Responseto 和 ResponsetoWithData 在路由中使用的例子：\n1// AudibleActive return active users list 2func AudibleActive(c *gin.Context) { 3\tregionals, defaultDBQry := registerOrActive(c) 4\tif regionals != nil { 5\t// 查找活跃数据 6\tactives := []models.ActiveModel{} 7\tfindError := defaultDBQry.Find(\u0026amp;actives).Error 8\tif findError != nil { 9\tre2q.Responseto(c, findError.Error(), 503) 10\treturn 11\t} 12\tactivesSerializer := models.ActivesSerializer{c, actives} 13\tre2q.ResponsetoWithData(c, gin.H{ 14\t\u0026#34;registers\u0026#34;: activesSerializer.Response(), 15\t\u0026#34;regionals\u0026#34;: regionals, 16\t\u0026#34;code\u0026#34;: 200, 17\t}) 18\t} 19} 和上面 Flask 路由的例子类似，第一个 Responseto 方法提供了一个包含 error/code/message 键名的 JSON 对象，第二个 ResponsetoWithData 方法提供了一个带有具体数据返回的 JSON 对象。\n看吧，完美！\n序列化 然鹅并没有那么完美。\n也许你注意到了上面代码中的 models.ActiveSerializer 这个名称，是的，序列化必须自己动手。\n由于 Golang 的语言特色（Public 必须大写字母开头），在定义数据库字段的时候，你必须要进行一些映射。另外，你也不一定希望所有的数据库字段都返回给客户端，因此需要手动序列化。\n1package models 2 3import ( 4\t\u0026#34;github.com/gin-gonic/gin\u0026#34; 5) 6 7// ActiveModel is a table for active users 8type ActiveModel struct { 9\tGid int `gorm:\u0026#34;PRIMARY_KEY,AUTO_INCREMENT\u0026#34;` 10\tDate int `gorm:\u0026#34;PRIMARY_KEY,INDEX\u0026#34;` 11\tChannel int `gorm:\u0026#34;PRIMARY_KEY,INDEX\u0026#34;` 12\tNum int `gorm:\u0026#34;NOT NULL\u0026#34;` 13} 14 15// ActiveResponse is a JSON config for response 16type ActiveResponse struct { 17\tGid int `json:\u0026#34;gid\u0026#34;` 18\tDate int `json:\u0026#34;date\u0026#34;` 19\tChannel int `json:\u0026#34;channel\u0026#34;` 20\tNum int `json:\u0026#34;num\u0026#34;` 21} 22 23// ActiveSerializer is a secializer for JSON object 24type ActiveSerializer struct { 25\tC *gin.Context 26\tActiveModel 27} 28 29// ActivesSerializer is a secializer for JSON list 30type ActivesSerializer struct { 31\tC *gin.Context 32\tActives []ActiveModel 33} 34 35// Response is for JSON response 36func (s *ActiveSerializer) Response() ActiveResponse { 37\tresponse := ActiveResponse{ 38\tGid: s.Gid, 39\tDate: s.Date, 40\tChannel: s.Channel, 41\tNum: s.Num, 42\t} 43\treturn response 44} 45 46// Response is for JSON response 47func (s *ActivesSerializer) Response() []ActiveResponse { 48\tresponse := []ActiveResponse{} 49\tfor _, active := range s.Actives { 50\tserializer := ActiveSerializer{s.C, active} 51\tresponse = append(response, serializer.Response()) 52\t} 53\treturn response 54} 在上面的代码中，ActiveModel 是一个数据表定义。ActiveResponse 用来完成数据库字段与 JSON 响应之间的映射。\n要了解 ActiveModel 中 gorm，请参考： Flask 到 Gin —— SQLAlchemy 和 gorm。\n需要注意 ActiveSerializer 和 ActivesSerializer 的区别（后者是复数形式）。它们分别用来返回 一个 ActiveModel 对象和 一组 ActiveModel 对象。在 Python 这类动态语言中，我们可以将它们放在同一个方法中，在 Golang 中则必须分开处理。\n如果不希望这么麻烦，只想简单把 JSON 转换成 Struct 定义，可以使用这个网站： JSON-to-Go。\nGo by Example: JSON 详细介绍了在 Golang 中使用 JSON 的一些特性，适合初学者阅读。\n阅读系列所有文章：从 Flask 到 Gin。\n全文完 ","date":"2019-12-01","description":"","lastmod":"2019-12-01T03:33:49Z","slug":"flask-to-gin-json","tags":["fromto","flask","golang","python","gin","mjp"],"title":"从 Flask 到 Gin —— 处理 JSON","url":"https://blog.zengrong.net/post/flask-to-gin-json/"},{"categories":["technology"],"content":"我写过很多 “从...到...” 的文章，你如果也想看看，请访问 fromto 这个 Tag。\n在 Golang Resources 这篇中我给自己挖了个 Golang 的坑。中老年程序员都知道，最快掌握一门技术的方法就是用它做项目，而且要做真实的项目。所以，我准备把 SAGITEAM 目前正在使用的 MJP API 服务中的部分功能采用 Golang 实现一些。\nMJP API 采用 Python Web 框架 Flask 开发，使用 uwsgi + Nginx 部署。目前有几十个实例，服务的游戏群 DAU 大约 200 万。选择 Flask 框架的原因在 Python web框架的选择 一文中有提及。\nMJP API 是采用微服务思想实现的，换一个架构来实现新功能，对原来的服务不会产生任何影响。我选择了一个新的微服务使用 Golang 的 Gin 框架开发。选择 Gin 的原因在 Golang Resources 一文中有提及：\n得益于 Golang 自带的 http 包的完善度，Golang 的 Web 框架可是百花齐放。我仔细比较了大约十几个 Web 框架，根据我的标准（小巧、灵活、优雅、性能、成长），在两个框架里面纠结了好一阵子：\nGin Echo 最终我选择了 Gin。至于原因么…… 可能是因为名字短一点？\n在具体实现的过程中，我碰到了不少问题。目前这个使用 Gin 开发的服务已经开发完成并上线了，我打算花一点时间，写一个系列文章，简短地介绍一下从 Flask 到 Gin 的转变。这个系列文章不会深入到框架原理层面，也不会非常详细，仅仅是我解决现实问题的记录。其中出现的源码基本都是片段。\n所以呢，这并不是一个初学者入门系列。文中我只求记录，不求完整和连贯。若文中有部分内容对你有用，我就会感到很开心了。\n基于上面的原因，读这个系列文章，可能需要下面的技能：\n了解 SQLAlchemy 熟悉 Flask 知道 uwsgi 读完 The Way to Go 当然，如果你本来是 Golang 的老司机，现在希望了解一下 Flask，这个系列也会有所帮助的。\n下面是目录部分：\n处理 JSON Logging 读取配置文件 SQLAlchemy 和 gorm 装饰器和中间件 部署和优雅重启 从 Flash 到 Gin，并不代表我会放弃 Flask。新的服务使用 Gin 还是 Flask 实现，取决于具体情况。作为程序员，不停折腾才有意义。\n只要我们还活着，最大的不变就是改变本身。\n全文完 ","date":"2019-11-24","description":"","lastmod":"2019-11-24T07:18:27Z","slug":"flask-to-gin-index","tags":["fromto","flask","golang","python","gin","mjp"],"title":"从 Flask 到 Gin —— 目录","url":"https://blog.zengrong.net/post/flask-to-gin-index/"},{"categories":["technology"],"content":"本文作者为 SAGITEAM 团队成员 蟹老板 。首发于 Cocos论坛 。\n文章的开始，请允许我挖个坟 —— 微信小游戏从立项到上线！谈谈《猎头专家》的开发历程，点击下面的链接阅读：\ncocos论坛 zrong's Blog 这个系列由于时间、精力和生存的问题，最终还是太监了，现在我们新开一个时间线继续。\n去年这个时候，我们还只有一款小游戏，今年我们已经可以召唤两次神龙了…… 以下是为大家精选的小游戏\n（请用微信扫码开始游戏）\n这次跟大家分享的是小游戏《单挑篮球》的开发历程，我会从 立项，创意，技术、迭代 四个方面来介绍。\n一、立项 在小游戏平台上的篮球游戏，99%都是纯投篮类小游戏，没有一款真正意义上的竞技对抗型篮球游戏。我们参考了 Basketball Battle，这是一款轻度、有趣的1v1篮球对抗游戏，但存在游戏表现力和玩法深度不足的问题，比如：角色及动作太Q，AI存在明显的bug，数值压制无力吐槽……\n所以我们仅借鉴其2D横版的游戏表现方式，在角色形象、动作、操作方式上做了大量创新，代入原创的数值及AI系统，打造出了这样一个易上手，但又有足够深度的竞技小游戏。\n你没看错！这是一个“横版”游戏。在微信小游戏平台上，做横版游戏代表提前“放弃”了一部分玩家。SAGITEAM 的第一款游戏《开心射手》也是横版。那时选择横版是因为不了解小游戏平台的玩法，而这次立项选择横版，是因为《单挑篮球》必须做成横版才能满足竞技游戏的操作需求。\n竞技+篮球这个独特的小品类，与小游戏平台上爆款特征不沾边。SAGITEAM希望不盲目追随市场爆点，专心打造一个优质的产品，在小游戏的下半场持续磨练团队和产品的竞争力。\n《单挑篮球》目前已在四个平台上线，欢迎品鉴！\n请分别用四个APP来扫：\n二、创意 场景 调整好篮框和角色的大小比例，球场大小可以控制在一屏显示完整，不需要移动镜头，这对于1v1的对抗表现完全够用。\n角色 不同于大部分街篮游戏使用的Q版三头四头身风格，我们参考了《羽毛球高高手》的美术设定，使用五到六头身，外形更接近真实人身比例，这样角色的动作细节有更大的表现空间，每个角色有多至26个不同的全身动作，力求做到真实。\n主界面 进入游戏主界面后，你会看到默认角色“超级新人“在训练，这就是在告诉你，这不是一款投篮机游戏，你看到的就是游戏中的真实效果。\n新手引导 亲切的”西安教练”会告诉你游戏的基本操作，当然，只是基本操作，更高级的，就得你自己去摸索了……\n操作 为了增强玩家的操作性，我们在游戏中加入了 ActionButton，实现防守时可抢断，进攻时可突破的功能，如果深度体验，你还会发现假动作、后仰跳投、盖火锅…… 下图标明了四个键的分布，同时，我们放大了按钮的触摸区，让玩家可以更好的盲操作。\n数值及AI 体育类游戏的数值实际上是有上限的，简单地说，就是角色的表现不能太离谱，比如跑动速度，弹跳力，投篮命中率，不然就会感觉很“假”，要避免这种情况，就不能纯粹使用数值来做压制，而是更多使用分级AI来帮助玩家成长，下面两张图就是初级AI与最高AI的行为树配置，可看出复杂程度差别还是比较大的。\n道具系统 如果你想赢得轻松点，可以在赛前来点兴奋剂，但作为一个公平竞技游戏，还希望玩家提升硬实力才是。\n三、技术 物理表现 我们抛弃了Box2D，所有碰撞计算都是自行用代码实现，主要是基于两个原因：\nBox2D性能在小游戏平台上实在堪忧 篮球游戏所用到的物理公式非常简单 不过我们依然使用了 cc.Intersection 来做碰撞检测。\n游戏中的物理运动，无外乎移动，加速，重力、反弹、抛物线五种，网上教程很多，这里不详述，比较值得一说的是本游戏内的抛物线实现，主要运用在两个地方：一、球出手到触框（板）的飞行轨迹，二、扣篮时角色的起跳轨迹\n这两处抛物线最初使用的是比较常见的匀速线性公式，无法表现出球在飞行的初/中/末段的速度变化，也无法表现出角色在扣篮时的起跳爆发力及滞空效果，后来我改为使用三阶贝塞尔曲线，通过改变两个控制点的位置，较好地模拟了非线性抛物线效果，上图中的白点即为贝塞尔曲线在每帧绘制的坐标点，并附上三阶贝塞尔曲线的生成代码。\n1export default class Bezier { 2 3 /** 4 * 获得贝塞尔曲线的点的具体坐标 5 * @param cps [[起点],[控制点1],[控制点2],[结束点]] 6 * @param t [0-1] 7 * @return [number, number] 8 */ 9 public static getPoint (cps: number[][], t: number) { 10 let ax, bx, cx 11 let ay, by, cy 12 let tSquared, tCubed 13 let result = [0, 0] 14 15 cx = 3.0 * (cps[1][0] - cps[0][0]) 16 bx = 3.0 * (cps[2][0] - cps[1][0]) - cx 17 ax = cps[3][0] - cps[0][0] - cx - bx 18 19 cy = 3.0 * (cps[1][1] - cps[0][1]) 20 by = 3.0 * (cps[2][1] - cps[1][1]) - cy 21 ay = cps[3][1] - cps[0][1] - cy - by 22 23 tSquared = t * t 24 tCubed = tSquared * t 25 26 result[0] = (ax * tCubed) + (bx * tSquared) + (cx * t) + cps[0][0] 27 result[1] = (ay * tCubed) + (by * tSquared) + (cy * t) + cps[0][1] 28 29 return result 30 } 31} ECS框架 本游戏使用了 开源 ECS 框架，ECS 框架的好处不必多介绍，有兴趣的同学自行找资料学习吧，这里简单贴一下我设计的系统与组件\n1export default class LogicSystem extends Systems { 2 constructor (context: GameContext, service: Service) { 3 super() 4 5 let pool: Pool = Pool.instance 6 7 this.add(pool.createSystem(new GameInitSystem(context, service))) 8 this.add(pool.createSystem(new EnergySystem(context, service))) 9 this.add(pool.createSystem(new BehaviorTickSystem(context, service))) 10 this.add(pool.createSystem(new PlayerInputSystem(context, service))) 11 this.add(pool.createSystem(new ActorMoveSystem(context, service))) 12 this.add(pool.createSystem(new BallMoveSystem(context, service))) 13 this.add(pool.createSystem(new CollisionFactorySystem(context, service))) 14 this.add(pool.createSystem(new CollisionCheckerSystem(context, service))) 15 this.add(pool.createSystem(new CollisionHandlerSystem(context, service))) 16 this.add(pool.createSystem(new StealSystem(context, service))) 17 this.add(pool.createSystem(new CatchSystem(context, service))) 18 this.add(pool.createSystem(new DunkSystem(context, service))) 19 this.add(pool.createSystem(new CloseSystem(context, service))) 20 this.add(pool.createSystem(new SkillCheckSystem(context, service))) 21 this.add(pool.createSystem(new TimerSystem(context, service))) 22 } 23} 24 25export enum ComponentIds { 26 Data, // 数据组件 27 Render, // 渲染组件 28 Player, // 玩家 29 Robot, // 机器人 30 Shadow, // 投影 31 Actor, // 角色 32 Ball, // 球 33 Action, // 动作组件 34 Parabola, // 实现抛物线 35 ActorMove, // 实现人移动（速度, 重力） 36 BallMove, // 实现球移动（速度, 摩擦力, 空气阻力, 重力） 37 Collider, // 碰撞组 38 Contact, // 碰撞关系 39 Collision, // 碰撞事件 40 Body, // 坐标及体积 41 State, // 角色状态 42 Timer, // 定时器 43 Behavior, // 行为器 44 45 totalComponents 46} AI行为树 本游戏使用了一个开源可视化的行为树编辑器 behavior3editor，要聊起行为树，篇幅就不够了，所以，就此打住……，这里仅奉上行为树节点的名称供参考\nUI设计 本游戏UI使用的是 http://www.fairygui.com/ 这主要是为了方便美术同学，因为美术同学觉得fgui比creator好用……你要问我fgui好不好，我只能说有好有坏吧，可以省掉开发同学一些拼界面的工作量，但坑也是有一些的。\n性能调优 由于小游戏平台的限制，对游戏包体的大小和运行性能要求都比较严格，我从以下几个方面做了优化：\n砍掉没有使用的模块，可以将 cocos2d-js-min.js 由 1.6mb降低至 900kb 将 main.scene 所需的 logo 及白底图放在首包内，使其启动即渲染，避免黑屏 拆分 fgui 生成的包资源，做一个小的 loading 包，在logo及健康忠告的掩护下优先载入loading包 对合图进行 tinypng 压缩，将1.8mb的合图压至500kb 非必须资源在使用时异步加载（如音频、纹理、spine） 四、迭代 篮球从立项到上线（微信）小范围测试，历经了三个多月，之后保持着一周一到二个版本的迭代，迭代的方向是针对游戏数据进行调优，主要就是留存/活跃/时长/付费四个指标，但这四个指标并不孤立，而是一个整体，比如我们试着提出以下问题：\n游戏界面信息是否足够清晰？ 新手引导的完成度如何？ 玩家玩到第几局后会流失？ 游戏是否太难（或简单）？ 玩家的成就感和挫败感在哪里？ 卡点是否合理？ 如何提升玩家的付费意愿？ …… 这些问题都需要通过详细的打点来分析，好游戏一定是调出来的，每一次调整都要有清晰的目标，充分验证假设。\n每个版本做出一些调整，观察数据的变化，会发现，四个指标是同步上升的，我以上面的问题2、4作个简单的回答：\n回答2： 虽然我们认为新手引导已足够清晰，但最初的通过率只有70%，通过对新手引导共16步提示进行打点分析，发现有三步引导（滞空跳投，前进上篮、前进扣篮）的通过率非常低，判断是由于是组合键操作，基础较低的玩家初次接触无法很好掌握而放弃。于是我们对这三个引导进行了简化调整，通过率上升至90%，反映到新增次留上，可以提升3%以上。\n回答4： 最初的AI等级只有五级，与积分挂钩，后来发现五级不够，难度曲线太陡，玩家每升一个level，胜率就会降低10多个%，后来改为10个level，并根据玩家最近10场的胜负状况动态调整AI配置，让玩家成长曲线更平滑，反映到游戏时长上，由最初的500秒升至最高的800秒。\n五、总结 小游戏行业门槛低，每天都会有无数新游上线，但行业俨然已是红海，99%的游戏进来可能连个泡都没冒就沉底，这是每个CP都不愿看到的结果，希望我们分享的《单挑篮球》的创作过程能给大家带来一些启示。\n全文完 ","date":"2019-11-12","description":"","lastmod":"2019-11-12T02:06:51Z","slug":"basketball-development","tags":["sagiteam"],"title":"小游戏《单挑篮球》开发过程分享","url":"https://blog.zengrong.net/post/basketball-development/"},{"categories":["technology"],"content":"一直以来，SAGITEAM 的小游戏矩阵使用的是自建的分析系统。我们使用 ZeroMQ 把数据传递给一个后端程序，作为 CSV 存储下来。需要分析的时候，将这些数据作为 pandas 格式载入，在 zeppelin 中进行多维度分析，最后进行图表化呈现。\nzeppelin 对服务器要求颇高，不稳定(v0.8.2)，在大量计算的情况下，内置 Python 进程会经常崩掉，内存释放有问题，32G 内存常常吃紧。但对于我们上百万 DAU 的小盘子来说，还是够用的。\n考虑到海外再部署一套这样的统计系统的复杂性和稳定性，我们希望使用更成熟的商业解决方案，所以选择了 Firebase。\n本文面向游戏开发者和游戏数据分析师，试图解释使用 Firebase 来实现游戏的统计分析的一般流程。对于数据分析师和程序员需要重点阅读的段落，本文中进行了特别标注。但无论程序员还是分析师，我都建议能够通读本文和本文提到的所有文档。\nFirebase 的文档比较复杂（Firebase文档入口），如果你也使用 Unity 开发游戏并希望使用 Firebase，那么阅读本文可能会帮你节省一些时间。\n1. 流程 除了 Firebase，这套流程还需要 BigQuery 和 Data Studio。下面这张图能让你更容易理解每个产品之间的分工：\n2. Firebase 2.1 Firebase 是什么 Firebase 是一套工具集。它把一大坨生产力工具整合在一起，方便开发者使用和选择，同时支持 iOS/Android/Unity/C++/Web 平台和环境。\n在我们的游戏中，使用了 Analytics、Predictions 和 A/B Testing（如上图中红框所示）。在本文中，只会包含 Analytics 的内容。\n2.2 事件和属性 Firebase 中的数据来自于 Firebase SDK 的自动搜集，以及程序员在游戏中使用 SDK 提供的 logEvent 方法主动上报。\n面向分析师\nFirebase 中记录的数据，包括“事件”和“用户属性”，官方文档 中的描述为：\n事件：您的应用中发生了什么，例如用户操作、系统事件或错误。 用户属性：您为了描述自己的细分用户群而定义的属性，例如语言首选项或地理位置。 面向全体\nFirebase 会自动记录下面这些事件： 自动收集的事件。\nFirebase 还定义了所有应用都支持的常规事件：事件：所有应用，这类事件在 Firebase 中不会自动记录，程序员需要根据游戏中定义的行为，使用指定名称主动上报它们。\nFirebase 还针对游戏的特点指定了一些专用事件：事件：游戏，这类事件在 Firebase 中不会自动记录，程序员需要根据游戏中定义的行为，使用这些名称主动为上报它们。\n除了上面指定名称的事件之外，Firebase 还提供了最多 500 个自定义事件名称，这对于大多数游戏来说都够用。需要注意的是，需要仔细规划这些事件名称，不要浪费它们。因为使用过的名称是 不能删除 的：收集和配置限制。\n2.3 接入 面向分析师\n快速阅读文档 Google Analytics（分析）\n面向程序员\n请仔细阅读文档： 将 Firebase 添加到您的 Unity 项目。\n在这里可以找到范例：https://firebase.google.com/docs/samples\n注意：接入 SDK 之前，要先创建对应的 Firebase 项目并拿到配置参数。\n2.4 在 Firebase 中查看报告 登录 Firebase Console，点击 Dashboard 菜单，在这里查看各种可视化数据，并可以根据用户属性进行各种组合分析。\n如果希望查看自定义的事件，点击 Events 菜单，在这里查看上报的各种自定义事件。\n自定义事件是以表格形式呈现的。如果希望将其可视化，或者综合其他的数据一起进行分析，在 Firebase Console 中是无法做到的，此时我们需要 BigQuery。\n3. BigQuery BigQuery 是一个数据库产品，支持使用标准 SQL 语句查询。我们将 Firebase 中收集的原始数据导出到 BigQuery 中，就可以使用 SQL 的强大语法来进行自定义查询了。\n上图底部，红色箭头指向 “在 BigQuery 中查看您的原始事件” 链接，点击它就可以将 Firebase 数据导出到 BigQuery。\n如果这个链接没有出现，可能需要预先 将 Firebase 与 BigQuery 相关联。\n面向程序员\n为了方便演示，我们导入一些公共数据到 BigQuery 中。选择 “资源-添加数据-浏览公共数据集”，搜索 NCAA Basketball 数据将其加入 BigQuery 用于测试。\nncaa_basketball.mbb_historical_teams_games 这个表保存了 NCAA 联赛从 1996-97 赛季到现在的每支队伍的比分数据。每场比赛包含两条记录，每条记录代表一支球队。\n上图中的 SQL 语句查询出赛季、球队名称、球队比分，对手比分等字段，并使用赛季进行排序。\n查询的结果以表格的形式呈现。\n要将查询到的数据可视化展示，我们需要 Data Studio。\n4. Data Studio 上图中，红色箭头指向 “使用 Data Studio” 链接，点击它可以在 Data Studio 中对查询结果进行可视化。\n面向分析师\n在 DataStudio 界面中，可以对数据进行多种维度的比较和分析，也有多种图表和关联可供选择。\n分析师需要提前制定好数据分析需求，要求程序员按最大数据集的在 BigQuery 中进行查询，才能保证 Data Studio 中有更灵活的维度。\n5. FAQ 5.1 Firebase 和 Analysis 对于没有使用过 Google Analytics 的新用户，可以忽略这一节。\n熟悉 Google 产品的同学可能知道，Google 在 Firebase 推出之前，就推出了 Google Analytics 这款产品。那么刚才介绍的 Google Analytics 和 Firebase 是什么关系呢？\n根据资料，我认为 Google 希望将 Analytics 这个产品整合成为 Firebase 的一部分。但由于 Analytics 产品的用户众多，这个工作量相当大。Google 或许选择了两个产品后端融合，但前端分离的折衷办法。\n对于没有 Google Analytics 历史的产品，直接接入 Firebase 中的 Analytics 即可。Firebase Console 中显示的数据与 Google Analytics 前端中显示的数据是完全相同的。\n如果对这个观点感兴趣，可以仔细阅读：\nGoogle Analytics（分析）服务 SDK 将停用 What is Google Analytics for Firebase? 5.2 在服务器端使用 logEvent 一个很容易想到的需求，是能否在 server 端使用 logEvent 记录数据到 Firebase？\n对于游戏来说，许多需要分析的数据是由服务器产生的，或者在服务器中可以更安全，更准确的获取到这些数据。\n但 Firebase 不是为了这个场景设计的。因为其 SDK 在记录数据的时候，还搜集了大量设备数据和用户数据（例如用户年龄、设备型号、登录状态），这些数据与其他数据是相关联的，在 server 端不易或难以完整得到这些数据。\n有一个简单的替代方案。\n我们使用 BigQuery 来分析数据，可利用 BigQuery API 从 server 端上报数据到 BigQuery 中，与 Firebase 的导出数据实现关联查询。\n5.3 费用 面向程序员\nBigQuery 是一个付费的数据库产品，每一次查询都要付费。\n有一些技巧用来控制费用。\n例如避免使用 SELECT * FROM ... 查看数据可用性，改为使用免费的 ”预览“ 功能。或者 ”在运行之前估算查询费用“ 都是很好的方法。\n下面的文档请程序员务必认真阅读。\n控制费用 优化查询性能 BigQuery 最佳做法：优化存储 全文完 ","date":"2019-11-09","description":"","lastmod":"2019-11-09T06:27:51Z","slug":"firebase123","tags":["google","firebase","sagiteam"],"title":"Firebase 123","url":"https://blog.zengrong.net/post/firebase123/"},{"categories":["technology"],"content":"还有两个月就要过年了，想起来今年还有一大任务没有完成，就是掌握一门新的编程语言。所以呢，这两天阅读了大量 Golang 文档，看得我颈椎病都犯了。\n我学习新技术的时候有个不好的习惯，就是除了新技术本身，还会第一时间找一堆与这个技术相关的信息，了解一下其主流应用，把今后可能用到的库啊开源项目啊都找出来。这就导致我在学习 Golang 的同时又跑去看了大量的开源库和 Golang 主流应用的资料。另一个不太好的习惯就是，我很有点选择困难综合症，总执着于在一堆技术里面选择出来一个自己看起来顺眼的，相对主流，用起来不错，源码足够优雅的那个。这也就直接导致我足不出户的这两天阅读量巨大，眼都快瞎了。除了选择起来很痛苦，寻找佐证和写垃圾代码测试的过程也是难以描述啊……\n好处就是，似乎又找到了一点点十几年前忘情怼代码的感觉。\n扯远了……\n记录一下这些资料，方便后面深入学习的时候查找。\nGolang 的入门资料 《Go入门指南》 《使用 Golang 构建 web 应用程序》 Go 指南 我的学习路线：\n花 8 小时读完《Go 入门指南》，看心情做一下书中练习。 快速跑一遍”Go 指南“中的例子，大约 1 小时。 挑《使用 Golang 构建 web 应用程序》中感兴趣的章节浏览一遍。 Golang Web 框架 我使用 Golang 的第一个目标是逐渐替换 SAGITEAM 正在广泛使用的 API 服务框架（uwsgi+Python)。因此我的第一站就是 Golang 的 Web 框架。\n得益于 Golang 自带的 http 包的完善度，Golang 的 Web 框架可是百花齐放。我仔细比较了大约十几个 Web 框架，根据我的标准（小巧、灵活、优雅、性能、成长），在两个框架里面纠结了好一阵子：\nGin Echo 最终我选择了 Gin。至于原因么…… 可能是因为名字短一点？\n顺便说一句，之前在选择 Python Web 框架的时候我也很纠结：Python web框架的选择。\n项目结构 项目结构的 best practice 可以参考下面两个资源：\nHow to Write Go Code Standard Go Project Layout 需要注意的是从 Go 1.11 开始，已经可以把项目放在 GOPATH 之外了，我就准备这么做。\n这里还有一篇 Restfull Projects API with Gin 可以参考。我就是准备专门用 Golang 来写 API。\nAPI Gateway Golang 在 API Gateway 这个领域里面也有不少实践，我顺便研究了一下。Kong 是目前应用较多的实现，个人也比较倾向于使用它。\nAPI Gateways: Kong vs. Tyk KrakenD Golang 实现 Tyk Golang 实现 Kong Openresty + Lua 实现 Orange Openresty + Lua 实现 全文完 ","date":"2019-10-27","description":"","lastmod":"2019-10-27T14:02:02Z","slug":"golang-resources","tags":["golang","openresty","python"],"title":"Golang Resources","url":"https://blog.zengrong.net/post/golang-resources/"},{"categories":["web"],"content":"是的，我回来了。停更了 4 个月之后，再次开始更新这个写了 14 年的博客。\n停更主要有两个原因：\n太忙(lan) Hexo 太慢 原因并不重要，直接看迁移的过程吧！\n1. 为什么要迁移到 Hugo 我曾经在 2014 年 博客静态化工作 时写了一个工具 wpcmd 用来实现 Wordpress 的本地发布。3 年后的 2017 年，我花了一些时间把 WordPress 迁移到了 Hexo 1/2。现在，我要从 Hexo 迁移到 Hugo。\n转到 Hexo 的这两年时间，我用得并不顺畅。主要原因有下面三个：\n我的博客现在有接近 900 篇文章，在我的 MacBook Pro (Retina, 13-inch, Early 2015, 3.1 GHz Intel Core i7, 16 GB 1867 MHz DDR3)上，每次使用 Hexo 编译都超过 2 分钟。如果在我的 1C2G 服务器上编译，时间将会更长。这个时长是无法忍受的。 Hexo 的文档不全，在需要对其进行扩展的时候，经常需要去读源码。对于一个工具来说，这降低了使用效率。虽然我仍然是一个程序员，但我不认为在这个工具上花时间是值得的。 Node.js 的工具链我一直不太喜欢。安装慢，依赖复杂，而且容易出错。 所以当我看到 Hugo 这个号称 The world’s fastest framework for building websites 的静态化工具时，不动心是很难的。部署容易，使用简单，还能顺便学一下 Go 语言，简直是为我量身打造的工具了。\n事实证明，切换到 Hugo 之后，构建我的整个博客，只需要 10 秒多点，那叫一个快！\n1 | EN 2+------------------+------+ 3 Pages | 1431 4 Paginator pages | 268 5 Non-page files | 0 6 Static files | 1009 7 Processed images | 0 8 Aliases | 1133 9 Sitemaps | 1 10 Cleaned | 0 11 12Total in 10549 ms 下面就按照时间顺序来叙述。\n2. themes maupassant 简洁美观，目前我使用这款。后面可能会换成 capsule。\n3. URL 对齐 网站迁移的过程，最重要的就是不能影响之前的 SEO。在从 WordPress 迁移到 Hexo 的时候，由于要切换域名，我在 Nginx 上做了一些工作来保证 SEO 正常。这一次，要做的工作也不少。我依然采用了和 WordPress 相同的结构来处理博客。\n3.1 POST 和 PAGE 使用 /post/{\\d+}.html 作为博客文章的 URL，对应 WordPress 中的 post，例如： https://blog.zengrong.net/post/2635.html 使用 /{\\w+}/ 作为博客页面的 URL，对应 WordPress 中的 page，例如： https://blog.zengrong.net/wpcmd/ 对于 post，我将每篇文章的 Markdown 文件放在 /content/post/ 路径下，文件名保持原来的 {\\d+}.md 不变。他们的 type 都是 post，对 slug 和 aliases 的处理保证了这个页面的老地址也能被访问。\n这样处理之后，该页面的地址变成了： https://blog.zengrong.net/post/wordpress-to-hexo1/，但是之前的页面地址 https://blog.zengrong.net/post/2635.html 依然可以访问，并会自动跳转到新的地址。这个转向并不是在服务端做的，而是在客户端生成了一个转向界面完成的。搜索引擎将识别这个跳转，在今后漫长的时间里，把 SEO 转到新的地址。\n下面是 /content/post/2635.md 这个页面的 Front Matter 内容：\n1+++ 2title = \u0026#34;WordPress to Hexo(1)\u0026#34; 3postid = 2635 4date = 2017-05-24T23:08:36+08:00 5isCJKLanguage = true 6toc = true 7type = \u0026#34;post\u0026#34; 8slug = \u0026#34;wordpress-to-hexo1\u0026#34; 9aliases = [ \u0026#34;/post/2635.html\u0026#34;,] 10category = [ \u0026#34;web\u0026#34;,] 11tag = [ \u0026#34;wordpress\u0026#34;, \u0026#34;master\u0026#34;, \u0026#34;hexo\u0026#34;, \u0026#34;staticize\u0026#34;,] 12lastmod = 2017-05-24T23:08:36+08:00 13+++ 对于 page，我将每篇页面的 Markdown 文件放在了 /content/page/ 路径下，文件名和原来的一致。它们的 type 都是 page，对 slug 和 url 进行了处理。\n根据 Hugo 的约定，/content/page/ 这个 Section 的文件会生成在 /page/{\\w+}/ URL 下面，我通过制定 url 来让页面被直接生成到根目录下。\n下面是 /content/page/wpcmd.md 这个页面的 Front Matter 内容。\n1+++ 2title = \u0026#34;WPCMD\u0026#34; 3postid = 2321 4date = 2015-06-12T09:40:22+08:00 5isCJKLanguage = true 6toc = true 7type = \u0026#34;page\u0026#34; 8slug = \u0026#34;wpcmd\u0026#34; 9url = \u0026#34;/wpcmd/\u0026#34; 10+++ 当然，手工来做这件事愚蠢且耗时，我写了一个 Python 脚本来完成这些自动转换： hexo2hugo.py\n3.2 Category 和 Tag 我之前使用的 Category 和 Tag 的单数形式，因此我的标签页地址为： https://blog.zengrong.net/tag/study/ 。而在 Hugo 中采用了复数模式，默认的标签页地址是： {domain}/tags/xxx/。\n要解决这个问题，可以在 config.toml 中进行如下配置：\n1[taxonomies] 2 category = \u0026#34;category\u0026#34; 3 tag = \u0026#34;tag\u0026#34; 但是，配置完成后，文章中的分类和标签都无法显示了。\n这是因为在 theme 中写死了对变量 .Params.Categories 和 .Params.Tags 的引用。将模版中这样的变量都改为单数形式即可。\n通过修改了下面几个模版中，解决了上面的问题：\nmaupassant/layouts/index.html maupassant/layouts/_default/single.html maupassant/layouts/_default/taxonomy.html maupassant/layouts/partials/categories.html maupassant/layouts/partials/tags.html 4. 嵌入标签处理 在 Hexo 中，我定义了几个嵌入式标签：\nflash 用于嵌入 flash 动画 download 用于支持下载链接管理 label Hexo 主题中自带，用于实现带颜色的 label 效果 在 Hugo 中可以使用 Shortcodes 来实现它们。我写了 3 个 Shortcodes 来实现这些功能。\n当然，需要批量转换 Hexo 格式的嵌入标签到 Hugo 格式的 Shortcode 。这不难，用 Python 正则替换一下就好。这部分替换脚本也在 hexo2hugo.py 中有提供。\n5. 留言服务 上次将博客迁移到 Hexo 的时候，我选择的评论服务是国内的畅言。因为当时多说濒临倒闭，Disqus 在国内又被墙。现在来看这个选择挺糟糕的，畅言的乱七八糟广告特别多，接入后我的博客就变成了牛皮癣广告墙，这里一块那里一块。\n我在 WordPress to Hexo(2) 中提到过，希望使用基于 Github 的 Issue 系统的评论系统来完成评论。这样的系统现在还不少的：\nutteranc Gitment Comment.js 使用它们存在一个问题，博客现有的三千多条评论就没法转换过去了。\n因此，我还是选择了需要自己部署的产品 Isso。Python 实现，自己也可以随便折腾。\n5.1 uwsgi.ini Isso 的架设过程并不复杂，因为对 uWSGI 已经很熟悉了，我自然是选择 uWSGI 作为生产环境的部署，下面是我的 uwsgi.ini 配置文件。\n1[uwsgi] 2;socket = %d%n.sock 3http = 127.0.0.1:1314 4master-fifo = %d%n.fifo 5pidfile = %d%n.pid 6 7master = true 8processes = 2 9threads = 2 10max-requests = 600 11chmod-socket = 666 12thunder-lock = true 13harakiri-verbose = true 14harakiri = 10 15buffer-size = 32768 16 17ignore-sigpipe = true 18ignore-write-errors = true 19disable-write-exception = true 20 21req-logger = file:%dlogs/req.log 22logger = file:%dlogs/%n.log 23daemonize = %dlogs/%n.log 24log-master = true 25threaded-logger = true 26 27env = ISSO_SETTINGS=%disso.cfg 28env = ISSO_CORS_ORIGIN=*.zengrong.net 29spooler = %dspooler 30wsgi = isso.run 31venv = %dvenv 32chdir = %d 33uid = app 34gid = app 5.2 isso.cfg isso.cfg 配置文件的内容如下：\n1[general] 2name = blog 3dbpath = /xxx/comment.db 4host = https://blog.zengrong.net/ 5log-file = /xxx/isso.log 6notify = smtp 7reply-notifications = true 8gravatar = true 9 10[smtp] 11username = zrong 12password = xxxxxx 13host = mail.xxx 14port = 111 15security = ssl 16to = i@zengrong.net 17from = i@zengrong.net 18timeout = 5 19 20[moderation] 21enabled = true 22 23[hash] 24salt = xxxxxxxxxxxxxxxxxxx 5.3 CROS Bugs of isso isso 让人很迷的有几点：\n环境变量中配置的 ISSO_CORS_ORIGIN 无效； 配置文件中配置的 host 用来指定跨域信息的域名也无效； 配置文件中配置的 log-file 无效，看不到 isso 的日志。 我在跨域的问题上纠结了许久，最后还是决定把 isso 部署到博客所在的 URL 子域下以规避跨域问题。这应该是 isso 的 bug，等有闲工夫了，我再去修改 isso 的源码来解决。\n5.4 迁移畅言的历史评论到 isso 在畅言后台，可以把历史评论记录导出为 json 文件，我用 Python 写了一个脚本： changyan2isso.py 将导出的数据转换成为 isso 可以导入的标准 json 格式。然后使用 isso import 命令将其导入到 isso 的数据库中。\nisso 也可以导入 WordPress 和 Disqus 的 XML 格式数据。\nisso 导入 json 文件的时候，会忽略留言数据的 parent 属性，这个属性用来指引留言的回复关系。我曾经修改 changyan2isso 使其支持 parent 属性，但回复关系依赖被回复的留言必须优先存在。而我在处理历史数据的时候并没有考虑这一点。折腾了一阵子之后，我不打算在十几年的老回复上再浪费时间，就此作罢。\n有兴趣的同学，可以根据 这段代码，配合 isso 中的 import 包来解决这个不支持 parent 的问题。当然，你也可以把历史回复导出成 WordPress 或者 Disqus 格式然后再导入。\n我不太喜欢 XML，所以，就酱紫了。\n全文完 ","date":"2019-08-29","description":"","lastmod":"2019-08-29T06:48:45Z","slug":"hexo-to-hugo","tags":["staticize","wordpress","hexo","hugo","fromto"],"title":"Hexo to Hugo","url":"https://blog.zengrong.net/post/hexo-to-hugo/"},{"categories":["impressions"],"content":"\n本周四，SAGITEAM 的肥宅同学精心准备，做了一次内部分享，关于 行为树。\nSAGITEAM 制作的部分小游戏使用了 行为树 作为游戏 AI 的实现基础。行为树解放了程序员的一部分工作力，让策划同学能更加全面和直接地控制游戏中的行为。在开始使用行为树之前，SAGITEAM 已经将 FairyGUI 加入美术岗位的工作流中，让美术同学跳过程序员直接实现 GUI，成功避免了程序员提刀追砍产品经理，美术同学在一旁助威的杯具。\n本文并不是一篇讨论行为树的技术文章，而是在此次分享结束后我站在 团队管理、个人成长以及工作效率 角度的一些关联思考。为了方便阅读，下面摘录一些 PPT 内容。很欣慰这次肥宅同学的文案中”既然“没有什么错别字！\n肥宅的讲稿摘录 行为树可以做什么 AI（AI的行为逻辑） 通过胜率控制本场难度 较为复杂的抽奖逻辑 游戏外的更多领域 行为树和状态机的比较 优点\n行为逻辑和状态数据分离，任何节点写好以后可以反复利用 重用性高，可用通过重组不同的节点来实现不同的行为树 呈线性的方式扩展，易于扩展 可配置，把工作交给程序之外的人 能够胜任”AI” “掉宝”等等场景 缺点\n每一帧都从 Root 开始，有可能会访问到所以的节点，相对 State Machine 消耗更多的 CPU 任何一个简单的操作都必须要使用节点 如何构建行为树 思考需求，分解动作，包括需要的参数 和程序沟通核对，调整具体需求 程序完成后，核实节点是否有效并且满足需求 问题思考 美术可以利用行为树做什么 策划如何更好和程序沟通 面对有些行为树不太好实现的功能需要如何解决 关于行为树的讨论 肥宅的分享结束后，大家的讨论格外激烈（如果不是我拦着，估计肥宅的分享就讲不完了）。下面我列出这些讨论过的问题以及大家讨论出的解决方案。\n第一，有些功能用行为树很难实现 肥宅同学认为一些行为很难用行为树来实现。但阿森(程序员)认为这些行为不能实现的原因是对行为树的思维方式不了解导致。此处策划和程序各自表明了自己的观点，但我认为双方都没有站在对方角度想问题。\n我认为策划理解程序思维是非常有必要的。行为树的使用涉及到很多程序术语，在对情况程序运行方式不了解的前提下，策划会陷入一个思维模式的陷阱中无法自拔。此时程序员要主动去对策划进行教学，帮助策划勇敢地走出第一步。在这一步中程序员会消耗较多的精力，甚至需要帮助策划去思考。但这个消耗是值得的。\n策划也需要在这个阶段中努力学习程序术语，积极与程序沟通以理解程序的思维方式和逻辑。这个过程可能很痛苦，但这个痛苦依然是值得承受的。\n第二，什么时候应该选择行为树，什么时候应该选择状态机？ Nef 同学提到了 AI 实现的三重境界：\n用 If/Else 硬编码； 使用状态机，基于事件编程； 使用行为树。 这三重境界越来越高级，对使用者的要求也越来越高。SAGITEAM 的作品”天才射手“中使用了状态机，现在的新作中开始使用行为树，说明我们对自己的要求更高了。\n木易同学认为策划同学应在游戏设计之初就确定选择。并非所有的游戏都适合使用行为树。一些比较简单的 AI 可能更适合使用状态机或者硬编码。如果策划能够在项目开启之初就想好方案，就能避免返工，让产品开发更快捷。\n这就需要策划更深入地思考项目，做出更加精确的规划。这是对策划要求的进一步提升。\n第三，行为树的优势 对于这一点，湃总讲得很清晰。他认为行为树可以提高协作效率。就像我们使用 FairyGUI 提升美术和开发之间的协作效率一样，行为树也能将对 AI 行为的改变限制到策划岗位，所有的调整都不需要程序员参与。这样既保证了策划同学对于行为思考和实现的一致性，又解放了程序员的生产力。\n彼得原理 上面的讨论大部分是程序员在向策划发出质问，或者对策划岗位提出更高的要求。在 SAGITEAM，我一贯主张并持续推进下面几点：\n策划需要熟悉美术、程序和运营 程序员必须会 PS 切图 美术需要了解程序的思维方式 只有这样，我们才能尽量晚地接触到 《彼得原理》 所提到的 上升悖论 限制：\n在层级组织里，如果我们积极进取、不断晋升，那么我们迟早会升到一个超出我们能力的位置，陷入工作不胜任的困境之中，成为组织的障碍和负资产。\n彼得原理在 1969 年提出，它与 1955 年提出的 帕金森定律 和 1948 年出现的 墨菲定律 一起被称为西方管理学上的三大发现。彼得博士甚至提出，如果人类无节制地追求进步和发展，人类本身也迟早会进入生存不胜任的状态。\n我并不担心人类的生存不胜任。我更关心 SAGITEAM 成员的成长。彼得原理所提到悖论有一个很重要的前提，就是 ”在层级组织里“。现代互联网创业公司的扁平化结构，削弱了层级观念对整个组织的影响。坚持小团队规模，强联系和职能侵入的工作方式，也能在一定程度上缓解不胜任困境。然而，这些方案都只能做到“缓解”，如果希望团队和成员进一步成长，除了给大家提供学习目标和方向之外，还必须想办法从底层激发团队成员的热情，让成员推着团队走。\n组织行为学 这次关于“行为树”的培训，就是一个自我激发的例子。行为树的使用是在产品设计的过程中，由策划推荐给程序员，再从程序员反推至策划的。它不是一个自上而下的产物，而是一个自下而上的过程。在木易同学意识到行为树的重要性时，我们制定了一个全员分享计划，让所有人都参与到行为树的学习中来。这次分享又造成了更深入和积极的讨论，进一步影响着 SAGITEAM 全体成员的思维模式。\n我觉得这个过程是 组织行为学 的一个极好范例：\n组织行为学（英语：Human Behavior in Organization），是通过研究一定组织体系内人的心理和行为表现及其规律，从而提高预测、引导和控制人的行为的能力，以实现组织既定目标的一种社会科学。\n组织行为学是研究在组织中以及组织与环境相互作用，人们从事工作的心理活动和行为反应规律性的科学。它采用系统分析的方法，综合运用心理学、社会学、人类学、生理学、生物学、经济学和政治学等知识，研究一定组织中人的心理和行为的规律性，从而提高各级领导者和管理者对人的行为预测和引导能力，以便更有效地实现组织预定的目标1。\n组织行为学的目的只有一个，就是改变工业化时代建立起来的那套边界清晰，分工明确，职责固定的管理方式。把它变得更灵活、更能激发创新、更适应当下快速变化的商业环境。\n现代管理的目的不是约束个体，而是要激活个体2。生态就像一片肆意生长的草原，它不是控制出来的，而是失控的结果3。管理的目的是把企业还原到最小的颗粒度：人，然后通过激发个体的活力让团队保持活力。\n缺乏创新、目标僵化、缺乏工作参与度，归根到底就是缺乏探索欲。探索系统是人类大脑的一个生理结构，是进化赋予人类族群的整理算法。它激发人类主动离开舒适区，去寻找新的可能性。一旦产生对外探索的欲望，它就会刺激身体，释放出积极的生理信号，让你感觉到接下来进行的工作充满了强烈的使命感和意义。\n长久地停留在舒适区，会让探索系统失灵。对探索者行为的约束(例如限制创新方式)，对探索者错误的惩罚都会导致探索行为止步不前。我们需要建立更自由的创新方式，创建安全区，保护探索者的激情和梦想。\n要升级我们的探索系统，当然需要对探索者提出更高的要求。\nSAGITEAM 值得这样做。\nSAGITEAM 也正在这样做。\n所以，不要害怕我对你要求太高。\n欢迎加微信吐槽\n公众号：曾嵘胡扯的地方\n全文完 ","date":"2019-04-21","description":"","lastmod":"2019-04-21T04:19:12Z","slug":"behavior-tree-and-organizational-behavior","tags":["management","sagiteam"],"title":"行为树，彼得原理和组织行为学","url":"https://blog.zengrong.net/post/behavior-tree-and-organizational-behavior/"},{"categories":["impressions"],"content":"\n自从最大的男性交友社区 Github 上的 996.ICU 项目火了之后，各大|小|自媒体们就进入了高潮阶段。科技圈大佬纷纷站出来表达自己的 996 观点。马老师，东哥以及“好兄弟”国庆在这个观点上花费了不少口水。加上国内浏览器对该项目的封禁，一个本该严肃讨论的话题变成了娱乐段子。\n本文的标题是不谈996，也暴露了我这个喜欢胡扯的博主蹭流量的小心思。\n所以我就谈点儿其他的吧：\n10 年前我和朋友创业做手游。彼时我是个程序员，对自己的技术灰常自信，经常跑去知乎回复小白的问题，在自己的博客上写点技术文章布布道吹吹牛逼。感觉没有啥东西是我做不出来的，给我时间我能把所有的技术难点搞定。产品只要做出来了肯定是市面上最好的，如果做出来不挣钱？那不是我的问题，那肯定是策划太傻逼。\n我当时会骂所有竞争对手的产品是渣渣，合伙人发一个产品过来讨论，我会把这个产品的技术特点以及实现方案从头喷到尾。我会追求代码的美观性和结构的合理性，review 同事写的代码，我一般会将它们重构一遍，然后写好文档，要求所有人之后必须按照规范来写代码。\n我经常和策划吵架，质问他为什么设计出来的东西又要改，为什么不能一次就想清楚。我也和服务器程序员吵架，对他不按照文档写协议以及写出来的协议不测试非常不满。我开始自己写服务端代码，心想搞不定你，我还搞不定服务器么？\n现在我知道了，这是极端的技术角度思维，是非常片面的思维方式。\n后来我接触到很多人，也经常面试。我发现年轻的求职者很喜欢问的一个问题是：”你们公司开了多久了？“。这问题本身没有错，可这些信息都是公开的，很容易得到啊。求职难道不应该做一下功课？还有，关心这个公司会不会马上垮掉，难道不应该是我这种中老年求职者才更在意的吗？刚入职场，是不是应该更关心这里有没有你能学到的东西？能不能持续成长？\n说到学习和成长，还有一些言论比较有趣：”做什么都一样，我就关心在你们这里有没有技术成长，让自己在业界中有立足之地！“。好的，你在公司练手这没问题，你技术好了另谋高就也没问题，可这些话放到面试上说真的好么？\n现在我知道了，这是典型的利益角度思维，完全从利益最大化的角度去思考，是非常危险的。\n最近我们要做产品运营，加了业界不少运营和商务的微信群。我发现，在运营和商务的眼里，产品是另外一个维度的工具。群里每天在收产品，看产品，讨论一个产品的留存、DAU、ARPU、分享率数据，产品的质量标准只有一个维度，就是这些数据好。数据好，投放就能赚钱。\n在产品上投入情感？做精品？抠体验细节？谈情怀？埋彩蛋？那不是需要考虑的事。我就只看数值。\n下面这段对话很能反映这种观点：\n”你把次留做到30，分享率做到20，ARPU 做到 0.1。然后就可以推了，肯定挣钱！“ “产品怎么做呢？” “不重要，做到这个值就可以了！” “可是怎么做到这个数据呢？”\n这有点像北京房市不好的时候，在中介办公室里，有个急于出手别墅的卖家拉住买房者说：“兄弟，这套别墅打折五百万，你们要不要考虑一下？”\n买房者也很诚恳：“兄弟！咱不缺那五百万，咱是缺那三千万啊！”\n我不是不知道产品调到这个数据就能挣钱，我是想知道该怎么调到这个数据啊！\n现在我知道了，这是纯粹的运营角度思维，这种思维对产品团队的帮助是很有限的。\n思维有那么多角度，为什么大家会偏爱一个角度呢？\n因为这样思考更轻松。这样思考能够借助之前的成功经验，这样思考不会陷入纠结和痛苦。\n如果能多从几个角度考虑问题，我们的成功率会不会更大？\n产品策划不仅仅使用产品角度思维考虑问题，还要综合运营角度思维来思考数据表现，然后在技术角度找到一个折衷点，一起实现利益最大化。在一个团队中推行这样的全面角度思维，是不是会更容易做成一个产品？\nSAGITEAM 正在尝试这样做。 对于 996.ICU，不同的人站在不同的角度。\n媒体们唯恐天下不乱；其他行业来标榜自己比 IT 行业更忙；科技圈的大佬们纷纷表态；朋友圈的老板们个个站队；自媒体们疯狂寻找角度解读，希望在这一轮热点中吸引流量；卫道士们站在自己的角度批判所有处于不利地位的观点，试图证明自己的道德正确性。\n我们习惯把一个人理解成单一的实体，但每个人有很多面。大佬们站在聚光灯下，展现出自己的另外几个面的时候，总有记性好的人，翻出之前的对立言论说：XXX 人设崩塌了……\n没有谁的角度是完全正确的，说来说去，引经据典都是那些话，真没劲。\n上周周会的时候，SAGITEAM 讨论了 996 的问题，我很明确的表明了我的观点：我反对低效、强制、形式主义的 996。\n搞不定是能力不行，效率低是管理太弱，靠加班就能搞定么？\n我也真的讨厌“福报”这个词。\n全文完 ","date":"2019-04-15","description":"","lastmod":"2019-04-15T06:07:21Z","slug":"no-996","tags":["life","management","sagiteam"],"title":"不谈996","url":"https://blog.zengrong.net/post/no-996/"},{"categories":["impressions"],"content":"\n最近遇到一些事，如鲠在喉，不吐不快。\n我上学的时候，有一次周五下午准备回家，发现不见了十块钱，我想可能是翻柜子的时候掉在地上了，就马上从校门口跑回宿舍寻找。当时宿舍里有一个隔壁宿舍的哥们，我问他有没有看到我柜子下面掉的十块钱，他说没有。因为经常串门，我没多想，没追问，也没注意分析他当时的表情，匆匆回家了。那时候，一餐中饭的价格是三块五，我每个月的生活费是两百块钱，十块钱省着吃可以吃两天呢。\n后来，这个隔壁宿舍的哥们被查出来盗窃，直接从课堂上被警察揪走了，我再没看到他。有一次同学聚会，听同学说他陆续进进出出监狱好几次，短的几个月，长的两三年。\n还是上学的时候，去学校的路上，祖孙两个人拦住我，说太饿了，能不能给点东西吃。我掏出了身上所有的钱给了他们。那可是一笔巨款，为了不破产我吃了好几天泡面呢。\n后来呢？后来这样的人在街上就越来越多了。Now，隔着电脑屏幕，我都能感受到当时那个十几岁男孩在知道真相后内心的迷惑、无助和痛苦。\n工作了很久之后，有一次我独自出差。火车是始发站，我又到的比较早，进入车厢的时候只有我一个人。我找到位于车厢后部的座位坐下开始阅读。一男一女从前门走进来，把手上的东西放在座位上，用武汉话和我打招呼：”您家能不能帮我们看一下东西？我们东西比较多，要下去再拿一趟。“\n我一下子警觉起来，看了他们一眼，犹豫了三秒钟。他们的表情从友好逐渐变成失望和漠然，那失望让我惭愧。我想我应该脸红了。但我最终没有回话，可能轻轻摇了摇头但不自知。我不知道一个本来可以不假思索答应的小事，为什么让我心里转过了那么多念头：他们是骗子么？他们留下的行李是什么？他们的意图是什么？他们是准备栽赃还是准备讹诈？\n后来呢？后来他们又拎了几大包行李上了火车。他们是善良的人。\n第一次创业坚持了五年。这是最好的五年，是一个青葱小伙变成油腻大叔的五年，也是一头茂密自然卷走向脱发小平头的五年，是我博客中技术文章最多产的几年，是我在知乎上最活跃的几年。团队洋溢着向上的力量，我们一路向前。翻了下之前的博客和知乎文章，很能代表我当时的状态：\n喜迎2014 游戏逻辑程序员的成长出路？——曾嵘的回答 我还找到一段当时发泄郁闷的话：\n有人说这些是无意义的事。\n从程序员的角度看，确实无意义。\n因为大多数程序员，根本不需要知道怎么修电脑做网线配 IP 地址。但当有一天光纤断了大家全都看着屏幕哈气的时候，我还能说这些工作“无意义”么？\n当然，修光纤是中国电信的事，接网线是我的事，其他人都在打乒乓球。\n大家不会关心你是怎么修好网络的，大家只会欢呼一声：哦，网通了！ 然后该干嘛干嘛去。\n这时候我一般是默默地收好压线钳，拿刷子刷一下交换机的灰尘，拿扫帚扫一下刚才剪掉的线头，继续回工位敲代码。\n创业后期，团队最困难的时候，我离开了。离开的痛苦持续了好几个月。\n因为这一次，我成了释放恶意的那个人。\n在百纳，我带的技术团队加上实习生有近百人。我记住每个人的名字和特点，尝试让每个人都能找到自己的价值。我试着让合理的技术、正确的流程、简洁的架构和顺畅的组织来加速团队的运转。\n我沟通的人越多，越发现：每个人心里都有一面墙。就像我在火车上竖起的那面墙一样，每一面墙都是冰冷的。这些冰冷的墙后有一个个或炽热，或聪慧，或自大，或脆弱，或温柔，或敏感，或直率，或纯真的灵魂。没有哪个灵魂是绝对纯化的，所有的灵魂都由多种特点杂糅在一起。那些墙有的坚不可摧，有的吹弹可破。有的墙光滑无痕，有的墙会在裂缝中透出一丝光。\n从 大五人格新力模型 来看，亲和性高的人应该更温柔，神经质低的人会更容易感受到恶意。没有两个人的感觉是完全相同的，这就造成了善意和恶意之间的差距。有些人认为表达了善意，在另一些人眼里并无差别。有些人做了自认为平常的表达，接受者会认为对方充满恶意。\n每个人都在渴望世界的善意，然而我们如此不同：我对善意无动于衷，你觉得善意毫无价值，他从善意中摄取利益。\n世界本身并无恶意。世界创造了我们。从善意的角度看，它对我们的态度是：”我爱你，与你无关“。从恶意的角度看，它的态度是：”毁灭你，与你何干！“。\n上面两句分别来自于 Kathinka Zitz 的诗歌 Was geht es dich an 以及刘慈欣的小说《三体》。\n最近重读了《三体》。《三体》里有几个很有代表性的人：\n一个成功的好人：罗辑。 一个失败的好人：程心。 一个成功的坏人：叶文洁。 一个失败的坏人：维德。 一个先成功后失败的好人：章北海。 一个自我放逐的浪子：云天明。 重读后，我最欣赏的角色没有改变。因为道德限制而最后失败的章北海怀着拯救人类的信念去暗杀航天权威，却无法在拯救自己战舰的最后关头下达攻击命令。相比而言，维德更加老道与决绝，他比章北海多了一丝兽性，少了一点人性，可他居然遵守了和程心之间的约定，导致自己被执行死刑。章北海秉持着善良之心，做出了大恶之事（针对被他暗杀的人）。维德承受了世界最多的恶意，保持了一颗信守承诺的本心。\n不必讨论绝对的标杆好人罗辑，来看看其他几个复杂的人：\n叶文洁承受了时代的恶意，释放的也是恶意。一报还一报，冤冤相报何时了。\n程心承受了人类的善意，释放了最大的善意。但她的几个重要选择对人类而言都是毁灭性打击。\n云天明对人类社会失去了全部希望，以大脑的形式被流放出了的地球（真凄惨）。他对人类释放出的所有内容全是善意的。他是一个纯粹的人，一个最善良的人，一个浪漫的诗人，一个优雅的浪子。\n第二次创业，我依然会接网线，但心态已经决然不同了。这根本不必被解释成恶意，只是一种我自己选择的工作方式而已。\n第一次创业只做技术，这次我了解所有的流程。我知道团队需要什么，知道现在该做什么。我了解我们在用的所有技术和流程，我能管理项目，我也能读懂人心。\n我真心地分享经验，不藏私；我也习惯了对方的遮遮掩掩。\n我知道成人的世界里只有利益，我知道需要出让一些利益才能换取一些利益。吃一些亏，交一些学费，我都能接收。\n但我还是低估了商业世界里的尔虞我诈和”白手起家“。我还是会为被欺骗这件事情感到很难受。\n是否我太相信 投我以桃，报之以李；我给你阳光，你就灿烂；我给你温柔，你回我善良 这些过于诗意的句子？\n并不是。\n是我身边太多善意的人，不计较得失帮助我； 是我合作过太多靠谱的商业伙伴，不计较利益信任我； 是我碰到太多热血的汉子和妹纸，一言不发支持我。 就算这个世界充满恶意，我依然要对她温柔以待。因为我知道，有那些温柔的人，在不远处等着我。\n我也要用世界的口吻说一句话：\n我给你温柔，与你无关！ 全文完 ","date":"2019-04-05","description":"","lastmod":"2019-04-05T00:37:45Z","slug":"gentle-for-world","tags":["life"],"title":"就算这个世界充满恶意，我还是要温柔以待","url":"https://blog.zengrong.net/post/gentle-for-world/"},{"categories":["impressions"],"content":"从去年 10 月写过一篇 微信小游戏：风口还是泡沫 之后，我就没有怎么写博客了。一是由于创业的心态下很难以平静之心写作，二是博客文章接近 900 篇，静态化 之后生成速度非常慢，烦躁之下就搁笔了。\n当然，前面说的都是借口。\n又是半年过去了，增加了一些思考。创业不能不焦虑，但也不能带着焦虑前行。学会和焦虑共存，摆正心态才能睡得着觉。今天开始，写点东西。\n关于技术难度 前几天 SAGITEAM 的运营总坚，很神秘地找湃总请教 HTTP 协议那些事。虽然我对运营的要求是要兼职做游戏策划，但还没有要求兼职写代码哇！正暗自揣摩，这哥们就把某个头部小游戏的排行榜给刷坏了。接着他如法炮制，成功玩坏了多个游戏的排行榜。\n这让我开始担心一件事（当然不是担心我们的排行榜被玩坏）：微信小游戏的 CP 到底是一群什么样的人？\n微信小游戏在游戏世界里可以对应超休闲游戏(Hyper Casual)这个品类。我在 AppStore 上也看到很多超休闲游戏的排行榜被玩坏过。要知道，普通的游戏玩家绝大多数都没有破解程序中校验算法的能力。一个头部产品的排行榜如此容易被玩坏，只能说明一点：它们的排行榜 API 没有做基本的数据校验。\n继续思考，作为一个程序员，应该了解数据校验虽然是基本的防护手段，却能防住绝大多数的破解者（因为这些破解者就是小白），但为什么不做校验呢？两个可能：\n不知道怎么做校验，甚至不知道需要做校验。 不认为安全性是很重要的事情。 如果是第一点原因还好理解，团队技术差嘛。如果第二点就细思极恐了，这又有两个可能：\n专心做有趣的游戏，不关心游戏之外的东西（排行榜坏了无所谓，反正我的游戏好玩） 没有技术敬畏心，技术不重要。 对于第一点，我虽然不认同，但内心是服气的。第二点就很让人无语了，这真是一个游戏 CP 么？\n技术上有一个维度压制的说法。特斯拉电动车能跑到 500 公里，其它的车企做不出来，这就是维度压制。但现在小游戏的 CP 们还处于同一个维度，没有谁能形成维度压制。无论大厂还是 SAGITEAM 这样的小作坊，大家的层级都没有拉开。大厂们还没有想清楚微信小游戏应该怎么做，它们的技术优势派不上用场。小作坊们倒是各有各的活法：换个皮，剽窃一下别人的代码，甚至能直接用别人的代码和资源混淆后重新上架。从另一个角度看，这种做法也未尝不是一种维度压制。\n回到 SAGITEAM 做的游戏上来，我们是不是有点过于关注技术本身了呢？我们第一个游戏“天才射手”就上了真人 PVP，产品改了3个版本，做了三十多个精美的角色，每个角色有 6 套动作，还有数不清的终结技和特殊技能。但玩家并不买账，我也一度据此作出“微信上不适合做对战游戏”的结论。但半年后的“站桩英雄”爆红，我被 PiaPiaPia 打脸。\n用事后诸葛亮的方法来看，射手这个产品的失败的原因，并非在于是否真人对战，而是没有认真抓住玩家心态。一味炫技自嗨是危险的。玩家不会关心游戏用了哪些技术。PVP 帧同步？行为预测算法？光线追踪？拉倒吧，大部分玩家甚至都分不清游戏里的对手是 AI 还是真人。玩家只要觉得简单好玩易上手就会留下来。\n上面天才射手主界面的那些系统，都需要吗？对于一个都没有怎么玩过游戏的普通微信用户来说，进来之后就立刻蒙圈了，说不定随手点了一个 ICON 就跳出了呢！\n关于“玩家质量堪忧” SAGITEAM 经常打趣说这一届“玩家质量堪忧”。这不是玩家的问题，而是我们的问题，是我们思维方式的大问题。我们在技术和美术上专业度足够了，在“好玩”这件事情上还相当嫩。\n到了现在，我们已经知道：在不同平台，玩家的需求是不一致的，不能用相同的游戏设计思路去面对不同的玩家。玩家会用脚投票，不好玩的游戏，玩家离开你甚至都不会挥挥衣袖，更别说带走一片云彩。\n做好微信小游戏这个平台，需要深挖玩家的需求，做贴合玩家需要的产品。做一些“真正的小游戏”。早点放下自己“专业”的架子，进入“接地气”的阶段，才能做出爆品。我们需要把心思放在如何“更好玩”，而不是“更专业”上。\n什么是“真正的小游戏”呢？\n下面这两个游戏的主界面，就很让人有开始的欲望。界面足够简单，也不会让人小白用户产生选择困难，他们一进来就知道该从哪里开始，也知道在哪里可以免费领到奖励。\n世界上有两种人，他们都高瞻远瞩、高屋建瓴，但一种人的脚踏在地上，另一种人的脚悬在空中，前者叫巨人，后者叫鸟人。\n我们之前就是鸟人。\n关于深耕品类 网易的“家园大改造”这种精品游戏，是否能收回成本呢？我不得而知。但小作坊里却有不少几人团队通过深耕一个小品类实现盈利的例子。\n看看现在有多少切水果？还有把切水果变成切菜然后融合做菜的小游戏，我怀疑它能不能收回美术成本……\n这样做出来的游戏，CP 自己真的会喜欢玩么？如果自己都不喜欢玩，怎么让玩家也喜欢玩呢？怎么锻炼团队技能呢？怎么提升团队产品质量呢？怎么让做游戏的人也开心呢？\n我知道，有方法可以让套路满满，批量生产的游戏取悦没有什么游戏经历的小白微信用户。可是，怎么套路自己呢？\n我理解的深耕品类，应该是在一个品类里面不断创新。可以借鉴，但不能照抄。微创新是需要的，感动玩家是必要的，感动自己是必须的。\n关于套路 Banner 误点、Banner 移动、假分享这些小套路是没有能力的 CP 为了掩盖产品的无力而想出来的运营点子，反而被奉为圭臬，被大量游戏学习。这和流量明星们一戏万金却没有演技，老戏骨拿着微薄薪酬却默默无闻的现状何其相似！\n我们也看到有些产品不含套路，甚至没有 Banner，依然能做到高留存和高收入，靠的就是“真心好玩”。\n是的，SAGITEAM 的游戏也有套路，但我们正在尝试怎样才能不套路。\n在绝对的力量面前,一切阴谋诡计都没有用。\n希望 我只希望，SAGITEAM 做出来的游戏，让玩家能真心地说一句“真 TMD 好玩”。\n然后我们就可以挺直腰杆说出那句话：Thank you for playing our games.\n对于上来就骂骂咧咧说 “这游戏广告太多了，不玩！“ 的玩家，我们只会说出一个字(附带标点符号)：\n滚！\n全文完 ","date":"2019-03-31","description":"","lastmod":"2019-03-31T08:34:09Z","slug":"minigame2","tags":["game","sagiteam","entrepreneurship"],"title":"做个靠谱的微信小游戏 CP","url":"https://blog.zengrong.net/post/minigame2/"},{"categories":["impressions"],"content":"《拆除你的情绪地雷》 很多时候，烦恼来自于自己有意无意地以不理性的方式思考，产生了不健康的负面情绪，然后又以消极的方式行事。转变的核心就是要无条件地接纳自己、他人以及这个世界。\n一、理性情绪行为疗法 表面上看，是1导致了3，实际上，2更为重要。\n挫折或阻力 自己的想法，尤其是不理性的想法 挫折和不理性的想法造成的焦虑 如果1只是一些普通的倾向或者愿望时，你会产生不愉快但健康的情绪，例如后悔、遗憾，对自己不满。这些情绪可以化作动力，为你带来后续的努力。\n如果用力过猛，把愿望变成了一种必须达到的要求，则会压迫身心，产生不健康和毁灭性的严重负面情绪。\n首先需要承认自己的这些想法不够合理（不理性想法），接着要反驳自己，直到将强迫性要求转变为健康、理性的想法。对事不对人，只评判自己的行为，而不去评价自己本身。\n二、如何把不理性的想法转化为理性的想法 无条件地接纳自我，不要评价自己的人格。可以承认自己做了错事和蠢事，但不要认为自己是一个蠢人或者笨蛋。 无条件地接纳他人。可以去讨厌别人做的事，讨厌别人因为能力或习惯原因做不出来的事，但不能强加自己的想法，也不能否定他们的存在价值。 接受现实，坦然面对挫折。麻烦永远在那里，我们无法选择或者改变，唯一可以选择的是面对麻烦的时候是否产生负面情绪。 三、如何通过行为反驳不理性的想法 尝试冒险，做自己没有做过或者之前不敢做的事情。 强化技巧。强迫自己做更愉快的事情之前去完成一些不喜欢做的事情。然后再做开心的事情作为奖励。 惩罚自己。例如强迫自己吃难吃的事物，或者做些很烦人的家务。 使用非命令式，冷静、自信的语言。 寻求积极的改变。无论改变有多么艰难。 养成良好的，健康的个人爱好。 《灰度决策：如何处理复杂、棘手、高风险的难题》 灰度问题不一定要同时满足复杂、棘手和高风险等所有标准。但是它一定是没办法轻易下结论的。非黑即白的事情在现实世界中少之又少，而且没有决策的必要。灰度决策能力是管理者的基本素质。\n灰度问题没有完美的解决方案。也许穷尽我们的智慧，都很难找到一个对所有人都好的方法。更多的时候，只能在 糟糕和更糟糕 之间选择糟糕，做一个 NO BAD 的决定。这要求决策者在正向思考之后，还需要反向思考，明白自己的 底线在哪里 ，什么事情是可以接收的，什么事情是绝对不能接受的。底线不同，决策就不一样。但无论结果如何，都要做一个不后悔的决定。\n一、过于相信自己很危险 1997年克林顿国情咨文邀请了一位工厂大火后决定重建工厂并在重建期间坚持发工资的f纺织厂管理者，该管理者获得了极大的社会荣誉。但这家纺织厂随后破产了。\n人性是有弱点的，灰度决策中，最容易干扰决策者的弱点就是高估自己。高估自己容易让人盲目和冲动。\n研究人员调查美国人认为死后的几个人物是否会上天堂。获得诺贝尔和平奖的特蕾莎修女以72%的支持率位居第二，而被调查者(YOUSELF)的支持率是89%。看来大家都相信自己的品格高尚到足以上天堂。\n避免决策失误\n画出决策树：A 罗列所有的选择项 B 把这些选择能带来的后果（直接和间接的）全部写出来 储备解决灰度问题的专家团。多观察自己身边的人或团队中的员工，谁能在处理模糊事件的时候判断准确又谨慎。谁更擅长跳出问题提供新的解决视角。 开会的时候指定人专门负责唱反调。避免“老板总是对的”效应。 二、只重视结果会带来更大的损失 范例：电车难题、杜鲁门对原子弹的看法，如何辞退一个工作能力下降的老员工。\n决策时要考虑以下三点的综合利益：\n追求结果利益最大化 考虑社会规范和道德 不能忽略人性 三、不能简单、绝对地坚持把事情做好 可能会碰到的问题：劣币驱逐良币、关系户、公司政治\n《君主论》的作者马基雅维利对世界的三个评价：\n不可预测 不受控制 非常混乱 在这个世界中做决策，需要避免非此即彼的思考方式。公司政治、关系户等问题并不是某一个实体所独有的，你无法逃避。应该寻找能满足利益相关方的办法，同时要保护自己的利益不受损失，始终寻找最有可能达成一致的解决方案。\n全文完 ","date":"2018-10-29","description":"","lastmod":"2018-10-29T23:25:38Z","slug":"reading2672","tags":["reading","readingnote"],"title":"读书笔记：《拆除你的情绪地雷》《灰度决策》","url":"https://blog.zengrong.net/post/reading2672/"},{"categories":["impressions"],"content":"小游戏从 4 月 4 日发布到现在，已经过去了整整半年时间。排在阿拉丁小程序榜单上前五的小程序，已经有三个是小游戏。虽然小游戏热度一直在上涨，但微信的管控也越来越严厉，加上版号政策的不明朗，多种因素让小游戏的盈利能力备受质疑。那么，当前对于小游戏创业者来说，到底是风口还是泡沫？\n作为小游戏创业者，我基于这几个月观察到的行业现象随便聊聊。观点肤浅，数据空洞，纯属自娱自乐。\n先来看见微信对小游戏所做的政策调整：\n微信的政策 小游戏正式发布一个多月后的 5 月 17 日，为了解决愈演愈烈的诱导分享问题，微信对分享功能进行了调整，取消获取分享成功消息，和获取群 ID 的功能。不过这个功能直到 10 月 10 日才会被完全禁止掉，而且不会影响这之前已经发布的小游戏。\n接着，微信对授权接口进行了限制，开发者需要使用组件方式唤起登录授权弹窗，这意味着必须让玩家交互一次才允许获得登录授权。\n8 月 17 日，微信发布了 《致小游戏开发者的一封信》，强调小游戏的本质是创意，确定小游戏平台的目标，是成为一个汇聚创意并且让创意体现价值的平台。\n接下来，小游戏增强了投诉功能，公布了 《小游戏发布内容规范》，接连发布了两辑《小游戏“免坑手册”》，封禁了一批踩线的小游戏，下架了一批涉嫌抄袭的小游戏。\n就在国庆长假前一天，微信发布了 《“小程序跳转小程序”功能调整》，限制跳转功能必须由用户触发，并要求在小程序发布时设置跳转白名单，白名单的上限为 10 个，且不允许动态更新白名单。这是在将大刀挥向小游戏盒子们了。\n微信的这些措施当然是有效的。现在微信群里分享小游戏卡片的情况已经越来越少。这些措施增强了微信用户的体验，却无疑让小游戏的开发者们过得愈发艰难了。\n流量 和小游戏圈子里的人聊天，不出十句话，你一定能听到这个词：“裂变”。“裂变”这个词其实是唬人的说法，说平凡点就是“用户主动分享”罢了。上面谈到的微信政策，有些就是针对无节制的诱导分享来制订的。除了政策上的限制，微信在审核这一关也对诱导分享进行了特别关照，违背微信的分享规则的产品，无法通过审核。“裂变”是越来越难做了。\n裂变的目标是“廉价的流量”，让用户能主动分享为小游戏带来新的用户，在不花或者少花买量费用的情况下实现游戏的用户增长。微信虽然限制了“诱导分享”，但并没有限制“分享”行为本身。通过对不同年龄阶段和产品目标用户属性的分析，经过精心的设计，还是能得到不错的效果的。\n比如下面这些的效果就不错：\n哎呀，不好意思给错图了……��\n游戏质量 小游戏的开发还是挺简单的，游戏的玩法相对容易，游戏素材也不多。中国有最好的 HTML5 游戏开发引擎 Cocos/Egret/Laya ，它们都对小游戏开发做了贴心的适配。这些优秀的工具也降低了小游戏开发的门槛，从另一个角度促进了小游戏行业的快速发展和繁荣。\n但事情到后面变了味，有人把优秀的工具和简单的玩法造成的开发时间大幅缩短作为了宣传手段。“3人小团队，3天开发，日活过百万”，仿佛一个游戏开发时间超过3天就很 low ，就拿不出手，仿佛 3 天做出一个小游戏是很值得吹捧，是超一流牛逼似的。拜托，你几个月做了 200 个小游戏，有啥用呢？数量要是能取胜，为啥市场要期待精品？\n我们团队内部把交互简单，开发快速的小游戏统称为“七天小游戏”，意味着这类游戏能用 2 个人（包括程序、美术、策划）在 1 周时间内开发测试完成并上线发布。目前微信平台上的绝大部分小游戏都是七天小游戏。\n“七天小游戏”这个词本身并没有贬义，用心做，七天小游戏也做出精品。通过研究游戏数据并发现用户痛点，持续调优，看着一款游戏的数据不断增长至爆款，需要花的时间远远不止七天。但目前大多数的微信小游戏并不是这个思路。纯单机，开了流量主之后就不再维护，粗制滥造，疯狂换皮，只注重广告不注重体验，这样的做法只会留下一堆堆小游戏垃圾。\n什么样的团队容易产出小游戏垃圾呢？主要是这样三种：\n以流量为主的公司，做的是流量生意，没有自己的技术团队，通过为已上架的小游戏导量分成的方式运营。 看到小游戏红利一头扎进来的公司，需要快速大量出产品，快速组建开发团队，快速上线小游戏。 个人开发者和小规模团队，抱着试水的想法快速发布产品。 以上团队没有能够做出精品游戏的基因。贪多求快，原封不动地抄袭，占山头卖流量拿红利，不会做出好产品。还是那句话，如果快有用，为啥市场要期待精品？\n李开复在他的新书《AI未来》里说:\n中国的创业者竞争更加激烈，高密度的竞争珀斯公司迭代产品、调整方案、发明商业模式。在模仿和借鉴司空见惯的的市场上，创业者只能选择比竞争对手工作得更努力，执行得更到位。\n可惜，“努力”和“快”并不能划等号，“执行到位”也不是“数量众多”可以取代的。\n为什么有人喜欢用“开发快”做宣传呢？我觉得主要的原因是大多数的小游戏团队实在是太烂了，烂到都没办法用几天时间做出一个小游戏，所以才把正常水平说成超常发挥。上面讲到的三种团队要么想着流量变现，要么想着快速发布，在这样的前提条件下很难架构起优秀的团队。好产品是磨出来的，好团队是吵出来的，好基因是可以锻炼出来的。但这一切碰上“资本”和“红利”，就只能给速度让路。\n看看现在最赚钱的几个微信小游戏，都是有好几年 HTML5 游戏开发运营经验的团队做出来的。长期的经验积累有加成效应，微信小游戏就是这些团队的爆发时机。\n情况在好转。现在精品小游戏越来越多了。两个月前，市场上还鲜有使用 3D 技术的微信小游戏出现，而现在已经出现了不少。不说黑洞和台风系列，一些七天小游戏也采用了 3D 技术，体验还不差。这说明，小游戏行业在向着良性发展，也开始有优秀的团队在尝试进入这个领域。\n盈利 小游戏的付费是个硬伤。版号从3月停发到现在，连腾讯的小游戏“欢乐枪战”为了版号都改名成“保卫豆豆”了。前面提到的有积累的团队的优势在此时就体现出来了，他们可以把之前的产品直接移植到小游戏平台，使用现成的版号继续内购。微信小游戏的整体体验比 HTML5 更好（虽然某些方面的性能要差很多），玩家的付费体验也更加流畅。\n没有版号积累的团队，就只能依靠广告。微信为了吸引开发者，把 2018 年的广告分成调整到了 50%，这对个人开发者来说并无甚EGG用。一个小游戏开发出来，使用个人资质上架，需要先提交软著。办理软著需要近千元的手续费。如果开发能力强，能达到一周开发一个小游戏的水平，办理软著就要选择加急的方式，付出更多金钱才赶得上游戏发布的时机。游戏上线后需要积累到 1000 用户才能开通流量主。如果游戏的名称不够“吸量”，无法引来较多的自然流量，开发者个人的朋友圈又没有足够的影响力，开通流量主就需要买量，这又是一笔千元的成本。好不容易开通了流量主，接入了广告准备变现，发现一天几百的自然流量只能支持几块钱的广告收入，想收回之前投入的成本都遥遥无期。\n上面说到的还仅仅是一个小游戏。如果用两个月做 200 个小游戏呢？想想成本吧。“3人小团队，3天开发，日活过百万”是一个口号，喊出来的为什么不是你？\n微信用户并不是真正的游戏玩家。来看看暑假之后的用户量走向，你能接收每周数据只有周末两天能看的事实么？我最近都在琢磨着是不是要开发老人向游戏，这是很可能赚大钱的。\n机会 回到文章标题，题目起得有点大了，并不是这样一篇小文可以承载的。现在的作者们真的是为了博眼球啥标题都敢乱搞，世风日下，人心不古。\n小游戏既不是风口也不是泡沫，它就是个机会，是一个让有准备，有预谋，有理想，有道德的团队走向成熟的机会。沉下心来，多出精品，它就是能成为你的风口。\n本文观点肤浅，数据空洞，纯属胡扯。\n全文完 ","date":"2018-10-07","description":"","lastmod":"2018-10-07T12:34:40Z","slug":"minigame","tags":["game","sagiteam","entrepreneurship"],"title":"微信小游戏：风口还是泡沫","url":"https://blog.zengrong.net/post/minigame/"},{"categories":["impressions"],"content":"1.《从优秀到卓越》 zrong 感受最深的部分： 第五级经理人、团队建设、刺猬理念、技术不是决定性因素\n1.1 姊妹篇 《基业长青》 《从优秀到卓越》 《再造卓越》 《选择卓越》 1.2 样板公司标准 30年以上历史 前15年平庸，后15年卓越 累计股票收益率达到市场平均水平的3倍以上（可口可乐沃尔玛等只有2.5倍） 没有包含高科技公司（时间段原因） 排除公司处于行业风口的情况（若整个行业收益率大大高于市场平均水平则排除这个行业） 寻找1965-1995这30年间，登上过财富500强中的1435家公司。找到了11家符合标准的样板公司。\n又找到11家对照公司（没有实现跨越式发展）和6家对照公司（昙花一现）\n1.3 实现跨越的关键要素 人员 战略 动力机制 zrong：宁高宁把企业创新的动力系统，归纳为四个具有递进关系的要素，分别是：原始动力系统，立体动力系统，职业动力系统和信仰动力系统。越往后对员工的要求越高。原始和立体两种动力系统处于“管理”阶段，只需要管理人员进行自上而下的管理，就能实现企业动力的提升。而职业和信仰两种动力系统处于“自发”阶段，必须员工进行自下而上的成长才可能提升。有趣的是，后两种动力系统必须建立在物质回报的基础上。员工的价值没有被承认，不可能去考虑职业和信仰。\n1.4 第五级经理人的特点 拥有双重人格：极度坚定的意志，极度谦虚的为人 公司成功高于个人成功 遇到功劳归功于团队，遇到问题反省自己。 重视团队的建设 紧紧盯住最强大的对手，制定高标准的计划。 zrong：第五级经理人时刻考虑的是“公司”的利益，第四级经理人考虑的是“个人”的利益。撇开经理人不谈，达到第五级思考方式的人就很少，这真的是取决于目光和眼界。许多人偏安一隅，在没有监管的情况下很难做到自我反省和成长。许多人关注当下利益，无法制定出高标准的计划。\n1.5 第四级经理人的特点 公司里的“独裁者” 秉承1个天才加1000个助手的模式。 凭一己之力让公司实现利益 有意无意选择无能的接班人 离开公司后公司迅速衰落 1.6 团队建设 卓越的公司需要卓越的管理团队 不是战略要求决定招募的人才，而是招募来的人才决定公司战略方向（先人后事） 公司是一辆客车，领导人是司机，请合适的人上车，一起商量开到哪里去。 zrong：公司是一辆车，要找到何时的人。最重要的问题是人，要花大量的精力去找到和公司志向一致的人。战略方向可以变，但公司的理念和价值观不能快速变化。\n1.7 刺猬理念 古希腊谚语：狐狸的招数再多，也有无计可施的时候。刺猬只会一招，却没谁奈何得了它。\n我们最擅长干什么事 我们对什么事最感兴趣 怎样才能从这件事中获得最大回报 zrong：一头扎进去，做有价值的事情，做我们最擅长的事情。\n1.8 技术不是决定性因素 技术变革是加速器 若没有找到跨域的方法，技术变革不可能成为第一推动力 zrong：技术并没有技术人想象的那么重要，它必须和商业价值合并才能起到加速作用。我们碰到的许多互联网产品底层技术都非常简单（早期），但这并不能阻碍他们盈利。\n1.9 飞轮效应 整个跨越过程中，不可能找出某个见证奇迹的时刻，或者某个一抓就灵的手段。 需要快成的持续不断向同一个方向的力，一个行动接着一个行动 飞轮很重，开始的推动并不明显 飞轮一旦开始滚动，就会不断积累动量，借着惯性越滚越快 如果不愿意下苦力去推动轮子，而是搞事情，碰运气，不断换方案，没有形成合力，连续的挫败耗干了员工士气，飞轮就会变成“厄运之轮” 2. 《情绪勒索》 2.1 识别情绪勒索（6个特征） 要求 抵抗 压力 威胁 顺从 旧事重演 2.2 情绪勒索的恶性循环 打击对方的自我价值感 引发对方的罪恶感 剥夺对方的安全感 2.3 什么样的人最容易被情绪勒索 自我价值感低的人 受到服从权威影响的人 2.4 如何摆脱情绪勒索 停：停止情绪勒索者的对话，尽快远离现场 观：观看自己的情绪和别人对待你的方式 应：给出坚定的回应，告诉对方你的真实想法 3. 《自我导向行为》 zrong：只要意识到了有些行为需要改变，就应该去改变。\n3.1 改变行为的三个关键点 认识情境 改变行为 强化结果。 3.2 对行为发生的情境进行仔细的观察和记录 结构化日志包含的三要素：情境、行为、结果。 记录的重点是导致某个行为发生的原因，而不是行为的结果。 3.3 建立新行为的四种方法 用积极的自我陈述代替消极的自我陈述。 进行想象预演。 模仿榜样。 塑造，逐次逼近。拆解大的目标。 3.4 对新行为进行强化管理 找到强化物 进行奖励而不是惩罚 制定一份计划 全文完 ","date":"2018-10-03","description":"","lastmod":"2018-10-03T03:34:04Z","slug":"reading2670","tags":["reading","readingnote"],"title":"读书笔记：《从优秀到卓越》《情绪勒索》《自我导向行为》","url":"https://blog.zengrong.net/post/reading2670/"},{"categories":["technology"],"content":"本文作者： 射手座团队 蟹老板\n在《猎头专家》里，站得高不一定是好事，掉坑里也不一定是坏事，大家应该都体会到了，地形的重要性不言而喻，今天就来聊聊地形在《猎头专家》里的实现。\n设计目标 实现容易（简单） 生成灵活（新奇） 难度可控（又好玩） 设计思路 地形在游戏的战斗场景中长度固定且需要考虑画面外的碰撞，所以我们的地形没有使用渐进式生，而是在战斗场景初始化时就全部生成完成，在线对战时，会预先生成地形数据发送给双方。\n地形的组成 对于 size 这个单位，1 代表 128x128 像素的矩形贴图。\n**平路：**type=0，size：1x1，插件：RigidBody, PhysicsBoxCollider **小坡：**type=1，size：3x2，插件：RigidBody, PhysicsPolygonCollider 红色的圆点是顶点，组成多边形碰撞盒，顶点越多越平滑，当然性能也会受影响 **大坡：**type=2，size：4x3，插件：RigidBody, PhysicsPolygonCollider 红色的圆点是顶点，组成多边形碰撞盒，顶点越多越平滑，当然性能也会受影响 我们游戏中的地形仅使用了Box2D自带的插件，没有使用第三方插件，小坡与大坡的原理是一样的，主要是视觉上的差别，如果你愿意可以做出更复杂的地形。 坡路的设计思路 坡的角度为45度，这样保证了物体滑落时的感觉比较自然。 坡底和坡顶拐角处都包含了一块平路并且设置了2个顶点，确保物体滑落时的平滑感。 坡度和坡顶都是平路可以和”平路“地形无缝连接。 地形数据格式 从下面的配置文件片段中可以看出，如果type为负值，则表示翻转，那么 上坡 素材就会变成 下坡 素材。\n1[ 2 {\u0026#34;type\u0026#34;: 0, \u0026#34;x\u0026#34;: 0, \u0026#34;y\u0026#34;: 0}, 3 {\u0026#34;type\u0026#34;: 0, \u0026#34;x\u0026#34;: 28, \u0026#34;y\u0026#34;: 0}, 4 {\u0026#34;type\u0026#34;: 0, \u0026#34;x\u0026#34;: 256, \u0026#34;y\u0026#34;: 0}, 5 {\u0026#34;type\u0026#34;: 1, \u0026#34;x\u0026#34;: 384, \u0026#34;y\u0026#34;: 0}, 6 {\u0026#34;type\u0026#34;: 0, \u0026#34;x\u0026#34;: 768, \u0026#34;y\u0026#34;: 128}, 7 {\u0026#34;type\u0026#34;: -1, \u0026#34;x\u0026#34;: 896, \u0026#34;y\u0026#34;: 0} 8] 地形的控制 地形组件有一下几个参数：\n战场宽度：目前是一个固定宽度8000px 最大高度：地形顶部可触及高度 最小高度：地形底部最低高度 最大连续平路：连续平路的最大数量 最小连续平路：连续平路的最小数量 参数2、3控制地形的最大落差，落差越大越可能出现大坑。\n参数4、5控制平路的最小与最大长度，平路少，则容易掉沟里，有利有弊吧，这两组参数决定了地形的难度，坡路地形会根据高度的变化自动翻转，实现上坡和下坡的无缝连接。\n地形的美化 刚才提到的地形生成，仅仅填充了角色脚下的那一排地面。还有很多空白区块需要填充，我们用一个 size 1x1 的地形同色纹理来补充，并随机补上一些装饰用的纹理。\n不管是地形组件还是地形纹理，都按颜色风格分组为多个plist，在战场生成时，随机调用。\n最后附上战场地形实现示意图 初始地图：没有经过纹理填充时\n美化过的地图\n为优化填充性能，填充是按自上而下找出整块区域使用平铺的方式填充，以节省Sprite的使用数量。\ngound.ts 部分代码实现 1@ccclass 2export default class Ground extends cc.Component { 3 // 纹理的 plist 4 atlas: cc.SpriteAtlas = null 5 // 纹理组件 6 tile: cc.Prefab = null 7 // 平路组件 8 flat: cc.Prefab = null 9 // 小坡组件 10 ramp1: cc.Prefab = null 11 // 大坡组件 12 ramp2: cc.Prefab = null 13 14 @property(cc.Integer) 15 sceneWidth: number = 8000 16 @property(cc.Integer) 17 maxHeight: number = 5 18 @property(cc.Integer) 19 minHeight: number = -2 20 @property(cc.Integer) 21 maxStraightParts: number = 4 22 @property(cc.Integer) 23 minStraightParts: number = 2 24 // 起始方向：1为朝右(正)，-1为朝左(反) 25 @property(cc.Integer) 26 direction: number = 1 27 // 起始高度(台阶) 28 @property(cc.Integer) 29 stairs: number = 0 30 31 // 每个体积单位的实际像素 32 sizeUnit = 128 33 34 onLoad () { 35 // 地形使用了缓存类管理（内部使用的是cc.NodePool） 36 this._nodeCacheManager = new NodeCacheManager() 37 } 38 39 // 设置纹理主题（配合背景），之后动态更换组件的 spriteFrame 40 setAtlas (atlas) { 41 this.atlas = atlas 42 } 43 44 // 根据server发来的地形数据动态生成 45 onSyncGround (groundData: any[]) { 46 for (let i = 0; i \u0026lt; groundData.length; i++) { 47 // 取出每一块的数据 48 let prefabData = groundData[i] 49 // 根据地形类型取出组件并实例化到界面上 50 let prefab = this._getPrefab(prefabData.type) 51 let node = cc.instantiate(prefab).addTo(this.panel, -1) 52 node.getComponent(cc.Sprite).spriteFrame = this.atlas.getSpriteFrame(prefabData.type) 53 // 坡路可能需要翻转 54 if (prefab.size.h \u0026gt; 0) { 55 node.scaleX = prefabData.type \u0026gt; 0 ? 1 : -1 56 } 57 node.x = prefabData.x 58 node.y = prefabData.y 59 60 // 每铺一块地形，计算出下块地形坐标 61 this.startX += prefab.width * this.sizeUnit 62 // 如果是反向，则坡路由于锚点问题会造成偏移，所以此处修正一下 63 if (this.direction \u0026lt; 0 \u0026amp;\u0026amp; prefab.size.h \u0026gt; 0) { 64 node.x = this.startX 65 } 66 // 根据当前地形坐标及大小创建它下面的装饰纹理 67 this.createTiles(prefab.size.w, prefab.size.h, node.x, node.y) 68 // 超过屏幕宽度后退出 69 if (this.startX \u0026gt; this.sceneWidth) { 70 break 71 } 72 } 73 // 地形创建完成后清除缓存 74 this._nodeCacheManager.clear() 75 } 76 77 // 平铺空白处 78 createTiles (w, h, x, y) { 79 // 在每块地形底部向下铺一块纹理（不超过minHeight） 80 // ... 81 // 随机创建其它风格样式砖块 82 // ... 83 } 84 85 // 以地形最底部的y点（即 minHeight 之下）算出最大平铺纹理区域 86 private _createMainTile (y: number = 0) { 87 let stails: number = Math.floor(y / 128) 88 let mainTile = this._nodeCacheManager.createNode(this.tile) 89 let sprite = mainTile.getComponent(cc.Sprite) 90 sprite.spriteFrame = this.atlas.getSpriteFrame(\u0026#39;tile\u0026#39;) 91 let height: number = (5 + stails) * this.sizeUnit 92 mainTile.setContentSize(cc.size(this.sceneWidth, height)) 93 mainTile.x = 0 94 mainTile.y = y + this.minHeight * this.sizeUnit - height 95 this.panel.addChild(mainTile, -2) 96 } 97} 全文完 ","date":"2018-07-27","description":"","lastmod":"2018-07-27T07:15:09Z","slug":"youshootfirst4","tags":["game","html","youshootfirst","sagiteam"],"title":"《猎头专家》地形和背景实现","url":"https://blog.zengrong.net/post/youshootfirst4/"},{"categories":["technology"],"content":"本文作者： 射手座团队 阿森\n序 游戏总杜绝不了 BUG，每当出现一个 BUG 时，程序猿们总是需要搅破脑汁去解决，这是一件很不愉快的事情。但如果 BUG 的出现暴露出了更严重的其它问题时，也算是不幸中的万幸了。我们在《猎头专家》的运营过程中就遇到过这么一个事。\n《猎头专家》中有一个 无限模式 的玩法，可以用有限的复活次数不停挑战难度越来越高的电脑关卡，技术好的玩家一般能打到十多层。但某次更新版本后，我们发现有个玩家居然挑战到了 23 层，而且复活次数为 0 ，我们整个团队都震惊了…… 为了研究玩家如何做到的，我们也进入无限模式不断尝试，结果发现这种奇异的现象居然是由于一个 BUG 导致的！！！\n出现 BUG 的原因是，上次版本更新导致复活次数没有正确累计，玩家可无限复活并不消耗复活次数。但当我们达到 无限模式 30 层希望更进一步的时候，游戏崩溃了！！！鉴于无限模式不停切换地图与角色的特点，我们把问题的矛头指向了内存泄露。原来那个达到 23 层的玩家之所以没有打得更高，是因为手机比我们的差啊……\n我们使用的一直是 CCC 推荐的释放资源的方法，在切换地图与人物后，将“所有未用到的已加载资源”通过 cc.loader.release 方法释放掉。但在深入了解、不停地观测 cc.loader._cache 中所有加载的资源条目之后，我们发现手动释放掉的资源只是冰山一角————大量依赖资源并未被同时释放，而这些资源占用了较多的内存。我们先尝试了一个简单的解决方案：使用接口 cc.loader.getDependsRecursively 获取到资源的依赖关系并将其释放，但这个方案并不好用。因为某些资源间的互相引用，极有可能导致资源错误\b释放，出现更严重的问题。为此，我们设计了一个动态资源管理模块，让单个功能模块或界面或场景，能够更简单的管理自己所使用的资源，如下：\n1// 加载资源： 2this.loader.load(path, sp.SkeletonData, (skeletonDatas) =\u0026gt; { 3 // xxx 4}) 5 6// 释放资源 7this.loader.release() Cocos Creator(CCC) 资源加载特性 在介绍动态资源管理模块之前，我们先需要了解一下 Cocos Creator 的资源加载机制与其特性。依据官方提供的相关文档 获取和加载资源 与简单的 DEMO 测试，我们对 Cocos Creator 的资源加载特性做一个简单的总结：\n资源动态加载都是异步的。在处理资源加载与释放时，就需要考虑加载中的资源如何释放的问题。 资源是互相依赖的，指定加载资源时，也会加载其所有依赖项。在处理资源释放时，需要考虑资源的依赖关系。 动态加载的资源都是不会自动释放的。就算切换场景，动态加载的资源依然需要手动释放。 cc.loader.getDependsRecursively 接口可以获取到资源的所有依赖项，包括依赖的依赖。这个接口可以让我们方便地获取到资源的依赖关系。 cc.loader.getDependsRecursively 接口获取到的数据，是每个资源对应的唯一的 reference id ，该值可以通过cc.loader的私有方法 _getReferenceKey 获取。释放时使用 cc.loader.release 直接传入资源的 reference id 进行释放。 按路径加载或释放资源时，需要指定目标资源的类型（简单的配置文件除外）。释放资源时，如果该路径下有多种资源类型（比如 spine 动画相关文件有 json/png/atlas ），你将不知道它会释放什么资源，而且释放也不完全（只会释放其中一种资源）。 类似于上一条，在使用 cc.loader.getDependsRecursively 接口获取依赖项时，不要使用文件路径作为参数获取。 《猎头专家》资源概况 在《猎头专家》小游戏中，除去开启页资源外，其它所有资源均为动态加载，即资源文件绝大部分都在 resources 文件夹中存放。全局只有一个主场景，游戏过程中没有场景的切换。这种结构既有优势也有劣势：\n优势：\n全局脚本的挂载更容易 小游戏初包尽量小 不会出现自动加载资源与动态加载资源间的互相依赖问题（这个会加大游戏过程中的资源管理难度） 劣势：\n必须有完善的释放机制 在适合的时候需要释放未使用的资源来减轻内存压力 为了满足《猎头专家》资源管理的诉求，我们设计了一套资源管理模块，仅供大家参考。\n资源管理模块设计及实现 资源单项加载与释放 资源加载时需要指定资源类型（配置文件不需要），于是在加载资源时，我们选择了资源分类加载。而为了方便资源释放，在资源加载完成后，需要记录所有加载到的资源，包括其依赖项。\n因此，我们设计了一个专用于单个资源加载的类 LoaderItem ，有以下几个主要属性:\n1class LoaderItem { 2 isReleased: boolean = false // 是否已被释放 3 urls: string[] = null // 加载项列表 4 type: typeof cc.Asset = null // 加载资源类型 5 resources: Object = null // 所有使用资源的reference id 6 maxRetryTimes: number = 0 // 最大重试次数 7} 在资源加载完成时，记录 LoaderItem 对象所有使用到的资源（包括自身），具体实现如下：\n1type SUCCESS_CALL = (res:any[])=\u0026gt;void 2type FAILED_CALL = (err:Error)=\u0026gt;void 3type ERROR_CALL = (error:string)=\u0026gt;void 4type PROGRESS_CALL = (completedCount: number, totalCount: number, item: any) =\u0026gt; void 5 6/** 7 * 缓存已使用资源 8 * @param resource 缓存单个资源的所有使用资源 9 */ 10private _cacheRes (resource: any) { 11 let loader: any = cc.loader 12 this.resources[loader._getReferenceKey(resource)] = true 13 for (let key of loader.getDependsRecursively(resource)) { 14 this.resources[key] = true 15 } 16} 17 18/** 19 * 开始加载资源 20 * @param successCall 加载成功回调 21 * @param failedCall 加载失败回调 22 * @param progressCall 加载进度回调 23 */ 24load (successCall: SUCCESS_CALL, failedCall:FAILED_CALL, progressCall:PROGRESS_CALL) { 25 let completedCallFunc = (error: Error, resources: any[])=\u0026gt;{ 26 if (!error) { 27 for (let res of resources) { 28 this._cacheRes(res, errorCall) 29 } 30 successCall \u0026amp;\u0026amp; successCall(resources) 31 } else { 32 if (this.maxRetryTimes === this._currentRetryTimes) { 33 failedCall \u0026amp;\u0026amp; failedCall(error) 34 } else { 35 this._currentRetryTimes += 1 36 return this.load(successCall, failedCall, errorCall, progressCall) 37 } 38 } 39 } 40 let callFuncArgs: any[] = [this.urls] 41 this.type \u0026amp;\u0026amp; callFuncArgs.push(this.type) 42 progressCall \u0026amp;\u0026amp; callFuncArgs.push(progressCall) 43 callFuncArgs.push(completedCallFunc) 44 cc.loader.loadResArray.apply(cc.loader, callFuncArgs) 45} 由于在加载完成后我们记录了全部资源，释放时的资源处理就会非常简单直接：\n1/** 2 * 释放资源 3 */ 4release () { 5 this.isReleased = true 6 let resources: string[] = Object.keys(this.resources) 7 cc.loader.release(resources) 8 this.resources = {} 9} 10 11/** 12 * 释放资源 13 * @param otherDepends 其它依赖项，释放资源会跳过这些资源 14 */ 15releaseWithout (otherDepends: Object) { 16 for (let reference in this.resources) { 17 if (otherDepends[reference]) { 18 delete this.resources[reference] 19 } 20 } 21 this.release() 22} 模块的资源管理类 功能模块使用的资源不可能全都是单一类型，而且模块与模块之间，资源加载对象之间也有着可能的重合的使用资源。当某个模块资源需要释放时，其它模块引用的资源需要确保不被释放。因此，需要一个资源加载与释放的管理者，来告知 LoadItem 在释放时需要过滤的资源。\n鉴于模块或界面之间的树状关系结构，管理者也需要设计成树状结构，即有一个根管理者及其派生管理者，而每一个节点上的资源在被释放时，都需要考虑其它所有节点所使用到的资源。管理者对象结构如下：\n1class Loader { 2 private _parentLoader: Loader = null 3 private _subLoaders: Loader[] = null 4 private _loadItems: LoaderItem[] = null 5 private _released: boolean = false 6} 我们需要一个根管理器来加载通用资源，这些资源将不会被其它管理器释放。同时，所有其它管理器应当是根管理器的子节点，这样才契合 Cocos 节点的树状关系，在释放时可以方便的获得到其它模块使用到的所有资源。\n1/** 2 * 获取到根管理器 3 */ 4get rootLoader (): Loader { 5 let root: Loader = this 6 while (root._parentLoader) { 7 root = root._parentLoader 8 } 9 return root 10} 11 12/** 13 * 创建子管理器 14 */ 15createSubLoader (): Loader { 16 let loader = new Loader() 17 loader._parentLoader = this 18 this._subLoaders.push(loader) 19 return loader 20} 21 22/** 23 * 移除子管理器 24 * @param loader 需移除的子管理器 25 */ 26private _removeSubLoader (loader:Loader) { 27 let index: number = this._subLoaders.indexOf(loader) 28 if (index \u0026gt;= 0) { 29 this._subLoaders.splice(index, 1) 30 } 31} 加载时，管理器需将所有的加载项记录下来；释放时，直接释放这些加载项。需要注意的是：\n由于资源加载是异步的，资源加载完成时可能该加载项已被释放，此时需要单独处理释放逻辑。 释放需要在下一个 Tick 进行。因为同一时刻，同一个文件可能有多个加载项在加载，LoaderItem 对象中还未对已引用的资源作记录，直接释放可能会错误释放掉其它资源的依赖项。 1/** 2 * 3 * @param urls 加载资源项 4 * @param type 加载资源类型 5 * @param succCall 加载成功回调 6 * @param failCall 加载失败回调 7 * @param retryTimes 重试次数 8 * @param progressCall 加载进度回调 9 */ 10load (urls: string[]|string, type:typeof cc.Asset, succCall: SUCCESS_CALL = null, failCall: FAILED_CALL = null, retryTimes:number = 0, progressCall:PROGRESS_CALL = null) { 11 let item: LoaderItem = new LoaderItem(urls, type, retryTimes) 12 item.load((res:any[])=\u0026gt;{ 13 if (this._released|| item.isReleased) { 14 // 释放刚加载的资源，需在下一Tick释放，保证其它加载成功 15 return callInNextTick (()=\u0026gt;{ 16 item.releaseWithout(this.rootLoader.getAllResources()) 17 }) 18 } 19 return succCall \u0026amp;\u0026amp; succCall(res) 20 }, (error:Error)=\u0026gt;{ 21 if (this._released) return 22 failCall \u0026amp;\u0026amp; failCall(error) 23 }, progressCall) 24 this._loadItems.push(item) 25} 26 27/** 28 * 释放管理器 29 */ 30release () { 31 this._released = true 32 this._parentLoader._removeSubLoader(this) 33 // 释放当前加载的所有资源，需在当前Tick释放，以让后续的加载请求生效 34 let allResouces: Object = this.rootLoader.getAllResources() 35 this._releaseWithout(allResouces) 36} 37 38/** 39 * 选择性释放资源 40 * @param allResouces 不能被释放的资源 41 */ 42private _releaseWithout (allResouces: Object = null) { 43 for (let item of this._loadItems) { 44 item.releaseWithout(allResouces) 45 } 46 this._loadItems.length = 0 47 48 for (let loader of this._subLoaders) { 49 loader._releaseWithout(allResouces) 50 } 51} 如何使用资源管理类 在使用资源管理类 Loader 时，我们需要一个根管理器来加载所有无需动态释放的公共资源。根管理器可以直接使用 new Loader 来创建，全局或场景唯一。其它子模块的资源管理器，需要通过根管理器的 createSubLoader 来创建。这样就建立了一个全局唯一的资源管理树。我们可以很方便的获取到当前正在使用的所有资源，也可以针对某个节点的资源进行定点释放。\n例如，我们需要对某个界面中所有加载的 Spine 骨骼动画资源进行管理，在显示动画时加载资源，在界面销毁时移除资源，只需要按如下方式进行加载和释放：\n1onLoad () { 2 // app.mainLoader为根管理器 3 this._actorLoader = app.mainLoader.createSubLoader() 4} 5 6showSpine (path) { 7 this._actorLoader.load(path, sp.SkeletonData, (skeletonDatas) =\u0026gt; { 8 let skeletonData = skeletonDatas[0] 9 if (this \u0026amp;\u0026amp; this.node \u0026amp;\u0026amp; cc.isValid(this.node) \u0026amp;\u0026amp; skeletonData) { 10 this.spine.skeletonData = skeletonData 11 this.spine.setAnimation(0, \u0026#39;idle\u0026#39;, true) 12 } 13 }, null, -1) 14} 15 16onDestroy () { 17 this._actorLoader.release() 18} 需要注意的是，资源加载在节点创建之前。在需要对该节点资源（包括节点自身）进行管理时， Loader 需要提前创建并用来加载节点预制件。为了方便节点在销毁时自动释放相关资源，我们增加了 LoaderKeeper 组件，动态加到节点上并将其 Loader 记录下来，在销毁时释放资源：\n1@ccclass 2export default class LoaderKeeper extends cc.Component { 3 private _loader: Loader = null 4 5 get loader ():Loader { 6 return this._loader 7 } 8 9 init (loader: Loader) { 10 this._loader = loader 11 return this 12 } 13 14 onDestroy () { 15 if (this._loader) { 16 this._loader.release() 17 this._loader = null 18 } 19 } 20} 完整的使用方式如下：\n1let loader: Loader = app.mainLoader.createSubLoader() 2loader.load(prefabPath, cc.Prefab, (prefabs) =\u0026gt; { 3 let prefab: cc.Prefab = prefabs[0] 4 let node = cc.instantiate(prefab) 5 app.canvas.addChild(node) 6 node.addComponent(LoaderKeeper).init(loader) 7}, (err:Error)=\u0026gt;{ 8 loader.release() 9}) 这套资源加载机制可以依据项目的需求进行灵活调整，Loader/LoaderKeeper/LoaderItem 联合使用能有效地按需 加载/释放 游戏中所使用到的所有动态资源。\n结语 由于《猎头专家》的资源结构特点，资源管理在设计时并未考虑自动加载资源与动态加载资源之间互相依赖时，资源释放的过程与特性。在此特别提醒，如果项目中有这种使用方式时，请自行研究以免出现资源错误释放的问题。此文主要是通过提供一种管理器的设计思路来介绍 Cocos Creator 的资源加载与释放机制，与各位交流学习。\n欢迎留言讨论，欢迎吐槽拍砖。\n全文完 ","date":"2018-07-11","description":"","lastmod":"2018-07-11T06:32:20Z","slug":"youshootfirst3","tags":["game","html","youshootfirst"],"title":"《猎头专家》动态资源加载与释放技巧","url":"https://blog.zengrong.net/post/youshootfirst3/"},{"categories":["technology"],"content":"为什么要做猎头专家 《猎头专家》对标的游戏是 Bowmasters ，\u001b这款游戏的打击感和操作都很不错，评分相当高。但它是一款单机功能为主的休闲游戏，联网对战\b功能很弱，\b画面太血腥，\b不适合\b年龄较小的玩家和女性玩家。\n射手座团队认为，血腥的画面的确给人更强的力量感，但这是一个比较小众的需求，也不符合国内用户的习惯。我们可以把游戏做得足够有趣——在战斗终结技\b中加入大家耳熟能详的梗，让人物的动作更丰富，让武器的技能更出人意料……这是一个更大的发展方向，\b能够吸引到更加广泛的玩家。\n然而，微信玩家们对终结技似乎并不买账。这点以后再说。\n与现在充斥微信小游戏平台上的大部分小游戏不同，在《猎头专家》中，我们瞄准的主要玩法是在线对战。这是\b\b猎头专家第一个版本就包含的功能，也是游戏最核心，最主要的功能。\b这让《猎头专家》的开发过程变得不那么简单。玩家习惯和在线人数都会影响到对战游戏的表现。\b射手座团队认为，和真实的玩家对战，可以促进玩家真正主动地分享游戏。这是《猎头专家\u001b》用户增长的个重要途径之一。\n从5月9日发布 1.0 版本以来，《猎头专家\b》已经发布了 27 个版本，平均每周 3~4 个版本，新增游戏角色 21 个。射手座团队正在用自己特有的速度，让《猎头专家》这款对战游戏变得更加丰满和完善。\n技术选型 \b《猎头专家》一出现就面向微信\b小游戏平台，因此，技术上我们无法选择更成熟的 Unity ，\b而是限制在 HTML5 平台。对于国内 HTML5 的“三大”引擎 Cocos/Egret/Layabox 的“终极选择”，我们并没有纠结过多。虽然\b深入用过其中两款，但 Cocos \b对于团队来说还是更熟悉一些。 我们的不少开发者都经历过 cocos2d-x 2~3 的阵痛，踩过 CocosStudio 这个大坑，因此对 CocosCreator(CCC) 挺有好感，我们也希望这款编辑器能越来越好。\b想让一款工具变好的最有力的方法就是： 选择它，使用它。\n服务器方面，为了方便开发者\b进行全栈开发，我们选择了网易的 pomelo 这款基于 node.js 的服务器开发\b框架。\b在开发《猎头专家》之前，射手座团队已经使用过\b该框架半年以上。\n事实证明，选择\b团队熟悉的工具，对团队开发\b速度影响很大。CocosCreator 支持 TypeScript 对多人合作有好处，在 node.js 中大量使用 ES6/7 也能提升开发者的幸福感。\b射手座的开发者\b全部以全栈\b方式工作，这也进一步提升\b了功能模块的开发质量和进度。\n角色方面，我们使用了 Spine 这个成熟的 2D 骨骼动画制作工具。CCC 对 Spine 的封装还不是很完善，我们对其做了一些修改。\n对于系统工具，我们大量使用了 Fabric 和 Jenkins 来实现自动部署，\b微信小游戏的发布、打包，游戏服务器的部署都是一键自动完成的。\n\b顺便说一句，CocosCreator 支持命令行打包是很好的功能，但如果能支持 Linux 就更好了（最好能把打包工具从 CocosCreator 中剥离出来，作为一个独立的工具发布），\b这样我们就不需要拿一台 Mac 做打包机了，真浪费……\n微信小游戏引擎的性能问题 这点真的要拿出来说一下的，微信小游戏引擎的性能 ！真！的！很！差！劲！\n《猎头专家》的开发调试均在 Chrome 浏览器中进行，测试阶段也会在手机浏览器和微信浏览器中进行测试。在原生的 HTML5 环境下，游戏运行非常流畅，但在小游戏环境下，游戏非常卡顿。这导致我们不得不减少特效的使用，简化物理碰撞盒，同时对计算进行优化。\n例如，我们在 6 月上旬推出的游戏角色 “龙娘” ，她的武器龙蛋可以爆炸造成溅射伤害，地图上会留下火焰粒子效果。这个角色在测试中表现的效果非常惊艳，但上了小游戏引擎后却出现了卡顿噩梦。我们不得不减少火焰效果的表现以及粒子数量才能让帧率勉强回升到 40 FPS以上。\n经过测试，我们发现小游戏引擎的渲染性能尚可，但 Javascript 引擎的执行效率有很大的问题。因此，诸如物理碰撞以及粒子之类需要大量计算的场合，就会拖慢帧率。\n开发者只能通过降低效果来改变自己的游戏在微信小游戏引擎中的表现，或者等待微信小游戏团队的更新。看着我的骁龙 835 也这么卡，真的让人很沮丧。 \b 全文完 ","date":"2018-07-09","description":"","lastmod":"2018-07-09T05:35:27Z","slug":"youshootfirst2","tags":["game","html","youshootfirst"],"title":"《猎头专家》产品立项","url":"https://blog.zengrong.net/post/youshootfirst2/"},{"categories":["technology"],"content":"三个月了，终于有时间坐下来写一点东西了。\n在这个系列里，我将制作《猎头专家》的过程进行梳理，作为射手座团队开发第一款小游戏的小结。希望对小游戏行业的朋友们有用。\n我会详细阐述《猎头专家》这个产品从立项到技术选型的过程，在开发过程中遇到的有代表性的问题以及我们的解决方案，欢迎大家批评指正。\n关于猎头专家 直接看游戏视频最能说明问题了：\n猎头专家道具系统 猎头专家核能肥宅VS肥婆七 猎头专家吸螺VS没戏 猎头专家粉红小妹VS小红帽 猎头专家面霸VS女皇大人 猎头专家超级智VS白雪公主 猎头专家大粽子VS萝拉 猎头专家绝地鸡王VS萝拉 猎头专家女皇大人VS吸血猎人 这是一款极具趣味性的射击游戏！凭借精湛的技能即可获得源源不断的奖赏！\n加入战斗， 扮演弓箭手，用你的弓箭征服你的好友，征服世界各地的玩家，成为最强的弓箭手！！\n深吸一口气，瞄准目标，发出你的箭，争取一击爆头！！！\n你会是最好的弓箭手吗？还在等什么，快来玩玩看吧~~~\n提纲 产品立项 为什么要做《猎头专家》 技术选型 微信小游戏引擎的性能问题 模块设计 地形和背景实现 武器系统设计 我们需要布娃娃 角色是有情感的，如何用动作细节表现？ 机器人 让机器人活在物理世界中 让机器人学会瞄准 CCC 踩坑 动态资源加载与释放技巧 H5 横竖屏切换后，物理坐标系出错了 因为《猎头专家》的开发还在进行当中，团队各位成员也在长期加班加点，你们也知道写文档是技术人员最不愿意做的事情啦…… 所以，这个系列可能会更新比较慢。但我保证，慢是慢点，坚决不太监！\n关于射手座团队 射手座团队坐标武汉，由一群游戏爱好者组成的。团队中有游戏行业的老司机，持续游戏创业者，中老年程序员，当然也少不了朝气蓬勃的小姐姐。我们希望做出让自己开心，让朋友和家人都爱玩的精品游戏。\n下面当然就是厚颜无耻的广告了 大家可以来体验下哈，现在进游戏还有红包可以送呢。\n全文完 ","date":"2018-07-06","description":"","lastmod":"2018-07-06T01:39:55Z","slug":"youshootfirst1","tags":["game","html5","youshootfirst"],"title":"微信小游戏从立项到上线！谈谈《猎头专家》的开发历程","url":"https://blog.zengrong.net/post/youshootfirst1/"},{"categories":["impressions"],"content":"书籍信息 作者: [美]菲利普•休斯顿 迈克尔•弗洛伊德 苏珊•卡尼瑟洛 出版社: 江苏凤凰文艺出版社 译者: 钱峰 页数: 280 主观信息 开始阅读：2018-04-02 结束阅读：2018-04-06 我的标签：思维方式、心理学、谈话技巧 我的评分：9 我的读书记录 摘录 瞬时思维 内在脆弱。在无法反向输出的时候（看报纸/电视/文章/演讲），就是内在脆弱。 趁虚而入。利用人在懈怠状态的“第一选择”。 循序渐进。不先给出最期待的结果，而是一步步深入。 翻页信息。传递“事情已经过去”的信息，让对方将重点放在“如何取得更好的结果”，不纠结“说不说”，而考虑“如何说对我更有利”。 营造环境 一个人感受自己“不会被别人反驳”时才敢说出心中所想。要注意：\n低声说话，注意语速。不要用眼神和动作期待别人的回答。 营造出私密的环境，让人觉得安全。 真诚地沟通。要让人觉得真诚。你付出的越多，可以使用的工具就越多。 要让人认为你认可他。 要表现出一切尽在掌握的心态，要让人认为你什么都知道了，让别人说出你不知道的东西。 任何人的任何行为都是可以合理化的，要从TA的角度理解为什么TA会这么做。 对方最大的后顾之忧就是事情被知晓之后的严重性。要让TA明白所有的问题都是可以解决的，但可以解决不代表我要告诉你应该如何解决。否则TA会自己去衡量这个问题是否可以用你说的方法解决，然后就不会再麻烦你了。要让TA有想象的空间。\n对方的情感攻势，要等TA宣泄完毕之后再出手。\n面对孩子 孩子有时候不是刻意说谎，由于经验的匮乏和大脑发育的过程，TA会因为害怕而“刻意记错”一些事。\n解释为什么要沟通，让TA感到自在和安全。 告诉TA区分谎言和真相，允许说“我不知道”和“我不懂”，这并不代表说谎。 TA会粉饰语言。如果你一定要听真话，要做好情绪管理。 分享你在TA的年纪所作的荒唐事，暗示你可能理解TA。 选择性地纠正孩子，在大是大非的事情上要明确表明带毒，小事上应该谨慎包容。 谨慎对孩子说谎。 抵赖循环 打断你的话使用说服性陈述拒绝你的继续交流。说服性陈述的表象有插话、不屑等等。当你发现说服性陈述的信号，应该及时做出反应，快速夺回主动权。因为抵赖是一个循环，当第一个抵赖出口之后，TA一定会把抵赖持续下去，要在第一个或者第二个抵赖出口的时候阻止对方。\n喊名字 使用控制性短语，但不要抬高嗓门。“请稍等”、“请听我把话说完” 举双手。表示“我没有攻击性”，“你理解错了我的意思” 奖励合作 如果TA在交流的过程中提供了信息，我们也不能表现出得意的感觉。不能对于TA之前的行为展开语言或者动作报复，这是幼稚的表现。\n要对TA的每一个配合进行奖励。可以是语言或者行为、表情上的奖励。\n面对孩子更要进行奖励。无论孩子出了什么错，只要TA说出了真相，就应该对这种用于说出真相的行为进行单独的奖励。\n专注金块 知道TA只告诉了你一部分信息（金块），不要想着整个金矿。多得到几块金块之后，金矿就会自动出现。\n自己的角色定位 你的角色不一定符合你的心理认知。要摆脱自己真实的角色定位，你的第一目的是掌控。\n心理雷区 每个人都有一个自己心目中的真相。雷区可能是一个观点，一句话甚至一个词。说谎需要勇气，会引起认知失调，说服自己仅仅需要一个“不同定义”。\n一致性掌控 不明确告诉TA要干什么。但干了之后TA更容易满足，于是TA就会自动去干。但干完之后TA没有被强迫的感觉。 小小地进行让步，不算背弃原则，双方都更愿意去做。 用独白造成对方的认知失调，破坏对方建立起的认知平衡，再用替代的方案帮助TA缓解之前的认知失调。 全文完 ","date":"2018-04-18","description":"","lastmod":"2018-04-18T13:15:06Z","slug":"get-the-truth","tags":["reading","readingnote"],"title":"【读书笔记】掌控力——让所有人对你讲真话","url":"https://blog.zengrong.net/post/get-the-truth/"},{"categories":["use"],"content":"我曾经在 Kindle的使用 和 时间都去哪儿了？——善用工具形成高效习惯 中都介绍过 Kindle 的使用。在我阅读的书籍中，一部分是通过听的方式完成的，还有一部分是在 Kindle 上读完的。但更多的书，是在电脑上读完的。因为用过 Kindle 的人都知道，在 Kindle 的 6 寸屏幕上上读 PDF 就是一种折磨。而技术文档和书籍大部分都是 PDF 格式的。\n即使是我在 Amazon 上购买了一些经过排版调整的技术书籍，依然发现无法畅快阅读。例如碰到代码的时候，Kindle 的显示非常糟糕。目前对我来说，Kindle 只适合阅读非技术类书籍，而这类书籍我完全可以用听的方式来解决。这就尴尬了……\n选择 由于阅读技术文档的需求，我一直考虑买个大屏的阅读设备。之前也购买过 2 个平板电脑阅读 PDF ，阅读体验当然是不错的，就是看时间长了眼睛受不了，毕竟年纪大了嘛 😭 ……\n我又考察了 reMarkable 和 Sony 大法的 DPT ，发现它们对中文的支持都有些瑕疵，这个不能忍。\n在查看 Sony DPT 资料的同时，看到了文石的 Boox 系列。在 NOTE 和 MAX2 中纠结了一阵，因为 NOTE 没货就直接入手了 MAX2 。用了一个月，写一点简单的体验。\n优点 大。这个是最大的优点了。阅读 PDF 非常非常地爽，真的是一大抵三丑了。 支持扫描版 PDF 重排。可以调整扫描版 PDF 的行距，字体大小等等。也可以把扫描歪了地 PDF 调正阅读。 笔记功能挺好用。在 PDF 上可以直接手写做笔记，直接找到读纸书做笔记地感觉。如果书上写字的地方不够，还可以平分屏幕做笔记。 还是大。可以当画板和笔记本用。13.3 英寸的屏幕，27x20.2CM 的可书写范围，比A4（29.7x21CM）纸略小一点，打草稿和画图之类的都很方便。由于使用的是电磁笔，完全不用担心手指误按。书写感觉略微有一点延迟，但尚可接受。据说 reMarkable 的书写感觉是最好的，因为没用过没法比较了。 用作显示器。2K 的屏幕，接到 macbook 上可以直接作为 API 和文档专用屏幕。这个对于码农来说的确好用，怎么看都不伤眼啊。 开放的安卓系统，自带 Google 市场，蓝牙/WIFI/耳机孔/扬声器 一个不缺，想干什么你看着办。 Kindle 替代。装一个 Kindle App 之后，以前在 Kindle 中买的书都能同步回来。呵呵 缺点 丑。旧模具的边框太宽，在拥有了触摸屏和电磁笔之后，下面的四个按钮没有什么存在的必要了。 开着笔记 APP 不使用，有一定机率导致系统重启。 插入 HDMI 线之后还要打开显示 APP。这里应该修改为自动启动显示 APP。显示器 App 如果不退出，会一直耗电到 0% ，这点对电池伤害极大。应该修改为拔掉显示器 HDMI 连线后显示 APP 就自动退出。 图集 全文完 ","date":"2018-03-25","description":"","lastmod":"2018-03-25T02:14:20Z","slug":"about-onyx-boox-max2","tags":["reading"],"title":"文石 Boox Max2 简单评测","url":"https://blog.zengrong.net/post/about-onyx-boox-max2/"},{"categories":["technology"],"content":"**2018-04-12更新： ** 加入使用 unicodedata 的代码范例。\nEmoji 是 Unicode 字符集的一部分。目前我看过的最好的介绍 unicode 的文章是这一篇： 写给程序员的 Unicode 入门介绍 。\n程序员经常要处理 Emoji 。在微信流行起来之后，许多使用微信作为第三方账号登录的 App/Game 都面临处理 Emoji 的问题，因为微信的昵称中可以包含 Emoji。\n本文介绍一些处理 Emoji 的相关信息，不展开。\n操作系统支持 Emoji 是需要操作系统支持的。每个操作系统实现的 Emoji 不太相同。Windows/iOS/Android/macOS 的最新版本都对 Emoji 提供了支持。\n想看一下不同操作系统下的同一个 Emoji 形象有何不同，可以看这里： Emoji Unicode Tables 。\nAndroid Android 4.4 开始支持完整的 Emoji 列表。在输入法中就能打开 Emoji 面板。\niOS iOS 8.3 开始支持完整的 Emoji 。原生键盘上就提供了 Emoji 面板。\nmacOS macOS Sierra 10.12 中，直接按下 Command - Control - Spacebar 组合键就能打开 Emoji 面板。\nWindows Windows 10 和 Windows 8.1 的触摸键盘提供了对 Emoji 的面板支持。\n数据库支持 对于 MySQL 来说，如果需要存储 Emoji 编码，需要在 MySQL 5.5.3 或更高版本中启用 utf8mb4 编码。\n使用正则表达式替换 Emoji 最简单的处理方案就是把字符串中的 Emoji 清除掉。\n在 Javascript 中，可以采用下面的代码（不保证完整）：\n1/** 2 * 过滤掉字符中的emoji 3 * @param strWithEmoji 4 * @returns {string} 5 */ 6Utils.clearEmoji = function (strWithEmoji) { 7 if (strWithEmoji) { 8 return strWithEmoji.replace(/([\\uE000-\\uF8FF]|\\uD83C[\\uDF00-\\uDFFF]|\\uD83D[\\uDC00-\\uDDFF])/g, \u0026#39;\u0026#39;) 9 } 10 return \u0026#39;\u0026#39; 11} 在 Python 3.3 或更高版本中，可以考虑使用下面的代码：\n1import re 2 3EMOJI_RE = re.compile(ur\u0026#34;\u0026#34;\u0026#34; 4 [\\U0001F600-\\U0001F64F] # emoticons 5 | 6 [\\U0001F300-\\U0001F5FF] # symbols \u0026amp; pictographs 7 | 8 [\\U0001F680-\\U0001F6FF] # transport \u0026amp; map symbols 9 | 10 [\\U0001F1E0-\\U0001F1FF] # flags (iOS) 11\u0026#34;\u0026#34;\u0026#34;, re.VERBOSE) 12 13def clear_emoji(str_with_emoji): 14 if str_with_emoji: 15 return EMOJI_RE.sub(str_with_emoji, \u0026#39;\u0026#39;) 16 return \u0026#39;\u0026#39; 使用库工具 Python 自带一个 unicodedata 库，提供了 unicode 转换功能。\n下面的 replace_emoji 方法就使用 unicodedata 提供的功能来进行转换（当然，没有考虑性能问题）：\n1def replace_emoji(self, input_string): 2 for character in input_string: 3 try: 4 character.encode(\u0026#34;ascii\u0026#34;) 5 return_string += character 6 except UnicodeEncodeError: 7 replaced = str(character) 8 if replaced != \u0026#39;\u0026#39;: 9 return_string += replaced 10 else: 11 try: 12 return_string += \u0026#34;[\u0026#34; + unicodedata.name(character) + \u0026#34;]\u0026#34; 13 except ValueError: 14 return_string += \u0026#34;[x]\u0026#34; 15 return return_string emoji-cheat-sheet.com 项目则提供了大量的库可供选择，包括 Ruby/Javascript/Objective-C/Java/Python/Swift 。\n例如对于 Python 中的库 emoji，只需要简单的代码就能在 Emoji 和 :name: 形式见互相转换：\n1import emoji 2 3print(emoji.demojize(\u0026#39;Hello 🌊\u0026#39;)) 4# \u0026#39;Hello :water_wave:\u0026#39; 5print(emoji.emojize(\u0026#39;Hello :water_wave:\u0026#39;)) 6# \u0026#39;Hello 🌊\u0026#39; 其它 Emoji-Helper 一个浏览器插件，支持在浏览器中显示 Emoji。\nhttp://johannh.me/emoji-helper/\ngetemoji.com 一个介绍 Emoji 的网站，可以在上面查看和复制 Emoji 。\nhttp://getemoji.com/\nPlaying with Emoji 这个网站提供了不同操作系统中 Emoji 形象的功能，很容易进行对比。\nhttps://apps.timwhitlock.info/emoji\nUnicode Emoji Charts Emoji 的官方网站。\nhttp://unicode.org/emoji/charts/\n全文完 ","date":"2018-01-05","description":"","lastmod":"2018-01-05T05:09:36Z","slug":"about-emoji","tags":["python","node"],"title":"处理 Emoji","url":"https://blog.zengrong.net/post/about-emoji/"},{"categories":["technology"],"content":"最近感觉 Java 软件在 macOS 上启动明显偏慢。例如我的 IntelliJ IDEA CE 启动居然耗时 2 分钟，Spine 启动耗时 30 秒。\n表现为，在启动后迟迟看不到 splash 界面，只能看到一个标题栏名称.\n查了一下资料，发现原因是 java.net.InetAddress 在 macOS Sirra 上的运行速度导致： java.net.InetAddress: getLocalHost() slow after MacOS Sierra upgrade?\n可以通过设置 hostname 来解决这个问题：\n运行 hostname 得到本机的值，例如我的机器为 zrong-mbp ; 编辑 /etc/hosts 文件，增加或者修改下面的配置： 1127.0.0.1 locahost zrong-mbp 2::1 localhost zrong-mbp 如此处理后，上述软件显示 Splash 界面的时间降低到 3 秒。\n全文完 ","date":"2018-01-03","description":"","lastmod":"2018-01-03T03:17:20Z","slug":"java-software-load-slow-on-macos","tags":["java","macos"],"title":"解决 Java 软件 macOS 上启动慢的问题","url":"https://blog.zengrong.net/post/java-software-load-slow-on-macos/"},{"categories":["impressions"],"content":"系列 【读书笔记】思考，快与慢（一） 【读书笔记】思考，快与慢（二） 【读书笔记】思考，快与慢（三） 我的理解 第18章 如何让直觉性预测更恰当有效？ 我们认为预测是一种天生的能力，但是这种能力是可以通过练习而习得的，而且有公式可以遵循。\n根据直觉进行的预测往往是失准的，大脑常常这样做：\n寻找因果关系。前面的读书笔记种讲到了大脑偏爱因果联系。对于一个没有更多数据提供的预测，大脑会探寻证据和预测目标之间是否存在因果联系，这种联系往往不是直接的。系统一能够摈弃无关活错误信息，但无法解决较小的瑕疵。眼见为实原则会发生作用，大脑会自动联想（脑部）出一个合理恰当的故事。 替代和快速配对。系统一会在证据不足的情况下将要预测的内容替换成一个更容易回答，资源更丰富的题目，然后选择一个合理的答案。 更概括的说：大脑被信息和问题激发起联想记忆，然后自我反馈，最后选择最具连贯性和合理性的解决方案。\n无偏见预测四步法：\n先估测出平均绩点的平均值。 根据你对证据的印象算出与之相配的平均绩点; 对你的证据和平均绩点的关联做出估计; 如果关联度是0.3，则从估算出的平均绩点的平均值中抽出30%，放到与之匹配的平均绩点里。 未整理 \u0026quot;在环境缺乏牢靠的规律时，不要相信直觉。\u0026quot; (章节:第22章 什么时候可以相信专家的直觉？)\n\u0026quot;大多数情况下，我们还是可以将可能是有效的直觉与可能是无效的直觉区分开来的。这就好比判断一件艺术作品的真假一样，关注这件作品的出处通常比关注作品本身的判断准确率更高。如果环境有足够的规律性，并且在判断时有机会掌握这些规律，联想机制就会识别这些情境并做出快速且准确的预测与决策。这些条件若得到满足，你就可以相信某个人的直觉。\u0026quot; (章节:第22章 什么时候可以相信专家的直觉？)\n\u0026quot;外推法是错误的。我们根据眼前的信息进行预测，但我们写出的前几章有可能比其他章节简单，而且在写那几章时，我们对这个项目的投入程度很可能正处于最高点。然而，最主要的问题在于我们没有考虑到由唐纳德·拉姆斯菲尔德（Donald Rumsfeld）提出的著名观点：“未知的未知数。”那天，我们没能预见到接下来发生的事会导致这个项目拖延这么久。离婚、生病、与官员的协调等事情导致工作一再延迟，这些事情都是意料之外的。这样的事情不仅会减慢教材的编写速度，还会导致任务在长时间内没有任何进展或进展非常缓慢。\u0026quot; (章节:第23章 努力养成采纳外部意见的决策习惯)\n\u0026quot;我和阿莫斯创造了“规划谬误”这个新词来描述下列计划和预测： ·不切实际地接近理想状况（的计划和预测）。 ·可通过参考类似案例的数据得到提高（的计划和预测）。\u0026quot; (章节:第23章 努力养成采纳外部意见的决策习惯)\n\u0026quot;楼预计的最高预算是4000万英镑。到了1999年6月，建楼的预算就变成了1.09亿英镑。2000年4月，规划者将“成本上限”修改为1.95亿英镑。到2001年11月，他们又将“最终成本”预估为2.41亿英镑。这个最终成本在2002年年末的时候又上涨了两次，成为2.946亿英镑。到2003年6月，预算又增加了3次，达到3.758亿英镑。这栋大楼最终在2004年建成，最终耗资约为4.31亿英镑。\u0026quot; (章节:第23章 努力养成采纳外部意见的决策习惯)\n\u0026quot;弗林夫伯格使用的预测方法与为克服对基础比率的忽视而采取的建议非常相似： 1.识别对应的参考类别（例如厨房改建和大型铁路项目等）。 2.获取参考类别的统计数据（每英里铁路的造价或是支出超过预算的百分比），利用这些数据作出基准预测。 3.如果有特别的原因说明这个项目多少会比同类项目的乐观偏差更为明显，则可使用此例的具体信息对基准预测进行调整。\u0026quot; (章节:第23章 努力养成采纳外部意见的决策习惯)\n\u0026quot;人们之所以经常（但不是总是）承担风险项目是因为他们对成功率过于乐观。我将在本书中反复提到这一点，因为它可能有助于解释为什么人们会对簿公堂、发起战争或者急于创业。\u0026quot; (章节:第23章 努力养成采纳外部意见的决策习惯)\n\u0026quot;那个项目是由我发起的，因此我的主要责任就是赋予这个项目意义，而其他主要的问题则由团队进行讨论，但我却失职了。当我听到希莫的统计分析后，就改正了那个谬误。如果是在迫不得已的情况下，我会承认我们的项目从一开始就是错的，我们至少应该认真考虑承认失败并打包回家这个选择。但是没有人向我施压，也没有人和我讨论过这个问题，我们默许了这个项目继续下去，根本就不考虑到底需要多长时间。这样做很容易，因为我们在最开始时并没有作出这样的预测。如果在开始做这个项目时就有了合理的基准预测，那么我们就不会再将项目进行下去了，但我们已经投入了大量精力—这是一个沉没成本悖论的例子，我们将在本书后半部分重点关注这个问题。放弃这个项目会令我们很尴尬，尤其是我\u0026quot; (章节:第23章 努力养成采纳外部意见的决策习惯)\n\u0026quot;禀赋效应是指当个人一旦拥有某个物品，那么他对该物品价值的评价要比未拥有之前大大提高。\u0026quot; (标注: 禀赋效应) (章节:第27章 禀赋效应与市场交易)\n未完待续 ","date":"2017-12-21","description":"","lastmod":"2017-12-21T14:55:08Z","slug":"readingnotes-thinking-fast-and-slow3","tags":["reading","readingnote"],"title":"【读书笔记】思考，快与慢（三）","url":"https://blog.zengrong.net/post/readingnotes-thinking-fast-and-slow3/"},{"categories":["technology"],"content":" 2020-01-27 更新 增加相关阅读和 Flask+uWSGI Logging rotate：重要补充。 我在 部署Flask + uWSGI + Nginx 一文中详细讲解了如何部署一个 Flask 应用。但这篇文章忽略了生产环境的一个重要的功能： Logging 。\n本文基于 Flask 0.12.2 。\n当 Flask App 被部署到生产环境时，我们会选择关闭 DEBUG 配置。在这种情况下，Flask 中使用 flask.current_app.logger.info() 打印的 LOG 仿佛消失了一样。它们去了哪里呢？\n默认的 Handler 下面的源码位于 [flask.logging][loggin] 中。从源码可以看出，Flash 自动创建了 logger 并加入了一个 DEBUG 级别的 Handler 和一个 ERROR 级别的 Handler。根据 DEBUG 变量的值，DEBUG Handler 在生产环境下是不生效的。因此我们就只能看到来自于 ProductHandler 的 ERROR 级别 Log 信息。\n1def create_logger(app): 2 \u0026#34;\u0026#34;\u0026#34;Creates a logger for the given application. This logger works 3 similar to a regular Python logger but changes the effective logging 4 level based on the application\u0026#39;s debug flag. Furthermore this 5 function also removes all attached handlers in case there was a 6 logger with the log name before. 7 \u0026#34;\u0026#34;\u0026#34; 8 Logger = getLoggerClass() 9 10 class DebugLogger(Logger): 11 def getEffectiveLevel(self): 12 if self.level == 0 and app.debug: 13 return DEBUG 14 return Logger.getEffectiveLevel(self) 15 16 class DebugHandler(StreamHandler): 17 def emit(self, record): 18 if app.debug and _should_log_for(app, \u0026#39;debug\u0026#39;): 19 StreamHandler.emit(self, record) 20 21 class ProductionHandler(StreamHandler): 22 def emit(self, record): 23 if not app.debug and _should_log_for(app, \u0026#39;production\u0026#39;): 24 StreamHandler.emit(self, record) 25 26 debug_handler = DebugHandler() 27 debug_handler.setLevel(DEBUG) 28 debug_handler.setFormatter(Formatter(DEBUG_LOG_FORMAT)) 29 30 prod_handler = ProductionHandler(_proxy_stream) 31 prod_handler.setLevel(ERROR) 32 prod_handler.setFormatter(Formatter(PROD_LOG_FORMAT)) 33 34 logger = getLogger(app.logger_name) 35 # just in case that was not a new logger, get rid of all the handlers 36 # already attached to it. 37 del logger.handlers[:] 38 logger.__class__ = DebugLogger 39 logger.addHandler(debug_handler) 40 logger.addHandler(prod_handler) 41 42 # Disable propagation by default 43 logger.propagate = False 44 45 return logger 要解决这个问题，我们需要创建自己的 Handler 。\n创建自己的 Handler 在创建了 Flask app 之后，调用下面的 _set_logger 方法将 app 实例传入即可。详细的介绍见代码中的注释。\n1def _set_logger(flaskapp): 2 \u0026#34;\u0026#34;\u0026#34; 3 设置 Flask app 的logger 4 \u0026#34;\u0026#34;\u0026#34; 5 # 删除 Flask 的默认 Handler 6 del flaskapp.logger.handlers[:] 7 if flaskapp.config.get(\u0026#39;DEBUG\u0026#39;): 8 # 在 DEBUG 模式下，使用 StreamHandler，并使用 DEBUG 级别，这样可以将所有的信息都输出到控制台 9 hdr = logging.StreamHandler() 10 hdr.setLevel(logging.DEBUG) 11 flask.logger.setLevel(DEBUG) 12 else: 13 # 不使用 StreamHandler 的原因，是 uwsgi 可能会在标准输出中加入它自己的 Log，为了避免Log被弄乱，单独使用一个 FileHandler 14 hdr = logging.FileHandler(config.getdir(\u0026#39;logs\u0026#39;, \u0026#39;app.log\u0026#39;), encoding=\u0026#39;utf8\u0026#39;) 15 hdr.setLevel(logging.INFO) 16 flask.logger.setLevel(INFO) 17 18 # 加入足够详细的信息 19 LOG_FORMAT = \u0026#34;\u0026#34;\u0026#34; 20[%(asctime)s] %(levelname)s in %(module)s.%(funcName)s [%(pathname)s:%(lineno)d]: 21%(message)s\u0026#34;\u0026#34;\u0026#34; 22 hdr.setFormatter(logging.Formatter(LOG_FORMAT)) 23 24 # 如果存在 sqlalchemy 的 Log 对象，也为其加入这个 Handler 25 for log in (flaskapp.logger, logging.getLogger(\u0026#39;sqlalchemy\u0026#39;)): 26 if log: 27 log.addHandler(hdr) 我们还可以重写 Flask 对象的 log_exception 方法，自动将所有的异常记录下来，并提供一些更详细的信息：\n1class MYFlask(Flask): 2 def log_exception(self, exc_info): 3 \u0026#34;\u0026#34;\u0026#34;...description omitted...\u0026#34;\u0026#34;\u0026#34; 4 self.logger.error( 5 \u0026#34;\u0026#34;\u0026#34; 6Request: {method} {path} 7IP: {ip} 8Agent: {agent_platform} | {agent_browser} {agent_browser_version} 9Raw Agent: {agent} 10 \u0026#34;\u0026#34;\u0026#34;.format( 11 method=request.method, 12 path=request.path, 13 ip=request.remote_addr, 14 agent_platform=request.user_agent.platform, 15 agent_browser=request.user_agent.browser, 16 agent_browser_version=request.user_agent.version, 17 agent=request.user_agent.string, 18 ), exc_info=exc_info 19 ) uWSGI 的 Logging 配置 我使用 INI 格式的配置文件，文件名一般为 uwsgi.ini。其中关于 Logging 的配置，我常用这样几个：\n1; 将写入 log 的工作委托给 master 进程 2log-master = true 3 4; 单独开一个线程进行 log 写入工作，这样有更好的性能 5threaded-log = true 6 7; 所有 log 都会写入这个文件 8; 若希望所有 log 放在一起，设置了此选项后，不要设置 req-logger 和 logger 选项 9; %d 代表 uwsgi.ini 所在文件夹（包含结尾的/）， %n 代表 uwsgi.ini 的主文件名 10; 魔术变量： http://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/Configuration.html#magicvars 11daemonize = %dlogs/%n.log 12 13; 将 uWSGI 请求 log 写入单独的 log 文件，这样做可以让log更加分离，便于查错 14; 设置了此选项后，daemonize 设置的输出文件就得不到请求相关的日志输出了 15req-logger = file:%dlogs/req.log 16 17; 将 uWSGI stdout/stderr log 写入单独的 log 文件 18; 因为设定了 req-logger ，必须同时设定 logger ，此时 daemonize 不会有 stderr/stdout 之外的日志输出 19logger = file:%dlogs/%n.log 自定义 uWSGI 的请求 log uWSGI 会在 log 中自动写入请求 log，默认的格式如下：\n[pid: 22740|app: 0|req: 162/324] 127.0.0.1 () {36 vars in 608 bytes} [Wed Nov 29 11:42:08 2017] GET /login/?code=001SkzFb1ppEDu0lTzHb1WPCFb1SkzF0 =\u0026gt; generated 181 bytes in 69 msecs (HTTP/1.1 200) 5 headers in 209 bytes (2 switches on core 1)\n关于其中信息如何解释，文档中并没有详细进行介绍，只能通过阅读 源码 logging.c 理解。\n我在 uWSGI 的邮件列表中找到一封邮件 Default Log Format Explained? 介绍了 log 中每个项的详细作用。\npid -\u0026gt; the pid of the worker managing the request app -\u0026gt; the id (it is a integer, starting from 0) of the app, it makes sense when multiple apps are hosted in the same instance. It is -1 when no app managed the request (like when serving static files) or when the 'app' concept does not apply (like with php or cgi's) req: N/M -\u0026gt; N is the number of managed requests by the current worker for the specific app, M is the grand total (sum of all requests of all workers)\nthen you have REMOTE_ADDR followd by the (optional) REMOTE_USER (very similar to apache)\nvars are the number of CGI vars in the request, and their size (from the uwsgi protocol point of view). The size is never higher than the --buffer-size (higher requests are discarded)\nThe time of the request follows\nThen you have REQUEST_METHOD + REQUEST_URI\nThen the response size and the time required for generating it\n\u0026quot;via\u0026quot; is the techology used to send the response, currently can be sendfile, routing or offloading.\nThe response status follows, as well as the number of response headers.\n\u0026quot;core\u0026quot; is the low-level concept for uWSGI concurrency context in a process (can be a thread or a greenlet or a fiber or a goroutine and so on...) while switches count is incremented whenever an app \u0026quot;yield\u0026quot; its status (this has various meanings based on the lower concurrency model used)\n根据上面找到的资料和 格式化uWSGI请求日志 文档，通过设置 log-format 选项，我们可以模仿出默认的请求 log：\n1log-format = [pid: %(pid)] %(addr) (%(user)) {%(vars) vars in %(pktsize) bytes} [%(ctime)] %(method) %(uri) =\u0026gt; generated %(rsize) bytes in %(msecs) msecs (%(proto) %(status)) %(headers) headers in %(hsize) bytes (%(switches) switches on core %(core)) 除了 app: 和 req: 没有提供对应变量，其它的值都可以显示出来。\n其它 日志编码器 也是一个重要的选项，若有需要可以添加该设置。\nuWSGI 还可以使用 touch-logrotate 和 touch-logreopen 来实现 logging rotate，但为了让系统更加简单的独立，我建议使用 logrotate 来实现 logging rotate，并已经在 uWSGI+rsyslog 实现 rotating logging 一文中介绍过具体做法。\n需要注意的是，我在 uWSGI+rsyslog 实现 rotating logging 一文的 单独使用 logrotate 小节中提到的使用 copytruncate 选项替换 create 选项，是因为没有通知 uWSGI 重新打开 log 文件。要做到这一点非常简单，除了使用刚才提到的 touch-logreopen 之外，还可以使用 Master FIFO 中的 l 命令。\n相关阅读 部署Flask + uWSGI + Nginx uWSGI+rsyslog 实现 rotating logging Flask+uWSGI Logging rotate：重要补充 pyzog：uWSGI logging rotate 的终极方案 全文完 ","date":"2017-11-28","description":"","lastmod":"2020-01-27T00:53:02Z","slug":"flask-uwsgi-logging","tags":["flask","uwsgi","python","server","logging"],"title":"Flask+uWSGI 的 Logging 支持","url":"https://blog.zengrong.net/post/flask-uwsgi-logging/"},{"categories":["technology"],"content":"uWSGI 采用的是多进程模式。如果在 uWSGI 中运行的 Python 需要共享数据，可以使用第三方服务如 Redis/Memcached 等。如果数据量不大，还可以使用 uWSGI 提供的 缓存框架 。\nuWSGI 有两套缓存框架。旧的一套叫做 WebCaching ，采用 cache 开头的配置项进行配置。我这里使用的是新的框架，采用 cache2 配置项进行配置。配置语法使用的是 key=value 语法。\n1[uwsgi] 2 3master = true 4processes = 4 5threads = 1 6max-requests = 6000 7chmod-socket = 664 8buffer-size = 32768 9thunder-lock = true 10cache2 = name=default,items=20,blocksize=65536,keysize=60,bitmap=1 11 12wsgi-file = manage.py 13callable = flaskapp 14uid = app 15gid = app 16 17venv = %dvenv 18pidfile = %d%n.pid 19http = 127.0.0.1:5001 在生产服务器中，开启 master 线程是必须的。uWSGI 在启动的时候会自动开启一个 sweeper 线程来处理过期。从下面的 log 可以看出这点：\n1WSGI app 0 (mountpoint=\u0026#39;\u0026#39;) ready in 2 seconds on interpreter 0x7f95aa818e00 pid: 54383 (default app) 2*** uWSGI is running in multiple interpreter mode *** 3spawned uWSGI master process (pid: 54383) 4spawned uWSGI worker 1 (pid: 54401, cores: 1) 5spawned uWSGI worker 2 (pid: 54402, cores: 1) 6cache sweeper thread enabled 7spawned uWSGI http 1 (pid: 54403) 如果加入了 purge_lru 的配置，则上面那句 cache sweeper thread enabled 不会出现。\n1cache2 = name=default,items=20,blocksize=65536,keysize=60,bitmap=1,purge_lru=0 在文档中并没有找到 purge_lru 设置为 0 是否可以禁用缓存过期这个功能。倒是老版本的缓存设置中有一个 cache-no-expire 选项可以直接禁用缓存过期功能。\nuWSGI 会自动插入一个名为 uwsgi 的 模块 到运行在其下的 Python app 中，使用这个模块提供的 缓存接口 可以对 uWSGI 提供的缓存功能进行操作，有这样几个接口：\nuwsgi.cache_get uwsgi.cache_set uwsgi.cache_update uwsgi.cache_exists uwsgi.cache_del uwsgi.cache_clear 文档写得非常简略，从字面也很容易理解它们的意思。\n为了方便使用，我封装了一个 Cache 类。这样在本地开发的时候直接使用一个 dict 模仿缓存，而在 uWSGI 环境中则可以直接使用缓存功能。\n1import pickle 2 3class Cache(object): 4 \u0026#34;\u0026#34;\u0026#34; 5 处理缓存 6 在 uWSGI 中运行的时候，使用 UWSGI 的缓存机制，实现进程间共享 7 否则，缓存到一个 dict 中 8 \u0026#34;\u0026#34;\u0026#34; 9 def __init__(self): 10 self.__g = None 11 try: 12 self.__uwsgi = importlib.import_module(\u0026#39;uwsgi\u0026#39;) 13 print(\u0026#39;USE CACHE UWSGI\u0026#39;) 14 except: 15 self.__g = {} 16 print(\u0026#39;USE CACHE MEMORY\u0026#39;) 17 18 def _getuwsgicache(self, name): 19 \u0026#34;\u0026#34;\u0026#34; 20 获取 UWSGI 缓存 21 :param name: 真实的 name，带有 regional 信息 22 :return: 序列化之后的 python 对象 23 \u0026#34;\u0026#34;\u0026#34; 24 raw_value = self.__uwsgi.cache_get(name) 25 # print(\u0026#39;_getuwsgicache:\u0026#39;, raw_value) 26 if raw_value is not None: 27 return pickle.loads(raw_value, encoding=\u0026#39;utf8\u0026#39;) 28 return None 29 30 def _setuwsgicache(self, name, value): 31 \u0026#34;\u0026#34;\u0026#34; 32 设置 UWSGI 缓存 33 :param name: 设置名称 34 :param value: 值 35 :return: 36 \u0026#34;\u0026#34;\u0026#34; 37 if value is None: 38 self.__uwsgi.cache_del(name) 39 return 40 raw_value = pickle.dumps(value) 41 # print(\u0026#39;_setuwsgicache:\u0026#39;, raw_value) 42 if self.__uwsgi.cache_exists(name): 43 self.__uwsgi.cache_update(name, raw_value) 44 else: 45 self.__uwsgi.cache_set(name, raw_value) 46 47 def get(self, name): 48 if self.__g is None: 49 return self._getuwsgicache(name) 50 return self.__g.get(name, None) 51 52 def set(self, name, value): 53 if self.__g is None: 54 self._setuwsgicache(name, value) 55 else: 56 self.__g[name] = value 更一般的使用方式是在 uWSGI 配置中通过内部路由来使用缓存。这部分可以参考 The uWSGI Caching Cookbook 。\n全文完 ","date":"2017-11-24","description":"","lastmod":"2017-11-24T10:20:14Z","slug":"use-cache-in-uwsgi-with-python","tags":["python","server","uwsgi"],"title":"在 Python+uWSGI 应用中使用缓存","url":"https://blog.zengrong.net/post/use-cache-in-uwsgi-with-python/"},{"categories":["impressions"],"content":"系列 【读书笔记】思考，快与慢（一） 【读书笔记】思考，快与慢（二） 【读书笔记】思考，快与慢（三） 我的理解 这里的基础比率就是前面第7章中的比率忽略的内容。作为人类，我们常常会忽视基础比率，这是因为我们的系统一对更“特殊”的事物感兴趣，而我们碰到的大多数事情都是“一般”的。从另一个方面，系统一在快速判断的时候，又偏爱更“典型”的信息。这是在长期的进化中得到的经验：我们需要关注事物的特殊性以躲避危险，在文明社会之前，一点点的特殊性带来的后果都是致命性的。\n系统一会引起一个错误的直觉，系统二则直接不加评判地采纳这个直觉。忽视和懒惰是主因。在进行复杂的价值判断时，系统一倾向于考虑平均值而不是考虑累加值，这就是“少即是多”的现象存在的原因。\n聪明的女人常常会嫁给不如她们聪明的男人。要理解这个现象，甚至不必使用均值回归来解释。只需要考使用“聪明的男人常常会娶不如她们的女人”这个概念就行了。两个概念其实时同等的概念，只是因为在社会中女性常常是弱者并被认为不如男性聪明，使得“聪明的女人”变成了一个“特殊”事物并具有“典型性”，系统一使用了这个思维定式。\n如果孩子的考试成绩一致很好，突然有一次考试低于 80 分。那么首先考虑的不应该是均值回归现象，而应该是拿到这次考试她的同学的得分进行比较，因为同学之间分数的“相关系数”是很高的。只有相关度不高的情况才会出现回归平均值现象。\n最近红黄蓝事件官方出的调查报告被人诟病也是相同的原因。ZF 的公信力不足，导致了民众的思维定式，再配合 《乌合之众》中提到的“群众不必负责的心态” ，就造成了当前的局面。\n系统一最喜欢的是因果关系，它希望第一时间找到因果，找到了之后它就会通知系统二，然后系统二就信了。\n摘录 名词 基础比率 贝叶斯定理-贝式统计学（Bayesian statistics） 概率和可能性的区别 典型性的两宗罪 合取谬误 思维定式 帮助实验 回归平均值（均值回归） 相关系数 少即是多 概念 \u0026quot;并无迹象表明除了判断典型性以外，受试者还用了别的方法。因为关于概率的问题较难回答，而关于相似性的问题就比较简单，所以在回答时受试者就置换了问题。这是一个严重的错误，因为对相似性和概率的判断所遵守的并不是同一个逻辑规律。我们对相似性的判断可以完全不受基础比率的影响，不受可能会出现的不当描述的影响，但是在判断概率时，如果忽略基础比率和证据的可靠性的话，就注定会犯错误。\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;对于外行人来说，概率（在日常生活中和“可能性”是同义词）是一个相对含糊的概念，与不确定性、倾向性、貌似正确以及出乎意料等词紧密相关。模糊性和令人不爽的感觉不都是这个概念所特有的特性。\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;关于概率或可能性的问题引起了思维的发散性，让人想起比较简单的问题的答案。其中一个简单的答案就是对典型（代表性）的自动评估—在我们理解语言时这种现象很常见。\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;在这些例子及其他更多例子中，典型的形象特征左右着我们对典型性的判断，受这种典型性启发得到的预测有可能是对的，这样的说法在某种程度上就是事实。然而在其他情况下这种典型形象却是错误的，因而典型性的启发也会造成误导，尤其会使人们忽略基础比率信息、找错预测方向。即使启发性具有一定的真实性，但绝对依赖启发效应就是违背统计学逻辑，是有严重“罪过”的。\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;典型性的第一宗罪就是，它过于喜爱预测不可能发生的（低基础比率的）事件。 \u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;一旦人们作出一个错误的直觉判断，系统1和系统2都脱不了干系。系统1引起了错误的直觉，系统2采纳了这个直觉，并将其运用在判断当中。然而，造成系统2犯下此类错误的原因有两个——忽视与懒惰。许多人忽视了基础比率，因为在有个人信息的情况下他们认为基础比率与问题并无关联。另一些人犯下同样的错误则是因为他们没有将注意力集中在任务上。\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;典型性的第二宗罪是它对证据质量不够敏感。请回想系统1的眼见即为事实的原则。在汤姆的问题中，激活你联想机制的是对汤姆的描述，且这个描述不一定是真实的。对汤姆“对人冷淡，缺乏同情心”的表述也许能让你（以及许多其他读者）相信他不太可能是社会科学与社会工作专业的学生。然而，彼时你已经清楚地知道这样的描述是不可信的。\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;原则上讲，你当然知道不值得信任的信息就相当于没有信息，但是眼见即为事实使你难以遵循那条原则。除非你决定立刻否定证据（例如，你坚信的信息是从一个骗子口中得来的），否则你的系统1会自动将这一信息视为真实的。当\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;当你怀疑信息的可靠性时，可以做一件事：作概率判断时，往基础比率那方面想。别期望遵循这条原则会很容易—它需要在付出很多努力的情况下，才能实现自我监督和自我控制。\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;第一，基础比率十分重要，即便是在手头的案例已有证据的情况下依然如此；第二，通过分析证据得到的直观印象通常都会被夸大。\u0026quot; (章节:第14章 猜一下，汤姆的专业是什么？)\n以下是对贝叶斯定理关键点的总结：\n以相对合理的基础比率对结果的可能性作出判断。 质疑你对证据的分析。 (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;通常，当人们没能运用明显相关的逻辑原则时，就会出现“谬误”。阿莫斯和我引入了“合取谬误”（conjunction fallaly）这个想法，通过直接比较，人们总会认为两个事件（在此即为银行出纳和女权主义者）的联合出现比只出现其中一件事（银行出纳）的可能性要大，此时就出现了合取谬误。\u0026quot; (章节:第15章 琳达问题的社会效应)\n\u0026quot;实验经济学家约翰·李斯特（John List）对奚恺元的发现进行了复制，他在真正的市场上拍卖两套相同的高价值棒球卡片，每套各为10张，但其中一套附赠3张普通价值的卡片。就像餐具的例子一样，在综合评估中，数量多的组合会比少的更有价值，但在单一评估中则正好相反。\u0026quot; (章节:第15章 琳达问题的社会效应)\nA.伯格会赢得比赛。 B.伯格会输掉首局。 C.伯格会输掉首局，但会赢得比赛。 D.伯格会赢得首局，但会输掉比赛。 上述结果中B和C两项比较重要。B囊括的内容更多，其概率“一定”比自身所包含的一个事件发生的概率大。受试者给出的答案与逻辑相悖，却顺应了典型性和貌似合理性，72%的人认为B选项比C选项的可能性更小—又一个通过直接比较得出“少即是多”的例子。这一次受试者选出的可能性最大的描述无疑貌似更合理，更符合当今世界一流网球运动员身上所具有的所有公认的特质。 (章节:第15章 琳达问题的社会效应)\n\u0026quot;他们构建了一个非常复杂的情节，还坚持说这个情节出现的可能性很大。这不是真的，这只是个貌似合理的故事而已。\u0026quot; (章节:第15章 琳达问题的社会效应)\n\u0026quot;同一问题的两种表述从数学角度来看并没有区别，但从心理学角度来看则有很大不同。看了第一种表述的人并不知道怎样运用基础比率，通常会忽略它。相反，看到第二种表述的人会对基础比率给予一定重视，他们的平均判断与运用贝叶斯定理解决该问题得出的答案相差不多。\u0026quot; (章节:第16章 因果关系比统计学信息更具说服力)\n\u0026quot;这个出租车的实例阐明了两种基础比率。“统计学基础比率”（statistical base rates）是指某一事件所属类别的事实总量，与单独事件无关；而“因果关系基础比率”（causal base rates）则会改变你对单独事件的看法。对两种基础比率，人们往往会区别对待： ·统计学基础比率普遍受到轻视，当人们手头有与该事件相关的具体信息时，有时还会完全忽略这一比率。 ·因果关系基础比率被视为个别事件的信息，人们很容易将这一比率与其他具体事件的信息结合起来考虑问题。\u0026quot; (章节:第16章 因果关系比统计学信息更具说服力)\n\u0026quot;这些受试者不愿从普遍现象中推导出特殊性，这一点与他们愿意从特殊现象中归纳出普遍性如出一辙。\u0026quot; (章节:第16章 因果关系比统计学信息更具说服力)\n\u0026quot;学习心理学面临的考验是，你对所处环境的理解是否发生了改变，而不是你是否了解到一个新的事实。\u0026quot; (章节:第16章 因果关系比统计学信息更具说服力)\n\u0026quot;思维定式是指人们会（至少暂时会）将自己对某个团体的看法延伸到这个团体中每一个成员的身上（团体存在某些问题，其中的成员无一例外也都会有这些问题）。\u0026quot; (章节:第16章 因果关系比统计学信息更具说服力)\n\u0026quot;他所观察到的就是众所周知的“回归平均值”现象，这种现象与表现质量的随机波动相关。\u0026quot; (标注: 回归平均值) (章节:第17章 所有表现都会回归平均值)\n\u0026quot;两个值之间的“相关系数”指的是两个值共有因素的相对比重。这个值在零和1之间浮动。\u0026quot; (标注: 相关系数) (章节:第17章 所有表现都会回归平均值)\n重要的信息 \u0026quot;我曾无意中发现了人类环境中一个意义重大的事实：生活给予我们的反馈常常违背常理。因为当别人取悦我们时，我们也会对他好；当别人对我们不好时，我们也会对他产生厌恶之情。然而从统计学角度来看，我们却是因为对人友好而受到惩罚，因为举止无礼而得到嘉奖。\u0026quot; (章节:第17章 所有表现都会回归平均值)\n\u0026quot;成功=天赋+运气巨大的成功=更多的天赋+更多的运气\u0026quot; (章节:第17章 所有表现都会回归平均值)\n\u0026quot;我们理解“回归”概念存在很多困难，这些困难皆源自两个系统—系统1和系统2。在相当数量的案例中，即便提供了一些统计数据，若无特殊说明，“相关”与“回归”的关系还是相当模糊的。因此，系统2认为理解这种关系很难。因为从某种程度上讲，这是由于我们总是要求对事物进行因果关系解释，这也是系统1的一个特征。\u0026quot; (章节:第17章 所有表现都会回归平均值)\n有趣的范例 \u0026quot;尽管缺乏创造力，但汤姆智商很高。他喜欢按部就班的简单生活，喜欢干净整洁的环境，屋子里的物件要摆放得规规矩矩。他写的文章枯燥，偶尔会写一些老掉牙的双关语，或者迸发出类似科幻小说的火花，文章还显得有那么点生动。他颇具竞争意识。此外，汤姆待人冷淡，缺乏同情心，也不愿与他人接触。尽管他总是以自我为中心，但却有强烈的道德观念。\u0026quot; (标注: 范例) (章节:第14章 猜一下，汤姆的专业是什么？)\n在大多数情况下，表现得很友好的人实际上也很友好。 又高又瘦的职业运动员很有可能是打篮球的而不是踢足球的。 获得哲学博士学位的人比只读完高中的人更有可能订阅《纽约时报》。 年轻的男性会比年老的女性更不要命地踩油门。 (章节:第14章 猜一下，汤姆的专业是什么？)\n\u0026quot;他们将实验受试者分别带入房间，并要求他们对着麦克风谈论自己的生活和烦恼。他们轮流叙述两分钟，每个房间的麦克风只有在受试者讲述时才会出声。每一组有6位受试者，其中一位是我们派去扮演受试者的工作人员。这位工作人员是第一个叙述的人，他是按照研究人员准备的稿子说的。他说他很难适应纽约的生活，并十分尴尬地承认自己很容易抽搐，在紧张的时候尤其如此。接着，所有受试者都依次叙述。当那位工作人员再次对着麦克风讲述时，他变得焦虑和不连贯，他说他感到一阵抽搐，希望有人能帮助他。他最后几句说的是“有没有人……能……救救我……（喘气声）我……我要……死了，我要……死了（气哽声，然后安静了下来）”。此时，下一位受试者的麦克风被自动打开，人们再也听不到那位有可能濒临死亡的人的动静了。 你认为这个实验的其他受试者会做些什么呢？到现在为止，受试者知道他们中的一员癫痫发作并希望得到帮助，然而他们觉得可能已经有几个人冲出去并提供了帮助，所以自己可以安然地待在隔间中。实验结果是：15个受试者中，只有3个人立刻对请求做出了反应。6个人没有踏出过房间，另外5个人在“癫痫患者”明显气哽时才冲出房间。这项实验说明当某人知道其他人也听到了同样的求救信息时，就会感到自己肩上的责任变小了。\u0026quot; (章节:第16章 因果关系比统计学信息更具说服力)\n\u0026quot;聪明的女人常常会嫁给不如她们聪明的男人。 如果你在朋友聚会时挑起这个话题，一定会引起热烈讨论，大家肯定都愿意分享自己的看法。即使有些对统计学有所了解的人也会很自然地用因果关系去解释这个现象。一些人认为高智商的女人为了避免和同样高智商的男人竞争才这么做；或者是在择偶之时不得不妥协，因为同等智商的男人不愿意与这些女人竞争……也许还会有其他更牵强的解释。现在我们来看看下面的表述： 夫妻二人智商之间的相关性并不是绝对的。 这个观点显然是正确的，而且很无聊。谁会期待这样一种相关性是绝对的呢？那就没有什么好解释的了。不过，你认为有趣的观点和你认为毫无意义的观点又是等值的。如果夫妻二人智商之间的相关性并不是绝对的（如果男人和女人在平均智商上没有差异），那么从数学上来讲，高智商女人嫁给那些不如她们智商高的男人是顺理成章的（反之也成立）。对于这一现象，用回归平均值效应来解释要比用并不绝对的相关性来解释更通俗，也更有说服力。\u0026quot; (章节:第17章 所有表现都会回归平均值)\n\u0026quot;回归效应无处不在，很多可以说明这一效应的误导性因果事件同样司空见惯。有一个经典的例子，那就是“体育画报的诅咒”—凡是登上《体育画报》（Sports Illustrated）这本杂志封面的运动员都会在接下来的赛季中表现欠佳。一般来说，人们会认为过度自信以及人们对其期望过高的压力造成了这些人表现不佳。不过，这个诅咒可以用更简单的方式来解释：能够成为《体育画报》封面人物的运动员在前一赛季一定表现极为出色，也许这种出色的表现在很大程度上源于运气—运气是善变的，接下来他就没那么走运了。\u0026quot; (章节:第17章 所有表现都会回归平均值)\n\u0026quot;父母和子女的相关系数在0.5左右。\u0026quot; \u0026quot;一个物体的型号用英制单位精确测量的结果与用公制单位精确测量的结果之间的相关系数为1。\u0026quot; \u0026quot;美国成年男性自报的身高和体重之间的相关系数为0.41。\u0026quot; \u0026quot;·学术能力评估考试成绩（SAT）和平均绩点（GPA）之间的相关系数大约是0.6。\u0026quot; \u0026quot;美国人收入和教育程度的相关系数约为0.4。\u0026quot; \u0026quot;·家庭收入和他们电话号码后4位之间的相关系数为零\u0026quot; \u0026quot;弗朗西斯·高尔顿用了好几年的时间才确定相关性和回归性并非两个概念—它们只是从不同视角对同一个概念作出的阐释。只要两个数值之间的相关度不高，就会出现回归平均值的情况。\u0026quot; (章节:第17章 所有表现都会回归平均值)\n\u0026quot;“我们的筛选过程并不是很完美，所以我们会考虑回归性。有些极其优秀的候选人也会让我们失望，对此我们并不感到惊讶。”\u0026quot; (章节:第17章 所有表现都会回归平均值)\n未完待续 ","date":"2017-11-20","description":"","lastmod":"2017-12-21T15:04:04Z","slug":"readingnotes-thinking-fast-and-slow2","tags":["reading","readingnote"],"title":"【读书笔记】思考，快与慢（二）","url":"https://blog.zengrong.net/post/readingnotes-thinking-fast-and-slow2/"},{"categories":["impressions"],"content":"系列 【读书笔记】思考，快与慢（一） 【读书笔记】思考，快与慢（二） 【读书笔记】思考，快与慢（三） 这本书断断续续读了三遍，大部分都是用“听”的方式完成的。“听书”（具体方法详见 时间都去哪儿了？——善用工具形成高效习惯）可以充分利用随便时间来进行阅读，是个不错的读书方法。当把 TTS 引擎的读音调到正常语速偏快时，速度也远远小于“看”。也正是因为慢，在“看”的时候可能跳过的细节就会巨细无遗地提现出来。“慢”也给了我一个思考的时间。在听到简单论述的时候，大脑会想到作者后面会如何行文，然后比较作者后面的行文和我的思考之异同，这是一大乐趣。在听到较为复杂的论述时，语音并不会因为我需要思考的时间而停下来，为了能跟上节奏，我必须强迫自己加快思考的效率。当然，碰到重要的内容，我还是必须停下 TTS 进行思考。\n听书的过程，我都是在做着其它事（步行、开车等等），当需要回顾重要内容的时候，不太可能像“看”书一样把页面翻到前面继续回味（最近我就有一次因为回味内容而在地下车库撞到头的经历），这就造成了阅读不连贯的问题。书中的插图也是没有办法语音化的，当听到有关插图的描述内容时，由于在做其他事，也不可能去翻看插图。语音并不会等待，当继续到后面的章节时，前面关于插图的内容就会被大脑错过。即使后面有了翻看插图的机会，多半也不太可能翻到前面去看了。\n解决听书弊端的问题是复习。每天抽出时间来，把“听过”的章节再“看”一遍。这样可以用较少的时间获得牢固的知识印象。\n书籍信息 作者: [美] 丹尼尔·卡尼曼 出版社: 中信出版社 原作名: Thinking, Fast and Slow 译者: 胡晓姣 / 李爱民 / 何梦莹 出版年: 2012-7 页数: 424 定价: 69.00元 装帧: 精装 ISBN: 9787508633558 主观信息 开始日期： 2017-08-13 读完日期： 书籍介绍： 编号1724 我的标签：心理学、社会学、思维方式 我的评分：9.0 我的理解 在这本书里面，能找到几乎所有的思维陷阱。系统一和系统二简直可以说是相爱相杀的典范。我们以为自己能控制自己的思想，反过来却被古老的情感系统控制。多了解一下思考的本质，少一点思考的误区，让我们被这个世界影响得更少一点。\n\u0026quot;一个好故事最重要的是信息的前后一致性，而不是其完整性。\u0026quot;对于系统一的处事方式我也是醉了，为什么我们会在信息匮乏的时候还能做出自以为有把握的决定？这也是系统一追求连贯性而非完整性（数量、质量、论据）造成的。我们常说自己有灵敏的“直觉”，其实最终导致我们选择正确的都是“运气”。\n工作中的焦虑情绪会造成认知紧张，进而推动工作的准确性和深入程度。但是焦虑造成的认知紧张也会导致缺乏创造性以及思维广度忽略。系统二可能会这么认为：我已经这么紧张了，还要啥自行车！\n摘录和思考 由于这本书的复杂性，我把摘录的内容分为四类：名词、概念、重要的建议、有趣的实验。\n正确地概念和名词可以加强我们在日常谈话时的说服力，这些名词也能方便地让我们想起相关的具体概念。名词是从所有的摘录中提取出来的。\n重要的信息包括一些不方便使用单个词语来描述的做法或者建议，这些做法对我们的日常生活非常有用。\n有趣的返利包含作者在书中论述研究结果时引用的实验，这些实验我们也可以做做看。还有一些实际的例子，也可以做写作和谈话的素材。\n名词 系统1和系统2 自我损耗 心流 弗罗里达效应 启动效应 光环效应 认知放松+认知紧张 直觉+错觉 眼见为实 过于自信 比率忽略 框架效应 思维的发散性 替代 概念 \u0026quot;系统1的运行是无意识且快速的，不怎么费脑力，没有感觉，完全处于自主控制状态。\u0026quot;\n\u0026quot;系统2将注意力转移到需要费脑力的大脑活动上来，例如复杂的运算。系统2的运行通常与行为、选择和专注等主观体验相关联。\u0026quot;\n\u0026quot;系统2的运作是高度多样化的，但所有这些运作方式都有一个共同特征：所有运作都需要集中注意力，如若注意力分散，运作也会随之中断。\u0026quot;\n\u0026quot;系统1不断为系统2提供印象、直觉、意向和感觉等信息。如果系统2接收了这些信息，则会将印象、直觉等转变为信念，将冲动转化为自主行为。通常情况下，一切都会顺利进行，系统2会稍微调整或是毫无保留地接受系统1的建议。\u0026quot;\n\u0026quot;当系统1的运行遇到阻碍时，便会向系统2寻求支持，请求系统2给出更为详细和明确的处理方式来解决当前问题。\u0026quot; (章节:第1章 一张愤怒的脸和一道乘法题)\n\u0026quot;系统2和你家里的电表能力都有限，但它们对超负荷的负载反应不同。当用电超负荷时，断路器会跳闸，致使那条线路上的所有电器都断电。相反，如果大脑的使用超负荷，其处理则是有选择性且精确的：系统2会偏向最重要的活动，因此这个活动会得到其所需的注意力，其他“多出来的”注意力再慢慢被分配到其他任务中去。\u0026quot;\n\u0026quot;当你对执行一个任务越来越熟练时，需要付出的努力程度就会降低。对大脑的各项研究证明，与行动相关的活动模式会随着熟练程度的加强而变化，一些大脑区域将不再参与其中。天才也是如此。通过观察瞳孔变化和大脑活动，我们发现高智商的人往往需要较少的努力便可解决同样的问题。普遍的“最省力法则”不仅适用于体力活儿，还适用于我们的认知行为。这个法则主张，如果达成同一个目标的方法有多种，人们往往会选择最简单的那一种。在经济行为中，付出就是成本，学习技能是为了追求利益和成本的平衡。因为懒惰是人类的本性。\u0026quot; (章节:第2章 电影的主角与配角)\n\u0026quot;心流巧妙地区分了两种努力形式：对任务的关注和对注意力的严格控制。以每小时150英里的速度骑摩托车和在象棋大赛中角逐都需要付出努力，然而在心流状态下，集中注意力关注吸引人的事并不要求自我控制。因此，我们要将所有资源都用于手头上的任务才好。\u0026quot; (标注: 心流) (章节:第3章 惰性思维与延迟满足的矛盾)\n\u0026quot;刻意掌控意志和进行自我控制很辛苦。如果你必须强迫自己去做某件事，而此时这件事又面临一个新的挑战，你就会很不情愿或是根本无法进行自我控制。这种现象被命名为自我损耗（ego depletion）。\u0026quot; (标注: 自我损耗) (章节:第3章 惰性思维与延迟满足的矛盾)\n\u0026quot;启动效应：让人不知不觉微笑的铅笔\u0026quot; (标注: 启动效应) (章节:第4章 联想的神奇力量)\n\u0026quot;问题的关键是要接受相关研究的结果，而不是对此心存怀疑。这些结果不是捏造出来的，也不是统计上的偶然现象。你别无选择，只能接受这些研究的主要结论是正确的这一事实。更重要的是，你必须承认这些结论对你自己来说也是正确的。\u0026quot; (标注: 相信启动效应) (章节:第4章 联想的神奇力量)\n\u0026quot;上述所有发现有一个共同主题，即钱这一概念会滋生个人主义：不愿和他人在一起，不愿依赖他人，也不愿接受他人的请求。\u0026quot; \u0026quot;有些文化常常提醒人们尊重他人，另外一些文化则常让人们想起上帝，还有一些国家的人们会对着伟大领袖的照片顶礼膜拜。在一个专制国家中，到处挂着领袖的肖像不仅能向你传达“老大哥在看着你”①的感觉，还会使你逐渐丧失自主的思想和独立的行为能力。\u0026quot; \u0026quot;你不相信这些结论适用于你，因为这些结论与你的主观体验不相符，你的主观体验主要是由系统2决定的。启动效应来自系统1，而这个效应发生时，你根本就意识不到。\u0026quot; (章节:第4章 联想的神奇力量) \u0026quot;在一项实验中，心理学家约翰·巴奇（John Bargh）和他的同事们让纽约大学的数位学生从一个包含5个单词的词组中（例如“发现、他、它、黄色的、马上”）挑出4个单词来重组句子。其中一个小组的学生重组的句子中有一半都含有与老年人相关的词汇，例如佛罗里达州、健忘的、秃顶的、灰白的或者满脸皱纹的。当他们完成这项任务时，又被叫到大厅另一头的办公室里去参加另一个实验。从大厅的一头走到另一头是这次实验的关键所在。研究者悄悄地测量了他们所用的时间。正如巴奇预料的那样，那些以老年为主题造句子的年轻人比其他人走得要慢得多。这个实验后来成了经典案例。\u0026quot; (标注: 弗罗里达效应) (章节:第4章 联想的神奇力量)\n\u0026quot;认知放松度介于“放松”和“紧张”之间。放松是事情进展顺利的标志——没有障碍、没有新情况、没必要转移注意力或投入更多精力。紧张说明存在某种问题，且需要不断调动系统2参与其中。如若事情进展不顺利，你便处于认知紧张状态中。认知紧张同时还会受当时的努力程度和未得到满足的需求的影响。\u0026quot; \u0026quot;只要熟悉其中一个短语，就会觉得对整个陈述都很熟悉，也会因此对陈述内容信以为真。如果你记不清楚某个陈述的来源，也无法将其与自己知道的事物联系起来，这时你就别无选择，只能跟着认知放松的感觉走了。\u0026quot; (标注: 认知放松和认知紧张) (章节:第5章 你的直觉有可能只是错觉)\n\u0026quot;你的大脑愿意甚至急切地想要辨别一些因素，确定这些因素的特点和特殊意图，并将其活动视为表达个人偏爱的举动。\u0026quot; (章节:第6章 意料之外与情理之中)\n\u0026quot;喜爱（或讨厌）某个人就会喜爱（或讨厌）这个人的全部—包括你还没有观察到的方面—这种倾向就叫做光环效应。\u0026quot; (标注: 光环效应) (章节:第7章 字母“B”与数字“13”)\n\u0026quot;过于自信：正如眼见即为事实原则指出的那样，无论是证据的数量还是质量都与主观自信关系不大。每个人对自身想法的自信程度主要取决于他们对亲眼所见的事情的讲述效果，即使他们几乎什么都没有看到也没有关系。\u0026quot; (标注: 眼见为实原则对过于自信现象的解释) (章节:第7章 字母“B”与数字“13”)\n\u0026quot;框架效应：同一信息的不同表达方式常常会激发人们不同的情感。“手术后一个月内的存活率是90%”的说法要比“手术后一个月的死亡率是10%”更令人安心。同样，说凉菜“90%不含脂肪”要比说“10%含有脂肪”更具吸引力。很明显，前述每组句子的深层含义都是相同的，只是表达方式不同而已，但人们通常能读出不同的含义，而且觉得自己的所见就是事实。\u0026quot; (标注: 眼见为实原则对框架效应的解释) (章节:第7章 字母“B”与数字“13”)\n\u0026quot;比率忽略：回忆一下史蒂夫，那个本性怯懦、做事井井有条，常被看做图书管理员的人。这个人物性格的描述是生动形象的，尽管你清楚地知道男性农民比男性图书管理员多，但在考虑这个问题时，你总会忽略这个事实。你觉得自己的所见即为事实。\u0026quot; (标注: 眼见为实原则对比率忽略现象的解释) (章节:第7章 字母“B”与数字“13”)\n\u0026quot;在这两个例子中最为关键的一点是：你作了一个确切的选择，但自己却没有意识到自己这样做了。你的脑海中出现的只有一种解释，而且你从未意识到这些问题会有歧义。系统1不会记得自己放弃的几个选项，甚至都不记得曾有过多种选择。有意识的怀疑需要同时在脑中记住多种互不相容的解释，需要付出努力，而这并不是系统1的长项。易变和怀疑是系统2的职责范围。 \u0026quot; (章节:第7章 字母“B”与数字“13”)\n\u0026quot;我们计算的结果往往比自己想要的或需要的要多。这种过量计算的过程就体现了“思维的发散性”。如同想用散弹猎枪瞄准一个点是不可能的一样（它射出的子弹是分散的），想要让系统1完全执行系统2的命令且不做多余的工作也很难，这一点与散弹枪很相似。\u0026quot; (标注: 思维发散性) (章节:第8章 我们究竟是如何作出判断的？)\n\u0026quot;对于有难度的问题，我们总是很难快速找到令人满意的答案，此时系统1就会找到一个相关问题来回答，这个问题比原来的问题更易作答。我把这种回答一个问题而绕开另一个问题的做法叫做“替代”。\u0026quot; (标注: 替代现象) (章节:第9章 目标问题与启发性问题形影不离)\n\u0026quot;当人们按照要求对可能性作出判断时，他们实际上是对其他的事情作了判断，并且认为自己已经完成了判断可能性的任务。遇到很难的“目标问题”时，如果脑海中马上出现了一些与之相关联且容易回答的“启发性问题”的答案，系统1通常便会采取这种“替代”的做法，采用替代问题的答案。\u0026quot; (标注: 替代于启发性问题) (章节:第9章 目标问题与启发性问题形影不离)\n\u0026quot;思维的发散性可以使懒惰的系统2摆脱很多繁重的工作，快速找到难题的答案。\u0026quot; (章节:第9章 目标问题与启发性问题形影不离)\n\u0026quot;系统1并不善于质疑。它抑制了不明确的信息，不由自主地将信息处理得尽可能连贯。除非该信息被立刻否定，不然，它引发的联想就会扩散开，仿佛这条信息就是千真万确的。系统2能够提出质疑，因为它可以同时包含不相容的多种可能性。\u0026quot; (章节:第10章 大数法则与小数定律)\n\u0026quot;系统1试图建立一个将锚定数字视为真实数值的世界。\u0026quot; (章节:第11章 锚定效应在生活中随处可见)\n\u0026quot;随机锚定效应还会使我们更加了解系统1和系统2之间的关系。人们一直利用判断与选择的案例来研究锚定效应，而判断与选择最终总是由系统2完成的。但是，系统2对从记忆中提取的数据进行加工，并由系统1进行自主的、无意识的运行，因此很容易受锚定效应的影响，而这种影响会使某些信息更容易让人回想起来。此外，系统2对这种影响一无所知，也无法控制。\u0026quot; (章节:第11章 锚定效应在生活中随处可见)\n\u0026quot;在讨论小数定律时我们发现，如果一则消息没有马上被视为谎言，那么不管其可靠性如何，它都会对联想系统产生同样的影响。这个消息的重点是故事，随便根据什么信息编造的都无所谓，即使这则消息的信息量很少，质量很差劲也无所谓，因为眼见即为事实。\u0026quot; (章节:第11章 锚定效应在生活中随处可见)\n\u0026quot;你总能意识到锚定，甚至会对它格外关注，但你不知道它是如何引导和限制你的思考的，因为你不能想象如果锚定改变（或不存在）你会如何思考。但是，你应该假设任何一个公开谈判时的数字都对你有锚定效应，如果概率大，你应该抵制（你的系统2）该效应。\u0026quot; (章节:第11章 锚定效应在生活中随处可见)\n\u0026quot;为偏见植入政策这一机制起了个名字“效用层叠”（availability cascade）①。他们评论道，在社会大背景下：“所有的启发式都是平等的，但可得性相比而言更平等。”他们了解广义的启发式概念，在这个概念中，可得性为判断（而不是概率）提供了启发，尤其是我们通过想到某个概念的轻松程度（和情感的释放）来判断其重要性时，这种启发的作用就体现出来了。\u0026quot; (标注: 效用层叠的解释) (章节:第13章 焦虑情绪与风险政策的设计)\n\u0026quot;我们脑海中的世界并不是真实世界的准确反映；我们对事件发生频率的估测也会受到自己接触这些信息和频率与个人情感强烈程度等因素的影响。\u0026quot; \u0026quot;“感性细节掌控理性大局。”情绪启发式通过创造一个比现实更明了的世界来简化我们的生活。好的技术在我们的虚拟世界中成本较小，不好的技术没有利益，所有的决策在这里都变得很简单。当然，在现实世界中我们常要在利益和成本中作出权衡。\u0026quot; \u0026quot;艾拉事件说明，我们的大脑解决小风险的能力有一个基本限度：我们要么完全忽视风险，要么过于重视风险，没有中间地带。每位等待晚归女儿的家长都能体验到这种感觉。你也许知道真的是（几乎是）没有什么可担心的，但你的大脑会不自觉地闪现危险的景象。\u0026quot; (章节:第13章 焦虑情绪与风险政策的设计) 重要的信息 \u0026quot;即为什么很难具备统计型思维。我们思考时总是会把多种事情联系起来，会将一件事情比喻成另一件，会突然想起一件事来，但统计学要求同一时间把多件事情串联起来，而这一点系统1是做不到的。\u0026quot; (章节:start)\n\u0026quot;我们对自己认为熟知的事物确信不疑，我们显然无法了解自己的无知程度，无法确切了解自己所生活的这个世界的不确定性。我们总是高估自己对世界的了解，却低估了事件中存在的偶然性。当我们回顾以往时，由于后见之明，对有些事会产生虚幻的确定感，因此我们变得过于自信。\u0026quot; (章节:start)\n\u0026quot;受试者并没有意识到上述问题，他们只是习惯性地将否定或接受的态度与其常用的身体语言联系起来而已。由此可见，老话讲“不管你怎么想的，都得心平气和”，这真是条很好的建议，只有真正做到心平气和，你才可能有回报。\u0026quot; (标注: 不管你怎么想的，都得心平气和) (章节:第4章 联想的神奇力量)\n\u0026quot;我采取的避免光环效应的评卷方法遵循了一个普遍原则：消除错误的关联！\u0026quot;\n\u0026quot;想要从大量证据来源中获取最有用的信息，你应设法使这些来源相互独立。\u0026quot;\n\u0026quot;一条简单的规则就能发挥作用：在开始讨论某个问题之前，先让与会的每一位成员各自写下简短的意见阐明自己的观点。这个过程很好地利用了小组里不同知识和见解的价值。而开放性讨论这一常规做法总会注重那些发言早而又强势的人的意见，使得其他人一味附和他们的观点。\u0026quot;\n\u0026quot;联想机制一个最基本的结构特点就是它只能回忆起已被激活的观点。无法从记忆中获取的信息（即使是无意识的）可能并不存在。系统1善于提取当前激活的想法来构建最可信的故事情节，但它不会（也不能）提取本系统中根本不存在的信息。\u0026quot;\n\u0026quot;衡量系统1是否成功的方法是看它所创造的情境是否具有连贯性，而与故事所需数据的数量和质量关系不大。信息匮乏是常事，一旦出现这种情况，系统1则会仓促作出结论。\u0026quot;\n\u0026quot;一个好故事最重要的是信息的前后一致性，而不是其完整性。\u0026quot;\n\u0026quot;当我们对答案不确定时，系统1就根据过往经历去赌一个答案。这种下赌的规则是明智的：最近发生的事及当前情境是作抉择时最重要的因素。如果脑中没有闪现出任何最近发生的事，那更为遥远的记忆便会呈现出来。你最早、记忆最为深刻的经历一定是唱英文字母歌，这首歌的开头一定是“ABC”，而不是“A13 C”。\u0026quot;\n\u0026quot;有系统2参与时，我们几乎会相信所有事情。因为系统1不仅好骗，还容易产生偏见，而尽管系统2掌管怀疑和不信任的大权，但是它有时很忙，不忙时也很懒惰，总会擅离职守。的确，已有证据显示，当人们劳累或是精力耗尽时，更容易受那些空洞却有说服力的信息影响，例如广告。\u0026quot;\n\u0026quot;系统1可以通过很多比现实更简单却更连贯的方式来表现这个世界，光环效应就是其中一种。 \u0026quot; (章节:第7章 字母“B”与数字“13”)\n\u0026quot;我们也可以立刻对众多物品的数量生成一个印象，如果其数量只有4个或少于4个的话，印象会很精确；如果更多的话，就会变得模糊。\u0026quot;\n\u0026quot;他们通过一种范畴向另一范畴的转换来回答这个问题，并且选出了相应的学分积点值。我们也明白为什么这种利用匹配进行预测的模式从统计学角度来看是错误的，尽管对于系统1来说这很正常，但对于统计学家以外的大多数人来说，系统2也可以接受这种做法。\u0026quot;\n\u0026quot;思维的发散性让我们作出直觉性判断\u0026quot; (章节:第8章 我们究竟是如何作出判断的？)\n\u0026quot;我在教学生谈判时，给他们的建议是如果你认为是对方作出了无礼的提议，你就不应该提出同样无礼的提议，因为两者之间有距离的话会使此后的商谈难以进行。你应该大吵大闹，夺门而出，或者威胁对方说自己也会这样做，要让对方明白以这个数字为基准的话，谈判将难以继续。 \u0026quot; (章节:第11章 锚定效应在生活中随处可见)\n\u0026quot;回答时其实根本就不需要复杂的推理。在系统1的基本特征中，其中一点就是这一系统具有设定预期的能力，当现实与预期相悖时它就会感到惊讶。该系统还会提取造成惊讶情绪的可能原因—通常是在近期所经历的各种惊讶体验中找到一个可能的原因。此外，系统2在运行中会重塑系统1的预期，因此一件本该令人惊讶的事就变得正常了。\u0026quot; \u0026quot;判断涉及自身情况的人往往更有可能关注他们从记忆中提取的事件数量，对顺畅度则不大关注。\u0026quot; \u0026quot;由此可见，事件在脑海中呈现的轻松程度体现出系统1的启发作用，然而当系统2越来越多地参与其中时，受试者关注的就不再是提取记忆的轻松度，而是回忆起来的若干事例的内容了。各种各样的证据都指向统一结论，即那些跟着系统1走的人更容易受可得性偏见的影响，比那些警惕性更高的人受影响的程度更大。\u0026quot; \u0026quot;在下面这些情况中，人们都在“跟着感觉走”，提取轻松度对他们的影响要大于其回想事例内容带给他们的影响： ·当他们同时忙于另一件需要付出努力的任务时。 ·因他们刚刚想起生命中的一个快乐片段而心情大好时。 ·如果他们在抑郁量表中得分很低的话。 ·尽管对这项任务所给话题的了解达不到专家级水准，但他们也算是对此领域了解颇多的新手了。 ·他们跟着感觉走却拿了高分时。 ·如果他们（或别人令他们感到）很强大时。\u0026quot; (章节:第12章 科学地利用可得性启发法) \u0026quot;为偏见植入政策这一机制起了个名字“效用层叠”（availability cascade）。他们评论道，在社会大背景下：“所有的启发式都是平等的，但可得性相比而言更平等。”他们了解广义的启发式概念，在这个概念中，可得性为判断（而不是概率）提供了启发，尤其是我们通过想到某个概念的轻松程度（和情感的释放）来判断其重要性时，这种启发的作用就体现出来了。 效用层叠是一连串自持事件，它可能开始于对相对次要的事件的媒体报道，然后会引起公众恐慌和大规模的政府行动。有些情况下，关于某一风险的媒体报道能抓住部分公众的注意力，这部分注意力进而会变成激愤和焦虑。这种情感反应本身就是一种宣扬，会推动媒体跟进报道，继而会令人产生更大的焦虑，波及面也更大。\u0026quot; (章节:第13章 焦虑情绪与风险政策的设计)\n有趣的范例 \u0026quot;在《看不见的大猩猩》（The Invisible Gorilla）一书中，克里斯托弗 ·查布里斯（Christopher Chabris）和丹尼尔 ·西蒙斯（Daniel Simons）两位作者为我们提供了一个最具戏剧性的证明。他们设计了一部两队传篮球的短片，其中一队穿的是白色球衣，另一队穿的是黑色球衣。观看短片的人需要数出白衣球队的传球次数，忽略掉另一队传的球。这个任务比较困难，需要完全投入才行。短片播到一半时，一个套着大猩猩服装的女人出现了，她穿过球场，捶着胸，然后继续走动。这只“猩猩”出现了9秒钟。上万人看了这部短片，其中约有一半人并未注意到有什么异。之所以这样，是因为这个计数任务——尤其是那个忽略黑衣球队的要求——造成了这种屏蔽。\u0026quot; (标注: 范例) (章节:第1章 一张愤怒的脸和一道乘法题)\n\u0026quot;“有时，你会碰到一两个这样的病人—他会像说故事一样，讲述自己以前遭遇的误诊，这些诊断五花八门，让人担心。他看过几个临床医生，但都没多大效果。这个病人还能清楚地描述医生是如何误解他的，但他很快就观察到，你和其他医生是不一样的，你能感同身受，充分理解他，并可以为他提供帮助。”此时，我的老师提高了音量，继续讲道：“千万别有接收这个病人的想法！将他赶走！他很有可能是位精神病患者，而且你也帮不了他。”\u0026quot; (标注: 范例) (章节:第1章 一张愤怒的脸和一道乘法题)\n\u0026quot;你即将要执行的任务叫做加1，以下是其具体做法： 敲打出稳定的节奏（最好是有一个节拍器，并将其设定为一秒一拍）。移动空白卡纸，大声读出数字。然后等待两个节拍，说出一个新的数字（这个数字是将原来那个数字的每一位都加1得来的）。例如：卡片上的数字是5294，新的数字就应该是6305。另外，跟上节奏很重要。 很少有人在加1任务中能胜任超过4位数的数字，但如果你想挑战一下自己，可以尝试一下加3的任务。 如果想知道大脑在快速运转时身体正在干些什么的话，你可以这样做：在书桌上堆两摞书，将你的下巴放在其中一摞上，将一台摄像机放在另一摞上。打开摄像机，在你做加1或加3任务时，盯着摄像机的镜头看。然后，你可以通过摄像机真实的记录发现，你的瞳孔大小会随着你的努力程度而变化。\u0026quot; \u0026quot;时间制约是人们付出努力的另一个驱动因素。执行加3任务时，你的匆忙一方面是因为节拍器，另一方面是因为记忆负荷。\u0026quot; (标注: 范例) (章节:第2章 电影的主角与配角)\n\u0026quot;示例—启动效应 “这些人都穿着刻板的制服，看到他们时我们的大脑中是不会有什么创造性想法的。” “这个世界比你想象的要复杂得多，能否对它有个清晰的认识多半要看你的大脑的工作方式。” “他们的作用就是发现问题，而他们也的确发现了很多问题。” “系统1编了一个故事，而系统2也相信了这个故事。我们每个人都有过这种体验。” “我让自己微笑，这样做我也的确感觉好多了！”\u0026quot; (章节:第4章 联想的神奇力量)\n\u0026quot;你在某派对上遇到了个名叫琼的女士，发现她既漂亮又善谈。现在，她的名字再次出现，并有可能是被叫去捐款。你知道琼有多慷慨吗？正确答案是：你事实上什么都不知道，因为没有理由可以让你认为善于社交的人在慈善方面会表现得慷慨。但你喜爱琼，当你想到琼时，那种喜爱的感觉会再次涌上心头。你自己慷慨，也喜欢慷慨的人。通过联想，你预先倾向于相信琼是慷慨的。现在，你认为琼是慷慨的，你可能会比以前更喜欢她，因为你又增加了一条令她讨你喜欢的特点。\u0026quot; (章节:第7章 字母“B”与数字“13”)\n\u0026quot;这个例子就是1989年的艾拉事件，也就是环境问题批评者口中的“艾拉恐慌”。艾拉是种化学品，喷洒到苹果上用以调节苹果的生长周期并改善其外观。有报道称该化学品用量大，可导致大老鼠和家鼠得癌症，恐慌便由此引发。报道自然可以吓到众人，而且这些恐慌情绪也促使媒体争相报道，这就是效用层叠的基本机制。这一主题对新闻形成引导作用，进而引发了重大的媒体事件，例如梅丽尔·斯特里普（Meryl Streep）在国会前的证词。由于苹果和苹果产品引起人们的恐慌，苹果产业损失巨大。库兰和桑斯坦引用了一位打来电话的居民的话，此人问道：“是把苹果汁倒进下水道更安全，还是扔到有毒废物垃圾场更安全？”生产商回收了苹果杀虫剂产品，美国食品药品管理局也对此产品颁布禁令。此后的研究证实这种物质致癌的可能性很小，艾拉事件显然是对一个小问题做出的过激反应。\u0026quot; (标注: 艾拉事件) (章节:第13章 焦虑情绪与风险政策的设计)\n\u0026quot;中风致死的数量几乎是所有意外事故致死总数的2倍，但80%的受试者却判断意外事故致死的可能性更大。 ·人们认为龙卷风比哮喘更容易致死，尽管后者的致死率是前者的20倍。 ·人们认为被闪电击中致死的概率比食物中毒要小，不过，前者致死率却是后者的52倍。 ·得病致死是意外死亡的18倍，但两者却被认为概率相等。 ·意外死亡被认为是糖尿病致死率的300倍，但真正的比率却是1∶4.\u0026quot; (标注: 常见的偏见) (章节:第13章 焦虑情绪与风险政策的设计)\n未完待续 ","date":"2017-11-10","description":"","lastmod":"2017-12-21T15:03:43Z","slug":"readingnotes-thinking-fast-and-slow1","tags":["reading","readingnote"],"title":"【读书笔记】思考，快与慢（一）","url":"https://blog.zengrong.net/post/readingnotes-thinking-fast-and-slow1/"},{"categories":["technology"],"content":"SQLAlchemy 是一个功能强大的 ORM 。 Flask-SQLAlchemy 是一个 Flask 插件，它让我们在 Flask 框架中使用 SQLAlchemy 变得更容易。\n本篇介绍我在使用 Flask-SQLAlchemy 2.1 时进行联表查询的一些经验。\n表定义 这里有两个表，account 表保存帐号 ID 和昵称，bind 表保存 account 之间的绑定关系。\n1# 省略了外键定义，请自行脑补 2create table account 3( 4\tgameuid int auto_increment primary key, 5 nickname varchar(34) not null 6); 7create table bind 8( 9\tbindid int auto_increment primary key, 10\tfromid int not null, 11\ttoid int not null 12); 对应的 Model 如下：\n1class Account(db.Model): 2 __tablename__ = \u0026#39;account\u0026#39; 3 gameuid = db.Column(db.INT, primary_key=True, nullable=False, autoincrement=True) 4 nickname = db.Column(db.VARCHAR(64), nullable=False, unique=True) 5 def __repr__(self): 6 return \u0026#39;\u0026lt;Account %r\u0026gt;\u0026#39; % (self.gameuid) 7 8class Bind(db.Model): 9 __tablename__ = \u0026#39;bind\u0026#39; 10 bindid = db.Column(db.BIGINT, primary_key=True, autoincrement=True) 11 # 绑定者和被绑定者的 gameuid 12 fromid = db.Column(db.BIGINT, db.ForeignKey(\u0026#39;account.gameuid\u0026#39;), nullable=False) 13 toid = db.Column(db.BIGINT, db.ForeignKey(\u0026#39;account.gameuid\u0026#39;), nullable=False) 14 def __repr__(self): 15 return \u0026#39;\u0026lt;Bind %r.%r\u0026gt;\u0026#39; % (self.fromid, self.toid) 关联查询 先来看一个简单的例子：查询 gameuid 1000 账号下绑定的所有帐号。\n1\u0026gt;\u0026gt;\u0026gt; db.session.query(Bind.bindid, Bind.fromid, Bind.toid, Account.gameuid, Account.nickname). \\ 2 filter(Bind.toid == 1000). \\ 3 filter(Bind.fromid == Account.gameuid). \\ 4 all() 5[(2, 10001, 1000, 10001, \u0026#39;玩家10001\u0026#39;), (3, 10002, 1000, 10002, \u0026#39;玩家10002\u0026#39;), (4, 10003, 1000, 10003, \u0026#39;玩家10003\u0026#39;), (5, 10004, 1000, 10004, \u0026#39;玩家10004\u0026#39;), (6, 10005, 1000, 10005, \u0026#39;玩家10005\u0026#39;), (7, 10006, 1000, 10006, \u0026#39;玩家10006\u0026#39;), (8, 10007, 1000, 10007, \u0026#39;玩家10007\u0026#39;), (9, 10008, 1000, 10008, \u0026#39;玩家10008\u0026#39;), (10, 10009, 1000, 10009, \u0026#39;玩家10009\u0026#39;), (53, 10000, 1000, 10000, \u0026#39;玩家10000\u0026#39;), (54, 11000, 1000, 11000, \u0026#39;玩家11000\u0026#39;)] 看一看生成的 SQL 语句：\n1\u0026gt;\u0026gt;\u0026gt; print(db.session.query(Bind.bindid, Bind.fromid, Bind.toid, Account.gameuid, Account.nickname). \\ 2 filter(Bind.toid == 1000). \\ 3 filter(Bind.fromid == Account.gameuid)) 4SELECT bind.bindid AS bind_bindid, bind.fromid AS bind_fromid, bind.toid AS bind_toid, account.gameuid AS account_gameuid, account.nickname AS account_nickname 5FROM bind, account 6WHERE bind.toid = %(toid_1)s AND bind.fromid = account.gameuid 这里的联表查询使用的是 WHERE 语句。如果希望使用 JOIN 语句，可以这样写：\n1\u0026gt;\u0026gt;\u0026gt; print(db.session.query(Bind.bindid, Account.gameuid, Account.nickname). \\ 2 join(Account, Account.gameuid==Bind.fromid). \\ 3 filter(Bind.toid == 1000)) 4SELECT bind.bindid AS bind_bindid, bind.fromid AS bind_fromid, account.gameuid AS account_gameuid, account.nickname AS account_nickname 5FROM bind INNER JOIN account ON account.gameuid = bind.fromid 6WHERE bind.toid = %(toid_1)s 可以看出，现在生成的 SQL 语句已经使用 JOIN 语句了。但上面的语意有点奇怪，既然已经在 query 中使用了 Bind 和 Account，后面再 join 一次 Account 总觉得有点多余。那么 SQLAlchemy 如何选择 JOIN 的时候谁先谁后呢？看看这个错误就知道了：\n1\u0026gt;\u0026gt;\u0026gt; db.session.query(Bind.bindid, Bind.fromid, Account.gameuid, Account.nickname). \\ 2 join(Bind, Account.gameuid==Bind.fromid). \\ 3 filter(Bind.toid == 1000) 4\u0026gt;\u0026gt;\u0026gt; Traceback (most recent call last): 5 File \u0026#34;\u0026lt;console\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt; 6 File \u0026#34;/Users/zrong/.pyvenv/api/lib/python3.6/site-packages/sqlalchemy/orm/query.py\u0026#34;, line 1971, in join 7 from_joinpoint=from_joinpoint) 8 File \u0026#34;\u0026lt;string\u0026gt;\u0026#34;, line 2, in _join 9 File \u0026#34;/Users/zrong/.pyvenv/api/lib/python3.6/site-packages/sqlalchemy/orm/base.py\u0026#34;, line 201, in generate 10 fn(self, *args[1:], **kw) 11 File \u0026#34;/Users/zrong/.pyvenv/api/lib/python3.6/site-packages/sqlalchemy/orm/query.py\u0026#34;, line 2115, in _join 12 outerjoin, full, create_aliases, prop) 13 File \u0026#34;/Users/zrong/.pyvenv/api/lib/python3.6/site-packages/sqlalchemy/orm/query.py\u0026#34;, line 2171, in _join_left_to_right 14 l_info.selectable) 15sqlalchemy.exc.InvalidRequestError: Can\u0026#39;t join table/selectable \u0026#39;bind\u0026#39; to itself 这个错误显然说明，query 中参数的顺序很重要，第一个参数所代表的 table 就是 JOIN 时放在前面的那个 table。因此，此处 JOIN 的目标应该是 Account， 而不应该是 Bind 自身。\n分页支持 上面的例子已经解决了大多数需求了。我们再来看看分页。在 Flask-SQLAlchemy 中封装了一个 paginate 方法，可以方便地将查询记录进行分页：\n1\u0026gt;\u0026gt;\u0026gt; db.session.query(Bind.bindid, Bind.fromid, Account.gameuid, Account.nickname). \\ 2 join(Bind, Account.gameuid==Bind.fromid). \\ 3 filter(Bind.toid == 1000). \\ 4 paginate(1, 10) 5Traceback (most recent call last): 6 File \u0026#34;\u0026lt;console\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt; 7AttributeError: \u0026#39;Query\u0026#39; object has no attribute \u0026#39;paginate\u0026#39; 报错的原因是 db.session.query 默认返回的是 orm.Query 对象，这个对象并不包含 paginate 方法。要解决这个问题，需要修改 Flask-SQLAlchemy 的源码。\n找到 SQLAlchemy 对象的 __init__ 定义，在其中加入 session_options['query_cls'] = BaseQuery 即可：\n1def __init__(self, app=None, use_native_unicode=True, session_options=None, metadata=None): 2 3 if session_options is None: 4 session_options = {} 5 6 session_options.setdefault(\u0026#39;scopefunc\u0026#39;, connection_stack.__ident_func__) 7 self.use_native_unicode = use_native_unicode 8 self.app = app 9 10 # 使用 BaseQuery，这样可以让使用 db.session.query 等方法创建的 Query 对象支持 BaseQuery 的方法 11 session_options[\u0026#39;query_cls\u0026#39;] = BaseQuery 另一种关联查询语法 在 Flask-SQLAlchemy 提供的 Model 对象中，可以使用 Model.query 这样的语法来直接得到一个查询对象，这是由于 Flask-SQLAlchemy 中存在一个 _QueryProperty 类，每次调用 Model.__get__ 时，会自动生成一个基于当前 session 的 query 对象：\n1class _QueryProperty(object): 2 3 def __init__(self, sa): 4 self.sa = sa 5 6 def __get__(self, obj, type): 7 try: 8 mapper = orm.class_mapper(type) 9 if mapper: 10 return type.query_class(mapper, session=self.sa.session()) 11 except UnmappedClassError: 12 return None 使用 Model.query 得到的这个 query 对象可以直接进行 JOIN 操作，得到的结果是 Model 对象。这样就方便多了：\n1\u0026gt;\u0026gt;\u0026gt; Account.query.join(Bind, Bind.fromid == Account.gameuid).filter(Bind.toid == 1000).all() 2[\u0026lt;Account 10001\u0026gt;, \u0026lt;Account 10002\u0026gt;, \u0026lt;Account 10003\u0026gt;, \u0026lt;Account 10004\u0026gt;, \u0026lt;Account 10005\u0026gt;, \u0026lt;Account 10006\u0026gt;, \u0026lt;Account 10007\u0026gt;, \u0026lt;Account 10008\u0026gt;, \u0026lt;Account 10009\u0026gt;, \u0026lt;Account 10000\u0026gt;, \u0026lt;Account 11000\u0026gt;] 转换成 SQL 是这样的：\n1SELECT account.gameuid AS account_gameuid, account.nickname AS account_nickname 2FROM account INNER JOIN bind ON bind.fromid = account.gameuid 3WHERE bind.toid = %(toid_1)s 可以看出，这样的查询结果和使用 db.session.query 并没有什么不同。由于返回的是 Model 对象，使用上可能还更加方便了。\n筛选字段 如何使用 Model.query.join 语法得到部分字段呢？这里可以使用 SQLAlchemy 提供的 with_eitities 方法：\n1\u0026gt;\u0026gt;\u0026gt; Account.query.join(Bind, Bind.fromid == Account.gameuid). \\ 2 filter(Bind.toid == 1000). \\ 3 with_entities(Bind.bindid, Account.nickname).all() 4[(2, \u0026#39;玩家10001\u0026#39;), (3, \u0026#39;玩家10002\u0026#39;), (4, \u0026#39;玩家10003\u0026#39;), (5, \u0026#39;玩家10004\u0026#39;), (6, \u0026#39;玩家10005\u0026#39;), (7, \u0026#39;玩家10006\u0026#39;), (8, \u0026#39;玩家10007\u0026#39;), (9, \u0026#39;玩家10008\u0026#39;), (10, \u0026#39;玩家10009\u0026#39;), (53, \u0026#39;玩家10000\u0026#39;), (54, \u0026#39;玩家11000\u0026#39;)] 5\u0026gt;\u0026gt;\u0026gt; 注意，列表中的项 (2, '玩家10001') 并不是标准的 Python tuple。你如果查看它的类型，会发现一个奇怪的名称： \u0026lt;class 'sqlalchemy.util._collections.result'\u0026gt; 。它是一个 AbstractKeyedTuple 对象，拥有一个 keys() 方法，这样可以很容易将其转换成 dict ：\n1\u0026gt;\u0026gt;\u0026gt; results = Account.query.join(Bind, Bind.fromid == Account.gameuid). \\ 2 filter(Bind.toid == 1000). \\ 3 with_entities(Bind.bindid, Account.nickname).all() 4\u0026gt;\u0026gt;\u0026gt; [dict(zip(result.keys(), result)) for result in results] 5[{\u0026#39;bindid\u0026#39;: 2, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10001\u0026#39;}, {\u0026#39;bindid\u0026#39;: 3, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10002\u0026#39;}, {\u0026#39;bindid\u0026#39;: 4, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10003\u0026#39;}, {\u0026#39;bindid\u0026#39;: 5, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10004\u0026#39;}, {\u0026#39;bindid\u0026#39;: 6, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10005\u0026#39;}, {\u0026#39;bindid\u0026#39;: 7, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10006\u0026#39;}, {\u0026#39;bindid\u0026#39;: 8, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10007\u0026#39;}, {\u0026#39;bindid\u0026#39;: 9, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10008\u0026#39;}, {\u0026#39;bindid\u0026#39;: 10, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10009\u0026#39;}, {\u0026#39;bindid\u0026#39;: 53, \u0026#39;nickname\u0026#39;: \u0026#39;玩家10000\u0026#39;}, {\u0026#39;bindid\u0026#39;: 54, \u0026#39;nickname\u0026#39;: \u0026#39;玩家11000\u0026#39;}] 想了解 AbstractKeyedTuple ，可以看看这篇文档 New KeyedTuple implementation dramatically faster 。\n获得多个 Model 的记录 除了筛选字段外，还可以用另一个方法获取多个 Model 的记录。那就是，返回两个 Model 的所有字段：\n1\u0026gt;\u0026gt;\u0026gt; db.session.query(Account, Bind).join(Bind, Account.gameuid==Bind.fromid).filter(Bind.toid==1000).all() 2[(\u0026lt;Account 10001\u0026gt;, \u0026lt;Bind 10001, 1000\u0026gt;), (\u0026lt;Account 10002\u0026gt;, \u0026lt;Bind 10002, 1000\u0026gt;), (\u0026lt;Account 10004\u0026gt;, \u0026lt;Bind 10004, 1000\u0026gt;), (\u0026lt;Account 10005\u0026gt;, \u0026lt;Bind 10005, 1000\u0026gt;), (\u0026lt;Account 10006\u0026gt;, \u0026lt;Bind 10006, 1000\u0026gt;), (\u0026lt;Account 10007\u0026gt;, \u0026lt;Bind 10007, 1000\u0026gt;), (\u0026lt;Account 10008\u0026gt;, \u0026lt;Bind 10008, 1000\u0026gt;), (\u0026lt;Account 10009\u0026gt;, \u0026lt;Bind 10009, 1000\u0026gt;), (\u0026lt;Account 10000\u0026gt;, \u0026lt;Bind 10000, 1000\u0026gt;), (\u0026lt;Account 11000\u0026gt;, \u0026lt;Bind 11000, 1000\u0026gt;)] 使用上面的语法直接返回 Account 和 Bind 对象，可以进行更加灵活的操作。\n多表查询 要联结超过 2 张以上的表，可以直接在 join 得到的结果之后链式调用 join 。也可以在 filter 的结果后面链式调用 join 。join 和 filter 返回的都是 query 对象，因此可以无限链式调用下去。\n写完查询后，应该打印生成的 SQL 语句查看一下有没有性能问题。\n全文完 ","date":"2017-11-04","description":"","lastmod":"2017-11-04T12:06:17Z","slug":"join-in-flash-sqlalchemy","tags":["python","sqlalchemy","flask","mysql"],"title":"在 Flask-SQLAlchemy 中联表查询","url":"https://blog.zengrong.net/post/join-in-flash-sqlalchemy/"},{"categories":["technology"],"content":"在 Windows 10 上安装 PyCtypto PyCrypto 是一个 Python 加密库，核心使用 C 实现，因此在安装的过程中需要编译。\n最简单的按照方法莫过于寻找编译好的 exe 版本进行安装。但由于这个库已经 3 年多没有维护了，目前能找到的编译好的版本基本上都针对较老的 Python 版本，例如 Python 3.3/3.5 等等，这些 exe 版本都无法在我需要的环境中安装成功。\n我的环境：\nWindows 10 x64 Python 3.6.2 要成功安装，首先必须安装 Microsoft 的编译工具。如果已经安装了 Visual Studio ，则可以跳过这一步。若还没有，而且后续也没有使用 VS 的需求，可以下载独立的编译工具 Visual C++ 2015 Build Tools 。\n使用 pip 安装：\n1pip install pycrypto 在安装过程中会出现编译失败。这是由于新的 python 源码 include\\pyport.h 不再包含 #include \u0026lt; stdint.h \u0026gt; ，导致 intmax_t 未定义。\n我们需要在编译环境中设置 CL 参数才能成功编译。\n执行下面命令的时候需要注意两点：\n如果使用的是独立版本的 Visual C++ Build Tool，文件夹可能会不同，请自行修改。 需要在同一个 Shell 会话中执行下面所有命令。这意味着如果你使用了 Python 虚拟环境，需要先进入虚拟环境，再执行 vcvarsall 以及 set CL... 。 1C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\u0026gt;vcvarsall 2set CL=-FI\u0026#34;%VCINSTALLDIR%\\INCLUDE\\stdint.h\u0026#34; 3pip install pycrypto 4C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\u0026gt;pip install pycrypto 5Collecting pycrypto 6 Using cached pycrypto-2.6.1.tar.gz 7Installing collected packages: pycrypto 8 Running setup.py install for pycrypto ... done 9Successfully installed pycrypto-2.6.1 迁移到 PyCryptodome 上面已经提到， PyCrypto 已经三年多没更新了 。因此有人在 PyCrypto 的 Issues 列表中 号召 PyCrypto 的使用者 迁移到 PyCryptodome 。我已经完成了迁移，下面记录一下迁移过程。\n下面这段代码是使用 PyCrypto 库进行 AES 对称加密的封装，其中 key 代表密钥， plain_text 和 cipher_text 分别代表明文和密文。\n1import binascii 2from Crypto.Cipher import AES 3 4def _encrypt(key, plain_text): 5 mod = len(plain_text) % 16 6 if mod \u0026gt; 0: 7 # 补齐16的倍数 8 zero = \u0026#39;\\0\u0026#39; * (16 - mod) 9 plain_text += zero 10 IV = 16 * \u0026#39;\u0026#39; 11 aes = AES.new(key, AES.MODE_ECB, IV) 12 cipher_text = binascii.hexlify(aes.encrypt(plain_text)).decode() 13 return cipher_text 14 15def _decrypt(key, cipher_text): 16 IV = 16 * \u0026#39;\u0026#39; 17 aes = AES.new(key, AES.MODE_ECB, IV) 18 plain_text = aes.decrypt(binascii.unhexlify(cipher_text)).decode().rstrip(\u0026#39;\\0\u0026#39;) 19 return plain_text PyCryptodome 使用相同的包名和模块名，因此需要先删除 PyCrypto，然后安装 PyCryptodome ：\n1pip uninstall pycrypto 2pip install pycryptodome 原来的代码如果一字不改，在调用 _encrypt 进行加密的时候会报错：\n1TypeError: IV is not meaningful for the ECB mode 这是因为 IV 仅应用于 MODE_CBC , MODE_CFB , MODE_OFB 和 MODE_OPENPGP ， AES 文档 中有说明。\nPyCryptodome 的作者在 Compatibility with PyCrypto 中也提到：\nSymmetric ciphers do not have ECB as default mode anymore. ECB is not semantically secure and it exposes correlation across blocks. An expression like AES.new(key) will now fail. If ECB is the desired mode, one has to explicitly use AES.new(key, AES.MODE_ECB).\n关于 ECB 模式不是语义安全的说法，这里有更详细介绍： Why shouldn't I use ECB encryption? 。\n不过我们就不深究安全性的问题了。去掉 IV：\n1def _encrypt(key, plain_text): 2 aes = AES.new(key, AES.MODE_ECB) 3 cipher_text = binascii.hexlify(aes.encrypt(plain_text)).decode() 4 return cipher_text 出现新的错误：\nTypeError: Only byte strings can be passed to C code\n这是因为加密时提供的字符串必须使用 byte 格式而不能使用 str 格式。是所有 str.encode() 进行转换即可。\n1def _encrypt(key, plain_text): 2 aes = AES.new(key.encode(), AES.MODE_ECB) 3 cipher_text = binascii.hexlify(aes.encrypt(plain_text.encode())).decode() 4 return cipher_text 迁移成功的完整代码如下所示：\n1def _encrypt(key, plain_text): 2 mod = len(plain_text) % 16 3 if mod \u0026gt; 0: 4 # 补齐16的倍数 5 zero = \u0026#39;\\0\u0026#39; * (16 - mod) 6 plain_text += zero 7 aes = AES.new(key.encode(), AES.MODE_ECB) 8 cipher_text = binascii.hexlify(aes.encrypt(plain_text.encode())).decode() 9 return cipher_text 10 11 12def _decrypt(key, cipher_text): 13 aes = AES.new(key.encode(), AES.MODE_ECB) 14 plain_text = aes.decrypt(binascii.unhexlify(cipher_text)).decode().rstrip(\u0026#39;\\0\u0026#39;) 15 return plain_text 去掉解密后字符串结尾的控制字符 有时候（尤其是解密使用其它语言加密的密文时），解密后的字符串末尾为了补全长度，带有用于填充的控制字符。这些控制字符一般是 \\0 ，使用 rstrip('\\0') 就可以去掉。\n但当我解密使用 Node.js 加密的密文时，结果是这样的：\n1HelloWorld!你好世界！\\x06\\x06\\x06\\x06\\x06\\x06 因为字符串的长度必须是 16 的整数倍，Node.js 在字符串最后填充了具体的位数。如果是需要补 6 字符，它会填充 6 个 \\0x06 。如果需要补 3 个字符，则填充 3 个 \\0x03 。由于缺少的字符数不能确定，使用 rstrip('\\0') 就不行了。\n可以使用正则来处理：\n1_zero_re = re.compile(r\u0026#39;[\\x00-\\x1F]+$\u0026#39;) 2plain_text = _zero_re.sub(\u0026#39;\u0026#39;, plain_text) 其它的 Python 密码学库 Cryptograph\ncryptography includes both high level recipes and low level interfaces to common cryptographic algorithms such as symmetric ciphers, message digests, and key derivation functions.\nPyNaCl: Python binding to the libsodium library\nPyNaCl is a Python binding to libsodium, which is a fork of the Networking and Cryptography library. These libraries have a stated goal of improving usability, security and speed. It supports Python 2.7 and 3.3+ as well as PyPy 2.6+.\n参考文档 How to install pycrypto on Windows Compatibility with PyCrypto Why shouldn't I use ECB encryption? READ THIS IF YOU WANT HELP Best Python package to use for cryptography? 全文完 ","date":"2017-10-15","description":"","lastmod":"2017-10-15T02:46:38Z","slug":"migrate-pycryto-to-pycryptodome","tags":["python"],"title":"Windows10 下安装 PyCrypto 以及迁移到 PyCryptodome","url":"https://blog.zengrong.net/post/migrate-pycryto-to-pycryptodome/"},{"categories":["technology"],"content":"lftp 是我一直在使用的 ftp 客户端。今天将其更新到 4.8.2 之后，出现了下面的错误：\n1\u0026lt;--- 220---------- Welcome to Pure-FTPd [privsep] [TLS] ---------- 2\u0026lt;--- 220-You are user number 3 of 50 allowed. 3\u0026lt;--- 220-Local time is now 16:59. Server port: 21. 4\u0026lt;--- 220-This is a private system - No anonymous login 5\u0026lt;--- 220-IPv6 connections are also welcome on this server. 6\u0026lt;--- 220 You will be disconnected after 15 minutes of inactivity. 7[FEAT negotiation...] 提示符停止 [FEAT negotiation...] 就不再继续了。\n直接使用 ftp 命令是可以正常登录服务器的，这说明服务器本身没有问题。\nFEAT 是一个 FTP 扩展，定义在 RFC2389 中。它的作用是获取服务器支持的特性列表。\n从日志中可以看到，lftp 在鉴权之前就调用了 FEAT 命令，这应该是服务器拒绝登录的原因。\n解决方案：\n编辑配置文件 ~/.config/lftp/rc ，加入下面的配置：\n1set use-feat no {% label '全文完' %}\n","date":"2017-10-08","description":"","lastmod":"2017-10-08T09:09:11Z","slug":"lftp-feat-negotiation","tags":["server","ftp"],"title":"lftp [FEAT negotiation...]","url":"https://blog.zengrong.net/post/lftp-feat-negotiation/"},{"categories":["technology"],"content":"系列文章 mitmproxy 应用（一）基础代理 mitmproxy 是可编程的，而且非常容易使用。先来看一个简单的例子：\n替换内容 1import re 2from mitmproxy import ctx 3 4def request(flow): 5 remote_host = flow.request.pretty_host 6 method = flow.request.method 7 8 ctx.log.info(\u0026#39;remote_host %s\u0026#39;%remote_host) 9 ctx.log.info(\u0026#39;request.host %s\u0026#39; % flow.request.host) 10 ctx.log.info(\u0026#39;request.path %s\u0026#39; % flow.request.path) 11 ctx.log.info(\u0026#39;request.pretty_url %s\u0026#39; % flow.request.pretty_url) 12 ctx.log.info(\u0026#39;request.query %s\u0026#39; % flow.request.query) 13 ctx.log.info(\u0026#39;request.method %s\u0026#39; % flow.request.method) 14 ctx.log.info(\u0026#39;client host %s\u0026#39; % flow.client_conn.address.host) 15 16 flow.request.replace(\u0026#39;Mozilla\u0026#39;, \u0026#39;Google\u0026#39;, re.M) 这个例子打印出了客户端发来的请求中的一些有用的属性，其中：\nflow.request.pretty_host 请求的域名。如果希望对不同的网站采取不同的策略，可使用该值。 flow.request.host 请求的 IP 地址。 flow.request.path 请求的路径。如果希望对不同的请求路径采用不同的策略，可使用该值。 flow.client_conn.address.host 客户端 IP。如果希望对不同的客户端采取不同的策略，可使用该值。 ctx.log.info 可以将内容打印在 mitmproxy 的 Event log 窗口中（采用快捷键 e 打开) ，下面是访问网站 https://zengrong.net 的 log 内容：\n1remote_host zengrong.net 2request.host 121.40.23.108 3request.path / 4request.pretty_url https://zengrong.net/ 5request.query MultiDictView[] 6request.method GET 7client host 192.168.43.196 上面代码中最后还有一句 replace ，是将请求中的所有 Mozilla 字符串替换成 Google 。这里的“所有”包含下面的内容：\nheader path body 在我的这个请求中，仅有一个 Header User-Agent 包含 Mozilla 字符串：\n动态修改代理 有 5 个客户端，我希望这 5 个客户端在访问同一个网站的时候，使用不同的代理服务器。该如何处理？\n传统的方法是在这 5 个客户端上设置不同的代理服务器。这样存在两个问题：\n有的应用程序可能不走系统代理。 如果有 100 个客户端呢？ 用 mitmproxy 可以解决这些问题。\n对于不走系统代理的应用程序，我们可以采用 mitmproxy 应用（一）基础代理 中提到的透明代理的方法来解决。\n我们可以将多个客户端的代理服务器全部指向 mitmproxy ，然后在 mitmproxy 中通过编程的方法，为不同的客户端指定不同的代理服务器。\n下面是（伪）代码：\n1def request(flow): 2 client_ip = flow.client_conn.address.host 3 if client_ip == \u0026#39;192.168.0.10\u0026#39;: 4 proxy = (\u0026#39;1.2.3.4\u0026#39;, 8080) 5 else: 6 proxy = (\u0026#39;3.4.5.6\u0026#39;, 8080) 7 if flow.live: 8 flow.live.change_upstream_proxy_server(proxy) 需要注意的是，request 在每次请求的时候都会被调用。你可能并不希望每次请求都切换代理服务器，这种情况下，可以通过判断 flow.request.path 或者相关属性来实现条件切换。\n全文完 ","date":"2017-08-18","description":"","lastmod":"2017-08-18T15:10:26Z","slug":"use-mitmproxy-2","tags":["proxy","python","network"],"title":"mitmproxy 应用（二）可编程代理","url":"https://blog.zengrong.net/post/use-mitmproxy-2/"},{"categories":["technology"],"content":"系列文章 mitmproxy 应用（二）可编程代理 mitmproxy 是一个开源的代理工具，我曾经在 手机抓包工具汇总 中提到过它。本系列会把我使用 mitmproxy 时碰到的一些经验列出来。\n透明代理 我们经常会在系统中设置代理，但有些软件并不理会系统代理，坚持使用直接连接。要解决这个问题，我们可以使用透明代理。详细的说明可以看 Transparent Proxying 。\n下面以 Android（客户端）+macOS（开发宿主机）为例说一下透明代理的实现。我的步骤和 mitmproxy 的文档 略有出入，可对比查看。\n1. 启动 mitmproxy ，安装根证书，按标准方式配置好代理之后，在 Android 设备上用浏览器访问 http://mitm.it 即可下载根证书。\n2. 启用 IP forwarding\n1sudo sysctl -w net.inet.ip.forwarding=1 3. 编译一个配置文件 pf.conf（名称随意），内容如下。将其中的 en0 换成 macOS 上用来代理的网络设备的名称。\n1rdr on en0 inet proto tcp to any port 80 -\u0026gt; 127.0.0.1 port 8080 2rdr on en0 inet proto tcp to any port 443 -\u0026gt; 127.0.0.1 port 8080 想知道自己的网络设备的名称，按住 Option(Alt) 键的同时单击网络连接即可：\n4. 加载配置文件\n1sudo pfctl -f pf.conf 如果你碰到这样的提示，不用理会：\nNo ALTQ support in kernel ALTQ related functions disabled\n当然，如果你非常在意这个提示，可以开启 macOS 的防火墙：\n系统偏好设置 -\u0026gt; 安全与隐私 -\u0026gt; 防火墙 -\u0026gt; 打开防火墙 5. 启用配置文件\n1sudo pfctl -e 6. 编辑 sudo 权限\n注意，这里限制了命令行的使用，因此是安全的。\n1sudo visudo 2# 加入以下内容 3ALL ALL=NOPASSWD: /sbin/pfctl -s state 7. 启用透明代理\n1mitmproxy -T --host 8. 设置默认网关\n在 Android 设备上将默认网关设置为 macOS 的 IP 地址。\n一切搞定，打开 Android 设备，就能看到所有的流量都通过代理传输。\nCertificate Pinning mitmproxy 有一套完整的机制处理 HTTPS 加密问题，这套机制能帮助我们解密 HTTPS 流量。How mitmproxy works 介绍了这套机制的原理。\n因此，当我们在 mitmproxy 的 log 信息中看到这样的错误时，一般的原因是没有正常安装并信任根证书。\n10.42.0.45:50438: Client Handshake failed. The client may not trust the proxy's certificate for xxx.com.\n配置客户端代理后访问 http://mitm.it 即可下载根证书，然后手动信任即可。About Certificates 文档详述了方法。\n但如果客户端已经正常进行了证书安装，还是碰到这样的提示，就应该考虑是否碰到了 Certificate Pinning ，也叫做 HPKP 。在这种情况下，客户端并不会信任位于根证书域中 mitmproxy 下发的证书，而是仅仅信任自己的证书签发机构签发的证书。\nCertificate Pinning 是一种 HTTPS 扩展行为，采用 HSTS 机制实现，一些新的浏览器支持这种行为。当然，自行编写的客户端也可以自行支持这种行为。\nChrome/Firefox/Opera 支持 Certificate Pinning ，但是 IE/Edge/Safari 目前不支持。\n一些资料：\nerror while running mmitmproxy What is certificate pinning? Public Key Pinning Extension for HTTP HTTP Strict Transport Security 全文完 ","date":"2017-08-09","description":"","lastmod":"2017-08-09T02:17:28Z","slug":"use-mitmproxy-1","tags":["proxy","python","network"],"title":"mitmproxy 应用（一）基础代理","url":"https://blog.zengrong.net/post/use-mitmproxy-1/"},{"categories":["technology"],"content":"这段代码用来生成 URI 中的查询字符串。\n例如，有一个 object 的内容如下：\n1var obj = {a:\u0026#39;1\u0026#39;,b:\u0026#39;2\u0026#39;}; 2console.log(makeParamsString(obj, true) === \u0026#39;?a=1\u0026amp;b=2\u0026#39;); 3// true 好看的代码应该是怎么样的：\n1var makeParamsString = function(obj, addAsk) { 2 let ret = []; 3 for (let d in obj) { 4 ret.push(encodeURIComponent(d) + \u0026#39;=\u0026#39; + encodeURIComponent(data[d])); 5 } 6 return (addAsk ? \u0026#39;?\u0026#39; : \u0026#39;\u0026#39;) + ret.join(\u0026#39;\u0026amp;\u0026#39;); 7}; 如果希望更好看点，也可以使用 URI.js 来做这件事。\n难看的版本：\n1var makeParamsString = function(obj, addAsk) { 2 var result = \u0026#39;\u0026#39;; 3 4 if (addAsk){ 5 result += \u0026#39;?\u0026#39;; 6 } else { 7 result += \u0026#39;\u0026amp;\u0026#39;; 8 } 9 10 for(var i in obj){ 11 result += i + \u0026#39;=\u0026#39; + obj[i] + \u0026#39;\u0026amp;\u0026#39;; 12 } 13 14 result = result.substr(0, result.length-1); 15 16 return result; 17}; 之所以难看，是因为：\nsubstr 仅仅为了去掉最后一个 \u0026amp; ，这是完全可以避免的； for 循环中的 += 会占用更多的内存； addAsk 实现得不够简洁。 全文完 ","date":"2017-08-08","description":"","lastmod":"2017-08-08T08:52:31Z","slug":"how-to-write-ugly-code-1","tags":["javascript","ugly"],"title":"如何写出难看的代码（一）","url":"https://blog.zengrong.net/post/how-to-write-ugly-code-1/"},{"categories":[],"content":" 搜索 ","date":"2017-08-06","description":"","lastmod":"2017-08-06T03:12:42Z","slug":"search","tags":[],"title":"站内搜索","url":"https://blog.zengrong.net/search/"},{"categories":["web"],"content":" 2017-10-11 更新： renew 错误 2017-10-12 更新： 使用 --webroot 更新证书 2018-03-23 更新： 使用通配符 去年我写了一个太监系列 HTTPS 小白知识（一） ，其中提到使用 Let's Encrypt 来实现 HTTPS 支持。现在是时候了。\n昨天我花了半小时把我的所有网站和博客全部加入了 HTTPS 支持。整个过程非常顺利，下面拣要点说一说。\n根据 Let's Encrypt 的文档，我们需要把情况分成 拥有服务器权限 和 没有服务器权限 两种。 我的所有网站也正好包含这两种情况。Let's Encrypt 建议使用 Certbot 这个工具来自动请求证书。\n在服务器上操作 Certbot 的使用很简单，选择了你的 web 服务器和操作系统版本后，网站会告诉你如何下载最新版本的 Certbot 。由于我使用的是 Ubuntu 12.04 ，就直接下载 Certbot 的运行脚本 certbot-auto：\n1wget https://dl.eff.org/certbot-auto 2chmod a+x certbot-auto {{\n2018-03-23更新 2018年3月13日，Let's Encrypt 开始支持通配符证书的申请 。在申请通配符证书的时候需要注意以下几点。\n1. 升级 certbot 我使用的版本是 v0.22.2\n2. 切换API 如果 Certbot 提示下面的错误，说明需要切换支持 ACME2 的服务器 API 地址：\nThe currently selected ACME CA endpoint does not support issuing wildcard certificates.\n有两种方法：\n在调用 certbot 的时候提供 --server 参数：\n1$ sudo certbot --server https://acme-v02.api.letsencrypt.org/directory 也可以直接把这个配置写到配置文件中:\n1echo \u0026#34;server=https://acme-v02.api.letsencrypt.org/directory\u0026#34; \u0026gt;\u0026gt; ~/.config/letsencrypt/cli.ini 3. 增加 DNS TXT 记录 为了证明你是域名的所有者，你需要为自己的域名增加一条名为 _acme-challenge 的 DNS TXT 记录，具体的值则根据 certbot 的提示填写。\n4. 通配符和裸域名 最后要注意的问题是，通配符证书并不一定支持裸域名。\n例如，我申请的通配符证书 *.zengrong.net 可以支持 blog.zengrong.net/www.zengrong.net ，但并不一定能支持 zengrong.net 。\n证书更新 使用 renew 更新 Let's Encrypt 的证书有效期为三个月，在证书到期时间还剩一个月的时候，你会收到要求更新证书的提醒邮件。\n根据文档 ，更新证书相当简单。但我在更新上文中通过 certonly 申请的证书时，却碰到下面的错误：\n1$ sudo certbot --debug renew --dry-run 2Saving debug log to /var/log/letsencrypt/letsencrypt.log 3 4Processing /etc/letsencrypt/renewal/blog.zengrong.net.conf 5Cert is due for renewal, auto-renewing... 6Could not choose appropriate plugin: The manual plugin is not working; there may be problems with your existing configuration. 7The error was: PluginError(\u0026#39;An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.\u0026#39;,) 8Attempting to renew cert (blog.zengrong.net) from /etc/letsencrypt/renewal/blog.zengrong.net.conf produced an unexpected error: The manual plugin is not working; there may be problems with your existing configuration. 9The error was: PluginError(\u0026#39;An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.\u0026#39;,). Skipping. 出现这个错误的原因是在非交互模式下， Certbot 并不知道如何确认网站是你的。我们可以自己写一个 hook 解决鉴权问题，在这个 hook 中上传鉴权文件到 web 服务器。详见： Pre and Post Validation Hooks 。\n使用 standalone 更新 如果不愿意自己写 hook ，80 或者 443 端口有一个空余，那么可以使用 --standalone 来更新，同时使用 --preferred-challenges 来指定使用的端口。默认使用 80 端口。\n1sudo certbot certonly --debug --force-renew -a standalone -d blog.zengrong.net 使用 webroot 更新 如果 443 和 80 端口都被占用了， 我们当然不应该为了更新安全证书而停止网站服务。此时可以使用 --webroot 插件来更新证书。\n以 zengrong.net 的证书更新为例，使用 -w 参数指定网站根目录地址，使用 -d 参数来指定域名，多个域名也没问题。\n1% certbot certonly --webroot -w /srv/www/zengrong.net -d zengrong.net -d www.zengrong.net 在上面的操作中，certbot 会自动生成鉴权文件，保存在网站根目录下，并发起一个 http 访问以确认域名的所有者是你。 注意这里默认使用的是 http 访问。所以如果你的网站仅监控了 https 请求的话，需要做一下 配置 ，或者使用 --preferred-challenges tls-sni 指定脚本使用 443 端口。\n使用 manual 更新 如果没有服务器控制权，就只能使用 --manual 来更新了。Certbot 会询问你问题并将指导你上传一个文本文件到服务器目录中以证明网站是你的。参见 没有服务器权限 。\n1certbot certonly --debug --force-renew -a manual -d blog.zengrong.net 您与此网站之间建立的连接并非完全安全 启用了 HTTPS 之后，可能会碰到这样的情况：\n这是因为该页面中可能包含非 HTTPS 内容，例如显示了一个位于 HTTP 网站上的图像，或者引用了一个 HTTP 网站上的 JS。对于 HTTPS 网站来说，其中嵌入的所有内容都必须是 HTTPS 的。\n打开 Chrome 的开发者工具，可以在 console 中看到这样的 warning：\nMixed Content: The page at 'https://blog.zengrong.net/spritesheeteditor/' was loaded over HTTPS, but requested an insecure image 'http://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png'. This content should also be served over HTTPS.\n我们需要仔细查看网站中包含的图像、Javascript、CSS 等，确保其处于 HTTPS 域。如果把这些问题都解决了，那么网站的显示应该是这样的：\n检测网站安全性 使用 SSL Labs 提供 的 SSL Server Test 工具来检测自己网站的安全性，进入下面的链接，输入自己的网址即可：\nhttps://www.ssllabs.com/ssltest/analyze.html\n全文完 ","date":"2017-08-02","description":"","lastmod":"2017-08-02T05:25:59Z","slug":"add-https-support-with-letsencrypt","tags":["master","encrypt","ops"],"title":"使用 Let's Encrypt 加入全站 HTTPS 支持","url":"https://blog.zengrong.net/post/add-https-support-with-letsencrypt/"},{"categories":["technology"],"content":"错误解决 在 macOS 中配置 nginx+php-fpm 出错，访问 http://localhost/phpinfo.php ，浏览器中的显示是：\nFile not found.\n在 nginx 的 log 中，完整的错误为：\n12017/08/01 10:39:54 [error] 50087#0: *15 FastCGI sent in stderr: \u0026#34;Primary script unknown\u0026#34; while reading response header from upstream, client: 127.0.0.1, server: localhost, request: \u0026#34;GET /phpinfo.php HTTP/1.1\u0026#34;, upstream: \u0026#34;fastcgi://127.0.0.1:9000\u0026#34;, host: \u0026#34;localhost\u0026#34; nginx 配置内容（位于 server 段）：\n1location / { 2 root /srv/www; 3 index index.html index.htm; 4 autoindex on; 5} 6 7location ~ \\.php$ { 8 include fastcgi_params; 9 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 10 fastcgi_pass 127.0.0.1:9000; 11 fastcgi_index index.php; 12} 首先要判断是 nginx 的问题，还是 php-fpm 的问题。\n在 /etc/php-fpm.conf 中配置：\n1access.log = /var/log/php-fpm.$pool.access.log 重启 php-fpm，查看 /var/log/php-fpm.www.access.log 的内容：\n如果看到类似于 \u0026quot;GET /\u0026quot; 这样的没有包含具体的 php 文件名的 log，则说明可能是 nginx 路径配置问题； 如果看到类似于 \u0026quot;GET /your_php_file.php\u0026quot; 404 这样的 log，则说明可能是 php-fpm 进程无法读取文件。 无法读取文件的原因有很多，可能是文件路径错误，也可能是权限问题。幸运的是，我碰到的是后者：\n1127.0.0.1 - 01/Aug/2017:10:39:48 +0800 \u0026#34;GET /phpinfo.php\u0026#34; 404 首先排除权限问题，配置 /etc/php-fpm.conf ，将 user 和 group 设置为 macOS 中我的用户。然后重启 php-fpm 服务。\n1user = zrong 2group = staff 错误依旧。\n接着怀疑路径问题。我们需要知道 nginx 传给 php-fpm 的具体文件路径是什么，这需要做一些 log 配置。\n把下面的配置写入 nginx 配置文件的 http 段：\n1log_format scripts \u0026#39;$document_root$fastcgi_script_name \u0026gt; $request\u0026#39;; 把下面的配置写入 nginx 配置文件的 server 段：\n1access_log /usr/local/var/log/nginx/scripts.log scripts; 重启 nginx 服务，查看 /usr/local/var/log/nginx/scripts.log ，此时真相大白。由于我没有在 location ~ \\.php$ 段中配置 root，nginx 默认使用了其安装目录下的 html 作为 root，导致传递的文件路径不正确，php-fpm 无法接收到实际的 php 文件。\n我误以为 location / 中配置的 root 会自动向下传递，但我忽略了 location ~ \\.php$ 其实与 location / 是平级，没有继承关系。\n1/usr/local/Cellar/openresty/1.9.7.4/nginx/html/phpinfo.php \u0026gt; GET /phpinfo.php HTTP/1.1 2/usr/local/Cellar/openresty/1.9.7.4/nginx/html/phpinfo.php \u0026gt; GET /phpinfo.php HTTP/1.1 修改 nginx 配置文件，问题解决：\n1location ~ \\.php$ { 2 root /srv/www; 3 include fastcgi_params; 4 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 5 fastcgi_pass 127.0.0.1:9000; 6 fastcgi_index index.php; 7} 此时查看 /usr/local/var/log/nginx/scripts.log ，内容为：\n1/srv/www/phpinfo.php \u0026gt; GET /phpinfo.php HTTP/1.1 2/srv/www/phpinfo.php \u0026gt; GET /phpinfo.php HTTP/1.1 几个小技巧 php-fpm.plist 在 macOS 下应该创建一个 launch plist 文件，将其放在 /Library/LaunchDaemons/zrong.php-fpm.plist ：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; 2\u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; 3\u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; 4\u0026lt;dict\u0026gt; 5 \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; 6 \u0026lt;false/\u0026gt; 7 \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; 8 \u0026lt;string\u0026gt;zrong.php-fpm\u0026lt;/string\u0026gt; 9 \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; 10 \u0026lt;array\u0026gt; 11 \u0026lt;string\u0026gt;/usr/sbin/php-fpm\u0026lt;/string\u0026gt; 12 \u0026lt;string\u0026gt;--fpm-config\u0026lt;/string\u0026gt; 13 \u0026lt;string\u0026gt;/etc/php-fpm.conf\u0026lt;/string\u0026gt; 14 \u0026lt;string\u0026gt;--nodaemonize\u0026lt;/string\u0026gt; 15 \u0026lt;/array\u0026gt; 16 \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; 17 \u0026lt;true/\u0026gt; 18 \u0026lt;key\u0026gt;UserName\u0026lt;/key\u0026gt; 19 \u0026lt;string\u0026gt;root\u0026lt;/string\u0026gt; 20 \u0026lt;key\u0026gt;WorkingDirectory\u0026lt;/key\u0026gt; 21 \u0026lt;string\u0026gt;/var\u0026lt;/string\u0026gt; 22 \u0026lt;key\u0026gt;StandardErrorPath\u0026lt;/key\u0026gt; 23 \u0026lt;string\u0026gt;/var/log/php-fpm.log\u0026lt;/string\u0026gt; 24\u0026lt;/dict\u0026gt; 25\u0026lt;/plist\u0026gt; 启动：\n1sudo launchctl load /Library/LaunchDaemons/zrong.php-fpm.plist 停止：\n1sudo launchctl unload /Library/LaunchDaemons/zrong.php-fpm.plist 在 macOS 上重启 php-fpm 需要注意的是，php-fpm 默认是以守护进程的方式启动的。因此需要在命令行参数中传递 --nodaemonize 禁止其以守护进程启动，否则上面使用 launchctl 来停止服务的操作就是无效的。\n你还可以在 /etc/php-fpm.conf 中通过设置参数来禁用守护进程（命令行参数的优先级最高）：\n1; Send FPM to background. Set to \u0026#39;no\u0026#39; to keep FPM in foreground for debugging. 2; Default Value: yes 3daemonize = no 当然，最简单粗暴的重启办法是这样的，怎么都有效：\n1sudo pkill php-fpm 参考 Debugging the famous - FastCGI sent in stderr: “Primary script unknown” while reading response header from upstream Nginx 1 FastCGI sent in stderr: “Primary script unknown” 全文完 ","date":"2017-08-01","description":"","lastmod":"2017-08-01T03:07:00Z","slug":"primary-script-unknown-on-macos","tags":["web","nginx","php","ops"],"title":"Primary script unknown 错误解决","url":"https://blog.zengrong.net/post/primary-script-unknown-on-macos/"},{"categories":["technology"],"content":"gitlab 是个不错的开源套件，它提供了一套自建私有的类 github 服务的一站式解决方案。\n和我之前介绍过的 gitolite 不同， gitlab 提供了清晰完善的前后台操作界面，管理上更加容易。\n另外，如果有更进一步的 Code Review 需求，应该尝试 gerrit 。\ngitlab 有相当丰富的文档，因此我这里仅记录一些配置中需要注意的点。\n下载和安装 强烈建议使用 gitlab 提供的 Omnibus 安装方式，在一台干净的服务器上独立部署 gitlab。注意服务器配置应该至少 2 核 CPU + 4GB 内存 ，如果采用云服务器，建议也使用云硬盘，这样能够更方便地进行扩容，同时兼顾容灾需求。\ngitlab 的官方源在国内访问速度比较慢。建议使用 Gitlab 中文社区 提供的 源 ，这个源托管在 清华大学 服务器。\n安装并没有什么较难的地方，按照官方文档走完流程，然后启动服务即可。Omnibus 方式安装的路径为 /opt/gitlab 。\n中文化 gitlab 并没有提供语言包，因此要使用补丁的方式进行中文化。gitlab 上的用户 xhang 进行了该工作，感谢他的辛苦努力。\n中文化的流程如下（复制自 xhang 的介绍，有简化）：\n确认当前版本。\n1sudo cat /opt/gitlab/embedded/service/gitlab-rails/VERSION 我的版本是 9.3.6 ，查看汉化版本库是否包含 v9.3.6-zh 这个 tag 。若包含，就进行下面的流程。否则，你可以需要升级自己的 gitlab 到相应的版本。\n1# 克隆汉化版本库 2git clone https://gitlab.com/xhang/gitlab.git 3# 如果已经克隆过，则进行更新 4git fetch 然后比较汉化标签和原标签，导出 patch 用的 diff 文件。\n1# 导出9.3.6 版本的汉化补丁 2git diff v9.0.0 v9.3.6-zh \u0026gt; ../9.3.6-zh.diff 接下来应用补丁：\n1# 停止 gitlab 2sudo gitlab-ctl stop 3sudo patch -d /opt/gitlab/embedded/service/gitlab-rails -p1 \u0026lt; 9.0.0-zh.diff 确定没有 .rej 文件，启动 GitLab 即可。\n1sudo gitlab-ctl start 执行重新配置命令\n1sudo gitlab-ctl reconfigure 邮件配置 如果不想折腾 Postfix 的话，可以直接配置一个 SMTP 服务器。我尝试过使用腾讯企业邮箱的 smtp 服务器，但是每封邮件发出时都会被腾讯邮件系统认为是滥用，要求输入验证码才可以发信。我想应该是腾讯邮箱并不欢迎这样使用他们的服务器。最终我购买了 阿里云的邮件推送服务 ，每天免费 200 封，这也够用了。\n时区 默认时区为 UTC，因此显示的时间和本地时间相隔了8小时。可以参照 Changing your time zone 进行配置。对于中国使用的东八区时间，可以采用下面的值，任选一个均可：\n1Beijing 2Asia/Shanghai 3Choneqing 4Asia/Chongqing 5Hong Kong 6Asia/Hong_Kong 更多的取值可以参考 TimeZone 。\n使用子 URL 有时我们希望使用类似与 http://sample.com/gitlab/ 这样的 URL 来提供 gitlab 服务。gitlab 也提供了一个这样的试验性的功能。我尝试了一下，挺好用：\nConfiguring a relative URL for Gitlab\n修改仓库地址 通过设置多个仓库地址，gitlab 可以将仓库分散到多个存储空间中，在 Repository storage paths 中有详细说明。\n使用非绑定的 nginx gitlab 自带了一个 nginx 服务器提供 web 服务。如果你的 gitlab 服务器还要进行其他工作，那么可能你已经自行在服务器安装了 nginx 。在这种情况下，你可能希望使用自己安装的 nginx 代替 gitlab 自带的 nginx 。这篇文章做了说明：\nUsing a non-bundled web-server\n我也碰到了这样的情况，有一台服务器除了运行 gitlab ，还需要提供简单的 http 静态服务。但我采用了一个更简单的方案： 让 gitlab 自带的 nginx 监听 8000 端口，主 nginx 做一个代理转发：\n1location ^~ /gitlab { 2 proxy_pass http://localhost:8000; 3} 查看错误 gitlab 提供了 Logs 工具帮助查看实时 log，例如使用下面的命令就能查看 gitlab 所有子系统的试试 log。\n1sudo gitlab-ctl tail 如果你的 gitlab 无法工作，不妨先看看 log 再说。\n我在一台已经安装了 nginx 的服务器上部署 gitlab 后，发现无法访问管理界面，查看 log 后发现 80 端口冲突，很快就解决了问题。\nGIT LFS 提交报错 gitlab 支持 git-lfs ，并且默认在新项目中开启。在推送使用了 git-lfs 的项目时，我碰到了这样的错误：\nLFS: Client error: http://xxx/gitlab/mj/project.git/gitlab-lfs/objects/df4e2115c80d07ca4345ba92053dcc38c4002554677a04509d02669a50ab86bf/2858355 from HTTP 413 HTTP 413 错误的意思是负载过大，需要调整默认的 nginx 参数 client_max_body_size 。如果你使用的是我上面提到的采用 proxy_pass 实现两个 nginx 同时工作的方法，那么两个 nginx 的参数都需要调整。\n全文完 ","date":"2017-07-18","description":"","lastmod":"2017-07-28T02:35:14Z","slug":"gitlab-configure","tags":["git","ops"],"title":"gitlab 配置要点","url":"https://blog.zengrong.net/post/gitlab-configure/"},{"categories":["web"],"content":" WordPress to Hexo（上） WordPress to Hexo（下） 一个多月前，我第二次开始了 WordPress to Hexo（上） 的工作。拖拖拉拉的，现在总算弄完了。记在这里，做个汇报。\nTheme 我选择了一个支持 bootstrap 的主题 freemind ，并进行了修改。\nAID 服务 正如 WordPress to Hexo（上） 中提到的，我写了一个独立的 AID 服务来提供 Download 和 PageView 功能。\n例如这个 API 就直接从 WordPress 的数据库中读取下载信息。\n同样的，PageView 的信息也是直接从 WordPress 数据库读取。\n嵌入标签处理 在 WordPress 中我使用最多的嵌入标签就是 [kml_flashembed] 和 [download] 。 我写了一些脚本将这些嵌入标签转换成为 Hexo 支持的 flash 和 download 标签。\n为了让 Hexo 支持 flash 和 download 标签，我在主题中加入了一段 script 来进行处理。本来准备写一个 package 级别的 Hexo 插件来处理这些标签的。但在 Hexo 插件开发过程中不知道如何获得 Hexo 和 Theme 的配置文件内容，没时间看源码，就干脆用 script 来处理了。\nFlash Movie 支持 在 WordPress 中嵌入的 kml_flashembed 代码：\n1[kml_flashembed width=\u0026#34;450\u0026#34; height=\u0026#34;250\u0026#34; movie=\u0026#34;/wp-content/uploads/2006/06/Video2BitmapData_1.swf\u0026#34; /] 会被转换成下面的样子：\n1{{\u0026lt; flash width=\u0026#34;450\u0026#34; height=\u0026#34;250\u0026#34; movie=\u0026#34;/uploads/2006/06/Video2BitmapData_1.swf\u0026#34; \u0026gt;}} 最终 Hexo 会使用 flash.ejs 模版进行渲染。\n在博客中的效果如下： BitmapData类不能正常获取Video中的像素 。\nDownload 服务 在 WordPress 中嵌入的 download 代码：\n1[download id=\u0026#34;142\u0026#34;] 会转换成下面的样子：\n1{{\u0026lt; download 142 \u0026gt;}} 最终 Hexo 会使用 download.ejs 模版进行渲染。\n由于我的服务器性能很差，为了避免每次展示下载页面的时候都去读取数据库影响性能，我在模版中将下载交互方式设置为，先点击下载按钮，做一次 AJAX 调用展示下载信息，在下载信息中提供直接下载链接。\n在博客中的效果如下： MacBook Pro Retina 睡眠耗电现象深究 。\n域名更换 博客的域名目前更改为 blog.zengrong.net ，要考虑以下两点访问问题：\n在搜索引擎中缓存的文章地址更改； 各种引用（包括博客自身的引用）的文章地址更改。 博客自身的引用，可以使用替换完成，而搜索引擎中的地址更改，就要动用 301 重定向了。下面是部分重定向内容：\n1location ~ /post/ { 2 rewrite ^/post/(\\d+).html? https://blog.zengrong.net/post/$1.html permanent; 3 rewrite ^/post/date/(.*) https://blog.zengrong.net/archive/$1/ permanent; 4 rewrite ^/post/tag/(.*) https://blog.zengrong.net/tag/$1/ permanent; 5 rewrite ^/post/category/(.*) https://blog.zengrong.net/category/$1/ permanent; 6 rewrite ^/post/author/.* http://zengrong.net/about/ permanent; 7 return 404; 8} 需要注意的是，URL 最后部分的锚点（#及其后面的内容）是 不会 被发到服务器的，因此锚点的重定向只能放弃了。 性能 Hexo 生成的博客内容全部上传到又拍云提供的 CDN 中，域名在 CDN 中作了绑定，因此现在博客中全部都是静态内容。\n目前的博客文章数量近千篇，编译一次耗时 1 分钟以上，上传使用又拍提供的 upx 同步客户端工具，速度也不快。\n这才是硬伤啊……\n1hexo g 2INFO Start processing 3INFO Files loaded in 13 s 4... 5INFO 2694 files generated in 1.85 min 剩余工作 早期的一些文章使用了 gallery 标签，需要对其做替换。 重写主题。因为实在不喜欢 EJS 模版，我考虑使用 Nunjucks 重写主题并进行美化。 之前 WordPress 的畅言插件可以将畅言的评论同步到 WordPress 中。考虑自己写一个这样的功能。 看到基于 Github 的 Issue 功能实现的 评论系统 和 comment.js ，觉得挺有意思，考虑折腾一下。 优化 Hexo 的编译性能，我这近千篇文章的编译速度的确是很糟糕。 优化上传性能，目前使用的 upx 的同步功能上传，速度较慢，应该改为仅上传更新过的静态文件。 WordPress to Hexo（上） WordPress to Hexo（下） ","date":"2017-07-16","description":"","lastmod":"2017-07-16T02:27:18Z","slug":"wordpress-to-hexo2","tags":["wordpress","master","hexo","staticize","fromto"],"title":"WordPress to Hexo（下）","url":"https://blog.zengrong.net/post/wordpress-to-hexo2/"},{"categories":[],"content":"排名不分先后：\n名称 网址 CoolCode https://coolcode.org FindHao https://www.findhao.net Rect Notes：技术在于分享,专注开发,逆向,安全 http://shadowkong.com ChildhoodAndy https://dabing1022.github.io/ 大数据学习之路 https://dhcp.cn/ ","date":"2017-07-15","description":"","lastmod":"2017-07-15T14:45:40Z","slug":"link","tags":[],"title":"友情连接","url":"https://blog.zengrong.net/link/"},{"categories":["technology"],"content":"2017-08-26 更新： 加入 mitm 相关介绍。\n做移动开发，抓包是基本功。现在的开发者当真是非常幸福，因为抓包工具已经非常成熟了。\n在移动设备上抓包，需要下面几个方面的配合：\n移动设备支持； 代理服务器； 分析工具。 初级工具 先说个抓 HTTP(HTTPS) 包的例子算是入门啦。\n移动设备支持\n操作系统 越狱或root 移动设备支持 Android 否 操作系统自带代理 iOS 否 操作系统自带代理 代理服务器+分析工具\n操作系统 软件 Windows Fiddler macOS Charles Fiddler 是 freeware， 它的 macOS 版本已经在进行 beta 测试了，由于采用了 .NET Framework，它的 macOS 版本可以基于 mono 运行。\nCharles 是商业软件，它也有 Windows 版本，由于使用 JAVA 编写，它的 Windows 版很容易实现。\n既然它们都已经是全平台支持，怎么选就看你了。我目前主要在 macOS 下工作。就介绍 Charles 好了。\nCharles 的配置很简单，只需要 在 Proxy-\u0026gt;Proxy Settings 对话框中启动 HTTP Proxy，然后在移动设备上设置代理为 macOS 的 IP 地址即可。\n设置 Charles\n设置 iOS 的 HTTP 代理\n设置 Android（小米手机） 的 HTTP 代理\n有些安卓系统的 HTTP 代理设置需要长按 WIFI 名称，单击“修改网络”\n接着要做的就是拿起手机访问访问网络。下图是我在 iOS 设备上使用 Safari 访问百度的包信息。是的，HTTPS 也是可以解密的，参见 Charles 的官方文档 SSL Proxying 即可。\nFiddler 的操作也类似，网上大把教程，这里就略过了。\n进阶工具 有些应用比较调皮，不走操作系统的 HTTP 代理。还有些应用（例如游戏）直接走 TCP 协议，无法使用 HTTP 代理抓包。虽然 Charles 支持 SOCKS 代理，但无法分析 TCP 包。\n这就需要祭出大杀器 tcpdump 和 Wireshark 啦。还有一些 App 可以直接运行在 Android 上进行抓包，这也是很方便的啊。\n还可以使用透明代理来处理这些不走系统代理的应用，我在 mitmproxy 应用（一）基础代理 一文中作了介绍。 移动设备支持\n操作系统 越狱或root 移动设备支持 Android 否 PacketCapture Android 否 tPacketCapture Android 是 tcpdump iOS 否 rvictl 代理服务器+分析工具\n操作系统 软件 Windows Wireshark macOS Wireshark Android - 支持抓包的 App（不需要 Root 权限） PacketCapture 和 tPacketCapture 可以直接安装在 Android 设备上，他们会在设备上启动一个 VPN，让所有的网络流量都从 VPN 经过，从而实现抓包。这两个 App 在安装的时候都不需要 Root 权限。\n包内容可以保存成 PCAP 格式，将其传到桌面环境中使用 Wireshark 打开分析。Charles 也可以打开 PCAP 格式，但是仅能分析其中的 HTTP 协议内容。\nPacket Capture 抓包界面\ntPacketCapture 抓包界面\nPacket Capture 目前只能保存单个包，这略显不便。tPacketCapture 则可以将包内容直接保存到文件中。\nAndroid - 使用 tcpdump 抓包（需要 Root 权限） 在 Android 上安装专用于安卓的 tcpdump ，然后启动它对 Android 的网卡抓包。这个方法很简单粗暴，但需要拥有 Root 权限。\n用数据线将 Root 过的 Android 设备连上电脑，并开启 USB 调试：\n1# 检查设备是否连接 2adb devices 3# 登入移动设备 4adb shell 5# 切换到 Root 用户 6su 7# 修改文件夹权限 8chmod 777 /data/local/ 9# 退出移动设备 10exit 11# 上传前面下载的 tcpdump 文件到移动设备 12adb push ~/Downloads/tcpdump /data/local/ 13# 登入移动设备 14adb shell 15# 切换到 Root 用户 16su 17# 赋予 tcpdump 执行权限 18chmod 777 /data/local/tcpdump 19# 改回文件夹权限 20chmod 775 /data/local/ 21# 开始抓包，结果存到 SD 卡的 tcpdump.pcap 文件 22/data/local/tcpdump -p -vv -s 0 -w /sdcard/tcpdump.pcap 此时可以在手机上进行正常操作了。抓包结束的时候使用 Ctrl+C 结束 tcpdump 的执行，将 /sdcard/tcpdump.pcap 复制到桌面环境，使用 Wireshark 打开进行分析就 OK 啦。\niOS - 使用 rvictl 抓包（不需要越狱） RVI 代表的意思是 Remote Virtual Interface 。这是 XCode 自带的一个工具，它可以将连接到 macOS 的 iOS 设备的网卡虚拟成 macOS 的网络设备。\n若碰到提示 rvictl: command not found ，那么可能你需要访问 App Store ，安装 XCode 并运行一次。\n先使用数据线连接 iOS 设备，打开 iTunes 获取 UDID，在保持数据线连接的情况下，执行下面的命令创建虚拟设备 rvi0 （xxxx代表设备的 UDID）：\n1rvictl -s xxxxxxxxxxxxxxx 2Starting device xxxxxxxxxxxxxxx [SUCCEEDED] with interface rvi0 开启 Wireshark 或者 tcpdump (macOS 自带) ，选择设备 rvi0 抓包即可。\n抓包完毕后，要用 rvictl -x 取消使用。\n更简单的办法 桌面系统上的 Wireshark 和 tcpdump 可以对该系统的网络设备抓包。因此我们只需保证移动设备的所有流量经过某个网络设备，就能得到它传输的所有信息。\n把桌面设备变成一个热点，让移动设备通过该热点上网就能做到这点了。无论桌面设备使用有线网络还是无线网络，只需要购买一个无线网卡就能实现热点功能。不用做任何配置，就能抓包了。\n其他工具 mitmproxy = Man In The Middle Proxy，这个名字相当的拉风啊。它是使用 Python + C 实现的开源软件。\nmitmprxoy 是基于命令行界面的，但同时它可以在命令行界面进行交互。 另外，它也提供了一个类似于 tcpdump 的 mitmdump 工具。为了方便查看，它还提供了 mitmweb 这个可视化分析工具。\n我写了两篇文章来介绍 mitmproxy 的应用。\nmitmproxy 应用（一）基础代理 mitmproxy 应用（二）基础代理 另一个稍微小众一点的工具 burp 虽然是商业软件，但免费版提供的功能已经够用了。\n苹果提供了一个相当详细的如何抓包的文档，其中提到了许多抓包软件和方法，值得一看：Getting a Packet Trace 。\n全文完 ","date":"2017-06-27","description":"","lastmod":"2017-06-28T13:43:51Z","slug":"capture-package-on-phone","tags":["ios","android","mobile"],"title":"手机抓包工具汇总","url":"https://blog.zengrong.net/post/capture-package-on-phone/"},{"categories":["technology"],"content":"出于研究的目的，我们可能需要反编译 APK 并得到源码。这件事相当简单，因为先行者们已经做好了所有的工作，我们要做的就是下载，使用他们开发出的工具而已。\n如果只是需要反编译之后的 JAVA 源码，最简单的方案是这样：\n安装 JADX ； 使用它打开 APK 文件，Done 。 简单吧？就两步而已。\n如果想尝试更复杂一点的呢，可以使用下面的方案：\n安装 dex2jar ； 执行 sh d2j-dex2jar.sh -f ~/path/to/apk_to_decompile.apk ，这会生成 apk_to_decompile-dex2jar.jar ； 安装 JD-GUI ，或者 Luyten; 用 JD-GUI 或者 Luyten 打开刚才生成的 jar 文件，Done 。 上面的方法可以得到 JAVA 源码，如果还需要 apk 中的资源文件，或者需要查看 AndroidManifest.xml 的内容，可以使用 Apktool 这个大杀器。\n安装 Apktool ； 执行 apktool d apk_to_decompile.apk ，该命令会将 APK 中的资源和源码反编译，直接输出到当前文件夹下。 注意：\nJADX 已经可以查看资源文件了，所以使用 JADX 并只希望得到资源文件的话，就可以免去了解 Apktool； Apktool 会将代码反编译为 smali 文件，smali 是 dalvik 虚拟机的字节码实现，通过学习 smali 语法，可以比较方便的将自己的逻辑注入到原来的代码中，然后重新打包得到新的 APK。smail 也可以转换成 JAVA 源码。 需要注意的是，一些使用加固技术处理过的 apk，是无法通过上面的方法得到 JAVA 源码的。\n若要寻找更多的 JAVA 反编译器，可以看看这篇： What is a good Java decompiler and deobfuscator? 。\n（全文完）\n","date":"2017-06-14","description":"","lastmod":"2017-06-15T08:59:27Z","slug":"unpack-apk","tags":["android"],"title":"解包 APK","url":"https://blog.zengrong.net/post/unpack-apk/"},{"categories":["others"],"content":"（本文所有图片来自互联网，侵删）\n这几天的通勤完全使用共享单车+地铁，今天骑到一辆使用智能锁的 ofo，长这样儿：\n这个据说 与北斗导航合作 的智能锁用起来很奇怪：\n扫码后依然出现4个数字，需要手动解锁； 关锁后不会立即停止计费，而是需要等待1分钟才能结束行程。虽然可以强行结束，但提醒却在恐吓影响信用云云。 既然是智能锁，为什么还要设计按键？为什么关锁后需要等待1分钟？\n我想应该和锁的功能定位和用户习惯有关。和摩拜一开始就考虑极致的用户体验（大规模瘫痪这不算好么）不同。ofo 已经培养了 “手动解锁” 这个用户习惯，它的智能锁也需要保持这个习惯，然后进一步简化习惯。新的智能锁因为可以动态更新密码，仅保留了4个数字，这就是简化。或许 ofo 的下一代会取消按键也未可知。\n至于为何上锁后要1分钟才能结束行程，或许是考虑到用户开锁时间损耗。因为 ofo 本来就是开锁后 1 分钟才开始计费的。\n当然，上面我分析的都是狗屁。\n顺手搜索了一下，已经有网友拆解了智能锁，该锁采用的是一次性电池和联通的 SIM 卡，定位使用的是 Quectel M26 这颗 GSM/GPRS(2G) 芯片，至于北斗导航？并没有。\n当然，ofo 也并没有说在第一代智能锁上会使用北斗导航芯片。\nofo 的智能锁如此设计的原因可能只有一个：成本。或许 ofo 在研发上投入的资金并不够，或许之前的野蛮发展让其不重视研发。无论如何，这个智能锁的智能含量真的很低啊。在武汉这种热死非洲人和冻死爱斯基摩人的残酷环境中，本来就环境恶劣的小黄车能撑多久？电池一挂，秒变僵尸车！希望这颗武汉企业生产的电池考虑了地域特性。\n至于按键的设计，或许是由于开锁这个机械动作比较耗电。ofo 智能锁的电池可能仅仅够用来做通信。（摩拜是自发电的土豪，不能比啊）\n再说 1 分钟结束行程，这应该是考虑到了 2G 芯片的定位和连接速率（还有服务器排队？）问题进行的折衷。\n理想和现实差距还是挺大的。\n另外，ofo 智能锁的负面消息大约都是5月4日同一天发出的…… 呵呵。\n多思考总是没错的，至少离本质更近了不是？狗屁自有道理。 ^_^\n（放完了）\n","date":"2017-06-08","description":"","lastmod":"2017-06-08T14:52:58Z","slug":"ofo-smart-lock","tags":["live","life"],"title":"ofo 智能锁的小思考","url":"https://blog.zengrong.net/post/ofo-smart-lock/"},{"categories":["impressions"],"content":"这是一篇鸡汤文，不喜欢看鸡汤的可以退出了。\n2016年11月开始，我就一直在以996的方式工作，工作结束的时间经常会超过12点。2017年2月的某一天，我站在体重秤上，发现体重没变化，但拎起腹部和腰部的赘肉，能感受到皮下脂肪的分量。我的肌肉正在萎缩，并逐渐被脂肪替代，该健身了。\n健身没办法速成，它是值得长期坚持的运动。若形成了健身习惯，对身体健康的好处不言而喻。在进行系统健身之前，我并没有停止过锻炼。我的 1500M 游泳的成绩是 40 分钟，并一直在使用 《囚徒健身》 的方式训练，在加班前的频率为每周2-3次，这让我有了一些身体力量的基础。年轻时候的舞蹈底子仍在，身体柔韧性基础也不错。感谢这些基础，让我能快速进入训练状态。\n万事皆学问，健身也一样。作为一个纠结细节的人，在大量的细节如潮水般涌来的时候，我不得不收起“随便练练就好”的心态，在每一次动作中找到最完美的那个点。我发现，之前自己锻炼时碰到许多问题在了解了更多健身知识之后都迎刃而解，之前的锻炼瓶颈很容易就突破了，知识就是力量啊。\n从2月14日办理健身卡到现在，我已经有系统地健身了三个多月的时间。对于健身这件事来说，三个月时间并不长，可以说刚刚迈入健身的门槛，另一只脚还在门外。尽管如此，我的身体还是有了一些明显的变化，催促我在健身这条路上继续走下去。\n这些变化包括：\n4块腹肌显现，第1、2块更明显。腹直肌的轮廓逐渐清晰，腰部在腹直肌周围出现明显的凹陷。第5、6块腹肌虽然依然包裹在脂肪下，但触摸能感觉明显的饱满和弹性。 虽然胸围变化不大，但胸大肌增厚明显，胸大肌外侧轮廓明显。三角肌、斜方肌和背阔肌也有了进步（也是因为之前太弱了），肱二头和三头肌更清晰。 锻炼前热身的 2 公里跑耗时 8'14\u0026quot; 。 从每周2~3次训练，到每周6次训练。 消化能力变化，会经常感觉到饿。 体重减了1公斤。这说明脂肪量在减少，肌肉量在增加。 大脑对身体的控制更清晰了。做一个动作，能感觉到是哪块肌肉在发力，也基本能通过控制其他肌肉来保证局部发力。在日常生活着也能通过大脑对肌肉的控制来限制不当使用。例如搬起重物的时候会更多利用腿部和臀部力量。 由于工作的特点，驼背是比较常见的 IT 工作者体态，我也有驼背和颈前倾的情况。健身时的肌肉控制锻炼，还有背部肌肉的增强让这些不良体态得到了纠正，“昂首挺胸”更自然了。 精神控制能力增强，看到一个目标（例如一个重量，或者一个运动次数），能感受到自己是不是能完成它。许多目标初看觉得无法完成，一点点增加重量的时候，目标逐渐靠近，半程过后就能感到该目标定能达成。完成之后的成就感让人快乐。成功就是越来越近。 从结果看，三个月前我为自己设定的小目标（腹肌、胸肌显现）已经达成。下面讲讲我这三个月的健身经验。\n入门之初，在条件允许的情况下，我建议请一个靠谱的私教帮助你。当然了，如果你不那么忙，现在的健身资料如此丰富，你自己慢慢琢磨训练也能出成果（但必须要多看多学习多思考！），但可能会走一些弯路（就像我之前那样）。一个优秀的私教能帮你大幅节约时间，让你迅速走上健身的不归路。在我看来，一个优秀的私教应该有下面几点品质：\n一定的身体条件。教练的身材可能没有你想象中那么好（施瓦辛格的身材绝大多数人都达不到），但是一个身材很差，看起来没有经过系统健身的人绝不可能是个好教练。 丰富的运动知识，包括防护知识，应急处理，运动营养学知识。这些可以从日常的沟通过程中了解。 关注你的感受。好教练会不断通过观察和询问了解你训练中的感受，不断纠正你的动作细节，根据实际情况随时调整训练目标。一个把你晾在那里让你不断重复动作但不给足够指导的教练可不称职。 如何在教练的知道下练得更好？我觉得要注意下面几点：\n完全信任你的教练。教练既然是你选的，你就应该相信他； 尽全力完成教练给你的任务。教练会按照你的承受能力安排训练任务，你要 不打折扣 地完成它。别偷懒！想想你的目标！ 多交流。训练中不理解的地方，还有训练后身体的感受都要及时和教练交流，这样能得到最大程度的指导。放心，好教练都会不遗余力的帮助你。话说回来，毕竟费用还挺贵的，不用好怎么对的起自己？ 如果不准备请私教，我的建议是： 一！定！要！去！健！身！房！ 有人可能觉得“我在家用 KEEP 一样可以练出人鱼线”，这个我当然相信，但是去健身房你能 更！快！练！出！人！鱼！线！\n表被上面的咆哮体吓到，让我来说说去健身房的几点好处：\n可能和你想象中的不一样，健身房的常客大部分是“身材很好”的，而非“身材很糟”的。在那里你可以得到更多的心理暗示：“只要我经常来，我的身材会和他们一样”。你也可以从他们身上学到很多健身知识，“JIAN友们”大多是很乐意分享的。 家里的干扰太多。熟悉的环境会让你难以专心。去健身房的路程/换衣/洗澡虽然会消耗一些时间，但这种必不可少的“仪式感”会增强你持续锻炼下去的信心，让你更容易形成健身习惯。健身房的气氛也容易让你的健身激情被快速点燃。 健身房的器械是为健身人群量身定做的。比如你要冲击大重量，或者要快速切换哑铃重量，能比较快速地完成。在家里，你总不能把4磅到15公斤的哑铃全部配齐吧？ 可以约上一两个朋（JI）友一起去健身房。你们一起健身，一起成长，在做大重量冲刺的时候可以互相保护，在耐力训练的时候可以互相竞争。在犯懒不想去健身的中午，想着健身房有个人等着你都是一件很有期盼的事啊！如果是叫你的好JIAN友来家里？两人在狭小的空间里汗流浃背娇喘连连？是不是有点奇怪呢！ 在健身房养成了自己的健身习惯，有了足够的自律性，你就可以自己在家锻炼啦！在不能去健身房的周末，我一般是在家练40分钟的K3/K4级别 KEEP 来进行巩固的。\n许多人会关注训练过程中的饮食。这三个月，我的饮食习惯如下：\n饮料：茶、黑咖啡、牛奶、白开水。除此之外不喝其他饮料； 食物：由于在公司旁边租房住，没办法挑肥拣瘦。碳水居多，水果适量，蔬菜较少。没有鸡胸肉、偶尔买速冻牛排自己煎；每日25克坚果；一日三餐，偶尔四餐，睡前太饿就会吃点饼干； 补剂：训练后2勺蛋白粉+300ML 牛奶。 我并没有怎么修改自己的饮食习惯，有些习惯甚至不合理（例如睡前吃饼干这种高热量的东西）。我的观点是，当训练到了一定阶段，身体会自己知道需要什么。例如我现在看到比较油的食物会自动反感。由于离城区比较远，热干面和牛肉面都吃得少了（这是好事）…… 光谷金融港附近实在是没有可以吃的东西啊。\n下一个小目标：\n10公里跑，配速达到 4'40\u0026quot; 。 能在1小时内完成1500米自由泳。 6块腹肌显现。 制定适合自己的训练计划，坚持每周6次训练。 有人说能坚持的人做啥事儿都会成功。这句话有点极端，但我知道，能坚持健身的人，真的能坚持完成任何事！坚持是成功的必要因素！成功就是越来越近！\n一起来健身吧！\n（全文完）\n","date":"2017-05-28","description":"","lastmod":"2017-05-28T01:41:39Z","slug":"home","tags":["live","life"],"title":"系统化健身三个月感受","url":"https://blog.zengrong.net/post/home/"},{"categories":["web"],"content":" WordPress to Hexo（上） WordPress to Hexo（下） 大约两年多前，我写过一篇 博客静态化工作 ，当时是准备使用 Pelican 来做静态化。但由于静态化有一些工作量，就写了 WPCMD 来作为过渡工具。\n现在终于有时间来做静态化了，但我选择的工具变成了 Hexo 。\n在上次的静态化准备工作中，我的所有博客文章已经完全使用 Markdown 来撰写了，所以这次的静态化工作就会简单一些。下面是主要的工作：\nTAG 相关 这些嵌入到 Markdown 中的 TAG，需要写一个 Hexo 的 Tag 插件来实现。\nFront Matter。我需要把之前 Markdown 文件开头的格式转换成 Hexo 的 Front Matter 。 Flash 动画。我对 Flash 技术 很熟悉。要在网页中显示 swf 文件，我使用了 kml_flashembed 插件。现在要将这个插件提供的文本进行替换。这影响 36 篇文章。 Graphviz 支持。我在 Fenced Code Extra for Python-Markdown 中提供了 Graphviz 的支持，在 Hexo 中也要提供。 下载管理器。我使用 wp-downloadmanager 插件来管理博客中文件的下载，有 138 个文件和 1.8GB 内容。目前有 41 万次点击和 42TB 的下载。我需要创建一个下载服务以持续记录这些数据。由于该插件使用 id 保存关联，我也需要在服务中提供记录下载次数，id 和文件名、路径的对应关系的功能。这影响 83 篇文章。 页面访问计数 虽然已经使用了 Google 和 百度统计，但我还是希望使用这种古老的技术，以便接续之前 WordPress 中提供的访问计数。我需要创建一个计数器服务。\n留言 我使用 畅言 提供的留言服务。但是我希望创建一个服务将畅言的服务同步到之前 WordPress 的留言数据库中。\n其他 HTTPS 支持，使用 Let’s Encrypt 提供的服务来提供支持。 CDN 支持。所有博客内容将上传 CDN，又拍云 很早就为我提供了免费无限量的 CDN 服务，终于可以用上了。 域名切换为 blog.zengrong.net ，根域名将被用于个人网站，所有链接使用 301 转发。 开始 第一步已经开始了，我写了一个 转换工具 将博客中的 Markdown 源文件进行处理，替换 Front Matter 和 Flash+Download 插件的标签。\nWordPress to Hexo（上） WordPress to Hexo（下） ","date":"2017-05-24","description":"","lastmod":"2017-05-24T15:08:36Z","slug":"wordpress-to-hexo1","tags":["wordpress","master","hexo","staticize","fromto"],"title":"WordPress to Hexo（上）","url":"https://blog.zengrong.net/post/wordpress-to-hexo1/"},{"categories":["technology"],"content":"Flask 在 Debug 模式下启动的时候，会被初始化两次。看下面的代码：\n1from app import app 2import time 3 4if __name__ == \u0026#39;__main__\u0026#39;: 5 print(time.time()) 6 app.run(port=5000, debug=True) 输出：\n11492742262.002537 2 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 3 * Restarting with stat 41492742262.598912 5 * Debugger is active! 6 * Debugger pin code: xxx-xxx-xxx 这将导致我们的某些需要在初始化时执行的方法被执行2次，这显然不是我们需要的结果。\n出现这样的问题的原因是在开启 Debug 模式的时候，Werkzeug 默认会 启动一个额外的进程 来监控文件变化以方便重启进程。\n要解决这个启动两次的问题，有这样几种方法：\n1. 取消自动重启 在 Debug 模式下，为了方便调试，Flask 提供了当文件变化的时候自动重启实例的功能。关闭这个功能就可以避免初始化2次的情况。\n1app.run(port=5000, debug=True, use_reloader=False) 2. 判断 Werkzeug 主进程是否执行 在 restart_with_reloader function 中，我们可以看到在新进程启动前，环境变量 WEAKZEUG_RUN_MAIN 被置为 'true' ：\n1new_environ[\u0026#39;WERKZEUG_RUN_MAIN\u0026#39;] = \u0026#39;true\u0026#39; 通过判断这个变量的值，我们就能保证在启动时仅执行一次：\n1if __name__ == \u0026#39;__main__\u0026#39;: 2 import os 3 if os.environ.get(\u0026#39;WERKZEUG_RUN_MAIN\u0026#39;) == \u0026#39;true\u0026#39;: 4 print(time.time()) 5 app.run(port=5000, debug=config.DEBUG) 3. 在第一次请求的时候执行 使用 before_first_request 这个钩子，把执行放在 Flask 第一次收到请求的时候。这就避免了2次初始化的干扰。\n1@app.before_first_request 2def initialize(): 3 print(time.time()) 4. 在需要请求的时候执行 和“在第一次请求的时候执行”类似，使用一个开关变量，控制执行仅一次。把执行延迟到了应用逻辑层面。\n（全文完）\n参考:\nWhy does running the Flask dev server run itself twice? How to stop Flask from initialising twice in Debug Mode? ","date":"2017-04-21","description":"","lastmod":"2017-04-21T02:33:53Z","slug":"flask-init-twice-in-debug-mode","tags":["flask","server","python"],"title":"Flask 在 Debug 模式下初始化2次","url":"https://blog.zengrong.net/post/flask-init-twice-in-debug-mode/"},{"categories":["technology"],"content":" 2020-01-27 更新 增加相关阅读和 Flask+uWSGI Logging rotate：重要补充。 uWSGI 可以使用 --logto / --logto2 / --daemonize 这几个参数来指定把 log 写入普通文件。但普通文件管理起来比较麻烦，我们可以利用 Ubuntu/CentOS 中自带的 Rsyslog 来实现日志管理。本文以 Ubuntu 16.04 为例。\nuWSGI 的外部 logging 支持 让我们先来看看 uWSGI 内置了哪些 logging 系统支持：\n1% uwsgi --logger-list 2 3*** uWSGI loaded loggers *** 4python 5syslog 6rsyslog 7socket 8redislog 9mongodblog 10file 11fd 12stdio 看来 syslog 已经在内置支持中了。不过要注意的是，上面列表中的 rsyslog 指的是 Remote Syslog ，如果你的 syslog 服务器在远程，则使用这个值，否则应该使用 syslog 。\n使用外部 logging 系统 uWSGI 有两个参数来设定使用 外部的 logging 系统。req-logger 包含 uWSGI 的请求 log，而 logger 包含标准错误。\n下面的设置将 req-logger 和 logger 分别命名，并使用了 local5 这个 facility 。local5 是可选的，也可以仅包含名称。\n1[uwsgi] 2req-logger = syslog:game-proxy-req,local5 3logger = syslog:game-proxy,local5 设置 syslog 创建文件夹 /var/log/uwsgi 用来保存 uwsgi 的 log，并设置正确的权限：\n1sudo chown syslog:adm /var/log/uwsgi 加入一个文件： /etc/rsyslog.d/80-game.conf ，内容如下：\n1# local5.* /var/log/uwsgi/game-all.log 2:programname,isequal,\u0026#34;game-proxy\u0026#34; /var/log/uwsgi/game-proxy.log 3:programname,isequal,\u0026#34;game-proxy-req\u0026#34; /var/log/uwsgi/game-proxy-req.log programname 就代表每条 log 的静态名称，在这里 Message Properties 。isequal 代表完全相等判断，在 Compare-Operations 这里可以找到所有可用的比较类型。\n上面的配置根据 uWSGI 中配置的名称，将 log 分离到不同的文件。第一行 local5.* 则将所有使用 local5 的 log 全部置入 game-all.log，该配置并没有启用。\n设置完成后重启 rsyslog 服务：\n1sudo service rsyslog restart 设置 logrotate logrotate Ubuntu 自带的压缩、分隔 log 文件的工具，它被 crontab 调用。有了 logrotate ，我们就不必自己设计压缩和分割方案。\n主 logrotate 配置文件在 /etc/logrotate.conf ，文件夹 /etc/logrotate.d/ 中的所有配置文件也会被载入。我们只需要在该文件夹中增加一个文件就可以实现对 /var/log/uwsgi 中 log 文件的压缩。下面是 /etc/logrotate.d/game 文件的内容：\n1/var/log/uwsgi/game-*.log { 2 create 0664 app app 3 daily 4 rotate 60 5 compress 6 delaycompress 7 missingok 8 notifempty 9 dateext 10 dateyesterday 11} create 分割后创建新文件，可以指定文件权限，owner 和 group 。 daily 每日分割。 rotate 60 保留60天的日志。 compress 压缩日志。 delaycompress 延迟压缩，在下一个日志分割的时候压缩上一个日志。这样同时有两个日志没有压缩：昨天的和今天的。这样可以方便查询昨天的内容。 missingok 没有日志文件的时候不报错。 notifempty 空日志不处理。 dateext 使用日期作为分割日志后缀名称。若不设置则使用序号。 dateyesterday 当设置了 dateext 的时候有效。在分割日志后面加上昨天的日期，这样文件后面的日期和文件中记录的时间戳时间是一致的。 更多详细的设置，可以看 logrotate 的 man page 。\nlogrotate 是被 crontab 自动调用的，详细的配置可以看文件： /etc/cron.daily/logrotate 。\n如果等不及 crontab 执行，可以自行使用下面的命令测试：\n1logrotate -df /etc/logrotate.d/game 在使用 -df 的时候，仅仅是展示 rotate 效果，但并不会真正执行。如果要真正执行，可以使用 -vf 参数。 使用 python 的 logging 模块 Python 自带 logging 模块。我们也可以禁用 uWSGI 中的设置，改为使用 Python 语言的 logging 模块向 rsyslog 记录日志。之前我写过一篇这样的文章：rsyslog/Python/LogAnalyzer 记录和查看日志 。\n查看日志可以使用 LogAnalyzer 这个 PHP 写成的前端，用法在 rsyslog/Python/LogAnalyzer 记录和查看日志 一文中也有提到。\n单独使用 logrotate 当然，我们也可以不使用 Rsyslog ，直接对 --logto / --logto2 / --daemonize 这几个参数生成的 log 文件执行 logrotate。\n但要注意一个问题，当 logrotate 生效后，log 依然写入旧的文件！\n举例说明：\n原始的 log 文件名称为 uwsgi.log ； 执行 logrotate -vf /etc/logrotate.d/game ，rotate 成功，文件名被修改为 uwsgi.log-20171012 ，并创建了新文件 uwsgi.log ； 此时发现新的 log 依然被写入 uwsgi.log-20171012 而非 uwsgi.log ！ 这是因为在 linux 系统下，一个进程打开文件时使用的是文件系统的 inode 编号而非文件名。移动或者重命名一个文件，并不会修改它的 inode 编号。因此需要在进行 rotate 之后，通知 uwsgi 重新打开 log 文件。\n或者，可以将 create 创建方式修改为 copytruncate 创建方式，后者的特点是复制一份现有的 log 为新文件，然后清空旧文件。这样就不需要通知 uwsgi 重新打开 log 文件了。\ncopytruncate 的缺点就是，复制 log 文件和清空 log 文件之间有一段时间（若 log 文件较大就更明显），这段时间中的 log 文件可能丢失。\ncopytruncate 还有一个关于权限的问题。 create 可提供文件权限，owner/group 信息，而 copytruncate 则是直接复制原始文件的权限。在使用 copytruncate 的时候，可以使用 su 来设置文件权限。上面的配置文件可以改成这样：\n1/var/log/uwsgi/game-*.log { 2 su app app 3 copytruncate 4 daily 5 rotate 60 6 compress 7 delaycompress 8 missingok 9 notifempty 10 dateext 11 dateyesterday 12} 如果仍然希望使用 create 参数，那么可以参考 Flask+uWSGI 的 Logging 支持 中的“uWSGI 的 Logging 配置“一节。\n参考 Linux logrotate 命令教程日志分割 logrotate 使用方法 相关阅读 部署Flask + uWSGI + Nginx Flask+uWSGI 的 Logging 支持 Flask+uWSGI Logging rotate：重要补充 pyzog：uWSGI logging rotate 的终极方案 全文完 ","date":"2017-04-13","description":"","lastmod":"2020-01-27T01:36:05Z","slug":"uwsgi-rsyslog-rotating-logging","tags":["python","server","uwsgi","logging"],"title":"uWSGI+rsyslog 实现 rotating logging","url":"https://blog.zengrong.net/post/uwsgi-rsyslog-rotating-logging/"},{"categories":["technology"],"content":"有时我们在一台机器上部署多个 uWSGI 服务，并提供 HTTPS 支持。使用 proxy_pass 代替 uwsgi_pass 是很简单的方案：\n1server { 2 listen 443; 3 server_name your.domain.com; 4 5 root /srv/www/static; 6 index index.html index.htm; 7 ssl on; 8 ssl_certificate /your/ssl/certificate.pem; 9 ssl_certificate_key /your/ssl/certificate.key; 10 11 ssl_session_timeout 5m; 12 13 ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; 14 ssl_ciphers \u0026#34;HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES\u0026#34;; 15 ssl_prefer_server_ciphers on; 16 17 location / { 18 try_files $uri $uri/ =404; 19 } 20 21 # 把 test1 代理到 5000 端口 22 location ^~/test1/ { 23 proxy_pass http://localhost:5000; 24 } 25 26 # 仅允许 1.2.3.4 访问 27 location ^~/test1/private/ { 28 allow 1.2.3.4; 29 deny all; 30 proxy_pass http://localhost:5000; 31 } 32 33 # 把 test2 代理到 5001 端口 34 location ^~/test2/ { 35 proxy_pass http://localhost:5001; 36 } 37} 如果在服务器前端启用了负载均衡，需要做一些调整。因为负载均衡服务器已经帮我们把 https 流量进行解密了，到了 web 服务器就不必再配置证书。因此 nginx 改为 listen 80 端口。\n这么做的问题是，当 uwsgi app(这里是 flask) 的内部路由进行跳转的时候 (flask url_for)，由于侦听的是 80 端口，app 会自动把原有的 https url 路由到 http url。导致跳转失败。\n例如：\n当我们在 https://your.domain.com/test1/ 中跳转 url_for('index') 的时候，URL 会变成 http://your.domain.com/index/ ，这会导致访问失败。\n1server { 2 listen 80; 3 server_name your.domain.com; 4 5 root /srv/www/static; 6 index index.html index.htm; 7 8 location / { 9 try_files $uri $uri/ =404; 10 } 11 12 location ^~/test1/ { 13 proxy_pass http://localhost:5000; 14 } 15} 解决方案，就是把 proxy_pass 改回 uwsgi_pass ，并修改 uwsgi_params，在最后加入：\n1uwsgi_param UWSGI_SCHEME https; 修改后的 nginx 配置：\n1server { 2 listen 80; 3 server_name your.domain.com; 4 5 root /srv/www/static; 6 index index.html index.htm; 7 8 location / { 9 try_files $uri $uri/ =404; 10 } 11 12 location ^~/test1/ { 13 uwsgi_pass 127.0.0.1:5000; 14 include uwsgi_params; 15 } 16} 更多内容请阅读： 部署Flask + uWSGI + Nginx\n（全文完）\n","date":"2017-04-12","description":"","lastmod":"2017-04-12T03:59:34Z","slug":"uwsgi-nginx-https","tags":["nginx","server","flask","uwsgi"],"title":"uWSGI + Nginx 的 HTTPS 支持","url":"https://blog.zengrong.net/post/uwsgi-nginx-https/"},{"categories":["technology"],"content":"在把 Flask 写的应用通过 Supervisor+uWSGI 部署到正式服务器上时，出现了这样的错误：\nUnable to print the message and arguments - possible formatting error.\n或者\nUnicodeEncodeError: 'ascii' codec can't encode characters in position 24-25: ordinal not in range(128)\n有趣的是，直接在 Python 环境下运行的时候，没有这样的错误。使用 uwsgi uwsgi.ini 这种方式来运行也正常。\n由于对 unicode 的支持不够完善，这种报错经常会出现在 Python2 中，但我的所有程序都在 Python3 中写成，不应该再出现这样的错误。况且，所有的 python 文件都在首行设定了编码：\n1# -*- coding: utf-8 -*- 我的环境如下：\nUbuntu 16.04.1 LTS Python 3.5.2 uWSGI 2.0.14 (in python3 pip) Supervisor 3.3.1 (in python2 pip) uwsgi.ini 配置文件内容如下：\n1[uwsgi] 2master = true 3 4wsgi-file = manage.py 5callable = app 6 7processes = 2 8threads = 2 9max-requests = 6000 10chmod-socket = 664 11 12uid = app 13gid = app 14 15buffer-size = 32768 16 17venv = {project_dir}/venv 18 19; http = 127.0.0.1:5001 20 21logto = {project_dir}/logs/uwsgi.log 由于直接使用 Python 和 uwsgi 都不会出现这样的错误，因此可以判断应该是环境编码设置导致的问题。查看服务器的编码如下：\n1% locale 2LANG=C 3LANGUAGE=C: 4LC_CTYPE=\u0026#34;en_US.UTF-8\u0026#34; 5LC_NUMERIC=\u0026#34;en_US.UTF-8\u0026#34; 6LC_TIME=\u0026#34;en_US.UTF-8\u0026#34; 7LC_COLLATE=\u0026#34;en_US.UTF-8\u0026#34; 8LC_MONETARY=\u0026#34;en_US.UTF-8\u0026#34; 9LC_MESSAGES=\u0026#34;en_US.UTF-8\u0026#34; 10LC_PAPER=\u0026#34;en_US.UTF-8\u0026#34; 11LC_NAME=\u0026#34;en_US.UTF-8\u0026#34; 12LC_ADDRESS=\u0026#34;en_US.UTF-8\u0026#34; 13LC_TELEPHONE=\u0026#34;en_US.UTF-8\u0026#34; 14LC_MEASUREMENT=\u0026#34;en_US.UTF-8\u0026#34; 15LC_IDENTIFICATION=\u0026#34;en_US.UTF-8\u0026#34; 16LC_ALL=en_US.UTF-8 从上面的配置可知 LANG 和 LANGUAGE 环境变量并没有设置。\n可以在 uwsgi.ini 中设定这两个环境变量的值。\n1env LANG=\u0026#34;en_US.UTF-8\u0026#34; 2env LANGUAGE=\u0026#34;en_US.UTF-8\u0026#34; 另一个方法，直接修改系统的 local 设置。在 Ubuntu 上，编辑该配置文件：\n1/etc/default/locale 2LANG=\u0026#34;en_US.UTF-8\u0026#34; 3LANGUAGE=\u0026#34;en_US:en:\u0026#34; 还可以直接使用 localectl 来更改配置：\n1localectl set-locale LANG=en_US.UTF-8 参考 uWSGI / Emperor: UnicodeEncodeError: 'ascii' codec can't encode character （全文完）\n","date":"2017-03-23","description":"","lastmod":"2017-03-23T02:03:29Z","slug":"uwsgi-unicodeencode-error","tags":["python","server"],"title":"uWSGI 的编码问题解决","url":"https://blog.zengrong.net/post/uwsgi-unicodeencode-error/"},{"categories":["technology"],"content":"SQLAlchemy 是一个功能强大的 ORM 。本篇介绍使用 SQLAlchemy 处理 MySQL 上的 TIMESTAMP 类型的一点小技巧，很冷门的知识点哦。\n列的默认值 注意，在使用 db.create_all() 进行初始化创建表的时候，如果为 Column 指定了 default 的值，并不会影响创建的表中的对应列的默认值。这些 default 的值仅仅是在使用 SQLAlchemy 系统插入值的时候会提供默认值。如果你希望影响 MySQL 中 Column 的默认值，必须使用 server_default 来指定。\n例如要设置一个 Colmun 默认值为0 ，则需要设定 server_default=text('0') 。\nMySQL 的默认行为 使用下面的代码创建一个默认值不为空的 TIMESTAMP Column ：\n1updatetime = db.Column(db.TIMESTAMP(True), nullable=False) 如果对一个 TIMESTAMP Column 使用 nullable=False ，MySQL 会自动加入on update CURRENT_TIMESTAMP 。这是 MySQL 的默认行为：sysvar_explicit_defaults_for_timestamp 。 请关注下面的 updatetime Field ：\n1mysql\u0026gt; desc bonus; 2+------------+------------+------+-----+-------------------+-----------------------------+ 3| Field | Type | Null | Key | Default | Extra | 4+------------+------------+------+-----+-------------------+-----------------------------+ 5| bid | int(11) | NO | PRI | NULL | auto_increment | 6| price | int(11) | NO | | 0 | | 7| share | int(11) | NO | | 0 | | 8| updatetime | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | 9| createtime | timestamp | NO | | CURRENT_TIMESTAMP | | 10+------------+------------+------+-----+-------------------+-----------------------------+ 然而，Extra 中包含 on update CURRENT_TIMESTAMP 的 Column， 在每次更新该 Recored 的时候，updatetime 都会自动更新。\n所以，如果需要给时间戳类型加入默认值，但不在每次更新的时候自动更新时间戳，可以这样做：\n1# 条目的更新时间。每次更新条目的时候，本字段会自动更新时间戳 2updatetime = db.Column(db.TIMESTAMP(True), nullable=False) 3# 条目的创建时间。每次更新条目的时候，本字段不会自动更新时间戳 4createtime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;NOW()\u0026#39;)) 5# 或者 6createtime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;CURRENT_TIMESTAMP\u0026#39;)) 调整默认行为的顺序 SqlAlchemy TIMESTAMP 'on update' extra 中提到 on update CURRENT_TIMESTAMP 必须是第一个 TIMESTAMP 列。对这点我并不认同，经过测试，我的结论如下：\n如果你希望通过设定非空让 MySQL 自动生成 on update CURRENT_TIMESTAMP ，则 必须 将该列作为第一个 TIMESTAMP 列。\n1class Bonus(db.Model): 2 __tablename__ = \u0026#39;bonus\u0026#39; 3 bid = db.Column(db.INT, primary_key=True, autoincrement=True) 4 # 总充值 5 price = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 6 # 总分红 7 share = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 8 # 更新时间 9 updatetime = db.Column(db.TIMESTAMP(True), nullable=False) 10 # 创建时间 11 createtime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;NOW()\u0026#39;)) 效果和 上面提到的 相同。\n如果调换顺序如下：\n1class Bonus(db.Model): 2 __tablename__ = \u0026#39;bonus\u0026#39; 3 bid = db.Column(db.INT, primary_key=True, autoincrement=True) 4 # 总充值 5 price = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 6 # 总分红 7 share = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 8 # 创建时间 9 createtime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;NOW()\u0026#39;)) 10 # 更新时间 11 updatetime = db.Column(db.TIMESTAMP(True), nullable=False) 会报错：\nsqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1067, \u0026quot;Invalid default value for 'updatetime'\u0026quot;) [SQL: '\\nCREATE TABLE bonus (\\n\\tbid INTEGER NOT NULL AUTO_INCREMENT, \\n\\tagent BIGINT NOT NULL, \\n\\tmaster BIGINT NOT NULL, \\n\\tprice INTEGER NOT NULL DEFAULT 0, \\n\\tshare INTEGER NOT NULL DEFAULT 0, \\n\\tcreatetime TIMESTAMP NOT NULL DEFAULT NOW(), \\n\\tupdatetime TIMESTAMP NOT NULL, \\n\\tPRIMARY KEY (bid), \\n\\tFOREIGN KEY(agent) REFERENCES account (gameuid), \\n\\tFOREIGN KEY(master) REFERENCES account (gameuid)\\n)\\n\\n']\n如果你一定要把 updatetime 作为第二个 timestamp 列，可以这样做：\n1class Bonus(db.Model): 2 __tablename__ = \u0026#39;bonus\u0026#39; 3 bid = db.Column(db.INT, primary_key=True, autoincrement=True) 4 # 总充值 5 price = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 6 # 总分红 7 share = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 8 # 创建时间 9 createtime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;NOW()\u0026#39;)) 10 # 更新时间 11 updatetime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP\u0026#39;)) 效果如下：\n1+------------+------------+------+-----+-------------------+-----------------------------+ 2| Field | Type | Null | Key | Default | Extra | 3+------------+------------+------+-----+-------------------+-----------------------------+ 4| bid | int(11) | NO | PRI | NULL | auto_increment | 5| agent | bigint(20) | NO | MUL | NULL | | 6| master | bigint(20) | NO | MUL | NULL | | 7| price | int(11) | NO | | 0 | | 8| share | int(11) | NO | | 0 | | 9| createtime | timestamp | NO | | CURRENT_TIMESTAMP | | 10| updatetime | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP | 11+------------+------------+------+-----+-------------------+-----------------------------+ 使用 default 我们也可以把默认值设置为空，然后通过 SQLAlchemy Column 提供的 default 在 python 层面自动加入默认值：\n1class Bonus(db.Model): 2 __tablename__ = \u0026#39;bonus\u0026#39; 3 bid = db.Column(db.INT, primary_key=True, autoincrement=True) 4 # 总充值 5 price = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 6 # 总分红 7 share = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 8 # 创建时间 9 createtime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;NOW()\u0026#39;)) 10 # 更新时间 11 updatetime = db.Column(db.TIMESTAMP(True), nullable=True, default=func.utcnow()) 效果如下。在这种情况下，MySQL 中没有设定 updatetime 的默认值，但是在给 Column 赋值的时候，python 会使用 utcnow 自动为其加入默认值。这是在 SQLAlchemy 层面实现的，并不是在 MySQL 中实现的。\n1+------------+------------+------+-----+-------------------+----------------+ 2| Field | Type | Null | Key | Default | Extra | 3+------------+------------+------+-----+-------------------+----------------+ 4| bid | int(11) | NO | PRI | NULL | auto_increment | 5| agent | bigint(20) | NO | MUL | NULL | | 6| master | bigint(20) | NO | MUL | NULL | | 7| price | int(11) | NO | | 0 | | 8| share | int(11) | NO | | 0 | | 9| createtime | timestamp | NO | | CURRENT_TIMESTAMP | | 10| updatetime | timestamp | YES | | NULL | | 11+------------+------------+------+-----+-------------------+----------------+ MySQL 版本的限制 另外，很多文章提到了 使用 server_default=text('0') 作为默认值。在 MySQL5.7上，这个默认值是不可用的：\n1class Bonus(db.Model): 2 __tablename__ = \u0026#39;bonus\u0026#39; 3 bid = db.Column(db.INT, primary_key=True, autoincrement=True) 4 # 总充值 5 price = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 6 # 总分红 7 share = db.Column(db.INTEGER, nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 8 # 更新时间 9 updatetime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;0\u0026#39;)) 10 createtime = db.Column(db.TIMESTAMP(True), nullable=False, server_default=text(\u0026#39;NOW()\u0026#39;)) sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1067, \u0026quot;Invalid default value for 'updatetime'\u0026quot;) [SQL: '\\nCREATE TABLE bonus (\\n\\tbid INTEGER NOT NULL AUTO_INCREMENT, \\n\\tagent BIGINT NOT NULL, \\n\\tmaster BIGINT NOT NULL, \\n\\tprice INTEGER NOT NULL DEFAULT 0, \\n\\tshare INTEGER NOT NULL DEFAULT 0, \\n\\tupdatetime TIMESTAMP NOT NULL DEFAULT 0, \\n\\tcreatetime TIMESTAMP NOT NULL DEFAULT NOW(), \\n\\tPRIMARY KEY (bid), \\n\\tFOREIGN KEY(agent) REFERENCES account (gameuid), \\n\\tFOREIGN KEY(master) REFERENCES account (gameuid)\\n)\\n\\n']\n参考文章 How do you get SQLAlchemy to override MySQL “on update CURRENT_TIMESTAMP SqlAlchemy TIMESTAMP 'on update' extra sysvar_explicit_defaults_for_timestamp ","date":"2017-02-27","description":"","lastmod":"2017-02-27T01:40:48Z","slug":"salqlchemy-and-timestamp","tags":["mysql","python","sqlalchemy"],"title":"SQLAlchemy and MySQL TIMESTAMP","url":"https://blog.zengrong.net/post/salqlchemy-and-timestamp/"},{"categories":["technology"],"content":"注意：这是一篇非常基础的工具使用文章。\n虽然已经迈入了 4G 时代，但由于各种各样的原因，国内的手机用户（主要是安卓用户）依然偏爱比较小的安装包。同样偏爱小安装包的还有国内各种安卓野鸡市场。因此，对于移动游戏开发者来说，掌握素材瘦身技能是必要的。本文主要介绍如何对 mp3 音频文件进行瘦身。\n前置知识 为了避免使用专业术语导致不易理解，下面的指标描述均使用较为模糊的，为大众所接受的语言，同时直接给出结论，不会详细解释选择的原因。\n音频文件有这样几个基础指标：\n采样率\n单位 KHZ（千赫兹），48/44.1 为 CD 音质；22.05/24 为语音音质；8/12/11.025 为电话音质。如果只是为了瘦身，不必关注这个值。在瘦身的过程中一般不需要重采样（resample）。 比特率\n单位 kbit/s（aka kbps,千字位每秒），极限音质使用 320；较好音质使用 128；普通音质使用64。我们在互联网下载到的大多数 mp3 音乐都是 128 kbps。见下方网易云音乐截图。 CBR/VBR/ABR\n固定比特率、可变比特率、平均比特率。它们的比特率曲线见下图。 Constant Bit Rate 固定比特率对音乐中的所有部分使用固定的比特率编码。对于手机游戏来说不建议选择。 Variable Bit Rate 可变比特率通过对声音进行分析，对动态大的部分使用较大的比特率，否则采用较小的比特率。这种方式可以在音质类似的前提下降低文件大小。 Average Bit Rate 每隔50帧对声音进行一次分析，在这50帧中采用 VBR 的编码方式。这是 LAME 发明的方法，是对 CBR 和 VBR 的折衷，理论上音质比 VBR 略好，文件大小和 VBR 相当。 Mono/Stereo/Joint Stereo\n单声道、立体声和联合立体声。 单声道音频使用单条音轨保存声音信息，左右耳听到的声音完全相同，保存的信息量比立体声少二分之一。虽然使用单声道可以让文件变得更小，但目前的手机都支持立体声，为了让用户得到更好的体验，除非音源就是单声道，否则建议使用立体声。 立体声使用两条音轨分别存储左耳和右耳听到的音频。 联合立体声会利用两条音轨中的相似的内容进行压缩，因此文件会比立体声小。许多encoder 在进行大码率压缩（\u0026gt;256）的时候，会自动把联合立体声替换成立体声。 网易云音乐选择在线播放音质的界面：\nCBR/VBR/ABR 比特率曲线：\n对于一般的非故事类手机游戏，我个人的建议是下面的值：\n背景音乐： 64kbps ABR 或 45~96kbps VBR，联合立体声。\n背景音乐一般比较长，文件也比较大，应该适当调低码率以降低文件大小。 人声： 64kbps ABR 或 32~80kbps VBR，联合立体声。\n人声对高频音并不敏感。而且由于人耳对人声比较熟悉，可以比背景音乐的码率稍低。 音效： 96 kbps ABR 或 60~128kbps VBR，联合立体声。\n音效文件长度都比较短，且对用户体验的影响较大。适当增加音效的码率会带来更好的游戏体验。 而故事类和音乐类游戏对音质的要求更高，不在本文讨论范围内。\nGUI 瘦身不仅可以通过调整比特率实现。对于背景音乐，我们还可以通过找到音频中的循环部分，删除多余的部分来实现音频瘦身。这需要一个可视化的编辑工具。\nAudacity 是一个跨平台的开源音频编辑软件，使用它可以很方便对音频文件进行编辑和转换。\n先选择音频文件中要转换的部分，然后就可以进行转换：\n导出选择的音频，选择 MP3 格式（注意Audacity的中文翻译和前置知识中的不太相同）：\n注意 Audacity 使用 LAME 作为 mp3 的编码器，而 LAME 在安装包中并没有默认提供，需要手动安装：How do I download and install the LAME MP3 encoder? 。\nLAME 若有多达数十个音频文件需要转换，使用 GUI 就不方便了。我建议使用的工具是 LAME，我对 LAME 的评价如下：\nLAME 是地球上最好的 mp3 编码器，没有之一。她凭借一己之力把 mp3 的渣音质提升到了精品级别。前面提到的 ABR 模式就是 LAME 发明的。虽然已经很久没有更新，但 LAME 在 mp3 音频编码器的地位上依然无码可以撼动。\n看看如何使用 LAME 压缩一个 45~80kbps VBR 联合立体声的 mp3：\n1lame -m j -V 0 -q 0 -b 45 -B 80 {input_file} {output_file} 再来看看如何使用压缩一个 64kbps ABR 联合立体声的 mp3：\n1lame -m j --abr 64 {input_file} {output_file} -m 参数指定 j 为联合立体声。如果不提供这个参数，lame 会默认按照码率来自动选择 (j)oint ，或者 (s)imple 。j 代表联合立体声，s 代表立体声。一般情况下，超过 256kbps 的码率会自动使用立体声。\n更详细的说明请看这里： Usage LAME 。\n我编写了一个简单的脚本 shrink.sh ，可以根据传递的文件路径自动创建子文件夹，用它配合 find 命令使用，就很容易进行批量转换了。\n脚本用法 $ shrink.sh {input_file} {output_root_dir} 范例1，处理单个文件 瘦身 bgm/background_table.mp3 ，另存为 shrink/bgm/background_table.mp3 。\n$ shrink.sh current/bgm/background_table.mp3 shrink 范例2，批量处理 批量处理，对所有 bgm 文件夹下的 mp3 文件进行瘦身，目标文件夹是 shrink，支持嵌套文件夹。\n$ find bgm -name '*.mp3' -exec ./shrink.sh {} shrink \\; FFMPEG 用 Audacity 很容易实现导出音频文件的一部分。而是用 LAME 就无法做到。我们可以借助大名鼎鼎的 FFMPEG 来实现 mp3 无损分割。FFMPEG 是个全能型软件，对声音和视频的操作都不在话下。\n无损剪切一段 mp3 文件的脚本是这样的：\n1$ ffmpeg -i {input_file} -t {time} -acodec copy {output_file} ffmpeg 会自动根据扩展名 '.mp3' 来自动判断你的文件格式进行处理。\n其中 time 的值可以是下面这两种：\n秒 例如：78.693 时:分:秒 例如： 0:25:33.108 我同样封装了一个简单的 cutmp3.sh 来简化输入。\n脚本用法 $ cutmp3.sh {time} {input_file} {output_file} 范例 剪切 bgm/background_table.mp3 ，从文件开头到 75.869 秒，生成 current/bgm/background_table2.mp3 ：\n$ cutmp3.sh 75.869 bgm/background_table.mp3 bgm/background_table2.mp3 查看码率 在转换 mp3 文件之前，应该先查看一下文件的比特率。如果原始的比特率很低，就没必要进行瘦身了。\n可以使用 file 或者 ffmpeg 命令来查看音频文件信息：\n1$ file background_table.mp3 2background_table.mp3: Audio file with ID3 version 2.3.0, contains: MPEG ADTS, layer III, v2, 64 kbps, 24 kHz, JntStereo 1$ ffmpeg -i background_table.mp3 2Input #0, mp3, from \u0026#39;background_table.mp3\u0026#39;: 3 Metadata: 4 encoder : Lavf55.19.100 5 Duration: 00:02:00.10, start: 0.046042, bitrate: 62 kb/s 6 Stream #0:0: Audio: mp3, 24000 Hz, stereo, s16p, 62 kb/s 7 Metadata: 8 encoder : LAME3.99r 9 Side data: 10 replaygain: track gain - -5.400000, track peak - unknown, album gain - unknown, album peak - unknown, （全文完）\n","date":"2017-02-12","description":"","lastmod":"2024-05-19T03:54:27Z","slug":"use-lame-to-shrink-mp3-file","tags":["shell"],"title":"使用 audacity/lame/ffmpeg 进行 mp3 文件瘦身","url":"https://blog.zengrong.net/post/use-lame-to-shrink-mp3-file/"},{"categories":["technology"],"content":"我在使用 requests 的时候频繁遇到下面的错误：\nEOF occurred in violation of protocol (_ssl.c:600)\n这个错误在 Python2 和 Python3，macOS 和 Ubuntu 下都有出现。从报错信息中可以看出该错误与 HTTPS 握手相关。\n这个错误在 这个 issue 中讨论得最为充分。\n通过分析，我找到了这个问题产生的原因：在安装 requests 库的时候，没有正确安装 security 包。\n下面是解决方案（针对 Ubuntu）：\n1. 安装支持的 dev 包：\n1sudo apt-get install libffi-dev libssl-dev python-dev 若不安装上面的包，在安装 requests 的相关支持包的时候，可能会出现下面的错误：\n1distutils.errors.DistutilsError: Setup script exited with error: command \u0026#39;x86_64-linux-gnu-gcc\u0026#39; failed with exit status 1 2Failed cleaning build dir for cryptography 2. 安装 requests 及其安全支持包\n1pip install \u0026#39;requests[security]\u0026#39; requests[security] 是一个扩展，它会安装下面三个附加的包用来支持安全连接：\npyOpenSSL ndg-httpsclient pyasn1 详见 extras_require 。\n参考：\nSSL InsecurePlatform error when using Requests package pip install requests[security] vs pip install requests: Difference Issue #3006 （全文完）\n","date":"2017-02-06","description":"","lastmod":"2017-02-06T12:15:31Z","slug":"eof-occurred-in-violation-of-protocol","tags":["python","flask"],"title":"EOF occurred in violation of protocol","url":"https://blog.zengrong.net/post/eof-occurred-in-violation-of-protocol/"},{"categories":["technology"],"content":"要提供一个 RESTful API ，就必须考虑 跨域请求(CORS) 问题。在 Flask 中，我们可以进行这样的简单处理：\n1@main.route(\u0026#39;/\u0026#39;, methods=[\u0026#39;GET\u0026#39;]) 2def index(): 3 resp = jsonify({\u0026#39;error\u0026#39;:False}) 4 # 跨域设置 5 resp.headers[\u0026#39;Access-Control-Allow-Origin\u0026#39;] = \u0026#39;*\u0026#39; 6 return resp 当路由较多的时候，这样写未免不优雅，我们可以封装一个方法 responseto 用来代替 jsonify：\n1def responseto(message=None, error=None, data=None, **kwargs): 2 \u0026#34;\u0026#34;\u0026#34; 封装 json 响应 3 \u0026#34;\u0026#34;\u0026#34; 4 # 如果提供了 data，那么不理任何其他参数，直接响应 data 5 if not data: 6 data = kwargs 7 data[\u0026#39;error\u0026#39;] = error 8 if message: 9 # 除非显示提供 error 的值，否则默认为 True 10 # 意思是提供了 message 就代表有 error 11 data[\u0026#39;message\u0026#39;] = message 12 if error is None: 13 data[\u0026#39;error\u0026#39;] = True 14 else: 15 # 除非显示提供 error 的值，否则默认为 False 16 # 意思是没有提供 message 就代表没有 error 17 if error is None: 18 data[\u0026#39;error\u0026#39;] = False 19 if not isinstance(data, dict): 20 data = {\u0026#39;error\u0026#39;:True, \u0026#39;message\u0026#39;:\u0026#39;data 必须是一个 dict！\u0026#39;} 21 resp = jsonify(data) 22 # 跨域设置 23 resp.headers[\u0026#39;Access-Control-Allow-Origin\u0026#39;] = \u0026#39;*\u0026#39; 24 return resp 这样，上面的代码可以简化为：\n1@main.route(\u0026#39;/\u0026#39;, methods=[\u0026#39;GET\u0026#39;]) 2def index(): 3 return responseto() 要响应一个错误消息，可以简化为：\n1responseto(\u0026#39;发生了一个错误！\u0026#39;) 但是，当我使用 PUT 方法请求时，出现了这样的错误：\n1XMLHttpRequest cannot load http://127.0.0.1:5000/account/status/. Response to preflight request doesn\u0026#39;t pass access control check: No \u0026#39;Access-Control-Allow-Origin\u0026#39; header is present on the requested resource. Origin \u0026#39;http://localhost:5001\u0026#39; is therefore not allowed access. 从当时请求的内容（见下方）可以看出，请求变成了 OPTIONS 而不是 PUT 。这是由于根据 CORS 规范 (via) ，浏览器会做一次 preflight 请求，这次请求询问服务器支持哪些方法。\n1OPTIONS /account/status/ HTTP/1.1 2Host: 127.0.0.1:5000 3Connection: keep-alive 4Pragma: no-cache 5Cache-Control: no-cache 6Access-Control-Request-Method: PUT 7Origin: http://localhost:5001 8User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Mobile Safari/537.36 9Access-Control-Request-Headers: 10Accept: */* 11Referer: http://localhost:5001/account/ 12Accept-Encoding: gzip, deflate, sdch, br 13Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 因此，上面提到的使用 responseto 的方法就没有作用了。因为任何一个路由都有可能包含 preflight 请求。\n我们可以通过继承 Flask 的 Response 类来实现这个需求，让 flask 回复的任何响应都带有 Access-Control-Allow-* 的 HEAD。通过设置 Flask app 的 response_class 属性可以让 Flask 使用我们自定义的子类作为响应。\n1from flask import Flask, Response 2 3class MyResponse(Response): 4 pass 5 6def create_app(): 7 app = Flask(__name__) 8 app.response_class = MyResponse 我们需要在 MyResponse 类中对 headers 进行一些操作。为此我们需要了解 Flask Response 的源码实现。\nFlask 的 Response 是对 werkzeug.wrappers.Response 的一个简单继承。下面是 flask 中 Response 的源码实现（位于 wrappers.py 中）：\n1from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase 2 3class Response(ResponseBase): 4 \u0026#34;\u0026#34;\u0026#34;The response object that is used by default in Flask. Works like the 5 response object from Werkzeug but is set to have an HTML mimetype by 6 default. Quite often you don\u0026#39;t have to create this object yourself because 7 :meth:`~flask.Flask.make_response` will take care of that for you. 8 9 If you want to replace the response object used you can subclass this and 10 set :attr:`~flask.Flask.response_class` to your subclass. 11 \u0026#34;\u0026#34;\u0026#34; 12 default_mimetype = \u0026#39;text/html\u0026#39; 没错，就是仅此而已。因此我们还需要去看 werkzeug.wrappers.Response 的源码。下面是一点点节选：\n1 def __init__(self, response=None, status=None, headers=None, 2 mimetype=None, content_type=None, direct_passthrough=False): 3 if isinstance(headers, Headers): 4 self.headers = headers 5 elif not headers: 6 self.headers = Headers() 7 else: 8 self.headers = Headers(headers) 由于参数太多，在实现继承的时候，我们仅保留一个 response 参数，其余的使用 **kwargs 代替：\n1from werkzeug.datastructures import Headers 2 3class MyResponse(Response): 4 def __init__(self, response=None, **kwargs): 5 kwargs[\u0026#39;headers\u0026#39;] = \u0026#39;\u0026#39; 6 headers = kwargs.get(\u0026#39;headers\u0026#39;) 7 # 跨域控制 8 origin = (\u0026#39;Access-Control-Allow-Origin\u0026#39;, \u0026#39;*\u0026#39;) 9 methods = (\u0026#39;Access-Control-Allow-Methods\u0026#39;, \u0026#39;HEAD, OPTIONS, GET, POST, DELETE, PUT\u0026#39;) 10 if headers: 11 headers.add(*origin) 12 headers.add(*methods) 13 else: 14 headers = Headers([origin, methods]) 15 kwargs[\u0026#39;headers\u0026#39;] = headers 16 return super().__init__(response, **kwargs) 使用上面的代码可以方便地实现跨域支持，根据需要调整 methods 和 origin 的值即可。\n关于自定义响应类，Miguel 的这篇文章写得更详细： Customizing the Flask Response Class 。\n（全文完）\n","date":"2017-01-15","description":"","lastmod":"2017-01-15T09:38:36Z","slug":"modify-the-response-head-in-flask","tags":["flask","python","server"],"title":"修改 Flask 的默认响应头实现跨域(CORS)支持","url":"https://blog.zengrong.net/post/modify-the-response-head-in-flask/"},{"categories":["technology"],"content":"前段时间用 Flask 制作的一个网站在进行较大量计算时频繁出现 502 错误，在本地调试却无法重现发现，我怀疑是服务器配置问题。现已查明原因，分别是 Nginx 配置和 uwsgi 配置造成的。\nupstream sent too big header while reading response header from upstream 这个错误的可以在 Nginx log 中找到：\n12017/01/06 16:42:17 [error] 15273#0: *1087067 upstream sent too big header while reading response header from upstream, client: xxx.xx.xxx.98, server: xxx.bbb.com, request: \u0026#34;GET /kpi/sheets/calc/?team_id=client\u0026amp;ks_date=2016-12\u0026amp;typ=all HTTP/1.1\u0026#34;, upstream: \u0026#34;uwsgi://127.0.0.1:5000\u0026#34;, host: \u0026#34;xxx.bbb.com\u0026#34;, referrer: \u0026#34;http://xxx.bbb.com/kpi/sheets/client/2016-12/\u0026#34; 这是一个缓存设置的问题，打开 nginx 配置，加入两行配置即可：\n1uwsgi_buffers 16 16k; 2uwsgi_buffer_size 32k; 注意，如果你没有明确写入这两个参数，它们的默认值为：\n1uwsgi_buffers 8 4k|8k; # 根据操作系统内存页大小 2uwsgi_buffer_size 4k|8k; # 根据操作系统内存页大小 具体配置的说明可以参考 uwsgi_buffers 和 uwsgi_buffer_size 。\n解决方案来自于这个回答： upstream sent too big header while reading response header from upstream ，该回答针对 fastcgi ，改为 uwsgi 即可。\n如何配置 Nginx 可以参考我的文章《部署Flask + uWSGI + Nginx》 中的 配置 Nginx + uwsgi 。\ninvalid request block size 另一个 502 错误则是由 uwsgi 造成的。现象是 uwsgi 的 log 中出现了这样的提示：\n1invalid request block size: 4363 (max 4096)...skip 将 uwsgi 配置文件中的 buffer-size 设置为较大的值即可，例如我设置为32KB：\n1buffer-size = 32768 如何配置 Nginx 可以参考我的文章《部署Flask + uWSGI + Nginx》 中的 配置 uwsgi 。\n","date":"2017-01-06","description":"","lastmod":"2017-01-06T08:54:52Z","slug":"flask-502-uwsgi-buffer","tags":["flask","python","server","uwsgi"],"title":"Flask 502 错误解决：upstream sent too big header and invalid request block size","url":"https://blog.zengrong.net/post/flask-502-uwsgi-buffer/"},{"categories":["technology"],"content":"上个月，我写过一篇 SaltStack/Ansible/Fabric 的选择 。这段时间里，我在大约 4 台服务器上启用了 SaltStack + Fabric 。下面说说我的上手方法。\nSaltStack 的文档质量的确很高，我建议直接从 SaltStack Get Started 看起即可，不超过1个小时，你就能对 SaltStack 的主要功能有个大概的了解，并会爱上这个工具。\nGet Started 系列文档有8个主题，文档中很贴心地用主题的大小来标明了文档的重要性。我建议的阅读顺序为：\n首先阅读 SaltStack Fundamentals 和 Configuration Management ,这样就能立刻开始使用 SaltStack。 接着阅读 Agentless SaltStack ，了解使用 SSH 的方式来使用 SaltStack。SaltStack 是支持不安装 Agent 直接部署的。 随后阅读 Understanding SaltStack ，了解 SaltStack 的相关知识点，这些知识点对理解 SaltStack 的工作方式至关重要。 最后阅读 Event-Driven Infrastructure ，了解 SaltStack 事件驱动基础，整个 SaltStack 的通信都构建在该基础之上。 剩下的几篇就慢慢读啦 :-) SaltStack 的入门文档中，每一篇都贴心的提供了阅读速度。例如安装这篇需要大约 10 分钟。如果熟悉运维工作，那么这篇其实只要 1 分钟就好了。\n本来准备写篇长文，但本文停了一个月之后，不想再写了。看看各位与 saltstack 的缘分如何，到此为止吧。\n（全文完）\n","date":"2017-01-05","description":"","lastmod":"2017-01-05T01:37:46Z","slug":"saltstack-newbie","tags":["python","devops"],"title":"saltstack 简单上手","url":"https://blog.zengrong.net/post/saltstack-newbie/"},{"categories":["technology"],"content":" 2016-12-19 更新： 增加一些对 Salt 文档的理解 2017-04-16 更新： 增加一些对 SaltStack 简单上手 运维自动化管理必须要提上日程了。因为 Python 语言的关系，我将选择的目标定在 SaltStack、Ansible、Fabric 上。\n我的选择是 Salt 。\n查看 选择系列文章 。\nFabric 和 Salt/Ansible 并非一个级别。它适合用来做较少的服务器管理。如果超过 10 台服务器，用它就不太合适了。况且，Fabric 可以很容易和其他系统整合在一起使用。\nSalt 和 Ansible 倒是不相上下。下面做一些比较：\n社区 下面表格中的所有的实时数据采集时间为 2016-12-13 20:09:26 。\nKey Salt Ansible 备注 公司支持 SaltStack Ansible 开源 Salt Ansible Star 7140 20199 Opend Pull requests 43 621 可以从侧面说明社区解决问题的速度 Opend Issues 4026 621 可以从侧面说明产品稳定性 Contributors 1665 2301 可以从侧面说明社区活跃度 下面的判断是我根据互联网上到的信息总结而成。其中有些信息可能不准确、不可靠或已经过时，请自行判断。\nAgent Salt 和 Ansible 都使用 YAML 做为配置，且都支持 Server-Client 模式。Salt 默认就使用 ZeroMQ 队列，而 Ansible 默认使用 SSH 协议。在默认情况下 ，Salt 会比 Ansible 快很多（根据测试，大约40倍）。而 Ansible 部署起来比 Salt 又方便很多（因为不用安装 Agent）。\n注意我前面说到了 在默认情况下 。实际上， Salt 可以使用 salt ssh 基于 SSH 实现 agentless 部署。Ansible 也可以使用 fireball 模式 来得到和 Salt 完全相同的速度。然而，Ansible 已经将 fireball 模式废弃了，更推荐使用 SSH 的 pipelining ，根据官方文档，这能大幅提升通过 SSH 部署的速度。\n例如，Salt 文档的 Agentless Salt 中就提到了：\nYou can use Salt agentless to run Salt commands on a system without installing a Salt minion. The only requirements on the remote system are SSH and Python.\n这已经和 Ansible 的部署方式相同了。更有趣的是，Salt 还提供了对 Ansible Inventory 支持！\n所以啊，说 Ansible 慢并不是很准确，说 Salt 无法 agentless 也未必。\n关于 Agent，个人认为大可不必将其作为决定选择的条件。无论如何，Agent 的部署是很简单的 。需要考虑的，应该是 Agent 的安全性。毕竟运维 Agent 权限那么高，本身就是一个大后门啊！！！\nWeb UI 好的 Web UI 是非常非常非常重要的。\n当我在搜寻 Salt 的 GUI 的时候，发现 salt-api 的文档 Outdated 了。后来找到了 halite ，发现 官方又将其 DEPRECATED 了 。最后才找到 SaltPad ，却发现它还只是个 Alpha 版本。这种更新速度也是让人醉了…… SaltStack Enterprise 的界面倒是看起来不错啊！就是不知道多少钱……\nAnsible 的 Tower 看起来也不错，100 个 node 也只要 $5000 每年挺便宜的…… 另外有一个社区的开源替代版本 Semaphore。\n文档 开源产品最重要的就是文档。从文档的卖相来看，两者都是不差的。但互联网上找到的大多数资料都提到了 Ansible 的文档比较乱，而且挺多错误。但再乱也乱不过 Redmine 的文档吧？\nAnsible Documentation Salt Table of Contents 仔细看过 Salt 的文档后，发现它的设计是真心不错：saltstack 简单上手 。\nWindows 支持 Salt 有 良好的 Windows 操作系统支持 。\nAnsible 从 1.7 版本才加入 Windows 支持 ，由于 Windows 默认没有 SSH 的缘故，它依赖 PowerShell remoting 来实现远程管理，要求必须使用 Linux 机器来控制 ，在控制机器上需要安装 pywinrm 包。有趣的是，从 Ansible 2.0 开始，一些配置中均去掉了 ssh 字样。例如 ansible_ssh_user, ansible_ssh_host, ansible_ssh_port 改名成了 ansible_user, ansible_host, ansible_post 。这应该也是为了迎合 Windows 服务器的需要吧？\n从这一点来看，如果集群中有 Windows 服务器，应该尽量选择 Salt 。\nWhy Salt? 这个问题不好回答，说凭感觉又不合适。我想我应该是考虑了文档风格、模版语法风格、社区活跃性这三点吧。\n其实起决定性的是 Windows 服务器支持。咦，我好像透露了什么……\n参考 Ansible and Salt: A detailed comparison SaltPad salt-api SaltStack 与 Ansible 选择？ Salt Stack VS Fabric SaltStack Package Repo 全文完 ","date":"2016-12-13","description":"","lastmod":"2017-04-16T02:43:15Z","slug":"choose-in-salt-ansible-fabric","tags":["devops","choice","python"],"title":"SaltStack/Ansible/Fabric 的选择","url":"https://blog.zengrong.net/post/choose-in-salt-ansible-fabric/"},{"categories":["technology"],"content":"Redmine 的官方 Wiki 里面有许多文档可能过时了，可能并非 Best Practice 。关于如何在 Redmine 中整合 Git 版本库，这篇文章 是最简单的。我把原文做了一些修改，使用中文提供在这里：\n[TOC]\n我的配置 我的 redmine 用户名称为： redmine ； 我的安装目录在 /srv/redemine ，我的仓库文件夹为 /srv/redmine/repos ； git 的仓库的地址为 ssh://redmine@git.mysite.com:29418/a.git 和 ssh://redmine@git.mysite.com:29418/b.git （是的，我使用 Gerrit）。 Redmine 使用的方法，就是从远程版本库中 Clone 一个版本库，然后把所有的提交信息写入 Redmine 的数据库中。\n第一步： Clone 版本库 首先使用 MIRROR 模式来 Clone 我们的版本库。Mirror 模式的版本库将仅包含提交信息而不包含具体提交的文件。这会让我们 Clone 的内容比较小，速度足够快。\n1sudo -su redmine 2cd /srv/redmine/repos/ 3git clone --mirror ssh://redmine@git.mysite.com:29418/a.git 4git clone --mirror ssh://redmine@git.mysite.com:29418/b.git 接下来需要让 Redmine 中配置版本库的路径。\n第二步： 配置 Redmine 进入 \u0026quot;administration \u0026gt; project -\u0026gt; repositories\u0026quot; 界面开启版本库支持。然后进入 \u0026quot;project -\u0026gt; settings -\u0026gt; repositories\u0026quot; 增加版本库配置：\nType: GIT Main-repository: 是否是主版本库。主版本库只能有一个。 Name: 一般是使用和版本库相同的名称。 Path: 版本库在服务器上的绝对路径。/srv/redmine/repos/a.git 版本库可以增加多个，可以再加入一个版本库指向 b.git 。但是主版本库只能有一个，如果两个都设置成了主版本库，那么生效的是最新的那一个。\n配置完成后，进入项目的版本库界面就能看到版本库的提交历史记录和版本树了。\n如果你的版本库有成百上千次提交，那么第一次打开版本库界面可能会等上一段时间，因为 Redmine 在把版本库中的提交信息写入数据库。此时最好不要刷新界面。\n为了避免上面的情况，可以做离线刷新： Attaching an existing repository to a project 。\n第三步：定时刷新版本库 Redmine 并不会主动去刷新版本库。我们可以使用 GitHook 的方式来更新版本库，也可以直接用 Crontab 来实现。\n下面的代码设定每隔 5 分钟去远程仓库同步一次。\n1sudo crontab -e -u redmine 2# 如果当前处于 redmine 账户下，也可以使用 3crontab -e 4 5# 选择你喜欢的编辑器，在打开的编辑器中写入下面的内容 6*/5 * * * * git -C /srv/redmine/repos/a fetch --all 7*/5 * * * * git -C /srv/redmine/repos/b fetch --all 设置默认显示的版本库分支 Redmine 默认显示的提交信息是仓库的默认分值（一般为 master 分支）。如果你使用了 git flow 工作流 ，那么很可能你会希望默认分支是 develop 。\n这就出现了新问题：在使用 --mirror 参数 clone 的仓库中，你不能使用 git checkout develop 来切换工作分值。\n此时你可以使用下面的命令来切换默认分支：\n1git symbolic-ref HEAD refs/heads/develop symbolic-ref 子命令的说明在此： git-symbolic-ref 。\n当然，你也可以直接编辑 /srv/redmine/repos/a.git/HEAD 这个文件，能达到一样的效果。\n在提交信息中更新问题 在管理员面板中将版本库的 Referencing keywords 设置为： issue；将 Fixing keywords 的值设置为： fix 。\n在提交信息中使用下面的语法来引用问题1，2，同时把问题3置为 已解决 状态：\n这个问题 issue #1, #2 和 fix #3 这个问题 issue:#1, #2 和 fix:#3 这个问题 issue: #1, #2 和 fix: #3 更详细的内容可以参考： Referencing issues in commit messages 。\n（全文完）\n","date":"2016-12-12","description":"","lastmod":"2016-12-12T14:14:01Z","slug":"integrate-git-into-redmine","tags":["ruby","git"],"title":"在 Redmine 中整合 Git 版本库","url":"https://blog.zengrong.net/post/integrate-git-into-redmine/"},{"categories":["technology"],"content":"今天花了3个小时把公司内网论坛架好了，同时接入了公司的 Active Directory 服务器账号登录。在此做一点记录。\n因为最近用 flask 写了几个网站，所以论坛系统我也选择了 flaskbb，方便平时改着玩儿。它是一个基于 flask 的轻量级论坛，在 Github 上有 800+ follows，它使用了大量的 flask 插件来简化开发，自身也支持插件系统。\n依赖 应该使用 python 2.7 来部署 flaskbb。\n为了支持 MySQL，我们需要安装一个数据库驱动库，这里我选择 PyMySQL，它使用纯 Python 实现，在 Python2/3 下都可以使用。使用 pip install pymysql 安装它。\n为了支持 LDAP 协议，我们需要安装一个 LDAP 驱动。这里我选择 ldap3，我在 实现 LDAP 验证登录 中做过介绍。它也是使用纯 Python 实现， 同时兼容 Python2/3 。使用 pip install ldap3 安装它。\n基于 MySQL 安装 默认配置文件中使用的数据库系统是 sqlite。如果要使用 MySQL，需要做一些小小的修改。\n首先，需要修改官方配置文件中的 SQLALCHEMY_DATABASE_URI 为如下所示：\nSQLALCHEMY_DATABASE_URI = 'mysql+pymysql://zrong:password@localhost/flaskbb' 这里的配置已经指定使用 MySQL 数据库，而且采用 pymysql 作为数据库驱动，数据库名称是 flaskbb。flaskbb 应该是一个已经存在的数据库，而且内容为空。除了基本的 SELECT/INSERT/UPDATE/DELETE 权限外，zrong 这个帐号需要有 ALTER/CREATE/INDEX 权限。\n接下来，需要初始化数据库。如果按照官方文档所说执行 make install 多半是不成功的，会提示表不存在。这可能是由于作者没有仔细测试，或者没有更新文档的原因。正确的用法应该是执行下面的命令对数据库进行初始化（建表，填充必要数据）：\n1python manage.py initdb 然后创建一个管理员，命令行会询问帐号和密码等信息：\n1python manage.py create_admin 如果要显示中文翻译（并不完全），还可以编译语言文件：\n1python manage.py compile_translations BTW: flaskbb 采用的是 GNU gettext 实现多语言支持的，这和 Wordpress 一样。我曾经写过一个 lua 封装：在Lua中使用gettext实现多语言支持。在 Python 中，babel 就带有 gettext 的支持。\nLDAP (AD) 集成 我在 实现 LDAP 验证登录 中已经介绍过 Python 的 LDAP 实现。这里介绍一下应该如何修改 flaskbb 使其支持与公司的 AD 服务器集成。\n一开始我准备采用 flaskbb 的 插件系统 来实现这个功能，这样就可以不必修改源码。但我仔细看了官方提供的 Portal 插件的实现后后发现，flaskbb 中的插件机制是通过预埋 event 钩子来实现注入的。这个 event 必须自己埋入代码中才能生效。这就违背了我不愿意改源码的初衷。时间有限，插件的机制后面再深究，先把功能实现再说。\nLDAP 服务器的信息需要加入到配置项中，我修改了 configs/production.py.example ，这些项目必须配置才能实现 LDAP 登录：\n1# LDAP Settings 2# ------------------------------ # 3# For LDAP authorize 4LDAP_SERVER = \u0026#39;ldapserver.example.org\u0026#39; 5LDAP_PORT = None 6LDAP_USER_FMT = \u0026#39;Yourcompany\\\\%s\u0026#39; 7LDAP_EMAIL_SUFFIX = \u0026#39;@example.org\u0026#39; 我增加了一个 staticmethod User._check_login_password，在这个方法中提供用户名和密码，读取上述配置，去指定的 LDAP 服务器鉴权。\n帐号鉴权调用的是 user/models.py 中的 User.authenticate 方法。这是个 classmethod 。我增加了一个 User.authenticate_ldap classmethod 用来替换原始方法，这个修改的方法中会调用 User._check_login_password 。\n这个方法的实现流程为：先检测帐号的合法性，去 AD 服务器鉴权。若鉴权成功，就查询 flashbb 的数据库中有没有该账号，若有则更新登录时间，若无则在 flaskbb 的 users 表中创建该帐号，将密码置为 'password' ，并立即启用该帐号。\n1user = User(username=login, 2 email=login+email_suffix, 3 password=\u0026#39;password\u0026#39;, 4 date_joined=time_utcnow(), 5 primary_group_id=4, 6 language=\u0026#39;zh\u0026#39;, 7 activated=True) 8user.save() 由于鉴权完全使用 AD 服务器实现，在 users 表中 不应该 保存密码。\n完整的方法可查看 User.authenticate_ldap。\n调用鉴权的位置是 auth/views.py 中的 login 路由。将这个路由的端点注释掉，改名为 login_origin，创建一个新的 login 方法，使其调用 User.authenticate_ldap 方法进行鉴权。\n完整的方法可查看 login。\n2016-11-17 增加：\n在 auth/views.py 中还有一个 reauth 方法需要重新实现。reauth 用来重新验证用户的密码。我们需要取得用户的账号，带上用户输入的密码去 LDAP 服务器进行鉴权。修改后的 reauth 方法直接调用 User.check_password_ldap 来进行鉴权。\n完整的方法可查看 reauth。\n禁用功能 由于鉴权完全使用 AD 服务器实现，注册账号、修改账号、重置密码、修改密码、修改 email 等功能都需要禁用。\n注册账号的功能可以直接在 flaskbb 的后台中取消。其他几个功能则需要修改源码实现。\n忘记密码和重置密码功能在 auth/views.py 的 forgot_password 和 reset_password 中，abort(403) 即可：\n1@auth.route(\u0026#39;/reset-password\u0026#39;, methods=[\u0026#34;GET\u0026#34;, \u0026#34;POST\u0026#34;]) 2def forgot_password(): 3 \u0026#34;\u0026#34;\u0026#34;Sends a reset password token to the user.\u0026#34;\u0026#34;\u0026#34; 4 # zrong start 2016-11-16 No permission to reset password when login by LDAP 5 abort(403) 6 # zrong end 2016-11-16 完整的方法可查看 forgot_password 和 reset_password。\n修改密码和修改 email 功能在 user/views.py 的 change_password 和 change_email 中，处理方法相同。\n1@user.route(\u0026#34;/settings/password\u0026#34;, methods=[\u0026#34;POST\u0026#34;, \u0026#34;GET\u0026#34;]) 2@login_required 3def change_password(): 4 # zrong start 2016-11-16 No permission to modify password when login by LDAP 5 abort(403) 6 # zrong end 2016-11-16 完整的方法可查看 change_password 和 change_email。\n最后的部署，当然是看我之前写的 部署Flask + uWSGI + Nginx 啦！全部搞定！\n（全文完）\n","date":"2016-11-16","description":"","lastmod":"2016-11-17T11:06:16Z","slug":"flashbb-and-ad","tags":["python","server","flask"],"title":"flaskbb 配置与 AD 登录","url":"https://blog.zengrong.net/post/flashbb-and-ad/"},{"categories":["technology"],"content":"10月发生的 Apple 在iOS 可信根证书列表中屏蔽其对中级CA WoSign(沃通) CA Free SSL Certificate G2 的信任 让许多普通人第一次接触到了 HTTPS 这个已经陪伴了我们很久的技术。作为程序员，我们应该更多地了解 HTTPS 的相关知识。\nHTTPS 到了应该全面普及的时候了。\nHTTPS 是什么？ 在 Wikipedia 上，HTTPS的解释是：\nHTTPS 也被叫做“基于 TLS 的 HTTP，基于 SSL 的 HTTP，HTTP 安全“，它是一个在 Internet 上广泛使用的计算机网络安全通信协议。\nHTTPS (also called HTTP over TLS, HTTP over SSL, and HTTP Secure) is a protocol for secure communication over a computer network which is widely used on the Internet.\n由于现代浏览器的普及，对于普通用户来说，不会太关注 HTTP 后面多的那个 'S' 。现代浏览器在解析域名的时候都可以做到不必输入 HTTP 或者 HTTPS，就能直达网站。一些网站把自己的 HTTP 网站重定向到 HTTPS 网站。而一些网站则同时提供 HTTP 和 HTTPS 网站。网站是否支持 HTTPS，是由网站开发者/提供者决定的。\n通过 HTTP 协议传输的信息是明文的。网络包从客户端/浏览器到服务器的这段过程中，需要经过多个网络设备。我们的登录帐号、密码可以轻易被任何一个中间设备探测到。\nHTTPS 是非常安全的。信息在客户端/浏览器中被加密，服务器通过私钥解密。中间设备如果没有私钥，就无法解密。\n1digraph https_transport { 2 graph[fontsize=30;rankdir=\u0026#34;LR\u0026#34;,label=\u0026#34;HTTP(S)传输\u0026#34;]; 3 node[shape=box]; 4 { node[shape=cds]; middle1; middle2; middle3; middle4 } 5 { node[shape=doubleoctagon]; server1; server2 } 6 7 client1[label=\u0026#34;客户端\u0026#34;]; 8 client2[label=\u0026#34;客户端\u0026#34;]; 9 middle1[label=\u0026#34;中间设备\u0026#34;] 10 middle2[label=\u0026#34;中间设备\u0026#34;] 11 middle3[label=\u0026#34;中间设备\u0026#34;] 12 middle4[label=\u0026#34;中间设备\u0026#34;] 13 server1[label=\u0026#34;服务端\u0026#34;]; 14 server2[label=\u0026#34;服务端\u0026#34;]; 15 16 subgraph cluster_https{ 17 label=\u0026#34;HTTPS\u0026#34;; 18 edge[headlabel=\u0026#34;密文\u0026#34;]; 19 client1 -\u0026gt; middle1 -\u0026gt; middle2 -\u0026gt; server1; 20 } 21 22 subgraph cluster_http{ 23 label=\u0026#34;HTTP\u0026#34;; 24 edge[headlabel=\u0026#34;明文\u0026#34;]; 25 client2 -\u0026gt; middle3 -\u0026gt; middle4 -\u0026gt; server2 26 } 27} 示意图采用 graphviz 绘制，下载源码：\n1 文件 大公司在 HTTPS 的推进上也是不遗余力的。Google 在 2010年5月 就已经实现了全站 HTTPS 支持。2011年，Google 更是要求 Google要求广告主必须在其网站部署SSL证书。\nApple 也对 HTTPS 进行了强力推动。我在 2014 年发布 Apple 企业应用的时候，就碰到了必须使用 HTTPS 的问题：iOS 7.1在线安装IPA失败以及数字证书 。Apple 还要求 2017 年必须在上架 AppStore 的 App 中使用 HTTPS ：App Transport Security REQUIRED January 2017 。\n在国内，腾讯、网易等网站虽然没有启用全站 HTTPS，但涉及到帐号的页面（例如 QQ 注册，网易邮箱注册）均使用了 HTTPS。微信要求支付回调页面必须使用 HTTPS 协议。一些新兴的互联网公司，例如 直播平台斗鱼 甚至超前实现了全站 HTTPS 支持。\n所以，HTTPS 到了应该全面普及的时候了。\nHTTPS 证书 HTTPS 是由客户端和服务端一起实现的。对于普通用户来说，最常见的客户端就是浏览器。浏览器在访问支持 HTTPS 的网站时，会对该网站的证书进行校验，若证书有问题，则会给出提示：\n若该证书通过了校验，在浏览器地址栏中就会显示一个绿色的小锁，代表这个网站是安全的。下图就是全球最大的男性交友网站的 HTTPS 安全提醒：\n要申请一个证书，我们需要找 CA(Catificate Authority)。任何组织和个人都可以扮演 CA 的角色，给自己签发证书。全球最大的火车票销售网站就是这么干的：\n但是从下面的表现也能看出来，这么着自己给自己发证书，浏览器(Safari)是不认的。你得手动信任该证书才能正常买票。从安全角度来说，我个人并不建议你信任 12306 的那个一看就很不专业的根证书。\n因此，我们必须找一个信得过的 CA 来申请证书。得到各大浏览器信任的内置根证书（内置于浏览器或操作系统）颁发机构 TOP5 是 Symantec、Comodo、Godaddy、GolbalSign 和 Digicert。\n上面提到的证书颁发机构都是根证书颁发机构。为了方便分级管理，根证书颁发机构一般会颁发几个中级证书颁发机构证书，然后再由中级证书颁发机构面向个人或者机构颁发证书。文章开头提到的 9月30日 Apple 在iOS 可信根证书列表中屏蔽其对中级CA WoSign(沃通) CA Free SSL Certificate G2 的信任，处理的就是沃通的中级证书（而不是根证书）。\n证书是 HTTPS 安全最重要的保证。如果有一家 CA 公司签发虚假的证书，或者颁发的证书时间有问题会怎样？它会被浏览器或操作系统公司处罚。\n沃通(WoSign) 被处罚的情况 声明：以下内容是根据 参考 中的内容整理而成。不代表本站观点。\n沃通(WoSign)是国内市场占有率最高的 CA，上面两次谈到它被 Apple 处罚的情况。实际上，并非只有 Apple 处罚了沃通。\n2016 年 8 月 17 日，谷歌接到了 GitHub 安全团队的通告，称沃通在没有得到他们授权的情况下签发了一个 GitHub 的证书。谷歌于 10 月 31 日发布了 Chrome 56 的 Dev 渠道版本，决定从该版本的 Chrome 开始，不再信任沃通和 StartCom 于 2016 年 10 月 21 日之后签发的证书。\nMozilla(Firefox 的开发商)也对沃通进行过处罚。主要的原因是沃通故意通过倒填日期来规避 SHA-1 停用政策：CA:WoSign Issues 。在 Mozilla 披露的技术细节中显示，StartCom 已经开始使用沃通的基础架构来签发新的证书了。而且，StartCom 也和沃通一样在 2016 年采用了倒填日期的手段来签发 SHA-1 证书。Mozilla 的安全工程师也展示了这种违例的案例细节。\n10 月 7 日，沃通公布了它的 最新事故报告，CEO 王高华宣布辞职。\n在沃通的报告中，王高华称 2015 年 8 月沃通与以色列 CA 公司 StartCom 达成了收购协议，100% 收购了 StartCom。在报告中，沃通承认它在 2016 年 1 月 1 日之后签发了 64 个倒填日期的 SHA-1 证书，王高华表示承担错误的责任。\nStartSSL.com 是 StartCom 的网站。\n奇虎 360 持有沃通 84% 的股份。\n沃通的遭遇比起 CNNIC 还要好一点，CNNIC 的根证书都已经被各大浏览器和操作系统吊销 。连它自己的网站都在使用 DigiCert 颁发的证书。\n臆测 前段时间，有几位同事在 Mac 上无法访问微信企业号的二维码验证，可能与证书失效有关。\n我自己也碰到了在 macOS 上使用 Chrome/Safari 无法访问淘宝 、阿里云的问题，可能与证书失效有关。\n去哪些 CA 申请证书？ Comodo Positive SSL Certificate Rapid SSL Godaddy，大名鼎鼎的米国老牌域名提供商。zrong 的域名就在它家买的。 Let's Encrypt，一个免费的 SSL 证书提供机构。zrong 将会使用它的证书。 从最近卡梅隆大学的论文 Shedding Light on the Adoption of Let’s Encrypt 提到的数据可以看出，Let's encrypt 的确在推动 HTTPS 的普及。\nHTTPS 的资源占用 有人会担心网站全面切换到 HTTPS 后会占用大量的服务器资源用于加密和解密运算。知乎有篇文章 HTTPS 要比 HTTP 多用多少服务器资源 回答了这个问题。我这里就直接引用结论了：\n2010年1月 Gmail切换到完全使用 https， 前端处理 SSL 机器的CPU 负荷增加不超过1%，每个连接的内存消耗少于20KB，网络流量增加少于2%。由于 Gmail 应该是使用N台服务器分布式处理，所以CPU 负荷的数据并不具有太多的参考意义，每个连接内存消耗和网络流量数据有参考意义。\n参考内容 Distrusting New CNNIC Certificates Shedding Light on the Adoption of Let’s Encrypt， CA:WoSign Issues WoSign Incidents Report Update App Transport Security REQUIRED January 2017 Chrome/Safari 无法访问淘宝 NET::ERR_CERT_REVOKED 如何看待中国沃通wosign偷偷收购自己的根CA startcom并且签发github.com的证书？ 如何看待 CNNIC 官方网站的证书改换成了 DigiCert 签发的证书？ 在iOS 可信根证书列表中屏蔽其对中级CA WoSign(沃通) CA Free SSL Certificate G2 的信任 沃通 CEO 王高华辞职 Mozilla 将封杀沃通和 StartSSL 一年内新签发的所有证书 谷歌也不信任沃通的证书了，StartCom CA 一并受到同等处罚 HTTPS 要比 HTTP 多用多少服务器资源 iOS 7.1在线安装IPA失败以及数字证书 Google要求广告主必须在其网站部署SSL证书 （第一部分完）\n推荐继续阅读腾讯 Bugly 团队的这篇文章： 全站 HTTPS 来了 ，比本文更详细，知识点更多。\n","date":"2016-11-12","description":"","lastmod":"2016-11-14T03:14:54Z","slug":"about-https-1","tags":["http","encrypt"],"title":"HTTPS 小白知识（一）","url":"https://blog.zengrong.net/post/about-https-1/"},{"categories":["impressions"],"content":"\n一个同事聊天时问我，别的公司的技术说 1 天能接 1 个 SDK，我们为啥接那么慢？\n有个项目负责人找到我说：外面有个团队手上有个项目，是他们 1 个月开发出来的。据说效果还不错。我们现在开发这么慢，要不把他们的项目引进来？我说：你要是真想他们进来后还那么快，就必须保持团队完全独立，不要和公司的开发流程有联系。这个团队人员的考核和成长都要单独处理。\n上面的两位同事提出的问题，都和团队规模有关系。身处大团队，但眼睛盯着小团队，很容易产生这种能力不对称的感觉。\n普通员工无法理解也没关系，就像传说 1 天能接 1 个 SDK 的那位（想大叔我当年……），若有必要就解释一下技术细节，若无必要就呵呵一下呗。但如果 Manager 也这么想就有关系了。Manager 的这种想法，会影响其决策和一线技术人员的工作进度。\n我来试着解释一下这件事。\n无论小团队还是大团队，在工作中总会碰到这些方面的问题： 沟通方式、过程流转、绩效管理、员工成长、产品研发。下面这张图展示了小团队和大团队在这几个方面的区别。\n1digraph team { 2 graph[fontsize=30;rankdir=\u0026#34;TB\u0026#34;,label=\u0026#34;小团队和大团队\u0026#34;] 3 //ranksep=.75; 4 node[shape=rect] 5 6 7 subgraph cluster1 { 8 label = \u0026#34;小团队\u0026#34;; fontsize = 20; 9 style = \u0026#34;filled\u0026#34;; fillcolor=aliceblue;color=azure3; 10 node[shape=Mrecord,style=\u0026#34;filled\u0026#34;,fillcolor=\u0026#34;white\u0026#34;,color=azure3]; 11 edge[arrowhead=none, color=transparent] 12 13 c1s1[label=\u0026#34; 喊 | 大声喊 \u0026#34;] 14 c1s2[label=\u0026#34; 继续喊 | 人肉 \u0026#34;] 15 c1s3[label=\u0026#34; 没有/口头 | 脑补 | 半年/一年 \u0026#34;] 16 c1s4[label=\u0026#34; 没有/项目内成长 | 个人影响 \u0026#34;] 17 c1s5[label=\u0026#34; 一人搞定 \u0026#34;] 18 19 c1s1 -\u0026gt; c1s2 -\u0026gt; c1s3 -\u0026gt; c1s4 -\u0026gt; c1s5 20 } 21 subgraph cluster2 { 22 style = \u0026#34;invis\u0026#34;; 23 node[shape=none, fontsize=20]; 24 edge[arrowhead=none]; 25 26 c2s1[label=\u0026#34;沟通方式\u0026#34;] 27 c2s2[label=\u0026#34;过程流转\u0026#34;] 28 c2s3[label=\u0026#34;绩效管理\u0026#34;] 29 c2s4[label=\u0026#34;个人成长\u0026#34;] 30 c2s5[label=\u0026#34;产品研发\u0026#34;] 31 32 c2s1 -\u0026gt; c2s2 -\u0026gt; c2s3 -\u0026gt; c2s4 -\u0026gt; c2s5 33 } 34 35 subgraph cluster3 { 36 label = \u0026#34;大团队\u0026#34;; fontsize = 20; 37 style = \u0026#34;filled\u0026#34;; fillcolor=aliceblue; color=azure3; 38 node[shape=Mrecord,style=\u0026#34;filled\u0026#34;,fillcolor=\u0026#34;white\u0026#34;,color=azure3]; 39 edge[arrowhead=none, color=transparent] 40 41 c3s1[label=\u0026#34; 邮件 | 电话 \u0026#34;] 42 c3s2[label=\u0026#34; 邮件 | 会议 \u0026#34;] 43 c3s3[label=\u0026#34; 书面成体系 | 跨部门 | 每月/每季 \u0026#34;] 44 c3s4[label=\u0026#34; 阶梯/成体系 | 跨部门/跨业务 \u0026#34;] 45 c3s5[label=\u0026#34; 多人合作 \u0026#34;] 46 47 c3s1 -\u0026gt; c3s2 -\u0026gt; c3s3 -\u0026gt; c3s4 -\u0026gt; c3s5 48 } 49 50 subgraph cluster4 { 51 label = \u0026#34;成本\u0026#34;; fontsize = 20; 52 style = \u0026#34;filled, dashed\u0026#34;; fillcolor=gold1; color=goldenrod3; 53 node[shape=hexagon, style=\u0026#34;filled, dashed\u0026#34;, fillcolor=white,color=goldenrod3]; 54 waiting[label=\u0026#34;等待成本\u0026#34;]; 55 talking[label=\u0026#34;沟通成本\u0026#34;]; 56 } 57 58 { 59 c3s1 -\u0026gt; talking; 60 c3s2 -\u0026gt; talking; 61 c3s5 -\u0026gt; talking; 62 63 c3s2 -\u0026gt; waiting; 64 c3s3 -\u0026gt; waiting; 65 c3s4 -\u0026gt; waiting; 66 c3s5 -\u0026gt; waiting; 67 } 68 69 { 70 edge[style=dashed] 71 c2s1 -\u0026gt; c1s1 72 c2s2 -\u0026gt; c1s2 73 c2s3 -\u0026gt; c1s3 74 c2s4 -\u0026gt; c1s4 75 c2s5 -\u0026gt; c1s5 76 77 c2s1 -\u0026gt; c3s1 78 c2s2 -\u0026gt; c3s2 79 c2s3 -\u0026gt; c3s3 80 c2s4 -\u0026gt; c3s4 81 c2s5 -\u0026gt; c3s5 82 } 83 84} 该图采用 graphviz 绘制，可下载源码：\n1 文件 沟通方式，小团队靠喊，大团队靠邮件； 过程流转，小团队靠喊，大团队靠邮件、会议； 绩效考核，小团队不需要，大团队要专人来管理，所有人都要参与； 个人成长，小团队可以不考虑，大团队则要考虑体系性和阶梯性； 产品研发，小团队可以一个人搞定全栈，大团队则需要多人配合。 从上面可以看出，大团队需要多做的事，都是吃力不讨好的事。这些事增加了等待成本、同步成本和沟通成本。如果没有处理好这些成本，就会造成整个团队工作效率的严重下降。\n随便举几个例子吧。\n在小团队里，由于一个人负责多个职能，碰到过程流转的时候，他只需要跳起来喊一嗓子，另一个人就能把活儿接过去。由于小团队专注于一个项目，随便的一嗓子互相都知道啥意思，接下来专心把活儿干好就行。\n大团队就不行了。不同的职能由不同的人处理，碰到流转的时候，首先需要找到负责下一个流程的人，中间还极有可能会碰到需要某个流程外的人支持的情况。如果涉及的人多了，用邮件效率很低（因为要等其他人反馈），需要组织一个会议，会议的组织又会打断所有参会人的连续工作时间。一个流转，就降低了一批人的工作效率。\n在小团队里，绩效考核是不需要的。半年或者一年后出结果的时候，每个人做了什么事，大家一清二楚（因为平时工作靠喊，开会都是全员）。大团队里，某个人的工作情况不可能让所有人知道，为了在年底给出一个所有人信服的评判标准，绩效考核就必须做。定期的绩效考核会消耗员工的工作时间。填写、审核、打分、谈话等等流程，至少需要消耗0.5个工作日。重要岗位的人员消耗的时间更多（例如主程需要为自己项目组的成员评分）。一个考核，就降低了所有人的工作效率。\n产品研发上就更明显了。在小团队里，一个人就能完成整个产品的工作。例如我 花了 5 天时间就完成了一个内部工具开发 。产品设计、技术选型、美术、后端、前端都是我一个人完成，中间不需要等待，不需要流转，不需要沟通，效率高的惊人。如果把这个工具交给一个团队来做，就需要有需求调研、美术、前后端分工、测试等等一系列工作。等待成本和沟通成本又增加了多少呢？\n这些成本，是大团队不得不考虑的。\n张小龙在最近的一次演讲中谈到(via)：\n我们的记忆里面只适合处理150人以内的人际关系，一旦超过150人的时候，它就变成一个社会化的组织。这个时候对个体来说是不太舒适的，已经超过了他的舒适区。\n要让大团队做得比小团队更快更好，就是要让个体重新变得舒适起来。\n高效的大团队有什么好处呢？\n从腾讯回来的同事说，他们的游戏服务器受到攻击的时候，只需要通知公司的网络安全团队就行了。 只！需！要！通！知！就！行！了！\n我们开发了一套服务型 手机游戏开发框架，已经在 3 个游戏上进行了接入。这套框架可以完成游戏帐号接入、聊天系统、充值系统、SDK 接入、防沉迷系统、配置管理系统等等一系列的工作。开发一款新的游戏，只需要接入该框架的客户端和服务器 SDK 既可以选择性地使用框架提供的功能。这就是大团队的优势。\n在大团队中，每个人都能找到自己的位置。如果对当前的工作安排不满意，或者希望学习另一个岗位的技能技巧，完全可以内部调岗以得到更多的锻炼。如果员工愿意在某个岗位上持续研究，大团队也能提供足够的时间、资源和空间。这是一人一岗、快速出结果的小团队不可能提供的。\n只有团队中的每个成员都熟悉大团队的沟通流程和方式，互相尊重，互相配合，才可能发挥出大团队的优势。这个熟悉的过程，就是从小团队到大团队的成长阵痛，这是一个成熟的团队必须经历的过程。\n我在前几天的吐槽文：一只青蛙怎么收邮件？ 中提到了邮件培训。邮件的使用仅仅是大团队日常工作中的一个很小的细节，这样的细节在大团队的日常工作中还有许多。仅当这些细节为团队中每个成员都熟知的时候，整个团队的工作效率才可能得到质的飞跃。\n这需要团队所有人一起努力，尤其是需要 Manager 的认同。\n回到前面的例子，我们为什么不能 1 天接 1 个 SDK？我们当然能。小团队接完 1 个 SDK，直接就能上线，出了问题马上发新包。不会影响用户么？当然会影响！但没办法！因为只有1个人嘛！为什么快？因为只有1个人嘛！而我们要走测试流程，在SDK 这件事上起码涉及到3个岗位。每个岗位上的人马虎一点，整个流程就要报废了从头再来。\n我们为什么不能 1 个月开发一个游戏？我们当然能。我自己就干过类似的事情。在没有 大团队成本 的前提下，1 个月开发完那个游戏是不难的。这样开发出来的游戏包括主要玩法，但游戏运营工具、激活、支付、分析工具、公告、客服系统等等都是不完备甚至是没有的。它当然也能上线，甚至可能挣钱。它当然也“看起来不错”，但想要把这个游戏运营起来，还是需要一个团队持续跟进，持续完善支持的。这些支持，不能因为刚开始看不到，就认为不存在啊！后续的工作是不可避免的成本，小团队和大团队都避免不了。\n做产品，要专注在产品上，但不能只专注在产品上。要带好团队，产品和人同样重要。流程顺了，人的效率提高了，产品质量和开发速度就上来了。如果只把眼光放在面前的一小块儿上，和盲人摸象又有多大分别呢？\n（全文完）\n","date":"2016-11-07","description":"","lastmod":"2016-11-07T16:00:19Z","slug":"small-and-big-team","tags":["management"],"title":"小团队和大团队","url":"https://blog.zengrong.net/post/small-and-big-team/"},{"categories":["impressions"],"content":"\n关于工作邮件，思考很久了，今天就写一点儿。\n先把造成这些思考的事例列出来（加粗的是我的吐槽）：\n思考 我建立了一个 网站开发学习组 ，要求组内同学每天发日报报告学习进度，我每天查看日报做指导，每周开一次学习会。有位刚来没多久的同学一直没有发日报。问之，答曰：现在太忙，没有时间看邮件。你比 Hillary Clinton 还忙？人家还在 FBI 眼皮下面删除了 3 万封邮件呢！ 一次人肉追进度，某同事当我的面打开邮箱，我们一起等了 3 分钟等待新邮件同步，因为邮箱很久没开了。 我看着 Outlook 的收件箱中未读数字不断增加，心中万马奔腾。 某次会议上该同事抱怨自己的手机收不到邮件，我就帮他配置了一下手机邮箱，发现最后一封邮件是7月的。重新配置邮箱只需要几分钟时间，而这个问题困扰了他 4 个月。我本来以为这是特例，但事实证明我还是 Too Young 。更让我难以相信的是，这样的问题在 Manager 身上也不少。 我经常收到代发的请假申请。问本人为何不自己用手机邮箱发邮件？回答有几种类型： 1. 手机上没配置。2. 手机上不好发。3. 不记得申请邮件的格式。4. 不知道可以用手机发邮件。 收到了一封关于充值失败的报告邮件。看了一下收件人和 CC，居然没有必须关注充值流程的关键人员。问之，答曰：觉得好像不用抄送给 TA。 一封时间非常紧迫的邮件抄送给了许多人，没人继续回复，感觉时间停止了…… 无论是收件人还是发件人似乎都在等待对方做决策，而整个流程还有很多疑点。我跟进之，发现其中两方的工作都在进行，但有不对称信息，继续下去得到的结果会有问题。问之，答曰：觉得事情的进度不需要汇报，等到有结果再汇报就好了。 我还能举出很多类似的事例。每件事例的细节多有不同，但造成这样的情形的结果却可以归纳为如下几类：\n不重视\n有些是由于散漫惯了，不习惯这种收发邮件的管理方式。有些是由于之前的工作经验中从来没有使用过工作邮件，认为这不重要。 懒/鸵鸟政策\n觉得邮件里面肯定好多事情，我不开邮箱这些事情就和我无关了，就不会影响我单纯的工作了。 我最重要\n我的工作方法是最好的，我要完成自己的工作，邮件来打扰我是怎么个情况？ No best practice\n不知道如何使用。不知道工作邮件的规则和方法。这就和传统公司中不知道如何打电话，不知道如何写公文是一样的。 说难听点，可以归结为： 自大、无知、懒惰、不学习。\n坐井观天 让我们看一篇小学二年级课文：\n一只青蛙坐在井里，一只小鸟飞来，落在井沿上。\n青蛙问小鸟：“你从哪儿飞来呀？”\n小鸟回答说：“我从远处飞来。我在天空中飞了一百多里，口渴了，下来找点水喝。”\n青蛙说：“朋友，别说大话了！天不过井口那么大，还用飞那么远吗？”\n小鸟说：“你弄错了，天无边无际，大得很哪！”\n青蛙笑了，说：“朋友，我天天坐在井里，一抬头就看见天。我不会弄错的。”\n小鸟也笑了，说：“朋友，你是弄错了。不相信，你跳出井口来看一看吧。 ”\n这个成语开始是说，一只在井底的青蛙，一直以为天只有井口那么大，一天他被一只鸟带出了井，才发现天不只井口大。意思就说，不要只用自己的方式想问题，否则会被局限，也可以说是没见识。\n这只青蛙出了井口之后估计会很郁闷：我是没法跳出去啊，为啥要讽刺我？\n我想说的是：没有能力跳和不想跳，真的是两件事啊。\n如果青蛙兄弟放弃成见，每天在井里跳两下，可能就发现这口井并不简单。即使最终没有跳出井口，至少也能发现一些不同的方法，得到一些不同的启示。\n我的例子 只吐槽不给解决方案，不是无能就是傻。所以就说说我的例子。\n我每天平均收到邮件 200+，但我能保证花在邮件上的时间不超过1个小时。怎么做的？\n我深受邮件困扰。最多的时候，我每天能收到 300+ 邮件，回复 50+ 邮件。在这些邮件中，要做到 表述完整，目标明确，用语合理，格式美观，不写（少写）错别字 ，会耗费大量的时间。我现在能相对轻松地处理邮件，主要是做到了这几点：熟悉工具、分类法、优先级、快速阅读。\n熟悉工具 人类发明工具，其实是为了偷懒。一款工具只要能提升一点点效率，都值得去尝试。我尝试过许多邮件客户端，甚至丧心病狂地研究了 Mutt ，最后回归了 Outlook 。它并不是最好的工具，只是目前环境下最合理的工具。手机上我直接使用手机自带的邮件 App ，好处就是工作日历可以同步进入手机日历。\n分类法 工具有自己的规则。邮件是一种工具，因此也有规则。邮件的规则就是邮件分类法。\n所有的邮箱客户端都有自定义规则功能，你可以添加自定义规则让拥有不同标题或内容的邮件进入不同的邮箱。这种自动分类的过程可以帮助你减轻在看到收件箱中的几百封未读邮件时的痛苦。由于不同邮箱在你心中的优先级是不同的，你可以可以更加合理地安排自己的阅读时间。有了邮件分类，在查询邮件的时候会更加轻松。\n有些邮件不是你发出的，因此主题并不符合你的规则。你可以在回复邮件的时候加入使自己规则生效的关键字。\n无论你每天处理多少次邮箱，都要记住：保持你的 INBOX 为空。\n优先级 除了邮件自带的优先级功能，我还有一套优先级规则：\n需要我直接操办的。一般是收件人是我的。这种邮件最少。 需要我配合推动的。一般是抄送人中包含我的。这种邮件较多。 非上面两种情况的。这种情况最多。在这种情况中，我就是一个吃瓜群众。我的邮箱包含在所有的业务组中，因此能收到所有业务相关邮件。我要保证对这些邮件拥有足够的阅读率（快速看懂邮件内容），又需要在这些邮件中发现别人遗漏的问题。作为一个专业的吃瓜群众，需要有主人翁的意识和觉悟，想主人翁之所想，急主人翁之所急，甚至要有能力发现主人翁的弦外之音。 这三类不同优先级的邮件，处理的时间、方式都是不同的。第一类和第二类邮件我需要把任务安排到 TODO List 中去，按预定的计划去执行。第三类则比较灵活，我一般的处理方式是找到那个真正负责的人，然后持续跟进。邮件发完之后，就将其立刻分类（参见分类法），然后等待下一封该邮件的后续邮件即可。\n快速阅读 作为现代人，阅读是必要的技能。一个普通人，每天阅读 30 分钟，1 年就能读完 50 本书。大多数邮件的内容都比书更加易读，我们说邮件太多没有时间读，只是一种借口而已。这就和我们说工作太忙没有时间读书这个借口一样烂。\n先给自己定一个小目标，1 个月读完 4 本书。快速阅读的能力自然而然就建立起来了。\n然而就算做到了 表述完整，目标明确，用语合理，格式美观，不写（少写）错别字 ，也没有什么卵用。因为有的人根本就 ！不！看！邮！件！\n改进 我们处在互联网行业，工作邮件本身已经是非常普通（甚至有些过时）的工作方式，对这种方式不熟悉，不了解，又不能主动观察和学习，是我们的问题。\n怎么改进这些问题？依靠个人的自觉性是不够的，公司的培训方面也是有一部分责任。从公司层面，需要做这样几件事：\n在入职培训时培养 Outlook 使用方法、讲解邮件写作规范和技巧。由 HR 执行。 在入职培训时强调邮件的重要性，要求必须在移动设备中配置好企业邮箱，并培训：1. 如何使用手机发邮件；2. 如何在个人时间关闭企业邮箱通知。 由 HR 执行。 入职后持续保证工作邮件的及时反馈，对不正常使用工作邮件办公的员工提出批评。由 PeopleManager 执行。 定期组织邮件使用培训，对工作中发现的邮件使用不合理的情况作出反馈。由 HR 执行。 开发更合理的工具，在某些特定任务上代替邮件的功能。例如：考勤模块和全员通知系统可以使用企业微信来开发。 回到标题提出的问题，一只青蛙怎么收邮件呢？\n答：先要帮 TA 从井里跳出来。\n（全文完）\n","date":"2016-11-06","description":"","lastmod":"2016-11-06T04:22:16Z","slug":"how-does-a-frog-get-a-e-mail","tags":["management"],"title":"一只青蛙怎么收邮件？","url":"https://blog.zengrong.net/post/how-does-a-frog-get-a-e-mail/"},{"categories":["technology"],"content":"在我写的 KPI 工具中 需要做 MSAD 账号集成，网上找到的资料有点混乱，我把最重要的信息整理在这里，保证一看就懂。 :)\nLDAP 协议是 Lightweight Directory Access Protocol 的简称。它是一个通用协议，并不依赖于特定平台。\nMSAD(Microsoft Active Directory) 是微软服务器操作系统中基于 LDAP 协议的具体实现。\nLDAP 是一个类似于 Unix 目录树的结构。对于 Unix 目录树来说，项目可以是文件或者目录，文件项不能再包含下级目录。而 LDAP 则没有这个限制。\n先说几个经常出现的缩写词的含义：\nDN = Distinguished Name 唯一名称 CN = Common Name 通用名 UID = User ID OU = Organization Unit 组织单元 DC = Domain Componen O = Organization 组织 C = Country 国家名 LDAP 中最基本的信息单元是 Entry ，用 DN 可以标识一个条目的位置，从名称可以看出，DN 是唯一的。我们需要用 DN 指定一个确定的条目。\n我司的 LDAP 服务器根目录的 DN 是： DC=baina, DC=com ，这句话就和 Unix 的根目录 / 的含义一致。LDAP 协议规定还可以用 O 和 C 来指定一个 DN。例如根目录的 DN 也可以写成： O=\u0026quot;baina, Inc.\u0026quot;,C=CN 或者 O=baina.com。但 MSAD 限制必须使用 DC 的这种写法，所以我只记住第一种就好。\n在我司的 LDAP 服务中，我的信息也是一个 Entry，我的 DN 是这样的：\n1CN=\u0026#34;Rong Zeng\u0026#34;, OU=Game, OU=Wuhan, OU=FTE, OU=Mobile, DC=baina, DC=com 很好懂吧？通过 CN 和 OU 就把我进行了分类。可以看出我是 Wuhan 分公司、Game 部门、移动部门、正式员工。\n换成 Unix 目录树的表达方式，可能是这样的： /wuhan/game/fte/rong zeng ，但是 Unix 目录树无法处理并列关系。wuhan、game、fte 这三个 OU 并不是层级关系，而是并列关系。\n一个 Entry 可以包含多个 OU，也可以包含多个 CN。可以用 UID 代替 CN 来表示名字。我司的服务器并没有使用 UID，就不研究了。\n一个 Entry 可以包含多个 Attribute 。每个 Entry 中含有哪些 Attribute 是由 object class 决定的。object class 和 OU 不同，OU 纯粹用于分类，而 object class 则规定了每个条目必须或可选的属性。每个 Entry 可以属于多个 object class。\n常用的 object class 有下面几个：\ntop 所有 class 的基类 person organizationalPerson 继承 person organization organizationalRole organizationalUnit 例如 person 要求 CN 和 SN(Surename) 不能为空，并允许 givenname、telephonenumber 等属性为可选。organizationalPerson 则增加了更多可选的属性。\n当然，自定义 object class 也是允许的。\n最重要的概念都介绍完了，下面就直入主题。\n我的网站使用 Flask 写成，因此需要一个 Python 的 LDAP 支持库，有两个库被广为使用：\npython-ldap python-ldap provides an object-oriented API to access LDAP directory servers from Python programs. Mainly it wraps the OpenLDAP 2.x libs for that purpose. Additionally the package contains modules for other LDAP-related stuff. ldap3 A strictly RFC 4510 conforming LDAP V3 pure Python client library. The same codebase works with Python 2, Python 3, PyPy and PyPy3 python-ldap 主要是 C 实现的，仅支持 Python2 。ldap3 则为纯 Python 实现，不仅支持 Python3， 还支持 PyPy。\n下面是一段使用 ladap3 在我司的 MSAD 服务器上实现鉴权的代码：\n1import ldap3 2 3def authorize(host=None, port=None, user=None, password=None): 4 if not host: 5 return False, \u0026#39;no host\u0026#39; 6 server = ldap3.Server(host, port, get_info=ldap3.ALL) 7 conn = None 8 auto_bind = False 9 try: 10 if user: 11 user = \u0026#39;baina\\\\%s\u0026#39;%user 12 if password: 13 auto_bind = True 14 conn = ldap3.Connection(server, user=user, password=password, auto_bind=auto_bind, authentication=ldap3.NTLM) 15 if not auto_bind: 16 succ = conn.bind() 17 else: 18 succ = True 19 msg = conn.result 20 conn.unbind() 21 return succ, msg 22 except Exception as e: 23 if conn: 24 conn.unbind() 25 return False, e 主要的参考内容如下：\n0 文件 设置 Microsoft Active Directory 的 LDAP 参数 利用 LDAP 查询收集同步数据 LDAP 入门文档 RFC 2252 ldap3 Tutorial （全文完）\n","date":"2016-11-01","description":"","lastmod":"2016-11-06T14:52:54Z","slug":"ldap-introduce","tags":["server","protocol","python"],"title":"实现 LDAP 验证登录","url":"https://blog.zengrong.net/post/ldap-introduce/"},{"categories":["technology"],"content":"十一期间，我花了大概 5 天时间（每天3-4小时），用 Flask 开发了一个内部工具用来管理团队 KPI 。这个工具会交给其他同学继续开发，为了方便交流，我用几篇文章来描述一下如何从零开始快速上手 Flask 的开发流程。我会提供包括参考资料，书籍链接，学习顺序和方法，最佳实践等内容。\n注意： 这里的“从零开始”并非毫无开发经验，这个零代表：有一定的IT技术基础，使用过脚本语言，但从未或很少接触 Web 开发。\n[TOC]\nFlask 简介 Flask 非常适合用来快速开发一些小型工具。它也是目前上手最快的 Python Web 开发框架。基于 Python 的著名 Web 开发框架还有 django 。Flask、Django、Pyramid三个框架的对比 介绍了几个主流 Python Web 开发框架的区别。\nFlask 的技术栈 Flask 的依赖非常少，只依赖一套模版系统 Jinja2 和 WSGI 工具库 Werkzeug 。其它的内容就需要自己选择了。我的选择如下：\nPython Python3\n具体的学习方法请看 Python 入门建议 。该文中提到了如何学习 Python，看什么书，学习顺序以及学习工具。\n数据库 MySQL\n推荐书籍：\n《MySQL 必知必会》 《SQL 必知必会》 这两本薄书的作者是同一个人，内容差别也不大，可以一起读。之所以读了 MySQL 还要读 SQL，是因为后者强调了各种不同数据库支持的 SQL 语言的异同，值得了解一下。\nMySQL 的客户端库使用 PyMySQL 。PyMySQL 不需要学习，因为我选择使用 SQLAlchemy 。\n我使用 SQLAlchemy 实际操作数据库。 SQLAlchemy 是一种 ORM ，可以理解成基于编程语言的虚拟数据库。使用 SQLAlchemy ，就不必编写实际的 SQL 语句，直接使用 Python 对象语法就能完成查询，能同时支持多种不同的数据库。\n学习 SQLAlchemy 最好的办法就是阅读 官方文档（英文） ，我还没有发现很好的中文文档。\n前端 Bootstrap （待续）\njQuery （待续）\n学习顺序 学习 Python，见 Python入门建议 ; 学习 Flask，阅读 《Flask Web 开发》 ，该书中包含 Jinja2 和 SQLAlchemy 的入门教程； 在开发过程中持续学习 SQLAlchemy 和 Jinja2/Werkzeug ； （未完待续）\n","date":"2016-11-01","description":"","lastmod":"2016-11-01T04:42:11Z","slug":"how-to-build-a-flask-website","tags":["python","flask"],"title":"开发一个 Flask 网站：基础知识","url":"https://blog.zengrong.net/post/how-to-build-a-flask-website/"},{"categories":["technology"],"content":"\n由于 苹果公司不再信任根证书签发机构 WoSign ，我在 macOS Sierra 上无法访问淘宝和阿里云了。\n访问的提示如下：\n您的连接不是私密连接\n攻击者可能会试图从www.taobao.com窃取您的信息（例如：密码、通讯内容或信用卡信息）。 NET::ERR_CERT_REVOKED\nwww.taobao.com 通常会使用加密技术来保护您的信息。Google Chrome 此次尝试连接到 www.taobao.com 时，此网站发回了异常的错误凭据。这可能是因为有攻击者在试图冒充 www.taobao.com，或 Wi-Fi 登录屏幕中断了此次连接。请放心，您的信息仍然是安全的，因为 Google Chrome 尚未进行任何数据交换便停止了连接。\n您目前无法访问www.taobao.com，因为此证书已被撤消。网络错误和攻击通常是暂时的，因此，此网页稍后可能会恢复正常。\nChrome 好歹给个提示，而 Safari 就是直接无法访问啊，啥提示都没有。Firefox 还好，能正常访问，但提示网站不安全。\n翻了下知乎，看来 WoSign 的名声不怎么好啊 。\n最简单的解决方案当然是无条件信任这些已经撤销的证书，但这点就违背了安全证书的本意了。\n有网友说可以等待数字证书缓存更新，但我等了好几天也没更新好么。\nstackoverflow 中提到了解决方案：NET::ERR_CERT_REVOKED in Chrome, when the certificate is not actually revoked 。为了方便，我截了个图上来。\n可是依然没有什么卵用。\n对我有用的解决方案是 苹果论坛上提供 的，在命令行中输入：\n1sqlite3 ~/Library/Keychains/*/ocspcache.sqlite3 \u0026#39;DELETE FROM responses WHERE responderURI LIKE \u0026#34;%http://%.globalsign.com/%\u0026#34;;\u0026#39; （全文完）\n","date":"2016-10-29","description":"","lastmod":"2016-10-29T14:44:34Z","slug":"chrome-safari-err-cert-revoked","tags":["browser"],"title":"Chrome/Safari 无法访问淘宝 NET::ERR_CERT_REVOKED","url":"https://blog.zengrong.net/post/chrome-safari-err-cert-revoked/"},{"categories":["technology"],"content":"2017-03-01： 加入获取模块内部的方法。\n今天有个需求要得到一个类的静态属性，也就是说有个类 Type ，我要动态获取 Type.FTE 这个属性的值。\n最简单的方案有两个：\ngetattr(Type, 'FTE') Type.__dict__['FTE'] 那么，如果要获取类属性的列表，该怎么做呢？\n首先上场的是 dir ，它能返回当前范围的所有属性名称列表：\n1\u0026gt;\u0026gt;\u0026gt; dir() 2[\u0026#39;__builtins__\u0026#39;, \u0026#39;__doc__\u0026#39;, \u0026#39;__name__\u0026#39;, \u0026#39;__package__\u0026#39;] 3\u0026gt;\u0026gt;\u0026gt; dir(list) 4[\u0026#39;__add__\u0026#39;, \u0026#39;__class__\u0026#39;, \u0026#39;__contains__\u0026#39;, \u0026#39;__delattr__\u0026#39;, \u0026#39;__delitem__\u0026#39;, \u0026#39;__delslice__\u0026#39;, \u0026#39;__doc__\u0026#39;, \u0026#39;__eq__\u0026#39;, \u0026#39;__format__\u0026#39;, \u0026#39;__ge__\u0026#39;, \u0026#39;__getattribute__\u0026#39;, \u0026#39;__getitem__\u0026#39;, \u0026#39;__getslice__\u0026#39;, \u0026#39;__gt__\u0026#39;, \u0026#39;__hash__\u0026#39;, \u0026#39;__iadd__\u0026#39;, \u0026#39;__imul__\u0026#39;, \u0026#39;__init__\u0026#39;, \u0026#39;__iter__\u0026#39;, \u0026#39;__le__\u0026#39;, \u0026#39;__len__\u0026#39;, \u0026#39;__lt__\u0026#39;, \u0026#39;__mul__\u0026#39;, \u0026#39;__ne__\u0026#39;, \u0026#39;__new__\u0026#39;, \u0026#39;__reduce__\u0026#39;, \u0026#39;__reduce_ex__\u0026#39;, \u0026#39;__repr__\u0026#39;, \u0026#39;__reversed__\u0026#39;, \u0026#39;__rmul__\u0026#39;, \u0026#39;__setattr__\u0026#39;, \u0026#39;__setitem__\u0026#39;, \u0026#39;__setslice__\u0026#39;, \u0026#39;__sizeof__\u0026#39;, \u0026#39;__str__\u0026#39;, \u0026#39;__subclasshook__\u0026#39;, \u0026#39;append\u0026#39;, \u0026#39;count\u0026#39;, \u0026#39;extend\u0026#39;, \u0026#39;index\u0026#39;, \u0026#39;insert\u0026#39;, \u0026#39;pop\u0026#39;, \u0026#39;remove\u0026#39;, \u0026#39;reverse\u0026#39;, \u0026#39;sort\u0026#39;] 可以配合使用 inspect 包中的功能来过滤：\n1 2\u0026gt;\u0026gt;\u0026gt; [i for i in dir(list) if inspect.isbuiltin(getattr(list, i))] 3[\u0026#39;__new__\u0026#39;, \u0026#39;__subclasshook__\u0026#39;] inspect 包中还包含：\n1\u0026gt;\u0026gt;\u0026gt; [i for i in dir(inspect) if inspect.isfunction(getattr(inspect, i))] 2[\u0026#39;_searchbases\u0026#39;, \u0026#39;classify_class_attrs\u0026#39;, \u0026#39;cleandoc\u0026#39;, \u0026#39;findsource\u0026#39;, \u0026#39;formatargspec\u0026#39;, \u0026#39;formatargvalues\u0026#39;, \u0026#39;getabsfile\u0026#39;, \u0026#39;getargs\u0026#39;, \u0026#39;getargspec\u0026#39;, \u0026#39;getargvalues\u0026#39;, \u0026#39;getblock\u0026#39;, \u0026#39;getcallargs\u0026#39;, \u0026#39;getclasstree\u0026#39;, \u0026#39;getcomments\u0026#39;, \u0026#39;getdoc\u0026#39;, \u0026#39;getfile\u0026#39;, \u0026#39;getframeinfo\u0026#39;, \u0026#39;getinnerframes\u0026#39;, \u0026#39;getlineno\u0026#39;, \u0026#39;getmembers\u0026#39;, \u0026#39;getmodule\u0026#39;, \u0026#39;getmoduleinfo\u0026#39;, \u0026#39;getmodulename\u0026#39;, \u0026#39;getmro\u0026#39;, \u0026#39;getouterframes\u0026#39;, \u0026#39;getsource\u0026#39;, \u0026#39;getsourcefile\u0026#39;, \u0026#39;getsourcelines\u0026#39;, \u0026#39;indentsize\u0026#39;, \u0026#39;isabstract\u0026#39;, \u0026#39;isbuiltin\u0026#39;, \u0026#39;isclass\u0026#39;, \u0026#39;iscode\u0026#39;, \u0026#39;isdatadescriptor\u0026#39;, \u0026#39;isframe\u0026#39;, \u0026#39;isfunction\u0026#39;, \u0026#39;isgenerator\u0026#39;, \u0026#39;isgeneratorfunction\u0026#39;, \u0026#39;isgetsetdescriptor\u0026#39;, \u0026#39;ismemberdescriptor\u0026#39;, \u0026#39;ismethod\u0026#39;, \u0026#39;ismethoddescriptor\u0026#39;, \u0026#39;ismodule\u0026#39;, \u0026#39;isroutine\u0026#39;, \u0026#39;istraceback\u0026#39;, \u0026#39;joinseq\u0026#39;, \u0026#39;namedtuple\u0026#39;, \u0026#39;stack\u0026#39;, \u0026#39;strseq\u0026#39;, \u0026#39;trace\u0026#39;, \u0026#39;walktree\u0026#39;] 还可以配合 callable 来使用：\n1\u0026gt;\u0026gt;\u0026gt; [i for i in dir(inspect) if not callable(getattr(inspect, i))] 2[\u0026#39;CO_GENERATOR\u0026#39;, \u0026#39;CO_NESTED\u0026#39;, \u0026#39;CO_NEWLOCALS\u0026#39;, \u0026#39;CO_NOFREE\u0026#39;, \u0026#39;CO_OPTIMIZED\u0026#39;, \u0026#39;CO_VARARGS\u0026#39;, \u0026#39;CO_VARKEYWORDS\u0026#39;, \u0026#39;TPFLAGS_IS_ABSTRACT\u0026#39;, \u0026#39;__author__\u0026#39;, \u0026#39;__builtins__\u0026#39;, \u0026#39;__date__\u0026#39;, \u0026#39;__doc__\u0026#39;, \u0026#39;__file__\u0026#39;, \u0026#39;__name__\u0026#39;, \u0026#39;__package__\u0026#39;, \u0026#39;_filesbymodname\u0026#39;, \u0026#39;dis\u0026#39;, \u0026#39;imp\u0026#39;, \u0026#39;linecache\u0026#39;, \u0026#39;modulesbyfile\u0026#39;, \u0026#39;os\u0026#39;, \u0026#39;re\u0026#39;, \u0026#39;string\u0026#39;, \u0026#39;sys\u0026#39;, \u0026#39;tokenize\u0026#39;, \u0026#39;types\u0026#39;] 上面提到了 __dict__ ，也可以用它来获取属性列表：\n1\u0026gt;\u0026gt;\u0026gt; list.__dict__.keys() 2[\u0026#39;__getslice__\u0026#39;, \u0026#39;__getattribute__\u0026#39;, \u0026#39;pop\u0026#39;, \u0026#39;remove\u0026#39;, \u0026#39;__rmul__\u0026#39;, \u0026#39;__lt__\u0026#39;, \u0026#39;__sizeof__\u0026#39;, \u0026#39;__init__\u0026#39;, \u0026#39;count\u0026#39;, \u0026#39;index\u0026#39;, \u0026#39;__delslice__\u0026#39;, \u0026#39;__new__\u0026#39;, \u0026#39;__contains__\u0026#39;, \u0026#39;append\u0026#39;, \u0026#39;__doc__\u0026#39;, \u0026#39;__len__\u0026#39;, \u0026#39;__mul__\u0026#39;, \u0026#39;sort\u0026#39;, \u0026#39;__ne__\u0026#39;, \u0026#39;__getitem__\u0026#39;, \u0026#39;insert\u0026#39;, \u0026#39;__setitem__\u0026#39;, \u0026#39;__add__\u0026#39;, \u0026#39;__gt__\u0026#39;, \u0026#39;__eq__\u0026#39;, \u0026#39;reverse\u0026#39;, \u0026#39;extend\u0026#39;, \u0026#39;__delitem__\u0026#39;, \u0026#39;__reversed__\u0026#39;, \u0026#39;__imul__\u0026#39;, \u0026#39;__setslice__\u0026#39;, \u0026#39;__iter__\u0026#39;, \u0026#39;__iadd__\u0026#39;, \u0026#39;__le__\u0026#39;, \u0026#39;__repr__\u0026#39;, \u0026#39;__hash__\u0026#39;, \u0026#39;__ge__\u0026#39;] 那么，如何获取当前模块的属性呢？\n在一个模块内部，可以使用下面的方法获取模块对象本身。\n1\u0026gt;\u0026gt;\u0026gt; import sys 2\u0026gt;\u0026gt;\u0026gt; sys.modules[__name__] 3\u0026lt;module \u0026#39;__main__\u0026#39; (built-in)\u0026gt; 然后就可以使用 getattr 来获取模块中的信息了。\n除此以外，还可以使用 globals() 来获取已经导入的所有模块。\n在一个模块内部，也可以使用 vars() 或者 locales() 来获取模块对象。\n（全文完）\n","date":"2016-10-07","description":"","lastmod":"2017-03-01T13:05:00Z","slug":"get-class-attributes-in-python","tags":["python"],"title":"在 Python 中得到类属性的列表","url":"https://blog.zengrong.net/post/get-class-attributes-in-python/"},{"categories":["technology"],"content":" 2016-09-26更新： HHKB支持。 2016-10-04更新： 蓝牙鼠标支持。 2016-10-10 更新： macOS Sierra 兼容软件列表。 今天手贱升级了 macOS Sierra ，进入新系统后发现 Karabiner 不能用了。\nKarabiner 用来映射按键，我在 OS X 技巧与软件选择 一文中提到过。对于我这种使用 HHKB 键位的小众人群来说，所有的按键都必须一致，否则效率奇低。使用 Karabiner 把我常用的键盘定义存成 Profile 就行了。\n但由于 macOS Sierra 大幅修改了鼠标和键盘驱动，导致 Karabiner 在该系统下无法工作。我定义的所有 Profile 都失效了。\nKarabiner 作者也在官网提到了：\nmacOS Sierra support status\nKarabiner does not work on macOS Sierra at the moment.\nWe are developing Karabiner-Elements which provides simple key modification for macOS Sierra at first. (Karabiner-Elements works on macOS Sierra except prefernces GUI.)\nWe'll start updating for the full featured Karabiner for Sierra after Karabiner-Elements is completed.\nhttps://github.com/tekezo/Karabiner-Elements\n目前 Karabiner-Elements 还没有较好的 GUI 界面，需要直接修改配置文件。\n因为这是个不完善的版本，使用方法随时可能修改，我就不写使用方法说明了，直接看文档：\nhttps://github.com/tekezo/Karabiner-Elements/blob/master/usage/README.md\n贴上我的配置文件：\n1{ 2 \u0026#34;profiles\u0026#34;:[ 3 { 4 \u0026#34;name\u0026#34;:\u0026#34;Internal\u0026#34;, 5 \u0026#34;selected\u0026#34;:true, 6 \u0026#34;simple_modifications\u0026#34;:{ 7 \u0026#34;backslash\u0026#34;:\u0026#34;delete_or_backspace\u0026#34;, 8 \u0026#34;delete_or_backspace\u0026#34;:\u0026#34;backslash\u0026#34; 9 } 10 }, 11 { 12 \u0026#34;name\u0026#34;:\u0026#34;HHKB\u0026#34;, 13 \u0026#34;selected\u0026#34;:false, 14 \u0026#34;simple_modifications\u0026#34;:{ 15 } 16 } 17 ] 18} 我的配置文件中，Internal 配置（MBP 内置键盘）中，把删除键和反斜杠按键进行了调换。因为我常用键盘是 HHKB Pro2 ，这样切换是为了保证键位和 HHKB 一致。\n注意删除键的名称并不是 delete ，而是 delete_or_backspace 。这个在官方给出的文档中也是错的。\n反斜杠按键的名称是 backslash 。\n更多的按键名称可以看源码 types.hpp 。\n我的配置文件中，HHKB 这个配置中什么都没有填写，是为了还原原始的配置。如果两个配置的 selected 值都为 true，则会以最上面的一个为准（因为最先碰到这个 true）。\n悲催的是，当我打开 Karabiner-Elements 之后，按键切换是正常了，但蓝牙鼠标立马不能用了。\n希望我的 HHKB 还能用……明天去公司试试。\n2016-09-26 更新：\n在开启和关闭 Karabiner-Elements 时，HHKB Pro2 一切如常。Apple Magic Trackpad 也一切正常。看来只有蓝牙鼠标有问题了。目前的解决方案只能是：在使用 Karabiner-Elements 的时候，放弃使用蓝牙鼠标。\n2016-10-04 更新：\n升级到 0.90.46 版本之后，Karabiner-Elements 已经不会影响我的蓝牙鼠标的使用。如果已经连接了鼠标，开启 Karabiner-Elements 之后鼠标失效，那么可以关闭鼠标然后重新开启，鼠标再次生效后就可使用了。\n但我又发现了另一个问题，在开启 Karabiner-Elements 之后，操作系统中映射的“修饰键”无效了。例如，我习惯把 Caps Lock 键映射成为 Control 键，但开启 Karabiner-Elements 之后，Caps Lock 按键上的指示灯又会亮起，功能还原了。即使我在 Bluetooth Mouse 中再次进行映射也无济于事（如下图）。\n解决这种情况，需要在配置文件中加入一行映射：\n1 \u0026#34;simple_modifications\u0026#34;:{ 2 \u0026#34;backslash\u0026#34;:\u0026#34;delete_or_backspace\u0026#34;, 3 \u0026#34;delete_or_backspace\u0026#34;:\u0026#34;backslash\u0026#34;, 4 \u0026#34;caps_lock\u0026#34;:\u0026#34;left_control\u0026#34; 5 } 2016-10-10 更新：\nP.S. 从这段时间的使用看来，并不是仅有 Karabiner 不能使用。在我的机器上， MplayerX 和 GIMP 都不能使用了。这里有一个列表，显示 macOS Sierra 的软件兼容情况： macOS Sierra 10.12: Compatible Apps 。\n（全文完）\n","date":"2016-09-24","description":"","lastmod":"2016-10-04T09:57:33Z","slug":"karabiner-in-macos-sierra","tags":["osx","hardware"],"title":"Karabiner 在 macOS Sierra 下的解决方案","url":"https://blog.zengrong.net/post/karabiner-in-macos-sierra/"},{"categories":["technology"],"content":"2014年底，我在开发给策划使用的游戏工具时，曾短暂地研究了几个月 Qt，当时感觉用 Qt 来写桌面程序还是挺方便的，生成的界面也比 Swing 要漂亮许多， QML 则让 Qt 的易用性得到了进一步增强。\n不过这两年来，Javascript 如日中天，开源世界基于 Chrominum 创造了类似 Chrome App 的桌面应用开发工具，可以使用 Javascript+CSS+HTML 来开发桌面程序。除了安装包大一点之外，看起来似乎还不赖。Atom 和 Visual Studio Code 都是使用这种方式开发的。\n有两套不相上下的框架可以做这件事：Electron 和 NW.js 。\n关于这两个框架的讨论很多，下面就是几个：\nElectron vs nwjs Electron vs nwjs (part 2) NW.js and Electron compared Electron 官方文档中也有和 NW.js 的技术比较，虽然看完之后觉得并没有什么卵用。\n根据目前网上找到的资料和我自己的分析来看，Electron 似乎更受欢迎，理由有如下几个：\n更小的安装包和更快的启动速度； 更好的文档，文档更新很快，还有 本地语言翻译 ； Bug 修复快，NW.js 的 issue 现在（今天）是4位数，而 Electron 只有3位数； 来自 Github 的支持； 重要产品的采用（Atom 和 Visual Studio Code）； 2011年诞生的 NW.js 目前 Star 的数量已经全面落后于 2014 年诞生的 Electron，而在2016年初，NW.js 还是领先的。 以上种种都说明了 Electron 社区更加活跃。当然，也有许多开发者是两者一起用。\n看起来 Electron 是个不错的选择。\n不过，Adobe Brackets 是用什么框架开发的？难道又是自己撸了一套么？\n心疼 AIR ……\n（全文完）\n","date":"2016-09-15","description":"","lastmod":"2016-09-15T05:37:20Z","slug":"choose-a-gui-framework","tags":["gui","qt","nodejs","javascript","browser","choice"],"title":"桌面软件GUI开发框架","url":"https://blog.zengrong.net/post/choose-a-gui-framework/"},{"categories":["impressions"],"content":"\n书籍信息 当我们阅读时，我们看到了什么 WHAT WE SEE WHEN WE READ 作者: [美] 彼得·门德尔桑德 出版社: 北京联合出版公司 译者: 应宁 出版年: 2015-5 页数: 456 主观信息 开始阅读：2016-08-25 结束阅读：2016-08-25 我的标签：阅读、思维方式 我的评分：8.5 我的点评 讲述思考的方法最难，本书做到了，而且做得别具一格，非常有趣。无论讲述问题的方式，还是排版的方法，这本书都让人出乎意料。作为一个阅读者，书中提到的很多技巧和方法都让我有种似曾相识的感觉，我总觉得作者总结的方法似乎在哪里见过，或者在我阅读某小说的时候使用过，但我却不能像作者这样把这些不可名状的方法用简单直白的语言和无厘头的插图归纳出来。这本书告诉了我阅读的时候为什么要那样做，例如为什么会在跳过某些段落的时候依然能了解这些段落的含义，以及为什么小说改编成电影之后会招致书迷们的一篇骂声。\n这更加说明了：每个人都是一个独特的个体，每本小说都是依赖该个体的经验，为这个个体创作的独一无二的故事。\n纲要（目录） 画“画” 虚构 开头 时间 生动性 演绎 草图 技艺 共同创作 地图与规则 抽象 眼睛，视像和媒介 记忆与幻想 通感 能指 信念 模型 部分与整体 一片模糊 摘抄 zrong: 注意，下面的摘抄并不保证逐字记录，我可能会在做笔记的时候进行简化和顺序变化以保证容易理解。\n阅读行为并不是由一系列“现在”的体验组成的时间序列。\n在每个有意识的时刻——以及在每个阅读行为发生的时刻，过去、现在和将来交织在一起。每一处流畅的阅读间隙，都掺杂着对已读的记忆（过去），对“当下”所知的知觉体验（现在） ，以及对未读的预期（未来）。\nzrong：流畅阅读之间的间隙，就好像漫画格子之间的间隙。\n我们阅读时会把视野中的词全数揽进，像大口喝水一样把它们吞咽下去。\nzrong：阅读小说时，有时候可以刻意丢弃段落（快速阅览），得到的效果甚至比逐字逐句好很多。\n作家们为了创造令人信服的世界（或人物），并不需要大量地堆砌细节。\n一个几何图形可以由它周界上的几个点来确定——无需其他条件。或者说，规则由此确立。\nzrong：想想中土世界，我们相信它存在，并不是因为细节，而是因为规则。天宫也一样，仅仅只是简单的时间和空间规则，就让我们相信了神话中天宫的合理性，并愿意自动帮它去脑补细节。\n我们阅读时会做白日梦。\nzrong：呵呵。\n我们常把阅读时沉浸其中、顺势漂流的状态比喻成在水上漂浮。我们仿佛坐在一艘无浆的船里，被故事带着走。有时候我们必须逆着水流用力划，或是小心绕过凸出的礁石。哪怕我们只是沿着岸边行驶，领着我们的那艘船其实是——我们的自己的头脑。\n我们谈到的世界是由片段组成的，断断续续的点，零零散散地分布着。我们自身亦如此。我们的同事、伴侣、父母、孩子、朋友……都是如此。\n我们通过解读来了解自认和周边的人，给他们冠以代称、隐喻、提喻、借代词，哪怕他们是我们在这世上最爱的人。我们通过片段和替换的概念来了解他们。\nzrong：虽然很可悲，但我也不得不承认这是事实。想想半个月未见的父母，在我们脑中不也仅仅是一个符号么？我们对他们的认识和理解就是通过脑中断断续续的片段形成的。首先是几个形容词，然后是几个片段，再后来就是几个场景。最终合成我们对他人的印象。\n世界对我们来说是正在创作中的作品。我们将只言片语拼拼凑凑——日复一日，将他们合成在一起，才形成了我们的理解。\n作家是体验的策划者。他们将世界的噪声过滤掉，从而发出最纯净的信号。然而无论他们先前经过多么费心的筛选和严密的组织，读者的大脑会执行既定的任务：分析、筛查、分类。作者写的书，到了读者手里，又复原成了一种噪声。我们尽其所能接纳作者的世界，与自己的材料加以混合，提炼出独一无二的感受：不尽完美，有所偏重，朦胧不清。无论是阅读行为还是阅读感受，都和意识本身的工作方式类似，都是共同创造的结果。,\nzrong：小说作者是在控制我们的思想，我们所想象出来的和小说作者想象出来的并不完全一致。其实小说作者是利用了我们的记忆和经验来重新加工了小说本身。小说作者只需要在文字中指出一个方向，我们自然会朝着这个方向去努力加工。\n全文完 ","date":"2016-08-28","description":"","lastmod":"2016-08-28T03:29:45Z","slug":"what-we-see-when-we-read","tags":["reading","readingnote"],"title":"【读书笔记】当我们阅读时，我们看到了什么","url":"https://blog.zengrong.net/post/what-we-see-when-we-read/"},{"categories":["others"],"content":"小曾同学开始写小说了，这还真的是让我吃了一惊。这是第一部短篇小说，看看怎么样？\n王妃紫罗兰 作者：曾琬淑\n前言 每个狼群都有狼群之主，每个狮群都有狮群之主，每个狗群都有狗群之主，就像每个国家都有国家之主一样。但是当每个人都想当“王”时，战争是不可避免的，动物群——也不例外。\n虎口夺食 鹿茸帕狼群生活在拉雅雪山上，常常冬天找不到丰盛的食物，虽然绿钻石的嗅觉和听觉都很灵敏，但是它和大多数的狼一样都饿得皮包骨头，有猎物也没有力气再去追赶了。\n绿钻石是鹿茸帕狼群的狼王，由于绿钻石夜间的眼睛能发出绿莹莹的光，而且比其它狼的眼睛还要亮，所以叫绿钻石。绿钻石是狼王，当然有提前挑选王妃的优势。紫罗兰就是狼群里最漂亮的母狼，它的腰间长了一圈紫色的毛，所以起名紫罗兰。 鹿茸帕狼群已经饿了好几天，如果再找不到食物，恐怕幼狼就会被饿死了。绿钻石急得像热锅上的蚂蚁。它们艰难地从大雪山上转到沼泽地，突然发现一个棕色的点点，欣喜若狂的它们以为是猎物，立刻飞奔过去。 跑到那里一看，原来是一只老虎叼着一匹野马，帕雅丁狼群的兴奋立刻烟消云散，老虎可不是好惹的。记得有一次，一只叫白二的狼王不知道是不是吃了熊心豹子胆，去虎口夺食，没想到猎物没有吃成，反而被老虎咬死了，何况现在每只狼都这么虚弱呢？\n就在这时，一只叫白燕子的幼狼因为好几天没有吃到食物饿晕了过去，那可是紫罗兰的心肝宝贝。紫罗兰立刻惊叫一声，跑了过去，把白燕子护在身体下。想用身体的温度让幼狼醒过来。绿钻石心疼地看着自己的孩子和王妃，做出了一个大胆的决定。它身体一抖小心翼翼地像老虎走去。老虎正啃食着野马，没有发现绿钻石。这真是个好机会，绿钻石飞快地扑向了老虎，老虎打了一个趔趄，后退了几步，掉进了旁边的泥潭。本以为胜利在望，没想到老虎求生的本能，在最后关头咬住了绿钻石的前腿，绿钻石也掉进了泥潭。泥巴慢慢地淹过了绿钻石的前腿、嘴巴、鼻子、眼睛、耳朵、身体，直到后腿也陷进了泥潭，最后泥潭里冒出一串串气泡。\n紫罗兰伤心地朝天长啸“呦——呦——你怎么忍心离开我呢？我的儿女也离不开你呀！呦——”紫罗兰呆站着，幻想着，眼睛里闪动着泪光，或许绿钻石踩到了地，跳了上来，或许它借助老虎的背跳了上来，或许......\n过了许久，最有经验的老母狼菊花叼着一只马腿过来了，安慰道“呦——呦——你的宝宝不用挨饿了!狼总是会死的！如果捕不到食物，也会饿死的！呦——”\n紫罗兰叼起老母狼菊花吐在地上的马腿，往泥潭里悲伤地忘了最后一眼，向幼狼走去。绿钻石英雄一死，却给鹿茸帕狼群带来了灾难。\n被迫离群 狼王绿钻石英雄一死，却给鹿茸帕狼群带来了翻天覆地的变化。托绿钻石的福，每只狼都吃饱了，还留下了两个马腿和一副皮囊在大本营白玉洞的雪堆里，可以在抓不到猎物时再吃。每一只狼都悠闲地梳理着自己的皮毛，舔着爪子上的血丝。从表面上看，鹿茸帕狼群风平浪静，大家似乎都沉浸在饱餐后的惬意与舒适之中。但是，俗话说: 于无声处听惊雷。仔细观察后不难发现，狼群表面的平静下正酝酿着一场暴风雨，那正是狼王之战。\n紫罗兰虽然有无限的忧伤，但是在这种非常时期，它必须忍住悲伤为后面的生活做打算。它把四只幼狼罩在身体下，以防万一被其它公狼抓伤。幼狼分别是：白燕子、黑百合；红火焰、蓝海浪。它们分别是两雌两雄，它们的腰部都长着与众不同的毛，所以名字里才会有颜色。\n狼群里一共有四只公狼，两只老公狼，两只年轻强壮的。狼王之战开始了，两只年轻强壮的公狼互相吼叫，好像要用自己的气势吓到对方，又好像在向对方挑衅：“这次我赢定了！”两只公狼咆哮着，撕咬着，简直跟厮杀其它动物差不多。你抓我的脖子，我咬你的耳朵，比以牙还牙还厉害。“本是同根生，相煎何太急？”紫罗兰叹了口气。\n狼王之战一直从上午持续到晚上，都还没有推出谁是狼王，只好到明天早上再开始。这股浓烈的血腥味引来了狼獾群，又引发了一场新的战争，狼獾群和早已身负重伤的公狼战斗简直是轻而易举，狼獾群不用摧毁之力咬死了一只老公狼，叼走了两只幼狼。帕雅丁狼群损失惨重。紫罗兰因为顺风远远的就闻到了狼獾的骚味，早已逃之夭夭，逃进了巴萨斯狼群的领地。\n忍辱求全 紫罗兰跑了很长时间，身体非常虚弱，一逃到巴莎萨斯狼群的领地就睡着了。第二天早上醒来时已经是中午了。紫罗兰和孩子们的肚子饿得咕咕叫，于是抓了几只老鼠充饥。紫罗兰不想再回到鹿茸帕狼群了，回到鹿茸帕狼群看到的只有天昏黑地的斗争，血腥味一定会吸引更多的野兽来抓捕更多的幼狼，再这样下去，自己的幼狼说不定也会被捉。更何况鹿茸帕狼群现在的力量很弱，寻找食物和防止外来的侵袭都成首要问题。为了绿钻石的孩子，紫罗兰决定先在这个狼群里混一段时间。 这时，巴沙萨斯狼群的狼王——巴萨走了过来。巴萨的样子看上去非常威严，一身褐色的毛很有光泽，它的目光也很敏锐，好像可以看透别人的心思一样。巴萨见到紫罗兰先是一愣，它从来没有见过长着紫色毛的狼，然后左顾右盼了一阵，觉得没有什么危险，便甜蜜地靠了过来。\n紫罗兰曾经听说过巴萨的事，知道它是这里的狼王，也知道只要讨好狼王便能让孩子们不那么危险，于是低下头，轻柔地叫了两声。并亮出自己的四个孩子，用尾巴把孩子扫到狼王面前，用肢体语言告诉狼王：“您如果愿意成为它们慈爱的父亲，我愿意成为您的妻子！”\n狼王看了看紫罗兰，又看了看四只幼狼，不知如何是好，最终，巴萨舔了舔每只幼狼的额头，算是同意了紫罗兰的提议。\n紫罗兰被巴萨带进了巴沙萨斯狼群的大本营——沙巴马特洞穴。紫罗兰一进洞穴，所有的狼都张大了嘴巴，纷纷议论：\n——狼王要换王妃了吗？现在的王妃玫瑰肯能地位不保了。\n——它是谁？看上去真漂亮！\n——刚进来的母狼还带着四只幼狼呢！\n......\n这时，只听“阿呜——”一声，狼王宣布了自己的新王妃是紫罗兰。紫罗兰昂首挺胸地站在狼王旁边，扫视着整个狼群，心里坚定地想着我要为了孩子好好地生活下去。玫瑰惊呆了，竟然还有这种事？嫉妒和愤怒的玫瑰死死地盯着紫罗兰和它的孩子们。那眼神充满了敌意，紫罗兰突然忐忑不安起来，虽然早已预料到会有危险，但该怎样应对，它还没有准备好。\n融入群体 紫罗兰当上了新王妃，它开始适应这个狼群的新生活。有一次，它跟着狼王巴萨去打猎进入北部沼泽。虽然那里的野猪肉质鲜美，但是那里的沼泽地非常可怕，经常吞噬各种野兽，进入了沼泽就相当于进入了地狱。这时，有一群野猪正向我们跑过来，巴萨立刻扑了过去，就在这时，一只母野猪侧了侧身子，让巴萨扑了个空。\n巴萨气极了，又来了个“飞鱼跃身”，野猪是扑倒了，但是因为巴萨用力过猛导致连狼带猪都掉进了泥潭。巴萨还算机灵，见形势不对，便松开口，及时咬住了一根树枝。紫罗兰见小野猪没了靠山，于是一声下令，让狼群去扑咬小野猪。一时忘了陷在泥潭里的狼王巴萨。当狼群成功地捕捉到四只小野猪后，泥潭里突然发出了一声声凄凉的嗷叫，紫罗兰这才从喜悦中回过神来，它竟然忘记了陷在泥潭里的狼王巴萨。\n于是急切地跑到泥潭旁，看到了深入绝境的巴萨，它急中生智想到了一个办法，但是这个办法会伤害到它自己。为了自己的孩子，它咬咬牙，把自己蓬松的尾巴伸到泥潭里。大声说道：“哟--------咬住我的尾巴!!!”,巴萨像得到救命稻草一样，死死地咬住了紫罗兰的尾巴。紫罗兰忍着疼，两只前爪死死地抠住地面，好像要在土地里生根一样，它一步一步往前挪动。正在这时，狼群其它成员也赶到了。它们面面相觑不知道怎么办才好。其中一只身强力壮的大公狼——茄子，走了过来，咬住了紫罗兰的一只前爪，往前拔。其它的狼也照着这个方法，有的咬着紫罗兰的肩胛，有的咬着后爪，有的咬着腰部......\n狼王巴萨的身体已经出来了一半，它用尽全身的力气往上一跃而起，终于跳出了泥潭。大公狼们松开了嘴，紫罗兰瘫倒在地面上，它的身上到处都是齿印，还渗出一滴一滴的血珠。它扭头看看自己的尾巴，已经僵直的不能动弹了，自己腰部紫色毛也被血染红了。巴萨抖落身上的泥巴后，走到紫罗兰身旁，伸出舌头反复地舔着紫罗兰的伤口。巴萨的眼神里充满了关爱和感激之情。\n回到大本营，公狼们把野猪肉大卸八块，并把剩下的猪肉藏进大本营的冰窟窿里。紫罗兰救狼王的事情在狼群里传开了。狼王日夜守护在虚弱的紫罗兰身边。整个狼群对紫罗兰的勇敢产生了由衷的敬佩，狼王也更加信任紫罗兰。雌狼玫瑰对发生的这一切更是咬牙切齿，希望让紫罗兰付出更多的代价。\n生命威胁 半周后紫罗兰恢复了健康，精神也越来越好。今天它开始和狼群一起去打猎。留下雌狼玫瑰看管幼狼。\n玫瑰本来是狼王巴萨的王妃，手握着权利，受到其它狼的尊重，而且为狼王生了两只幼狼，日子过得很舒心。可是不知从哪里来了个紫罗兰让它的好日子到了头。本想着找个茬可以把紫罗兰赶走，没想到上次救狼王事件，让紫罗兰的名声大振。不仅是狼王对紫罗兰更加信任，就连小狼们也把它当成了英雄。玫瑰的内心像刀割一样，恨它恨得牙痒痒，它想报仇，它想让紫罗兰害怕它，更想看到紫罗兰伤心的样子。\n今天是个大好机会，玫瑰把幼狼分成三组，每组去寻觅不同的食物，它事先把死老鼠，死壁虎，死青蛇隐藏在洞穴周围，看哪组最先找到它们需要找到的食物。幼狼们高兴极了，它们觉得自己长大了可以像爸爸妈妈那样去捕猎了。\n玫瑰把紫罗兰的四个孩子分到一组，分配它们去找青蛇。在洞穴旁有座悬崖，悬崖下面是雪山，如果不小心从悬崖上掉下去肯定必死无疑。青蛇当然也隐藏在一个危险的地方。这四只幼狼不管谁掉下去玫瑰都很开心，所以它高枕无忧地在洞穴里睡大觉。\n到了中午，有两组幼狼分别找到了自己的食物，欢天喜地地跑了回来，唯有紫罗兰孩子的那组没有回。玫瑰表扬了这两组幼狼，其实内心高兴着另一件事。又过了很久，那组幼狼任然没有回来。其它幼狼都已经开始睡午觉了。\n紫罗兰虽然出去打猎，其实它很担心自己的孩子。想到玫瑰那仇恨的眼神，不觉打了个寒颤。于是，它提前从打猎的队伍里回到大本营。一进洞穴，看见幼狼们在睡觉，可是怎么没有自己的孩子呢？\n再看看玫瑰，正埋头靠着一块石头睡大觉。紫罗兰威严地吼了两声，玫瑰立刻跳了起来，眨巴眨巴眼，好像在说：“呦——尊敬的王妃，我做了什么让您不满意的事了？”紫罗兰越想越生气，“啊呜”在玫瑰身上咬了一搓狼毛:“嗷——我的孩子去哪里了？”\n“它们去找食物了，其它的组都已经回来了，不知道它们怎么那么慢。”玫瑰不屑的回答道。\n紫罗兰听到这里，愤怒地看了一眼玫瑰，飞快地奔了出去，寻找孩子。紫罗兰拼命地呼唤着孩子们的名字“红火焰”、“白燕子”......,心里也祈祷着：孩子们千万不要出事啊！\n“嗷——”传来了一声回应，那是蓝海浪的声音。\n“嗷——”又传来了一声回应，那是黑百合的声音。\n“嗷——”又传来了一声回应，那是白燕子的声音。咦？怎么没有听到红火焰的回应？\n紫罗兰正担心着寻着声音来到了悬崖边上，看到红火焰身体正悬在悬崖边上，其它的三只幼狼正奋力的把它拽上来。紫罗兰跑过去，咬住红火焰的脖子，用力往后一跃，红火焰脱离了危险，红火焰虽然是幼狼，但也不是很轻。\n跳跃时，小青蛇也从红火焰的嘴巴里掉了出来。四个孩子没有意识到刚才的危险，还开心的问：“呦——我们终于找到了食物，我们是不是很棒啊！”紫罗兰惊魂未定地说：“呦——你们很棒，很勇敢。”\n俗话说眼睛是心灵之窗 ，回到洞穴，紫罗兰看到玫瑰那失望的眼神，就可以断定这是玫瑰精心策划的阴谋。它真为这几个小家伙的性命感到担忧。\n保护幼狼 虽然雌狼玫瑰多次陷害紫罗兰，但是紫罗兰的乐于助人的精神并没有改变。这天，狼王带着狼群外出觅食了，留下紫罗兰在大本营看管幼狼。紫罗兰用蓬松美丽的大尾巴逗着小家伙玩，玩得很开心呢！\n就在这时，一只母狼獾从一块岩石旁跳了出来，恶狠狠地向紫罗兰扑了过来。紫罗兰愣住了，一块平凡的岩石里怎么会跳出一只狼獾呢？现在只有我孤身一狼，怎么对付它呢？这只狼獾难道是事先藏好，等洞穴里只有一只狼的时候趁机叼走狼崽吗？\n反正现在没有时间分析事情的前因后果了。紫罗兰很快镇定下来，它知道唯有一搏才能保住这些幼崽。它轻轻一跃，主动发出攻击。狼獾也凶猛地朝它扑过来。紫罗兰向左一闪躲开了这一扑，迅速掉头朝狼獾的头部扑咬过去。这一口正好要在了耳朵上，母狼獾的耳朵上留下了两个血窟窿。母狼獾虽占了下风但更加凶蛮了，直奔狼崽，不再对付紫罗兰。\n狼獾叼起一只狼崽就跑，这可是玫瑰的孩子，紫罗兰见大事不妙，纵身一跃，竟然骑在了母狼獾的背上，紫罗兰想：既然都已经骑到它背上了，还不如就给它做个了断！\n紫罗兰刚准备咬下去的时候，狼獾突然丢下幼狼反咬了一口，咬到了紫罗兰的前腿。紫罗兰忍着腿上的伤痛，死死地扒在狼獾身上。狼獾见它没有掉下去，又在紫罗兰的另一条腿上狠狠地咬了一口。现在的疼痛传遍了紫罗兰的整个身体，但是它任然坚如磐石地扒在狼獾身上。\n紫罗兰深深地吸了一口气，用尽全身的力气看准母狼獾的脖子，狠狠地咬了下去。这一咬至关重要，咬断了母狼獾的颈部大动脉，狼獾惊叫了一声倒了下去。 狼群正好回营。狼王巴萨见到了这情景非常感动，舔着紫罗兰身上的伤口，连玫瑰也不禁暗暗佩服紫罗兰不屈不挠的精神，慢慢地尊敬起紫罗兰来了。\n完美生活 通过上一次紫罗兰保护幼狼的事，更是赢得了大家的信任。连玫瑰都渐渐喜欢上紫罗兰了，并且建立了友情。有一次，依然是玫瑰来看管幼狼，紫罗兰也是提前回到洞穴。它看到的并不是空空如也的洞穴，而是充满欢声笑语的洞穴。只见玫瑰也用它蓬松的大尾巴逗着幼狼玩耍，没有丝毫的恶意。数数看：一、二、三、四。四只幼狼都还在呢！！！\n玫瑰为什么不陷害它们呢？难不成......就在这时，玫瑰侧躺下来，暴露出最容易受攻击的颈部，这在狼群社会里，是一种信任，是一种尊敬，更是一种责任感。紫罗兰的心中，一股暖流流遍全身——原来玫瑰并不是为了和自己做一段殊死拼搏，而是为了做出气味认证，同意自己当上新王妃。它也在玫瑰的脖子上舔了舔，完成了气味认证。\n这段提心吊胆的日子终于结束了！紫罗兰感叹到。\n（完）\n","date":"2016-08-19","description":"","lastmod":"2016-08-19T14:54:36Z","slug":"princess-violet","tags":["qiqi"],"title":"王妃紫罗兰","url":"https://blog.zengrong.net/post/princess-violet/"},{"categories":["impressions"],"content":"\n一天里遇到两件值得思考且能互相印证的事，也算有趣。\n第一件是个快递小包裹，快递员在一楼摆摊，打电话给我要求下去拿。此事见仁见智，有人倾向于说我付了快递费你就必须给我送上来；我则倾向于在不影响工作的前提下尽量下楼一趟。一来大家都不容易，何况现在是四十度的大热天；二来我也算是可以顺便活动活动筋骨，舒展一下我那该死的颈椎。\n这件快递可巧，周三送的时候我正在二楼面试，面完下楼转一圈发现快递员已经没影儿；周四下午送的时候我在外面开会；今天上午（周五）送的时候，我又在外面开会。\n每次快递员电话打来，我说：“不好意思，我在开会，麻烦你帮我送到二楼或者三楼前台，公司有人签收的。”快递员小伙的回答是：“你开会，不能找个同事来收么！你们这么多快递，又不是你一个人的！”这番话不需要回应，因为没法回应。然后，就没有然后了。\n今天电话的时候，我们的会议刚好进行到会歇。我第三次听到同样的话，口气强硬起来：“你是怎么在干工作？一个快递三天都没有送到？你说其它快递都和你一样在一楼摆摊，为什么不提顺丰和京东？不提申通和中国邮政？为什么别的快递摆摊，你也一定要摆摊？你今天一定要给我送到二楼前台，否则我要投诉你！我一定会投诉你！”十分钟后，我接到快递员的电话，说快递已经送到前台了。\n我们往往要求别人这样那样，却很少用同样的标准要求自己。我们往往只站在对自己有利的方向思考，却很少站在别人的角度思考。\n中午去交下个月停车费，金融港物业按惯例会把停车卡留下来，充值完毕后放到二号岗亭，我下班后去取。这会导致我中午停车的时候无卡可用。物业的说法是要我和岗亭工作人员说明此事，要求放行。今天的岗亭当班似乎是个新手，她要求我必须领一张临时卡，否则不让我入场。\n下午出门的时候遇到了麻烦。岗亭显然已经换过班，当班的大姐死活不让我出门，坚持说拿了临时卡就必须交停车费。怎么解释都不理，还劈头盖脸一顿数落，大致是你怎么这么傻，有卡还拿临时卡之类云云。\n我心说真日了狗了，本来正式卡已经到手，临时卡直接扔掉，她又知道个鸟？我当真是犹豫了一下还是想着好心有好报不要浪费公共财物掏出了临时卡，可怜好报变好骂。找谁说理去？\n我听出大姐操着本地口音，马上换成武汉话敬语，摆出无害笑脸平静以对，一番稀里哗啦，保证永不再犯，最后没付钱放行。同车小伙伴奇怪我为啥这么好脾气。讲真，我也奇怪呢。\n碰到不可理喻之事，有句指导语是“莫与傻B论短长”，笃信这句话的人，在心中都会认为对方是傻B吧？那么对于对方来说，一定也以为对方的对方是傻B吧？那么问题来了，两个傻B在一起能论出什么短长？\n还有句话说“弱者都易怒”：越困窘，火气越大。越失败，越不容忍。越不被尊重，越容易侮辱他人。那么问题来了，互相攻击的两个人之间，孰弱孰强？\n快递小哥，你消耗了我的时间，没有尽到你的责任，却无法有意识到下楼领快递并不是我的义务。所以我会立刻强硬起来，这是在捍卫我的利益。\n岗亭大姐，你今天碰到过类似的情况，第一次你忍了，碰到我这个第二次破坏规则的家伙，你爆发了。我挺能理解这种情况，所以莫名其妙地好脾气，保持了自己回家路上好心情。\n我在想，如果把处理这两件事的态度调换一下，会得到什么结果？结果或许并没有什么不同，但过程显然会不太开心。\n我们往往用严苛的规则去束缚别人，用宽松的标准来对待自己。所以，我们任何时候都是对的。要解决这样的问题，尝试把自己拉到（无论孰高孰低，孰强孰弱）和对方一样的标准，再比较一下，同样的标准下，我是否能做到？\n若能做到，你就强硬，不能做到，赔笑脸也没什么不好。\n你真的就那么强吗？我看未必吧。\n","date":"2016-07-29","description":"","lastmod":"2016-07-29T15:46:09Z","slug":"let-us-do-it","tags":["live"],"title":"像要求别人那样要求自己","url":"https://blog.zengrong.net/post/let-us-do-it/"},{"categories":["impressions"],"content":"\n停车场守门大爷平时喜欢光着上身，坐在门前打瞌睡。我晚上开车回来，碰到他值班时，他一看到是我，就不跟过来指挥倒车，而是手一挥：“您家自己小心，谢谢啊！”接着回去喝他的小酒。我停好车经过他面前时和他打个招呼，他会停下正在哼着的小曲儿，抬起头问一句：“还冇七饭？快点回克啊！”一股浓重的老武汉市井气儿就散漫开来。\n今天在停车场等人的当儿，我和他攀谈起来。大爷依然光着膀子，左手戴一块黄色表带的手表，似乎有些年头了。我瞥了一眼，是块劳力士，看年头不像仿品，心中便隐约觉得这位大爷有些来历。主动一问，原来这是一块28年前购买的自动上链机械表。老大爷说不出表的品牌，但他对这块表还是很自豪的，以至于到现在还能记得买表时候的细节。\n老大爷年轻时做的是长途客车包车生意。他从公司包了3台卧铺车，从武汉跑温州和南昌。一台车5万元费用，8万元购买费用，签合同5年。二十多年前，川军南下北上如火如荼。卧铺车票240元，坐票（就是在地板上放几个小板凳）180元，每个月除了租子钱，还能赚3万。那可是二十多年前的3W！老爷子说到这里意气风发：“那时候武汉港码头，哪个不认识我！汉正街多少人找我带货！”我脑海中浮现起80年代武汉港的模样，站在码头长途客运站前揽客的意气风发的青年，和眼前的老大爷重合在一起。\n每个人都有自己的历史。你要找到那个唤起历史的引信。\n","date":"2016-07-24","description":"","lastmod":"2016-07-24T14:21:11Z","slug":"talking-with-the-porter","tags":["life"],"title":"和停车场守门大爷聊天","url":"https://blog.zengrong.net/post/talking-with-the-porter/"},{"categories":["technology"],"content":" 2017-01-06 更新： 增加 Flask 502 错误解决 2017-04-12 更新 增加 uWSGI HTTPS 支持 2017-08-10 更新 增加详细的 uWSGI 配置说明 2017-11-24 更新 增加在 Python+uWSGI 中使用缓存 2017-11-28 更新 增加 Flask+uWSGI 的 Logging 支持 2020-01-27 更新 增加 Flask+uWSGI Logging rotate：重要补充 2020-08-15 更新 增加 pyzog：uWSGI logging rotate 的终极方案，修改关于 supervisor 版本的说明 作为一个选择综合症+洁癖患者，在部署 Flask app 的时候着实有点纠结。互联网上能找到的教程都不怎么靠谱，要么太老，要么太乱连话都说不通顺，更别提那些转了无数手的资料了。 uwsgi 的参数别名众多，各种旧版本的配置也不尽相同，让急着把自己的 Flask 站点搭建到正式服务器上的新手们无所适从。\n我把自己的部署过程写出来，所有资源参考官方文档，我也尽量给出选择的原因。这教程适用于各个 linux 发行版。若有疑问，请留言。\n组件选择 Flask 开发使用 python3.5 ; python 虚拟环境，使用 python3.3 开始自带的 venv； 启动器使用 supervisor v3，uwsgi 使用 v2； uwsgi 和 supervisor 均使用 pip 安装，而不使用 linux 自带的版本。 因为 ubuntu 自带 uwsgi，我曾经纠结过使用系统自带版本还是使用 pip 版本。使用自带的版本的好处，就是安装后可以直接使用 /etc/init.d/uwsgi start 来启动，不需要自己写启动脚本，但我仍然选择了使用 pip 版本。下面是原因。\nuwsgi 的版本在 Ubuntu 12.04 上比较老了(1.03)，这让人心生不快。supervisor 的优势摆在那里，而且也是 python 写成，这激发了我的“纯正血统情怀”。还有一个优势就是 uwsgi 可以安装在 venv 之下，这样不必影响系统包的稳定性（不过我并没有用上这个优势，后面会讲到原因）。\nsupervisor 也有系统自带版本，版本号很老了(3.0a8-1.1)。supervisor 文档 中贴心地提供了全套 initscripts ，在各个发行版上启动都不是问题。\n项目文件夹结构 待部署的项目名称为 EM ，文件夹结构如下，部署在服务器的 /srv/www/em 文件夹下：\n1├── README.md 2├── app 3│ ├── __init__.py 4│ ├── main 5│ ├── models.py 6│ ├── static 7│ └── templates 8├── config.py 9├── manage.py 10├── migrations 11├── requirements.txt 12├── tests 13├── venv 14└── uwsgi.ini 安装 supervisor supervisor 不支持 python3 ，这在官方文档中就已经说明：\nSupervisor is known to work with Python 2.4 or later but will not work under any version of Python 3.\n2020-08-15 更新： Supervisor 3.x 版本已经支持 Python 3，请自行参考官方文档升级： Upgrading Supervisor 2 to 3。本文内容基于 Supervisor 2.x，且不再更新，仅供参考。\n好在服务器上都有自带的 python2 。但我的服务器上的 python2 中并没有 pip，因此需要先安装 pip。\n标准的 pip 安装方法是：\n1wget https://bootstrap.pypa.io/get-pip.py 2python get-pip.py 安装和下载过程中的全部内容都是国外的服务器导致速度堪忧，下面提供一个简单的方法。\n1. 访问 豆瓣的 pypi 源 ，在该页面中搜索 6.1.0.tar.gz ，下载搜索到的那个链接，例如我的下载链接为：\n1http://pypi.doubanio.com/packages/6c/84/432eb60bbcb414b9cdfcb135d5f4925e253c74e7d6916ada79990d6cc1a0/pip-6.1.0.tar.gz#md5=d0c349765bbc23743cec42b37bd8a281 更多的源请参见： 常用开源镜像站整理\n2. pip 的安装依赖 setuptools ，搜索 20.1.tar.gz ，下载搜索到的那个链接。\n3. 将下载到的两个压缩包解压，然后安装：\n1python setuptools-20.1/setup.py build 2python setuptools-20.1/setup.py install 3python pip-6.1.0/setup.py build 4python pip-6.1.0/setup.py install 上面的版本号不一定要和我的一致，但一定要选择 tar.gz 的压缩包，这样的压缩包还是使用 egg 格式发布的，可以使用 setup.py 安装。新版本的 pip 和 setuptools 都使用 wheel 格式发布了，解压后无法使用 setup.py 安装。想了解 egg 和 wheel 的区别，可参考 Python 包管理工具解惑 。\n安装了 pip 之后，就可以使用 pip install supervisor 了。注意，不应该将 supervisor 安装在虚拟环境中，作为一个通用组件，它应该是一个系统级别的存在。\n这里还有一个小插曲。我使用 常用开源镜像站整理 中提到的方法把 pypi 的源设置成了豆瓣 HTTPS 源，但更新 pip 的时候报错：\n1# pip install -U pip 2/usr/local/lib/python2.7/dist-packages/pip-6.1.0-py2.7.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py:79: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning. 3 InsecurePlatformWarning 4DEPRECATION: Failed to find \u0026#39;pip\u0026#39; at https://pypi.doubanio.com/simple/pip/. It is suggested to upgrade your index to support normalized names as the name in /simple/{name}. 5/usr/local/lib/python2.7/dist-packages/pip-6.1.0-py2.7.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py:79: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning. 6 InsecurePlatformWarning 7Cannot fetch index base URL https://pypi.doubanio.com/simple/ 8/usr/local/lib/python2.7/dist-packages/pip-6.1.0-py2.7.egg/pip/_vendor/requests/packages/urllib3/util/ssl_.py:79: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning. 9 InsecurePlatformWarning 10Requirement already up-to-date: pip in /usr/local/lib/python2.7/dist-packages/pip-6.1.0-py2.7.egg 看反馈的信息可能是 urllib3 自带的 ssl 加密问题。pip 升级到 8.1.2 也有相同的问题。\n因此，我把 pypi 源换成了没有 https 加密的地址（见下方），安装成功。\n1# pip install supervisor 2Collecting supervisor 3 Downloading http://pypi.zenlogic.net/packages/44/80/d28047d120bfcc8158b4e41127706731ee6a3419c661e0a858fb0e7c4b2d/supervisor-3.3.0.tar.gz (416kB) 4 100% 419kB 3.3MB/s 5Collecting meld3\u0026gt;=0.6.5 (from supervisor) 6 Downloading http://pypi.zenlogic.net/packages/py2.py3/m/meld3/meld3-1.0.2-py2.py3-none-any.whl 7Installing collected packages: meld3, supervisor 8 Running setup.py install for supervisor ... done 9Successfully installed meld3-1.0.2 supervisor-3.3.0 配置 supervisor 下面以 ubuntu 为例进行配置。\n1. 在initscripts 下载 ubuntu 的启动脚本，复制到 /etc/init.d/supervisor ；\n2. 修改启动脚本中的路径，使其与安装路径相匹配。如果使用 pip 安装，supervisord 一般位于 /usr/local/bin/ 而非默认的 /usr/bin/ ;\n3. 执行 echo_supervisord_conf \u0026gt; /etc/supervisor/supervisord.conf 创建一个默认的配置文件；\n4. 调整 /etc/supervisor/supervisord.conf 中的几个路径相关的参数，使其与 /etc/init.d/supervisor 中的路径参数完全一致：\n1; (the path to the socket file) 2file=/var/run/supervisor.sock 3; (main log file;default $CWD/supervisord.log) 4logfile=/var/log/supervisord.log 5; (supervisord pidfile;default supervisord.pid) 6pidfile=/var/run/supervisord.pid 7; use a unix:// URL for a unix socket 8serverurl=unix:///var/run/supervisor.sock 上面的三个参数默认的值位于 /tmp 中，这样可能会导致执行 /etc/init.d/supervisor start 的时候由于找不到 pid 文件导致误判。\n5. 调整 /etc/supervisor/supervisord.conf 中的 [includes] section 为如下所示（注意要使用绝对路径）：\n1[include] 2files = /etc/supervisor/conf.d/*.conf 这样，加入程序的时候就不必修改主配置文件，只需要在 /etc/supervisor/conf.d 文件夹下面增加一个 .conf 配置文件即可。\n来看看成果吧！\n1# service supervisor start 2Starting supervisor: supervisord. 3# service supervisor status 4supervisord is not running. 安装 uwsgi 在服务器上 clone 了 flask 项目，然后安装 uwsgi ，但碰到如下错误：\n1# pip3 install uwsgi 2...... 3d -ldl -lutil -lrt -lm /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a -lutil -lcrypt 4 /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a(pytime.o): In function `pygettimeofday\u0026#39;: 5 /root/software/Python-3.5.1/Python/pytime.c:494: undefined reference to `clock_gettime\u0026#39; 6 /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a(pytime.o): In function `pymonotonic\u0026#39;: 7 /root/software/Python-3.5.1/Python/pytime.c:633: undefined reference to `clock_gettime\u0026#39; 8 /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a(pytime.o): In function `pygettimeofday\u0026#39;: 9 /root/software/Python-3.5.1/Python/pytime.c:494: undefined reference to `clock_gettime\u0026#39; 10 /root/software/Python-3.5.1/Python/pytime.c:508: undefined reference to `clock_getres\u0026#39; 11 /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a(pytime.o): In function `pymonotonic\u0026#39;: 12 /root/software/Python-3.5.1/Python/pytime.c:633: undefined reference to `clock_gettime\u0026#39; 13 /root/software/Python-3.5.1/Python/pytime.c:646: undefined reference to `clock_getres\u0026#39; 14 /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a(timemodule.o): In function `py_process_time\u0026#39;: 15 /root/software/Python-3.5.1/./Modules/timemodule.c:977: undefined reference to `clock_gettime\u0026#39; 16 /root/software/Python-3.5.1/./Modules/timemodule.c:983: undefined reference to `clock_getres\u0026#39; 17 /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a(timemodule.o): In function `time_clock_getres\u0026#39;: 18 /root/software/Python-3.5.1/./Modules/timemodule.c:205: undefined reference to `clock_getres\u0026#39; 19 /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a(timemodule.o): In function `time_clock_gettime\u0026#39;: 20 /root/software/Python-3.5.1/./Modules/timemodule.c:151: undefined reference to `clock_gettime\u0026#39; 21 /usr/local/lib/python3.5/config-3.5m/libpython3.5m.a(timemodule.o): In function `time_clock_settime\u0026#39;: 22 /root/software/Python-3.5.1/./Modules/timemodule.c:182: undefined reference to `clock_settime\u0026#39; 23 collect2: ld returned 1 exit status 24 *** error linking uWSGI *** 25 26 ---------------------------------------- 27Command \u0026#34;/urr/local/bin/python3.5 -u -c \u0026#34;import setuptools, tokenize;__file__=\u0026#39;/tmp/pip-build-vz6gni75/uwsgi/setup.py\u0026#39;;exec(compile(getattr(tokenize, \u0026#39;open\u0026#39;, open)(__file__).read().replace(\u0026#39;\\r\\n\u0026#39;, \u0026#39;\\n\u0026#39;), __file__, \u0026#39;exec\u0026#39;))\u0026#34; install --record /tmp/pip-klu1fasz-record/install-record.txt --single-version-externally-managed --compile --install-headers /url/local/lib/site/python3.5/uwsgi\u0026#34; failed with error code 1 in /tmp/pip-build-vz6gni75/uwsgi/ 我的服务器中的 python3.5 是自行编译安装的。因为服务器发行版是 Ubuntu12.04，官方源中并没有 python3.5。自行编译安装的 python 缺少了 python3.5-dev 这个包，导致 uwsgi 安装的时候无法编译通过。我碰到的问题和 这里 描述的一致。\n我采用了一种比较简单的解决方案，就是删除编译安装的 python3.5 ，加入 PPA 源，再安装 PPA 源中的 python3.5 。\n但是，python3.5 的源码包并没有提供 make uninstall 命令，因此删除已经编译安装的 python3.5 成了一个问题。有两个方法解决这个问题。\n1. 使用 checkinstall 基于当时编译用的源码创建一个 deb 包，然后安装它，再删除它。 via\n1# 安装 checkinstall 2sudo apt-get install checkinstall 3cd /home/rzeng/Python_3.5.1 4# 创建一个 deb 包。注意在这里提供一个特定的包名。例如 python351，如果使用默认的 python，则可能和发行版自带的 python2 冲突。 5sudo checkinstall -D --fstrans=no make install 6# 基于创建的 deb 包重新安装 python 7sudo dpkg -i python_3.5.1-1.amd64.deb 8# 移除刚才安装的包 9sudo dpkg -r python351 2. 直接删除已经安装的文件。 via\n使用 whereis 获取到编译安装的 python 的安装路径，然后逐一删除即可。\n1# whereis python3 2python3: /usr/bin/python3.5m-config /usr/bin/python3.5m /usr/bin/python3.5-config /usr/bin/python3.5 /etc/python3.5 /usr/lib/python3 /usr/lib/python3.5 /usr/local/lib/python3.5 /usr/include/python3.5m /usr/include/python3.5 如果怕 whereis 删不干净，也可以重新编译安装一遍，记录所有的安装日志：make install \u0026amp;\u0026gt; |tee make.log ，然后在日志里分析所有文件的安装路径，再逐个删除。\n下面是加入 ppa 源并安装 python3.5 就简单多了：\n1sudo add-apt-repository ppa:fkrull/deadsnakes 2sudo apt-get update 3sudo apt-get install python3.5 python3.5-dev 现在安装 uwsgi ，一次成功。注意，和 supervisor 一样，不应该将 uwsgi 安装在虚拟环境中。\n1# python3.5 -m pip install uwsgi 2Collecting uwsgi 3 Downloading http://pypi.zenlogic.net/packages/83/22/47b6ff871a5f01b9f660121cf61ba1eccbf7886b5cbe24caacccd0d00d07/uwsgi-2.0.13.1.tar.gz (784kB) 4 100% 788kB 3.0MB/s 5Installing collected packages: uwsgi 6 Running setup.py install for uwsgi ... done 7Successfully installed uwsgi-2.0.13.1 配置 uwsgi uwsgi 支持使用 ini/xml/json/yaml 格式作为配置文件（关于配置文件，可参考 常用配置文件格式简析 ）。我建议使用 ini 格式，这样最为简单。下面是个最简单的配置文件(uwsgi.ini)：\n1[uwsgi] 2; 当设定了 max-request 的时候，到达值后 worker 会重启 3; http://uwsgi-docs-cn.readthedocs.io/zh_CN/latest/Options.html?highlight=max-requests 4; 如果没有 master，重启将不会实现 5; http://stackoverflow.com/a/5430522/1542345 6master = true 7max-requests = 6000 8 9; Simple math like processes = 2 * cpucores will not be enough. 10processes = 2 11 12; 如果你想维持 Python 的线程支持同时应用又不启动多个线程，只需要加上 --enable-threads 选项 (或者 enable-threads = true 在 ini 风格配置文件中)。 13; http://uwsgi-docs-cn.readthedocs.io/zh_CN/latest/WSGIquickstart.html#python 14; enable-threads = true 15 16; http://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html 17; 启用了多线程模式后，但自动开启线程支持，因此上面的 enabled-threads 不用设置 18threads = 2 19 20; 惊群效应 21; http://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/articles/SerializingAccept.html 22thunder-lock = true 23 24chmod-socket = 664 25 26; 参见 https://blog.zengrong.net/post/2614.htm 27buffer-size = 32768 28 29; 执行的启动文件 30wsgi-file = manage.py 31; 对应 manage.py 的同名全局变量，是一个 Flask app 32callable = app 33 34; 在 ubuntu 下，使用 www-data 权限来执行，其他发行版请自行修改 35uid = www-data 36gid = www-data 37 38; 虚拟环境的文件夹 39venv = /srv/www/em/venv 40; 可以使用魔术变量, %d 代表 uwsgi 工作目录 41; venv = %dvenv 42 43; 启动 http 监听，注意不要写成了 socket 44http = 0.0.0.0:5000 45 46; 使用 pidfile，方便用 --stop 或者 --reload 47pidfile = %d%n.pid 48 49; 除了 pidfile 也同时使用 Master Fifo 来管理进程 50; http://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/MasterFIFO.html 51; 如果使用 supervisor 管理进程，是否使用 master-fifo 则可以自行选择，这是对 uwsgi 进行高级管理的好办法 52master-fifo = %d%n.fifo 在 uwsgi 的配置中，有许多配置是有多个别名的。例如设置虚拟环境的这个配置 venv，就有如下几个别名：\nvenv virtualenv home pyhome wsgi-file 的别名是 file 。\n所以，在网上搜索到的教程使用各种别名的都有，让人感觉很混乱。查一下 官方文档 就很清楚了。由于 home 这个名称有欺骗性，我偏爱于使用 venv 这个名称。\n接下来启动 uwsgi 进程，显示如下：\n1#uwsgi uwsgi.ini 2[uWSGI] getting INI configuration from uwsgi.ini 3*** Starting uWSGI 2.0.13.1 (64bit) on [Wed Jul 27 20:29:52 2016] *** 4compiled with version: 4.6.3 on 27 July 2016 09:59:07 5os: Linux-3.2.0-60-generic #91-Ubuntu SMP Wed Feb 19 03:54:44 UTC 2014 6nodename: aliyun 7machine: x86_64 8clock source: unix 9pcre jit disabled 10detected number of CPU cores: 1 11current working directory: /srv/www/em 12detected binary path: /usr/local/bin/uwsgi 13setgid() to 33 14setuid() to 33 15*** WARNING: you are running uWSGI without its master process manager *** 16your processes number limit is 7815 17your memory page size is 4096 bytes 18detected max file descriptor number: 1024 19lock engine: pthread robust mutexes 20thunder lock: disabled (you can enable it with --thunder-lock) 21uWSGI http bound on 0.0.0.0:5000 fd 4 22spawned uWSGI http 1 (pid: 25209) 23uwsgi socket 0 bound to TCP address 127.0.0.1:41177 (port auto-assigned) fd 3 24Python version: 3.5.2 (default, Jul 17 2016, 17:43:04) [GCC 4.6.3] 25PEP 405 virtualenv detected: /srv/www/em/venv 26Set PythonHome to /srv/www/em/venv 27*** Python threads support is disabled. You can enable it with --enable-threads *** 28Python main interpreter initialized at 0x18f2ba0 29your server socket listen backlog is limited to 100 connections 30your mercy for graceful operations on workers is 60 seconds 31mapped 72768 bytes (71 KB) for 1 cores 32*** Operational MODE: single process *** 33WSGI app 0 (mountpoint=\u0026#39;\u0026#39;) ready in 1 seconds on interpreter 0x18f2ba0 pid: 25208 (default app) 34*** uWSGI is running in multiple interpreter mode *** 35spawned uWSGI worker 1 (and the only) (pid: 25208, cores: 1) 访问服务器，可以看到 uwsgi 收到的 get 请求：\n1[pid: 25208|app: 0|req: 1/1] 119.97.214.98 () {36 vars in 913 bytes} [Wed Jul 27 20:31:56 2016] GET / =\u0026gt; generated 1349 bytes in 68 msecs (HTTP/1.1 200) 2 headers in 81 bytes (1 switches on core 0) 最后，可以把标准输出记录到 log 中，在 uwsgi.ini 中加入（记得要把 logs 文件夹的写权限给 www-data 账户）：\n1[uwsgi] 2; ..... 3logto = /srv/www/em/logs/uwsgi.log 这样，再次启动 uwsgi 的时候，显示如下：\n1#uwsgi uwsgi.ini 2[uWSGI] getting INI configuration from uwsgi.ini 所有其他的输出都被记录到日志文件中去了。\n配置 supervisor + uwsgi 增加一个 supervisor 配置文件： /etc/supervisor/conf.d/em.conf ，内容如下：\n1[program:em] 2 3; 启动命令行。uwsgi 已经在系统 PATH 中，因此直接调用，参数使用了绝对路径。 4command=uwsgi /srv/www/em/uwsgi.ini 5 6; 启动文件夹。这里使用 Flask 项目所在文件夹，也就是 uwsgi.ini 所在文件夹。 7directory=/srv/www/em 8 9; 启动帐号。和 uwsgi 的配置相同 10user=www-data 11 12; 分别保存标准输出和标准错误日志，如果 uwsgi 启动出错，可以去日志里面找原因。 13stdout_logfile=/srv/www/em/logs/supervisor_uwsgi_stdout.log 14stderr_logfile=/srv/www/em/logs/supervisor_uwsgi_stderr.log 接着进入 supervisorctl 来启动 em 这个程序：\n1supervisor\u0026gt; start em 2em: started 3supervisor\u0026gt; status em RUNNING pid 26267, uptime 0:00:29 可以使用 help 命令查看 supervisorctl 提示符支持的子命令。\n此时可以访问 http://your-domain:5000 来查看 Flask 站点了。\n配置 Nginx + uwsgi 最后一步，需要把 Flask 站点置于 Nginx 之后。因为我的服务器上还有 php 程序，要共用 80 端口，就需要 Nginx 来做转发。我使用的是 Nginx 的修改版 OpenResty，它的配置和 Nginx 完全相同。如果你要安装 OpenResty，可以参考这篇： 从 Lighttpd 到 OpenResty 。\n我的 OpenResty 配置文件夹为 /usr/local/openresty/nginx/conf ，所有的虚拟主机放在 vhost 子文件夹之下。ubuntu 和 centos 的配置文件夹路径可能不同，请自行判断。\n下面是 /usr/local/openresty/nginx/conf/vhost/em.conf 的内容：\n1server { 2 listen 80; 3 server_name your-domain; 4\taccess_log logs/access-em.log; 5\terror_log logs/error-em.log; 6 7 location / { try_files $uri @em; } 8 location @em { 9\troot /srv/www/em/app; 10\tinclude uwsgi_params; 11\tuwsgi_pass 127.0.0.1:5000; 12 } 13} 这部分的配置在 Flask 官方文档 Configuring nginx 中有提及，uwsgi 的官方文档 Virtual Hosting 中也有说明。这些文档写的简洁清晰，比乱七八糟夹缠不清的各种转载文档是要好多了。\n重启 Nginx 看效果。此时访问 http://your-domain ，你很可能会碰到 502 错误：\n1502 Bad Gateway 2openresty/1.9.3.1 这是因为 uwsgi 配置文件中，我们使用的是 http 方式，直接把 uwsgi 作为 HTTP 服务器使用。我们需要修改 uwsgi 配置：\n1[uwsgi] 2; ... 3; 启动 http 监听，注意不要写成了 socket 4; http = 0.0.0.0:5000 5; 启动 socket 监听 6socket = 0.0.0.0:5000 然后使用 supervisorctl 重启 em 程序:\n1# supervisorctl 2em RUNNING pid 32073, uptime 0:04:25 3supervisor\u0026gt; restart em 4em: stopped 5em: started 再次访问 http://your-domain 。成功！\n在这里我多次碰到一个很诡异的问题，也记录一下，或许有用。\n当 root 配置位于 location 块之外的时候，Safari 访问程序的子页面正常，但访问主页 http://your-domain 或者 http://your-domain/ 的时候会被跳转到默认主页（就好像虚拟主机没有配置成功），必须使用这样的链接访问 http://your-domain// （多个斜杠）才能成功访问主页。\n有趣的是，Firefox 和 Chrome 都没有这个问题。例如使用下面的配置，Safari 访问会有问题。\n1server { 2 ... 3 server_name your-domain; 4\taccess_log logs/access-em.log; 5\terror_log logs/error-em.log; 6 7 root /srv/www/em/app; 8 location / { try_files $uri @em; } 9 location @em { 10\tinclude uwsgi_params; 11\tuwsgi_pass 127.0.0.1:5000; 12 } 13} 当把 root 放在 location 之内，就没有这个问题了。Safari 和其他浏览器都正常。\n使用子目录提供 Flask app 服务 有时候我们脑袋中会冒出一些 BT 的需求，例如，使用 /em 这个 URL 来提供对 EM 项目的服务，使用 /sexyzone 这个 URL 提供另一个毫不相干的项目的服务。就像这样：\n1http://your-domain/em 2http://your-domain/sexyzone 那么，使用 http://your-domain/em 这个路径来提供 EM 这个项目的服务，应该如何做？\n首先，需要在注册 Blueprint 的时候提供 url_prefix 参数。\n1app = Flask(__name__) 2app.register_blueprint(main_blueprint, url_prefix=\u0026#39;/em\u0026#39;) 这样处理之后，在使用 url_for() 命令的时候，url 会自动加上 /em 前缀。\n例如之前的源码为：\n1\u0026lt;ul class=\u0026#34;nav navbar-nav\u0026#34;\u0026gt; 2 \u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;/\u0026#34;\u0026gt;Home\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 3 \u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;/persons/\u0026#34;\u0026gt;Persons\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 4 \u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;/teams/\u0026#34;\u0026gt;Teams\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 5\u0026lt;/ul\u0026gt; 修改之后会变为：\n1\u0026lt;ul class=\u0026#34;nav navbar-nav\u0026#34;\u0026gt; 2 \u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;/em/\u0026#34;\u0026gt;Home\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 3 \u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;/em/persons/\u0026#34;\u0026gt;Persons\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 4 \u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;/em/teams/\u0026#34;\u0026gt;Teams\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 5\u0026lt;/ul\u0026gt; 其次，需要考虑 static 文件的路径，并没有跟随 Blueprint 的变化而变化，现在 static 内容依然没有前缀：\n1\u0026lt;script src=\u0026#34;/static/bootstrap/jquery.min.js?bootstrap=3.3.6.0\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 2\u0026lt;script src=\u0026#34;/static/bootstrap/js/bootstrap.min.js?bootstrap=3.3.6.0\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 要解决这个问题，需要另一个参数，在创建 Flask 实例的时候增加 static_url_path 参数即可。\n1app = Flask(__name__, static_url_path=\u0026#39;/em/static\u0026#39;) 错误解决 Flask 502 错误解决：upstream sent too big header and invalid request block size\nuWSGI + Nginx 的 HTTPS 支持 uWSGI + Nginx 的 HTTPS 支持 使用 Let's Encrypt 加入全站 HTTPS 支持 相关文章 在 Python+uWSGI 应用中使用缓存 uWSGI+rsyslog 实现 rotating logging Flask+uWSGI 的 Logging 支持 Flask+uWSGI Logging rotate：重要补充 pyzog：uWSGI logging rotate 的终极方案 全文完 ","date":"2016-07-23","description":"","lastmod":"2020-08-15T03:36:16Z","slug":"deploy-flask-uwsgi-nginx","tags":["python","server","flask","uwsgi","logging"],"title":"部署Flask + uWSGI + Nginx","url":"https://blog.zengrong.net/post/deploy-flask-uwsgi-nginx/"},{"categories":["impressions"],"content":"\n书籍信息 《聘谁——用A级招聘法找到最合适的人》 Who: The A Method For Hiring 作者: 杰夫·斯玛特（Geoff Smart） / 兰迪·斯特里特（Randy Street） 出版社: 海天出版社 译者: 任月园 出版年: 2009-7 页数: 185 主观信息 开始阅读：2016-05-31 结束阅读：2016-06-04 我的标签：人力资源、招聘、HR、管理 我的评分：7.0 我的点评 虽然主要讲如何招募经理人级别的人才，但对于日常的技术招聘也有一定的指导意义。对于书中 “所有的问题都是 人 而不是 事 的问题，只有找对了人才可能做好事，坚持聘用A级选手，放弃B和C级选手” 的观点，我十分赞同。然并卵，作为创业公司，我们可能还是需要在一堆B/C级选手中淘出一二三名。\n公司的声望决定了候选人的平均水平。尽管如此，根据自己公司的特点和现状，活用书中提到的 记分卡、筛选面试、3P/5F法则 ，是能够提高招聘效率的。尽信书不如无书，关键是 活用 。\n纲要（目录） 第1章 你的头号难题 10大错误招聘术 呼唤A级选手 聘人决定成败 第2章 记分卡：绘制成功蓝图 使命：工作的实质 成果：任务必达 能力：确保胜任 文化适应性：融入公司 从记分卡到战略 案例：记分卡的应用 第3章 ；物色：招揽选手 从职业圈和人际圈中寻求推荐 员工推荐 委任招聘代表 聘用外部猎头 聘用招聘调研员 系统化管理 案例研究：找到合适的CEO 第4章 选拔：考察选手的4次面试 筛选面试：剔除不合格者 升级面试：选准人才的模式 专项面试：了解更多选手信息 咨询证明人：检验信息真伪 决定聘谁 第5章 说服：“成交”的5大法宝 用“适合”说服 用“家庭”说服 用“自由”说服 用“财富”说服 用“乐趣”说服 说服的5个波段 将说服进行到底 第6章 如何在公司内部运用A级招聘法 避开法律陷阱 组建A级团队 应对新人冲击 其他招聘关键点 你能够成功 摘抄 zrong: 注意，下面的摘抄并不全是原文摘录。我会对某些文字进行改动以保证内容足够简洁和易懂。\nP25\n“人”是你的头号难题。 “事”则不然。 “事”，指的是你采用何种策略，提供哪些产品和服务，运用哪套流程等。你可以把整个职业生涯都花在解决无穷无尽的烦心事上，以为这样就可以推进经营。多数经理人乐此不疲。遗憾的是，只关心“事”不但会增大你的压力，减少你的收入，还会大大牺牲掉你的个人时间。\nP28\n如果经理人触犯以下任何一条，就会聘错“人”：\n不清楚一份工作的要求; 应聘者寥寥无几; 面对一群不分伯仲的候选人，没有把握挑出最合适的; 说服不了看中的候选人加入团队。 P35\n“看，我相中的是你的那份简历，但实际工作的却是你本人，真遗憾！”\nP36-38\n十大错误招聘术\n直觉判断式 聘人时，直觉是最信不过的。\nzrong： 直觉可以在最后出场，在充分研究还无法作出决定时（一般是由于经验不足），如果有直觉感觉该候选人不合适，就要坚决不予录用。（这点在本书后面也有提到） 海绵吸取式 6名考官坐成一排，询问应聘者的潜水爱好。 审讯式 不停提问，试图让应聘者上套或者出现逻辑矛盾。事实上，工作所需的知识和能力同这根本不搭边。 请愿式 面试这不停向应聘者推销工作机会，更重视是否能打动对方而不是考察其能力。 花招式 不久你也许会很尴尬地告诉朋友：那个帮忙把纸捡起来，在Party上表现不错的家伙为什么被你开除了。 宠物评判式 “你想成为哪种动物？”“我在找回答巧妙的人。” 无谓闲聊式 虽然气氛融洽，但对作出正确决策毫无益处。你找的是未来同事，不是邻居。 个性心理评测式 大众心理评测不能预测未来的职场表现。 能力评测式 可以使用能力测试来筛选，但不能忘了其他方法。 预言式 “你会做什么？你将来怎么做？你能做到吗？如果你和同事起了冲突，你会怎么做？”避免这类问题，这必将毫无效果。 P39\nA级选手的定义：他有至少90%的希望实现排名在前10%的选手能够实现的结果。\nzrong： 注意是可能性，因为在该人还没有入职的时候，只能判断可能性\nP42\nA级招聘法，把A的每个边和下划线看作整套方法的4大步骤：\n填制记分卡 记分开并非职位的描述，而是一系列成果和能力的描述。 物色 要在有空缺之前就系统化地物色人才，保证需要时有高素质的候选人补位。 选拔 系统化的面试，对照记分卡作出明确的决定。 说服 正确说服保证你不功亏一篑，在最后一刻失去理想人选。 P48\n记分卡由三部分组成：使命/成果/能力。\nP49\n要想让使命有意义，必须用平实的语言，千万不能是当今商界常见的冗长辞令。\n错误的描述：此岗位的使命是通过改良NPC部门的核心资产并确保最小沟通缺陷来最大化股东价值。 正确的描述：拿出领导者的远见，分析市场，制定新的战略，提供新的产品服务，帮助银行成功争取到市场份额。 P51\n别请通才，聘请专才。\n使命，不是帮你找来能指出问题的通才，而是要帮你请到能够解决问题的专家。\n岗位需要聘请新人的时候，不要直接翻出以前的使命陈述来用。记分卡需要不断调整以适应变化。\nP53\n成果，记分卡的第二部分，描述了一个人在岗位上必须干出什么。多数岗位需要诞生3-8项成果，可按重要性排序。上面的记分卡样本中，候选人要么做得到，要么做不到。A级选手能做到，但B/C就做不到。制定合理的高标准成果，你就会吓跑B级和C级选手，同时引来A级选手。\n要尽量将成果定得客观、可量化。\n有了这些明确的规定，新官一上任就能大施拳脚。他们知道别人将从哪方面评估自己，也知道公司和老板最看重什么。他们无须猜测怎样才能干好，也无须犹豫该从哪方面出击，因为行动策略已经摆在面前。这不是限制人，而是给人发挥的空间。\nP55-57\nA级选手的关键能力\n高效，诚实/忠诚，擅长组织规划（迅速高效做好规划，分清轻重缓急），进攻进取（行动迅速，姿态强势，但并不粗鲁无理），兑现承诺（口头和书面均兑现，不计较个人投入），智慧，善于分析，专注细节（不忽略足以毁掉工作成果的细节），坚韧（有始有终），积极主动（不用交代就去做），能够聘用A级选手（适用于经理人，主动寻找选拔和说服），能够培养人（适用于经理人），具有灵活性/适应性，抗压能力强，有战略思维和远见，有创造性/善于创新，热情洋溢（热爱工作且充满激情），具有良好的职业道德（努力完成，有时加班，勤奋），高标准要求，善于倾听，敞开胸怀接受批评和想法（冷静对待批评和负面反馈），善于沟通（口头书面和电子邮件清晰，不冗长），富有团队精神，具有说服力。\n实践中，人们可以殊途同归——方法相异，成果相同。因此，我们建议你不要让能力要求过于狭隘。\n再聪明的员工，如果自我膨胀、闭耳塞听，也会沦为无能之辈。\nP58-64\n能力有两个层面：一是拥有岗位所需的技能并做出相应行动，二是满足企业文化的整体需求。\n考察对方的文化适应性先要了解自身企业的文化，这需要花费时间、精力，但了解后会让企业大为受益，不仅仅是局限于招聘过程。\n考察文化适应性就意味着剔除那些不能融入企业文化的人。\n要想招聘成功，就得有规定确保剔除那些不适合的“高人”。\n记分卡保证你不仅聘到A级选手，还要他们做出A级成绩。\n你会用哪个形容词来描述我们的企业文化？在活动挂图或者白板上记下来，要不了多久，企业文化就能清晰呈现出来。\n记分卡是保护组织文化的卫士。它在纸上描摹整个公司的动态经营，并确保你将之与具体招聘岗位联系起来。\n几年前，在《财富》杂志（Fortune）组织的会议上，我们询问200名与会CEO：“你们当中有多少人位下面直接汇报工作的经理人制定了书面目标？”只有10%的人举手。\n记分卡可以：\n为新人确定愿景 监督员工慢慢进步 量化年度评估系统 在评估人才的同时考察团队水平 记分卡迫使经理人作出选择，并始终坚守自己的选择。\nP66\n如何填制记分卡\n使命。构思1-5句简短陈述，描述岗位存在的必要性。 成果。构思3-8项某人必须作出的A级成绩必须实现的具体、客观的成果。 能力。确认为实现岗位成果所需的行动能力。接着，明确5-8项用于适应企业文化的能力。 确保工作协调一致，清晰传达记分卡内容。 P69-P83\n广告能引来大量简历，但却引不来最合适的人。最好的物色办法是从你的人际圈和职业圈中征询推荐。\n积极运用熟人推荐这一方式来编织人才网络，然后追踪高潜力候选人，建立并维护与其的关系。他不断运用自己的物色网络并及时更新。这样一来，当有职位空缺时，根本不需要临时抓人。\n公司内部推荐跟外部推荐一样有价值，并且更具有针对性。\n把内部推荐的要求填进员工记分卡。内部推荐的最大好处是它可以改变整个企业中员工的心态。员工会变成“星探”，大家开始更加关注公司的“人”，而不只是“事”。\n跟踪他们的情况并建立联系。选一个固定的时间，跟你物色的人才打电话。\n花点时间选拔和培训合适的猎头。确保他了解你的需求和文化，同时借机好好向其学习。\n要始终参与其中，毕竟，是你要用人才。\n物色人才方式：从职业圈和人际圈中寻求推荐，员工推荐，聘用猎头，委任代表，聘用调研员，形成物色体系。\nP88-P92\n筛选面试通常为简短的电话面试。旨在快速清除“杂草”，这是唯一目的。\n筛选面试提问指南：\n你的职业目标是什么？ 你有何职业专长？ 你在职业上不擅长什么，或对什么不感兴趣？ 请说出你过去的5 位老板。如果按1 ～10 分来打分，当我们给你的老板打电话时，他们各会给你打多少分？ 英才知道自己想做什么，并且无惧于告诉你他的目标。\n要他们举例说明自己的强项是如何发挥作用的。如果对方的强项跟你记分卡上的要求相距十万八千里，请毫不犹豫地将此人清除出候选人名单。\n如果你听到模板式答案，只要说：“我怎么觉得这像优点呢。你真正不擅长什么，或者对什么不感兴趣？”聪明人会领会，重新给出回答。\n如果你难以套出实话，我们建议你拿证明人来威慑对方。你可以说：“如果你通过这轮面试，我们会要求提供一些证明人——你的老板、同事或下属，可以吗？”“我想知道，他们觉得你不擅长什么，或者对什么缺乏兴趣？”\n如果他不愿意多讲，或者缺点全是“明贬实褒”，或者与记分卡要求相左，请把此人剔除出备选名单。\n“当我们给你老板打电话时，他们会给你打多少分？”而不是“如果我们给你的老板打电话”，是“当……时候”。雕琢问题对发现真相至为关键。\n你要找的是8分、9分甚至10分者，7分是中等成绩，6分就是差。给自己打6分的人通常在实际上只能得到2分。\n如何打筛选电话\n打电话前，先看记分卡，刷新记忆。打电话时，先讲清安排：“我一直盼望与你通话，我是这样打算的。先花30分钟了解一下你的情况，然后，你可以提问，我很高兴作答。这样你也可以了解我们，好吗？”\n如果你对听到的不感兴趣，加速提问让通话快点结束，通常，如果一开始感觉不好，我们就会在15-20分钟内结束谈话。反之，可以问他有无更多时间或者是否愿意多谈。\n结束电话前，要给对方提问的机会。根据前20分钟所了解的，你可以更好地介绍公司，激发对方兴趣。反之可以言简意赅。记住：你是过程掌控者——你可以把搜集的信息和记分卡相对照，根据实际情况来决定延长还是压缩时间。\n面试后问自己：“此人的强项符合记分卡要求吗？他的弱点在可接受范围内吗？我会迫不及待让此人参加后续面试吗？”\n如果你有任何犹豫，或你觉得还需要进一步考察，那么，毫不留情地将其剔除。只邀请那些自身情况跟记分卡要求强烈吻合的候选人。\nP93-P96\n充满好奇，可以问：“如何”“告诉我更多”来追问，你是什么意思？那是什么情况？发生了什么事？有什么好例子吗？你扮演什么角色？你做了什么事？你老板说了什么？结果是什么？还有什么别的吗？你是如何做的？那情形怎么样？你感觉如何？你攒了多少钱？你是如何处理的？\n我认为直觉可以帮你剔除错误人选，但要雇谁就不能光靠直觉判断了。当大家的简历都很棒，你难以取舍的时候，如果觉得这个人比较别扭，或者不能完全信任他，就毫不留情将其拒之门外。\nP98-105\n升级面试提问指南：\n聘你去是做什么的？ 你最骄傲的成就是什么？ 做那份工作的低谷是什么？ 你跟谁一起共事？具体来说： （1）你老板叫什么名字，怎么拼写？跟他一起工作感觉如何？他将告诉我你的最大强项是什么，又有哪些不足？\n（2）如果按A、B、C三级来评判的话，你会给曾加入的团队评几级？你为团队带来何种变化？你聘人了吗？你炒人鱿鱼了吗？当你离开时，按A、B、C三级评判，你又会给该团队评几级？ 你为何终止那份工作？ 引导其回答如何衡量工作的成功。在脑海里勾画他的记分卡：使命和关键成果是什么？哪些能力最重要？\nA级选手喜欢谈论岗位要求的成果，B级和C级选手则总爱谈论事情、遇到的人、喜欢工作的哪方面等等，提都不提结果的事。\n先问候选人的老板姓甚名谁。要他说清该怎么写，并故意当面记下来。接下来，问他跟其老板一起工作感觉如何。\n不管名字多么普通，逼着候选人说清怎么写，都会向他强烈传递：你会给他的老板打电话，所以最好还是说实话。\n现在问问“史密斯讲说你的最强项是什么，又有哪些不足？”一定要用“将说”，而不是“可能说”。\n你会给曾加入的团队评几级？这个问题适合招聘经理人。当他加入一个团队的时候，是接受这个团队原本的做法，还是发动变革，让团队做得更好？他们会作出何种改变？需要多少时间？另外，可以用团队威慑候选人说实话：“当我询问贵团队成员时，他们将怎样评价你作为经理的强项和不足？”\n坚持互惠。互惠最能引人吐露实情。\n最后一个问题能洞悉内幕。他们是按职业生涯规划跳槽，还是追逐什么东西？他们自己怎么想？老板对其离去又怎么看？\n务必搞清选手是做得成功但主动离开（如A级选手），还是“掉队”（如B级或C级选手）而被老板“请”走。别接受那种含混的回答：“我跟老板不打交道。”这等于白说。要充满好奇，找出原因，紧咬不放，直至看清到底是怎么回事。\n在实践升级面试时，可把一个人的职场故事划分为“章”。每一章是单独的一份工作，或三五年内做的几份工作。\n我们一再强调，顺序很重要。不要使用“倒推法”，先问现在，再问从前，这样会让选手思维紊乱。要按时间顺序梳理整个职业生涯，使其重视。选手会沉浸进去，告诉你们他的故事。在聆听的过程中，你会感觉他们的职业画卷在你眼前逐渐展开。\n通常，升级面试需要3个小时。为市值数十亿美元的大公司面试CEO需要5个小时，面试初级职位需要90分钟。\n最终时长将由两点决定：（1）选手职业生涯的长短；（2）你划分的章数。\n开始时，你可以运用下面的开场白：\n谢谢你今天过来。我们已经说过，会按时间顺序了解你的工作经历。对每份工作，我们会问5 个核心问题：聘你去是做什么的？你最骄傲的成就是什么？做那份工作的低谷是什么？你跟谁一起共事？你为何终止那份工作？面试结束时，我们会了解你的职业目标和渴望，你也有机会提问。\n面试中，80％ 的时间是在这个房间进行。如果双方感觉良好，我们会打电话咨询你提供的证明人，以完成此轮面试。\n面试听起来好像很长，但会进行得很快。我需要保证你有机会说全自己的工作经历，因此会掌控整个交谈的节奏。有时候，我们会要你多说些某阶段的工作情况；有时候，又会要你讲下面的。我们会保证留出足够的时间，了解你最近且最相关的工作。对面试过程，你有疑问吗？\nP107-110\n经典策略1：学会打断选手。 一旦话题脱轨，马上拉回来。每3 ～4 分钟你就需要打断一次，对此要做好心理准备。保持高度融洽有利于得到最珍贵的信息，因此，你要学会“礼貌”地打断。\n经典策略2：运用3P法则。 如何辨别对方所说成就的大小？运用“3P”法则吧。这3 个“P”就是跟以前（Previous）比、跟计划（Plan）比、跟同事（Peers）比，可以帮你明确做出的成就到底有多大价值。\n经典策略3：分清“排斥力”和“吸引力”。\n排斥力：\n离职既有我的原因也有老板的原因 我该离开了 我和老板不合 我没有完成业绩，位置岌岌可危 我打了CEO 吸引力：\n我最大的客户聘请我 从前的老板找我回去担任更重要的工作 那个CEO给我连升两级 以前的同事去了竞争对手哪里，向他的老板推荐我 经典策略4：描绘景象。 若你脑海中能勾勒出景象，那么就理解了选手的话。你必须钻进对方心里，弄明白他们为什么那样处理问题。\n例如，选手说她擅长交流。千万别自行理解，要充满好奇弄个明白。你可能得知：（1）她擅长起草公文，负责公司的往来函件和市场宣传资料。（2）她却不善言辞。这些事实帮你穿过“擅长交流”的表面，洞悉她的真正能力。\nzrong: 可以提问“你擅长哪方面的交流？”\n经典策略5：通过观察对方的肢体语言来发掘真相。\n*P111-125\n专项面试提问指南：\n此轮面试的目的是谈论______ （填上具体的成果和能力要求，如开拓新客户的经验，组建和领导团队，制定战略计划，积极进取，持之以恒地行动等）。 职业生涯中，你在这方面的最大成就是什么？ 在这方面，你犯下的最大错误和得到的教训是什么？ 专项面试针对记分卡上的成果和能力两项。\n可以列出6项能力，安排3名招聘团队成员按照记分卡能力要求组织专项面试。每3人考核记分卡上的成果和几项能力。\n要想成功咨询证明人，你要做到以下三点：\n第一，选好证明人。总共咨询7人，7位可选3位老板，2位同事或者客户，2位下属。 第二，要候选人联系证明人进行电话预约。 第三，咨询人数要够。你亲自打给4位，另外3位同时代劳。 “您可能认为”会让证明人觉得：候选人主动坦白，所以自己可以放心谈论。\n不要想当然地接受候选人提供的证明人。\n听出弦外之音：鉴别证明人所述，辨清可疑选手。“唔”、“呃”是另一种话语隐藏。冷漠和勉强称赞也表示应聘者能力欠佳。\n咨询证明人提问指南：\n你跟应聘者是在什么情况下共事的？ 此人的强项是什么？ 当时，此人最该弥补的不足是什么？ 你怎么评价他在那个岗位上的总体表现？请按1-10来打分。你为什么给他这个分数？ 此人提到他做这份工作，遇到的困难时 。你能给我讲详细点吗？ 红旗警戒\n闭口不提过去的失败 回答时夸大其辞 把别人的功劳视为己有 说过去老板的坏话 说不清为何要换工作 身边最重要的人不支持他换工作 应聘管理职位的选手从未招聘或解雇过人 对薪酬福利比对工作本人更感兴趣 总是摆出“专家”面孔 过于关注自我 畅销著作《魔鬼管理学》中诊断出N种可毁掉经理人职业生涯的破坏性行为：\n爱当常胜将军；（要当然那些不分轻重缓急只想取胜的人，他会让你和同事投入巨大精力干芝麻大的小事） 过度贡献； 面试中，说话以‘不’、‘但是’和‘然而’开头； 向世界证明自己有多聪明； 贬低以前的同事； 推诿责任； 爱找借口； 老强调‘我就是’怎么样。 P126\n如何选拔A级选手？\n筛选面试：进行20 分～30 分钟的筛选面试。提问4 大关键问题。使用“什么”、“如何”、“告诉我更多”等了解更多信息。把发现的B 级和C 级选手剔除在外。 升级面试：进行1.5 小时～3 小时的升级面试，按顺序了解选手的整个职业生涯，对每份工作或工作历史的每章提出5 大问题。招聘经理可同另外一名同事共同给予面试。 专项面试：给团队人员分配任务，让他们参与面试，主要考察选手符不符合记分卡上对成果和能力的要求。 评估选手：每天面试结束时，使用“技能—意愿”图来对照记分卡。筛选出技能（擅长做的事）和意愿（想做的事，喜欢的文化）符合记分卡上使命、成果和能力要求的选手。寻找在关键成果和能力上得A 的人。人无完人，请选中那些符合记分卡上最关键要求的选手。 咨询证明人：从升级面试获取的证明人中挑选7 位，打咨询电话。让选手帮你预约联系，减少咨询的阻力。 最终决定：再次审视“技能—意愿”档案，确保要聘的人选档案能组成牛眼图。 P129-145\n说服选手的“5F”法宝：\n适合（fit）：公司的愿景、需求和文化与选手的目标、强项和价值观一致 家庭（family）：家庭成员很在乎换工作造成的影响 自由（freedom）：加入后可独立自主工作 财富（fortune）：反映公司的稳定性和整体盈利情况 乐趣（fun）：描述未来的工作环境和同事关系 你会努力为他们创造条件，这跟别的只关注“适不适合我们”的人不同。\n你必须给他们介绍公司。你得推销公司，推销公司的愿景和潜力。有才之士从不会轻易“下嫁”既无潜力又跟自身目标和能力不搭界的公司。\n让选手和家庭成员四处看看，见见工作人员，感受一下文化。\n5F法宝不是用来操纵人的，而是临近招聘尾声的时候，你应该带着一颗真诚之心多加关注的方面。\n乐趣就是有机会施展出全部才华，用上所有经验。\n物色过程中，多关注选手的兴趣和才华，就有机会鉴别选手最注重“5F”中的哪个。\n保持沉默会让你痛失英才。如果经理人没有和对方保持联系，对方会觉得受冷落。\n我们的人员知道怎么获得奖金。奖金和定额目标以及其他8项明确目标联系起来。\n在对方答应考虑是否加盟后，送些礼物以示心意，如鲜花、气球或奖券，保持紧密联系。\n跟他们保持联系。使用5F法宝来表示公司对他们的关心。告诉他们多么适合，来公司能作出多大贡献。关爱他们的家人，承诺给他们工作的独立自主权。消除他们的经济顾虑。让他们分享公司营造的乐趣。\n最能加盟公司的因素是坚持！\n说服的五个波段：\n物色时 面试时 录用后选手考虑接受工作时 选手接受之后至到岗之前 新人上岗后头100天 P149-158\n竞争最激烈的地方也蕴藏着最大的机会。\n哪些因素最能影响经营成果？答案一半以上是“管理才能”。其他方面：执行只占到20%；战略份额更少，只占到17%；外部因素只占11%。\n关键的差异是执行，而它需要“人”来做。\n推行A级招聘法，做到10件事：\n把“人”视为重中之重 亲身践行A级招聘法 争取管理层或同事们的支持 给团队描绘清晰的愿景，并在每次沟通时强化它 培训团队做到最佳 清除成功绊脚石 制定新政策，为新办法推行保驾护航 发现并奖励那些使用该方法取得圆满成果的人 换掉不合作的经理人 庆祝胜利，争取做得更好 使用A级招聘法，并不需要你是CEO。你可以在现有岗位和部门轻松推行这项方法。\n不要违反法律，做到以下4点：\n不要因与工作无关的原因拒绝候选人。 不管面试哪类人群，请使用同一招聘流程。经理人有意无意地区别对待会给自己惹来麻烦。 请说“他/她”，这显示没有预先设定好岗位性别（没有歧视）。不要对人使用不敬的语言。 不要问违法问题。例如隐私和个人生理缺陷等等。 A级选手并非“全能运动员”。A级选手是能实现记分卡上规定目标的人。这些目标，只有10%的人能够实现。\n虽然引进的A级选手需要调整自身以适应公司的文化，但是，企业文化自身也需要有一定弹性，以迎接A级选手带来的冲击，尤其在需要改变的领域。你必须营造一种“支持性”文化，给人空间，容许人的个性上有所不同。\nP159-166\n什么样的CEO最为投资人赚钱？\n绵羊成功的概率是57%，猎豹的成功率是100%。\n仅仅根据某人在现岗位上做得如何，而不是评估他能否胜任未来岗位来招聘是错误的。\n请关注“人”，别光盯着“事”。这是实现职业成功、获得财富和幸福的正确方法。\n全文完 ","date":"2016-06-05","description":"","lastmod":"2016-06-05T02:32:40Z","slug":"who-the-a-method-for-hiring","tags":["reading","management","readingnote"],"title":"【读书笔记】聘谁","url":"https://blog.zengrong.net/post/who-the-a-method-for-hiring/"},{"categories":["technology"],"content":"用 Homebrew 安装的 MySQL 5.7.12 莫名其妙就挂掉了，翻看系统日志，看到一堆这样的提示：\nJun 2 20:08:49 zrong-mbp com.apple.xpc.launchd[1] (homebrew.mxcl.mysql) : Service only ran for 0 seconds. Pushing respawn out by 10 seconds.\n这样的提示每10秒一次，从中午一直到现在。\nMySQL 是使用 Homebrew 提供的 servies 子命令启动的：\n1sudo brew services start mysql 这条命令做的事情比较简单，大概是这样：\n1cp /usr/local/opt/mysql/homebrew.mxcl.mysql.plist /Library/LaunchDaemons 2sudo launchctl load /Library/LaunchDaemons/homebrew.mxcl.mysql.plist 我在 在 Mac OS X 上安装lighttpd 中做过介绍。\n折腾了几次之后，我又发现一条这样的信息：\nJun 2 20:11:08 zrong-mbp com.apple.xpc.launchd[1] (homebrew.mxcl.mysql) : This service is defined to be constantly running and is inherently inefficient.\n这一下让我找错了方向，有人说这是因为 Apple suggest avoiding KeepAlive and RunAtLoad 导致的。我删除了 homebrew.mxcl.mysql.plist 中的 KeepAlive 和 RunAtLoad ，当然是没有什么卵用。\n接着看 /usr/local/var/mysql/{your-mac}.err ，我才发现真正问题所在：\n2016-06-02T15:11:34.350795Z 0 [ERROR] InnoDB: The innodb_system data file 'ibdata1' must be writable\n注意这个log文档中的时间是格林威治时间。\n原来是因为文件不可写导致的。看看这个 err 文件的权限是 _mysql:admin ，于是将整个 /usr/local/var/mysql 文件夹和之下所有文件权限改为相同即可：\n1sudo chown -r _mysql:admin /usr/local/var/mysql （全文完）\n","date":"2016-06-02","description":"","lastmod":"2016-06-02T15:37:28Z","slug":"homebrew-mysql-no-start-in-ei-caption","tags":["osx","mysql"],"title":"Homebrew 安装的 MySQL 在 EI Capitan 上无法启动","url":"https://blog.zengrong.net/post/homebrew-mysql-no-start-in-ei-caption/"},{"categories":["technology"],"content":"最近用 Alfred 写了一些 小工具 ，涉及到网络操作。\n由于我的电脑 睡眠耗电 的问题，不得不在睡眠前关闭 Wi-Fi 网络。在 OS X 中，有这样一些命令与网络操作相关。\nairport 是一个隐藏的命令，路径如下：\n1/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport 使用它可以做一些网络相关工作，例如使用 airport -I 可以获取当前WIFI的信息：\n1 agrCtlRSSI: -55 2 agrExtRSSI: 0 3 agrCtlNoise: -93 4 agrExtNoise: 0 5 state: running 6 op mode: station 7 lastTxRate: 217 8 maxRate: 217 9lastAssocStatus: 0 10 802.11 auth: open 11 link auth: wpa2 12 BSSID: 0:8:2f:b1:00:99 13 SSID: Baina-WiFi 14 MCS: 23 15 channel: 161 使用 airport -z 可以断开当前的WIFI连接。就像我们按住 ALT 点击无线信号图标可以做的那样。\n不幸的是，airport 是一个隐藏工具，按照 Apple 的尿性，我们并不知道什么时候它会消失。而且，一些操作必须使用 sudo 权限才可以处理，这不太方便。\n在 OS X 中，使用 networksetup 工具是最方便的。例如，要关闭 Wi-Fi 网络，可以这样调用：\n1networksetup -setairportpower \u0026#39;Wi-Fi\u0026#39; off 显示结果为：\nWi-Fi is not a Wi-Fi interface. Turning on the only airport interface found: en0\n因此，也可以这样调用：\n1networksetup -setairportpower en0 off 开启 Wi-Fi 网络，则可以这样写：\n1networksetup -setairportpower en0 on 使用 ifconfig 也能实现类似功能：\n1# 关闭网络 2sudo ifconfig en0 down 3# 启动网络 4sudo ifconfig en0 up 但同样的，ifconfig 需要 sudo 权限，因此没有 networksetup 那么方便。\n（全文完）\n","date":"2016-05-30","description":"","lastmod":"2016-05-30T05:48:27Z","slug":"osx-network-command-line","tags":["osx"],"title":"OS X 中使用命令行来管理网络","url":"https://blog.zengrong.net/post/osx-network-command-line/"},{"categories":["impressions"],"content":"\n程序员都讨厌开会，我也讨厌开会。\n《重来：更为简单有效的商业思维》 一书中对于开会有一段这样精彩的描述：\n通常会议只是文字和抽象的内容，没有实质。 通常会议每分钟只传达出极为少的信息。 人们在会议中容易跑题堪比暴风雪里的芝加哥出租车。 会议要求做充分的准备，但是大多数人没有时间做这个。 频繁地提出模糊的议程而没有人能真的清楚目标是什么。 常常会出现至少一个傻瓜不可避免的，毫无意义的浪费大家时间。\n上面列出的是低效失败会议的通病。就像那句著名的话一样：所有的糟糕的会议都是一样的，而完美的会议则根本不存在。\n等等，我好像说错名言了。\n记得我刚入职场的时候，经常向老大抱怨开会的频率和时间，老大很郑重的对我说：“我也讨厌开会。但很多问题，只有开会才能解决”。之后的时间里，每次碰到让人抓狂的会议，我都会常常想起这句话，并用会议的议程去印证这句话。现在如果要我对这句话做个评价，它会是这样的：“这句话极有道理。但是，只有高效的会议才可能解决真正问题”。\n如今，我也到了当年我老大的年纪。如果要我重讲这句话，我会改成这样：“我也讨厌开会。所以我只开一定要开的会”。\n什么会是一定要开的？在我心目中，一定要开的会符合下面几条原则：\n有明确的组织者，为了一个明确的目标，组织者对会议内容和效果负全部责任； 会议内容精准，会议流程有条理，发言人不讲废话，主持人不讲套话； 有明确的时间限制，并且严格执行； 限制每位发言者的时间，少安排讨论，若有讨论则必须限制时间； 阶段内容不相关者离会； 每次会议不超过1个小时。 为了达到上面的标准，还需要做以下几点：\n不浪费参会人员的时间。在会议中禁止刷手机或打盹的行为。刷手机代表已经不再关注会议的具体内容。有两种情况：一是认为会议的内容与己无关，二是认为手机中的内容更加重要。如果会议邀请你来参加，那么一定与你有关。所以无论如何都要全神贯注听下去。如果真的与你无关，那么可以申请离会。主持人应该了解在哪个会议阶段，哪些人可以例会，并主动提醒。一个一定要开的会不应该浪费参会人员的时间，这是对参会人员起码的尊重。 不允许开小会。在会议中的任何观点，都可以讨论。讨论应该是面向所有人，因此没有开小会的必要。如果一定要在会上讨论，那么应该在会议的讨论时间大声说出来。否则，就会后再讨论。 可以讨论，不允许争论。讨论是在寻求真理，而争论则是为了说服对方。再通俗一点，讨论是为了解决问题，而争论则是为了证明自己是对的。会议不是辩论赛，争论应该由双方关起门来吵完了再统一到会上，那是会前的工作，而不是会议上要解决的问题。即使是讨论，也需要严格限制时间。有些人废话特多，有些人一说起来就没完没了。主持人需要提醒这类人说到点子上，并在时间到了的时候直接打断。形成习惯之后，每个发言人都能意识到会议时间的宝贵，并自发改进自己的会议行为。 要有会前准备。如果会议中有讨论什么问题，主持人要确保所有的参会者都事先拿到了资料并阅读过了。如果连要解决的问题都不清楚，开什么会？所有的准备工作要在会前完成，会议上的时间是宝贵的，不是用来搞初级培训的。会议中需要展示的图表、PPT都必须事先准备好。许多会议开始后10分钟内还在开电脑，调整投影仪，整理PPT……这只能说明会议的组织者对会议不上心，浪费的就是所有与会者的时间。 准时开始，准时结束。参会人员总是迟到，主要因为会议的组织者造成的。就像上面一条说的，如果大家认为会议开始总是要调整一下设备说几句不痛不痒的废话，谁愿意先来听废话呢？会议如果不能准时结束，就会影响所有与会者的后续工作安排。在会议中弄个定时器，到时间直接打断是很有必要的，这不但能降低发言人的废话率，还能保证所有参会者思路清晰，心情愉快。 :) 要有会后Review。最糟糕的事情莫过于：会散了，参会者还是该干嘛干嘛。会议结束前，对会议中讨论的每件事情，必须要指定明确的责任人。由谁继续推动？什么时候完成？完成的质量是怎样的？在会议结束之后，还有建立Review机制，保证会议的结论能够落到实处。这是会议最重要的一步，否则这会就等于白开。 让我们看看上面提到的《重来》一书中是如何让会议变得有成效的：\n用计时器。当它响起，会议结束。句号。 尽量减少与会人员。 保持清楚的议程。 开篇点题。 就地解决。指出实际问题，提出实质改变。 作出结论，找个靠谱的人去执行。\n因此，我更喜欢开小会，而不是开大会。就某一个问题，约个时间，直接到工位上讨论。短则几分钟，长则半小时，直接解决问题，让结论落地。\n但是，总有些事情必须让所有人明确的知道（有些重要的事情，你以为大家都知道，其实往往只有你自己知道），这就是动用一定要开的会的时候。\n尊重所有与会者的时间，他们也会尊重会议的组织者。\n（全文完）\n","date":"2016-05-02","description":"","lastmod":"2016-05-02T15:07:20Z","slug":"must-meeting","tags":["management"],"title":"一定要开的会","url":"https://blog.zengrong.net/post/must-meeting/"},{"categories":["technology"],"content":" 2016-10-05更新： 加入macOS Sierra 的情况。 2016-11-05更新： 加入关闭 Wi-Fi 的工具下载（Workflow for Alfred2）。 经实测，我的机器(rMPB2015, EI Capitan)睡眠一晚耗电超过10% 。这显然是不正常的，许多网友都有此问题 。\n我的配置如下图所示：\n跳过废话直接看解决方案\n[TOC]\n影响睡眠耗电的因素 一番折腾，我发现有多种因素会影响睡眠中的耗电：\napp 阻止和唤醒\n可以在下面的截图中看到每个app是否被允许唤醒。\n蓝牙唤醒\n在下面的设置界面中可以关闭蓝牙唤醒。\n网络唤醒\n网络唤醒可以在 节能器设置 中取消。取消之后，可以看到“查找我的Mac”功能出现问题。\n节能器设置 在节能器设置中，可以设置电池和电源适配器的选项。需要注意的一点是：“唤醒以供WIFI网络访问”一项虽然是在电源适配器中设定，但依然对电池设定起作用。\n将“唤醒以供WIFI网络访问”一项禁用，可以避免电脑被其他网络任务唤醒。在 OS X：通过睡眠节省能源 中有详细介绍。\npmset 使用pmset 这个命令可以对省电功能做深入详细的设置。可使用 man pmset 查看完整用法或者点击 链接 。\n使用 pmset -p custom 查看当前的节能设置。例如我的机器上是这样的：\n1% ~ pmset -g custom 2Battery Power: 3 lidwake 1 4 autopoweroff 1 5 autopoweroffdelay 14400 6 standbydelay 10800 7 standby 1 8 ttyskeepawake 0 9 hibernatemode 3 10 powernap 0 11 gpuswitch 2 12 hibernatefile /var/vm/sleepimage 13 displaysleep 3 14 sleep 3 15 acwake 0 16 halfdim 1 17 lessbright 1 18 disksleep 10 19AC Power: 20 lidwake 1 21 autopoweroff 1 22 autopoweroffdelay 14400 23 standbydelay 10800 24 standby 1 25 ttyskeepawake 0 26 hibernatemode 3 27 powernap 0 28 gpuswitch 2 29 hibernatefile /var/vm/sleepimage 30 womp 1 31 displaysleep 10 32 networkoversleep 0 33 sleep 0 34 acwake 0 35 halfdim 1 36 disksleep 0 Battery Power（电池）和 AC Power（电源）的信息是分开的。\n网上很多文章提到设定 hibernatemode/autopoweroff/standby 来减少硬盘写入等等，我认为没有必要，OS X 的默认设置已经相当好了。\n重置 SMC 在取消蓝牙和网络唤醒之后若还耗电，应该先试试 重置 Mac 上的系统管理控制器 (SMC) ：\n将 Mac 关机。 将 MagSafe 或 USB-C 电源适配器连接到电源和 Mac。 在内建键盘上，按下键盘左侧的 Shift-Control-Option 键，然后同时按下电源按钮。 松开所有按键，然后再次按下电源按钮以开启 Mac。 要确定上面的组合快捷键是否生效，可以观察在按下快捷键的时候，MagSafe 指示灯的变化。在我的机器上，指示灯会从橙色（充电状态）跳转到绿色，然后又跳回橙色。\n查看唤醒原因 我重置SMC后依然没有什么卵用，于是采用下面的命令查询原因：\n1% ~ syslog |grep \u0026#34;Wake Reason\u0026#34; 2 3Apr 25 22:00:32 zrong-mbp kernel[0] \u0026lt;Notice\u0026gt;: ARPT: 185462.901438: ARPT: Wake Reason: Wake on TCP Timeout 4Apr 25 22:12:36 zrong-mbp kernel[0] \u0026lt;Notice\u0026gt;: ARPT: 185496.250029: ARPT: Wake Reason: Wake on TCP Timeout 从log中可以看到，我的机器每10分钟被TCP超时唤醒一次。唤醒的原因是ARPT。 Yosemite Battery Issues. 说明了除ARPT外的其他唤醒原因。\n但是，我没有找到资料说明ARPT是什么含义。\n我也没找到TCP超时的原因。这篇14页的讨论也没能给出一个具体的解决方案： rMBP 2015 Wake Reason: ARPT (Network) ，问题似乎是集中在 EI Capitan 版本中。\n终极方案 最有效的但是很low的解决方案是：在睡眠之前手动关闭WIFI连接 。\n最终方案就是最简单的方案。真是愚蠢的人类啊…… 还深究个毛线！\n2016-11-05更新：\n为了方便使(tou)用(lan)，我写了一个 Alfred Workflow ，用起来是这个样子的：\n这个 Workflow 直接调用 macOS 的命令行来关闭/打开 Wi-Fi。具体介绍可以看这里：OS X 中使用命令行来管理网络 。\n下载这个 Workflow ：\n1 文件 源码下载： UCC.alfredworkflow 。\n要对源码进行开发，请自行安装其依赖 alfred-workflow 。\n这个解决方案很早就写成了，今天看到评论中 wujunchuan 网友的文章，才想到应该把这个方案共享出来。wujunchuan 也提出了 更自动化的方案 。\n2016-10-05更新：\n升级 macOS Sierra 之后，情况似乎有所好转。目前观察到的情况是，Sierra 在休眠之后会自动禁用 Wi-Fi 连接。唤醒时，可以短时间内看到 Wi-Fi 连接是断开的，然后就自动启用了。然而这并没有什么卵用，每晚耗电还是在 3% 左右。\n参考 rMBP 2015 Wake Reason: ARPT (Network) Yosemite Battery Issues. 如果 Mac 未按预期睡眠或唤醒 Determine Why Your Mac Wakes Up From Sleep 增强的通知功能可以将 Mac 唤醒 关于“请求时唤醒”和 Bonjour Sleep Proxy OS X：通过睡眠节省能源 （全文完）\n","date":"2016-04-28","description":"","lastmod":"2016-11-05T15:32:43Z","slug":"osx-saving-energy","tags":["osx"],"title":"MacBook Pro Retina 睡眠耗电现象深究","url":"https://blog.zengrong.net/post/osx-saving-energy/"},{"categories":["technology"],"content":"QCon2016三天我的感受：\nQCon2016全球软件开发大会随笔（一） QCon2016全球软件开发大会随笔（二） QCon2016全球软件开发大会随笔（三） QCon2016资料：\nQCon2016（北京）幻灯片下载合辑\nQCon2016（北京）视频合集\n","date":"2016-04-27","description":"","lastmod":"2016-04-27T01:37:22Z","slug":"qcon2016d","tags":["conference"],"title":"QCon2016视频和PPT资料下载","url":"https://blog.zengrong.net/post/qcon2016d/"},{"categories":["technology"],"content":" QCon2016全球软件开发大会随笔（一） QCon2016全球软件开发大会随笔（二） 今天是QCon2016的最后一天，我就不说天气了，反正比武汉好。照例上两张图吧。\n今天一共44场演讲，使用和昨天一样的 10M听课法 听了8场，对我有意义的列在下面（按时间排序）。\n移动端 IM 开发如何拥有 SaaS 级体验 架构选型之道：如何选择靠谱的 Javascript 应用架构？ Facebook 的项目开发流程和工程师的绩效管理机制 Rust 语言的核心优势和核心竞争力 见微知著，技术人如何选择靠谱的合伙人？ 老树新花——Lua在聚划算App动态化中的应用 编号为1的演讲者是一个提供IM级别云服务的厂商。碰巧他们的服务我也使用过，就是融云。虽然由于各种原因我们改用了其它的服务，但这次的演讲的确是很精彩的。融云技术副总裁杨威在演讲中分享了低延迟的IM服务的协议选择、服务器优化和版本迭代中的宝贵经验。看得出很多经验都是在一线开发中积累下来的。杨威在演讲的时候抛出了很多语录级别的句子。例如：“程序员要像产品经理一样思考，产品经理则要像COO一样思考”；“让程序员交流的最好方式，就是不要交流“；“快速迭代是互联网精神，正确迭代是开发者的服务精神”等等。这些句子很值得深思。\n和某厂商派了个副总像没吃饱饭一样讲满45分钟但却啥也没说比起来，杨总太厚道了。我是实在听不下去才换场次听杨总的报告的，后来和听完那场没吃饱饭报告的哥们印证，我的10M听课法还是相当管用的。\n编号为2的演讲者是Dojo的作者之一Dylan Schiemann。老外的演讲深入浅出，看起来好像都是你完全知道的道理，但仔细琢磨起来好像还是他讲的比较有道理……恩，就这么回事。该演讲的内容主要是分析了目前的Javascript生态，从Dart/TypeScript/CoffeeScript/ES6/ES7讲到对目前如火如荼每年火一个的Javascript框架的选择。当然最后的结论依然是选择最适合你的选择这种烂大街的理论。这类演讲者的高明之处就在这里，虽然是烂大街的理论，但你听起来就是觉得：嗯，好像是有那么点道理。\n不过我能不能吐槽一下QCon的同声传译同学。本来听英文能听懂一半儿的，为啥听了你们的口译之后就完全听不懂了？\n编号为3的演讲主要内容是Facebook的工程师文化，绩效制度和薪酬体系。这中间有非常多值得参考的地方，干货极多。例如Facebook使用的是OKR而不是KPI，使用360 PerfReview 并允许员工自行决定是否公开4种不同的Review类型等等。历年统计居然有85%的公开率。这意味着大多数的互相评价都是能被所有人看到的。Facebook的权限也非常open，所有工程师都能看到所有的代码和公司的运营报表。当然，这种开放的文化并不是一朝一夕形成的，而是长期以来不断努力的结果。\nFacebook的CodeReview文化也很有趣。每个人的代码只需要一个人Review就可以进入master主库，如果上线产品出现了问题，不会扣钱也不会批评，通过 git blame 把出现问题的代码的提交者和review者找出来，把代码打印出来挂在墙上供人参观。呵呵！\n编号为5的演讲提到了合伙人问题。演讲者是5次创业的徐凯强，也曾经是一个技术合伙人。总结的4点相当精到，能早看到就好了。\n编号为6的演讲者提到了Lua的使用。聚划算使用Lua开发了一套LuaViewSDK，在iOS和Android中使用了Lua来调用Native的API实现高性能的界面展现。这样的实现比HTML5的性能要高出不少。他们在Native的Bridge层面统一了iOS和Android的API，然后完全使用Lua语言来开发。这和ReactNative的实现方式是类似的，但Lua的引入更符合C++等传统程序员的习惯。我在场提了几个问题，发现他们的确在Lua方面颇有研究。实在是没有想到阿里巴巴还居然还真有使用Lua开发App的团队。LuaViewSDK可以在 Github 中找到。\n总算是在火车上把这篇写完了。左边右边的熊孩子把我闹腾得不轻。中国移动的信号在火车上还算稳定。\n在火车上居然能搜到这么热点，这是几个意思？\n周末愉快！\n（全文完）\n","date":"2016-04-23","description":"","lastmod":"2016-04-23T13:32:33Z","slug":"qcon2016c","tags":["conference"],"title":"QCon2016全球软件开发大会随笔（三）","url":"https://blog.zengrong.net/post/qcon2016c/"},{"categories":["technology"],"content":"QCon2016全球软件开发大会随笔（一）\n今天是QCon2016的第二天。北京的天气依然好得一塌糊涂。除了风大了点。啥也不说了，继续上短袖。\n有了昨天的良好睡眠，今天听课终于是不困了。但无论如何，9个分会场的63场演讲是没办法同时听完的。也正因为有了更清醒的大脑，我采取了一种新的听课方法：10M听课法 。\n10M听课法是一种创新的听课方法，灵活运用10M，可以让学习者使用同样的时间得到超过1.5倍的课程内容！这种方法易学难精，但很容易掌握！\n当然了你知道上面是扯淡的。\n我其实只是把自己想听的演讲先随便找一个听10分钟，然后再选择听哪个而已。于我而言，10分钟已经足以判断这个演讲是否值得听了。如果无法判断……那就再听5分钟。\n所以，我今天一共听了11场演讲。我把里面对我有较大价值的列在下面（按时间排序）：\nAndroid超级补丁包技术 移动互联网的音视频传输挑战 用WebGL打造HTML5游戏引擎平台 滴滴运维架构的演化史 移动端音视频应用优化之道 互动娱乐时代下的web音视频性能优化 其中，有3个关于移动互联网音视频优化的演讲。我这样选择的原因是：希望在多个不同机构（海外厂商、网易、腾讯）的相同内容分享中，了解他们对音视频技术的不同处理方案，并从演讲风格和侧重点方面进行比较。\n事实证明我的选择是非常正确。这三个演讲不但内容侧重点不同，演讲风格也迥然不同。\n上面编号为2的演讲是一个在硅谷的公司声网 Agora.io 提供的。讲师的Keynote经过了精心准备，演讲应该也是排练过多次。所有的描述都非常准确且没有废话，Keynote和演讲配合天衣无缝。该演讲述了该公司基于WebRTC进行音视频传输时碰到的问题，并从质量评估，数据统计，虚拟专线，丢包对抗，网络可用，后台稳定这六个方面来阐述问题的解决方案。其中既包含服务器架构，也包含算法的选择和讨论，从内容上和形式上来说都是一场极为精彩的演讲。\n编号为5的演讲是网易杭州研究院提供的。郭再荣老师的演讲风格沉稳内联，技术点讲得极透彻。他分享了网易视频云的直播和点播架构，还分推送端和播放端讨论了音视频处理的协议选择，编解码技术和优化方案。如果10年前做基于 RTMP 协议的视频直播的时候能看到这个演讲，或许我也能做到现在了吧……\n编号为6的演讲是腾讯提供的。讲师小哥有点紧张，但演讲的内容却十分不错。该演讲对腾讯课堂的技术架构进行了讲解。其中不仅包含服务器架构，同时也包含客户端的架构以及如何解决延迟等棘手的问题。腾讯采用的协议和网易云并不相同，使用了一些私有的协议和Flash技术。例如，下面是音视频直播的典型架构：\n但腾讯课堂采用了自己的架构，不但选用了私有的UDP协议，音频部分采用的也不是常用的AAC编码，而是Skype的SILK编码。\n腾讯课堂还使用了 CrossBridge 把C++开发的SDK编译成SWF版本，基于Flash技术实现了自己的专有客户端播放器。\n编号为3的演讲是青瓷HTML5引擎的作者林意炜。青瓷引擎我之前并没有了解过，该引擎完整提供了一个基于浏览器的IDE，的确是让我震撼了。之前我们使用白鹭开发微信游戏时候碰到的iOS掉帧的问题，也在演讲中有所提及。林意炜提到的WebGL已经Ready的观点让人兴奋。联想到最近微信升级到X5 Blink内核全面解决安卓声音问题的新闻，我不禁感慨HTML5的春天真的已经到来了。\n编号为4的演讲说的是滴滴出行运维平台的演化史。他们采用Gitlab作为运维平台开发基础的做法非常值得借鉴，至少有下面几点好处：\n文本化方便管理，提交可多级审核 可以使用hook实现自动脚本功能 模版的继承 不需要在界面上多次调用 原生的版本管理 滴滴出行运维团队说他们是在“开着飞机换引擎”。话说回来，哪个初创公司又不是这样呢？\n最开始滴滴运维团队选择了让运维学习研发，而放弃了从纯研发转向运维研发的人才培养方式。现在看来，还是从研发转向运维效率更高。\n不早了，明天继续学习，晚安。\nQCon2016全球软件开发大会随笔（三）\n","date":"2016-04-23","description":"","lastmod":"2016-04-23T13:03:59Z","slug":"qcon2016b","tags":["conference"],"title":"QCon2016全球软件开发大会随笔（二）","url":"https://blog.zengrong.net/post/qcon2016b/"},{"categories":["technology"],"content":"今天是QCon2016的第一天。北京的天气简直是好得不可思议，9°到31°的温差美得令人心醉。啥也不说了，短袖伺候。\n和上个月来参加TFC（见国内VR发展现状——12届TFC会议期间见闻）不同的是，北京国际会展中心门口没有看到任何莫名其妙的布置，没有ShowGirl，专业的技术会议本来就应该这么低调才对。\n和上个月来参加TFC（ 见国内VR发展现状——12届TFC会议期间见闻 ）不同的是，北京国际会展中心门口没有看到任何莫名其妙的布置，没有ShowGirl，专业的技术会议本来就应该这么低调才对。\n第一天的演讲一共49场，分成了10个分会场。每个都听是不可能了。我按个人兴趣听取了7场演讲（按时间排序）：\n腾讯神盾开放通用推荐系统 云直播平台架构与实践 从万物生长到繁荣有序——重新思考移动开发 Deep Learning with Python 知乎反作弊系统演变 Golang 在 Baidu-FrontEnd 的应用 大型移动广告平台的架构衍化 演讲的质量良莠不齐。QCon 的内容主要以后端技术为主，第一天的日程中，只有阿里百川庄卓然的《从万物生长到繁荣有序》算是前端的内容。给我带来最多思考的演讲是上面的第1、4、6号。\n从内容来看，涉及到热门技术，或者大厂系统架构的演讲更受欢迎，例如下图中的《Docker应用：如何设计超大规模容器调度系统》的演讲听众爆满，我无法挤进去，只好换了内容。\n下面这个演讲是腾讯大数据中心介绍他们的《神盾开放通用推荐系统》，程序员们都已经坐在了地板上。当然也包括我……\n一天下来，可以深入思考的点有下面几个：\n大公司也并不惧怕架构的调整。相反，他们在架构的调整和重构上还可能比较激进。例如百度使用 Golang 开发了整套BFE架构； 除了C++外，如Go和Python也在被大量使用； 通用功能应该集成进入SDK。例如客户端的IP表策略，网络质量检测策略等等； 客户端模块考虑使用二进制分发，而不使用源码分发。 不早了，明天继续学习，晚安。\nQCon2016全球软件开发大会随笔（二）\n","date":"2016-04-23","description":"","lastmod":"2016-04-23T12:42:56Z","slug":"qcon2016a","tags":["conference"],"title":"QCon2016全球软件开发大会随笔（一）","url":"https://blog.zengrong.net/post/qcon2016a/"},{"categories":["use"],"content":" 2016-06-01 更新： 增加 QuickLook 插件 2016-09-24 更新： 增加 Karabiner 在 macOS Sierra 下的解决方案 2016-10-06 更新： 增加键盘快捷键。 MBP 已经到手，结束了在 Windows、Mac OS X 和 Linux Mint 中来回切换的痛苦。记录一些技巧吧。\n[TOC]\n快捷键 Mac 键盘快捷键\n必装软件 下面两兄弟一个是包管理，一个装常用 app，别提多方便了。基本可以告别买不起服务器的 AppStore 了。\nHomebrew Homebrew Cask 另外两个著名的包管理软件是 MacPorts 和 Fink ，喜欢折腾的可以尝试下。\n下面的几个软件均可以使用 Cask 安装。\n看图软件妥妥的 XnViewMP ，图库、预览、转换等常用功能都有了。不过非固态硬盘最好就不要尝试了。\n视频播放器用 MPlayerX ，我在 Windows 和 Linux 上用的都是 MPlayer ，这下习惯统一了。\nPDF阅读使用 Skim。\n[Karabiner][11] ，映射按键。对于我这种使用 [HHKB][16] 键位的小众人群来说，所有的按键都必须一致，否则效率奇低。使用 Karabiner 把我常用的键盘定义存成 Profile 就行了。 2016-09-24 更新：Karabiner 在 macOS Sierra 下不能使用了。请看 解决方案 。\n下面的软件无法用 Cask 安装，请去官网或 AppStore 。\nf.lux ，用来根据时间调整显示器色温，对眼睛有好处。\nALZip ，一直免费的解压缩软件，支持 RAR/7Z 等大多数压缩格式。偷懒不用命令行的选择。\nCheatSheet ，长按 Command 键显示当前软件的所有快捷键，对提升效率极有好处。\nchnroutes ，懂的人自然懂。\nQuickLook 这些好用的 QuickLook 插件都可以使用 Home Cask 来安装。\nqlstephen 快速预览 README/INSTALL 这类纯文本文件。 qlcolorcode 源码着色。 qlmarkdown 渲染 Markdown 文本。 quicklook-csv 渲染 CSV 为表格。 betterzipql 预览 ZIP 文件。 qlimagesize 查看文件信息。 qlvideo 预览视频文件。 qlprettypatch 对 patch 文件着色。 suspicious-package 查看 PKG 包的内容。 离开锁屏密码 在 Windows 中有个很有用的快捷键 Win+L ，在短暂离开电脑的时候用于锁屏，避免泄露重要信息。这种锁屏不会导致程序和网络中断。\n在 OS X 中，需要首先在“安全与隐私”中设定“进入睡眠或开始屏幕保护程序后输入密码”，然后使用 Control+Shift+Power 快捷键，就能获得相同的功能。\n直接睡眠会导致断网，使用 Command+Option+Power 实现。\n查看所有的 Mac 键盘快捷键 。\n写入NTFS分区 如果移动硬盘使用的是 NTFS 分区，那么默认情况下，在 OS X 中只可读，不可写。有一些付费软件可以解决这个问题，但也可以简单修改 /etc/fstab 来使用 OS X 的内置 NTFS 支持。\nLABEL=NAME none ntfs rw,auto,nobrowse NAME 就是硬盘的标签，不可包含空格。如果要改标签名找台 Windows 插上去改一下即可。许多文章提到使用 UUID 来代替 LABEL ，但我新买的 TOSHIBA 1TB USB3 移动硬盘并没有UUID。\n查看硬盘信息可以使用 disktracerouteutil list ，或者 diskutil info /dev/disk_name 。\n实测有时在 OS X 中写入 NTFS 分区会出问题，主要是权限问题，在我的硬盘上甚至造成了一些数据丢失。所以最好还是在 Windows 虚拟机中对 NTFS 分区进行操作吧。\n睡眠耗电过高 MacBook Pro Retina 睡眠耗电现象深究\n（全文完）\n","date":"2016-04-16","description":"","lastmod":"2016-10-05T23:34:47Z","slug":"osx-software-cheat","tags":["osx","software","choice"],"title":"macOS 技巧与软件选择","url":"https://blog.zengrong.net/post/osx-software-cheat/"},{"categories":["impressions"],"content":"今天一早起来，手机就被汉马刷屏了。汉马的这次组织工作做得很赞，阵仗也足够大。\n微信好友里面，有3个全马和1个半马选手。我们Game部门2位全马选手——豹哥和敏妹，都跑完了全程。\n为他们叫好的同时，我也为各种营销手段叫好。看看下面令人眼花缭乱的展示，无论是营销品牌，还是营销自己，都借助汉马这个平台达到了自己的目的。整个汉马就是一个营销的盛宴。\n早餐的时候，小曾同学指着面包包装袋问我，为什么“跑起来，棒棒哒”面包是汉马专供？于是我开始解释42.195，以及仟吉使用的 中央厨房 运营模式，还有像汉马这种大型活动采用的 赞助商 合作模式。\n从我自己以前组织会议的经验来看，对于几百人规模的会议，即使是收取一定的门票费用，也是一定要亏损的。若希望有盈余，就必须找赞助商。像汉马这样仅参赛选手就超过2万人，且有官方支持的比赛，有足够的曝光率，一定有规模庞大的赞助商的支持。从仟吉的广告“2016武汉马拉松赛事度假烘焙类官方支持商”来看，汉马的赞助级别应该是分得很细致的。\n在范爷的照片里可以看到，这次汉马的冠名赞助商是东风雷诺，顶级赞助商是钱宝、华侨城和特步。范爷能来汉马跑开头和结尾，也是由于她是 科雷嘉 的代言人。\n汉马官网 能找到所有赞助商的名单。\n赞助商和活动之间，如何相互利用，达到自己的目标？\n以仟吉为例。仟吉的产品是烘焙食品，主要的原料是碳水化合物。碳水化合物可以作为马拉松运动开始60分钟之后的补充食品。仟吉把自己的产品用“汉马专供”的方式来宣传，再加上“跑起来，棒棒哒”的口号，形成消费者“仟吉的面包是武汉马拉松的专供食品”的印象，达到提升品牌形象和知名度的广告效果。赞助武汉马拉松这种国际性比赛，也符合消费者心中的“大厂”的形象。\n1digraph kengee { 2 rankdir=\u0026#34;TB\u0026#34; 3 kengee[label=\u0026#34;仟吉\u0026#34; shape=doublecircle] 4 marathon[label=\u0026#34;武汉马拉松\u0026#34; shape=doublecircle] 5 food1[label=\u0026#34;烘焙食品\u0026#34;] 6 food2[label=\u0026#34;补充食品\u0026#34;] 7 food3[label=\u0026#34;专供补充食品\u0026#34; shape=box] 8 9 kengee -\u0026gt; food1 10 marathon -\u0026gt; food2 11 food1 -\u0026gt; food2[label=\u0026#34;碳水化合物\u0026#34;] 12 food2 -\u0026gt; food3 13 kengee -\u0026gt; food3[label=\u0026#34;汉马专供\u0026#34;] 14} 汉马的赞助商里，有1个汽车行业，2个金融/保险行业，4个食品/药品/饮料行业，其他则每行业只有1个赞助商。\n发散一下，要是有个游戏厂商来赞助会怎样？Subway Marathon Surfers？范爷向前冲！！！\n讲真，个人觉得 Runtastic 或者咕咚运动之类的可以来做个广告，挺应景的。\n话说回来，“汉马”为什么不叫“武松”呢？这是因为来领跑的范爷刚刚演了《我不是潘金莲》……\n叫“武马”也是极好的，这样可以在终点处叫卖参赛选手的 WU MA 视频，应该会很受欢迎。\n其实汉马叫“斑马”岂不是更好？长江大桥上碰见，打招呼就是这样的：“你跑个斑马？”\n（胡扯完毕）\n","date":"2016-04-10","description":"","lastmod":"2016-04-10T13:48:06Z","slug":"wuhanmarathon-sponsor","tags":["management"],"title":"从汉马赞助商想开去","url":"https://blog.zengrong.net/post/wuhanmarathon-sponsor/"},{"categories":["impressions"],"content":"\n零和博弈（Zero-sum game），也叫零和游戏。它的原理如下：\n两人对弈，总会有一个赢，一个输，如果我们把获胜计算为得1分，而输棋为-1分。则若A获胜次数为N，B的失败次数必然也为N。若A失败的次数为M，则B获胜的次数必然为M。这样，A的总分为（N-M），B的总分为（M-N），显然（N-M）+（M-N）=0，这就是零和游戏的数学表达式。\n简单的说，在零和博弈中，总有一方是绝对赢家，另一方是绝对输家。\n囚徒困境（Prisoner's dilemma）的原理如下：\n两个共谋犯罪的人被关入监狱，不能互相沟通情况。如果两个人都不揭发对方，则由于证据不确定，每个人都坐牢一年；若一人揭发，而另一人沉默，则揭发者因为立功而立即获释，沉默者因不合作而入狱五年；若互相揭发，则因证据确实，二者都判刑两年。由于囚徒无法信任对方，因此倾向于互相揭发，而不是同守沉默。\n囚徒困境是博弈论的非零和博弈中具代表性的例子，博弈的双方存在“双赢”的可能性。所以，囚徒困境也可能称为“正和博弈”。\n扑克是一种零和游戏。从对局的双方来看，只有获胜的一方才能获得最大的利益。但扑克有很多种玩法，起源于“跑得快”玩法的“斗地主”，为什么能从武汉快速传播到全国？就是因为在打法中的“合作双赢”。“斗地主”每一局中需要二人配合对付一人，下一局时互相配合的人又会变化。这促成了一种快速的合作。玩家不但要熟悉合作者的打法和特点，还需要避免自己的特点被他人看透。因为到了下一局，谁也不知道自己是“地主”还是“佃户”。\n我们在2015年看到的滴滴和快的合并、美团和大众点评合并、58同城和赶集网合并，都是“双赢”的例子。在资本收紧的寒冬，何必拼个你死我活？不如合在一起有钱大家赚嘛。我甚至在想，会不会哪天Intel和AMD见了个面说：“反正通用CPU现在都过得挺辛苦的，要不就合并吧！一起赚他们的钱呗。X86架构没人做的过我们两家啊。”当然了，美国政府可能不会答应就是了。 :)\n企业如此，团队亦是如此。一些大型企业会设置只能类似的多个团队（产品团队），使之互相竞争，从而达到快速高效创新的结果。这种竞争一定不能是零和博弈，否则最终的结果就是两败俱伤。\n在职能不同的团队之间，也可能存在竞争关系。例如资源的竞争（多个团队竞争公共资源）、人才的竞争（优秀人才在职能部门之间的流转）等等。团队间唯有互相了解，才能做到共生共赢。\n离职的员工和公司之间，也并非对立关系。作为企业，永远不能让员工“负气出走”，要让员工准确理解企业的发展目标和文化，让员工认同企业的理念和观点。即使不能做朋友，也绝不能成为敌人。\n聚是一团火，散作满天星，就是这个道理。\n（全文完）\n","date":"2016-04-03","description":"","lastmod":"2016-04-03T02:23:55Z","slug":"zreo-sum-game-and-prisoners-dilemma","tags":["reading"],"title":"零和博弈与囚徒困境","url":"https://blog.zengrong.net/post/zreo-sum-game-and-prisoners-dilemma/"},{"categories":["technology"],"content":"2016-04-29 更新：加入 unlock-keychain\n我们的 Android 包一直是使用 Jenkins 发布的。这样，不必依赖开发就能得到 APK 包，打包的工作从开发交给测试，运营同学直接从内网取包上渠道。整个发布过程，开发可以不必参与，非开发可以不接触源码。\n现在 iOS 版本也需要加入到这个流程中，这就需要扩展以前的脚本工具使其可以支持 iOS 自动化打包。然后才能将其集成进入 Jenkins 中。\niOS自动打包并发布脚本 这篇文章详细介绍了使用 XCode Command Line Tools 中的 xcodebuild 和 xcrun 来进行 iOS 自动化打包的方法。按这篇教程操作很容易打包成功。我将该文中提到的一些重点记录下来便于记忆。\n1. 指定要编译的项目 使用 -project 参数指定项目名称，支持绝对路径和相对路径。\n2. 获取可用的参数值 想知道 -target 、 -configuration 和 -scheme 参数的可用值可以通过调用 xcodebuild -list 获得。\n想知道 -sdk 参数的可用值可以使用 xcodebuild -showsdks 获得。\n要使用不同的签名，可以在命令行中重新设定 CODE_SIGN_IDENTITY 和 PROVISIONING_PROFILE 的值。\n若是要知道还有哪些值可以设置，使用 xcodebuild -showBuildSettings 来获得。\n3. 签名和 mobileprovision UUID 上面提到，若要使用不同证书进行签名，需要设置两个参数。\nCODE_SIGN_IDENTITY 为开发者证书标识，可以在 Keychain Access -\u0026gt; Certificates -\u0026gt; 选中证书右键弹出菜单 -\u0026gt; Get Info -\u0026gt; Common Name 获取。获得的值类似于：\niPhone Distribution: Company name Co. Ltd (xxxxxxxx9A) PROVISIONING_PROFILE 参数就是 mobileprovision 文件的 UUID，类似于这样的字符串：\nxxxxx-xxxx-xxx-xxxx-xxxxxxxxx 有好几种方法可以得到这个 UUID。\n第一种方法，如果已经在 XCode 中自动下载了这个 mobileprovision ，该文件位于 ~/Library/MobileDevice/Provisioning Profiles 下，文件名就是 UDID 的值。\n第二种方法，如果已经在 XCode 中自动下载了这个 mobileprovision ，进入 Xcode -\u0026gt; Preferences -\u0026gt; 选中申请开发者证书的 Apple ID -\u0026gt; 选中开发者证书 -\u0026gt; View Details… -\u0026gt; 根据 Provisioning Profiles 的名字选中打包所需的 mobileprovision 文件 -\u0026gt; 右键菜单 -\u0026gt; Show in Finder ，这样就能直接打开第一种方法中的文件夹，并定位到该文件。\n第三种方法，当已经有了一个 mobileprovision 文件（假设文件名为 adhoc.mobileprovision）但不知道 UUID 的时候，可以使用这个脚本来实现：\n/usr/libexec/PlistBuddy -c \u0026quot;Print UUID\u0026quot; /dev/stdin \u0026lt;\u0026lt;\u0026lt; $(/usr/bin/security cms -D -i ./adhoc.mobileprovision) 上面脚本的原理是，先使用 security 解密 mobileprovision 成文本（结果为一个plist格式的文本），然后使用 PlistBuddy 计算其 UUID 。\n4. unlock-keychain 本段落为jxyi 提供。\n接入Jenkiin之后，打包时提示信息如下：\n1Signing Identity: \u0026#34;iPhone Developer: jiaxing yi (XXXXXXXXX)\u0026#34; 2Provisioning Profile: \u0026#34;ad-hoc for dz\u0026#34; 3 (xxxxxxxx-15ea-xxxx-aee9-xxxxxxxxxxxx) 4 5 /usr/bin/codesign --force --sign XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX --entitlements /Users/baina/jenkins/workspace/game-dz-iOS-release-build/game-lerna-texas/lerna_player/runtime/proj.mac_ios/build/lernaplayer.build/Debug-iphoneos/lernaproduct.build/lernaproduct.app.xcent --timestamp=none /Users/baina/jenkins/workspace/game-dz-iOS-release-build/game-lerna-texas/lerna_player/runtime/proj.mac_ios/build/Debug-iphoneos/lernaproduct.app 6/Users/baina/jenkins/workspace/game-dz-iOS-release-build/game-lerna-texas/lerna_player/runtime/proj.mac_ios/build/Debug-iphoneos/lernaproduct.app: User interaction is not allowed. 7Command /usr/bin/codesign failed with exit code 1 8 9** BUILD FAILED ** 出现该问题是由于需要输入登录密码，而脚本编译时无法人工输入登录密码导致。因此需要在打包之前先解锁账号。\n要解决上面的问题，需要在调用打包脚本之前，先调用如下命令：\n1security unlock-keychain -p \u0026#34;your password\u0026#34; ~/Library/Keychains/login.keychain （全文完）\n","date":"2016-04-01","description":"","lastmod":"2016-04-29T07:19:58Z","slug":"package-ipa-by-shell","tags":["ios","android"],"title":"自动化发布iOS包","url":"https://blog.zengrong.net/post/package-ipa-by-shell/"},{"categories":["technology"],"content":"在 时间都去哪儿了？——善用工具形成高效习惯（上） 中，我提到了我的方法： TODO List 工具+笔记管理工具+时间记录工具+阅读工具+写作工具 ，并介绍了 TODO List 工具和笔记管理工具。下面接着介绍时间管理工具、阅读工具和写作工具。\n3. 时间记录 3.1 形成自己的时间感 《奇特的一生》 中提到了柳比歇夫同学的 时间感 时这样说：\n多年来经常看表的结果，柳比歇夫肯定形成了一种特殊的时间感。在我们机体深处滴答滴答走着的生物表，在他身上已成为一种感觉兼知觉器官。我作出这样推断的根据是：我同他见过两次面，在他日记中都有记载，时间记得十分准确——“一小时三十五分”、“一小时五十分”；然而当时他自然没有看表。我同他一起散步，不慌不忙，我陪着他；他借助于一种内在的注意力，感觉得到时针在表面上移动。\n许多人可能会有这种感觉：对于某些非常投入的工作（例如编程），会觉得时间过得很快；而对于一些无聊的场景（例如开会），会觉得时间过得极慢；对于某些没有具体时间限制的工作（例如讨论和谈话），使用的时间经常会超出预期。出现这种情况的原因是由于大脑对于时间意识的特殊性导致。大脑中存在着至少两个不同版本的时间：一个主计时器告诉你对“现在”的感知，而另一个则在不停地对这种感知进行调整。所以，大脑对时间的认知并不是统一的，对于专注的场景，它会选择性忽略时间的感受；对于规律性和无趣的场景，因为关注度分散，它会放大对时间的感受；在一些极端场景（例如受到生命威胁），大脑感受到的时间会比实际时间多出不少，让人有 更多的时间 做出反应。这并非时间变多了，而是大脑对时间的感知进行了调整。\n养成时间记录的习惯，能够帮我们形成自己的时间感。也就是说，通过不断地训练自己，调整大脑中的两个计时器，让它们的感知接近。 这样就能保证时间感知的一致性，从而进一步提高我们的工作和学习效率。\n例如 番茄工作法 要求把工作按照“番茄钟”来分离，推荐每个番茄钟（一般为25分钟）休息5-10分钟。如果坚持这种方法较长的时间，会在大脑中形成对25分钟这个时间段的感知。坚持的时间越长，这种感知就越强烈。大脑会自行在“后台”推进工作，保证这个时间段的高效思考，然后保证5分钟时间的高效休息。这种时间感对我们的习惯和精力分配都是一种良好的促进。\n上面提到的 无时间限制的谈话 也是一个好例子。在开始谈话之前，可以先在内心预计一个谈话结束的时间。在谈话过程进行到一定阶段的时候（可以把谈话分成开场寒暄、主旨、讨论、结论等阶段），通过看表来确定时间的流逝，并尽量在自己预计的时间范围之内完成谈话。如果一直按这种方式来进行谈话，大脑就会形成特定的时间感。熟悉了这种时间感，你甚至在谈话过程中不必看表，就能知道什么时候应该切换话题，什么时候应该转换阶段，什么时候应该结束谈话。这种时间感能够真正让 TODO List 有价值，而不是让 List 越来越长，不断 Delay。\n3.2 工具选择 我采用的时间记录工具是一个名为 aTimeLogger 的 App ，有 iOS 和 Android 版本。它工作起来如下图所示。\naTimerLogger 可以显示出条形图，从而分析每周和每月的时间分配情况。\n对于这个工具的使用，我有两条建议：\n一定要重新规划适合自己的分类； 一定要定期分析自己的时间占用情况。 4. 阅读 在个人电脑、智能手机普及的时代，资讯来源从书籍、杂志、报纸逐渐转向网络、论坛、微博，我们的阅读时间已经越来越少了。尽管如此，我依然认为读一本好书，写一篇读后感是获取 优质系统化信息 的最快方式。我并不摈弃现代的阅读习惯，我也会大量阅读公众号、今日头条和知乎中的内容，我采用的是一种和阅读碎片化信息并不冲突的方法。\n4.1 善用阅读工具 我在 豆瓣阅读 和 多看 上都购买过不少电子书。这两个阅读软件的阅读体验都相当好。但我最喜欢的阅读软件依然是 静读天下专业版 ，在 Google Play 打折的时候购买，价钱很公道。\nKindle 也是非常优秀的阅读工具。和智能手机相比，它的好处就是不费眼睛，适合长时间阅读。不到200克的重量，适合单手握持，比大多数纸质书都要轻。很多人会纠结于买哪一款Kindle。我的建议就是，直接买带阅读灯的 Paper White 3 即可。使用 Kindle 最重要的事情一定要说三遍： 不要带套！不要带套！不要带套！ 亚马逊好不容易把重量降到了200克以下，您一个套加上去，还能不能愉快地阅读了？如果怕碎屏，买个抽取式保护套放包里，阅读的时候从保护套里面拿出来读。\n阅读一定要随时随地。Kindle 要随身带。这样在地铁上，等公交，等人这类零碎的时间可以顺手拿出来阅读。可能你会认为几分钟的时间不算什么，但一天下来，积累下来的时间足够你读完整个章节。\n4.2 听书 有些零碎的时间并不适合阅读。例如在公交上阅读会影响视力（可能还会头晕恶心），开车的时候也无法阅读。这种情况下，我采取的方法就是——听。\n我选择静读天下作为阅读软件的一个主要原因，就是支持朗读。讯飞语音引擎是目前中文系统中最好的TTS语音引擎，你甚至可以要求它使用方言来朗读。朗读能够更好地利用零碎的时间，在步行时，散步时，锻炼时，你都可以听书。在我的 2015 的阅读计划中，有一半的书是通过听完成阅读的。2016 读完的11本书中，除了第一本之外，全部是通过听的方式阅读的。阅读这些非技术类的书籍不需要花费太多的精力思考，只需要零碎时间就能轻松完成。\n4.3 听文章 公众号是我获取最新资讯的一个重要途径。在我订阅的大量公众号中，许多文章的质量是相当高的。但公众号文章显然无法使用阅读软件来阅读。每天早上，公众号会有大量的推送。我花10分钟时间将它们浏览一遍，收藏有意义的文章，将它们发送到 @Voice Aloud Reader 这个软件进行朗读。最简单的发送就是将文章内容复制然后粘贴到 @Voice Aloud Reader 软件中，也可以从浏览器中选择“分享到 @Voice Aloud Reader”来实现朗读。\n4.4 读书笔记 只读不记，阅读的作用并不大。对于小说类的读物，可以仅仅记录下其中优秀的词句。而对于科技、社会、方法论等等书籍，必须用自己的方式对书籍内容进行分类，才能实现对书籍内容的理解。思维导图是读书笔记的一个好方式，使用思维导图要避开把所有的章节标题抄一遍的习惯，应该尽量采用自己的方式对书籍内容进行重新的分类和组合，找出不同章节之间的联系，标出自己关注的重点。\n另一种读书笔记的方式是评论。我喜欢摘抄出书籍中的部分关键句子，然后对这些句子中表达的意思和观点进行评论。评论的内容可以是对原文观点的批判，可以是印证自己身上发生的实例，也可以是根据该观点产生的发散联想。把这种方式和思维导图的方式结合在一起，更容易深入理解一本书。\n5. 写作 我们常说“听说读写”，写被认为是最难的事。这常常是因为“写”这件事比较累人，大多数人都不愿意尝试和坚持而已。其实“听说读写”中哪一件想做到一定深入的程度，都要下苦工。在我看来，写作可以锻炼人的逻辑思维能力，观察能力和归纳总结的能力。有些技术点实现了是一回事，写出来且让别人能看懂就是另一回事了。这两件事的关注点并不在一个方向。许多程序员能很容易地解决技术难题，但让他把解题思路写下来（或者说给别人听）却很难办。文章读者的能力良莠不齐，把文章写得深入浅出，架构合理，内容丰富才能cover更多的读者。因此，在动笔写作技术文章之前，首先要考虑的就是读者的能力。对于新手读者，文章中的比较深入的内容要给出相关资料供读者参阅，对于能力较强的读者，又不能让文章过于简单而降低他们的阅读兴趣。你怎么知道阅读你的文章的读者是哪种类型！这种谋划能力需要大量阅读和写作才能逐渐掌握。\n5.1 重要性 对于程序员来说，写作还能积累日常开发经验以便于日后查阅。例如，对于我来说，运维知识、服务器开发知识在工作中接触得较少。工作之外，我喜欢折腾一下服务器相关技术，在解决服务器问题的时候，我把经验写成文字，不但方便了碰到同样问题的人，也方便自己的查询。我从2005年到现在，坚持写了11年的博客，近800篇文章中，大多数是技术内容。我常常会碰到搜索技术问题最后找到自己博客的情况。博客对我来说，既是一个写作练习的场所，也是一个个人知识库。所有热爱阅读的人，都应该有一个自己的博客。\n5.2 工具选择 我的写作工具就是 WordPress。我在2003年曾经使用过其他的开源博客程序，但由于该程序的安全隐患导致站点被入侵，数据库被删，所有文章无法找回。我比较了多个博客程序的特点，最终选择了 Wordpress，一直写到现在。事实证明这是正确的选择。WordPress 的优点和特性自不必多说，但在线编辑功能无论如何进化，都让我无法适应。我也试用过一些博客写作工具，例如 Microsoft Live Write 、ScribeFire ，但对其功能并不满意。我希望用一种更方便更简单（更适合程序员）的方式来管理博客。\n后来我自己开发了 WPCMD ，这就是最适合我的工具。采用 Markdown 语法写作，使用任意编辑器来编辑文章，使用命令行更新博客。我把博客的所有源码托管到 Github ，再也不用担心文章丢失了。即使是我的硬盘损坏，博客服务器被黑，数据库被删，我也能随时恢复博客内容。\n6. 结语 到此为止，我把自己使用工具形成工作习惯的方法详细进行了描述。还有一些细节没有想透，待成熟后我再对本文进行修正和更新。从上面的描述可以看出，对于选择工具，我并非采用一成不变的标准。我讨厌重复造轮子，但也不排斥制造 WPCMD 这类工具以满足自己“变态的”需求。 没有完美的工具，只有完美的习惯。 想要形成完美的习惯，唯一要做的就是 坚持-调整-坚持-调整-坚持……\n7. 书籍和参考 番茄工作法 把时间当作朋友 奇特的一生 高效能人士的7个习惯 大脑时间相对论：遇危险时人是否感知时间变慢 （全文完）\n","date":"2016-03-23","description":"","lastmod":"2016-03-23T03:57:52Z","slug":"work-management-2","tags":["management"],"title":"时间都去哪儿了？——善用工具形成高效习惯（下）","url":"https://blog.zengrong.net/post/work-management-2/"},{"categories":["technology"],"content":"许多人会把这句话挂在嘴边：“我没有时间了。”这句话是不正确的。时间就在那里，不多不少，对所有人都完全平等，每个人的时间都是完全相等的。你偷不来别人的时间，没办法减少自己的时间，不能出卖自己的时间，更无法管理自己的时间。从科学的角度讲，在广义相对论下，即使能做到时间旅行，不过也是做到在别人的时间中穿梭而已，并没有增加可用时间。类似于 In Time（中译名：时间规划局） 中出现的用时间当货币，用生命支付食物的情况，只可能出现在科幻作品里。\n我担任技术 Leader 以来，感觉时间越来越不够用。白天基本在协调、解决项目的技术问题、review代码、开会中度过，自己的开发任务只能放到晚上进行。看看那些时间管理的大师们，他们是如何做到能同时完成那么多的工作？ 例如，《奇特的一生》 中提到的柳比歇夫同学，如何能在没有计算机，没有TODO软件，没有手机APP，没有网络同步的情况下，几十年如一日地手记自己的完整时间使用情况，整理自己所有的手稿和来往信件，完成著作等身的成就？\n李笑来在他的 《把时间当做朋友》 一书中提到：时间不可管理。我认同这个说法。因此我放弃管理时间的念头，改为 管理精力分配 。人的精力是有限的，好的习惯可以节省精力的付出。我把这套还不够完善的工作习惯写出来，是希望整理思绪，并不断地完善它。我的方式是： TODO List 工具+笔记管理工具+时间记录工具+阅读工具+写作工具 。\n1. TODO List 1.1 选择工具 很多时间管理或者方法管理的书会推荐使用普通的纸笔来做TODO List，对此我并不赞同。我尝试过 The Bullet Journal 方法1年以上，记录了2个笔记本。实践之后我认为，在目前智能手机普及的情况下，还坚持使用纸笔是不太明智的。智能手机无论在携带性上、同步性上还是输入便捷性上，都比纸笔系统要方便太多了。\n纸笔系统也不是全无好处。我认为目前纸笔系统最大的优势，就是可以随手画。当前，智能手机依然无法纸笔系统比拼手绘体验。我依然会随身带上纸笔系统，供需要手绘的时候使用。有价值的手绘，我会使用智能手机拍摄照片并将其加入笔记管理系统。\n有很多优秀的 TODO List 软件，目前获得赞誉最多的非 OmniFocus 莫属，且不谈 $39.99 的售价（iOS 和 Mac 版本分开销售），甚至有人会专门为了这个软件从 Windows 切换到 Mac OS X 系统。但由于它仅限于 Mac OS X/iOS 系统，我并没有使用它。\n我试用过十几款 TODO List 软件，感觉其中最有优势的是 奇妙清单 ，Any.do 。下面是我选择 TODO List 软件的标准：\n必须全平台支持（浏览器、客户端、iOS、安卓）； UI简洁，功能简单； 服务稳定，同步迅速； 价格合理。 根据这些标准，我使用的是 TickTick 的中文版本： 滴答清单 。想了解为什么这么选择，可以知乎用户 Jesson Zhou 的回答： GTD类的工具哪种免费且好用点？ 。\n1.2 清单技巧 清单的分类方式是需要技巧的。开始，我把清单按照工作的类型分类。例如， 工作、杂务、生活、学习、读书 等等。但很快我发现这种分类方式是不合理的。\n目前，我采用按照任务的完成重要性与完成方式来分类： 立刻、跨日、延迟、循环、结束 。\n这样的设计的好处在于，可以专注一个分类来完成自己的清单。这几个清单的工作流，我是这样进行的：\n每天早上，将所有要在当天完成的任务，加入到 立刻 这个清单。对于当天的工作，我只需要关注 立刻 这个清单即可。不用再管其他的清单。 立刻 清单中不能完成的，可以视情况调整到 跨日 、延迟 这两个清单中。 在 立刻 清单中的任务全部清空后，再花时间去关注 延迟 和 跨日 清单中的内容，将其按优先级移动到 立刻 清单中进行完成。 所有完成的任务，全部已入 完成 清单，方便review。 一些循环任务，例如身体锻炼、阅读、英语、生日提醒等任务，放入 循环 清单。 一些临时加入但无法确定应该放入哪个清单的任务，放入 收集箱 。每天结束前花固定的时间清空收集箱，将其移入其它清单。 TODO List 只是个工具，必须要坚持才有作用。而且要坚持必须每天清空清单才有作用。就像保持 清空收件箱 一样，你要保持 立刻 这个清单总是空的。\n2. 笔记管理 TODO List 仅仅应该保存需要完成的任务。一般任务只需要一个 Title，少有任务需要写 Content 。如果这个任务有相关的附件，或者产生了一些详细内容，不应该将它们放在 TODO List 中，而是应该将其放入笔记管理系统中。\n2.1 选择工具 我从2002年开始使用的笔记管理软件。市面上所有出现过的所有笔记软件我都使用过，并保存了大量笔记内容。 Evernote 、WebSaver 、One Note 都是不错的软件，但我一直使用至今的是 为知笔记 。\n为知笔记的前身是 CyberArticle 网文快捕 ，这是我最早购买的软件之一。刚参加工作的那几年，我用网文快捕积累了大量的资料，这些资料的积累为我的大量撰稿和书籍的出版打下了坚实的基础。长期坚持笔记记录是良好的习惯，强烈推荐大家使用为知笔记。\n当然，现在市面上有许多优秀的笔记软件，我说一下我选择笔记管理软件的标准：\n必须全平台支持（Windows/Mac/Linx/iOS/安卓）； 分类和标签支持； 允许分享笔记； 抓取功能强大； 服务稳定，同步迅速； 支持插件系统； 允许导出； 价格合理。 2.2 管理技巧 2.2.1 搜集 “好记性不如烂笔头。” 阅读量大了之后，一些优秀的资源需要随时搜集起来备用。工作中也有大量的信息需要搜集。平时在网上乱逛的时候，或者查找资料的时候，临时碰到的一些优秀的资源都可以搜集下来。为知笔记提供了抓取功能，搜集资源相当方便。我根据自己的习惯对搜集的内容进行了分类。\n只搜集，不整理，搜集再多再全也无效。每周需要花一定的时间对搜集来的信息进行整理，制作索引，删除无效过时的信息，并精简冗余的信息。这个整理过程在整个笔记管理系统是是不可或缺的。\n2.2.2 GTD 前面提到 TODO List 并不适合保存大量资料。因为 GTD 的核心是 “完成” ，一旦一个任务清单完成之后，就应该忘掉它的存在。值得记录的细节也是值得回溯的，放在 TODO List 中的项目是需要遗忘的，这就产生了冲突。\n我的办法是把 GTD 过程中产生的值得记录的细节记录在笔记管理系统中，每天的工作用一个日报笔记来记录。如下图所示。\n除了记录 TODO 细节，对于重要的工作邮件内容、各种讨论和会议内容我都会记录下来。比较独立的事件，并不放在日报笔记中，而是将它们进行单独记录，再以链接的形式将其加入到日报中。每月结束的时候，回顾一下这些记录，对下个月的工作安排有极大的指导意义。通过浏览这些记录，能够很容易地写出周工作总结和月工作总结。\n2.2.3 其他记录 Project 这个分类，除了上面提到的工作总结外，还有培训、谈话、人事、工作推进作为子项目。工作效率提升的过程，其实就是团队磨合以及个人能力成长的过程。将谈话和工作推进的细节记录下来，能够清晰地体现这个过程。而将人事内容记录下来，则可以方便查询一个员工的入职情况，与谈话内容互相印证。\n（请继续阅读）\n时间都去哪儿了？——善用工具形成高效习惯（下）\n","date":"2016-03-22","description":"","lastmod":"2016-03-22T09:18:53Z","slug":"work-management-1","tags":["management"],"title":"时间都去哪儿了？——善用工具形成高效习惯（上）","url":"https://blog.zengrong.net/post/work-management-1/"},{"categories":["technology"],"content":"这是一封发给客户端团队的邮件，略有改动。\n程序员和开发者 昨天晚上的部门培训上，我们谈到了时间使用问题。那么，如何让我们的时间更有价值？让我们每天都有新的进步？我一直在思考这个问题。\n每天，大家被看似繁重的开发任务消耗了所有的时间和精力。把自己从繁重的任务解救出来需要一些时间管理技巧和习惯，我们后续会安排培训。在这里，我们先谈一谈如何让自己每天都有进步。\n2014年出版的《大数据时代：生活、工作与思维的大变革》中有段话：\n就像20世纪60~80年代之间计算机编程技术变得越来越普遍一样，现在国外的外包公司使得基础的计算机编程技术越来越廉价，如今它甚至成为了世界贫困人口的致富驱动力，而不再代表着高端技术。\n看到这句话，你后背会发凉么？\n我在知乎上经常看到这样的问题：”我该转行做软件开发么？”当有人询问转行的原因时，得到的大多数是：“这个行业工资高”之类的回答。这是不是印证了上面这段话的正确性？当 猪八戒 上一个 App 定制开发报价200起， 一个企业网站报价400起的时候，你还会认为，程序员是一个你向往中的高技术职业么？\n如何避免这样的窘境？答案是：不仅仅做一个能完成需求的 程序员 ，还要做一个无法替代的 开发者 。（开发者比程序员的外延更大）\n想成为无法替代的开发者，就是要在一般程序员相同的时间里，做更多的事，操更多的心。\n在游戏开发中，有大量的特性，都是被技术驱动的。而最了解的技术的，就是我们开发者。可是，你是一个合格的程序员么？还是仅仅是个码农呢？如果连合格的程序员都不算，怎么做开发者呢？ 优秀程序员和一般程序员差别在哪？\n作为想要晋升为游戏开发者的合格程序员，我们不仅要为如何实现功能操心，还要为如何得到更好的产品操心。这些心可以操在下面几个点（难度逐渐增高）：\n代码和整洁、高效，架构设计的合理性； 资源优化，产品安全； 产品交互上的合理性； 产品开发流程、测试流程、发布流程的优化； 在目前的架构上，开发出新的功能点，并协助策划/运营/商务等其它部门探询新的发展方向。 除了上面的第1点外，剩余的4点都是必须和其他部门同事配合实现的。为了达到配合（而不是帮倒忙）的目的，我们是不是应该了解其他部门的工作方法和工作流程？是不是需要知道他们的思维方式和决策方式？我们是不是要和他们打成一片，混个脸熟？\n再看回来，连第1点我们都没有做到。\n看到这句话，你后背会发凉么？\n我早就凉透了。\n如何解决？这就是我今天要说的： 功能点计划 。\n功能点计划 功能点计划是让我们以技术为驱动，定期寻找能够推动产品的功能点，然后联合策划/运营/商务部门在实际产品中使用这些功能，最终达到产品收入增长的结果。寻找功能点说起来不难，但加上“定期”就比较难了。功能点发现一个就少一个，要持续找到新的功能点，必须做到以下几点：\n精通正在使用的语言。对于 C++ 和 Lua 我们并没有掌握到足够精通的程度。不了解细节，就无法做出创新； 熟悉正在使用的平台。我们在使用 C++ 开发，这导致我们对 Android/iOS 平台却不够了解，。和语言一样，不了解细节，无法做出创新； 熟悉相关业务。对市场上的同类产品，我们玩过多少？对于一个游戏从打包到上架、推广的流程，我们知道多少？我们怎么做才能帮助其他部门同事提高效率，并避免他们提出的不合理的要求降低我们的开发效率？ 熟悉玩家心理。把自己当玩家，不是所有开发者都能做到的。 功能点计划 v1.0 要做到以下几点：\n每月至少提出一个客户端的功能点； 每月必须完成一个客户端的功能点开发； 保证这个功能点普及到所有相关部门 ; 推进功能点在产品中的实现。 该计划从本月开始施行。请大家积极参与。\n（全文完）\n","date":"2016-03-22","description":"","lastmod":"2016-03-22T06:43:45Z","slug":"function-points","tags":["management","study"],"title":"功能点计划","url":"https://blog.zengrong.net/post/function-points/"},{"categories":["technology"],"content":"昨天，GDC2016 让 PSVR 以 $399 的价格成为爆款。放眼望去，VR技术成为下一个 Big Thing 已经是铁板钉钉的事情。那么国内的VR现状到底如何？\n前几天，我写了一篇 VR 时代，我们应该如何编程？ 介绍了一点VR技术开发的现状，并提到了 暴风魔镜 和 蚁视 。今天，我在 第12届TFC的会场 了解到了更多国内VR发展的相关信息。\n硬件配置 Oculus 等设备一直都在拿刷新率和分辨率说事儿，下面是VR三巨头的参数对比：\nOculus Rift HTC Vive PSVR 分辨率 2160x1200 2160x1200 1920x1080 刷新率 90hz 90HZ 120HZ 视域范围 未公布 110° 100° 计算设备 PC PC PS4 发售日 2016.03.28 2016.04.05 2016.10 重量 470克 555克 610克 售价 $599 $799 $399 让我惊诧的是，这些参数，中国VR厂商们早就达到了。我现场佩戴了4个不同厂商的VR头盔，他们无一例外地都达到了2K分辨率，从DEMO的质量和现场体验来看，眩晕感被大大减轻。佩戴暴风魔镜第四代产品5分钟就想吐的感觉已经没有了。在暴风魔镜的展位，我听到的最多的叹息就是：像素太差了！\n下图中的这个产品，看介绍已经达到了 2560x1440 分辨率，而且是在2014年就达到了！\nVR 应用方向 下面我根据TFC展会中得到的信息，整理出来一些VR的应用方向。整理的内容并不全面，大部分是我的主观感受。\n1. VR 展示厅 在国内，VR和体感设备结合这个应用层面，已经非常成熟了。目前在许多旅游点、商场展示区、电影院、博物馆等等地方已经有了VR技术的展示厅。下图中的展示的是一个可以实现虚拟空战、虚拟赛车和虚拟过山车的设备。其中的VR头盔使用的是 Oculus DK2 。\n我在VR过山车的体验中，深刻体验到了视觉加上体感是可以欺骗大脑的。我在虚拟过山车上手心出汗，心跳加速，不得不闭上眼睛来告诉自己不过是坐在个椅子上而已……\n该设备的搭建者是一个内容提供商（CP）。他们不制造VR头盔，也不制作体感设备，而是仅仅提供游戏内容。体感设备制造商把游戏内容和体感设备一起打包提供给展厅。\n下图中的设备提供的是一个类似于游乐场中常见的大摆锤的VR体验。我最害怕这类往复式的娱乐设备，如果在真实的游乐场中，我一定是拒绝尝试的。但在VR设备中我却愿意冒险。体验总体令人满意，我可以确定，以后在真实的游乐场中，我一定会一如既往并且更坚决地拒绝尝试此类设备。然而，能不能把旁边人的表情做得丰富一点啊！拿个大白眼珠子瞪着我真的好么……\n目前的VR设备，除了 M$ 那个不缺钱的 HoloLens 之外，计算单元都是外置的。因此就要考虑计算单元放在哪里的问题。上面提到的和体感设备结合的VR设备，都不必考虑这个问题，它们只需要把计算单元放到基础设置中就行了。\n我想起去年和武汉的一个VR创业公司聊天，他们的设备也是准备面向网吧进行体验。看来大家都不约而同地想到了这点。国内的公司已经走得很远了。\n2. 主题公园 提供下面这个设备的是一家南京的公司。他们和主题公园合作提供VR体验。在这个设备上，你可以坐在一条龙的背上飞翔，也能操纵一架战斗机攻击。这台设备的成本价是30万元，售价50万左右。无论机械部分还是VR头盔、VR软件都是由该公司自行研发。从该公司在TFC上的演讲看，这是一家极为低调的公司，一直把精力放在研发上，并没有做太大的宣传。从现场体验和DEMO的效果看，这套设备的完成度已经相当高了。\n3. 射击游戏 许多VR设备最开始提供的DEMO就是射击游戏。这家杭州公司设计的VR设备系统中，你穿上一个特制背心（把计算单元背在身上），然后就可以没有线缆束缚地自由进行游戏了。照片中这位哥们正在忘我地冲着我们这些排队体验的与会者开枪。旁边的大屏幕中显示了游戏中的场景。额，我还是别排了，真丢不起这人……\n4. VR影视 VR 影视是少有国内公司涉足的领域。兰亭数字已经拍摄了第一部VR电影， 第二部正在筹备中。他们提出，VR视频领域因为没有明显的变现模式，所以成为还没有被巨头进入的蓝海。传统巨头转型VR，也难以放弃原本的团队和流程，所以比较尴尬。而传统影视在转型VR影视的最佳时间段就是今后的一年半，因为此时硬件、制作流程、参考内容等都完成了“0-1”的过程。VR内容主要有四个发展方向：\n泛娱乐，主要是综艺节目和电影，关键在于探索出VR里镜头语言和导演逻辑，它的技术红利期很可观； VR直播，它能够满足一些刚需，比如演唱会的VIP体验，可实现粉丝经济; 旅游，会针对一些现实中很难到达的地方; 新闻，会采取还原事件现场的方式。 5. 成人游戏 我意外地在展厅中碰到一个武汉的创业团队（应该是少于10人），他们使用 Unreal 开发了一款宅男游戏，你可以在游戏中与一位美女互动。画面相当精细，据说美术团队在长期给迪斯尼公司做外包。展示用的设备使用的是 Oculus DK1 。不过遗憾的是，他们并没有想好产品的盈利模式。\n这类产品已经有大作了，例如 Summer Lesson。\n像 死或生：沙滩排球 这样在PS4上发布的大作，很容易制成VR版本。配合PSVR，效果一定不错。\n国内巨头在怎么玩？ Sony 大法好，PSVR 的 $399 售价和200多款正在开发的游戏，PS4 完善的产业链以及游戏质量，让各大媒体纷纷跪舔，不再唱衰。那么，国内的巨头在干什么呢？\n腾讯早在去年12月21日就发布了自己的 Tencent VR SDK和开发者支持计划，首次完整首次系统的阐述了腾讯在虚拟现实领域的规划。\n引用自： http://zhidx.com/p/34730.html\n按照腾讯的规划，Tencent VR将同时支持VR主机，VR头盔和手机VR3种产品形态：\n2016年3月，推出集成了传感器和专用屏幕的头戴显示设备（HMD），即DK；通过HDMI和USB接口，配合腾讯第一代miniStation微游戏、PC作为虚拟现实的开发平台提供给合作伙伴。 2016年第三季度，推出带有电池的便携主机式的头戴显示设备，即CV；配合体感手柄，提供类似于PS VR的虚拟现实解决方案。同时，消费者版本或也将在下半年推出。 2017年第三季度，在内容生态及技术成熟的基础上，发展手机VR和一体机方案，或推出类似于Gear VR形态的产品。 而就在昨天（3月17日）下午，阿里巴巴宣布成立VR（虚拟现实）实验室，并首次对外透露集团VR战略。\n引用自： http://zhidx.com/p/42037.html\n阿里巴巴的VR战略核心在这几方面：\n推动VR购物这样的商业模式。 协同旗下影业、音乐、视频网站培育VR内容。 用VR商业生态，助推VR硬件普及。 通过投资Magic Leap等公司谋划AR布局。 其官方公开描述为，“阿里将发挥平台优势，同步推动VR内容培育和硬件孵化。在内容方面，阿里已经全面启动Buy＋计划引领未来购物体验，并将协同旗下的影业、音乐、视频网站等，推动优质VR内容产出。在硬件方面，阿里将依托全球最大电商平台，搭建VR商业生态，加速VR设备普及，助力硬件厂商发展。”\n小团队该怎么玩？ 从目前国内的情况看，VR硬件设备这个战场上已经是狼烟四起。腾讯和阿里巴巴野心勃勃，已经在自己熟悉的领域开始布局。百度虽然还未发布自己的VR规划，但一定也早已蓄力。全面地看，VR目前最缺的依然是内容。优秀的CP还没有出现，小团队依然可以抓住机遇，在2年内专注于VR内容的研发，争取在VR这个崭新的世界中闯出自己的天地。\n（全文完）\n","date":"2016-03-18","description":"","lastmod":"2016-03-18T02:56:52Z","slug":"tfc12th-vr","tags":["3d","vr","conference"],"title":"国内VR发展现状——12届TFC会议期间见闻","url":"https://blog.zengrong.net/post/tfc12th-vr/"},{"categories":["impressions"],"content":"\n昨天和一个朋友聊天，讲到关于职业选择的事情。这触动我进行了一些思考，梳理一下思路写下来。\n选择与规划 工作和职业这两个词，我们经常把它们划上等号，但仔细看来，它们是两码事。\n平常我们经常说“找工作”，找到了工作就意味着有了一份相对稳定的收入，满足我们日常所需，才有可能追求更高层次的需求。对于大多数人来说，有了工作才可能有进一步的生活，才能满足自己的存在感和成就感。逐渐从马斯洛需求层次的“生理需求”过度到“自我实现”。工作是用来“做”的，我们可以选择做我们喜欢的工作，当我们觉得工作无法带来我们需要的满足感/成就感/收入，可能我们就需要换一个工作了。换工作不一定是换公司，你也可以在公司内部通过换岗的方式来换工作。\n和工作不同，职业是不能“做”的，职业需要的是完善与规划。过年回家时长辈问“你是做什么工作的？”他们问的虽然是我们的职业，但又往往需要我们说出具体的工作，他们才能够理解。我在从教师职业转到程序员职业的时候，很多原来的同事都说我去“电脑城”工作了（那时候还没有京东，买电脑还是必须去电脑城的）。因为在他们眼中，所有从事和计算机相关的职业的人，一定是去电脑城卖电脑了。这说明，普通人喜欢把职业和工作划上等号的，这是对职业认识的不足。\n工作和职业的关系就好像战斗与战争的关系。职业需要通过具体的工作来体现。要解释自己的职业，就是要弄清楚自己做的所有工作之间的关系，再给它们贴上一个标签。工作可以选择，而职业需要规划。\n许多人满足于一生做一种职业，把自己的职业做到极致。我至今还记得2003年听过的一节特级教师示范课。特级教师能把枯燥无味的知识讲得栩栩如生，甚至让我这个作为教师（不是学生）坐在教室最后，并非他的教学对象的人也如痴如醉，忘了时间。这是积累到极致的力量。\n也有些人会陶醉于切换自己的职业，不断尝试新的体验。他们在不同的职业之间汲取营养，最终成为优秀的领导者，创业者，独立制作人，或者合伙人。\n还有些人对职业的选择是被动的，有什么做什么，例如在人力资源市场找工作的人。对于他们来说，职业和工作是一种概念，没有区别。\n很早以前看过一个段子：\n有位同学以非常优异的成绩毕业，直接留校工作。但是，在从事了教师工作2年之后， 他辞掉了大学教师工作，进入某个知名企业。随后，他经历了支教、销售、建筑等多个行业，甚至做起了搬运工（那个时候快递业并不发达，否则这个段子就该把京东快递编进去了）。他的同学有一次看到他，是在工地上看到他正在挥汗如雨地搬运建筑材料。而不久之后，传来的消息让大家大跌眼镜。这位同学顺利通过考核，进入了联合国教科文组织工作。\n原来，他在学习时就给自己定下了职业目标。由于联合国教科文组织的工作人员需要拥有5年以上和10个不同行业的工作经验，他不惜频繁切换工作来达到这个目标，最终凭借成绩的努力和信息获得了自己喜欢的职业。\n虽然这只是个段子，但包含的道理是正确的： 我们的职业需要规划 。想要达到我们的职业目标，就必须在工作过程中不断成长，不断进步。\n去还是留？ 员工离职有个232现象，就是分别在入职之后的2周、3个月（试用期）和2年这三个时间点容易离职。2周离职很可能是在入职的时候被HR欺骗，或者求职者自己对公司的理解有差异。3个月离职的原因一般是员工在试用期过程中发现公司的企业文化与自己并不合拍，可能有夸大。下面主要讨论一下2年离职。\n员工在一个公司做了2年，已经是资深老员工了。他在这个单位工作了2年的时间，说明从心底他认可公司的企业文化，热爱本职工作。但然而经过2年的时间，他很可能已经进入了自己的瓶颈期，他希望在自己目前的工作岗位上能够实现一个突破，能够得到学习新知识新技能的机会，想要升职或者进行工作轮换；也有可能他的身边没有足够牛逼的人可以学习，抑或对目前从事的项目不感兴趣。这时若公司如果忽略了他的需求，不给他提供足够的机会，不能给他工作扩大化，到两年这个节骨眼上老员工也就留不住了。\n一旦程序员萌生了换工作的意愿，找到下一个工作一般不是太难的事情。那么，如何评估新的工作是否就能符合自己的意愿呢？\n为了行文方便，下面采用第二人称叙述。\n可以试着问自己以下几个问题：\n1. 新的公司给你的承诺是真的么？\n许多 HR 在新人入职的时候，会给新人非常多的承诺。这些承诺有些可以兑现，有些很可能成为过眼云烟。例如薪资福利与HR介绍的有差异，当初应聘时HR介绍其入职后享有出差补贴、季度奖金等各项福利，然而真正入职以后发现补贴少的可怜根本不够用、季度奖金并不是每个人都能享受到的等等，实际收入与你期望的薪资要求差距甚远等等。这样的事情有可能是 HR 有意为之，也有可能是其他原因。但作为求职者，你需要有分辨的能力。\nHR 工作优秀的公司（许多拿到投资的公司都非常重视 HR 工作），很可能会在新人还没有入职的时候，给予一些特别的对待。例如尽快签订入职协议，提前给你制作工牌，每天用微信和你联系，寄送一些小礼物，并充分展示公司的优势等等。这些都是常用的手段，但却很奏效。作为求职者，要学会正确对待这些给你温情感觉的手段，学会认真、客观地看待公司，它毕竟是你的未来，而你需要为自己的未来负责。\n2. 你能不能拿到足够的资源？拥有足够的话语权？\n新的公司给你怎样的职位？ Title 并不重要，重要的是实际的资源。\n让我们考虑一个虚拟的公司，暂且命名为G公司吧。G公司是一个初创企业，已经拿到了A轮融资，现在全国拥有1000名以上的员工，并准备在武汉建立一个研发中心。你去应聘G公司的开发部门Leader职位，HR 承诺你会负责在武汉从无到有组织一个5人开发团队，并直接向北京的公司技术副总汇报。薪水方面，你能获得相当于原公司薪水30%的加薪，也可以选择期权激励计划。\n一个相当不错的工作，对么？等等，让我们分析一下其中的问题。\n首先，你的Boss不在武汉。这可能会导致你的工作状态无法被你的Boss知晓，你每天在忙什么？如何决策？如何开发？Boss只能从你的工作结果上看到。他无法完整地了解到你的特点，这可能会影响他对你工作的评价。同样的，你也无法知道你的Boss的工作状态和想法，而这些对你如何高效完成工作至关重要。比如年底汇报，资源分配这些决策，Boss可能只能看到一些冷冰冰的数据，无法了解你这个活生生的人。而作为 Team Leader，和Boss的情感交流是很重要的。\n其次，你组建的是一个5人团队。那就意味着在武汉有多个团队和你一起竞争。你的Boss会偏向哪个团队？你并不清楚。\n再次，G公司在武汉建立的是一个研发中心。这种选择往往意味着公司看重武汉的人才，以及较低的生活成本。这也同时意味着公司的工作重心并不在武汉。研发中心是只会花钱不会赚钱的部门，而赚钱对于拿到A轮融资的企业来说十分重要。如果碰到业务调整或者方向变化，首先裁撤的就是研发中心。还记得诺基亚、摩托罗拉研发中心的员工维权事件么？这就是研发中心架构的问题。\n当然，所有的研发中心都有这样的问题。并不是说研发和总部在一起就没有裁员风险。相对而言，和总部在一起，更能快速实时地了解到公司动向，不至于像研发中心那样到了裁员令发布之后才被动应对。\n最后，你能拿到的资源，取决Boss对你的信任程度，取决于你在工作上的表现。由于前面说到的原因，相对于能和Boss经常见面的状态，你的工作会更难进行。\n3. 新的公司的发展可能性？\n要回答这个问题，你要了解目前的经济走向，以及新公司从事的行业的前景。你可以从公司目前的业务发展情况、人员状况、在职人员的精神面貌、离职人员的口碑来判断。如果公司从事的是面向大众的业务，你更可以从公司的产品中看出这些趋势。做出判断很难，但这是你必须做的事情。\n你还要考虑公司目前是否盈利。拿到A轮融资的公司通常还处于烧钱阶段，并没有实现收支平衡。你需要判断，公司目前的状态，能在研发上投入多大的精力？是否有时间和资源支持你在业务上进行创新？是否允许你在已有架构的前提下做出合适的调整？仅仅考虑收入和增长，而没有持续的研发投入，是否能满足你的更高追求？\n当然，想要离开现在的公司，应该是经过了深思熟虑。有时候离开是被迫的选择，有时候离开则是你还做得不够。说到这里你可能会问：我都已经准备离职了，你凭什么说我做得不够？\n勇于改变 一个人所处的环境，往往和自己息息相关。当你认为这个环境不适合你的时候，环境并不一定真的到了像你所想的那样糟糕的状态。你需要知道，环境变成这样，一定有你的一份功劳。\n人和所处的环境是互相影响的。也许你会抱怨你的Boss不理解你，会抱怨目前工作无法让你得到技能上的提升，还会抱怨同事之间的关系不和谐。你需要问一下自己：是否为了这些抱怨做出过改变？\nBoss不理解你，很可能是你采取了不合适的沟通方式；工作技能没有提升，或许是因为你在工作中的投入不够；同事之间的关系不和谐，是不是因为你自己的性格问题？\n你有没有试图去“教育”你的Boss？没有人是完美的人，你的Boss也不例外。如果你真的确信你的观点正确，那么应该使用正确的方式让你的Boss理解和支持。做到了这一点，Boss不理解你的问题是否就迎刃而解了？\n所有人都有惰性，无一例外。当你做了多年工作之后，对正在从事的工作，会感觉缺少激情。你可能会认为，这仅仅是熟能生巧而已，到了后来，不需要花什么太大精力就能完成工作。\n但是，想象这个Case：你能不能把以前需要3天完成的工作，在1天内完成？孰能生巧仅仅是量变而已，如果能做到用原来三分之一的时间完成工作，才是从量变到了质变。为了达到这个目标，你必须全力思考更加新颖的方式，和其他同事更加积极地合作，从Boss那里争取一切可以争取的资源。把这一切都做成了，你还认为工作技能没有提升么？这样做提升的是完整的工作技能树，而不仅仅是工作技能本身。\n抱怨工作环境有问题的人，往往是自身有问题。环境不合适，你要么改变自己去适合它，要么就改变它使其适合你。一旦你尝试了所有的可能，并发挥了自己全部的力量，还没有达到你改变环境的目的，那么唯一的可能就是你和公司的基因不和。这时在带着你的历练离开，岂不更美？\n不过，朋友，你真的尝试过了么？\n勇于尝试 没有熬过夜的人不需要面对第二天的糟糕感觉，但也无法享受夜深人静时的孤独和高效；没有登过山的人不需要竭尽全力和面对受伤的危险，但也无法享受在峰顶看朝阳从云层中喷薄而出的快乐。如果没有做过改变，如何能知道自己做不到呢？\n如果能够和熟悉的同事一起努力，改造当前的工作环境和方法，利用自己的力量和能力改变公司的现状，让停步不前的业务发生翻天覆地的变化，让你的想法得到Boss的全力支持，那么，留在原来的公司一定是更好的选择。\n一点思考，与君共勉。\n（全文完）\n","date":"2016-03-16","description":"","lastmod":"2016-03-16T02:29:45Z","slug":"choose-your-career","tags":["management","choice"],"title":"工作选择与职业规划","url":"https://blog.zengrong.net/post/choose-your-career/"},{"categories":["technology"],"content":"3月3日，我被这篇文章轰炸了： Firefox联手Chrome合作开发网页VR标准 。文中提到：\n随着1.0版WebVR API的完成，Mozilla已经收到了许多开发者发回的反馈。值得注意的是，该公司已经改进了如下内容：\n—以虚拟现实为核心的设备渲染和显示标准； —WebVR页面之间的遍历链接能力； —能够枚举虚拟现实输入的输入处理机制，包括6轴动作手柄； —适应坐姿和站姿两种体验； —使用桌面和移动平台。\n在获得认可后，Mozilla还计划于今年上半年在Firefox Nightly中推出一个WebVR 1.0的工作版本。如果你勇于探索，可以从布兰登-琼斯那里下载几个实验版Chromium浏览器，体验这种API的概念验证效果。\n我们知道，Mozilla 的 WebVR API 早已在 2015年7月 推出草案，那么这篇没有任何引用的语焉不详的 WebVR 1.0 是啥？有趣的是，中文互联网上该文章大部分为转载，内容完全相同（甚至更少）。这让我开始质疑其内容的完整和正确性。\n稍微搜索一下，发现原文应该是这篇 Introducing the WebVR 1.0 API Proposal ，译者可能是偷懒，也可能是并非技术人员，仅仅翻译了新闻部分，没有翻译代码部分。看看 WebVR 草案 中的 Editors 信息，3 个 Mozilla 的人，1 个 Google 的人，我们就能了解到，目前暂时只有 Firefox 和 Chrome 陪玩了。\n回到本文的标题，身为开发者，我们该如何更新自己的技能，才能适应这个新的平台？更准确的说，我们应该学习什么编程语言/或3D引擎(3D Engine)，才能适应 VR 时代的发展？\nVR 设备分析 让我们先来看看现在的 VR 设备。\n目前主要的 VR 设备有 Oculus , Sumsang Gear VR ， HTC Vive ， Microsoft HoloLens ， Sony PlayStation VR ，国内的 VR 设备则太多，销量较高的有 蚁视 和 暴风魔镜 等等。\nOculus 代表产品： Oculus Rift\n简单介绍： 万众瞩目的 Oculus Rift 已经预售了。简单地说，Oculus Rift 是一个带有部分输入设备的虚拟现实显示器。这些输入设备包括一个遥控器，一个红外传感器，还有 Touch 控制器。Rift 的所有计算是在一台 PC 上进行的，在 Oculus Ready PCs 可以看到 Rift 要求的 PC 配置。由于显卡的性能问题，Rift 目前并不支持 Mac，只能下载到基于 Windows 的 Runtime。\nSDK支持： Oculus Rift 支持 Unity 5 、Unity 4 和 Unreal 。3月1日，Oculus 发布了 MobileSDK 1.0.0.1 升级，这个 SDK 提供了对 Oculus Remote Monitor 的支持，同时也让我们可以在 Mac OSX 上基于 MobileSDK 进行开发。\nSamsung 代表产品： Gear VR\n简单介绍： Gear VR 是 Samsung 和 Oculus 共同开发的。它目前使用 Samsung 的 Galaxy S7/S7 edge, Note5, S6, 和 S6 edge 来代替头显中原来的显示器。Gear VR 还内置传感器用于和三星手机配对，并内置了触摸板用于操作。和 Oculus Rift 使用 PC 来进行计算不同，Gear VR 把计算放在了 Samsung 手机上。\nSDK支持： Oculus 的 MobileSDK 最开始就是为 Gear VR 提供的。\nHTC 代表产品： Vive\n简单介绍： Vive 也已经预售了。它是目前呼声最高的消费级 VR 设备了。Base stations 设计，电子围栏，多功能操纵手柄，StreamVR，良好的体验都让它成为目前最好的 VR 设备。在 VIVE OPTIMIZED PCs 可以看到推荐的 PC 配置，和 Oculus 类似，但不要求 8G 内存 :-) 。\nSDK支持： Steam VR 、Unity 和 Unreal 。\nMicrosoft 代表产品：HoloLens\n简单介绍： M$ 的确是不差钱，一上来就搞出来一个 MR(Mixed Reality) 设备，还让 HeloLens 上了国际空间站 。简单的说，HoloLens 就是一台微型 Windows 10 一体机啊！和竞争对手们完全不是一个打法好么？不在一个纬度好么！硬件我就不说了，直接看 Hardware 好了，简直惊悚。\nSDK支持： 为 HoloLens 开发应用，其实就是为 Windows 10 开发 App ，目前官方页面并没有公布太多的开发信息。但你也知道，IDE 一定是宇宙最强的 Visual Studio，语言一定是 C# 。\nSony 代表产品： PlayStation VR\n简单介绍： Sony 大法虽然好，但 PSVR 和上面几家比起来会出来的比较晚，又从 2016 上半年跳票到下半年。它是和 PS4 共同使用的，目前我没有找到它的 SDK。但 Unity 提到了对 PlayStation VR 的支持。\n暴风魔镜 代表产品： 魔镜4\n简单介绍： 暴风魔镜是目前在国内大肆宣传的一款产品。暂时还没有哪一款产品的宣传力度超过它。当然，它也是我唯一买过（主要是买得起）的产品。和 Google Cardboard 类似，它是一款把手机塞进去就能体验廉价 VR 的产品。具体的体验效果，取决于手机的高（tu）端（hao）程度。\nSDK支持：暴风魔镜提供了 Android/iOS/Unity/Unreal 的 SDK 。\nVR 引擎分析 从上面的设备分析可以看出，目前对 VR 支持最好的 3D 引擎就是 Unity 和 Unreal 了。它们两位可以说是 VR 的标配引擎。借用一句广告词： 2U ，你值得拥有！\n除了 2U 哥俩外，下面还有一些在 VR/AR 界十分活跃的引擎和软件：\nOSVR Open Source Virtual Reality 是一个全面开源的软件平台，支持多种设备和引擎，Blender/MonoGame/StreamVR/Unity/Unreal/CryEngine/WebVR 都在支持之列。在国内，OSVR 已经和 360 公司展开了合作。\nOSVR是全球的开源虚拟现实平台，由Razer(雷蛇)——全球高性能游戏硬件、软件与系统品牌，以及Sensics——全球专业级虚拟现实头戴式显示器品牌联合创立。该软件平台致力于为所有虚拟现实技术树立开放标准，使得各款支持OSVR的虚拟设备与支持OSVR的软件能够顺利交互、接合。\nOSVR软件 介绍了这套引擎的架构方式。\nOSVR能够提供简单而标准化的方式，助你探索、配置和操作数以百计的设备，包括VR眼镜、定位追踪、深度摄像头、眼动议、游戏控制器等等。\nVRPN Virtual Reality Peripheral Network 是学术圈搞起来的一套基于网络传输界面的开源 VR 库。允许大家共同建设，把自家的设备加进去。这个库支持非常多的设备，甚至直接包含了设备驱动 Supported hardware devices 。VRPN 已经在 PC/Win32, PC/Cygwin, PC/Linux, and Mac/OSX (32- and 64-bits on all), ARM Linux systems 和 Android 上测试过。\nMiddleVR MiddleVR 目前提供 Unity 插件 ，当然你也可以直接使用它的 SDK，它也支持目前大多数主流的 HMD (Head-mounted display）设备。虽然是由一家公司在维护，但目前是免费使用的。\nVR 语言 说起 VR 语言，其实还不如说是 3D 引擎的语言。VR 没有 3D 引擎支持是无法进行开发的。让我们看看上面提到的 3D 引擎和 SDK 主要使用什么语言。\n首当其冲的是 C/C++ ，这是毋庸置疑的。大多数 3D 引擎都使用 C++ 开发，Oculus 提供的 SDK 也是使用 C++ 进行开发的。 Gear VR 是在安卓设备上运行，需要使用 Android NDK 基于 C++ 进行开发。Unreal 引擎同样使用 C++ 进行开发。MiddleVR 提供了基于 C++ 的 SDK。\n其次是 C# 。 Unity 把 C# 当作脚本语言使用。在 VRPN 中可以使用 .NET bindings for VRPN 作为开发语言。不出意外的话， HoloLens 也一定使用的是 C# 。\n再次就是 Javascript 。它也是 Unity 的一种脚本语言（即使选择的人并不多）。但 WebVR 和 Javascript 程序员的基数（基友数？）一定会让 Javascript 继续在使用人数上占优（我真的不是黑啊）。即使不考虑 WebVR ， three.js ， Babylonjs 这类已经非常成熟的 HTML5 3D 引擎也足够证明 Javascript 在 3D 上的强大生命力。\nVR 平台 从目前的状态看， Oculus 仅支持 Windows 和 Android（通过 Gear VR） 。MiddleVR 仅支持 Windows 平台。其他的几个引擎例如 OSVR/VRPN 都是多平台支持的，Unity 和 Unreal 当然也是多平台支持。Playstation VR 无疑是 PS 独占。HoloLens 则无疑是 Windows 独占。\n只有 WebVR ，不挑平台陪你玩。\n结语 VR/AR/MR 仍然在如火如荼的发展。以上的技术 1~2 年后或许会有翻天覆地的变化，领跑者说不定就成了追赶者，黑马也可能随时出现。但总的来说，VR 在目前这个阶段的发展，最重要的仍是内容。内容的产出依然离不开程序员，离不开 3D 引擎和编程语言。作为程序员，只要我们苦练内功，紧跟技术发展的脚步，就不必担心被时代所遗弃。若是不小心搭上了 VR 时代的快车，或许更容易一飞冲天。\n而当下，我们需要一往无前。\n（全文完）\n","date":"2016-03-06","description":"","lastmod":"2016-03-06T02:32:24Z","slug":"what-program-languane-in-vr-time","tags":["vr","3d","html5"],"title":"VR 时代，我们应该如何编程？","url":"https://blog.zengrong.net/post/what-program-languane-in-vr-time/"},{"categories":["technology"],"content":"现象 cocos2d-x 2.2.6 项目的源码中使用了 std::to_string() 方法，使用 NDK r9d 编译的时候，报如下错误：\n1error: \u0026#39;to_string\u0026#39; is not a member of \u0026#39;std\u0026#39; Application.mk 文件的部分内容如下：\n1APP_STL := gnustl_static 2NDK_TOOLCHAIN_VERSION := 4.8 3APP_CPPFLAGS := -frtti -std=c++11 -fexceptions -Wno-error=format-security -Wno-literal-suffix -Wno-deprecated-declarations -fsigned-char -Os $(CPPFLAGS) 换用 NDK 10e ，错误依然。\n快速解决 想快速解决这个问题，可以自己写一个 std::to_string() 替换标准库中的。\n首先写一个 stdtostring.h 文件：\n1#ifndef STDTOSTRING_H 2#define STDTOSTRING_H 3#include \u0026lt;string\u0026gt; 4#include \u0026lt;sstream\u0026gt; 5 6using namespace std; 7namespace std 8{ 9 template \u0026lt; typename T \u0026gt; std::string to_string( const T\u0026amp; n ) 10 { 11 std::ostringstream stm ; 12 stm \u0026lt;\u0026lt; n ; 13 return stm.str() ; 14 } 15} 16#endif 然后在需要使用 std::to_stirng() 方法的源文件中包含它：\n1#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID 2#include \u0026#34;stdtostring.h\u0026#34; 3#endif 追根溯源 Why gnustl_static 中介绍了采用 c++_static 和 clang 编译器来实现 std::to_string() 支持，于是我尝试了下：\n1# APP_STL := gnustl_static 2# NDK_TOOLCHAIN_VERSION := 4.8 3APP_STL := c++_static 4NDK_TOOLCHAIN_VERSION := clang 根据 C++ Library Support 的说明，c++_static 对应的是 The LLVM libc++ runtime (static) ，因此应该采用 clang 的工具链进行编译。\n结果是： std::to_string() 编译正常，但出现下面的错误：\n1In file included from src/Utilities.cpp:1:0: 2android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1498:46: error: expected unqualified-id before \u0026#39;float\u0026#39; 3inline _LIBCPP_INLINE_VISIBILITY float remainder(float __x, float __y) _NOEXCEPT {return remainderf(__x, __y);} 4 ^ 5android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1498:46: error: expected \u0026#39;)\u0026#39; before \u0026#39;float\u0026#39; 6android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1499:46: error: expected unqualified-id before \u0026#39;long\u0026#39; 7inline _LIBCPP_INLINE_VISIBILITY long double remainder(long double __x, long double __y) _NOEXCEPT {return remainderl(__x, __y);} 8 ^ 9android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1499:46: error: expected \u0026#39;)\u0026#39; before \u0026#39;long\u0026#39; 10android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1509:1: error: expected \u0026#39;)\u0026#39; before \u0026#39;__x\u0026#39; 11remainder(_A1 __x, _A2 __y) _NOEXCEPT 12^ 13android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1559:46: error: expected unqualified-id before \u0026#39;float\u0026#39; 14inline _LIBCPP_INLINE_VISIBILITY float round(float __x) _NOEXCEPT {return roundf(__x);} 15 ^ 16android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1559:46: error: expected \u0026#39;)\u0026#39; before \u0026#39;float\u0026#39; 17android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1560:46: error: expected unqualified-id before \u0026#39;long\u0026#39; 18inline _LIBCPP_INLINE_VISIBILITY long double round(long double __x) _NOEXCEPT {return roundl(__x);} 19 ^ 20android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1560:46: error: expected \u0026#39;)\u0026#39; before \u0026#39;long\u0026#39; 21android-ndk-r9d/sources/cxx-stl/llvm-libc++/libcxx/include/cmath:1565:1: error: expected \u0026#39;)\u0026#39; before \u0026#39;__x\u0026#39; 22round(_A1 __x) _NOEXCEPT {return round((double)__x);} minggo 在 Cocos can't compile Android with APP_STL := c++_static 中提到：\nA prebuilt version can only support one type of c++ lib. Did you mean we should provide two kinds of prebuilt libcocos2d that using two different c++ libs?\nI don't know if cocos2d can compile with c++_static or not. I don't remember if there is a good reason to choose one instead of other one. But cocos2d-x uses gnustl_static from very early version, so we know it works well. You can replace it with c++_static if you like, but it is not fully tested.\n因此我怀疑在 cocos2d-x 的旧版本中，c++_static 是没有经过完整测试的。\n也有人说 在新版本（\u0026gt;3.7）中， gnustl_static 会导致 lambda 和 std::to_string 不能使用，已经切换到使用 c++_static 和 clange 组合。\nI never made it to work with gnustl_static and gcc with all the c++11 features. I was getting crashes in simple lambdas, as mentioned std::to_string was missing and much more, so i've switched to c++_static and clang long time ago.\n同样也有人做了证明 。\n既然目前我们依然在使用旧版本，就不折腾了。\n全文完 ","date":"2016-02-23","description":"","lastmod":"2017-08-02T05:25:21Z","slug":"std-to-string-in-ndk","tags":["android","ndk","cpp","cocos2d-x"],"title":"to_string is not a member of std","url":"https://blog.zengrong.net/post/std-to-string-in-ndk/"},{"categories":["technology"],"content":"Apple Pay 在 2016-02-18 发布，朋友圈和公众号就被刷屏了。其实自从库克同学背着乔老爷子给 iPhone 装上了 NFC 之后，我们就都知道这一天迟早会到来的。\n但是 Apple Pay 并不是什么 Apple 的超级武器， 甚至不是什么新东西 。Apple Pay 的最大得利者，很可能不是 Apple，而是 中国银联 。Apple Pay 是中国银联用来对抗支付宝和微信的一把剑。当然，这个合作是双赢的， 中国银联的“云支付”开始支持 Apple Pay， 至少能帮助可怜的 AppStore 服务器承担一点点支付失败绑定银行卡失败的骂名吧……至于说 Apple Pay 这把剑能不能如愿以偿地让中国银联拳打支付宝脚踢微信支付，我们就只能拭目以待了。\n如果大家厌烦了这几天各大媒体、各个公众号像教傻X一样教学怎么在 Apple Pay 中绑定银行卡的话，就听我来说说后面的故事。\n[TOC]\n闪付 细心的同学可能会注意到，许多新办理的银行卡上都会有一个 QuickPass 的小标志，越来越多的银联 POS 机上也出现了同样的标志，这就是 闪付 。\n从银联的网站可以看出，闪付功能在 2012 年就已经推出，但一直处于不愠不火的状态。除了支持闪付的 POS 机更新缓慢的原因外，另一个重要的原因就是圈存麻烦。\n闪付功能虽然包含在银联卡中，但它和公交卡类似，是不记名，和银联账户没有关联的单独账户。要使用闪付功能，必须到指定银行的圈存机（一般是带有闪付功能的ATM机）上向闪付账户中转账。仅这一点就卡住了许多使用者。而且，银联的推广也并不给力。我是在 2015 年下半年才在麦当劳门店看到使用闪付的优惠（用闪付支付半价）。\n说白了，闪付就是在银行卡中集成了一个独立的支持 NFC 功能的账户，用于非接触式的小额支付。商家必须有支付闪付的 POS 机才能使用。详见 银联网站的介绍 。\nNFC NFC (Near Field Communication) 是一种短距离高频无线电技术，详细的介绍可以参见 百度百科 （很抱歉维基百科打不开） 。\nNFC 在手机上也一度非常流行，Google 从 Nexus S 开始就在的亲儿子们里面集成了 NFC 功能。小米 2S/3 也集成了 NFC 芯片。\n不过，即使是许多手机都开始支持 NFC 功能，NFC 的主要功能还是限制在帮支持的公交卡充值、以及手机面对面对传之类的简单应用上。可能正因为如此， NFC 在火了一阵子之后沉寂了下来。乔布斯发声说 NFC 是 a trick 之后，许多厂商似乎打了退堂鼓。小米3就取消了 NFC 的支持，当然在小米5中又加上了。\n各方博弈 NFC 是天生为支付而生的技术。为什么一直没有发展起来？主要是各方博弈的结果。\n兼容性 以目前支持最广泛的 NFC 技术 —— 公交卡 为例。为什么有的公交卡可以被 NFC 手机识别甚至充值，有的却不行？ 你的NFC手机为何不能读公交卡？ 一文做出了解释：\n一个完备的NFC产品，应该包括三个部分：NFC硬件、统一的系统接口支持、App。这个结构和蓝牙产品、WiFi产品没啥区别，只是NFC多用于支付领域，所以它的App最好能带支付接口。\n在这三个部分中，都存在不同的方案，各家互相兼容的状况有差异，其中以硬件部分最甚。\nNFC的硬件部分并不复杂，由CLF（非接前端模块）、射频天线、SE（安全区域）三部分构成。只是读公交卡以及充值，CLF+天线就够了；如果还想把手机App做成电子公交卡（支持空中发卡和空中圈存），那么还需要SE部分。\n前边提的读卡兼容问题，出在CLF的NFC芯片上。目前国内主流的手机NFC芯片供应商有恩智浦、博通、联发科、三星电子，恩智浦为业内份额第一，博通次之，联发科、三星电子更次，其中三星电子的量主要在Note 4这款机型上。\n各家芯片公司在NFC的公开标准上，都会有自己的实现，比如有些更兼容老的标准，有的选用了标准内的不同方法，诸如此类等等。如果不能读卡，基本上可以确认是不同芯片之间实现方法不同所造成的兼容问题。\n上面提到的 NFC 的 SE 部分，目前有三种技术，它们是：\neSE、NFC-SIM 和 HCE Apple Pay 属于 eSE，它将加密信息存储在手机独立的安全芯片里。\nNFC-SIM 是有运营商主导的安全方案，也就是常见的各类手机钱包，它将加密信息储存在了 SIM 卡里，因此需要去营业厅更换特制的 SIM 卡才能使用。\n银联的云闪付则使用的是 HCE (Host Card Emulation) 方案，它被业内人士视为移动支付的未来趋势。它将加密信息交由银行的云端服务器来处理，不需要手机制造商以及运营商参与。HCE 的优势在于减少了对硬件的依赖，既不需要专门的安全芯片，也不需要专用的 SIM 卡。只要手机的 NFC 设计符合规范，就能方便地进行「刷卡购物」了。\n顺便说一句， Android Pay 使用的也是 HCE 方案。\n对于HCE方案的理解可以参考这篇文章： HCE方案全面解析：特点、安全及展望\n从上面的分析可以看出，正是不同的厂商之间的博弈，造成了 NFC 的普及困难。也正是由于芯片厂商、移动设备制造商、电信运营商、银行之间的竞争，造成了 NFC 技术的欣欣向荣。\nHCE 和 SE方案的比较：\nHCE 方案 SE 方案 手机NFC支持 要求 要求 Android 版本 4.4 以上 2.3 以上 手机设备硬件 通用设备+普通UIM卡 定制设备+SWP卡 应用实现 手机客户端APP SE 内的Applet 网络要求 需要联机 支持离线方式 应用安全性 较高，可以从UIM卡中获取一定信息作为验证 高 POS 终端兼容性 兼容既有空片受理终端 兼容既有空片受理终端 用户体验 一致，无需换卡 一致，需换卡 武汉通 武汉通 是武汉的公交卡，目前已经可以使用 QQ 钱包给武汉通充值 。但是，如果乘车不想刷卡而是希望刷手机的话，还必须换用 武汉通NFC-SIM卡 。\n最早的武汉通 SIM 卡是这样的：\nSIM 卡插在卡槽中，那个黑色的部分则是 NFC 天线。我当时超级想换一张这样的卡，可是跑了3个营业厅都没货，最终放弃。\n根据 手机武汉通 网站的描述，现在的武汉通 SIM 卡貌似不再自带 NFC 天线，而是转而要求手机必须具有 NFC 功能。\n从上面的资料看来，武汉通应该采用的是 SE 方案。\n详情参见得意生活的帖子： 【武汉通实用帖】不用花钱办公交卡的方法有很多！附办理武汉通手机SIM亲身经历及经验~\n云闪付 中国银联早在 2015-12-12 就针对安卓手机开通了 “云闪付” 这个业务。也就是说，安卓手机是比 Apple Pay 提前了2个多月实现了 “刷手机” 支付的功能。\n云闪付要求手机支持 NFC 功能，并在各个支持云闪付功能的手机银行 App 中进行卡片绑定，然后就和 Apple Pay 一样，可以在 QuickPass 的 POS 机上刷卡了。注意这个 QuickPass ！\n当然，我的屌丝机红米 Note3 没有 NFC 功能，所以招商银行手机银行的“一闪通”功能（就是云闪付的招行版本，每个银行的名称可能不同）非常客气地提醒我要换手机了：\n简单说，云闪付就是采用 HCE 与 TOKEN 技术，在手机中虚拟出一张银行卡。这张银行卡和真实的银行卡绑定，每次支付的时候 TOKEN 都会变化以保证支付安全。消费时可能要输入银行卡密码。\n如果想看看具体怎么实现的，参考这篇文章： 中国银联联合多方发布“云闪付”刷手机等同刷银行卡 。\nApple Pay 了解了云闪付，再看看 Apple Pay 。请注意银联官网的文章标题： 银联云闪付开通支持Apple Pay服务 。也就是说， Apple Pay 是云闪付的一部分 。\n和安卓的云闪付不同的是，Apple Pay 可以使用指纹代替密码来授权。但是在安卓手机普及指纹识别的今天，我相信各大银行的 App 升级支持指纹代替银行卡密码仅仅是时间问题。\n另一个不同可能是，Apple Pay 依然利用了 iPhone 手机中的安全芯片。但安卓的云闪付显然就完全基于 HCE 了。\n注意！ Apple Pay 在线下也是依赖银联的 QuickPass 提供服务的！所以，中国银联和 Apple Pay 是要抱团取暖啊！毕竟在中国，支付宝和微信支付已经跑在前面了。\n支付宝和微信支付 直接看这个比较好，我就不废话了：\n易图看懂：Apple Pay与支付宝/微信的区别\n其他 Rubberso 在 我不用 iPhone，但也想「刷手机购物」怎么办？ 中提到：\n如果你在使用三星 Galaxy S6/S6 edge/S6 edge+ 或者 Galaxy Note5 手机，那可以不用羡慕 iPhone 用户了。\n在去年 12 月 18 日苹果和银联宣布合作的同时，银联还宣布与 Samsung Pay 达成了合作。除了和 Apple Pay 类似的非接触式（NFC）支付方案，Samsung Pay 还支持 MST 模拟卡方案——这意味着在不支持银联的闪付（QuickPass）的普通 POS 机上，Samsung Pay 依然可以使用。\n通过手机发射磁力信号，MST 能实现与刷普通磁条卡相同的效果，商家不需要进行任何设备升级正如像三星硬件工程师戈蓝 V 在微博上说的：\n第一，就应用场景而言～Samsung Pay 支持所有的 Pos 机，而 Apple Pay 只支持带有 NFC 的 (和银联闪付等同)。就统计而言，目前国内支持 NFC 的 POS 大概 700W（感谢银联的大力推广）, 而全国的 POS 机有 1800W 左右，百分之 40 左右，大城市会高一点。\n从这点来看，棒子还是挺努力的。\n而 Android Pay 已经哭晕在厕所……\n参考资料 银联“闪付”（Quick Pass）：快捷支付新体验 NFC Why does the iPhone 4S lack NFC support? 你的NFC手机为何不能读公交卡？ Android Pay HCE方案全面解析：特点、安全及展望 武汉通联合腾讯财付通在全国率先实现手机QQ钱包充值 手机武汉通办理 【武汉通实用帖】不用花钱办公交卡的方法有很多！附办理武汉通手机SIM亲身经历及经验~ 银联云闪付开通支持Apple Pay服务 我不用 iPhone，但也想「刷手机购物」怎么办？ 易图看懂：Apple Pay与支付宝/微信的区别 （全文完）\n","date":"2016-02-21","description":"","lastmod":"2016-02-21T13:18:19Z","slug":"applepay-nfc-and-cloud-quick-pass","tags":["ios","android","mobile"],"title":"八一八 Apple Pay 、NFC 和“云闪付”","url":"https://blog.zengrong.net/post/applepay-nfc-and-cloud-quick-pass/"},{"categories":["technology"],"content":"这诡异的事情发生在今天早上，打开 Keychain Access 一看，无论是开发者证书还是发布证书，所有的 iPhone 证书全部无效了。\n重启还是 revoke 之后重新生成证书都不管用。\n真实的原因是： Apple Worldwide Developer Relations Certification Authority 失效了。\n所有的 Apple 证书都使用这个证书签发，它失效了，其他的证书完全不能幸免。\n解决方案，打开 Keychain Access -\u0026gt; (Keychains)System -\u0026gt; (Category)Certificates ，可以看到两个 Apple Worldwide Developer Relations Certification Authority 证书，一个有效一个失效。失效的那个证书的过期时间正好是 2016-02-16 （昨天）。\n若看不到，单击 Keychain Access -\u0026gt; （菜单）View -\u0026gt; Show Expired Certificates 。\n删除这个证书，保留有效的那一个即可。\n截图为证。（因为手快，删除之前忘记截图了，现在只剩那个有效的证书了）\n（全文完）\n","date":"2016-02-17","description":"","lastmod":"2016-02-17T03:46:19Z","slug":"iphone-certificates-is-not-valid","tags":["ios"],"title":"过了个年，iPhone Certificates 全部失效","url":"https://blog.zengrong.net/post/iphone-certificates-is-not-valid/"},{"categories":["technology"],"content":"去年11月买的 Pebble 用了一个多月就花屏了，这质量很糟糕啊。好在 Pebble 的售后还是很给力的，直接给换新了。由于旧的手表不用退回，自己动动手就能修好。下面说说流程。\n换新 1. 在手机上 Pebble 的 App 中点击 Connect Support ，Pebble App 会自动启动手机中邮件客户端，把手机型号和 Pebble 型号带上，在这封邮件中，你可以描述一下 Pebble 的问题，然后附上照片。\n2. 24小时内会收到 Pebble 客服的确认邮件。如果确认需要换新，再过24小时会收到一封要求提供地址信息的邮件，大概是这样的：\nThanks for helping me diagnose this problem. Let's get you Pebbled again as soon as possible!\nTo make sure I fill out your Replacement Authorization with the correct information, please confirm the following:\nYour permanent shipping address in the following format: Name: Street Address 1: Street Address 2: City: Region/State: Postal Code: Country: Phone Number:\nThe style and color of your defective Pebble watch\nThe serial number of your defective watch\nPlease also include a picture of the back of your Pebble with the Serial Number clearly shown and \u0026quot;Case #XXXXXX\u0026quot; written beneath it on a sheet of paper.\nThis watch was not registered with the email address you've contacted us from. Can you please confirm the email address associated to the Pebble account this watch was activated with?\nOnce you have confirmed the above, we can proceed with the RMA process.\n邮件中提供了一个 Case 号码，要求将手表背面朝上放在一张写有 Case 号码的纸上，拍一张照片发过去。\n同时，由于我发送 support 邮件的邮箱并不是 Pebble 帐号的邮箱，邮件中还要求我提供 Pebble 注册帐号的邮箱。\n3. 接着，会收到 Pebble 的一个订单邮件，告知你购买了 Pebble ，当然这个购买是不必花钱的。\n4. 然后，会收到一个带有快递单号的邮件。发往中国的快递是使用 singpost.com 提供的服务，快递状态可以在网站上查询，当然只有一些基本状态而已。\n5. 剩下就是等待了！\n维修 Pebble 花屏的原因就是排线松掉了，排线松掉的原因就是模具没做好。\n上图中黄色圈的部分，作用是压住排线，是因为它的尺寸差异导致无法压住排线，最终导致花屏。\n上图中黄色圈的部分是排线。维修方法就是在上面粘几层透明胶（我粘了5层），然后重新拧上螺丝就OK！\n维修注意事项 拆机的时候一定要注意！振动器（圆形部分）是粘在后盖上的，但振动器的供电线是连在主板上的。如果一下就拉开后盖的话，后果就像我上面一样！振动器和后盖是用双面胶粘上的，很容易取下来。\n装回去的时候要注意密封胶圈，若是没有装好会影响防水。\n（全文完）\n","date":"2016-02-12","description":"","lastmod":"2016-02-12T12:30:54Z","slug":"pebble-classic","tags":["hardware"],"title":"Pebble Classic 的售后和花屏维修","url":"https://blog.zengrong.net/post/pebble-classic/"},{"categories":["impressions"],"content":"想这个问题很久了。自从担任 技术 Leader 之后，这个问题就一直盘桓在我心头，如何分配时间也让我经常焦虑。今天在我的 游戏部门推荐书单汇总 一文中，从筠网友发了这样一篇评论：\n为什么不继续发技术帖了？为我们后来人指导一下？您那么多的Lua等技术储备……不发点文章出来，可惜了！经常写写，或者一周一文也好！教导一下后辈，也好继续把你的精神发扬光大！\n其实我早已在制定2016计划的时候想清楚了这个问题，但我觉得现在，必须写一篇东西，给自己一点鼓励，也算是对这位提醒我的 从筠 网友的感谢和反馈。\n我从2015年5月下旬开始做 Team Leader ，现在8个月过去了。如果一定要用一个词形容一下的话，那就是： 痛并快乐着 。\n先说痛。\n人的精力和时间总是有限的。我在团队管理上花的时间越多，那么花在技术上的精力就会不足。我眼睁睁看着那么多好玩的东西出现，但没有太多的时间去深入研究它们。即使技术相关的工作，例如对现有的框架做技术调整，或者设计 手机游戏开发框架 ，也只是做好架构设计即可，不必亲手写代码。大多数的时间，都用来 review 别人的代码，以保证实现不出现偏差。\n但是，实现难免会出现偏差。即使是在设计的过程中巨细无遗（实际上也很难做到没有缺漏），也不能保证负责实现的同事就能有相同的理解。这样，当实现已经接近完成的时候发现偏差，将这些设计进行纠正就是一个非常复杂的过程了。\n很多时候，我恨不得挽起袖子自己干。但大多数情况下我不得不打消这个念头。 Team Leader 的任务，不是要自己做完所有的事（实际上也不可能），而是能 让团队一起做好所有的事 ，并保证大家在一起 好像一个人在做事 。如果做不到，就不是一个称职的 Leader 。\n为了达到 做一个称职 Leader 的目标，我不得不多头开展工作，把技术框架升级、技术规范建设、技术培训、流程规范、人事相关工作一同启动。这样的后果就是，我把大部分的精力都放在了管理上。对我来说这是痛苦的，但想把事情做到极致的习惯又迫使我不能放下任何一项。因为我清楚，这些基础工作都是其他工作开展的前提。\n再说快乐。\n开始 Team Leader 工作之后，我才发现面对的是一个复杂的现状。4个游戏项目一共使用了2套不兼容的框架，每个游戏项目都有互相独立的开发团队，开发团队之间交流甚少，重复造轮子的现象普遍。大家习惯了目前的工作方式，不愿意改变……\n但我喜欢从好的方面来看待这个现状。我掌握了更多的资源，可以从团队角度去印证之前创业时积累的许多想法。这些想法，在创业的过程中是无法进行验证的，一来没有那么多人手，二来创业团队往往是无法进行并行项目开发的。现在的4个项目并行的架构则刚好符合验证条件。通过我的努力，让所有客户端程序员在一起默契工作，既完成各自独立的项目，又互相亲密无间地合作，还能保证所有项目的产出都能共享，同时确保底层框架的一致……这可是一个大挑战。\n到目前，上面的目标虽不能说全部达成，但已经逐步上路，这说明团队磨合已经初见成效了。\n把更多的精力投入到管理中，让我可以全身心地去思考工作安排和时间分配，完全放开手脚去推动工作的进程，跟踪每一个细节，影响每一个相关的人。我渐渐发现，我们和其他团队之间的配合更加顺畅了，联系更加紧密了，以前许多没有头绪的事情，大家都知道应该怎么去完成了，遇到重要的问题，能够在最快的时间内找到正确的人了。很多棘手的事情，甚至不需要我来跟进了。不止一个其他部门同事和我说过，感觉到我们的工作方式有些不一样了。我倒真的愿意相信这是我那些努力得到的一点点成果。\n既然前面是 痛并快乐着 ，接下来的事情应该就是 找乐子 了！\n管理工作已经走上正轨，我不需要分出那么多的精力制定制度，推动流程了。我在 2016 年我能分出大部分的精力去做技术相关的工作，真正做到把 客户端技能树 落实。\n那些好玩的技术，我来了！\n（全文完）\n","date":"2016-01-29","description":"","lastmod":"2016-01-29T09:30:13Z","slug":"how-to-choose-in-management-and-technology","tags":["management","choice"],"title":"在技术和管理中选择","url":"https://blog.zengrong.net/post/how-to-choose-in-management-and-technology/"},{"categories":["impressions"],"content":"这篇文章是我在知乎上的一个回答。\n问题：\n如题，怎么做好互联网公司的技术团队负责人。互联网公司的技术团队负责人应该具备怎样的能力\n回答原文：\nhttps://www.zhihu.com/question/39421456/answer/81853837\n利益相关：4年创业经验，目前带手机游戏前端团队。\n1. 在创业公司，最重要的是人，其次才是团队 先就说说这个有负能量的事情。有多负能量？先看看这个： 游戏逻辑程序员的成长出路？ - 曾嵘的回答\n其实本节的标题可以进一步理解成：必须要有足够好的人，才能通过团队负责人的努力得到一个合格的团队。现在这个时代，不是一个人牛逼就能成事儿的。\n当然，你可以自豪的说：以我的能力，一帮乌合之众（ 《乌合之众》读书笔记 ）也能被我带好了！这种豪言壮语我也说过（哦，是想过），而现在我只能呵呵一笑：兄弟， U NB,U UP！\n看看上面那个负能量的例子，想一想：你干得了所有的活儿么？你有精力解决所有的BUG么？你有能力完成所有的流程么？你的工作是让团队干活，不是自己干活。\n我们团队内部会议上，我问大家：一个程序员最重要的 feature 是什么？有兄弟说是 出活 。而我认为程序员不仅要能出活，还要高效地出活，出精致的活，不达目的誓不罢休地折腾自己，变着法儿把自己玩坏。\n团队打造出来始终是要做事的，技术团队需要高效地完成业务部门需要的结果。高效的团队需要流畅的合作、清晰的流程、明确的目标、严谨的标准，而要准确无误地达到这些要求，就需要 足够好的人。\n每个人在团队中都有自己的位置，如果碰到了比较弱的人，TA 就会成为团队合作中的那个短板。我丝毫不怀疑我能把 TA 带起来，但问题是 TA 在成长的同时，团队中的其他人也在进步，你需要在 TA 身上花多少精力才能保证 TA 的成长速度呢？弱的人总有弱的理由，强的人自有强的原因。还是把有限的精力，投入到能更快成长的团队成员身上吧！\n所以，团队中最重要的问题，不是你有多牛逼，而是团队中的其他人有多牛逼。负责人需要解决的问题就是让这群牛逼的人在一起能更牛逼。避免弱的人进入团队的最重要的一关就是招聘。那我就来说说招聘。\n2.1 不怕闷，就怕不骚 都说程序员很闷，所以才有上面 @财神 说的赚够了钱去嫁程序员的段子，毕竟话少钱多死的早的老公并不是到处都有。但优秀的程序员往往是闷骚的。当然，明骚的程序员也不是没有，只不过TA们容易把大部分精力都花在骚上，这也不好。\n闷骚的特点在面试的时候就能看出来。一个优秀的闷骚，面对TA感兴趣的话题，根本不用我提醒就能滔滔不绝讲下去，那是拦也拦不住的，二面分分钟超过两小时，而且我都插不上话！我经常用这种方法在面试的时候偷懒，既能少回去开会，也能多了解这位同学的方方方面面。\n前几天刚面了以为只闷不骚的同学，这位同学有一种逆天的技能，就是所有的句子都是短句，所有的回答都是封闭性回答，所有的谈话都不展开。即使是我的开放式问题，他也能转换成封闭式问题然后给一个短句答案。我举两个他回答的经典语录：\n问：你之前做页游的时候写了两年多AS3，对语言算是比较熟悉了。你最深的感受是什么？ 答：加班。 我：…… 问：你为什么热爱程序员这个很有前途的职业？ 答：我能比较积极的完成工作，并能保证工作的质量，所以我热爱程序员工作。 我：…… 问：AS3、C++和Go语言你都用过，对比一下它们的特点？ 答：主要是定义不同，Go语言的定义需要大写。它们的 API 不太一样，其他都差不多。 我：…… 当然，上面说的是一些极端情况，大部分程序员还是挺本分的，中规中矩，不温不火。作为技术团队的负责人，在招聘中需要睁大眼睛分辨这类大多数情况，保证不漏过一个可以培养的骚人，也不能招入一个没法培养的弱人。\n在我看来，闷骚的程序员可能有下面几个特性：\n自信 挑你的刺儿 有自己的想法 不愿和你妥协 知道如何拒绝 上面的特点可以说是极骚，下面的是普骚：\n干活快 喜欢啥都接着 有记录，有安排 学习快 2.2 面试的两级 要筛选出足够好的人，面试一定要慎重。我曾经就因为太想找到“能干活的人”而吃了许多苦头。我碰到过干了一个月从不加班等到发薪水那天晚上加班到21点薪水到账就马上删除代码来要挟我们的；也碰到过受不了一点批评摔门而去的；还碰到过使用物理技能攻击公司 boss 然后威胁 format d: /P 的……一些事情到现在想起来还会呼吸急促，大脑缺氧。\n上面这些人中，有从360公司回来的北漂，有十几年的程序员，甚至有公司的创始员工……\n所以，在面试层面多花点功夫是值得的。面试至少要有两面。下面是我的老大给的关于面试的要求：\n一面：\n着重考察技术能力，不必涉及文化、性格、价值观和软技巧。但一面面试官可反馈这方面对候选人的主观印象。\n二面：\n事业心。是否积极主动追求事业（技术）上的成功，有什么样的事例（或者成绩）可以证明？是否有家庭、情感方面的困扰阻碍候选人全身心地投入工作？能否接受合理的超时间工作和学习（无论是在公司，或者在家利用业余时间学习或者工作）？ 与人沟通交流和合作情况。可以问问他如何评价前领导、同事，有什么业余爱好，参加什么集体活动，在集体活动中做什么。 职业生涯规划。对Junior员工可以接受他没有清晰的规划。考察他留在武汉的意愿，从事游戏行业的意愿，工作稳定性。 住址。除特别优秀外，不建议招上下班太远的人。 我们招的人，一定要有优秀、过人之处，有较强的学习能力并能在我们的环境下持续成长，而不仅仅是能应付眼前的工作。有三年工作经历的人，就一定要有三年或者三年以上的工作经验和能力。请大家谨记。在面试时，一定要带着这个问题：这个候选人如何融入到我们现有的环境，如何成长，如何适应未来的变化？\n其实就算是严格执行上面的建议，也很难杜绝所有的问题。曾经有一个十年经验的程序员，经过了两轮面试，表现都很好。但工作起来和面试感受完全不同。没过多久他主动提出了离职，给出的原因让人匪夷所思，就是觉得工作上很 “别扭” 。HR 无法接受这样的离职原因，我们也尝试了各种方式努力挽留（包括解决他目前的工作问题，承诺即将到来的适合他的工作任务等等），依然无法得知他离职的真正原因。不过，上了二十年小学的柯南同学说过，如果排除所有不可能的情况，那么无论如何不可相信，真相就是真相。\n十年的工作经验，不一定就是真是十年。 有些人是做了十年不一样的事，有些人则只是把第一年的事情做了十年。 对于上面的这个情况，我觉得在我当时的能力下无可避免，可亡羊补牢，犹未晚矣。现在我至少找到了两个解决办法：\n1. 充分求证\n对简历进行充分求证。了解每个项目的始终，并通过其能提供的原同事信息进行求证。 《注意！有人在盯着你》 一书中提到了一些关于背景调查的问题，也可以问一下。多花点时间总比最后承担后果要好很多。\n2. 相信直觉\n比较糟糕的员工，在我第一次和他们见面的时候，我的直觉总会告诉我哪里有些不对。但之后，我的理智又告诉我现在是急需用人的时刻，这人经验丰富，能把项目向前快速带动……此时直觉就被理智给压制了。最后出事的时候，往往发现给项目带来的影响比之前还要大许多。\n人的直觉往往非常准，但也经常被理智所否定。大脑总会认为理智比较靠谱而直觉比较玄乎，而事实似乎正好相反。直觉是在你的感官观察到这个人的第一印象的时候做出的，其实在用大脑思考这个人的时候，你的感官已经对这个人的一些细节做出评价了，一旦有不符合常理的地方，你的直觉就会报警，而此时你的大脑还没有开始视觉分析呢！\n下一次碰到不知道该不该录用一个人的时候，尝试一下相信自己的直觉吧！当然是在充分求证之后！\n3. 兴趣分类 除非是超人或者有别的重要目的，否则大多数人都只愿意做自己感兴趣的事。程序员则更是如此。因为闷骚的特性，TA们的兴趣点大多比较集中，或者说在一段时间内比较集中。这样，要TA们能高效的出活，就要找对路子。这个路子就是抓住TA们的兴趣点。\n我会保持每季度都能和团队所有成员一对一沟通一次。平时大家的能力，特点都已经在日常工作中了解得八九不离十，但内心的想法大家并不会全告诉你。而且一对一沟通中，我可以全面了解团队成员内心中的想法，一些平时忽略掉的细节也会在这种沟通中被放大，同时一些平时工作中不方便说的事情，也可以在这次沟通中进行全面交流。\n这种沟通是非常重要的。我在沟通中了解到了大量平时无法观察到的问题，甚至是可能得到对一个人完全相反的印象。我更愿意把自己的职责归结为如何帮助团队成员成长，所以在沟通中我说的最多的就是：“我能帮助你做什么？”\n下面这次沟通汇总是我担任 Leader 之后三个月不到时进行的。这次谈话过程中，我制定了一些特定的技能点问题，谈话结束后，再整理大家的回答，对每个技能点进行打分。下面是这次沟通的结果：\n根据这个结果做出下面三张图表：\n兴趣点分布\n这个图表代表了每个人关注的兴趣点，数量越多则代表这个人越有发展的可能。需要注意的是“无趣”和“Lua”这两个点。\n“无趣”代表其认为目前的工作无法给其带来快感。这种感觉比较普遍，集中在3年以上工作年限，以及对技术有理解和追求的程序员身上。\n“Lua”则代表多数人愿意往 Lua 脚本化方面转换。这也符合目前 C++ 为主的技术特点。\n重点指示\n大部分的人的主动性是不错的，少部分人有很强的自我推动力。C++底层是老本行自然分数高，Lua 也在情理之中。但有趣的是服务器技术和 3D 技术的关注度。另外少数人有难得的产品思维和管理思维，值得培养。\n潜力\n该表能在一定程度上指出个人潜力 ，但由于数值的广泛性缺失，并没有决定意义。\n做产品久了，程序员的眼光会局限在某一块，纠结于产品逻辑，少有技术创新。为了维持团队稳定，保持推动力，我根据团队成员的兴趣点制定了兴趣组计划，把整个团队按兴趣方向分成了3D、HTML、Lua几个兴趣组，并着手申请现有项目的HTML5移植和脚本化。\n这次沟通的结果就是促成了兴趣组的建立。兴趣组对团队成员是一个重要的推动，同时也是对客户端开发培训工作的方向性指导。\n到了年底，我让大家写2016计划之后，又针对TA们的个人计划进行了一次统计：\n兴趣点的人员分布\n人员的兴趣点分布\n可以看出，由于是计划，大家放得比较开，各种不同的想法和各种零碎的细节都出现在计划中，下一步，就是把这些碎碎念都能融入到兴趣组计划中。兴趣组已经不再局限于技术，写作、读书、健身都可以是兴趣的一部分，这类有生活气息的兴趣组与基于技术的强连接兴趣组相辅相成，更能加强团队成员之间的联系交流，保持团队活力。\n4. 让团队成员信任你 都说文人相轻，其实程序员相轻地更厉害。作为一个“空降”的 Leader，用什么方法让你的团队成员信任你，从而提高工作效率？\n很多空降的家伙们喜欢直接推翻前任的设计重来一次。我不否认这是一种不错的方式。同样的，这还是一种最偷懒的方式，一种最轻松的方式，一种最高效的方式。\n可惜我不能这么做。我们团队负责的4个项目，每个项目都在线上跑。每个项目都抽不出人手来调整现有架构。何况，4个项目的架构还都不太一样。对这样的架构动大手术，稍稍不注意就会造成大的问题，影响收入。而我初来乍到，还没有得到团队成员的认可就进行大的改动，大家心里也不会认同。如果得不到全力的配合，就会事倍功半，把好事办砸。\n我采用的方法是，找到一个痛点集中精力解决掉。 这个痛点应该让大家都感到棘手（凸显你的能力），使用非常频繁（大家会记住你），很难修改，比较复杂，但修改它（或者重写它）不能对现有的项目有太大的影响（安全），不用动用太多人力就能搞定（最好是你自己搞定）。\n这个痛点就是项目的打包工具。旧的打包工具是一个设计得相当复杂的系统（其实就是没有设计），该系统由多人（5+）完成，多人（10+）对其进行了具体的实现和修改，最后的情况就是没有人知道这个系统的完整设计是怎样的。系统大部分由 bash 写成，在 windows 上依赖 cygwin，体量如下：\n我阅读了所有代码，绘制出了系统的架构图：\n这套架构违反了许多设计原则，但正因为其复杂，最后没有人愿意改它。\n打包的同学每接入一个 SDK，就需要写几百行 bash 脚本，这些脚本从别的 setup.sh 文件复制过来，再进行修改。这导致如果有了更好的实现，也只可能在最新的脚本中才可以用上，没人敢跑回去改老的脚本。\n这样一个系统整合符合我的选择。我花了一些时间询问所有和打包系统相关的人，弄清楚了整套系统的结构，用 Python 写了一套全新的打包工具，这套工具不必再依赖 cygwin 。新的打包系统设计了一套继承和覆盖流程，让负责发布的同学可以不用写脚本，直接通过配置文件就能支持新的 SDK。新的配置文件基于 ini 格式，测试同学可以更易懂地进行配置。由于有完整的设计文档和使用文档，再加上使用了配置文件模版，无论是修改还是扩展都很方便。\n项目中很快用上了这套工具，事实证明打包效率的提高是非常明显的。我在每个项目中找了一位同学负责该工具的后续维护，我就全身而退了。\n后面的事情证明，这套工具不但带动了大家主动学习 Python 的热情，还增强了大家对文档的认识，同时我的设计思路和执行方法也得到了大家的认同，后面对工具、框架、方法的推动就比较容易了。\n5. 培训计划 在和团队成员沟通的过程中，我发现许多人并不是不够努力，而是不知道往哪个方向努力。或者说，不知道应该如何努力，才能更快地成长。有些人认为已经掌握的技能足够解决目前的问题，就开始懈怠，或者开始焦虑。大家不知道自己处于技能树的那个级别，也不知道下一步应该爬往哪个方向。\n于是，培训，尤其是系统的培训计划就相当重要。我根据手机游戏客户端技术的特点编写了一个技能树：\n客户端技能树\n所有的培训内容、文档、PPT 和源码都是通过 git 管理的。主要文档使用 Sphinx 写成，所有团队成员一起维护，所有的培训都有记录。绝大部分培训都有练习。每个人都可以是合作者、执行者和学习者。\n采用这种方式，我可以直接在技能树培训的过程中，顺便把入职培训的内容也做了。后面进入团队的新人，可以直接 无缝接入 这套培训系统。\n因为培训的内容都在内网，这里给出两张截图：\n6. 拉动其他团队 在游戏公司里，前端团队是非常特殊的存在。前端是产品从设计到成果的最终一环，玩家能直接体验到前端团队的成果。一个优秀的产品，必须是由一个优秀的前端团队打造出来的。\n我一直认为一个优秀的手机游戏前端程序员必须做到以下几点：\n能指导美术团队按照客户端的优化需求切图； 能给UI/UX团队提出靠谱的修改建议； 能根据技术特点与商务团队沟通的SDK接入策略； 能根据各OS平台特点和运营团队讨论推广策略； 能根据客服团队需求设计更方便的反馈模块； 能制订通信编码协议（必须是客户端制订）； 能与服务端团队讨论性能和优化策略； …… 做到这些并不容易。前端程序员不但要熟悉美术工具，对各种媒体格式了如指掌，还必须了解UI和UX的基本知识，并将其运用到最终产品中；程序员要了解商务和运营的各种术语，要学会查看各种运营报表，从中分析出产品在前端方面的问题；程序员还要熟悉各种网络通信协议，要了解服务端开发的特点，要熟悉数据库、服务器性能，熟悉移动网络的特点，才能做到和服务端团队协同配合提升游戏的网络体验。\n可是，大多数客户端程序员都不擅长做上面的工作。然后我可以反其道而行之，让其他团队了解一些客户端的工作。\n我发现其他团队在制订操作方案的时候，并没有过多地考虑手机游戏的特点。这并非是由于他们不想做到更好，而是不知道我们前端团队能做到哪一步。和前端团队一样，其他团队也是对自己团队的工作范围熟悉，对其他团队的工作不熟悉。如果我们可以让大家互相多了解一些，那么在涉及到多个团队协作的部分，效率就会更高。\n例如界面切图工作。美术同学切图可能不会考虑共享资源的重用，以及scale9等技巧，由于不了解界面在游戏中如何分割，图像文件的分类也不一定合理。而由于客户端程序员对PS等工具并不熟悉，也无法提出合理的修改意见。最终导致的结果就是美术素材浪费了内存和增大了最终包体积，或者客户端团队需要对美术资源进行二次修改才能使用。\n要提高这部分工作效率，我采用的方式是让了解美术的前端程序员和美术团队一起工作，了解具体的切图流程，在切图过程中确定一些常用的规则，并让美术团队了解这样做的目的。一来二去，美术团队对客户端的需求会越来越了解，资源返工的可能性就越来越小了。\n再比如，商务团队在和渠道谈SDK接入的时候，会涉及到一些技术问题，而渠道方负责合作的人员往往并不是技术人员，此时和我们客户端团队对接就会出现问题。如果商务团队了解一些接入的基本规则和技术术语，在和渠道方合作的时候，就能够更加得心应手地处理沟通问题，为客户端团队节省了大量的沟通时间。\n7. 团队文化 文化有非常浓厚的个人烙印。一个公司的文化，一定有浓厚的创始人风格。而一个团队的文化，则反映着Leader的特点。\n在创业时，我从无到有完整地打造了公司行政、人事流程和公司文化。而现在，我更希望按照公司特点打造一个高效的客户端团队。\n文化是对共同价值观的认同。没有共同的价值观，也就没有共同的文化。我希望的团队文化必须是轻松的，有趣的，发展的，共赢的。\n团队文化的建设即是实实在在的，又是潜移默化的。我们可以从日常的团建工作中找到突破口。所有这类工作，必须让所有人一起参与。比如我们团队上一次团建的内容这就是这么定下来的：\n这是我发的第一封开脑洞邮件：\nHi all: 我们客户端团队要搞一次团建活动，请大家开一下脑洞。 唱歌就不必了，一群爷们木有妹子。 吃饭啥的有点 low，做备选方案吧 内容要有趣，大家都想参与 比如攀岩啥的，大家没搞过的在预算内的都可以搞搞 客户端共xx人，公司预算每人xxx，我再出xxx，大家按这个来计算。若再超过，大家 AA 多出的部分。 今天下班前，每人必须回复。 没想法的请回复： “没想法” ，然后 AA 的时候出2倍。\n期间，给一些合适的引导：\nHi game client: LHJ制作人JY同学组织了一群乌合之众试图与我们在网吧一争高下，请大家准备好，给他们点 color 瞧瞧，让他们知道游戏是用键盘敲出来 DI ！！！！ @JX，是不是可以设计点彩头哇……\n经过了十几轮邮件轰炸之后，结果变成了这样：\nHi all: 投票结果已经出来了，见附件。 可以看出，69%的同学选择网吧游戏，46%的同学选择吃饭。那么我们这次就搞 网吧游戏+吃饭 的团建吧！运动也有不少同学选择，我们在下次团建考虑。 时间上，由于必须在 12月31日前完成，我选了3个时间点。54%的同学选择了圣诞节当天，那么我们就定这个时间。\n团建本来是轻松的事情。但开放式讨论必须有人收尾。就像我初中时团支书说的那样：既要民主，也要集中。按大家的兴趣点把时间地点人物确定，整件事儿基本上就可以愉快地开始了！这次团建大家都是蛮Happy的： 客户端团队圣诞团建回顾--我们的快乐与大家分享！\n团建仅仅是团队文化建设的一部分。更多的文化建设是潜移默化进行的。例如在上面提到的培训过程中，合作者与执行者的交流，每次授课之前的小范围试讲；再例如根据每个程序员的特点安排不同的工作，安排不同项目之间的经验交流；还有让团队中每一个成员都了解自己的位置和发展方向，都知道碰到某些问题的时候应该找哪位成员询问等等。这些看似平常，但都是文化建设的重点。\n团队文化建设的前提，就是Leader要充分了解团队成员，知道每个人的喜好和特点，还要保持鲜明的个人风格，并把自己的风格与团队成员特点，公司风格融合起来。这样建立起来的团队文化，才是经得起考验的，才是能帮助团队成长的，才是能和公司一同进步的。\n8. 积累 想要做一个称职的 Leader，积累是至关重要的。\n在技术经验上，你的积累能够帮助你的团队快速解决棘手的问题。当然，这是技术 Leader 的基本功。我说一点技术之外的积累。\n8.1 谈话 一对一谈话是了解团队成员想法的重要手段。上面谈到的对团队成员兴趣的了解，大多数都是通过一对一谈话来完成的。谈话的内容要实时做好记录，并做好对每次谈话的结果分析。\n下面是我做的一个典型谈话分析。包括谈话内容，对于普遍情况的总结，以及对于特殊情况的描述，最后包括解决这些问题的可选方案。\n只有记录了每一次谈话的内容，才能在下次谈话的时候根据原来的记录做出合理的比较，看看哪些人进步了，哪些人的方向变化了，然后继续寻找更合适的方法。\n8.2 人事 人才和面试，是团队成长和建设的重中之重。上面我专门用了两个独立的章节来讲。\n我对所有经手的面试和离职都会做好记录。在有员工离职时，我可以通过对比分析其他员工的离职原因，看看到底是因为干得不爽还是钱没给够。每个优秀员工的离职，对团队都会有一定的影响，对我的工作也是一个考验。如果能够在离职之前发现问题所在，有针对性的进行工作的调整和团队建设，无论对团队来讲还是对离职员工来讲，都是件好事。\n所有的面试资料中都包含简历资料，在一个新员工入职后，我可以通过 TA 的表现，慢慢去印证当时的面试有哪些偏差，是否出现了判断错误，把一个优秀的求职者放弃了？还是看走了眼，招入了一个平庸的人？\n8.3 GTD 大多数公司都会使用邮件来管理和发布工作。但邮件并不是进行工作管理的好工具。为了方便分析和整理，我会把每天的工作记录下来。这些工作包括一些重要的邮件，讨论和会议，还有一些就是日常的杂事。\n有了每日工作记录，就很容易写出周报和月报并作出总结。一些比较独立的事件，并不放在日报中，而是将它们进行单独记录，再以链接的形式将其加入到每日工作记录中。每月结束的时候，回顾一下这些记录，对下个月的工作安排有极大的指导意义。\n9. 结语 本来准备随便写写的，一不小心就写成了工作汇报。谈不上经验，就是胡扯而已。就像我在 游戏逻辑程序员的成长出路？ - 曾嵘的回答 里面说的那样：\n如果你所处的是有 HR 有网管有安全部门有运维部门有扫地大妈的公司。就当我放屁好了。\n我现在就在有 HR 有网管有安全部门有运维部门有扫地大妈的公司。\n所以你们就当我放屁好了。\n（放完了）\n","date":"2016-01-25","description":"","lastmod":"2016-01-25T02:17:15Z","slug":"how-to-be-a-leader","tags":["management"],"title":"怎么做好互联网公司的技术负责人？","url":"https://blog.zengrong.net/post/how-to-be-a-leader/"},{"categories":["impressions"],"content":" 开始： 2016-01-01 读完： 2016-01-09 书籍介绍： 编号1601 本以为看完这本 The Gift of Fear 之后心情会很沉重，没想到其实还挺轻松的。全书分了14章，讲了这样几个主要内容：\n识别危险； 摆脱身边的小人； 家庭暴力和避免孩子成长的暴力倾向； 应对自己的恐惧。 全书主要是针对女性（贝克尔的大部分服务对象是女性和企业），这就让该书成为了解男性攻击心理的必要读物。\n下面文字中完全引用原文的部分，我会使用引述的格式给出。其它没有使用引述的内容，可能是因为我更改了原文的内容和顺序以方便理解，或者加入了自己的评论和感想。\n在本书中， 办公室里的小人 那部分，对我的帮助最大。我在这十几年的工作过程中遇到的那些人，简直可以和他们对号入座。\n[TOC]\n识别危险 那些逃避问题的人会陷入“危险无法预防”的焦虑中，而那些愿意正视危险的人，反而可以把那种焦虑转换成预测危险的能力。\n事实上，在全部的谋杀案中，凶犯是陌生人的比例，只占20%，而其余的80%则一律是被害人的旧识。\n关注一下法制新闻，也可以验证这个书中提出的观点。许多猥亵案和强奸案的作案者都是熟人。而谋杀案，也是熟人作案居多。毕竟熟人之间可能产生的利益纠葛更多。\n被人盯上的七种细节 注意： 下文中出现的 “骗子” 一词，具有更广泛的含义，它代表 “所有想伤害你的人” ，无论是抢夺你的财物，伤害你的感情，还是威胁你的安全。\n1. 套近乎：骗子要骗你，先会讨好你 相同的境遇会拉近彼此的举例，身体因快乐而结合，心灵因痛苦而靠近。\n骗子在骗你之前会先讨好你，在这种情况下，我们要分辨出和陌生人之间的亲近感是 自然而然 发生的，还是被对方 刻意营造 出来的。抵御骗子套近乎的最直接有效的方法是告诉他：“我没有请你帮忙，也不想接受你的帮忙”。不要担心无礼，为了自己的安全不得已做出的无礼举动是无可厚非的。一个真正的绅士面对这种无礼，只会觉得你无趣，并没有大碍。\n2. 灌迷魂汤：迷人的微笑里藏着一把刀 我试图欺骗一个人时，如果对方想看穿我，他就必须比我聪明一点点才行。换句话说 欺骗的过程有点像掰手腕 ，道高一尺，魔高一丈。\n而骗子会尽量掩饰，让你看不出来你们在掰手腕。人类的直觉在发现危险的时候是很准确的，它会在内心中不断提醒我们：“有地方不对劲儿”，骗子会不时露出笑容让你感到他们的友好，以掩饰这种 角力 。\n但是， 友好并不等于善良。友好仅仅是一种与人互动的策略，而不是一种人格特质。 我们由于工作原因遇到的人，大多数都会由于利益关系而对你友好，可是如果根据这些友好就判断 TA 的本性善良，就大错特错了。现代社会中的一部分人已经锻炼成为能隐藏自己的真实想法，快速征服别人的心理专家。在这个情感缺失的年代，要获取别人的信任，或许只需要帮一把忙，说几句好听话而已。\n3. 堆砌细节：用一些细节引开你的注意力 一个实话实说的人，根本不会担心别人的怀疑，不会用一堆细节来佐证自己的话。而一个讲假话的人，不管他的话听起来有多么合情合理，他都会感到心虚，所以才会说个不停。\n说实话的人，本来就是无懈可击的。他不必强调自己的话，也不必强求你来相信它的话。一个人除非另有目的，否则不会不断强调真实性，也没必要告知细节。\n抵御这种伎俩的方法就是要不断提醒自己所处的境况，包括你所处的位置、当时的时间，以及与对方是一种什么样的人际关系。\n细节会麻痹你的大脑，让你的直觉处于停工状态，这样就不会让你有 掰手腕 的感觉了。而自我主动的提醒，则可以让自己的直觉重新开始活动，考虑这些细节之外的，被你的大脑忽略的东西。其实这种技巧不仅仅可以用于对抗骗子的时候，也应该用在别人试图给你洗脑、说服你，商业谈判，还价，以及任何在谈话中占尽上风的时候。要时刻提醒自己的位置，以及这次谈话的实际目的，不要让对方的细节改变了你的初衷。\n注意，这不是刚愎自用，而是为了保护自己不受伤害。\n4. 激将法：你说我是那种人，我偏偏证明自己不是那种人 所谓 “激将法”，就是用轻微的批评，让你就范。对于激将法，最好的防卫手段就是保持沉默。不管他是好人还是坏人，你根本不必在意一个陌生人对你的看法。\n激怒你总是有目的的，愤怒的心态会让人变得不理智。而且，你也没有必要对一个陌生人证明什么。这个技巧依然可以用于日常工作和生活，你无需对别人有意的激怒产生什么反应：一只狗对你狂吠，难道你要去咬它么？\n5. 放高利贷：给你一点儿帮助，利用你的愧疚来控制你 和“套近乎”那点的解决方案一致。陌生人帮你（女性）主动拿东西一定有其动机：\n骗子，想伤害你； 普通人，想讨好你（因为你漂亮）； 真正的绅士，仅仅是展示其自我风度（自我价值的证明是其动机）。 6. 主动做出保证：急于承诺的人都有险恶的用心 如果有人对你说 “我保证”（不伤害你，马上就走）时，你最佳的防御策略是告诉他，至少是提醒自己：“你说对了，我还真不敢相信你。”。\n7. 无视你的拒绝：这是常用手段，也是最重要的警讯 最糟糕的反应莫过于作出越来越大的让步，用商量的口气会让你丢掉立场。不要使用条件句，而要把拒绝本身作为一个完整的句子。\n简单的说，就是要“不”得很彻底。在工作中，这种特点也能给你带来坚强、果断的性格标签。\n坏人盯上一个人，会先进行面试。他会寻找易于下手的目标，而把那些棘手的目标放弃掉。因此，要注重上面这些警讯，也许你会成为坏人的目标，但是因为你的警觉，你不会成为最终的猎物。坏人也怕麻烦，如果有更好的选择，当然不会选择你。 要留神的，是那些主动提供帮忙而又无视你的拒绝的人 ，真正的绅士绝不会不理会你的拒绝。\n相信直觉 一个人在某一行做得越久，反而会失去外行人所具备的创造力和想象力。他们脑中已经形成了固定的模式，会忽略掉一些新的细节所代表的含义。\n第一次看到一个人的时候，一定要相信自己的直觉。这些直觉是在你的感官观察到这个人的第一印象的时候做出的，其实在用大脑思考这个人的时候，你的感官已经对这个人的一些细节做出评价了，一旦有不符合常理的地方，你的直觉就会报警，而此时你的大脑还没有开始视觉分析呢！\n直觉是非常有效的认知工具，却经常遭受自我否定的压制。\n大脑的理性部分告诉我们，不应该随便怀疑别人。在夜晚的路上被人跟踪的的时候，你的大脑可能会告诉自己：“他仅仅是和我恰巧同路而已”。此时最好的方法是： 完全转过身去，把身后的全景眼里，并直视那个让你怀疑的家伙。 如果你的怀疑是错误的，那人顶多会小声嘀咕一声傻X然后走开；如果你的怀疑是正确的，就能迫使坏人离开，或者提前采取行动，但那一定不是他全部准备好了的时候。不要担心你的做法激怒了坏人，他如果选定了目标，总会采取行动的，所一你不能让他完全准备好。\n有的专家建议女性在遭遇强暴时，千万不要反抗，而另一些专家则建议一定要反抗。这两种策略没有一个适用于所有的强暴情形。唯一万能的策略只有一个：聆听你自己的直觉。\n识别坏人 每个人都是天生的心理学家，早已不知不觉地把人作为研究对象研究了许多年。然而，大脑的高级部分喜欢将事物分类，把人简单区分为好人或者坏人。事实上，一个强奸犯可能看起来像翩翩君子，一个外貌丑陋的人也可能有一颗善良的心灵。弗洛伊德说：“人类有两种本能，一种是生存和交流的本能，一种是摧毁和杀戮的本能。”每个圣人都有自己的过去，每个罪人都有自己的未来。\n看看人类行为的八条法则，用正常的人性去观察人，反常的如果不是圣贤，就一定是奸人：\n我们希望和别人有往来； 失去会让我们难过，所以我们避免失去； 被拒绝和被厌弃让我们不好受； 我们喜欢被人认同和关注； 为了寻求快乐，我们付出很多；为了化解伤痛，我们付出更多； 我们不喜欢被嘲笑和戏弄； 我们在乎别人如何看我们； 我们希望在某种程度上控制自己的生活。 奸人的几条特征：\n花言巧语，不真诚； 以自我为中心，夸夸其谈； 缺乏内疚和羞耻感； 喜欢欺骗，有控制欲； 易冲动； 寻求刺激和兴奋点； 责任感缺失； 感情淡漠。 干坏事之前的四个心理准备 预测危险的时候，我们要格外注重对方行为中透漏的讯息：\n表达拒绝的语言； 表达权利与资格的语言； 夸大事实的语言； 寻求注意的语言； 复仇的语言； 表达情意的语言； 寻求自我认同的语言。 人在做坏事之前，会考虑下面几点：\n1. 正当性 冒险之前，人会给自己找一个正当的理由以说服自己，无论这个理由有多么荒谬，只要能说服自己就好。\n2. 可选择性 别无选择的人，往往会选择最危险的行为。抱着煤气罐和拆迁队伍对抗的往往是无知小人，而为了自己的摊点和城管队员拼命的小贩可能是别无选择。\n3. 后果 如果一个人按照自己的判断评估这件事做了之后对自己有利，或者至少无害，那么他采取行动的可能性就会比较高。\n4. 能力 重要的不是人有没有能力，而是他相信自己有没有能力。\n要了解上面四点，就必须站在坏人的角度去考虑他们的想法。\n摆脱身边的小人 恐吓、威胁与要挟 恐吓和承诺有其相似之处。首先，它们都是一种宣誓决心的工具；其次，他们都说起来容易，做起来难。恐吓是让对方的心悬在那里，感受到事情的不确定性。\n恐吓者不给你选择，不给你变通，不给你出路，也不包含“除非”、“如果”、“否则”这类字眼。有这种字眼的言辞都是威胁，而非恐吓。威胁是有前提的、有动机的。而恐吓则不然，恐吓不包含任何前提，提出恐吓的人通常认为事情已经没有回转的余地了。\n永远不要让一个人走投无路。恐吓传递出承诺所不具备的特质，那就是无力感。需要发出恐吓，说明这个人已经无计可施了。\n真正危险的人都有一个共性：冷静。“咬人的狗不叫，叫的狗不咬人。”\n人的第一反应往往是最正确的，而第一反映最大的敌人则是知性判断。知性判断会摒弃陌生的信息，仅仅将熟悉的部分进行归类整理，让我们对一些新的东西视而不见。\n我们生活中还有一种很常见的危险，就是要挟。但是如果你不把要挟当回事，要挟就起不了作用。\n有一点需要牢记，对要挟者要保持礼貌。一个新手在要挟别人的时候，正是他自尊最脆弱的时候。你不要同情要挟者，但是也别去刺激他。\n动机处于恶意的要挟者比动机出于贪婪的要挟者要狠；一开始就把话挑明的要挟者比那些吞吞吐吐的要挟者更可能把要挟付诸行动；明目张胆的恐吓比匿名的恐吓更危险。\n情境才是根本。任何事件都不是孤立的，必须置于一个情境才能进行准确地判断。\n摆脱纠缠 像疯狂的影迷，痴心的男友，不愿被解聘的员工这种纠缠者有很多。在纠缠者耳中，“可能” 会被理解为 “一定” ，“喜欢” 会被理解为 “爱” 。被人纠缠并不可怕，可怕的是你想改变他的行为，进而导致纠缠者的行为升级。对于一个被纠缠者来说，什么都不做才是人生耐性的一大考验。\n你对纠缠的人越强势，危险就理你越近。\n要解决一个苍蝇男的电话纠缠，明智的做法是不是换新号码，而是保留原来的电话，再加装一部新电话。这样纠缠者就不会发现你装了新电话，久而久之，打旧电话的人就一定是纠缠者。还可以请一位女性朋友帮你进行电话录音，但不建议请男士。这可能会让苍蝇男产生怀疑。\n要分辨苍蝇男，可以在和新对象约会的时候，聊聊他和前女友的关系，以及他们是什么原因分手的。听听他对这个问题的反应。他有没有觉得自己应该负一部分责任呢？最关键的问题是：分手是谁先提出来的。\n《了不起的盖茨比》作者菲茨杰拉德的名言：\n不仅是因为你的坚持，更是因为你有重新开始的勇气，才显示出生命的无限力量。\n办公室里的小人 小人的心理：不惜牺牲自己，也要毁灭他人\n什么是“剧作家”员工？ 他们缺乏弹性，不喜欢接收建议，把别人的建议视为对自己的行为的批评；常以小人之心来揣度别人的动机和性格；他们不会听你在说什么，因为他们早在你心中为你写好了台词。\n危险倾向判断 有危险倾向的同事都有下面的一些特征，若具有三项以上则要多加小心：\n缺乏弹性 喜欢武器（这点在中国估计很难） 忧郁，喜欢生气 不乐观。“那有什么用呢？这什么都改变不了。我算是没指望了。” 认同危险事件 和 TA 相处会莫名其妙感到恐惧 使用 TIME（见后） 综合法 有疑心病，觉得别人都在算计他 对别人的批评很在意，怀疑批评者的动机，完全不认同批评里的中肯之处 拒绝承担责任，把过错归咎于他人 不合理的期待 有攻击或者伤害他人的前科 关注别人的言行，做记录，甚至跟踪 被解聘后，继续与原公司雇员保持联系 TIME 综合症 处理问题员工的最佳方式是多谈话，尽量在早期发现他的问题。解聘是要遵循两个原则：一是理由充分；二是态度坚决。否则会引发 TIME 综合症：\nThreats （恐吓） Intimnidations （威胁） Manipulations （操控） Escalation （行动升级） 应聘者问题 对应聘者的履历进行背景调查，是决定是否雇用一个人的关键。在面试的时候也可以问这些问题：\n请谈一下你遇到过的最好的老板是一个什么样的人？最差的呢？ 你觉得人生中最大的失败是什么？导致它产生的原因是什么？ 你想对前任老板提出什么样的建议，他如何改进才能使经营更加成功？ 你对前任老板提过你刚才说的建议吗？ 前任老板要怎样做才能留住你呢？ 你工作中遇到问题，一般是怎么解决的？ 举一个你在别人的帮助下解决困难的例子？ 你最好的朋友是谁？能否几句话概括一下你们的交情？ 在面试中不必问优点，而是应该多找缺点。应聘者在面试过程中表现出的优点，可能恰恰映射出他的缺点。例如说自己有条理的人，很可能刻板守旧，缺乏弹性。好的应聘者会自然而然地把自己的优点显露出来，不需要刻意去找。\n正确的督导应该做到赏罚分明，不能把问题员工当成特殊人对待。我以前就犯过这种错误。不能让问题员工认为他们有特权，否则最后会对其他员工造成极坏的影响。\n越早解聘一个问题员工，留下的后遗症就越少。这部分，我也有完全相同的体验，现在想起来，如果能早读一下这本书，也不会有那些失败。\n解聘问题员工的策略 尊重对方 做一个彻底的了解 不要用商量的口吻 谈话重点放在未来\n“如果你离职后有人打电话找你，需要我们怎么答复？”“如果收到你的信件，需要我们转寄么？”\n用这样的做法打消 TA 心中的疑虑，因为 TA 最怕前任老板翻老底。 开门见山，直截了当\n要强调，你相信他又一个成功的未来，会找到合适的平台，会有很好的成绩。不要把气氛搞得太沉重。应该说：“工作中的坎坷每个人都会遇到，我也有类似经历，但我挺过来了，我相信你也可以。” 要宽泛，不要具体\n不要把解聘谈话当成教学课。聪明的做法是说一些宽泛的话，比如离职双方都有好处，他的能力一定会找到发挥的平台。千万不要说谁将会接替他的职位，若被问道，只说还没有做出决定即可。 要出其不意\n不要让员工事先知道被解聘这件事 选择合适的时间\n在一个工作日结束，别的员工下班时，可以考虑周五。这样 TA 就无法找那个 TA 认为导致 TA 解聘的人寻衅。也不会因为第二天别人都去上班了而感到不舒服。 选择合适的地点\n考虑一个封闭的，不透明的空间。不要选择你的办公室（避免寻仇）或者他的办公室（不方便脱身）。 选择合适的列席者\n不要选择 TA 的上司或者平级同事（感到侮辱），不要找保安或者硬汉（感到不安）；尽量选择 TA 没有怎么接触的，遇事冷静自若的高级员工。 面对解聘后的恐吓：\n“我知道你现在心情很差，所以才会说出这样的话。我也知道你不是这样的。你是个理智的人，一定会有光明的前途。我相信你刚才说的话不是真的。“ 恐吓已经发出时：“情绪不好时，谁都会说一些傻话。我也有这样的经历。让我们忘记刚才的话吧。我敢肯定，你明天一觉醒来，就会有不同的想法。”（让 TA 认为事情还有回转余地） 要考虑，有没有同事或者主管成为 TA 发泄的对象，收回通行证，并通知保安； 愚人懂得原则，智者懂得例外。 暴力 家庭暴力 即使有家庭暴力，50%的女性还是不会离开。因为：\n她们根本无处可去； 她们幻想这是最后一次； 暴力摧毁了她们的自我保护本能，她们已经不能算具有完整价值观的人。 对家暴，女人解决的最好方案是：离开他，去一个他找不到的地方，永不见面。\n孩子的暴力倾向 孩子如果没有得到关注，就会用危险的行为让你不得不关注他们。不具备这七种能力，孩子就容易成为一个危险的人：\n激励自己的能力 不畏挫折的能力 推迟满足感的能力 调节情绪的能力 怀有希望的能力 同情的能力 克制冲动的能力 有两个兄弟，老大问老二：“你长大后怎么成了酒鬼？” 老二回答：“因为老爸是个酒鬼。” 老二反问老大：“为什么你没有变成酒鬼？” 老大说：“因为老爸是个酒鬼。”\n坏人心理 一个人陷害你不需要太复杂的动机 内心孤独的人，最容易成为危险的人 坏人就是通过干坏事来肯定自己的人 暗杀名人的动机是为了出名 只有父母慈，才有儿女孝 坏人心中往往有太多的恨 在心理上强过坏人，才能战胜他们 应对自己的恐惧 恐惧分为两种，一种是良性的，一种是恶性的。没有根据的恐惧是消极的东西，它会造成恐慌。而恐慌本身的危险性超过我们可能会遇到的危险本身。登山健将和海上运动员会告诉你，致人死地的不是山叶不是水，而是你心中的恐慌。\n关于恐惧的两条法则 我们要首先知道，自己是害怕，焦虑，还是仅仅有点担心？注意以开放的心态接纳这两条法则：\n1. 你对一件事情感到恐惧，就证明这件事情现在还没发生。\n一个跳楼自杀的人，恐惧是在跳楼之前，或者是在跳下之后落地之前。他恐惧的是着陆。而着陆之后，则没有什么好恐惧的（挂了）。\n一个要离开（某人，某种状态，某个地方）的人，在真正离开之前的恐惧其实是焦虑，TA 恐惧离开的那一刻，焦虑之后未知的生活。即使那之后的生活往往比现在要好得多。\n等真正的恐惧发生时（例如抢劫犯已经破门而入），你就不应该再恐惧，而应该冷静面对，仔细观察，寻求脱身之法。\n恐惧是你的朋友，焦虑是你的死敌。\n2. 你认为的恐惧，很少是真正的恐惧，它仅仅是你所恐惧的事务的中介。\n真正的恐惧只会在危险逼近的时刻出现，它与痛苦相连，攸关生死。\n例如有个人恐惧在多人面前演讲。大脑会对这件事进行加工改造，它会去想：如果失败了，就会被嘲笑，意味着自己不会被接纳和认同，自己是个无能的人，失败的人，没有价值的人。渐渐的，大脑就会认为演讲的失败就等同于生命的消失，他们就像害怕死亡一样害怕演讲。\n实际上，一个害怕公开演讲的人，他害怕的是演讲失败带来的 身份认同危机 ，在这些人眼中，身份认同的丢失是一件比死亡更加无法忍受的事。\n其实，人虽然是社会的动物，但身份并不是唯一重要的东西。 要解决身份认同危机，就要想清楚，谁的认同最重要？如果你最在乎的人并不会因为演讲失败而不认同你，那么就没有必要恐惧。\n更进一步，要培养 自我认同 ，使用自我认同去推动 他人认同 ，最终实现 自我身份认同 ，如此就会无所畏惧。\n忧虑的好处 忧虑往往是人们自愿的选择。人们之所以选择忧虑，是因为有时他们可以从忧虑中得到好处。你在演讲前很忧虑，就有借口取消或者推迟演讲。\n忧虑可以让我们回避问题，忧虑让我们心中充满对自己的认同。忧虑的人除了忧虑，什么都不会做。\n攻击我们的两只箭 我们常常受到两支箭的攻击，一只是外界射向你的，它就是我们经常遭受的困难和痛苦本身；一支是自己射向自己的，它就是第一支箭造成的负面情绪。\n如果我们正常接纳这些痛苦，第一支箭给我们造成的伤害就不会太大，仅仅是外伤而已。但如果我们因第一支箭而产生了怨恨、忧虑、自责、沮丧和绝望等情绪，那么我们就会接着遭受第二支箭的攻击，让我们深陷痛苦而不可自拔。\n关于恐惧的三个建议 当恐惧的讯号出现时，倾听它的声音； 当你没有感到恐惧时，不要去制造恐惧； 如果你正感到忧虑，找出背后的原因。 要降低焦虑的困扰，就要加强你的预测能力。可以常常做一些智力练习，试想自己在经历不好的遭遇时，能得到什么好处。\n凶险只存在于少数几片乌云当中，而我们周围此刻都是蓝天白云。现在我们应该做的事就是在乌云飘来以前，尽情嬉戏。\nzrong 寄语 死亡才是永恒不变的事。其他的事情都会变化，万一变好了呢？ 要快乐地活着，因为我们会死很久。 全文完 ","date":"2016-01-09","description":"","lastmod":"2016-01-09T12:11:44Z","slug":"the-gift-of-fear","tags":["reading","life","readingnote"],"title":"【读书笔记】注意！有人在盯着你","url":"https://blog.zengrong.net/post/the-gift-of-fear/"},{"categories":["technology"],"content":"公司要求整理书单期间，我写了 我的推荐书单（一） 和 我的推荐书单（二） ，现在将同事们推荐的所有书单进行汇总整理。\n这次汇总中包含我上次推荐的两辑书单，同时提供了购买链接。\n机器学习和数据挖掘 1. 《机器学习》 作者（译者）： Mitchell T.M. (曾华军等译) 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 经典之作，内容稍显滞后，公式较多，不过讲述很清楚，由简到难。 2. 《统计学习方法》 作者（译者）： 李航 出版社： 清华大学出版社 购买链接： 购买 推荐理由： 讲述了10个经典的监督学习和非监督学习算法，主要包括分类、标注和回归的方法，内容较为注重公式推理，深入浅出，主要为NLP/IR研究人士提供参考。 3. 《Data Mining: Concepts and Techniques, Third Edition》 作者（译者）： Jiawei Han, Micheling Kamber, Jian Pei etc 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 数据挖掘领域具有里程碑意义的经典著作，完整全面阐述该领域的重要知识和技术创新。中文版是《数据挖掘概念与技术》，不推荐中文版，翻译有较多错误。 4. 《数据挖掘导论 完整版》 作者（译者）： 陈风能，斯坦巴赫，库玛尔(范明，范红建等译) 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 书中图表，示例和习题较为丰富，需要部分概率统计的基础，每章后面有大量参考文献，附录有需要的数学知识。 5. 《机器学习实战》 作者（译者）： Peter Harrington (李锐，李鹏，曲亚东，王斌译) 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 比较适合入门，内容讲解并不深，只是把主流机器学习算法简单用python实现。 6. 《集体智慧编程》 作者（译者）： Toby Segaran (莫映，王开福译) 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 本书主要讲解如何将AI的一些算法应用到web开发中，如何挖掘和分析web应用上的数据和资源，也是用python实现。 7. 《概率论及其应用》 作者（译者）： 威廉费勒(胡迪鹤译) 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 内容最丰富，最经典的一本概率论书籍即是本书。 8. 《线性代数及其应用》 作者（译者）： 莱(Lay D.C.) (刘深泉译) 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 国内教材学完了不知道应用地方，本书用几何的，形象化的，具体的问题和实例回答了线性代数的用处，强烈推荐。 开发经典 9. Game Code Complete 作者（译者）： Mike McShaffry (作者), David Graham (作者) 出版社： Cengage Learning PTR 购买链接： 购买 10. C++高效编程：内存与性能优化 作者（译者）： [美] R.Alexande 出版社： 中国电力出版社 购买链接： 购买 11. 代码珠玑 作者（译者）： [美]Jon Bentley 出版社： 人民邮电出版社 购买链接： 购买 12. Code Complete 作者（译者）： Steve McConnell 出版社： Microsoft Press 购买链接： 购买 13. Clean Code 作者（译者）： Robert C. Martin 出版社： Robert C. Martin 购买链接： 购买 14. The Pragmatic Programmer 作者（译者）： Andrew Hunt , David Thomas 出版社： Addison-Wesley Professional 购买链接： 购买 15. LUA程序设计（第二版）：Lua的作者写的，必读 作者（译者）： (巴西)Roberto Ierusalimschy 出版社： Lua.Org 购买链接： 购买 架构 16. 网络游戏核心技术与实战（日本人写的:非常严谨和实用） 作者（译者）： 中嶋谦互(作者),毛姝雯(译者),田剑(译者) 出版社： 人民邮电出版社 购买链接： 购买 17. 大型分布式网站架构设计与实践 作者（译者）： 陈康贤 出版社： 电子工业出版社 购买链接： 购买 18. 大型网站技术技术架构：核心原理与案例分析 作者（译者）： 李智慧 出版社： 电子工业出版社 购买链接： 购买 19. 大型网站系统与java中间件实践 作者（译者）： 曾宪杰 出版社： 电子工业出版社 购买链接： 购买 20. 深入应用c++11 代码优化与工程级应用 作者（译者）： 祁宇 出版社： 机械工业出版社 购买链接： 购买 21. 技术团队启示录 作者（译者）： 麦思博（北京）软件技术有限公司 出版社： 电子工业出版社 购买链接： 购买 22. 技术管理之巅:如何从零打造高质效互联网技术团队 作者（译者）： 黄哲铿 出版社： 电子工业出版社 购买链接： 购买 服务器 23. 《UNIX网络编程》卷1，卷2 作者（译者）： (美)W. Richard Stevens Bill Fenner Andrew M. Rudoff 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 啥都不说了，网络编程入门经典中的经典，已经出了3版。 24. 《软件调试》 作者（译者）： 张银奎 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 商业化软件调试各种方法和工具，如果你只知道用调试器打断点，看LOG来调试太LOW了，这个里面有些更高级的工具和方法。这本书我没有看完，只是看了部分我个人觉得能看懂和有用的东西，简单说其他的部分我看不懂。还是要继续修炼内功啊。 25. 《设计模式》 作者（译者）： [美]弗里曼（Freeman E.） 出版社： 中国电力出版社 购买链接： 购买 推荐理由： 例子都是JAVA版的，但是思想是通的 26. 《MYSQL开发者权威指南》 作者（译者）： (荷)Rick F. van der Lans 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 内容很多，几乎涵盖了MYSQL官方文档的大部分内容，入门必备，是不错的工具书 27. 《LINUX系统管理技术手册》 作者（译者）： [美]Evi Nemeth,Garth Snyder,Trent R.Hein 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 不喜欢看MAN文档，这本书那就比较适合了，入门必备 28. 《POSIX多线程编程》 作者（译者）： 布腾霍夫（Butenhof David R.），于磊，曾刚 出版社： 中国电力出版社出版 购买链接： 停刊，淘宝上可以买到影印版 推荐理由： UNIX下多线程程序设计必看 29. 《C++标准程序库》 作者（译者）： Nicolai M.Josuttis 出版社： 华中科技大学出版社 购买链接： 购买 推荐理由： 新手入门必看，如果你不太愿意看MSDN或者看STL的官方英文文档，这本书应该比较适合英文不好的程序员，里面会讲一些STL使用的注意事项以及新手很喜欢犯的错，作为桌上常备的工具书比较好。 30. 《WIN32多线程程序设计》 作者（译者）： Jim Beveridge \u0026amp; Robert Wiener 出版社： 华中科技大学出版社 购买链接： 购买 推荐理由： 虽然上面的内容很陈旧了，不过对于初学者来说是非常好的入门书籍 31. 《windows网络编程技术》 作者（译者）： Anthony Jones 出版社： 微软出版社 购买链接： 购买 推荐理由： 微软官方出品，内容陈旧，不过里面从古老的邮槽，NETBIOS，红外接口，到WINSOCK都有讲到，其中WINDOWS的IO复用（完成端口除外）全部涉及到。对比UNIX网络编程看，还是可以发现很多不同的地方。友情提示，日本人写的IPMSG早期的版本就是基于WINDOWS下基于窗口消息的IO模型来做的。 32. LUA程序设计（第二版） 作者（译者）： (巴西)Roberto Ierusalimschy 出版社： 电子工业出版社 购买链接： 购买 33. Python Cookbook 中文版，第 3 版 作者（译者）： 美]大卫·比斯利（David Beazley），布莱恩·K.琼斯（Brian K.Jones） 出版社： 人民邮电出版社 购买链接： 购买 34. 利用Python进行数据分析 作者（译者）： 美）麦金尼　著，唐学韬　等译 出版社： 机械工业出版社 购买链接： 购买 35. Node.js+MongoDB+AngularJS Web开发 作者（译者）： Brad Dayley（布拉德.德雷） 出版社： 电子工业出版社 购买链接： 购买 36. 链接器和加载器 作者（译者）： （美）莱文 出版社： 北京航空航天大学出版社 购买链接： 购买 37. 《GCC技术参考大全》 作者（译者）： [美]格里菲斯 著；胡恩华 译 出版社： 清华大学出版社 购买链接： 购买 推荐理由： 不喜欢看英文文档的同学的工具书 38. Python Cookbook 中文版 作者（译者）： [美]大卫·比斯利（David Beazley），布莱恩·K.琼斯（Brian K.Jones） 著；陈舸 译 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 都第三版了，值得一读 39. 利用Python进行数据分析 作者（译者）： Wes McKinney著；唐学韬 等 译 出版社： 机械工业出版社 购买链接： 购买 40. 链接器和加载器 作者（译者）： John R. Levine 出版社： Morgan Kaufmann 购买链接： 购买 41. Node.js+MongoDB+AngularJS Web开发 作者（译者）： Brad Dayley（布拉德·德雷） 著；卢涛 译 出版社： 电子工业出版社 购买链接： 购买 游戏相关 42. 3D数学基础：图形与游戏开发 作者（译者）： [美]邓恩（Dunn F.） 著；史银雪，陈洪，王荣静 译；北京递归开元教育科技有限公司 校 出版社： 清华大学出版社 购买链接： 购买 43. 我所理解的 Cocos2d-x 作者（译者）： 秦春林 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 除了源码之外，唯一值得阅读的Cocos2d-x资料。 44. Cocos2d-x by Example Beginner’s Guide Second Edition 作者（译者）： Roger Engelbert 出版社： None 购买链接： 购买 推荐理由： 好吧，这个是唯二。 45. OpenGL ES 3.0 编程指南 作者（译者）： Dan Ginsburg，Budi Purnomo，Dave Shreine著；姚军 译 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 之前阅读过 该书2.0版本的英文版 ，感觉深入浅出，非常好懂。这本书我买了但还没有读，相信同作者出的3.0版本也会不错。 46. OpenGL 编程指南（原书第7版） 作者（译者）： （美）Dave Shreiner The Khronos OpenGL ARB Working Group 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 毛主席语录一般的存在。说实话，到现在还没读完（囧——） 47. 游戏引擎架构 作者（译者）： 杰森·格雷戈瑞(Jason Gregory) 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 这本书的译者 叶劲峰 是知乎上的技术大牛，为人诚恳，认真，专注，谦和。当然这本书也不错。 48. 游戏机制——高级游戏设计技术 作者（译者）： [美] Ernest Adams，Joris Dormans著；石曦 译 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 这本书是写给游戏策划看的。我随便翻了一下，感觉挺有趣，能学到不少让人脑子进水的方法。建议有独立游戏制作意愿的程序员和大脑一根筋症状者阅读。 数学入门 49. 神马是数学 作者（译者）： [美] R·柯朗（Richard Courant），[美] H·罗宾（Herbert Robbins） 著；左平，张饴慈 译 出版社： 复旦大学出版社 购买链接： 购买 推荐理由： 数学入门经典，看了都说好，即使不能完全看懂，也能开阔一下眼界。 50. 程序员的数学 作者（译者）： 结城浩(作者),管杰(译者) 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 很简单的数学知识，很快就能读完的，你会有一些有趣的认识。 51. 程序员的数学2-概率统计 作者（译者）： [日]平冈和幸，堀玄 著；陈筱烟 译 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 上本书的作者的作品，每个程序员都该学一点统计知识。 编程理论 52. 重构-改善既有代码的设计 作者（译者）： None 出版社： addison-wesley professional 购买链接： 购买 推荐理由： 烂代码终结者。 53. 代码大全 作者（译者）： 美] Steve，[美] McConnell著；金戈，汤凌，陈硕，张菲 译 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 烂代码终结者（之二）。 54. JAVA 与模式 作者（译者）： 阎宏 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 能砸死人，但作者足够细致。 55. Head First 设计模式 作者（译者）： 美]弗里曼（Freeman E.） 著；UML China编；OReilly Taiwan公司 译 出版社： 中国电力出版社 购买链接： 购买 推荐理由： 如果只希望看一本设计模式的书，这本就够了（虽然废话很多）。 56. 人月神话 作者（译者）： [美] 弗雷德里克·布鲁克斯 著；UMLChina翻译组，汪颖 译 出版社： 清华大学出版社 购买链接： 购买 推荐理由： 这就是那句著名的 “没有银弹” 的出处了。 57. 七周七语言 作者（译者）： [Mei]BruceA.Tate 出版社： Posts and Telecom Press 购买链接： 购买 推荐理由： 世界并不是由 C++ 组成的。 58. 代码的未来 作者（译者）： 松本行弘(Yukihiro Matsumoto) (作者),周自恒(译者) 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： Ruby 作者的展望。 程序员修养 59. 程序员的思维修炼：开发认知潜能的九堂课 作者（译者）： Andy Hunt (作者) 出版社： The Pragmatic Programmers 购买链接： 购买 推荐理由： 仔细读，并跟着练习，这给你一种全新的思路。建议大脑一根筋症状者阅读。 60. 高校程序员的45个习惯：敏捷开发修炼之道 作者（译者）： （美）苏帕拉马尼亚姆　著，钱安川，郑柯　译 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 啥是敏捷？ Web 61. 无懈可击的 Web 设计 作者（译者）： 西德霍姆(Dan Cederholm) (作者),马跃(译者) 出版社： 清华大学出版社 购买链接： 购买 推荐理由： 改变你的Web排版固有思维。 62. CSS 禅意花园 作者（译者）： (美)Dave Shea Molly E.Holzschlag 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： CSS是优美的语言，这本书中艺术的内容偏多。 63. HTML5 程序设计（第2版） 作者（译者）： [荷]Peter Lubbers [美]Brian Albers [美]Frank Salim著 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 讲述清晰，覆盖了HTML5中的大部分知识点。 64. HTTP 权威指南 作者（译者）： [美]David Gourley　Brian Totty　Marjorie Sayer　Sailu Reddy　Aushu Aggarwal 著 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 做Web不懂HTTP就太low了。读完后，你就可以知道当你在浏览器中输入网址然后按下回车到显示页面之前这段时间里面都发生了神马。 客户端 65. Objective-C 基础教程 作者（译者）： (美)Mark Dalrymple Scott Knaster 出版社： 人民邮电出版社 购买链接： 购买 66. OpenGL ES 2.0游戏开发(上卷) 作者（译者）： 吴亚峰 出版社： 人民邮电出版社 购买链接： 购买 67. OpenGL ES 2.0游戏开发(下卷) 作者（译者）： 吴亚峰 出版社： 人民邮电出版社 购买链接： 购买 68. Cocos2d-x 之Lua 核心编程 作者（译者）： 刘克男，杨雍 出版社： 清华大学出版社 购买链接： 购买 69. 项目管理知识体系指南（PMBOK指南）（第5版） 作者（译者）： (美)Project Management Institute项目管理协会 出版社： 电子工业出版社 购买链接： 购买 groot 70. Hadoop.-.The.Definitive.Guide.4th.Edition 作者（译者）： 怀特(Tom White) 出版社： 东南大学出版社 购买链接： 购买 71. Learning.Spark.-.Lightning-Fast.Big.Data.Analysis 作者（译者）： Holden Karau (作者), Andy Kowinski (作者), Mark Hamstra (作者), Matei Zaharia (作者) 出版社： O'Reilly Media, Inc, USA 购买链接： 购买 72. Learning.Apache.Kafka.2nd.Edition 作者（译者）： Nishant Garg 出版社： Packt Publishing Limited 购买链接： 购买 C/C++/JAVA 73. 深入java虚拟机第二版 作者（译者）： 周志明 出版社： 机械工业出版社 购买链接： 购买 74. HotSpot实战 作者（译者）： 陈涛 出版社： 人民邮电出版社 购买链接： 购买 75. Java并发编程的艺术 + Java多线程编程核心技术 作者（译者）： 方腾飞 魏鹏 程晓明 出版社： 机械工业出版社 购买链接： 购买 76. Effective Java中文版（第2版） 作者（译者）： [美]布洛克（Joshua Bloch） 出版社： 机械工业出版社 购买链接： 购买 77. 分布式Java应用：基础与实践 作者（译者）： 林昊 出版社： 电子工业出版社 购买链接： 购买 78. MySQL技术内幕（第5版） 作者（译者）： [美]保罗·迪布瓦（Paul DuBois） 出版社： 人民邮电出版社 购买链接： 购买 79. Redis设计与实现 作者（译者）： 黄健宏 出版社： 机械工业出版社 购买链接： 购买 80. Spring技术内幕：深入解析Spring架构与设计原理（第2版） 作者（译者）： 计文柯 出版社： 机械工业出版社 购买链接： 购买 81. CDN技术详解 作者（译者）： 雷葆华，孙颖，王峰，陈晓益 出版社： 电子工业出版社 购买链接： 购买 82. 飞天开放平台编程指南：阿里云计算的实践 作者（译者）： 周憬宇，李武军，过敏意 出版社： 电子工业出版社 购买链接： 购买 83. C++ Primer（第5版） 作者（译者）： Stanley B. Lippman(斯坦利李普曼),Josee Lajoie(约瑟拉乔伊),Barbara E. Moo (芭芭拉默) 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 入门的经典，培养了一代代C++程序员。一定要买第五版哦！这里有 第五版和第四版的区别 84. C++语言的设计和演化 作者（译者）： （美）Bjarne Stroustrup 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 听C++语言的设计者娓娓道来，C++是怎样变成这样一个怪兽？ 85. QT 高级编程 作者（译者）： [英]萨默菲尔德（Mark Summerfield） 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 如果只需要学一种GUI库，那么就学习QT吧！其实我并不建议买本书来入门QT，QT应该在实践中掌握。但读读这本书也是不错的。 86. C程序设计语言（第二版，新版） 作者（译者）： （美）克尼汉，（美）里奇 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 这是最经典的C入门书籍。没有语言的入门书能够写得这样言简意赅直切要害。C语言设计者亲自操刀。 87. C陷阱与缺陷 作者（译者）： （美）凯尼格 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 写于ANSI规范以前。太多东西现在已经是常识了。随便翻翻当复习即可。 88. C和指针 作者（译者）： [美] Kenneth A.Reek 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 本书比较标题党，涵盖了指针之外的许多内容。 89. C专家编程 作者（译者）： [美] Perter Van Der Linden 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 这本书从c语言的起源讲起，分析c的语法和各种设定以及各种漏洞，对新人来说的确是值得一读的。 90. JAVA2核心技术第1卷.基础知识（第八版） 作者（译者）： [美]霍斯特曼/ [美]科奈尔 出版社： 机械工业出版社 购买链接： 购买 推荐理由： JAVA入门经典，不解释。 91. JAVA2核心技术II卷.高级特性（第八版） 作者（译者）： Cay S.Horstmann Gary Cornell 出版社： 机械工业出版社 购买链接： 购买 推荐理由： JAVA入门经典，不解释。 92. Java TCP/IP Socket编程 作者（译者）： Kenneth L. Calvert / Michael J. Donahoo 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 一本很薄的小册子，但覆盖了使用JAVA编写Socket服务器的全部知识。 脚本语言 93. HTML5+JavaScript动画基础 作者（译者）： [美]兰贝塔，[美]彼得 出版社： 人民邮电出版社 购买链接： 购买 94. Javascript 高效图形编程 作者（译者）： （英）切克 出版社： 人民邮电出版社 购买链接： 购买 95. Javascript DOM 编程艺术 作者（译者）： Jeremy Keith 出版社： 人民邮电出版社 购买链接： 购买 96. JavaScript权威指南(第6版) 作者（译者）： 美）弗兰纳根　著,淘宝前端团队　译 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 淘宝前端团队翻译的版本。犀牛书在手，天下我有。 97. Linux Shell脚本攻略 作者（译者）： (印)Sarath Lakshman 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 有很强的操作性，适合入门新手。里面给的范例也很有代表性。 98. sed与awk 作者（译者）： Dale Dougberty,Arnold Robbins 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 这两个古老的工具其实是两个脚本语言。 99. 精通正则表达式 作者（译者）： (美)Jeffrey E.F.Friedl 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 正则也算是一门强大的脚本语言，这个大家没异议把？ 工具 100. Git 权威指南 作者（译者）： 蒋鑫 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 国人所作，绝对的权威指南。 101. Ant 权威指南 作者（译者）： Eric M. Burke 出版社： 中国电力出版社 购买链接： 购买 推荐理由： Ant虽然很老了，但还在很多地方发挥作用。 102. Wireshark网络分析就这么简单 作者（译者）： 林沛满 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 绝对利器，前后端开发者都应该掌握。 103. 游戏人工智能编程案例精粹 作者（译者）： [美]Mat Buckland 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 这本书原版是AMAZON的准五星书籍。在现今游戏界被视为AI入门必读。作者讲解清楚，每章以一个有趣的实例作为框架说明具体算法的应用，每章也都附有实现代码。 104. Blender大师：建模、雕刻、材质、渲染 作者（译者）： （英）Ben Simonds 出版社： 科学出版社 购买链接： 购买 推荐理由： 开源的3D软件，制作3D游戏必备。 科幻 105. 凡尔纳科幻经典 作者（译者）： [法]凡尔纳 出版社： 安徽教育出版社 购买链接： 购买 推荐理由： 科幻小说的启蒙者，不用介绍太多。 106. 基地系列 作者（译者）： 艾萨克·阿西莫夫 出版社： 江苏文艺出版社 购买链接： 购买 推荐理由： 思考银河系中的战争，机器人与人的共存。 107. 永恒的终结 作者（译者）： 艾萨克·阿西莫夫 出版社： 江苏文艺出版社 购买链接： 购买 推荐理由： 如果你能在法律允许的框架下大规模干预历史，会发生什么？ 108. 三体 作者（译者）： 刘慈欣 出版社： 重庆出版社 购买链接： 购买 推荐理由： 不用介绍了吧。 109. 球状闪电 作者（译者）： 刘慈欣 出版社： 四川科技出版社 购买链接： 购买 推荐理由： 心爱的人永远活在我们的身边。 110. 流浪地球 作者（译者）： 刘慈欣 出版社： 长江文艺出版社 购买链接： 购买 推荐理由： 太阳的生命走到了终结，地球就是我们的诺亚方舟。 111. 朝闻道 作者（译者）： 刘慈欣 出版社： 山西春秋电子音像出版社 购买链接： 购买 推荐理由： 如果让你用生命换取你最想解决的难题的答案，你愿意么？ 112. 镜子 作者（译者）： 刘慈欣 出版社： 工人出版社 购买链接： 购买 推荐理由： 你能看到你的历史和未来？ 运维 113. 鸟哥的Linux私房菜 （基础学习篇 第三版） 作者（译者）： 鸟哥 出版社： 人民邮电出版社 购买链接： 购买 114. TCP/IP详解卷1 协议 作者（译者）： [美] W.Richard Stevens 出版社： 机械工业出版社 购买链接： 购买 115. 构建高性能Web站点 作者（译者）： 郭欣 出版社： 电子工业出版社 购买链接： 购买 116. 构建高可用Linux服务器（第3版） 作者（译者）： 余洪春 出版社： 机械工业出版社 购买链接： 购买 测试 117. 《颠覆完美软件:软件测试必须知道的几件事》 作者（译者）： 杰拉尔德•温伯格 (Gerald M.Weinberg) (作者), 宋锐 (译者) 出版社： 电子工业出版社 购买链接： 购买 118. 《从菜鸟到测试架构师:一个测试工程师的成长日记 》 作者（译者）： 《从菜鸟到测试架构师》编委会 (作者) 出版社： 电子工业出版社 购买链接： 购买 119. 大话移动APP测试：Android与 iOS应用测试指南 作者（译者）： 陈晔 出版社： 清华大学出版社 购买链接： 购买 120. google的软件测试之道 作者（译者）： 美] James Whittaker，[美] Jason Arbon，[美] Jeff Carollo 著；黄利，李中杰，薛明 译 出版社： 人民邮电出版社 购买链接： 购买 121. 探索式软件测试 作者（译者）： James A. Whittaker 出版社： 清华大学出版社 购买链接： 购买 122. 软件测试经验与教训 作者（译者）： Lessons Learned in Software Testing 出版社： 机械工业出版社 购买链接： 购买 游戏美术 123. 进阶理解版式设计 [Layout design step by step] 作者（译者）： eye4u视觉设计工作室 出版社： 中国青年出版社 购买链接： 购买 推荐理由： 所有板式设计相关的理论性参考书，比较实在。 124. The Art of the Disney Golden Books 作者（译者）： Charles Solomon 出版社： Disney Editions 购买链接： 购买 推荐理由： 欧式卡通插画经典 125. The Art of the Croods 作者（译者）： Noela Hueso 出版社： Titan Books 购买链接： 购买 推荐理由： 欧美风卡通原画典范 126. 插画大师Alphonse Mucha Masterworks阿尔丰斯 穆夏作品集 作者（译者）： 穆夏 出版社： Flame Tree Publishing; New edition edition 购买链接： 购买 推荐理由： 近几年才在国内被大众熟知的绘画名家，视觉引导的教科书级高手，作品华丽而富有韵味。 127. 国际经典交互设计教程 用户体验设计 作者（译者）： 孔祥富 出版社： 电子工业出版社 购买链接： 购买 推荐理由： Ui设计教程 交互设计 界面设计 图标手机UI素材设计书 128. 设计师要懂心理学 作者（译者）： Susan Weinschenk 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 设计心理学力作 畅销正版书籍 129. 移动设计 作者（译者）： 傅小贞,胡甲超,郑元拢 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 淘宝移动应用先驱团队，数年交互设计经验和盘托出 数值 130. 概率论与数理统计 作者（译者）： 盛骤，谢式千，潘承毅 出版社： 高等教育出版社 购买链接： 购买 推荐理由： 数值策划的基础科学，最有用的教材书，没有之一。 131. 数学建模 作者（译者）： 吉奥丹诺 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 帮助建立数值模型。 132. 机会的数学原理 作者（译者）： （英）约翰·黑格 出版社： 吉林人民出版社 购买链接： 购买 推荐理由： 深入探讨彩票、轮盘赌、扑克游戏等以概率为核心的问题，已经停印只有盗版。 133. 游戏中的数学与物理学 作者（译者）： （美）弗林特，（美）科迪塞克 出版社： 清华大学出版社 购买链接： 购买 推荐理由： 更适合开发看，看了这本书能让百人场的金币飞得更科学一点。 134. 几率游戏 作者（译者）： （美）弗尔（Fawer，A.） 出版社： 上海人民出版社 购买链接： 购买 推荐理由： 世间一切选择都是概率问题。 游戏设计 135. 全景探秘游戏设计艺术 作者（译者）： （美）谢尔 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 游戏设计经典书籍。 136. 体验引擎：游戏设计全景探秘 作者（译者）： Tynan Sylvester 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 将游戏体验总结抽象为理论。大佬推荐。 137. 有生之年非玩不可的1001款游戏 作者（译者）： （英）托尼 莫特（Tony Mott） 出版社： 中央编译出版社 购买链接： 购买 推荐理由： 对游戏设计来讲，看1000本书不如玩1000款游戏。另外墙裂建议公司购入PS4和Xbox各一台放到公共区域。 138. 像艺术家一样思考 作者（译者）： [美]艾德华 出版社： 北方文艺出版社 购买链接： 购买 推荐理由： 提高美术素养，方便与美术沟通。 心理与人性 139. 心理学与生活 作者（译者）： [美]格里格，津巴多 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 心理学基本书籍。 140. 乌合之众 作者（译者）： （法）古斯塔夫 勒庞 出版社： 中央编译出版社 购买链接： 购买 推荐理由： 了解群体心理，毕竟网络游戏就是交互。 141. 无快乐的经济:人类获得满足的心理学 作者（译者）： （美）西托夫斯基 出版社： 中国人民大学出版社 购买链接： 购买 推荐理由： 了解人类动机和满足的心理学。 142. 思维导图 作者（译者）： （英）东尼·博赞，巴利·博赞 出版社： 化学工业出版社 购买链接： 购买 推荐理由： 作为策划，逻辑流程图是基础技能，应该养成良好的逻辑思维习惯。 工具 143. MATLAB 在数学建模中的应用 作者（译者）： 卓金武 出版社： 北京航空航天大学出版社 购买链接： 购买 推荐理由： 学习如何使用MATLAB拟合数学函数和多项式。 144. Excel VBA程序开发自学宝典 作者（译者）： 罗刚君 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 曾经有人跟我说要成为一个牛逼的数值策划，VBA和MATLAB必须熟练使用一个，但是到现在我还是一个都不会。。。 145. 游戏关卡设计 作者（译者）： 美）科（Co,P.） 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 暴雪公司十年磨一剑的游戏精品《魔兽世界》副本任务的参考书籍 146. 群体智慧：用团队解决难题 作者（译者）： 美）哈克曼 出版社： 北京大学出版社 购买链接： 购买 推荐理由： 和《乌合之众》对比着看 147. 游戏设计：原始与实践 作者（译者）： Richard RouseⅢ 出版社： 电子工业出版社 购买链接： 购买 148. 全景揭秘游戏设计艺术 The Art of Game Design 作者（译者）： （美）谢尔 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 都卖绝版了，到货就下手吧 149. 21天搞掂电影剧本 作者（译者）： （美）维基金（Viki King）著 出版社： 北京联合出版公司 购买链接： 购买 推荐理由： 好莱坞圈内编剧的行业标准书 “8分钟写作法”帮你提高编剧效率 150. 游戏设计快乐之道 作者（译者）： [美]Raph Koste著 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 体会游戏设计的黄金法则，领悟游戏的快乐之道，游戏创意和设计大师经典之作 151. 游戏设计的100个原理 作者（译者）： （美）迪斯潘 出版社： 人民邮电出版社 购买链接： 购买 152. 游戏机制——高级游戏设计技术 作者（译者）： [美]Ernest AdamsJoris Dormans 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 游戏设计权威专家力作，游戏编程实例教程，游戏引擎设计精粹，android游戏开发宝典！首次全面剖析游戏机制，全彩印刷！ 153. 神作之路——卓越游戏设计剖析 作者（译者）： [美]Michael Thornton Wyman著 出版社： 人民邮电出版社 购买链接： 购买 154. 通关！游戏设计之道 作者（译者）： （美）罗杰斯 著 出版社： 人民邮电出版社 购买链接： 购买 155. 游戏设计快乐之道 作者（译者）： [美]Raph Koste著 出版社： 人民邮电出版社 购买链接： 购买 156. 格式塔心理学原理 作者（译者）： (美)库尔特·考夫卡,李维 出版社： 北京大学出版社 购买链接： 购买 157. 游戏设计师修炼之道：数据驱动的游戏设计 作者（译者）： (美)摩尔 出版社： 机械工业出版社 购买链接： 购买 推荐理由： 资深游戏设计师25年工作经验结晶，以数据驱动方式讲解了各类游戏各个环节的设计原理和技巧，游戏设计 158. 游戏改变世界 作者（译者）： [美]简麦戈尼格尔（Jane McGonigal）著 出版社： 浙江人民出版社 购买链接： 购买 推荐理由： 著名未来学家 、TED新锐演讲者首次权威阐释：游戏化，互联时代的重要趋势 159. 游戏架构设计与策划基础 作者（译者）： 黄石，李志远，陈洪 出版社： 清华大学出版社 购买链接： 购买 160. 游戏设计原理 作者（译者）： 桂宇晖 出版社： 清华大学出版社 购买链接： 购买 161. 游戏设计概论（第4版） 作者（译者）： 胡昭民，吴灿铭　编著 出版社： 清华大学出版社 购买链接： 购买 运营 162. 小白学运营 作者（译者）： 刘异，伍斌，赵强 出版社： 电子工业出版社 购买链接： 购买 163. 谁说菜鸟不会数据分析（入门篇） 作者（译者）： 张文霖，刘夏璐，狄松 出版社： 电子工业出版社 购买链接： 购买 164. 谁说菜鸟不会数据分析（工具篇） 作者（译者）： 张文霖，刘夏璐，狄松 出版社： 电子工业出版社 购买链接： 购买 165. 人人都是产品经理 作者（译者）： 苏杰 出版社： 电子工业出版社 购买链接： 购买 166. 游戏设计要则探秘 作者（译者）： Anna Anthropy (安娜.安托弗),Naomi Clark（娜奥米.克拉克） 出版社： 电子工业出版社 购买链接： 购买 167. 体验引擎：游戏设计全景探秘 作者（译者）： 西尔维斯特 出版社： 电子工业出版社 购买链接： 购买 168. 暴雪的艺术 作者（译者）： 美国暴雪娱乐股份有限公司 出版社： 北京联合出版公司 购买链接： 购买 169. 古剑奇谭官方美术设定集 作者（译者）： 上海烛龙 出版社： 北京网元圣唐娱乐科技有限公司 购买链接： 购买 170. 浪潮之巅 第二版套装：上册+下册 作者（译者）： 吴军 出版社： 人民邮电出版社 购买链接： 购买 171. 参与感 作者（译者）： 黎万强 出版社： 中信出版社 购买链接： 购买 172. 史玉柱自述：我的营销心得 作者（译者）： 史玉柱 出版社： 北京日报出版社（原同心出版社） 购买链接： 购买 173. 周鸿祎自述：我的互联网方法论 作者（译者）： 周鸿祎 出版社： 中信出版社 购买链接： 购买 174. 怪诞行为学（升级版） 作者（译者）： （美）艾瑞里 出版社： 中信出版社 购买链接： 购买 175. 客服圣经：如何成功打造顾客忠诚度（原书第6版） 作者（译者）： （美）蒂姆 出版社： 机械工业出版社 购买链接： 购买 176. 互联网周刊 作者（译者）： 半月刊 出版社： 科学出版社 购买链接： 购买 177. 家用电脑与游戏 作者（译者）： 月刊 出版社： 《家用电脑与游戏》杂志社 购买链接： 购买 客服 178. 不一样的客户服务 作者（译者）： 吴宏晖 出版社： 北京联合出版公司 购买链接： 购买 179. 客户服务管理工具大全 作者（译者）： 洪冬星 出版社： 人民邮电出版社 购买链接： 购买 180. 客户服务管理体系设计全案 作者（译者）： 洪冬星 出版社： 人民邮电出版社 购买链接： 购买 商务 181. 卓越的商务沟通 作者（译者）： （美）希尔，（美）博韦 出版社： 北京大学出版社 购买链接： 购买 182. 受益一生的哈佛心理课——经典案例版 作者（译者）： 哈佛公开课研究会 出版社： 中国铁道出版社 购买链接： 购买 183. 一本书读懂APP营销 作者（译者）： 海天理财 出版社： 清华大学出版社 购买链接： 购买 184. APP运营推广：抢占移动互联网入口、引爆下载量、留住用户 作者（译者）： 胡保坤 出版社： 人民邮电出版社 购买链接： 购买 185. 《一个人的电商》 作者（译者）： 许晓辉 出版社： 电子工业出版社 购买链接： 购买 推荐理由： 真实记录如何运营用户 做品牌等很多内容，内容偏运营，推荐做业务的同学都看一下。 186. 《计算广告》 作者（译者）： 刘鹏和王超 出版社： 人民邮电出版社 购买链接： 购买 推荐理由： 本书的内容是介绍与互联网数据变现、广告营销相关的产品体系与计算技术，并进而启发大家对于互联网商业模式具体的思考。 （全文完）\n","date":"2015-12-23","description":"","lastmod":"2015-12-23T05:50:03Z","slug":"reading-list3","tags":["reading"],"title":"游戏部门推荐书单汇总","url":"https://blog.zengrong.net/post/reading-list3/"},{"categories":["impressions"],"content":" 开始：2015-11-22 读完：2015-12-08 书籍介绍： 编号1529 对于这本享誉多年的小册子，我读完之后收获还是挺大的。以我的学识，并没有能力做出什么评价，所以只是把书中的一些我觉得有趣的观点摘抄出来，并发表一些自己不成熟的想法。\n这本小册子中有一小半的篇幅是后人对勒庞这本著作的评论，提前看更容易理解勒庞的完整观点。\nRobert Merton 在 1960年1月 所写的 “勒庞《乌合之众》的得与失“ 中就提到：\n我们只能说，勒庞与我们中间的大多数人一样，没有能力对他提出的各种观点的价值作出区分。它们全是他头脑的产物，因此他显然对它们一样厚爱有加。不管是好的还是坏的，能结出果实的还是寸草不生的，所有这些观点都受到了其长辈同样的呵护。\n我觉得这段话说明了一个朴素的观点：不能因为观点是自己头脑的产物，就全盘接受它。要找到证据证明观点的正确性。证明的最好办法就是实践和听取该观点的反对者的意见。\n我以为 Robert Merton 对全书的总结是很公平的：\n绝对谈不上完全创新，严格地说也不正确；……表现最佳时也只能算较好，最差时也不算很糟；……眼光时而偏于一隅时而放眼全球；既有预见又观念落后；在实践中有效地利用着历史，又从原则上否认它的真实性和有效性；……还有一些并不影响其本质的乌七八糟的意识形态怪论。\n下面是我的摘录和一些看法。\n1. 形成群体的个人也会感到一种势不可挡的力量，这使他敢于发泄出自本能的欲望，而在他独自一人时，他是必须对这些欲望加以限制的。他很难约束自己不产生这样的念头：群体是个无名氏，因此也不必承担责任。\n2. 第二个原因是传染的现象，也对群体的特点起着决定作用，同时还决定着它所接受的倾向。\n3. 有意识人格的消失，无意识人格的得势，思想和感情因暗示和互相传染作用而转向一个共同的方向，以及立刻把暗示的关键转化为行动的倾向，是组成群体的个人所表现出来的主要特点。他不再是他自己，他变成了一个不再受自己意志支配的玩偶。\n4. 群体在智力上总是低于孤立的个人，但是从感情及其激起的行动这个角度看，群体可以比个人表现得更好或更差，这全看环境如何。一切取决于群体所接受的暗示具有什么性质。\n5. 群体不仅冲动而且多变。在孤身一人时，他不能焚烧宫殿或洗劫商店，即使收到这样做的诱惑，他也很容易抵御这种诱惑。但是在成为群体的一员时，他就会意识到人数赋予他的力量，这足以让他生出杀人劫掠的念头，并且会立刻屈从于这种诱惑。\n6. 群体通常总是处在一种期待状态中，因此很容易受人暗示。\n7. 于是，群体永远漫游在无意识的领地，会随时听命于一切暗示，表现出对理性的影响无动于衷的生物所特有的激情，他们失去了一切批判能力，除了极端轻信外再无别的可能。\n8. 从他们成为群体一员之日始，博学之士便和白痴一起失去了观察能力。\n9. 应当指出，产生这种误认的经常是妇女和儿童——即最没有主见的人。尤其是儿童而言，绝不能拿他们的证词当真。……儿童就一直在撒谎。……还不如用扔钱币的方式来得合理。\n我的看法： 这种说法太过偏激，什么破学说嘛……完全没有依据。\n10. 那些在人类历史上起过重大作用的为人，如赫拉克利特，释迦摩尼或穆哈默德，我们拥有一句真实的记录吗？我们极可能一句也没有。\n我的看法： 这倒是有可能的。\n11. 对何为真理何为缪误总是心存怀疑，另一方面，又清楚地意识到自己的强大，群体便给自己的理想和偏执赋予了专横的性质。个人可以接受矛盾，进行讨论，群体是绝对不会这样做的。\n我的看法： 文化大革命。\n12. 即使在一群罪大恶极的坏蛋中间，经常也会出现这样的情况，他们仅仅是群体中的一员，便会暂时表现出严格的道德纪律。\n我的看法： 盗亦有道。\n13. 群体对个人的这种道德净化作用，肯定不是一种不变的常规。然而，它却是一种经常可以看到的常态。\n14. 如今，被我们的父辈视为人生支柱的那些伟大的基本概念，正在摇摇欲坠。它们的稳定性已丧失殆尽。同时，建立于其上的制度也受到了严重动摇。每天都在形成大量的我刚才说过的那种过眼烟云一般的观念，但是看来它们很少具有生命力并能发挥持久的影响。\n15. 群体没有推理能力，因此它也无法表现出任何批判精神，也就是说，它不能辨别真伪或对任何事物形成正确的判断。群体所接受的判断，仅仅是强加给它们的判断，而绝不是经过讨论后得到采纳的判断。\n16. 影响民众想象力的，并不是事实本身，而是它们发生和引起注意的方式。必须对它们进行浓缩加工，它们才会形成一种令人膛目结舌的惊人形象。掌握了影响群众想象力的艺术，也就掌握了统治他们的艺术。\n17. 如果一个民族使自己的习俗变得过于牢固，它便不会再发生变化，于是就像中国一样，变得没有改进能力。\n我的看法： 这不扯淡嘛。任何一个民族，没有改进能力的永远都是那些因循守旧的老家伙们，年轻人只要给予合适的教育和正确的引导，给予他们开放的思想，习俗这东西往往就从内部不攻自破。人类总是充满好奇心的，何况这是中国，伟大的中华民族。\n18. 最不受怀疑的偶像，并不住在庙堂之上，也不是宫廷里那些最专制的暴君，他们转瞬之间就可以被人打碎。支配着我们内心最深处的自我的，是那些看不见的主人，它可以安全地避开一切反驳，只能在数百年的时间里被慢慢地磨损。\n我的看法： 在当今社会，或许要不了数百年。数十年或者十数年就损失殆尽了。这让我想起另一句话：距离感带来神秘感，任何一个伟人在他的贴身侍从眼中都只是个普通人。\n19. 从来不考虑是否严谨对称，更多地是考虑它是否方便实用；从来不单纯以不一致为理由去消除不一致；除非感到有所不满，绝对不进行革新；除了针对具体情况必须提供的条款之外，绝对不制定任何范围更大的条款。\n我的评注： 我想这就是许多官员内心的信条吧。这是一百多年前的勒庞提出的 “不求有功，但求无过” 的为官之道的翻版。\n20. 生活中取得成功的条件是判断力，是经验，是开拓精神和个性——这些素质都不是书本能够带来的。教科书和字典可以是有用的参考工具但长久把它们放在脑子里却没有任何用处。\n我的看法： 我们需要思考，我们需要经历，我们需要尝试，我们需要批判。仅仅做好别人安排的事情是不够的，要时刻想着“如果是我，应该怎么做”？\n21. 他们中间有一半甚至是三分之二的人，是为了考试而活着……在规定的某一天，坐在一把椅子上，面对一个答辩团，在连续两小时的时间里，怀着对一切人类知识清单的敬畏，他们要做到正确……用不了一个月，他们便不再是这样。他们不可能再通过考试。他们脑子里那些过多的、过于沉重的所学不断流失，且没有新东西补充进去。他们的精神活力衰退了，他们继续成长的能力枯竭了，一个得到充分发展的人出现了。然而他也是个精疲力尽的人。他成家立业，落入生活的俗套，而只要落入这种俗套，他就会把自己封闭在狭隘的职业中，工作也许还算本分，但仅此而已。这就是平庸的生活，收益和风险不成比例的生活。\n我的看法： 这是对教育体系的抨击。即使是到了当代，中国的教育体系也没有太大的改变。如果不是家庭教育和社会教育的参与，仅凭学校教育很难把普通的孩子培养成当今社会需要的人才。好在目前中国的家长已经意识到了教育的重要性，越来越多地在教育上进行投入和干预，尽管这些投入和干预的方向并不是完全正确。\n22. 在医院、矿山和工厂，在建筑师或者律师的办公室里，十分年轻便开始学业的学生们，按部就班地经历他们的学徒期，非常类似于办公室里的律师秘书或工作室里的艺术家……而且他们能够利用自己的空闲时间得到各种各样的技能……实践能力得到了发展，并且与学生的才能相适应，发展方向也符合他未来的任务和特定工作的要求，这些工作就是他今后要从事的工作。他不但成了一个有用的工作者，甚至具备自我创业的能力；他不只是机器上的一个零件，而且是个发动机。\n我的看法： 我喜欢“发动机”这个词。现在我国大力推进的“大学生创业计划”，我认为是很难成功的。原因就是没有勒庞所说的这种 “学徒期” 。教育在短时期内（几十年）退回到上百年前那种学徒时代是不可能的，也是不现实的。因此现在的大学生休学创业成功的可能性极低。没有任何社会和项目经验的 “零件” ，不可能干 “发动机” 的活儿。创业并非是社会推动，学校支持就能成功的，它必须要符合市场规律并创造足够的价值。从我目前接触的985高效大四实习生来看，还没有观察到拥有创业能力的倾向。\n23. 极为重要的修辞只有一个，那就是重复。得到断言的事情，是通过不断重复才在头脑中生根，并且这种方式最终能够使人把它当作得到证实的真理接受下来。\n我的看法： 在长期的培训过程和带人的过程中，我发现一个铁律： 没有人会仔细在意你说过的每一句话，不管你说得多么认真和详细。所以，必须不断重复重复再重复，才能让人记住你的观点。当 80% 的人记住你 80% 的观点之后，剩下那 20% 知识只需要 20% 的人知道就行了（这些都是精髓）。\n24. 不错，成吉思汗和拿破仑都是可怕的暴君，但是躺在坟墓深处的摩西、佛祖、耶稣和穆罕默德，对人类实行着更深刻的专制统治。\n我的看法： 可怕的不是这些已经死去的人或神，而是利用这些死人（神）来谋取利益的活人。想想IS吧。\n全文完 ","date":"2015-12-19","description":"","lastmod":"2015-12-19T14:01:09Z","slug":"the-crowd-a-study-of-the-popular-mind","tags":["reading","life","readingnote"],"title":"【读书笔记】乌合之众","url":"https://blog.zengrong.net/post/the-crowd-a-study-of-the-popular-mind/"},{"categories":["technology"],"content":"上次写了 我的推荐书单（一） ，这块砖引出了不少真玉。稍后我会把公司其他同事推荐的书单整理在我的博客上。这次，就在看医生排队期间在书店里把上次承诺的（二）补全吧。\n总书单在此： 游戏部门推荐书单汇总 。\nC/C++/JAVA 书名 曾嵘评注 C++ Primer（第5版） 入门的经典，培养了一代代 C++ 程序员。一定要买第五版哦！这里有 第五版和第四版的区别 C++语言的设计和演化 听 C++ 语言的设计者娓娓道来，C++ 是怎样变成这样一个怪兽？ QT 高级编程 如果只需要学一种 GUI 库，那么就学习 QT 吧！其实我并不建议买本书来入门QT，QT应该在实践中掌握。但读读这本书也是不错的。 C程序设计语言（第二版，新版） 这是最经典的 C 入门书籍。没有语言的入门书能够写得这样言简意赅直切要害。C 语言设计者亲自操刀。 C陷阱与缺陷 写于ANSI规范以前。太多东西现在已经是常识了。随便翻翻当复习即可。 C和指针 本书比较标题党，涵盖了指针之外的许多内容。 C专家编程 这本书从c语言的起源讲起，分析c的语法和各种设定以及各种漏洞，对新人来说的确是值得一读的。 JAVA2核心技术第1卷.基础知识（第八版） JAVA入门经典，不解释。 JAVA2核心技术II卷.高级特性（第八版） 同上 Java TCP/IP Socket编程 一本很薄的小册子，但覆盖了使用 JAVA 编写 Socket 服务器的全部知识。 脚本语言 书名 曾嵘评注 HTML5+JavaScript动画基础 Javascript 高效图形编程 Javascript DOM 编程艺术 JavaScript权威指南(第6版) 淘宝前端团队翻译的版本。犀牛书在手，天下我有。 Linux Shell脚本攻略 有很强的操作性，适合入门新手。里面给的范例也很有代表性。 sed与awk 这两个古老的工具其实是两个脚本语言。 精通正则表达式 正则也算是一门强大的脚本语言，这个大家没异议把？ 工具 书名 曾嵘评注 Git 权威指南 国人所作，绝对的权威指南。 Ant 权威指南 Ant 虽然很老了，但还在很多地方发挥作用。 Wireshark网络分析就这么简单 绝对利器，前后端开发者都应该掌握。 游戏人工智能编程案例精粹 这本书原版是AMAZON的准五星书籍。在现今游戏界被视为AI入门必读。作者讲解清楚，每章以一个有趣的实例作为框架说明具体算法的应用，每章也都附有实现代码。 Blender大师：建模、雕刻、材质、渲染 开源的 3D 软件，制作 3D 游戏必备。 科幻 虽然科幻小说并非技术类书籍。但的确是值得程序员们看一看的。这里仅仅推荐了三个作者的作品，凡尔纳代表机器工业时代，阿西莫夫代表计算机时代，大刘则代表互联网时代。\n这三人都是硬科幻的代表，从他们的作品中，你可以看到科学幻想其实并没有超出当前的时代范畴。凡尔纳想到的潜艇中的一切都是基于机械时代的知识；阿西莫夫的“胶卷书”并没有触摸屏；大刘的许多故事都建立在互联网基础之上。\n这能看出一个有趣的现象：没有谁能脱离这个时代。作家如此，技术如此，生活亦如此。但只要披上想象的翅膀，你就能在这个时代飞过所有人的头顶。\n书名 作者 曾嵘评注 儒勒凡尔纳系列 科幻小说的启蒙者，不用介绍太多。 基地系列 艾萨克·阿西莫夫 思考银河系中的战争，机器人与人的共存。 永恒的终结 艾萨克·阿西莫夫 如果你能在法律允许的框架下大规模干预历史，会发生什么？ 最后的答案 艾萨克·阿西莫夫 这是我最喜欢的科幻短篇。神说，要有光，于是就有了光。 三体 刘慈欣 不用介绍了吧。 球状闪电 刘慈欣 心爱的人永远活在我们的身边。 流浪地球 刘慈欣 太阳的生命走到了终结，地球就是我们的诺亚方舟。 朝闻道 刘慈欣 如果让你用生命换取你最想解决的难题的答案，你愿意么？ 镜子 刘慈欣 你能看到你的历史和未来？ （全文完）\n","date":"2015-12-13","description":"","lastmod":"2015-12-13T03:22:29Z","slug":"reading-list2","tags":["reading"],"title":"我的推荐书单（二）","url":"https://blog.zengrong.net/post/reading-list2/"},{"categories":["technology"],"content":"昨天公司要求整理书单，我顺便也弄一个。这是第一辑，若时间足够，后面会继续整理第二辑 第二辑在此： 我的推荐书单（二） ，总书单在此： 游戏部门推荐书单汇总 。\n[TOC]\n推荐理由 我读过，或者正在读； 我自己打分7分以上（满分10分）。 游戏相关 书名 曾嵘评注 3D数学基础：图形与游戏开发 我所理解的 Cocos2d-x 除了源码之外，唯一值得阅读的 Cocos2d-x 资料。 Cocos2d-x by Example Beginner’s Guide Second Edition 好吧，这个是唯二。 OpenGL ES 3.0 编程指南 之前阅读过 该书 2.0 版本的英文版 ，感觉深入浅出，非常好懂。这本书我买了但还没有读，相信同作者出的 3.0 版本也会不错。 OpenGL 编程指南（原书第7版） 圣经 毛主席语录一般的存在。说实话，到现在还没读完（囧——） 游戏引擎架构 这本书的译者 叶劲峰 是知乎上的技术大牛，为人诚恳，认真，专注，谦和。当然这本书也不错。 游戏机制——高级游戏设计技术 这本书是写给游戏策划看的。我随便翻了一下，感觉挺有趣，能学到不少让人脑子进水的方法。建议有独立游戏制作意愿的程序员和大脑一根筋症状者阅读。 数学入门 书名 曾嵘评注 神马是数学 数学入门经典，看了都说好，即使不能完全看懂，也能开阔一下眼界。 程序员的数学 很简单的数学知识，很快就能读完的，你会有一些有趣的认识。 程序员的数学2-概率统计 上本书的作者的作品，每个程序员都该学一点统计知识。 编程理论 书名 曾嵘评注 重构-改善既有代码的设计 烂代码终结者。 代码大全 烂代码终结者（之二）。 JAVA 与模式 能砸死人，但作者足够细致。 Head First 设计模式 如果只希望看一本设计模式的书，这本就够了（虽然废话很多）。 人月神话 这就是那句著名的 “没有银弹” 的出处了。 七周七语言 世界并不是由 C++ 组成的。 代码的未来 Ruby 作者的展望。 程序员修养 书名 曾嵘评注 程序员的思维修炼：开发认知潜能的九堂课 仔细读，并跟着练习，这给你一种全新的思路。建议大脑一根筋症状者阅读。 高校程序员的45个习惯：敏捷开发修炼之道 啥是敏捷？ Web 书名 曾嵘评注 无懈可击的 Web 设计 改变你的 Web 排版固有思维。 CSS 禅意花园 CSS 是优美的语言，这本书中艺术的内容偏多。 HTML5 程序设计（第2版） 讲述清晰，覆盖了 HTML5 中的大部分知识点。 HTTP 权威指南 做 Web 不懂 HTTP 就太 low 了。读完后，你就可以知道当你在浏览器中输入网址然后按下回车到显示页面之前这段时间里面都发生了神马。 我的 2015 年书单 2015年接近尾声，我的 2015 年书单 也接近完成了。很少一部分书提供了读书笔记，希望对你有用。\n（文本完）\n","date":"2015-12-02","description":"","lastmod":"2015-12-02T03:06:40Z","slug":"reading-list1","tags":["reading"],"title":"我的推荐书单（一）","url":"https://blog.zengrong.net/post/reading-list1/"},{"categories":["technology"],"content":"本系列所有文章：手机游戏开发框架设计（前言）\n包管理中心 包管理中心是整个配置管理中心的基础，所以与客户端有关的配置都是基于包管理的。\n包管理中心可配置每个包的 启动公告 ，实现 停服维护管理 ，这与服务器是否真正关闭无关。\n包管理中心还能够提供给客户端需要的服务器列表，实现 服务器故障灵活切换 。\n包管理中心按照每个客户端包提供不同的配置信息，使客户端能够实现以包和渠道为单位的细粒度配置。\n[TOC]\n配置依赖 在新的客户端开发构架中， 所有 的配置信息，都是从配置中心获取的。这里的 所有 包括：\n客户端要连接的服务器和端口号 客户端运行在哪个平台之上 客户端接入的 SDK 需要的信息（例如 talkdata 需要传递 tdid） 客户端用来证明自己属于哪个渠道的信息 客户端在支付的时候需要提供给第三方的信息（例如 IAB 和 IAP 中提供的证明自己账户的 appkey） 其他需要的信息 为了做到这一点，客户端需要两个值来表明自己的身份，还需要一个 API 地址来获取配置信息：\nPackage ID\n证明 你妈是你妈 的唯一身份标识，且不重复\nVersion\n表明包的版本号，用于 在线升级（TODO）\nConfig API\n配置 API 地址，API 依赖上面两个值来查找配置信息。例如：http://yoursite/api/config/\n这种架构，能够保证客户端的所有信息（版本信息和包ID除外）都是可以修改的，避免了包发出去之后出现配置错误而无法解决的问题。\n为了保证信息的安全（不被第三方随意获取到），需要增加一些附加验证信息，此处暂不讨论。\n包管理的两层架构 上面已经提到过，包管理中心是整个配置管理中心的基础。若无法区分包，则 支付中心（TODO） 和 版本管理中心（TODO） 都没有基础， 支付（TODO） 和 在线升级（TODO） 则更加无法完成。\n包管理仅有两层结构，这是为了避免层级过多导致的混乱。从现有的发行方式看来，两层结构已经足以解决 运营渠道 和 包 的所有问题。\n第一层结构是 运营渠道 ，我们使用 Channel ID 来指代它。\nChannel ID\n用来指定特定的 运营渠道 。每个渠道都有一个唯一的 Channel ID。例如 中国移动 是我们的一个渠道，我们可以使用 CMCC 来代表它。\n第二层结构是 包 。我们使用 Package ID 来指代它。\n为了推广，一个运营渠道大多数情况下都不会只有一个包，两个包之间只要有任何信息不同，就应该单独分包。每个包都要分配不同的 Package ID 。\n在客户端仅保存了 Package ID ，不必保存 Channel ID 。这是因为 [Config API][#configapi] 可以根据 Package ID 查询到 Channel ID 。\n在每个 Package ID 之下，可以配置不同的信息。根据 Package ID 的不同，这个包的配置也不同。简单的结构就是如此：\n1 2digraph packagecentersimple { 3 graph[rankdir=\u0026#34;LR\u0026#34;,label=\u0026#34;包管理的两层架构（略图）\u0026#34;] 4 node[shape=\u0026#34;box\u0026#34;,fillcolor=\u0026#34;white\u0026#34;,color=\u0026#34;black\u0026#34;] 5 6 channel[label=\u0026#34;Channel ID\u0026#34;, shape=\u0026#34;doubleoctagon\u0026#34;] 7 8 pid[label=\u0026#34;Package ID\u0026#34;] 9 config[label=\u0026#34;配置信息\u0026#34;] 10 11 channel -\u0026gt; pid -\u0026gt; config 12} 那么配置信息有哪些呢？在 配置依赖 中已经进行过简单介绍。在这里详细进行分类：\nSDK Config\n我们接入的 SDK 需要客户端告诉它们一些值，这些值若嵌入到客户端则不方便修改，放在配置中心则不然。\nPlatform\n客户端运行在何种平台之上。根据目前的运营状态，可能涉及到的所有平台如下：\nAndroid Windows iOS iOS Jailbreak Web 每个 Package ID ，仅能对应一个 Platform 。即使是同样的名称，同样的打包配置（这种可能性并不高），在不同平台上的两个包，也应该使用不同的 Package ID 。\nServer Info\n这个包要连接的服务器的信息。这些信息可能包括服务器 IP 地址、端口号、服务器名称等等。\n我们可以根据包的不同为它分配不同的服务器地址，这种设计为 分服分区 提供了便利。\n这里仅包含一些通用配置。支付配置信息和版本配置信息都依赖 包管理的两层架构 ，它们的详细配置信息的说明，见相关章节。\n详细的关系示意图：\n1 2digraph package2tiers { 3 graph[rankdir=\u0026#34;LR\u0026#34;,label=\u0026#34;包管理的两层架构（详图）\u0026#34;] 4 node[shape=\u0026#34;box\u0026#34;,fillcolor=\u0026#34;white\u0026#34;,color=\u0026#34;black\u0026#34;] 5 6 channel[label=\u0026#34;Channel ID\u0026#34;, shape=\u0026#34;doubleoctagon\u0026#34;] 7 8 pid[label=\u0026#34;Package ID\u0026#34;] 9 sdk[label=\u0026#34;SDK Config\u0026#34;] 10 plat[label=\u0026#34;Platform\u0026#34;] 11 si[label=\u0026#34;Server Info\u0026#34;] 12 13 { 14 node[shape=\u0026#34;Mdiamond\u0026#34;] 15 Android 16 Windows 17 iOS 18 ij[label=\u0026#34;iOS Jailbreak\u0026#34;] 19 Web 20 } 21 22 channel -\u0026gt; pid 23 pid -\u0026gt; { sdk plat si \u0026#34;更多...\u0026#34; } 24 plat -\u0026gt; { Android Windows iOS ij Web } 25 si -\u0026gt; { \u0026#34;服务器IP\u0026#34; \u0026#34;服务器端口\u0026#34; \u0026#34;服务器名称\u0026#34; } 26} 游戏管理 同时可能有多个游戏处于包管理系统之中，我们使用 Game ID 来处理这种情况。\nGame ID\n用于区分游戏的身份，每个游戏的 Game ID 都是唯一的。\nGame ID 和 Channel ID 既可以是上下级关系，也可以是平级关系。\n对于上下级关系，以 Game ID 作为根节点，在根节点下选择可用的 Channel ID ，再在 Channel ID 之下建立 Package ID 。 Channel ID 是允许重用的。\n1 2digraph relation1 { 3 graph[rankdir=\u0026#34;LR\u0026#34;,label=\u0026#34;上下级关系\u0026#34;] 4 5 gid[label=\u0026#34;Game ID\u0026#34;,shape=\u0026#34;doubleoctagon\u0026#34;] 6 cid[label=\u0026#34;Channel ID\u0026#34;,shape=\u0026#34;box\u0026#34;] 7 pid[label=\u0026#34;Package ID\u0026#34;,shape=\u0026#34;box\u0026#34;] 8 9 gid -\u0026gt; cid -\u0026gt; pid 10} 对于平级关系， Game ID 可以作为 Package ID 的一个属性存在。也就是说在建立 Package ID 的时候，选择其属于哪一个 Game ID 。\n这种关系具体如何选择，取决于数据库如何设计。\n1 2digraph relation2 { 3 4 graph[rankdir=\u0026#34;LR\u0026#34;,label=\u0026#34;平级关系\u0026#34;] 5 6 gid[label=\u0026#34;Game ID\u0026#34;,shape=\u0026#34;doubleoctagon\u0026#34;] 7 cid[label=\u0026#34;Channel ID\u0026#34;,shape=\u0026#34;doubleoctagon\u0026#34;] 8 pid[label=\u0026#34;Package ID\u0026#34;,shape=\u0026#34;box\u0026#34;] 9 10 gid -\u0026gt; pid 11 cid -\u0026gt; pid 12} 配置继承 对于 Channel ID 相同的包来说，它们的大多数配置可能都是相同的。为了避免输入时的冗余和错误，配置中心有一套继承机制。简述如下：\n在 Channel ID 级别进行配置，其中的所有配置自动继承给 Package ID ； Package ID 级别的配置的优先级更高，允许其在继承来的配置的基础上进行覆盖、删除和增加。 安全性和可靠性 可靠性上要考虑的因素：\n所有的游戏客户端启动的时候都会访问包管理中心； 若包管理中心不工作，则所有的游戏客户端都无法正常工作； 游戏的配置要考虑性能和速度。 安全性上要考虑的因素：\n除我们自己的客户端或者处于调试状态外，Config API 不允许直接访问； 需要一些附加的验证信息对客户端的合法性进行验证。 （本文完）\n","date":"2015-11-27","description":"","lastmod":"2015-11-27T08:56:13Z","slug":"mobile-game-framework-package-center","tags":["game","mobile-game-framework"],"title":"手机游戏开发框架设计（一）：配置管理-包管理中心","url":"https://blog.zengrong.net/post/mobile-game-framework-package-center/"},{"categories":["technology"],"content":"手机游戏的开发和上线是个很复杂的过程。无论是独代还是联运，都涉及到大量的不同分发渠道，同一个游戏包的不同名称，不一样的 Icon 和包装，不一样的商品设置，不一样的支付方式设置，分离的登录流程，分服和混服，不同平台包的下载和更新等等内容。这些内容涉及到开发、商务、运营和客服各个岗位，繁杂且容易出错。\n在长期的与这个过程斗争中，我们积累了一些可以通用的系统。这套系统包括客户端的通用模块，服务器登录设计，热更新系统设计，SDK 接入设计，启动公告设计，消息推送系统设计，支付系统设计等等。大部分设计都是需要客户端与数据服务端的结合。我们把模块一一拆开，让它们能够支持多个游戏，还设计了一套统一的后台系统，让它能够管理多个游戏。\n这些设计减少了开发的工作量，也让游戏发行管理更加简单。这套系统包含的主要是客户端开发设计与 API 开发设计，与游戏服务器开发没有太大的关系。这套系统主要为客户端开发和游戏运营服务，因此对各种类型的手机游戏都可以支持。\n因此，把这套系统叫做 手机游戏开发框架 其实并不合适，它不包含游戏服务端的内容，也并不是一套完整的客户端开发框架。但我们一时也找不到更合适的名称了。我们把这些内容写出来，让这些文章作为我们游戏开发工作的一个总结，同时也希望从事手机游戏开发的同行指正。\n我们将撰写一系列文章来说明这套系统的设计。下面是这个系列文章的目录，所有的文章链接将更新在这里：\n手机游戏开发框架设计（一）配置管理-包管理中心 ","date":"2015-11-27","description":"","lastmod":"2015-11-27T08:50:55Z","slug":"mobile-game-framework-start","tags":["game","mobile-game-framework"],"title":"手机游戏开发框架设计（前言+目录）","url":"https://blog.zengrong.net/post/mobile-game-framework-start/"},{"categories":[],"content":"重点文章 Flash \u0026amp; Flex 大全 https://blog.zengrong.net/flashassistant/\n十年学会程序设计 https://blog.zengrong.net/21-days/\n提问的智慧 https://blog.zengrong.net/howtoask/\n","date":"2015-11-15","description":"","lastmod":"2015-11-15T03:32:04Z","slug":"favorite","tags":[],"title":"重点文章","url":"https://blog.zengrong.net/favorite/"},{"categories":["technology"],"content":" 2015-11-29 更新：完善『工具-文档工具』，增加『工具-国际化』，调整『引擎』。 2016-01-19 更新：增加培训类型和层层递进的培训进程。调整技能树。 这半年的时间我一直在带团队。\n从4年前创业开始，我就了解到每个人的兴趣点，学习方式，工作方式和追求终究是不同的。作为 CTO 或者技术 Leader，你可以制定整个公司的技术架构，但无法影响每个人固有的思路。每个人都有自己的短板和长处，每个人都有自己的擅长点和禁区。有些人死守自己的领域不放，有些人则不知该如何成长。如果不能从最根本的层面去改变开发者，那么整个公司的技术方向最终一定会在每个开发者 小小翅膀的扇动 下偏离初始目标。\n我从心理学、社会学和工程学中寻找方法，旁观这一切，参加这一切，推动这一切，改变这一切。有小成功，有大失败。我现在知道，对我的团队而言，深入了解每个人的特点和兴趣，根据具体情况制定每一个人（或一组人）的培训计划和成长线路，才是目前最适合我的方法。\n但我一直觉得缺少一个工具，这个工具需要让整个团队知道自己应该如何努力，他们能通过这个工具找到自己的位置，并以当前位置作为一个起始点，向一个最近的目标奔去，顺着知识的脉络奔向一个个更远的目标，就如拿下一个个城池。在这个过程中，他们收获希望，得到成长，找到方向。\n现在，这个工具已经基本成型了，我会不断改进它。\n[TOC]\n1. 目标 客户端技能树是包含了一整套客户端程序员的技术发展方向和知识点的树状信息点。根据目前的团队情况，受限于我自己的能力，技能树仅仅是面向 手机游戏客户端程序员 的。\n我们使用客户端技能树这个工具来达成如下目标：\n1.1 职级确定 在入职时根据技能树的掌握点来判断能力级别，确定职级。 指导职级提升。 1.2 持续培训 让程序员明确自己的学习目标，并有一套完整的培训系统来保证程序员能力的持续提升。\n1.3 熟悉开发框架和工具 每个人对现有的开发框架的理解程度并不相同，我使用技能树来规范每一个知识点。确保每一个人对重要的知识点的理解达到相同的程度。\n1.4 兴(ji)趣(xue)培养 若没有兴趣，程序员不可能持续提高自己的能力，也不可能持续高强度的产出。\n2. 维度 2.1 兴趣组 根据程序员自己的兴趣和特点分配兴趣组。兴趣组是与项目组平行的单位，一个人同时只能处于一个兴趣组，但允许根据自己的兴趣调整兴趣组。调整兴趣组之前，必须对前兴趣组中得到的技能进行评估。\n2.2 技能点 技能树上包含的所有技能点，都可以被选择。一旦选择了一个方向的技能点，就必须将与这个技术点关联的同一树枝的所有技能点修炼到 熟悉 级别才能去往其他树枝。\n2.3 成长链 技能点的起始选择决定了成长链。兴趣组的选择决定了成长链。程序员个人能力的高低影响了成长链。\n2.4 技能掌握程度 我把对技能点的掌握情况分成下面5个级别：\n知道\n听说过这门技术的名称，知道相关概念； 了解\n知道这个技术是如何工作的，知道具体流程，没有进行过完整的操作，或仅进行过部分测试； 熟悉\n可以使用这门技术制作出完整的产品； 掌握\n了解技术的每个细节，被问到这门技术时，可以使用简洁的语言概述其梗概，陈述其优缺点（5分钟）；也可以使用复杂的语言描述其细节（1小时）； 精通\n知道这门技术是如何被实现出来的，了解这门技术的所有周边技术。 技能树质量核准基于学习者的知识掌握程度。学习者在自己选择的成长链中，对于每个知识点都应该至少达到 熟悉 程度，对于重要的知识点必须达到 掌握 程度。对于 精通 这个级别，很难有一个确切的范畴，必须在 掌握 的基础上不断深挖。不要轻言 精通 ，你 掌握 得越多，就发现自己 知道 得越少。与我而言，精通的技能有3、4个吧，比如 吃饭饭，睡觉觉，打豆豆 （我不是豆豆）。\n当然，如果你 正在找工作 ，在简历上可以稍稍夸张一点，比如这个：\n精通汇编、C/C++、Linux网络编程、JAVA EE 精通Perl、Bash、Python等脚本语言 精通Windows驱动开发、单片机开发 精通网站开发、HTML、CSS3、JavaScript、PHP 精通游戏开发、cocos2d-x、Unity 精通 App 开发、iOS开发、安卓开发。 2.5 程序员水平分级 What Level Programmer Are You? 这文章将程序员的级别分成了10个级别（神不是一个级别，而是个信仰）。对于程序员来说，应该了解自己处于什么级别。\nLevel 1，阅读和键入 Level 2，脚本小子 Level 3，图书管理员 Level 4，“对象.方法”实践者 Level 5，多范式实践者 Level 6，初级构架师 Level 7，资深构架师 Level 8，诠释者 Level 9，函数式编程者 Level 10，面向语言设计师 神级，计算机科学家 在我看来，大部分程序员都处于 Level 3 图书管理员 级别，也就是 “API 程序员” 的级别。糟糕的是许多人认为这是最终级别。多数程序员无法升级并不是因为本身的能力问题，而是看待问题的方式： 优秀的程序员和一般的程序员差别在哪？ 。\n上面文章的中文翻译：程序员水平分级，你属于哪一级？\n3. 方法 我设计了一套完善的方法支持技能树的实施。\n3.1 质量控制系统 要保证每个开发者对技能点的理解都达到相同的高度，必须严格确保技能点的实施质量，以及培训的质量。设计中有这样几个级别来控制质量：\n1digraph qualitycontrol { 2 3 graph[label=\u0026#34;质量控制与级别提升\u0026#34;] 4 gatekeeper[label=\u0026#34;守门员\\n(Gatekeeper)\u0026#34;,shape=\u0026#34;tripleoctagon\u0026#34;] 5 collaborator[label=\u0026#34;合作者\\n(Collaborator)\u0026#34;,shape=\u0026#34;doubleoctagon\u0026#34;] 6 executor[label=\u0026#34;执行者\\n(Executor)\u0026#34;,shape=\u0026#34;octagon\u0026#34;] 7 student[label=\u0026#34;学习者\\n(Student)\u0026#34;,shape=\u0026#34;circle\u0026#34;] 8 9 gatekeeper -\u0026gt; collaborator[label=\u0026#34;把关\u0026#34;] 10 collaborator -\u0026gt; executor[label=\u0026#34;指定\u0026#34;] 11 executor -\u0026gt; student[label=\u0026#34;培训和执行\u0026#34;] 12 13 student -\u0026gt; executor -\u0026gt; collaborator -\u0026gt; gatekeeper[style=\u0026#34;dashed\u0026#34;,taillabel=\u0026#34;提升\u0026#34;] 14} 3.1.1 守门员(Gatekeeper) 守门员是培训质量和代码质量的最终把关者，整个技能树的实施质量取决于守门员的能力。整个系统设置 1~3 个守门员。\n3.1.2 合作者(Collaborator) 在技能树繁茂的枝叶（知识领域）中，不是所有的人都能对整个体系完整深入地进行了解。在团队的所有的程序员中，总有人对某些枝叶特别熟悉，这些人就是合作者。\n合作者是某个领域的专家，他们需要根据技能树中的枝叶制定这个领域的培训计划，需要审核具体领域实施过程的代码，并保证代码的质量，保证培训计划的完整深入。\n3.1.3 执行者(Executor) 执行者可以是合作者，也可以由合作者来指定。执行者负责具体的代码编写和培训授课。合作者负责执行者的培训计划的质量。合作者是执行者的导师，执行者是合作者的接班人。\n在技能树的实施过程中，执行者负责具体的培训授课，以及具体技能点的质量监控，是整个技能树实施中的 最大受益者 ，是整个技能树实施过程中 成长最快的人 。\n3.1.4 学习者(Student) 在一个技能点的实施过程中，学习者需要面对培训做出反馈，将培训中学习到的知识用于工作中。学习者是整个技能树实施的 最直接的受益者 。\n学习者的身份不是绝对的。在某个知识领域的学习者，可能是另一个知识领域的执行者、合作者，甚至守门员。这取决于学习者的 成长链 。\n3.2 培训和执行 技能掌握和 Level 提升需要完善的培训计划和强大的执行力。培训计划的制定流程如下：\n守门员确定合作者的知识领域； 合作者基于现有项目选择开始点； 合作者选择执行者，共同制定详细的培训文案和计划； 在执行开始之前，使用培训来统一执行标准，由执行者进行培训； 在执行阶段，执行者、合作者需要通过 Code review 和其他方式确保执行效果； 合作者确定该领域培训成功，总结会议； 进入该知识领域中的下一个技能点，继续上述流程。 对于技能点的知识领域的选择，必须遵循以下三个原则：\n尽量在现有正在进行的项目中选择培训内容，确定计划起始点。现有项目的框架、功能、公用库、流程等等都可以起始点； 若1不满足，则应考虑现有项目的开发方向，基于现有项目的升级版本确定计划的起始点； 若2不满足，则起始点选择必须基于可实际操作的项目，不允许空中楼阁，不允许只谈方法没有实战；不允许试验性的无意义项目；所有选择的项目必须有实际意义；选择的项目，要么可以直接作为一个新项目的起点，要么可以 作为旧项目升级的方向。 3.3 培训类型 技能树的培训分为三类：\n3.3.1 所有开发者都需要掌握的 例如 markdown 和 sphinx 这类培训。这类技能非常简单和容易掌握，而且在今后的工作甚至生活中，可以为每个开发者带来好处。这类培训会安排详细的练习；\n3.3.2 所有人都需要了解，但不需要完全掌握的 程序员分为两种，一种喜欢偷懒，偏爱使用各种工具或者自己开发工具偷懒，另一种则喜欢实现功能，按部就班地开发。\ncmake 这类培训就是能让这两种程序员都达到自己目的的培训。\n对于第一种程序员，听完课程之后就会自行去搜索更深的信息，进行了解；而对于第二种程序员，培训需要达到的效果是他在下次看到 CMakeLists.txt 的时候不发怵，碰到 CMake 错误的时候能知道怎么找到相关信息解决即可。这类培训不会安排练习，或者安排不强制的练习。\n3.3.3 所有开发者都要严格熟悉的 这就是和项目的开发流程、开发框架相关的内容。对于这些内容，许多开发者并不熟悉，由于没有统一的培训，程序员们完全是按照自己的本能在开发，这导致了代码风格不统一，代码质量不一致，重复早轮子等许多问题。这部分的培训，是和 入职培训 类似的内容，目的就在于解决上面提到的问题。\nlerna 框架的系列培训，就属于这类培训。这类培训会严格安排联系并检查，保证学习者对这些内容的完全掌握。\n3.4 层层递进的培训进程 当学习者的水平并不在同一个层级的时候，技能树培训系统只能进行 “浅尝即止” 的培训。只有将所有学习者的水平都拉高到平均值之后，才可能进行更深入的内容培训。到那个时候，培训不应该是 “大锅饭” 式的，而是可能多个主题同时开讲。到那个时候，每个学习者也都会了解了自己的特点，确定了自己的成长链，并安排好了自己的成长方向。\n3.5 现有范例 在 HTML5 Game 项目中，我对上面的执行流程做了一次完整的实践。\n作为守门员/合作者/执行者，面对大家对 git 操作不熟悉，对 Code Review 流程不理解的现状，我制定了这样的培训计划：在开发 HTML5 Game 这个实际项目时，学习使用 Git Flow 流程以及严格执行 Code Review； 在 HTML5 Game 项目开发组会议中，培训 Git Flow 流程，规定每个 Feature 若有2个以上的开发者，每次推送提交必须互相 Review ，推送到 develop 分支则必须给我 Review ； 在实际的开发阶段，严格按照 Git Flow 和 Code Review 流程执行； 执行期间碰到的问题，在每次项目会议上进行补充培训。 这个实践的结果是成功的，所有人都能感到代码质量的提升。有一位成员中途转到了其他项目，他主动在转到的项目中推广了这套流程。这说明学习者们对这套流程是认同的，对流程整体是清晰的。\n4. 技能树 这个技能树的完成度为 87.53% 。主干不会有太大变化了， 需要补充的是枝叶。\n技能树是 SVG 图像，若无法显示请换用 Chrome/Firefox/Safari 。\n在查看 SVG 大图的条件下，部分技能点是拥有链接的（手型指针）。\n查看大图\n（全文完）\n","date":"2015-11-13","description":"","lastmod":"2015-11-13T03:24:03Z","slug":"game-client-skill-tree","tags":["study","game","management"],"title":"客户端技能树","url":"https://blog.zengrong.net/post/game-client-skill-tree/"},{"categories":["technology"],"content":" 2020-02-07 更新： 调整链接。 2016-11-08 更新： 修改一些文字错误。 2015-12-01 更新： 加入 Sphinx 插件 设置说明。 在 Graphviz 简易教程 中，我介绍了一点 Graphviz 的知识。下面的内容则是我在使用中积累的一些和工具有关的内容。\n通用配置 使用下面三个关键字可以指定图片、节点和线段的默认设置。\n1// 影响图片整张图片的配置 2// 左右方向，图片背景色为红色 3// 也可以用于 subgraph 4graph[rankdir=\u0026#34;LR\u0026#34;,bgcolor=\u0026#34;red\u0026#34;]; 5 6// 影响所有节点 7node[shape=\u0026#34;box\u0026#34;]; 8 9// 影响所有线条和箭头 10edge[style=\u0026#34;dashed\u0026#34;]; 指定多个节点 要想一次制定特定的多个节点的属性，可以这样写：\n1{ node[fillcolor=\u0026#34;red\u0026#34;]; node1; node2; node3; } 也可以换行写：\n1{ 2\tnode[fillcolor=\u0026#34;red\u0026#34;]; 3\tnode1; node2; 4\tnode3; 5} 中文乱码问题 若渲染出的图片中出现乱码，检查两项：\n文件编码需要使用 utf-8； 指定字体名称。 在 Mac OS X 上，我没有指定字体名称，中文工作得很好。\n但在 Windows 上，我的渲染出现了乱码，于是我在 dot 源码中指定了字体名称：\n1// 影响图片级别的字体 2graph[fontname=\u0026#34;Microsoft YaHei\u0026#34;]; 3// 影响 node 中的文字字体 4node[fontname=\u0026#34;Microsoft YaHei\u0026#34;]; 5// 影响箭头或线条上的文字字体 6edge[fontname=\u0026#34;Microsoft YaHei\u0026#34;]; 但是在 dot 源码中制定字体会影响其他 OS 下的表现（例如在 OS X 上没有微软雅黑），因此应该在编译的时候通过参数指定字体。\n在编译的时候指定字体：\n1dot -Tpng -Gfontname=SimSun -Nfontname=SimSun -Efontname=SimSun graph.dot Sphinx 插件 Sphinx 是一个优秀的文档工具。可以直接在其中使用 Graphviz 支持。\n需要注意的是，如果在 Windows 上使用 Sphinx ，必须使用参数指定具体的字体，否则就会出现乱码。\n这些配置直接写入 conf.py 即可。\n1# 启用 graphviz 配置 2extensions = [ \u0026#39;sphinx.ext.graphviz\u0026#39; ] 3 4# 为了不影响其他操作系统，需要判断操作系统 5if sys.platform == \u0026#39;win32\u0026#39;: 6 graphviz_dot_args = [\u0026#39;-Gfontname=Simsun\u0026#39;, \u0026#39;-Efontname=Simsun\u0026#39;, \u0026#39;-Nfontname=Simsun\u0026#39;] Vim 插件 在 Graphviz 简易教程 中，我建议使用 Eclipse 等 IDE 配合插件来编辑 dot 源码。实际上，我自己是使用 wmgraphviz.vim 来编辑 dot 的。默认情况下，wmgraphviz.vim 在 windows 下使用会有问题，主要是由于在它的编译代码中使用了 tee 这个 windows 批处理并不包含的命令。\n许多 unix 移植工具中都包含 tee 工具（可参考 这里 ），由于只需要 tee ，我们可以使用 tee.bat ，把这个命令放在 path 路径中即可。\n当然也可以直接修改 wmgraphviz.vim 的源码，修改 ftplugin/dot.vim 中的这一行：\n1let cmd = \u0026#39;!(\u0026#39;.a:tool.\u0026#39; -O -T\u0026#39;.a:output.\u0026#39; \u0026#39;.g:WMGraphviz_shelloptions.\u0026#39; \u0026#39;.shellescape(expand(\u0026#39;%:p\u0026#39;)).\u0026#39; 2\u0026gt;\u0026amp;1) \u0026gt; _ \u0026amp;\u0026amp; type _\u0026amp;\u0026amp;type _\u0026gt;\u0026gt;\u0026#39;.shellescape(expand(\u0026#39;%:p:r\u0026#39;).\u0026#39;.log\u0026#39;) 原理在这里有介绍： How to display the output text in the dos command line while redirecting 。\n对于上面提到的中文问题，可以直接编辑配置文件：\n1if has(\u0026#39;win32\u0026#39;) 2 let g:WMGraphviz_shelloptions=\u0026#34;-Gfontname=SimSun -Nfontname=SimSun -Efontname=SimSun\u0026#34; 3endif 自动刷新的图像查看软件 用 Vim 配合自动刷新的看图软件即可实现一个 Graphviz 编辑器，把屏幕分成两半，一般是 Vim，一半是看图软件，Vim 这边保存之后使用 wmgraphviz.vim 提供的快捷键输出图像，然后另一半的看图软件自动刷新实现预览。\nMac 下的 graphviz 官方 pkg 安装包自带一个 Graphviz.app 工具，安装后自动与扩展名 .gv 关联，双击即可打开预览，在修改 .gv 文件的时候，它将自动刷新； Mac 下还可以使用 Finder 的预览功能，或者使用 Xee3； Windows 下可使用资源管理器的预览窗格，或者 IrfanView 的 Shirt+R 快捷键手动刷新，或者 JPEGView 直接支持自动刷新。 标记语言支持 我的 Fenced Code Extra for Python-Markdown 支持在 Markdown 中嵌入 Graphviz 支持。\nSphinx 也支持 Graphviz 插件。\n两篇很完整的教程 Graphviz的使用及中文乱码问题 DOT语言 全文完 ","date":"2015-10-29","description":"","lastmod":"2020-02-07T04:11:51Z","slug":"graphviz-tool-guide","tags":["graphic","graphviz"],"title":"Graphviz 工具教程","url":"https://blog.zengrong.net/post/graphviz-tool-guide/"},{"categories":["technology"],"content":" 2015-10-19 更新： 加入 npm 和 gem 2015-10-20 更新： 加入 pypi 2016-02-21 更新： 加入 msys2 2016-07-13 更新： 加入 pypi 的豆瓣 https 源 2017-04-17 更新： 加入 nvm 2021-01-05 更新： 加入 conda/Anaconda/Miniconda 2021-11-14 更新： 加入腾讯云和阿里云的部分镜像地址 由于众所周知的原因，许多开源软件的源站点在国内访问不顺，因此就有了镜像站这东东。这段时间我在各种开源技术中跳来跳去，也就积攒了不少镜像站，下面是个梳理。\nAndroid Android SDK Manager 在国内基本上完全废了。好在我们有腾讯。腾讯的 bugly 团队制作了一个 Android 镜像 ，下载速度贼快。不但有 Android SDK，还有 Eclipst ADT 和 Android Studio ，简直是业界良心。\n更多的 Android SDK 镜像（甚至下载安装包）可以在这里找到： AndroidDevTools 。\n需要 ASOP 镜像可以去 清华大学的站点 。\nHomebrew 若要更新 Homebrew 的源，使用 sudo brew update 可能会非常慢，因为这需要更新一个位于 /usr/local 下的 git repostory ，而这个仓库是指向 https://github.com/Homebrew/homebrew 的。\n所以我们可以把这个 repostory 的 remote 指向清华大学的 Homebrew 镜像源：\n1cd /usr/local 2git remote set-url origin git://mirrors.tuna.tsinghua.edu.cn/homebrew.git 3sudo brew update 也可以在上面替换 origin 的时候使用中国科学技术大学的源： git://mirrors.ustc.edu.cn/homebrew.git\n更详细的说明可以看这里： http://mirrors.tuna.tsinghua.edu.cn/help/#homebrew\nRubyGems 这个我在 离线安装 compass 中曾经提到过，使用 Ruby China 提供的 RubyGems 镜像淘宝提供的 RubyGems 镜像 。\n注意，如果你使用 Mac OS X 并升级到了 EI Caption (10.11) ，那么很可能先前安装的包已经不能使用，而且 gem 也不能安装了，会出现这样的提示：\n1? 20151019 git:(master) ? gem install compass 2ERROR: While executing gem ... (Gem::FilePermissionError) 3 You don\u0026#39;t have write permissions for the /Library/Ruby/Gems/2.0.0 directory. 4 5? 20151019 git:(master) ? sudo gem install compass 6ERROR: While executing gem ... (Errno::EPERM) 7 Operation not permitted - /usr/bin/compass 这是因为苹果收紧了安全政策，不能在 /usr/local 之外的文件夹下写入文件。\n可以这样做：\n1sudo gem install -n /usr/local/bin compass 也可以使用 brew 安装一个 ruby 用来替换系统自带的。因为 brew 默认会把程序安装在 /usr/local 中，这就没有权限问题了。\nNode.js 如果使用默认的源，npm 甚至启动都会慢成狗。\n淘宝的 TAONPM 的速度相当快，值得设置成默认的 regisrty 。\n临时使用：\n1npm install \u0026lt;packagename\u0026gt; --registry https://registry.npm.taobao.org/ 永久使用（这会写入 ~/.npmrc )：\n1npm set registry https://registry.npm.taobao.org 淘宝还提供了 cnpm 用来替代 npm，怎么用随你了。\n如果使用 nvm(Node Version Manager) 进行 node 版本切换，那么可以设置环境变量，修改 nvm 的安装源：\n1export NVM_NODEJS_ORG_MIRROR=https://npm.taobao.org/mirrors/node 当然也可以使用腾讯的镜像：\n1npm config set registry https://mirrors.cloud.tencent.com/npm/ pypi pypi 虽然在国内没被封掉，但速度也够慢的。我们可以使用下面两个公司提供的镜像源：\n豆瓣 豆瓣(HTTPS) V2EX 豆瓣和 V2EX 都是采用 python 开发的，因此它的源应该是比较稳定的。\n以安装我的 wpcmd 工具为例：\n临时使用：\n1pip install wpcmd -i http://pypi.doubanio.com/simple 注意一定要注意加上后面的 /simple ！\n永久使用：\n修改 ~/.pip/pip.conf (Linux) 或 %HOME%\\pip\\pip.ini (Windows) ，写入：\n1[global] 2index-url = http://pypi.doubanio.com/simple 注意，上面的配置文件路径已经不建议使用， 新版本的 pip 建议使用下面的路径：\nUnix/Linux 1$HOME/.config/pip/pip.conf macOS 1$HOME/Library/Application Support/pip/pip.conf 2# 若上面的路径不存在，则使用 Unix/Linux 路径 Windows 1%APPDATA%\\pip\\pip.ini 更详细的介绍看这里：Config file 。\ntrusted-host 如果你使用的 pip 版本是6或者更低，在使用 http 协议的 pypi 站点时，可能会出现这样的提醒（黄色）：\nThis repository located at pypi.doubanio.com is not a trusted host, if this repository is available via HTTPS it is recommend to use HTTPS instead, otherwise you may silence this warning with '--trusted-host pypi.doubanio.com'.\n接着可能还有一段红色提醒：\nDEPRECATION: Implicitly allowing locations which are not hosted at a secure origin is deprecated and will require the use of --trusted-host in the future.\n而如果你使用的版本是 7 ，则会提示直接找不到你要的包：\nCould not find a version that satisfies the requirement wpcmd (from versions: ) No matching distribution found for rookout\n2016-07-13 更新：\n豆瓣已经启用了新的支持 https 的源 https://pypi.doubanio.com ，使用这个源可以不必设置 trused-host 选项。\n这是因为豆瓣的源并不是 https 协议的，你可以修改前面所述的配置文件为：\n1[global] 2index-url = http://pypi.douban.com/simple 3trusted-host = pypi.douban.com 当然，你也可以使用支持 https 协议的镜像源，例如下面两个都挺快的：\n清华大学 pypi 镜像源 中国科学技术大学 pypi 镜像源 2021-11-14 更新：\n升级 pip 到最新的版本 (\u0026gt;=10.0.0) 后可以直接用 pip 配置：\n1pip install pip -U 2# 使用腾讯云的镜像 3pip config set global.index-url https://mirrors.cloud.tencent.com/pypi/simple 4 5# 或者使用阿里云的镜像 6pip config set global.index-url https://mirrors.aliyun.com/pypi/simple conda/Anaconda/Miniconda Anaconda 是一个用于科学计算的 Python 发行版，支持 Linux, Mac, Windows, 包含了众多流行的科学计算、数据分析的 Python 包。\nAnaconda 安装包： https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/\nMiniconda 是一个 Anaconda 的轻量级替代，默认只包含了 python 和 conda，但是可以通过 pip 和 conda 来安装所需要的包。\nMiniconda 安装包： https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/\n修改 .condarc 加入清华源：\n1channels: 2 - defaults 3show_channel_urls: true 4channel_alias: https://mirrors.tuna.tsinghua.edu.cn/anaconda 5default_channels: 6 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main 7 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free 8 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r 9 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro 10 - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2 11custom_channels: 12 conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 13 msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 14 bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 15 menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 16 pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 17 simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud 也可以考虑上海交通大学的源： https://mirrors.sjtug.sjtu.edu.cn/anaconda/\nconda 和 JupyterLab 干净安装与最小使用\nMSYS2 要了解 MSYS2 ，可参考这篇文章 Cygwin 与 MinGW/MSYS/MSYS2，如何选择？ 。\n安装 MSYS2 之后，编辑 pacman 的配置文件：\n编辑 /etc/pacman.d/mirrorlist.mingw32 ，在文件开头添加：\n# 中国科学技术大学 Server = http://mirrors.ustc.edu.cn/msys2/REPOS/MINGW/i686 # 北京理工大学 Server = http://mirror.bit.edu.cn/msys2/REPOS/MINGW/i686 编辑 /etc/pacman.d/mirrorlist.mingw64 ，在文件开头添加：\n# 中国科学技术大学 Server = http://mirrors.ustc.edu.cn/msys2/REPOS/MINGW/x86_64 # 北京理工大学 Server = http://mirror.bit.edu.cn/msys2/REPOS/MINGW/x86_64 编辑 /etc/pacman.d/mirrorlist.msys ，在文件开头添加：\n# 中国科学技术大学 Server = http://mirrors.ustc.edu.cn/msys2/REPOS/MSYS2/$arch # 北京理工大学 Server = http://mirror.bit.edu.cn/msys2/REPOS/MSYS2/$arch 更多说明见： MSYS2 镜像使用帮助\n其它 大学的镜像站点也提供了许多其他的开源项目的镜像源，上面提到的 npm ， cygwin ， pypi 在几个大学镜像站也都有提供。我在这里列出几个著名的镜像站点列表（按推荐程度排序），如果找不到自己想要的源可以上去瞅瞅。\n需要注意的是，大学提供的镜像站大多数为教育网出口，建议根据地理位置选择离自己较近的站点。\n清华大学 TUNA 镜像源 网易开源镜像站 中国科学技术大学 搜狐开源镜像站 北京交通大学镜像站 浙江大学开源镜像站（特色配置生成器） 厦门大学信息与网络中心开源软件镜像服务 华中科技大学开源镜像站 兰州大学开源社区镜像站 上海交通大学 西南大学开源协会镜像站 东北大学开源镜像 大连东软信息学院开源镜像站 重庆大学 北京理工大学开源软件镜像服务 LUPA 镜像 中山大学 参考文章 Installing sass on OS X 10.11 (El Capitan) 快速搭建 Node.js 开发环境以及加速 npm How to fix Error: ENOENT lstat npm when trying to install modules （全文完）\n","date":"2015-10-17","description":"","lastmod":"2021-01-05T09:37:07Z","slug":"open-source-mirror-site","tags":["ruby","python","javascript","android"],"title":"常用开源镜像站整理","url":"https://blog.zengrong.net/post/open-source-mirror-site/"},{"categories":["impressions"],"content":" 开始：2015-09-26 读完：2015-09-27 书籍介绍： 编号1520 这是一本儿童小说，买给女儿。女儿热爱读书，因此我打算在还能控制她的读书范围的时候，认真读一读她要读的每本书。\n这个感人的故事讲述了一个小男孩在8个月的时间里独自求生的故事。同时，这也是一个包含了友谊、亲情、冒险、平等、执着和坚持的扣人心弦的故事，我认为这些因素是一篇好的儿童读物不可缺少的。\n小说一开头的细腻的感情描写，将孩子对其父亲的眷恋，对未来的渴望，对独自一人的害怕和向往，恰如其分地表达出来。后面独自生活被偷走来复枪，被蜜蜂追险些死掉，教阿天读《鲁滨逊漂流记》，和阿天猎杀母熊，进入印第安部落，和朋友分别，独自在冬天生活，坚持到父母到来，一系列的故事让人不舍释卷。我仿佛又回到9岁那年，捧着母亲拿回的《汤姆索亚历险记》，趴在床上就着墨香读书的时候。\n有趣的是，虽然女儿读过《鲁滨逊漂流记》，但她还无法理解书中借主人公之口表达出的对《鲁滨逊漂流记》的质疑和反对。“人怎么能拥有土地？”对于女儿而言，她能理解她拥有的芭比娃娃，却不能理解“拥有土地”的概念，自然也无法理解作者所要表达的韵味。我故意没有去引导她，也许这种概念晚一点了解更好。\n或许对于女儿来说，这只是一本书，一个故事，而对于我来说，这是个回忆。到现在我脑海里还经常浮现出茫茫森林中的那个孤独小屋，一个少年背着弓箭，拎着一只兔子耳朵摇哇摇，不时低下头辨认着自己或者别的印第安人留下的记号，一只耳朵上掉了毛的老狗在前面跑，跛着一只脚。\n（全文完）\n","date":"2015-09-30","description":"","lastmod":"2015-09-30T02:49:44Z","slug":"the-sign-of-the-beaver","tags":["life","reading"],"title":"【读书笔记】海狸的记号","url":"https://blog.zengrong.net/post/the-sign-of-the-beaver/"},{"categories":["technology"],"content":"今天无意中看到一个在 Python 将字符串倒序的有趣方法：\n1print(\u0026#39;abc\u0026#39;[::-1]) 2# cba 没弄清楚 [::] 的含义是什么，又不知道在 Python 文档中哪里能找到这个 [::] 的用法说明，于是开始 Google 搜索。\n但 Google 默认是直接忽略掉搜索框中的特殊符号的。但 SymbolHound 可以。\n这可是专门针对程序员的搜索引擎：\nSymbolHound is a search engine that doesn't ignore special characters. This means you can easily search for symbols like \u0026amp;, %, and π. We hope SymbolHound will help programmers find information about their chosen languages and frameworks more easily.\n有了 SymbolHound ，就很容易搜索到 [::] 的出处了，原来它叫做：Extends Slices ，是在 Python2.3 版本加入的。\nStackoverflow 的解释更加好懂：reverse a string in Python 。\n（全文完）\n","date":"2015-09-25","description":"","lastmod":"2015-09-25T03:56:49Z","slug":"search-symbol-in-search-engine","tags":["python"],"title":"在搜索引擎中搜索特殊字符","url":"https://blog.zengrong.net/post/search-symbol-in-search-engine/"},{"categories":["technology"],"content":"前几天写了一篇 Javascript 的 64bit Int 支持，列举了一些在 Javascript 中支持 64bit 数值的已有方法。\n其实，写那篇是为了在 egret 中支持 64bit 数值，原因么，上一篇有讲。\n由于 egret 使用的是 TypeScript ，我基于 node-int64 翻译了一个 TypeScript 版本Int64.ts ，方便伸手党。同时为了方便和服务端大爷通信，又继承 egert.ByteArray 写了个 Buffer.ts 。\nnote-int64 采用的是 node 的 Buffer 来保存 64bit 数字信息。我给改成了使用 egret.ByteArray 。后来为了更加通用，又改成了直接使用 Array。\nBuffer.ts 中则仅仅实现了 readInt64 和 writeInt64，Unsigned 版本直接调用这两个方法。\n这两个文件都在 gist 上，请科学上网。\n给一段测试代码：\n1var i64:Int64 = new Int64(0x1020304050607); 2var buf:Buffer = new Buffer(); 3buf.writeInt64(i64); 4buf.writeUnsignedInt64(i64.toNumber()); 5buf.position = 0; 6for(var i:number=0;i\u0026lt;buf.length;i++) 7{ 8\tconsole.log(buf.readByte()); 9} 10buf.position = 0; 11console.log(buf.readInt64()); 12console.log(buf.readUnsignedInt64()); 13// 1 14// 2 15// 3 16// 4 17// 5 18// 6 19// 7 20// 0 21// 1 22// 2 23// 3 24// 4 25// 5 26// 6 27// 7 28// 283686952306183 29// 283686952306183 （全文完）\n","date":"2015-09-22","description":"","lastmod":"2015-09-22T03:43:21Z","slug":"int64-and-buffer-ts-version-for-egret","tags":["typescript","javascript","egret"],"title":"Int64.ts and Buffer.ts for Egret","url":"https://blog.zengrong.net/post/int64-and-buffer-ts-version-for-egret/"},{"categories":["technology"],"content":"我准备在一个图片站上抓点图，但发现它启用了 DDos 保护。站点会首先显示一段文本：\nThis process is automatic. Your browser will redirect to your requested content shortly.\n要求你等待几秒钟检测浏览器，然后通过 302 重定向跳转到正确的页面（当然，这个正确的页面地址依然没变）。\n等待的过程表现在浏览器上是这样的：\n这个保护的详细说明在这里： CloudFlare advanced DDoS protection 。\n让我们看看怎么来解决这个问题。\n源码 打开页面看源码，可以看到这样的 javascript 代码：\n1(function(){ 2var a = function() {try{return !!window.addEventListener} catch(e) {return !1} }, 3b = function(b, c) {a() ? document.addEventListener(\u0026#34;DOMContentLoaded\u0026#34;, b, c) : document.attachEvent(\u0026#34;onreadystatechange\u0026#34;, b)}; 4b(function(){ 5 var a = document.getElementById(\u0026#39;cf-content\u0026#39;);a.style.display = \u0026#39;block\u0026#39;; 6 setTimeout(function(){ 7\tvar t,r,a,f, cgksoUW={\u0026#34;bRM\u0026#34;:+((+!![]+[])+(!+[]+!![]))}; 8\tt = document.createElement(\u0026#39;div\u0026#39;); 9\tt.innerHTML=\u0026#34;\u0026lt;a href=\u0026#39;/\u0026#39;\u0026gt;x\u0026lt;/a\u0026gt;\u0026#34;; 10\tt = t.firstChild.href;r = t.match(/https?:\\/\\//)[0]; 11\tt = t.substr(r.length); t = t.substr(0,t.length-1); 12\ta = document.getElementById(\u0026#39;jschl-answer\u0026#39;); 13\tf = document.getElementById(\u0026#39;challenge-form\u0026#39;); 14\t;cgksoUW.bRM-=+((!+[]+!![]+!![]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]));cgksoUW.bRM*=+((!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]));cgksoUW.bRM*=+((!+[]+!![]+!![]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]));cgksoUW.bRM+=+((!+[]+!![]+[])+(!+[]+!![]));cgksoUW.bRM*=+((!+[]+!![]+!![]+[])+(+[]));a.value = parseInt(cgksoUW.bRM, 10) + t.length; 15\tf.submit(); 16 }, 5000); 17}, false); 18})(); 这是 JS 中出现的几个相关 Element：\n1\u0026lt;p data-translate=\u0026#34;process_is_automatic\u0026#34;\u0026gt;This process is automatic. Your browser will redirect to your requested content shortly.\u0026lt;/p\u0026gt; 2\u0026lt;p data-translate=\u0026#34;allow_5_secs\u0026#34;\u0026gt;Please allow up to 5 seconds\u0026amp;hellip;\u0026lt;/p\u0026gt; 3\u0026lt;/div\u0026gt; 4\u0026lt;form id=\u0026#34;challenge-form\u0026#34; action=\u0026#34;/cdn-cgi/l/chk_jschl\u0026#34; method=\u0026#34;get\u0026#34;\u0026gt; 5\u0026lt;input type=\u0026#34;hidden\u0026#34; name=\u0026#34;jschl_vc\u0026#34; value=\u0026#34;c841963d655c6bb040c4ef02fab8d5a3\u0026#34;/\u0026gt; 6\u0026lt;input type=\u0026#34;hidden\u0026#34; name=\u0026#34;pass\u0026#34; value=\u0026#34;1441270501.274-WM96e3sQx+\u0026#34;/\u0026gt; 7\u0026lt;input type=\u0026#34;hidden\u0026#34; id=\u0026#34;jschl-answer\u0026#34; name=\u0026#34;jschl_answer\u0026#34;/\u0026gt; 8\u0026lt;/form\u0026gt; 分析 上面代码的作用，主要是原来判断访问站点的客户端是否是一个真实的浏览器。\n访问网站主页的时候，首先得到的是一个 503 错误，显示上面的页面内容。然后等待 5 秒，让用户看清楚提示信息，然后再根据得到的随机值东拼西凑计算出另一个值，写入 jschl-answer 中，最后通过 challenge-form 这个表单提交。\n提交成功之后， /cdn-cgi/l/chk_jschl 会使用 302 重定向返回真实的网页内容。\ncgksoUW 是一个随机的名称，pass 那个 input 中包含的值也是一个变化的值。每次进入这个页面，这两个值都会变化。\n那么，pass 的值在提交的时候到底是如何计算出来的？\n从 f = document.getElementById('challenge-form'); 下面的那段代码可以看出，这个计算无非是对 cgksoUW.bRM 进行一些加乘运算。然后再转成一个 Int 进行提交。关键在于那些看起来莫名其妙的加号、括号和方括号的集合的含义为何？\n这是一个简单的障眼法。\n让我们看看 cgksoUW.bRM 的初始值，这个表达式的结果等于数字 12 ：\n1+((+!![]+[])+(!+[]+!![])) 我们把上面的代码拆成几个子表达式，就好理解了。\n首先是第一个嵌套括号中的： (+!![]+[])，打开浏览器的 console ,在其中输入 +!![] 回车，可以得到数字 1 。\n先不管哪个加号，单独看 !![] 的值，它是布尔值 true 。这是因为 [] 返回的是一个有效的 Array，代表 true ，两次取反，依然是 true 。\n而左边的那个加号由于没有提供左操作数，因此它的含义是正数，这里做了一次数字转换，自动把 true 转换成了 1 。\n接着往后算，后面是个加号，然后是个 [] 。空的数组，默认是作为字符串处理的。[].toString() 的值为空字符串。前面的 1 加上后面的空字符串，得到一个字符串 \u0026quot;1\u0026quot; 。\n知道了原理，可以使用同样的方式算出第二个嵌套括号中的值为数字 2 。\n接下来是字符串 \u0026quot;1\u0026quot; 加上数字 2，得到字符串 \u0026quot;12\u0026quot; 。\n最左边的加号又做了一次转换，将字符串 \u0026quot;12\u0026quot; 转换成了数字 12 。\n所以，这一段看似乱码的代码，只是为了获取一些随机的数字，便于和服务器验证罢了。我判断这些数值都是有意义的，而且和服务器时间关联，用来判断访问网站的是不是标准的浏览器。\n选择 分析完之后，就要进行技术的选择了。\n写爬虫自然是首选 Python 。开始，我准备使用 requests 来模拟请求，使用 BeautifulSoup4 来解析得到的 HTML，然后进行资源的下载。\n但 requests 并不擅于模拟浏览器的行为，也不能执行 Javascript，还需要用 PyV8 等库来执行上面的数据计算，得到最终的 pass 。\nPyExecJS 可以实现在 Python 环境中调用 Javascript 引擎，支持的引擎有 PyV8 ，Node.js 等等。\n成功跳过 DDos 防护之后，还需要保持 cookies，构建 heads 。\n既然计算部分要依赖别的引擎解析 Javascript ，那还不如直接使用 Javascript 来写好了。\n这就是 Headless Browser 大显身手的时候了。\nHeadless Browser Headless Browser ，通俗地来说就是个无界面且完整功能的浏览器。目前最流行的 Headless Browser 框架非 PhantomJS 莫属。它是基于 WebKit 开发的，想要整个网页截图神马的（再长也不怕！）用它就最好了。\n不过，用 PhantomJS 保存网页中的图片 还真是有点麻烦。由于上面的 DDOS 防护的存在，每个单独的图像文件依然存在防护，所以不能拿到地址后直接下载，必须使用浏览器来浏览这个图片。\n于是我选择了另一个框架 SlimerJS ，它和 PhantomJS 功能基本一致，API 也基本一致，所不同的就是它使用 Mozilla Firefox 的 Gecko 引擎。\nSlimerJS 的方便之处就在于它的 OnResourceReceived 回调支持 body 这个属性，其中包含正在访问的页面资源（页面中的 CSS、JS、Image 或者页面本身都算资源）的内容。我只需要简单地把它写入到文件中就能得到我需要的图片了。就像这样：\n1function onResourceReceived(response) 2{ 3\tif(state != \u0026#39;image\u0026#39; || response.stage!=\u0026#34;end\u0026#34; || response.stage == \u0026#34;fail\u0026#34; || !response.bodySize) return; 4\tconsole.log(\u0026#39;[onResourceReceived] id: \u0026#39;+response.id + \u0026#39; starge:\u0026#39; + response.stage + \u0026#39; contentType:\u0026#39; + response.contentType+\u0026#39; url:\u0026#39;+response.url); 5\tvar curGalleryObj = galleryList[curGallery]; 6\tvar curImageObj = curGalleryImages[curImage]; 7 8\tif(!fs.exists(curGalleryObj.dir)) 9\t{ 10\tfs.makeTree(curGalleryObj.dir); 11\t} 12\tvar fname = curGalleryObj.dir+\u0026#34;/\u0026#34;+curImage+\u0026#39;.\u0026#39;+curImageObj.ext; 13\tcurImageObj.file = fname; 14\tcurImageObj.done = true; 15 16\tfs.write(fname, response.body, \u0026#39;b\u0026#39;); 17} 下载流程 下面我挑选下载流程中的一些比较重要的实现贴出来，其实很简单的啦。\n1function getGalleryList(htmlFile, callback) 2{ 3\t//htmlFile 是个本地的 HTML 文件，这是为了方便 4\tvar file = htmlDir + \u0026#39;/\u0026#39; + htmlFile + \u0026#39;.html\u0026#39;; 5\tvar p = WebPage.create(); 6\t//不解析这个文件中的javascript 因为有些 js 保存在 google 的服务器上，下载很慢 7\tp.settings.javascriptEnabled = false; 8\t//不再如这个文件中的图像，原因同上 9\tp.settings.loadImages = false; 10\tp.open(file, function(status) 11\t{ 12\t//在页面中调用 DOM 函数或者 DOM 列表 13\tvar list = p.evaluate(function() 14\t{ 15\treturn document.querySelectorAll(\u0026#39;.content .gallery-list div a\u0026#39;); 16\t17\tvar newList = []; 18\t//根据列表构建需要下载的 Gallery 列表 19\tfor(var i=0;i\u0026lt;list.length;i++) 20\t{ 21\tvar href = list[i].href.slice(8); 22\tvar arr = href.split(\u0026#39;/\u0026#39;); 23\tvar obj = {}; 24\tobj.url = href; 25\tobj.id = arr[1]; 26\tobj.name = arr[2].split(\u0026#39;.\u0026#39;)[0]; 27\tobj.dir = imageDir+\u0026#39;/[\u0026#39;+obj.id+\u0026#39;]\u0026#39;+obj.name; 28\tobj.index = site + \u0026#39;/images/\u0026#39; + obj.id + \u0026#39;/\u0026#39; + obj.name + \u0026#39;.html\u0026#39;; 29\tobj.progress = obj.dir + \u0026#39;/progress.json\u0026#39;; 30\tnewList[i] = obj; 31\t} 32\tcallback(newList); 33\t}); 34} 35 36function onIndexOpen(status) 37{ 38\tconsole.log(\u0026#39;[onIndexOpen]\u0026#39;); 39\tstate = \u0026#39;index\u0026#39;; 40\tvar intervalId = null; 41\tvar t = 0; 42\tvar _checkTimeout = function() 43\t{ 44\tif(t \u0026gt; 60) 45\t{ 46\tconsole.log(\u0026#39;!!!!Timeout!!!!\u0026#39;); 47\tclearInterval(intervalId); 48\tslimer.exit(); 49\t} 50\t} 51 52\tvar _checkTitle = function() 53\t{ 54\tconsole.log(\u0026#39;[_checkTitle]\u0026#39;); 55\tconsole.log(\u0026#39;[\u0026#39;+t+\u0026#39;] Check Title...\u0026#39;+page.title); 56\tt++; 57\t_checkTimeout(); 58\t//若出现正确的标题则代表载入成功 59\tif(page.title.indexOf(\u0026#39;Welcome\u0026#39;) == 0) 60\t{ 61\tclearInterval(intervalId); 62\tt = 0; 63\tpage.close(); 64\topenGallery(); 65\t} 66\t} 67 68\tintervalId = setInterval(_checkTitle, 1000); 69} 70 71function openGallery() 72{ 73\tconsole.log(\u0026#39;[openGallery \u0026#39;+curGallery+\u0026#39;]\u0026#39;); 74\tif(curGallery \u0026gt;= galleryList.length) 75\t{ 76\tconsole.log(\u0026#39;==== Download all Gallery Done! ====\u0026#39;); 77\tslimer.exit(); 78\t} 79\tcurImage = 0; 80\tvar curGalleryObj = galleryList[curGallery]; 81\t//本地已有列表，不必再获取 82\tif(fs.exists(curGalleryObj.progress)) 83\t{ 84\tcurGalleryImages = JSON.parse(fs.read(curGalleryObj.progress, \u0026#39;r\u0026#39;)).images; 85\t//本地已经有这个文件了（上次下载成功），下一个 86\twhile(curGalleryImages[curImage] \u0026amp;\u0026amp; curGalleryImages[curImage].done) 87\t{ 88\tconsole.log(\u0026#39;The file \u0026#39;+curGalleryImages[curImage].file + \u0026#39; is downloaded.\u0026#39;); 89\tcurImage++; 90\t} 91\t//开始下载文件 92\tdownloadImage(); 93\t} 94\telse 95\t{ 96\tstate = \u0026#39;gallery\u0026#39;; 97\tpage.open(curGalleryObj.index, onGalleryOpen); 98\t} 99} 100 101function onGalleryOpen(status) 102{ 103\tconsole.log(\u0026#39;[onGalleryOpen \u0026#39;+curGallery+\u0026#39;]\u0026#39;); 104\t//script 标签中保存了 JSON 格式的图像列表，直接作为字符串获取它们 105\t//这样就不必翻页分析 DOM 了 106\tvar script = page.evaluate(function() 107\t{ 108\treturn document.querySelector(\u0026#39;body \u0026gt; div.outer-wrapper.image-page \u0026gt; div.page-wrapper.page-wrapper-full \u0026gt; script\u0026#39;); 109\t}); 110\tif(script) 111\t{ 112\tvar re = /\\\u0026#34;images\\\u0026#34;:(.+\\])\\}\\);/i; 113\tvar images = script.innerHTML.match(re)[1]; 114\tcurGalleryImages = JSON.parse(images); 115\tcurGalleryImages.forEach(function(e,i,a) 116\t{ 117\tvar name = e.f.split(\u0026#39;.\u0026#39;); 118\ta[i].ext = name[1]; 119\ta[i].name = name[0]; 120\t}); 121\tcurImage = 0; 122\tdownloadImage(); 123\t} 124\telse 125\t{ 126\tconsole.log(\u0026#39;NO SCRIPT\u0026#39;); 127\tslimer.exit(); 128\t} 129} 130 131function downloadImage() 132{ 133\tif(curImage \u0026gt;= curGalleryImages.length) 134\t{ 135\tvar curGalleryObj = galleryList[curGallery]; 136\tconsole.log(\u0026#39;==== download \u0026#39;+curGalleryObj.dir +\u0026#39; has done. ====\u0026#39;); 137\t//下载下一个 gallery 138\tcurGallery ++; 139\topenGallery(); 140\treturn; 141\t} 142\tstate = \u0026#39;image\u0026#39;; 143\tvar image = getCurImage(); 144\tconsole.log(\u0026#39;[downloadImage]\u0026#39;+image); 145\t//关闭当前的 page 146\tpage.close(); 147\t//重用资源 148\tpage.open(image); 149} 150 151// 检测文件是否是标准的 JPEG 152function checkJPEG(path) 153{ 154\tconsole.log(\u0026#39;[checkJPEG \u0026#39;+path+ \u0026#39; \u0026#39;+fs.exists(path)+\u0026#39;]\u0026#39;); 155\tif(fs.exists(path)) 156\t{ 157\tvar content = fs.read(path, \u0026#39;rb\u0026#39;); 158\tif(\tcontent \u0026amp;\u0026amp; 159\t//content.length \u0026gt; 10240 \u0026amp;\u0026amp; 160\tcontent.charCodeAt(0) == 0xff \u0026amp;\u0026amp; 161\tcontent.charCodeAt(1) == 0xd8 \u0026amp;\u0026amp; 162\tcontent.charCodeAt(2) == 0xff \u0026amp;\u0026amp; 163\tcontent.charCodeAt(3) == 0xe0) 164\t{ 165\treturn true; 166\t} 167\t} 168\treturn false; 169} 170 171function onLoadFinished(status, url, isFrame) 172{ 173\tloading = false; 174\tconsole.log(\u0026#39;==== [\u0026#39;+state+\u0026#39;]onLoadFinished Loading page(\u0026#39;+url+\u0026#39;) \u0026#39;+ status + \u0026#34; loading:\u0026#34;+loading); 175\tif(state == \u0026#39;image\u0026#39;) 176\t{ 177\tvar curGalleryObj = galleryList[curGallery]; 178\tvar curImageObj = curGalleryImages[curImage]; 179\tvar progressObj = {\u0026#39;gallery\u0026#39;:curGalleryObj, \u0026#39;images\u0026#39;:curGalleryImages}; 180 181\t// 判断 JPEG 文件头，看看是否需要重新下载 182\tif(checkJPEG(curImageObj.file)) 183\t{ 184\tcurImageObj.done = true; 185\tcurImage++; 186\t} 187\telse 188\t{ 189\tcurImageObj.done = false; 190\t} 191\t// 将当前的下载进度写入到配置文件中，以便一次下载不完， 192\t// 或异常退出后。下次下载时可以知道进度 193\tfs.write(curGalleryObj.progress, JSON.stringify(progressObj), \u0026#39;w\u0026#39;); 194\t//下载下一个文件，或者重新下载 195\tdownloadImage(); 196\t} 197} 198 199function onLoadStarted() 200{ 201\tloading = true; 202\tvar currentUrl = page.evaluate(function() { 203\treturn window.location.href; 204\t}); 205\tconsole.log(\u0026#39;==== [\u0026#39;+state+\u0026#39;]onLoadStarted Current page(\u0026#39; + currentUrl + \u0026#39;) will gone. loading:\u0026#39;+loading); 206}; 207 208function onUrlChanged(targetUrl) { 209\tconsole.log(\u0026#39;=== [\u0026#39;+state+\u0026#39;]onUrlChanged New URL: \u0026#39; + targetUrl+\u0026#39; loading:\u0026#39;+loading); 210} 211 212var WebPage = require(\u0026#39;webpage\u0026#39;); 213var page = WebPage.create(); 214var loading = false; 215var imageDir = \u0026#39;images\u0026#39;; 216var htmlDir = \u0026#39;html\u0026#39;; 217var site = \u0026#39;http://examples.com\u0026#39;; 218var fs = require(\u0026#39;fs\u0026#39;); 219 220var page = WebPage.create(); 221var galleryList = null; 222var curGallery = null; 223var curGalleryImages = null; 224var curImage = 0; 225var state = null; 226 227page.onLoadStarted = onLoadStarted; 228page.onLoadFinished = onLoadFinished; 229page.onUrlChanged = onUrlChanged 230page.onResourceReceived = onResourceReceived; 231getGalleryList(\u0026#39;favorites_0\u0026#39;, function(list) 232\t{ 233\tgalleryList = list; 234\tcurGallery = 1; 235\tpage.open(site, onIndexOpen); 236\t}); （全文完）\n","date":"2015-09-13","description":"","lastmod":"2015-09-13T13:02:03Z","slug":"use-slimerjs-to-grab-pages-under-cloudflare-ddos-protection","tags":["javascript","python","browser"],"title":"使用 slimerjs 抓取 DDos 保护的站点","url":"https://blog.zengrong.net/post/use-slimerjs-to-grab-pages-under-cloudflare-ddos-protection/"},{"categories":["technology"],"content":" 2015-09-22 更新：加入 Int64.ts 的介绍连接。 2017-04-28 更新：加入 Number.isSafeInteger 最近把一个 native 游戏移植到 HTML5，客户端和服务端都是 C++ ，而且游戏金币经常性超过 231 ，所以服务端的大爷们很任性地使用了 int64 。\n这下问题来了，Javascript 不支持 int64 。\n说服服务端的大爷们改用 32bit 是不可能的。说服大爷们使用字符串也是不可能的。说服策划重新设置数值使其小于 231 也是不可能的。\n有句话怎么说的？如果不能反抗，那就默默享受吧……\n看我这个 Javascript 前端菜鸟如何应对！\n本着不重复(tou)造轮(lan)子的优良传统，我发现了这样几个已有的实现方案：\n2个uint 拼接 lizi 这位比我还懒的程序员同学实现的 方法，这酸爽……\n1package lz.jprotoc 2{ 3\timport flash.utils.IDataInput; 4\t/** 5\t* ... 6\t* @author lizhi http://matrix3d.github.io/ 7\t*/ 8\tpublic class Int64 9\t{ 10\tpublic var low:uint = 0; 11\tpublic var high:uint = 0; 12\tpublic function Int64(low:uint=0,high:uint=0) 13\t{ 14\tthis.low = low; 15\tthis.high = high; 16\t} 17\t18\tpublic function equal(v:Int64):Boolean { 19\tif (v == null) return false; 20\treturn (v.low == low) \u0026amp;\u0026amp; (v.high == high); 21\t} 22\t23\tpublic function isZero():Boolean { 24\treturn low == 0 \u0026amp;\u0026amp; high == 0; 25\t} 26\t27\tpublic function toString():String { 28\treturn \u0026#34;high:0x\u0026#34; + high.toString(16)+\u0026#34; low:0x\u0026#34; + low.toString(16); 29\t} 30\t31\t} 32} 虽然是 AS3 写的，但转成 JS 也是分分钟。\n字符串拼接法 dom 同学用 ByteArray 来保存每个字节 （同样是 AS3），然后将其转成字符串来显示，缺点和上面 lizi 的一样，就是无法计算。\nnode-int64 node-int64 采用 Javascript 的 Number 来实现对超过 int32 的数值的保存。由于 Number 采用 双精度浮点数 来保存数值，因此该值的范围只能在 +/- 253 的范围内。\n这是我最终的选择。因为金币的值在客户端是会参与计算的，但估计在游戏的有生之年都不可能大于 253 。\n我基于该版本修改了一个 TypeScript 版的 Int64.ts，可以在 egret 中使用。\nNumber.isSafeInteger TishoYs Space 提到了使用 Number.isSafeInteger + parseInt 来处理 Int64，需要注意几个问题：\n如果服务器传递过来的是数字，因为字节序的问题（2个4字节），必须使用上面提到的方法来读取；如果服务器传递过来的是字符串，那么可以使用 parseInt。 isSafeInteger 是在 ES6 加入的，在客户端要慎用。可以使用下面的代码自行实现 Number.isSafeInteger: 1Number.isSafeInteger = Number.isSafeInteger || function (value) { 2 return Number.isInteger(value) \u0026amp;\u0026amp; Math.abs(value) \u0026lt;= Number.MAX_SAFE_INTEGER; 3}; （全文完）\n","date":"2015-09-11","description":"","lastmod":"2017-04-28T02:39:29Z","slug":"int64-in-javascript","tags":["javascript","egret"],"title":"Javascript 的 64bit Int 支持","url":"https://blog.zengrong.net/post/int64-in-javascript/"},{"categories":["technology"],"content":"在 TypeScript 中将一个 number 转换成 string ，这样做会报错：\n1var a:number = 12345; 2var b:string = \u0026lt;string\u0026gt; a; 3// laygroundSingle.ts(24,18): error TS2352: Neither type \u0026#39;number\u0026#39; nor type \u0026#39;string\u0026#39; is assignable to the other. 这样写虽然不会报错，但没有什么卵用：\n1var a:number = 12345; 2var b:string = \u0026lt;string\u0026gt;\u0026lt;any\u0026gt; a; 3console.log(typeof b) 4// \u0026#34;number\u0026#34; PlaygroundSingle.js:19:1 还是直接用 javascript 的方法比较靠谱：\n1var b:string = String(a); 2// or 3var b:string = a.toString(); 注意 new String() 和 String() 的区别：\n1var a:number = 12345; 2// 使用 new 的时候类型必须是 String 而非 string ，否则无法编译通过 3var b:String = new String(a); 4// 不使用 new 则无所谓 5var c:string = String(a); 6console.log(a); 7console.log(\u0026#39;--------b\u0026#39;); 8console.log(typeof b); 9console.log(b); 10console.log(b.length); 11console.log(\u0026#39;--------c\u0026#39;); 12console.log(typeof c); 13console.log(c); 14console.log(c.length); 结果如下：\n112345 PlaygroundSingle.js:22:9 2\u0026#34;--------b\u0026#34; PlaygroundSingle.js:23:9 3\u0026#34;object\u0026#34; PlaygroundSingle.js:24:1 4String [ \u0026#34;1\u0026#34;, \u0026#34;2\u0026#34;, \u0026#34;3\u0026#34;, \u0026#34;4\u0026#34;, \u0026#34;5\u0026#34; ] PlaygroundSingle.js:25:9 55 PlaygroundSingle.js:26:9 6\u0026#34;--------c\u0026#34; PlaygroundSingle.js:27:9 7\u0026#34;string\u0026#34; PlaygroundSingle.js:28:1 8\u0026#34;12345\u0026#34; PlaygroundSingle.js:29:9 95 （全文完）\n","date":"2015-08-11","description":"","lastmod":"2015-08-11T04:12:22Z","slug":"casts-in-typescript","tags":["typescript","javascript"],"title":"TypeScript 的强制类型转换","url":"https://blog.zengrong.net/post/casts-in-typescript/"},{"categories":["technology"],"content":"我大量使用过的配置文件主要有以下几种：\nlua python JSON XML 和 Property list(PLIST) .properties INI YAML 在这些中间，我最喜欢的三种格式是： lua、INI 和 YAML ，最不喜欢的是 JSON 。下面简单的说说它们。\n优秀的配置文件 我认为 好的配置文件 必须包含下面几个因素：\n1.\t规则足够简单 2. 人类友好 在没有任何辅助工具的情况下清晰可读 3.\t支持简单的层级关系 4.\t允许注释\n如果还包含下面几点，则可以认为是 优秀的配置文件：\n5. 易于解析（解析库有多种语言实现） 6.\t逐行解析 在数据不完整的情况下不影响解析 7.\t支持嵌套的层级关系 8.\t支持列表和字典 9.\t支持类型\nlua 和 python 在 lua 开发中，由于 lua 语言的特性，使用一个 lua 文件作为配置文件是很常见也很自然的事情。在使用 quick-cocos2d-x 进行游戏开发的日子里，我大量使用 lua 的这种特性，甚至直接在配置文件中包含简单的逻辑来替换 _G 中的全局变量。而在使用这些配置文件的时候，只需要简单地 require 它们，lua 解释器会帮我们搞定一切。\n当然，lua 作为配置文件的这种方便是 lua 解释器提供的，这也限制了 lua 配置文件的通用性。我们应该尽量在 lua 程序中使用 lua 作为配置文件。离开这个框架，则不太合适了。\npython 的情况也类似。在 python 中，我将一个 dict 直接 dump 成为字符串，在使用这个配置文件的时候，使用 eval 解析。显然，这样使用并没有 lua 那样方便。\n严格来说，这两种格式算不得配置文件。因为它们在特定的语言范畴易用，但缺乏跨语言和跨环境的通用性。例如，如果需要在 Python 中解析 lua 文件，可能需要自己写 解析器 来实现。\n然而，我们可以使用语言的解析器来解析它们，可以不需要任何外部库可能直接使用它们，它们还完全符合我上面定义的 “好的配置文件” 标准。在没有其它选择（或不愿意进行选择），不考虑跨语言的前提下，它们用起来是挺不错的。\nJSON —— 糟糕 由于 JavaScript 的流行，JSON 当然成了最适合 JavaScript 使用的配置文件。JSON 可以在 JavaScript 中使用 eval() 或者 JSON.parse() 来解析，解析后的内容直接成为 JavaScript 的内置对象。\n但是，其实 JSON 不适合作为配置文件使用。根据上面我定义的 好的配置文件 条件来看：JSON 规则简单但不宜读（尤其是复杂的没有格式化过的 JSON），不支持注释，也不能使用较为宽松的语法。\n例如，下面这种多余的逗号在 JSON 中被认为是错误的语法：\n1JSON.parse(\u0026#39;[1, 2, 3,]\u0026#39;) 2// SyntaxError: JSON.parse: unexpected character at 3// line 1 column 10 of the JSON data JSON 的确是在网页和 API 中传递信息的一种好格式，而且各种语言都有它的解析库，但但并不适合做配置文件。\n.properties 和 INI .properties 主要用在 JAVA 程序中，JAVA 内置对它的解析。它是一种简单的配置文件格式，规则大概只有下面几条：\n使用 = : 或者空格作为键值对的分隔符； 单引号和双引号会作为值的一部分； 允许使用 # 作为注释； 可以使用 \\ 作为转义符，用来转义空格、换行和 Unicode 编码。 因为如此简单的格式，.properteis 基本上不需要进行任何解释就能直接使用。也正因为它如此简单，不支持层级关系，因此它并不能算做 好的配置文件 。\nINI 则相当于扩展版本的 .properties ，包含 .properties 的所有优点，去除了一些缺点，并且可以使用 section 来支持层级。它与 .properties 的主要区别如下：\n支持 [section]； 不使用空格作为分隔符； 允许在键名和值中直接包含空格（不需使用转义符）； 支持更多的转义符。 INI 格式是 好的配置文件 ，还包含 优秀的配置文件 的一些特性。由于解析简单，我们可以对它做一些扩展以符合我们的需求。\n例如，我在 rookout 中对 ConfigParser 进行了一点点扩展，使得 ini 可以支持 [@list name] 这类特殊的 section ，将它们直接解析成 Python 中的 list。\nXML 和 PLIST —— 虐心（也虐手） XML 当然是 好的配置文件 ，但它编辑起来太麻烦。即使是有 XML Notepad 这类软件的存在，太多的尖括号和嵌套也让手写这种配置文件变得艰难。同样是由于太多的尖括号，让我不愿意阅读它。\nPLIST 是 XML 的子集，是 Apple 定义的配置文件格式，在 Mac OS X 和 iOS 操作系统中得到大量的应用。PLIST 中直接包含类型信息，因此解析 PLIST 可以直接得到确定的数据类型（甚至是字典和数组）。 XCode 直接支持 PLIST 的编辑，但由于在 Apple 生态系统之外使用是在比较少，支持这种格式的编辑器和解析库都比较有限。当然你可以把它当作纯文本文件来编辑，那就又碰到了 XML 的问题。\n我曾经用 ActionScript3 写过一个 PLIST 格式解析库，由于 AS3 对 XML 的原生支持，所以完成这个解析器还是很简单的工作。\n同样，由于太多的尖括号，导致在版本管理中对不同的文件版本进行比较的时候并不太直观。\n总之，我不愿意手写这两种配置文件格式，机器生成倒是很好的选择。\n实际上，一些软件的特定文件格式就是基于 XML 的。例如 TexturePacker 的 tps 文件格式。\nYAML —— 堪称完美 YAML: YAML Ain't Markup Language 是为了替代 XML 而生。它在 Ruby 中被发扬光大并为世人所知。下面摘抄一段关于 YAML 的评价，来自这里 ：\n为什么不是XML呢？因为：\nYAML的可读性好。 YAML和脚本语言的交互性好。 YAML使用实现语言的数据类型。 YAML有一个一致的信息模型。 YAML易于实现。 上面5条也就是XML不足的地方。同时，YAML也有XML的下列优点：\nYAML可以基于流来处理； YAML表达能力强，扩展性好。 总之，YAML试图用一种比XML更敏捷的方式，来完成XML所完成的任务。\nYAML 完全达到我上面提到的 优秀配置文件 的标准，到目前为止，堪称完美。\n（全文完）\n","date":"2015-08-03","description":"","lastmod":"2015-08-21T09:29:15Z","slug":"configuration-files","tags":["study","as3","python","lua","ruby","xml"],"title":"常用配置文件格式简析","url":"https://blog.zengrong.net/post/configuration-files/"},{"categories":["technology"],"content":"由于 HTML5 和 CSS3 的表现力增强，在线简报系统（好吧，我们还是熟悉 PPT 这个词） 技术也逐渐成熟了， [Slides][1] 和 [SliderRocket][2] 这种在线服务现在已经得到了广泛应用。\n对于爱折腾的程序员来说，希望得到表现力更强的工具，这样的东东也不少：\n[reveal.js -- The HTML Presentation Framework][3] [impress.js -- presentation tool based on the power of CSS3][4] [deck.js -- Modern HTML Presentations][5] 仍嫌不过多的可参考这篇： [5 of the Best Free HTML5 Presentation Systems][6] 。\n[nodePPT][7] 是个国人开发的类似系统，可以采用 GFM(Github Flavored Markdown) 来编写在线演示。\n本文简单介绍如何实现 nodePPT 的自定义模版。\n1. 关于模版 nodePPT 的文档中关于自定义模版的部分是这样写的：\n1感觉默认的模板不符合新意？可以支持自定义模板，查看theme.moon 2 3自定义后的模板路径在markdown的设置里填写： 4 5title: 这是演讲的题目 6speaker: 演讲者名字 7url: 可以设置链接 8transition: 转场效果，例如：zoomin/cards/slide 9files: /css/theme.moon.css 10 11另外有：colors-moon-blue-dark-green-light 共六套自带皮肤可供选择 12 13theme: moon 14 15or url?theme=moon [theme.moon][9] 是一个 [Sass][10] 文件，我们可以使用 [compass][8] 将其转换成 css 文件，再使用文档中提到的 files: 标签将其引入。\n但这种方式并不合适，因为在 nodePPT 生成的结果文档中，使用 files: 引入的文件会被安排在文档自带的 theme 文件之前，这就导致文档自带的 theme style 会覆盖引入的 css 定义。如下所示：\n1\u0026lt;script src=\u0026#34;./js/zoom.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 2\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;./css/theme.your_theme.css\u0026#34;\u0026gt; 3\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;./css/theme.moon.css\u0026#34;\u0026gt; 4\u0026lt;!--placeholder--\u0026gt; 5\u0026lt;/body\u0026gt; 6\u0026lt;/html\u0026gt; 因此，正确的方法应该是使用 theme: 标签指定模版名称。以模版名称为 theme.your_theme.css 为例，有两种方法：\n方法一\n直接将模版文件复制到 nodePPT 的安装目录中，在 Mac OSX 上的默认安装路径如下：\n/usr/local/lib/node_modules/nodeppt/assets/scss 这样，就可以使用 theme: your_theme 来指定 PPT 的模版。此时的 theme.your_theme.css 就是一个 Sass 文件，在使用的时候 nodePPT 会自动帮我们转换成 css。\n方法二\n使用 [compass][8] 将 theme.your_theme.css 转换成 css 文件，然后将其复制到 ppt 根目录中的 css 子目录。同样使用 theme: your_theme 来指定 PPT 模版，效果相同。\n2. 针对首页和末页的特殊处理 我的模版要求如下：\n首页和末页必须显示特定的背景图像，而内容页则使用另一张背景图像。 首页和末页的背景图像的背景色不同。因此首页的文字主体色应为浅色，而内容页的文字主体色应为深色。 在 notePPT 中，所有的 [slide] 标签会被渲染成 \u0026lt;slide\u0026gt; html 标签，并包含在 id 为 container 的 \u0026lt;slides\u0026gt; 标签中。如下图所示：\n![nodePPT slides][51]\n同时，要注意最后一个 slide 并非就是最后一页。 最后一页其实是 nodePPT 自动生成了一个 id 为 tip 的 \u0026lt;div\u0026gt; 标签（上图中反色的部分）。\n所以，要实现选择最后一页幻灯片，就不能使用 :last-child 选择符，而应该使用 :nth-last-child(2) 。\n下面的 Sass 实现了针对首页和末页的特殊处理，其中 $font-color 是浅色，而 $title-color 是深色。同时，要注意 css 中图像的相对路径问题。\n1slides \u0026gt; slide{ 2 color: $font-color; 3 letter-spacing: 2px; 4\tbackground-color: $bg-color; 5\tbackground-image: url(../img/content.png); 6\tbackground-repeat: no-repeat; 7\tbackground-size: 100% 100%; 8\tcolor: $font-color; 9\t\u0026amp;:first-child, 10\t\u0026amp;:nth-last-child(2){ 11\tbackground-image: url(../img/title.png); 12\tcolor: $title-color; 13\t} 14\t\u0026amp;:first-child h1, 15\t\u0026amp;:first-child h2, 16\t\u0026amp;:first-child h3, 17\t\u0026amp;:nth-last-child(2) h1, 18\t\u0026amp;:nth-last-child(2) h2, 19\t\u0026amp;:nth-last-child(2) h3{ 20\tcolor: $title-color; 21\t} 22\t// more and more 效果可以看这里： [game-deploy][11]\n（全文完） [1]: http://slides.com/ [2]: http://www.sliderrocket.com/ [3]: http://lab.hakim.se/reveal-js/ [4]: http://bartaz.github.io/impress.js/ [5]: http://imakewebthings.com/deck.js/ [6]: http://www.sitepoint.com/5-free-html5-presentation-systems/ [7]: https://github.com/ksky521/nodePPT [8]: https://blog.zengrong.net/post/2353.html [9]: https://github.com/ksky521/nodePPT/blob/master/assets/scss/theme.moon.scss [10]: http://sass-lang.com/ [11]: http://doc.zengrong.net/ppt/game-deploy/index.htm [51]: /uploads/2015/07/nodeppt-slides.png\n","date":"2015-07-29","description":"","lastmod":"2015-07-29T01:35:00Z","slug":"nodeppt-template","tags":["html5","javascript"],"title":"nodePPT 自定义模版","url":"https://blog.zengrong.net/post/nodeppt-template/"},{"categories":["technology"],"content":"安装 compass 遇到错误，看错误信息是网络问题。\n可能是被墙，也可能是我所处的网络太垃圾了。\ngem 的 -p 参数可以指定代理服务器。\n如果没有好用的代理的话，也可以使用 --local 进行本地安装。下面是本地安装的方法：\n访问 https://rubygems.org/ 搜索 compass ; 依次下载 compass 的 gem 文件以及其依赖的 gem 文件，大约 2MB ； 使用 gem install --local compass-1.0.3.gem 查看依赖关系，然后逐个安装即可。 当然，更简单的方法是使用淘宝提供的 RubyGems 镜像 。\n","date":"2015-07-21","description":"","lastmod":"2015-07-29T03:22:06Z","slug":"install-compass-in-local","tags":["ruby","css","html5"],"title":"离线安装 compass","url":"https://blog.zengrong.net/post/install-compass-in-local/"},{"categories":["others"],"content":"LP 在纠结移动电源能否上国际航班的问题，于是我顺手找了点资料。\n中国民航局在2014年8月发文对这件事情专门做了规定：[关于民航旅客携带“充电宝”乘机规定的公告][1] 。\n规定中的内容简化一下就是这样：\n只能在手提行李中携带或随身携带，禁止托运； 每人最多可带2个充电宝，充电宝合计容量不超过100Wh； 充电宝上必须有明确的标示。 民航局给出的容量单位是 Wh（瓦时），而一般充电宝上标注的是 mAh（毫安时），两者的换算方式是：\nWh = V * (mAh/1000) 公式中的 V 代表 伏特。\n上面是程序员表示法，现在用中文说明下：\n正规厂商的充电宝都会在外包装上标注容量（mAh），但少有充电宝在包装上标注电压。由于充电宝一般使用 USB 接口作为输出，而 USB 接口的标准电压是 5V，因此对于没有标注电压的充电宝来说，应该以 5V 作为充电宝电压计算。\n以我的 [SSK SRBC509][2] 来看，根据上面的公式，先用6600mAh除以1000，然后乘以5V，得到的值是 33Wh 。\n民航局的计算采用的是 3.7V，估计是他们考虑到锂电池电池组的电压就是 3.7V。其实移动电源为了支持 USB 的标准电压，在内部一定是有升压电路的，这部分电路其实也会造成一定的损耗。\n北京首都国际机场网站也发布了 [相关声明][3]，和民航局的一致。\n那么国际航空的规定是怎样的？\n有 [知友][4] 说国际航空一般是不检查的，也有人说在加拿大买的充电宝在国外畅通无阻，一到国内就被扣。至于不检查的原因，有知友说歪果仁一般习惯在包里带上充电器而不习惯用充电宝。我看这倒不一定，看看人家的评测吧：[20 best power banks 2015 UK: best portable chargers and emergency chargers for phones and tablets - best power bank reviews][5]\n（全文完） [1]: http://www.caac.gov.cn/C1/201408/t20140807_66977.html [2]: http://en.ssk.cn/index.php/Product/show/id/116?mid=32\u0026amp;pmid=3 [3]: http://www.bcia.com.cn/news/news/141114/news1063.shtml [4]: http://www.zhihu.com/question/21570131/answer/30544226 [5]: http://www.pcadvisor.co.uk/test-centre/mobile-phone/20-best-power-banks-2015-uk-3534490/\n","date":"2015-07-12","description":"","lastmod":"2015-07-12T07:29:19Z","slug":"can-i-take-plain-with-a-power-bank","tags":["journey"],"title":"移动电源能否带上国际航班？","url":"https://blog.zengrong.net/post/can-i-take-plain-with-a-power-bank/"},{"categories":["technology"],"content":"Python2 和 Python3 是不兼容的，如果碰到无法升级到 Python2 代码，或者同事中有坚守 Python2 阵营的情况，就要考虑 Python2 和 Python3 在系统中共存的情况。\nMac OS X 和 Linux 不必考虑这个情况。因为这些类 Unix 操作系统在安装 Python3 的时候，会将其直接映射为 Python3 。同时安装 Python2 和 Python3 并不会有什么冲突。\n这里只讨论 Windows 下的情况， 下面是我常用的方法。\n1. 创建 D:\\Python 文件夹；\n2. 将 Python 2.7 安装到 D:\\Python27，将 Python 3.4 安装到 D:\\Python34 ，安装的时候注意 不要 把 Python 加入到 PATH 环境变量；\n3. 创建 D:\\Python\\py2.bat ，内容如下：\n1D:\\Python\\27\\python.exe %* 4. 创建 D:\\Python\\py3.bat ，内容如下：\n1D:\\Python\\34\\python.exe %* 5. 将 D:\\Python 加入环境变量 PATH 。\n这样处理之后，就可以直接在命令行中敲入 py3 yourpythonfile.py arg1 arg2 来调用 Python3 了，Python2 也一样。\n（全文完）\n","date":"2015-07-07","description":"","lastmod":"2015-07-07T01:10:12Z","slug":"python2-3-in-windows","tags":["python"],"title":"Python2/3 在Windows 下的共存","url":"https://blog.zengrong.net/post/python2-3-in-windows/"},{"categories":["others"],"content":"2015-08-04 更新：增加其它适合编程的键盘。\n用了一个月的 Logitech MK520 ，左手小拇指快废了。\n于是，抱着 没有 HHKB 的人生是不完整的码农 的思想，立刻入了一个 HHKB Professional 2 白色无刻版 。由于网上相关的装逼文已经很多，我就只评测一下我关心的部分了。\n我的键盘 Logitech MK520 这个键盘放置了很久，最近重新拿出来用。它的按键有一些内凹，边缘又做了圆角处理，看起来是比较讨喜的。\n刚开始的时候还感觉手感较好，但敲了一年多青轴再次拿出来用时，立刻感觉左 Shift 键手感特别硬，回弹也不够。\n由于 Vim 的命令需要用 : 开启，时间一长，小拇指的第一关节和指腹都无法忍受。我在指尖上包了一个创可贴都无济于事。\nMicrosoft Wireless Desktop 3000 Wireless Desktop 3000 是个多媒体键盘，并不适合编程。尤其是 ESC 太小，必须做一些按键映射才能在 Vim 下使用。\n手感上，Wireless Desktop 3000 要好于 Logitech MK520 （一分钱一分货嘛），没那么硬，也有一定的回弹。但在连击的时候键位之间有明显的迟滞感，无法打出行云流水的感觉。\nBenq 天机镜 KX890 青轴版 这是近两年使用的键盘。有人说青轴是机械键盘的入门版，用了之后再换黑轴茶轴红轴会比较好。但我一用青轴就爱上了这种段落感和清脆的声音，却无法喜欢茶轴和黑轴。\n有人说在公司里用青轴是找打+没素质，其实解决的方法很简单，就是忽悠大家都买青轴，那么键盘声不就成白噪音了么……\n如果不是因为用了3个月就坏了（京东换新），我可以说这是个手感完美的键盘了。Cheey 原厂轴体+大 Enter 键+钢板+不到 1000RMB 的价格，已经让它很超值了。只是为什么原厂轴这么不耐操？现在 Enter 又出现连击现象了。\nHHKB Professional 2 Type-S 还是 Pro2 最开始，我考虑的是 Type-S ，但看了一些评测之后，发现为了实现静音，Type-S 的键程会比较偏短。对于我这种大爱青轴的人来说，是并不适合的。\n白色与无刻 看了墨色的照片，很廉价的感觉。至于为什么选择无刻版？装 B 就要装到底嘛~~\n键位 熟悉 Unix 系统和使用 Vim/Emacs 的同学应该不在意这点折腾，但我还是去找了一个键位图放在桌面上对比。需要的拿去：\n另外，这几个快捷键还是必须记牢的(via)：\n移动光标快捷键\nControl-F 光标前进一个字符，相当于右键（F = Forward） Control-B 光标后退一个字符，相当于左键（B = Backward） Control-P 上移一行，相当于上键（P = Previous） Control-N 下移一行，相当于下键（N = Next） Control-A 移动到一行的开头（A = Ahead） Control-E 移动到一行的结尾（E = End） Fn-[ 上箭头 Fn-; 左箭头 Fn-, 右箭头 Fn-/ 下箭头 注意，在 Mac OS X 操作系统的原生程序中，是可以使用 Control-P/N 代替上下键的。但是对于某些跨平台程序来说，很多时候还是必须使用箭头键。\n例如在 Firefox 的浏览历史下拉列表中，就无法使用 Control-N 来选择，而必须使用 Fn-/ 等箭头键。\n文字删除快捷键\nControl-H 删除光标前面的字符 Control-D 删除光标后面的字符 Control-K 删除从光标开始，到一行结尾的所有字符 文字选择快捷键\nControl-Shift-A 选中从光标开始，到一行开头的所有文字 Control-Shift-E 选中从光标开始，到一行结尾的所有文字 与青轴的对比——打字感受 电容轴和机械轴的确是不能对比的，它们完全不在一个维度。有人说 HHKB 的使用感觉是揉胸，这个比喻虽然有点过分（对没有女朋友的男程序员来说更过分），但不得不说还是有那么一点点准确。HHKB Pro2 的敲击感明确，但没有青轴那么清晰；回弹力度不及青轴，但又有足够的段落感。从连击的顺畅感来说，电容轴似乎比青轴多了一点点的连贯性，少了一点点的打击感；从施力的角度来说，电容轴在按下那一刹那的阻力要小于青轴。\n打字感受中还有很重要的一点是声音。青轴的声音是“啪啪”，很清亮，很干脆；电容轴的声音是“噗噗”，感觉略显沉闷，没有青轴的穿透感那么强。下面有录音，可以感受下。\n对于使用机械键盘的程序员来说，如果你喜欢青轴，我认为你一定也会喜欢 HHKB Pro2 的手感。\n与青轴的对比——声音 购买之前，我担心 HHKB Pro2 声音太大，但在网上找不到 HHKB Pro2 和青轴的对比，抱着“再怎么大也不可能大过青轴”的想法入手。现在经过实际对比，感觉 HHKB Pro2 的声音比青轴还是小许多的。如果薄膜键盘的声音是100，那么 HHKB Pro2 就是160，青轴是220。青轴的杀伤力在于它那极有穿透力的清脆敲击声，而 HHKB Pro2 虽然比一般的薄膜键盘要响不少，但没有那种高频的敲击声。当然，夜深人静的时候，没有沉浸在代码中的人听起来还是挺刺耳的。LP隔了一个房间都来投诉了，但我拿出青轴之后，她什么也没说默默走开了。\n我将目前正在用的3个键盘的敲击声分别录制了音频，大家可以自行比较下。\n录音设备：红米2增强版 录音设备与键盘距离：模仿键盘与耳朵的距离 录制测试程序：TT Game-Letter Invaders-Practice Keys （真是个暴露年龄的软件） HHKB Professional 2\n您的浏览器不支持音频！！！ Benq 天机镜 KX890 青轴版\n您的浏览器不支持音频！！！ Microsoft Wireless Desktop 3000\n您的浏览器不支持音频！！！ 由于模仿了键盘与耳朵的距离，上面所有键盘的声音听起来都比较“温柔”，只有青轴的穿透力要强一些。那么把录音设备放在键盘旁边如何？听听下面的两个声音就知道了，连 HHKB Pro2 都显得那么暴烈。(其中有规律的呼呼声是风扇)\nHHKB Professional 2（近距离）\n您的浏览器不支持音频！！！ Benq 天机镜 KX890 青轴版（近距离）\n您的浏览器不支持音频！！！ 购买 我的 HHKB Pro2 是在万能淘宝购买的，包邮包税 1430 RMB。EMS 国际快递，一共花了6天。到货之后，我才想起来现在日元这么低，100日元都换不到5块钱人民币，中国人民全都跑去日本扫货了，我本该直接去日亚(Amazon.jp)海淘才对。下面我把海淘的方式写出来，也算一个日本市场拯救计划，欢迎参加。\n注册 EMS 的海淘服务 EMS 目前开通了 中邮海外购 业务，可以先注册一个账户。注意 gmail 邮箱好像无法验证，反正我收不到验证邮件。\n日亚填写仓库地址 在日亚购物的时候，需要填写中邮海外购提供的转运仓库地址。这里有篇中邮海外购提供的 一分钟学会转运 的文章，讲得比较清楚。\n费用计算 海外购的费用就是商品包含下面几个部分：\n购买价格 寄往转运仓库的邮费 转运费用 可能产生的税费 个人邮寄物品入境的规定请查看海关总署2010年第43号公告，文件规定，“个人寄自或寄往港、澳、台地区的物品，每次限值为800元人民币；寄自或寄往其它国家和地区的物品，每次限值为1000元人民币。个人邮寄进境物品，海关依法征收进口税，但应征进口税税额在人民币50元（含50元）以下的，海关予以免征。个人邮寄进出境物品超出规定限值的，应办理退运手续或者按照海关规定办理通关手续。但邮包内仅有一件物品且不可分割的，虽超出规定限值，经海关审核确属个人自用的，可以按照个人物品规定办理通关手续。”\n对于这种小件商品，关税是抽查的，所以你购买的键盘不一定会完税。即使海关要对你的键盘征税，中邮海外购也提供了方便的代征渠道。这里有详细的说明：服务与价格 。\n我大致算了一下，今天 HHKB Professional 2 在日亚上的价格是 21852 日元，按照今天的汇率，合人民币 1103.526 元；邮费不详；专用费按 1KG 算是 97 元（这个价格和我的键盘从日本寄来的价格2100日元是一致的）；若运气不好需要征税，那么按照海关的完税税率，键盘是 80元*10%=8元？是不是太少了点？我有算错么？\n若计算正确的话，自己海淘的话价格应该是 1104+97+8=1209 元。\n不是吧？赚这么多？这也许是我没算运费的缘故。反正自己海淘至少能省100多是没错的啦。\n你自己看着办。\n其它键盘 下面几个键盘，是其它程序员推荐的，列在这里，有空一个个败：\n1. Topre RealForce 87UB\n也是静电容，热升华印刷，也是 日本制造，有国行版（有中国网站），但太贵，标准版 1700RMB。日亚只卖 1000RMB 不到。\n2. Filco MINILA AIR\n机械青轴，蓝牙无线。使用 DIP 开关支持 CapsLock 与 Ctrl 互换，Esc 与 `/~ 互换，Backspace 与 |/\\ 互换。\n3. WASD V2 88-Key ISO Custom Mechanical Keyboard\n机械键盘，自己定制。适合比我更纠结的程序员。\n装逼时间 下面就是垃圾时间了，放些照片，不看也罢。\n（全文完）\n","date":"2015-07-04","description":"","lastmod":"2015-08-04T01:21:03Z","slug":"hhkb-pro2","tags":["hardware"],"title":"HHKB Pro2 简单评测","url":"https://blog.zengrong.net/post/hhkb-pro2/"},{"categories":["technology"],"content":" 2015-08-18 更新：加入两本在线书到“没有终点“部分。 2021-01-22 更新：加入 devdoc 和 IDE 。 2021-06-23 更新：加入新的项目源码。 对于我来说，Python 似乎已经入门了。那就把我的入门建议写出来吧，能让看到的人避免走弯路就好。下面的标题按时间顺序排列。\nPython2 还是 Python3 ？ 毫无疑问，Python2 是辉煌，Python3 是未来。不要相信网上那些大牛吐槽 Python3 速度慢、兼容性差、库少等等言论了，仔细看看那些言论的发表时间。现在都特么已经2015了好么。\n如果某个库到现在还没有兼容 Python3，那么放弃它就是了，因为它的作者已经抛弃了它。\n使用 Python3 ，你不会再碰到恶心的 i18n 问题，也不会再面对那些奇怪的2包名了。\n选 Python3 吧，因为你属于未来。\n第一本 Python 书？ 自然是这本了：The Python Tutorial ，中文翻译。\nPython 文档的质量很高，更新也非常及时。看完上面这本（大约2天就够了），你就已经了解了 Python 最主要的特性。注意中文翻译版可能有些错误，应和原版一起阅读。\n离线文档 在 Windows/Linux 上安装 Zeal ，在 Mac OS X 上安装 Dash，然后下载 Python 的离线文档。\n若是不愿意使用上面的软件，可以直接下载 Python.org 提供的离线文档，建议下载 HTML 版本，这样搜索会比较快速。\n我常用的方案是在本地架设 HTTP Server(使用Apache 或者 Nginx)，使用本地站点定位（例如：http://localhost/docs），这样可以整合许多可用的文档，搜索起来比较方便，例如这个：http://doc.zengrong.net/python/ 。\n推荐开源项目 DevDocs，支持离线访问。\n编辑器/IDE 如果没有什么偏好的话，可以使用 Sublime 。若是喜欢 IDE 类型，可以使用 PyCharm 这个和 Intellij IDEA 同源的优秀 IDE。若已经加入 Vim 或 Emacs 党，就什么都不用选。\n2021-01-22 更新：\n我在 Python日课-2.2-IDE的选择 中推荐了 VSCode/Notepad++/Geany ，也很值得尝试。\nCoding with Quick And Dirty 接着可以写一些菜鸟级的程序了。把以前用 bash 写的打包脚本神马的拿来改一改，用 sed/awk 写的文本分析器拿来重写下，找点自信让自己继续下去。不要太关注是否采用了 Python 风格的用法，这个过程的重点在于建立自信和找到使用 Python 的快乐感觉。\n在这个过程中，不可避免的会接触到 Python 的标准库，用 Quick and Dirty 的方法去学习它们，让它们在你脑海中留下印象。\n在这个不超过一周的过程中，可能会发出感叹：Kao，真 TMD 的简单。\nCoding Reference 在 Coding 的过程当中，可能会碰到不少问题，毕竟一门流行的语言都有一个完善的社区和工作流程，要干活，就要了解他们。\n我将这些信息集中在这里，方便查找：\n你经常会碰到 PEP 类的东东，它们是什么？—— PEP Purpose and Guidelines Python 代码规范 —— Style Guide for Python Code 安装第三方包，各种不同的方法令人很焦虑？ —— Python 包管理工具解惑 你需要一个虚拟环境。 —— Python 虚拟环境 这是不可避免的 —— Python2/3 在Windows 下的共存 __init__.py 和 __main__.py 第二本 Python 书 第二本当然就是传说中 The Fuck Reference 了。你不可能在第一时间全部读完它，但你至少应该先读完这个：Data model 。Data Model 本来就是一个面向对象语言的基础，先读完这个，才能够更顺畅地阅读 Reference 的其他部分。\nReference 应该作为字典，在不懂的时候就来查一下，在没事的时候就来翻一下。毕竟，这些是 Python 最基础的东西。\nCoding With a Project 现在的 Coding 不能局限于脚本级别了，你应该完成一个项目。这个项目可以是你以前项目的 Python 翻版，也可以从头设计一个新的小型项目。\n这个部分对你的帮助在于，通过完整项目的设计，你能将以前的工程化经验应用到 Python 中来（或者反过来说也行），你会更了解 Python 在项目管理上的一些技能。\n这方面值得单独写一篇文章来说明，不过我似乎不必写了，因为已经有一篇了： Open Sourcing a Python Project the Right Way 。\n下面是我初学 Python 的时候建立的两个项目，我一直在对它们维护到现在。他们的内容足够简单，可以拿来稍作参考。\nrookout 一些我自己常用的 python 功能封装，Python 库项目。已经发布到 pipy 上。 wpcmd 通过 WordPress XML-RPC 接口在本地创建、更新 WordPress 博客的命令行工具，Python 命令行工具项目。zengrong.net 就是使用该工具进行管理。 如果对 GUI 开发感兴趣，我建议在这个阶段直接开启一个 GUI 项目，使用 PyQt5，然后使用 cx_Freeze 打包。\n在这个阶段，必须要注重 code style，深入理解各种不同用法，了解API和标准库（仅仅是了解，不必熟悉），并完全忘记前面施行的 Quick And Dirty 方法。\n2021-06-13 更新：\n之后我又写了一些稍复杂一些的开源项目，也可以参考。\nWizNote 转换到 Joplin系列 花了一些时间对这十年的文档进行了整理，把 3000 多篇精简到 2000 多篇，在 2021 牛年春节期间，用 Python 写了一个转换工具 wiz2joplin，完成了十年的转换。 pyape 我对 Flask、SQLAlchemy 等相关 WEB 包做的封装，包含构建 RESTFul API 的通用工具。SAGI GAMES 早期的短连接服务器采用这套包来开发。 没有终点 Python 可以做很多事情。科学家用它来做 科学计算，漫画爱好者用它来爬站，程序员们用它来写开源/商业软件，我们也曾经用它做游戏服务器，还有 游戏引擎 直接使用它做脚本语言。\n写 Python，需要注意的是它是 Python，不是 C++。对任何一个新的语言，都不要试图完全用自己已有的语言经验去理解和使用它。在程序中要体现出 Python 的特点：lambda、生成器、列表推导式，这些东西不可不用，也不应乱用。\n入门到此为止，但学习没有终点。我列出一些可能所有 Python 使用者都需要了解的东西（或需要的资源），而更多的内容，只能根据偏好和使用方向自己选择了。\nPython 2/3 区别(What’s New In Python 3.0) 2to3 - Automated Python 2 to 3 code translation PyZh 全局解释器锁 GIL Python 测试框架的选择 《深入Python3》 《Python Cookbook 3rd Edition》 《用Python做科学计算》 笨办法学Python A Beginner’s Guide to Python 全文完 ","date":"2015-07-03","description":"","lastmod":"2021-06-13T05:42:02Z","slug":"python-rookie","tags":["python","study"],"title":"Python 入门建议","url":"https://blog.zengrong.net/post/python-rookie/"},{"categories":["impressions"],"content":" 开始日期：2015-06-08 读完日期：2015-06-16 书籍介绍： 编号1510 《从0到1》 讲了许多直白的道理和创业故事，而如果把《从0到1》和《浪潮之巅》互相印证，则会觉得更有趣。下面是我的摘录和一些思考：\n最反主流的行动不是抵制潮流，而是在潮流中不丢弃自己的独立思考。 第2章 像1999年那样狂欢\n从技术层面看，我之前一直坚持帮 Adobe 洗地，也算是抵制潮流的一种表现吧。独立思考必须坚定地建立在对当前局势和潮流的理解和剖析之上。没有永远正确的潮流，也不可能有屹立不倒的公司。\n即使你非常有才能，也未必要创建自己的公司。现在自己开公司的人太多了。懂得幂次法则的人在创建企业时候会比其他人更犹豫：他们知道加入一个发展迅速的一流企业会获得更大的成功。 最重要的事往往不能一眼就看出来，它甚至像个密码不为人知。但是在幂次法则的实践中，如果你不认真想一想你的行动会使公司落在80-20曲线的什么位置上，后果你真的承担不起。 第7章 向钱看\n我一直坚信自己会成功。我勤奋且经验丰富、经历多个行业且都很有收获，一个人就能解决技术和管理上的所有问题。但事实是我错得离谱，我做了太多的事，却没有看出来我们处在曲线的什么位置上。\n卡辛斯基将人们的目标分为三组：\n稍作努力即可达到的目标。 不懈努力才能达到的目标。 再怎么努力都不可能达到的目标。 人生是漫长的旅程，由前人踏出来的路，一眼望去，没有尽头。 道路不必无限延伸，一直走下去，选择哪条隐秘的路吧。 第8章 秘密 我设定的短期目标（3年）都是必须经过不懈努力才能达成的，我从来没有第3种目标，因为它们迟早会变成第2种。而隐秘的路，走起来是否更加有趣？\n基础没有打好的初创企业是无法挽救的。 每段关系开始的时候都会乐观，而冷静思考以后可能会出现的问题就不那么令人愉悦了。 技术能力和才华互补固然重要，但创始人之间的了解程度和他们合作的默契程度也同样重要。 因为每天在不同的时间、相同的地点上班，同事之间就会产生分歧。美国20世纪小说家肯.凯西说得对：要么上车，要么下车。 第9章 基础决定命运\n创业和婚姻很相似。在漫长的婚姻过程中，两人为了婚姻的持续和家庭的和谐，必须互相做出一些妥协，互相影响，做出改变。这就是我们说的 “夫妻相” 的原因。\n创业有着和婚姻类似的因素：双方（或多方），共同的孩子——公司，共同的财产，共同的责任和共同的利益。若与美好的婚姻一样做到互相妥协，或许会越来越好；若与糟糕的婚姻一样互不买账，互不妥协，坚持不改，毫无默契，那总得有人下车。\n时间是最宝贵的资产，将时间浪费在不能长久合作的人身上得不偿失。如果你不能在工作上建立持久的关系，那么你就浪费了时间——即使纯粹从财务的角度来看，也是如此。 因此我们打算雇佣真正喜欢团队合作的人。他们必须有才华，但是更为重要的是，他们要由衷地喜欢与我们共事。 奔着免费洗衣或宠物看护而来的人是不会成为你团队中的一名合格成员的。 在薪酬福利上你可能比不上2014年的谷歌，但如果能就公司使命和团队给出好的回答，你便与1999年的谷歌站在同一高度。 每名员工只专注一件事情 第10章 打造帮派文化\n我很自豪一手打造了公司的文化，然并卵。我们的同事努力、聪明且勤奋，可我们在增加新的同事的时候遇到了很大麻烦。我花了大量时间在面试、招聘和培养新人上，但大部分的努力都失败了。甚至有些人给公司带来了很大麻烦。我们总是想 “再等等，再等等，再看一下”，而这些在一面就让我们感觉不对的人本应直接被拒绝。\n创业公司招人，其实是在找合伙人。我们需要品性、特点、风格、能力和我们类似的人，而不仅仅只是“能做事”的人。\n每名创业者也应该关注尽量少的方面。在创业初期，创始人可能要做各种 脏活累活 ，但这种事情如果做得太久，只能说明公司人力资源和员工能力上有问题。\n推销广告的人被称为“业务经理”，推销客户的人被称为“业务开发”，推销公司的人被称为“投资银行家”，推销自己的人被称为“政客”。这些称谓的改换大有道理，没有人愿意被提醒自己正在被推销。 即使你的公司仅仅由你和电脑组成，也是如此。环视四周，如果没有看到销售员，那么你就是。 第11章 顾客不会自动上门\n推销，我们关注得太少。产品再好，也是需要推销的。更何况现在已经是血海时代。\n多数能源公司折戟是因为至少忽略了以下7个问题之一，而这些问题是每个公司必须回答的：\n工程问题：你的技术具有突破性，而不仅仅是稍有改进吗？ 时机问题：现在开创事业，时机合适吗？ 垄断问题：开创之初，是在一个小市场抢占大份额吗？ 人员问题：你有合适的团队吗？ 销售问题：除了创造产品，你有没有办法销售产品？ 持久问题：未来10年或20年，你能保住自己的市场地位吗？ 秘密问题：你有没有找到一个其他人没有发现的独特机会？ 阻碍进步的不是企业的贪婪与非营利组织的善良之间的差异，而是做一样的事。 最好的项目可能是人们忽视的项目，或没有大肆宣扬的项目；最好的问题是无人尝试解决的问题。 第13章 绿色能源与特斯拉 看着这7个问题，我不禁一头冷汗。如果说公司创始之初（2011年底），我们还在2和4上有优势的话，现在则无法回答任何问题。\n在中国这个还不规范的市场，最好的项目是有内幕交易的项目，正是因为这样的项目太多，才没有人愿意去尝试上面提到的最好的问题。\n所有的创始人都特立独行吗？或者我们只记住或者夸大了创始人身上那些最独特的地方？更重要的是，创始人身上的哪些个人特质是帮助他们成功的？ 独树一帜的创始人能做出权威决策，激发员工强烈的忠诚度，提前做出未来几十年的规划。 总而言之，不要高估自己的个人能力。创始人的重要性并非源自自身工作带来的价值，事实上，优秀的创始人能使公司的每个人发挥所长。 创始人最大的危险是对自己的神话过于肯定，因而迷失了方向。同样，对于公司，最大的危险是不再相信创始人的神话，错把不信神话当作一种智慧。 第14章 创始人的悖论\n创始人无疑是创业公司最重要的那个人。这个人必须坚韧、果敢、有智慧、有担当。创始人是创业公司的领袖，是精神支柱。创始人不必真正做到这几点，但创始人必须让员工相信自己能做到这几点。\n公司走上正轨之后，创始人就不应该纠结在工作的细节上，甚至不应该参与具体工作。对具体工作的参与会分散创始人的精力，还会影响创始人在重要事务上的决定。同时，细节的模棱两可与斤斤计较，会削弱员工对创始人能力的判断，降低员工认同感。\n这里引用一段晨兴创投刘芹的话：\n创业者要具备两个核心能力：一个叫以身作则的犀利杀手气质；另一个是你有传教士能力，能聚一帮牛人。 杀手的气质，意思是解决公司业务发展中的短板，就跟打仗一样，你要拿下这个山头，你要有在百万军中取上将首级的能力。公司的发展是长板跟短板理论，什么短要补什么，所以创业者最重要的能力是杀手气质，还有补短板的能力，你得不停的变，缺什么都能自己顶上，也许不一定能做的最好，但是你要有0到1解决短板的能力，虽然你不专业，但你永远是那个冲在第一线的。 传教士是什么？就是你的深入思考和能影响到什么样优秀的人跟你一起。传教士传教一定是你不信我的时候我来传。所以，你身边聚一群什么样的人，基本能衡量你的理念。如果你下面的人，各个都是很有想法的人，那一定是你的想法比他们都大，能把他们震住，你就能取得1+1大于2极强的成功。 所以，发挥你的领导力，你能吸引什么样的人，很能反映你的所有综合性能力。因为那些牛人，只会服务比自己更牛的人，如果你的力量都没他大，如果你的战略不够犀利，他为什么跟你？最好的公司融资都是市场上被追捧的，如果要不停想怎么融资的公司，一定哪有问题，一定要做减法，有可能早死早超生，取得接下来的成功。\n不是每个创业者都有魄力杀掉自己的孩子，所以早死早超生这种话，或许只是没有什么卵关系的 VC 说说而已。\n（全文完）\n","date":"2015-06-22","description":"","lastmod":"2015-06-22T07:19:05Z","slug":"zero-to-one","tags":["reading","life"],"title":"【读书笔记】我的创业-读《从0到1》有感","url":"https://blog.zengrong.net/post/zero-to-one/"},{"categories":["technology"],"content":"Python3 中的排序，在 Sorting HOW TO 中已经讲得很清楚了。来个实际的例子，对下面的这个 list 依据创建时间排序：\n1pages = [ 2{\u0026#39;title\u0026#39;: \u0026#39;十年学会程序设计\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2012-02-14\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;21-days\u0026#39;}, 3{\u0026#39;title\u0026#39;: \u0026#39;ANE Toolkit\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2012-06-07\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;anetoolkit\u0026#39;}, 4{\u0026#39;title\u0026#39;: \u0026#39;cocos2d-x-filters\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2015-05-06\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;cocos2d-x-filters\u0026#39;}, 5{\u0026#39;title\u0026#39;: \u0026#39;我的Firefox插件\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2006-05-23\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;firefox-addons\u0026#39;}, 6{\u0026#39;title\u0026#39;: \u0026#39;Flash＆Flex大全\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2005-11-02\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;flashassistant\u0026#39;}, 7{\u0026#39;title\u0026#39;: \u0026#39;提问的智慧\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2005-10-08\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;howtoask\u0026#39;}, 8{\u0026#39;title\u0026#39;: \u0026#39;Linux软件\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2009-04-30\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;linux-software\u0026#39;}, 9{\u0026#39;title\u0026#39;: \u0026#39;Platform ANEs\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2013-08-22\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;platform-anes\u0026#39;}, 10{\u0026#39;title\u0026#39;: \u0026#39;阅读\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2015-03-03\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;read\u0026#39;}, 11{\u0026#39;title\u0026#39;: \u0026#39;Sprite Sheet Editor\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2011-08-18\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;sprite_sheet_editor\u0026#39;}, 12{\u0026#39;title\u0026#39;: \u0026#39;SpriteSheetPacker\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2011-04-19\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;spritesheetpacker\u0026#39;}, 13{\u0026#39;title\u0026#39;: \u0026#39;WordPress大全\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2006-03-07\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;wordpressfavorite\u0026#39;}, 14{\u0026#39;title\u0026#39;: \u0026#39;WPCMD\u0026#39;, \u0026#39;time\u0026#39;: \u0026#39;2015-06-12\u0026#39;, \u0026#39;name\u0026#39;: \u0026#39;wpcmd\u0026#39;} 15] 首先，排序需要一个可以比较的对象，我使用键名为 index 中的对象：\n1from datetime import date 2 3for item in pages: 4\tt = item[\u0026#39;time\u0026#39;].split(\u0026#39;-\u0026#39;) 5\titem[\u0026#39;index\u0026#39;] = date(int(t[0]), int(t[1]), int(t[2])) date 的实例是可比较的（它实现了 __lt__ 那一套方法）， date(2012,2,14) \u0026lt; data(2005, 11, 2) == False 。\n然后，对 pages 调用 sort 方法：\n1pages.sort(key=lambda item : item[\u0026#39;index\u0026#39;]) 在这里，我需要为 key 传递一个函数，这个函数能返回需要比较的值。\n当然，也可以使用 operator 提供的 itemgetter 方法来获取这个待比较的值。\n1from operator import itemgetter 2names.sort(key=itemgetter(\u0026#39;index\u0026#39;)) 除了 itemgetter 之外， operator 模块还提供了 attrgetter 和 methodcaller 。\n张贺 对上面提到的 Sorting Mini-HOW TO 做了一些必要的中文评注，该文和 Sorting HOW TO 基本相同。\n如果想要再了解一点，可以看这篇 通过某个关键字排序一个字典列表 和 排序不支持原生比较的对象 。\n当然，你最好先把 Sorting HOW TO 看完。\n（全文完）\n","date":"2015-06-20","description":"","lastmod":"2015-06-20T12:17:07Z","slug":"sort-dict-list-in-python3","tags":["python"],"title":"Python3 对 dict list 进行排序","url":"https://blog.zengrong.net/post/sort-dict-list-in-python3/"},{"categories":["impressions"],"content":" 开始日期： 2015-06-12 读完日期： 2015-06-14 书籍介绍： 编号1512 本篇笔记算是 《硬派健身》 和 《囚徒健身》 的读书笔记合集。\n我的健身历史 最早接触健身知识大概是在16岁的时候。那时候读书比较广泛也有时间，什么书都拿来读。有空喜欢去旧书摊淘书，然后就买回来两本健身书。现在想起来，那两本书讲的内容还是蛮靠谱的，至少给我的健身知识打下了不错的基础，我健身的过程中没有走弯路。\n由于个人非常喜欢运动，上学时篮球、田径、排球、舞蹈等运动量都非常大，身材也一直比较标准，就没有进行刻意的肌肉训练。毕业的时候，是65公斤，其实还是有些偏瘦的。\n毕业之后，体重慢慢增长到75公斤，然后就没有继续增长，尽管如此，我对身材还是不够满意。去年读到 《囚徒健身》 ，感觉作者比较有意思，里面的方法也简单易行（主要是不用去健身房），于是就坚持了10个月，一直到现在（70公斤）。\n《囚徒健身》 现在，我已经把作者提到的六艺中的 深蹲、举腿、桥 练到了最终式。把 俯卧撑 练到了第九式， 引体向上 练到了第六式， 倒立撑 第三式。\n到了这里就碰到了瓶颈， 俯卧撑第九式练了 3 个月都无法进入到最终式。肱三头肌的增长明显，但胸大肌的增长我并不满意。我意识到可能需要更系统地复习一下健身知识了。16 岁时读的那本书已经不知道去了哪里，正好知乎的《硬派健身》出书了，就买了这本。\n《硬派健身》 VS 《囚徒健身》 《囚徒健身》的作者 保罗-威德 非常鄙视健身房，认为健身房练出的并非是真正的男人肌肉；而《硬派健身》的作者斌卡则正好相反，他认为徒手锻炼的效果因人而异，无论对初学者还是有一定经验的健身者，徒手锻炼都不是最好的方法。\n当然，这两本书都是不错的书，要理解两个作者完全相悖的观点，则要从另一个角度来看。\n保罗-威德 本来是个囚犯，在缺少健身设备的监狱里呆久了，想法会比较偏激。他的训练理论偏重于实际，易学、易用、易开始，同时由于他的训练对象是囚犯，他需要一些激进的观点（健身房都是些娘娘腔的家伙）让囚犯们易于接受。况且，他教授的是保命技巧，因此训练中更偏重于力量而不是形体。从这个角度来看，他的观点是完全合乎他的目标的。\n斌卡的目标则不同，他更加注重形体而不是力量。他的读者有男有女，他更要考略普适性和安全性。另外，《硬派健身》本来是在知乎连载，文章的读者理工狗偏多，不拿出点材料、表格、引用的话可能会不能服众。城市里面健身房本就普遍，去健身房锻炼也更符合这本书的读者需求。\n所以，究竟从这两本书中学到什么，还是要看读书的人。 “尽信书不如无书” ，了解自己的身体，从两本书中找到适合自己的方法才是最好的。\n深蹲和俯卧撑 我已经做到单腿深蹲 15X3 组以上，但并没有使用斌卡说到的标准的深蹲动作，《囚徒健身》中并不强调动作，而是强调过程。虽然由于前面《囚徒健身》打下的基础，现在切换到徒手标准深蹲毫无压力，但我还是觉得标准深蹲是必须掌握的技能。如果刚刚开始深蹲，最好还是从斌卡提到的标准深蹲动作做起。\n要理解标准深蹲，就要理解“向后蹲”和“向下蹲”的区别。《囚徒健身》中提到的深蹲并没有要求动作，所以我做的是“向下蹲”。向后蹲的动作要点是“向后挺腰成微微反弓”，上半身保持正直向前，“保证膝盖不要超过脚尖”。可以使用沙发深蹲（初级）、拉力深蹲（中级）和面壁深蹲（高级）来调整自己的动作。\n对于俯卧撑，则需要让胸肌感觉到拉伸。我最近一直在找这种胸肌拉伸的感觉，逐渐把依赖肱三头肌的俯卧撑转换到依赖胸肌上来。窄距俯卧撑主要锻炼肱三头肌，而宽距俯卧撑才会更多地锻炼胸大肌。\n注意力与肌肉辅助 无论是什么动作，意随心动是非常重要的健身诀窍。在做动作的时候，需要把所有的注意力放到锻炼的那块肌肉上去，才能达到更好的效果。\n当你的其他肌肉力量足够强大的时候，往往会发生更大的作用，而自己希望锻炼到的肌肉则工作不够。\n例如仰卧起坐就很容易锻炼到背部肌肉，而腹部的锻炼效果就不好了。这也是《囚徒健身》不推荐仰卧起坐的原因。这种情况下，就必须集中注意力在腹肌，不再利用腿部、腰部和背部，仅仅使用腹肌来拉伸身体。\n锻炼时机与休息 按《硬派健身》所说，工作之后锻炼并不会对健康的身体有什么影响。在工作日，我也只有晚上回到家（20:30之后）才有时间锻炼，到现在为止并没有什么问题。只是在开了一个多小时车之后，身体上的确也比较累了，只有做了两组动作之后，才能进入到锻炼状态中，而且会明显感到没有白天锻炼那么投入。\n锻炼之后的休息十分重要，即使是一周六练，也不应该每天都锻炼相同的部位。我目前是每周五练，准备采用《硬派健身》推荐的 臀腿、肩腰腹、胸\u0026amp;肱三头肌 的方式，这应该是目前最适合我的方式。\n《囚徒健身》则没有特别强调锻炼时间，但也提到了需要在初学的时候注意休息。按我先前的锻炼方式，能感觉到连续锻炼同一个部位，会让肌肉产生疲劳，即使总数量提升了，但肌肉的灼烧感并没有那么强烈。反而是修改 2 天之后的锻炼能明显感觉到力量的提升。\n饮食 健身房系的健身锻炼，无一例外地会推荐吃蛋白粉，《硬派健身》也不能免俗。而我一直对蛋白粉比较排斥。想想《囚徒健身》的条件吧，或许连饭都吃不好，何来蛋白粉？古希腊的运动员们有着完美的身材，他们可没有蛋白粉。\n不得不承认的是，从现代健身理论来说，蛋白粉是很重要的一环。然而，对于刚刚开始健身锻炼的新手（半年以内）来说，可以不考虑蛋白粉，但要保持足够好的饮食。入门之后如果能坚持下来，再考虑也不迟。请自行判断： 健身吃蛋白粉对身体是否有损害？\n掷铁饼者\n休息的赫拉克勒斯（神的身材脱胎于人）\n于我而言，多吃点鸡蛋，不暴饮暴食，不克制饮食，多喝牛奶，顺其自然就好啦。\n健身的目的 关于健身，每个人都有自己的目的。而 坚持，保持平常心 才是常态。健身不可能一蹴而就，也不必踌躇满志地想：“万一我练成 Arnold Schwarzenegger 那样子，好恐怖！！“\n个人认为，对于大多数人而言，不必追求健身房中的 最强训练 ，只需要坚持训练，达到流线型的身材，有爆发力，看起来赏心悦目即可。\n《硬派健身》和《囚徒健身》中对肌肉群的描述都不够详细，主要是缺少图例所致。若是真的纠结书中提到的那些肌肉到底在哪里，有必要再参考这本书 《肌肉健美训练图解》 ，了解肌肉的位置和形状，对指导发力，找对感觉也十分重要。\n（全文完）\n","date":"2015-06-14","description":"","lastmod":"2015-06-14T14:18:10Z","slug":"touch-workout-and-convict-conditioning","tags":["life","live","reading"],"title":"【读书笔记】《硬派健身》+《囚徒健身》","url":"https://blog.zengrong.net/post/touch-workout-and-convict-conditioning/"},{"categories":[],"content":"我 2003 年开始写博客，经历过一次数据丢失，现在的博客 blog.zengrong.net 从2005 年起开始更新，一直没有中断。虽然 WordPress 的编辑功能越来越强大，而且也有大量的博客写作工具，但我都用不习惯。我总希望用一种更方便更简单（更适合程序员）的方式来管理博客。\n2014 年的时候我考虑过 博客静态化，但现有的博客静态化工具不太符合我的要求，因此我准备自己造个轮子。造轮子的工程未免复杂，为了满足在轮子诞生之前的更新欲望，WPCMD 诞生了。\nWPCMD 的源码托管在 Github 上。\n1. WPCMD 是什么 WPCMD(WordPress command) 是一个通过 WordPress XML-RPC 接口在本地创建、更新 WordPress 博客的命令行工具。 zengrong.net 就是使用该工具进行管理。\n简单的说，WPCMD 就是一个用于 WordPress 的命令行工具。而且，由于没有 GUI 界面，这个工具是主要面向技术类博主的。\n这是一些优点：\n使用 MarkDown 语法写博客； 随意选择自己最喜欢的版本管理来保存博客文章（例如我用 Github ）； 随意选择自己最喜欢的编辑器编写博客（例如我一直用 Vim ）； 生成 所有文章的列表 ； 生成文章的 HTML 文件； 不用打开 WordPress 后台就能完成： 文章和页面的创建和更新； 文章内媒体文件的自动上传； 分类和标签的创建和更新； 查看博客文章/页面/分类/标签/媒体等信息。 使用 Fenced Code Extra 支持： graphviz ； 语法高亮； 代码注释。 同时管理多个 WordPress 博客。 2. Hello WPCMD 快速看一下基本用法：\n2.1 显示博客信息 1wpcmd show -t option 2 3// blog_title=\u0026#34;zrong\u0026amp;#039;s blog\u0026#34; 4// image_default_size=\u0026#34;\u0026#34; 5// medium_size_w=\u0026#34;300\u0026#34; 6// blog_tagline=\u0026#34;可能是一个程序员\u0026#34; 7// stylesheet=\u0026#34;responsive_child\u0026#34; 8// thumbnail_crop=\u0026#34;1\u0026#34; 9// image_default_align=\u0026#34;\u0026#34; 10// large_size_h=\u0026#34;1024\u0026#34; 11// medium_size_h=\u0026#34;300\u0026#34; 12// thumbnail_size_h=\u0026#34;150\u0026#34; 13// time_format=\u0026#34;H:i\u0026#34; 14// default_ping_status=\u0026#34;open\u0026#34; 15// default_comment_status=\u0026#34;open\u0026#34; 16// home_url=\u0026#34;http://zengrong.net\u0026#34; 17// users_can_register=\u0026#34;0\u0026#34; 18// blog_url=\u0026#34;http://zengrong.net\u0026#34; 19// post_thumbnail=\u0026#34;True\u0026#34; 20// thumbnail_size_w=\u0026#34;150\u0026#34; 21// large_size_w=\u0026#34;1024\u0026#34; 22// date_format=\u0026#34;m/d/Y\u0026#34; 23// software_version=\u0026#34;4.3.1\u0026#34; 24// time_zone=\u0026#34;8\u0026#34; 25// software_name=\u0026#34;WordPress\u0026#34; 26// template=\u0026#34;responsive\u0026#34; 27// image_default_link_type=\u0026#34;\u0026#34; 2.2 显示最新的5篇文章的基本信息 1wpcmd show -t post -n 5 2 3// id=2374, date=2015-10-17 13:52:59, date_modified=2015-10-20 08:30:41, slug=open-source-mirror-site, title=常用开源镜像站整理, post_status=publish, post_type=post 4// id=2370, date=2015-09-30 02:49:44, date_modified=2015-09-30 02:57:02, slug=the-sign-of-the-beaver, title=《海狸的记号》读书笔记, post_status=publish, post_type=post 5// id=2369, date=2015-09-25 03:56:49, date_modified=2015-09-25 04:13:27, slug=search-symbol-in-search-engine, title=在搜索引擎中搜索特殊字符, post_status=publish, post_type=post 6// id=2367, date=2015-09-22 03:43:21, date_modified=2015-09-22 04:12:25, slug=int64-and-buffer-ts-version-for-egret, title=Int64.ts and Buffer.ts for Egret, post_status=publish, post_type=post 7// id=2366, date=2015-09-13 13:02:00, date_modified=2015-09-13 13:16:32, slug=use-slimerjs-to-grab-pages-under-cloudflare-ddos-protection, title=使用 slimerjs 抓取 DDos 保护的站点, post_status=publish, post_type=post 2.3 显示所有分类信息 1wpcmd show -t category 2 3// use {\u0026#39;description\u0026#39;: \u0026#39;应用技巧\u0026#39;, \u0026#39;taxonomy\u0026#39;: \u0026#39;category\u0026#39;, \u0026#39;slug\u0026#39;: \u0026#39;use\u0026#39;, \u0026#39;count\u0026#39;: 55, \u0026#39;name\u0026#39;: \u0026#39;应用\u0026#39;, \u0026#39;taxonomy_id\u0026#39;: \u0026#39;12\u0026#39;, \u0026#39;id\u0026#39;: \u0026#39;12\u0026#39;, \u0026#39;parent\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;group\u0026#39;: \u0026#39;0\u0026#39;} 4// others {\u0026#39;description\u0026#39;: \u0026#39;什么都有\u0026#39;, \u0026#39;taxonomy\u0026#39;: \u0026#39;category\u0026#39;, \u0026#39;slug\u0026#39;: \u0026#39;others\u0026#39;, \u0026#39;count\u0026#39;: 86, \u0026#39;name\u0026#39;: \u0026#39;乱弹\u0026#39;, \u0026#39;taxonomy_id\u0026#39;: \u0026#39;8\u0026#39;, \u0026#39;id\u0026#39;: \u0026#39;8\u0026#39;, \u0026#39;parent\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;group\u0026#39;: \u0026#39;0\u0026#39;} 5// impressions {\u0026#39;description\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;taxonomy\u0026#39;: \u0026#39;category\u0026#39;, \u0026#39;slug\u0026#39;: \u0026#39;impressions\u0026#39;, \u0026#39;count\u0026#39;: 61, \u0026#39;name\u0026#39;: \u0026#39;感悟\u0026#39;, \u0026#39;taxonomy_id\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;id\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;parent\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;group\u0026#39;: \u0026#39;0\u0026#39;} 6// news {\u0026#39;description\u0026#39;: \u0026#39;听到的消息和要说消息，某些转载的文章也在这里\u0026#39;, \u0026#39;taxonomy\u0026#39;: \u0026#39;category\u0026#39;, \u0026#39;slug\u0026#39;: \u0026#39;news\u0026#39;, \u0026#39;count\u0026#39;: 76, \u0026#39;name\u0026#39;: \u0026#39;听说\u0026#39;, \u0026#39;taxonomy_id\u0026#39;: \u0026#39;7\u0026#39;, \u0026#39;id\u0026#39;: \u0026#39;7\u0026#39;, \u0026#39;parent\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;group\u0026#39;: \u0026#39;0\u0026#39;} 7// web {\u0026#39;description\u0026#39;: \u0026#39;与网站相关的程序、CMS系统、技巧、内容\u0026#39;, \u0026#39;taxonomy\u0026#39;: \u0026#39;category\u0026#39;, \u0026#39;slug\u0026#39;: \u0026#39;web\u0026#39;, \u0026#39;count\u0026#39;: 54, \u0026#39;name\u0026#39;: \u0026#39;网站\u0026#39;, \u0026#39;taxonomy_id\u0026#39;: \u0026#39;11\u0026#39;, \u0026#39;id\u0026#39;: \u0026#39;11\u0026#39;, \u0026#39;parent\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;group\u0026#39;: \u0026#39;0\u0026#39;} 8// technology {\u0026#39;description\u0026#39;: \u0026#39;技术文章\u0026#39;, \u0026#39;taxonomy\u0026#39;: \u0026#39;category\u0026#39;, \u0026#39;slug\u0026#39;: \u0026#39;technology\u0026#39;, \u0026#39;count\u0026#39;: 478, \u0026#39;name\u0026#39;: \u0026#39;技术\u0026#39;, \u0026#39;taxonomy_id\u0026#39;: \u0026#39;22\u0026#39;, \u0026#39;id\u0026#39;: \u0026#39;22\u0026#39;, \u0026#39;parent\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;group\u0026#39;: \u0026#39;0\u0026#39;} 9// design {\u0026#39;description\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;taxonomy\u0026#39;: \u0026#39;category\u0026#39;, \u0026#39;slug\u0026#39;: \u0026#39;design\u0026#39;, \u0026#39;count\u0026#39;: 8, \u0026#39;name\u0026#39;: \u0026#39;设计\u0026#39;, \u0026#39;taxonomy_id\u0026#39;: \u0026#39;9\u0026#39;, \u0026#39;id\u0026#39;: \u0026#39;9\u0026#39;, \u0026#39;parent\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;group\u0026#39;: \u0026#39;0\u0026#39;} 10// tutorial {\u0026#39;description\u0026#39;: \u0026#39;培训相关信息\u0026#39;, \u0026#39;taxonomy\u0026#39;: \u0026#39;category\u0026#39;, \u0026#39;slug\u0026#39;: \u0026#39;tutorial\u0026#39;, \u0026#39;count\u0026#39;: 12, \u0026#39;name\u0026#39;: \u0026#39;培训\u0026#39;, \u0026#39;taxonomy_id\u0026#39;: \u0026#39;18\u0026#39;, \u0026#39;id\u0026#39;: \u0026#39;18\u0026#39;, \u0026#39;parent\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;group\u0026#39;: \u0026#39;0\u0026#39;} 2.4 更新名称为 wpcmd 的页面的内容 该命令找到名称为 wpcmd 的页面的 markdown 源码，然后将其转换成 HTML ，再将 HTML 更新到 WordPress 博客（wpcmd 页面对应的编号是2321）：\n1wpcmd update -t page -q wpcmd 2 3// Old article: 4// id=2321, date=2015-06-12 01:40:22, date_modified=2015-10-21 08:04:02, slug=wpcmd, title=WPCMD, post_status=publish, post_type=page 5// Update 2321 successfully! 3. 安装和配置 3.1 依赖 python 3.4 or higher Markdown\u0026gt;=2.6.2 Pygments\u0026gt;=2.0.2 python-wordpress-xmlrpc\u0026gt;=2.3 rookout\u0026gt;=0.4.5 3.2 安装 pip3 install wpcmd\n因为包比较多，想快点也可以使用国内的镜像站来安装，下面使用的是中国科学技术大学的镜像源：（详情可参考 常用镜像站整理）：\npip3 install -i https://mirrors.ustc.edu.cn/pypi/web/simple wpcmd\n3.3 配置 输入 wpcmd -h ，第一次运行会生成一个默认的配置文件，必须修改这个配置文件进行设置。这个配置文件位于 ~/.wpcmd.ini（OS X 和 Linux) 和 %HOME%\\_wpcmd.ini （Windows） 。\n配置文件默认的内容如下：\n1[site] 2 3name = my blog 4url = http://my blog/xmlrpc.php 5user = myname 6password = password123456 7# Mac or Linux 8cachefile = /Users/zrong/.wpcmd.cache.py 9# Windows 10# cachefile = C:\\Users\\zrong\\_wpcmd.cache.py 11 12# file 13 14ext = .md 15draftfmt = draft_%s 16 17# directory 18 19# Mac or Linux 20work = /Users/zrong/blog 21# Windows 22# cachefile = C:\\Users\\zrong\\blog 23draft = draft 24page = page 25post = post 26output = output 27media = media 其中，name 为博客的名称，url 为博客的 xmlrpc.php 地址，user 和 password 为博客的管理密码，这些都是必须填写的。\nwork 代表 blog 源码所在的文件夹（绝对路径），其下的几个设置为相当于 work 的文件夹： draft 还没发布的文章源码； page 已经发布的页面； post 已经发布的文章； output 若要将 markdown 源码转换成 html ，则会写入这个文件夹； media 博客中使用的媒体文件，例如图片、资源、提供下载的压缩包。 要了解这些文件夹的具体内容，可参考 本博客源码 。\n3.4 多博客配置 WPCMD 可以很容易管理多个 WordPress 博客。毕竟多个博客就是多个配置而已。\n在配置文件中复制一份 [site] section 中的内容，然后将 [site] 改成 [site1] ，再修改 [site1] 下面的 name/url/user/password/work 等重要配置即可。\n在使用的时候，可以通过 --site 参数指定你的博客配置。例如要查看新配置的博客的基本信息，可以这样写：\n1wpcmd show --site site1 -t option 这样，通过为 --site 参数指定不同的 section 名称，就能管理你的多个博客了。\n3.5 代码高亮配置 WPCMD 使用 Pygments 来实现代码高亮，因此，我们需要生成一个 css 文件加入到 WordPress 的模版中，代码高亮才能生效。\n安装了 WPCMD 后，就可以使用 pygmentize 命令来生成这个 css 文件。\n查看所有支持的 style：\n1-\u0026gt; % pygmentize -L styles 2Pygments version 2.0.2, (c) 2006-2014 by Georg Brandl. 3 4Styles: 5~~~~~~~ 6* paraiso-dark: 7 8* default: 9 The default style (inspired by Emacs 22). 10* emacs: 11 The default style (inspired by Emacs 22). 12* murphy: 13 Murphy\u0026#39;s style from CodeRay. 14* igor: 15 Pygments version of the official colors for Igor Pro procedures. 16* perldoc: 17 Style similar to the style used in the perldoc code blocks. 18* xcode: 19 Style similar to the Xcode default colouring theme. 20* monokai: 21 This style mimics the Monokai color scheme. 22* colorful: 23* manni: 24* pastie: 25* rrt: 26* bw: 27 28* paraiso-light: 29 30* trac: 31* tango: 32* native: 33* autumn: 34* friendly: 35* vs: 36* vim: 37* borland: 38* fruity: 然后选择一个自己喜欢的格式生成 css 文件。在下面的代码中，我使用 -S 参数指定生成类似于 vim 7.0 的代码高亮效果，并指定了一个文件名。\n1-\u0026gt; % pygmentize -S vim -f html \u0026gt; codehilite.css 接着，把这个生成的 css 文件上传到 WordPress 文件夹，并在 WordPress 模版的 style.css 文件中加入这一行（注意修改路径，我使用的是子模板）：\n1@import url(\u0026#34;/wp-content/themes/twentyfifteen_child/codehilite.css\u0026#34;); 4. 使用 WPCMD 有4个子命令，主要作用如下：\nnew\n创建新的文章、页面、分类或标签； update\n更新已有的文章、页面、分类或标签。这是使用最频繁的命令； show\n显示指定 WordPress 站点的文章、页面、分类、标签、选项和媒体文件信息； util\n一些工具，最有用的就是生成文章列表页面。 要查看这些子命令的详细帮助，可以使用 wpcmd {subcommand} -h 。例如 wpcmd update -h 就查看 update 命令的详细用法。\n4.1 new 使用 new 命令可以创建4种类型的内容：\ncategory 对应 WordPress 中的分类目录。 tag 对应 WordPress 中的标签； post 对应 WordPress 中的文章； page 对应 WordPress 中的页面； 4.1.1 创建分类目录 下面的命令创建一个名为 PHP ，别名为 php ，描述为 『世界上最好的语言』的分类目录。\n1-\u0026gt; % wpcmd new -t category -q php \u0026#34;PHP\u0026#34; \u0026#34;世界上最好的语言\u0026#34; 2Save \u0026#39;/Users/zrong/.wpcmd.cache2.py\u0026#39; done. 3Get term from WordPress, query: [\u0026#39;category\u0026#39;, \u0026#39;php\u0026#39;], result: None 4The term PHP(21) has created. 5Save \u0026#39;/Users/zrong/.wpcmd.cache2.py\u0026#39; done. 6The term PHP has saved. 也可以简化一下，只为 -q 提供一个参数。这样就会创建一个别名和名称都是 php 的标签，但这样一来，PHP 就不是世界上最好的语言了：\n1-\u0026gt; % wpcmd new -t category -q php 4.1.2 创建标签 在 WordPress 的数据库中，无论是标签还是分类目录，都是放在 wp_terms 表中，它们的区别仅仅是 taxonomy 不同。一个是 category ，一个是 post_tag 。\n所以，创建一个标签，只需要为 -t 参数传递 post_tag 即可。我在处理标签的时候做了简化，使用 tag 和 post_tag 都是可以的。\n1-\u0026gt; % wpcmd new -t tag -q python Python \u0026#34;Python 语言相关\u0026#34; 2Save \u0026#39;/Users/zrong/.wpcmd.cache2.py\u0026#39; done. 3Get term from WordPress, query: [\u0026#39;post_tag\u0026#39;, \u0026#39;python\u0026#39;], result: None 4The term Python(20) has created. 5Save \u0026#39;/Users/zrong/.wpcmd.cache2.py\u0026#39; done. 6The term Python has saved. 4.1.3 创建文章 创建的文章处于 draft 文件夹中。下面的代码将创建 draft/draft_wpcmd.md 这篇文章：\n1-\u0026gt; % wpcmd new -t post -q wpcmd 2The draft file \u0026#34;/Users/zrong/blog/draft/draft_wpcmd.md\u0026#34; has created. 也可以提供 -q 参数，这样会自动用数字作为文件名：\n1-\u0026gt; % wpcmd new -t post 2The draft file \u0026#34;/Users/zrong/blog/draft/draft_1.md\u0026#34; has created. 创建出来的文章的默认内容如下：\n1title: 2date: 2015-11-28 21:44:38 3modified: 2015-11-28 21:44:38 4author: zrong 5postid: $POSTID 6slug: $SLUG 7nicename: 8attachments: $ATTACHMENTS 9posttype: post 10poststatus: draft 11tags: 12category: technology 这里的 Metadata 是使用 Python-Markdown 的 Metadata 插件修改版 来解析的的。默认的插件认为 Metadata 是无序的，而 WPCMD 需要它们是有序的。\n我们需要对这些 Metadata 填充内容。下面是个具体填写说明：\n1title: 这里填写标题 2date: 创建日期 3modified: 修改日期 4author: 作者 5postid: 不必修改，这个值会在提交到 WordPress 之后自动替换成 Post ID 的值 6slug: 不必修改，将被自动替换，一般情况下值与 nicename 相同 7nicename: 填写 URL 友好的名称，若不填写则会自动使用 title 的 BASE64 编码 8attachments: 不必修改，文中若包含图片等上传的内容，则该值被替换成这些上传内容的 Post ID 9posttype: 值为 post 或者 page 10poststatus: 值为 draft（不可发布）或者 publish（允许发布到 WordPress） 11tags: 标签，允许为空，使用英文半角逗号分隔，标签必须在 WordPress 中存在 12category: 分类目录，必填，必须在 WordPress 中存在 接下来，就可以使 Markdown 语法进行博客的撰写了。支持下面的插件：\nFenced Code Extra Tables CodeHilite Table of Contents 这里有 大量的源码 可以参考。\n4.1.4 创建页面 创建页面和创建文章的方法类似，只是需要为 -t 参数传递 page 。\n1-\u0026gt; % wpcmd new -t page 2The draft file \u0026#34;/Users/zrong/blog/draft/draft_2.md\u0026#34; has created. 生成的文档也类似，不过 Metadata 会不包含 tag 和 category ：\n1title: 2date: 2015-11-28 22:23:00 3modified: 2015-11-28 22:23:00 4author: zrong 5postid: $POSTID 6slug: $SLUG 7nicename: 8attachments: $ATTACHMENTS 9posttype: page 10poststatus: draft 4.2 update 使用 update 命令可以更新6种类型的内容：\ncategory 对应 WordPress 中的分类目录。 tag 对应 WordPress 中的标签； post 对应 WordPress 中的文章； page 对应 WordPress 中的页面； option 对应 WordPress 中的设置选项； draft 对应在 WordPress 中尚不存在（但存在于 draft 文件夹）中的草稿。 4.2.1 更新分类目录和标签 更新这两者的语法和 new 完全相同，只是需要将 new 换成 update：\n1-\u0026gt; % wpcmd update -t category -q php \u0026#34;PHP\u0026#34; \u0026#34;世界上最糟的语言\u0026#34; 4.2.2 更新文章和页面 这分为两种情况：在 WordPress 中还不存在的文章，以及已经存在于 WordPress 中的文章。\n对于在 WordPress 中 还不存在 的新文章来说，需要使用 -t draft 类型来进行更新。下面的命令把 draft/draft_1.md 这篇文章更新到 WordPress 上（ -q 参数不必添加 draft_ 前缀）：\n1-\u0026gt; % wpcmd update -t draft -q 1 在这种更新过程中，WPCMD 将会做下面的事：\n将 Markdown 格式转换成 HTML 格式； 将 HTML 格式发布到 WordPress 上； 获取新文章的 Post ID，用它替换 Markdown 源文件的 Metadata 中的 $POSTID ; 更新 $SLUG 的值； 将 draft/draft_1.md 文件移动并改名为 post/{postid}.md ，postid 就是刚才得到的值。 对于 已存在 于 WordPress 的文章或者页面，就需要使用 -t post 或者 -t page 类型了：\n1-\u0026gt; % wpcmd update -t page -q wpcmd 2Old article: 3id=2321, date=2015-06-12 01:40:22, date_modified=2015-11-22 14:04:15, slug=wpcmd, title=WPCMD, post_status=publish, post_type=page 4Update 2321 successfully! 在这种更新过程中，WPCMD 会做上面的1、2两步，后面的步骤都不会发生。\n注意，由于更新文章是最常见的操作，因此 -t 参数的默认值就是 post 。更新文章时，可以仅仅提供 -q 参数。\n4.2.3 在文章和页面中包含图像 WPCMD 会自动检测文章中的图像文件，将其上传到 WordPress 中。但这需要遵循一些特殊的规则：\n要上传的图像必须放在 media/draft/ 文件夹中； 必须使用 media/draft/imagefile 的形式提供图像文件的 URL。下面是个范例： ![myimage](media/draft/myimage.jpg)\n当然你也可以采用另一种语法：\nSome text ....\n![myimage][img1]\nAnother text ....\n[img1]: media/draft/myimage.jpg\n使用这种规则，在更新文章和页面的时候，WPCMD 会做下面的事：\n自动扫描复合规则的图像，将其上传到 WordPress 中； 获得已上传文件的 Post ID ，用它替换 Markdown 源文件的 Metadata 中的 $ATTACHMENTS ； 获取已上传文件的 URL 地址，用它替换 media/draft/myimage.jpg ； 在 media 文件夹下建立对应的年、月文件夹，讲这张已经上任的图像文件移动到对应的文件夹中。 在完成上述操作后，上面的 Markdown 源码会变成如下所示（以 zengrong.net 为例）：\n1![myimage](/wp-content/upload/2015/11/myimage.jpg) 同时 media/draft/myimage.jpg 将移动到 media/2015/11/myimage.jpg 。\n4.2.4 输出 HTML 格式源码 为了提前查看效果，我们也可以不将内容发布到 WordPress ，而是先行输出一个 HTML 文件查看效果。使用 update 命令的 -o 参数指定输出文件名即可完成：\n1-\u0026gt; % wpcmd update -t page -q wpcmd -o wpcmd.html 上面的命令会生成文件 output/wpcmd.html 。\n注意这里生成的文件是一个不完整的 HTML 文件，仅包含正文的 HTML 信息，没有 \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;body\u0026gt; 等标签。因此若包含中文，直接用浏览器打开会显示乱码。此时手动指定一下页面编码为 UTF-8 即可。\n4.3 show show 命令显示博客的信息，使用 -t 或 --type 参数指定信息的类型，目前有这样几种类型可以显示：\noption 从 Wordpress 中查询博客的基础信息并显示 post（远程列表） 从 Wordpress 中查询博客文章列表并显示 page（远程列表） 从 Wordpress 中查询博客页面列表并显示 draft（本地列表） 显示位于 draft 文件夹中的的所有草稿 markdown 源文件路径 tax（远程列表） 从 Wordpress 中查询所有可用的分类类型 term 使用 tax 类型查到的分类类型，显示这些分类关键词列表 category（本地列表） 从本地缓存中查询博客中的文章分类并显示 tag（本地列表） 从本地缓存查询博客中的 tag 并显示 medialib（远程列表） 从 Wordpress 中查询博客中已经上传的媒体文件信息 mediaitem（远程列表） 从 Wordpress 中查询博客中已经上传的某个媒体文件信息，需要指定一个媒体文件 ID 4.3.1 post 和 page 对于 post 和 page 这两种类型，可以使用 -n 或者 --number 来指定显示几条信息。在我的博客上执行下面的命令，会显示最新的两篇文章的基本信息：\n1-\u0026gt; % wpcmd show -t post -n 2 2id=2441, date=2016-02-12 12:30:54, date_modified=2016-02-12 13:50:44, slug=pebble-classic, title=Pebble Classic 的售后和花屏维修, post_status=publish, post_type=post 3id=2434, date=2016-01-29 09:30:17, date_modified=2016-01-30 15:35:11, slug=how-to-choose-in-management-and-technology, title=在技术和管理中选择, post_status=publish, post_type=post 使用 -d 或者 --order 可以实现列表排序，默认是按时间倒序 DESC ，如果设置为 ASC 则显示最老的两篇文章:\n1-\u0026gt; % wpcmd show -t post -n 2 -d ASC 2id=23, date=2005-04-25 05:21:48, date_modified=2005-10-10 05:28:52, slug=creative-commons, title=创作共用（Creative Commons） , post_status=publish, post_type=post 3id=19, date=2005-04-27 15:14:22, date_modified=2007-12-30 15:03:11, slug=display_errors, title=终于解决了Mambo出错的问题, post_status=publish, post_type=post 使用 -o 或者 --orderby 参数可以指定是按 post_modified 排序还是按 post_id 排序。\n4.3.2 medialib 和 mediaitem 下面的命令显示最新上传的两个媒体文件信息：\n1-\u0026gt; % wpcmd show -t medialib -n 2 2field:{\u0026#39;number\u0026#39;: 2} 3id=2442, parent=2441, title=watch3, description=, caption=, date_created=2016-02-12 13:32:20, link=http://zengrong.net/wp-content/uploads/2016/02/watch3.jpg, thumbnail=http://zengrong.net/wp-content/uploads/2016/02/watch3-150x150.jpg, metadata={\u0026#39;width\u0026#39;: 721, \u0026#39;file\u0026#39;: \u0026#39;2016/02/watch3.jpg\u0026#39;, \u0026#39;height\u0026#39;: 1280, \u0026#39;sizes\u0026#39;: {\u0026#39;medium\u0026#39;: {\u0026#39;width\u0026#39;: 169, \u0026#39;mime-type\u0026#39;: \u0026#39;image/jpeg\u0026#39;, \u0026#39;file\u0026#39;: \u0026#39;watch3-169x300.jpg\u0026#39;, \u0026#39;height\u0026#39;: 300}, \u0026#39;post-thumbnail\u0026#39;: {\u0026#39;width\u0026#39;: 721, \u0026#39;mime-type\u0026#39;: \u0026#39;image/jpeg\u0026#39;, \u0026#39;file\u0026#39;: \u0026#39;watch3-721x510.jpg\u0026#39;, \u0026#39;height\u0026#39;: 510}, \u0026#39;thumbnail\u0026#39;: {\u0026#39;width\u0026#39;: 150, \u0026#39;mime-type\u0026#39;: \u0026#39;image/jpeg\u0026#39;, \u0026#39;file\u0026#39;: \u0026#39;watch3-150x150.jpg\u0026#39;, \u0026#39;height\u0026#39;: 150}, \u0026#39;large\u0026#39;: {\u0026#39;width\u0026#39;: 577, \u0026#39;mime-type\u0026#39;: \u0026#39;image/jpeg\u0026#39;, \u0026#39;file\u0026#39;: \u0026#39;watch3-577x1024.jpg\u0026#39;, \u0026#39;height\u0026#39;: 1024}}, \u0026#39;image_meta\u0026#39;: {\u0026#39;iso\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;orientation\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;caption\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;credit\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;focal_length\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;camera\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;title\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;created_timestamp\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;copyright\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;shutter_speed\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;keywords\u0026#39;: [], \u0026#39;aperture\u0026#39;: \u0026#39;0\u0026#39;}} 4id=2440, parent=2441, title=watch4.jpg, description=, caption=, date_created=2016-02-12 13:27:29, link=http://zengrong.net/wp-content/uploads/2016/02/watch4.jpg, thumbnail=http://zengrong.net/wp-content/uploads/2016/02/watch4-150x150.jpg, metadata={\u0026#39;width\u0026#39;: 721, \u0026#39;file\u0026#39;: \u0026#39;2016/02/watch4.jpg\u0026#39;, \u0026#39;height\u0026#39;: 1280, \u0026#39;sizes\u0026#39;: {\u0026#39;medium\u0026#39;: {\u0026#39;width\u0026#39;: 169, \u0026#39;mime-type\u0026#39;: \u0026#39;image/jpeg\u0026#39;, \u0026#39;file\u0026#39;: \u0026#39;watch4-169x300.jpg\u0026#39;, \u0026#39;height\u0026#39;: 300}, \u0026#39;post-thumbnail\u0026#39;: {\u0026#39;width\u0026#39;: 721, \u0026#39;mime-type\u0026#39;: \u0026#39;image/jpeg\u0026#39;, \u0026#39;file\u0026#39;: \u0026#39;watch4-721x510.jpg\u0026#39;, \u0026#39;height\u0026#39;: 510}, \u0026#39;thumbnail\u0026#39;: {\u0026#39;width\u0026#39;: 150, \u0026#39;mime-type\u0026#39;: \u0026#39;image/jpeg\u0026#39;, \u0026#39;file\u0026#39;: \u0026#39;watch4-150x150.jpg\u0026#39;, \u0026#39;height\u0026#39;: 150}, \u0026#39;large\u0026#39;: {\u0026#39;width\u0026#39;: 577, \u0026#39;mime-type\u0026#39;: \u0026#39;image/jpeg\u0026#39;, \u0026#39;file\u0026#39;: \u0026#39;watch4-577x1024.jpg\u0026#39;, \u0026#39;height\u0026#39;: 1024}}, \u0026#39;image_meta\u0026#39;: {\u0026#39;iso\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;orientation\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;caption\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;credit\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;focal_length\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;camera\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;title\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;created_timestamp\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;copyright\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;shutter_speed\u0026#39;: \u0026#39;0\u0026#39;, \u0026#39;keywords\u0026#39;: [], \u0026#39;aperture\u0026#39;: \u0026#39;0\u0026#39;}} 从上面的信息中，我们可以知道 id 为 2442 的媒体文件是一张图片，也知道了它的尺寸、URL 路径以及所属的文章 ID 为 2441 。\n如果希望只看特定的媒体文件信息，可以指定媒体文件的 id：\n1-\u0026gt; % wpcmd show -t mediaitem -q 2442 4.3.3 category/tag 和 term/tax category 和 tag 直接显示本地缓存的文章分类和标签信息。在 Wordpress 中， tag 原名为 post_tag ，它和 category 一样都是 term 。\n因此，下面两条命令是同义语：\n1-\u0026gt; % wpcmd show -t tag 2-\u0026gt; % wpcmd show -t term -q post_tag 同样的，下面两条命令也是同义语：\n1-\u0026gt; % wpcmd show -t category 2-\u0026gt; % wpcmd show -t term -q category 使用 tax 可以查到 Wordpress 支持哪些 term ：\n1-\u0026gt; % wpcmd show -t tax 2category 3post_tag 4post_format 4.4 util util 是一些批量处理博客的小工具，目前仅有 -r , --readme 这条命令有意义。它会生成一个 README.md 文件，效果请查看 README.md 。\n（全文完）\n","date":"2015-06-12","description":"","lastmod":"2015-06-12T01:40:22Z","slug":"wpcmd","tags":[],"title":"WPCMD","url":"https://blog.zengrong.net/wpcmd/"},{"categories":["technology"],"content":"在 Python-Markdown 库中，有一个插件 Fenced Code Blocks 。使用它能够支持 PHP Markdown Extra 和 GitHub Flavored Markdown 格式的代码块。\n我对 Fenced Code Blocks 插件进行了扩展，使其可以支持以下功能：\n注释的代码块； Graphviz 支持。 扩展过的插件，名为 Fenced Code Extra ，暂时托管于我的 wpcmd 项目中。\n1. 注释的代码块 Markdown 本身是不支持注释的。这个扩展功能，就是让代码块能够支持注释。\n举个简单的例子，这段 Markdown 代码：\n## graph ``` # graph pic1 { a -- b a -- b b -- a [color=blue] } ``` ![Graphviz chart 2294-graphviz-0.png](/uploads/2015/06/2294-graphviz-0.png) 会被渲染成这样：\n1\u0026lt;h2\u0026gt;graph\u0026lt;/h2\u0026gt; 2\u0026lt;p\u0026gt;\u0026lt;img alt=\u0026#34;Graphviz chart 2294-graphviz-0.png\u0026#34; src=\u0026#34;/uploads/2015/06/2294-graphviz-0.png\u0026#34; /\u0026gt;\u0026lt;/p\u0026gt; 也就是说，在代码块开始指示符后面加上 # 符号（ # 后面可以带有其他字符），整个代码块就变成了注释，不会被渲染成 HTML。\n2. Graphviz 支持 在上一篇文章 GraphViz 简易教程 中，我介绍了 Graphviz 这个有趣且有用的工具。现在使用 Fenced Code Extra ，可以将位于 Markdown 中的 Graphviz 代码自动转换成图片，同时支持在 Markdown 源文件中仅保存图片、仅保存代码或者同时保存代码和图片。\n2.1 graphviz 范例 例如，有这样一段 Markdown ，保存在变量 txt 中：\n## graph ``` graphviz graph pic1 { a -- b a -- b b -- a [color=blue] } ``` 让我们使用 Markdown 库渲染它：\n1import markdown 2 3def _get_extra_output(md, name): 4 extra_output = getattr(md, \u0026#39;fenced_code_extra_output\u0026#39;, None) 5 if not extra_output: 6 return None 7 return extra_output.get(name) 8 9md = markdown.Markdown( 10 extensions=[ 11 \u0026#39;fenced_code_extra\u0026#39;, 12 ], 13 extension_configs={ 14 \u0026#39;fenced_code_extra\u0026#39;:{ 15 \u0026#39;graphviz\u0026#39;:{ 16 \u0026#39;OUTPUT_DIR\u0026#39;:\u0026#39;/home/wp/upload\u0026#39;, 17 \u0026#39;BASE_URL\u0026#39;:\u0026#39;/uploads\u0026#39;, 18 \u0026#39;NAME_PRE\u0026#39;:\u0026#39;graphviz\u0026#39;}, 19 } 20 } 21 ) 22 23html = md.convert(txt) 24 25graphviz = _get_extra_output(md, \u0026#39;graphviz\u0026#39;) 26if graphviz: 27 mdtxt = graphviz[\u0026#39;text\u0026#39;] 在上面，必须加入 markdown 的扩展参数，其中包含 fenced_code_extra 插件的 graphviz 设置。\nOUTPUT_DIR 指图像的输出文件夹； BASE_URL 指输出的图像被转换成的基础 URL； NAME_PRE 指输出文件名的前缀，可以设置成空字符串。 调用之后， html 的值将为：\n1\u0026lt;h2\u0026gt;graph\u0026lt;/h2\u0026gt; 2\u0026lt;p\u0026gt;\u0026lt;img alt=\u0026#34;Graphviz chart graphviz-0.png\u0026#34; src=\u0026#34;/uploads/graphviz-0.png\u0026#34; /\u0026gt;\u0026lt;/p\u0026gt; mdtxt 的值为：\n## graph ![Graphviz chart graphviz-0.png](/uploads/graphviz-0.png) mdtxt 是对 graphviz 代码进行处理之后的 Markdown 源码，可以将这个源码保存下来以便重复使用。\n2.2 生成的文件名 文件名的命名规则是 NAME_PRE-NAME-NUM.TYPE ，下面一一介绍：\nNAME_PRE 在 Markdown 的扩展参数中，上面有范例； NAME 在 graphviz 块开始的时候指定（下文详述），在上面的范例中并没有明确指定 NAME 的值，因此默认为空字符串； NUM 源码中可能有多个 graphviz 块，这样生成的文件也可能有多个。为了避免重名，NUM 是自动计算出的编号，从0开始； TYPE 在 graphviz 块开始的时候指定（下文详述），在上面的范例中并没有明确指定 TYPE 的值，因此默认为 png 。 2.3 config 让我们详细看看 graphviz 代码块支持的配置参数：\n## graph ``` graphviz_dot config=\u0026quot;name=pic1,type=svg,show=codeandimage\u0026quot; graph pic1 { a -- b a -- b b -- a [color=blue] } ``` 代码起始符号加上 graphviz 指示这是一个包含 graphviz 代码的块。graphviz 后面的 _ 之后的内容代表 graphviz 的渲染程序，若不指定任何渲染程序，将默认使用 dot 。\nconfig 中可以包含三个值：\nname 和上文中提到的文件名 NAME 相关。若不设置，将使用空字符串； type 生成的图像类型，和上文中提到的 TYPE 相关，这里是 支持的格式 列表。若不设置，将使用 png ； show 指示是仅显示图像（image），仅显示代码（code）还是都处理（codeandimage）。若不设置，将使用 image 。 用与范例相同的代码处理之后，得到的 html 的值内容为：\n1\u0026lt;h2\u0026gt;graph\u0026lt;/h2\u0026gt; 2\u0026lt;div class=\u0026#34;codehilite\u0026#34;\u0026gt; 3\u0026lt;pre\u0026gt;graph pic1 { 4 a -- b 5 a -- b 6 b -- a [color=blue] 7} 8\u0026lt;/pre\u0026gt; 9\u0026lt;/div\u0026gt; 10\u0026lt;p\u0026gt;\u0026lt;img alt=\u0026#34;Graphviz chart graphviz-pic1-0.svg\u0026#34; src=\u0026#34;/uploads/graphviz-pic1-0.svg\u0026#34; /\u0026gt;\u0026lt;/p\u0026gt; mdtxt 的值为：\n## graph ``` graph pic1 { a -- b a -- b b -- a [color=blue] } ``` ![Graphviz chart graphviz-pic1-0.png](/uploads/graphviz-pic1-0.png) 注意，上面的 Markdown 代码中用来指示 graphviz 代码的字段以及 config 信息都已经被删除。\n（全文完）\n","date":"2015-06-11","description":"","lastmod":"2015-06-11T14:09:22Z","slug":"fenced-code-extra-for-python-markdown","tags":["python","markup","graphviz"],"title":"Fenced Code Extra for Python-Markdown","url":"https://blog.zengrong.net/post/fenced-code-extra-for-python-markdown/"},{"categories":["technology"],"content":" 2020-02-07 更新： Graphviz 官网迁移，修改链接地址。 Graphviz 是一个绘制关系图/流程图的工具包，使用它提供的 dot-language ，我们就可以在文本中通过嵌入绘制代码的方式实现图的绘制。不再需要 GUI 工具了，也更便于版本管理。\n听起来有点像 SVG 的味道，不同之处在于 SVG 是使用浏览器的渲染引擎来渲染。\n基本语法 让我们了解两种不同类型的图： graph 和 digraph。前者使用 -- 描述关系，后者使用 -\u0026gt; 描述关系。我们一般会为每张图定义一个名称。\ndigraph 代表 direction graph ，意指 “有方向的图“ 。\n看看下面两个例子，就知道它们的区别了：\ngraph 1graph pic1 { 2 a -- b 3 a -- b 4 b -- a [color=blue] 5} digraph 1digraph pic2 { 2 a -\u0026gt; b 3 a -\u0026gt; b 4 b -\u0026gt; a [style=filled color=blue] 5} 定义 对于图中的每个对象，可以采用直接使用的方式（就像上面的例子那样），也可以采用 先声明，再使用 的方式。\n使用方括号来设定对象的属性。常用的属性有 shape/style/color 等等，具体的属性可查看attr ， shape 的类型可查看 node-shape ，所有的文档汇总页面在这里： Graphviz Documents。\n复杂的例子 下面的例子描述一个手机游戏启动后的更新流程：\n1digraph startgame { 2 label=\u0026#34;游戏资源更新流程\u0026#34; 3 rankdir=\u0026#34;TB\u0026#34; 4 start[label=\u0026#34;启动游戏\u0026#34; shape=circle style=filled] 5 ifwifi[label=\u0026#34;网络环境判断是否 WIFI\u0026#34; shape=diamond] 6 needupdate[label=\u0026#34;是否有资源需要更新\u0026#34; shape=diamond] 7 startslientdl[label=\u0026#34;静默下载\u0026#34; shape=box] 8 enterhall[label=\u0026#34;进入游戏大厅\u0026#34; shape=box] 9 10 enterroom[label=\u0026#34;进入房间\u0026#34; shape=box] 11 resourceuptodate[label=\u0026#34;资源不完整\u0026#34; shape=diamond] 12 startplay[label=\u0026#34;正常游戏\u0026#34; shape=circle fillcolor=blue] 13 warning[label=\u0026#34;提醒玩家是否更新\u0026#34; shape=diamond] 14 startdl[label=\u0026#34;进入下载界面\u0026#34; shape=box] 15 //{rank=same; needupdate, enterhall} 16 17 {shape=diamond; ifwifi, needupdate} 18 19 start -\u0026gt; ifwifi 20 ifwifi-\u0026gt;needupdate[label=\u0026#34;是\u0026#34;] 21 ifwifi-\u0026gt;enterhall[label=\u0026#34;否\u0026#34;] 22 needupdate-\u0026gt;startslientdl[label=\u0026#34;是\u0026#34;] 23 startslientdl-\u0026gt;enterhall 24 needupdate-\u0026gt;enterhall[label=\u0026#34;否\u0026#34;] 25 26 enterhall -\u0026gt; enterroom 27 enterroom -\u0026gt; resourceuptodate 28 resourceuptodate -\u0026gt; warning[label=\u0026#34;是\u0026#34;] 29 resourceuptodate -\u0026gt; startplay[label=\u0026#34;否\u0026#34;] 30 warning -\u0026gt; startdl[label=\u0026#34;确认下载\u0026#34;] 31 warning -\u0026gt; enterhall[label=\u0026#34;取消下载\u0026#34;] 32 startdl -\u0026gt; enterhall[label=\u0026#34;取消下载\u0026#34;] 33 startdl -\u0026gt; startplay[label=\u0026#34;下载完成\u0026#34;] 34} 其他文档 下面有两篇入门教学的文章，可以略作参考。\nGraphViz for discrete math students An Introduction to GraphViz and dot GUI 在节点很多的情况下，纯看代码很容易把自己绕进去。\n因此，我的使用方法就是打开一个支持 GraphViz 的 GUI，一边编写，一边查看效果。\n两个常用的 IDE Eclipse 和 IntillJ IDEA 都有能够支持 GraphViz 的插件。它们是通过 PlantUML 来实现支持的。至于 PlantUML 是什么，这又是另一个故事了。\n我目前(2020 年)主要使用的编辑器是 Visual Studio Code，使用的插件是 Graphviz Preview。\n在 这里 你能找到所有支持 PlantUML/Graphviz 的软件，各种论坛程序、Wiki 和文本编辑器、IDE 均在此列。Vim 和 Emacs 一个也不少，甚至还支持 Microsoft Word ！\n相关文章 请接着阅读 Graphviz 工具教程 。\n全文完 ","date":"2015-05-29","description":"","lastmod":"2020-02-07T04:11:51Z","slug":"graphviz-brief","tags":["graphic","graphviz"],"title":"Graphviz 简易教程","url":"https://blog.zengrong.net/post/graphviz-brief/"},{"categories":["technology"],"content":"在 Mac OS X 上，我们可以在 Users \u0026amp; Groups 设置中指定 Network Account Server 来开启网络账户的登录。但是，当我试图将网络账户的默认 shell 从 bash 修改为 zsh 的时候，出现了问题。\n直接执行 chsh 命令，会在默认编辑器中打开下面的内容：\n1# Changing user information for rzeng. 2# Use \u0026#34;passwd\u0026#34; to change the password. 3## 4# Open Directory: /Active Directory/XXXX/xxxx.com 5## 6Shell: /bin/bash 7Full Name: Rong Zeng 8Office Location: 9Office Phone: 10Home Phone: 当编辑了 shell: 的值之后，保存会出现这样的提示：\nchsh: Operation was denied because the current credentials do not have the appropriate privileges. Operation was denied because the current credentials do not have the appropriate privileges. chsh: no changes made\n直接使用 sudo chsh -s /bin/zsh ，也会出现同样的提示。\n根据 How do I change a users default shell in OSX? 提到的，按住 Ctrl 键单击 User \u0026amp; Groups 中显示的用户名称，会出现 Advanced Options... 选项，在其中可以设置默认的 shell。\n但是，Network Account 没有 Advanced Options ，而其他用户都有。\n最后，还是这个问答通过曲线救国的方式解决了问题： chsh doesn't change $SHELL 。\n方法很简单，操作系统不是不让改么？那么就直接改应用程序！\n我使用的是 iTerm.app 代替默认的 Terminal 。进行如下的设置即可让 iTerm.app 启动的时候自动启动 zsh。\n点击 iTerm.app -\u0026gt; Preferences -\u0026gt; Profiles -\u0026gt; General -\u0026gt; Command ； 设置 Command: 的值为 /bin/zsh 。 其实，如果不怕麻烦，在启动 shell 之后，再敲入 zsh 回车也能达到同样的效果。\n（全文完）\n","date":"2015-05-26","description":"","lastmod":"2015-05-26T10:10:53Z","slug":"chsh-in-network-account-on-osx","tags":["osx"],"title":"在 Mac OS X 的网络账户中修改 shell(chsh)","url":"https://blog.zengrong.net/post/chsh-in-network-account-on-osx/"},{"categories":["technology"],"content":"目前项目需要对 dragonbones 骨骼动画增加滤镜支持。这样就可以方便地利用同一套骨骼动画资源制作例如变色、中毒或者真假英雄等效果，节省美术同学的工作量以及运行时的内存占用。\ncocos2d-x-filters 项目提供的滤镜功能，是针对纹理进行操作的。用于显示的两个类 FilteredSpriteWithOne 和 FilteredSpriteWithMulti 也是继承自 Sprite 。\n但 dragonbones 是一个层级结构，dragonbones 中的那个 _display ，其实仅仅是起到了容器的作用，并没有什么特殊的渲染工作。因此要对 dragonbones 进行滤镜处理，最简单的办法就是对 dragonbones 中的每一个骨骼做滤镜处理。\n具体的方法如下：\n1. 创建 DBCCFilterArmature ，继承 DBCCArmature ，在其中实现 setFilter 和 clearFilter 等方法；\n2. 创建 DBCCFilterFactory ，继承 DBCCFactory ，覆盖其中的三个重载的 buildArmature 方法，使其返回 DBCCFilterArmature；覆盖 generateArmature 方法，使其生成 DBCCFilterArmature ；覆盖 generateDisplay 方法，使其返回 FilterSpriteWithOne ；\ngenerateDisplay 这个覆盖是关键。因为每个骨骼动画中的各个骨骼部件的具体显示的纹理，是由这个方法生成的。在原来的方法中返回的是 Sprite ，而这里改成 FilterSpriteWithOne 就行了。因为性能的考虑，不应该让骨骼动画支持多重滤镜。\n3. 还可以再实现一个 DBCCFilterArmatureNode ，集成 DBCCArmatureNode ，这是为了在使用的时候更方便。但这并非必要。\n我已经将所有的代码放在 v3.x-with-dbs 分支中了。这个分支中也包含 dragonbones 的代码 c59b444af4 。\n由于精力有限，仅仅实现了 Mac 版本的 Demo ，其他设备请自行处理。\n将 cocos2d-x 3.4 的源码放在 demos/filters-cpp-demo 文件夹下即可直接编译成功。也可以使用符号链接：\n下面是几个截图：\n（全文完）\n","date":"2015-05-07","description":"","lastmod":"2015-05-07T10:21:31Z","slug":"cocos2d-x-filters-with-dragonbones","tags":["cpp","cocos2d-x"],"title":"cocos2d-x 滤镜对 dragonbones 的支持","url":"https://blog.zengrong.net/post/cocos2d-x-filters-with-dragonbones/"},{"categories":["technology"],"content":"cocos2d-x 并没有提供滤镜支持。由于美术同学的强烈需求，我不得不在刚刚学习 C++ 的时候写了一个 cocos2d-x-filters 项目，为游戏中的纹理提供一些基本的变色、模糊等效果。\n这个项目从2014年3月开始，到现在已经一年多了。最近为了给 dragonbones 增加滤镜支持，不得不把原来的项目翻开来重新看了一遍，发现以前学的 OpenGL 现在都忘得差不多了。这是个很糟糕的体验，我决定趁着这个机会把这个项目捡起来，增加一些必要的滤镜，同时继续学习 OpenGL 知识。\n于是，我为 cocos2d-x-filters 项目单独写了一个 page ，方便持续更新。\n这套滤镜项目最早是在 cocos2d-x v2.2 下开发的，后来我将其移植到 quick-cocos2d-x 中并做了必要的 Lua 封装，也将其推送到了 quick 官方库 。\n后来，quick 团队将其移植到了 v3quick 中，我基于 quick 团队移植的版本，重新调整了项目结构，形成了现在的 cocos2d-x-filters v3.x 分支 。\n我将在这个分支的基础上持续更新。\n（全文完）\n","date":"2015-05-06","description":"","lastmod":"2015-05-06T10:22:36Z","slug":"about-cocos2d-x-filters","tags":["cpp","cocos2d-x"],"title":"关于 cocos2d-x 滤镜","url":"https://blog.zengrong.net/post/about-cocos2d-x-filters/"},{"categories":[],"content":"cocos2d-x-filters 是一个基于 cocos2d-x 开发的滤镜项目，项目托管于 github 。\n目前，这个项目支持下面的几个 cocos2d-x 版本：\nquick-cocos2d-x 2.x lua封装（仅支持quick-cocos2d-x 2.x） 基于 quick 的范例 quick-cocos2d-x 3.x cocos2d-x 2.x cocos2d-x 3.x 滤镜列表 Gray 去色滤镜，可以调整 RGB 通道的去色比例； RGB 和 cocos2d-x 中的 setRGB 功能一致； HUE 改变色调，功能同 Photoshop； Brightness 调整明度，功能同 Photoshop； Contrast 调整对比度，功能同 Photoshop； Exposure 曝光； Gamma 伽马值调整； Haze ； GaussianVBlur 高斯模糊，纵向； GaussianHBlur 高斯模糊，横向； ZoomBlur ； MotionBlur 运动模糊； Sharpen 锐化。 注意事项 滤镜支持叠用（多重滤镜），但性能可能很糟糕； 若生成的 PLIST 纹理中的帧被旋转过（TexturePacker --enable-rotation），可能会出现纹理无法显示的情况。这是一个已知的 bug，目前的解决方案是生成 PLIST 纹理时禁用旋转（TexturePacker --disable-rotation）。 截图 GammaFilter(2)\nHueFilter(90)\nZoomBlurFilter(4, 0.7, 0.7)\n多重滤镜\nHueFilter(240)+StaturationFilter(1.5)+BrightnessFilter(-0.4)\n相关文章 在 cocos2d-x 中使用多组shader实现多重滤镜 关于 cocos2d-x 滤镜 cocos2d-x 滤镜对 dragonbones 的支持 免责声明 该项目是我刚刚开始学习 C++ 时所作的第一个项目，因此我不能保证代码质量（可能很糟糕）。若使用中有问题，欢迎讨论。\n","date":"2015-05-06","description":"","lastmod":"2015-05-06T09:28:51Z","slug":"cocos2d-x-filters","tags":[],"title":"cocos2d-x-filters","url":"https://blog.zengrong.net/cocos2d-x-filters/"},{"categories":["technology"],"content":" 开始日期：2015-03-27 读完日期：2015-04-04 书籍介绍： 编号1506 本月初就已经读完了这本 《HTML5程序设计（第2版）》 ，可一直磨蹭到现在才开始写读书笔记。\nHTML5 和我十年前学习的 HTML4 相比并没有什么太大的变化，只是多了一些 API 而已。\n看来我还要找本 CSS3 相关的教材看看。\n下面是本书的思维导图：\n全文完 ","date":"2015-04-30","description":"","lastmod":"2015-04-30T10:30:20Z","slug":"pro-html5-programming","tags":["reading","html5","readingnote"],"title":"【读书笔记】HTML5程序设计（第2版）","url":"https://blog.zengrong.net/post/pro-html5-programming/"},{"categories":["use"],"content":"今天是读书日，我来谈谈 Kindle 的使用技巧。\n购买 Kindle 之前，我一直是读纸质书，也用平板读扫描版。LP 每天抱怨书太多，于是买了 Kindle。\n我一直认为 Kindle 看技术书籍是不太合适的，一个原因是幅面较小，另一个原因是书不够多，第三个原因是没办法看扫描版。不过2月27日捡了一张苏宁易购的优惠券，就莫名其妙地买了一个 Kindle Paperwhite 2 。\n后来 Kindle 被 LP 霸占，就趁着4月8日大陆版白色 Kindle 上市，又买了一个送 LP ，把我的小黑换回来了。\nKindle的比较 Kindle 和 Kindle Paperwhite 的做工相差还是比较大的。虽然前者轻了 19g，但看起来较厚，塑料感很强，感觉很笨重。而后者颜值就比较高。\n前者戴手套都能翻页，而后者就必须用手指翻页。所以前者在武汉的冬天还是很有优势的。\n选择上，如果不在乎有没有背光（我就不在乎），买 Kindle 就可以了，毕竟便宜 400 块啦。当然，如果不差钱的话，最好是买 Kindle Voyage 。 :)\n另外，不要套。本来 200g 左右的重量，单手握持是很轻松的，加上个套，重的跟锤子似的。\n软件 唯一推荐的桌面软件是 Calibre 。Windows/Mac/Linux 通用，且免费。这是我目前找到的最好的书籍管理软件。主要特色如下：\n支持识别多种设备，例如可以识别Android 设备、Kindle 各种型号； 支持制作 epub、mobi 等图书； 支持从各种格式导入、转换； 支持发邮件到 Kindle 的绑定邮箱； 更详细的看这里吧： About calibre 这本书就是我用 Calibre 制作的（mobi+epub）：\n1 文件 推送 Kindle 的邮箱推送功能，总觉得有点问题，经常出现发送不成功，有时候有较长时间的延迟，让人以为没有发送成功。但通过 USB 连接拷贝的书籍，并不能和云端同步，因此，我采用的下面这种方式。\n安装 Kindle 的 app （当然也有桌面版），然后使用该 app 的发送到功能，将书籍发送到已经注册的 Kindle 即可。\n如果有多个 Kindle 设备，最好是能注册到一个 amazon 账号下。这样多个设备之间可以同步读取进度和书籍。\n获得书籍 尽管 Amazon 已经提供了不少电子书可供购买，但许多较新的以及专业性强的书，在 Amazon 中是无法找到的。这种情况下可能只能去读 D 版了。我采用的方法是，优先考虑购买正版电子书，若 Amazon 没有，则再去 多看 或者 豆瓣阅读 购买。若实在买不到电子书，则购买一本正版的纸质书，然后再用 Kindle 看 D 版。这样是给作者最好的认同了。 :)\n","date":"2015-04-23","description":"","lastmod":"2015-04-23T10:03:07Z","slug":"use-kindle","tags":["reading"],"title":"Kindle的使用","url":"https://blog.zengrong.net/post/use-kindle/"},{"categories":["technology"],"content":"2016-06-05更新： 加入 硬件监控 内容\n本文讲的是 Ubuntu 桌面。在服务器环境，当然我一直使用的都是 Linux 。公司的服务器是 CenOS ，自己的服务器用 Ubuntu。为了练手，用一台旧 Notebook 装了 ArchLinux 折腾着玩。\n2010年，我使用了半年的 Ubuntu 桌面来办公，还写了一个 抛弃 Windows ，用 Ubuntu 办公 系列。经过这半年的测试，我认为，Ubuntu 已经完全可以满足办公的需求。\n后来，我抛弃了 Office Boy 的工作，开始程序员生涯，桌面系统重新用回了 Windows 。\n接下来的2年多时间里，我主要使用 cygwin 和 MinGW 在 Windows 下工作。\n2012年，由于从页游开发转向手游开发，我开始使用 Mac OS X 系统。OS X 与 Linux 同源，因此上手非常快。但由于极其讨厌 Finder 的垃圾设计，自己内心一直抵触使用 OS X 。\n2013年，我的主要技术转向 cocos2d-x ，我不得不开始使用 XCode 和 Objective-C ，这样，就必须在 OS X 下工作了。\n于是，在公司，我同时使用一台 Mac Mini 和一台 PC 开发。在家里，常用的设备目前是这样的：\n一台 Windows 8 PC 一台 Windows 7 Notebook 一台 Windows 8 平板电脑 一台 Android 平板电脑 一部 Android 手机 一部 iPhone 手机 一部 iPod 设备 以前，对于我来说，必须使用 Windows 主要有这样几个考虑：\n网上银行； QQ； 网盘客户端； Flash。 而现在，前三者都已经可以使用手机 App 替代了，何况我工作之外的时间已经不再使用QQ。而 Flash ？ 再见了 。\n另外，即使我的主 PC 不再是 Windows ，碰到必须在 Windows 下面进行的操作（例如Andoid刷机？），也可以借助 Notebook 来进行。Windows 8 平板也能起到作用。\n看起来我似乎已经不需要 Windows 了。但由于主系统是 AMD 平台，安装黑苹果十分折腾，也不愿意再折腾原来折腾过的 ArchLinux ，于是就安装了省事的 Ubuntu。\n下面记录一些我调整系统的过程。毕竟把系统调整到我的习惯，也是一件麻烦事。\n1. 环境 1.1 shell 一直用的是 zsh。这个最简单了，安装 oh-my-zsh 即可，Linux/OS X/cygwin/MinGW 都通用。\n1.2 服务管理器 sysv-rc-conf 这个最好用了。\n2. 梯子 2.1 shadowsocks 以前在 Ubuntu 上搭建 shadowsocks 是作为服务端，这次则是作为客户端使用。shadowsocks 客户端自己就支持 daemon 模式，加上 -d 参数就可以了：\n1# 启动本地服务器(daemon模式) 2sudo sslocal -s your_server_ip -p your_server_port -l your_local_port -k your_password -d start 3# 停止本地服务器 4sudo sslocal -s your_server_ip -p your_server_port -l your_local_port -k your_password -d stop 当然，也可以先创建一个配置文件 sudo vim /etc/shadowsocks.json ：\n1{ 2\u0026#34;server\u0026#34;:\u0026#34;your_server_ip\u0026#34;, 3\u0026#34;server_port\u0026#34;:your_server_port, 4\u0026#34;local_port\u0026#34;:your_local_port, 5\u0026#34;password\u0026#34;:\u0026#34;your_password\u0026#34;, 6\u0026#34;timeout\u0026#34;:300, 7\u0026#34;method\u0026#34;:\u0026#34;aes-256-cfb\u0026#34;, 8\u0026#34;fast_open\u0026#34;:true 9} 然后使用配置文件来启动，这就简单许多了：\n1sudo sslocal -c /etc/shadowsocks.json 2.2 supervisor 我更喜欢使用 supervisor 来控制 shadowsocks。先创建一个配置文件：\n1sudo vim /etc/supervisor/conf.d/shadowsocks.conf 内容如下：\n1[program:shadowsocks] 2command=sslocal -c /etc/shadowsocks.json 3autostart=true 4startsecs=10 5startretries=10 然后重载 supervisor ：\n1sudo supervisorctl reload 2# Restarted supervisord 查看当前的状态，可以看到 shadowsocks 已经在运行了：\n1sudo supervisorctl status 2# shadowsocks STARTING 2.3 proxychains 如果要在命令行下也使用 shadowsocks ，那么可以借助 proxychains 。\nproxychains 的系统配置文件位于 /etc/proxychains.conf ，为了方便修改，我选择创建一个自己的配置文件，这个配置文件在读取的时候优先于系统配置文件：\n1vim ~/.proxychains/proxychains.conf 内容如下（可以打开系统配置文件进行参考）：\n1strict_chain 2proxy_dns 3remote_dns_subnet 224 4tcp_read_time_out 15000 5tcp_connect_time_out 8000 6localnet 127.0.0.0/255.0.0.0 7quiet_mode 8 9[ProxyList] 10socks5 127.0.0.1 your_local_port 然后做个测试：\n1proxychains wget twitter.com 2 3ProxyChains-3.1 (http://proxychains.sf.net) 4--2015-04-04 21:18:39-- http://twitter.com/ 5Resolving twitter.com (twitter.com)... 199.59.148.10 6Connecting to twitter.com (twitter.com)|199.59.148.10|:80... connected. 7HTTP request sent, awaiting response... 301 Moved Permanently 8Location: https://twitter.com/ [following] 9--2015-04-04 21:18:40-- https://twitter.com/ 10Connecting to twitter.com (twitter.com)|199.59.148.10|:443... connected. 11HTTP request sent, awaiting response... 200 OK 12Length: 59703 (58K) [text/html] 13Saving to: ‘index.html’ 14 15100%[============================================================\u0026gt;] 59,703 163KB/s in 0.4s 16 172015-04-04 21:18:42 (163 KB/s) - ‘index.html’ saved [59703/59703] 3. 按键绑定 我习惯把 CapsLocks 变成 Control L 。因为大写锁定键本来就没有什么作用。在Windows下，有许多软件 做这件事，\n在 Linux Mint 17 中，键盘的布局设置中，提供了选项来调整键盘布局。\n而在 Ubuntu 下，12.04 之前系统设置中就可以互换，之后的版本中，这个功能就消失了。\n可以使用这样几种方法来搞定。\n3.1 使用 xmodmap 编辑下面的内容，保存为 ~/.xmodmap ：\n1remove Lock = Caps_Lock 2keysym Caps_Lock = Control_L 3add Control = Control_L 然后在 ~/.zshrc 中加入 xmodmap ~/.xmodmap 即可。\n3.2 使用 setxkbmap 这种方法来自于这里： CapsLock Remap Howto ：\n直接在 ~/.zshrc 中加入 setxkbmap -layout us -option ctrl:nocaps 即可。\n3.3 问题 上面的两种方法，都有一个很大的缺点，那就是：\n切换 tty 的时候失效； 屏幕锁定后解锁失效。 虽然失效后可以再次执行上面的命令来恢复，但毕竟太麻烦，下面的几个方法就没有这个问题了。\n3.4 XKeyCaps XKeyCaps 是一个可视化调整键盘映射的软件。具体功能可自行研究。\n3.5 gnome-tweak-tool 这个 GUI 软件界面有些 BUG ，但可用。\n3.6 dconf 使用 dconf 来永久改变键映射，而且立即生效。\n首先可以使用 read 子命令来查看当前的映射：\ndconf read /org/gnome/desktop/input-sources/xkb-options 例如我的值就是：\n['esperanto:qwerty'] 然后可以使用 write 子命令来设置当前的映射，为了避免影响原来的映射，可以把原来的映射的选项加在设置的前面：\ndconf write /org/gnome/desktop/input-sources/xkb-options \u0026quot;['esperanto:qwerty','ctrl:nocaps']\u0026quot; 使用 man xkeyboard-config 查看所有的选项。例如，如果只是希望将 CapsLock 与 Control L 互换，则可以使用 ctrl:swapcaps 这个值。\n┌──────────────────────────────────────────────┐ │Option Description │ ├──────────────────────────────────────────────┤ │ctrl:nocaps Caps Lock as Ctrl │ │ctrl:lctrl_meta Left Ctrl as Meta │ │ctrl:swapcaps Swap Ctrl and Caps Lock │ │ctrl:ac_ctrl At left of 'A' │ │ctrl:aa_ctrl At bottom left │ │ctrl:rctrl_ralt Right Ctrl as Right Alt │ │ctrl:menu_rctrl Menu as Right Ctrl │ │ctrl:ctrl_ralt Right Alt as Right Ctrl │ │ │ └──────────────────────────────────────────────┘ 4. 应用软件 麒麟 操作系统的生态圈开发了许多软件，在 Ubuntu 下直接可用。\n快盘 农历 filefox 地址栏点击全选网页地址 进入 about:config 设置 browser.urlbar.clickSelectsAll 的值即可。 5. wine 中文乱码 有个 Windows 软件我是必须要使用的，那就是用了10年的 Acemoney ，这个软件仅有 Windows 版本。这种情况，就只能上 wine 了。\n首先加入 ppa 的源，安装最新的 wine1.7\n1sudo add-apt-repository ppa:ubuntu-wine/ppa 2sudo apt-get update 3sudo apt-get install wine1.7 wine 中的中文软件界面一如既往地出现了乱码，在注册表中替换字体都没用。 Wine的中文显示与字体设置 中提到的一句话启发了我：\n注意字无法显示和显示为乱码是不同的症状，如果看到乱码，请确保系统语言和运行Wine时的语言环境一致。如果有汉字丢失或显示为方框，请继续阅读本文。\n而我的软件界面中似乎每个字符都可以显示，而我的语言环境的确设置的是 en_us.UTF-8 ，这和 Acemoney 中显示的中文是不一样的。因为 ubuntu 在英文环境下可以正常显示中文，因此我就没有将系统设置成中文界面。再说了，git 的提示等信息，看中文还的确怪不习惯的。\n于是将系统语言改为中文，重新登出一次，Wine中的中文就显示正常了。\n「手把手」告別你的Wine中文亂碼-Wine二部曲 一文也是比较靠谱的，可以参考。\n6. 输入法 自带的 Pinyin 就已经很好用了，支持双拼，且支持自定义词组，输入时间和日期都非常方便。\n唯一不爽的是，rq 输出的日期是类似于 2015-4-6 这种形式，而我需要 2015-04-06 这种形式。这只需要编辑自定义词组即可。编辑完后，使用下面的代码重启 ibus 即可使用：\n1killall ibus-daemon 2ibus-daemon -d 7. 配置工具 除了上面提到的 gnome-tweak-tool 之外，还有几个配置工具是可以用的：\nubuntu-tweak compizconfig-settings-manager 8. 硬件监控 使用 Psensor 来监控CPU和主板温度。首先安装 lm-sensors ：\n1sudo apt-get install lm-sensors 然后运行：\n1sudo sensors-detect 这会询问一堆问题，直接使用默认的回答（回车）即可。然后运行 sudo sensors 可以看到数据（下面是我的电脑）：\n1radeon-pci-0008 2Adapter: PCI adapter 3temp1: +24.0°C (crit = +120.0°C, hyst = +90.0°C) 4 5k10temp-pci-00c3 6Adapter: PCI adapter 7temp1: +39.6°C (high = +70.0°C) 8 (crit = +70.0°C, hyst = +69.0°C) 接下来安装 Psensor ，配置一下就能显示出界面了。\n1sudo apt-get install psensor 硬盘温度的检测，需要安装 hddtemp ：\n1sudo apt-get install lm-sensors hddtemp 然后使用下面的命令查看硬盘温度：\n1sudo hddtemp SATA:/dev/sda 2# /dev/sda: ADATA SP600: 36°C 3 4sudo hddtemp SATA:/dev/sdb 5# /dev/sdb: WDC WD10EARS-00Y5B1: 41°C 9. 其它 pyvenv 3.4 的bug解决 System Load Indicator ","date":"2015-04-04","description":"","lastmod":"2015-04-06T13:11:38Z","slug":"return-to-ubuntu","tags":["linux","ubuntu"],"title":"重回 Ubuntu 桌面","url":"https://blog.zengrong.net/post/return-to-ubuntu/"},{"categories":["web"],"content":"由于博客不再转载文章，我弄了一个 wiki系统 ，这个系统使用的是 dokuwiki 。\n使用 wiki 系统还有许多好处，包括但不限于：\n方便保存一些零碎的想法； 转载文章便于管理； 文章之间的关系更加系统化； …… 这个系统大概有2年多了，也积累了 300 篇 wiki ，dokuwiki 有很多优点，例如小巧易用，不需要数据库等等。但最近我越来越感觉在线系统的不方便。例如：\n编辑器太弱，在浏览器中编辑大段文章的体验很糟糕； 没有版本管理，需要手动备份； 需要经常更新； wiki 许多优秀的生成器不支持 wiki 格式。 因此，No Zuo No Die 的思想又开始泛滥，继 博客静态化工作 之后，我又开始折腾 wiki 静态化。\n数据备份倒是好办，直接把 wiki 格式的纯文本从 dokuwiki 的文件夹中复制出来就可以了。wiki 这种标记格式的支持，还是没有 Markdown 广泛，甚至都不如 AsciiDoc 。因此，我需要把 wiki 格式转换成 Markdown 。这个简单，祭出 Pandoc 大旗就好了。\n比较麻烦的是新系统的选择。在我看来，这个系统必须包含这样几个特点：\n必须是静态化生成系统； 采用 Markdown 作为基准格式； 支持自动转换成 HTML； 支持模板，最好是 Jinja2； 支持搜索。 我首先考虑的是 vimwiki ，毕竟我的 wiki 的第一个版本就是用它实现的。但这个系统对中文的支持比较糟糕，对 MarkDown 的支持也不够好。想来想去，还是放弃了。\n接着是 gollum ，这是 github 的 wiki 系统的开源项目，应该是首选。但折腾了一会儿之后，发现在 windows 上很难配置，于是放弃。\n然后是 Read the Docs ，许多 python 库的文档都放在这里，且同时支持 Markdown 和 reStructuredText 。在 CentOS 上折腾了一会儿之后，感觉这玩意儿依赖太多了点，又放弃。\n再然后是 Gitbook ，这个我倒是挺喜欢。界面简洁，依赖也不太多。同时它提供一个不错的在线编辑器，编辑器支持多种标记语言，支持实时预览。和 github 整合后，可以直接用编辑器修改 github 上的项目，也可以自动在 github 的仓库和 gitbook 的仓库中做切换。折腾了两天后，我发现只有位于 SUMMARY.md 中的文章才会被转换成 html ，所以这个系统完全是为在线发布书籍而生，不适合 wiki。\nLeanpub 也是类似的系统，我就没仔细研究了。\n选到最后，还是回到了 Pelican ，改改用来生成 wiki ，看起来也蛮不错的。\n顺便说一句，如果希望自动监控 git仓库 ，将其中的内容通过 FTP 传到自己的发布网站上，可以使用这个在线服务： FTPLOY 。当然，自己写 hook 也行啦。\n下面两个网站列出了许多静态生成器供选择和比较，可以去瞅瞅：\nStatic Site Generators StaticGen 全文完 ","date":"2015-03-25","description":"","lastmod":"2017-08-02T05:31:30Z","slug":"wiki-system","tags":["python","master","choice"],"title":"WIKI 系统的选择","url":"https://blog.zengrong.net/post/wiki-system/"},{"categories":["technology"],"content":"这是我对知乎上一个问题的回答，原文见： http://www.zhihu.com/question/28584388/answer/41376487\n原文问题：\n入行5年多的as3程序员，不知道未来怎么样！该转型吗？ ？对未来有些迷茫！？\n转型哪个新语言呢？unity,cocos2dx,swift,oc,h5,java还是c＋＋呢！\n我的回答：\n放弃AS3吧。我写了十几年Flash/Flex/FMS/AIR/AS1/2/3了，还不是转了： Good Bye, Flash!\nAS3的程序员（假如你也同时熟悉Flash的话），转型的方向还是很多的。因为Flash的缘故，你对前端周边技术（视频、音频、图形图像）了解应该会比较多一点，那么转前端会非常容易。\nH5之类的就不说了，转那个没什么难度，本来 AS3 和 Javascript 就是同源， HTML 和 CSS 学起来也就是两三天的事情，业余时间平时学一下就可以了。但要注意补习一些 HTTP 协议的知识（AS3 的 API 封装得实在太傻瓜了……）。\nAS3的许多特性都是致(chao)敬(xi)JAVA的(例如 flash.utils.ByteArray 就是 java.io.RandomAssessFile 和 java.nio 的合体)，所以你转 JAVA 问题也不会太大。不过，也仅仅是语法比较熟悉，设计模式可以通用而已，关于语言应用范畴、性能、用法、文化、优化方法……这两门语言都差别太大了。\nJAVA的应用范畴要广泛许多，但和Flash Player 类似的跑在浏览器中的 Java applet 已经是日薄西山了，而 JAVA SE 的应用似乎局限在 Eclipse 这种 IDE 上（当然优秀的 JAVA 软件不少的），似乎后端应用才是 JAVA 的正途。如果你打算向后端发展的话， JAVA 是可以考虑的。\nJAVA 在前端还有一个方向，就是 Android 开发。如果你开发 Android （独占）应用，这个绕不过去。如果你开发跨平台应用，那么 H5 似乎是更合适一点，孰轻孰重，需要自己考虑。\nOC 和 swift 依然有 Android 类似的问题，这两个语言绑定在 Apple 平台上，对个人发展方向的限制未免太多，而且只能开发 iOS 和 Mac 独占软件，不能做后端，在考虑方向的时候，优先级应该排在 JAVA 之后。\nFlash 目前在页游依然是压倒性的优势，其实在视频领域和在线 APP 领域，Flash Player 还是有不错表现的，我在 怎样学习Flash？ - 曾嵘的回答 中总结了Flash能达到的领域。直到现在，Flash Player 依然是有优势的，Flash 只是被 Adobe 自己玩坏了而已： 为什么 .NET 一宣布开源和支援跨平台，一些人就在喊 Java 可以去死？因果关系何在？ - 曾嵘的回答 。\n所以，我假设你现在是在页游领域，那么转型就有两条路：Unity 和 cocos2d-x。 什么？你发现虚幻4免费了？ 这个……暂时还是别碰吧……以 Flash 和 AS3 的基础，去碰虚幻基本上就等于找死。\n我以前写过一个回答： AdobeAIR与unity3D都是跨平台，那个更好呢？ - 曾嵘的回答 ，现在，我的建议依然不变。假如你想走这个方向的话，我好像还写过一篇可以勉强参考一下的文章： 如何学习一种开发框架（如：手机开发cocos2dx方向）？ - 曾嵘的回答 。\n如果有空的话，Unity也可以学习下，毕竟cocos2d-x的工具链不完善（cocostudio 那个东西不是给人类使用的，不要浪费时间）\n所以呢，在你本来是个页游AS3程序员的基础上，我建议的优先级顺序是 C++（cocos2d-x）-\u0026gt; Unity -\u0026gt; JAVA -\u0026gt; OC\u0026amp;swift 。\n等等，H5在哪里？H5不是移动互联网的趋势么： 为什么说html5是移动互联网的趋势? - 曾嵘的回答\n前面我提到了，H5相对简单些，所以我认为你必须要学。\n（全文完）\n","date":"2015-03-07","description":"","lastmod":"2015-03-07T11:42:52Z","slug":"as3-future","tags":["as3","flash","adobe","java","cocos2d-x","fromto"],"title":"入行5年多的as3程序员，不知道未来怎么样！该转型吗？对未来有些迷茫！","url":"https://blog.zengrong.net/post/as3-future/"},{"categories":["technology"],"content":"2015-04-20 更新：该 bug 已经解决，见本文结尾。\n查看所有 quick 移植到 cocos2d-x lua 的文章\n在 移植 quick2.2.3 的项目到 cocos2d-x 3.4 lua 中提到，我已经开始使用 cocos2d-x 3.4 lua 来开发目前公司的两个项目。\n在这两个项目中，我将 cocos 的 lua 框架、quick 的 lua 框架和我自己开发的 lua 代码，全部整合到了我的 lua 项目 中。\n下面提到的 quick 框架，都是指的我整合的这个 lua 项目。\n问题描述 使用 cocos2d-x 3.4 lua + quick 框架进行开发的时候，发现对于销毁再重建的按钮来说，有超过 30% 的几率无法响应触摸事件。\n为了方便重现问题，我使用 quick 框架提供的 cc.ui.UIPushButton 做了测试。下面这段代码中 testUIButton() 方法会创建一个按钮，每次点击这个按钮时调用 newUIButton() 方法，这个方法销毁前面创建的按钮，然后再新建一个按钮。\n这个新建的按钮，相当大的几率会出现无法响应触摸事件的情况。\n1function TestScene:ctor() 2\tself:testUIButton() 3end 4 5function TestScene:testUIButton() 6\tself._bi = 0 7\tcc.ui.UIPushButton.new(\u0026#39;normal.jpg\u0026#39;) 8\t:setButtonSize(240, 60) 9\t:onButtonClicked(function(evt) 10\tself:newUIButton() 11\tend) 12\t:align(display.LEFT_CENTER, display.cx, display.cy) 13\t:addTo(self) 14end 15 16function TestScene:newUIButton() 17\tif self._bi \u0026gt; 0 then 18\tlocal name = \u0026#39;btn\u0026#39;..self._bi 19\tself[name]:removeSelf(true) 20\tself[name] = nil 21\tend 22\tself._bi = self._bi + 1 23\tname = \u0026#39;btn\u0026#39;..self._bi 24\tlocal x = math.random(200) 25\tlocal y = math.random(200) 26\tself[name] = cc.ui.UIPushButton.new(\u0026#39;normal.jpg\u0026#39;) 27\t:setButtonSize(240, 60) 28\t:addTo(self) 29\t:align(display.CENTER, 30\tdisplay.cx - x, display.cy - y) 31\tself[name]:onButtonClicked(function() 32\tprint(\u0026#39;--------------- test \u0026#39;.. name) 33\tend) 34end 问题解决 用一个很简单的方法可以解决这个问题，修改 testUIButton() 方法，将 newUIButton() 进行延迟调用即可：\nsheduler 的实现在 quick 框架中可以找到。\n1function TestScene:testUIButton() 2\tself._bi = 0 3\tcc.ui.UIPushButton.new(\u0026#39;normal.jpg\u0026#39;) 4\t:setButtonSize(240, 60) 5\t:onButtonClicked(function(evt) 6\tscheduler.performWithDelayGlobal(function() 7\tself:newUIButton() 8\tend, 0.1) 9\tend) 10\t:align(display.LEFT_CENTER, display.cx, display.cy) 11\t:addTo(self) 12end 但这是个恶心的解决方案。若想知道真正的解决方案，就要知道这个 BUG 产生的原因。\n原因分析 当然，上面测试代码的实现方式也是很恶心的。在一个按钮响应事件中创建一个按钮，在真实项目中应该不会这样使用。\n不过，虽然这个代码有些极端，但能更方便地重现问题。在我们的实际项目中，只要是销毁之后再重建的按钮，都有可能出现这个 BUG 。而且，不仅仅是按钮，只要是继承 Node 的对象，销毁之后再重建，都会出现这个问题。\n咨询了 cocos2d-x lua 开发组的 阳光七月 ，才知道原来这还是个历史遗留问题：\n阳光七月 16:15:52 销毁的时候那个node标记为已经移除了，如果再创建时指针一样的话会判断为旧的，不会被添加触摸处理 Jacky 16:16:37 但是我已经换了变量名称，这样也会导致创建出相同的指针么？ 阳光七月 16:17:18 在C++层创建与变量名称没有相关性啊 阳光七月 16:17:38 内存释放后又重新分配，当然有可能使用同一块内存 Jacky 16:17:54 是的，而且在同一段代码中，这种情况比较容易发生？ 阳光七月 16:18:49 还是有不小机率的 阳光七月 16:21:13 估计不改Node是不行了 Jacky 16:21:41 Node 在 quick 里面不是改过么？ 阳光七月 16:21:57 之前是想尽量不改-x的代码，在外面挂一层触摸模块 阳光七月 16:22:04 3.3之后没有改了 阳光七月 16:22:47 以现在为基础的话，改动应该也不大 阳光七月 16:23:01 主要是在Node释放时要处理一下 Jacky 16:23:25 quick2确实是没有这个问题的。 Jacky 16:23:42 如果在 C++层面这么写，也会有这个问题么？ 阳光七月 16:24:09 肯定没有的，因为之前触摸模块是Node的一部分，不可能有问题 阳光七月 16:24:15 3.3之后才分开的 阳光七月 16:27:29 现在3.3之后是在lua层面收到onCleanup消息再回到C++层面释放触摸模块的 阳光七月 17:16:46 现在就是绕了个弯，在lua端收到onCleanup消息时再调用removeTouchEvent，这样的联系实在是不牢靠，所以问题也多 阳光七月 17:52:40 嗯，或者可以改一下引用机制，在底层延时销毁，上层不需要考虑这个细节。不过这样也要改Node，我后面再评估一下\n看来，只需要坐等 cocos2d-x lua 开发组来修改就行啦。\n2015-04-24 更新\n这个 bug 已经被 阳光七月 解决，合并这个 PR 即可：\nhttps://github.com/dualface/v3quick/pull/422\n（全文完）\n","date":"2015-03-03","description":"","lastmod":"2015-03-03T09:41:24Z","slug":"cocos2d-x34-touch-bug","tags":["cocos2d-x","lua","cpp","quick2cocoslua"],"title":"cocos2d-x 3.4 lua 触摸 BUG","url":"https://blog.zengrong.net/post/cocos2d-x34-touch-bug/"},{"categories":["technology"],"content":"查看所有 quick 移植到 cocos2d-x lua 的文章\n我们的第二个游戏 Lulala 是采用我修改过的 quick 2.2.3 开发的，这个项目已经完成。\nLulala 完成后，我们立项了一个新的游戏 HHL，此时我转向了 cocos2d-x lua，具体原因我在 从 quick 转向 cocos2d-x 3.3 lua 一文中做过介绍。\n在 HHL 中，我本想将以前在 quick 中修改的东西，全部移到到 cocos2d-x 3.x 中来，但仔细考虑后放弃了。我修改了太多的 C++ 代码，同样的工作我不可能在 cocos2d-x 3.x 中再来做一遍。\n由于 从 quick 转向 cocos2d-x 3.3 lua 中提到的痛苦经历，我现在要保证 cocos2d-x 跟随官方一起更新。我不打算再大幅修改 cocos2d-x 3.4 的 C++ 代码，否则合并 cocos2d-x 官方库一定会出问题。\n因此我把以前重写过的部分全部用 lua 来实现了，例如 SpriteFrameCache 不支持异步加载、AnimationCache 支持的 plist 格式定义文件采用 lua 等等。这能保证尽量不修改 C++ 代码。\n另外，filter、dragonbones 这些支持，我就将其包含在项目库中，这样 cocos2d-x 的升级和项目模块升级可以完全解耦，互不相关。\n同时，我还要保证 Lulala 项目中积累起来的一套 lua 库在 cocos2d-x 3.4 中也可以正常使用。但 quick 对 cocos2d-x 的修改也比较大，很多 lua 代码是依赖相关的 quick C++ 代码的。因此，我需要把部分 quick 中的 C++ 代码也移到我的项目库中。\n同时我规范了 一套文档 ，希望统一开发者的开发行为。\n现在 Lulala 准备发布。这就意味着，我需要马上做出选择，是基于 quick 继续开发和维护 Lulala 项目，还是将 Lulala 项目转到 cocos2d-x 3.4。\n这是个艰难的决定。最终我选择后者。\n因为负责接入 SDK 的同学说 cocos2d-x 3.4 提供的插件机制能够方便他接入 facebook，而其他同学也已经习惯了 HHL 项目的工作方式，再回到 Lulala 会比较痛苦。\n而更重要的原因是，我不可能同时维护两个框架。\nquick 开发组的 阳光七月 多次提醒我不要升级项目。我也知道工作量会非常大，但由于上面的两个原因，不得不升级。\n所以，我花了2周多时间做了这件事，移植的内容包括 cocos2d-x 框架、lua 框架以及工具链。Lulala 项目中所有的 lua 文件，我基本上都改过了。因为不是一个人开发的，每个人的写码习惯不同，我经历了各种匪夷所思的错误和莫名其妙的调用，深刻感受到了码农和程序员之间的不同。\n即使我非常爱折腾，我也依然觉得这件事确实是相当浪费精力浪费生命的事情。如果团队大一点，钱多一点，我也是不愿意移植的。最多 Lulala 和 HHL 两个项目分两个团队同时做就好了嘛。到时候 Lulala 项目不赚钱了就直接扔掉。但为了能活下来，这事儿不得不做，且只能我来做。\n移植工作其实已经在农历新年之前完成了。这两天我会把移植中碰到的一些问题写出来。由于经过了一个春节假期，现在年纪大了记性不好，想起来多少就写多少吧。\n","date":"2015-02-27","description":"","lastmod":"2015-02-27T10:36:14Z","slug":"move-quick223-to-cocos2dx34","tags":["cocos2d-x","lua","quick2cocoslua"],"title":"移植 quick2.2.3 的项目到 cocos2d-x 3.4 lua","url":"https://blog.zengrong.net/post/move-quick223-to-cocos2dx34/"},{"categories":["technology"],"content":"在 cocos2d-x 3.4 上遇到了一个诡异的问题，记录如下。\n平台和版本 框架： cocos2d-x 3.4 final 设备： Nexus 4/5/7 with Android 5.0.1/5.0.2 NDK version： r9d 问题描述 一个 dragonbonesCPP 骨骼动画，在 iOS、Windows、Mac OS X、Android with 4.x 上表现完全正常，但在 Android 5.x 下表现不正常。\n具体表现为解析骨骼动画数据的时候，既不 crash，也没有任何报错信息，整个程序就停住了。\n问题追溯 通过加入 log 信息，发现问题处在 XMLDataParser::parserDragonBonesData 方法中，如下所示：\n1//............. 2DragonBonesData* XMLDataParser::parseDragonBonesData(const void *rawDragonBonesData, float scale) const 3{ 4 CCLOG(\u0026#34;Start parse dragonbones data.\u0026#34;); 5 _armatureScale = scale; 6 const XMLElement *dragonBonesXML = static_cast\u0026lt;const XMLElement*\u0026gt;(rawDragonBonesData); 7 std::string version = dragonBonesXML-\u0026gt;Attribute(ConstValues::A_VERSION.c_str()); 8 CCLOG(\u0026#34;Start parse A_VERSION.\u0026#34;); 9 // TODO 10 /* 11 switch(version) 12 { 13 14 } 15 */ 16 CCLOG(\u0026#34;A_FRAME_RATE: %s .\u0026#34;, ConstValues::A_FRAME_RATE.c_str()); 17\t//打印了上面一行log 之后，整个程序就停住了 18 _frameRate = dragonBonesXML-\u0026gt;IntAttribute(ConstValues::A_FRAME_RATE.c_str()); 19 CCLOG(\u0026#34;Start parse A_FRAME_RATE %d.\u0026#34;, _frameRate); 20 DragonBonesData *dragonBonesData = new DragonBonesData(); 21\t//................ IntAttribute 是 tinyxml2 提供的解析方法，继续追溯，发现程序在 tinyxml2 中的 XMLUtil::ToInt 方法中停住。\n1//............. 2bool XMLUtil::ToInt(const char *str, int *value) 3{ 4 CCLOG(\u0026#34;XMLUtil::ToInt str: %s, value: %d\u0026#34;, str, *value); 5\t//打印了上面一行 log 之后，整个程序就停住了 6 if (DBTIXML_SSCANF(str, \u0026#34;%d\u0026#34;, value) == 1) 7 { 8 CCLOG(\u0026#34;XMLUtil::ToInt SSCANF true\u0026#34;); 9 return true; 10 } 11 12 CCLOG(\u0026#34;XMLUtil::ToInt SSCANF false\u0026#34;); 13 return false; 14} 15//............. 而 DBTIXML_SSCANF 是一个宏，在 NDK 中被指向 sscanf 。\n我本以为是 tinyxml2 的问题，但换了 github 最新版本的 tinyxml2 依然有同样问题，于是开始怀疑 NDK。\nGoogle 一番之后，找到了一个关键的 issue 77988 。这里说到的情况和我碰到的几乎一模一样，看来这是 NDK 的 BUG 没错了：\nI have found that any call into the standard library sscanf function will cause the application to lockup. My device is the Nexus 5 running Android 5.0 preview version hammerhead-lpx13d-preview-f7596f51.tgz\nSteps to recreate the problem:\nMake any call to sscanf, for example:\nint v1 = 0; sscanf(\u0026quot;1\u0026quot;, \u0026quot;%d\u0026quot;, \u0026amp;v1);\nNotice that the application will not return from the call to sscanf and completely freeze without any error message generated\n解决方案 方案1 升级 NDK 到 r10d ，该版本已经解决了这个问题：\nDocumented a fix to a libc++ sscanf/vsscanf hang that occurred in API level 21. The fix itself had been implemented in r10c. (Issue 77988)\n但进行 NDK 连接的时候，我碰到了这样的报错信息：\n/Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: /Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/libgcc.a(pr-support.o): multiple definition of '_Unwind_GetRegionStart' /Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: /Volumes/HD1/Android/android-ndk-r10d/sources/cxx-stl/llvm-libc++/libs/armeabi/thumb/libc++_static.a(Unwind-EHABI.o): previous definition here /Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: error: /Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/libgcc.a(pr-support.o): multiple definition of '_Unwind_GetLanguageSpecificData' /Volumes/HD1/Android/android-ndk-r10d/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9/../../../../arm-linux-androideabi/bin/ld: /Volumes/HD1/Android/android-ndk-r10d/sources/cxx-stl/llvm-libc++/libs/armeabi/thumb/libc++_static.a(Unwind-EHABI.o): previous definition here collect2: error: ld returned 1 exit status make: *** [obj/local/armeabi/libcocos2dlua.so] Error 1\n我没有去解决这个问题，因为还有下一个方案。\n方案2 如果担心 cocos2d-x 3.4 和 NDK r10d 的兼容性，也可以修改 Android 的编译参数：\n编辑 android 项目中的 Application.mk 文件：\nvim frameworks/runtime-src/proj.android/jni/Application.mk 将第一行：\nAPP_STL := c++_static 改为：\nAPP_STL := gnustl_static （全文完）\n","date":"2015-02-27","description":"","lastmod":"2015-02-27T09:42:05Z","slug":"dragonbonescpp-on-android5","tags":["dragonbones","ndk","android","cocos2d-x","cpp"],"title":"dragonbonesCPP 在 Android 5.x 上问题的解决","url":"https://blog.zengrong.net/post/dragonbonescpp-on-android5/"},{"categories":["technology"],"content":"在服务器上，我一直使用的是 vsftpd，但由于 vsftpd 不支持 MLSD ，我开始转向 pure-ftpd。\n简单配置完毕后，登录一直出现错误。\n1-\u0026gt; % ftp ssi@xxxx.xx 2Connected to xxxx.xx. 3220---------- Welcome to Pure-FTPd [privsep] [TLS] ---------- 4220-You are user number 1 of 50 allowed. 5220-Local time is now 12:08. Server port: 21. 6220-This is a private system - No anonymous login 7220-IPv6 connections are also welcome on this server. 8220 You will be disconnected after 15 minutes of inactivity. 9331 User ssi OK. Password required 10Password: 11530 Login authentication failed 12ftp: Login failed 那么，下面是解决方案。\n1. 前置知识 在 ubuntu 上，直接使用 apt-get 可以安装 pure-ftpd 的最新版。但它的配置有些古怪。\npure-ftpd 的配置文件默认处于 /etc/pure-ftpd/conf ，其中的每个文件就是一个命令行参数的名字，文件中的内容就是这个参数的值。\npure-ftpd 使用 pure-ftpd-wrapper 命令来将这些配置文件转换成命令行参数，用它们来启动服务。\n例如，我要禁止 chroot，需要增加一个 /etc/pure-ftpd/conf/ChrootEveryone 文件，在其中写入 yes 这个值。\npure-ftpd 的所有命令行参数都对应一个短参数和一个长参数，可以通过 man pure-ftpd 来查看所有支持的参数。\n这些参数都是全小写的，如果要查看它们的标准形式，可以使用 man pure-ftpd-wrapper 查看。\n好了，这就进入正题。为了描述清楚，我还是把完整的配置流程说一遍。\n2. 创建系统用户 创建一个 ftp 专用账号，同时创建一个 ftp 用户组。设置其 shell 禁止登录。注意，这里设置的 home 值就是 Anonymous 账户的访问地址。\nuseradd -M -U -s /usr/sbin/nologin -d /var/ftp ftp 关于禁止登录的 shell 设置，大家常用的是 /usr/sbin/nologn 和 /bin/false ，在 这里 可以了解一下它们的区别。更详细的信息也可以通过 man false 和 man nologin 得到（当然还可以顺便看看 man true ）。\n3. 创建虚拟用户 下面的命令将创建一个用户名为 ssi 的用户，将其信息写入到 /etc/pure-ftpd/pureftpd.passwd 中。\n这个 ssi 用户，就是虚拟用户。系统中不必（也不应该）存在这个用户。这个用户的权限，与我们通过 -u 和 -g 参数指定的系统账户相同。\n期间， pure-pw 会要求你输入这个用户的 ftp 密码。\npure-pw useradd ssi -u ftp -g ftp -d /var/ftp/ssi ftp 大多数时候都是为 www 服务的，有时候为了方便（不一定安全），甚至可以直接将虚拟用户绑定到 www-data 用户上。\n将用户信息更新到 /etc/pure-ftpd/pureftpd.pdb 文件：\npure-pw mkdb 4. 设置掩码 将需要的文件和目录掩码写入 Umask 配置文件中，中间用空格分隔：\necho '003 002' \u0026gt; /etc/pure-ftpd/conf/Umask 注意，虽然 pure-ftp 的 man 页中这样说：\n-U umask files:umask dirs Change the mask for creation of new files and directories. The default are 133 (files are readable -but not writable- by other users) and 022 (same thing for directory, with the execute bit on). If new files should only be readable by the user, use 177:077. If you want uploaded files to be executable, use 022:022 (files will be read‐ able by other people) or 077:077 (files will only be readable by their owner).\n但你如果真的设置成这样 003:002 这样，pure-ftpd 重启时会报错：\nRestarting ftp server: /usr/sbin/pure-ftpd-wrapper: Invalid configuration file /etc/pure-ftpd/conf/Umask: \u0026quot;003:002\u0026quot; not two octal numbers\n所以，应该将分隔符设置成空格。可能这也是 ubuntu 上的特点吧。 ;)\n5. 解决登录错误 终于到正题了。\n根据 google 到的 信息 ，先是在 auth 中指定 PureDB。\nln -s /etc/pure-ftpd/conf/PureDB /etc/pure-ftpd/auth/50pure 处理之后，问题依旧。\n然后在 pure-ftpd 的 README 中找到这段话：\n--with-pam: use pluggable authentification modules. Don't use this option if your login/passwd pairs are always refused (but the real fix would be to fix your PAM configuration). You need to create a /etc/pam.d/pure-ftpd file to properly use the PAM authentication. The 'pam' directory contains an example of such a file.\n同时又找到 这里，确定取消了 PAM 验证，登录就成功了。\necho no \u0026gt; /etc/pure-ftpd/conf/PAMAuthentication 但取消 PAM 是不合理的，也会让系统不安全。查看 /etc/pam.d/pure-ftpd 的配置都是正确的，说明这不是 PAM 的问题。\n看到 PAM 的配置文件中有 pam_shells.so 的字样，然后鬼使神差地看了一眼 /etc/shells ，发现其中并不包含 /usr/sbin/nologin 。\n于是将其加入：\necho '/usr/sbin/nologin' \u0026gt;\u0026gt; /etc/shells 然后还原 PAMAuthentication 的设置，重启服务，搞定：\nservice pure-ftpd restart 6. 分析 man pam_shells 可以看到下面的信息：\nNAME pam_shells - PAM module to check for valid login shell SYNOPSIS pam_shells.so DESCRIPTION pam_shells is a PAM module that only allows access to the system if the users shell is listed in /etc/shells. It also checks if /etc/shells is a plain file and not world writable. 而在 /etc/pam.d/pure-ftpd 中配置了下面的内容：\nauth required pam_shells.so 因为 ftp 用户的 shell 是 /usr/sbin/nologin ，那么这个 shell 就必须存在于 /etc/shells 中，才能通过 PAM 模块。\n7. 其他问题 在 CentOS 上，我是编译安装 pure-ftpd 的，这里一起说一些注意事项。\n7.1 启动服务用的 script 源码并没有提供启动 script，我将一个可用的传到了 gist ，下载后修改路径然后将其复制到 /etc/init.d 即可使用。\n记得在编译的时候要加入 --with-ftpwho 选择，这样使用上面的脚本实现 service pureftpd status 的时候，可以看到当前正在连接的客户端的信息。\n7.2 重启服务出现 421 错误 是因为在编译的时候没有加入 --with-puredb 选项。需要重新编译。\n421 Unknown authentication method: puredb:/opt/pureftpd/etc/pureftpd.pdb 7.3 530 Sorry, but I can't trust you 同样是 530 登录错误，提示如下：\n530 Sorry, but I can't trust you ftp: Login failed. 这是因为系统的 ftp 帐号是系统帐号（system account），可能是在创建 ftp 帐号的时候加了 -r 参数。查看一下 ftp 帐号的 id：\nid ftp uid=14(ftp) gid=50(ftp) groups=50(ftp) 在 pure-ftp.conf 配置文件中有一个选项，设置最小帐号 UID，若小于这个 UID 则会出现上面的 530 错误。这个值默认为 100 。\n# Minimum UID for an authenticated user to log in. MinUID 100 解决方案有两个：\n修改 MinUID 的值使其小于 ftp 帐号的值（我的是14，所以要修改成13或更小）， 修改 ftp 帐号的 UID 。 这里我选择后者：\nusermod -u 600 ftp groupmod -g 600 ftp 必须要更新虚拟帐号才会生效：\npure-pw usermod ssi -u ftp -g ftp -m （全文完）\n","date":"2015-01-25","description":"","lastmod":"2015-01-27T03:44:21Z","slug":"pure-ftpd-530-failed","tags":["linux","server","ftp"],"title":"pure-ftpd 530 Login authentication failed","url":"https://blog.zengrong.net/post/pure-ftpd-530-failed/"},{"categories":["technology"],"content":"作为一个 Vimer ，很可能不会习惯其他的 IDE 中的编辑功能。\n但作为一个理性的 Vimer，我们也应该知道并非所有的东西都适合用 Vim 来写。\n例如，写后端或许可以只用 Vim（或许 JAVA 除外），但写跨平台的游戏前端，就不太可能了。\n作为 cocos2d-x 的使用者，我必须在 Windows/Mac OS X 下不断切换，在 Visual Sudio/Xcode/Eclipse 三个 IDE 中不断辗转。那些坑爹的快捷键，我经常记错。那些纠结的键绑定，我经常搞混。\n所以还不如把编辑模式都都改成 Vim 方便。下面是分别针对这三个 IDE 的 Vim 支持插件：\n1. Visual Studio VsVim ，支持 Visual Studio 2010 或更高版本，直接下载安装即可。\n这里是源码： VsVim in github 。\n2. Xcode Xvim ，支持 Xcode 5 和 6，需要编译。\n这里是源码： Xvim in github 。\n3. Eclipse 参照这篇文章：在Eclipse中使用Vim 。\n（全文完）\n","date":"2015-01-22","description":"","lastmod":"2015-01-22T06:19:44Z","slug":"using-vim-in-ides","tags":["vim"],"title":"在 IDE 中使用 Vim","url":"https://blog.zengrong.net/post/using-vim-in-ides/"},{"categories":["impressions"],"content":"最近才在 Robin 的介绍下读到 Keith Peters 的 Flash and Me ，细读后当真是感慨万千。\nRobin 在邮件中写道：\n从刚接触Flash时看的小小动画、大话三国、小破孩等动画，到闪客帝国、闪吧和后来的瑞研社区、天地会, 岁月好是把快刀啊， 更觉KP的话实在。\n人生逆旅，且行且珍惜\n既然已经开始感慨了，那就感慨一下：\n提起 Keith Peters ，真正的 Flash 平台开发者应该都不陌生，他是我在 怎样快速从其他语言转到Actionscript游戏开发 一文中提到的好几本书的作者，Flash 平台的真正大婶，哦不，大叔。\n例如，下面几本书的作者都是这位大叔：\nActionScript 3.0 Cookbook Flash ActionScript 3.0动画教程 Flash ActionScript 3.0 动画高级教程 可是，这位标杆式的人物也已经离开了 Flash 平台。\n至于原因，下面一句话就能概括了：\nI don’t see it as a viable, evolving technology for the future.\nAdobe 再也无法给开发者希望，我在 flash的stage3d技术为什么没落？ 中提到：\n总的来说，Adobe 是个卖软件起家的公司（现在依然是这样），这决定了它没什么创新的基因，一切的一切，都要为它的核心业务（卖）服务。\n那么，这样的公司当然不会对开发者友好，它的产品一直是对更有钱的设计师友好的。\nAdobe 一再做出抛弃开发者的事情（看看 Flash Lite、Alchemy、Flex 、AIR 和 AS4 就知道了），这样做当然是被开发者抛弃了。\n更准确的解释，且看 Keith Peters 如是说：\nWhere is Flex? Where is Flash Mobile? Where is Flash Catalyst? Where is any sign of Flash on Linux? (hint: gone, gone, gone and gone.) What is in store for the next version of ActionScript? (hint: there is no next version of ActionScript planned.) What improvements are upcoming for Flash Builder? (hint: none.) How many people were on the Flash team a few years ago and how many are on it now? (hint: fractional.) How is the Flash Platform monetized? (no hint. no clue.) How many people do you know who were full time Flash developers 5-6 years ago who don’t Flash at all now? (hint: a LOT.) How many of the numerous Flash conferences from 5-10 years ago are still held, and have not changed their name to exclude the concept of Flash? (hint: zero.)\n我认为，Flash 没落的原因主要在于：\nH5 的崛起：为什么说html5是移动互联网的趋势? - 曾嵘的回答 对手太强大：为什么 .NET 一宣布开源和支援跨平台，一些人就在喊 Java 可以去死？因果关系何在？ - 曾嵘的回答 Adobe 的功利性决策： Adobe 停止 Linux 平台 Flash 插件开发了？ - 曾嵘的回答 Keith Peters 一直强调自己不是 Flash hater，我其实也不是。Flash 带给我许多欢笑，快乐……当然还有金钱。我在 2005 年出版的第一本纸质著作 《Flash MX互动教学课件制作实例教程》 ，就是写 Flash 。也就是因为 Flash，我的职业从美术教师，教育管理岗位，转到了程序员。\n掐指算来，从2001 年第一次接触 Flash 到现在，已经 14 年了。\n人生，有几个14年？工作，又有多少个14年？\n即使再留恋，为了自己的技术生涯，为了还能继续程序员梦想，我必须转向。\n想想在 2013 年的时候，我还非常积极地帮 Adobe 洗地：\n怎样学习Flash？ 如果 Flash 死了，会怎么样？ 到了 2014 年，我发现原来我就是那个帮别人洗地的傻B：你是否已经放弃了flash,转投其他行业？ 。所以整个 2014 年，我都没怎么写 Flash 的技术文章。\n因为公司使用 AIR 技术开发的游戏仍在运营，我还是更新了几篇 AIR 和 ANE 相关的 bug 解决。\n我突然发现，在浏览器弹出安装 Flash Player 插件的提示时，我现在总是下意识地点击 Cancel 。\n2013 年下半年开始，我逐渐转向 cocos2d-x 。相比而言，C++ 社区更加活跃，而且优秀的人非常多。我发现更多有趣的东西，意识到自己的知识究竟有多浅薄。\n我在继续努力，努力成为真正的程序员。\n也许有时间，我会写一个 Flash 专题。\n但这篇文章就只能到这里了。以我给 Robin 的回信内容节选结束吧：\n有时候会想，如果当时 MM 拒绝收购，Flash 如今会是怎样？或许会更好吧。\n我也不是 Flash hater，但我绝对是个 Adobe hater。\n岁月如刀，但还好香蕉仍在。\n（全文完）\n再见，Flash！\n就像古龙大师说的：\n再见， 就是再也不见\n","date":"2015-01-21","description":"","lastmod":"2015-01-21T01:16:08Z","slug":"goodbye-flash","tags":["flash","adobe","study"],"title":"Goodbye, Flash!","url":"https://blog.zengrong.net/post/goodbye-flash/"},{"categories":["technology"],"content":"两年前，我写了 从Apache到Lighttpd。现在，是时候又发生改变了。\n前段时间，我写了许多关于 OpenResty 的文章，并在公司内网服务器和自己的电脑上开始使用 OpenResty。\n现在，我开始在外网部署 OpenResty。\n编译和安装 OpenResty 的编译和安装就是老三套：\n1tar xzvf ngx_openresty-VERSION.tar.gz 2cd ngx_openresty-VERSION/ 3./configure 4make 5make install 默认情况下，OpenResy 会安装到 /usr/local/openresty 中，编译时会默认使用 luajit。\n我一般会将其安装到 /opt/openresty 中，这就需要指定 --prefix 参数：\n1./configuare --prefix=/opt/openresty 2make 3make install 启用 PHP-FPM 以 Ubuntu 14.04 LTS(trusty) 为例，首先安装 php-fpm 。\napt-get install php5-fpm 安装成功后该进程会自动启动，查看状态：\nps aux | grep fpm 输出：\nroot 10785 0.0 1.8 133624 18308 ? Ss 14:13 0:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf) www-data 10787 0.0 1.0 133764 10956 ? S 14:13 0:00 php-fpm: pool www www-data 10788 0.0 0.9 133624 9280 ? S 14:13 0:00 php-fpm: pool www 注意，php-fpm 是使用 www-data 用户权限运行的。\n修改 /opt/openresty/nginx/conf/nginx.conf 配置文件，找到下面的内容取消注释：\n# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \\.php$ { root html; fastcgi_pass 127.0.0.1:9000 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME html$fastcgi_script_name; include fastcgi_params; } 默认情况下，会报错：\n2015/01/16 14:09:03 [error] 10614#0: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 59.175.48.91, server: localhost, request: \u0026quot;GET /info.php HTTP/1.1\u0026quot;, upstream: \u0026quot;fastcgi://127.0.0.1:9000\u0026quot;, host: \u0026quot;yourname.com:8080\u0026quot; 这是因为，php-fpm 默认没有使用端口监听。查看 /etc/php5/fpm/pool.d/www.conf 可以找到下面的配置：\n; The address on which to accept FastCGI requests. ; Valid syntaxes are: ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on ; a specific port; ; 'port' - to listen on a TCP socket to all addresses on a ; specific port; ; '/path/to/unix/socket' - to listen on a unix socket. ; Note: This value is mandatory. listen = /var/run/php5-fpm.sock 我们可以在这里将 php-fpm 修改成监听端口，也可以直接修改 nginx.conf 配置文件。在这里我采用第二个方案，修改 fastcgi_pass ：\nfastcgi_pass unix:/var/run/php5-fpm.sock; 重载配置文件：\nnginx -s reload 如果出现这样的错误，说明 nginx 进程没有权限访问 sock 。\n2015/01/16 14:19:58 [crit] 11022#0: *6 connect() to unix:/var/run/php5-fpm.sock failed (13: Permission denied) while connecting to upstream, client: 59.175.48.91, server: localhost, request: \u0026quot;GET /info.php HTTP/1.1\u0026quot;, upstream: \u0026quot;fastcgi://unix:/var/run/php5-fpm.sock:\u0026quot;, host: \u0026quot;youname.com:8080\u0026quot; 我们注意到前面 php-fpm 是使用 www-data 权限运行的。要解决这个问题，将 nginx worker 进程的权限也改为 www-data 。编辑 nginx.conf ：\n将 user nobody; 改为 user www-data; 。\n然后必须使用 nginx -s quit 退出主进程再重启，这个修改才能生效。\n（全文完）\n","date":"2015-01-16","description":"","lastmod":"2015-01-20T13:47:03Z","slug":"from-lighttpd-to-openresty","tags":["openresty","server","nginx","linux","fromto"],"title":"从 Lighttpd 到 OpenResty","url":"https://blog.zengrong.net/post/from-lighttpd-to-openresty/"},{"categories":["technology"],"content":"这个服务器架构不一定能用上，记录在这里，算是一个小小的学习成果。\n1. 技术选择 Cocos2d-x 3.x —— 客户端框架。 WebSockt —— 网络协议。 HTTP —— 网络协议。 OpenResty —— 基于 nginx+lua 实现 WebSocket 或 HTTP 服务器。 MySQL —— 数据库支持。 Redis —— NoSQL 支持。 2. 逻辑服务器 有两个不同的客户端需要提供服务。data_tester 和 client 。它们都需要 WebSocket 服务， client 还需要 HTTP 服务。\ndtserver 为 data_tester 提供 WebSocket 服务。\nsocketserver 为 client 提供 WebSocket 服务，httpserver 为 client 提供 HTTP 服务。\ndtserver/socketserver/httpserver 均使用 OpenResty(with lua) 实现。\n下图表述了这种关系。虚线框内代表逻辑服务器部署。\n3. 开发者分离 共有 4 个开发者 dev1~dev4 ，他们需要独立的服务器环境进行开发与测试。\n按照常规，开发者可以在自己的开发计算机上建立服务器环境进行开发，待测试完毕后，合并代码并部署到公用服务器上进行测试。\n但 OpenResty 在 Windows 系统上编译比较麻烦，于是我采取了上图所示的部署方式：\n在服务器上为每个开发者绑定一个独立的 OpenResty 端口和 master 进程，使用独立的端口号、配置文件和服务端代码(lua files)，这样能够实现完全隔离。\n对于 Redis，也进行类似的操作，每个开发者绑定一个独立的 Redis 配置文件和端口号。\nHTTP 和 MySql 服务是共享的，不需要单独分配。因为所有开发者都共享相同的用户信息。\n4. 部署工具 由于所有的代码都在远程服务器上，为了方便开发者管理服务器、更新自己的测试代码，我使用 git 库钩子和脚本来实现了一套部署工具。\n4.1 serverctrl serverctrl 是一个专用于控制服务器的 git 仓库。\n下面的 OpenResty 服务器的和 redis 服务器的配置文件模版都保存在这个 git 库中。\n4.2 OpenResty 服务器 下面的例子是中，使用 [server] 代表服务器名称，使用 [user] 代表开发者名称（也是 git 库的分支名称）。\n4.2.1 支持的服务器 OpenResty 服务器程序部署在 /opt/hhl 文件夹中，下面几个服务器都是基于 OpenResty 的：\n服务器 服务器路径 socketserver /opt/hhl/socketserver/[user] dtserver /opt/hhl/dtserver/[user] httpserver /opt/hhl/httpserver/[user] 4.2.2 每个服务器程序的文件夹结构 每个 OpenResty 服务器程序实例，都有完全独立的一套配置，以 /opt/hhl/socketserver/dev1 为例，包含这样几个子文件夹：\nlogs 存放 log 日志和 pid 文件； conf 其中只有一个 nginx.conf 配置文件，供当前的 OpenResty 服务器程序实例使用； src 服务器逻辑的 lua 文件。 4.2.3 配置文件模版 每个 OpenResty 服务器程序实例都使用不同的端口、 prefix 和配置文件，完全独立。\n上面提到的 nginx.conf 配置文件的模版位于 serverctrl 的 git 仓库中。模版文件为： nginx.[server].templ.conf ，用于替换模版的值文件为 nginx.[server].sub.py 。\n下面是 nginx.socketserver.templ.conf 的内容：\nworker_processes $WORKER_PROCESSES; error_log logs/error.log debug; events { worker_connections 1024; } http { lua_package_path \u0026quot;/opt/hhl/socketserver/$USER_NAME/src/?.lua;;\u0026quot;; lua_code_cache $LUA_CODE_CACHE; server { listen $LISTEN; server_name $SERVER_NAME; location /hhl { content_by_lua_file src/main.lua; } } } 下面是 nginx.socketserver.sub.py 的内容：\n{ 'master':{ 'WORKER_PROCESSES' : 1, 'LUA_CODE_CACHE':'off', 'LISTEN':'1080', 'SERVER_NAME':'localhost', }, 'dev1':{ 'LISTEN':'1081', }, 'dev2':{ 'LISTEN':'1082', }, 'dev3':{ 'LISTEN':'1083', }, 'dev4':{ 'LISTEN':'1084', }, } 从上面的配置可以看出，master 是配置文件基准值，下面的属性会替换掉 master 中的同名值，然后写入配置文件。\n4.3 Redis 服务器 下面的例子是中，使用 [port] 代表 redis 服务器程序使用的端口号。\nredis 服务器程序部署在 /opt/redis 文件夹中，与 OpenResty 不同，Redis 没有采用分文件夹的方式，而是采用不同的配置文件来实现隔离。\n配置文件名为： /opt/redis/etc/[port].conf 。\n每个 Redis 服务器实例，使用不同的端口号和配置文件，完全独立。\n上面提到的配置文件的模版位于 serverctrl 的 git 仓库中。模版文件为： redis.templ.conf ，用于替换模版的值文件为 redis.sub.py 。\n下面是 redis.templ.py 的内容：\ninclude /opt/redis/etc/redis.conf daemonize yes pidfile /opt/redis/var/$PORT.pid port $PORT bind $BIND tcp-keepalive 0 loglevel notice logfile $PORT.log databases 16 dbfilename $PORT.rdb dir /opt/redis/var/ appendonly no appendfilename \u0026quot;$PORT.aof\u0026quot; syslog-enabled yes syslog-ident redis-$PORT syslog-facility local5 下面是 redis.sub.py 的内容：\n{ 'master':{ 'PORT': 6379, 'BIND': '127.0.0.1 192.168.18.18', }, 'dev1':{ 'PORT': 6381, }, 'dev2':{ 'PORT': 6382, }, 'dev3':{ 'PORT': 6383, }, 'dev4':{ 'PORT': 6384, }, } 替换规则与 OpenResty 的相同。\n5. git 钩子，部署代码与重启服务 要将本地的代码部署到服务器上，只需要通过 git 钩子，在提交本地代码的同时，更新服务器上的代码即可。\n同时，不是所有的开发者都有服务器的管理权限。我使用 git 钩子提供了重启服务的功能，让所有开发者都可以实现对自己的 Redis、OpenResty 进程的控制。\n我在这篇文章中作了描述：使用 git post-receive 钩子部署服务端代码 。\n另外，由于存在 3 个服务器程序，涉及到许多通用代码和库。我将这些代码放置在一个单独的 git 项目中，不对所有的开发者可见，但提供开发者文档。这样一来，开发者在自己的服务器程序中，只需要关注逻辑相关的代码，而不必在意底层库是如何实现的。\n6. log 日志系统 由于开发者没有服务器权限，无法看到服务的出错日志。为了解决这个问题，我将所有的错误日志使用 rsyslog 来管理，同时提供了基于 web 的查看系统。这样一来，开发者们完全不必和服务器打交道了。\n我在这篇文章中作了描述：rsyslog/Python/LogAnalyzer 记录和查看日志 。\n我基于 OpenResty 提供的 resty.logger.socket 实现了从服务端的 lua 代码中将 log 提交到 rsyslog，这样开发者调试代码也可以使用这种方式。\n详情见这两个实现：\nSyslogHandler RestyAdapter （全文完）\n","date":"2015-01-14","description":"","lastmod":"2015-01-14T03:45:26Z","slug":"server-construction-for-OpenResty","tags":["nginx","openresty","linux","server"],"title":"基于 OpenResty 的服务器架构设计","url":"https://blog.zengrong.net/post/server-construction-for-openresty/"},{"categories":["technology"],"content":"这是一段分析 lua 协程（协同程序，coroutine）的代码，来自 Lua reference manual interface （略有修改）：\n1function foo (a) 2\tprint(\u0026#34;foo\u0026#34;, a) 3\treturn coroutine.yield(2*a) 4end 5 6co = coroutine.create(function (a,b) 7 print(\u0026#34;co-body1\u0026#34;, a, b) 8 local r = foo(a+1) 9 print(\u0026#34;co-body2\u0026#34;, r) 10 local r, s = coroutine.yield(a+b, a-b) 11 print(\u0026#34;co-body3\u0026#34;, r, s) 12 return b, \u0026#34;end\u0026#34; 13end) 14\t15print(\u0026#34;1----\u0026#34;) 16print(\u0026#34;main\u0026#34;, coroutine.resume(co, 1, 10)) 17print(\u0026#34;2----\u0026#34;) 18print(\u0026#34;main\u0026#34;, coroutine.resume(co, \u0026#34;r\u0026#34;)) 19print(\u0026#34;3----\u0026#34;) 20print(\u0026#34;main\u0026#34;, coroutine.resume(co, \u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;)) 21print(\u0026#34;4----\u0026#34;) 22print(\u0026#34;main\u0026#34;, coroutine.resume(co, \u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;)) 运行效果如下：\n11------ 2co-body1\t1\t10 3foo\t2 4main\ttrue\t4 52------ 6co-body2\tr 7main\ttrue\t11\t-9 83------ 9co-body3\tx\ty 10main\ttrue\t10\tend 114------ 12main\tfalse\tcannot resume dead coroutine 这里一共调用了 4 次 resume ，让我们来看看它是怎么运行的。\n第一次： 1print(\u0026#34;main\u0026#34;, coroutine.resume(co, 1, 10)) 执行 print(\u0026quot;co-body1\u0026quot;, a, b) ，a 和 b 的值为 resume 提供，a=1, b=10 ； 计算 a+1=2 ，进入 foo(a) ，同时将刚才的计算结果通过 a 参数传递，执行 print(\u0026quot;foo\u0026quot;, a)； 考虑 return coroutine.yield(2*a) ； 计算 2*a=4 ，碰到 yield，挂起 foo(a) 调用，将 4 返回给 resume 。注意，foo 的 return 还没有执行； resume 执行成功，返回 true, 4 。 第二次： 1print(\u0026#34;main\u0026#34;, coroutine.resume(co, \u0026#34;r\u0026#34;)) 从上一次挂起的 foo(a) 调用开始执行，接着执行没有完成的 return 调用； 因为 yield 返回 resume 的调用参数，此时 foo(a+1) 返回的值就是字符串 \u0026quot;r\u0026quot;。这里比较难理解。\n因为大家可能会顺理成章地认为 local r 这个变量的值应该是 yield(2*a) 中的 2*a 的值。 需要注意的是， yield 的返回值 与 yield 参数的值 是不同的。\n前者你可以将其保存在一个变量中，或者 return 它，或者不使用它（不保存 yield 的返回结果）；后者则是 resume 的返回值。 执行 print(\u0026quot;co-body2\u0026quot;, r) ，r 的值为 \u0026quot;r\u0026quot; ; 考虑 local r, s = coroutine.yield(a+b, a-b) ； 计算 a+b=11, a-b=-9 ，碰到 yield ，挂起 co 的调用，将 11 和 9 返回给 resume 。注意，此时 local r, s 的赋值还没有开始。\n这里不太好理解的是，为什么 a 的值不是 \u0026quot;r\u0026quot; ？因为 \u0026quot;r\u0026quot; 已经被上面的 yield 的返回值给消费掉了。 resume 执行成功，返回 true, 11, -9 。 第三次： 1print(\u0026#34;main\u0026#34;, coroutine.resume(co, \u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;)) 从上一次 yield 的地方开始执行，接着执行没有完成的 local r, s = 赋值。上面提到， yield 会返回 resume 的调用参数，因此 r 和 s 的值就是 \u0026quot;x\u0026quot; 和 \u0026quot;y\u0026quot; ； 执行 print(\u0026quot;co-body3\u0026quot;, r, s) 进行打印； 考虑 return b, \u0026quot;end\u0026quot; ； b 的值一直都是 10 没有变，这里直接返回了，同时返回的还有 \u0026quot;end\u0026quot; 这个字符串； 由于协程函数返回的时候，它的所有返回值都作为 resume 的返回值返回。因此这里的 resume 执行成功，返回 10, \u0026quot;end\u0026quot; 。 第四次： 1print(\u0026#34;main\u0026#34;, coroutine.resume(co, \u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;)) 由于 co 函数已经返回，它处于 dead 状态，不能 resume ，因此第 4 次 resume 失败。\n（全文完）\n","date":"2015-01-11","description":"","lastmod":"2015-01-11T08:17:00Z","slug":"lua-coroutine","tags":["lua"],"title":"lua 协程（coroutine）分析","url":"https://blog.zengrong.net/post/lua-coroutine/"},{"categories":["technology"],"content":"2017-04-13更新： 加入 php7.0 信息\n在新的项目中，我希望服务端、客户端的所有信息都通过独立的日志系统记录。这样的好处是：\n日志可以使用单独的服务器存储和管理，实现权限分离，增强生产服务器的安全性； 使用通用的格式方便分析日志； 所有日志统一处理； 客户端可以直接和日志服务器对话。 RSYSLOG 是一个高效的日志系统，也是目前 Ubuntu 和 CentOS 默认使用的日志系统。\nLogAnalyzer 是一个 PHP 写成的 Web 前端，使用它可以分析和查看 RSYSLOG 生成的日志。\n经过研究，我准备直接使用这两个系统。本文记录了我在配置这两个系统中遇到的问题。\n本文不会详细从头开始介绍 RSYSLOG 的配置，若希望得到这些信息，请参考下面的文章：\nrsyslog 配置简介 rsyslog研究 Linux系统管理实践(12)：Syslog系统日志配置 我的系统配置：\nCentOS 6.3 RSYSLOG 5.8.10 LogAnalyzer 4.1.1(v4-beta) 一、筛选日志到到独立的文件 在 使用 GIT POST-RECEIVE 钩子部署服务端代码 一文中，我提到了两个 git 库 server.git 和 serverctrl.git 。在这两个库中，我使用了 Server-side hook（服务端钩子） 实现自动部署。这两个钩子程序会产生日志信息。\n下面的设置，做了三件事：\n将 FACILITY 为 local5 ，PRIORITY 为任意值的日志写入 all.log ； 将程序名称为 hhl-git-serverctrl 的日志写入 serverctrl.log ； 将程序名称为 hhl-git-server 的日志写入 server.log 。 1local5.* /var/log/hhl/all.log 2:programname,isequal,\u0026#34;hhl-git-serverctrl\u0026#34; /var/log/hhl/serverctrl.log 3:programname,isequal,\u0026#34;hhl-git-server\u0026#34; /var/log/hhl/server.log 我将这些内容放在独立的配置文件中，存放在 /etc/rsyslog.d/ 文件夹下。它会在 rsyslogd 服务重启的时候被自动载入。\n二、LogAnalyzer 不显示 FACILITY 和 PRIORITY 截图可以参见这篇文章： Facility and severity not showing in logs and loganalyzer 。\n这是因为日志文件默认采用老的格式，而 LogAnalyzer 不完全支持这种格式。RSYSLOG WIKI 提供了解决方案。需要做下面几处修改：\n1. 修改 /etc/rsyslog.conf 查找下面的内容：\n$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat 将其替换成为：\n$ActionFileDefaultTemplate RSYSLOG_SyslogProtocol23Format 这个操作将日志格式替换成了 RFC 5424 定义的格式。替换完毕记得重启 rsyslogd 。\n2. 同上，将这个模版加入配置文件\n$template TraditionalFormatWithPRI,\u0026quot;\u0026lt;%syslogpriority%\u0026gt;%syslogseverity% %timegenerated% %HOSTNAM E% %syslogtag%%msg:::drop-last-lf%\\n\u0026quot; 3. 进入 LogAnalyzer 的 Admin Center 界面，修改 Source 使其使用 RSyslog Format23(RFC 5424) 。\n关于 LogAnalyzer ，这里还有一篇文章可以参考： 解决rsyslog+loganalyzer不能同时显示IP和主机名\n2017-04-13 更新：\n注意，Ubuntu 16.04 自带的 php 源为 7.0 。但 LogAnalyzer 最新版(LogAnalyzer v4.1.5) 的安装文件使用的是 mysql_connect 在 PHP 7.0 中已经被移除了，这导致在安装 LogAnalyzer 并启用用户数据库支持的时候，安装程序会报错。要解决这个问题，必须安装 PHP 5.x 。使用下面的代码安装 php5.6 及其相关支持：\n1sudo add-apt-repository ppa:ondrej/php 2sudo apt-get update 3sudo apt-get install php5.6-fpm php5.6-mysql 三、使用 Python 写入日志 使用 Python 的 logging 包可以直接将日志写入 RSYSLOG 。在上面的配置文件中，我指定 FACILITY 是 local5 ，因此这里给出了相同的值。\n还要注意 Formatter 第一个参数的第二段就是程序名称，RSYSLOG 可以依赖这个名称来筛选日志，写入到不同的文件。\n#!/usr/bin/env python import logging from logging.handlers import SysLogHandler log = logging.getLogger('git') log.setLevel(logging.DEBUG) log_hdlr = SysLogHandler(facility=SysLogHandler.LOG_LOCAL5, address='/dev/log') log_format = logging.Formatter( '%(asctime)s hhl-%(name)s-server[%(process)d]: %(message)s:', '%b %e %H:%M:%S') log_hdlr.setFormatter(log_format) log.addHandler(log_hdlr) 如果希望同时加上 print 效果，加入 StreamHandler 即可。\n#!/usr/bin/env python log_hdlr2 = logging.StreamHandler(sys.stdout) log_hdlr2.setFormatter(log_format) log.addHandler(log_hdlr2) ","date":"2014-12-19","description":"","lastmod":"2017-04-13T13:44:10Z","slug":"rsyslog-loganalyzer-python","tags":["linux","python"],"title":"rsyslog/Python/LogAnalyzer 记录和查看日志","url":"https://blog.zengrong.net/post/rsyslog-loganalyzer-python/"},{"categories":["technology"],"content":"在 git 中提交服务器源码的时候，如果能够直接更新到测试服务器，并且重启服务使其生效，会节省懒惰的程序员们大量的时间。\ngit 的 Server-side hook （服务端钩子/挂钩）可以用来做件事。\n本文以部署基于 OpenResty 的服务端程序为例来介绍我的做法。\n技术信息\nOS： CentOS 6.3 服务器软件： OpenResty 开发语言： Lua 名词解释\n服务器： 服务器硬件 + OS 服务端程序： OpenResty 在服务器中的进程 服务端代码： 部署在 OpenResty 中的 Lua 源程序 一、git 服务端钩子类型 Pro git 中介绍了 git 钩子的几种类型，其中和服务端相关的有：\npre-receive 在客户端推送时最先执行，可以用它来拒绝客户端的推送。 update 与 pre-receive 类似，但会在每个分支都执行一次。 post-receive 在客户端推送完成后执行。 根据我的需求，我选择 post-receive 钩子来做这件事。因为我不希望拒绝客户端的推送（那样程序员们可能不知道该怎么办）。推送总是会成功的，只是 命令 不成功而已。碰到 命令 不成功的情况，客户端只需要再次推送一个正确的 命令 即可。\n关于 命令 的配置，后面会详述。\n二、git repostories 我建立了2个 git 仓库来完成这个任务。分成2个仓库的好处是，更新服务端代码和控制服务端程序互不干扰。\n在开发服务器上，我可以将 OpenResty 的 lua file 缓存关闭。这样服务端代码更新后会立刻生效，不必重启服务端程序。\n而如果服务端程序出现错误，只需要更新它的状态（reopen/reload 等）即可，不必更新服务端代码。\nserver.git 这个仓库保存服务端逻辑代码，客户端的文件夹结构如下：\nserver ├── README.md └── src └── main.lua 每次提交代码的时候，在提交信息中可以包含特定的 命令 ，在推送这次提交时，git 服务端钩子就会起作用，将提交的代码更新到合适的地方。\n如果提交信息中没有特定的 命令 ，那么这就是一次普通的推送而已。\n在本例中，钩子所做的事情就是将 src/ 文件夹中的所有代码更新到服务端程序中。\nserverctrl.git 这个仓库是空的，永远不会有内容。通过在提交信息中包含特定的 命令，git 服务器钩子会对我们的服务端程序进行给定的操作。\n三、使用钩子重启服务端程序 OpenResty 的进程控制 使用 nginx 自己提供的 -s 参数来控制服务端程序：\nnginx -s [stop|quit|reopen|reload] -p /path/to 不带 -s 参数，就是 start 功能了：\nnginx -p /path/to 命令 serverctrl 是个空项目，不可能有任何内容。因此我规定提交信息中直接写操作命令即可。\n要控制服务端程序重启，只需要进行这样的提交和推送：\ngit commit --allow-empty -m 'reopen' \u0026amp;\u0026amp; git push origin zrong 执行 [start|stop|quit|reload] 也是一样道理。\n具体代码 下面的代码展示了 serverctrl 项目中 post-receive 钩子的内容。钩子可以用操作系统能够识别的任意脚本语言来撰写。这里我使用的是 Python （2.7和3.4通用）。\n下面的代码和注释已经非常清楚了，我说一下几点要注意的。\n在 callnginx 方法中，需要把 subprocess.check_output 方法的 stderr 参数设置为 STDOUT 。因为如果执行 nginx 出错，出错信息默认是写入到 STDERR 中的，如果不进行这样的修改，出错时返回的输入信息就是空的。\n需要把 nginx 程序以及 '/opt/openresty/nginx' 整个文件夹和其下文件的 owner 设置为 git 用户，否则钩子将没有权限启动 nginx 进程。\npost-receive 钩子本身的 owner 也要设置成 git 用户，并给予执行权限。\n如果已经有一个 -p 参数（prefix）相同的 nginx 进程在运行了，注意先将其结束。否则 git 用户可能无权关闭这个进程。\n#!/usr/bin/env python import sys import os import subprocess # prefix 配置路径 openresty = '/opt/openresty/nginx' # 执行文件路径 nginx = openresty + '/sbin/nginx' # 能够识别的信号 signals = ('start', 'stop', 'quit', 'reopen', 'reload') # 支持的分支（用户） branches = ('master', 'allen', 'zrong', 'xiefei', 'zm') def getgitargs(*args): if args: return ['git', '--bare', '--git-dir', os.getcwd()] + list(args) return [] def callgit(*args): return subprocess.check_output(['env', '-i'] + getgitargs(*args), universal_newlines=True) def callnginx(action): args = [nginx, '-p' ,openresty] if action != 'start': args += ['-s', action] return subprocess.check_output(args, stderr=subprocess.STDOUT, universal_newlines=True) # 钩子会将信息从 STDIN 写入，将这些信息读入变量 oldrev,newrev,refname = sys.stdin.readline().strip().split(' ') # 对我们的程序而言，只有 branch 名称有用 branch = refname.split('/')[-1] print('oldref:%s, newrev:%s, refname:%s'%(oldrev, newrev, refname)) if not branch in branches: print('The branch \u0026quot;%s\u0026quot; is not in available list! ' 'The list is %s.'%(branch, str(branches))) exit(1) try: # 得到当前提供的 branch 下的最新提交信息 commitmsg = callgit('log', branch, '--oneline', '-1')[8:-1] print(branch+' '+commitmsg) if commitmsg in signals: callnginx(commitmsg) print('======= %s %s SUCCESS ======='%(branch, commitmsg)) else: print('The signal \u0026quot;%s\u0026quot; is not in available list! ' 'The list is %s.'%(commitmsg, str(signals))) except subprocess.CalledProcessError as err: print('======= %s %s %s ERROR ======='%(branch, commitmsg, err.output)) exit(1) exit(0) 四、从服务器的 git bare repostory 中部署服务端代码 在这个例子中，我假设 git 仓库和测试服务器处于同一台服务器上。这也是小团队开发比较普遍的情况。\n服务端代码的结构 服务端代码位于 /opt/openresty/nginx/lua 这个文件夹中。在这个文件夹中有按照用户名称（分支名称）建立的子文件夹，使用这种方式，可以让多个开发者的服务端代码互不干扰，独立运行。\n下面的文件夹结构展示了目前有两个开发者 master 和 zrong 已经部署了自己的服务端代码。\n/opt/openresty/nginx/lua/ ├── master │ └── main.lua └── zrong └── main.lua 程序员甚至可以选择部署别人的代码到自己的文件夹中。它只需要在执行命令的时候提供希望部署的用户的分支名称（或者干脆提供一个 git 的 commit sha1），就能达到这种效果。\n命令 server 包含服务端代码，命令需要区分是代码内容推送还是代码部署推送，或者二者兼有。\n命令的设计如下：\n若提交信息的第一行包含 UP 字样，就代表是命令推送。如果同时还有其他的提交信息，可以换行写。\n如果只是普通的提交，那么直接写就可以了。\n假设当前的 git 库位于 zrong 分支，而且当前的 git 仓库是干净的，下面是几个例子：\nSample 1 将已经提交过的代码推送到服务器，并更新服务端代码：\ngit commit --allow-empty -m 'UP' \u0026amp;\u0026amp; git push origin zrong Sample 2 将 server 仓库中的 allen 分支的内容更新到 /opt/openresty/nginx/lua/zrong ：\ngit commit --allow-empty -m 'UP allen' \u0026amp;\u0026amp; git push origin zrong Sample 3 将 sha1 为 7ebbf4f3151e2dfd5bdcbe9fe276fc9b6afd25e7 的提交中的内容更新到 /opt/openresty/nginx/lua/zrong ：\ngit commit --allow-empty -m 'UP 7ebbf4f' \u0026amp;\u0026amp; git push origin zrong 导出一个 bare 仓库中的内容 位于服务器上的 git 仓库，通常是 bare 的，它没有 work-tree ，不能直接通过复制的方式来更新服务端代码。\n但我们可以使用 git archive 命令先将仓库中的代码导出成一个包，然后再解压这个包到合适的部署路径即可实现服务端代码更新。\n下面的代码做这样几件事：\n找到 server.git 这个 bare 仓库； 处理 zrong 分支； 将 src/ 文件夹打包成 src.tar 文件。 git --git-dir server.git archive -o src.tar zrong src/ 如果只想要其中的一个文件，我们可以用 git show 把这个文件的内容输出到一个文件：\ngit --git-dir server.git show zrong:src/main.lua \u0026gt; main.lua 具体代码 下面的代码展示了 server 项目中 post-receive 钩子的内容。\n#!/usr/bin/env python import sys import re import os import subprocess import shutil luahome = '/opt/openresty/nginx/lua' # 使用正则获取命令代码和要处理的分支信息 actionfmt = r'^UP ?(\\w+)?$' branches = ('master', 'allen', 'zrong', 'xiefei', 'zm') def getgitargs(*args): if args: return ['git', '--bare', '--git-dir', os.getcwd()] + list(args) return [] def callgit(*args): return subprocess.check_output(['env', '-i'] + getgitargs(*args), universal_newlines=True, stderr=subprocess.STDOUT) # 从提交信息中得到要处理的真正分支 def getref(branch): commitmsg = callgit('log', branch, '--oneline', '-1')[8:-1] matchobj = re.search(actionfmt, commitmsg) if matchobj: if matchobj.group(1): return matchobj.group(1) return branch return None # 将 server/src 备份到一个文件 def archive(refname): tarfile = '%s/%s.tar'%(luahome, refname) callgit('archive', '-o', tarfile, refname, 'src/') return tarfile # 解压缩备份文件到正确的文件夹 def tarxf(tarfile, refname, branch): refdir = os.path.join(luahome, branch) if os.path.exists(refdir): shutil.rmtree(refdir) args = ['tar', 'xf', tarfile, '-C', luahome] output = subprocess.check_output(args, stderr=subprocess.STDOUT, universal_newlines=True) shutil.move(os.path.join(luahome, 'src/'), refdir) os.remove(tarfile) return output oldrev,newrev,refname = sys.stdin.readline().strip().split(' ') print('oldref:%s, newrev:%s, refname:%s'%(oldrev, newrev, refname)) branch = refname.split('/')[-1] if not branch in branches: print('The branch \u0026quot;%s\u0026quot; is not in available list! ' 'The list is %s.'%(branch, str(branches))) exit(1) try: refname = getref(branch) if refname: tarfile = archive(refname) succ = tarxf(tarfile, refname, branch) print('update [%s] by [%s] success.'%(branch, refname) ) print('======= update [%s] by [%s] SUCCESS ======='%(branch, refname)) except subprocess.CalledProcessError as err: print('update [%s] by [%s] error: %s'%(branch, refname, err.output)) print('======= update [%s] by [%s] ERROR ======='%(branch, refname)) exit(1) exit(0) ","date":"2014-12-17","description":"","lastmod":"2014-12-19T01:52:25Z","slug":"distribute-by-git-post-receive-hook","tags":["python","git","openresty"],"title":"使用 git post-receive 钩子部署服务端代码","url":"https://blog.zengrong.net/post/distribute-by-git-post-receive-hook/"},{"categories":["technology"],"content":"nginx 提供了 -s signal 命令行参数让我们对其进行操作。我们可以很方便地利用 -p prefix 参数指定启动路径来启动多个完全隔离的 master 进程；还可以使用 -c filename 显式指定不同的配置文件。\n由于 prefix 的路径一般都比较长， reload 或者 reopen nginx master 进程就会比较不方便。此时可以采用我在 在 OS X 中使用 OpenResty - 3.快捷方式 中提到的自建快捷方式的形式来简化这些操作。\n更一般的，在服务器上我们会采用操作系统提供的标准服务的方式来进行这些简化。当然，这种方法一般仅针对一个 nginx master 进程进行操作。\n本文将介绍在多个操作系统上将 nginx 注册为服务的方式（其实，也没有那么多啦）。\n1. Mac OS X 参照 在 OS X 中使用 OpenResty - 2. 配置 OpenResty 环境 进行配置即可。\n2. CentOS 根据 CentOS Nginx的一个初始化脚本(用于启动、停止、查看状态) 一文提供的方法，我整理了代码到 gist 上，只需要将 nginxd 下载到 /etc/init.d 并执行 addnginxservice 脚本即可。\n下面的代码自动帮你做了上面的事：\nwget --no-check-certificate https://gist.githubusercontent.com/zrong/9c7dfce8f274ee451188/raw/77eada5f92dd5583838390f26cc9790b00e63137/nginxd wget --no-check-certificate https://gist.githubusercontent.com/zrong/9c7dfce8f274ee451188/raw/a0084d1ae6d5175c913e4593c99d493487d14c75/addnginxservice \u0026amp;\u0026amp; source addnginxservice 3. Ubuntu 已经都是 Ubuntu了，自然是直接 apt-get 啦。这个没什么好说的。\n4. Windows nginx 的 Windows 二进制包并没有提供注册成为服务的功能。官方提供的方法和 Linux 相同，都是使用命令行参数进行操作。例如下面的 来自官方wiki 的方法，进入 nginx 文件夹，启动nginx进程：\ncd nginx/ start nginx.exe 使用下面的命令查看 nginx 进程是否正在运行：\ntasklist /fi \u0026quot;imagename eq nginx.exe\u0026quot; 关闭 nginx 进程：\nnginx.exe -s quit nginx 并不是为 windows 开发的，windows 版本的作用是为了方便在 windows 上工作的程序员们使用 nginx 。因此，nginx 好像并没有什么需要成为一个服务的动力。尽管 Running as a service. 出现在了 Possible future enhancements 列表中。\n如果一定要在 windows 下将其注册为服务的话，把 Nginx 创建为 Windows 的一个服务 （原文）一文提供了一种方式。这是一篇写于 2009 年的文章，我并没有在我的 Windows 8.1 上尝试，因为我直接使用 Vagrang 了。\n（全文完）\n","date":"2014-12-12","description":"","lastmod":"2014-12-12T02:52:56Z","slug":"register-nginx-as-a-service","tags":["nginx","server","http","openresty"],"title":"将 nginx 注册为系统服务","url":"https://blog.zengrong.net/post/register-nginx-as-a-service/"},{"categories":["technology"],"content":"我曾经写过 在 Mac OS X 上安装lighttpd ，从那时起，我的 Mac 上的 HTTP 服务器一直是 lighttpd。\n现在，为了学习 OpenResty ，我将 lighttpd 进行了替换。下面记录这个过程。\n1. 移除 lighttpd 在 Mac OS X 上安装lighttpd 的时候，我使用 launchctl 来实现 lighttpd 的自动启动。现在，只需要 unload 即可。\nlaunchctl unload ~/Library/LaunchAgents/homebrew.mxcl.lighttpd.plist 这会立即停止 lighttpd 的运行。接下来，将 homebrew.mxcl.lighttpd.plist 从~/Library/LanuchAgents 目录中移除。否则下次启动系统的时候，lighttpd 又会启动。\n2. 配置 OpenResty 环境 2.1 编译和安装 OpenResty 的编译和安装流程，在官网上讲得很清楚，我不再赘述。\n在这里我假设 OpenResty 已经安装到默认路径 /usr/local/openresty 。\n修改 /usr/local/openresty/nginx/conf/nginx.conf ，将其中的 server 段下的 listen 80 改为 listen 8080 。\n这是因为 80 端口只能被 root 用户启动。而在本文中我们是使用当前用户启动 nginx 的。\n如果 8080 端口也被占用，请自行换成可用端口。\n2.2 创建 plist 文件 launchctl 依赖一个 plist 配置文件来工作。我们需要手动创建这个文件。\nplist 是一种标准的 xml 格式，这种格式的详细介绍，可以看这里：cocos2d-x中的plist文件格式详解 。\nlaunchctl 对这个配置文件的格式有一些具体的要求，可以查看 launchd.plist 。\n我们创建的 ~/Library/LaunchAgents/org.openresty.plist 文件，内容如下：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; 2\u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; 3\u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; 4\u0026lt;dict\u0026gt; 5 \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; 6 \u0026lt;string\u0026gt;org.openresty\u0026lt;/string\u0026gt; 7 \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; 8 \u0026lt;array\u0026gt; 9 \u0026lt;string\u0026gt;/usr/local/openresty/nginx/sbin/nginx\u0026lt;/string\u0026gt; 10 \u0026lt;string\u0026gt;-p\u0026lt;/string\u0026gt; 11 \u0026lt;string\u0026gt;/usr/local/openresty/nginx\u0026lt;/string\u0026gt; 12 \u0026lt;/array\u0026gt; 13 \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; 14 \u0026lt;true/\u0026gt; 15 \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; 16 \u0026lt;false/\u0026gt; 17 \u0026lt;key\u0026gt;HardResourceLimits\u0026lt;/key\u0026gt; 18 \u0026lt;dict\u0026gt; 19 \u0026lt;key\u0026gt;NumberOfFiles\u0026lt;/key\u0026gt; 20 \u0026lt;integer\u0026gt;512\u0026lt;/integer\u0026gt; 21 \u0026lt;/dict\u0026gt; 22 \u0026lt;key\u0026gt;SoftResourceLimits\u0026lt;/key\u0026gt; 23 \u0026lt;dict\u0026gt; 24 \u0026lt;key\u0026gt;NumberOfFiles\u0026lt;/key\u0026gt; 25 \u0026lt;integer\u0026gt;512\u0026lt;/integer\u0026gt; 26 \u0026lt;/dict\u0026gt; 27\u0026lt;/dict\u0026gt; 28\u0026lt;/plist\u0026gt; 注意其中 Label、Program和ProgramArguments 这三个 Key 是必须存在的。\nKeepAlive 这个值建议设置成 false ，除非在 nginx.conf 中设置了 daemon off 。Wayne 在 launchctl笔记 中作了解释：\n设定nginx登录后自启动后发现，由于缺省情况下是生成子进程后父进程立即退出，导致launchctl在KeepAlive配置的作用下反复启动nginx，产生了很多错误信息，所以在设置守护进程的时候要注意规避这类问题，nginx可以通过设置daemon off;或者去除掉KeepAlive设置来解决。\n2.3 载入任务 1launchctl load ~/Library/LaunchAgents/org.openresty.plist 这条命令会直接启动 openresty ，下次系统重启的时候，也会自动启动。\n2.4 使用 80 端口 既然是自己用，那么使用 8080 总是让人很不爽。要使用 80 端口，也很简单。\n首先，将 ~/Library/LaunchAgents/org.openresty.plist 复制到 /Library/LaunchDaemons/org.openresty.plist ：\n1cp ~/Library/LaunchAgents/org.openresty.plist /Library/LaunchDaemons/org.openresty.plist /Library/LaunchDaemons 是给管理员使用的，在用户登录前生效，以 root 身份执行任务。\n为什么不复制到 /Library/LaunchAgent 中呢？因为 Wayne 在 launchctl笔记 中提到：\nLaunchAgents下的plist都会以当前登录用户的身份load进来……\n然后，移除当前的监听：\n1launchctl unload ~/Library/LaunchAgents/org.openresty.plist 2rm ~/Library/LaunchAgents/org.openresty.plist 最后，用 sudo 调用 launchctl：\n1sudo launchctl load /Library/LaunchDaemons/org.openresty.plist 当然，记得要把 nginx 配置文件中的监听端口改成 80 。\n3. 快捷方式 在开发过程中，经常需要重启 nginx 进程。在我的电脑上，有两个 nginx 进程，一个负责正常的 HTTP 服务；一个负责测试 OpenResty 功能。\n我写了一个脚本 openresty 用于快速操作 nginx 进程。\n1#!/bin/bash 2sign=${1:-reload} 3prefix=${2:-1} 4 5if [ \u0026#34;$prefix\u0026#34; = 1 ]; then 6 prefix=\u0026#39;/usr/local/etc/openresty\u0026#39; 7else 8 prefix=\u0026#34;$hhl/server\u0026#34; 9fi 10 11echo \u0026#34;nginx -s $sign -p $prefix\u0026#34; 12nginx -s \u0026#34;$sign\u0026#34; -p \u0026#34;$prefix\u0026#34; 在实际使用中，我只需要这样调用就行了：\n1#!/bin/bash 2openresty reload 1 3openresty reopen 2 ","date":"2014-12-10","description":"","lastmod":"2014-12-10T10:43:42Z","slug":"using-openresty-in-osx","tags":["osx","server","http","nginx","openresty"],"title":"在 OS X 中使用 OpenResty","url":"https://blog.zengrong.net/post/using-openresty-in-osx/"},{"categories":["technology"],"content":"基于 Python 打包成 exe 的工具，主要有这样几个：\npy2exe pyinstaller cx-Freeze 但遗憾的是，目前只有 cx-Freeze 明确表示支持 Python3 。pyinstaller 则有一个试验性分支在进行这方面的测试。\n我有一个项目 data_tester 使用 Python3.4+PyQt5.3 开发，需要使用 cx-Freeze 将其打包成 exe 格式，提供给其它同事在 Windows 7(64bit) 系统中使用。\n具体打包的方法，查看 cx-Freeze 官方文档 即可知晓，这篇文章中仅仅记述我踩过的几个坑。\n_fix_up_module 打包成功后，运行出现这样的错误：\n这是因为，在 PiPy 上安装的 cx-Freeze 有问题 。需要在 这里下载一个独立的 Windows 编译版本 进行安装。\n由于我们使用的是 64bit 系统，我下载的是 cx_Freeze‑4.3.3.win‑amd64‑py3.4.exe ，双击安装即可。注意安装前先卸载在 PyPi 上安装的 cx-Freeze 包。\nNo such file or directory 使用 exe 版本的 cx-Freeze 打包成功后，运行时又出现新的错误：\n在程序中，我们经常需要获取项目所在的当前路径，用来读取一些配置文件或资源。\n在 data_tester 中，我需要读取一个位于 hhl 包中的 dt.conf 资源文件。下面是读取代码：\n:::python workDir = os.path.abspath(os.path.join( os.path.split(os.path.abspath(__file__))[0], os.pardir)) templFile = os.path.join(workDir, 'hhl', 'dt.conf') 而在 exe 版本中，由于所有的 python 代码都被打包到 library.zip 文件中了，__file__ 的值就变成了 library.zip 中的相关路径。这样去取一个资源文件，显然是取不到的。\n还有一个问题，就是 cx-Freeze 并不会将非 python 文件自动打包到最终的 exe 所在的文件夹中。我们需要在 setup.py 中指定我们需要打包的资源文件。这些指定的文件会被直接复制到 exe 所在的文件夹中，而不会被打包到 library.zip 中。\n:::python options = { 'build_exe':{'packages':packages, 'include_files':['hhl/dt.conf']}, } 这样进行处理之后，dt.conf 这个资源文件在 exe 版本中的路径和非 exe 版本中的路径就不同了。我们需要判断当前执行的是否是 exe 版本，根据结果来读取不同路径的 dt.conf 。\n下面的代码进行了这样的处理：\n#!/usr/bin/env python # 当位于 exe 版本中时，可以使用 os.sys.executable 来获取当前执行文件的路径 if hasattr(sys, 'frozen'): workDir = os.path.dirname(os.path.join(os.sys.executable)) templFile = os.path.join(workDir, 'dt.conf') else: workDir = os.path.abspath(os.path.join( os.path.split(os.path.abspath(__file__))[0], os.pardir)) templFile = os.path.join(workDir, 'hhl', 'dt.conf') 这是完整的 setup.py 文件：\n#!/usr/bin/env python import sys from cx_Freeze import setup, Executable base = None if sys.platform == 'win32': base = 'Win32GUI' packages = [ 'hhl', 'hhl.protos', 'hhl.components', 'hhl.libs.helper', 'hhl.libs.net', ] options = { 'build_exe':{'packages':packages, 'include_files':['hhl/dt.conf']}, } executables = [Executable('main.py', base=base, targetName='datatester.exe')] setup( name=\u0026quot;datatester\u0026quot;, version=\u0026quot;1.0.0\u0026quot;, url='http://zengrong.net/', author='zrong', author_email='zrongzrong@gmail.com', description=\u0026quot;A data tester for HHL.\u0026quot;, options=options, executables=executables, ) Could not load the Qt platform plugin \u0026quot;windows\u0026quot; 经历了上面两个坑，在打包的电脑上，运行成功了。但是把打包结果复制到其它电脑上运行时，出现了这样的错误：\npython 3.3, pyqt5 and cx_freeze exe on 64-bit windows 一文中提到，将 libEGL.dll 复制到 exe 相同目录可以解决这个问题。这个文件可以在 PyQt5 的安装文件夹中找到。\n","date":"2014-12-09","description":"","lastmod":"2014-12-09T07:53:29Z","slug":"cx_freeze-package-python34-and-pyqt53","tags":["python","qt","gui"],"title":"cx_Freeze 打包 Python3.4+PyQt5.3","url":"https://blog.zengrong.net/post/cx_freeze-package-python34-and-pyqt53/"},{"categories":["technology"],"content":"我曾经在 Python 虚拟环境 一文中介绍过 Python 的几种虚拟环境。 我使用的是 Python 3.4 ，自带虚拟环境 venv ，因此就没有安装 virtualenv 。\n今天在虚拟环境中使用 PyQt5 的时候，出现了问题。Python 告诉我说找不到 PyQt5 这个模块，但其实我的 PyQt5 已经安装在主环境中了。\n要解决这个问题，只需要把主环境中的 PyQt5 模块复制到虚拟环境中即可。\n下面举例说明。\n在我的 Mac OS X 中，PyQt5 的安装路径为：\n/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/PyQt5 /Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/sip.so 虚拟环境的 site-package 路径为：\n/Users/zrong/pythonenv/hhl/lib/python3.4/site-packages 将 PyQt5 整个文件夹和 sip.so 复制到虚拟环境的 site-packages 文件夹中，即可解决这个问题。\n当然，也可以用符号链接的方式，参见：Is it possible to add PyQt4/PySide packages on a Virtualenv sandbox?\n","date":"2014-12-08","description":"","lastmod":"2014-12-08T03:52:34Z","slug":"pythonvenv-with-pyqt5","tags":["python","gui","qt"],"title":"在 Python 虚拟环境中使用 PyQt5","url":"https://blog.zengrong.net/post/pythonvenv-with-pyqt5/"},{"categories":["technology"],"content":"2016-09-15 更新： 加入 PDF 版本网络协议图\n去年光棍节的时候，我写过一篇 quick-cocos2d-x 中的 socket 技术选择：LuaSocket 和 WebSocket 。这篇文章介绍了我为何决定在项目中使用 LuaSocket 。\n现在想起来，当时对 WebSocket 是很感兴趣的，但由于服务端的限制，最终依然选择了 LuaSocket。我后来对 LuaSocket 进行了封装，使其更好用。\n现在，面对一个全新的项目，我自然而然地选择了 WebSocket。\n因此，我需要了解下面这些问题：\nSocket 和 WebSocket 有哪些区别和联系？ WebSocket 和 HTML5 是什么关系？ 必须在浏览器中才能使用 WebSocket 吗？ WebSocket 能和 Socket 一样传输 raw 数据么？ WebSocket 和 Socket 相比会多耗费流量么？ 但是，目前网上全面介绍这两种协议的中文文章并不多，或者说不够全面。我无法找到一篇文章能解决上面的所有问题。因此，我写了本文，把找到的 Socket 和 WebSocket 的相关资料做一个梳理，以方便理解。\n本文并不能直接完整回答上面提出的几个问题，但读完本文，要理解上面的那些问题，是很容易的事。\n由于能力有限，本文不可能很长。而且，技术细节并非所有人都愿意仔细了解。本文包含了大量的外部链接，跟随这些链接，可以找到足够多的细节，满足你/我的求知欲。\n1. 概述 选择了 WebSocket 技术之后，不可避免的，我要将它和其他协议以及技术做一下比较。最常见的，就是需要比较 WebSocket 与 HTTP、Socket 技术的异同。\nWebSocket 是为了满足基于 Web 的日益增长的实时通信需求而产生的。在传统的 Web 中，要实现实时通信，通用的方式是采用 HTTP 协议不断发送请求。但这种方式即浪费带宽（HTTP HEAD 是比较大的），又消耗服务器 CPU 占用（没有信息也要接受请求）。（下图来自 WebSocket.org）\n而是用 WebSocket 技术，则会大幅降低上面提到的消耗：（下图来自 websocket.org）\n关于更详细的描述，尹立的这篇文章讲得非常好：WebSocket（2）--为什么引入WebSocket协议 。\n那么，WebSocket 到底与 HTTP 协议到底是一个什么样的关系呢？它和 Socket 又有什么联系？这就要讲到 OSI 模型和 TCP/IP 协议族。\n2. OSI 模型与 TCP/IP 以下是 维基百科 中关于OSI 模型的说明：\n开放式系统互联通信参考模型（英语：Open System Interconnection Reference Model，ISO/IEC 7498-1），简称为OSI模型（OSI model），一种概念模型，由国际标准化组织（ISO）提出，一个试图使各种计算机在世界范围内互连为网络的标准框架。\n而 TCP/IP 协议可以看做是对 OSI 模型的一种简化（以下内容来自 维基百科）：\n它将软件通信过程抽象化为四个抽象层，采取协议堆叠的方式，分别实作出不同通信协议。协议套组下的各种协议，依其功能不同，被分别归属到这四个阶层之中7，常被视为是简化的七层OSI模型。\n这里有一张图详细介绍了 TCP/IP 协议族中的各个协议在 OSI模型 中的分布，一图胜千言（下图来自 科来）：\n这里是 PDF 版：\n1 文件 TCP/IP 协议和 OSI 模型的内容，在互联网上有很多。我没有必要再次介绍它们。在这里，我们只需要知道，HTTP、WebSocket 等协议都是处于 OSI 模型的最高层： 应用层 。而 IP 协议工作在网络层（第3层），TCP 协议工作在传输层（第4层）。\n至于 OSI 模型的各个层次都有什么系统和它们对应，这里有篇很好的文章可以满足大家的求知欲：OSI七层模型详解 。\n3. WebSocket、HTTP 与 TCP 从上面的图中可以看出，HTTP、WebSocket 等应用层协议，都是基于 TCP 协议来传输数据的。我们可以把这些高级协议理解成对 TCP 的封装。\n既然大家都使用 TCP 协议，那么大家的连接和断开，都要遵循 TCP 协议中的三次握手和四次握手 ，只是在连接之后发送的内容不同，或者是断开的时间不同。\n更详细内容可阅读：wireshark抓包图解 TCP三次握手/四次挥手详解\n对于 WebSocket 来说，它必须依赖 HTTP 协议进行一次握手 ，握手成功后，数据就直接从 TCP 通道传输，与 HTTP 无关了。\n4. Socket 与 WebScoket Socket 其实并不是一个协议。它工作在 OSI 模型会话层（第5层），是为了方便大家直接使用更底层协议（一般是 TCP 或 UDP ）而存在的一个抽象层。\n最早的一套 Socket API 是 Berkeley sockets ，采用 C 语言实现。它是 Socket 的事实标准，POSIX sockets 是基于它构建的，多种编程语言都遵循这套 API，在 JAVA、Python 中都能看到这套 API 的影子。\n下面摘录一段更容易理解的文字（来自 http和socket之长连接和短连接区别）：\nSocket是应用层与TCP/IP协议族通信的中间软件抽象层，它是一组接口。在设计模式中，Socket其实就是一个门面模式，它把复杂的TCP/IP协议族隐藏在Socket接口后面，对用户来说，一组简单的接口就是全部，让Socket去组织数据，以符合指定的协议。\n主机 A 的应用程序要能和主机 B 的应用程序通信，必须通过 Socket 建立连接，而建立 Socket 连接必须需要底层 TCP/IP 协议来建立 TCP 连接。建立 TCP 连接需要底层 IP 协议来寻址网络中的主机。我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机，但是一台主机上可能运行着多个应用程序，如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定。这样就可以通过一个 Socket 实例唯一代表一个主机上的一个应用程序的通信链路了。\n而 WebSocket 则不同，它是一个完整的 应用层协议，包含一套标准的 API 。\n所以，从使用上来说，WebSocket 更易用，而 Socket 更灵活。\n5. HTML5 与 WebSocket WebSocket API 是 HTML5 标准的一部分， 但这并不代表 WebSocket 一定要用在 HTML 中，或者只能在基于浏览器的应用程序中使用。\n实际上，许多语言、框架和服务器都提供了 WebSocket 支持，例如：\n基于 C 的 libwebsocket.org 基于 Node.js 的 Socket.io 基于 Python 的 ws4py 基于 C++ 的 WebSocket++ Apache 对 WebSocket 的支持： Apache Module mod_proxy_wstunnel Nginx 对 WebSockets 的支持： NGINX as a WebSockets Proxy 、 NGINX Announces Support for WebSocket Protocol 、WebSocket proxying lighttpd 对 WebSocket 的支持：mod_websocket ","date":"2014-11-30","description":"","lastmod":"2016-09-15T05:34:43Z","slug":"socket-and-websocket","tags":["socket","websocket","netconnection"],"title":"Socket 与 WebSocket","url":"https://blog.zengrong.net/post/socket-and-websocket/"},{"categories":["technology"],"content":"今天花了2个小时看了下 Docker 。感觉真是太好用了。\ndocker 的文档组织得非常好，想要了解的内容基本上在官方文档中都有讲到。我把这些内容用中文做一下简单梳理，方便大家在1个小时内快速了解 docker。\n1. 安装 1.1 Mac OS X 下面的内容主要来自于： Installing Docker on Mac OS X 。\n在 OS X 上，docker 提供了一个 PKG 安装包，下载安装即可。如果觉得 github 下载太慢，可以把链接粘贴到 迅雷 或者 旋风 中下载。当然如果有 VIP 就更快了。\n安装完毕后，在 terminal 中执行：\nboot2docker init boot2docker start\n此时会启动虚拟机，等十几秒之后，根据提示把几个环境变量加入到 ~/.profile 中。在我的系统中，需要加入这几个变量：\nexport DOCKER_HOST=tcp://192.168.59.103:2376 export DOCKER_CERT_PATH=$HOME/.boot2docker/certs/boot2docker-vm export DOCKER_TLS_VERIFY=1 然后运行 soruce ~/.profile 使其生效。\n如果不加入这些变量，那么执行 docker 命令的时候，可能会碰到类似这样的错误提示：\n-\u0026gt; % docker search openresty 2014/11/28 15:48:22 Get http:///var/run/docker.sock/v1.15/images/search?term=openresty: dial unix /var/run/docker.sock: no such file or directory 1.2 Windows 等我换到 windows 系统再来写。 2. 使用 2.1 查询镜像 \u0026gt; % docker search openresty NAME DESCRIPTION STARS OFFICIAL AUTOMATED 3scale/openresty Latest Openresty with redis and some handy... 4 [OK] coreos/openresty 2 jchaney/openresty-nginx OpenResty/Nginx purpose built for McMyAdmi... 1 [OK] 2.2 安装镜像 -\u0026gt; % docker pull 3scale/openresty Pulling repository 3scale/openresty 8d23714124be: Download complete 511136ea3c5a: Download complete f10ebce2c0e1: Download complete 82cdea7ab5b5: Download complete 5dbd9cb5a02f: Download complete 74fe38d11401: Download complete d7afc99e4495: Download complete 9be47dd6f833: Download complete c63b444ad530: Download complete ad547c33594c: Download complete abb2669cc06c: Download complete 161d4899b06f: Download complete a22e5d6a04d9: Download complete 4a2294651bfa: Download complete 5cc2a4516586: Download complete Status: Downloaded newer image for 3scale/openresty:latest 2.3 查看已安装镜像 -\u0026gt; % docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu latest 86ce37374f40 2 days ago 192.7 MB nginx latest e426f6ef897e 2 weeks ago 100.2 MB 3scale/openresty latest 8d23714124be 7 months ago 422.2 MB 2.4 运行镜像 2.5 删除容器 2.6 删除镜像 3. 快速学习 InfoQ 上有一个 深入浅入 Docker 系列：\n深入浅出Docker（一）：Docker核心技术预览 深入浅出Docker（二）：Docker命令行探秘 深入浅出Docker（三）：Docker开源之路 深入浅出Docker（四）：Docker的集成测试部署之道 深入浅出Docker（五）：基于Fig搭建开发环境 这本由国内 Docker 社区 DockerPool 编写的开源书则是最好的学习资料：\nDocker —— 从入门到实践\n","date":"2014-11-28","description":"","lastmod":"2014-11-28T07:27:08Z","slug":"usage-docker","tags":["linux","docker","virtualization"],"title":"【未完待续】开始使用Docker","url":"https://blog.zengrong.net/post/usage-docker/"},{"categories":["technology"],"content":"这是我对知乎上一个问题的回答，原题是：Vim 约等于记事本吗？\n最近看了linux的C编程书和vim用法 感觉vim和记事本似乎没多大区别 相对IDE缺少：代码自动补全，debug时查看所有变量值 等功能 平常编写一个自己的电脑可以运行的程序，加了适当插件的vim和VS应该差不多吧？\n下面是我的回答。\n题主真的看完了 Vim 手册么？\n====== 很2的分割线 ======\nVim 并不适合所有人用。如果你已经习惯了 VS 或者其它 IDE，并且觉得它们就是神级工具，那么不必尝试 Vim。\nVim 是给 Vimer 用的，是给熟悉或者认同 Unix哲学 的人用的。\n不要被网上那些 编辑器之神 和 神之编辑器 之争的文章刺激得热血沸腾就跑来学习 Vim。如果你工作在 Windows 上，很少接触类 Unix 操作系统，不了解 Unix哲学，或者对 Unix哲学 之说感到不屑，那么趁早放弃。\nVim 并不适合所有场合，很多时候，你还是依然需要 Xcode、Eclipse、IntelliJ IDEA 和 VS，没必要让 Vim 做所有的事情。\n虽然我知道 VS 也能当记事本，但我从来不敢这么用。\n你现在只能把 Vim 当记事本来用，但不代表 Vim 就是记事本。是的，约等于也不是。\n====== 分割线之2 ======\n（如有雷同，实属巧合）\n选择和放弃： 看手册； 手册看一半（或几章），开始用； 记快捷键，不用鼠标，坚持用； 理解 Vim 到底是给什么人用的，适合我么？ 一个月后，还是想用鼠标，放弃 Vim； 过了半年（或几个月），期间无数次思念 Vim； 重新启用 Vim； 再次放弃 Vim…… 循环3次以上…… 入门： 看完手册； 折腾各种配置； 寻找各种插件； 配置各种插件； 试图把 Vim 配置成 IDE； 经常会在任何可以输入文本的地方 JKILdd…… 入魔： 删除各种插件； 自己写插件； 改别人的插件； 开始把以前用 sed 和 awk 写的功能改成 vimscript； 试图在任何可以编辑文本的地方使用 Vim； 开始不习惯各种 IDE 和编辑器； 开始在各种 IDE 甚至浏览器中寻找并安装 Vim 插件。 回归正常： 删除各种 IDE 中的 Vim 插件； shell 配置成 vi 模式； 在其它 IDE 中不会再莫名其妙 ggG 或者 y$； 感觉 VS 和 Xcode 还是蛮好用的嘛； 继续使用 Vim…… ","date":"2014-11-28","description":"","lastmod":"2014-11-28T01:06:48Z","slug":"learning-vim","tags":["vim"],"title":"怎么理解Vim以及怎么学","url":"https://blog.zengrong.net/post/learning-vim/"},{"categories":["technology"],"content":"这篇文章是我在知乎上的一个回答：网页游戏是如何做到媲美客户端的网络游戏效果的？\n我不认为现在的页游的3D 效果能 “媲美” 端游，充其量只算是 “接近” 罢了。\n网页游戏运行在浏览器框架下。浏览器是操作系统中的一个应用程序。\n实际上，网页游戏作为一个应用，必须运行在浏览器的一个插件（或者一个引擎）之中。\n可能用于开发 3D 页游的几个技术是：\nAdobe Stage 3D Unity - Game Engine Microsoft Silverlight Chrome Native Client WebGL - OpenGL ES 2.0 for the Web 以及自己编写的各类基于浏览器的插件或者微端 其中，Stage 3D 、 Unity 和 Silverlight 技术对于页游来说，都是运行在浏览器插件中的，这比浏览器又低了一个级别。\nSliverlight 已死。\nChrome 的 native client 是运行在 Chrome 的沙盒中的，可能比前面几个技术要好些。\nWebGL 则是浏览器直接提供支持，优先级上应该和 Chrome native client 处于同一个级别。\n操作系统对浏览器这个应用软件有一定的限制，对于浏览器中的插件，操作系统不会给与什么资源上的特权。所以，浏览器中的插件的性能，不可能超过原生本地程序。\n大多数的插件是跨平台的。以我相对了解的 Stage 3D 来说，它在 Windows 上使用的是 DirectX API，在 MacOS 和 Linux 上使用的是 OpenGL API，在 Android 和 iOS 上则是使用 OpenGL ES。为了使用一套语言兼容多套标准，Adobe 做了 AGAL 语言，程序员可以针对 Stage3D 以及 AGAL 编程，不用直接面向多个平台上不同的底层3D API。一个通用的插件还需要支持较老的显卡。\n但这样做的问题就在于，管线支持变少了，功能也变少了。\nWebGL 貌似可以解决性能问题，但等 H5 稳定了再说吧。\n至于自己编写的浏览器插件或者微端的性能，就取决于程序员的能力了。\n和上面这些技术比起来，原生的客户端游戏不必考虑多平台兼容。优秀的游戏基本上都是单平台独占的，即使是多平台也经过了二次开发。这是因为这样更能全力发挥平台的性能，甚至可以直接为平台做硬件优化。很多游戏主机都是这么做的。对于通用平台来说（目前也只有Windows算是通用游戏平台吧），针对Windows平台开发，原生程序不会有什么限制，能使用最新的显卡驱动（必须的啊），最大限度地发挥显卡和其他硬件的性能，得到操作系统的最大的支持。因此，端游的效果无论如何都会比页游好太多了。\n当然，随着时间的流逝，这个 “接近” 会越来越接近，最终会转换为 “媲美” 吧。\n","date":"2014-11-27","description":"","lastmod":"2014-11-27T06:06:11Z","slug":"webgame-aaagame-technology","tags":["adobe","3d","game"],"title":"页游效果能否媲美端游","url":"https://blog.zengrong.net/post/webgame-aaagame-technology/"},{"categories":["technology"],"content":"在 从 quick 转向 cocos2d-x 3.3 lua 中，我提到会将转换过程中积累的经验进行分享。这是其中一篇。要查看更多，请点击：查看所有 quick 移植到 cocos2d-x lua 的文章 。\n2015-02-26更新： 修正解决方案。 cocos2d-x 3.3 的 lua 项目在运行时，会出现一些不符合人类习惯的问题。下面是我碰到的以及解决方案。\n1. SpriteFrameCache.addSpriteFrameWithFile 调用错误 问题描述： SpriteFrameCache 的 addSpriteFramesWithFile 方法是支持重载的。\n在 tolua 中，addSpriteFrameWithFile 也同样支持重载。从 lua_cocos2dx_auth.cpp 中的 lua_cocos2dx_SpriteFrameCache_addSpriteFramesWithFile 可以看出：\n:::C++ int lua_cocos2dx_SpriteFrameCache_addSpriteFramesWithFile(lua_State* tolua_S) { int argc = 0; cocos2d::SpriteFrameCache* cobj = nullptr; bool ok = true; #if COCOS2D_DEBUG \u0026gt;= 1 tolua_Error tolua_err; #endif #if COCOS2D_DEBUG \u0026gt;= 1 if (!tolua_isusertype(tolua_S,1,\u0026quot;cc.SpriteFrameCache\u0026quot;,0,\u0026amp;tolua_err)) goto tolua_lerror; #endif cobj = (cocos2d::SpriteFrameCache*)tolua_tousertype(tolua_S,1,0); #if COCOS2D_DEBUG \u0026gt;= 1 if (!cobj) { tolua_error(tolua_S,\u0026quot;invalid 'cobj' in function 'lua_cocos2dx_SpriteFrameCache_addSpriteFramesWithFile'\u0026quot;, nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; do{ if (argc == 2) { std::string arg0; ok \u0026amp;= luaval_to_std_string(tolua_S, 2,\u0026amp;arg0, \u0026quot;cc.SpriteFrameCache:addSpriteFramesWithFile\u0026quot;); if (!ok) { break; } std::string arg1; ok \u0026amp;= luaval_to_std_string(tolua_S, 3,\u0026amp;arg1, \u0026quot;cc.SpriteFrameCache:addSpriteFramesWithFile\u0026quot;); if (!ok) { break; } cobj-\u0026gt;addSpriteFramesWithFile(arg0, arg1); return 0; } }while(0); ok = true; do{ if (argc == 1) { std::string arg0; ok \u0026amp;= luaval_to_std_string(tolua_S, 2,\u0026amp;arg0, \u0026quot;cc.SpriteFrameCache:addSpriteFramesWithFile\u0026quot;); if (!ok) { break; } cobj-\u0026gt;addSpriteFramesWithFile(arg0); return 0; } }while(0); ok = true; do{ if (argc == 2) { std::string arg0; ok \u0026amp;= luaval_to_std_string(tolua_S, 2,\u0026amp;arg0, \u0026quot;cc.SpriteFrameCache:addSpriteFramesWithFile\u0026quot;); if (!ok) { break; } cocos2d::Texture2D* arg1; ok \u0026amp;= luaval_to_object\u0026lt;cocos2d::Texture2D\u0026gt;(tolua_S, 3, \u0026quot;cc.Texture2D\u0026quot;,\u0026amp;arg1); if (!ok) { break; } cobj-\u0026gt;addSpriteFramesWithFile(arg0, arg1); return 0; } }while(0); ok = true; CCLOG(\u0026quot;%s has wrong number of arguments: %d, was expecting %d \\n\u0026quot;, \u0026quot;cc.SpriteFrameCache:addSpriteFramesWithFile\u0026quot;,argc, 2); return 0; #if COCOS2D_DEBUG \u0026gt;= 1 tolua_lerror: tolua_error(tolua_S,\u0026quot;#ferror in function 'lua_cocos2dx_SpriteFrameCache_addSpriteFramesWithFile'.\u0026quot;,\u0026amp;tolua_err); #endif return 0; } 但是，在 lua 里面调用 addSpriteFramesWithFile 时，如果第二个参数传递 Texture2D，就会报错。信息如下：\ncocos2d: error: cc.SpriteFrameCache:addSpriteFramesWithFile argument #3 is 'cc.Texture2D'; 'string' expected.\n解决方案： 不解决。\n问题分析： 因为这是个误报。\n从上面 lua_cocos2dx_SpriteFrameCache_addSpriteFramesWithFile 的绑定代码可以看出，第二个参数是字符串的重载版本（版本1）是先调用的。当使用第二个参数是 Texture2D 的重载版本时，版本1也会被调用，但不会执行。\n不过参数判断总是会执行，因此就出现了误报。\n误报的输出发生在 LuaBasicConversions.cpp 的 luaval_to_native_err 函数中：\n:::C++ void luaval_to_native_err(lua_State* L,const char* msg,tolua_Error* err, const char* funcName) { if (NULL == L || NULL == err || NULL == msg || 0 == strlen(msg)) return; if (msg[0] == '#') { const char* expected = err-\u0026gt;type; const char* provided = tolua_typename(L,err-\u0026gt;index); if (msg[1]=='f') { int narg = err-\u0026gt;index; if (err-\u0026gt;array) CCLOG(\u0026quot;%s\\n %s argument #%d is array of '%s'; array of '%s' expected.\\n\u0026quot;,msg+2,funcName,narg,provided,expected); else CCLOG(\u0026quot;%s\\n %s argument #%d is '%s'; '%s' expected.\\n\u0026quot;,msg+2,funcName,narg,provided,expected); } else if (msg[1]=='v') { if (err-\u0026gt;array) CCLOG(\u0026quot;%s\\n %s value is array of '%s'; array of '%s' expected.\\n\u0026quot;,funcName,msg+2,provided,expected); else CCLOG(\u0026quot;%s\\n %s value is '%s'; '%s' expected.\\n\u0026quot;,msg+2,funcName,provided,expected); } } } 终极解决方案就是取消这个方法的自动导出，改为手动导出。但本着尽量少修改 cocos2d-x 源码的初衷，知道就好，不必改动。\n2. 不能载入 luac 文件 问题描述： 在载入 main.lua 文件的时候，明明已经载入成功，但却一直会收到下面的提示：\ncan not get file data of main.luac\n如果有多个 lua 文件，那么载入每个 lua 文件都会收到这个提示，只是扩展名变成了 luac 。\n解决方案： 找到 Cocos2dxLuaLoader.cpp 文件，找到其中的 cocos2dx_lua_loader 方法，将下面这段删除或注释：\n:::C++ chunkName = prefix.substr(0, pos) + filename + BYTECODE_FILE_EXT; if (utils-\u0026gt;isFileExist(chunkName)) { chunk = utils-\u0026gt;getFileData(chunkName.c_str(), \u0026quot;rb\u0026quot;, \u0026amp;chunkSize); break; } 注意，上面的代码是一段 if ... else 结构的一部分，因此删除或注释后，还需要做一些其它的修改才能通过编译。\n2015-02-26 修正解决方案：\n上面的解决方案会导致进行正式打包（使用 luac 格式）的时候无法显示界面。因此，正确的修改应该是在 main.lua 中加入这一行：\n:::lua cc.FileUtils:getInstance():setPopupNotify(false) 问题分析： cocos2d-x 的 lua 载入代码会优先载入 luac 文件，然后再处理 lua 文件。取消载入 luac 文件即可解决这个问题。\n3. print 内容净化 问题描述： 使用 lua 的 print 方法打印出来的内容，在控制台中显示如下：\ncocos2d: [LUA-print] -------- require(root.pretreatent.init)\n这个 cocos2d: [LUA-print] 看起来相当讨厌，要干掉它。\n解决方案： 在 CCLuaStack.cpp 中找到 lua_print 方法，将其中的：\n:::C++ CCLOG(\u0026quot;[LUA-print] %s\u0026quot;, t.c_str()); 改为：\n:::C++ CCLOG(\u0026quot;%s\u0026quot;, t.c_str()); 在 CCConsole.cpp 中找到 _log 方法，将其中的：\n:::C++ fprintf(stdout, \u0026quot;cocos2d: %s\u0026quot;, buf); 改为：\n:::C++ fprintf(stdout, \u0026quot;%s\u0026quot;, buf); 4. print 中加入时间戳显示 问题描述： 以前使用 quick 的时候，每一句 print 之前，都有一个当前 print 时刻的时间戳显示。单位是从程序执行开始的秒数。\ncocos2d-x 3.3 lua 版本中没有这个时间显示。\n解决方案： 在 CCLuaStack.cpp 中，在全局命名空间中（namespace { 之下）加入下面的内容，定义一个全局变量和一个全局函数：\n:::C++ static timeval lua_print_lasttime = {0}; std::string lua_print_gettime() { timeval now; float deltatime = 0; if(gettimeofday(\u0026amp;now, nullptr) != 0) { CCLOG(\u0026quot;lua_print_gettime() - error in gettimeofday\u0026quot;); } else { if (lua_print_lasttime.tv_sec) { deltatime = now.tv_sec - lua_print_lasttime.tv_sec + (now.tv_usec - lua_print_lasttime.tv_usec) / 1000000.0f; } else { lua_print_lasttime = now; deltatime = 0; } } std::string t(\u0026quot;[\u0026quot;); char timestr[32]; memset(timestr, 0, sizeof(timestr)); sprintf(timestr, \u0026quot;%.4f\u0026quot;, deltatime); t += timestr; t += \u0026quot;] \u0026quot;; return t; } 在 LuaStack::init 方法的第一行加入 gettimeofday(\u0026amp;lua_print_lasttime, nullptr); ，修改完毕的函数如下所示：\n:::C++ bool LuaStack::init(void) { gettimeofday(\u0026amp;lua_print_lasttime, nullptr); //.........原来的内容 _state = lua_open(); luaL_openlibs(_state); toluafix_open(_state); //.........原来的内容 } 修改 lua_print 方法，将其中的：\n:::C++ std::string t; 改为：\n:::C++ std::string t(lua_print_gettime()); ","date":"2014-11-26","description":"","lastmod":"2014-11-26T06:53:39Z","slug":"fix-cocos2d-x-33-lua","tags":["cocos2d-x","cpp","lua","quick2cocoslua"],"title":"cocos2d-x 3.3 lua 相关修改","url":"https://blog.zengrong.net/post/fix-cocos2d-x-33-lua/"},{"categories":["technology"],"content":"2015-05-04 更新： 加入 OpenGL 和 MenuItem 的相关变化。\n本文的内容来自于对其它几篇文章的翻译、修改和合成，同时，我也会不断增加自己的内容。\n下面这部分内容来自对这篇文章的翻译：cocos2d-x v2 to v3 mapping guide\n但这篇文章有一些老了，还有一些内容已经在 cocos2d-x 3.3 中过时。因此，我并没有进行完全对照翻译。对原文中的错误，我也进行了一些修改。\n我的新项目开始使用 cocos2d-x v3 。cocos2d-x v3 和 v2 相比有非常大的改变。我把踩过的坑列在下面，以方便后来之人。\ncocos2d-x 常用类名改变 下面的表格中的类名的转换方式主要是直接删除了 CC 前缀。\n# v2 v3 1 CCAction Action 2 CCPoint Point 3 CCAnimation Animation 4 CCSprite Sprite 5 CCLabel Label 6 CCMenu Menu 7 CCObject Ref 8 CCNode Node 9 CCScene Scene 10 CCLayer Layer 11 CCSpriteBatchNoe SpriteBatchNode 12 CCTMXTiledMap TMXTiledMap cocos2d-x 类名改变 下面表格中的类名的转换就比较大了。\n# v2 v3 1 CCDictionary ValueMap 2 CCArray ValueVector 3 CCString Value CCString 用法改变 之前：\n:::C++ CCString* str = CCString::createWithFormat(\u0026quot;%s.png\u0026quot;,\u0026quot;picture\u0026quot;); 现在：\n:::C++ std::string str = StringUtils::format(\u0026quot;%s.png\u0026quot;,\u0026quot;picture\u0026quot;); CCDictinoary 用法改变 之前：\n:::C++ CCDictionary* dict = CCDictionary::createWithContentsOfFile(\u0026quot;name.plist\u0026quot;); CCArray* arr = (CCArray*) data-\u0026gt;objectForKey(\u0026quot;Levels\u0026quot;); 现在：\n:::C++ std::string path = FileUtils::getInstance()-\u0026gt;fullPathForFilename(\u0026quot;name.plist\u0026quot;); ValueMap dict = FileUtils::getInstance()-\u0026gt;getValueMapFromFile(path); ValueVector arrLevels = data.at(\u0026quot;Levels\u0026quot;).asValueVector(); CCArray 用法改变 这里就是 C++ vector 容器的标准用法了。\n# v2 v3 1 CCArray* sprites; Vector\u0026lt;Sprint*\u0026gt; sprites; 2 sprites-\u0026gt;addObject(sprite); sprites.pushBack(sprite); 3 sprites-\u0026gt;removeObject(sprite); sprites.eraseObject(sprite); 4 sprites-\u0026gt;removeObjectAtIndex(i); sprites.erase(i); 5 sprites-\u0026gt;objectAtIndex(i); sprites.at(i); 6 sprites-\u0026gt;count(); sprites.size(); 下面这部分内容来自 这里 。\n触摸用法改变 # v2 v3 1 ccTouchBegan onTouchBegan 2 ccTouchMoved onTouchMoved 3 ccTouchEnded onTouchEnded 单例类用法改变 # v2 v3 1 CCEGLView::sharedOpenGLView(); Director::getInstance()-\u0026gt;getOpenGLView(); 2 CCTextureCache::sharedTextureCache(); Director::getInstance()-\u0026gt;getTextureCache(); 3 CCNotificationCenter::sharedNotificationCenter(); Director::getInstance()-\u0026gt;getEventDispatcher(); CCTime 用法改变 CCTime cocos2d-x v3 中已经被删除了。\n# v2 v3 1 cc_timeval timeval 2 CCTime::gettimeofdayCocos2d gettimeofday 3 CCTime::timesubCocos2d getTimeDiffenceMS 范例：\n:::C++ static inline float getTimeDifferenceMS(timeval\u0026amp; start, timeval\u0026amp; end) { return ((((end.tv_sec - start.tv_sec)*1000.0f + end.tv_usec) - start.tv_usec) / 1000.0f); } 下面的内容为 zrong 原创。\nOpenGL 的用法变化 # v2 v3 1 CCGLProgram GLProgram 3 kCCUniformPMatrix_s GLProgram::UNIFORM_NAME_P_MATRIX 4 kCCUniformMVMatrix_s GLProgram::UNIFORM_NAME_MV_MATRIX 5 kCCUniformMVPMatrix_s GLProgram::UNIFORM_NAME_MVP_MATRIX 6 kCCUniformTime_s GLProgram::UNIFORM_NAME_TIME 7 kCCUniformSinTime_s GLProgram::UNIFORM_NAME_SIN_TIME 8 kCCUniformCosTime_s GLProgram::UNIFORM_NAME_COS_TIME 9 kCCUniformRandom01_s GLProgram::UNIFORM_NAME_RANDOM01 10 kCCUniformSampler_s GLProgram::UNIFORM_NAME_SAMPLER0 11 kCCUniformAlphaTestValue GLProgram::UNIFORM_NAME_ALPHA_TEST_VALUE 12 kCCAttributeNameColor GLProgram::ATTRIBUTE_NAME_COLOR 13 kCCAttributeNamePosition GLProgram::ATTRIBUTE_NAME_POSITION 14 kCCAttributeNameTexCoord GLProgram::ATTRIBUTE_NAME_TEX_COORD MenuItem 的用法变化 以前我写过一篇 Cocos2d-x中的事件调用方式汇总 ，其中介绍了 cocos2d-x 中的回调函数。而在 v3 版本中，这些回调函数已经完全废弃了。\n在 cocos2d-x v3 中，使用的是 C++11 提供的标准的 std::bind 功能来实现回调。\n让我们看看 base/ccMacros.h 中的几个宏：\n1// new callbacks based on C++11 2#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(\u0026amp;__selector__,__target__, ##__VA_ARGS__) 3#define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(\u0026amp;__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__) 4#define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(\u0026amp;__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__) 5#define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(\u0026amp;__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__) 所以，对于 cocos2d-x v2 中这样的调用：\n1CMenuItemImage *item1 = CCMenuItemImage::create(s_pPathB1, s_pPathB2, this, menu_selector(ActionsDemo::backCallback)); 在 cocos2d-x v3 中应该是这样的：\n1MenuItemImage *item1 = MenuItemImage::create(s_pPathB1, s_pPathB2, CC_CALLBACK_1(ActionsDemo::backCallback, this)); ","date":"2014-11-26","description":"","lastmod":"2015-01-26T08:04:54Z","slug":"cocos2d-x-v2-to-v3-mapping-guide","tags":["cocos2d-x","cpp"],"title":"cocos2d-x v2 和 v3 对照手册","url":"https://blog.zengrong.net/post/cocos2d-x-v2-to-v3-mapping-guide/"},{"categories":["technology"],"content":"本文基于 python 3.4 ，在 python 2.7 上也可以使用。\n在昨天的文章 在 setuptools 中使用 dependency_links 里，我提到了发布 hhlb 工具集的工作。今天又遇到了一些具体的问题。\nhhlb 的文件夹结构如下：\n. ├── MANIFEST.in ├── README.rst ├── hhlb │ ├── __init__.py │ ├── __main__.py │ ├── admin.py │ ├── base.py │ ├── bin │ ├── build.conf │ ├── config.py │ ├── init.py │ ├── res.py │ ├── templ.py │ └── update.py ├── requirements.txt ├── setup.py └── test └── testbuild.py hhlb 是一个 python package。最初的做法，我在 hhlb 的同级目录中放了一个 build.py 文件，在这个文件中 import hhlb 这个 package ，使用其功能。\n例如，下面的代码会强制初始化整个项目：\npython build.py init -af build.py 这个引导文件其实 没有存在的必要 。既然我要将 hhlb 发布成一个工具，我就应该让它能像这样被调用：\nhhlb init -af python -m hhlb init -af 第一种调用方法，就像 pip 一样。第二种方法，就像 http.server 一样。\n另外，在开发过程中，还可以直接通过指定 hhlb 文件夹路径的方式来调用：\npython /path/to/hhlb init -af 1. 调用顺序 为了实现上面的功能，我们先需要了解一下 python 对于 package 的调用顺序。\n如果你希望 python 将一个文件夹作为 package 对待，那么这个文件夹中必须包含一个名为 __init__.py 的文件，即使它是空的。 参见： Packages\n如果你需要 python 讲一个文件夹作为 package 执行，那么这个文件夹中必须包含一个名为 __main__.py 的文件，当执行 python -m hhlb 或者 python hhlb 的时候，这个文件中的代码都会被执行。参见： main — Top-level script environment\n让我们做个试验。\n在 hhlb/__init__.py 中写入如下内容：\n1print(\u0026#39;__init__\u0026#39;) 2print(\u0026#39;__init__.__name__\u0026#39;, __name__) 3print(\u0026#39;__init__.__package__\u0026#39;, __package__) 在 hhlb/__main__.py 中写入如下内容：\n1print(\u0026#39;__main__\u0026#39;) 2print(\u0026#39;__main__.__name__\u0026#39;, __name__) 3print(\u0026#39;__main__.__package__\u0026#39;, __package__) 执行 python hhlb ，打印如下：\n-\u0026gt; % python hhlb __main__ __main__.__name__ __main__ __main__.__package__ 这说明，将 hhlb 当作文件夹执行的时候，对于 __main__.py 来说，变量 __package__ 是一个空字符串。而 __init__.py 不会被执行。\n下面看看执行 python -m hhlb 的效果：\n-\u0026gt; % python -m hhlb __init__ __init__.__name__ hhlb __init__.__package__ hhlb __main__ __main__.__name__ __main__ __main__.__package__ hhlb 当作为模块执行的时候，python 会先执行 __init__.py ，然后执行 __main__.py 。而且，前者和后者对于 __name__ 变量的理解是不同的。\n2. 定义一个 main() 对于一个 package 来说，既然 __init__.py 必须存在，而且当作为模块执行的时候，它会先执行，我们就应该把入口函数 main() 定义在 __init__.py 中。\n当我们使用模块方式 -m 调用包的时候，__init__.py 定义了 main() 函数，然后在 __main__.py 中调用它，就能实现我们统一入口的目的。\n那么继续试验。\n将 hhlb/__init__.py 修改成这样：\n1print(\u0026#39;__init__\u0026#39;) 2print(\u0026#39;__init__.__name__\u0026#39;, __name__) 3print(\u0026#39;__init__.__package__\u0026#39;, __package__) 4 5def main(): 6 print(\u0026#39;__init__.main()\u0026#39;) 在 hhlb/__main__.py 中对 main() 进行调用：\n1print(\u0026#39;__main__\u0026#39;) 2print(\u0026#39;__main__.__name__\u0026#39;, __name__) 3print(\u0026#39;__main__.__package__\u0026#39;, __package__) 4 5import hhlb 6hhlb.main() 注意，在 __main__.py 中，没有必要判断 __name__ 是否为 __main__ ，因为无论如何调用， __name__ 的值都一定是 __main__ 。 :)\n执行 python -m hhlb ，调用正常：\n-\u0026gt; % python -m hhlb __init__ __init__.__name__ hhlb __init__.__package__ hhlb __main__ __main__.__name__ __main__ __main__.__package__ hhlb __init__.main() 执行 python hhlb ，调用失败：\n-\u0026gt; % python hhlb __main__ __main__.__name__ __main__ __main__.__package__ Traceback (most recent call last): File \u0026quot;/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/runpy.py\u0026quot;, line 171, in _run_module_as_main \u0026quot;__main__\u0026quot;, mod_spec) File \u0026quot;/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/runpy.py\u0026quot;, line 86, in _run_code exec(code, run_globals) File \u0026quot;hhlb/__main__.py\u0026quot;, line 7, in \u0026lt;module\u0026gt; import hhlb ImportError: No module named 'hhlb' 发生了什么事？\n3. sys.path 从上面的试验看出，不同的调用顺序，得到的结果完全不同。\n究其原因，还是调用顺序的问题。把下面这段代码分别加入 __init__.py 和 __main__.py 即可了解原因：\n1import sys 2print(\u0026#39;sys.path\u0026#39;, sys.path) 在使用 python hhlb 调用的时候， sys.path 的内容如下（省略了报错信息）：\n-\u0026gt; % python hhlb __main__ __main__.__name__ __main__ __main__.__package__ __main__.sys.path ['hhlb', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Users/zrong/pythonenv/hhl/lib/python3.4/site-packages'] 在使用 python -m hhlb 调用的时候， sys.path 的内容如下：\n-\u0026gt; % python -m hhlb __init__ __init__.__name__ hhlb __init__.__package__ hhlb __init__.sys.path ['', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Users/zrong/pythonenv/hhl/lib/python3.4/site-packages'] __main__ __main__.__name__ __main__ __main__.__package__ hhlb __main__.sys.path ['', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Users/zrong/pythonenv/hhl/lib/python3.4/site-packages'] __init__.main() 可以看出，sys.path 的第一个搜索路径，一个是 hhlb ，一个是空字符串。\n对于 python -m hhlb 的调用方式来说，由于 __init__.py 被事先载入，此时 python 解释器已经知道了自己就是一个 package ，因此当前路径被包含在 sys.path 中。然后再调用 __main__.py ，这时 import hhlb 这个包就毫无压力了。\n而对于 python hhlb 的调用方式来说，由于 __init__.py 没有被载入，python 解释器并不知道自己正在一个 package 下面工作。默认的，python 解释器将 __main__.py 的当前路径 hhlb 加入 sys.path 中，然后在这个路径下面寻找 hhlb 这个模块。显然， hhlb 文件夹下面并没有 hhlb 这个模块，因此出错。\n要理解这点，就要明白 __init__.py 是 python 解释器将当前文件夹作为 package 处理的必要条件。如果没有读取到 __init__.py ，python 就不会认为当前的文件夹是一个 package，而只是把它当作普通文件夹来处理。\n4. 问题解决 既然知道了问题原因，解决的方法也就立刻浮现了。\n我们只需要把当前路径加入到 sys.path 中，就能解决这个问题。\n最终的 __main__.py 是这个样子的（删除了 print）：\n1import sys 2import os 3 4if not __package__: 5 path = os.path.join(os.path.dirname(__file__), os.pardir) 6 sys.path.insert(0, path) 7 8import hhlb 9hhlb.main() QA\n为什么不像上面提到的那样，直接在 sys.path 前面加上一个空字符串来解决问题呢？\n因为，如果不是在当前路径下调用，空字符串就没效果了： python /path/to/hhlb 。 为什么不写 if __package__ == '' 来判断 __package__ 的值呢？这样应该更准确。 这并不是偷懒。因为你可能会变态到使用这种方法调用： python hhlb/__main__.py 。而这种情况下， __package__ 的值就是 None 而不是 '' 了。 5. 参考资料 Python: modules and packaging - why isn't __init__.py file executed before __main__.py? __init__.py in pip ","date":"2014-11-21","description":"","lastmod":"2014-11-21T10:08:57Z","slug":"__main__py-and-__init__py","tags":["python"],"title":"__main__.py 和 __init__.py","url":"https://blog.zengrong.net/post/__main__py-and-__init__py/"},{"categories":["technology"],"content":"1. 关于 build 工具集 我开发的项目，都会提供一个 build 工具集，这个工具集为开发人员提供了所有可用的功能，包括：\n初始化项目； 更新版本库； 资源转换、加密、压缩、包装； 模版生成； 打包、发布； 更多开发人员需要的功能…… 以前写 Actionscript 的时候，我使用 Bash + Ant + Java 开发工具集，现在转向使用 Python。\n正在开发的这个 build 工具集，是 hhlb ，其中，HHL 是我们的项目代号，b 代表 build。\n2. 架构调整 这个工具集一直是以脚本的形式调用的。但我正在把它改为使用独立命令行的形式调用。\n例如，这是脚本的调用方式：\npython3 /path/to/hhlb.py init -af 而这是命令行的调用方式：\nhhlb init -af 这样一来，更新和管理都变得容易，其他的程序员也不必再维护一个和他们毫无关系的工具集的源码仓库，他们只需要安装或者更新 hhlb 这个工具就行了，就像这样（hhlb 工具的安装包在内网服务器中）：\npip install http://192.168.18.18/project/hhl/tool/hhlb-0.1.0.tar.gz 每次更新 hhlb 工具，我只需要提供一个新的 gz 包，然后通知大家更新：\npip install http://192.168.18.18/project/hhl/tool/hhlb-0.1.3.tar.gz 为了降低 url 拼写错误的可能，我在项目源码仓库中建立了一个 requirements.txt 文件，里面的内容如下：\nhttp://192.168.18.18/project/hhl/tool/hhlb-0.1.0.tar.gz 每次更新库，我只需要修改文件内容指向新的下载链接，大家更新源码库，然后这样操作就可以了：\npip install -r requirements.txt 大家都赞同这样的架构，然后我就开始实施。\n3. 安装问题 hhlb 依赖我写的一个名为 zrong 的 python 库。这个库并没有发布到 PyPI 上，因此，我采用 dependency_links 参数进行部署。\n完整的 setup.py 的内容是这样的：\n#!/usr/bin/env python from setuptools import setup requires = ['zrong\u0026lt;=0.2.1'] dependency_links = [ 'http://192.168.18.18/project/hhl/tool/zrong-0.2.1.tar.gz' ] entry_points = { 'console_scripts': [ 'hhlb = hhlb:main', ] } setup( name=\u0026quot;hhlb\u0026quot;, version=\u0026quot;0.1.0\u0026quot;, url='http://zengrong.net/', author='zrong', author_email='zrongzrong@gmail.com', description=\u0026quot;A tool to build HHL project.\u0026quot;, packages=['hhlb'], include_package_data=True, install_requires=requires, entry_points=entry_points, dependency_links = dependency_links, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3.4', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Software Development :: Libraries :: Python Modules', ], test_suite='hhlb.test', ) 使用 python setup.py sdist 之后，得到一个 hhlb-0.1.0.tar.gz 文件，我可以使用 pip install hhlb-0.1.0.tar.gz 进行安装。\n就在此时，pip 报错了。\n-\u0026gt; % pip install ~/hhl/build/dist/hhlb-0.1.0.tar.gz Unpacking /Volumes/HD1/works/hhl/build/dist/hhlb-0.1.0.tar.gz Running setup.py (path:/var/folders/3g/w_67mm9d7bs3kgzkfwj516k80000gq/T/pip-i234hels-build/setup.py) egg_info for package from file:///Volumes/HD1/works/hhl/build/dist/hhlb-0.1.0.tar.gz Downloading/unpacking zrong\u0026lt;=0.2.1 (from hhlb==0.1.0) Could not find any downloads that satisfy the requirement zrong\u0026lt;=0.2.1 (from hhlb==0.1.0) Cleaning up... No distributions at all found for zrong\u0026lt;=0.2.1 (from hhlb==0.1.0) Storing debug log for failure in /Users/zrong/.pip/pip.log 这个错误说明 pip 其实并没有去处理 dependency_links 指示的内容。\n4. 解决问题 在 stackoverflow 上翻来翻去，从这两个问题中找到了答案：\nSetuptools unable to use link from dependency_links How can I make setuptools install a package that's not on PyPI? 要想简单地解决这个问题，可以使用 --process-dependency-links 参数通知 pip 强制处理 depencency_links 参数。就像下面这样：\n-\u0026gt; % pip install --process-dependency-links ~/hhl/build/dist/hhlb-0.1.0.tar.gz Unpacking /Volumes/HD1/works/hhl/build/dist/hhlb-0.1.0.tar.gz Running setup.py (path:/var/folders/3g/w_67mm9d7bs3kgzkfwj516k80000gq/T/pip-juu7d582-build/setup.py) egg_info for package from file:///Volumes/HD1/works/hhl/build/dist/hhlb-0.1.0.tar.gz Dependency Links processing has been deprecated with an accelerated time schedule and will be removed in pip 1.6 Downloading/unpacking zrong\u0026lt;=0.2.1 (from hhlb==0.1.0) http://192.168.18.18/project/hhl/tool/zrong-0.2.1.tar.gz#egg=zrong-0.2.1 uses an insecure transport scheme (http). Consider using https if 192.168.18.18 has it available Downloading zrong-0.2.1.tar.gz Running setup.py (path:/Users/zrong/pythonenv/testhhlb/build/zrong/setup.py) egg_info for package zrong Installing collected packages: zrong, hhlb Running setup.py install for zrong Running setup.py install for hhlb Installing hhlb script to /Users/zrong/pythonenv/testhhlb/bin Successfully installed zrong hhlb Cleaning up... 但这样的体验并不好。\n首先，pip 无论如何都会去 PyPI 搜索 zrong 这个包，这会花去不少时间，还不能停止。\n其次，这个参数将在 pip 1.6 版本被移除。因为这是 a bad practice 。\n因此，正确的做法应该是这样的：\n将我 前面提到 的 requirements.txt 的内容进行修改。就像下面这样就可以啦！\nhttp://192.168.18.18/project/hhl/tool/zrong-0.2.1.tar.gz http://192.168.18.18/project/hhl/tool/hhlb-0.1.0.tar.gz ","date":"2014-11-20","description":"","lastmod":"2014-11-20T10:26:53Z","slug":"using-dependenty_links-in-setuptools","tags":["python"],"title":"在 setuptools 中使用 dependency_links","url":"https://blog.zengrong.net/post/using-dependenty_links-in-setuptools/"},{"categories":["technology"],"content":" 2014-11-30 更新：\n今天在 quick 的博客上看到了 duceface 的新文章：cocos2d-lua 与 quick 的未来，不由得热(kua)泪(zhang)盈(shou)眶(fa)，看来 dualface 一直在努力，终于有成果了。 2015-01-15 更新：\n加入 LiteFeel 的 如何在lua项目中使用dragonbones 文章链接。 2015-01-16 更新：\n今天下载了 cocos2d-x 3.4 RC1 ，发现其中已经集成了一个 simulator，包含了原来 quick-player 的部分功能。根据阳光七月的说法，quick 到了3.4之后将不再单独发展。这真是个好消息。 在我的第一个 cocos2d-x 项目立项时，我曾经纠结过是使用 Lua 绑定还是使用 JS 绑定。最后选择了使用 Lua ： Cocos2d-x script language binding:Lua or Javascript? 。cocos2d-x 的 Lua 绑定做得很糟糕，所以，我基于 quick-cocos2d-x 2.2.3 这个更好的 Lua 绑定版本进行了许多修改，那时 quick 还是在独立发展。\n后来，Quick 被触控收购，Quick 进行了不兼容的升级。由于我对 Quick 的修改过多，当时的项目也在非常重要的时期，我没有持续跟进 Quick 的升级步伐，一直在使用自己 forked 的版本。\n在这段艰苦时间中，我在 Quick 社区和 Quick 团队身上也学到了不少东西。感谢 dualface、Jimmy、阳光七月、ChildhoodAndy、微微蓝 等朋友提供的无私帮助。\n在这些朋友的帮助下，我对 openGL ES 做了初步的了解，写出了 cocos2d-x 的 filter 组件；熟悉了 cocos2d-x 引擎的源码架构；把 DragonBonesCPP 整合进入了 quick 框架中；了解了 lua 与 C++ 交互的方式……\n下面有一个不全的列表：\n加入 SocketTCP 对 luasocket 进行了封装； 模仿AS3用lua实现了 ByteArray； 加入 DragonBonesCPP 组件，使用 CCDragonBones.lua 进行了封装； 加入 cocos2d-x-filter 组件，使用 filters.lua 进行了封装； 加入了 CCDrawNodeExtend.lua 对 CCDrawNode 的常用方法进行封装； 对 display.lua 、CCSpriteExtend.lua 等等功能进行扩展，增加 获取纹理像素 、printscreen 等常用方法； 让 CCSpriteFrameCache 和 CCAnimationCache 支持异步加载； 在 network 中增加上传文件功能； 在 cocos2d-x 中嵌入浏览器 ； …… 现在，新项目立项，我面临又一个选择。\n根据目前 cocos2d-x 的发展情况，无论从代码质量，还是引擎性能和维护成本来说，使用 3.x 版本是必须的，2.x 应该放弃。从触控现在表现出来的状态看，业务的重点是在 JS 版以及周边工具集。Lua 完全没有机会。Quick 的发展受到 cocos2d-x 团队的极大限制，速度极其缓慢，架构多次调整，Quick3 也迟迟没有发布。\n我依然不看好 cocos2d-x JS 版。从目前触控的混乱情况来看，如果选择使用 H5 ，我肯定不会使用 cocos2d-x。\nLua 一定是移动设备上最快的，这点毋庸置疑。但从目前 Quick 的发展看来，我似乎不能继续使用它了。我不愿再走 quick 2.2.3 到 2.2.5 那段不兼容的老路。在 Quick 的发展方向没有确定的前提下，为了保持向下兼容，我现在的选择是： 直接使用 cocos2d-x 3.3 Lua 版本。\n这个选择相当痛苦。cocos2d-x 3.3 的 Lua 支持非常糟糕，和 Quick 完全无法相比。一堆错误的提示和 warning，而且没有 Quick-player 的支持。从 dualface 那里得到的消息，Quick 已经把大部分的 C++ 修改都整合到 cocos2d-x 中了，但这还不够。\n我花了许多时间让 Quick 的 lua 框架能在我们的 cocos2d-x 3.3 lua 项目中跑起来，接着我把 DragonBonesCPP 和 filter 都移植到了 cocos2d-x 3.3 中。目前，我们的团队已经可以使用 cocos2d-x 3.3 进行游戏的正常开发了。\n我将这段时间积累的移植经验写成了一系列文章，方便以后查阅。下面是个列表（不断更新）：\ncocos2d-x 3.3 lua 相关修改\n使用 cocos2d-x 3.3 之后，要正常使用 lua 绑定，需要做一些修改，这篇文章记录了一些常用的修改。 如何在lua项目中使用dragonbones\n在 quick-cocos2d-x 中，我将 dragonbones 集成进入了 quick-player 。但 cocos2d-x 中是没有 dragonbones 的，需要自行移植。LiteFeel 写了一篇在标准 cocos2d-x 中使用 dragonbones 的文章，参照他的步骤就可以了。 查看所有 quick 移植到 cocos2d-x lua 的文章 ","date":"2014-11-19","description":"","lastmod":"2015-01-16T03:25:52Z","slug":"quick-to-cocos2d-x-33","tags":["cocos2d-x","cpp","lua","quick2cocoslua","fromto"],"title":"从 quick 转向 cocos2d-x 3.3 lua","url":"https://blog.zengrong.net/post/quick-to-cocos2d-x-33/"},{"categories":["web"],"content":"很早以前就想把博客转换为静态站点。现在，我终于下决心来做这件事了。\n为了这件值得纪念的事，我必须要写一点东西。 不得不说的历史 如果从 2005年4月 开始算起， zrong's blog 已经存在了将近10年的时间。但事实上，我在2003年就开始写博客了。最早用的博客系统是基于 ASP 的 oblog 系统。由于当时不太注重网络安全（其实是菜鸟啥都不懂），我的第一个博客被黑掉，然后我转投了 Wordpress ，没想到这一写就是10年。\n2011年6月11日，我开始使用 Markdown 写博客，文章会直接提交到 Github 上。然后再手动粘贴到 Wordpress 后台，使用 Markdown On Save 插件来渲染博客内容。\n之前的文章使用的是 HTML 格式，我使用 Pandoc 将它们全部转换成了 Markdown 格式。\n到现在，博客上已经有了700多篇文章，大部分是原创，也有一部分是翻译和转载的。最近几年，基本没有转载过文章了。\n目前，我的博客跑在 阿里云主机 上，用 lighttpd 做服务器，并使用了 WP Super Cache 扩展。\n静态化的原因 说起来就简单了，就是下面几条。\n不满 Wordpress 的臃肿； 一直不太喜欢 PHP； 希望访问起来更快； 希望维护起来更直接； 希望能使用版本库管理。 生成器选择 选择了 Python 平台后，生成器选择就是要纠结的问题了。\n这里有一个列表可供参考：Static Site Generators 。\n最后在 Choosing a new python based blog engine 的影响下，我暂时选择了 pelican 作为我的生成器工具。\n后续工作 转换到 pelican 有许多问题要解决，包括但不限于：\ntag 和 category 的映射。这个我已经从 wp 中把它们提出成为列表了，但 pelican 不支持一篇博客有多个 category 。而我的文章中有不少是这样的。目前我批量把这些文章找了出来，然后人肉一个个判断应该属于哪个 category。 Wordpress 中的附件和文章需要进行一一对应。我需要将把所有的附件地址都放在一个文件中，这样方便把附件放到 CDN 中，使其可以不占主站流量。毕竟我的主机带宽只有 1M。这需要写个插件处理。 写了10年 Wordpress ，我不可避免地使用了一些插件，例如 wpdownload、jwplayer、kmlflashembed 等等，这些插件特有的标签，都要进行转换。所以也要写几个 pelican 插件来处理。 comment 功能，我打算用搜狐的畅言，导入不是问题，但是使用第三方插件系统就要考虑评论回调的问题，这需要写个评论插件，仅供回调用。 还需要一个 pageview 插件…… No Zuo No Die 前两天手贱，把 Markdown On Save 换成了 Markdown On Save Improved ，这一换，就导致了双引号转义出现问题。目前，博客中大部分文章中的超链接都由于这次手贱变得不能访问。\n恢复数据库是不现实的，我准备化悲痛为力量，努力加快转换的工作。\nWPCMD 上面提到的转换工作不是一天能完成的，尤其是对于我这种 python 菜鸟来说，许多东西都要一边学习一边开发。\n我基于 python-wordpress-xmlrpc 写了一个工具用来进行这段时间的博客撰写工作。有了这个工具，我不必再苦逼地把 Markdown 源码粘贴到 Wordpress 后台了。同时，在这个工具的维护过程中，我也可以为 Pelican 的插件开发积累经验。\n这个工具暂时命名为 wpcmd ，它的作用是将 Markdown 格式的文章转换成为 HTML 格式，然后通过 Wordpress 提供的 XML-RPC 接口发布到 Wordpress 博客。大部分博客的常用操作都可以使用这个工具在命令行上完成，我终于不必再面对 Wordpress 的后台了。\n","date":"2014-11-18","description":"","lastmod":"2015-10-20T14:17:21Z","slug":"blog-static","tags":["wordpress","master","python","hexo","staticize","fromto"],"title":"博客静态化工作","url":"https://blog.zengrong.net/post/blog-static/"},{"categories":["technology"],"content":"Markdown Markdown 可以说是目前最流行的标记语言，Github 对它的支持很好，许多论坛、博客也支持这种语法。\n我的博客从2011年6月11日开始使用 Markdown 写作，文章托管在 Github 上。在 Wordpress 中，我曾经采用 Markdown On Save 插件来实现 Markdown 的解析。现在，我写了一个 WPCMD 工具来实现博客更新，终于不再需要和 Workdpress 后台打交道。\n标准的 Markdown 在没有扩展的情况下，是非常简单的，就算写个博客都嫌不够。\n例如，标准的 Markdown 不支持脚注、不支持表格、也不支持 TOC ，而这些在博客写作中都是常用的功能。\n虽然在 Markdown 中可以直接使用 HTML 语法来实现这些需求，但对于表格这种变态的实现来说，手写还不如不写吧。\n正因为如此，Markdown 发展出许多扩展。例如 Markdown Extra 、 GitHub Flavored Markdown 以及 Mastering Markdown 。\nMarkdown On Save 使用的就是 Markdown Extra 这个扩展。\nreStructuredText 开始学习 Python 之后，我又接触到 reStructuredText 这个标记语言。它是 Python 官方的文档写作语言，主要用于 Python 的文档写作，Python 还提供了一个 Docutils 实现它的解析。\nreStructuredText 的复杂程度远远大于 Markdown 。而且，Python 官方文档使用了 sphinx 这个生成器来生成文档，它对 reSturcturedText 又进行了一定程度的扩展，使得其 语法 更加复杂。\n当然，reStructuredText 的复杂也就意味着它更强大。表格、脚注、无所不在的链接、注释支持、内置的语法高亮都让其更加适合撰写 技术文档。当然，写博客也不在话下。\n详细对比 常用轻量级标记语言对照 这篇文章是目前最好的对比文章。其中对比了 Markdown 、reStructuredText 、Textile 、 AsciiDoc 和 Org-mode 这五种常见的标记语言的特点和语法。\n用这篇文章来作为 reStructuredText 的入门也不错，尤其是在熟悉 Markdown 的基础上。\n其他标记语言 这个表列出了更多的标记语言：List of plain-text markup and tools ，维基百科上的 Comparison of document markup languages 一文也对这些语言进行了比较。\n生成器 有文档标记语言，就必须有生成器。\nPandoc 是个异常强大的生成器，看看这张图就知道它有多牛B了：\n我的博客在使用 Markdown 写作之前，还有 500 多篇文章使用的是 HTML。我用 Pandoc 将它们都转到了 Markdown 。虽然结果并不是非常完美，但也尚可接受。\n这里有一篇关于生成器比较的文章可以一看： Comparison of documentation generators 。\n这里有一篇 reStructuredText tool support 展示了各个语言的 rst 工具。\n","date":"2014-11-05","description":"","lastmod":"2015-07-30T01:32:39Z","slug":"markdown_and_restructuredtext","tags":["python","markup"],"title":"Markdown 和 reStructuredText","url":"https://blog.zengrong.net/post/markdown_and_restructuredtext/"},{"categories":["technology"],"content":"Python web框架的选择\n2016-12-13 更新： 加入 CherryPy python web frameworks\n一、缘起网站 大约十多年前（1998年），我做了人生中的第一个网站。那是用的语言是 ASP，工具是 FrontPage。\n后来（2000~2004年），我做了一段时间网站开发，负责前端到后端、美术到产品的所有内容，使用的语言是 ASP 和 PHP，工具是 Macromedia Dreamweaver 和 Editplus。\n那时，我已经开始手写 CSS 和 HTML，并使用 \u0026lt;DIV\u0026gt; 重构网站了。\n再后来，我就没有继续做网站了。\n现在，因为众所周知的原因，我必须捡起这个老本行。由于一直都不太喜欢 PHP，我在考虑选择一个 Python Web 框架。\n二、百花齐放的 Python Web 框架 这里有一个 不完全的列表 展示了一些 Python Web 框架。我关心的主要是下面几个：\nDjango web.py Tornade Bottle CherryPy Flask 三、相关对比 浅谈Python web框架 web.py更面向对象，flask更面向过程 Python Web 框架哪个入门快？Django、Tornado、web.py？ 初学web框架，选哪一个好呢？ web开发框架的选择(bottle or flask)及为autumn增加多线程支持 bottle 和 flask Which is better: Flask vs web.py? Why? Python Flask vs Bottle Bottle vs Flask, who will win? Flask vs. CherryPy 四、我的选择 根据上面的对比，我对这些框架的大致了解如下：\nDjango 是一个一站式解决方案，提供了所有web开发者需要的东西。它有相对封闭的环境和耦合较紧密的系统，同时提供一个管理员后台。但由于功能全面，学习起来可能需要花费较多的时间。但也是由于功能全面，对于不愿意费劲折腾的人来说，应该是更容易使用。文档方面，Django 做得非常优秀。\nweb.py 的原作者好像已经出走？Tornade 算是 web.py 的继任者和加强版，同时它还是个服务器。但文档方面貌似比较缺乏。\nBottle 是和 web.py 类似的微型框架，它的设计更加简洁合理，且只有一个 3K 行的 py 文件，可支持多种模板和服务器。文档也足够完善。\nCherryPy 是个优秀的微星框架。而且自带一个可以用于生产环境的基于线程池的 Web 服务器。使用 Flask 开发，使用 CheeryPy 来部署甚至成了一种通用的部署方式。\nFlask 也是微型框架，在使用上和 Bottle 很像。但Flask 是 Pocoo 团队的作品，在使用上应该更有保障。文档丰富，且有中译本。\n由于我比较爱折腾，Flask 的社区也相对完善，所以选择 Flask。\n","date":"2014-10-12","description":"","lastmod":"2016-12-13T15:16:11Z","slug":"python_web_frameworks","tags":["python","flask","choice"],"title":"Python web框架的选择","url":"https://blog.zengrong.net/post/python_web_frameworks/"},{"categories":["technology"],"content":"Python 测试框架的选择\npython testing framework.\n如何选择一个易用、主流的测试框架？下面是我的方法。\n一、网络资源 Google 上搜索 python testing framework 排名前三的链接分别是：\nunittest — Unit testing framework\npython 标准库自带的单元测试框架。 PythonTestingToolsTaxonomy - Python Wiki\n列举了许多 python 测试框架库，并进行了简单的介绍。 Python Testing - Python Test Frameworks and Strategies python 测试框架的文章汇总，主要介绍 unitest/nose/pytest。 另外，IBM developerWorks 上的 Python 测试框架: 用 Python 测试框架简化测试 介绍了 zope.testing/pytest/nose 框架。\n二、源码库 在 github 上查看目前主流 python 项目使用的测试框架。每个 python 项目都有 tests（或者类似）目录，查看其中的源码就能知道具体采用的测试框架是什么。\n限于时间，我仅查看了 Python Packaging Authority 和 Pocoo 这两个开发组织的项目。\npip 采用的是 pytest; virtualdev 采用的是 pytest; flask 采用的是 pytest; jinjia2 采用的是 unittest; sphinx 采用的是 nose。 三、PyPI PyPI 中的每个库，都有下载次数。根据下载次数可以得知库的流行程度。\n下面的数字是今天（2014-10-09）统计的上月下载次数。\nzope.testing 11,263 pytest 204,190 py 311,705 nose 1,105,383 因为 pytest 已经整合进入 py ，所以上面提供这两个包的下载次数。\n四、我的选择 虽然我更偏向于使用标准库，但观察了 unittest 的写法，发现还是比较繁琐。\nnose 看起来似乎更加流行。就它了。\n五、其它参考 可爱的 Python: Python 中的测试框架 用 Python 编写干净、可测试、高质量的代码 单元测试 ","date":"2014-10-09","description":"","lastmod":"2014-11-13T03:07:35Z","slug":"python_testing_framewrok","tags":["python","testing","choice"],"title":"Python 测试框架的选择","url":"https://blog.zengrong.net/post/python_testing_framewrok/"},{"categories":["technology"],"content":"一、困惑 作为一个 Python 初学者，我在包管理上感到相当疑惑（嗯，是困惑）。主要表现在下面几个方面：\n这几个包管理工具有什么不同？ distutils setuptools distribute disutils2 distlib pip 什么时候该用pip，什么时候该用 setup.py ，它们有关系么？ easy_install、ez_setup.py、setup.py、setup.cfg 分别都是干啥的？ wheel 和 pip 的关系？ Egg 和 whl 的关系？ 如何发布自己的模块（发布到PyPI）？ 如何进行模块的私有发布（不发布到PyPI）？ 为了弄清这些问题，我找了许多资料。最后发现最好的资料还是 python 的官方文档。\n下面是阅读了所有我找到的资料后的一个总结，希望能帮到几个月后又把这些全部忘光的那个自己。\n二、python 包管理工具大乱斗 我用时间顺序来描述乱斗过程。\n1. distutils distutils 是 python 标准库的一部分，2000年发布。使用它能够进行 python 模块的 安装 和 发布。\nsetup.py 就是利用 distutils 的功能写成，我们可以看一个简单的 setup.py 的例子。\n在这里可以看到关于 setupt.py 格式的所有详细描述：Writing the Setup Script。\n要安装一个模块到当前的 python 环境中，可以使用这个模块提供的 setup.py 文件：\n1python setup.py install 下面的代码会发布一个 python 模块，将其打包成 tar.gz 或者 zip 压缩包：\n1python setup.py sdist 甚至能打包成 rpm 或者 exe 安装包：\n1python setup.py bdist_rpm 2python setup.py bdist_wininst 2. setuptools 和 distribute setuptools 是一个为了增强 distutils 而开发的集合，2004年发布。它包含了 easy_install 这个工具。\nez_setup.py 是 setuptools 的安装工具。ez 就是 easy 的缩写。\n简单的说，setuptools 是一个项目的名称，是基础组件。而 easy_install 是这个项目中提供的工具，它依赖基础组件工作。\n为了方便描述，下面文章中提到的 setuptools 被认为与 easy_install 同义。\n使用 setuptools 可以自动 下载、构建、安装和管理 python 模块。\n例如，从 PyPI 上安装一个包：\n1easy_install SQLObject 下载一个包文件，然后安装它：\n1easy_install http://example.com/path/to/MyPackage-1.2.3.tgz 从一个 .egg 格式安装：\n1easy_install /my_downloads/OtherPackage-3.2.1-py2.3.egg distribute 是 setuptools 的一个分支版本。分支的原因可能是有一部分开发者认为 setuptools 开发太慢了。但现在，distribute 又合并回了 setuptools 中。因此，我们可以认为它们是同一个东西。事实上，如果你查看一下 easy_install 的版本，会发现它本质上就是 distribute 。\n1# easy_install --version 2distribute 0.6.28 3. Eggs Eggs 格式是 setuptools 引入的一种文件格式，它使用 .egg 扩展名，用于 Python 模块的安装。\nsetuptools 可以识别这种格式。并解析它，安装它。\n想要详细了解，可以看看这篇：The Quick Guide to Python Eggs。\nEggs are to Pythons as Jars are to Java..\n4. pip 注意，从此处开始，easy_install 和 setuptools 不再同义。\npip 是目前 python 包管理的事实标准，2008年发布。它被用作 easy_install 的替代品，但是它仍有大量的功能建立在 setuptools 组件之上。\npip 希望不再使用 Eggs 格式（虽然它支持 Eggs），而更希望采用“源码发行版”（使用 python setup.py sdist 创建）。这可以充分利用 Requirements File Format 提供的方便功能。\npip 可以利用 requirments.txt 来实现在依赖的安装。在 setup.py 中，也存在一个 install_requires 表来指定依赖的安装。它们的区别在哪里？可以看这篇文章：setup.py vs requirements.txt （中文版）。\npip 支持 git/svn/hg 等流行的 VCS 系统，可以直接从 gz 或者 zip 压缩包安装，支持搜索包，以及指定服务器安装等等功能。\npip vs easy_install 详细介绍了两者的不同。它们可以说是各占胜场，但 pip 明显优势更大。\n5. wheel wheel 本质上是一个 zip 包格式，它使用 .whl 扩展名，用于 python 模块的安装，它的出现是为了替代 Eggs。\nwheel 还提供了一个 bdist_wheel 作为 setuptools 的扩展命令，这个命令可以用来生成 wheel 包。\npip 提供了一个 wheel 子命令来安装 wheel 包。当然，需要先安装 wheel 模块。\nsetup.cfg 可以用来定义 wheel 打包时候的相关信息。\nWheel vs Egg 详细介绍了 wheel 和 Eggs 格式的区别，很显然，wheel 优势明显。\nPython Wheels 网站展示了使用 Wheels 发行的 python 模块在 PyPI 上的占有率。\npypip.in 也支持 wheel。\n6. distutils2 和 distlib distutils2 被设计为 distutils 的替代品。从2009年开发到2012年。它包含更多的功能，并希望以 packaging 作为名称进入 python 3.3 成为标准库的一部分。但这个计划 后来停滞了 。\ndistlib 是 distutils2 的部分，它为 distutils2/packaging 提供的低级功能增加高级 API，使其便于使用。\n这里 介绍了 distlib 没有进入 python 3.3 标准库的一些原因。\n因此，可以暂时不必了解这两个工具，静观其变即可。\n三、工具选择 如果仔细看过上面的乱斗内容，我相信你已经清楚当前应该如何选择了。\n对于我这样刚刚开始的新手来说，自然是使用 pip 而不使用 easy_install 了。\n如果发布模块，当然是使用 wheel 格式。\n四、发布自己的模块 对于 python3 程序员来说，当然应该先看这一篇：Distributing Python Modules。\n另外，Tutorial on Packaging and Distributing Projects 也足够详细和官方。\n而 Python2 程序员则应该看这篇 Distributing Python Modules 。\n当然，setuptools 的官方文档也是不错的教程：Building and Distributing Packages with Setuptools 。\n这篇教程可以用来入门：Sharing Your Labor of Love: PyPI Quick and Dirty\n至于如何发布自己的模块到 PyPI 或者搭建自己的私有包管理服务器，上面的文章已经讲得非常清楚了。\n五、引用 上面提到的大部分资料，都是在这里找到，或者是提到：Python Packaging User Guide。\n感谢这篇文章，让我不再纠结，然后又下决心写了本文继续纠结：Differences between distribute, distutils, setuptools and distutils2? 。\n这篇文章也比较碎：关于python中的setup.py，而且比本文范例更多。\n全文完 ","date":"2014-10-08","description":"","lastmod":"2015-10-31T14:06:40Z","slug":"python_packaging","tags":["python"],"title":"Python 包管理工具解惑","url":"https://blog.zengrong.net/post/python_packaging/"},{"categories":["technology"],"content":"cannot use 'throw' with exceptions disabled\n在为 DragonBonesCPP/refactoring 的 cocos2d-x-3.2 demo 增加 Android 编译时，NDK 报了一个编译错误：\nerror: cannot use 'throw' with exceptions disabled throw std::invalid_argument(\u0026quot;Invalid data.\u0026quot;);\n这是由于 DragonBonesCPP 库使用了 C++ 标准异常，而 Android NDK 编译器默认不支持 C++ 异常控制导致。\n但是，从Android NDK r5 版本开始，NDK 就开始支持 C++ 异常控制了，我的版本是 r9d ，为什么还会出现这个错误呢？\n原来，NDK 编译器的 C++ 异常控制特性支持默认是关闭的。要打开它，可以这样做：\n编辑 Android.mk 文件，加入下面的代码：\nLOCAL_CPP_FEATURES += exceptions 或者：\nLOCAL_CPPFLAGS += -fexceptions 这样可以对当前项目开启 C++ 异常控制编译支持。\n如果想偷懒，也可以编辑 Application.mk 文件，加入下面的代码：\nAPP_CPPFLAGS += -fexceptions 这样，所有模块的 C++ 异常控制编译支持都会被打开。\n上面的两个 mk 文件位于 proj.android/jni 文件夹中。\n参考文章：\n解决NDK出现error: exception handling disabled, use -fexceptions to enable的问题 Enable Exception C++ ","date":"2014-10-07","description":"","lastmod":"2014-10-07T06:10:50Z","slug":"cannot_use_throw_with_exceptions_disable","tags":["android","c","cocos2d-x","dragonbones"],"title":"cannot use 'throw' with exceptions disabled","url":"https://blog.zengrong.net/post/cannot_use_throw_with_exceptions_disable/"},{"categories":["technology"],"content":"python virtual enviorments\n2014-10-10更新： 更新描述。 2015-04-18更新： 加入 ubuntu 14.04 下 pyvenv bug 的解决。 为了解决包依赖、安装权限和 python 版本问题，程序员们制造了许多工具。我把它们做一个汇总，便于查找。\n这里介绍的工具都是当前流行的，不会包含已经不再维护的工具。\nvirtualenv virtualenv 是目前最流行的 python 虚拟环境配置工具。它不仅同时支持 python2 和 python3，而且可以为每个虚拟环境指定 python 解释器，并选择不继承基础版本的包。\nvirtualenvwrapper 顾名思义，virtualenvwrapper 是对 virtualenv 的一个封装，目的是使后者更好用。\n关于为什么使用 shell 脚本开发，作者专门 进行了解释 。\nvirtualenvwrapper 还有针对 vim 用户和 emacs 用户的 扩展 。\nvirtualenvwrapper 能支持 bash/ksh/zsh ，所以我们可以看出，它不支持 Windows。\nvirtualenvwrapper-win 由于 virtualenvwrapper 基于 shell 开发，因此不能在 Windows 系统上使用。但我们可以使用针对 Windows batch shell 的 virtualenvwrapper-win。\nvenv Python 从3.3 版本开始，自带了一个虚拟环境 venv，在 PEP-405 中可以看到它的详细介绍。它的很多操作都和 virtualenv 类似。\n因为是从 3.3 版本开始自带的，这个工具也仅仅支持 python 3.3 和以后版本。所以，要在 python2 上使用虚拟环境，依然要利用 virtualenv 。\n在 *nix 系统上，可以直接执行 pyvenv /path/to/new/virtual/enviorment 来创建一个虚拟环境，在 Windows 系统上，则可以使用 python -m venv myenv 来创建。\n2015-04-18 更新：\npyvenv 3.4 在 Ubuntu 14.04 下有 bug，如下：\n1pyvenv ➤ python3 -m venv blog 2Error: Command \u0026#39;[\u0026#39;/home/zrong/pyvenv/blog/bin/python3\u0026#39;, \u0026#39;-Im\u0026#39;, \u0026#39;ensurepip\u0026#39;, \u0026#39;--upgrade\u0026#39;, \u0026#39;--default-pip\u0026#39;]\u0026#39; returned non-zero exit status 1 3pyvenv ➤ pyvenv-3.4 --without-pip blog 解决方法是创建一个不含 pip 的虚拟环境，然后手动安装 pip ：\n1pyvenv-3.4 --without-pip venvdir 2source venvdir/bin/activate 3curl https://bootstrap.pypa.io/get-pip.py | python 4source venvdir/bin/activate 参见：\npyvenv-3.4 error: returned non-zero exit status 1 pyvenv-3.4 returned non-zero exit status 1 pyenv 我们可以用许多方法让不同的 Python 版本在系统上共存。\n例如在 OS X 上，如果使用官方提供的 DMG 版本安装，那么自带的 python2 和新安装的 python3 是可以共存的。python3 可以使用 python3 来调用，甚至 pip 都可以使用 pip3 来调用。\n但如果还有其它小版本需要共存么？我要记忆多少命令呢？\npyenv 用来解决这类问题。它可以安装、卸载、编译、管理多个 python 版本，并随时将其中一个设置为工作环境。\npyenv 不支持 Windows 系统。\npywin Windows 上有一个 pyenv 的替代品，是 pywin 。它用来在多个安装的 Python 版本之间进行切换，也支持 MSYS/MINGW32 。\nPython Launcher for Windows Python 从3.3版本开始（又是3.3？），在 Windows 系统中自带了一个 py.exe 启动工具。如果你是使用 Python.org 官网下载的安装包安装的 Python 3.3（或更新版本）环境，那么可以直接在命令提示符中使用这个工具。\npy 可以打开默认的 python 提示符； py -2.7 和 py -3 打开对应的 Python 版本。\n上面介绍的工具中，前四个是虚拟环境切换工具，后三个是 Python 版本环境切换工具。将这两套工具结合使用，可以完美解决 python 多版本环境的问题。\n（全文完）\n","date":"2014-10-07","description":"","lastmod":"2014-10-07T03:23:26Z","slug":"python_virtual_env","tags":["python"],"title":"Python 虚拟环境","url":"https://blog.zengrong.net/post/python_virtual_env/"},{"categories":["technology"],"content":"AIR 15.0 提交 AppStore 错误 ERROR ITMS-9000: Invalid Segment Alignment\n上周提交到 AppStore 的 IPA 被拒了，其中的主要错误如下：\nITC.apps.assetvalidation.PURPLE_EXECUTABLE_OUT_OF_ALIGNMENT.error.message\n我联想到有可能是因为 iOS8 支持的原因，于是在 labs.adobe.com 下载了 2014-09-24 日发布的 AIR 15.0.289 beta 版重新打包，但在提交到 AppStore 的时候收到这样的错误提示：\nERROR ITMS-9000: \u0026quot;Invalid Segment Alignment. This app does not have proper segment alignment and should be rebuilt with the latest version of Xcode. Please contact Developer Technical Support if you need further assistance.\u0026quot;\n这消息看起来和上次被拒的原因一致。我可以确定是 iOS8 的原因了。最可能的情况是 Apple 要求新提审的包必须加入 iOS8 的相关字段，而 Adobe AIR SDK 的编译器没有提供这些字段。\n在 Google 上搜了一通，发现不少人碰到这个问题，只是没有解决方案。\n一旦 Adobe AIR 开发者碰到这种情况，只能等待 Adobe 解决，就像我以前发现的这些 BUG 一样：\nAIR在iOS7上的Microphone权限问题解决 朝鲜语/韩文字符在Anrdoid4.2.2上不显示Korean text isn’t shown in Android 4.2.2 AIR 3.7 SDK Bug:You uploaded an unsigned APK 经过努力搜索，终于找到这一篇：No longer able to submit app to iTunes. ，Adobe 的员工在论坛上给出了临时的解决方案：\n1cd /lib/aot/bin/ld64 # 进入AIR SDK 的相关目录 2mv ld64 ld64_orig # 备份原始的链接工具 3ln -s /usr/bin/ld ld64 # 使用系统自带的 ld 链接工具替换 SDK 中的链接工具 然后重新打包，顺利提交。\n注意事项：\n此方案只能在 Mac OSX 系统上可用，Windows 神马的就不要尝试了； 原作者并没有说明是否要升级到 Xcode6，但我在尝试之前将系统升级到了 OSX 10.9.5，Xcode升级到了 6.0.1。 只能在OS X上使用的原因其实很简单。AIR SDK 是通过在 Windows 上提供全套 iOS 编译和链接工具来实现交叉编译（在Windows平台上编译IPA包），现在的问题出在链接器上。上面的解决方案是通过使用OS X自带的链接器替换 AIR SDK 中的链接器。而在 Windows 平台上，我们无法直接使用 OS X 的链接器。所以只能等待 Adobe 更新了。\n话说 Adobe 的动作也太慢了点。\n2014-09-29 更新\nAdobe 员工 Pahup 已经在论坛上放出了更新的 SDK 下载，可惜，没有 Flex 版本的。详情看 这里 .\n转载如下：\nWe're pleased to share that we have a Win/Mac build with the fix, you can download the SDK (zip for Win, tbz2 for Mac) from here. We strongly recommend to test the application functionality before trying out the submissions on app store. We really appreciate your patience and support.\nPlease note, this SDK also sets the MinimumOSVersion to 6.1 in the info.plist of the resulting IPA.\nA warning like \u0026quot;ld: warning: CPU_SUBTYPE_ARM_ALL subtype is deprecated\u0026quot; with '-uselegacyAOT no' might occur, but that should be harmless. If you observe any other packaging issues, please report to us as soon as possible with your configuration details.\nUpdate, the alternate links are following\nMac - https://dh8vjmvwgc27o.cloudfront.net/airsdk_ld64/archive_air_15.0_with_asc2_drm_sdk.tbz2 Win - https://dh8vjmvwgc27o.cloudfront.net/airsdk_ld64/archive_air_15.0_with_asc2_drm_sdk.zip 2014-10-05 更新\n2014-10-02，Adobe在 labs.adobe.com 上更新了一个SDK版本，但并没有说明解决了什么问题。有需要的可以尝试。\n2014-10-10 更新\n经确认，这个 SDK 版本已经解决了上述问题。\n","date":"2014-09-29","description":"","lastmod":"2014-09-29T06:56:45Z","slug":"itms-9000-invalid-segment-aligenment-on-commit-to-appstore","tags":["air","ios"],"title":"AIR 15.0 提交 AppStore 错误 ERROR ITMS-9000","url":"https://blog.zengrong.net/post/itms-9000-invalid-segment-aligenment-on-commit-to-appstore/"},{"categories":["technology"],"content":"IBM developerWorks 线程文章汇总\n最早我在 POSIX線程(pthread)入門文章分享 看到了关于 IBM developerWorks 中线程文章的一些汇总。但是按照该文中提供的链接一一找去，发现均不能访问。\n原来 IBM developerWorks 对目录结构进行了修改，因此原来的链接就都作废了。\n由于找不到 IBM developerWorks 中的相关汇总页面，我就人肉汇总了一次。\n原文中只有 C 和 C++ 部分的内容，我又加入了 Python 和 HTML5 的内容。\n下文的繁体字内容来自 POSIX線程(pthread)入門文章分享。\nPOSIX线程 POSIX 表示可移植操作系統接口（Portable Operating System Interface ，縮寫為 POSIX 是為了讀音更像 UNIX）。電氣和電子工程師協會（Institute of Electrical and Electronics Engineers，IEEE）最初開發 POSIX 標準，是為了提高 UNIX 環境下應用程序的可移植性。具體的說 POSIX 是 IEEE 為要在各種 UNIX 操作系統上運行的軟件定義 API 所規定的一系列互相關聯的標準的總稱，而 X 則表明其對 Unix API 的傳承。Linux 基本上逐步實現了 POSIX 兼容，但並沒有參加正式的 POSIX 認證。當前的 POSIX 文檔分為三個部分：POSIX Kernel API，POSIX 命令和工具集，及 POSIX 一致性測試。Posix 線程（POSIX threads，又稱 Pthreads）是負責 POSIX 的 IEEE 委員會開發的一套線程接口。\n中文版 pthreads 的基本用法-介绍POSIX线程 英文版 Basic use of pthreads-An introduction to POSIX threads POSIX 线程详解 Daniel Robbins 從實例入手，逐步講解 POSIX thread 編程技巧，有共享內存、互斥以及條件變量的運用。\n中文版本 (1)一种支持内存共享的简捷工具 (2)称作互斥对象的小玩意 (3)使用条件变量提高效率 英文版本 POSIX threads explained Common threads: POSIX threads explained, Part 2 Common threads: POSIX threads explained, Part 3 Posix线程编程指南 (1)线程的创建和取消 (2)线程私有数据 (3)线程同步 (4)线程终止 (5)杂项 pthread 内存泄露 中文版 在 POSIX 线程编程中避免内存泄漏\n英文版 Avoiding memory leaks in POSIX thread programming 其它 pthread Pthreads arguments passing POSIX Threads Programming POSIX thread (pthread) libraries Linux线程模型 Linux 最初用的線程模型是 LinuxThread, 它不兼容 POSIX，而且存在一些性能問題，所以目前 Linux 摒棄了它，採用了基於 Pthreads 的 NPTL（Native POSIX Threads Library for Linux）模型， NPTL 修復了 LinuxThread 的許多缺點，並提供了更好的性能。\nLinux 线程模型的比较：LinuxThreads 和 NPTL Linux线程实现机制分析 Linux 线程库性能测试与分析 HTML5 深入 HTML5 Web Worker 应用实践：多线程编程\nPython 使用 Python 进行线程编程 使用 Python 实现多进程 ","date":"2014-09-26","description":"","lastmod":"2014-09-26T03:12:40Z","slug":"thread_articles_in_ibm-developerworks","tags":["c","html5","linux","posix","python","thread"],"title":"IBM developerWorks 线程文章汇总","url":"https://blog.zengrong.net/post/thread_articles_in_ibm-developerworks/"},{"categories":["technology"],"content":"两天前，Robert Mening 给我发来邮件，告知我 [转]30余款HTML工具和教程 一文中的 EchoEcho.com 网站的内容太老，希望我能更新，并加入他的 HTML5 Beginner's Guide 。\nHTML5 Beginner's Guide 的设计简洁，内容也是完全针对新手，有兴趣从头学习 HTML5 的话，可以根据他的教程对 HTML5 进行初步了解。\nRobert 是个挺有趣的人，他在 About 页面中写道许多人在他的网站上线后给他发邮件，要他帮忙做网站，然后他委婉地拒绝了（I don't have THAT much time to set it up for YOU）。这是意料之中的事，就和我在 一个项目开源到底有哪些考虑？ 中说的一样，总有人想把你当免费劳力使唤，此事中外亦同。\nRobert 提到的文章是六年前发布的，虽然我经常对旧文进行修改，但那篇文章是转载的，为了尊重原作者，我决定依然保留原文内容。\n","date":"2014-09-11","description":"","lastmod":"2014-09-11T01:32:23Z","slug":"html5-beginners-guide","tags":["html5"],"title":"HTML5入门指南","url":"https://blog.zengrong.net/post/html5-beginners-guide/"},{"categories":["technology"],"content":"各视频站做到自动切换flash和html5播放器的难度有多大？\nThe difficulty of change video player from flash to html5.\n这是一个知乎上的回答，原文在这里：http://www.zhihu.com/question/25259832/answer/30319472\n问：\n做到根据操作系统、浏览器或者用户自定义来使用不同的播放器是否有难度？ 如果有难度，问题点在哪里？ 如果不算难，为什么不做？ 答：\n判断操作系统和浏览器种类和版本，这个不难。\n目前的主流平台一共5个：\n桌面平台：\nWindows（完美支持H5和 Flash Player） Mac OS X（完美支持 H5和 Flash Player） Linux（支持 H5，Flash Player支持到11.2，Chrome对 Flash Player 支持更好） 移动平台：\nAndroid（支持 H5，Adobe已经放弃Flash Player支持） iOS（支持 H5， 不支持 Flash Player） 从上面可以看到，有半数以上的平台（包括已经放弃支持的）支持 Flash Player。既然超过了半数，就有进行切换的需求与可能。\n最完美的情况，就是检测操作系统和浏览器版本，根据不同情况进行适配，例如：\nWindows XP 系统，IE6浏览器，使用 Flash Player 来播放视频； Mac OS X 系统，使用 H5 来播放视频； 移动平台，必须使用 H5 ； …… 虽然在前端开发上有一定的成本（例如 Flash Player 和 H5 的界面适配、Desktop 和 Mobile 的界面适配，以及各种浏览器和平台的组合之类的），但也不是不可承受。\n让我们来看看 Flash Player 与 H5 以及各个浏览器支持的视频格式吧（下表来自 HTML5 Video ）：\n也就是说，在支持 video 标签的 H5 浏览器中，并没有一种视频格式是所有浏览器通吃的。\n如果觉得这个表不够详(zhuan)细(ye)的话，还可以看看下面这张图（下表来自 wikipedia HTML5 video ）：\n如果抛弃掉可爱又可怜的 Opera ，以及不考虑 Firefox 在 OS X 和 Linux 上的问题，我们确实可以说，MP4格式已经是所有现代浏览器能够直接支持的视频格式（下表来自 A/V Formats | The State of HTML5 Video Report ）：\n而实际上，已经有公司在做这件事（而且做了好久），JWPlayer 是一个优秀的 Flash Player 视频播放器，后来开始支持 H5，下面是它的一些介绍：\nHTML5 Video Player \u0026amp; Flash Video Player HTML5 Video Attributes \u0026amp; Market Share Report 所以，技术上并不是问题。做还是不做，我猜测最大的原因是视频格式转换成本。\n这些视频网站已经积累的大量的视频资源，早期基本上是 Flash Only 的 FLV 格式。后期如果继续用Flash Player 来播放视频，为了实现高清的支持，很可能使用的是 MP4 的马甲 F4V 格式。这两种文件格式的标准可以看这里： F4V/FLV Technology Center。\nFLV 格式早期使用过两种编码，分别是 Sorenson Spark 和 On2 VP6 （下表来自 抛弃FLV，迎接MP4——制作Flash Player支持的H.264视频格式），这两种格式 H5 肯定是不支持的。\n因此，要将现有的海量视频数据转换成 MP4，需要一个相当的过程，视频网站可能考虑过这个过程比较漫长，就暂时将该工作搁置（或者正在平稳进行中）。\n比如，YouTube 就已经把自己的所有视频转向 WebM（这是 Google 推动的视频格式），可以看这里：YouTube Swiftly Converts Videos in WebM Format\n但 Google 毕竟是钞票多到用不完，牛人多到一个团，从标准到产品到实现一条龙的一流公司。对于天朝这些小公司比如 爱X艺优X库马铃薯之类来说，除了技术市场钞票广告之外，还必须考虑国情。\n在伟大而神奇的中国，大量的平台依然是Windows XP + IE6 组合，让这些系统升级到 IE8都嫌困难，更别提安装 Chrome 和 Firefox 这种现代浏览器了。针对于它们，Flash Player 是唯一的选择。既然用 Flash Player 来播放视频，那么 FLV 还是 MP4 显然就无所谓了。\n不过，一切都会改变的，至少在我们的有生之年，它们是一定会改过来的。\n相比用 H5 全平台看视频，我更期待在有生之年登上月球（火星太远就不考虑了）。不过就目前科技的发展速度来看，有点儿悬。\n另外吐槽题主3点：\n不是所有人都买得起 MBP 的…… 发热不能全怪在Flash Player 头上…… 就算你一定要怪在 Flash Player 头上，也不要仅仅是怪在视频播放器头上…… ","date":"2014-09-09","description":"","lastmod":"2014-09-09T07:13:44Z","slug":"the_difficulty_of_change_video_player_from_flash_to_html5","tags":["adobe","flash","google","h264","html5"],"title":"各视频站做到自动切换flash和html5播放器的难度有多大？","url":"https://blog.zengrong.net/post/the_difficulty_of_change_video_player_from_flash_to_html5/"},{"categories":["technology"],"content":"在 iOS 7 中获取唯一标识符（UDID/UUID）\n在 iOS 5 中， 可以获取到系统的 UDID(Unique Device Identifier) ，后来被 Apple 禁止掉了。\n于是，在 iOS 6 中，大家开始使用 MAC 地址 MAC(Medium/Media Access Control) ，后来又被 Apple 禁止掉了。\n同样的，OpenUDID 也不能用了：\nOpenUDID doesn't work on iOS 7 。 UDID is dead, OpenUDID is deprecated, long live advertisingIdentifier! 在 iOS 7 中，Apple 推荐使用广告标识符 advertisingIdentifier 来获取系统的唯一标识符。但是，用户如果重置了系统，广告标识符会重新生成。这就达不到 “唯一标识符” 的作用。\n于是，在 iOS 7 中，程序员们发明了 “钥匙串保存” 方法，将这个唯一标识符保存在钥匙串中，安装了 App 后读取这个标识符即可。参见这里：ios 利用钥匙串保存密码和获取密码 和 Simple iPhone Keychain Access 。\n更详细的操作，可以参考这篇：如何使用KeyChain保存和获取UDID 。\n下面这篇文章非常详细地介绍了 iOS 上获取唯一标识符的所有方式，以及相关知识，为了避免这么优秀的文章以后消失掉，我将其全文转载如下（已经很少全文转载了……）。\n原创部分完毕 下面的内容转自： 网易杭州 QA Team\n英文原文：\nIn iOS 7 and later, if you ask for the MAC address of an iOS device, the system returns the value 02:00:00:00:00:00. If you need to identify the device, use the identifierForVendor property of UIDevice instead. (Apps that need an identifier for their own advertising purposes should consider using the advertisingIdentifier property of ASIdentifierManager instead.)\n翻译：从iOS7及更高版本往后，如果你向ios设备请求获取mac地址，系统将返回一个固定值“02:00:00:00:00:00”，如果你需要识别设备的 唯一性，请使用UIDevice的identifierForVendor属性。（因广告目的而需要识别设备的应用，请考虑使用 ASIdentifierManager的advertisingIdentifier属性作为替代）\n这个MAC地址是指什么？有什么用？\nMAC(Medium/Media Access Control)地址，用来表示互联网上每一个站点的标识符，采用十六进制数表示，共六个字节（48位）。其中，前三个字节是由IEEE的注册管理机构 RA负责给不同厂家分配的代码(高位24位)，也称为“编制上唯一的标识符” （Organizationally Unique Identifier)，后三个字节(低位24位)由各厂家自行指派给生产的适配器接口，称为扩展标识符（唯一性）。\nMAC地址在网络上用来区分设备的唯一性，接入网络的设备都有一个MAC地址，他们肯定都是不同的，是唯一的。一部iPhone上可能有多个MAC地址，包括WIFI的、SIM的等，但是iTouch和iPad上就有一个WIFI的，因此只需获取WIFI的MAC地址就好了，也就是en0的地址。\n形象的说，MAC地址就如同我们身份证上的身份证号码，具有全球唯一性。这样就可以非常好的标识设备唯一性，类似与苹果设备的UDID号，通常的用途有：1）用于一些统计与分析目的，利用用户的操作习惯和数据更好的规划产品；2）作为用户ID来唯一识别用户，可以用游客身份使用app又能在服务器端保存相应的信息，省去用户名、密码等注册过程。\n那么，如何使用Mac地址生成设备的唯一标识呢？主要分三种：\n直接使用“MAC Address” 使用“MD5(MAC Address)” 使用“MD5(Mac Address+bundle_id)”获得“机器＋应用”的唯一标识（bundle_id 是应用的唯一标识） iOS7之前，因为Mac地址是唯一的， 一般app开发者会采取第3种方式来识别安装对应app的设备。为什么会使用它？在iOS5之前，都是使用UDID的，后来被禁用。苹果推荐使用UUID 但是也有诸多问题，从而使用MAC地址。而MAC地址跟UDID一样，存在隐私问题，现在苹果新发布的iOS7上，如果请求Mac地址都会返回一个固定 值，那么Mac Address+bundle_id这个值大家的设备都变成一致的啦，跟UDID一样相当于被禁用。那么，要怎么标识设备唯一呢？\n在iOS系统中，获取设备唯一标识的方法有很多：\nUDID(Unique Device Identifier) UUID(Universally Unique Identifier) MAC Address OPEN UDID 广告标示符（IDFA-identifierForIdentifier） Vendor标示符 (IDFV-identifierForVendor) 推送token＋bundle_id UDID的全称是Unique Device Identifier，它就是苹果IOS设备的唯一识别码，它由40个字符的字母和数字组成（越狱的设备通过某些工具可以改变设备的UDID）。移动网络可利用UDID来识别移动设备，但是，从IOS5.0（2011年8月份）开始，苹果宣布将不再支持用uniqueIdentifier方法获取设备的UDID，iOS5以下是可以用的。在2013年3月21日苹果已经通知开发者：从2013年5月1日起，访问UIDIDs的程序将不再被审核通过，替代的方案是开发者应该使用“在iOS 6中介绍的Vendor或Advertising标示符”。所以UDID是绝对不能用啦。\nOPEN UDID，没有用到MAC地址，同时能保证同一台设备上的不同应用使用同一个OpenUDID，只要用户设备上有一个使用了OpenUDID的应用存在时，其他后续安装的应用如果获取OpenUDID，都将会获得第一个应用生成的那个。但是根据贡献者的代码和方法，和一些开发者的经验，如果把使用了OpenUDID方案的应用全部都删除，再重新获取OpenUDID，此时的OpenUDID就跟以前的不一样。可见，这种方法还是不保险。\n广告标示符，是iOS 6中另外一个新的方法，提供了一个方法advertisingIdentifier，通过调用该方法会返回一个NSUUID实例，最后可以获得一个UUID，由系统存储着的。不过即使这是由系统存储的，但是有几种情况下，会重新生成广告标示符。如果用户完全重置系统（(设置程序 -\u0026gt; 通用 -\u0026gt; 还原 -\u0026gt; 还原位置与隐私) ，这个广告标示符会重新生成。另外如果用户明确的还原广告(设置程序-\u0026gt; 通用 -\u0026gt; 关于本机 -\u0026gt; 广告 -\u0026gt; 还原广告标示符) ，那么广告标示符也会重新生成。关于广告标示符的还原，有一点需要注意：如果程序在后台运行，此时用户“还原广告标示符”，然后再回到程序中，此时获取广 告标示符并不会立即获得还原后的标示符。必须要终止程序，然后再重新启动程序，才能获得还原后的广告标示符。\nVendor标示符，也是在iOS 6中新增的，跟advertisingIdentifier一样，该方法返回的是一个 NSUUID对象，可以获得一个UUID。如果满足条件“相同的一个程序里面-相同的vendor-相同的设备”，那么获取到的这个属性值就不会变。如果是“相同的程序-相同的设备-不同的vendor，或者是相同的程序-不同的设备-无论是否相同的vendor”这样的情况，那么这个值是不会相同的。\n推送token＋bundle_id的方法：\n应用中增加推送用来获取 token 获取应用 bundle_id 根据 token+bundle_id 进行散列运算 apple push token 保证设备唯一，但必须有网络情况下才能工作，该方法不依赖于设备本身，但依赖于apple push，而苹果push有时候会抽风的。\nUUID 是 Universally Unique Identifier 的缩写，中文意思是通用唯一识别码。它是让分布式系统中的所有元素，都能有唯一的辨识资讯，而不需要透过中央控制端来做辨识资讯的指定。这样，每个人都可以建立不与其它人冲突的 UUID。在此情况下，就不需考虑数据库建立时的名称重复问题。苹果公司建议使用UUID为应用生成唯一标识字符串。\niOS中获取UUID的代码如下:\n1-(NSString*) uuid { 2 CFUUIDRef puuid = CFUUIDCreate( nil ); 3 CFStringRef uuidString = CFUUIDCreateString( nil, puuid ); 4 NSString * result = (NSString *)CFStringCreateCopy( NULL, uuidString); 5 CFRelease(puuid); 6 CFRelease(uuidString); 7 return [result autorelease]; 8} 开发者可以在应用第一次启动时调用一 次，然后将该串存储起来，以便以后替代UDID来使用。但是，如果用户删除该应用再次安装时，又会生成新的字符串，所以不能保证唯一识别该设备。这就需要各路高手想出各种解决方案。所以，之前很多应用就采用MAC Address。但是现在如果用户升级到iOS7（及其以后的苹果系统）后，他们机子的MAC Address就是一样的，没办法做区分，只能弃用此方法，重新使用UUID来标识。如果使用UUID，就要考虑应用被删除后再重新安装时的处理。\n一个解决的办法是：UUID一般只生成一次，保存在iOS系统里面，如果应用删除了，重装应用之后它的UUID还是一样的，除非系统重置 。但是不能保证在以后的系统升级后还能用（如果系统保存了该信息就能用）。\n转载完毕 ","date":"2014-08-27","description":"","lastmod":"2014-08-27T03:39:01Z","slug":"get_unique_id_in_ios7","tags":["ios"],"title":"在 iOS 7 中获取唯一标识符（UDID/UUID）","url":"https://blog.zengrong.net/post/get_unique_id_in_ios7/"},{"categories":["technology"],"content":"我们是否还应该支持 iOS 5？\nMust we support iOS 5 now?\n游戏准备上线，纠结于一个API的支持问题。这个 API 从 iOS 6 开始支持。\n那么，我们的游戏是否还要支持 iOS 5？\n寻找了一些资料：\n为什么很多 App 都变成 iOS 6 only 的了 （2013-01） iOS 7开放下载27天后渗透率达71%（2013-10-15） 移动互联网用户终端环境分析报告（2013-05~2013-07） 从腾讯的报告中可以看到，在2013年7月，iOS 6的占有率已经高达77%。\n从今天 apple 的官方数据来看，iOS 7 的占用率已经达到91%，iOS 6 是8%，剩下的版本只有可怜的 1%。\nAs measured by the App Store during a 7‑day period ending August 24, 2014.\n看来答案已经很清晰了。\n2015-10-12 更新：\n顺便把 Android 版本占有率也贴上吧：The relative number of devices running a given version of the Android platform\n","date":"2014-08-27","description":"","lastmod":"2014-08-27T03:05:10Z","slug":"must_we_support_ios5_now","tags":["ios"],"title":"我们是否还应该支持 iOS 5？","url":"https://blog.zengrong.net/post/must_we_support_ios5_now/"},{"categories":["technology"],"content":"Flash air 开发iOS游戏在苹果上架，能否热更新？\n这是我在知乎上的一个回答。原文地址： http://www.zhihu.com/question/24847013/answer/29263899\n我曾经多次被问到过这个问题。正好整理一下思路，免得以后忘了。\n问： 游戏已经上架，以前的更新方案在苹果更新后，发现更新下来的swf不能正常读入，导致游戏无法进入。不知各位大神有没有什么方法解决。\n答： 目前，在移动平台上想利用SWF热更新，只有 Android 能做到。\nAIR曾经有一个 3.5beta 更新说在 iOS 上支持完整的 SWF 载入（和 PC 上的表现一样），但后来相关的说明被全部删除了，历史都找不到。我估计是 Adobe 和 Apple 没有谈拢。这或许可以说明，在技术上是可行的，但商业策略上 Apple 是坚决反对的。\n后来，AIR3.6更新了一个功能，在 ipa 的 AOT 模式中支持多个 SWF 的载入，但仅支持载入打包到 IPA 中的 SWF ，看这里：Release Notes。\n这里有一篇详细的中文介绍：Adobe AIR读取本地外部SWF文件的功能概览\n这篇文章是我上面提到的 Adobe 删除事件之前发布的，因此里面有许多内容后来变化了，阅读的时候要注意。\n若我没有记错，文章作者 James Li 已经从 Adobe 离职了。\n那么，不利用 SWF 有可能实现热更新么？\n可能。\nAS 本身是基于 ECMAScript 的脚本语言，和 JS 相同的祖先。所以只要实现 AS 的解析器就好。已有现成的实现，可以找找，但速度不保证。\n在 cocos2d-x 上，lua 的热更新就是这样实现的，参照这个：quick-cocos2d-x的热更新机制实现 。\n当然，Apple 是明文禁止热更新的，所以我们所要做的，就是在 Apple 审核的时候关闭热更新功能。\n","date":"2014-08-16","description":"","lastmod":"2014-08-16T02:08:16Z","slug":"swf-hot-update_on_ios","tags":["actionscript","air","cocos2d-x","lua"],"title":"Flash air 开发iOS游戏在苹果上架，能否热更新？","url":"https://blog.zengrong.net/post/swf-hot-update_on_ios/"},{"categories":["technology"],"content":"Python3 的异常处理\nException in Python3.\nPython3 的异常处理，在官方文档的 tutorial 中有说明。\n这里把常用的异常处理方法都列出来，方便平时查找。\n捕获异常基类 Python3 要求我们的异常必须继承 Exception 类。Built-in 的所有异常也都是继承自这个类。因此，我们只需要捕获这个类的实例，就可以捕获所有的异常。\n1try: 2\traise 3except Exception as err: 4\tprint(err) 使用 sys.exc_info() 和 sys.last_traceback sys.exc_info() 会返回一个3值元表，其中包含调用该命令时捕获的异常。\n这个元表的内容为 (type, value, traceback) ，其中：\ntype 从获取到的异常中得到类型名称，它是BaseException 的子类； value 是捕获到的异常实例； traceback 是一个 traceback 对象，下面会详述。 sys.last_traceback 包含的内容与 sys.exc_info() 相同，但它主要用于调试，并不总是被定义。\n1import sys 2try: 3\traise 4except: 5\tt,v,tb = sys.exc_info() 6\tprint(t,v) 使用 traceback trackback 模块用来精确模仿 python3 解析器的 stack trace 行为。在程序中应该尽量使用这个模块。\ntraceback.print_exc() 可以直接打印当前的异常。\n1import traceback 2try: 3\traise 4except: 5\ttraceback.print_exc() traceback.print_tb() 用来打印上面提到的 trackback 对象。\n1import sys,traceback 2try: 3\traise 4except: 5\tt,v,tb = sys.exc_info() 6\ttraceback.print_tb(tb) traceback.print_exception() 可以直接打印 sys.exc_info() 提供的元表。\n1import sys,traceback 2try: 3\traise 4except: 5\ttraceback.print_exception(*sys.exc_info()) 其实，下面两句是等价的：\ntraceback.print_exc() traceback.print_exception(*sys.exc_info()) traceback 提供的参数可以将 print 的内容写入到文件中，详见这里：29.9. traceback — Print or retrieve a stack traceback\n","date":"2014-08-14","description":"","lastmod":"2014-08-14T01:53:27Z","slug":"exception_in_python3","tags":["python"],"title":"Python3 的异常处理","url":"https://blog.zengrong.net/post/exception_in_python3/"},{"categories":["technology"],"content":"删除 lua.ByteArray 中与 Long 相关的方法\nDelete some methods about Long in lua.ByteArray.\n在 用lua实现ByteArray和ByteArrayVarint 一文中，我介绍了用 lua+lpack+BitOp 实现的 ByteArray 模块，这个模块模仿 AS3 的ByteArray，给从Flash转到quick-cocos2d-x的程序员以亲切感。\n这个模块被网友们找出了一些BUG，例如 这个 ， 这个 ，这个 ，和 这个 。\n今天，我将这些网友提出的问题一一做了确认和测试，发现有些并非BUG，而是理解有偏差。\n但有个不是BUG的问题却可能造成重大的错误，那就是：\nlpack 库操作long值的时候，长度在不同机器上是不统一的。\n看下面这段代码（完整代码 在此）：\n1require(\u0026#34;pack\u0026#34;) 2local l = string.pack(\u0026#34;l\u0026#34;, 32333) 3print(#l) 4local L = string.pack(\u0026#34;L\u0026#34;, 33333) 5print(#L) 6local i = string.pack(\u0026#34;i\u0026#34;, 32333) 7print(#i) 8local I = string.pack(\u0026#34;I\u0026#34;, 33333) 9print(#I) 这段代码在不同的平台上执行得到的结果并不相同。在 Mac 平台上，输出的结果是8,8,4,4，而在 iOS 模拟器中，输出的结果是 4,4,4,4 。\n当然，lpack 库的表现并非它的bug，而是因为根据 C语言标准，long的长度是 At least 32 bits in size，这就导致了 long 的长度受到机器字长和编译器的影响。\nMac 版本的 quick-x-player 采用64bit的编译器来编译，因此 long 的长度是8；而 iOS 模拟器采用的可能是32位编译器（以及CPU架构），所以 long 的长度是4.\nApple的新移动设备将采用64bit的CPU架构，但淘汰旧设备可能需要很长时间。\n何况还有Android。\n虽然这并非quick的问题，也不是lpack的问题，甚至不是lua和C语言的问题。但对于quick这样一个跨了4个平台（Win/Mac/iOS/Android）的引擎来说，这种表现显然是不合理的。\n虽然 Windows 和 Mac 并非 quick 主要面向的平台，但我们必须考虑在这两个平台上使用 quick-x-player 开发的程序员的使用习惯。\n同时，由于 ByteArray 主要用于和服务器通讯（至少在我的项目中是这样），我们也要考虑long在服务器端的不同的处理方式。\n因此，我决定从 ByteArray 库中删除4个与Long相关的方法，它们是：\nreadLong writeLong readULong writeULong 这些方法已经从我修改的 quick-cocos2d-x 中移除。我会稍后将其推送给触控。\n","date":"2014-07-09","description":"","lastmod":"2014-07-09T06:21:30Z","slug":"delete-some-methods-about-long-in-lua-bytearray","tags":["cpp","lua"],"title":"删除 lua.ByteArray 中与 Long 相关的方法","url":"https://blog.zengrong.net/post/delete-some-methods-about-long-in-lua-bytearray/"},{"categories":["technology"],"content":"在 quick-cocos2d-x 中使用 DragonBonesCPP\nUsing DragonBonesCPP in quick-cocos2d-x.\n2014-07-31更新：三件事已经全部搞定，DragonBonesCPP已经推送到了quick-cocos2d-x 官方库。\n1 前言 在 DragonBones 官方C++版本 for cocos2d-x 这篇文章中，我已经简单地介绍过了 DragonBonesCPP 这套用于取代 CCArmature 的库。\n在我自己修改的 quick-cocos2d-x 版本中，我已经把 CCArmature 库删除，完全使用 DragonBonesCPP 。\n我们的产品也完全使用 Flash 和 DragonBonesCPP 来制作骨骼动画。所有AS3版本的DragonBones提供的功能，在CPP版本中都能完整地实现。\n几个月之前，我就已经将 DragonBonesCPP 整合到了 quick-cocos2d-x 中，只是一直没有向官方库推送PR。这也是因为有几个内存泄露的BUG还没有解决。\n现在，我终于有时间可以来做这件事。我的计划如下：\n写一篇关于 DragonBonesCPP 的教程（quick专用哦）； 解决 DragonBonesCPP 的遗留问题，并进一步封装lua api方便使用； 将 DragonBonesCPP 推送到quick的官方仓库。 那么，这篇文章就是第一件事了。\n2 Samples and Codes 本文用到的所有 范例文件 在这里提供。性急的同学可以直接下载代码研究。\nDragonBonesCPP 在quick中的内容，包含在这样几个路径下：\nsamples/dragonbones 范例项目 extensions/DragonBones C++库支持 luabinding/extensions/DragonBones lua绑定 CCDragonBonesExtend framework Extend支持 display.newDragonBones framework display支持 在下一步之前，还需要一些必要的资源。\nDragonBones samples 这是DragonBones提供的一些FLA文件源文件，你可以自行修改他们； DragonBones Design Panel 这是一个Flash插件，支持Flash CS5~Flash CC 。你需要首先安装它。本文使用的是 2.4.1 版； 这些资源可以在这里下载：http://dragonbones.github.io/download.html\n本文讲解的3个范例都在这里：samples/dragonbones ，请提前下载。\n这3个范例使用的都是Dragon这个动画，就是那只可爱的绿色小龙啦！\n在 DragonBones Design Panel 中，可以输出多种文件格式。但是在 DragonBonesCPP 中，目前仅能支持 PNG+XML 这种格式。\n如果选择 PNG+XML 格式输出，每个骨骼动画会包含三个文件：\nskeleton.xml 骨骼数据以及动画数据 texture.png 纹理素材，采用碎图拼接而成 texture.xml 碎图拼接的数据文件，和TexturePacker生成的plist的作用相同 3 显示 打开 samples/dragonbones/scripts/demos/DragonDemoEntry.lua 这个文件，让我们看看如何显示一个骨骼动画。\n这个范例的效果是这样的：\n下面的 _createDB() 方法做了两件事，一是显示一个骨骼动画，二是将这个骨骼动画中的所有动作名称提取出来，加入到 _ANIMATION_LIST 列表中。\n1function DragonDemoEntry:_createDB() 2\tself._db = dragonbones.new({ 3\tskeleton=\u0026#34;dragon/skeleton.xml\u0026#34;, 4\ttexture=\u0026#34;dragon/texture.xml\u0026#34;, 5\tdragonBonesName=\u0026#34;Dragon\u0026#34;, 6\tarmatureName=\u0026#34;Dragon\u0026#34;, 7\taniName=\u0026#34;\u0026#34;, 8\t}) 9\t:addTo(self, 10) 10\t:pos(display.cx,100) 11\t:addMovementScriptListener(handler(self, self._onMovement)) 12\tlocal aniList = self._db:getAnimationList() 13\tfor i=0,aniList:count()-1 do 14\t_ANIMATION_LIST[#_ANIMATION_LIST+1] = aniList:objectAtIndex(i):getCString() 15\tend 16end dragonbones.new 方法封装在 dragonbones 库中。它需要1个table格式的参数：\nskeleton 骨骼数据的XML文件路径； texture 素材数据的XML文件地址； armatureName 骨骼名称。一个dragonbones文件可以包含多个骨骼，这里一般指定主骨骼； skeletonName skeleton.xml的根元素的name值，查看skeleton.xml即可看到，一般与FLA文件的文件名相同； aniName 播放这个名称指定的动画，空字符串代表不播放动画。 返回的对象是 CCDragonBones 的实例，它继承自 CCNode ，我在framework中使用了 CCDragonBonesExtend 来增加它的功能。在这个例子中，它被保存到 self._db 对象。\n貌似quick的framework关于扩展功能这部分做了修改和整合，那么在我将DragonBonesCPP提交到quick的时候，这个类的名称可能会改变。\ngetAnimationList() 返回的是一个 CCArray 对象，其中每个项是 CCString 对象。for循环将其中的字符串提出并假如到 lua table 中。\n看看 addMovementScriptListener 的定义：\n1function CCDragonBonesExtend:addMovementScriptListener(listener) 2\tself:removeMovementScriptListener() 3\tself:addScriptListener(CCDragonBonesExtend.EVENTS.START, listener) 4\tself:addScriptListener(CCDragonBonesExtend.EVENTS.COMPLETE, listener) 5\tself:addScriptListener(CCDragonBonesExtend.EVENTS.LOOP_COMPLETE, listener) 6\treturn self 7end 这个方法其实是注册了三个事件，这三个事件分别在动画开始播放、播放完成和循环播放完成的时候调用。\n让我们看看在点击 Change Animation 菜单的时候会发生什么：\n1function DragonDemoEntry:_onChangeAnimation() 2\t_aniIndex = _aniIndex + 1 3\tif _aniIndex \u0026gt; #_ANIMATION_LIST then 4\t_aniIndex = _aniIndex - #_ANIMATION_LIST 5\tend 6 7\tself._db:gotoAndPlay(_ANIMATION_LIST[_aniIndex]) 8end Dragon这个小龙的动作一共有4个：walk,stand,jump,fall。上面的代码做的事情就是将它们循环播放。\n要停止或者播放动画，可以使用下面的代码：\n1self._db:getAnimation():stop() 2self._db:getAnimation():play() 4 换装 打开 samples/dragonbones/scripts/demos/DragonSwitchClothes.lua 这个文件，让我们看看如何实现一个简单的换装。\n这个范例的效果是这样的：\n在文件开头，我定义了一个4个元素的table，用来保存每个不同服装的纹理：\n1local _TEXTURES = { 2\t\u0026#34;parts/clothes1\u0026#34;, 3\t\u0026#34;parts/clothes2\u0026#34;, 4\t\u0026#34;parts/clothes3\u0026#34;, 5\t\u0026#34;parts/clothes4\u0026#34;, 6} 这些纹理的名称，是根据FLA文件的库结构自动生成的，我们可以打开texture.xml这个文件，就能看到这个DragonBones的所有纹理。\n在 _onSwitchClothes 事件处理函数中，不断循环调用不同的纹理实现换装。\n1function DragonSwitchClothes:_onSwitchClothes() 2\t_textureIndex = _textureIndex + 1 3\tif _textureIndex \u0026gt; #_TEXTURES then 4\t_textureIndex = _textureIndex - #_TEXTURES 5\tend 6 7 self._db:setBoneTexture(\u0026#34;clothes\u0026#34;, _TEXTURES[_textureIndex], \u0026#34;Dragon\u0026#34;); 8end setBoneTexture 是为了方便换装而封装在 CCDragonBones 中的一个函数。它的内容如下：\n1void CCDragonBones::setBoneTexture(const char* boneName, const char* textureName, const char* textureAtlasName) 2{ 3 4\tCocos2dxFactory* fac = Cocos2dxFactory::getInstance(); 5\tObject* clothesObj = fac-\u0026gt;getTextureDisplay(textureName, textureAtlasName); 6 7\t//CCLOG(\u0026#34;CLOSE %d\u0026#34;, clothesObj); 8 9\tBone* bone = getArmature()-\u0026gt;getBone(boneName); 10\tCocosNode* oldClothesObj = static_cast\u0026lt;CocosNode*\u0026gt;(bone-\u0026gt;getDisplay()); 11\tbone-\u0026gt;setDisplay(clothesObj); 12} 从上面我们可以看出，换装的方法就是根据提供的纹理新建一个对象，然后找到要切换纹理的骨骼，将这个骨骼的显示部分替换成这个纹理对象。\n在DragonBones中，有很多方式可以实现换装。因为一个骨骼不一定是一个单独的纹理，还可能是一个逐帧动画对象。这里描述的换装方法，只能把骨骼替换成单个纹理。\n以后我会专门撰文描述其它的换装方式。\n这个例子其实并不只是说明了换装一种用法，还有如何让动画移动等等。请参考相关代码自行理解。\n5 追鸟 打开 samples/dragonbones/scripts/demos/DragonChaseStarling.lua 这个文件，让我们看看Dragon如何孽待一只叫做Starling的小八哥。\n这个例子中的小鸟来自基于Flash Stage3D技术的2D引擎 Starling ，这个引擎也算是cocos2d-x的竞争对手吧 :)\n这个范例的效果是这样的：\n一个月黑风高的晚上，许多乌鸦在背景的天空中飞。一直红色的小鸟正在挑逗一只胸饿的小龙（请自行脑补……），小龙带着呆萌的眼神挥舞那凶猛的爪子准备抓住小鸟然后OOXX……从此它们过上了幸福的生活……\n哦，请原谅我走神。看起来这动人的一幕是这样的：\n在这个例子中，因为要控制小龙的头部、两只爪子根据小鸟的位置而移动，我们需要实现得到这三个部位的骨骼引用：\n1_head = self._db:getArmature():getBone(\u0026#34;head\u0026#34;) 2_armR = self._db:getArmature():getBone(\u0026#34;armUpperR\u0026#34;) 3_armL = self._db:getArmature():getBone(\u0026#34;armUpperL\u0026#34;) 小鸟就是一个CCSprite而已，小龙和上面的创建一样，这里就不贴出代码了：\n1self._starlingBird = display.newSprite(\u0026#34;starling.png\u0026#34;) 2\t:pos(display.left + 20, display.cy) 3\t:addTo(self, 10) 当触摸屏幕的时候，需要开启一个计时器来更新。后面所有的更新都在计时器 self._update 中进行。\n在触摸屏幕并移动的时候，实时更新小鸟的坐标，并记录当前触摸的坐标以供在 self._update 的时候计算骨骼的旋转角度。\n1function DragonChaseStarling:_onTouch(event,x, y, px, py) 2\tif event == \u0026#34;began\u0026#34; then 3\tif not _isChasing then 4\tself:scheduleUpdate(handler(self, self._update)); 5\t_isChasing = true 6\tend 7\treturn true 8\tend 9\tif event == \u0026#34;moved\u0026#34; then 10\tself:_updatePosition(x, y) 11\telseif event == \u0026#34;ended\u0026#34; then 12\tend 13end 14 15function DragonChaseStarling:_updatePosition(x, y) 16 _touchX = x 17 _touchY = y 18 self._starlingBird:pos(x,y); 19end 更新骨骼角度的代码在 self._updateBones 方法中。\n下面的代码计算当前触摸点与小龙的中心点的角度，然后分别设置三个骨骼（头部、左爪，右爪）以及小鸟的旋转。\n需要注意几点：\nDragonBones的中心点在脚底中心，因此身体中心要加上高度的一半； 每个骨骼的起始角度以及可偏转极限不同，因此使用不同的参数与 _r 相乘； CCDragonBones只是一个CCNode，默认是没有ContentSize的，因此必须先使用 setContentSize 设置尺寸。 1function DragonChaseStarling:_updateBones() 2\tlocal dbsize = self._db:getContentSize() 3 _r = math.pi + math.atan2(self._db:getPositionY() + 4\tdbsize.height / 2-_touchY , 5\t_touchX - self._db:getPositionX()) 6 if _r \u0026gt; math.pi then 7 _r = _r - math.pi * 2; 8\tend 9 10 _head.offset:setRotation(_r*0.3); 11 _armR.offset:setRotation(_r*0.8); 12\t_armL.offset:setRotation(_r*1.5); 13 self._starlingBird:setRotation(_r*0.2*(180/math.pi)); 14end 其它的关于小龙的移动和超范围判断，看源码即可。\n6 更多 这三个例子只是展示了DragonBones的很小一部分功能。更多的例子和使用方法，请访问 DragonBones官方网站 下载。\n当然，官方网站的例子都是基于 ActionScript3 的。\n","date":"2014-07-08","description":"","lastmod":"2014-07-08T10:01:20Z","slug":"using_ccarmature_in_cocos2d-x","tags":["cpp","cocos2d-x","dragonbones","lua","skeletalanimation"],"title":"在 quick-cocos2d-x 中使用 DragonBonesCPP","url":"https://blog.zengrong.net/post/using_ccarmature_in_cocos2d-x/"},{"categories":["use"],"content":"项目版本描述规则\nVersion definition\n1. 概述 团队内所有产品和项目的版本号均遵循此规则。\n2. 原则 基于 Tom Preston-Werner 的 语义化版本2.0.0 构建本规则。\n版本实现内外统一，方便宣传、开发、运营和发音。\n3. 版本号 3.1 版本号组成 版本号由 X.Y.Z-P.C 五个部分组成。详解如下：\n代码 名称 递增条件 递增方式 可用字符 备注 X 主版本号 做了大改动的时候递增 人为递增 [0-9] Y 次版本号 做了一般改动的时候递增 人为递增 [0-9] Z 协议版本号 客户端-服务端协议做了变动的时候递增 机器递增 [0-9] 服务端所有协议版本相加 P 先行版本号 描述产品发布的方式，详见 先行版本 人为递增 [ABR][0-9] 团队内部描述和讨论仅使用此版本号 C 编译版本号 客户端版本编译时候的版本 机器递增 [0-9a-z] 客户端git代码库提交sha码前6位，编译当时确定 3.2 正则表达式表达法（Perl风格） 使用下面的正则拆分版本号各部分\n([0-9]+)\\.([0-9]+)\\.([0-9]+)-([ABR][0-9]+)\\.([0-9a-z]{6})\n3.3 先行版本 公司内部版本Alpha，简称A。版本编号从1开始，顺序递增； 局部开放的测试版本Beta，简称B。版本编号依赖Alpha； 公开发行的版本Release，简称R。版本编号依赖Beta。 3.4 先行版本工作流 所有的功能增加和修改，都是从Alpha版本开始的。每次增加功能，都必须递增先行版本。先行版本必须顺序递增，不得跳过任何数字。\n团队按照Ax版本的计划逐步完成、完善内部版本。在内部对每一个Ax版本进行测试。Ax版本存在的问题，记录存档后，放在Ax+1版本中解决。 根据运营需求，挑选计划中或以完成的Ax版本，封装为Bx版本，进行小范围的、面向运营的测试。Bx版本存在的问题，记录存档后，放在同版本Bx中解决，直至完善。 挑选完善的Bx版本，封装为Rx版本发行。若Rx版本存在问题，回返上一步骤直至解决，然后重新封装为Rx版本号发行。 3.5 版本号制定工作流 策划和开发部门提出增加或者修改功能的要求； 运营部门制定先行版本号P； 开发部门制定主次版本号X.Y； 编译包时自动生成协议协议版本号Z和编译版本号C。 4. 范例 发布日期 发布用途 版本号 1024年12月25日 内部测试演示 0.1.100-A1.679ABF 2048年1月6日 内部测试演示 0.1.120-A2.5114f8 2048年1月31日 小范围外部测试 0.2.160-A3.5617ae 2048年X月X日 玩家小范围外部测试 0.3.100-B4.5617ae ... ... ... 2048年X月X日 公测或封测 0.9.301-B15.5617ae 2048年XX月XX-1日 1服前的最后1个Beta版本 0.9.356-B29.5617ae 4096年XX月XX日 1服 1.0.356-R29.5617ae 5. 使用 团队内部会议中，仅使用先行版本号。\n例如：A3版，B30版 在游戏界面中，显示完整版本号； 对外宣传时，一般使用 X.Y.Z 版本号；如有必要，可加入先行版本号。\n例如： 1.0.130 ， 1.0.356-R29 ","date":"2014-07-05","description":"","lastmod":"2014-07-05T02:57:28Z","slug":"version_definition","tags":["regexp"],"title":"项目版本描述规则","url":"https://blog.zengrong.net/post/version_definition/"},{"categories":["technology"],"content":"quick-cocos2d-x的热更新机制实现\n0 依赖 这里说的热更新，指的是客户端的更新。\n大致的流程是，客户端在启动后访问更新api，根据更新api的反馈，下载更新资源，然后使用新的资源启动客户端，或者直接使用新资源不重启客户端。\n这种方式可以跳过AppStore的审核，避免了用户频繁下载、安装、覆盖产品包。\n我们一般使用这种方式快速修复产品BUG和增加新功能。\n本文基于 quick-cocos2d-x zrong 修改版 。\n1 前言 1.1 他山之石 在实现这个机制之前，我研究了这几篇文章：\nquick-cocos2d-x基于源码加密打包功能的更新策略 by SunLightJuly 看到有同学在研究在线更新，希望我能帮到你一些 by Henry 基于Quick-cocos2dx 2.2.3 的动态更新实现完整篇 by 西门大官人 另外，我也查看了 AssetsManager 的源码和 sample 。\n不幸的是，这几个方案我都不能直接拿来用。因此思考再三，还是自己写了一套方案。\n==重要提醒==\n这篇文章很长，但我不愿意将其分成多个部分。这本来就是一件事，分开的话有种开房时洗完澡妹子却说两个小时后才能来。这中间干点啥呢？\n所以，如果你不能坚持两个小时（能么？不能？），或者你的持久度不能坚持到把这篇文章看完（大概要10~30分钟吧），那还是不要往下看的比较好。\n当然，你也可能坚挺了30分钟之后才发现妹子是凤姐，不要怪我这30分钟里面没开灯哦……\n1.2 为什么要重复造轮子 上面的几个方案侧重于尽量简化用户（使用此方案的程序员）的操作，而简化带来的副作用就是会损失一些灵活性。\n正如 Roberto Ierusalimschy 在 Lua程序设计(第2版) 第15章开头所说：\n通常，Lua不会设置规则（policy）。相反，Lua会提供许多强有力的机制来使开发者有能力实现出最适合的规则。\n我认为更新模块也不应该设置规则，而是尽可能提供一些机制来满足程序员的需要。这些机制并不是我发明的，而是Lua和quick本来就提供的。让程序员自己实现自己的升级系统，一定比我这个无证野路子的方法更好.\n因此，本文中讲述的并非是一套通用的机制，而是我根据上面说到的这些机制实现的一套适合自己的方法。当然你可以直接拿去用，但要记住：\n用的好，请告诉你的朋友。 出了问题，请告诉别找我。 1.3 需求的复杂性 热更新有许多的必要条件，每个产品的需求可能都不太相同。\n例如，每个产品的版本号设计都不太相同，有的有大版本、小版本；有的则有主版本、次版本、编译版本。我以前的习惯，是在主版本变化的时候需要整包更新，而次版本变化代表逻辑更新，编译版本代表资源更新等等。这些需要自己来定义升级规则。\n再例如，有的产品希望逐个下载升级包，有的产品希望把所有资源打包成一个升级包；有的产品直接使用文件名作为资源名在游戏中调用，而有的产品会把资源名改为指纹码（例如MD5）形式来实现升级的多版本共存和实时回滚，还有的产品甚至要求能在用户玩游戏的过程中完成自动更新。\nAssetsManager 那套机制就太死板，在真实的产品中不修改很难使用。\n而我也不建议使用 CCUserDefault 这种东西——在Lua的世界里，为什么要用XML做配置文件？\n如果抽象出我的需求，其实只有1点：\n能更新一切\n这个说的有点大了，准确的说，应该是 能更新一切Lua代码与资源 。\n如果你的整个游戏都是Lua写的（对于quick的项目来说应该是这样），其实也就是更新一切。\n1.4 版本号设计 关于上面 需求的复杂性 中提到的版本号的问题，可以参考一下这篇文章：语义化版本2.0.0 。\n我基于语义化版本设计了一套规则在团队内部使用：项目版本描述规则 。\n在这里，我尽量详细地阐述我的思路和做法，抛砖引玉吧。\n2 特色 基本的热更新功能就不说了大家都有。我这套机制还有如下几个特色：\n2.1 可以更新 frameworks_precompiled.zip 模块 为了行文方便，后面会把 frameworks_precompiled.zip 简称为 framework 。\nframeworks 模块是 quick 的核心模块，在quick 生成的项目中，它直接在 AppDelegate.cpp 中载入 main.lua 之前进行载入。如下：\n1bool AppDelegate::applicationDidFinishLaunching() 2{ 3 // initialize director 4 CCDirector *pDirector = CCDirector::sharedDirector(); 5 pDirector-\u0026gt;setOpenGLView(CCEGLView::sharedOpenGLView()); 6 pDirector-\u0026gt;setProjection(kCCDirectorProjection2D); 7 8 // set FPS. the default value is 1.0/60 if you don\u0026#39;t call this 9 pDirector-\u0026gt;setAnimationInterval(1.0 / 60); 10 11 // register lua engine 12 CCLuaEngine *pEngine = CCLuaEngine::defaultEngine(); 13 CCScriptEngineManager::sharedManager()-\u0026gt;setScriptEngine(pEngine); 14 15 CCLuaStack *pStack = pEngine-\u0026gt;getLuaStack(); 16 17#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) 18 // load framework 19 pStack-\u0026gt;loadChunksFromZIP(\u0026#34;res/framework_precompiled.zip\u0026#34;); 20 21 // set script path 22 string path = CCFileUtils::sharedFileUtils()-\u0026gt;fullPathForFilename(\u0026#34;scripts/main.lua\u0026#34;); 23\t...... 这可以说明这个核心模块对quick的重要性。正因为它重要，所以必须要能更新它。\n2.2 可以更新 update 模块自身 更新功能是客户端启动后载入的第一个lua模块，它负责载入更新资源，以及启动主项目。一般情况下，这个模块是不需要改动的。对它进行改动，既不科学，也不安全（安全啊……）。\n但是万一呢？大家知道策划和运营同学都是二班的，或许哪天就有二班同学找你说：改改怕什么？又不会怀孕…… 所以这个必须有。\n2.3 纯lua实现 把这个拿出来说纯粹是撑数的。不凑个三大特色怎么好意思？\n上面SunLightJuly和Henry同学的方案当然也是纯lua的。用quick你不搞纯lua好意思出来混？小心廖大一眼瞪死你。\n当然，我这个不是纯lua的，我基于 AssetsManager(C++) 的代码实现了一个 Updater 模块。\n而且，我还改了 AppDelegate 中的启动代码。\n所以，你看，我不仅是撑数，还是忽悠。\n3 Updater(C++) AssetsManager 中提供了下载资源，访问更新列表，解压zip包，删除临时文件，设置搜索路径等等一系列的功能。但它的使用方式相当死板，我只能传递一个获取版本号的地址，一个zip包的地址，一个临时文件夹路径，然后就干等着。期间啥也干不了。\n当然，我可以通过 quick 为其增加的 registerScriptHandler 方法让lua得知下载进度和网络状态等等。但下载进度的数字居然以事件名的方式通过字符串传过来的！这个就太匪夷所思了点。\n于是，我对这个 AssetsManager 进行了修改。因为修改的东西实在太多，改完后就不好意思再叫这个名字了（其实主要是现在的名字比较短 XD）。我们只需要记住这个 Updater 是使用 AssetsManager 修改的即可。\n在上面SunLightJuly和Henry同学的方法中，使用的是 CCHTTPRequest 来获取网络资源的。CCHTTPRequest 封装了cURL 操作。而在 Updater 中，是直接封装的 cURL 操作。\n在我的设计中，逻辑应该尽量放在lua中，C++部分只提供功能供lua调用。因为lua可以进行热更新，而C++部分则只能整包更新。\nUpdater 主要实现的内容如下：\n3.1 删除了不需要的方法 get和set相关的一堆方法都被删除了。new对象的时候也不必传递参数了。\n3.2 增加 getUpdateInfo 方法 这个方法通过HTTP协议获取升级列表数据，获取到的数据直接返回，C++并不做处理。\n3.3 修改 update 方法 这个方法通过HTTP协议下载升级包，需要提供四个参数：\nzip文件的url； zip文件的保存位置； zip 文件的解压临时目录； 解压之前是否需要清空临时目录。 3.4 修改事件类型 我把把传递给lua的事件分成了四种类型：\n3.4.1 UPDATER_MESSAGE_UPDATE_SUCCEED\n事件名为 success，代表更新成功，zip文件下载并解压完毕；\n3.4.2 UPDATER_MESSAGE_STATE\n事件名为 state，更新过程中的状态（下载开始、结束，解压开始、结束）也传递给了lua。这个方法是这样实现的：\n1void Updater::Helper::handlerState(Message *msg) 2{ 3 StateMessage* stateMsg = (StateMessage*)msg-\u0026gt;obj; 4 if(stateMsg-\u0026gt;manager-\u0026gt;_delegate) 5 { 6 stateMsg-\u0026gt;manager-\u0026gt;_delegate-\u0026gt;onState(stateMsg-\u0026gt;code); 7 } 8 if (stateMsg-\u0026gt;manager-\u0026gt;_scriptHandler) 9 { 10 std::string stateMessage; 11 switch ((StateCode)stateMsg-\u0026gt;code) 12 { 13 case kDownStart: 14 stateMessage = \u0026#34;downloadStart\u0026#34;; 15 break; 16 17 case kDownDone: 18 stateMessage = \u0026#34;downloadDone\u0026#34;; 19 break; 20 21 case kUncompressStart: 22 stateMessage = \u0026#34;uncompressStart\u0026#34;; 23 break; 24 case kUncompressDone: 25 stateMessage = \u0026#34;uncompressDone\u0026#34;; 26 break; 27 28 default: 29 stateMessage = \u0026#34;stateUnknown\u0026#34;; 30 } 31 32 CCScriptEngineManager::sharedManager() 33 -\u0026gt;getScriptEngine() 34 -\u0026gt;executeEvent( 35 stateMsg-\u0026gt;manager-\u0026gt;_scriptHandler, 36 \u0026#34;state\u0026#34;, 37 CCString::create(stateMessage.c_str()), 38 \u0026#34;CCString\u0026#34;); 39 } 40 41 delete ((StateMessage*)msg-\u0026gt;obj); 42} 3.4.3 UPDATER_MESSAGE_PROGRESS\n事件名为 progress，传递的对象为一个 CCInteger ，代表进度。详细的实现可以看 源码。\n3.4.4 UPDATER_MESSAGE_ERROR\n事件名为 error，传递的对象是一个 CCString，值有这样几个：\nerrorCreateFile errorNetwork errorNoNewVersion errorUncompress errorUnknown 方法的实现和上面的 UPDATER_MESSAGE_STATE 类似，这里就不贴了。详细的实现可以看 源码。\nUpdater(C++) 部分只做了这些苦力工作，而具体的分析逻辑（分析getUserInfo返回的数据决定是否升级、如何升级和升级什么），下载命令的发出（调用update方法），解压成功之后的操作（比如合并新文件到就文件中，更新文件索引列表等等），全部需要lua来做。下面是一个处理Updater(C++)事件的lua函数的例子。\n1function us._updateHandler(event, value) 2\tupdater.state = event 3\tif event == \u0026#34;success\u0026#34; then 4\tupdater.stateValue = value:getCString() 5\t-- 成功之后更新资源列表，合并新资源 6\tupdater.updateFinalResInfo() 7\t-- 调用成功后的处理函数 8\tif us._succHandler then 9\tus._succHandler() 10\tend 11\telseif event == \u0026#34;error\u0026#34; then 12\tupdater.stateValue = value:getCString() 13\telseif event == \u0026#34;progress\u0026#34; then 14\tupdater.stateValue = tostring(value:getValue()) 15\telseif event == \u0026#34;state\u0026#34; then 16\tupdater.stateValue = value:getCString() 17\tend 18\t-- us._label 是一个CCLabelTTF，用来显示进度和状态 19\tus._label:setString(updater.stateValue) 20\tassert(event ~= \u0026#34;error\u0026#34;, 21\tstring.format(\u0026#34;Update error: %s !\u0026#34;, updater.stateValue)) 22end 23 24updater:registerScriptHandler(us._updateHandler) 4. update包（lua） update包是整个项目的入口包，quick会首先载入这个包，甚至在 framework 之前。\n4.1 为update包所做的项目修改 我修改了quick项目文件 AppDelegate.cpp 中的 applicationDidFinishLaunching 方法，使其变成这样：\n1bool AppDelegate::applicationDidFinishLaunching() 2{ 3 // initialize director 4 CCDirector *pDirector = CCDirector::sharedDirector(); 5 pDirector-\u0026gt;setOpenGLView(CCEGLView::sharedOpenGLView()); 6 pDirector-\u0026gt;setProjection(kCCDirectorProjection2D); 7 8 // set FPS. the default value is 1.0/60 if you don\u0026#39;t call this 9 pDirector-\u0026gt;setAnimationInterval(1.0 / 60); 10 11 // register lua engine 12 CCLuaEngine *pEngine = CCLuaEngine::defaultEngine(); 13 CCScriptEngineManager::sharedManager()-\u0026gt;setScriptEngine(pEngine); 14 15 CCLuaStack *pStack = pEngine-\u0026gt;getLuaStack(); 16 17 string gtrackback = \u0026#34;\\ 18 function __G__TRACKBACK__(errorMessage) \\ 19 print(\\\u0026#34;----------------------------------------\\\u0026#34;) \\ 20 print(\\\u0026#34;LUA ERROR: \\\u0026#34; .. tostring(errorMessage) .. \\\u0026#34;\\\\n\\\u0026#34;) \\ 21 print(debug.traceback(\\\u0026#34;\\\u0026#34;, 2)) \\ 22 print(\\\u0026#34;----------------------------------------\\\u0026#34;) \\ 23 end\u0026#34;; 24 pEngine-\u0026gt;executeString(gtrackback.c_str()); 25 26 // load update framework 27 pStack-\u0026gt;loadChunksFromZIP(\u0026#34;res/lib/update.zip\u0026#34;); 28 29 string start_path = \u0026#34;require(\\\u0026#34;update.UpdateApp\\\u0026#34;).new(\\\u0026#34;update\\\u0026#34;):run(true)\u0026#34;; 30 CCLOG(\u0026#34;------------------------------------------------\u0026#34;); 31 CCLOG(\u0026#34;EXECUTE LUA STRING: %s\u0026#34;, start_path.c_str()); 32 CCLOG(\u0026#34;------------------------------------------------\u0026#34;); 33 pEngine-\u0026gt;executeString(start_path.c_str()); 34 35 return true; 36} 原来位于 main.lua 中的 __G_TRACKBACK__ 函数（用于输出lua报错信息）直接包含在C++代码中了。那么现在 main.lua 就不再需要了。\n同样的，第一个载入的模块变成了 res/lib/update.zip，这个zip也可以放在quick能找到的其它路径中，使用这个路径只是我的个人习惯。\n最后，LuaStack直接执行了下面这句代码启动了 update.UpdateApp 模块：\n1require(\u0026#34;update.UpdateApp\u0026#34;).new(\u0026#34;update\u0026#34;):run(true); 4.2 update包中的模块 update包有三个子模块，每个模块是一个lua文件，分别为：\nupdate.UpdateApp 检测更新，决定启动哪个模块。 update.updater 负责真正的更新工作，与C++通信，下载、解压、复制。 update.updateScene 负责在更新过程中显示界面，进度条等等。 对于不同的大小写，是因为在我的命名规则中，类用大写开头，对象是小写开头。 update.UpdateApp 是一个类，其它两个是对象(table)。\n下面的 4.3、4.4、4.5 将分别对这3个模块进行详细介绍。\n4.3 update.UpdateApp 下面是入口模块 UpdateApp 的内容：\n1-- @author zrong(zengrong.net) 2-- Creation 2014-07-03 3 4local UpdateApp = {} 5 6UpdateApp.__cname = \u0026#34;UpdateApp\u0026#34; 7UpdateApp.__index = UpdateApp 8UpdateApp.__ctype = 2 9 10local sharedDirector = CCDirector:sharedDirector() 11local sharedFileUtils = CCFileUtils:sharedFileUtils() 12local updater = require(\u0026#34;update.updater\u0026#34;) 13 14function UpdateApp.new(...) 15\tlocal instance = setmetatable({}, UpdateApp) 16\tinstance.class = UpdateApp 17\tinstance:ctor(...) 18\treturn instance 19end 20 21function UpdateApp:ctor(appName, packageRoot) 22 self.name = appName 23 self.packageRoot = packageRoot or appName 24 25\tprint(string.format(\u0026#34;UpdateApp.ctor, appName:%s, packageRoot:%s\u0026#34;, appName, packageRoot)) 26 27 -- set global app 28 _G[self.name] = self 29end 30 31function UpdateApp:run(checkNewUpdatePackage) 32\t--print(\u0026#34;I am new update package\u0026#34;) 33\tlocal newUpdatePackage = updater.hasNewUpdatePackage() 34\tprint(string.format(\u0026#34;UpdateApp.run(%s), newUpdatePackage:%s\u0026#34;, 35\tcheckNewUpdatePackage, newUpdatePackage)) 36\tif checkNewUpdatePackage and newUpdatePackage then 37\tself:updateSelf(newUpdatePackage) 38\telseif updater.checkUpdate() then 39\tself:runUpdateScene(function() 40\t_G[\u0026#34;finalRes\u0026#34;] = updater.getResCopy() 41\tself:runRootScene() 42\tend) 43\telse 44\t_G[\u0026#34;finalRes\u0026#34;] = updater.getResCopy() 45\tself:runRootScene() 46\tend 47end 48 49-- Remove update package, load new update package and run it. 50function UpdateApp:updateSelf(newUpdatePackage) 51\tprint(\u0026#34;UpdateApp.updateSelf \u0026#34;, newUpdatePackage) 52\tlocal updatePackage = { 53\t\u0026#34;update.UpdateApp\u0026#34;, 54\t\u0026#34;update.updater\u0026#34;, 55\t\u0026#34;update.updateScene\u0026#34;, 56\t} 57\tself:_printPackages(\u0026#34;--before clean\u0026#34;) 58\tfor __,v in ipairs(updatePackage) do 59\tpackage.preload[v] = nil 60\tpackage.loaded[v] = nil 61\tend 62\tself:_printPackages(\u0026#34;--after clean\u0026#34;) 63\t_G[\u0026#34;update\u0026#34;] = nil 64\tCCLuaLoadChunksFromZIP(newUpdatePackage) 65\tself:_printPackages(\u0026#34;--after CCLuaLoadChunksForZIP\u0026#34;) 66 require(\u0026#34;update.UpdateApp\u0026#34;).new(\u0026#34;update\u0026#34;):run(false) 67\tself:_printPackages(\u0026#34;--after require and run\u0026#34;) 68end 69 70-- Show a scene for update. 71function UpdateApp:runUpdateScene(handler) 72\tself:enterScene(require(\u0026#34;update.updateScene\u0026#34;).addListener(handler)) 73end 74 75-- Load all of packages(except update package, it is not in finalRes.lib) 76-- and run root app. 77function UpdateApp:runRootScene() 78\tfor __, v in pairs(finalRes.lib) do 79\tprint(\u0026#34;runRootScene:CCLuaLoadChunksFromZip\u0026#34;,__, v) 80\tCCLuaLoadChunksFromZIP(v) 81\tend 82\t83\trequire(\u0026#34;root.RootScene\u0026#34;).new(\u0026#34;root\u0026#34;):run() 84end 85 86function UpdateApp:_printPackages(label) 87\tlabel = label or \u0026#34;\u0026#34; 88\tprint(\u0026#34;\\npring packages \u0026#34;..label..\u0026#34;------------------\u0026#34;) 89\tfor __k, __v in pairs(package.preload) do 90\tprint(\u0026#34;package.preload:\u0026#34;, __k, __v) 91\tend 92\tfor __k, __v in pairs(package.loaded) do 93\tprint(\u0026#34;package.loaded:\u0026#34;, __k, __v) 94\tend 95\tprint(\u0026#34;print packages \u0026#34;..label..\u0026#34;------------------\\n\u0026#34;) 96end 97 98 99function UpdateApp:exit() 100 sharedDirector:endToLua() 101 os.exit() 102end 103 104function UpdateApp:enterScene(__scene) 105 if sharedDirector:getRunningScene() then 106 sharedDirector:replaceScene(__scene) 107 else 108 sharedDirector:runWithScene(__scene) 109 end 110end 111 112return UpdateApp 我来说几个重点。\n4.3.1 没有framework\n由于没有加载 framework，class当然是不能用的。所有quick framework 提供的方法都不能使用。\n我借用class中的一些代码来实现 UpdateApp 的继承。其实我觉得这个UpdateApp也可以不必写成class的。\n4.3.2 入口函数 update.UpdateApp:run(checkNewUpdatePackage)\nrun 是入口函数，同时接受一个参数，这个参数用于判断是否要检测本地有新的 update.zip 模块。\n是的，run 就是那个在 AppDelegate.cpp 中第一个调用的lua函数。\n这个函数接受一个参数 checkNewUpdatePackage ，在C++调用 run 的时候，传递的值是 true 。\n如果这个值为真，则会检测本地是否拥有新的更新模块，这个检测通过 update.updater.hasNewUpdatePackage() 方法进行，后面会说到这个方法。\n本地有更新的 update 模块，则直接调用 updateSelf 来更新 update 模块自身；若无则检测是否有项目更新，下载更新的资源，解析它们，处理它们，然后启动主项目。这些工作通过 update.updater.checkUpdate() 完成，后面会说到这个方法。\n若没有任何内容需要更新，则直接调用 runRootScene 来显示主场景了。这后面的内容就交给住场景去做了，update 模块退出历史舞台。\n从上面这个流程可以看出。在更新完成之前，主要的项目代码和资源没有进行任何载入。这也就大致达到了我们 更新一切 的需求。因为所有的东西都没有载入，也就不存在更新。只需要保证我载入的内容是最新的就行了。\n因此，只要保证 update 模块能更新，就达到我们最开始的目标了。\n这个流程还可以保证，如果没有更新，甚至根本就不需要载入 update 模块的场景界面，直接跳转到游戏的主场景即可。\n有句代码在 run 函数中至关重要：\n1_G[\u0026#34;finalRes\u0026#34;] = updater.getResCopy() finalRes 这个全局变量保存了本地所有的 原始/更新 资源索引。它是一个嵌套的tabel，保存的是所有资源的名称以及它们对应的 绝对/相对 路径的对应关系。后面会详述。\n4.3.3 更新自身 update.UpdateApp:updateSelf(newUpdatePackage)\n这是本套机制中最重要的一环。理解了它，你就知道更新一切其实没什么秘密。Lua本来就提供了这样一套机制。\n由于在 C++ 中已经将 update 模块载入了内存，那么要更新自身首先要做的是清除 Lua 的载入标记。\nLua在两个全局变量中做了标记：\npackage.preload 执行 CCLuaLoadChunksFromZIP 之后会将模块缓存在这里作为 require 的加载器； package.loaded 执行 require 的时候会先查询 package.loaded，若没有则会查询 package.preload 得到加载器，利用加载器加载模块，再将加载的模块缓存到 package.loaded 中。 详细的机制可以阅读 Lua程序设计(第2版) 15.1 require 函数。\n那么，要更新自己，只需要把 package.preload 和 package.loaded 清除，然后再用新的 模块填充 package.preload 即可。下面就是核心代码了：\n1local updatePackage = { 2\t\u0026#34;update.UpdateApp\u0026#34;, 3\t\u0026#34;update.updater\u0026#34;, 4\t\u0026#34;update.updateScene\u0026#34;, 5} 6for __,v in ipairs(updatePackage) do 7\tpackage.preload[v] = nil 8\tpackage.loaded[v] = nil 9end 10_G[\u0026#34;update\u0026#34;] = nil 11CCLuaLoadChunksFromZIP(newUpdatePackage) 12require(\u0026#34;update.UpdateApp\u0026#34;).new(\u0026#34;update\u0026#34;):run(false) 如果不相信这么简单，可以用上面完整的 UpdateApp 模块中提供的 UpdateApp:_printPackages(label) 方法来检测。\n4.3.4 显示更新界面 update.UpdateApp:runUpdateScene(handler)\nupdate.updater.checkUpdate() 的返回是异步的，下载和解压都需要时间，在这段时间里面，我们需要一个界面。runUpdateScene 方法的作用就是显示这个界面。并在更新成功之后调用handler处理函数。\n4.3.5 显示主场景 update.UpdateApp:runRootScene()\n到了这里，update 包就没有作用了。但由于我们先前没有载入除 update 包外的任何包，这里必须先载入它们。\n我上面提到过，finalRes 这个全局变量是一个索引表，它的 lib 对象就是一个包含所有待载入的包（类似于 frameworks_precompiled.zip 这种）的列表。我们通过循环将它们载入内存。\n对于 root.RootScene 这个模块来说，就是标准的quick模块了，它可以使用quick中的任何特性。\n1for __, v in pairs(finalRes.lib) do 2\tprint(\u0026#34;runRootScene:CCLuaLoadChunksFromZip\u0026#34;,__, v) 3\tCCLuaLoadChunksFromZIP(v) 4end 5 6require(\u0026#34;root.RootScene\u0026#34;).new(\u0026#34;root\u0026#34;):run() 4.3.6 怎么使用这个模块\n你如果要直接拿来就用，这个模块基本上不需要修改。因为本来它就没什么特别的功能。当然，你可以看完下面两个模块再决定。\n4.4 update.updateScene 这个模块用于显示更新过程的进度和一些信息。所有内容如下：\n1-- updateScene for update package. 2-- This is a object, not a class. 3-- In this scene, it will show download progress bar 4-- and state for uncompress. 5-- @author zrong(zengrong.net) 6-- Creation: 2014-07-03 7 8local updater = require(\u0026#34;update.updater\u0026#34;) 9local sharedDirector = CCDirector:sharedDirector() 10 11-- check device screen size 12local glview = sharedDirector:getOpenGLView() 13local size = glview:getFrameSize() 14local display = {} 15display.sizeInPixels = {width = size.width, height = size.height} 16 17local w = display.sizeInPixels.width 18local h = display.sizeInPixels.height 19 20CONFIG_SCREEN_WIDTH = 1280 21CONFIG_SCREEN_HEIGHT = 800 22CONFIG_SCREEN_AUTOSCALE = \u0026#34;FIXED_HEIGHT\u0026#34; 23 24local scale, scaleX, scaleY 25 26scaleX, scaleY = w / CONFIG_SCREEN_WIDTH, h / CONFIG_SCREEN_HEIGHT 27scale = scaleY 28CONFIG_SCREEN_WIDTH = w / scale 29 30glview:setDesignResolutionSize(CONFIG_SCREEN_WIDTH, CONFIG_SCREEN_HEIGHT, kResolutionNoBorder) 31 32local winSize = sharedDirector:getWinSize() 33display.contentScaleFactor = scale 34display.size = {width = winSize.width, height = winSize.height} 35display.width = display.size.width 36display.height = display.size.height 37display.cx = display.width / 2 38display.cy = display.height / 2 39display.c_left = -display.width / 2 40display.c_right = display.width / 2 41display.c_top = display.height / 2 42display.c_bottom = -display.height / 2 43display.left = 0 44display.right = display.width 45display.top = display.height 46display.bottom = 0 47display.widthInPixels = display.sizeInPixels.width 48display.heightInPixels = display.sizeInPixels.height 49 50print(\u0026#34;# display in updateScene start\u0026#34;) 51print(string.format(\u0026#34;# us.CONFIG_SCREEN_AUTOSCALE = %s\u0026#34;, CONFIG_SCREEN_AUTOSCALE)) 52print(string.format(\u0026#34;# us.CONFIG_SCREEN_WIDTH = %0.2f\u0026#34;, CONFIG_SCREEN_WIDTH)) 53print(string.format(\u0026#34;# us.CONFIG_SCREEN_HEIGHT = %0.2f\u0026#34;, CONFIG_SCREEN_HEIGHT)) 54print(string.format(\u0026#34;# us.display.widthInPixels = %0.2f\u0026#34;, display.widthInPixels)) 55print(string.format(\u0026#34;# us.display.heightInPixels = %0.2f\u0026#34;, display.heightInPixels)) 56print(string.format(\u0026#34;# us.display.contentScaleFactor = %0.2f\u0026#34;, display.contentScaleFactor)) 57print(string.format(\u0026#34;# us.display.width = %0.2f\u0026#34;, display.width)) 58print(string.format(\u0026#34;# us.display.height = %0.2f\u0026#34;, display.height)) 59print(string.format(\u0026#34;# us.display.cx = %0.2f\u0026#34;, display.cx)) 60print(string.format(\u0026#34;# us.display.cy = %0.2f\u0026#34;, display.cy)) 61print(string.format(\u0026#34;# us.display.left = %0.2f\u0026#34;, display.left)) 62print(string.format(\u0026#34;# us.display.right = %0.2f\u0026#34;, display.right)) 63print(string.format(\u0026#34;# us.display.top = %0.2f\u0026#34;, display.top)) 64print(string.format(\u0026#34;# us.display.bottom = %0.2f\u0026#34;, display.bottom)) 65print(string.format(\u0026#34;# us.display.c_left = %0.2f\u0026#34;, display.c_left)) 66print(string.format(\u0026#34;# us.display.c_right = %0.2f\u0026#34;, display.c_right)) 67print(string.format(\u0026#34;# us.display.c_top = %0.2f\u0026#34;, display.c_top)) 68print(string.format(\u0026#34;# us.display.c_bottom = %0.2f\u0026#34;, display.c_bottom)) 69print(\u0026#34;# display in updateScene done\u0026#34;) 70 71display.ANCHOR_POINTS = { 72 CCPoint(0.5, 0.5), -- CENTER 73 CCPoint(0, 1), -- TOP_LEFT 74 CCPoint(0.5, 1), -- TOP_CENTER 75 CCPoint(1, 1), -- TOP_RIGHT 76 CCPoint(0, 0.5), -- CENTER_LEFT 77 CCPoint(1, 0.5), -- CENTER_RIGHT 78 CCPoint(0, 0), -- BOTTOM_LEFT 79 CCPoint(1, 0), -- BOTTOM_RIGHT 80 CCPoint(0.5, 0), -- BOTTOM_CENTER 81} 82 83display.CENTER = 1 84display.LEFT_TOP = 2; display.TOP_LEFT = 2 85display.CENTER_TOP = 3; display.TOP_CENTER = 3 86display.RIGHT_TOP = 4; display.TOP_RIGHT = 4 87display.CENTER_LEFT = 5; display.LEFT_CENTER = 5 88display.CENTER_RIGHT = 6; display.RIGHT_CENTER = 6 89display.BOTTOM_LEFT = 7; display.LEFT_BOTTOM = 7 90display.BOTTOM_RIGHT = 8; display.RIGHT_BOTTOM = 8 91display.BOTTOM_CENTER = 9; display.CENTER_BOTTOM = 9 92 93function display.align(target, anchorPoint, x, y) 94 target:setAnchorPoint(display.ANCHOR_POINTS[anchorPoint]) 95 if x and y then target:setPosition(x, y) end 96end 97 98local us = CCScene:create() 99us.name = \u0026#34;updateScene\u0026#34; 100 101local localResInfo = nil 102 103function us._addUI() 104\t-- Get the newest resinfo in ures. 105\tlocal localResInfo = updater.getResCopy() 106 107\tlocal __bg = CCSprite:create(us._getres(\u0026#34;res/pic/init_bg.png\u0026#34;)) 108\tdisplay.align(__bg, display.CENTER, display.cx, display.cy) 109\tus:addChild(__bg, 0) 110 111\tlocal __label = CCLabelTTF:create(\u0026#34;Loading...\u0026#34;, \u0026#34;Arial\u0026#34;, 24) 112\t__label:setColor(ccc3(255, 0, 0)) 113\tus._label = __label 114\tdisplay.align(__label, display.CENTER, display.cx, display.bottom+30) 115\tus:addChild(__label, 10) 116end 117 118function us._getres(path) 119\tif not localResInfo then 120\tlocalResInfo = updater.getResCopy() 121\tend 122\tfor key, value in pairs(localResInfo.oth) do 123\tprint(\u0026#34;us._getres:\u0026#34;, key, value) 124\tlocal pathInIndex = string.find(key, path) 125\tif pathInIndex and pathInIndex \u0026gt;= 1 then 126\tprint(\u0026#34;us._getres getvalue:\u0026#34;, path) 127\tres[path] = value 128\treturn value 129\tend 130\tend 131\treturn path 132end 133 134function us._sceneHandler(event) 135\tif event == \u0026#34;enter\u0026#34; then 136\tprint(string.format(\u0026#34;updateScene \\\u0026#34;%s:onEnter()\\\u0026#34;\u0026#34;, us.name)) 137\tus.onEnter() 138\telseif event == \u0026#34;cleanup\u0026#34; then 139\tprint(string.format(\u0026#34;updateScene \\\u0026#34;%s:onCleanup()\\\u0026#34;\u0026#34;, us.name)) 140\tus.onCleanup() 141\telseif event == \u0026#34;exit\u0026#34; then 142\tprint(string.format(\u0026#34;updateScene \\\u0026#34;%s:onExit()\\\u0026#34;\u0026#34;, us.name)) 143\tus.onExit() 144 145\tif DEBUG_MEM then 146\tprint(\u0026#34;----------------------------------------\u0026#34;) 147\tprint(string.format(\u0026#34;LUA VM MEMORY USED: %0.2f KB\u0026#34;, collectgarbage(\u0026#34;count\u0026#34;))) 148\tCCTextureCache:sharedTextureCache():dumpCachedTextureInfo() 149\tprint(\u0026#34;----------------------------------------\u0026#34;) 150\tend 151\tend 152end 153 154function us._updateHandler(event, value) 155\tupdater.state = event 156\tif event == \u0026#34;success\u0026#34; then 157\tupdater.stateValue = value:getCString() 158\tupdater.updateFinalResInfo() 159\tif us._succHandler then 160\tus._succHandler() 161\tend 162\telseif event == \u0026#34;error\u0026#34; then 163\tupdater.stateValue = value:getCString() 164\telseif event == \u0026#34;progress\u0026#34; then 165\tupdater.stateValue = tostring(value:getValue()) 166\telseif event == \u0026#34;state\u0026#34; then 167\tupdater.stateValue = value:getCString() 168\tend 169\tus._label:setString(updater.stateValue) 170\tassert(event ~= \u0026#34;error\u0026#34;, 171\tstring.format(\u0026#34;Update error: %s !\u0026#34;, updater.stateValue)) 172end 173 174function us.addListener(handler) 175\tus._succHandler = handler 176\treturn us 177end 178 179function us.onEnter() 180\tupdater.update(us._updateHandler) 181end 182 183function us.onExit() 184\tupdater.clean() 185\tus:unregisterScriptHandler() 186end 187 188function us.onCleanup() 189end 190 191us:registerScriptHandler(us._sceneHandler) 192us._addUI() 193return us 代码都在上面，说重点：\n4.4.1 还是没有framework\n这是必须一直牢记的。由于没有载入quick的 framework，所有的quick特性都不能使用。\n你也许会说没有framework我怎么写界面？那么想想用C++的同学吧！那个代码怎么也比Lua多吧？\n什么什么？你说有CCB和CCS？CCS你妹啊！同学我和你不是一个班的。\n例如，原来在quick中这样写：\n1display.newSprite(\u0026#34;res/pic/init_bg.png\u0026#34;) 2\t:align(display.CENTER, display.cx, display.cy) 3\t:addTo(self, 0) 在没有quick framework的时候需要改成这样：\n1local __bg = CCSprite:create(us._getres(\u0026#34;res/pic/init_bg.png\u0026#34;)) 2display.align(__bg, display.CENTER, display.cx, display.cy) 3us:addChild(__bg, 0) 等等！为啥我用了 display ！！！笨蛋，你不会偷quick的啊啊啊！\n4.4.2 必须要偷的代码\n为了方便使用，我们可以偷一部分framework的代码过来（干嘛说得那么难听嘛，程序员怎么能用偷？程序员的事，用CV啊），注意CV来的代码用local变量来保存。由于 updateScene 已经是一个可视的场景，因此quick中关于界面缩放设置的那部分代码也是必须CV过来。不多，几十行而已。\n游戏产品绝大多数都不会做成横屏竖屏自动适应的（自己找SHI啊有木有），因此界面缩放的代码我也只保存了一个横屏的，这又省了不少。那CV的同学，注意自己改啊！\n4.4.3 update.updateScene._getres(path)\n在 update.updateScene 模块中，所有涉及到资源路径的地方，必须使用这个方法来包裹。\n这个方法先从 update.updater 模块中获取最新的资源索引列表，然后根据我们传递的相对路径从索引列表中查找到资源的实际路径（可能是原包自带的资源，也可能是更新后的资源的绝对路径），然后载入它们。这能保证我们使用的是最新的资源。\n4.4.4 update.updateScene._updateHandler(event, value)\n这个方法已经在 上面 C++ 模块中 讲过了。注意其中的 _succHandler 是在 update.UpdateApp 中定义的匿名函数。\n4.4.5 怎么使用这个模块\n如果你要使用这个模块，那么可能大部分都要重写。你可以看到，我在这个模块中只有一个背景图和一个 CCLabeTTF 来显示下载进度和状态。你当然不希望你的更新界面就是这个样子。怎么也得来个妹子做封面不是？\n4.5 update.updater 这是整个更新系统的核心部分了。代码更长一点，但其实很好懂。\n在这个模块中，我们需要完成下面的工作：\n调用C++的Updater模块来获取远程的版本号以及资源下载地址； 调用C++的Updater模块来下载解压； 合并解压后的新资源到新资源文件夹； 更新总的资源索引； 删除临时文件； 报告更新中的各种错误。 所以说，这是一个工具模块。它提供的是给更新使用的各种工具。而 UpdateApp 和 updateScene 则分别是功能和界面模块。\n1-- It can download resources and uncompress it, 2-- copy new package to res directory, 3-- and remove temporery directory. 4-- @author zrong(zengrong.net) 5-- Creation 2014-07-03 6 7require \u0026#34;lfs\u0026#34; 8local updater = {} 9updater.STATES = { 10\tkDownStart = \u0026#34;downloadStart\u0026#34;, 11\tkDownDone = \u0026#34;downloadDone\u0026#34;, 12\tkUncompressStart = \u0026#34;uncompressStart\u0026#34;, 13\tkUncompressDone = \u0026#34;uncompressDone\u0026#34;, 14\tunknown = \u0026#34;stateUnknown\u0026#34;, 15} 16 17updater.ERRORS = { 18\tkCreateFile = \u0026#34;errorCreateFile\u0026#34;, 19\tkNetwork = \u0026#34;errorNetwork\u0026#34;, 20\tkNoNewVersion = \u0026#34;errorNoNewVersion\u0026#34;, 21\tkUncompress = \u0026#34;errorUncompress\u0026#34;, 22\tunknown = \u0026#34;errorUnknown\u0026#34;; 23} 24 25function updater.isState(state) 26\tfor k,v in pairs(updater.STATES) do 27\tif v == state then 28\treturn true 29\tend 30\tend 31\treturn false 32end 33 34function updater.clone(object) 35 local lookup_table = {} 36 local function _copy(object) 37 if type(object) ~= \u0026#34;table\u0026#34; then 38 return object 39 elseif lookup_table[object] then 40 return lookup_table[object] 41 end 42 local new_table = {} 43 lookup_table[object] = new_table 44 for key, value in pairs(object) do 45 new_table[_copy(key)] = _copy(value) 46 end 47 return setmetatable(new_table, getmetatable(object)) 48 end 49 return _copy(object) 50end 51 52function updater.vardump(object, label, returnTable) 53 local lookupTable = {} 54 local result = {} 55 56 local function _v(v) 57 if type(v) == \u0026#34;string\u0026#34; then 58 v = \u0026#34;\\\u0026#34;\u0026#34; .. v .. \u0026#34;\\\u0026#34;\u0026#34; 59 end 60 return tostring(v) 61 end 62 63 local function _vardump(object, label, indent, nest) 64 label = label or \u0026#34;\u0026#34; 65 local postfix = \u0026#34;\u0026#34; 66 if nest \u0026gt; 1 then postfix = \u0026#34;,\u0026#34; end 67 if type(object) ~= \u0026#34;table\u0026#34; then 68 if type(label) == \u0026#34;string\u0026#34; then 69 result[#result +1] = string.format(\u0026#34;%s[\\\u0026#34;%s\\\u0026#34;] = %s%s\u0026#34;, indent, label, _v(object), postfix) 70 else 71 result[#result +1] = string.format(\u0026#34;%s%s%s\u0026#34;, indent, _v(object), postfix) 72 end 73 elseif not lookupTable[object] then 74 lookupTable[object] = true 75 76 if type(label) == \u0026#34;string\u0026#34; then 77 result[#result +1 ] = string.format(\u0026#34;%s%s = {\u0026#34;, indent, label) 78 else 79 result[#result +1 ] = string.format(\u0026#34;%s{\u0026#34;, indent) 80 end 81 local indent2 = indent .. \u0026#34; \u0026#34; 82 local keys = {} 83 local values = {} 84 for k, v in pairs(object) do 85 keys[#keys + 1] = k 86 values[k] = v 87 end 88 table.sort(keys, function(a, b) 89 if type(a) == \u0026#34;number\u0026#34; and type(b) == \u0026#34;number\u0026#34; then 90 return a \u0026lt; b 91 else 92 return tostring(a) \u0026lt; tostring(b) 93 end 94 end) 95 for i, k in ipairs(keys) do 96 _vardump(values[k], k, indent2, nest + 1) 97 end 98 result[#result +1] = string.format(\u0026#34;%s}%s\u0026#34;, indent, postfix) 99 end 100 end 101 _vardump(object, label, \u0026#34;\u0026#34;, 1) 102 103\tif returnTable then return result end 104 return table.concat(result, \u0026#34;\\n\u0026#34;) 105end 106 107local u = nil 108local f = CCFileUtils:sharedFileUtils() 109-- The res index file in original package. 110local lresinfo = \u0026#34;res/resinfo.lua\u0026#34; 111local uroot = f:getWritablePath() 112-- The directory for save updated files. 113local ures = uroot..\u0026#34;res/\u0026#34; 114-- The package zip file what download from server. 115local uzip = uroot..\u0026#34;res.zip\u0026#34; 116-- The directory for uncompress res.zip. 117local utmp = uroot..\u0026#34;utmp/\u0026#34; 118-- The res index file in zip package for update. 119local zresinfo = utmp..\u0026#34;res/resinfo.lua\u0026#34; 120 121-- The res index file for final game. 122-- It combiled original lresinfo and zresinfo. 123local uresinfo = ures .. \u0026#34;resinfo.lua\u0026#34; 124 125local localResInfo = nil 126local remoteResInfo = nil 127local finalResInfo = nil 128 129local function _initUpdater() 130\tprint(\u0026#34;initUpdater, \u0026#34;, u) 131\tif not u then u = Updater:new() end 132\tprint(\u0026#34;after initUpdater:\u0026#34;, u) 133end 134 135function updater.writeFile(path, content, mode) 136 mode = mode or \u0026#34;w+b\u0026#34; 137 local file = io.open(path, mode) 138 if file then 139 if file:write(content) == nil then return false end 140 io.close(file) 141 return true 142 else 143 return false 144 end 145end 146 147function updater.readFile(path) 148\treturn f:getFileData(path) 149end 150 151function updater.exists(path) 152\treturn f:isFileExist(path) 153end 154 155--[[ 156-- Departed, uses lfs instead. 157function updater._mkdir(path) 158\t_initUpdater() 159\treturn u:createDirectory(path) 160end 161 162-- Departed, get a warning in ios simulator 163function updater._rmdir(path) 164\t_initUpdater() 165\treturn u:removeDirectory(path) 166end 167--]] 168 169function updater.mkdir(path) 170\tif not updater.exists(path) then 171\treturn lfs.mkdir(path) 172\tend 173\treturn true 174end 175 176function updater.rmdir(path) 177\tprint(\u0026#34;updater.rmdir:\u0026#34;, path) 178\tif updater.exists(path) then 179\tlocal function _rmdir(path) 180\tlocal iter, dir_obj = lfs.dir(path) 181\twhile true do 182\tlocal dir = iter(dir_obj) 183\tif dir == nil then break end 184\tif dir ~= \u0026#34;.\u0026#34; and dir ~= \u0026#34;..\u0026#34; then 185\tlocal curDir = path..dir 186\tlocal mode = lfs.attributes(curDir, \u0026#34;mode\u0026#34;) 187\tif mode == \u0026#34;directory\u0026#34; then 188\t_rmdir(curDir..\u0026#34;/\u0026#34;) 189\telseif mode == \u0026#34;file\u0026#34; then 190\tos.remove(curDir) 191\tend 192\tend 193\tend 194\tlocal succ, des = os.remove(path) 195\tif des then print(des) end 196\treturn succ 197\tend 198\t_rmdir(path) 199\tend 200\treturn true 201end 202 203-- Is there a update.zip package in ures directory? 204-- If it is true, return its abstract path. 205function updater.hasNewUpdatePackage() 206\tlocal newUpdater = ures..\u0026#34;lib/update.zip\u0026#34; 207\tif updater.exists(newUpdater) then 208\treturn newUpdater 209\tend 210\treturn nil 211end 212 213-- Check local resinfo and remote resinfo, compare their version value. 214function updater.checkUpdate() 215\tlocalResInfo = updater.getLocalResInfo() 216\tlocal localVer = localResInfo.version 217\tprint(\u0026#34;localVer:\u0026#34;, localVer) 218\tremoteResInfo = updater.getRemoteResInfo(localResInfo.update_url) 219\tlocal remoteVer = remoteResInfo.version 220\tprint(\u0026#34;remoteVer:\u0026#34;, remoteVer) 221\treturn remoteVer ~= localVer 222end 223 224-- Copy resinfo.lua from original package to update directory(ures) 225-- when it is not in ures. 226function updater.getLocalResInfo() 227\tprint(string.format(\u0026#34;updater.getLocalResInfo, lresinfo:%s, uresinfo:%s\u0026#34;, 228\tlresinfo,uresinfo)) 229\tlocal resInfoTxt = nil 230\tif updater.exists(uresinfo) then 231\tresInfoTxt = updater.readFile(uresinfo) 232\telse 233\tassert(updater.mkdir(ures), ures..\u0026#34; create error!\u0026#34;) 234\tlocal info = updater.readFile(lresinfo) 235\tprint(\u0026#34;localResInfo:\u0026#34;, info) 236\tassert(info, string.format(\u0026#34;Can not get the constent from %s!\u0026#34;, lresinfo)) 237\tupdater.writeFile(uresinfo, info) 238\tresInfoTxt = info 239\tend 240\treturn assert(loadstring(resInfoTxt))() 241end 242 243function updater.getRemoteResInfo(path) 244\t_initUpdater() 245\tprint(\u0026#34;updater.getRemoteResInfo:\u0026#34;, path) 246\tlocal resInfoTxt = u:getUpdateInfo(path) 247\tprint(\u0026#34;resInfoTxt:\u0026#34;, resInfoTxt) 248\treturn assert(loadstring(resInfoTxt))() 249end 250 251function updater.update(handler) 252\tassert(remoteResInfo and remoteResInfo.package, \u0026#34;Can not get remoteResInfo!\u0026#34;) 253\tprint(\u0026#34;updater.update:\u0026#34;, remoteResInfo.package) 254\tif handler then 255\tu:registerScriptHandler(handler) 256\tend 257\tupdater.rmdir(utmp) 258\tu:update(remoteResInfo.package, uzip, utmp, false) 259end 260 261function updater._copyNewFile(resInZip) 262\t-- Create nonexistent directory in update res. 263\tlocal i,j = 1,1 264\twhile true do 265\tj = string.find(resInZip, \u0026#34;/\u0026#34;, i) 266\tif j == nil then break end 267\tlocal dir = string.sub(resInZip, 1,j) 268\t-- Save created directory flag to a table because 269\t-- the io operation is too slow. 270\tif not updater._dirList[dir] then 271\tupdater._dirList[dir] = true 272\tlocal fullUDir = uroot..dir 273\tupdater.mkdir(fullUDir) 274\tend 275\ti = j+1 276\tend 277\tlocal fullFileInURes = uroot..resInZip 278\tlocal fullFileInUTmp = utmp..resInZip 279\tprint(string.format(\u0026#39;copy %s to %s\u0026#39;, fullFileInUTmp, fullFileInURes)) 280\tlocal zipFileContent = updater.readFile(fullFileInUTmp) 281\tif zipFileContent then 282\tupdater.writeFile(fullFileInURes, zipFileContent) 283\treturn fullFileInURes 284\tend 285\treturn nil 286end 287 288function updater._copyNewFilesBatch(resType, resInfoInZip) 289\tlocal resList = resInfoInZip[resType] 290\tif not resList then return end 291\tlocal finalRes = finalResInfo[resType] 292\tfor __,v in ipairs(resList) do 293\tlocal fullFileInURes = updater._copyNewFile(v) 294\tif fullFileInURes then 295\t-- Update key and file in the finalResInfo 296\t-- Ignores the update package because it has been in memory. 297\tif v ~= \u0026#34;res/lib/update.zip\u0026#34; then 298\tfinalRes[v] = fullFileInURes 299\tend 300\telse 301\tprint(string.format(\u0026#34;updater ERROR, copy file %s.\u0026#34;, v)) 302\tend 303\tend 304end 305 306function updater.updateFinalResInfo() 307\tassert(localResInfo and remoteResInfo, 308\t\u0026#34;Perform updater.checkUpdate() first!\u0026#34;) 309\tif not finalResInfo then 310\tfinalResInfo = updater.clone(localResInfo) 311\tend 312\t--do return end 313\tlocal resInfoTxt = updater.readFile(zresinfo) 314\tlocal zipResInfo = assert(loadstring(resInfoTxt))() 315\tif zipResInfo[\u0026#34;version\u0026#34;] then 316\tfinalResInfo.version = zipResInfo[\u0026#34;version\u0026#34;] 317\tend 318\t-- Save a dir list maked. 319\tupdater._dirList = {} 320\tupdater._copyNewFilesBatch(\u0026#34;lib\u0026#34;, zipResInfo) 321\tupdater._copyNewFilesBatch(\u0026#34;oth\u0026#34;, zipResInfo) 322\t-- Clean dir list. 323\tupdater._dirList = nil 324\tupdater.rmdir(utmp) 325\tlocal dumpTable = updater.vardump(finalResInfo, \u0026#34;local data\u0026#34;, true) 326\tdumpTable[#dumpTable+1] = \u0026#34;return data\u0026#34; 327\tif updater.writeFile(uresinfo, table.concat(dumpTable, \u0026#34;\\n\u0026#34;)) then 328\treturn true 329\tend 330\tprint(string.format(\u0026#34;updater ERROR, write file %s.\u0026#34;, uresinfo)) 331\treturn false 332end 333 334function updater.getResCopy() 335\tif finalResInfo then return updater.clone(finalResInfo) end 336\treturn updater.clone(localResInfo) 337end 338 339function updater.clean() 340\tif u then 341\tu:unregisterScriptHandler() 342\tu:delete() 343\tu = nil 344\tend 345\tupdater.rmdir(utmp) 346\tlocalResInfo = nil 347\tremoteResInfo = nil 348\tfinalResInfo = nil 349end 350 351return updater 代码都在上面，还是说重点：\n4.5.1 就是没有framework\n我嘴巴都说出茧子了，没有就是没有。\n不过，我又从quick CV了几个方法过来：\nclone 方法用来完全复制一个table，在复制文件索引列表的时候使用； vardump 方法用来1持久化索引列表，使其作为一个lua文件保存在设备存储器上。有修改。 writeFile 和 readFile 用于把需要的文件写入设备中，也用它来复制文件（读入一个文件，在另一个地方写入来实现复制） exists 这个和quick实现的不太一样，直接用 CCFileUtils 了。 4.5.2 文件操作\n除了可以用 writeFile 和 readFile 来实现文件的复制操作之外，还要实现文件夹的创建和删除。\n这个功能可以使用 lfs(Lua file system) 来实现，参见：在lua中递归删除一个文件夹 。\n4.5.3 相关目录和变量\n上面的代码中定义了几个变量，在这里进行介绍方便理解：\n4.5.3.1 lres(local res)\n安装包所带的res目录；\n4.5.3.2 ures(updated res)\n保存在设备上的res目录，用于保存从网上下载的新资源；\n4.5.3.3 utmp（update temp）\n临时文件夹，用于解压缩，更新后会删除；\n4.5.3.4 lresinfo（本地索引文件）\n安装包内自带的所有资源的索引文件，所有资源路径指向包内自带的资源。打包的时候和产品包一起提供，产品包会默认使用这个资源索引文件来查找资源。它的大概内容如下：\n1local data = { 2 version = \u0026#34;1.0\u0026#34;, 3 update_url = \u0026#34;http://192.168.18.22:8080/updater/resinfo.lua\u0026#34;, 4 lib = { 5 [\u0026#34;res/lib/config.zip\u0026#34;] = \u0026#34;res/lib/config.zip\u0026#34;, 6 [\u0026#34;res/lib/framework_precompiled.zip\u0026#34;] = \u0026#34;res/lib/framework_precompiled.zip\u0026#34;, 7 [\u0026#34;res/lib/root.zip\u0026#34;] = \u0026#34;res/lib/root.zip\u0026#34;, 8\t...... 9 }, 10 oth = { 11 [\u0026#34;res/pic/init_bg.png\u0026#34;] = \u0026#34;res/pic/init_bg.png\u0026#34;, 12 ...... 13 }, 14} 15return data 从它的结构可以看出，它包含了当前包的版本(version)、在哪里获取要更新的资源索引文件(update_url)、当前包中所有的lua模块的路径(lib)、当前包中所有的资源文件的路径(oth)。\n4.5.3.5 uresinfo（更新索引文件）\n保存在 ures 中的更新后的索引文件，没有更新的资源路径指向包内自带的资源，更新后的资源路径指向ures中的资源。它的内容大致如下：\nconfig.zip 的路径是在 iOS 模拟器中得到的。\n1local data = { 2 version = \u0026#34;1.0\u0026#34;, 3 update_url = \u0026#34;http://192.168.18.22:8080/updater/resinfo.lua\u0026#34;, 4 lib = { 5 [\u0026#34;res/lib/cc.zip\u0026#34;] = \u0026#34;res/lib/cc.zip\u0026#34;, 6 [\u0026#34;res/lib/config.zip\u0026#34;] = \u0026#34;/Users/zrong/Library/Application Support/iPhone Simulator/7.1/Applications/2B46FAC0-C419-42B5-92B0-B06DD16E113B/Documents/res/lib/config.zip\u0026#34;, 7\t...... 8 }, 9 oth = { 10 [\u0026#34;res/pic/init_bg.png\u0026#34;] = \u0026#34;res/pic/init_bg.png\u0026#34;, 11 ...... 12 }, 13} 14return data 4.5.3.6 http://192.168.18.22:8080/updater/resinfo.lua\ngetRemoteResInfo 方法会读取这个文件，然后将结果解析成lua table。对比其中的version与 lrefinfo 中的区别，来决定是否需要更新。\n若需要，则调用C++ Updater模块中的方法下载 package 指定的zip包并解压。\n它的内容如下：\n1local data = { 2\tversion = \u0026#34;1.0.2\u0026#34;, 3\tpackage = \u0026#34;http://192.168.18.22:8080/updater/res.zip\u0026#34;, 4} 5return data 4.5.3.7 http://192.168.18.22:8080/updater/res.zip\nzip包的文件夹结构大致如下：\nres/ res/resinfo.lua res/lib/cc.zip res/pic/init_bg.png ...... zip文件的下载和解压都是由C++完成的，但是下载和解压的路径需要Lua来提供。这个动作完成后，C++会通知Lua更新成功。Lua会接着进行后续操作就使用下面 4.5.4 中提到的方法来复制资源、合并 uresinfo 。\n4.5.3.8 zresinfo（zip资源索引文件）\nzip文件中也包含一个 resinfo.lua ，它用于指示哪些文件需要更新。内容大致如下：\n1local data = { 2\tversion = \u0026#34;1.0.2\u0026#34;, 3\tlib = { 4\t\u0026#34;res/lib/cc.zip\u0026#34;, 5\t...... 6\t}, 7\toth = { 8\t\u0026#34;res/pic/init_bg\u0026#34;, 9\t...... 10\t}, 11} 12return data 这个文件中包含的所有文件必须能在zip解压后找到。\n4.5.4 update.updater.updateFinalResInfo()\n这是一个至关重要的方法，让我们代入用上面提到的变量名和目录来描述它的功能：\n它实现的功能是：\n读取 uresinfo，若没有，则将 lresinfo 复制成 uresinfo； 从 utmp 中读取 zresinfo，注意此时zip文件已经解压； 将需要更新的资源文件从 utmp 中复制到 ures 中； 更新 uresinfo ，使其中的资源键名指向正确的资源路径（上一步复制的目标路径）； 删除 utmp； 将更新后的 uresinfo 作为lua文件写入 ures 。 4.5.5 其它方法\n对 update.updater 的调用一般是这样的顺序：\n调用 checkUpdat 方法检测是否需要升级； 调用 update 方法执行升级，同时注册事件管理handler； 升级成功，调用 getResCopy 方法获取最新的 uresinfo 。 5 对 framework 的修改 5.1 写一个 getres 方法 ures 中包含的就是所有素材的索引（键值对）。形式如下：\n键名：res/pic/init_bg.png 键值（lres中）: res/pic/init_bg.png 键值（ures中）：/Users/zrong/Library/Application Support/iPhone Simulator/7.1/Applications/2B46FAC0-C419-42B5-92B0-B06DD16E113B/Documents/res/pic/init_bg.png 在程序中，我们一般会使用这样的写法来获取资源：\n1display.newSprite(\u0026#34;pic/init_bg.png\u0026#34;) 或者干脆简化成了：\n1display.newSprite(\u0026#34;init_bg.png\u0026#34;) 要上面的代码能够工作，需要为 CCFileUtils 设置搜索路径：\n1CCFileUtils:sharedFileUtils:addSearchPath(\u0026#34;res/\u0026#34;) 2CCFileUtils:sharedFileUtils:addSearchPath(\u0026#34;res/pic/\u0026#34;) 但是，在这套更新机制中，我不建议设置搜索路径，因为素材都是以完整路径格式保存的，这样使用起来更方便和更确定。\n如果是新项目，那么挺好，我只需要保证素材路径基于 res 提供即可，类似这样：\n1display.newSprite(\u0026#34;res/pic/init_bg.png\u0026#34;) 但是对于已经开发了一段时间的项目来说，一个个改就太不专业了。这是我们需要扩展一个 io.getres 方法：\n1res = {} 2 3function io.getres(path) 4\tprint(\u0026#34;io.getres originl:\u0026#34;, path) 5\tif CCFileUtils:sharedFileUtils():isAbsolutePath(path) then 6\treturn path 7\tend 8\tif res[path] then return res[path] end 9\tfor key, value in pairs(finalRes.oth) do 10\tprint(key, value) 11\tlocal pathInIndex = string.find(key, path) 12\tif pathInIndex and pathInIndex \u0026gt;= 1 then 13\tprint(\u0026#34;io.getres getvalue:\u0026#34;, path) 14\tres[path] = value 15\treturn value 16\tend 17\tend 18\tprint(\u0026#34;io.getres no get:\u0026#34;, path) 19\treturn path 20end 然后，我们需要修改 quick framework 中的display模块让我们的旧代码不必进行任何改动就能生效。\n5.2 修改 display.newSprite 找到该方法中的这个部分：\n1if string.byte(filename) == 35 then -- first char is # 2\tlocal frame = display.newSpriteFrame(string.sub(filename, 2)) 3\tif frame then 4\tsprite = spriteClass:createWithSpriteFrame(frame) 5\tend 6else 7\tif display.TEXTURES_PIXEL_FORMAT[filename] then 8\tCCTexture2D:setDefaultAlphaPixelFormat(display.TEXTURES_PIXEL_FORMAT[filename]) 9\tsprite = spriteClass:create(filename) 10\tCCTexture2D:setDefaultAlphaPixelFormat(kCCTexture2DPixelFormat_RGBA8888) 11\telse 12\tsprite = spriteClass:create(filename) 13\tend 14end 将其改为：\n1if string.byte(filename) == 35 then -- first char is # 2\tlocal frame = display.newSpriteFrame(string.sub(filename, 2)) 3\tif frame then 4\tsprite = spriteClass:createWithSpriteFrame(frame) 5\tend 6else 7\tlocal absfilename = io.getres(filename) 8\tif display.TEXTURES_PIXEL_FORMAT[filename] then 9\tCCTexture2D:setDefaultAlphaPixelFormat(display.TEXTURES_PIXEL_FORMAT[filename]) 10\tsprite = spriteClass:create(absfilename) 11\tCCTexture2D:setDefaultAlphaPixelFormat(kCCTexture2DPixelFormat_RGBA8888) 12\telse 13\tsprite = spriteClass:create(absfilename) 14\tend 15end 5.3 修改display.newTilesSprite 将其中的 local sprite = CCSprite:create(filename, rect)\n改为local sprite = CCSprite:create(io.getres(filename), rect)\n5.4 修改 display.newBatchNode 改法与上面相同。\n6. 后记 噢！这真是一篇太长的文章了，真希望我都说清了。\n其实还有一些东西在这个机制中没有涉及，例如：\n6.1 更新的健壮性 在更新 update.zip 模块自身的时候，如果新的update.zip有问题怎么办？ 如果索引文件找不到怎么办？zip文件解压失败怎么办？zresinfo 中的内容与zip文件解压后的内容不符怎么办？ 下载更新的时候网断了如何处理？如何处理断点续传？设备磁盘空间不够了怎么处理？ 6.2 更多的更新方式 我在 需求的复杂性 里面描述了一些需求，例如：\n如何回滚更新？ 如何多个版本共存？ 如何对资源进行指纹码化？ 这些问题都不难解决。方法自己想，我只能写到这儿了。\n话说回来，实现了 更新一切 ，你还担心什么呢？\n**射手，30分钟够么？**","date":"2014-07-04","description":"","lastmod":"2014-07-04T06:07:39Z","slug":"hot_update_in_quick-cocos2d-x","tags":["cpp","cocos2d-x","lua"],"title":"quick-cocos2d-x的热更新机制实现","url":"https://blog.zengrong.net/post/hot_update_in_quick-cocos2d-x/"},{"categories":["technology"],"content":"在lua中递归删除一个文件夹\nrmdir in quick-cocos2d-x with lua.\n在使用 quick-cocos2d-x 做项目热更新的时候，我需要建立临时文件夹以保存下载的更新包。在更新完成后，我需要删除这些临时文件和文件夹。\ncocos2d-x 和 quick-cocos2d-x 都没有提供删除文件夹功能。我做了如下2个尝试：\n1. 使用C++ 在 cocos2d-x 2.x 中的 AssetsManager 包中提供了一个 CreateDirectory 方法。这个方法可以跨平台支持创建文件夹。在实际项目中运行没有问题。\n1bool AssetsManager::createDirectory(const char *path) 2{ 3#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) 4 mode_t processMask = umask(0); 5 int ret = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); 6 umask(processMask); 7 if (ret != 0 \u0026amp;\u0026amp; (errno != EEXIST)) 8 { 9 return false; 10 } 11 12 return true; 13#else 14 BOOL ret = CreateDirectoryA(path, NULL); 15if (!ret \u0026amp;\u0026amp; ERROR_ALREADY_EXISTS != GetLastError()) 16{ 17return false; 18} 19 return true; 20#endif 21} 在 cocos2d-x 2.x 的 AssetsManager sample 范例中提供了一个 reset 方法，这个方法使用系统命令递归删除文件夹。\n1void UpdateLayer::reset(cocos2d::CCObject *pSender) 2{ 3 pProgressLabel-\u0026gt;setString(\u0026#34; \u0026#34;); 4 5 // Remove downloaded files 6#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) 7 string command = \u0026#34;rm -r \u0026#34;; 8 // Path may include space. 9 command += \u0026#34;\\\u0026#34;\u0026#34; + pathToSave + \u0026#34;\\\u0026#34;\u0026#34;; 10 system(command.c_str()); 11#else 12 string command = \u0026#34;rd /s /q \u0026#34;; 13 // Path may include space. 14 command += \u0026#34;\\\u0026#34;\u0026#34; + pathToSave + \u0026#34;\\\u0026#34;\u0026#34;; 15 system(command.c_str()); 16#endif 17 // Delete recorded version codes. 18 getAssetsManager()-\u0026gt;deleteVersion(); 19 20 createDownloadedDir(); 21} 但是，这个 reset 在 ios 模拟器中运行的时候，xcode会报这样的warinng：\nThe iOS Simulator libSystem was initialized out of order. This is most often caused by running host executables or inserting host dylibs. In the future, this will cause an abort.\n因此，我转而考虑另一个方案。\n2. 纯lua 纯 lua 其实是个噱头。这里还是要依赖 lfs(lua file sytem)，好在 quick-cocos2d-x 已经包含了这个库。\nlfs.rmdir 命令 和 os.remove 命令一样，只能删除空文件夹。因此实现类似 rm -rf 的功能， 必须要递归删除文件夹中所有的文件和子文件夹。\n让我们扩展一下 os 包。\n1require(\u0026#34;lfs\u0026#34;) 2 3function os.exists(path) 4\treturn CCFileUtils:sharedFileUtils():isFileExist(path) 5end 6 7function os.mkdir(path) 8\tif not os.exists(path) then 9\treturn lfs.mkdir(path) 10\tend 11\treturn true 12end 13 14function os.rmdir(path) 15\tprint(\u0026#34;os.rmdir:\u0026#34;, path) 16\tif os.exists(path) then 17\tlocal function _rmdir(path) 18\tlocal iter, dir_obj = lfs.dir(path) 19\twhile true do 20\tlocal dir = iter(dir_obj) 21\tif dir == nil then break end 22\tif dir ~= \u0026#34;.\u0026#34; and dir ~= \u0026#34;..\u0026#34; then 23\tlocal curDir = path..dir 24\tlocal mode = lfs.attributes(curDir, \u0026#34;mode\u0026#34;) 25\tif mode == \u0026#34;directory\u0026#34; then 26\t_rmdir(curDir..\u0026#34;/\u0026#34;) 27\telseif mode == \u0026#34;file\u0026#34; then 28\tos.remove(curDir) 29\tend 30\tend 31\tend 32\tlocal succ, des = os.remove(path) 33\tif des then print(des) end 34\treturn succ 35\tend 36\t_rmdir(path) 37\tend 38\treturn true 39end 上面的代码在 iOS 模拟器和 Android 真机上测试成功。Windows系统、Mac OSX 以及 iOS 真机还没有测试。我测试后会立即更新。\n","date":"2014-07-03","description":"","lastmod":"2014-07-03T03:22:22Z","slug":"rmdir_in_quick-cocos2d-x_with_lua","tags":["cpp","cocos2d-x","lua"],"title":"在lua中递归删除一个文件夹","url":"https://blog.zengrong.net/post/rmdir_in_quick-cocos2d-x_with_lua/"},{"categories":["technology"],"content":"在 Mac OS X 上安装lighttpd\nInstall lighttpd in Mac OS X\n2014-10-22更新： 加入更新到 Yosemite 后出现错误的解决方法。\n在Windows上，我使用的是XAMPP套件。\nMac上我还没有安装HTTP服务器。我的VPS上使用的是lighttpd（从Apache到Lighttpd），因此在工作电脑上也希望使用它。\n安装 1-\u0026gt; % brew search lighttpd 2lighttpd 3-\u0026gt; % brew install lighttpd 安装过程中，有几个重点需要提醒：\n1. 下载失败 lighttpd 的源托管在 github 上，github 在国内访问并不稳定，多试几次或者拨vpn；\n2. 安装后无法链接文档 安装成功后提示无法链接 man 文档，并要求重新执行 brew link lighttpd 。\n这可能是由于你安装 homebrew 的时候，并没有使用 root 权限。执行下面的命令把你的 brew 的 owner 改为 root。\n1sudo chown root $(which brew) 配置 配置文件默认在这个路径： /usr/local/etc/lighttpd/lighttpd.conf。\ndocument root 默认在这里： /usr/local/var/www/htdocs 。\n默认端口是 8080 ，如果希望使用 80 端口，则需要用 sudo 启动 lighttpd。\n为了本地调试方便，我会在配置文件中加上这两个属性：\n1# 允许跟踪符号链接 2server.follow-symlink= \u0026#34;enable\u0026#34; 3# 目录中没有index文件时直接显示目录内容 4server.dir-listing = \u0026#34;enable\u0026#34; 启动和重新启动 lighttd安装后是不会自动启动的，但Mac又没有类似CenOS的 service start lighttpd 这种用法。\n我们可以使用这条命令来简单测试：\n1lighttpd -Df /usr/local/etc/lighttpd/lighttpd.conf 2014-10-22更新： 将系统升级到 Yosemite 之后，启动碰到这样的错误提示：\n2014-10-22 18:51:43: (server.c.693) opening pid-file failed: /usr/local/var/lighttpd//lighttpd.pid No such file or directory\n创建这个目录即可：\n1mkdir -p /usr/local/var/lighttpd 当然最好还是使用 Mac 提供的工具 launchctl 来启动：\n1ln -sfv /usr/local/opt/lighttpd/*.plist ~/Library/LaunchAgents 2launchctl load ~/Library/LaunchAgents/homebrew.mxcl.lighttpd.plist launchctl 是一个与 crontab 类似的工具，它使用 plist 作为配置文件。详细介绍可以看这里：launchctl 和 launchd 。\n有时候我们修改了conf之后，需要重启lighttpd使其生效，为了避免大量输入，我写了一个 restarthttpd 脚本放在bin中，内容如下：\n1launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.lighttpd.plist 2launchctl load ~/Library/LaunchAgents/homebrew.mxcl.lighttpd.plist 然后可以测试一下：\n1ln -s ~/docs /usr/local/var/www/htdocs/docs 2# 访问 http://localhost:8080/docs 试试看？ 没效果？ 检查配置文件是否出错：lighttpd -tf /usr/local/etc/lighttpd/lighttpd.conf ； 是否配置了80端口但没有使用root用户运行？ 是否没有启动 lighttpd ？ ","date":"2014-06-27","description":"","lastmod":"2014-10-22T10:55:20Z","slug":"install-lighttpd-in-mac-os-x","tags":["bash","http","lighttpd","mac","shell"],"title":"在 Mac OS X 上安装lighttpd","url":"https://blog.zengrong.net/post/install-lighttpd-in-mac-os-x/"},{"categories":["technology"],"content":"在 cocos2d-x 中嵌入浏览器\nEmbeds a browser in cocos2d-x\n在游戏中嵌入网页是很常见的需求，cocos2d-x 引擎官方并没有提供这个功能。\n我在网上转了一圈，把找到的资料做了一些修改，将其集成到我们使用的 quick-cocos2d-x 引擎中。\n主要代码来自：CCXWebview，这里 还有一篇专门讲解Android嵌入浏览器的文章，可以参考。\n集成的类叫做 CCWebView，位于 extensions 之中。\n本文基于 cocos2d-x 2.x，下面的 Android 平台部分介绍了一些 cocos2d-x 3.x的相关信息。\n效果如下：\n显示本博客\n在浏览器中输入\n做什么？ 在游戏中，我们需要显示系统公告，或者制作一些需要复杂图文混排的界面，这些东西如果用 cocos2d-x 来做，未免太过麻烦。嵌入一个网页就简单的多。\n现在的修改能满足这样一些简单的使用：\n显示一个指定地址的网页，设定网页的大小和位置； 更新一个已经显示的网页的内容； 关闭已经显示的网页。 然后，就没有了。因为目前的项目不需要和浏览器交互，所以希望用 CCWebView 来实现一个商城的话可能会比较难办，要做一些扩展。\n在 Android 中，浏览器与 Game 并不在一个线程，因此也没有提供把让cocos2d-x 来控制增加浏览器的关闭按钮之类的功能。如果要实现这些，最好的方法是浏览器不做全屏，然后用cocos2d-x实现一些按钮放在浏览器之上，点击按钮调用 CCWebView 的关闭函数。\n怎么做？ 这里只放出lua代码，C++请脑补。\n创建内嵌浏览器并显示一个网站：\n1-- 创建一个CCWebView，同时设置ActivityName为主Activity的包（后面会详述） 2self._webview = CCWebView:create(\u0026#34;us/t1201/testplayer/Testplayer\u0026#34;) 3self._webview:retain() 4-- 显示一个网页，坐标20，20（左上角为0，0），宽度1000， 高度500 5self._webview:showWebView(\u0026#34;http://zengrong.net\u0026#34;, 20, 20, 1000, 500) 6-- 显示包名 7print(\u0026#34;getActivityName:\u0026#34;, self._webview:getActivityName()) 更新已有浏览器中显示的网址，移除并销毁浏览器：\n1self._webview:updateURL(\u0026#34;https://blog.zengrong.net/post/2112.html\u0026#34;) 2self._webview:removeWebView(); 3self._webview:release() 4self._webview = nil 封装 为了方便使用，我封装了一个 webview.lua 放在 framework 里面，这样只需要记住 show 和 remove 方法就好了。\n由于C++中没有处理CCWebView可能重复的情况，我把 webview 做成单例的，保证任何时候都只有一个 CCWebView 在工作。\n使用这个封装，我写了一个完整的测试项目，看这里：\n1local WebViewTest = class(\u0026#34;WebViewTest\u0026#34;, function() 2\treturn display.newNode() 3end) 4 5function WebViewTest:ctor() 6\tself:_showUI() 7\tself:_test() 8end 9 10function WebViewTest:_test() 11\twebview.setActivityName(\u0026#34;us/t1201/testplayer/Testplayer\u0026#34;) 12\tprint(\u0026#34;getActivityName:\u0026#34;, webview.getActivityName()) 13end 14 15function WebViewTest:_showUI() 16\tlocal __menu = ui.newMenu({ 17\tui.newTTFLabelMenuItem( 18\t{ 19\ttext=\u0026#34;show(http://zengrong.net, 20,20,1000,500)\u0026#34;, 20\tlistener = function() 21\twebview.show(\u0026#34;http://zengrong.net\u0026#34;, 20,20, 1000, 500) 22\tend 23\t}), 24\tui.newTTFLabelMenuItem( 25\t{ 26\ttext=\u0026#34;show(http://zhihu.com, 0,0,500,300)\u0026#34;, 27\tlistener = function() 28\twebview.show(\u0026#34;http://zhihu.com\u0026#34;, 0,0, 500, 300) 29\tend 30\t}), 31\tui.newTTFLabelMenuItem( 32\t{ 33\ttext=\u0026#34;show(http://github.com)\u0026#34;, 34\tlistener = function() 35\twebview.show(\u0026#34;http://github.com\u0026#34;) 36\tend 37\t}), 38\tui.newTTFLabelMenuItem( 39\t{ 40\ttext=\u0026#34;remove()\u0026#34;, 41\tlistener = function() 42\twebview.remove() 43\tend 44\t}), 45\t}) 46\t:addTo(self) 47\t:pos(display.cx,display.bottom+100) 48\t__menu:alignItemsVertically() 49end 50 51return WebViewTest 需要注意的是，在上面的例子中，如果希望改变已有的内嵌浏览器的大小，必须先remove才可以生效。\n跨平台 目前内嵌浏览器仅支持 iOS 和Android 平台。以下是一些需要注意的地方（非常重要！）：\nAndroid 平台 在创建CCWebView的时候必须提供你的项目的主Activity的包路径和类名。CCWebView 需要结合主Activity中提供的一些方法才能工作。这些方法我已经添加到 项目模板 中。\n如果是老项目，请比较 项目模版 和自己的主Activity的区别，加入缺少的方法（相信我，这很简单）。\n如果是新项目，请使用项目创建程序 create_project 来创建。它会使用模版来自动加入这些方法。\n注意写包路径和类名的格式与JAVA的习惯不同，需要把点 . 替换成斜线 / 。\n使用 getActivityName() 方法可以返回传入的包名。\n对于 cocos2d-x 3.x，由于Activity继承自NativeActivity，因此需要使用 getWindowManager().addView() 来加入Android控件。详见：Cocos2d-x 3.0beta成功添加Android的webview 。\niOS 平台 iOS不需要提供包名，因此可以直接使用不带参数的 create() 方法来创建 CCWebView 。但为了避免判断平台使用不同的创建方法，也可以直接传入 Android 中需要的包名。iOS平台下的代码不会记录和处理这个值。\n使用 getActivityName() 方法将总是返回空字符串。\n在iOS平台上，浏览器的分辨率设定是个问题。对于高清设备，你传递的值其实是真实值的一半。例如在iPhone5上调用这句：\n1showWebView(\u0026#34;http://zengrong.net\u0026#34;, 20, 20, 1000, 500) 那么最终显示的效果是浏览器宽度超出屏幕。因为这里的宽度1000其实等于2000。\n而在标清设备上（例如iPad2），传递的宽度就是真实的宽度。\nMac OS X 平台 在 quick-x-player Mac 版本中，调用 CCWebView 的方法将不会有任何作用，也不会报错，这是正常的。\nWindows 平台 在 quick-x-player Windows 版本中，调用 CCWebView 的方法将不会有任何作用，也不会报错，这是正常的。\n","date":"2014-06-25","description":"","lastmod":"2014-06-25T04:29:24Z","slug":"embed_browser_in_cocos2d-x","tags":["cpp","cocos2d-x","lua"],"title":"在 cocos2d-x 中嵌入浏览器","url":"https://blog.zengrong.net/post/embed_browser_in_cocos2d-x/"},{"categories":["web"],"content":"最近我的博客经常无法访问。可放在同一主机的 Rect的博客 却正常。\n留意了一下访问顺序，发现页面载入的时候会先访问 fonts.googleapis.com 去获取字体。但是目前 google 的服务器被封严重，导致我的博客也遭殃了。\n网上找了一个插件用来禁用 Google 字体，在这里：\nDisable Google Fonts\n","date":"2014-06-16","description":"","lastmod":"2014-06-16T04:19:40Z","slug":"disable-google-fonts","tags":["google","wordpress"],"title":"在Wordpress中禁用Google字体","url":"https://blog.zengrong.net/post/disable-google-fonts/"},{"categories":["impressions"],"content":"flash动画是如何兴起和衰退的？\nHow did the Flash MV rise and fall?\n这篇文章是我在之乎上的一个回答：http://www.zhihu.com/question/23940320/answer/26149559\n原文提问： 记得刚上网那会，flash动画还是很火的，比如有什么大话三国，秋水堂，火柴人格斗，东北人都是活雷锋等数不完的MV二次制作，现在怎么就看不到看到当年的盛况了。\n我的回答： 因为大家都去看美剧了。\n=============== 为了报答题主邀请之恩，认真回答下。\n上面的回答是很认真的，大家真的都去看美剧了。\nFlash动画（专业点叫Flash MV）这种媒体表现形式和Flash的兴衰有很大的关系。2001年到2008年是Flash MV发展的黄金时期。让我们看看那段时间发生了什么。\n图片出处：Actionscript,AS3,MXML,Flex,Flex Builder,Flash Builder,Flash,AIR,Flash Player之关系\n1. 没有之一 2001年前后，互联网上的动画表现形式，一个是GIF，另一个还是GIF。那些IE浏览器专用的跑马灯之类效果就不算了吧？基于浏览器看视频还必须要装神马Media Player插件Real Player插件吧？那时候大家还是用Real Player看VCD光盘上的大话西游和女神兰兰的动作片吧？还在用超级解霸看租来的港片VCD吧？\n嗯，我说的是动画，怎么又扯到视频？在那个没有网吧只有游戏吧（一种有十几台电脑只连局域网的小黑屋）的年代，只要有点能动起来的东西，管他动画还是视频，大家都乐意看啊。\n于是，Flash突然就火了。这么小的文件（几百KB~几MB），这样精美的画面，放大不失真（基于矢量）的效果，支持流式播放，边下载边播放，能支持交互正看重看倒看或者点个“开始播放”神马的……\n在那个网速64K/512K/1024K的年代，这真的是业界良心啊！\n而且，那个时候在互联网上能流畅播放的可以动起来的大段的内容，还！只！有！Flash！！！\n不火才怪了。\n2. 自掘坟墓 既然后面蓬勃发展的事情你都知道了，那我们就直接进入高潮吧。\nFlash MX(Flash Player 6)开始，Macromedia给Flash加入了支持播放视频的能力。当时可以在SWF格式的文件中嵌入视频数据，依然支持流播放。\n然后Flash MX 2004(Flash Player 7)开始把视频单独作为一种文件格式提出来，这就是大名鼎鼎的FLV格式。\nFLV这种视频格式的出现引爆了视频流媒体整个行业。因为那时Flash播放器的装机率已经超过95%（当然现在也是啦），用Flash做一个几十KB的小播放器，然后用这个小播放器来流式播放FLV视频文件，做一个视频点播网站不是妥妥的么！\n后面的事情，我们都知道了。Youtube、土豆、优酷等等就起来了。\nMacromedia/Adobe 后来一直在改善FLV格式，视频编码从Sorenson Spark，到On VP6再到H.264，音频编码从MP3到AAC，容器格式从FLV到F4V/MP4……这些修改让这种小众的视频格式越来越优秀和开放，最后成了FLV成了主流的视频格式。现在你想找一款不支持FLV的视频播放器，还真难。\n抛弃FLV，迎接MP4——制作Flash Player支持的H.264视频格式\n当然，FLV的流行与 FFmpeg 和MEncoder这两套开源codec的支持是分不开的。觉得上传任何视频文件都能帮你转换成在线视频的网站很牛B？他们只是在后台运行了一套自动转换程序而已。\n当然，做视频站不是那么简单的事情，我这里只是想表达，这个技术已经主流化，免费化了，简单化了。在这种技术支撑下，FLV想不火都难。\n大家在互联网上关注的，无非是吸引眼球的，好看的，精美的，有趣的，色情的（我有说么）……这些美剧日剧韩剧都能给你，那为毛还要看Flash MV这种上世纪的东西？\n3. 百花齐放 到了AS3时代，Flash的性能大幅提升，语言大幅强化，在企业开发和交互站点上进行过一番尝试和推广。有段时间，招行的网银专业版甚至都使用了Flex技术。一时间画板应用、虚拟现实、在线音频视频编辑器等等充斥互联网。Adobe抛出RIA(Rich Internet Application)概念，意思是说你那半成品Web2.0和AJAX算个毛线啊……\n虽然在企业开发中使用比较广泛，但大众并不了解RIA是个什么东西。我可以不负责任的说，那时的RIA体验绝对比AJAX好很多，可是……可是……（不说了，说起来都是泪）\n由于后来出现Adobe对Flash Player高级功能收费以及 放弃Linux平台 、被乔帮主忽悠、被Unity3D等平台捧杀 等等事件。Adobe自知在企业市场干不过H5，将Flash的主要发展方向转向了视频和游戏。Flash Player在移动设备上的失败，造就了AIR在手机游戏上的成功。彼时，已经有更多漂亮的，大胆的（你敢说没看过页游广告妹妹的招牌动作？）Flash网页游戏和更多的手机游戏可玩，Flash MV还有什么吸引力？\n4. 苟延残喘 当然，Flash MV的生命力仍在。我经常在公交车上看到一些相声段子或者视频广告，一看就是上世纪Flash MV的手法。他们在二十一世纪依然占据着一席之地，而且会持续下去。\nFlash 是个优秀的软件，大量的美术工作者可以在不懂程序的情况下，使用她做出精美的交互动画。但成也萧何，败也萧何。大量不懂程序的美术工作者做出的交互作品，对于互联网和浏览器就是一个噩梦。乔布斯忽悠大家说Flash性能低占CPU高，Adobe还真的浑身是嘴也说不清。但各种小白们还真的能找到各种理由证明帮主的忽悠是真实的谎言。\n何况，Adobe根本就没说什么。\nFlash 和Flash MV依然会有市场，它们还会在自己的领域继续发光，Adobe依然可以开心地卖软件，Macromedia依然是死不瞑目。\n只是——这世界变了。\n","date":"2014-05-28","description":"","lastmod":"2014-05-28T14:34:30Z","slug":"how-did-the-flash-mv-rise-and-fall","tags":["adobe","flash","study"],"title":"flash动画是如何兴起和衰退的？","url":"https://blog.zengrong.net/post/how-did-the-flash-mv-rise-and-fall/"},{"categories":["technology"],"content":"**2014-06-20更新：**增加对七牛云存储的HTTPS服务说明和又拍云的介绍。\niOS 7.1在线安装IPA失败以及数字证书\nEnterprise app deployment doesn't work on iOS 7.1 and CA\n错误现象 在 iOS 升级到 7.1 之后，使用 Safari 在线安装企业版或者 AD-HOC 版本的IPA时，iOS会提示：\n无法安装应用程序，因为“xx.xx.xx” 的证书无效。\n这是一个蛋疼的提示，它的真正人类能懂的含义是：\n在 iOS 7.1 版本以后，请使用 HTTPS 部署此类安装。\n解决方案 1. 部署自己的HTTPS服务器 这种方案需要使用一个可信的证书颁发机构办法的证书，将其部署在自己的服务器上。配置自己的服务器软件（例如 Apache、Nginx等） 使其支持它。\n这是一劳永逸的解决方案，如果需要大量部署，那么此种方案是首选。\n注意，自己给自己颁发的证书是不行的。必须使用可信的证书颁发机构颁发的证书。这就意味着，你很可能需要付费。\n后面会讲到数字证书的部分，会稍稍展开一点。\n2. 使用已有的HTTPS服务器 只需要找一个已经部署的HTTPS服务器，将我们的部署文件（plist）传到该服务器上即可。\n注意，为了保证下载速度，也为了保证域名的统一性，我们可以只把 plist 文件上传到HTTPS服务器上。\n例如，在我们的网站（http://my.http.server）上有一个在线安装IPA页面是这样的：\n1\u0026lt;!DOCTYPE HTML PUBLIC \u0026#34;-//W3C//DTD HTML 4.01 Transitional//EN\u0026#34; \u0026#34;http://www.w3.org/TR/html4/loose.dtd\u0026#34;\u0026gt; 2\u0026lt;html\u0026gt; 3\u0026lt;head\u0026gt; 4\u0026lt;title\u0026gt;Setup My App\u0026lt;/title\u0026gt; 5\u0026lt;/head\u0026gt; 6\u0026lt;body\u0026gt; 7\u0026lt;ul\u0026gt; 8\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;itms-services://?action=download-manifest\u0026amp;url=http://my.http.server/myapp.plist\u0026#34;\u0026gt; Tap Here to Install My App \u0026lt;/a\u0026gt; 9\u0026lt;/li\u0026gt; 10\u0026lt;/ul\u0026gt; 11\u0026lt;/body\u0026gt; 12\u0026lt;/html\u0026gt; 由于 iOS 7.1 不支持通过 HTTP 服务器在线安装IPA，我们需要修改其中的链接，将其改到 HTTPS 服务器上。请注意修改过的前缀和域名。\n1\u0026lt;a href=\u0026#34;itms-services://?action=download-manifest\u0026amp;url=https://my.HTTPS.server/myapp.plist\u0026#34;\u0026gt; Tap Here to Install My App\u0026lt;/a\u0026gt; 我们只需要把这个plist文件上传到HTTPS服务器即可，用户仍然可以访问原来的HTTP来进行下载。IPA文件也可以放在HTTP服务器上。\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; 2\u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; 3\u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; 4\u0026lt;dict\u0026gt; 5\t\u0026lt;key\u0026gt;items\u0026lt;/key\u0026gt; 6\t\u0026lt;array\u0026gt; 7\t\u0026lt;dict\u0026gt; 8\t\u0026lt;key\u0026gt;assets\u0026lt;/key\u0026gt; 9\t\u0026lt;array\u0026gt; 10\t\u0026lt;dict\u0026gt; 11\t\u0026lt;key\u0026gt;kind\u0026lt;/key\u0026gt; 12\t\u0026lt;string\u0026gt;software-package\u0026lt;/string\u0026gt; 13\t\u0026lt;key\u0026gt;url\u0026lt;/key\u0026gt; 14\t\u0026lt;string\u0026gt;http://my.http.server/myapp.ipa\u0026lt;/string\u0026gt; 15\t\u0026lt;/dict\u0026gt; 16\t\u0026lt;/array\u0026gt; 17\t\u0026lt;key\u0026gt;metadata\u0026lt;/key\u0026gt; 18\t\u0026lt;dict\u0026gt; 19\t\u0026lt;key\u0026gt;bundle-identifier\u0026lt;/key\u0026gt; 20\t\u0026lt;string\u0026gt;us.1201.lulala\u0026lt;/string\u0026gt; 21\t\u0026lt;key\u0026gt;bundle-version\u0026lt;/key\u0026gt; 22\t\u0026lt;string\u0026gt;1.0\u0026lt;/string\u0026gt; 23\t\u0026lt;key\u0026gt;kind\u0026lt;/key\u0026gt; 24\t\u0026lt;string\u0026gt;software\u0026lt;/string\u0026gt; 25\t\u0026lt;key\u0026gt;title\u0026lt;/key\u0026gt; 26\t\u0026lt;string\u0026gt;My App\u0026lt;/string\u0026gt; 27\t\u0026lt;/dict\u0026gt; 28\t\u0026lt;/dict\u0026gt; 29\t\u0026lt;/array\u0026gt; 30\u0026lt;/dict\u0026gt; 31\u0026lt;/plist\u0026gt; 而且，在 iOS 7.1 以下的系统中，也可以支持 HTTPS 服务器。所以这个修改对所有iOS系统都生效。\n3. 使用免费的 HTTPS 服务器 如果你找不到拥有 HTTPS 服务器的土豪朋友，那么可以使用下面两种免费的方案：\nDropBox 这位美国土豪提供免费服务。只需要把你的plist文件传到 dropbox 的 public 共享区并 Copy public link 得到公共下载地址就行了。 七牛云存储 这位中国土豪也支持 HTTPS ，服务同样免费（有流量限制），速度在国内访问更快，方法类似。\n在使用七牛的时候要注意，七牛默认提供的外网访问地址，使用HTTPS协议直接访问时浏览器会提示证书无效。可参考 如何通过 SSL 的形式来访问七牛云存储上的资源 重新绑定一个 qbox.me 域名来实现HTTPS访问。 又拍云 也支持HTTPS服务，但仅支持7天试用。 2014-06-20更新：\n最近DropBox在国内抽风，估计离永久屏蔽也不远了，还是早点转吧。\n数字证书 由于考虑自己架设 HTTPS 服务器，于是找了一些数字证书的资料放在这里备查。\n如果一定要使用自签名的证书或者来发布自己的App，那么可以要求iOS系统使用者在TA的设备上把你的证书加入信任。具体参考：Adding CA Cert root and personal certificates to your iPhone, iPod or iPad 。（对于苹果小白来说，这个很难...） 一些证书颁发机构可以免费申请30天、60天的试用证书，例如：试用型 SSL 证书（Free Trial SSL） 和 StartSSL。 Godaddy 有一个面向开源项目1年免费的计划。 还有一些证书每年大约只要6刀，Google可以得到信息。 不要在国内的证书颁发机构购买证书。 不要信任CNNIC的根证书。 还有一些链接：\nHTTPS Certificate authority ","date":"2014-05-02","description":"","lastmod":"2014-05-02T02:58:20Z","slug":"enterprise_app_deployment_on_ios71_and_ca","tags":["http","ios"],"title":"iOS 7.1在线安装IPA失败以及数字证书","url":"https://blog.zengrong.net/post/enterprise_app_deployment_on_ios71_and_ca/"},{"categories":["technology"],"content":"DragonBones 官方C++版本 for cocos2d-x\nDragonBonesCPP for cocos2d-x\nDragonBones 和 CCArmature DragonBones 是一套骨骼动画工具集，最早使用 Flash 和 ActionScript 3.0 语言开发，主要在 Flash 游戏中使用，目前在页游和手游项目中使用很广泛。\nDragonBones 目前包含下面这些内容：\n基于 Flash IDE 中时间轴动画的编辑工具（美术GGMM的最爱）； 基于 Flash IDE 插件的骨骼动画设计面板 DesignPanel； 基于 ActionScript 3.0 的骨骼动画解析库和渲染库； 基于 Javascript 的骨骼动画解析库和渲染库； 基于 Unity3D C# 脚本的骨骼动画解析库和渲染库； 基于 LoomScript 的骨骼动画解析库和渲染库。 详细的入门介绍可以看这里：\nDragonBones快速入门指南 。\ncocos2d-x 中自带的 CCArmature 移植自 DragonBones 2.1 版本，我对 DragonBones Design Panel进行了修改 cocos2d-x专用的DragonBones2.2 ，使其直接输出让 CCArmature 支持的资源格式。\nCCArmature 的问题 CCArmature 的移植并不完善，主要有下面这些问题：\n不支持逐帧动画； 对嵌套骨骼支持不力； 没有实现关键帧事件； 不支持将某个Bone替换成逐帧动画； 完全针对 cocos2d-x 优化，与DragonBones的API不兼容； cocos2d-x中CCArmature展示挤压和变形动画的问题 ； 等等等…… 另外，为了支持 cocostudio 的特性，CCArmature 进行了许多不兼容 DragonBones 的修改，导致新版本的 CCArmature 对 DragonBones Design Panel 生成的资源格式的支持也不好了。详情见：在cocos2d-x中使用DragonBones实现骨骼动画 。\nDragonBonesCPP 正是因为这些问题，DragonBones 开发组 使用 C++ 移植了 DragonBones 的解析库和动画渲染库，命名为 DragonBonesCPP 。它的特点如下：\n这套库完全与 ActionScript 3 库保持一致，可以实现 ActionScript 3 库的所有功能； 使用 DragonBones 官方的 Design Panel，不再需要使用专门针对cocos2d-x的修改版，可保持数据格式与最新版兼容； 由于支持官方 DragonBones Design Panel 输出的格式，因此也可以支持 Spine 的格式； 支持多套渲染引擎，不再仅限于cocos2d-x。 目前，DragonBonesCPP 首先完成了对 cocos2d-x 引擎的支持。下面是一些Demo：\nDragonBonesCPP for cocos2d-x 2.x DragonBonesCPP for cocos2d-x 3.x DragonBonesCPP for quick-cocos2d-x 已知的问题：\nDragonBones Design Panel 可以导出多种资源格式，但 DragonBonesCPP 仅支持 XML+PNG 格式； 有一些内存泄露； 没有做异步资源加载，可能会导致在加载资源时候的性能问题。 更进一步 DragonBones 开发组将继续更新该库，解决现有问题，并保证对 cocos2d-x 引擎的兼容。\n喜欢用 Flash 编辑骨骼动画（或者讨厌用CCS编辑骨骼动画）的同学们，是时候了！\n","date":"2014-04-28","description":"","lastmod":"2014-04-28T09:00:22Z","slug":"dragonbonescpp_for_cocos2d-x","tags":["cpp","cocos2d-x","dragonbones","lua","skeletalanimation"],"title":"DragonBones 官方C++版本 for cocos2d-x","url":"https://blog.zengrong.net/post/dragonbonescpp_for_cocos2d-x/"},{"categories":["technology"],"content":"在 cocos2d-x 中获取纹理的像素值\nHow to get a pixel in cocos2d-x?\n2014-10-24更新： 解决内存泄露以及越界问题。由于 quick 目前的版本分支混乱，这些修改不会提交到 quick 了。请自行参照下面的方法修改。\n本文基于cocos2d-x 2.2.3\n项目需要一个功能，就是在点击某个不规则边缘图片的时候，不响应图片的透明部分。\n以前在 AS3 中处理类似需求的时候，就是获取点击点的像素值，得到 Alpha 的值，然后根据 Alpha 的值来判断是否需要响应。\n但在 cocos2d-x 中，有一些问题。\ncocos2d-x 的渲染流程是这样的：\n载入一张图片，将它解析成 CCImage 对象； 根据 CCImage 对象，建立 CCTexture2D 对象； 在 CCTexturte2D 对象中，将 CCImage 对象中的定点和像素信息上传到显卡缓存； 删除 CCImage 对象。 从上面的流程可以看出，其实我们在看到这张显示在 cocos2d-x 中的图片时，图像的像素信息已经不存在 CCTexture2D 中了。\n那么，怎么得到这些像素值呢？\n一、预先存储的方法 下面两篇文章采用了预先存储的方法：\n在cocos2d-x中如何判断CCSprite上的点是否透明 cocos2d-x像素点击事件：精灵【FDPixelSprite】 这种方法的原理就是修改 CCTexture2D ，使其在处理 CCImage 的时候将需要的像素信息缓存下来。\n这种方法的弊端如下：\n会占用内存来保存像素信息，如果保存所有的像素信息，则占用的内存还相当大； 需要修改 CCTexture2D。 二、使用 CCTexture2DMutable 下面两篇文章中都提到了这个类：\ngetPixel setPixel from CCTexture2D Fast set/getPixel for an opengl texture? 这个类继承了 CCTextuer2D，并在内部实现了对像素信息的缓存。如果要实现绘图等功能，这个类倒是挺有用的。\n另外，在 cocos2d-x 中的 extensions/CCArmature 扩展中也带有这个类。\n这种方法的弊端如下：\n与 CCTextureCache 和 CCSprite 等常用类不兼容； 占用内存保存像素信息。 三、重绘图片取得像素 我采用的是这种办法。流程如下：\n在需要图片像素值的时候，将这张图片使用 FrameBuffer 重新绘制成像素； 获得相关像素的颜色值； 删除已经获得的像素。 这种方法的弊端如下：\n如果要取得的像素图片巨大，可能对性能有影响； 每次的数据没有缓存，频繁执行的话性能消耗巨大。 当然，如果确实需要在同一张图片上多次操作，缓存可以程序员自己来做。\n为了实现这个流程，我修改了 CCImage.h，增加了两个方法 getColor4B 和 getColor4F ：\n1ccColor4B getColor4B(float x, float y) 2{ 3\tccColor4B color = { 0, 0, 0, 0 }; 4\tint ix = (int)x - 1; 5\tint iy = (int)y - 1; 6\tm_pData += (iy*getWidth() + ix) * 4; 7\tcolor.r = *(m_pData++); 8\tcolor.g = *(m_pData++); 9\tcolor.b = *(m_pData++); 10\tcolor.a = *(m_pData++); 11\treturn color; 12}; 13 14ccColor4F getColor4F(float x, float y) 15{ 16\treturn ccc4FFromccc4B(getColor4B(x, y)); 17}; 2014-10-24更新：上面的代码没有考虑越界问题，在传递的坐标不在图像中时，程序会崩溃。\n最新的代码改正了问题，请参考 github 。\n由于 CCImage 是跨平台实现的，所以放在头文件中比放在实现文件中要方便许多。否则，就需要在 CCImage 的若干个平台相关实现中分别执行实现了。\n下面是 quick-cocos2d-x 中的实现代码，我将其放在了 CCSpriteExtned.lua 框架中，这样能让所有的 CCSprite 实例都支持这个方法。\n具体的实现请看代码，不解释了。\n1-- NOTE!!! The method is very slowly! Please use it in carefully. 2-- @param __point A coordinate for color. 3-- @param __convertToNodeSpace Optional, default is true, convert a coordinate to node space from world space. 4-- @param __isFloat Optional, default is false, convert a coordinate to node space from world space. 5function CCSpriteExtend:getColor(__point, __convertToNodeSpace, __isFloat) 6\tif __convertToNodeSpace == nil then 7\t__convertToNodeSpace = true 8\tend 9\tif __convertToNodeSpace then 10\t__point = self:convertToNodeSpace(__point) 11\tend 12\t-- Create a new Texture to get the pixel datas. 13\tlocal __size = self:getContentSize() 14\tlocal __rt = CCRenderTexture:create(__size.width, __size.height) 15\t-- Hold the old anchor and position to restore it late on. 16\tlocal __oldAnchor = self:getAnchorPoint() 17\tlocal __oldPos = self:getPositionInCCPoint() 18\t-- Move the sprite to left bottom. 19\tself:align(display.LEFT_BOTTOM, 0,0) 20\t--print(\u0026#34;getColor:\u0026#34;, __point.x, __point.y, __size.width, __size.height) 21\t-- Render the sprite to get a new texture. 22\t__rt:begin(); 23\tself:visit() 24\t__rt:endToLua(); 25\t-- Restore the original anchor and position. 26\tself:setAnchorPoint(__oldAnchor) 27\tself:setPosition(__oldPos) 28\tlocal __img = __rt:newCCImage(false) 29\tlocal __color = nil 30\tif __isFloat then 31\t__color = __img:getColor4F(__point.x, __point.y) 32\telse 33\t__color = __img:getColor4B(__point.x, __point.y) 34\tend 35\treturn __color, __rt 36end 37 38-- Only get a alpha value. 39function CCSpriteExtend:getColorAlpha(__point, __convertToNodeSpace, __isFloat) 40\tlocal color = self:getColor(__point, __convertToNodeSpace, __isFloat) 41\treturn color.a 42en 这个方法已经合并进入 quick-cocos2d-x 的 develop 分支。\n2014-10-24更新：由于 newCCImage 方法在 C++ 中是请求堆内存并返回一个指针。因此必须手动释放。上面的代码没有考虑释放问题，将会导致内存泄露。\n最新的代码改正了问题，请参考 github 。\n如有问题，请留言。\n","date":"2014-04-23","description":"","lastmod":"2014-10-24T10:45:22Z","slug":"how_to_get_a_pixel_in_cocos2d-x","tags":["cpp","cocos2d-x","howto","lua"],"title":"在 cocos2d-x 中获取纹理的像素值","url":"https://blog.zengrong.net/post/how_to_get_a_pixel_in_cocos2d-x/"},{"categories":["technology"],"content":"如何学习 cocos2d-x ？\nHow to study cocos2d-x?\n这是我在之乎上的一个回答，原文在这里(有修改)：如何学习一种开发框架（如：手机开发cocos2dx方向）？\n刚巧我也是从AS3转到了cocos2d-x，说说我的经验吧。\n一、 cocos2d-x 部分 1.1 技术选型 从第三个手游开始，我实在被Adobe的ANE和国内的小平台折腾得没了脾气，决定转到cocos2d-x。在选择哪种语言绑定的时候纠结了很久，最后力排众议选了 lua：Cocos2d-x script language binding:Lua or Javascript? 。cocos2d-x 2.x 的lua绑定做得并不好，于是我选择了 quick-cocos2d-x 。\n1.2 学习 C++ 《C++ Primer》 是不错的入门书籍，建议看第五版，我写过一篇 C++Primer 第4版和第5版比较 。学习期间建议画一些思维导图帮助理解和整理思路。例如这个（不完整）：\n1.3 学习DEMO和熟悉开发平台。 编译 cocos2d-x 自带的 TestCPP 项目，全部跑一遍。熟悉 cocos2d-x API 的用法。记得生成文档，建议每个API的功能都浏览一遍。\n在学习DEMO的过程中，你还必须熟悉自己开发平台的IDE，例如Windows上必须熟悉 Visual Studio，而OS X上必须熟悉xcode，Linux平台上就熟悉Eclipse+CDT吧。\n注意，quick-cocos2d-x 是不支持Linux开发平台的。\n1.4 了解引擎的文件夹结构 基于 cocos2d-x 源码生成的文档并不怎么详细，许多功能必须看源码。但这个阶段，我不建议纠结源码太深，倒是可以纠结一下 cocos2d-x 的文件夹结构，看看各个类放在什么地方，找一找常见的哪些宏和常量以及枚举定义在什么地方，这花不了多少时间，但能让你对cocos2d-x有更深刻的了解，同时给你很强的成就感。这种成就感冲淡了你面对大量源码时候的无力感，让你能够继续前行。\n下面是我的分析：\n1.5 重复上面的第3步：再次学习DEMO 这时候看DEMO可能会轻松不少，但是你会有更多的问题去纠结。例如多分辨率支持？例如坐标系统？例如绘图功能？例如层级管理系统？例如事件传递系统？等等等等……这时候可以去Google（注意不要用百毒和其他搜索引擎）找文档看了。我推荐一些我看过的不错的文档和博客：\nCocos2d-x 多分辨率适配完全解析 Cocos2d-x官方中文文档 v2.x 子龙山人 - 博客园 红孩儿的游戏编程之路 Cocos2D | iOS Development Tips \u0026amp; Tricks by BiOM Ray Wenderlich Cocos2d-x | Cross Platform Open Source 2D Game Engine 许多博客都是极好的。红孩儿 写了许多源码分析，每一句源码都加了详细注释。虽然我不太认同他这种吃力不讨好的方法，但这对于新手来说确实是有很大帮助的。\n另外在 Stack Overflow 你能找到绝大部分问题的答案。当然，许多问题是针对 cocos2d 而非 cocos2d-x 的，不过用法相同，照看不误。许多优秀的文章都是基于 cocos2d 的，所以，不要介意，可以先花半天时间熟悉 OC 的语法，能看懂即可。\n暂时不要买中文书。我曾经花3天时间看完了 《Cocos2D-X游戏开发技术精解》 和 《cocos2d-x手机游戏开发--跨iOS、Android和沃Phone平台》 ，发现作者其实就是来赚稿费的。\n忘了广告了，我的博客（cocos2d-x | zrong's Blog）也有一些cocos2d-x内容，欢迎来喷。\n1.6 熟悉工具集 现在你应该对周边工具感兴趣了。例如帧动画使用什么制作？BMFont使用什么制作？骨骼动画呢？plist文件怎么编辑？碎图用什么工具拼合？有些项目上，你有许多选择，也可能没得选。去Google吧，如果遇到选择上的问题，欢迎找我讨论。\n二、 quick-cocos2d-x 部分 2.1 现在就到lua时间了 请认真读完 《Lua程序设计(第2版) 》 第一、二、三部分。第四部分可暂时不读。\n2.2 熟悉 quick-cocos2d-x 的文件夹结构 相信有了上面 cocos2d-x 的基础，这个应该不难理解。\n2.3 跑完 quick-cocos2d-x 自带的所有sample 在这期间，熟悉 framework 中的所有封装。可以参考 quick网站 和 quick wiki 。\n2.4 学习导出API给Lua使用 导出 C/C++ API 给 Lua 使用\n三、学习 OpenGL ES 熟悉Lua部分之后，作为一个有志于成为程序员的码农，依然要回到C++来，这里才是 cocos2d-x 的本质。\ncocos2d-x 使用 OpenGL ES 进行渲染的，如果要对 cocos2d-x 的渲染层进行任何形式的扩展，你完全避不开 OpenGL ES。\n既然逃不开，那就对她说，请张开双腿，我要上。\n3.1 阅读 OpenGL ES 2.0 Programming Guide 下面是这本书的官网，目前已经出了 OpenGL ES 3.0 版本，cocos2d-x 使用的是2.0。\nhttp://www.opengles-book.com/es2/index.html\n这本书写得浅显易懂，非常适合新手。有位网友花3个月时间翻译了中文版，但还是建议你不要看了。\n这里是 OpenGL ES 的 官方文档 和 API中文翻译 。\n当然，你也可以买那本著名的 OpenGL 红宝书 《OpenGL编程指南（原书第7版）》 来看，不过 OpenGL ES 相对与 OpenGL 来说还是有一些不同的，你要知道如何区分这些不同。\n3.2 尝试理解 cocos2d-x 的渲染部分架构 相关的类我就不列出了，我正在酝酿一个这方面的系列文章准备发到博客上。如果写完了，我会在这里更新。\n3.3 自己写一些滤镜、绘图功能的扩展 例如这个： cocos2d-x-filters\n四、回归项目 到了这里，你可以开始你的项目了。虽然源码还没有读完，但相信整个架构都能理解了。在做项目的过程中，再去一点点读源码是比较快捷和我能够接受的方法，否则就太枯燥了。\n你可以还需要选择一下cocos2d-x相关的工具。你可能听说过 CCB（cocosBuilder）和CCS（cocoStudio），尝试了解她们，再决定是否使用她们。\n你已经有了AS3的基础，这样许多前端的概念和经验已经很丰富了。这些经验在cocos2d-x中同样适用。但是要注意的是，cocos2d-x不是AS3，不要试图用AS3的方法去使用cocos2d-x。\n有位码农在从C#转到AS3的时候向我抱怨：为什么Flash Builder没有Visual Studio的这个功能那个功能？ 然后这位码农在从AS3转到Sublime Text（用于Lua编辑）的时候向我抱怨，为什么Sublime Text没有Flash Builder的这个功能那个功能？ 上面是个笑话。\n同时，为了解决一些cocos2d-x没有提供的功能，你必须去学习相关的知识。例如这个：在 cocos2d-x 中使用 libcurl 实现上传文件功能（附quick-cocos2d-x封装） 。不过你现在已经有了基础，学习这些不是难事。\n在有时间的时候，可认真读一下上面提到的《Lua程序设计(第2版) 》第四部分，了解Lua和C语言通信的方式，顺便看看cocos2d-x里面是怎么实现的。\n另外，一些你需要的引擎中没有的功能，也可以顺手实现了开源出来（例如这个： 一个LuaSocket封装 ），说不定你需要的东西正好是别人也需要的。\n五、熟悉发布平台 既然是做手游，Android和iOS平台是逃不掉了。\n你要熟悉Android和iOS平台的特点，熟悉JAVA语言在Android上的应用，熟悉Objective-C在iOS上的应用，熟悉Eclipse ADT和XCode（上面应该已经熟悉过了）。\n你还要熟悉Google Play和AppStore的发布流程以及方针、政策，内购的接入、SDK的嵌入。\n你要学习和各个接入平台的程序员、商务、前台小妹打交道（因为经常会找不到你要找的真正的负责该SDK的技术人员），你要能忍受国内小平台糟糕的文档、代码规范和逆天的编程方法。\n终、没有终点 上面说的并不完整，应该遗漏了不少东西，但我只能说这么多了。因为我的经验也止于此。\n祝你成功！\n","date":"2014-04-23","description":"","lastmod":"2014-04-23T03:04:24Z","slug":"how_to_study_cocos2d-x","tags":["cpp","cocos2d-x","howto","study"],"title":"如何学习 cocos2d-x ？","url":"https://blog.zengrong.net/post/how_to_study_cocos2d-x/"},{"categories":["technology"],"content":"2015-09-06 更新： 加入 删除子模块 的内容。\nDragonBonesCPP 是一个包含子模块的库，在 clone/commit/push 的时候需要一些额外的操作。\n本文将描述这些操作。\n子模块(submodule) 限(wei)于(le)篇(tou)幅(lan)，请自行学习下面的内容：\nGit Submodule使用完整教程 6.6 Git Tools - Submodules git-submodule(1) Manual Page 子模块 clone DragonBonesCPP 蛋碎方法一 1git clone --recursive git@github.com:DragonBones/DragonBonesCPP.git 使用这个方法，将会自动 clone 该项目下的所有子模块。这其实是最简单的方法了。\n为什么说它令人蛋碎呢？因为 DragoneBones 中包含的 engines/cocos2d-x 子模块也包含子模块。\n这样就出现了嵌套子模块，一个 cocos2d-x 的所有历史加起来就超过1G了，再加上子模块的所有历史，这就……。\n蛋疼方法二 为了避免蛋碎的问题，我们换一个方法，可是蛋疼是免不了了。\n1# git clone git@github.com:DragonBones/DragonBonesCPP.git 2Cloning into \u0026#39;DragonBonesCPP\u0026#39;... 3remote: Counting objects: 103, done. 4remote: Compressing objects: 100% (87/87), done. 5remote: Total 103 (delta 18), reused 96 (delta 13) 6Receiving objects: 100% (103/103), 88.96 KiB | 47 KiB/s, done. 7Resolving deltas: 100% (18/18), done. 此时已经将所有 DragonBonesCPP 的核心代码 clone 下来了，由于项目不大，这个过程会非常快。\n但是子模块不会自动 clone，接下来需要设置子模块。\n1# cd DragonBonesCPP 2# DragonBonesCPP git:(master) \u0026gt; git submodule status 3-a7709c35badbd930f98adaf1c172a3251f8c6543 engines/cocos2d-x refid 前面的减号 - 代表子模块还没有加载，我们需要初始化并检出它：\n1# DragonBonesCPP git:(master) \u0026gt; git submodule init 2Submodule \u0026#39;cocos2d-x\u0026#39; (git@github.com:DragonBones/cocos2d-x.git) registered for path \u0026#39;engines/cocos2d-x\u0026#39; 3 4# DragonBonesCPP git:(master) \u0026gt; git submodule update 5Cloning into \u0026#39;engines/cocos2d-x\u0026#39;... 6remote: Reusing existing pack: 200502, done. 7Receiving objects: 100% (200502/200502), 763.78 MiB | 98 KiB/s, done. 8Resolving deltas: 100% (133006/133006), done. 9Submodule path \u0026#39;engines/cocos2d-x\u0026#39;: checked out \u0026#39;a7709c35badbd930f98adaf1c172a32 51f8c6543\u0026#39; 等待是漫长的，看我的网速就知道这到底花了多长时间。\n和蛋碎方法不同的是，这种方法不会 clone cocos2d-x 中的子模块（嵌套子模块）。若有需要，可以进入 cocos2d-x 中再次执行蛋疼方法二。\n分支 DragonBonesCPP 的分支介绍 DragonBonesCPP 项目目前有2个分支，master 和 dev ，区别如下：\nmaster 分支中包含的内容是稳定的或者基于稳定版本的引擎。 dev 分支中包含的内容是目前正在开发的功能或者基于开发版本的引擎。 以对 cocos2d-x 引擎的支持为例：\nmaster 分支包含对 cocos2d-x 2.x 版本的支持； dev 分支包含对 cocos2d-x 3.x 版本的支持。 切换分支 在开发的过程中，我们经常需要切换分支。例如对 cocos2d-x 2.x 的相关功能进行修改后（位于master分支），希望切换到 cocos2d-x 3.x 的另一些功能进行修改。\n这种情况下，我们一般使用 git checkout 来切换分支。于是，奇怪的事情发生了：\n1# DragonBonesCPP git:(master) \u0026gt; git checkout dev 2M engines/cocos2d-x 3Switched to branch \u0026#39;dev\u0026#39; 4# DragonBonesCPP git:(dev) \u0026gt; git status 5 On branch dev 6 Changes not staged for commit: 7 (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to update what will be committed) 8 (use \u0026#34;git checkout -- \u0026lt;file\u0026gt;...\u0026#34; to discard changes in working directory) 9 10 modified: engines/cocos2d-x (new commits) 11 12no changes added to commit (use \u0026#34;git add\u0026#34; and/or \u0026#34;git commit -a\u0026#34;) 显然我们切换分支后还没有对dev进行修改，但dev分支中已经出现了一个 modified!\ndiff 看看有什么不同：\n1DragonBonesCPP git:(dev) \u0026gt; git diff 2diff --git a/engines/cocos2d-x b/engines/cocos2d-x 3index 123b410..a7709c3 160000 4thumbnail = \u0026#34;/uploads/2014/04/gitsubmodule1.png\u0026#34; 5+++ b/engines/cocos2d-x 6@@ -1 +1 @@ 7-Subproject commit 123b410b99510e4a33ceadc08abc09d292f5df49 8+Subproject commit a7709c35badbd930f98adaf1c172a3251f8c6543 从比较的结果看来，子模块指向的 refid 发生了变化，从 123b410... 变成了 a7709c3... 。\nwwwwait! 这两个id怎么看着那么眼熟捏？原来，他们分别是 master 和 dev 分支中的 cocos2d-x 子模块指向的 refid。见下图：\n也就是说，虽然从 master 切换到了 dev 分支，但是子模块 cocos2d-x 中 HEAD 指向的 refid 并没有跟着改变到 dev 分支中应有的指向。\n为了进一步证明这个推测，我们可以进入子模块 cocos2d-x 中查看所处分支： （注意下面的操作位于子模块 cocos2d-x 中）\n1# DragonBonesCPP git:(dev) \u0026gt; cd engines/cocos2d-x 2# cocos2d-x git:(master) \u0026gt; git status 3# On branch master 4nothing to commit, working directory clean 注意，在这里可以看到，我们并不是位于我们期望的 cocos2d-x 的 develop 分支（这是 cocos2d-x 3.x的所在分支），而是依然位于 master（cocos2d-x 2.x的所在分支） 分支。\n知道了原因，切换起来就简单了。我们只需要进入 cocos2d-x 子模块，将现有的指向改为 cocos2d-x 的 develop 分支即可。\n1# cocos2d-x git:(master) \u0026gt; git checkout develop 2Switched to branch \u0026#39;develop\u0026#39; 3 4# cocos2d-x git:(develop) cd ../../ 5# DragonBonesCPP git:(dev) git status 6# On branch dev 7# Your branch is ahead of \u0026#39;origin/dev\u0026#39; by 3 commits. 8# (use \u0026#34;git push\u0026#34; to publish your local commits) 9# 10nothing to commit, working directory clean 看吧，没有提交，世界清静了。\n合并分支 由于我们使用了分支来区分不同的版本，因此在合并的时候，必须做一些处理确保正常。\n例如，对于 renderer 中的内容，一定是基于分支独立的。它们不应该在合并的时候被处理。\nmaster 分支中的 renderer ，与 dev 分支中的 renderer 就可能完全不同，我们不希望在将 master 分支合并到 dev 分支（或者相反）的时候，导致完全不同的两个渲染器互相冲突； 而 library 中的内容，则恰恰相反，它们需要在分支之间合并。 gitattributes 文件可以解决这个问题，可参阅这里： Merge-Strategies 。\n为了解决这个矛盾，我已在 .gitattributes 中将 renderer 和 engines 目录排除，在合并的时候，它们将保存各自的版本。\n若有更深入的需求，例如 renderer 中的某些渲染器需要合并而有些不要，则后续进一步处理。\n提交和推送 在提交 DragonBonesCPP 之前，若已经修改过子模块，请务必先提交子模块的修改并推送子模块，然后再提交并推送 DragonBonesCPP 。\n删除子模块 为了方便查询，这里摘抄 Git Submodule使用完整教程 一文中的移除 Submodule 的方法如下：\n1. 删除 git cache 和物理文件夹 以删除 engines/cocos2d-x 为例：\n1git rm -r --cached engines/cocos2d-x/ 2rm -rf engines/cocos2d-x/ 2. 修改配置文件 删除 .gitmodules 文件中对应的配置内容； 删除 .git/config 中的对应 submodule remote 的内容。 3. 提交更改 （全文完）\n","date":"2014-04-19","description":"","lastmod":"2015-09-06T09:59:20Z","slug":"manage_a_git_library_with_submodules","tags":["cpp","dragonbones","git","skeletalanimation"],"title":"管理带有子模块的git库（以 DragonBonesCPP 为例）","url":"https://blog.zengrong.net/post/manage_a_git_library_with_submodules/"},{"categories":["technology"],"content":"解决Github访问超慢问题\nGithub is so slowly.\n这段时间访问 github 都非常慢，google了一下发现是github某个CDN被伟大的墙屏蔽所致。\n出问题的应该是这个CDN: github.global.ssl.fastly.net，有图为证:\n解决方法就是使用万能的host文件，将这个域名映射到它所在的ip。\n访问 IPAddress.com 使用 IP Lookup 工具获得这个域名的ip地址（注意，该网站可能需要梯子）； 也可以直接点击 这个地址 查看，结果如下:\n撰写本文时，该ip地址为：185.31.17.185 ； 修改host文件； 刷新本机DNS缓存。 参考文章：\ngithub加载过慢 github访问速度慢，样式加载不完全解决 ","date":"2014-04-19","description":"","lastmod":"2014-04-19T02:44:49Z","slug":"github_is_slowly","tags":["git","howto","network"],"title":"解决Github访问超慢问题","url":"https://blog.zengrong.net/post/github_is_slowly/"},{"categories":["technology"],"content":"从 Actionscript 1.0 迁移到 Actionscript 3.0\nMigrate Actionscript 1.0 to Actionscript 3.0\n这并不是一篇好几年前的老文章，而是我去年10月份在之乎上的一个回答，今天有知友回复让我看到这篇，于是把她转到自己的博客上来。\n原文见这里：AS1.0/2.0 改写成 AS3.0 难吗？一款中型的flash游戏改写，大概需要多少时间和成本？\n做为一个从AS1一路写到AS3的程序员，我来回答一下。\n首先需要声明的是，关于难不难，每个人有自己的判断标准，这个我不会回答。 其次需要声明的是，我也无法回答大概需要多少时间和成本，但看完我的回答后，你应该能自己计算出来。 AS从1到2，其实没有什么本质的改变，AS2.0只是加入了一个似是而非的class关键字而已，最终编译成的字节码，还是一样的。\n而AS3就完全不同，从FlashPlayer9（第一个支持AS3的Player）开始，FlashPlayer就内置了2套AVM虚拟机，AVM1针对AS1+2，AVM2针对AS3。AS3甚至专门加了一个类AVM1Movie来处理AS1+2的MC，看这个： AVM1Movie - Adobe ActionScript? 3 (AS3 ) API 参考 。我认为，即使说AS3是另外一门语言也不为过。\n个人升级经历 我在从AS1转到AS2的时候，没什么太难的感觉。当时只是深入学了一些面向对象编程概念和设计模式知识，这些概念都是面向对象编程必学的内容，是放之语言四海皆准的准则，对于有大量项目经验的人来说简直就是让你高端大气上档次的非学不可的必要知识，与语言本身倒没有太大关系。\n可是，从AS1+2转到AS3，我就彻底崩溃了。这TMD完全就是一门不同的语言嘛！用法变了不说，和AS2的面向对象完全两码事，以前写的类绝对是不能用的；以前的编程经验，技巧、语法糖大部分都无效了啊啊啊。而且AS3已经完全面向对象，对习惯在时间轴上写代码的 普通、文艺闪客 来说，是非常非常的不友好了。\n我认识的AS程序员（或者叫社稷师更好）中，不愿意转向AS3的不少，还有朋友一直到现在都停留在AS1中，依然在时间轴上写代码；依然认为 addEventListener 神马的是很讨厌的事情，依然用 mc._x = 100 用得不亦知乎。\n当然，作为一个有理想无底线的 2B闪客 ，迁移到AS3是必须的。下面是几个迁移中涉及的方面。\n工具层面 AS1的工具，只有Flash IDE了。实际上，Actionscript这个名字，也是从Flash 5开始才有的，以前在Flash 4及以前，这门语言是没有名字的。\n而到了AS2，就可以增加一个选择—— MTASC 。这是个速度超快的编译器，可以把AS2源码编译成 SWF bytecode。是的，它比Flash IDE的编译要快很多很多。\n但是，MTASC的问题在于它并非是Adobe制作的，它是一个第三方工具。但即使是这样，它也成了最好的AS1/AS2编译器。它为不使用Flash设计工具，纯粹使用文本编辑器生成SWF文件提供了可能。也为纯粹的程序员加入到SWF制作队伍铺平了道路。\n到了AS3时代，由于Flex SDK的出现，我们可以使用MXMLC这个编译器来把AS3源文件直接编译成SWF格式。我们已经可以完全抛弃Flash IDE工具了，完全用代码来制作SWF文件。我们可以忘掉图像元件、按钮元件、影片剪辑、时间轴、Library和Scene，我们可以不在时间轴上制作补间，不用再考虑gotoAndPlay()或者stop()，不必在时间轴上放代码，也不必在库中“导出元件”了。\n上面的转变大么？真的很大。你希望从AS1/2转到AS3，就要习惯这些工具的变化，同样也要习惯从“社稷师”到“程序猿/媛”的变化。\n语言层面 这块说起来就大了，我举个实例吧。\n下面是一段 AS1 代码：\n1_root.arrow1._x = v.p1.x*game.step; 2_root.arrow1._y = v.p1.y*game.step; 3_root.arrow1._rotation = 180/Math.PI*Math.atan2(v.vy, v.vx); 下面是迁移到 AS3 的代码：\n1_arrow1.x = _dragger1.x; 2_arrow1.y = _dragger1.y; 3_arrow1.rotation = 180 / Math.PI * Math.atan2(_v.y, _v.x); 嗯，看起来很像。但AS1在属性前面多了下划线，还有就是多了无处不在的 _root 。\n如果只说下划线，那么有许多属性，其实本来就没有下划线……在AS1中，有的有，有的没有，而在AS3中，统一都没有……\n上面的代码来自于这里：https://github.com/zrong/as3-vector2d/tree/master/sample2-normals 其中AS1的部分在这里：https://github.com/zrong/as3-vector2d/tree/master/sample2-normals/legacy AS3的部分就是这个文件：https://github.com/zrong/as3-vector2d/blob/master/sample2-normals/src/Vector2dSample2.as\n下划线只是很小很小的一部分，另一个很重要的问题，就是类的变化。\nAS1是基于ECMAScript标准的，与JS的语法几乎完全一致，是在JS的基础上加入了一些Flash特有的类（例如MovieClip等等），可用的功能比较少。\n而AS3其实是增加了非常多的类，许多类与AS1名称一致，但功能已经大不相同。例如XML类，在AS3中与AS1中的XML已经不是同样的功能。AS1中的XML在AS3中被重命名为XMLNode。\n最大的差别，是编程习惯上的。上面讲到，设计师们习惯在时间轴上写代码，在Flash IDE中，也可以直接在元件上写代码，这种灵活和可视化的方式，让设计师们很高兴。\n可是程序员们不高兴。因为代码放得太分散，往往很难找。我不知道一段代码是在哪个MC中的某个MC中的其中一个按钮的实例上，还是在某个MC的某个MC的某个帧上？这完全不是程序员们的style。\nAS3加入了强类型，也加入了包、类等面向对象风格，但设计师们不买账。可是如果还是用以前的做法，程序员们又不买账。\n分裂开始了。\n多个迁移的例子 最近我把TONYPA写的 Vectors for Flash 教程中提供的范例从Flash 5迁移到了Actionscript 3。没有fla文件，顿时整个世界清静了。\n如果你更喜欢直接看例子的话，上面的废话都不用看。\nlegacy文件夹中的fla文件，是用Flash 5制作的。而src文件夹中的内容，是用Actionscript 3写成。\n你一定要习惯没有fla文件的生活。\n例子见这里：https://github.com/zrong/as3-vector2d\n","date":"2014-04-16","description":"","lastmod":"2014-04-16T06:01:10Z","slug":"migrate_as1_to_as3","tags":["actionscript","adobe","flash","study","fromto"],"title":"从 Actionscript 1.0 迁移到 Actionscript 3.0","url":"https://blog.zengrong.net/post/migrate_as1_to_as3/"},{"categories":["technology"],"content":"在 cocos2d-x 中使用 libcurl 实现上传文件功能（附quick-cocos2d-x封装）\nUpload a file use libcurl in cocos2d-x.\n本文基于 cocos2d-x 2.2.2 和 quick-cocos2d-x zrong修改版 3be9b8\n目前做的项目中需要实现b截图分享功能，我的设计思路是使用 CCRenderTexture 来截图，并通过HTTP上传到截图分享服务器。\n通过查看 cocos2d-x 源码，我发现 cocos2d-x 封装了一个 CCHttpClient 类，用于调用 libcurl 实现HTTP协议通信。不过并没有实现文件上传功能。\n但 quick-cocos2d-x 不知道什么原因，删除了这个 CHttpClient 实现，而改用了 CCHTTPRequest 类，内部封装的依然是 libcurl ，但也依然没有实现文件上传功能。\n既然都是封装 libcurl ，我们完全完全为现有的类扩展上传文件功能。在本文中，我基于 quick-cocos2d-x 的 CCHTTPRequest 类进行了扩展。cocos2d-x 如果需要扩展，方法也类似。\n如果不愿意了解具体的封装过程，请直接clone我封装好的版本：quick-cocos2d-x forked by zrong\ncURL 的文档 学习 cURL 最好的文档自然在 cURL 的官网 ，这里能找到所有的官方文档以及推荐，这些文档都写得不错。\n如果来不及想看API，可以直接戳这里：Using the libcurl C Interface 。\n直接看源码则是最快的上手方式：libcurl - small example snippets 。\nphp 中的 cURL 函数 也是不错的学习材料。优点就是有中文资源，缺点就是一些地方和标准 libcurl 库名称和用法不太相同。\n表单上传用法 如果单纯使用 libcurl 来上传，那么官方的sample中就提供了具体的用法。戳这里：postit2.c。\n因为有了上面的sample，我这里也就不再画蛇添足地演示整个流程了。我只说自认为重要（意思就是我犯过错）的地方吧。\n在这个sample中，我们采用的是表单上传，因此采用了 curl_formadd 方法来构建表单。\n我的表单是这样的：\n1\u0026lt;form method=\u0026#34;post\u0026#34; enctype=\u0026#34;multipart/form-data\u0026#34;\u0026gt; 2\t\u0026lt;input id=\u0026#34;filepath\u0026#34; name=\u0026#34;filepath\u0026#34; type=\u0026#34;file\u0026#34; /\u0026gt; 3\t\u0026lt;input type=\u0026#34;submit\u0026#34; value=\u0026#34;upload\u0026#34; /\u0026gt; 4\t\u0026lt;input name=\u0026#34;act\u0026#34; type=\u0026#34;hidden\u0026#34; value=\u0026#34;upload\u0026#34; /\u0026gt; 5\u0026lt;/form\u0026gt; 在这个表单中，我一共给出了2个变量：\nfilepath 指定要上传的文件； act 是个要传递的变量，值是 upload。 相应的，我的封装是这样的（省略了curl的初始化和变量定义，省略了curl的调用和资源的销毁。具体的流程看上面提到的 postit2.c）：\n1void CCHTTPRequest::setUploadFile(const char *filepath) 2{ 3\tcurl_formadd(\u0026amp;m_formPost, \u0026amp;m_lastPost, 4\tCURLFORM_COPYNAME, \u0026#34;filepath\u0026#34;, 5\tCURLFORM_FILE, filepath, 6\tCURLFORM_CONTENTTYPE, \u0026#34;Image/jpeg\u0026#34;, 7\tCURLFORM_END); 8\tcurl_formadd(\u0026amp;m_formPost, \u0026amp;m_lastPost, 9\tCURLFORM_COPYNAME, \u0026#34;act\u0026#34;, 10\tCURLFORM_COPYCONTENTS, \u0026#34;upload\u0026#34;, 11\tCURLFORM_END); 12\tcurl_easy_setopt(m_curl, CURLOPT_HTTPPOST, m_formPost); 13} 简单点说，我需要把表单中使用到的所有变量通过 curl_formadd 命令添加到表单中，然后使用 curl_easy_setopt 将表单内容传递给curl。\n这两个API的说明：\ncurl_formadd curl_easy_setopt 在使用 CURLFORM_FILE 来传递值的时候，可以一起再设定一个 CURLFORM_CONTENTTYPE 指定上传的文件类型(Content-Type/Mime-Type)。常见的取值可以在这里找到： Content-Type/Mime-Type 。\n扩展 CCHTTPRequest 根据上面的介绍，我对 quick-cocos2d-x 中包含的 CCHTTPRequest 进行了扩展，使其可以支持上传文件。\n在 CCHTTPRequest.h 文件中，定义两个成员和两个方法：\n1//...... 2private: 3\tcurl_httppost *m_formPost; 4\tcurl_httppost *m_lastPost; 5public: 6\t//用于设置上传文件名以及文件类型 7\tvoid addFormFile(const char *name, const char *filePath, const char *fileType=\u0026#34;application/octet-stream\u0026#34;); 8\t//用于增加附加的表单变量 9\tvoid addFormContents(const char *name, const char *value); 10//...... 在 CCHTTPRequest.cpp 文件中，实现着这个方法：\n1void CCHTTPRequest::addFormFile(const char *name, const char *filePath, const char *contentType) 2{ 3\tcurl_formadd(\u0026amp;m_formPost, \u0026amp;m_lastPost, 4\tCURLFORM_COPYNAME, name, 5\tCURLFORM_FILE, filePath, 6\tCURLFORM_CONTENTTYPE, contentType, 7\tCURLFORM_END); 8} 9 10void CCHTTPRequest::addFormContents(const char *name, const char *value) 11{ 12\tcurl_formadd(\u0026amp;m_formPost, \u0026amp;m_lastPost, 13\tCURLFORM_COPYNAME, name, 14\tCURLFORM_COPYCONTENTS, value, 15\tCURLFORM_END); 16} 找到 onRequest 方法的定义，在 curl_easy_perform 之前，加入这句代码：\n1if (m_formPost) 2{ 3\tcurl_easy_setopt(m_curl, CURLOPT_HTTPPOST, m_formPost); 4} 还是在 onRequest 方法中，在 curl_easy_clearup 之后，加入这句代码：\n1if (m_formPost) 2{ 3\tcurl_formfree(m_formPost); 4\tm_formPost = NULL; 5} 导出到 lua CCHTTPRequest 并没有单独的tolua文件。所有位于 lib/cocos2d-x/external/extra 包中的功能的导出都集中在 lib/cocos2d-x/external/extra/luabinding/cocos2dx_extra_luabinding.tolua 中。关于这点，我足足找了半小时，多亏 ChildhoodAndy 提醒，再次感谢。\n在该文件中相应的地方增加这两个要导出的方法，然后运行 lib/cocos2d-x/external/extra/luabinding/build.bat ，并重新编译 quick-x-player 即可实现导出。\n完善 network.lua 我在 network.lua 中增加了一个 uploadFile 方法，这样用起来更加方便。\n该方法定义如下：\n1-- @author zrong(zengrong.net) 2-- Creation: 2014-04-14 3-- @param callback 和 network.createHTTPRequest 的第一个参数一样。 4-- @param url 和 network.createHTTPRequest 的第二个参数一样。 5-- @param datas 包含下列值： 6-- fileFiledName（表单中的type值为file的input标签的name值）； 7-- filePath（要上传文件的绝对路径） 8-- contentType（可选，上传文件的 Content-Type。默认的值为 application/octet-stream） 9-- extra（可选，要附加到form中的变量的键值对） 10function network.uploadFile(callback, url, datas) 11\tassert(datas or datas.fileFieldName or datas.filePath, \u0026#34;Need file datas!\u0026#34;) 12\tlocal request = network.createHTTPRequest(callback, url, \u0026#34;POST\u0026#34;) 13\tlocal fileFieldName = datas.fileFieldName 14\tlocal filePath = datas.filePath 15\tlocal contentType = datas.contentType 16\tif contentType then 17\trequest:addFormFile(fileFieldName, filePath, contentType) 18\telse 19\trequest:addFormFile(fileFieldName, filePath) 20\tend 21\tif datas.extra then 22\tfor i in ipairs(datas.extra) do 23\tlocal data = datas.extra[i] 24\trequest:addFormContents(data[1], data[2]) 25\tend 26\tend 27\trequest:start() 28\treturn request 29end 使用范例：\n1network.uploadFile(function(evt) 2\tif evt.name == \u0026#34;completed\u0026#34; then 3\tlocal request = evt.request 4\tprintf(\u0026#34;REQUEST getResponseStatusCode() = %d\u0026#34;, request:getResponseStatusCode()) 5\tprintf(\u0026#34;REQUEST getResponseHeadersString() =\\n%s\u0026#34;, request:getResponseHeadersString()) 6\tprintf(\u0026#34;REQUEST getResponseDataLength() = %d\u0026#34;, request:getResponseDataLength()) 7\tprintf(\u0026#34;REQUEST getResponseString() =\\n%s\u0026#34;, request:getResponseString()) 8\tend 9\tend, 10\t\u0026#34;http://127.0.0.1/upload.php\u0026#34;, 11\t{ 12\tfileFieldName=\u0026#34;filepath\u0026#34;, 13\tfilePath=device.writablePath..\u0026#34;screen.jpg\u0026#34;, 14\tcontentType=\u0026#34;Image/jpeg\u0026#34;, 15\textra={ 16\t{\u0026#34;act\u0026#34;, \u0026#34;upload\u0026#34;}, 17\t} 18\t} 19) 已经封装好的版本，请查看： quick-cocos2d-x forked by zrong\n","date":"2014-04-14","description":"","lastmod":"2014-04-14T07:26:06Z","slug":"upload-a-file-in-quick-cocos2d-x","tags":["cpp","cocos2d-x","howto","lua"],"title":"在 cocos2d-x 中使用 libcurl 实现上传文件功能（附quick-cocos2d-x封装）","url":"https://blog.zengrong.net/post/upload-a-file-in-quick-cocos2d-x/"},{"categories":["others"],"content":"在ITX小机箱中替换CPU风扇\nReplace CPU fan in ITX computer case.\n抛弃旧情人 今年情人节那天（2014-02-14），我把用了十几年的老机箱送人，换成了 酷冷至尊(CoolerMaster)小魔方 。具体配置为：\nAMD A10 5800K Team Vulcan DDR3 2400 4GBX2 ASRock FM2A85X-ITX 硬盘和电源就沿用原来的。特别说一下10年前买的 航嘉 磐石355 电源，一直无故障用到现在，果然是坚如磐石啊 :-) 。\nAMD牌飞机发动机 虽然AMD盒装CPU自带的风扇是PWM风扇，支持智能调速（PWM风扇介绍），但降温效果并不好。在室温23度，CPU占用30%左右时，CPU温度就达到了56度。此时，风扇转速已经高达6000+RPM，风扇叶片切割空气的声音听起来好像是飞机引擎，在夜深人静的夜晚让人毛骨悚然，连键盘的敲击声都被盖过了。\n购买 九州风神（DEEPCOOL） 玄刃射手版 的时候，我比较担心的是这么大一个风扇无法装进小魔方机箱。因为AMD的原装风扇直径7厘米，而这个直径达到了12厘米。\n仔细量了机箱尺寸后，想想反正不到50块，还是入手了。\n安装新风扇 1. 开拆之前\n2. AMD牌飞机发动机果照\n为了安装风扇，只能把电源和硬盘拆掉了。\n3. CPU果照\n去掉原装风扇，擦掉硅脂。\n4. 新风扇散热片\n因为扣具太结实，只能先去掉风扇，先上散热片。\n5. 安装成功！\n6. 装回电源\n这里我就有点担心了，因为电源和风扇之间只有5毫米距离，这点距离很难满足散热的需求。\n7. 还原\n装好机箱，准备测试。\n测试效果 我的担心果然应验了。开机后虽然风扇没那么响了，但CPU温度不断上升，一会儿就超过了60度。\n72度的时候，电脑自动重启。进入 UEFI 界面检测温度继续上升，由于我开了超温保护，当CPU温度上升到75度的时候，系统自动关机了。\n打开机箱触摸散热片，感觉相当烫手，这说明CPU热量并没有正常被风扇带走。\n改装 这应该并非风扇的问题，而是电源阻挡了风扇的进风渠道。\n于是我把机箱改装成了这个样子：\n嗯，这样确实丑了点。\n不过问题终于解决了，室温25度，30%负载的情况下，CPU温度稳定在52度。\n闲置了10年的电源开关也能派上用场了，多好。\n","date":"2014-04-11","description":"","lastmod":"2014-04-11T14:27:57Z","slug":"replace_cpu_fan_in_itx_computer_case","tags":["hardware"],"title":"在ITX小机箱中替换CPU风扇","url":"https://blog.zengrong.net/post/replace_cpu_fan_in_itx_computer_case/"},{"categories":["technology"],"content":"如何升级Ubuntu中的OpenSSL库\nHow to upgrade OpenSSL in unbutu?\n4月8日爆出的 heartbleed 漏洞要求把 OpenSSL 升级到 1.0.1g 版本。\n关于这个漏洞的技术说明，可以看这里： 关于OpenSSL“心脏出血”漏洞的分析 。\n在 Heartbleed test 网站，可以测试自己的网站有没有这个漏洞。\n我最担心的，是在升级 OpenSSL 的过程中，远程 SSH 无法连线。\n在 OSChina 和 Segmentfault 上询问后，得知这种情况不会发生。\n另外，可以采用比较保险的方法：\n保险起见，你在现有的ssh连接上输入命令升级openssl，然后重启服务。不要断开SSH连接。然后新开一个SSH会话，确认一切正常后再断开旧的SSH连接。\n升级的方法，参照这几篇文章吧，我就懒得写了：\nUpgrade OpenSSL on Ubuntu 12.04 linux升级openssl和php_openssl模块 Linux 从源码编译安装 OpenSSL ","date":"2014-04-09","description":"","lastmod":"2014-04-09T09:08:50Z","slug":"how_to_upgrade_openssl_in_ubuntu","tags":["howto","linux","ubuntu"],"title":"如何升级Ubuntu中的OpenSSL库","url":"https://blog.zengrong.net/post/how_to_upgrade_openssl_in_ubuntu/"},{"categories":["technology"],"content":"在手机游戏开发中如何选择图像素材格式？\nHow to choose the picture texture format in the game develop?\n2014-08-01更新：增加ETC1格式\n这是我在知乎上的一个回答，这里是原文。\n回答的前提是：使用OpenGL来渲染。\n分几个点来回答。\n1. RGBA4444真的比RGBA8888占用的RAM要少 其实这里说的RAM，是指的显存而非内存。OpenGL支持以这几种形式来使用纹理资源(via http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml)：\nGL_UNSIGNED_BYTE（RGBA8888或RGBA888） GL_UNSIGNED_SHORT_5_6_5 GL_UNSIGNED_SHORT_4_4_4_4 GL_UNSIGNED_SHORT_5_5_5_1 在程序将图片载入系统内存后，会根据你选择的形式（RGBA8888/RGB565 etc.）对其做一些处理（怎么处理后面说），然后就将这些纹理上传到显卡的显存，之后会把这些图片占用的内存删除掉。\n也就是说，载入的图片在变成了显卡能处理的纹理之后，就根本不会保存在内存中，所以你看不到内存占用的变化。\n当然，这个操作是由程序员自行控制（或者由你选择的框架来决定）的，你如果决定不删除它们而让它们留在内存中，那当然会占用系统内存。\n2. TexturePackger导出的图片是个什么情况？ 你可以拿一张图片尝试，在导出为RGBA8888和RGBA4444的时候，它们的文件大小确实是不同的。请看下面的图片，并注意我加亮的部分：\n对于同一张图片，在RGBA8888格式下，唯一颜色数是5864；而RGBA4444格式下，唯一颜色数是1454，文件大小减少了80KB左右。\n至于图片信息中显示的 Original Colors依然是32Bit，这是因为在图像处理软件显示图像的时候，内部使用的色彩是8888的。\n3. 保存的文件是个什么情况？ 上面的RGBA4444是否就真的使用的16bit（4x4）来保存每个像素呢？\n不是。\n我们知道，PNG格式可以保存成 8bit 和 24bit 以及 24bit(with alpha channel)=32bit 三种格式。而JPEG格式是24bit的。RGBA4444有16bit，所以无论如何是不能使用8bit格式来保存的。\n因此，这个RGBA4444是使用24bit（或者32bit）格式来保存的。\n那么，为什么RGBA4444的文件体积会比RGBA8888小呢？\n注意上面的两个 Number of unique colors。在这里，压缩算法起了作用，将相同的颜色压缩了，导致文件体积变小。\n不过，即使是采用同样的色深保存，我仍然要说的是，RGBA4444比RGBA8888的图像质量会差一些。\n4. 怎么做到的？ 要回答上面的下划线部分结论，我们需要提出一个新的问题：RGBA8888转换成RGBA4444，发生了什么变化？\n先来看看它们分别代表什么：\nRGBA8888 : R 8bit + G 8bit + B 8bit + A 8bit RGBA4444 : R 4bit + G 4bit + B 8bit + A 8bit 8bit 能代表的最大数字是256，也就是说每种颜色可以表达256个级别，那么8x3=24bit（不算A）就能表现 2^{24} = 167772165 种颜色。\n同样的，RGBA4444能表现的颜色是 2^{12} = 4096 种颜色。\n也就是说，进行这种转换，是一定会丢失颜色信息的。\n以 0xFFFFFFFF 这个RGBA8888 颜色为例，转换成 RGBA4444 可以这样做：\n1unsigned int pixel32 = 0xFFFFFFFF; 2unsigned short pixel16 = 3 ((((pixel32 \u0026gt;\u0026gt; 0) \u0026amp; 0xFF) \u0026gt;\u0026gt; 4) \u0026lt;\u0026lt; 12) | // R 4 ((((pixel32 \u0026gt;\u0026gt; 8) \u0026amp; 0xFF) \u0026gt;\u0026gt; 4) \u0026lt;\u0026lt; 8) | // G 5 ((((pixel32 \u0026gt;\u0026gt; 16) \u0026amp; 0xFF) \u0026gt;\u0026gt; 4) \u0026lt;\u0026lt; 4) | // B 6 ((((pixel32 \u0026gt;\u0026gt; 24) \u0026amp; 0xFF) \u0026gt;\u0026gt; 4) \u0026lt;\u0026lt; 0); // A 最终的结果是 0xFFFF。\n5. iOS用什么？ 当然是用PVR格式。\npvr是iOS设备的图形芯片 PowerVR 图形 支持的专用压缩纹理格式。它在PowerVR图形芯片中效率极高，占用显存也小。 性能对比可以看这里：In Depth iOS \u0026amp; Cocos2D Performance Analysis with Test Project。\n6. Android用什么？ Android设备就没有那么好的运气了。由于硬件平台不统一，每个厂商的GPU可能使用不同的纹理压缩格式。所以还是老老实实用PNG比较好。\n大部分的Android设备都支持ETC1格式，它也是受OpenGL ES 2.0标准支持的。\nETC1不支持Alpha通道，但可以采用一些方法来处理：ETC 纹理压缩和 Alpha 通道处理 « Mali 开发人员中心 。\nETC2支持Alpha通道，但需要 OpenGL ES 3.0标准 支持。\n但由于硬件平台不统一，每个厂商的GPU可能使用不同的纹理压缩格式，极少数的GPU甚至对ETC格式支持有问题。\n所以在Android设备上，目前可以采用ETC1或者PNG/JPEG格式。\n7. 参考文章 PowerVR PowerVR 图形 手持平台纹理格式说明 拒绝忽悠 移动GPU全解读 In Depth iOS \u0026amp; Cocos2D Performance Analysis with Test Project ","date":"2014-04-04","description":"","lastmod":"2014-04-04T06:01:16Z","slug":"how_to_choose_the_picture_format_in_the_mobile_game_develop","tags":["game","opengl","cpp","choice"],"title":"在手机游戏开发中如何选择图像素材格式？","url":"https://blog.zengrong.net/post/how_to_choose_the_picture_format_in_the_mobile_game_develop/"},{"categories":["technology"],"content":"在cocos2d-x中实现橡皮擦功能\nHow to make an eraser in cocos2d-x?\ncocos2d-x 是使用 OpenGL ES 来渲染的，实现橡皮擦，需要一点点 OpenGL 知识。\n/* 下面是可以跳过不看的废话。\n是的，不需要 OpenGL ，我们也能使用 cocos2d-x 制作出游戏。至少我接触过的几个团队都是这么干的，有的团队中甚至无人了解 C++。\n但是，在我学习 cocos2d-x 的这几个月里，我发现不学习 OpenGL ES 对我来说是无法想象的。在看源码的时候，你不能碰到 OpenGL 就无视它们，对程序员来说这是罪过。\n这不难，Trust me.\n废话结束 */\n依赖 本文基于 cocos2d-x 2.2.2\n效果截图 原理 先将要被擦除的像素渲染到 FrameBuffer 中，然后使用 Alpha 为 0 的像素块与已有像素做混合，将已有的像素替换成 Alpha 为 0 的像素即可完成擦除。\n注意，这里说的“擦除”，准确的描述是“擦除到透明”。对于不透明画布（例如白色背景）的擦除，只需要使用画布的背景颜色进行绘制就行了，本文并不讨论。\n我们并不需要真的去考虑 FrameBuffer 的建立和渲染，cocos2d-x 已经为我们准备好了 CCRenderTexture 类。\n使用 CCRenderTexture ，我们可以方便地完成绘制工作，比使用 Shader 要灵活，弱点可能就是性能较低。\n设置混合模式，可使用 CCNode 提供的 ccBlendFunc 这个预定义类型。\nC++代码 这里仅贴出主要代码，详细的代码请在本文最后下载。\n本文代码使用 cocos2d-x project_creator 创建的项目修改。\n1. 以下代码位于 HelloWorldScene.cpp 文件的 init 方法中，详细的内容看注释。\n1//启用触摸支持 2this-\u0026gt;setTouchMode(kCCTouchesOneByOne); 3this-\u0026gt;setTouchEnabled(true); 4//... 其它初始化内容 5//... 6// 显示背景图片，方便查看“擦除到透明”效果 7CCPoint center = ccp(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y); 8CCSprite* pSprite = CCSprite::create(\u0026#34;HelloWorld.png\u0026#34;); 9pSprite-\u0026gt;setPosition(center); 10this-\u0026gt;addChild(pSprite, 0); 11 12// 创建一个橡皮擦，注意颜色的设置是全透明黑色 13pEraser = CCDrawNode::create(); 14pEraser-\u0026gt;drawDot(ccp(0, 0), 20, ccc4f(0, 0, 0, 0)); 15pEraser-\u0026gt;retain(); 16 17// 创建画布，并显示它 18pRTex = CCRenderTexture::create(visibleSize.width, visibleSize.height); 19pRTex-\u0026gt;setPosition(ccp(visibleSize.width/2, visibleSize.height/2)); 20this-\u0026gt;addChild(pRTex, 10); 21 22// 创建被擦除的内容，将其渲染到画布上 23CCSprite* pBg = CCSprite::create(\u0026#34;dirt.png\u0026#34;); 24pBg-\u0026gt;setAnchorPoint(ccp(0.5,0.5)); 25pBg-\u0026gt;setPosition(center); 26pRTex-\u0026gt;begin(); 27pBg-\u0026gt;visit(); 28pRTex-\u0026gt;end(); 2. 下面是执行擦除逻辑。代码位于 ccTouchMoved 方法中。\n1// 获取触摸坐标并移动橡皮擦到该坐标 2CCPoint touchPoint = touch-\u0026gt;getLocation(); 3pEraser-\u0026gt;setPosition(touchPoint); 4 5// 设置混合模式 6ccBlendFunc blendFunc = { GL_ONE, GL_ZERO }; 7pEraser-\u0026gt;setBlendFunc(blendFunc); 8 9// 将橡皮擦的像素渲染到画布上，与原来的像素进行混合 10pRTex-\u0026gt;begin(); 11pEraser-\u0026gt;visit(); 12pRTex-\u0026gt;end(); 关于混合 混合（blend） 发生在 OpenGL 将像素渲染 FrameBuffer 之前。我们使用的混合模式 {GL_ONE, GL_ZERO} 的含义如下：\n完全使用源像素（橡皮擦）的内容，替换掉目标像素（被擦除画面）对应坐标的内容。\n假设源像素（橡皮擦）的颜色值为 Rs, Gs, Bs, As ，目标像素的颜色值为 Rd, Gd, Bd, Ad ，那么使用上面的混合模式，最终的像素值为：\n(Rs*1 + Rd*0), (Gs*1 + Gd*0), (Bs*1 + Bd*0), (As*1 + Ad*0) 从上面的公式可以看出，最终的结果与橡皮擦原来的颜色值完全相同。\nquick-cocos2d-x lua代码 lua代码依赖我forked的 quick-cocos2d-x 。\ndisplay.newSolidCircle 方法，目前并没有收录进入 dualface 的官方 quick-cocos2d-x 版本。\n1local __touchPoint = cc.p(0, 0) 2local __sp = display.newSolidCircle(20, {fillColor=cc.c4f(0,0,0,0)}) 3local __bf = ccBlendFunc:new() 4__bf.src = GL_ONE 5__bf.dst = GL_ZERO 6__sp:setBlendFunc(__bf) 7\t8local __rTex = CCRenderTexture:create(display.width, display.height) 9__rTex:begin() 10__sp:align(display.CENTER, __touchPoint.x, __touchPoint.y) 11__sp:visit() 12__rTex:endToLua() 13 14__rTex:setPosition(display.cx, display.cy) 15self:addChild(__rTex) 项目下载 请将此项目放在 cocos2d-x 中的 projects 目录下。\n1 文件 参考文章 Eraser in OpenGL ES iphone Blending a texture to erase alpha values softly with OpenGL \u0026quot;Scratch Off\u0026quot; effect (mask) CCRenderTexture + CCSprite instead of a solid color ","date":"2014-04-03","description":"","lastmod":"2014-04-03T03:44:21Z","slug":"how_to_make_an_eraser_in_coco2d-x","tags":["cpp","cocos2d-x","howto","lua","opengl"],"title":"在cocos2d-x中实现橡皮擦功能","url":"https://blog.zengrong.net/post/how_to_make_an_eraser_in_coco2d-x/"},{"categories":["technology"],"content":"在 cocos2d-x 中实现蒙版支持（一）——使用 CCRenderTexture Get a masked sprite in cocos2d-x use CCRenderTexture\n在 cocos2d-x 框架中，并没有为我们提供蒙版支持。想想 AS3 中的 mask 属性，多么地让人怀念啊！\n这个系列文章讲解如何在 cocos2d-x 中实现蒙版的支持。\n依赖 本文基于 cocos2d-x 2.2.1 和 quick-cocos2d-x 2.2.1 rc 。\n系列文章 在 cocos2d-x 中实现蒙版支持（二）——使用 CCClippingNode（待完成） CCRenderTexture 我们可以将这个类看成 AS3 中的 BitmapData 类。它实现了一块屏幕之外的画布，我们可以利用 OpenGL ES 在上面进行渲染，然后将得到的像素进行处理，并绘制在屏幕上。\n实现方案 为了实现遮罩，我们需要两张图片，一张为遮罩图片（mask.png），一张为被遮罩图片（helloworld.jpg）。\nmask.png\n这张图片中有颜色的部分，根据颜色的 alpha 的值，显示被遮罩图片中对应的像素；没有颜色的部分（alpha为0），则不显示任何被遮罩图片的像素。\nhelloworld.jpg\n最终效果如下：\nmasked.png\n流程如下：\n1. 分别创建遮罩图片和被遮罩图片的 CCSprite 对象，统一使用左下角对齐；\n1CCSprite* __pMask = CCSprite::create(\u0026#34;mask.png\u0026#34;); 2__pMask-\u0026gt;setAnchorPoint(ccp(0, 0)); 3__pMask-\u0026gt;setPosition(ccp(0, 0)); 4 5CCSprite* __pImg = CCSprite::create(\u0026#34;helloworld.jpg\u0026#34;); 6__pImg-\u0026gt;setAnchorPoint(ccp(0, 0)); 7__pImg-\u0026gt;setPosition(ccp(0, 0)); 2. 创建两个混合模式对象，分别设置给蒙版图像和被蒙版图像；\n这两个 ccBlendFunc 实现了以蒙版图像的像素透明度来显示被蒙版图像。具体的含义，可以参考这里： glBlendFunc。\n一个更直观的例子和实时预览工具，可以看这里：Visual glBlendFunc + glBlendEquation Tool 。\n1ccBlendFunc __maskBF = { GL_ONE, GL_ONE }; 2__pMask-\u0026gt;setBlendFunc(__maskBF); 3 4ccBlendFunc __imgBF = { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA }; 5__pImg-\u0026gt;setBlendFunc(__imgBF); 3. 创建一个 CCRenderTexture 的实例，将其初始化为被遮罩图片的大小；\n1CCSize __size = __pImg-\u0026gt;getContentSize(); 2CCRenderTexture* __pRender = CCRenderTexture::create(__size.width, __size.height); 4. 依次绘制蒙版图像和被蒙版图像，注意是先绘制蒙版图像，再绘制被蒙版图像；\n1__pRender-\u0026gt;begin(); 2__pMask-\u0026gt;visit(); 3__pImg-\u0026gt;visit(); 4__pRender-\u0026gt;end(); 5. 创建一个新的 CCTexture2D 对象，并根据此纹理建立一个 CCSprite ，搞定。\n1CCTexture2D* __pTex = new CCTexture2D(); 2__pTex-\u0026gt;initWithImage(__pRender-\u0026gt;newCCImage(true)); 3__pTex-\u0026gt;autorelease(); 4CCSprite* __newSprite = CCSprite::createWithTexture(__pTex); 5this-\u0026gt;addChild(__newSprite); 在这一步中，还有一个做法，就是直接从 CCRenderTexture 中包含的 CCSprite 对象中获取 CCTexture2D 对象，然后创建新的 CCSprite：\n1CCTexture2D* __pTex = __pRender-\u0026gt;getSprite()-\u0026gt;getTexture(); 2CCSprite* __newSprite = CCSprite::createWithTexture(__pTex); 3__newSprite-\u0026gt;filpY(true); 4this-\u0026gt;addChild(__newSprite); 但由于得到的纹理是根据Y轴反转过的，我们必须再把它反转回来。\n那么上面使用 newCCImage 得到的纹理为什么不用翻转呢？\n其实不然， newCCImage(true) 这个调用中的参数 true 就代表需要翻转纹理。\n完整代码(C++) 1ccBlendFunc __maskBF = { GL_ONE, GL_ONE }; 2ccBlendFunc __imgBF = { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA }; 3 4CCSprite* __pMask = CCSprite::createWithTexture($sprite-\u0026gt;getTexture()); 5__pMask-\u0026gt;setAnchorPoint(ccp(0, 0)); 6__pMask-\u0026gt;setPosition(ccp(0, 0)); 7 8CCSprite* __pImg = CCSprite::createWithTexture($sprite-\u0026gt;getTexture()); 9__pImg-\u0026gt;setAnchorPoint(ccp(0, 0)); 10__pImg-\u0026gt;setPosition(ccp(0, 0)); 11 12__pMask-\u0026gt;setBlendFunc(__maskBF); 13__pImg-\u0026gt;setBlendFunc(__imgBF); 14 15CCSize __size = __pImg-\u0026gt;getContentSize(); 16CCRenderTexture* __pRender = CCRenderTexture::create(__size.width, __size.height); 17__pRender-\u0026gt;begin(); 18__pMask-\u0026gt;visit(); 19__pImg-\u0026gt;visit(); 20__pRender-\u0026gt;end(); 21 22CCTexture2D* __pTex = new CCTexture2D(); 23__pTex-\u0026gt;initWithImage(__pRender-\u0026gt;newCCImage(true)); 24__pTex-\u0026gt;autorelease(); 25CCSprite* __newSprite = CCSprite::createWithTexture(__pTex); 26this-\u0026gt;addChild(__newSprite); 完整代码(lua) 1-- @author zrong(zengrong.net) 2-- Creation: 2014-01-21 3function display.newMaskedSprite(__mask, __pic) 4\tlocal __mb = ccBlendFunc:new() 5\t__mb.src = GL_ONE 6\t__mb.dst = GL_ZERO 7 8\tlocal __pb = ccBlendFunc:new() 9\t__pb.src = GL_DST_ALPHA 10\t__pb.dst = GL_ZERO 11 12\tlocal __maskSprite = display.newSprite(__mask):align(display.LEFT_BOTTOM, 0, 0) 13\t__maskSprite:setBlendFunc(__mb) 14 15\tlocal __picSprite = display.newSprite(__pic):align(display.LEFT_BOTTOM, 0, 0) 16\t__picSprite:setBlendFunc(__pb) 17 18\tlocal __maskSize = __maskSprite:getContentSize() 19\tlocal __canva = CCRenderTexture:create(__maskSize.width,__maskSize.height) 20\t__canva:begin() 21\t__maskSprite:visit() 22\t__picSprite:visit() 23\t__canva:endToLua() 24 25\tlocal __resultSprite = CCSpriteExtend.extend( 26\tCCSprite:createWithTexture( 27\t__canva:getSprite():getTexture() 28\t)) 29\t:flipY(true) 30\treturn __resultSprite 31end 性能忧虑 在进行了上面的处理之后，CCSprite 其实就变成了一张单独的纹理图片，在显示的时候就和我们从文件中调用的图片一样进行渲染。\n这种做法虽然方便，但它会对GPU造成一定的消耗，毕竟合并的工作是实时完成的。GPU在程序运行的过程中帮我们进行了类似 Photoshop 所做的图层合并处理。如果这样的操作过多，肯定会对性能有影响。\n在 cocos2d-x 中实现蒙版支持（二）——使用 CCClippingNode（待完成） 一文中，我们会再看看另一种性能更好的实现遮罩的方法。\n参考文章 How To Mask a Sprite with Cocos2D 1.0 ","date":"2014-03-19","description":"","lastmod":"2014-03-19T10:31:22Z","slug":"get_a_masked_sprite_in_cocos2d-x_use_ccrendertexture","tags":["cpp","cocos2d-x","howto","lua","opengl"],"title":"在 cocos2d-x 中实现蒙版支持（一）——使用 CCRenderTexture","url":"https://blog.zengrong.net/post/get_a_masked_sprite_in_cocos2d-x_use_ccrendertexture/"},{"categories":["others"],"content":"真正的程序员应该能自己解决问题\nA programmer may solve problems on their own.\n刚巧我写了这篇 优秀的程序员和一般的程序员差别在哪？ ，就看到了这篇 quick 论坛上的讨论， 原文在这里 。\n我征得了原作者同意，转载在这里。\nXpol Wan 大家好，\n我来发点牢骚。\n最近老是看到有人发错误信息的截图问怎么办。虽然论坛里面没有说不能问这样的问题。\n但是我个人的意思是，这些真的是属于你自己的鸡毛蒜皮的小问题，不必到这里来问，这些问题多数都不属于quick-x。\n以下是我的一些建议：\n程序员需要学习英语，如果你连错误信息里面的英语的字面意思都不懂，要么洗洗睡了，要么去学学英语吧。 你需要学会从错误信息中找到最关键的信息：比如LUA ERROR... module 'xxxxx' not found或者undefined reference to ‘xxxxx’ 找到这些关键后（比如，上面的这些'xxxx'就是你解决问题的关键，无非是运行时，或者编译时找不到xxxx。）进行对应的处理和解决 如果你不知道怎么处理，可以google，把关键错误信息贴到google里面，搜索一下，多半就有答案了；千万别找度娘，切记切记～～（我想说程序员用度娘搜索程序问题，只有两个原因：一是你英语很菜，二是你程序员能力很菜） 翻墙技能也是必须的，最简单的就是买个靠谱的VPN，喜欢折腾可以goagent或者vps+shandowsocks。 就算是你要问这些问题，也请别截图，直接贴错误信息文本；就算别人愿意帮你google，你也得得让别人好拷贝粘贴啊，大家都这么忙不是吗？ 问问题的时候，大家希望看到的时候你做了那些分析和尝试，而不是：来吧，帮我看看这个图里面的错误信息，然后就没有然后了。你都懒得打字说明问题，为什么指望别人帮你分析帮你解决，然后打字告诉你该如何如何呢？ 一切技术问题都是纸老虎，搞定他，一个错误信息都搞不定，何以搞定天下？ 就是这么多，希望对大家有帮助。\n如果你觉得说的都是狗屁，就全当杂音，直接 m 吧。 Best Regards!\nXpol Wan // There is a better way!\nChildhoodAndy 对楼主说的表示支持。分析问题，能够准确描述问题以及解决问题是程序员走向成熟必不能缺少的阶段。\nhttp://wiki.woodpecker.org.cn/moin/AskForHelp 你会发现你熟读“提问的智慧”并在问题上付诸实践之后，问的问题基本上都会有人答，而且可能直接就是问题的答案所在。为什么stackoverflow上有些问题答案质量那么高，这跟提问者为了提问问题所作出的努力有很大关系，包括事先google、查阅手册、尝试解决问题的方式等等，如果你是stackoverflow网站的注册用户，你还可以对提问着的问题进行编辑，去除不重要的信息，明显语法问题等会让回答者更快的理解到问题所要表达的意思。\nJacky(zrong) 相当赞同。\n中文版 提问的智慧（2013-10-26翻译） 在这里：http://www.beiww.com/doc/oss/smart-questions.html ，里面也有英文版的链接。\n想要别人回答你的问题，应该先想尽办法把问题描述清楚，加上其中你自己的理解，你的错误尝试等等等~\n在问题的格式上应该尽量清晰，容易辨别，这起码是对回答者的一种最基本的尊重。毕竟，没有谁必须回答你的问题。\n说实话，现在quick论坛里看到的很多问题，真的是让人看一眼就生气（或者想笑），更别说回答了。\n关于为什么必须使用Google而不是百毒，Childhood说的很有道理。但我的意见是，对于技术问题，无论如何都不要用百毒。\n算法是一部分原因，更多的原因是这几个：\n1. 百毒通常不会把你最需要的技术问题列在第一页（或者第一页的上半部分）。\n例如你在百毒中搜索一个软件名称，Google一般会直接把软件的官方网站列在第一个。而在百毒里面第一个一般都是百毒的应用下载中心，或者国内的软件下载站等等地址。你真的去国内的软件下载站下载软件看看就知道了，很难找到真正的下载地址，这得耽误多少事啊~~\n再例如，你要搜索一门新的技术（或者老的技术），谷歌通常直接列出该技术的官网，或者比较权威的资源，或者维基百科。而百毒排在前面的一般是百毒百科，然后就是哪些抄来抄去的文章、评测、博客等等。\n2. 百毒给出的文章质量通常不高。\n当然，这和国内的技术文章质量高的较少有关系。但我很奇怪的一点是，为什么同样是中文，谷歌却能搜出质量很高的原出处中文技术文章，百毒搜出的却都是百毒空间里面哪些被转载了无数次的格式乱成一锅粥前后颠倒内容残缺不全甚至语句都不通顺的烂文？\n你看，耽误多少事啊~~\n3. 百毒更懂中文，Google更懂英文。\n众所周知，许多新的技术（或者老的技术）英文资源更多，优秀的英文资源则更更多。让更懂中文的百毒来做这件不可能完成的任务，还不如仔细学一下怎么用简单的英文关键词得到自己想要的内容更不耽误事儿~\nBTW：如果实在不愿意搭梯子，可以勉为其难的用一下Bing的英文搜索。当然，Google是无可替代的。\nAo Xu 本人是百度黑，走到哪黑到哪，从里黑到外，对@Jacky 的回答很赞同，无论如何都不要用百度。就连我原先在github pages的个人博客，都在robots.txt里写了不允许百度收录。 并且我对@Jacky 的回答从原理上补充说明：百度为什么会这样，Google为什么不会。\n1. 百度为什么不会把官网列在第一个？这里有几重原因。\n首先，百度通过付费可以影响搜索结果的排序（就是臭名昭著的『竞价排名』），而Google不允许这种行为，两者在公司盈利模式上就有本质区别，Google是通过广告盈利的，例如AdWords和AdSense。\n其次，两家公司追求的宗旨不同，百度希望自家的产品占领互联网，而Google希望全互联网的网页都能得到有效的检索。百度搜索越来越趋向于『百度站内搜索』，就像@Jacky 说的，搜软件会引导到百度应用下载中心；大家留意一下，平时搜一些东西，结果往往是引导到百度知道、百度百科、百度贴吧、百度空间这些同属于baidu.com域名下的低质量内容来源。而Google从来不偏袒自己的产品，我仅仅举出几个例子：\n谷歌第三次自罚下调Chrome网站搜索排名 Google惩罚性降低Google日本的搜索排名 2. 为什么百度搜不出原始出处的文章？\n原因跟之前的相同：竞价排名+百度站内搜索。而且从搜索引擎基本原理上，Google计算搜索结果的权重（PageRank值）的主要来源是『反向链接』数，就是有多少网页有链接指向它。可以想到，一篇原始出处的技术文章，必定有很多地方会引用它，所以它的反向链接数也会很多，自然排名就高。事实上搜索引擎的排序原理有着非常复杂的算法，篇幅所限我无法一一说明。Google搜索在技术原理上的先进性是百度完全无法比拟的，可以说是数量级的差距。\n3. 关于『百度更懂中文』这一点，我一直都是不赞同的\n黑百度的人都常说『百度是卖假药的』，这不就是中文搜索特有、百度特有的现象吗？另外Google领先于百度的技术有很多是与语言无关的，Google是一个国际化服务，提供世界各国语言的搜索，这不是光靠百度对中文的研究就能超越的。所以即使中文搜索，我依然用Google。\n如果不想搭梯子，可以用 www.googlestable.com 无痛搜索。\n欢迎百度的粉丝来反驳我不对的地方，请举出你的事实和观点。\n","date":"2014-03-18","description":"","lastmod":"2014-03-18T06:15:19Z","slug":"a_programmer_may_solve_problems_on_their_own","tags":["study"],"title":"真正的程序员应该能自己解决问题","url":"https://blog.zengrong.net/post/a_programmer_may_solve_problems_on_their_own/"},{"categories":["others"],"content":"优秀的程序员和一般的程序员差别在哪？\nWhat difference between an excellent programmer and a normal programmer?\n这是我在知乎上的一个回答，原文在这里：http://www.zhihu.com/question/19759297/answer/23187279\n在我看来，没有优秀程序员和一般程序员，只有程序员和码农。\n如果按 排名第一 的 @郭凛 的答案来分类，那就是5是程序员，1234是码农。\n举个栗子：\nA：对于我们这种需要多方合作的项目，还是用git比较方便管理。 B：svn很牛逼的！我用了很多年了！ A：git的分支很方便，合并操作相当简洁且没有负担。 B：svn也有分支！百度上说svn更适合企业！ A：git只有一个.git目录来管理所有版本，在排除的时候很方便！ B：svn现在也支持单 .svn目录了！ 再举个栗子：\nA：为什么我们网站的根目录下面还有个.settings和.project文件夹？ B：我上传时候直接用flashfxp一拖就上来了，没看有什么文件夹。 A：为什么我刚才pull之后在项目库下面多了个 未命名.txt 文件？ B：嗯，那是我自己做笔记备份用的。 三举个栗子：\nA：为什么Hero这个类有1万行？ B：我觉得把所有功能写在一个类里面很方便啊！ A：为什么这段代码复制了4次？封装成函数啊！ B：啊！有么？ A：这几个类为什么不用适配器模式？ B：什么是适配器模式？ 最后举个栗子：\nB：怎么加一个Sprite到Node中？ A：Node.addChild(Sprite) B：我要在加的时候设置层级关系！ A：RTFM！ 码农真正意识（而不是调侃）到自己是码农的时候，TA才可能成为一个程序员。\n而当TA成为一个程序员的时候，TA才有资格站在山脚下，憧憬着那那高耸入云的峰顶，开始攀登。\n","date":"2014-03-17","description":"","lastmod":"2014-03-17T07:26:10Z","slug":"what_difference_between_excellent_programmers_and_normal_programmers","tags":["study"],"title":"优秀的程序员和一般的程序员差别在哪？","url":"https://blog.zengrong.net/post/what_difference_between_excellent_programmers_and_normal_programmers/"},{"categories":["technology"],"content":"quick-cocos2d-x中的绘图API\nquick-cocos2d-x 提供的绘图API的相关C++文件位于 draw_nodes 文件夹中，有这样几个：\nCCDrawingPrimitives 提供 ccDraw* 开头的全局方法，可用来绘制游戏开发中调试用的图形。cocos2d-x开发组在该功能源码前使用了这样的说明：\nLEGACY FUNCTIONS USE CCDrawNode instead\n在ccDrawCubicBezier下面还有这样的说明：\nThis function could be pretty slow. Use it only for debugging purposes.\n这说明这并不是开发组推荐的方法。因为它没有使用批量渲染，执行起来可能会很慢。\n使用这些方法还有个问题，就是必须将绘制的函数调用放在 CCNode 的 draw 方法中。这意味着在单独使用绘图功能的时候，可能需要实现一个继承 CCNode 的类，并实现其中的 draw 方法。\nCCDrawNode 它支持批量渲染绘制，但支持的绘制功能并没有 CCDrawingPrimitives 多。它只提供了3个方法：\ndrawDot 画点 drawSegment 画线段 drawPolygon 画多边形 当然，画多边形功能其实可以画任何图像。只需要顶点坐标值即可。\n例如 display.newSolidCircle 这个方法就是使用 CCDrawNode 的 drawPolygon 方法来绘制圆形、饼型或扇形（还未完成）。\nCCShapeNode 这个文件只在 quick-cocos2d-x 中有，标准的 cocos2d-x 中是找不到的。 它包含了1个基类和4个子类：\nCCShapeNode CCCircleShape CCRectShape CCPointShape CCPolygonShape CCShapeNode 是基类，定义了诸如设置颜色、线宽、线性等公用方法。其他几个类是子类，分别实现了画圆、画矩形、画点和画多边形。\nCCShapeNode 是对 CCDrawingPrimitives 的封装。它解决了上面我在 CCDrawingPrimitives 中提到的问题，使绘图操作更简便。\n","date":"2014-03-13","description":"","lastmod":"2014-03-13T08:09:31Z","slug":"drawing_api_in_quick-cocos2d-x","tags":["cpp","cocos2d-x"],"title":"quick-cocos2d-x中的绘图API","url":"https://blog.zengrong.net/post/drawing_api_in_quick-cocos2d-x/"},{"categories":["technology"],"content":"在quick-cocos2d-x中对非2的幂纹理进行平铺操作\ntile use NPOT texture in quick-cocos2d-x\n本文基于 quick 2.2.1rc 版本\n在 quick 的 framework.display 包中，有这样一个方法实现图片的平铺：\n1function display.newTilesSprite(filename, rect) 2 if not rect then 3 rect = CCRect(0, 0, display.width, display.height) 4 end 5 local sprite = CCSprite:create(filename, rect) 6 if not sprite then 7 echoError(\u0026#34;display.newTilesSprite() - create sprite failure, filename %s\u0026#34;, tostring(filename)) 8 return 9 end 10 11 local tp = ccTexParams() 12 tp.minFilter = 9729 13 tp.magFilter = 9729 14 tp.wrapS = 10497 15 tp.wrapT = 10497 16 sprite:getTexture():setTexParameters(tp) 17 CCSpriteExtend.extend(sprite) 18 19 display.align(sprite, display.LEFT_BOTTOM, 0, 0) 20 21 return sprite 22end 这里直接通过调整 OpenGL ES 的纹理渲染参数来实现平铺，性能相当好。\n但使用这个方法有个限制，就是被平铺的纹理必须是2的幂(POT, Power Of Two)。\n于是，我尝试使用 CCSpriteBatchNode 来对纹理进行平铺，达到不用2的幂(NPOT）的纹理即可进行平铺的目的。\n但是这个方法也有一个问题。当对这个 CCSpriteBatchNode 进行缩放的时候，平铺的纹理之间会出现非常细的空隙，导致最终看起来的效果像是栅栏一样，无法实现无缝平铺的效果。\n这种缩放并不一定需要主动进行。假设你的游戏做了多分辨率处理（在如今的世界，这是不可避免的），在不同的设备分辨率中，cocos2d-x 会根据分辨率进行缩放处理。 这种情况下也会出现这个问题。\n一个可行的办法，是制作平铺图的时候，在图像边缘留1个像素的重复像素。在拼接的时候，将两个相邻的纹理互相覆盖1个像素。\n我把这个功能封装成了 display.newTiledBatchNode 方法：\n1-- 创建一个可平铺的 CCSpriteBatchNode，纹理的大小可以是非2的幂 2-- @param __fileName 与 display.newSprite 的第一个参数相同 3-- @param __texture plist文件，因为使用了BatchNode，必须传递plist文件 4-- @param __size 一个CCSize对象 5-- @param __hPadding 横向覆盖几个像素 6-- @param __vPadding 纵向覆盖几个像素 7-- @return a CCSpriteBatchNode 8 9function display.newTiledBatchNode(__fileName, __texture, __size, __hPadding, __vPadding) 10\t__size = __size or cc.size(display.width, display.height) 11\t__hPadding = __hPadding or 0 12\t__vPadding = __vPadding or 0 13\tlocal __sprite = display.newSprite(__fileName) 14\tlocal __sliceSize = __sprite:getContentSize() 15\t__sliceSize.width = __sliceSize.width - __hPadding 16\t__sliceSize.height = __sliceSize.height - __vPadding 17\tlocal __xRepeat = math.ceil(__size.width/__sliceSize.width) 18\tlocal __yRepeat = math.ceil(__size.height/__sliceSize.height) 19\t-- how maney sprites we need to fill in tiled node? 20\tlocal __capacity = __xRepeat * __yRepeat 21\tlocal __batch = display.newBatchNode(__texture, __capacity) 22\tlocal __newSize = cc.size(0,0) 23\t--printf(\u0026#34;newTileNode xRepeat:%u, yRepeat:%u\u0026#34;, __xRepeat, __yRepeat) 24\tfor y=0,__yRepeat-1 do 25\tfor x=0,__xRepeat-1 do 26\t__newSize.width = __newSize.width + __sliceSize.width 27\t__sprite = display.newSprite(__fileName) 28\t:align(display.LEFT_BOTTOM,x*__sliceSize.width, y*__sliceSize.height) 29\t:addTo(__batch) 30\t--print(\u0026#34;newTileNode:\u0026#34;, x*__sliceSize.width, y*__sliceSize.height) 31\tend 32\t__newSize.height = __newSize.height + __sliceSize.height 33\tend 34\t__batch:setContentSize(__newSize) 35\treturn __batch, __newSize.width, __newSize.height 36end 这个方法已经加入了 quick 官方仓库的 develop 分支。\n但是，这个方法无法处理这样的情景——平铺带有透明度的纹理。\n","date":"2014-03-13","description":"","lastmod":"2014-03-13T07:25:13Z","slug":"tile_use_npot_texture_in_quick-cocos2d-x","tags":["cocos2d-x","howto","lua"],"title":"在quick-cocos2d-x中对非2的幂纹理进行平铺操作","url":"https://blog.zengrong.net/post/tile_use_npot_texture_in_quick-cocos2d-x/"},{"categories":["technology"],"content":"在 cocos2d-x 中使用多组shader实现多重滤镜\n2015-05-06 更新：加入 cocos2d-x-filters 项目介绍，已支持 cocos2d-x 3.x。\n本文基于 cocos2d-x v2.2。\n在 cocos2d-x 中使用 shader 实现了滤镜之后，我发现实现多重滤镜是个问题。\n多重滤镜是什么 举例说，我希望先对一个纹理执行一次 blur 滤镜，然后再执行一次 glow 滤镜。这样纹理既有模糊效果也有发光效果。\nFireworks 和 Flash 中的滤镜，都允许这样进行叠加。在Actionscript 3中，Sprite 的 filters 属性本来就是一个滤镜数组。这说明至少在API的设计上，Actionscript 3 鼓励多重滤镜的使用。\n多重滤镜的问题 而在 cocos2d-x 中，则不是这样。\ncocos2d-x 中的 shader 支持位于几个类中，它们是：\nshaders/CCGLProgram shaders/CCShaderCache shaders/ccShaders shaders/ccGLStateCache 其中， CCGLPrgram 负责 shader 的生成、编译、缓存、attribute 的 bind、uniform 设置等等工作，是非常重要的一个类。在这个类中， m_uVertShader 和 m_uFragShader 这两个用来指定 shader 位置索引值的变量值定为 0，且没有地方进行修改。\n这也就是说，如果不对 CCGLProgram 进行 hack ，是无法实现多重shader的。\n那么，能不能使用多个 CCGLProgram 实例实现多重 shader 呢？\n从 CCNode 的源码来看，每个 CCNode 实例都持有一个 m_pShaderProgram 成员变量，所有的渲染操作都是基于这一个变量来实现。那么也就是说，在不对 CCNode 进行 hack 的前提下，也无法实现多重渲染。\n使用 CCRenderTexture CCRenderTexture 可以用来渲染屏幕外的内容。我们一般使用它将纹理渲染到一块屏幕之外的区域中，然后对纹理进行进一步操作。在需要的时候，再将其渲染到屏幕上。\n我们可以利用 CCRenderTexture 来实现多重滤镜支持。\n在下面的代码中，CCHBlurFilter 和 CCVBlurFilter 分别负责实现横向模糊和纵向模糊。而CCFilteredSprite 则负责根据 Filter 进行渲染。\n1//建立横向和纵向模糊滤镜 2CCHBlurFilter* __hblurFilter = CCHBlurFilter::create(0.02f); 3CCBlurBaseFilter* __vblurFilter = CCVBlurFilter::create(0.02f); 4//对helloworld.pn使用横向模糊滤镜 5CCSprite* __hs = CCFilteredSprite::create(\u0026#34;helloworld.png\u0026#34;, __hblurFilter); 6__hs-\u0026gt;setAnchorPoint(ccp(0,0)); 7CCSize __size = __hs-\u0026gt;getContentSize(); 8//将进行过横向模糊的纹理渲染到CCRenderTexture中 9CCRenderTexture* __canva = CCRenderTexture::create(__size.width, __size.height); 10__canva-\u0026gt;begin(); 11__hs-\u0026gt;visit(); 12__canva-\u0026gt;end(); 13//根据CCRenderTexture中的纹理创建新的纹理 14CCTexture2D* __tex2 = new CCTexture2D(); 15__tex2-\u0026gt;initWithImage(__canva-\u0026gt;newCCImage(true)); 16__tex2-\u0026gt;autorelease(); 17//根据新的纹理创建最终的CCFilteredSprite，应用纵向模糊滤镜 18CCSprite* __final = CCFilteredSprite::createWithTexture(__tex2, __vblurFilter); 19__final-\u0026gt;setAnchorPoint(ccp(0,0)); 20__final-\u0026gt;setPosition(ccp(0,0)); 21//显示最终效果 22this-\u0026gt;addChild(__final); 如果把渲染的功能集成到 CCFilteredSprite 中，就可以简化调用，例如：\n1CCSprite* __final = CCFilteredSprite::create(\u0026#34;helloworld.png\u0026#34;, 2\tCCArray::create(__hblurFilter, __vblurFilter, NULL)); 3this-\u0026gt;addChild(__final); 上面的关于滤镜的类可以在 cocos2d-x-filters 项目中找到。\n参考文章 Multi shader cocos2d-x 2.2.1 How to use CCRenderTexture for Motion Blur, Screenshots and Drawing Sketches ","date":"2014-03-10","description":"","lastmod":"2014-03-10T09:36:27Z","slug":"multi_shaders_in_cocos2d-x","tags":["cpp","cocos2d-x","howto","opengl"],"title":"在 cocos2d-x 中使用多组shader实现多重滤镜","url":"https://blog.zengrong.net/post/multi_shaders_in_cocos2d-x/"},{"categories":["technology"],"content":"cygwin在windows8.1上的chmod无法修改权限\n这几天安装了windows 8.1，在使用cygwin的时候发现了问题。\n表现 在使用git clone一个项目时，cygwin告知 .ssh 的权限不正常：\n1$ git clone git@github.com:zrong/cocos2d-x-filters.git 2Cloning into \u0026#39;cocos2d-x-filters\u0026#39;... 3Bad owner or permissions on /home/rong/.ssh/config 4fatal: Could not read from remote repository. 5 6Please make sure you have the correct access rights 查看一下权限，是这样的：\n1$ ll .ssh 2total 47K 3-rw-rw----+ 1 rong None 1.7K 2012-04-08 admin 4-rw-rw----+ 1 rong None 398 2012-04-08 admin.pub 5-rw-rw----+ 1 rong None 725 09-13 22:03 config 6-rw-rw----+ 1 rong None 1.7K 03-02 16:58 id_rsa 7-rw-rw----+ 1 rong None 402 03-02 16:58 id_rsa.pub 8-rw-rw----+ 1 rong None 13K 10-06 21:20 known_hosts 使用 chomd 600 * ，发现无效，权限依然是和上面一样。\n解决 要解决这个问题，可以先为 .ssh 文件夹给予一个有效的Group。例如：\n1chgrp Users .ssh 然后再次执行 chmod 600 -R ~/.ssh 就搞定了。\n我猜这是因为 Windows 8 的权限控制发生了变化。在 Windows 7 中，可以使用 None 作为用户组，而在 Windows 8 中就必须指定一个存在的用户组。\n进一步解决 上面的方案解决了 .ssh 的权限问题，目前 git clone 正常了。但是，当我在 cygwin 下新建一个文件的时候，它的属组依然是 None 。\n可以通过修改 /etc/passwd 文件来解决这个问题:\n在 /etc/group 中找到 Users 用户对应的id，我的是 545； 在 /etc/passwd 中找到 rong 用户条目，将它的属组id改为刚才找到的id； 重启 cygwin，搞定。 参考文章 Why cannot chmod in cygwin on Windows 8 CP? cygwin permissions bug on Windows 8 ","date":"2014-03-02","description":"","lastmod":"2014-03-02T09:27:46Z","slug":"cygwin_cannot_chmod_on_windows8","tags":["cygwin","git","ssh","windows"],"title":"cygwin在windows8.1上的chmod无法修改权限","url":"https://blog.zengrong.net/post/cygwin_cannot_chmod_on_windows8/"},{"categories":["impressions"],"content":"求转发\n","date":"2014-02-19","description":"","lastmod":"2014-02-19T03:57:58Z","slug":"hire2014","tags":["entrepreneurship"],"title":"TEAM1201 招人","url":"https://blog.zengrong.net/post/hire2014/"},{"categories":["use"],"content":"终于把糟蹋了2年的 Moto Atrix 2 换成了华为荣耀3X，花点时间做了Root、Recovery，以及加入了Google服务套件，下面是流程。\n安装驱动程序； 用刷机工具刷入Recovery（线刷）； 在手机上启动Recovery（音量上+电源开机），刷入Root包实现Root（卡刷）； 同样使用Recovery，刷入Google服务套件包（卡刷）。 以上除了第4步Google服务器套件外，所有的资源和教程都可以在这里找到：华为荣耀3X(G750-T00)一站式Root/Recovery 。\n第4步对我最为重要，但目前网上没有找到针对荣耀3x的Google套件。我回想起以前 摆弄Evo 4G 的时候，使用了CyanogenMod 提供的套件。于是找到这篇文章：CyanogenMod Rom加入Google Apps官方套件 ，尝试刷了一下，一切正常。\n荣耀3X使用的是 Android 4.2.2 版本，因此对于 GMS 选择对应的 4.2.2 套件即可。该网站下载较慢，我已将其传到百度网盘上共享：\ngoo.im 下载 百度网盘 下载 ","date":"2014-02-17","description":"","lastmod":"2014-02-17T07:07:15Z","slug":"honor3x-g750-t00-recover-root-gms","tags":["android","google","mobile","smartphone"],"title":"华为荣耀3X(G750-T00)Recovery/Root/加入Google服务套件(GMS)","url":"https://blog.zengrong.net/post/honor3x-g750-t00-recover-root-gms/"},{"categories":["others"],"content":"入手阿里云服务器之后，我的博客进入了一段稳定的时期，终于不用到处找合适的主机了。但由于主机的内存太小（512MB），我还是想了一些方法来进行优化。例如挂载swap文件 ，从Apache到Lighttpd 等等。\n但最近几天，博客无响应变得频繁，基本上每天都有2~3次。远程上主机 top 看一下，还是内存吃紧。博客无响应的时候，可用内存只剩4MB，大多数内存被 php-fpm 进程吃掉了。\n于是在主机后台把内存加到了1GB，阿里云自动计算到本次到期需要的费用，花了153元。\n重启主机后，访问博客瞬间感觉高大上了。\n","date":"2014-02-12","description":"","lastmod":"2014-02-12T09:23:29Z","slug":"improve-host-memory-to-1g","tags":["linux"],"title":"主机增加到1G内存","url":"https://blog.zengrong.net/post/improve-host-memory-to-1g/"},{"categories":["technology"],"content":"git svn dcommit 提交失败，原因：assertion \u0026quot;svn_fspath__is_canonical(child_fspath)\u0026quot; failed\n在使用 git svn 作为客户端对一个 svn 库进行提交的时候出现了错误：\ngit svn dcommit Committing to https://xx.xx.xx.xx/svn/xyz/trunk ... R CODING_STYLE.md =\u0026gt; doc/CODING_STYLE.md assertion \u0026quot;svn_fspath__is_canonical(child_fspath)\u0026quot; failed: file \u0026quot;/usr/src/packages/subversion/subversion-1.8.5-1/src/subversion-1.8.5/subversion/libsvn_subr/dirent_uri.c\u0026quot;, line 2504, function: svn_fspath__skip_ancestor error: git-svn died of signal 6\n错误发生在 cygwin 下，git 版本 1.7.9，svn 版本 1.8.5。\nstackoverflow 上说，原因是 git 的重命名检测机制与 svn 发生了冲突。有两个解决方案：\n把 svn 降级到 1.7.9，一劳永逸。 使用 git svn dcommit -C1 -l1 来提交。这样会关闭 git 的重命名检测机制，这次提交会丢失重命名操作，取而代之的是一次 remove 和 一次 add 操作，就和 svn 1.4 一样。 ","date":"2014-02-11","description":"","lastmod":"2014-02-11T09:18:31Z","slug":"git_svn_dcommit_fails_because_of_assertion_error_svn_fspath__is_canonical","tags":["cygwin","git","svn"],"title":"git svn dcommit 提交失败，原因：assertion \"svn_fspath__is_canonical(child_fspath)\" faile","url":"https://blog.zengrong.net/post/git_svn_dcommit_fails_because_of_assertion_error_svn_fspath__is_canonical/"},{"categories":["others"],"content":"距离上次到香港，又过去两年。当然，这次依然是自由行。有兴趣的可以看前面两篇：\n香港自由行经验 香港自由行经验（二） 这篇或许比较无趣了。主要是发点感受，做个记录，以便以后老了也有点回忆。\n这次旅行，本来准备去长隆。那里玩的的地方比较多，而且我对广州的美食又嘴馋了。\n但小曾同学还是喜欢迪斯尼。想想女儿越大，对迪斯尼的好感度会越小，那么趁着她的思想还在童话中，多让她体验一下也好。\n时间\n2014年2月1日（大年初二）~2月7日（大年初八）\n交通\n去香港最方便的方法当然是灰机，省去了排队过关，也省去了不断的转车。但无奈小曾同学对几年前睡卧铺的感受念念不忘，而在高铁这么发达的当下，找量有票的卧铺车还真不容易。于是提前20天开始抢票。\n顺便吐槽一下12306。在订票的时候，我发现软卧还剩26张。可是当我提交订单的时候，系统总是提示无票。我换了各种抢票插件，还安装了我最讨厌的数字公司浏览器，但全部无济于事。\n偶然发现买2张票是可以的。于是我猜测是由于订票系统试图将3张票都分到1个车厢，而剩余的所有票中，没有3张票处于一个车厢的情况。这个猜测是正确的，我顺利买到3张车票。可惜我的车票就被分配到了另一个车厢。\n除了灰机之外，最方便的过关方法就是广九直通车了。我买的是武昌—广州的T255次，早上9点多就到了广州，然后地铁直奔广州东站购买广九直通车的票。和上次不同，过年期间的广九直通购票排队人很多。我被告知只有下午3点30分的一趟车才有票。于是果断买了到深圳的票转到深圳罗湖过关。\n回程的时候，考虑到过关时间的不确定性，我购买了深圳北—武汉的G1010次，然后在罗湖口岸旁定了一间如家，晚上就不怕过关排队太晚了。\n至于 HoneKong 的交通工具，当然是八达通了。\n流水账\n2月2日 今天最纠结之处莫过于算错了广九直通车在旺季的承载，这最终导致了大半天时间都在不停转车。\n广州→广州东→深圳→Kowloon Tong→Prince Edward→Jordan，这一路下来，连车上从来睡不着的我也打了旽。\n罗湖口岸依旧拥挤，内地同胞依旧热衷插队，武汉人依旧无处不在，可为什么我碰到的都是爆发户，哦，土豪？看来下次应该首选皇岗或福田口岸了。\nB P International 位置不错，楼下就是九龙公园，距离科学馆和太空馆都不远，老店小店云集，不愧是扫街的好住处。\n科学馆无论是否带娃，都值得一看。里面的社施全！都!是!好!的!…是的，这很重要。\n第二纠结之处就是老曾小王和小曾同学三人空着肚子进了科学馆。小曾同学一开始还拿个本子做笔记，过一会儿就开始和小王满场飞跑，然后突然说老爸我饿了…我说我比你还饿，我们去都拍冰火菠萝油和叉烧饭 OK?\n让我感到欣慰的是，小曾同学开始有独立思考和表达思想的愿望。比如对HongKong市容和市民的描述还是很靠谱的，比起两年前那个只知Disneyland和Oceanpack的小家伙来说是有趣多了。\n2月3日 碰到许多亚洲面孔的人，无法辩别语言，但能说英文。她们在公共场合大声谈笑，语速很快，碰到人后毫无表示，感觉和昨天碰到的武汉土豪类似。\n上 香港政府网站 查了下，原来她们是菲律宾人，也可能是印尼人。\nNgong Ping 360的缆车挺长的，20多分钟吧。水晶车厢算是噱头，没什么特别感觉。\nCitygate outlets 人超多，我跑到 Columbia 试了几件衣服，受不了人多，啥也没买跑掉了。不得不感慨内地同胞旺盛的购买力。\n晚上9：30在 Harbout City 的 PRADA 和 COACH 门口看到送钱长队，再次感慨和这些排队买奢侈品的土豪相比，上午的 Citygate 算个鸟啊啊啊啊！\n2月4日 一天都在 Disneyland Resort。没啥好说的，到处人多。每个项目排队1~2小时。\n2月5日 大年初五和老婆在尖沙咀扫街，正碰到舞狮队和财神爷在街上转悠。 早上有人拖着一车菜挂在每家店家门口，有葱，生菜和芹菜。每棵上都挂个红包。狮子会去店里转一圈讨红包，然后“衔”下菜，“嚼碎”吐给店员和店面，意味“发财”之意。\nLP必须去龙城大药房买化妆品和药品。于是我就碰到了龙城门口的长队，以及药房里面的水泄不通。LP说到龙城来必须早上10点前来排队，而且龙城只收现金不刷卡。我和小曾同学在药房里面挤了5分钟之后，终于碰到失散的LP。她说，走吧，我们不买了。我和小曾同学立刻欢呼起来。\n下午在天文馆，可巧周三正好是免费日，我们都省了门票。小曾同学等到晚上看了天文望远镜，又看了两场球幕电影。\n2月6日 又是 Disneyland Resort。 这次我们学乖了，先玩最里面的项目，顺路拿一个 FastPass ，然后往外玩。这样花了2天，所有项目玩遍。\nGrizzly Mountain灰熊山谷是新的Disneyland区域，上次来还在修。里面的Mine Cars体验相当不错。\n迷离庄园也是Disneyland的新区域，这是我看过的最好的4D电影，噢，不是电影，但胜似电影。坐在无轨电磁车上的虚拟现实体验相当完美。\n小结\n这次最大的收获，是在九龙公园呆了半天一下香港本地人的生活。以往来相当自己都是个游客，不断赶场。而这次的感觉不同，没有赶场，也不必拥挤（虽然少不了排队）。这就是自由行的魅力所在。\n","date":"2014-02-07","description":"","lastmod":"2014-02-07T07:11:17Z","slug":"travel-to-honekong-at-fourth-times","tags":["journey"],"title":"第四次香港之行","url":"https://blog.zengrong.net/post/travel-to-honekong-at-fourth-times/"},{"categories":["impressions"],"content":"Adobe AIR与unity3D都是跨平台，那个更好呢？\n这篇文章是我在之乎上的一个回答：http://www.zhihu.com/question/22611908/answer/22051811\n碰巧我最近写了这篇文章： 你是否已经放弃了flash,转投其他行业？\n这篇文章是对一个Flash社区的一个投票的评论。\n对Flash社区的开发者们来说，早早转换技术的，心有戚戚的，观望的，坚守的都有。从我认识的Flash资深开发者（3年以上）来说，逃离的和坚守的应该是一半对一半（不排除有人说假话）。根据我这几年对Adobe的观察，我觉得一个开发者要继续发展，必须远远离开Adobe。\n从Adobe的开发平台转到其他平台，目前热门的就是两个方向：Cocos2d-x和Unity3D。一开放，一封闭，一程序员友好，一设计师友好。\n而对于我这个不愿被封闭技术绑架的人来说，当然是义无反顾地转向Cocos2d-x了。\n似乎有点偏题。\nUnity3D我一直很关注，但没有进行过深入了解。我根据目前知道的部分信息做一个不完善的分析：\n注意，下面的分析在Unity部分可能有错误，因此引起的错误判断我不负任何责任！但Adobe部分一定没有错误！\n两者都是封闭的； Unity有完善的IDE。说实话如果Flash IDE真心认真做，一定能和Unity3D分庭抗礼啊，可惜啊啊啊； 在原生支持上一样有坑，但Unity3D似乎官方更给力。而Adobe在GameSDK中抛出了一个不开源的iOS ANE之后就没有生息（我真搞不懂那个毫无技术含量的东西为什么不●开●源！），Android支持更是没有。所以Rect才和我一起做了 Platform ANEs ； Unity是3D更强，Adobe是2D更强。虽然Adobe方面有Stage3D存在，但没有配套的优秀IDE（这对3D是多么重要啊啊啊！），Flash IDE不给力啊不给力； Adobe方面前几天开源了 AS4语言和虚拟机定义白皮书，这似乎说明了AS4铁定不会出了，Unity则JS/C#等各种语言包容并蓄的； Adobe是全球顶尖的软件公司：Adobe Systems，手上有大把赚钱的软件；Unity只有一款产品，就是Unity3D； 乔布斯忽悠说Flash很烂，然后Adobe就转头把Android的Flash Player停了，然后就去做H5，屁都没放一个； …… 既然你还没有决定选择什么，为什么不再加一个选择呢？这是我在开头提到的那篇文章中的摘录：\n//////////////////////////////////// 既然是自己的博客，还是直接看原文吧：你是否已经放弃了flash,转投其他行业？ ////////////////////////////////////\n祝你早日做出正确的选择！\n","date":"2014-01-30","description":"","lastmod":"2014-01-30T07:29:02Z","slug":"how-to-choose-a-cross-platform-technology-between-unity3d-and-adobe-air","tags":["adobe","air","cocos2d-x","unity"],"title":"Adobe AIR与unity3D都是跨平台，那个更好呢？","url":"https://blog.zengrong.net/post/how-to-choose-a-cross-platform-technology-between-unity3d-and-adobe-air/"},{"categories":["technology"],"content":"这是我对 9RIA上的一个投票 的回答，以此来作为我和Flash的分手信也不错。\n不可否认，AS3一直是小众语言，Flash到现在也一直是页游的唯一靠谱平台。\n我一直不屑于网上跟风要把Flash投死的言论，也一直坚定地认为Flash平台的性能和成熟度高于H5，但还是不可否认，Adobe两条腿走路的态度已经很明确，甚至慢慢在弱化Flash，它是要自己把Flash平台玩死，\n在Flash与H5的这场战争中，我一直在力挺Flash，可到了现在，我内心已经放弃了Flash。 从我在之乎上的回答 ，可以看到这种转变 。\n作为一个 真正的 程序员，本来就不应该把自己限制在某个平台或者某个语言之上。无论是 Unity 还是 cocos2d-x 都不难学。前面有人说到 Unity 和 cocos2d-x 也会死，这个我认同，但他们的死法不同：\n如果学 Unity，在Unity死的时候，开发者会碰到和Flash死掉一样的情况，因为Unity和Flash的情况非常类似，闭源、完全依赖官方。只是现在Unity对开发者相对于Flash要友好些而已。 如果学 cocos2d-x ，则不存在Flash和Unity的问题。 cocos2d-x 只是个框架，不是个平台（虽然现在触控极想将其做成平台）。只要程序员在学习的时候注意一点，不过于依赖闭源技术（例如那个超级难用的CocoStudio），那么 cocos2d-x 死了也没什么。你掌握的OpenGL、C++等等技术已经为你铺好了前端核心的康庄大道，你大可以像 云风 那样写/改个自己的引擎出来。 况且，一个开源引擎，真的会“死”么？\n所以，我强烈推荐有兴趣的同学转向 cocos2d-x，不要再犹豫了！！\n对C++不感冒的，推荐使用 quick-cocos2d-x ，这个 cocos2d-x 的lua绑定增强版。这也是我正在使用的框架。\n","date":"2014-01-21","description":"","lastmod":"2014-01-21T06:37:27Z","slug":"how_to_choose_a_cross-platform_technology_between_unity3d_and_adobe_air","tags":["adobe","cocos2d-x","flash"],"title":"你是否已经放弃了flash,转投其他行业？","url":"https://blog.zengrong.net/post/how_to_choose_a_cross-platform_technology_between_unity3d_and_adobe_air/"},{"categories":["technology"],"content":"在Windows批处理中处理网上邻居中的文件\nuse UNC files in command line\n在 Windows 批处理中，默认不能处理网上邻居中的文件。本文介绍解决办法。\n问题 当使用 cd 命令进入一个网上邻居目录的时候，会出现下面的错误提示：\n1C:\\Users\\zrong\u0026gt;cd \\\\192.168.18.18\\project 2\u0026#39;\\\\192.168.18.18\\project\u0026#39; 3CMD does not support UNC paths as current directories. 那么，如何处理？\n使用 pushd / popd 使用 pushd 命令，可以自动将网上邻居中的共享目录映射为一个驱动器。\n例如，下面的命令映射了一个 z: 驱动器，然后我们就可以像操作本地硬盘一样操作该驱动器。\n1C:\\Users\\zrong\u0026gt;pushd \\\\192.168.18.18\\project 2Z:\\\u0026gt; pushd 只能将共享的目录映射为驱动器的根目录，如果映射的是一个子目录，则会自动进入该驱动器的子目录。\n例如：\n1C:\\Users\\zrong\u0026gt;pushd \\\\192.168.18.18\\project\\lulala 2 3Z:\\lulala\u0026gt; 使用完毕之后，使用 popd 命令取消映射。\n1Z:\\\u0026gt;popd 2 3C:\\Users\\zrong\u0026gt; 注意， pushd / popd 是可以递归使用的，在 pushd 映射成功的驱动器中，可以继续使用 pushd 映射新的驱动器。\n使用 net use 使用强大的 net 命令集中的 use 命令，也可以映射网络驱动器。\n1C:\\Users\\zrong\u0026gt;net use z: \\\\192.168.18.18\\project 2The command completed successfully. 3 4C:\\Users\\zrong\u0026gt;z: 5Z:\\\u0026gt; 与 pushd 不同的是，net use 可以直接将一个共享目录的子目录映射成为驱动器的根目录。例如：\n1C:\\Users\\zrong\u0026gt;net use z: \\\\192.168.18.18\\project\\lulala 2The command completed successfully. 此时， z: 驱动器的根目录就是 \\\\192.168.18.18\\project\\lulala 。\n删除映射：\n1C:\\Users\\zrong\u0026gt;net use z: /del /yes 2z: was deleted successfully. 参考文章 Simplify UNC usage in command line batch files\n","date":"2014-01-14","description":"","lastmod":"2014-01-14T09:22:53Z","slug":"use_unc_in_command_line","tags":["shell","windows"],"title":"在Windows批处理中处理网上邻居中的文件","url":"https://blog.zengrong.net/post/use_unc_in_command_line/"},{"categories":["technology"],"content":"这是一个 luasocket 范例。\n为了便于使用，我封装了 luasocket 到 cc.net.SocketTCP 类中。这个范例展示如何使用 cc.net.SocketTCP 。\n同时，在本范例中还使用了 cc.utils.ByteArray 和 cc.utils.ByteArrayVarint 。\n要了解更多关于 cc.net.SocketTCP 和 cc.utils.ByteArray 的信息，请阅读 一个LuaSocket封装 和 用lua实现ByteArray和ByteArrayVarint .\n在本范例的 net 包中，有3个 lua 类：\nnet.PacketBuffer\n我们收到的数据包经常是不完整的，特别是当数据包比较大的时候。因此我们必须确保数据包完整才能使用。PacketBuffer 就是做这件事的。 net.Protocol\n这个类把数据包解析成人类可读的格式。它转换一个数据包到一个lua table，并且为每个数据项命名。数据项的名称定义在 net.protocols 文件中。 net.protocols 这是你的协议定义文件。你可以写一个生成器程序来生成它。 下面是本范例运行的log文件。当然，你需要自己实现服务端程序。\n[0.2467] [INFO] socket.getTime:1389319197.462940 [0.2472] [INFO] os.gettime:1389319197.000000 [0.2477] [INFO] socket._VERSION: LuaSocket 2.1-rc1 [0.2530] [INFO] Scene \u0026quot;MainScene:onEnter()\u0026quot; [1.9754] [INFO] socket status: SOCKET_TCP_CONNECTED [5.8635] send 1000 packet: 86 7B 00 0B 00 00 00 B1 04 00 03 00 08 10 C0 3E 01 01 [5.8859] socket receive raw data: 86 7B 00 60 00 00 00 00 7D 00 0D 00 09 11 18 20 28 30 38 40 48 50 58 60 00 3C 63 73 76 3D 34 38 32 28 30 31 30 39 2E 31 35 33 32 32 38 29 0A 66 73 3D 31 33 31 28 30 31 30 39 2E 31 35 33 31 35 31 29 0A 67 73 3D 33 35 32 28 30 31 30 39 2E 31 38 30 33 32 37 29 04 74 65 73 74 00 14 50 D6 03 96 01 00 00 00 0F 6E 103 [5.8873] start analyzing... buffer len: 103, available: 103 [5.8878] method:2000 [5.8884] before get meta position:11 [5.8897] after get meta position:25 [5.8904] after get body position:104 [5.8909] dump from: [string \u0026quot;d:/cocos2dx/quick/samples/luasocket/scripts/scenes/MainScene.lua\u0026quot;]:54: in function \u0026lt;[string \u0026quot;d:/cocos2dx/quick/samples/luasocket/scripts/scenes/MainScene.lua\u0026quot;]:48\u0026gt; [5.8921] - \u0026quot;\u0026lt;var\u0026gt;\u0026quot; = { [5.8924] - \u0026quot;body\u0026quot; = { [5.8931] - \u0026quot;ap\u0026quot; = 0 [5.8938] - \u0026quot;crystal\u0026quot; = 150 [5.8944] - \u0026quot;dust\u0026quot; = 0 [5.8950] - \u0026quot;gold\u0026quot; = 470 [5.8955] - \u0026quot;goodsMaxNum\u0026quot; = 110 [5.8959] - \u0026quot;goodsNum\u0026quot; = 15 [5.8965] - \u0026quot;lv\u0026quot; = 20 [5.8971] - \u0026quot;mithril\u0026quot; = 0 [5.8975] - \u0026quot;money\u0026quot; = 80 [5.8981] - \u0026quot;flag\u0026quot; = 0 [5.8987] - \u0026quot;name\u0026quot; = \u0026quot;test\u0026quot; [5.8993] - \u0026quot;sex\u0026quot; = 0 [5.8999] - \u0026quot;ver\u0026quot; = \u0026quot;csv=482(0109.153228)\\nfs=131(0109.153151)\\ngs=352(0109.180327)\u0026quot; [5.9008] - } [5.9013] - \u0026quot;method\u0026quot; = 2000 [5.9019] - \u0026quot;ver\u0026quot; = 0 [5.9025] - } ","date":"2014-01-10","description":"","lastmod":"2014-01-10T03:49:04Z","slug":"lua-sockettcp-sample-in-quick-cocos2d-x","tags":["cocos2d-x","lua","socket"],"title":"基于quick-cocos2d-x的LuaSocket范例","url":"https://blog.zengrong.net/post/lua-sockettcp-sample-in-quick-cocos2d-x/"},{"categories":["technology"],"content":"4TB HDD + RAID1 on CentOS 6.1\n本篇是 基于JMicron JMB363在CentOS上架设 RAID 的问题 的续集。在本篇里面，我放弃了使用 JMicron JMB363 实现硬 RAID 的方式，改为了使用软 RAID。\n配置 主板： MSI EFINITY CPU： Intel Core2 内存： 金士顿DDR2 800 2Gx2 硬盘： /dev/sda 希捷 500Gx1 做主盘安装操作系统。分成3个区，50G挂载 /， 4G挂载为交换分区， 剩余400G挂载为 /home，采用LVM管理分区。 /dev/sdb 第一块4T硬盘 /dev/sdc 第二块4T硬盘 操作系统：CentOS 6.1 对物理硬盘进行分区 大于 2TB 的硬盘，已经不能再使用 MBR 分区表，必须改为使用 GPT 分区表。\nfdisk 命令不能识别和创建 GPT 分区表，因此我采用 parted 命令来分区。\nparted /dev/sdb (parted) mklabel gpt (parted) unit TB (parted) mkpart primary 0.00TB 4.00TB (parted) print 这样就创建了一个4TB的分区。使用同样的方法对 dev/sdc 进行操作。\n创建 RAID1 逻辑分区 mdadm --create /dev/md0 --level=raid1 --raid-devices=2 /dev/sdb1 /dev/sdc1 上面的命令含义是：\n创建一个多重分区设备 /dev/md0； RAID 方式为 raid1 这个设备使用两个分区 /dev/sdb1 和 /dev/sdc1。 可以使用下面的命令查看这个设备的状态：\ncat /proc/mdstat md0 : active raid1 sdc1[1] sdb1[0] 3907015544 blocks super 1.2 [2/2] [UU] [==\u0026gt;..................] resync = 11.8% (462385856/3907015544) finish=363.3min speed=157999K/sec 下面的命令获取到的状态更加详细：\nmdadm --detail /dev/md0 /dev/md0: Version : 1.2 Creation Time : Tue Jan 7 16:20:31 2014 Raid Level : raid1 Array Size : 3907015544 (3726.02 GiB 4000.78 GB) Used Dev Size : 3907015544 (3726.02 GiB 4000.78 GB) Raid Devices : 2 Total Devices : 2 Persistence : Superblock is persistent Update Time : Tue Jan 7 16:55:18 2014 State : active, resyncing Active Devices : 2 Working Devices : 2 Failed Devices : 0 Spare Devices : 0 Resync Status : 11% complete Name : localhost.localdomain:0 (local to host localhost.localdomain) UUID : dd46e62a:4b4b7e2a:2d8e962e:c1707817 Events : 2 Number Major Minor RaidDevice State 0 8 17 0 active sync /dev/sdb1 1 8 33 1 active sync /dev/sdc1 创建并挂载逻辑分区 parted /dev/dm0 (parted) mklabel gpt (parted) unit TB (parted) mkpart primary 0.00TB 4.00TB (parted) print (parted) quit 这会创建一个分区 /dev/dm0p1 ，格式化它：\nmkfs.ext4 /dev/dm0p1 将下面的内容加入 /etc/fstab ，我将它用作共享盘：\n/dev/md0p1\t/ /home/share ext4 defaults 1 1 重启或者直接使用 mount -a 挂载。\n问题出现 上面的 RAID 分区挂载和使用均成功。但服务器重启后，CentOS无法启动，进入了 (Repair Filesystem) 提示符。\nCentOS 报告说 /dev/dm0p1 分区找不到，挂载 /home 分区错误。\n其实 /home 分区没有问题。只是因为 /dev/dm0p1 是挂载在 /home 中的，由于 /dev/dm0p1 找不到，导致了后续的挂载出错。\n在 (Repair Filesystem) 中使用任何lvm管理命令都是无效的，例如输入 lvdisplay lvs pvs vgs 等命令，都会显示同一个错误：\nFile-based locking initialisation failed 为了解决这个问题，先要将原来的挂载RAID分区的部分取消。可是在 (Repair Filesystem) 提示符下，对硬盘的管理是只读的，修改 /etc/fstab 将不能保存。\n跳出 Repair Filesystem 要解决这个问题，需要重新挂载文件系统为可写。在 (Repair Filesystem) 中输入 root 密码之后，输入如下命令：\nmount -w -o remount / 然后再次编辑 /etc/fstab ，取消对 RAID 分区的挂载，重启即可正常进入系统。\n问题解决 这个问题出现的原因，是 /dev/dm0 变成了 /dev/dm127 ，那么相应的分区也从 /dev/dm0p1 变成了 /dev/dm127p1 ，导致CentOS启动的时候找不到这个原来的分区。\n这可能是我偷懒没有提供 /etc/mdadm.conf 导致的问题。 但再次重启 /dev/dm127 的名称也不会变化了。\n重新调整 /etc/fstab ，重启系统正常。我这个冒牌运维的工作终于可以结束了。\n参考文章 3TB HDD+RAID5+LVM on CentOS 6.2 管理软件磁盘阵列 PC 上的 LVM 灾难修复 修复虚拟磁盘LVM表 File-based locking initialisation failed. Server Down ","date":"2014-01-07","description":"","lastmod":"2014-01-07T09:40:58Z","slug":"4tb-hdd-raid1-on-centos-6-1","tags":["centos","linux"],"title":"在CentOS 6.1上配置 4TB硬盘+RAID1","url":"https://blog.zengrong.net/post/4tb-hdd-raid1-on-centos-6-1/"},{"categories":["impressions"],"content":"二零一三俱往矣，有过欢笑和脾气。携手奋战月湖桥，一二零一要雄起！我亲爱的小伙伴，感谢一路都有你。\n","date":"2013-12-31","description":"","lastmod":"2013-12-31T10:19:10Z","slug":"2011","tags":[],"title":"喜迎2014","url":"https://blog.zengrong.net/post/2011/"},{"categories":["technology"],"content":"配置 主板： MSI EFINITY CPU： Intel Core2 内存： 金士顿DDR2 800 2Gx2 硬盘：希捷 500Gx1 做主盘安装操作系统。500G硬盘分成3个区，50G挂载 /， 4G挂载为交换分区， 剩余400G挂载为 /home。 操作系统：CentOS 6.1\n需求 购买了2块希捷4T做RAID1，当作数据盘。\n系统已经运行2年，我不希望重装操作系统，RAID1的磁盘仅做数据盘使用，分成1个4TB分区。\n已经完成的步骤 主板上自带 JMicron JMB363 芯片，支持RAID0/1/JBOD。JMB363提供的2个SATA2口连接2块4T硬盘。\n我已经使用主板BIOS中自带的JMicro工具将2块4T硬盘组成了一个 RAID Disk Drive。见下图：\n![][/uploads/2013/12/jmicron.jpg]\n根据 jimicron 提供的 FAQ，Linux内核已经自带了JMB363的驱动。\n而要让CentOS支持RAID硬盘，需要使用 dmraid 工具。这篇文章 详细介绍了该工具的用法。\n问题 使用 jmiron 虚拟出来的RAID设备，使用 parted 只能认到1.8T，即使是使用 GPT 分区表也是如此。\n最终我放弃了使用硬RAID，而改为使用软RAID。见 4TB HDD + RAID1 on CentOS 6.1 。\n参考 How to Create Partition on Linux for \u0026gt;2TB Size using Parted GPT dmraid的介绍 ","date":"2013-12-30","description":"","lastmod":"2013-12-30T10:57:21Z","slug":"centos_jmicron_fmb363_raid1","tags":["centos","linux"],"title":"基于JMicron JMB363在CentOS上架设 RAID 的问题","url":"https://blog.zengrong.net/post/centos_jmicron_fmb363_raid1/"},{"categories":["use"],"content":"Samsung galaxy S3 i535，手机网络信号一直正常。\n最近几天手机经常没有数据连接，但短信、电话正常。\n有时手机会偶尔显示有3G数据连接，有时偶尔显示1x数据连接，而大多数情况下是没有任何数据连接。\n原因-中国电信作怪 这款手机是美版水货，默认开通了eHRPD网络(eHRPD是对EvDo网络的演进和增强)。在找不到eHRPD网络的时候，会连接目前的电信目前的EvDo网络。\n但这几天电信在测试eHRPD网络，导致这款手机自动找到了该网络并进行了连接。但由于目前测试网络经常没有信号，我的手机当然也就没有数据连接了。\n解决方法 关闭手机对eHRPD网络的支持即可。步骤如下：\n打开拨号面板输入 *#22745927； 在开启的 hiddenMenu 界面中选择 enable ，启用隐藏菜单； 打开拨号面板输入 *#197328640#； 在开启的工程模式面板中依次选择菜单 CDMA-DATA-ehrpd ； 你会发现默认是 enable 状态，将其设置为 disable 即可禁用； 重启手机。 参考文章 eHRPD：CDMA迈进LTE的桥梁 成功解决I535某些地区无法上网或无法上3G问题 ","date":"2013-12-27","description":"","lastmod":"2013-12-27T08:30:43Z","slug":"samsung-galaxy-s3-i535-network","tags":["smartphone"],"title":"Samsung galaxy S3 i535 无3G信号解决方案","url":"https://blog.zengrong.net/post/samsung-galaxy-s3-i535-network/"},{"categories":["technology"],"content":"Cocos2d-x 中的帧动画\n在 Cocos2d-x 中可以通过 CCAnimcation 来实现一个帧动画。在这种动画中，根据时间的流逝显示不同的纹理，形成动画。这就是我们平常所说的 逐帧动画。\n本文将讨论这种动画的使用方法，以及如何利用现有的工具来简化使用和提高开发效率。\n本文基于 Cocos2d-x 2.2.1\n1. 直接使用代码实现动画 当纹理作为单独的图像文件出现的时候，动画是这样实现的：\n1//建立一个帧动画，填充14个纹理 2CCAnimation* animation = CCAnimation::create(); 3for( int i=1;i\u0026lt;15;i++) 4{ 5\tchar szName[100] = {0}; 6\tsprintf(szName, \u0026#34;Images/grossini_dance_%02d.png\u0026#34;, i); 7\tanimation-\u0026gt;addSpriteFrameWithFileName(szName); 8} 9//在2.8秒中显示14帧，计算延迟 10animation-\u0026gt;setDelayPerUnit(2.8f / 14.0f); 11animation-\u0026gt;setRestoreOriginalFrame(true); 12 13CCAnimate* action = CCAnimate::create(animation); 14 15// grossini 是一个CCSprite，使用它播放一个自动回绕的动画 16grossini-\u0026gt;runAction(CCSequence::create(action, action-\u0026gt;reverse(), NULL)); 如果纹理处于plist文件中，则可以这样实现：\n1//将纹理提前加入精灵帧缓存 2CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache(); 3frameCache-\u0026gt;addSpriteFramesWithFile(\u0026#34;animations/grossini.plist\u0026#34;); 4 5//建立15个元素的精灵帧数组 6CCArray* animFrames = CCArray::createWithCapacity(15); 7char str[100] = {0}; 8for(int i = 1; i \u0026lt; 15; i++) 9{ 10\tsprintf(str, \u0026#34;grossini_dance_%02d.png\u0026#34;,i); 11\tCCSpriteFrame *frame = frameCache-\u0026gt;spriteFrameByName(str); 12\tanimFrames-\u0026gt;addObject(frame); 13} 14 15CCAnimation *animation = CCAnimation::createWithSpriteFrames(animFrames, 0.2f); 16CCAnimate *anim = CCAnimate::create(animation); 17 18//创建一个精灵用于显示动画 19CCSprite *grossini = CCSprite::create(); 20CCSpriteFrame *frame = frameCache-\u0026gt;spriteFrameByName(\u0026#34;grossini_dance_01.png\u0026#34;); 21grossini-\u0026gt;setDisplayFrame(frame); 22 23//播放一个永久循环的动画 24grossini-\u0026gt;runAction(CCRepeatForever::create(anim)) 2. 使用帧动画定义文件播放动画 使用代码实现动画的缺点是必须将动画的纹理名称硬编码到源码中，这样在修改纹理的时候必须重编编译程序。\n如果使用帧动画定义文件，就可以避免这个问题，并得到更多的便利。\n这种文件有2种格式：\nformat 1，定义了一个动画包含哪些帧以及帧纹理的名称，但是却并不能指定这些纹理从哪里加载。因此，我们必须提前加载将这些帧纹理使用的文件加载到缓存中。它可以为一个动画指定平均的帧延迟。 format 2，可以直接指定需要的纹理文件的名称，也可以定义每帧的延迟。 CCAnimationCache::addAnimationsWithDictionary 负责解析这两种格式，并将它们转换成 CCAnimation ，同时保存在缓存中。\n2.1 format 1 动画格式的播放 这是 format 1 格式的内容，文件名为 armatures.plist 。它定义了 dance_1/dance_2/dance_3 这3个动画，每个动画的帧延迟为0.2秒。\n这个格式一看就懂，不需要详细介绍。\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; 2\u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; 3\u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; 4\u0026lt;dict\u0026gt; 5\t\u0026lt;key\u0026gt;animations\u0026lt;/key\u0026gt; 6\t\u0026lt;dict\u0026gt; 7\t\u0026lt;key\u0026gt;dance_1\u0026lt;/key\u0026gt; 8\t\u0026lt;dict\u0026gt; 9\t\u0026lt;key\u0026gt;delay\u0026lt;/key\u0026gt; 10\t\u0026lt;real\u0026gt;0.2\u0026lt;/real\u0026gt; 11\t\u0026lt;key\u0026gt;frames\u0026lt;/key\u0026gt; 12\t\u0026lt;array\u0026gt; 13\t\u0026lt;string\u0026gt;grossini_dance_01.png\u0026lt;/string\u0026gt; 14\t\u0026lt;string\u0026gt;grossini_dance_02.png\u0026lt;/string\u0026gt; 15\t\u0026lt;string\u0026gt;grossini_dance_03.png\u0026lt;/string\u0026gt; 16\t\u0026lt;string\u0026gt;grossini_dance_04.png\u0026lt;/string\u0026gt; 17\t\u0026lt;string\u0026gt;grossini_dance_05.png\u0026lt;/string\u0026gt; 18\t\u0026lt;string\u0026gt;grossini_dance_06.png\u0026lt;/string\u0026gt; 19\t\u0026lt;string\u0026gt;grossini_dance_07.png\u0026lt;/string\u0026gt; 20\t\u0026lt;string\u0026gt;grossini_dance_08.png\u0026lt;/string\u0026gt; 21\t\u0026lt;string\u0026gt;grossini_dance_09.png\u0026lt;/string\u0026gt; 22\t\u0026lt;string\u0026gt;grossini_dance_10.png\u0026lt;/string\u0026gt; 23\t\u0026lt;string\u0026gt;grossini_dance_11.png\u0026lt;/string\u0026gt; 24\t\u0026lt;string\u0026gt;grossini_dance_12.png\u0026lt;/string\u0026gt; 25\t\u0026lt;string\u0026gt;grossini_dance_13.png\u0026lt;/string\u0026gt; 26\t\u0026lt;string\u0026gt;grossini_dance_14.png\u0026lt;/string\u0026gt; 27\t\u0026lt;/array\u0026gt; 28\t\u0026lt;/dict\u0026gt; 29\t\u0026lt;key\u0026gt;dance_2\u0026lt;/key\u0026gt; 30\t\u0026lt;dict\u0026gt; 31\t\u0026lt;key\u0026gt;delay\u0026lt;/key\u0026gt; 32\t\u0026lt;real\u0026gt;0.2\u0026lt;/real\u0026gt; 33\t\u0026lt;key\u0026gt;frames\u0026lt;/key\u0026gt; 34\t\u0026lt;array\u0026gt; 35\t\u0026lt;string\u0026gt;grossini_dance_gray_01.png\u0026lt;/string\u0026gt; 36\t\u0026lt;string\u0026gt;grossini_dance_gray_02.png\u0026lt;/string\u0026gt; 37\t\u0026lt;string\u0026gt;grossini_dance_gray_03.png\u0026lt;/string\u0026gt; 38\t\u0026lt;string\u0026gt;grossini_dance_gray_04.png\u0026lt;/string\u0026gt; 39\t\u0026lt;string\u0026gt;grossini_dance_gray_05.png\u0026lt;/string\u0026gt; 40\t\u0026lt;string\u0026gt;grossini_dance_gray_06.png\u0026lt;/string\u0026gt; 41\t\u0026lt;string\u0026gt;grossini_dance_gray_07.png\u0026lt;/string\u0026gt; 42\t\u0026lt;string\u0026gt;grossini_dance_gray_08.png\u0026lt;/string\u0026gt; 43\t\u0026lt;string\u0026gt;grossini_dance_gray_09.png\u0026lt;/string\u0026gt; 44\t\u0026lt;string\u0026gt;grossini_dance_gray_10.png\u0026lt;/string\u0026gt; 45\t\u0026lt;string\u0026gt;grossini_dance_gray_11.png\u0026lt;/string\u0026gt; 46\t\u0026lt;string\u0026gt;grossini_dance_gray_12.png\u0026lt;/string\u0026gt; 47\t\u0026lt;string\u0026gt;grossini_dance_gray_13.png\u0026lt;/string\u0026gt; 48\t\u0026lt;string\u0026gt;grossini_dance_gray_14.png\u0026lt;/string\u0026gt; 49\t\u0026lt;/array\u0026gt; 50\t\u0026lt;/dict\u0026gt; 51\t\u0026lt;key\u0026gt;dance_3\u0026lt;/key\u0026gt; 52\t\u0026lt;dict\u0026gt; 53\t\u0026lt;key\u0026gt;delay\u0026lt;/key\u0026gt; 54\t\u0026lt;real\u0026gt;0.2\u0026lt;/real\u0026gt; 55\t\u0026lt;key\u0026gt;frames\u0026lt;/key\u0026gt; 56\t\u0026lt;array\u0026gt; 57\t\u0026lt;string\u0026gt;grossini_blue_01.png\u0026lt;/string\u0026gt; 58\t\u0026lt;string\u0026gt;grossini_blue_02.png\u0026lt;/string\u0026gt; 59\t\u0026lt;string\u0026gt;grossini_blue_03.png\u0026lt;/string\u0026gt; 60\t\u0026lt;string\u0026gt;grossini_blue_04.png\u0026lt;/string\u0026gt; 61\t\u0026lt;/array\u0026gt; 62\t\u0026lt;/dict\u0026gt; 63\t\u0026lt;/dict\u0026gt; 64\u0026lt;/dict\u0026gt; 65\u0026lt;/plist\u0026gt; 使用下面的代码将这3个动画串起来并连续播放：\n1CAnimationCache *animCache = CCAnimationCache::sharedAnimationCache(); 2 3//把动画定义文件载入缓存中 4animCache-\u0026gt;addAnimationsWithFile(\u0026#34;animations/animations.plist\u0026#34;); 5 6//根据动画定义文件中的名称获取3个动画 7CCAnimation *normal = animCache-\u0026gt;animationByName(\u0026#34;dance_1\u0026#34;); 8normal-\u0026gt;setRestoreOriginalFrame(true); 9CCAnimation *dance_grey = animCache-\u0026gt;animationByName(\u0026#34;dance_2\u0026#34;); 10dance_grey-\u0026gt;setRestoreOriginalFrame(true); 11CCAnimation *dance_blue = animCache-\u0026gt;animationByName(\u0026#34;dance_3\u0026#34;); 12dance_blue-\u0026gt;setRestoreOriginalFrame(true); 13 14//创建Action进行播放 15CCAnimate *animN = CCAnimate::create(normal); 16CCAnimate *animG = CCAnimate::create(dance_grey); 17CCAnimate *animB = CCAnimate::create(dance_blue); 18CCSequence *seq = CCSequence::create(animN, animG, animB, NULL); 19 20//创建一个不带纹理的精灵 21CCSprite *grossini = CCSprite::create(); 22//获取一个精灵帧，设置成精灵的第1帧 23CCSpriteFrame *frame = frameCache-\u0026gt;spriteFrameByName(\u0026#34;grossini_dance_01.png\u0026#34;); 24grossini-\u0026gt;setDisplayFrame(frame); 25addChild(grossini); 26 27//播放动画 28grossini-\u0026gt;runAction(seq); 2.2 format 2 动画格式plist文件内容 这是 format 2 格式的内容，文件名为 armatures-2.plist 。它定义了 dance_1 这个动画。其中：\ndelayPerUnit 定义的就是每帧之间的间隔，是 0.2 秒。 delayUnits 定义了每帧的延迟值，它会覆盖 delayPerUnit 的定义。实际上，动画持续的时间，就是 所有帧的 delayUnits 的和乘以 delayPerUnit 。 notification 中定义的值会保存在 CCAnimationFrame 中。这些值的本意是在动画播放到设置值的帧时，触发一些事件。但是在 Cocos2d-x 2.2.1 中，这个事件触发并没有实现。 properties 定义了动画格式的版本以及这个动画中使用的帧来自于哪些纹理。 1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; 2\u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; 3\u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; 4\u0026lt;dict\u0026gt; 5\t\u0026lt;key\u0026gt;animations\u0026lt;/key\u0026gt; 6\t\u0026lt;dict\u0026gt; 7\t\u0026lt;key\u0026gt;dance_1\u0026lt;/key\u0026gt; 8\t\u0026lt;dict\u0026gt; 9\t\u0026lt;key\u0026gt;delayPerUnit\u0026lt;/key\u0026gt; 10\t\u0026lt;real\u0026gt;0.2\u0026lt;/real\u0026gt; 11\t\u0026lt;key\u0026gt;restoreOriginalFrame\u0026lt;/key\u0026gt; 12\t\u0026lt;true/\u0026gt; 13\t\u0026lt;key\u0026gt;loops\u0026lt;/key\u0026gt; 14\t\u0026lt;integer\u0026gt;2\u0026lt;/integer\u0026gt; 15\t\u0026lt;key\u0026gt;frames\u0026lt;/key\u0026gt; 16\t\u0026lt;array\u0026gt; 17\t\u0026lt;dict\u0026gt; 18\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 19\t\u0026lt;string\u0026gt;grossini_dance_01.png\u0026lt;/string\u0026gt; 20\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 21\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 22\t\u0026lt;key\u0026gt;notification\u0026lt;/key\u0026gt; 23\t\u0026lt;dict\u0026gt; 24\t\u0026lt;key\u0026gt;firstframe\u0026lt;/key\u0026gt; 25\t\u0026lt;true/\u0026gt; 26\t\u0026lt;/dict\u0026gt; 27\t\u0026lt;/dict\u0026gt; 28\t\u0026lt;dict\u0026gt; 29\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 30\t\u0026lt;string\u0026gt;grossini_dance_02.png\u0026lt;/string\u0026gt; 31\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 32\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 33\t\u0026lt;/dict\u0026gt; 34\t\u0026lt;dict\u0026gt; 35\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 36\t\u0026lt;string\u0026gt;grossini_dance_03.png\u0026lt;/string\u0026gt; 37\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 38\t\u0026lt;real\u0026gt;0.5\u0026lt;/real\u0026gt; 39\t\u0026lt;/dict\u0026gt; 40\t\u0026lt;dict\u0026gt; 41\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 42\t\u0026lt;string\u0026gt;grossini_dance_04.png\u0026lt;/string\u0026gt; 43\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 44\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 45\t\u0026lt;/dict\u0026gt; 46\t\u0026lt;dict\u0026gt; 47\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 48\t\u0026lt;string\u0026gt;grossini_dance_05.png\u0026lt;/string\u0026gt; 49\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 50\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 51\t\u0026lt;/dict\u0026gt; 52\t\u0026lt;dict\u0026gt; 53\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 54\t\u0026lt;string\u0026gt;grossini_dance_06.png\u0026lt;/string\u0026gt; 55\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 56\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 57\t\u0026lt;key\u0026gt;notification\u0026lt;/key\u0026gt; 58\t\u0026lt;dict\u0026gt; 59\t\u0026lt;key\u0026gt;key1\u0026lt;/key\u0026gt; 60\t\u0026lt;integer\u0026gt;1234\u0026lt;/integer\u0026gt; 61\t\u0026lt;key\u0026gt;key2\u0026lt;/key\u0026gt; 62\t\u0026lt;false/\u0026gt; 63\t\u0026lt;/dict\u0026gt; 64\t\u0026lt;/dict\u0026gt; 65\t\u0026lt;dict\u0026gt; 66\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 67\t\u0026lt;string\u0026gt;grossini_dance_07.png\u0026lt;/string\u0026gt; 68\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 69\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 70\t\u0026lt;/dict\u0026gt; 71\t\u0026lt;dict\u0026gt; 72\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 73\t\u0026lt;string\u0026gt;grossini_dance_08.png\u0026lt;/string\u0026gt; 74\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 75\t\u0026lt;integer\u0026gt;2\u0026lt;/integer\u0026gt; 76\t\u0026lt;/dict\u0026gt; 77\t\u0026lt;dict\u0026gt; 78\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 79\t\u0026lt;string\u0026gt;grossini_dance_09.png\u0026lt;/string\u0026gt; 80\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 81\t\u0026lt;real\u0026gt;0.5\u0026lt;/real\u0026gt; 82\t\u0026lt;/dict\u0026gt; 83\t\u0026lt;dict\u0026gt; 84\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 85\t\u0026lt;string\u0026gt;grossini_dance_10.png\u0026lt;/string\u0026gt; 86\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 87\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 88\t\u0026lt;/dict\u0026gt; 89\t\u0026lt;dict\u0026gt; 90\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 91\t\u0026lt;string\u0026gt;grossini_dance_11.png\u0026lt;/string\u0026gt; 92\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 93\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 94\t\u0026lt;/dict\u0026gt; 95\t\u0026lt;dict\u0026gt; 96\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 97\t\u0026lt;string\u0026gt;grossini_dance_12.png\u0026lt;/string\u0026gt; 98\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 99\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 100\t\u0026lt;/dict\u0026gt; 101\t\u0026lt;dict\u0026gt; 102\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 103\t\u0026lt;string\u0026gt;grossini_dance_13.png\u0026lt;/string\u0026gt; 104\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 105\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 106\t\u0026lt;/dict\u0026gt; 107\t\u0026lt;dict\u0026gt; 108\t\u0026lt;key\u0026gt;spriteframe\u0026lt;/key\u0026gt; 109\t\u0026lt;string\u0026gt;grossini_dance_14.png\u0026lt;/string\u0026gt; 110\t\u0026lt;key\u0026gt;delayUnits\u0026lt;/key\u0026gt; 111\t\u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; 112\t\u0026lt;key\u0026gt;notification\u0026lt;/key\u0026gt; 113\t\u0026lt;dict\u0026gt; 114\t\u0026lt;key\u0026gt;lastframe\u0026lt;/key\u0026gt; 115\t\u0026lt;true/\u0026gt; 116\t\u0026lt;/dict\u0026gt; 117\t\u0026lt;/dict\u0026gt; 118\t\u0026lt;/array\u0026gt; 119\t\u0026lt;/dict\u0026gt; 120\t\u0026lt;/dict\u0026gt; 121\t\u0026lt;key\u0026gt;properties\u0026lt;/key\u0026gt; 122\t\u0026lt;dict\u0026gt; 123\t\u0026lt;key\u0026gt;spritesheets\u0026lt;/key\u0026gt; 124\t\u0026lt;array\u0026gt; 125\t\u0026lt;string\u0026gt;animations/grossini.plist\u0026lt;/string\u0026gt; 126\t\u0026lt;string\u0026gt;animations/grossini_blue.plist\u0026lt;/string\u0026gt; 127\t\u0026lt;string\u0026gt;animations/grossini_family.plist\u0026lt;/string\u0026gt; 128\t\u0026lt;/array\u0026gt; 129\t\u0026lt;key\u0026gt;format\u0026lt;/key\u0026gt; 130\t\u0026lt;integer\u0026gt;2\u0026lt;/integer\u0026gt; 131\t\u0026lt;/dict\u0026gt; 132\u0026lt;/dict\u0026gt; 133\u0026lt;/plist\u0026gt; 使用下面的代码播放这个动画：\n1//解析动画定义文件，将动画载入缓存 2CCAnimationCache *cache = CCAnimationCache::sharedAnimationCache(); 3cache-\u0026gt;addAnimationsWithFile(\u0026#34;animations/animations-2.plist\u0026#34;); 4 5//创建动画 6CCAnimation *animation = cache-\u0026gt;animationByName(\u0026#34;dance_1\u0026#34;); 7CCAnimate* action = CCAnimate::create(animation); 8 9//播放一个回绕的动画，grossini是一个已存在的精灵 10grossini-\u0026gt;runAction(CCSequence::create(action, action-\u0026gt;reverse(), NULL)); （全文完）\n","date":"2013-12-21","description":"","lastmod":"2013-12-21T09:18:03Z","slug":"explain-cocos2d-x-plist-file-format","tags":["cpp","cocos2d-x","plist"],"title":"Cocos2d-x 中的帧动画","url":"https://blog.zengrong.net/post/explain-cocos2d-x-plist-file-format/"},{"categories":["technology"],"content":"2015-05-04 更新： 对于 cocos2d-x v3 ，可参考 MenuItem 的用法变化 。\n从 AS3 转到 Cocos2d-x 后，最纠结的一点就是，事件怎么调？\n在 AS3 中，只要继承一下 EventDispatcher ，就能发事件了。而 C++ 没有这些。所有的轮子，都要自己造。\n好在 Cocos2d-x 内部已经造好了一些轮子供我们使用。这些轮子分别是：\n回调函数 CCNotificationCenter Signals 本文基于 cocos2d-x 2.1.5\n1. Cocos2d-x 中的回调函数 Cocos2d-x 内部大量使用回调函数来进行消息传递（或者说事件调用）。 例如 CCMenu 的事件触发，CCAction 中的结束回调等等。\n具体实现在 cocos2dx/cocoa/CCObject.h 中，这里包含了菜单、Action和shedule的回调。\n1typedef void (CCObject::*SEL_SCHEDULE)(float); 2typedef void (CCObject::*SEL_CallFunc)(); 3typedef void (CCObject::*SEL_CallFuncN)(CCNode*); 4typedef void (CCObject::*SEL_CallFuncND)(CCNode*, void*); 5typedef void (CCObject::*SEL_CallFuncO)(CCObject*); 6typedef void (CCObject::*SEL_MenuHandler)(CCObject*); 7typedef void (CCObject::*SEL_EventHandler)(CCEvent*); 8typedef int (CCObject::*SEL_Compare)(CCObject*); 9 10#define schedule_selector(_SELECTOR) (SEL_SCHEDULE)(\u0026amp;_SELECTOR) 11#define callfunc_selector(_SELECTOR) (SEL_CallFunc)(\u0026amp;_SELECTOR) 12#define callfuncN_selector(_SELECTOR) (SEL_CallFuncN)(\u0026amp;_SELECTOR) 13#define callfuncND_selector(_SELECTOR) (SEL_CallFuncND)(\u0026amp;_SELECTOR) 14#define callfuncO_selector(_SELECTOR) (SEL_CallFuncO)(\u0026amp;_SELECTOR) 15#define menu_selector(_SELECTOR) (SEL_MenuHandler)(\u0026amp;_SELECTOR) 16#define event_selector(_SELECTOR) (SEL_EventHandler)(\u0026amp;_SELECTOR) 17#define compare_selector(_SELECTOR) (SEL_Compare)(\u0026amp;_SELECTOR) 以菜单组件常用的 menu_selector 来分析。\n首先，使用typedef定义了一个成员函数指针 SEL_MenuHandler。\n1typedef void (CCObject::*SEL_MenuHandler)(CCObject*); SEL_MenuHandler 是 CCObject 的成员，接收一个 CCObject 指针形参。\n用 C++11 提供的方式，也可以这样写：\n1using SEL_MenuHandler = void (CCObject::*)(CCObject*); 接着，定义进行这种类型转换的宏。\n1#define menu_selector(_SELECTOR) (SEL_MenuHandler)(\u0026amp;_SELECTOR) 这个宏将使用 menu_selector 封装的代码，转换成一个 SEL_MenuHandler 函数指针的定义。(SEL_MenuHandler) 的作用是进行类型强制转换。\n让我们看看具体的使用代码，位于 HelloCpp 项目的 ActionTest.cpp 中：\n1CCMenuItemImage *item1 = CCMenuItemImage::create(s_pPathB1, s_pPathB2, this, menu_selector(ActionsDemo::backCallback) ); 在这句代码中，将 ActionDemo::backCallback 这个函数作为指针传递进入 CCMenuItemImage 中。\nCCMenuItemImage 在 initWithTarget 方法中将 ActionDemo 的实例 this， 以及 this 中的 backCallback 函数保存为 m_pListener 和 m_pfnSelector 。\n1bool CCMenuItem::initWithTarget(CCObject *rec, SEL_MenuHandler selector) 2{ 3 setAnchorPoint(ccp(0.5f, 0.5f)); 4 m_pListener = rec; 5 m_pfnSelector = selector; 6 m_bEnabled = true; 7 m_bSelected = false; 8 return true; 9} 在 CCMenuItemImage 的 activate 方法中，对这个函数指针进行了调用。\n1void CCMenuItem::activate() 2{ 3 if (m_bEnabled) 4 { 5 if (m_pListener \u0026amp;\u0026amp; m_pfnSelector) 6 { 7 (m_pListener-\u0026gt;*m_pfnSelector)(this); 8 } 9 } 10} 若希望对上面函数指针的内容做进一步的了解，可以查看 《C++ Primer中文版（第5版）》 6.7 函数指针 和 19.4.2 成员函数指针 。\n2. CCNotificationCenter CCNotificationCenter 在 cocos2d-x 内部提供了一套观察者模式的实现。\n下面是注册观察者的代码。注意这里依然用到了上面提到的函数指针的方法，使用的是 callfuncO_selector 这个宏。最后一个参数用于保存需要的数据到观察者中，之后可以使用 CCNotificationObserver::getObject() 来获取到这个数据。\n1//定义事件 2#define CLICK_EVENT \u0026#34;clickEvent\u0026#34; 3//注册观察者 4CCNotificationCenter::sharedNotificationCenter()-\u0026gt;addObserver(this, callfuncO_selector(NotifTestScene::onClick), CLICK_EVENT, NULL); 5//收到事件之后要移除观察者以避免内存泄露 6void NotifTestScene::onClick(CCObject* __obj) 7{ 8\tCCMessageBox(static_cast\u0026lt;CCString*\u0026gt;(__obj)-\u0026gt;getCString(), \u0026#34;onClick\u0026#34;); 9\tCCNotificationCenter::sharedNotificationCenter()-\u0026gt;removeObserver(this, CLICK_EVENT); 10} 下面是发送事件的代码。发送事件的同时可以传递一个 CCObject 指针作为数据。\n1CCNotificationCenter::sharedNotificationCenter()-\u0026gt;postNotification(CLICK_EVENT, \u0026amp;CCString(\u0026#34;Hello World\u0026#34;)); CCNotificationCenter 源码位于 cocos2dx/support 目录中。\n3. Signals 我在 获取CCArmature动画的播放状态 一文中对 Signals 做了介绍。\nSignals 并非是 cocos2d-x 内部通信的常用方式，Signals 也并不是 cocos2d-x 核心代码的一部分。\n","date":"2013-12-18","description":"","lastmod":"2013-12-18T15:39:23Z","slug":"events_in_cocos2dx","tags":["cpp","cocos2d-x"],"title":"Cocos2d-x中的事件调用方式汇总","url":"https://blog.zengrong.net/post/events_in_cocos2dx/"},{"categories":["technology"],"content":"在Lua中使用gettext实现多语言支持\nUsing gettext in lua\n2014-06-07更新：加入gettext包含的工具简介。\nGNU gettext 是一套优秀的国际化工具。在 linux 中被大量采用。wordpress 也使用 gettext 实现多语言支持。\n本文介绍如何在 quick-cocos2d-x 中使用 gettext 做多语言支持。同时介绍多语言翻译工具 poedit 对 Lua 语言的支持。\ngettext 简介 gettext 是一套工具集的名称。这套工具集包含 xgettext/msginit/msgfmt 等一套建立模版(POT)、创建PO文件和编译MO文件的工具。\ngettext 包含的工具如下：\nxgettext 从源码中生成POT模版或直接生成PO文件； msginit 基于POT文件生成待翻译的PO文件； msgfmt 将PO文件编译成二进制的MO文件； msgunfmt 顾名思义，将MO文件反编译成PO文件； msgmerge 将POT模版文件与PO文件合并。用于在源码更新之后将新的待翻译内容更新到已经翻译的PO文件中； msgcat 将多个PO文件合并在一起。 使用 gettext 需要涉及这样几个概念：\n源码\n程序的源代码，本文中是 lua 文件； POT 文件\n从源码中扫描得到的翻译模版文件，原始语言取决于源码字符串中使用的自然语言，建议使用英文。纯文本格式； PO 文件 根据 POT 文件建立的各种语言版本的待翻译文件，其中包含原始语言和被翻译的目标语言。纯文本格式； MO 文件 供最终软件实际使用的文件，使用 PO 编译而成。二进制格式。 一般的工作流程是这样的：\n在源码中使用约定的语法来书写字符串，C语言默认是 gettext(\u0026quot;my text\u0026quot;)。在本文中，将使用 _(\u0026quot;my text\u0026quot;) ； 使用 xgettext 从源码中扫描出需要翻译的文本，建立 POT 文件； 使用 msginit 命令根据 POT 文件建立 PO 文件。或者直接在上一步也可以直接建立 PO 文件； 进行人工翻译（当然也可以进行机器翻译），翻译的结果保存在 PO 文件中； 使用 msgfmt 命令将 PO 文件编译成 MO 文件； 在程序中实现调用命令，本文中是 _ 函数，这个函数将读取并解析 MO 文件，根据调用的原始语言文本返回翻译之后的文本。 Poedit 简介 gettext 提供的工具集都是基于命令行的，有些程序员并不习惯命令行。它也没有提供用于翻译工作者的对照翻译工具。\n而 Poedit 则提供了一切。\nPoedit 可以从源码中提取文本生成 PO 文件，也提供了一个GUI界面用于对照翻译。它还可以直接生成最终的 MO 文件。\n当然，在GUI之后，它依然是使用 gettext 来处理的，但这些具体的细节被隐藏了。\n使用 Poedit 生成和翻译 PO 文件 软件设置 1.开启 Poedit，执行 File - Preferences 命令，填入自己的个人信息。\n2.切换到 Editor tab，选中 Automatically compile .mo file on save 和 Show summary after catalog update 两项。\n3.切换到 Parsers tab，这里提供了几种源码解析器，但默认没有Lua。我们下面将加入Lua源码解析。\n4.点击 New 新建一个源码解析器，进行如下图的设置。\n这些设置基本上与 C/C++ 的设置一致，只是修改了扩展名支持以及 Parser command 栏位。在 xgettext 命令的参数中，-C 就是 --language=C 的简写。\n接下来将新建一个项目进行设置。\n项目设置 1.执行 File - New catelog... 命令，在 Translation properties tab 中加入自己的信息。这里的 Language 选项只是个显示选项而已，并不能决定你使用的是何种语言，也不和刚才的软件设置中的 Parsers 相关。\n2.设定源代码路径，这个非常重要。\n我采用 quick 默认的目录风格，所有的源码放在 scripts 中。同时我建立了一个新的 i18n 目录，这个目录与 scripts 目录同级。生成的 PO 文件将保存在这个目录中。\n那么对于当前正在编辑的 PO 文件来说， Base path 就应该设置成 ..\\scripts ， 下面的 Paths 就应该加入 . 这个路径。注意在 Windows 下， Base path 的路径分隔符必须采用 Windows 的格式（反斜杠 \\ ），而不应该使用 / 。\n3.设定源代码关键词\n前面我们已经确定了自己的关键词，那就是 _ 。当然我们也可以使用 C 语言的默认关键词 gettext ，但那样未免长了一点。\n4.设定完毕后，将项目保存为 i18n/zh_CN.po ，请注意我上面强调过的第2步。\n解析源码 一切设定成功之后，直接按下主界面上的 Update 按钮即可解析源码。Poedit 会将源码中解析到的使用 _(\u0026quot;my text\u0026quot;) 格式的关键词，将其中的字符串提取处理显示在主界面中。\n我们可以在主界面中对关键词进行翻译。\n如果源码有更新，再次单击 Update 按钮重新解析源码。这是如果解析到新的字符串，或者有字符串不再使用，Poedit都会进行提示。\n需要注意的一点是，在 Lua 的注释中不要包含半角的单引号。\n这是由于 Lua 的注释使用 -- 符号，而 C 语言使用 // ，所以 gettext 会认为 Lua 中的注释是代码的一部分而不会忽略解析。在一般情况没有什么问题，但如果在注释中包含了半角的单引号（这是在 C 语言中代表字面值），gettext 就会认为语法错误因此产生解析错误。\n生成 MO 文件 在软件设置中，我们设置了在保存项目的时候自动生成 MO 文件，所以这点不用操心了。生成的 MO 文件与 PO 文件在相同的目录。\n在 Lua 中解析 MO 文件 MO 是个二进制格式的文件，我们的程序在读取它的时候，需要分析 它的结构 ，将它解析成 原始文本 - 翻译文本 的键值对形式，供我们使用。\n许多语言中已经包含了 MO 文件的解析库，但是 Lua 没有。所以我们需要自己写一个。\n不过 J.J?rgen von Bargen 已经完成了这件事，我将他的代码进行了简单封装，将其放在我的 lua 库中了，名称是 utils.Gettext。\n由于要支持跨平台的原因，读取 MO 文件采用的是 CCFileUtils ，如果要在其他环境中使用，可以改用 io 库。我在 Gettext._getFileData 中保留了使用 io 库的代码，只是将其注释了。\n综合范例 使用这种方法，MO 文件会被解析成功一个 table 返回：\n1local mo_data=assert(require(\u0026#34;utils.Gettext\u0026#34;).loadMOFromFile(\u0026#34;res/zh_CN.mo\u0026#34;)) 2print(mo_data[\u0026#34;Hello World!\u0026#34;]) 3-- 你好，世界 4print(mo_data[\u0026#34;Foobar\u0026#34;]) 5-- nil 更通用的方法是这样：\n1_ = assert(require(\u0026#34;utils.Gettext\u0026#34;).gettextFromFile(\u0026#34;res/zh_CN.mo\u0026#34;)) 2print(_(\u0026#34;Hello World!\u0026#34;)) 在我的游戏中，则是这样使用的：\n1LANG = \u0026#34;res/zh_CN.mo\u0026#34; 2function _(__text) 3 return __text 4end 5if CCFileUtils:sharedFileUtils():isFileExist(LANG) then 6 _ = assert(require(\u0026#34;utils.Gettext\u0026#34;).gettextFromFile(LANG)) 7end ","date":"2013-12-12","description":"","lastmod":"2013-12-12T04:24:00Z","slug":"using_gettext_in_lua","tags":["cocos2d-x","linux","lua"],"title":"在Lua中使用gettext实现多语言支持","url":"https://blog.zengrong.net/post/using_gettext_in_lua/"},{"categories":["technology"],"content":"TureType/OpenType 字体瘦身、字体转换和字符替换\n本文讲解下面一些关于 TTF/OTF 字体的基本知识和操作：\n什么是TTF和OTF字体 删除/插入/替换字体中的字符 TTF/OTF 的互相转换 1. 什么是TTF和OTF字体 下面是英文全称，免费加送一个TTC\nTTF: True Type Font TTC: True Type Collections OTF: Open Type Font 嗯，懒惰的我觉得写了中文说明反而更加难懂（当然这是借口 :-)），这里引用一段微软的解释（ 原文在这里 ）：\nTrueType 字体可调整到任意大小，并且在所有大小情况下，都是清晰可读的。可以将它们发送给 Windows 支持的任何打印机或其他输出设备。OpenType 字体与 TrueType 字体相关，但包括更大的基本字符集扩展，包括小型大写、老样式数字及更复杂的形状，如“字形”和“连字”。OpenType 字体在任意大小下仍清晰可读，并且可以发送到 Windows 支持的任何打印机或其他输出设备。\n我们只需要知道，OTF 格式相对先进（目前），TTF 格式相对主流（也是目前）。\nTTC 则是一坨 OTF 或者 TTF 字体的集合体。例如 Windows7 下面的宋体（simsun.ttc） 就是个 TTC 文件。\n2. 删除字体中不需要的字符 这就要请出字体编辑工具了。FontCreator 无疑是Windows上最好的字体编辑工具；Mac OS 上是 Glyphs ；fontforge 是跨平台的开源工具。\n使用 FontCreator 删除不需要的字符 删除恐怕是最简单的事情了。在 FontCreator 7.5 上，已经将字符进行了分类。切换到相应的分类，选择不需要的字符，直接删除即可。\n如果是使用 FontCreator 6，由于软件还没有分类功能，所以可以先使用 Font 菜单提供的排序功能将字符进行排序。然后再删除。\n使用 FontCreator 插入字符 有时我们需要将A字体中的一些字符加入到B字体中，形成一个新的字体，例如 雅黑-Consolas混合字体 就是这种字体。步骤应该是这样的：\n同时打开A字体和B字体，选择A字体中需要加入的字符并复制； 在B字体的界面中选择 Insert-Glyphs 插入空的字形，数量与复制的A字体字符相同； 在B字体界面中选择这些空的字形，粘贴即可。 使用 FontCreator 替换字符 选择A字体中的字符复制； 在B字体中选择 Edit-Paste Special； 选中 Items 中的所有选项，Codepoints 选择 Overrule same codepoints ，单击 OK 完成替换。 3. TTF/OTF 相互转换 经常会有一些莫名其妙的需求，要把TTF和OTF这两种格式相互转换。当然，作为一个有责任感的无证程序员，一定要尽可能满足这些莫名其妙的需求。生命在于折腾嘛。\nfreefontconverter 这个网站提供在线转换功能。简单说就是你传个字体上去，然后选择转换类型，再下载一个已经转换过的字体下来。\nfontforge 软件则直接提供转换功能。步骤应该是这样的：\n打开要转换的字体； 选择 File-Generate Fonts 命令； 选择字体类型，例如 TureType； 保存即可； 如果保存的时候软件直接挂了，可以取消勾选 Validate Before Saving 选项然后重新保存。 ","date":"2013-12-04","description":"","lastmod":"2013-12-04T16:04:49Z","slug":"font_trim_replacement_converting","tags":["font","software"],"title":"TureType/OpenType 字体瘦身、字体转换和字符替换","url":"https://blog.zengrong.net/post/font_trim_replacement_converting/"},{"categories":["technology"],"content":"一个基于AS3的plist库\n本文并未全部完成，请耐心等待……\n为了在 Sprite Sheet Editor 中加入 plist 格式的 metadata 支持，我在 f60k的as3plist库 基础上进行了修改，实现了我的 as3plist 库。\nPlist格式的本质是XML文件。由于AS3内置XML支持，所以这个库的实现还是比较容易的。\nCocos2d-x 中大量使用了plist格式文件 ，因此实现plist的支持非常必要。目前我还没有找到软件能导入 plist+png 格式的 Sprite Sheet。大多数软件都只是能生成该格式。而 Sprite Sheet Editor 只需要稍加修改就能做到这一点。\n由于精力有限，项目中的文档并不齐全且可能有错，直接编译 sample 会比较靠谱。\n下面是范例代码：\n:::actionscript var __olist:Plist10 = new Plist10();\nvar __parr:PArray = new PArray(); var __pnum:PNumber = new PNumber(); __pnum.object = 3; var __pstring:PString = new PString(); __pstring.object = \u0026quot;hello\u0026quot;; var __pbool:PBoolean = new PBoolean(); __pbool.object = true; __parr.addValue(__pnum, __pbool, __pstring); __parr.addValue(new PArray().addValue(3, 4, 5, 7)); __parr.addValue(new PDict().addValue(\u0026quot;name\u0026quot;, new PNumber())); //trace(__parr.toXMLString()); __olist.root = __parr; //trace(__olist.toString()); var __alist:Plist10 = new Plist10(); __alist.parse(__olist.toString()); trace(__alist.toString()); 输出内容：\n:::xml \u0026lt;?xml version=\u0026quot;1.0\u0026quot; encoding=\u0026quot;UTF-8\u0026quot;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026quot;-//Apple Computer//DTD PLIST 1.0//EN\u0026quot; \u0026quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026quot;\u0026gt; \u0026lt;plist version=\u0026quot;1.0\u0026quot;\u0026gt; \u0026lt;array\u0026gt; \u0026lt;integer\u0026gt;3\u0026lt;/integer\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;string\u0026gt;hello\u0026lt;/string\u0026gt; \u0026lt;array\u0026gt; \u0026lt;integer\u0026gt;3\u0026lt;/integer\u0026gt; \u0026lt;integer\u0026gt;4\u0026lt;/integer\u0026gt; \u0026lt;integer\u0026gt;5\u0026lt;/integer\u0026gt; \u0026lt;integer\u0026gt;7\u0026lt;/integer\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;name\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;0\u0026lt;/integer\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;/plist\u0026gt; ZwoptexFormat2File 是 Plist10 的子类，它实现了 Zwoptex 软件第二版的文件风格。目前 Cocos2d-x 支持的plist Sprite Sheet格式就是这种格式。\n:::actionscript var __zwoptex2:ZwoptexFormat2File = new ZwoptexFormat2File(); var __frame:ZwoptexFormat2Frame = new ZwoptexFormat2Frame(); __frame.setOffset(0, 0).setRotated(false).setSourceColorRect(0, 0, 100, 100).setSourceSize(100, 100).setFrame(50, 50, 100, 100); __zwoptex2.setSize(100, 100).setRealTextureFileName(\u0026quot;abc.png\u0026quot;).setTextureFileName(\u0026quot;abc.png\u0026quot;).addFrame(\u0026quot;test01\u0026quot;, __frame); trace(__zwoptex2.toString()); 输出内容：\n:::xml \u0026lt;?xml version=\u0026quot;1.0\u0026quot; encoding=\u0026quot;UTF-8\u0026quot;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026quot;-//Apple Computer//DTD PLIST 1.0//EN\u0026quot; \u0026quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026quot;\u0026gt; \u0026lt;plist version=\u0026quot;1.0\u0026quot;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;frames\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;test01\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;offset\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{0,0}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;rotated\u0026lt;/key\u0026gt; \u0026lt;false/\u0026gt; \u0026lt;key\u0026gt;sourceColorRect\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{{0,0},{100,100}}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;sourceSize\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{100,100}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;frame\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{{50,50},{100,100}}\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;key\u0026gt;metadata\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;format\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;2\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;size\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{100,100}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;realTextureFileName\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;abc.png\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;textureFileName\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;abc.png\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; ","date":"2013-11-27","description":"","lastmod":"2013-11-27T15:52:37Z","slug":"as3plist","tags":["air","as3","xml"],"title":"【未完待续】一个基于AS3的plist库","url":"https://blog.zengrong.net/post/as3plist/"},{"categories":["technology"],"content":"cocos2d-x中的plist文件格式详解\n本文完成度90%，请耐心等待……\n1. 什么是plist文件格式？ 这是一种人类可读的串行化对象文件，由苹果公司发明，最早用于NeXTSTEP系统。详情看这里：Plist 。\ncocos2d-x 从 cocos2d-iphone 发展而来，因此在引擎中大量使用了这种文件格式。\n2. 如何编辑plist文件？ 在 OS X 系统上，XCode 就可以直接打开和编辑plist文件。而在Windows上，我还没有找到可用的plist编辑软件。\n当然，plist是基于XML的纯文本格式，随便找个文本编辑器就可以编辑了。\n3. cocos2d-x在哪些地方使用了plist格式？ 大致有这样几种：\n图像纹理定义文件\n将多个纹理拼在一张大图上，使用 CCSpriteFrameCache 可以载入这类plist文件；\n这里有一个图像纹理定义文件的范例： [cocos2d-x]\\samples\\Cpp\\TestCpp\\Resources\\animations\\grossini_family.plist 。 Label纹理定义文件 作用与图像纹理定义文件类似，只不过处理的是自己，面向 CCLabelAtlas； 这里有一个Label纹理定义文件的范例： [cocos2d-x]\\samples\\Cpp\\TestCpp\\Resources\\fonts\\tuffy_bold_italic-charmap.plist 。 帧动画定义 定义一个或多个动画中，使用哪些纹理，使用 CCAnimationCache 可以载入这类plist文件；\n这里有一个帧动画定义文件的范例： [cocos2d-x]\\samples\\Cpp\\TestCpp\\Resources\\animations\\animations.plist 。 4. 生成plist文件的工具 对于纹理定义文件来说，它的作用是如何在大图中找到碎图的坐标。因此很多拼合碎图的软件可以在拼合碎图的同时生成plist文件。\nTexturePacker 是所有平台上最好用的工具了； Zwoptex 是MAC Only的软件，我不太喜欢用； SpritePacker 是Windows Only的软件，功能尚可。 5. 图像纹理定义文件格式说明 cocos2d-x中的纹理定义格式，是以Zwoptex生成的格式为标准的。\nZwoptex生成的格式，有4种主要不同的版本：\nformat值为0，代表Flash版本； format值为1，Zwoptex 0.4b以前支持； format值为2，Zwoptex 1.0以后支持，与format1的区别在于支持旋转； format值为3，属性名称进行了大幅修改，Zwoptes1.0.2之后支持。 这3种格式的plist文件，cocos2d-x都能支持，具体的解析代码在 CCSpriteFrameCache::addSpriteFramesWithDictionary。\nTexturePacker生成的for cocos2d plist格式与Zwoptex生成的format为2的格式相同。\n5.1 format为0的plist文件 这里贴一个比较完整plist文件，为了方便描述，其中仅包含一个frame。\n:::xml \u0026lt;?xml version=\u0026quot;1.0\u0026quot; encoding=\u0026quot;UTF-8\u0026quot;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026quot;-//Apple//DTD PLIST 1.0//EN\u0026quot; \u0026quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026quot;\u0026gt; \u0026lt;plist version=\u0026quot;1.0\u0026quot;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;texture\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;width\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;256\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;height\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;128\u0026lt;/integer\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;key\u0026gt;frames\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;grossini.png\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;x\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;103\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;y\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;1\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;width\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;51\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;height\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;109\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;offsetX\u0026lt;/key\u0026gt; \u0026lt;real\u0026gt;0\u0026lt;/real\u0026gt; \u0026lt;key\u0026gt;offsetY\u0026lt;/key\u0026gt; \u0026lt;real\u0026gt;-1\u0026lt;/real\u0026gt; \u0026lt;key\u0026gt;originalWidth\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;85\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;originalHeight\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;121\u0026lt;/integer\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; 5.2 format为2的plist文件内容 :::xml \u0026lt;?xml version=\u0026quot;1.0\u0026quot; encoding=\u0026quot;UTF-8\u0026quot;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026quot;-//Apple Computer//DTD PLIST 1.0//EN\u0026quot; \u0026quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026quot;\u0026gt; \u0026lt;plist version=\u0026quot;1.0\u0026quot;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;frames\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;parts-dragon_leg_l.png\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;frame\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{{866,2},{152,254}}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;offset\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{0,0}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;rotated\u0026lt;/key\u0026gt; \u0026lt;false/\u0026gt; \u0026lt;key\u0026gt;sourceColorRect\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{{0,0},{152,254}}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;sourceSize\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{152,254}\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;key\u0026gt;metadata\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;format\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;2\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;realTextureFileName\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;dragon.png\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;size\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{1024,2048}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;smartupdate\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;$TexturePacker:SmartUpdate:26d1d28da42d49170ab3142654fea750:1/1$\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;textureFileName\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;dragon.png\u0026lt;/string\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; 5.3 format为3的plist文件内容 :::xml \u0026lt;?xml version=\u0026quot;1.0\u0026quot; encoding=\u0026quot;UTF-8\u0026quot;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026quot;-//Apple//DTD PLIST 1.0//EN\u0026quot; \u0026quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026quot;\u0026gt; \u0026lt;plist version=\u0026quot;1.0\u0026quot;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;frames\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;armyLevelBg.png\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;aliases\u0026lt;/key\u0026gt; \u0026lt;array/\u0026gt; \u0026lt;key\u0026gt;spriteColorRect\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{{0, 0}, {61, 36}}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;spriteOffset\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{0, -0}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;spriteSize\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{61, 36}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;spriteSourceSize\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{61, 36}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;spriteTrimmed\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;textureRect\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{{195, 2}, {36, 61}}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;textureRotated\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;key\u0026gt;metadata\u0026lt;/key\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;version\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;1.6.0\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;format\u0026lt;/key\u0026gt; \u0026lt;integer\u0026gt;3\u0026lt;/integer\u0026gt; \u0026lt;key\u0026gt;size\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;{256, 512}\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;name\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;army_zw.zwd\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;textureFileName\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;army_zw_cocos2d.png\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;premultipliedAlpha\u0026lt;/key\u0026gt; \u0026lt;false/\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; Frame:\nTop-Left originating rectangle of the sprite’s pixel texture coordinates. Cocos2′d will convert these to UV coordinates (0-1) when loading based on the texture size.\nOffset:\nZwoptex trim’s transparency off sprites. Because of this sprite’s need to be offset to ensure their texture is drawn in correct alignment to their original size.\nSource Color Rect:\nThis is the Top-Left originating rectangle that is the valid pixel data of the sprite. Say you have a 512×512 sprite that only has 10×10 pixels of data inside of it located at 500×500. The source color rect could be {500,500,10,10}.\nFormat:\nVersion number related to what version of Zwoptex was used so cocos2d knows how to parse the plist properly.\nFlash Version: 0\nDesktop Version 0-0.4b: 1\nDesktop Version 1.x: 2\nIn general if you’re not trimming images you can set offset to be 0,0 and sourceColorRect to 0,0,frame.size.width,frame.size.height.\nhttp://www.cocos2d-iphone.org/forums/topic/zwoptex-and-their-plist-explanation/\n5. Label纹理定义文件 对这种格式的具体的解析代码在 CCLabelAtlas::initWithString 。\n6. 帧动画定义文件格式说明 详见 Cocos2d-x 中的帧动画\n","date":"2013-11-27","description":"","lastmod":"2013-11-27T15:13:36Z","slug":"explain-cocos2dx-plist-file-format","tags":["cocos2d-x","plist","xml"],"title":"【未完待续】cocos2d-x中的plist文件格式详解","url":"https://blog.zengrong.net/post/explain-cocos2dx-plist-file-format/"},{"categories":["technology"],"content":"一个LuaSocket封装\n2014-01-10更新： SocketTCP 已经进入 quick-cocos2d-x 的 framework.\n在 [quick-cocos2d-x 中的 socket 技术选择：LuaSocket 和 WebSocket][1] 一文中，我提到了选择 [LuaSocket][2] 与服务器通信。 为了方便使用，我对LuaSocket进行了封装。封装主要做了这样几件事：\n封装基于 LuaSocket 的 TCP 模式，使用 settimeout 实现异步调用； 利用 cocos2d-x 提供的定时器实现失败重连； 利用 quick-cocos2d-x 提供的 framework.api.EventProtocol 实现了事件的注册和发布。 这个封装完全依赖 quick-cocos2d-x ，因此不能单独在lua环境中使用。\n这个封装修改自 quick 论坛的一个源码 ，感谢原作者！\n下面的例子依赖 ByteArray，详情看这里：用lua实现ByteArray和ByteArrayVarint 。\nsocket = SocketTCP.new(\u0026quot;192.168.18.22\u0026quot;, 12001, false) socket:addEventListener(SocketTCP.EVENT_CONNECTED, onStatus) socket:addEventListener(SocketTCP.EVENT_CLOSE, onStatus) socket:addEventListener(SocketTCP.EVENT_CLOSED, onStatus) socket:addEventListener(SocketTCP.EVENT_CONNECT_FAILURE, onStatus) socket:addEventListener(SocketTCP.EVENT_DATA, onData)\nsocket:send(ByteArray.new():writeByte(0x59):getPack()) function onStatus(__event) echoInfo(\u0026quot;socket status: %s\u0026quot;, __event.name) end function onData(__event) echoInfo(\u0026quot;socket status: %s, data:%s\u0026quot;, __event.name, ByteArray.toString(__event.data)) end SocketTCP 位于我的 lua库 项目中。\n在实际使用的过程中，要处理数据粘包、协议解析、数据还原、数据压缩之类的具体情况，因此我写了一个 sample 来说明如何使用 ByteArray 和 SocketTCP 来做这些事。具体请参考：基于QUICK-COCOS2D-X的LUASOCKET范例 。\n这个范例已经被我提交到了 quick-cocos2d-x sample 。\n我会把自己在游戏中常用的 lua 功能陆续整合进入这个开源库，就像我的 as3库 和 java库 一样。\n","date":"2013-11-26","description":"","lastmod":"2013-11-26T15:03:39Z","slug":"lua_sockettcp","tags":["cocos2d-x","lua","socket","netconnection"],"title":"一个LuaSocket封装","url":"https://blog.zengrong.net/post/lua_sockettcp/"},{"categories":["use"],"content":"相信许多人和我一样，在打开电脑工作的时候，经常被其他因素干扰导致工作效率降低。而 RescueTime 就是避免这种情况的一个优秀工具。\n我已经使用 RescueTime 4天时间了，我确定它对我有用。下面是几点分析：\n作用 RescueTime 能记录你的电脑和移动设备上的所有操作。它知道你访问了什么网站，打开了什么程序，以及在这个程序（或网站）上花费了多少时间。根据这些数据， RescueTime 就能知道你是否在认真干活，以及什么时候在开小差。\n比如这是我今天的成绩：\n看得出来我还是很敬业的。\n数据 RescueTime 将工作状态分为 干劲十足/努力/不知在干啥/开小差/撂挑子 五种（请原谅我的渣翻译吧，我知道你并不在意这个）,它应该有个庞大的数据库来分辨哪些程序（网站）是高生产力的，哪些则代表你在分心。\n例如，对于一个程序员来说，上班时间聊 QQ 的生产力就不高。即使是用于工作交流，也一定会影响开发工作。\n但是上面这个定义对于运营人员来说就不不适用了，人家可指着这个吃饭哪…… RescueTime 提供了自定义方法：\n下面是我今天名列前茅的几个活动，有PC上的，也有手机上的：\n隐私 对于一个安装在自己电脑（而且还需要管理员权限）和手机上的程序来说，有什么是它不能做到的呢？上传艳照？窃取银行卡密码？分析cookies？记录键盘？\n当然它都能做到，不过同样的事情，金山词霸/百度输入法/QQ 也能做到。\n对于我来说，不装360就很安全了。再不济我装个 Wireshark 分析下数据包总行了吧？\n成人内容 要是突然（或者预谋）想观摩苍老师的作品的该怎么办？或者去Cao榴逛逛不希望被记录下来？有这样几个办法：\nRescueTime 的隐私选项中，已经默认忽视了成人站点（当然只是它们的数据库中的成人站点）； 在隐私选项中设置监控时间为白天（难道你喜欢在格子间学习苍老师，不怕隔壁大叔对你有兴趣？）； 在 RescueTime 监控程序中暂停监控； 直接退出 RescueTime 监控进程。Windows上的这个进程就在托盘图标中，直接退出即可。 注册 还等什么？ 赶快拿起电话订购，只要999元 免费的哦！赶快猛击 RescueTime 注册吧！\n全文完 ","date":"2013-11-21","description":"","lastmod":"2013-11-21T16:00:28Z","slug":"rescue_time","tags":["gtd"],"title":"挽救时间 RescueTime 中文介绍","url":"https://blog.zengrong.net/post/rescue_time/"},{"categories":["technology"],"content":"用lua实现ByteArray和ByteArrayVarint\n2014-01-10更新： ByteArray 和 ByteArrayVarint 已经进入 quick-cocos2d-x 的 framework.\n许多 Actionscript 程序员已经从 Flash 转到 Cocos2d-x 了。那么以前的那些超级好用的类库都不见了，只好重新来过。\n我利用 Lua 和 lpack 库实现了一套 lua版本的 ByteArray 。这套库用于 quick-cocos2d-x(后称quick) 中。因此依赖一些 quick 中已经包含的c库和lua库 。\n主要依赖如下：\nlpack (已经包含在quick中) BitOp (已经包含在quick中) quick framework utils.ByteArray ByteArray依赖 lpack 。\nlpack 其实已经足够好用了。但是由于lpack的指针功能有限，所以使用起来还是有些麻烦。这个类模仿了 Actionscript 的 flash.utils.ByteArray ，指针在内部实现，方便读取和缓存 socket 服务器数据。\n当然了，有一点点不同，但或许更好用。\n下面是使用ByteArray的例子：\n1-- 直接使用 lpack 库生成一个字节流 2local __pack = string.pack(\u0026#34;\u0026lt;bihP2\u0026#34;, 0x59, 11, 1101, \u0026#34;\u0026#34;, \u0026#34;中文\u0026#34;) 3 4-- 创建一个ByteArray 5local __ba = ByteArray.new() 6 7-- ByteArray 允许直接写入 lpack 生成的字节流 8__ba:writeBuf(__pack) 9 10-- 不要忘了，lua数组是1基的。而且函数名称比 position 短 11__ba:setPos(1) 12 13-- 这个用法和AS3相同了，只是有些函数名称被我改掉了 14print(\u0026#34;ba.len:\u0026#34;, __ba:getLen()) 15print(\u0026#34;ba.readByte:\u0026#34;, __ba:readByte()) 16print(\u0026#34;ba.readInt:\u0026#34;, __ba:readInt()) 17print(\u0026#34;ba.readShort:\u0026#34;, __ba:readShort()) 18print(\u0026#34;ba.readString:\u0026#34;, __ba:readStringUShort()) 19print(\u0026#34;ba.available:\u0026#34;, __ba:getAvailable()) 20-- 自带的toString方法可以以10进制、16进制、8进制打印 21print(\u0026#34;ba.toString(16):\u0026#34;, __ba:toString(16)) 22 23-- 创建一个新的ByteArray 24local __ba2 = ByteArray.new() 25 26-- 和AS3的用法相同，还支持链式调用 27__ba2:writeByte(0x59) 28\t:writeInt(11) 29\t:writeShort(1101) 30-- 写入空字符串 31__ba2:writeStringUShort(\u0026#34;\u0026#34;) 32-- 写入中文（UTF8）字符串 33__ba2:writeStringUShort(\u0026#34;中文\u0026#34;) 34 35-- 十进制输出 36print(\u0026#34;ba2.toString(10):\u0026#34;, __ba2:toString(10)) 下面就是效果了：\nutils.ByteArrayVarint ByteArrayVarint 继承 ByteArray，同时依赖 BitOp。\nByteArrayVarint 实现了 google protocol buffer协议中的Varint编码 。\nProtocol Buffer协议是何方神圣，google一下就知道。简单说就是能大幅降低 socket 协议中传递的字节流的长度。但是protocol buffer协议还包含许多东西，而我只需要 varint 编码而已。\n我们的上一个游戏就在 AS3中实现了varint编码 。现在又用lua实现了一遍。\n方法名称 说明 ByteArrayVarint.readUVInt() read a unsigned varint int ByteArrayVarint.writeUVInt() write a unsigned varint int ByteArrayVarint.readVInt() read varint int ByteArrayVarint.writeVInt() write varint int ByteArrayVarint.readStringUVInt() read a string preceding a unsigned varint int ByteArrayVarint.writeStringUVInt() write a string preceding a unsigned varint int 虽然lua支持64位数字，但由于 BitOp 仅支持32位有符号整数，这个 ByteArrayVarint 类能做的事情有限，甚至比AS3的都要差点（AS3好歹还支持32位无符号整数）。但如果程序中不使用变态的64位（或更高）数字的话，还是挺好的。\n我会把自己在游戏中常用的 lua 功能陆续整合进入这个开源库，就像我的 as3库 和 java库 一样。\n","date":"2013-11-14","description":"","lastmod":"2013-11-14T16:10:05Z","slug":"delete_some_methods_about_long_in_lua_bytearray","tags":["cocos2d-x","lua"],"title":"用lua实现ByteArray和ByteArrayVarint","url":"https://blog.zengrong.net/post/delete_some_methods_about_long_in_lua_bytearray/"},{"categories":["technology"],"content":"quick-cocos2d-x 中的 socket 技术选择：LuaSocket 和 WebSocket\n2013-11-17更新：加入SocketTCP和ByteArray类的实现链接。 2014-11-05更新：增加范例链接。 2014-12-04更新：一篇更详细的文章： Socket 与 WebSocket 。 在 quick-cocos2d-x 中，默认集成了 LuaSocket 和 WebSocket 两个 Socket 库。那么，在开发需要长连接的手机游戏时，应该选择哪个库呢？下面从几个方面进行比较：\n跨平台； 易用性； 性能； 流量； 灵活性； 二进制编码； 服务器实现。 一、跨平台 WebSocket 是跨平台的，其导出到lua的代码位于 [quick]/lib/cocos2d-x/scripting/lua/cocos2dx_support/Lua_web_socket.cpp 。\nLuaSocket 也是跨平台的，其导出到lua的代码位于 [quick]/lib/cocos2d-x/scripting/lua/lua_extensions/lua_extensions.c 。quick 中集成的 LuaSocket 是 2.1RC 版本。\n二、易用性 [quick]/samples/websockets 是quick提供的一个WebSocket范例。 [quick]/samples/cocos2dx_luatest/scripts/ExtensionTest/WebProxyTest.lua 也是一个范例。WebSocket 库封装了一些基本的事件支持 open/message/close/error ，在使用的时候比较方便。\nWebSocket 天生就是非阻塞的。\n在 quick 中，并没有提供 LuaSocket 的范例。 我为 luasocket 写了一个 sample ，已经提交给了 quick 。\nLuaSocket 项目本身页提供了 不少范例 和 库 。从 ftp 到 socket 服务器实现，应有尽有了。\nLuaSocket 并没有封装事件支持。不过我们完全可以自己来封装。LuaSocket 支持阻塞和非阻塞的方式获取数据。\n我在 一个LuaSocket封装 中，基于 quick 框架提供了事件支持。\n三、性能 我并没有做过具体的对比测试，所以无从回答具体性能。但从协议实现上来说，LuaSocket 应该会高些的。WebSocket 因为要实现 HTTP 握手 和 数据帧，性能或许会低那么一点点。\n但从真实应用上来说，这个性能应该是可以忽略不计了。\n四、流量 WebSocket 在握手阶段必须使用 HTTP协议 ，此时的流量消耗会比 LuaSocket 略高。但连接建立之后，就与标准的 TCP 协议相同了。\nLuaSocket 就不说了，标准的 TCP 协议实现，还支持 UDP/FTP/HTTP/DNS/SMTP。\n五、灵活性 这里是 WebSocket API 和 LuaSocket API 。毫无疑问，LuaSocket当然更灵活。\n灵活和易用似乎总是一堆矛盾。为了更方便地使用 LuaSocket ，我们少不了要自己做一些封装。我参考 quick论坛上非阻塞socket的实现 做了一些修改， 在 一个LuaSocket封装 这篇文章中做了详细介绍。\n六、二进制编码 WebSocket 支持可选的二进制数据传输。LuaSocket 的 send 方法虽然只支持 string ，但其实我们完全可以用 string.char() 把需要发送的数据转成二进制编码来传送，效果其实是一致的。\nquick 中封装了 lpack ，能够更方便的把 lua 中的值转换成二进制数据。而 quick 自带的 luajit 还带有 BitOp 库，支持常用的二进制操作。这些都能直接使用，既能用于 WebSocket， 也能用于 LuaSocket。\n例如下面这段混用了 lpack 和 BitOp 库的代码：\n:::lua require(\u0026quot;bit\u0026quot;) require(\u0026quot;pack\u0026quot;) local __pack = string.pack(\u0026quot;\u0026lt;b3ihb5\u0026quot;, 0x59, 0x7a, 0, 11, 1101, 0, 3, bit.bor(0,0), bit.bor(bit.lshift(1,3), 0), bit.bor(bit.lshift(2,3), 0)) local __s = string.gsub(s,\u0026quot;(.)\u0026quot;,function (x) return string.format(\u0026quot;%02d \u0026quot;,string.byte(x)) end) print(__s) 如果不使用C模块，这里也有几个完全使用lua实现的位运算库。速度会比C慢：1 2 3\n我基于 lpack 封装了一个 ByteArray 类，用来模仿 Actionscript 中 flash.utils.ByteArray 的行为。详情可以看这里： 用lua实现ByteArray 。\n在我的 luasocket sample 中，我还提供了 PacketBuffer 来负责数据解析以及粘包处理，可以参考。\n七、服务器实现 服务端的选择就更广泛了。C/C++/JAVA/Go/Node.js/Python 等主流语言都有 WebSocket 的开源实现。标准的 TCP Socket 就更不用说了，那个是网络基础好吧。\n但是，WebSocket 和 标准Socket 服务器的实现，还是有区别的。主要的问题在于WebSocket的 握手 和 数据帧 方式与标准Socket不同。\n由于我们的服务端已经采用标准Socket实现，再转向 WebSocket 就有点多此一举。所以我这个客户端就苦B一点把，把 LuaSocket 封装一下直接用了。\n","date":"2013-11-11","description":"","lastmod":"2014-11-05T02:05:33Z","slug":"select_socket_library_in_quick-cocos2d-x","tags":["cpp","cocos2d-x","lua","socket","websocket","netconnection","choice"],"title":"quick-cocos2d-x 中的 socket 技术选择：LuaSocket 和 WebSocket","url":"https://blog.zengrong.net/post/select_socket_library_in_quick-cocos2d-x/"},{"categories":["technology"],"content":"在 quick-cocos2d-x 中导出 CCFileUtils::getFileData 给Lua使用\n2013-11-18更新:廖大在 468be4 这次提交中解决了本文提到的问题并进行了 getFileData 和 getFileDataFromZip 两个方法的导出。如果你使用的是develop分支，只需要pull即可，下面的修改不必做了。当然，可以继续将本文当作导出教程。\n本文讲解如何将 cocos2d-x 中的 CCFileUtils::getFileData 方法导出给Lua使用。提纲如下：\nquick-cocos2d-x 读取外部文件的问题 简单地使用 tolua++ 导出 获取到的字符串问题 修改 cocos2d-x 源文件解决字符串问题 重新编译和测试 本文基于 quick-cocos2d-x devel 分支 e55be13b8d6275c3eee3e86651f42857d5f1576f 版本（2013-10-22）\n一、 quick-cocos2d-x 读取外部文件的问题 在将 cocos2d-x 制作的一个 Demo 移植到 quick-cocos2d-x 时，我碰到了读取外部文件的问题。\n这个 Demo 使用一个 JSON 文件作为数据文件，在 cocos2d-x 中，我使用 CCFileUtils::getFileData 来读取这个 JSON 文件。\n查看了一下 [quick-cocos2d-x]/lib/luabinding/cocos2dx/platform/CCFileUtils.tolua 发现其中并没有导出 getFileData 方法。\n可以使用 Lua 的 io 库来读取，例如这样：\n1io.input(\u0026#34;res/fightdata.json\u0026#34;) 2local __jsonTxt = io.read(\u0026#34;*all\u0026#34;) 3print(__jsonTxt) 4local __json = json.decode(__jsonTxt) 5print(__json.actions) 但这样一来，就无法跨平台了，例如在 Android 真机上，是读取不到 fightdata.json 的。\n其中原因 廖大做了说明 ：\n假定有一个 res/game.json 文件 build_native.sh 执行时会将 res 目录中的所有内容复制到 proj.android/assets/res 目录中 编译结束，用 Eclipse 打包 apk 时，打包工具会将整个游戏打包为一个 apk 文件 将 apk 安装到设备上后，apk 文件的内容并不会解压缩（apk 实际是 ZIP 压缩文件格式）\n由于 apk 文件在设备上并不会解压缩，所以其中包含的文件就无法直接通过文件系统读取（因为文件都内嵌在 apk 里，而没有实际保存在文件系统中）。\n但是 Android 也提供了一种间接的途径来访问文件：\n假定原始目录是 res/game.json 那么在 Android 上应该以 assets/res/game.json 的路径读取\n既然 quick 没有导出，那么我就来导出试试。不过为什么 quick 没有导出这个方法呢？\n二、 简单地使用 tolua++ 导出 修改 [quick-cocos2d-x]/lib/luabinding/cocos2dx/platform/CCFileUtils.tolua 文件，在其中加入下面这行：\n1unsigned char* getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize); 然后运行 [quick-cocos2d-x]/lib/luabinding/build.bat ，它会生成 [quick-cocos2d-x]/lib/cocos2d-x/scripting/lua/cocos2dx_support/LuaCocos2d.cpp 这个 60,000+ 行的文件，所有的 Lua 绑定均在其中。\n重新编译 quick-cocos2d-x 中的 player 项目。\n三、 获取到的字符串问题 在 player 中运行下面的 Lua 代码：\n1local __size = 0 2local __jsonTxt = fileUtil:getFileData(\u0026#34;fightdata.json\u0026#34;, \u0026#34;r\u0026#34;, __size) 3print(__jsonTxt) 发现输出的 JSON 文本最后会多出一些字节，这样的 JSON 当然不能解析成功：\n换了一个 XML 文件载入，也一样会多出字节。\n我猜想这应该是 C++ 与 Lua 通信时对字符串末尾结束字节计算不正确所致。\n四、 修改 cocos2d-x 源文件解决字符串问题 为了解决这个问题，需要修改 cocos2d-x 源码。涉及的文件有下面两个：\n[quick-cocos2d-x]/lib/cocos2d-x/cocos2dx/platform/CCFileUtils.h [quick-cocos2d-x]/lib/cocos2d-x/cocos2dx/platform/CCFileUtils.cpp 在头文件中找到 getFileData 的声明，在其上方增加一个重载函数的声明：\n1// ......... many codes 2/** 3 * Get resource file data(for lua export) 4 * zrong 2013-10-31 5 */ 6unsigned char* getFileData(const char* pszFileName); 7 8virtual unsigned char* getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize); 9// ......... many codes 在cpp文件中找到 getFileData 的定义，在其上方定义这个重载函数：\n1// ......... many codes 2// for lua export 3// zrong 2013-10-31 4unsigned char* CCFileUtils::getFileData(const char* pszFileName) 5{ 6\tunsigned long __size = 0; 7\tunsigned char* __pFileContent = getFileData(pszFileName, \u0026#34;r\u0026#34;, \u0026amp;__size); 8\tif (0 == __size) 9\t{ 10\tCCLuaLog(\u0026#34;CCFileUtils::getFileData: file length is 0, return null\u0026#34;); 11\treturn NULL; 12\t} 13\tif (__pFileContent[__size] != \u0026#39;\\0\u0026#39;) 14\t__pFileContent[__size] = \u0026#39;\\0\u0026#39;; //let the texts have correct size 15\treturn __pFileContent; 16} 17 18unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize) 19{ 20} 21// ......... many codes 我猜想 quick 没有导出 getFileData 方法，应该是因为不愿意修改源文件。因为这样一来，就对 cocos2d-x 底层进行了修改，影响了 quick 的设计初衷。\n五、 重新编译和测试 再次修改 CCFileUtils.tolua 文件，改变 getFileData 的签名：\n1unsigned char* getFileData(const char* pszFileName); 运行 tolua++ ，重新编译 player。\n在 player 中运行下面的 Lua 代码：\n1local __size = 0 2local __jsonTxt = fileUtil:getFileData(\u0026#34;fightdata.json\u0026#34;, \u0026#34;r\u0026#34;, __size) 3print(__jsonTxt) 搞定。\n","date":"2013-10-31","description":"","lastmod":"2013-10-31T10:24:28Z","slug":"export_ccfileutils_getfiledata_to_lua","tags":["cpp","cocos2d-x","lua"],"title":"在 quick-cocos2d-x 中导出 CCFileUtils::getFileData 给Lua使用","url":"https://blog.zengrong.net/post/export_ccfileutils_getfiledata_to_lua/"},{"categories":["technology"],"content":"使用 ZeroBrane Studio 和 Eclipse LDT 断点调试 quick-cocos2d-x\n2013-12-13 更新： 加入quick官网提供的调试方式链接\nquick-cocos2d-x 是个基于 cocos2d-x 的 Lua Binding 加强版。本文介绍在quick-cocos2d-x中进行断点调试的方法。\n为了便于阅读和减少废话，本文有如下假设：\n读者阅读过 quick-x-player 使用说明 和 初窥 Quick-cocos2d-x ; 读者了解 quick-cocos2d-x 项目的文件夹结构； 读者安装了 ZeroBrane Studio 0.39 或/和 Eclipse LDT 1.0； 本文基于 quick-cocos2d-x 提供的 coinflip sample 进行调试。 提纲如下：\nQuick官方提供的调试方式 在 ZeroBrane Studio 中进行断点调试 在 Eclipse LDT 中进行断点调试 一、 Quick官方提供的调试方式 廖大已经在 Quick X Player 中加入了LDT调试支持，也就是说，LDT的 debugger.lua 模块已经被廖大加入到了 Quick X Player 中，不需要像本文下面写的那样 require(\u0026quot;debugger\u0026quot;) 了。\n取而代之，是需要打开调试的项目后，选择菜单 Player -\u0026gt; Auto Connect Debugger。\n详情请参阅quick官网： 用 Eclipse LDT 调试 quick-cocos2d-x 游戏\n二、 在ZeroBrane Studio中进行断点调试 2013-12-13 更新：由于quick作者修改了调试方式，可能此处介绍的方法不再有效，请参阅 一、 Quick官方提供的调试方式\nZeroBrane Studio是一个用Lua写成的跨平台Lua IDE。界面使用 wxLua 实现。\n1. 调试模块 ZeroBrane Studio 使用 modbdebug 模块（位于 [ZeroBrane]/lualibs/mobdebug/mobdebug.lua） 实现调试支持。为了让项目找到这个模块，我采用最简单的方法，将该模块复制进入 coinflip 的 scripts 文件夹。\n若不希望这样粗暴，可采用另外两种方法，参考： Remote debugging\n2. require mobdebug 在 coinflip/scripts/main.lua 的第一行加入下面的代码，让项目启动调试支持。\n1require(\u0026#34;mobdebug\u0026#34;).start() 3. 启动调试服务器 在 ZeroBrane Studio 中选择 Project-\u0026gt;Start Debugger Server 命令。如果该命令是灰色的，说明调试服务器已经启动了。\n4. 加断点 编辑 game.lua 文件，在32行 game.enterChooseLevelScene() 处选择 Project -\u0026gt; Toggle BreakPoint 加入断点。\n5. 启动 quick-player 在 quick-player 中启动 coinflip 项目，ZeroBrane Studio 会自动停在 main.lua 中。按 Project -\u0026gt; Continue 继续运行，游戏界面出现。\n单击游戏中的 \u0026quot;Start\u0026quot; 按钮，调试停止在 game.lua 中的断点处。如下图所示：\n查看大图\n6. 进入源码调试 若要进入框架内部调试，可以取消 main.lua 中的 CCLuaLoadChunksFromZip(\u0026quot;res/framework_precompiled.zip\u0026quot;) 调用，然后将 [quick-cocos2d-x]/framework 复制的 coinflip/scripts/ 文件夹，这样在调试的时候，就可以进入框架内部了。如下图所示：\n查看大图\n三、 在Eclipse LDT 中进行断点调试 2013-12-13 更新：由于quick作者修改了调试方式，可能此处介绍的方法不再有效，请参阅 一、 Quick官方提供的调试方式\nLDT(Lua Development Tools)是一个 Eclipse 插件，支持Lua语言的编写和调试。\n1. 调试模块 LDT 使用 另一个调试模块来实现调试支持。LDT可以自动生成这个模块。\n单击 Run -\u0026gt; Debug Configurations 菜单，新建一个 Lua Attach to Application 配置，点击其中的 Lua Debugger Client 将模块输出到 coinclip 的 scripts 文件夹，默认文件名为 debugger.lua 。\n查看大图\n2. require debugger 在 coinflip/scripts/main.lua 的第一行加入下面的代码，让项目启动调试支持。\n1require(\u0026#34;debugger\u0026#34;)(\u0026#34;127.0.0.1\u0026#34;, 10000, \u0026#34;luaidekey\u0026#34;) 这里的 luaidekey 是调试过程中IDE用来保持会话的键名，与上面 Debug Configurations 配置界面中的 IDE Key 相同。\n3. 启动调试服务器 单击 Run -\u0026gt; Debug Configurations 菜单，选择刚才新建的配置，单击 Debug 按钮。\n4. 加断点 编辑 game.lua 文件，在32行 game.enterChooseLevelScene() 处加入断点。\n5. 启动 quick-player 在 quick-player 中启动 coinflip 项目，单击游戏中的 \u0026quot;Start\u0026quot; 按钮，调试停止在 game.lua 中的断点处。如下图所示：\n查看大图\n若要进入 framework 源码内部调试，见上方的 6. 进入源码内部调试 。\n关于LDT更详细的调试信息，可以阅读 Debugging a Lua program 。\n","date":"2013-10-29","description":"","lastmod":"2013-10-29T07:10:01Z","slug":"breakpoint-debugging-in-quick-cocos2d-x","tags":["cocos2d-x","eclipse","lua"],"title":"使用 ZeroBrane Studio 和 Eclipse LDT 断点调试 quick-cocos2d-x","url":"https://blog.zengrong.net/post/breakpoint-debugging-in-quick-cocos2d-x/"},{"categories":["technology"],"content":"获取CCArmature动画的播放状态\n本文介绍如何在 CCArmature 播放的过程中获取动画的播放状态。例如，如何判断 “死亡” 动作已经播放完毕了？如果知道一个 “循环” 动作已经播放了一次？，提纲如下：\n先决条件； 必要知识； MovementEventSignal 的用法。 本文基于 cocos2d-x 2.1.5\n一、 先决条件 代码基于 cocos2d-x 2.1.5； 本文假设你已经会使用CCArmature，若不会，请看这里： 在cocos2d-x中使用DragonBones实现骨骼动画 。 二、 必要知识 CCArmature是cocos2d-x中包含的一个扩展功能，用来实现骨骼动画。它是从DragonBones 2.2移植而来。\n要使用CCArmature来播放骨骼动画，可以查看这里：在cocos2d-x中使用DragonBones实现骨骼动画 。\nDragonBones使用Actionscript 3写成。在 DragonBones的文档 中，我们可以看到Armature提供了一组事件 boneFrameEvent/complete/loopComplete/movementChange/movementFrameEvent/start 来通知动画状态的改变。\n那么，CCArmature这套C++移植版是怎么实现这些事件的？答案是： sigslot\nsigslot 是一套 C++ 实现的调用机制，在 Qt 和 Boost 中都有应用。Actionscript 3中也有它的一个实现，我曾经介绍过： Signals框架介绍 。\nsigslot 是一个合成词，由 Signals+Slot合成。翻译成中文就是 “信号槽”。信号（Signals）负责发布事件，槽（Slot）负责接收发布的事件。在使用时，通过信号提供的connect方法连接一个信号和一个/多个槽。\nCCArmatureAnimation 中定义了 MovementEventSignal 和 FrameEventSignal 这两个信号（Signals）来发布状态改变事件，但没有连接槽（Slot）进行处理。从源码来看，FrameEventSignal 只是做了声明，既没有实现，也没有使用。\n三、 MovementEventSignal 的用法 1// in ArmaturePlayer.h 2#include \u0026#34;CCArmature/external_tool/sigslot.h\u0026#34; 3//... other CCArmature package #include 4class ArmaturePlayer : public cocos2d::CCLayer , public sigslot::has_slots\u0026lt;\u0026gt; 5{ 6\t//... your implemention 7\tvoid handler_movementEvent(cocos2d::extension::CCArmature *, cocos2d::extension::MovementEventType, const char *); 8} 9 10// in ArmaturePlayer.cpp 11 12_pArmature = CCAramture::create(\u0026#34;hero\u0026#34;); 13_pArmature-\u0026gt;getAnimation()-\u0026gt;MovementEventSignal.connect(this, \u0026amp;ArmaturePlayer::handler_movementEvent); 14 15void ArmaturePlayer::handler_movementEvent(CCArmature* $pArmature, MovementEventType $evtType, const char* $movId) 16{ 17\tCCLOG(\u0026#34;movementEvent, evtType:%d, moveID: %s\u0026#34;, $evtType, $movId); 18\tif($evtType == MovementEventType::START) 19\tCCLOG(\u0026#34;the movement %s is starting!\u0026#34;, %movId); 20\telse if($evtType == MovementEventType::COMPLETE) 21\tCCLOG(\u0026#34;the movement %s has completed!\u0026#34;, %movId); 22\telse($evtType == MovementEventType::LOOP_COMPLETE) 23\tCCLOG(\u0026#34;the movement %s has loop completed!\u0026#34;, %movId); 24} 本文假设读者已经会使用CCArmature，因此省略了CCArmature的包含部分，但对 sigslog.h 头文件的包含进行了强调； 使用connect方法连接信号（Signals）和槽（Slot），这个方法接受两个指针：槽对象的指针，和槽处理器函数的指针； 实现槽处理器的类，必须继承 sigslot::has_slots\u0026lt;\u0026gt; 这个模版； 槽处理器函数不能有返回值，即必须返回void； 槽处理函数的三个参数分别为：发出信号的CCArmature对象指针、动画事件类型、动画id（动作的名称字符串）。 要了解 sigslot 的具体用法，这里有一篇详细的文章：Sigslot介绍 。\n","date":"2013-10-23","description":"","lastmod":"2013-10-23T13:55:24Z","slug":"get_ccarmature_animation_status","tags":["cpp","cocos2d-x","dragonbones","signals","skeletalanimation"],"title":"获取CCArmature动画的播放状态","url":"https://blog.zengrong.net/post/get_ccarmature_animation_status/"},{"categories":["technology"],"content":"首先声明，这个尝试没有成功。我依然还是必须使用Visual Studio。\nCocos2d-x 中的项目，在Windows下默认使用Visual Studio作为开发工具。\n但我并不喜欢Visual Studio，经过了一个月的折磨，用Visual Studio Express 2012做了两个小项目之后，我实在是无法习惯它，心里一直希望能用Eclipse+CDT来代替它。\n花时间google了一下，发现此类文章几乎没有。中文社区应该是都认为VS很好用，极少有我这样的变态非要用Eclipse。搜到的文章大多数都是讲解如何基于Eclipse+CDT+cygwin/NDK来配置Android编译环境，而这类配置我已经写过一篇文章搞定了：\nCocos2d-x Windows 7配置备忘 。\n我觉得唯一有点价值的是这一篇： Now compiles with the mingw compiler ，作者修改的代码应该已经合并进入cocos2d-x分支，但他并没有提供他所说的cmake代码，这导致我必须再去研究cmake。时间所限，我没有在这篇文章上纠结太久。\n我是个惯于折腾的人，曾经放着Flash Builder不用，去折腾 Vim调用Ant编译swf并自动调试 ，也花了一些时间 修改cocos2d-x项目创建器，支持自定义项目路径 。虽然知道这次折腾肯定会浪费不少时间，但我还是义无反顾地开始了。\n注意：\n下面的折腾，假设Eclipse+CDT、cygwin、MinGW/MSYS均已经安装好。 为了统一界面版本和偷懒，所有的步骤不会有截图，全部采用文字描述。 如果希望看step to step，推荐这篇：How to setup Eclipse CDT environment on Windows 一、 选择编译器 既然不愿意用VS，那么当然也不愿意选择VS的编译器。\n我平时工作的时候，是完全离不开cygwin的，使用git、用bash写脚本，全部都在cygwin下进行。因此我自然而然选择了cygwin作为编译环境。\n选择cygwin环境的另一个好处是可以实现交叉编译——在Windows系统上编译Linux程序。编译出来的程序依赖cygwin1.dll，而不依赖ms的运行库。\n同时，也可以利用cygwin调用mingw环境来编译不依赖cygwin1.dll的windows应用程序。\n关于cygwin和MinGW，这里有篇文章做知识普及： Cygwin与MinGW/MSYS，如何选择？\n二、cygwin环境配置 网上的教程大多数都是要求把cygwin/bin目录加入到系统的PATH变量。我认为这是没有必要的。\n加入PATH变量的确方便，但也为后面切换编译器带来了麻烦。\n假设我们需要在一台电脑上同时使用cygwin和MinGW环境，那么PATH中的路径就会发生冲突。\n正确的方法，应该是直接在C++项目中设置项目的PATH变量。这样不会对系统PATH造成任何影响，也方便切换编译环境（例如从cygwin切换到MinGW）。\n三、在cygwin环境下编译chipmunk chipmunk是cocos2d-x中包含的一个物理引擎，位于 [cocos2d-x]\\external\\chipmunk\\ ，在不熟悉的Eclipse+CDT的情况下，我先拿这个小项目练手。\n新建 d:\\temp\\cygwincpp 文件夹，作为一个新的 workspace 。新建一个workspace是为了避免与原来配置的冲突，保证是一个全新的配置； Eclipse File-\u0026gt;Switch Workspace ，切换到这个 workspace； Windows-\u0026gt;Preference-\u0026gt;C/C++-\u0026gt;Debug-Source Lookup Path ，增加一个 Path Mapping； Compliation Path 填写 \\cygdrive\\c ，Local file system path 填写 c:\\ ； 如果有更多的盘符，最好全部添加到 Path Mapping 中； 路径映射的作用是在使用gdb调试的时候，让gdb能够找到基于cygwin路径的文件； New-\u0026gt;C++ Project-\u0026gt;Static Library-\u0026gt;Empty Project-\u0026gt;Cygwin GCC ，创建一个空的库项目，命名为 chipmunk ； 打开 chipmunk 项目的属性，C/C++ Build-\u0026gt;Enviroment 界面应该已经自动设置了 PATH 变量。这个变量仅用于这个项目，不会影响环境变量的值。这个变量中已经默认加入了 ${CYGWIN_HOME}\\bin 路径； 如果你的环境变量中没有 CYGWIN_HOME ，那么设置它，指向cygwin的安装目录即可； 或者你也可以直接在 C/C++ Build-\u0026gt;Enviroment 界面中， 在 PATH 之前增加一个 CYGWIN_HOME 变量，指向cygwin的安装目录； 项目属性 C/C++ Build-\u0026gt;Settings-\u0026gt;Tool Settings-\u0026gt;Cygwin C Compiler-\u0026gt;Miscellaneous-\u0026gt;Other flags ，在编译参数的最后加入 -std=c99 参数。否则在编译的时候会for循环格式错误； 项目属性 C/C++ Build-\u0026gt;Settings-\u0026gt;Binary Pasers 确保 Cygwin PE Parser 处于选中状态。这个设置保证在编译出错的时候，可以通过点击错误文本直接跳转到错误源码位置； 将 [cocos2d-x]\\external\\chipmunk\\ 下的 src 和 include 目录复制到新建的项目所在目录中； 项目属性 C/C++ General-\u0026gt;Paths and Symbols-\u0026gt;Includes-\u0026gt;GNU C ，添加 include/chipmunk 路径； Eclipse Project-\u0026gt;Build ，默认会构建 Debug的程序； Build成功之后，可以使用F11进行gdb调试。 编译chipmunk是比较顺利的。但chipmunk完全使用C++写成，没有依赖特定平台的代码。CocosDenshion 就不同了，它利用了不同平台的代码进行处理，我使用cygwin无法将其编译成功。\n使用cygwin还有一个问题，就是不支持Windows路径。make自动生成的源码，中间如果包含 C:\\... 开头的绝对路径，就会无法编译。\nchipmunk能够正常编译，是由于我们把源码复制到了项目中，它的编译完全是基于相对路径的。而cocos2d-x其他项目的互相引用非常复杂，不太容易完全使用相对路径进行配置。而且我又碰到了一个更纠结的问题：在使用相对路径引用的时候，如果路径不在项目中，即使填写是正确的，CDT总是报错找不到源文件。\n在纠结的编译过程中，我还发现有些头文件干脆就只能允许 Visual C++ 编译器来编译（源码中的提示），这说明用cygwin环境来编译win32上的cocos2d-x根本行不通。\n但是，我在源码中也看到了许多 MINGW32 宏，这说明cocos2d-x是为 MINGW32 做过优化处理的，或许和上面提到的 Now compiles with the mingw compiler 有关。而且 MINGW 可以看作是原生执行的Windows程序，因此我决定改为使用 MinGW 编译器来继续折腾。\n四、MinGW环境配置 由于上面说到的原因，我换用 MinGW 作为编译环境。设置与 cygwin 类似，不必把 MinGW 路径加入 PATH，只需要定义两个环境变量，或者直接设置C++项目的 Enviroment 即可。\n在 cocos2d-x 提供的项目中，Linux项目是使用Eclipse建立的。而且，Linux项目也是采用 gcc 编译器，很多配置都是一样的。所以，我采用比较取巧（也是偷懒）的办法，直接复制Linux项目，基于Linux项目进行修改，然后编译。\n五、在 MinGW 环境下编译 libChipmunk 复制 [cocos2d-x]\\external\\chipmunk\\proj.linux 为 [cocos2d-x]\\external\\chipmunk\\proj.elipse.mingw ； 使用Eclipse导入这个项目，由于在Linux下也是使用CDT来建立的项目，因此项目的名称等都已经配置好； 打开 libChipmunk 项目的属性，C/C++ Build-\u0026gt;Enviroment界面应该已经自动设置了 PATH 变量，且已经默认加入了 ${MINGW_HOME}\\bin;${MSYS_HOME}\\bin 路径； 在操作系统环境变量中/或者在 C/C++ Build-\u0026gt;Enviroment 界面的 PATH 之前定义 MINGW_HOME 和 MSYS_HOME 这两个变量，分别指向 MinGW 和 MSYS的安装目录； 在我的机器上，它们是 d:\\MinGW 和 d:\\MinGW\\msys\\1.0； 项目属性 C/C++ Build-\u0026gt;Settings-\u0026gt;Tool Settings 将Debug和Release的 Current Toolchain 改为 MinGW GCC ； 其他的配置，与 cygwin 的类似。 使用 MinGW 编译 libChilpmunk 也比较顺利。 我接着又使用同样的方法编译了 cocos2d-x 自带的Box2D库，也成功了。\n六、在 MinGW 环境下编译 libCocosDenshion 我采用和编译 libChipmunk/Box2D 同样的方法，来编译 libCocosDenshion。\n这个编译碰到了许多问题，最终以失败告终。虽然失败了，我还是把自己的几点体会和注意事项总结一下：\n与前面两个库不同，libCocosDenshion 包含了太多平台相关的库； 有的include库包含了 MINGW32 宏，说明对MINGW32做了优化，而有的库则没有； Linux项目对于平台相关库，引用的是Linux相关的头文件，我需要把它们都切换成w32的头文件； 可能正如 Now compiles with the mingw compiler 所说，必须自己写 cmake 文件才能正常编译（该文透漏自己改了许多地方），而我没有cmake经验，也暂时没有大量时间去研究它； 对 gcc 编译器的使用方法不熟悉，对命令行参数不熟悉。 七、总结 如果连 libCocosDeshion 都编译不过，自然就不能尝试 libCocos2d 了。这次配置最终还是失败了。\n我后来又不甘心尝试了使用 MS Virtual C++ 编译环境，依然是碰到了许多问题，但我已无力（更无心）去解决了，或许这才是正道吧。\n还是打回原形老老实实去调教VS吧。\n八、参考文章 Setup Cygwin toolchain in Eclipse CDT 不用vs和cygwin！Eclipse+cdt实现cocos2dx跨平台解决方案(Android版) Eclipse 4.3 (Kepler) for C/C++ Programming How To Install Eclipse CDT 8.2 and Get Started Eclipse CDT + Cygwin C/C++开发配置中的问题 error: ‘for’ loop initial declarations are only allowed in C99 mode 将 Visual Studio C 和 C++ 项目迁移到 Eclipse CDT ","date":"2013-10-21","description":"","lastmod":"2013-10-21T16:29:15Z","slug":"using-eclipse-cdt-to-develop-cocos2d-x-no-visual-studio","tags":["cpp","cocos2d-x","cygwin","gcc","mingw"],"title":"使用Eclipse+CDT开发Cocos2d-x（不用Visual Studio）","url":"https://blog.zengrong.net/post/using-eclipse-cdt-to-develop-cocos2d-x-no-visual-studio/"},{"categories":["technology"],"content":"今天整理以前的源码时，发现一个用ANE实现iOS消息推送的半成品。隐约中记得AIR的某个更新版本在运行时中实现了iOS消息推送，于是找了一下资料。\n原来，AIR 3.4已经把消息推送功能整合到AIR运行时中了。下面的内容摘自 发行说明 | Flash Player® 11.4、AIR® 3.4\niOS 推送通知\n此功能将使用 APNS（苹果推送通知服务）和提供程序（将与 APN 进行通信的第三方服务器）生成通知。已引进新的软件包 flash.notifications。 推送通知的发送完全依赖 Apple 的推送通知服务，APNS 不保证推送通知的发送。 Apple 也建议每次启动应用程序时订阅推送通知。每次客户端应用程序订阅推送通知时，APNS 会向客户端应用程序提供代号 id，并且此代号 id 将发送给将发送远程通知的第三方服务器或提供程序。\n下面有一些文章介绍，从实现到范例一应俱全：\nUsing push notifications in AIR iOS apps Push Notifications Support in iOS RemoteNotifier AIR3.4 Push Notification事例 Adobe AIR 與 Push Notifications ","date":"2013-10-18","description":"","lastmod":"2013-10-18T01:28:23Z","slug":"push-notifications-support-in-ios-in-air34","tags":["air","ane","ios"],"title":"AIR中不使用ANE实现iOS消息推送","url":"https://blog.zengrong.net/post/push-notifications-support-in-ios-in-air34/"},{"categories":["technology"],"content":"最近正在阅读《C++ Primer（中文第4版）》，正好又碰上中文第5版出版，于是入了一本，斗胆在这里以一个C++新手的思维方式对两版进行比较，并写出自己的感受。\n章节修改 修改 感受 4版2.8节的 Sames_item 提前到1.5节。 体现了对类的重视 4版第4章中“数组和指针”中的指针类型介绍加入到第2章“变量和基本类型” 让读者尽早接触指针概念 4版第2章“变量和基本类型”，重新进行了分类，从9个小节整合成了5个小节 分类后结构更清晰 4版第4章“数组”，内容整合进入第3章，篇幅变小。 因为指针和const的概念已经移到前面章节，“数组和指针”不再作为单一的章节存在 4版第5章、5版第4章“表达式”篇幅变化不大，将new和delete移动到新的章节中 4版第6章、5版第5章“语句”，重新进行了分类，从14个小节整合成了6个小节 分类后结构更清晰 4版第7章、5版第6章“函数”，重新进行了分类，从9个小节整合成了7个小节 分类后结构更清晰 4版12章“类”提前到了5版第7章 对类的介绍提前 增加了新的第8章“IO库” IO库篇幅不大，单独拿出来应该是为了表明重要性 增加了新的第12章“动态内存”，将其他几章中关于动态内存的内容全部整合在这里，并着重增加了C++11标准新增的关于内存管理的内容 内存管理相当重要，作为单独的章节非常合理 增加了新的章节第17章“标准库特殊设施”，将不常用的C++编程技巧放在一起 便于在阅读的时候选择 部分修改 4版部分 5版部分 修改 感受 第二部分 容器和算法 第二部分 C++标准库 4版第3章“标准库”、第9章“顺序容器”、第10章“关联容器”、第11章“泛型算法”加上新的第12章“动态内存”，组成了第二部分“C++标准库” 把标准库作为一个大的部分，强调标准库与“动态内存”的关系 第三部分 类和数据抽象，第四部分 面向对象编程与泛型编程 第三部分 类设计者工具 4版第13章“复制控制”、第14章“重载操作符与转换”、第15章“面向对象编程”、第16章“模版和泛型编程”组成了第三部分“类设计者工具” 4版的第三、第四部分整合了，这样整合更方便面向对象概念的理解，并强调泛型编程和类型转换、拷贝控制等等都是为了对类进行更好的设计 第五部分 高级主题 第四部分 高级主题 增加了新的第17章 将不常用的C++功能放在一起，方便读者在阅读时候的选择 C++11 《C++ Primer（第五版）》为C++11标准进行了重写。C++11标准曾经被称为C++0x，支持C++11标准主要靠编译器。这里贴几个C++11标准的链接。\nVisual Studio 2012 中 Visual C++的新增功能 C++0x/C++11 Support in GCC A comparison of C++11 language support in VS2012, g++ 4.7 and Clang 3.1 Announcing November CTP of the C++ compiler, now with more C++11 C++11 in wikipedia ","date":"2013-10-14","description":"","lastmod":"2013-10-14T06:18:33Z","slug":"comparison-of-cpp-primer-between-v4-and-v5","tags":["cpp"],"title":"C++Primer 第4版和第5版比较","url":"https://blog.zengrong.net/post/comparison-of-cpp-primer-between-v4-and-v5/"},{"categories":["others"],"content":"有那么多智能手环，我选择了几个研究了下：\nFitbit Flex Jawbone Up Nike Fuelband 最后发现还是Fitbit更适合我。于是找某宝700元入了个。用了2天，发下评测。\n顺便说一句，由于 Jawbone Up 比较有名，目前散装货已经卖到500元，不知是否正品。而 Fitbit Flex 价位一直坚挺在 600~700 元区间。\n顺便再说一句，Fitbit Force 今天已经发布，加入手表功能，加入高度计可以检测爬楼，还加入了来电提醒。\n优点：\n和手机、电脑同步都很方便，而且自动； 支持Android； 设计低调，不显眼，不易被人发现，适合真正的Geek； 轻敲操作很有趣； 静默闹钟很有用； 电池5天没问题； 睡眠监控比较准； 防水牛B。 缺点：\n没有时钟功能，其实我认为是很容易实现的； 虽然防水，但腕带凹槽中容易积累水和汗渍，感觉不干净； 没有jawbone up那么显眼，不适合希望装B的人； report居然要付费。 我在fitbit上的主页\n","date":"2013-10-11","description":"","lastmod":"2013-10-11T02:10:58Z","slug":"fitbit-flex-testing","tags":["iot"],"title":"Fitbit Flex试用体验","url":"https://blog.zengrong.net/post/fitbit-flex-testing/"},{"categories":["technology"],"content":"Redmine安装过程疑难杂症\nRedmine diagnoses on installations.\n2016-12-05 更新： 在 Ubuntu 14.04 + ruby 2.3.3 下安装 Redmine 3.3.1。 2014-09-05 更新： 进行了一次服务器搬迁，将原来位于香港的服务器搬回内地机房，redmine也要搬过来。因此增加了一些记录。 Redmine的安装，看 RedmineInstall 就可以搞定。但由于我对Ruby不熟悉，还是碰到了一些问题，下面是个记录。\n安装平台的选择 Redmine 明确标注了可以使用哪几个版本的 Ruby 。但并没有说哪个版本比较好。我的感受是 1.9.3 好像比较靠谱。\nUbuntu 12.04 LTS/CentOS 6.3 Ruby 2.0.0/Ruby 1.9.3 Redmine 2.3.3 Ruby on Rails 安装 如何使用RVM在Ubuntu 12.04 LTS上安装Ruby on Rails\ngem --version 如果在使用gem的时候碰到这样的提示：\n1gem --version 2# /usr/local/lib/ruby/1.9.1/yaml.rb:84:in `\u0026lt;top (required)\u0026gt;\u0026#39;: 3# It seems your ruby installation is missing psych (for YAML output). 4# To eliminate this warning, please install libyaml and reinstall your ruby. 这是在编译安装 ruby 的时候没有先安装 libyaml 所致。但是，即使是你安装 libyaml 之后重新安装 ruby ，这个问题还是不能解决。\n正确的方法，是安装 libyam-devel 库。下面是在 CentOS 6.3 上安装：\n1yum install libyaml-devel 然后，找到你先前编译 ruby 的目录，进入 ext/psych/ 文件夹，执行：\n1ruby extconf.rb make make install 2# checking for yaml.h... yes 3# checking for yaml_get_version() in -lyaml... yes 4# creating Makefile 然后再执行一次 make install 。如果你已经 clean 了原来的编译内容，那么则需要重新编译。\n1make install 2# compiling to_ruby.c 3# compiling parser.c 4# compiling psych.c 5# compiling emitter.c 6# compiling yaml_tree.c 7# linking shared-object psych.so 8# /usr/bin/install -c -m 0755 psych.so /usr/local/lib/ruby/site_ruby/1.9.1/x86_64-linux 9# installing default psych libraries 再次执行 gem -v ，发现 warning 已经消失了。\nbundle install 如果碰到 mysql2 问题，就像这样：\nInstalling mysql2 (0.3.11) with native extensions Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension. /Users/rarneson/.rvm/rubies/ruby-1.9.3-p125/bin/ruby extconf.rb checking for rb_thread_blocking_region()... yes checking for rb_wait_for_single_fd()... yes checking for mysql_query() in -lmysqlclient... no checking for main() in -lm... yes checking for mysql_query() in -lmysqlclient... no checking for main() in -lz... yes checking for mysql_query() in -lmysqlclient... no checking for main() in -lsocket... no checking for mysql_query() in -lmysqlclient... no checking for main() in -lnsl... no checking for mysql_query() in -lmysqlclient... no checking for main() in -lmygcc... no checking for mysql_query() in -lmysqlclient... no *** extconf.rb failed ***\n安装这几个包以解决 mysql2 问题：\n1apt-get install mysql-client libmysqlclient-dev 如果碰到 pg 问题，就像这样：\nInstalling pg (0.17.0) Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension. /usr/local/rvm/rubies/ruby-2.0.0-p247/bin/ruby extconf.rb checking for pg_config... no No pg_config... trying anyway. If building fails, please try again with --with-pg-config=/path/to/pg_config checking for libpq-fe.h... no Can't find the 'libpq-fe.h header *** extconf.rb failed ***\n安装这个包（我没拼错，就是libpq，不是libpg）：\n1apt-get install libpq-dev 如果碰到 rmagick 问题，就像这样：\nInstalling rmagick (2.13.2) Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension. /usr/local/rvm/rubies/ruby-2.0.0-p247/bin/ruby extconf.rb checking for Ruby version \u0026gt;= 1.8.5... yes checking for gcc... yes checking for Magick-config... no Can't install RMagick 2.13.2. Can't find Magick-config in /usr/local/rvm/gems/ruby-2.0.0-p247/bin:/usr/local/rvm/gems/ruby-2.0.0-p247@global/bin:/usr/local/rvm/rubies/ruby-2.0.0-p247/bin:/usr/local/rvm/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games *** extconf.rb failed ***\n安装这个几个包解决它：\n1apt-get install imagemagick libmagickwand-dev 碰到上面两个问题，也可以不必安装相关的库，而是在安装的时候忽略它们：\n1bundle install --without postgresql rmagick 如果上面的代码不管用，可能是在配置文件中指定了它们。你可以打开 config/database.yml 中有相关配置，例如我的：\n1#test_pgsql: 2# adapter: postgresql 3# database: redmine_test 4# host: localhost 5# username: postgres 6# password: \u0026#34;postgres\u0026#34; 7# 8#test_sqlite3: 9# adapter: sqlite3 10# database: db/test.sqlite3 那么，可以同样忽略它们： --without test_pgsql test_sqlite3 。我的方法更简单粗暴（像上面），就是注释它们。\n如果使用ruby 1.8.7，安装 redcarpet 出错\n默认安装的redcarpet是3.0，如果出错可以选择安装2.0版本：\n1gem install redcarpet -v 2.0 使用测试服务器正常，但 Apache 不能访问 你使用下面的代码，可以在 http://localhost:3000/ 正常访问 redmine.\n1ruby script/rails server webrick -e production 但是，你使用 Apache 却不行，这一般是因为你被 SELinux 给禁止了。\n例如我在 CentOS 6.3 下碰到的情况是：\n18084 type=AVC msg=audit(1409905240.973:788834): avc: denied { execute_no_trans } for pid=27260 comm=\u0026#34;httpd\u0026#34; path=\u0026#34;/usr/local/l ib/ruby/gems/1.9.1/gems/passenger-4.0.50/buildout/agents/PassengerWatchdog\u0026#34; dev=dm-0 ino=1578438 scontext=unconfined_u:syste m_r:httpd_t:s0 tcontext=unconfined_u:object_r:lib_t:s0 tclass=file 这些 log 可以在 /var/log/audit/audit.log 中查到。\n最简单的方法是禁用 SELinux ，执行 setenforce 0 在系统运行状态取消，这样不必重启系统。\n然后编辑 /etc/sysconfig/selinux 保证系统重启后也处于禁用状态：\n1# This file controls the state of SELinux on the system. 2# SELINUX= can take one of these three values: 3# enforcing - SELinux security policy is enforced. 4# permissive - SELinux prints warnings instead of enforcing. 5# disabled - No SELinux policy is loaded. 6#SELINUX=enforcing 7SELINUX=disabled 8# SELINUXTYPE= can take one of these two values: 9# targeted - Targeted processes are protected, 10# mls - Multi Level Security protection. 11SELINUXTYPE=targeted 然后重启 Apache 即可生效。\n当然，如果愿意折腾 SELinux（毕竟比较安全） ，那么可以安装 audit2allow 来允许模块通过。\n在 CentOS 上找一下这个模块在哪里：\n1yum provides \\*/audit2allow 在 CentOS 上安装 audit2allow ：\n1yum install policycoreutils-python 然后，参考下面的文章自己折腾吧！\nSELinux 的 audit2allow 工具程序 selinux阻止服务器启动解决方法(需要安装audit2allow) 利用 audit2allow 创建自定 SELinux 政策模块 Redmine Error: Phusion Passenger Watchdog Failed to Start 进入 Administration-Settings 报 HTTP 500 错误 我将 redmine 整体从另一台服务器搬迁过来之后，终于配置成功。进入后台正常，但进入管理员的设置界面则出现下面的提示：\nInternal error An error occurred on the page you were trying to access. If you continue to experience problems please contact your Redmine administrator for assistance. If you are the Redmine administrator, check your log files for details about the error. 解决方法：\n查看 redmine 的 tmp/cache 目录，查看目录结构应该如下所示：\n1[root@localhost redmine]# tree tmp/cache 2tmp/cache 3└── 900 4 └── 0F0 5 └── i18n%2Flanguages_options 停止 redmine，然后删除 tmp/cache 目录下的所有文件，再启动 redmine 。\n然后 管理员设置界面 Administration-Settings 就可以进入了。\n这时查看 tmp/cache 目录结构，会发现先前删除的文件和文件夹自动被创建了。\n[passenger_native_support.so] trying to compile for the current user (redmine) and Ruby interpreter App 25230 stderr: [passenger_native_support.so] trying to compile for the current user (redmine) and Ruby interpreter... App 25230 stderr: (set PASSENGER_COMPILE_NATIVE_SUPPORT_BINARY=0 to disable) App 25230 stderr: Warning: compilation didn't succeed. To learn why, read this file: App 25230 stderr: /tmp/passenger_native_support-1bk9vkp.log App 25230 stderr: [passenger_native_support.so] finding downloads for the current Ruby interpreter... App 25230 stderr: (set PASSENGER_DOWNLOAD_NATIVE_SUPPORT_BINARY=0 to disable) App 25230 stderr: Could not download https://oss-binaries.phusionpassenger.com/binaries/passenger/by_release/5.0.30/rubyext-ruby-2.3.3-x86_64-linux.tar.gz: no download tool found (curl or wget required) App 25230 stderr: Trying next mirror... App 25230 stderr: Could not download https://s3.amazonaws.com/phusion-passenger/binaries/passenger/by_release/5.0.30/rubyext-ruby-2.3.3-x86_64-linux.tar.gz: no download tool found (curl or wget required) App 25230 stderr: [passenger_native_support.so] will not be used (can't compile or download) App 25230 stderr: --\u0026gt; Passenger will still operate normally. App 25230 stderr: /usr/local/lib/ruby/gems/2.3.0/gems/htmlentities-4.3.1/lib/htmlentities/mappings/expanded.rb:465: warning: key \u0026quot;inodot\u0026quot; is duplicated and overwritten on line 466 App 25230 stderr: sh: 1: svn: not found App 25230 stderr: sh: 1: env: not found 要解决这个问题，首先要确保使用当前的 ruby 版本编译依次 native 支持：\nruby $(which passenger-config) build-native-support 但是，由于权限的原因，passenger 在启动的时候，使用的是 nobody 权限。使用 passenger_user 参数可以调整权限。例如我的个人用户是 zrong ，我在 zrong 账户下使用 passenger-config build-native-support 编译成功，就需要在 nginx 的 redmine 站点配置文件中设置 passenger_user zrong; 即可。 这个 issue 中有更详细的说明。\nMissing secret_token App 25230 stderr: [ 2016-12-05 20:47:44.2447 25312/0x007fb5363b5f40(Worker 1) utils.rb:87 ]: *** Exception RuntimeError in Rack application object (Missing `secret_token` and `secret_key_base` for 'production' environment, set these values in `config/secrets.yml`) (pr ocess 25312, thread 0x007fb5363b5f40(Worker 1)): App 25230 stderr: from /usr/local/lib/ruby/gems/2.3.0/gems/railties-4.2.7.1/lib/rails/application.rb:534:in `validate_secret_key_config!' App 25230 stderr: from /usr/local/lib/ruby/gems/2.3.0/gems/railties-4.2.7.1/lib/rails/application.rb:246:in `env_config' App 25230 stderr: from /usr/local/lib/ruby/gems/2.3.0/gems/railties-4.2.7.1/lib/rails/engine.rb:514:in `call' App 25230 stderr: from /usr/local/lib/ruby/gems/2.3.0/gems/railties-4.2.7.1/lib/rails/application.rb:165:in `call' App 25230 stderr: from /usr/local/lib/ruby/gems/2.3.0/gems/railties-4.2.7.1/lib/rails/railtie.rb:194:in `public_send' App 25230 stderr: from /usr/local/lib/ruby/gems/2.3.0/gems/railties-4.2.7.1/lib/rails/railtie.rb:194:in `method_missing' App 25230 stderr: from /usr/lib/ruby/vendor_ruby/phusion_passenger/rack/thread_handler_extension.rb:97:in `process_request' App 25230 stderr: from /usr/lib/ruby/vendor_ruby/phusion_passenger/request_handler/thread_handler.rb:152:in `accept_and_process_next_request' App 25230 stderr: from /usr/lib/ruby/vendor_ruby/phusion_passenger/request_handler/thread_handler.rb:113:in `main_loop' App 25230 stderr: from /usr/lib/ruby/vendor_ruby/phusion_passenger/request_handler.rb:416:in `block (3 levels) in start_threads' App 25230 stderr: from /usr/lib/ruby/vendor_ruby/phusion_passenger/utils.rb:113:in `block in create_thread_and_abort_on_exception' 需要执行下面这条指令：\nsudo bundle exec rake generate_secret_token cannot load such file -- bundler/setup (LoadError) 我的 ruby 版本是 2.3.3 ，但运行 redmine 出现这样的错误：\n[ 2016-12-05 22:00:13.3781 28180/7fd216076700 age/Cor/App/Implementation.cpp:304 ]: Could not spawn process for application /opt/redmine/redmine: An error occurred while starting up the preloader. Error ID: 545284a9 Error details saved to: /tmp/passenger-error-deS5EL.html Message from application: cannot load such file -- bundler/setup (LoadError) /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require' /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require' /usr/lib/ruby/vendor_ruby/phusion_passenger/loader_shared_helpers.rb:430:in `activate_gem' /usr/lib/ruby/vendor_ruby/phusion_passenger/loader_shared_helpers.rb:297:in `block in run_load_path_setup_code' /usr/lib/ruby/vendor_ruby/phusion_passenger/loader_shared_helpers.rb:435:in `running_bundler' /usr/lib/ruby/vendor_ruby/phusion_passenger/loader_shared_helpers.rb:296:in `run_load_path_setup_code' /usr/share/passenger/helper-scripts/rack-preloader.rb:100:in `preload_app' /usr/share/passenger/helper-scripts/rack-preloader.rb:156:in `\u0026lt;module:App\u0026gt;' /usr/share/passenger/helper-scripts/rack-preloader.rb:30:in `\u0026lt;module:PhusionPassenger\u0026gt;' /usr/share/passenger/helper-scripts/rack-preloader.rb:29:in `\u0026lt;main\u0026gt;' 这是由于路径错乱导致。由于 passenger 安装在 /usr/bin 中，它默认使用了系统自带的 ruby 1.9.1 。这需要修改 /etc/nginx/passenger.conf ：\n将：\npassenger_ruby /usr/bin/passenger_free_ruby; 改为：\npassenger_ruby /usr/local/bin/ruby; 当然，你也可以指定其他的 ruby 版本，passenger_root 的值保持不变。\n参考 SELinux permission denied to Phusion Passenger for redmine Wang Zhe Blog How do I install Ruby with libyaml on Ubuntu 11.10? Install Ruby 1.9.3 with libyaml on CentOS Internal Error 500 on \u0026quot;settings\u0026quot; Installing Passenger + Nginx on Ubuntu 14.04 LTS (with APT) Redmine, Passenger, and Nginx on Ubuntu How to Install Redmine 3.2 with Nginx on Ubuntu 16.04 ","date":"2013-09-29","description":"","lastmod":"2013-09-29T07:14:48Z","slug":"redmine_diagnoses_on_installations","tags":["centos","linux","ruby","ubuntu"],"title":"Redmine安装过程疑难杂症","url":"https://blog.zengrong.net/post/redmine_diagnoses_on_installations/"},{"categories":["technology"],"content":"Ubuntu 12.01 LTS 的源自带的Lighttpd版本稍旧，因此我还是准备自己编译一个最新的1.4.33版。\n具体的编译方法可以参考这里 。\n编译中遇到2个问题：\n1.找不到 pcre-devel 包\n这个包在ubuntu的源中名称为 libpcre3-dev\n2.找不到 bzip2-headers\n在ubuntu的源中，安装这个包： libbz2-dev\n参考：\n手动编译mplayer(mencoder)，支持x264+AAC，解决ubuntu下使用mencoder压缩视频出现MPlayer was compiled without libfaac错误问题 What package I should install for pcre-devel? Source Package: bzip2 (1.0.6-1) ","date":"2013-09-28","description":"","lastmod":"2013-09-28T10:24:36Z","slug":"compile-lighttpd-on-ubuntu-12-04-lts","tags":["apache","http","lighttpd","linux","ubuntu"],"title":"在Ubuntu 12.04 LTS上编译Lighttpd","url":"https://blog.zengrong.net/post/compile-lighttpd-on-ubuntu-12-04-lts/"},{"categories":["news"],"content":"原文：How To Install Ruby on Rails on Ubuntu 12.04 LTS (Precise Pangolin) with RVM\n关于Ruby on Rails Ruby on Rails是一个快速创建web程序的框架。\n在一个虚拟服务器上安装 Ruby on Rails 需要花点时间，不过幸运的是，有个有用的工具能帮我们简化安装过程。\n第一步——用RVM安装Ruby 在开始之前，我们需要在VPS上运行一次快速更新，确保所有的包都是最新的：\n1sudo apt-get update 更新完毕之后，我们可以开始安装RVM(Ruby Version Manager).这是个让我们能在一台服务器上使用多个Ruby版本的优秀程序。不过，我们将只使用它来安装最新的Ruby版本。\n如果在你的系统上没有 curl ，你需要安装它：\n1sudo apt-get install curl 要安装RVM，在终端中输入下面的命令：\n1curl -L https://get.rvm.io | bash -s stable 安装完毕后，载入RVM。你需要先退出shell然后重新开启一个新的shell会话。\n1source ~/.rvm/scripts/rvm (zrong: 我没有退出和重启shell会话，所以我使用的是 source /usr/local/rvm/scripts/rvm ）\n为了能正常工作，RMV必须安装一些依赖。你可以让RVM自动安装它们：\n1rvm requirements 你可能需要输入root密码来允许安装这些依赖。\n某些情况下，系统可能会提示找不到zlib包。RVM 页面详细描述了 这个问题以及解决方法。\n第二步——安装 Ruby 一旦你使用了 RVM，安装Ruby就简单了。\n1rvm install ruby 最新版本的的Ruby现在已经被安装。不过，因为我们会同时使用多个 Ruby 版本，我们需要告诉系统应该使用刚才安装的 Ruby 作为默认版本。\n1rvm use ruby --default (zrong: RVM并不能管理系统自带的Ruby，如果想返回系统管理的Ruby，可以执行 rvm reset ）\n第三步——安装 RubyGems 确认我们已经安装了 Ruby on Rails 所需的所有组件。现在可以继续使用 RVM 安装 gems。在终端中输入下面的命令：\n1rvm rubygems current 第四步——安装 Rails 万事俱备，是安装 Rails 的时候了。打开终端输入下面的命令：\n1gem install rails 这次安装需要一些时间和耐心。它将安装 Ruby on Rails 到你的服务器上。\n更多 在服务器上安装了 Ruby on Rails之后，你就可以开始为你的站点创建 SSL证书 或者安装 FTP服务器 了。\n","date":"2013-09-28","description":"","lastmod":"2013-09-28T04:49:19Z","slug":"how-to-install-ruby-on-rails-on-ubuntu-12-04-lts-with-rvm","tags":["linux","ruby","ubuntu"],"title":"【译】如何使用RVM在Ubuntu 12.04 LTS上安装Ruby on Rails","url":"https://blog.zengrong.net/post/how-to-install-ruby-on-rails-on-ubuntu-12-04-lts-with-rvm/"},{"categories":["technology"],"content":"近期提交到AppStore上的应用被拒，原因如下：\n2.3 In addition, during review we were prompted to provide consent to use the microphone, however, we were not able to find any features or functionality that use the microphone for audio recording. Please see the attached screenshot's for more information. The microphone consent request is generated by the use of either AVAudioSessionCategoryRecord or AVAudioSessionCategoryPlayAndRecord audio categories. If you do not intend to record audio with your application, it would be appropriate to choose the AVAudioSession session category that fits your application's needs or modify your app to include audio-recording features. ......\n大概的意思是说我的应用有请求Microphone权限，但没有使用这个权限。Apple同时发来一张截图：\n但是我清楚的知道，我的应用绝对是没有使用麦克风的。只有某卫士某地图才会无耻地跑去请求发短信权限麦克风权限银行帐号权限……\n我错了，那是Android。\n下面是调试过程：\n开始怀疑刚刚接入的Facebook SDK，去掉之，无效。 再怀疑AppleIAP代码，去掉之，无效。 去掉所有的外部ANE，重新打包，无效。 安装目前正在AppStore上下载的包，在iOS 7上也会出现这个权限请求。 看来，是AIR的问题没错了。 重新制作了一个空的AIR项目，这下正常了，没有再出现这个出现这个权限请求对话框。 花费了半天的时间调试，终于让我明白了原因所在。\n原来AIR3.8（和AIR3.8以前）的SDK打包的IPA，只要使用了 Sound.play() 命令，就会出现 Microphone 权限请求。\n这是多么蛋疼的一个BUG啊啊啊啊！！！\n好在AIR 3.9 beta解决了这个问题，看来只有硬着头皮用Beta了。\n我发现的更多Adobe Bug，请看这里： AdobeBug 。\n","date":"2013-09-26","description":"","lastmod":"2013-09-26T11:16:25Z","slug":"wrong-microphone-features-in-air-on-ios7","tags":["adobebug","air","ios"],"title":"AIR在iOS7上的Microphone权限问题解决","url":"https://blog.zengrong.net/post/wrong-microphone-features-in-air-on-ios7/"},{"categories":["others"],"content":"2013-10-03 更新：加入去大队处理后的内容。\n这是我在武汉市公安局交通管理局上的咨询结果，放在这里备忘。原文在 这里。\n问题 我是A2驾驶证，初次领证日是2003-09-25，副页上写着“请于每年09月提交身体条件证明。请于2015年9月25日前九十日申请换领新驾驶证。”\n我在本网站“驾驶证定期审验”中看到：\n持有大型客车、牵引车、城市公交车、中型客车、大型货车驾驶证的驾驶人，应当在每个记分周期结束后三十日内到公安机关交通管理部门接受审验，但在一个记分周期内没有记分记录的，免予本记分周期审验。\n但是，我又在网上看到，如果在领证日后年审，会罚款200元扣3分。\n我的问题是：\n我到底是应该在9月25日之后去进行年审，还是要在9月25日之前去年审？ 我在2013年没有扣分，是否必须去年审？ 江汉交通大队是否设有体检点？ 回答 您好，结束后30日内审验。没记分就不去。各大队都设有体检点。另，3分200元，是车辆年审逾期的处罚。\n评论 不得不说，这个回答还是比较及时的。我7:30分发的问题，中午12:00左右就回答了。\n回答简洁准确，职业素养不错，我估计应该是临时工答的。\n但是，对于可以不去年审这件事，我依然是不敢做的。我还是准备25日后去一趟大队。因为，这有些回答也不全是正确的，例如 这条 ，江汉大队负责驾照年审的办事地点已经不在红旗渠路特1号了。\n2013-10-03 更新：\n上面的关于办事地点的回答是正确的。我跑了一趟武汉市公交局交通管理局江汉大队得知，负责年审的办事窗口 又搬回红旗渠路特1号了 。\n真是浪费纳税人金钱和时间啊……\n像我这样的职业纳税人，骑着电动车跑了30分钟，呼吸了那么多新鲜的汽车尾气和灰尘，面对着一脸“我是你爸爸” 模样的大队保安员同志，就得到这么六个字答复：“红旗渠路特1号” 。\n不过话说江汉大队还真是大啊…… 在那么好的一个地方，居然还那么大的占地。\n说点正事吧。像我这样没有记分记录的A照，确实是不用年审，甚至是连体检都不用。排队之后，什么表都没有填，直接就通过了，让我明年再去。\n明年到底去不去列？这是个问题。\n其他 经常去这个咨询窗口看看也是丰富知识的好办法，例如这几个：\n掉头 闯黄灯 ETC ","date":"2013-09-24","description":"","lastmod":"2013-09-24T07:54:37Z","slug":"wuhan-a2-driver-license","tags":["live"],"title":"武汉A2驾驶证年审具体日期","url":"https://blog.zengrong.net/post/wuhan-a2-driver-license/"},{"categories":["technology"],"content":"问题 cocos2d-x目前的项目生成使用的是tools/project-creator/create_project.py 命令。这个命令生成的项目，默认处于cocos2d-x的projects文件夹中。\n我当然不希望自己的项目处于 cocos2d-x的目录结构中文件夹中，这样不方便管理。\n尝试解决 我首先想到的是使用 mklink 来解决，让 projects中的项目文件夹是我自己的项目文件夹的一个 symbol link。 mklink 是 WIN7自带的一个命令，必须在管理员模式下运行。\n但这个方法有个问题，让我们来看看模版文件：[cocos2d-x]\\template\\multi-platform-cpp\\proj.win32\\HelloCpp.sln\n...... Project(\u0026quot;{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\u0026quot;) = \u0026quot;libcocos2d\u0026quot;, \u0026quot;..\\..\\..\\cocos2dx\\proj.win32\\cocos2d.vcxproj\u0026quot;, \u0026quot;{98A51BA8-FC3A-415B-AC8F-8C7BD464E93E}\u0026quot; ...... Project(\u0026quot;{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\u0026quot;) = \u0026quot;libCocosDenshion\u0026quot;, \u0026quot;..\\..\\..\\CocosDenshion\\proj.win32\\CocosDenshion.vcxproj\u0026quot;, \u0026quot;{F8EDD7FA-9A51-4E80-BAEB-860825D2EAC6}\u0026quot; ...... Virtual Studio项目解决方案中对于其他项目的引用，采用的是相对路径，如果我在symbol link 的文件夹中打开这个项目，当然没有问题。但是如果我在自己的项目文件夹中打开这个项目，这个相对路径引用就会出错。\n我想到的另一个解决方案就是使用环境变量。\n我定义了一个 COCOS2DX_HOME 环境变量，指向我本机的cocos2d-x的安装目录：d:\\cocos2dx\\2.1.4 ，然后在解决方案(.sln)文件中将相对路径修改成这样：\n...... Project(\u0026quot;{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\u0026quot;) = \u0026quot;libcocos2d\u0026quot;, \u0026quot;$(COCOS2DX_HOME)\\cocos2dx\\proj.win32\\cocos2d.vcxproj\u0026quot;, \u0026quot;{98A51BA8-FC3A-415B-AC8F-8C7BD464E93E}\u0026quot; ...... Project(\u0026quot;{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\u0026quot;) = \u0026quot;libCocosDenshion\u0026quot;, \u0026quot;$(COCOS2DX_HOME)\\CocosDenshion\\proj.win32\\CocosDenshion.vcxproj\u0026quot;, \u0026quot;{F8EDD7FA-9A51-4E80-BAEB-860825D2EAC6}\u0026quot; ...... 但在打开这个解决方案的时候，Virtual Studio报错如下：\n项目文件 \u0026quot;d:\\works\\projects\\test\\proj.win32\\$(COCOS2DX_HOME)\\CocosDenshion\\proj.win32\\CocosDenshion.vcxproj\u0026quot; 已被移动、重命名或不在您的计算机上 这说明解决方案并不支持环境变量。\n根据dualface的留言，原来解决方案是支持环境变量的，使用 %COCOS2DX_HOME%这种语法即可。\n最终解决 目前的解决方案，就是修改创建项目脚本，使其支持自定义项目文件夹，并在生成的时候自动替换解决方案(*.sln)中引用目录为绝对路径。当然，也可以替换成环境变量。\n当然，还有项目文件(*.vcxproj)中的一部分引用，这个引用是支持环境变量的。\n这就需要修改 [cocos2dx]\\tools\\project-creator\\create_project.py 和[cocos2dx]\\template\\multi-platform-cpp\\ 。\n我将它们按照原来的目录结构进行了整理，使其仅包含这两个修改过的文件夹。由于内容可能会经常修改，我在github 上建立了一个项目。\n此项目基于 cocos2d-x 2.1.4。\ncocos2d-x-project-creator\n使用方法 定义一个环境变量 COCOS2DX_HOME，指向 cocos2d-x 安装目录。\n使用github项目中的文件夹覆盖自己的cocos2d-x文件夹中的同名目录。用 OS X 的同学要小心点。\n后面就看 Usage 吧：\nUsage: create_project.py -project PROJECT_NAME -package PACKAGE_NAME -language PROGRAMING_LANGUAGE -path d:/works/projects/ Options: -project PROJECT_NAME Project name, for example: MyGame -package PACKAGE_NAME Package name, for example: com.MyCompany.MyAwesomeGame -language PROGRAMING_LANGUAGE Major programing lanauge you want to used, should be [cpp | lua | javascript] -path PROJECT_PATH The directory that project will create in, should be [ d:/works/projects/ | /home/zrong/projects/ ]\nSample 1: ./create_project.py -project MyGame -package com.MyCompany.AwesomeGame Sample 2: ./create_project.py -project MyGame -package com.MyCompany.AwesomeGame -language cpp -path d:/works/projects/\n","date":"2013-09-12","description":"","lastmod":"2013-09-12T03:35:29Z","slug":"modify-cocos2d-x-create-project","tags":["cpp","cocos2d-x","python"],"title":"修改cocos2d-x项目创建器，支持自定义项目路径","url":"https://blog.zengrong.net/post/modify-cocos2d-x-create-project/"},{"categories":["technology"],"content":"Cocos2d-x script language binding:Lua or Javascript?\n2014-01-15更新： 加入Gwill的文章连接。\ncocos2dx script binding: lua or javascript?\n这篇文章是我在之乎的一个回答： http://www.zhihu.com/question/21130385/answer/18827858 ，有修改。\n强调：这篇文章有极强的时效性，在1年时间内应该有一定指导意义。本文成文日期是2013年9月11日。\n目前 Javascript 风头似乎超越一切，所以我一直在纠结，到底是使用Lua Binding还是 Javascript Binding。\n我找了一些资料。如果把下面的资料看完，相信每个人心里都会自有分辨。\nWhy I switched from Lua to Javascript Lua is simple and fast Lua vs. Javascript A discussion on JIT compilers by Brendan Eich Learning Lua Form JS Why Lua? Cocos2dx+lua合适还是Cocos2dx+js合适 Lua 语言有哪些不足？ LUA程序设计(第2版)豆瓣书评中云风的评论 subtle differences between javascript and Lua 乐元素CTO凌聪-基于cocos2d-x二次开发的自有引擎方案分享 Cocos2d-x 2013春季新功能发布和发展规划 Architecture and Directory Structure cocos2d-x changelog cocos2d-x官方论坛，请比较lua binding和js binding帖数 cocos2d JavaScript tests and games 下面说说我的分析，不一定正确。\n再说一遍， 把上面的资料全部认真看完，你一定会有一个对你来说正确的选择。\n就我的感觉，虽然王哲说 Javascript Binding 和 Lua Binding 会并重发展，但我认为对于cocos2d-x团队来说，应该是偏向于Javascript。从现在cocos2d-html5的频繁更新就能看出来了。不过cocos2d-x是开源的引擎，即使官方团队不重视（或者相比JS而言不够重视），也依然有优秀的实现 。\nJavascript 和 Lua 语言本身的优势（性能、语法、特性、 学习曲线、熟练程度），上面已经说了很多，我提供的资料中也有许多深入的讨论。我个人认为这两种语言性能上的差异并不是我选择的主要原因，学习曲线也不是。我没用过Lua，JS能写点东西。但我并不在意学习Lua “浪费” 的那两天时间。\n从 cocos2d-x官方论坛 比较，Lua Binding 和 Javascript Binding帖数差别不大，JS略多，毕竟现在JS在流行程度上更强一些。\n比较最近的 cocos2d-x changelog，会发现JS的相关修改更多一些。而且比较有趣的是，JS的修改有许多是Bug fix，而Lua的修改就只是feature。这是不是从侧面说明了，Lua Binding目前更加稳定呢？ 稳定是多重要的特性啊！\nJavascript Binding最大的优势，我认为就是游戏可以直接通过cocos2d-x html5移植到网页。但我看过 cocos2d JavaScript tests and games 之后，真的感觉到是无法接受那种性能。即使抛开 手游是否需要开发一个网页版本 这种运营问题不谈，运营的同学也一定不希望开放一个这样的HTML5玩家吐槽专版。\n我的选择，已经很明确了，就是Lua。各位提到的IDE问题、调试问题等等，我只能再想办法去解决。我相信即使我选了JS，碰到的此类问题也不会比Lua更少。上面提到的资料中有人讲到了cocos2d-x lua的IDE和调试，应该是个正确的思路。\n这个游戏完成之后，Javascript Binding应该成熟了（以现在的速度，应该会很快），到下一款游戏考虑也不迟。\n强调：这篇文章有极强的时效性，在1年时间内应该有一定指导意义。本文成文日期是2013年9月11日。\n发现 G_will 也写了一篇类似的文章： Cocos2d-x 绑定脚本究竟选择 Lua 还是 JS ？\n","date":"2013-09-11","description":"","lastmod":"2013-09-11T07:55:51Z","slug":"cocos2dx-binding-lua-or-javasrcipt","tags":["cocos2d-x","game","javascript","lua"],"title":"Cocos2d-x script language binding:Lua or Javascript?","url":"https://blog.zengrong.net/post/cocos2dx-binding-lua-or-javasrcipt/"},{"categories":["technology"],"content":"还是cocos2d-x，还是DragonBones。\n已经写了两篇关于cocos2d-x和DragonBones的文章了，对它们两位感兴趣的，可以参考一下：\n在cocos2d-x中使用DragonBones cocos2d-x专用的DragonBones2.2 为了提升骨骼动画的表现力，我们对部分骨骼做了倾斜、缩放、变形的操作，在DragonBones中表现正常。这样，我们可以比较方便的实现呼吸、压扁这类特效，它们的变化虽然比较小，但是对整个动画的质量和表现力来说，会产生非常大的提升。\n可是，这样的效果在CCAramture中表现不正常。\n在对整个动画使用压扁效果的时候，在CCArmature中，相关的几个部件在压扁的过程中表现得好像被拉开了，好像外部有几个力希望把几个骨骼部件拉散一样。而在DragonBones中表现的是整体压扁。\n我看了一下CCArmature包，发现这个包实际上是把AS3写的DragonBones用C++移植了一遍。\n看来必须要看源码才能解决问题了。\n","date":"2013-09-09","description":"","lastmod":"2013-09-09T09:38:18Z","slug":"ccarmature_skew_scale_error","tags":["cpp","cocos2d-x","dragonbones","skeletalanimation"],"title":"cocos2d-x中CCArmature展示挤压和变形动画的问题","url":"https://blog.zengrong.net/post/ccarmature_skew_scale_error/"},{"categories":["technology"],"content":"2015-01-31更新 依然还有朋友在问我这个版本的问题。再次重申，请 不要再使用 这个版本，DragonBones 官方团队已经提供了 DragonBonesCPP for cocos2d-x 2.x 和 3.x 版本。因此，请直接使用官方版本的 DragonBones Panel + DragonBonesCPP，它们会提供所有 AS3 版本的所有功能。\n详情参考下面的两次 重要更新 。\n重要更新 2014-07-15 在 quick-cocos2d-x 中使用 DragonBonesCPP\n重要更新 2014-04-28 DragonBones 开发组已经对 DragonBones 进行了官方的 C++ 移植，很多以前我提到的问题都不复存在了。详情看这里 DragonBones 官方C++版本 for cocos2d-x 。\n我写过一篇 在cocos2d-x中使用DragonBones ，这篇文章中提到了DragonBones插件。本篇是一个补充。\n插件问题 在 CocoStudio官方下载页面 中提供了一个DragonBones插件，用于导出cocos2d-x可以识别的骨骼动画格式。\n但是，这个插件有个问题。\n在导出时，如果使用 “Zip(XML和分开的PNG)” 类型（事实上，对于cocos2d-x来说，只能选择这个选项），同时设置“导出缩放比”选项不为1的时候，导出的元数据文件中的坐标都没有经过缩放。\n这将导致动画在播放的时候出现问题，就像这样：\n其实，这个问题并不是由于修改DragonBones所形成的，而是原来的DragonBones2.0就存在这个问题。\n如果你有兴趣研究的话，可以看这里 ExportDataCommand.as ，这里判断了 _exportType 值为4的情况下不缩放坐标。而“Zip(XML和分开的PNG)”就代表4。\n另外，这个插件是基于2.0版本，而目前的DragonBones最新版本是2.3。当然，由于 2.3的DataFormat改动太大 ，暂时不能使用v2.3版本进行修改。但v2.2是可以的。\n如果你再次有兴趣的话，可以找到cocos2d-x负责解析骨骼动画元数据的类 [cocos2d-x]\\extensions\\CCArmature\\utils\\CCDataReaderHelper.cpp ，通过修改它，来支持新的格式。\nDragonBone2.2 for cocos2d-x 基于上面的原因，我以DragonBones2.2版本为基础制作了这个插件，并重新打包发布。项目的源码在这里：DragonBones2.2 for cocos2d-x\n我做的工作并不多，只是花了一些时间理解了DragonBonesDesignPanel的结构。\n这个项目包含了 SkeletonAnimationLibrary 和 SkeletonAnimationDesignPanel 项目v2.2的内容，做了极少量的修改； jsfl的修改，直接比较 这3个文件即可。其中 skeleton.jsfl.original 是原始文件， skeleton.jsfl.20cocos2dx 是2.0修改版提供的文件， skeketon.jsfl 是我修改的文件； 导出面板中，我增加了一种导出类型 “Zip(XML and PNGs, for cocos2d-x)”； 我修改了导入代码，现在也能在DesignPanel中导入使用上面的类型导出的资源； 其他的修改，请直接比较源码； 如何使用可以看这里： 在cocos2d-x中使用DragonBones。 感谢 DragonBones Team 带来这样优秀的软件；\n感谢对 DragonBones2.0 进行修改的程序员；\n希望这个项目对你们有用。\n","date":"2013-08-29","description":"","lastmod":"2013-08-29T05:48:34Z","slug":"dragonbones20_for_cocos2d-x","tags":["cpp","cocos2d-x","skeletalanimation"],"title":"cocos2d-x专用的DragonBones2.2","url":"https://blog.zengrong.net/post/dragonbones20_for_cocos2d-x/"},{"categories":["impressions"],"content":"这是我在之乎上的一个回答：http://www.zhihu.com/question/20428679/answer/18577831\n其实这篇文章是标题党，我并没有回答这个问题。\n每次看到这样的问题，我就忍不住想出来吐：\nFlash要死了这种言论完全是不负责任不懂Flash没做过AS3没看过Flash的真正能力不了解技术不懂JS没做过JS没看过JS的真正能力不知道浏览器区别不了解webGL不知道JS能干什么的小白胡扯挨踢编辑跟风写出来博眼球博同情博@博follow博顶的扯淡文章中的无脑言论！\n可偏偏有那么多善良的同学们就信了！其实你们只要学一下AS学一下JS学一下H5学一下webGL学一下Stage3D了解一下Flash历史了解一下Adobe的复杂产品线了解一下Jobs的真正意图了解一下Adobe白痴的高层决策了解一下目前页游了解一下目前的手游市场了解一下目前视频网站市场了解一下目前在线点播市场了解一下目前的传统动画市场了解一下……当然，同学们没必要了解也没兴趣了解也不愿意了解直接跟喷是多爽的事情而且还有H5这个后起之秀接着墙倒众人推跟风就是雨Flash死了就是死了跟我也没什么关系Flash没死就是苟延残喘总有一天会死Flash就算重新崛起了最开始Flash死了的言论也不是我说的跟我有毛关系！\n问题中提到的文章：Flash离死亡再近一步：Android正式禁用Flash-CSDN.NET，作者难道从来就不知道有个东东叫做AIR么？这篇文章被到处引用和转载，然后被大量误读，CSDN的编辑居然不做一下了解？\n我难道没有告诉过作者，AIR在手机上正如火如荼的发展么？我们玩的手机游戏（Android和iOS的），可能就是AIR制作的，而AIR这个东西，就是Flash（如果你们认为Flash是个东西的话……嗯，Flash应该是个东西，但Adobe不是个东西）。\n看看这个：Play web and mobile games built with Flash\n当然远远不止这些，因为能上Adobe Showcase的，都是塞过钱的……美帝一定比我们天朝更邪恶更腐败。\n更别提现在的页游，你玩过多少不是用Flash做的页游？你又玩过多少H5的页游？\n为了上面的吐槽，我只能再苦逼一点粘贴一些链接让大家更了解我的本意。当然，看不看随你：\n怎样学习Flash？ 前端开发与 Flash 游戏开发，该怎么选择？ 为什么平板对Flash支持的不好？ Flash 商业模式是什么？Adobe 如何赚钱生存？Flash 能给 Adobe 带来收入吗？ Flex（Adobe Air）开发的应用能直接上架 App Store 吗？ 用adobe air开发的应用程序有哪些局限性或者缺点？ Flash网页应用前景怎样？ 与 Xcode 相比，用 Adobe AIR/Flex做 iOS 开发有哪些优势和局限？ 我在之乎上回答的问题，基本上全是关于Flash的，有兴趣可以看这里：曾嵘答过的问题\n=======================\n吐完了，说说我自己的观点：\nFlash一定会死，我也一定会死； Flash近期不会死； Flash的死，不一定是Flash自身的技术原因导致； Flash的死，很大一部分原因是Adobe那帮白痴高层的决策导致； Flash的死，还有很大一部分原因是Flash的特点决定（会用Flash且愿意用Flash的人大多数都不是程序员）； Flash的死，主要的原因是她仅仅属于Adobe，她仅仅是一张牌而已； Flash一定会死，但Flash IDE不会死，动画师们还需要她； 如果你不了解Flash和Flash IDE的区别，可以看这篇：Flash, Flex, Air, Flashplayer之间的相互关系是什么？ ","date":"2013-08-27","description":"","lastmod":"2013-08-27T02:05:25Z","slug":"flash_will_dead","tags":["adobe","air","flash","study"],"title":"如果 Flash 死了，会怎么样？","url":"https://blog.zengrong.net/post/flash_will_dead/"},{"categories":["technology"],"content":"在cocos2d-x中使用CCArmature实现骨骼动画\nusing CCArmature in cocos2d-x.\n2014-07-15更新：更新 在 quick-cocos2d-x 中使用 DragonBonesCPP 2014-04-28更新：更新 DragonBones 官方C++版本 for cocos2d-x 2013-11-19更新：更新2.2.0bug处理情况 2013-10-15更新：更新2.2.0bug处理情况 2013-10-12更新：加入Cocos2d-x 2.2.0 的bug 2013-10-11更新：加入CocoStudio动画编辑器导入 .fla 文件注意事项 重要更新 2014-07-15 [在 quick-cocos2d-x 中使用 DragonBonesCPP][quick]\n重要更新 2014-04-28 DragonBones 开发组已经对 DragonBones 进行了官方的 C++ 移植，很多以前我提到的问题都不复存在了。详情看这里 [DragonBones 官方C++版本 for cocos2d-x][dbcpp] 。\n基础知识 要看懂本文，你需要了解骨骼动画（或称关节动画）是什么，以及CCArmature 和 DragonBones是什么。下面提供了一些资料：\nSkeletal Animation (Wikipedia en) DragonBones 2.1快速入门指南 Skeletal Animation (cocos2d-x wiki en) 骨骼动画详解 (泰然网) [DragonBones 官方C++版本 for cocos2d-x][dbcpp] 注意，本文讲解的是 cocos2d-x 官方版本中自带的 CCArmature 库的使用。这个库是 DragonBones AS3 库的一个不完全移植版，它的官方骨骼动画制作工具是 Cocostudio(CCS)，而不是Flash。\n因此，使用Flash制作的骨骼动画，可能会被CCArmaue不完全支持。尤其是在CCArmature针对CCS做了那么多修改和优化之后。\n用过CCS的人都知道，这个编辑器超级难用。很难说服美术GGMM去学习这么难用且难看的软件。对于只愿意使用熟悉的工具的美术GGMM来说，或许Flash就是唯一适合的。\nTo flash veteran: do you remember the 'Moho'?\n不要使用官方版本的DragonBones Design Panel 在写这篇文章的时候，DragonBones的官方版本为v2.3。cocos2d-x的稳定版本为2.1.5。\n为了使用cocos2d-x，我们需要选择 Zip(XML and PNGs) 的方式，将图像文件导出为独立的图像帧加上XML格式的元数据文件。官方版本的DragonBones Desigin Panel，会将元数据分成texture.xml和skeleton.xml两个文件，而cocos2d-x目前不支持这种情况。\n因此，我们需要使用修改过的DragonBones Design Panel插件。在CocoStudio的官方论坛中提供了一个这样的插件，版本是2.0。但这个插件有bug。\n因此，我在2.2的基础上重新打包了插件，并修复了bug。详情看这里：cocos2d-x专用的DragonBones2.2 。\n使用这个版本的插件，在导出图像文件的时候，会将texture.xml和skeleton.xml文件合并成1个，同时会修改元数据中的部分格式，使其满足cocos2d-x CCArmature 的解析库要求。\n注意：下面提到DragonBones的时候，均指这个修改过的插件。\nDragonBones Desigin Panel输出的图像数据可以导入CocoStudio Action编辑器 可以使用CocoStudio的Action编辑器将DragonBones Design Panel输出的图片导入，然后重新输出成Cocos2d-x支持的格式。这种格式包含一个把碎图拼接好的png文件，一个plist文件和一个json文件。\n如何进行上面的导入操作？可以看这个视频：flash插件 DragonBone Design Panel导出以及CocoStudio动画编辑器的导入\n使用CocoStudio的Action编辑器导出的格式有什么优势呢？\nDragonBones输出的png是碎图，而CocoStudio Action编辑器将碎图拼接成大图了； plist文件是png碎图拼接的metadata； json文件是骨骼和动画信息。 但是，使用CocoStudio Action编辑器有几个问题：\n莫名其妙的崩溃。在导入的DragonBones文件中包含空格、中文等内容的时候，编辑器经常直接退出，让人较难判断原因； 不支持DragonBones的层级关系。如果在DragonBones中设计了层级关系，那么CocoStudio无法识别，骨骼会发生很大的错位； CocoStudio Action编辑器不稳定。我在0.3.0.0版本中，可以导入DragonBones格式，但使用0.3.2.0，又不能导入同样的格式了，编辑器直接退出。 为什么不用CocoStudio 既然导入有这样那样的问题，那么直接用CocoStudio做骨骼动画好了，干吗还要用DragonBones Design Panel？\nDragonBones Design Panel有如下优势：\nDragonBones的骨骼动画实现起来非常非常容易，在时间轴上放好影片剪辑就行了； 大多数做动画的同学都熟悉Flash，但极少知道CocoStudio，谁都愿意用自己熟悉的软件； Flash和DragonBones Design Panel的操作体验优于CocoStudio太多。 加上上面提到的不稳定原因，我也无法说服自己使用CocoStudio Action编辑器，更别说把它交给美术MM了。\n更何况，我们根本不必把DragonBones Design Panel生成的文件导入CocoStudio！cocos2d-x能直接支持DragonBones Desian Panel生成的文件！\n如果你还是希望用CocoStudo来做骨骼动画，可以参考这篇文章：使用 CocoStudio 创建 Cocos2d-x 序列帧和骨骼动画。\n2013-10-11 update CocoStudio升级到1.0之后，已经支持导入fla文件，但这个导入是有限制的：\n需要将 想要导入到动画编辑器的元件放入到主场景中 暂时不支持导入矢量图形，可以右键矢量图形 装换为位图 暂不支持嵌套元件的元件作为骨骼部件 这篇帖子详细介绍了这个限制 ，也有允许导入的文件范例。\n生成cocos2d-x支持的文件格式 上面已经提到，使用DragonBones可以生成一堆碎图文件和一个xml文件。我们首先要做的，就是把这堆碎图文件拼成一张大图。cocos2d-x支持plist格式(基于XML)的元数据。\n当然，如果你使用我修改过的 cocos2d-x专用的DragonBones2.2， 就不必进行下面的拼合工作，它会自动帮你生成png/xml/plist文件。\n你也可以选择 Sprite Packer(免费) 或者 Texture Packer 来做拼合碎图以及生成plist文件的工作。Sprite Sheet Editor 正在准备支持cocos2d的plist文件格式。\n拼合成功后，可以将碎图删除，现在我们有3个文件（为了方便描述，这里假设主文件名均为skeleton）：\n拼合后的大的png文件 skeleton.png； plist元数据文件 skeleton.plist； xml骨骼动画数据文件 skeleton.xml。 再次强调，这里让主文件相同只是为了方便描述，实际使用的时候，主文件不必相同。\n但是（为什么非要有但是呢？），你不认为文件名相同更方便人类阅读么？\n我先来说一下 skeleton.xml 的内部结构吧。下面是我用 DragonBones 官方提供的 Dragon.fla 生成的xml文件……呃……的一部分。\n:::xml \u0026lt;skeleton version=\u0026quot;2.1\u0026quot; frameRate=\u0026quot;24\u0026quot; name=\u0026quot;Dragon\u0026quot;\u0026gt; \u0026lt;armatures\u0026gt; \u0026lt;armature name=\u0026quot;Dragon\u0026quot;\u0026gt; \u0026lt;!-- 这里是一坨b 那啥~ b标签（表想不正……） --\u0026gt; \u0026lt;/armature\u0026gt; \u0026lt;/armatures\u0026gt; \u0026lt;animations\u0026gt; \u0026lt;animation name=\u0026quot;Dragon\u0026quot;\u0026gt; \u0026lt;!-- 这里是一坨mov --\u0026gt; \u0026lt;/animation\u0026gt; \u0026lt;/animations\u0026gt; \u0026lt;TextureAtlas name=\u0026quot;Dragon\u0026quot; height=\u0026quot;512\u0026quot; width=\u0026quot;512\u0026quot;\u0026gt; \u0026lt;!-- 这里是一坨SubTexture --\u0026gt; \u0026lt;/TextureAtlas\u0026gt; \u0026lt;/skeleton\u0026gt; 好了，1和2不必再检查了。但 skeleton.xml 必须检查。如果你不希望和我一样耗费一下午来猜谜的话，就记住下面几点吧：\n最新的测试标明，armatures是允许有多个子标签的，只要与animations中的子标签对应即可。armature标签只允许有一个。你生成的xml文件中，可能由于FLA制作的问题，在armatures下面有多个armature，这是绝对不行DI。cocos2d-x碰到这种情况会直接异常没商量。所以，留一个最终正确的吧！ armature和animation的name属性必须完全相同，这个名称将是cocos2d-x中最终使用的名称。 TextureAtlas的name属性和skeleton的name属性就无所谓啦，可以随便填了。 还是有必要再罗嗦一遍，各种name不要用中文，不要加空格，不要用特殊字符……grumble,grumble…… 话说，为什么生成的xml文件中会有多个armature呢？借势淫威……你的FLA库中的某个MovieClip中的第一层中包含label！\n我们知道（我不知道你知不知道，你知道你就是我们知道中的我们，你不知道你就不是我们知道中的我们），DragonBones会将MovieClip第一层的label当作骨骼动画中的不同动作。如果你某个不开眼的MovieClip中莫名其妙的加了一个不知所谓的label，而且你这个MovieClip又被制作动画的那个主MovieClip使用了，那么这个带有label的MovieClip也会被作为armature输出。\n使用CCArmature包实现骨骼动画 终于特码嘚进入代码阶段了，我快要累死了。\nCCArmature并不是cocos2d-x核心包的内容，你可以在 cocos2d-x/extensions 中找到它。\n在头文件中，需要include CCArmature包的所有内容。我不明白为什么 cocos2d-x 开发组不把这些包含文件都放到 cocos-ext.h 中去。毕竟 spine 都被放进去了啊。难道是不稳定？不敢再往下想了，一定有阴谋，借势个阴谋……\n:::c++ #include \u0026quot;cocos2d.h\u0026quot; #include \u0026quot;cocos-ext.h\u0026quot; #include \u0026quot;VisibleRect.h\u0026quot; #include \u0026quot;CCArmature/CCArmature.h\u0026quot; #include \u0026quot;CCArmature/CCBone.h\u0026quot; #include \u0026quot;CCArmature/animation/CCArmatureAnimation.h\u0026quot; #include \u0026quot;CCArmature/datas/CCDatas.h\u0026quot; #include \u0026quot;CCArmature/display/CCBatchNode.h\u0026quot; #include \u0026quot;CCArmature/display/CCDecorativeDisplay.h\u0026quot; #include \u0026quot;CCArmature/display/CCDisplayManager.h\u0026quot; #include \u0026quot;CCArmature/display/CCSkin.h\u0026quot; #include \u0026quot;CCArmature/physics/CCColliderDetector.h\u0026quot; #include \u0026quot;CCArmature/physics/CCPhysicsWorld.h\u0026quot; #include \u0026quot;CCArmature/utils/CCArmatureDataManager.h\u0026quot; #include \u0026quot;CCArmature/utils/CCConstValue.h\u0026quot; #include \u0026quot;CCArmature/utils/CCDataReaderHelper.h\u0026quot; #include \u0026quot;CCArmature/utils/CCTweenFunction.h\u0026quot; #include \u0026quot;CCArmature/external_tool/sigslot.h\u0026quot; 2013-10-12 update 现在阴谋终于展现了，原来cocos2d-x 2.2.0 把 CCArmature 整合进入了 extensions/CocoStudio 包。下面的内容出现在 cocos-ext.h 中：\n:::c++ #include \u0026quot;CocoStudio/Armature/CCArmature.h\u0026quot; #include \u0026quot;CocoStudio/Armature/CCBone.h\u0026quot; #include \u0026quot;CocoStudio/Armature/animation/CCArmatureAnimation.h\u0026quot; #include \u0026quot;CocoStudio/Armature/datas/CCDatas.h\u0026quot; #include \u0026quot;CocoStudio/Armature/display/CCBatchNode.h\u0026quot; #include \u0026quot;CocoStudio/Armature/display/CCDecorativeDisplay.h\u0026quot; #include \u0026quot;CocoStudio/Armature/display/CCDisplayManager.h\u0026quot; #include \u0026quot;CocoStudio/Armature/display/CCSkin.h\u0026quot; #include \u0026quot;CocoStudio/Armature/physics/CCColliderDetector.h\u0026quot; #include \u0026quot;CocoStudio/Armature/utils/CCArmatureDataManager.h\u0026quot; #include \u0026quot;CocoStudio/Armature/utils/CCDataReaderHelper.h\u0026quot; #include \u0026quot;CocoStudio/Armature/utils/CCTweenFunction.h\u0026quot; #include \u0026quot;CocoStudio/Armature/external_tool/sigslot.h\u0026quot; 那么，如果你使用cocos2d-x 2.2.0（或以上），只需要 #include \u0026quot;cocos-ext.h\u0026quot; 即可了。\n然后：\n载入资源、创建动画、播放第一个动画。 VisibleRect这个类可以在 TestCpp 范例中找到。 我写得简单，是因为我只说重点和易错的地方。 埋怨我写的简单的，可以直接查看 TestCpp/ExtensionsTest/ArmatureTest 范例，那个详细得令人发指。 2013-10-12 update 在 cocos2d-x 2.2.0 中，应该查看 ExtensionsTest/CocoStudioArmatureTest 范例。\n:::c++ CCArmatureDataManager::sharedArmatureDataManager()-\u0026gt;addArmatureFileInfo(\u0026quot;skeleton.png\u0026quot;, \u0026quot;skeleton.plist\u0026quot;, \u0026quot;skeleton.xml\u0026quot;); CCArmature* __armature = CCArmature::create(\u0026quot;Dragon\u0026quot;); __armature-\u0026gt;getAnimation()-\u0026gt;playByIndex(0); __armature-\u0026gt;setPosition(VisibleRect::center().x, VisibleRect::center().y*0.3f); addChild(__armature) Cocos2d-x 2.2.0 在 CCArmature 上的BUG 比较 Cocos2d-x 2.1.5 和 Cocos2d-2.2.0，它们的CCArmature播放效果不同。\n原本在2.1.5上非常流畅的Armature的骨骼动画，在2.2.0上会出现卡顿，以及动作不协调等情况。这不是帧率的问题，应该是在修改的过程中，Armature的Tween出了问题。\n我分析出现这个BUG的原因应该是这样的：\nCCArmature其实是基于DragonBones2.1的库移植而来，就是用C++把ActionScript3代码库重写了一遍； 现在触控传媒希望大力推进CocoStudio的使用（目前是lite版，可能以后会出收费版），让CocoStudio去支持CCArmature骨骼动画，因此不断对CCArmature进行改进以支持CocoStudio生成的JSON文件格式； 在改进的过程中，可能因为某些失误，导致对原来的DragonBones格式的支持出现了问题； 由于这次改进仅针对CocoStudio导出的JSON格式，而没有测试DragonBones生成的格式，导致这个BUG在2.2.0发布的时候也没有得到解决； cocos2d-x在各种大会上说支持Flash，让Flash成为cocos2d-x的编辑器，其实团队内并不是真正重视Flash，或者并没有安排Flash的相关测试； 对于Cocos2d-x的使用者来说，深入了解Flash的可能比较少，更少人知道CCArmature其实是来自DragonBones，使用DragonBones的就少得可怜。再加上网上的资源匮乏，旧的、被转帖的教程泛滥，描述正确且准确的教程缺失，这个BUG就很难发现； 以上全是我的猜测。\n但即使是官方的TestCPP，如果测试DragonBones Design Panel导出的Dragon那个Armature动画，也是能看出问题的。下面是动画，请注意尾部顶端、右臂以及腿的动作区别（这两个gif文件很大，要稍微等一会儿才会播放流畅）：\nFor v2.2.0 testcpp/ExtensionsTest/CocoStudioArmatureTest\nFor v2.1.5 testcpp/ExtensionsTest/ArmatureTest\n这个BUG我已经在 Cocos2d-x官网提出 bug#2887 ，不过开发团队似乎只测试了CocoStudio导出的格式情况，并且认为并无问题。我补发了TestCpp的测试视频，官方并未回复。\n2013-10-15 update 根据开发团队的最新回复，此BUG已经被确认，并指定目标版本为3.0alpha-1。看来要等一段时间了。在官方解决这个BUG之前，我只能使用2.1.5版本。 2013-11-19 update 开发团队不认为这是个bug，而是认为DragonBones官方提供的fla demo有问题，必须按照他们要求的格式修改，才可以被2.2.0支持。说实话，辗转这么长时间，花精力提供资料和截图，却得到这样的回复我挺无语的。不过目前这个bug已经关闭，显然是开发团队认为这就是正确的解决之道。 关于开发团队对于CCArmature这个DragonBones移植版的态度，我不好多说什么。毕竟大家都是要吃饭的，把DragonBones移植过去，然后搞自己的一套东西免费开发者使用，本来也是为了开发者好。不过我应该是不会再用CocoSudio这个软件了，cocos2d-x也不会升级到2.2。\n[dbcpp]: https://blog.zengrong.net/post/2106.html) [quick]: https://blog.zengrong.net/post/2133.html\n","date":"2013-08-24","description":"","lastmod":"2013-08-24T16:41:23Z","slug":"using-ccarmature-in-cocos2d-x","tags":["cpp","cocos2d-x","dragonbones","skeletalanimation"],"title":"在cocos2d-x中使用CCArmature实现骨骼动画","url":"https://blog.zengrong.net/post/using-ccarmature-in-cocos2d-x/"},{"categories":["technology"],"content":"这是我在之乎上的一个回答，原文在 这里 。\n要回答这个问题，首先要知道，你学Flash干什么？\nFlash早已不是十几年前闪客们的玩具。现在的Flash，是 Flash Platform 。\n在前端，有AIR和Flash Player两大Runtime，覆盖Windows/OS X/Android/iOS这4个平台； IDE方面，有Flash CS/CC和Flash Builder（原来有Flash Catalyst，后来自杀了）； 服务端方面，和Flash关系比较大的主要是Flash Media Server，简称FMS； 框架方面，Flex被送人，这个不考虑也罢…… 语言方面，ActionScript 3，简称AS3，在 TIOBE排行榜上 一直位于30~40名上下，最高的一次冲到过20名，这里还有个榜单：http://lang-index.sourceforge.net/。 Flash能做什么呢？ 传统动画片制作（输出SWF）：用Flash CS/CC吧，这是它的老本行； 视频短片制作（输出传统视频格式，在视频网站播出）：用Flash CS/CC吧，可能要加点AE/Premiere； 互动网站制作：用Flash CS/CC吧，不过这个目前国内很少了； 小广告制作：用Flash CS/CC吧，不过这个慢慢也变少了； 弹窗广告制作：用Flash CS/CC/纯AS3吧，据说被拦截的情况果比JS好； 在线视频直播、点播（那啥房间，啥主播？你懂的）：用Flash+FMS，FMS买不起用Red5也行； 通用软件（FlashPlayer）：运行在FlashPlayer中的软件，一般是多媒体类的，例如图像编辑器 Online image editor pixlr free ，曾经非常著名的 Picnik（被google整合进入Google+），Photoshop的Online版本 Photoshop Online Tools ，甚至还有视频非线性编辑视频编辑软件；在开发上可以用Flash CS,CC/Flash Builder/Flex + AS3； 通用软件（AIR）：和上条一样，只是运行在AIR中。例如我写的 Sprite Sheet Editor ，还有不是我写的 马克鳗 - 设计稿标注、测量神器 ，这块软件非常多，也许你用了，但不知道它是AIR开发的。我前几天买绘图板，发现居然Wacom的驱动程序中所带的桌面小工具也是用AIR开发的； 网页游戏：用Flash Buider/AS3/Flash CS/CC都行； 手机游戏：用Flash Builder/FlashCS,CC/AS3/AIR，服务端可用FMS，买不起（我有病啊买它当服务端）的话就自己写； 企业应用（大家见过招行网银有段时间内嵌Flex开发的一个报表么）：Flash Builder+Flex，服务端嘛，一般是JAVA。 ……可能还有一些把，我没想到的 附送两篇姊妹篇 Actionscript,AS3,MXML,Flex,Flex Builder,Flash Builder,Flash,AIR,Flash Player之关系 怎样快速从其他语言转到Actionscript游戏开发 首先之后，我还是要问：\n你学Flash干什么？\n","date":"2013-08-24","description":"","lastmod":"2013-08-24T14:32:52Z","slug":"how_to_study_flash","tags":["air","flash","flashplayer","study"],"title":"怎样学习Flash？","url":"https://blog.zengrong.net/post/how_to_study_flash/"},{"categories":[],"content":"Platform ANEs\n我讨厌ANE，更讨厌国内那些要要死不活还非要你接SDK的三流平台。\n在ANE上，Rect 花了很多精力。于是我撺掇他把他写过的ANE都分享出来，于是他就分享了 :-)\n准确的说，我只是建立了Github帐号，找朋友设计了图标，其他的活儿都是他干的。\n让我们感谢 Rect 的无私和奉献吧！Rect万岁！！！ Amen~\nBTW: Rect还有不少私货！！！\n已加入的ANE 优化版UC android版 ANE 奇虎360 android版ANE 小米android版ANE 瓦力android版ANE 当乐android版ANE 飞流android版ANE 豆玩android版ANE 奇天乐地android版ANE 百度android版ANE（百度账号登录） 新浪微薄android版ANE（新浪微薄账号登录） 中国移动MM内支付android测试版（打包方式略复杂） 机锋android版支付ANE （2013-7-28 update） …… 资源地址 项目地址：https://github.com/platformanes Rect 写的ANE教程：http://www.shadowkong.com/archives/1090 另一个通用的ANE源码免费送： ANE Toolkit ","date":"2013-08-22","description":"","lastmod":"2013-08-22T15:49:29Z","slug":"platform-anes","tags":[],"title":"Platform ANEs","url":"https://blog.zengrong.net/platform-anes/"},{"categories":["technology"],"content":"2013-08-21：v0.8.2版发布\n增加拖放功能，可以直接从资源管理器中拖动图像文件到编辑器中，规则如下： 在开始界面中，根据文件扩展名和有无metadata文件，进入不同的界面（SWF、PIc或SS）； 在PIC界面中，将文件作为图像文件对待，加入PIC列表； 在SS页面中，将文件作为图像文件或SpriteSheet对待，加入SpriteSheet列表； 解决导入图像列表会出现重复的BUG； 重写TransformTool； 允许在SWF预览界面中移动SWF内容，以方便舞台对位有问题的SWF动画截取； 合并了SS界面中的“增加图像帧”和“增加SpriteSheet”。 关于“允许在SWF预览界面中移动SWF内容”，看下面两张图：\n使用鼠标拖动即可实现移动功能\n更多的功能介绍以及软件下载，看这里。\n","date":"2013-08-21","description":"","lastmod":"2013-08-21T08:07:21Z","slug":"spritesheeteditor-0-8-2","tags":["air","spritesheet"],"title":"SpriteSheetEditor 0.8.2 发布","url":"https://blog.zengrong.net/post/spritesheeteditor-0-8-2/"},{"categories":["technology"],"content":"修改Visual Studio Express 2012新建C++文件编码\n我突然发现Visual Studio Express 2012（后简称为VS）建立的源文件是cp936（GB2312）编码，这个以后在Linux和Mac下面编译起来可能会有麻烦。\n初步解决 于是，我尝试将已有项目中的文件改成了UTF-8格式，并设置保存格式为UTF-8，大概有这样几步：\n“工具-选项-环境-文档-不能以代码页的编码格式保存数据时将文档保存为Unicode” “工具-选项-文本编辑器-常规-自动检测不带签名的UTF-8编码” 在文件打开的情况下，选择“文件-高级保存选项”，设置编码和行尾 在另存文件的时候选择“编码保存” 请原谅我使用中文描述界面，因为当时安装VS的时候手贱选了简中界面，然后无论如何都没法安装英文语言包……\n上面的1、2两项好像没什么用，关键是第3项。\n根据我的习惯，将编码设置为“Unicode(UTF-8无签名) - 代码页 65001”，将行尾设置为“Unix(LF)”。\n然后测试保存，再用Vim打开文件查看编码，发现文件确实保存为UTF-8编码了。\n但是（什么事情都有但是），当我重新打开这个文件的时候，重新查看“高级保存选项”，发现编码又变成了“简体中文（GB2312）-代码页936”。\n届时维尼奥神马！！！\n第二步解决 我猜测，是由于VS无法识别不带签名的UTF-8编码文件造成的。\n要将一个文本文件被作为UTF-8编码对待，可能需要以下几个条件（或者之一）：\n文件中包含UTF-8签名； 文件中包含UTF-8编码的字符（不能全是ASCII可以解释的字符）； 编辑器优先以UTF-8编码来解析。 例如我对Vim的设置，就是将UTF-8侦测放在优先的位置。\n那么，让我来验证一下猜测是否正确。\n在“高级保存选项”中将编码设置为“Unicode(UTF-8带签名) - 代码页 65001”，然后保存。重新打开，编码没有变化。\n这说明我的猜测是正确的。\n那么，再来验证一下上面条件的第2项是否正确。\n在“高级保存选项”中将编码设置为“Unicode(UTF-8无签名) - 代码页 65001”，加入一些中文内容，然后保存。重新打开，编码没有变化。\n那么，现在我可以断定，VS可以正常识别UTF-8编码，但我需要满足上面的条件1或者2。\n需要注意的是，即使是已有的项目中，所有文件都已经是UTF-8编码（例如cocos2d-x的项目模版），但使用VS打开并进行保存后，原来的文件编码都会变成CP936。这是因为原来的模版文件中的文件是UTF-8（无签名）的。至于解决方法，参考上面了。\n新建C++文件的编码 修改下面这几个模版文件的编码和行尾值，在VS中新建的cpp或者h文件，默认就是UTF-8编码了（路径自己改）：\nc:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\VCProjectItems_WDExpress\\newc++file.cpp c:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\VCProjectItems_WDExpress\\hfile.h c:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\VCNewItems_WDExpress\\newc++file.cpp c:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\VCNewItems_WDExpress\\hfile.h 这些文件默认都是0字节的空文件，因此建议在其中加入一些注释，并将文件编码设置为UTF-8(带签名)，行尾设置为Unix格式即可。\n如果希望修改更多的东西，比如wizard之类的，可以看看这篇文章：VS2008中自定义C++工程模板与修改新建文件默认编码的办法\n","date":"2013-08-15","description":"","lastmod":"2013-08-15T02:38:17Z","slug":"modify_default_cpp_file_encoding_in_vsexpress_2012","tags":["cpp","cocos2d-x"],"title":"修改Visual Studio Express 2012新建C++文件编码","url":"https://blog.zengrong.net/post/modify_default_cpp_file_encoding_in_vsexpress_2012/"},{"categories":["technology"],"content":"在Visual Studio Express 2012中创建一个cocos2d-x的win32项目\n好吧，这个月快忙死了，各种心理上和生理上的创伤以及无休止的工作让人疲惫，不过还好我没死掉。\n那么继续开始写文吧，Cocos2d-X实在太有趣了。\n下面的内容基于 Visual Studio Express 2012 和 cocos2d-x 2.1.4。\n看完了Cocos2d-x的几个范例之后，我准备自己创建一个玩一下，可是失败。\n大致的流程是这样的：\n创建一个空的C++项目； 设置项目的C/C++属性，将先前编译出的libcocos2d.dll等文件所在目录添加到“附加包含目录”； 设置项目的“链接器”属性，将上面的目录再添加到“附加依赖项”； 设置项目的“链接器-输入”属性，加入libcocos2d.lib等文件； 查看HelloCpp项目，将其中包含的头文件也加入到“附加包含目录”中。 如果添加的目录过多，可以先自定义一个路径宏，在添加的时候会方便点，定义的方法可以看这里： 用户定义的宏 。\n但这样做，是无法成功编译的。\n于是，我找到Cocos2d-x官方wiki的文章，准备使用winzard的方式来创建一个项目： Cocos2d-x Application Wizard for Visual Studio User Guide\n可是，第一步执行 Build-win32.bat 的时候，就失败了，报错如下：\nSolution file error MSB5009: Error parsing the nested project section in solution file. 有网友说用VS Express 2012重新打开 cocos2d-win32.vc2012.sln 文件再保存，就不会出错 。我照了……然后……确实没错了！\n于是开始编译，但编译完毕之后，出现了下面的提示：\n599 File(s) copied 13 File(s) copied 15 File(s) copied 109 File(s) copied 18 File(s) copied 12 File(s) copied 586 File(s) copied 71 File(s) copied File not found - WatermelonWithMe 0 File(s) copied File not found - Published files iOS 0 File(s) copied Can't find the binary \u0026quot;TestCpp.exe\u0026quot;, is there build error? 是的，也许我的TestCpp项目有问题，导致了无法编译，放下吧。\n于是（第二个于是）我开始测试 install-templates-msvc.bat ，给我的VS装个模版总可以吧……\n可这个也失败：\nInput Error: Can not find script file \u0026quot;D:\\cocos2dx\\2.1.4\\template\\msvc\\InstallWizardForVS2012Express.js\u0026quot;. 翻开bat的源码，再看看 template 目录，根本就没有这个js文件嘛！又有网友说了，以前是有的，现在真没有 。\n于是（第三个了），我找到这篇：How to create a multi-platform project in one command line ，项目终于创建成功！\n而且，这是个多平台项目，当然，无关的项目直接删掉就好了。\n于是（还有完没完！！），于是就没有于是了……\n","date":"2013-08-12","description":"","lastmod":"2013-08-12T12:34:00Z","slug":"build_a_cocos2d-x_project_in_visual_studio_express_2012","tags":["cpp","cocos2d-x"],"title":"在Visual Studio Express 2012中创建一个cocos2d-x的win32项目","url":"https://blog.zengrong.net/post/build_a_cocos2d-x_project_in_visual_studio_express_2012/"},{"categories":["technology"],"content":"注意，这篇文章并非基于任何移动设备，而是完完全全基于Windows，面向Desktop调试。\n一、Cocos2d-X 跨平台开发工作流 根据这几天的学习，看起来Cocos2d-X跨平台开发的工作流是这样的： 使用OS X操作系统； 在XCode上开发项目； 在iOS设备或者iOS模拟器上调试； 在Android设备上调试； 重复上面的过程。 当然，对于一个讨厌在Apple那套审美疲劳的桌面环境下工作的人来说，工作流可以是这样的： 使用Windows或者Linux操作系统； 在Eclipse上开发Android项目； 在Android设备或者Android模拟器上调试； 使用OS X操作系统； 在iOS设备或者iOS模拟器上调试； 重复上面的过程。 我找了一些关于Eclipse+NDK调试C++代码的文章，发现好像有一点点麻烦。所以我或许可以借用MS Visual Studio来基于Desktop调试C++代码，正常后再去Eclipse调试？\n当然，我依然可以用Eclipse作为主IDE来基于Desktop调试C++代码，然后在合适的时候调用NDK来调试。\n无论使用VS还是用Eclipse来作为主要的IDE，都能够大幅降低在移动设备或者移动设备模拟器上花费的调试时间。当然，与移动设备紧密联系的功能（例如重力感应，多点触摸），还是必须在设备上调试的。\n既然cocos2d-x提供了win32的sample，那么还是用VS2012来试试把。就让我这个从没用过VS的人来尝尝这个神级IDE的味道。\n于是，我的工作流可能就变成了这样（只是可能）： 使用Windows操作系统； 在VS2012上开发Desktop项目； 在Eclipse中建立Android项目，引用VS2012项目中的C++源码； 在Android设备或者Android模拟器上调试； 使用OS X操作系统； 在XCode中建立iOS项目，引用VS2012项目中的C++源码； 在iOs设备或者iOS模拟器上调试； 重复上面的过程。 那么，这就开始吧！\n二、我的环境 Cocos2d-x-2.1.4 安装路径：D:\\cocos2d-x\\cocos2d-x-2.1.4 Visual Studio 2012 Express （这个版本是免费的，直接在微软网站注册就可以得到注册码） 三、 调试流程 1. 项目文件\u0026quot;\u0026quot;已被重命名或者不在解决方案中 用VS打开 D:\\cocos2d-x\\cocos2d-x-2.1.4\\samples\\Cpp\\HelloCpp\\proj.win32 项目之后，试图编译，就出现了这个错误。\n原因是由于原始项目引用的某些项目不存在，主要是引用的 libcocos2d 和 libCocosDenshion 两个库不存在。删掉它们就不会报错了，可以在下图所示的地方找到它们：\n2. 编译libcocos2d库 当然，上面的项目依然是无法编译的，因为Cocos2d-X库还没有编译。\n使用VS2012打开 D:\\cocos2d-x\\cocos2d-x-2.1.4\\cocos2dx\\proj.win32 项目，然后生成它。\n下面是部分生成过程中的输出信息，视电脑配置情况时间应该从几分钟到十几分钟不等。\n1\u0026gt;------ 已启动生成: 项目: libcocos2d, 配置: Debug Win32 ------ 1\u0026gt; cocos2d.cpp 1\u0026gt; CCScheduler.cpp 1\u0026gt; CCDirector.cpp 1\u0026gt; CCConfiguration.cpp 1\u0026gt; CCCamera.cpp 1\u0026gt; CCScriptSupport.cpp 1\u0026gt; CCTextFieldTTF.cpp 1\u0026gt; CCIMEDispatcher.cpp 1\u0026gt; CCKeypadDispatcher.cpp 1\u0026gt; CCKeypadDelegate.cpp 1\u0026gt; CCTouchHandler.cpp 1\u0026gt; CCTouchDispatcher.cpp 1\u0026gt; CCTouch.cpp 1\u0026gt; CCTMXXMLParser.cpp 1\u0026gt; CCTMXTiledMap.cpp 1\u0026gt; CCTMXObjectGroup.cpp 1\u0026gt; CCTMXLayer.cpp 1\u0026gt; CCTileMapAtlas.cpp 1\u0026gt; CCParallaxNode.cpp 1\u0026gt; CCTexturePVR.cpp 1\u0026gt; 正在生成代码... 1\u0026gt; 正在编译... ………… 1\u0026gt; cocos2d.vcxproj -\u0026gt; D:\\cocos2d-x\\cocos2d-x-2.1.4\\cocos2dx\\proj.win32\\Debug.win32\\libcocos2d.dll ========== 生成: 成功 1 个，失败 0 个，最新 0 个，跳过 0 个 ========== 生成完毕之后，D:\\cocos2d-x\\cocos2d-x-2.1.4\\cocos2dx\\proj.win32\\Debug.win32 目录下会出现一堆dll、lib、obj文件。\nVS的确是赞啊……居然都不用配置，而且看一眼就会用了……\n3. 编译libCocosDension库 直接把 D:\\cocos2d-x\\cocos2d-x-2.1.4\\CocosDenshion\\proj.win32 这个项目添加到刚才 libcocos2d 解决方案，然后再生成 libCocosDenshion 库，在默认情况下，生成的文件也会集中在 D:\\cocos2d-x\\cocos2d-x-2.1.4\\cocos2dx\\proj.win32\\Debug.win32 目录中。\n当然，也可以不将这两个项目放在同一个解决方案中。但这样就需要把libcocos2d生成的dll和lib文件复制到libCocosDenshion的Debug.win32目录，否则会导致libCocosDenshion无法成功编译。\n4. 编译HelloCpp项目 用上面同样的方法将HelloCpp项目也加入到libcocos2d解决方案。然后设置依赖项和生成顺序：\n将HelloCpp项目设置成启动项目（右键即可搞定）：\n有兴趣的话，还可以单独设置一下HelloCpp项目的输出路径，否则也会生成到 D:\\cocos2d-x\\cocos2d-x-2.1.4\\cocos2dx\\proj.win32\\Debug.win32 目录中。\n按下“本地Windows调试器”按钮，结果就是酱紫了：\n不过不知道为何，我使用Release生成的exe文件不能直接双击显示，但我已经很满足了。至少我第一次使用VS的经历，还是蛮圆满的 :)\n","date":"2013-06-26","description":"","lastmod":"2013-06-26T16:30:41Z","slug":"cocos2d-xvs2012-hellocpp-debug","tags":["cpp","cocos2d-x"],"title":"Cocos2d-x VS2012 HelloCpp调试","url":"https://blog.zengrong.net/post/cocos2d-xvs2012-hellocpp-debug/"},{"categories":["technology"],"content":"Cocos2d-x在Windows下配置起来确实问题较多，但我找到的几篇文章都很给力，虽然花费了一些力气，但总算成功了。\n我的环境 Cocos2d-x-2.1.4 android-ndk-r8e-windows-x86_64 JDK 1.7 eclipse 4.2.0 with CDT/ADT 参考文章 这篇文章基于cygwin来配置，文章非常详细，熟悉cygwin的人基本上可以一次成功：\nHow to set up the Android Cocos2d-x development environment on Windows 7\n这篇文章基于NDK来配置，不需要cygwin支持（下文简称为ndk only）。文章很详细，但有些细节没有说得太清楚，对于不熟悉eclipse的人来说可能会比较纠结。另外，该文较老，其中关于JDK必须使用1.6版本的说法已经过时：\n让人死去活来的cocos2d-x安卓开发环境搭建\n易出错点 由于上面两篇文章都很不错，我这里就不再复述过程，只说一下我在配置过程中碰到的几个纠结的地方。配置成功以能运行HelloCpp/proj.android项目为准。\n要看下面的内容，必须先看完上面两篇文章，否则等于白看。\n1. Library错误 导入HelloCpp项目后，在 “项目属性-Java Build Path-source” 中加入cocos2d-x-2.1.4\\cocos2dx\\platform\\android\\java 路径。然后进入“项目属性-Android-Library”，删除这里的“Reference Project”，否则怎么编译都会报错。cygwin环境和ndk only环境均会出现。切记切记！\n2. NDK_MODULE_PATH 环境变量配置 具体的路径可以查看 cocos2d-x-2.1.4\\samples\\Cpp\\HelloCpp\\proj.android\\build_native.sh 的最后几行。cygwin环境不需要设置这个变量，直接执行 build_native.sh 即可。\n这个环境变量可以配置在Windows系统环境变量中，也可以配置在Eclipse项目的builder属性中，还可以直接配置在 project/jni/Application.mk 文件中。\n若配置在 Application.mk 文件中，可以这样写： NDK_MODULE_PATH:=cocos2d-x-2.1.4;cocos2d-x-2.1.4\\cocos2dx\\platform\\third_party\\android\\prebuilt\n3. Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1) apk打包成功，安装执行后出现这个错误退出。这一般是由于C++编译失败造成。对于cygwin环境，再执行 build_native.sh 编译一次即可成功；对于ndk only环境，也需要重新执行一次编译（如果没有配置自动刷新，那么可以手动选择Run-build project）。\n4. 建立自定义builder的问题 死去活来 和网上其他文章都提到为了不依赖cygwin，需要在 “项目属性-Builders”中建立一个新的buildr，调用NDK提供的 ndk-build.cmd来编译。这样做有2个问题：\n没有强调 Builder 的编译顺序。默认新建的Builder是处于所有已有Builder的末尾，这可能会导致 Android Package Builder 等 Builder依赖错误； 默认存在的 “CDT Builder” 依然是调用 bash build_native.sh 来编译，若没有安装cygwin，可能会出现下面的错误： bash D:/cocos2d-x/cocos2d-x-2.1.4/samples/Cpp/HelloCpp/proj.android/build_native.sh clean Cannot run program \u0026quot;bash\u0026quot;: Launching failed Error: Program \u0026quot;bash\u0026quot; not found in PATH\n我认为正确的做法是，不必新建一个Builder，而是调整现有的Builder的属性：\n调整 “项目属性-C/C++ Builder” 选项，将 “Build command” 的值从原来的 bash ${ProjDirPath}/build_native.sh 设置为 x:\\android-ndk-r8e\\ndk-build.cmd； 调整 “项目属性-C/C++ builder-Environment” 选项，加入 NDK_MODULE_PATH 环境变量，其值与上面介绍的相同。 ","date":"2013-06-24","description":"","lastmod":"2013-06-24T13:37:49Z","slug":"memo-of-cocos2d-x-windows-7-configuration","tags":["cpp","cocos2d-x"],"title":"Cocos2d-x Windows 7配置备忘","url":"https://blog.zengrong.net/post/memo-of-cocos2d-x-windows-7-configuration/"},{"categories":["technology"],"content":"2013-06-18更新： 解决图像质量选择的bug，请重新下载。\n2013-06-14：v0.8.1版发布\n增加保存为Starling格式的SpriteSheet，但不支持打开Starling； 调整程序结构以支持更多格式； 在“编辑SpriteSheet界面”，直接单击大图选择对应的帧，左侧帧列表会自动滚动到该帧； 在“编辑SpriteSheet界面”，可以使用Shift实现连续多选，使用Ctrl/Control键实现不连续多选。 更多的功能介绍以及软件下载，看 这里 。\n","date":"2013-06-14","description":"","lastmod":"2013-06-14T03:25:39Z","slug":"spritesheeteditor-0-8-1-release","tags":["air","spritesheet"],"title":"SpriteSheetEditor 0.8.1 发布","url":"https://blog.zengrong.net/post/spritesheeteditor-0-8-1-release/"},{"categories":["technology"],"content":"Robotlegs2中文教程-1使用MVCBundle\n本系列全部文章：using robotlegs2\n目的 本章使用Robotlegs2自带的MVCBundle实现一个简单的MVC实例。\nRobotlegs2在架构设计上，框架仅实现了生命周期管理、Logger、消息调度、插件管理器、配置管理器等核心功能，其他功能全部使用插件实现。而MVCBundle，就是Robotlegs2提供的一个插件和配置集合，这个集合包含所有MVC需要的插件和功能。\n本章不会研究Robotlegs2在结构上的设计，而是从最终用户的角度来使用MVCBundle。若希望了解Robotlegs2的架构，请关注本系列后续文章。\n本章也不会详细介绍MVCBundle的所有用法，那样会导致本文篇幅过长。下一章会进行详细介绍。\nTimer-MVCBundle-Sample概述 Timer-MVCBundle-Sample是本章的实例名称。这个实例实现了一个简单的定时器。看看定时器的3个状态：\n初始状态，设置时、分，类名为TimerSetView。按Start按钮开始倒计时。 倒计时状态，类名为TimerActionView。按Cancel按钮取消倒计时，回到初始状态。 倒计时时间到，类名为AlertView。按Dismiss按钮回到初始状态。 项目依赖 本项目依赖 minimalcomps 组件库和 robotlegs 2.0.0b6。 本项目源码下载 项目结构 这里简单介绍一下项目中的包分类，以及各个部分的作用。介绍按照MVC分块进行。如果你性急，也可以跳过这部分直接看后面的项目详解。\nView + Mediator 刚才看到的后缀为View的类，我叫它们“视图类”，所有的视图类，都位于 view 包中，并实现 ITimerView 接口。 ITimerView 中并没有包含任何方法签名。实现这个接口，是为了方便进行类型匹配。\n视图类用于我们能看到的定时器的3个状态，每个状态对应一个视图。定时器运行的过程中所显示的状态，在这3个视图之间切换。上节 Timer-MCBundle-Sample 已经介绍了这3个状态以及对应的类名。\n视图类都是显示对象的子类，3个视图也就是3个显示对象。在一个视图显示的时候，它会被加到舞台上，其他的视图则移出舞台。\n在这个项目中，我使用了MinialComps组件。视图类继承其中的VBox或者HBox。3个视图类如下图：\n每个视图类都有一个带有Mediator后缀的类，这是视图类的中介类。中介类负责管理视图事件，接收和传递系统事件等等。所有的中介类都继承 Robotlegs2 提供的 Mediator 类。如下图：\nModel 此项目有2个Model：TimerModel 和 ViewModel。\nTimerModel 实现 ITimerModel 接口。由于项目过于简单，ITimerModel 中不包含任何需要具体实现的方法，但是这种编程习惯是Robotlegs所推荐的。它既方便了在Robotlegs中替换Model（例如你可以在开发和发布的时候使用不同的Model），也符合中的针对接口编程的OO原则。\nTimerModel 中包含了一个 flash.util.Timer 实例，用它来实现定时器的核心功能。同时它还会发布定时器 工作的事件，并暴露一些方法来停止和启动定时器。\nViewModel 保存上面提到的3个视图类的实例，以此实现对视图实例的重用。同时它还提供一个 getView() 方法根据类或者类名来获取一个新的/重用的实例。你可以把 ViewModel 理解成一个对象池，虽然它并不是对象池。如下图： Command + Event 此项目有3个Commnd： ChangeStateCmd 、 TimerStartCmd 和 TimerStopCmd 。\nChangeStateCmd 负责切换视图状态。它收到切换的命令，然后移除掉当前舞台上的视图实例，再从 ViewModel 中获取一个视图实例，将它加到舞台上。\nTimerStartCmd 负责用户主动开始定时器运行的事件。它只是简单的更新 TimerModel 中的定时器流逝时间，然后开启定时器。\nTimerStopCmd 负责用户主动停止定时器运行的事件。它停止 TimerModel 中的定时器，然后将视图切换到 TimerSetView。\nTEvent 是此项目中使用的系统事件类型。上面的几个Command均使用这个类型代表的事件来触发。如下图： 项目详解 初始化Robotlegs2 先来看看主类 Robotlegs2TimerExample 的全部内容（省略了package和import）：\n1[SWF(width=200,height=200)] 2public class Robotlegs2TimerExample extends Sprite 3{ 4\tpublic function Robotlegs2TimerExample() 5\t{ 6\tStyle.embedFonts = true; 7\tStyle.fontSize = 8; 8\tComponent.initStage(this.stage); 9\tinit(); 10\t} 11\t12\tprivate var _context:IContext; 13\t14\tprivate function init():void 15\t{ 16\t_context = new Context() 17\t.install(MVCSBundle) 18\t.configure(AppConfig) 19\t.configure(new ContextView(this)); 20\t21\t_context.logLevel = LogLevel.DEBUG; 22\ttrace(\u0026#34;init done\u0026#34;); 23\t24\t//这里通过内置的事件框架来实现View的启动 25\t(_context.injector.getInstance(IEventDispatcher) as IEventDispatcher).dispatchEvent(new TEvent(TEvent.CHANGE_STATE, TimerSetView)); 26\t} 27} Style和Component都是 minimalcomps 中的类，具体用法可参考该组件源码。需要注意的是，如果需要显示中文，那么需要将 Style.embedFonts 设置为 false，具体原因可参考 MinimalComps简介－一个超轻量级的纯AS组件库。\n让我们来看 init 方法的具体内容。Context 类是Robotlegs2初始化的核心。在本项目的初始化当中，我们使用了 Context 的 install 和 configuare 方法。这两个方法都会返回 Context 自身，因此我们可以进行链式调用。\n链式调用是JAVA和JavaScript中常用的调用方式，能减少代码量，让代码看起来更干净。当然，如果你不愿意用它，依然可以使用旧的方式。例如，上面的链式调用可以改成这样：\n1_context = new Context(); 2_content.install(MVCSBundle); 3_content.configure(AppConfig); 4_content.configure(new ContextView(this)); install() 的作用是安装一个 IExtension 或者 IBundle。前者是一个扩展，后者是一个/一堆扩展和配置的集合。Robotlegs2根据我们的调用对 IExtension 和 IBundle 进行按需安装，这样可以节省资源。install() 支持无限参数，如果有多个 IBundle，可以使用链式调用进行单个安装，也可以使用一个 install() 多个参数进行安装。\n注意\n为了描述的统一性和准确性，后文将不对 IExtension 和 IBundle 进行翻译，而直接使用接口名。\nMVCBundle 是一个包含许多 IExtension 的 IBundle，它包含了MVC框架中的所有 IExtension。除此以外，它还包含一些Logger系统等核心功能。在这个项目中，我们不需要其他的 IExtension ，一包足矣。\nconfiguare() 的作用是对项目进行配置，通常将各种映射放在这里。与 install() 只能接收 IBundle 和 IExtension 不同， configuare() 可以接受任何类型的对象作为参数。同时，它也接受多个参数。例如上面的 configuare() 相关代码可以写成这样：\n1_content.configure(AppConfig, new ContextView(this)); 需要注意的是，对于ContextView的配置，必须放在所有配置的最后。具体原因，请关注本系列后续文章。\nContextView 是Robotlegs2提供的类，它没有实现任何接口，只是用于保存根显示对象的引用。我们可以将它注入到需要的地方，以方便访问根显示对象。\nInjector init() 的最后一句直接发布 TEvent.CHANGE_STATE 事件，ChangeStateCmd 会处理这个事件，它获取 TimerSetView 的实例，将其添加到舞台上。\n注意这一句的作用：\n1(_context.injector.getInstance(IEventDispatcher) as IEventDispatcher).... 最外层的括号中的内容，是为了获取一个 IEventDispatcher 的实例。IEventDispatcher 是AS3原生的用于发布事件的接口。 Robotlegs2中使用它作为 MVCBundle 的事件核心。\n它以单例的形式存在，我们使用 injector.getInstance 方法，就可以得到这个单例。\ninjector是SwiftSuspenders提供的注入器。Robotlegs2使用这个注入器来实现注入，我们也可以使用它来获取Robotlegs2中注册过的各种资源。在 Context 中包含一个它的引用。我们既可以使用 _context.injector 的方式获取它，也可以使用注入的方式获取它。\n例如，我们要得到 TimerModel 的单例，可以这样做(注意我使用的是接口）：\n1[Inject] 2public var injector:Injector; 3 4//获取到ITimerModel的单例，调用它的start方法让计时器开始运行 5(injector.getInstance(ITimerModel) as ITimerModel).start(); 那么，既然 ITimerModel 已经在Robotlegs2中注册（这个注册是在AppConfig中完成的，后面会讲到），更简单的方法是这样的：\n1[Inject] 2public var timerModel:ITimerModel; 3 4timerModel.start(); 可是，我们为什么不在 Robotlegs2TimerSample 中直接注入 IEventDispatcher ，而要用 injector 来获取呢？例如像这样：\n1[Inject] 2public var eventDispatcher:IEventDispatcher; 3 4private function init():void 5{ 6\t................ 7\teventDispatcher.dispatchEvent(new TEvent(TEvent.CHANGE_STATE, TimerSetView)); 8} 这样的代码会出现运行时错误，原因是 eventDispatcher 的值为 null 。\n这是因为在默认情况下， 只有被注入器初始化的类，才能被注入 。主类 Robotlegs2TimerSample 是无法被注入器初始化的，因此在主类中进行注入，在默认情况下是不会成功的。\nAppConfig 让我们看看 AppConfig 的内容（同样省略了package和import）。\n1public class AppConfig implements IConfig 2{ 3\t[Inject] 4\tpublic var injector:Injector; 5\t6\t[Inject] 7\tpublic var mediatorMap:IMediatorMap; 8\t9\t[Inject] 10\tpublic var commandMap:IEventCommandMap; 11\t12\t[Inject] 13\tpublic var logger:ILogger; 14\t15\tpublic function configure():void 16\t{ 17\tmodels(); 18\tmediators(); 19\tcommands(); 20\tlogger.info(\u0026#34;logger in AppConfig\u0026#34;); 21\t} 22\t23\tprivate function models():void 24\t{ 25\tinjector.map(ITimerModel).toSingleton(TimerModel); 26\tinjector.map(ViewModel).asSingleton(); 27\t} 28\t29\tprivate function mediators():void 30\t{ 31\tmediatorMap.map(TimerSetView).toMediator(TimerSetMediator); 32\tmediatorMap.map(TimerActionView).toMediator(TimerActionMediator); 33\tmediatorMap.map(AlertView).toMediator(AlertViewMediator); 34\t} 35\t36\tprivate function commands():void 37\t{ 38\tcommandMap.map(TEvent.TIMER_START, TEvent).toCommand(TimerStartCmd); 39\tcommandMap.map(TEvent.TIMER_STOP, TEvent).toCommand(TimerStopCmd); 40\tcommandMap.map(TEvent.CHANGE_STATE, TEvent).toCommand(ChangeStateCmd); 41\t} 42} AppConfig 实现了 IConfig 接口。Robotlegs2在对该接口进行配置的时候，会自动调用它的 configuare 方法进行注册。\n我们来仔细看看这个类中注入的4个对象。\ninjector上面讲过一些，这里继续。使用它，可以进行单例映射。toSingleton() 方法和 asSingleton() 方法的不同之处在于，前者针对接口映射，我们可以方便在该方法的参数中替换具体实现；后者针对具体实现映射，它不能被替换。\n如果你用过Robotlegs1，你会发现injector的映射语法改变了不少。它抛弃了原来使用参数来映射的方式，改用链式调用，在我看来，这样的改动让语法更加简洁，且容易记忆。\n注意\n本系列文章在必要的时候会对Robotlegs1和2进行比较，这是为了方便使用过Robotlegs1的读者进行更深入的理解。如果你以前没有使用过Robotlegs1，可以跳过这些内容。\n例如，在Robotlegs1中，要实现本例中的两个单例映射，需要这样调用：\n1injector.mapSingletonOf(ITimerModel, TimerModel); 2injector.mapSingleton(ViewModel); mediatorMap用来实现视图类和Mediator的映射。以 mediatorMap.map(TimerSetView).toMediator(TimerSetMediator) 为例，这句实现了在 TimerSetView 被添加到舞台的时候， TimerSetMediator 会自动创建并完成初始化，同时进行需要的注入。\n在Robotlegs1中，mediatorMap的调用方式也做了与injector类似的修改。这并不奇怪，因为mediatorMap的映射就是使用injector实现的。\n例如，在Robotlegs1中，实现 TimerSetView 与 TimerSetMediator 的映射，需要这样调用：\n1mediatorMap.mapView(TimerActionView, TimerActionMediator); commandMap用来实现事件与Command的映射。以 commandMap.map(TEvent.TIMER_START, TEvent).toCommand(TimerStartCmd) 为例，这句实现了在 TEvent.TIMER_START 事件发生的时候， TimerStartCmd 被自动创建，同时进行需要的注入，然后执行其 execute 方法。\n在Robotlegs1中，要实现 TEvent.TIMER_START 与 TimerStartCmd 的映射，需要这样调用：\n1commandMap.mapEvent(TEvent.TIMER_START, TimerStartCmd); logger提供一个全局的日志分析器，默认使用tarce实现。logger不但可以像上面的代码一样，直接输出文字，也可以方便的实现文本替换。例如：\n1logger.info(\u0026#34;logger in {0}, {1}\u0026#34;, [this, \u0026#34;done\u0026#34;]); 2//输出内容 3//638 INFO Context-0-9f [class AppConfig] logger in [object AppConfig], done AppConfig实际上是分担了一部分Context的功能。我们可以把不同模块，不同需求的Config进行分离，让程序之间的耦合更加松散。\n在Robotlegs1中，我们一般把映射放在 Context 的 startup 方法中，这样在项目逐渐庞大的时候，Context就无可避免的庞大起来：\n1.......... 2//======================================== 3// 注入Model和Service 4//======================================== 5injector.mapSingletonOf(SocketServiceBase, SocketService); 6injector.mapSingleton(SocketDataDispatcherModel); 7injector.mapSingleton(StateModel); 8injector.mapSingleton(HTTPService2); 9injector.mapSingleton(UpdateService); 10 11//----界面信号 12signalCommandMap.mapSignalClass(FightEndSign, FightEndCmd); 13signalCommandMap.mapSignalClass(GuideAniEndSign, GuideAniEndCmd); 14signalCommandMap.mapSignalClass(GuideHelpSign, GuideHelpCmd); 15signalCommandMap.mapSignalClass(GuideCheckOpen8Sign, GuideCheckOpen8Cmd); 16signalCommandMap.mapSignalClass(CheckMausoleumSign, CheckMausoleumCmd); 17signalCommandMap.mapSignalClass(ExitMausoleumSign, ExitMausoleumCmd); 18 19//----其他不便分类的信号 20signalCommandMap.mapSignalClass(NecessarySocketInitDataDoneSign, NecessarySocketInitDataDoneCmd); 21signalCommandMap.mapSignalClass(SetDefaultPreferenceSign, SetDefaultPreferenceCmd); 22signalCommandMap.mapSignalClass(ActiveSign, ActiveCmd); 23signalCommandMap.mapSignalClass(ChargeInWebSign, ChargeInWebCmd); 24 25//======================================== 26// 所有的子滑动界面注册 27//======================================== 28//LoginModuleContent和RegisterModuleContent与接入商相关，因此在子Context类中注册 29mediatorMap.mapView(FightDeployModuleContent, FightDeployModuleContentMediator, null, false, false); 30mediatorMap.mapView(FightSpyModuleContent, FightSpyModuleContentMediator, null, false, false); 31mediatorMap.mapView(ChooseServerModuleContent, ChooseServerModuleContentMediator, null, false, false); 32mediatorMap.mapView(AccelerateCoolingModule,AccelerateCoolingModuleMediator,null,false,false); 33........ 运行流程 1 这段代码是项目的界面入口 1(_context.injector.getInstance(IEventDispatcher) as IEventDispatcher).dispatchEvent(new TEvent(TEvent.CHANGE_STATE, TimerSetView)); IEventDispatcher 发布了一个事件 TEvent.CHANGE_STATE，同时传递了一个视图类 TimerSetView。由于我们前面已经将该事件映射到了 ChangeStateCmd，因此，ChangeStateCmd 中的 execute() 方法被执行。\n2 ChangeStateCmd做了什么？ 让我们看看 ChangeStateCmd 的全部内容：\n1public class ChangeStateCmd extends Command 2{ 3\t[Inject] 4\tpublic var evt:TEvent; 5\t6\t[Inject] 7\tpublic var logger:ILogger; 8\t9\t[Inject] 10\tpublic var contextView:ContextView; 11\t12\t[Inject] 13\tpublic var viewModel:ViewModel; 14\t15\toverride public function execute():void 16\t{ 17\tlogger.debug(evt.info); 18\tlogger.debug(contextView.view.numChildren); 19\tif(contextView.view.numChildren \u0026gt; 0) 20\t{ 21\tvar __currentView:ITimerView = contextView.view.getChildAt(0) as ITimerView; 22\tif(__currentView) 23\t{ 24\tcontextView.view.removeChild(__currentView as DisplayObject); 25\t} 26\t} 27\tvar __newView:DisplayObject = viewModel.getView(evt.info) as DisplayObject; 28\tcenter(__newView); 29\tcontextView.view.addChild(__newView); 30\tlogger.debug(\u0026#34;get view:{0}\u0026#34;, [__newView]); 31\t} 32\t33\tprivate function center($view:DisplayObject):void 34\t{ 35\t$view.x = (contextView.view.stage.stageWidth - $view.width) / 2; 36\t$view.y = (contextView.view.stage.stageHeight - $view.height) / 2; 37\t} 38} 在 ChangeStateCmd 中，我们需要得到 TEvent 传来的视图类，使用其创建视图实例，然后将实例添加到舞台上。\n注入的 evt 实例，也就是前面 TEvent.CHANGE_STATE 事件传来的事件实例。在这次的调用这，evt.info 的值，就是 TimerSetView。\nexecute() 方法先移除已存在的视图实例，然后根据 evt.info 的值获取一个新的实例，并将其添加到舞台。\n所有的视图类，都实现了 ITimerView 接口。因此在这里可以通过判断 ITimerView 的实现情况，来了解实例的创建是否成功。\n创建实例的工作在 ViewModel 中完成，可查看源码了解，此处不深入讲解。\nTimerSetView 视图实例被添加到舞台的时候，TimerSetViewMediator 会自动创建。\n3 TimerSetViewMediator 做了什么？ 让我们来看看 TimerSetViewMediator 的全部内容：\n1public class TimerSetMediator extends Mediator 2{ 3\t[Inject] 4\tpublic var logger:ILogger; 5\t6\t[Inject] 7\tpublic var v:TimerSetView; 8\t9\tpublic function TimerSetMediator() 10\t{ 11\t} 12\t13\toverride public function initialize():void 14\t{ 15\tsuper.initialize(); 16\tlogger.info(\u0026#34;initialize\u0026#34;); 17\tv.startBtn.addEventListener(MouseEvent.CLICK, handler_start); 18\t} 19\t20\toverride public function destroy():void 21\t{ 22\tv.startBtn.removeEventListener(MouseEvent.CLICK, handler_start); 23\tsuper.destroy(); 24\tlogger.info(\u0026#34;destory\u0026#34;); 25\t} 26\t27\tprivate function handler_start(e:Event):void 28\t{ 29\tlogger.debug(\u0026#34;click\u0026#34;); 30\teventDispatcher.dispatchEvent(new TEvent(TEvent.TIMER_START, {minute:v.minute, second:v.second})); 31\teventDispatcher.dispatchEvent(new TEvent(TEvent.CHANGE_STATE, TimerActionView)); 32\t} 33} v 被自动注入，它就是刚才我们创建的 TimerSetView 视图实例。\ninitialize() 方法在注入完成之后自动执行。因此，该方法适合进行 v 的视图侦听器。在本类中，加入了用户按下“Start”按钮时的鼠标事件侦听。\nTimerSetView 的实例从舞台移除的时候， TimerSetViewMediator 被销毁。 destroy() 方法在此时执行。我们在这里移除“Start”按钮的鼠标事件侦听。\n在单击“Start”按钮的时候，我们发布了两个系统事件 —— 1. TEvent.TIMER_START，并传递当前选择的分、秒；2. TEvent.CHANGE_STATE，切换到 TimerActionView 视图。\n由于前面我们已经将 TEVent.TIMER_START 映射到了 TimerStartCmd ，因此 TimerStartCmd 中的 execute() 方法将被执行。\n4 启动计时器 让我们来看看 TimerStartCmd 的全部内容：\n1public class TimerStartCmd extends Command 2{ 3\t[Inject] 4\tpublic var timerModel:ITimerModel; 5\t6\t[Inject] 7\tpublic var evt:TEvent; 8\t9\toverride public function execute():void 10\t{ 11\ttimerModel.minute = evt.info.minute; 12\ttimerModel.second = evt.info.second; 13\ttimerModel.start(); 14\t} 15\t16} 在这里，我们更新了 TimerModel 中保存的分、秒，然后启动了计时器。\n5 请继续 到此，我已经把主要流程讲完。至于后面的计时器时间到，或者中断计时器等操作，和前面的流程类似。大家可以继续在 TimerModel 和 TimerActionMediator 中找到它们的具体实现。\n小结 本章使用一个简单的计时器例子，讲解了Robotlegs2中的MVCBundle的使用。拥有Robotlegs1使用经验的同学，现在应该已经可以转移到Robotlegs2上来了。\n即使你从没有用过Robotlegs，那么也可以从本文开始，从头学习Robotlegs2。\n下一章，我们将深入MVCBundle中，分析其中的Extension组成，并试图抛弃MVCBundle，使用更底层的方式来重新构架我们的计时器范例。\n","date":"2013-05-29","description":"","lastmod":"2013-05-29T03:16:05Z","slug":"use_robotlegs2_1timermvcbundle","tags":["as3","framework","robotlegs","usingrobotlegs2"],"title":"Robotlegs2中文教程-1使用MVCBundle","url":"https://blog.zengrong.net/post/use_robotlegs2_1timermvcbundle/"},{"categories":["technology"],"content":"Korean text isn't shown in Android 4.2.2\n环境 编译SDK：AIR 3.6/3.7 测试设备：Nexus 7(Android 4.2.2)/Samsung Note 2(Android 4.1.2)/Moto Atrix 2(Android 4.1.2) 测试模拟器：Android 4.2.2/4.1.2 现象 App中使用朝鲜语/韩文字符（UTF-8编码）的文本，在Android4.2.2系统上不能显示，模拟器和设备均出现这个问题; 不仅是App的显示有这个问题，App在Android系统App列表中的名称也无法显示； 无论设备语言是英文、朝鲜语/韩文还是中文，这个问题都会出现； 同样的App放到Android 4.1.2及以下系统中均表现正常； 使用中文字符（UTF-8编码）的文本，无论在Android 4.2还是其他系统上，均表现正常； 如果希望看现象截图，可以看这里：http://forums.adobe.com/message/5361043#5361043。 解决过程 我在 Android 项目的 issue 列表中发现了同样的问题 Korean font/locale unavailable in Android 4.2 ，但该问题没有得到正面答复。从截图上看，提出该问题的也是中国开发者。这是个巧合吗？ 我怀疑是AIR的问题，因为如果这是Android操作系统问题，那韩国的Android 4.2开发者怎么办？为什么我用英文搜索不到相关信息？我在 AIR Bugs and Performance Issues 上询问了这个问题； 2013-05-29，Adobe的员工Pahup回复我AIR 3.8 beta解决了这个问题。我在labs上看了一下，3.8 beta确实更新了，更新时间就是今天。但经过实际测试，这个问题依然存在。 2013-05-30，Pahup回复说需要设置字体属性，尝试之后，果然OK。 解决方法 首先确保使用AIR 3.8SDK。\n若使用Flex，可以通过设置 fontFamily 来支持Korean字符显示，例如：\n1\u0026lt;s:Label text=\u0026#34;고속도로\u0026#34; fontFamily=\u0026#34;NanumGothic\u0026#34;/\u0026gt; 若使用TextField，可以通过设置 TextFormat 的 font 属性来支持Korean字符显示， 例如：\n1var __label:TextField = new TextField(); 2var __ft:TextFormat = new TextFormat(); 3__ft.font= \u0026#34;NanumGothic\u0026#34;; 4__label.defaultTextFormat = __ft; 5__label.text = \u0026#34;고속도로\u0026#34;; 以上做法，只有AIR 3.8支持。因此，我还必须要等待AIR 3.8发布。\n","date":"2013-05-29","description":"","lastmod":"2013-05-29T01:25:40Z","slug":"korean-text-isnt-shown-in-android-4-2-2","tags":["adobebug","air","android"],"title":"朝鲜语/韩文字符在Anrdoid4.2.2上不显示Korean text isn't shown in Android 4.2.2","url":"https://blog.zengrong.net/post/korean-text-isnt-shown-in-android-4-2-2/"},{"categories":["technology"],"content":"不同的JDK，在OSX下的路径是不同的，需要有针对性的进行设置。下面是我能找到的所有JDK在OSX下的路径，在此做个记录。\nOS X 自带JDK 这个JDK由APPLE维护，也是OSX默认的JDK，它的路径是：\n/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home\n这其实是一个符号链接，它指向：\n/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home\nOS X自带的JRE /System/Library/Frameworks/JavaVM.framework/Versions/Current\nORACLE的JDK7 /Library/Java/JavaVirtualMachines/jdk1.7.0_xx.jdk/Contents/Home\nORACLE的JRE7 /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home\nOPENJDK7 /Library/Java/JavaVirtualMachines/jdk1.7.0.jdk/Contents/Home\n","date":"2013-05-27","description":"","lastmod":"2013-05-27T08:02:34Z","slug":"jdkpath-in-mac-os","tags":["java","osx"],"title":"JDK在MAC OS X下的路径设置","url":"https://blog.zengrong.net/post/jdkpath-in-mac-os/"},{"categories":["technology"],"content":"新功能 让Android应用能够重启自身。iOS就别想了。\n本功能由 rect 编写。\n文档 http://zrong.github.io/anetoolkit/doc/org/zengrong/ane/tool/RestartCont.html\n用法说明 1//先关闭当前app 2NativeApplication.nativeApplication.exit(); 3//重启 4ANEToolkit.restart.restartApp(); 注意事项 需要在应用程序描述文件中加入如下Android标签：\n1\u0026lt;application android:enabled=\u0026#34;true\u0026#34;\u0026gt; 2\t\u0026lt;!-- 以下receiver和service为重启APP所用 --\u0026gt; 3\t\u0026lt;!-- 若希望修改indent name 可以修改JAVA源码 org.zengrong.ane.funs.restart.AppRestart 中的对应字符串 --\u0026gt; 4\t\u0026lt;receiver android:name=\u0026#34;org.zengrong.ane.funs.restart.BootSystemReceiver\u0026#34; \u0026gt; 5\t\u0026lt;intent-filter\u0026gt; 6\t\u0026lt;action android:name=\u0026#34;com.android.rect.restart.airApp\u0026#34; /\u0026gt; 7\t\u0026lt;/intent-filter\u0026gt; 8\t\u0026lt;/receiver\u0026gt; 9\t\u0026lt;service android:name=\u0026#34;org.zengrong.ane.funs.restart.NotificationService\u0026#34;\u0026gt;\u0026lt;/service\u0026gt; 10\t\u0026lt;!-- 重启APP需要内容完毕 --\u0026gt; 11\u0026lt;/application\u0026gt; 完整的应用程序描述文件可参考 范例项目的应用程序描述文件 。\n范例项目中已经加入 新功能的调用范例。\n","date":"2013-05-21","description":"","lastmod":"2013-05-21T10:00:13Z","slug":"ane-toolkit-reset-self","tags":["air","android","ane"],"title":"ANE Toolkit增加重启自身功能","url":"https://blog.zengrong.net/post/ane-toolkit-reset-self/"},{"categories":["technology"],"content":"使用AIR打包Android APK的时候，碰到了error 100错误，具体错误提示为：\nD:\\works\\tools\\anetoolkit\\project\\sample\\src\\ANEToolkitSample-app.xml: error 100 : Descriptor cannot be parsed 根据 Adobe提供的文档 ，error 100属于应用程序描述文件XML语法错误。\n但是，我的XML语法明显是正确的。\n花了1个小时测试，发现问题出在注释的位置上。\n错误的注释如下所示， “ 若希望修改indent name 可以修改JAVA源码 ....” 这段，如果放在intent-filter上方，就会出现error 100错误\n1\u0026lt;android\u0026gt; 2 \u0026lt;colorDepth\u0026gt;16bit\u0026lt;/colorDepth\u0026gt; 3 \u0026lt;manifestAdditions\u0026gt;\u0026lt;![CDATA[\u0026lt;manifest android:installLocation=\u0026#34;auto\u0026#34;\u0026gt; 4 \u0026lt;application android:enabled=\u0026#34;true\u0026#34;\u0026gt; 5 \u0026lt;!-- 以下receiver和service为重启APP所用 --\u0026gt; 6 \u0026lt;receiver android:name=\u0026#34;org.zengrong.ane.funs.restart.BootSystemReceiver\u0026#34; \u0026gt; 7 \u0026lt;!-- 若希望修改indent name 可以修改JAVA源码 org.zengrong.ane.funs.restart.AppRestart 中的对应字符串 --\u0026gt; 8 \u0026lt;intent-filter\u0026gt; 9 \u0026lt;action android:name=\u0026#34;com.android.rect.restart.airApp\u0026#34; /\u0026gt; 10 \u0026lt;/intent-filter\u0026gt; 11 \u0026lt;/receiver\u0026gt; 12 \u0026lt;service android:name=\u0026#34;org.zengrong.ane.funs.restart.NotificationService\u0026#34;\u0026gt;\u0026lt;/service\u0026gt; 13 \u0026lt;!-- 重启APP需要内容完毕 --\u0026gt; 14 \u0026lt;/application\u0026gt; 15.......... 如果把这段注释放在 `receiver` 上方，就没有问题：\n1\u0026lt;android\u0026gt; 2 \u0026lt;colorDepth\u0026gt;16bit\u0026lt;/colorDepth\u0026gt; 3 \u0026lt;manifestAdditions\u0026gt;\u0026lt;![CDATA[\u0026lt;manifest android:installLocation=\u0026#34;auto\u0026#34;\u0026gt; 4 \u0026lt;application android:enabled=\u0026#34;true\u0026#34;\u0026gt; 5 \u0026lt;!-- 以下receiver和service为重启APP所用 --\u0026gt; 6 \u0026lt;!-- 若希望修改indent name 可以修改JAVA源码 org.zengrong.ane.funs.restart.AppRestart 中的对应字符串 --\u0026gt; 7 \u0026lt;receiver android:name=\u0026#34;org.zengrong.ane.funs.restart.BootSystemReceiver\u0026#34; \u0026gt; 8 \u0026lt;intent-filter\u0026gt; 9 \u0026lt;action android:name=\u0026#34;com.android.rect.restart.airApp\u0026#34; /\u0026gt; 10 \u0026lt;/intent-filter\u0026gt; 11 \u0026lt;/receiver\u0026gt; 12 \u0026lt;service android:name=\u0026#34;org.zengrong.ane.funs.restart.NotificationService\u0026#34;\u0026gt;\u0026lt;/service\u0026gt; 13 \u0026lt;!-- 重启APP需要内容完毕 --\u0026gt; 14 \u0026lt;/application\u0026gt; 15.......... ","date":"2013-05-21","description":"","lastmod":"2013-05-21T07:33:49Z","slug":"adt-note-bug","tags":["adobebug","air","android"],"title":"ADT error 100 Descriptor cannot be parsed","url":"https://blog.zengrong.net/post/adt-note-bug/"},{"categories":["technology"],"content":"Robotlegs2中文教程-概述\n起因 好久没写长文了，感觉平时写代码多了，在遣词造句上逐渐生疏起来。博客也慢慢写得七零八落，彻底沦为只有自己能看懂的个人笔记（当然，对我来说这并没有什么不好 :)）。\n趁介绍Robotlegs2的机会，锻炼一下文笔，看看自己还有没有被废掉。\n本文开始的时候，Robotlegs2的版本为b6。此时，网上完全找不到像样的中文介绍，更别说文档了。即使是在Robotlegs2官网知识库，也没有系统的学习资料，只有爱好者们写的一些Sample，或者框架作者以及资深用户在回答网友问题的时候形成了一些有用的资料。当然，还有框架源码中可爱的README以及源码注释。\nRobotlegs2有一个漫长的发展期。正因为这个“漫长”，导致了网上找到的一些依赖“旧版本”的Robotlegs2制作的sample无法在新版本上运行。前段时间，Robotlegs的作者闭关了一段时间，框架的进展突然加速，接连发布了几个beta版本。到了现在的b6版本，整个框架已经比较稳定了。\n我在 Github 上watch了Robotlegs2项目，每天都能收到关于框架的最近进展与issue。框架的两个作者关于功能的讨论相当活跃和激烈，即使是在我写本文的过程当中，某几个类的位置都发生过变动。两个作者甚至在讨论修改SwiftSuspender的问题。因此，在 框架release 之前，我不会深入到源码级别进行介绍。\n本系列教程会比较长，完整目录会在这里更新：using robotlegs2。\n提纲 先列个提纲，根据撰写进度会不断修改。\n概述 使用MVCBundle Context与Config Extension与Bundle hook …… SwiftSuspenders 前言 我的框架使用和选择过程比较有趣，以下按时间顺序排列：\n不用框架 自己写框架 使用PureMVC两年 自己写框架 不用框架 Robotlegs 使用了 Robotlegs 之后，我就没有再换过其他框架，也没有再自己写框架。Robotlegs小巧，易上手，使用也方便，非常适合中小型项目的开发。\nRobotlegs2相对于Robotlegs1来说，是完全的重写。甚至说是两个不同的框架都不为过。v2的主要特点如下（来自官方网站） ：\n更容易配置 更容易扩展 更好的模块支持 灵活的类型匹配 我承认，上面的特点等于没说。\n每个重写的版本，大抵上都有类似的特点。因此，要深入理解Robotlegs2的特性，必须依赖实例。本系列教程也是基于实例来讲解。\nSwiftSuspender SwiftSuspender是Robotlegs默认使用的注入器。在v1版本中，它是外置的。我们可以用其他注入器来替代它。\n但是到了v2版本，该注入器已经被包含在了Robotlegs框架之中，而且不可替换。\nRobotlegs1 是否一定要先学习Robotlegs1，才能学习Robotlegs2？\n当然不需要。\n前面说过了，Robotlegs2是一个完全重写的版本。甚至连MVC模式都被剥离出来，使用插件来实现。熟悉Robotlegs 1对于理解Robotlegs 2必然有帮助，但并非必要。\n本系列教程也不会将Robotlegs1作为阅读的必要条件。在教程中，由于某些需要，我可能会对v1和v2进行对比，但这个对比不会影响没有v1基础读者的理解。\n当然，如果读者愿意学习一下Robotlegs1，我愿意提供一些资源：\nRobotlegs最佳实践 ActionScript Developer's Guide to Robotlegs 什么人不需要阅读本教程 “精通”一门面向对象编程语言的人\n无论你是真精通还是假精通……大师，给小弟留点面子； 不知道设计模式是什么\n当你不知道什么是单例模式、中介模式、MVC模式的时候，最好是先去找本 Head First 设计模式 看看先。 经常问别人 “addChildAt 的第2个参数怎么用” 之类问题的人\n朋友，程序员是个很危险的职业，或许卖水果更适合你。 阅读本教程需要什么基础知识 你需要了解设计模式，最好在自己的项目中使用过它们； 你独立开发过一个项目，或者正打算独立开发一个项目。 小结 本章说了一些废话，简单介绍了Robotlegs2的一些特点。下章将从一个简单的实例来介绍Robotlegs2 MVCBundle的使用。\n","date":"2013-05-20","description":"","lastmod":"2013-05-20T16:29:51Z","slug":"use_robotlegs2_overview","tags":["as3","framework","robotlegs","usingrobotlegs2"],"title":"Robotlegs2中文教程-概述","url":"https://blog.zengrong.net/post/use_robotlegs2_overview/"},{"categories":["technology"],"content":"今天从韩国合作伙伴那里得知，几天前提审到AppStore的游戏得到了Apple的警告：\nDear developer,\nWe have discovered one or more issues with your recent delivery for \u0026quot;ì?€?￡?ì ?ê?°\u0026quot;. Your delivery was successful, but you may wish to correct the following issues in your next delivery: Non-PIE Binary - The executable 'AVOCIOS.app' is not a Position Independent Executable. Please ensure that your build settings are configured to create PIE executables. If you would like to update your binary for this app, you can reject this binary from the Binary Details page in iTunes Connect. Note that rejecting your binary will remove your app from the review queue and the review process will start over from the beginning when you resubmit your binary.\nRegards, The App Store team\n提审AppStore都N次了，这个Non-PIE Binary问题我从未碰到过。从Google搜到的消息，也都是从5月开始的。看来是Apple修改了政策。\n首先搜到的是Apple老巢的文章：Non-PIE Binary - The executable is not a Position Independent Executable.\n这篇文章里面谈到，需要设置 Target-\u0026gt;Build Setting 如下：\ndeployment target\u0026gt;=4.3 Generate Position-Dependent Code=NO Generate Position-Dependent Executable=NO\n但是，我们是AIR有没有！强大的AIR不需要XCode（你以为我不想要啊），怎么设置？\n接着，在Starling论坛上找到了这个：AIR, iOS, PIE disabled warning 我对其中这段话是深以为然啊！:-)\nAdobe Gaming SDK is bad... outdated APIs (testflight), poorly designed and bugged sample projects...\n这篇讨论中提到，在使用了Adobe Gaming SDK中的 StageAd ANE 之后，出现了这个警告。无论Adobe是不是躺枪，ANE在编译的时候是否加入了PIE支持都是值得怀疑的。\n我的项目中并没有使用Adobe提供的ANE，而是自己实现的一套。这套ANE一直比较稳定，很长时间没有修改过。看来确实如果上面推测的那样，Apple修改了审核政策，导致老的ANE出现了Non-PIE Binary警告。\n如果按照这个思路，重新编译一下ANE，在 Build Setting 中进行上面的设定，应该就能解决问题。\n但愿是这样，我尝试之后再更新本文。\nOK，正在我准备重新打包ANE的时候，我在Adobe老巢找到这篇讨论：Build for iPhone5 and AppStore。看标题和内容，都与本文无关，而且这是一张去年的老帖。\n但是，可能是某个论坛新手看到这贴顺眼，把它挖坟弄出来，在帖子后面补上了关于Non-PIE的问题，看看 55楼。\n然后，一群网友开始吐槽这个问题，大家都遇到了，不论是AIR3.4、3.5……3.7，无一不中枪。还有兄弟 was an all night battle 来测试，有比这更惨的么？另外还有网友用Flash CC也碰到这问题，Flash CC啊！那可是天朝都买不到的东西。\n更有趣的是， 60楼 的一个家伙没明白 Position Independent Executable 这句话的意思，以为是屏幕方向问题，跳出来做了一堆搞笑解释。\n最后，Adobe的人实在看不下去了，在 64楼 跳出来做了解释，承认了是AIR的问题：\nHi All,\nThanks for reporting. PIE has nothing to do with Orientation of the applications, it's related to security aspects of the application. Currently, AIR applications are not being generated as PIE binaries, but looks like Apple now encourages to do so. We're investigating it.\nThanks Pahup Adobe AIR\n然后他继续说：\nThis is just a warning as of now, we've not seen any apps being rejected because of this.\n我艹，这真是不是你自家的程序你不担心啊。这就好像楼上厕所漏水到我家，然后楼上的说： 没关系，我这几天不用厕所，你等等看 （我说真的这就是昨天发生在我家的真事）。\nApple虽然说了这次放你通过，但别人也说了下次不改的话老子拒你没商量。我可不敢没事去捋Apple的猫须。\nSo, I have to be patient. 下面是几个扩展参考：\nNon-PIE Binary - The executable is not a Position Independent Executable.一文中提到了可以使用 otool 工具来检测PIE支持。 Position Independent Executables一文详细介绍了PIE是什么； otool 与dylib介绍了otool的用法。 ","date":"2013-05-17","description":"","lastmod":"2013-05-17T03:14:41Z","slug":"non-pie_binary_in_adobe_air","tags":["air","ane","ios"],"title":"Non-PIE Binary in Adobe AIR","url":"https://blog.zengrong.net/post/non-pie_binary_in_adobe_air/"},{"categories":["technology"],"content":"Ant编译的jar文件，ANE不识别\n问题描述 Android的ANE打包需要jar文件。Eclipse可以提供jar文件的导出。\n然而，当我使用Ant来自动化完成ANE打包流程的时候，jar文件出了问题。\n如果使用Ant生成的jar文件来打包ANE。那么ANE在使用的时候，会发生 ExtensionContext 无法初始化的情况。\n也就是说，在调用 ExtensionContext.createExtensionContext(EXTENSION_ID) 的时候，得到的永远是null。\n问题分析 以下是构建jar的target：\n1\u0026lt;target name=\u0026#34;android\u0026#34; description=\u0026#34;Build Android Library\u0026#34;\u0026gt; 2\t\u0026lt;delete dir=\u0026#34;../android/temp\u0026#34;/\u0026gt; 3\t\u0026lt;mkdir dir=\u0026#34;../android/temp/classes\u0026#34;/\u0026gt; 4\t\u0026lt;echo message=\u0026#34;Using Java version ${ant.java.version}.\u0026#34;/\u0026gt; 5\t\u0026lt;javac source=\u0026#34;1.6\u0026#34; srcdir=\u0026#34;../android/src\u0026#34; destdir=\u0026#34;../android/temp/classes\u0026#34; includeantruntime=\u0026#34;false\u0026#34;\u0026gt; 6\t\u0026lt;classpath\u0026gt; 7\t\u0026lt;pathelement location=\u0026#34;${android.sdk}/android.jar\u0026#34;/\u0026gt; 8\t\u0026lt;pathelement location=\u0026#34;${flex.sdk}/lib/android/FlashRuntimeExtensions.jar\u0026#34;/\u0026gt; 9\t\u0026lt;pathelement location=\u0026#34;../android/libs/android-support-v4.jar\u0026#34;/\u0026gt; 10\t\u0026lt;pathelement location=\u0026#34;${android.tools}/support/annotations.jar\u0026#34;/\u0026gt; 11\t\u0026lt;/classpath\u0026gt; 12\t\u0026lt;/javac\u0026gt; 13\t\u0026lt;mkdir dir=\u0026#34;../temp/android/\u0026#34;/\u0026gt; 14\t\u0026lt;jar basedir=\u0026#34;../android/temp/classes\u0026#34; destfile=\u0026#34;../temp/android/lib${name}.jar\u0026#34;/\u0026gt; 15\t\u0026lt;copy todir=\u0026#34;../temp/android/res/\u0026#34;\u0026gt; 16\t\u0026lt;fileset dir=\u0026#34;../android/res\u0026#34;/\u0026gt; 17\t\u0026lt;/copy\u0026gt; 18\t\u0026lt;delete dir=\u0026#34;../android/temp\u0026#34;/\u0026gt; 19\u0026lt;/target\u0026gt; Ant打包得到的jar文件，比使用Eclipse导出的jar文件要小。解压打包的jar文件之后，我发现使用Ant编译出来的class文件比Eclipse编译的要小。\n于是，应该从javac上找原因。\n解决过程 酱油debug 先为JAVAC task加上 debug=\u0026quot;on\u0026quot; 参数。这样编译出来的class文件，大小和Eclipse类似了。但是做成的ANE依然不管用。\nJDT编译器 因为Eclipse的编译器和Ant默认使用的编译器是不同的，我尝试将Ant的编译器换成Eclipse看看。使用JDT编译器的步骤为：\n进入 Eclipse downloads 找一个版本，进入下载页面，搜索 JDT Core Batch Compiler，可以下载到JDT编译器； 将解压后得到的 ecj-\u0026lt;version\u0026gt;.jar 复制到Ant的lib目录； 为JAVAC task加入 compiler=\u0026quot;org.eclipse.jdt.core.JDTCompilerAdapter\u0026quot; 参数，运行task。 JAVAC报告如下错误：\nTarget level '1.2' is incompatible with source level '1.6'. A target level '1.6' or better is required\n为JAVAC task加入 target=\u0026quot;1.6\u0026quot; 参数，运行task正常。完整JAVAC参数如下：\n1..... 2\u0026lt;javac source=\u0026#34;1.6\u0026#34; srcdir=\u0026#34;../android/src\u0026#34; destdir=\u0026#34;../android/temp/classes\u0026#34; includeantruntime=\u0026#34;false\u0026#34; compiler=\u0026#34;org.eclipse.jdt.core.JDTCompilerAdapter\u0026#34; target=\u0026#34;1.6\u0026#34;\u0026gt; 3..... 然后生成ANE进行调试，一切正常，问题解决。\njavac编译器 既然使用JDT编译器正常，那么使用javac的默认编译器（JDK自带的编译器）是否也正常？\n删除 compiler 参数设置，保留 target 参数设置，运行task正常，生成ANE调试，一切正常。\n1\u0026lt;target name=\u0026#34;android\u0026#34; description=\u0026#34;Build Android Library\u0026#34;\u0026gt; 2\t\u0026lt;delete dir=\u0026#34;../android/temp\u0026#34;/\u0026gt; 3\t\u0026lt;mkdir dir=\u0026#34;../android/temp/classes\u0026#34;/\u0026gt; 4\t\u0026lt;echo message=\u0026#34;Using Java version ${ant.java.version}.\u0026#34;/\u0026gt; 5\t\u0026lt;javac source=\u0026#34;1.6\u0026#34; target=\u0026#34;1.6\u0026#34; srcdir=\u0026#34;../android/src\u0026#34; destdir=\u0026#34;../android/temp/classes\u0026#34; includeantruntime=\u0026#34;false\u0026#34;\u0026gt; 6\t\u0026lt;classpath\u0026gt; 7\t\u0026lt;pathelement location=\u0026#34;${android.sdk}/android.jar\u0026#34;/\u0026gt; 8\t\u0026lt;pathelement location=\u0026#34;${flex.sdk}/lib/android/FlashRuntimeExtensions.jar\u0026#34;/\u0026gt; 9\t\u0026lt;pathelement location=\u0026#34;../android/libs/android-support-v4.jar\u0026#34;/\u0026gt; 10\t\u0026lt;pathelement location=\u0026#34;${android.tools}/support/annotations.jar\u0026#34;/\u0026gt; 11\t\u0026lt;/classpath\u0026gt; 12\t\u0026lt;/javac\u0026gt; 13\t\u0026lt;mkdir dir=\u0026#34;../temp/android/\u0026#34;/\u0026gt; 14\t\u0026lt;jar basedir=\u0026#34;../android/temp/classes\u0026#34; destfile=\u0026#34;../temp/android/lib${name}.jar\u0026#34;/\u0026gt; 15\t\u0026lt;copy todir=\u0026#34;../temp/android/res/\u0026#34;\u0026gt; 16\t\u0026lt;fileset dir=\u0026#34;../android/res\u0026#34;/\u0026gt; 17\t\u0026lt;/copy\u0026gt; 18\t\u0026lt;delete dir=\u0026#34;../android/temp\u0026#34;/\u0026gt; 19\u0026lt;/target\u0026gt; 结论 原来，导致class不能使用的真正原因，并非编译器差别，而是在编译的时候没有设置目标版本。Eclipse编译设置中，默认就为编译参数加上了目标版本，而Ant则没有。\n我的自动编译ANE功能终于完善了。\n参考 Compiling Java code Why are class files different size when compiling the same code in eclipse, and then with the eclipse compiler via ant? Eclipse vs. javac (ant) compilation problem ","date":"2013-05-16","description":"","lastmod":"2013-05-16T10:03:14Z","slug":"ane_cant_know_the_jar_compile_from_ant","tags":["ane","ant","java"],"title":"Ant编译的jar文件，ANE不识别","url":"https://blog.zengrong.net/post/ane_cant_know_the_jar_compile_from_ant/"},{"categories":["news"],"content":"在2013 Adobe MAX大会上，Adobe宣布升级CS套件为CC套件。而CC套件只租不卖。\nCC套件的全称是 Creative Cloud，外界一直盛传CC套件只能在云上使用，或者必须每月付费。这篇文章解释了这些流言。\n详见下文（英文）：\nhttp://terrywhite.com/5-myths-about-adobe-creative-cloud/\n","date":"2013-05-14","description":"","lastmod":"2013-05-14T02:14:03Z","slug":"adobe-creative-cloud-romour","tags":["adobe"],"title":"关于Adobe Creative Cloud的5个流言","url":"https://blog.zengrong.net/post/adobe-creative-cloud-romour/"},{"categories":["impressions"],"content":"Adobe博客发布了名为 The future of Adobe Fireworks 的文章，描述了Adobe对Fireworks的态度。\n所以，Fireworks被杀了。\nFireworks在Macromedia被收购的时候，就差点被杀掉。那次未遂。\n我从设计转开发，从 Fireworks 3 就开始使用这个可爱的设计软件。Fireworks在Web设计上，是无法被Photoshop替代的。\n虽然已经好多年没有用她，但我依然很伤心。\nAdobe似乎被Yahoo!附体。\n这篇文章的标题很可笑： 未来？ 没有未来。\n程序员跟着Adobe走，就不会有未来。兄弟姐妹们，趁着船还没有沉，赶快逃生把。\n","date":"2013-05-09","description":"","lastmod":"2013-05-09T09:35:50Z","slug":"1851","tags":["adobe"],"title":"Adobe又杀掉了Fireworks","url":"https://blog.zengrong.net/post/1851/"},{"categories":["technology"],"content":"在使用 Apache Flex 4.9.1 运行一个测试项目的时候，出现了错误，ANT告知我找不到 (playerglobalHome) 变量。\n这个变量是在 frameworks/flex-config.xml 中出现的。我比较了 Apache Flex 4.9.1 和 Adobe Flex 4.6.0 的 flex-config.xml 文件，发现有源码中有两个地方将原来的 libs/player 替换成了 {playerglobalHome} 。\n要解决这个问题，有两个方案：\n设置环境变量 PLAYERGLOBAL_HOME ，值为 %FLEX_HOME%\\frameworks\\libs\\player 即可； 修改flex-config.xml为原来的样子。 我把所有的 playerglobal.swf 都放在了一个独立的目录，可以让多个版本的Flex SDK共享。很壮观吧……\n参考文章：\nhttp://mail-archives.apache.org/mod_mbox/incubator-flex-dev/201205.mbox/%3C66E38C42347D6446BF7FCB22C3D3878072C4B06A5B@ECCR06PUBLIC.exchange.local%3E 配置Apache Flex SDK 4.9.0（包括：手动方式和自动方式） Integrating Apache Flex 4.8.0 fails ","date":"2013-05-07","description":"","lastmod":"2013-05-07T16:04:54Z","slug":"apache-flex-4-9-1-playerglobalhome-config","tags":["apache","flex"],"title":"Apache Flex 4.9.1 playerglobalHome config","url":"https://blog.zengrong.net/post/apache-flex-4-9-1-playerglobalhome-config/"},{"categories":["technology"],"content":"在AIR模拟器模式中设置Screen DPI属性\nSet Screen DPI on ADL for air simulator mode\n在使用AIR开发移动应用程序的时候，我们可以利用AIR Debug Launcher (ADL)在PC上进行调试，这样测试效率更高，速度也更快。\n但是，PC的DPI值（72）与移动设备（160、252、362……）不同，这将导致某些依赖设备分辨率的框架和程序，在PC上的表现与移动设备不同。\n如果使用Flash Builder 4.7开发，在一般情况下，不会遇到分辨率问题。因为Flash Builder会自动进行DPI的设置。从下面的设置界面中，我们可以看到，在选择一个模拟器配置的时候，这个配置是包含DPI设置的。\n在启动参数中，我们也可以看到，这个DPI设置是有效的。\n在模拟器启动之后，通过检查 Capabitilities.screenDPI 的值，我们可以知道，这个设置确实是有效的。\nFlash Builder是调用ADL来以模拟器模式启动应用的。那么对于其他IDE来说，是否也能通过ADL的启动参数来进行这样的设置呢？\n遗憾的是，我找遍了 ADL 的文档，都没有看到关于分辨率的设置。受到上面第二张图的启发，我也尝试了这样的语法\nadl -screensize NexusOne -DPI 252 application.xml bin ADL直接报错了。显然，这个参数不能这么用。\n我查看了FlashDevelop中关于移动设备项目的 Run.bat 脚本，他们也没有解决这个问题。论坛上有人提到过这个问题，但没有得到正面的回答。\n那么，Flash Builder是怎么做到的？\n其实，我刚才的尝试已经接近成功了。隐藏的参数确实存在，只是和我使用的参数名称不同而已。上面的代码，写成这样，就OK了。\nadl -screensize NexusOne -XscreenDPI 252 application.xml bin 我可以更新一下FlashDevelop的Run.bat脚本了。\n参考文章：\nhttp://youtrack.jetbrains.com/issue/IDEA-89860 AIR Debug Launcher Scale Too Small ","date":"2013-04-29","description":"","lastmod":"2013-04-29T13:45:41Z","slug":"set_screen_dpi_on_adl_for_air_simulator_mode","tags":["air","android","flashbuilder","flashdevelop"],"title":"在AIR模拟器模式中设置Screen DPI属性","url":"https://blog.zengrong.net/post/set_screen_dpi_on_adl_for_air_simulator_mode/"},{"categories":["impressions"],"content":"一大早，就在CB上看到了关于Unity3D放弃支持Flash的消息。害怕媒体误读，专门去Unity3D网站看了原文：Sunsetting Flash。\n不得不说，Unity3D提出的3条放弃理由都让我无法反驳：\n1 We don’t see Adobe being firmly committed to the future development of Flash.\nAdobe最近取消了Flash Player \u0026quot;Next\u0026quot;的开发计划，当然Actionscript4.0也将不会存在。\n对于已有的产品和技术，Flash Builder发展缓慢；ANE打包工具都没有一个；ANE的调试目前只能依赖原生平台……\n这是基因问题么？\n2 By introducing, and then abandoning, a revenue sharing model, Adobe eroded developers’ (and our) trust in Flash as a dependable, continuously improving platform.\n不仅仅是先前大张旗鼓的宣布对Flash Player高级功能收费，后来又莫名其妙的低调取消收费这件事。还有停止Linux版本AIR开发，停止移动设备Flash Player开发，停止Flash Builder Linux版本开发这类已经被大众遗忘（或者根本就不知道）的事情。它们连在一起，伤透了我的心。\n3 Developers are moving away from Flash.\n真正的Flash开发者，或者说像我这样从Flash 4开始的Flash开发者，对Flash始终有着难以割舍的情感。\n我尝试过许多平台，但由于种种原因，我从未真正离开过Flash平台。我把其他平台和语言学到的技巧和能力，都用于配合Flash工作。\n或许有一天，我真的能够抛弃对AS1/2/3的感情，义无反顾的走入新的世界。\n抒情完毕。下面是吐槽：\n我不得不说Unity3D是高级黑，过河拆桥的家伙。而且拆桥的时候还不忘做广告：\nand while Flash publishing has gotten little traction, our own Unity Web Player has seen unprecedented growth in recent months (now installed on over 200M computers and already installed by 1/3 of all Facebook gamers).\n在Unity3D弱势的那段时间里，它加入了导出Flash的支持，吸引了大量的Flash开发者转向Unity3D。而羽翼渐丰的现在，又以这些冠冕堂皇的理由，取消Flash导出的支持。\n此举不但成功的吸引到了一部分原Flash开发者，同时也以“痛打落水狗”的姿态标榜了自己的能力，确实一举多得。\n目前在移动开发中，Unity3D或许已经超过了Flash成为更好的跨平台开发工具。Unity3D更加希望自己能保持这个优势，踩着Flash的头向上爬，当然就不再愿意和Flash合作。\n不过话说回来，最后都怪Adobe自己不争气。看看我最近发现的AIR 4.7BUG。\n","date":"2013-04-25","description":"","lastmod":"2013-04-25T06:26:42Z","slug":"the_unity_3d_sunsetting_flash","tags":["3d","adobe","air"],"title":"由Unity3D放弃Flash支持想到的","url":"https://blog.zengrong.net/post/the_unity_3d_sunsetting_flash/"},{"categories":["technology"],"content":"我的Google Play账户一直是好好的，今天突然出现无法支付的情况。\n错误描述 在Android设备中进入Google Play的支付流程，选择信用卡并付款后，Google Play弹出界面提醒如下：\nError Payment Declined\n同时，我的Google Wallet账户会收到一笔canceled的付款。\n而后，我发现在Google Play中的每一笔付款都会被立即cancel。\n在Google Wallet中，可以看到这条交易的Status为：\nYour purchase has been cancelled. You won't be charged. Google has cancelled this purchase. Comments from Google: We have cancelled your order because your account is on hold.\n问题搜索 采用这些错误关键词搜索到Google Play的帮助文档 Troubleshooting declined payment，该文提到两种可能：\n取消订单，尝试其他支付项目\n我并没有订单需要取消，尝试支付所有的项目，均得到错误提示。 另外，这个订单在开发状态下生成，我支付的项目在Google Play中处于Draft状态，因此也无法在Android设备的Google Play账户中取消订单。\n信用卡失效，或者信用卡资料变更 我取消信用卡并重新加入，又尝试了另外两张信用卡，问题依旧。\n然后我又找到这篇：Payment issues。详读之后发现，或许是我的Wallet地址与信用卡订单地址不统一？Google能查到我天朝银行的订单地址么？\n我当然没有米国地址，但我在Google Wallet中填写的地址也并非虚假地址啊。\n然后，我发现还有许多朋友和我有同样的情况：Google Wallet account on hold?\nWhen I try and buy something on Google Play I get an error message telling me to check my Google Wallet. When I check Google Wallet it says my order was cancelled due to my account being on hold. All my payment details are upto date and have enough funds, so I have no idea what is going on, but it's very frustrating. Called Google Play and they said they would pass my account to an 'account specialist', who would get back to me within 24-48hrs. So I'm just waiting for them to get in touch now.\n看来，我也只能等一下了。\n2013-04-19更新： 等的办法确实管用，24小时之后，我的账户已经可以支付了。\n","date":"2013-04-18","description":"","lastmod":"2013-04-18T07:15:40Z","slug":"googleplay_payment_declined","tags":["android","google","pay"],"title":"Google Play Payment Declined","url":"https://blog.zengrong.net/post/googleplay_payment_declined/"},{"categories":["technology"],"content":"AIR 3.7 SDK Bug:You uploaded an unsigned APK\n2013-04-18更新：使用AIR 3.7 SDK打包的APK文件，出现了在某些手机上无法安装的情况，换回AIR SDK 3.6就一切正常。 2013-04-19更新：使用AIR 3.7 SDK打包的APK文件，在Google In-app Billing支付的时候，也出现了问题。具体表现为，Google Play支付成功，信用卡扣款成功，但Google Playe并没有正常返回支付信息，导致最终的支付无法完成。但改为AIR 3.6 SDK打包就一切正常。 我推测，是由于Google Play中上传的那个APK是使用AIR 3.6 SDK打包，必须使用AIR 3.7 SDK打包的APK文件替换原来的AIR 3.6 SDK打包的APK，才会支付正常。但由于AIR 3.7 SDK打包的APK在某些设备上无法安装的问题，我不可能再使用AIR 3.7 SDK去打包了。 2013-04-30更新：根据赵客的建议，向Adobe提交了此BUG：https://bugbase.adobe.com/index.cfm?event=bug\u0026amp;id=3552540\n将一个需要更新的APK上传到Google Play的时候，Google Play提示我这样的错误： Uploaded failed You uploaded an unsigned APK. You need to create a signed APK.\n截图如下：\n这个提示无疑是 错误 的。我并没有修改过任何编译参数，也从来没有修改过打包使用的证书。在程序编译正常的情况下去修改编译参数和证书？我不是吃饱了撑的么。\n这个APK在Android设备上是可以正常安装和运行的，因此程序本身没有什么问题。\n我怀疑了许多地方，比如是否有ANE需要签名，或者是否有某些Android权限比较特殊，都没有找到什么线索。\n突然想起昨天曾经把 AIR 3.6 SDK 更新成了 AIR 3.7 SDK ，是否是这个原因呢？于是我将SDK切换回 3.6版本，果然一切正常了。使用 AIR 3.6 SDK 编译的APK包，Google Play上传和更新正常。\n但是，这并没有解决问题。对于 AIR 这个 被Adobe寄予厚望的、BUG重重的、快速更新的 平台，我不可能永远不更新它。我必须找到真正的原因。\n终于，在编译了几十个测试包之后，我终于找到了真正原因。\n原来导致APK签名错误的罪魁祸首，是图标！\nAPK的图标是在 应用程序描述符文件 app-xml 中设置的，这是我原来的设置：\n1\u0026lt;!-- 此内容是AIR 的应用程序描述符文件的一部分 --\u0026gt; 2\u0026lt;icon\u0026gt; 3\t\u0026lt;image16x16\u0026gt;assets/icon/16.png\u0026lt;/image16x16\u0026gt; 4\t\u0026lt;image32x32\u0026gt;assets/icon/32.png\u0026lt;/image32x32\u0026gt; 5\t\u0026lt;image36x36\u0026gt;assets/icon/36.png\u0026lt;/image36x36\u0026gt; 6\t\u0026lt;image48x48\u0026gt;assets/icon/48.png\u0026lt;/image48x48\u0026gt; 7\t\u0026lt;image57x57\u0026gt;assets/icon/57.png\u0026lt;/image57x57\u0026gt; 8\t\u0026lt;image72x72\u0026gt;assets/icon/72.png\u0026lt;/image72x72\u0026gt; 9\t\u0026lt;image114x114\u0026gt;assets/icon/114.png\u0026lt;/image114x114\u0026gt; 10\t\u0026lt;image128x128\u0026gt;assets/icon/128.png\u0026lt;/image128x128\u0026gt; 11\t\u0026lt;image144x144\u0026gt;assets/icon/144.png\u0026lt;/image144x144\u0026gt; 12\u0026lt;/icon\u0026gt; 我知道这样设置的图标有点多。其实Android不需要这么多图标。但iOS那个变态需要。早期写编译脚本的时候，对于iOS和Android使用的是同一个app-xml文件，需要在XML中包含Android和iOS支持的所有图标尺寸。现在虽然已经分开了，但旧的app-xml就一直保存了下来。\n直到AIR 3.6 SDK，这样写都是没有问题的，打包出APK可以正常安装运行，也可以正常上传到Google Play。\n可是到了AIR 3.7 SDK，这样写就不行了，这会导致我上面阐述的问题。可是纠结的地方就在于打包出来的APK是可以正常使用的，而且打包过程中，也没有任何的提示或者错误信息。\n算了，多的话不说了，浪费的时间和精力那都是浮云。现在说解决方案。\n1\u0026lt;icon\u0026gt; 2\t\u0026lt;image36x36\u0026gt;assets/icon/36.png\u0026lt;/image36x36\u0026gt; 3\t\u0026lt;image48x48\u0026gt;assets/icon/48.png\u0026lt;/image48x48\u0026gt; 4\t\u0026lt;image72x72\u0026gt;assets/icon/72.png\u0026lt;/image72x72\u0026gt; 5\u0026lt;/icon\u0026gt; 对于Android来说，只需要3个图标就可以了，分别对应Android系统的低密度、中密度、高密度设备。\n更详细的描述，可以在这里找到：Application icons\n重新打包，搞定。\n要查看我发现的更多Adobe产品Bug，可以看这里：AdobeBug\n","date":"2013-04-16","description":"","lastmod":"2013-04-16T08:38:48Z","slug":"the_you_uploaded_an_unsigned_apk_error_in_google_play_in_air_3.7","tags":["adobe","adobebug","air","android"],"title":"AIR 3.7 SDK Bug:You uploaded an unsigned APK","url":"https://blog.zengrong.net/post/the_you_uploaded_an_unsigned_apk_error_in_google_play_in_air_3.7/"},{"categories":["technology"],"content":"AIR在打包Android的时候，是可以选择是否绑定运行时的，但是AIR SDK升级到3.7之后，将绑定运行时设置成了默认状态。这就意味着即使你在打包APK的时候选择了**“导出使用共享AIR运行时的应用程序”** ，最终导出的依然是 “具有运行时绑定的应用程序” 。\n这样，在 Flash Builder 中进行真机调试时，也会打包绑定的运行时程序，然后安装到设备中进行调试，这个过程无疑增加了打包时间和调试成本。\n要取消默认的运行时绑定，可以在环境变量中增加 AIR_ANDROID_SHARED_RUNTIME ，并将其值设为true。\n注意：可能需要重启计算机才会生效。\n参见： Packaging a mobile AIR application\n","date":"2013-04-12","description":"","lastmod":"2013-04-12T04:30:21Z","slug":"remove-air-runtime","tags":["air","android"],"title":"取消AIR 3.7 ADT打包时强制运行时绑定","url":"https://blog.zengrong.net/post/remove-air-runtime/"},{"categories":["technology"],"content":"Integrating facebook use ANE\n使用ANE整合Facebook\n2013-04-02更新： 加入Native Android App设置部分 2013-05-17更新： 加入支持Android SDK3.0的项目介绍 如何在AIR编写的移动应用中整合Facebook？以下是我这两天的研究成果。\n1. 如何阅读开发文档？ Facebook的开发文档很全，但对于一个时间紧、任务重、被Boss一天催10遍，急于实现整合的开发者来说，或许没有那么多时间去详细阅读所有文档。这里整理了一个顺序：\n1.1 The Login Dialog 无论如何，登录是第一步。这篇文档不但介绍了关于登录的所有细节，也详细介绍了关于Permissions的用法。去吧：The Login Dialog\n1.2 Dialogs Overview 各种SDK中，都提供了Dialogs的相关方法。那么Dialog是什么呢？这篇文档让你了解全部。去吧：Dialogs Overview\n1.3 Graph API Graph API是FaceBook的核心API，不了解它是不行DI。但这篇文档基本上看一半你就懂的。去吧：Graph API\n2. 选择哪个SDK？ 上面说了，Facebook的核心API是 Graph API ，其他的SDK是架构在Graph API之上。\nFacebook大力推荐了四个SDK，不用对不起观众：\nJavaScript SDK PHP SDK iOS SDK Android SDK 但是，对于AIR来说，我们必须将上面的SDK进行二次封装才能用。\n2.1 JavaScript SDK 一开始我考虑的是JavaScript SDK，因为它可以同时兼容Mobile Device和Desktop。经常做测试的同学都知道，在AIR模拟器里面测试，比在手机上测试可爽多了。\n在Google Code上有这样一个项目：facebook-actionscript-api ，它封装了Facebook的JavaScript SDK，采用StageWebView做登录。但是这个项目最后一次是在2011-10-19，以Facebook一日千里的变化，这个API估计很难用了。\n我Checkout了该项目的代码，跑了几个Sample，看了它的所有Wiki，也没有调试成功。该项目的所有Wiki都标注成了departed，看得人心惊胆战啊……\n2.2 Native SDK Facebook提供了原生SDK支持，分别对应iOS和Android设备。可是要在AIR上使用它们，也必须做二次封装。\n好在有人已经帮忙做好了这件事，而且直接提供ANE和 项目源码。请大家记住这样的好人吧：ANE-Facebook\n这个项目中，使用的AndroidSDK是1.x版本。3.x版本作者也已经基本上实现了，但还有问题。我fork了这个项目，解决了剩下的一点点问题，然后写了一个sample。我新建了 zrong 分支 来进行修改。\n这比那几个在Adobe Devnet上写文章，然后卖 $50 的ANE插件的 guy 们厚道多了。\n3. ANE-Facebook使用 ANE-Facebook 没有提供 Sample，所以我写了一个：ANE-Facebook-Sample 。\n4. Native Android App设置 如果使用了Native Android App，那么需要在Facebook中需要配置一些信息才能正常使用。\n4.1 配套名称 AIR的appid。AIR在打包成Android APK的时候，会自动在appid前面加入air前缀。例如 org.zengrong.facebook.test ，会被自动修改成 air.org.zengrong.facebook.test ；\n4.2 Class Name 配套名称+主Class名称。AIR生成的APK的主Class为AppEntry。例如上例中的APP，应该填写 air.org.zengrong.facebook.AppEntry ；\n4.3 Hash Key 这个值需要使用JDK的keytool来生成，在使用的时候，需要知道原来证书的alias name。假设原来的证书名称为zrong.keystore，alias name为“root”，那么生成Hash Key的代码如下：\nkeytool -exportcert -alias root -keystore zrong.keystore | openssl sha1 -binary | openssl base64 AIR使用的证书为p12格式，假设证书名称为zrong.p12。那么代码应该这样写：\nkeytool -exportcert -alias root -keystore zrong.p12 -storetype pkcs12 | openssl sha1 -binary | openssl base64 如果忘记了原来证书的alias name，可以使用这个命令显示它：\nkeytool -list -keystore zrong2.p12 -storetype pkcs12 -v 关于keytool的用法，可以参看这篇文章：生成Google Play需要的p12自签名数字证书\n5. 参考 Getting Started step 4 做了更详细的介绍。 Extending AIR for Android ","date":"2013-03-26","description":"","lastmod":"2013-03-26T15:17:57Z","slug":"integrating_facebook_use_ane","tags":["adobe","air","android","ane","as3","ios"],"title":"使用ANE整合Facebook","url":"https://blog.zengrong.net/post/integrating_facebook_use_ane/"},{"categories":["technology"],"content":"升级Mountain Lion的bash到4.2版本\nMountain Line自带的Bash是3.2.58版本，这个版本不支持关联数组的定义。\n而我以前写的脚本中，大量使用了关联数组。因此，我需要将Bash升级到4.2版本。\n安装GCC 如果GCC编译器没有安装，那么需要去Apple Developer 下载Command Line Tools。我下载的是 Command Line Tools(OSX Mountain Lion) for Xcode，文件大小为118MB。\n下载bash源码 1curl https://ftp.gnu.org/gnu/bash/bash-4.2.tar.gz -o ~/Downloads/bash42.gz 2tar xzf ~/Downloads/bash42.gz 编译和安装 1cd ~/Downloads/bashr42 2./configure \u0026amp;\u0026amp; make \u0026amp;\u0026amp; sudo make install 使用新的bash 新安装的bash在/usr/local/bin/bash，与旧的bash并不冲突，需要设置当前用户使用它。\n1chsh -s /usr/local/bin/bash {user_name} 2sudo bash -c \u0026#34;echo /usr/local/bin/bash \u0026gt;\u0026gt; /private/etc/shells\u0026#34; 关闭终端并重新打开，输入bash -version 或 help。查看bash安装情况。\n替换旧的bash 1sudo mv /bin/bash /bin/bash3.2.58 2sudo ln -s /usr/local/bin/bash /bin/bash 参考文章 http://techscienceinterest.blogspot.com/2010/05/change-to-new-bash-shell-41-for-mac-os.html http://concisionandconcinnity.blogspot.com/2009/03/upgrade-bash-to-40-in-mac-os-x.html https://github.com/kennethreitz/osx-gcc-installer/ ","date":"2013-03-19","description":"","lastmod":"2013-03-19T03:04:05Z","slug":"upggrade_bash_to_42_in_osx","tags":["bash","osx"],"title":"升级Mountain Lion的bash到4.2版本","url":"https://blog.zengrong.net/post/upggrade_bash_to_42_in_osx/"},{"categories":["technology"],"content":"在Cygwin下编译tmux（失败）\ntmux是GNU Screen的替代者，本文是我在Cygwin下编译tmux的失败经历，记录在案，方便以后重新尝试编译。 本文假设你已经在Cygwin下配置好了编译环境。\n要了解tmux，可以看这篇文章：http://linuxtoy.org/archives/from-screen-to-tmux.html 要了解GNU Screen，可以看这两篇文章：\nlinux 技巧：使用 screen 管理你的远程会话 对话 UNIX: 使用 Screen 创建并管理多个 shell Cygwin的源中是包含GNU Screen的，但是没有tmux，在 Cygwin ports 中也没有。想要在Cygwin使用tmux，需要自行编译。\n1. 安装libevent包 tmux编译需要依赖libevent包，但是Cygwin的官方源不包含这个包。不过，我们可以在 Cygwin Ports 找到她。\n下面的代码使用apt-cyg安装libevent包。关于apt-cyg用法，可以看这里：Cygwin的包管理器：apt-cyg 。\n1apt-cyg install libevent-devel --mirror ftp://ftp.cygwinports.org/pub/cygwinports 如果你更喜欢源码编译安装，可以在这里下载libevent源码：http://libevent.org/\n1tar xzvf libevent-2.0.21-stable.tar.gz -C /usr/src 2cd /usr/src/libevent-2.0.21 3./configuare 4make \u0026amp;\u0026amp; make install 2. 安装ncurses包 tmux编译需要依赖ncurses包，Cygwin的官方源中就有这个包。\n1apt-cyg install libncurses-devel --mirror http://mirrors.163.com/cygwin 但是，这样安装的libncurses包，在编译tmux的时候，始终报错找不到 ncurses.h文件，因此我卸载了 ncurses 包，改用编译安装。\n在这里下载ncurses源码：http://ftp.gnu.org/pub/gnu/ncurses/，我下载的是最新的5.9。\n编译和安装的方式与 libevent 相同，这里不再重复。\n3. 下载tmux源码 在这里下载tmux源码：http://tmux.sourceforge.net/，可能需要梯子，目前最新版本为1.7。\n虽然上面解决了依赖问题，但编译过程中依然遇到了编译错误，无法解决。\n就此打住，依然使用 GNU Screen 。\n","date":"2013-03-07","description":"","lastmod":"2013-03-07T01:18:35Z","slug":"compile_tmux_in_cygwin","tags":[""],"title":"在Cygwin下编译tmux(失败)","url":"https://blog.zengrong.net/post/compile_tmux_in_cygwin/"},{"categories":["technology"],"content":"在Cygwin中编译Git\n概述 我一直在Cygwin中以命令行的方式使用git。但是Cygwin源中的git版本比较老（v1.7.9），而自 1.7.10以来，git增加了许多新的特性，尤其是对中文用户特别有用的 使用UTF-8编码保存文件名 等等。为了使用这些新特性，我们只能自己编译Git。\n如果希望了解我上面说的“对中文用户特别有用的特性”，可以看这篇文章：git乱码解决方案汇总 。\n下载源码 在这里下载最新的Git源码，我下载的是 v1.8.1.4:\nhttp://code.google.com/p/git-core/downloads/list\n解压缩源码和手册：\n1tar xvf git-1.8.1.4.tar.gz -C /usr/src 2mkdir /usr/local/share/man 3tar xvf git-manpages-1.8.1.4.tar.gz -C /usr/local/share/man 编译过程 1. 使用Cygwin提供的setup.exe工具将其升级到最新版本；\n2. 如果你安装了git包，卸载它；\n3. 安装以下包： * zlib * openssh * openssl * perl * subversion-perl (如果希望使用 git-svn，需要安装这个包) * curl * libcurl-devel * expat * tcltk * make * gcc * ncurses (如果希望使用 clear 命令，需要安装这个包。更多内容可参考这里：Cygwin，那些不知道在哪里的命令 * python\n4. 编译和安装\n1./configure --prefix=/usr/local 2make \u0026amp;\u0026amp; make install 检测 1cd ~ 2which git 错误解决 在 make install 过程中，出现下面的错误：\n1make: execvp: gcc: Permission denied 2make: install -d -m 755 \u0026#39;/usr/local/bin\u0026#39; 这种错误一般是由于 Cygwin目录权限设置有问题所致。有这样几个解决途径：\n如果是 Windows7或者Vista系统，用管理员来执行Cygwin； 修改makefile文件，查找 -d -m 755 字样，将其删除； 安装到自己的主目录中，然后移动到 /usr 目录，例如： 1make install DESTDIR=/tmp/myinst/ 2cp -va /tmp/myinst/ / 在 make install 过程中，出现了下面的错误：\n1install -d -m 755 \u0026#39;/usr/local/bin\u0026#39; 2git: \u0026#39;installation\u0026#39; is not a git command. See \u0026#39;git --help\u0026#39;. 3./install: line 4: Normally: command not found 4./install: line 5: will: command not found 5./install: line 6: to: command not found 6./install: line 8: $: command not found 7./install: line 11: syntax error near unexpected token `.\u0026#39; 8./install: line 11: `(or prefix=/usr/local, of course). Just like any program suite\u0026#39; 9Makefile:2759: recipe for target `install\u0026#39; failed 10make: *** [install] Error 2 这种错误是由于在 Windows 下，大小写不区分造成的。在git源码根目录中，有一个 INSTALL 文件，这是一个文档文件，调用 make install 的时候，由于Windows不区分大小写，会认为 INSTALL 和 install 是同一个文件。这导致 INSTALL 被当作脚本解析，这当然会报错。\n解决方案有2个：\n重命名 INSTALL 文件 在 gitweb 子目录也有一个 INSTALL 文件，同样需要重命名。安装成功之后，再改回原名。 让 Windows 区分文件名大小写这个方法，仅可以用于NTFS分区，我在 Windows 7 x64 下测试成功，过程如下： 将下面这段注册表信息保存为reg文件，导入注册表\n1Windows Registry Editor Version 5.00 2[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\kernel] 3\u0026#34;obcaseinsensitive\u0026#34;=dword:00000000 将 obcaseinsensitive 改为0，是让Windows系统支持大小写区分。 修改Cygwin的 /etc/fstab 文件，将这行 none /cygdrive cygdrive binary,posix=1,user 0 0 取消注释，并将其中的 posix=0 改为 posix=1。这样让Cygwin使用Windows提供的posix API，而不是使用Win32API。Win32API是不支持大小写区分的。\n参考 Why do I get permission denied when I try use “make” to install something? make: execvp: /usr/bin: Permission denied How to install git on windows 使用cygwin编译git v1.8.0源码报错 cygwin 下分区文件名大小写 ","date":"2013-02-25","description":"","lastmod":"2013-02-25T15:41:34Z","slug":"compile_git_in_cygwin","tags":["cygwin","git","linux"],"title":"在Cygwin中编译Git","url":"https://blog.zengrong.net/post/compile_git_in_cygwin/"},{"categories":["technology"],"content":"久违的v0.8.0终于发布了，Sprite Sheet Editor会一直更新下去的。\n2013-02-19：v0.8.0版发布\n关于帧和Label界面的大幅调整； 预览界面使用单独的弹出窗口； Label中的帧现在支持重用； Label和帧的管理界面更新； 允许调整Label中帧的顺序； 支持英文界面。 更多的功能介绍以及软件下载，看这里。\n","date":"2013-02-19","description":"","lastmod":"2013-02-19T06:36:05Z","slug":"sprite-sheet-editor-0-8-0","tags":["air","spritesheet"],"title":"Sprite Sheet Editor 0.8.0发布","url":"https://blog.zengrong.net/post/sprite-sheet-editor-0-8-0/"},{"categories":["technology"],"content":"这篇文章用来记录Cygwin默认安装中找不到的那些命令。不断更新。\nwhereis\n移步这里：https://blog.zengrong.net/post/1807.html\nclear\nclear包含在 ncurses 包中，直接从默认源安装即可。\nncurses 是一个基于终端界面开发 类GUI应用程序 的库。\n详细介绍 包中的所有程序 xgettext,msginit,msgfmt\n这几个命令属于 gettext_devel 包，用于生成pot/po文件，以及编译po到mo格式。\naclocal\naclocal在 automake 包中，默认源中包含。\nautoreconf\nautoreconf在 autoconf 包中，默认源中包含。\ntelnet\n安装 xinetd 和 inetutils 包即可，默认源中包含。\n","date":"2013-02-08","description":"","lastmod":"2013-02-08T01:15:30Z","slug":"cygwin-command-finder","tags":["cygwin","linux"],"title":"Cygwin，那些不知道在哪里的命令","url":"https://blog.zengrong.net/post/cygwin-command-finder/"},{"categories":["use"],"content":"莫名其妙在我手机中发现了豌豆荚(SnapPea)应用，一看权限，我艹，你一野鸡市场，要那么多权限干嘛？果断删除之，结果发现过了两天又出现了。\n由于个人习惯的原因，我的手机相当安全，因此不应该是手机上的应用作怪，我把目标投向PC。因为工作中经常要连接PC调试，很可能是某个PC软件在我的手机连接到PC的时候，用ADB自动安装了豌豆荚应用。\n稍一分析，发现这个偷偷安装应用的家伙是金山快盘。\n但是，我记得在金山快盘安装豌豆荚应用的时候，是会提示我的，我勾选过“不再提醒”，怎么它就热情的给我装了呢？\n看了这篇帖子，我发现自己在调试手机应用的时候Eclipse经常出现无法连接手机的情况，原因也就是金山快盘中自带的 db_iab 进程作怪。\n既然知道罪魁祸首，那么干掉他就很容易了（基于快盘 2.12.26.15）：\n完全退出快盘，退出后可以查看一下任务管理器中有没有 kuaipan.exe 进程； 打开快盘安装目录，删除 res 目录中的 adb 文件夹，如下图:\n删除快盘res adb目录[/caption] 打开 C:\\Users\\[yourname]\\AppData\\Roaming，设置 Wandoujia2 文件夹的权限； 在这个文件夹上点右键－属性－安全－高级； 在弹出对话框上把“包含继承的权限”前面的勾勾去掉； 在弹出的对话框上点“移除”； 最后依次点“确定”关掉刚才开的两个窗口。 设置成功之后，这个文件夹应该是没有任何权限：\n当然，终极的解决方案是不再使用快盘。可以使用 坚果云 或者 skydrive 。\n参考\nhttp://bbs.kuaipan.cn/thread-55525-1-1.html\n全文完 ","date":"2013-02-02","description":"","lastmod":"2013-02-02T01:42:40Z","slug":"remove_wandoujia_in_kuaipan","tags":["android"],"title":"干掉金山快盘中的豌豆荚","url":"https://blog.zengrong.net/post/remove_wandoujia_in_kuaipan/"},{"categories":["technology"],"content":"cygwin技巧2则：whereis和updatedb\n使用 updatedb 在cygwin中使用 locate 命令的时候，它提示我数据库太旧需要更新，但执行 updatedb 时，却提示 Permission denied 导致更新总是不成功。\n这种情况下，需要使用 --prunepaths 来限制不更新某些特权目录。\n例如我就不处理C盘和 /proc：\n1updatedb --prunepaths=\u0026#39;/proc /cygdrive/c\u0026#39; 获取 whereis cygwin中没有包含 whereis 和 more，要得到这些命令，可以安装 util-linux 包。\nutil-linux中包含的所有程序如下：\naddpart, agetty, blockdev, cal, cfdisk, chfn, chkdupexe, chrt, chsh, col, colcrt, colrm, column, ctrlaltdel, cytune, ddate, delpart, display-services, dmesg, elvtune, fastboot, fasthalt, fdformat, fdisk, flock, fsck.cramfs, fsck.minix, getopt, halt, hexdump, hwclock, initctl, ionice, ipcrm, ipcs, isosize, kill, last, line, logger, login, look, losetup, mcookie, mesg, mkfs, mkfs.bfs, mkfs.cramfs, mkfs.minix, mkswap, more, mount, namei, need, newgrp, partx, pg, pivot_root, provide, ramsize, raw, rdev, readprofile, reboot, rename, renice, reset, rev, rootflags, script, scriptreplay, setsid, setterm, sfdisk, shutdown, simpleinit, swapoff, swapon, taskset, tailf, tunelp, ul, umount, vidmode, vipw, wall, whereis, and write\n如果不喜欢cygwin的setup.exe工具，可以试试用 apt-cyg 来安装：\n1apt-cyg install util-linux 其他 locate 和 whereis 都是linux中的查找命令，它们的区别和用法可以看这里：Linux的五个查找命令 更详细的updatedb排除列表，可以看这里（自行跨墙）：Getting updatedb on cygwin to prune paths with spaces ","date":"2013-01-30","description":"","lastmod":"2013-01-30T08:15:23Z","slug":"cygwin_two_tips","tags":["cygwin","linux"],"title":"cygwin技巧2则：whereis和updatedb","url":"https://blog.zengrong.net/post/cygwin_two_tips/"},{"categories":["technology"],"content":"为了迎接GitHub解封，我怀着悸动无笔的心情为ANE Toolkit增加了新的功能。今天天气真好，晴空万里无云。\n新功能 新增PowerManager的大部分功能，详见：http://developer.android.com/reference/android/os/PowerManager.html 使用 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 的方法来保持当前Activity常亮 所有可用方法及其用法说明\n举例说明 这是acquire方法的定义：\n1/** 2 * 执行对电源的设置 3 * @param $flags 要设置的电源模式，具体使用方法见：http://developer.android.com/reference/android/os/PowerManager.html 4 * @param $timeout 如果为大于0，则为超时锁，否则为永久锁。 5 * @param $counted 如果值为true，则为计数锁，否则为不计数锁。 6 * @see http://developer.android.com/reference/android/os/PowerManager.html 7 * 需要WAKE_LOCK权限 8 * @see http://developer.android.com/reference/android/Manifest.permission.html#WAKE_LOCK 9 */ 10public function acquire($flags:int, $timeout:int=0, $counted:Boolean=true):void 11{ 12 if($flags\u0026lt;=0) throw new RangeError(\u0026#39;请提供一个正确的flags!\u0026#39;); 13 _extension.call(PowerFunction.ACQUIRE, $flags, $timeout); 14} 在你的ActionScript项目中这样使用它：\n1//FLAG的取值要看这里：http://developer.android.com/reference/android/os/PowerManager.html 2private static const POWER_FLAG:int = 0x0000001a; 3//禁止设备休眠，永久锁，不计数锁 4ANEToolkit.power.acquire(POWER_FLAG, 0, false); Android推荐我们使用使用 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 来保持当前Activity常亮，下面是个例子：\n1//在主Sprite被加入舞台的时候执行一次就可以了。 2this.addEventListener(Event.ADDED_TO_STAGE, init); 3 4private function init($evt:Event):void 5{ 6 ANEToolkit.power.flagKeepScreenOn(); 7} 更多内容和ANE下载，请看这里：https://blog.zengrong.net/anetoolkit/\n","date":"2013-01-25","description":"","lastmod":"2013-01-25T02:56:23Z","slug":"ane_toolkit_powermanager","tags":["air","android","ane"],"title":"ANE Toolkit增加电源管理功能","url":"https://blog.zengrong.net/post/ane_toolkit_powermanager/"},{"categories":["technology"],"content":"【转】HTTP POST GET 本质区别详解\n原文地址：http://blog.csdn.net/gideal_wang/article/details/4316691 原文作者：gideal_wang\n感谢原文作者写出这篇通俗易懂的文章。转载过程中，对格式和部分用语做了修改。\n一 原理区别 一般在浏览器中输入网址访问资源都是通过GET方式；在FORM提交中，可以通过Method指定提交方式为GET或者POST，默认为GET提交。\nHttp定义了与服务器交互的不同方法，最基本的方法有4种，分别是GET，POST，PUT，DELETE。\nURL全称是资源描述符，我们可以这样认 为：一个URL地址，它用于描述一个网络上的资源，而HTTP中的GET，POST，PUT，DELETE就对应着对这个资源的查 ，改 ，增 ，删 4个操作。到这里，大家应该有个大概的了解了，GET一般用于获取/查询 资源信息，而POST一般用于更新 资源信息(个人认为这是GET和POST的本质区别，也是协议设计者的本意，其它区别都是具体表现形式的差异 )。\n根据HTTP规范，GET用于信息获取，而且应该是安全的和幂等的 。\n所谓安全的意味着该操作用于获取信息而非修改信息。换句话说，GET请求一般不应产生副作用。就是说，它仅仅是获取资源信息，就像数据库查询一样，不会修改，增加数据，不会影响资源的状态。注意：这里安全的含义仅仅是指是非修改信息。 幂等的意味着对同一URL的多个请求应该返回同样的结果。这里我再解释一下幂等 这个概念： 幂等 （idempotent、idempotence）是一个数学或计算机学概念，常见于抽象代数中。 幂等有以下几种定义： 对于单目运算，如果一个运算对于在范围内的所有的一个数多次进行该运算所得的结果和进行一次该运算所得的结果是一样的，那么我们就称该运算是幂等的。比如绝对值运算就是一个例子，在实数集中，有abs(a) = abs(abs(a)) 。 对于双目运算，则要求当参与运算的两个值是等值的情况下，如果满足运算结果与参与运算的两个值相等，则称该运算幂等，如求两个数的最大值的函数，有在在实数集中幂等，即max(x,x) = x 。 上述解释后，应该可以理解GET幂等的含义了。 但在实际应用中，以上2条规定并没有这么严格。引用别人文章的例子：比如，新闻站点的头版不断更新。虽然第二次请求会返回不同的一批新闻，该操 作仍然被认为是安全的和幂等的，因为它总是返回当前的新闻。从根本上说，如果目标是当用户打开一个链接时，他可以确信从自身的角度来看没有改变资源即可。\n根据HTTP规范，POST表示可能修改变服务器上的资源的请求 。继续引用上面的例子：还是新闻以网站为例，读者对新闻发表自己的评论应该通过POST实现，因为在评论提交后站点的资源已经不同了，或者说资源被修改了。\n上面大概说了一下HTTP规范中，GET和POST的一些原理性的问题。但在实际的做的时候，很多人却没有按照HTTP规范去做，导致这个问题的原因有很多，比如说：\n很多人贪方便，更新资源时用了GET，因为用POST必须要到FORM（表单），这样会麻烦一点。 对资源的增，删，改，查操作，其实都可以通过GET/POST完成，不需要用到PUT和DELETE。 另外一个是，早期的但是Web MVC框架设计者们并没有有意识地将URL当作抽象的资源来看待和设计 。还有一个较为严重的问题是传统的Web MVC框架基本上都只支持GET和POST两种HTTP方法，而不支持PUT和DELETE方法。 简单解释一下MVC：MVC本来是存在于Desktop程序中的，M是指数据模型，V是指用户界面，C则是控制器。使用MVC的目的是将M和V的实现代码分离，从而使同一个程序可以使用不同的表现形式。\n以上3点典型地描述了老一套的风格（没有严格遵守HTTP规范），随着架构的发展，现在出现REST(Representational State Transfer)，一套支持HTTP规范的新风格，这里不多说了，可以参考《RESTful Web Services》。\n二 表现形式区别 搞清了两者的原理区别，我们再来看一下他们实际应用中的区别。\n为了理解两者在传输过程中的不同，我们先看一下HTTP协议的格式：\nHTTP请求：\n1\u0026lt;request line\u0026gt; 2\u0026lt;headers\u0026gt; 3\u0026lt;blank line\u0026gt; 4\u0026lt;request-body\u0026gt; 在HTTP请求中，第一行必须是一个请求行（request line），用来说明请求类型、要访问的资源以及使用的HTTP版本。紧接着是一个首部（header）小节，用来说明服务器要使用的附加信息。在首部之后是一个空行，再此之后可以添加任意的其他数据[称之为主体（body）]。\nGET与POST方法实例：\n1GET /books/?sex=man\u0026amp;name=Professional HTTP/1.1 2Host: www.wrox.com 3User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) 4Gecko/20050225 Firefox/1.0.1 5Connection: Keep-Alive 6 7POST / HTTP/1.1 8Host: www.wrox.com 9User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) 10Gecko/20050225 Firefox/1.0.1 11Content-Type: application/x-www-form-urlencoded 12Content-Length: 40 13Connection: Keep-Alive 14 （----此处空一行----） 15name=Professional%20Ajax\u0026amp;publisher=Wiley 有了以上对HTTP请求的了解和示例，我们再来看两种提交方式的区别：\n1 提交的比较\n对于GET提交，请求的数据会附在URL之后（就是把数据放置在HTTP协议头中），以?分割URL和传输数据，多个参数用\u0026amp;连接；例如：login.action?name=hyddd\u0026amp;password=idontknow\u0026amp;verify=%E4%BD%A0 %E5%A5%BD。如果数据是英文字母/数字，原样发送，如果是空格，转换为+，如果是中文/其他字符，则直接把字符串用BASE64加密，得出如： %E4%BD%A0%E5%A5%BD，其中％XX中的XX为该符号以16进制表示的ASCII。\n对于POST提交，把提交的数据放置在是HTTP包的包体中。上文示例中红色字体标明的就是实际的传输数据\n因此，GET提交的数据会在地址栏中显示出来，而POST提交，地址栏不会改变\n2 传输数据的大小\n首先声明：HTTP协议没有对传输的数据大小进行限制，HTTP协议规范也没有对URL长度进行限制。\n而在实际开发中存在的限制主要有：\nGET:特定浏览器和服务器对URL长度有限制，例如IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器，如Netscape、FireFox等，理论上没有长度限制，其限制取决于操作系统的支持。\n因此对于GET提交时，传输数据就会受到URL长度的限制。\nPOST:由于不是通过URL传值，理论上数据不受限。但实际各个WEB服务器会规定对post提交数据大小进行限制，Apache、IIS6都有各自的配置。\n3 安全性\nPOST的安全性要比GET的安全性高。注意：这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改，而这 里安全的含义是真正的Security的含义，比如：通过GET提交数据，用户名和密码将明文出现在URL上，因为(1)登录页面有可能被浏览器缓存， (2)其他人查看浏览器的历史纪录，那么别人就可以拿到你的账号和密码了，除此之外，使用GET提交数据还可能会造成Cross-site request forgery攻击\n4 Http get,post,soap协议都是在http上运行的\nget：请求参数是作为一个key/value对的序列（查询字符串）附加到URL上的查询字符串的长度受到web浏览器和web服务器的限制（如IE最多支持2048个字符），不适合传输大型数据集同时，它很不安全 post：请求参数是在http标题的一个不同部分（名为entity body）传输的，这一部分用来传输表单信息，因此必须将Content-type设置为:application/x-www-form-urlencoded。post设计用来支持web窗体上的用户字段，其参数也是作为key/value对传输。但是，它不支持复杂数据类型，因为post没有定义传输数据结构的语义和规则。 soap：是http post的一个专用版本，遵循一种特殊的xml消息格式，Content-type设置为: text/xml，任何数据都可以xml化 三 HTTP响应 1 HTTP响应格式\n1\u0026lt;status line\u0026gt; 2\u0026lt;headers\u0026gt; 3\u0026lt;blank line\u0026gt; 4[\u0026lt;response-body\u0026gt;] 在响应中唯一真正的区别在于第一行中用状态信息代替了请求信息。状态行（status line）通过提供一个状态码来说明所请求的资源情况。\n2 HTTP响应实例\n1HTTP/1.1 200 OK 2Date: Sat, 31 Dec 2005 23:59:59 GMT 3Content-Type: text/html;charset=ISO-8859-1 4Content-Length: 122 5＜html＞ 6＜head＞ 7＜title＞Wrox Homepage＜/title＞ 8＜/head＞ 9＜body＞ 10＜!-- body goes here --＞ 11＜/body＞ 12＜/html＞ 3 最常用的状态码\n200 (OK): 找到了该资源，并且一切正常。 304 (NOT MODIFIED): 该资源在上次请求之后没有任何修改。这通常用于浏览器的缓存机制。 401 (UNAUTHORIZED): 客户端无权访问该资源。这通常会使得浏览器要求用户输入用户名和密码，以登录到服务器。 403 (FORBIDDEN): 客户端未能获得授权。这通常是在401之后输入了不正确的用户名或密码。 404 (NOT FOUND): 在指定的位置不存在所申请的资源。 四 完整示例： HTTP GET\n发送\n1GET /DEMOWebServices2.8/Service.asmx/CancelOrder?UserID=string\u0026amp;PWD=string\u0026amp;OrderConfirmation=string HTTP/1.1 2Host: api.efxnow.com 回复\n1HTTP/1.1 200 OK 2Content-Type: text/xml; charset=utf-8 3Content-Length: length 4 5\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 6\u0026lt;objPlaceOrderResponse xmlns=\u0026#34;https://api.efxnow.com/webservices2.3\u0026#34;\u0026gt; 7\u0026lt;Success\u0026gt;boolean\u0026lt;/Success\u0026gt; 8\u0026lt;ErrorDescription\u0026gt;string\u0026lt;/ErrorDescription\u0026gt; 9\u0026lt;ErrorNumber\u0026gt;int\u0026lt;/ErrorNumber\u0026gt; 10\u0026lt;CustomerOrderReference\u0026gt;long\u0026lt;/CustomerOrderReference\u0026gt; 11\u0026lt;OrderConfirmation\u0026gt;string\u0026lt;/OrderConfirmation\u0026gt; 12\u0026lt;CustomerDealRef\u0026gt;string\u0026lt;/CustomerDealRef\u0026gt; 13\u0026lt;/objPlaceOrderResponse\u0026gt; HTTP POST\n发送\n1POST /DEMOWebServices2.8/Service.asmx/CancelOrder HTTP/1.1 2Host: api.efxnow.com 3Content-Type: application/x-www-form-urlencoded 4Content-Length: length 5 6UserID=string\u0026amp;PWD=string\u0026amp;OrderConfirmation=string 回复\n1HTTP/1.1 200 OK 2Content-Type: text/xml; charset=utf-8 3Content-Length: length 4 5\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 6\u0026lt;objPlaceOrderResponse xmlns=\u0026#34;https://api.efxnow.com/webservices2.3\u0026#34;\u0026gt; 7\u0026lt;Success\u0026gt;boolean\u0026lt;/Success\u0026gt; 8\u0026lt;ErrorDescription\u0026gt;string\u0026lt;/ErrorDescription\u0026gt; 9\u0026lt;ErrorNumber\u0026gt;int\u0026lt;/ErrorNumber\u0026gt; 10\u0026lt;CustomerOrderReference\u0026gt;long\u0026lt;/CustomerOrderReference\u0026gt; 11\u0026lt;OrderConfirmation\u0026gt;string\u0026lt;/OrderConfirmation\u0026gt; 12\u0026lt;CustomerDealRef\u0026gt;string\u0026lt;/CustomerDealRef\u0026gt; 13\u0026lt;/objPlaceOrderResponse\u0026gt; SOAP 1.2\n发送\n1POST /DEMOWebServices2.8/Service.asmx HTTP/1.1 2Host: api.efxnow.com 3Content-Type: application/soap+xml; charset=utf-8 4Content-Length: length 5 6\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 7\u0026lt;soap12:Envelope xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xmlns:xsd=\u0026#34;http://www.w3.org/2001/XMLSchema\u0026#34; xmlns:soap12=\u0026#34;http://www.w3.org/2003/05/soap-envelope\u0026#34;\u0026gt; 8\u0026lt;soap12:Body\u0026gt; 9 \u0026lt;CancelOrder xmlns=\u0026#34;https://api.efxnow.com/webservices2.3\u0026#34;\u0026gt; 10 \u0026lt;UserID\u0026gt;string\u0026lt;/UserID\u0026gt; 11 \u0026lt;PWD\u0026gt;string\u0026lt;/PWD\u0026gt; 12 \u0026lt;OrderConfirmation\u0026gt;string\u0026lt;/OrderConfirmation\u0026gt; 13 \u0026lt;/CancelOrder\u0026gt; 14\u0026lt;/soap12:Body\u0026gt; 15\u0026lt;/soap12:Envelope\u0026gt; 回复\n1HTTP/1.1 200 OK 2Content-Type: application/soap+xml; charset=utf-8 3Content-Length: length 4 5\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 6\u0026lt;soap12:Envelope xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xmlns:xsd=\u0026#34;http://www.w3.org/2001/XMLSchema\u0026#34; xmlns:soap12=\u0026#34;http://www.w3.org/2003/05/soap-envelope\u0026#34;\u0026gt; 7/\u0026lt;soap12:Body\u0026gt; 8 \u0026lt;CancelOrderResponse xmlns=\u0026#34;https://api.efxnow.com/webservices2.3\u0026#34;\u0026gt; 9 \u0026lt;CancelOrderResult\u0026gt; 10 \u0026lt;Success\u0026gt;boolean\u0026lt;/Success\u0026gt; 11 \u0026lt;ErrorDescription\u0026gt;string\u0026lt;/ErrorDescription\u0026gt; 12 \u0026lt;ErrorNumber\u0026gt;int\u0026lt;/ErrorNumber\u0026gt; 13 \u0026lt;CustomerOrderReference\u0026gt;long\u0026lt;/CustomerOrderReference\u0026gt; 14 \u0026lt;OrderConfirmation\u0026gt;string\u0026lt;/OrderConfirmation\u0026gt; 15 \u0026lt;CustomerDealRef\u0026gt;string\u0026lt;/CustomerDealRef\u0026gt; 16 \u0026lt;/CancelOrderResult\u0026gt; 17 \u0026lt;/CancelOrderResponse\u0026gt; 18\u0026lt;/soap12:Body\u0026gt; 19\u0026lt;/soap12:Envelope\u0026gt; ","date":"2013-01-24","description":"","lastmod":"2013-01-24T14:35:04Z","slug":"http_protocol_introduce","tags":["html","http","netconnection"],"title":"【转】HTTP POST GET 本质区别详解","url":"https://blog.zengrong.net/post/http_protocol_introduce/"},{"categories":["technology"],"content":"在中国大陆调试Android In-app-billing\n2013-03-01更新：加入简单解决方案。\n历尽艰难险阻终于在中国大陆调试Google Play In-app Billing成功，过程记录如下，方便后来之人。\n简单解决方案 找一台带有Google Play的手机，不必ROOT； 找一个米国VPN，在手机上拨通； 在手机上，删除Google Play的缓存，同时卸载Google Play的更新。\nGoogle Play使用普通方法是不能被卸载的，因此可以放心的卸载Google Play的更新； 重启一次手机，在手机上拨通VPN； 打开Google Play，如果能够看到付费应用，就说明手机已经支持支付了； 登录Google Wallet，绑定一张双币种信用卡（亲测招行和民生银行均可用），测试支付。 心得：\n经过多台手机的测试，可以确定，手机不必Root，不必安装Market Enable，也不必安装米国手机卡，就可以实现Google Play支付。 我猜测，Google Play检测手机是否可以支付，是通过网络位置判定的。但是由于缓存的原因，临时改用米国VPN是不管用的，必须要清除缓存和Google Play的更新。 Google Play看到付费应用之后，理论上就可以进行支付了。如果测试还有问题，只需要稍等片刻，或者多测试几次，或者重启手机再测试。 一定要保证，测试的过程中，你的手机是全程VPN的。 其实，直接用路由器做梯子，是最保险的。\n如果上面的简单方案不管用，还是尝试下面的复杂解决方案把。\n复杂解决方案 一、错误描述 Android的文档写得相当好，根据下面两篇教程，理应顺利通过调试。\nIn-app Billing Overview Preparing Your In-app Billing Application 可郁闷的事情，是在使用Google提供的TrivalDrive Sample进行调试的时候，Sample程序总是抛出异常： Error checking for billing v3 support. (response: 3:Billing Unavailable)\n二、原因 这个异常对应的是 BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE 错误，Google对它的定义是：\nBilling API version is not supported for the type requested\n详见这里：In-app Billing Reference (IAB Version 3)\n这个含义其实是非常模糊的，我用标准和专业的中文翻译一下，它应该是这个意思：\n老子就是不支持你在天朝使用，元芳，你能怎么着？\n血泪教训啊，为了翻译上面那句话，秒秒钟几十亿上下的我的track遍及大江南北，城墙内外……\n三、解决条件 Android文档中提供的什么账户啊、产品啊、Developer Console之类的我就不说了，中文不好的就去看英文，英文好的就去看英文。我要说的内容绝不违反党的政策，和12306、GitHub以及城墙都无关。我只是欺骗了万恶的美帝。\nAndroid中的In-app billing其实是调用Google Play实现的。因此要解决上面的问题，首先要让Google Play支持支付。\n默认的情况下，在Google Play Store中，我们只能看到免费的App，且无法搜索到收费App。要完成这一步，需要以下条件：\nRoot过的Android手机一枚； 跨墙工具一套（推荐美帝VPN）； Market Enabler或者Market Unlocker； 双币种信用卡一张（亲测招行VISA可用）； 美帝通信地址和邮编（如果木有亲戚朋友，就随便找个大学地址）； Google账户一个。 四、解决流程 还是先说句废话：开机有风险，用户须谨慎\nGoogle帐号绑定信用卡\nPC拨上美帝VPN，进入 https://wallet.google.com ，账户选择美国，地址邮编填写上面准备好的，然后绑定一张双币种信用卡。如果绑定成功，你应该会收到银行短信说有$1的交易。不要着急，这个交易只是预授权费用，是为了检测你的卡是否正常，不用还款的。详见：Authorizations。 修改运营商\n运行上面准备的 Market Unlocker，按照界面提示开启Enable Unlocker 和 Auto Unlock 选项。这个操作将运营商改成 Verizon。 取消定位服务（我不确定是否必须）\n在Android系统设置中把 使用wifi定位 和 用定位数据改善google服务 关闭。 清除Google Play Store的缓存和更新\n在Android的App管理中，清除Google Play Stroe的程序数据和缓存。Android会有一个提醒，不必管它，哪些东西都会回来的。如果Google Play更新过，也卸载更新。Google Play Store一般是保存在Rom中的，所以不会被卸载，只能卸载更新。 完成\n在Android设备中拨上美帝VPN，重新打开Google Play Store。如果你能在首页看到收费应用，就说明已经成功了。 五、参考 以下是参考资料中有价值的一小部分，大部分资料都需要跨墙：\n招行信用卡绑定Google Checkout 有人在国内成功使用过google market的in-app-billing吗？ 解决“此商品无法在您设备所在的国家使用”（更新） 关于google checkout绑定信用卡 In App Billing Implementaion In-app Billing Overview （感谢这位苦B的in china兄弟，让我不至于偏的太远） Android In App BIlling v3 doesn't work Nexus 7 这两篇可能用得上，一并放这里。第一篇也是字字血泪啊！！\n串接 Google Play In-app-billing 易犯的錯誤 使用针对 Android 的应用内付费 Adobe AIR 原生扩展 ","date":"2013-01-23","description":"","lastmod":"2013-01-23T15:59:42Z","slug":"use_android_iab_in_china","tags":["android","google","pay","sdk"],"title":"在中国大陆调试Android In-app-billing","url":"https://blog.zengrong.net/post/use_android_iab_in_china/"},{"categories":["technology"],"content":"在bash中替换屏幕上显示的行\n在编写一些比较耗时的程序时，我经常需要在屏幕上显示一些进度信息。默认的echo在输出的时候，会将每一条进度信息换行显示，就像下面这样：\n1for i in {1..3};do 2\tsleep 1 3\techo \u0026#34;第$i次\u0026#34; 4done 5# 输出 6第1次 7第2次 8第3次 如果希望每次显示替换掉前一次显示，可以这样处理：\n1for i in {1..3};do 2\tsleep 1 3\techo -en \u0026#34;\\r第$i次\u0026#34; 4done -n 参数的作用是不在echo的结尾自动加入换行符，-e 参数的作用是对反斜杠字符进行转义。把它们用在一起，对输出文本中的 \\r 进行了转义，相当于在上一行内容之后输入了一次回车，清空了当前行的显示。\n下面的代码运行后，“倒计时完毕”字样会紧接在“第3次”之后，这个应该如何处理呢？\n1for i in {1..3};do 2\tsleep 1 3\techo -en \u0026#34;\\r第$i次\u0026#34; 4done 5echo \u0026#39;倒计时完毕\u0026#39; ","date":"2013-01-14","description":"","lastmod":"2013-01-14T08:51:34Z","slug":"replace_last_line_on_screen_in_bash","tags":["bash","linux"],"title":"在bash中替换屏幕上显示的行","url":"https://blog.zengrong.net/post/replace_last_line_on_screen_in_bash/"},{"categories":["technology"],"content":"使用bash从SVN和Git中获取顺序版本号\n在进行自动部署的时候，经常需要用脚本获取程序的最新版本号，下面是我的两个解决方案。\nfor SVN 1# 获取XML版本的svn信息，这样可以避免不同语言的问题 2xml=`svn info --xml --incremental` 3# 我们可以获取到2个版本号，一个是最新版本库版本号，一个是自己的提交版本号。删除自己提交的版本号。 4revision=`echo \u0026#34;$xml\u0026#34;|sed \u0026#39;/revision/!d\u0026#39;|sed \u0026#39;$d\u0026#39;` 5# 提取出版本号的数字部分 6echo $revision|sed \u0026#39;s/revision=\u0026#34;\\([0-9]\\+\\)\u0026#34;\u0026gt;\\?/\\1/\u0026#39; for Git Git采用的是SHA散列码作为版本号，因此它没有顺序的版本号。但我们可以通过统计Git版本库的提交次数来获得一个顺序版本号。\n1# 基准版本号默认是1，可以通过传递一个参数修改 2get_version() 3{ 4\tlocal base=${1:-1} 5\techo $((`git rev-list --all|wc -l` + $base)) 6} 7get_version 7000 这个版本对网上搜到的那个被普遍转载的版本做了简化和调整。网上那个版本写得比较复杂，例如awk的使用没有必要，而且要统计所有提交，应该用 git rev-list --all 参数，而不是用 git rev-list HEAD。\n","date":"2013-01-12","description":"","lastmod":"2013-01-12T08:53:37Z","slug":"get_sequential_version_number_in_svn_and_git_use_bash","tags":["bash","git","linux","svn"],"title":"使用bash从SVN和Git中获取顺序版本号","url":"https://blog.zengrong.net/post/get_sequential_version_number_in_svn_and_git_use_bash/"},{"categories":["technology"],"content":"使用Cygwin在Windows中以服务方式安装Lighttpd\n缘起 自从前段时间对服务器进行了从Apache到Lighttp 的转换之后，服务器运行异常的稳定。Lighttpd占用内存非常小，配置文件也很简单，这让我萌生了把所有服务器都从 Apache 转到 Lighttpd 的想法。\n但这毕竟是个浩大的工程，而且运行正稳定的服务器也不必这么折腾。于是先从我的本机开刀。\n我的每台工作电脑都会安装 XAMPP 套件，主要是用于开发和共享，以及存放一些html格式的文档以方便浏览。\n而现在XAMPP可以退休了。\n系统需求 Lighttpd并没有官方的Windows版本，本文基于Cygwin 来安装 Lighttpd。\n本文假设你了解Cygwin的基本用法和配置，了解如何使用Cygwin的setup来安装新的包。\n如果你不喜欢setup.exe那个界面，可以试试 apt-cyg 。\n安装cygserver cygserver 是为Cygwin作为后台服务运行而设计的，默认安装Cygwin的时候并没有启动它。我们需要打开它，并将它作为Windows的标准服务来安装。\n在Cygwin中输入命令：\n1cygserver-config 按照界面提示进行cygserver的安装，并同意将其安装为服务。\n运行该服务：\n1cygrunsrv --start cygserver cygrunsrv其实是启动的Windows标准服务cygserver，这个命令也可以在Cmd下这样做：\n1net start cygserver 或者也可以按下按键 Windows+R ，键入 services.msc 回车，找到 CYGWI cygwerver服务，并启动之。\n这三种启动服务的方法的作用都一样，下面统一使用 cygrunsrv 的方法。\n查看cygserver服务的状态：\n1cygrunsrv --qurey cygserver 2 3# 显示 4Service : cygserver 5Display name : CYGWIN cygserver 6Current State : Running 7Controls Accepted : Stop 8Command : /usr/sbin/cygserver 测试Lighttpd 安装Lighttpd包，由于 系统需求 中提到的假设，安装过程略。\n测试Lighttpd的运行：\n1/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf 如果出现下面这样的报错，需要调整lighttpd.conf中的某些和网络相关的配置。因为Lighttpd是运行在Linux下的，默认的配置在Windows下面是不受支持的。\n12013-01-11 11:01:59: (/usr/src/ports/lighttpd/lighttpd-1.4.32-2/src/lighttpd-1.4.32/src/configfile.c.1339) the selected event-handler in unknown or not supported: linux-sysepoll 22013-01-11 11:01:59: (/usr/src/ports/lighttpd/lighttpd-1.4.32-2/src/lighttpd-1.4.32/src/server.c.646) setting default values failed 32013-01-11 11:07:36: (/usr/src/ports/lighttpd/lighttpd-1.4.32-2/src/lighttpd-1.4.32/src/network.c.260) warning: please use server.use-ipv6 only for hostnames, not without server.bind / empty address; your config will break if the kernel default for IPV6_V6ONLY changes 42013-01-11 11:07:36: (/usr/src/ports/lighttpd/lighttpd-1.4.32-2/src/lighttpd-1.4.32/src/network.c.802) server.network-backend has a unknown value: linux-sendfile 52013-01-11 11:11:34: (/usr/src/ports/lighttpd/lighttpd-1.4.32-2/src/lighttpd-1.4.32/src/server.c.915) can\u0026#39;t have more connections than fds/2: 1024 256 涉及的配置项如下，将他们全部注释：\nserver.event-handler 约在181行 server.use-ipv6 约在93行 server.network-backend 约在191行 server.max-fds 约在207行 server.max-connections 约在245行 如果在运行的时候提示目录权限之类问题，则可以修改 Lighttpd.conf 中的 16~20 行配置，设置正确的目录和权限。\n如果执行正常，那么不会输出任何信息，Lighttpd会一直在前台运行。\n此时可以去浏览器中输入 http://localhost 测试一下，当然，要看到内容，需要确保你的Lighttpd.conf中的主页地址配置正确，即使你看到的是404错误，其实也说明你的服务器配置成功了，只是没有默认的页面而已。\n将Lighttpd安装成服务 使用cygrunsrv可以将Lighttpd安装成Windows系统服务\n1cygrunsrv -I lighttpd -d \u0026#39;CYGWIN lighttpd\u0026#39; -p \u0026#39;/usr/sbin/lighttpd\u0026#39; -a \u0026#39;-D -f /etc/lighttpd/lighttpd.conf\u0026#39; 然后运行它\n1cygrunsrv --start lighttpd 查询运行状态\n1cygrunsrv --query lighttpd 2 3Service : lighttpd 4Display name : CYGWIN lighttpd 5Current State : Running 6Controls Accepted : Stop 7Command : /usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf 参考 http://redmine.lighttpd.net/boards/2/topics/3235 http://www.xker.com/page/e2007/1022/36592.html http://webcache.googleusercontent.com/search?q=cache:ngnWxnFusY0J:zandyware.com/blog/2010/11/23/install-lighttpd-as-a-service-in-cygwin/\u0026amp;hl=zh-CN\u0026amp;tbo=d\u0026amp;strip=1 http://fabrizioballiano.net/2012/05/02/cygwin-lighttpd-php-mysql-the-perfect-dev-setup-for-windows/ http://linuxzh.3322.org/?p=476 http://guoyoooping.blog.163.com/blog/static/1357051832010101794654132/ http://cygwin.com/cygwin-ug-net/using-cygserver.html http://ipggi.wordpress.com/2011/07/01/cygwin-walkthrough-and-beginners-guide-is-it-linux-for-windows-or-a-posix-compatible-alternative-to-powershell/ ","date":"2013-01-11","description":"","lastmod":"2013-01-11T03:24:38Z","slug":"install_lighttpd_as_service_in_cygwin","tags":["cygwin","http","linux","windows"],"title":"使用Cygwin在Windows中以服务方式安装Lighttpd","url":"https://blog.zengrong.net/post/install_lighttpd_as_service_in_cygwin/"},{"categories":["technology"],"content":"这个问题是我在知乎上的一个回答：http://www.zhihu.com/question/20700892/answer/15902820\n我猜想是历史原因。\nAS提供的NetStream类比较特殊。它既支持本地Video对象播放，也支持基于HTTP的流媒体播放。最重要的，是它支持Adobe自家的Flash Media Server的实时流和点播流。\nFlash Media Server（以下简称FMS）是一个重量级的产品，它在AS 1.0/2.0时代就推出了。FMS最初叫做Flash Communication Server（以下简称FCS），它当时是和（Flash MX/Flash MX 2004）|（Flash Player 6.0/7.0）|（AS1.0/2.0）配合使用的。\n上面一段的括号、斜杠、分隔符很多，如果看不懂，可以先看这篇文章：\nActionscript,AS3,MXML,Flex,Flex Builder,Flash Builder,Flash,AIR,Flash Player之关系\n不要怪我，Adobe的产品线确实就是如此的混乱（这和收购Macromedia无关）。\nFMS是个服务器，它的升级不可能与Flash Player这个客户端同步。这就导致了可能出现Flash 6/AS1的客户端程序连接目前最新的FMS 5.0服务器的情况；当然也可能会出现Flash 11.5/AS 3.0客户端程序连接FCS 1.5的情况。为了保证语法一致性和向下兼容，Adobe估计就只能在NetStream这个特殊的类上保持这种特殊的语法了。\n其实这种语法也不算怎么“特殊”。同样的情况也还发生在NetConnection类上。可以参考NetConnection的client属性和call方法：\nhttp://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/flash/net/NetConnection.html#client\n为了向下兼容，AS3 的继承机制本来就是两套：固定属性继承和原型(prototype)继承。client这种特殊对象的存在，在原型继承的基础上是很合理的。可以参考这里：\nhttp://help.adobe.com/zh_CN/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f3f.html\n况且，FMS中使用的语言是Javascript。Javascript采用的是原型继承。所以对于一个服务器端和客户端都要使用的功能/类/方法，而且要兼顾客户端的AS1/AS2/AS3的变迁，采用这种方式应该是再合理不过。\n以上全是我的猜测。可能不正确。\n知乎好像有几位Adobe员工，正确答案你可以问问他们。\n附：FMS中的NetStream、Stream、NetConnectioin、Client\nhttp://help.adobe.com/en_US/adobemediaserver/ssaslr/WS5b3ccc516d4fbf351e63e3d11a11afc95e-7e42SSASLR.2.3.html#WS5b3ccc516d4fbf351e63e3d11a11afc95e-7f38SSASLR.2.3 http://help.adobe.com/en_US/adobemediaserver/ssaslr/WS5b3ccc516d4fbf351e63e3d11a11aff5ba-7d13SSASLR.2.3.html http://help.adobe.com/en_US/adobemediaserver/ssaslr/WS5b3ccc516d4fbf351e63e3d11a11afc95e-7ec3SSASLR.2.3.html http://help.adobe.com/en_US/adobemediaserver/ssaslr/WS5b3ccc516d4fbf351e63e3d11a11afc95e-7e76SSASLR.2.3.html#WS5b3ccc516d4fbf351e63e3d11a11afc95e-7f6fSSASLR.2.3 ","date":"2013-01-08","description":"","lastmod":"2013-01-08T15:55:16Z","slug":"why-using-onmetadata-oncuepoint-in-as3","tags":["actionscript","fms","study","zhihu"],"title":"AS3.0监听视频的metaData和cuePoint时，使用了onMetaData()和onCuePoint()类AS2.0的形式，而没用addEventListener这种更\"现代化\"的形式，为什么？","url":"https://blog.zengrong.net/post/why-using-onmetadata-oncuepoint-in-as3/"},{"categories":["use"],"content":"入手了 Samsung galaxy S3 i535，网络信号一直显示1X，不显示3G。\n打10000台问电信，客服MM说是我的信号不好造成的，可是记得原来在EVO 4G上也是从未显示过3G信号图标。\n搜索一番，发现可能性有这些：\n手机卡没有开通3G功能。这个我问过电信了，排除； 手机设置为只使用1X网络。i535根本就没有这个选项，排除； 手机信号不好。排除。 解决方案有这些：\n去电信换一张128K的国际卡；\n我是64K的卡，这个倒是可以试试。不过我用下面的方法解决了；\n国际漫游列表更新：天翼菜单--UIM卡应用--国际漫游服物--漫游列表更新\n如果手机中找不到类似菜单，可以编辑短信PRL发送到10659165，收到回复4-6说明更新成功，重启手机即可。\n我按上面的说明发送了短信，但没有收到任何短信回复。不过重启手机之后，确实能看到3G信号图标了。\n参考文章：\nhttp://bbs.xiaomi.cn/thread-2989931-1-1.html http://www.in189.com/forum.php?mod=redirect\u0026amp;goto=findpost\u0026amp;ptid=80939\u0026amp;pid=1757835 ","date":"2013-01-08","description":"","lastmod":"2013-01-08T14:10:56Z","slug":"samsung-galaxy-s3-i535-only-1x","tags":["smartphone"],"title":"中国电信不显示3G，只显示1X网络的问题","url":"https://blog.zengrong.net/post/samsung-galaxy-s3-i535-only-1x/"},{"categories":["technology"],"content":"Cygwin的包管理器：apt-cyg\nCygwin的包管理工具setup.exe实在是难用的让人蛋碎。于是就有了这样一个apt-cyg，可以提供类似于 apt-get 的体验。\napt-cyg使用bash编写，所以连编译都省了。\n安装apt-cyg 1wget http://apt-cyg.googlecode.com/svn/trunk/apt-cyg 2chmod +x apt-cyg 3mv apt-cyg /usr/local/bin/ 安装包 1apt-cyg install bc 查找包 1apt-cyg find php 设置下载站点和缓存目录 1apt-cyg -c /cygdrive/d/downloads/cygwin -m http://mirrors.163.com/cygwin/ find php 也可以把默认的缓存和下载站点存到文件中，编辑apt-cyg，找到68行：\n1#mirror=ftp://mirror.mcs.anl.gov/pub/cygwin 2#cache=/setup 3mirror=http://mirrors.163.com/cygwin 4cache=/cygdrive/d/downloads/cygwin 当然，PHP 在 cygwin的官方源中是不存在的，我们可以使用 cygwinports 提供的源：\n1$ apt-cyg -m ftp://ftp.cygwinports.org/pub/cygwinports find php 2Working directory is /cygdrive/d/downloads/software/cygwin 3Mirror is ftp://ftp.cygwinports.org/pub/cygwinports 4--2013-01-08 12:08:07-- ftp://ftp.cygwinports.org/pub/cygwinports/setup.bz2 =\u0026gt; “.listing” 5正在解析主机 ftp.cygwinports.org (ftp.cygwinports.org)... 209.132.180.131 6正在连接 ftp.cygwinports.org (ftp.cygwinports.org)|209.132.180.131|:21... 已连接。 7正在以 anonymous 登录 ... 登录成功！ 8==\u0026gt; SYST ... 完成。 ==\u0026gt; PWD ... 完成。 9==\u0026gt; TYPE I ... 完成。 ==\u0026gt; CWD (1) /pub/cygwinports ... 完成。 10==\u0026gt; PASV ... 完成。 ==\u0026gt; LIST ... 完成。 11 12 [ \u0026lt;=\u0026gt; ] 966 --.-K/s 用时 0.01s 13 142013-01-08 12:08:12 (95.4 KB/s) - “.listing” 已保存 [966] 15 16已删除 “.listing”。 17--2013-01-08 12:08:12-- ftp://ftp.cygwinports.org/pub/cygwinports/setup.bz2 18 =\u0026gt; “setup.bz2” 19==\u0026gt; 不需要 CWD。 20==\u0026gt; PASV ... 完成。 ==\u0026gt; RETR setup.bz2 ... 完成。 21长度：580198 (567K) 22 23100%[============================================================\u0026gt;] 580,198 52.2K/s 用时 16s 24 252013-01-08 12:08:29 (35.5 KB/s) - “setup.bz2” 已保存 [580198] 26 27Updated setup.ini 28 29Searching for installed packages matching php: 30php 31php-Archive_Tar 32php-Console_Getopt 33php-PEAR 34php-Structures_Graph 35# 以下省略 36...... ","date":"2013-01-08","description":"","lastmod":"2013-01-08T04:11:34Z","slug":"cygwin-package-manager-apt-cyg","tags":["cygwin","linux","php"],"title":"Cygwin的包管理器：apt-cyg","url":"https://blog.zengrong.net/post/cygwin-package-manager-apt-cyg/"},{"categories":["technology"],"content":"JSON文件和CSV文件互转工具\n工作需要，写了这么一个 JSON和CSV互转的小工具。\n界面 源码 https://github.com/zrong/json2csv\nJSON范例 1{\u0026#34;widget\u0026#34;: { 2 \u0026#34;debug\u0026#34;: \u0026#34;on\u0026#34;, 3 \u0026#34;window\u0026#34;: { 4 \u0026#34;title\u0026#34;: \u0026#34;Sample Konfabulator Widget\u0026#34;, 5 \u0026#34;name\u0026#34;: \u0026#34;main_window\u0026#34;, 6 \u0026#34;width\u0026#34;: 500, 7 \u0026#34;height\u0026#34;: 500 8 }, 9 \u0026#34;image\u0026#34;: { 10 \u0026#34;src\u0026#34;: \u0026#34;Images/Sun.png\u0026#34;, 11 \u0026#34;name\u0026#34;: \u0026#34;sun1\u0026#34;, 12 \u0026#34;hOffset\u0026#34;: 250, 13 \u0026#34;vOffset\u0026#34;: 250, 14 \u0026#34;alignment\u0026#34;: \u0026#34;center\u0026#34; 15 }, 16 \u0026#34;text\u0026#34;: { 17 \u0026#34;data\u0026#34;: \u0026#34;Click Here\u0026#34;, 18 \u0026#34;size\u0026#34;: 36, 19 \u0026#34;style\u0026#34;: \u0026#34;bold\u0026#34;, 20 \u0026#34;name\u0026#34;: \u0026#34;text1\u0026#34;, 21 \u0026#34;hOffset\u0026#34;: 250, 22 \u0026#34;vOffset\u0026#34;: 100, 23 \u0026#34;alignment\u0026#34;: \u0026#34;center\u0026#34;, 24 \u0026#34;onMouseUp\u0026#34;: \u0026#34;sun1.opacity = (sun1.opacity / 100) * 90;\u0026#34; 25 } 26}} 转换成的CSV 1widget,text,onMouseUp,sun1.opacity = (sun1.opacity / 100) * 90; 2widget,text,style,bold 3widget,text,alignment,center 4widget,text,name,text1 5widget,text,data,Click Here 6widget,image,name,sun1 7widget,image,src,Images/Sun.png 8widget,image,alignment,center 9widget,debug,on, 10widget,window,name,main_window 11widget,window,title,Sample Konfabulator Widget ","date":"2013-01-06","description":"","lastmod":"2013-01-06T09:17:52Z","slug":"json2csv","tags":["air","json"],"title":"JSON文件和CSV文件互转工具","url":"https://blog.zengrong.net/post/json2csv/"},{"categories":["technology"],"content":"从Apache到Lighttpd\n购买了阿里云服务器之后，由于内存过小，MySQL经常挂掉。挂载了虚拟内存之后，MySQL倒是不挂了，但Apache总是占用过多内存，导致磁盘频繁读取（阿里云服务器的磁盘性能就那就一个弱啊），服务器响应缓慢，甚至远程登录都无法完成，只能通过网页控制台重启服务器。\nGoogle了一堆优化512MB内存服务器的资料，设置后都没什么用处，服务器依然是每天挂2～3次。\n别以为我的服务器访问量有多么的大，整个服务器上只放了一个博客程序，IP访问量每天几百次。当然，这个博客程序就是臭名昭著的各大主机商都痛恨的WordPress。\n今天下决心把Apache换成Lighttpd，希望能解决内存不够的老大难问题。\n以下是从Apache到Lighttpd转换过程中遇到的几个新手小白问题。这不是教程，只是解决方案，因此略去了安装等基本过程。\n403 forbidden 问题：\n按照 http://wiki.ubuntu.org.cn/Lighttpd 的说明装好Lighttpd并启动之后，在访问php页面的时候，提示403 forbidden，html页面正常。\n解决\n这个403错误其实是误报，并非没有权限访问php文件，而是没有权限访问php-cgi模块。 Lighttpd安装后，默认只启用了一个mod，就是fastcgi，但没有启用php支持。必须手动启用fastcgi-php模块才支持php访问：\n1lighty-enable-mod fastcgi-php 2service lighttpd force-reload Ubuntu英文官方Wiki说得很清楚： https://wiki.ubuntu.com/Lighttpd+PHP。\n启用PHP-FPM 问题\n按照上面的方式启用的php运行在fast-cgi模式。如何启用PHP-FPM？\n解决\n建立 /etc/lighttpd/conf-availeable/10-fastcgi-fpm.conf 文件，写入如下内容：\n1server.modules += (\u0026#34;mod_fastcgi\u0026#34;) 2fastcgi.server = ( \u0026#34;.php\u0026#34; =\u0026gt; 3 (( 4 \u0026#34;host\u0026#34; =\u0026gt; \u0026#34;127.0.0.1\u0026#34;, 5 \u0026#34;port\u0026#34; =\u0026gt; \u0026#34;9000\u0026#34; 6 )) 7) 禁用fastcgi模块和fastcgi的php支持，使用刚才建立的 fastcgi-fpm 模块同时支持两者。\n1lighty-disable-mod fastcgi 2lighty-disable-mod fastcgi-php 3lighty-enable-mod fastcgi-fpm 4service lighttpd force-reload 查看phpinfo，可以看到已经是FPM支持了：\nWordPress的rewrite规则不起作用 问题\nWordPress中设定的固定链接不起作用，访问的时候显示404\n解决\nWordPress启用了固定链接功能后，会自动在网页根目录建立.htaccess文件，并在其中写入rewrite规则。Apache会读取这个规则，从而实现固定链接。\n但是Lighttpd并不兼容Apache制订的rewrite规则。因此，需要为WordPress制订Lighttpd能够支持的rewrite规则。\n我使用的规则如下：\n1url.rewrite = ( 2\t\u0026#34;^/(wp-.+).*/?\u0026#34; =\u0026gt; \u0026#34;$0\u0026#34;, 3\t\u0026#34;^/(sitemap.xml)\u0026#34; =\u0026gt; \u0026#34;$0\u0026#34;, 4\t\u0026#34;^/(xmlrpc.php)\u0026#34; =\u0026gt; \u0026#34;$0\u0026#34;, 5\t\u0026#34;^/(.+)/?$\u0026#34; =\u0026gt; \u0026#34;/index.php/$1\u0026#34; 6) 或者这个：\n1url.rewrite-final = ( 2\t# Exclude some directories from rewriting 3\t\u0026#34;^/(wp-admin|wp-includes|wp-content|gallery2)/(.*)\u0026#34; =\u0026gt; \u0026#34;$0\u0026#34;, 4\t# Exclude .php files at root from rewriting 5\t\u0026#34;^/(.*.php)\u0026#34; =\u0026gt; \u0026#34;$0\u0026#34;, 6\t# Handle permalinks and feeds 7\t\u0026#34;^/(.*)$\u0026#34; =\u0026gt; \u0026#34;/index.php/$1\u0026#34; 8) 参考 URL rewriting for wordparess and lighttpd url.rewrite for WordPress on Lighttpd Migrating from Apache to lighty URL Rewrites lighttpd虚拟主机的配置 Lighttpd, PHP with PHP-FPM, and MySQL Under Ubuntu Maverick ","date":"2013-01-01","description":"","lastmod":"2013-01-01T10:20:16Z","slug":"from-apache-to-lighttpd","tags":["apache","http","linux","wordpress","fromto"],"title":"从Apache到Lighttpd","url":"https://blog.zengrong.net/post/from-apache-to-lighttpd/"},{"categories":["impressions"],"content":"十几年了，回想起鏖战帝国的日子，还是历历在目。\n不负众望的《帝国时代II》之《被遗忘的帝国》终于发布了，30、40的大叔大伯们，可以重温一下旧梦了。\n新特性（来自）：\n增加了5个新文明\n和意大利人一同从威尼斯扬帆起航，带领黑军驰骋匈牙利平原，在南亚次大陆发动战争。 28个新科技\n包括希腊火，马扎尔掠夺，中国长城。 5个新的兵种\n保卫意大利各国的热那亚十字弓手。其他几个新文明有新的攻城兵种，骑兵，步兵和弓箭手。 新改进\n支持宽屏和更高的分辨率，与队友交流变的更容易，与Windows 7完全兼容。 安装注意事项 （译自）\n你必须安装了《帝国时代II-征服者》； 下载你的语言版本（有简体中文哦！）； 将下载的压缩包解压缩到《帝国时代II-征服者》的安装目录，例如：C:\\Program Files\\Microsoft Games\\Age of Empires II\\； 注意，解压缩的文件不会覆盖已有文件； 进入 Age2_x1 子目录，双击运行 Fix.exe 程序（该程序静默执行，不会弹出任何窗口，就好象你没有执行一样 :D）； 双击运行 age2_x2.exe，开始你的怀旧之旅吧！ 常见问题解决\nWindows7/8上的色彩问题\n运行 Age2_x1\\Fix.exe 开始单人游戏的时候，要求插入光盘\n下载 SETUPREG.exe，将其解压到《帝国时代II-征服者》安装目录，然后双击运行。 双击 age2_x2.exe 之后没动静\n检查《帝国时代II-征服者》目录是否包含 empires2.exe，如果没有，重命名 Age of Kings exe 为 empires2.exe 双击 age2_x2.exe 之后黑屏\n修改 age2_x1\\Xwndmode.dll 为 wndmode.dll，这样会进入窗口模式 在游戏封面的选项界面中设置宽屏分辨率无效（这条是zrong加的）\n进入游戏界面之后，从游戏菜单中设置就有效了 下载页面\nhttp://www.forgottenempires.net/install/\n简体中文版下载地址\nhttp://mirror.forgottenempires.net/download/fe_update_cn.zip\n","date":"2012-12-30","description":"","lastmod":"2012-12-30T15:38:01Z","slug":"1785","tags":[],"title":"《帝国时代II》之《被遗忘的帝国》安装说明","url":"https://blog.zengrong.net/post/1785/"},{"categories":["technology"],"content":"在TortoiseGit中使用SSH host\n2012-12-30更新：修改由于理解错误造成的描述错误\ngit命令行与OpenSSH 在OpenSSH中，我们可以很方便的通过编辑 ~/.ssh/config 文件来为git指定不同的host，达到使用不同的端口，不同的密钥访问git服务的目的。看看下面这个例子：\n192.168.18.18 这台服务器的SSH服务器使用2012端口，我可以使用 git clone git@server18:myrepo.git 来获取我的版本库； 我在bitbucket上有两个帐号 test 和 release，需要使用不同的密钥。我可以使用 git clone bitbucket_test:myrepo.git 和 git clone bitbucket_release:myrepo.git 来获取。\n1host server18 2 HostName 192.168.18.18 3 port 2012 4 IdentityFile ~/.ssh/server18 5 6host bitbucket_test 7 HostName bitbucket.org 8 port 22 9 User git 10 IdentityFile ~/.ssh/bitbucket_test 11 12host bitbucket_release 13 HostName bitbucket.org 14 port 22 15 User git 16 IdentityFile ~/.ssh/bitbucket_release TortoiseGit与PuTTY 那TortoiseGit怎么办？\n在安装TortoiseGit的时候，可以选择使用OpenSSH客户端还是使用Putty客户端。如果使用的是Putty客户端，那么上面的设置不会起作用，我们必须在PuTTY中进行类似的设置。步骤如下：\nTortoiseGit并没有自带PuTTY客户端，你需要先去下载一个。 在 Session 界面，设置 Host Name 和 Port： 在 Connect-\u0026gt;SSH-\u0026gt;Auth 界面，设置 Private Key： 回到 Session 界面，保存 Session。 有了 Session 之后，就可以在TortoiseGit中将这个 Session Name 作为服务器URL连接，如下图：\n我没有找到办法在 Session 设置中找到设置用户名的地方，因此如果Tortoise出现这样的提示：\n只需要把URL设置成 git@server18:sg/binary 即可。\n参考文章 使用Git、Git GUI和TortoiseGit http://code.google.com/p/tortoisegit/wiki/UsingPuTTY ","date":"2012-12-26","description":"","lastmod":"2012-12-26T02:41:48Z","slug":"use_ssh_host_in_tortoisegit","tags":["git","linux"],"title":"在TortoiseGit中使用SSH host","url":"https://blog.zengrong.net/post/use_ssh_host_in_tortoisegit/"},{"categories":["technology"],"content":"在Flash Builder 4.7的纯AS项目中使用Flex SDK\n现象 将Flash Builder升级到4.7版本后，我发现以前建立的 ActionScript 项目会默认使用 AIR SDK，而不是使用 Flex SDK。这导致我以前的项目无法使用。因为某些类依赖Flex SDK中提供的 TLF 框架，或者只有Flex SDK才有的 SpriteAssets 等Class。\n查看项目属性，会发现编译器默认为AIR SDK 3.4，且无法修改：\n解决方案 用文本编辑器打开项目文件中的 .actionScriptProperties，搜索 useFlashSDK，将其值替换成 false。\n刷新一次项目，再次查看项目属性，发现编译器已经改成了 Flex SDK。\n","date":"2012-12-20","description":"","lastmod":"2012-12-20T14:02:28Z","slug":"use_flex_sdk_in_flash_builder4.7_at_pure_actionscripot_project","tags":["air","flashbuilder","flex"],"title":"在Flash Builder 4.7的纯AS项目中使用Flex SDK","url":"https://blog.zengrong.net/post/use_flex_sdk_in_flash_builder4.7_at_pure_actionscripot_project/"},{"categories":["technology"],"content":"【译】升级Flash Builder 4.7中的AIR SDK\n原文地址：http://helpx.adobe.com/flash-builder/kb/overlay-air-sdk-flash-builder.html 本文并没有“忠于”原文翻译。\nFlash Builder 4.7自带AIR SDK 3.4版本。如果你希望使用更新版本的AIR SDK，下载并覆盖软件自带的AIR SDK版本就行了。下面是步骤：\n下载对应你操作系统版本的AIR SDK（其实也就2个版本而已……）。这个版本包含AIR SDK、AS编译器和其他必要的组件和文件； 退出Flash Builder； 备份AIR SDK\n这是可选步骤，AIR SDK默认位于下面这些目录： Windows 7(32位):\nC:\\Program Files (x86)\\Adobe\\Adobe Flash Builder 4.7\\eclipse\\plugins\\com.adobe.flash.compiler_4.7.0.349722 Windows 7(64位):\nC:\\Program Files\\Adobe\\Adobe Flash Builder 4.7 (64 Bit)\\eclipse\\plugins\\com.adobe.flash.compiler_4.7.0.349722 Mac OS:\n/Applications/Adobe Flash Builder 4.7/eclipse/plugins/com.adobe.flash.compiler_4.7.0.349722 Linux：\nnull（这个我是骗你的，AIR的Linux版被Adobe谋杀了） 备份完毕后，删除 AIRSDK 目录中的所有文件。 解压缩下载的 AIR SDK 压缩包到已经被删除所有内容的 AIRSDK 空目录中。 ","date":"2012-12-20","description":"","lastmod":"2012-12-20T12:31:44Z","slug":"Update_the_AIR_SDK_in_Flash_Builder_4.7","tags":["air","flashbuilder"],"title":"【译】升级Flash Builder 4.7中的AIR SDK","url":"https://blog.zengrong.net/post/update_the_air_sdk_in_flash_builder_4.7/"},{"categories":["news"],"content":"【译】在AIR3.5中，iOS上的SharedObject的行为改变\n原文地址：http://blogs.adobe.com/airodynamics/2012/12/10/changed-behavior-of-shared-object-on-ios-in-air-3-5/\n对于AIR3.4和AIR3.5，它们的SharedObject的保存路径不同：\nAIR 3.4:\n1AppName/Library/Application Support/com.namecompany.name/Local Store/ #SharedObjects/Filename.swf Filename.swf 就是应用程序描述文件app-xml中的 \u0026lt;Filename\u0026gt; 标签指定的值。\nAIR 3.5:\n1AppName/Library/Application Support/com.namecompany.name/Local Store/ #SharedObjects/Content.swf Content.swf 就是应用程序描述符文件app-xml中的 \u0026lt;Content\u0026gt; 标签包含的主swf的文件名。\n这可能会导致在AIR 3.4升级到AIR 3.5的时候，SharedObject中保存的数据丢失。 这个问题在 AIR3.6 中已经修复。所以当用户从AIR3.4升级到AIR3.6，那么应用程序不会丢失已经保存的SharedObject数据。\n新发布应用的SharedObject的路径为：\n1AppName/Library/Application Support/com.namecompany.name/Local Store/ #SharedObjects/Content.swf AIR 3.5的变通方案：\n如果你准备在AIR 3.5中发布你的应用，你可以重命名你的主SWF文件，以匹配 \u0026lt;Filename\u0026gt;标签，下面是个例子。\n在AIR 3.4中，你的app-xml文件中的配置如下所示:\n1\u0026lt;Filename\u0026gt;MysharedObject\u0026lt;/Filename\u0026gt; 2\u0026lt;Content\u0026gt;Root.swf\u0026lt;/Content\u0026gt; 在AIR 3.5中，将你的app-xml改成下面这样：\n1\u0026lt;Filename\u0026gt;MysharedObject\u0026lt;/Filename\u0026gt; 2\u0026lt;Content\u0026gt;MysharedObject.swf\u0026lt;/Content\u0026gt; 同时，也要重命名你的主swf文件名从 Root.swf 改为 MysharedObject.swf。\n用这种方法，在AIR3.4升级到AIR3.5的时候，SharedObject数据会自动保存。\n","date":"2012-12-17","description":"","lastmod":"2012-12-17T03:39:26Z","slug":"Changed_behavior_of_Shared_Object_on_iOS_in_AIR_3.5","tags":["air","ios","sharedobject"],"title":"【译】在AIR3.5中，iOS上的SharedObject的行为改变","url":"https://blog.zengrong.net/post/changed_behavior_of_shared_object_on_ios_in_air_3.5/"},{"categories":["technology"],"content":"我在为 服务器 安装系统的时候，并没有挂载swap分区。把博客转过来之后，发现 MySQL时不时就罢工 ，原因就是内存不足。看来必须要挂载一个swap分区才好。\n但是，我在对数据盘进行分区的时候，把整个磁盘都用上了，并没用预留空间。swap分区是行不通了，只能试试swap文件。\n建立一个有连续空间的空白文件 服务器的物理内存是512MB，按照1.5~2倍原则，我将swap文件设置为1GB。\n1#root@aliyun:/srv# dd if=/dev/zero of=SWAPFILE bs=1024 count=1048576 21048576+0 records in 31048576+0 records out 41073741824 bytes (1.1 GB) copied, 59.7957 s, 18.0 MB/s 使用swap文件 使用 swapon 命令让系统使用这个文件作为swap文件。但是这个文件不能直接使用，否则会报错：\n1root@aliyun:/srv# swapon swapfile 2swapon: /srv/swapfile: read swap header failed: Invalid argument 必须先使用 mkswap 将文件格式化成swap格式（不知道为什么会少了4KB）：\n1root@aliyun:/srv# mkswap SWAPFILE 1048576 2Setting up swapspace version 1, size = 1048572 KiB 3no label, UUID=1aaed031-33ef-479b-a9a4-2f008a7bbb2f 使用格式化完毕的文件：\n1root@aliyun:/srv# swapon SWAPFILE 查看文件使用情况：\n1root@aliyun:/srv# swapon -s 2Filename Type Size Used Priority 3/srv/SWAPFILE file 1048572 95852 -1 加入自动启用 为避免重启后swapfile生效，可以将启用swap的代码加入启动文件中，对于ubuntu server，编辑 /etc/rc.local 文件，加入以下内容（具体文件路径自定）：\n1swapon /srv/SWAPFILE 或者\n修改 /etc/fstab 文件，加入以下内容：\n1/srv/SWAPFILE swap swap defaults 0 0 ","date":"2012-12-16","description":"","lastmod":"2012-12-16T13:22:52Z","slug":"mount_swapfile","tags":["linux","ubuntu"],"title":"Ubuntu Server挂载swap文件","url":"https://blog.zengrong.net/post/mount_swapfile/"},{"categories":["technology"],"content":"2012-12-16更新：这方法貌似没有什么用，MySQL依然一天挂掉3次，给服务器加上swap文件 试试。\n使用阿里云服务器的第二天，我就收到阿里云的提醒短信，说网站挂掉了。\n上网站一看，提示数据库连接错误，重启MySQL了事。\n没想到11点钟的时候MySQL再次挂掉了，上服务器一看，原来是MySQL进程被Kill了，原因是 Out of memory。\nDec 14 11:38:02 aliyun kernel: [69756.532361] Out of memory: Kill process 11168 (mysqld) score 114 or sacrifice child Dec 14 11:38:02 aliyun kernel: [69756.532430] Killed process 11168 (mysqld) total-vm:821140kB, anon-rss:57004kB, file-rss:0kB\n我的服务器内存只有512MB，而且没有配置SWAP分区，看来是MySQL占用的太多内存。找到 /etc/mysql/my.cnv 配置进行修改（注意备份）：\n1key_buffer = 16K 2max_allowed_packet = 1M 3thread_stack = 64K 4thread_cache_size = 4 5sort_buffer = 64K 6net_buffer_length = 2K 7#max_connections = 100 8#table_cache = 64 9#thread_concurrency = 10 同时也修改Apache的配置\n1Timeout 45 2KeepAlive On 3MaxKeepAliveRequests 200 4KeepAliveTimeout 3 5\u0026lt;IfModule mpm_prefork_module\u0026gt; 6\tStartServers 5 7\tMinSpareServers 5 8\tMaxSpareServers 10 9\tMaxClients 30 10\tMaxRequestsPerChild 2000 11\u0026lt;/IfModule\u0026gt; 重启Apache 和 MySQL\n1service apache2 restart 2service mysql restart 参考资料：\nhttp://forum.slicehost.com/index.php?p=/discussion/3629/out-of-memory-kill-process/p1 http://www.chrisjohnston.org/tech/configuring-a-lightweight-apache-mysql-install-on-debian-ubuntu ","date":"2012-12-14","description":"","lastmod":"2012-12-14T07:31:37Z","slug":"out_of_memory_mysql","tags":["apache","linux","mysql"],"title":"小内存服务器MySQL出现Out of memory错误","url":"https://blog.zengrong.net/post/out_of_memory_mysql/"},{"categories":["technology"],"content":"双11抢了一台阿里云的服务器，一直到今天才有时间来配置，把自己的博客迁了过来。下面是配置过程。\n服务器配置 CPU: Xeon 2.26GHz 单核 内存：512M 硬盘：20GB系统盘 + 20GB数据盘 系统：ubuntu 12.04 64位 挂载数据盘 数据盘默认是没有挂载的，因此需要先初始化数据盘。 我将数据盘挂载到了 /srv 目录中，用它来存放网站文件。\n1#查看所有硬盘 2fdisk -l 3#创建一个主分区，使用数据盘所有空间，创建过程略 4fdisk /dev/xvdb 5#格式化刚刚创建的分区为ext4格式 6mkfs -t ext4 /dev/xvdb1 7#修改fstab，将/dev/xvdb1挂载到/srv 8vim fstab 9#加入下面一行 10/dev/xvdb1 /srv ext4 defaults 0 0 11#自动挂载 12mount -a 配置Apache+PHP+MySQL 安装必须的组件。php5-mcrypt 是PHPMyAdmin必须的组件，所以这里一起安装了。\n1apt-get install apache2 mysql-server php5 php5-mysql libapache2-mod-php5 php5-mcrypt 启动Apache和MySQL\n1/etc/init.d/apache2 restart 2/etc/init.d/mysql restart 3#或 4service apache2 restart 5service mysql restart 配置虚拟主机 Ubuntu的虚拟主机配置很简单，它将虚拟主机分配到 /etc/apache2/sites-available/ 和 /etc/apache2/sites-enabeld 两个目录，前者用于放置可用站点的配置，后者放置已经启用的站点。\n复制默认的配置文件，将其修改成我需要的配置，然后启用它。\n1cp /etc/apache2/sites-available/default /etc/apache2/sites-available/zengrong.net 2#a2ensite脚本会自动在 `/etc/apache2/sites-enabled` 下建立一个同名的符号链接 3a2ensite zengrong.net 4#重启apache使配置生效 5service apache2 restart 如果希望暂时停止站点，可以使用：\n1a2dissite 站点名 配置模块 使用 a2enmod 和 a2dismod 可以启用和禁用模块。可用的模块放在 /etc/apache2/mods-avalable 中，已经启用的模块放在 /etc/apache2/modes-enabled 中。\n1#启动rewrite模块 2a2enmod rewrite 参考文章 http://wiki.ubuntu.com.cn/Apache http://wiki.ubuntu.com.cn/Apache%E8%99%9A%E6%8B%9F%E4%B8%BB%E6%9C%BA%E6%8C%87%E5%8D%97 ","date":"2012-12-14","description":"","lastmod":"2012-12-14T07:10:08Z","slug":"aliyun_vps_setup","tags":["linux"],"title":"阿里云经济A型服务器配置","url":"https://blog.zengrong.net/post/aliyun_vps_setup/"},{"categories":["technology"],"content":"这是我在知乎上的一个回答：http://www.zhihu.com/question/20653345/answer/15754680\nAS3代码层面，我写过和转载过几篇文章，很浅显，希望对你有帮助： 比较Object/Dictionary/Array顺序读写性能 【转】Flash高性能开发基础系列—内存篇 Array/Vector/AS3DS/ds/dsforas 性能比较 强烈建议你关注 http://jacksondunstan.com/ ，这个博客在AS优化上一直都非常有研究，有很多优秀的经验； 从开发习惯上入手，例如缓存Array.length的值、把除法尽量用乘法代替等等这种小技巧。就像@吴茜 说的，网上真的很多； 了解FlashPlayer的 执行模型 和显示列表机制。了解游戏中实现动画的几种不同的方法（例如位图引擎和显示列表的区别，如果加上Stage3D又怎样？），尽量少用现成的框架，能自己写就自己写，千万不要用Flex；专用的东西永远比通用的东西要更高效； 善用FlashBuilder提供的概要分析工具；善用Adobe提供的Adobe Scout等性能分析工具；利用Flash C++ Compiler编写更高效的代码； 从虚拟机层面进行优化，@吴茜 推荐的资料 Avm2虚拟机浅析与as3性能优化 很不错，建议详读。 ","date":"2012-12-13","description":"","lastmod":"2012-12-13T13:53:03Z","slug":"high-performance-trick-in-as3","tags":["actionscript","performance"],"title":"有哪些提高Actionscript执行效率的技巧？","url":"https://blog.zengrong.net/post/high-performance-trick-in-as3/"},{"categories":["technology"],"content":"让AIR3.5支持iPhone5（去掉黑边）\n我知道，在iPhone5那个BT的长脸上，游戏会出现黑边的。\n下面的代码在AIR3.5下编译，设置了全屏显示，如果在iPhone5上运行，那么显示的所有分辨率相关的尺寸都是960X640。\nwhat？！iPhone5的脸明明是1136那么长，为啥子就得不到？没错，AIR就是得不到正确的尺寸。\n解决的办法，是加一个白色半透明圆角矩形图片，图片上写上 “水果之王万岁，乔布斯永垂不朽，圆角矩形专利一万年” 这句话，放在src根目录下，然后重新发布ipa就可以了。\n我承认上面在骗人和胡扯。\n正确的方法是这样的：\n弄一张图片，高度1136，宽度640，里面随便放点什么内容（不要放果照）； 将其命名为 Default-568h@2x.png，放在项目的src目录下，和你的 project-app.xml 放在一起； 重新发布ipa，在iPhone5上运行，你就能得到正确的屏幕尺寸了。 回答几个小问题：\n为神马一定是568h？因为568x2=1136； 你不必显示这张图片，不必把在放在舞台上，不必在你的代码的任何地方使用这张图片； 你只需要保证这张图片在打包的时候被加入到ipa中就行了，Flash Buidler会自动做这件事的； 如果你不放心，你可以在ipa打包成功之后，随便找个压缩软件打开它，看看解压后能不能找到这个图片。 1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;s:Application xmlns:fx=\u0026#34;http://ns.adobe.com/mxml/2009\u0026#34; 3\txmlns:s=\u0026#34;library://ns.adobe.com/flex/spark\u0026#34; 4\tapplicationComplete=\u0026#34;application1_applicationCompleteHandler(event)\u0026#34;\u0026gt; 5\t\u0026lt;s:TextArea id=\u0026#34;infoTA\u0026#34; left=\u0026#34;10\u0026#34; right=\u0026#34;10\u0026#34; top=\u0026#34;10\u0026#34; bottom=\u0026#34;100\u0026#34;/\u0026gt; 6\t\u0026lt;s:Button id=\u0026#34;refresBTN\u0026#34; label=\u0026#34;刷新\u0026#34; horizontalCenter=\u0026#34;0\u0026#34; bottom=\u0026#34;20\u0026#34; width=\u0026#34;200\u0026#34; click=\u0026#34;refresBTN_clickHandler(event)\u0026#34;/\u0026gt; 7\t\u0026lt;fx:Script\u0026gt; 8\t\u0026lt;![CDATA[ 9\timport mx.events.FlexEvent;\t10\tprotected function refresBTN_clickHandler(event:MouseEvent):void 11\t{ 12\tinfoTA.text = getScreen(); 13\t} 14\t15\tprotected function application1_applicationCompleteHandler(event:FlexEvent):void 16\t{ 17\tinfoTA.text = getScreen(); 18\t} 19\t20\tprivate function getScreen():String 21\t{ 22\tvar __msg:String = \u0026#34;\u0026#34;; 23\t__msg += \u0026#34;OS:\u0026#34;+Capabilities.os + \u0026#34;\\n\u0026#34;; 24\t__msg += \u0026#34;screenDPI:\u0026#34;+Capabilities.screenDPI + \u0026#34;\\n\u0026#34;; 25\t__msg += \u0026#34;screenResolutionX:\u0026#34;+Capabilities.screenResolutionX + \u0026#34;\\n\u0026#34;; 26\t__msg += \u0026#34;screenResolutionY:\u0026#34;+Capabilities.screenResolutionY + \u0026#34;\\n\u0026#34;; 27\t__msg += \u0026#34;stageWidth:\u0026#34;+this.stage.stageWidth+ \u0026#34;\\n\u0026#34;; 28\t__msg += \u0026#34;stageHeight:\u0026#34;+this.stage.stageHeight+ \u0026#34;\\n\u0026#34;; 29\t__msg += \u0026#34;stage.width:\u0026#34;+this.stage.width+ \u0026#34;\\n\u0026#34;; 30\t__msg += \u0026#34;stage.height:\u0026#34;+this.stage.height+ \u0026#34;\\n\u0026#34;; 31\t__msg += \u0026#34;stage.fullScreenWidth:\u0026#34;+this.stage.fullScreenWidth+ \u0026#34;\\n\u0026#34;; 32\t__msg += \u0026#34;stage.fullScreenHeight:\u0026#34;+this.stage.fullScreenHeight+ \u0026#34;\\n\u0026#34;; 33\treturn __msg; 34\t}\t35\t]]\u0026gt; 36\t\u0026lt;/fx:Script\u0026gt; 37\u0026lt;/s:Application\u0026gt; 还可以看看下面有几篇文章，献给爱鸟语的同学：\nhttp://forums.adobe.com/message/4751222 http://forums.adobe.com/thread/1069815 http://forums.adobe.com/thread/1073656 http://forums.adobe.com/message/4727086 ","date":"2012-12-04","description":"","lastmod":"2012-12-04T08:03:23Z","slug":"iphon5_air_black","tags":["air","ios"],"title":"让AIR3.5支持iPhone5（去掉黑边）","url":"https://blog.zengrong.net/post/iphon5_air_black/"},{"categories":["technology"],"content":" 32bit 64bit 2012-12-05更新：需要先在这里登录http://www.adobe.com/downloads/，才能下载成功。\n截图一张：\n","date":"2012-12-04","description":"","lastmod":"2012-12-04T01:53:47Z","slug":"flashbuilder4-7","tags":["flashbuilder"],"title":"Flash Builder 4.7 正式版下载地址","url":"https://blog.zengrong.net/post/flashbuilder4-7/"},{"categories":["technology"],"content":" 2015-06-15：加入姊妹篇； 2013-11-06：加入重命名远程分支的内容； 2013-01-09：加入删除远程tag的内容。 姊妹篇：使用Git、Git GUI和TortoiseGit\n这篇文章记录我在使用git的过程中碰到远程分支和tag的相关内容，提纲：\n查看远程分支 删除远程分支和tag 删除不存在对应远程分支的本地分支 重命名远程分支 把本地tag推送到远程 获取远程tag 查看远程分支 加上 -a 参数可以查看远程分支，远程分支会用红色表示出来（如果你开了颜色支持的话）：\n1$ git branch -a 2 master 3 remote 4 tungway 5 v1.52 6* zrong 7 remotes/origin/master 8 remotes/origin/tungway 9 remotes/origin/v1.52 10 remotes/origin/zrong 删除远程分支和tag 在Git v1.7.0 之后，可以使用这种语法删除远程分支：\n1$ git push origin --delete \u0026lt;branchName\u0026gt; 删除tag这么用：\n1git push origin --delete tag \u0026lt;tagname\u0026gt; 否则，可以使用这种语法，推送一个空分支到远程分支，其实就相当于删除远程分支：\n1git push origin :\u0026lt;branchName\u0026gt; 这是删除tag的方法，推送一个空tag到远程tag：\n1git tag -d \u0026lt;tagname\u0026gt; 2git push origin :refs/tags/\u0026lt;tagname\u0026gt; 两种语法作用完全相同。\n删除不存在对应远程分支的本地分支 假设这样一种情况：\n我创建了本地分支b1并pull到远程分支 origin/b1； 其他人在本地使用fetch或pull创建了本地的b1分支； 我删除了 origin/b1 远程分支； 其他人再次执行fetch或者pull并不会删除这个他们本地的 b1 分支，运行 git branch -a 也不能看出这个branch被删除了，如何处理？ 使用下面的代码查看b1的状态：\n1$ git remote show origin 2* remote origin 3 Fetch URL: git@github.com:xxx/xxx.git 4 Push URL: git@github.com:xxx/xxx.git 5 HEAD branch: master 6 Remote branches: 7 master tracked 8 refs/remotes/origin/b1 stale (use \u0026#39;git remote prune\u0026#39; to remove) 9 Local branch configured for \u0026#39;git pull\u0026#39;: 10 master merges with remote master 11 Local ref configured for \u0026#39;git push\u0026#39;: 12 master pushes to master (up to date) 这时候能够看到b1是stale的，使用 git remote prune origin 可以将其从本地版本库中去除。\n更简单的方法是使用这个命令，它在fetch之后删除掉没有与远程分支对应的本地分支：\n1git fetch -p 重命名远程分支 在git中重命名远程分支，其实就是先删除远程分支，然后重命名本地分支，再重新提交一个远程分支。\n例如下面的例子中，我需要把 devel 分支重命名为 develop 分支：\n1$ git branch -av 2* devel 752bb84 Merge pull request #158 from Gwill/devel 3 master 53b27b8 Merge pull request #138 from tdlrobin/master 4 zrong 2ae98d8 modify CCFileUtils, export getFileData 5 remotes/origin/HEAD -\u0026gt; origin/master 6 remotes/origin/add_build_script d4a8c4f Merge branch \u0026#39;master\u0026#39; into add_build_script 7 remotes/origin/devel 752bb84 Merge pull request #158 from Gwill/devel 8 remotes/origin/devel_qt51 62208f1 update .gitignore 9 remotes/origin/master 53b27b8 Merge pull request #138 from tdlrobin/master 10 remotes/origin/zrong 2ae98d8 modify CCFileUtils, export getFileData 删除远程分支：\n1$ git push --delete origin devel 2To git@github.com:zrong/quick-cocos2d-x.git 3 - [deleted] devel 重命名本地分支：\n1git branch -m devel develop 推送本地分支：\n1$ git push origin develop 2Counting objects: 92, done. 3Delta compression using up to 4 threads. 4Compressing objects: 100% (48/48), done. 5Writing objects: 100% (58/58), 1.38 MiB, done. 6Total 58 (delta 34), reused 12 (delta 5) 7To git@github.com:zrong/quick-cocos2d-x.git 8 * [new branch] develop -\u0026gt; develop 然而，在 github 上操作的时候，我在删除远程分支时碰到这个错误：\n1$ git push --delete origin devel 2remote: error: refusing to delete the current branch: refs/heads/devel 3To git@github.com:zrong/quick-cocos2d-x.git 4 ! [remote rejected] devel (deletion of the current branch prohibited) 5error: failed to push some refs to \u0026#39;git@github.com:zrong/quick-cocos2d-x.git\u0026#39; 这是由于在 github 中，devel 是项目的默认分支。要解决此问题，这样操作：\n进入 github 中该项目的 Settings 页面； 设置 Default Branch 为其他的分支（例如 master）； 重新执行删除远程分支命令。 把本地tag推送到远程 1git push --tags 获取远程tag 1git fetch origin tag \u0026lt;tagname\u0026gt; 参考文章 https://makandracards.com/makandra/621-git-delete-a-branch-local-or-remote http://stackoverflow.com/questions/2003505/how-do-i-delete-a-git-branch-both-locally-and-in-github http://www.cnblogs.com/deepnighttwo/archive/2011/06/18/2084438.html http://stackoverflow.com/questions/14040754/deleting-remote-master-branch-refused-due-to-being-current-branch http://weli.iteye.com/blog/1441582 ","date":"2012-11-17","description":"","lastmod":"2012-11-17T09:58:25Z","slug":"delete_git_remote_brahch","tags":["git"],"title":"Git查看、删除、重命名远程分支和tag","url":"https://blog.zengrong.net/post/delete_git_remote_brahch/"},{"categories":["others"],"content":"转载自译言网\n原文译者：legendsland\n译文出处：http://article.yeeyan.org/view/legendsland/209584\n在 ILC 2002 大会上前Lisp大神，当今的Python倡导者 Peter Norvig，由于某些原因，做一个类似于马丁路德在梵蒂冈宣扬新教的主题演讲，因为他在演讲中大胆地声称Python就是一种Lisp。\n讲完后进入提问环节，出乎我意料的是，Peter点了我过道另一侧，靠上面几排座位的一个老头，他衣着皱褶，在演讲刚开始的时候踱步进来，然后就靠在了那个座位上面。\n这老头满头凌乱的白发，邋遢的白胡须，像是从旅行团中落下的游客，已经完全迷路了，闲逛到这里来歇歇脚，随便看看我们都在这里干什么。我的第一个念头是，他会因为我们的奇怪的话题感到相当失望；接着，我意识到这位老头的年纪，想到斯坦福就在附近，而且我想那人也在斯坦福 —— 难道他是……\n“嗨，John，有什么问题？” Peter说。\n虽然这只是10个字左右的问题，我不会假装自己记住了Lisp之父约翰麦卡锡说的每一个字。他在问Python程序能不能像处理数据一样，优雅地处理Python代码。\n“不行。John, Python做不到。”\nPeter就回答了这一句，然后静静地等待，准备接受教授的质疑，但老人没有再说什么了。此时，无语已胜千言。\nzrong增加的 约翰.麦卡锡 介绍：\n约翰·麦卡锡（John McCarthy，1927年9月4日－2011年10月24日[1][2]），生于美国马萨诸塞州波士顿，计算机科学家。他因在人工智能领域的贡献而在1971年获得图灵奖。实际上，正是他在1955年的达特矛斯会议上提出了“人工智能”这个概念。\n可参照《黑客-计算机革命的英雄》，了解部分麦卡锡大叔的事迹。\n","date":"2012-11-17","description":"","lastmod":"2012-11-17T06:10:44Z","slug":"a-list-story","tags":["lisp"],"title":"【转】至今听到关于 Lisp 最迷人的故事","url":"https://blog.zengrong.net/post/a-list-story/"},{"categories":["technology"],"content":"Android SDK中的PowerManager用来管理设备电源、重启、锁定休眠状态、唤醒等等操作。我已经把PowerManager的功能加入到了ANEToolkit中，这里记录一下开发过程中遇到的几个要注意的东东。\n关于权限 PowerManager的所有功能，需要以下三类权限\nandroid.permission.DEVICE_POWER isScreenOn需要这个权限 android.permission.WAKE_LOCK WakeLock类中的方法需要这个权限 android.permission.REBOOT reboot方法需要这个权限 PowerManager.WakeLock 使用acquire方法可以锁定自动休眠。\n例如在下载大文件的时候，自动休眠会中断WIFI信号导致下载失败。为了避免这种情况，可以使用acquire方法来保持设备为不休眠状态。\nacquire 有两种调用方式：\nacquire(long timeout) 超时后取消锁。中断休眠一段时间，时间到了就自动取消中断。应该多采用这种方式。 acquire() 永久性锁。必须调用release才能解锁。 使用acquire锁定休眠有计数锁和计数锁两种机制，使用 setReferenceCounted (boolean value) 可以设置是否使用计数锁。默认使用的是计数锁。\n这两种机制的区别在于，前者无论 acquire() 了多少次，只要通过一次 release() 即可解锁。而后者正真解锁是在 (--count == 0) 的时候，同样当 (count == 0) 的时候才会去申请加锁。\nreboot 即使是你为应用加入了REBOOT权限，在调用reboot方法的时候，依然会遇到异常，告知你没有权限执行这个方法。\n11-13 18:30:28.409: W/System.err(11290): java.lang.SecurityException: Neither user 10150 nor current process has android.permission.REBOOT.\n这是因为 REBOOT 权限，只有系统程序才可以获得，用户程序无法获取这个权限。\n参考资料 Why does my app throw an android.permission.REBOOT SecurityException? PowerManager.WakeLock源码解读(By DADA) ","date":"2012-11-13","description":"","lastmod":"2012-11-13T10:39:11Z","slug":"powermanager_note","tags":["android","ane"],"title":"PowerManager使用注意事项","url":"https://blog.zengrong.net/post/powermanager_note/"},{"categories":["technology"],"content":"2012-11-12 17:29更新：更正了编译文件的大小。\n这是我在知乎上的一个回答，原文在这里：http://www.zhihu.com/question/20001972/answer/15572624\n我没有用XCode开发过具体项目，我的移动开发经验主要是Android SDK、AIR for Android、AIR for iOS。\n因为开发经验的限制，我不能准确的说明XCode的优势和劣势，这里只基于自己的Android开发经验，以及AIR在iOS上的开发经验来分析。\nAIR的优势 AIR的优势其实就是Flash或者ActionScript语言的优势。这些优势大家已经在互联网上看过许多了，我还是啰嗦一下：\n1. 优秀的2D性能和渲染机制\n网络上关于Flash性能底下的言论是绝对错误的。其实Flash的性能相当高，而且大多数情况下都比Javascript高。ActionScript经过如此长时间的专制发展，形成了一套易于使用的显示列表（DisplayObject）机制，加上灵活的MovieClip和Sprite等等对象，在制作2D动画方面，是目前互联网技术中最好的选择。即使是你认为显示列表的性能底下（在显示对象超过1K的情况下确实低下），你也完全可以使用BitmapData这个高性能的引擎做位图渲染。\n2. 蓬勃发展的3D技术\nStage3D比OpenGL要更容易掌握。使用各种开源、付费的引擎，程序员可能不需要了解3D工作机制，就能制作3D动画（或者游戏）。当然，目前的Stage3D的驱动支持还有待完善，但Adobe目前很努力（不努力就挂掉了），驱动情况会慢慢解决掉。\n更让人激动的是Starling这类使用Stage3D进行2D渲染的引擎。完全为游戏而生，把Flash的2D性能又提高了一个数量级。\n3. 比较完善的框架和社区\nFlash社区经过多年发展，已经非常完善，有很多的优秀的框架、工具、引擎、调试器、甚至编译器可以使用。当然，OC社区或许更完善，所以这个有优势并不明显。\n4. 简单易用的语言\nActionScript是简化版的JAVA。我无法把ActionScript与OC对比，但ActionScript绝对比JAVA易用。相关比较可以看这个： http://www.zhihu.com/question/19762068/answer/15544195\n5. 使用ANE可以完成所有OC能做的事情\nAIR使用的ANE插件技术，让你用OC开发一些本机插件，以API的方式来调用它，让你能完成AIR本不能完成的事情。后面我会提到，其实这个也算劣势。\nAIR的劣势 1. 大文件\nAIR在iOS上并非采用的是虚拟机模式。它直接把ActionScript代码编译成二进制代码，这与XCode变成成的二进制代码没有区别。整个AIR运行时也变成二进制代码。这就导致了无论是什么大小的程序，你总要在它的基础上加上运行时的大小。——10MB+。\n2012-11-12 17:29更新：\n准确的编译文件大小测试：\nAIR3.5，AS项目，使用了graphics中的drawRect方法，3.8MB AIR3.5，Flex4.6项目，没有放任何组件，5.8MB 所以，上面的10MB+说法不准确。\n2. 不是BUG的BUG\n由于上面描述的原因，你要把ActionScript当作OC来用，否则可能会碰到某些不是BUG的BUG。我在这篇文章中就讲到了这样一个BUG： https://blog.zengrong.net/post/1654.html\n3. 痛苦的调试\nFlashBuilder并不是面向iOS开发的，所以它的调试过程复杂且痛苦。在FlashBuilder 4.6上，我必须利用iTunes这个垃圾软件把打包好的Debug版本的ipa文件安装到iOS设备上，然后在FlashBuider上启动调试进程。Debug版本的ipa运行十分缓慢（对，是十分），甚至因为它的缓慢，很多BUG都无法发生。\n当然，这种情况在AIR 3.4出现之后有所好转。AIR 3.4不需要iTunes就能把ipa部署到iOS设备中进行调试。但是，目前的FlashBuilder4.6还不支持这种方式，你要使用AIR3.4的新的直接部署调试功能，就必须使用命令行，然后调用fdb来调试。\nAIR 3.5支持在Release版本（非Debug版本）中输出调试堆栈，这能让我们用正常的速度来调试ipa，但这其实是让我们更麻烦了。\n4. 痛苦的编译\n你能忍受一次编译需要20分钟么？如果你的程序很复杂，那么这个时间还会延长。你能忍受在发布程序之前，突然发现一个小bug，然后等待20分钟编译调试么？注意，某些bug，只能在编译之后才会出现。\n5.痛苦的ANE调试\n和上面的调试不同，ANE的调试更加痛苦可不可捉摸。很多情况下，ANE的错误是直接FC，没有报错代码，没有消息，解决问题只能靠猜，你能猜中么？\n更痛苦的是，大部分情况下，使用AIR的程序员都在Windows下工作，使用AIR自带的ADL在Windows系统上调试，这种调试方法是不支持ANE的，你要测试ANE，必须打包后在iOS真实设备上调试，这又碰到了上面说的“痛苦的调试”的情况。\n不完善的小结 这种情况下可以使用AIR\n你要开发的东西是游戏（不要用AIR开发应用） 有一个Flash游戏需要移植到iOS上（移植） 开发一个新游戏，只有1个月时间（快速开发） 只会ActionScript和Flash（技术限制） 关于Flex\nFlex SDK包含swf编译器、swf相关工具、MXML语言和一套名为Flex的框架，这套框架大部分是做界面的事情。但即使是Adobe说他们的Flex中包含的UI组件为移动设备做了多少优化，也千万不要用它来开发移动设备上的程序，否则你会痛苦一被子。\n","date":"2012-11-12","description":"","lastmod":"2012-11-12T02:25:33Z","slug":"adobe-air-flex-and-xcode","tags":["air","ios"],"title":"与 Xcode 相比，用 Adobe AIR/Flex 做 iOS 开发有哪些优势和局限？","url":"https://blog.zengrong.net/post/adobe-air-flex-and-xcode/"},{"categories":["web"],"content":"一直希望有一台属于自己的独立服务器，这几天阿里云做11.11活动，就抢了一台。\n话说11月10日23:00按下支付，结果整个页面就挂掉了，刷了1分钟还是没进去。接着陆陆续续刷了大概4分钟，好像发现成功了。第二天上线一看，购买顺序排在400多名，能送10个月，这结果也勉强能接受吧。\n测了一下速度，下载能达到阿里云标称的带宽。但服务器间互传不知道为什么没限速，我在阿里云服务器中使用scp下载另一台独立服务器（非云服务器）上的文件，速度居然是5MByte每秒。难道两台服务器那么巧在同一机房？\n系统盘读取速度75.95MB/s，写入速度29.3MB/s，看来和网上传的差不多，磁盘性能不给力啊。\n已经被盛大云坑过了，希望阿里云不是第二个坑。\n用一段阿里云之后，我会写一篇阿里云和盛大云的对比文章。\n","date":"2012-11-11","description":"","lastmod":"2012-11-11T14:35:38Z","slug":"purchase-aliyun","tags":["master"],"title":"入手阿里云服务器","url":"https://blog.zengrong.net/post/purchase-aliyun/"},{"categories":["technology"],"content":"依然是Android先更新，收到Google Play的通知后，我才知道AIR3.5更新正式版了。\n不知道为什么，这次3.5的更新好像beta了很长时间，和前面3.3、3.4的作风不太一样啊……\n对比了一下3.5beta和正式版的release note，发现原来处于beta版中的 运行时载入swf的功能 不见了。我以为是眼花，来到beta版的releas note中一看，也不见了。这是Adobe的优良传统，只要不发的功能全部删掉，连历史尸首都找不到。原来那个睡眠模式通知功能就是被这样干掉的。\n为什么取消？难道是因为水果公司施压？\n增加了一些iOS6的特性，或许这就是beta时间长的原因吧。总之，AIR在iOS6下面的表现可以说是慢如蜗牛啊……期望3.5能好点。\n精华内容在这里\nbeta版本release note 正式版本release note 对比它们，你能发现许多不同。\n下载页面\nhttp://get.adobe.com/air\n","date":"2012-11-07","description":"","lastmod":"2012-11-07T01:30:52Z","slug":"flash-player11-5air3-5-release","tags":["air","flashplayer"],"title":"Flash Player11.5/AIR3.5正式版已经发布","url":"https://blog.zengrong.net/post/flash-player11-5air3-5-release/"},{"categories":["technology"],"content":"使用Git、Git GUI和TortoiseGit\nUsing git, git gui and tortoisegit\n2016-12-27更新：加入中文乱码解决方案 2015-06-15更新：加入姊妹篇 2015-01-28更新：Mac OS X 的 GUI 工具选择 2013-11-05更新：加入从 putty 切换到 OpenSSH 2012-12-30更新：在安装的时候选择TortoiseGit使用的SSH客户端 2012-12-26更新：在TortoiseGit中使用SSH host 注意： 本文不讲解任何关于Git提交、合并等等使用细节和语法，只记录作者在使用Git相关工具中碰到的问题和选择的经验。本文只是个人意见的集中，不代表适合所有人。如果你是 “被惯坏了的那批”，请不要介意。:-)\n姊妹篇：Git查看、删除、重命名远程分支和tag\n1. 关于命令行 我一直建议在命令行中使用Git或者SVN。因为这样可能更加了解他们的工作方式，也不容易遗漏重要的问题和提醒。\n在Windows习惯的驱使下，大多数人是不会看弹出的对话框中有什么信息的，一般都是直接关掉。但是，版本库给我们的提示信息都是非常重要的，有的是冲突，有的是提交失败，等等，这些都被略过了。\n我碰到的关于版本库使用问题主要包括下面这些：\n没有获取到最新版本就编译程序\n出现这个问题的原因，主要是忽略了文件的冲突。有的是从不看版本库给的提示，有的是太依赖Windows资源管理器中指示冲突的图标覆盖。众所周知，Windows系统的图标缓存一直都有那么点问题。 用删除文件的方式解决冲突\n许多程序猿并不知道如何解决冲突，也看不懂版本库在文件中加入的解决冲突的提示。 用删除文件代替revert\n至于为什么大家都这么用，我还搞不懂…… 强行覆盖提交\n碰到冲突后，备份自己修改的文件，然后恢复版本库中最新文件，再用自己的文件覆盖版本库中的文件然后提交。 哥哥诶～～你干的好事！ 其实如果在命令行中使用 Git 或者 SVN ，以上的问题应该都不会存在。因为命令行会事无巨细的给我们提示，尤其是Git的命令行，会非常聪明的猜测我们的意图并给我们提示。 使用者要正确的使用命令行，就必须去仔细阅读版本库的文档。这样就能进一步了解版本库的工作原理，减少在使用中的错误。\n另外，Git的很多功能，尤其是高级功能，只有命令行能实现。\n但并非所有程序猿都愿意使用命令行工具，尤其是被Windows惯坏了的那批。\n所以，有了TortoiseSVN和TortoiseGit。\n2. Git GUI Git自带GUI界面。使用 git gui 命令可以打开它。在这个界面中可以完成commit、merge、push、pull等等常用操作。\n使用 gitk 可以打开查看Git版本库历史，在 git gui 中也有菜单可以打开它。\n个人以为，完全可以不用安装TortoiseGit，对于绝大多数程序猿来说，这个界面已经足够了。\n但是，和“关于命令行”中说的那句话一样，并非所有的程序猿都愿意使用这个 界面简陋到丑陋 的工具，尤其是被TortoiseSVN惯坏了的那批。\n那些从SVN转换过来的程序猿，绝大多数都只用过TortoisSVN。那么好吧，就让界面、名字都完全一样的TortoiseGit登场吧！\n3. 安装TortoiseGit 3.1 TortoiseGit没有集成Git 在TortoiseGit官方网站可以下载到它。有32bit和64bit版本，同时也有中文语言包（但我不建议你安装）。\n安装完毕之后，如果你没有安装过Git，那么还需要去下载msysGit来安装。因为TortoiseGit其实只是一个壳，它需要调用Git命令行才能发挥作用。（现在你知道我为什么推荐你用命令行了么？）\n如果你不安装msysGit，那么在运行TortoiseGit的时候会弹出这个提示：\n为什么TortoiseGit不像TortoiseSVN一样，把SVN命令行工具集成在安装包中呢？我猜想是以下几点原因：\nGit官方从未出过Windows版本二进制包； msysGit和TortoiseGit是两个不同的团队开发的； msysGit和TortoiseGit的更新周期差异较大； TortoiseGit团队希望安装包更小； TortoiseGit团队给用户更灵活的选择Git版本的权利。 3.2 Git for Windows VS msysGit msysGit的主页提供了两个项目：Git for Windows和msysGit，并写明了它们的详细区别。 个人认为，Git for Windows适合绝大多数程序猿（又见绝大多数 :D）。所以，强烈建议安装Git for Window。 msysGit使用一种很BT也很NB的方式来安装。先安装一个最小的MinGW/MSYS系统，然后使用git pull 所有的源码，调用gcc在本地编译成可执行文件。\n3.3 Cygwin 如果本机安装过Cygwin，那么在安装msysGit的时候，cygwin的bin目录不能位于PATH环境变量中，否则msysGit会拒绝安装。 其实，如果你不在意Cygwin提供的Git版本比较老，你完全可以不安装Git for Windows或者msysGit，直接在TortoiseGit中设置Git.exe的路径为Cygwin的bin目录即可。\n由于Cygwin目前的Git版本较老，在运行TortoiseGit的时候你会得到这个提示：\n关于Cygwin、MinGW以及msysGit的关系和选择，可以看这篇文章：Cygwin与MinGW，如何选择？ 还有这篇转载的文章：Msys/MinGW与Cygwin/gcc\n4. TortoiseGit的密钥 我认为TortoiseGit最大的问题，就是在于它使用ppk密钥格式，而不是使用OpenSSH密钥格式。 因为linux系统是默认使用OpenSSH的，所以Git在基于命令行的时候是使用OpenSSH格式的密钥。 同理，gitolite这种服务器端程序使用的是OpenSSH格式的密钥。 所以，必须将原有的OpenSSH密钥转换成PPK密钥才能在TortoiseGit中使用。\n在安装TortoiseGit的时候，你可以选择使用Putty还是OpenSSH作为SSH客户端。安装程序中说，Putty和Windows配合得更好。\n如何选择？我分别给出它们的特点：\nPutty\nPutty有GUI界面，可以通过配置sessions来访问不同的git服务器端口； Putty有GUI程序(Putty Key Generator)来生成密钥； 如果使用Putty作为SSH客户端，那么传输速度可能会比较慢（个人感觉，当然也有人和我有一样的感觉）； Putty不能直接使用原有的OpenSSH密钥，必须将其转换成PPK密钥才行。 OpenSSH\nOpenSSH是Git命令行程序默认使用的SSH客户端程序； Git for Windows默认就包含了OpenSSH程序； 你可以利用已有的OpenSSH密钥，不用做转换（例如我原来用cygwin的时候积累了一堆OpenSSH密钥，现在只需要在~/.ssh下做一个符号链接就能用了）； GitHub/bitbucket等Host使用的都是OpenSSH密钥； 大多数Linux发行版默认使用OpenSSH作为服务端； 你可以方便的使用命令行程序来实现自动化处理。 看完上面的特点，如果你还是选择了Putty作为客户端的话，那么需要转换原有的OpenSSH密钥（如果有的话）； 如果你依然义无反顾选择了OpenSSH作为客户端的话，我相信你已经知道如何生成、修改、配置SSH了，看来我也不必罗嗦 :D\n4.1 转换OpenSSH密钥到ppk格式 可以使用TortoiseGit自带的Putty Key Generator来转换原来的OpenSSH密钥到ppk格式。\n打开该程序，选择 Conversions-\u0026gt;Import Key 命令将OpenSSH 私钥 导入界面中，然后点击 Save private key 按钮将密钥保存成ppk格式。建议在 Key comment 中输入说明，否则密钥多了很难分辨。至于密码，为了方便可以不设置。\n4.2 生成OpenSSH和ppk格式的密钥 为了同时支持服务端和客户端，我们可以在生成一个密钥的时候，同时生成该密钥的ppk格式和OpenSSH格式。而每个密钥对都包含 公钥 和 私钥，两对一共是4个文件。这样就可以满足所有情况了。\n打开Putty Key Generator，选择 Generator 按钮，晃动鼠标生成一个密钥，然后这样处理：\n点击 Save private key 按钮将密钥保存成 ppk格式私钥； 点击 Save public key 按钮将密钥保存成 ppk格式公钥； 点击 Conversions-\u0026gt;Export OpenSSH Key 按钮将密钥保存成 OpenSSH格式私钥； 获取上图红框中的所有文本内容，粘贴到文本编辑软件中，保存为一个单行的文件，这就是 OpenSSH格式公钥； 4.3 在TortoiseGit中使用SSH host 如果使用Putty作为TortoiseGit的SSH客户端，那么就不能使用OpenSSH的 ~/.ssh/config 来定义使用不同的端口和密钥访问SSH，而是需要使用 PuTTY Session。这篇文章进行了详细讲解： 在TortoiseGit中使用SSH host\n4.4 从 putty 切换到 OpenSSH 也许是你一时 手贱……唔，手快选择了 putty 作为客户端，某天又良心发现想用 OpenSSH ，是否必须重装一次 TortoiseGit 来重新选择一次呢？\n我曾经这样做过，直到我找到这个设置：\n是的，只需要把 ssh客户端 改成 git for windows 提供的 ssh.exe 即可。如果用 Cygwin，那么这个程序在 cygwin/bin 目录中。\n5. 杂项 5.1 换行符的问题 autocrlf and safecrlf Windows(\\r\\n)、Linux(\\n)和MacOS(\\r)三个主流系统的换行符各不相同，这样在跨平台合作的时候就容易出现换行符的问题。\nGit提供了 autocrlf 和 safecrlf 两个参数来解决这个问题。但这两个参数如果没用好，就会影响开发。\n例如，出现这种情况：\nA和B两个开发人员，A使用LF(\\n)做换行符，B使用CRLF(\\r\\n)做换行符，且都没有开启 autocrlf 参数，那么A在迁出B的文件，并使用自己的编辑器打开之后就会发现，虽然没有对文件做任何修改，但它的状态是modified。这是由于A的编辑器自动将B的文件中的所有换行符替换成了(LF)，这与版本库中的(CRLF)不同。\n让我们来看看 autocrlf 参数的作用：\n1# 签出时将换行符转换成CRLF，签入时转换回 LF。 2git config --global core.autocrlf true 3 4#签出时不转换换行符，签入时转换回 LF 5git config --global core.autocrlf input 6 7#签出签入均不转换 8git config --global core.autocrlf false 这些选项在TorgoiseGit中也可以设置。\n我的建议是在无论在什么系统下编程，都把所有人的编辑器的换行符模式设置成Unix格式，然后把autocrlf设置成false，这样一劳永逸。 毕竟除了Windows记事本这类软件外，已经很少有文本编辑器不支持换行符设置了。\n如果你把换行符搞乱了，在一个文件中既包含windows风格的换行符也包含unix风格换行符，那么 safecrlf 就可以发挥作用了：\n1# 拒绝提交包含混合换行符的文件 2git config --global core.safecrlf true 3 4# 允许提交包含混合换行符的文件 5git config --global core.safecrlf false 6 7# 提交包含混合换行符的文件时候给出警示 8git config --global core.safecrlf warn 5.2 文件权限问题 755 and 664 我在Cygwin下以命令行的形式使用Git，同时也使用TortoiseGit。 在使用TortoiseGit签出使用cygwin提交的项目时，发现所有的文件权限都改变了：\n1$ git diff 2diff --git a/launch4j/spritesheet_conterver.xml b/launch4j/spritesheet_conterver.xml 3old mode 100755 4new mode 100644 这是因为msysgit是一个类Unix模拟器，需要拥有Unix形式的文件访问权限。而由于Windows的种种限制，信息不能复原，从而导致原来的755成644了。\n解决方法：\n1git config --global core.filemode false 2git config core.filemode false 这个选择的在TortoiseGit中没有界面来设置，只能用命令行或者手动修改git配置文件。\n## 5.3 中文文件名和路径乱码 在文件名和路径名中包含中文的时候， git status 的显示是这样的：\n1git status 2On branch master 3Untracked files: 4 (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to include in what will be committed) 5 6 \u0026#34;\\344\\275\\240\\345\\245\\275.txt\u0026#34; 7 8nothing added to commit but untracked files present (use \u0026#34;git add\u0026#34; to track) 要解决这个问题，只需要告诉 git 不对 0x80 以上的字符进行转义即可：\n1git config --global core.quotepath false 查看 官方说明 ：\nThe commands that output paths (e.g. ls-files, diff), when not given the -z option, will quote \u0026quot;unusual\u0026quot; characters in the pathname by enclosing the pathname in a double-quote pair and with backslashes the same way strings in C source code are quoted. If this variable is set to false, the bytes higher than 0x80 are not quoted but output as verbatim. Note that double quote, backslash and control characters are always quoted without -z regardless of the setting of this variable.\n使用后效果如下：\n1git status 2On branch master 3Untracked files: 4 (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to include in what will be committed) 5 6 你好.txt 7 8nothing added to commit but untracked files present (use \u0026#34;git add\u0026#34; to track) 6. Mac OS X GUI 选择 因为 Mac OS X 已经自带了 git 工具，绝大多数情况下，不需要使用 GUI 工具。\n如果一定要使用，那么建议使用下面两款：\n6.1 GitX-dev 6.2 SourceTree 我的选择主要参考 The Best Mac Git Gui 以及 Git 官网的 GUI Clients 。\n7. 文章参考 http://blog.csdn.net/dbzhang800/article/details/6426834 http://ycyuxin.blog.hexun.com/37440972_d.html http://stackoverflow.com/questions/4181870/git-on-windows-what-do-the-crlf-settings-mean http://www.360doc.com/content/11/0117/18/2036337_87178762.shtml ","date":"2012-11-03","description":"","lastmod":"2012-11-03T03:49:01Z","slug":"git_garbled","tags":["cygwin","git","mingw","svn"],"title":"使用Git、Git GUI和TortoiseGit","url":"https://blog.zengrong.net/post/git_garbled/"},{"categories":["technology"],"content":"在使用上，请参考： Cygwin 与 MinGW/MSYS，如何选择？\n【转】Msys/MinGW与Cygwin/gcc\n本文转自：http://www.lupaworld.com/273398/viewspace-122539.html\n一、 MinGW MinGW 官方网站为 http://www.mingw.org/\nMinGW，即 Minimalist GNU For Windows（GCC compiler suite）。它是一些头文件和端口库的集合，该集合允许人们在没有第三方动态链接库的情况下使用 GCC（GNU Compiler C）产生 Windows32 程序。\nMinGW: 一个可自由使用和自由发布的Windows特定头文件和使用GNU工具集导入库的集合，在基本层，MinGW 是一组包含文件和端口库，其功能是允许控制台模式的程序使用微软的标准C运行时间库（MSVCRT.DLL）,该库在所有的 NT OS 上有效，在所有的 Windows 95 发行版以上的 Windows OS 有效，使用基本运行时，你可以使用 GCC 写控制台模式的符合美国标准化组织（ANSI）程序，可以使用微软提供的 C 运行时扩展。该功能是 Windows32 API 不具备的。下一个组成部分是 w32api 包，它是一组可以使用 Windows32 API 的包含文件和端口库。与基本运行时相结合，就可以有充分的权利既使用 CRT（C Runtime）又使用 Windows32 API 功能。\n实际上 MinGW 并不仅是一个 C/C++ 编译器，而是一套 GNU 工具集合。除开 GCC (GNU 编译器集合) 以外，MinGW 还包含有一些其他的 GNU 程序开发工具 (比如 gawk bison 等等)。\n开发 MinGW 是为了那些不喜欢工作在 Linux(FreeBSD) 操作系统而留在 Windows 的人提供一套符合 GNU 的 GNU 工作环境。所以，使用 MinGW 我们就可以像在 Linux 下一样使用 GNU 程序开发工具。\nGCC 就是 MinGW 的核心所在，GCC 是一套支持众多计算机程序语言的编译系统，而且在语言标准的实现上是最接近于标准的。并且 GCC 几乎可以移植到目前所有可用的计算机平台。\nGCC 本身不像VC那样拥有IDE界面（但是有很多的开源的IDE支持使用MinGW，例如codeblocks，eclipse等）。源代码编辑你可以选用任何你喜欢的文本编辑器（据说微软的开发人员包括 VC 的开发都不用 VC 所带的 IDE 编辑器，而是选用 GNU 的 VIM 编辑器）。然后使用 make 等工具来进行软件项目的编译、链接、打包乃至发布。而像 cvs(svn) 源代码版本控制工具可以让世界上任何一个角落的人都可以参与到软件项目中来。\n关于 MFC，微软基础库类，这个随 VC++ 携带的一个源代码公开的开发包，和其他 Windows 程序开发包是一样的。如果有 VC++ 的授权，你完全可以使用 MFC 的源代码，也就是你使用 GCC 来编译 MFC 程序是完全可以的。\n当然，GNU 下也有很多 Windows 程序开发包，甚至有一些是支持跨平台使用的。不仅仅可以直接把源代码编译为 Windows 程序，也可以不经修改编译为其他操作系统的图形程序。\n二、 MSYS 官方网站为 http://www.mingw.org/\nMSYS：Unix-like command line utilities，包括基本的bash, make, gawk and grep 等等。通常也可以认为是小型的UNIX on Windows。提供在windows上模拟Unix环境来使用MinGW。\nmsys-cn:http://code.google.com/p/msys-cn/\nMSYS中国发行版，用UNIX开发环境开发Windows程序。\nMSYS在windows下模拟了一个类unix的终端，它只提供了MinGW的用户载入环境，在MSYS模拟的unix环境下使用MinGW，就像在Unix使用gcc一样。\n三、 cygwin/gcc cygwin/gcc和MinGW都是gcc在windows下的编译环境，但是它们有什么区别，在实际工作中如何选择这两种编译器。\ncygwin/gcc完全可以和在linux下的gcc化做等号，这个可以从boost库的划分中可以看出来端倪，cygwin下的gcc和linux下的gcc完全使用的是相同的Toolsets。所以完全可以和linux一起同步更新gcc版本，而不用担心问题，并且在cygwin/gcc做的东西（不用win32的）可以无缝的用在linux下，没有任何问题。是在windows下开发linux程序的一个很好的选择。\n但是在cygwin/gcc下编译出来的程序，在windows执行必须依赖cygwin1.dll，并且速度有些慢，如果不想依赖这个东西的化，必须在gcc的编译选项中加入-mno-cygwin。加入这个选项其实gcc编译器就会自动的选择在安装cygwin/gcc时安上的mingw,这个mingw就是gcc的一个交叉编译。\n对于mingw作为gcc在windows上的一个实现，由于不像cygwin的gcc在一个模拟linux上运行，同时相当一部分linux的工具不能够使用，不过现在已经有Msys这个模拟unix的shell，可以解决很多的问题。\n四、 总结 MinGW是windows版本的gcc集合，不需要依赖中间层。\nMSYS是小型的linux的环境的模拟，可以与MinGW结合来模拟linux环境下使用MinGW的gcc。\nCygwin是功能强大的linux环境，由于有cygwin1.dll实现了底层的windows api到linux api的转化。所以在Cygwin里开发就相当于在linux上开发，对于开发人员来说就相当于调用linux类型的api，所以这样开发的程序也可以直接移植到linux上。但是如果这样的程序要在windows上执行的话，运行时必须要cygwin1.dll支持。\n根据以上的分析，如果在windows开发linux跨平台的程序，linux模拟器Cygwin以及所包含的gcc是很好的选择，但是开发的程序必须依赖一个cygwin1.dll。如果你只是想在windows下使用gcc编译器也不想依赖其他的dll，mingw是很好的一个选择。\n","date":"2012-11-03","description":"","lastmod":"2012-11-03T02:48:32Z","slug":"msys_mingw_and_cygwin_gcc","tags":["cygwin","linux","mingw","simulator","terminal"],"title":"【转】Msys/MinGW与Cygwin/gcc","url":"https://blog.zengrong.net/post/msys_mingw_and_cygwin_gcc/"},{"categories":["technology"],"content":"安装gitolite\ngit默认使用SSH协议，在服务器上基本上不用怎么配置就能直接使用。但是如果面向团队服务，需要控制权限的话，还是用gitolite方便些。\n本文的大部分内容来自 https://github.com/sitaramc/gitolite ，但并不是翻译。\n本文面向的版本是gitolite v3；所有的操作基于命令行；服务器为CentOS 6.2 x86_64；客户端使用cygwin。\ngitolite vs gitosis 为什么不用gitosis呢？原因很简单，它已经好几年没有更新了。 gitolite原本是作为gitosis的lite版本出现的，可是现在的功能甚至已经超过gitosis了。 关于gitolite和gitosis的更详细信息，可以看看下面几篇文章：\n使用gitosis来配置管理git服务器端 Gitolite and Gitosis in a Nutshell 从 Gitosis 迁移到 Gitolite Gitolite 2.2 安裝筆記 系统需求 类unix操作系统 sh git 1.6.6+ Installing git on centos 6 Git 1.7.10.2 on CentOS 5.6 perl 5.8.8+ openssh 5.0+ 一个git用户 一个openssh公钥 安装 登录到git用户。如果没有给git用户设置密码，可以从root用户通过su切换过去。 确认 ~/.ssh/authorized_keys 不存在 将公钥放在 ~/YourName.pub 运行下面的命令： 1# 获取版本库 2git clone git://github.com/sitaramc/gitolite 3# 创建bin目录，用于存放安装后的文件 4mkdir -p ~/bin 5# 将gitolite安装到bin目录 6gitolite/install -to ~/bin 7# 使用YourName.pub公钥初始化版本库 8gitolite setup -pk YourName.pub 管理用户和版本库 不应该手动在服务器端加入新的用户或者版本库。 gitolite使用一个特殊的版本库 gitolite-admin 来管理员用户和版本库，只要在这个版本库中修改并 push，服务器就会自动根据配置作出修改。\n首先在客户端迁出版本库：\n1git clone git@host:gitolite-admin 如果在迁出的过程中询问密码，那么说明配置出了问题。一般情况是密钥配置错误。可以检查客户端的 ~/.ssh 下有没有 YourName 私钥。如果需要使用不同的密钥连接多个ssh服务器，可以编辑 ~/.ssh/config 进行配置。\n进入 gitolite-admin 目录，其中的 keydir 目录是用来放置用户公钥的，而 conf/gitolite.conf 则是用来配置用户和版本库。\n编辑 conf/gitolite.conf如下：\n1repo foo 2\tRW+ = alice 3\tRW = bob 4\tR = carol 上面的配置的含义是：\n有一个名为 foo 的版本库； 用户 alice 对它有读、写、删除权限； 用户 bob 对它有读写权限； 用户 carol 对它仅有只读权限。 另外，需要找 alice/bob/carol 用户索要他们的公钥，保存在 keydir 目录中，命名为 alice.pub/bob.pub/carol.pub，然后提交这些改动， push 到服务器。\n服务器会自动将公钥加入到 ~/.ssh/authorized_keys 中，并创建 foo 版本库。\nfoo 版本库的访问地址为 git@host:foo。\n如果希望把 foo 版本库放在 bar 目录下，可以这样编辑配置文件：\n1repo bar/foo 2\tRW+ = alice 3\tRW = bob 4\tR = carol 此时foo 版本库的访问地址为 git@host:bar/foo。\n更多内容 https://github.com/sitaramc/gitolite http://sitaramc.github.com/gitolite/master-toc.html https://github.com/sitaramc/gitolite/wiki ","date":"2012-11-02","description":"","lastmod":"2012-11-02T09:41:02Z","slug":"setup_giolite","tags":["centos","cygwin","git","linux"],"title":"安装gitolite","url":"https://blog.zengrong.net/post/setup_giolite/"},{"categories":["technology"],"content":"在Ant中替换尖括号\n我使用 ReplaceRegexp 任务写了一段脚本替换XML的值：\n1\u0026lt;replaceregexp file=\u0026#34;app.xml\u0026#34; 2\tmatch=\u0026#34;\u0026lt;filename\u0026gt;\u0026#34; 3\treplace=\u0026#34;name\u0026#34; 4\tencoding=\u0026#34;UTF-8\u0026#34;/\u0026gt; 由于XML规范不允许在属性值中出现尖括号，Ant会报错：\nd:\\works\\build\\build.xml:70: 与元素类型 \u0026quot;null\u0026quot; 相关联的 \u0026quot;match\u0026quot; 属性值不能包含 '\u0026lt;' 字符。\n把左右尖括号用他们的十六进制代码代替就可以解决这个问题：\n1\u0026lt;replaceregexp file=\u0026#34;app.xml\u0026#34; 2\tmatch=\u0026#34;\\x3Cfilename\\x3E\u0026#34; 3\treplace=\u0026#34;name\u0026#34; 4\tencoding=\u0026#34;UTF-8\u0026#34;/\u0026gt; 如果希望在替换的内容中也使用尖括号，需要一点点小技巧：\n1\u0026lt;replaceregexp file=\u0026#34;app.xml\u0026#34; 2\tmatch=\u0026#34;(\\x3C)filename(\\x3E)\u0026#34; 3\treplace=\u0026#34;\\1name\\2\u0026#34; 4\tencoding=\u0026#34;UTF-8\u0026#34;/\u0026gt; 当然，还有更简单的办法，就是使用 Replace 的 replacetoken 。\n1\u0026lt;replace file=\u0026#34;app.xml\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;\u0026gt; 2\t\u0026lt;replacetoken\u0026gt;\u0026lt;![CDATA[\u0026lt;filename\u0026gt;]]\u0026gt;\u0026lt;/replacetoken\u0026gt; 3\t\u0026lt;replacevalue\u0026gt;\u0026lt;![CDATA[\u0026lt;name\u0026gt;]]\u0026gt;\u0026lt;/replacevalue\u0026gt; 4\u0026lt;/replace\u0026gt; ","date":"2012-10-31","description":"","lastmod":"2012-10-31T01:39:02Z","slug":"replace_angle_brackets_in_ant","tags":["ant","xml","java"],"title":"在Ant中替换尖括号","url":"https://blog.zengrong.net/post/replace_angle_brackets_in_ant/"},{"categories":["technology"],"content":"在PHP中读取二进制文件\n很多时候，数据并不是用文本的方式保存的，这就需要将二进制数据读取出来，还原成我们需要的格式。PHP在二进制处理方面也提供了强大的支持。\n任务 下面以读取并分析一个PNG图像的文件头为例，讲解如何使用PHP读取和分析二进制文件。\n涉及函数 fopen fread unpack bin2hex PNG格式简介 为了完成任务，下面简单介绍一下PNG文件格式。 PNG是一种无损压缩的图像文件格式，该格式的第1-8字节保存着PNG署名域，内容如下：\n十进制：\t137\t80\t78\t71\t13\t10\t26\t10 十六进制：\t89\t50\t4e\t47\t0d\t0a\t1a\t0a 我们的任务就是将这个文件头读取出来。\n更详细的关于PNG格式的介绍：\nhttp://www.w3.org/TR/2003/REC-PNG-20031110/ http://www.libpng.org/pub/png/ 读取文件 1$filePath = \u0026#34;icon.png\u0026#34;; 2//必须使用rb来读取文件，这样能保证跨平台二进制数据的读取安全 3$fh = fopen($filePath, \u0026#34;rb\u0026#34;); 4//仅读取前面的8个字节 5$head = fread($fh, 8); 6fclose($fh); 上面的代码已经把我们需要的8个字节读入变量head中了。head是一个保存二进制数据的数组，我们还需要对它做一些操作才能得到我们需要的数据。\nunpack unpack可以将二进制数据解析成关系数组，它接受2个参数，第一个提供解析方式字符串（见下方），第二个参数就提供我们前面读出的head变量就可以了。\na：NULL填充的字节串 A：空格填充的字节串 h：十六进制数，低四位字节优先 H：十六进制数，高四位字节优先 c：有符号字符 C：无符号字符 s：有符号短整型(总是16位，机器字节序) S：无符号短整型(总是16位，机器字节序) n：无符号短整型(总是16位，大尾字节序) v：无符号短整型(总是16位，小尾字节序) I：有符号整型(机器相关大小和字节序) I：无符号整型(机器相关大小和字节序) l：有符号长整型(总是32位，机器字节序) L：无符号长整型(总是32位，机器字节序) N：无符号长整型(总是32位，大尾字节序) V：无符号长整型(总是32位，小尾字节序) f：浮点数(机器相关大小和表示) d：双精度数(机器相关大小和表示) x：空字节 X：倒退一个字节 @：用NULL填充绝对位置 unpack的第一个参数在在使用上有一点点小技巧，下面是范例：\nC 读取1个字符，返回的数组索引为1 C4 读取4个字节，每个字节一个字符，返回的数组索引为1，2，3，4 C4head 读取4个字符，每个字节一个字符，返回的数组索引为head1，head2，head3，head4 Chead 读取1个字符，返回的数组索引为head 现在试着读取第1个字节：\n1$arr = unpack(\u0026#34;Chead\u0026#34;, $head); 2print_r($arr); 3//Array ( [head] =\u0026gt; 137 ) 读取所有的8个字节，用斜杠可以分隔：\n1$arr = unpack(\u0026#34;Chead/C3string/C4number\u0026#34;, $head); 2print_r($arr); 3//Array ( [head] =\u0026gt; 137 [string1] =\u0026gt; 80 [string2] =\u0026gt; 78 [string3] =\u0026gt; 71 [number1] =\u0026gt; 13 [number2] =\u0026gt; 10 [number3] =\u0026gt; 26 [number4] =\u0026gt; 10 ) 把string开头的键拼成字符串：\n1$arr = unpack(\u0026#34;Chead/C3string/C4number\u0026#34;, $head); 2for($i=1;$i\u0026lt;=3;$i++) 3{ 4\t$type.=chr($arr[\u0026#39;string\u0026#39;.$i]); 5} 6echo $type; 7//PNG bin2hex 上面使用print_r打印出来的内容，都是十进制数字，如果希望直接得到十六进制值，可以使用bin2hex函数。\n1echo bin2hex($head[0]); 2//89 注意，使用这种方法得到的是字符串，并不是数字。因此下面的条件是不成立的：\n1if(bin2hex($head[0]) == 0x89) 2{ 3\techo \u0026#39;match!\u0026#39;; 4} ","date":"2012-10-30","description":"","lastmod":"2012-10-30T10:56:25Z","slug":"read_binary_in_php","tags":["php"],"title":"在PHP中读取二进制文件","url":"https://blog.zengrong.net/post/read_binary_in_php/"},{"categories":["technology"],"content":"AIR提供的ADL工具输出的调试信息是UTF-8格式编码，而Windows控制台的默认设置并不支持UTF-8编码。这就导致使用ADL调试的时候，在控制台中显示的是乱码。\n需要两步来解决这个问题：\n使用chcp命令将当前的内码修改为UTF-8，UTF-8的内码在Windows中为65001； 在控制台的标题栏上单击右键，选择“默认值-\u0026gt;字体”，将字体修改为“新宋体”。 也可以先将控制台字体修改为新宋体，然后在批处理中加入chcp命令。\nchcp 65001 sdk\\bin\\adl.exe ...... ","date":"2012-10-22","description":"","lastmod":"2012-10-22T06:03:08Z","slug":"windows-console-utf8","tags":["air","utf-8","windows"],"title":"Windows cmd控制台查看UTF-8文本","url":"https://blog.zengrong.net/post/windows-console-utf8/"},{"categories":["use"],"content":"2013-03-02更新：CyanogenMod的 Wiki已经删除了下载地址页面，因此加入了新的Google Apps下载地址。\nLP总抱怨她的HTC EVO 4G速度太慢，今天终于能抽出点时间来刷个机了。\n像EVO 4G这种老机器，是肯定没有官方版的ICS可以OTA升级的，只能考虑刷第三方Rom。于是我转战机锋、安卓网等多个论坛，刷了4个Rom，发现要么是吸费软件太多，要么是刷完无法启动，忙活了半个多个小时也没找到满意的Rom。\n在重刷第4个Rom的时候，刷机的过程中发现原Rom作者嵌入到刷机代码中的博客地址，终于在moonlight的博客找到了近乎纯净版的CyanogenMod9版本的EVO 4G Rom。而且moonlight也正在放出CM10版本的EVO 4G ROM。\n刷了一个moonlight提供的基于CM9的10.18版本之后，我发现Android 4.04在EVO 4G这种老机器上，表现还真不错，似乎比以前的2.3.5都要好点。可惜的是，没有Google Apps可用。\n原来的通讯录是备份在Google账户中的，而没有Google Apps就无法同步原来的通讯录，要导出成VCard格式再用Android通讯录导入。虽然LP不用Google的服务，但是这样一来，就无法再进行通讯录的同步了。我记得原来在玩Samsang i5700的时候，是单独刷过Google官方套件的，只是时间实在太久，记不清怎么弄的了。\n又是Google一番，发现网上大多数的帖子都语焉不详。这也难怪，活跃的那部分玩家，能把一件事情用文字说清楚的的确很少（或许用语言都难得说清楚）。但真正的老鸟，又大都不愿意写这些入门文章。\n好不容易找到的比较全面和系统的是这一篇：Google Apps知多少与快速安装方法\n下面摘录部分：\n1：Google Apps是什么？ Google Apps(简称:GApps)是谷歌公司专属的应用，用于各种安卓平台。目前Gapps里大多数的应用，都可以在谷歌市场(Google Play Store)找到并安装。这些应用主要包括：Google Play、Google Talk、Google Map、Gmail、Google Search、Google Voice Search、Google Music、Google Docs、Google Sync、Google Backup Transport 以及Car Home、YouTube、Facebook等等。 2：Google Apps与Roms 目前市场只有部分固件Roms中，已经内置了GApps。这是因为这些Roms，要么是获得官方授权定制的Rom，要么是个人编译的Rom或者从对版权不是很重视的地区流出的Rom。 鉴于licensing issues（许可证发放）问题，大多数的AOSP、CM以及AOKP的Rom，都不会内置Google Apps。 3：Google Apps最新版本与下载 下表所列Google Apps及其下载包，均为官方最新数据。 需要的板友，请根据使用设备的Android版本，选择合适的GApps下载包。 遗憾的是，该文中提供的GoogleApps的下载地址都是115网盘的，而115网盘已经关闭共享了。\n无奈之中找到CyanogenMod的官方WIKI，惊喜地发现这里也有Google Apps提供下载！\nhttp://goo.im/gapps\n在上面的链接中，根据自己的CM版本下载对应的Google Apps就可以了。至于刷入的方法，和卡刷一样，本文就不提了。\n","date":"2012-10-21","description":"","lastmod":"2012-10-21T12:06:28Z","slug":"google_app_for_htc_evo4g","tags":["android","google","mobile"],"title":"CyanogenMod Rom加入Google Apps官方套件","url":"https://blog.zengrong.net/post/google_app_for_htc_evo4g/"},{"categories":["technology"],"content":"2012-10-15：v0.7.3版发布\n增加对JPEG-XR格式描述符的支持； 解决输出的XML文件第一行换行符为\\r\\n的问题。 更多的功能介绍以及软件下载，看这里。\n","date":"2012-10-15","description":"","lastmod":"2012-10-15T02:37:41Z","slug":"sprite_sheet_0_7_3","tags":["air","spritesheet"],"title":"sprite sheet editor v0.7.3发布","url":"https://blog.zengrong.net/post/sprite_sheet_0_7_3/"},{"categories":["technology"],"content":"在Debug Configurations 或者 Run Configuration 界面中，进入Arguments选项卡。\n如果是给main方法传递的参数，直接写在Program arguments中，使用空格分隔。\n如果是给系统JVM传递的参数，写在VM arguments中，使用-D打头。\n以设置LAF为例：\n-Dswing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel 如果设置了一个变量，名称为swing.defaultlaf，值为com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel。则可以这样调用它：\n-Dswing.defaultlaf=${swing.defaultlaf} 要注意对变量的引用，只会使用变量的值，而不会自动调用变量的名称。即使把swing.defaultlaf定义为变量，像下面这样调用，也是没有效果的：\n-D${laf_name}=${swing.defaultlaf} ","date":"2012-10-10","description":"","lastmod":"2012-10-10T08:13:34Z","slug":"1700","tags":["eclipse","java"],"title":"在Eclipse中给JAVA项目传递参数","url":"https://blog.zengrong.net/post/1700/"},{"categories":["technology"],"content":"2013-05-29更新： 加入adt的-validityPeriod参数\nGoogle Play的比App Store的要求松太多，可以制作一个自签名证书来对自己的应用进行签名。\n有许多工具可以生成这个自签名证书。下面讲讲在使用AIR发布Google Play应用的时候，如何生成需要的证书。\n使用AIR ADT工具生成p12证书 AIR SDK中包含的ADT工具提供了方法让我们创建一个自签名的p12证书。\n下面的代码生成一个所有者为zengrong.net，密码为123456，保存在当前目录下，文件名为zrong.p12。\n1adt -certificate -cn zengrong.net 1024-RSA zrong.p12 123456 使用Flash Builder的”导出发行版”功能，也可以基于图形界面生成一个p12证书，用于给AIR程序签名。\n关于使用ADT签名的详细信息，可以看下面两篇文章：\nADT 代码签名选项 使用 ADT 创建自签名证书 但是，这样得到的证书打包出的Android应用程序，并不能提交到Google Play上。\n这是因为Google Play要求对应用程序进行签名的证书，过期日期必须在2033年10月22日之后。而ADT生成的数字证书，只有5年有效期。\n我们可以用JDK提供的keytool工具来查看刚才生成的证书的具体信息：\n1keytool -list -keystore zrong.p12 -storetype pkcs12 -v 2 3输入密钥库口令: 4 5密钥库类型: PKCS12 6密钥库提供方: SunJSSE 7 8您的密钥库包含 1 个条目 9 10别名: 1 11创建日期: 2012-9-27 12条目类型: PrivateKeyEntry 13证书链长度: 1 14证书[1]: 15所有者: CN=zengrong.net 16发布者: CN=zengrong.net 17序列号: 2d35623566386132633a31336130373138653863333a2d38303030 18有效期开始日期: Wed Sep 26 17:40:04 CST 2012, 截止日期: Wed Sep 27 17:40:04 CST 2017 19证书指纹: 20 MD5: 6C:EB:0D:73:1A:15:C9:12:5E:DE:69:BA:C7:C2:0F:23 21 SHA1: 3E:2C:38:0A:CA:D5:D0:5B:67:80:92:50:46:36:99:82:1D:41:C9:25 22 SHA256: 83:C4:F8:4F:7C:97:CB:EC:51:64:BC:B2:D0:DA:E8:97:48:C1:FD:BF:A1:8F:45:A5:75:39:81:E9:6A:51:7C:FB 23 签名算法名称: SHA1withRSA 24 版本: 3 从该证书的截至日期，可以看出这个证书在2017年9月27日过期。\n如果使用这个证书来对Android程序进行签名，在网上提供给别人下载是可以的，提交野鸡市场也是可以的。但如果提交到Google Play，Google就会提示你证书的时间有问题，并拒绝提交。\n如果希望设置生成的证书的有效期限，可以使用 -validityPeriod 参数。下面这段代码生成有效期限为25年的证书，其余参数相同。\n1adt -certificate -validityPeriod 25 -cn zengrong.net 1024-RSA zrong.p12 123456 使用这个证书，重新对AIR程序进行打包，就可以提交Google Play了。\n使用JDK keytool工具生成p12证书 keytool工具包含在JDK中，如果没有JDK，猛击下载。\nkeytool支持交互的方式提供证书信息。要生成一个p12证书，必须了解这样几个参数：\n-genkeypair 生成证书 -keystore 生成证书的路径和文件名 -storetype 生成的证书类型，使用pkcs12指定p12格式证书 -validity 有效期的天数，用一个足够大的值跳转到2034年 下面是一个例子：\n1keytool -genkeypair -keystore zrong2.p12 -storetype pkcs12 -validity 8050 2输入密钥库口令: 3再次输入新口令: 4您的名字与姓氏是什么? 5 [Unknown]: zengrong.net 6您的组织单位名称是什么? 7 [Unknown]: zengrong.net 8您的组织名称是什么? 9 [Unknown]: zengrong.net 10您所在的城市或区域名称是什么? 11 [Unknown]: WUHAN 12您所在的省/市/自治区名称是什么? 13 [Unknown]: HUBEI 14该单位的双字母国家/地区代码是什么? 15 [Unknown]: CN 16CN=zengrong.net, OU=zengrong.net, O=zengrong.net, L=WUHAN, ST=HUBEI, C=CN是否正确? 17 [否]: y 还是用上面的方法验证一下证书的有效期：\n1keytool -list -keystore zrong2.p12 -storetype pkcs12 -v 2输入密钥库口令: 3 4密钥库类型: PKCS12 5密钥库提供方: SunJSSE 6 7您的密钥库包含 1 个条目 8 9别名: mykey 10创建日期: 2012-9-27 11条目类型: PrivateKeyEntry 12证书链长度: 1 13证书[1]: 14所有者: CN=zengrong.net, OU=zengrong.net, O=zengrong.net, L=WUHAN, ST=HUBEI, C=CN 15发布者: CN=zengrong.net, OU=zengrong.net, O=zengrong.net, L=WUHAN, ST=HUBEI, C=CN 16序列号: 1faa29fb 17有效期开始日期: Thu Sep 27 18:23:31 CST 2012, 截止日期: Thu Oct 12 18:23:31 CST 2034 18证书指纹: 19 MD5: F8:00:9C:3B:7B:4F:F2:9D:A3:B6:3F:E9:78:2D:9A:46 20 SHA1: 10:21:FF:B3:DE:3F:D4:0D:44:F7:D1:07:6A:3F:09:D8:36:B9:D1:21 21 SHA256: AB:8A:09:5B:69:1F:95:A5:94:F7:60:F6:D0:81:8A:1D:23:42:94:3C:96:D3:04:AD:C9:59:05:14:2E:B6:6D:79 22 签名算法名称: SHA1withDSA 23 版本: 3 参考文章 http://blog.csdn.net/kmyhy/article/details/6431609 http://5aijava.iteye.com/blog/123269 http://www.android123.com.cn/androidkaifa/173.html ","date":"2012-09-27","description":"","lastmod":"2012-09-27T10:44:08Z","slug":"generate_p12_centificate","tags":["air","android","google"],"title":"生成Google Play需要的p12自签名数字证书","url":"https://blog.zengrong.net/post/generate_p12_centificate/"},{"categories":["technology"],"content":"要高效的使用一个软件，首先需要了解它的快捷键。\n我准备深入使用一下FlashDevelop(以下简称FD)，却发现它的快捷键完全与Flash Builder不同。\n这让我觉得很不可思议。是有意为之，还是无心之过？\n或许这是开源软件的一个不成文的规定？比如 GIMP 就和PhotoShop的快捷键完全不同。这可能是为了规避版权风险也未可知。\n那么，怎样设置FD的快捷键呢？\n在 Tools-\u0026gt;Keyboard Shortcuts 菜单的界面中，可以显示当前的快捷键。但是这个界面只能显示，不能设置。\n如果需要设置FD菜单中的快捷键，需要手动编辑FD安装目录/Settings/MainMenu.xml这个文件。这显得略有不便。但是对于FD的使用者来说，这应该不是太大的问题。\n但是，前面我特别提到，这个文件只能修改FD标准菜单中的快捷键。部分使用插件实现的功能的快捷键，在这里无法设置。\n例如，Search-\u0026gt;Find All References这个功能，在MainMenu.xml中就找不到。\n因为这个功能是使用插件CodeRefactor来实现的，而对于这类使用插件实现的功能，必须在插件设置界面中来设置快捷键。\n遗憾的是，CodeRefactor 插件并没有提供快捷键设置功能。\nFind All References 功能对应在Flash Builder中的快捷键是 Ctrl+Shift+G 。这是个非常常用的功能，方便在所有的源码中搜索到一个方法或者变量的引用。如果必须用菜单来操作的话，易用性就大打折扣了。\n还有一个常用的快捷键在FD中无法设置：alt+/。这个快捷键用来展开自动完成列表。\n幸运的是，FlashDevelop爱好者群(257978195)的Freedom修改了CodeRefactor和ASCompletion两个插件，让它们可以支持快捷键设置。\n将这两个插件下载后覆盖到FD安装目录/Plugins目录覆盖同名文件，然后重启FD，在Tools-\u0026gt;Program Settings中找到对应的插件设置快捷键即可。\n需要注意的是，这些快捷键必须设置成2键，如果设置成3键将没有效果。\n","date":"2012-09-25","description":"","lastmod":"2012-09-25T01:44:50Z","slug":"flashdevelop_shortcuts","tags":["flashbuilder","flashdevelop"],"title":"FlashDevelop的快捷键设置","url":"https://blog.zengrong.net/post/flashdevelop_shortcuts/"},{"categories":["technology"],"content":"\u0026quot;=========== Meta ============ \u0026quot;StrID : 1692 \u0026quot;Title : 在64位操作系统上使用FlashDevelop的Debug功能 \u0026quot;Cats : 技术 \u0026quot;Status: draft \u0026quot;Preview: https://blog.zengrong.net/post/1692.html\u0026amp;preview=true \u0026quot;Tags : FlashDevelop, Flex, JAVA \u0026quot;========== Content ========== 最近用上了FlashDevelop。与Flash Builder比起来，它确实优点很多：小巧，快速，灵活的定制功能，免费且开源。\n使用FlashDevelop开发AS/Flex/AIR程序的时候，可以使用Flex SDK来编译和调试。Flex SDK使用JAVA写成，需要系统中安装JAVA虚拟机（JVM）。而我的系统中已经安装了64位的JAVA虚拟机。\n在调试的时候，FlashDevelop报告了下面的错误。\nDebugger startup error: System.BadImageFormatException: 试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B) 在 net.sf.jni4net.jni.JNI.Dll.JNI_GetDefaultJavaVMInitArgs(JavaVMInitArgs* args) 在 net.sf.jni4net.jni.JNI.Init() 在 net.sf.jni4net.jni.JNI.CreateJavaVM(JavaVM\u0026amp; jvm, JNIEnv\u0026amp; env, Boolean attachIfExists, String[] options) 在 net.sf.jni4net.Bridge.CreateJVM() 在 net.sf.jni4net.Bridge.CreateJVM(BridgeSetup setup) 在 FlashDebugger.DebuggerManager.Start(Boolean alwaysStart) FlashDevelop是直接调用Flex SDK中的fdb进行调试的，出现这个错误的原因，是因为fdb仅支持32位的JVM。\n可是，JAVA不是平台无关的么？为什么fdb却只能支持32位的JVM？\n的确，纯JAVA程序确实是平台无关的，但是调用了JNI就不同了，JNI是受平台限制的。而通过上面的报错信息，明显能看出是JNI在报错。\n找到了问题所在，解决起来就容易了。 下面是解决步骤：\n安装32位的JVM。JVM是允许32位和64位共存的。 将环境变量JAVA_HOME改为指向32位JVM的安装路径。 搞定。 但是，在64位操作系统中修改JAVA_HOME环境变量指向32位JVM是个愚蠢的做法。因为这样会导致操作系统中默认使用32位的JVM。所以，有个稍微麻烦一点的办法。\n安装32位的JVM。我的JVM 32bit安装在C:\\Program Files (x86)\\Java\\jre7目录。 在FlashDevelop.exe文件相同的目录下创建一个startFD.bat文件，写入如下内容： set JAVA_HOME=C:\\Program Files (x86)\\Java\\jre7 start FlashDevelop.exe 双击startFD.bat，程序会首先设置JAVA_HOME变量，然后启动FlashDevelop，并关闭cmd窗口。 使用这种方式设置的JAVA_HOME环境变量，只在启动FlashDevelop.exe的时候有效，不会影响系统的已有的环境变量。\n网上还能搜到一些其它的解决方案，让我们来看看：\n方案1，来源\n复制 jre\\bin中的msvcr71.dll到Windows\\System32下就可以了 这个方案明显是针对32位操作系统的，所以解决不了本文的问题。 方案2，来源\nGoogling about this problem tells that many users have this in JDK 6 solved with msvcr71.dll, but not for me :( And it's because I have latest JDK 7 which needs msvcr100.dll, so just find this DLL in \"jre7/bin\" directory and copy to FlashDevelop.exe folder. For thos who had BadImage problem while building on x64 system, don't forget, that now FD4 uses x32 component, so set JAVA_HOME to point to x32 version of JDK. 这个方案说的比较详细，也指明了JDK7与JDK6所需的msvcr*.dll并不相同。不过按这个方案也是解决不了本文的问题的。倒是最后那句话给了我解决问题的启示。 这是FlashDevelop社区针对这个问题的讨论\n","date":"2012-09-12","description":"","lastmod":"2012-09-12T14:01:46Z","slug":"flashdevelop_debug_in_x64windows","tags":["flashbuilder","flashdevelop","flex","java"],"title":"在64位操作系统上使用FlashDevelop的Debug功能","url":"https://blog.zengrong.net/post/flashdevelop_debug_in_x64windows/"},{"categories":["technology"],"content":"写了9年博客，尝试过多种博客写作工具，但一直没有持续用下去，原因是使用博客写作工具会弄乱HTML代码，另外一些插件的语法在这些写作工具中并不支持。\n但WordPress提供的编辑器并不好用，这几年来我一直都是用Vim以MarkDown格式写博客。写完后将内容粘贴到WordPress的在线编辑器中发布。\n一直都知道有Vimpress这类插件，可以直接使用xmlrpc在Vim中发布博文，这几天终于付诸于实践，把它装上了。\n在Vim的官方网站上，这类插件主要有下面几个：\nVimpress\n这是最早的一个，但2007年就停止更新了； Vim Blog\n这个是在Vimpress的基础上进行修改的一个版本； VimRepress\n这个也是基于Vimpress的修改，但修改得更彻底，加入了许多功能； blogit.vim\n从帮助看，这个比VimRepress更强大，而且并不是基于Vimpress修改的。 这些插件都需要Python支持，如何让自己的Vim支持Python，可以看这篇文章：不重新编译，让官方网站下载的Vim支持Python\n我一一使用了上面的插件。VimRepress和blogit.vim我始终无法调试成功。不知道是不是配置错误。\n我目前使用的，是 Vim Blog 。虽然功能少点，但也足够用了。\n使用方法在插件的源文件里面已经说的非常清楚了，相关文章在Google上一搜一堆，我这里不再介绍。\n但是关于中文乱码的问题，网上的文章都没做过介绍。这里讲一下解决方案。\n如果使用 :BlogList 得到的中文文章标题乱码，可以使用 set encoding=utf-8 来解决。这样设置后，可能会导致你的软件界面出现乱码，但这并不影响使用。写完博客之后，在使用 set encoding=chinese 把设置改回来就可以了。\n如果希望了解Vim的编码选项(也就是弄懂上面为什么这么写)，可以看这篇文章：VIM中与编码有关的选项\n","date":"2012-09-09","description":"","lastmod":"2012-09-09T04:48:02Z","slug":"use_vimpress","tags":["python","vim","wordpress"],"title":"vimpress使用解惑","url":"https://blog.zengrong.net/post/use_vimpress/"},{"categories":["technology"],"content":"在为Vim安装VimPress插件的时候，发现它需要Python支持。查了一些资料，原来在编译Vim的时候，是可以将Python语言整个编译进入Vim中的。这样的Vim可以直接使用Python语言来写扩展。\n除了编译嵌入方式，在Windows系统中，Vim还可以以动态链接的方式支持Python。\nVim的帮助文件中这样描述：\nVim 可以用四种方式编译 (:version 输出结果):\n无 Python 支持 (-python、-python3) 只有 Python 2 支持 (+python 或 +python/dyn、-python3) 只有 Python 3 支持 (-python、+python3 或 +python3/dyn) Python 2 和 3 支持 (+python/dyn、+python3/dyn) 我的Vim是在Vim官方网站下载的32位windows版。怎么知道我的Vim有没有包含Python支持呢？\n使用:version命令可以查看Vim编译时候的选项，看看输出的信息中是否包含上面帮助中描述的选项，就能知道自己使用的Vim对Python的支持情况。\n在Vim官方网站下载的32位Vim 7.3.46版，是带有+python/dyn和+python3/dyn选项的。所以我们只需要再安装Python环境就行了。\n安装的过程也很纠结，许多地方需要注意。我辗转Stack Overflow和Google数次才搞定。下面是具体的过程。\n我的计算机环境：\nWindow 7 64bit Vim 7.3.46 32bit 访问下载Pyton安装包，使用Python2还是Python3取决于你自己。\n但要注意，一定要下载和自己Vim匹配的版本。例如我的Windows虽然是64bit，但是由于Vim是32bit，这里就必须下载32bit的Python安装包。\nPython官方网站非常贴心的为天朝用户准备了一个“下载”链接。你一定要点击那个链接下载。否则，你会被GFW干掉。\n安装过程没有什么好说。我选择的路径是d:\\Python2.7。\n设置环境变量中的PATH变量，将你的Python安装路径加到PATH末尾。\n我永久性鄙视Windows的环境变量配置界面，都多少年了，还搞那么个小框，居然还不能拉大！\n重启电脑。重启的原因是我们需要让新的环境变量生效。虽然有很多非官方的方法可以让环境变量的改变立即生效，但某些时候貌似不靠谱。我再次永久性鄙视微软。\n进入Vim界面，输入echo has(\u0026quot;python\u0026quot;)，如果输出1，代表你的配置成功了。\n如果配置没有成功，仔细检查以下几条：\n你的Vim是否是在官方网站下载的Vim 32bit版本？ 你的Vim编译信息中是否包含+python/dyn或者+python3/dyn？检验方法见上文。 你下载的Python版本是否和Vim版本不匹配？他们必须同时是32bit或者64bit版才行。 你是否正确设置了环境变量？可以使用win+r然后输入python回车试试。如果能显示python提示符，说明你的设置正确。 设置了环境变量后，是否重启了计算机？ 如果试了所有的办法均不奏效，那就重新编译一个支持Python的Vim吧！\n祝你成功！\n","date":"2012-09-08","description":"","lastmod":"2012-09-08T09:31:16Z","slug":"pyton_support_on_vim","tags":["python","vim","wordpress"],"title":"不重新编译，让官方网站下载的Vim支持Python","url":"https://blog.zengrong.net/post/pyton_support_on_vim/"},{"categories":["technology"],"content":"Android中SharedPreferences的模式\n在Android开发中，使用SharedPreferences来共享一些小的配置数据是非常方便的。可是我发现在不同版本上，SharedPreferences的表现并不一样。\n我的测试机是Android 2.3.6，程序的写入和读取都正常。但把相同的程序在Android 4.1上运行，就发现虽然写入正常，但刚刚写入的数据不能被读取到。\n经过仔细调试，发现在Android 4.1中，读取到的写入的SharedPreference并不在同一个线程中，其实是2个不同的SharedPreference。\n找到SDK文档，才发现可以通过设置 [Context.getSharedPreferences](http://developer.android.com/reference/android/content/Context.html#getSharedPreferences(java.lang.String, int)) 的第二个参数解决这个问题。\n因为这个方法比较简单，一直没怎么看文档，直接写0代表私有访问模式。没想到这个方法的第二个参数从Android 3.0开始有了变化。\n下面是第二个参数mode的说明：\nOperating mode. Use 0 or MODE_PRIVATE for the default operation, MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE to control permissions. The bit MODE_MULTI_PROCESS can also be used if multiple processes are mutating the same SharedPreferences file. MODE_MULTI_PROCESS is always on in apps targetting Gingerbread (Android 2.3) and below, and off by default in later versions.\n下面是 MODE_MULTI_PROCESS 的说明：\nSharedPreference loading flag: when set, the file on disk will be checked for modification even if the shared preferences instance is already loaded in this process. This behavior is sometimes desired in cases where the application has multiple processes, all writing to the same SharedPreferences file. Generally there are better forms of communication between processes, though. This was the legacy (but undocumented) behavior in and before Gingerbread (Android 2.3) and this flag is implied when targetting such releases. For applications targetting SDK versions greater than Android 2.3, this flag must be explicitly set if desired.\n也就是说， MODE_MULTI_PROCESS 这个值是一个标志，在Android 2.3及以前，这个标志位都是默认开启的，允许多个进程访问同一个 SharedPrecferences 对象。而以后的Android版本，必须通过明确的将 MODE_MULTI_PROCESS 这个值传递给mode参数，才能开启多进程访问。\n所以，我们在获得 SharedPreferences 的时候，需要判断一下SDK的版本号：\n1int __sdkLevel = Build.VERSION.SDK_INT; 2SharedPreferences __sp = $context.getSharedPreferences(SETTING_NAME, (__sdkLevel \u0026gt; Build.VERSION_CODES.FROYO) ? 4 : 0); ","date":"2012-09-04","description":"","lastmod":"2012-09-04T15:54:33Z","slug":"android_context_getsharedpreferences","tags":["android","java"],"title":"Android中SharedPreferences的模式","url":"https://blog.zengrong.net/post/android_context_getsharedpreferences/"},{"categories":["technology"],"content":"改变AIR for Android消息通知栏默认图标\n如果从愤怒的角度来说，这个勉强可以算作AIR的BUG，但我知道不是。估计这事儿也只有我能碰上。且听我细细道来……\nshow notification in Android 在Android中显示消息通知，是个很简单的事情，见下面的代码：\n1Intent __activityIntent = _context.getPackageManager().getLaunchIntentForPackage(_setting.getPackageName()); 2if(__activityIntent == null) throw new NullPointerException(\u0026#34;无法获取到名称为【\u0026#34;+_setting.getPackageName()+\u0026#34;】的Intent!\u0026#34;); 3Notification __msg = new Notification(R.drawable.ic_launcher, $ticket, System.currentTimeMillis()); 4ApplicationInfo __info = _context.getApplicationInfo(); 5__activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 6PendingIntent __intent = PendingIntent.getActivity(_context, getRequestCode(), __activityIntent, PendingIntent.FLAG_UPDATE_CURRENT); 7__msg.ledARGB = $color; 8__msg.ledOnMS = 300; 9__msg.ledOffMS = 1000; 10__msg.flags |= Notification.FLAG_SHOW_LIGHTS; 11__msg.flags |= Notification.FLAG_AUTO_CANCEL; 12__msg.defaults |= Notification.DEFAULT_SOUND; 13__msg.setLatestEventInfo(_context, $title, $msg, __intent); 14NotificationManager __nm = (NotificationManager) _context.getSystemService(Context.NOTIFICATION_SERVICE); 15__nm.notify(0, __msg); 上面的代码基于Android 2.2，Android 3.0以后有更好的方法，google也不推荐使用这样的方法。但我们为了兼容旧设备，只能这么用。\n将这段代码编译后打包成ANE，在AS中调用，在Android设备中调试运行，就可以弹出一个通知栏，显示的图标是AIR的程序配置文件中配置的图标。\n想要知道如何打包ANE，可以参考Adobe的官方教程（中文）\n但是本文讲的不是这么简单的东西，本文讲的是一个相当纠结的问题。\n问题出现 这个方法在我的设备中一直运行得很好，直到有一天，当我要发布它的时候，出问题了。\n显示在Notification bar区域的图标，变成了AIR的红色图标，而不是我的应用的图标了。就像下面这样：\n而我的应用的图标，原本是这样的：\n这个问题让我百思不得其解，为什么在调试的时候正常，在正式的发布版之后就不正常了么？郁闷的寻找了一段时间之后，一个偶然的机会让我发现了该问题的原因。\n问题原因 我们知道，AIR在打包成Android apk文件的时候，可以选择AIR运行时的处理方式。我们可以选择“共享AIR运行时(apk)”和“运行时绑定(apk-captive-runtime)”两种方式。\n在调试的时候，Flash Builder会直接将apk打包成共享AIR运行时版本。而在发布的时候，我们一般都会选择运行时绑定。至于原因，你懂的。\n而这两种运行时打包方式对于图标的处理方式是不一样的。我解压了同一个项目的“共享运行时”和“运行时绑定”apk文件，发现他们的res/drawable目录中的图像文件不同。在“共享运行时”的apk文件中，该目录只有一个alert形式的半透明图标，而“运行时绑定”的apk文件中，则多出了一个AIR的默认图标。\n看完这张图，出现AIR默认图标的原因已经找到了，下面是分析。\n问题的分析 由于应用需要支持多种分辨率，Notification bar的图标并不是使用一个图标文件来指定的，而是使用一个编号。也就是上面代码中的 R.drawable.ic_launcher 。这是一个int类型的值。\n在ANE的代码中指定的这个常量，其实和AIR项目并没有什么关系，ANE项目是没有界面的，所使用的资源与AIR项目的资源也完全不同。将ANE打包到AIR项目中之后，就会改用AIR项目的资源。\n但为什么在ANE中指定的图标编号值，在AIR项目中依然有作用呢（仅限“共享AIR运行时”）？\n为了弄清这个问题，我创建了一个原生的Android项目。我发现默认情况下，它使用的图标也指向 R.drawable.ic_launcher ，而且这个常量的值与ANE项目中的值完全相同，都是 0x7f020000 。\n我可以这样认为，这是Android项目的默认程序图标常量值。既然是这样，那么AIR也会遵循这个常量值。因此，在ANE中指定的图标常量值正好和AIR中的图标常量值相同，这是个“正确的巧合”。\n在“共享AIR运行时”的时候，因为apk的 res/drawable 目录中没有其他的系统图标，Notification 会自动去 res/drawable-hdpi；res/drawable-ldpi;res/drawable-mdpi 3个图标文件夹下寻找匹配的图标。这3个文件夹中保存的就是我们在AIR程序配置文件中指定的程序图标。\n在“运行时绑定”的APK文件中，由于AIR添加了一个默认图标，Notification 显示的时候就直接中又直接调用了 res/drawable 中的这个图标，因此显示的就是默认图标了。\n问题解决 有了上面的分析，我只要在指定Notification图标的时候，指定一个图标资源的对应常量值，就能够得到正确的图标了。但可惜的是，除了我自己要求AIR包含的文件外，我并不知道AIR在打包的时候将哪些图标文件放在了APK包中，也不知道它们的常量是什么。\n在使用 Android SDK 开发的应用中，这些常量都在SDK自动生成的R类中，我很容易得到他们。但AIR并没有告诉我怎么得到这些资源。\n看来我只能自己想办法。\n我发现，Android SDK自动生成的 R.java 文件中的常量值是有规律的，比如：\ndrawable 资源都以 0x7f02 开头； string 资源都以 0x7f04 开头； id 资源都已0x7f07`开头； 从0000开始顺号排列。 如下所示：\n1public final class R { 2 public static final class attr { 3 } 4 public static final class drawable { 5 public static final int ic_action_search=0x7f020000; 6 public static final int ic_launcher=0x7f020001; 7 } 8 public static final class id { 9 public static final int menu_settings=0x7f070002; 10 public static final int textView1=0x7f070000; 11 public static final int toggleButton1=0x7f070001; 12 } 13 public static final class layout { 14 public static final int activity_main=0x7f030000; 15 } 16 public static final class menu { 17 public static final int activity_main=0x7f060000; 18 } 19 public static final class string { 20 public static final int app_name=0x7f040000; 21 public static final int hello_world=0x7f040001; 22 public static final int menu_settings=0x7f040002; 23 public static final int title_activity_main=0x7f040003; 24 } 25 public static final class style { 26 public static final int AppTheme=0x7f050000; 27 } 28} 我可以这样认为，0x7f020000 就是第一个图标文件的常量值，而第二个图标文件应该是 0x7f020001 ，第三个是 0x7f020002 ，第四个……唔，没有第四个，如果使用 0x7f020003 ，AIR会直接崩溃退出。\n测试证明，我的猜想是正确的。至此问题解决。\n感受 和Adobe打交道这么多年，已经被无数的BUG折磨得“百度不亲”。Flex 的 BUG 因为有源码，可以自己动手解决。而 Flash Player和AIR的BUG就只能想办法绕过 。现在做 AIR for mobile 开发也有一段时间了，碰到了不少棘手的调试问题 ，忍受了 ipa 那乌龟一般的编译速度和 iTunes 那烂到无敌的用户体验，最后在这个不是 BUG 的问题上纠结了2天时间，彻底无语了……\n从Adobe的角度看，在自己的产品中保留一个自己的默认图标，好像也无可厚非。从我的角度看，既然选择用 AIR 技术，碰到这样的问题，只能怪我手贱。\nAIR for mobile给我的感觉，就像是一个保险箱，在我往里面放东西的时候，非常顺手。但我要修理它的时候，却发现我没有工具、没有手册、也没有指导。\n当然，个人能力有限，也许我对Android更加了解之后，这个问题根本就不是问题了。\n有哪位Android专家能给点建议么？\n","date":"2012-08-29","description":"","lastmod":"2012-08-29T10:37:14Z","slug":"change_notification_icon_in_air_for_android","tags":["air","android","ane","java"],"title":"改变AIR for Android消息通知栏默认图标","url":"https://blog.zengrong.net/post/change_notification_icon_in_air_for_android/"},{"categories":["technology"],"content":"如何知道某个Activity是否在前台？\n有一个Android应用包含包含一个后台程序，该程序会定期连接服务器来实现自定义信息的推送。但是，当这个应用处于前台的时候，后台程序就没有必要连接服务器了。这样可以节省网络资源，也更省电。\n用什么方法知道该应用是否处于前台呢？\n网上搜到的方法大多数都是使用下面的代码：\n1ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE); 2//获得task列表 3List\u0026lt;ActivityManager.RunningTaskInfo \u0026gt; taskInfo = am.getRunningTasks(1); 4Log.d(\u0026#34;topActivity\u0026#34;, \u0026#34;CURRENT Activity ::\u0026#34;+ taskInfo.get(0).topActivity.getClassName()); 5ComponentName componentInfo = taskInfo.get(0).topActivity; 6componentInfo.getPackageName(); 但是查阅 Android文档 后发现，google并不推荐使用这个方法：\nThis should never be used for core logic in an application, such as deciding between different behaviors based on the information found here. Such uses are not supported, and will likely break in the future. For example, if multiple applications can be actively running at the same time, assumptions made about the meaning of the data here for purposes of control flow will be incorrect.\n而且，这个方法还要求设置android.permission.GET_TASKS权限。\n因此，我必须寻找更加合适的方法来做这件事。最终，我找到这个方法 getRunningAppProcesses() ，它并不需要增加特殊的权限。\n下面是范例代码：\n1/** 2 * 返回当前的应用是否处于前台显示状态 3 * @param $packageName 4 * @return 5 */ 6private boolean isTopActivity(String $packageName) 7{ 8\t//_context是一个保存的上下文 9\tActivityManager __am = (ActivityManager) _context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE); 10\tList\u0026lt;ActivityManager.RunningAppProcessInfo\u0026gt; __list = __am.getRunningAppProcesses(); 11\tif(__list.size() == 0) return false; 12\tfor(ActivityManager.RunningAppProcessInfo __process:__list) 13\t{ 14\tLog.d(getTAG(),Integer.toString(__process.importance)); 15\tLog.d(getTAG(),__process.processName); 16\tif(__process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND \u0026amp;\u0026amp; 17\t__process.processName.equals($packageName)) 18\t{ 19\treturn true; 20\t} 21\t} 22\treturn false; 23} ","date":"2012-08-28","description":"","lastmod":"2012-08-28T07:22:10Z","slug":"how_to_get_current_on_screen_activity","tags":["android","java"],"title":"如何知道某个Activity是否在前台？","url":"https://blog.zengrong.net/post/how_to_get_current_on_screen_activity/"},{"categories":["technology"],"content":"刷了几天labs，终于等来了。\n主要功能：\n支持Apache Flex SDK。\n获取Apache Flex SDK 新的编译器支持ActionScript项目；\nFB4.7中的AS项目支持新的编译器，编译时间将大幅改善。 支持创建ActionScrip Worker；\nFB4.7可以调试AS workers。你可以在AS中处理并发任务。详见A sneak peek: Concurrency with ActionScript Workers 和 Using ActionScript Workers 支持iOS USB部署、调试和测试，支持iOS模拟器；\nFB4.7让你能在在iOS设备上通过USB测试程序，当然也能通过iOS模拟器来测试。详见Test and debug an iOS application on a simulator 和 Test and debug an application on an iOS device。 配置多个目标平台\n详见：Support for multiple build targets 支持自定义ADT和ADL\n自定义ADT和ADL的打包和调试参数。详见Customize ADL and ADT parameter values 提升开发效率的特性：组织import声明\n在你的项目中管理、增加、合并import声明。详见Organize imports statements 提升开发效率的特性：快速助手\n详见Quick Assist 64bit windows:\nhttp://trials.adobe.com/pub/esd/labs/flashbuilder4-7/flashbuilder4-7_p1_win64_082712.exe 32bit windows:\nhttp://trials.adobe.com/pub/esd/labs/flashbuilder4-7/flashbuilder4-7_p1_win32_082712.exe 更多介绍：\nhttp://labs.adobe.com/technologies/flashbuilder4-7/ http://labs.adobe.com/technologies/flashbuilder4-7/?tabID=details#FAQ http://helpx.adobe.com/flash-builder/release-note/flash-builder-4-7-release-notes.html beta版序列号：1424-4008-5724-4257-2856-5289\n","date":"2012-08-28","description":"","lastmod":"2012-08-28T00:54:19Z","slug":"flash-builder-4-7-beta","tags":["flashbuilder","flex","ios"],"title":"Flash Builder 4.7 beta","url":"https://blog.zengrong.net/post/flash-builder-4-7-beta/"},{"categories":["news"],"content":" 译文转自：http://www.oschina.net/question/28_65139 英文原文：http://thecodist.com/article/bad_software_worse_solutions_programming_will_always_be_hard 最近读了一篇文章 Software Runs the World: How Scared Should We Be That So Much of It Is So Bad?，这篇文章没什么可说，但是评论却很有趣，特别是很多计算机科学的博士表示，使用一些规范的方法，可以让软件变的更好，还有规范、认证、授权。\n对于这些想法，我只能说“祝你好运”。我干码农干了30年，我可以很肯定的说“没有银弹”，没有神奇的方法或者论文可以保证让软件变得完美，没有bug。\n事实上，我们甚至很难定义一个程序，就更别提如何开发一个完美的程序了。软件在太多领域存在，有太多的编程语言，业务需求，还有运行环境，并且要和各种不确定的系统交互。你觉得你的iPhone 游戏，股票交易系统，烤面包机还有火星登陆器有什么共同之处吗？你觉得给汇编，C，Java，PHP，Fortran，LIST和 Erlang 颁发证书可行吗？还有各种混合语言 web 应用，以及分布式系统。\n你需要正式的描述太多的东西，光列出所有的编程语言似乎就不可能，更不用说如何用这些语言写出完美的程序了。\n我喜欢人们拿程序和社会工程对比。建造一座桥的基础在过去的两千年中没有改变过：你需要考虑重力，风力，下雨以及炎热还有很多基本的材料。这些东西罗马的工程师就知道了。但是编程不一样，我们为每个程序创造一个独立的世界，或者说我们把各种世界元素整合在一个世界中，然后在这个世界中建造几百座不同的桥，它们要同时良好的工作，还要考虑到我们创造的世界会不可预知的改变。\n还有，我们造一座桥需要经过几年的规划和建造，然后使用几十年，上百年而不用进行任何修改。而软件在一直的变化当中，不管是在开发的时候还是开发完以后。美国在过去的两百年间建造了60万座桥。但是过去五年中我们就创造了一百万个 iPhone 的应用。应用程序可以从几行到几千万行代码，并且在现代生活的每一个角落存在。\n让编程如此困难的原因是它无处不在，可以影响一个人或者10亿人，可以运行几十年或者只需要运行一次。它跟造桥完全不一样。最近的火星登陆器是用几百万行C代码写的，并且只运行一次。而高速股票交易系统一秒钟就要被执行无数次。\n认为世界上存在一套标准的方法可以让你保证开发的程序是完美的这种想法很可笑。即使这种完美的奇迹存在，它的开销也会让它无法实际实施。好吧，你可以证明一段代码没问题，但是它运行在哪里呢？你能控制操作系统吗？还是其他公司提供的WebService 接口？你不能控制用户的交互，你也不能控制CPU，那么你如何保证这段完美的代码可以完美的运行？\n我宁愿相信你发明了时间机器也不愿意相信你的标准流程可以在这个混乱的世界行得通。\n另外一个“善意的谎言”是我们可以通过标准的测试和认证来证明一个程序员可以编写完美的代码。程序员不是管道工，管道工和水，管道，气体还有阀门打交道。这些东西几乎不会改变。它们遵循物理规律，是经过认可的科学，它们不会莫名其妙的变成另外一个东西。\n我认识一个程序员通过了各种 Java 认证，但是还是没有能力编写实际的应用程序。想象一下，要为一个人进行所有语言的认证，所有程序的认证，从面包机到宇宙飞船。要么你创造一些非常基础而无用的认证，或者非常细节的认证而没有人在乎。考虑到所有这些，还有不断变化的需求，当你需要开发一些新东西的时候你怎么办？把所有人炒了，然后雇佣新的人？\n我在工作中和一位 UX 设计师一起培训一组 Java Web 程序员开发 iOS 程序。即使一个有经验的开发者突然做一些不一样的开发也是很困难的。但是假如我们一定坚持要正规的流程和经过认证的iOS 程序员才能在市场上竞争，那么我们可能已经关门了。\n我们三个人花了两个月时间开发了一个新的 iPhone 应用。我们只有不断快速的迭代开发才能在不断变化的需求和竞争中胜出。期望一个正规的流程在发布前验证所有的代码是噩梦。在产品发布几年后公司可能都不存在了，谁有在乎这个程序是否被认证过呢？\n现实是残酷的。30多年的编程经验，可以让我非常自信的说我写的代码还不错。在 1987-1994 年间，我领导开发并且要最终敲定发布的二进制码。我们通过软盘的形式发布了三个 Mac 应用，11个主要版本，没有一次大的事故（要知道每次发布的硬件成本就是100万美元）。我们发布的代码很完美吗？我们是经过认证的 Mac OS 开发者/设计师吗？不是！但这些应用解决用户的问题了吗？是的。\n我说的这些能证明任何东西吗？不能。但是好的代码确实存在，并且常常出自没有证书，没有学历，没有被证明过的程序员之手。\n也许有一天机器人可以代替人来开发软件，并且保证不出错。人类就可以幸福的生活在完美的软件创造的世界中了。\n但是我更相信时间机器会先出现。并且软件是由人类编写的。\n","date":"2012-08-26","description":"","lastmod":"2012-08-26T14:45:04Z","slug":"no-perfect-software","tags":["develop","study"],"title":"【转】没有完美的软件：编程永远不容易","url":"https://blog.zengrong.net/post/no-perfect-software/"},{"categories":["technology"],"content":"删除已经安装的项目：\n1yum erase ruby ruby-libs ruby-mode ruby-rdoc ruby-irb ruby-ri ruby-docs 安装依赖：\n1yum -y install zlib-devel curl-devel openssl-devel httpd-devel apr-devel apr-util-devel mysql-devel 下载Ruby源码并解压缩：\n1cd ~/Downloads #进入下载目录 2lftp ftp.ruby-lang.org 3lftp ftp.ruby-lang.org:/\u0026gt; cd /pub/ruby 4lftp ftp.ruby-lang.org:/pub/ruby\u0026gt; 5lftp ftp.ruby-lang.org:/pub/ruby\u0026gt; get ruby-1.9.3.pXXX.tar.gz # 2012-08-26，XXX为194 6lftp ftp.ruby-lang.org:/pub/ruby\u0026gt; exit 7tar zxvf ruby-1.9.3.pXXX.tar.gz 编译安装：\n1cd ruby-1.9.3.pXXX 2./configure 3make \u0026amp;\u0026amp; make install 检查安装：\n1ruby -v 2ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux] 参考文章：\nhttp://www.redmine.org/projects/redmine/wiki/Redmine_on_CentOS_installation_HOWTO http://freshblurbs.com/install-ruby-1-9-centos ","date":"2012-08-26","description":"","lastmod":"2012-08-26T02:38:19Z","slug":"compile-install-ruby19-in-centos","tags":["centos","linux","ruby"],"title":"在CentOS上编译安装Ruby 1.9","url":"https://blog.zengrong.net/post/compile-install-ruby19-in-centos/"},{"categories":["technology"],"content":"2012-10-31更新：修改源地址，以前的源地址已经不能使用。\n原文：Installing Ruby 1.8.6 on CentOS with Yum\n64位系统：\n1$ sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm 2$ sudo rpm -Uvh http://download.elff.bravenet.com/5/x86_64/elff-release-5-3.noarch.rpm 3$ sudo yum install ruby ruby-shadow ruby-ri ruby-rdoc gcc gcc-c++ ruby-devel -y 4$ ruby -v 5ruby 1.8.6 (2010-02-05 patchlevel 399) [x86_64-linux] 32位系统：\n1$ sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm 2$ sudo rpm -Uvh http://download.elff.bravenet.com/5/i386/elff-release-5-3.noarch.rpm 3$ sudo yum install ruby ruby-shadow ruby-ri ruby-rdoc gcc gcc-c++ ruby-devel -y 4$ ruby -v 安装RubyGems：\n1$ wget http://rubyforge.org/frs/?group_id=126\u0026amp;release_id=43601 2$ tar xzvf rubygems-1.3.7.tgz 3$ cd cd rubygems-1.3.7 4$ sudo ruby setup.rb 5$ gem --version 61.3.7 ","date":"2012-08-26","description":"","lastmod":"2012-08-26T01:07:46Z","slug":"install-ruby186-in-centos-using-yum","tags":["centos","linux","ruby"],"title":"【译】使用Yum在CentOS上安装Ruby 1.8.6","url":"https://blog.zengrong.net/post/install-ruby186-in-centos-using-yum/"},{"categories":["technology"],"content":"2012-08-20：v0.7.2版发布\n解决增加帧有时会报错的bug； 在帧列表中选择多个帧的时候，Sheet上的显示范围也为多个帧（上个版本只能显示单个帧）； 在Sheet上单击的时候，自动选择帧列表中的对应帧。 更多的功能介绍以及软件下载，看这里。\n","date":"2012-08-20","description":"","lastmod":"2012-08-20T02:46:37Z","slug":"1672","tags":["air","spritesheet"],"title":"sprite sheet editor v0.7.2发布","url":"https://blog.zengrong.net/post/1672/"},{"categories":["news"],"content":" 译文转自：http://www.ituring.com.cn/article/details/8290 英文原文：http://java.dzone.com/programmers-are-bad-estimating 一个曾经与我一起工作过的经验丰富的项目经理声称，他拿到程序员的时间估算以后，先将它乘以π，然后转化下一个时间数量级后，才能得到真正的值。1天转化成3.14周。他过去因为程序员不擅长估算时间而吃尽了苦头。我创建了一个用来翻译程序员时间估算的表格，来尽量缩小估算错误。\n估算时间 程序员所想象的 程序员所忘记的 实际时间 30秒 只需要做一个很小的代码改动。我准确地知道怎么改，在哪里改。花费30秒敲键盘即可。 启动计算机，开发环境和获取正确源码的时间。用于构件，测试，检查和文档修复的时间。 1小时 5分钟 小事一桩，我只要上谷歌查一下语法就可以修复它了。 很少有一次就能找到完全正确的信息。即使找到，在它能工作前，也需要做一些调整。外加构件，测试等等时间。 2小时 1 小时 我知道怎么做，但是写这些代码需要花费一些时间。 面对未来可能发生的问题，1小时稍纵即逝。有些东西总是会出错。 2小时 4小时 需要写一些代码，但是我粗略地知道步骤。我知道标准框架中的Wizzabanga模块可以做到，不过我得查看文档，了解它的准确地调用方式。 这个大概是唯一现实的估算。它为意外的错误留下了足够大的余地，而这个任务也小到足以把握。 4小时 8 小时 我先要把Balunga类重构成2个，然后为Wizzabanga模块加一个调用，最后为GUI加一些字段。 总会有许多系统的不同部分依赖着Balunga类。大概有40个不同的文件需要修改。为GUI新加的字段，同样也需要加到数据库中。8小时太长，无法完全把握。总会有比程序员估算时更多的步骤出现。 12-16小时 2 天 真的有一大堆代码要写。我需要往数据库里加一些新table，显示table的GUI，还有读写table的代码逻辑。 对于大多数开发者来说，两天的工作量已经大到难以估算了。肯定会有什么东西被遗漏掉。不仅仅是一些小事情，而是整个一大块主要功能会被遗忘在估算中。 5 天 1 周 哎哟，这真是一项艰巨的任务。虽然我还没有思路，但我不能说我不知道。一周应该够了，我希望，我真心希望，但是我不能要求更多了，否则他们会认为我不够称职。 这个任务已经大到超过大多数程序员的理解了。它应该被发回给架构师，帮忙将它划分成更小的部分，然后提供一些解决问题的方向。架构师可能会发现一种更简单的方法来完成它，或者发现其实有更多超乎想象的工作。。。 2-20 天 时间估算时困难的。每一个程序员都有一个现实的估计区间。低于这个区间的估计意味着（构件，测试，检查代码的）时间开销被低估了。超过这个区间的估计意味着这个任务太大而很难预估。\n对于初级开发者来说，这个区间甚至都不存在。他们忽略（构件，测试，检查代码的）时间开销，同时困难的任务他们却又无法预估。我想说一个有经验的开发者应该在0.5至24小时将事情做完。超过24小时，就需要细分。这项工作应该在开发者的头脑中完成，然后总和到60小时。但是即使是有一些有经验的开发者也需要有利用管理时间块来思考。\n同样重要的是明白：编程经验不等同于估算经验。一个不被包含在估算流程中的开发者将不会擅长估算。同样，如果实际的时间花费不被测量和用于与估算比较，那么将没有反馈来学习。\n最后，每个程序员都应该具备估算的技能。为磨练这个技能，接手每个任务时，先决定你要做什么。然后在开始之前估算任务所需时间。最后测量实际花费时间，并与估算相比较。同样比较你实际完成的与计划完成的。这样你将会既提高你对一个任务包含细节的理解，同样也提高了你的估算技能。\n","date":"2012-08-20","description":"","lastmod":"2012-08-20T00:57:11Z","slug":"programmers-are-bad-estimating","tags":["study"],"title":"【转】程序员的时间换算表--为什么程序员不擅长估算时间","url":"https://blog.zengrong.net/post/programmers-are-bad-estimating/"},{"categories":["news"],"content":" 译文转自：http://www.aqee.net/im-sure-it-will-only-take-you-a-few-days-to-code/ 英文原文：http://danshipper.com/non-technical-people-cant-estimate-developmen “这个网站相当简单，所有你需要做的就是完成X，Y，Z。你看起来应该是技术很好，所以，我相信，你不需要花费太多时间就能把它搭建起来。”\n我时不时的就会收到这样的Email。写这些邮件的人几乎都是跟技术不沾边的人，或正在研究他们的第一个产品。起初，当听到人们这样的话，我总是十分的恼怒。他们在跟谁辩论软件开发所需要的时间？但后来我意识到，即使我自己对自己的项目预测要花去多少开发时间,我也是一筹莫展。如果连我自己都做不好，我何必对那些人恼怒呢？\n真正让我郁闷的不是他们预估的错误。问题在于他们竟然认为自己可以做出正确的估计。作为开发人员，我们经常会发现，在软件开发的问题上，一个外行人会很自然的把复杂的事情估计的很简单。\n这并不是为我们的愤怒找借口。但这引起了另外一个有趣的问题：为什么我们天生的预测复杂性的能力在遇到编程问题时会失灵？\n为了回答这个问题，让我们来认识一下我们的大脑如何估计事情的。有些事情对于一些没有经验的人也很容易预估正确，但有些事情则不然。\n我们来想想观看一个人弹吉他。即使你从来没有弹过吉他，在观看了一场弹奏《玛丽有只小羊羔(Mary had a Little Lamb)》的吉他表演后，你也能大概推测出这很简单，一个人不需要太高的技术就能演奏出来。同样，当观看了有人演奏D大调的《卡农(Pachabel’s Canon)》后，你也很容易推测出，这很复杂，需要很长时间的练习才能演奏的出来。\n为什么我们能够很迅速准确的预估这两首曲子的复杂性呢？这是跟我们用来判断一个事情简单和还是复杂的方法有关的。我们的大脑有一些现成的模式来完成这些事情，首先一个就是根据速度。这种情况下，大脑会辨别每秒钟演奏的东西。根据每秒钟演奏了多少东西，我们很容易有一个直观的判断曲子的复杂度。因为用吉他演奏一首歌是一种物理过程，一种感官上的活动，我们的大脑很容易依此来推测速度，继而转换成复杂度。\n我们还有另外一个天生的推测依据：体积。想想把一个帐篷和一栋公寓放在一起对比。即使一个人从来没有学过建筑学，他也能告诉你通常设计和建造一个帐篷会比设计和建造一栋公寓要简单。为什么？因为我们天生的会使用物理体积作为事物复杂性的一个指标。\n当然。上面说的这两种逻辑分析并不是总是100%的有效。但大多数情况下，人们就是这样干，而且很成功。大多数情况中，我们在对物理过程评估时，我们的大脑会对物理事物进行有效的关联，不需要依赖之前的经验。\n现在让我们来谈谈软件。当一个不懂技术的人试图对软件开发时间进行评估时，有两个很基本的直观指标在辅助他们：以体积为指标的复杂度和以速度为指标的复杂度。但他们没有意识到，软件跟他们想象的不一样。软件本质上不是有形物质。没有体积和速度。它的极小的组成部分可能会时不时的在电脑屏幕上闪现。正因为如此，当面对开发一个web应用时(或任何类型的软件)，我们的基本直观感觉失效了。\n这第一点，速度，很显然根本不可能被外行人拿来对软件进行评估。于是很自然的，他们倾向于使用体积指标进行评估。要么是根据描述文档的页数，要么是根据软件的功能用例数或特征数。\n有时候，这种评估手段确实有效！当面对一个静态网站，没有特别的设计要求，外行人很容易用这种方法估计出开发时间。但是，通常情况下，对于软件开发，体积并不能真实有效的反映复杂度。\n不幸的是，对于软件的复杂度，唯一有效的推测方法是依据经验。而且还不是时时都好用。作为一个程序员，我知道，根据我之前开发过的相似的功能特征，我可以估计出现在的这些功能特征各自要多少开发时间。然后，我把总时间加起来，这就得到了完成整个项目需要的大致时间。然而，事实情况中，每个项目在开发过程中都遇到二、三个瓶颈。这些瓶颈会肆意的消耗程序员的大量时间，你在遇到它们之前根本不会有所预见。它们会拖住整个项目，致使工期延后数周甚至数月。\n这些是没有经验的人在评估复杂度时不会理解的。他们不明白在其他事情上都很灵的方法，为什么放到软件开发上就不灵了。所以，下一次当你听到有人说“我想你几天时间就能把它开发出来”时，不管是谁说的，都不要懊恼。深呼吸一下，告诉他这篇文章的地址，自己该干什么还干什么。\n","date":"2012-08-20","description":"","lastmod":"2012-08-20T00:55:35Z","slug":"non-technical-people-cant-estimate-developmen","tags":["study"],"title":"【转】不懂技术的人不要对懂技术的人说这很容易实现","url":"https://blog.zengrong.net/post/non-technical-people-cant-estimate-developmen/"},{"categories":["technology"],"content":"2012-08-18：v0.7.1版发布\n解决加入图像的BUG； 解决保存的文件不包含文件名称的BUG； 帧列表可以改变高度，在帧较多的时候方便查看； 将设置预览图背景色的功能从右键菜单改为下拉列表。 更多的功能介绍以及软件下载，看这里。\n","date":"2012-08-18","description":"","lastmod":"2012-08-18T02:09:44Z","slug":"sprite-sheet-editor-v0-7-1","tags":["air","spritesheet"],"title":"sprite sheet editor v0.7.1发布","url":"https://blog.zengrong.net/post/sprite-sheet-editor-v0-7-1/"},{"categories":["technology"],"content":"在ANE中连接Socket服务器的注意事项\n前戏 也许你会奇怪，既然AS提供了Socket实现，为什么还要用ANE来实现Socket连接？\n在 在ANE插件中启动AIR开发的Android应用 一文的最后，我提到了一个应用案例，我现在将这个案例明确的说明一下。\n对于游戏开发者来说，我们希望能推送给用户一些消息。如果使用常规的手段，只能在用户打开游戏的时候，才能和服务器通信，收到这些消息。\n如果用户几天不上线，那么可能会错过这些消息，导致游戏中的公告、奖励不能及时到达。\n要解决这个问题，我们可以在Android系统中注册一个Service。这个Service长期保持与服务器的连接，或者隔段时间连接一次服务器，收到消息后马上推送给用户。\n这种Service，使用AIR是无法实现的，必须用ANE来解决。因此，我们不可能使用AS的Socket来连接服务器，必须用Android SDK提供的Socket连接方法。\n阻力 在JAVA中实现Socket客户端的方法很简单，这里提供一些简单(且不完整)的代码：\n1private void connectSocket() throws UnknownHostException, IOException 2{ 3\tSocket __socket = new Socket(); 4\t//超时10秒 5\t__socket.connect(new InetSocketAddress(\u0026#34;192.168.18.30\u0026#34;, 30000), 10000); 6\tInputStream __input = __socket.getInputStream(); 7\tOutputStream __output = __socket.getOutputStream(); 8\tif(__socket.isConnected()) Log.i(TAG, \u0026#34;连接成功\u0026#34;); 9\tbyte[] __sendByte = getSenderData(); 10\tLog.i(TAG, \u0026#34;发送数据的长度:\u0026#34;+__sendByte.length); 11\t__output.write(__sendByte); 12\twhile(true) 13\t{ 14\tif(__input.available()\u0026gt;0) 15\t{ 16\tbyte[] __bytes = new byte[__input.available()]; 17\t__input.read(__bytes); 18\tLog.i(TAG, \u0026#34;收到的数据长度：\u0026#34;+__bytes.length); 19\tbreak; 20\t} 21\t} 22\tLog.i(TAG, \u0026#34;关于连接\u0026#34;); 23\t__socket.close(); 24} 上面的代码是阻塞式的，没有使用nio是因为我觉得ANE本来就运行在单独的线程中，不用考虑阻塞对UI的影响。\n上面的代码在3台Android 2.3设备上运行良好。但当我在Android 4.0设备上测试的时候，问题出现了。\n在连接Socket服务器的时候，ANE进程会崩溃并报错，虽然它不影响AIR主进程，但用户可以看到Android系统推送的错误提示。\n我测试了3台Android 4.0设备，其中包括2部手机和一部平板电脑，均有同样的问题出现。\n高潮 为什么会如此？\n从stackoverflow上找到的说法是，从Honeycomb（蜂巢，Android 3.0）开始，Andorid就不允许在主进程中进行网络IO的调用。应该使用后台线程或者非阻塞式的API来进行网络通信。如果强插，Android会抛出 NetworkOnMainThreadException 异常。\n于是，我把connectServer方法丢到一个线程中去执行，在3台Android 4.0设备上，测试全部正常。\n1try 2{ 3\tThread __socketThread = new Thread( 4\tnew Runnable() 5\t{ 6\t7\t@Override 8\tpublic void run() 9\t{ 10\ttry 11\t{ 12\tconnectServer(); 13\t} 14\tcatch(Exception $e) 15\t{ 16\tLog.e(TAG, $e.getMessage()); 17\t} 18\t19\t} 20\t} 21\t); 22\t__socketThread.start(); 23} 24catch (Exception e) 25{ 26\tLog.e(TAG, e.getMessage()); 27} 尾声 虽然问题解决了，但疑问依然存在。\n疑问1\nANE线程难道不就是后台线程么？本身ANE是没有可视界面的，与UI也无关，应该符合上面的条件啊。\n疑问2\n我随后使用Android SDK开发写了一个应用，就在主UI进程中使用阻塞式IO访问Socket服务器，且在Android 4.0系统上测试，却没有发现任何问题。所有的网络通信都正常。\n谁能解答这两个问题么？\n参考：\nhttp://stackoverflow.com/questions/10530451/android-4-0-socket-problems http://stackoverflow.com/questions/10313377/tcp-socket-on-android-4-0-3 ","date":"2012-08-03","description":"","lastmod":"2012-08-03T07:29:12Z","slug":"socket_in_ane","tags":["air","android","ane","java","socket"],"title":"在ANE中连接Socket服务器的注意事项","url":"https://blog.zengrong.net/post/socket_in_ane/"},{"categories":["technology"],"content":"Android SDK中找不到Arrays.copyOf？\n原因很简单——选择的 API Level 不对。\njava.util.Arrays.copyOf 方法：\n对于 JAVA 来说，是从 JAVA 1.6 版本开始加入的； 对于 Android 来说，是从 API Level 9 才开始有的。 如果基于Android 2.2(API Level 8)开发，当然就没有copyOf方法。\n解决办法？\n最简单的办法当然是将API Level设置为9。如果一定要基于 API Level 8 开发，可以使用 System.arrayCopy 。\n如果要寻找copyOf的替代方法，则可以使用这段代码：\n1//支持基础类型，结果需要转换类型 2private final Object copyOf(Object $source, int $newLength) 3{ 4\tClass\u0026lt;?\u0026gt; __type = $source.getClass().getComponentType(); 5\tint __oldLength = Array.getLength($source); 6\tObject __target = Array.newInstance(__type, $newLength); 7\tint __preserveLength = Math.min(__oldLength, $newLength); 8\tSystem.arraycopy($source, 0, __target, 0, __preserveLength); 9\treturn __target; 10} 11 12//支持泛型，但不支持基础类型数组，例如要处理byte[]需要使用上面的方法。 13private final \u0026lt;T\u0026gt; T[] copyOf(T[] $source, int $newLength) 14{ 15\tClass\u0026lt;?\u0026gt; __type = $source.getClass().getComponentType(); 16\tint __oldLength = Array.getLength($source); 17\t@SuppressWarnings(\u0026#34;unchecked\u0026#34;) 18\tT[] __target = (T[]) Array.newInstance(__type, $newLength); 19\tint __preserveLength = Math.min(__oldLength, $newLength); 20\tSystem.arraycopy($source, 0, __target, 0, __preserveLength); 21\treturn __target; 22} ","date":"2012-08-02","description":"","lastmod":"2012-08-02T15:49:32Z","slug":"copyof_in_android_api8","tags":["java"],"title":"Android SDK中找不到Arrays.copyOf？","url":"https://blog.zengrong.net/post/copyof_in_android_api8/"},{"categories":["technology"],"content":"在ANE插件中启动AIR开发的Android应用\n在Android原生应用开发中，启动一个应用非常容易：\n1Intent __intent = new Intent(this, YourAppActivity.class); 2startActivity(__intent); 但在ANE插件开发中，要启动AIR开发的Android应用，就不那么容易了。\n因为我并不知道AIR应用的Activicy类名是什么，无法设置Indent。\nANE包含在AIR应用中，我或许可以在ANE中得到AIR应用的Activity类名，但我尝试了下面的方法，不顶用：\n1public class SetAlarmFun implements FREFunction 2{ 3\tpublic static final String TAG = \u0026#34;org.zengrong.ane.funs.SetAlarmFun\u0026#34;; 4\t5\t/** 6\t* 保存上下文 7\t*/ 8\tprivate FREContext _context; 9\t10\t@Override 11\tpublic FREObject call(FREContext $context, FREObject[] $args) 12\t{ 13\t_context = $context; 14\tLog.i(TAG, _context.getActivity().getApplicationInfo().className); 15\t//null 16\t} 17} ANE和AIR应用应该是运行在不同的线程中的，这或许是无法得到类名的原因。\n最后，下面的代码可以在ANE中启动AIR开发的Android应用。当然，在Android原生应用中，也可以用这种方法启动AIR应用。\n我的AIR应用的包名为org.zengrong.ane.test。但是AIR会自动为包名加入air前缀。如果是调试版的AIR应用，还会被自动加上debug后缀。\n因此，这个AIR应用的id实际上变成了air.org.zengrong.ane.test.debug。真够长的……\n1public FREObject call(FREContext $context, FREObject[] $args) 2{ 3\t_context = $context; 4\tIntent __activityIntent = _context.getActivity().getPackageManager().getLaunchIntentForPackage(\u0026#34;air.org.zengrong.ane.test.debug\u0026#34;); 5\tstartActivity(__activityIntent); 6} 这个技巧有什么用？\n例如有个AIR开发的游戏希望在自己没有打开的时候提醒玩家上线，那么它就可以在后台使用ANE悄悄放一个Service，这个Service可以在合适的时候弹出Notification提醒玩家上线。玩家看到消息后，直接单击消息提示，自动打开游戏。\n如果我上面说的应用环境你没有看懂，那么就当我没说好了。\n","date":"2012-08-02","description":"","lastmod":"2012-08-02T15:20:22Z","slug":"launching_air_app_from_ane","tags":["air","android","ane","java"],"title":"在ANE插件中启动AIR开发的Android应用","url":"https://blog.zengrong.net/post/launching_air_app_from_ane/"},{"categories":["technology"],"content":"2012-08-18更新：0.7.0版本基本上是重写，由于测试不充分，导致了非常重大的BUG，所有问题已经在 0.7.1版本 中修复。\n距离上一次更新已经7个多月了。这7个月里AIR发生了很多较大的改变，尤其是原生支持JPEG/JPEG-XR编码，可以让Sprite Sheet Editor保存文件的速度更快。JPEG-XR支持Alpha通道，压缩效果也优于JPEG，以前使用蒙版来降低图像尺寸的方法可以彻底抛弃掉了。\nSprite Sheet Editor会持续更新，今后会加入原生插件以提升性能，还会加入多语言支持。我会在下载页面发布下一步开发计划。\n2012-07-26：v0.7.0版发布\n使用AIR3.3的原生压缩功能对图像进行压缩； 支持使用JPEG-XR作为Sprite Sheet的格式； 让预览图部分浮动； 选择Sprite Sheet中的图片，在整张预览图中指示出该图片的位置； 使用robotlegs重写界面架构。 如果替换安装失败，请先卸载原来的版本，再安装0.7.0版。\n更多的功能介绍以及软件下载，看这里。\n","date":"2012-07-26","description":"","lastmod":"2012-07-26T09:25:33Z","slug":"sprite-sheet-editor-v0-7-0","tags":["air","spritesheet"],"title":"sprite sheet editor v0.7.0发布","url":"https://blog.zengrong.net/post/sprite-sheet-editor-v0-7-0/"},{"categories":["technology"],"content":"本文转自Limbo-Nova：http://www.limbo-nova.com/blog/2012/07/software-development/flash-dev/apache-flex-4-8-0-news-trial/\nApache Flex 工作 Apache Flex 团队目前正在测试一个Apache Flex 4.8.0的发行候选版本。Apache Flex 4.8.0 是Adobe Flex 4.6.0的等同版本。当前的主要工作是保证在新的Apache license下，能够继续使用Flex框架所有的特性；下个阶段会进行bug修复，新功能和新组件的实现工作。如果想自行build可以Check out这个说明文档。 商标问题已经大量清除。 JIRA已经导入了Adobe bug tracker 所有问题。 问题附件涵盖直到2012年1月30日之前的SDK，DMV和Automation bug。 Mustella 测试框架已经捐献并做了一些测试。 对于那些想自己编译编译器，框架等代码的人，基于ANT的编译脚本已经重新设计并且非常易于使用。Check out 上面那个README文档和相关的env.properties模板文件获取入门信息。 社区工作 一群热心的贡献者们正努力让Flex更易于下载和编译，以便其能够在IDE中使用(包括 Flash Builder 和ItelliJ)。他们制作了一个AIR/Flex应用来帮你下载所有必要组件，并放置在相应的目录。点这里查看。 FlexUnit 正在捐献给Apache 的过程中。 关于Apache Flex 历史和发展，Justin Mclean 做了一个非常精彩的演讲。 360|Flex (现在叫 360|Stack)宣布360|MIN，活动将于10月22-23日在拉斯维加斯举办。Adobe 2012 MAX大会将在同一周举办。360|MIN是一个barcamp式的聚会活动，届时将会有许多Apache Flex的贡献者在现场。 欲悉更多，请订阅 flex-dev 邮件列表！ Apache Flex 4.8.0 安装使用 声明里也一直提到过，这个版本的Flex不会有太多变化，可以认为和Adobe Flex 4.6.0是等同的，主要的区别在于Apache Flex不再支持缓存RSL(Runtime Shared Libraries)。\n可以从下面的地址取得 Apache Flex 4.8.0 最新的RC版本：\nhttp://people.apache.org/~cframpton/ApacheFlexRC/current/\n导入Flash Builder，有三种方式：\n脚本导入\n修改目录 ide/flashbuilder/ 中的 bat/sh 脚本中的相关路径，运行时将自动下载设置sdk。 工具导入\n安装这个工具：https://github.com/bigosmallm/MakeApacheFlexForFlashBuilder 手动导入 下载包，解压至flash builder的sdk目录； Copy playerglobal.swc 到 frameworks\\libs\\player\\11.1 中； 下载 http://airdownload.adobe.com/air/win/download/3.1/AdobeAIRSDK.zip 解压至目录4.8.0； 将 /ide/flashbuilder/config 内的3个xml配置文件，复制覆盖至 /frameworks 中； 配置完成之后，在Flash Builder 首选项里设置SDK即可。开发流程和Flex 4.6一样。 ","date":"2012-07-24","description":"","lastmod":"2012-07-24T09:17:34Z","slug":"apache-flex-4-8-0","tags":["flashbuilder","flex"],"title":"【转】Apache Flex 4.8.0 近况及试用","url":"https://blog.zengrong.net/post/apache-flex-4-8-0/"},{"categories":["technology"],"content":"BUG？AIR打包的iOS程序在整数比较上的问题\n以前找到过一个 FlashPlayer在执行NetStream.play的时候崩溃的BUG ，没想到今天又让我碰到一个AIR的BUG。\n和上个BUG不同，这个BUG再现起来相当容易，但我还是找了1天才找到再现的方式。\n不说了，直接上代码：\n1package 2{ 3import flash.display.Sprite; 4import flash.filesystem.File; 5import flash.text.TextField; 6 7/** 8 * 测试在iOS分发包中的unit与int不能比较的问题 9 */ 10public class IOSUintTest extends Sprite 11{ 12\tpublic function IOSUintTest() 13\t{ 14\tsuper(); 15\tinit(); 16\tshowInfo(-1 \u0026lt;= ZERO_INT); 17\tshowInfo(-1 \u0026lt;= ZERO_UINT); 18\tshowInfo(_num \u0026lt;= ZERO_INT); 19\tshowInfo(_num \u0026lt;= ZERO_UINT); 20\t} 21\t22\tprivate var _tf:TextField; 23\t24\tprivate var _num:int = -1; 25\t26\tpublic static const ZERO_INT:int = 0; 27\t28\tpublic static const ZERO_UINT:uint = 0; 29\t30\tprivate function init():void 31\t{ 32\t_tf = new TextField(); 33\t_tf.width = 400; 34\t_tf.height = 400; 35\tthis.addChild(_tf); 36\t} 37\t38\tprivate function showInfo($info:*):void 39\t{ 40\t_tf.appendText(String($info) + File.lineEnding); 41\t} 42} 43} 地球人都知道，showInfo中的4个比较表达式的值应该都为true。恩，是的，在adb提供的调试版ipa中，它们的值都是true。\n#但是，在用于发布的ipa中，它们的值并非都是true！\n我这里所说的“用于发布的ipa”，如果用Adobe的话来说，就是“限制分发的临时包”和“部署到Apple App Store的最终发行包“。\n将这种包安装到iOS设备上，得到的4个值分别是 true,false,true,false\n问题出在int与uint的比较上。因为AIR打包成ipa，实际上是直接将AIR程序打包成2进制代码，而不是采取虚拟机的形式（APK是采取的这种形式）。因此，使用AIR制作的ipa，理论上与使用Objective-C写的ipa没有什么不同。这也是为什么AIR写的ipa能堂而皇之的登上App Store的原因。否则，以苹果那个独裁政策，不卡死Adobe才怪！\n既然是Objective-C代码，那么Objective-C的类型转换规则也同样适用与这个比较表达式。在Objective-C中，将int与uint互相比较的时候，会先将int转换成uint，得到4294967294，(4294967294 \u0026lt;= 0) 的值应为false。\nObjective-C是基于C语言的。在C语言中，这种情况叫做整型提升。\n以下摘自《The C Programming Language》\nA character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer maybe used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion.\n在ActionScript中，int与uint比较的时候，是不会进行整型提升的。int和uint都是基于Number，在AVM中，我不知道它们是否进行了严格的划分。\n从这个观点上说，这并不是BUG，而是不同语言的特性所致。但是，Adobe既然在大力推广AIR开发iOS应用，就要考虑到不同语言之间的差别，避免出现这种容易被忽视的错误。\n这个BUG说起来简单，但是在一个已经存在的大型项目中发现这样的小错误，还是非常困难的。\n困难的关键点在于，允许调试的ipa文件(target ipa-debug)中，并不会出现整型提升的问题。这就导致调试的时候正常的程序，在发布的时候不正常。何况发布的文件还无法调试！这绝对是Adobe的工作失误。\n这个问题，我不准备报告给Adobe了。大家在开发中养成更严谨的习惯吧。\n","date":"2012-07-21","description":"","lastmod":"2012-07-21T08:02:21Z","slug":"int_compare_in_air_ios","tags":["adobebug","air","c","ios","objective-c"],"title":"BUG？AIR打包的iOS程序在整数比较上的问题","url":"https://blog.zengrong.net/post/int_compare_in_air_ios/"},{"categories":["technology"],"content":"2012-08-22更新：正式版发布\n看来Adobe是真的急了，AIR3.3发布没多久，3.4就来了。\nFlash Player 11.4增加了以下几个功能：\n并发运算，这可能是近几年来除了3D之外最令开发者激动的功能了。 Stage 3D强制模式，让Stage 3D加速能运行在更多的老硬件上，比如Intel的GMA显卡（这个垃圾……） StageVideo.attachCamera，让StageVideo的加速功能可以用于摄像头。 Camera.copyToByteArray/Camera.copyToVector，虽然目前可以用draw来讲摄像头画面转换成位图，但貌似这个精度更高。 支持ByteArray的LZMA压缩，7z威武…… AIR3.4的更新看来全部是针对iOS设备的。Adobe是卯足了劲要咬住iOS这块肥肉不松口啊……\n支持iOS 5.1 SDK 支持iOS上的C++和Objective-C原生扩展 iOS消息推送 直接在iOS设备上部署AIR程序（不需要iTunes，这个非常的牛B！） Flash Builder 4.7将在8月最后一周在labs上发布beta版，将支持iOS debug调试、iOS模拟器、iOS直接部署。或许会支持并发调试？\n喜欢看英文的可以看这里：\nhttp://helpx.adobe.com/flash-player/release-note/fp_114_air_34_release_notes.html http://blogs.adobe.com/flashplayer/2012/08/flash-player-11-4-and-air-3-4.html flash player debugger air sdk 更多的源码和Demo：\nhttp://www.bytearray.org/?p=4516\n详细的功能介绍可以看release note，包括如何绕过iTunes在iOS上部署的方式。\n下面是release note的pdf版本下载（beta2版，我没有找到正式版的pdf）：\n1 文件 ","date":"2012-07-17","description":"","lastmod":"2012-07-17T14:32:25Z","slug":"air3-4_flashplayer11-4","tags":["adobe","air","flashplayer","ios"],"title":"FlashPlayer11.4/AIR3.4新功能","url":"https://blog.zengrong.net/post/air3-4_flashplayer11-4/"},{"categories":["technology"],"content":"NetworkInfo for iOS\n我在Android上使用 flash.net.NetworkInfo 实现了socket连接在网络状态改变时的自动重连机制，但却发现在iOS设备上不支持 flash.net.NetworkInfo 。\nAdobe的 API文档 中说，NetworkInfo需要 AIR Profile Support 支持，我在该文档中找到 mobileDevice Profile ，发现 NetworkInfo 一栏的值为 Check ，也就是说，必须由开发者使用 NetworkInfo.isSupported 来检测设备是否支持 NetworkInfo。\n悲催的是，iOS设备的 NetworkInfo.isSupported 是 false ！这就意味着，不能使用 flash.net.NetworkInfo 来获取iOS设备的网络状态。\nAdobe AIR Developer Center 中提供了一个iOS的原生插件 NetworkInfo native extension sample 来实现获取iOS设备中的网络状态。该文章中直接提供了ANE包的下载，可以直接在Windows环境下使用。\n该插件实现了三个类：\n1com.adobe.nativeExtensions.Networkinfo.InterfaceAddress; 2com.adobe.nativeExtensions.Networkinfo.NetworkInfo; 3com.adobe.nativeExtensions.Networkinfo.NetworkInterface; 名称与 flash.net 下的类相同，但包不同，因此使用的时候要注意包的区别。\n这个插件的功能并不完整。因为 flash.net.NetworkInfo 支持网络状态变更通知 (flash.events.Event.NETWORK_CHANGE) ，但该插件不支持。\n如果希望在一个项目中同时兼容 Android 和 iOS 的网络状态，这篇文章提供了一些思路： Getting NetworkInfo from both Android and iOS\n","date":"2012-07-13","description":"","lastmod":"2012-07-13T01:23:36Z","slug":"networkinfo_fo_ios","tags":["air","android","ane","ios"],"title":"NetworkInfo for iOS","url":"https://blog.zengrong.net/post/networkinfo_fo_ios/"},{"categories":["technology"],"content":"2012-07-13更新：加入iOS设备测试。\n怎样在使用AIR编写的Android程序中控制设备的方向呢？\n例如有一款游戏，希望无论在平板电脑上，还是在手机中，都以竖屏（垂直方向）的方式启动，而且无论如何都不可能横屏。我们可以这样设置：\n在应用程序描述符中，将autoOrients设置为true，将aspectRatio设置为portrait。\n在应用程序描述符中，将autoOrients设置为false，将aspectRatio设置为portrait。\n在Android平台上，将autoOrients设置为true或者false，是同样的效果。而在iOS平台上，必须将autoOrients设置为false才能得到上面的效果。\n本文测试平台：\nAndroid手机：Moto Atrix 2，Android 2.3.6 Android平板：Acer Iconia Tab A500，Androd 4.0.3 iOS手机：iPhone 4 iOS平板：iPad 2 AIR版本：3.3 下表中描述了autoOrients和aspectRatio这两个值的所有组合状态，以及手机的“锁定旋转”功能对它们的影响（这活儿相当苦逼……）。\n平台 autoOrients aspectRatio 表现 锁定功能 Android true portrait 启动后转到垂直方向 无效 Android true landscape 启动后转到水平方向 无效 Android true any 启动后转到设备的当前方向 有效 Android true 不设置 启动后转到设备的当前方向 有效 Android false portrait 启动后转到垂直方向 无效 Android false landscape 启动后转到水平方向 无效 Android false any 启动后转到设备的当前方向 不确定 Android false 不设置 启动后转到设备的默认方向 无效 iOS false portrait 启动后转到垂直方向 无效 iOS true portrait 启动后转到设备的当前方向 有效 iOS false landscape 启动后转到水平方向 无效 iOS true landscape 启动后转到设备的当前方向 有效 名词解释：\n垂直方向：设备的长边垂直于地面的方向； 水平方向：设备的短边垂直于地面的方向； 当前方向：设备当前的方向，取决于你怎么拿它； 默认方向：对于平板电脑来说，水平方向是默认方向，手机则相反； 锁定功能无效：无论是否开启锁定，都不会影响程序的方向； 锁定功能有效：参见上一句； 锁定功能不确定：这个比较坑爹，在我的平板上时有时无；但对于作出这种搭配（ autoOrients=false,aspectRatio=any）的程序员，我只能说两个字：你相当手贱。 有点标题党了，说是控制设备方向，其实只是控制设备的初始方向而已。\n如果在运行时要控制设备的方向，在stage的方法中找找吧……\n参考：http://blogs.adobe.com/airodynamics/2012/05/22/stage-aspectratio-enhancements/\n","date":"2012-06-10","description":"","lastmod":"2012-06-10T14:27:38Z","slug":"rotation-control-in-air","tags":["air","android","ios"],"title":"在AIR编写的Android/iOS程序中控制设备方向","url":"https://blog.zengrong.net/post/rotation-control-in-air/"},{"categories":["technology"],"content":"读写FREByteArray\n由于AIR的File API在Android上设备上的限制，我在 ANEToolkit 的 Storage 工具中，提供了 readFile 和 writeFile 方法。这两个方法提供将 ByteArray 作为文件写入 Android 设备，或者从 Android 设备中读取一个文件，并作为 ByteArray 返回。\n本来挺简单的一个功能，可调试来调试去总是报错。插件的调试并不那么容易，必须不断的打包插件、打包APK，测试APK。而且这样的功能只能在手机上调试才行。\n弄了几个小时，把注意事项总结如下：\n在将JAVA的byte[]数组写入FREByteArray对象之前，需要先设定FREByteArray的length属性，否则写入不会成功； 从FREByteArray对象中读取AS的ByteArray，不能使用ByteBuffer.array()，应该使用ByteBuffer.get(byte[])。 以下代码取自ReadFile.java\n1FileInputStream __inputFile = new FileInputStream(__file); 2byte[] __byte = new byte[(int) __file.length()]; 3__inputFile.read(__byte); 4__inputFile.close(); 5FREByteArray __ba = FREByteArray.newByteArray(); 6//必须先设置length，否则写入数据不会成功，这点非常重要！ 7__ba.setProperty(\u0026#34;length\u0026#34;, FREObject.newObject(__file.length())); 8//设置属性必须在捕获锁定之前 9__ba.acquire(); 10ByteBuffer __bb = __ba.getBytes(); 11__bb.put(__byte); 12__ba.release(); 13return __ba; 以下代码取自WriteFile.java\n1/** 2 * 将FREByteArray转换成byte[] 3 * @param $ba 4 * @return 5 * @throws FREWrongThreadException 6 * @throws FREInvalidObjectException 7 * @throws IllegalStateException 8 */ 9public byte[] getByteArray(FREByteArray $ba) throws IllegalStateException, FREInvalidObjectException, FREWrongThreadException 10{ 11\t//锁定参数 12\t$ba.acquire(); 13\tByteBuffer __bb = $ba.getBytes(); 14\t//建立一个数组保存传递来的参数 15\tbyte[] __byte = new byte[(int) $ba.getLength()]; 16\t__bb.get(__byte); 17\t$ba.release(); 18\t//获取字节数组 19\treturn __byte; 20} ","date":"2012-06-07","description":"","lastmod":"2012-06-07T13:14:00Z","slug":"frebytearray_rw","tags":["air","android","ane","java"],"title":"读写FREByteArray","url":"https://blog.zengrong.net/post/frebytearray_rw/"},{"categories":[],"content":"ANE Toolkit\n据说Adobe正在开发ANE插件包，但我可能永远也等不到那一天了。 Adobe发布了一个闭源的水果插件包(in gaming SDK)，写得很烂，但不知道为什么要闭源？没有Android。\n写了十几年AS，被Adobe折腾得挺累，想换点口味。不过可能依然会在社区出现骗骗小白，或者用AIR的快速跨平台开发抢点钱什么的……\n我把自己用的插件包放出来，算是为AS社区做点贡献。\n目前只有Android版本，采用JAVA API，可能以后会有iOS的版本也说不定应该不会有iOS版本，但我仍会撺掇别人开源，比如这个：\nPLATFORM-ANES 。\n这东西没什么技术含量，就是体力活调试而已。\n如果希望增加什么功能，可以联系我。\n由于研究方向变化，我个人应该不会为其增加新功能了。但仍有人在继续使用它，若有修改，我会更新。\n如果有人愿意接收项目，联系我。\n有能力的人，希望能多付出一点，少索取一些。\n官方更新： 2013-01-25 增加电源管理功能 2013-05-21 增加重启功能 官方介绍： ANEToolkit是一个非官方的稀烂的插件包，主要是zrong为苦逼的只会AS的或者不是只会AS但讨厌JAVA和Object-C和C和C++的又希望在Android或者水果设备上开发游戏或者应用的程序猿所做的一个很是不到位的努力。\n提供以下功能： 安装APK文件 调用各种设置面板 振动 访问SD卡状态 读取AIR不能读取的文件 向AIR不能读取的路径写入文件 删除AIR不能删除的文件 获取硬件信息，包括CPU名称、速度、内存大小、存储器大小 获取手机信息，包括品牌、名称等等 获取网络名称，包括手机号码（部分手机可用/介是不道德DI……）、手机网络（GPRS/GSM/WCDMA...） 获取网络连通状态 电源管理，禁止休眠，保持屏幕常亮 重启自身 下载资源 doc（非实时更新，最新文档请直接看源码）：http://zrong.github.io/anetoolkit/doc/ ANE：https://github.com/zrong/anetoolkit/blob/master/bin/ANEToolkit.ane sample：https://github.com/zrong/anetoolkit/tree/master/sample source：https://github.com/zrong/anetoolkit ","date":"2012-06-07","description":"","lastmod":"2012-06-07T11:19:45Z","slug":"anetoolkit","tags":[],"title":"ANE Toolkit","url":"https://blog.zengrong.net/anetoolkit/"},{"categories":["technology"],"content":"**2012-10-04更新：**加入Vimwiki2.0新特性\n做个记录，近期准备尝试Vimwiki（部分文章可能需要翻墙）\nVimwiki Vimwiki Vimwiki googlecode Vimwiki github Vim Script 文档中文计划 Vimwiki介绍文章 用 vimwiki搭建你自己的维基世界 Pkm工具：Vimwiki VimWiki使用入门 用 Vimwiki 写博客 Vim插件-VimWiki 同步 vimwiki到线上的各种方法 VimWiki安装配置 Vimwiki 2.0 新特性 可以嵌入Vimwiki的评论系统 disqus 评论啦 ","date":"2012-05-10","description":"","lastmod":"2012-05-10T00:37:15Z","slug":"about-vimwiki","tags":["vim"],"title":"Vimwiki的相关资料搜集","url":"https://blog.zengrong.net/post/about-vimwiki/"},{"categories":["technology"],"content":"完全使用SFTP替代FTP：SFTP+OpenSSH+ChrootDirectory设置详解\n2012-09-28更新:加入web服务器需求的内容。\n由于采用明文传输用户名和密码，FTP协议是不安全的。在同一机房中只要有一台服务器被攻击者控制，它就可能获取到其它服务器上的FTP密码，从而控制其它的服务器。\n当然，很多优秀的FTP服务器都已经支持加密。但如果服务器上已经开了SSH服务，我们完全可以使用SFTP来传输数据，何必要多开一个进程和端口呢？\n下面，我就从账户设置、SSH设置、权限设置这三个方面来讲讲如何使用SFTP完全替代FTP。本教程基于CentOS5.4。\n范例 本文要实现以下功能：\nSFTP要管理3个目录：\nhomepage blog pay 权限配置如下：\n账户www，可以管理所有的3个目录； 账户blog，只能管理blog目录； 账户pay，只能管理pay目录。 web服务器需求：\n账户blog管理的目录是一个博客网站，使用apache服务器。apache服务器的启动账户是apache账户，组为apache组。 账户blog属于apache组，它上传的文件能够被apache服务器删除。同样的，它也能删除在博客中上传的文件（就是属于apache账户的文件）。 账户设置 SFTP的账户直接使用Linux操作系统账户，我们可以用useradd命令来创建账户。\n首先建立3个要管理的目录：\n1mkdir /home/sftp/homepage 2mkdir /home/sftp/blog 3mkdir /home/sftp/pay 创建sftp组和www、blog、pay账号，这3个账号都属于sftp组：\n1groupadd sftp 2useradd -M -d /home/sftp -G sftp www 3useradd -M -d /home/sftp/blog -G sftp blog 4useradd -M -d /home/sftp/pay -G sftp pay 5 6# 将blog账户也加到apache组 7useradd -M -d /home/sftp/blog -G apache blog 8 9#设置3个账户的密码密码 10passwd www 11passwd blog 12passwd pay 至此账户设置完毕。\nSSH设置 首先要升级OpenSSH的版本。只有4.8p1及以上版本才支持Chroot。\nCentOS 5.4的源中的最新版本是4.3，因此需要升级OpenSSH。\n指定新的源：\n1vim /etc/yum.repos.d/test.repo 2#输入如下内容 3[centalt] 4name=CentALT Packages for Enterprise Linux 5 - $basearch 5baseurl=http://centos.alt.ru/repository/centos/5/$basearch/ 6enabled=0 7gpgcheck=0 8# wq保存 执行升级：\n1yum --enablerepo=centalt update -y openssh* openssl* 2# 重启服务 3service sshd restart 4# 重看版本 5ssh -V 6# OpenSSH_5.8p1, OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008 升级成功后，设置sshd_config。通过Chroot限制用户的根目录。\n1vim /etc/ssh/sshd_config 2#注释原来的Subsystem设置 3Subsystem\tsftp\t/usr/libexec/openssh/sftp-server 4#启用internal-sftp 5Subsystem sftp internal-sftp 6#限制www用户的根目录 7Match User www 8\tChrootDirectory /home/sftp 9\tForceCommand\tinternal-sftp 10#限制blog和pay用户的根目录 11Match Group sftp 12\tChrootDirectory %h 13\tForceCommand\tinternal-sftp 完成这一步之后，尝试登录SFTP：\n1sftp www@abc.com 2#或者 3ssh www@abc.com 4#如果出现下面的错误信息，则可能是目录权限设置错误，继续看下一步 5#Connection to abc.com closed by remote host. 6#Connection closed 权限设置 要实现Chroot功能，目录权限的设置非常重要。否则无法登录，给出的错误提示也让人摸不着头脑，无从查起。我在这上面浪费了很多时间。\n目录权限设置上要遵循2点：\nChrootDirectory设置的目录权限及其所有的上级文件夹权限，属主和属组必须是root； ChrootDirectory设置的目录权限及其所有的上级文件夹权限，只有属主能拥有写权限，也就是说权限最大设置只能是755。 如果不能遵循以上2点，即使是该目录仅属于某个用户，也可能会影响到所有的SFTP用户。\n1chown root.root /home/sftp /home/sftp/homepage /home/sftp/blog /home/sftp/pay 2chmod 755 /home/sftp /home/sftp/homepage /home/sftp/blog /home/sftp/pay 由于上面设置了目录的权限是755，因此所有非root用户都无法在目录中写入文件。我们需要在ChrootDirectory指定的目录下建立子目录，重新设置属主和权限。以homepage目录为例：\n1mkdir /home/sftp/homepage/web 2chown www.sftp /home/sftp/homepage/web 3chmod 775 /home/sftp/homepage/web 要实现web服务器与blog账户互删文件的权限需求，需要设置umask，让默认创建的文件和目录权限为775即可。将下面的内容写入.bashrc中：\n1umask 0002 至此，我们已经实现了所有需要的功能。\n参考资料 http://www.mike.org.cn/articles/centos-sftp-chroot/ http://www.mike.org.cn/articles/centos-install-openssh/ http://www.ctohome.com/FuWuQi/29/554.html http://rainbird.blog.51cto.com/211214/275162/ http://www.debian-administration.org/articles/590 ","date":"2012-05-07","description":"","lastmod":"2012-05-07T10:42:46Z","slug":"openssh_chroot","tags":["linux","ssh","server","ftp"],"title":"完全使用 SFTP 替代 FTP ：SFTP+OpenSSH+ChrootDirectory 设置详解","url":"https://blog.zengrong.net/post/openssh_chroot/"},{"categories":["technology"],"content":"本文转自：TICORE'S BLOG\n網路上有很多可以將 FLV 轉為 SWF 的工具，譬如 FLV to SWF Using FFMPEG command line 不過測試發現 FPS 變得超高以下分享一個另類的做法，只要一行 Dos Command 就可以把 FLV 轉成 SWF，由於不需要重新取樣，速度非常快\nFlash Player 10.1 之後新增 NetStream.appendBytesAction() 方法可以直接從 AS3 Binary 資料播放 Streaming 只要想辦法把 FLV 資料嵌入到 SWF，就能夠用 AS3 來播放最簡單的嵌入方式就事先寫好一個 Player SWF，然後用 Dos Command 將 FLV 資料接在後面。\nFLVBytePlayer 程式碼如下：\n1package { 2 import flash.display.Loader; 3 import flash.display.Sprite; 4 import flash.display.StageAlign; 5 import flash.display.StageScaleMode; 6 import flash.events.Event; 7 import flash.events.NetStatusEvent; 8 import flash.media.Video; 9 import flash.net.NetConnection; 10 import flash.net.NetStream; 11 import flash.net.NetStreamAppendBytesAction; 12 import flash.net.URLLoader; 13 import flash.net.URLLoaderDataFormat; 14 import flash.net.URLRequest; 15 import flash.text.TextField; 16 import flash.ui.ContextMenu; 17 import flash.utils.ByteArray; 18 19 [SWF(width=\u0026#34;300\u0026#34;, height=\u0026#34;300\u0026#34;)] 20 public class FLVBytePlayer extends Sprite { 21 public var nc:NetConnection = new NetConnection(); 22 public var ns:NetStream; 23 public var video:Video = new Video(); 24 public var videoWidth:Number = 1; 25 public var videoHeight:Number = 1; 26 public var videoByte:ByteArray; 27 public var urlLdr:URLLoader = new URLLoader(); 28 public function FLVBytePlayer() { 29 stage.scaleMode = StageScaleMode.NO_SCALE; 30 stage.align = StageAlign.TOP_LEFT; 31 stage.addEventListener(Event.RESIZE, onStageResizeHandler); 32 contextMenu = new ContextMenu(); 33 contextMenu.hideBuiltInItems(); 34 nc.connect(null); 35 ns = new NetStream(nc); 36 ns.client = { 37\tonMetaData: function(info:Object):void{ 38\ttrace(\u0026#34;onMetaData:\u0026#34;); 39\tfor (var i:String in info) { 40\ttrace(\u0026#34;\\t\u0026#34;, i, info[i]); 41\t} 42\tvideoWidth = info.width; 43\tvideoHeight = info.height; 44\tonStageResizeHandler(); 45\t} 46 }; 47 ns.addEventListener(NetStatusEvent.NET_STATUS, onNsNetStatusHandler); 48 video.smoothing = true; 49 video.attachNetStream(ns); 50 addChild(video); 51 var ldr:Loader = new Loader(); 52 var req:URLRequest = new URLRequest(ldr.contentLoaderInfo.loaderURL); 53 trace(ldr.contentLoaderInfo.loaderURL); 54 urlLdr.dataFormat = URLLoaderDataFormat.BINARY; 55 urlLdr.addEventListener(Event.COMPLETE, onLoadCompleteHandler); 56 urlLdr.load(req); 57 } 58 public function onStageResizeHandler(e:Event = null):void{ 59 var stageRatio:Number = stage.stageWidth / stage.stageHeight; 60 var videoRatio:Number = videoWidth / videoHeight; 61 if (stageRatio \u0026gt; videoRatio) { 62\tvideo.height = stage.stageHeight; 63\tvideo.width = stage.stageHeight * videoRatio; 64\tvideo.y = 0; 65\tvideo.x = (stage.stageWidth - video.width) / 2; 66 } else { 67\tvideo.width = stage.stageWidth; 68\tvideo.height = stage.stageWidth / videoRatio; 69\tvideo.x = 0; 70\tvideo.y = (stage.stageHeight - video.height) / 2; 71 } 72 } 73 public function onNsNetStatusHandler(e:NetStatusEvent):void{ 74 trace(e.info.code); 75 switch (e.info.code) { 76\tcase \u0026#34;NetStream.Buffer.Empty\u0026#34;: 77\tbreak; 78 } 79 } 80 public function onLoadCompleteHandler(e:Event):void{ 81 var data:ByteArray = urlLdr.data; 82 for (var i:int = data.length - 1 ; i \u0026gt; 0 ; --i) { 83\tif ( 84\tdata[i + 0] == 70 \u0026amp;\u0026amp; /* \u0026#34;F\u0026#34;.charCodeAt(0) */ 85\t(data[i + 1] == 76 || data[i + 1] == 52) \u0026amp;\u0026amp; 86\t/* \u0026#34;L\u0026#34;.charCodeAt(0) *//* \u0026#34;4\u0026#34;.charCodeAt(0) */ 87\tdata[i + 2] == 86 /* \u0026#34;V\u0026#34;.charCodeAt(0) */ 88\t) { 89\tvideoByte = new ByteArray(); 90\tdata.position = i; 91\tdata.readBytes(videoByte, 0, 0); 92\ttrace(data.length, i, videoByte.length); 93\tns.play(null); 94\tns.appendBytesAction(NetStreamAppendBytesAction.RESET_SEEK); 95\tns.appendBytes(videoByte); 96\tbreak; 97\t} 98 } 99 } 100 } 101} 以上 Player 只要編譯一次產生 SWF 後，利用以下的 Dos Command 接上 FLV就可以直接撥放了\ncopy /b FLVBytePlayer.swf + Video.flv FLVBytePlayer_FLV.swf 需要注意的是 NetStream.appendBytesAction 不適用於 H264 編碼請自行斟酌使用。\n","date":"2012-05-03","description":"","lastmod":"2014-10-26T02:04:50Z","slug":"convert-flv-to-swf","tags":["flash","h264","video"],"title":"【转】FLV 轉 SWF 另類作法","url":"https://blog.zengrong.net/post/convert-flv-to-swf/"},{"categories":["technology"],"content":"在cygwin中调用JAVA程序\n只要安装了JDK或者JRE，就可以在cygwin中直接使用JAVA命令。但最大的问题，是在于windows下的JAVA需要Windows格式的路径，而cygwin默认提供给JAVA的路径，JAVA是无法识别的。\n例如有一个JAVA程序encrypt.jar，正确的方式应该这样调用：\n1java -Dsource=源文件路径 -Dtarget=目标文件路径 -jar encrypt.jar 但如果直接在cygwin下这样调用，就会报错：\n1java -Dsource=~/source.txt -Dtarget=~/target.txt -jar encrypt.jar 2#Exception in thread \u0026#34;main\u0026#34; java.io.FileNotFoundException: \\home\\zrong\\source.txt (系统找不到指定的路径。) 所以，我们需要借助cygpath命令，将cygwin格式的路径转换成Windows格式。\n1$ cygpath -w ~/source.txt 2#D:\\cygwin\\home\\zrong\\source.txt 这样调用就没问题了\n1java -Dsource=`cygpath -w ~/source.txt` -Dtarget=`cygpath -w ~/target.txt` -jar encrypt.jar cygpath的参数不少，可以使用 cygpath --help 查看\n","date":"2012-04-28","description":"","lastmod":"2012-04-28T01:11:19Z","slug":"invoke_java_in_cygwin","tags":["cygwin","java","linux"],"title":"在cygwin中调用JAVA程序","url":"https://blog.zengrong.net/post/invoke_java_in_cygwin/"},{"categories":["news"],"content":"【转】Linux的五个查找命令\n本文转自：阮一峰的网络日志\n最近，我在学习Linux，下面是一些笔记。\n使用电脑的时候，经常需要查找文件。\n在Linux中，有很多方法可以做到这一点。国外网站LinuxHaxor总结了五条命令，你可以看看自己知道几条。大多数程序员，可能经常使用其中的2到3条，对这5条命令都很熟悉的人应该是不多的。\n1. find find是最常见和最强大的查找命令，你可以用它找到任何你想找的文件。\nfind的使用格式如下：\n$ find \u0026lt;指定目录\u0026gt; \u0026lt;指定条件\u0026gt; \u0026lt;指定动作\u0026gt;\n\u0026lt;指定目录\u0026gt;： 所要搜索的目录及其所有子目录。默认为当前目录。 \u0026lt;指定条件\u0026gt;： 所要搜索的文件的特征。 \u0026lt;指定动作\u0026gt;： 对搜索结果进行特定的处理。 如果什么参数也不加，find默认搜索当前目录及其子目录，并且不过滤任何结果（也就是返回所有文件），将它们全都显示在屏幕上。\nfind的使用实例：\n搜索当前目录（含子目录，以下同）中，所有文件名以my开头的文件。\n$ find . -name 'my*' 搜索当前目录中，所有文件名以my开头的文件，并显示它们的详细信息。\n$ find . -name 'my*' -ls 搜索当前目录中，所有过去10分钟中更新过的普通文件。如果不加-type f参数，则搜索普通文件+特殊文件+目录。\n$ find . -type f -mmin -10 2. locate locate命令其实是“find -name”的另一种写法，但是要比后者快得多，原因在于它不搜索具体目录，而是搜索一个数据库（/var/lib/locatedb），这个数据库中含有本地所有文件信息。Linux系统自动创建这个数据库，并且每天自动更新一次，所以使用locate命令查不到最新变动过的文件。为了避免这种情况，可以在使用locate之前，先使用updatedb命令，手动更新数据库。\nlocate命令的使用实例：\n搜索etc目录下所有以sh开头的文件。\n$ locate /etc/sh 搜索用户主目录下，所有以m开头的文件。\n$ locate ~/m 搜索用户主目录下，所有以m开头的文件，并且忽略大小写。\n$ locate -i ~/m 3. whereis whereis命令只能用于程序名的搜索，而且只搜索二进制文件（参数-b）、man说明文件（参数-m）和源代码文件（参数-s）。如果省略参数，则返回所有信息。\nwhereis命令的使用实例：\n$ whereis grep 4. which which命令的作用是，在PATH变量指定的路径中，搜索某个系统命令的位置，并且返回第一个搜索结果。也就是说，使用which命令，就可以看到某个系统命令是否存在，以及执行的到底是哪一个位置的命令。\nwhich命令的使用实例：\n$ which grep 5. type type命令其实不能算查找命令，它是用来区分某个命令到底是由shell自带的，还是由shell外部的独立二进制文件提供的。如果一个命令是外部命令，那么使用-p参数，会显示该命令的路径，相当于which命令。\ntype命令的使用实例：\n系统会提示，cd是shell的自带命令（build-in）。\n$ type cd 系统会提示，grep是一个外部命令，并显示该命令的路径。\n$ type grep 加上-p参数后，就相当于which命令。\n$ type -p grep ","date":"2012-04-28","description":"","lastmod":"2012-04-28T00:40:14Z","slug":"5_ways_to_search_for_files_using_the_terminal","tags":["linux"],"title":"【转】Linux的五个查找命令","url":"https://blog.zengrong.net/post/5_ways_to_search_for_files_using_the_terminal/"},{"categories":["technology"],"content":"目前我知道的比较好用的，有3种方法：\n1.Vrapper 建议使用 http://vrapper.sourceforge.net/update-site/unstable 来安装，虽然是不稳定版，但用起来感觉比稳定版还要稳定。 :wink:\nvrapper的Bug不少，和中文输入法也会有一些冲突，但总之还是为我带了来效率的提升。\n最困扰我的问题在于，Esc键经常会不起作用，由于Eclipse中经常会有多层次的代码提示，而按一次Esc键之后，只会消除一层代码提示。如果此时立即开始Vim编辑，则会将代码提示的内容自动加入到源码中，最后不得不一条条删除它们，这点很让然很烦躁。\n注意：如果希望使用Ctrl+D快捷键实现翻页，就必须将Eclipse中的Ctrl+D屏蔽掉。因为Ctrl+D在Eclipse中是删除一行，如果你经常在Vim中Ctrl+D的话，你就杯具了……\n2.Eclim 这个就太强大了，它并不是在Eclipse中模仿Vim的习惯，而是直接把Vim嵌入到Eclipse中来。同样的，在Vim中也可以使用Eclipse中的代码提示等功能。\n但是，如果你在Vim或者Eclipse中定义了相同的热键，这些热键会冲突，这又是一件纠结的事情，Eclipse中提高效率的就是热键，如果连热键都不能用了，那效率也太低了。\n反过来说，在Vim中提供Eclipse的功能，我觉得没有什么必要。Vim有自已的一套方法，配置的好的话，甚至比Eclipse更好用。话说回来，如果只是开发JAVA的话，完全没有必要用Vim，Eclipse已经完美了。\n3.viplugin 我从 paddy.w 看到这个插件。这是个收费插件，目前要15欧。我正在试用中，感觉比Vrapper要稳定。\n","date":"2012-04-27","description":"","lastmod":"2012-04-27T01:06:25Z","slug":"use_vim_in_eclipse","tags":["eclipse","vim"],"title":"在Eclipse中使用Vim","url":"https://blog.zengrong.net/post/use_vim_in_eclipse/"},{"categories":["technology"],"content":"AIR对Mobile设备位图取色的问题\n在网页游戏开发中，我经常使用bitmapData.getPixel32来获得单击的部位的透明度值，以此来确定是否交互。\n在PC平台中，这个用法是很靠谱的：\n1public function checkOpaque($x:Number,$y:Number):Boolean 2{ 3\t//如果没有图像，当然是透明的 4\tif(!this.bitmapData) return false; 5\tvar __argb:uint = this.bitmapData.getPixel32($x,$y); 6\t//否则就判断透明度 7\treturn (__argb\u0026gt;\u0026gt;24\u0026amp;0xFF) \u0026gt; 0; 8} 但是，到了Mobile平台上，这个方法就有问题，使用getPixel32获得透明部分的像素值，得到的是16777216！\n这个值其实是2的24次方。也就是说，在Mobile平台上，getPixel32只能支持到24bit色彩！\n那么，是不是我的设置不正确呢？在AIR项目配置文件中，可以设置colorDepth，默认值为16bit，将其设置为32bit，还是没有作用。\n所以只能把判断函数改成这样了：\n1public function checkOpaque($x:Number,$y:Number):Boolean 2{ 3\t//如果没有图像，当然是透明的 4\tif(!this.bitmapData) return false; 5\tvar __argb:uint = this.bitmapData.getPixel32($x,$y); 6\ttrace(\u0026#39;单击的像素的颜色：\u0026#39;, __argb.toString(16)); 7\t//手机上可能最大只能支持到24bit颜色，所以当颜色等于24bit颜色的最大值的时候，直接认为像素是透明的 8\tif(__argb == 0x1000000) return false; 9\t//否则就判断透明度 10\treturn (__argb\u0026gt;\u0026gt;24\u0026amp;0xFF) \u0026gt; 0; 11} ","date":"2012-04-20","description":"","lastmod":"2012-04-20T02:27:51Z","slug":"air_mobile_getpixel32","tags":["air","as3","bitmapdata","mobile"],"title":"AIR对Mobile设备位图取色的问题","url":"https://blog.zengrong.net/post/air_mobile_getpixel32/"},{"categories":["technology"],"content":"使用svnsync实现已有版本库的镜像 svn不支持分布式开发，所以把svn版本库保存在一台服务器上是不安全的。制作一个镜像svn版本库有多种方式，我采用subversion自带的svnsync程序。\n此教程基于Archlinux。windows用户请注意路径表示法。\n源版本库路径：http://192.168.16.10/svn/project1 镜像版本库路径：/var/svn/project1\n一、建立镜像版本库 1# 创建版本库 2svnadmin create /var/svn/project1 3# 创建钩子 4cp /var/svn/project1/hooks/pre-revprop-change.tmpl /var/svn/project1/hooks/pre-revprop-change 5# 给予钩子运行权限 6chmod a+x /var/svn/project1/hooks/pre-revprop-change 7# 编辑pre-revprop-change钩子，将最后一行的 `exit 1` 改为 `exit 0` 8vim /var/svn/project1/hooks/pre-revprop-change 二、初始化镜像库 1svnsync init file:///var/svn/project1/ http://192.168.16.10/svn/project1 然后按照提示输入密码，提示信息可能如下：\n认证领域: http://192.168.16.10 Subversion Repositories “zrong”的密码: 认证领域: http://192.168.16.10 Subversion Repositories 用户名: zrong “zrong”的密码:xxxx 复制版本 0 的属性。\n三、同步 1# 以后的同步也使用这段代码进行 2svnsync sync file:///var/svn/project1 如果系统提示要保存明文密码，输入yes就可以了。这里我没有研究如何对密码进行加密。\n接下来进入同步流程，同步的提示可能如下：\n已提交版本 1。 复制版本 1 的属性。 传输文件数据.................................... 已提交版本 1。\n这个时间可能会很长，具体要看网速和原始版本库的大小。如果版本库中有带的的二进制文件，则进度会更慢。在svnsync同步的过程中，应该保证原始的svn数据库不要进行提交操作，否则可能会造成同步失败，同步失败会引起镜像版本库的锁定。下面会讲解如何解锁。\n四、解锁 如果在同步的时候遇到下面的提示，可能是由于原始版本库同时正在提交：\n从目标版本库获得锁失败，当前被“xxxxx”持有\n如果是在svn中，可以使用svn cleanup来解除锁定。但是svnsync并没有这个功能。我们可以使用下面的代码来解除锁定：\n1svn propdel svn:sync-lock --revprop -r 0 file:///var/svn/project1/ 2# 删除属性 “svn:sync-lock” 于版本库版本 0 五、修改原始版本库的地址 如果原始版本库的地址改变了（比如说换了域名或者IP地址），在svn中可以使用 svn relocate 来实现，但在svnsync中，就必须用propset来实现了，具体代码如下：\n1svn propget svn:sync-from-url –-revprop -r 0 file:///var/svn/project1/ 2# 这个操作会显示原始版本库的地址（假如你忘了原始地址就很有用） 3# http://192.168.16.10/svn/project1 4 5# 设置新的版本库地址 6svn propset svn:sync-from-url –-revprop -r 0 http://10.0.0.9/svn/project1 file:///var/svn/project1/ 7# 再次同步 8svnsync sync file:///var/svn/project1/ 参考文章：\nhttp://blog.csdn.net/nuoyazhizhou/article/details/5259876 http://www.emreakkas.com/linux-tips/how-to-change-svnsync-url-for-source-repository ","date":"2012-04-19","description":"","lastmod":"2012-04-19T14:18:16Z","slug":"use_svnsync","tags":["linux","svn"],"title":"使用svnsync实现已有版本库的镜像","url":"https://blog.zengrong.net/post/use_svnsync/"},{"categories":["technology"],"content":"升级CentOS 5.x中的PHP 5.1到5.3\n2012-04-19 更新：后来发现直接使用yum install php53，也能安装php，而且处于官方源中。所以可以先试试这个，不行再试下面的方法。\n服务器版本为CentOS 5.8，自带的php为5.1.6。在配置phpMyAdmin的时候，发现必须要PHP 5.2才可以支持，但yum的源中并没有PHP 5.2，无法使用yum来升级。\n首先按照官方wiki的说明进行了修改，结果没用，yum始终报告5.1.6是最新版，无法升级。\n找到一篇介绍文章服务器配置之-在CentOS中安装php5.3，但发现文章中提供的download.fedora.redhat.com网站已经无法访问。google了一下，原来是http://download.fedora.redhat.com下载地址已经改为http://dl.fedoraproject.org。下面是更新后的方法（针对64bit CentOS 5.x）：\n1# 删除原来的PHP版本，请自行备份php.ini 2yum remove php-cli php-common php 3# 安装新的源 4rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm 5rpm -Uvh http://dl.iuscommunity.org/pub/ius/stable/Redhat/5/x86_64/ius-release-1.0-10.ius.el5.noarch.rpm 6# 安装新版php 7yum install php53u 如果你使用CentOS 32bit版本，则需要将上面的x86_64改为i386。\n如果在安装php53u的过程中出现这样的错误：\nfile /usr/bin/php from install of php53u-cli-5.3.4-3.ius.el5.x86_64 conflicts with file from package php-cli-5.1.6-27.el5_5.3.x86_64 file /usr/bin/php-cgi from install of php53u-cli-5.3.4-3.ius.el5.x86_64 conflicts with file from package php-cli-5.1.6-27.el5_5.3.x86_64 file /usr/share/man/man1/php.1.gz from install of php53u-cli-5.3.4-3.ius.el5.x86_64 conflicts with file from package php-cli-5.1.6-27.el5_5.3.x86_64 file /etc/php.ini from install of php53u-common-5.3.4-3.ius.el5.x86_64 conflicts with file from package php-common-5.1.6-27.el5_5.3.x86_64 这多半是由于没有删除原来的php版本造成的，执行下面的命令即可清除：\n1yum remove php-cli php-common php 参考资料：\nhttp://dev.yidianhulian.com/2011/01/19/how-to-install-php53-on-centos/ http://en.ispdoc.com/index.php/Updating_PHP_in_CentOS_Linux http://www.andresmontalban.com/update-centos-5-php-5-1-to-php-5-3/ http://blog.lilujun.com/post/1208/ http://www.webtatic.com/packages/php53/ http://serverfault.com/questions/221251/how-do-i-install-php-5-3-on-centos ","date":"2012-04-18","description":"","lastmod":"2012-04-18T04:06:42Z","slug":"update_centos5x_php51_to_php53","tags":["centos","linux","php"],"title":"升级CentOS 5.x中的PHP 5.1到5.3","url":"https://blog.zengrong.net/post/update_centos5x_php51_to_php53/"},{"categories":["technology"],"content":"我个人的版本库目前全部用Git，但公司的版本库则全部都是SVN，所以我是SVN＋Git通用，只用命令行。\n为了便于查看，我会为git和SVN搭建一个web查看环境，这方面就要依赖GitWeb和 WebSVN。\n关于这两者的配置和安装，Google中有非常多的教程（当然有优秀的教程和稀烂的教程，看你怎么找了），我不再给出详细的步骤，只说一下自己在配置中碰到的问题和注意事项。\nGitweb Gitweb已经包含在git的安装包和源码中了，因此稍微配置一下就可以使用。这里有一篇面向ArchLinux的配置方法：https://wiki.archlinux.org/index.php/Gitweb。\nLinux内核代码就可以使用Gitweb查看：http://git.kernel.org/，界面虽然简陋，但该有的功能一样都不少。\nGitweb的优点在于它可以递归的显示指定目录下的所有仓库。我的习惯是为每个项目的子项目建立独立的版本库方便管理。因此需要在总库下建立目录来分离每个项目。Gitweb会自动将总库下面的所有字库递归地读取出来进行显示。\n例如，我的总库保存在 /home/git 下，A项目有3个子项目，分别是a1、a2和a3，对应路径为 /home/git/A/a1.git;/home/git/A/a2.git;/home/git/A/a3.git 。如果使用Gitweb指定版本库的根目录为 /home/git ，那么Gitweb会自动将 a1/a2/a3 子版本库都读取出来。\n如果在配置完成后，Gitweb的首页依然显示404 project not find，而你又确定路径正确，那么最大的可能就是你的版本库的权限没有设置正确。如果希望Gitweb能显示你的版本库，则为你所有的版本库加入读权限和执行权限。\nWebSVN 和Gitweb相比，WebSVN的界面就漂亮了许多。在这里可以看到一个WebSVN的Demo：http://demo.websvn.info/\nWebSVN使用PHP写成，配置上非常容易。如果将WebSVN布署在SVN所在的服务器上，在配置的时候甚至都不需要SVN的访问权限，就可以在网页上完整地查看SVN版本库。\n","date":"2012-04-16","description":"","lastmod":"2012-04-16T12:36:58Z","slug":"gitweb_and_websvn","tags":["develop","git","linux","software","svn"],"title":"Gitweb 和 WebSVN","url":"https://blog.zengrong.net/post/gitweb_and_websvn/"},{"categories":["technology"],"content":"在Bash中将字符串拆分成数组\nsplit string to array in bash\n和AS中的String.split不同，Bash没有类似的语法，但它实在是太灵活了，有很多种方式可以做类似的事情。\n以前我写过一篇 Bash数组操作教程 ，今天使用Bash做文件处理的时候，发现有必要再写一篇将字符串拆分成数组的教程。但发现有人已经写了更好的教程在前面了，于是就偷懒转过来好了。\n** 下面的内容转自Bash @ Linux **\n以空白作为分隔符来拆分字符串构造数组 ARR=($STR)\n注意$STR不能加引号。\n1STR=\u0026#34;Hello World\u0026#34; 2ARR=($STR) 3declare -p ARR 4declare -a ARR=\u0026#39;([0]=\u0026#34;Hello\u0026#34; [1]=\u0026#34;World\u0026#34;)\u0026#39; 用指定分隔符来拆分字符串构造数组 如果分隔符不是空白，而是别的，那么需要借助IFS变量。\ndefault IFS (Internal Field Separator, which is space/tab/new line)\n1echo $PATH 2/usr/kerberos/sbin:/usr/kerberos/bin:/usr/apache/apache-ant-1.7.1/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin 3IFS=: DIRS=($PATH) 4declare -p DIRS 5declare -a DIRS=\u0026#39;([0]=\u0026#34;/usr/kerberos/sbin\u0026#34; [1]=\u0026#34;/usr/kerberos/bin\u0026#34; [2]=\u0026#34;/usr/apache/apache-ant-1.7.1/bin\u0026#34; [3]=\u0026#34;/usr/local/sbin\u0026#34; [4]=\u0026#34;/usr/local/bin\u0026#34; [5]=\u0026#34;/sbin\u0026#34; [6]=\u0026#34;/bin\u0026#34; [7]=\u0026#34;/usr/sbin\u0026#34; [8]=\u0026#34;/usr/bin\u0026#34; [9]=\u0026#34;/root/bin\u0026#34;)\u0026#39; 但是下面的方式是不行的。\n1echo $PATH 2/usr/kerberos/sbin:/usr/kerberos/bin:/usr/apache/apache-ant-1.7.1/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin 3IFS=: declare -a DIRS=($PATH) 4declare -p DIRS 5declare -a DIRS=\u0026#39;([0]=\u0026#34;/usr/kerberos/sbin:/usr/kerberos/bin:/usr/apache/apache-ant-1.7.1/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin\u0026#34;)\u0026#39; 使用read -a来拆分字符串构造数组 All work and no play makes Jack a dull boy. 只会用功不玩耍，聪明孩子也变傻。\n1PROVERB=\u0026#34;All work and no play makes Jack a dull boy.\u0026#34; 2read -a WORDS \u0026lt;\u0026lt;\u0026lt; $PROVERB 3echo \u0026#34;$WORDS\u0026#34; 4All 5echo \u0026#34;${#WORDS}\u0026#34; 63 7echo \u0026#34;${WORDS[*]}\u0026#34; 8All work and no play makes Jack a dull boy. 9echo \u0026#34;${WORDS[@]}\u0026#34; 10All work and no play makes Jack a dull boy. 11echo \u0026#34;${#WORDS[*]}\u0026#34; 1210 13echo \u0026#34;${#WORDS[@]}\u0026#34; 1410 前面的例子中要分割的字符串是以空格分割的，现在举一个以:分割的例子。\n1echo $IFS 2IFS=: read -r -a DIRS \u0026lt;\u0026lt;\u0026lt; \u0026#34;$PATH\u0026#34; 3echo $IFS 4declare -p DIRS 5declare -a DIRS=\u0026#39;([0]=\u0026#34;/usr/kerberos/sbin\u0026#34; [1]=\u0026#34;/usr/kerberos/bin\u0026#34; [2]=\u0026#34;/usr/apache/apache-ant-1.7.1/bin\u0026#34; [3]=\u0026#34;/usr/local/sbin\u0026#34; [4]=\u0026#34;/usr/local/bin\u0026#34; [5]=\u0026#34;/sbin\u0026#34; [6]=\u0026#34;/bin\u0026#34; [7]=\u0026#34;/usr/sbin\u0026#34; [8]=\u0026#34;/usr/bin\u0026#34; [9]=\u0026#34;/root/bin\u0026#34;)\u0026#39; 下面的例子是将当前工作目录以/进行分割。\n1echo $PWD 2/root/work191/ct08/src/ctmw 3IFS=/ read -r -a PARTS \u0026lt;\u0026lt;\u0026lt; $PWD 4declare -p PARTS 5declare -a PARTS=\u0026#39;([0]=\u0026#34; root work191 ct08 src ctmw\u0026#34;)\u0026#39; 6IFS=/ read -r -a PARTS \u0026lt;\u0026lt;\u0026lt;\u0026#34;$PWD\u0026#34; 7declare -p PARTS 8declare -a PARTS=\u0026#39;([0]=\u0026#34;\u0026#34; [1]=\u0026#34;root\u0026#34; [2]=\u0026#34;work191\u0026#34; [3]=\u0026#34;ct08\u0026#34; [4]=\u0026#34;src\u0026#34; [5]=\u0026#34;ctmw\u0026#34;)\u0026#39; 使用cut命令分隔字符串 1echo \u0026#34;$STR\u0026#34; | cut -f $N 以TAB分隔，打印第N个子串值，N从1开始计数。\n1echo \u0026#34;$STR\u0026#34; | cut -d \u0026#34;$DELIM\u0026#34; -d $N 以指定DELIM分隔，打印第N个子串值，N从1开始计数。 其中，-d部分也可以是$N1,$N2,$N3的形式，即输出多个子串。\nA good beginning is half done. 良好的开端是成功的一半。\n1STR=\u0026#34;A good beginning is half done.\u0026#34; 2echo $STR | cut -d \u0026#39; \u0026#39; -f 2 3good 4echo $PATH 5/usr/kerberos/sbin:/usr/kerberos/bin:/usr/apache/apache-ant-1.7.1/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin 6echo $PATH | cut -d \u0026#39;:\u0026#39; -f 3 7/usr/apache/apache-ant-1.7.1/bin 8echo $PATH | cut -d \u0026#39;:\u0026#39; -f 3,5 9/usr/apache/apache-ant-1.7.1/bin:/usr/local/bin 使用awk命令分隔字符串 1echo \u0026#34;$STR\u0026#34; | awk \u0026#39;{print $1}\u0026#39; 2echo \u0026#34;$STR\u0026#34; | awk \u0026#39;{print $2}\u0026#39; 注意：awk后面的参数用单引号，不能用双引号。\n1STR=\u0026#34;A good beginning is half done.\u0026#34; 2echo \u0026#34;$STR\u0026#34; | awk \u0026#39;{print $1}\u0026#39; 3A 4echo \u0026#34;$STR\u0026#34; | awk \u0026#39;{print $2}\u0026#39; 5good 6echo \u0026#34;$STR\u0026#34; | awk \u0026#39;{print $5,$6}\u0026#39; 7half done. ","date":"2012-04-13","description":"","lastmod":"2012-04-13T13:10:00Z","slug":"bash_split_string_to_array","tags":["bash"],"title":"在Bash中将字符串拆分成数组","url":"https://blog.zengrong.net/post/bash_split_string_to_array/"},{"categories":["technology"],"content":"原来在写 Sprite Sheet Editor 的时候，就发现 mx.graphics 包中提供的 JPEG 和 PNG 压缩程序的性能实在很差。用 JAVA 写的压缩器，在 JPEG 的压缩上比 mx.graphics.JPEGEncoder 要高20倍左右。\n现在，Flash Player 11.3 beta1/AIR 3.3 beta1 终于为我们带来了原生支持的图像压缩功能。\n我使用AIR 3.3 beta1（它和Flash Player 11.3功能相同）写了一个demo来测试新加入的bitmapData.encode方法的性能。实际的性能令人满意：\n压缩JPEG的性能大约是AS3编写的JPEGEncoder的20倍； 压缩PNG的性能大约是AS3编写的PNGEncoder的4～6倍。 以下是该Demo的截图：\n以下是安装包和源代码下载，注意，必须下载 AIR 3.3 beta1 才能使用下面的程序。\n1 文件 1 文件 ","date":"2012-04-08","description":"","lastmod":"2012-04-08T15:10:28Z","slug":"test-bitmapdata-encode","tags":["air","bitmapdata","flashplayer"],"title":"测试 Flash Player 11.3/AIR 3.3 提供的 bitmapData.encode 方法的性能","url":"https://blog.zengrong.net/post/test-bitmapdata-encode/"},{"categories":["news"],"content":" 英文原文 转自 OSChina.NET 到目前为止，在过去60年中在世界排名前20的国家中，没有哪条街是以程序员或者计算机科学家的名字命名的。\n没有任何一个世界主要城市拥有程序员或者计算机科学家的雕像。\n没有程序员或者计算机科学家获得过总统奖章（Presidential Medal）或者国会金质奖章（Congressional Gold Medal）。\n没有国家级别的针对程序员或者计算机科学家的颁奖典礼。但是我们有艺术、运动、经济、娱乐等大型颁奖典礼。更没有红地毯或者类似诺贝尔奖来表彰程序员的成就和为人类作出的贡献。\n即使程序员、计算机科学家的工作让整个世界大唱赞歌，他们也只是坐在后排座位，看着别人发表演讲，拿起奖杯。\n唯一的一个例外，是英国的计算机科学家 Alan Turing （图灵）被誉为计算机之父。图灵有以下荣誉：一座桥和一条街被命名为“Alan Turing Way”。图灵的雕像从2001年6月23日开始立在了曼彻斯特。从1966年开始，图灵奖每年都会颁发给在这个领域有杰出贡献的人。图灵奖被誉为计算机世界的最高奖项，等同于诺贝尔奖。\n大部分的程序员和计算机科学家的薪水都不如销售，管理，设计，医生等等。当然程序员在约会方面也没有优势，他们不是艺术家，音乐家，设计师，建筑师，运动员……程序员们绝对不处于男女关系链（Sexual food chain）的上层。\n时代杂志在过去的85年中曾经提名过4位计算机领域的年度人物：\nThe Computer 1982年 Andrew Grove 1997年 Jeffrey P. Bezos 1999年 Mark Zuckerberg 2010年 图为Facebook的创始人，Mark Zuckerberg 和他的华人女友Chen。\n不管怎样，计算机明星要进入人们的日常生活，学校，街道，市场，媒体等等还有很长的路要走。\n于是，我就在思考一个问题，为什么程序员和计算机科学家不能得到合理的认可和荣誉？我看看周围的世界，我在想假如没有这些怪人，世界将会怎样？从iPhone到其他最新的电子产品，少了这些疯狂工作的怪人，一切将不复存在。\n还有一个令人不悦的事实：\n在2011年10月8日，C语言的发明人Dennis Ritchies和乔布斯差不多时间离开我们。但是关于Ritchie的报道寥寥无几，几乎被世人忽略。只有非常少的媒体，博客和论坛报道了这件事。全世界铺天盖地都是乔布斯的消息，包括美国总统在内的数不清的商界精英和媒体都在报道乔布斯的人生、成就以及遗留的问题。\n我想问“Dennis Ritchie 的成就不如乔布斯吗？乔布斯的成就给我们的生活带来了更多的进步？”假如你的答案是NO，那么你怎么解释我上面说的事实、现状？！\n于是，基于上述事实，我做了一些调查。我的发现令我震惊，也让我觉得很好笑。\n**第一个发现：**这就是程序员的命。程序员的自我尊重和自我认知价值太低。\n有一个现象可以证明这一点：程序员们互相厌恶。他们通常在非程序员面前抨击别的程序员。把一个程序员的代码给另外一个程序员，往往，常常，后者会给前者的工作以负面评价。\n艺术家不会这样。 销售员不会这样。 管理者不会这样。 政治家这么做只是为了赢得选票。 其他专业人士他们会公开的支持同行，即使他们有竞争。（例如律师，请不要问我为什么美国事实上是被这帮混蛋统治的）。\n简而言之，上面提到的这些行业人员拥有更大的行业事业，以自己的工作和行业自豪。\n螃蟹思维是要不得的！（螃蟹思维：一堆螃蟹在盒子里面，盒子是开口的，但是任何一个螃蟹尝试向上爬的时候，其他的螃蟹就会把它拉下来，不让它爬出去。）\n**第二个发现：**大部分人讨厌程序员。\n销售讨厌程序员。 管理者讨厌程序员。 设计师讨厌程序员。 政治明星讨厌程序员。 女孩们讨厌程序员。 为什么？我不知道。我们需要让少一些人讨厌我们！\n第三个发现：程序员们并不追求浮华的头衔，性感的典礼、奖项、他们讨厌聚光灯。\n程序员们喜欢编程。他们想要独自编程。编程是他们的目标，是他们的政治，是他们的荣耀。他们对公共演讲不感兴趣。他们讨厌不如他们聪明的人。他们讨厌不理解C++编程逻辑的人。\n于是他开始慢慢说服自己生来就是内向的。所以他害怕外出，害怕接电话，害怕去泡吧，害怕去见陌生人，害怕所有能为他建立社会关系和社会地位的活动。\n图为Marissa\nMayer，Google的美女高管，程序员出生，常常代表Google在公众场合发表意见，接受采访。\n我有一个梦想：\n书呆子、怪人也有资格获得总统奖章、国会金质奖章以及其他能够承认他们贡献的高等级的奖项。 我是一个程序员，我和其他程序员是朋友，不是敌人。我不在别人面前贬低程序员。（大家一起跟我念！） 程序员是聪明勇敢的，而且我们也很风趣。“内向不是我们的DNA，所以我们并不是生来就内向，我可以调整我的态度和社交能力。”（大家跟我一起念！） 目前这个年代对程序员是非常有利的，起码我们很容易找工作而其他行业还得头破血流的抢饭碗，哈哈！程序员大复仇！\n","date":"2012-04-06","description":"","lastmod":"2012-04-06T13:35:37Z","slug":"programmers-high-social-status","tags":["study"],"title":"【转】为什么程序员的社会地位不高？","url":"https://blog.zengrong.net/post/programmers-high-social-status/"},{"categories":["tutorial"],"content":"原文地址：Differences Between PDF and JPG/PNG Image Format for DPS\n当PDF图像格式在DPS文章中第一次可用的时候，该格式还有很多限制。最重要的限制是，基于PDF的文章不支持交互操作。因此大多数设计师仍坚持使用JPG/PNG图像格式，除非他们希望用户会在页面上收缩和放大图像。\nv18版本的工具改变了这种情况。现在，PDF文章中所有的交互操作与JPG/PNG文章一样好。\n在新iPad的视网膜显示屏上，PDF图像格式是被推荐的选择。PDF文章比JPG/PNG文章更小，矢量格式能增强缩放体验。然而，你依然应该了解PDF和JPG/PNG图像格式之间的不同。\nPDF的优势 PDF文章通常比JPG/PNG文章更小，尤其是对于长文章来说； PDF文章的页面支持手势缩放； PDF格式现在支持所有的交互操作； 文件格式小和支持矢量文本的特点，让PDF格式成为新iPad的视网膜显示屏的理想格式； PDF的劣势 只有iOS查看器支持PDF图像格式。无法在桌面查看器或者Andorid设备上观看PDF文章； PDF图像格式不支持平滑卷动。如果你导入了多篇文章到Folio中，且设定默认的图像设置为PDF，平滑卷动的文章将转换成JPG或者PNG； PDF文章在翻页的时候需要一点点载入时间； 在Folio Producer Editor中无法预览PDF文章的缩略图。如果你知道这个限制就没什么问题。不过大多数人在Folio Producer Editor界面中看不到PDF页时，可能会认为页面丢失了。 原文内容：\nDifferences Between PDF and JPG/PNG Image Format for DPS\nWhen the PDF image format was first made available for DPS articles, the format was limited. Most importantly, interactive overlays were not supported in PDF-based articles, so most designers stuck with the JPG/PNG image format unless they wanted users to be able to pinch \u0026amp; zoom on a page.\nWith the v18 release of the tools, that changed. Now, all interactive overlays in PDF articles work just as well as they do in JPG/PNG articles.\nWith the new retina display iPad, using the PDF format has now become the recommended choice for image format. PDF articles are smaller than JPG/PNG articles, and vector content is maintained, resulting in improved scaling. However, there are still some differences between the PDF and JPG/PNG image formats that you should be aware of.\nAdvantages of PDF PDF articles are usually much smaller than JPG/PNG articles, especially long articles. Pages in PDF articles can have pinch and zoom enabled. All interactive overlays are now supported in PDF. The combination of small file size and vector text makes PDF the ideal format for the new retina display iPad. Disadvantages of PDF Only iOS viewers support the PDF image format. You cannot preview PDF articles in the Desktop Viewer or in Android devices. Smooth Scrolling articles cannot use the PDF image format. If you import multiple articles into a folio in which the default image setting is PDF, the Smooth Scrolling articles are converted to JPG or PNG. There is sometimes a brief load time when flipping to a PDF article. In the Folio Producer Editor, preview thumbnails are not available for PDF articles. This shouldn’t be a problem as long as you’re aware of this limitation, but some people look at their PDF folios in the Folio Producer Editor and think their content is missing. ","date":"2012-04-04","description":"","lastmod":"2012-04-04T15:54:52Z","slug":"differences-between-pdf-and-jpgpng-image-format-for-dps","tags":["adobe","software"],"title":"【译】Adobe数字出版套件(DPS)中PDF与JPG/PNG图像格式的不同","url":"https://blog.zengrong.net/post/differences-between-pdf-and-jpgpng-image-format-for-dps/"},{"categories":["technology"],"content":"【译】如何创建游戏的续作\n原文地址：How to create the sequel of a game\n这些天，我在研究Box2D，并正在用它开发SamePhysics这个游戏的续作。\n这是我的第一个Box2D游戏（你可以从这里下载它的源码），大家认为它很有趣。于是我决定制作一个更有趣的续作，以改进我在旧版本中错过的那些东西。\n这是我的第一个续作，因此我必须在开始编码前考虑一些要注意的事项。现在我把它们共享出来：\n明智地选择值得制作续作的那个游戏 这是第一件事，也是最明显的一件。如果大家不喜欢原来的游戏，那么很可能他们也不喜欢续作。或者更糟，他们都不会看它一眼。SamePhysics在Kongregate和Newgrounds这两个网站上获得了250万次浏览，且分数分别达到3分和3.5分，可以说大家是喜欢这个游戏的。\n修复反馈中的问题 你上传游戏到Kongregate并不是为了传播病毒，不是么？在Kongregate中最有趣的事情是玩家对你游戏的高质量回复。一个好的游戏续作的目的，是降低不好的反馈，而让好的东西更好。\n在旧版本游戏中，主要的缺陷是碰撞检测。在Box2D中，如果使用接触侦听器类来检测两个箱子是否碰撞，只有当它们真正在物理世界中碰到时才会生效。这太糟糕了。因为在我的游戏中，2个箱子之间的距离是2厘米，舞台单位是30像素/米。这种情况下，两个箱子之间只有0.6像素的间隙，这是很难用肉眼分辨的。\n看：\n在物理世界中，左边的红色箱子并没有碰到右边的哪个，但是在玩家看来它们碰到了，所以玩家希望能移除它们。\n在续作中，我没有使用定制的接触侦听器类来实现碰撞检测，而是使用了一些技巧来实现了一种容差的“所见即所得”效果。\n在这张图中，箭头所指的两个箱子没有接触，但是它们之间距离很紧，玩家期望（或者至少希望）它们接触。于是，我让它们接触。除了这种容差处理，还有另外3个要点：\n加入新的游戏模式 给游戏换个马甲，或者只是加入一些新的关卡，这不是续作。你应该加入一些新的游戏模式。像上面提到的容错玩法，我可以用它创建各种游戏模式，比如可以叫做“真实的世界”、“拱廊”、“噩梦世界”，等等。\n改善图像素材的质量 你需要重画图像素材，让它们更漂亮，更精致。玩家能看到你在第一个游戏发布之后这段时间内所做的工作，他们也发现你的技能更纯熟了。上面所有的事情，你都是为了让游戏尽量做到最好。我依然使用图像，但是我改用奇特的角色而不再使用简单的箱子形象。\n加入新的游戏元素设计 你会玩与旧版“愤怒的小鸟”完全相同的游戏么？不会。这就是为什么“愤怒的小鸟太空版”要加入新的环境和新的小鸟形象的原因。新版本的“愤鸟”和旧版本基本相同：随机发射一只小鸟去干掉所有的可怜猪，但由于增加了新的鸟类，且重力半径会影响飞行路线，你依然会去玩它，这就是重点。\n看看新的API 从旧版本发布起，过去多长时间了？或许你用来管理排行榜和处理成就系统的API有了变化，或者引入了新的功能。你需要熬夜加入新的API，因为在新游戏中使用旧的API，比在旧游戏中使用新的API更糟糕。\n我希望你能在一周左右看到SamePhysics续作。\n","date":"2012-03-31","description":"","lastmod":"2012-03-31T00:10:33Z","slug":"how_to_create_the_sequel_of_a_game","tags":["box2d","engine","game","translate"],"title":"【译】如何创建游戏的续作","url":"https://blog.zengrong.net/post/how_to_create_the_sequel_of_a_game/"},{"categories":["technology"],"content":"【译】使用ConstraintLayout创建复杂的表单布局\n原文地址：Flex - Complex Form layouts with ContraintLayout\nForm容器默认使用FormLayout布局排列子表单项。FormLayout继承自VerticalLayout，它对FormItem容器进行纵向排列。如果你想实现纵向的排列表单，这当然好，但是很多时候往往不是这样。看看下面的例子。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n通常情况下，我看到开发者在实现一个表单布局的时候，会放弃FormLayout布局而是使用一堆嵌套的HGroup和VGroup，尽管这种做法有点傻X，效果也不太理想。\n还记得Flex 3中的 ConstraintRows 和 ConstraintColumns 么？它们在Flex 4中换了个 ConstraintLayout 的马甲又出现了，这个 Layout 可以用在 Form 中。我发现许多 Flex 开发者不知道这件事，这不奇怪，因为它们在官方文档中的说明少得可怜。而且，如果使用 Google 搜索它，你只能得到 MX 组件的相关内容。\n上面的表单在 Spark Form 容器中使用 ConstraintLayout 替代了 FormLayout ，这个 Layout 为 FormItem 容器提供了类似于网格布局的功能，同时实现跨行、跨列。\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;s:Application xmlns:fx=\u0026#34;http://ns.adobe.com/mxml/2009\u0026#34; 3 xmlns:s=\u0026#34;library://ns.adobe.com/flex/spark\u0026#34; 4 xmlns:mx=\u0026#34;library://ns.adobe.com/flex/mx\u0026#34; 5 viewSourceURL=\u0026#34;srcview/index.html\u0026#34;\u0026gt; 6 \u0026lt;s:layout\u0026gt; 7 \u0026lt;s:VerticalLayout horizontalAlign=\u0026#34;center\u0026#34; verticalAlign=\u0026#34;middle\u0026#34;/\u0026gt; 8 \u0026lt;/s:layout\u0026gt; 9 10 \u0026lt;fx:Declarations\u0026gt; 11 \u0026lt;!-- Place non-visual elements (e.g., services, value objects) here --\u0026gt; 12 \u0026lt;/fx:Declarations\u0026gt; 13 14 \u0026lt;s:Form\u0026gt; 15 \u0026lt;s:layout\u0026gt; 16 \u0026lt;s:ConstraintLayout\u0026gt; 17 \u0026lt;s:constraintRows\u0026gt; 18 \u0026lt;s:ConstraintRow id=\u0026#34;row1\u0026#34; /\u0026gt; 19 \u0026lt;s:ConstraintRow id=\u0026#34;row2\u0026#34;/\u0026gt; 20 \u0026lt;s:ConstraintRow id=\u0026#34;row3\u0026#34;/\u0026gt; 21 \u0026lt;/s:constraintRows\u0026gt; 22 \u0026lt;s:constraintColumns\u0026gt; 23 \u0026lt;s:ConstraintColumn id=\u0026#34;col1\u0026#34; width=\u0026#34;100\u0026#34;/\u0026gt; 24 \u0026lt;s:ConstraintColumn id=\u0026#34;col2\u0026#34; width=\u0026#34;100\u0026#34;/\u0026gt; 25 \u0026lt;s:ConstraintColumn id=\u0026#34;col3\u0026#34; width=\u0026#34;100\u0026#34;/\u0026gt; 26 \u0026lt;/s:constraintColumns\u0026gt; 27 \u0026lt;/s:ConstraintLayout\u0026gt; 28 \u0026lt;/s:layout\u0026gt; 29 30 \u0026lt;s:FormItem label=\u0026#34;First Name\u0026#34; 31 left=\u0026#34;col1:3\u0026#34; right=\u0026#34;col2:53\u0026#34; top=\u0026#34;row1:0\u0026#34; bottom=\u0026#34;row1:0\u0026#34; 32 skinClass=\u0026#34;com.shinynet.shinylib.skins.CondensedStackedFormItemSkin\u0026#34;\u0026gt; 33 \u0026lt;s:TextInput width=\u0026#34;100%\u0026#34;/\u0026gt; 34 \u0026lt;/s:FormItem\u0026gt; 35 \u0026lt;s:FormItem label=\u0026#34;Last Name\u0026#34; 36 left=\u0026#34;col2:53\u0026#34; right=\u0026#34;col3:3\u0026#34; top=\u0026#34;row1:0\u0026#34; bottom=\u0026#34;row1:0\u0026#34; 37 skinClass=\u0026#34;com.shinynet.shinylib.skins.CondensedStackedFormItemSkin\u0026#34;\u0026gt; 38 \u0026lt;s:TextInput width=\u0026#34;100%\u0026#34;/\u0026gt; 39 \u0026lt;/s:FormItem\u0026gt; 40 41 \u0026lt;s:FormItem label=\u0026#34;Address\u0026#34; 42 left=\u0026#34;col1:3\u0026#34; right=\u0026#34;col3:3\u0026#34; top=\u0026#34;row2:0\u0026#34; bottom=\u0026#34;row2:0\u0026#34; 43 skinClass=\u0026#34;com.shinynet.shinylib.skins.CondensedStackedFormItemSkin\u0026#34;\u0026gt; 44 \u0026lt;s:TextInput width=\u0026#34;100%\u0026#34;/\u0026gt; 45 \u0026lt;/s:FormItem\u0026gt; 46 47 \u0026lt;s:FormItem label=\u0026#34;City\u0026#34; 48 left=\u0026#34;col1:3\u0026#34; right=\u0026#34;col1:3\u0026#34; top=\u0026#34;row3:0\u0026#34; bottom=\u0026#34;row3:0\u0026#34; 49 skinClass=\u0026#34;com.shinynet.shinylib.skins.CondensedStackedFormItemSkin\u0026#34;\u0026gt; 50 \u0026lt;s:TextInput width=\u0026#34;100%\u0026#34;/\u0026gt; 51 \u0026lt;/s:FormItem\u0026gt; 52 \u0026lt;s:FormItem label=\u0026#34;State\u0026#34; 53 left=\u0026#34;col2:3\u0026#34; right=\u0026#34;col2:3\u0026#34; top=\u0026#34;row3:0\u0026#34; bottom=\u0026#34;row3:0\u0026#34; 54 skinClass=\u0026#34;com.shinynet.shinylib.skins.CondensedStackedFormItemSkin\u0026#34;\u0026gt; 55 \u0026lt;s:TextInput width=\u0026#34;100%\u0026#34;/\u0026gt; 56 \u0026lt;/s:FormItem\u0026gt; 57 \u0026lt;s:FormItem label=\u0026#34;Zip\u0026#34; 58 left=\u0026#34;col3:3\u0026#34; right=\u0026#34;col3:3\u0026#34; top=\u0026#34;row3:0\u0026#34; bottom=\u0026#34;row3:0\u0026#34; 59 skinClass=\u0026#34;com.shinynet.shinylib.skins.CondensedStackedFormItemSkin\u0026#34;\u0026gt; 60 \u0026lt;s:TextInput width=\u0026#34;100%\u0026#34;/\u0026gt; 61 \u0026lt;/s:FormItem\u0026gt; 62 \u0026lt;/s:Form\u0026gt; 63 64\u0026lt;/s:Application\u0026gt; 下面的内容和本文主题没什么关系，但我还是要说一下。我使用了自定义的 FormItem 皮肤 CondensedStackedFormItemSkin ，这个皮肤移除了 sequenceLabelDisplay 和 helpContentGroup 这两个外观部件，因为它们是不必要的，而且占用了太多空间。你可以从 ShinyLib 库中得到它们，你会发现它非常易用。\n","date":"2012-03-31","description":"","lastmod":"2012-03-31T00:04:44Z","slug":"flex_complex_form_layouts_with_contraintlayout","tags":["flex","translate"],"title":"【译】使用ConstraintLayout创建复杂的表单布局","url":"https://blog.zengrong.net/post/flex_complex_form_layouts_with_contraintlayout/"},{"categories":["technology"],"content":"2015-04-03更新： 加入更新 Adobe AIR 的相关内容。\n今天在论坛上看到一篇文章：H264VideoStreamSettings无法在cs4里面使用吗？，并做了回复。记录在这里，希望对更多的人有帮助。\nH264VideoStreamSettings是Flash Player 11才开始支持的功能，但目前Aodbe发布的最新版的Flash CS5.5，都不支持将Flash Player 11作为目标播放器版本发布。这个问题很容易解决，但涉及到的东西却比较多，我在这里将一一讲解。\n首先来个Setp by Step，这里以Flash Player CS5.5为例，但Flash CS4也没有问题（Flash CS3我就不确定了）：\n第一步 首先下载最新的 playerglobal.swc（基于Flash Player11）：\n第二步 将其复制到 FlashCS5.5\\CommonConfiguration\\ActionScript 3.0\\FP11.1 ，没有这个文件夹请自行建立；\n第三步 复制 FlashCS5.5\\CommonConfiguration\\Players\\FlashPlayer10_1.xml 为 `FlashPlayer11_1.xml；\n第四步 用 NotePad++/EditPlus/UltraEdit （但不要用windows Notepad，避免换行符问题）之类的软件打开 `FlashPlayer11_1.xml ，修改以下内容：\n第3行修改为：\n1\u0026lt;player id=\u0026#34;FlashPlayer11.1\u0026#34; version=\u0026#34;14\u0026#34; asversion=\u0026#34;3\u0026#34;\u0026gt; 第4行修改为：\n1\u0026lt;name\u0026gt;Flash Player 11.1\u0026lt;/name\u0026gt; 第8行修改为：\n1\u0026lt;playerDefinitionPath as2=\u0026#34;$(UserConfig)/Classes/FP10;$(UserConfig)/Classes/FP9;$(UserConfig)/Classes/FP8;$(UserConfig)/Classes/FP7\u0026#34; as3=\u0026#34;$(AppConfig)/ActionScript 3.0/FP11_1/playerglobal.swc\u0026#34; /\u0026gt; 这是我修改好的，可以直接用：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; 2\u0026lt;players\u0026gt; 3 \u0026lt;player id=\u0026#34;FlashPlayer11.1\u0026#34; version=\u0026#34;14\u0026#34; asversion=\u0026#34;3\u0026#34;\u0026gt; 4 \u0026lt;name\u0026gt;Flash Player 11.1\u0026lt;/name\u0026gt; 5 \u0026lt;path builtin=\u0026#34;true\u0026#34;/\u0026gt; 6 \u0026lt;path platform=\u0026#34;WIN\u0026#34;\u0026gt;Device Central/adcdl.exe\u0026lt;/path\u0026gt; 7 \u0026lt;path platform=\u0026#34;MAC\u0026#34;\u0026gt;Device Central/adcdl\u0026lt;/path\u0026gt; 8 \u0026lt;playerDefinitionPath as2=\u0026#34;$(UserConfig)/Classes/FP10;$(UserConfig)/Classes/FP9;$(UserConfig)/Classes/FP8;$(UserConfig)/Classes/FP7\u0026#34; as3=\u0026#34;$(AppConfig)/ActionScript 3.0/FP11_1/playerglobal.swc\u0026#34; /\u0026gt; 9 \u0026lt;feature name=\u0026#34;multiScreenPublish\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 10 \u0026lt;feature name=\u0026#34;mobileAuthoringIntegration\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 11 \u0026lt;feature name=\u0026#34;deviceSound\u0026#34; supported=\u0026#34;false\u0026#34;/\u0026gt; 12 \u0026lt;feature name=\u0026#34;exportStreamingSound\u0026#34; supported=\u0026#34;true\u0026#34;/\u0026gt; 13 \u0026lt;feature name=\u0026#34;exportEventSound\u0026#34; supported=\u0026#34;true\u0026#34;/\u0026gt; 14 \u0026lt;feature name=\u0026#34;FSCommand2\u0026#34; supported=\u0026#34;false\u0026#34;/\u0026gt; 15 \u0026lt;feature name=\u0026#34;gradient_linearRGB\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 16 \u0026lt;feature name=\u0026#34;gradient_overflow\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 17 \u0026lt;feature name=\u0026#34;shape_strokeHint\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 18 \u0026lt;feature name=\u0026#34;shape_cap\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 19 \u0026lt;feature name=\u0026#34;shape_join\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 20 \u0026lt;feature name=\u0026#34;shape_mitre\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 21 \u0026lt;feature name=\u0026#34;shape_scale\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 22 \u0026lt;feature name=\u0026#34;linkage_exportForActionscript\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 23 \u0026lt;feature name=\u0026#34;linkage_exportForRuntimeSharing\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 24 \u0026lt;feature name=\u0026#34;linkage_exportInFirstFrame\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 25 \u0026lt;feature name=\u0026#34;linkage_importForRuntimeSharing\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 26 \u0026lt;feature name=\u0026#34;linkage_importAndAddToCache\u0026#34; supported=\u0026#34;false\u0026#34; /\u0026gt; 27 \u0026lt;feature name=\u0026#34;publish_localPlaybackSecurity\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 28 \u0026lt;feature name=\u0026#34;publish_hardwareAcceleration\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 29 \u0026lt;feature name=\u0026#34;symbol_blendMode\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 30 \u0026lt;feature name=\u0026#34;actionScript_documentClass\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 31 \u0026lt;feature name=\u0026#34;symbol_blendMode\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 32 \u0026lt;feature name=\u0026#34;filters\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 33 \u0026lt;feature name=\u0026#34;component_binding\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 34 \u0026lt;feature name=\u0026#34;component_schema\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 35 \u0026lt;feature name=\u0026#34;screens\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 36 \u0026lt;feature name=\u0026#34;video\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 37 \u0026lt;feature name=\u0026#34;deviceVideo\u0026#34; supported=\u0026#34;false\u0026#34;/\u0026gt; 38 \u0026lt;feature name=\u0026#34;accessibility\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 39 \u0026lt;feature name=\u0026#34;dynamic_text_kerning\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 40 \u0026lt;feature name=\u0026#34;static_text_charwidth_nondeviceFont\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 41 \u0026lt;feature name=\u0026#34;static_text_charwidth_deviceFont\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 42 \u0026lt;feature name=\u0026#34;advanced_anti_alias_text\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 43 \u0026lt;feature name=\u0026#34;nine_slice_scaling\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 44 \u0026lt;feature name=\u0026#34;runtimeNumberMinMax\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 45 \u0026lt;feature name=\u0026#34;use8kSampleRate\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 46 \u0026lt;feature name=\u0026#34;useDefineFont4ForDeviceText\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 47 \u0026lt;feature name=\u0026#34;useDefineFont4ForEmbeddedFonts\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 48 \u0026lt;feature name=\u0026#34;textLayoutFramework\u0026#34; supported=\u0026#34;true\u0026#34; /\u0026gt; 49 \u0026lt;encodingPresets\u0026gt; 50 \u0026lt;preset uuid=\u0026#34;da5cac1a-417a-4d86-b7f7-ef21010a5d7d\u0026#34; name=\u0026#34;FLV - Match Source Attributes (High Quality)\u0026#34; ext=\u0026#34;flv\u0026#34; default=\u0026#34;true\u0026#34;/\u0026gt; 51 \u0026lt;/encodingPresets\u0026gt; 52 53 \u0026lt;testmenu\u0026gt; 54 \u0026lt;menu name=\u0026#34;ID_testInFlash\u0026#34; default=\u0026#34;true\u0026#34;/\u0026gt; 55 \u0026lt;menu name=\u0026#34;ID_testInDeviceCentral\u0026#34; /\u0026gt; 56 \u0026lt;/testmenu\u0026gt; 57 58 \u0026lt;debugmenu\u0026gt; 59 \u0026lt;menu name=\u0026#34;ID_debugtInFlash\u0026#34; default=\u0026#34;true\u0026#34;/\u0026gt; 60 \u0026lt;menu name=\u0026#34;ID_debugInDeviceCentral\u0026#34;/\u0026gt; 61 \u0026lt;/debugmenu\u0026gt; 62 63 64 \u0026lt;/player\u0026gt; 65\u0026lt;/players\u0026gt; 第五步 重新启动Flash CS5.5， 就可以看到新的发布设置生效了，在新建Fla文件的时候选择这个发布设置，就可以支持Flash Player 11.1新增的功能。如下图：\n**第六步 为了能够播放和调试新生成的Flash Player 11文件，还需要更新DebugFlashPlayer，在 这里 下载。注意必须下载Debug版本：Windows Flash Player 11.1 Projector content debugger。如果不知道这些版本之间的区别，可以看看我写的这篇文章：有史以来关于Flash Player的最详细说明 。\n第七步 下载了上面的文件后，将其复制到 FlashCS5.5\\Adobe Flash CS5.5\\Players\\Debug，替换原来的FlashPlayerDebugger.exe。\n下面是原理讲解（Flash IDE代指Flash CS4/CS5/CS5.5）：\n在Flash 8时代，编译器和Flash Player是完全同步的。因为那时，只有Flash IDE可以生成swf文件。而Flex问世，以及MacroMedia被Adobe收购以后，Flash的发展就变得多样了，FlashIDE和Flex都可以生成swf文件，Flash Player的发展也更加独立。现在的情况，Flash CS，Flash Builder，Flex SDK，Flash Player的发布已经完全不同步了。尤其是当Adobe将Flex SDK交给Apache发展后，SDK的更新速度估计会更快。Adobe自己又会紧紧将Flash Player攥在手里，保持自己的步调来更新。\nFlex SDK/Flash IDE可以和不同版本的Flash Player相配。即使是使用Flex SDK 3.6/Flash CS4，同样也可以开发出Flash Player 11.1支持的swf程序。通过使用不同版本Flash Player提供的playerglobal.swc文件，就可以让旧的Flex SDK/Flash IDE兼容新的Flash Player提供的功能，方法就是修改编译属性中的-swf-version和 -target-player编译属性。当然，也可以让新的Flex SDK兼容旧的Flash Player功能。\n以上这段来自我写的这篇文章：Flex编译器参数中-swf-version与-target-player之关系，有修改。\n2015-04-03 更新\nFlash IDE 整合了 AIR SDK 之后，我们也经常需要将最新的 AIR SDK 更新到 Flash IDE 中。\n在 Flash Professional CS6 中，参考这个官方帮助页面即可： Flash Professional Help / Overlay AIR 。\n在 Flash CC 中，则已经在 Help 菜单中提供了 Manage Adobe AIR SDK... 命令可供配置。\n","date":"2012-03-19","description":"","lastmod":"2012-03-19T23:34:40Z","slug":"update-flash-ide-to-support-newer-flash-player","tags":["flash","flashplayer","flex","study"],"title":"让旧版本的 Flash IDE 支持更新的 Flash Player/AIR 功能","url":"https://blog.zengrong.net/post/update-flash-ide-to-support-newer-flash-player/"},{"categories":["news"],"content":"原文地址：A Big Week of News for Adobe’s Gaming Efforts: Conferences, Demos and Screenings Galore\n在Mobile World大会的AIR预览之后，本周的游戏活动让我们更加兴奋。在本周末的Flash游戏高层会议上，Adobe游戏传教士Lee Brimelow、Flash运行时产品经理Thibault Imbert和几百名与会者讨论了AIR3.2中的新特性以及Adobe游戏的发展方向。开发者现在可以在超过五亿部支持Stage 3D技术的手机和平板电脑上开发游戏，这些手机和平板设备包含苹果iOS、Andorid、Barnes \u0026amp; Noble’s Color NOOK、亚马逊的Kindle Fire和黑莓平台。开发者可以利用以前的Stage 3D代码，而不必从头开始。\nAIR 3.2在移动平台上的渲染性能是AIR2的1000倍，它允许游戏开发者在屏幕上渲染包含几百万个对象的动画，并保持60FPS以确保动画流畅。开发者能使用单一的工作流程优化他们的应用，而且能使用设备特有原生扩展。这些原生扩展可以实现振动控制、陀螺仪、双屏幕或整合苹果游戏中心的支付系统。AIR让更多的开发者能容易的提交自己的应用到移动市场。在去年，Android市场和AppStore中的AIR应用程序的数量已经增长了7倍以上！\nAdobe游戏团队也将参加本周在旧金山举行的游戏开发者大会（GDC），在那里，我们将展示多种基于新的浏览器桌面游戏和移动平台游戏，这些游戏的提供者是Adobe的重要合作伙伴Rovio、Zynga、GameFly、EPIC、SpilGames等等。Soundquest、Delta Strike和Sherwood Dungeon这几个高性能、跨平台的游戏使用了新的flash技术，它们将在Adobe的展位中展示。展位中基于AIR的移动平台游戏包括Waste Invaders、Spaced Away和Winter on Whale Island，还有Machinarium，这是一个iPad游戏。并且，我们的朋友NVIDIA和AMD也将展示它们使用Flash建立的演示。那么，来看看我们在北大厅的展位吧，编号2328。\nFlash Player 11.2将在本月迟些时候发布，Adobe正致力于确保Flash技术提供的质量水平能满足游戏开发者的需要，使Flash能够成为真正的web游戏机。通过Stage 3D，Flash播放器已经 在web上带来了主机质量的硬件加速图形能力，这比任何平台做的更多。Flash Player 11.2包含鼠标锁定和扩展GPU支持，我们最近发布的路线图演示了即将推出的直接针对游戏开发者的功能，为客户提供新的功能，我们将继续作出投资。正如我们以前所沟通的，Adobe将产品化“Archemy”，这是一个允许用户在Flash运行时中安全的执行高性能的C和C++代码Adobe研究项目。Archemy的商业版本将提供超越实验室版本的更好性能和生产力，它包括完整的调试支持，并减少了75％的代码量。\n还有一件事，我们高兴的宣布，一个专注于游戏开发者的新站点http://gaming.adobe.com已经上线。它有一个使用Flash和AIR的游戏展台，以及更多开发者资源，比如教程和关于如何使用Flash技术开发游戏的信息。去看看吧，然后告诉我们你的想法。游戏！\n","date":"2012-03-18","description":"","lastmod":"2012-03-18T12:29:30Z","slug":"adobes_gaming_efforts","tags":["3d","adobe","air","flashplayer","translate"],"title":"【译】Adobe游戏方面的一周新闻：会议，演示和精挑细选的信息","url":"https://blog.zengrong.net/post/adobes_gaming_efforts/"},{"categories":["technology"],"content":"博客地址：pacman升级到4.0之后的错误处理\n今天升级ArchLinux的时候碰到这个错误：\nerror: failed to prepare transaction (could not satisfy dependencies) :: package-query: requires pacman\u0026lt;3.6\n网上查了一下资料，是ArchLinux的源中pacman均更新到4.0所致，解决办法如下：\n一、更新pacman，修改pacman.conf 若安装过 yaourt ，则删除 yaourt 和 package-query ； 也可以安装 package-query-git ，安装的过程中会自动更新 pacman 到4.0； pacman安装完成后，备份原来的 /etc/pacman.conf ，使用 /etc/pacman.conf.pacnew 替换； 修改 /etc/pacman.conf ，修改的内容如下： 加入原来的archlinuxfr的源； 去掉 SigLevel = Optional TrustAll 的注释； 将原来的 SigLevel = Never 注释； 重新安装yaourt。 二、关于密钥 生成本地加密密钥的步骤比较复杂，具体如下；\n执行 pacman-key --init；\n提示如下：\ngpg: Generating pacman keychain master key... Not enough random bytes available. Please do some other work to give the OS a chance to collect more entropy!(Need 284 more bytes)\n桌面环境下：\n在终端输入 cat /udev/urandom ，然后移动鼠标或者随意按键，获取生成密钥需要的随机数，稍等一会儿密钥就会自动生成。\n非桌面环境下：\n如果是通过SSH连接，或者没有桌面环境，上面的方法可能会不起作用。需要这样处理：\n1#使用yaourt从AUR安装rng-tools包 2yaourt -S rng-tools 3#将随机数超时改为10 4sed -i \u0026#39;s/0/10/\u0026#39; /etc/conf.d/rngd 5#生成密钥 6rngd -f -r /dev/urandom \u0026amp; pacman-key --init 7#生成成功之后，杀死rngd进程 8killall rngd 9#删除rng-tools包 10pacman -Rns rng-tools 三、更新系统 修改gpg服务器地址，这是为了导入密钥能够速度更快； 修改 /etc/pacman.d/gnupg/gpg.conf ，将 keyserver hkp://keys.gnupg.net 改为 keyserver hkp://pgp.mit.edu 。如果后面导入密钥失败，再改为 hkp://pgp.mit.edu:11371 ；\n执行 pacman -Syu 更新系统，若出现下面的错误： error: failed to commit transaction (conflicting files) filesystem: /etc/mtab exists in filesystem Errors occurred, no packages were upgraded.\n则安装下面的包：\n1pacman -S filesystem --force 然后重新更新系统。\n四、参考链接 https://bbs.archlinux.org/viewtopic.php?id=132225 https://bbs.archlinux.org/viewtopic.php?id=128340 https://bbs.archlinux.org/viewtopic.php?pid=1003855 http://www.archlinux.org/news/filesystem-upgrade-manual-intervention-required/ http://bbs.archlinuxcn.org/viewtopic.php?id=533 ","date":"2012-03-15","description":"","lastmod":"2012-03-15T14:53:14Z","slug":"update_pacman_to_v4","tags":["arch","linux"],"title":"pacman升级到4.0之后的错误处理","url":"https://blog.zengrong.net/post/update_pacman_to_v4/"},{"categories":["technology"],"content":"很简单的一个匹配ip地址的测试，死活就是匹配不了：\n1newip=\u0026#39;192.168.1.100\u0026#39; 2if [[ \u0026#34;$newip\u0026#34; =~ \u0026#39;^([0-9]{1,3}\\.){3}[0-9]{1,3}$\u0026#39; ]];then 3 echo \u0026#39;找到了ip地址\u0026#39; 4fi 翻遍了google中的中文资料，都没发现问题。最后终于用英文搜到《Advanced Bash-Scripting Guide》中的一篇介绍Bash版本的文章，其中写道：\nThe =~ Regular Expression match operator no longer requires quoting of the pattern within [[ ... ]].\n天哪，这不坑爹么…… google上那些文章中的代码都是在正则周围加了引号的啊！！！\n把测试条件换成下面这样就成功了。\n1if [[ \u0026#34;$newip\u0026#34; =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]];then 2 .... 3fi 使用变量的时候，是可以使用引号的：\n1newip=\u0026#39;192.168.1.100\u0026#39; 2reg=\u0026#39;^([0-9]{1,3}.){3}[0-9]{1,3}$\u0026#39; 3if [[ \u0026#34;$newip\u0026#34; =~ $reg ]];then 4 echo \u0026#39;找到了ip地址\u0026#39; 5fi 但是，对于 $reg 这个变量，在 [[]] 操作符中，就不能加上双引号，否则也会不匹配。\n旧文章害死人啊\n","date":"2012-03-10","description":"","lastmod":"2012-03-10T16:02:49Z","slug":"bash-reg-fuck-old-article","tags":["bash","linux","regexp"],"title":"Bash的=~正则表达式匹配～旧文章害死人啊","url":"https://blog.zengrong.net/post/bash-reg-fuck-old-article/"},{"categories":["technology"],"content":"本文转自：http://coolshell.cn/articles/4990.html\n前言 你是否觉得自己从学校毕业的时候只做过小玩具一样的程序？走入职场后哪怕没有什么经验也可以把以下这些课外练习走一遍（朋友的抱怨：学校课程总是从理论出发，作业项目都看不出有什么实际作用，不如从工作中的需求出发）\n建议：\n不要乱买书，不要乱追新技术新名词，基础的东西经过很长时间积累而且还会在未来至少10年通用。 回顾一下历史，看看历史上时间线上技术的发展，你才能明白明天会是什么样。 一定要动手，例子不管多么简单，建议至少自己手敲一遍看看是否理解了里头的细枝末节。 一定要学会思考，思考为什么要这样，而不是那样。还要举一反三地思考。 注：你也许会很奇怪为什么下面的东西很偏Unix/Linux，这是因为我觉得Windows下的编程可能会在未来很没有前途，原因如下：\n现在的用户界面几乎被两个东西主宰了，1）Web，2）移动设备iOS或Android。Windows的图形界面不吃香了。 越来越多的企业在用成本低性能高的Linux和各种开源技术来构架其系统，Windows的成本太高了。 微软的东西变得太快了，很不持久，他们完全是在玩弄程序员。详情参见《Windows编程革命史》 所以，我个人认为以后的趋势是前端是Web+移动，后端是Linux+开源。开发这边基本上没Windows什么事。\n启蒙入门 1、 学习一门脚本语言，例如Python/Ruby\n可以让你摆脱对底层语言的恐惧感，脚本语言可以让你很快开发出能用得上的小程序。实践项目:\n处理文本文件，或者csv (关键词 python csv, python open, python sys) 读一个本地文件，逐行处理（例如 word count，或者处理log） 遍历本地文件系统 (sys, os, path)，例如写一个程序统计一个目录下所有文件大小并按各种条件排序并保存结果 跟数据库打交道 (python sqlite)，写一个小脚本统计数据库里条目数量 学会用各种print之类简单粗暴的方式进行调试 学会用Google (phrase, domain, use reader to follow tech blogs) 为什么要学脚本语言，因为他们实在是太方便了，很多时候我们需要写点小工具或是脚本来帮我们解决问题，你就会发现正规的编程语言太难用了。\n2、 用熟一种程序员的编辑器(不是IDE) 和一些基本工具\nVim / Emacs / Notepad++，学会如何配置代码补全，外观，外部命令等。 Source Insight (或 ctag) 使用这些东西不是为了Cool，而是这些编辑器在查看、修改代码/配置文章/日志会更快更有效率。\n3、 熟悉Unix/Linux Shell和常见的命令行\n如果你用windows，至少学会用虚拟机里的linux， vmware player是免费的，装个Ubuntu吧 一定要少用少用图形界面。 学会使用man来查看帮助 文件系统结构和基本操作 ls/chmod/chown/rm/find/ln/cat/mount/mkdir/tar/gzip … 学会使用一些文本操作命令 sed/awk/grep/tail/less/more … 学会使用一些管理命令 ps/top/lsof/netstat/kill/tcpdump/iptables/dd… 了解/etc目录下的各种配置文章，学会查看/var/log下的系统日志，以及/proc下的系统运行信息 了解正则表达式，使用正则表达式来查找文件。 对于程序员来说Unix/Linux比Windows简单多了。（参看我四年前CSDN的博文《其实Unix很简单》）学会使用Unix/Linux你会发现图形界面在某些时候实在是太难用了，相当地相当地降低工作效率。\n4、 学习Web基础（HTML/CSS/JS) + 服务器端技术 (LAMP)\n未来必然是Web的世界，学习WEB基础的最佳网站是W3School。\n学习HTML基本语法 学习CSS如何选中HTML元素并应用一些基本样式（关键词：box model） 学会用 Firefox + Firebug 或 chrome 查看你觉得很炫的网页结构，并动态修改。 学习使用Javascript操纵HTML元件。理解DOM和动态网页（http://oreilly.com/catalog/9780596527402)网上有免费的章节，足够用了。或参看 DOM 。 学会用 Firefox + Firebug 或 chrome 调试Javascript代码（设置断点，查看变量，性能，控制台等） 在一台机器上配置Apache或 Nginx 学习PHP，让后台PHP和前台HTML进行数据交互，对服务器相应浏览器请求形成初步认识。实现一个表单提交和反显的功能。 把PHP连接本地或者远程数据库 MySQL（MySQL 和 SQL现学现用够了） 跟完一个名校的网络编程课程（例如：http://www.stanford.edu/~ouster/cgi-bin/cs142-fall10/index.php) 不要觉得需要多于一学期时间，大学生是全职一学期选3-5门课，你业余时间一定可以跟上 学习一个javascript库（例如jQuery 或 ExtJS）+ Ajax (异步读入一个服务器端图片或者数据库内容）+JSON数据格式。 HTTP: The Definitive Guide 读完前4章你就明白你每天上网用浏览器的时候发生的事情了(proxy, gateway, browsers) 做个小网站（例如：一个小的留言板，支持用户登录，Cookie/Session，增、删、改、查，上传图片附件，分页显示） 买个域名，租个空间，做个自己的网站。 进阶加深 1、 C语言和操作系统调用\n重新学C语言，理解指针和内存模型，用C语言实现一下各种经典的算法和数据结构。推荐《计算机程序设计艺术》、《算法导论》和《编程珠玑》。 学习（麻省理工免费课程）计算机科学和编程导论 学习（麻省理工免费课程）C语言内存管理 学习Unix/Linux系统调用（Unix高级环境编程），，了解系统层面的东西。 用这些系统知识操作一下文件系统，用户（实现一个可以拷贝目录树的小程序） 用fork/wait/waitpid写一个多进程的程序，用pthread写一个多线程带同步或互斥的程序。多进程多进程购票的程序。 用signal/kill/raise/alarm/pause/sigprocmask实现一个多进程间的信号量通信的程序。 学会使用gcc和gdb来编程和调试程序（参看我的《用gdb调试程序》） 学会使用makefile来编译程序。（参看我的《跟我一起写makefile》） IPC和Socket的东西可以放到高级中来实践。 学习Windows SDK编程（Windows程序设计，MFC程序设计） 写一个窗口，了解WinMain/WinProcedure，以及Windows的消息机制。 写一些程序来操作Windows SDK中的资源文件或是各种图形控件，以及作图的编程。 学习如何使用MSDN查看相关的SDK函数，各种WM_消息以及一些例程。 这本书中有很多例程，在实践中请不要照抄，试着自己写一个自己的例程。 不用太多于精通这些东西，因为GUI正在被Web取代，主要是了解一下Windows图形界面的编程。@virushuo说：“我觉得GUI确实不那么热门了，但充分理解GUI工作原理是很重要的。包括移动设备开发，如果没有基础知识仍然很吃力。或者说移动设备开发必须理解GUI工作，或者在win那边学，或者在mac/iOS上学”。 2、学习Java\nJava 的学习主要是看经典的Core Java 《Java核心技术编程》和《Java编程思想》（有两卷，我仅链了第一卷，足够了，因为Java的图形界面了解就可以了） 学习JDK，学会查阅Java API Doc http://download.oracle.com/javase/6/docs/api/ 了解一下Java这种虚拟机语言和C和Python语言在编译和执行上的差别。从C、Java、Python思考一下“跨平台”这种技术。 学会使用IDE Eclipse，使用Eclipse 编译，调试和开发Java程序。 建一个Tomcat的网站，尝试一下JSP/Servlet/JDBC/MySQL的Web开发。把前面所说的那个PHP的小项目试着用JSP和Servlet实现一下。 3、Web的安全与架构\n学习HTML5，网上有很多很多教程，以前酷壳也介绍过很多，我在这里就不罗列了。 学习Web开发的安全问题（参考新浪微博被攻击的这个事，以及Ruby的这篇文章） 学习HTTP Server的rewrite机制，Nginx的反向代理机制，fast-cgi（如：PHP-FPM） 学习Web的静态页面缓存技术。 学习Web的异步工作流处理，数据Cache，数据分区，负载均衡，水平扩展的构架。 实践任务：\n使用HTML5的canvas 制作一些Web动画。 尝试在前面开发过的那个Web应用中进行SQL注入，JS注入，以及XSS攻击。 把前面开发过的那个Web应用改成构造在Nginx + PHP-FPM + 静态页面缓存的网站 4、学习关系型数据库\n你可以安装MSSQLServer或MySQL来学习数据库。 学习教科书里数据库设计的那几个范式，1NF，2NF，3NF，…… 学习数据库的存过，触发器，视图，建索引，游标等。 学习SQL语句，明白表连接的各种概念（参看《SQL Join的图示》） 学习如何优化数据库查询（参看《MySQL的优化》） 实践任务：设计一个论坛的数据库，至少满足3NF，使用SQL语句查询本周，本月的最新文章，评论最多的文章，最活跃用户。 5、一些开发工具\n学会使用SVN或Git来管理程序版本。 学会使用JUnit来对Java进行单元测试。 学习C语言和Java语言的coding standard 或 coding guideline。（我N年前写过一篇关C语言非常简单的文章——《编程修养》，这样的东西你可以上网查一下，一大堆）。 推荐阅读《代码大全》《重构》《代码整洁之道》 高级深入 1、C++ / Java 和面向对象\n我个人以为学好C++，Java也就是举手之劳。但是C++的学习曲线相当的陡。不过，我觉得C++是最需要学好的语言了。参看两篇趣文“C++学习信心图”和“21天学好C++”\n学习（麻省理工免费课程）C++面向对象编程 读我的“如何学好C++”中所推荐的那些书至少两遍以上（如果你对C++的理解能够深入到像我所写的《C++虚函数表解析》或是《C++对象内存存局（上）（下）》，或是《C/C++返回内部静态成员的陷阱》那就非常不错了） 然后反思为什么C++要干成这样，Java则不是？你一定要学会对比C++和Java的不同。比如，Java中的初始化，垃圾回收，接口，异常，虚函数，等等。 实践任务：\n用C++实现一个BigInt，支持128位的整形的加减乘除的操作。 用C++封装一个数据结构的容量，比如hash table。 用C++封装并实现一个智能指针（一定要使用模板）。 《设计模式》必需一读，两遍以上，思考一下，这23个模式的应用场景。主要是两点：1）钟爱组合而不是继承，2）钟爱接口而不是实现。（也推荐《深入浅出设计模式》）\n实践任务：\n使用工厂模式实现一个内存池。 使用策略模式制做一个类其可以把文本文件进行左对齐，右对齐和中对齐。 使用命令模式实现一个命令行计算器，并支持undo和redo。 使用修饰模式实现一个酒店的房间价格订价策略——旺季，服务，VIP、旅行团、等影响价格的因素。 学习STL的用法和其设计概念 -容器，算法，迭代器，函数子。如果可能，请读一下其源码。\n**实践任务：**尝试使用面向对象、STL，设计模式、和WindowsSDK图形编程的各种技能\n做一个贪吃蛇或是俄罗斯方块的游戏。支持不同的级别和难度。 做一个文件浏览器，可以浏览目录下的文件，并可以对不同的文件有不同的操作，文本文件可以打开编辑，执行文件则执行之，mp3或avi文件可以播放，图片文件可以展示图片。 学习C++的一些类库的设计，如：MFC（看看候捷老师的《深入浅出MFC》），Boost, ACE, CPPUnit，STL（STL可能会太难了，但是如果你能了解其中的设计模式和设计那就太好了，如果你能深入到我写的《STL string类的写时拷贝技术》那就非常不错了，ACE需要很强在的系统知识，参见后面的“加强对系统的了解”）\nJava是真正的面向对象的语言，Java的设计模式多得不能再多，也是用来学习面向对象的设计模式的最佳语言了（参看Java中的设计模式）。推荐阅读《Effective Java》 and 《Java解惑》\n学习Java的框架，Java的框架也是多，如Spring, Hibernate，Struts等等，主要是学习Java的设计，如IoC等。\nJava的技术也是烂多，重点学习J2EE架构以及JMS， RMI,等消息传递和远程调用的技术。\n学习使用Java做Web Service（官方教程在这里）\n实践任务： 尝试在Spring或Hibernate框架下构建一个有网络的Web Service的远程调用程序，并可以在两个Service中通过JMS传递消息。\nC++和Java都不是能在短时间内能学好的，C++玩是的深，Java玩的是广，我建议两者选一个。我个人的学习经历是：\n深究C++（我深究C/C++了十来年了） 学习Java的各种设计模式。 2、加强系统了解\n重要阅读下面的几本书：\n《Unix编程艺术》了解Unix系统领域中的设计和开发哲学、思想文化体系、原则与经验。你一定会有一种醍醐灌顶的感觉。 《Unix网络编程卷1，套接字》这是一本看完你就明白网络编程的书。重要注意TCP、UDP，以及多路复用的系统调用select/poll/epoll的差别。 《TCP/IP详解 卷1:协议》-这是一本看完后你就可以当网络黑客的书。了解以太网的的运作原理，了解TCP/IP的协议，运作原理以及如何TCP的调优。 实践任务：\n理解什么是阻塞（同步IO），非阻塞（异步IO），多路复用（select, poll, epoll）的IO技术。 写一个网络聊天程序，有聊天服务器和多个聊天客户端（服务端用UDP对部分或所有的的聊天客户端进Multicast或Broadcast）。 写一个简易的HTTP服务器。 《Unix网络编程卷2，进程间通信》信号量，管道，共享内存，消息等各种IPC……这些技术好像有点老掉牙了，不过还是值得了解。\n实践任务：\n主要实践各种IPC进程序通信的方法。 尝试写一个管道程序，父子进程通过管道交换数据。 尝试写一个共享内存的程序，两个进程通过共享内存交换一个C的结构体数组。 学习《Windows核心编程》一书。把CreateProcess，Windows线程、线程调度、线程同步（Event, 信号量，互斥量）、异步I/O，内存管理，DLL，这几大块搞精通。\n**实践任务：**使用CreateProcess启动一个记事本或IE，并监控该程序的运行。把前面写过的那个简易的HTTP服务用线程池实现一下。写一个DLL的钩子程序监控指定窗口的关闭事件，或是记录某个窗口的按键。\n有了多线程、多进程通信，TCP/IP，套接字，C++和设计模式的基本，你可以研究一下ACE了。使用ACE重写上述的聊天程序和HTTP服务器（带线程池）\n**实践任务：**通过以上的所有知识，尝试\n写一个服务端给客户端传大文件，要求把100M的带宽用到80%以上。（注意，磁盘I/O和网络I/O可能会很有问题，想一想怎么解决，另外，请注意网络传输最大单元MTU） 了解BT下载的工作原理，用多进程的方式模拟BT下载的原理。 3、系统架构\n负载均衡。HASH式的，纯动态式的。（可以到Google学术里搜一些关于负载均衡的文章读读） 多层分布式系统 – 客户端服务结点层、计算结点层、数据cache层，数据层。J2EE是经典的多层结构。 CDN系统 –就近访问，内容边缘化。 P2P式系统，研究一下BT和电驴的算法。比如：DHT算法。 服务器备份，双机备份系统（Live-Standby和Live-Live系统），两台机器如何通过心跳监测对方？集群主结点备份。 虚拟化技术，使用这个技术，可以把操作系统当应用程序一下切换或重新配置和部署。 学习Thrift，二进制的高性能的通讯中间件，支持数据(对象)序列化和多种类型的RPC服务。 学习Hadoop。Hadoop框架中最核心的设计就是：MapReduce和HDFS。MapReduce的思想是由Google的一篇论文所提及而被广为流传的，简单的一句话解释MapReduce就是“任务的分解与结果的汇总”。HDFS是Hadoop分布式文件系统（Hadoop Distributed File System）的缩写，为分布式计算存储提供了底层支持。 了解NoSQL数据库（有人说可能是一个过渡炒作的技术），不过因为超大规模以及高并发的纯动态型网站日渐成为主流，而SNS类网站在数据存取过程中有着实时性等刚性需求，这使得目前NoSQL数据库慢慢成了人们所关注的焦点，并大有成为取代关系型数据库而成为未来主流数据存储模式的趋势。当前NoSQL数据库很多，大部分都是开源的，其中比较知名的有：MemcacheDB、Redis、Tokyo Cabinet(升级版为Kyoto Cabinet)、Flare、MongoDB、CouchDB、Cassandra、Voldemort等。 写了那么多，回顾一下，觉得自己相当的有成就感。希望大家不要吓着，我自己这十来年也在不断地学习，今天我也在学习中，人生本来就是一个不断学习和练级的过程。不过，一定有漏的，也有不对的，还希望大家补充和更正。（我会根据大家的反馈随时更新此文）欢迎大家通过我的微博（@左耳朵耗子）和twitter（@haoel）和我交流。\n—– 更新 2011/07/19 —–\n1）有朋友奇怪为什么我在这篇文章开头说了web+移动，却没有在后面提到iOS/Android的前端开发。因为我心里有一种感觉，移动设备上的UI最终也会被Javascript取代。大家可以用iPhone或Android看看google+，你就会明白了。\n2）有朋友说我这里的东西太多了，不能为了学习而学习，我非常同意。我在文章的前面也说了要思考。另外，千万不要以为我说的这些东西是一些新的技术，这份攻略里95%以上的全是基础。而且都是久经考验的基础技术。即是可以让你一通百通的技术，也是可以让你找到一份不错工作的技术。\n3）有朋友说学这些东西学完都40了，还不如想想怎么去挣钱。我想告诉大家，一是我今年还没有40岁，二是学无止境啊，三是我不觉得挣钱有多难，难的是怎么让你值那么多钱？无论是打工还是创业，是什么东西让你自己的价值，让你公司的价值更值钱？别的地方我不敢说，对于互联网或IT公司来说，技术实力绝对是其中之一。\n4）有朋友说技术都是工具，不应该如此痴迷这句话没有错，有时候我们需要更多的是抬起头来看看技术以外的事情，或者是说我们在作技术的时候不去思考为什么会有这个技术，为什么不是别的，问题不在于技术，问题在于我们死读书，读死书，成了技术的书呆子。\n5）对于NoSQL，最近比较火，但我对其有点保守，所以，我只是说了解就可以。对于Hadoop，我觉得其在分布式系统上有巨大的潜力，所以需要学习。对于关系型数据库，的确是很重要的东西，这点是我的疏忽，在原文里补充。\n","date":"2012-03-09","description":"","lastmod":"2012-03-09T13:33:20Z","slug":"1561","tags":["develop","study"],"title":"【转】程序员技术练级攻略","url":"https://blog.zengrong.net/post/1561/"},{"categories":["technology"],"content":" 英文原文：An open letter to those who want to start programming 中文翻译：伯乐在线 首先，欢迎来到程序员的世界。在这个世界上，不是有很多人想创造软件并解决问题。你是一名hacker，属于那些愿意做一些有挑战性的事情的人。\n“当你不创造东西时，你只会根据自己的感觉而不是能力去看待问题。” –WhyTheLuckyStiff\n对于下面的文字你不必完全接受，所有这些来自一个其貌不扬的程序员。我喜欢把事情做到最好，而不是对原来的东西修修补补。\n仅仅是因为爱好开始做一些创新，这是一个很好的开始！如果你说“我要先学习一下再开始做”那么你永远不会真正开始。每个人都需要从某个地方开始，所以现在打开你的编辑器开始写代码吧。\n下面是一些很重要的建议，虽然有人并不认同，但我保证你看完我的分析以后会站在我这一边。一开始，先不要管算法和数据结构。大多数简单的程序不需要用到算法和数据结构，所以当你真正需要时再去学习。编程一段时间以后，你就会知道在哪些地方用到他们。这时知道算法的名字并了解它们的功能，然后找一些相关的论文去理解算法并动手编程实践。如果没有现成的函数库（其他程序员提供的可重用代码），你用自己喜欢的编程语言来实现它。\n0.选择一门好的编程语言，一门你自认为可以用它快速地做出一些有用东西的语言。\n不要选择C语言作为你第一门编程语言。也许选择C语言会给你一种过时的满足感。虽然它比早期的汇编语言提供了更好语法描述，但是今天C（或者C++）不是能够快速编写软件的语言。毫不偏袒的说，我建议你学习一种动态语言。选择一种你觉得语法（文档）让你使用起来更舒服的编程。为此，你可能需要花费一些时间来在几种不同的语言之间进行比较。这么做目的不是为了让你感觉更好或者让编程更容易。更快地完成自己的作品并且能够看到成果，才是激励你编程的源动力。不要选择一门需要某种重量级的IDE（IDE是一种能够帮助你编写并运行代码的工具）才能轻松编程的语言。你所需要的仅仅是一个文本编辑器。（注：原文并没有标号，是我们加上的，请大家别忘记了哦，我们是从零开始数数的。:) ）\n1. 选择一个好的编辑器。\n编辑器对程序员而言，就是像是弓对弓箭手一样重要。这里有一些推荐的编辑器：\nSublimeText 2 – 如果你刚开始编程，推荐使用SublimeText 2 Emacs – 学习曲线陡峭，快捷键复杂。如果你想定制自己的使用习惯，需要学习Emacs Lisp. Vim – 不仅操作简洁而且它默认包含在linux的发行版中，所以深受大家欢迎。我开始使用了2年Emacs。由于Emacs的快捷键操作过于复杂，我的双手开始经常疼痛，这迫使我转向了Vim阵营。了解Vim的快捷键是非常有必要的。当你在远程的服务器上编辑代码时，你唯一不需要安装的编辑器就是Vim。 注意！Emacs和Vim可能已经成为成为古董了。但是，它们都具备一些现代编辑器多不具备的功能。\n2. 选择一个你可以从中学到东西的操作系统\nWindows不会教会你任何东西。使用Windows你唯一学会的就是打开一个exe（可执行）文件来安装软件和使用它。可能刚开始的时候你会觉得很酷，但是长远来看，如果你立志成为一个web开发人员，可能还是要具备基本的linux知识，尤其是当你要发布程序的时候。Linux也允许你根据自己的要求来定制程序。Macs也很酷，但是我认为你现在经济上还负担不起。\n3. 不要通过复制粘贴来做备份\n一个新手程序员通常会把文件复制粘贴到一个临时目录下面来进行备份。这也许也是他们唯一知道的方法。不要那么做！你需要使用版本控制软件。我强烈推荐使用Git，它是个目前很流行的版本控制工具，而且使用起来非常简单。对一个Git新手，有一个良好的社区和很多参考资源来学习。（除了Git之外，还有mercurial, darcs, fossil等等。但是我还是建议从Git开始，我有很多的理由来支持Git，这里就不多费口舌了。）\n4. 知道去哪里寻求帮助\n参加一个和你相关的（你使用编程工具相关的）程序员社区。比如StackOverflow就是程序员的Facebook。那里没有消息状态和回复，取而代之的是问题和答案。除此之外还要学会使用IRC。IRC是一种老式的聊天室，现在还在被大多数开发人员用来分享技术信息和帮助解决问题。\n5. 培养你的礼仪\n知道什么时候问问题。你遇到的大多数问题都是别人碰到过并且在Internet上能够找到解答的。在IRC或者任何论坛里发问之前，先搜索一下google（或者blekko）来看看是不是有现成的答案。在IRC上问问题需要有耐心。永远记住，人们只是处于好心来免费帮助你。有时你发问以后需要几个小时才能有回复。所以，耐心等待。除此之外，记得问问题的时候要礼貌。世界很小，你怎么对待别人，别人就会怎么对待你。\n6. 结交朋友，技术书籍只能教你解决常见的问题（他们说：书是死的）\n当你在捣鼓一些程序或者从程序的作者那里学习，你能学到一些书本上没有的东西。当你散步的时候，请和遇到的朋友打招呼。你肯定不是唯一的程序员。和其他的程序员一起交朋友一起工作。你会注意到，当一群技术爱好者在一起的时候，不论话题是从什么地方开始，最后总是以技术话题收尾。这是必然的现象。所以你可以尽情参与。在你的黄金年龄努力编程，我能告诉你的是，我在开始6年前寻找朋友并开始讨论编程，从那时起才学到那些书本和文章不会我的东西。所以我总是说，我的编程经验有6年，因为只有那个时候开始，我才开始与人交流并开始感觉真正地开始学习编程。\n7. 参加开源项目\n为开源项目编写代码能带来回报。这不仅仅是帮助别人，你留下的代码会被其他人使用或（可能）改进。当别人给你的代码添加功能或者提出修改意见时，也是在提高你的编程水平。开源软件项目不一定要是一个大工程，编写一个下载youtube视频的小程序也是很有用的。更重要的是，你会惊奇地发现你编写的代码会成为与他人有效沟通的桥梁。\n最后，若干年之后如果本文的内容曾经对你有一点点的帮助，作为回报请写一封类似的信给希望开始编程并不知如何入手的朋友，有可能的话请纠正本文中过时的内容。\n智者说过，掌握某个东西需要10年或10000个小时，也就是汉语中的“十年磨一剑”，所以不用着急。\n","date":"2012-03-09","description":"","lastmod":"2012-03-09T13:30:33Z","slug":"1560","tags":["develop","study"],"title":"【转】写给新手程序员的一封信","url":"https://blog.zengrong.net/post/1560/"},{"categories":["technology"],"content":"Cygwin与MinGW/MSYS，如何选择？\n2012-11-03更新：加入 MSYS 的内容。 2013-10-15更新：修改表格格式，加入介绍链接。 2014-12-17更新：加入 MSYS2 的内容。 什么是Cygwin和MinGW？请看这篇：Msys/MinGW与Cygwin/gcc。\n在无法完全转换到Linux系统的前提下，我一直在 Cygwin 下工作，使用全套的Linux移植工具，学习Bash编程。\n但Cygwin由于工作在模拟模式下，速度较慢，相比而言， MinGW 就要快不少。\n下面是我选择的对比：\n特点 Cygwin MinGW/MSYS MSYS2 是否GNU 否 是 是 更多软件支持？ 支持绝大多数的 GNU 软件 支持常用软件，git、Vim等软件需要独立支持(详细介绍见下方） 支持大多数 GNU 软件 更类Linux？ Cygwin在Windows中就好像Wine在Linux中 实现了Bash等主要的Linux程序 原生64/32bit支持 GCC编译 内含MingGW32交叉编译功能，既支持依赖cygwin1.dll的程序编译，也支持独立的Windows程序编译；可以直接编译Linux下的应用程序 支持独立的Windows程序编译 支持独立的Windows程序编译 中文支持 直接支持中文显示和输入法 需要配置才能支持中文显示和输入，删除一个中文字符需要删除2次 支持中文显示和输入法，中文帮助系统和中文提示（部分软件） 运行速度 慢 快 快 Git for Windows和msysGit是建立在MinGW/MSYS的基础之上的。但如果已经安装过MinGW/MSYS，希望在已有的MinGW/MSYS上获得Git的功能，则会比较麻烦，详见下方的2篇文章：\nInstalling Git under MinGW (+MSys) Using msysGit from MinGW and vice versa 另外，在安装msysGit的时候，要注意cygwin的bin目录不能位于PATH环境变量中。否则msysGit会拒绝安装。\n最终，我还是决定继续Cygwin。git、Vim和中文是主要原因。\n2014-12-17 更新：\n这段时间尝试了 MSYS2 ，发现它已经能满足我上面列出的所有需求。目前我已经删除了 Cygwin ，改用 MSYS2 。\n下面是 MSYS2 的官方介绍：\nMSYS2 is an updated, modern version of MSYS, both of which are Cygwin (POSIX compatibility layer) forks with the aim of better interoperability with native Windows software.\nThe name is a contraction of Minimal SYStem 2, and aims to provide support to facilitate using the bash shell, Autotools, revision control systems and the like for building native Windows applications using MinGW-w64 toolchains.\n目前碰到的问题，是 Python pip 在 MSYS2 下无法运行。但我可以使用 Windows 版本的 Python 。\n贴一张运行截图：\n2015-10-30 更新：\n如果不愿意使用 Cygwin 或者 MinGW 这种 ”重型“ 的 port，又喜欢 unix 下的工具链，那么可以尝试一下下面几个原生移植工具集：\nGNU utilities for Win32 CoreUtils for Windows 或者 完整的 package dd for windows Unix ports - WHICH, TEE \u0026amp; CUT ","date":"2012-03-06","description":"","lastmod":"2015-10-30T02:36:38Z","slug":"cygwin_and_mingw","tags":["cygwin","git","linux","mingw","simulator","terminal","choice"],"title":"Cygwin 与 MinGW/MSYS/MSYS2，如何选择？","url":"https://blog.zengrong.net/post/cygwin_and_mingw/"},{"categories":["technology"],"content":"网上绝大多数教程讲到使用gcc编译编译不依赖cygwin1.dll的应用程序时，需要加入-mno-cygwin参数。但这个方法已经不奏效了。\n在cygwin下使用gcc 4.5.3版本编译时，加入-mno-cygwin参数后，gcc提示下面的信息：\ngcc: The -mno-cygwin flag has been removed; use a mingw-targeted cross-compiler.\n那么，去哪里找这个cross-compiler呢?\n它就在/bin/下，32位的文件名为：i686-pc-cygwin-gcc-4.exe。\n如果需要64位的编译器，可以使用：x86_64-w64-mingw32-gcc.exe。\n","date":"2012-03-05","description":"","lastmod":"2012-03-05T15:00:36Z","slug":"1556","tags":["c","cygwin","gcc"],"title":"使用gcc -mno-cygwin参数编译失败的解决办法","url":"https://blog.zengrong.net/post/1556/"},{"categories":["technology"],"content":"将cygwin升级到1.7以后，使用git gui或者gitk的时候，会出现下面的错误提示：\nApplication initialization failed: no display name and no $DISPLAY environment variable Error in startup script: invalid command name \u0026quot;tk_messageBox\u0026quot; while executing \u0026quot;tk_messageBox -icon error -type ok -title \u0026quot;git-gui: fatal error\u0026quot; -message $err\u0026quot; invoked from within \u0026quot;if {[catch {package require Tcl 8.4} err] || [catch {package require Tk 8.4} err] } { catch {wm withdraw .} tk_messageBox -icon error -typ...\u0026quot; (file \u0026quot;/usr/lib/git-core/git-gui\u0026quot; line 34)\n本来以为是Tk版本太低，可是升级到8.5后问题依旧，无论怎么尝试都找不到原因，使用中文也搜索不到相关资料。或许大家都使用git for windows？\n后来还是在cygwin的官方邮件列表中找到了原因：\ngitk fails to start after updating cygwin git gui and gitk fail to start on cygwin 其实原因很简单：Tk现在需要X支持了。\ncygwin装上X环境之后，运行startx启动X Server，在X环境下运行git gui，熟悉的界面又出来了。（当然，期间错误无数……）\n可是在cygwin下运行X……速度那个慢的哦……还是纯命令行吧\n记录在此，希望对碰到同样问题又不愿意查英文资料的同学有帮助。\n","date":"2012-03-05","description":"","lastmod":"2012-03-05T14:43:35Z","slug":"gitk_error_on_cygwin_1.7","tags":["cygwin","git","linux"],"title":"cygwin升级到1.7后，git gui和gitk报错","url":"https://blog.zengrong.net/post/gitk_error_on_cygwin_1.7/"},{"categories":["technology"],"content":"上次写了 PuTTYcyg的替代者FuTTY 之后，一直使用它。今天由于安装gcc，发现 cygwin 升级到了1.7，自带了 mintty ，这个终端比futty更好用。\nminitty既能支持cygwin，也可以支持MinGW。\n这个终端修改自putty 0.60，我最喜欢的特性就是它能将对终端的配置保存在.minittyrc文件中。而futty、putty和puttycyg都是将配置文件保存在注册表中的。\n特性如下（基于mintty主页介绍翻译）：\nXterm兼容； 原生windows用户界面，选项简洁； 复制粘贴更容易； 支持文本、文件和文件夹的拖放； Ctrl＋单击可以打开文件或URL； 支持多种编码，包含UTF－8； 多字节字符显示（支持中文），支持Windows输入法； 在Vista和windows7上支持窗口透明和玻璃效果； 支持256色； 支持全屏模式； 存储配置文件在文本文件中，不需要注册表； 程序小巧，滚动快速。 ","date":"2012-03-05","description":"","lastmod":"2012-03-05T14:25:59Z","slug":"puttycyg-and-mintty","tags":["cygwin","linux","software","terminal"],"title":"PuTTYcyg的替代者mintty","url":"https://blog.zengrong.net/post/puttycyg-and-mintty/"},{"categories":["technology"],"content":"昨天Adobe发布了 Flash Player 11.2 RC版 ，这意味着伴随着我们十几年的FlashPlayer右键菜单终于可以取消了。\n随之而来的还有鼠标坐标锁定功能，睡眠事件支持等等，详细列表看这里：http://labs.adobe.com/technologies/flashplatformruntimes/flashplayer11-2/。\n下面这个例子演示了右键支持以及到底什么是鼠标坐标锁定。点击鼠标左键全屏观察鼠标锁定效果。右键取消全屏。\n这个例子需要Flash Player 11.2支持，请在这里下载：http://labs.adobe.com/downloads/flashplayer11-2.html\n文档支持：http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/index.html\n泪奔中……\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n源码：\n1package 2{ 3import flash.display.Sprite; 4import flash.display.StageDisplayState; 5import flash.events.MouseEvent; 6import flash.text.TextField; 7 8/** 9 * Flash Player鼠标右键支持 10 * @author zrong(zengrong.net) 11 * 创建日期:2012-02-28 12 */ 13[SWF(width=500,height=400)] 14public class RightClick extends Sprite 15{ 16 public function RightClick() 17 { 18 _traceTF = createTF(); 19 _traceTF.width = this.stage.stageWidth*.5; 20 _traceTF.height = this.stage.stageHeight; 21 _traceTF.text = \u0026#39;左键全屏观察鼠标锁定效果。右键取消全屏。\\n\u0026#39;; 22 this.addChild(_traceTF); 23 24 _locTF = createTF(); 25 _locTF.width = 100; 26 _locTF.height = this.stage.stageHeight; 27 _locTF.x = 400; 28 this.addChild(_locTF); 29 30 this.stage.doubleClickEnabled = true; 31 this.stage.addEventListener(MouseEvent.CLICK, handler_mouseEvent); 32 this.stage.addEventListener(MouseEvent.RIGHT_CLICK, handler_mouseEvent); 33 this.stage.addEventListener(MouseEvent.MOUSE_MOVE, handler_mouseMove); 34 } 35 36 private var _traceTF:TextField; 37 private var _locTF:TextField; 38 39 private function createTF():TextField 40 { 41 var __tf:TextField = new TextField(); 42 __tf.selectable = false; 43 __tf.mouseEnabled = false; 44 __tf.wordWrap = true; 45 return __tf; 46 } 47 48 private function handler_mouseMove($evt:MouseEvent):void 49 { 50 showLoc($evt.movementX, $evt.movementY); 51 } 52 53 private function handler_mouseEvent($evt:MouseEvent):void 54 { 55 showTrace($evt.type); 56 if($evt.type == MouseEvent.CLICK) 57 { 58 this.stage.displayState = StageDisplayState.FULL_SCREEN; 59 this.stage.mouseLock = true; 60 } 61 else 62 { 63 this.stage.displayState = StageDisplayState.NORMAL; 64 } 65 showTrace(\u0026#39;fullscreen:\u0026#39;+this.stage.displayState); 66 } 67 68 private function showTrace($msg:String):void 69 { 70 _traceTF.appendText($msg + \u0026#39;\\n\u0026#39;); 71 _traceTF.scrollV = _traceTF.maxScrollV; 72 } 73 74 private function showLoc($x:Number, $y:Number):void 75 { 76 _locTF.appendText($x+\u0026#39;,\u0026#39;+$y+\u0026#39;\\n\u0026#39;); 77 _locTF.scrollV = _locTF.maxScrollV; 78 } 79} 80} ","date":"2012-02-28","description":"","lastmod":"2012-02-28T15:03:47Z","slug":"flash-context-menu","tags":["as3","flashplayer"],"title":"Flash Player右键菜单终于可以取消了，我等了十几年……","url":"https://blog.zengrong.net/post/flash-context-menu/"},{"categories":["others"],"content":"\n","date":"2012-02-28","description":"","lastmod":"2012-02-28T01:39:59Z","slug":"1547","tags":[],"title":"不同人心中的界面设计","url":"https://blog.zengrong.net/post/1547/"},{"categories":["technology"],"content":"由于安全或者其它原因，我们可能会修改默认的SSH服务端口号，默认情况下，已有的git项目在pull或者push的时候会报错。\n现在假设原来的项目的remote设置为git@domain.com:Projects/p1.git，将服务器SSH默认端口修改为3022后，导致push出错。\n有两个解决办法：\n一、直接修改URL为SSH://开头\n1git remote set-url origin ssh://git@domain.com:3022/~/Projects/p1.git 二、 修改本地配置文件\n1cat\u0026gt;~/.ssh/config 2# 映射一个别名 3host newdomain 4hostname domain.com 5port 3022 6# ctrl+D 修改p1.git项目下的git配置文件\n1git remote set-url origin git@newdomain:Projects/p1.git ","date":"2012-02-27","description":"","lastmod":"2012-02-27T16:33:24Z","slug":"1544","tags":["bash","git"],"title":"修改了SSH默认端口之后，如何配置git？","url":"https://blog.zengrong.net/post/1544/"},{"categories":["technology"],"content":"如果你因为 对Adobe的失望 不愿意再学习Stage3D，而选择了其它语言。那么在转换的这段痛苦的时间里，不如继续利用Stage3D的GPU加速功能开发2D游戏吧！如果你决定这样，那么一定不要错过 Starling。\nStarling 是一个基于Stage3D所开发的一个能够使用GPU来加速的2D Flash 应用程序的ActionScript3 框架。\nFacebook上的Flash版《愤怒的小鸟》，就使用了这个框架。\n这里是ByteArray.org提供的《Introducing Starling》，介绍该框架的使用方法。\n英文版下载：\n1 文件 中文版下载（S_eVent翻译）：\n1 文件 ","date":"2012-02-27","description":"","lastmod":"2012-02-27T13:37:41Z","slug":"starling-reference-cn","tags":["3d","as3","framework"],"title":"Starling框架帮助手册中文版","url":"https://blog.zengrong.net/post/starling-reference-cn/"},{"categories":["others"],"content":"注：下方的所有言论，仅限吐槽。中间使用的所有头衔均系我自封，所有脏话仅限于增强文章的感情色彩（吐槽专用），无任何的针对性，也不会影响我对Adobe这伟大公司的崇敬之情，勿喷勿喷……\n今天一早看到 Adobe放弃Linux平台除Chrome浏览器之外的Flash Player插件 的消息，不禁心头一震，XX一紧。心想你这败家子 放弃Linux上的AIR 也就好了，现在又放弃Linux上的 Flash PLayer？你让我这 在Linux上用Vim写AS3 的家伙怎么玩？你还不如直接把Windows也放弃掉把……\n但接着有看到了 Adobe对于Flex的看法以及对未来Flex的承诺 和Adobe Flash runtimes路线图 两篇文章，心情逐渐放松了点。\n就在我的上篇 吐槽文 中，我写道：\n说不定那天Google想了想，老子也把Chrome Only的Native Client拿来做强插？\n没想到才两天，强插就真的发生了，只是主语变成了Adobe。\n从Adobe对Flex的承诺来看，Adobe不但拿出了足够的诚意。也足够傻B，摘录几点如下：\nFlash Builder取消了设计视图之后，还拿什么来吸引初级开发者？ 唯独不捐献Linux版本的SDK，介是为鸟神马？ 那个该死的RSL……还是不搞了吧？ 但让我这个有十多年历史的Adobe/Macromedia全系列盗版产品使用者和第三方免费Adobe传教士感到欣慰的是，Adobe终于肯着力提升ActionScript的性能了：\nFlash Runtime的多线程支持？ C/C++支持？是基于Alchemy的吧？ byte/short/long的支持？终于不需要Number了…… 该死的右键终于可以完全屏蔽了，苍天啊，大地啊…… 终于能知道睡眠状态什么时候开始和停止了 Adobe，我真不想陪你玩了\n","date":"2012-02-23","description":"","lastmod":"2012-02-23T01:11:07Z","slug":"flash-platform-roadmap","tags":["adobe","as3","flash","flashplayer","flex"],"title":"无力吐槽：观近期Adobe Flash Platform战略有感","url":"https://blog.zengrong.net/post/flash-platform-roadmap/"},{"categories":["news"],"content":"本文转自：7yue's Weblog\n原文地址：http://www.adobe.com/devnet/flashplatform/whitepapers/roadmap.html\nAdobe官方在今天发表了一篇非常非常重要的声明文档，是Adobe Flash runtimes路线图。这篇路线图提供了对于Adobe Flash runtimes的开发路线概览，这篇概览的目的是提供一个清楚的指引，告知外界与社区开发者们Adobe现在是如何思考未来1到2年内核心Flash功能的发展计划。\n下面的中文内容要点全部出自该文档（由7yue翻译并提炼）\n在过去的10多年里，Flash Player和Adobe AIR在互联网领域扮演了及其重要的角色，为多个平台系统，浏览器，桌面和设备提供了一致的用户体验内容。从最初的动画实现，Flash runtimes涉及了几乎所有的互联网媒体平台领域。Adobe坚信Flash在未来是契合于游戏和增值视频市场需求的，因此决定将Flash runtimes的开发专注在这些领域。与此同时，Adobe将针对runtimes进行架构和语言上的大幅改进，确保Flash runtimes将在其专注的领域可以提供未来十年的最优秀的用户体验内容制作的支持。\nFlash runtimes主要指Flash Player与Adobe AIR两个运行时，Flash content主要指使用ActionScript3语言开发打包而成的SWF文件，通过Flash runtimes来展现。\nFlash runtimes core，指Flash runtimes运行时核心功能，包括基础APIs和功能集合。主要的运行时核心在这份官方声明中是指通过Flash Player和Adobe AIR共同分享和具备的功能集合。\n上个世纪90年代引入的Flash Player，发展至今，已经通过浏览器插件的形式实现了以下的功能：\nAnimation,矢量图形，音频（包括mp3），视频，麦克风与摄像头外设访问，底层位图操作，2进制数据sockets，强类型基于类的编程模型，基于硬件加速的2D和3D内容。\nFlash runtimes的战略核心\n伴随浏览器市场的加速竞争，浏览器厂商大幅增加了直接透过浏览器部署丰富图形动画交互的创新能力，这部分曾经正是Flash Player支持的功能。渐渐地，这部分内容已经可以透过HTML5,CSS3和JavaScript直接在浏览器上实现，而Flash Player在Web领域的需求也开始改变。\nAdobe Flash runtimes的战略专注核心将面向2个领域，游戏和增值视频。首要的目标是，提供一流的，引人入胜的，游戏主机级别的图形交互内容和部署一系列的增值视频服务。这一战略计划不代表Flash runtimes不能支持以前的旧有内容，而是在未来，其研发的核心功能将高度优先支持游戏和增值视频领域。\n游戏领域\nFlash runtimes将允许Adobe满足市场的全新功能需求，用户通过Flash制作的游戏将快于其他同类型技术，同时还能使游戏触及最广泛的用户群体。Flash runtimes旨在游戏领域提供以下独一无二的优势：\n通过Flash Player浏览器插件，触及几乎全球的互联网用户，游戏内容通过Adobe AIR也能交付在移动设备之上 完全基于硬件加速的2D和3D渲染支持，提供游戏主机级别的图形渲染质量 丰富的游戏开发者生态系统 强壮的，面向对象的编程语言 世界级的设计师与开发者工作流 Adobe将围绕游戏市场专注打造一套强壮的业务体系，未来包括（但不限于）：\n成熟的游戏开发者生态体系 游戏服务体系 允许游戏开发者在Flash游戏中使用C/C++语言的产品化支持 增值视频领域\nAdobe Flash满足在线视频市场的爆炸性增长，通过提供高质量的视频，安全保护机制，跨浏览器和操作系统的一致性来达到这一要求。Adobe Flash runtimes在在线视频增值内容上会提供以下方面的支持：\n在多个平台上将Adobe视频流媒体服务和内容保护机制引入系统视频文件格式的支持。 支持增值视频内容拥有者的业务需求 同硬件厂商密切合作，提供高质量的整合级别的视频体验服务 通过Flash runtimes打造的一致性播放器，实现多种视频格式编码的跨系统播放支持 支持DRM方案 成熟的全功能视频广告植入及后台分析方案 7yue:接下来的将是全文档最重要的摘要部分...\nFlash runtimes技术路线图\n下面的技术路线图包含了未来2年的Flash runtimes的发布功能。（这些部分会随着日期不断被Adobe更新）\nAdobe AIR\n未来Adobe AIR的开发将专注于全力协作核心Flash Player运行时。当桌面和移动设备特有APIs将被研发时，他们将不会特别针对Adobe AIR而实现（含义就是不会有runtime级别的功能只在AIR上有，而player上没有，SDK特别功能除外）\nFlash Player 11.2\n将在2012年第一季度发布内发布，将针对游戏和视频市场添加关键runtime核心功能：\n某些计划加入的核心功能有：\nMouse-lock支持 鼠标右键与中键事件支持 context menu关联菜单禁用 针对Apple iOS和Google Android平台的Stage3D（通过AIR) Stage3D硬件显卡驱动兼容支持提前至2008年1月 全新的throttling event API （将在Flash页面最小化，暂停，恢复时派发） 多线程视频解码管道 Flash Player \u0026quot;Cyril\u0026quot;\nAdobe将在2012年第2季度发布的一个版本，code name \u0026quot;Cyril\u0026quot;。此发布将继续为游戏添加核心新功能，关键包括以下部分：\n全屏模式键盘输入支持 低延迟音频 Stage3D 纹理素材流式下载 ByteArray的LZMA高压缩支持 Frame label事件体系 Flash Player \u0026quot;Dolores\u0026quot;\nAdobe将在2012年下半年发布的一个版本，code name \u0026quot;Dolores\u0026quot;。此发布将继续为游戏添加核心新功能，关键包括以下部分：\nActionScript workers （允许ActionScript脚本的多个独立线程的并发执行） 高级profiling工具服务 支持更多的独立显卡，驱动兼容检测将提前至2005或2006年 面向iOS发布的ActionScript性能优化 鼠标游离事件体系 Flash Player \u0026quot;Next\u0026quot;\n除去上述一系列的Flash Player发布之外，为了领先未来5到10年的市场，Adobe将持续改进Flash Player底层代码，这部分我们称之为Flash Player \u0026quot;Next\u0026quot;计划。下面是一些关键计划（但不限于此）：\n重构和持续进化Flash runtime核心代码 革新ActionScript VM 更新ActionScript language 这些工作主要目的是为了让Flash runtimes和AS虚拟机显著提升代码执行性能，确保Flash runtimes在下个十年的领先地位。初步实现的计划是2013年。\nActionScript \u0026quot;Next\u0026quot;\n从2006年引入ActionScript3后，一定范围内借助AS3实现的应用和内容在这几年发生了显著的变化。Adobe认为是时候认真修订和掌握其未来的变化了。从语言设计的角度，Adobe将使用下列的假设（设想）来指引下一代的ActionScript开发。\n针对长期生产力的增长需求（包括强壮性，模块化和维护程度）和短期生产力的收益特征（快速敏捷开发）来考虑改进语言设计\n高性能的要求 硬件实现的要求 首先，Adobe计划在短期内实现性能的显著提升，并以持续的性能提升为长期目标。所以，性能，性能，性能！将作为我们改进ActionScript的最高优先级目标。其次，Adobe将通过简化语言，提升工具支持，加速bug预防下手为开发者提高生产效率。最后，降低语言不必要的复杂程度。\n以下几点是关于下一代ActionScript语言和虚拟机的有限披露：\nStringent static typing as default, with optional dynamic typing，默认非常严格的静态类型和可供选择的动态类型。大多数程序的大多数扩展都受益于语言的静态类型。然而，AS3倾向于在所有场景使用动态类型，其实绝对严格的静态类型则是首选。这一点将被修正。动态类型仍然存在，但是不再是ActionScript的默认设定，它将在需要使用的时候被明确指出。 Type inference，类型推断。类型声明将只在特定环境下变的需要。编译器将自动推测合适的类型声明，并且针对整个程序进行静态类型化，即使开发者没有声明任何类型。 Hardware-oriented numeric types，面向硬件的数字类型。例如，int,uint,float,float4,byte,short,long等（具体类型集合在讨论中）。现在AS3整形数值可以溢出到浮点类型，这一点将会被改动，未来数字的操作将不再数值运算时改变其类型，这能够大幅度降低运算的复杂度，提升runtime性能。 以上几点仅是我们正在考虑的部分，未来我们将会公布更多如何改动语言和VM的细节。\n兼容性\n下一个版本的ActionScript将是AS3的一个变革，但是一些场景下将无法完全兼容AS3.Adobe期望从AS3的移植成本可以降到最低，少于当年从AS2迁移到AS3的成本。\n平台支持\nFlash runtimes将在未来的平台支持上包含如下部分：\nPC领域——Apple OS X，Adobe承诺将使Flash runtimes支持Apple的最新OS X （Mac OS X的升级），AIR应用也能够分发到桌面或Mac App store。 Microsoft Windows——Adobe承诺继续提供Flash runtimes对于微软Windows操作系统的支持。 **Window8——**微软目前正在开发Windows8，下一代微软Windows操作系统，这个系统包括一系列不同的配置，包括desktop和metro，以及芯片架构的不同支持（x86/64和ARM）。Adobe目前正在与微软紧密协作完成最终的Flash runtimes的Windows8配置支持。具体细节将会在最终确定配置类型后公布。 **Linux——**Adobe目前与Google正在紧密合作开发一款独立的，现代化的API，用来host浏览器的插件。这称为PPAPI，code-name是\u0026quot;Pepper\u0026quot;，目的在于提供一个介于插件和浏览器之间的抽象层，用来区分浏览器和系统级别的功能实现。更多信息，你可以参考Pepper API在http://code.google.com/ppapi/。Google未来将通过Pepper来实现Chrome全平台版本对于Flash Player的支持，包括Linux系统。 **Mobile——**Flash Player 11.1将是移动设备上基于浏览器的最后一个版本的Flash Player 插件。Adobe将持续投资开发者将Flash内容通过AIR打包分发到移动设备的方案。 TV...... 全英文版本的白皮书在此下载：flash-runtimes-roadmap.pdf\n","date":"2012-02-23","description":"","lastmod":"2012-02-23T00:45:30Z","slug":"1535","tags":["adobe","air","flash","flashplayer"],"title":"【转】Adobe Flash runtimes路线图","url":"https://blog.zengrong.net/post/1535/"},{"categories":["news"],"content":"本文转自：Pilihou's Blog\n原文地址：http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/flex/pdfs/flex_roadmap.pdf\nAdobe认为当前Flex是企业和数据中心应用程序开发的最好解决方案，把Flex捐献给社区推动性的开源项目是确保Flex今后几年的持续发展和成功。在这种新的模式下，社区将负责Flex SDK的维护和开发，而Adobe将继续开发工具和运行时。本文档提供了Adobe对于Flex捐献给开源项目的看法，以及对未来Flex的承诺。\nAdobe Flex Adobe Flex是为开发针对桌面浏览器中的Adobe Flash Player，桌面Adobe AIR，以及移动设备Adobe AIR的富互联应用（RIAs）的软件开发工具包（SDK）。SDK提供了一个以ActionScript 3编写的统称为Flex框架的组件库，ActionScript 3是Flash平台的面向对象编程语言。除了编译器和调试工具之外，SDK定义了MXML，一个声明性的XML语法，开发人员可以使用它来创建和维护复杂的用户界面。\nAdobe Flex历史\n在21世纪初，Web应用程序开发人员挣扎在缺乏跨浏览器支持的JavaScript和CSS样式，文本传输协议的无状态性质，HTML的限制之中。公司和开发人员在寻找方法通过提供更好的用户体验来改善客户互动。Macromedia帮助给web应用程序定义了富互联网应用程序的概念，结合桌面软件应用程序的功能和Web应用程序的广泛范围和低成本部署以达到更直观、灵活、高效的用户体验（http://www.adobe.com/products/central/whitepaper/central_wp.pdf）。RIAs也减少了页面刷新的次数，需要较少的带宽，为客户端提供了独一无二的卸载应用程序进程的功能。在当时，RIAs针对浏览器中的Macromedia Flash Player，Macromedia也展望跨多设备部署RIAs，包括RIAs中即时通信，以及提供分布式数据存储。\n在浏览器中部署Flash Player，而不是浏览器本身，消除了web应用程序开发人员正面经历的问题，同时提高了应用程序的设计和可用性并创建了丰富的用户体验。虽然开发者使用Flash MX来构建RIAs，其非常有限的代码编辑器和时间轴的存在使用Flash MX让开发人员感到尴尬并打乱了他们标准的工作流程。\n由Macromedia在2004年发布的Flex 1.0，是一种基于服务器的产品，旨在提供企业级应用程序开发的方式来部署web应用程序到几乎无所不在的Flash Player，同时使他们能够使用他们习惯的传统应用程序开发工作流程。Flex 2，由Adobe收购Macromedia后发布，不再是一个基于服务器的产品。Flex Builder 2是一个基于Eclips构建的综合开发环境（IDE），允许开发人员在本地编写和编译应用程序，然后将其部署到Web服务器。此时ActionScript 3也被发布了。\nAdobe继续开发Flex，发布了Flex 3.0，Flex 4.0，Flex 4.5，以及最近在2011年11月30日发布Flex 4.6。多年来，新的组件被添加到Flex框架。组件皮肤结构被重新设计。IDE重命名为Flash Builder，新的代码重构和格式化功能，新的调试功能，增加的profiler和网络监控器提高了开发人员的生产力。4.5和4.6版本增加了对移动应用程序开发的支持。\n对于Flex使用情况\nFlex是利用针对Flash平台的优势用于建立各种动态，数据驱动的应用程序。虽然Flex可以，并已用于建立规模较小的应用程序，但在更大的规模和企业级应用程序开发中，它特别受欢迎。在企业级中，已使用Flex建立了医院和医疗信息系统，”财富”100强企业的报告应用程序，银行的财富和资产管理应用程序，教育管理者的黑板应用程序，军事信息发布系统，以及递送订阅和优质视频应用程序。\n虽然许多企业和数据中心的应用程序提供了一个高水平用户交互性和丰富的用户体验，但企业级应用程序的要求通常是与那些规模较小或消费类的应用程序不同。Flex提供了为企业级开发者一套独特的好处使它选择合适的工具构建企业级应用程序，如：\n在长时间高压力条件下能维持最佳性能 如JEE、PHP、.NET等其他技术都能与Flex应用程序集成 Flex框架允许为应用程序进行国际化和本土化以便部署在多语言环境下。 整个开发过程中的测试，可以使用为Flex应用程序专门设计的许多工具来执行单元，集成，功能，和其他类型的测试。 辅助性工具提供自动构建和持续集成Flex应用程序。 个别类与Flex项目易于跟踪和保持在一个版本控制系统中有关。 Flex与HTML\n在Adobe，我们相信，在开发大型，复杂，丰富的应用程序方面，使用Flex具有重大的和可持续的优势，超过任何其他的替代技术。HTML创新正在以非常快的步伐进行，并且在为项目选择适当的技术时，这将为开发者提供更多的选择。Adobe正致力于帮助推进HTML，并相信它是交互开发的一种重要技术。但是我们也承认， Flex有优势，这可能使它几年来在企业空间是最佳解决方案。Flex企业级应用程序开发的几个独特的优势包括：\nFlex提供了完整的功能水平一样的跨多个平台，浏览器和设备支持。 Flex组件集和编程模型使得它在构建复杂的用户界面方面非常高效。 ActionScript 3是一个适用于大型应用程序开发的成熟的面向对象编程语言。 辅助性工具提供了代码编辑，调试，分析和测试方面的生产力开发环境。 认识到这些优势，Adobe公司将继续致力于使所有现有的和新的Flex项目的成功。\nAdobe对Flex的愿景 作为把Flex捐献给社区推动性的开源项目，Adobe将做初步的技术贡献，也将继续提供一个全职Flex SDK工程师组成的团队来为Apache项目做贡献。虽然这种新模式下，Adobe将提供比以往更少的工程师资源，但我们正与Flex开发社区一起来增加积极贡献者和资源的总数。\nFlex SDK捐献给Apache\nAdobe是当前处在捐献Flex SDK核心（包括先进的数据可视化组件），自动化库，AIR SDK的二进制文件，文档，以及Apache的Flex项目的规范的进程中。我们也将帮助完成，但尚未发布的Spark组件包括ViewStack，Accordion，DateField，DataChooser，以及一个增强的DataGrid 。\nFalcon编译器捐献给Apache\nFalcon 1.0是ActionScriptr的下一代编译器，目前处于开发中。在编译器ActionScript部分完成之后，Adobe将捐献Falcon 1.0给Apache Flex项目，我们预计将在2012年第四季度交付。\nFalcon JS编译器捐献给Apache\nFalcon JS是一个实验性的ActionScript 3编译器，它针对JavaScript而不是Flash运行时。Adobe将在它完成和捐献了Falcon 1.0之后捐献Falcon JS源代码的原型给Apache Flex项目。\n测试框架捐献给Apache\nAdobe将捐献Mustella，Adobe开发和用于测试Flex SDK的功能测试框架。Adobe正在把它捐献给Apache Flex项目，以帮助Flex成功的，持续的，高质量的开发。\nBlazeDS捐献给Apache\nAdobe打算捐献BlazeDS代码给Apache以促进开放开发。我们目前正在做捐献的细节，很可能我们会捐献BlazeDS给现有的Apache Flex项目。BlazeDS代码已经基于GNU通用公共许可证第三版，发布在adobe.com/go/blazeds_source。\nBlazeDS提供了基于Java的服务器端的远程处理和网络通讯技术允许Flex客户端与服务器交换二进制类型的数据，并接收来自其他客户端和服务器的通知。消息服务还允许Flex应用程序与其他非Flex，JMS可用的应用程序交换信息。\n工程师帮助Apache Flex项目\nAdobe将提供一个Flex SDK工程师团队，他们将帮助和全职支持Apache Flex项目，包括为新功能做贡献。这些工程师也将提供以前发布的Adobe Flex版本的错误和对主要问题的安全修复。\n相关技术的捐献\nAdobe将不会捐献Linux AIR的SDK，或LCCS给Apache。Adobe仍然在调查TLF、BlazeDS.NET、Gravity、FXG、Squiggles、以及OSMF捐献的可行性。\n支持合约\nAdobe正全面致力于为Adobe Flex SDK发布版支持合约的履行，并将继续提供新的支持合约五年。Adobe不计划为Apache发布的Flex SDK版本提供支持。我们预计，第三方将为Apache Flex发布版本的提供支持。\n社区支持\nFlex开发者社区是一个积极的，具有丰富知识和经验的高度熟练的充满活力的开发者社区。Flex采用开源模式，意味着Flex的未来在这个社区的手中。Adobe希望帮助维持这个重要的社区，并打算与整个社区共同努力，超额地过渡到一个Apache项目。\nAdobe将为360|FLEX （360flex.com）赞助和提供资金，首映发布会上捐给Flex。Adobe也将继续支持它的所有用户群体。将没有变化，包括应用/再应用。Adobe专业社区（ACP）的计划将持续2012年6月1日。用户群体正在由Adobe组织的（北美日期，欧洲日期）多城市国际旅游扮演着重要的角色。Adobe传教士预计在这些会议中促进开放，与尽可能多的Flex开发人员面对面的讨论。\nFlex的Adobe运行时支持 Flash Player 11.2和Adobe AIR 3.2，预计在2012年第一季度发布，将与使用Adobe Flex 4.6构建的应用程序一起进行测试。Adobe将在未来五年测试Flash Player和AIR的版本与Adobe Flex 4.6 SDK保持向后兼容性。\n虽然Adobe将确保Adobe Flex SDK 4.6及之前的版本将在Flash Player和AIR未来版本中被支持，但测试未来版本的Apache Flex SDK与已发布的Adobe运行时确保兼容性和正常工作，这将是Apache Flex项目的责任。\n在过去，功能被专门添加到Flash Player和AIR，用于支持Flex应用程序的需求。展望未来，功能将添加到运行时来支持Adobe版的Flash平台。Apache Flex项目可以选择利用这些功能；然而，新功能将不再专门添加到运行时以支持Apache项目的成果。\nApache发布版Flex的RSLs\n现在， Flex是一个社区推动的项目，不再占用也不实用为Adobe签署所产生的Apache Flex RSLs。这意味使用Apache Flex发布版，框架RSLs将不被Flash Player全局地缓存,而是在web浏览器的每个域名中。\nAdobe Flash平台 Adobe正在积极开发的Flash运行时和下一个版本的Flash Professional。Flash平台技术将继续发展重点在游戏和优质视频。关于Flash平台未来创新的细节的Flash Player线路白皮书不久将公布。在它发布之后你将可以在http://www.adobe.com/devnet/flashplatform/whitepapers/阅读。\n桌面浏览器版Adobe Flash Player\nAdobe继续致力于开发桌面浏览器版的Adobe Flash Player。最新的主要版本Flash Player 11引入了几十个新功能。其中桌面版Adobe Flash Player未来版本的Adobe Flash播放器特色计划是并发，鼠标锁支持，遥测技术支持，音频API改进，以及ActionScript 3中新的数据类型。\n在Flash Player中辅助功能支持将继续被维持但预计在这段时间没有新的辅助功能或增强。\n桌面版Adobe AIR\nAdobe正致力于继续支持运行在桌面上的Adobe AIR应用程序。我们正在积极努力地为下一个版本的桌面版Adobe AIR工作。最近的主要版本Adobe AIR 3.0添加支持绑定运行时（captive runtimes）以允许开发人员在不要求用户必须先安装AIR运行时打包和分发Adobe AIR应用程序。据预计，Adobe AIR 3.2即将发布。并发在随后发布的Adobe AIR有针对性地交付使用。\nAIR 3.x SDK目前在Linux上不可用并且未来在Linux平台上也没有支持计划。\n手机版Adobe AIR\nAdobe当前致力于支持和开发手机版Adobe AIR以及未来设备和操作系统更新包括iOS 5，iPhone 5，iPad 3，及Android 4（Ice Cream Sandwich）。AIR将继续作为独立的应用程序用Flash内容和应用程序部署的交付机制。RIM计划将继续支持和开发黑莓PlayBook操作系统版AIR。\nWindows 8版Adobe Flash Player和AIR\n至于未来操作系统的支持信息，请参阅将于短期内公布的Flash Player路线白皮书。\nApache Flex版Adobe工具支持 Flash Builder\n继续开发Flash Builder。Adobe计划保持Flex项目的支持，在更新到Flash Builder 4.X，包括额外的工作以确保基于Apache Flex的SDK可以在Flash Builder中工作。如果当一个来自开源项目的合适的编译器可用时，Adobe将评估整合Apache衍生Falcon MXML编译器到Flash Builder中。为了更好地支持未来Apache衍生Flex SDKs，”设计”视图、数据中心开发工具，以及Flash Catalyst工作流将从Flash Builder更新的4.x版本移除。\nFlash Catalyst\nFlash Catalyst CS5.5是最后一个版本的Flash Catalyst。在这个时候，它不会被更新与Flex SDK 4.6或任意Apache Flex版本协作，也不会被包括在未来版本的Adobe Creative Suite软件产品中。\nFlash Professional\nAdobe将不会改变它的承诺以及发布Flash Professional的计划。接下来的两个版本，代号为Reuben和Hellcat，目前在正轨中。Reuben计划在2012年发布。没有计划在Flash Professional中支持Apache Flex SDK。\n总结 Adobe认为当前Flex是企业和数据中心应用程序开发的最好解决方案，把Flex捐献给社区推动性的开源项目是确保Flex今后几年的持续发展和成功。我们当前处在捐献Flex SDK核心，自动化库，AIR SDK的二进制文件，文档给Apache Flex项目的的进程中，我们也捐献Falcon， Falcon JS，Mustella，以及BlazeDS。\n除了这些捐款，Adobe将提供一个全职的Flex SDK工程师团队，他们将帮助和支持Apache Flex项目。这些Adobe工程师们将直接与高熟练度的Flex开发社区一起来维护、支持以及改进Flex SDK。我们将继续致力于使所有现有的和新的Flex项目成功。\n欲了解更多信息 Flash Player路线白皮书（即将推出）\nBlazeDS源代码 Apache Flex项目 Flex用户组2012之旅 – 北美 Flex用户组2012之旅 – 欧洲 ","date":"2012-02-23","description":"","lastmod":"2012-02-23T00:38:50Z","slug":"1533","tags":["adobe","air","flashbuilder","flashplayer","flex"],"title":"【转】Adobe对于Flex的看法以及对未来Flex的承诺","url":"https://blog.zengrong.net/post/1533/"},{"categories":["others"],"content":"看了《移动开发中HTML5能否替代本地程序？》一文后，突然想吐槽一下，就有了下面的文字：\n说到头来就是一句中国古话：中不偏，庸不宜；或者是一句大俗话：因地制宜。\n原文作者说的好，就像B/S替代C/S一样，转型过程中总会有阵痛和选择。实际上过了这么多年，BS也没有完全替代CS。QQ大家还是愿意用客户端的（即使有WebQQ和3Q大战）；Word大家还是愿意用M$的（即使有Google Docs）。但也就像网页游戏逐渐火爆一样，这个趋势总是在不断的发展。\nHTML5实在是炒得太凶了，比小米还凶，凶的像个外强中干银样蜡枪头，不知道能坚挺到哪天？Adobe太怂，放着98%市占率的 Flash Player 不知道怎么玩，结果被一群不懂程序不懂Flash不懂AS不懂JS不懂CSS只懂HTML的傻B喷到放弃。但只要Google不倒，HTML和JS都会一直发展。说不定那天Google想了想，老子也把 Chrome Only 的 Native Client 拿来做强插？大家要陪着一起玩不？\n是高级（脚本）语言重要，还是底层（类C类、汇编、函数式）语言重要？现在没人能说的清。但看看iOS和Android的开发语言把，无一例外地最终都支持C/C++（微软是异类，不去谈），就知道最重要的是什么。\n其实有时候，选择的只是一个选择而已。\n","date":"2012-02-21","description":"","lastmod":"2012-02-21T14:48:24Z","slug":"html5-instead-of-native","tags":[],"title":"”移动开发中HTML5能否替代本地程序“之吐槽文","url":"https://blog.zengrong.net/post/html5-instead-of-native/"},{"categories":["impressions"],"content":" 本文转自：http://blog.chinaunix.net/uid-11143705-id-3077709.html 英文原文地址：What Level Programmer Are You? zrong: 看了原文后，感觉译文有一些问题。原文的译者似乎并不是IT界内人士，或者对编程了解的不够，一些专业术语和流程并没有给出清晰的翻译，我按照原文对人称部分做了修改，加入了一些注释和名字解释的链接，并重新排版。\n感谢原文译者的工作。\n原文的作者后来又写了一篇”非常短“的文章用另一种方式讨论程序员水平分级，可以看看：Or to put the programming levels another way...\n导读：近日，whattofix.com刊登了一篇Daniel Markham的文章《What Level Programmer Are You?》，文内将参差不齐的程序员按照技术水平分为从“读写”到“神级”，共十一个阶段，以帮助广大程序员找到自身定位并从中发现自己发展方向。以下是文章摘要：\n随着技术发展，编程悄然融入了我们的生活。我们已然离不开那些程序和编程语言。很多人都在不同程度地谈论着如何编程，也诞生出很多编程语言排行，那么程序员到底应该如何分级呢？\n首先要明白什么是程序员。设计自己的Apache Web服务器的家伙？制作一个复杂Excel的家伙？还是能自己开发RPG的家伙？Minecraft的玩家？这些都属于程序员的哪一个级别呢？当我们说：“每个人都需要了解编程”，这句话意味着什么？\n所以，我们需要制定一个程序员的等级，以便让程序员们了解自己所处的环境和发展方向。\n**Level 1，阅读和键入——**他们了解设置Web服务器的方法，在普通情况下能够通过配置方式获取工作所需要的东西。他们有使用文本编辑器和文件系统的基础知识，还拥有移动和操作文件的能力，如ZIP和FTP。也能在结构中使用抽象符号、解析方式以及系统技能。\n**Level 2，脚本小子——**能够编写线性脚本告诉电脑要做什么，能够学习和使用自己的符号。如果有足够的时间和书面指导，他们可以使用bash或VB编写一些简单脚本，为自己做些实用的事情。\n**Level 3，图书管理员——**这些人了解库和API，并有能力学习更多。他们知道不管使用什么脚本语言都需要连接库。如果他们遇到问题，可以通过阅读使用手册或是在网上搜索库来解决。在工作中他能够发现或是开发自己的符号（使用struct编写函数或是声明）。大多数系统程序员都处在这个等级阶段，Excel的初级宏系统用户也同样处在这个阶段。\n**Level 4，“对象.方法”实践者——**刚开始接触耦合性与内核，他们能编写自己的对象化脚本（有公有/私有成员和方法）。这些人能玩转技巧，了解库和构建类。如果不使用类，他们也能够创建自己的模块或是代码文件。高级脚本用户可远不止这样，有一些Excel技巧经验丰富的股票经纪人，他们使用自己VBA作为基础代码，而没有意识到自己处于这个级别。\n**Level 5，多范式实践者——**一旦上升到这个级别，你将会遇到各种各样的麻烦。常见编程下一阶段就是能够使用多重框架。你可以编写C#，也知道如何创建表格和编写SQL语句；可以编写C，同时也会使用JavaScript和HTML。这两个技能并不代表你就是大师，只是要学会在理解同一个问题上，需要通过多种不同的方式来找寻答案。\n**Level 6，初级构架师——**到了这个阶段，你才算是真正的从事编程工作。你每天的工作就是要深入了解库和API。你并不需要记住这些，但要了解用不同的模式去实现自己的目标。想要在Win32中绘制？可能需要一些GDI资源句柄，你知道句柄是在系统表某处的一个UInt32 hash——常见的Win32范例。想在自己的库中添加jQuery？新库里有很多格式，你应该知道怎么做并且能解释其工作方式。与此同时，也要开始学习如何重构自己的代码并突显亮点，增强代码的质量。\n**Level 7，资深构架师——**你的时间是用来创建自己的API、平台或是库。也许这是一个大项目的必需品，也许只是你想把自己的学识推广出去。你开发的系统为用户解决实际问题，所面对的受众群是Level 5程序员。在这个阶段需要深入OOAD，模板和实践。我们大多数人在刚开始都会搞得一团糟，不能理解怎么用更加简单的方法将复杂事情简易化。这个阶段需要历经很长时间，需要不断修正错误，开发高质量的系统并创建更复杂、灵活性高的库。当你从外面接受几个月的特定培训回来，肯定希望团队其他人都能理解你做了什么。\n**Level 8，诠释者——**在某些时候，你希望并意识到大量复杂的结构和构架可以使用DSL（这里还有一篇更详细的—— 開發人員的逆襲：Domain-Specific Languages）进行更好的描述，你的受众群体是Level 4程序员。是的，你的代码的复杂性增大，但是代码本身却更短了，而且DSL的开放性让你可以将代码用于所有语言。总之，你将成为程序员使用脚本语言方面的传道士。通过简单的使用脚本和向导将你的系统开放给初学者。你可以分别学习这些技能，但大多数情况是在遇到了非常复杂的结构后，你希望使用DSL来解决，一遍又一遍地分析和处理。很快就能看到最初的模式：利用自己编写的DSL能够很好的理解问题。\n**Level 9，函数式编程者——**你的技能开始加分了。你开始学习如何函数式编程，受众群是Level 3的程序员。函数式编程是必不可少的，帮助你轻松地脱离DSL，并能够大幅度减少代码数量来解决问题，同时拥有较高的扩展性，某些功能语言在系统运行时可以热交换。就像所有高级的技能一样，在某一时刻，你会有涅磐重生之感。到现在，你已经接收了前面的好消息。所以，现在说点坏事休息一下吧。函数式变成可以减少Bug，而且多数时候速度更快，但很难编码。程序员的脑海里需要时刻记忆很多东西——你必须要记住繁琐和复杂的符号。但是，最后你会意识到函数式编程只是道路上的一个阶段，就像OOAD一样。\n**Level 10，面向语言设计师——**你查看所有新创建的编程语言程序。惟一的区别是新语言应该做什么。你了解如何开发面向对象语言，语言功能和脚本语言，知道每一个语言在何时何地的用途。受众群是Level 2程序员。你对每一个项目都会问：“这个项目我们需要什么样的语言？”在项目完结后你将会得到一个很好的工具，既解决了问题并易于理解还扩展了入门级程序员。你可以很容易的实现大多数项目而不需要面向语言设计师。但那一天或许是一个传奇。\n**神级，计算机科学家——**要么就是编程的顶峰，要么就不存在。这个取决于自己的观点。这是超酷的编程科学，你的受众群体是所有人。但重点不是在于做人们想要的东西，而是促进学术的发展。因为大多数编程能力水平都是基于人而言，人们居住在世界各地，也是必要条件之一。如何简单的在两个世界之间来回穿梭？这是个问题。\n你在这里可能受到很多启发，例如，首先学习函数式编程再学习OOP。你也可以开发自己的编译器，这不是大学的乐趣吗？或是程序集，或是真正理解集理论，或是如何开发适用于数据库与编程数据的结构，或是开发你的第一个编程工具。\n这些是我所看到的大多数程序员所遵循的道路。\n","date":"2012-02-21","description":"","lastmod":"2012-02-21T02:26:30Z","slug":"what-level-programmer-are-you","tags":["develop","study"],"title":"【转】程序员水平分级，你属于哪一级？","url":"https://blog.zengrong.net/post/what-level-programmer-are-you/"},{"categories":["others"],"content":"Linux系统的乐趣就是——自己动手，乐在其中\n使用Linux系统的人，通常比较偏执，这种偏执可能表现在如下n个方面：\n1.修改狂\n这类人喜欢把系统改得面目全非（比如去掉任务栏，找一个拉风的终端程序做桌面），以满足自己的变态破坏欲和掌控欲，而这种面目全非的修改，在Windows下面是不可能实现的。\n2.怀疑狂\n怀疑任何不开源的软件会偷偷或者光明正大的在自己辛辛苦苦花钱买来的PC上玩3Q大战。\n3.键盘党\n这种人天生讨厌鼠标。而类Unix系统的特点，就是用命令行完成绝大多数的事。折腾Linux，大多数时间应该都是在折腾各种命令行程序。几次行云流水的击键换来与windows上单击无数次鼠标相同的效果，折腾就在所难免\n4.盗版党\n实在不愿意继续承受盗版软件迫害，举起大旗奔向Linux。\n…………\nn-1.自由门\n任何与自由和解放扯上关系的言论都会被河蟹，我就不信这条能显示出来？\nn.装B的小白\n刚学会ls就打开一个全黑的终端装B的家伙是大有人在的\n顺便说一句：ubuntu只是Linux众多发行版中较为出名和被小众们接受的一个，是比较像Windows的一个，不一定是最好的一个，更不能完整的体现Linux那帅呆了的特质。\n本文是我在知乎上的一个回答：http://www.zhihu.com/question/19911489/answer/13327953\n","date":"2012-02-19","description":"","lastmod":"2012-02-19T11:14:03Z","slug":"why-ubuntu","tags":["linux","ubuntu"],"title":"为什么那么多人喜欢折腾ubuntu？","url":"https://blog.zengrong.net/post/why-ubuntu/"},{"categories":[],"content":"这是一篇转载文章。\nPeter Norvig (Copyright 2001) 原文网址 中译文出处 为何大家如此匆忙? 走进任何一家书店，你会看到书架上一排不见尽头的放着如 《7天自学Java语言》以及几天或者几小时学会Windows, 因特网或者Visual Basic这类书。我在Amazon网上书店用以下的方式进行高级搜索:\n出版年份: 1992以后　书名包括：“天” 和 “学习” 或“自学”\n得到了268条搜索结果，其中前78条都是计算机书（第79条是30天学会孟加拉语）。\n我用“小时”代替“天”作为关键字，得到了神奇般类似的结果：这次有253本书，前77本是计算机书，第78本是24小时自学语法和写作风格。排名前200的书中有96%是计算机书。\n由此可见，人们要不就是急着想学会计算机，要不就是计算机相比于其他事情太容易学会了。比如说把，没有书是写在几天弹奏贝多芬或几天学会量子物理，甚至也没有几天学会帮小狗打扮这样的书。\n让我们分析一下三天学会Pascal语言 [英文网页] 这样的标题表达了什么意思：\n1. 学会: 在三天内，你没有时间去写几个有意义的程序，或者从成功和失败中学到东西。你也没时间跟有经验的程序员一起工作，所以也无法了解在真正编程是什么样子。简短的说，就学会而言，时间显然不够。所以这些书只是浮于表面的熟悉，而不是深刻的理解。如同Alexander Pope 所说，一知半解是危险的。\n2. Pascal 语言: 三天内你可能学会Pasacl语言的语法（如果你已经掌握一个类似的编程语言），但你无法学会如何合理运用这些语法。简言之，如果你是个Basic程序员，你可以用Pascal 语言写出类似Basic风格的程序，但你学不到Pascal语言的优点（还有缺点）到底在哪。重点是什么呢?\nAlan Perlis曾说: “如果编程语言不能影响你的编程思维，那就不值得去学.”\n另一个可能是，你必须学会一点点Pascal语言（或是像VB语言、Javascript等），因为你需要跟现成的工具组合完成特定的工作。不过这个时候，你实际上学的不是怎么写程序，而是要学着如何完成工作。\n3. 三天 不幸的是三天根本不够；下面的章节会告诉你为什么\n十年学会程序设计 研究者 Hayes, Bloom 的研究表明，在几乎所有的各种领域，大约要十年才能培养出专业技能。这些领域包括下西洋棋、音乐作曲、绘画、钢琴、游泳、网球，及神经心理学和数学拓扑学。似乎没有真正的捷径－－即便是莫扎特在四岁就展露出音乐天才，在他写出世界级的音乐之前仍然用了超过十三年的时间。\n再看另一种类型的领域。披头士乐团似乎是在１９６４年的Ed Sullivan剧场表演突然地火起来并成为第一乐队的。但其实他们从 1957年开始，就在利物浦、汉堡等地的小型俱乐部表演。虽然他们很早就显现强大的吸引力，但他们决定性的成功作品 Sgt Pepper 也到1967年才发行。Samuel Johnson则认为或许还不止十年才行，他说：任何领域的卓越成就都必须用一生的努力才能取得;稍微低一点的代价都是换不到的。Chaucer 则感叹道：“生命如此短促，学习技艺却要这么地长”\n以下是我在编程上成功的秘诀:\n对编程产生感兴趣并因为乐趣而写程序。确信你自始至终都能乐在其中，这样你才愿意将十年光阴投入编程事业. 与其他程序员交流；阅读别人的代码。这比任何书任何培训都重要。 不断地编写。最好的学习方法是在实践中学习。从技术角度说，”在特定领域的个人最高效率并不因为经验够多就会自动获得；但若有意识的通过努力去提升经验，个人效率会变高”（第336页）而“高效的学习一般需要明确的任务和因人而异的适当难度，以及及时的反馈和重复或者修正错误的机会”（20～21页）Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life（实践中认知：心智、数学与日常文化）是这个观点的一本有趣参考书籍。 如果你愿意，你可以去读四年大学（或再读研究生）。这可以让你满足一些工作的学历要求，同时也可让你对这个领域有更深的认识。但如你不喜欢上学，你也能（得有牺牲）通过工作获得类似的经验。无论如何，只读书是不够的。《New Hacker’s Dictionary》的作者Eric Raymond 曾经说过:“计算机的教育无法让人成为编程的专家，正如研究画笔与颜料不能让人成为专业画家一样.” 一个在我所有招聘过的人中属于最优秀之一的程序员只有高中毕业，但他写出很多很棒的程序，他甚至有自己的新闻组。他获得的股票期权使得他可以拥有自己的午夜酒吧. 跟其他程序员一起完成项目。在一些项目中成为最好的程序员；在一些中则充当最差的一个。当你是最佳的，你要测试自己领导项目的能力，并以你的能力鼓励他人。当你是最差的，要看看高手做些什么，他们不喜欢做什么(因为他们会叫你去帮他们做). 接手别的程序员完成项目。全心投入并理解别人的程序。当原作者不在的时候，看看在理解与修改时有什么要注意的。想想如何设计你的程序使得后来维护的人容易上手。 至少学会六门编程语言。一种要支持类/对象(class abstractions)的语言,如Java或C++; 一种函数式(functional abstraction)语言, 如 LISP 或 ML;一种支持语法抽象(syntactic abstraction) 的语言 如 LISP;一种声明式语言, 如Prolog或 C++模版; 一种支持协同式(coroutines)编程,如 Icon 或 Scheme; 还有一种支持并行(parallelism)的语言, 如 Sisal. 记住在 “计算机科学” 中包括”计算机”这个词。要知道你的计算机执行一条指令需要多久，到内存中取一个字需要多久(缓存是否击中),到磁盘读取连续的字需要多久，而磁盘的定位又需要多久. (解答见文末) 进行语言标准化的工作。可以像是由ANSI　C++委员会，或由你自己的团队，来决定你们的编码风格，譬如说缩排是2或4个空格。不管怎样，你都能学到别人到底喜欢什么，对语言的感受有多深，甚至能了解到一点他们为什么有这样的感觉。 并具备良好的判断力，也别老纠缠在语言标准化上. 谈了上面所有的想法后，我不禁要问究竟能从书上学到多少。在第一个孩子出生前，我读完了所有的“怎样…”的书，仍觉得自己是个一无所知的（照顾孩子的）菜鸟。30个月后，第二个孩子出世，我要重回这些书好好复习么?\n不!取而代之的是，我开始相信自己的个人经验。这些难得的经验，比专家写的几千页手册还要有用，而且让我重新找到了自信.\nFred Brooks (译注: 《人月神话》作者) 在他的文章没有银弹中指出，发掘卓越软体设计者的三部曲：\n尽早尽可能地以系统化的方式发掘最佳设计人员。 给有潜力者指派生涯规划师，并谨慎地规划他们的职业生涯。 提供机会给正在成长的程序员，让他们能相互影响，彼此激励。 这里假定了某些人已具备成为卓越设计师的必要潜能；工作只是诱导他们前进。 Alan Perlis 说得更简洁了，你可以教任何人学雕塑，但对米开朗基罗而言，要教他的反倒是有哪些事不要做, 卓越的程序员也一样。\n所以，尽管买那些 Java书吧！你或许能从中找到点有用的，但是在24小时，几天或者几个月中，这些都不会改变你的人生，你也不能掌握一个真正的程序员应该具备的真正的综合的技能。\n参考文献 Bloom, Benjamin (ed.) Developing Talent in Young People, Ballantine, 1985. Brooks, Fred, No Silver Bullets, IEEE Computer, vol. 20, no. 4, 1987, p. 10-19. Hayes, John R., Complete Problem Solver Lawrence Erlbaum, 1989. Lave, Jean, Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life, Cambridge University Press, 1988. 解答 各种操作的时间，以2001年夏季，典型配置的 1GHz 个人计算机为标准：\n命令 速度 执行单一指令 1 纳秒 从L1 高速缓存取一个字 2 纳秒 从内存取一个字 10 纳秒 从磁盘取连续存放的一个字 200 纳秒 磁盘寻址并取字 8 毫秒 附录 I: 语言的选择 好几个人问过我一开始应该先学哪个计算机编程语言，这个问题没有唯一的答案，不过选择的时候可以从以下的几个方面考虑：\n朋友在用的. 当人们问我:”我该用什么操作系统的时候”，我通常的回答是：“用你朋友用的”.这样的好处是从朋友那儿学习可以弥补复杂的操作系统差异或者编程语言差异(给你造成的困惑)。这里也要考虑你未来的朋友：如果你一直使用的话，开发社区会是你的朋友。你选择了一个具有有巨大的增长的开发社区还是一个小的快消失的开发社区的语言?它有相关的书，网站和论坛可以获取解答么?你喜欢那些论坛上的人么? 保持简单.诸如C++和Java是为那些关心代码执行效率的有经验的大型团队的开发人员设计的。因此这些语言中有些为这些特殊场合设计的部件。你只是关心编程而不需要关心复杂情况。你需要一个为新学编程的人设计的容易学习和理解的语言。 实践.什么是学习钢琴的好方法呢? 是一边听音一边弹奏的“交互式”的方法呢，还是全听完整首歌然后再弹奏的那种“批处理”方式呢? 很显然,交互学习的方式能够让学钢琴变得简单–这也适用于编程.选取一种交互式的编程语言并且坚持使用. 基于以上的这些标准，我对于第一次接触编程的人推荐 Python 或 Scheme.但是情况各有不同，或许也有其他的选择. 如果你不满10岁，你可能会喜欢Alice或者Squeak(年龄大的人或许也喜欢这些). 重要的是在选择后,立即开始学习和使用.\n附录II: 书和其他资源: 很多人问我该从什么书或者什么网页开始看起。我重申一句：“仅仅看书是不够的”，不过我也推荐一下的一些：\nScheme: 计算机程序的结构和释义 (Abelson \u0026amp; Sussman) 可能是计算机科学最好的导论了, 他还告诉程序员怎么理解计算机科学,你可以访问这本书的 在线视频讲座 和 全文在线 .这本书也很有挑战性，可能会排除掉一些可能在其他领域成功的人. Scheme: 怎样设计程序(Felleisen 等) 是讲解怎样设计优雅的函数式语言的最好的书之一. Python: Python 编程，面向计算机科学的导论(Zelle) 是用Python介绍计算机科学的好书. Python: 一些关于Python的 入门教程 可以在 Python.org 上找到. Oz: 计算机编程的概念，技术和模型(Van Roy \u0026amp; Haridi) 可以视为第一本书的现代版.他是关于编程的一些总揽，包含了比第一本书更加广泛也更加容易阅读和理解的领域。这本书使用了一个不太为人所知的编程语言叫Oz, 不过这个可以作为学习其他编程语言的一个基础。 注: T. Capey 指出，在Amazon 的 问题彻底解决者的页面上购买了这本书的人还买了: “21天学孟加拉语” 和 “自学语法和写作风格”这两本书，我估计大部分是我这个页面带过去的用户.\nPeter Norvig (Copyright 2001) Eric You XU 翻译，2007年4月 ","date":"2012-02-14","description":"","lastmod":"2012-02-14T13:41:57Z","slug":"21-days","tags":[],"title":"【转】十年学会程序设计","url":"https://blog.zengrong.net/21-days/"},{"categories":["technology"],"content":" 2012-03-12更新\n判断DNSPOD API返回值为空的情况，这种情况每天会出现2－3次，这种情况不退出程序，而是继续等待； 2012-03-11更新 调整了部分正则表达式的写法； 增加了错误判断功能。 2012-03-10 更新\n支持多域名多记录。 多域名使用数组定义，每个元素代表一个域名的一组要更新的子记录； 仅支持A记录的更新，因为其他类型的记录可能有重名的情况出现； 每一组记录，以空格分隔域名和子域名； 第一个空格前为主域名，后面用空格分离多个子域名； 如果使用泛域名，必须用 \\* 转义。 以下为要更新的域名范例：\n1domainList[0]=\u0026#39;domain1.com \\* @\u0026#39; 2domainList[1]=\u0026#39;domain2.com www subdomain1 subdomain2\u0026#39; 使用方法：\n配置domainList数组，详细内容见上方； 配置delay的值，多长时间检测一次ip变化，单位是秒。 我一直想买购买 github 的付费服务，但7美刀的价格算起来比一个月的电费（下面有计算）还要贵许多。因此决定使用DDNS来实现自己的git服务器。\n设备和网络的情况是这样的：\n10M电信光纤网络； 使用2008年购买的HP mini-note 2133做服务器； 操作系统为ArchLinux。 服务器情况如下：\nVIA C7-M 1.5G单核； DDR2 667 2G内存； 5400RPM 160GB硬盘； 实测功耗20W，月耗电约7-8元，比7美刀要便宜多了； 不但可以架设git服务器，还可以客串http、ftp、mysql……。 电信的光纤网络也是使用PPPOE拨号，但电信似乎有连接过长时间后自动断线的功能，所以希望用拨号后不下线的方式实现”伪固定IP“是不现实的，必须使用DDNS来实现解析。\n原来我一直用老牌的 花生壳 来实现DDNS，花生壳集成在TP－link路由器中，使用起来还算方便。但花生壳的web界面的体验和功能比 DNSPod差了太多，且有各种各样的限制，让人非常的不爽。\nDNSPod提供了全功能的API ，完全可以使用它来实现DDNS。于是乎，我使用初学的bash，来实现了这样一个DDNS客户端。\n我实现的思路是这样的：\n访问DNSPod的API，获得要实现DDNS的域名的当前对应ip； 访问一个外部网站，获取本机的外网ip； 比较外网ip和DNSPod保存的ip，若不相同，就更新DNSPod上的ip地址； 每5分钟重复2－3。 注意事项：\nDNSPod在检测到5分钟内登录错误30次后，会禁用该账号的登录。所以必须在每次调用API的时候，都检测返回代码； 不要过多的调用DNSPod的API，尽量使用缓存。 将dnspodsh复制到/usr/bin下，并在/etc/rc.local中加入下面一行即可实现你自己的免费DDNS：\n1/usr/bin/dnspodsh dnspod_name dnspod_passwaord \u0026amp;\u0026gt;/dev/null 如果遇到错误，可以查看/var/log/dnspodsh.log。\ndnspodsh下载：https://gist.github.com/1822396\n","date":"2012-02-14","description":"","lastmod":"2012-02-14T01:40:33Z","slug":"dnspod-client-write-by-bash","tags":["bash"],"title":"在bash中使用DNSPod的API接口实现DDNS客户端","url":"https://blog.zengrong.net/post/dnspod-client-write-by-bash/"},{"categories":["technology"],"content":"最近成功忽悠 Allen 加入Vim使用者行列，颇有些自得。共享两套给程序员看的Vim键盘清单吧：\n来自于http://michael.peopleofhonoronly.com/vim/：\n1 文件 下面这个来自于http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html：\n1 文件 ","date":"2012-02-11","description":"","lastmod":"2012-02-11T14:40:04Z","slug":"vim-cheat-sheet","tags":["vim"],"title":"Vim Cheat Sheet两套","url":"https://blog.zengrong.net/post/vim-cheat-sheet/"},{"categories":["technology"],"content":"Bash数组操作教程\n一、定义数组 1. 使用[]操作符 1names[0]=\u0026#39;zrong\u0026#39; 2names[1]=\u0026#39;jacky\u0026#39; 2. 使用()直接赋值 1names=(\u0026#39;zrong\u0026#39; \u0026#39;jacky\u0026#39;) 2# 或 3names=([0]=\u0026#39;zrong\u0026#39; [1]=\u0026#39;jacky\u0026#39;) 3. 使用declare -a定义数组。这种方法可以将一个空的变量定义成数组类型。 1declare -a names 4. 从文件中读取数组 1cat\u0026gt;names.txt 2zrong 3jacky 4sweet 5Ctrl+C 6# 将每一行读取为数组的一个元素 7names=(`cat \u0026#39;names.txt\u0026#39;`) 二、读取数组 1. 数组取值 和ActionScript一样，Bash也使用[]操作符和基于0的下标来取值：\n1adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39;) 2echo ${adobe[0]} 3# 打印 4# Flash 2. 数组长度（元素个数） 使用“@”这个特殊的下标，可以将数组扩展成列表，然后就可以使用bash中的获取变量长度的操作符“#”来获取数组中元素的个数了：\n1adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39;) 2echo ${#adobe[@]} 3# 打印 4# 3 有趣的是，没有定义的数组下标，并不会占用数组中元素的个数：\n1adobe=([0]=\u0026#39;Flash\u0026#39; [2]=\u0026#39;Flex\u0026#39; [4]=\u0026#39;Photoshop\u0026#39;) 2echo ${#adobe[@]} 3# 打印 4# 3 3. 获取数组的一部分 命令替换对数组也是有效的，可以使用偏移操作符来取得数组的一部分：\n1adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39; \u0026#39;Dreamweaver\u0026#39; \u0026#39;Premiere\u0026#39;) 2echo ${adobe[@]:1:3} 3# 打印 4# Flex Photoshop Dreamweaver 5echo ${adobe[@]:3} 6# 打印 7# Dreamweaver Premiere 4. 连接两个数组 1adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39; \u0026#39;Dreamweaver\u0026#39; \u0026#39;Premiere\u0026#39;) 2adobe2=(\u0026#39;Fireworks\u0026#39; \u0026#39;Illustrator\u0026#39;) 3adobe3=(${adobe[@]} ${adobe2[@]}) 4echo ${#adobe3[@]} 5# 打印 6# 7 三、修改数组 1. 替换数组元素 模式操作符对数组也是有效的，可以使用它来替换数组中的元素\n1adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39; \u0026#39;Dreamweaver\u0026#39; \u0026#39;Premiere\u0026#39;) 2echo ${adobe[@]/Flash/FlashCS5} 3# 打印 4# 注意，打印的结果是一个字符串列表，不是数组 5# FlashCS5 Flex Photoshop Dreamweaver Premiere 6# 7# 将替换后的值重新保存成数组 8adobe=(${adobe[@]/Flash/FlashCS5}) 2. 删除数组元素 使用命令替换并重新赋值的方式删除数组元素\n1# 删除Photoshop元素 2adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39; \u0026#39;Dreamweaver\u0026#39; \u0026#39;Premiere\u0026#39;) 3adobe=(${adobe[@]:0:2} ${adobe[@]:3}) 4echo ${adobe[@]} 5# 打印 6# Flash Flex Dreamweaver Premiere 使用模式操作符删除数组元素\n1adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39; \u0026#39;Dreamweaver\u0026#39; \u0026#39;Premiere\u0026#39;) 2adobe=(${adobe[@]/Photoshop/}) 3echo ${adobe[@]} 4# 打印 5# Flash Flex Dreamweaver Premiere 四、循环 使用for in循环读取数组：\n1adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39; \u0026#39;Dreamweaver\u0026#39; \u0026#39;Premiere\u0026#39;) 2for item in ${adobe[@]};do 3\techo $item 4done 5# 打印 6# Flash 7# Flex 8# Photoshop 9# Dreamweaver 10# Premiere 使用for循环读取数组：\n1adobe=(\u0026#39;Flash\u0026#39; \u0026#39;Flex\u0026#39; \u0026#39;Photoshop\u0026#39; \u0026#39;Dreamweaver\u0026#39; \u0026#39;Premiere\u0026#39;) 2len=${#adobe[@]} 3for ((i=0;i\u0026lt;$len;i++));do 4\techo ${adobe[$i]} 5done 6# 打印 7# Flash 8# Flex 9# Photoshop 10# Dreamweaver 11# Premiere ","date":"2012-02-09","description":"","lastmod":"2012-02-09T15:01:33Z","slug":"bash_array","tags":["bash","linux","shell"],"title":"Bash数组操作教程","url":"https://blog.zengrong.net/post/bash_array/"},{"categories":["technology"],"content":"一直不明白既然目前多数CPU内部都使用 Little Endian 字节序，为什么 ActionScript3 中的 ByteArray 默认使用 Big Endian 字节序。\n看了下面的文章，大致明白了：\n或许ActionScript是为了兼容TCP/IP协议的Big Endian字节序？\n下文转自： http://linux.chinaunix.net/techdoc/desktop/2009/06/26/1120396.shtml\nBig Endian和Little Endian的区别 故事的起源“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，其中一个皇帝送了命，另一个丢了王位。我们一般将endian翻译成“字节序”，将big endian和little endian称作“大尾”和“小尾”。\n1. 什么是Big Endian和Little Endian？ 在设计计算机系统的时候，有两种处理内存中数据的方法。一种叫为little-endian，存放在内存中最低位的数值是来自数据的最右边部分（也就是数据的最低位部分）。\n比如某些文件需要在不同平台处理，或者通过Socket通信。这方面我们可以借助ntohl(), ntohs(), htonl(), and htons()函数进行格式转换，\n个人补充：一个操作数作htonl或ntohl结果不一定相同，当机器字节序跟网络字节序刚好是仅仅big endian和little endian的区别时是相同的。\n2. 如何理解Big Endian和Little Endian 举个例子： int a = 1;\na这个数本身的16进制表示是0x00 00 00 01 在内存中怎么存储呢?如果你的CPU是intel x86架构的(基本上就是通常我们说的奔腾cpu),那么就是 0x01 0x00 0x00 0x00 , 这也就是所谓的little-endian, 低字节存放在内存的低位。\n如果你的CPU是老式AMD系列的(很老很老的那种，因为最新的AMD系列已经是x86架构了),它的字节序就是big-endian, 其内存存储就是 0x00 0x00 0x00 0x01 ,在内存中从高字节开始存放。\n现在世界上绝大多数的CPU都是little-endian。\n3. 了解big-endian和little-endian有什么作用？ 一个重要的作用就是了解在网络上不同的机器间的数据如何传输。\n假设我们在网络上有两台机器A和B, 其中A为little-endian, B为big-endian。机器A要传输上面的整数a给机器B，如何传输呢？ 过程是这样的：\n机器A先把a在内存中的四个字节0x01 0x00 0x00 0x00 转化为网络字节序 0x00 0x00 0x00 0x01，然后一个字节一个字节(从0x00到0x01)喂到网络上去。然后机器B从网络上一个字节一个字节地取出四个字节 0x00 0x00 0x00 0x01 后又会转化为本地字节序 0x00 0x00 0x00 0x01 后放入内存。因而B正确地得到了来自A的数据a\n如果数据缺少在网络上的字节序转换的话，情况会怎样呢？\n机器A先把a由在内存的四个字节 0x01 0x00 0x00 0x00 一个字节一个字节地喂到网络上，然后机器B从网络上一个字节一个字节地收到 0x01 0x00 0x00 0x00 并放入到内存中, B认为他收到了 0x01000000 ,也就是十进制数 1677216 ,这显然是错误的。\n4. 如何判断系统是Big Endian还是Little Endian？ 在 /usr/include/ 中（包括子目录）查找字符串 BYTE_ORDER (或 _BYTE_ORDER , __BYTE_ORDER )，确定其值。这个值一般在 endian.h 或 machine/endian.h 文件中可以找到,有时在 feature.h 中，不同的操作系统可能有所不同。一般来说，Little Endian 系统 BYTE_ORDER (或 _BYTE_ORDER , __BYTE_ORDER )为1234，Big Endian 系统为 4321 。大部分用户的操作系统（如windows, FreeBsd,Linux）是Little Endian的。少部分，如MAC OS ,是Big Endian的。本质上说，Little Endian还是Big Endian与操作系统和芯片类型都有关系。\n","date":"2012-02-08","description":"","lastmod":"2012-02-08T02:56:28Z","slug":"why-actionsript-use-big-endian","tags":["bitandbyte","actionscript"],"title":"ActionScript为什么默认使用Big Endian字节序？","url":"https://blog.zengrong.net/post/why-actionsript-use-big-endian/"},{"categories":["impressions"],"content":"淇淇（看着面前广告牌上的美女）：爸爸，这个女生穿的裙子是粉红色的，我喜欢粉红色。\n我：哦……\n淇淇：爸爸，你肯定喜欢蓝色。\n我：为神马捏？\n淇淇：因为你是男生，所以你喜欢男色……\n我（默念……其实我喜欢女色）：……\n","date":"2012-02-05","description":"","lastmod":"2012-02-05T12:54:54Z","slug":"1515","tags":["qiqi"],"title":"淇淇语录之男色","url":"https://blog.zengrong.net/post/1515/"},{"categories":["technology"],"content":"注意：本文参考An introduction to AS3 Signals写成，但不是翻译，有增删改。\n第一部分：Signals框架介绍（一）基本用法 第二部分：Signals框架介绍（二）高级事件 原生事件 为了达到替换AS3事件机制的目的，Signals当然包含了对AS3原生事件的支持。这依赖于NativeSignal类。\n下面的范例演示了如何在Stage上添加单击事件。由于一看就懂，这里就不废话解释了。\n[NativeSignalSample.as]\n1package 2{ 3import flash.display.Sprite; 4import flash.events.MouseEvent; 5import org.osflash.signals.natives.NativeSignal; 6 7[SWF(width=500,height=300,frameRate=30,backgroundColor=0xFFFFFF)] 8/** 9 * 测试原生事件 10 * @author zrong(zengrong.net) 11 */ 12public class NativeSignalSample extends Sprite 13{ 14\tpublic function NativeSignalSample() 15\t{ 16\t_click = new NativeSignal(this.stage, MouseEvent.CLICK, MouseEvent); 17\t_click.add(handler_click); 18\t//测试只发生一次的点击事件 19\t//_click.addOnce(handler_click); 20\t} 21 22\tprivate var _click:NativeSignal; 23 24\tprivate function handler_click($evt:MouseEvent):void 25\t{ 26\ttrace(\u0026#39;currentTarget：\u0026#39;,$evt.currentTarget); 27\ttrace(\u0026#39;target：\u0026#39;,$evt.target); 28\t} 29} 30} 其它资料 An introduction to AS3 Signals（本文的参考） AS3 Signals Tutorial（一个非常棒的视频教程） 更多的文章，与其它框架(PureMVC,Robotlegs,Flex)的连用 Signals的作者关于AS3事件机制的3篇吐槽文 之一,之二,之三 ","date":"2012-01-23","description":"","lastmod":"2012-01-23T15:16:15Z","slug":"as3_signal_intro3","tags":["as3","framework","signals","design-pattern"],"title":"Signals框架介绍（三）原生事件","url":"https://blog.zengrong.net/post/as3_signal_intro3/"},{"categories":["technology"],"content":"注意：本文参考An introduction to AS3 Signals写成，但不是翻译，有增删改。\n第二部分：Signals框架介绍（二）高级事件 第三部分：Signals框架介绍（三）原生事件 Signals是一个基于AS3的轻量级事件框架，它被设计用来代替AS3内置的Event框架。\n为什么我们要放弃Event框架，改用Signals呢？原因主要有以下几点：\n使用AS3内置的Event框架，必须继承EventDispatcher或者自行实现IEventDispatcher，而Signals使用组合，可以避免继承； 使用AS3内置框架在事件中实现值的传递，必须自定义事件类才可以。而Signals可以方便的实现多个强类型值的传递； Signals不使用字符串来区分不同的事件，而是使用实例; Signals的速度更快，据说是AS3内置事件机制的4倍。 来看个简单的例子吧，这个例子中的闹钟会叫我们起床。此范例部分源码来自An introduction to AS3 Signals\n基本用法 先看看闹钟类AlarmClock.as\n1package 2{ 3import org.osflash.signals.Signal; 4 5/** 6* Signals闹钟范例 7* @author Aiden Tailor(http://www.developria.com/2010/10/an-introduction-to-as3-signals.html) 8* @author zrong(zengrong.net) 9*/ 10 11public class AlarmClock 12{ 13\tpublic function AlarmClock() 14\t{ 15\talarm = new Signal(); 16\t} 17 18\t/** 19\t* 声明一个Signal实例用来发送Signal 20\t*/ 21\tpublic var alarm:Signal; 22 23\tpublic function ring():void 24\t{ 25\t//发布闹钟的响铃事件 26\talarm.dispatch(); 27\t} 28} 29} 再来看看起床类Wakeup.as\n1package 2{ 3import flash.display.Sprite; 4 5[SWF(width=500,height=300,frameRate=30,backgroundColor=0xFFFFFF)] 6/** 7 * 测试闹钟 8 * @author Aiden Tailor(http://www.developria.com/2010/10/an-introduction-to-as3-signals.html) 9 * @author zrong(zengrong.net) 10 */ 11public class Wakeup extends Sprite 12{ 13\tpublic function Wakeup() 14\t{ 15\t_alarm = new AlarmClock(); 16 17\t//向alarm注册事件，这里不需要提供任何的事件名称。因为alarm就是一个确定的Signal实例。 18\t_alarm.alarm.add(handler_ring); 19 20\t//使用addOnce，让alarm在事件收到一次后立即取消 21\t//_alarm.alarm.addOnce(handler_ring); 22 23\t_alarm.ring(); 24\t_alarm.ring(); 25\t} 26 27\tprivate var _alarm:AlarmClock; 28 29\t/** 30\t* 由于我们并没有在事件中传递参数，因此这里的方法也不需要提供任何参数。 31\t*/ 32\tprivate function handler_ring():void 33\t{ 34\ttrace(\u0026#39;起床了！！！\u0026#39;); 35\t} 36} 37} 在进行事件注册的时候，我们使用的是AlarmClock的public属性alarm。在面向对象编程中，这种方式是不可取的。但我们现在只是个范例对么？不要要求那么严格好不好？\n当然，如果你知道在AS3中，使用get方法会比直接使用public属性的性能低不少，或许你也会让这种“不规范”的编程方法延续下去？\n###取消事件注册\n如果运行这个范例，应该可以看到2条“起床了！！！”的trace信息。然后，取消addOnce一行的注释，并注释掉add那行，让它们变成这样：\n1public function Wakeup() 2{ 3\t_alarm = new AlarmClock(); 4 5\t//向alarm注册事件，这里不需要提供任何的事件名称。因为alarm就是一个确定的Signal实例。 6\t//_alarm.alarm.add(handler_ring); 7 8\t//使用addOnce，让alarm在事件收到一次后立即取消 9\t_alarm.alarm.addOnce(handler_ring); 10 11\t_alarm.ring(); 12\t_alarm.ring(); 13} 再次运行范例，应该只能看到1条trace信息了。你可以把这种现象理解成原来的AS事件机制中的removeEventDispatcher被自动执行了。\n当然，手动移除也是可以的。如果你使用的是add方法注册，可以使用下面的方法来移除事件的注册。\n1_alarm.alarm.remove(handler_ring); 2_alarm.alarm.removeAll(); ###传递参数\n看看传递参数有多简单吧……修改AlarmClock.as中的相关代码：\n1public function AlarmClock() 2{ 3\t//让我们传递一个AS3的标准Date对象吧，让那个懒虫知道现在几点 4\talarm = new Signal(Date); 5} 6 7public function ring():void 8{ 9\t//把当前的时间发出去 10\talarm.dispatch(new Date()); 11} 再修改Wakeup.as中的相关代码：\n1private function handler_ring($date:Date):void 2{ 3\ttrace(\u0026#39;起床了！！！也不看看几点了：\u0026#39;+$date.toString()); 4} 就这样，不需要该死的继承Event和重写clone()了，是不是很清净呢？\n","date":"2012-01-23","description":"","lastmod":"2012-01-23T14:28:32Z","slug":"as3_signal_intro1","tags":["as3","framework","signals","design-pattern"],"title":"Signals框架介绍（一）基本用法","url":"https://blog.zengrong.net/post/as3_signal_intro1/"},{"categories":["technology"],"content":"PuTTYcyg的替代者mintty\n上次写了PuTTYcyg的替代者FuTTY之后，一直使用它。今天由于安装gcc，发现cygwin升级到了1.7，自带了mintty，这个终端比futty更好用。\nminitty既能支持cygwin，也可以支持MinGW。\n这个终端修改自putty 0.60，我最喜欢的特性就是它能将对终端的配置保存在.minittyrc文件中。而futty、putty和puttycyg都是将配置文件保存在注册表中的。\n特性如下（基于mintty主页介绍翻译）：\nXterm兼容； 原生windows用户界面，选项简洁； 复制粘贴更容易； 支持文本、文件和文件夹的拖放； Ctrl＋单击可以打开文件或URL； 支持多种编码，包含UTF－8； 多字节字符显示（支持中文），支持Windows输入法； 在Vista和windows7上支持窗口透明和玻璃效果； 支持256色； 支持全屏模式； 存储配置文件在文本文件中，不需要注册表； 程序小巧，滚动快速。 ","date":"2012-01-21","description":"","lastmod":"2012-01-21T15:55:23Z","slug":"mintty_is_replacer_of_puttycyg","tags":["cygwin","linux","software","terminal"],"title":"PuTTYcyg的替代者FuTTY","url":"https://blog.zengrong.net/post/mintty_is_replacer_of_puttycyg/"},{"categories":["technology"],"content":"注意：本文参考An introduction to AS3 Signals写成，但不是翻译，有增删改。\n第一部分：Signals框架介绍（一）基本用法 第三部分：Signals框架介绍（三）原生事件 也许你现在又想起了AS3内置事件框架的好处，希望使用currentTarget？或者希望支持冒泡？OK，Signals也能满足你那多变的心……（貌似是我自己多变罢了ˇ^ˇ）\n###高级事件\n使用DeluxeSignal可以实现更高级的事件传递。还是基于闹钟的例子进行修改，将原来AlarmClock中的Signal改为DeluxSignal：\n[AlarmClock.as]\n1public function AlarmClock() 2{ 3\t//将自己作为引用传递给DeluxeSignal，同时增加GenericEvent的传递 4\talarm = new DeluxeSignal(this, GenericEvent, Date); 5} 6 7public var alarm:DeluxeSignal; 8 9public function ring():void 10{ 11\talarm.dispatch(new GenericEvent(), new Date()); 12} 修改Wakeup.as中的处理器函数，让它能够接受到传递的2个参数。\n[Wakeup.as]\n1private function handler_ring($evt:GenericEvent, $date:Date):void 2{ 3\ttrace(\u0026#39;currentTarget：\u0026#39;,$evt.currentTarget); 4\ttrace(\u0026#39;target：\u0026#39;,$evt.target); 5\ttrace(\u0026#39;signal：\u0026#39;,$evt.signal); 6\ttrace(\u0026#39;起床了！！！也不看看几点了：\u0026#39;+$date.toString()); 7} 在这里出现的GenericEvent，并非继承自flash.events.Event，而是实现了Signals自己的IEvent接口，与AS3的事件机制毫无关系。取一个貌似兄弟的名称，是为了方便大家理解罢了。它所提供的currentTarget和target属性，也是由IEvent自身提供。\n运行修改后的例子，可以看到如下输出：\n[trace] currentTarget： [object AlarmClock] [trace] target： [object AlarmClock] [trace] signal： [object DeluxeSignal] [trace] 起床了！！！也不看看几点了：Mon Jan 23 17:07:16 GMT+0800 2012\n现在，是不是可以对currentTarget“为所欲为”了呢？\n冒泡事件 Signals一样可以冒泡，而且并不依赖AS3自带的事件机制。当然，发送和接收冒泡事件的对象必须处于显示列表中。\n为了让AlarmClock能够被加入显示列表，我们让AlarmClock继承Sprite。而抛出冒泡事件就非常简单，只需要在实例化GenericEvent的同时传递true参数即可。看看AlarmClock类被修改的部分吧：\n[AlarmClock.as]\n1public class AlarmClock extends Sprite 2{ 3\tpublic function ring():void 4\t{ 5\talarm.dispatch(new GenericEvent(true), new Date()); 6\t} 7} 接收事件的类，必须实现IBubbleEventHandler接口，在onEventBubbled中处理冒泡事件。同时，要将_alarm实例加入显示列表。\n为了方便查看冒泡效果，可以将addOnce所在的一行注释掉，只接收冒泡事件。\n[Wakeup.as]\n1public class Wakeup extends Sprite implements IBubbleEventHandler 2{ 3\tpublic function Wakeup() 4\t{ 5\t_alarm = new AlarmClock(); 6\taddChild(_alarm); 7\t//_alarm.alarm.addOnce(handler_ring); 8\t_alarm.ring(); 9\t} 10 11\tpublic function onEventBubbled($evt:IEvent):Boolean 12\t{ 13\ttrace(\u0026#39;冒泡 currentTarget：\u0026#39;,$evt.currentTarget); 14\ttrace(\u0026#39;冒泡 target：\u0026#39;,$evt.target); 15\ttrace(\u0026#39;冒泡 signal：\u0026#39;,$evt.signal); 16\t//返回false代表不再继续冒泡 17\treturn false; 18\t} 19} 运行修改后的例子，可以看到如下输出：\n[trace] 冒泡 currentTarget： [object Wakeup] [trace] 冒泡 target： [object AlarmClock] [trace] 冒泡 signal： [object DeluxeSignal]\n###完整的类\n[AlarmClock.as]\n1package 2{ 3import flash.display.Sprite; 4import org.osflash.signals.Signal; 5import org.osflash.signals.DeluxeSignal; 6import org.osflash.signals.events.GenericEvent; 7 8/** 9* Signals闹钟范例 10* @author Aiden Tailor(http://www.developria.com/2010/10/an-introduction-to-as3-signals.html) 11* @author zrong(zengrong.net) 12*/ 13 14public class AlarmClock extends Sprite 15{ 16\tpublic function AlarmClock() 17\t{ 18\talarm = new DeluxeSignal(this, GenericEvent, Date); 19\t} 20 21\tpublic var alarm:DeluxeSignal; 22 23\tpublic function ring():void 24\t{ 25\t//使用冒泡的方式发布闹钟的响铃事件 26\talarm.dispatch(new GenericEvent(true), new Date()); 27\t} 28} 29} 30\u0026lt;/pre\u0026gt; 31 32**[Wakeup.as]** 33 34\u0026lt;pre lang=\u0026#34;ActionScript\u0026#34; line=\u0026#34;1\u0026#34; coloa=\u0026#34;+\u0026#34;\u0026gt; 35package 36{ 37import flash.display.Sprite; 38import org.osflash.signals.events.GenericEvent; 39import org.osflash.signals.events.IBubbleEventHandler; 40import org.osflash.signals.events.IEvent; 41 42[SWF(width=500,height=300,frameRate=30,backgroundColor=0xFFFFFF)] 43/** 44 * 测试闹钟 45 * @author Aiden Tailor(http://www.developria.com/2010/10/an-introduction-to-as3-signals.html) 46 * @author zrong(zengrong.net) 47 */ 48public class Wakeup extends Sprite implements IBubbleEventHandler 49{ 50\tpublic function Wakeup() 51\t{ 52\t_alarm = new AlarmClock(); 53\taddChild(_alarm); 54\t_alarm.alarm.addOnce(handler_ring); 55\t_alarm.ring(); 56\t} 57 58\tprivate var _alarm:AlarmClock; 59 60\tprivate function handler_ring($evt:GenericEvent, $date:Date):void 61\t{ 62\ttrace(\u0026#39;currentTarget：\u0026#39;,$evt.currentTarget); 63\ttrace(\u0026#39;target：\u0026#39;,$evt.target); 64\ttrace(\u0026#39;signal：\u0026#39;,$evt.signal); 65\ttrace(\u0026#39;起床了！！！也不看看几点了：\u0026#39;+$date.toString()); 66\t} 67\t68\tpublic function onEventBubbled($evt:IEvent):Boolean 69\t{ 70\ttrace(\u0026#39;冒泡 currentTarget：\u0026#39;,$evt.currentTarget); 71\ttrace(\u0026#39;冒泡 target：\u0026#39;,$evt.target); 72\ttrace(\u0026#39;冒泡 signal：\u0026#39;,$evt.signal); 73\t//返回false代表不再继续冒泡 74\treturn false; 75\t} 76} 77} ","date":"2012-01-20","description":"","lastmod":"2012-01-20T10:16:19Z","slug":"as3_signal_intro2","tags":["as3","framework","signals","design-pattern"],"title":"Signals框架介绍（二）高级事件","url":"https://blog.zengrong.net/post/as3_signal_intro2/"},{"categories":["technology"],"content":"今天准备卸载Flash Builder 4.5.1，安装4.6，哪料到卸载出错，提示去Adobe网站下载 Adobe Creative Suite Cleaner Tool 进行卸载。\n下载后，发现该工具是一个交互命令行工具，用来删除Adobe系列软件的注册信息。运行后，该工具只会卸载掉注册表或文件系统中的信息，不会删除Flash Builder安装目录。\n卸载完成后安装Flash Builder 4.6，结果报错：“安装过程中安装程序遇到错误(-1)。请重新启动计算机,然后重试。”\n再次使用Adobe Creative Suite Cleaner Tool删除所有能删除的软件信息，依然无效。\n最后，删除了 C:\\Program Files\\Common Files\\Adobe 下的所有文件夹，安装成功。\n","date":"2011-12-28","description":"","lastmod":"2011-12-28T03:59:05Z","slug":"remove-flash-builder","tags":["flashbuilder"],"title":"Flash Builder安装和卸载错误的解决办法","url":"https://blog.zengrong.net/post/remove-flash-builder/"},{"categories":["others"],"content":"今天收到一个武汉联通手机18627164090打来的电话，说最近国家在办理退购置税,我的房子可以退购置税8000左右，由于前段时间给我发的信件我没收到，现在退税必须联系国税局。她要求我打电话联系国税局询问相关事项，同时给了我一个电话号码(01086007773)和一个编号（58228），嘱咐我立即电话过去询问退税事项。\n听到一半我就确定这是个骗局了：\n对方显得过于热情（国家工作人员不可能这么热情），一再强调咨询电话是免费的，且一再要求我挂了电话后马上打电话询问； 对方普通话很差劲； 对方打来的电话是手机号码，且是联通的不记名GSM卡。 马上google了一下，确认是个骗局。如果打通骗子提供的电话，骗子会要求带银行卡去自动取款机进行操作，至于后面……你懂的。\n这个骗局比较高级，骗子知道我的姓名，小区的具体名称，且让我自己电话询问，很容易让人上当。\n确认之后我果断110报警。110的妹纸只记录了我提供的2个号码，说她们会跟进。估计后面也不会有什么结果了。\n希望没有人受骗吧……\n","date":"2011-12-26","description":"","lastmod":"2011-12-26T06:39:49Z","slug":"1500","tags":[],"title":"碰到一个国税局退购置税的骗局","url":"https://blog.zengrong.net/post/1500/"},{"categories":["technology"],"content":"面对Android设备那混乱的硬件体系，最淡定的开发者也会不免蛋疼起来。在做Android UI的时候，考虑最多的就是这些不同设备的适配问题。在为不同分辨率设计不同UI的时候，我们首先要知道目标设备的分辨率到底是多少。如何计算出来？\n水果手机经常喜欢用分辨率超过人眼极限来标榜自己的设备有多么牛B，据说iPhone 4S的分辨率达到了326 PPI(pixel per inch)，这个数值的计算公式为：\n开方(屏幕宽度平方+屏幕高度平方)÷对角线尺寸 iPhone 4S，960x640像素，对角线尺寸3.5英寸，用上面的公式计算结果如下：\nsqrt(sqr(960) + sqr(640)) / 3.5 ≈ 329.65\n看来水果公司并未说谎。只是3.5寸的屏搞这么高的分辨率，考视力么？\n和水果比起来，Android就更加复杂了，它把屏幕分成small,normal,large,xlarge四种，其对应的屏幕尺寸如下：\nxlarge 至少960dp x 720dp large 至少640dp x 480dp normal 至少470dp x 320dp small 至少426dp x 320dp dp(Density-independent pixel)是一个密度无关的像素单位，是用来表示基于160 dpi的设备计算出来的虚拟像素。\n屏幕分辨率被分成low dpi,medium dpi,high dpi,extra high dpi。详见下图：\n最近的Android设备屏幕分布图：\n更详细的文章：\nSupporting Multiple Screens Screen Sizes and Densities 另外，在分辨率单位上，DPI(dots per inch)和PPI(pixel per inch)目前已经通用。\n","date":"2011-12-23","description":"","lastmod":"2011-12-23T16:24:11Z","slug":"android-resolution","tags":["android","mobile"],"title":"Android设备分辨率计算","url":"https://blog.zengrong.net/post/android-resolution/"},{"categories":["impressions"],"content":"距离第一次到香港，时间过去3年多了。这是第三次到香港，写点新的经验吧。\n一、关于酒店 最开始订的是“香港之家”，原因有二：\n价格低 朋友推荐 但这次订房让我很失望。香港之家是必须先确定好行程再打全款的，可我订房全部成功6天后，他们却告知我无法提供房间，又将所有款项退还给我。这时距离我去香港只剩一周时间，再找比较便宜的酒店已经很困难了。而且事先根据香港之家的地理位置安排的7天行程，也要全部重新安排。可以说，这次失败的订房完全打乱了我的旅游计划。\n在淘宝上订房，也不靠谱。香港之家泡汤后，我首先去了淘宝，看着哪些300－400的均价，心中一喜。可一问都不是那么回事。由于进入12月，香港旅游也进入旺季，酒店标准间价格都在700港币以上，2间房一晚就要1400港币，条件也非常一般，打破天2星标准，在内地就是招待所级别了。那些300、400元的价格，都是幌子而已。\n最后敲定了香港迪士尼乐园酒店。由于当时连住3日的8折优惠房已经订完，就购买了一张600元的儿童“奇妙处处通”银卡，该卡可以在酒店房价上打85折，酒店餐饮和迪士尼乐园餐饮打9折。这样算下来，房费+服务费一共是港币2057每天。迪士尼乐园酒店的客房较大，有38平方，可以住4人。这比淘宝上的酒店靠谱多了。\n迪士尼乐园一共有两座酒店。如果带孩子去迪士尼，最好是住“迪士尼好莱坞酒店”。虽然星级较前者要低，房间也小些，但价格要便宜不少，且酒店房间的布置主题是迪士尼人物（迪士尼乐园酒店房间布置是公主房），孩子会比较喜欢。\n订房的时候，最好是直接电话到迪士尼乐园酒店订房中心订房，只要提供信用卡就可以订房，但要注意强调走银联通道，否则会多花不少手续费。接线生一般会主动告知你折扣的情况，也可先访问酒店网站查询折扣，在订房的时候询问接线生折扣细节。\n二、关于迪士尼乐园 如果计划在迪士尼玩3天以上的，建议还是给孩子买一张“奇妙处处通”银卡，因为事先买好的儿童票，是可以抵差价的（我的抵了250港币）。而银卡在周一至周五是可以凭卡入园的，不用再买门票（记得要带上孩子的港澳通行证或护照，银卡上有名字，入园需要验证身份）。\n如果安排的好，迪士尼乐园大约1天半左右的时间就可以玩完。好玩的项目可以Google之，这里不提。值得一说的是迪士尼金奖音乐剧和每晚20点的散场烟花，你不得不佩服美国人的大手笔。难怪老谋子在好莱坞也那么受欢迎……\n迪士尼乐园是禁止带饮料的，但可以带食物。海洋公园可以带饮料，但禁止带食物。其实门口检查并不太严，想逃避也很容易。可以找个有很多拉链的包（双肩的那种），然后打开其中的大部分拉链进行检查，放食物或水的拉链就不开了……这样只要带的东西不是太多，就能带进乐园的。\n三、关于交通 八达通是必备的，方便且省钱。很多地方可以不用排队买票，直接刷卡。还省去了找零的时间（那些角币，是无法换回人民币的）。港铁“香港”站和“中环”站，在地图上不在一起，其实是可以不出站从香港走到中环的。只是地方太大，我都走迷路了……\n由于迪士尼在离岛上，距离市中心很远，每次从迪士尼乐园酒店到旺角或港岛购物，都要耗去40分钟至一个小时转车。下班高峰期在尖沙咀和旺角、太子等换乘站，还可能会挤不上车。所以如果酒店比较远，就一定要控制好出行的时间点。另外港铁价格较高，4个人出门一趟，随随便便就是一、两百块。比如，从罗湖到迪士尼，如果全程港铁，单程票是55元港币，刷八达通也要48。\n四、关于过关 罗湖口岸当然是最方便、最快捷的过关地点选择。但这个口岸也是人最多的关口。我选在周六20点出关，那就是个错误。几千人熙熙攘攘排在3米宽的通道中小碎步向前挪动，还要时不时提防旁边巨大行李箱和纸盒子的侵袭，再看着孩子不会在你眼前消失，是何等纠结的事情啊！\n其实现在过关查验并不严的，我看到很多推着大量原包纸箱的人，也能顺利过关（当然也有被拦下来的）。像我们这样把东西偷偷摸摸藏在旅行箱里面，还战战兢兢小心翼翼，实在是没有必要。\n从深圳入关的时候，是全程不查行李的（这貌似和3年前不同了）。而从香港出关的时候也不查行李，只在深圳入关会使用安检机查一次。如果你不被海关人员盯上（这几率也太低了），就没什么事。\n五、关于通信 千万不要去办移不动、连不通、点不信的坑爹的手机国际漫游。先要交保证金不说，还贵得离谱。建议如下：\n提前网购电话卡，建议每人一张，入关前激活（激活一般需要几分钟到几十分钟不等，口岸边都有香港方向的手机信号）方便走失的时候联系。 入关后找一家便利店购买电话卡。最便宜的是港币28元。 原号码可提前办理关机呼转（注意要开通国际长途），在香港收到电话后，可挂断，然后用香港手机拨打回去。 我购买的是RMB35元包打240分钟的电话卡，这个7天一般用不完。每天30MB上网流量，跑地图是不够的。还有28元包100分钟的卡。 六、入港行程 从武汉到香港，有这样几种走法：\n飞到广州，广九直通车到香港（推荐）\n优点：机票比车票便宜，过关排队人数少，直达九龙 缺点：广九直通车首班车时间较晚 动车到广州，广九直通车到香港\n优点：时间安排比飞机灵活；比飞机安全？（至于你信不信，反正我信） 缺点：票价较贵 飞或动车到广州，广深高速到深圳，罗湖过关\n优点：可在深圳住一晚，第二天一早过关，节省一天房费，在香港呆满一天 缺点：一天都在广州地铁、动车上转车 火车直接到深圳，罗湖过关\n优点：在火车上节省出一晚时间，第二天一早过关，在香港呆满一天；硬卧票价便宜 缺点：火车上的睡眠质量实在不咋地；带孩子的话建议买软卧 问问百度？（强烈不推荐！）\n七、其他 对于能自己行动的孩子来说，一定要准备一张联系方式卡，写上孩子的名字和家长的香港+内地手机号码，放在孩子口袋里或挂在脖子上，并每天和孩子强调一遍走失后的应对方法，切记切记。\n问路尽量找穿制服的人（警察、清洁工、酒店服务员……）和香港本地的年青人。他们会比较热情，普通话也较好。\n香港的巴士是不报站的，所以一定要知道自己去的地方是哪里。繁华地段的巴士上，你碰到的大多数人可能都是内地人，所以问到站没有，最好还是问巴士司机。\n如果一定在香港要买泡面吃的话，记得买一双筷子。香港出售的泡面是不带“工具”的，筷子叉子一概没有。想想你看着一碗“香喷喷”的泡面流口水的杯具情景吧。还好不是没有调料包……\n","date":"2011-12-12","description":"","lastmod":"2011-12-12T15:19:31Z","slug":"travel-in-hongkong2","tags":["journey"],"title":"香港自由行经验（二）","url":"https://blog.zengrong.net/post/travel-in-hongkong2/"},{"categories":["others"],"content":"打开百度地图，搜索“深圳海关到香港迪士尼乐园”，百度会给你一个非常给力的答案：\n先坐15个小时火车到上海，然后坐飞机到香港机场，然后……\n一起欢乐一下：\nhttp://j.map.baidu.com/ZElyc\n","date":"2011-12-05","description":"","lastmod":"2011-12-05T12:32:18Z","slug":"from-luohu-to-honekong-disney-land","tags":["journey"],"title":"从罗湖海关怎么去香港迪士尼乐园？百度给你答案","url":"https://blog.zengrong.net/post/from-luohu-to-honekong-disney-land/"},{"categories":["technology"],"content":"Flex编译器参数中-swf-version与-target-player之关系\n2014-04-11更新：更新Flash Player 13.0/AIR 13.0正式版，加入对Flash Player和AIR的版本号开始同步的说明。 2014-02-22更新：更新Flash Player 13.0/AIR 13.0 beta版。 2014-01-18更新：更新Flash Player 12.0/AIR 4.0正式版。 2013-12-11更新：更新Flash Player 11.9/AIR 3.9正式版。 2013-04-16更新：更新Flash Player 11.7/AIR 3.7正式版。 2013-03-10更新：更新Flash Player 11.6/AIR 3.6正式版以及beta 11.7。 2012-11-07更新：更新Flash Player 11.5/AIR 3.5正式版发布日期。 2012-10-10更新：加入Flash Player 11.5/AIR 3.5的对应关系；加入发布日期列；修改文章结构和部分内容。 2012-08-27更新：加入Flash Player 11.4/AIR 3.4的对应关系。 2012-03-20更新：本篇文章只讲了Flex SDK的情况，但Flash IDE也能通过修改配置文件的方式支持新的Flash Player功能，详见这里：让Flash支持更新的Flash Player功能 2012-04-08更新：加入AIR的版本说明；加入Flash Player 11.3的对应关系。 注意 由于我不再进行 Flash 开发，这个列表可能不会再更新。\n在Flex 4.6 SDK中，可以发现framework/flex-config.xml中的默认-swf-version的值变成了14，而-target-player则变成了11.1。\n记得在Flash Player 10.2发布的时候，为了使用Flash 10.2提供的原生位图鼠标光标功能，需要在编译的时候将-swf-version编译器属性值设置为11。以此推算，14这个值是针对Flash Player 11.1的。\n那么-swf-version和-target-player的对应关系如何？见下表：\nFlash Player AIR Flex -swf-version -target-player 发布日期 9 3 9 9 10.0 1.5 4.0 10 10.0.0 10.1 2.0/2.5 4.1 10 10.1.0 10.2 2.6 4.5/4.5.1 11 10.2.0 2011-2-9 10.3 2.7 12 10.3.0 11.0 3.0 13 11.0.0 2011-10-4 11.1 3.1 4.6 14 11.1 2011-11-7 11.2 3.2 15 11.2 2012-3-28 11.3 3.3 16 11.3 2012-6-8 11.4 3.4 Adobe Flex 4.6/Apache Flex 4.8 17 11.4 2012-08-21 11.5 3.5 Adobe Flex 4.6/Apache Flex 4.8 18 11.5 2012-11-06 11.6 3.6 Adobe Flex 4.7/Apache Flex 4.9 19 11.6 2013-02-12 11.7 3.7 Adobe Flex 4.7/Apache Flex 4.9 20 11.7 2013-04-09 11.8 3.8 Adobe Flex 4.7/Apache Flex 4.10 21 11.8 2013-07-09 11.9 3.9 Adobe Flex 4.7/Apache Flex 4.11 22 11.9 2013-10-08 12.0 4.0 Adobe Flex 4.7/Apache Flex 4.11 23 12.0 2014-01-14 13.0 13.0 Adobe Flex 4.7/Apache Flex 4.12 24 13.0 2014-04-08 -target-player和-swf-version 上面的这份表格，一部分是根据Targeting Flash Player versions整理出来的，最新的部分是我自己根据Flash Player/AIR的更新不断增加的。但这个表格是不精确的。\n因为，-swf-version的值能支持到那个程度，其实与Flex SDK并没有直接的关系，而是依赖于Flex SDK中的playerglobal.swc（位于frameworks/libs/player）。\n打开Flex 4.6 SDK的frameworks/libs/player文件夹，可以看到其中只有一个11.1子文件夹，放置着针对Flash Player 11.1的playerglobal.swc。这个swc的作用有2个： （这里是基于Flex SDK和Flash Builder讲解，如果你使用Flash Professional，可以看这里：让Flash支持更新的Flash Player功能）\n在程序编写期间，Flash Builder使用它来提供自动完成功能。当然，如果直接用mxml编译器（比如我），就没多大关系； 在程序编译期间，mxmlc编译器需要调用它。 那么-target-player是干嘛的？它用来告诉Flex编译器，在哪里去找playerglobal.swc。\n在Flash Builder 4.6 的项目的 ActionScrip编译器 设置中，可以设置-target-player参数的值。默认是“使用SDK所需的最低版本”。对于我目前安装的Flex SDK 4.6来说，这个“最低版本”就是11.1。\n而我们可以使用特定的版本，例如下图中是11.2.0。\n在程序编写期间，Flash Builder会自动去frameworks/libs/player/11.2这个目录中寻找playerglobal.swc，如果找不到，一些11.2才支持的功能（例如MouseEvent.RIGHT_CLICK）就无法得到语法提示。\n而在调试和发布程序的时候，编译器使用frameworks/libs/player/11.2/playerglobal.swc进行编译。如果依然找不到这个文件，编译会报错无法打开“D:\\flex_sdks\\4.6.0\\frameworks\\libs\\player\\11.2\\playerglobal.swc”，如下图所示：\n不同步性 在每个新版本的Flash Player 发布的时候，Adobe都一起提供了playerglobal.swc文件，而且会在发布文档中说明这个版本的Flash Player对应的-swf-version是多少。\n因为SDK的发布，和Flash Player的发布并非总是同步的。\n在Flash 8时代，编译器和Flash Player是完全同步的。因为那时，只有Flash IDE可以生成swf文件。而Flex问世，以及MacroMedia被Adobe收购以后，Flash的发展就变得多样了，FlashIDE和Flex都可以生成swf文件，Flash Player的发展也更加独立。现在的情况，Flash CS，Flash Builder，Flex SDK，Flash Player的发布已经完全不同步了。尤其是当Adobe将Flex SDK交给Apache发展后，SDK的更新速度估计会更快。Adobe自己又会紧紧将Flash Player攥在手里，保持自己的步调来更新。（关于这段历史，我在Actionscript,AS3,MXML,Flex,Flex Builder,Flash Builder,Flash,AIR,Flash Player之关系一文中做了详述）\nFlex SDK可以和不同版本的Flash Player相配。即使是使用Flex SDK 3.6，同样也可以开发出Flash Player 11.1支持的swf程序。（当然，前提是不使用Flex frameworks提供的组件，仅仅使用Flash Player提供的API）。通过使用不同版本Flash Player提供的playerglobal.swc文件，就可以让旧的Flex SDK兼容新的-swf-version和 -target-player编译属性。当然，也可以让新的Flex SDK兼容旧的-swf-version和-target-player。\n例如，目前Adobe官方提供的最新版（也是Adobe Flex的最终版，因为后面会更名为Apache Flex SDK）Flex SDK版本为4.6。根据Adobe的说法，这个版本的Flex SDK的最低支持的-target-player为11.1。这是因为frameworks/libs/player中仅仅提供了11.1版本的playerglobal.swc。我们可以将其他版本的playerglobal.swc复制到该目录下，并修改frameworks/flex-config.xml中的target-player标签，以改变Flex SDK默认编译的swf目标。当然，更方便和灵活的做法是在Flex项目的编译属性中设置-target-player属性。\nFlash Player 和 AIR 的版本号同步 AIR Runtime在Flash Player的10.0版本的时候才发布了1.5版本，这导致了这两位的版本号一直不同步。\n在2014年4月14日，Adobe终于做出了一个“重要的”决定，就是让它们的版本号同步！此时，Flash Player和AIR的版本号同为13。\n其实它们的底层本来就没什么大的不同，只是AIR增加了一些专用的本地API而已。所以我个人认为或许把它们的名字改成 Adobe Runtime for Browser 和 Adobe Runtim for Desktop 更好些。\n这里还有一篇文章介绍：Versioning in Flash Runtime (-swf-version)\n","date":"2011-12-03","description":"","lastmod":"2011-12-03T09:40:34Z","slug":"swf-version-target-player","tags":["flashbuilder","flashplayer","flex","study"],"title":"Flex编译器参数中-swf-version与-target-player之关系","url":"https://blog.zengrong.net/post/swf-version-target-player/"},{"categories":["technology"],"content":"在对Flash Media Server中的视频流使用BitmapData.draw()进行绘制的时候，会抛出这样异常：\ncannot access rtmp://xxxxx. No policy files granted access. at flash.display::BitmapData/draw()\n这个错误出现的原因是，客户端（swf）没有权限复制NetStream中的原始视频数据。看提示，是需要一个策略文件。\n但是，在FMS服务器上无法放置策略文件，FMS也不能像Socket服务器那样发送策略文件给客户端，这种情况应该怎么处理呢？\n答案在这里：\nhttp://help.adobe.com/en_US/flashmediaserver/ssaslr/WS5b3ccc516d4fbf351e63e3d11a11afc95e-7ec3SSASLR.html#WS5b3ccc516d4fbf351e63e3d11a11afc95e-7fcbSSASLR\n只需要FMS在同意client连接后，为其设置videoSampleAccess属性即可。videoSampleAccess的设置方式与readAccess相同。\n范例：\n1application.onConnect = function($client) 2{ 3\tapplication.acceptConnection($client); 4\t//设置成\u0026#34;/\u0026#34;，允许所有路径 5\tclient.videoSampleAccess = \u0026#34;/\u0026#34;; 6} ","date":"2011-12-01","description":"","lastmod":"2011-12-01T05:52:23Z","slug":"bitmapdata_draw_on_rtmp_video","tags":["bitmapdata","fms","rtmp"],"title":"对RTMP视频流进行BitmapData.draw()出错的解决办法","url":"https://blog.zengrong.net/post/bitmapdata_draw_on_rtmp_video/"},{"categories":["technology"],"content":"2011-12-21：v0.6.2版发布\n加入在SpriteSheet中增加帧的功能。帧只能增加到已有帧的末尾。 加入在打开SpriteSheet中增加SpriteSheet的功能。这样就可以实现多个SpriteSheet的合并。规则如下： 所有帧会增加到当前Sheet的末尾； 若label重名，则被导入Sheet中的label不导入，但依然会导入该lable的所有帧； 若name重名，则被导入的Sheet中该name对应的重名帧不会被导入； 若原始Sheet中包含name，但被导入Sheet中不含name，则会自动为被导入的Sheet中的所有帧命名。 更多的功能介绍以及软件下载，看这里。\n","date":"2011-11-30","description":"","lastmod":"2011-11-30T10:26:28Z","slug":"sprite-sheet-062","tags":["air","bitmapdata","spritesheet"],"title":"编辑Sprite Sheet的小工具：sprite sheet editor v0.6.2发布","url":"https://blog.zengrong.net/post/sprite-sheet-062/"},{"categories":["others"],"content":"原文：http://www.programmer.com.cn/282/（建议看原文评论，更能了解中国程序员的特点）\n最近以裁判的身份参加了公司举办的编程大赛，发现高手云集，对公司内部的程序员能力也有了更深入的了解。我觉得编程能力对程序员而言，虽然很重要，但并不是全部。那么作为一个程序员，到底应该具备什么样的能力呢？这个话题显然太大。不过我觉得可以看看其它国家的程序员，也许可以得到一些借鉴。我有幸和中国，美国，印度和日本四国程序员有比较深入的合作过。虽然他们不一定有代表性，但我觉得他们的共性还是比较明显的。以下的比较纯属个人见解，欢迎指正。\n首先是日本程序员。他们的特点是非常仔细。我认为很主要的一个原因是日本公司的需求非常细致。细致到在网页上，连一个像素都不能偏差的地步。另外，日本人的执行力非常强，对老板的承诺比命还重要。一个项目可以做到连续3个月天天加班，每天只睡4个小时。然而，高执行力背后的代价是低创造力。在日新月异的互联网今天，很少听说日本工程师发明了哪些重要的技术。与其说这些特点是日本程序员的，不如说是大部分日本人的。因为在日本文化中，追求品质和遵守等级制度是根深蒂固的。另外，技术领域中的很多专业词汇是外来语，以英语(论坛)为主。这些专业词汇往往会被翻译成片假名。而片假名的发言有时候和英语大相径庭，导致沟通的困难。比如病毒一词在英语中是Virus，发音为歪儒斯，而日语的发音是味鲁斯。再例如服务器（Server）一词在日语中的发音是萨巴，和英文发言简直风牛马不相及。因此与日本程序员沟通是比较痛苦的，除非你懂日语。\n其次来看看印度程序员。我所接触的印度工程师都是在美国工作的。虽然他们和印度本地的工程师肯定有区别，不过相似的地方应该更多一些吧。我觉得他们的普遍优点就一个：流程做得好，文档写得好。但是他们写代码的能力，我个人的观点是一般般。我想这里面有两层原因。一是有相当一部分在美国工作的印度程序员是半路出家。转行做程序员是为了生存而已。二是印度程序员在算法，数据机构等基本功方面的水平明显低于中国程序员的。这就导致他们写的很多代码逻辑性不强和性能不优（以我的标准来看）。不过这两个问题在一定程度上被大量的文档和高性能的硬件设备弥补和掩盖了。在沟通方面，印度人的英语发音对西方人而言几乎没有问题，但很难被中国人听懂，甚至往往被国人怀疑他们是不是在说英文。\n从某种意义上讲，日本程序员和印度程序员十分相似。他们都很敬业，都能让领导比较满意，但不要过多地期望他们能做得更好，因为他们的目标就是完成领导指派的任务。日本程序员让领导满意的方法是不折不扣的执行和狂热的加班。而印度程序员让领导满意的方法是通过大量的文档来告诉领导他们的工作意义重大，流程严谨，资料齐全，而且成本很低。夸张一点地讲：日本程序员善于做领导想做的事，印度程序员善于说领导想听的话。\n接下来说说美国程序员。美国程序员千奇百怪，好像很难只用几个词来定义他们。可能是因为美国是一个移民国家吧，本来就千奇百怪。但大部分程序员有一个共同的特点：喜欢技术，甚至崇尚技术。这点在硅谷尤为突出。这就导致每个技术领域中都有一些人会废寝忘食地钻研。其实这和打游戏一样，如果你着了迷，自然会忘了吃，忘了喝，拼命地玩。我所认识的美国程序员还有一个特点，才艺能力都不错。以前在波士顿工作的一家公司中，几十位工程师居然可以组成一个交响乐团。有小提琴，大提琴，小号，竖琴，打击乐等各种各样的西洋乐器手。而且这些哥们姐们还不是一般地玩玩，周末都有自己的固定乐队，经常参加社区的表演。更有甚者，在硅谷工作时的一位同事，白天写程序，晚上在自家的车库里练习乒乓球，竟然代表美国参加了悉尼和雅典的两届奥运会。说起写文档的能力，美国程序员绝对不亚于印度人。但是美国人写文档不是为了老板，而是为了自己，为了分享。因此他们的文档往往读起来很有趣，很实用。当然，这会让老板有时候很头疼，因为程序员不那么“听话”。他们不是给老板交差，而是要实现自己的想法，自己的设计，自己的完美。说白了，就是美国程序员有时候想法多了点。\n最后是我们中国的程序员。和其他国家的程序员相比，我觉得他们的特点还是比较明显的。他们的算法能力普遍高于其它几个国家的。这可能是我们的教育体制导致的，比较注重理论知识。反过来，实践能力就相对差些。我们的程序员执行能力并不差，但在解决问题的能力上明显不足。往往需要把任务分解得很细以后才能完成，独立解决问题的能力不够。另外在表达能力上也相对差些。相信大家一定见过技术水平很高，但表达能力很差的工程师。最好笑的是，我见过不少工程师拿着一支写不出字的白板笔（我们的白板笔质量也确实不咋样），有模有样地在白板上写字。仿佛听众可以看得到他/她写得是什么。因为他/她完全沉浸在自己的逻辑中，完全不去体会听众的感受。不过我认为这些缺点并不严重。\n因为这些是属于技能和经验方面的东西，是可以通过实际工作或者培训来提升的。我认为国内程序员最大的问题还是所处的环境不利，导致相当一部分人比较浮躁和急功近利。真正能够沉下心来钻研技术，热爱技术的是凤毛麟角。我在面试的时候，常常发现工程师知识面还挺广，但深度几乎没有。这样的人很难在技术领域有所作为。我希望找到的人是，敢于承认自己不会的地方，但是只要会的东西，哪怕就一样，就要一定比别人理解得透，钻研得深。我相信一个人如果在某一个问题上比别人做得好，在其它问题上也一定有能力超越别人。\n虽然比较下来，看到中国程序员不少的问题。但作为群体，中国的程序员可能是全世界最聪明的工程师群体。因为环境的原因，使得他们不得不想法很多，顾虑很多，无法最大程度地将聪明才智发挥在技术上。改变这种状况首先要从公司的管理层开始。只有技术负责人热爱技术，追求卓越，才可能为技术人员创造环境，激励他们钻研和创新。技术负责人需要深入项目，和工程师们一起讨论技术设计，从而通过具体问题来提升工程师的能力，同时也防止自己的技术能力滑坡。在技术管理上，很多国内的公司把工程师简单地作为资源，过于强调流程管理和资源管理。我的观点是：工程师不是高级蓝领，不能以管理生产线的方式来进行管理。优良的环境只有靠大家一起来创造。中国工程师一定可以成为世界上最优秀的工程师群体。\n","date":"2011-11-25","description":"","lastmod":"2011-11-25T15:12:19Z","slug":"programmer-in-caij","tags":[],"title":"【转】中美印日四国程序员的特点","url":"https://blog.zengrong.net/post/programmer-in-caij/"},{"categories":["technology"],"content":"现在主要操作系统转到ArchLinux。在搭建了Apache后，DocumentRoot为/srv/http\n我/srv/http下建立了一个符号链接docs，指向/home/zrong/docs\n可是，无论怎样设置（FollowSymLinks当然是加了的），Apache就是不显示这个符号链接。\n怀疑是权限问题，但docs的权限已经设置成了777。\n查看apache的log，发现一条有用的信息：\nSymbolic link not allowed or link target not accessible\n看来确实是权限问题，仔细查看，我发现/home/zrong的权限是700。\n为/home/zrong加上执行权限，一切正常：\n1chmod +x /home/zrong 另外，网上还有说此错误是由于SELinux对apache的影响。因为我没有装SELinux，因此排除。\n","date":"2011-11-23","description":"","lastmod":"2011-11-23T07:32:44Z","slug":"1479","tags":["apache","linux"],"title":"Apache不显示符号链接的处理办法","url":"https://blog.zengrong.net/post/1479/"},{"categories":["technology"],"content":"这是我在知乎上的一个回答，原文：http://www.zhihu.com/question/19910415/answer/13337373\nHTML5势不可挡，但Flash在视频、游戏方面依然有优势。\n其实现在互联网上跟风叫嚣Flash烂的人，绝大多数都是没有深入过Flash开发的人。\nFlash在技术上与HTML5+JS2+CSS3相比，目前还是有很多优势的：\n比较完善的开发和调试环境； 统一的性能表现； Socket连接的支持； 优秀的摄像头和麦克风的支持； 统一的平台语言； 面向虚拟3D设备的编程。 但，Flash也有很多劣势：\n不适合企业开发。Flex是一个优秀的框架，但性能始终差强人意，开发过程太过缓慢； 拥有大量不懂编程的“伪程序员和伪闪客”编写了大量影响浏览器性能的Flash小广告和小动画，影响了互联网上绝大多数不懂技术和不懂Flash编程技术的人对Flash技术的正确判断； 不专业。上面两点影响了所有人的判断（包括我），认为一个用Flash技术制作的企业网站是不专业的； 性能，还是性能。基于浏览器的性能和基于浏览器的插件的性能，谁可能做得更好？ 当然，上面说的大多数的优势与劣势，都是基于桌面平台的。Flash技术在移动平台上可以说是完败。JAVA都做不到的事情，Flash也做不到。Sun做不到的事情，Adobe更不可能做到。\nAdobe一开始就走错了路，搞了个不伦不类的Flash Lite，居然现在还开发到4.0！如果一开始就走原生程序的路，现在或许会好很多。\nAIR是个不错的东西，但还是被Adobe那龟速的开发时间表所拖累，先是放弃了Linux平台的用户与开发者，然后到了3.0才加上原生插件支持，结果现在都脱不了“玩具”的帽子。\nAdobe放弃支持移动平台Flash Player，是希望集中精力用AIR统一移动开发平台，至于结果如何，就只有拭目以待了。\n","date":"2011-11-14","description":"","lastmod":"2011-11-14T03:13:51Z","slug":"adobe-stop-flash","tags":["adobe","flash","flashplayer","flex"],"title":"怎么看待Adobe停止移动版Flash播放器开发，而加大HTML5的开发？","url":"https://blog.zengrong.net/post/adobe-stop-flash/"},{"categories":["technology"],"content":" 转自：http://www.riadev.com/flex-thread-1820-1-1.html 译者：瑞研社区 郭少瑞 原文：http://blogs.adobe.com/flex/2011/11/your-questions-about-flex.html 这个问答对于Flex开发者非常重要：\n在Adobe公布策略调整的这一周，我们知道，Flex社区围绕Adobe的Flex SDK的计划有很多疑问，我们尝试回答这些问题：\n问题1：Adobe公司还会致力于发展Flex吗 ？ 答：是的。我们知道Flex为企业级开发提供了一套独特的解决方案，也知道应用开发领域的技术前景正在迅速改变，并且我们的客户希望更直接的控制他们所使用的技术。鉴于此，正如我们将收购Nitobi得到的PhoneGap贡献给Apache基金会那样，我们计划将Flex SDK贡献给一个开源组织。\n这个项目的成员组成包括：一些来自Flex SDK工程团队的开发者，Flex社区的技术达人，还有一些成员来自于Spoon项目和一些已经使用Flex技术的企业。Flex SDK未来的开发将在新的领导模式下继续，Adobe也将为此作出积极贡献。\n问题2：Adobe建议我们在企业级开发中应该使用Flex还是HTML5？ 答：长期来看，我们相信HTML5将成为企业级开发的最好技术。我们也知道，目前来看，Flex在某些大型客户端项目上具备明显优势。\n鉴于我们在Flex上的经验和创新能力，我们将为HTML5的发展和进步作出积极的贡献，这个将从移动应用开始。事实上，许多Flex SDK的工程师和产品经理将转移到我们的HTML方面努力工作。我们将继续作出显著的贡献，以开放的WebKit和jQuery的Web技术，推进PhoneGap发展，并创造新的工具来解决开发HTML5应用面临的挑战。!\n问题3：之前宣布的Flex路线图会被实施吗？ 答：Flex的路线图将由未来管理Flex的理事会决定。我们计划将之前为Flex制定的规划贡献给这个新项目（即由新的理事会管理的Flex）。\n问题4：Adobe仍然会发展Flash Builder吗？ 答：是的。Flash Builder将会继续发展，Adobe会努力确保Flex开发人员顺利使用这个工具，并支持未来的Flex SDK的新特性。\n问题5：Adobe会继续支持已经使用Flex的客户吗？ 答：是的。Adobe将继续履行现有的Flex支持合同。\n因此，下一步是什么？ 我们正在接近完成Flex 4.6 SDK的开发，将在2011年11月29日发布。在此之后，我们将开始转移到开放的开发模式，即上面所说的内容。\n就个人而言，我们会和大家保持沟通，并承诺在未来几周和几个月内定期发布最新的消息。\n我们相信，这将改变Flex SDK的发展模式，将确保更广泛的社区可以在今后的多年内直接使用Flex。\n==== UPDATE – 11/15/11 ====\n在上面的澄清之后，我们又收到了很多意见和反馈。在这里我们继续回答以下问题：\nAdobe对Flex发展的未来规划是什么？ 答：我们为Apache基金会提供了两个关于未来发展Flex SDK和Blaze DS的规划。\n除了核心的Flex SDK（包含自动化和高级数据可视化组件），Adobe还计划捐赠以下内容： 已完成的，但还未公布的Spark组件，包括：ViewStack，Accordion，DateFiled，DateChooser和一个增强的DataGrid。 BlazeDS部分，则是基于Java服务器的远程访问和网络通讯技术，使开发人员可以轻松连接到后端的分布式数据，或将实时数据推送到Flex应用程序。 Falcon，下一代MXML和ActionScript编译器，目前正在开发中（有望于2012年完成）。 Falcon JS，一个实验性的交叉编译器，将MXML和ActionScript编译到HTML和JavaScript。 Flex测试工具，将继续沿用Adobe之前使用的，以确保可以继续高效的开发Flex应用。 Adobe仍然有一个包含Flex SDK工程师的团队，来支持未来的新的Apache项目。Adobe已经开始做一些开发工作，包括其他的Spark组件。 是不是Adobe放弃了Flex，把它交给Apache，等死而已？ 答：绝对不是。我们为实现了Flex而感到无比自豪，并且我们知道，它将在今后的很多年里仍然有显著的价值。我们期望从Apache社区中得到积极的，可持续的发展模式。需要澄清的是，我们将为这个项目提供可靠的支持，并且我们将和Flex社区一起工作，来让他们的贡献更有效。\n从Flex 3 SDK开始，Flex已经被开源了。那么现在重新宣布的开源意味着什么？ 答：在Flex 3开源之后，用户主要使用Flex源代码来调试Flex框架中的基本问题，而不是开发新的功能或修复错误，和将他们的反馈体现到SDK。\n正如周五宣布的，Adobe将不再是Flex正在进行的路线图的所有者。相反，该项目将在Apache基金会得到良好的规划和管理。在这种模式下，Apache社区成员提供该项目的领导。我们期望项目管理团队中包含Adobe工程师和关键的社区领袖。他们将一起协作，来规划未来版本的Flex SDK的新功能和特性增强。Apache的模式已经证明，这是一个充满活力的，可持续发展的模式。\n新的开源项目如何展开？它在哪里托管？谁将管理这个项目？Adobe是否仍然控制了Flex的路线图？我如何提交贡献？ 答：我们正积极努力的促使Flex SDK和Blaze DS项目成为Apache基金会的孵化器项目。我们期待在未来几周内取得进展并和大家分享信息。我们正积极联系Flex社区成员，以确保他们和Adobe的工程师一起参与项目的管理。\n关于Flex应用程序将继续运行于Flash Player和AIR，Adobe有什么保证？ 答：Adobe将继续支持未来版本的Flex构建的应用程序，包括允许于PC端的Flash Player之上的在线应用，和基于Adobe AIR的运行于iOS，Android，和RIM平板操作系统的移动应用程序。\n你说的Adobe仍然致力于发展Flash Builder，是什么意思？是指在未来的Flex SDK的背景下？ 答：未来版本的Adobe Flash Builder中，将继续提供代码编辑，编译，调试和分析Flex应用程序的支持。Adobe将继续工作，以确保Flash Builder兼容Flex SDK未来的版本。此前路线图通报的功能，如增强的代码编辑，实时错误突出显示和编译型的支持，将继续提供给ActionScript和Flex开发人员。\nFlex还可以作为现有或新项目的可行的技术方案吗？ 答：当然可以。Flex SDK将作为一个开源项目继续得到发展和维护，Adobe公司会积极推动它的发展。\n你说，“长期来看HTML是最合适的企业级应用的解决方案”，什么意思？ 答：HTML5的相关技术（包括HTML，JavaScript和CSS）正变得越来越强大，包括：高级特性（Canvas），性能（在许多浏览器中的GPU加速），和应用程序相关的功能（包括离线存储，网络监测）。这样，我们完全有理由相信，它将继续以迅猛的速度发展。至于需要多长时间，取决于你的应用程序的要求，可能是3到5年。我们相信，HTML5可以支持现在Flex的大部分功能。\n但是，Flex已经有多年发展，和HTML5相比在企业级开发上还是有很多优势，尤其是：\nFlex提供了跨平台的一致性 Flex组件和编程模型来构建复杂的应用程序用户界面时非常高效 ActionScript是一个成熟的语言，适用于大型应用程序开发 工具的支持（Adobe和第三方），包括代码编辑，测试和分析 我们打算在HTML相关的技术领域投资，来推动HTML5适合开发企业级应用程序。 Adobe是否提供迁移工具，把现有的Flex应用程序转换为HTML/JavaScript？ 答：我们已经在这个领域进行了一些尝试性的工作，但目前仍不能确保将Flex内容转换为HTML的可行性。Falcon JS交叉编译器，就是做这个的，这个项目仍然处于早期，将作为开源项目捐献。\n接下来会发生什么？ 答：我们正积极和Apache基金会协商开源事宜。一旦建议被接受，Adobe和社区贡献者就可以开始新的贡献。我们将积极和大家分享信息-希望在未来的几周内就可以确定。\n我们将为您提供更详细的开源方面的信息，我们正在做的工作，您可以作出的贡献，以及未来我们HTML5的相关计划。\n我们希望有机会和更多关注这个变化的Flex开发人员接触，为此，将由Adobe Flex技术传教士举办多个城市的国际巡回演讲，以便和大家直接交流。请继续关注更多的信息。\nAndrew Shorten \u0026amp; Deepa Subramaniam\nGroup Product Managers, Adobe\n","date":"2011-11-12","description":"","lastmod":"2011-11-12T14:39:49Z","slug":"your-questions-about-flex","tags":["flex","html5"],"title":"【转】关于Flex未来走向的问答","url":"https://blog.zengrong.net/post/your-questions-about-flex/"},{"categories":["technology"],"content":"美国时间11月9日消息：Adobe将停止基于移动设备浏览器的FlashPlayer开发，专注于基于移动设备的原生AIR应用程序。\n英文原文：http://blogs.adobe.com/conversations/2011/11/flash-focus.html\n7yue总结的中文要点：\n个人认为，Adobe走了一步好棋。\nFlash Player能做的，除了在浏览器中运行之外，AIR都能做；AIR能做的，Flash Player并不是都能做。\n既然在移动设备中，原生程序才是王道，那何苦还开发费力不讨好的 Flash Player？\nAIR推出的最初原因就是为了打破浏览器对Flash的限制。在PC平台上，Adobe估计永远无法做到（Adobe今年6月也停止了Linux平台上的AIR开发）；那么现在在移动设备上有了做到的可能，Adobe当然要竭尽全力了。\n","date":"2011-11-10","description":"","lastmod":"2011-11-10T06:13:49Z","slug":"1473","tags":["adobe","air","flashplayer"],"title":"Adobe将停止基于移动设备浏览器的Flash Player开发","url":"https://blog.zengrong.net/post/1473/"},{"categories":["technology"],"content":"前段时间一个C#的同事转AS3，我给了他一些意见，他很顺利的转型了，现在已经可以适应RPG游戏开发。这些意见记录如下:\n1. 了解Flash Platform的全部技术。可以看看这篇文章：Actionscript,AS3,MXML,Flex,Flex Builder,Flash Builder,Flash,AIR,Flash Player之关系\n2. 在下面选择一本书籍在1周内看完：\nFlash ActionScript 3殿堂之路 ActionScript 3.0 Cookbook中文版 ActionScript 3.0编程精髓 这三本从难度上是从易到难，如果是有经验的开发者，建议看第三本《ActionScript 3.0编程精髓》，原因是足够深入。前面两本都很浅。\n3. 看完官方的 ActionScript 3.0 开发人员指南\n建议用1周，看不懂的跳过，碰到问题再来看。\n4. 把官方的 用于 Adobe Flash Platform 的 ActionScript 3.0 参考 浏览一遍，注意选择运行时和产品。\n运行时中，AIR可以暂时不看； 产品中，可以只看Flash Professional CS5.5 和更早版本这个部分。 5. 看完下面两本书：\nFlash ActionScript3.0动画教程 Flash ActionScript3.0动画高级教程 6. 熟悉设计模式（如果以前不熟），建议在下面两本中选择一本（个人偏向第一本）：\nHead First 设计模式 ActionScript 3.0设计模式 7. 熟悉一套UI框架，建议看这套：http://www.minimalcomps.com/ ，然后自己写一套UI框架。\n如果还想了解其他的UI框架，可以看这里：https://blog.zengrong.net/flashassistant/#UI\nFlex是一套设计的非常不错的框架，有空可以看看源码。\n8. 这里还有一篇文章，可以参考一下：有Flex应用基础，做游戏还需要学习什么\n","date":"2011-11-06","description":"","lastmod":"2011-11-06T06:11:28Z","slug":"port2actionscript","tags":["flashplayer","study","as3","fromto"],"title":"怎样快速从其他语言转到Actionscript游戏开发","url":"https://blog.zengrong.net/post/port2actionscript/"},{"categories":["others"],"content":"转自：http://groups.google.com/group/sansi-org/browse_thread/thread/2cd44f047fea89f9?hl=zh-CN\n前几天晚饭间，老华组织在座的12个人玩一个猜数字的游戏，游戏规则是这样的：\n**每人给出一个从0到100之间的数字，把所有人的数字求算术平均值，谁选的数字最接近这个算术平均值的2/3，谁就赢得整场游戏。 **\n这是个很有趣的游戏，建议大家每个人都再仔细读一下题，想一想，试一下，选一个数，写一个理由，然后再往后看。\n分析一下过程\n我们来分析一下这个游戏里的每个人。如果每个人都是真的随机的选择的话，大家平均值应该在50左右。50的2/3应该是33.3，对吗？很多人都写了33.3。（当然还有很多人没有想到这一步）\n不过多想一步，如果你写了33.3，难道其他的人不会想得和你一样，也写33.3吗？如果这样，你应该写22.2。如果继续想下去，大家的平均值应该越来越小，就是这样：\n50 33.33333333 22.22222222 14.81481481 9.87654321 6.58436214 4.38957476 2.926383173 1.950922116 ..... 最后，把问题想得非常地复杂的人的答案是0。\n这是我们那天的结果：\n30 98.16 32 50 12 33.3 22 8 8.2 18 28.68 37 所有的平均数：31.445，它的三分之二是20.96333333。选22的人获胜。\n世界不是由天才创造的\n老华的很多次游戏表明，无论是什么样的群体，最终的获胜的数字，都在22左右徘徊。群体决策的结果和天才的想法总是有些格格不入。这个游戏告诉我们，这个世界不是由天才决定的。在众人决策的过程中，赢得游戏的人，都是比别人多想一步的人，而不是多想两步或更多步的人。游戏中的人这个游戏里面，选择不同的数，或许就代表了不同的人。\n先说选超过66.67的人。在开始游戏的时候，我悄悄对Wendy说，\u0026quot;肯定不会有人选超过66.67的数字的，要是谁要是写了，一定是没动脑子的\u0026quot;。就算是所有人都写100，获胜的数字也才66.67。结果出来，第二个报出的数字就是98.16。我窃笑。他解释写这个数字的原因是因为没听清楚题。慢着，先别就这样放过这个现象。在现实社会里面，没听清楚规则的人不是比比皆是吗？比方说，做产品的人认为质优价廉用户就会买，而实际上，花高价买差产品的人大有人在，我们不能指望所有的用户都和和业内人士有一样的判别力，一样的了解规则，对吗？\n再说选0的。或许这个结果很多人想都想不到，但老华组织的游戏里面，几乎每次都有选零的，而且越理性的群体，选零的比例越高。比如微软研究院30个人里面高达3个人选零。选零的人，沉浸于自己对世界了解的快感中，却知之者甚少。很可惜，在每次游戏里面，比一般人想一步的人就不多，想两步的人更少，经过重重地归迭代到达0的最终境界的人少之又少，我们只好轻叹一声，说，你是天才，但是你赢得不了游戏。或许原本他们在写0的时候，本来也就清楚的知道自己不可能赢得游戏，而他们就是用这种近似自杀的方式向世界宣称，“我放弃获奖，因为我是天才。我可以接受没有奖励，但我不能接受大家不认为我聪明”。我们假想一下，如果天才的理论有机会向每一个参与者传播，让他们理解，跟随天才的选择，说不定他还有一线获胜的机会，不过让每个人了解，从古到今就不曾在天才在世的时候实现过。天才不是疯了，就是穷困潦倒。\n然后我们来说选33.3的。他们是正常的，平凡的人。就像数以百万计的洗发水的使用者或者报纸的阅读者一样，再正常和普通不过。在明白无误的规则面前，按照规则办事，用思考指导行动，却不多想更多。33.3的人是社会的大多数，在他们前面，有引领世界的0和推动世界的22；在他的后面，有大量的选择随机数的更平凡的人。是33们，奠定了这个社会的基调。\n再说赢得游戏的22。他们也遵循规则，但是比规则前进了一步，不多不少，刚刚好一步。他们提出的方案让大多数人（33）感觉的有道理，却不像天才（0）提出的解决方案那么晦涩难懂。我们假设，如果布鲁诺要是发现一个新的号称是绕地球旋转的星星或许能为他赢得终生的荣誉和财富，但如果走得像推翻地心说，宣扬日心说那般的接近真理，得来的就是8年的监禁和熊熊的烈火了。\n社会上除了这些种类，还有很多，在游戏里也在出现。比如说选50的。他在公布答案之前就解释说，“我知道这个数字肯定会非常小，趋近于0，而我就是想说一个大一点的数字，把平均值拉大，看看是会不会左右游戏的结果”。这叫做“搅局的”或者说“损人不利己”的。现实社会里面有吗？大有人在呀。\n天才的悲哀就在于，他搞懂了规则，却没有搞懂人。他自己想明白了，就想当然的以为别人也会想明白。他不但错误的忽略了只想到33的人的存在，更忽略了没有思考的，或者存心不按规则玩的人的存在。毕竟，这个世界不是一个只有天才的世界。\n最后说一说8.2，就是我了。我对8.2的分析是，这个人有一点点天才的倾向，却又不能像选0的天才一样潇洒的放弃冠军的奖励；他希望赢得游戏，却又过高的估计了大众思考的步伐；8.2被天才斥责有太多功利心，却被22嘲笑过于“自作聪明”，算是一个摇摆在理想和现实之间的人。自嘲一下罢了。\n天才的选择对于这个社会，必然有看得比别人稍微透彻些的，离真理更近些的，我们姑且称之为天才吧。这些已经窥探到天机的天才，在现实世界里面，选零还是选22，这是个问题。选零，就注定了要放弃大多数人的认可。这认可可能是名声，可能是钱。选零的人，适合当教授，适合当评论者，不合适自己来做商业。如果你本来想选0，却又为了迎合大众选了22，就注定了你要伪装的傻一些，要被业内人士批判，会被选0或者8的人认为不紧跟潮流。大家看一看现在大凡成功的公司，从美国的软件业网络业巨头们，到中国的门户和成功网站，哪个躲得过选0的人的指指点点？或者说，选22的人是易中天，会用通俗（甚至有些错误）的方式讲史，而选0的人就是严肃的历史学家。通俗文学，流行音乐和热门网站，在大众和同行两个世界里面有完全不同的声名，大多是因为这样。没有选0的人，这个世界何以进步？选零的天才们艰难的拖着这个世界前行。我对他们表示敬佩。只可惜，他们获得的只有一小部分人的敬佩。对于选22个人，帮助了无数选33的人改善了生活，他们也获得了巨大的商业成功。没有22，世界怎么可能从33过渡到更小的数字去呢？我对他们也表示尊敬。\n世界毕竟不是由天才创造的。\n","date":"2011-11-06","description":"","lastmod":"2011-11-06T04:06:29Z","slug":"no-more-genius","tags":[],"title":"【转】世界毕竟不是由天才创造的","url":"https://blog.zengrong.net/post/no-more-genius/"},{"categories":["technology"],"content":"2011-11-04：v0.5.9版发布\n导入图像文件后，支持多选排序，支持“移到顶部/底部”。\n善用此功能，可对最终生成SpriteSheet进行排版，以降低最终文件的尺寸。 预览的背景可以在方格/白色/黑色之间切换，方便查看半透明的动画。 更多的功能介绍以及软件下载，看这里。\n","date":"2011-11-04","description":"","lastmod":"2011-11-04T03:30:15Z","slug":"1468","tags":["air","bitmapdata","spritesheet"],"title":"编辑Sprite Sheet的小工具：sprite sheet editor v0.5.9发布","url":"https://blog.zengrong.net/post/1468/"},{"categories":["technology"],"content":"在JAVA中将透明的图像转换成JPEG格式 在JAVA中将图像文件转换成JPEG，通常使用下面的代码：\n1File __pngFile = new File(\u0026#34;exportUnit.png\u0026#34;); 2File __jpgFile = new File(\u0026#34;exportUnit.jpg\u0026#34;); 3writeJPEG(__pngFile, __jpgFile, 80); 4 5public static void writeJPEG(File $source, File $dest, int $quality) throws IOException 6{ 7\tString __formatName = \u0026#34;jpeg\u0026#34;; 8\tBufferedImage __image = ImageIO.read($source); 9\tImageWriter __writer = ImageIO.getImageWritersByFormatName(__formatName).next(); 10\tImageWriteParam __writeParam = __writer.getDefaultWriteParam(); 11\tFileOutputStream __out = new FileOutputStream($dest); 12\t__writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 13\t__writeParam.setCompressionQuality((float)$quality/100f); 14\t__writer.setOutput(ImageIO.createImageOutputStream(__out)); 15\t__writer.write(null, new IIOImage(__image, null, null), __writeParam); 16\t__out.flush(); 17\t__out.close(); 18\t__writer.dispose(); 19} 但是，当被转换的文件是32位带Alpha通道的图像文件转换为JPEG的时候，会出现颜色错误。就像下面这样：\n转换前的 PNG 图像\n转换后的 JPEG 图像\n出现这种错误的原因，是强行将32位的ARGB图像转换成了24位的RGB图像，转换过程中没有指定正确的色彩转换方式。下面的2种方法都可以解决这个问题。\n要使用这2种方法，可以在writeJPEG方法中加入下面的判断：\n1//如果图像是透明的，就丢弃Alpha通道 2if(__image.getTransparency() == Transparency.TRANSLUCENT) 3\t__image = get24BitImage(__image); 4\t//__image = get24BitImage(__image, Color.BLACK); 第一种方法，删除32位ARGB颜色中的Alpha通道 根据ARGB的颜色表示法，32位颜色的前8位代表Alpha值。因此只需要将前8位去掉，就得到了不带Alpha通道的24位颜色值。详见下面的代码：\n1/** 2 * 使用删除alpha值的方式去掉图像的alpha通道 3 * @param $image 4 * @return 5 */ 6protected static BufferedImage get24BitImage(BufferedImage $image) 7{ 8\tint __w = $image.getWidth(); 9\tint __h = $image.getHeight(); 10\tint[] __imgARGB = getRGBs($image.getRGB(0, 0, __w, __h, null, 0, __w)); 11\tBufferedImage __newImg = new BufferedImage(__w, __h, BufferedImage.TYPE_INT_RGB); 12\t__newImg.setRGB(0, 0, __w, __h, __imgARGB, 0, __w); 13\treturn __newImg; 14} 15 16/** 17 * 将32位色彩转换成24位色彩（丢弃Alpha通道） 18 * @param $argb 19 * @return 20 */ 21public static int[] getRGBs(int[] $argb) 22{ 23\tint[] __rgbs = new int[$argb.length]; 24\tfor(int i=0;i\u0026lt;$argb.length;i++) 25\t{ 26\t__rgbs[i] = $argb[i] \u0026amp; 0xFFFFFF; 27\t} 28\treturn __rgbs; 29} 第二种方法，使用绘制的方式去掉图像的alpha值 使用这种方法的优势在于，可以很方便的控制生成的JPEG图像的背景色。\n1/** 2 * 使用绘制的方式去掉图像的alpha值 3 * @param $image 4 * @param $bgColor 5 * @return 6 */ 7protected static BufferedImage get24BitImage(BufferedImage $image, Color $bgColor) 8{ 9\tint $w = $image.getWidth(); 10\tint $h = $image.getHeight(); 11\tBufferedImage __image = new BufferedImage($w, $h, BufferedImage.TYPE_INT_RGB); 12\tGraphics2D __graphic = __image.createGraphics(); 13\t__graphic.setColor($bgColor); 14\t__graphic.fillRect(0,0,$w,$h); 15\t__graphic.drawRenderedImage($image, null); 16\t__graphic.dispose(); 17\treturn __image; 18} 完整的代码 1import java.awt.Color; 2import java.awt.Graphics2D; 3import java.awt.Transparency; 4import java.awt.image.BufferedImage; 5import java.io.File; 6import java.io.FileOutputStream; 7import java.io.IOException; 8 9import javax.imageio.IIOImage; 10import javax.imageio.ImageIO; 11import javax.imageio.ImageWriteParam; 12import javax.imageio.ImageWriter; 13 14public class ConvertJPEG 15{ 16 17\t/** 18\t* @param args 19\t* @throws IOException 20\t*/ 21\tpublic static void main(String[] args) throws IOException 22\t{ 23\tFile __pngFile = new File(\u0026#34;exportUnit.png\u0026#34;); 24\tFile __jpgFile = new File(\u0026#34;exportUnit.jpg\u0026#34;); 25\twriteJPEG(__pngFile, __jpgFile, 80); 26\t} 27\t28\tpublic static void writeJPEG(File $source, File $dest, int $quality) throws IOException 29\t{ 30\tString __formatName = \u0026#34;jpeg\u0026#34;; 31\tBufferedImage __image = ImageIO.read($source); 32\t//如果图像是透明的，就丢弃Alpha通道 33\tif(__image.getTransparency() == Transparency.TRANSLUCENT) 34\t__image = get24BitImage(__image); 35\t//__image = get24BitImage(__image, Color.BLACK); 36\tImageWriter __writer = ImageIO.getImageWritersByFormatName(__formatName).next(); 37\tImageWriteParam __writeParam = __writer.getDefaultWriteParam(); 38\tFileOutputStream __out = new FileOutputStream($dest); 39\t__writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 40\t__writeParam.setCompressionQuality((float)$quality/100f); 41\t__writer.setOutput(ImageIO.createImageOutputStream(__out)); 42\t__writer.write(null, new IIOImage(__image, null, null), __writeParam); 43\t__out.flush(); 44\t__out.close(); 45\t__writer.dispose(); 46\t} 47\t48\t/** 49\t* 使用删除alpha值的方式去掉图像的alpha通道 50\t* @param $image 51\t* @return 52\t*/ 53\tprotected static BufferedImage get24BitImage(BufferedImage $image) 54\t{ 55\tint __w = $image.getWidth(); 56\tint __h = $image.getHeight(); 57\tint[] __imgARGB = getRGBs($image.getRGB(0, 0, __w, __h, null, 0, __w)); 58\tBufferedImage __newImg = new BufferedImage(__w, __h, BufferedImage.TYPE_INT_RGB); 59\t__newImg.setRGB(0, 0, __w, __h, __imgARGB, 0, __w); 60\treturn __newImg; 61\t} 62\t63\t/** 64\t* 使用绘制的方式去掉图像的alpha值 65\t* @param $image 66\t* @param $bgColor 67\t* @return 68\t*/ 69\tprotected static BufferedImage get24BitImage(BufferedImage $image, Color $bgColor) 70\t{ 71\tint $w = $image.getWidth(); 72\tint $h = $image.getHeight(); 73\tBufferedImage __image = new BufferedImage($w, $h, BufferedImage.TYPE_INT_RGB); 74\tGraphics2D __graphic = __image.createGraphics(); 75\t__graphic.setColor($bgColor); 76\t__graphic.fillRect(0,0,$w,$h); 77\t__graphic.drawRenderedImage($image, null); 78\t__graphic.dispose(); 79\treturn __image; 80\t} 81\t82\t/** 83\t* 将32位色彩转换成24位色彩（丢弃Alpha通道） 84\t* @param $argb 85\t* @return 86\t*/ 87\tpublic static int[] getRGBs(int[] $argb) 88\t{ 89\tint[] __rgbs = new int[$argb.length]; 90\tfor(int i=0;i\u0026lt;$argb.length;i++) 91\t{ 92\t__rgbs[i] = $argb[i] \u0026amp; 0xFFFFFF; 93\t} 94\treturn __rgbs; 95\t} 96} ","date":"2011-10-18","description":"","lastmod":"2011-10-18T02:28:21Z","slug":"convert_transparent_image_to_jpg_in_java","tags":["image","java"],"title":"在JAVA中将透明的图像转换成JPEG格式","url":"https://blog.zengrong.net/post/convert_transparent_image_to_jpg_in_java/"},{"categories":["technology"],"content":"关于快速启动，我最早使用的是Win+R这种绿色的方式。但由于要记忆大量的自定义文件名，且不支持模糊查找，我改用了 Find and Run Robot (FARR) 。\n但突然有一天，我感觉FARR实在是太慢了……\n于是我改用 TypeAndRun 。\n原来用FARR的时候，我是把一堆常用的快捷方式放在一个文件夹中，然后在FARR中将这个文件夹加入索引位置，以实现快速启动。4年多来，这个文件夹中有88个快捷方式。\n现在Type And Run要求将所有的自定义别名加入到Config.ini中。一个个手动加实在是太痛苦了，我用Vim写了个脚本实现它：\n1\u0026#34; 获取当前路径下的所有文件 2let s:links = glob(getcwd().\u0026#39;/**\u0026#39;) 3\u0026#34; 使用换行符作为分隔符，将所有路径转换成列表 4let s:linklist = split(s:links, \u0026#34;\\\u0026#34;) 5\u0026#34; 在列表中循环处理 6for linkstr in s:linklist 7 \u0026#34; 获取快捷方式的真实路径 8 let s:path = resolve(linkstr) 9 \u0026#34; 获取快捷方式的主文件名 10 let s:substr = matchstr(linkstr, \u0026#39;\\w\\+\\.lnk\u0026#39;) 11 \u0026#34; 去掉住文件名中包含的扩展名 12 let s:mainname = strpart(s:substr, 0, strlen(s:substr)-4) 13 \u0026#34; 按照TypeAndRun的Config.ini文件的格式插入当前缓冲区 14 exec \u0026#34;normal o\\i\u0026#34;.s:mainname.\u0026#34;|\u0026#34;.s:path 15endfor 如果希望将某个目录下的所有exe文件（包括子目录）加入Config.ini中，也只需要稍稍改一下上面的代码即可。当然，这种需求还可以使用 Total Commander 实现，更加方便。\n","date":"2011-10-17","description":"","lastmod":"2011-10-17T14:48:23Z","slug":"vim-convert-typeandrun-config","tags":["vim"],"title":"使用Vim将批量将快捷方式转换为TypeAndRun的Config.ini格式","url":"https://blog.zengrong.net/post/vim-convert-typeandrun-config/"},{"categories":["news"],"content":" 虚幻引擎（Unreal）已支持Flash Player 11 Adobe推出3D框架Proscenium Adobe将大力支持Alchemy CryEngine(孤岛危机引擎)可能将支持Flash Player 11 Mandreel Framework,C++ 2 AS3框架 ","date":"2011-10-08","description":"","lastmod":"2011-10-08T02:10:03Z","slug":"flash-player-11-messages","tags":["3d","flash"],"title":"Flash Player 11-真实的和不真实的消息","url":"https://blog.zengrong.net/post/flash-player-11-messages/"},{"categories":["technology"],"content":"在Sprite Sheep Editor中，使用了这样的一个小技术（思路来自这里）：将透明图像的Alpha通道转换成黑白（灰度）图像，然后与正常图像拼合成一张大图，再存储成JPEG格式。\n这方法其实是一个折衷方案。因为JPEG格式是不支持透明的，很多时候为了获得透明效果，我们只能使用PNG格式。但PNG是无损压缩的，在图像尺寸上没有优势。如果将Alpha通道转换成黑白图并保存到JPEG图像中，就能大幅降低最终的图像文件大小。\n在我的测试中，一个2000x3300的32位带Alpha通道的PNG图像文件大小为2MB，转换为4000x3300（因为拼合了黑白图片，宽度增加一倍）70%压缩比的JPEG后，文件大小为1.1MB。\n当然，转换后的JPEG文件画质比PNG要稍差一些。这可以通过调整压缩比得到一定程度的改善。\n要将图像的Alpha通道转换为黑白图像，在AS3中很容易：\n1var __p:Point = new Point(0,0); 2_channelBmd = new BitmapData(_bmd.width, _bmd.height, true, 0x00000000); 3_channelBmd.fillRect(_bmd.rect, 0xff000000); 4_channelBmd.copyChannel(_bmd, _bmd.rect, __p, 8, 1); 5_channelBmd.copyChannel(_bmd, _bmd.rect, __p, 8, 2); 6_channelBmd.copyChannel(_bmd, _bmd.rect, __p, 8, 4) 这些代码将 _bmd的Alpha通道信息分别复制到 _channelBmd 的红绿蓝通道中，最终合成了一张代表Alpha通道的灰度图。\n但是，并不是所有语言都有“通道”这个概念。其实AS3中的“通道”（包括Photoshop中的）就是在颜色中的红绿蓝值而已，我们可以手工把ARGB颜色中的Alpha分离出来，将它们组成RGB颜色。这是AS3的实现：\n1_pixelBmd = new BitmapData(_bmd.width, _bmd.height, false); 2for(var i:int=0;i\u0026lt;_bmd.height;i++) 3{ 4 var __str:String = \u0026#39;\u0026#39;; 5 for(var j:int=0;j\u0026lt;_bmd.width;j++) 6 { 7 var __pixel:int = _bmd.getPixel32(j,i); 8 //分离ARGB颜色中的alpha值，alpha处于32位的最前面8位 9 var __alpha:int = $argb \u0026gt;\u0026gt; 24 \u0026amp; 0xFF; 10 var __argbstr:String = __alpha == 0 ? \u0026#39;00\u0026#39; : __alpha.toString(16); 11 __str += __argbstr + \u0026#39; \u0026#39;; 12 //利用alpha的值合并成RGB颜色值，因为不需要透明，因此24位即可 13 __pixel = __alpha \u0026lt;\u0026lt; 16 | __alpha \u0026lt;\u0026lt; 8 | __alpha; 14 _pixelBmd.setPixel(j,i, __pixel); 15 } 16 //如果你的屏幕足够宽，可以看到一个字符画，内容你猜猜？ 17 trace(__str); 18} 这是JAVA的实现，当然，也可以用RGBImageFilter来做这件事：\n1public BufferedImage convertImage(BufferedImage $image) 2{ 3 _img = $image; 4 _w = _img.getWidth(); 5 _h = _img.getHeight(); 6 _sourceARGB = _img.getRGB(0, 0, _w, _h, null, 0, _w); 7 8 BufferedImage __image = new BufferedImage(_w, _h, BufferedImage.TYPE_INT_RGB); 9 int[] __destARGB = new int[_sourceARGB.length]; 10 System.out.println(\u0026#34;所有数组的长度：\u0026#34;+_sourceARGB.length); 11 for(int i=0;i\u0026lt;_sourceARGB.length;i++) 12 { 13 int __alpha = _sourceARGB[i] \u0026gt;\u0026gt; 24 \u0026amp; 0xFF; 14 __destARGB[i] = __alpha \u0026lt;\u0026lt; 16 | __alpha \u0026lt;\u0026lt; 8 | __alpha; 15 } 16 __image.setRGB(0, 0, _w, _h, __destARGB, 0, _w); 17 return __image; 18} 一个AS3实现的范例：\n1 文件 ","date":"2011-09-27","description":"","lastmod":"2011-09-27T01:56:56Z","slug":"convert-transparent-color-to-alpha-channel","tags":["as3","bitmapdata","java"],"title":"将图像的透明区域转换为Alpha通道原理","url":"https://blog.zengrong.net/post/convert-transparent-color-to-alpha-channel/"},{"categories":["technology"],"content":"2011-09-21:Adobe已经修正了我提交的这个bug，并在安全更新最后做出了感谢。但并非在v11中修正的，而是在v10.3.183.10中修正，安全更新在这里：http://www.adobe.com/support/security/bulletins/apsb11-26.html。所以我先前的推论可能不正确。但我想，Adobe很可能在10月3日的MAX大会上发布Flash Player 11。\n今天Adobe给我发来邮件，告知我原来提交的关于FlashPlayer在执行NetStream.play的时候崩溃的问题已经解决，并询问我是否要加入安全公告致谢部分：\nThank you again for sending us the Flash Player Netstream issue. This issue should be resolved in the upcoming release of Adobe Flash, currently planned for the next couple of weeks. Please let us know how you would like to be included in the Acknowledgements section of the upcoming Security Bulletin:\n这样，也就说明Flash Player 11应该是在两周后发布。\n","date":"2011-09-17","description":"","lastmod":"2011-09-17T03:20:17Z","slug":"flash-player-11-is-going-release-after-two-weeks","tags":["flashplayer","netstream"],"title":"Flash Player 11应该在两周后发布","url":"https://blog.zengrong.net/post/flash-player-11-is-going-release-after-two-weeks/"},{"categories":["technology"],"content":"Sprite Sheet Editor 0.5.6有一个很重大的Bug，在保存metadata的时候，我将frame的ow/oh属性保存成了与w/h属性相同的值。\n根据Sprite Sheet Editor修剪每帧中的空白区域的原理说明可以知道，对于剪切过空白的帧来说，ow和oh是还原原始帧大小的关键。如果这两个值出错，会导致无法取得动画的正确尺寸。\n好在这个是可以回溯的。可以通过比较某个Label包含的所有帧的尺寸，通过ox/oy和w/h计算出每帧的实际尺寸，最大的那一个，就是该Label中的所有帧的统一ow/oh属性值。\n使用这个方法，得到的实际值，甚至比原来通过Sprite Sheet Editor进行手工设定的值更小，也就是更加节省内存。\n选择什么工具处理？AS3有强悍的E4X，JAVA和BASH也不错。但我正好想学习下Vim脚本，就用它了！\n处理思路：\nmetadata中的 \u0026lt;labels\u0026gt;标签中保存所有Label的帧索引； 取得每个Label的帧索引，获取每帧的行号，保存； 针对每个Label进行处理，通过Label中每帧的ox/oy/w/h计算出ow/oh； 记录每个Label中最大的ow/oh； 用最大的ow/oh替换该Label中所有帧的ow/oh； 循环处理所有Label。 错误的XML文件\n1\u0026#34; 转换Sprite Sheet Editor 0.5.6版生成的错误XML文件 2\u0026#34; zrong (zrongzrong@gmail.com) 2011-09-02 3 4\u0026#34; 获取当前目录下的所有xml文件 5let xmls = split(glob(\u0026#34;*.xml\u0026#34;), \u0026#34;\\\u0026#34;) 6\u0026#34; 执行批量修复工作 7for b:xml in xmls 8 echom \u0026#39;progress \u0026#39;.b:xml 9 call RepareWH(b:xml) 10endfor 11 12\u0026#34; 修复file参数提供的xml文件 13function! RepareWH(file) 14 exec \u0026#39;sp \u0026#39;.a:file 15 normal gg 16 let labelStart = search(\u0026#39;\u0026#39;)+1 17 \u0026#34; 找不到Label，处理所有帧 18 if labelStart \u0026lt;= 1 19 echom \u0026#39;label not found\u0026#39; 20 let framesLine = GetFrames() 21 call WriteFrames(range(len(framesLine)), framesLine) 22 update 23 close 24 return 25 endif 26 let labelEnd = search(\u0026#39;\u0026lt;\\/labels\u0026gt;\u0026#39;)-1 27 let labels = [] 28 \u0026#34; 将所有的label名称与对应的帧索引写入一个List 29 \u0026#34; List中的每一项是一个字典，name是label名称，index帧索引的数组（字符串形式保存） 30 for i in range(labelStart, labelEnd) 31 let labelMatch = matchlist(getline(i), \u0026#39;\u0026lt;\\(\\w\\+\\)\u0026gt;\\([0-9,]\\+\\)\u0026#39;) 32 call add(labels, {\u0026#39;name\u0026#39;:labelMatch[1], \u0026#39;index\u0026#39;:split(labelMatch[2], \u0026#39;,\u0026#39;)}) 33 endfor 34 normal gg 35 let framesLine = GetFrames() 36 echom \u0026#39;find \u0026#39;.len(framesLine).\u0026#39; frames\u0026#39; 37 \u0026#34; 修复所有的label 38 for label in labels 39 echo \u0026#39;progress label \u0026#39;.label.name 40 call WriteFrames(label.index, framesLine) 41 endfor 42 update 43 close 44endfunction 45 46\u0026#34; 将所有的frame块转换成字典，并存入list 47function! GetFrames() 48 let framesLine = [] 49 while search(\u0026#39;\u0026#39;,\u0026#39;\u0026#39;,line(\u0026#39;$\u0026#39;)) 50 let frameStart = line(\u0026#34;.\u0026#34;)+1 51 let frameEnd = search(\u0026#39;\u0026lt;\\/frame\u0026gt;\u0026#39;)-1 52 call add(framesLine, ParseFrame(frameStart, frameEnd)) 53 endwhile 54 return framesLine 55endfunction 56 57\u0026#34; 将一帧的信息转换成字典，并返回 58function! ParseFrame(start, end) 59 let aFrame = {} 60 let reg = \u0026#39;\u0026lt;\\(\\w\\+\\)\u0026gt;\\([-0-9]\\+\\)\u0026#39; 61 for lineIndex in range(a:start, a:end) 62 let matchFrame = matchlist(getline(lineIndex), reg) 63 let aFrame[matchFrame[1]] = str2nr(matchFrame[2]) 64 \u0026#34; 写入ow和oh的行号方便后面替换 65 if matchFrame[1] =~ \u0026#39;ow\u0026#39; 66 let aFrame.owl = lineIndex 67 endif 68 if matchFrame[1] =~ \u0026#39;oh\u0026#39; 69 let aFrame.ohl = lineIndex 70 endif 71 endfor 72 return aFrame 73endfunction 74 75\u0026#34; 计算真实尺寸的最大值，将最大值写入这一组帧 76function! WriteFrames(frameIndexs, framesLine) 77 let owMax = 0 78 let ohMax = 0 79 for index in a:frameIndexs 80 \u0026#34;echo \u0026#39;== progress frame \u0026#39;.index 81 let l:frame = a:framesLine[str2nr(index)] 82 \u0026#34; 如果修剪尺寸小于原始尺寸，则说明该帧没有问题 83 if l:frame.w \u0026lt; l:frame.ow \u0026amp;\u0026amp; l:frame.h \u0026lt; l:frame.oh 84 \u0026#34;echom \u0026#39;this frame \u0026#39;.index.\u0026#39; is not problem\u0026#39; 85 continue 86 endif 87 \u0026#34; 真实的帧大小，是用修剪大小减去偏移值 88 let trueW = l:frame.w - l:frame.ox 89 let trueH = l:frame.h - l:frame.oy 90 \u0026#34; 更新真实帧大小的最大值 91 if trueW \u0026gt; owMax 92 let owMax = trueW 93 endif 94 if trueH \u0026gt; ohMax 95 let ohMax = trueH 96 endif 97 endfor 98 if owMax \u0026gt; 0 \u0026amp;\u0026amp; ohMax \u0026gt; 0 99 \u0026#34; 写入帧的真实大小 100 for index in a:frameIndexs 101 let l:frame = a:framesLine[str2nr(index)] 102 let ows = substitute(getline(l:frame.owl), \u0026#39;\\d\\+\u0026#39;, owMax, \u0026#39;\u0026#39;) 103 let ohs = substitute(getline(l:frame.ohl), \u0026#39;\\d\\+\u0026#39;, ohMax, \u0026#39;\u0026#39;) 104 echo \u0026#39;== write frame \u0026#39;.index.\u0026#39; line:\u0026#39;.l:frame.owl.\u0026#39;,\u0026#39;.l:frame.ohl.\u0026#39; w:\u0026#39;.owMax.\u0026#39; h:\u0026#39;.ohMax 105 call setline(l:frame.owl, ows) 106 call setline(l:frame.ohl, ohs) 107 endfor 108 endif 109endfunction ","date":"2011-09-13","description":"","lastmod":"2011-09-13T07:40:53Z","slug":"fix-sse-056-xml-bug-with-vim","tags":["spritesheet","vim","xml"],"title":"使用Vim修复Sprite Sheet Editor 0.5.6版生成的错误XML文件","url":"https://blog.zengrong.net/post/fix-sse-056-xml-bug-with-vim/"},{"categories":["technology"],"content":"转自 蜗牛的博客\nmatrix是一个长度为4*5＝20的数组，其构成如下所示：\nR ,G, B, A, offset [1, 0, 0, 0, 0]); // red [0, 1, 0, 0, 0]); // green [0, 0, 1, 0, 0]); // blue [0, 0, 0, 1, 0]); // alpha 上面是matrix的初始状态。\n下面我分先来分析一下其初始状态。\nred通道的值：（1，0，0，0，0）表示，R通道的乘数是1（完全保留），别的道道的的乘数是0，（不加入别的通道的颜色），色彩偏移量off是0；\n别的通道依次类推。\n下面来做一些效果，增加对colorMatrixFilter的认识：\n1、调整亮度： 亮度(N取值为-255到255)\n1,0,0,0,N 0,1,0,0,N 0,0,1,0,N 0,0,0,1,0 我们只需要设置一下RGB的色彩偏移就能调节其亮度，是不是很简单呢。\n2、颜色反向 -1,0,0,0,255 0,-1,0,0,255 0,0,-1,0,255 0,0,0,1,0 先解释一下颜色反向：就是把0变为255，255变为0，1变为254，254变为1.....\n因此，我们只需把RGB通道的原通道乘数设为－1，然后再把色彩偏移量设为255就行了。\n3、图像去色： 0.3086, 0.6094, 0.0820, 0, 0 0.3086, 0.6094, 0.0820, 0, 0 0.3086, 0.6094, 0.0820, 0, 0 0 , 0 , 0 , 1, 0 首先了解一下去色原理：只要把RGB三通道的色彩信息设置成一样；即：R＝G＝B，那么图像就变成了灰色，并且，为了保证图像亮度不变，同一个通道中的R+G+B=1:如：0.3086+0.6094+0.0820＝1； 三个数字的由来：0.3086, 0.6094, 0.0820； 按理说应该把RGB平分，都是0.3333333。三个数字应该是根据色彩光波频率及色彩心理学计算出来的（本人是这么认为，当然也查询了一些资料，目前尚未找到准确答案）。\n在作用于人眼的光线中，彩色光要明显强于无色光。对一个图像按RGB平分理论给图像去色的话，人眼就会明显感觉到图像变暗了（当然可能有心理上的原因，也有光波的科学依据）另外，在彩色图像中能识别的一下细节也可能会丢失。我假想：可能绿色的一些东西会丢失。\n下面是我从PS中对RGB都为255的明度对比图：\n同样的RGB，给人的感觉是绿色最亮，红色次之，蓝色最暗。它们的比例大概是3：6：1，即：0.3086, 0.6094, 0.0820\n所以，在给图像去色时我们保留了大量的G通道信息，使得图像不至于变暗或者绿色信息不至于丢失（我猜想）。\n4、色彩饱和度 N取值为0到2，当然也可以更高。\n0.3086*(1-N) + N, 0.6094*(1-N) , 0.0820*(1-N) , 0, 0, 0.3086*(1-N) , 0.6094*(1-N) + N, 0.0820*(1-N) , 0, 0, 0.3086*(1-N) , 0.6094*(1-N) , 0.0820*(1-N) + N 0, 0, 0 , 0 , 0 , 1, 0 分析：\n当色彩饱和度低到一定成度的时候，就想当于给图像去色，所以跟第3条：图像去色，有着千丝万缕的联系，在此不想过多解释； N为原有通道信息保留量；可以理解为百分之几，等于0时完全去色，小于1时降低色度，大于1时增加色度，等于2时色度翻一倍，等于3时……。注意：RGB的原有通道信息保留量都应该相等，不然会产生偏色。 为什么是这样的计算公式： N是原通道色彩保留量：所以，在原通道中，我们都+ N，这是不能被别的通道瓜分的。剩余的就是（1－N），就让RGB按0.3086, 0.6094, 0.0820的比例还瓜分这个剩余量吧。\n5、对比度 N取值为0到10\nN,0,0,0,128*(1-N) 0,N,0,0,128*(1-N) 0,0,N,0,128*(1-N) 0,0,0,1,0 分析：\n所谓对比度就是让红的更红，绿的更绿……或反之。初一想，我们只需要修改RGB的乘数（要一至，不然偏色）。可仔细一琢磨，不对。如果只增加乘数，那么整个图像就会被漂白，（或反之）。好，有办法了，设置色彩偏移量，offset。具体要偏移多少呢，我们找到了一个折中的方案：128（1－N);即：一幅图像，不论很亮或很黑，但对比度为0了，最终得到的都是一幅中性灰度的图像（128），\n6、阈值 所谓阈值，就是以一个色度值为基准对图像作非黑即白的处理（注意没有灰色），由于不去除了彩色属性，因此，也离不开0.3086, 0.6094, 0.0820这三组神奇的数字。\n(N取值为0到255)\n下面的256也可以改成255；（那样就能看到图一和图五的小黑点和小白点）；\n0.3086*256,0.6094*256,0.0820*256,0,-256*N 0.3086*256,0.6094*256,0.0820*256,0,-256*N 0.3086*256,0.6094*256,0.0820*256,0,-256*N 0, 0, 0, 1, 0 分析：\n先不看最后面的色彩偏移：-256*N\n前面我们提及过，当RGB三个通道的色彩信息一模一样时，图像就失去了色彩（去色），从 0.3086*256,0.6094*256,0.0820*256,0,-256*N 可以看出：图像已经去色了，并且，（*256） 亮度已经翻了256倍（当然也可以是255）；我们知道，RGB的有效值是0－255，即：0，1，2……255，把这些值乘以255以后会出现什么情况呢？但是除了0之外，别的全都大于或等于255了，所以此时的图像除了剩有几个黑点外，其它的全都变成白色了如图一（N＝0）；那么现在我们再作色彩偏移处理：把RGB都减去255；上次值为255（白色）的现在又变成0（黑色了）超过255的仍然是白色，我们不断的反复减255，图2，图3，图4，图5，分别是 N＝64，N＝128，n=192,n=255 时的图像：\n7、色彩旋转 所谓色彩旋转就是让某一个通道的色彩信息让另一个通道去显示；比如，R显示G的信息，G显示B的信息，B显示R的信息，也可以只拿出一部份信息让给别的通道去显示，至于参数的瓜分可以平分。不必太讲究，但是，始终要坚持的一个原则就是每一个通道中的RGB信息量之和一定要为1，不然将会生偏色，如果您要制作偏色效果又另当别论；请偿试下面的参数：\n0,1,0,0,0 0,0,1,0,0 1,0,0,0,0 0,0,0,1,0 //--------------- 0,0,1,0,0 1,0,0,0,0 0,1,0,0,0 0,0,0,1,0 8、只显示某个通道； 1,0,0,0,0 0,0,0,0,0 0,0,0,0,0 0,0,0,1,0 上面是只显示红色通道。依次类推。\n","date":"2011-09-09","description":"","lastmod":"2011-09-09T06:20:12Z","slug":"colormatrixfilter-in-as3","tags":["actionscript","color","filter"],"title":"【转】ColorMatrixFilter色彩矩阵滤镜","url":"https://blog.zengrong.net/post/colormatrixfilter-in-as3/"},{"categories":["technology"],"content":"AIR看来一直想脱掉玩具的帽子，继AIR2开始支持本地进程通信后，AIR3又开始支持本地插件了，这真是个好消息。具体的支持情况是这样的：\nAndroid平台：Java包文件 (.jar) 或共享库文件(.so) iOS：静态库(.a) OS X ：Framework (.framework) Windows：动态链接库(.dll) 有了这个东东，可以说AIR真是如猫添翼啊！\n详细介绍（英文）：http://www.adobe.com/devnet/air/articles/extending-air.html\n","date":"2011-09-08","description":"","lastmod":"2011-09-08T01:31:42Z","slug":"air3-extensions","tags":["air"],"title":"AIR3开始支持外部扩展","url":"https://blog.zengrong.net/post/air3-extensions/"},{"categories":["technology"],"content":"使用Embed标签在AS3项目中嵌入字体\n关于嵌入字体，其实 Embed fonts 这篇文章已经很详细的介绍了。但这篇文章有这样几个问题：\n它是针对Flex开发者的，纯AS开发者看起来未免不爽； 没有讲解怎么使用在Flash IDE中嵌入的字体； 嵌入字体就那么点东西，其实不值得花功夫读这一大篇鸟语（介是偷懒……） 那我就把要点总结下……\n配合Flash IDE使用 我现在开始讨厌Flash IDE，因为它实在太慢，而且跳版本不兼容。但如果希望可视化的控制嵌入的字体中的文本，还必须使用Flash CS5。\n下面是Flash CS5嵌入字体的两张截图。\n选择要嵌入的文本\n这个界面很直观，但其实我们可以用更直观的方式（控制unicodeRange的方式，当然，这个“直观”只是对程序员来说），后面会讲到。\n选择要嵌入的方式\n图中的“传统”和“TLF”分别对应你的字体是用于TextField还是用于FlashPlayer 10支持的 FTE 引擎。这两者是互斥的，不能选错。\n如果在输出属性面板中选择的目标是Flash Player 9，那么在这里是不能选择嵌入方式的。\n输出后的swf，使用下面的语法嵌入：\n1[Embed(source=\u0026#34;myFont.swf\u0026#34;,fontFamily=\u0026#34;04b_08\u0026#34;] 2public var myFont:Class; 其中的 fontFamily就是双击嵌入的字体打开的的字体预览中显示的全名。例如，微软雅黑的fontFamily的值为 Microsoft YaHei。\n这里要注意的是，不要使用这种错误的语法来嵌入字体：\n1[Embed(source=\u0026#34;myFont.swf\u0026#34;,symbol=\u0026#34;MyFont\u0026#34;] 2public var myFont:Class; 这种语法一般会得到一个编译错误，因为 MyFont并不是一个Symbol，这种语法只能用于在Flash IDE中导出的MovieClip或者图像、声音等资源。\n在纯AS3项目中嵌入字体 如果你和我一样讨厌Flash IDE的话，那不如直接在AS3代码中嵌入TTF字体好了，语法如下：\n1[Embed(source=\u0026#34;04b_08__.ttf\u0026#34;,fontName=\u0026#34;04b_08\u0026#34;,embedAsCFF=\u0026#34;false\u0026#34;,unicodeRange=\u0026#34;U+0020,U+0041-005A,U+0020,U+0061-007A,U+0030-0039,U+002E,U+0020-002F,U+003A-0040,U+005B-0060,U+007B-007E,U+0020-002F,U+0030-0039,U+003A-0040,U+0041-005A,U+005B-0060,U+0061-007A,U+007B-007E\u0026#34;)] 2public var Font04b08:Class; 3public var myFont:Class; 这一串代码代表的含义是：\n使用传统方式（就是上面图2中提到的“传统”）嵌入字体名为04b_08的TTF字体中的英文、数字和标点符号，包含大小写。\n来看看这四个常用参数的作用吧：\nsource\t指定要嵌入的字体文件路径。还可以用 systemFont指定一个系统中安装的字体。这样的话就可以不需要 source参数了。 fontName\t这个实际上就是 fontFamily 的别名。 embedAsCFF\t如果不提供这个参数，默认就是true。所以，如果系统你嵌入的字体用于TextField，一定要将其设置为false。 unicodeRange\t要嵌入的文本的范围。见下表： 嵌入字体范围：\n大写字符\tU+0020,U+0041-U+005A 小写字符\tU+0020,U+0061-U+007A 数字\tU+0030-U+0039,U+002E 标点符号\tU+0020-U+002F,U+003A-U+0040,U+005B-U+0060,U+007B-U+007E 基本拉丁字符\tU+0020-U+002F, U+0030-U+0039, U+003A-U+0040, U+0041-U+005A, U+005B-U+0060, U+0061-U+007A, U+007B-U+007E 当然，还有中文范围等等，详细的可以找到你本机的 FlexSDK/frameworks/flash-unicode-table.xml 看看就明白了。还可以参考Setting character ranges。\n那么，怎样制作一个只有嵌入字体数据的swf文件呢？有两种方法：\n第一种，使用下面的范例代码。下面的代码嵌入了两个字体，使用的范围和上面的例子一样。\n有趣的是，如果你在嵌入字体的这个SWF文件中使用嵌入的字体（额……我知道有点拗口），你不需要注册这个字体。如果查看注册字体列表，你会发现它已经注册过了。运行下面的代码就知道了。\n1package 2{ 3import flash.display.Sprite; 4import flash.text.Font; 5public class fonts extends Sprite 6{ 7\t[Embed(source=\u0026#34;04b_08__.ttf\u0026#34;,fontName=\u0026#34;04b_08\u0026#34;,embedAsCFF=\u0026#34;false\u0026#34;,unicodeRange=\u0026#34;U+0020,U+0041-005A,U+0020,U+0061-007A,U+0030-0039,U+002E,U+0020-002F,U+003A-0040,U+005B-0060,U+007B-007E,U+0020-002F,U+0030-0039,U+003A-0040,U+0041-005A,U+005B-0060,U+0061-007A,U+007B-007E\u0026#34;)] 8\tpublic var Font04b08:Class; 9\t[Embed(source=\u0026#34;Frabk.ttf\u0026#34;,fontFamily=\u0026#34;Franklin Gothic Book\u0026#34;,embedAsCFF=\u0026#34;false\u0026#34;,unicodeRange=\u0026#34;U+0020,U+0041-005A,U+0020,U+0061-007A,U+0030-0039,U+002E,U+0020-002F,U+003A-0040,U+005B-0060,U+007B-007E,U+0020-002F,U+0030-0039,U+003A-0040,U+0041-005A,U+005B-0060,U+0061-007A,U+007B-007E\u0026#34;)] 10\tpublic var FontFrabk:Class; 11 12\tpublic function fonts() 13\t{ 14\tvar __fontArr:Array= Font.enumerateFonts(false); 15\t//Font.registerFont(myFont); 16\tfor each(var __font:Font in __fontArr) 17\ttrace(__font.fontName, __font.fontType); 18\t} 19} 20} 第二种，使用Flex SDK提供的fontswf工具，这个工具位于 FlexSDK/bin文件夹下，是JAVA开发的命令行工具。具体用法和参数与Embed类似，看Using the fontswf utility就清楚了。\n载入外部的字体文件 当然，你不应该载入ttf文件。应该先使用上面介绍的方法把字体做成swf文件，然后载入。\n使用Loader载入外部的swf文件后，需要获取到该swf中嵌入的字体的Class，然后使用 Font.registerFont 注册这个字体，注册成功后就可以使用了。\n获取swf中嵌入的字体的Class，也有两种方法：\n第一种，载入成功后，使用Loader.content获取到载入的swf的root，然后直接通过该swf中定义的public变量获取到类定义：\n1var __font:* = _loader.content; 2trace(\u0026#39;04b08:\u0026#39;, __font.Font04b08); 3 4Font.registerFont(__font.Font04b08); 5Font.registerFont(__font.FontFrabk); 第二种，载入成功后，使用 ApplicationDomain.getDefinition 获取嵌入的字体类，类的名称是“源文件类名_嵌入目标变量名称”：\n1var __fontClass:Class = _loader.contentLoaderInfo.applicationDomain.getDefinition(\u0026#34;fonts_Font04b08\u0026#34;) as Class; 2trace(\u0026#39;class:\u0026#39;,__fontClass); 3Font.registerFont(__fontClass); 在使用外部字体swf文件的时候，如果自身又被另一个swf载入，情况就变得非常复杂，需要设置应用程序域和系统安全域。简单的说，就是要遵循以下两条原则：\n1//如果自己被父SWF载入，那么应用程序域就必须设置成当前域或者是子域才行 2var __loaderContext:LoaderContext = new LoaderContext(true, ApplicationDomain.currentDomain); 3_loader.load(new URLRequest(\u0026#39;fonts_local.swf\u0026#39;), __loaderContext); 4 5//如果载入的字体swf与发起载入的swf不在一个网域，就需要将安全域设定为当前安全域 6var __loaderContext:LoaderContext = new LoaderContext(true, ApplicationDomain.currentDomain, SecurityDomain.currentDomain); 7_loader.load(new URLRequest(\u0026#39;fonts_local.swf\u0026#39;), __loaderContext); 但是，如果子域和父域有同名包、同名类、同名方法，那么就要注意了，使用当前应用程序域或者子域会让你程序的结构变得一团糟，甚至可能会产生命名空间冲突。而这种冲突给出的运行时错误提示基本让你没法查出错误在哪里。\n要理解这块的纠结之处，最好的办法是下载我的范例程序自已调试把！\n2 文件 ","date":"2011-09-06","description":"","lastmod":"2011-09-06T09:24:03Z","slug":"as3_embed_font","tags":["as3"],"title":"使用Embed标签在AS3项目中嵌入字体","url":"https://blog.zengrong.net/post/as3_embed_font/"},{"categories":["technology"],"content":"2011-09-02：v0.5.7版发布\n解决metadata中frame的的ow和oh与w和h相等的**重大BUG**； 现在可以把帧还原到“修剪空白”前的状态； 使用SharedObject保存设置。目前仅实现了帧率的保存。 更多的功能介绍以及软件下载，看这里。\n","date":"2011-09-02","description":"","lastmod":"2011-09-02T12:27:28Z","slug":"1436","tags":["air","bitmapdata","spritesheet"],"title":"编辑Sprite Sheet的小工具：sprite sheet editor v0.5.7发布","url":"https://blog.zengrong.net/post/1436/"},{"categories":["technology"],"content":"source on github\n在Sprite Sheet Editor 0.5.0版本中，我加入了我加入了修剪空白的功能。这个功能将每帧图像的上下左右的空白全部清空，然后再将所有清空了空白的帧绘制在一张大的sheet上，从而使最终sheet文件尺寸尽可能的小。\n但是，对于动画来说，所有帧的尺寸应该是统一的，这样才能实现正确对位。所以，一个完整的动作的帧的大小，应该以这个动作所有帧的有效像素的外延来确定。\n看看这张图，就清楚了：\n加入这个动作由两帧组成，第一帧占据左下角，第二帧占据右上角，那么整个动作的帧的尺寸就应该以第一帧和第二帧的宽高的最大值来确定。也就是图中的虚线部分。\n所以，Sprite Sheet Editor 在保存操作的同时，将动作的修剪尺寸和原始尺寸（实际在程序中用于动作的尺寸，也就是上面说的最大值）同时保存在元数据当中，在使用它的时候，利用这些数据还原。\n以一帧的数据为例，下面是以XML格式保存的元数据中的一帧的定义。\n其中，x和y定义这一帧在整个sheet中的绝对位置；w和h定义这一帧在sheet中的宽高；ox和oy的值如果不为0，说明此帧被修剪过，它的实际宽高应该是ow和oh，而相对于实际宽高的偏移则是ox和oy。\n1\u0026lt;frame\u0026gt; 2 \u0026lt;x\u0026gt;100\u0026lt;/x\u0026gt; 3 \u0026lt;y\u0026gt;120\u0026lt;/y\u0026gt; 4 \u0026lt;w\u0026gt;37\u0026lt;/w\u0026gt; 5 \u0026lt;h\u0026gt;124\u0026lt;/h\u0026gt; 6 \u0026lt;ox\u0026gt;-9\u0026lt;/ox\u0026gt; 7 \u0026lt;oy\u0026gt;-3\u0026lt;/oy\u0026gt; 8 \u0026lt;ow\u0026gt;131\u0026lt;/ow\u0026gt; 9 \u0026lt;oh\u0026gt;130\u0026lt;/oh\u0026gt; 10\u0026lt;/frame\u0026gt; 原理见下图：\n红色的点就是该帧在整个sheet中的坐标位置，虚线矩形框代表该帧在程序中使用的实际宽高（就是trim之前的宽高），而实线矩形框代表该帧在sheet中保存的宽高。\n","date":"2011-09-02","description":"","lastmod":"2011-09-02T06:39:13Z","slug":"sprite-sheet-editor-trim-transparent-area","tags":["spritesheet","vim"],"title":"Sprite Sheet Editor修剪每帧中的空白区域的原理说明","url":"https://blog.zengrong.net/post/sprite-sheet-editor-trim-transparent-area/"},{"categories":["technology"],"content":"Vim多行匹配以及排除字符串 原文地址:https://blog.zengrong.net/post/1429.html\n一个很简单的需求，在下面的XML文件中匹配 \u0026lt;frame\u0026gt; 块。\n一开始写了一个：\n1^\\s\\+\u0026lt;frame\u0026gt;\\_[^\\(frame\\)]*\u0026lt;\\/frame\u0026gt; 这样能达到需求，但是却是凑巧。\n因为 [^\\(frame\\)] 其实并不会匹配一个frame字符串，它依然是将组中的字符串单独对待的。如果在\u0026lt;frame\u0026gt;块之间包含frame中的任何一个字符串，那么匹配就会失败。\n凑巧的是，正好在下面的范例中，并没有出现这样的情况。\n既然有问题，那么就修改下，最终的版本是这样的：\n1^\\s\\+\u0026lt;frame\u0026gt;\\(frame\\)\\@!\\_.*\u0026lt;\\/frame\u0026gt; \\@! 相当于 perl正则表达式中的 ?!，它的含义是“不匹配一个组”。\\@! 后面需要跟 . 来确定匹配范围。为了匹配换行，这里使用了\\_.。\nVim的正则格式真是不走寻常路啊……无处不在的转义和非转义让人崩溃……\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; 2\u0026lt;metadata\u0026gt; 3 \u0026lt;frames\u0026gt; 4\t\u0026lt;frame\u0026gt; 5\t\u0026lt;x\u0026gt;0\u0026lt;/x\u0026gt; 6\t\u0026lt;y\u0026gt;0\u0026lt;/y\u0026gt; 7\t\u0026lt;w\u0026gt;70\u0026lt;/w\u0026gt; 8\t\u0026lt;h\u0026gt;124\u0026lt;/h\u0026gt; 9\t\u0026lt;ox\u0026gt;-10\u0026lt;/ox\u0026gt; 10\t\u0026lt;oy\u0026gt;-1\u0026lt;/oy\u0026gt; 11\t\u0026lt;ow\u0026gt;70\u0026lt;/ow\u0026gt; 12\t\u0026lt;oh\u0026gt;124\u0026lt;/oh\u0026gt; 13\t\u0026lt;/frame\u0026gt; 14\t\u0026lt;frame\u0026gt; 15\t\u0026lt;x\u0026gt;70\u0026lt;/x\u0026gt; 16\t\u0026lt;y\u0026gt;0\u0026lt;/y\u0026gt; 17\t\u0026lt;w\u0026gt;53\u0026lt;/w\u0026gt; 18\t\u0026lt;h\u0026gt;124\u0026lt;/h\u0026gt; 19\t\u0026lt;ox\u0026gt;-10\u0026lt;/ox\u0026gt; 20\t\u0026lt;oy\u0026gt;-2\u0026lt;/oy\u0026gt; 21\t\u0026lt;ow\u0026gt;53\u0026lt;/ow\u0026gt; 22\t\u0026lt;oh\u0026gt;124\u0026lt;/oh\u0026gt; 23\t\u0026lt;/frame\u0026gt; 24\t\u0026lt;frame\u0026gt; 25\t\u0026lt;x\u0026gt;123\u0026lt;/x\u0026gt; 26\t\u0026lt;y\u0026gt;0\u0026lt;/y\u0026gt; 27\t\u0026lt;w\u0026gt;68\u0026lt;/w\u0026gt; 28\t\u0026lt;f\u0026gt;aaa\u0026lt;/f\u0026gt; 29\t\u0026lt;h\u0026gt;124\u0026lt;/h\u0026gt; 30\t\u0026lt;ox\u0026gt;-7\u0026lt;/ox\u0026gt; 31\t\u0026lt;oy\u0026gt;-2\u0026lt;/oy\u0026gt; 32\t\u0026lt;ow\u0026gt;68\u0026lt;/ow\u0026gt; 33\t\u0026lt;oh\u0026gt;124\u0026lt;/oh\u0026gt; 34\t\u0026lt;/frame\u0026gt; 35\t\u0026lt;frame\u0026gt; 36\t\u0026lt;x\u0026gt;191\u0026lt;/x\u0026gt; 37\t\u0026lt;y\u0026gt;0\u0026lt;/y\u0026gt; 38\t\u0026lt;w\u0026gt;75\u0026lt;/w\u0026gt; 39\t\u0026lt;h\u0026gt;124\u0026lt;/h\u0026gt; 40\t\u0026lt;ox\u0026gt;-9\u0026lt;/ox\u0026gt; 41\t\u0026lt;oy\u0026gt;-2\u0026lt;/oy\u0026gt; 42\t\u0026lt;ow\u0026gt;75\u0026lt;/ow\u0026gt; 43\t\u0026lt;oh\u0026gt;124\u0026lt;/oh\u0026gt; 44\t\u0026lt;/frame\u0026gt; 45\t\u0026lt;frame\u0026gt; 46\t\u0026lt;x\u0026gt;266\u0026lt;/x\u0026gt; 47\t\u0026lt;y\u0026gt;0\u0026lt;/y\u0026gt; 48\t\u0026lt;w\u0026gt;75\u0026lt;/w\u0026gt; 49\t\u0026lt;h\u0026gt;123\u0026lt;/h\u0026gt; 50\t\u0026lt;ox\u0026gt;-26\u0026lt;/ox\u0026gt; 51\t\u0026lt;oy\u0026gt;-3\u0026lt;/oy\u0026gt; 52\t\u0026lt;ow\u0026gt;75\u0026lt;/ow\u0026gt; 53\t\u0026lt;oh\u0026gt;123\u0026lt;/oh\u0026gt; 54\t\u0026lt;/frame\u0026gt; 55 \u0026lt;/frames\u0026gt; 56\u0026lt;/metadata\u0026gt; ","date":"2011-09-01","description":"","lastmod":"2011-09-01T10:30:50Z","slug":"vim_reg_multiline_and_unmatch_string","tags":["regexp","vim"],"title":"Vim多行匹配以及排除字符串","url":"https://blog.zengrong.net/post/vim_reg_multiline_and_unmatch_string/"},{"categories":["technology"],"content":"为了方便分类，我经常会自定义文本文件的扩展名。比如json代表JSON格式的文本，md代表markdown格式的文本等等。这些文件都是纯文本文件，但svn在默认情况下，会将其识别成为二进制（bin）文件。\n这样，在文件合并等操作时，就会出现问题。\n用一句代码可以解决这个问题：\nsvn ps svn:mime-type text/plain *.md 处理完毕后，需要提交一次修改到服务器。\n","date":"2011-08-31","description":"","lastmod":"2011-08-31T03:51:35Z","slug":"1428","tags":["svn"],"title":"让svn将非标准扩展名识别为文本文件","url":"https://blog.zengrong.net/post/1428/"},{"categories":["technology"],"content":"vim的缓冲区列表(buffer list)保存着打开过的文件集合。使用 :b filename 可以快速打开需要的文件，且支持文件名自动补全，为编辑提供方便。\n如果所有源文件都加入到缓冲区列表中，在编辑源码的时候不就更方便了么？\n使用 :badd 命令可以将一个文件加入到缓冲区，我写了3个函数来提供批量加入和文件搜索的功能：\nGetFileList返回提供的路径（和子目录）下的所有as文件的列表； EchoBaddList将提供的路径（和子目录）下的所有as文件输出成Vim支持的添加缓冲区列表语句，并输出到当前缓冲区中；使用这个函数，可以方便的编辑自己的Session文件； BaddList则直接将提供的路径（和子目录）下的所有as文件加入到缓冲区列表。 1\u0026#34; 获取目录下的所有文件的列表 2function! GetFileList(...) 3 if exists(\u0026#39;a:1\u0026#39;) 4 let path = a:1 5 else 6 let path = glob(\u0026#34;%:h\u0026#34;) 7 endif 8 if exists(\u0026#39;a:2\u0026#39;) 9 let ext = a:2 10 else 11 let ext = \u0026#34;as\u0026#34; 12 endif 13 let trueList = [] 14 \u0026#34; 获取子目录中的文件列表 15 let fileList = split(glob(path.\u0026#34;/**/*.\u0026#34;.ext), \u0026#34;\\\u0026#34;) 16 for afile in fileList 17 if isdirectory(afile) 18 \u0026#34; 排除目录 19 continue 20 end 21 call add(trueList, afile) 22 endfor 23 return trueList 24endfunction 25 26\u0026#34; 输出buffer列表到当前缓冲区 27function! EchoBaddList(...) 28 let baddList = call(\u0026#34;GetFileList\u0026#34;, a:000) 29 for afile in baddList 30 execute \u0026#39;normal obadd \u0026#39;.afile 31 endfor 32endfunction 33 34\u0026#34; 将path中的文件直接加入buffer列表 35function! BaddList(...) 36 let baddList = call(\u0026#34;GetFileList\u0026#34;, a:000) 37 for afile in baddList 38 execute \u0026#39;badd \u0026#39;.afile 39 endfor 40endfunctio 运行一下看看 :call BaddList('src')\n再看看缓冲区是否被加入了： :ls\n","date":"2011-08-27","description":"","lastmod":"2011-08-27T11:45:27Z","slug":"1427","tags":["actionscript","vim"],"title":"将当前目录下的所有as文件加入缓冲区列表","url":"https://blog.zengrong.net/post/1427/"},{"categories":["technology"],"content":"在Vim调用Ant编译swf并自动调试一文中，我介绍了使用Vim来编译swf的方法，这半年来，我一直都用Vim+Ant+FlexSDK做AS开发，感觉总体效率上比Flash Builder要高许多。\n不好的地方，其实也有许多。最大的不便就是没有代码提示，对于不熟悉的方法，必须要去查看Language Reference。还有个附带的不便就是必须自己手动导入包。不过这两个不便之处，又让我远离了Flash Builder中的哪些错误的代码提示（我被误导过好多次），以及让我更熟悉AS3的包结构。\n今天说的不是这些，好了，进入正题把！\n在Vim中，有许多为程序员着想的地方。比如碰到编译错误后会自动打开第一个出错的文件并跳转到出错行。改完这个错误后可以用 :cn 命令跳转到下一个出错处继续修改。这个特性降低了程序员在调试过程中对鼠标的依赖，提高了效率。\n不过，在使用Vim+Ant+FlexSDK来编译AS的时候，虽然能够看到编译的出错信息，但是Vim会自动打开一个不存在的文件，无法使用上面的功能。\n要了解出现这个问题原因，首先要知道Vim获取编译错误的原理。 Vim通过分析编译器输出的错误信息文本，来得到自己能识别的出错信息，从而实现正确的跳转。 但不同的编译器的输出信息千奇百怪，Vim默认的分析程序不能正确解析 Ant+mxmlc 编译器的输出信息，造成了无法正确跳转到错误文件。让我们看张图：\n可以看出，Vim将错误文件路径前面的[mxmlc]前缀也当作了路径的一部分，然后打开这个不存在的文件，导致无法正常进行修改。\n再看一张详细一点的错误信息：\n那么，只要我们将错误信息的解析程序设置正确，就能正确解析这些信息了！\n执行下面的代码，然后编译，如果有编译错误，就能正常的跳转到错误文件的对应位置：\n1:set errorformat=%E\\ \\ \\ \\ [mxmlc]%f(%l):\\ 列:\\ %c\\ %m 需要注意一点的是，在ex模式或者在命令行模式中输入errorformat属性值的时候，必须对空格用反斜杠转义，否则Vim会不接受这些参数值。网上找的很多例子可能都忽略了这点。上面的例子中，已经进行了转义。\n**为什么例子中有个中文？**因为编译器自动识别了我的语言，因此输出的错误信息是中文的，我也必须用中文来分析。如果编译器输出信息为英文，那么这里的 列 应为 col。\n由于使用了中文，如果将这段代码写在vimrc中的话，就必须进行编码转换：\n1let \u0026amp;errorformat=iconv(\u0026#34;%E\\ \\ \\ \\ [mxmlc]%f(%l):\\ 列:\\ %c\\ %m\u0026#34;, \u0026#39;utf8\u0026#39;, \u0026amp;enc) 至于语法的详细含义和用法，这里写的很清楚：:help errorformat\n","date":"2011-08-26","description":"","lastmod":"2011-08-26T14:42:48Z","slug":"vim-errorformat-mxmlc","tags":["ant","flex","vim"],"title":"设置Vim的errorformat以支持mxmlc编译器","url":"https://blog.zengrong.net/post/vim-errorformat-mxmlc/"},{"categories":["others"],"content":"**2014-06-18更新：**增加ATM说明\n在购买手表的时候，“30米防水”、“50米防水”、“100米防水”这些都代表什么？100米防水的手表能戴着游泳么？能潜水么？\n标注 防水手表，背面一般都会进行这样的标注，下面介绍标注的单位：\nM 米 ATM atmosphere 大气压\n1个大气压大约等于10.3米水柱的压力，因此可以认为1ATM约等于10米防水 防水级别表格描述 标注 M ATM 说明 SWEAT-RESISTANT 未标注 未标注 防汗 Waterproof 未标注 未标注 防汗 WATER-RESISTANT 未标注 未标注 相当于30M防水 WATER-RESISTANT 30M 3ATM 生活防水，洗手洗车防水，防溅水 防水级别图片描述 更多说明 手表上标注的防水深度，其实都是指静态防水。而在水中运动的时候，手表承受的压力是远远超过静态检测时的情况。同时，随着时间的流逝，手表受到汗液的腐蚀和灰尘的侵蚀，密封性能也会收到影响。因此，如果希望在游泳的时候也戴表的话，尽量还是买防水200米以上的比较靠谱。\n引用 ATM explanation\n","date":"2011-08-24","description":"","lastmod":"2011-08-24T12:11:02Z","slug":"water-resistant","tags":[],"title":"手表的防水等级说明","url":"https://blog.zengrong.net/post/water-resistant/"},{"categories":["technology"],"content":"2011-08-23：v0.5.6版发布\n加入缩小帧原始尺寸的功能； 删除帧后，会立即更新Sheet预览； 解决一些bug。 所有帧的原始尺寸是一个很有用的功能，例子：\n一个角色的动作是由多帧组成的，为了配合占用最大空间的那一帧，每一帧中都会有一些多余的空白。虽然可以使用“修剪空白”功能来删除这些空白，但这只能降低保存的文件的大小。\n在将角色载入到程序中的时候，空白依然是会占用内存空间的。\n使用缩小帧原始尺寸这个功能，就能将每个动作中的所有帧的原始外边框尽量变小（当然是人工操作），以节省内存空间。\n更多的功能介绍以及软件下载，看这里。\n","date":"2011-08-23","description":"","lastmod":"2011-08-23T08:17:22Z","slug":"1414","tags":["air","bitmapdata","spritesheet"],"title":"编辑Sprite Sheet的小工具：sprite sheet editor v0.5.6发布","url":"https://blog.zengrong.net/post/1414/"},{"categories":[],"content":"Sprite Sheet Editor\nSprite Sheet Editor 不再更新 我已经放弃 Flash 平台，本工具不再更新。详见： Goodbye Flash!\n下一步开发计划 增加编辑动画中心点功能； 加入自动更新功能。 Sprite Sheet Editor 是一个生成Sprite Sheet(也叫Tile Sheet)的免费工具。\n开发平台 Apache Flex 4.9.1 Adobe AIR 3.7 特点 将swf转换成序列图或者Sprite Sheet格式； 将多张图像拼合成一张大的Sprite Sheet以降低文件尺寸和减少网络请求； 让Sprite Sheet也支持Label，实现类似于MovieClip中Label的功能； 自动修剪Sheet中每帧四周的空白像素； 使用Mask技术让JPEG格式也支持透明，大幅降低需要透明的文件的尺寸； 支持JPEG-XR格式，该格式支持Alpha通道，图像质量优于JPEG格式； 还有更多…… 类似软件 Sprite sheet Editor有许多缺点，因此我必须推荐两个优秀的软件给大家。你应该在安装SpriteSheetEditor之前去尝试一下它们。\nShoeBox TexturePacker 下载和安装 安装AIR环境\n1 文件 历史版本\n8 文件 更新历史 这个工具原来的名字叫做Sprite Sheet Packer，从v0.5.0改名为Sprite Sheet Editor。\n2013-08-21：v0.8.2版发布 2013-06-14：v0.8.1版发布 2013-02-19：v0.8.0版发布 2012-10-15：v0.7.3版发布 2012-08-20：v0.7.2版发布 2012-08-18：v0.7.1版发布 2012-07-26：v0.7.0版发布 2011-12-21：v0.6.2版发布 2011-11-04：v0.5.9版发布 2011-09-02：v0.5.7版发布 2011-08-23：v0.5.6版发布 2011-08-18：v0.5.0版发布，同时更名为Sprite Sheet Editor 2011-06-30：v0.4版发布 2011-04-26：v0.3版发布 2011-04-22：v0.2版发布 2011-04-19：v0.1版发布 界面截图 [gallery link=\u0026quot;file\u0026quot;]\n","date":"2011-08-18","description":"","lastmod":"2011-08-18T04:29:04Z","slug":"spritesheeteditor","tags":[],"title":"Sprite Sheet Editor","url":"https://blog.zengrong.net/spritesheeteditor/"},{"categories":["technology"],"content":"2011-08-18：v0.5.0版发布，同时更名为Sprite Sheet Editor\n采用Flex重写了界面； 修改了生成Sheet的流程； 自动计算生成的Sheet的尺寸，自动计算支持“正方形”和“2的幂”； 可更改已生成的Sheet的尺寸和排列方式； 增加对Sheet中的帧周围空白区域的自动修剪； 可以采用可视化的方式修改截取区域； 可以对待拼合的图像文件进行排序； SpriteSheetMetadata格式小幅修改。 更多的功能介绍以及软件下载，看这里。\n","date":"2011-08-18","description":"","lastmod":"2011-08-18T04:26:31Z","slug":"sse050","tags":["air","bitmapdata","spritesheet"],"title":"编辑Sprite Sheet的小工具：sprite sheet editor v0.5.0发布","url":"https://blog.zengrong.net/post/sse050/"},{"categories":["technology"],"content":"Thank smithfox translation, 中文版\nThis is not a 100% frequency bug, I spent 3 days to debug it and I'm not sure I can duplicate it again.\nCase Connect to FMS Server using NetStream, once invoke method NetStream.play('streamName'), the flash player will crash.\nall of flash player release have the same issue: single,debug,plugin\nThe bug will appear if satisfy the conditions below, (but not all)\nWin7 OS Play RTMP stream. RTMP stream can be published by Flash Media Server(FMS) or Red5. In other words, playing local flv/f4v/mp4 video have not this problem the Stream contains audio Use Frame meta to pre-load. if you do not understand Frame pre-load, see this article: Preloaders in AS3; After complete pre-load, call 'removeChild' to remove the pre-loaded instance of the class (BUG here). Development and testing platform (appear BUG) Flex SDK 4.5.1 Flash Media Server 4.0 Flash Player 10.3 Windows 7 Chrome12/Opera11.5/Firefox5/IE9 BUG reproduction I've wrote two simple Demo (one is sender，and another is receiver) to reproduce this bug. (Demo needs FMS support.)\nThe key is PreloaderNSPlay.as. As the pre-loaded class is no longer needed after the completion of loading, the general way is removing it from Stage. Bug will occur after removal (not occur immediately, but in a time of receiving the audio stream). If using 'visible' to hide pre-loaded classes, would not have this problem.\nUsage of the Demo (Here, the server is FMS) Install FMS, create folder /applications/testspeed/ under the installation directory 0 文件 Make sure that cameras and microphones are installed on your PC. run NSPublish.swf, click \u0026quot;connect\u0026quot; button, then view log to confirm the connection is OK,see the following figure.\nIf client has been connected the FMS server successfully, we will can watch the cameras video, if the flash player still does not crash, let's breath toward the microphone, ...huuuuu...., the whole wold are quiet!!\nThe full source code of the project is here download\nNSPublish.as is the video publisher, NSPlay.as is the receiver. You can build the project using Ant, and you can change the SDK in build.properties file.\n","date":"2011-08-16","description":"","lastmod":"2011-08-16T01:23:21Z","slug":"flashplayer-crash-on-netstream-play","tags":["flashplayer","fms","netstream"],"title":"flashplayer crash on Netstream.play()","url":"https://blog.zengrong.net/post/flashplayer-crash-on-netstream-play/"},{"categories":["technology"],"content":"FlashPlayer/AIR在new Vector(-1)的时候崩溃\n试试这段代码：\n1var __length:int = -1; 2var __v:Vector.\u0026lt;String\u0026gt; = new Vector.\u0026lt;String\u0026gt;(__length); 如果你用Flash builder编译，不会显示任何错误。编译后的swf无法双击打开，或打开后立即退出。\n如果你用编译的是AIR程序，程序运行后会立即崩溃，同时弹出下面的提示信息:\n如果你用Flash IDE来编译，则会看到错误提示：\nError: Error #1000: 系统内存不足。 at Vector$object/set length() at Vector$object() at aaa_fla::MainTimeline/frame1()\n这本来不是什么大问题，毕竟极少极少有人会使用 -1 这个值来作为Vector的length属性。\n可是，起码给点提示好不好？起码让我不要找错方向！\n测试平台：\nFlashPlayer 10.3 Flash Builder 4.5.1 Flex SDK 4.5.1 AIR 1.7 ","date":"2011-08-04","description":"","lastmod":"2011-08-04T09:50:38Z","slug":"flashplayer_crash_on_vector_create","tags":["adobebug","air","as3","flashplayer","vector"],"title":"FlashPlayer/AIR在new Vector(-1)的时候崩溃","url":"https://blog.zengrong.net/post/flashplayer_crash_on_vector_create/"},{"categories":["technology"],"content":"一个未完成的简单AS3 eval实现\nAS3没有eval确实不方便，目前我所知有两种方法：\n使用ActionScript 3 Eval Library来实现。但这个库比较大，debug编译越140KB。如果项目非常注重文件大小，则需要考虑； 如果swf部署在浏览器环境中，可以将eval交给Javascript来处理，然后获取Javascript的返回值即可。 我在工作中需要使用eval的地方，往往比较简单，且比较规律。这种情况下，完全可以自己写一些简单的逻辑来实现。\n这个例子中的需求就很简单，是实现一个较为灵活的站位算法。我在外部配置文件中定位了12个站位坐标（包含xy值），人物进入界面后，基于这些站位点来站位。但由于舞台的大小是变化的，如果定义一个确定的值，无法根据舞台的大小进行变化。\n如果让外部的配置文件中支持类似于 h/2 或者 w-100 这种语法，就非常灵活了。\n这个实现仅支持一次运算，而且不支持单个的 w 这种类型的值，要获取 w，必须用 w+0 这种语法。但对当前的需求来说，足够了。\n下面就是代码：\n1/** 2 * 占位容器宽高 3 */ 4private var box_w:int = 600; 5private var box_h:int = 400; 6 7/** 8 * 对传来的值进行运算，支持加减乘除的一次运算，变量支持w和h 9 */ 10public function calculate($coord:*):int 11{ 12\t/*支持的运算符和变量 13\t例如：w-8 h/2 等等。格式必须是 1(w或h) 2(运算符) 3(数字)*/ 14\tconst reg:RegExp = /([w|h])([+|\\-|*|\\/]+)(\\d+)/; 15\tvar __num:Number = parseInt($coord); 16\t//如果可以解析，就返回解析后的值 17\tif(!isNaN(__num)) return __num; 18\t//如果该值不是字符串，就返回0 19 20\tif(!($coord is String)) return 0; 21\tvar __coord:String = String($coord).toLowerCase(); 22\t//不符合规则，返回0 23\tif(!reg.test(__coord)) return 0; 24\t//如果没有设置容器宽高，返回0 25\tif(box_w == 0 || box_h == 0) return 0; 26\tvar __arr:Array = __coord.match(reg); 27\t//trace(\u0026#39;arr:\u0026#39;, __arr); 28\tvar __box:int = __arr[1] == \u0026#39;w\u0026#39; ? box_w : box_h; 29\t__num = int(__arr[3]); 30\t//加减乘除运算 31\tif(__arr[2] == \u0026#39;+\u0026#39;) 32\treturn __box + __num; 33\telse if(__arr[2] == \u0026#39;-\u0026#39;) 34\treturn __box - __num; 35\telse if(__arr[2] == \u0026#39;*\u0026#39;) 36\treturn __box * __num; 37\treturn int(__box/__num); 38} ","date":"2011-08-01","description":"","lastmod":"2011-08-01T03:48:09Z","slug":"unfinished_as3_eval","tags":["as3","eval"],"title":"一个未完成的简单AS3 eval实现","url":"https://blog.zengrong.net/post/unfinished_as3_eval/"},{"categories":["impressions"],"content":"今天在OllyDbg 的博客上看到这样一篇文章，心有所感，也来说两句。\nOllyDbg的文章转载如下：\n今天在KFC门口见了几个可爱的外国嫚，我看了她们一眼，她们冲我一笑，我冲她们一笑。我就感觉整个晚上都被幸福感包围着。其实人和人之间的感觉就是这么简单，幸福也这么简单。但是我们自己人看自己人的时候，却那么的冷漠，所以才有了我这一篇文章。\n我觉得咱们真的应该反思下了，为什么我们不停下来思考一下，然后再更好的往前走呢？我思考了自己，我们这么经常的各种被骗，各种交易，早就没有那颗容易接近的心，人与人之前的种种沟壑，让人淡漠了。\n想起来小时候，那么的相信周围的人。人与一个陌生人基本上没有特别的隔阂。\n我们的感觉去哪了呢？\n看完后我突然想起自己的两件亲身经历：\n有一次我坐火车上的比较早，整个车厢也就我一个人。后面上来了两个人拖着一大包东西。他们把东西搬到我的位置前面后，就下车去搬另外的东西，其中一个对我说：帮我们把东西看看吧。我当时的第一反应就是：是不是骗子？或者是“做笼子”的？所以我没吭声。\n但我马上就反应过来，为什么我会有这样的想法？为什么我不能一口答应下来？是自己的谨慎，还是对当前的险恶社会的理解造成的？如果再碰到这样的事情，我该怎样去教育我的孩子？\n我又想起10年前做老师的时候，我在学校门口例行执勤。当时还是BP机时代，我收到一个Call，找旁边的发廊（那时已经叫发廊了）打了个电话（大概5毛钱吧）。打完后才发现自己钱包放在办公室没有带出来。我当时就和发廊的理发师说，我是旁边学校的（我还戴着执勤的工作证），待会进去拿钱出来给你。结果他看了我一眼说道：把工作证押在这里吧。\n我才发现，10年后，我已经变得和那个发廊的家伙一样了……\n","date":"2011-07-31","description":"","lastmod":"2011-07-31T14:34:50Z","slug":"human-touch","tags":[],"title":"人情味","url":"https://blog.zengrong.net/post/human-touch/"},{"categories":["technology"],"content":"FlashPlayer在执行NetStream.play的时候崩溃的解决办法\n这是个隐藏非常深的BUG，我都怀疑如果再做一次，我能不能把它找出来。它耗费了我宝贵的三天时间，三天啊……\nBUG表现 在使用NetStream连接FMS发布的流时，在执行NetStream.play('streamName')方法时，FlashPlayer会崩溃。独立版、调试版以及基于浏览器的插件版均如此。\n但是，这还不是全部。必须满足以下几点，该BUG才会出现。\n使用Windows 7操作系统。也就是说，Windows XP不会出现这个问题； 播放的必须是RTMP流，RTMP流可以由Flash Media Server或者Red5来发布。也就是说，使用NetStream播放本地的flv/f4v/mp4视频不会出现这个问题； 播放的流包含音频。也就是说，如果该流只包含视频，不会出现这个问题； 播放的流中包含的音频声音较大。也就是说，即使该流包含音频，但如果发布方没有发出声音，或者发出的声音很小，该问题不会出现；当然，不需要很大的声音就能让播放端立即崩溃； 使用了Frame标签来做预加载。不了解Frame标签预加载的，看这篇文章：Preloaders in AS3; 在预加载完毕之后，使用removeChild移除了预加载类的实例（BUG就在这里）。 开发和测试平台(出现BUG的平台) Flex SDK 4.5.1 Flash Media Server 4.0 Flash Player 10.3独立版/调试版/插件版 Windows 7 旗舰版 Chrome12/Opera11.5/Firefox5/IE9 BUG再现 我写了两个简单的Demo（一个发布端，一个接收端）来重现这个BUG。Demo需要FMS的支持。\n错误的重点在于预加载类(PreloaderNSPlay.as)。由于预加载类在完成加载后就不再需要，一般的处理方法是将其从Stage中移除。只要将移除，就会出现这个BUG（并非移除后立即出现，而是在接收音频流的视频出现）。而如果使用visible将预加载类隐藏，就不会出现这个问题。\nDemo的使用方法（服务端以FMS为例）：\n安装FMS，在安装目录下建立 /applications/testspeed/ 文件夹； 编译NSPulish和NSPlay，或者 在这里直接下载 ； 确认本机安装了摄像头和麦克风，运行NSPublish.swf，单击“连接”按钮，查看log信息确定连接正常，见下图： 运行NSPlay.swf，单击“连接”按钮，查看log信息确认连接正常。此时会看到发布端的摄像头视频。如果FlashPlaye没有崩溃的话，就向着麦克风吹口气……呼……整个世界清静了…… 下面只贴出了 PreloaderNSPlay.as 的源码，需要整个项目源码可以在 这里 下载。\nPreloaderNSPlay.as\n1package 2{ 3import flash.display.MovieClip; 4import flash.display.DisplayObject; 5import flash.display.StageAlign; 6import flash.display.StageScaleMode; 7import flash.events.Event; 8import flash.events.ProgressEvent; 9import flash.text.TextField; 10import flash.text.TextFormat; 11import flash.utils.getDefinitionByName; 12 13public class PreloaderNSPlay extends MovieClip 14{ 15\tpublic function PreloaderNSPlay() 16\t{ 17\t_mainClassName = \u0026#39;NSPlay\u0026#39;; 18\tstage.scaleMode = StageScaleMode.NO_SCALE; 19\tstage.align = StageAlign.TOP_LEFT; 20\tstage.showDefaultContextMenu = false; 21\t_tf = new TextField(); 22\t_tf.defaultTextFormat = new TextFormat(null,12,0,null,null,null,null,null,\u0026#34;center\u0026#34;); 23\t_tf.mouseEnabled = false; 24\t_tf.height = 20; 25\t_tf.x = (stage.stageWidth-_tf.width)*.5; 26\t_tf.y = stage.stageHeight*.5; 27\tthis.addChild(_tf); 28 29\tthis.loaderInfo.addEventListener(ProgressEvent.PROGRESS,progress); 30\tthis.loaderInfo.addEventListener(Event.COMPLETE,complete); 31\t} 32 33\tprotected var _tf:TextField; 34\tprotected var _mainClassName:String; 35\t36\tprivate function progress(e:ProgressEvent):void 37\t{ 38\t_tf.text = int(e.bytesLoaded/e.bytesTotal*100)+\u0026#34;% 载入中……\u0026#34;; 39\t} 40 41\tprivate function complete(e:Event):void 42\t{ 43\tgotoAndStop(2); 44\tvar mainClass:Class = Class(getDefinitionByName(_mainClassName)); 45\tstage.addChild(new mainClass() as DisplayObject); 46\tdestroy(); 47\t} 48 49\tprivate function destroy():void 50\t{ 51\tthis.loaderInfo.removeEventListener(ProgressEvent.PROGRESS,progress); 52\tthis.loaderInfo.removeEventListener(Event.COMPLETE,complete); 53\t//将预加载类从舞台移除(parent.removeChild也一样，因为parent就是舞台），就会导致Flash Player崩溃 54\tstage.removeChild(this); 55\t//parent.removeChild(this); 56 57\t//如果只移除显示进度的文本，或者只将自身隐藏而不移除，就不会出现这个Bug 58\t//this.removeChild(_tf); 59\t//this.visible = false; 60\t} 61} 62} ","date":"2011-07-30","description":"","lastmod":"2011-07-30T07:49:50Z","slug":"flashplayer_crash_on_netstream_play","tags":["adobebug","flashplayer","fms","netstream"],"title":"FlashPlayer在执行NetStream.play的时候崩溃的解决办法","url":"https://blog.zengrong.net/post/flashplayer_crash_on_netstream_play/"},{"categories":["use"],"content":"Google code 开始支持Git了！\n从现在开始，新建code项目就可以选择Git作为版本管理系统；\n或者在Administer的Source面板中，也能将现有的项目转换成Git系统来管理。\n不过，目前仅支持Https协议来clone，这速度就有点慢了啊。\nGoogle应该早点动手，否则也不会那么多人将项目转到github上去了\n","date":"2011-07-27","description":"","lastmod":"2011-07-27T02:02:08Z","slug":"google-code-support-git","tags":["git","google"],"title":"Google Code 开始支持 Git","url":"https://blog.zengrong.net/post/google-code-support-git/"},{"categories":["technology"],"content":"2011-08-05升级：加入自定义数据发送的功能，规则：s字符串,b字节,i长整数,u无符号整形,n短整形。例如 :n1000,b1,i65555,s你懂的\n为了测试新服务器的连接承载能力，我花了点时间写了这个测试器，但其实这只能算个雏形而已。\n后来测试器使用C#重写，所以这个测试器就没再修改和更新，里面已有的BUG也没怎么解决。\n目前的版本实现了以下功能：\n指定连接数量； 允许指定连接间隔时间； 发送指定大小的测试数据包； 自动写入log文件（不要妄想用TextArea显示Log，最后程序会直接被log信息的更新拖死……） 加入自定义数据发送的功能，规则：s字符串,b字节,i长整数,u无符号整形,n短整形。例如:n1000,b1,i65555,s你懂的 截图：\nAIR包下载\n1 文件 源码下载\n1 文件 souce in github\nhttps://github.com/zrong/socket_performance_tester\n","date":"2011-07-21","description":"","lastmod":"2011-07-21T14:12:57Z","slug":"socket-performance-tester","tags":["air","performance","socket"],"title":"Socket服务器性能测试器+源码","url":"https://blog.zengrong.net/post/socket-performance-tester/"},{"categories":["technology"],"content":"原文链接， 可能是版权原因，目前该网站已不可访问。\n本文是Learn Python the Hard Way（中译《笨办法学Python》）一书的尾声部分。\n你已经完成了这本书而且打算继续编程。也许这会成为你的一门职业，也许你只是作为业余爱好玩玩。无论如何，你都需要一些建议以保证你在正确的道路上继续前行，并且让这项新的爱好为你带来最大程度的享受。\n我从事编程已经太长时间，长到对我来说编程已经是非常乏味的事情了。我写这本书的时候，已经懂得大约 20 种编程语言，而且可以在大约一天或者一个星期内学会一门编程语言(取决于这门语言有多古怪)。现在对我来说编程这件事情已经很无聊，已经谈不上什么兴趣了。当然这不是说编程本身是一件无聊的事情，也不是说你以后也一定会这样觉得，这只是我个人在当前的感觉而已。\n在这么久的旅程下来我的体会是：编程语言这东西并不重要，重要的是你用这些语言做的事情。事实上我一直知道这一点，不过以前我会周期性地被各种编程语言分神而忘记了这一点。现在我是永远不会忘记这一点了，你也不应该忘记这一点。\n你学到和用到的编程语言并不重要。不要被围绕某一种语言的宗教把你扯进去，这只会让你忘掉了语言的真正目的，也就是作为你的工具来实现有趣的事情。\n编程作为一项智力活动，是唯一一种能让你创建交互式艺术的艺术形式。你可以创建项目让别人使用，而且你可以间接地和使用者沟通。没有其他的艺术形式能做到如此程度的交互性。电影领着观众走向一个方向，绘画是不会动的。而代码却是双向互动的。\n编程作为一项职业只是一般般有趣而已。编程可能是一份好工作，但如果你想赚更多的钱而且过得更快乐，你其实开一间快餐分店就可以了。你最好的选择是将你的编程技术作为你其他职业的秘密武器。\n技术公司里边会编程的人多到一毛钱一打，根本得不到什么尊敬。而在生物学、医药学、政府部门、社会学、物理学、数学等行业领域从事编程的人就能得到足够的尊敬，而且你可以使用这项技能在这些领域做出令人惊异的成就。\n当然，所有的这些建议都是没啥意义的。如果你跟着这本书学习写软件而且觉得很喜欢这件事情的话，那你完全可以将其当作一门职业去追求。你应该继续深入拓展这个近五十年来极少有人探索过的奇异而美妙的智力工作领域。若能从中得到乐趣当然就更好了。\n最后我要说的是学习创造软件的过程会改变你而让你与众不同。不是说更好或更坏，只是不同了。你也许会发现因为你会写软件而人们对你的态度有些怪异，也许会用“怪人”这样的词来形容你。也许你会发现因为你会戳穿他们的逻辑漏洞而他们开始讨厌和你争辩。甚至你可能会发现有人因为你懂得计算机怎么工作而觉得你是个讨厌的怪人。\n对于这些我只有一个建议:\n让他们去死吧。这个世界需要更多的怪人，他们知道东西是怎么工作的而且喜欢找到答案。当他们那样对你时，只要记住这是你的旅程，不是他们的。“与众不同”不是谁的错，告诉你“与众不同是一种错”的人只是嫉妒你掌握了他们做梦都不能想到的技能而已。\n你会编程。他们不会。这真他妈的酷。\n","date":"2011-07-18","description":"","lastmod":"2011-07-18T04:15:32Z","slug":"learn-python-the-hard-way","tags":["book","python"],"title":"【转】老程序员的建议","url":"https://blog.zengrong.net/post/learn-python-the-hard-way/"},{"categories":["technology"],"content":"本文转自Perter's Blog，稍作修改\nAdobe Flash Player支持GPU加速的Stage3D功能无疑对未来的Web game渲染性能带来质的飞跃。目前包含这一功能的Flash Player 11 Beta版本已经发布在Adobe Labs。\n或许很多开发者都有这样的问题，我如何知道哪些设备支持Stage3D呢？按下面方法即可获取到：\n运行这个工具 http://zp.amsnet.pl/cdragan/d3dcaps.html 得到一个叫‘devcaps.txt’ 的文件。 查看两个着色器的值。如：\nVERTEX_SHADER_VERSION FFFE0300\nPIXEL_SHADER_VERSION FFFF0300\n忽略FFFE/FFFF, 只看红色部分的值，这个值必须都大于0200 才能支持molehill,否则就软解。\n注意，这两个值会在devcaps.txt文件中出现两次，用后面出现的那一组（第一组的值是0101/0104，忽略即可）。\n","date":"2011-07-15","description":"","lastmod":"2011-07-15T01:51:23Z","slug":"adobe-flash-stage-3d-checker","tags":["3d","flashplayer"],"title":"【转】判断计算机显卡是否支持Adobe Flash Stage3D","url":"https://blog.zengrong.net/post/adobe-flash-stage-3d-checker/"},{"categories":["technology"],"content":"7月13日，Adobe Labels发布了Flash Player 11和AIR3的Beta，简介如下：\n共同所有的新功能 基于显卡的3D渲染加速\n给力啊……网页游戏界要洗牌了 支持对摄像头视频流进行H.264/AVC编码\n基于FMS的视频聊天室可以更加清晰了 原生JSON格式支持\n这个早就该有了，Adobe真是不思进取 支持手机设备上的G.711音频压缩 垃圾回收通告\n虽然不能手动GC，但有这个也不错 三次贝塞尔曲线 安全的随机数生成器 受保护的HTTP动态流(HDS)和Flash访问增强 提供Socket进度事件 JPEG-XR格式支持 高分辨率位图支持\n呵呵，终于可以不受那个该死的1677万像素的限制了 更高压缩比的SWF格式\n改为LZMA（7z）压缩了，压缩比高达40% DisplayObjectContainer.removeChildren 和 MovieClip.isPlaying\n有总比没有好，不是么？ 仅Flash Player所有的新功能 原生的64位操作系统支持\n拖了这么多年，总算出来了 支持异步位图解码 TLS安全套接字支持 仅AIR3所有的新功能 高清视频硬件解码加速\n同时支持移动设备，这个还不错。但Linux版终于离我们而去了 资源 详细pdf下载链接（鸟语版，不喜勿下） 话说Flash Player 11 beta的reference，原来在这里：http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/index.html ","date":"2011-07-14","description":"","lastmod":"2011-07-14T13:09:11Z","slug":"beta-flash-player-11air3","tags":["3d","air","flashplayer"],"title":"Beta(Flash Player 11+AIR3)新功能简介（中文版）","url":"https://blog.zengrong.net/post/beta-flash-player-11air3/"},{"categories":["technology"],"content":"原文地址：https://blog.zengrong.net/post/1374.html\nAS3中正则表达式对反斜杠的替换\n一个有趣的小问题，下面的正则表达式能替换成功么？\n1var __str:String = \u0026#39;1234\\6789\u0026#39;; 2trace(__str.replace(/\\\\/g, \u0026#39;5\u0026#39;)); 答案是：不能。trace出来的结果为：\n[trace] 12346789\n其实正则本身并没有写错，错在被替换的字符串。反斜杠 “\\” 在AS3中是转义符，会将其后的任何值转换为本身，因此看到的字符串其实本身就是 12346789 ，也就是没有反斜杠，当然无法搜索到。\n直接 trace(__str) ，结果和上面的trace相同。\n希望得到正确的结果，需要将字符串设置为： 1234\\\\6789 ，我们看到的是 两个 反斜杠，而AS3认为它是 一个 反斜杠。\n如果使用RegExp来建立正则，则需要使用4个反斜杠：\n1var __str:String = \u0026#39;1234\\\\6789\u0026#39;; 2var __reg:RegExp = new RegExp(\u0026#39;\\\\\\\\\u0026#39;, \u0026#39;\u0026#39;); 3trace(__str.replace(__reg, \u0026#39;5\u0026#39;)); 这种情况只在硬编码字符串的时候出现，而如果字符串出现在 TextField中，从 TextField.text 中取出的字符串，本身就自动进行了转义，看到的 一个 反斜杠，其实是 两个 反斜杠。\n","date":"2011-07-14","description":"","lastmod":"2011-07-14T01:24:29Z","slug":"backslash_in_regexp","tags":["as3","regexp"],"title":"AS3中正则表达式对反斜杠的替换","url":"https://blog.zengrong.net/post/backslash_in_regexp/"},{"categories":["technology"],"content":"Flash Player从版本10.1开始加入了Sleep Mode（睡眠模式），具体表现为在浏览器中的Flash Player窗口不可见（最小化、被完全遮挡，或被滚动条带到不能显示的地方）时，自动将帧率降低到4帧/秒。我原来也写过一篇文章介绍FPS对Socket链接的影响\n睡眠模式给游戏开发者带来很大的麻烦，但并非不可解决。目前最常用的两种解决方式是这样：\n在JS中写一个计时器，每隔一段时间调用SWF提供的方法，可以避免进入睡眠模式； 在swf中播放一段不间断的声音，可以让帧频维持在8帧/秒。 但是，即使是使用了这两种方法，在IE9中，依然会受到“睡眠模式”的影响！ 而且，这个“睡眠模式”的提供者是IE9，我们无法绕过。\n就目前我测试得到的情况，IE9对Flash Player有如下影响：\n在IE9窗口最小化时，会自动切断音视频流； 在IE9窗口最小化时，会自动切断客户端与服务器的通信，但不会切断连接。 测试环境：\nWindows 7 旗舰版 Internet Explorer 9 Flash Player 10.3.181.34 标准版+Debug版 开发环境：\n客户端：Flex SDK 4.5.1 服务端：Flash Media Server 4.0 以上结论是只测试了FMS为服务器的情况，没有测试Socket的情况，想来应该没什么区别。\n网上搜了一通，没有什么中文资料，但在Microsoft Answer上找到 这样一篇文章 。我想中文资料少的原因可能是国内的开发者都不太愿意转到Win7环境，而导致无法升级IE9，所以碰不到这个问题。\n上文中也说到一个解决办法，由于公司电脑无法装IE9，我暂时还没有测试：\n测试了，这方法没用。\nSee if this fixes the Flash/IE9 problem. From Safety (on the IE Command Bar) uncheck) ActiveX filtering.\n","date":"2011-07-11","description":"","lastmod":"2011-07-11T02:20:57Z","slug":"ie9_the_impact_of_flash_player","tags":["flashplayer","sleepmode"],"title":"并非SleepMode之错——IE9对Flash Player的影响","url":"https://blog.zengrong.net/post/ie9_the_impact_of_flash_player/"},{"categories":["technology"],"content":"【转】Flash Player 执行模型之可变跑道\n原文地址：http://blogs.adobe.com/xwlin/2010/04/flash_player_101_-_adobe_max_2009_1.html 转载地址：http://blog.csdn.net/zlxluofeng/article/details/5516349 演讲人: Lee Thomason (lthomaso@adobe.com) 翻译: 林晓伟 (xwlin@adobe.com) 上一篇我们介绍了Flash Player的代码库是如何归并一体以及对Flash平台的后期开发产生的影响，在第二节里，我们将重点讨论Flash Player的体系结构及其对开发人员的影响。\n理解执行模型 执行模型是指Flash Player在每一个帧周期中如何执行相应的指令操作。Flash Player后台事实上运行着n多线程，只是AS并没有给开发人员提供多线程编程模型。这意味着从概念上来讲我们要把Flash Player看做是单线程运行实体，有关这一单线程编程模型的优势/劣势的争论从未休止过，我不想对这一具有争议性的问题做过多评论，但请大家记住这一事实。\n可变跑道(Elastic Racetrack) 可变跑道是Flash Player的帧执行模型，这个模型描述了在一帧的处理周期中，代码执行和帧渲染的工作是怎样彼此平衡的。Flash Player 9和AVM2对这一模型进行了一些改进，这一信息是基于对事件机制和渲染模型的研究总结出来的，完整的模型尚未被官方公布。\n基本的跑道理论没有发生改变，在Flash Player执行一帧的周期里，前一部分时间用于执行代码，剩余时间用于渲染显示列表中的对象。每个执行阶段都可以根据实际需求增加执行时间来执行更多代码或做更多的渲染工作，而跑道的总长度也将相应增长。\n在前一模型基础上发生改变的是每一阶段在一个微观周期里的样子以及他们怎样形成一帧。\nAVM2是由Flash Player中一个叫做Marshal的元帅级组件所操控，Marshal负责将时间切割成Flash Player工作所依的基本时间片，在这里我希望澄清一下Flash Player的时间片跟swf文件运行时的帧速率没有任何关系，我们将最终看到Flash Player是如何将这些时间片合成为一帧。在Mac OS版Firefox中执行一个由Flex编译得来的swf文件，Marshal通常会将时间切割成19-20毫秒的时间片,时间片大小根据平台和浏览器的不同而存在差异.为方便我们接下来的讨论,我们假定时间片大小为20毫秒,也就是说Marshal每秒钟会产生不超过50个时间片，每个时间片中，五步可能的操作按如下顺序执行：\nPlayer事件调度 - 比如Timer事件，鼠标事件，ENTER_FRAME事件，URLLoader事件等等。 用户代码执行 - 所有侦听上一步相应事件的代码被执行。 RENDER事件调度 - 在用户代码执行期间调用stage.invalidate()会触发这一特殊事件。 最后的用户代码执行 - 侦听上述第三步特殊事件的用户代码此时被执行。 Player更改显示列表。 Marshal如此反复的执行20毫秒时间片并在运行中决定下一步操作。一个时间片中执行的所有这些操作最终归纳为上述两段式跑道(代码执行，图像渲染)也就是我们所说的一帧。用户代码和失效操作填充在代码执行区，渲染操作填充在跑道的渲染区段。需要指出的是相关操作只能在Marshal预定的时间内发生，如果你的用户代码很短，那么Marshal仍然会在执行完用户代码后等待一段时间然后进入渲染阶段。\n为了更好阐述哪些action被如何执行以及可变跑道如何被创建，请参考如下示例，分别描述了以5fps, 25fps和50fps帧速率工作的swf中时间片是如何被处理的。\n以上示例可以看出，不同的帧速率下，一个帧周期中的可变跑道会执行不同操作，例如对于5fps的swf，每帧处理10个用户action，1个失效action，1个渲染action；帧速率25fps的swf，每帧处理2个用户action，1个失效action，1个渲染action；对于50fps的swf，每帧只能处理1个用户action，1个失效action，1个渲染action。需要指出的很重要的一点是，某些事件只可能能发生在某些特定的时间片里，比如，Event.ENTER_FRAME事件只能在某一帧的初始时间片中被调度。\n一个时间片中代码部分和渲染部分都有可能过长而导致相应时间片大于20毫秒，这就是\u0026quot;可变\u0026quot;的含义，为了保证帧的播放速率仍然接近swf编译时设定的帧率，Marshal会选择丢掉一些时间片。 swf文件的播放速率不可能超过当前Flash Player切割时间片的速率，你可以为swf文件设定120fps的播放速率，但Flash最多可以按照50帧的速度播放(具体数值取决于当前系统的设置)。 代码的执行频率可能比swf的帧率更高，播放一个1fps的swf文件，播放一帧时间为1秒，也就是50个时间片，但在每个时间片里，都会有触发鼠标或计时器事件，尽管只有最后一个时间片才会渲染，另外你可以选择通过调用函数updateAfterEvent() 提前渲染，但只能在鼠标，计时器和键盘事件处理函数中调用。在这种情况下，Marshal会认为当前帧已结束并从下一个时间片起进入下一帧。 最后，如果一个Sprite的外观属性比如width,height等发生变化而将鼠标从该Sprite上方掠过，Flash会进行强制渲染。 如果帧率不是每秒时间片数量的整数因子，那么该平台的渲染时间间隔将变得不固定，比如帧率20fps的swf运行在50个时间片每秒的系统上，Flash Player的行为将是每5个时间片播放两帧，那swf的渲染频率将是2-3-2-3-2-3(时间片)。 【原博主】\n在9RIA上，有关于该文章的进一步讨论，主要是updateAfterEvent()到底能不能结束当前帧而进入下一帧，下面是讨论内容：\n【jinni】\n我一直对这篇文章的结论有质疑，根据我测试的结果，当调用updateAfterEvent()时，FP并不认为是一帧的结束，而只是单纯的重绘屏幕例如你可以在一帧中点击20次鼠标＋updateAfterEvent()，并造成20次重绘事件(RENDER)，但对于Flash Player的帧事件(ENTER_FRAME)，只会触发一次。换句话说，帧的运行次数和重绘屏幕的次数是没有任何关系的\n【jinni】\n其实有一个很简单的论证很多Flash的逻辑依赖于ENTER_FRAME执行如果调用updateAfterEvent()就会立即结束当前帧从并触发下一帧的话那么这些Flash的运行逻辑就会加快了，这显然不合理而事实上（根据我的测试），调用updateAfterEvent()只会触发RENDER事件和系统重绘。另外，Flex中的Mouse事件，默认会调用updateAfterEvent()来保证运行效果\n【Aone】\n我测试下来不会打断。不过确实有种可以打断帧的方法，就是给 stage.frameRate赋值，即便是stage.frameRate = stage.frameRate也会打断帧强制该帧结束。测试代码如下：\n1stage.frameRate = 1; 2var timer:Timer = new Timer(1); 3timer.addEventListener(TimerEvent.TIMER,onTimer); 4timer.start(); 5addEventListener(Event.ENTER_FRAME,test); 6var old:int = getTimer(); 7 8function test(e:Event):void { 9 trace(getTimer()-old); 10 old = getTimer(); 11} 12 13function onTimer(e:TimerEvent):void { 14 //stage.frameRate = 1; 15 e.updateAfterEvent(); 16 trace(\u0026#34;timer\u0026#34;); 17} 【jinni】\n我所做的测试都是Mac和Win同时做的，事实证明updateAfterEvent()是不会造成帧的结束。而且，在实际情况中，updateAfterEvent()并不只是用户手动调用才会触发。Flash Player会根据需要来对自身进行强制重绘，一帧中可能触发数十次RENDER事件和重绘，而原文所描述的模型基于一个假设：“一帧之中用户事件代码会执行多次，但渲染事件代码和渲染过程只会执行一次。”就是不成立的。\n【Aone】\nupdateAfterEvent()必须Timer触发或者鼠标事件还有键盘事件触发后才能正确重绘。不过呢... 鼠标和键盘就算不注册侦听在某些情况下也会自动updateAfterEvent()来强制渲染。\n【jinni】\n我和Ted讨论了一下,这是他的回复：\nYes you can call updateAfterEvent over and over between standard renders. You basically inject a partial render in the code execution side of the racetrack and elongate it. I would love to see AS have full render control as a playermode in FP11. If set there are no automatic renders and the developer must call render for all visual changes to occur.\n是的，你可以在每次标准RENDER（即原文中的Last slice's render）之间调用多次updateAfterEvent（并进行重绘），相当于你在跑道一圈中代码执行的阶段\u0026quot;注入\u0026quot;了部分渲染过程并延长了整个跑道。\n其实我很希望看到Flash Player 11可以在某种模式下给开发者完整的渲染控制。在这种情况下开发者必须主动调用API来实现屏幕重绘。\n我想问题其实已经比较清楚了，文中跑道模型描述的只是其中一种形态，很多时候，渲染和用户代码的调用在一帧的过程中，是穿插进行的，而不是简单的先执行用户代码，最后渲染\n","date":"2011-07-08","description":"","lastmod":"2011-07-08T03:29:48Z","slug":"/uploads/2011/07","tags":["as3","flashplayer"],"title":"【转】Flash Player 执行模型之可变跑道","url":"https://blog.zengrong.net/post/uploads/2011/07/"},{"categories":["technology"],"content":"2011-06-30：v0.4版发布\n将菜单改为按钮，避免Mac等不支持菜单的操作系统无法显示菜单； 可以打开SpriteSheet格式的图片，然后保存成其他格式。即可以在SS、JPG、PNG之间互转格式； 可以打开已有的SpriteSheet，修改Metadata后保存成新的文件； 解决0.3版手动提供的元数据无效的问题； 播放位图动画的时候，在Sheet预览中显示当前帧的范围； 解决打开的SpriteSheet格式的Label起始帧显示不正常的问题； 采用SDK2.7编译。因此需要卸载原来的软件，再升级AIR Runtime，才能正常安装。（AIR的版本兼容性很糟糕，经常无法安装，而且给出错误的提示） 更多的功能介绍以及软件下载，看这里。\n","date":"2011-06-30","description":"","lastmod":"2011-06-30T05:30:52Z","slug":"1357","tags":["air","bitmapdata","spritesheet"],"title":"SpriteSheet小工具：spritesheetpacker v0.4","url":"https://blog.zengrong.net/post/1357/"},{"categories":["use"],"content":"禁用大写锁定键，以及更多\n在使用Vim的过程中，我经常会误按到大写锁定(Caps Lock)键。误按后我会下意识的通过u或者Ctrl+R恢复。但由于大写锁定键已经按下，命令并不会像我想的那样去执行，反而会错得更离谱。\n因此，禁用大写锁定键就是最好的办法了。Google了一下，发现方法还不少：\n通过软件来禁用 使用“ 键盘屏蔽器 ”这个国产软件，可以轻易屏蔽键盘按键或者切换键位，而且立即生效。唯一的缺点就是必须保持软件运行。这显然是我无法接受的。\n修改注册表 找到注册表键名 HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout 在其下新建一个二进制键值 Scancode Map，注意不要建立在子键中了。 将键值设置为： 00000000 00000000 00000000 0200000000 003A00 00000000 重启操作系统生效。 修改注册表的更简单的办法 Silence 为我们准备了一个小工具专门做这件事情，于是我们就不用面对注册表了，而且也能很方便的还原它。\n该工具的名字叫Keybmap，下载地址：http://www.mympc.org/down/1/2005-11-26_0111998067.html\n注意，在 Windows8 系统下，需要使用最新的 1.8 版本才有效果。该版本目前只有 64 位版。\n在 RemapKey等:小巧实用的键盘映射工具 这篇文章中还能找到更多类似工具和详细介绍。\n原理及更多 作为一个有理想有抱负的程序猿，当然希望知道其中原理。其实“Scancode Map”这个东东，我们可以把它翻译成“扫描码映射”，是从Windows 2000开始提供的一种改变键盘布局的方法。利用它，我们可以禁用某些键，或者让某些键代替另外的某些键的功能（好拗口啊……）\n上面的那串很长很长的二进制码，我们可以这么理解它：\nScancode Map 代码的一般格式是：\nhex:00,00,00,00,00,00,00,00,|02|,00,00,00,|原键,替代键,原键,替代键,|00,00,00,00\n其含义为：\n前8个00(DWord两个0)是版本号，接下来的“02”表示映射数，其最小为值为“02”，表示只映射一组，若要映射多组，只需增加相应的值即可，如映射2组其值应为“03”，3组为“04”，4组为“05”\n后边代码每4个是一组：前两个是映射后键位的扫描码，后两个是键位原扫描码。如果要交换两个键，则最后四个值的排列形式是：键A，键B，键B，键A——它表示：键A成为键B，键B成为键A\n最后以“00,00,00,00” 结尾。\n如果你想知道更多的Scancode，盖茨先生为我们提供了 文档 。\n如果你不喜欢看文档，还可以看这张图：\n参考文章 http://429006.com/article/technology/1532.htm http://www.experts-exchange.com/OS/Microsoft_Operating_Systems/Windows/A_2155-Keyboard-Remapping-CAPSLOCK-to-Ctrl-and-Beyond.html http://hi.baidu.com/dopod100/blog/item/fe23a37e4f4e093f0dd7da62.html ","date":"2011-06-28","description":"","lastmod":"2011-06-28T03:03:58Z","slug":"use_scancode_map_to_disable_the_caps_lock_key","tags":["vim","windows"],"title":"禁用大写锁定键，以及更多","url":"https://blog.zengrong.net/post/use_scancode_map_to_disable_the_caps_lock_key/"},{"categories":["technology"],"content":"这两个家伙有关系么？\n当然有，而且还挺紧密的。\n它们之间的关系就是：\n如果一个Sprite不开启buttonMode，那么就不会有FocusEvent事件发出\n当然，TextField虽然没有buttonMode，也会有FocusEvent。\n那么SimpleButton会如何？我没试，懒得试了……\n把下面的代码中的 _sprite.buttonMode = true; 一行注释掉，可以看到(或者说看不到？)效果。\n1package 2{ 3import flash.display.Sprite; 4import flash.events.Event; 5import flash.events.FocusEvent; 6import flash.events.MouseEvent; 7import flash.text.TextField; 8 9public class Focus extends Sprite 10{ 11\tpublic function Focus() 12\t{ 13\tgetSprite(); 14\tgetSprite(200, 0); 15 16\tvar _tf:TextField = new TextField(); 17\t_tf.type = \u0026#39;input\u0026#39;; 18\t_tf.border = true; 19\t_tf.x = 200; 20\t_tf.y = 200; 21\t_tf.addEventListener(FocusEvent.FOCUS_IN, handler_focusin); 22\t_tf.addEventListener(FocusEvent.FOCUS_OUT, handler_focusout); 23\taddChild(_tf); 24 25\t} 26 27\tprivate var _sprite:Sprite; 28 29\tprivate function getSprite($x:int=0, $y:int=0):Sprite 30\t{ 31\tvar _sprite:Sprite = new Sprite(); 32\t_sprite.graphics.beginFill(0); 33\t_sprite.graphics.drawRect(0,0,100,100); 34\t_sprite.graphics.endFill(); 35\t_sprite.x = $x; 36\t_sprite.y = $y; 37\t_sprite.useHandCursor = true; 38\t_sprite.buttonMode = true; 39\tthis.addChild(_sprite); 40\t_sprite.addEventListener(FocusEvent.FOCUS_OUT, handler_focusout); 41\t_sprite.addEventListener(FocusEvent.FOCUS_IN, handler_focusin); 42\t_sprite.addEventListener(MouseEvent.CLICK, handler_click); 43\treturn _sprite; 44\t} 45 46\tprivate function handler_focusout(evt:FocusEvent):void 47\t{ 48\ttrace(\u0026#39;focusout,target:\u0026#39;, evt.target, \u0026#39;,relatedTarget:\u0026#39;, evt.relatedObject); 49\t} 50 51\tprivate function handler_focusin(evt:FocusEvent):void 52\t{ 53\ttrace(\u0026#39;focusin,target:\u0026#39;, evt.target, \u0026#39;,relatedTarget:\u0026#39;, evt.relatedObject); 54\t} 55 56\tprivate function handler_click(evt:MouseEvent):void 57\t{ 58\ttrace(\u0026#39;click\u0026#39;); 59\t} 60} 61} ","date":"2011-06-27","description":"","lastmod":"2011-06-27T14:24:34Z","slug":"focusevent_and_buttonmode","tags":["as3"],"title":"FocusEvent与buttonMode的关系","url":"https://blog.zengrong.net/post/focusevent_and_buttonmode/"},{"categories":["technology"],"content":"從2.7版本後，Adobe停止了在Linux桌面環境中發布AIR的運行時和SDK，原因是Linux佔有率太低，而Android在不斷上升，Adobe需要整合資源到移動設備的AIR開發中去。\n想想蘋果拋棄了Flash，可Adobe始終對IOS不離不棄……可Adobe就這樣無情的拋棄了Linuxer，真的很憤怒啊！\n看看這兩篇文章，評論裡基本是罵聲一片\nhttp://blogs.adobe.com/flashplayer/2011/06/adobe-air-and-linux-increasing-distribution-on-devices.html\nhttp://blogs.adobe.com/open/2011/06/focusing-on-the-next-linux-client.html\n當然，Adobe也有它的解決方案，就這個08年發布的所謂OSP計劃：http://www.openscreenproject.org/partners/apply.html\n","date":"2011-06-19","description":"","lastmod":"2011-06-19T00:35:19Z","slug":"1349","tags":["adobe","air","linux"],"title":"TNND，Adobe居然停止了AIR的Linux版本開發？","url":"https://blog.zengrong.net/post/1349/"},{"categories":["others"],"content":"2011-12-28更新：刷到港行3.2Rom后，目前是无法Root的，所以想要刷到3.2的朋友要注意。最好是等待4.0。\n要删除掉国行版A500自带的那些垃圾软件，就必须获取root权限。\n著名的z4root对A500不起作用，需要使用另外的软件和方法，流程如下：\n在A500的设置界面中开启USB调试模式和允许未知来源； 插入一张TF卡，卡中的内容可能会丢失（但我的没丢失），因此需要事先做好备份； 将A500竖起来放，开启锁定按钮时期不会自动切换屏幕方向； 安装GingerBreak-v1.20.apk； 在A500上运行该软件，执行root。 1 文件 root的时间大约需要20秒左右，成功后会自动重启。\nroot成功后，安装Root Explorer，删除system/apps下的对应文件，即可卸载那些国行自带的垃圾软件了，什么百度，什么开心网，统统见鬼去吧！\n需要注意的几点：\n如果安装过系统自带软件的更新，那么需要先在设置界面中使用正常的方式卸载更新，然后再用root删除，否则会使该软件遗留在程序界面中无法删除； 使用root删除掉系统自带软件后，程序界面不会立即更新，可能需要重启系统1-2次才会更新； 如果第1条所述的情况出现，那么这些更新应该安装在data/apps中，只要将这个文件夹下对应的文件删除，然后重启即可。 ","date":"2011-06-12","description":"","lastmod":"2011-06-12T00:08:38Z","slug":"1342","tags":["android"],"title":"Acer Iconia tab A500 root教程|删除国行自带的垃圾软件","url":"https://blog.zengrong.net/post/1342/"},{"categories":["others"],"content":"**版本：**大陆行货（简称“国行”）\n购买地：苏宁易购\n**价格：**RMB3094=3449-55(积分兑换)-300(以旧换新)\n该机器5月24日付款，6月6日到货。6月7日苏宁出了个优惠，免费赠送288元的套装。\n和同事的IPAD2深入比较了一段时间，列表如下：\nIPAD2的优点：\n屏幕效果好； 触屏更灵敏。 A500优点：\n1280X800宽屏分辨率，看电影较爽； 全尺寸USB口，无限应用可能； HDMI接口、GPS……； 32G存储空间； 应用免费。 当然，A500优点中的第5点是建立在IPAD2没有越狱，或者不愿意花钱买正版应用的基础之上的。\n平心而论，IPAD2确实不值得购买，真可怜，连个SD插槽都做不出来……我都替Jobs脸红啊\n[gallery link=\u0026quot;file\u0026quot;]\n","date":"2011-06-11","description":"","lastmod":"2011-06-11T15:19:54Z","slug":"1334","tags":["android"],"title":"Acer Iconia Tab A500 32G开箱照","url":"https://blog.zengrong.net/post/1334/"},{"categories":["others"],"content":"Google6月9日纪念莱斯·保罗电吉他之父诞辰 96 周年的doodle，在办公室掀起了一阵音乐旋风。正好有人把它的源码抓出来了，我把它放在这里，随时可玩，呵呵。\n你的浏览器不支持IFRAME，请[单击这里](/uploads/2011/06/google-guitar/google-guitar.htm)观看。 Google guitar也使用了flash技术。源码下载 。\n","date":"2011-06-11","description":"","lastmod":"2011-06-11T02:18:22Z","slug":"google-guitar","tags":["google","javascript"],"title":"Google的弹吉他Doodle源码","url":"https://blog.zengrong.net/post/google-guitar/"},{"categories":["technology"],"content":"自从AS3中加入了int后，我使用int的次数就多过Number。并不是为了降低那一点点的内存占用，而是实在用整数的机会比较多。本文讨论的，就是使用int的地方，却必须使用Number的情况。\n听起来比较拗口，那看看下面这个例子吧：\n1public function move($x:int, $y:int=NaN):void 2{ 3\tif(isNaN($y)) 4\t$y = $x; 5\tthis.x += $x; 6\tthis.y += $y; 7} 想来上面这种情况大家可能会碰到。x和y相同的情况比较多，所以为了偷懒，我就不想提供y参数。y在这里可以是任何整数值，所以我不能将它的默认值设置成一个特殊的整数值（比如-1），然后通过比较来判断是不是让y与x的值相等。\n在这里，NaN就是一个最好的选择。NaN不是任何数字，所以处理起来就简单许多了。只需要判断y是不是默认值NaN，就可以处理后面的情况。\n不过，上面这个例子是错误的。如果执行，在不提供y参数的时候，y的值永远是0。\n可以看看下面这个完整的例子，比上面的部分代码更能说明问题：\n1package 2{ 3import flash.display.Sprite; 4public class TestNaN extends Sprite 5{ 6\tpublic function TestNaN() 7\t{ 8\ttest(); 9\ttest(null); 10\ttest(undefined); 11\ttest(1); 12\t} 13 14\tpublic function test($num:int=NaN):void 15\t{ 16\ttrace($num); 17\t} 18} 19} 运行后得到的结果是：\n[trace] 0\n[trace] 0\n[trace] 0\n[trace] 1\n而将test方法的$num参数的类型从int改成Number，就能得到我们需要的结果：\n[trace] NaN\n[trace] 0\n[trace] NaN\n[trace] 1\n原因是什么？这又回到了很久很久以前在AS3刚刚出来的时候，对默认值的强制类型转换的一些讨论。原因很简单：Number和int的类型转换的处理方式不同。\n有兴趣的话，还可以研究下Class()转换与as转换。也有很多有趣的地方。\n这篇07年的文章也值得一看：AS3中的强制类型转换\n","date":"2011-06-02","description":"","lastmod":"2011-06-02T02:32:57Z","slug":"number_int_and_nan","tags":["as3"],"title":"Number,int和NaN","url":"https://blog.zengrong.net/post/number_int_and_nan/"},{"categories":["technology"],"content":" 2011-09-09更新：为《学习Bash（第二版）》PDF手工加入书签。 2011-09-20更新：加入《Learning.The.Bash.Shell.3rd》下载。 有一批形如 map_XXX.XXX 的文件，要改名为 map1_XXX.XXX 。如果使用其他改名工具改名，git 会认为是删除了这些文件，并要求将改名后的文件重新添加到版本库，这会无端的增加版本库的大小。\n最好的办法当然是用git mv来处理。鼓捣了半小时，写了我的第一行BASH代码：\n1for i in map*{jpg,xml};do git mv $i \u0026#34;map1${i:3}\u0026#34;;done 说明：\nmap*{jpg,xml} 是只处理jpg和xml文件； $i 是引用循环中的文件名，和 ${i} 的作用相同； ${i:3} 返回文件名的第3至最后一个字符的字符串。 BASH真的很好玩，推荐两本书：\n《高级 Bash 脚本编程指南》（Advanced Bash-Scripting Guide）\n1 文件 上面这本书扫描的很不清晰，很多代码看不清，最好是对照英文版来看，下面是英文版下载：\n1 文件 ","date":"2011-06-01","description":"","lastmod":"2011-06-01T09:33:44Z","slug":"multi-rename-in-git","tags":["bash","git","linux","shell"],"title":"在git中批量重命名","url":"https://blog.zengrong.net/post/multi-rename-in-git/"},{"categories":["technology"],"content":"一般情况下，我只需要一个全局鼠标事件就可以判断所有可视对象的点击事件了。但是这个全局鼠标事件应该加在哪里呢？\n至少有3个（类）对象可以侦听这个事件：\nStage对象； root对象，就是你的swf的根显示对象； 自己绘制一个与舞台等大Sprite对象。 那么，加在什么地方合适呢？\nStage是首先被我排除的对象 Stage上的点击事件总是会被侦测到，我不能控制某些“被遮盖”的对象。举个例子：\n假设我制作的这个SWF是游戏地图，另外还有一个UI.swf是游戏的UI界面。UI处于地图的上层。在UI窗体上操作的时候，地图是不应该收到单击事件的。因为这种单击事件，会造成地图中玩家的某些行动。但我总是能收到鼠标的事件，且不容易判断当前的点击是否处于窗体上方。\nroot对象的有趣之处 每个显示对象都有一个root属性。在将其加入到显示列表的时候，root就指向根显示对象。这个根显示对象，对于纯AS项目来说是主Class；对于Flash项目来说，就是文档类或者MainTimeline（取决于你是否定义了文档类）。\n如果root中没有任何的内容，那么即使对root进行了侦听，也是没办法得到鼠标事件的。这很容易理解，因为root是空的，它的里面必须有内容，才可以收到单击。\n可是，我在root的graphics对象中绘制了图形，依然是没办法收到鼠标事件。\n同样的，即使在root中添加的其他的非InteractiveObject（例如Bitmap、Shape等等）显示对象，也无法收到鼠标事件。\n还是用一个Sprite来侦听吧 一个普通的Sprite，只需要在其graphics属性中绘制图形，就可以接收到鼠标事件了。如果想禁用鼠标事件，只需要将该sprite的mouseEnabled设置为false就行了。稍稍麻烦一点的就是，在舞台大小变化的时候，需要重绘一下这个Sprite让它适合舞台而已。\n让人奇怪的是，既然root也是Sprite（AS项目的主Class是继承Sprite的），为啥就不行呢？\n下面是测试用的代码：\n1package 2{ 3 4import flash.display.Sprite; 5import flash.events.MouseEvent; 6import flash.display.Shape; 7/** 8 * 侦测鼠标事件 9 */ 10public class Main extends Sprite 11{ 12 public function Main() 13 { 14 this.mouseEnabled = true; 15 this.mouseChildren = true; 16 var __sprite:Sprite = new Sprite(); 17 __sprite.graphics.beginFill(0xFF0000); 18 __sprite.graphics.drawRect(0,0,this.stage.stageWidth, this.stage.stageHeight); 19 __sprite.graphics.endFill(); 20 //__sprite.mouseEnabled = false; 21 this.addChild(__sprite); 22 23 var __shape:Shape = new Shape(); 24 __shape.graphics.beginFill(0x000FF0); 25 __shape.graphics.drawRect(0,0,100, 100); 26 __shape.graphics.endFill(); 27 this.addChild(__shape); 28 this.addEventListener(MouseEvent.CLICK, handler_click); 29 30 } 31 32 private function draw():void 33 { 34 this.graphics.beginFill(0); 35 this.graphics.drawRect(0,0,this.stage.stageWidth, this.stage.stageHeight); 36 this.graphics.endFill(); 37 } 38 39 private function handler_click($evt:MouseEvent):void 40 { 41 trace(this.mouseX, this.mouseY); 42 } 43} 44} ","date":"2011-05-18","description":"","lastmod":"2011-05-18T06:52:23Z","slug":"global-mouse-event","tags":["as3"],"title":"全局鼠标事件应该怎么加？","url":"https://blog.zengrong.net/post/global-mouse-event/"},{"categories":["technology"],"content":"我知道，Timer是不准确的。但是，我从来没有想过，Timer是依赖于frameRate的。\n看下面的代码，你认为 _delay 的值是多少？我想，如果不是500，也应该是501,498之类的……\n但是，结果却是 500,1000,500,1000 的顺序间隔。不信就试试看 :wink:\n**2011-05-16 17:06更新：**貌似，将帧率设置成30也不准！我的FP是10.2.152.32，难道是传说中的BUG？\n**2011-05-16 18:10更新：**看了跑道模型和FP的渲染模式，但也无法解释这种现象：\n如果是说Timer受到跑道模型的影响，为何enterFrame是准确的？\n按跑道模型，EnterFrame事件应该在跑道模型第1步的时候执行，而Timer也是在这个时候进行计算的。如果enterFrame准确，那么timer也应该准确，起码不应该相差太多才是。\n可是500ms的差别，太大了。\n关于跑道模型的文章：\nFlash Player 10.1内部机制(第二部分) -执行模型之可变跑道\nFlash Player 10.1内部机制(第二部分) -执行模型之可变跑道（扩展）\n1package 2{ 3import flash.utils.Timer; 4import flash.events.TimerEvent; 5import flash.utils.getTimer; 6import flash.display.Sprite; 7import flash.events.MouseEvent; 8import flash.display.BitmapData; 9import flash.events.Event; 10 11/** 12 * 测试在CPU高负载的时候Timer与EnterFrame的间隔 13 */ 14[SWF(frameRate=2)] 15public class RunTest extends Sprite 16{ 17 18 public function RunTest() 19 { 20 _timer = new Timer(500); 21 _timer.addEventListener(TimerEvent.TIMER, handler_timer); 22 _timer.start(); 23 this.stage.addEventListener(MouseEvent.CLICK, handler_click); 24 //this.stage.addEventListener(Event.ENTER_FRAME, handler_enterFrame); 25 } 26 27 private var _timer:Timer; 28 private var _elapse:int = 0; 29 private var _delay:int = 0; 30 31 public function updateTime():void 32 { 33 var __thisTime:int = getTimer(); 34 _delay = __thisTime - _elapse; 35 trace(\u0026#39;_elapse:\u0026#39;, _elapse, \u0026#39;,_delay:\u0026#39;, _delay, \u0026#39;,getTimer:\u0026#39;, getTimer()); 36 _elapse = __thisTime; 37 } 38 39 public function handler_timer($evt:TimerEvent):void 40 { 41 updateTime(); 42 } 43 44 public function handler_enterFrame($evt:Event):void 45 { 46 updateTime(); 47 } 48 49 public function handler_click($evt:MouseEvent):void 50 { 51 trace(\u0026#39;draw\u0026#39;); 52 for(var i:int=0;i\u0026lt;1000; i++) 53 { 54 var __aaa:BitmapData = new BitmapData(550, 400); 55 __aaa.draw(this); 56 } 57 } 58} 59} 找到几篇类似中文文章介绍Timer的准确性问题：\n时间效率，Timer和EnterFrame在FP 10.1之后测试和建议 Timer与getTimer的准确率 Understanding AS3 Timer Class ","date":"2011-05-16","description":"","lastmod":"2011-05-16T08:35:47Z","slug":"timer-require-framerate","tags":["as3"],"title":"Timer是依赖于帧率的？","url":"https://blog.zengrong.net/post/timer-require-framerate/"},{"categories":["technology"],"content":"看过Ant and FCSH资源合集的童鞋应该都知道Flex Compiler SHell Server(后简称为FCSHServer)这个用VB开发的FCSH wrapper。本篇就介绍它的用法。\n1. 下载FCSHServer并安装；\n2. 设置环境变量FCSHServer={你的安装目录,例如：D:\\FcshServer}。zrong强烈建议你安装的文件夹不要包含空格，貌似环境变量设置之后，只有重启Windows才能起作用；\n3. 设置安装目录下server.ini中的sdk和java的值。sdk指向flex SDK文件夹，java指向JDK文件夹，例如：\n1sdk=d:\\flex_sdks\\4.1.0 2java=d:\\Java\\jdk1.6.0_25 4. 配置Ant项目，直接看代码吧，有注释。这个项目会经常更新，最新的版本看 这里：\nbuild.xml\n1\u0026lt;project name=\u0026#34;runtest\u0026#34; default=\u0026#34;build\u0026#34;\u0026gt; 2 3 \u0026lt;property file=\u0026#34;build.properties\u0026#34; /\u0026gt; 4 \u0026lt;!-- 取环境变量 --\u0026gt; 5 \u0026lt;property environment=\u0026#34;env\u0026#34;/\u0026gt; 6 \u0026lt;!-- 设定一个默认的主文件，默认编译的是RunTest.as。 7 若需要编译其他文件，可以调用ant -Dmain=Other，不需要扩展名 --\u0026gt; 8 \u0026lt;property name=\u0026#34;main\u0026#34; value=\u0026#34;RunTest\u0026#34; /\u0026gt; 9 \u0026lt;!-- fcsh的编译地址 --\u0026gt; 10 \u0026lt;taskdef name=\u0026#34;fcsh\u0026#34; classname=\u0026#34;fcsh\u0026#34; classpath=\u0026#34;${env.FCSHServer}/fcsh.jar\u0026#34; /\u0026gt; 11 12 \u0026lt;target name=\u0026#34;build\u0026#34; depends=\u0026#34;init,compile,fdb\u0026#34; /\u0026gt; 13 14 \u0026lt;!-- 清理目录，复制没有嵌入的文件 --\u0026gt; 15 \u0026lt;target name=\u0026#34;init\u0026#34;\u0026gt; 16 \u0026lt;delete dir=\u0026#34;${DEPLOY_DIR}/test_${main}.swf\u0026#34; /\u0026gt; 17 \u0026lt;delete dir=\u0026#34;${DEPLOY_DIR}/assets\u0026#34; /\u0026gt; 18 \u0026lt;mkdir dir=\u0026#34;${DEPLOY_DIR}/assets\u0026#34; /\u0026gt; 19 \u0026lt;copy todir=\u0026#34;${DEPLOY_DIR}/assets\u0026#34;\u0026gt; 20 \u0026lt;fileset dir=\u0026#34;${SRC_DIR}/assets\u0026#34; excludes=\u0026#34;**/source/\u0026#34; /\u0026gt; 21 \u0026lt;/copy\u0026gt; 22 \u0026lt;/target\u0026gt; 23 24 \u0026lt;!-- 编译 --\u0026gt; 25 \u0026lt;target name=\u0026#34;compile\u0026#34;\u0026gt; 26 \u0026lt;fcsh consoleencoding=\u0026#34;UTF8\u0026#34;\u0026gt; 27 \u0026lt;arg value=\u0026#34;mxmlc ${SRC_DIR}/${main}.as\u0026#34;/\u0026gt; 28 \u0026lt;arg value=\u0026#34;-output=${DEPLOY_DIR}/test_${main}.swf\u0026#34;/\u0026gt; 29 \u0026lt;!-- 这个参数其实可要可不要，因为编译的时候，默认会调用这个xml文件。--\u0026gt; 30 \u0026lt;arg value=\u0026#34;-load-config=${FLEX_HOME}/frameworks/flex-config.xml\u0026#34;/\u0026gt; 31 \u0026lt;arg value=\u0026#34;-source-path=${LIBS_DUDU}\u0026#34; /\u0026gt; 32 \u0026lt;arg value=\u0026#34;-debug=true\u0026#34; /\u0026gt; 33 \u0026lt;arg value=\u0026#34;-static-link-runtime-shared-libraries=true\u0026#34;/\u0026gt; 34 \u0026lt;/fcsh\u0026gt; 35 \u0026lt;/target\u0026gt; 36 37 \u0026lt;!-- 打开调试器进行调试 --\u0026gt; 38 \u0026lt;target name=\u0026#34;fdb\u0026#34;\u0026gt; 39 \u0026lt;exec executable=\u0026#34;cmd\u0026#34; spawn=\u0026#34;true\u0026#34; osfamily=\u0026#34;windows\u0026#34;\u0026gt; 40 \u0026lt;arg line=\u0026#34;/K start fdb ${DEPLOY_DIR}/test_${main}.swf\u0026#34; /\u0026gt; 41 \u0026lt;/exec\u0026gt; 42 \u0026lt;/target\u0026gt; 43\u0026lt;/project\u0026gt; build.properties\n1# 设置FLEX SDK的路径 2FLEX_HOME=D:/flex_sdks/4.1.0 3 4# 设置源文件路径 5# {$basedir} 就是本文件所在的目录 6SRC_DIR =${basedir}/src 7 8# libs目录，一般用来放swc文件 9LIBS_DIR =${basedir}/libs 10 11# 这个就是Flash Builder建立的bin-debug 12DEPLOY_DIR = ${basedir}/bin-debug 13 14#自定义的几个类库源码 15LIBS_DUDU = e:/works/duduw_as3lib/src ","date":"2011-05-09","description":"","lastmod":"2011-05-09T10:11:52Z","slug":"flex-compiler-shell-server-config","tags":["ant","flashbuilder","flex"],"title":"Flex Compiler SHell Server配置方法","url":"https://blog.zengrong.net/post/flex-compiler-shell-server-config/"},{"categories":["technology"],"content":"2011-12-28更新： 对于Flash Builder4.6来说，不需要进行下面仅针对4.5的复杂操作，而是直接运行安装目录下的“utilities\\Adobe Flash Builder 4.6 Plug-in Utility.exe”，并根据提示操作，即可完成插件版安装。 2015-09-18更新： 大部分连接都已经失效，删除。\n早上在闲逛的时候，4.5正式版还没影子，结果下午一看，就发布了：（链接因失效删除）\n当然，Flash Builder 4.5也发布了，独立版的下载地址：（链接因失效删除）\n专供PHP开发者的版本（链接因失效删除）（900多MB，Adobe还真是越来越大，怀念Macromedia……）\n看看有什么新东西：http://www.adobe.com/devnet/flash-builder/articles/whatsnew-flashbuilder-45.html\nfor PHP开发者：http://www.adobe.com/devnet/flash-builder/articles/introducing-flashbuilder45-php.html\n等等，插件版在哪里？\n这次Flash Builder没有提供专门的插件版（或许以后也不会有了），如果希望安装插件版，可以参考下面的步骤（来自RIAMetting）：\n首先，下载独立版的Flash Builder 4.5，并安装；\n安装完毕后，找到安装目录，下面有一个utilities的文件夹，打开，双击Adobe Flash Builder 4.5 Plug-in Utility.exe这个文件，会出现提示画面：\n选择语言，然后点击确定，按照提示，一步一步执行（定位FB和Eclipse的安装位置），最后安装完成后，注意屏幕上的提示语句，安装说明完成对eclipse.ini的修改：\n启动Eclipse，切换透视图，选择Flash，就看到熟悉的Flex开发环境了。\n","date":"2011-05-03","description":"","lastmod":"2015-09-18T08:58:39Z","slug":"flash-builder-45","tags":["flashbuilder"],"title":"Flash Builder 4.5 插件版的安装方法","url":"https://blog.zengrong.net/post/flash-builder-45/"},{"categories":["technology"],"content":"如果你和我一样，正在或者准备用纯Flex SDK开发SWF或AIR，那么你一定需要FCSH。\nfcsh(the Flex compiler shell)是一个基于命令行的编译环境，你可以用它来编译应用程序,模块,和SWC库。它的工作方式与mxmlc和compc类似，但编译速度则要快很多。原因是fcsh会利用内存和缓存。想看更多的介绍，可以看看这里：Using fcsh, the Flex compiler shell\n现在，你已经知道使用SDK编译程序比Flash Builder要慢许多的原因了（实际上，Flash Builder第一次编译程序也是很慢的，但编译之后，它就在后台运行了一个fcsh用来加速）\nfcsh命令行的使用方法没有什么好说的，直接看上面的链接即可。fcsh是使用命令行来调用mxmlc的，而且fcsh必须始终保持运行，并总是使用这个进程进行编译，否则就没有优化效果。所以，现在有个问题：\n如果你要用ant来编译程序，Ant无法保证fcsh始终在后台运行。因此就有许多网友开发了一些Wrapper程序来保证fcsh始终在后台运行。简单的说，这些Wrapper就是服务器，而Ant变成了客户端，将资源发送给服务器编译。\n我将找到的这类资源整理成一个列表，如果你和我一样用纯Flex SDK的话，或许有用：\nfsch，Wrapper for Adobe Flex Compiler SHell + ant tasks. Visual Basic 6.0.\n你也看到了，这个是用VB开发的，所以只能运行在Windows平台上。\nFCSH Wrapper\nJAVA开发的Wrapper，理论上可以用于多平台。博客基于blogspot，所以大多数时候要翻墙才能访问。\nBigSource Zarkov\nEclipse插件，功能很多。但如果使用独立版（非Eclipse版），则不支持FCSH\n","date":"2011-05-03","description":"","lastmod":"2011-05-03T02:04:01Z","slug":"1317","tags":["ant","flashbuilder","flex"],"title":"Ant and FCSH资源合集","url":"https://blog.zengrong.net/post/1317/"},{"categories":["technology"],"content":"或许很少有人像我这么极端吧，放着好好的盗版XP和盗版Flash Builder不用，偏要去鼓捣什么Vim……\n当然，在Vim下面，你可以自己写脚本调用Flex SDK的命令行编译器mxmlc进行编译，但我更愿意用Ant，原因如下：\n配置方便\n基于XML的配置文件，比命令行好读好写； JAVA原生\nAnt是JAVA写的，Flex SDK的编译器（mxmlc、compc、asdoc……）也是JAVA写的； 通用\n换用Linux或Mac，也一样用，顶多改改变量中的路径。 在Flash Builder下面，按F11后，就可以自动编译swf并打开Flash Player，并显示trace信息，以及调试断点。在Vim中也能这样么？\n大部分可以。我们可以用fdb进行swf的调试工作，能看到trace信息，也能设置断点和进行调试，可惜没有Flash Builder那么直观。\n但是！！！你可以用Vim写AS代码了！塞翁失马你有没有！！！！\n那么，开始吧！\n(注意，本文假定你了解Vim)\n安装Ant 到Ant下载页面下载最新版的Ant。我将它解压到D:\\ant； Ant需要安装JRE，但是如果你安装JDK的话，支持的task更多，所以现在下载JDK并安装，我将其安装到D:\\Java\\jdk1.6.0_25； 设置环境变量JAVA_HOME到D:\\Java\\jdk1.6.0_25，设置环境变量ANT_HOME到D:\\ant； 将D:\\Java\\jdk1.6.0_25\\bin和D:\\ant\\bin加入环境变量PATH。 Vim的设置 只需一步，将下面这句代码加入你的vimrc即可：\nset makeprg=ant \u0026quot;设置编译器为ant Ant的设置 将下面两个文件复制到你的项目文件夹（即src文件夹的上层文件夹）这两个文件的最新版本在这里：https://gist.github.com/944712\n1# 设置FLEX SDK的路径 2FLEX_HOME=c:/Program Files/Adobe/FlashBuilder4Plug-in/sdks/4.1.0 3 4# 设置源文件路径 5# {$basedir} 就是本文件所在的目录 6SRC_DIR =${basedir}/src 7 8# libs目录，一般用来放swc文件 9LIBS_DIR =${basedir}/libs 10 11# 这个就是Flash Builder建立的bin-debug 12DEPLOY_DIR = ${basedir}/bin-debug 13 14#自定义的类库源码 15LIBS_DUDU = e:/works/duduw_as3lib/src 1\u0026lt;!-- 载入配置文件 --\u0026gt; 2 3\u0026lt;!-- 确定flexTasks.jar的位置 --\u0026gt; 4 5\u0026lt;!-- 清理部署目录中的内容 --\u0026gt; 6 7\u0026lt;!-- 将资源目录复制到部署目录 --\u0026gt; 8 9\u0026lt;!-- 编译 --\u0026gt; 10 11\u0026lt;!-- 需要libs的时候解开注释 12\u0026lt;compiler.library-path dir=\u0026#34;${basedir}\u0026#34; append=\u0026#34;true\u0026#34;\u0026gt; 13\t\u0026lt;include name=\u0026#34;libs\u0026#34; /\u0026gt; 14\u0026lt;/compiler.library-path\u0026gt; 15--\u0026gt; 16\u0026lt;!-- 必须加上这行，如果不加，当使用[Embed]的标签的时候，就会出现VerifyError: Error #1014: 无法找到类 。 原因应该是没有将mx.core包编译进入。官方文档说这个属性默认是true， 不要相信它--\u0026gt; 17true 18\u0026lt;!-- 编译成可调试的版本 --\u0026gt; 19true\t20 21\u0026lt;!-- 打开调试器进行调试 --\u0026gt; 22 23\u0026lt;!-- 不能直接调用fdb，因为这样不会打开新的命令行窗口，必须使用/K或者/C参数，加上start来启动fdb --\u0026gt; 编译 这个就简单了，只需要在Vim下执行下面的语句：\n:make 或者\n:!ant 当然，也可以带参数。例如你的项目中有一个以上的主文件，你可以制作build_a.xml、build_b.xml等等，然后执行：\n:make -f build_a.xml 看图片吧！ 延伸阅读：设置Vim的errorformat以支持mxmlc编译器\n","date":"2011-04-29","description":"","lastmod":"2011-04-29T02:00:11Z","slug":"vim-ant-swf","tags":["ant","flashbuilder","flex","linux","vim"],"title":"Vim调用Ant编译swf并自动调试","url":"https://blog.zengrong.net/post/vim-ant-swf/"},{"categories":["technology"],"content":"2011-04-26：v0.3版发布\n1. 加入了XML格式的元数据导出功能。 2. 可以打开由SpriteSheetPacker保存的SS格式文件。 3. 如果以SheetSprite方式打开jpg或png文件，可以提供一个SpriteSheetPacker生成的元数据xml文件，SpriteSheetPacker会根据元数据进行解析；若没有提供元数据，会自动在图像文件所在路径寻找同名xml文件。包含mask信息的jpg文件，会自动应用Alpha通道。\n用这个文件来测试：\n1 文件 4. 一些界面上的修改，就不说了。\n更多的功能介绍以及软件下载，看这里。\n","date":"2011-04-26","description":"","lastmod":"2011-04-26T09:30:47Z","slug":"1313","tags":["air","bitmapdata","spritesheet"],"title":"SpriteSheet小工具：spritesheetpacker v0.3","url":"https://blog.zengrong.net/post/1313/"},{"categories":["technology"],"content":"修改列表：\n修正了图像排列的BUG；\n将保存图像和元数据信息合并到一个菜单，便于对照；\n保存元数据的时候可以“包含附加信息”。附加信息包含：是否mask、有没有label、有没有包含名称、总帧数等等。\n附加信息选项只会影响元数据，SS格式嵌入的数据总是包含附加信息的。\n保存SS格式图像和元数据的时候，可以“包含名称”；\n有时候我们希望用名称来查找一个Sheet中的Sprite。包含名称功能是独立的，不受“包含附加信息”影响；\nSWF视图中不能使用“包含名称”功能；\n名称自动使用图片的主文件名；\n将文件名列表截断，只显示文件名，不显示路径。\n更多的功能介绍以及软件下载，看这里。\n","date":"2011-04-22","description":"","lastmod":"2011-04-22T10:34:38Z","slug":"1311","tags":["air","bitmapdata","spritesheet"],"title":"编辑SpriteSheet的小工具：spritesheetpacker v0.2","url":"https://blog.zengrong.net/post/1311/"},{"categories":["technology"],"content":"flash动画是如何兴起和衰退的？\nHow did the Flash MV rise and fall?\n这篇文章是我在之乎上的一个回答：http://www.zhihu.com/question/23940320/answer/26149559\n原文提问： 记得刚上网那会，flash动画还是很火的，比如有什么 大话三国，秋水堂，火柴人格斗，东北人都是活雷锋等数不完的MV二次制作，现在怎么就看不到看到当年的盛况了。\n我的回答： 因为大家都去看美剧了。\n=============== 为了报答题主邀请之恩，认真回答下。\n上面的回答是很认真的，大家真的都去看美剧了。\nFlash动画（专业点叫Flash MV）这种媒体表现形式和Flash的兴衰有很大的关系。2001年到2008年是Flash MV发展的黄金时期。让我们看看那段时间发生了什么。\n图片出处：Actionscript,AS3,MXML,Flex,Flex Builder,Flash Builder,Flash,AIR,Flash Player之关系\n1. 没有之一 2001年前后，互联网上的动画表现形式，一个是GIF，另一个还是GIF。那些IE浏览器专用的跑马灯之类效果就不算了吧？基于浏览器看视频还必须要装神马Media Player插件Real Player插件吧？那时候大家还是用Real Player看VCD光盘上的大话西游和女神兰兰的动作片吧？还在用超级解霸看租来的港片VCD吧？\n嗯，我说的是动画，怎么又扯到视频？在那个没有网吧只有游戏吧（一种有十几台电脑只连局域网的小黑屋）的年代，只要有点能动起来的东西，管他动画还是视频，大家都乐意看啊。\n于是，Flash突然就火了。这么小的文件（几百KB~几MB），这样精美的画面，放大不失真（基于矢量）的效果，支持流式播放，边下载边播放，能支持交互正看重看倒看或者点个“开始播放”神马的……\n在那个网速64K/512K/1024K的年代，这真的是业界良心啊！\n而且，那个时候在互联网上能流畅播放的可以动起来的大段的内容，还！只！有！Flash！！！\n不火才怪了。\n2. 自掘坟墓 既然后面蓬勃发展的事情你都知道了，那我们就直接进入高潮吧。\nFlash MX(Flash Player 6)开始，Macromedia 给Flash加入了支持播放视频的能力。当时可以在SWF格式的文件中嵌入视频数据，依然支持流播放。\n然后Flash MX 2004(Flash Player 7)开始把视频单独作为一种文件格式提出来，这就是大名鼎鼎的FLV格式。\nFLV这种视频格式的出现引爆了视频流媒体整个行业。因为那时Flash播放器的装机率已经超过95%（当然现在也是啦），用Flash做一个几十KB的小播放器，然后用这个小播放器来流式播放FLV视频文件，做一个视频点播网站不是妥妥的么！\n后面的事情，我们都知道了。Youtube、土豆、优酷等等就起来了。\nMacromedia/Adobe 后来一直在改善FLV格式，视频编码从Sorenson Spark，到On VP6再到H.264，音频编码从MP3到AAC，容器格式从FLV到F4V/MP4……这些修改让这种小众的视频格式越来越优秀和开放，最后成了FLV成了主流的视频格式。现在你想找一款不支持FLV的视频播放器，还真难。 抛弃FLV，迎接MP4——制作Flash Player支持的H.264视频格式\n当然，FLV的流行与 FFmpeg 和 MEncoder 这两套开源codec的支持是分不开的。觉得上传任何视频文件都能帮你转换成在线视频的网站很牛B？他们只是在后台运行了一套自动转换程序而已。\n当然，做视频站不是那么简单的事情，我这里只是想表达，这个技术已经主流化，免费化了，简单化了。在这种技术支撑下，FLV想不火都难。\n大家在互联网上关注的，无非是吸引眼球的，好看的，精美的，有趣的，色情的（我有说么）……这些美剧日剧韩剧都能给你，那为毛还要看Flash MV这种上世纪的东西？\n3. 百花齐放 到了AS3时代，Flash的性能大幅提升，语言大幅强化，在企业开发和交互站点上进行过一番尝试和推广。有段时间，招行的网银专业版甚至都使用了Flex技术。一时间画板应用、虚拟现实、在线音频视频编辑器等等充斥互联网。Adobe抛出RIA(Rich Internet Application)概念，意思是说你那半成品Web2.0和AJAX算个毛线啊……\n虽然在企业开发中使用比较广泛，但大众并不了解RIA是个什么东西。我可以不负责任的说，那时的RIA体验绝对比AJAX好很多，可是……可是……（不说了，说起来都是泪）\n由于后来出现Adobe对Flash Player高级功能收费以及 放弃Linux平台 、被乔帮主忽悠、被Unity3D等平台捧杀 等等事件。Adobe自知在企业市场干不过H5，将Flash的主要发展方向转向了视频和游戏。Flash Player在移动设备上的失败，造就了AIR在手机游戏上的成功。彼时，已经有更多漂亮的，大胆的（你敢说没看过页游广告妹妹的招牌动作？）Flash网页游戏和更多的手机游戏可玩，Flash MV还有什么吸引力？\n4. 苟延残喘 当然，Flash MV的生命力仍在。我经常在公交车上看到一些相声段子或者视频广告，一看就是上世纪Flash MV的手法。他们在二十一世纪依然占据着一席之地，而且会持续下去。\nFlash 是个优秀的软件，大量的美术工作者可以在不懂程序的情况下，使用她做出精美的交互动画。但成也萧何，败也萧何。大量不懂程序的美术工作者做出的交互作品，对于互联网和浏览器就是一个噩梦。乔布斯忽悠大家说Flash性能低占CPU高，Adobe还真的浑身是嘴也说不清。但各种小白们还真的能找到各种理由证明帮主的忽悠是真实的谎言。\n何况，Adobe根本就没说什么。\nFlash 和Flash MV依然会有市场，它们还会在自己的领域继续发光，Adobe依然可以开心地卖软件，Macromedia依然是死不瞑目。\n只是——这世界变了。\n","date":"2011-04-21","description":"","lastmod":"2011-04-21T12:42:42Z","slug":"how_did_the_flash_mv_rise_and_fall","tags":["actionscript","air","as3","flash","flashbuilder","flashplayer","flex","study"],"title":"Actionscript,AS3,MXML,Flex,Flex Builder,Flash Builder,Flash,AIR,Flash Player之关系","url":"https://blog.zengrong.net/post/how_did_the_flash_mv_rise_and_fall/"},{"categories":["technology"],"content":"游戏开发中经常会用到这类工具，网上找的工具都不太符合自己的需求，还有很多要收费，所以就自己写了一个（不知道什么是SpriteSheet可看这里）。\n具体功能主要有以下几个：\n将SWF动画导出成SpriteSheet或者PNG序列； 将元数据信息导出成JSON格式或文本格式； 将多个PNG或者JPG图片拼成一张大图； 将Alpha通道以MASK方式保存在JPG图中，以减小文件大小。 更多的功能介绍以及软件下载，看这里。\n","date":"2011-04-19","description":"","lastmod":"2011-04-19T03:32:27Z","slug":"1306","tags":["air","bitmapdata","spritesheet"],"title":"编辑SpriteSheet的小工具：spritesheetpacker v0.1发布","url":"https://blog.zengrong.net/post/1306/"},{"categories":[],"content":"2011-08-18：v0.5.0版发布，同时更名为Sprite Sheet Editor，请访问Sprite Sheet Editor的页面获得最新版本。本页面不再更新。\n下载和安装：\n安装AIR\n1 文件 [gallery link=\u0026quot;file\u0026quot; columns=\u0026quot;3\u0026quot;]\n2011-06-30：v0.4版发布\n将菜单改为按钮，避免Mac等不支持菜单的操作系统无法显示菜单； 可以打开SpriteSheet格式的图片，然后保存成其他格式。即可以在SS、JPG、PNG之间互转格式； 可以打开已有的SpriteSheet，修改Metadata后保存成新的文件； 解决0.3版手动提供的元数据无效的问题； 播放位图动画的时候，在Sheet预览中显示当前帧的范围； 解决打开的SpriteSheet格式的Label起始帧显示不正常的问题； 采用SDK2.7编译。因此需要卸载原来的软件，再升级AIR Runtime，才能正常安装。（AIR的版本兼容性很糟糕，经常无法安装，而且给出错误的提示） 2011-04-26：v0.3版发布\n加入了XML格式的元数据导出功能。 可以打开由SpriteSheetPacker保存的SS格式文件。 如果以SheetSprite方式打开jpg或png文件，可以提供一个SpriteSheetPacker生成的元数据xml文件，SpriteSheetPacker会根据元数据进行解析；若没有提供元数据，会自动在图像文件所在路径寻找同名xml文件。包含mask信息的jpg文件，会自动应用Alpha通道。\n用这个文件来测试： 1 文件 一些界面上的修改，就不说了。 2011-04-22：v0.2版发布\n修正了图像排列的BUG； 将保存图像和元数据信息合并到一个菜单，便于对照； 保存元数据的时候可以“包含附加信息”。附加信息包含：是否mask、有没有label、有没有包含名称、总帧数等等。 附加信息选项只会影响元数据，SS格式嵌入的数据总是包含附加信息的。 保存SS格式图像和元数据的时候，可以“包含名称”。有时候我们希望用名称来查找一个Sheet中的Sprite。包含名称功能是独立的，不受“包含附加信息”影响。 SWF视图中不能使用“包含名称”功能。名称自动使用图片的主文件名； 将文件名列表截断，只显示文件名，不显示路径。 2011-04-19：v0.1版发布\nQ：这东东是干嘛的？\nA：我做游戏的时候，经常需要使用SpriteSheet（也称为TileSheet）技术。网上找到的工具都不太好用，所以自己写了个。就这样。\nQ：这东东能处理SWF文件？\nA：对，它能把SWF变成SpriteSheet，也能将SWF导出成PNG序列。即使你的SWF只有一帧，里面用的MC实现的动画，它也能做到。你知道的，Flash的导出序列图功能，基本上形同虚设。:D\nQ：能把多张小图拼成SpriteSheet么？\nA：当然可以。你可以设定使用原图大小，也可以只拼接每张图的一个固定的区域（使用“设置-帧”面板）。\nQ：帧设置面板里面的那个“Label”是什么意思？\nA：有时候，我们会在一个SWF文件中包含多个Label，例如一个人物形象有跑、跳、死亡等等不同的动作。在SWF中很容易表现，加帧标签或者用MC区分。使用SpriteSheet怎么办呢？就可以用这个Label了。\n你可以指定Label的起始帧和总帧数。然后添加到右边的Label列表。每个Label包含的帧允许有重复。\n（抱歉用了两段，太罗嗦了。恩？加上这段好像是三段？）\nQ：没关系的，谢谢你解释清楚。不过我还是要问一下帧设置里面的那个“使用Mask”是什么意思？\nA：你知道的，JPG文件在文件大小上，某些时候是有一些优势的。对于一些颜色很复杂的SpriteSheet，你可能会发现PNG文件比JPG文件大很多，于是你希望用JPG格式。但是！JPG不支持透明有没有！！用JPG你伤不起啊！！！\n可是，你可以把PNG中的Alpha通道提取出来，在JPG文件中以MASK的方式保存。使用它的时候，再通过bitmapData的copyChannel合并这个MASK，就能得到透明的图了。这岂不是鱼与熊掌兼得的好事么！\nQ：你是马教主的传人么？\nA：你也是么？我太高兴了有没有！！恩，我顺便再说一下帧面板中的“修改FPS”的功能吧。它可以直接修改AIR程序的FPS，会影响位图动画预览的速度，也会影响SWF动画位图序列生成的速度。\nQ：-_-|||“SS格式”是一种什么格式？\nA：不告诉你。\nQ：AIR支持自动升级，你为什么不做？\nA：我懒。\nQ：你有参考其他人的软件或者代码么？\nA：我盗取了Keith Peters大神的作品SWFSheet的创意和思路（虽然他不认识我，也看不懂这些，但我还是要向他致敬）；还有另一位大神Grant Skinner的作品Zoe，我也安装并测试了，对本软件有一定影响；还有TexturePacker软件，这个挺好用，可惜要收钱；还有RIAidea的一篇关于PNG瘦身的文章，本软件中关于MASK部分的思路就来自这里。\nQ：你这个软件的图标好丑。\nA：我根本没做图标好不好！你有本事帮我做个？\nQ：这个工具的源码在哪里下载？\nA：暂时不会公开源码（主要原因是写的太烂，不好意思）。等我做成熟了可能会考虑公开。\nQ：你是谁？\nA：Kao！问了半天你不知道我是谁！我是个写代码的家伙，zengrong.net是我的博客。i@zengrong.net是我的邮箱。\n","date":"2011-04-19","description":"","lastmod":"2011-04-19T03:18:04Z","slug":"spritesheetpacker","tags":[],"title":"SpriteSheetPacker","url":"https://blog.zengrong.net/spritesheetpacker/"},{"categories":["web"],"content":"从beta版开始，我一直在用有道词典，小巧且绿色，基本能满足需要。\n今天偶尔发现有道词典中有“有道网页翻译2.0beta”的广告，点击试用了一下，感觉还不错。\n与Google translate比较，个人感觉有以下几点优缺点：\n优点：\n不会被墙（Google translate经常被墙，影响心情和效率） 提供翻译难词模式，只显示难词的示意，不会让全文翻译影响阅读 缺点：\n测试版有些不稳定，某些网站无法翻译，例如这个（当然这是测试版的原因，且Google translate的“不稳定”情况更严重） 翻译质量 ","date":"2011-04-18","description":"","lastmod":"2011-04-18T01:48:46Z","slug":"youdao-web-translate","tags":["translate"],"title":"有道网页翻译","url":"https://blog.zengrong.net/post/youdao-web-translate/"},{"categories":["use"],"content":"貌似从版本9开始，Chrome浏览器在Gmail和QQ邮箱中就不能上传附件。在QQ邮箱中，即使安装了超大附件支持插件，在单击“上传”按钮的时候也不能打开选择文件的对话框。\n其实，造成这个问题的原因就是在chrome 9的某个版本后，Chrome使用了沙箱来限制Flash Player在Chrome浏览器中的权限，导致依赖Flash Player的某些动作在Chrome中无法执行。\n对于Gmail，可以通过邮箱设置来解决：\n进入Settings(设置)-\u0026gt;General(常规)，将最下方的Attachments(附件)改为Basic attachment features(基本附件功能)。\n根本的方法，是禁用Flash沙箱，在Chrome启动的时候加上参数 -disable-flash-sandbox即可，如下图：\n还有一个我喜欢用的参数是 -disable-tabbed-options，用来取消Tab设置面板，将Chrome设置面板还原成对话框形式。\n","date":"2011-04-09","description":"","lastmod":"2011-04-09T00:58:57Z","slug":"1292","tags":["chrome","flash"],"title":"解决Chrome谷歌浏览器在Gmail、QQ邮箱中不能上传附件的问题","url":"https://blog.zengrong.net/post/1292/"},{"categories":["technology"],"content":"一、起因 公司采用svn管理源码，因此我一直是用git svn来与svn服务器进行同步。由于当前工作的版本库中包含多个项目，而权限也没有细分，所以每个使用版本库的人都可以管理其他人的项目。这样一旦出现误操作，就会非常难以恢复。我前段时间就 碰上了这样的事情，还好找回了大部分的文件。\n解决的办法有两个：\n自己单独使用git管理源码 新建一个独立的svn库并设定独立权限 在windows server 2003上通过cygwin安装git服务器未果后，我只能选择后者了。\n二、svn部分 当前工作的版本库的路径为: http://10.0.0.5:8080/svn/kaitian/trunk/fight/KTFight ，其中kaitian是该版本库的根目录。\n我在服务器上新建了一个版本库，名为ktfight，路径为：http://10.0.0.5:8080/svn/ktfight ，希望这个版本库对应 http://10.0.0.5:8080/svn/kaitian/trunk/fight 的所有内容。\n然后就是转移版本库的工作。原本考虑使用svnsync命令，但发现这个命令仅能sync根目录，不支持子目录。而我不希望新的版本库中保留原来版本库的其他项目的内容，放弃。\n后来确定的命令是svndumpfilter，操作如下：\n1#备份原始版本库 2svnadmin dump kaitian \u0026gt; kaitian.dump 3#将备份的版本库进行过滤 4type kaitian.dump | svndumpfilter include trunk/fight \u0026gt; fight.dump 这样就得到了一个过滤后的版本库，只包含需要的项目。\n原来的项目的路径（相对于版本库根目录）实在很长，可以通过查找和替换缩短路径，方法如下：\n使用WinHex打开fight.dump，搜索trunk/fight，将其替换为trunk。这里我原本打算将其直接放在根目录，但替换为/或者替换为空导致最后倒入都不成功，因此就改为替换成trunk了。能短一点就短一点吧。见下面两张图。\n1#创建新的版本库 2svnadmin create ktfight 3#载入过滤后的版本库 4svnadmin load ktfight \u0026lt; fight.dump 三、git部分 现在需要将git项目指向新的svn文件夹，我开始的想法是直接修改配置文件中的路径。\n在原来的git项目下输入git svn info，可以看到svn服务器的信息，这里的信息还是指向原来的路径：\n1$git svn info 2Path: . 3URL: http://10.0.0.5:8080/svn/kaitian/trunk/fight/KTFight 4Repository Root: http://10.0.0.5:8080/svn/kaitian 5Repository UUID: b60ff3ac-2211-494c-89f2-e927ee7ddff6 6Revision: 191 7Node Kind: directory 8Schedule: normal 9Last Changed Author: zrong 10Last Changed Rev: 191 11Last Changed Date: 2011-03-31 05:25:13 +0000 (星期四, 31 三月 2011) 修改配置文件中的路径：\n1git config svn-remote.svn.url http://10.0.0.5:8080/svn/ktfight/trunk/KTFight 2vim .svn/.metadata 可以看到.metadata中的内容如下，注释提醒不应该进行编辑：\n; This file is used internally by git-svn\n; You should not have to edit it\n[svn-remote \u0026quot;svn\u0026quot;]\nreposRoot = http://gameserver:8080/svn/ktfight\nuuid = b60ff3ac-2211-494c-89f2-e927ee7ddff6\n修改reposRoot的值为版本库的根路径（http://10.0.0.5:8080/svn/ktfight）后保存。\n使用git svn info查看新的svn信息。\n如果成功，恭喜你！！！你比我人品好1000倍！\n我当然是没有成功的，看来注释中的提示还是应该遵守DI Orz……\ngit的错误提示如下：\nUnable to determine upstream SVN information from working tree history\n于是我使用了一个更笨的方法：\n1 git svn clone http://10.0.0.5:8080/svn/ktfight/trunk/KTFight 虽然笨，但够直接。\n参考：http://www.aspzz.com.cn/c/man/svn/svn-ch-5-sect-3.html#svn-ch-5-sect-3.1.3\n（全文完）\n","date":"2011-03-31","description":"","lastmod":"2011-03-31T06:31:24Z","slug":"move-svn-path-under-git","tags":["git","svn"],"title":"移动git管理的svn版本库的路径","url":"https://blog.zengrong.net/post/move-svn-path-under-git/"},{"categories":["technology"],"content":"今天碰到了一个关于Git的问题，Git Community Book 中文版的译者liuhui998帮忙解决了，感谢！记录如下：\nQ:\n我发现丢失了一个重要的文件，执行步骤如下：\n把mvc/view/Fight.as移动到了mvc/Fight.as，做了大量的修改，做了一次提交； 使用git svn rebase，此时git提示mvc/view/Fight.as冲突； 执行git rm mvc/view/Fight.as移除了这个文件； 执行git rebase --continue，rebase成功； 执行git svn dcommit提交到svn。 但我随后发现mvc/Fight.as不见了，在历史记录中，也只能找到mvc/view/Fight.as的内容。\n请问：还能找回我修改过的mvc/Fight.as么？\nA:\n试一下git fsck --lost-found，可以找到很多dangling commit。\n再用git cat-file -p看一下每个commit里面有没有删掉的内容（只看commit即可）\n如果有你要想的commit，再执行git rebase 'commit-name'。\n","date":"2011-03-23","description":"","lastmod":"2011-03-23T08:00:36Z","slug":"1286","tags":["git"],"title":"找回git svn rebase中因冲突而丢失的文件","url":"https://blog.zengrong.net/post/1286/"},{"categories":["technology"],"content":"2011-07-01更新：关于字符串创建时间的计算，以及一些新发现。\n一个既需要顺序读取又需要随机读取的表，采用什么进行存储比较合适呢？\n我们知道，Object和Dictionary适合随机存取，而Array和Vector适合顺序存取（**关于Array和Vector的性能可以看这篇文章： **Array/Vector/AS3DS/ds/dsforas性能比较 ），但它们之间的性能差距有大呢？\n分析如下：\nObject的写性能低于Dictionary； Object与Dictionary的读写性能基本相当； Object在使用数字键名的时候，读写速度接近Array； AS会优化使用过的Object的字符串键名，因此对于整个程序的运行时间来说，创建字符串的时间不是问题；但对于性能测试来说，是很大的问题； AS也会优化创建变量的过程（或许是在编辑器层次优化？），所以对于字符串和数字等类型，把var放在循环外部，是一种良好的习惯，但对性能或许影响不大（相比而言，new的影响更大）； 对于Object和Dictionary的读取，使用for each循环性能最高，已经接近Array了； 对于Array的读取，for循环性能最高。 为什么for each循环在Object与Array上的表现正好相反呢？这正体现了这两种数据结构的特性，for在顺序存取上性能高，for each在随机存取上性能高。因此，对于Object和Dictionary的读取，应尽量使用for each，而对Array和Vector则尽量使用for。\nfor each和for in循环在Object与Array上的性能，取决于Object是采用字符串键名还是采用数字键名。如果Object采用的是字符串键名，则for each和for in在Object上的表现就与Array一致。否则，她们的表现就与Array正好相反。\n测试结果：\nObject write:7582\nObject for read:2309\nObject for in read:234\nObject for each read:103\nDictionary write:2557\nDictionary for read:2302\nDictionary for in read:242\nDictionary for each read:94\nArray write:90\nArray for read:64\nArray for in read:81\nArray for each read:79\n1package 2{ 3import flash.display.Sprite; 4import flash.utils.Dictionary; 5import flash.utils.getTimer; 6 7/** 8 * 比较Object/Dictionary/Array顺序读写性能 9 * @author zrong 10 */ 11public class ObjectForSpeedTest extends Sprite 12{ 13\tpublic function ObjectForSpeedTest() 14\t{ 15\tconst max:int = 500000; 16\tvar i:int = 0; 17\tvar r:* = null; 18\t_obj = {}; 19\t_tim = getTimer(); 20\tfor(i=0;i\u0026lt;max;i++) 21\t{ 22\t_obj[\u0026#39;a\u0026#39;+i] = i; 23\t} 24\ttrace2(\u0026#39;Object write\u0026#39;); 25\t26\t_tim = getTimer(); 27\tfor(i=0;i\u0026lt;max;i++) 28\t{ 29\tr = _obj[\u0026#39;a\u0026#39;+i]; 30\t} 31\ttrace2(\u0026#39;Object for read\u0026#39;); 32\t33\t_tim = getTimer(); 34\tfor(var __objin:* in _obj) 35\t{ 36\tr = _obj[__objin]; 37\t} 38\ttrace2(\u0026#39;Object for in read\u0026#39;); 39\t40\t_tim = getTimer(); 41\tfor each(var __objeach:* in _obj) 42\t{ 43\tr = __objeach; 44\t} 45\ttrace2(\u0026#39;Object for each read\u0026#39;); 46\t47\t_dic = new Dictionary(); 48\t_tim = getTimer(); 49\tfor(i=0;i\u0026lt;max;i++) 50\t{ 51\t_dic[\u0026#39;a\u0026#39;+i] = i; 52\t} 53\ttrace2(\u0026#34;Dictionary write\u0026#34;); 54\t55\t_tim = getTimer(); 56\tfor(i=0;i\u0026lt;max;i++) 57\t{ 58\tr = _dic[\u0026#39;a\u0026#39;+i]; 59\t} 60\ttrace2(\u0026#34;Dictionary for read\u0026#34;); 61\t62\t_tim = getTimer(); 63\tfor(var __dicin:* in _dic) 64\t{ 65\tr = _dic[__dicin]; 66\t} 67\ttrace2(\u0026#39;Dictionary for in read\u0026#39;); 68\t69\t_tim = getTimer(); 70\tfor each(var __diceach:* in _dic) 71\t{ 72\tr = __diceach; 73\t} 74\ttrace2(\u0026#39;Dictionary for each read\u0026#39;); 75\t76\t_arr = []; 77\t_tim = getTimer(); 78\tfor(i=0;i\u0026lt;max;i++) 79\t{ 80\t_arr[i] = i; 81\t} 82\ttrace2(\u0026#34;Array write\u0026#34;); 83\t84\t_tim = getTimer(); 85\tfor(i=0;i\u0026lt;max;i++) 86\t{ 87\tr = _arr[i]; 88\t} 89\ttrace2(\u0026#34;Array for read\u0026#34;); 90\t91\t_tim = getTimer(); 92\tfor(var __arrin:* in _arr) 93\t{ 94\tr = _arr[__arrin]; 95\t} 96\ttrace2(\u0026#34;Array for in read\u0026#34;); 97\t98\t_tim = getTimer(); 99\tfor each(var __arreach:* in _arr) 100\t{ 101\tr = __arreach; 102\t} 103\ttrace2(\u0026#34;Array for each read\u0026#34;); 104\t} 105\t106\tprivate var _tim:int = 0; 107\tprivate var _obj:Object = {}; 108\tprivate var _dic:Dictionary; 109\tprivate var _arr:Array = []; 110\t111\tprivate function trace2($n:String):void 112\t{ 113\ttrace($n+\u0026#39;:\u0026#39;+(getTimer() - _tim)); 114\t} 115} 116} 2011-07-01更新：\n测试环境：\nSDK：4.5 FlashPlayer：10.3.181.22 debug 有网友在留言中提到，这个测试没有考虑字符串的创建时间，我仔细看了代码，确实是不够严谨。\n将for循环中的var **放在了循环外部，不考虑var的创建时间，发现性能并没有什么变化。\n[trace] Object write:7388 [trace] Object for read:2281 [trace] Object for in read:230 [trace] Object for each read:111 [trace] Dictionary write:2575 [trace] Dictionary for read:2294 [trace] Dictionary for in read:234 [trace] Dictionary for each read:109 [trace] Array write:91 [trace] Array for read:64 [trace] Array for in read:91 [trace] Array for each read:89\n代码如下：\n1package 2{ 3import flash.display.Sprite; 4import flash.utils.Dictionary; 5import flash.utils.getTimer; 6/** 7 * 比较Object/Dictionary/Array顺序读写性能，改进var 8 * @author zrong 9 * @data 2011-07-01 10 */ 11public class ObjectForSpeedTest extends Sprite 12{ 13\tpublic function ObjectForSpeedTest() 14\t{ 15\tconst max:int = 500000; 16\tvar i:int = 0; 17\tvar r:* = null; 18\t_obj = {}; 19 20\t_tim = getTimer(); 21\tfor(i=0;i\u0026lt;max;i++) 22\t{ 23\t_obj[\u0026#39;a\u0026#39;+i] = i; 24\t} 25\ttrace2(\u0026#39;Object write\u0026#39;); 26\t27\t_tim = getTimer(); 28\tfor(i=0;i\u0026lt;max;i++) 29\t{ 30\tr = _obj[\u0026#39;a\u0026#39;+i]; 31\t} 32\ttrace2(\u0026#39;Object for read\u0026#39;); 33 34\tvar __objin:* = null; 35\t_tim = getTimer(); 36\tfor(__objin in _obj) 37\t{ 38\tr = _obj[__objin]; 39\t} 40\ttrace2(\u0026#39;Object for in read\u0026#39;); 41 42\tvar __objeach:* = null; 43\t_tim = getTimer(); 44\tfor each(__objeach in _obj) 45\t{ 46\tr = __objeach; 47\t} 48\ttrace2(\u0026#39;Object for each read\u0026#39;); 49 50\t_dic = new Dictionary(); 51\t_tim = getTimer(); 52\tfor(i=0;i\u0026lt;max;i++) 53\t{ 54\t_dic[\u0026#39;a\u0026#39;+i] = i; 55\t} 56\ttrace2(\u0026#34;Dictionary write\u0026#34;); 57 58\t_tim = getTimer(); 59\tfor(i=0;i\u0026lt;max;i++) 60\t{ 61\tr = _dic[\u0026#39;a\u0026#39;+i]; 62\t} 63\ttrace2(\u0026#34;Dictionary for read\u0026#34;); 64 65\tvar __dicin:* = null; 66\t_tim = getTimer(); 67\tfor(__dicin in _dic) 68\t{ 69\tr = _dic[__dicin]; 70\t} 71\ttrace2(\u0026#39;Dictionary for in read\u0026#39;); 72 73\tvar __diceach:* = null; 74\t_tim = getTimer(); 75\tfor each(__diceach in _dic) 76\t{ 77\tr = __diceach; 78\t} 79\ttrace2(\u0026#39;Dictionary for each read\u0026#39;); 80 81\t_arr = []; 82\t_tim = getTimer(); 83\tfor(i=0;i\u0026lt;max;i++) 84\t{ 85\t_arr[i] = i; 86\t} 87\ttrace2(\u0026#34;Array write\u0026#34;); 88 89\t_tim = getTimer(); 90\tfor(i=0;i\u0026lt;max;i++) 91\t{ 92\tr = _arr[i]; 93\t} 94\ttrace2(\u0026#34;Array for read\u0026#34;); 95 96\tvar __arrin:* = null; 97\t_tim = getTimer(); 98\tfor(__arrin in _arr) 99\t{ 100\tr = _arr[__arrin]; 101\t} 102\ttrace2(\u0026#34;Array for in read\u0026#34;); 103 104\tvar __arreach:* = null; 105\t_tim = getTimer(); 106\tfor each(__arreach in _arr) 107\t{ 108\tr = __arreach; 109\t} 110\ttrace2(\u0026#34;Array for each read\u0026#34;); 111\t} 112 113\tprivate var _tim:int = 0; 114\tprivate var _obj:Object = {}; 115\tprivate var _dic:Dictionary; 116\tprivate var _arr:Array = []; 117 118\tprivate function trace2($n:String):void 119\t{ 120\ttrace($n+\u0026#39;:\u0026#39;+(getTimer() - _tim)); 121\t} 122} 123} 和 smithfox 讨论了一下，觉得留言中的“没考虑字符串的创建时间”应该指的是'a'+i这部分，于是写了个测试，发现50万个a+i的创建，确实需要消耗600毫秒的时间。\n于是乎，我对创建字符串进行了优化，首先想到的方法是创建一个临时的Object，将50万个字符串丢进去做缓存。写Object的时候，从缓存中读入值，应该会省掉这600毫秒了吧？下面将只展示部分代码：\n1var __key:Object = {}; 2 3//建立字符串缓存 4for(i=0;i\u0026lt;max;i++) 5{ 6\t__key[\u0026#39;a\u0026#39;+i] = \u0026#39;a\u0026#39;+i; 7} 8 9_tim = getTimer(); 10for(i=0;i\u0026lt;max;i++) 11{ 12\t_obj[\u0026#39;a\u0026#39;+i] = i; 13} 14trace2(\u0026#39;Object write\u0026#39;); 由于粗心，上面对_obj进行写入的时候，并没有调用__key中缓存的值，但就是这样，速度却降到了2500毫秒！\n那么这样写会如何？\n1_tim = getTimer(); 2for(i=0;i\u0026lt;max;i++) 3{ 4\t_obj[__key[\u0026#39;a\u0026#39;+i]] = i; 5} 6trace2(\u0026#39;Object write\u0026#39;); 为了调用__key中的缓存，我必须知道键名，而键名也是用'a'+i拼出来的，这个创建字符串的过程，依然在循环中。准确的说，在这个循环中不仅仅包含写入的时间，还包含创建字符串以及从Object中读取的时间。但即使是这样，速度也仅有2626毫秒！\n看着这有如神助的速度，我只能猜想Adobe在编译器上做了优化，或者在AVM中进行了缓存，这个优化针对Object或者Dictionary的键名。只要是使用过一次的键名，就会被缓存下来供下次使用。\n那么再来看看对Object使用数字键名的结果吧：\n[trace] Object write:142 [trace] Object for read:82 [trace] Object for in read:136 [trace] Object for each read:110 [trace] Dictionary write:7539 [trace] Dictionary for read:2349 [trace] Dictionary for in read:242 [trace] Dictionary for each read:110 [trace] Array write:127 [trace] Array for read:69 [trace] Array for in read:92 [trace] Array for each read:88\n部分代码如下：\n1_tim = getTimer(); 2for(i=0;i\u0026lt;max;i++) 3{ 4\t_obj[i] = i; 5} 6trace2(\u0026#39;Object write\u0026#39;); 7 8_tim = getTimer(); 9for(i=0;i\u0026lt;max;i++) 10{ 11\tr = _obj[i]; 12} 13trace2(\u0026#39;Object for read\u0026#39;); 如此可见，对Object使用数字键名的速度，居然已经和数组相仿。或许，它们在AVM中就是一回事吧。\n","date":"2011-03-17","description":"","lastmod":"2011-03-17T02:27:53Z","slug":"compare-performance-in-object-dictionary-array","tags":["as3","performance"],"title":"比较Object/Dictionary/Array顺序读写性能","url":"https://blog.zengrong.net/post/compare-performance-in-object-dictionary-array/"},{"categories":["technology"],"content":"2011-07-16更新：更新为Flash Player 11 + AIR3\n2011-07-18更新：加入一些支持Flash Player 11 Beta1的例子\nAdobe在Max 2010上就放出风来，支持3D API的Flash Player \u0026quot;Molehill\u0026quot;正在开发中，并提供了一系列让人垂涎的Demo，现在，它终于来了……\n### 一 Player插件和开发工具 版本号：11.0.1.60\nFlash Player 11下载地址 AIR3下载地址 Flash Player 11 + AIR3新功能简介（中文版） 判断计算机显卡是否支持Adobe Flash Stage3D QA 开发者文档下载 playerglobal.swc Flex SDK 4.5 深入挖掘Molehill API的一些特征 深入浅出了解Molehill的底层API－顶点着色器与片段着色器 着色器 注意，这个版本的Flash Player不支持基于前面发布的两个Incubator器版本开发的程序，如果运行会报错：\nReferenceError: Error #1056: Cannot create property viewPort on flash.display.Stage3D.\n这是因为新的Flash Player移除了Stage3D的viewport属性，使用Stage3D.x/y替代。\n另外，目前我还没有找到Flash Player 11 Beta Reference的下载地址（上面提供的是Incubator版本的），但Adobe提供了livedoc。\n一些支持Flash Player 11 beta1的例子：\nmolehill 3d shooter 基于3D API的游戏（先下载插件并安装，重启浏览器，再单击图片访问）：\n以下来自hxzpily\n二 支持Molehill的3D引擎（不支持肯定活不下去了） Alternativa http://alternativaplatform.com/en/ Away3D 4.0 Alpha http://away3d.com/away3d-4-0-alpha-release-broomstick Flare3D 2.0 http://www.flare3d.com/ Aerys Minko http://aerys.in/minko CopperCube 2.5 http://www.ambiera.com/coppercube/ Zest3D http://zest3d.digital-glue.com/ sophie3d http://www.sophie3d.com/website/index_en.php yogurt3d http://www.yogurt3d.com/ Unity 3D 支持输出 flash 格式：http://blogs.unity3d.com/2011/02/27/unity-flash-3d-on-the-web/ 三 Molehill在线例子 http://aerys.in/minko-quake-3 http://molehill.zombietycoon.com/ http://alternativaplatform.com/en/demos/maxracer/ http://www.mcfunkypants.com/2011/molehill-terrain-demo/ http://infiniteturtles.co.uk/pro ... /LoaderOBJTest.html http://not-so-stupid.com/clients/not-so-stupid/away4/duck/ （要翻墙） http://infiniteturtles.co.uk/pro ... ick/EnvMapTest.html http://infiniteturtles.co.uk/pro ... /AnimBlendTest.html http://infiniteturtles.co.uk/pro ... MapDiffuseTest.html http://www.bytearray.org/wp-content/projects/molehill2d/molehill/ http://acemobe.com/dungeon/demo.php http://iflash3d.com/performance/how-fast-is-molehill/ http://www.ringo.nl/projects/jiglibflash/Away3DGridSystem.html http://www.ringo.nl/projects/jiglibflash/Away3DTriangleMesh.html http://www.ringo.nl/projects/jiglibflash/Away3DStackingTest.html http://www.ringo.nl/projects/jiglibflash/Away3DTerrainTest.html http://www.ringo.nl/projects/jiglibflash/Away3DCarDrive.html http://ryanspeets.com/uncategorized/adobe-releases-molehill/ http://www.ambiera.com/coppercube/demo.php?demo=backyard 四 教程 http://lab.polygonal.de/2011/02/27/simple-2d-molehill-example/ http://blog.kaourantin.net/?p=110 http://iflash3d.com/performance/how-fast-is-molehill/ http://www.swfgeek.net/2011/02/2 ... ayer-and-adobe-air/ ","date":"2011-02-28","description":"","lastmod":"2011-02-28T06:13:47Z","slug":"1281","tags":["3d","flashplayer"],"title":"支持3D API的Flash Player 11/AIR3相关资源","url":"https://blog.zengrong.net/post/1281/"},{"categories":["others"],"content":"\n推荐一篇文章，对所有人都应该有用：“番茄”让时间变成我们的朋友；\n该文提到的《番茄工作法图解:简单易行的时间管理方法》第一章电子版阅读；\n该书购买地址：http://www.china-pub.com/197686；\n想装13买英文版的：http://pragprog.com/titles/snfocus/pomodoro-technique-illustrated；\n另外，如果想深入研究工作方法和提高你的工作效率，还可以看看《尽管去做》；\n这本书中文翻译得超级稀烂，但还好我把它看完了，说明对于绝大多数人类来说，理解起来还是没问题的。需要了解该书中的工作方法或下载电子版，可以用google搜索GTD；\n还有一篇推荐的文章：(10+2)*5把你从惰性中拯救出来。\n[gallery link=\u0026quot;file\u0026quot; columns=\u0026quot;2\u0026quot;]\n","date":"2011-02-26","description":"","lastmod":"2011-02-26T05:44:50Z","slug":"1276","tags":["study"],"title":"番茄工作法","url":"https://blog.zengrong.net/post/1276/"},{"categories":["technology"],"content":"SWF帧频（FPS）对Socket连接的影响\nSWF帧频（FPS）对Socket连接的影响 Adobe官方帮助中对于Flash Player的睡眠模式有 这样一段介绍 ：\nActionScript 代码在睡眠模式下继续执行，这与将 Stage.frameRate 属性设置为 4 fps 类似。但是跳过呈现步骤，因此用户看不到该 Player 正在以 4 fps 的速率运行。之所以将帧速率选择为 4 fps （而不是 0），是因为该速率允许所有连接保持打开状态（NetStream、Socket 和 NetConnection）。将帧速率切换到 0 fps 会断开打开的连接。之所以将刷新速率选择为 250 毫秒 (4 fps)，是因为……。\n确实是这样么？当FPS低于4或者为0的时候，Socket连接会断开么？低帧频的时候，通过Socket发送的数据，是根据帧率触发，还是直接触发呢？于是我写了个测试程序做了如下测试：\n在舞台上双击的时候，可以在帧频2和帧频0之间切换； 每次EnterFrame的时候，向Socket服务器发送布尔值false； 每次单击鼠标的时候，向Socket服务器发送布尔值true； 在服务端，显示出每次发送数据之间的间隔，同时显示发送的数据内容（1=true，0=false）。 结论 将SWF帧频设为0的时候，Sokcet不会断开连接（等了十几分钟也没断开）； Socket的发送请求，与帧率是没有关系的，只要发送了，就会立即执行； 我每秒钟可以单击鼠标7次（这个很强悍！ :lol:） 服务器端的输出 //冒号前面是客户端传送的值，后面是时间间隔（毫秒）\nconnected 0:15.625 0:406.25 0:484.375 0:500 0:500 0:500 0:500 0:500 0:500 0:500 0:500 0:500 0:500 1:125 0:156.25 1:6796.875 0:93187.5 1:22328.125 0:125 0:500 0:484.375 0:500 0:484.375 0:515.625 0:500 0:484.375 0:515.625 1:218.75 0:203.125\n代码 1package 2{ 3import flash.display.Sprite; 4import flash.events.ErrorEvent; 5import flash.events.Event; 6import flash.events.IOErrorEvent; 7import flash.events.MouseEvent; 8import flash.events.SecurityErrorEvent; 9import flash.net.Socket; 10 11[SWF(width=300,height=200,frameRate=2)] 12public class SocketFPSTest extends Sprite 13{ 14\tpublic function SocketFPSTest() 15\t{ 16\t_socket = new Socket(); 17\t_socket.addEventListener(Event.CONNECT, handler_connect); 18\t_socket.addEventListener(IOErrorEvent.IO_ERROR, handler_error); 19\t_socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handler_error); 20\tthis.stage.doubleClickEnabled = true; 21\tthis.stage.addEventListener(MouseEvent.CLICK, handler_click); 22\tthis.stage.addEventListener(MouseEvent.DOUBLE_CLICK, handler_doubleClick); 23\tthis.stage.addEventListener(Event.ENTER_FRAME, handler_enterFrame); 24\ttrace(\u0026#39;frameRate:\u0026#39;, this.stage.frameRate); 25\t} 26 27\tprivate var _socket:Socket; 28 29\tprivate function handler_error(evt:ErrorEvent):void 30\t{ 31\ttrace(evt.toString()); 32\t} 33 34\tprivate function handler_connect(evt:Event):void 35\t{ 36\ttrace(\u0026#39;连接成功！\u0026#39;); 37\t} 38 39\t//每500毫秒自动发送一次消息，传送false 40\tprivate function handler_enterFrame(evt:Event):void 41\t{ 42\tsend(false); 43\ttrace(\u0026#39;enterFrame send\u0026#39;); 44\t} 45 46\t//双击切换帧频 47\tprivate function handler_doubleClick(evt:MouseEvent):void 48\t{ 49\tthis.stage.frameRate = (this.stage.frameRate==2) ? 0 : 2; 50\t} 51 52\t//单击鼠标的时候发送一次消息，传送true 53\tprivate function handler_click(evt:MouseEvent):void 54\t{ 55\tsend(true); 56 57\t} 58 59\tprivate function send($value:Boolean):void 60\t{ 61\tif(!_socket.connected) 62\t_socket.connect(\u0026#39;127.0.0.1\u0026#39;, 3000); 63\telse 64\t{ 65\t_socket.writeBoolean($value); 66\t_socket.flush(); 67\t} 68\t} 69} 70} ","date":"2011-02-24","description":"","lastmod":"2011-02-24T09:31:01Z","slug":"socket_connection_on_the_impact_of_fps","tags":["flashplayer","sleepmode","socket","flash"],"title":"SWF帧频（FPS）对Socket连接的影响","url":"https://blog.zengrong.net/post/socket_connection_on_the_impact_of_fps/"},{"categories":["use"],"content":"Gtalk桌面版已经好几年没更新过了，许多新的功能，Google都是将其加入到Gmail界面，但是我只想要个单纯的聊天界面，不想登录Gmail，也不想使用桌面版。\n今天被邀请一个三方群聊的时候，找到了这个官方的地址，可以单独开启一个聊天页面：\nhttp://talkgadget.google.com/talkgadget/popout\n建议使用Chrome的”工具-创建应用程序快捷方式”将这个路径创建到桌面上，这样聊天就更方便了。\n","date":"2011-02-24","description":"","lastmod":"2011-02-24T05:40:19Z","slug":"1269","tags":["google"],"title":"GoogleTalk网页版，终于可以不在Gmail界面下聊天了","url":"https://blog.zengrong.net/post/1269/"},{"categories":["news"],"content":"感谢wonder分享所悟\n其实，每个人都拥有一家神奇的公司。\n这公司实力雄厚，资质非凡，不是人才济济，而是天才济济。\n这公司的结构，以时间和空间来划分。该类公司中高明者，甚至可将触角伸及时空之外。\n从时间来分，该公司拥有过去，现在和未来三大部门。\n这三个部们，虽然名头很响，法力无边，却都听从一个ceo的指挥。\n这个ceo就是你自己。\n经营这家公司与经营世间所有的公司一样，都需要懂点哲学中的管理学。\nwonder目前所知最好的管理，是老子的无为而治。\n世人误以为老子很消极。殊不知世人往往只看到“无为”，这个似乎很容易；没有看到“而治”，这个往往难上难。\n治而无为，也许是世间最高深的管理学了。\n一个劳心劳力的ceo，恐怕还只是较低层次的ceo。\n理想的ceo，帮助他人找到自己的心声，把合适的人放到合适的位置上。公司里人人都做自己喜欢和擅长的事，工作本身就是快乐生活的一部分。\n--而这些，就是ceo自己喜欢，和应该做的事。\n做好自己的ceo，\n把过去留给过去，\n把未来交给未来，\n把现在倾注在现在。\n做好的ceo，\n不要把过去、现在和未来全都背负在心里。\n","date":"2011-02-21","description":"","lastmod":"2011-02-21T12:37:48Z","slug":"1267","tags":[],"title":"【转】每个人都是CEO","url":"https://blog.zengrong.net/post/1267/"},{"categories":["impressions"],"content":"今天在论坛上回复了一篇帖子，记下来：\n原帖：http://bbs.9ria.com/thread-74150-1-1.html\n小弟以前是做java 程序员的 ，接触flex 也有一年了 从之前的flex3 到现在的flex4，在公司的项目中，我也经常使用flex的各种组件，但我个人对flex游戏制作方面很有兴趣，所以想请教下有flex游戏开发经验的各位达人，小弟现在欠缺的是什么？\n我的回复：\nFlex只是基于AS的一个框架而已，Flex的组件完全是用AS实现的。所以，准确的说，不应该是“Flex游戏开发”，而应该是“AS游戏开发”。\n要开发游戏，我的建议是首先“忘掉Flex”，后面的流程应该是这样的：\n全面学习AS3 API，着重了解显示列表、网络通信、bitmapData、drawAPI、二进制操作 了解如何与美术配合，如何从SWF中获取你需要的资源 了解嵌入和载入外部资源的方法，外部资源主要包括swf、png、jpg、mp3（建议必须学习FlashIDE，并掌握PS或fireworks中的一种） 了解用AS制作动画的基础、概念和技巧（推荐Keith Peters的两本Animation） 复习三角函数概念，初中几何（其实有这些就够了） 了解Flash Player性能优化知识，通过优化算法和选择合适的方法（例如用Vector替换Array）来优化性能和内存占用 了解swf文件格式，了解如何保护自己的代码（混淆、加密、障眼法） 学习设计模式，学习几种MVC框架（推荐pureMVC），但不一定要在项目中应用这些框架（学习思想，最好自己写框架） 学习物理引擎和3DAPI，继续复习数学知识 算法和全局观 ","date":"2011-02-21","description":"","lastmod":"2011-02-21T02:27:22Z","slug":"flex-game-study","tags":["flex","game","study"],"title":"有Flex应用基础，做游戏还需要学习什么","url":"https://blog.zengrong.net/post/flex-game-study/"},{"categories":["technology"],"content":"转自：Java情侠的空间\n类型 优点 缺点 数组 插入块，如果知道下标，可以非常快的存储 查找慢，删除慢，大小固定 有序数组 比无序数组查找快 删除和插入慢，大小固定 栈 提供后进先出方式的存取 效率低 队列 提供先进先出的方式存取 效率低 链表 插入，删除快 查找慢 二叉树 查找，插入，删除都快 如果非平衡就很慢，删除的算法复杂 红黑树 查找，插入，删除都快 算法复杂 哈希表 如果关键字已知则存取极快，插入块 删除慢，如果不知道关键字则存取很慢，对存储空间使用不充分 堆 插入，删除快，对最大数据项的存取很快 对其他存储项很慢\n","date":"2011-02-11","description":"","lastmod":"2011-02-11T13:23:33Z","slug":"1262","tags":["ds","game"],"title":"【转】数据结构的优缺点","url":"https://blog.zengrong.net/post/1262/"},{"categories":["technology"],"content":" **2011-02-11更新：**加入dsforas的测试 **2011-02-15更新：**这里有篇更详细的评测：http://jacksondunstan.com/articles/1064 AS3DS是我们常用的数据结构，后来polygonal又开发了ds，国人也开发了一套dsforas。那么，这些常用的数据结构与AS3原生的Array和Vector，性能上有何区别？我在网上只找到了这篇，该文也没有给出一个直观的演示，因此决定自己来测试一下。\n测试平台：\nAS3DS: v1.04 ds: v1.23 dsforas: r25 Flash Player: v10.2.152.26 类型 写入(ms) 读取(ms) Array 452 132 Vector 256 130 SLinkedList 2708 1718 SSL 1266 222 LinkedList 1498 1392 注意：\n这次测试只是针对单链表测试了1000000个随机数的顺序写入和读取速度，不能代表整体性能。（链表的性能特点看这里：数据结构的优缺点） 使用AS3DS，文件增大4K；使用ds，文件增大16K。 AS3DS和ds不能在一个项目中测试，因为很多类名重复，必须分开测试。 测试代码：\n1package 2{ 3//import de.polygonal.ds.SLinkedList; 4//import de.polygonal.ds.SListIterator; 5 6//import de.polygonal.ds.Itr; 7//import de.polygonal.ds.SLL; 8//import de.polygonal.ds.SLLNode; 9 10import asds.list.LinkedList; 11import asds.list.LinkedListIterator; 12 13import flash.display.Sprite; 14import flash.utils.getTimer; 15 16public class DSPerformanceTest extends Sprite 17{ 18 public static const MAX_COUNT:int = 1000000; 19 public function DSPerformanceTest() 20 { 21 _array = []; 22 _vector = new Vector.(MAX_COUNT, true); 23 _sl = new LinkedList(MAX_COUNT); 24 //_sl = new SLinkedList(); 25 //_sl = new SLL(); 26 var __t:int = 0; 27 trace(\u0026#39;==================写入性能===============\u0026#39;); 28 __t = getTimer(); 29 for(var i:int = 0; i\u0026lt; MAX_COUNT; i++) 30 { 31 _array[i] = (Math.random()*1024); 32 } 33 trace(\u0026#39;array：\u0026#39;, getTimer()-__t); 34 35 __t = getTimer(); 36 for(var j:int = 0; j\u0026lt; MAX_COUNT; j++) 37 { 38 _vector[j] = (Math.random()*1024); 39 } 40 trace(\u0026#39;Vector：\u0026#39;, getTimer()-__t); 41 42 __t = getTimer(); 43 for(var k:int=0; k\u0026lt; MAX_COUNT; k++) 44 { 45 _sl.addAtLast(Math.random()*1024); 46// _sl.addAtFirst(Math.random()*1024); 47 } 48 trace(\u0026#39;LinkList：\u0026#39;, getTimer()-__t); 49 50 trace(\u0026#39;==================读取性能===============\u0026#39;); 51 52 __t = getTimer(); 53 var __i2:Number=0; 54 for(var i2:int=0; i2; 55 private var _sl:LinkedList; 56 //private var _sl:SLinkedList; 57 //private var _sl:SLL; 58} 59} ","date":"2011-02-11","description":"","lastmod":"2011-02-11T04:59:49Z","slug":"array-vector-as3ds-ds-dsforas","tags":["as3","ds","game","performance"],"title":"Array/Vector/AS3DS/ds/dsforas 性能比较","url":"https://blog.zengrong.net/post/array-vector-as3ds-ds-dsforas/"},{"categories":["technology"],"content":"转自：乔部落格\nShell元字符 元字符 含义 \u0026gt; 将标准输出重定向到文件 \u0026gt;\u0026gt; 将标准输出附加到文件 \u0026lt; 从文件中获取标准输入 ` ` \u0026lt;\u0026lt;串 here文档：标准输入从here文档读入，直到出现串 * 匹配文件名中的零个或多个字符 ? 匹配文件名中的任何单个字符 [ccc] 匹配文件名中 ccc范围内的任何字符 ; 命令结束符，如p1;p2表示先执行p1,再执行p2 \u0026amp; 与;类似，但不等p1结束 `…` 运行…中的命令，输出结果代替`…` (…) 在子shell中运行…中的命令 {…} 在当前shell中运行…中的命令 $1, $2等 $0…$9 可代表shell文件的参数 $变量 Shell变量的值 ${变量} Shell变量的值，为避免在文本联接时混淆 \\ 转义字符，\\c 将c字符作为字符 '…' 单引号内表示文字 \u0026quot;…\u0026quot; 在 … 中的 $, `…`,和 \\ 得到解释后，将 … 作为文本文字 # 注释 变量=值 为变量赋值 p1\u0026amp;\u0026amp;p2 运行p1;若成功，再运行p2 `p1 Shell内部变量 变量 含义 $# 参数个数 $* Shell的所有参数 $@ （注） 类似 $* $- Shell的选项 $? 上次执行命令的返回值 $$ Shell的pid $! 用 \u0026amp; 启动的最后一个命令的pid $HOME Cd命令的默认参数 $IFS 参数分隔符 $PATH 搜索命令的目录表 注：( $* 与 $@ 区别)\n$* 和 $@ 扩展为参数，并被重复扫描；参数的空格将字符串分成多个参数。 $* 表示shell文件的所有参数及其空格连在一起作为单个词处理。 $@ 与shell文件接收的参数等价，参数中的空格被忽略，其结果是等同于原来参数的一个单词列表。 Shell变量的赋值 变量 赋值 $var var的值；若var无定义则无值 ${var} 同上；用于变量后跟着字母数字串的情况 ${var-thing} var有定义时，取值var；否则取值thing，而 $var 的值不变 ${var=thing} var有定义时，取值var；否则取值thing，$var 的值设为thing ${var?message} var有定义时，取值var；否则打印message ${var+thing} var有定义时，取值thing；否则无值 \u0026gt;a 或touch a创建文件。\nCat \u0026gt; a 从标准输入获取字符，写入文件a中，按 ctrl-d 结束。\n（以上参考《UNIX编程环境》）\n为了装软件方便，我写了一个简单的小脚本（我使用yum作为软件包管理者）。\n脚本如下：\n1#!/bin/bash 2#program name: pack.sh 3if [ $# -eq 0 ] 4then 5echo “try pach.sh -h for help” 6exit 1 7fi 8case $1 in 9“-h”)echo “usage: pack.sh -[suihc] ‘pack’” 10echo “-s for search, -u for update, -i for install, -c for check updates” 11;; 12“-s”)echo “This may take a few minutes, please wait…” 13sudo yum list | grep -i $2 14;; 15“-c”)echo “This may take a few minutes, please wait…” 16if [ -z $2 ] 17then 18sudo yum check-update 19else 20sudo yum check-update | grep -i $2 21fi 22;; 23“-u”)sudo yum -y update $2 24;; 25“-i”)sudo yum -y install $2 26;; 27*)echo “NO such options!try pach.sh -h” 28exit 2 29;; 30esac ","date":"2011-02-10","description":"","lastmod":"2011-02-10T14:52:56Z","slug":"via-shell-summary","tags":["linux","shell","bash"],"title":"【转】shell总结","url":"https://blog.zengrong.net/post/via-shell-summary/"},{"categories":["technology"],"content":"转自：逍遥云's Blog\n本文PDF格式电子书下载：\n1 文件 基本的历史命令快捷键 你可能记得，Bash提供了两种编辑命令的模式：emacs与vi, 很多快捷键在不同的编辑模式下是不同的。 切换模式：\n1$ set -o mode (vi/emacs) 假设你要执行如下命令：\n1$ echo foo bar baz 2$ iptables -L -n -v -t nat 3$ ... lots and lots more commands 4$ echo foo foo foo 5$ perl -wle \u0026#39;print q/hello world/\u0026#39; 6$ awk -F: \u0026#39;{print$1}\u0026#39; /etc/passwd 然后你想执行最后一条历史命令(awk -F ...).\n你会想当然的按下键盘的上箭头并切安逸地陪伴你一生，但是真的需要把你的手移动哪么远吗？\n如果在Emacs模式下你只用按下Ctrl-P就能够从历史记录中取到前一条命令（Ctrl-n取下一条命令）\n在vi模式下你只要按下 Ctrl-[ (或ESC)(用来切换到命令模式)与 'h'('j'取下一条命令)即可\n这儿有一个等同有效的方式就是Bash的历史扩展机制－－事件引用符，输入\u0026quot;!!\u0026quot;就可以达到以上等同的效果（详细后面说明）\n现在，假设你不想通过再次重复输入就想执行以上第二条命令iptables -L -n -v -t nat\n天真的用户会照常按下键盘上箭头直到找那所需的那条命令，但这并不是黑客们的玩转方式，黑客们喜欢快速有效的方式。忘掉键盘的上下右左键以及翻页那一块键吧，依我看，它们跟本用不着，而且离键盘主要工作区域太远了。（看来作者应该玩的是vi）\n在emacs模式下输入iptables前几个字符，然后使用Ctrl-r就行了，比如:ipt, 这将会正确地得到最后的以iptables起始的命令，再次输入Ctrl-r会得到更老输入的匹配，假如你错过了正确的结果，你要通过Ctrl-s就可以反向的搜索了（不要忘记默认的Ctrl-s操作会停止终端的输入，看起来像是假死了，记得按下Ctrl-g让终端恢复正常，具体设置，参见sttycommand）。\n在vi模式下使用Ctrl-r与Ctrl-s会有些不同。\n通过使用Ctrl-[或ESC结合/来切换到命令模式，输入iptables前几个字符（插入模式），如ipt，然后回车.\nBash将会匹配到最近的一条以ipt开始的历史记录，输入n或者通过/再次操作会得到相关的上一条命令，使用N or ? 得到下一条命令。\n通过事件引用符可以得到最最近以string开头的历史命令\n而使用!iptables就能扩展到最近的以iptables开头的历史命令\n另一种方式是使用Bash自身的history命令与grep结合，然后再使用事件引用符!N, N指的是历史记录中的第几条命令。\n例如：\n1$ history | grep \u0026#39;ipt\u0026#39; 22 iptables -L -n -v -t nat 3$ !2 # will execute the iptables command 在vi编辑模式下，使用N（命令行号）与G，就可以达到相同的效果，此例使用2G即可\n列出与擦除历史命令 Bash内建了历史命令(history)的查看与擦除\n及以下例为例：\n1$ echo foo bar baz 2$ iptables -L -n -v -t nat 3$ ... lots and lots more commands 4$ echo foo foo foo 5$ perl -wle \u0026#39;print q/hello world/\u0026#39; 6$ awk -F: \u0026#39;{print$1}\u0026#39; /etc/passwd 输入history将会得到所有的带行号的历史命令\n11 echo foo bar baz 22 iptables -L -n -v -t nat 3... lots and lots more commands 4568 echo foo foo foo 5569 perl -wle \u0026#39;print q/hello world/\u0026#39; 6570 awk -F: \u0026#39;{print$1}\u0026#39; /etc/passwd 输入history N(N为整数)，会得到最近的N条历史命令，如:history 3,结果如下：\n1568 echo foo foo foo 2569 perl -wle \u0026#39;print q/hello world/\u0026#39; 3570 awk -F: \u0026#39;{print$1}\u0026#39; /etc/passwd history -c将清除所以的历史记录，history -d N将会删除N条历史记录\n默认在用户的主目录下会有一个名为.bash_history的历史记录文件\n历史命令扩展 历史命令扩展的完善就是依赖所谓的事件引用符与词组引用符，事件引用符适用于先前取得的命令（事件），词组引用符适用于由事件而来的命令行。我们可以选择的就是：多样的修饰符可应用于已提取的参数。\n事件引用符 事件引用符都是的以感叹号开头的特殊命令（也有以 ^ 开头的），它们可跟一个词组引用符和一个或多个修饰符。引用符与修饰符都是由: 分隔开的\n让我们看看下面的一组事件引用符的例子是如何运作的。\n事件引用符!!用来取得前一条命令，例如：\n1$ echo foo bar baz 2foo bar baz 3$ !! 4foo bar baz 这里的!!就执行了echo foo bar baz这条命令\n事件引用符!N用于执行命令历史的第N条记录\n假如你的历史记录输出如下：\n11 echo foo foo foo 22 iptables -L -n -v -t nat 3... lots and lots more commands 4568 echo bar bar bar 5569 perl -wle \u0026#39;print q/hello world/\u0026#39; 6570 awk -F: \u0026#39;{print$1}\u0026#39; /etc/passwd! !569就会执行perl … 这条命令，而!1就会执行echo foo foo foo\n!-N就会执行当前的命令的计数前N条命令（中间的-可以理解为减号），如下：\n1$ echo foo bar baz 2foo bar baz 3$ echo a b c d e 4a b c d e 5$ !-2 6foo bar baz !-2执行上上一条命令，或者说是当前的命令的前2条命令\n!string适用以string开头的历史命令。例如:\n1$ awk --help 2$ perl --help !p或者!perl或者!per都可以正确是执行到perl -help，同理，!a亦可以执行那条以awk开头的命令\n面!?string?就能匹配到包含string的命令，并不严格要求开头。\n最有意思的事件引用符莫过于^string1^string^了，它将替换最后一条命令中的string1字符窜为string2，例如：\n1$ ehco foo bar baz 2bash: ehco: command not found 3$ ^ehco^echo^ 4foo bar baz 上面的^ehco^echo^作用就是取得上一条历史记录并替换ehco为echo，然后再执行\n词组引用符与修饰符 事件引用符后面词组引用符（一个或多个）是用冒句分隔开的，它们适用于当前参照命令的部分或者所有的参数\n例如：\n1$ echo a b c d e 2a b c d e 3$ echo !!:2 4b 这里有一个最简单的词组引用符，:2对应第2个参数（第3个单词），通常情况下:N对应命令的第N-1个单词\n词组引用符也接受一个范围值，例如：\n1$ echo a b c d e 2a b c d e 3$ echo !!:3-4 4c d 符号用法也是多样的，例如，:$对应最后一个参数，:^对应第一个参数，:*对应所有的参数(等同:1-$),等等，详情看完整的参照表\n修饰符可以改变单词引用符的行为，例如：\n1$ tar -xvzf software-1.0.tgz 2software-1.0/file 3... 4$ cd !!:$:r 5software-1.0$ r在这里就匹配了上一条命令的最后一个参数，r在此用作去掉后缀.tgz\nh用于匹配并删除文件路径中的文件名部分，返回文件路径的开头部分：\n1$ echo /usr/local/apache 2/usr/local/apache 3$ echo !!:$:h 4/usr/local e 则只保留扩展名\n1$ ls -la /usr/src/software-4.2.messy-Extension 2... 3$ echo /usr/src/*!!:$:e 4/usr/src/*.messy-Extension # ls could have been used instead of echo 另一个有趣的修饰符是替换符:s/old/new/,即替换单词old为new，g用于全局替换，例如：\n1$ ls /urs/local/software-4.2 /urs/local/software-4.3 2/usr/bin/ls: /urs/local/software-4.2: No such file or directory 3/usr/bin/ls: /urs/local/software-4.3: No such file or directory 4$ !!:gs/urs/usr/ 5... 以上这个例子就是替换所有的 urs 为 usr 来达到我们想要的结果。\n这里还有一些其它的修饰符，如 p 只是用来回显扩展过的命令，并不执行。详情看完整的参照表。\n配置命令历史 Bash允许用户自己配置命令历史文件，文件保存着命令以及记录号和一些选项\n变量 HISTFILE, HISTFILESIZE, HISTIGNORE 和 HISTSIZEenvironment 对命令历史起着决定的作用。\nHISTFILE, 正如词面意思，代表命令历史文件的路径\n1$ export HISTFILE=/home/pkrumins/todays_history 上面命令当保存命令历史文件路径为 /home/pkrumins/todays_history\n值设置为 /dev/null 或空将不保存历史记录\nHISTFILESIZE 控制着历史文件最大记录数，例如：\n1$ export HISTFILESIZE=1000 这将保存1000条最近的历史命令\nHISTSIZE 控制着当前会话（终端）的历史记录条数\n1$ export HISTSIZE=42 上面将保存当前会话最近42条历史命令\n如果HISTSIZE变量的值小于HISTFILESIZE变量的值，只有限定数量的命令会被记录下来\nHISTIGNORE用来控制我们不想记录的命令，此变量是用冒号来分隔不同的模式，\u0026amp; 对匹配历史命令起来特殊的作用。\n[ ]\\* 这个技巧可以用来忽略以空格开头的命令。\n例如：\n1$ export HISTIGNORE=\u0026#34;\u0026amp;:[ ]*:exit\u0026#34; 上面的命令就控制bash忽略复制命令，以及以空格开头，以及内容为 exit 字符的命令\n1There are several other options of interest controlled by the built-in \u0026#39;shopt\u0026#39; command. 这儿有几个有关shopt命令的选项。\n-s参数让shopt命令的配置有效，而-u参数使其无效。\nhistappend控制着历史列表如何写到命令历史记录中，设置这个选项将追加当前会话的历史命令到命令历史记录中，不设置（默认不设置，我的ubuntu 10.04默认却是打开的）每次将会重写命令历史记录文件。\n打开：\n1$ shopt -s histappend 关闭：\n1$ shopt -u histappend 选项 histreedit 允许用户重编辑一条有误的历史替换命令\n试想你已经输入如下：\n1$ echo foo bar baz 本意想通过^baz^test^替换baz为test, 但是却输成了^boo^test^,这将因为不匹配而导致替换失败\n如果将此选项打开，bash将会保持你当前的^boo^test^错误的输入\n最后，选项histverify允许用户去验证一条替换历史扩展命令\n就拿前面的例子来说吧，假设你想通过!!再次echo命令，如果这个选项打开，bash将不会立即执行这条echo命令，而是将命令显示好等待你去验证是否是你想要的结果。\n调整命令提示 我的命令提示是这样的：\n1Wed Jan 30@07:07:03 2pkrumins@catonmat:1002:2:~$ 第一行显示日期与时间可以及时的追溯命令\n第二行显示用户名，主机名，全局行号以及当前命令当号\n全局行号可以让我更快捷地使用事件引用符\n我的PS1参数，变量值如下：\n1PS1=\u0026#39;\\d@\\t\\n\\u@\\h:\\!:\\#:\\w$ \u0026#39; ","date":"2011-02-10","description":"","lastmod":"2011-02-10T14:36:58Z","slug":"via-bash-comman-history","tags":["linux","shell","bash"],"title":"【转】Bash命令行历史权威指南","url":"https://blog.zengrong.net/post/via-bash-comman-history/"},{"categories":["technology"],"content":"转自：乔部落格\nbash是Linuxer们常用的工具，高效的使用它能大大提高工作的效率。\n下面就来总结一下bash命令行快捷键。\nCTRL+a或Home移动光标至行首。\nCTRL+e或End移动光标至行尾。\nCTRL+左方向键或ALT +b将光标向前移动一个单词。\nCTRL+右方向键或ALT +f将光标向后移动一个单词。\nCTRL+w向前删除一个单词。\nEsc+d向后删除一个单词。\nCTRL+u删除光标至行首的字符。\nCTRL+k删除光标至行尾的字符。\n上方向键查找前一个命令，下方向键查找后一个命令。\nCTRL+r向前搜索历史命令。\nCTRL+y复制最后删除的项。\n!$代表上一个命令的最后一个参数。\nALT+.复制上一个命令的最后一个参数。\nCTRL+l清除屏幕。\nCTRL+t反转光标所在字符及其前面的字符。\n^字符a^字符b将上一个命令中的字符a替换为字符b并执行。\n^字符a删除上一个命令中的字符a并执行。\n","date":"2011-02-10","description":"","lastmod":"2011-02-10T14:18:12Z","slug":"via-bash-command-shortcut","tags":["linux","shell","bash"],"title":"【转】bash命令行快捷键","url":"https://blog.zengrong.net/post/via-bash-command-shortcut/"},{"categories":["technology"],"content":"对于已经发布在网页上的swf文件，使用什么方法能看到开发过程中的调试信息呢？主要有以下几种方法：（如果喜欢直奔主题，看这里）\n1.开发者自行提供隐藏的查看调试信息的方法\n这种方法一般需要开发者在产品中预留快捷键或者隐藏命令，使用后可以开启一个调试面板；\n**优点：**性能高，较隐蔽\n**缺点：**开发成本与较高\n2.使用Flex自带的命令行Debug工具\n**优点：**可查看源码中的trace消息，支持断点和调试\n**缺点：**仅支持Debug版的swf文件，且需要Debug版本的Flash Player支持\n3.使用开发库+Debug客户端工具\n由于是客户端Debug工具，因此这种方法的功能更加强大，但效率相对上面两种方法较低。Debug客户端一般是AIR开发的，也有利用Firefox插件做客户端的。\n**优点：**开发快速，使用方便\n**缺点：**难以同时监测多个swf文件\n本文介绍的是第3种方法，Debug工具选择的是基于AIR的Alcon，希望了解更多的Debug工具，可以看这篇文章：Alcon/De MonsterDebugger/Arthropod Flash Debugger简单评测。还可以看这里：https://blog.zengrong.net/flashassistant/#debugger。\n下载并安装AIR运行时：http://get.adobe.com/cn/air/ 下载并安装Alcon：http://blog.hexagonstar.com/download/alcon.air 访问要查看调试信息的swf文件（独立文件或基于浏览器访问址均可） 使用菜单Log-\u0026gt;Pause/Clear/Reset可以暂停、清除、重置监控； 使用菜单Edit可以将显示的调试信息输出成文本文件。 重要：只有开发者使用了Alcon提供的Debug库进行开发并提供Debug信息，才可以使用Alcon客户端监控到调试信息。\n附图：\n**\n**\n","date":"2011-02-10","description":"","lastmod":"2011-02-10T09:44:14Z","slug":"1251","tags":["air","debug"],"title":"SWF调试工具Alcon使用说明（仅面向调试工具使用者）","url":"https://blog.zengrong.net/post/1251/"},{"categories":["technology"],"content":"git乱码解决方案汇\n2012-11-04更新：官方的“终极”解决方案：msysGit1.7.10开始使用UTF-8编码保存文件名。 2011-10-24更新： 从一篇链接到本篇文章的文章(我对这篇文章提出的与windows患者的相处之道深感赞同)找到了一个“终极”解决方案，但我没有测试。 我一直是在cygwin下使用git，辅以TortoiseGit。使用上没什么问题，但今天在处理一个有中文文件名的项目时却出现文件名乱码的问题。\n情况重现 在一个使用cygwin的bash提交的git项目中，已经完成了所有的提交，但使用TortoiseGit查看的时候，却发现仍有文件没有提交，甚至是有文件还处于未暂存的状态。于是使用TortoiseGit提交； 再次用cygwin下的git status查看，这次又发现了未提交的情况。再次用git commit命令行提交； 回到TortoiseGit下查看，问题又出现了！此时准备返回两次提交前的版本，却因为文件名乱码的问题，无法返回了！ 乱码原因 搜索一番，发现git文件名、log乱码，是普遍问题，这其中有编码的原因，也有跨平台的原因。主要原因是Windows 系统中的Git对中文文件名采用不同的编码保存所致。\nWindows系统中使用的msysGit，采用的是系统编码来保存文件名；而Cygwin中的Git默认采用UTF-8编码来保存文件名。如果两个软件同时对一个版本库进行操作，且都认为对方是使用自己使用的编码来保存文件，就会导致文件名编码混乱，无法识别。\n这就导致，如果一直使用TortoiseGit（实际调用MsysGit）提交，那么中文文件名没问题；一直使用cygwin提交，中文文件名也没问题。 但一定不能交叉使用。\n分别设置 LANG、LC_CTYPE、LC_ALL 参数为同样的编码，问题依旧。\ncygwin官方网站提到了非拉丁语文件名的问题，也许研究后能解决该吧： Chapter 2. Setting Up Cygwin\n这里还有一篇讲解Linux系统编码文章：locale的设定及其LANG、LC_ALL、LANGUAGE环境变量的区别 。\n官方终极解决方案 这个问题的官方终极解决方案，就是更新到msysGit1.7.10或更新版本。这个版本之后，msysGit和Git for Windows已经采用了UTF-8编码来保存文件名，不会再出现乱码的情况。安装和使用可参考这篇文章：使用Git、Git GUI和TortoiseGit\n不幸的是，对于使用老版本msysGit提交的版本库，升级到msysGit1.7.10或者更高会出现编码问题。\n有两篇文章介绍了这个问题的解决办法：\n升級到 msysgit 1.7.10 的檔名亂碼處理方式（需要翻墙） upgrading to msysGit 1.7.10 (or higher) 下面的文章，是历史遗留，可以不看。若希望知其所以然，则不妨观之。\n乱码情景对号入座和解决方案 乱码情景1 在cygwin中，使用git add添加要提交的文件的时候，如果文件名是中文，会显示形如 274\\232\\350\\256\\256\\346\\200\\273\\347\\273\\223.png 的乱码。\n解决方案：\n在bash提示符下输入：\n1git config --global core.quotepath false core.quotepath设为false的话，就不会对0x80以上的字符进行quote。中文显示正常。\n乱码情景2 在MsysGit中，使用git log显示提交的中文log乱码。\n解决方案：\n设置git gui的界面编码\ngit config --global gui.encoding utf-8\n设置 commit log 提交时使用 utf-8 编码，可避免服务器上乱码，同时与linux上的提交保持一致！\n1git config --global i18n.commitencoding utf-8 使得在 $ git log 时将 utf-8 编码转换成 gbk 编码，解决Msys bash中git log 乱码。\n1git config --global i18n.logoutputencoding gbk 使得 git log 可以正常显示中文（配合i18n.logoutputencoding = gbk)，在 /etc/profile 中添加：\n1export LESSCHARSET=utf-8 乱码情景3 在MsysGit自带的bash中，使用ls命令查看中文文件名乱码。cygwin没有这个问题。\n解决方案：\n使用 ls --show-control-chars 命令来强制使用控制台字符编码显示文件名，即可查看中文文件名。\n为了方便使用，可以编辑 /etc/git-completion.bash ，新增一行 alias ls=\u0026quot;ls --show-control-chars\u0026quot;\n终极解决方案 终极的解决方案是通过修改git和TortoiseGit源码实现，有网友这么做了：让Windows下Git和TortoiseGit支持中文文件名/UTF-8 ，也可以直接访问这个开源的Google项目：utf8-git-on-windows 。\n如果不抗拒命令行的话，直接用Cygwin来提交Git库。因为Cygwin其实是一个在Windows平台上的模拟器，它完全模拟GNU/Linux的方式运行，所以Cygwin中的Git是采用UTF-8编码来保存中文的。\n又一个“终极”解决方案（来自）（msysGit1.7.10之后，不再推荐此方案） 在操作git时，把区域设置修改为 中文GBK。这之后就可以进行git相关操作了。\n在终端中跟windows保持一致\n1export LC_ALL=zh_CN.GBK; export LANG=zh_CN.GBK 2terminal -\u0026gt; set charactor encoding -\u0026gt; gbk 切换回linux默认\n1export LC_ALL=en_US.utf8; export LANG=en_US.utf8 2terminal -\u0026gt; set charactor encoding -\u0026gt; unicode(utf-8) 改变文件名的编码\n如果已经造成乱码的恶果，还可以在utf8和gbk之间切换文件名。真的修改，而不是像上面那样修改显示的（解码的）效果。\n1convmv \u0026lt;filename\u0026gt; -f utf8 -t gbk 例外：convmv在fat32的U盘上运行无效，估计是fat32不允许非法编码。\n本文参考链接 搞定Git中文乱码、用TortoiseMerge实现Diff/Merge MsysGit乱码与跨平台版本管理 git中文文件名、目录名乱码应该怎么解决？ ","date":"2011-02-09","description":"","lastmod":"2011-02-09T17:59:50Z","slug":"git-codec-issues","tags":["cygwin","git","linux"],"title":"git乱码解决方案汇总","url":"https://blog.zengrong.net/post/git-codec-issues/"},{"categories":["technology"],"content":"[原文链接: Some Thoughts on TLF \u0026amp; FTE 原文创作时间: July 13, 2010 原文作者: Grant Skinner ]\n[原创翻译链接: http://www.smithfox.com/?e=74 , 转载请保留此声明, 谢谢]\n[部分内容参考了 Tomyail的blog 的翻译文章 一些对TLF和FTE的思考 ]\n这篇文章阐述了作者对Adobe TLF的架构实现的不足, 以及他的一些期望. 很少很少FTE相关内容. 没有怎么使用TLF的例子之类的内容. 但看完作者精辟的分析, 肯定使你对当前TLF不足, 乃至 Adobe framework 都会有一定的了解.\n下面的翻译内容:\n几个月前,我一直在思考关于TLF的一些事情: 怎么使用它,哪些是我喜欢的特征, 以及那些我所遇到的bug. 但不仅仅是这些, 还有关于它的原理和底层模型。我期望能过分享这些思考和来自社区的建议, 我能提供一些有用的反馈给Adobe,这将有助于TLF的未来发展.\n背景\nflash player 10 引入了新的flash.text.engine.*(FTE)包,这个包提供了一些处理低级文本的类。FTE又被flashx.textLayout.*包下面的一组类所继承, 这组类就是Text Layout Framework (TLF),TLF同时被flex和flash共享. TLF在FTE基础上做了一些抽象, 但仍然是比较低级的.\nFlash通过一个新的组件, TLFTextField对外提供TLF功能, 这个组件集成在IDE里面了, 它提供了类似TextField API的高级抽象方法, 并且提供多种文字排版功能. 这些功能包括多列文本,文本线程(texttreads又称链接文本域),右到左和垂直的文字,以及一些印刷方面的增强.\nFlex4框架通过3个基本组件实现TLF:Label,RichText以及RichEditableText,这些组件增加了文字处理能力.Label用于显示单行, 提供了对文本进行格式化的部分功能, RichText支持多行文本,并且可以嵌入图形以及文本格式化的全部功能.RichEditableText增加了链接,编辑,滚动以及选择功能. 但Flex并不支持文本线程(text threads).\n原理\nTLF说明了Adobe在API开发上思想转变, 借助TLF, Adobe选择了只暴露一些非常低级的player API, 然后在其之上建立一个\u0026quot;标准\u0026quot;的高级AS3 API. 这样做的原因主要考虑到减小播放器的尺寸,并且使TLF库的发展不依赖于播放器的升级而变化. 这意味着开发人员可以在新的播放器一发布出来就能利用新的TLF特征, 而不必再等到新播放器达到一个可接受的普及程度再考虑使用了。\n其意图是好的,但我并不觉得这是最好的解决办法. 几乎没有例外, 文本是在Flash平台上的每一个项目的核心元素, 使文本功能依赖于Actionscript库所带来的各种问题, 个人觉得远远超过了上述所说的各种优点.\n文件大小\n核心TLF库的大小大约是160Kb.它是在你swf文件之外的一个签名的RSL(.swz)文件. 这意味着一旦用户下载了这个swz文件.它将存储在本地缓存中并且下次不用重新下载就可重复使用,多个域名也可以共享这个swz的.\n这样很好, 但如果TLF是未来Flash的文本框架, 而文本又是用户体验的核心, 这会使flash播放器变小一点的优势变得没有意义.如果所有用户无论如何都得下载这个RSL, 你们就是在将问题扩大. 我也有这样的感觉(也许是错误的):这个功能直接内置在player中实现会更小一些。\n即使使用RSL, 在flash专业版中使用TLFTTextField类也将使你的swf增加60kb.This is a pretty big hit for just putting hello world on the stage, and makes TLFunusable for banners and other experiences with file size restrictions. 使这一问题更加突出的是: Flash是将TLF作为文本选项, 而不是一个文本组件, 这使得用TLF的潜在成本更不清楚.\n性能\n由于TLF是用ActionScript写的, 所以它和别的as代码一样同样面临性能和内存问题. 相比以前AS的性能已经有所提高, 而且10.1在内存使用方面也有了明显的改善, 但AS的运行效率比原生代码还是要慢 *很多* (在许多情况下有10 ~ 100倍的差距),并且内存的使用量仍然很大。TLF很大程序上因此而表现糟糕, 相比于TextField, TLF使用了更多的CPU资源计算以及处理显示对象, 基于这个原因, Adobe不建议将TLF用于手机产品. 并且正在考虑专为移动设备开发另外一套轻量级的TLF实现, 我觉得很失望,有点讽刺是, 很重要的Flash平台的两个 新 举措, 同时开发却互不兼容。\n另外TLF比TextField占用更多的内存, 除了自身的AS framework所需内存外, 它必须产生大量AS对象来描述文本的结构和排版, 还必须产生大量的显示对象(TextLine) 来显示文本.\n更糟糕的是, 这些占用内存的对象始终为所有文本而产生和维护着, 而不是只处理屏幕或者滚动条内可见的部分文本. 当只有那些可见的文本渲染到屏幕时, 由于必须要被再次遍历全部TextLine实例, 从而计算是否应该排除在scroll rect范围外,使得每一个TextLine仍然有显示的消耗(display cost). 这意味着在滚动区域内有大量的文本, 将会在内存和性能两方面都有着非常高的成本, 而在TextField中, 这个成本是很低的.\nFlash性能已经是够热的话题。TLF的AS实现将会使情况变得更糟。\n一致性和复杂性\nTLF代表了flash和flex团队的一次重大合作, 这次合作是一次 *伟大* 的事件!! 这些团队真的需要共同努力以创造一个统一的组件框架和综合创造平台. 我希望TLF提供了一个机会真正测试一下, 但不幸的是, 我认为他们做的还不够.\n目前, Flash Pro有用TLFTextField提供了一个非常象样的TLF高级抽象, 它公开一个以几乎和TextField相同的API, 而且增加了很多的附加接口。这使得它的易于使用和便于扩展。\n令人遗憾的是, Flex中似乎没有任何类似的TLF高级文本控制。即使TextArea也缺乏TextField的大部分功能. 即使为了实现一个非常简单的任务, 比如找一个字符位置, 你不得不费力地通过TLF库甚至涉及FTE来实现。\n在这一点上, 我担心难以改造这些功能而集成到Flex的spark架构中。我同时也感到纳闷: 是否是因为这些功能用AS实现而产生了性能问题, 从而导致了在flex中还没有这些功能。\n版本\n我明白将TLF版本和flash player分开而带来的好处, 但也正是因为它带来了其它的问题: 首先, 它减少了缓存的RSL的效果, 它必须下载每一个新版本而缓存起来. 它也明显影响所有基于TLF的团队开发、自定义控件、商业或OSS(开放源码) 软件库/组件,就算这些都不是(也许除了最后一个)大问题, 他们也还是降低了该模型的整体效应。\n思考\n如果有位同学在深入地用尽了TextField API之后也搞不定某些事, 他就会被迫转而尝试用其它方法, 很可能是FTE(多列, 文本装饰). 我赞同flash player中包含低级的文本API, 不过, 我认为 文本 是互动媒体不可或缺的部分, 也应该是 平台 的不能不用的高性能的核心部分.\n我真的很想看到的, 和FTE API一样低级的, 扩展自playe层面的和现在TLF相似的文本排版API. 这可能是 建立在 一组接口 ( 因为它已经 在很大程度上 是)之上 , 以方便 替换现有的 AS3 实现的这些类。此外, 我希望看到的一个可用的player层面实现的TextField, 实现了现在TLFTextField的大部分功能。而且会有一个ITextField接口, TextField也将改造实现这个接口。\n理想的情况下，这将大大增强性能和减少内存占用，同时使开发人员能深入挖掘和创建自定义实现。还可保证Flash和Flex有一个一致的API和实现，甚至允许开发人员在不知道容器是是TLF还是TextField的情况下处理文字。虽然这会增加player的大小，它会占用一点整体带宽来用来下载有TLF有关的内容。Adobe甚至可以扩展player内建类而生成AS3类, 通过不断更新这些AS3类来更新TLF的某些方面。\n可能为时已晚，但我不这么认为。当前player API不必改变, 这意味着目前的工作将不会中断。目前TLF API和TLFTextField能原生地在flash.*包中实现, 而不必中断当前的AS3版本. 未来版本的Flex框架能以非常小的影响而迁移到原生API中. 同样, Flash Pro的 TLFTextField 也可被剥离, and become viable for banners and mobile.\n如果不能做到这点, 我想至少花时间修复和增强的TextField, 在TLF根本不是最好的选择的时候, 使我们可以继续使用它.\n即使是来不及修复(在我看来)TLF，我觉得人们在讨论未来的决策时, 明确Adobe的新理念和忠告也是很重要的。我不认为Adobe应该象MS Silverlight那样(其中控制/组件都嵌入在播放器中)，但我认为作为核心元素，TLF应该在播放器中。\n我 想说的还有很 多 , 但是这 太长了 , 我认为 本文涵盖了 最紧迫的 话题。 我很想 听听 你 的意见 看法。\n[原创翻译链接: http://www.smithfox.com/?e=74 , 转载请保留此声明, 谢谢]\n[部分内容参考了 Tomyail的blog 的翻译文章 一些对TLF和FTE的思考 ]\n在原文的comments中看到了一个adobe的和FTE的bug, https://bugs.adobe.com/jira/browse/FP-3133, 大家可以去投票, 促使adobe早点改进\nsmithfox said: 06-02-'11 22:18\n不知在你认真看完这篇文章后, 你是否有和我有一样的感觉: 一片茫然.\n我们应该怎么选择文本框架? 或是我们还有选择吗？\n不过我不是一个迷信的人, 尽管这篇文章是很牛的Grant Skinner写的.\n让我们看一下作者说TLF的不足的重点: TLF是用AS3库的方式来实现的, 而没有在flash player中用native实现.\n从adobe来说, 其实TextField(在flash player中实现)本身就有很多的bug, TLF是比较复杂的, 放在player无疑会使flash player在相当长时间内无法稳定. 将TLF放在player还是AS3 lib中, 对于flash app来说, 其实不是非常重要,对flash app来说最重要的是API的稳定, 我们肯定不希望因为adobe将实现从as3 lib移到player中(或是相反), 我们就必须要大量改动现有的代码. TLF2.0和FXG相结合, 为API的长期稳定打下了基础. 我们最期望, 也是最有可能的是, AS3的运行效率将会持续地不断地得到提高, 这一点我们可以从Java的发展路线中得到启示.\n而且现在已经有这样的更高效的AS3的运行效率尝试了, 见 JITB http://blog.joa-ebert.com/2010/08/19/int..\n以这种方式提高效率, 对用户来说是代价最低的(不用改代码, 最多是重新编译, 这样可以省去大量regression bug的验证成本). 我们的大部分程序不是专门的文本处理程序, 要显示的最多也就一屏的字, 也不会用到很多不同风格的文本格式. 所以因scroll的出现面形成的TLF浪费很多的TextLine实例的可能性就很小. 幸运的是, FTE还是建立在player中的. 见 http://opensource.adobe.com/wiki/display..and TLF text in MX components\n原文如下: \u0026quot;FTE is the foundation for all future text functionality in the Player. TextField will be maintained for backward compatibility, but it will no longer be enhanced, and bug fixes are unlikely.\u0026quot; 我在另一篇和本文有着很想似的外国文章的comments中看到了不同的声音, 呵呵, 这篇文章的标题更直接: \u0026quot;请少一点AS3 Lib,多一点原生的API吧, adobe求你了\u0026quot;,http://artman.fi/2010/08/less-as3-framew..\nPaul.e.Taylor的comments中有一段说: 因为自己有做过一个Rich Text Framework的经验, 他想说FTE做的真的已经他妈的很完美了, 要知道文本的 \u0026quot;装饰/交互/排版\u0026quot; 和FTE要解决的事情完全是两码事, 用FTE的方式来处理TLF, 你会发现那是难以置信的低效.\nPaul.e.Taylor的感受, 用中国人的话就是\u0026quot;不养儿, 不知父母恩\u0026quot;呀, 这个观点正好对应Grant Skinner同学在文中说 \u0026quot;象FTE一样地处理TLF\u0026quot;的倡议. 对于有人用MS的Silverlight方式, 所有都放在player中实现, Paul.e.Taylor也指出了MS的特定性. 如果你不知道Paul.e.Taylor是何方神圣, 那就在网上找一下tinyTLF吧.http://www.tinytlf.org/ 某blog中说: FTE 不但显示效果比 TextField 对象好，而且允许更精确地定位文本。可以使用 TextLine 的 ascent、descent、textWidth 和 textHeight 等属性精确地定位文本。 7yue在微博(http://t.sina.com.cn/7yue)中写道: AS3优化小技巧：Flash里处理只读字体效率最高的类是TextLine，而非TextField。如果你的Flash需要从外部加载文字并显示在Stage上，不需要用户编辑什么，请使用TextBlock.createTextLine方法（因为TextLine不能直接实例化）。 7yue在CSDN上授课说: 只读文字用flash.text.engine(FTE)、输入文本尽可能用textfield类、对于textfield对象不要使用=运算符，而使用appendtext()方法. Text Input - FTE TextField, TLFTextField, Custom Classhttp://forums.adobe.com/message/3255803 ","date":"2011-02-09","description":"","lastmod":"2011-02-09T06:16:38Z","slug":"1248","tags":["fte","tlf"],"title":"【转】一些对TLF和FTE的思考","url":"https://blog.zengrong.net/post/1248/"},{"categories":["technology"],"content":"第一天上班，发现Flash Player 10.2已经发布了。顺便看了下自己最感兴趣的两个特性：原生鼠标光标和Stage Video。\n需要观看下面的Demo，需要下载最新的Flash Player 10.2版。\n非开发者，可以去http://get.adobe.com/flashplayer下载。\n开发者，最好是下载调试版；如果还要做个小例子的话，需要下载Flex SDK4.5.18623以上版本。至于SDK怎么安装，我就不说了，你懂的！\n关于Flash Player的安装，有一些小技巧，可以看这篇文章：有史以来关于Flash Player的最详细说明\n顺便说一句，Flash Player10.2终于可以通过右键菜单看到Flash Player的版本号了，这可是真是亲娘四舅奶奶的好事啊！\n下面是原生鼠标光标的一些Demo：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n两篇介绍原生鼠标光标的文章：\nWorking with native mouse cursors in Flash Player 10. Flash Player 10.2 With FlashDevelop (and a quick Cursor Manager) 一篇介绍Stage Video的文章：\nhttp://www.adobe.com/devnet/flashplayer/articles/stage_video.html\n再附送一篇介绍3DAPI的文章吧，虽然现在不能用，但结合上面一篇文章看，更容易理解：\nhttp://www.adobe.com/newsletters/edge/january2011/articles/article1/index.html\n","date":"2011-02-09","description":"","lastmod":"2011-02-09T02:10:11Z","slug":"flash-player-10-2-new-feature","tags":["3d","flashplayer","video"],"title":"Flash Player 10.2新特性","url":"https://blog.zengrong.net/post/flash-player-10-2-new-feature/"},{"categories":["technology"],"content":"在进行概要分析的时候，出现下面的错误提示：\n无法连接到应用程序以访存概要分析数据。请再次尝试对应用程序进行概要分析。\n网上找了资料讲到要修改FlashBuilder.ini：\n进入Flash Builder安装目录，打开FlashBuilder.ini文件，增加下面这句话：\nWindows 7系统\n-Duser.home=C:\\\\Users\\\\[你的用户名]\nWindows XP系统\n-Duser.home=C:\\\\Documents and Settings\\\\[你的用户名]\n但我安装的是插件版，没有FlashBuilder.ini这个文件，则需要修改Eclipse安装目录下的eclipse.ini文件。\n关于这个错误产生的原因，可以看这里。\n","date":"2011-01-28","description":"","lastmod":"2011-01-28T01:57:43Z","slug":"1241","tags":["flashbuilder"],"title":"Flash Builder 概要分析 无法连接","url":"https://blog.zengrong.net/post/1241/"},{"categories":["technology"],"content":"早上起来找了1个多小时的资料，足迹遍及CSDN、JavaEye、9RIA、51job，关于网页游戏服务器端开发，主要有以下几点说法和结论：\n语言之间性能的区别并不重要，重要的是数据库性能、开发成本（开发时间和开发效率）、语言平台成熟性（是否有大量优质框架和类库可用、是否有高质量团队支持）； 更重要的是服务器架构的设计； C++性能自然比C#和JAVA高，但开发、调试成本太高，除非有成熟的框架，否则不适合做网页游戏的快速开发； 现有的大量C++服务端，主要源于以前网络游戏的服务端主要是C++开发，后来转到网页游戏，比较方便； Linux平台，想都不用想：C++或JAVA； 从招聘信息来看，C++最多，JAVA其次，偶有C#和Python。 附51job的游戏服务端招聘信息\n","date":"2011-01-25","description":"","lastmod":"2011-01-25T02:31:11Z","slug":"gameserver-language","tags":["game","choice"],"title":"网页游戏服务器端语言选择分析","url":"https://blog.zengrong.net/post/gameserver-language/"},{"categories":["technology"],"content":"在AIR中对静态文件使用POST方法获取内容的时候，会出现一个IOError错误：\n[IOErrorEvent type=\u0026quot;ioError\u0026quot; bubbles=false cancelable=false eventPhase=2 text=\u0026quot;Error #2032: Stream Error. URL: http://demo.zengrong.net/freeroom/index.html\u0026quot; errorID=2032]\n注意，只有对 真正的纯静态文件 才会发生这样的错误，而对于使用URL重写方式虚拟出来的静态文件，则不会报错。（本文地址就是用UrlRewrite重写出来的，不信的可以试试 2013-01-24 本博客已经改为静态化地址了，请另找地方测试）\n对于以 file:/// 开头的本地文件，不会报错。 在非AIR环境下，也完全正常，不会报错。 究其原因，可能是由于AIR对HTTP协议的方法实现得更加标准。因为AIR不仅支持POST和GET方法，还支持PUT和DELETE方法。 不了解PUT、DELETE、GET、POST的看这里。\n看下面的代码：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;s:WindowedApplication xmlns:fx=\u0026#34;http://ns.adobe.com/mxml/2009\u0026#34; 3 xmlns:s=\u0026#34;library://ns.adobe.com/flex/spark\u0026#34; 4 xmlns:mx=\u0026#34;library://ns.adobe.com/flex/mx\u0026#34; 5 applicationComplete=\u0026#34;windowedapplication1_applicationCompleteHandler(event)\u0026#34;\u0026gt; 6\u0026lt;fx:Script\u0026gt; 7 \u0026lt;![CDATA[ 8 import mx.events.FlexEvent; 9 10 protected function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void 11 { 12 _loader = new URLLoader(); 13 _loader.addEventListener(Event.COMPLETE,handler_comp); 14 _loader.addEventListener(IOErrorEvent.IO_ERROR,handler_err); 15 var __request:URLRequest = new URLRequest(\u0026#39;http://demo.zengrong.net/freeroom/index.html\u0026#39;); 16 __request.method = URLRequestMethod.POST; 17 _loader.load(__request); 18 } 19 20 private var _loader:URLLoader; 21 22 private function handler_comp(evt:Event):void 23 { 24 trace(_loader.data); 25 } 26 27 private function handler_err(evt:IOErrorEvent):void 28 { 29 trace(evt.toString()); 30 } 31 32 ]]\u0026gt; 33\u0026lt;/fx:Script\u0026gt; 34\u0026lt;/s:WindowedApplication\u0026gt; ","date":"2011-01-05","description":"","lastmod":"2011-01-05T01:49:08Z","slug":"air-urlloader-post","tags":["air","http","as3"],"title":"AIR的URLLoader不支持对静态文件使用POST方法获取","url":"https://blog.zengrong.net/post/air-urlloader-post/"},{"categories":["technology"],"content":"正则表达式是非常强大的字符串处理工具，但由于晦涩难懂，唯有不断的学习和使用，才能积累经验。我使用正则表达式总是断断续续，所以水平也很初级。下面就记录这次的使用经验，备查。\n下面的xml代码是一个游戏技能配置文件的简化版，其中的items是一个技能，item是该技能的一个级别的值。desc属性是该技能的介绍文本。由于介绍中包含对技能的效果的引用，而技能的效果在不同的技能级别中的值是不同的，因此这里使用定界符来标识可能会变动的值。\n针对定界符，我制定的规则很简单，用花括号 {} 包含要替换的属性值即可，如 {key} ，其中key就是属性名称。对于使用数组方式提供的属性值，则使用 {key[n]} 的方式来提供，其中n是数组的索引。\n程序中要做的，就是在游戏中需要技能的介绍的时候，会根据技能的级别获取到相关的的值，然后查询desc中有哪些定界符，再根据定界符获取到item中的值，最后进行替换。\n这个工作虽然并不复杂，但也不是indexof能够解决的，后面就是我使用正则表达式的处理思路。对于XML的解析，我使用E4X。\n1\u0026lt;skill\u0026gt; 2 \u0026lt;items id=\u0026#34;2121\u0026#34; name=\u0026#34;魔能烧烬\u0026#34; targetSide=\u0026#34;2\u0026#34; type=\u0026#34;special1\u0026#34; element=\u0026#34;2\u0026#34; desc=\u0026#34;主动技能。使魔导师能够掌握魔法精密之处，从而对目标造成{effect[0]}点的火元素伤害，同时减少目标{effect[1]}%MP上限。\u0026#34;\u0026gt; 3 \u0026lt;item lv=\u0026#34;1\u0026#34; effect=\u0026#34;[20,4]\u0026#34; targetNum=\u0026#34;1\u0026#34; expended=\u0026#34;600\u0026#34; cd=\u0026#34;20000\u0026#34; /\u0026gt; 4 \u0026lt;item lv=\u0026#34;2\u0026#34; effect=\u0026#34;[40,6]\u0026#34; targetNum=\u0026#34;1\u0026#34; expended=\u0026#34;640\u0026#34; cd=\u0026#34;20000\u0026#34; /\u0026gt; 5 \u0026lt;/items\u0026gt; 6 \u0026lt;items id=\u0026#34;1203\u0026#34; name=\u0026#34;无情杀戮\u0026#34; targetSide=\u0026#34;2\u0026#34; type=\u0026#34;phy\u0026#34; element=\u0026#34;-1\u0026#34; desc=\u0026#34;主动技能。没有一丝怜悯的连续两次猛击目标要害，对目标造成{effect}点的物理伤害。\u0026#34;\u0026gt; 7 \u0026lt;item lv=\u0026#34;1\u0026#34; effect=\u0026#34;1114\u0026#34; targetNum=\u0026#34;1\u0026#34; expended=\u0026#34;50\u0026#34; cd=\u0026#34;13000\u0026#34; /\u0026gt; 8 \u0026lt;item lv=\u0026#34;2\u0026#34; effect=\u0026#34;1180\u0026#34; targetNum=\u0026#34;1\u0026#34; expended=\u0026#34;60\u0026#34; cd=\u0026#34;13000\u0026#34; /\u0026gt; 9 \u0026lt;/items\u0026gt; 10\u0026lt;/skill\u0026gt; 一、检测值是否是数组形式 在这里，由于值都是整数，出现的也值可能是整数数组，所以正则表达式写起来就简单许多：\n1public static function isArray($str:String):Boolean 2{ 3 $str = $str.replace(/\\s/g, \u0026#39;\u0026#39;); 4 var _reg:RegExp = /^\\[(\\d+,?)+\\d+\\]$/; 5 return _reg.test($str); 6} 先使用replace将字符串中的空格剔除，然后检测如 [int,int...] 的格式。这个正则原理如下：\n(\\d+,?)+ 匹配以逗号分隔的整型字符串，例如“123,456”或者“1”这样的格式 第二个 \\d+ 则确保 123, 这样的字符串不被匹配 其他的就很简单，不说了 二、将字符串转成数组 先剔除空格，再剔除方括号，然后通过定界符将字符串转成数组，并转换成int存储。其中重点是字符类 [] 的运用。由于要查找的正好是方括号，而正则表达式中也是用方括号来定义字符类的，因此方括号在字符类中，必须使用转义符 \\ 进行转义才能使用。\n1public static function toArray($str:String, $delimiter:String=\u0026#39;,\u0026#39;):Array 2{ 3 $str = $str.replace(/\\s/g, \u0026#39;\u0026#39;); 4 $str = $str.replace(/[\\[\\]]/g, \u0026#39;\u0026#39;); 5 var __arr:Array = $str.split($delimiter); 6 for(var i:int=0; i\u0026lt;__arr.length; i++) 7 __arr[i] = int(__arr[i]); 8 return __arr; 9} 三、将XML中的其他值存入一个对象 为了方便后面提取值，将XML中的所有属性存入一个对象中，这里为了演示方便，只存储了effect属性：\n1public function getSkillVO($id:String, $lv:String):Object 2{ 3 var __itemxml:XML = _skill.items.(@id==$id).item.(@lv==$lv)[0]; 4 var __vo:Object = {}; 5 var __effect:String = __itemxml.@effect.toString(); 6 if(isArray(__effect)) 7 __vo.effect = toArray(__effect); 8 else 9 __vo.effect = int(__effect); 10 return __vo; 11} 四、替换 重要的是String的match方法的作用。这个方法对没有使用global标志 /g 的正则表达式，会将其中的每个组的匹配结果作为一个元素加入到匹配结果数组中。这样，就可以方便的提取到形如 {effect[0]} 的字符串中的数组索引值。\n所有的分析都写在注释中了：\n1//检测字符串中是否有如{delimter}和{delimter[n]}形式的定界符，并使用对应的值进行替换 2private function replaceDesc($str:String, $skillvo:Object):String 3{ 4 //支持全局匹配的正则 5 var __globalReg:RegExp = /{(\\w+)(\\[(\\d)\\])?}/g; 6 //用于匹配每个全局匹配结果的正则 7 var __itemReg:RegExp = /{(\\w+)(\\[(\\d)\\])?}/; 8 //全局匹配的结果 9 var __globalMatch:Array = $str.match(__globalReg); 10 //待替换的键名数组 11 var __keys:Array = []; 12 //待替换的值数组 13 var __values:Array = []; 14 15 var __itemMatch:Array = null; 16 var __itemIsArray:Boolean = false; 17 var __itemKey:String = \u0026#39;\u0026#39;; 18 for each(var __str:String in __globalMatch) 19 { 20 __itemMatch = __str.match(__itemReg); 21 /*按照正则表达式规则，__itemMatch应该是有4个元素的数组： 22 1 整个字符串，2 第一个括号(\\w+)的内容，3 第二个括号(\\[(\\d)\\])的内容，4 第三个括号(\\d)的内容 23 因此，如果第4个元素为undefined，就说明在字符串中，没有数组的定界符*/ 24 __itemIsArray = __itemMatch[3] != undefined; 25 //第2个元素就是去掉了花括号和数组定界符（如果有）的字符串，也就是要$skillvo中的变量名 26 __itemKey = __itemMatch[1]; 27 //第1个元素是整个大字符串中的要被替换的部分，包含花括号 28 __keys.push(__itemMatch[0]); 29 //如果值是数组中的元素，就是用数组中对应索引的值 30 if(__itemIsArray) 31 __values.push($skillvo[__itemKey][__itemMatch[3]]); 32 //否则，就直接使用变量的值 33 else 34 __values.push($skillvo[__itemKey]); 35 } 36 //开始替换 37 for(var j:int=0; j\u0026lt;__keys.length; j++) 38 { 39 $str = $str.replace(__keys[j], __values[j]); 40 } 41 return $str; 42} 全部代码 1package 2{ 3import flash.display.Sprite; 4 5public class SkillRegExpTest extends Sprite 6{ 7 public function SkillRegExpTest() 8 { 9 trace(replaceDesc(_skill.items.(@id==\u0026#34;2121\u0026#34;).@desc.toString(), getSkillVO(\u0026#39;2121\u0026#39;, \u0026#39;2\u0026#39;))); 10 } 11 12 private var _skill:XML= 13 \u0026lt;skill\u0026gt; 14 \u0026lt;items id=\u0026#34;2121\u0026#34; name=\u0026#34;魔能烧烬\u0026#34; targetSide=\u0026#34;2\u0026#34; type=\u0026#34;special1\u0026#34; element=\u0026#34;2\u0026#34; desc=\u0026#34;主动技能。使魔导师能够掌握魔法精密之处，从而对目标造成{effect[0]}点的火元素伤害，同时减少目标{effect[1]}%MP上限。\u0026#34;\u0026gt; 15 \u0026lt;item lv=\u0026#34;1\u0026#34; effect=\u0026#34;[20,4]\u0026#34; targetNum=\u0026#34;1\u0026#34; expended=\u0026#34;600\u0026#34; cd=\u0026#34;20000\u0026#34; /\u0026gt; 16 \u0026lt;item lv=\u0026#34;2\u0026#34; effect=\u0026#34;[40,6]\u0026#34; targetNum=\u0026#34;1\u0026#34; expended=\u0026#34;640\u0026#34; cd=\u0026#34;20000\u0026#34; /\u0026gt; 17 \u0026lt;/items\u0026gt; 18 \u0026lt;items id=\u0026#34;1203\u0026#34; name=\u0026#34;无情杀戮\u0026#34; targetSide=\u0026#34;2\u0026#34; type=\u0026#34;phy\u0026#34; element=\u0026#34;-1\u0026#34; desc=\u0026#34;主动技能。没有一丝怜悯的连续两次猛击目标要害，对目标造成{effect}点的物理伤害。\u0026#34;\u0026gt; 19 \u0026lt;item lv=\u0026#34;1\u0026#34; effect=\u0026#34;1114\u0026#34; targetNum=\u0026#34;1\u0026#34; expended=\u0026#34;50\u0026#34; cd=\u0026#34;13000\u0026#34; /\u0026gt; 20 \u0026lt;item lv=\u0026#34;2\u0026#34; effect=\u0026#34;1180\u0026#34; targetNum=\u0026#34;1\u0026#34; expended=\u0026#34;60\u0026#34; cd=\u0026#34;13000\u0026#34; /\u0026gt; 21 \u0026lt;/items\u0026gt; 22 \u0026lt;/skill\u0026gt; 23 24 /** 25 * 检测一个字符串是否是[int,int...]的形式 26 */ 27 public function isArray($str:String):Boolean 28 { 29 //将字符串中的空格剔除 30 $str = $str.replace(/\\s/g, \u0026#39;\u0026#39;); 31 var _reg:RegExp = /^\\[(\\d+,?)+\\d+\\]$/; 32 return _reg.test($str); 33 } 34 35 /** 36 * 将一个符合数组格式的字符串转换成整型数组 37 */ 38 public function toArray($str:String, $delimiter:String=\u0026#39;,\u0026#39;):Array 39 { 40 $str = $str.replace(/\\s/g, \u0026#39;\u0026#39;); 41 $str = $str.replace(/[\\[\\]]/g, \u0026#39;\u0026#39;); 42 var __arr:Array = $str.split($delimiter); 43 for(var i:int=0; i\u0026lt;__arr.length; i++) 44 { 45 __arr[i] = int(__arr[i]); 46 } 47 return __arr; 48 } 49 50 public function getSkillVO($id:String, $lv:String):Object 51 { 52 var __itemxml:XML = _skill.items.(@id==$id).item.(@lv==$lv)[0]; 53 var __vo:Object = {}; 54 var __effect:String = __itemxml.@effect.toString(); 55 if(isArray(__effect)) 56 __vo.effect = toArray(__effect); 57 else 58 __vo.effect = int(__effect); 59 return __vo; 60 } 61 62 /** 63 * 检测字符串中是否有如{delimter}和{delimter[n]}形式的定界符，并使用对应的值进行替换 64 * @param $str 被替换的完整字符串 65 * @param $skillvo 包含要替换的内容的 66 * @return 67 * 68 */ 69 private function replaceDesc($str:String, $skillvo:Object):String 70 { 71 //支持全局匹配的正则 72 var __globalReg:RegExp = /{(\\w+)(\\[(\\d)\\])?}/g; 73 //用于匹配每个全局匹配结果的正则 74 var __itemReg:RegExp = /{(\\w+)(\\[(\\d)\\])?}/; 75 //全局匹配的结果 76 var __globalMatch:Array = $str.match(__globalReg); 77 //待替换的键名数组 78 var __keys:Array = []; 79 //待替换的值数组 80 var __values:Array = []; 81 82 var __itemMatch:Array = null; 83 var __itemIsArray:Boolean = false; 84 var __itemKey:String = \u0026#39;\u0026#39;; 85 for each(var __str:String in __globalMatch) 86 { 87 __itemMatch = __str.match(__itemReg); 88 /*按照正则表达式规则，__itemMatch应该是有4个元素的数组： 89 1 整个字符串，2 第一个括号(\\w+)的内容，3 第二个括号(\\[(\\d)\\])的内容，4 第三个括号(\\d)的内容 90 因此，如果第4个元素为undefined，就说明在字符串中，没有数组的定界符*/ 91 __itemIsArray = __itemMatch[3] != undefined; 92 //第2个元素就是去掉了花括号和数组定界符（如果有）的字符串，也就是要$skillvo中的变量名 93 __itemKey = __itemMatch[1]; 94 //第1个元素是整个大字符串中的要被替换的部分，包含花括号 95 __keys.push(__itemMatch[0]); 96 //如果值是数组中的元素，就是用数组中对应索引的值 97 if(__itemIsArray) 98 __values.push($skillvo[__itemKey][__itemMatch[3]]); 99 //否则，就直接使用变量的值 100 else 101 __values.push($skillvo[__itemKey]); 102 } 103 //开始替换 104 for(var j:int=0; j\u0026lt;__keys.length; j++) 105 { 106 $str = $str.replace(__keys[j], __values[j]); 107 } 108 return $str; 109 } 110} 111} ","date":"2010-12-31","description":"","lastmod":"2010-12-31T07:11:22Z","slug":"regexp-config-files-replace","tags":["as3","regexp"],"title":"使用正则表达式解决配置文件字符串替换的思路和例子","url":"https://blog.zengrong.net/post/regexp-config-files-replace/"},{"categories":["use"],"content":"最开始，我使用Courier New做编程用的字体。时间长了感觉这种扁扁的一成不变的字体太生硬，且不支持ClearType，于是就改用了Consolas。\nConsolas确实不错，但在中文显示方面，又不行了。在程序源码中，不可避免要使用中文注释。Consolas当然不支持中文，因此中文默认是使用宋体显示的。当使用10点大小的时候，中文就模糊不清了。如果采用斜体显示注释的话，宋体就更加显得支离破碎。\n雅黑字体确实在中文显示上不错，但雅黑不是等宽字体，不能用于源码显示。\n所以，就有网友使用字体工具将雅黑和Consolas集成在一起，让程序员可以在源码中看到优秀的中文显示效果。如下：\n不过，这个字体中的Consolas有了一些变化，不像纯Consolas那么圆滑了。\n字体下载：\n1 文件 去字体合并作者博客下载\n","date":"2010-12-27","description":"","lastmod":"2010-12-27T02:21:32Z","slug":"yahei-consolas","tags":["font"],"title":"雅黑-Consolas混合字体，编程专用","url":"https://blog.zengrong.net/post/yahei-consolas/"},{"categories":["news"],"content":" 转自：http://bigt.javaeye.com/blog/764430 英文原文：http://www.billdwhite.com/wordpress/?p=296 当我了解到Flex4那些对我诸多裨益的新特性后,我便决定转而使用它。刚开始的时候，我试图利用在Flex前作中的认识和既有经验来快速进入状态。但很快我便发现有时即使面对一些显而易见的问题我也不得不求助于API文档或者运行一些示例程序来弄清这种问题的来龙去脉。根据以往经验，Flex3的Halo在处理显示列表的时候隐藏了大量的实现细节和不良设计。然而一旦你开始使用新的Spark架构后，你就得以近距离的面对这些实现细节—Halo究竟在私底下干了什么，而且你会体会到为什么说Spark对于显示列表的处理更为“直白”。\n“elements”是一个关键性的问题。elements是何物？它同child是否是一回事？刚开始的时候我曾武断的认为elements不过是children的另一种说法。通过反复梳理组件中所有的elements和children，我发觉在新的容器类（也包括一些经过改良的传统容器）某些似乎是理所当然应该具备的方法消失了。如果没有getElements()，我该如何获取elements的数目呢？我能否把getChildren()的返回结果作为IVisualElement来对待。这令我十分纠结。\n困扰的我于是开始认真阅读学习API文档，Flex的源码以及相关的博客文章。我也曾尝试解读一些博主发布的关于Flex4新特性的幻灯片。然而事实证明脱离讲解而孤立的看幻灯片作用相当有限。\n最后，我拼凑了一些言简意赅的示例。这些示例将带领我了解有关elements的全新知识，告诉我那些在新的Spark容器背后发生的故事。\n言归正传，首先从问题开始。\n问题一，“应该如何获得Spark容器的全部elements？”\n我曾想当然的认为是通过一个类似Flex3中的getChildren()的方法。然而实际上我们需要通过两个Property来达到这个目的：numElements \u0026amp; numChildren 。可以通过对numElements计数的循环语句配合getElementAt()来实现遍历容器elements或特定访问。这种方式还比较直观。\n问题二，“element和child的区别何在？”，让我们来看看两者的差异。\n语义上，element简单的说就是实现了IVisualElement接口的任意型别。child是指扩展了DisplayObject类的任意型别。判断某个组件是element还是child亦或两者都是的关键在于以下几点。UIComponent(所有Flex组件的基类：译者注)是由DisplayObject扩展而来，故所有UIComponent都是DisplayObject，也就是说UIComponent都是children。UIComponent同时也实现了IVisualElement接口，因而所有的UIComponent也可以被作为elements看待。但这并不是说所有的DisplayObjects（文中所言的DisplayObject一般指扩展于DisplayObject的子类，译者注）都是elements。容器中的DisplayObject对象是该无疑是容器的child。而只有当此DisplayObject对象同时也实现了IVisualElement接口时它才是容器的element。那么对容器而言，DisplayObject什么情况下是child，什么情况下又是element？通过示例来认识这个问题。\n在首个示例中，我们使用了传统的Halo容器（这里我们使用的Panel）。Panel扩展与DisplayObject类，所以它可以使用addChild()方法。进一步而言，Panel也是Container类的子类（mx.core.Container实现了IVisualElementContainer接口），它具有addElement()方法。Container类的IVisualElementContainer接口实现只是基于显示列表API的门面，所以理论上它和同样实现了IVisualElementContainer接口的新式Spark容器具有相同的方法集合。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n源文件\n通过以上阅读，也许起不到拨云见日的效果。但可以让你明白厘清以下七个类/接口的继承结构和相互关系是十分有必要的：\nDisplayObject UIComponent Container IVisualElement IGraphicElement IVisualElementContainer ISharedDisplayObject 一旦你掌握它们之间的关系，你就能明白elements和children的不同。可以肯定的是我在某些问题的认识和阐述上存在很多谬误之处。如果你发现了这样的问题望不吝赐教，在评论处写下您的正确观点吧。\n访问下面的链接可以获得关于本文探讨及其相关的主题的更多内容。\nGumbo DOM Tree API（关于本话题的详实细节）\n关于了解组件的owner和parent之间差异的极好的示例\n","date":"2010-12-24","description":"","lastmod":"2010-12-24T05:55:52Z","slug":"flash4-element-and-child","tags":["component","flex","spark"],"title":"【转】深入Flex4——了解Element和Child的异同","url":"https://blog.zengrong.net/post/flash4-element-and-child/"},{"categories":["news"],"content":"转自：青竹的日志\n内存优化在项目是一个重要的环节，如果不合理的利用和回收内存会合你的程序整体大大下降.\n合理使用对象 创建不同对象一般所消耗的内存是不一样的。如:Number 消耗 8 个字节,int消耗4个字节, uint消耗 4个字节.下面我举一些例子:\n1. int 类可使用表示为 32 位带符号整数的数据类型。 int类表示的值的范围是：-2,147,483,648 (-2\\^31) 到 2,147,483,647 (2\\^31-1)，所以如果你的取值范围在 -2,147,483,648 (-2\\^31) 到 2,147,483,647 (2\\^31-1) 请你用int而不是用Number(刚从2.0转过来的人可能喜欢用Number) uint 范围是0 到 4,294,967,295 (2\\^32-1) 之间, Actionscript3 中类型很少，所以这些只要你平时稍加注意一下就行.\n2. 合理使用Shape与Sprite,MovieClip,你可能用MovieClip可以完成Sprite与Shape的功能，但是他们所需的内存是不一样的Shape需要 236 字节,Sprite 需要 412字节, MovieClip 需要440字节,如果你只想显示图形没有交互那你使用Shape,如果是有交互的图形你可以用Sprite,如果是动画你才用MovieClip.\n以上只是2个常见的实例，其实在as3中还有很多值得注意的这类情况。我还看到有些大哥为了派发一个事件而去继承Sprite类,Sprite需要消耗400字节, EventDispatcher只需要40字节。\n对象重用 简单的说就是重复使用对象，而不是释放再重新申请,你可能这么认为：释放一个对象回收100字节的空间，我重新在new一个又占用100字节，返正都得占用100字。在flashplayer中不是说你想释放就能释放的,垃圾回收是由flashplayer来执行的，程序是不能控制，更不知道他是什么时候来执行的.所以对于我们来说回来是完全不可控的.所以说你觉得可回收了对象，很可能没回收继续占用空间直到flashplayer觉得内存不够时才可能执行垃圾回收从而被释放.而执行垃圾回收是非常耗费资源的操作尤其在大型的项目中.\n虽然我们不能控制垃圾回收，但是我们可以降低垃圾回收器执行的次数，这就是我们尽量做到不去new,而使用现有的对象。这些做还有个好处就是，节省了创建对象的性能开销.\n这里是不断的创建对象\n1var size:Rectangle; 2var bitmap:BitmapData=new BitmapData(100,100); 3for (var:int = 0; i \u0026lt; 100; i++) 4{ 5 6 size = new Rectangle(i,0,1,10); 7 myBitmapData.fillRect(size,COLOR); 8} 这是重用对象\n1var bitmap:BitmapData=new BitmapData(100,100); 2var size:Rectangle = new Rectangle(0,0,1,10); 3for (var:int = 0; i \u0026lt; 100; i++) 4{ 5 size.x = i; 6 myBitmapData.fillRect(size,COLOR); 7} 对象池技术 对象池技术的原理就是回收不使用的对象而不是遗弃等待FlashPlayer垃圾回器的执行,等到再需要时再拿来使用.这种技术使用非常广泛,看下面的简单的实现(这里只是一个抛砖引玉，具体怎么设计这个对象池，要看各位的具体项目了).\nIRecyclable接口，可以被回收对象必须实现。再就是ObjectPool.\n1package 2{ 3 public interface IRecyclable 4 { 5 function dispose():void; 6 } 7} 8//对象池的实现 9package{ 10public class ObjectPool{ 11 12 private var pool:Vector.; 13 private var type:Class;//回收对象的类型 14 15 public function ObjectPool(type:Class) 16 { 17 this.type=type; 18 pool=new Vector.(); 19 } 20 public function addObject(o:IRecyclable):void{ 21 o.dispose(); 22 pool.push(o); 23 } 24 public function getObject():*{ 25 if(pool.length\u0026lt;=0) return new type(); 26 return pool.pop(); 27 } 28 } 29} 对象的另类存储 在存储大量对象时，我们可以以另一种方式存储或者叫序列化，即存储对象的数据而不存储具体的对象，当需要时再根椐需要数据返序列化出一个对象。这样做法的好处在于不会有大量的对象产生，在as中对即使是空对象也会占用40个字节.\n很多时候你可能这样写代码:\n1var p:Array=[]; 2 3for(var i:int=0;i\u0026lt;1000000;i++){ 4 p.push(new Point(i,i)) 5} 这里我来实现另一种对象存储:\n1package{ 2 import flash.geom.Point; 3 4 public class PointContainer{ 5 6 private var container:Array 7 public function PointContainer() 8 { 9 container=[]; 10 } 11 public function add(v:Point):void{ 12 container.push(v.x); 13 container.push(v.y); 14 } 15 public function getPointAt(index:int):Point{ 16 return new Point(container[index],container[index+1]); 17 } 18 } 19} 20//使用 21var p:PointContainer=new PointContainer 22for(var i:int=0;i\u0026lt;1000000;i++){ 23 p.add(new Point(i,i)) 24} 25for(var i:int=0;i\u0026lt;1000000;i++){ 26 p.getPointAt(i); 27} 这种对象存储方式适合大量小型对象存储，比如粒子系统.如果是庞大的对象，这种方式没有任何优式.\n事件优化 使用事件模型通信与使用传统的回调函数相比，速度更慢且占用的内存更多\nAS3中事件的派发传递参数是采用Event,所以在高频率派发事件的地方你可以采用传统的回调函数这样可以大大提高你的效率和内存消耗.\n位图优化 一般最占用内存的部分就是位图,在我开发的MMO游戏中90%以上的的内存是由位图占据的,所以在位图的使用过程序要特别注意，不使用的位图一定要释放掉。在这里我提一些小的建议，以尽量控制位图的内存占用。\n将滤镜应用于显示对象时，Flash Player 将在内存中创建两个位图,所以这需要大量内存。所以尽量不要去使用滤镜，一般可以用ps做好滤镜后生成位图给flash来使用. 合理的使用位图缓存.对矢量图形做位图缓存，其实在把矢量图形变成位图，并使用该位图进行呈现,此会显著提高呈现的性能，但需要占用大量内存。针对复杂的矢量内容使用位图缓存功能。 释放对象 释放对象其实只有一句话,就是不支有对象的引用,包括声音/视频流，socket,件事等等.我最多的一种情况是事件忘记移除导致对象无法回收，这并不是我不知道这一点，而是在写代码时的疏忽。如果你是一个人开发，你可能经常去profile你的代码，可能很容易找出哪个地方没被移除，但是如果你主程或者架构师你手下有很多少人在Coding，你怎么让他不遗忘移除事件呢，下面我来简单介绍一种方法一次性移除所有事件，避免一个一个移除带来的遗漏问题.\n一般大家都用EventDispatcher来派发事件.现在我们就对addEventListener进行一个小小的改造即可.\n1package 2{ 3 import flash.events.EventDispatcher; 4 import flash.events.IEventDispatcher; 5 6 public class MyEventDispatcher extends EventDispatcher{ 7 private var events:Array 8 public function MyEventDispatcher(target:IEventDispatcher=null) 9 { 10 super(target); 11 events=[]; 12 } 13 override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void{ 14 15 events.push({type:type,fun:listener}) 16 super.addEventListener(type, listener, useCapture, priority, useWeakReference); 17 } 18 override public function removeEventListener (type:String, listener:Function, useCapture:Boolean = false):void{ 19 super. removeEventListener(type,listener, useCapture); 20 for(var i:int=0;i\u0026lt; ;i++){ 21 if( events[i].type==type \u0026amp;\u0026amp; events[i].fun==listenner){ 22 events.splice(i,1) 23 } 24 } 25 } 26 public function dispose():void{ 27 var ev:Object; 28 while(events.length){ 29 ev=events.pop(); 30 super.removeEventListener(ev.type,ev.fun); 31 } 32 } 33 } 34} 所以你在使用EventDispatcher的地方全部使用MyEventDispatcher即可，在回收之前端调用一下dispose方法，就会内部移除所有事件.\n还有很多方法可以做到这一点，以上方法只是一个抛砖引玉.\n滥用强制垃圾回收 在 Flashplayer debug 版本提供了System.gc()接口，可以让虚拟机执行垃圾回收,但是flashplayer 普通用户版是没有这个接口的,于是有人想出使用异常还出发垃圾回收如:\n1function gc():void{ 2 try{ 3 (new LocalConnection).connect(\u0026#34;foo\u0026#34;); 4 (new LocalConnection).connect(\u0026#34;foo\u0026#34;); 5 }catch(e){ 6 trace(System.totalMemory); 7 } 8} 当然还有其他方法在这里我就不多举了.\nAdobe为什么在普通用户版的flashplayer中取消对System.gc()接口的支持,adobe肯定不希望用户直接去触发垃圾回器,肯定是有理由的,下面我将对这个理由进行的浅薄分析.\n垃圾回收器通过查找系统中的相互引用，从而检测出处于非活动状态的对象。将删除通过这种方式检测到的处于非活动状态的对象。也就说他要扫描可能持有对象的的所有变量，这一点所需的代价很大，尤其是在大型项目中.如果你经常去做这样的事，这会大大浪费你的CPU资源.\n我做Flash开发已快5年了,我没有什么地方非要用到强制垃圾回收的地方.所以我要在这里提醒广大Flash开发者,在没有特术需求的时候不要使用强制垃圾回收，不要会了腾出一点内存空间而去强制垃圾回收，可能很多时候你是检芝麻丢西瓜,回收器的执行不是普通程序员管的事，Flashplayer会选择最合适的时候去调用.(原本这一节不是我要写的内容，原因是我看到在天地会主页很醒目的位置有一篇《AS3强制内存回收方法之二》,而且受到大家的广泛关注,所以我不想让这篇文章误导了一些新手,以上只是个人见解我希望大家一起讨论这个问题)\n评论的转载 guissy 2010-12-11 12:32\n优先级 int uint Number 优先级 EventDispatcher Shape Sprite MovieClip for 或EnterFrame或TIMER里边少用new 闲置对象=null 不如 ObjectPool.add(闲置对象) [new Point(0,0),new Point(1,1)]不如 [0,0,1,1] guissy 2010-12-13 20:32\n关于对象池，考虑使用new Dictionary(true),具备弱引用。\n思路见贴：http://bbs.9ria.com/redirect.php?goto=findpost\u0026amp;pid=561614\u0026amp;ptid=63490\nlcj0526 2010-12-14 09:50\n另外关于对象池的看法，我提点个人意见，其实我觉得只要你不是在同时进行大量new的话，我并不赞成采用对象池的方法，加了对象池，会额外增加swf文件的大小，尽管不一定很多，另外，因为不是大量的生成，所以你new一个的对象所需的时间，可能比你去取对象池的速度还快。所以一般对象池技术是用于服务器的，除非客户端像你所说要做粒子效果的话，采用这个倒是不错，呵呵。以上纯属个人看法，不当之处还请高人指点\n青竹 2010-12-14 10:25\n谢谢这位兄弟的评点,对象池技术不只用在服务器，在设计中高密度创建的对象地方，对象池技术可以减小创建与销毁的代价.\nguissy 2010-12-13 17:36\n位图优化。如果是mmo之类需要严格控制内存的，这是非常值得关注的技术。如果是网页特效或一些全屏的小应用，还是矢量为佳. override addEventListener 与某人说的“事件总线”，都存在性能上和编码习惯的不友好。必要时使用弱引用，这是比较好的习惯。另外,框架式的代码必须用profile去检测.每个类都用dispose()做销毁工作也是相当好的习惯。赞同。 gc是自动管理内存。但不觉得gc能占用多少cpu.除非你的是循环着用.如果真有有那么费cpu，请截图. lcj0526 2010-12-14 09:44\n呵呵，你这个占CPU的我测试过，在我的项目里如果每桢都调用gc方法的话，CPU会一直在13以上，如果不调用的一般就是在0-5之间。我指的是在同种情况下，具体就是玩家不动，当前视野里没其他对象操作的情况下测试的。另外CPU占用的多少与计算机的配置也是有很大的关系的\n","date":"2010-12-21","description":"","lastmod":"2010-12-21T05:59:56Z","slug":"flash-high-performance-memory","tags":["as3","performance","tipsandtricks"],"title":"【转】Flash高性能开发基础系列—内存篇","url":"https://blog.zengrong.net/post/flash-high-performance-memory/"},{"categories":["technology"],"content":"相信用Flash Builder/Flex Builder做开发的大部分开发者都遇到过这样的问题，编译的swf在bin-debug中打开（不是在IDE环境中调试，而是直接双击用Flash Player打开），一切正常；而将其复制到其他文件夹，就会弹出安全错误。这是由于Flash Player对本地回放安全性的限制。详情可看官方文档中的“权限控制”部分。不想看官方文档的，继续往下看。\n解决这问题的方法主要是以下两种：\n一、使用本地信任文件 在Windows XP操作系统中，当前用户的本地信任文件路径为：\nC:\\Documents and Settings\\[你的用户名]\\Application Data\\Macromedia\\Flash Player\\#Security\\FlashPlayerTrust\n全局的信任文件路径为：\nC:\\windows\\system32\\Macromed\\Flash\\FlashPlayerTrust\n在这个文件夹中新建一个纯文本文件，文件名使用英文，扩展名为cfg，其中的内容就是你要信任的swf文件所在的本地路径，每行一个路径即可。\n例如，在我的信任路径中，已经被FlashBuilder自动建立了一个名为flashbuilder_plugin.cfg的本地信任文件列表，自动添加了当前所有项目的bin-debug目录，其部分内容为：\nC:\\Documents and Settings\\Administrator\\Adobe Flash Builder 4 Plug-in\nE:\\works\\kaitian\\demo\\TestNullFun\\bin-debug\nE:\\works\\kaitian\\demo\\TestPost\\bin-debug\nE:\\works\\kaitian\\demo\\Vector\\bin-debug\nE:\\works\\zrong.googlecode.com\\bin\n由于默认这些目录已经存在于信任文件中，因此是被信任的，这样在bin-debug下直接打开swf文件，没有权限限制。而如果复制到其他文件夹，而那个文件夹并不在被信任的目录中，就会有权限的限制，从而出现安全错误。\n也可以使用设置管理器来做这件事:http://www.adobe.com/go/settingsmanager_cn。\n这里还有一篇比较老的文章讲的也很详细。\n二、编译的时候指定本地回放安全性 如果swf在运行中载入了外部文件，那么这个swf在单独运行的时候，如果不处于信任目录中，就会显示安全错误（前提是使用的Debug版的Flash Player），无法载入外部的文件。\n有时候我们在网上下载一些swf小游戏，同时也将这些小游戏需要的资源一同下载并且放在正确的文件夹中，但却仍然出现安全错误，就是这个原因。这种情况下，只需要在本地搭建Web服务器，将要运行的swf放在web环境中运行即可解决。\n如果制作的swf只会在本地运行，不妨将它的回放安全性设定为“只访问本地文件”，这样无论是否在信任目录中，都可以载入本地的外部文件了。在Flash中可以通过“发布设置”进行设定：\n在Flash Builder/Flex Builder中，可以使用编译参数-use-network=false实现，如下图：\n","date":"2010-12-20","description":"","lastmod":"2010-12-20T06:57:10Z","slug":"1206","tags":["as3","flash","flashbuilder","flashplayer"],"title":"Flash Builder编译的swf为什么在bin-debug下运行正常，复制到其他文件夹就不正常？","url":"https://blog.zengrong.net/post/1206/"},{"categories":["technology"],"content":"**2014-03-20更新：**加入Yahoo! Astra 组件源文件下载\n在Flex或纯AcitonScript项目中使用Flash组件一文中，我介绍了在纯AS项目中使用FlashUI组件的方法，并提供了FlashUI.swc的下载。由于Flash提供的UI中缺少一些常用的组件（如Alert、Menu等），我将Yahoo! ASTRA Flash Components整合进入了FlashUI.swc中，以方便使用。整合进入的组件包括：\nUI类：\nAutoComplete、AlertManager、AudioPlayback、Carousel 、Form、Menu、MenuBar、TabBar、Tree\n布局类：\nHBoxPane、VBoxPane、FlowPane、TilePane、BorderPane\n图表类：\nBarChart、 ColumnChart、LineChart、PieChart\nASTRA组件的使用文档：http://developer.yahoo.com/flash/astra-flash/classreference/\n同时整合进入FlashUI.swc的，还有Flash自带的Video类组件。使用文档。\n所有整合的组件列表：\n整合后的swc文件命名为FlashUIPlus.swc。如果只希望使用基本的组件，也仍然可以使用原来的FlashUI.swc：\n1 文件 **2014-03-20 更新：**今天发现Yahoo的Flash开发者网站已经不能访问了，为了方便大家，我把收藏的Yahoo! Astra组件的源文件、文档等等全部共享了，请在下面下载：\n1 文件 1 文件 ","date":"2010-12-19","description":"","lastmod":"2010-12-19T12:25:30Z","slug":"1220","tags":["as3","component","flash","library","ui"],"title":"包含Alert、MenuBar等更多组件的Flash组件，可用于纯AS项目","url":"https://blog.zengrong.net/post/1220/"},{"categories":["technology"],"content":"在Flex中，界面中的各个组件可以随着浏览器的大小而重新排列位置，始终在浏览器中保持满屏显示的状态。我们将这种效果称为“布局”。\nFlex框架实现了一套自己的布局框架。在Flex3中，可以使用HBox、VBox等进行布局管理；在Flex4中，可以使用spark.layouts包中的布局管理器，这个包的布局功能更加强大和灵活。\n那么，在Flash或者纯AS项目中，如何实现布局呢？\n侦听舞台的Resize事件是解决这个问题的常用方法。但是如果布局比较复杂，就需要大量的代码来实现这些布局，使用侦听Resize事件的方法未免显得繁琐。于是，就有人实现了AS下的布局类或者布局框架。例如：senocular Layout class、Yahoo ASTRA Layout Utility等等。本文介绍的是BaseUI。\nBaseUI的中文资料较少，这一篇官方说明的译文，通过它大致可以了解BaseUI的工作方式和特点。本文则准备以实例的方式基于一个纯AS项目来介绍BaseUI的基本用法，这样更容易理解。\n一、BaseUI基本使用 在下面的这段代码中，建立了一个BaseUI的实例，然后绘制了一个shape并加入到显示列表中，将其置于舞台右下角，与舞台边框间隔10像素。\n几个注意点：\n必须设定stage的scaleMode为NO_SCALE，以及设定align为TOP_LEFT，否则布局可能会不正常； BaseUI必须基于stage，虽然基于root也能工作，但是可能会导致布局不正常； 将元素加入BaseUI并不等于加入显示列表，因此还需要将元素自行加入显示列表中 效果（在浏览器中查看swf或下载后使用Flash Player播放）：\n1 文件 源码：\n1[SWF(width=500,height=300,backgroundColor=0xcccccc)] 2public class BaseUITest extends Sprite 3{ 4 public function BaseUITest() 5 { 6 //绘制一个背景颜色以便于观察 7 graphics.beginFill(0xFFFFFF); 8 graphics.drawRect(0, 0, 500, 300); 9 graphics.endFill(); 10 //必须设置scaleMode和align，否则在布局的时候会不正常 11 stage.scaleMode = StageScaleMode.NO_SCALE; 12 stage.align = StageAlign.TOP_LEFT; 13 14 //绘制一个背景颜色以便于观察 15 graphics.beginFill(0xFFFFFF); 16 graphics.drawRect(0, 0, 500, 300); 17 graphics.endFill(); 18 //必须设置scaleMode和align，否则在布局的时候会不正常 19 stage.scaleMode = StageScaleMode.NO_SCALE; 20 stage.align = StageAlign.TOP_LEFT; 21 22 //基于stage建立一个布局 23 _baseUI = new BaseUI(this.stage); 24 //为布局添加一个布局元素 25 var __elementRB:ElementUI = _baseUI.add(createShape(this.stage)); 26 //设置该布局元素距右边和下方各10像素，并刷新布局使其生效 27 __elementRB.right = 10; 28 __elementRB.bottom = 10; 29 __elementRB.refresh(); 30 } 31 32 private var _baseUI:BaseUI; 33 34 //快速绘制一个Shape用于测试，如果绘制的时候提供了父显示对象，就将其加入父显示对象的显示列表 35 private function createShape($parent:DisplayObjectContainer=null):Shape 36 { 37 var __shape:Shape = new Shape(); 38 __shape.graphics.lineStyle(2, 0xFF0000); 39 __shape.graphics.drawRect(0, 0 , 30, 30); 40 if($parent) 41 $parent.addChild(__shape); 42 return __shape; 43 } 44} 二、可以使用几个BaseUI？ 最开始，我以为BaseUI是可以用于其它的显示容器的，于是就做了个实验，建立一个Sprite并加入到stage中，并基于这个Sprite建立一个BaseUI，再使用这个新的BaseUI来控制Sprite的子显示对象。但发现这样做出来的布局无法正常显示。当然，可能是由于我遗忘了某些东西或者使用方法不正确，但我还是建议对于子容器使用com.soma.ui.layouts包。\n三、HBoxUI的用法 HBox是一个横向布局容器，它继承自LayoutUI，而LayoutUI则继承自MovieClip（为什么是MovieClip而不是Sprite？这个我也不知道，我在源码中将LayoutUI的超类改为了Sprite，使用也一切正常）。\n下面这段代码中建立了一个HBox容器，并为它增加了6个子显示对象，通过对HBox属性的设定，可以控制子显示对象的间隔以及在HBox中的对其方式。\n几个注意点：\nbackgroundAlpha属性默认是完全透明，因此如果要显示背景色，就必须将其设定为大于0的值； 因为LayoutUI是显示对象，因此可以直接将HBoxUI的实例加入显示列表，而ElementUI是不能加入显示列表的； 要获取LayoutUI的对应ElementUI，可以使用element属性； 不需要将LayoutUI加入到BaseUI的实例中，但需要为LayouUI传递stage，如果传递其它的容器，则可能会导致布局问题。 效果（在浏览器中查看swf或下载后使用Flash Player播放）：\n1 文件 源码：\n1//增加一个HBox容器，设定背景色和透明度 2var __hbox:HBoxUI = new HBoxUI(this.stage); 3__hbox.backgroundColor = 0x0000FF; 4//透明度默认是完全透明，所以必须设定才能让背景色显示出来 5__hbox.backgroundAlpha = 0.2; 6//让该容器置顶，宽度始终与舞台相同 7__hbox.right = 0; 8__hbox.left = 0; 9__hbox.top = 0; 10//让容器中的子显示对象的横向间隔为10像素 11__hbox.childrenGap = new GapUI(10); 12//让容器中的子显示对象基于容器的右下方对齐 13__hbox.childrenAlign = HBoxUI.ALIGN_BOTTOM_RIGHT; 14//加入6个shape 15for(var i:int=0; i\u0026lt;6; i++) 16{ 17 createShape(__hbox); 18} 19//将容器加入显示列表并刷新容器布局 20addChild(__hbox); 21__hbox.refresh(); 四、CanvasUI的用法 使用HBoxUI和VBoxUI可以实现横向和纵向的布局。但我们有时候需要布局更灵活一些。例如希望3个显示对象分别排列在容器的左、中、右，如何处理呢？\n下面这段代码建立了一个CanvasUI容器，在容器中增加了3个shape，并实现左、中、右排列。\n效果（在浏览器中查看swf或下载后使用Flash Player播放）：\n1 文件 源码：\n1//增加一个CanvasUI容器 2var __canvas:CanvasUI = new CanvasUI(this.stage); 3__canvas.backgroundColor = 0xFF0000; 4__canvas.backgroundAlpha = .5; 5//让canvas在舞台中纵向居中，宽度与舞台宽度始终相同 6__canvas.verticalCenter = 0; 7__canvas.left = 0; 8__canvas.right = 0; 9//建立canvas的第1个子显示对象，在canvas中左下对齐 10var __shape1:Shape = createShape(); 11var __element1:ElementUI = __canvas.add(__shape1); 12__element1.left = 0; 13__element1.bottom = 0; 14//建立canvas的第2个子显示对象，在canvas中居中对齐 15var __shape2:Shape = createShape(); 16var __element2:ElementUI = __canvas.add(__shape2); 17__element2.horizontalCenter = 0; 18__element2.verticalCenter = 0; 19//建立canvas的第3个子显示对象，在canvas中右上对齐 20var __shape3:Shape = createShape(); 21var __element3:ElementUI = __canvas.add(__shape3); 22__element3.right =0; 23addChild(__canvas); 24__canvas.refresh(); 五、全部源代码 1package 2{ 3import com.soma.ui.BaseUI; 4import com.soma.ui.ElementUI; 5import com.soma.ui.layouts.CanvasUI; 6import com.soma.ui.layouts.HBoxUI; 7import com.soma.ui.vo.GapUI; 8 9import flash.display.DisplayObjectContainer; 10import flash.display.Shape; 11import flash.display.Sprite; 12import flash.display.StageAlign; 13import flash.display.StageScaleMode; 14import flash.events.Event; 15 16[SWF(width=500,height=300,backgroundColor=0xcccccc)] 17public class BaseUITest extends Sprite 18{ 19 public function BaseUITest() 20 { 21 //绘制一个背景颜色以便于观察 22 graphics.beginFill(0xFFFFFF); 23 graphics.drawRect(0, 0, 500, 300); 24 graphics.endFill(); 25 //必须设置scaleMode和align，否则在布局的时候会不正常 26 stage.scaleMode = StageScaleMode.NO_SCALE; 27 stage.align = StageAlign.TOP_LEFT; 28 29 //基于stage建立一个布局 30 _baseUI = new BaseUI(this.stage); 31 //为布局添加一个布局元素 32 var __elementRB:ElementUI = _baseUI.add(createShape(this.stage)); 33 //设置该布局元素距右边和下方各10像素，并刷新布局使其生效 34 __elementRB.right = 10; 35 __elementRB.bottom = 10; 36 __elementRB.refresh(); 37 38 //增加一个HBox容器，设定背景色和透明度 39 var __hbox:HBoxUI = new HBoxUI(this.stage); 40 __hbox.backgroundColor = 0x0000FF; 41 //透明度默认是完全透明，所以必须设定才能让背景色显示出来 42 __hbox.backgroundAlpha = 0.2; 43 //让该容器置顶，宽度始终与舞台相同 44 __hbox.right = 0; 45 __hbox.left = 0; 46 __hbox.top = 0; 47 //让容器中的子显示对象的横向间隔为10像素 48 __hbox.childrenGap = new GapUI(10); 49 //让容器中的子显示对象基于容器的右下方对齐 50 __hbox.childrenAlign = HBoxUI.ALIGN_BOTTOM_RIGHT; 51 //加入6个shape 52 for(var i:int=0; i\u0026lt;6; i++) 53 { 54 createShape(__hbox); 55 } 56 //将容器加入显示列表并刷新容器布局 57 addChild(__hbox); 58 __hbox.refresh(); 59 60 //增加一个CanvasUI容器 61 var __canvas:CanvasUI = new CanvasUI(this.stage); 62 __canvas.backgroundColor = 0xFF0000; 63 __canvas.backgroundAlpha = .5; 64 //让canvas在舞台中纵向居中，宽度与舞台宽度始终相同 65 __canvas.verticalCenter = 0; 66 __canvas.left = 0; 67 __canvas.right = 0; 68 //建立canvas的第1个子显示对象，在canvas中左下对齐 69 var __shape1:Shape = createShape(); 70 var __element1:ElementUI = __canvas.add(__shape1); 71 __element1.left = 0; 72 __element1.bottom = 0; 73 //建立canvas的第2个子显示对象，在canvas中居中对齐 74 var __shape2:Shape = createShape(); 75 var __element2:ElementUI = __canvas.add(__shape2); 76 __element2.horizontalCenter = 0; 77 __element2.verticalCenter = 0; 78 //建立canvas的第3个子显示对象，在canvas中右上对齐 79 var __shape3:Shape = createShape(); 80 var __element3:ElementUI = __canvas.add(__shape3); 81 __element3.right =0; 82 addChild(__canvas); 83 __canvas.refresh(); 84 85 this.stage.addEventListener(Event.RESIZE, handler_resize); 86 } 87 88 private var _baseUI:BaseUI; 89 90 //快速绘制一个Shape用于测试，如果绘制的时候提供了父显示对象，就将其加入父显示对象的显示列表 91 private function createShape($parent:DisplayObjectContainer=null):Shape 92 { 93 var __shape:Shape = new Shape(); 94 __shape.graphics.lineStyle(2, 0xFF0000); 95 __shape.graphics.drawRect(0, 0 , 30, 30); 96 if($parent) 97 $parent.addChild(__shape); 98 return __shape; 99 } 100 101 private function handler_resize(evt:Event):void 102 { 103 trace(stage.stageWidth, stage.stageHeight, stage.width, stage.height); 104 } 105} 106} ","date":"2010-12-19","description":"","lastmod":"2010-12-19T11:38:12Z","slug":"baseui-chinese-sample","tags":["as3","flex","layout"],"title":"BaseUI中文使用说明和范例","url":"https://blog.zengrong.net/post/baseui-chinese-sample/"},{"categories":["technology"],"content":"在BMPText与BMPSlicer——将位图切割成块来显示文字一文中，可以按照相同大小对大图片进行切片。但是如果图片的大小不同，就无能为力了。因此，我写了BMPDiverseSlicer，支持切块不同大小的图片。\n例如这张图片：\n使用方法：\n1package 2{ 3import flash.display.Bitmap; 4import flash.display.Sprite; 5 6import org.zengrong.display.BMPDiverseSlicer; 7 8[SWF(width=400,height=200)] 9public class BMPDiverseSlicerTest extends Sprite 10{ 11 public function BMPDiverseSlicerTest() 12 { 13 var bmpSlicer:BMPDiverseSlicer = new BMPDiverseSlicer(Bitmap(new CELL_CLASS()).bitmapData, [38,47,59,71,86], [42,53,66,80,96]); 14 for(var i:int=0; i\u0026lt;5; i++) 15 { 16 var __bmp:Bitmap = bmpSlicer.getSlicedBMP(i); 17 __bmp.x = i * 50 + i * i * 5; 18 __bmp.y = i * 20; 19 addChild(__bmp); 20 } 21 } 22 23 [Embed(source=\u0026#34;cell_ar.png\u0026#34;)] 24 private static const CELL_CLASS:Class; 25} 26} 编译效果：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2010-12-17","description":"","lastmod":"2010-12-17T07:04:52Z","slug":"bmpdiverseslicer-cut-bitmap","tags":["as3","bitmapdata"],"title":"BMPDiverseSlicer——将位图按照不同的大小进行切割","url":"https://blog.zengrong.net/post/bmpdiverseslicer-cut-bitmap/"},{"categories":["design"],"content":"因为矢量图的表现力毕竟有限，因此我们经常需要使用外部的图片来显示文字等信息，最多的情况就是显示数字和字母。\n但是，如果将每个数字或者字母做成一个图片，在程序运行过程中，就要载入许多的小文件，这样出错的几率就会变大，而且也会影响网络性能。因此，我写了两个类来处理这种情况。\n例如：要使用0-9的数字图片，可以将0-9拼在一张长条形的图片中，载入后使用 BMPSlicer 来切割，切割后，就可以使用 BMPText 来显示它。\n下图就是被切割的图片：\n程序效果演示：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n测试程序源码：\n1package 2{ 3import flash.display.Bitmap; 4import flash.display.Sprite; 5import flash.events.Event; 6import flash.events.TimerEvent; 7import flash.utils.Timer; 8 9import org.zengrong.display.BMPText; 10 11 12[SWF(width=200,height=100,backgroundColor=0xCCCCCC)] 13public class BMPTextTest extends Sprite 14{ 15 public function BMPTextTest() 16 { 17 _bmpText = new BMPText(\u0026#39;0123456789\u0026#39;, 46, 52, true, Bitmap(new _timerClass()).bitmapData); 18 _bmpText.gap = -10; 19 addChild(_bmpText); 20 _timer = new Timer(100, 99999); 21 _timer.addEventListener(TimerEvent.TIMER, handler_timer); 22 _timer.start(); 23 this.addEventListener(Event.ADDED_TO_STAGE, handler_addToStage); 24 } 25 26 [Embed(source=\u0026#34;timer.png\u0026#34;)] 27 private static var _timerClass:Class; 28 29 private var _bmpText:BMPText; 30 private var _timer:Timer; 31 32 private function handler_addToStage(evt:Event=null):void 33 { 34 _bmpText.x = stage.stageWidth/2 - _bmpText.width/2; 35 _bmpText.y = stage.stageHeight/2 - _bmpText.height/2; 36 } 37 38 private function handler_timer(evt:TimerEvent):void 39 { 40 _bmpText.text = _timer.currentCount.toString(); 41 handler_addToStage(); 42 } 43} 44} BMPText与BMPSlicer源码\n","date":"2010-12-09","description":"","lastmod":"2010-12-09T01:45:36Z","slug":"bmptext-and-bmpslicer","tags":["as3","bitmapdata"],"title":"BMPText与BMPSlicer——将位图切割成块来显示文字","url":"https://blog.zengrong.net/post/bmptext-and-bmpslicer/"},{"categories":["technology"],"content":"转自云の部族\n@see标签 @see标记的作用是生成一个参考引用。在一些情况下某些类、属性或者方法在其他地方有进行说明或者引用，这时候我们可以通过此标记来引用此例子来进行说明。其书写格式如下：\n@see 引用 [显示文本]\n为了更好的理解其含义，我将在原来的print方法中引入getString方法，然后getString方法中则采用@see标记来进行参考引用。\n1/** 2* 输出信息 3* @param firstParam 需要输出信息的对象 4* @param aaaaaaa 输出格式 5* @return 该函数无返回值 6* @example 下面例子是通过print函数输出信息。 7* 8* var i:int=1; 9* var demo:Demo=new Demo(); 10* demo.print(demo.getString(),\u0026#34;%s\u0026#34;); 11* * */ 12public function print(info:Object,format:String):void{ 13} 14/** 15* 返回一个字符串 16* @return 返回一个字符串 17* @see #print() 18**/ 19public function getString():String{ 20return \u0026#34;demo\u0026#34;; 21} 生成文档后，输出样式如下：\n如图所示，getString方法下多出了一栏See also的信息，在这栏里面就有刚才所写的print方法的引用。可能你会问@see标记中的引用部分应该怎么写呢？其实对于引用类内部方法来说是通过锚点来实现的，所以引用部分就是填写一个锚点（如果要引用到当页的锚点，学个HTML的朋友就知道是用#锚点名称）。其实用ASDoc生成的方法和属性都带有一个锚点的，其规律就是方法的锚点就是方法名称()（一定要加括号），属性的锚点就是属性名称。下图就是的状态栏中就有显示一个方法的锚点\n如图所示，Demo.html后面的就是锚点名称了。如果不知道的朋友可以通过生成一份文档来观察一下。那如果要引用其他类的方法呢？呆会再作演示，现在来看一下上例中的See also一栏下面的只是一个纯粹的方法名称，如果想要一些更加详细的说明，可以在应用部分加入提示性文字。我们把上面的例子改一下，代码如下：\n1/** 2* 输出信息 3* @param firstParam 需要输出信息的对象 4* @param aaaaaaa 输出格式 5* @return 该函数无返回值 6* @example 下面例子是通过print函数输出信息。 7* 8* var i:int=1; 9* var demo:Demo=new Demo(); 10* demo.print(demo.getString(),\u0026#34;%s\u0026#34;); 11* 12* */ 13public function print(info:Object,format:String):void{ 14} 15/** 16* 返回一个字符串 17* @return 返回一个字符串 18* @see #print() 具体用法请参考print方法 19**/ 20 public function getString():String{ 21 return \u0026#34;demo\u0026#34;; 22} 然后进行文档生成，效果如下图所示：\n可以看到引用部分不再是换成了我们添加的文本了。其中官方文档中还提到一个@see标记的参数是不能包含HTML格式的字符的。为了验证这个说法，我做了一下实验，把刚才的例子中的说明文字加上了一个 \u0026lt;b\u0026gt; 加粗字体的标记，如下所示：\n1/** 2* 返回一个字符串 3* @return 返回一个字符串 4* @see #print() 具体用法请参考print方法 5**/ 6public function getString():String{ 7 return \u0026#34;demo\u0026#34;; 8} 然后进行文档生成时出现错误了，其提示说不能使用HTML格式，如下图所示：\n对于如果一个类、方法或属性中有多个参考引用的地方我们可以使用多个@see来进行引用，这是ASDoc中所允许的。基本上就@see标记在类内引用就讲到这里，对于如何引用其他类的元素现在来通过例子说明一下。\n首先新建一个类，代码如下：\n1package{ 2 public class Demo2 extends Object{ 3 public function Demo2():void{ 4 } 5 6 public function getString():String{ 7 return \u0026#34;Demo2\u0026#34;; 8 } 9 } 10} 现在用Demo类中的getString方法来引用Demo2类中的getString方法。代码如下：\n1/** 2* 返回一个字符串 3* @return 返回一个字符串 4* @see #print() 具体用法请参考print方法 5* @see Demo2#getString() 6**/ 7public function getString():String{ 8 return \u0026#34;demo\u0026#34;; 9} 然后生成文档，效果如下图所示：\n可以看到Demo2的getString()方法被正确引用到了。根据笔者总结，对于@see标记的引用参数的写法应该是(分别对于类、方法和属性)：\n包路径.类名称 包路径.类名称#方法名称() 包路径.类名称#属性名称 如果是类内的方法则可以省略包路径.类名称部分。 ","date":"2010-12-07","description":"","lastmod":"2010-12-07T05:55:53Z","slug":"asdoc-see","tags":["as3","asdoc"],"title":"【转】探索ASDoc:标签篇-@see标签","url":"https://blog.zengrong.net/post/asdoc-see/"},{"categories":["technology"],"content":"首先看下面的错误代码：\n1public static const GLOBAL_INFO:String = \u0026#39;global\u0026#39;; 2_fightInfoQueue = {GLOBAL_INFO:[]}; 3_currentInfoQueue = _fightInfoQueue[GLOBAL_INFO]; 执行这段代码，_currentInfoQueue 的值应该会是一个数组么？不是，是 null 。\n通过调试器，可以看到 _fightInfoQueue 中的这个唯一的元素的键名是字符串 GLOBAL_INFO ，不是我认为的 global 。如果希望上面的 _currentInfoQueue 得到正确的值，必须使用下面的语法：\n1_currentInfoQueue = _fightInfoQueue.GLOBAL_INFO; 或者在定义的时候，使用下面的语法：\n1_fightInfoQueue = {\u0026#34;global\u0026#34;:[]}; 那么究竟是怎么回事呢？我又做了个测试：\n1var colors:Object = {RED:0xFF0000, GREEN:0x00FF00, BLUE:0x0000FF}; 2for(var i:String in colors) 3{ 4 trace(i + \u0026#39;:\u0026#39; + colors[i]); 5} 6trace(\u0026#39;===============\u0026#39;); 7trace(colors.RED); 8trace(colors.BLUE); 9trace(colors.GREEN); 在上面的代码中，RED、GREEN、BLUE都是没有定义的变量。\n执行后的效果：\nBLUE:255 RED:16711680 GREEN:65280 16711680 255 65280\n也就是说，如果使用没有定义的变量作为Object的键名，那么AS会自动将这个变量转换成为字符串。同时，也可以使用这个未定义的名称来引用Object中的值。\n不知是不是AS的BUG。\n","date":"2010-12-06","description":"","lastmod":"2010-12-06T05:02:20Z","slug":"object-key-name-maybe-undefined","tags":["as3"],"title":"Object的键名可以为未定义变量？","url":"https://blog.zengrong.net/post/object-key-name-maybe-undefined/"},{"categories":["technology"],"content":"2014-03-10更新：更新FlashCC自带的组件；\n2010-12-19更新：包含Alert、MenuBar等更多组件的Flash组件；\n2011-08-21更新：加入Flash CS 5.5 的UI组件编译的SWC，原因见正文。\n在纯AS环境下做开发的时候，往往需要一些简单的UI组件支持，例如按钮、复选框等等。这时候就需要选择或者自行开发一套常用的UI组件库，我在Flash\u0026amp;Flex大全中介绍了一些可以用于纯AS环境开发的第三方组件库，但一直没有介绍Flash的组件。现在看来，在纯AS项目中使用Flash的组件，至少有以下三个好处：\n1. 有[完善的中文文档](http://help.adobe.com/zh_CN/Flash/CS5/AS3LR/index.html)； 2. 有大量参考资料； 3. 有丰富的免费皮肤； 4. 生成的文件较小，只有使用了的组件才会被导出。 至于Flex，它已经有了完善且更加高级的组件库，而且也包含上面1-3条优点，所以采用Flex框架开发，当然还是用Flex组件比较好。这里只是提供一种方法罢了。\nFlash组件位于安装目录下的“Common\\Configuration\\Components\\”目录中（Flash CS3则在“zh_cn\\Configuration\\Components\\“）。其中“User Interface.fla”为源文件，只需要用Flash打开这个文件，在发布设置中选中“导出SWC”然后导出，就可以得到一个119KB的SWC文件。将其复制到AS项目的“libs”文件夹即可，也可以直接在Flash Builder中的AS项目的属性中设置“Actionscript构建路径-库路径-添加SWC文件夹/添加SWC“。\nFlash CS3/CS4/CS5所带的UI组件是完全一样的。UI组件的源码在“User Interface.fla”的库面板中的“Component Assets/_private/ComponentShim”中，这是一个编译剪辑，因此看不到源码。它们的真实源码在“Common\\Configuration\\Component Source\\ActionScript 3.0\\User Interface\\”文件夹中，完全由AS3写成。\n从CS5开始，由于增加了新的FTE文本引擎和TLF框架，Flash也提供了一个TLFTextField组件。相应的，UIScrollBar.scrollTarget也发生了改变。原来的类型是TextField，现在可以接受TextField和FTETextField。因此，原来提供的SWC中的UIScrollBar组件就不能支持FTETextField。因此这里提供了Flash CS 5.5版本的UI组件。\nFlashCS3提供的UI组件编译的SWC下载（不支持FTETextField）：\n1 文件 FlashCS5.5提供的UI组件编译的SWC下载（支持FTETextField）：\n1 文件 在纯AS3项目中使用，还需要这个SWC提供FTETextField支持：\n1 文件 2014-03-10 更新：\nRaymond 同学使用 FlashCC 编译了 几个SWC，放在这里提供给有需要的朋友： FlashUI_CC.swc 来自于 xxx\\Adobe\\Adobe FlashCC\\Common\\Configuration\\Components 目录下的User Interface.fla ，发布目标 是FlashPlayer10.3； Video 来自于同一个目录路径的Video.fla ，同样的发布参数； tlfRuntime_cc.swc 和 textLayout.swc 来自于 xxx\\Adobe\\Adobe FlashCC\\Common\\Configuration\\TLFConversion 目录下 Adobe 已经编译好的SWC文件。用于Text Layout Framework支持。 1 文件 ","date":"2010-12-05","description":"","lastmod":"2010-12-05T13:35:21Z","slug":"1192","tags":["component","flash","flashbuilder","flex","library","ui"],"title":"在Flex或纯AcitonScript项目中使用Flash组件","url":"https://blog.zengrong.net/post/1192/"},{"categories":["use"],"content":"2010-12-09更新：加入卸载工具\n一、Flash Player的版本 1.独立版和插件版\nFlash Player分为插件版和独立版，插件版安装后，让浏览器可以播放swf文件。独立版安装后，则可以直接在Windows中双击swf文件打开一个窗口来播放，不依赖浏览器。\n2.调试版和普通版\nFlash Player还有调试版（Debug版）和普通版，通过Adobe更新程序更新的均为普通版。调试版在swf发生运行时错误的时候会弹出对话框，而普通版不会。因此，游戏测试人员应该安装调试版。\n3.版本号\nFlash Player的版本号对程序的支持也有影响，如果没有特别的需求，应该使用最新的版本。目前最新版本为10.1。对AS3的提供支持的最低版本为9.0。\n如果一个使用了Flash Player 10.1提供的新功能的swf文件在Flash Player 9.0版本的Flash Player下播放，可能会播放不正常或者报错。\n如果一个在Flash 6下制作的swf在Flash Player 10.1下播放，也可能出现问题（恩，出问题后请记下错误号，然后用这个号码去买中国体育彩票）。\n4.下载最新的独立版和调试版\n访问 http://www.adobe.com/support/flashplayer/downloads.html\nActiveX control content debugger (for IE)为针对IE的调试插件版 Plugin content debugger (for Netscape-compatible browsers) 为针对其他浏览器（firefox、chrome、opera）的调试插件版 Projector content debugger为独立调试版 Projector为独立普通版 如果需要下载普通版，则访问 http://get.adobe.com/flashplayer，这个地址会根据你的浏览器自动下载对应的普通插件版。需要独立版请访问上面的地址。\n5.如果我下了一堆版本，但忘记哪个是XX版，哪个是无码版，怎么办？\n文件名中带有_sa_字样的为独立版播放器，否则为插件版。 文件名中带有_debug字样的为调试版，否则为非调试版。 文件名中带有_ax_字样的是针对IE浏览器的版本。 文件名中带有_plugin字样的针对其他浏览器版本。 二、关于独立Flash Player播放器 如果要播放swf文件，一定不要使用什么暴风影音，QQ影音、垃圾影音等软件，那些全都是浮云……\n正确的做法应该是：\n复制独立调试版Flash Player到你的C:\\Program Files文件夹（其他文件夹也行，建议是路径为全英文），双击运行一次，即可与swf文件自动关联（Win7/Vista用户右击，选择“以管理员身份运行”） 没有2，如果你一定要找2，那么你很2…… 三、如何分辨我正在使用的Flash Player是不是调试版？ 无论是独立版还是插件版，只需要在正在播放的swf内容上单击右键，如果右键菜单中有“调试器”字样，就说明你使用的是调试版的Flash Player。见图：\n四、如何判断我正在使用的Flash Player的版本？ 对于插件版，请访问http://www.flashplayerversion.com 网站，可以看到自己的使用的FlashPlayer版本号，还可以判断你的Flash Player是否为调试版（Debug版）\n对于独立版，在播放器exe文件上单击右键查看属性，可以看到版本号以及是否为Debug版本，Debugger值为1代表是Debug版，为0代表普通版。见图：\n五、我在chrome浏览器下安装了调试版的FlashPlayer插件，为什么通过 flashplayerversion 网站检测，仍然是普通版？版本号也不对？ 安装完后应该关闭所有的浏览器窗口并重启浏览器，或重启Windows，或直接按下机箱电源键5秒直至屏幕变黑，或直接拔下主机电源插头。 chrome自己维护一个最新版的Flash Player插件更新（没办法，Flash Player漏洞太多），需要禁用它自己维护的插件，方法如下： 在chrome地址栏中输入：chrome://plugins/ 展开右上角的“详细信息” 找到“Flash (2 files) - 版本： 10.1.85.3”类似字样，停用位置位于“:\\Documents and Settings\\Administrator\\Local Settings\\......”的一个，启用位置位于“C:\\WINDOWS\\system32\\Macromed\\Flash\\NPSWF32.dll”的那个。见图： 六、因为我是果粉，所以我觉得Flash Player超级稀烂，我要卸载它！ OK，虽然我讨厌Jobs，但我能理解你的心情，我相信Adobe也有同样的感受，所以他们提供了卸载工具：\nhttp://www.adobe.com/go/14157\n","date":"2010-12-03","description":"","lastmod":"2010-12-03T01:10:06Z","slug":"1188","tags":["flashplayer","study"],"title":"有史以来关于Flash Player的最详细说明","url":"https://blog.zengrong.net/post/1188/"},{"categories":["technology"],"content":"在Adobe新发布的Flex SDK预览版“HERO”中，终于看到了基于Spark的DataGrid和Form，终于可以结束在SDK4下面混合使用mx Form和Spark的TextInput的郁闷经历。\n除此之外，Hero还带来了以下新功能：\nSpark BitmapImage终于可以载入远程图像文件了（……肉牛满面），同时还提供“smoothingQuality”和“scaleMode”等新的功能；详情 Spark Image建立在BitmapImage的基础上，在载入图像失败的时候可以显示一个载入失败的图标；详情 Spark Formatters OSMF升级到1.0版本 TLF升级到下一个版本，这个版本增强了文本性能，加入了有序列表等新功能 增强编译器性能——减少内存占用、降低编译时间、改善RSL库编译逻辑等等（每次都这么说，但我怎么感觉越来越慢……） RSL——“非常令人激动的改进”，例如没有使用过的库不会包含在RSL中（本来就应该这样），纯Spark或者纯MX项目将保证只会将使用的组件编译到RSL中（本来就应该这样，但我确实很激动） 更详细的官方说明，看这里：http://www.adobe.com/devnet/flex/articles/introducing_flexsdk_hero.html\n","date":"2010-12-01","description":"","lastmod":"2010-12-01T02:30:50Z","slug":"1187","tags":["datagrid","flex","spark"],"title":"Spark的DataGrid终于来了—Flex SDK Hero新功能","url":"https://blog.zengrong.net/post/1187/"},{"categories":["technology"],"content":"看下面的代码：\n1var n1:Number = 0.7; 2var n2:Number = 0.4; 3trace(n1 - n2); 4//输出 0.29999999999999993 简单说，就是两个一位小数相减，差为何不是一位小数？\n经测试，乘法也有同样的问题。\n遛狗找到了Flex团队的一个回复（要翻墙）：http://old.nabble.com/Float-number-calculation-in-AS3-td18447329.html\n从回复看，AS3的Number与JAVA或C++的double类似，采用二进制分数而非十进制分数保存浮点部分，因此会导致不够精确。这是语言的设计思路所致。当然，从另一个思路看，它也是个BUG。\n或许AS3引入double和float类型后会解决这个问题吧……\nAS3's Number type, like Java's or C++'s 'double' type, store\nfloating-point values using binary fractions, not decimal fractions, so\nthere is some loss of precision occuring. There is no fractional-decimal\ntype in AS3.\nI'll leave it to folks in the developer community working on financial\napplications to explain how they work around this limitation.\nGordon Smith\nAdobe Flex SDK Team\n","date":"2010-11-30","description":"","lastmod":"2010-11-30T03:43:12Z","slug":"actionscript3-number-precision","tags":["as3"],"title":"Actionscript3的Number类型计算精度问题","url":"https://blog.zengrong.net/post/actionscript3-number-precision/"},{"categories":["technology"],"content":"转自云の部族\n@example标记 @example是可以为某个类、方法或者属性加一个说明性的例子，从而让自己的代码更加容易理解。其书写格式为：\n1@example 例子的说明性文字 \u0026lt;listing version=”3.0”\u0026gt; 例子的代码 \u0026lt;/listing\u0026gt; 从格式中可知，例子的代码是写在 \u0026lt;listing\u0026gt; 标记之中的，下面通过一个例子来说明一下，还是以print函数为例：\n1/** 2* 输出信息 3* @param firstParam 需要输出信息的对象 4* @param aaaaaaa 输出格式 5* @example 下面例子是通过print函数输出信息。 6* 7* var i:int=1; 8* var demo:Demo=new Demo(); 9* demo.print(i,\u0026#34;%d\u0026#34;); 10* 11* */ 12public function print(info:Object,format:String):void 13{ 14} 其输出格式为：\n从上图可见，在@example后面的文字输出在Example的下面，此文字是用来对例子的一个说明。然后写在 \u0026lt;listing\u0026gt; 标记中的代码就放在一个灰色的矩形框中。根据官方的帮助说明，在个框是带水平滚动条的，所以当内容超出一定长度后就会显示水平的滚动条。笔者对此也作出了验证，发现确实能够出现水平滚动条，至于高度则由内容的高度决定，但不会出现垂直滚动条。如下图所示：\n@includeExample标记 此标记是引入一个外部示例文本到ASDoc输出中。ASDoc将从由ASDoc工具的-examplespath参数指定的基于类的包名或者目录来搜索示例文件。\n例如，你将示例路径参数设置为 c:\\eamples 。然后为 mx.controls.Button 类添加一个示例。他发生在 c:\\exmples 目录下的 mx\\controls\\directory 里面，即是 c:\\examples\\mx\\controls directory。你可以进一步用 @includeExample 标记来指定文件的位置。例如，你指定的 @includeExample 标记如下所示：\n1@includeExample buttonExample/ButtonExample.mxml ASDoc将从 c:\\examples\\mx\\controls\\buttonExample 目录下进行查找此示例。如果在一个类注释中插入此标记，这个例子将显示在输出的HTML文档的最后面，如果你在一个类的元素中插入此标记，那么示例将出现在该元素的详细说明中。\n其书写格式如下：\n1@includeExample 示例文件路径 下面来通过示例看一下生成的文档样式如何，先建立一个示例文件example.txt，然后在SubDemo中运用@includeExample来引入此文件，代码如下：\nexample.txt文件内容\n1//这是一个例子文件 2var demo:Demo=new Demo(); SubDemo类\n1package{ 2 /** 3 * Demo的子类 4 * @internal 这是一个内部文本，不在文档中显示的。 5 * @includeExample example.txt 6 * */ 7 public class SubDemo extends Demo{ 8 public function SubDemo(){ 9 } 10 /** 11 * @inheritDoc 12 * @throws Exception 异常 13 **/ 14 override public function getString():String{ 15 return \u0026#34;sub\u0026#34;; 16 } 17 } 18} 上面步骤完成后，接下来要生成文档了，这时候要注意的是，asdoc中要指定一下-examples-path这个参数，如果不指定的话就会提示找不到外部示例文件。最终命令行内容如下：\n1asdoc -source-path . -doc-classes Demo Demo2 fi.events.DemoEvent SubDemo Exception -output doc/ -examples-path . 生成后效果如下图所示：\n@exampleText标记 此标记是使用在一个通过@includeExample标记引入外部的示例文件中。其书写格式如下：\n@exampleText 说明文本\n通过此标记可以在例子的前面或者后面加上一个对例子的注释说明。其中外部例子是支持例子前后加注释的，所以标记也限定了加入ASDoc注释的位置，只能是例子的第一行前面或者例子的最后一行后面。如果加入到中间的话，那么后面的所有文本都会被ASDoc所丢弃。\n下面的例子来说一下其用法，首先在原来@includeExample的例子基础上往example.txt加上注释代码，如下所示：\n1/** 2* @exampleText 这是一个例子文件 3**/ 4//这是一个例子文件 5var demo:Demo=new Demo(); 6demo.getString(); 然后生成文档，效果如下图所示：\n","date":"2010-11-25","description":"","lastmod":"2010-11-25T04:01:53Z","slug":"asdoc-example-includeexample-exampletext","tags":["as3","asdoc"],"title":"【转】探索ASDoc:标签篇-@example|@includeExample|@exampleText标签","url":"https://blog.zengrong.net/post/asdoc-example-includeexample-exampletext/"},{"categories":["technology"],"content":"本文转自：云の部族\nASDoc是adobe官方提供的ActionScript的API文档生成工具，现在已经集成在FlexBuilder3中。笔者这段时间才刚刚接触到这个工具，所以在网站也搜索了一些资料来对这个工具作进一步的了解。不过中文的资料对此工具的介绍和使用也不是太多，经过我几天的努力，对一些国外资料的研究和总结写了以下这篇文章，这篇文章主要是对ASDoc在注释中所使用的标签作了一些深入的研究，现在把我在探索的这个过程中的一些心得分享给大家。\n首先在这里要先介绍一下API文档生成形式格式和结构，为了了解ASDoc的生成形式，在第一个例子中将不采用任何ASDoc标记来注释类。类定义如下：\n1package{ 2 public class Demo{ 3 private static const const_1:int; 4 protected static const const_2:String; 5 public static const const_3:Boolean; 6 7 private static var static_private_variable:int; 8 protected static var static_protected_variable:String; 9 public static var static_public_variable:Boolean; 10 11 private static function static_private_method():void{ 12 } 13 protected static function static_protected_method():void{ 14 } 15 public static function static_public_method():void{ 16 } 17 18 private var private_variable:int; 19 protected var protected_vairable:String; 20 public var public_variable:Boolean; 21 22 public function Demo(){ 23 } 24 25 public function public_method():void{ 26 } 27 28 protected function protected_method():void{ 29 } 30 31 private function private_method():void{ 32 } 33 } 34} 其包含了所有类相关的定义，运行ASDoc来生成此类文档（在输入命令使需要注意 -source-path 如果不在当前目录时需要自己指定，而且必须使用 -doc-classes 、-doc-namespaces 或者 -doc-sources 来指定那些类、名称空间或者源码）。\n笔者发现生成后的文档静态公有的属性会和公有属性组织在一起，而静态受保护属性会和受保护属性组织在一起；方法也如此。同时私有成员是不会体现在文档之中。其实，这些可以根据Flex的帮助文档就可以知道了，为了有一个好的开始，还是先进行了一下这样的测试。接着下来将逐步介绍ASDoc标记的用法以及一些ASDoc的参数使用。\n首先，我们一般会对类文件的类和成员以及成员函数做一些解析性说明。那么这个解析性说明应该怎么写呢？如果想给指定的类、成员属性、成员函数加上注释，可以在这些声明的顶部按照下面的格式属性注释：\n1/** 2 * 注释内容 3 * */ 这样我们在进行一次文档生成操作后，会发现你所添加的注释会在响应的类、属性或者方法下面多出一行说明文字。关于注释的内容可以为任意字符，甚至可以搭配HTML标记来修饰注释内容。（其输出的是HTML当然可以用HTML标记来描述了，呵呵），说完最常用的注释后，接下来说一下被ASDoc所能解析的标记，下面将逐一进行探讨。\n@param标签 @param标记是为带参数的函数中的参数作注释用的标记。通过此标记可以生成对应的参数的说明。此标记的书写格式如下：\n@param 参数名称 参数说明\n从书写格式可以看出来，一个这样的标记仅对应一个方法中的一个参数。如果一个方法中包含多个参数可以用多个@param来进行说明。\n现在我们来为Demo写一个函数print，然后我们来生成文档看一下文档格式如何，其中函数定义如下：\n1/** 2* 输出信息 3* @param info 需要输出信息的对象 4* */ 5public function print(info:Object):void 6{ 7} 在命令行输入：\nD:\\study\\asdoc\u0026gt;asdoc -doc-classes Demo -output doc\\ -source-path .\n其输出格式为：\n从例子中可以看出来，在@param标记中写入的内容会被写入Parameters栏中。在官方文档中对@param标记的功能还提到了一点就是，写入的参数名称必须要对应方法签名中的参数名称。也就是说如果有两个参数，必须要按照定义的参数顺序来进行注释。笔者做了一个尝试，就是给一个带两个参数的方法注释时不按照签名定义来进行注释，发现注释变得混乱。同时，也尝试过把@param的paramName部分随便填上一些字符，但是输出的参数名称依旧会按照方法中的参数名称来显示。所以，可以得出一个结论就是paramName可以为任意名称，但注释信息必须要对应签名顺序中的参数，这样ASDoc也能为你把参数名称正确地分配到正确的注释中。如下面例子所示：\n1/** 2* 输出信息 3* @param firstParam 需要输出信息的对象 4* @param secondParam 输出格式 5* */ 6public function print(info:Object,format:String):void 7{ 8} 其输出格式是：\n当然，笔者不赞成乱写参数名称的办法，因为这样会养成不好的习惯，同时也会造成程序的可读性降低。所以，应该对应参数名称来写入参数注释。\n","date":"2010-11-25","description":"","lastmod":"2010-11-25T01:43:31Z","slug":"asdoc-param","tags":["as3","asdoc"],"title":"【转】探索ASDoc:标签篇-@param标签","url":"https://blog.zengrong.net/post/asdoc-param/"},{"categories":["technology"],"content":"在游戏制作的过程中，经常需要将图片资源外置，因此我写了一个 VisualLoader 类用于批量载入皮肤。该类可以载入外部的图片文件或者swf文件，如果载入的是图片文件就将其转换为Bmp对象，如果载入的是swf文件，就利用反射获取库中导出的Symbol的Class，然后使用new操作符建立相应的对象。\n实际上，如果载入的是swf，则获取的不仅仅限于显示对象，只要在Flash软件的库中可以“导出”的类型都可以获取。例如Font、MovieClip、Sound、SimpleButton等等。关于具体的实现方式，还可以参考这篇文章： 使用Loader/URLLoader/URLStream载入外部swf并获取类定义 。\n这个类写好后，在实际使用的时候出了问题。地图配置中明明有18个使用了外部图片的对象，但显示出来却只有6个！一开始百思不得其解，做了许多调试工作。后来才想通，原来自己犯了个超级低级的错误！\n在载入后，我保存的是对象而不是类，这样无论将该对象加入几次显示列表，对象还是原来的那个对象。对于swf中的显示对象来说很简单，只需要使用VisualLoader的getClass方法获取该显示对象的Class，每次建立一个Class的实例即可。\n但是对于载入的外部图片，则无法通过反射获取图片的类。这就需要新建一个Bitmap对象，然后将外部载入的图像的bitmapData属性赋予它。看下面这个duplicateBMP方法：\n1private function duplicateBMP($bmp:Bitmap):Bitmap 2{ 3 return new Bitmap($bmp.bitmapData, PixelSnapping.AUTO, true); 4} 也可以使用BitmapData的draw方法来复制：\n1private function duplicateBMP($bmp:Bitmap):Bitmap 2{ 3 var __bmpData:BitmapData = new BitmapData($bmp.width, $bmp.height, true, 0x00FFFFFF); 4 __bmpData.draw($bmp); 5 return new Bitmap(__bmpData, PixelSnapping.AUTO, true); 6} 使用第二种方法要注意，如果复制的图片是支持Alpha透明的，那么BitmapData构造函数的第三个参数就必须填写。如果采用默认值，复制出来的图片就是不透明的。\n第三个参数填写的值，应该是下面的格式，其中大写的X代表任意0-F的十六进制数：\n10x00XXXXXX 这个格式与我们常用的6位的颜色格式不太相同，一共有8位。0x后面紧接的00就是代表Alpha透明度的。\n当然，如果只是用 0x00 或者 0x000000 也可以实现被复制的位图的透明，但是0x后面的紧接着的两个数字一定不能是除了00之外的其它数字。\n","date":"2010-11-22","description":"","lastmod":"2010-11-22T15:06:54Z","slug":"reuse-loded-picture-and-swf-display-object","tags":["as3","bitmapdata","loader","reflection"],"title":"外部载入的图片和SWF中的显示对象重用方法","url":"https://blog.zengrong.net/post/reuse-loded-picture-and-swf-display-object/"},{"categories":["use"],"content":"用XML写配置文件写得头痛，分析起来也头痛，干脆用JSON写。但是JSON纯用手写比较麻烦，于是网上一搜，居然发现一堆好用的编辑器，在这里记录一下。\nhttp://jsoneditor.net/\n除了在线版，还有AIR版本的离线版 JSONView\n有Firefor插件版、还有chrome插件版 eclipsejsonedit\nEclipse的编辑器插件 jsoneditor\n另一个在线编辑器 http://jsoneditor.appspot.com/ http://jsonformat.com ","date":"2010-11-18","description":"","lastmod":"2010-11-18T04:18:28Z","slug":"json-editor-and-viewer","tags":["json"],"title":"JSON编辑器和查看器","url":"https://blog.zengrong.net/post/json-editor-and-viewer/"},{"categories":["technology"],"content":"2011-07-01更新：smithfox提醒，这个BUG在Flex SDK4.5/Flash Player 10.3中已经解决（不知道是从什么时候解决的，只能把我发现时候的版本贴出来），Adobe解决的办法也比较BT，就是不带括号调用方法名，不会执行方法。这样的话，以前偷懒的代码都要改过了。\nA类需要调用B类中的showInfo方法，使用setShowInfo方法将被调用的方法保存在A类的 _showInfo 变量中，然后在调用该方法的时候判断该方法是否被设置，如果被设置就调用它。代码如下：\n1private var _showInfo:Function; 2public function setShowInfo($showInfo:Function):void 3{ 4 _showInfo = $showInfo; 5} 6protected function showInfo($msg:String):void 7{ 8 trace(\u0026#39;_showInfo is not null:\u0026#39;, _showInfo != null); 9 if(_showInfo != null) 10 _showInfo($msg); 11} 但这个代码有很大的问题，trace出来的内容居然是：\n_showInfo is not null:false\n想了想也对，因为 _showInfo 是方法，对方法进行调用（即使不带括号也可以调用），方法是有返回值的，而这个方法的并没有设定返回值（设定为void），也就是说返回值为空，这样就导致了即使_showInfo被设置， _showInfo != null 表达式也会返回空。\n至于解决办法，也很简单（setShowInfo就省略了）：\n1private var _showInfo:Function; 2protected function showInfo($msg:String):void 3{ 4 trace(\u0026#39;_showInfo is Function:\u0026#39;, _showInfo is Function); 5 if(_showInfo is Function) 6 _showInfo($msg); 7} ","date":"2010-11-09","description":"","lastmod":"2010-11-09T01:59:51Z","slug":"test-if-function-variable-be-set","tags":["as3"],"title":"如何测试一个Function变量是否被设置？","url":"https://blog.zengrong.net/post/test-if-function-variable-be-set/"},{"categories":["technology"],"content":"在游戏开发的过程中，经常需要将游戏资源放在外部swf中。本文讨论的就是在使用AS3载入外部的资源swf，并将其中的资源转换为类定义时要注意的一些东西。\n我建立了两个swf，一个为LoadAppDomain.swf，一个为B.swf，用前者载入后者。后者定义在sub包中，并使用draw API绘制了一个红色圆形，同时定义了一个公共的bTrace方法。B.as代码如下：\n1package sub 2{ 3 import flash.display.Sprite; 4 import flash.events.Event; 5 6 public class B extends Sprite 7 { 8 public function B() 9 { 10 trace(\u0026#39;B init\u0026#39;); 11 graphics.beginFill(0xff0000); 12 graphics.drawCircle(100,100, 50); 13 graphics.endFill(); 14 this.addEventListener(Event.ADDED_TO_STAGE, function():void{trace(\u0026#39;B add to stage\u0026#39;);}); 15 } 16 17 public function bTrace():void 18 { 19 trace(\u0026#39;B trace\u0026#39;); 20 trace(\u0026#39;B.stage:\u0026#39;, stage); 21 } 22 } 23} 在LoadAppDomain.as的构造函数中中，分别建立了Loader、URLLoader和URLStream的实例，并将它们的Complete事件指向同一个处理函数，如下：\n1var __request:URLRequest = new URLRequest(\u0026#39;B.swf\u0026#39;); 2trace(\u0026#39;LoadAppDomain init\u0026#39;); 3this.addEventListener(Event.ADDED_TO_STAGE, function():void{trace(\u0026#39;LoadAppDomain added to stage\u0026#39;);}); 4 5_loader = new Loader(); 6_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, complete_handler); 7_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progress_handler); 8//_loader.load(__request); 9 10_urlStream = new URLStream(); 11_urlStream.addEventListener(HTTPStatusEvent.HTTP_STATUS, http_statusHandler); 12_urlStream.addEventListener(Event.COMPLETE, complete_handler); 13_urlStream.addEventListener(ProgressEvent.PROGRESS, progress_handler); 14//_urlStream.load(__request); 15 16_urlLoader = new URLLoader(); 17_urlLoader.dataFormat = URLLoaderDataFormat.BINARY; 18_urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, http_statusHandler); 19_urlLoader.addEventListener(Event.COMPLETE, complete_handler); 20_urlLoader.addEventListener(ProgressEvent.PROGRESS, progress_handler); 21_urlLoader.load(__request); 这三个对象对于载入完成后的处理是不同的。其中Loader的处理最为直接。\n1.如果只是需要将B.swf显示出来，则直接将_loader.content加入显示列表即可。同时也可以执行bTrace方法，在B的构造函数中的trace语句，会在complete完成的时候自动执行，无论我们是否将其加入了显示列表。而只有将_loader.content加入了显示列表后，B的stage才不为空。\n2.如果需要获取载入的swf中的类定义，继续往下读。\n1private function complete_handler(evt:Event):void 2{ 3 addChild(_loader.content); 4 Object(_loader.content).bTrace(); 5} 如果使用URLLoader载入外部swf，则需要使用_urlLoader.dataFormat语句将载入的值设定为二进制格式，待载入完毕后，再使用_loader.loadBytes载入二进制数据。\n1private function complete_handler(evt:Event):void 2{ 3 _loader.loadBytes(_urlLoader.data as ByteArray); 4} 如果使用URLStream载入外部swf则没有这个限制，URLStream默认载入的就是二进制数据流。在载入完成后，可以使用readBytes方法swf的二进制数据读入一个ByteArray对象中，然后再交给_loader.loadBytes载入。\n1private function complete_handler(evt:Event):void 2{ 3 var __bytes:ByteArray = new ByteArray(); 4 _urlStream.readBytes(__bytes); 5 _loader.loadBytes(__bytes); 6} 在 _loader.loadBytes 完毕后，可以使用ApplicationDomain（应用程序域）对象的getDefinition方法获取类定义。如何使用ApplicationDomain，取决于载入swf的时候，将其放置于哪个应用程序域。详见 使用应用程序域 。\n1. 在使用 _loader.loadBytes 的时候，如果没有提供第二个参数，或者第二个参数为空，则默认将其载入自己的应用程序域，这样在获取类定义的时候，就需要使用下面的方法来获取类定义：\n1var __B:Class = _loader.contentLoaderInfo.applicationDomain.getDefinition(\u0026#39;sub.B\u0026#39;) as Class; 2. 在使用 _loader.loadBytes 的时候，提供第二个参数，并将其设置成为当前域(ApplicationDomain.currentDomain)：\n1var __context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain); 2_loader.loadBytes(__bytes ); 那么，就需要用下面的方法来获取类定义：\n1var __B:Class = ApplicationDomain.currentDomain.getDefinition(\u0026#39;sub.B\u0026#39;) as Class; 获取类定义后，就可以实例化，并调用实例的方法。如果B与LoadAppDomain在同一个项目中，要注意不要在LoadAppDomian中出现B的定义，否则编译器会将B也编译进入LoadAppDomain中，会无谓的增加文件大小。\n1Object(_loader.content).bTrace(); 2var __b2:* = new __B(); 3addChild(__b2); 4__b2.bTrace(); 5trace(__b2.stage == stage); 通过这个例子，也可以发现，被载入的swf在没有加入显示列表前是没有stage的，也就是说，被载入的swf使用的是载入它的swf的stage。在一个Flash Player中，只有一个stage存在。\n下载项目源码：\n1 文件 ","date":"2010-10-05","description":"","lastmod":"2010-10-05T06:17:26Z","slug":"loader-urlloader-urlstrent-swf","tags":["as3","loader","reflection"],"title":"使用Loader/URLLoader/URLStream载入外部swf并获取类定义","url":"https://blog.zengrong.net/post/loader-urlloader-urlstrent-swf/"},{"categories":["technology"],"content":"转自：http://bbs.9ria.com/viewthread.php?tid=63977\nTweenLite参数说明 TweenMAX参数说明 TweenFilterLite参数说明 Tween缓冲 Tween缓冲大家应该都不陌生，说白了就是从一起始位置逐渐移动到目标位置的过程，这个过程可以是加速移动，也可以是减速移动，这些不同的缓动方式就是Tween的各种ease。\nTween算法 概念知道了，了解一下Tween的算法对我们更好的使用Tween或者编写自己的Tween都是很有帮助的。\n在缓动过程中，随着时间的推移，对象从起始位置开始逐渐向目标位置移动，我们假设移动的距离为disX，并将其作为y轴，时间t作为x轴，这样可以轻松的得到一条曲线，如下图：\n在上图中我取了三个时间点ta、tb和tc，他们对于的移动的距离分别是da、db和dc，其中ta到tb与tb到tc之间的时间相同，但是很明显的可以看到db-da比dc-db小，也就是说相同的时间内，时间越靠后，对象移动的越快，实际上着就是Tween中的Cubic缓动模式，你可以在下面的SWF中看到它的效果。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n如果曲线是下面这种情况，你觉得缓动应该是什么样子的呢？\n在开始部分的SWF的舞台中点击，试着找到这条曲线，看到了吗？上面的方块像是一个篮球掉在地上一样，反弹了几下然后停止在目标位置，这是Tween种的Bounce缓冲效果。Tween中还有很多很好玩的缓冲模式，你可以在TweenLite开源类库中找到这些缓冲模式。\n效果是看到了,但是缓动是怎么计算的呢?别着急，听我们慢慢给你细说。\n首先我先声明几个变量：\ntime：缓动经历过的时间 beforeMove：起始位置 changeDistance：起始位置与目标位置的距离,也就是距离上的一个变化量 duration：我们要求对象从起始位置移动到目标位置所需的时间，也就是缓动的总时长 我们假设对象是匀速像目标位置移动，那么根据这四个变量可以轻松的计算出对象在经历time时间后应处的位置pos，公式如下：\n1pos = changeDistance * ( time/duration ) + beforeMove 能看明白上面的公式吗？在经历的time时间后，用changeDistance乘以这个时间time占缓动总时长duration的比例，可以得到移动的距离，再加上初始位置便是对象实际应该处的位置。 请你试着代开TweenLite开源类中的Linear类，你会看到下面的代码：\n1package gs.easing { 2public class Linear { 3\tpublic static function easeNone (t:Number, b:Number, c:Number, d:Number):Number 4\t{ 5\treturn c*t/d + b; 6\t} 7\tpublic static function easeIn (t:Number, b:Number, c:Number, d:Number):Number 8\t{ 9\treturn c*t/d + b; 10\t} 11\tpublic static function easeOut (t:Number, b:Number, c:Number, d:Number):Number 12\t{ 13\treturn c*t/d + b; 14\t} 15\tpublic static function easeInOut (t:Number, b:Number, c:Number, d:Number):Number 16\t{ 17\treturn c*t/d + b; 18\t} 19} 20} 是不是很眼熟？是的，就是刚刚我讲过的那个公式，这就是TweenLite中Linear缓动模式的算法，你可以试着打开其他缓动模式类看一下它们的源码，虽然easeIn、easeOut和easeInOut的算法不同，但是它们都包含了这四个参数：t（time）、b（beforeMove）、c（changeDistance）及d（duration）。\n但是在TweenLite计算缓动时，并不是直接将起始位置和起始与目标位置的距离作为参数传递给easeIn函数，因为TweenLite不只是缓动坐标，还包括对象的宽高，透明度等等，所以实际上TweenLite是将这两个数值分别用0和1代替，得到经历时间与缓动时间长的比例factor，然后再用factor应用到对象属性的缓动上。\n1factor = Linear.easeIn ( t,0,1,d); 2object.para = changeValue * factor + beforeValue; 理解了缓动的计算原理，你是不是可以运用大学里学的曲线方程写出自己的缓动模式呢？！\n","date":"2010-10-04","description":"","lastmod":"2010-10-04T15:48:16Z","slug":"tween-ease","tags":["actionscript","tween","as3"],"title":"【转】原来Tween缓冲是这么回事","url":"https://blog.zengrong.net/post/tween-ease/"},{"categories":["use"],"content":"今天下载了一堆C的书籍，准备深入学习一下。却发现这些书里面都没有讲解如何配置学习环境。无奈又google了一堆资料，选择了Dev-C++这个IDE，敲了Hello World代码，默认保存成C++源码（扩展名cpp），编译运行均正常。可是如果将其保存为C源码（扩展名c），在编译的时候，虽然也没有任何出错提示，但是却无法找到编译出来的exe文件。\n下面是解决步骤：\n操作系统：Windows 7 旗舰版\nDev-C++版本：5.0.0.0（下载地址：http://sourceforge.net/p/devcpp/home/）\n先需要说说的是版本问题。Dev-C++从2005年起就停止维护了，最终版为4.9.9.2。我下载的这个5.0.0.0版本，是另有人在原来的基础上重新开始维护而生成的。注意下面两个地址在项目名称上的区别，一个是dev-cpp，一个是devcpp：\nDev-C++ 4.9.9.2 http://sourceforge.net/projects/dev-cpp/files/\nDev-C++ 5.0.0.0 http://sourceforge.net/p/devcpp/home/\n网上有些资料说出现这个问题的原因，是因为Dev-C++安装程序无法将路径信息写入Vista系统（网上出现这问题的多为Vista系统）的PATH中，需要添加路径，并配置所有的命令行工具的路径为绝对路径。可我照做后发现，出现了错误提示：\ncannot exec `cc1': No such file or directory\n这明显是在编译的时候找不到cc1这个程序导致。仔细检查，发现网上教程中使用的mingw32版本为3.4.2，而我的版本是 3.4.5，在\\Dev-Cpp\\libexec\\gcc\\mingw32\\3.4.5\\文件夹下，没有cc1.exe，只有一个cc1plus.exe。再次搜索，发现cc1plus.exe是用来编译C++源码的。于是试着复制了一份cc1plus.exe，改名为cc1.exe，编译c源码成功！\n到这里就真相大白了，原来这个 5.0.0.0版本没有提供cc1.exe，而只是提供了cc1plus.exe，因此编译C++源码没问题，但编译C源码就不行（但也不报错）。上面我采用复制cc1plus并改名的方法只是权宜之计，下面是正确的解决方法（任选其一）：\n1.下载一份3.4.5版本的cc1.exe；\n2.下载最新的MinGW，然后在Dev-C++中新增一个编译器配置，将其设置为使用MinGW编译。\n关于MinGW是什么，可以看看这篇文章，如果想更深入的了解Dev-C++的使用与配置，这一系列的文章绝对不能错过。\n","date":"2010-09-23","description":"","lastmod":"2010-09-23T14:39:29Z","slug":"1150","tags":["c"],"title":"在Dev-C++中无法将C源码编译成exe，提示“源文件未编译”的解决办法","url":"https://blog.zengrong.net/post/1150/"},{"categories":["use"],"content":"我一直在寻找一套在纯AS环境下使用的组件库（如果你也在找，可以看看这里），曾经使用过一段时间ASWing，最终还是放弃了，后来就看到这套小巧的库。Minimal Comps是由业界大牛Keith Peter编写的一套轻量级纯AS组件库，用来写一些测试用的程序是再方便不过了，整个架构也非常清晰明了，一些没有提供的功能，自己也可以直接来修改源码实现。下面就简单说说这套组件的用法。\n网上有些资料说这套组件是不支持中文的，其实不然。默认不能显示中文的原因主要是组件默认使用了嵌入字体。如果希望组件支持中文，将Style.embedFonts设置为false即可。其他的一些默认设置，例如字体、文字大小和颜色、组件背景色等等，都可以通过设置Style这个静态类的属性来实现。对于中文用户来说，还可以取消Components类中的嵌入字体，这样可以进一步减小最终文件的大小，要取消字体嵌入，在Components中搜索下面两句并注释掉即可。\n1// Flex 4.x sdk: 2//[Embed(source=\u0026#34;/assets/pf_ronda_seven.ttf\u0026#34;, embedAsCFF=\u0026#34;false\u0026#34;, fontName=\u0026#34;PF Ronda Seven\u0026#34;, mimeType=\u0026#34;application/x-font\u0026#34;)] 3// Flex 3.x sdk: 4//[Embed(source=\u0026#34;/assets/pf_ronda_seven.ttf\u0026#34;, fontName=\u0026#34;PF Ronda Seven\u0026#34;, mimeType=\u0026#34;application/x-font\u0026#34;)] Minimal Comps组件非常小，Keith Peter用该组件制作了一个站点，该站点只有一个swf文件，大小为43K，同时还制作了一个Designer，这个更小，约26K。\n下面给出一个中文使用范例：\n1package 2{ 3 import com.bit101.components.PushButton; 4 import com.bit101.components.Style; 5 6 import flash.display.Sprite; 7 8 public class MinimalComps extends Sprite 9 { 10 private var _btn:PushButton; 11 public function MinimalComps() 12 { 13 Style.fontSize = 12; 14 Style.embedFonts = false; 15 Style.fontName = \u0026#39;Microsoft YaHei\u0026#39;; 16 Style.BACKGROUND = 0x000000; 17 Style.BUTTON_FACE = 0xCCCCCC; 18 _btn = new PushButton(this, 100, 100, \u0026#39;测试\u0026#39;); 19 _btn.setSize(100, 25); 20// _btn.enabled = false; 21 } 22 } 23} ","date":"2010-09-21","description":"","lastmod":"2010-09-21T14:21:26Z","slug":"minimalcomps-intro","tags":["as3","library","ui"],"title":"MinimalComps简介－一个超轻量级的纯AS组件库","url":"https://blog.zengrong.net/post/minimalcomps-intro/"},{"categories":["technology"],"content":"Alcon、De MonsterDebugger、Arthropod都是基于AIR的Debugger工具。今天有时间简单使用了一下这三个Debugger，下面是一些使用感受和选择建议。\n首选：De MonsterDebugger\n优点：\n显示正在运行的swf的显示列表 实时更改显示对象的属性 在Debugger中选择某个显示对象的时候，swf中该对象周边还会出现黄色边框 可以筛选（筛选比搜索更好用）调试信息 可以调整界面的显示，不显示不需要的界面 有Monitor界面 缺点：\nDebug.as文件较大 运行速度较慢 快速开发：Alcon\n优点：\n启动快速 使用更加简单 单独的Inspect可以显示复杂对象 缺点：\n暂无\n不再推荐：Arthropod\n著名的JWPlayer也可以采用Arthropod进行调试。但和上面两个调试器比起来就没什么特色了。\n","date":"2010-09-20","description":"","lastmod":"2010-09-20T06:37:31Z","slug":"1143","tags":["actionscript","air"],"title":"Alcon/De MonsterDebugger/Arthropod Flash Debugger简单评测","url":"https://blog.zengrong.net/post/1143/"},{"categories":["use"],"content":"不看文章中的废话直接下载\nFlash自从被Adobe收购后，安装包就做得越来越大。回想起Flash MX 2004时代，安装包不到100M，可现在都被Adobe搞得过G了。安装一次更是耗时十几分钟，痛苦啊……\n在做Flash开发的时候，我更多的是使用纯AS做开发， Flash只是被用作资源保存的工具 ，而Flash CS3已经提供了AS3的支持，并且比CS4、CS5更加快速和稳定，这就足够使用了。\n网上下载的 Flash CS3 9.02 精简绿色中文版 大多有几个问题，以下是解决办法：\n创建Flash放映文件时出错 在绿色版安装目录下建立“players”文件夹，并下载一个最新的Flash Player 9独立播放器文件，将其复制进入“player”文件夹并将文件名改为“flashplayer.exe“即可解决。\n在使用AS编辑面板的时候以及编译的时候弹出“Java运行时环境初始化时出现错误，你可能需要重装Flash”提示 在绿色版安装目录下建立“jvm”文件夹，然后下载并安装JAVA虚拟机（也就是上面出错提示中提到的JAVA运行环境），最后从JAVA虚拟机的安装目录中将“bin”和“lib”文件夹复制到“jvm”下即可。\n其实只需要安装 JAVA 虚拟机就能解决这个问题了。我提供的压缩包中附带了 jvm 文件夹。如果你不想安装 JAVA ，那么也可以设置 JAVA_HOME 环境变量到 jvm 文件夹，并将 jvm/bin 加入 PATH 。\npng格式的图片提示无法导入 这个问题我没有找到解决办法，有网友说自己解决了这问题，但我咨询过后发现他发给我的版本也是无法导入的。目前我用到的解决办法是，先导入一个jpg文件，然后在库中双击这个导入的jpg文件，在弹出的“位图属性”对话框中单击“导入”，就可以选择png文件导入了。导入成功后会替换库中先前导入的jpg文件。\n我将解决了上面1、2两步都完成的Flash CS3 9.02版重新打包，集成jre6.21和Flash Player 9.0.280，一共65MB，需要的朋友可以在这里下载 。\n","date":"2010-09-04","description":"","lastmod":"2010-09-04T01:39:24Z","slug":"flash-cs3-protable-version","tags":["flash","flashplayer","software"],"title":"Flash CS3 9.02绿色版（解决JAVA运行环境和创建Flash放映文件的错误）","url":"https://blog.zengrong.net/post/flash-cs3-protable-version/"},{"categories":["others"],"content":"千千静听一直是我喜欢的播放器，小巧且功能强大，自动下载歌词，最重要的是可以修改mp3的文件tag。\n后来有段时间千千静听开始弹出广告，但还好能够知道这广告是千千静听弹出来的，关掉就好了。\n于是弃用了千千静听，改用airplay。这几天有一些mp3文件的tag需要修改，于是又重新安装了千千静听。\n可是问题就来了。右下角不定期的弹出“1号店”小广告，弹出的频率不高，但是像这样不明不白弹出来，总让人感觉心里发毛。\n仔细查看，没有可疑进程，没有恶意软件，没有木马……\n先用win7有弹出，后来重装为xp，以为一切清静了，结果还有弹出。\n最后才发现是千千静听的原因。在千千静听安装目录下的info子目录中，会有一个swf文件，这就是1号店的广告了。\n立即卸载千千静听，坚决不再用。\n","date":"2010-09-03","description":"","lastmod":"2010-09-03T01:16:00Z","slug":"1132","tags":["software"],"title":"桌面右下角“1号店”弹出小广告，罪魁祸首千千静听","url":"https://blog.zengrong.net/post/1132/"},{"categories":["news"],"content":" 转自：http://blog.5d.cn/user12/dzxz/200809/500545.html\n版本: 7.14\n日期: 6/10/2008\n中文翻译：独自行走【闪航AS】 （英文名 Richard ) 博客 http://dzxz.blog.5d.cn\nTweenLite参数说明\nTweenMAX参数说明\n原来Tween缓冲是这么回事\nTweenFilterLite 继承自 TweenLite ，进行了功能上的扩充，提供了颜色、图象处理方面的一些简便的方法，如果对 Flash 中的矩阵有一些基本的了解之后，再来用它，会比较的得心应手，它有点像是将 Flash 的颜色矩阵和卷积矩阵揉合到一起，提供了更为简单的应用方法，因此，一并翻译出来供大家参考。\n描述: TweenFilterLite 继承自超轻量级的（大约3k）、强大的 TweenLite 核心类，新添加了滤镜缓动（模糊、发光、阴影、斜角滤镜、等等）和图像效果（调整对比度、色调、亮度、饱和度、灰度以及阀值），加起来大约 6k TweenFilterLite 类的语法结构与 TweenLite 相同。如果你还不熟悉 TweenLite ，推荐你先把它找出来看一下。（我也推荐你看一下先前翻译的 TweenLite 使用详解，即 TweenLite 类的描述文件----译者注）\n它提供了一种简便的方法，可以在同一时间对某个对象的多个属性进行缓动，包括 MovieClip 的位置、透明度、音量、颜色等。\n就像 TweenLite 一样，TweenFilterLite 允许你指定延迟缓动（让缓动在指定的时间之后开始进行----译者注），以及在缓动开始或结束时调用任何函数（同时还可以传递任意多个参数给这些函数），自动清除掉其它的对同一目标进行的缓动（从而避免冲突）、缓动数组，等等。使用这个类的一个很大的好处就是它非常努力地缩小了文件的尺寸（正如它名字中“Lite”所代表的含意）。外面还有一些其它的缓动引擎，但是根据我的经验，它们至少比这个文件要大上3倍以上，这在需要精确控制文件尺寸的情况下是不可接受的（比如制做 banner广告）。同样，我还尚未发现过比这更快的引擎。这里采用的语法是非常简单的，并且这个类不依赖于复杂的原型扩展手段，那样做通常会在一些编译器中制造难题。TweenFilterLite非常的简单、快捷、并且（依旧）非常的轻盈。\n如果你还在寻找更多的功能，去 www.TweenMax.com 看一下 TweenFilterLite 的大哥 TweenMax 。\n参数: target : Object - 缓动的目标对象，对它的属性进行缓动 duration : Number - 动画的时长（单位：秒） vars : Object - 对象类型的参数，该对象包含了所有的需要缓动的属性，属性中保存的是缓动结束时的目标值（如果使用 TweenFilterLite.from() 方法，属性中保存的是缓动开始时的初始值）。 所有的滤镜都是通过对象的属性来传递的(属性的名称必须准确，比如：blurFilter, glowFilter, colorMatrixFilter,等等)，滤镜对象可以包含任意多个与滤镜相关的属性，比如 blurX, blurY, contrast, color, distance, colorize, brightness, highlightAlpha, 等等。 专有属性: delay : Number - 延迟开始缓动 (以秒为单位). ease : Function - 缓动函数. 例如，fl.motion.easing.Elastic.easeOut 函数。默认的是 Regular.easeOut函数。 easeParams : Array - 用来存贮缓动公式所需要的额外数据。当使用 Elastic 公式并且希望控制一些额外的参数，比如放大系数和缓动时间。大多数的缓动公式是不需要参数的，因此，你不需要给其它的缓动公式传递参数。 autoAlpha : Number - 用它来代替 alpha 属性，可以获得一些副加的效果，比如当 alpha 值缓动到 0时，将 visible 属性改为 false。当缓动开始前，autoAlpha 大于 0时，它将会把 visible 属性变成 true 。 volume : Number - 对 MovieClip 或 SoundChannel 对象中的 volume 属性（音量大小）进行缓动。该属性表示的是缓动结束时的音量值（如果使用的是 TweenLite.from()，这个属性将表示目标对象开始缓动时的音量) tint : Number - 改变 DisplayObject 的色调或颜色，设置一个16进制颜色值，做为缓动结束时，目标对象的颜色值，（如果使用的是 TweenLite.from()，这个值将表示目标对象开始缓动时的颜色)。例如，颜色值可以设定为： 0xFF0000。如果要移除颜色，只需要传递一个 null 值给 tint 属性。( TweenLite 中使用的是 removeTint 属性----译者注) frame : Number - 将 MovieClip 缓动到指帧频。 onStart : Function - 在缓动开始时想要执行某个函数，就将函数的引用（通常是函数名）放到这里。当缓动是带延迟的，这一点会非常有用。 onStartParams : Array - 为缓动开始时要执行的函数传递参数。(可选的) onUpdate : Function - 缓动过程中，每次更新属性值时，会执行这里指定的函数(缓动开始后，每一帧被触发一次)。 onUpdateParams : Array - 给 onUpdate 参数指定的函数传递参数 (可选的) onComplete : Function - 缓动结束时执行的函数。 onCompleteParams : Array - 给 onComplete 参数所指定的函数传递参数(可选的) renderOnStart : Boolean - 当使用带有延迟缓动的 TweenFilterLite.from() ，并且希望阻止缓动的渲染（rendering）效果，直到缓动真正开始，将这个值设为 true.默认情况下该值为 false，这会让渲染效果立即被执行，甚至是在延迟尚未结束之前。 overwrite : Boolean - 如果 不 希望当前的缓动效果自动覆盖到其它的影响同一属性的缓动，请确保这个值设为 false。 blurFilter : Object - 应用模糊滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：blurX（横向的模糊度）, blurY（纵向的模糊度）, quality（品质,默认值为2） glowFilter : Object - 应用发光滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：alpha（通明度），blurX , blurY , color（颜色），strength（强度）, quality，inner（内侧发光），knockout（挖空） colorMatrixFilter : Object -应用颜色矩阵滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：colorize（色调），amount（总量），contrast（对比度），brightness（亮度），saturation（饱和度），hue（色相），threshold（阀值），relative（相关性），matrix（颜色矩阵） dropShadowFilter : Object - 应用阴影滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：alpha, angle（角度）, blurX, blurY, color, distance（距离）, strength, quality bevelFilter : Object - 应用斜角滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：angle, blurX, blurY, distance, highlightAlpha（高亮区的透明度）, highlightColor（高亮区的颜色），shadowAlpha（阴影区的透明度）, shadowColor（阴影区的颜色）, strength（强度）, quality 举例: 一个简单示例，将 clip_mc 的模糊度从当前值，经过1.5秒后，模糊到20，代码如下：\n1import gs.TweenFilterLite; 2TweenFilterLite.to(clip_mc, 1.5, {blurFilter:{blurX:20, blurY:20}}); 下面的代码演示的是连续缓动的例子，首先用 2 秒钟的时间改变 MovieClip 的颜色，然后用 1 秒种进行模糊处理（第二个缓动延迟了两秒后进行----译者注）：\n1import gs.TweenFilterLite; 2TweenFilterLite.to(clip_mc, 2, {colorMatrixFilter:{colorize:0xFF0000, amount:1}}); 3TweenFilterLite.to(clip_mc, 1, {blurFilter:{blurX:20, blurY:20}, delay:2, overwrite:false}); 更高级的应用，如果希望在 5 秒种的时间里将影片剪辑 clip_mc 的饱和度降到 0 ，推迟 2 秒启动缓动，缓动结束时调用“onFinishTween”函数，并且为这个函数传递一些参数（数值 5 和 clip_mc 的引用)，代码如下：\n1import gs.TweenFilterLite; 2import fl.motion.easing.Back; 3TweenFilterLite.to(clip_mc, 5, {colorMatrixFilter:{saturation:0}, delay:2, onComplete:onFinishTween, onCompleteParams:[5, clip_mc]}); 4function onFinishTween(argument1:Number, argument2:MovieClip):void { 5trace(\u0026#34;The tween has finished! argument1 = \u0026#34; + argument1 + \u0026#34;, and argument2 = \u0026#34; + argument2); 6} 如果舞台上的影片剪辑已经具备了期望的缓动结束时的属性值，你希望从某个色调的版本（比如 红色：0xFF0000）缓动到当前的属性状态，可以使用下面的代码：\n1import gs.TweenFilterLite; 2TweenFilterLite.from(clip_mc, 5, {type:\u0026#34;color\u0026#34;, colorize:0xFF0000}); 备注: 这个类 (包含它的父类 TweenLite ) 将会让你的 Flash 文件总共增加 6kb 。 需要 Flash 9 播放器或之后的版本( 支持 ActionScript 3.0 的播放器 )。 对所有滤镜来说，Quality （品质）参数的默认值为 \u0026quot;2\u0026quot;，但是你可以通过传递参数来改变这一设置。 Flash 中已经有自带的图像滤镜功能 （ColorMatrixFilter），该功能需要使用到矩阵，TweenFilterLite 提供了静态方法，可以直接进行调用，用于控制 ColorMatrixFilter 中使用的矩阵（因此，直接使用即可，不必缓动任何东西）。例如，可以通过下面的代码改变矩阵的颜色： 1var myNewMatrix:Array = TweenFilterLite.colorize(myOldMatrix, 0xFF0000, 1); 特别鸣谢 Mario Klingemann (http://www.quasimondo.com)，感谢他在 ColorMatrix 类中所做的工作，这个类对于图像效果真的非常有用。\n","date":"2010-09-01","description":"","lastmod":"2010-09-01T09:44:34Z","slug":"tweenfilterlite-param","tags":["as3","tween"],"title":"【转】TweenFilterLite 参数说明","url":"https://blog.zengrong.net/post/tweenfilterlite-param/"},{"categories":["news"],"content":"转自：http://blog.5d.cn/user12/dzxz/200809/500547.html\n版本: 1.17 日期: 6/10/2008 中文翻译： 独自行走【闪航AS】 （英文名 Richard ) 博客 TweenLite参数说明 TweenFilterLite参数说明 原来Tween缓冲是这么回事 TweenMax 建立在 TweenLite 和 TweenFilterLite 基础之上，因此，又揉合了这二者的功能，使得功能更加的齐备，但是如果说易用性，觉得还是 TweenLite 来得方便一些。我的译文是从 TweenLite 开始的，接着是 TweenFilterLite 最后是 TweenMax ，这也恰好是这个类发展的轨迹，沿着这个轨迹一路读过来，会觉得容易理解很多。\nTweenMax 建立在 TweenLite 核心类以及它的大哥 TweenFilterLite 基础之上，它为 Tween 家族增加了新的受欢迎的功能（尽管只是锦上添花），从而使家族更加的壮大，比如贝赛尔缓动、暂停/继续能力，简便的连续缓、16进制颜色缓动、以及更多的内容。\nTweenMax 采用了与它的兄弟相似的易于学习的语法结构。实事上，因为它扩展自它们，TweenMax 可以做任何 TweenLite 和/或者 TweenFilterLite 能做的事，还加上了更多的特色。那么为什么要建立 3 个类，而不是 1 个呢？问的好，我的目标是：效率最大化，尺寸最小化。坦白的说，TweenLite 可能是所有的程序员在 90% 的项目中都需要用到的，而它仅有 3k。相对它的功能来说，它是非常高效和紧凑的。\n但是如果你需要对滤镜进行缓动，或者更丰富的图像效果，比如说饱和度(saturation)、对比度(contrast)、色相(hue)、调色等等进行控制， 那就装上 TweenFilterLite 总共 6k 。还想要的更多？ No problem ！TweenMax 已经在总共 8k 的大小里面塞满了足够多的功能。\n想查看相关的功能对比图，请访问 TweenMax.com 以获取更多信息。\n( TweenMax 类包中直接包含有独立的 TweenLite 和 TweenFilterLite 类，因此下载这一个包就可以了，在 Flash 类路径中添加的时候，也只需要添加这一个路径就可以了----译者注)\nTweenMax 创造了一种全新的功能，叫做 \u0026quot;bezierThrough\u0026quot;（暂译为贝塞尔通路），这个功能允许你定义一些点，通过贝塞尔曲线连接这些点，（通常的控制点只是用来拉近曲线，这里的点直接在曲线的路径上）。当然，如果你愿意，你可以用更正规的贝塞尔曲线。目前，TweenMax增加了下列功能（相对于 TweenFilterLite 而言)：\n进行贝塞尔缓动（包括指定通路 THROUGH 点和运动对象的自动方位导航(在贝塞尔曲线中移动时，对象的旋转角度是自动控制的，译者注)) 连续的缓动 或称为 序列化的缓动 对对象数组(数组中存放了不同的对象，译者注)中的对象进行统一的缓动，使用 allTo() 和 allFrom() 缓动中的 暂停/继续 功能，使用 pause() 和 resume() 方法，或者 \u0026quot;paused\u0026quot; 属性 (比如 myTween.paused = true) isTweening 静态方法，用来判断一个对象是否正在进行缓动(比如 TweenMax.isTweening(my_mc)) 跳转至缓动的任何时段，使用 \u0026quot;progress\u0026quot; 属性。输入一个 0 到 1 之间的数值。 progress 值为零，将会把缓动的进程跳转到初始阶段，值为 1 时，跳转至 100% 完成状态，值为 0.5 时，将转至缓动过程的半山腰位置。 例如: myTween.progress = 0.5; 对 16 进制的颜色进行缓动，使用 hexColors 属性 获取缓动效果的实例数组，该数组中包括了加在一个指定的目标对象上的所有的缓动效果的实例，该数组可以容纳 TweenMax 和 TweenLite 和 TweenFilterLite 三种类型的缓动实例。 例如: TweenMax.getTweensOf(my_mc); // (如果 my_mc 上使用了不止一个的缓动效果，那么这里将返回一个数组，数组中是不同的缓动效果的实例，可以用来对每个缓动进行实时的控制，译者注) 获取 TweenMax (和 TweenLite 和 TweenFilterLite) 的实例数组，使用用静态函数 getAllTweens() 中止（杀死）所有的缓动(以及可选的完成部分缓动) 暂停/继续 全部的缓动 参数 target : Object - 目标 MovieClip (或其它对象)，对它的属性进行缓动 duration : Number - 动画的时间长度（单位：秒） vars : Object -一个包含了多种属性的对象，用来存贮缓动结束时的各种属性值（如果你使用 TweenLite.from() 方法，这里的参数表示缓动的初始值）。 例如:\nalpha: 目标对象在缓动结束时的 alpha (不透明度的级别)值。（当使用TweenMax.from()时，表示开始缓动时的 alpha 值)\n举个例子，如果 target_obj.alpha 是 1，当这个缓动代码被执行时，将 alpha 参数设成 0.5 ，它将实现从 1 到 0.5 的渐变缓动。\nx: 想要改变 MovieClip 的 x 位置，只需要将这个参数值设成你希望的缓动结束时的值即可。（如果使用 TweenMax.from()，则表示缓动开始时的 x 值）\n专有属性 delay : Number - 延迟缓动 (以秒为单位) ease : Function - 缓动函数. 例如，fl.motion.easing.Elastic.easeOut 函数。默认的是 Regular.easeOut函数 easeParams : Array - 用来存贮缓动公式所需要的额外数据. 当使用 Elastic 公式并且希望控制一些额外的参数， 比如振幅和周期。大多数的缓动公式是不需要参数的，因此，你不需要给其它的缓动公式传递参数。 autoAlpha : Number - 用它来代替 alpha 属性，可以获得一些副加的效果，比如当 alpha 值缓动到 0时， 自动将 visible 属性改为 false。当缓动开始前，autoAlpha 大于 0 时，它将会自动把 visible 属性变成 true 。 volume : Number - 改变 MovieClip 或者 SoundChannel 的音量，将缓动结束时的音量值写在这里即可。(如果使用 TweenMax.from()，这里的值表示缓动开始时的音量). tint : Number - 改变可显示对象(DisplayObject)的色调/颜色，将缓动结束时的16进制值颜色值（比如0xFF0000）写在这里即可。(如果使用 TweenMax.from()，这里的值表示缓动开始时的颜色值). 如果想要移除颜色，传一个 null 做为颜色值即可。 frame : Number - 将 MovieClip 缓动到指定的帧频 bezier : Array - Bezier 缓动，允许你以非线性的方式进行缓动。例如，将一个 MovieClip 从原始的 (0,0) 的位置，向右移动500像素，到(500,0)，在缓动的中间向下弯曲。只需要向贝赛尔数组中传递多个对象，每一个都是一个控制点（关于控制点是如何工作的请参考 Flash 的 curveTo() 绘图方法相关说明）。在这个例子中，假设我们的控制点是 x/y 坐标 250,50。把 my_mc 放到 0,0 位置，然后执行下面的代码：\nTweenMax.to(my_mc, 3, {_x:500, _y:0, bezier:[{_x:250, _y:50}]}); bezierThrough : Array - 与上面的贝赛尔数组相似，但是它接收的不是控制点，而是贝赛尔曲线要经过的位置点。与控制点相比，这样的用法更加直观。 orientToBezier : Array (或者 Boolean 类型) - 设计师/开发人员经常用到的一个效果，让 MovieClip/Sprite 自动调整自身的方向（改变 rotation），使之符合贝赛尔路径的方向（就像是汽车在曲折的道路上需要不断调整方向一样----译者注）。orientToBezier 使得这一切变得简单。为了更精确的调整 rotation 属性，TweenMax 需要 4 方面的信息: 位置属性 1 (通常为 \u0026quot;x\u0026quot;) 位置属性 2 (通常为 \u0026quot;y\u0026quot;) 旋转属性 (通常为 \u0026quot;rotation\u0026quot;) 旋转的度数(可选的 - 让它更容易的正确瞄准 MovieClip ) 当 orientToBezier 属性为数组时，该（容器）数组中的每个元素是包含了一组数据的数组（形如[[x1,y1,rota1,ang1],[x2,y2,rota2,ang2]]----译者注）。 为了获得最大的灵活性，你可以向容器数组中传递任意数量的数组，逐个的指定旋转属性。这在进行 3D 工作进会很方便，因为你可以旋转多个坐标轴。如果进行的是标准的 2D x/y 坐标系贝赛尔缓动，只需要传递一个布尔值 true ，然后 TweenMax 将会使用一个典型的数组设置 [[\u0026quot;x\u0026quot;, \u0026quot;y\u0026quot;, \u0026quot;rotation\u0026quot;, 0]] 。 提示：不要忘了这里存在着容器数组（注意：数组前后有两层方括号） hexColors : Object - 尽管 16 进制颜色是一种技术上使用的数字，但当你试途按照数字增减的老办法来缓动时，你会发现颜色的变化并不平滑。为了更恰当的进行颜色缓动，通常需要独立的对红、绿、蓝的成分进行缓动。TweenMax 把这一切变得简单。将一个 16 进制颜色值缓动到另一个 16 进制颜色值，只需要使用这个 TweenMax 中专用的 hexColors 属性即可。\n这个属性必须是一个对象，该对象具有相关的颜色属性，属性名称与目标对象中使用的 16 进制颜色属性的名称相同。比如，如果 my_obj 对象具有一个“myHexColor”属性，想让它在两秒种内缓动为红色 (0xFF0000)，需要这样做： TweenMax.to(my_obj, 2, {hexColors:{myHexColor:0xFF0000}}); (内层花括号括起来的部分，表示的就是一个包含有 myHexColor属性的对象----译者注) 可以传递任意数量的 hexColor 属性。 onStart : Function - 在缓动开始时想要执行某个函数，就将函数的引用（通常是函数名）放到这里。当缓动是带延迟的，这一点会非常有用。 onStartParams : Array - 为缓动开始时要执行的函数传递参数。(可选的) onUpdate : Function - 缓动过程中，每次更新属性值时，会执行这里指定的函数(缓动开始后，每一帧被触发一次)。 onUpdateParams : Array - 给 onUpdate 参数指定的函数传递参数 (可选的) onComplete : Function - 缓动结束时执行的函数。 onCompleteParams : Array - 给 onComplete 参数所指定的函数传递参数(可选的) renderOnStart : Boolean - 当使用带有延迟缓动的 TweenFilterLite.from() ，并且希望阻止缓动的渲染（rendering）效果，直到缓动真正开始，将这个值设为 true.默认情况下该值为 false，这会让 TweenMax.from() 渲染效果立即被执行，甚至是在延迟尚未结束之前。 overwrite : Boolean - 如果不希望当前的缓动效果自动覆盖到其它的影响同一属性的缓动，请确保这个值设为 false。 blurFilter : Object - 应用模糊滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：blurX（横向的模糊度）, blurY（纵向的模糊度）, quality（品质,默认值为2） glowFilter : Object - 应用发光滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：alpha（通明度），blurX , blurY , color（颜色），strength（强度）, quality，inner（内侧发光），knockout（挖空） colorMatrixFilter : Object - 应用颜色矩阵滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：colorize（色调），amount（总量），contrast（对比度），brightness（亮度），saturation（饱和度），hue（色相），threshold（阀值），relative（相关性），matrix（颜色矩阵） dropShadowFilter : Object - 应用阴影滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：alpha, angle（角度）, blurX, blurY, color, distance（距离）, strength, quality bevelFilter : Object - 应用斜角滤镜，需要传递一个具有下列（一个或多个）属性的对象做为参数：angle, blurX, blurY, distance, highlightAlpha（高亮区的透明度）, highlightColor（高亮区的颜色），shadowAlpha（阴影区的透明度）, shadowColor（阴影区的颜色）, strength（强度）, quality 关键属性 progress : Number (范围0 - 1，为 0 表示缓还未开始，0.5 缓动进行了一半，1 缓动结束) paused : Boolean （逻辑值） 关键方法: TweenMax.to(target:Object, duration:Number, vars:Object):TweenMax TweenMax.from(target:Object, duration:Number, vars:Object):TweenMax TweenMax.allTo(targets:Array, duration:Number, vars:Object):Array TweenMax.allFrom(targets:Array, duration:Number, vars:Object):Array TweenMax.sequence(target:Object, tweens:Array):Array TweenMax.getTweensOf(target:Object):Array TweenMax.isTweening(target:Object):Boolean TweenMax.getAllTweens():Array TweenMax.killAllTweens(complete:Boolean):void TweenMax.killAllDelayedCalls(complete:Boolean):void TweenMax.pauseAll(tweens:Boolean, delayedCalls:Boolean):void TweenMax.resumeAll(tweens:Boolean, delayedCalls:Boolean):void pause():void resume():void 示例:\n对 MovieClip 进行连续的缓动，先用2秒钟时间让透明度降到 50%，然后用1秒钟时间滑落到 y 坐标为300的位置，使用如下的代码：\n1import gs.TweenMax; 2TweenMax.sequence(clip_mc, [{time:2, alpha:0.5}, {time:1, y:300}]); 使用 Back.easeOut 函数对影片剪辑 clip_mc 进行缓动，缓动持续 5秒钟，alpha 值变到 0.5，x 坐标变到 120，延迟 2秒钟执行，缓动结束后，调用“onFinishTweenusing”，并且为这个函数传递几个参数（数值5 和 对 clip_mc 的引用），代码如下：\n1import gs.TweenMax; 2import fl.motion.easing.Back; 3TweenMax.to(clip_mc, 5, {alpha:0.5, x:120, ease:Back.easeOut, delay:2, onComplete:onFinishTween, onCompleteParams:[5, clip_mc]}); 4function onFinishTween(argument1:Number, argument2:MovieClip):void { 5trace(\u0026#34;The tween has finished! argument1 = \u0026#34; + argument1 + \u0026#34;, and argument2 = \u0026#34; + argument2); 6} 如果舞台上的影片剪辑已经处于期望的缓动结束时位置，而你希望它从其它位置用 5 秒钟的时间回到当前位置，（比如从比当前位置高 100 像素或屏幕外更高的地方，通过改变 y 属性，下落到当前位置）可以使用下面的代码：\n1import gs.TweenMax; 2import fl.motion.easing.Elastic; 3TweenMax.from(clip_mc, 5, {y:\u0026#34;-100\u0026#34;, ease:Elastic.easeOut}); 备注 给参数值加上引号，表示对指定的属性进行相应操作。比如，使用 TweenMax.to(mc, 2, {x:\u0026quot;-20\u0026quot;}); 它将 mc.x 向左移动 20 像素，与此相同效果的代码是：\n1TweenMax.to(mc, 2, {x:mc.x - 20}); 可以对任何 MovieClip 使用 \u0026quot;volume\u0026quot; 缓动,就比如：\n1TweenMax.to(myClip_mc, 1.5, {volume:0}); 可以将 MovieClip 缓动成某种色调或颜色，使用 \u0026quot;tint\u0026quot; 属性,比如:\n1TweenMax.to(myClip_mc, 1.5, {tint:0xFF0000}); 想要对数组内容进行缓动，将数值放到一个叫 endArray 的数组中即可，例如:\n1var myArray:Array = [1,2,3,4]; 2TweenMax.to(myArray, 1.5, {endArray:[10,20,30,40]}); 可以在任何时候终止缓动，使用 TweenMax.killTweensOf(myClip_mc); 函数。如果想强制终止缓动，可以传递一个 true 做为第二个参数，比如\n1TweenMax.killTweensOf(myClip_mc, true); 去除延迟回调函数，用 TweenMax.killDelayedCallsTo(myFunction_func); 这项功能可以用来控制回调函数的优先级。\n使用 TweenMax.from() 方法，可以使目标对象运动回它所在的位置。比如，你可以将对象在舞台上摆放整齐（缓动结束时的位置），然后利用缓动，让它们跑到那个位置上去，你可以将缓动初始位置的 x 或 y 或 alpha (或者其它你需要的属性)当做参数传递给这个方法函数。\n","date":"2010-09-01","description":"","lastmod":"2010-09-01T09:34:27Z","slug":"tweenmax-param","tags":["as3","tween"],"title":"【转】TweenMax参数说明","url":"https://blog.zengrong.net/post/tweenmax-param/"},{"categories":["technology"],"content":"在Flex3时代，要改变AIR程序的状态栏字体，可以使用修改CSS的方法：\n1\u0026lt;mx:Style\u0026gt; 2 .boldRed { 3 color: red; 4 fontSize: 12; 5 fontWeight: bold; 6 } 7 8 WindowedApplication { 9 statusTextStyleName: boldRed; 10 } 11\u0026lt;/mx:Style\u0026gt; 但是，在Flex4中，这招不管用了，默认的spark组件采用了新的方法来定义皮肤，这直接导致你根本找不到statusTextStyleName属性。\n幸运的是，spark提供了更好的方法。spark的Window或者WindowedApplication组件的默认皮肤都包含statusText这个“外观部件”，我们对它的属性进行设置即可。当然，我们也可以完全重写默认的skin，skin采用MXML写成，更好编辑。这个组件的路径在这里：\n1[Flash Builder安装路径]\\sdks\\[SDK版本]\\frameworks\\projects\\airspark\\src\\spark\\skins\\spark\\ 1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;s:WindowedApplication xmlns:fx=\u0026#34;http://ns.adobe.com/mxml/2009\u0026#34; 3 xmlns:s=\u0026#34;library://ns.adobe.com/flex/spark\u0026#34; 4 xmlns:mx=\u0026#34;library://ns.adobe.com/flex/mx\u0026#34; 5 applicationComplete=\u0026#34;init()\u0026#34; status=\u0026#34;测试\u0026#34;\u0026gt; 6 \u0026lt;fx:Style\u0026gt; 7 @namespace s \u0026#34;library://ns.adobe.com/flex/spark\u0026#34;; 8 @namespace mx \u0026#34;library://ns.adobe.com/flex/mx\u0026#34;; 9 s|WindowedApplication 10 { 11 font-size: 12; 12 font-family:\u0026#34;Microsoft YaHei,SimSun\u0026#34;; 13 } 14 .errorTip 15 { 16 font-size: 12; 17 } 18 \u0026lt;/fx:Style\u0026gt; 19 \u0026lt;fx:Script\u0026gt; 20 \u0026lt;![CDATA[ 21 private function init():void 22 { 23 statusText.setStyle(\u0026#39;fontSize\u0026#39;, 12); 24 } 25 ]]\u0026gt; 26 \u0026lt;/fx:Script\u0026gt; 27\u0026lt;/s:WindowedApplication\u0026gt; 需要注意的是，如果把这招用在Window组件中，就必须在Window.open()执行之后再调用setStyle，否则将会导致运行时错误。至于原因么……自己想 ^_^\n","date":"2010-08-31","description":"","lastmod":"2010-08-31T02:56:34Z","slug":"change-status-bar-font-size-in-flex4-air","tags":["air","flex","spark"],"title":"在基于Flex4的AIR程序中改变状态栏字体大小","url":"https://blog.zengrong.net/post/change-status-bar-font-size-in-flex4-air/"},{"categories":["news"],"content":"http://tankionline.com\nhttp://www.smallworlds.com\n","date":"2010-08-27","description":"","lastmod":"2010-08-27T14:51:01Z","slug":"1122","tags":["flash","game"],"title":"Flash大型在线游戏推荐","url":"https://blog.zengrong.net/post/1122/"},{"categories":["technology"],"content":"flixel 帮助组织游戏对象，更快的四叉树，更准确的碰撞，更简洁的API，加上所有的旧版flixel的经典功能：动画精灵，砖阶地图和和粒子生成器。\nflixel的教学站点\n地震逃生小游戏\npushbuttonengine\n内置Box2D库，提供大量的源码和文章供学习。\n十几个源码和文章\nAdobe EDGE的文章\n","date":"2010-08-25","description":"","lastmod":"2010-08-25T15:06:34Z","slug":"1121","tags":["as3","game","physics"],"title":"两个Flash游戏引擎介绍","url":"https://blog.zengrong.net/post/1121/"},{"categories":["technology"],"content":"转自：http://caihx.javaeye.com/blog/605143\n套接字基础 1. 介绍 多数程序员，不管他们是否使用 Java语言进行编码，都不想知道很多关于不同计算机上的应用程序彼此间如何通信的低级细节。程序员们希望处理更容易理解的更高级抽象。Java程序员希望能用他们熟悉的 Java 构造，通过直观接口与对象交互。\n套接字在两个领域中都存在 ―我们宁愿避开的低级细节和我们更愿处理的抽象层。本教程讨论的低级细节将只限于理解抽象应用程序所必须的部分。\n2. 计算机组网 101 计算机以一种非常简单的方式进行相互间的操作和通信。计算机芯片是以 1 和 0的形式存储并传输数据的开―闭转换器的集合。当计算机想共享数据时，它们所需做的全部就是以一致的速度、顺序、定时等等来回传输几百万比特和字节的数据流。每次想在两个应用程序之间进行信息通信时，您怎么会愿意担心那些细节呢？\n为免除这些担心，我们需要每次都以相同方式完成该项工作的一组包协议。这将允许我们处理应用程序级的工作，而不必担心低级网络细节。这些成包协议称为 协议栈（stack） 。TCP/IP 是当今最常见的协议栈。多数协议栈（包括TCP/IP）都大致对应于国际标准化组织（International Standards Organization，ISO）的开放系统互连参考模型（Open Systems Interconnect Reference Model，OSIRM）。OSIRM认为在一个可靠的计算机组网中有七个逻辑层（见图）。各个地方的公司都对这个模型某些层的实现做了一些贡献，从生成电子信号（光脉冲、射频等等）到提供数据给应用程序。TCP/IP映射到 OSI 模型中的两层的情形如图所示。\n我们不想涉及层的太多细节，但您应该知道套接字位于什么地方。\n3. 套接字位于什么地方 套接字大致驻留在 OSI 模型的 会话层 （见图）。会话层夹在其上面向应用的层和其下的实时数据通信层之间。会话层为两台计算机之间的数据流提供管理和控制服务。作为该层的一部分，套接字提供一个隐藏从导线上获取比特和字节的复杂性的抽象。换句话说，套接字允许我们让应用程序表明它想发送一些字节即可传输数据。套接字隐藏了完成该项工作的具体细节。\n当您打电话时，您的声音传到传感器，传感器把它转换成可以传输的电数据。电话机是人与电信网络的接口。您无须知道声音如何传输的细节，只要知道想打电话给谁就行了。同样地，套接字扮演隐藏在未知通道上传输 1 和 0 的复杂性的高级接口的角色。\n4. 把套接字暴露给应用程序 使用套接字的代码工作于 表示层 。表示层提供 应用层 能够使用的信息的公共表示。假设您打算把应用程序连接到只能识别 EBCDIC 的旧的银行系统。应用程序的域对象以 ASCII 格式存储信息。在这种情况下，您得负责在表示层上编写把数据从 EBCDIC 转换成 ASCII 的代码，然后（比方说）给应用层提供域对象。应用层然后就可以用域对象来做它想做的任何事情。\n您编写的套接字处理代码只存在于表示层中。您的应用层无须知道套接字如何工作的任何事情。\n5. 什么是套接字？ 既然我们已经知道套接字扮演的角色，那么剩下的问题是：什么是套接字？Bruce Eckel 在他的 《Java 编程思想》 一书中这样描述套接字：\n套接字是一种软件抽象，用于表达两台机器之间的连接“终端”。对于一个给定的连接，每台机器上都有一个套接字，您也可以想象它们之间有一条虚拟的“电缆”，“电缆”的每一端都插入到套接字中。当然，机器之间的物理硬件和电缆连接都是完全未知的。抽象的全部目的是使我们无须知道不必知道的细节。\n简言之，一台机器上的套接字与另一台机器上的套接字交谈就创建一条通信通道。程序员可以用该通道来在两台机器之间发送数据。当您发送数据时，TCP/IP协议栈的每一层都会添加适当的报头信息来包装数据。这些报头帮助协议栈把您的数据送到目的地。好消息是Java语言通过 “流” 为您的代码提供数据，从而隐藏了所有这些细节，这也是为什么它们有时候被叫做 流套接字（streaming socket） 的原因。\n把套接字想成两端电话上的听筒 ― 我和您通过专用通道在我们的电话听筒上讲话和聆听。直到我们决定挂断电话，对话才会结束（除非我们在使用蜂窝电话）。而且我们各自的电话线路都占线，直到我们挂断电话。\n如果想在没有更高级机制如 ORB（以及 CORBA、RMI、IIOP等等）开销的情况下进行两台计算机之间的通信，那么套接字就适合您。套接字的低级细节相当棘手。幸运的是，Java平台给了您一些虽然简单但却强大的更高级抽象，使您可以容易地创建和使用套接字。\n6. 套接字的类型 一般而言，Java 语言中的套接字有以下两种形式：\nTCP 套接字（由 Socket 类实现，稍后我们将讨论这个类） UDP 套接字（由 DatagramSocket 类实现） TCP 和 UDP 扮演相同角色，但做法不同。两者都接收传输协议数据包并将其内容向前传送到表示层。TCP把消息分解成数据包 （数据报，datagrams） 并在接收端以正确的顺序把它们重新装配起来。TCP还处理对遗失数据包的重传请求。有了TCP，位于上层的层要担心的事情就少多了。UDP不提供装配和重传请求这些功能。它只是向前传送信息包。位于上层的层必须确保消息是完整的并且是以正确的顺序装配的。\n一般而言，UDP强加给您的应用程序的性能开销更小，但只在应用程序不会突然交换大量数据并且不必装配大量数据报以完成一条消息的时候。否则，TCP才是最简单或许也是最高效的选择。\n因为多数读者都喜欢 TCP 胜过 UDP，所以我们将把讨论限制在 Java 语言中面向TCP 的类。\n","date":"2010-08-24","description":"","lastmod":"2010-08-24T06:43:11Z","slug":"java-socket-base","tags":["java","netconnection","socket"],"title":"【转】Java socket - 套接字基础","url":"https://blog.zengrong.net/post/java-socket-base/"},{"categories":["technology"],"content":"转自：http://blog.csdn.net/zsnlovewl/archive/2009/12/12/4991820.aspx\n1、TCP连接 要想明白Socket连接，先要明白TCP连接。手机能够使用联网功能是因为手机底层实现了TCP/IP协议，可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口，使上层网络数据的传输建立在“无差别”的网络之上。\n建立起一个TCP连接需要经过“三次握手”：\n第一次握手：客户端发送syn包(syn=j)到服务器，并进入SYN_SEND状态，等待服务器确认；\n第二次握手：服务器收到syn包，必须确认客户的SYN（ack=j+1），同时自己也发送一个SYN包（syn=k），即SYN+ACK包，此时服务器进入SYN_RECV状态；\n第三次握手：客户端收到服务器的SYN＋ACK包，向服务器发送确认包ACK(ack=k+1)，此包发送完毕，客户端和服务器进入ESTABLISHED状态，完成三次握手。\n握手过程中传送的包里不包含数据，三次握手完毕后，客户端与服务器才正式开始传送数据。理想状态下，TCP连接一旦建立，在通信双方中的任何一方主动关闭连接之前，TCP连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求，断开过程需要经过“四次握手”（过程就不细写了，就是服务器和客户端交互，最终确定断开）\n2、HTTP连接 HTTP协议即超文本传送协议(Hypertext Transfer Protocol)，是Web联网的基础，也是手机联网常用的协议之一，HTTP协议是建立在TCP协议之上的一种应用。\nHTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应，在请求结束后，会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。\n1）在HTTP 1.0中，客户端的每次请求都要求建立一次单独的连接，在处理完本次请求后，就自动释放连接。\n2）在HTTP 1.1中则可以在一次连接中处理多个请求，并且多个请求可以重叠进行，不需要等待一个请求结束后再发送下一个请求。\n由于HTTP在每次请求结束后都会主动释放连接，因此HTTP连接是一种“短连接”，要保持客户端程序的在线状态，需要不断地向服务器发起连接请求。通常的做法是即时不需要获得任何数据，客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求，服务器在收到该请求后对客户端进行回复，表明知道客户端“在线”。若服务器长时间无法收到客户端的请求，则认为客户端“下线”，若客户端长时间无法收到服务器的回复，则认为网络已经断开。\n3、SOCKET原理 3.1套接字（socket）概念 套接字（socket）是通信的基石，是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示，包含进行网络通信必须的五种信息：连接使用的协议，本地主机的IP地址，本地进程的协议端口，远地主机的IP地址，远地进程的协议端口。\n应用层通过传输层进行数据通信时，TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接，许多计算机操作系统为应用程序与TCP／IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口，区分来自不同应用程序进程或网络连接的通信，实现数据传输的并发服务。\n3.2 建立socket连接 建立Socket连接至少需要一对套接字，其中一个运行于客户端，称为ClientSocket，另一个运行于服务器端，称为ServerSocket 。\n套接字之间的连接过程分为三个步骤：服务器监听，客户端请求，连接确认。\n服务器监听：服务器端套接字并不定位具体的客户端套接字，而是处于等待连接的状态，实时监控网络状态，等待客户端的连接请求。\n客户端请求：指客户端的套接字提出连接请求，要连接的目标是服务器端的套接字。为此，客户端的套接字必须首先描述它要连接的服务器的套接字，指出服务器端套接字的地址和端口号，然后就向服务器端套接字提出连接请求。\n连接确认：当服务器端套接字监听到或者说接收到客户端套接字的连接请求时，就响应客户端套接字的请求，建立一个新的线程，把服务器端套接字的描述发给客户端，一旦客户端确认了此描述，双方就正式建立连接。而服务器端套接字继续处于监听状态，继续接收其他客户端套接字的连接请求。\n4、SOCKET连接与TCP连接 创建Socket连接时，可以指定使用的传输层协议，Socket可以支持不同的传输层协议（TCP或UDP），当使用TCP协议进行连接时，该Socket连接就是一个TCP连接。\n5、Socket连接与HTTP连接 由于通常情况下Socket连接就是TCP连接，因此Socket连接一旦建立，通信双方即可开始相互发送数据内容，直到双方连接断开。但在实际网络应用中，客户端到服务器之间的通信往往需要穿越多个中间节点，例如路由器、网关、防火墙等，大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致Socket 连接断连，因此需要通过轮询告诉网络，该连接处于活跃状态。\n而HTTP连接使用的是“请求—响应”的方式，不仅在请求时需要先建立连接，而且需要客户端向服务器发出请求后，服务器端才能回复数据。\n很多情况下，需要服务器端主动向客户端推送数据，保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接，服务器就可以直接将数据传送给客户端；若双方建立的是HTTP连接，则服务器需要等到客户端发送一次请求后才能将数据传回给客户端，因此，客户端定时向服务器端发送连接请求，不仅可以保持在线，同时也是在“询问”服务器是否有新的数据，如果有就将数据传给客户端。\n","date":"2010-08-24","description":"","lastmod":"2010-08-24T06:29:52Z","slug":"http-and-socket","tags":["http","netconnection","socket"],"title":"【转】Http和Socket连接区别","url":"https://blog.zengrong.net/post/http-and-socket/"},{"categories":["technology"],"content":"若使用Array，在取值的索引超出范围的时候，取得的值是undefined。\n而如果使用Vector，则会抛出一个RangeError异常告知调用的索引超出了范围\n例如下面的代码：\n1var array:Array = [1,2,3]; 2var vector:Vector. = Vector.([1,2,3]); 3trace(array[4]); 4trace(vector[4]); 执行后的结果：\nundefined\nRangeError: Error #1125: 索引 4 超出范围 3。\n","date":"2010-08-24","description":"","lastmod":"2010-08-24T02:17:56Z","slug":"vector-and-array-on-over-index","tags":["as3"],"title":"Vector与Array在索引超出范围时的区别","url":"https://blog.zengrong.net/post/vector-and-array-on-over-index/"},{"categories":["technology"],"content":"转自：http://blog.5d.cn/user12/dzxz/200808/497831.html\nTweenLite是一个缓动的类包，功能强大，并且易于使用，为了更多的（E文欠佳的、初学的）朋友了解它，使用它，特此翻译了一下TweenLite类文档中的说明文件，主要是对参数的说明，希望对大家有用。\nACTIONSCRIPT 语言版本: 3.0 (AS2 版也有提供) 更新 及更多文档请访问:\nhttp://www.tweenlite.com/ http://www.greensock.com/tweenlite/ TweenMax参数说明 TweenLiteFilter参数说明 描述:\n缓动。 我们都在做。我们很多人都知道除了 Adobe’s Tween 类之外，还有很多更好的动画引擎，（比如 Tweener）.每种引擎都有它们各自的优缺点。\n最近几年，为了得到一个更紧凑的，跑得更快，效率更高的引擎，我创建了TweenLite（我无法接受其它的一些引擎带来的文件尺寸上的负担）.它很快就融入到我的所有工作中。我告诉其它人，让大家能够从中获益，最终，我将它发布了出来。在过去的几年中，TweenLite越来越受欢迎，超乎了我的想像.\n基于此，我又添加了一些新的功能，并且尽量保持这个文件的尺寸，让它小于3K。\nTweenFilterLite 扩充了 TweenLite 并且加入了滤镜缓动，包含了ColorMatrixFilter的一些效果，比如饱和、对比、增亮、色调，甚至是着色，但文件的尺寸始终没有超过3K。与TweenLite 的做法相似，提供有AS2版和AS3版的类包下载。\nTweenMax 比 TweenFilterLite 增加了更多的特性，包含 bezier缓动，暂停/恢复，顺序执行等等。(见 www.TweenMax.com)\n我猜你会想“如果这个是‘轻量级的’，那么它一定会丢掉很多特性，让我用的时候会有点担心”。这种想法是对的，在这里缺少一些其它缓动引擎所包含的特效，但是我可以肯定的说，在过去几年我的工程（很多获奖的flash程序以及500强企业的项目中）中，我几乎一直都在用它，而它从没有让我失望过。\n我还真没发现过我还需要其它的功能。你可以对任何的属性（包括DisplayObject对象的音量和颜色）使用缓动函数，内置的延迟时间，回调函数，以及传递参数给这些回调函数，甚至根据数组进行缓动，统统只在一行代码中完成。如果你需要更多的特效，你可以装上TweenFilterLite 或者 TweenMax来用用。\n我也从来没有发现比这个更快的引擎。不同引擎执行效率的比较，请访问 http://blog.greensock.com/tweeing-speed-test/ .\nTweenLite参数说明:\n$target : Object – 作为目标的对象， MovieClip或者其它对象 $duration : Number – 动画的时间长度（单位：秒） $vars : Object –对象，通过属性值，来存贮各种属性参数用于缓动。（如果你使用TweenLite.from() 方法，这里的参数表示缓动的初始值） 该对象所具有的属性：\nalpha: alpha 目标对象应该完成 (或开始，当使用TweenLite.from()时)的透明度级别.如果 target.alpha是1，当缓动被执行的时候，你指定参数为 0.5，它将把透明度从 1 缓动到0.5. x: 改变 MovieClip的 x 位置,把这个值设置成你希望的 MovieClip的结束位置(如果你使用的是 TweenLite.from()这个值表示开始位置). ( y scaleX scaleY rotation　等属性不再重复说明） 特别的属性 (可选的):\ndelay : Number – 延迟缓动 (以秒为单位). ease : Function – 缓动函数. 例如，fl.motion.easing.Elastic.easeOut函数。默认的是 Regular.easeOut函数。 easeParams : Array – 用来存贮缓动公式所需要的额外数据. 当使用Elastic公式并且希望控制一些额外的参数，比如放大系数和缓动时间。大多数的缓动公式是不需要参数的，因此，你不需要给其它的缓动公式传递参数。 autoAlpha : Number – 用它来代替 alpha属性，可以获得一些副加的效果，比如当 alpha 值缓动到 0时，自动将visible 属性改为 false。当缓动开始前，autoAlpha 大于 0时，它将会把visible 属性变成 true 。 visible : Boolean – 在缓动结束时，想要指定 DisplayObject 的 visible属性，请使用这个参数。 volume : Number – 对 soundTransform（MovieClip/SoundChannel/NetStream等）对象中的volume属性（音量大小）进行缓动 tint : Number – 改变 DisplayObject的颜色，设置一个16进制颜色值之后，当缓动结束时，目标对象将被变成这个颜色，（如果使用的是TweenLite.from()，这个值将表示目标对象开始缓动时的颜色)。举个例子，颜色值可以设定为：0xFF0000。 removeTint : Boolean – 要移除 DisplayObject 颜色，将这个参数设成true 。 frame : Number – 将 MovieClip 缓动到指帧频。 onStart : Function –在缓动开始时想要执行某个函数，就将函数的引用（通常是函数名）放到这里。如果缓动是带延迟的，那么在缓动开始前该函数不会被执行。 onStartParams : Array – 为缓动开始时要执行的函数传递参数。(可选的) onUpdate : Function –缓动过程中，每次更新时调用这里指定的函数(缓动开始后，每一帧被触发一次), onUpdateParams : Array – 给 onUpdate 参数指定的函数传递参数 (可选的) onComplete : Function – 缓动结束时执行的函数。 onCompleteParams : Array – 给 onComplete 参数指定的函数传递参数(可选的) persist : Boolean – 值为 true 时，TweenLite实例将不会自动被系统的垃圾收集器给收走。但是当新的缓动出现时，它还是会被重写（overwritten）默认值为false. renderOnStart : Boolean – 如果你使用带有延迟缓动的TweenFilterLite.from() ，并且阻止缓动的渲染（rendering）效果，直到缓动真正开始，将这个值设为 true.默认情况下该值为 false，这会让渲染效果立即被执行，甚至是在延迟的时间还没到之前。 overwrite : int –当前的缓动被创建以后，通过这个参数可以限制作用于同一个对象的其它缓动，可选的参数值有： 0 (没有):没有缓动被重写。这种模式下，运行速度是最快的，但是需要注意避免创建一些控制相同属性的缓动，否则这些缓动效果间将出现冲突。 1 (全部): (这是默认值，除非 OverwriteManager.init() 被调用过)对于同一对象的所有缓动在创建时将会被完全的覆盖掉。 1TweenLite.to(mc,1,{x:100,y:200}); 2TweenLite.to(mc,1,{x:300,delay:2}); //后创建的缓动将会覆盖掉先前创建的缓动，（可以起到这样的作用：缓动进行到一半时被中断，执行新的缓动 译者注） 3- 2 (自动): (当 OverwriteManager.init() 被执行后,会根据具体的属性值进行选择)只覆盖对同一属性的缓动。 4TweenLite.to(mc,1,{x:100,y:200}); 5TweenLite.to(mc,1,{x:300}); //only \u0026#34;x\u0026#34; 属性的缓动将被覆盖 6- 3 (同时发生): 缓动开始时，覆盖全部的缓动。 7TweenLite.to(mc,1,{x:100,y:200}); 8TweenLite.to(mc,1,{x:300,delay:2});?//不会覆盖先前的缓动，因为每二个缓动开始时，第一个缓动已经结束了。 举例:\n将实例名为 clip_mc 的 MovieClip 透明度降到 50% (0.5) ，并将它 x轴位置移动到 120 ，将音量将到 0，缓动总共用时 1.5 秒，代码如下：\n1import gs.TweenLite; 2TweenLite.to(clip_mc, 1.5, {alpha:0.5, x:120, volume:0}); 如果希望使用更高级的缓动函数在 5 内，将 alpha 变到 0.5，将 x 移动 到 120，使用“easeOutBack” 弹性函数，缓动整体延迟 2秒发生，并且在缓动结束时，执行 “onFinishTween”函数，并且为这个函数传递几个参数，(一个数值 5 以及对 clip_mc 的引用)，代码如下：\n1import gs.TweenLite; 2import fl.motion.easing.Back; 3TweenLite.to(clip_mc, 5, {alpha:0.5, x:120, ease:Back.easeOut, delay:2, onComplete: onFinishTween, onCompleteParams:[5, clip_mc]}); 4function onFinishTween(argument1:Number, argument2:MovieClip):void { 5trace(\u0026#34;The tween has finished! argument1 = \u0026#34; + argument1 + \u0026#34;, and argument2 = \u0026#34; + argument2); 6} 如果你的舞台上的 MovieClip已经停在了它的结束位置，你只想让它花上5秒种回到这个位置，(只需要改变 y属性，比当前位置高 100 像素的位置，让它从那里下落),代码如下（这次使用的是 TweenLite.from 译者注):\n1import gs.TweenLite; 2import fl.motion.easing.Elastic; 3TweenLite.from(clip_mc, 5, {y:\u0026#34;-100\u0026#34;, ease:Elastic.easeOut}); 说明:\nTweenLite类会让你的 Flash 文件增加 3kb大小。 给参数值加上引号，表示对指定的属性进行相应操作。比如，使用 TweenLite.to(mc, 2, {x:”-20″}); 它将 mc.x 向左移动 20像素，与此相同效果的代码是：TweenLite.to(mc, 2, {x:mc.x – 20}); 你可以用别的缓动函数替换 TweenLite 默认的缓动函数: Regular.easeOut. 必须使用 Flash Player 9 或之后版本的播放器 (ActionScript 3.0) 可以对任何 MovieClip 使用 “volume”缓动,就比如： TweenLite.to(myClip_mc, 1.5, {volume:0}); 可以将 MovieClip 设定成某种颜色，使用 “tint” 参数,比如: TweenLite.to(myClip_mc, 1.5, {tint:0xFF0000}); 想要对数组内容进行缓动，将数值放到一个叫 endArray的数组中即可，例如: var myArray:Array = [1,2,3,4]; TweenLite.to(myArray, 1.5, {endArray:[10,20,30,40]}); 可以在任何时候终止缓动，使用 TweenLite.killTweensOf(myClip_mc); 函数。如果想强制终止缓动，可以传递一个 true 做为第二个参数，比如 TweenLite.killTweensOf(myClip_mc, true); 取掉延迟回调函数，用 TweenLite.killDelayedCallsTo(myFunction_func); 这项功能可以用来控制回调函数的优先级。 使用 TweenLite.from() 方法，可以使用对象从别的位置回到当前的位置。例如，你可以将对象在舞台上摆放整齐（缓动结束时的位置），然后利用缓动，让它们跑到那个位置上去，你可以将缓动的初始位置值 x 或 y 或alpha (或者其它你需要的属性)当做参数传递给这个方法函数。 TweenLite 下载链接：\n（AS3版本）http://www.greensock.com/as/greensock-as3.zip （AS2版本）http://www.greensock.com/as/greensock-as2.zip 下载下来的类包中，有一个 TweenLiteAS3_Sample_1.swf， 初学者可以用它来观察各种缓动的效果，并且直接得到相关的执行代码。算是一个可视化设计的工具，不要错过。\n下载到类包以后，解压缩到一个目录比如：d:\\AS3Class ，在flash9的首选参数 －\u0026gt;ActionScript-\u0026gt;ActionScript3.0设置 中添加类目录，d:\\AS3Class\\TweenLiteAS3 即可正确引用到相关的类。\n应用举例：\n1import gs.TweenLite; 2import gs.easing.*; 3stage.addEventListener(MouseEvent.CLICK, onCK); 4function onCK(evt):void { 5TweenLite.to(mc, 0.5, {x:mouseX, y:mouseY, rotation:360}); 6} 在舞台上点击，会让mc 元件旋转并跑动到鼠标位置。\n","date":"2010-08-21","description":"","lastmod":"2010-08-21T06:00:00Z","slug":"tweenlite","tags":["as3","tween"],"title":"【转】TweenLite 使用详解","url":"https://blog.zengrong.net/post/tweenlite/"},{"categories":["technology"],"content":"2012-07-14更新：修改了部分不流利的翻译；修改了部分表述方式；加入了iOS的一些限制；重新排版。\n资讯类型: 翻译+修改 来源页面: http://richardleggett.co.uk/blog/index.php/2010/03/08/flash_builder_and_flash_pro_asset_workflows 资讯原标题: Flash/Flex Builder \u0026lt;-\u0026gt; Flash Professional Asset Workflows 资讯原作者: Richard Leggett 转自：http://bbs.9ria.com/viewthread.php?tid=46629 这篇文章描述了Flash/flex中载入图片等资源文件的几种方法，目前Flash最新的版本是Flash Pro CS4，CS5快要发行beta版。那么，我们来具体的看一看如何输出SWC文件，并通过[Embed]元标签来获得资源的方法。\n1. 背景 如果，你正在创建一个应用程序，一个游戏，或者一个网站。在设置你的Flash项目时，有两种选择。1你可以创建一个FLA文件，指定一个文档类进行编码。或者2，你也可以选择Flash/Flex Builder，FDT，Flash Develop等，创建一个Flex或者AS3项目，然后使用Flex SDK编译它。几乎每一次，我都选择后者，因为后者增强了程序的可靠性，而且编译速度更快。\n即使你选择了使用Flash Pro创建并编译FLA文件，你也可能会在Flash Builder或者其他IDE中修改代码。但问题是，之前你使用的是Flash Pro编译，接着要使用Flex SDK进行编译。写这篇东西的目的是，当我们使用Flex SDK编译Flex或者AS3项目时，我们如何通过FLA载入资源的。\n我尽可能的将我所知道的写出来，但一定会有些技巧和窍门，甚至是方法，是我所不知道的。如果你发现任何错漏的地方，请一定让我知道，我会尽快修正。\n为什么非要是FLA文件？ 你可能已经知道，可以将PNG图像，SVG和其他文件类型嵌入到你的类中，并且永远也不需要通过FLA文件获取图形。当涉及到动画，你可以使用TweenLite或者GTween。但是涉及到帧动画，角色动画，或者干脆是按钮和手绘的元件时，你可能需要使用Flash Pro上强大的时间轴，图形和动画工具去创建元件，并使它们动起来，这时就需要FLA了。\n这时候你该问问自己，如果我的项目不使用Flash Pro编译，那么如何从FLA中获取资源导入到项目中去？\n2. 工作流程 这里有五种将FLA中的资源嵌入到Flex或者纯AS3项目中的方法。为了照顾通用性，我已经排除了那些只适用于MXML的方法。\n2.1 通过FLA发布SWC 这个方法涉及到库元素链接类，你必须在元素链接中写明类的路径，例如“com.package.MyClass”。接着你必须选择在“Flash发布设置面板中”勾选“导出SWC”，然后打开“AS3设置面板”勾选“自动申明舞台实例”选项以避免错误。最后，添加所有必须的类路径以避免在编译的时候出错。\n当你发布SWF文件时，SWC文件也会被发布到同个文件夹中。你将这个SWC文件加入到你的AS3项目中，就可以通过编码使用里面的类/元件了。\n优点：\n这个方法保持了时间轴上的as代码，适合复杂的，嵌套或者多个状态的动画。\n缺点：\n当你改变元件链接的类时，你必须重新编译整个FLA文件。这意味着又要打开Flash，导出SWC文件，再导回Flash Builder，刷新项目才能够重新引用新的SWC文件，最后再重新编译整个项目。 你还必须确保，元件链接的类不在项目的源目录（或项目映射的源代码目录）。如果你不这样做，你可能看不到你的图形/动画，因为Flex链接器会因为编译顺序而寻找类别定义，从而忽略SWC。 你必须添加所有必要的类路径进FLA。 Flash builder将不会报错，而且你也不能通过Ctrl/Cmd+左键点击跳到源文件中。 你不能立即在舞台上使用它们，解决办法是相当痛苦的（见 这里）。 总结：\n虽然这是保持时间轴代码的唯一方法，但缺点使它变得不直观，同时令人沮丧。如果谁能提供一个改善的方法，我会请你喝啤酒。\n2.2 在申明类之前使用 [Embed] 标签\n1package my.package { 2 [Embed(source=\u0026#34;assets/some.swf\u0026#34;, symbol=\u0026#34;SymbolName\u0026#34;)] 3 public class MyClass { 4 // code 5 } 6} 在这里我们仅仅使用一个FLA生成一个SWF来存储我们的元件。FLA中的元件没有做任何类绑定。库中只是充满了MovieClip。在我们的类文件中，我们添加[Embed]标签，接着绑定SWF文件中的元件。所以，当我们创建这个类的实例时，我们就能得到元件库中的图形。\n优点：\n你不必重新编译FLA文件，除非你改变FLA中的图形。 你可以在编码环境上花更多的时间，而不是在不停的来回捣腾。 你可以实时的获得编译错误，因为代码不是来自一个SWC文件。 缺点：\n它会取消元件中时间轴上的所有AS代码。换句话说，你在SWF中写的所有的AS代码都无效了。如果你的元件是一个MovieClip，而且有几帧中有stop命令。那么你的动画就会不停循环。可以使用addFrameScript(5, stop)来解决这个问题，但addFrameScript是一个未公开的方法，你不知道什么时候它会被取消。 还可以使用大量的帧标签作为“元标签”进行代码替换（依然是利用addFrameSCript），这又自成一篇http://code.google.com/p/as3-asset-modifier/。 2.3 [Embed] 标签在类属性前声明\n1[Embed(source=\u0026#34;assets/some.swf\u0026#34;, symbol=\u0026#34;MySymbol\u0026#34;)] 2private var MySymbol:Class; 3// later on in a function... 4var myInstance:Sprite = new MySymbol(); 和第2种用法类似，这种方法也会删除帧上的代码。\n采用这种方法，你要么实例化一个SpriteAsset（扩展自Sprite），要么是一个MovieClipAsset（扩展自MovieClip）。注意，如果在Flash中的MovieClip只有一帧，那么其实它是一个SpriteAssets。\n2.4 用 [Embed] 标签载入整个SWF\n这里还有另外一个可用的属性可以提供给Embed元标签，就是mimeType。如果你删除元件属性，并让mimeType=application/octet-stream” 。将会嵌入整个SWF，并且保持库中的类关联。\n当你使用这种方法从一个SWF中嵌入一个元件,你可以使用Loader去获得类，像这样:\n1[Embed(source=\u0026#34;assets/some.swf\u0026#34;, mimeType=\u0026#34;application/octet-stream\u0026#34;)] 2private var MySWF:Class; 3// within a method 4var bytes:ByteArray = (new MySWF() as ByteArray); 5var loader:Loader = new Loader(); 6loader.loadBytes(bytes, new LoaderContext(false, ApplicationDomain.currentDomain)); 7// wait for loader to dispatch Event.COMPLETE and... 8var myClass:Class = loader.contentLoaderInfo.applicationDomain.getDefinition(\u0026#34;com.package.MyClass\u0026#34;); 9var myInstance:DisplayObject = new myClass(); 10// myInstance is now an instance of the class linked to it 优点：\n这是一个很棒的方法去获得资源而不必重新编译FLA\n缺点：\n如果你有很多元件，或者很多嵌套了很多其他东西的元件，这种方法就不是一个理想的选择。即使库能提供很大的帮助。但是……它没有严格定义类型。 在生成iOS应用的时候，不能使用这种方法。因为iOS应用不允许你载入一个“可以执行的swf”。 2.5 运行时载入一个SWF 这也许是最古老的一个方法。在运行时加载SWF可以使用applicationDomain.getDefinition()获得元件/类，和方法3有点类似。如果你已经熟悉了getDefinition（）方法，那问题就简单得多了。但你针对的只是一个特定的SWF中的类。\nzrong: 这里也有一篇关于这种方法的介绍\n优点：\n适用于很少改变的内容，比如字体\n缺点：\n神奇的字符串:定义一个字符串常量不会有别的什么作用，如果你改变FLA中的字符串，你的常量就变得毫无意义。并且你只有在那片代码执行的时候才能才会发现。 你必须从FLA中导出你的SWF，或使用其他技术。这意味着你会遇到其他的一些问题。 在生成iOS应用的时候，不能使用这种方法。因为iOS应用不允许你载入一个“可以执行的swf”。 总的来说，我觉得每一种方法都有缺陷，所以要慎重做出选择。目前我还没有找到一个完美的方案。能够使用Flash Pro加快处理游戏资源，在舞台上放置资源，在时间轴上做动画……但这样做弊大于利，把读取资源的工作搞复杂了。我希望在未来能加强工具间的联系，或许能通过一个新的文件格式来实现（XFL：http://www.moock.org/blog/archives/000269.html）。\n或者看这里：http://www.riaidea.com/article.asp?id=36\n3. 延伸阅读 http://www.bit-101.com/blog/?p=853 http://www.bit-101.com/blog/?p=864 http://gskinner.com/blog/archives/2007/03/using_flash_sym.html http://www.airtightinteractive.com/news/?p=327 ","date":"2010-08-19","description":"","lastmod":"2010-08-19T14:49:40Z","slug":"flash-professional-assets-loading","tags":["embed","flash","flex","flexbuilder","loader","reflection"],"title":"【转】Flash/Actionscript3 载入资源文件方法考","url":"https://blog.zengrong.net/post/flash-professional-assets-loading/"},{"categories":["technology"],"content":"在MouseEvent中，ROLL_OVER 和 MOUSE_OVER 、ROLL_OUT 和 MOUSE_OUT 是两对比较相似的事件，它们有什么区别呢？AS3语言参考中是这样解释的：\nrollOver 事件的目的是简化带有子级的显示对象容器的移开行为的编码。 当鼠标进入某个显示对象区域或者从其子级以外的对象进入任何其子级区域时， 该显示对象将分派 rollOver 事件。这种行为与 mouseOver 事件的行为不同， 每次鼠标进入显示对象容器的任何子对象区域时都会分派此事件， 即使鼠标已在显示对象容器的另一个子对象上也是如此。\n解释得有些拗口，实际上简单说就是： ROLL_OVER 事件无视子对象，只监听根对象的事件。\n看了下面演示，就更清楚了。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n源码：\n1package 2{ 3 import flash.display.Sprite; 4 import flash.events.MouseEvent; 5 import flash.text.TextField; 6 7 [SWF(width=350,height=400)] 8 public class SimpleSample9 extends Sprite 9 { 10 private var _spriteChild:Sprite; 11 private var _spriteParent:Sprite; 12 private var _tf:TextField; 13 private var _line:int=0; 14 15 public function SimpleSample9() 16 { 17 _spriteChild = new Sprite(); 18 _spriteChild.name = \u0026#39;child\u0026#39;; 19 _spriteChild.graphics.beginFill(0x81ADF0); 20 _spriteChild.graphics.drawCircle(0, 0, 40); 21 _spriteChild.graphics.endFill(); 22 23 _spriteParent = new Sprite(); 24 _spriteParent.name = \u0026#39;parent\u0026#39;; 25 _spriteParent.graphics.beginFill(0x0000ff); 26 _spriteParent.graphics.drawCircle(0,0, 90); 27 _spriteParent.graphics.endFill(); 28 29 _spriteParent.addChild(_spriteChild); 30 addChild(_spriteParent); 31 _spriteParent.x = stage.stageWidth/2; 32 _spriteParent.y = _spriteParent.height/2; 33 34 _tf = new TextField(); 35 _tf.border = true; 36 _tf.width = stage.stageWidth; 37 _tf.height = stage.stageHeight-_spriteParent.y-_spriteParent.height/2; 38 _tf.y = stage.stageHeight - _tf.height; 39 addChild(_tf); 40 41 _spriteParent.addEventListener(MouseEvent.MOUSE_OVER, mouseHandler); 42 _spriteParent.addEventListener(MouseEvent.MOUSE_OUT, mouseHandler); 43 _spriteParent.addEventListener(MouseEvent.ROLL_OVER, mouseHandler); 44 _spriteParent.addEventListener(MouseEvent.ROLL_OUT, mouseHandler); 45 } 46 47 private function mouseHandler(evt:MouseEvent):void 48 { 49 _line++; 50 var __ctName:String = evt.currentTarget.name; 51 var __tName:String = evt.target.name; 52 var __rName:String = evt.relatedObject == null ? \u0026#39;null\u0026#39; : evt.relatedObject.name; 53 _tf.text = _line.toString()+\u0026#39;,\u0026#39;+(evt.type+\u0026#39;,currentTarget:\u0026#39;+__ctName+\u0026#39;,target:\u0026#39;+__tName+\u0026#39;,relatedObject:\u0026#39;+__rName+\u0026#39;\\n\u0026#39;) + _tf.text; 54 } 55 } 56} ","date":"2010-08-19","description":"","lastmod":"2010-08-19T02:26:45Z","slug":"mouseevent-roll_over-and-mouseevent-mouse-over","tags":["as3"],"title":"MouseEvent.ROLL_OVER和MouseEvent.MOUSE_OVER的区别","url":"https://blog.zengrong.net/post/mouseevent-roll_over-and-mouseevent-mouse-over/"},{"categories":["news"],"content":"http://swfever.com/?p=871\n2015-10-08更新：\n由于原作者网站已经不能访问，现提供 XMind 的 share 地址（原作者上传） 或者直接 点击查看大图 。\n","date":"2010-08-18","description":"","lastmod":"2010-08-18T14:44:57Z","slug":"flash-platform-skill-tree","tags":["flash","study"],"title":"Flash平台开发者\"技能树\"","url":"https://blog.zengrong.net/post/flash-platform-skill-tree/"},{"categories":["technology"],"content":"刚刚发的那个 获取任意位置与圆直径形成的等腰三角形的底边与圆的交点的坐标 其实没有多大用处，而这个用处就大了，可以在游戏中进行碰撞检测，在碰到圆形障碍物的时候求出最短的运行路径。\n原理图 运行效果 请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n源码 SimpleSample6.as\n1/** 2 * 计算鼠标位置与圆的切点坐标 3 * */ 4package 5{ 6 import flash.display.Sprite; 7 import flash.events.MouseEvent; 8 9 [SWF(width=400,height=400)] 10 public class SimpleSample6 extends Sprite 11 { 12 private var _centerX:Number; 13 private var _centerY:Number; 14 private var _radius:Number = 80; 15 16 public function SimpleSample6() 17 { 18 _centerX = stage.stageWidth/2; 19 _centerY = stage.stageHeight/2; 20 draw(); 21 drawTriangle(0, 0); 22 stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); 23 } 24 25 private function draw():void 26 { 27 graphics.clear(); 28 graphics.lineStyle(1); 29 graphics.moveTo(0, stage.stageHeight/2); 30 graphics.lineTo(stage.stageWidth, _centerY); 31 graphics.moveTo(_centerX, 0); 32 graphics.lineTo(_centerX, stage.stageHeight); 33 graphics.drawCircle(_centerX, _centerY, _radius); 34 } 35 36 private function drawTriangle($mouseX:Number, $mouseY:Number):void 37 { 38 var __dx:Number = _centerX - $mouseX; 39 var __dy:Number = _centerY - $mouseY; 40 //计算点击处与圆心相对于X轴的夹角 41 var __r1:Number = Math.atan2(__dy, __dx); 42 //计算点击处与圆心、点击处与切点1这两条线段间的夹角 43 var __d1:Number = Math.sqrt(__dx*__dx + __dy*__dy); 44 var __r2:Number = Math.asin(_radius/__d1); 45 //计算从切点1向圆的垂直直径做垂线形成的直角三角形的一个角 46 var __r3:Number = __r1 - __r2; 47 //计算坐标系中的角度 48 var __r4:Number = __r3 - Math.PI/2; 49 //计算切点1相对于圆心的x、y坐标 50 var __x1:Number = _radius * Math.cos(__r4); 51 var __y1:Number = _radius * Math.sin(__r4); 52 53 //计算点击处与切线2相对于X轴的夹角 54 var __r5:Number = Math.PI/2 - __r1 - __r2; 55 //计算坐标系中的角度 56 var __r6:Number = -__r5; 57 //计算切点2相对于圆心的x、y坐标 58 var __x2:Number = _radius * Math.cos(__r6); 59 var __y2:Number = _radius * Math.sin(__r6); 60 61 graphics.moveTo(_centerX, _centerY); 62 graphics.lineTo($mouseX, $mouseY); 63 graphics.lineTo(_centerX+__x1, _centerY+__y1); 64 graphics.lineTo(_centerX, _centerY); 65 graphics.lineTo(_centerX-__x2, _centerY-__y2); 66 graphics.lineTo($mouseX, $mouseY); 67 } 68 69 private function mouseMoveHandler(evt:MouseEvent):void 70 { 71 draw(); 72 drawTriangle(mouseX, mouseY); 73 } 74 } 75} ","date":"2010-08-18","description":"","lastmod":"2010-08-18T07:40:11Z","slug":"circle-hit-test2","tags":["as3","math"],"title":"获取任意位置与圆的切点的坐标","url":"https://blog.zengrong.net/post/circle-hit-test2/"},{"categories":["technology"],"content":"原理图 效果 请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n源码 1package 2{ 3 import flash.display.Sprite; 4 import flash.events.MouseEvent; 5 6 [SWF(width=400,height=400)] 7 public class SimpleSample5 extends Sprite 8 { 9 private var _centerX:Number; 10 private var _centerY:Number; 11 private var _radius:Number = 80; 12 13 public function SimpleSample5() 14 { 15 _centerX = stage.stageWidth/2; 16 _centerY = stage.stageHeight/2; 17 draw(); 18 drawTriangle(0, 0); 19 stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); 20 } 21 22 private function draw():void 23 { 24 graphics.clear(); 25 graphics.lineStyle(1); 26 graphics.moveTo(0, stage.stageHeight/2); 27 graphics.lineTo(stage.stageWidth, _centerY); 28 graphics.moveTo(_centerX, 0); 29 graphics.lineTo(_centerX, stage.stageHeight); 30 graphics.drawCircle(_centerX, _centerY, _radius); 31 } 32 33 private function drawTriangle($mouseX:Number, $mouseY:Number):void 34 { 35 var __dx:Number = _centerX - $mouseX; 36 var __dy:Number = _centerY - $mouseY; 37 var __distance:Number = Math.sqrt(__dx*__dx + __dy*__dy); 38 //计算点击处与圆心夹角的角度并在坐标系中旋转90度 39 var __radian:Number = Math.atan2(__dy, __dx) - Math.PI/2; 40 var __x2:Number = _radius * Math.cos(__radian); 41 var __y2:Number = _radius * Math.sin(__radian); 42 graphics.moveTo(_centerX, _centerY); 43 graphics.lineTo($mouseX, $mouseY); 44 graphics.lineTo(_centerX+__x2, _centerY+__y2); 45 graphics.lineTo(_centerX, _centerY); 46 graphics.lineTo(_centerX-__x2, _centerY-__y2); 47 graphics.lineTo($mouseX, $mouseY); 48 } 49 50 private function mouseMoveHandler(evt:MouseEvent):void 51 { 52 draw(); 53 drawTriangle(mouseX, mouseY); 54 } 55 } 56} ","date":"2010-08-18","description":"","lastmod":"2010-08-18T03:43:30Z","slug":"circle-hit-test","tags":["as3","math"],"title":"获取任意位置与圆直径形成的等腰三角形的底边与圆的交点的坐标","url":"https://blog.zengrong.net/post/circle-hit-test/"},{"categories":["technology"],"content":"转自InfoQ\n不久前，Michael Portuesi发表了一篇博文，谈到了Flex开发者需要知道的10件事。文章介绍了每个进入Flex领域的开发者都需要掌握的基本知识与技能。\nMichael Portuesi给出的10个条目中，有些是开发者需要了解的简单细节信息；有些则揭示了Flash/ActionScript/Flex与其他开发环境之间的差别。\n如果你了解HTML/CSS并熟悉JavaScript，但却对ActionScript或Flex一无所知的话，那么应该花些时间学习一下面向对象编程，因为ActionScript是一门完全的面向对象编程语言，而Flex则是一个面向对象的框架。\n1. 再简单的东西也是异步的 Flex是一个异步框架，因此我们绝对不能指望代码调用后就能立刻执行。事实上，我们是无法预知方法的调用序列的。\n2. 搞清楚Flex组件的样式与属性 Flex UI组件（按钮、菜单等等）既有属性（通过ActionScript语言指定）也有样式（通过Flex框架指定）。搞清楚他们之间的区别是非常重要的，因为组件的某些可视化效果可以通过属性指定，但另一些却只能通过样式设定。通过属性指定：\nbutton.width = 100; button.height = 50; 通过样式指定：\n\u0026lt;mx:Style\u0026gt; Button { color: #cc0000; textRollOverColor: #ccff00; fontFamily: Trebuchet MS; } \u0026lt;/mx:Style\u0026gt; \u0026lt;mx:Button id=\u0026quot;setupB\u0026quot; text=\u0026quot;Click Me\u0026quot; click=\u0026quot;onSetup()\u0026quot; /\u0026gt; 3. Flex中的样式与HTML中的不尽相同 可以使用标准的CSS样式表来为Flex组件添加样式，也可以在Flex应用中包含CSS样式表。虽然标准CSS使用连字符（例如text-font）格式来定义样式名称，但是Flex使用驼峰式的命名格式（例如textFont）。这是因为连字符不能出现在XML的属性中，所以不能用这样的名字作为MXML标签的属性。\n当然了，如果把样式定义在外部的CSS文件中或者Style标签中，也可以使用连字符格式的样式名。此外，Flex还定义了很多HTML中不存在的CSS样式。\n4. 尽管看起来不同，但MXML和ActionScript本质上是一回事 在Flex中声明的所有MXML标签都会被Flex编译器转换为ActionScript代码；当然了，也可以在MXML文件中嵌入内联的ActionScript代码。既可以使用MXML也可以使用ActionScript创建新组件。\n5. 理解Flex的Code-behind模式 虽然MXML和ActionScript本质上是一样的，但他们各司其职。一般来说，MXML负责显示界面，而ActionScript用来完成功能。Code-behind用于解耦MXML和ActionScript，这样设计师可以直接修改MXML而无需阅读代码，程序员则可以更好地组织和重用功能。\n6. 理解Flex组件的生命周期 Flex通过状态机机制定义了一套完美的生命周期模型，用于组件的创建、运行和销毁，还定义了一些“入口”，开发者可以借此完成定制化的工作。没有透彻理解组件的生命周期可能会导致错误的编程模型。\n7. 理解Flash运行时所使用的“跑道”模型 理解Flash Player的渲染和代码执行机制非常重要的。在执行了改变界面的指令时，Flash Player并不是立刻把你要的内容显示在屏幕上，它根据一定的周期来刷新屏幕，而代码的执行则是另一回事。这和Java正好相反，Java总是等待程序主动告诉它什么时候重绘屏幕。\n8. 理解数据绑定与查看器（Watcher） Flex提供了一种数据绑定机制。简单地说，就是将一个源属性绑定到一个目标属性上，当源属性发生变化时，目标属性也会随之变化。不仅仅可以绑定到属性，还可以绑定到函数。甚至可以为某个属性创建一个Watcher，当属性变化时会获得事件通知。\n9. 数据封装与松耦合非常重要 对于Flex和AIR项目来说，代码组织与高层结构非常重要。有些人竟然在一个文件中编写了1000多行代码，这导致的问题就是牵一发而动全身。\n10. 理解ActionScript中的弱引用与强引用 不管使用何种语言与开发环境，内存管理始终是一个重要的问题，ActionScript也不例外。如果不理解运行时环境的内存管理，那么很容易就会出现内存泄露与内存碎片问题。请阅读这篇博文及文章来深入了解ActionScript的垃圾收集机制。\n","date":"2010-08-16","description":"","lastmod":"2010-08-16T03:59:26Z","slug":"flex-ten-things","tags":["flash","flex","study"],"title":"【转】Flex开发者需要知道的10件事","url":"https://blog.zengrong.net/post/flex-ten-things/"},{"categories":["technology"],"content":"转自InfoQ\n近日John Lindquist谈到了在为Roundarch公司招聘Flash/Flex开发人员时的一些感受。他认为最难的地方在于问什么问题才能最好地了解到应聘者的Flash/Flex开发技能。因此，他给出了一个列表并说到：“根据我的经验，通过这个列表能更好地判断面试者的技术水平和经验”。\n本文就将概要地介绍Flash开发者需要知道的10件事并给出进一步阅读的链接。\n1. 弹性“跑道“模型 开发者应该知道事件何时被触发，代码何时被执行，Player何时进行渲染，这是每个Flash开发者都需要掌握的Flash Player基础概念。\n延伸阅读\nhttp://www.craftymind.com/2008/04/18/updated-elastic-racetrack-for-flash-9-and-avm2/\n2. FlexSDK、mxmlc、compc... 开发者应该知道在点击Eclipse中的“run”按钮时都发生了哪些事情。\n延伸阅读\nhttp://www.senocular.com/flash/tutorials/as3withmxmlc/ http://livedocs.adobe.com/flex/3/html/help.html?content=apparch_08.html 3. Player事件、客户化事件以及事件冒泡 没有帧（Frame）和鼠标点击的Flash Player是无法想象的。\n延伸阅读\nhttp://www.adobe.com/devnet/actionscript/articles/event_handling_as3_03.html http://livedocs.adobe.com/flex/3/langref/flash/events/package-detail.html http://www.tink.ws/blog/custom-events-in-as-30-dont-forget-to-override-the-clone-method/ http://jacwright.com/blog/70/how-to-listen-to-flash-events-that-dont-bubble/ 4. 语句、关键字和指令 开发者不应该对AS3中新出现的关键字感到惊讶。如果不知道“static”或是“override”是什么意思，学就行了。\n延伸阅读\nhttp://livedocs.adobe.com/flex/2/langref/statements.html\n5. ASDoc 今后当你再一次阅读自己编写的代码时可能看不懂写的是什么了，因此恰当的文档是非常必要的，但如果能自动生成岂不美哉？\n延伸阅读\nhttp://livedocs.adobe.com/flex/3/html/help.html?content=asdoc_3.html\n6. 管理好可视化资源（图片、字体、CSS等等） Flash是可视化的东西，因此有必要了解代码与资源的管理手段。\n延伸阅读\nhttp://www.gskinner.com/talks/flexlovesflash/ http://code.google.com/p/queueloader-as3/ http://code.google.com/p/bulk-loader/ 7. 理解Array、Collection、Dictionary与Map 通常开发者不会只处理一个MovieClip，因此需要了解如何控制多个对象。\n延伸阅读\nhttp://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/Array.html http://www.gskinner.com/blog/archives/2006/07/as3_dictionary.html http://code.google.com/p/as3ds/ 8. 以编程的方式处理动作 如果只搞静态的东西还不如使用HTML呢。\n延伸阅读\nhttp://blog.greensock.com/tweenmaxas3/ http://www.amazon.com/Foundation-Actionscript-3-0-Animation-Making/dp/1590597915/ref=sr_1_1?ie=UTF8\u0026amp;s=books\u0026amp;qid=1243014431\u0026amp;sr=8-1 9. OOP、编码与框架的合理使用 时至今日，一个巨大的.as文件闯天下的时代已经过去了。开发者需要将代码转到组织良好、可重用的对象中。现在的AS3框架也层出不穷，合理地使用框架有助于代码的管理。\n延伸阅读\nhttp://www.adobe.com/devnet/actionscript/articles/oop_as3.html http://www.actionscript.org/resources/articles/684/1/Object-Oriented-Programming-in-AS3/Page1.html 10. 版本控制 除非觉得代码毫无价值，否则开发者需要立刻学习一种版本控制系统并尽快应用到项目中去。\n延伸阅读\nhttp://tortoisesvn.net/downloads http://versionsapp.com/ http://en.wikipedia.org/wiki/Revision_control http://git-scm.com/ http://www.nongnu.org/cvs/ ","date":"2010-08-16","description":"","lastmod":"2010-08-16T03:56:53Z","slug":"flash-ten-things","tags":["flash","flex","study"],"title":"【转】Flash开发者需要知道的10件事","url":"https://blog.zengrong.net/post/flash-ten-things/"},{"categories":["news"],"content":"今天在论坛中发求助帖，发现原来发的一个关于AIR实现模式窗口的问题有人回复了，这里记录一下。\n解决方法：中文 英文\n","date":"2010-08-15","description":"","lastmod":"2010-08-15T14:28:32Z","slug":"1093","tags":["air"],"title":"AIR模式窗口","url":"https://blog.zengrong.net/post/1093/"},{"categories":["others"],"content":"妈妈：淇淇，你已经这么大了，该自己睡觉了。\n淇淇：我是要保护你撒，要是你被大灰狼吃了怎么办列？\n","date":"2010-08-04","description":"","lastmod":"2010-08-04T00:02:15Z","slug":"1092","tags":["qiqi"],"title":"淇淇语录之大灰狼","url":"https://blog.zengrong.net/post/1092/"},{"categories":["news"],"content":"本文转自冰山软件站\nassoc命令 要修改扩展名与文件类型的关联，我们需要用到一个命令：assoc\n点开始，搜索，输入cmd，打开cmd,在cmd中输入：assoc /? 回车。我们可以得到关于这个命令的帮助。\nassoc命令的作用有两个，一是显示扩展名与文件类型的关联，一是改写扩展名与文件类型的关联。\n让我们以快捷方式的关联为例来讲述这个问题。\n快捷方式的扩展名是.lnk，在windows下，这个扩展名一般是不会显现出来的，如果出现了，必是它的文件关联出问题了。\n我们在cmd中输入：assoc .lnk\n回车，我们可以得到的命令结果是：\n.lnk=lnkfile\n这个命令结果显示了：扩展名.lnk与文件类型lnkfile是相关联的。凡是扩展名为.lnk的文件，都属于lnkfile这个文件类型。\n现在让我们输入命令：\nassoc .lnk=lnk\n回车。.lnk这个扩展名与文件类型的关联就被改写了。然后我们再输入命令：\nassoc .lnk\n回车。我们得到的命令结果将是:\n.lnk=lnk\n这个结果显示了，扩展名.lnk已经是与文件类型lnk相关联，而不再是与lnkfile相关联。\n这个时候你去桌面上去看一下吧，你桌面上所有的快捷方式的后缀名.lnk全部显示出来了，所有的快捷方式都不再可用。双击时会提示：windows无法打开此文件。\n现在再让我们输入：\nassoc .lnk=lnkfile\n回车。我们将会发现，一切又恢复了原状。\n总之，如果是快捷方式的扩展名与文件类型之间的关联出现了问题，我们只需要输入：\nassoc .lnk=lnkfile\n回车后即可以解决问题。\n这样，如果我们知道一种扩展名所属的正确的文件类型，当这种扩展名与文件类型之间的关联出现问题的时候，我们只需要输入：\nassoc .ext=filetype\n回车即可。\n这个.ext代表的是文件扩展名，比如.lnk，这个filetype代表的是文件类型，比如lnkfile。如果我们不知道一种扩展名所关联的文件类型是什么，我们只需要在cmd中输入：assoc .ext回车。我们就能得到它所关联的文件类型是什么。\n每一个注册了的扩展名都会在注册表中存在着一个对应的注册表项：\nHKEY_CLASSES_ROOT\\.ext\n这里的.ext代表的是扩展名，比如快捷方式对应的项就是：\nHKEY_CLASSES_ROOT\\.lnk\n这个项有一个默认的值，这个默认的值的数据，就是这个快捷方式所关联的文件类型。因而assoc命令，主要的就是修改这个项的默认值的数据。但 需要注意的是，assoc命令所修改的内容，并不仅仅是这个项的默认值的数据，它还会要修改其它的一些方面。所以我们直接在注册表中改这个值的数据，并不 能完全代替assoc命令。\nfytpe命令 我们现在知道了如何把扩展名与文件类型关联起来了，进一步地需要知道的是如何把文件类型与windows命令关联起来。比如.txt扩展名所关联的文件 类型是txtfile,而txtfile正常的情形下，总是用notepad.exe（记事本）来打开的，这个notepad.exe就是打开 txtfile文件类型的windows命令。\n如何来修改这种文件类型与windows命令之间的关联呢？这需要用到另外的一个重要的命令：ftype\n让我们在cmd中输入：\nftype /? 回车。我们可以得到这个命令的帮助。\nftype命令有两个作用，一是显示文件类型与windows命令之间的关联，一是改写文件类型与windows命令之间的关联。\n如果我们想知道一种文件类型与什么样的windows命令相关联，我们只需要在cmd中输入：\nftype fileType\n回车。我们就能够得到我们想要得到的结果。这个fileType代表的是指定的文件类型，比如lnkfile\n让我们输入：ftype lnkfile\n回车。正常情形下我们得到的命令结果是：\n没有找到文件类型“lnkfile”或者与其相关的windows命令\n这个结果表明了，正常的情形下，lnkfile是没有与任何windows命令相关联的。\n让我们输入：ftype lnkfile=notepad.exe\n回车。让我们再输入：\nftype lnkfile\n回车，我们将得到的命令结果是：\nlnkfile=notepad.exe\n这个结果表明了：文件类型lnkfile就与windows命令notepad.exe关联上了。\n这种情形下，仍然并不会影响快捷方式的打开。\n那么，如何来清除这个关联，并且不与其它的windows命令关联呢？我们只要输入：\nftype lnkfile=\n回车即可。在xp下，这个命令无效，但是，我们可以在xp下输入：\nftype lnkfile=\n回车。这个命令与前一个命令在外表上几乎看不出分别，分别就在于，前一个命令在＝后面没有空格，而后一个命令在＝后有一个空格。\n让我们输入：\nftype txtfile\n回车。正常情形下，我们可以得到的命令结果是：\ntxtfile=\u0026quot;%SystemRoot%\\system32\\NOTEPAD.EXE\u0026quot; %1\n这个结果表明了：文件类型txtfile与windows命令txtfile=\u0026quot;%SystemRoot%\\system32\\NOTEPAD.EXE\u0026quot; %1相关联。\n如果一种文件类型与windows命令之间的关联出现了问题，而我们知道正确的windows命令是什么，这时我们只需要在cmd中输入如下命令并回车即可修复：\nftype fileType=openCommandString\n这里fileType代表的是指定的文件类型，比如txtfile,这里openCommandString代表的是windows命令，比如notepad.exe\n如果我们的txtfile与windows命令之间的关联出了问题，我们只需要在cmd中输入：\nftype txtfile=\u0026quot;%SystemRoot%\\system32\\NOTEPAD.EXE\u0026quot; %1\n回车。这样，我们也就修复了txtfile文件类型与它的windows命令之间的关联。\n所谓windows命令，其实就是对这种文件类型的打开方式。\n每一种注册了的文件类型，在注册表中都会存在着它的一个对应的注册表项，这个注册表项就是：\nHKEY_CLASSES_ROOT\\filetype\n这个filetype代表的是文件类型，比如batfile文件类型所对应的注册表项就是：\nHKEY_CLASSES_ROOT\\batfile\nftype命令所修改的注册表项主要就是---HKEY_CLASSES_ROOT\\filetype\\shell\\open\\command---这个项的默认值的数据。\n但需要注意的是，ftype命令所修改的并不仅仅是这个默认值的数据。因而直接在注册表中修改这个数据，并不能代替ftype命令的修改。\n当然，ftype命令所修改的全部的东西都可以在注册表中找到，但是，我们那样一一地去找，远不如用ftype命令简单修改来得爽。\n右键打开方式 前面我们谈到了扩展名与文件类型的关联，文件类型与windows命令的关联（也就是文件的打开方式），看起来好象我们关于文件关联的问题就谈完了，实 则不然，还有另外的一个重要的方面我们没有谈到。这就是我们右击一个文件，选择打开方式（并不是所有文件右键都有打开方式这个选项的），然后我们选择一个 程序，并把“始终使用这种程序打开同类型文件”前的勾选上，点确认。比如本文开头的那位朋友，把所有快捷方式都选择以word程序打开一样。这样之后，这 个文件类型也就与这个windows命令关联起来了。\nftype命令是修改文件类型与windows命令的关联的，而右键打开方式也可以修改文件类型与windows命令的关联，这二者的关系是什么呢？\n我们发现，ftype命令和右键打开方式，这二者所修改的注册表项是不同的。Ftype命令所修改的注册表项是：\nHKEY_CLASSES_ROOT\\filetype\n这个filetype代表的是文件类型，比如HKEY_CLASSES_ROOT\\lnkfile\n而右键打开方式所修改的主要注册表项是：\nHKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.ext\n这个.ext代表的是文件扩展名，比如：\nHKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.lnk\n按照我的理解，ftype命令所修改的内容属于系统设置，而右键打开方式所修改的内容则属于用户设置，这二者所设置的实际对象是一样的，当二者不一致的时候，用户设置优先于系统设置。\n三个注册表项 总之，文件关联所涉及到的注册表项主要是三个：\nHKEY_CLASSES_ROOT\\.ext\nHKEY_CLASSES_ROOT\\filetype\nHKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.ext\n问题解决\n现在要回到我们在开头所说到的问题了。\n我教那位朋友首先输入：\nassoc .lnk\n返回的命令结果是：\n.lnk=lnkfile\n这表明，扩展名.lnk与文件类型lnkfile之间的关联没有问题。\n我再要他输入：\nftype lnkfile\n返回的命令结果是：\n没有找到文件类型“lnkfile”或者与其相关的windows命令\n而这个结果是正常的，这表明，系统设置中的lnkfile与windows命令之间的关联也没有问题。\n那么，问题只能出在用户设置中的lnkfile与windows命令之间的关联出现了问题。\n一般的情形下，右键打开方式的设置优先于ftype命令对文件关联的设置，这在xp和Windows 7下都是一样的，但对于扩展名为.lnk的快捷方式，二者却具有不同。在Windows 7下，即便是对于.lnk快捷方式，也是右键打开方式的设置优先于ftype命令的设置。\n找到了问题所在，我们就可以知道，对于这种快捷方式的文件关联错误，我们用assoc和ftype命令都是无法解决的。解决的办法就是：\n在注册表中右键删除下面这个注册表项：\nHKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\.lnk\\UserChoice\n这个注册表项下的值和数据（具体就是名为progid的值和它的数据），是对应右键打开方式所设置的默认打开程序的。\n在删除后，重启电脑，或者重启explorer，问题完美得到解决。\n除了这种解决办法，其它的解决办法暂时没有找到。\n顺便说一下，在Windows XP下，UserChoice这个注册表项是没有的。\n由于一些文件右键没有打开方式选项，在xp下，我们可以在我的电脑里面点工具----查看----文件夹选项---文件类型，这里我们可以进行 同样的设置，而在Windows 7下，我们可以在----控制面板---程序---默认程序----始终使用指定的程序打开此文类型---里面进行设置，xp下设置的可选项要多得多，而 Windows 7下则的设置则极为简明。\n","date":"2010-07-31","description":"","lastmod":"2010-07-31T08:03:14Z","slug":"1089","tags":["windows"],"title":"【转】详解Windows 7中修改文件关联的方法","url":"https://blog.zengrong.net/post/1089/"},{"categories":["use"],"content":"在windows7旗舰版32位下安装招行专业版出现错误提示“复制PersonalbankMain.ocx文件失败”。\n用管理员权限安装，并设置兼容性为windows xp，重启电脑都没有效果，错误依旧。\n记得原来用正版的windows7专业版是可以正常安装招行专业版的，换了萝卜花园版的盗版win7就不行了。\n上Google搜索，原来是盗版精简了某些文件所致。解决方法如下：\n上网下载了被精简掉的“HHCTRL.OCX”； 复制到“c:\\windows\\system32”文件夹下； 运行下面的代码注册：\nregsvr32 hhctrl.ocx 重新安装招行专业版。 现在又发现chm文件无法打开，郁闷，用盗版就是这么麻烦啊……\n附hhctrl.ocx下载地址。\n","date":"2010-07-31","description":"","lastmod":"2010-07-31T07:24:24Z","slug":"1088","tags":["windows"],"title":"win7下安装招商银行网上银行专业版出现“复制PersonalbankMain.ocx文件失败”的解决方法","url":"https://blog.zengrong.net/post/1088/"},{"categories":["others"],"content":"众所周知，武汉的驾驶员领取了驾照之后，还必须领取电子信息卡（即武汉交管局与中国银行联名的信用卡——长城交管卡），而缴交罚款，也需要这张卡。而我缴交电子眼罚款的复杂过程也从这张卡开始。\n2010年6月7日 正好下午有点时间，上www.whjg.gov.cn打印了3月份的两个电子眼违章（代码7311，7207，罚5分200元）告知单去硚口大队处理。排队半小时，处理完后没有给我任何凭证，要我15天内到中国银行交罚款即可。当时有其他事情，就暂时没有去银行。\n2010年7月26日 突然想起罚款还没有交，上网Google一番得知如果去大队处理之后，不交罚款是要缴纳滞纳金的，滞纳金为罚金的3%每天。当时已经不记得是何时去大队办理的手续了（前面的时间是交了罚款才知道的），就心急火燎的去找银行交罚款。印象中邮局（现在交邮储银行）也可以办理，就跑到最近的崇仁路邮局询问，得到的答复是他们不办理，但武胜路邮局可以办理。于是又辗转来来到武胜路邮局，武胜路邮局说他们的确可以办理，但现在16:20已经过了办理的时间，必须在16:00前才能办理。而且他们不能办理交管卡的业务，只能收纸质罚单。我问电子眼怎么可能有纸质罚单？他们回答要去大队领纸质罚单。可是我在大队办理的时候，工作人员并未给我任何的纸质罚单啊！\n于是赶往最近的中国银行，结果该银行也不能办理，并给我一张银行列表，告知此列表上的银行才能办理，列表如下：\n看看最近的一个中国银行也在青年路，还不知道具体在什么地方，准备明天再去。\n2010年7月27日 9:30，找到位于青年路64-10号的“中国银行江汉支行青年支行”，该银行在青年广场再往前走约100米的样子，与青年广场同边。到了负责处理交通违法罚款的柜台，拿出“长城交管卡”，却被告知交管局的系统故障，不能缴交！大厅的保安要我去马场角的银行交，并告诉我那个银行不好找，在一个小区的背街里面，要我仔细问问才能找到。由于有其他的事情，只得作罢。\n16:50，从武昌过汉口，顺便找到了鹦鹉大道179号的中国银行汉阳支行鹦鹉分理处，结果发现该银行已铁门紧闭，莫非是下班了？\n2010年7月28日 13:00，在网上重新打印了“武汉市公安局交通管理局交通技术监控记录告知单”，跑到武胜路的邮储银行想把流程询问清楚。邮储银行告诉我说，凭我在网上打印的告知单，是不能缴费的，必须在交通大队换成缴费单才可以。如果没有告知单，也可以凭行车证在他们这里打印告知单，但还是必须到交通大队换成缴费单，然后就可以在邮储银行或者中国银行缴费。邮储银行打印出的告知单和网上打印的告知单格式是差不多的，只不过多了“武汉邮政速递公司”的印章而已。如下：\n13:30，来到“中国银行江汉支行青年支行”，交通罚款有18个人排队。排队间隙，发现“长城交管卡”有代扣罚款业务（协议如下），但当我咨询协议中的第六条中的15天期限是从违章日开始算起还是从去交通大队处理开始算起时，得到的是非常生硬的回答：不清楚，你去问交管局，我们这只是外挂系统。\n排队过程中又咨询了几个来交罚款的司机，才知道交电子眼罚款的流程应该是这样的：\n在去大队处理的时候，如果有“长城交管卡”，大队会把违章信息刷到卡上，然后凭卡到中国银行对应网点交罚款；如果没有卡，大队会开出一张纸质的缴款单，凭缴款单可以到中国银行或者邮储银行交罚款。但是，在中国银行交罚款，数据更新快，一般24小时就能在交管系统体现出来；而在邮储银行交罚款，则要等2-3天才能更新。所以如果赶时间年审，就在中国银行交比较好。\n最后，加上滞纳金，罚款应为200+200×3%×(51天-15天)=416元，由于规定滞纳金不能超过罚款总额，交400元。不得不说，这个滞纳金还真是相当的高啊……\n为了交这个罚款，前前后后一共跑了3天，辗转5家银行，足迹遍及硚口、江汉、汉阳，特此记载，顺便鄙视一下交管局和中国银行低下的服务能力。\n提醒一点：\n如果不是要年审，最好不要去处理电子眼，最好是等到年审前提前一周去处理，处理完的当天就去交罚款。这样不会耽误年审，也不容易遗忘。\n","date":"2010-07-29","description":"","lastmod":"2010-07-29T06:53:45Z","slug":"wuhan-transport-camera","tags":["live","wuhan"],"title":"武汉电子眼违章罚款缴交流程","url":"https://blog.zengrong.net/post/wuhan-transport-camera/"},{"categories":["others"],"content":"路过一个宠物医院，我对淇淇说：“看，这就是宠物医院。”\n淇淇：“宠物医院是干什么的？”\n我：“就是专门给小狗狗治病的地方，下次你要是再发烧，爸爸就带你到这个医院好么？“\n淇淇语气坚定：”不！“\n我：”为什么？“\n淇淇：”我有儿童医院。“\n（淇淇每月发烧一次，估计除了我家，她最熟悉的地方就是儿童医院了）\n","date":"2010-07-27","description":"","lastmod":"2010-07-27T15:42:10Z","slug":"1083","tags":["qiqi"],"title":"淇淇语录之宠物医院","url":"https://blog.zengrong.net/post/1083/"},{"categories":["others"],"content":"2010年7月22日 今天是最后一天行程。上海影视乐园在车墩镇，也有叫上海影视基地的。去的时候找不到路，GPS上没有，Google地图上也搜不到，后来才知道我搜的是“车墩影视基地”，但Google地图上是“上海影视乐园”。倒是百度地图可以搜到，可惜百度地图只有WM版本，没有Android版本。估计以百度这样的气量，估计也不会做Android版本吧？\n乐园的门票是50元一张，车辆免票。不知是门口的两位大嫂业务不熟还是来参观的游客太少，收了我的钱之后愣是不给票，还是我提醒了之后才恍然大悟撕了3张票给我，还提醒我说10:20在5号摄影棚有演出可看。\n停车场旁边就是一幢70年代工厂厂房类型的建筑，上面写着“为人民服务”的标语，相当的和谐啊。进去之后是老爷车展。一辆辆民国时期、抗小日本战争时期、解放战争时期的老爷车、坦克车、摩托车排列在厂房里，很是壮观。而且这些车都还是可以驾驶的。工厂门口放着一些破旧的发动机，估计有专人修理这些老爷车吧。\n从老爷车展出来，对面就是一个欧式花园，四周分布着各种风格的别墅，有西班牙别墅、意大利风格别墅等等，但别墅的内部要么是服装展览，要么是工作区域，总之别墅就是用来拍摄外景的。\n从欧式别墅出来，去看了5号摄影棚的表演，表演的是上海滩黑道火拼。十几个演员在台上拳来脚往很是热闹。听音效和配音效果是非常逼真的，但仔细看，拳脚是打不到人的身上的。但那些摔倒的动作却都是真摔。打戏也是很辛苦的啊。\n看完打戏，意外的在街上碰到了英国著名表演艺术家张铁林在拍电视剧。于是就瞅机会找了几张照片。和旁边的群众演员闲聊，才知道他们也非常辛苦，35元一天，包中餐盒饭，然后就穿上戏装在旁边等着。导演说要拍的时候，马上围过去，往往一个镜头要来几十遍。没事的时候，就只能在阴凉的地方睡觉。真不知道当年王宝强是怎么挨过来的。\n继续参观，看到了许多有趣的布景。有的临时布景在后面可以看到脚手架之类的，觉得很山寨，但转到前面一看，却非常逼真，完全看不出出布景。在和平广场又碰到了另一队拍戏的，拍的好像也是与小日本相关的戏。\n13:00离开乐园。这时乐园门口还站着几十个年轻人等待导演挑选群众演员。中餐在滋味轩酒楼吃的上海本帮菜，味道还不错。14:40在上海虹桥站上D3006次回汉口。这辆车是直达车，中间不停站，准点应该是18:55到。但我们提前了20分钟。汉口站一如既往的乱得一塌糊涂。但无论如何，终于回家了啊……\n","date":"2010-07-27","description":"","lastmod":"2010-07-27T15:34:24Z","slug":"1060","tags":["journey"],"title":"无锡-苏州-上海流水帐七-上海影视乐园","url":"https://blog.zengrong.net/post/1060/"},{"categories":["others"],"content":"2010年7月21日 8:10-9:30\n周庄本来应该从苏州去比较近，但为了赶上海的行程，我们先是不准备去周庄的。但既然第二天不去世博会了，这一天的时间当然就应该去一个有意义的地方——江南第一水乡。\n从上海到周庄，本应在金泽下高速，可是我们走过了，只有折返重上高速。上海的高速真是黑啊，最低15元，除了过路费还有贷款费，还要应付随时可能的世博安检，麻烦。\n开车到了一个丁字路口，我的Google地图上指引的是直行，但路上的标记标的是停车场向右。我们问了路边的警察，也是说向右。于是来到停车场，缴费10元，去游客中心购票。周庄的门票是100元，门口会有40元的游船进周庄的线路，但那个线路走的是大船，不能进入周庄内部的水道，我们觉得不划算就没有选择。\n买了票，不知道往哪里走才能进周庄。其实我们所在的购票处离入口还有1.5公里左右的路程。旁边三轮车围上来问要不要去入口处，并想了半天报出一个20元的价格。我笑着说，你想了半天是不是在考虑赚我多少钱合适？1.5公里被你说成3公里？正在这时，英明神武的“邓导”来电话，说开车带我们去入口。\n从另一个出口出了停车场，发现如果从这个出口进入的话，是完全可以不交停车费的。我们仍然开到刚才那个丁字路口问警察该如何走，警察看到我们买了门票，就说“直走”。真不知道为什么一开始不让我们直走啊。可以省10块钱停车费呢。\n09;40-13:30\n一进周庄，就觉得我来错了时间。周庄这样的江南水乡，其实那些旧房子并非亮点，亮点就是在水乡上泛舟的感觉。而现在烈日炎炎，泛舟应该很热，水很脏，一览无余也没有什么情调。应该是晚上泛舟更有情趣。但既然来了，也就只能玩下去了。\n进大门后右手有个“怪楼”，想进去参观还发现要另外买票，于是放弃。绕着小路随意逛了40分钟，跟着别的团免费听了一点讲解，一抬头，发现已经逛到了游船码头。周庄的游船是100元包船，每船可以坐2-8人。我们找了3组人一起拼了一艘船，25元搞定。上了船，在晃晃悠悠中行进，又觉得并不感觉那么热了。有人建议摇橹的大爷唱首曲子，因为看到别的船有唱的，可他居然要价2元每人！！！真是大煞风景。\n下了船，去“三毛茶楼（楼主是台湾作家三毛的朋友）”喝了两杯茶，隔壁的几位四川游客在下围棋，我也重新找到了惬意的感觉。和楼主闲聊几句，知道周庄其实已经没有什么本地人住了，基本上都是外地人在这里租房子做生意。坐了大约1个小时，在繁忙的旅途中享受了午间的一点点闲暇时光，正发呆的时候，感觉肚子饿了起来，一看已经过了中饭时间，想想外面“邓导”还在等我们，估计也饿着肚子，于是离开茶楼，直奔周庄大门。\n如果下次来周庄，我应该会这样安排：\n下午从上海或苏州赶往周庄（周庄晚上不用买票，可以省门票钱），直接进入周庄找一家靠水边的旅馆，用门票省下来的钱住店，晚上再夜游水乡，应该别有一番风味。\n13:30-14:30\n如果说周庄有什么特产，估计就是“沈万三猪蹄”了，大街上一家连着一家，也不知道哪家是正宗。但是每家的猪蹄都一个颜色，红的发亮，不知道是不是抹了蜂蜜什么的。我们找到门口的一家餐馆吃午饭，也点了他们的沈万三猪蹄尝鲜，只能说不算难吃，美味什么的就算不上了，真不知道乾隆爷能不能把这个吃下去。不过或许也是因为这家餐馆做的不正宗吧……\n14:30-21:00\n回上海，逛陆家嘴地区、外滩、老城隍庙、豫园、外白渡桥、南京路，又跑到“邓导”的办公室去喝了杯咖啡。上海就是一个大都市，所以逛起来没什么感觉，很多地方，我们都懒的下车，就干脆“远观”了。尤其是外滩，人就好象蚂蚁一样一层接着一层。上海的外滩比武汉江滩要小很多，绿化也不够。我们只去了浦东的外滩，人没那么多，但可惜就是照不到东方明珠的全景了。老城隍庙地段也是人山人海，几乎走不动。\n到上海两天了，记忆最深刻的，还是三家店名：\n一五一十（卖杂货，世博的板凳就在那里买的） 一茶一坐（路上看到的，觉得名字很直接很有趣） 一朵一果（在豫园商业圈里的一家店，专门卖怀旧记事本，就是把小学语文书啦、文革宣传画啦之类的作为记事本的封面） [gallery link=\u0026quot;file\u0026quot; columns=\u0026quot;2\u0026quot;]\n","date":"2010-07-24","description":"","lastmod":"2010-07-24T15:57:21Z","slug":"1051","tags":["journey"],"title":"无锡-苏州-上海流水帐六-周庄印象","url":"https://blog.zengrong.net/post/1051/"},{"categories":["others"],"content":"2010年7月20日 09:00-09:40\n今天的任务就是世博。手上有四张票，是可以玩两天的。不过据说进去了一次就不想再进去第二次，对此话我将信将疑。无论如何，自己体验一天再说。\n“邓导”把我们送到世博园6号门主入口，一进去就是世博轴和中国馆。本以为排队一次就可以进入了，没想到一共排了3次队！每次都有十几分钟的样子，最后发现这样才只是排队到安检的地方！排队的过程中，有人兜售椅子，由于我已经准备，就没兴趣了。一般是叫价15元，但还价10元可以搞定。顺便说一句，那种椅子是不结实的，我在武汉上火车的时候花9元买了一个，结果到了世博会排队进园的时候就坏掉了。\n多次排队，也发现了一个规律。就是宁可站在人多的队伍的队尾，也不要站在人少的队伍的排头。因为进安检排队是严格按照排队的时间来确定的。人多的队伍一定是先开始排的，一个队伍排满了就会封队，并新开一队。先排满的队伍一定是先走的。\n水是不能带入世博园的，但是可以带杯子和食物。于是安检口就出现了类似于“人在囧途”中王宝强上飞机前的一幕：许多人抱着水瓶在安检口大喝……\n过了安检，本以为就一切顺利了，其实噩梦就在等着我……\n09:50-16:00\n6号门进入后，下世博轴就是B片区。按照地图（进门后再世博轴可以免费领取，所有不懂的都可以问志愿者），我们决定先到C片区欧洲馆，然后再慢慢向B片区走，最后玩A片区（是A片区，不是A片 :em54:）。\n世博园中一共有五种交通工具，分别是：\n免费大巴（不过黄浦江） 收费电瓶车（不过黄浦江，10元每人，随时可停） 地铁（在浦东园区和浦西园区间行进，免费） 轮渡（在浦东园区和浦西园区间行进，免费） 11路（废话，当然免费。虽环保，但不推荐） 于是，在面对诺大的园区茫然无助的时候，我们挤上了一辆免费大巴，在潮湿的空气和一阵阵狐臭味道中间，直奔C片区。\n到了C区没有考虑太多，先找没人排队的馆玩。结果逛了波黑和匈牙利馆之后，发现不能这样瞎逛下去了。这样没营养的小馆里面就是些图片而已，我们没有买世博护照，也懒的去盖章，这样逛下去一点意义都没有。\n上网查了一下欧洲区的热门馆，就直奔最近的德国馆了。结果到了德国馆才发现，根本找不到排队队伍的队尾！绕着德国馆转了一圈，才发现队伍已经绕馆3/4周，且每边都有3层队伍。问了旁边的志愿者，得到的消息是德国馆的平均排队时间是3-4小时。这打消了我们去德国馆的念头，看看旁边的法国馆人好像少些，就找到队尾开始排队。\n法国馆排队的时间大约是1小时，在我们排到最后一层的时候，发现队伍明显加长了，如果现在去排，估计就需要2小时了。法国馆给人没什么惊艳的感觉，就是投影+照片+实景而已。设计者把法国馆内的一个餐厅的厨房的实景作为了电视墙上的内容，看起来有一点点意思。\n后来逛了波兰馆和比利时-欧盟馆。波兰馆的剪纸设计很有特色，另有一段20分钟左右的3D波兰历史电影也做得非常好。只是要看电影在馆内还需要拍个队，但这个排队就舒服多了，因为有空调嘛。看电影的时候最好是坐第一排，因为电影幕布相当低，座位也都是同等高度，如果在第一排坐个高人，就没法看了。\n比利时馆也还不错，会发一块饼干（据说原来是发巧克力的，可能现在人太多发不起了），在里面至少我知道了萨克斯乐器的发明人是谁，以及缅怀一下小时候最喜欢的蓝精灵。巧克力虽然吃不到，但还可以现场看制作过程。这两个馆排队时间均在1小时左右。\n最搞人的是爱沙尼亚馆。看到人不错，房子还算漂亮，我们也去排队。结果发现他虽然人不多，但十几分钟才放一批人，所以上百人也排了将近50分钟。一进去才发现什么也没有，就是木地板上写着“可回收地板”字样，还有几个放大的小猪存钱罐。后面墙上写着“继续参观”几个字，结果进去一看，里面只有4个电视，还是放的同一个片子（只是有时间差）。这才明白估计前面控制人数就是为了给大家造成这个馆人不是很多的假象啊……还好里面的工作人员不怎么管你是不是坐着，于是我们跑到管理员看不到的地方休息了十几分钟，算是对前面排队的一种回报吧。排队时候后面的两个东北大娘更直接，进来后就气呼呼的往外走，一边走一边说：“这不是坑人么。”\n看了几个馆，我渐渐发现，排队那么长时间，进馆后最开心的，并非看到馆里多么美妙的景象，而是在遭受了高温的烘烤和高湿（喷雾降温设备造成的）的蹂躏之后，突然进入一个凉爽好的房间，那中间的反差，真的让人宛如进入了天堂。可惜的是，这个天堂不能座，连蹲一下都不行，只能拖着好像灌铅的腿往前走，走到出口再在热浪的侵袭下坐下来休息，接着转战下一个馆……\n排队是个力气活，碰上插队的还要斗智斗勇，并且适当的教育一下他们。累了的时候找个座位吃了点干粮，去直饮水区域抢水（没人排队，只能靠抢。后来人更多，我就懒的抢了，反正纯净水也不贵，3元一瓶），就继续转战各馆。\n16:00-16:50\n出了比利时馆后，就坐着休息了半个小时，买了点比利时薯条（20元）和一瓶啤酒（15元）干掉。看到同桌休息的游客再吃肯德基，突然感到肚子饿了，就打听肯德基在哪里买的。回答问题的是个北方汉子，我估计是搞IT工作的，回答问题相当有逻辑性：“你走到那个馆门口，然后向右拐，看到一个矮的二层楼。不是这种高房子，我们坐的这里的房子是高房子，那种是矮房子（还一边比划），然后向右转，肯德基在二楼。”我又问，5分钟能走到么？他看了我一眼说：“你可以。”kao，这位兄弟太严谨了！\n16:50-17:20\n跑到肯德基，又要排队，连进肯德基的门也要排队，还是隔段时间放一批。后面的人非常拥挤，门口也很小，我是被人挤进去的。肯德基倒是没有涨价，只是套餐里面多了东西，一个汉堡一对奥尔良烤翅一块原味鸡一杯可乐共34元。相当于变相涨价吧。套餐就是个现成的盒子，直接给你，单点要等20分钟。本来我翻了半天随身带的优惠券，在背面看到“中国大陆通用”，刚刚高兴一下，结果后面又看到一句“世博园内不能使用”。阴谋，这就是个阴谋！\n17:30-19:00\n吃完肯德基，看看各个国家馆依然人满为患，就想去浦西园区的企业馆碰碰运气。坐地铁到了浦西园区，一出站就是日本企业馆。跑去看看排队人群也不少，就不想再排队了，其实当时人已经精疲力竭了。跑到10元电动车处，买了两张票绕浦西园区一周，发现其实很多企业馆，比如中国船舶、中国航空等都没什么人排队的，但热门的上海汽车、通信联合馆人也很多。本来打算找几个没人的馆看看，但LP已经走不动，我们就干脆直接从3号门出去，打电话朋友来接，直接回酒店洗了睡。太累了！！！真的不想来第二次！！我已经在考虑怎么把多的两张票卖掉了。\n如果重新来玩，我可能会这样安排：\n尽量提前进园，直奔德国馆。德国馆后可以看俄罗斯馆、英国馆、法国馆（看具体的情况了），然后直奔浦西园区，找人少的馆，能看几个看几个。吃饭最好也在浦西园区解决。因为浦西园区人少，不用抢水，坐的地方多，环境也比浦东好。\n","date":"2010-07-21","description":"","lastmod":"2010-07-21T16:06:30Z","slug":"1050","tags":["journey"],"title":"无锡-苏州-上海流水帐五","url":"https://blog.zengrong.net/post/1050/"},{"categories":["others"],"content":"2010年7月19日 09:00-10:00\n跑到哑巴生煎吃早餐，生煎的味道确实鲜美，与武汉的一绝煎包比起来，特点就是汤多、皮薄，味道别具一格。在里面吃的也是苏州本地人居多。不过貌似苏州人不怎么热情啊，LP看到别人喝的小碗汤，就跑去问是什么，结果那一桌是三个苏州婆婆，没一个理她，搞得她很郁闷……我说你不能怪别人嘛，或许是耳朵不好使，或许是别人不愿意理你啦。换个愿意理你的人问啦……比如说问我 :em61:\n10:00-12:00\n吃完饭直奔拙政园。本来准备找个三轮车去，结果等了半天都等不到，就到哑巴生煎斜对面的车站坐了一站路下。一下车就碰到很多拉着你卖便宜票的，我也被拉到旁边的一个旅行社，说可以50元买到拙政园的票（全价票70元），还说的天花乱坠，比如有专车啦，自己定时间啦，等等。我弄了半天才知道，原来他们是拼团的，到处拉人，然后用一辆大车把你拉倒地方，规定一个时间，等你出来再拉到下一个地方。我不愿意受时间限制，就跑出来了。结果又有其他“兔子”跟着我们一直说到拙政园门口……那个烦啊。怎么就没人管这事？这些拼团的看着不太正规，最好还是不参加。\n我觉得苏州最好的地方，是著名景点都会有免费讲解。比如拙政园的讲解就是20分钟一次，免费讲解还是很不错的，我跟着免费讲解听了一大半行程，又去听其他导游的讲解，发现每个导游的讲解都根据自己的知识体系有一定的侧重点。比如有的着重讲园子的布局、亭台楼阁榭的特点，有的主要讲典故，还有的会讲些野史之类的。总之，一百个导游一百个讲法，我怀疑我再呆两天，也能去混个导游当当了……\n拙政园我是第二次来，仍然是一如既往的人多，到处都是人，游客的素质不一，有不插队的，有不顾一切往前挤还不让人说的（一说就和你急），还有照相让孩子把住路口不让人尽的（听口音是武汉的，真是丢脸啊）。一个本来幽静的园子变得嘈杂无比，让人没有一点兴致。看了一半，我们就看不下去了，直接去园林博物馆，看完就到出口了。园林博物馆还是值得一看的，能在里面看到许多园子破败的景象与返修后的景象对比，还是很有感触的。这园子还真是有钱人才玩得起的啊！一旦破败，就和鱼塘差不多了……还是一位山东大哥总结的好：“我看要做这个园子，首先要有文化”！多精辟啊！有钱没文化不行，没钱有文化也不行，先有钱后有文化更不行！\n后来LP总结说，你认为拙政园不如寄畅园，并非是因为寄畅园就真的那么好。而是因为寄畅园没人，你可以慢慢散步和体会。若是寄畅园也像拙政园这样，恐怕你也会走几步就闪人了。想想也是，古人造园子，可不就是为了这点情调么？要是整个园子上千人在里头，再怎么样他们兴致都不会好了。\n12:00-15:30\n出了拙政园，已经没有兴趣再去看狮子林，坐上游2公交本来准备去山塘街坐船，临时又改变行程直接坐到终点站虎丘。事实证明我们的选择是灰常灰常正确的！连苏东坡都推荐来虎丘嘛，我们当然也没来错。（似乎郭沫若也推荐鼋头渚来着？看来还是古人的眼界比较高啊！）虎丘有亚洲第一斜塔、还有憨憨泉、千人石、剑池、真娘墓等无数典故，凉风习习、空气新鲜，让人心旷神怡。总之，如果到苏州，拙政园可以不去（可以选择留园、网师园，人会少些），但虎丘一定不可以不去！\n虎丘中有个“拥翠山庄”，当时免费导游说这个山庄是因为某人金屋藏娇而得名，还是那个庄字多一点就是相当于藏了个人。可是进入山庄一看，介绍上明明不是这么写。真不是是谁在忽悠。看来导游说的故事，不可不信，也不可全信啊。\n15:30-16:50\n坐游1回到观前街，去朱鸿兴吃了焖肉面，然后去黄天源和采芝斋买了点糖果和点心。黄天源很搞人，本来查到它家著名的就是玫瑰松糕和薄荷糕，结果进去问了半天都说没有，而且售货员还不解释为什么没有，后来才知道，虽然是黄天源的牌子下面进去的，但是里面卖点心的都有黄天源没关系，也不卖黄天源的东西，当然也不会为你义务介绍了，甚至还希望你早点离开。正宗的黄天源就只剩门口一点点窗口。跑去买了点，才发现这些点心里面都有猪油，不蒸一下是很难吃的，而它的保质期只有2天。所以我只能尝尝然后丢掉了，真浪费啊……\n16:50-19:00\n苏州基本是打不到车的，拖着行李坐游1去火车站，19:00到上海虹桥。苏州以前给我的感觉不错，但经过这短短的一天，在我心中的印象大打折扣。\n19:00-21:00\n上海的朋友“邓导”接风，从火车站直达仲盛广场的湘乐汇，这家湘菜做得还是比较正宗的，终于可以不吃甜甜的上海菜。酒店是畹町路的莫泰168，艺龙上订的。本来还是准备住如家的，可是上海的如家太贵，450一间房，这可住不起啊。这家莫泰是我见过的最大的连锁酒店了，一层的房间就有近80间，走廊N长啊……\n","date":"2010-07-20","description":"","lastmod":"2010-07-20T14:29:15Z","slug":"1049","tags":["journey"],"title":"无锡-苏州-上海流水帐四","url":"https://blog.zengrong.net/post/1049/"},{"categories":["others"],"content":"2010年7月18日 10:00-16:30\n郭沫若的一句“太湖佳绝处，毕竟在鼋头”把无数人忽悠到了鼋头渚这个地方。可我认为，就和任何名气大的地方一样，不去后悔，去了也后悔。\n早上起来，依然是去王兴记吃馄饨，然后坐公交去鼋头渚。中山路没有直达鼋头渚的公交。根据Google地图的提示，先坐66支到湖滨路下，然后向来的方向步行100米后左转，换乘1路到终点即可。期间会经过蠡园，由于时间不够没有去。如果可以重新选择，我还是会去蠡园的，然后压缩鼋头渚的时间。\n鼋头渚进了大门之后还有很长一段路才到景区，可以坐景区大巴，如果有车，也可以付15元开进去。景区大巴中间有一站会停，当时是从那一站上来几个人，旁边的人问他们下面有什么景点，他们说不知道有什么景点，就是稀里糊涂的下去了……\n鼋头渚大门有导游服务。导游有A线路和B线路，其中A线路就是鼋头渚，B线路是太湖仙岛，讲解时间都是30-40分钟，价格100元。我想到请个导游能介绍得清楚一点，就请了个A线路的。事实证明这100元真不值得，导游也没讲什么，景点也没什么好看。鼋头渚就是几块伸进湖中的石头拍拍照而已。再就是最后一个状元题写的石碑也可以拍拍照。然后也就没什么可看了。一边游览一边邪恶的想，幸亏古人用的是鼋来命名，如果是另一种类似的常见带壳爬行类动物，念起来可就有点麻烦了……\n去太湖仙岛坐船还是很快的，船20分钟一般，不用等太长时间。太湖仙岛就是一个纯人造的景观，把全国各地的石刻做了一些山寨版放（不是石头的，是空心的）在一起，再加上几个假山假水……真的不想去第二次。\n最有趣的事情还是在游船上碰到了在上海工作的初中同学。本来准备到了上海打电话他，请他出来吃饭的，当时LP还开玩笑说，要是你到了上海电话他，他说在无锡，看你怎么办。结果，还真的在无锡碰到他，而且他也会迟两天会上海。这样算来，如果到了上海电话他，他人还真的就在无锡。不得不感慨，这世界真的很小，LP预言真的很灵……\n16:30-18:30\n紧赶慢赶回到酒店取了寄存的行李，在85度C面包房吃了点东西，再打车到火车站。本以为到苏州的车票很好买，毕竟高铁约30分钟就有一趟车。结果排队20分钟后到了窗口一问，才发现只有18:12分的。原来这段时间由于世博的原因，车票也很紧张。无奈只得在火车站待了1个多小时。本来约了苏州的朋友19:00吃饭，也只能通知一声往后推迟了。\n18:30-19:30\n下火车后到处找不到打车的地方。苏州站在修，非常乱，黑车拉客横行，让我想到武汉的火车站。上次来苏州的时候，对火车站的感觉还挺好，站小，比较干净，一出站就可以打到车。结果这次出站后，拦到空车也不停。问了路人才知道出站这条路不准停车，必须走很远到公交中心才可以，但这段路一个指示牌都没有，完全让人摸不着头脑。\n好不容易到了公交中心，发现有6、70米的等出租的队伍。而且出租车十几秒才会来一辆。于是彻底无语，准备找辆黑车直接去吃饭的地方。黑车们都很热情，可一听我们去观前街，丢下一句不去立马走人……太直接了吧\n接到苏州朋友的电话，听说我们打不到车，自告奋勇来接我们，我自然是喜出望外。可她对这边的路不熟，我对这边更不熟，折腾了半天才接上头，上车时，离我们下车已经1个小时了。\n19:30-21:00\n晚餐在川福楼，因为知道朋友喜欢吃川菜，我也特意选的这家离酒店比较近的川菜馆。菜馆环境不错，但味道比正宗的川菜就差远了。水煮鱼看着很多辣椒，但没一点辣味，应该是为了配合苏州本地口味做的“改良”吧。\n我们4个人点了6个菜，但是最后两个比较大的菜“水煮鱼”和虾一直都没上来，催了很多遍都没用。后来我们要买单，才慢慢腾腾的把鱼上了，虾子干脆退掉了，我估计他们也没做。问服务员，结果她说不是她的问题，说如果菜到了她一定会端上来，这都什么逻辑……\n吃饭完毕，直接杀向如家观前二店，中途又开始下阵雨，这一天真是辛苦啊……\n","date":"2010-07-18","description":"","lastmod":"2010-07-18T15:55:13Z","slug":"1048","tags":["journey"],"title":"无锡-苏州-上海流水帐三","url":"https://blog.zengrong.net/post/1048/"},{"categories":["others"],"content":"2010年7月17日 11:30-13:30\n据说西新饭店的汤丝螺和一锅鲜很有特色，而且离中山路不远，就打开Google地图一路走去，期间还跑到商业大厦的DQ弄了杯冰淇淋，无锡在修地铁，到处挖的一塌糊涂，和武汉差不多了……\n用GPS看到西新饭店的人民西路店就在我旁边，但怎么都找不到，修路很乱，心想难道店子被拆掉了？或者和五爱路店就是一家？无奈来到旁边的五爱西路店，环境很嘈杂，旁边还有两个小孩子吹哨子，整个饭店的人都皱眉，孩子家长也不管。\n由于只有两个人，就点了汤丝螺和一锅鲜，味道确实都不错。汤丝螺里面很多小螺丝，吃起来有些不爽，但鲜味是足够了的。一锅鲜的鲜味就更别提了，只是里面的鱼用的是鲫鱼，很发啊……\n加上一瓶啤酒，一共85元。\n13:30-18:00\n从西新饭店出来向左走50米就是公交车站，座公交3站到锡惠公园后门。在后门花135元买了“无锡园林一票通”，可以在两天内玩锡惠公园、梅园、蠡园、吟苑、太湖鼋头渚，还是很划算的。也可以花28元坐缆车到山顶一览无锡市貌。\n锡惠公园还是非常值得一玩的。如果有时间的话，应该用一天来好好的玩。锡惠名胜区中的寄畅园、二泉书院、惠山寺、华孝子祠都值得一看。尤其是寄畅园，个人感觉比拙政园还要好看些。十步一景，构思奇巧，让人不得不惊异于建园者的奇思妙想。流连于亭榭之间时，我甚至恍惚想到，过了这个门，若是迎面碰上几位古人，该怎么打招呼呢？是说你好，还是说HI？\n美中不足的是，锡惠公园的指示标识设计非常不好。很多标识不完善。开始我在公园里面来来去去找了半个多小时，都没有找到寄畅园。明明看到了天下第二泉的标牌，偏偏就是找不到这个景点。阿炳墓隐藏在一个曲径通幽之地，门口的牌子还被茂盛的植物遮挡着，若不是走近看，根本不知道大名鼎鼎的华彦钧就葬在此处。寄畅园也是一样，从一条很小的容易让人忽视的路进去，才能看到寄畅园的指示牌，而路的外面被植物和树木遮掩着，不留意根本就看不到这条路。总之给人的感觉就是，公园根本就不想让你找到这些景点。或者用LP的话来说，就是商业气氛太淡了。索道16：30准时下班，然后卖工艺品和喝茶的地方也同时下班，似乎并不想做生意。要买瓶水都没地方去。\n关于索道，那是一定要坐上坐下，或者坐上走下，千万不要爬上去。我和LP就是在工作人员热情的推荐下爬上山的。按照工作人员说的，20分钟就能爬上山，然后再坐索道下来看看风景。结果这“20分钟”里，中途休息了3次，那个汗流浃背啊，下来后腿都软了。\n[gallery link=\u0026quot;file\u0026quot;]\n18:30-20:00\n到天福苑酒家吃晚餐，没想到玉兰饼那么撑人……两个人点了四个玉兰饼，结果每个人吃了一个就吃不下了，看隔壁都是一人点一个的。那东东吃了凉了也不能吃，只得浪费了。说实话，玉兰饼很腻，不像想象中那么好吃。\n青豆泥是非常的甜香，但是这几天甜食吃多了，受不了，吃了几口就再也吃不香，其实心里还是挺想继续吃的。\n纸锅豆腐也是吃到最后只剩甜味了，不知道是不是我的味觉系统已经到了承受的极致？总之明天不能再继续吃无锡本帮菜了，换川菜或者火锅好了。\n","date":"2010-07-17","description":"","lastmod":"2010-07-17T15:51:52Z","slug":"1041","tags":["journey"],"title":"无锡-苏州-上海流水帐二","url":"https://blog.zengrong.net/post/1041/"},{"categories":["others"],"content":"2010年7月15日 8:00-12:45\n在汉口站乘坐D3008赴无锡，除了武汉发现手机无信号，打10086一问，原来移动忘了开通我的全国漫游，可能前段时间转资费出的问题吧。\n12:45-14:00\n打车到如家快捷酒店中山路店，司机还不知道这个地方。其实很好找，就在无锡基督教堂旁边。是个闹中取静的地方。这家如家有落地窗，订房的时候不妨做些要求。\n14:00-14:30\n去王兴记中山路店解决肚子问题。\n王兴记是无锡老字号，最有名的食物是蟹粉小笼和虾仁馄饨，但这两样都没吃到，只吃了个鲜肉小笼和三鲜馄饨。味道也不错。\n14:30-16:00\n参观东林书院。门票16元，LP说要找讲解，于是30元请了讲解员，事实证明请讲解是值得的。对于这样有历史积淀的人文景观，如果参观前不事先做足功课，是看不出什么内容来的。而有人讲解，则会有趣多了。\n16:30-18:00\n按照Google地图上显示的阿炳故居的位置从东林书院开始步行，结果发现目的地在修地铁，故居也差点找不到。好不容易找到了，才发现这里就是大名鼎鼎的崇安寺景区，和武汉的江汉路比较像，但是区别也很大。崇安寺的商铺餐饮都非常集中，也都很小巧，而且十分钟就能走完整个景区了。\n为什么真正的人民艺术家总是过得那么凄凉……\n在大众点评网找到口味28分的爱茜茜里意大利手工冰淇淋店，点了个三色杯感觉还不错，细腻程度感觉介于DQ和哈根达斯之间，但没有两者那么甜。找店面地址的时候，在Google地图上已经看到自己就在餐厅旁边了，面前就是美特斯邦威的广告，就是看不到店面。结果无意中往左边一看，发现原来就在自己旁边啊……\n18:00-19:00 赴无锡国际饭店报到\n19:00-20:30\n去八佰伴7楼的“外婆家”吃晚餐。这家餐厅排队的人叫一个多啊，不过比起高峰时段的“世贸绿茵阁”还是差多了。等了20分钟，无奈选择了拼桌。4组共8个食客拼在一张大圆桌上，那叫一个壮观啊！\n江浙一带菜肴的特点就是量少，味甜。我是按照大众点评网上点了外婆红烧肉/麻婆豆腐/铁板蛏子/开心土豆，每道菜味道都相当不错。尤其是麻婆豆腐，只要3元，只是少了点麻辣，多了点甜味儿。最好觉得菜还不够，就又加了个凤爪香干。结果只是把凤爪吃完了。不是因为吃不下了，而是因为香干不入味，不好吃。五个菜加一瓶啤酒一共99元。多吉利的数字啊……\n2个人吃这么多，并非我们是大胃王，而是因为每份菜的分量都很少。如果在湖锦吃的话，点2个菜就要打包了。更搞人的是旁边的2位女士，点了7/8个菜，除了3个凉菜（貌似是酸菜）外，其他全部都是主食，什么绿茶饼之类的。结果她们把凉菜吃完了，其他吃不下全部打包了。不知道他们是否就是为了打包而来。\n2010年7月16日 8:00-17:00\n赴无锡机电高等职业技术学院。在Google地图上搜到可以坐65路公交直达。结果到了公交站发现该站点已取消，65路也已经改线。又往前走了一站，从站牌上发现66支也能到。公交上人不多，车子前半部分左边的座位全部拆掉了，方便站人。\n说一下几个细节。一是公交线路改线或者取消站点，在公交站牌上有很明确的说明，并且指导你应该到到哪一站去乘车。原来的站牌也全部拆掉，这样就不会误导乘客。而是公交站牌用颜色表明了当前是哪一站，下一站是哪一站，确保不会搞错方向。三是许多公交站名旁边都有一个括号，标明该站附近比较著名的建筑或企业。例如我知道66支到无锡机电高等职业技术学院，就是从括号中得知的。四是价格，投币2元，刷卡1.2元。这些都是值得武汉公交学习的地方了。\n18:00-19:00\n去“三凤桥肉庄”吃酱排骨，看了点评网介绍中的这句“如果没有时间坐下来用餐”，本来以为有地方坐，结果发现只能买了走人。所以，我只能买了一点请售货员帮忙加热，然后去王兴记吃了。购买的方式很奇特，先说买多少，售货员帮你装好打包承重后，给你小票，你再去付钱。很多东西都有真空包装，据售货员说可以保质9个月。准备等离开无锡的时候再去买。\n这次在王兴记终于吃到了蟹粉小笼和虾仁馄饨，原来，王兴记分为“风味厅”和“大众厅”，服务方式不同。蟹粉小笼/虾仁馄饨只有“风味厅”才有的，是先付钱，然后把小票交给服务员，服务员会把食物送到桌上；“大众厅”则需要自己在发货处等待食物然后自己端到桌子上。\n味道上感觉蟹粉小笼就有点甜了。吃两个还没什么，如果吃多了就觉得腻。再配合酱排骨一起吃的……都是甜的……好腻啊……\n","date":"2010-07-16","description":"","lastmod":"2010-07-16T15:57:56Z","slug":"1034","tags":["journey"],"title":"无锡-苏州-上海流水帐一","url":"https://blog.zengrong.net/post/1034/"},{"categories":["technology"],"content":"一、起因 做这个小工具的起因是把黑莓8700g换成了Android系统的三星i5700。由于我的8700g是4.2版本的ROM，不支持google sync程序的安装，而我换了手机后又懒得再去鼓捣黑莓的刷机，因此就直接用黑莓的管理软件导出CSV文件，然后在gmail的通讯录管理界面中导入。\n可是问题就接踵而来，gmail的通讯录导入程序不能识别黑莓导出的所有字段，因此只有部分字段被导入到gmail 通讯录中了，这显然不行。\n接着我用gmail通讯录导出了一个csv文件，然后按照导出的CSV文件的格式来修改黑莓导出的CSV文件，并使排序完全一致。结果这样也不行，导入的通讯录更加不完整。\n仔细查看了gmail的帮助，根据帮助提供的范例修改了CSV的字段名称，倒是可以导入。但gmail帮助提供的范例中的字段很少，并不能完整的支持我的通讯录。\n我试验过的gmail通讯录导入支持的字段如下：\n名,姓,公司（或者用“单位”也可以）,职务,移动电话,住宅电话,电子邮件地址\n如果联系人的信息并不多，那么用上面的字段就可以完成了，可是我的很多联系人，都有超过2个以上的电话，有十几个联系人有4个电话，因此就没办法用这个来导入。\n这还不算，最大的问题，是gmail中的导入功能无法支持群组的导入。也就是说，原来在黑莓中对联系人进行的分组，都无法识别成gmail的群组。\n要解决以上问题，就只能使用Google Contacts Data API来处理了。\n二、分析与选择 Google Data API其实已经提供了很多客户端库，但是唯独没有基于ActionScript的。因此只能使用XML来处理。\n1.AIR还是SWF\n本来准备做两个版本，先做SWF版，然后转成AIR版，但后来发现SWF有很多限制，因此只能使用AIR来制作这个工具。\nSWF的限制包括：\n仅支持HTTP协议的POST和GET方法，不支持PUT和DELETE，Google Data API的某些操作是需要PUT和DELETE的。（对PUT、DELETE不清楚的可以看这里） google的跨域文件的限制 2.选择何种google账户认证方式\ngoogle提供了三种账户认证的方式（注意：这个页面的ClientLogin的“了解详情”链接是错误的，要访问正确的地址，可猛击这里）、OAuth、AuthSub和ClientLogin,我对这三种都进行了测试。Google推荐使用的是OAuth，这种认证方式复杂但安全，它需要打开google的登录界面进行登录，登录后再返回软件界面。对于桌面程序来说，这就要判断用户的操作，怎样才算作登录成功了。比如判断浏览器的标题栏、或者判断cookie、或者使用时间轮询等等。对于一个小工具来说，这太复杂了。\nAuthSub与OAuth类似，但流程要简单一些，还是要使用google的登录界面。\nClientLogin就简单多了，只需要提供google账户的用户名和密码就可以了。因此这个工具使用的是ClientLogin。\n3.Google数据API（Google Data API）\n这里是Google Data API的列表页面，其中，Goole 账户验证和Google 数据 API是大部分API都要用到的规范，只要涉及到数据处理和账户验证，都要用到这两个API，而对应的产品也有自己的API，例如google通讯录的API就是Google Contacts Data API，联系人API中的很多细节，却是Google 数据 API提供的。\n4.E4X\n在没有客户端库的情况下，对XML的操作，是个挺麻烦的事情。这个小工具很简单，大部分的工作量其实都花在对XML的处理上。大量的XML操作让我感觉到E4X真的是却是非常方便和优雅。对于E4X操作，下面这两篇文章有很大的参考价值：\ne4x - 对xml操作的一些示例\nE4X用法简要\n或许，我后面也会写一篇经验吧。\n三、工具使用注意事项 CSV编码必须为UTF-8； CSV字段名称不能重复； CSV的换行符默认使用“\\n”，可以在源码google::GoogleContact.CSV_LINE_BREAK中修改。“\\n”是Linux系统的默认换行符，windows系统的默认换行符为“\\r\\n”。具体你的CSV是使用什么作为换行符，可以在CSV的编辑工具中指定。许多文本编辑器支持换行符的转换。 CSV中联系人群组字段的名称必须为“群组”（当然，可以在源码中自己改google::GoogleContact.CSV_GROUP_FIELD_NAME）； 每一次提交之后，不要重复操作，要等待google返回后再处理。 四、下载信息 1.下载AIR运行时并安装；\n2.下载AIR包并双击安装：\n1 文件 源码：http://googlecontactimporter.googlecode.com/svn/trunk/\n项目地址：http://googlecontactimporter.googlecode.com\n","date":"2010-06-29","description":"","lastmod":"2010-06-29T02:31:46Z","slug":"1032","tags":["air","android","google","mobile"],"title":"google联系人自定义导入小工具开发过程及源码","url":"https://blog.zengrong.net/post/1032/"},{"categories":["others"],"content":"这几天上网的时候，不定期的出现中国电信2M升4M的广告，这已经不是原来右下角弹出小广告那种方式了，而是当我输入一个网址的时候，直接出现的就是电信的广告，没有任何的提示，显示广告10秒左右，再自动跳转到输入的网站。\n既然电信不惜撕破面子如此辛苦地劫持我的浏览器做这种流氓广告，那……我就尝试一下这服务把。\n可是，稀奇古怪的事情就这么开始了……\n电信广告上是有5种方案可以挑选，但由于我目前是1299元的那种e9套餐，因此只有3种可以挑选。我自然选择每月25元，包25个小时无线上网的那种套餐了。\n到营业厅排队，电信的效率一如既往的低，前面的一个人愣是办了半个小时。轮到我后补交了一年套餐费1299，再加上25*12，一共1599元。\n这个套餐居然还“白送”一个3G上网卡，中兴AC2746。那我原来花600大洋搞定的华为EC1260不是浪费了？\n:em28:\n回家后一切正常。可第二天，不能上网了！以下是我采取的措施：\n我采用的是路由器上网，发现路由器拨号失败后，就直接用电脑连接外线，然后拨号。出现“错误629”提示。 电话咨询10000，被告知我的上网账号被改了，我说办业务的时候没通知我改了啊。对方回答说要我自己去看办业务时候的回执单，上面写得很清楚，同时密码也改成了回执的密码封中的密码了。 找到电信回执单，果然发现了一项： 登记事项3：变更光纤到户拨号速率、光纤到户拨号修改上网账号\n……\n上网账号 老：z****666 新：189********@ADSL\n……\n新的账号变成了这个套餐中捆绑的手机号码加上@ADSL，可怜我原来自选的上网账号啊，多吉利啊，就这么在我不知情的情况下被咔嚓了。可就算是用这个新的账号，也无法上网\n因为了解电信的一贯作风，我接着使用了多种组合，分别为：\n老账号+密码封密码\n新账号+老密码\n新账号+密码封编号（密码封上面是有个编号的，曾经有一次我帮朋友弄网络，就是用编号当密码拨号成功的 :em67: ）\n新账号去掉@ADSL+密码封密码\n新账号去掉@ADSL+老密码\n事实证明，我的最后一种组合是正确的。我的新ADSL上网账号应该就是手机号码，密码却不是密码封中的密码，仍然是老密码。\n弄完抹了把冷汗。还好我对电信作风以及网络设置非常熟悉，要是碰到普通用户，再加上并不专业的10000台客服MM，上哪儿说理去……\n武汉电信，让我说你什么好呢？\n","date":"2010-06-22","description":"","lastmod":"2010-06-22T14:55:42Z","slug":"1031","tags":["live","wuhan"],"title":"武汉电信，你让我怎么说你","url":"https://blog.zengrong.net/post/1031/"},{"categories":["technology"],"content":"出处：http://blog.yoz.sk/2010/06/quick-tip-timer-listeners-doesnt-require-remove/\n1EventDispatcher(event.currentTarget).removeEventListener(event.type, arguments.callee); ","date":"2010-06-09","description":"","lastmod":"2010-06-09T01:43:56Z","slug":"actionscript3-remove-anoniymous-listener","tags":["as3","tipsandtricks"],"title":"ActionScript3移除匿名函数的侦听","url":"https://blog.zengrong.net/post/actionscript3-remove-anoniymous-listener/"},{"categories":["use"],"content":"工作原因，必须在Ubuntu下面安装Flash Builder和Flash CS4，搜索一番找到两篇文章：\nHow to install Flash Builder on Linux\nInstalling Adobe CS4 in Wine\nFlash Builder的那篇，针对的是Beta1，我用正式版试了一下，不成功。安装Flash CS4的那篇由于比较麻烦，就没有试。况且现在都CS 5了，试了也没意义。\nAdobe的东西，在Wine下总是很麻烦。倒是原来Macromedia的Flash 8绿色版直接在Wine下运行得很好。\n接下来就是重头戏了：安装Linux下原生的Flash Builder 4。\n其实，Adobe labs有一个这样的项目：Flex Builder 3 for Linux，但这个项目多半已经停止开发了。因为Flash Builder是基于Eclipse的，而Eclipse本就跨平台，所以理论上可以将Window下的Flash Builder移植到Linux下面来。既然Adobe不愿意做，那么eshangrao就自己做了。\n安装方法：\n1.安装Eclipse，我直接安装源里的3.5.2版本\nsudo apt-get install eclipse 2.下载FB4Linuxaa、FB4Linuxab、FB4Linuxac、FB4Linuxad并放在同一个文件夹，然后执行：\ncat FB4Linux* \u0026gt;FB4Linux.tar.bz2 3.解压缩\ntar xjf FB4Linux.tar.bz2 -C ~/FlexBuilder4Linux 4.在Eclipse中安装插件。\n依次选择”Help-Install New Software”，单击“Add”按钮，在弹出的“Add Site”对话框中单击“Local”按钮，指向上一步解压缩的文件夹，并设定一个Name（例如FB4Linux），确认。如下图：\n在“Work with：”下拉列表中选择刚才添加的FB4Linux，要注意不要选择“Group items for category”项。选择所有显示的项目，单击“Next”进行安装。安装速度有些慢，要耐心等待。如下图：\n5.下载Flex SDK，并进行设置。如下图：\n6.AIR支持\n若需要运行和调试AIR项目，会出现错误提示：\n!ApolloLaunchDelegate.dirDoesNotExist!\n原因是从opensource.adobe.com下载的FlexSDK不包含Linux编译器，必须使用Linux版本的AIR SDK。\n根据自己的需要和安装的AIR运行时版本，从下面的链接下载：\nAIR SDK 2\n解压缩下载的内容并将其覆盖到你的Flex SDKS文件夹。\n同时，应安装AIR Runtime。\n英文安装说明\n","date":"2010-06-02","description":"","lastmod":"2010-06-02T12:56:01Z","slug":"flash-builder-4-for-linux","tags":["eclipse","flashbuilder","linux","wine"],"title":"Flash Builder 4 For Linux","url":"https://blog.zengrong.net/post/flash-builder-4-for-linux/"},{"categories":["technology"],"content":"encoding 可简写为enc。功能是设置VIM的内码，即默认以何种编码工作。这个是最重要的编码设置了，如果设置不好，可能会导致VIM的界面语言和提示语言乱码。Windows下面的gVIM安装中文文档后乱码的情况就是因为这个设置不当造成的。\n这里说一下 Windows 下面 gVIM 安装 中文文档 后乱码的情况。\n上面的VIM的中文文档有两个版本： vimcdoc-1.7.0-setup.exe 和 vimcdoc-1.7.0-setup-unicode.exe 。但这两个版本的编码都是utf-8的，不知道是不是作者的失误。\n安装程序将文档安装在 $VIM/vimfiles/doc 下，同时在 $VIM/vimfiles/plugin/ 下面安装了一个插件 vimcdoc.vim，并在该插件中设置了 encoding=utf-8 ，这导致gVIM的界面和提示文字乱码。因为在Windows下面，界面和提示文字编码是cp936。\n解决的方法，将 set encoding=utf-8 注释，或者改为 set encoding=chinese 。\n如果已经在 _vimrc 中设置过 secoding=utf-8 ，则也要将其注释掉才行。\n这样设置的gVim界面不会有问题，但默认保存的文件编码就变成了cp936（即GB2312）,如果你和我一样偏爱 utf-8 ,就还要设置 fileencoding=utf-8 。\n2015-01-21 更新： 我创建了一个项目 vimfiles ，使用它可以自动下载文档。\nfileencodings 可简写为 fencs。功能是设置一个列表，VIM打开文本文件的时候根据这个列表的顺序来检测编码。我的设置是这样的：\nset fileencodings=ucs-bom,utf-8,chinese,latin1 注意顺序是不能错的。ucs-bom必须要设置为第一个，utf-8必须是第二个，latin1必须是最后一个。为什么这样设置可以看VIM文档：\n:help fencs 另外，看到网上的某些教程把gbk、prc、gb2312、gb18030、cp936都设置在这个列表中，其实是没有必要的。\n在VIM中，对于中文，Windows 下和 Unix/Linux 下的编码表示是不同的，Windows 下是 cp936，Unix/Linux 下是 eus-cn。而gbk、prc等等都是中文编码阿的别名。因此，正确的方法是使用别名。chinese和prc都是中文的别名，它们在 Windows 下面和 Unix/Linux 下面分别对应正确的编码。详细介绍可查看VIM文档：\n:help encoding-names fileencoding 可简写为fenc。功能是设置VIM编辑的文件的编码。这个和encoding可以不同。如果不对其进行设置，其值与encoding相同。因此如果在Windows下，默认保存的文件编码就是gb2312的，而此时若希望默认保存的文件编码为utf-8，则可以在vimrc文件中如此设置：\nset fileencoding=utf-8 termencoding 可简写为tenc。功能是设置终端使用的编码。这个貌似没什么用。我开始准备用它来解决Windows下gVim安装中文文档乱码的问题，可发现不管用。\n++enc 也可写成 ++encoding。功能是强制指定编码。例如，正在编辑一个编码为gb2312的文件，想将其转存为utf-8编码，可以这样操作：\n:w ++enc=utf-8 newfile.txt ","date":"2010-05-31","description":"","lastmod":"2010-05-31T04:08:02Z","slug":"vim-encoding","tags":["vim"],"title":"VIM中与编码有关的选项","url":"https://blog.zengrong.net/post/vim-encoding/"},{"categories":["technology"],"content":"今天寻找原来写的关于FXG的文章，无意中发现sban写的几篇很不错的文章都看不到了，原来他的博客挂了。google到新的博客，也没有发现原来的博文。但又找到几篇不错的文章，看来还是转过来保险 :lol:\n原文作者：sban\n原文地址：http://sban.biz/239\n以下为转载\n杂症病因及解决方案简略：\n1， 在TileList中如果选择档过多，会出现卷轴，当拖动卷轴时，渲染的进度条会出现花屏现象； 并非TileList有这个问题，在Flex3内，DataGrid，List，Tree等控件如果使用不当，均存在这个问题;在先前的fl组件包内的TileList组件也有类似阴影。根本原因在于，使用ItemRenderer的大数据控件，其在渲染时，并不会一次创建所有数据列/行的显示对象 (ItemRenderer)，它仅会创建在屏幕上可见的数据列/行，并且重复利用这些显示对象，以提交运行时效率。\n可以做这样一种代码实验，以帮助人们理解这种机制：在一个TileList控件内，它本身有滚动条，它的ItemRenderer也使其有滚动条，在多屏数据的情况下，任意滚动一个ItemRenderer的滚动条，然后滚动TileList的滚动条到另一屏，你会发现，虽然数据已经变了，你从未滚动过这个数据，但它与你先前滚动过的那个数据具有相同的滚动位置。\n从严格意义上讲，这并不能算是Adobe的bug，因为如果你严格按照Adobe的官方说明使用，多数情况下，是不会出现的，因此这个bug的复现也颇具难度。从Flex SDk 3.5开始，Adobe Flex团队，对所有基于ItemRenderer实现的数据控件针对开发者遇到的问题进行了改良，优化了SDK内部控件实现方法，并且添加了一个 offscreenExtraRowsOrColumns属性，该属性意为非显示区域的行或列数，用于帮助开发者在特定情况下遇到的花屏问题。\n如果遇到这个问题，如果解决(方案按优先级自上向下排列)：\n1) 修改策划\n显示大数据时，传统滚动条是一个糟糕的设计，因为人的眼晴对于大量的，重复结构的数据，很难定位上次查看的位置，多数人都是边察看边用指在屏幕上做标记。此种情况下，\na)要么不使用滚动条，使用翻页，用户每次翻页后，重新取数据、向数据控件赋值，在这种情况下，DataGrid，TileList等均不会出现花屏问题，因为压根儿就不会有滚动，但在此时，使用Repeater效率更高。\nb)要么设计一种粗粒度滚动条，在这种粗粒度滚动中，每一个点相关于翻页设计中的一页，用户拖动时还相当有手感，相对传统滚动条要好许多，这种设计在许多产品中都已经开始使用。\n2）在更新DataProvider时手动刷新控件视图\n每次当data有变化，均手动再次设置一次ItemRenderer，大意如下：\nlist.itemRenderer = new ClassFactory(YourItemRenderer);\n注：在Gumbo中，如果使用Bindable绑定数据，FB在编译时已经做了代码优化。所以，多数时候，按照官方方法可以避免很多问题。\n3）使用offscreenExtraRowsOrColumns属性调整\n这是最BT的方案，让人感觉是Adobe自己用算法难已处理了，所以请用户告诉控件目前有多少数据列/行在显示区外。具体用法请参照livedoc说明。\n2-4，电子白板中，控制权转移；录制；画面同步\n在白板开发中，控制权转移，画面同步，录影属于基本功能点。在技术技巧上，录影使用ImageSnapshot取得数据，剩下的便是系统架构师的事情。白板若要做好，方方面面必须设计好，特别是多人同时在线应用。\n最基本的白板实现方案是基于ShareObject，但这种实现是demo级的，既浪费资源性能又低，比较合理的设计，作者认为应该是这样：\n1）控制权转移实则是多人数据同步，数据同步不要使用SO，当控制权变化时，由Server处理并向Client广播，如果在同一时间内白板只充许有一人控制，此时仅需向二人广播，如果其它人也需要知道当前人控权者是谁，通过另外统一的状态广播实现。\n2）画面同步必须设计出二种机制，一种为指令绘制型，另一种为图像同步型。对于后来进入观看白板的人用户，它第一次需要向server请求当前最新的白板画面，server选择一个最可靠的client的白板数据发给新来者，或者使用p2p技术直接由client端发送。指令绘制型用于在活动用户之间更新白板数据、动作。指令需自行设计，这种设计可以轻松实现白板重绘。\n3）录影在这里有两种实现，一种为ImageSnapshot，另一种为指令重绘型。\n5，FLASH的置顶问题\n默认情况下，在网页中swf对象之上放置不了浮动层，解决方案是修改FlashVars属性值，把wmode修改为opaque，同时对照其它对象，排列z-index。\n6，聊天表情無法复制粘贴，由于聊天表情是动态文本，所以添加到textflow中的是一个sprite对象\n这个问题没有一步到位的方案，属于架构师考虑的设计问题。自定义一种输入框，自定义一套emoticon标签，每一个表情用一个自定义标签标识，监听输入框的copy与paste事件，送入剪辑板的数据仅包含emoticon标签，而不是图像数据，在paste时进行解析、替换。\n7，老板模式，当系统焦点离开air程序后，无法检测到系统的key_down事件\n系统焦点离开AIR后，即使在AIR中有KEY_DOWN事情监听也无济于事。解决方案是，rumtime升级到AIR 2.0，在AIR程序启动时，同时启动一个C++ native progress，当AIR程序最小化至系统托盘后，由C++程序负责监听系统按键，以此实现AIR程度快捷键呼出。\n8，Air注册表操作(登陆启动look程序)\nAir直接写不了，解决方案有两种方向：\n1）与问题7同，使用nvtive progress写注册表\n2）不使用AIR，使用替换解决方案Flex4U\n9，在1.5的air运行时环境下，中文不能输入问题，因为客户端可能已经安装1.5的运行时，在网页安装中只能检测客户端是否安装了运行时，却无法检测到版本信息或者更新运行时\n这里面有二个问题，第一个，对于必须要求rumtime为2.0的air程序，在编译时指定，强制用户升级。\n第二个，在网页中安装air，如何知道用户的air rumtime版本？\nAdobe的air网页在线安装是通过这个swf实现的：\nhttp://airdownload.adobe.com/air/browserapi/air.swf\n下载，反编译后，里面使用一个叫做ProductManager的类进行客户端环境的签别，验证。一共有两个类文件：AIR.as与 AIRLCEndpoint.as。\n从原理上讲，可以hack反编译之后的源码，重新编译为自已的air.swf，然后自定义bridge网页安装实现。\n10，隐藏window边框后，鼠标在拖动窗口边界改变窗口大小时，不能设置系统光标样式\n这个问题不复杂。如果不使用系统镶边，自定义光标显示对象，添加进显示列表，并添加事情监听实现缩放与拖动逻辑。在livedoc中官方曾见有示例，有兴趣的朋友可以查一查。\n","date":"2010-05-29","description":"","lastmod":"2010-05-29T15:34:24Z","slug":"1022","tags":["air","flex","spark"],"title":"【转】十个Flex/Air疑难杂症及解决方案简略","url":"https://blog.zengrong.net/post/1022/"},{"categories":["use"],"content":"原文地址：http://bbs.wissky.com/t21986-p1-1.html\n前言： 以下内容 是在不严重损失编码速度的情况下 生成一个较高品质的 AVC+AAC 的视频文件 并且能被PSP正常播放 进行讨论的\n如果你是一个追求编码速度至上的人 那么阅读到此为止了\n另外基于时间和篇幅的关系 我不再对一些专业名词做过多解释了\n整体思路： 首先要求是 尽可能的让mencoder完成 解码 编码 和 容器封装 如果完成其中任何一项不能完成 再考虑尽可能少的增加辅助工具\n在自己试验中 发现mencoder不能完成 mp4封装 因为在Mplayer手册里提到 对于mencoder自身 AVI是最可靠的输出容器 其他容器都会有不稳定的因素\n在这期间 我曾尝试使用 mencoder内置mp4封装容器\n-of lavf -lavfopts format=psp -o OUTPUT\n但 mencoder提示 它不支持包含B帧的视频流 只能作罢\n于是选择MP4BOX 作为最后的MP4容器的封装工具\n需要准备的软件: 1)mencoder\n本人使用版本的是这里下载的\nhttp://sourceforge.net/projects/mplayer-win32/files/MPlayer%20%2F%20MEncoder/MPlayer-p4-svn-29355.7z/download\n当然你也可在这里选择 适合你的最新版\nhttp://sourceforge.net/projects/mplayer-win32/files/\n2)MP4BOX\n我是用的是 最新的DEV版 （0.4.6-dev_20090715）\n你可以在这里下载到\nhttp://www.digital-digest.com/software/MP4Box.html\n本次视频压缩使用以上版本的工具 在好友PSP 系统版本号为3.71M33-4 测试成功播放 压缩后的视频 并且在PPA能也能正常播放\n具体步骤说明： 1）视频来源是DVD NTSC制式 （视频流为 30P的MPEG2 音频是5.1ch的AC3）\n另外为了缩短测试时间 使用 Womble.MPEG.Video.Wizard.DVD.v4.0.4.114 无损截取了 30s的片段\n2）为了体现质量最大化 也理所应当的使用了x264的2pass模式\n3）编码参数\n（本人的CPU是 Intel Core 2 T5500的 \\ 内存为 DDR II 800 4GB \\ 主板为 Intel Mobile 945）\n1st pass （编码速度约：48fps CPU占用率约：75%)\nmencoder -sws 9 -oac faac -faacopts mpeg=4:br=160:object=2 -srate 48000 -channels 2 -vf crop=708:480:12:0,harddup,scale=480:272,unsharp=l3x3:0.5,dsize=-1 -ovc x264 -ffourcc H264 -x264encopts bitrate=900:turbo=1:keyint=300:keyint_min=6:scenecut=40:frameref=3:bframes=3:b_adapt=2:nob_pyramid:deblock:deblock=-1,-2:cabac:qp_step=6:qcomp=0.8:direct_pred=auto:weight_b:partitions=i4x4p8x8p4x4b8x8:no8x8dct:me=umh:me_range=24:subq=7:chroma_me:mixed_refs:trellis=2:psy-rd=0,0:nofast_pskip:nodct_decimate:aq_mode=2:aq_strength=1:level_idc=22:nointerlaced:nopsnr:nossim:threads=auto:pass=1 \u0026quot;INPUT.mpg\u0026quot; -o \u0026quot;OUTPUT.avi\u0026quot; 2ed pass （编码速度约：17fps CPU占用率约\u0026gt;90%)\nmencoder -sws 9 -oac faac -faacopts mpeg=4:br=160:object=2 -srate 48000 -channels 2 -vf crop=708:480:12:0,harddup,scale=480:272,unsharp=l3x3:0.5,dsize=-1 -ovc x264 -ffourcc H264 -x264encopts bitrate=900:turbo=1:keyint=300:keyint_min=6:scenecut=40:frameref=3:bframes=3:b_adapt=2:nob_pyramid:deblock:deblock=-1,-2:cabac:qp_step=6:qcomp=0.8:direct_pred=auto:weight_b:partitions=i4x4p8x8p4x4b8x8:no8x8dct:me=umh:me_range=24:subq=7:chroma_me:mixed_refs:trellis=2:psy-rd=0,0:nofast_pskip:nodct_decimate:aq_mode=2:aq_strength=1:level_idc=22:nointerlaced:nopsnr:nossim:threads=auto:pass=2 \u0026quot;INPUT.mpg\u0026quot; -o \u0026quot;OUTPUT.avi\u0026quot; 4）关于编码命令行的说明：\na] -sws 9\n因为视频 剪裁后需要进行 缩放为 480*272 于是就有了一个 选择什么样的缩放滤镜的问题\n这里推荐使用 lanczos滤镜\n=======================================================\nb] unsharp=l3x3:0.5\n这个滤镜是起到提升视觉清晰度的作用\n滤镜的中文译名为 （非锐化遮蔽罩）\n大致原理是 寻找出画像的轮廓 接着在轮廓部分 使其明度或是色度的过度产生很大的变化\n如果你玩过 Ps 的 USM滤镜就明白它的具体原理和显著效果了\n下面是老外浅显易懂的专业解释\nhttp://www.cambridgeincolour.com/tutorials/unsharp-mask.htm\n=======================================================\nC] dsize=-1\n如果mencoder自己不能获得 Movie AR的话 就会默认指定为 4：3\n这导致 x264 以DAR =4：3 为比例目标 调整SAR值 编码后输出画面的DAR永远是 4：3\n就算在x264命令行里指定 sar为1也无效\n此时 使用如果dsize=-1 就会使用 PAR的比例\n换言之sar永远为1:1\n=======================================================\nD] -channels 2\n这个滤镜的作用为 告诉音频解码 输出的声道为2.0ch\n使用理由很简单 PSP只能接受 2.0ch的音频\n缺点是 并不是mencoder内置的一切音频解码器 都支持输出2.0ch\n但是一般来说问题不大 大多数多声道的视频 一般以DVD片源比较常见 AC3的解码器可以正确downmix到2.0ch\n当然可以使用 pan滤镜手动映射,混合声道 但是这样做命令行的通用性就变差了\n因为 pan滤镜需要手动详细设定\n=======================================================\nE] 其他注意事项：\n有些人会说 1st pass时可以把 音频解码和编码禁用\n但我说这不行 因为x264 1st pass时 会有对每一帧的信息进行记录 （比如帧的类型 量化值等等） 这些信息是为了 2pass做准备的 （1st pass 和 2ed pass 输入的帧的内容 和序列组 必须完全一致）\n当你考虑到 2ed pass时 mencoder会为了 音画同步 而skip一些帧的时候\n而1st pass因为音频禁用 不会出现解码skip帧\n此时 1st pass 和 2ed pass 输入画面就不再是相同的帧序列组了\n这时 x264可能会提示你 1pass时是P帧 但是2pass实际编码时却是B帧之类 的警告提示\n5）从mencoder输出的AVI容器里 提取 视频流 和 音频流\n这里我们使用 MP4BOX完成提取工作\n命令行如下：（分两次执行）\nMP4Box -aviraw audio OUTPUT.avi MP4Box -aviraw video OUTPUT.avi 一些提示：\nMP4BOX会输出 视频流名为 OUTPUT.h264\n音频流名为 OUTPUT.raw （也许是 MP4不能识别出 AAC IN AVI容器的 FOURCC）\n接着手工rename OUTPUT.raw=》OUTPUT.aac\n6)把视频流和音频流 封装为MP4容器\n同样我们仍旧需要使用MP4BOX\n命令行如下：\nMP4Box -add \u0026quot;OUTPUT.h264\u0026quot;:fps=29.976 -add \u0026quot;OUTPUT.aac\u0026quot; \u0026quot;PSP.mp4\u0026quot; 提示要点：\nMP4BOX无法识别 AVC流的 FPS值需要手工设定 fps=xxx\n但可以借助 Meida info识别 AVC流的FPS值\n最后想说的： 本人其实并不擅长用 mencoder 用得最多的是AVS+x264\n上述的内容都是参考官方文档 和 国外网友的一些实例 花了2天的时间整理出来的 （官方的文档并不完善 有些命令行 并未出现在文档里 需要 mail list 和网友实例 加以补充）\n所以难免有纰漏和曲解 望大家指正\n虽然经过 朋友的PSP测试样片可以正常播放 但仅仅限于这个实例下 和 上述列举的工具为前提\n本人并不能保证 因为软件的升级 片源的变化 等 情况下 按照上述的方法 仍能正常输出 被PSP播放的视频\n在未来的几个月里 最多再对编码的命令行维护一次 （因为x264的默认值 即将在mencoder里发生改变）\n最后附上样片：(30秒) http://www.rayfile.com/files/062ec9d9-72e0-11de-a749-0019d11a795f/\n","date":"2010-05-28","description":"","lastmod":"2010-05-28T01:08:12Z","slug":"1020","tags":["h264","mencoder","video"],"title":"【转】Step By Step 使用 mencoder和 mp4box 输出高品质AVC+AAC For PSP","url":"https://blog.zengrong.net/post/1020/"},{"categories":["technology"],"content":"第一篇：字符集和编码II: fat/msdos/vfat（链接至原作者博客）\n具体到文件名乱码的问题，需要明确两点\n第一，文件名作为一个字符串，需要被编码后存入文件系统； 第二，Linux内核无非是个特殊的应用程序，它读取文件名，再把文件名以编码后的形式传递出去。 但Linux内核只能逐字节处理编码流（而Windows NT内核是UCS-2的，逐2字节处理编码流），因此必须采用某种单字节编码（这包括所有的不定长编码）进行输出——这就是Linux内核所谓的NLS。\n在对文件名的处理上，fat和vfat的区别在于：fat/msdos只支持短文件名（8.3命名法），而vfat加入了对长文件名和UNICODE的支持。\n为了保持与fat的兼容性，在vfat中，每个文件同时拥有“长”文件名和短文件名，其中短文件名不区分大小写（实际上是不允许小写字母出现在文件名中）。可以这么理解，对于vfat，“长文件名”是文件真正的名字，“短文件名”则是提供兼容性的名字。举例来说，文件“真名”为abc.txt，它的短文件名是ABC.TXT；文件“真名”为alongname.txt，它的短文件名则是ALONGN~1.TXT。\n无论是fat还是vfat，短文件名按codepage编码存储，长文件名按UNICODE编码存储。因此，如果文件的真名（也就是长文件名）是\\(s\\)，短文件名是\\(s’\\)，则它们在文件系统中分别被存储为\\[s,\\,\\varphi_{enc} \\left( s' \\right).\\]\n为了访问fat/vfat文件系统，我们需要用内核的msdos或vfat模块。它们有三个跟字符集有关的内核参数：codepage，iocharset，utf8。我们来确定它们对应着什么样的编码或解码函数。\ncodepage=enc：用来转换短文件名中字节码为128~255的代码页。这一选项指定的是短文件名的解码函数\\( \\varphi_{enc}^{-1} \\)（因为短文件名被codepage编码了），解码的结果是短文件名被用unicode表示； iocharset=enc：用来转换长文件名和解码后的短文件名。也就是说，这一选项指定编码函数\\(\\varphi_{enc}\\)，使得内核最终的输出是经过\\(\\varphi_{enc}\\)编码的； utf8：几乎等于iocharset=utf8 这样，参数有如下的具体选择：\ncodepage：这一选项只跟短文件名有关。短文件名可以通过mount -t msdos看到，也可以用windows命令行看到。不在乎短文件名的同学，这个选项完全无所谓；在乎短文件名并且使用简体中文的同学，请写\ncodepage=936 iocharset：分情况讨论。\n若locale（不知道什么是locale的同志请自行补课）不是utf8，则选择相应的locale，比如GB系就应该选\niocharset=cp936 若locale是utf8的，可以选择\niocharset=utf8 但这样做的缺点是会导致vfat模块将允许短文件名使用小写字母，这与windows是不兼容的（使用iocharset=utf8时，内核会出一条警告信息的）；\nutf8：一个解决办法是使用utf8参数，只要iocharset!=utf8，vfat就会处理大小写的问题；而utf8参数则会最终处理字符集的转换。utf8参数只能显式地通过mount调用（不能在编译内核时预先指定，当然也可以写在/etc/fstab里），我的意思是这个选项不能偷懒不写。\n最后是结论：\ncodepage=936，这一选项可以在内核默认设置；\n本地locale是gb系的，参数应为\nmount -t vfat -o iocharset=cp936 本地locale是utf8的，如果能忍受内核每次都要警告，并且不跟windows交互，可以\nmount -t vfat -o iocharset=utf8 要是不能忍3或者交互时出现奇怪的问题，那就\nmount -t vfat -o iocharset=cp936,utf8 实际上只要内核默认的iocharset不是utf8，直接写\nmount -o utf8 就可以，这里iocharset=xxx的作用仅仅是处理大小写，所以怎么填都没关系。\n我们来看一下这些选项究竟做了些什么事情，假设一个FAT文件系统在简体中文的windows上用过，而Linux程序的locale设置为UTF-8，文件名为\\(s\\)，其短文件名为\\(s’\\)\n在文件系统上（也就是在硬盘里），文件名被存为\\(s\\)，短文件名被存为\\(\\varphi_{cp936} \\left( s’ \\right)\\)； 内核通过codepage=936选项，将映射\\(\\varphi_{cp936}^{-1}\\)作用于\\(\\varphi_{cp936} \\left( s’ \\right)\\)，得到\\[\\varphi_{cp936}^{-1} \\circ \\varphi_{cp936} \\left( s' \\right) = s';\\] 内核通过utf8或iocharset=utf8选项，将映射\\(\\varphi_{utf8}\\)作用于\\(s, s’\\)，最终向应用程序输出\\[\\varphi_{utf8} \\left( s' \\right), \\varphi_{utf8} \\left( s \\right);\\] 应用程序（如ls、nautilus）通过locale的设置，将\\(\\varphi_{utf8}^{-1}\\)作用于内核输出，得到我们所看见的字符串\\(s,s’\\). 第二篇：smbfs/cifs（链接至原作者博客）\nSMB/CIFS是M$用于网络文件和打印共享的协议，linux下有不少该协议的实现（详情请咨询wikipedia）。server端基本是samba一统天下，client端常用的则有samba和linux的内核vfs模块smbfs/cifs。其中smbfs是依赖于samba的（更具体的，smbfs需要调用samba提供的smbmount等模块），而cifs则是一个独立的实现（尽管samba也提供了一个helper小程序mount.cifs，但这仅仅是一个转发函数而已，真正的mount过程完全由内核实现；也就是说，不需要安装samba就可以直接使用mount -t cifs，mount.cifs可以看成mount -t cifs的shortcut）。\n服务器端我们不做讨论，smbconf的文章满天飞。\n先说samba，这基本是SMB/CIFS的一个完整实现。大多数应用程序（如mc/nautilus/dolphin/mplayer/cups等)对windows网络共享的访问都依赖于samba所提供的smbclient，并且通常使用smb://server/share/file这种形式的URL。对于简单和偶尔的文件共享，一般来说这足够用了；另外，要用cups进行打印只能用samba。\n然而，smb://server/share/file这种形式毕竟用起来不方便，特别是大部分工具并不支持samba。这时候使用内核模块就很方便，因为对所有的应用程序来说都是透明的，而且只要不用windows打印机就完全不需要依赖samba。\n前面提到过内核提供了两个vfs模块：smbfs和cifs（注意区分作为协议的CIFS和作为内核vfs模块的cifs）。简单来说，用cifs。\n一、字符集/编码问题 足够新的服务端（windows\u0026gt;98，samba\u0026gt;=3）默认采用Unicode作内部编码以及网络传输，一般不会带来太多字符集/编码上的问题。\nsmbfs和cifs牵涉到字符集/编码的选项有两个：codepage和iocharset，其含义和MS系的其他文件系统（dosfs, vfat, ntfs, etc.）是一致的，前者指服务器传入字节流的编码，后者指本地所用的编码（具体解释请参见该文）。由于cifs总假定服务器用Unicode传输（这个假定基本上不会有问题，有问题的时候就只好用smbfs了），因此它没有codepage选项。\n下面简单说明一下使用内核cifs模块进行mount网络共享的设置。\n首先，保证服务端的网络传输字符集是Unicode。对于win2k之后的windows操作系统、以及正常配置的samba server，这一点一般不需要操心。（如果有特殊情况或需要而使得samba server的传输字符集不是Unicode，请参考该文）。\n其次，确定客户端（也就是本机）所用的字符集。支持中文的字符集有UTF-8，GB2312、GBK、GB18030等等。如果是UTF-8，取NLS=utf8；如果是GB2312、GBK、GB18030，取NLS=cp936。（对于足够新的发行版，基本上utf8一统天下。）\n最后的mount命令如下：\nmount -t cifs -o iocharset=${NLS},user=me,pass=you,file_mode=0644,dir_mode=0755,uid=subi,gid=subi //server/share /smbshare 另外，mount.cifs是samba自带的一个小程序，就是一个简单的wrapper。我印象中mount.cifs主要的好处是它可以interactively地从标准输入接受用户名和密码，而mount -t cifs只能显式在命令行里指定。通常，mount.cifs在samba软件包里（有的发行版也给mount.cifs单独打包），而mount在util-linux软件包里。 二、Samba的符号链接 问题：对于Samba共享目录中的符号链接（这些link指向共享目录外），Windows客户端能当成普通目录透明访问的。但在smbfs或cifs客户端里，这些符号链接能被认出来，但是链接所指的的目录在本地机器上当然是不存在的（即使存在也是本地机器的内容），因此服务器的相应资源无法访问。而用samba所附的smbclient可以正确访问。\n这个问题同时与samba服务器和smbfs、cifs有关。symlink是samba所实现的unix extension的一部分（其他还包括symlink、hardlink、uid、gid等）。samba、smbfs/cifs在某些版本都实现了对unix extension的支持，其中包括symlink。\n对于symlink的解释，有两种方式完成：由samba服务器follow symlink，这时候客户端是看不到symlink的存在的（比如Win客户端总是这样）；由客户端follow symlink，这时候客户端知道它们是符号链接。\n因此，对于windows客户端，symlink总是由服务器处理；而对于linux客户端，特别的，对于smbfs和cifs，symlink的处理依赖于服务器端和客户端的配置。samba服务器的配置文件smb.conf文件里，三个有关选项的默认设置如下：\nunix extensions = yes follow symlinks = yes wide links=yes 第一个选项如果选no，则总是由服务器端来处理symlink，这时候linux的客户端和win的客户端表现是相同的——symlink被当作普通目录；而默认设置yes则是启用unix extensioin，在客户端（smbfs/cifs）也开启unix extension支持的情况下，将会看到symlink并由客户端来handle them。\n第二、三个选项设置samba服务器处理symlink的方式：是否跟随链接、是否跟随指向共享目录外的链接。\n而在客户端，windows就不说了。smbfs总是启用unix extension，而cifs可以通过/proc/fs/cifs/LinuxExtensionsEnabled来控制。\n两种处理symlink的方式都有需求。在需要访问共享目录外内容时，由server follow link是唯一的选择；而把共享目录当作一般的网络文件系统，直接支持unix extension更符合linux的需要。如果想用smbfs又想看到共享目录外内容，请阅读Q:[30nov04] smbfs and smbclient behave differently with symlinks (soft links)进行相应的改动（修改服务器端的smb.conf或hack客户端的smbfs kernel code）。否则，最好的选择是cifs：希望由server follow link，只需将/proc/fs/cifs/LinuxExtensionsEnabled设为0后remount，该值默认为1，所以若由client follow link，啥也不用干。\n","date":"2010-05-27","description":"","lastmod":"2010-05-27T07:47:38Z","slug":"1019","tags":["i18n","linux"],"title":"【转】关于mount/samba/字符集的两篇好文","url":"https://blog.zengrong.net/post/1019/"},{"categories":["use"],"content":"两篇好文：\nUNIX 高手的 10 个习惯-克服不良的 UNIX 使用模式\nUNIX 高手的另外 10 个习惯-成为 UNIX 命令行高手\n","date":"2010-05-27","description":"","lastmod":"2010-05-27T05:53:16Z","slug":"1018","tags":["linux"],"title":"UNIX 高手的 20 个习惯","url":"https://blog.zengrong.net/post/1018/"},{"categories":["use"],"content":"/usr/share/icons\n系统图标文件夹\n/usr/share/applications\n系统菜单文件夹，要在左上角的应用程序菜单中添加一项，可以在这里加一个.desktop文件\n~/.local/share/applications\n用户菜单文件夹，在这里加入的菜单项就只会显示在当前用户的应用程序菜单中了。\n不过有个奇怪的问题：如果使用应用程序菜单右键的“编辑菜单”功能添加菜单项目，出现的菜单项会出现在这个文件夹中，但是没有.desktop扩展名，反而有默认图标，只能在Nautilus中看到，使用ls -a都看不到。双击这个文件可以直接启动程序。双击其他的.desktop文件则不能。实在是个令人费解的问题……\n~/.local/share/icons\n用户图标文件夹\n~/.config/menus\n这里的settings.menu和application.menu似乎是用来定义菜单分类的。如果有些程序卸载了但是菜单还在(例如WINE)，可以试试删除applications-merged目录下的内容。\n顺便记录一下.desktop文件应用程序分类的对应关系（来自）：\n互联网 Network 办公 Office 图像 Graphics 声音和视频 AudioVideo 系统工具 System 编程 Development 辅助选项 Utility 首选项 Settings ","date":"2010-05-27","description":"","lastmod":"2010-05-27T02:08:59Z","slug":"1016","tags":["ubuntu","wine"],"title":"Ubuntu下与菜单和图标相关的几个文件夹","url":"https://blog.zengrong.net/post/1016/"},{"categories":["use"],"content":"系列全部文章：抛弃Windows，用Ubuntu办公\n一、使用Windows字体 Ubuntu的字体文件夹在/usr/share/fonts，其中truetype字体放在该目录的truetype子目录下。因此只需要把windows下的常用字体文件复制到这个目录就可以了。我是在/usr/share/fonts/truetype下面建立了一个ms子目录，然后把Windows下面的楷体、黑体、宋体、仿宋四个字体复制过来，再重建一下字体缓存即可。具体的操作如下：\n1.在Windows下复制 C:\\Windows\\Fonts 目录下的 simsun.ttc、simhei.ttf、simfang.ttf、simfang.ttf 到U buntu的 ~/windowsfonts 目录中；\n2.执行下面的命令：\nsudo mkdir /usr/share/fonts/truetype/ms sudo cp ~/windowsfonts/* /usr/share/fonts/truetype/ms sudo fc-cache -fv 重建字体缓存后启动openoffice，就可以看到这些新安装的字体了。顺便说一句，Windows XP自带的楷体和仿宋，是遵循gb2312标准的，所以很多生僻字是无法显示的，例如“硚”。而Windows 7下面自带的楷体和仿宋都支持大字符集了。\n虽然我也喜欢文泉驿，但这毕竟是在中国，在周围的同事都在使用WindowsXP的情况下，在绝大多数人都认为Word就是唯一的办公软件的前提下，为了保证Ubuntu下面制作的doc文档能有个正确的排版和字体显示，我也只能入乡随俗的侵害一下版权了。 :oops:\n二、永中Office（EIO）的字体问题 装好新字体后，若启动永中office，会发现新字体还是没有显示。这是因为永中Office的字体文件夹要求是这个路径： /usr/share/fonts/zh_CN/TrueType/ ，把字体再复制一份到该目录下，再重新启动永中Office即可。\n若仍不认，就将永中Office安装目录下的system下的fonts和index删除后，再重新启动。\n另外，永中Office不认TTC扩展名的字体文件。可以将TTC扩展名改为TTF，或者使用转换工具将TTC转换为TTF。（TTC与TTF的区别、转换工具）\n如果为了节省硬盘空间不愿意重新复制字体文件，可以使用ln建立符号链接：\ncd /usr/share/fonts/truetype/ms sudo ln -s simsun.ttf /usr/share/fonts/zh_CN/TrueType/simsun.ttf 如果ms目录下的字体文件很多，不愿意一个个建立链接，可以用这个代码：\n要批量在B目录下面建立A目录下的所有文件的ln连接\nls A|xargs -i ln -s /path/A/{} /path/B/{} ","date":"2010-05-26","description":"","lastmod":"2010-05-26T01:05:08Z","slug":"use-ubuntu-to-replace-windows-font","tags":["linux","ubuntu","byewindows","fromto"],"title":"抛弃Windows，用Ubuntu办公-6.字体","url":"https://blog.zengrong.net/post/use-ubuntu-to-replace-windows-font/"},{"categories":["use"],"content":"标题很长，是因为这问题很严重\n我在Ubuntu10.04下使用源中自带的mencoder压缩H.264视频，就出现了这个错误：\nMPlayer was compiled without libfaac\n找了一堆资料，发现是Ubuntu10.04自带的mencoder不支持faac编码。于是寄希望于新立得，安装了faac、libfaac0、libfaac-dev等包，仍然无用。苦恼之下只有自己编译了。\n其实编译这事，我一直是比较排斥的。一直以来，我都是希望把Ubuntu作为Windows在办公电脑上的替代品来使用的。既然是替代品，当然应该使用简单、老少咸宜，而编译就已经超出了这个范畴。不过转念想来，我的应用其实也已经超出了这个范畴了。哪个office boy会吃饱了没事干在Windows下面用命令行来压制H.264视频？至少在我工作的单位，知道H.264这个名词是什么意思的，估计不会比三毛的头发数量多 :razz:\nmencoder是包含在mplayer中的，下载mplayer的源码一看，原来mplayer有良好的中文文档支持，手册和操作提示都有中文资源。相比而言，官方源中的mplayer虽然手册是中文，但帮助和提示信息就是英文了。于是摩拳擦掌，准备编译一套中文的mplayer了！\n一、下载 1.进入MPlayer官方网站的下载页面下载源码：mplayer-checkout-snapshot.tar.bz2，使用svn的方法该页面也有详述。\n解压：\ntar xjf mplayer-checkout-snapshot.tar.bz2 2.下载codecs：all-20100303.tar.bz（所有codecs）\n二、安装编译包 通过新立得发现，libgtk2.0和libstdc++6默认已经安装了，因此就只需要安装build-essential包了（其实如果不制作deb包的话，连build-essential也不需要装）：\nsudo apt-get install build-essential 三、调整参数 使用\n./configure --help 查看可以使用的参数。最有用的就是安装目录参数和语言参数。如下：\nInstallation directories:\n--prefix=DIR prefix directory for installation [/usr/local]\n--bindir=DIR directory for installing binaries [PREFIX/bin]\n--datadir=DIR directory for installing machine independent\ndata files (skins, etc) [PREFIX/share/mplayer]\n--mandir=DIR directory for installing man pages [PREFIX/share/man]\n--confdir=DIR directory for installing configuration files\n[PREFIX/etc/mplayer]\n--libdir=DIR directory for object code libraries [PREFIX/lib]\n--codecsdir=DIR directory for binary codecs [LIBDIR/codecs]\nLanguage options:\n--charset=charset convert the console messages to this character set\n--language-doc=lang language to use for the documentation [en]\n--language-man=lang language to use for the man pages [en]\n--language-msg=lang language to use for the messages and the GUI [en]\n--language=lang default language to use [en]\n我的配置参数是这样的：\n./configure --prefix=/opt/mplayer --language=zh_CN --language-doc=zh_CN --language-man=zh_CN --language-msg=zh_CN 这样编译安装的mplayer，使用man mplayer是看不到手册的，因为手册并不是在默认的man路径中。而是在/opt/mplayer/share/man中。我这样编译是为了实现“绿色软件”（Windows思想作怪），以后若不想要了，直接删除/opt/mplayer即可。\n也可以指定--mandir=/usr/share/man，这样编译安装的mplayer，就可以直接通过man mplayer命令看到中文手册了。\n四、安装codec库支持 在刚开始的几次编译中，我把Codec中的几十个参数一个个进行了添加。后来才发现我的做法是错误的。并非在参数中加上了-enable-x264 --enable-mp3lame，编译的时候就会将x264和mp3支持包含进去。相反的，如果系统中没有x264与lame库，编译的时候就会报错。正确的做法应该是安装对应的codec源码库，不使用任何的enabled参数，让配置文件自行检测是否支持该codec。\n那么如何知道系统中是否包含需要的编码呢？可以使用重定向将configure显示的内容保存到一个文本文件中，然后查看文本文件即可。\n./configure --prefix=/opt/mplayer --language=zh_CN --language-doc=zh_CN --language-man=zh_CN --language-msg=zh_CN \u0026gt; log.txt 打开log.txt，搜索“x264”，可以看到类似的文字：\nChecking for x264 ... no (in libavcodec: no)\n由此可以看出，系统默认是不支持x264编码的。同样的，常用的xvid、lame编码也是一概不支持的：\nChecking for libmp3lame ... no (in libavcodec: no)\nChecking for Xvid ... no\n在文件底部，可以看到配置的参数以及支持和不支持的编码信息：\nConfig files successfully generated by ./configure --prefix=/opt/mplayer --language=zh_CN --language-doc=zh_CN --language-man=zh_CN --language-msg=zh_CN !\nInstall prefix: /opt/mplayer\nData directory: /opt/mplayer/share/mplayer\nConfig direct.: /opt/mplayer/etc/mplayer\nByte order: little-endian\nOptimizing for: native\nLanguages:\nMessages/GUI: zh_CN\nManual pages: zh_CN\nDocumentation: zh_CN\nEnabled optional drivers:\nInput: dvdnav(internal) ftp pvr tv-v4l2 tv-v4l tv libdvdcss(internal) dvdread(internal) vcd dvb network\nCodecs: libavcodec(internal) qtx real xanim win32 faad2(internal) libmpeg2(internal) mp3lib(internal) tremor(internal)\nAudio output: oss v4l2 mpegpes(dvb)\nVideo output: v4l2 pnm mpegpes(dvb) fbdev cvidix yuv4mpeg md5sum tga\nDisabled optional drivers:\nInput: vstream radio tv-dshow live555 nemesi cddb cdda smb\nCodecs: libschroedinger libdirac x264 xvid libdv libopencore_amrwb libopencore_amrnb faac musepack libdca liba52 libtheora speex toolame twolame libmad liblzo gif OpenJPEG\nAudio output: sun alsa openal jack pulse nas esd arts ivtv dxr2 sdl\nVideo output: zr zr2 ivtv dxr3 dxr2 matrixview opengl sdl vesa gif89a jpeg svga caca aa ggi xmga mga\nix winvidix 3dfx dga vdpau xvmc xv x11 dfbmga directfb bl xvr100 tdfx_vid wii s3fb tdfxfb\n要系统支持这些codec也很简单，需要什么编码，在新立得里面搜索就可以了，例如，如果希望编译出来的mencoder支持xvid编码，就在新立得里面搜索“xvid”然后安装libxvidcore-dev即可，新立得会将相关的包一并选中。记住，搜索的时候可能有多个类似的包，但一定要安装-dev结尾的，这个才是源码。如下图：\n我安装的所有支持包如下：\nlibxvidcore-dev libmp3lame-dev libdv4-dev libopencore_amrwb-dev libopencore_amrnb-dev libfaac-dev libmpcdec-dev libdts-dev liba52-0.7.4-dev libtheora-dev libspeex-dev libtwolame-dev libmad0-dev liblzo2-dev libgif-dev libopenjpeg-dev libjpeg62-dev\n除了x264之外，常用的codec都在上面了。为什么不使用新立得安装x264呢？因为Ubuntu源中带的libx264-dev不能被自动识别。因此，需要自行下载x264编译安装，或者在这里下载国外网友打包好的deb进行安装。编译安装x264的方法放狗随便就能搜到。反正我是直接安装deb的 :em67:\n这两个deb都要装：；\nlibx264-dev_0.96.1602+git69588a-1_i386.deb\nlibx264-96_0.96.1602+git69588a-1_i386.deb\n安装带有dev这个的时候，Ubuntu会提示“您最好从软件仓库中安装libx264-dev，因为通常有更好的支持”，不要理他。\n全部安装完成后，再执行configure一次，查看是否还有codec漏掉了。如果没问题的话，就可以编译了。\n五、编译与安装 1.make的时间比较长，所以要有点耐心。当然如果是4核CPU就另当别论 :em67: 由于安装目录在/opt下，因此install的时候加上sudo：\nmake sudo make install 2.将先前下载的外部codec复制到对应的文件夹：\ntar xjf all-20100303.tar.bz2 sudo mkdir /opt/mplayer/lib/codecs sudo mv all-20100303/* /opt/mplayer/lib/codecs/ 3.由于安装路径不在系统路径中，因此制作链接让命令随时可用\ncd /opt/mplayer/bin sudo ln -s mplayer /usr/local/bin/mplayer sudo ln -s mencoder /usr/local/bin/mencoder 4.在manpath.conf中添加环境变量\n编辑/etc/manpath.config，在\nMANDATORY_MANPATH /usr/local/share/man\n一行下方加入\nMANDATORY_MANPATH /opt/mplayer/share/man\n如果在编译前指定了--mandir=/usr/share/man，这里就不用这么做了。\n六、反安装与其他 由于编译的时候指定了路径，因此反安装的时候，只需要删除/opt/mplayer即可。\n当然，也可以使用这个命名：\nmake uninstall 但这个命令删得并不干净，只会把文件删除，文件夹是仍然保留的。\n我一共编译了20多次，才最终形成了上面这些经验。如果看完了上面文章，仍然不能一次编译成功的，最好在下次编译之前，执行一下这条命令：\nmake clean 好了！享受中文的Mplayer和MEncoder吧！ {style=\u0026quot;color:red\u0026quot;}\n","date":"2010-05-25","description":"","lastmod":"2010-05-25T14:55:44Z","slug":"1012","tags":["h264","mencoder","ubuntu","video"],"title":"手动编译mplayer(mencoder)，支持x264+AAC，解决ubuntu下使用mencoder压缩视频出现MPlayer was compiled without libfaac错误问题","url":"https://blog.zengrong.net/post/1012/"},{"categories":["use"],"content":"系列全部文章：抛弃Windows，用Ubuntu办公\n作为一个Windows的重度使用者，难免会保留一些Windows下面的“坏习惯”。例如喜欢用Win+R快捷键来运行程序，使用Win+D来显示桌面等等。保留这些习惯，也是让我快速融入Ubuntu的一个桥梁。\n所以，这篇文章中要做的就是把我在Windows下面的习惯移植到Ubuntu中。\n一、快捷键 使用“系统-首选项-键盘快捷键”可以设置常用的快捷键。我设置了这样几个（Mod4就是Windows键）：\n锁住屏幕：Mod4+L 主文件夹：Mod4+H 搜索：Mod4+S 显示面板的“应用程序”对话框：Mod4+R 显示面板主菜单：Mod4+F1（其实这个应该是纯Mod4键，可惜貌似Ubuntu设置快捷键的时候至少要两键，因此只得作罢） 运行终端：Mod4+T 最小化窗口：Shift+Esc（这个是TotalCommand的习惯） 切换到工作区1：Mod4+1（2/3/4同） 隐藏所有正常窗口并将桌面设置为焦点：Mod4+D（这个不是很好用，并非每次都能成功的） 二、快速启动 15年的Windows生涯，让我积累了不少偷懒的方法。用电脑时间长了都知道，键盘的速度比鼠标要快。所以从Windows2K开始，我的桌面上就基本上不放置程序图标了。某段时间我桌面上甚至连“我的电脑”都不放。到现在，桌面上仍然只是我的临时文件夹而已。启动程序就要靠“快速启动器”。\n在Windows下面，我用过两种快速启动程序的方式，第一种是把所有的常用软件的快捷方式放在一个文件夹中，然后将这个文件夹的路径放置在Path变量中，这样就可以直接使用Win+R快捷键直接打开程序了。但是这种方式有个弊端，就是必须要记住快捷方式的全名才行。于是乎我将所有的快捷方式都命名为不超过5个字符的文件名。例如dreamweaver就叫做dw，photoshop就叫做ps……\n时间长了，常用的程序倒还好，不常用的程序还是记不住。所以这种方式使用了3年之后就放弃了。\n第二种方式一直使用到现在，就是使用Key Launch软件，该软件虽然并非开源软件，但是与几个类似的开源和免费软件比起来，胜在速度快，其他几个软件都是实时扫描改动，但是这样一来就在输入启动短语的时候有延迟（我曾经希望使用Win7的搜索功能实现快速启动，但几秒的延迟让人受不了），而这个软件的搜索基本上是没有延迟的。使用它可以设定几个监视的文件夹，该软件启动的时候会自动扫描这些文件夹下面的所有文件。无论是文件名还是文件介绍（有些exe文件是有介绍的），都可以作为启动的短语。这样一来，即使脑子再不好，只要记住和软件有关的几个词，就没问题。\n在Ubuntu下面，我也找到了一个类似的工具，就是GNOME-Do，可以直接从源里安装：\nsudo apt-get install gnome-do ","date":"2010-05-24","description":"","lastmod":"2010-05-24T15:47:45Z","slug":"use-ubuntu-to-replace-windows-shortcut","tags":["linux","ubuntu","fromto","byewindows"],"title":"抛弃Windows，用Ubuntu办公-5.使用习惯——快捷键设定和快速启动","url":"https://blog.zengrong.net/post/use-ubuntu-to-replace-windows-shortcut/"},{"categories":["impressions"],"content":"一、 20%的人 是富人 80%的人 是穷人\n二、 20%的人 掌握世上80%的财富 80%的人 掌握世上20%的财富\n三、 20%的人 用脖子以上来挣钱 80%的人 用脖子以下赚钱\n四、 20%的人 正面思考着 80%的人 负面思考着\n五、 20%的人 做事业 80%的人 做事情\n六、 20%的人 买时间 80%的人 卖时间\n七、 20%的人 重视经验 80%的人 重视学历\n八、 20%的人 知道行动才有结果 80%的人 认为知识就是力量\n九、 20%的人 我要怎样做就会有钱 80%的人 我要有钱我就会怎样做\n十、 20%的人 有目标 80%的人 爱瞎想\n十一 20%的人 爱投资 80%的人 爱购物\n十二 20%的人 在问题中找答案 80%的人 在答案中找问题\n十三 20%的人 放眼长远 80%的人 在乎眼前\n十四 20%的人 把握机会 80%的人 错失机会\n十五 20%的人 计划未来 80%的人 早上才想今天干什么\n十六 20%的人 按成功的经验做事情 80%的人 按自己的意愿来做\n十七 20%的人 可以重复做简单的事情 80%的人 不愿意做简单的事情\n十八 20%的人 明天的事情今天做 80%的人 今天的事情明天做\n十九 20%的人 如何能办到 80%的人 不可能办到\n二十 20%的人 记笔记 80%的人 忘性好\n二一 20%的人 受成功人的影响 80%的人 受失败人的影响\n二二 20%的人 状态很好 80%的人 状态不好\n二三 20%的人 整理资料 80%的人 不整理资料\n二四 20%的人 相信以后会成功 80%的人 受以前失败的影响\n二五 20%的人 与成功人为伍 80%的人 不愿意改变环境\n二六 20%的人 改变自己 80%的人 改变别人\n二七 20%的人 爱争气 80%的人 爱生气\n二八 20%的人 鼓励和赞美 80%的人 批评和漫骂\n二九 20%的人 会坚持 80%的人 爱放弃\n","date":"2010-05-24","description":"","lastmod":"2010-05-24T01:24:22Z","slug":"1008","tags":[],"title":"【转】28理论","url":"https://blog.zengrong.net/post/1008/"},{"categories":["use"],"content":"系列全部文章：抛弃Windows，用Ubuntu办公\n顺便说一句，我在windows下早就不用winRAR了，一直用的是7-zip的官方版，前几个月发现有个国产的HaoZip比较好用，就把7-zip也卸载了。\nWinRAR曾经给我所在的单位发过律师函，当时我给单位的建议是，全部换成7-zip，然后不理律师函。结果这事不了了之，当时有没有人响应这个号召我不清楚，反正我是强行把我们部门所有电脑上的WinRAR都卸载了，现在随便转一圈，电脑上又是RAR当道了……\n唉……普及免费软件何其难啊 :cry:\n为什么要把解压缩放在前面呢？\n因为解压缩碰到的问题远比压缩要多。不同压缩文件的支持问题、zip文件的乱码问题，都需要一个个去解决。而压缩就简单了，直接用右键菜单解决。（我懒，没办法）\n:em55:\n1.rar和7z格式压缩文件的支持 安装unrar和p7zip包，然后就可以用归档管理器打开了。也可以使用右键的“解压到此处”功能来解压。\nsudo apt-get install unrar p7zip 2.使用命令行解压 以前在windows下面看到.tar.gz的文件总是一头雾水，因为解压一次之后，还要再解压一次tar包。来到linux下才知道，原来tar只是个包，bz2或者gz才是起到压缩功能的。看tar的说明看得一头雾水，多试验几遍，把经常使用的几个参数弄明白了，就不再研究tar了。\n我一般这么用3个参数进行tar解压，第1个参数x就是解压文件;第2个参数指定压缩类型，如果是gz压缩的，就用z，如果是bzip2压缩的，就用j（其他解压类型可以man tar，至少我目前只碰到过这两种）;第3个参数f指定文件名。\n这样，如果要解压一个gz压缩包，就可以这样：\ntar xzf file.tar.gz 如果是bzip2压缩包，就是这样：\ntar xjf file.tar.gz 我的经验就这么多了，想知道更详细的东东，可以看这篇文章：http://docs.google.com/View?id=ajf3xsjz9vhb_3009r95sd8z\n3.解压zip文件出现乱码 由于zip文件不支持utf8（或许是这样吧），在windows下面压缩的zip文件中如果有中文文件名，在linux下面解压就是乱码。我最开始google到的消息，是可以使用unzip命令的-O选项指定CP936编码解决，好像是这样：\nunzip -O CP936 file.zip 但是我试验发现这个方法不行，才知道新的unzip命令取消了-O选项的支持。\n后来找到了解决方法，但是有点麻烦，但我觉得这就是目前能找到的最好的办法了。\n首先安装convmv，这个命令用来解决文件名乱码的问题：\nsudo apt-get install convmv 在安装了7z支持的前提下，运行下面的代码（范例文件夹中，有一个flash.zip压缩文件，还有几个中文文件名的文件）：\nLANG=C 7z x flash.zip 反馈如下：\n7-Zip 9.04 beta Copyright (c) 1999-2009 Igor Pavlov 2009-05-30 p7zip Version 9.04 (locale=C,Utf16=off,HugeFiles=on,2 CPUs) Processing archive: flash.zip Extracting ������ľ.fla Extracting ������ľ.swf Everything is Ok Files: 2 Size: 3004879 Compressed: 822477 然后使用convmv修改文件名，convmv的使用非常简单，只需要用f参数指定原编码，用t参数指定要转换到的编码，后面加上通配符*就行了（因为文件名是乱码，我们无法输入文件名，只能用通配符代替）。对于原编码，我们可以用gbk或者gb2312,而转换到的编码，则应该用utf8.\nconvmv -f gbk -t utf8 * 从直接结果可以看到，对于文件夹下面原来的中文命名的文件，convmv是直接跳过的。而如果我们不加上notest参数，它也不会实际进行转换，而只显示一个转换后的结果。\nYour Perl version has fleas #37757 #49830 Starting a dry run without changes... mv \u0026quot;./������ľ.fla\u0026quot; \u0026quot;./保护树木.fla\u0026quot; mv \u0026quot;./������ľ.swf\u0026quot; \u0026quot;./保护树木.swf\u0026quot; Skipping, already UTF-8: ./关于幼教网修改方案.doc Skipping, already UTF-8: ./现场竞赛名单.xls No changes to your files done. Use --notest to finally rename the files. 接下来就可以加上notest参数进行实际的转换了：\nconvmv -f gbk -t utf8 --notest * 当然，如果你还是怕convmv会弄错原有的文件名，可以把flash.zip中的文件解压到一个新的文件夹中再进行转换。\n","date":"2010-05-12","description":"","lastmod":"2010-05-12T05:45:17Z","slug":"use-ubuntu-to-replace-windows-zip","tags":["linux","ubuntu","fromto","byewindows"],"title":"抛弃Windows，用Ubuntu办公-4.解压缩与压缩","url":"https://blog.zengrong.net/post/use-ubuntu-to-replace-windows-zip/"},{"categories":["use"],"content":"今天发现libfetion for linux不能用了，出现的问题为输入用户名和密码登录后，停滞几秒自动退出。google了一阵发现应该是中国移动更改了飞信协议所致。\nopenfetion已经针对该问题进行了修改，但必须安装最新的1.4版才可以。1.4版没有deb包，可以参照作者主页上提供的方法进行安装。\n","date":"2010-05-12","description":"","lastmod":"2010-05-12T03:02:33Z","slug":"1006","tags":["freeware","linux","opensource","software"],"title":"libfetion(linux fetion)不能用了，改为使用openfetion","url":"https://blog.zengrong.net/post/1006/"},{"categories":["use"],"content":"系列全部文章：抛弃Windows，用Ubuntu办公\nUbuntu安装成功后，我的第一步就是安装输入法。毕竟没有输入法就不能打汉字，碰到问题都没得搜索。 Ubuntu 10.04自带的ibus我觉得太慢，因此还是安装fcitx比较好。fcitx的速度的确是快，但在配置的便捷上来讲，和搜狗还是有一点点距离的，如果能在自定义短语功能上再加强点就好了。\n安装的过程就不说了，网上有很多的教程可以搜到，我这里讲讲我配置的经验。\nfcitx在源里是有的，可以直接使用下面的代码安装：\n1sudo apt-get install fcitx 但是，源里面的版本比较老（例如10.04里面是3.6.2,而最新版本为3.6.3），如果需要最新版本，在googlecode（http://code.google.com/p/fcitx/）上也有deb包下载，不喜欢GB编码的童鞋还可以下载utf8版本。\n那么，在源里面安装和使用下载的deb包安装的版本有什么不同呢？主要是源里面安装的版本会在“~”下面创建“.fictx”目录，将配置文件和用户词库都保存在其中，而deb安装的则不会创建这个目录。因此，如果你安装的是deb包，又按照网上的许多教程所说的去寻找.fictx目录下面的config文件进行编辑的话，就要失望了 :em31:\n下面说说我摸索的一些经验教训：\n1.编辑config，如果使用的deb安装包，需要编辑/usr/share/fcitx/data/confg，如果是源自带的fcitx，则编辑.fcitx/config；\n2.如果安装的不是UTF8版本的fcitx，则使用Ubuntu自带的gedit编辑器配置文件的时候可能会显示乱码。解决乱码的方法可放狗搜索。我则是安装了一款支持自动识别编码的编辑器leafpad解决。这款编辑器轻便小巧，启动快速，我已经用它作为我的默认文本编辑器了;\n3.关于显示字体为方框的问题，配置文件中的字体设置就可以了，如下：\n[程序] 显示字体(中)=Microsoft YaHei 显示字体(英)=Courier New 4.对于一个只会拼音的人来说，多余的输入法都不需要载入。将“其他输入法”留空，并取消区位和码表输入法。同时，我把输入法名称改成了自己喜欢的样子。\n另外，我本来习惯使用微软拼音的双拼方案，但是由于微软双拼将分号键定义为韵母ing，而fcitx需要分号键来作为快速输入功能的开启键，这之间有冲突。因此就退而使用自然码的双拼方案了。（这个问题还是纠结了很长时间才解决的，开始我一直认为是fcitx的帮助文档写错了，要不然为什么我的设置完全正确，却无法使用快速输入呢 :em14: ）\n双拼方案的名称，可以打开/usr/share/fcitx/data/sp.dat查看。\n[输入法] 使用拼音=1 拼音名称=拼音 使用双拼=1 双拼名称=双拼 默认双拼方案=自然码 使用区位=0 区位名称=区位 使用码表=0 提示词库中的词组=1 其他输入法= 5.热键部分的设置，我习惯使用逗号和句号翻页，使用CTRL切换中英文，使用左右SHIFT选择第二三候选字。那么进行如下设置即可（只有修改的部分）：\n需要注意的是，虽然配置文件的注释中说\n除了“中英文快速切换键”外，其它的热键均可设置为两个，中间用空格分隔\n但是，对于“第二三候选词选择键”，必须设置为SHIFT，如果设置为 L_SHIFT R_SHIFT ，是无效的。\n[热键] 中英文快速切换键=L_CTRL 上一页=, 下一页=. 第二三候选词选择键=SHIFT 6.通过编辑QuickPhrase.mb，可以得到类似于搜狗中自定义短语的效果。不过这里的自定义短语必须在使用分号开启快速输入后才会显示。不知道如何能象搜狗一样直接显示呢？\n在定义快速输入的时候，还要注意的一点是，数字不能是自定义短语的最后一个字符。当时我有个地址短语最后是使用邮编结尾的，就导致fcitx出错，无论如何也无法启动了，我换了十几个版本才发现，原来问题出在这里。\n7.标点符号定义改punc.mb，成组的符号定义改pySym.mb，要备份输入过的用户词库可以备份.fcitx下面的“pyindex.dat”和“pyusrphrase.mb”（五笔用户可能名称会不同）。\n8.修改了配置和添加了快速输入短语后，在fcitx输入框显示的前提下，按Ctrl+5就可以重新载入设置了。当然，也可以更加暴力一点，使用下面的方法：\nsudo killall fcitx 然后再执行：\nfcitx ","date":"2010-05-10","description":"","lastmod":"2010-05-10T06:08:13Z","slug":"use-ubuntu-to-replace-windows-ime","tags":["ime","linux","ubuntu","fromto","byewindows"],"title":"抛弃Windows，用Ubuntu办公-3.输入法","url":"https://blog.zengrong.net/post/use-ubuntu-to-replace-windows-ime/"},{"categories":["news"],"content":"转自：关注开源软件发展 http://blog.sina.com.cn/s/blog_4c50644a0100hxvt.html\n纵览,国内比较大的软件公司(以下统一简称\u0026quot;国软\u0026quot;),清一色都是做政府项目的(他们能做大的原因我就不用说了吧),真正能做大的国软又有几家呢?更何况开源企业，这是为什么呢?\n今天风吹就给大家简单分析下:\n1.\u0026quot;作坊\u0026quot;式管理\n\u0026quot;作坊\u0026quot;往往是效率最高的, 国软几乎都是从作坊走过来的,但把作坊式的管理模式套用到一个不断壮大的公司中显然是不行的.组织架构到达一定程度后就必然要进行分工的细化,依靠作坊式的\u0026quot;暴力开发\u0026quot;是行不通的.\n2.\u0026quot;法制社会\u0026quot;\n上班必须打卡,迟到要扣钱,还一次比一次多,加班没有加班费,反正算下来就是,只有扣钱的项目,没有加钱的项目.比起外企,人家上班不打卡,迟到不扣钱,加班有加班费,这样宽松点的环境不好吗?\n3. 自身自灭\n国软一般没有师徒制,有的话也只是形式上的,公司基本没人管你,你也不用去管别人,新进的员工,不管会不会,先丢个东西给你做,自己研究,不懂的google去.这也是为什么国软喜欢招有经验的人,因为没经验的人熬不住,跑了几个以后,国软就不招了.\n销售-开发-维护脱节 这点是非常严重的,会直接导致项目流产的.国软的典型的做法是,销售为了业绩,在没有调研的情况下就签了合同 (这里主要是指项目型的,产品型的一般可以控制),而且合同的范围也非常模糊,可大可小,接下来就是调研人员上场,调研后发现,10w块钱的合同,调研出来了100w的需求,接下来就是和客户扯皮,最后直接导致项目流产,甚至打上官司.项目或产品上线后,维护人员对系统不了解(一方面是没有文档,另外一方面维护人员一般没有参与到开发中),接下来往往就会发生两种情况:\na.维护人员在不了解系统的情况下擅自修改,结果导致系统越改问题 越多.\nb.维护人员一不做二不休,所有客户反馈的问题全部打回给开发人员,于是开发人员就生不如死,在做别的项目的同时还要维护以前的项目,结果就是导致几个项目都失败.\n5.缺乏规划\n今天要用这个,明天想用那个(笔者就经历了公司在半年的时间内对框架进行了两次大的变动,导致开发人员都必须重新学习框架)产品也接,项目也接,大的也接,小的也接.今天领导说往左走,明天说往右走,也不能怪领导,他也没经验,我们就是他的DEMO.公司没有一个明确的目标,要做成什么样,只是一味的提出做大做强,但是没有规划出如何做大做强.(和我的标题一样哦)\n6.三无-无需求,无设计,无测试.\na.没有文档是国软的通病,曾几何时,产品经理丢过来的那一句话:\u0026quot; 喂,**,给我做一个**模块来\u0026quot;,然后开发人员就开始埋头苦写了.\nb.当然如果你天资聪慧,可以轻易理解出产品经理的意思,那有没有设计都无所谓了,但是,当有一天别人要维护你的程序的时候问题就出现了,没有文档,代码又那么天马行空,怎么维护?改了这个地方,又影响了那个地方...\nc.其实程序员都懂得测试的意义,可以工时安排的那么紧,哪来的时间测试?测试又没有算工时.所以几乎所有的程序员的做法就是,直接丢给用户测试\n这时候有人肯定要问:那项目经理呢?他不是可以测试吗?请记住这是国软,刚才写代码的那个人就是项目经理,还是售前,还是设计人员,还是维护,还是...归结还是成本问题,在外资软件公司中,做文档的工时是比做开发的工时更多的,国软为了节省成本,这块当然要 CUT掉了.不必去追去文档有多么详细多么美观,需要做的就是找到一个平衡点,一份适合自己的文档.\n7.员工都是\u0026quot;十项全能\u0026quot;\n在国软里面的员工各个都是十项全能(笔者就是一个鲜明的例子,从系统调研分析设计,到进度管理,开发,测试,验收,实施,维护,甚至拉给客户拉网线都需要我去.)直接导致的结果就是这些员工每过多久就直接出来自己开公司了...嘿嘿又一家作坊诞生了...这样做对员工个人其实是有好处的,但是对于企业本上来说是没有好处的,并不是说员工成立了作坊,成为了你的竞争对手,而是让员工各个都是十项全能的结果就是\na.员工都是\u0026quot;十项全不 能\u0026quot;.\nb.员工一旦离职,他手头的项目必定流产.\nc.对公司的发展是不利的(细化分工).\n8.莫不关心\n老板并不知道员工在做什么,员工也不知道老板在做什么.上级很少去关心下级的工作,更别说去关心下级的生活,一个东西丢给你,一个月后交差,中间不管你任何事情,交不了差就唯你是问.下级也不知道能为上级分担什么,只有等着上级分配任务.甚至还有些老板都不不知道员工的名字,在这样的国软的,每个人都是孤立的,又怎么能做大做强呢?\n9.企业文化\n所谓十年树木,百年树人.国软的企业文化表面功夫算是做的很好的了,什么\u0026quot;为客户创造价值\u0026quot;,\u0026quot;做最好的行业解决方案\u0026quot;,\u0026quot;软件公司的最大资源就是人才\u0026quot;等等,要多华丽有多华丽,重复体现了\u0026quot;口号文化\u0026quot;.真正做到企业文化又有多少呢?有多少仅仅是为了做给客户看的呢?\n10.盲目跟风\n很多国软看到人家外企软件公司最近在搞什么推进活动,就跟风,效仿外企做,可是无法领悟精髓,纯粹只是在模仿.(外企集体笑:\u0026quot;一直被模仿,从未被超越\u0026quot;)做完了也不知道这么做的意义,劳民伤财.\n11.缺乏\u0026quot;执行力\u0026quot;\n国软的通病,就是\u0026quot;执行力\u0026quot;,国软的学习劲头很足,今天提出要完善测试标准,明天提出要每周写工作报告,可是又有哪些东西能真正的去执行呢?\n导致这个问题的主要原因有两个:\na.提出来的东西到底有没必要做,还是只是应付领导走个过场.\nb.谁来跟踪这些东西?员工写了工作报告,领导没有去查看,去反馈,员 工觉得写的也没意义,自然不会继续执行下去.\n12.管理混乱\n没有划分清楚员工的归属组织,员工并不明确他的上级领导是谁,导致有的员工处于游离状态,有的是员工又是多个领导,不懂要听谁的,有些人忙的要死,有些人又闲的要命,最后搞的最痛苦的就是员工,导致员工离职.\n13.缺乏团队精神\n为什么会缺乏团队精神呢?并不是国软没有这方面的概念,国软也很希望培养员工的团队观念 和精神,\n无奈因为国软,一般都是一个人负责一个或者多个项目,连团队都没有,何来的团队精神?\n14.无法做到补 足\n一个项目一旦中途有人辞职,这个项目就会流产.一个员工一旦辞职,会有N个项目没人维护.A组的员工无法胜任B组的工作,归根结底就是组织上根本没有考虑过组织变动对项目的影响,没有提前培养人员.\n15.一成不变和随心所欲\n有两类人一种是把前辈的东西COPY过来,不作任何修改,因为他深信,前辈的一定是对的,还有一类是不管前人怎么做的,一律不要,全凭自己的\u0026quot;经验\u0026quot;,随心所欲,天马行空的进行自主研发,造成的结果就是错的还是错的,乱的更乱了.\n16.人才育成\n成本,还是成本,培养一个人要多少成 本?这就是国软做不大的原因,永远只能停留在\u0026quot;作坊\u0026quot;的原因.\n17.向心力.\n老板做的是事业,员工做的是事情, 这是国软员工的一致观点.\n18.恶性循环\na.人员力量不足 -\u0026gt; 接不了项目 -\u0026gt; 收入少 -\u0026gt; 人员流失.\nb.人员力量不足 -\u0026gt; 强行接项目 -\u0026gt; 亏本 -\u0026gt; 破产.\n最后 我想说一句的是:成也国软,败也国软.\n说的不对的地方请大家指出,或者补充下没说到的地方.\n有几个评论也是很中肯的\n新浪网友2010-05-04 11:59:26\n说的有道理。程序员的作风是行业的主要问题。中国程序员类似“艺术家”，各自为战，标新立异，无组织无纪律。他们只是“工人”，管理工人需要“制度”和“流程”。中国缺少“软件项目管理经理人”。我国的软件产品大多豆腐渣工程或命悬一线的危险品。\n北京女人2010-05-04 15:00:06 [举报]\nha,我圈外人，最近刚介入这领域，你说的问题看到很多，很不解，原来如此！都这么无序啊----\n我传统媒体电视制作人，看软件行业根本没啥“生产线”，基本没有流程和规范，也算不上“工人”，没啥工序工艺要求标准成本控制---，比电视台电视节目制作流程标准管理----差远了。\n每天都不知道进行到哪个流程了----完全是农民式的！还不如民工，民工起码是在建筑工程的成熟标准管理下操作干活儿----\n补充几个在初次和软件开发合作时看到的情况：\n1 没有以市场为主导的商业理念，貌似技术达人业余爱好在玩儿，随心所欲。比如：已经在iPhone做了一休闲小游戏，下载量已经近百万了，可不深入分析总结继续挖掘优化升级，而转头又试验别的了。\n2 没有清晰的定位，自己团队的优势到底是什么？市场是什么？没有通过做外包，提升自己的专注领域水平和建立品牌，做得再多，也就是个代工厂。什么活儿都接，活儿的类型五花八门，比如：翻译，教育，游戏，赛车------全都不搭界。\n3 做外包做多了，注意力分散，把团队的已有的优势淡化了。比如，一个翻译公司委托做为 iphone的翻译软件，测试完了那公司满意了，可对手机用户根本不好用，程序员本来是有水平能发现问题的，但能交差就懒得管了，将来可能就会失去自己对产品的从用户出发的敏锐度和动脑习惯。\n我方第一次接触这，写了详尽的类似电视节目脚本的创意，被表扬说写的很专业，哈----，一看你的文章才知道为什么受到表扬。现在我们等，想要就是你说的文档的东西，看样子挺不容易----，但搞不好一定会造成你说的以后维护---的困难，郁闷。\n我想，国软技术人习惯以后谁都离不开他，在地铁上都被人求问----，可这多么像农民自组的小装修队风格啊----\n看来，要赶快找外企软件公司参观一下了----要不以后怎么做大做强啊---哈\n","date":"2010-05-05","description":"","lastmod":"2010-05-05T23:44:12Z","slug":"1001","tags":[],"title":"【转】 浅谈:国内软件公司为何无法做大做强?","url":"https://blog.zengrong.net/post/1001/"},{"categories":["use"],"content":"系列全部文章：抛弃Windows，用Ubuntu办公\nUbuntu的安装非常简单，只需要下载ISO文件然后刻录，按步骤安装即可。我这里只提一些要点：\n一、关于刻录光盘 Ubuntu半年发布一次，如果每次都刻录一张光盘，会累积许多无用的光盘（虽然我就是这么做的）。所以有很多不使用光盘安装的办法。主要的两种是：\n使用UNetbootin，可以将ISO制作成一个USB引导盘。除了Ubuntu，还支持其他许多Linux发行版。 利用UltraISO的写入硬盘镜像功能将ISO写入到U盘或者移动硬盘中。当然，这两种方法我都试过，但总是因为RP问题未能试验成功。 :em67: 刻录光盘的时候要注意的是，不要使用最高倍速刻录。建议CD使用24X或者16X刻录，DVD使用8X刻录。\n二、关于不同ISO的选择与下载 要下载Ubuntu，自然是去官方网站。不过还有两个直接的地址可以选择：\n网易：http://mirrors.163.com Ubuntu官方：DVD版 CD版 Ubuntu有DVD版本、live CD（图形安装界面）版和alternate CD（字符安装界面）版，它们之间有什么区别呢？\nDVD版是图形安装界面的，它与live CD的区别就是将多国语言支持包和内置了，同时也多了一些软件。在安装过程中或者安装完毕后，就不用去联网下载简体中文支持了。所以如果条件允许的话，使用DVD版安装是最好的。\n我通常是使用alternate CD版进行安装，因为字符界面显示比较快，而图形界面载入都要好长时间，我懒得等。\n:em56:\n三、安装过程的注意事项 安装过程中一定要把网线拔掉。因为在安装程序会寻找DHCP服务器来自动分配IP地址，也会自动获取APT源，并从APT源获取简体中文语言包（这个步骤可选），这些操作一般是使用默认源来完成，但是默认源的速度很慢。这样一来，安装就会持续很长时间，甚至是几天都有可能。所以要把网线拔掉，一切的设置等安装完毕再进行。 Ubuntu安装的过程中最好是等在电脑跟前，因为不时有些信息需要你来填写的。 ","date":"2010-05-04","description":"","lastmod":"2010-05-04T05:11:11Z","slug":"use-ubuntu-to-replace-windows-install","tags":["linux","ubuntu","fromto","byewindows"],"title":"抛弃Windows，用Ubuntu办公-2.安装","url":"https://blog.zengrong.net/post/use-ubuntu-to-replace-windows-install/"},{"categories":["use"],"content":"掐指算来，在办公电脑上连续使用ubuntu也有一个多月了。也终于摆脱了开始的不适应，摆脱了对Windows的依赖。\n其实，在办公电脑上使用Ubunut是完全够用的，甚至比Windows少了许多麻烦。但是，Linux毕竟是Linux，许多地方，不动一番脑筋，查一堆资料，也是没法解决的。\n所以，我准备把这些经验写下来。\n先说说我在Windows下面的办公应用环境：\n频繁使用Word、Excel、Powerpoint制作各种文档，简报和表格； 频繁使用电子邮箱，但我从来不用客户端软件； 经常使用google docs共享各种文档； 经常使用QQ进行工作上的联系； 经常需要播放swf格式动画； 偶尔使用photoshop进行图像处理； 偶尔使用飞信发免费短信； 偶尔需要播放教学视频； 偶尔需要制作屏幕录制视频； 偶尔需要制作网站原型设计； 偶尔需要调试HTML源码与Javascript源码； 偶尔需要查看Flash源文件； 需要访问网络上的其他Windows系统中的共享文件； 需要访问网络上的其他Windows系统中共享的打印机； 从不偷菜； 从不玩单机游戏。 如果你的办公环境与我类似的话，建议你和我一样投入Linux的怀抱。我是个Linux菜鸟，所以，我的学习经验或许对菜鸟更有帮助把！\n系列全部内容：\n开篇 安装 输入法 解压缩与压缩 使用习惯——快捷键设定和快速启动 字体问题 或者可以查看这个 TAG： 抛弃Windows，用Ubuntu办公\n","date":"2010-04-28","description":"","lastmod":"2010-04-28T08:23:20Z","slug":"use-ubuntu-to-replace-windows","tags":["linux","ubuntu","byewindows","fromto"],"title":"抛弃Windows，用Ubuntu办公-1.开篇","url":"https://blog.zengrong.net/post/use-ubuntu-to-replace-windows/"},{"categories":["web"],"content":"终于还是投奔了米国主机。测试的这几天看来，速度还不错。除了刚刚买的当前有问题外，一直都保持在200MS延迟。ftp上传速度能到80KB/s，一般为50-60KB/s。平时用SSH管理主机还真是方便，直接用wget把备份在老主机上的100多MB的压缩包拖下来解压，速度那个快啊……\n建立的MySQL数据库可以允许远程访问，可以用Navicat管理自己的数据库了。\n10G空间，每月300G流量，可开25个子网站，可惜的是不能把子目录绑定到主域名上。\n","date":"2010-04-27","description":"","lastmod":"2010-04-27T13:47:56Z","slug":"943","tags":["godaddy","host","master"],"title":"投奔GODADDY主机","url":"https://blog.zengrong.net/post/943/"},{"categories":["news"],"content":"转自：http://flash.9ria.com/viewthread.php?tid=48055\n下面介绍个使用Flash Builder 4构建纯ActionScript书写AIR项目的窍门。\n文件 \u0026gt; 新建 \u0026gt; \u0026quot;Flex Project\u0026quot; 填项目名: \u0026quot;Foo\u0026quot; 选择 \u0026quot;Desktop (runs in Adobe AIR)\u0026quot; 按 \u0026quot;Finish\u0026quot; 删除 \u0026quot;Foo.mxml\u0026quot; 文件 \u0026gt; 新建 \u0026gt; ActionScript类命名为\u0026quot;Foo\u0026quot;，并使用\u0026quot;flash.display.Sprite\u0026quot;作为超类 按Finish. 右击\u0026quot;Foo.as\u0026quot;并选择\u0026quot;Set as Default Application\u0026quot; 在Foo.as的构造函数中,写下\u0026quot;this.stage.nativeWindow.visible = true;\u0026quot; 完成! ","date":"2010-03-27","description":"","lastmod":"2010-03-27T12:40:23Z","slug":"935","tags":["air","flashbuilder"],"title":"【转】Flash Builder 4中构建纯ActionScript书写的AIR项目","url":"https://blog.zengrong.net/post/935/"},{"categories":["news"],"content":"代朋友发个招聘信息，喜欢Flash开发的朋友可以看看。\nFlash开发工程师(程序员)\n* 性别、年龄、学历不限；\n* 工作经验2年及以上者；\n* 精通Action Script3语言，熟悉Socket通讯编程，熟悉OOP设计方式；\n* 熟练使用Flex3\\Flash CS3等编程工具，掌握SVN等团队协作工作软件；\n* 具备良好的协调沟通能力和团队协作能力；\n* 有网络游戏、商务应用、娱乐多媒体系统开发经验者优先；\nFlash开发工程师助理(程序员助理)\n\\* 性别男，年龄26周岁及以下，大学及以上学历； \\* 工作经验1年及以上者； \\* 熟悉Action Script2.0或者3.0语言，接触或曾经参与过Socket通讯编程; \\* 能使用Flex3\\\\Flash CS3等编程工具； \\* 具备良好的协调沟通能力和团队协作能力； \\* 有网站、多媒体、游戏开发经验者优先； 这个公司是湖北一家比较出名的游戏开发、运营企业，在全国也小有名气。有7年多的互联网业务经验和70多人的团队。企业业务管理与行政管理比较完整规范，薪资待遇和发展机会还是不错的。有兴趣的朋友可以联系我朋友的信箱：dick.yeah[at]gmail.com\n","date":"2010-02-24","description":"","lastmod":"2010-02-24T13:52:28Z","slug":"932","tags":[],"title":"代发招聘信息，Flash开发工程师、ActionScript3程序员","url":"https://blog.zengrong.net/post/932/"},{"categories":["others"],"content":"07年买的磐正AT690G主板，一直用DVI接口接BenQ19寸液晶。后来BenQ烧掉了（再次提醒大家不要买BenQ的显示器），买了个24寸液晶。接上DVI正常，但是玩泡泡龙、爆笑打野鸡等小游戏不能全屏，一全屏就黑屏。这个问题原来在BenQ上也出现过，只是我没有把他当回事儿。\n后来装Ubuntu居然也黑屏！能进入字符界面，但是不能进入图形界面。进入就黑屏。google找到真正原因：\n我在网上找到了一些资料，690G使用DVI接口间歇黑屏，和显卡DVI信号输出质量和显卡信号输出带宽有关。DVI对高分辨率的支持性不如D-SUB，使用DVI接口对显卡数字信号的输出质量要求非常之高的，从显卡芯片的采用到PCB电路的设计都会影响到DVI输出信号的质量，甚至显示器面板的性能的高低对信号质量的要求也不一样，所以仅仅拥有DVI接口不代表就可以享受真正的数字显示。而现实情况是相当一部分显卡的 DVI数字信号输出质量都不过关，最重要的原因在于显卡集成的TMDS数字信号传送器不过关，ATI显卡似乎更严重些，我在网上搜了一下结果显示接DVI 黑屏的现象不是690G的专利，极少数NV的显卡也存在此问题，ATI的相对多些。关于DVI数字接口技术标准具体可以参考以下文章。\n原文地址：http://www.soyo.com.cn/bbs/redirect.php?tid=18989\u0026amp;goto=lastpost\n据说用VGA不会黑屏，搞了根VGA线，确实是不黑屏了，但是分辨率只能上1680*1050，据说要换更好的VGA线才行，我……\n看来不换显卡是不行了。于是上淘宝买了块微星N210-MD512TH，加邮费228元。插上就搞定，不仅DVI不黑屏，原来的VGA线也能上1920*1080了，甚至还可以上1920*1200。\n顺便说一句，GT210显卡性能确实蛮差的，但是对于一个从不玩大型3D游戏，电脑只是用来上网和写代码的人来说，也足够用了（690我不是都用的不亦乐乎么），况且还支持CUDA和Flash Player硬件加速，200元值了。\n所以，690G黑屏的问题，我认为与其想尽办法去解决，倒不如直接买块显卡插上了事。\n","date":"2010-01-27","description":"","lastmod":"2010-01-27T15:10:06Z","slug":"929","tags":["amd","hardware"],"title":"AMD 690G主板使用DVI接口黑屏问题的解决","url":"https://blog.zengrong.net/post/929/"},{"categories":["others"],"content":"查了一下我的淘宝记录，这个路由器是08年1月20日被我用249大洋拿下的。自从拿下它之后，就问题不断。\n先是ADSL经常掉线，大概1小时掉一次。重启后恢复正常。\n然后是用了半小时后，上网速度会变慢，ping延迟超过1秒，而不适用路由器直接连接则正常。\n因为当时没在意，手上有其他路由器用，就把这个路由器放着了。\n今天在外出差，又把路由器拿出来用，还是这问题。google了一下，发现和我问题相同的不少。而且好像没有解决办法。有个台湾网友说是用1.8的某个版本固件会好些，但是我的路由器拿到手上就是1.9了，而且华硕网站也没有旧版固件下载。\n稀烂的华硕啊 决定永远不买华硕的东西\n","date":"2010-01-27","description":"","lastmod":"2010-01-27T13:44:18Z","slug":"927","tags":["hardware"],"title":"ASUS 华硕 WL530g v2是绝对不能买的！","url":"https://blog.zengrong.net/post/927/"},{"categories":["news"],"content":"JW Player是个不错的基于Flash Player的播放器，我一直用它，也帮它写过插件。它的代码并非ActionScript3，估计也是作者为了兼容老版本的Flash Player。下面两篇文章详细介绍了该播放器的使用方法。\nJW Player使用简介JW Player之XML播放清单简介\n","date":"2010-01-21","description":"","lastmod":"2010-01-21T06:36:52Z","slug":"923","tags":["player","video"],"title":"JW Player使用简介","url":"https://blog.zengrong.net/post/923/"},{"categories":["news"],"content":"转自：http://h30458.www3.hp.com/cn/zh/smb/898363.html?JUMPID=EM_TAW_CN_JAN10_ACROSS-BG_688288_HPGL_ZHS_898363_1\u0026amp;DIMID=1083254088\u0026amp;DICID=null\u0026amp;MRM=1-4BVUP\n“今日事今日毕。” 这句名言是由美国政治家 Thomas Jefferson 所提出，也是他一直遵循的工作准则（在他成功撰写国家《独立宣言》和成为州长、副总统以及最后担任第三任美国总统时）。zrong：不同意，这句话明明是中国谚语\n虽然 Jefferson 的建议可能是合理的，但是在如今多任务、快节奏的世界却很难被遵从。我们中的许多人往往需要尽力应付众多的优先事件（这很容易造成把事情拖到最后一分钟）以跟上工作的步伐而不失去我们的思想。然而，如果拖延发生的次数过于频繁，它可能导致更多不必要的压力，此处提供一些去掉这一坏习惯的方法。\n只要有可能，不要同时进行多项任务 当您一次执行多项任务时，往往很容易对您所做的事失去掌控，从而最终导致仅完成部分任务。 尝试一次专注于一项任务直到完成，然后再转移到下一项任务，这样的话，您就没有借口留下一些未完成、必须迟些时候再回来完成的工作了。\n2. 改变您的环境以排除干扰\n电子邮件、电话铃声以及聊天的同事，所有这些都很容易成为没有完成工作的借口。您很容易说，“好的，发送完电子邮件后，我会马上处理的”，但之后就开始推迟需要完成的任务了。 因此，使干扰保持在最低限度：仅仅运行您必须使用的应用程序以完成给定的任务、使自己与外部交谈隔离并且关闭您的手机。 甚至可以关闭您的电子邮件客户端，如果这有助于您保持注意力！\n3. 设定计时器\n我们中的许多人往往会推迟不愉快的任务，这只不过是人的本性。 为了帮助使无聊的琐事更易于处理，告诉自己您仅仅在设定的一段时间（可以是 10 或 15 分钟）内处理这件事情。十分钟后，如果您想停止工作，那就停下来。有趣的是，如果您允许自己在十分钟后停止工作，您会发现工作的势头与您一起高涨而且无论如何您会继续工作。\n4. 犒劳自己\n给自己一个积极的理由以完成工作。 告诉自己一旦完成任务，您就可以与同事一起出去吃午饭了。或者设定一小时的时间段，用于全神贯注于工作上，随后有十五分钟的休息时间：喝一杯您喜欢的茶。不需要特别的隆重奖励，只需能给您带来推动力的简单事物。 一旦完成任务，您甚至可以感觉到无比欣慰，通常这本身也是一种奖励！\n5. 尽可能的简单\n当开始一项您已经推迟的任务时，最重要的一点就是开始该任务。 不要马上关注于细节问题，而只需要专注于开端。例如，如果您需要准备一份报告，那么先开始写下最重要的事情。 不要担心报告的格式正确或寻找合适的言辞，只需要写下来。以减少压力和恐惧的方式开始，特别是对于较大且复杂的项目。\n虽然拖延时间可能难以克服，但上述策略可以帮助您保持自己处于正确的轨道。 同时，也不要忘记善待自己，辛苦的工作需要“休息时间”，因此，请确保不要太辛劳以至于使自己累倒。 充分休息后精力充沛地回来工作，是应对拖延时间的最佳预防措施。\n","date":"2010-01-21","description":"","lastmod":"2010-01-21T04:18:10Z","slug":"917","tags":[],"title":"【转】战胜拖延时间的技巧 - 立即行动吧！","url":"https://blog.zengrong.net/post/917/"},{"categories":["news"],"content":"List of 34 More ActionScript 3.0 APIs，还有中文版\n36 New, Cool Flex and AS3 Tools, Libraries and Components\n","date":"2009-12-24","description":"","lastmod":"2009-12-24T14:18:44Z","slug":"904","tags":["api","as3","flash","library"],"title":"两篇介绍ActionScript类库和工具的文章","url":"https://blog.zengrong.net/post/904/"},{"categories":["tutorial"],"content":"使用TMPGEnc，在上报作品前压缩录像课，可以有效减小视频文件的大小，一张DVD光盘装十几节课没有问题。而且压缩后的文件可以直接在网上使用Flash播放器播放。\n下面是流程：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n全屏播放，右键下载\n","date":"2009-12-21","description":"","lastmod":"2009-12-21T08:01:09Z","slug":"tmpgenc-4-xpress-video-compress","tags":["h264","video","training"],"title":"使用TMPGEnc 4.0 XPress(T4XP)压缩录像课","url":"https://blog.zengrong.net/post/tmpgenc-4-xpress-video-compress/"},{"categories":["news"],"content":"本文译自20 Flash Resources and Tools You’ll Want\n原文比较老了，所以里面很多东西我改了。\nTweenLite 最快的补间动画引擎。看这个测试。仅3K大小，可用于AS3和 AS2。 滤镜看这里：TweenFilterLite引擎。 Papervision3D 最流行的3D引擎。 Away3D另一个3D引擎。查看范例 WOW Engine AS3 3D 物理引擎。与 Papervision3D 或者 Away3D配合使用不错。 FIVe3D还是3D动画引擎。 Ribbit – An amazing technology that lets Flash call a phone and use the microphone on your computer. Enables Flash pieces to receive phone calls. Visual voice mail, with speech to text capabilities. Very easy component-based implementation. WiiFlash让Wii控制器用于Flash。使用WiiFlash，你可以创建更有趣的体验。 APE (Actionscript Physics Engine) 最大的物理引擎。 AMFPHP在Flash中通过PHP连接数据库。（不过zrong更推荐weborb for PHP） Flickr Libraryzrong：修改了原文提供的地址。 RSS Library zrong：修改了原文提供的地址。 YouTube zrong:修改了原文提供的地址。还可以用这个google官方提供的YouTube API。当然，国内基本用不上。 FZip在客户端压缩zip文件。以前必须使用服务器端代码才能做到这些。 AS3SoundEditorLib 使用这个库，可以以频谱的形式可视化的显示mp3文件，并支持提示点，可以在提示点之间播放mp3。 Facebook-AS3zrong:不说了，国内基本用不上。 AS3Crypto用ActionScript3加密。 AlivePDF AlivePDF是一个开源的ActionScript 3 (Flash, Flex, AIR) PDF 生成库。使用它可以在客户端生成PDF文件。zrong:这里还有一个aPDF ASSQL让ActionScript与MySQL直接沟通（不通过PHP等服务端）。 Yahoo!地图服务器通信开发包。 AS3CoreLib 包含数个基于ActionScript3的工具类库。有MD5 和 SHA 1 加密（上面的AS3Crypt也有这个），图像压缩（现在已经直接整合进入Flex4 SDK了），和 JSON 解析和生成工具，以及一些数字和日期API。 ","date":"2009-12-18","description":"","lastmod":"2009-12-18T07:23:00Z","slug":"887","tags":["actionscript","flash","library","translation"],"title":"【译】20个不能错过的ActionScript类库","url":"https://blog.zengrong.net/post/887/"},{"categories":["news"],"content":"值得学习，有空研究。\nhttp://www.insideria.com/2009/12/28-rich-data-visualization-too.html\n","date":"2009-12-18","description":"","lastmod":"2009-12-18T06:43:36Z","slug":"885","tags":["ria"],"title":"28个富数据呈现工具","url":"https://blog.zengrong.net/post/885/"},{"categories":["technology"],"content":"看了RIAMeeting的AIR2.0入门教程[二]:拖拽增强文章视频一文后，忍不住研究了一下AIR的拖拽功能以及AIR2提供的File Promise概念。AIR的拖拽基本与Flex的拖拽类似，只是AIR使用flash.desktop.NativeDragManager，Flex使用mx.managers.DragManager罢了。\n下面的两个源码就是分别使用文中所提到的AIR1.5的“临时文件”方法与AIR2.0的File Promise方法制作的。\n2 文件 使用方法：\n直接把界面中的红色圆形拖动到桌面上，就可以看到桌面上生成了一个图片文件。如下图所示。\n如果想更深入的了解拖放，可以看下面两篇文章（当然，都是中文的）：\n使用 Flex 开发 Adobe AIR 1.5 应用程序-文件和数据-拖放\n拖动、复制和粘贴数据\n","date":"2009-12-13","description":"","lastmod":"2009-12-13T08:43:44Z","slug":"874","tags":["air"],"title":"AIR2新功能-增强的拖拽","url":"https://blog.zengrong.net/post/874/"},{"categories":["technology"],"content":"通过侦测 StorageVolumeInfo 这个单例类的 StorageVolumeChangeEvent 事件来判断是否有USB存储设备插入了。需要注意以下几点：\n如果在设备插入之后运行的程序，那么这个设备被拔出的事件就不会被侦测到了（不知是否是beta的原因） 不能使用 StorageVolumeChangeEvent 事件侦测已经装载的USB设备，但可以使用 StorageVolumeInfo.storageVolumeInfo.getStorageVolumes() 方法来获取已装载设备的 vector 数组 代码范例：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;s:WindowedApplication xmlns:fx=\u0026#34;http://ns.adobe.com/mxml/2009\u0026#34; 3 xmlns:s=\u0026#34;library://ns.adobe.com/flex/spark\u0026#34; 4 xmlns:mx=\u0026#34;library://ns.adobe.com/flex/halo\u0026#34; 5 applicationComplete=\u0026#34;init()\u0026#34;\u0026gt; 6 \u0026lt;fx:Script\u0026gt; 7 \u0026lt;![CDATA[ 8 import flash.events.StorageVolumeChangeEvent; 9 10 private function init():void 11 { 12 flash.filesystem.StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT, onVolumeMount); 13 flash.filesystem.StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT, onVolumeUnmount); 14 var volumes:Vector.\u0026lt;StorageVolume\u0026gt; = flash.filesystem.StorageVolumeInfo.storageVolumeInfo.getStorageVolumes(); 15 trace(\u0026#39;已有的存储设备列表：\u0026#39;); 16 for each (var volume:StorageVolume in volumes) 17 { 18 trace(volume.name, volume.rootDirectory.nativePath); 19 } 20 } 21 22 private function onVolumeMount(e:StorageVolumeChangeEvent):void 23 { 24 trace(\u0026#39;加载USB存储设备\u0026#39;); 25 trace(e.storageVolume.drive); 26 trace(e.storageVolume.fileSystemType); 27 trace(e.storageVolume.isRemovable); 28 trace(e.storageVolume.isWritable); 29 trace(e.storageVolume.name); 30 trace(e.storageVolume.rootDirectory.url); 31 } 32 33 private function onVolumeUnmount(e:StorageVolumeChangeEvent):void 34 { 35 trace(\u0026#39;卸载\u0026#39;); 36 } 37 ]]\u0026gt; 38 \u0026lt;/fx:Script\u0026gt; 39\u0026lt;/s:WindowedApplication\u0026gt; import flash.events.StorageVolumeChangeEvent; private function init():void { flash.filesystem.StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT, onVolumeMount); flash.filesystem.StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT, onVolumeUnmount); var volumes:Vector.\u0026lt;StorageVolume\u0026gt; = flash.filesystem.StorageVolumeInfo.storageVolumeInfo.getStorageVolumes(); trace('已有的存储设备列表：'); for each (var volume:StorageVolume in volumes) { trace(volume.name, volume.rootDirectory.nativePath); } } private function onVolumeMount(e:StorageVolumeChangeEvent):void { trace('加载USB存储设备'); trace(e.storageVolume.drive); trace(e.storageVolume.fileSystemType); trace(e.storageVolume.isRemovable); trace(e.storageVolume.isWritable); trace(e.storageVolume.name); trace(e.storageVolume.rootDirectory.url); } private function onVolumeUnmount(e:StorageVolumeChangeEvent):void { trace('卸载'); } ","date":"2009-12-07","description":"","lastmod":"2009-12-07T15:44:59Z","slug":"air2-new-feature-usb-check-support","tags":["air"],"title":"AIR2新功能-侦测USB存储设备","url":"https://blog.zengrong.net/post/air2-new-feature-usb-check-support/"},{"categories":["technology"],"content":"本文为译文，原文地址\nAIR2在File类中添加了一个新的函数：openWithDefaultApplication，使用这个函数，可以再AIR2中调用系统中默认的关联程序打开文件。例如，在AIR2中，如果使用openWithDefaultApplication打开一个doc文档，那么在Windows系统中，就会直接打开Word软件打开它。当然，前提是你安装了Word。\n以下几点是需要注意的：\n不能打开一个没有关联程序的文件，否则会抛出运行时错误。当然，你可以获取这个错误并给出一个友好提示。 只能在程序安全沙盒中打开文件 不能与被打开的程序的进程通信，如果需要通信，可以使用NativeProcess AIR不会核实被打开文件的正确性 不能打开可执行程序，为了保证AIR程序的平台无关性，必须使用NativeProcess，同时使用本地安装包（例如，在Windows下面使用exe安装包分发AIR程序），才能调用可执行程序 如果要打开一个文件夹，则会调用默认的文件管理软件（例如，在Windows下是资源管理器） 我随便写的一个简单的范例程序代码：\n1 文件 ","date":"2009-12-07","description":"","lastmod":"2009-12-07T13:43:08Z","slug":"857","tags":["air"],"title":"AIR2新功能-用默认的关联程序打开文件","url":"https://blog.zengrong.net/post/857/"},{"categories":["technology"],"content":"OSMF（Open Source Media Framework）是一个开源的媒体播放框架，下面的视频教程介绍了如何使用OSMF建立视频播放器：Building video players in Flash with the Adobe Open Source Media Framework\n","date":"2009-11-09","description":"","lastmod":"2009-11-09T15:41:20Z","slug":"854","tags":["as3","opensource","osmf","video"],"title":"使用OSMF建立视频播放器","url":"https://blog.zengrong.net/post/854/"},{"categories":["technology"],"content":"下面是IViewport的几个基本属性以及它们分别都代表什么。\nwidth, height, contentWidth, contentHeight, horizontalScrollPosition, verticalScrollPosition.\n详细介绍见：Introduction to viewports and scrolling in Flex 4 beta\n","date":"2009-11-09","description":"","lastmod":"2009-11-09T15:12:48Z","slug":"851","tags":["flex"],"title":"Flex4 Beta中的viewports和scrolling介绍","url":"https://blog.zengrong.net/post/851/"},{"categories":["news"],"content":"各位同学、各位领导：\n大家上午好！（掌声）\n非常高兴许校长给我这么崇高的荣誉，谈一谈我在北大的体会。（掌声）\n可以说，北大是改变了我一生的地方，是提升了我自己的地方，使我从一个农村孩子最后走向了世界的地方。毫不夸张地说，没有北大，肯定就没有我的今天。北大给我留下了一连串美好的回忆，大概也留下了一连串的痛苦。正是在美好和痛苦中间，在挫折、挣扎和进步中间，最后找到了自我，开始为自己、为家庭、为社会能做一点事情。\n学生生活是非常美好的，有很多美好的回忆。我还记得我们班有一个男生，每天都在女生的宿舍楼下拉小提琴，（笑声）希望能够引起女生的注意，结果后来被女生扔了水瓶子。我还记得我自己为了吸引女生的注意，每到寒假和暑假都帮着女生扛包。（笑声、掌声）后来我发现那个女生有男朋友，（笑声）我就问她为什么还要让我扛包，她说为了让男朋友休息一下（笑声、掌声）。我也记得刚进北大的时候我不会讲普通话，全班同学第一次开班会的时候互相介绍，我站起来自我介绍了一番，结果我们的班长站起来跟我说：“俞敏洪你能不能不讲日语？”（笑声）我后来用了整整一年时间，拿着收音机在北大的树林中模仿广播台的播音，但是到今天普通话还依然讲得不好。\n人的进步可能是一辈子的事情。在北大是我们生活的一个开始，而不是结束。有很多事情特别让人感动。比如说，我们很有幸见过朱光潜教授。在他最后的日子里，是我们班的同学每天轮流推着轮椅在北大里陪他一起散步。（掌声）每当我推着轮椅的时候，我心中就充满了对朱光潜教授的崇拜，一种神圣感油然而生。所以，我在大学看书最多的领域是美学。因为他写了一本《西方美学史》，是我进大学以后读的第二本书。\n为什么是第二本呢？因为第一本是这样来的，我进北大以后走进宿舍，我有个同学已经在宿舍。那个同学躺在床上看一本书，叫做《第三帝国的兴亡》。所以我就问了他一句话，我说：“在大学还要读这种书吗？”他把书从眼睛上拿开，看了我一眼，没理我，继续读他的书。这一眼一直留在我心中。我知道进了北大不仅仅是来学专业的，要读大量大量的书。你才能够有资格把自己叫做北大的学生。（掌声）所以我在北大读的第一本书就是《第三帝国的兴亡》，而且读了三遍。后来我就去找这个同学，我说：“咱们聊聊《第三帝国的兴亡》”，他说：“我已经忘了。”（笑声）\n我也记得我的导师李赋宁教授，原来是北大英语系的主任，他给我们上《新概念英语》第四册的时候，每次都把板书写得非常的完整，非常的美丽。永远都是从黑板的左上角写起，等到下课铃响起的时候，刚好写到右下角结束。（掌声）我还记得我的英国文学史的老师罗经国教授，我在北大最后一年由于心情不好，导致考试不及格。我找到罗教授说：“这门课如果我不及格就毕不了业。”，罗教授说：“我可以给你一个及格的分数，但是请你记住了，未来你一定要做出值得我给你分数的事业。”（掌声）所以，北大老师的宽容、学识、奔放、自由，让我们真正能够成为北大的学生，真正能够得到北大的精神。 当我听说许智宏校长对学生唱《隐形的翅膀》的时候，我打开视频，感动得热泪盈眶。因为我觉得北大的校长就应该是这样的。（掌声）\n我记得自己在北大的时候有很多的苦闷。一是普通话不好，第二英语水平一塌糊涂。尽管我高考经过三年的努力考到了北大——因为我落榜了两次，最后一次很意外地考进了北大。我从来没有想过北大是我能够上学的地方，她是我心中一块圣地，觉得永远够不着。但是那一年，第三年考试时我的高考分数超过了北大录取分数线七分，我终于下定决心咬牙切齿填了“北京大学”四个字。我知道一定会有很多人比我分数高，我认为自己是不会被录取的。没想到北大的招生老师非常富有眼光，料到了三十年后我的今天。（掌声）但是实际上我的英语水平很差，在农村既不会听也不会说，只会背语法和单词。我们班分班的时候，五十个同学分成三个班，因为我的英语考试分数不错，就被分到了A班，但是一个月以后，我就被调到了C班。C班叫做“语音语调及听力障碍班”。（ 笑声）\n我也记得自己进北大以前连《红楼梦》都没有读过，所以看到同学们一本一本书在读，我拼命地追赶。结果我在大学差不多读了八百多本书，用了五年时间（掌声）。但是依然没有赶超上我那些同学。我记得我的班长王强是一个书癖，现在他也在新东方，是新东方教育研究院的院长。他每次买书我就跟着他去，当时北大给我们每个月发二十多块钱生活费，王强有个癖好就是把生活费一分为二，一半用来买书，一半用来买饭菜票。买书的钱绝不动用来买饭票。如果他没有饭菜票了就到处借，借不到就到处偷。（笑声）后来我发现他这个习惯很好，我也把我的生活费一份为二，一半用来买书，一半用来买饭菜票，饭票吃完了我就偷他的。（笑声掌声）\n毫不夸张地说，我们班的同学当时在北大，真是属于读书最多的班之一。而且我们班当时非常地活跃，光诗人就出了好几个。后来挺有名的一个诗人叫西川，真名叫刘军，就是我们班的。（掌声）我还记得我们班开风气之先，当时是北大的优秀集体，但是有一个晚上大家玩得高兴了，结果跳起了贴面舞，第二个礼拜被教育部通报批评了。那个时候跳舞是必须跳得很正规的，男女生稍微靠近一点就认为违反风纪。所以你们现在比我们当初要更加幸福一点。不光可以跳舞，而且可以手拉手地在校园里面走，我们如果当时男女生手拉手在校园里面走，一定会被扔到未名湖里，所以一般都是晚上十二点以后再在校园里面走。（笑声掌声）\n我也记得我们班五十个同学，刚好是二十五个男生二十五个女生，我听到这个比例以后当时就非常的兴奋（笑声），我觉得大家就应该是一个配一个。没想到女生们都看上了那些外表英俊潇洒、风流倜傥的男生。像我这样外表不怎么样，内心充满丰富感情、未来有巨大发展潜力的，女生一般都看不上。（笑声掌声）\n我记得我奋斗了整整两年希望能在成绩上赶上我的同学，但是就像刚才吕植老师说的，你尽管在中学高考可能考得很好，是第一名，但是北大精英人才太多了，你的前后左右可能都是智商极高的同学，也是各个省的状元或者说第二名。所以，在北大追赶同学是一个非常艰苦的过程，尽管我每天几乎都要比别的同学多学一两个小时，但是到了大学二年级结束的时候我的成绩依然排在班内最后几名。非常勤奋又非常郁闷，也没有女生来爱我安慰我。（笑声）这导致的结果是，我在大学三年级的时候得了一场重病，这个病叫做传染性侵润肺结核。当时我就晕了，因为当时我正在读《红楼梦》，正好读到林黛玉因为肺结核吐血而亡的那一章，（笑声）我还以为我的生命从此结束，后来北大医院的医生告诉我现在这种病能够治好，但是需要在医院里住一年。我在医院里住了一年，苦闷了一年，读了很多书，也写了六百多首诗歌，可惜一首诗歌都没有出版过。从此以后我就跟写诗结上了缘，但是我这个人有丰富的情感，但是没有优美的文笔，所以终于没有成为诗人。后来我感到非常的庆幸，因为我发现真正成为诗人的人后来都出事了。我们跟当时还不太出名的诗人海子在一起写过诗。后来他写过一首优美的诗歌，叫做《面朝大海，春暖花开》，我们每一个同学大概都能背。后来当我听说他卧轨自杀的时候，嚎啕大哭了整整一天。从此以后，我放下笔，再也不写诗了。（掌声）\n记得我在北大的时候，到大学四年级毕业时，我的成绩依然排在全班最后几名。但是，当时我已经有了一个良好的心态。我知道我在聪明上比不过我的同学，但是我有一种能力，就是持续不断的努力。所以在我们班的毕业典礼上我说了这么一段话，到现在我的同学还能记得，我说：“大家都获得了优异的成绩，我是我们班的落后同学。但是我想让同学们放心，我决不放弃。你们五年干成的事情我干十年，你们十年干成的我干二十年，你们二十年干成的我干四十年”。（ 掌声）我对他们说：“如果实在不行，我会保持心情愉快、身体健康，到八十岁以后把你们送走了我再走。”（笑声掌声）\n有一个故事说，能够到达金字塔顶端的只有两种动物，一是雄鹰，靠自己的天赋和翅膀飞了上去。我们这儿有很多雄鹰式的人物，很多同学学习不需要太努力就能达到高峰。很多同学后来可能很轻松地就能在北大毕业以后进入哈佛、耶鲁、牛津、剑桥这样的名牌大学继续深造。有很多同学身上充满了天赋，不需要学习就有这样的才能，比如说我刚才提到的我的班长王强，他的模仿能力就是超群的，到任何一个地方，听任何一句话，听一遍模仿出来的绝对不会两样。所以他在北大广播站当播音员当了整整四年。我每天听着他的声音，心头咬牙切齿充满仇恨。（笑声）所以，有天赋的人就像雄鹰。但是，大家也都知道，有另外一种动物，也到了金字塔的顶端。那就是蜗牛。蜗牛肯定只能是爬上去。从低下爬到上面可能要一个月、两个月，甚至一年、两年。在金字塔顶端，人们确实找到了蜗牛的痕迹。我相信蜗牛绝对不会一帆风顺地爬上去，一定会掉下来、再爬、掉下来、再爬。但是，同学们所要知道的是，蜗牛只要爬到金字塔顶端，它眼中所看到的世界，它收获的成就，跟雄鹰是一模一样的。（掌声）所以，也许我们在座的同学有的是雄鹰，有的是蜗牛。我在北大的时候，包括到今天为止，我一直认为我是一只蜗牛。但是我一直在爬，也许还没有爬到金字塔的顶端。但是只要你在爬，就足以给自己留下令生命感动的日子。（掌声）\n我常常跟同学们说，如果我们的生命不为自己留下一些让自己热泪盈眶的日子，你的生命就是白过的。我们很多同学凭着优异的成绩进入了北大，但是北大绝不是你们学习的终点，而是你们生命的起点。在一岁到十八岁的岁月中间，你听老师的话、听父母的话，现在你真正开始了自己的独立生活。我们必须为自己创造一些让自己感动的日子，你才能够感动别人。我们这儿有富裕家庭来的，也有贫困家庭来的，我们生命的起点由不得你选择出生在富裕家庭还是贫困家庭，如果你生在贫困家庭，你不能说老爸给我收回去，我不想在这里待着。但是我们生命的终点是由我们自己选择的。我们所有在座的同学过去都走得很好，已经在十八岁的年龄走到了很多中国孩子的前面去，因为北大是中国的骄傲，也可以说是世界的骄傲。但是，到北大并不意味着你从此大功告成，并不意味着你未来的路也能走好，后面的五十年、六十年，甚至一百年你该怎么走，成为了每一个同学都要思考的问题。就本人而言，我觉得只要有两样东西在心中，我们就能成就自己的人生。\n第一样叫做理想。我从小就有一种感觉，希望穿越地平线走向远方，我把它叫做“穿越地平线的渴望”。也正是因为这种强烈的渴望，使我有勇气不断地高考。当然，我生命中也有榜样。比如我有一个邻居，非常的有名，是我终生的榜样，他的名字叫徐霞客。当然，是五百年前的邻居。但是他确实是我的邻居，江苏江阴的，我也是江苏江阴的。因为崇拜徐霞客，直接导致我在高考的时候地理成绩考了九十七分。（掌声）也是徐霞客给我带来了穿越地平线的这种感觉，所以我也下定决心，如果徐霞客走遍了中国，我就要走遍世界。而我现在正在实现自己这一梦想。所以，只要你心中有理想，有志向，同学们，你终将走向成功。你所要做到的就是在这个过程要有艰苦奋斗、忍受挫折和失败的能力，要不断地把自己的心胸扩大，才能够把事情做得更好。\n第二样东西叫良心。什么叫良心呢？就是要做好事，要做对得起自己对得起别人的事情，要有和别人分享的姿态，要有愿意为别人服务的精神。有良心的人会从你具体的生活中间做的事情体现出来，而且你所做的事情一定对你未来的生命产生影响。我来讲两个小故事，讲完我就结束我的讲话，已经占用了很长的时间。\n第一个小故事。有一个企业家和我讲起他大学时候的一个故事，他们班有一个同学，家庭比较富有，每个礼拜都会带六个苹果到学校来。宿舍里的同学以为是一人一个，结果他是自己一天吃一个。尽管苹果是他的，不给你也不能抢，但是从此同学留下一个印象，就是这个孩子太自私。后来这个企业家做成功了事情，而那个吃苹果的同学还没有取得成功，就希望加入到这个企业家的队伍里来。但后来大家一商量，说不能让他加盟，原因很简单，因为在大学的时候他从来没有体现过分享精神。所以，对同学们来说在大学时代的第一个要点，你得跟同学们分享你所拥有的东西，感情、思想、财富，哪怕是一个苹果也可以分成六瓣大家一起吃。（掌声）因为你要知道，这样做你将来能得到更多，你的付出永远不会是白白付出的。\n我再来讲一下我自己的故事。在北大当学生的时候，我一直比较具备为同学服务的精神。我这个人成绩一直不怎么样，但我从小就热爱劳动，我希望通过勤奋的劳动来引起老师和同学的的注意，所以我从小学一年级就一直打扫教室卫生。到了北大以后我养成了一个良好的习惯，每天为宿舍打扫卫生，这一打扫就打扫了四年。所以我们宿舍从来没排过卫生值日表。另外，我每天都拎着宿舍的水壶去给同学打水，把它当作一种体育锻炼。大家看我打水习惯了，最后还产生这样一种情况，有的时候我忘了打水，同学就说“俞敏洪怎么还不去打水”。（笑声）。但是我并不觉得打水是一件多么吃亏的事情。因为大家都是一起同学，互相帮助是理所当然的。同学们一定认为我这件事情白做了。又过了十年，到了九五年年底的时候新东方做到了一定规模，我希望找合作者，结果就跑到了美国和加拿大去寻找我的那些同学，他们在大学的时候都是我生命的榜样，包括刚才讲到的王强老师等。我为了诱惑他们回来还带了一大把美元，每天在美国非常大方地花钱，想让他们知道在中国也能赚钱。我想大概这样就能让他们回来。后来他们回来了，但是给了我一个十分意外的理由。他们说：“俞敏洪，我们回去是冲着你过去为我们打了四年水。”（掌声）他们说：“我们知道，你有这样的一种精神，所以你有饭吃肯定不会给我们粥喝，所以让我们一起回中国，共同干新东方吧。”才有了新东方的今天。（掌声）\n人的一生是奋斗的一生，但是有的人一生过得很伟大，有的人一生过得很琐碎。如果我们有一个伟大的理想，有一颗善良的心，我们一定能把很多琐碎的日子堆砌起来，变成一个伟大的生命。但是如果你每天庸庸碌碌，没有理想，从此停止进步，那未来你一辈子的日子堆积起来将永远是一堆琐碎。所以，我希望所有的同学能把自己每天平凡的日子堆砌成伟大的人生。（掌声）\n最后，我代表全体老校友向在座的三千多位新生表一个心意，我代表全体老校友和新东方把两百万人民币捐给许校长，为在座同学们的学习、活动和成长提供一点帮助。（掌声）\n","date":"2009-11-02","description":"","lastmod":"2009-11-02T08:28:16Z","slug":"848","tags":["lecture"],"title":"【转】俞敏洪演讲稿","url":"https://blog.zengrong.net/post/848/"},{"categories":["news"],"content":"转自AuthurXF\n在地址栏输入about:config回车 找到browser.cache.check_doc_frequency选项，双击将3改成1 可以设置的值及其含义：\n0: Once per session 1: Each time\n每次访问此页时检查 2: Never\n不检查 3: When appropriate/automatically\n自动 ","date":"2009-10-07","description":"","lastmod":"2009-10-07T10:45:13Z","slug":"835","tags":["firefox"],"title":"设置firefox每次访问网页时检查所存网页的较新版本","url":"https://blog.zengrong.net/post/835/"},{"categories":["technology"],"content":"2011年6月17日更新：有网友说\nSDK4.5下编译报错 1119: 访问可能未定义的属性 textFlow (通过 static 类型 spark.core:IEditableText 引用)。 TextChat.mxml /TextChat/src 第 94 行\n问题的原因，在于SDK4.5将TextInput和TextArea的外观部件textDisplay的类型从原来的RichEditableText改为了IEditableText，因此无法得到textFlow属性。这是为了适应Mobile设备。在Spark主题下，它是 RichEditableText，而在Mobile主题下，它是StyleableTextField。\n我解决的办法是强制将IEditableText转换成RichEditableText（见TextChat.mxml第95,100行）。这在Spark主题下当然是没问题的，但这并非是最好的办法。现在的源码是可以正常在SDK4和SDK4.5下编译的，请重新下载。\n另外，经常有网友问到下载的压缩包中是一个FXP文件，这个该如何处理。其实这个问题Google一下自然有答案，做Flex，居然不知道FXP是什么，而且随手Google也不愿意做，我就只能鄙视你一下了。为了避免大家的麻烦，新提供的源码中增加了编译好的swf方便大家看效果，同时也放弃了FXP格式。我现在采用的是ANT+SDK编译，所以不要问我怎么把项目导入Flash Builder。\n2010年10月7日更新：很多网友反应在SDK4正式版中，即使修改了源码也用不了，于是就顺手改了一下。现在的源码基于SDK4.1正式版，删除了一些原来beta版的时候用来解决beta版bug的代码，并加入了切分字符串的功能。也就是说，如果先输入文字，然后将输入点置于文字中再插入图片，图片会自动将文字分开。唉——转眼间这个源码发布一年了，时间过得真快啊……\n顺便说一句：后来我又做了一个魔法表情功能，可以看这里\n2009年10月29日更新：今天发现，有几个问题，Flash Builder beta2 自带的SDK已经解决了（下面标出了），看来beta确实是不能用于正式产品啊……\n2009年10月8日更新：Flash Builder beta 2发布后，spark组件中有些类的名称修改了，比如TextFilter改为TextConverter，SimpleText改为Label，TextArea.textview.textFlow改为TextArea.textFlow……因此如果使用beta2，那么下面的源码可能不会编译通过，请自行修改。\n我发布“在Flex中实现聊天表情图片支持-资料篇”后，便有许多朋友找我要那个范例的源码。我在文章中就已提过那范例是从网上down来，并非我所做，自然没有源码。要源码，用X思，你懂的……\n那好吧，源码来了。\n例子很简单，所有的代码加起来都不到400行，但做的时候却很痛苦。因为要了解spark组件和FXG的特点，还要了解新的skin的做法。不过总算做好了，总结一下，纠结的地方主要有下面几点：\n1. 采用内嵌表情还是采用外部表情图片。\n为了获得更小的文件（Flex4比Flex3编译的文件大了200多K），一直是采用外部表情图片的。在本机调试都正常。但上传到网上后，就无法显示图片。调整了swf的base属性之后，图片可以在输入框和文字框中显示，就是不能显示在图片选择面板中。最后一气之下改成了内嵌图片。后来发现有可能是浏览器缓存问题，不过改就改了，也就没再纠缠这个问题。\n要注意的是，spark的BitmapImage是不支持外部图片的，必须要内嵌。所以如果要用外部图片，就必须用Halo组件中的Image。而由于TLF中的InlineGraphicElement又仅支持URL或者DisplayObject，而BitmapImage无法提供URL（本来嘛，是内嵌的撒）因此在我将外部图片转成内嵌后，原来的程序出了问题。原因是BitmapImage的source属性返回的是BitmapData对象，如果将其提供给InlineGraphicElement的source属性，就会报错。因此在提供图片信息的时候，必须要提供Bitmap才行。\n2010-10-07：正式版的InlineGraphicElement的source属性已经可以支持BitmapData对象了\n2. 图片插入到TextInput之后不更新。\n检查发现，InlineGraphicElement已经作为textFlow的元素存在了，但是在TextInput中却不显示出来，但如果再输入一些文字的话，图像又可以显示出来。因此判断是textFlow更新后没有执行flowComposer.updateAllControllers。把这个操作放在 StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGED 事件后就行了。\n2010-10-07正式版中已经支持自动更新了。\n3. Flex4 spark中的TextInpu焦点定位的怪问题。\n在发送输入框文字之后，再将文字清空，如果使用按钮来发送文字，则清空过后还能重新定位回输入框继续输入文字；而如果使用TextInput的enter事件来完成这个动作，则清空后返回虽然能重新定位输入框，并显示闪烁的输入光标，但无论如何也无法输入文字了，必须重新单击一次输入框，才能再次输入。后来用一个变通的方法解决了。就是在为输入框重新设置焦点之前，先将焦点移到按钮上，再移回来……很无语 Flash Builder beta2自带的SDK（以及后面的SDK）已经解决了这个问题\n4. flowComposer自动清空为null的问题。\nflowComposer偶尔会自动清空，造成flowComposer.compose()或者flowComposer.updateAllControllers()失效，后来发现在界面大小变更的时候以及通过text=''设置文字清空的时候，flowComposer都可能会清空为null。而只需要重新将ti或者ta设置焦点就可以填充flowComposer的值。\nFlash Builder beta2自带的SDK（以及后面的的SDK）已经解决了这个问题\n5. 滚动条不更新的问题。\n输入的文字超过一屏之后，TextArea会自动出现滚动条。在Halo组件中，我是通过valueCommit事件来更新滚动条，但在spark中没有。不仅valueCommit没用，change等统统没用。后来改为用updateComplete来更新滚动条。其实，如果用textFlow的事件来更新，应该更加保险。\n另外，spark的scroller组件也是蛮纠结的，在Halo中用 ta.verticalScrollPosition = ta.maxVerticalScrollPosition 就可以了，可spark又整了个viewport出来。这不，更新语句又要改成 ta.scroller.viewport.verticalScrollPosition=ta.scroller.viewport.contentHeight 。\n6. 还有几个麻烦的事情，比如ParagraphElement要进行深复制才能用，比如format和hostFormat……算了，不说了，直接看效果好了 :em50:\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n1 文件 ","date":"2009-09-16","description":"","lastmod":"2009-09-16T07:40:09Z","slug":"emotion-support-in-flex-2","tags":["actionscript","flex","fte","tlf"],"title":"在Flex中实现聊天表情图片支持-实战篇","url":"https://blog.zengrong.net/post/emotion-support-in-flex-2/"},{"categories":["news"],"content":" Differences between Flex 3 and Flex 4 beta（比较Flex3和Flex4的区别，很全面，值得一看） 了解一下Flex 4里的fx、mx以及s命名空间 Introducing skinning in Flex 4 beta（强烈推荐！简单地介绍了Skin part、state、skinClass的编写方法以及FXG语法，有范例） Overview of custom component changes in Adobe Flex 4 beta（自定义组件在Flex4中的改变） Spark layouts with Flex 4 beta（不了解Flex4新的布局机制的，看看这个吧） Enhanced States Syntax - Functional and Design Specification（新的state使用方法详细介绍） MXML 2009 - Functional and Design Specification（着重介绍了Flex4的Namespace、Declarations、Private、Library标签的用法，以及Flex4与Flex3之间的区别） Flex4:DataGroup and ItemRenderers Flex 4 ＆ Custom Layouts（Flex4与自定义布局，中文在这里） 视频专题 视频：Flex4中的高级CSS 视频：在Flex4中为组件和程序换肤 Learn to Use Flash Builder 4 and Flex 4 SDK（Adobe Labs的教程，比较全面，由浅入深的涉及到Flex4的各个方面，有视频、有文章、有资源） Tutorial and Demonstration Videos（视频教程集合，全面涉及Flex4的各个方面） FXG专题 Understanding FXG, a primer on Adobe's new graphics file format（了解FXG） FXG 1.0 - Functional and Design Specification（FXG语法详细介绍） FXG 1.0 Intro 1: Basic Shape FXG 1.0 Intro 2: 万能的Path之一 FXG 1.0 Intro 3: 万能的Path之二 FXG 1.0 Intro 4: stroke and fill FXG 1.0 Intro 5: Color Transform FXG 1.0 Intro 6: BitmapImage and Transform 组件专题 Creating a custom Halo Accordion header skin in Flex 4（如果你垂涎于Flex4提供的新的皮肤功能，但有必须使用老的Halo组件，那么可以看看将这篇文章。看看如何使用FXG来定义Halo组件的皮肤） Effect专题 Effects in Adobe Flex 4 beta DataGroup和虚拟化 Using virtualization with Spark DataGroup and SkinnableDataContainer（在DataGroup和SkinnableDataContainer中使用虚拟化） Using Virtualized Layouts and DataGroups in Flex 4（在Flex4中使用虚拟化布局） Spark Virtualization - Functional and Design Specification（Spark组件中的虚拟化技术功能和设计说明） ","date":"2009-09-02","description":"","lastmod":"2009-09-02T16:42:59Z","slug":"789","tags":["css","flex","fxg","layout","skin"],"title":"了解Flex4中新的的Skin、CSS、Layout、NameSpace相关资料","url":"https://blog.zengrong.net/post/789/"},{"categories":["technology"],"content":"文中介绍了Flex4的命名空间（namespaces）、FXG、布局（layout）、状态（state）和新的mxml标记（如fx:Declarations、fx:Definition、fx:Library等），值得一看。\n译文：http://blog.csdn.net/lihe111/archive/2009/08/10/4431579.aspx\n原文：http://www.sitepoint.com/article/whats-new-flex-4/\n","date":"2009-09-02","description":"","lastmod":"2009-09-02T16:22:56Z","slug":"787","tags":["flex","fxg","layout","spark"],"title":"Flex4 中有哪些出色的新特性？","url":"https://blog.zengrong.net/post/787/"},{"categories":["technology"],"content":"2011-08-24更新：几篇TLF相关文章的连接\nTLF的基本架构 TLF概览 Flow Model 的架构与设计 一些对TLF和FTE的思考 在Flex中实现聊天表情图片支持-实战篇 在在Flex中实现聊天表情图片支持-资料篇一文中，我提到了FTE（Flash Text Engine）和TLF（Text Layout Framework）。它们的强大自不必多说，可去该文章提供的网址查看，或者直接看这个Demo。但是，在Flex与Flash中有对应的组件可以使用么？FTE和TLF是什么关系？下面就来说说吧： :em03:\n关系 用下面这张图来说明是最好的了。TLF是建立在FTE的基础上的。或者说TLF在FTE的基础上实现了“高级功能”。直接使用FTE是很难受的（后面我会专门写文章演示代码），连Adobe都这样说：\n“FTE 提供对文本度量、格式和双向文本的复杂控制的低级别支持。 尽管可以使用 FTE 创建和管理简单的文本元素， 但设计 FTE 的主要目的在于为开发人员创建文本处理组件提供基础。”\n请注意加粗的部分。Adobe认为FTE并不适合做文本处理。因此，Adobe又做了TLF，并且将TLF整合到了Flex SDK 4.0（Gumbo）中。但TLF同样不是组件。不过TLF已经可以将需要显示的文本输入到DisplayObject当中，并加入了对选择文本、编辑文本、快捷键、事件的支持，虽然仍不能直接支持滚动条（配合事件和滚动条组件是可以支持的），但这已经前进了一大步，更方便我们使用了。\n那我们就基于TLF来开发么？不是。我需要的是像Flash或者Flex中的TextArea组件一样来简单的使用TLF。为了实现我这个美好的愿望，Adobe针对Flash CS4制作了Text Layout Component组件，针对Flex的组件则包含在新的spark包中。\n下载Text Layout Component：\n2 文件 说到这里，这个关系就比较顺了。TLF就是FTE的扩展，这个扩展其实还是代码层面多些。而Text Layout Component和spark中的相关组件又是TLF的扩展，它们才是真正的组件级别的应用。所以，我把主要的精力放在熟悉组件的功能与特性上（人懒，没办法 :em45: ）。当然，对于TLF和FTE我也做了了解，毕竟它们是底层。\n本图片采用国产软件EDrawMax绘制，本图片的源文件下载：\n1 文件 ","date":"2009-08-21","description":"","lastmod":"2009-08-21T02:10:53Z","slug":"flash-text-engine-text-layout-framework-in-flash-and-flex","tags":["flash","flashbuilder","flex","fte","spark","tlf"],"title":"Flash Text Engine、Text Layout Framework在Flex、Flash中的实现","url":"https://blog.zengrong.net/post/flash-text-engine-text-layout-framework-in-flash-and-flex/"},{"categories":["technology"],"content":"Flash Builder确实很让人激动，我最喜欢的是下面这几个特性：\n中文界面 增强的包管理 包重载 新的spark皮肤和组件 自动生成getter/setter 自动生成事件管理函数 主题管理器 新的调试器和概要分析器 中文界面 中文界面看起来就是舒服啊 ;-)\n增强的包管理 包重载 我有个不好的喜欢，过2个月后再看原来的代码，就喜欢把它们重新换个package，这个习惯让我经常头疼于几十上百的类文件的包修改，原来是使用在文件中查找替换进行的，现在FlashBuilder提供这个功能了。\n新的spark皮肤和组件 关于这点，我将单独写一篇文章说明\n自动生成getter/setter 不说了，直接看图\n自动生成事件管理函数 这个功能实在是太太太太方便了，即使没有为组件命名，FlashBuilder也会自动为组件设置一个id。充分方便我这懒人 :em45:\n主题管理器 还是直接看图\n新的调试器和概要分析器 这个就看这篇文章吧，英文。\n","date":"2009-08-19","description":"","lastmod":"2009-08-19T09:58:52Z","slug":"753","tags":["flashbuilder","flex","spark"],"title":"Flash Builder Beta(Flex4,Gumbo)：新特性不完全说明","url":"https://blog.zengrong.net/post/753/"},{"categories":["technology"],"content":"Adobe devnet的文章，详细介绍了如何利用Flex从客户端上传视频，用Flash Media Encoding Server在服务器端转换视频，然后利用FMS流发布视频的流程。浅显易懂，值得一看。\n[\n](/uploads/2009/08/fig01.jpg)\nCreating a video sharing web application using Flex, Flash Media Server, and Flash Media Encoding Server\nFlash Media Encoding Server好是好，就是太贵了，6K美刀啊 :shock: （看完文章我才发现，早知不看这鸟文）……还是ffmpeg比较靠谱\n","date":"2009-08-17","description":"","lastmod":"2009-08-17T08:55:02Z","slug":"745","tags":["flex","fms"],"title":"使用Flex、Flash Media Server和Flash Media Encoding Server创建视频共享程序","url":"https://blog.zengrong.net/post/745/"},{"categories":["technology"],"content":" 2011年8月24日09:44:35 更新：重新找到了基于riaidea的TextField的图文混排组件源码，见作者博客 这个组件在对文本进行滚动的时候占用CPU较大，我在30行文本+少量表情滚动的时候，CPU瞬间可达30%以上（AMD羿龙II三核），因此在性能较差的机器上可能感觉滚动会卡。综合目前Flash Player 10的市占率90%以上来考虑，应该使用TLF来开发聊天表情支持功能 （其实TLF大量文本滚动也会卡的，但比这个要好些） 。见在Flex中实现聊天表情图片支持-实战篇。 2009年10月9日11:09:14 更新：增加riaidea的范例，（riaidea的作者自行关闭了googlecode上的开源项目，因此riaidea的源码不能下载了，我也没有源码） 2009年9月16日20:34:30 更新：在Flex中实现聊天表情图片支持-实战篇 2009年8月21日09:36:19 更新：Flash Text Engine、Text Layout Framework在Flex、Flash中的实现 仔细研究了一下Flex支持表情图片，主要有下面三种方法：\n使用TextArea，或直接使用TextField的html支持功能，在html中使用 \u0026lt;img\u0026gt; 标签嵌入表情图片文件。但这样做有两个问题，一是图片文件载入较慢；二是不容易控制载入的图片在文本中的“流向”。虽然可以使用getImageReference获取对图片的引用，但处理起来也比较费力。 将文本中需要插入图像的地方留出空白空间，获取空白空间的位置，并使用图像替代。这种方法的缺点在于每次更新文字内容的时候，所有的图片都要重排位置，对性能有一定影响，尤其是文本和图像比较多的情况下。 使用Flash Player 10提供的flash.text.engine（简称FTE）提供的高级功能进行处理，具体是使用GraphicElement类，这无疑是更好的方法。Adobe提供的Text Layout Framework（简称TLF）就是架设FTE的基础之上的。 下面是一些我搜集的资料，为后面的实战做个记录：\nHow to use Text Layout Framework in Flex 3.2 or AIR 1.5 How to add a scrollbar to Text Layout Framework Utilizing Flash Text Layout Framework using MXML Flex 用AS3GIF类播放gif动画 Spark Text Primitives - Functional and Design Specification 下面这个范例是我在网上找到的，使用的应该是第2种方法。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n（2009年10月9日11:16:10更新）再加一个范例：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2009-08-10","description":"","lastmod":"2009-08-10T10:03:03Z","slug":"emotion-support-in-flex-1","tags":["actionscript","flex","fte","tlf"],"title":"在Flex中实现聊天表情图片支持-资料篇","url":"https://blog.zengrong.net/post/emotion-support-in-flex-1/"},{"categories":["technology"],"content":"我在为一个HBox标签添加背景图像的时候碰到了问题。\n虽然HBox的CSS属性支持中有background-position这个属性，并且支持top、left等位置，但是当我正确设定并编译swf之后，发现背景图像并不像我设置的那样排列，反而没什么变化。google了一下资料，找到了Flex cookbook上的解决办法：\nCSS background-position\n但是，这个办法在我的程序（Flex SDK 3.4）中并不管用，没办法，我还是只能用degrafa来实现了。degrafa是个优秀的矢量绘图引擎，使用它不仅能支持background-position，还能支持background-repeat（关于背景平铺，还可以看 这篇文章 ）。\n使用方法：\n1HBox 2{ 3 border-skin: ClassReference(\u0026#34;com.degrafa.skins.CSSSkin\u0026#34;); 4 background-image: Embed(source=\u0026#34;windowroom/bg2.jpg\u0026#34;); 5 background-position:top; 6 background-repeat:no-repeat; 7 8} 引入 com.degrafa.skins.CSSSkin 包后，编译的swf文件大小会增加51KB。如果对文件大小很在乎的话，还是去仔细研究一下Flex Cookbook中提供的方法好了。 ;-)\n","date":"2009-08-10","description":"","lastmod":"2009-08-10T02:10:33Z","slug":"background-position-in-flex","tags":["css","degrafa","flex","skin"],"title":"Flex不支持background-position属性的问题","url":"https://blog.zengrong.net/post/background-position-in-flex/"},{"categories":["technology"],"content":"转自:JustinYoung's Blog\nW3C最近宣布将于今年年底解散XTHML2工作组。一石激起千层浪，很多误解和谣言四起，江湖一片血雨腥风，搞得网页设计师人人自危，好像世界末日即将到来。其实，这只是个误解，看完下面的这幅漫画，大家就了解了。完全可以放心，然后回家洗洗睡了。\n非原创，来源网络：英文版源文地址：漫画英文版源文地址，这里是英文原版漫画\n[caption id=\u0026quot;attachment_708\u0026quot; align=\u0026quot;aligncenter\u0026quot; width=\u0026quot;169\u0026quot; caption=\u0026quot;混乱的标记语言XHTML2/HTML5\u0026quot;][/caption]\n","date":"2009-07-30","description":"","lastmod":"2009-07-30T15:28:30Z","slug":"709","tags":["html"],"title":"【转】漫画：混乱的标记语言XHTML2/HTML5（附中文版翻译）","url":"https://blog.zengrong.net/post/709/"},{"categories":["technology"],"content":"别找了，Flex自己是没这个功能的。下面这些链接都讲解了怎么自行实现。\n转自：http://bbs.airia.cn/FLEX/thread-4095-1-1.aspx\nBackground image repeat in flex 3 - DEGRAFA!\ne.g:\n1Application { 2 background-image: Embed(\u0026#34;assets/images/the_image_goes_here.png\u0026#34;); 3 background-repeat: repeat; 4 background-position: center; 5 background-blend: multiply; 6 borderSkin: ClassReference(\u0026#34;com.degrafa.skins.CSSSkin\u0026#34;); 7} 此外还有：\nhttp://frankieloscavio.blogspot.com/2007/08/repeating-and-tiling-css-backgrounds-in.html http://www.websector.de/blog/2007/07/06/pimp-your-flex-app-using-wsbackgroundpixelskin/ http://www.websector.de/blog/2007/07/10/wspatternstylegenerator-for-using-wsbackgroundpixelskin/ http://blog.creacog.co.uk/2007/06/11/tile-or-repeat-an-image-into-a-flex-application-background-ii/ http://blog.creacog.co.uk/2006/11/21/tile-or-repeat-an-image-into-a-flex-application-background/ http://renaun.com/blog/2006/12/08/165/ ","date":"2009-06-07","description":"","lastmod":"2009-06-07T15:13:06Z","slug":"repeat-background-in-flex","tags":["css","degrafa","flex","skin"],"title":"如何在Flex中实现图片背景平铺","url":"https://blog.zengrong.net/post/repeat-background-in-flex/"},{"categories":[],"content":"最后更新：2015-04-26\n因为 重回 Ubuntu 桌面 ，这篇文章又开始更新了。\n有人说，Windows下的好软件是选出来的，Linux下的好软件是配置出来的。而我认为，Linux下面有更多的选择。\n因此，就有了这篇文章，记录我最喜欢的Linux软件。\n网络 tsocks 类似于windows下面的sockscap，在ununtu的源中提供。 dante-client 同上，在ubuntu的源中提供 qbittorrent 好用的 bittorrent 客户端，基于 QT 。 图像 gThumb 总觉得ubuntu自带的 eog 不好用，这个就不错，带有一定的编辑功能。 GPicView 比 eog 的启动速度更快 feh 基于命令行的看图软件 scrot 基于命令行的截图软件 Shutter 截图软件 Fragment 超酷的图像管理软件，但并非自由软件。 Pinta 简单的编辑图像编辑功能。 这里有一个更全的图像查看软件列表：\nNifty Free Image Viewers 中译文\n这里是关于图像编辑软件的：\nBest Image Editors for Ubuntu\n文本编辑 leafpad Linux 下的 notepad ，支持编码自动识别，启动比 GEdit 快。 未分类 Ubuntu Tweak Meld FileZilla PuTTY SSH Client 星际译王 GNOME Do FoxitReader chromeplus ","date":"2009-04-30","description":"","lastmod":"2009-04-30T09:16:53Z","slug":"linux-software","tags":[],"title":"Linux软件","url":"https://blog.zengrong.net/linux-software/"},{"categories":["others"],"content":"前田约翰《简单法则》十条：\n第一，减少，就是说，达到简单的最简单方法，就是要有所割舍，割舍一些没用的功能、多余的部分，就能简单许多。\n第二，组织，妥善组织能使复杂的系统显得比较简单，这就好比合理使用一张写字台。\n第三，时间，节省时间也会让人感觉简单(虽然这种一时的简单不一定是真正的简单)。\n第四，学习，知识、经验的积累能帮助人们把某些事物变得更为简捷。\n第五，差异，简单和复杂相辅相成，没有复杂的对比反差，简单就不能更好地显现。\n第六，背景，简单的周边事物决非无关紧要，它有助于形成一种简单的氛围，让人感觉到简单。\n第七，感情，感情的寄托也有助于简单。\n第八，信任，要对一些简单的事物报以必要的信任。\n第九，失败，要相信有些事物不可能简单，不是所有东西都适合简单。\n第十，单一，简单就是要求减少形式的、无意义的，增加有意义的。\n","date":"2009-03-25","description":"","lastmod":"2009-03-25T06:21:09Z","slug":"simple-principle-10","tags":[],"title":"前田约翰《简单法则》十条","url":"https://blog.zengrong.net/post/simple-principle-10/"},{"categories":["news"],"content":"理解VCard（转自） vCard有三种数据类型，single type,list type,structured type.\nSingle type如FN;ENCODING=b;VALUE=uri:liudehua FN是字段名，第一个分号（;）到第一个冒号（:）之间的是参数，第一个冒号（:）后面的全部作为FN的值。\nList type 和single type不同的是在第一个冒号后面取出来的值都用分号分开（;）做为多个值返回。\nStructurd type如:\nN;ENCODING=b;VALUE=uri:family;given;other;suffix;prefix; N表示字段名称，ENCODING=b;VALUE=uri是参数列表，参数列表配置在mobileInfo配置文件中，从冒号(:)后面一次表示家庭姓名、给定名、其他姓名、前缀、后缀，这在vCard规范中定义的次序，其中的值用分号（;）分开。在把contact转换成vCard文本时次序一定不能乱。\nvCard 规范容许公开交换个人数据交换 (Personal Data Interchange PDI) 信息，在传统纸质商业名片可找到这些信息。规范定义电子名片（或叫vCard）的格式。\nvCard 规范可作为各种应用或系统之间的交换格式。定义的格式与传送的方法无关。传送交换可能是文件系统，点对点交换的公共电话网络，以有线网络或无线传送的方式。用户能在互联网上直接利用vCard。电子邮件能转发在vCard中人信息。网页上很多用户填写的表格可自动使用vCard。\n互联网邮件协会（Internet Mail Consortium）正在与互联网工程队伍（Internet Engineering Task Force IETF）共同努力，扩充以多用途互联网邮件为基础（Internet MIME-based）的互联网电子邮件标准标准，使之兼容vCard。vCard规范的XML绑定生成了DTD [vCard ，98] 有助于开发IMS LIP。\nVCard 2.1（rfc－2426）标准通信薄基本格式（转自） ** ** VCard 数据格式的标识符是VCARD 预定义的值类型：uri, date, date-time, float\n新增加的值类型：binary, phone-number, utc-offset and vcard value\n预定义的类型：SOURCE, NAME, PROFILE, BEGIN, END.\n新增加的类型：FN, N, NICKNAME, PHOTO, BDAY, ADR, LABEL, TEL, EMAIL,\nMAILER, TZ, GEO, TITLE, ROLE, LOGO, AGENT, ORG, CATEGORIES, NOTE,\nPRODID, REV, SORT-STRING, SOUND, URL, UID, VERSION, CLASS, KEY\n预定义的参数：ENCODING, VALUE, CHARSET, LANGUAGE, CONTEXT.\n新增加的参数：TYPE\nvCard数据格式行是: 类型 [;参数]:值 ADR;HOME;POSTAL;PARCEL:;;街道地址;深圳;广东;433330;中国\nADR：是一个类型，表示是一条地址信息\n“;”号是分隔符合\nHOME;POSTAL;PARCEL表示参数，表示ADR的用途或者是类别\n:;;街道地址;深圳;广东;433330;中国 表示是一个ADR值，地址值\n预定义类型的用法 BEGIN 和 END 类型\nVcard内容必须以BEGIN:VCARD开头，以END:VCARD结尾\n参考一个vcard的例子1：\n1BEGIN:VCARD 2VERSION:2.1 3N:姓;名 4FN:姓名NICKNAME:nickName 5ORG:公司;部门 6TITLE:职位 7NOTE;ENCODING=QUOTED-PRINTABLE:=C6=E4=CB=FB 8TEL;WORK;VOICE:电话1 9TEL;WORK;VOICE:电话2 10TEL;HOME;VOICE:电话1 11TEL;HOME;VOICE:电话2 12TEL;CELL;VOICE:13590342862 13TEL;PAGER;VOICE:0755 14TEL;WORK;FAX:传真 15TEL;HOME;FAX:传真 16ADR;WORK:;;单位地址;深圳;广东;433000;国家 17LABEL;WORK;ENCODING=QUOTED-PRINTABLE:=B5=A5=CE=BB=B5=D8=D6=B7 18=C9=EE=DB=DA 19=B9=E3=B6=AB 20433000 21=B9=FA=BC=D2 22ADR;HOME;POSTAL;PARCEL:;;街道地址;深圳;广东;433330;中国 23LABEL;HOME;ENCODING=QUOTED-PRINTABLE:=BD=D6=B5=C0=B5=D8=D6=B7 24=C9=EE=DB=DA 25=B9=E3=B6=AB 26433330 27=D6=D0=B9=FA 28URL:网址 29URL:单位主页 30EMAIL;PREF;INTERNET:邮箱地址 31X-QQ:38394246 32X-ICQ:icq 33X-WAB-GENDER:2 34REV:20060220T180305Z 35END:VCARD NAME 类型\n如果在内容中出现NAME类型，那么它的值是一个可以显示的，描述vCard源的文本 PROFILE类型\n如果出现PROFILE类型，那么它的值必须是“VCARD” SOURCE 类型\n如果包含SOURCE类型，它的值提供一些怎样找到vCard源的信息\n预定参数的用法\nLANGUAGE\n参考[MIME-DIR]文档\nENCODING\n参考[MIME-DIR]文档\nVALUE\n参考[MIME-DIR]文档\n预定义值类型的用法\n在[MIME-DIR]中预定类型的值一定不能包含用逗号分开的列表，除N，NICKNAME, ADR和 CATEGORIES值类型外。\n预定义值类型的扩展\nBINARY\n表明类型的值是二进制的，主要应用在类型PHOTO, LOGO, SOUND, and KEY中.\nENCODING参数的值必须指定为“B” 二进制的内容的编码参考[RFC 2047]\nVCARD\n表示一个类型的值是一个vCard对象\nPHONE-NUMBER\n表示类型的值是一个电话号码\nUTC-OFFSET\n表示时间\n结构类型的值\n复合类型值是用分号分开的字段的集合，复合类型值中避免使用分号，如何需要使用分号，需要用“\\分号“替代\n行的限定和分行\n参考[MIME DIR]，如果行的长度超过了75个字符，那么必须分行。\nVCard 特征\n标识类型\nFN 类型定义\n目的：vcard对象的名称，一个vcard对象必须包含FN类型。\n例子：FN:Mr. John Q. Public\\, Esq.\nN类型定义\n目的：FN表示一个vcard对象的名称，N表示这个对象名称的组成部分\n例子：\nN:Public;John;Quinlan;Mr.;Esq.\nN:Stevenson;John;Philip,Paul;Dr.;Jr.,M.D.,A.C.P.\n各个组成部分可以用分号分号，每个组成部分可以用逗号。\nNICKNAME类型定义\n目的：表示别名\n例子：\nNICKNAME:Robbie NICKNAME:Jim,Jimmie\nPHOTO类型定义\n目的：vcard对象的图像信息\n例子：\nPHOTO;VALUE=uri:图片地址\nPHOTO;ENCODING=b;TYPE=JPEG:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN\nAQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm\nljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0\n\u0026lt;...remainder of \u0026quot;B\u0026quot; encoded binary data...\u0026gt;\n如果使用内联的二进制数据表示图片信息，那么;ENCODING=b\nBDAY类型定义\n目的：表示出生日期\n例子：BDAY:1996-04-15\nBDAY:1953-10-15T23:10:00Z\nBDAY:1987-09-27T08:30:00-06:00\n地址类型\nADR 类型定义\n目的：是一个组合，用来表示一个地址信息，值类型是一个用分号分开的文本值\n例子：ADR;TYPE=dom,home,postal,parcel:;;123 Main Street;Any Town;CA;91921-1234;A\nADR;HOME;POSTAL;PARCEL:;;街道地址;深圳;广东;444444;中国\n组合由一下部分顺序的组成：\nthe post office box;\nthe extended address;\nthe street address;\nthe locality (e.g., city);\nthe region (e.g., state or province);\nthe postal code;\nthe country name\n七个部分组成，如果，其他的一个部分没有，必须用分号分开\ntype 参数的说明\n\u0026quot;dom\u0026quot; 国内地址\n\u0026quot;intl\u0026quot; 国际地址\n\u0026quot;parcel\u0026quot;包裹递送地址\n\u0026quot;home\u0026quot; 居住地址;\n\u0026quot;work\u0026quot;工作地址;\n\u0026quot;pref\u0026quot; 有多个地址的时候，优先的地址\n缺省的\u0026quot;TYPE=intl,postal,parcel,work\u0026quot;，可以替换 LABEL类型定义\n目的：是一格式化的文本值，表示一个地址\n例子：LABEL;TYPE=dom,home,postal,parcel:Mr.John Q. Public\\, Esq.\\n\nMail Drop: TNE QB\\n123 Main Street\\nAny Town\\, CA 91921-1234\n\\nU.S.A. type 参数的说明\n\u0026quot;dom\u0026quot; 国内地址\n\u0026quot;intl\u0026quot; 国际地址\n\u0026quot;parcel\u0026quot;包裹递送地址\n\u0026quot;home\u0026quot; 居住地址;\n\u0026quot;work\u0026quot;工作地址;\n\u0026quot;pref\u0026quot; 有多个地址的时候，优先的地址\n缺省的\u0026quot;TYPE=intl,postal,parcel,work\u0026quot;，可以替换\n和ADR的不同是 ADR的值是用分号分开的数据，LABEL就是一个格式化的文本。\n电话通信地址类型\nTEL类型定义\n目的：指定一个电话号码\n例子：TEL;TYPE=work,voice,pref,msg:+1-213-555-1234\n说明：值是一个规范的全球唯一的电话号码\nTYPE参数的值有：\n\u0026quot;home\u0026quot;表示家庭电话\n\u0026quot;msg\u0026quot; 表示这个号码支持语音\n\u0026quot;work\u0026quot; 工作电话\n\u0026quot;pref\u0026quot; 表示多个电话中最喜欢使用的电话\n\u0026quot;voice\u0026quot; 声音电话号码\n\u0026quot;fax\u0026quot;传真号码\n\u0026quot;cell\u0026quot; 表示手机电话\n\u0026quot;video\u0026quot; 视频电话\n\u0026quot;pager\u0026quot; 调度电话，估计是总机的电话\n\u0026quot;bbs\u0026quot; 公开的广播系统的电话\n\u0026quot;modem\u0026quot; 调制解调器电话\n\u0026quot;car\u0026quot;汽车电话\n\u0026quot;isdn\u0026quot; ISDN连接电话号码\n\u0026quot;pcs\u0026quot; 个人通信服务电话\n缺省是 \u0026quot;voice\u0026quot;.\nTYPE参数的用法是TYPE=work;TYPE=voice或者\u0026quot;TYPE=work,voice\u0026quot;，缺省值可以被重置\n\u0026quot;TYPE=work,home,voice,fax\u0026quot;.\nEMAIL类型定义\n目的：指定一个电子邮件\n例子：EMAIL;TYPE=internet:邮箱地址\nEMAIL;TYPE=x400:邮箱地址\nEMAIL;TYPE=internet,pref:邮箱地址\nTYPE参数的使用\n\u0026quot;internet\u0026quot; 表示一个internet 类型地址\n\u0026quot;x400\u0026quot; 表示是一个 X.400 地址\n\u0026quot;pref\u0026quot;最喜欢使用的邮件电子\n缺省是\u0026quot;internet\u0026quot;.\nMAILER 类型定义\n目的：指定一个电子邮件发送者\n例子：MAILER:PigeonMail 2.1\n地理类型\nTZ类型定义\n目的：时区信息\n例子：TZ:-05:00\nTZ;VALUE=text:-05:00; EST; Raleigh/North America\n缺省是一个utc-offset值.\n*GEO类型定义\n*目的：地理位置信息\n例子GEO:37.386013;-122.082932\nCEO 经度；纬度\n组织类型\n*TITLE类型定义\n*目的：工作位置，工作职能（job title）\n例子TITLE:Director\\, Research and Development\nROLE 类型定义\n目的：公司的职业（occupation）\n例子ROLE:Programmer\nLOGO类型定义\n目的：公司logo，是一个图像信息\n例子LOGO;VALUE=uri:图片地址\nLOGO;ENCODING=b;TYPE=JPEG:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm\nljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0\n\u0026lt;...the remainder of \u0026quot;B\u0026quot; encoded binary data...\u0026gt;\n说明：TYPE知道图像的格式，ENCODING=b表示是二进制的数据流\nURI表示是一个外部图像对象\nAGENT类型定义\n目的：指定另外一个人替换他的个人行为\n例子：AGENT;VALUE=uri: CID:JQPUBLIC.part3.960129T083020.邮箱地址\nAGENT:BEGIN:VCARD\\nFN:Susan Thomas\\nTEL:+1-919-555-\n1234\\nEMAIL\\;INTERNET:主机地址\\nEND:VCARD\\n\n说明：缺省是一个Vcard对象，当时也可是一个URI指定的外部Vcard对象\nORG类型定义\n目的：表示一个组织的名称\n例子ORG:ABC\\, Inc.;North American Division;Marketing\n解释类型\n*CATEGORIES类型定义\n*目的：vcard应用的分类信息\n例子：CATEGORIES:TRAVEL AGENT\nCATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY\nNOTE 类型定义\n目的：对vcard的注释和说明\n例子：NOTE:This fax number is operational 0800 to 1715\nEST\\, Mon-Fri.\nPRODID类型定义\n目的：指定创建Vcard对象的产品的ID\n例子：PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN\nREV类型定义\n目的：指定当前Vcard的修改信息\n例子：REV:1995-10-31T22:27:10Z\nREV:1997-11-15\nSORT-STRING类型定义\n目的：指定家庭名称或者其他名称对FN和N类型排序\n例子： FN:Rene van der Harten\nN:van der Harten;Rene;J.;Sir;R.D.O.N.\nSORT-STRING:Harten\nFN:Robert Pau Shou Chang\nN:Pau;Shou Chang;Robert\nSORT-STRING:Pau\nFN:Osamu Koura\nN:Koura;Osamu\nSORT-STRING:Koura\nFN:Oscar del Pozo\nN:del Pozo Triscon;Oscar\nSORT-STRING:Pozo\nFN:Chistine d'Aboville\nN:d'Aboville;Christine\nSORT-STRING:Aboville\nSOUND类型定义\n目的：指定Vcard的数字声音信息，缺省是指定vcard的name类型的发音信息。\n例子： SOUND;TYPE=BASIC;VALUE=uri:CID:JOHNQPUBLIC.part8.\n19960229T080000.地址\nSOUND;TYPE=BASIC;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcN AQEEBQAwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENvbW11bm ljYXRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0 \u0026lt;...the remainder of \u0026quot;B\u0026quot; encoded binary data...\u0026gt;\n可以用URI指定一个外部声音二进制流\n可以是内置的声音二进制流，ENCODING=b\nUID类型定义\n目的：指定一个全球唯一的个人或资源标识。\n例子：UID:19950401-080045-40000F192713-0052\nURL类型定义\n目的：指定Vcard 参考的路径。\n例子：URL: 网址\nVERSION类型定义\n目的：指定Vcard使用的vcard规范的版本。\n例子： VERSION:3.0\nSecurity 类型\nCLASS类型定义\n目的：指定访问Vcard对象的访问分级。\n例子： CLASS:PUBLIC\nCLASS:PRIVATE\nCLASS:CONFIDENTIAL\n说明：安全分级需要参考目录服务的访问分级\nKEY 类型定义\n目的：指定Vcard的公共钥匙值（加密解密是使用）或者是授权认证。\n例子： KEY;ENCODING=b:MIICajCCAdOgAwIBAgICBEUwDQYJKoZIhvcNAQEEBQA\nwdzELMAkGA1UEBhMCVVMxLDAqBgNVBAoTI05ldHNjYXBlIENbW11bmljYX\nRpb25zIENvcnBvcmF0aW9uMRwwGgYDVQQLExNJbmZvcm1hdGlvbiBTeXN0\nZW1zMRwwGgYDVQQDExNyb290Y2EubmV0c2NhcGUuY29tMB4XDTk3MDYwNj\nE5NDc1OVoXDTk3MTIwMzE5NDc1OVowgYkxCzAJBgNVBAYTAlVTMSYwJAYD\nVQQKEx1OZXRzY2FwZSBDb21tdW5pY2F0aW9ucyBDb3JwLjEYMBYGA1UEAx\nMPVGltb3RoeSBBIEhvd2VzMSEwHwYJKoZIhvcNAQkBFhJob3dlc0BuZXRz\nY2FwZS5jb20xFTATBgoJkiaJk/IsZAEBEwVob3dlczBcMA0GCSqGSIb3DQ\nEBAQUAA0sAMEgCQQC0JZf6wkg8pLMXHHCUvMfL5H6zjSk4vTTXZpYyrdN2\ndXcoX49LKiOmgeJSzoiFKHtLOIboyludF90CgqcxtwKnAgMBAAGjNjA0MB\nEGCWCGSAGG+EIBAQQEAwIAoDAfBgNVHSMEGDAWgBT84FToB/GV3jr3mcau\n+hUMbsQukjANBgkqhkiG9w0BAQQFAAOBgQBexv7o7mi3PLXadkmNP9LcIP\nmx93HGp0Kgyx1jIVMyNgsemeAwBM+MSlhMfcpbTrONwNjZYW8vJDSoi//y\nrZlVt9bJbs7MNYZVsyF1unsqaln4/vy6Uawfg8VUMk1U7jt8LYpo4YULU7\nUZHPYVUaSgVttImOHZIKi4hlPXBOhcUQ==\n扩展类型 可以定义自己的类型，自己定义的类型需要以“x-”开头\n比喻例子中的信息：\nX-QQ:000000\nX-ICQ:icq\nX-WAB-GENDER:2\n","date":"2009-03-24","description":"","lastmod":"2009-03-24T03:40:15Z","slug":"695","tags":["vcard"],"title":"VCard 2.1（rfc－2426）标准通信薄基本格式","url":"https://blog.zengrong.net/post/695/"},{"categories":["news"],"content":"本文原始出处：http://www.graphicfetish.com/28-fantastic-generators-for-easier-web-designing/\n随着许多优秀的网页设计工具和大量资源的出现，今天网页设计比起十年前要简便得多。本文收集了28款可以帮助你快速设计网页的自动生成工具，包括了logo，banner、Web2.0徽标生成器，域名推荐。\n1. Lorem Ipsum Generator 文本排版生成器\n2. Stripe Generator 条纹背景\n3. Mycoolbutton 按钮生成器\n4. Web20generator Web2.0生成器\n5. Buttonator 按钮生成器\n6. VectorMagic 矢量图生成器\n7. Free Online Web Template Generator 免费在线网页模板\n8. CSS Type Set CSS效果查看\n9. Free Logo Design – Logo Maker 免费Logo设计\n10. Tiled backgrounds designer 背景设计\n11. Web 2.0 logo creatr Web2.0 logo生成器\n12. Nameboy 域名搜索和建议\n13. Domain name generator 域名生成器\n14. Online Logo Generator 在线logo生成器\n15. Loona Pix 特效图片生成器\n16. Fancy text generator 文本特效生成器\n17. WebscriptlabWeb脚本生成器\n18. Form Generator 表单生成器\n19. ReflectionMaker.com 图片倒影生成器\n20. XHTML/CSS Markup Generator XHTML/CSS标签生成器\n21. Roundedcornr 圆角图片效果生成器\n22. favicon.cc favicon生成器\n23. Genfavicon favicon生成器\n24. stripedesigner.com 条纹背景生成器\n25. 3D Pack 3D图片效果\n26. CSS Text Wrapper CSS特殊文字形状\n27. Favicon Generator Favicon生成器\n28. Web 2.0 Badges Web2.0徽章生成器\n","date":"2009-03-24","description":"","lastmod":"2009-03-24T00:58:39Z","slug":"688","tags":["css","generator","html"],"title":"【转】快速设计网页必备的28款工具和生成器","url":"https://blog.zengrong.net/post/688/"},{"categories":["news"],"content":"转自Brian Sun @ 爬树的泡泡\n最近一不小心陷入了对第二次浏览器大战的深思，并参与了几次网上讨论。颇有心得，不知何处分享。下面要讲的四个故事，都是客户（或者你的雇主）对你我（这样的开发人员）讲的故事。来源于生活，高于生活。\n1）\n你受雇于Z商银行专业版开发团队，不久结识了负责专业版客服的漂亮MM。爱情的滋味让你忘记了工作的烦恼，但是你却清楚的记得那一天走进心仪MM的办公室，她的老板正在抱怨客户的投诉。原来很多江浙一带的有钱小老板，一直是Z商银行的忠实客户，可是人有钱就有了精神追求，他们这两年纷纷配备了Mac的本本，有白色的有银色的。。。明白了吧？！现在他们的钱都转存至浦东发展银行和深圳发展银行了。\n数据显示，MacBook和MacBook Pro在中国的销量每年都在高速增长，这还没有统计从香港出货而最终用户是大陆人的数量。Mac上人们常用两个浏览器，Safari和 Firefox，Safari非常酷，而且对标准的支持非常好，Firefox插件多，安全性好。最重要的是，如果你已经具有开发IE Web App的能力，那么就已经具备开发标准Web App的能力了。只要有心，万事不难。微软自己也不再喜欢ActiveX了，他们经常游说你们团队用Silverlight。这次，你的老板还会上当吗？千万别忘了，这还是微软的私有技术，即使他们承诺Silverlight将跨平台，你也会觉得难用无比，别忘了他们曾经做过让网页设计师谈虎色变的IE for Mac。\n前事不忘后事之师。我们能有今天的幸福生活，一要感谢党，二要感谢开放的国际互联网。HTML5，CSS3，以及其它的W3C标准共同组成了我们通常所说的Open Web。很多人误解了，以为ActiveX是Web安全性的不二选择。其实安全性向来都是开放平台、开放架构、开放源代码远胜于专有平台、专有架构、专有技术。HTTPS已经非常安全了。选择Open Web之后的最重要工作，就是让每个程序员都有一颗安全性第一的心。\n到了晚上，MM完全没有约会的心情了，她一个劲的问你，这两个银行很小啊，根本没有什么研发力量，为什么他们能做的事情，你们不能做？这怎么办？你又不能认错又不能让MM对公司失去信心，虽然她的信心已经失去了，虽然这根本不是你的错。憋了半天，你只能来一句：“他们不主流，我们不标准。”\n2）\n你大学毕业不到5年，与朋友一起创业做Web 2.0网站。专家给你们团队的建议是：用户第一，理念第二，技术第三。你非常赞同这一说法。作为主力开发，你非常希望产品能带给用户不一样的感觉，让用户在第一次就记住这个网站，而不是在浏览器还没有渲染完页面时就关闭页签。你非常羡慕Google的很多应用，简约而不简单，易用性强，速度快。\n作为Web开发的老手（还不敢称专家），让我来给你一些建议。第一，你要坚持在团队中宣扬少用图片的口号。第二，使用更多的Ajax异步装载，不断提升性能，优化用户体验。第三，大量用客户的计算资源，（反正客户的资源丰富而且几乎无成本），减少服务器的压力。第四，宣扬瘦服务器的理念，因为公司小，业务变化很快，投入服务器的资源不容易变换，如果设计成瘦服务器，成本投在客户端，因为客户端环境单一，全部重来也没有多少成本，容易随需应变。好，我们首先假设这些建议你已经接受了。\n接下来，5年从未遇到的问题来了。由于客户端代码越来越多，越来越复杂，影响了项目进度。老板开始质疑你的这种方式是否可取。再有，公司重金请人做网页设计，结果你总是提减少图片，设计师不悦，常常产生私人恩怨。更麻烦的是，连你自己都开始担心，因为前端代码常有bug，导致IE弹出对话框，打破了你一直的梦想。\n好吧，既然你能看到这里，我打算教你点绝活。在IE中使用直角矩形，而在Firefox/Safari/Chrome中使用圆角矩形。如果你的设计师喜欢圆角矩形，就给他/她看Safari中的效果，设计师都喜欢Safari，如果他/她喜欢直角，就给看IE！因为在Firefox/Safari /Chrome中，圆角可以用CSS实现，完全没有贴图。这一招还要用在阴影上。我太太最近还在跟我炫耀她能用CSS3直接实现雕刻字体效果，不过也不是在IE中。至于Ajax等开发的复杂性，建议你常年使用jQuery和jQuery插件。这个系统的所有API都是跨浏览器的，零学习成本，会用 Javascript就会用，零host成本，因为Google帮你host。这样你可以在Firefox上用Firebug开发，或者在Safari 4里开发（透露一下，Safari 4的调试功能真的超级棒），然后在IE上跑，没有任何问题。2008年年初的调查，jQuery市场占有率不足20%，年末已经过70%了，所以我常把 2008年称为jQuery年，这是国际开发者社群的共同选择。\n很多开发人员拒绝非IE浏览器，是因为他们害怕浏览器间的差异，带来开发成本，尤其是CSS上的差异很大。不过别忘了，资本主义世界只要有利润，人们就会勇往直前。那么多大公司（包括微软）都在奋发图强开发Web App，包括IBM的Lotus产品线，你遇到的难题，别人都遇到过。我们搞开发的就是要站在Google/IBM/Apple这些大公司的肩膀上。\n在感受了一次成功的喜悦后，绝大部分开发人员还是会相信：支持非IE浏览器，俺，能跑！！\n3）\n你大学毕业就进入了一家大型软件公司工作，ERP/CRM是公司的主要产品线类型，工资和福利是你与同龄人相比的骄傲，虚荣心是你参加同学聚会的动力。不久你升任产品经理，前途似锦。但困扰你的是每次与客户面对面，客户都对你的个人魅力毫无兴趣，而是反反复复的提一个字：“省”！\n你花了一个月的时间和客户吃住在一起，通过IT部门了解客户的IT支出到了哪里？尽管如此，客户还是希望你能够拿出一个方案让他们在2009～2010年经济危机的时候每年节省几百万。怎么办？把客户的员工裁了？还是把你裁了？减少买你们公司软件的支出？（这两年你们公司也不好过，这跟把你裁了是一样的。）还是减少。。。等等。。。硬件？对，硬件！\n一方面，如果客户对新员工和需要更换的笔记本采用Netbook，就可以节约大量成本。另一方面，如果采用云计算的产品，可以把需要支出的服务器端软件改为租用方式，用浏览器访问。两个方面前后一致且顺理成章。Netbook之所以存在市场就是因为大量的软件应用由Web App取代。新员工只要有浏览器用就可以访问ERP/CRM这些你们公司的拳头产品。\n好，于是你再次向客户提出了这10年来全球的IT大公司（除微软）不断向客户提出的建议：Linux。\n今天就是尝试Linux的最佳时间。Dvorak，这位以批判大公司出名的IT评论家，现在建议每个人都要尝试一下Ubuntu。真的很好用。你可以不相信我但是不能不相信Dvorak，他从不盲目追随新技术，加上年龄的原因，他总是比我们这些年轻人对新事物更抵触。据说俄罗斯的国立中小学已经全部是 Linux教学了。你心里的小算盘开始响了，如果每位员工减少本本方面的2000块支出，几百万不在话下。\n如果被客户问及Ubuntu不好用怎么办？你嘴角微微一笑，没关系，反正客户大部分时间都只干三件事：聊天、看电影，用办公软件。这些都是Ubuntu的强项。聊天？QQ和MSN、Skype都支持。看电影，有跟暴风影音一样强的（还不止一个）。办公，OpenOffice存取MS Office格式照常使用。至于你的软件，早就Firefox罗。\n怎么？客户还要培训？你挣钱的时候到了。Windows的钱都省了，出点服务费咋了？\n4）\n年过30的你急于在事业上攀到新的高峰，才能给妻子和正要上小学的孩子一个交代。天赐良机你被一个中型企业挖去做CTO，而你的老板，则是个精明的美国商人。（怎么听起来像小说《最后期限》？）为了拿到VC的钱，老板要求你夜以继日的扩大用户群。怎么办？八仙过海。\n大量的软件公司正在尝试把他们最挣钱的产品移植到Web上。这种趋势从2004年就已经开始了。不过用户还是不太喜欢Web Service或者SaaS这样的技术术语，比较讨用户喜欢的概念是“云计算”。然而金融危机到来，VC变得非常谨慎，不见兔子不撒鹰，除了大规模用户数量的增长，其余免谈。在这个冬季临危受命的你，光靠国内市场是显然不够的。加上笃信《世界是平的》，你决定到全球市场去碰碰运气。无论你是否相信，只要支持中英两种文字，就支持了全球半数以上的网民。你六级不都过了吗，这有什么难的？\n想象一下很多厂商靠iPhone和Android活着。如果让你选择一个作为平台，你愿意选哪一个？呵呵。你猜我选哪一个？Both！其实很多人不知道，如果做了iPhone的Web App就等于做了Android的Web App，他们是完全相同的浏览器内核Webkit。不相信？那你看看iPhone上的Gmail和Google Reader，是不是跟Android一模一样？以前是不是一直以为Google做了iPhone版？上当了，Google并未用Apple的风格，而是使用自己的风格，但是看起来易用性一点也不输给本地iPhone应用。更多的喜讯还在后面，Nokia S60也是Webkit，Blackberry上也有Webkit了，Gnome的缺省浏览器很快也是Webkit了。。。\n所以，坚持所有Web产品都支持Safari/Chrome的开发团队，获得了史上从未有过的光荣。这不仅是来自VC的青睐，也是直接来自客户的认同，更是钱在向你招手。。。（抱歉我总是想着钱，可谁不是呢？）。。。\nFirefox呢？也是不可多得的marketing阵地！因为Firefox插件多，而且非常容易开发。很多小公司靠这个活着呢。因为Firefox的用户基数大（比IE7大），所以一点细小的易用性改进都能吸引大量用户的眼球。这么多人痴迷与Facebook App和开心App，也是同一个道理。\n所以，我们的口号是：支持非IE浏览器，他好，你也好！用户好，投资人好，老板好。大家好，才是真的好！（怎么样？被我雷死了吧。）\n结语）\n还是前面说的那句话，用户第一，理念第二，技术第三。不要为你的技术找借口。更好的为用户服务就是你的使命。在世界平坦化的今天，把国人的聪明才智展现于世界舞台就是你的机遇。努力的去做吧。正所谓你不下地狱谁下地狱！\n欢迎大家续写这些故事和添加故事。谢谢！\n","date":"2009-03-24","description":"","lastmod":"2009-03-24T00:50:43Z","slug":"686","tags":["ie"],"title":"【转】他们不主流，我们不标准——开发人员为什么要支持非IE浏览器的四个故事","url":"https://blog.zengrong.net/post/686/"},{"categories":["use"],"content":"OpenOffice Write的首行缩进是没有“字符”这个单位的，只有厘米，怎么把我常用的四号、小四号换算成厘米呢？\n看看下面的对应关系就清楚了：\n“磅数制”对字的大小以点（Point）来计量。各个国家对其大小规定不尽相同，英美等国规定1磅为0.35146mm，我国为0.35mm。\n1磅=0.35mm\n1mm=2.84磅\n磅和字号的关系：\n磅 字号 5 八号 5.5 七号 6.5 小六号 7.5 六号 9 小五号 10.5 五号 12 小四号 14 四号 15 小三号 16 三号 18 小二号 22 二号 24 小一号 26 一号 36 小初号 42 初号\n另外，还找到一篇CSS的em、px、pt长度单位转换\n","date":"2009-03-19","description":"","lastmod":"2009-03-19T08:03:28Z","slug":"678","tags":["office"],"title":"OpenOffice首行缩进2字符的设置—字号、磅、厘米的关系","url":"https://blog.zengrong.net/post/678/"},{"categories":["others"],"content":"我是一个很专一的人，我的专一取决于我专一的对象。一旦有比它更好的替代者，我自然就会对这个替代者专一了。\n现在我准备改变我对搜狗拼音输入法的专一了，我的输入法列表中多了一个输入法：QQ拼音输入法，而且顺序排在搜狗之上。\n在拼音输入法上（对于一个拼音输入速度在140左右的人来说，五笔基本上可以直接无视了 :shock: (五笔支持者你可以说我不思进取，坐井观天，但我不是打字员)），我常用的输入法历程是：\n智能ABC→考拉输入法→紫光拼音输入法→拼音加加输入法→搜狗拼音输入法→QQ拼音输入法\n我尝试过的拼音输入法是：新华拼音输入法、三好拼音输入法、龙文输入平台、谷歌拼音输入法\n搜狗拼音输入法，我从第一个公测版用到现在，已经有2年多的时间了，期间也做过搜狗拼音的皮肤。它红火了很长一段时间，一度在我的电脑中占据唯一的一个输入法位置（其他的输入法全部删掉），并引起了谷歌、腾讯的竞相模仿。在我看来，有竞争自然是好事。除了不思悔改的微软拼音输入法（连个逗号句号翻页都不支持）、廉颇老矣的智能ABC，还有谁能和搜狗拼音输入法抗衡呢？拼音加加无视用户的体验，用户呼声极高的繁体输入功能一直都不添加，结果搜狗一出，它马上就加入了繁体输入，这不是找抽么……最终还是直接被用户无视掉；曾经是一方霸主的紫光，名称换来换去，换到最后还是被我换掉；谷歌拼音输入法一直都不争气，不在易用性上面下功夫，还经常和自家的google桌面冲突…… 其他的，不提也罢了。\n搜狗确实不错，一直无人可比，直到腾讯开始做输入法。\n其实，当前的搜狗，QQ输入法还是比不上，搜狗的智能人名模式、细胞词库、皮肤功能，QQ拼音还需要一段时间的追赶，但很多功能，其实我是不需要的或不那么需要的。皮肤这玩意儿是90后小女孩用的，而常用的人名，我早就收录到自己的词库中了。让我改变“专一”目标的，是细节。\n细节1：双拼下的U模式和V模式 搜狗拼音和QQ拼音都支持双拼下的U模式和V模式，但搜狗中双拼下的U模式和V模式有较多问题，下面是我在搜狗拼音输入法论坛上的问题反映贴中的内容\n双拼模式下，因为shift+U和shift+V是开启模式的快捷键，因此要输入URL和UTF等短语的时候，u始终是保持小写状态。其实大可不必这么做。如果用户确实要使用u模式，那么选择了u模式之后，一定不会继续输入大写的字符，因此可以判断如果用户u之后第二个字母仍是大写，就应该将u改为大写。\n但若这样处理，仍然没有解决输入单独的大写u和大写v的问题。\nQQ拼音输入法的U模式和V模式也采用同样的方式支持，就没有这个问题\n细节2：繁体模式 工作原因，经常要使用繁体输入模式，但搜狗拼音输入法开启繁体输入模式之后，如果关闭了输入法，再次开启的时候，就会自动换成简体模式，这就造成了不便。而QQ拼音输入法则会自动记忆上次的模式。\n老实说，腾讯的许许多多做法，确认很霸道，也很无赖。看见千千静听做播放器挺滋润，就做个QQ音乐；看见稀烂的暴风影音不行了，就做个QQ影音帮它送终；看见钻石能卖钱，就做黄钻绿钻红钻黑钻透明钻……看见衣服也能卖钱，就强行把我脱得只剩内裤；看见人民币升值，就做Q币；看见奇瑞做汽车，就…… 唔 扯远了……\n但我不得不承认，QQ拼音就是TMD比搜狗好用那么一点点\n就这么一点点，已经足够了。\n其实，我是一个很专一的人。\n2009年03月12日 09:31:43更新\n最新发布的4.1论坛预览版已经解决了“细节1”描述的问题，但“细节2”描述的问题仍然没有解决。\n2009年03月18日 21:34:45更新\n最新发布的4.1正式版已经解决了“细节1”描述的问题，但“细节2”描述的问题仍然没有解决。\n","date":"2009-03-09","description":"","lastmod":"2009-03-09T15:01:03Z","slug":"663","tags":["ime"],"title":"离开搜狗拼音，投奔QQ拼音","url":"https://blog.zengrong.net/post/663/"},{"categories":["tutorial"],"content":" 1 文件 转自\n中文目录：\n第一章. Flex和ActionScript基础 (常青)\n1.1节. 用Flex Builder创建Flex项目\n1.2节. 用Flex Builder创建Flex库项目\n1.3节. 创建ActionScript项目\n1.4节. 在Flex Builder中设置MXML编译器参数\n1.5节. 在Flex Builder外编译Flex项目\n1.6节. 在MXML中添加事件\n1.7节. 设置MXML的子节点属性\n1.8节. 定义对象数组\n1.9节. 在ActionScript中设置变量作用域\n1.10节. 在ActionScript中创建组件\n1.11节. 使用事件冒泡机制\n1.12节. 使用代码隐藏模式分离MXML和ActionScript\n1.13节. 组件属性绑定\n1.14节. 使用自定义事件以及事件数据传递\n1.15节. 监听键盘事件\n1.16节. 定义方法参数\n1.17节. 检测对象数据类型\n1.18节. 接口的定义和实现\n第二章. 控件与菜单 (Native|eas)\n2.1节. 监听按钮点击\n2.2节. 创建一组状态按钮\n2.3节. 使用ColorPicker设置Canvas颜色\n2.4节. 通过SWFLoader载入SWF\n2.5节. 设置组件标签索引\n2.6节. 设置控件的labelFunction\n2.7节. 提供菜单数据\n2.8节. 动态填充菜单\n2.9节. 为菜单类控件创建事件处理函数\n2.10节. 显示一个通知窗口\n2.11节. 使用Calendar控件数据\n2.12节. 弹出式窗口的显示与位置\n2.13节. 自定义弹出式窗口边框\n2.14节. 处理focusIn和focusOut事件\n第三章. 容器 (Nigel)\n3.1节. 使用布局管理器管理容器子节点位置\n3.2节. 用百分比设置容器的大小和位置\n3.3节. 用不同的坐标系统跟踪鼠标位置\n3.4节. 动态添加和删除容器子节点\n3.5节. 使用容器的约束布局\n3.6节. 设置容器子节点尺寸的最大值和最小值\n3.7节. 设定容器的约束行和约束列\n3.8节. 使用约束器为文本框创建布局流程\n3.9节. 控制容器内的滚动和溢出\n3.10节. 控制Box组件的布局\n3.11节. 容器的初始化\n3.12节. 创建TitleWindow\n3.13节. 用LinkBar控制ViewStack\n3.14节. 绑定ViewStack的当前索引到变量上\n3.15节. 使用延迟实例化来提高启动时间\n3.16节. 创建和控制可变大小的容器\n3.17节. 创建，启动和禁止TabNavigator的TabControls\n3.18节. 创建带关闭标签的TabNavigator\n3.19节. 创建和控制一个通知窗口\n3.20节. 基于调用其组件的对话框大小和位置\n3.21节. 管理弹出式对话框\n3.22节. 滚动到容器内的指定子节点\n3.23节. 使用IDeferredInstance创建一个模板\n3.24节. 手动布局容器\n3.25节. 测量并通知容器大小\n3.26节. 控制子节点的可视化和布局\n3.27节. 使用简单的重组创建一个平铺容器\n3.28节. 设置HBox的背景和圆角\n3.29节. 控制子组件的位置和滚动\n第四章. 文本 (ω草·衣·薰)\n4.1节. 正确设置Text对象的值\n4.2节. 绑定一个数值到TextInput上\n4.3节. 创建一个带自动提示的TextInput\n4.4节. 创建一个自适应编辑器\n4.5节. 检测用户电脑上已安装的所有字体\n4.6节. 创建自定义TextInput\n4.7节. 设置一段文本的Style属性\n4.8节. 在Html中显示图像和SWF\n4.9节. 在搜索域中高亮显示用户输入的文本\n4.10节. 操作字符作为各自图形\n4.11节. 为TextField中的HTML指定样式\n4.12节. 使用RichTextEditor\n4.13节. 在HTML中嵌入字体\n4.14节. 为Text组件的文本添加阴影\n4.15节. 找出TextArea中最后被显示的字符\n第五章. Lists，Tiles和Trees (桃之夭夭)\n5.1节. 创建一个可编辑的List\n5.2节. 为List的某项设置图标\n5.3节. 为List的内容变更添加特效\n5.4节. 为TileList创建一个项渲染器\n5.5节. 为Tree设置XML数据\n5.6节. 为Tree创建项渲染器\n5.7节. 在Tree控件中使用复杂数据对象\n5.8节. 只允许List的某一项可被选中\n5.9节. 为List的项编辑器添加格式化和验证\n5.10节. 跟踪TileList中所以被选中的子节点\n5.11节. 使用和显示项渲染器的NULL项\n5.12节. 为List创建右键菜单\n5.13节. 自定义List被选中项的外观\n第六章. DataGrid和高级DataGrid (常青)\n6.1节. 为DataGrid创建自定义列\n6.2节. 为DataGrid列设定排序函数\n6.3节. 启动多列排序\n6.4节. 过滤项\n6.5节. 为AdvancedDataGrid创建自定义表头\n6.6节. 处理事件\n6.7节. 选择项\n6.8节. 启动DataGrid拖拽功能\n6.9节. 编辑DataGrid某一项\n6.10节. 在DataGrid中搜索并自动滚动到匹配项\n6.11节. 使用群集对数据进行总结\n6.12节. 为群集创建一个异步刷新\n第七章. 渲染器和编辑器 (王平)\n7.1节. 创建自己的渲染器\n7.2节. 使用ClassFactory生成渲染器\n7.3节. 访问设置自己渲染器的组件\n7.4节. 创建一个简单的组件作为渲染器和编辑器\n7.5节. 创建一个项编辑器来出来多个域的数据\n7.6节. 使用项渲染器把SWF对象作为一个菜单项显示\n7.7节. 用一个复选框渲染器选择DataGrid列\n7.8节. 为DataGrid创建一个独立的复选框项渲染器\n7.9节. 为渲染器设置高效图像\n7.10节. 为项渲染器和项编辑器应用运行时样式\n7.11节. 为项编辑器应用状态和变换\n7.12节. 创建一个带复选框的Tree控件\n7.13节. 改变List中的渲染器大小\n第八章. 图像，位图，视频和声音 (ken)\n8.1节. 载入并显示图像\n8.2节. 创建视频显示\n8.3节. Mp3文件的播放和暂停\n8.4节. 为音频文件创建进度搜索条\n8.5节. 融合两幅图像\n8.6节. 将Convolution滤镜应用于图像\n8.7节. 通过摄像头将视频发送到FMS实例\n8.8节. 访问用户的麦克风并创建声音显示\n8.9节. 在Flex程序中流畅播放视频\n8.10节. 检测像素级别的碰撞\n8.11节. 读取和保持用户的网络摄像头图像\n8.12节. 在多幅图像中使用混合模式\n8.13节. 处理FLV数据的提示点\n8.14节. 创建视频播放进度条\n8.15节. 读取mp3文件的ID3数据\n8.16节. 在载入图像时显示自定义引导\n8.17节. 启动图像上传\n8.18节. 比较两幅位图\n第九章. 皮肤与样式 (屋檐下)\n9.1节. 用CSS定义组件样式\n9.2节. 覆盖程序默认样式\n9.3节. 嵌入CSS样式\n9.4节. 覆盖通用样式属性\n9.5节. 在运行时自定义样式\n9.6节. 在运行时载入CSS\n9.7节. 在运行时申明样式\n9.8节. 自定义组件样式属性\n9.9节. 在同一个程序中使用多个主题\n9.10节. 编译主题SWC\n9.11节. 使用嵌入字体\n9.12节. 在SWF文件中嵌入字体\n9.13节. 嵌入图像的皮肤\n9.14节. 在SWF文件中嵌入皮肤\n9.15节. 编程实现组件皮肤\n9.16节. 编程实现状态控件的皮肤\n9.17节. 创建动态皮肤\n9.18节. 自定义引导界面\n第十章. 拖拽操作 (小河)\n10.1节. 使用DragManager类\n10.2节. 指定一个Drag代理\n10.3节. 在List内部进行拖拽操作\n10.4节. 在List之间进行拖拽操作\n10.5节. 启动和禁止拖操作\n10.6节. 自定义列表类控件的拖动图像\n10.7节. 自定义列表类控件的拽动指示器\n第十一章. States(状态) (常青)\n11.1节. 设置State的样式和属性\n11.2节. 创建Transitions(转换)用于进入或退出States\n11.3节. 使用AddChildAction和RemoveChildAction\n11.4节. 过滤Transitions以便作用于某种类型的子节点\n11.5节. 应用一部分Transition到某个子节点\n11.6节. 建立在基本State上的State\n11.7节. 通过HistoryManagement整合视图States\n11.8节. 使用States延时实例化代理\n11.9节. 在State中使用数据绑定\n11.10节. 在State改变中添加和删除事件监听器\n11.11节. 添加视图States到Flash组件上\n11.12节. 处理State Change事件\n11.13节. 动态生成并使用新的States和Transitions\n11.14节. 在State中创建自定义行为\n第十二章. 特效(Effects) (Flexer:Nigel)\n12.1节. 在MXML和ActionScript中调用一个特效\n12.2节. 构建自定义特效\n12.3节. 创建平时执行序列和顺序执行序列的特效\n12.4节. 暂停，反向和重启特效\n12.5节. 创建自定义特效触发器\n12.6节. 创建渐变特效\n12.7节. 使用DisplacementMapFilter滤镜\n12.8节. 创建AnimateColor特效\n12.9节. 使用卷积滤镜创建一个渐变效果\n第十三章. 集合 (常青)\n13.1节. 添加，排序和反转ArrayCollection数据\n13.2节. 过滤ArrayCollection\n13.3节. 判定何时ArrayCollection的某数据项被修改\n13.4节. 创建一个GroupingCollection\n13.5节. 为控件创建一个分层的数据供应器\n13.6节. 导航集合对象并保持当前位置\n13.7节. 创建HierarchicalViewCollection对象\n13.8节. 过滤和排序XMLListCollection\n13.9节. 根据多个字段排序\n13.10节. 根据日期排序\n13.11节. 创建一个ArrayCollection的深度复制\n13.12节. 通过唯一的ID使用数据对象\n第十四章. 数据绑定 (Roast)\n14.1节. 绑定到属性\n14.2节. 绑定到函数\n14.3节. 创建双向绑定\n14.4节. 使用ActionScript绑定到属性\n14.5节. 使用可绑定的属性链\n14.6节. 使用E4X绑定XML到属性上\n14.7节. 创建自定义可绑定属性\n14.8节. 绑定到一个通用对象\n14.9节. 在动态类上榜的属性\n第十五章.验证，格式化和正则表达式 (tonyian)\n15.1节. 使用在TextInput和TextArea控件上使用验证器和格式化器\n15.2节. 创建自定义格式化器\n15.3节. 通过正则表达式创建国际邮政编码验证器\n15.4节. 创建一个验证器验证UPCs\n15.5节. 验证组合框和一组单选框\n15.6节. 在表单中通过TooTips显示验证错误信息\n15.7节. 使用正则表达式定义Email地址\n15.8节. 使用正则表达式匹配信用卡号码\n15.9节. 使用正则表达式验证ISBNs\n15.10节. 通过显示字符类创建正则表达式\n15.11节. 在正则表达式中使用字符类型\n15.12节. 使用子表达式验证合法的IP地址\n15.13节. 使用正则表达式为不同类型进行匹配\n15.14节. 使用正则表达式匹配行开头和结尾\n15.15节. 使用逆向引用\n15.16节. 使用正向查找和逆向查找\n第十六章.图表 (Na)\n第十七章.共享对象 (Native|eas)\n第十八章.数据服务和服务端通信 (Native|eas)\n第十九章.XML (tonyian)\n第二十章.与浏览器通信 (Nigel)\n第二十一章.开发策略 (ASer@欢乐学)\n第二十二章.Modules和RSL (常青)\n第二十三章.AIR (常青)\n第二十四章.单元测试之FlexUnit (常青)\n第二十五章. 编译和调试 (ASer@欢乐学)\n第二十六章. 配置，国际化和打印\n英文目录：\nChapter 1. Flex and ActionScript Basics\nRecipe 1.1. Create a Flex Project in Flex Builder\nRecipe 1.2. Create a Flex Library Project in Flex Builder\nRecipe 1.3. Create an ActionScript Project\nRecipe 1.4. Set Compiler Options for the MXML Compiler in Flex Builder\nRecipe 1.5. Compile a Flex Project Outside of Flex Builder\nRecipe 1.6. Add an Event Listener in MXML\nRecipe 1.7. Set Properties of a Child Defined in MXML in ActionScript\nRecipe 1.8. Define Arrays and Objects\nRecipe 1.9. Set the Scope of Variables in ActionScript\nRecipe 1.10. Create a Component in ActionScript\nRecipe 1.11. Use Event Bubbling\nRecipe 1.12. Use a Code-Behind Model to Separate MXML and ActionScript\nRecipe 1.13. Make Properties of a Component Bindable\nRecipe 1.14. Use Custom Events and Dispatch Data with Events\nRecipe 1.15. Listen for a Keyboard Event\nRecipe 1.16. Define Optional Parameters for Methods\nRecipe 1.17. Determine the Type of an Object\nRecipe 1.18. Define and Implement an Interface\nChapter 2. Menus and Controls\nRecipe 2.1. Listen to a Button Click\nRecipe 2.2. Create a Set of Buttons That Toggle\nRecipe 2.3. Use a ColorPicker to Set Canvas Color\nRecipe 2.4. Load a SWF by Using the SWFLoader\nRecipe 2.5. Set Tab Indexes for Components\nRecipe 2.6. Set a labelFunction for a Control\nRecipe 2.7. Provide Data for Menus\nRecipe 2.8. Dynamically Populate Menus\nRecipe 2.9. Create EventHandlers for Menu-Based Controls\nRecipe 2.10. Display an Alert in an Application\nRecipe 2.11. Use the Date from a Calendar Control\nRecipe 2.12. Display and Position Multiple Pop-ups\nRecipe 2.13. Create a Custom Border for a Pop-up Window\nRecipe 2.14. Handle focusIn and focusOut Events\nChapter 3. Containers\nRecipe 3.1. Position Children by Using Layout Management\nRecipe 3.2. Position and Size Containers via Percentage Positioning\nRecipe 3.3. Track Mouse Position Within Different Coordinate Systems\nRecipe 3.4. Dynamically Add and Remove Children from a Container\nRecipe 3.5. Use Constraint-Based Layout for Containers\nRecipe 3.6. Set Maximum and Minimum Sizes for Children Within Containers\nRecipe 3.7. Specify Constraint Rows and Columns for a Container\nRecipe 3.8. Create Layout Flows for Text Using Constraints\nRecipe 3.9. Control Scrolling and Overflow Within Containers\nRecipe 3.10. Control the Layout of Box Components\nRecipe 3.11. Use Containers for Initialization\nRecipe 3.12. Create a TitleWindow\nRecipe 3.13. Control a ViewStack via a LinkBar\nRecipe 3.14. Bind the Selected Index of a ViewStack to a Variable\nRecipe 3.15. Use Delayed Instantiation to Improve Startup Time\nRecipe 3.16. Create and Control Resizable Containers\nRecipe 3.17. Create, Enable, and Disable TabControls Within a TabNavigator\nRecipe 3.18. Create a TabNavigator with Closeable Tabs\nRecipe 3.19. Create and Control an Alert\nRecipe 3.20. Size and Position a Dialog Box Based on Its Calling Component\nRecipe 3.21. Manage Multiple Pop-up Dialog Boxes\nRecipe 3.22. Scroll to a Specific Child in a Container\nRecipe 3.23. Create a Template Using IDeferredInstance\nRecipe 3.24. Manually Lay Out a Container\nRecipe 3.25. Measure and Alter Container Size\nRecipe 3.26. Control the Visibility and Layout of Children\nRecipe 3.27. Create a Tile Container with Simple Reorganization\nRecipe 3.28. Set a Background Image and Rounded Corners in an HBox\nRecipe 3.29. Control Positioning and Scrolling of Child Components\nChapter 4. Text\nRecipe 4.1. Correctly Set the Value of a Text Object\nRecipe 4.2. Bind a Value to TextInput\nRecipe 4.3. Create a Suggestive TextInput\nRecipe 4.4. Create an In-Place Editor\nRecipe 4.5. Determine All Fonts Installed on a User's Computer\nRecipe 4.6. Create a Custom TextInput\nRecipe 4.7. Set the Style Properties for Text Ranges\nRecipe 4.8. Display Images and SWFs in HTML\nRecipe 4.9. Highlight User-Input Text in a Search Field\nRecipe 4.10. Manipulate Characters as Individual Graphics\nRecipe 4.11. Specify Styles for HTML in a TextField\nRecipe 4.12. Use the RichTextEditor\nRecipe 4.13. Apply Embedded Fonts with HTML\nRecipe 4.14. Add a Drop Shadow to Text in a Text Component\nRecipe 4.15. Find the Last Displayed Character in a TextArea\nChapter 5. Lists, Tiles, and Trees\nRecipe 5.1. Create an Editable List\nRecipe 5.2. Set Icons for Items in a List\nRecipe 5.3. Add Effects to a List to Indicate Changes\nRecipe 5.4. Set a Basic Item Renderer for a TileList\nRecipe 5.5. Set XML Data for a Tree\nRecipe 5.6. Create an Item Renderer for a Tree\nRecipe 5.7. Use Complex Data Objects in a Tree Control\nRecipe 5.8. Allow Only Certain Items in a List to Be Selectable\nRecipe 5.9. Format and Validate Data Added in a List's Item Editor\nRecipe 5.10. Track All Selected Children in a TileList\nRecipe 5.11. Use and Display Null Items in an Item Renderer\nRecipe 5.12. Create a Right-Click Menu for a List\nRecipe 5.13. Customize the Appearance of a Selection in a List\nChapter 6. DataGrid and Advanced DataGrid\nRecipe 6.1. Create Custom Columns for a DataGrid\nRecipe 6.2. Specify Sort Functions for DataGrid Columns\nRecipe 6.3. Enable Multicolumn Sorting in a DataGrid\nRecipe 6.4. Filter Items in a DataGrid\nRecipe 6.5. Create Custom Headers for an AdvancedDataGrid\nRecipe 6.6. Handle Events from a DataGrid/AdvancedDataGrid\nRecipe 6.7. Select Items in an AdvancedDataGrid\nRecipe 6.8. Enable Drag-and-Drop in a DataGrid\nRecipe 6.9. Edit Items in a DataGrid\nRecipe 6.10. Search Within a DataGrid and Autoscroll to the Match\nRecipe 6.11. Generate a Summary for Flat Data by Using GroupingCollection\nRecipe 6.12. Create an Async Refresh for a GroupingCollection\nChapter 7. Renderers and Editors\nRecipe 7.1. Create Your Own Renderers\nRecipe 7.2. Use the ClassFactory to Generate Renderers\nRecipe 7.3. Access the Component That Owns a Renderer\nRecipe 7.4. Create a Single Component to Act as Renderer and Editor\nRecipe 7.5. Create an Item Editor to Handle Data with Multiple Fields\nRecipe 7.6. Display SWF Objects as Items in a Menu by Using an Item Renderer\nRecipe 7.7. Select a DataGrid Column with a CheckBox Header Renderer\nRecipe 7.8. Create a Self-Contained CheckBox itemRenderer for Use in a DataGrid\nRecipe 7.9. Efficiently Set Images in a Renderer\nRecipe 7.10. Use Runtime Styling with itemRenderers and itemEditors\nRecipe 7.11. Use States and Transitions with an itemEditor\nRecipe 7.12. Create a CheckBox Tree Control\nRecipe 7.13. Resize Renderers Within a List\nChapter 8. Images, Bitmaps, Videos, Sounds\nRecipe 8.1. Load and Display an Image\nRecipe 8.2. Create a Video Display\nRecipe 8.3. Play and Pause an MP3 File\nRecipe 8.4. Create a Seek Bar for a Sound File\nRecipe 8.5. Blend Two Images\nRecipe 8.6. Apply a Convolution Filter to an Image\nRecipe 8.7. Send Video to an FMS Instance via a Camera\nRecipe 8.8. Access a User's Microphone and Create a Sound Display\nRecipe 8.9. Smooth Video Displayed in a Flex Application\nRecipe 8.10. Check Pixel-Level Collisions\nRecipe 8.11. Read and Save a User's Webcam Image\nRecipe 8.12. Use Blend Modes with Multiple Images\nRecipe 8.13. Handle Cue Points in FLV Data\nRecipe 8.14. Create a Video Scrubber\nRecipe 8.15. Read ID3 Data from an MP3 File\nRecipe 8.16. Display a Custom Loader while Loading Images\nRecipe 8.17. Enable Image Upload in Flex\nRecipe 8.18. Compare Two Bitmap Images\nChapter 9. Skinning and Styling\nRecipe 9.1. Use CSS to Style Components\nRecipe 9.2. Override the Default Application Style\nRecipe 9.3. Embed Styles by Using CSS\nRecipe 9.4. Override Base Style Properties\nRecipe 9.5. Customize Styles at Runtime\nRecipe 9.6. Load CSS at Runtime\nRecipe 9.7. Declare Styles at Runtime\nRecipe 9.8. Create Custom Style Properties for Components\nRecipe 9.9. Use Multiple Themes in the Same Application\nRecipe 9.10. Compile a Theme SWC\nRecipe 9.11. Use Embedded Fonts\nRecipe 9.12. Embed Fonts from a SWF File\nRecipe 9.13. Skin with Embedded Images\nRecipe 9.14. Apply Skins from a SWF File\nRecipe 9.15. Programmatically Skin a Component\nRecipe 9.16. Programmatically Skin a Stateful Control\nRecipe 9.17. Create Animated Skins from a SWF File\nRecipe 9.18. Customize the Preloader\nChapter 10. Dragging and Dropping\nRecipe 10.1. Use the DragManager Class\nRecipe 10.2. Specify a Drag Proxy\nRecipe 10.3. Drag and Drop Within a List\nRecipe 10.4. Drag and Drop Between Lists\nRecipe 10.5. Enable and Disable Drag Operations\nRecipe 10.6. Customize the DragImage of a List-Based Control\nRecipe 10.7. Customize the Drop Indicator of a List-Based Control\nChapter 11. States\nRecipe 11.1. Set Styles and Properties in a State\nRecipe 11.2. Create Transitions to Enter and Leave States\nRecipe 11.3. Use the AddChildAction and RemoveChildAction\nRecipe 11.4. Filter Transitions to Affect Only Certain Types of Children\nRecipe 11.5. Apply Parts of a Transition to Certain Children\nRecipe 11.6. Base a State on Another State\nRecipe 11.7. Integrate View States with HistoryManagement\nRecipe 11.8. Use Deferred Instance Factories with States\nRecipe 11.9. Use Data Binding with Objects Added in a State\nRecipe 11.10. Add and Remove Event Listeners in State Changes\nRecipe 11.11. Add View States to a Flash Component\nRecipe 11.12. Work with State Change Events\nRecipe 11.13. Dynamically Generate and Use New States and Transitions\nRecipe 11.14. Create Custom Actions to Use in a State\nChapter 12. Effects\nRecipe 12.1. Call an Effect in MXML and in ActionScript\nRecipe 12.2. Build a Custom Effect\nRecipe 12.3. Create Parallel Series or Sequences of Effects\nRecipe 12.4. Pause, Reverse, and Restart an Effect\nRecipe 12.5. Create Custom Effect Triggers\nRecipe 12.6. Create Tween Effects\nRecipe 12.7. Use the DisplacementMapFilter Filter in a Flex Effect\nRecipe 12.8. Create an AnimateColor Effect\nRecipe 12.9. Use the Convolution Filter to Create a Tween\nChapter 13. Collections\nRecipe 13.1. Add, Sort, and Retrieve Data from an ArrayCollection\nRecipe 13.2. Filter an ArrayCollection\nRecipe 13.3. Determine When an Item Is Modified in an ArrayCollection\nRecipe 13.4. Create a GroupingCollection\nRecipe 13.5. Create a Hierarchical Data Provider for a Control\nRecipe 13.6. Navigate a Collection Object and Save Your Position\nRecipe 13.7. Create a HierarchicalViewCollection Object\nRecipe 13.8. Filter and Sort an XMLListCollection\nRecipe 13.9. Sort on Multiple Fields in a Collection\nRecipe 13.10. Sort on Dates in a Collection\nRecipe 13.11. Create a Deep Copy of an ArrayCollection\nRecipe 13.12. Use Data Objects with Unique IDs\nChapter 14. Data Binding\nRecipe 14.1. Bind to a Property\nRecipe 14.2. Bind to a Function\nRecipe 14.3. Create a Bidirectional Binding\nRecipe 14.4. Bind to Properties by Using ActionScript\nRecipe 14.5. Use Bindable Property Chains\nRecipe 14.6. Bind to Properties on XML by Using E4X\nRecipe 14.7. Create Customized Bindable Properties\nRecipe 14.8. Bind to a Generic Object\nRecipe 14.9. Bind to Properties on a Dynamic Class\nChapter 15. Validation, Formatting, and Regular Expressions\nRecipe 15.1. Use Validators and Formatters with TextInput and TextArea Controls\nRecipe 15.2. Create a Custom Formatter\nRecipe 15.3. Create a More-International Zip Code Validator by Using Regular Expressions\nRecipe 15.4. Create a Validator to Validate UPCs\nRecipe 15.5. Validate Combo Boxes and Groups of Radio Buttons\nRecipe 15.6. Show Validation Errors by Using ToolTips in a Form\nRecipe 15.7. Use Regular Expressions for Locating Email Addresses\nRecipe 15.8. Use Regular Expressions for Matching Credit Card Numbers\nRecipe 15.9. Use Regular Expressions for Validating ISBNs\nRecipe 15.10. Create Regular Expressions by Using Explicit Character Classes\nRecipe 15.11. Use Character Types in Regular Expressions\nRecipe 15.12. Match Valid IP Addresses by Using Subexpressions\nRecipe 15.13. Use Regular Expressions for Different Types of Matches\nRecipe 15.14. Match Ends or Beginnings of Lines with Regular Expressions\nRecipe 15.15. Use Back-References\nRecipe 15.16. Use a Look-Ahead or Look-Behind\nChapter 16. Charting\nRecipe 16.1. Create a Chart\nRecipe 16.2. Add Effects to Charts\nRecipe 16.3. Select Regions of a Chart\nRecipe 16.4. Format Tick Marks for a Chart\nRecipe 16.5. Create a Custom Label for a Chart\nRecipe 16.6. Create a Drill-Down Effect for a Columnar Chart\nRecipe 16.7. Skin Chart Items\nRecipe 16.8. Use ActionScript to Dynamically Add and Remove Columns for a Chart\nRecipe 16.9. Overlap Multiple ChartSeries\nRecipe 16.10. Drag and Drop Items in a Chart\nRecipe 16.11. Create an Editable Line Chart\nChapter 17. SharedObjects\nRecipe 17.1. Create a SharedObject\nRecipe 17.2. Write Data into a SharedObject\nRecipe 17.3. Save a Local SharedObject\nRecipe 17.4. Read Data from a SharedObject\nRecipe 17.5. Remove Data from a SharedObject\nRecipe 17.6. Serialize Typed Objects\nRecipe 17.7. Access a SharedObject Between Multiple Flash Applications\nRecipe 17.8. Remember TextInput Values for Returning Users\nChapter 18. Working with Services and Server-Side Communication\nRecipe 18.1. Configure an HTTPService\nRecipe 18.2. Use RESTful Communication Between Flex Applications\nRecipe 18.3. Configure and Connect to a RemoteObject\nRecipe 18.4. Use Flex Remoting with AMFPHP 1.9\nRecipe 18.5. Use the IExternalizable Interface for Custom Serialization\nRecipe 18.6. Track Results from Multiple Simultaneous Service Calls\nRecipe 18.7. Use Publish/Subscribe Messaging\nRecipe 18.8. Register a Server-Side Data Type Within a Flex Application\nRecipe 18.9. Communicate with a WebService\nRecipe 18.10. Add a SOAP Header to a Request to a WebService\nRecipe 18.11. Parse a SOAP Response from a WebService\nRecipe 18.12. Communicate Securely with AMF by Using SecureAMFChannel\nRecipe 18.13. Send and Receive Binary Data via a Binary Socket\nRecipe 18.14. Communicate Using an XMLSocket\nChapter 19. XML\nRecipe 19.1. Load an XML File\nRecipe 19.2. Navigate an XML Document in E4X\nRecipe 19.3. Use Regular Expressions in E4X Queries\nRecipe 19.4. Add an XMLList to an XML Object\nRecipe 19.5. Bind to an XMLList or an E4X Query\nRecipe 19.6. Generate XML Objects from Arrays\nRecipe 19.7. Handle Namespaces in XML Returned by a Service\nRecipe 19.8. Encode an ActionScript Data Object as XML\nRecipe 19.9. Populate a Component with Complex XML Data\nRecipe 19.10. Decode XML from a Web Service into Strongly Typed Objects\nChapter 20. Browser Communication\nRecipe 20.1. Link to an External URL\nRecipe 20.2. Work with FlashVars\nRecipe 20.3. Invoke JavaScript Functions from Flex\nRecipe 20.4. Invoke ActionScript Functions from JavaScript\nRecipe 20.5. Change the HTML Page Title via BrowserManager\nRecipe 20.6. Parse the URL via BrowserManager\nRecipe 20.7. Deep-Link to Data via BrowserManager\nRecipe 20.8. Deep-Link Containers via BrowserManager\nRecipe 20.9. Implement Custom History Management\nChapter 21. Development Strategies\nRecipe 21.1. Use the Flex Component Kit to Create Components\nRecipe 21.2. Use the ContainerMovieClip to Create Flex Containers in Flash\nRecipe 21.3. Import Components from Flash CS3\nRecipe 21.4. Get Started with the Cairngorm Architecture\nRecipe 21.5. Create a Cairngorm View, Event, and Model\nRecipe 21.6. Create Cairngorm Commands and a Business Delegate Class\nRecipe 21.7. Create a Cairngorm FrontController and a ServiceLocator\nRecipe 21.8. Generate an Application Structure with the Cairngen Framework Generator\nRecipe 21.9. Learn Common Performance-Tuning Tricks\nRecipe 21.10. Create Custom Metadata Properties for a Component\nChapter 22. Modules and Runtime Shared Libraries\nRecipe 22.1. Create a Runtime Shared Library\nRecipe 22.2. Use Cross-Domain Runtime Shared Libraries\nRecipe 22.3. Use the Flex Framework as a Runtime Shared Library\nRecipe 22.4. Optimize a Runtime Shared Library\nRecipe 22.5. Create an MXML-Based Module\nRecipe 22.6. Create an ActionScript-Based Module\nRecipe 22.7. Load a Module by Using ModuleLoader\nRecipe 22.8. Use ModuleManager to Load Modules\nRecipe 22.9. Load Modules from Different Servers\nRecipe 22.10. Communicate with a Module\nRecipe 22.11. Pass Data to Modules by Using Query Strings\nRecipe 22.12. Optimize Modules by Using Linker Reports\nChapter 23. The Adobe Integrated Runtime API\nRecipe 23.1. Create an AIR Application Leveraging the Flex Framework\nRecipe 23.2. Understand the AIR Command-Line Tools\nRecipe 23.3. Open and Manage Native Windows\nRecipe 23.4. Create Native Menus\nRecipe 23.5. Read and Write to a File\nRecipe 23.6. Serialize Objects\nRecipe 23.7. Use the Encrypted Local Store\nRecipe 23.8. Browse for Files\nRecipe 23.9. Use the AIR File System Controls\nRecipe 23.10. Use the Native Drag-and-Drop API\nRecipe 23.11. Interact with the Operating System Clipboard\nRecipe 23.12. Add HTML Content\nRecipe 23.13. Cross-Script Between ActionScript and JavaScript\nRecipe 23.14. Work with Local SQL Databases\nRecipe 23.15. Detect and Monitor a Network Connection\nRecipe 23.16. Detect User Presence\nRecipe 23.17. Create System Tray and Dock Applications\nChapter 24. Unit Testing with FlexUnit\nRecipe 24.1. Create an Application That Uses the FlexUnit Framework\nRecipe 24.2. Create an Application to Run FlexUnit Tests\nRecipe 24.3. Create a FlexUnit Test Case\nRecipe 24.4. Add a Test Case to a Test Suite\nRecipe 24.5. Run Code Before and After Every Test\nRecipe 24.6. Share Test Data Between Test Cases\nRecipe 24.7. Handle Events in a Test Case\nRecipe 24.8. Test Visual Components with FlexUnit\nRecipe 24.9. Install and Configure Antennae\nRecipe 24.10. Generate Automated Test Suites\nChapter 25. Compiling and Debugging\nRecipe 25.1. Use Trace Statements Without Flex Builder\nRecipe 25.2. Use the Component Compiler\nRecipe 25.3. Install the Flex Ant Tasks\nRecipe 25.4. Use the compc and mxmlc Tasks in the Flex Ant Tasks\nRecipe 25.5. Compile and Deploy Flex Applications That Use RSLs\nRecipe 25.6. Create and Monitor Expressions in Flex Builder Debugging\nRecipe 25.7. Install the Ant View in the Stand-Alone Version of Flex Builder\nRecipe 25.8. Create an Ant Build File for Automating Common Tasks\nRecipe 25.9. Compile a Flex Application by Using mxmlc and Ant\nRecipe 25.10. Generate Documentation by Using ASDoc and Ant\nRecipe 25.11. Compile Flex Applications by Using Rake\nRecipe 25.12. Use ExpressInstall for Your Application\nRecipe 25.13. Use Memory Profiling with Flex Builder 3 to View Memory Snapshots\nChapter 26. Configuration, Internationalization, and Printing\nRecipe 26.1. Add an International Character Set to an Application\nRecipe 26.2. Use a Resource Bundle to Localize an Application\nRecipe 26.3. Use the ResourceManager for Localization\nRecipe 26.4. Use Resource Modules for Localization\nRecipe 26.5. Support IME Devices\nRecipe 26.6. Detect a Screen Reader\nRecipe 26.7. Create a Tabbing Reading Order for Accessibility\nRecipe 26.8. Print Selected Items in an Application\nRecipe 26.9. Format Application Content for Printing\nRecipe 26.10. Control Printing of Unknown Length Content over Multiple Pages\nRecipe 26.11. Add a Header and a Footer When Printing\n","date":"2009-03-08","description":"","lastmod":"2009-03-08T13:24:05Z","slug":"659","tags":["book","flex","training"],"title":"Flex入门的好书——《Flex3 Cookbook 中文版》","url":"https://blog.zengrong.net/post/659/"},{"categories":["news"],"content":" 应用最广的十大Javascript框架 9 个基于JavaScript 和 CSS 的 Web 图表框架 40 个轻量级 JavaScript 库（上） 40 个轻量级 JavaScript 库（下） 坚持web标准，平视jQuery ","date":"2009-03-05","description":"","lastmod":"2009-03-05T13:10:31Z","slug":"652","tags":["framework","javascript","jquery"],"title":"关于JavaScript的几篇文章","url":"https://blog.zengrong.net/post/652/"},{"categories":["use"],"content":"这几个文档分享网站都是基于Flash技术，原理是上传文档后在后台转换为swf，前台用FlashPlayer载入显示实现分享。目前支持后台转换Flash的技术，我所了解的只有Print2Flash。\n豆丁网 速度应该是三个中最快的，另外也是国产、中文界面。感觉广告太多，UI设计上太过华丽，“花”了点。文档的质量参差不齐，什么样的都有，但文档的总量却是很多的。上传文档需要等待审核，审核的时间还不短。 :em23: API分为“标准”和“高级”两种，高级API支持上传，需要申请（我申请了两天都没回音），标准的API只能预览，也太小气了点。\nscribd 个人推荐这个。支持OpenID登录，界面清新，使用简洁，API功能丰富，也有AS3的类库支持 :em02: 上传后貌似不需要审核，但它的文档并非一上传就转换，而是在第一次浏览的时候转换的，因此第一次观看的时候需要等待一段时间。Scribd对纯文本格式的中文文档，仅支持UTF-8编码，如果使用GB2312或者GBK，上传后就全部变成乱码。但如果是DOC格式就没问题。\ndocstoc 注册过程比较冗长，你要我那么多信息干嘛啊，帮我征婚？上传速度还不错，另外有官方的客户端可以使用，增强了操作体验。貌似没有API（也可能我没找到）。\n","date":"2009-03-04","description":"","lastmod":"2009-03-04T02:56:48Z","slug":"642","tags":["flash"],"title":"简单比较3个文档分享网站","url":"https://blog.zengrong.net/post/642/"},{"categories":["technology"],"content":"AIRClose.mxml\n1\u0026lt;mx:WindowedApplication xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; 2 layout=\u0026#34;vertical\u0026#34; showStatusBar=\u0026#34;false\u0026#34; 3 closing=\u0026#34;closeHandler(event)\u0026#34;\u0026gt; 4 \u0026lt;mx:Script\u0026gt; 5 \u0026lt;![CDATA[ 6 import org.zengrong.utils.Dialog; 7 private function closeHandler(evt:Event):void 8 { 9 trace(evt.toString()); 10 evt.preventDefault(); 11 Dialog.confirm(\u0026#39;确定退出？\u0026#39;, _close); 12 } 13 14 private function _close($yes:Boolean):void 15 { 16 if($yes) this.nativeApplication.exit(); 17 } 18 19 ]]\u0026gt; 20 \u0026lt;/mx:Script\u0026gt; 21\u0026lt;/mx:WindowedApplication\u0026gt; 有关Dialog的用法与最新源码，详见zrong's as3lib（下附源码）\nDialog.as\n1package org.zengrong.utils 2{ 3 import flash.display.Sprite; 4 5 import mx.controls.Alert; 6 import mx.core.Application; 7 import mx.events.CloseEvent; 8 9 public class Dialog 10 { 11 public static function alert($info:String, $title:String=\u0026#39;\u0026#39;):void 12 { 13 Alert.show($info, $title, 4, Application.application as Sprite); 14 } 15 16 /** 17 * 弹出confirm确认对话框，根据用户的交互返回是否确认布尔值 18 * @param $s 要显示的信息 19 * @param $closeFun 关闭确认对话框时调用的函数 20 */ 21 public static function confirm($s:String, $closeFun:Function):void 22 { 23 var __fun:Function = function(evt:CloseEvent):void 24 { 25 $closeFun(evt.detail == Alert.YES); 26 } 27 Alert.show($s, \u0026#39;\u0026#39;, Alert.YES|Alert.NO, Application.application as Sprite, __fun); 28 } 29 } 30} ","date":"2009-02-28","description":"","lastmod":"2009-02-28T17:04:15Z","slug":"show-an-alert-before-this-close-of-air","tags":["actionscript","air","flex"],"title":"在关闭AIR程序窗口前显示Alert","url":"https://blog.zengrong.net/post/show-an-alert-before-this-close-of-air/"},{"categories":["technology"],"content":"Flex帮助告诉我们，要在MXML的属性中使用特殊字符，可以使用HTML实体。但有的字符，没有HTML实体可用（例如换行符）。如果我需要在toolTip属性中换行，怎么办呢？答案是使用Latin字符集。对应的换行符的表示法是\u0026amp;#13;\n下面是一个比较全的对照表，转自旺仔的专栏\n字符 十进制字符编号 实体名字 说明\n--- \u0026amp;#00; --- 未使用Unused\n--- \u0026amp;#01; --- 未使用Unused\n--- \u0026amp;#02; --- 未使用Unused\n--- \u0026amp;#03; --- 未使用Unused\n--- \u0026amp;#04; --- 未使用Unused\n--- \u0026amp;#05; --- 未使用Unused\n--- \u0026amp;#06; --- 未使用Unused\n--- \u0026amp;#07; --- 未使用Unused\n--- \u0026amp;#08; --- 未使用Unused\n--- \u0026amp;#09; --- 制表符Horizontal tab\n--- \u0026amp;#10; --- 换行Line feed\n--- \u0026amp;#11; --- 未使用Unused\n--- \u0026amp;#12; --- 未使用Unused\n--- \u0026amp;#13; --- 回车Carriage Return\n--- \u0026amp;#14; --- 未使用Unused\n--- \u0026amp;#15; --- 未使用Unused\n--- \u0026amp;#16; --- 未使用Unused\n--- \u0026amp;#17; --- 未使用Unused\n--- \u0026amp;#18; --- 未使用Unused\n--- \u0026amp;#19; --- 未使用Unused\n--- \u0026amp;#20; --- 未使用Unused\n--- \u0026amp;#21; --- 未使用Unused\n--- \u0026amp;#22; --- 未使用Unused\n--- \u0026amp;#23; --- 未使用Unused\n--- \u0026amp;#24; --- 未使用Unused\n--- \u0026amp;#25; --- 未使用Unused\n--- \u0026amp;#26; --- 未使用Unused\n--- \u0026amp;#27; --- 未使用Unused\n--- \u0026amp;#28; --- 未使用Unused\n--- \u0026amp;#29; --- 未使用Unused\n--- \u0026amp;#30; --- 未使用Unused\n--- \u0026amp;#31; --- 未使用Unused\n\u0026amp;\\#32; --- Space ! \u0026amp;#33; --- 惊叹号Exclamation mark\n\u0026quot; \u0026amp;#34; \u0026quot; 双引号Quotation mark\n# \u0026amp;#35; --- 数字标志Number sign\n$ \u0026amp;#36; --- 美元标志Dollar sign\n% \u0026amp;#37; --- 百分号Percent sign\n\u0026amp; \u0026amp;#38; \u0026amp; Ampersand\n' \u0026amp;#39; --- 单引号Apostrophe\n( \u0026amp;#40; --- 小括号左边部分Left parenthesis\n) \u0026amp;#41; --- 小括号右边部分Right\nparenthesis * \u0026amp;#42; --- 星号Asterisk\n\u0026amp;\\#43; --- 加号Plus sign , \u0026amp;#44; --- 逗号Comma\n\u0026amp;\\#45; --- 连字号Hyphen . \u0026amp;#46; --- 句号Period (fullstop)\n/ \u0026amp;#47; --- 斜杠Solidus (slash)\n0 \u0026amp;#48; --- 数字0 Digit 0\n1 \u0026amp;#49; --- 数字1 Digit 1\n2 \u0026amp;#50; --- 数字2 Digit 2\n3 \u0026amp;#51; --- 数字3 Digit 3\n4 \u0026amp;#52; --- 数字4 Digit 4\n5 \u0026amp;#53; --- 数字5 Digit 5\n6 \u0026amp;#54; --- 数字6 Digit 6\n7 \u0026amp;#55; --- 数字7 Digit 7\n8 \u0026amp;#56; --- 数字8 Digit 8\n9 \u0026amp;#57; --- 数字9 Digit 9\n: \u0026amp;#58; --- 冒号Colon\n; \u0026amp;#59; --- 分号Semicolon\n\u0026lt; \u0026amp;#60; \u0026lt; 小于号Less than\n= \u0026amp;#61; --- 等于符号Equals sign\n\u0026gt; \u0026amp;#62; \u0026gt; 大于号Greater than\n? \u0026amp;#63; --- 问号Question mark\n@ \u0026amp;#64; --- Commercial at\nA \u0026amp;#65; --- 大写A Capital A\nB \u0026amp;#66; --- 大写B Capital B\nC \u0026amp;#67; --- 大写C Capital C\nD \u0026amp;#68; --- 大写D Capital D\nE \u0026amp;#69; --- 大写E Capital E\nF \u0026amp;#70; --- 大写F Capital F\nG \u0026amp;#71; --- 大写G Capital G\nH \u0026amp;#72; --- 大写H Capital H\nI \u0026amp;#73; --- 大写J Capital I\nJ \u0026amp;#74; --- 大写K Capital J\nK \u0026amp;#75; --- 大写L Capital K\nL \u0026amp;#76; --- 大写K Capital L\nM \u0026amp;#77; --- 大写M Capital M\nN \u0026amp;#78; --- 大写N Capital N\nO \u0026amp;#79; --- 大写O Capital O\nP \u0026amp;#80; --- 大写P Capital P\nQ \u0026amp;#81; --- 大写Q Capital Q\nR \u0026amp;#82; --- 大写R Capital R\nS \u0026amp;#83; --- 大写S Capital S\nT \u0026amp;#84; --- 大写T Capital T\nU \u0026amp;#85; --- 大写U Capital U\nV \u0026amp;#86; --- 大写V Capital V\nW \u0026amp;#87; --- 大写W Capital W\nX \u0026amp;#88; --- 大写X Capital X\nY \u0026amp;#89; --- 大写Y Capital Y\nZ \u0026amp;#90; --- 大写Z Capital Z\n[ \u0026amp;#91; --- 中括号左边部分Left square bracket\n\\ \u0026amp;#92; --- 反斜杠Reverse solidus (backslash)\n] \u0026amp;#93; --- 中括号右边部分Right\nsquare bracket ^ \u0026amp;#94; --- Caret\n_ \u0026amp;#95; --- 下划线Horizontal bar (underscore)\n` \u0026amp;#96; --- 尖重音符Acute accent\na \u0026amp;#97; --- 小写a Small a\nb \u0026amp;#98; --- 小写b Small b\nc \u0026amp;#99; --- 小写c Small c\nd \u0026amp;#100; --- 小写d Small d\ne \u0026amp;#101; --- 小写e Small e\nf \u0026amp;#102; --- 小写f Small f\ng \u0026amp;#103; --- 小写g Small g\nh \u0026amp;#104; --- 小写h Small h\ni \u0026amp;#105; --- 小写i Small i\nj \u0026amp;#106; --- 小写j Small j\nk \u0026amp;#107; --- 小写k Small k\nl \u0026amp;#108; --- 小写l Small l\nm \u0026amp;#109; --- 小写m Small m\nn \u0026amp;#110; --- 小写n Small n\no \u0026amp;#111; --- 小写o Small o\np \u0026amp;#112; --- 小写p Small p\nq \u0026amp;#113; --- 小写q Small q\nr \u0026amp;#114; --- 小写r Small r\ns \u0026amp;#115; --- 小写s Small s\nt \u0026amp;#116; --- 小写t Small t\nu \u0026amp;#117; --- 小写u Small u\nv \u0026amp;#118; --- 小写v Small v\nw \u0026amp;#119; --- 小写w Small w\nx \u0026amp;#120; --- 小写x Small x\ny \u0026amp;#121; --- 小写y Small y\nz \u0026amp;#122; --- 小写z Small z\n{ \u0026amp;#123; --- 大括号左边部分Left curly brace\n| \u0026amp;#124; --- 竖线Vertical bar\n} \u0026amp;#125; --- 大括号右边部分Right\ncurly brace ~ \u0026amp;#126; --- Tilde\n--- \u0026amp;#127; --- 未使用Unused\n\u0026amp;#160; 空格Nonbreaking space\n¡ \u0026amp;#161; ¡ Inverted exclamation\n¢ \u0026amp;#162; ¢ 货币分标志Cent sign\n£ \u0026amp;#163; £ 英镑标志Pound sterling\n¤ \u0026amp;#164; ¤ 通用货币标志General\ncurrency sign ¥ \u0026amp;#165; ¥ 日元标志Yen sign\n¦ \u0026amp;#166; ¦ or 断竖线Broken \u0026amp;brkbar; vertical bar\n§ \u0026amp;#167; § 分节号Section sign\n¨ \u0026amp;#168; ¨ or ¨ 变音符号Umlaut\n© \u0026amp;#169; © 版权标志Copyright\nª \u0026amp;#170; ª Feminine ordinal\n« \u0026amp;#171; « Left angle quote, guillemet left\n¬ \u0026amp;#172; \u0026amp;not Not sign\n­ \u0026amp;#173; ­ Soft hyphen\n® \u0026amp;#174; ® 注册商标标志Registered\ntrademark ¯ \u0026amp;#175; ¯ or \u0026amp;hibar; 长音符号Macron accent\n° \u0026amp;#176; ° 度数标志Degree sign\n± \u0026amp;#177; ± 加或减Plus or minus\n² \u0026amp;#178; ² 上标2 Superscript two\n³ \u0026amp;#179; ³ 上标3 Superscript three\n´ \u0026amp;#180; ´ 尖重音符Acute accent\nµ \u0026amp;#181; µ Micro sign\n¶ \u0026amp;#182; ¶ Paragraph sign\n· \u0026amp;#183; · Middle dot\n¸ \u0026amp;#184; ¸ Cedilla\n¹ \u0026amp;#185; ¹ 上标1 Superscript one\nº \u0026amp;#186; º Masculine ordinal\n» \u0026amp;#187; » Right angle quote, guillemet right\n¼ \u0026amp;#188; ¼ 四分之一Fraction one-fourth\n½ \u0026amp;#189; ½ 二分之一Fraction one-half\n¾ \u0026amp;#190; ¾ 四分之三Fraction three-fourths\n¿ \u0026amp;#191; ¿ Inverted question mark\nÀ \u0026amp;#192; À Capital A, grave accent\nÁ \u0026amp;#193; Á Capital A, acute accent\nÂ \u0026amp;#194; Â Capital A, circumflex\nÃ \u0026amp;#195; Ã Capital A, tilde\nÄ \u0026amp;#196; Ä Capital A, di?esis / umlaut\nÅ \u0026amp;#197; Å Capital A, ring\nÆ \u0026amp;#198; Æ Capital AE ligature\nÇ \u0026amp;#199; Ç Capital C, cedilla\nÈ \u0026amp;#200; È Capital E, grave accent\nÉ \u0026amp;#201; É Capital E, acute accent\nÊ \u0026amp;#202; Ê Capital E, circumflex\nË \u0026amp;#203; Ë Capital E, di?esis / umlaut\nÌ \u0026amp;#204; Ì Capital I, grave accent\nÍ \u0026amp;#205; Í Capital I, acute accent\nÎ \u0026amp;#206; Î Capital I, circumflex\nÏ \u0026amp;#207; Ï Capital I, di?esis / umlaut\nÐ \u0026amp;#208; Ð Capital Eth, Icelandic\nÑ \u0026amp;#209; Ñ Capital N, tilde\nÒ \u0026amp;#210; Ò Capital O, grave accent\nÓ \u0026amp;#211; Ó Capital O, acute accent\nÔ \u0026amp;#212; Ô Capital O, circumflex\nÕ \u0026amp;#213; Õ Capital O, tilde\nÖ \u0026amp;#214; Ö Capital O, di?esis / umlaut\n× \u0026amp;#215; × 乘号Multiply sign\nØ \u0026amp;#216; Ø Capital O, slash\nÙ \u0026amp;#217; Ù Capital U, grave accent\nÚ \u0026amp;#218; Ú Capital U, acute accent\nÛ \u0026amp;#219; Û Capital U, circumflex\nÜ \u0026amp;#220; Ü Capital U, di?esis / umlaut\nÝ \u0026amp;#221; Ý Capital Y, acute accent\nÞ \u0026amp;#222; Þ Capital Thorn, Icelandic\nß \u0026amp;#223; ß Small sharp s, German sz\nà \u0026amp;#224; à Small a, grave accent\ná \u0026amp;#225; á Small a, acute accent\nâ \u0026amp;#226; â Small a, circumflex\nã \u0026amp;#227; ã Small a, tilde\nä \u0026amp;#228; ä Small a, di?esis / umlaut\nå \u0026amp;#229; å Small a, ring\næ \u0026amp;#230; æ Small ae ligature\nç \u0026amp;#231; ç Small c, cedilla\nè \u0026amp;#232; è Small e, grave accent\né \u0026amp;#233; é Small e, acute accent\nê \u0026amp;#234; ê Small e, circumflex\në \u0026amp;#235; ë Small e, di?esis / umlaut\nì \u0026amp;#236; ì Small i, grave accent\ní \u0026amp;#237; í Small i, acute accent\nî \u0026amp;#238; î Small i, circumflex\nï \u0026amp;#239; ï Small i, di?esis / umlaut\nð \u0026amp;#240; ð Small eth, Icelandic\nñ \u0026amp;#241; ñ Small n, tilde\nò \u0026amp;#242; ò Small o, grave accent\nó \u0026amp;#243; ó Small o, acute accent\nô \u0026amp;#244; ô Small o, circumflex\nõ \u0026amp;#245; õ Small o, tilde\nö \u0026amp;#246; ö Small o, di?esis / umlaut\n÷ \u0026amp;#247; ÷ 除号Division sign\nø \u0026amp;#248; ø Small o, slash\nù \u0026amp;#249; ù Small u, grave accent\nú \u0026amp;#250; ú Small u, acute accent\nû \u0026amp;#251; û Small u, circumflex\nü \u0026amp;#252; ü Small u, di?esis / umlaut\ný \u0026amp;#253; ý Small y, acute accent\nþ \u0026amp;#254; þ Small thorn, Icelandic\nÿ \u0026amp;#255; ÿ Small y, umlaut\n","date":"2009-02-28","description":"","lastmod":"2009-02-28T14:40:43Z","slug":"615","tags":["flex","character-set"],"title":"ISO Latin-1字符集(HTML实体对照)-在Flex Tooltip中换行","url":"https://blog.zengrong.net/post/615/"},{"categories":["others"],"content":"作者：Riku\n原文： 11 Free And Useful Open-Source Alternatives For Designers\n译文： 11个免费开源的设计软件\n作为一个业余的图像设计爱好者，对于设计类的软件还是比较关注的，而在这篇文章中，作者整理出了一个11个免费开源的设计类软件，用于代替商业性的同类软件，对于版权问题比较关注的朋友来说，该文是非常值得阅读和收藏的。因此，我也不遗余力的把它翻译成了中文版，以便于大家阅读。\n# 以下是这11 款软件列表：\n1、Jahshaka[代替：Adobe After Effects ]\n这是世界上最先进的资源开放实时编辑和效果系统，Jahshaka 充分利用 OpenGL 和 OpenML，为用户提供了异常优异的性能，当前支持 Linux, OsX 和 Windows 操作系统。 Jahshaka 遵守 GNU GPL 协议下的公共许可。\n2、CinePaint [ 代替：Adobe Photoshop]\n一个开源的类似于Photoshop的图形制作和处理软件。它来源于Film Gimp项目，目的是为了满足Linux下图像制作和处理方面所谓DP（Deep Paint）的需求。DP的意思是要使颜色的深度达到八位以上，以达到电影制作的要求。\n3、Amaya[代替： Adobe Dreamweaver ]\nW3C 出品的所见即所得的网页编辑/浏览器, 支持 HTML 4.0 的绝大部分格式，支持包括 Win98、Win98、WinNT、Linux、Solaris、AIX 在内的多种操作系统。\n4、Open Office Draw [ 代替：Microsoft Visio ]\n它是OpenOffice.org办公软件套装的一部份。它的特色在于多样的「接头」形状，可以用在多种不同的线条上，方便建筑绘图，以及流程图上。\n5、Blender [代替： Autodesk 3DS Max ]\n[caption id=\u0026quot;attachment_599\u0026quot; align=\u0026quot;aligncenter\u0026quot; width=\u0026quot;500\u0026quot; caption=\u0026quot;Blender \u0026quot;][/caption]\nBlender 是一种3D建模软件，写得非常精炼。而且使用很方便，效果惊人，某些方面甚至不亚于3DS Max。\n6、Imgv [代替： ACDSee]\n[caption id=\u0026quot;attachment_600\u0026quot; align=\u0026quot;aligncenter\u0026quot; width=\u0026quot;500\u0026quot; caption=\u0026quot;Imgv\u0026quot;][/caption]\nImgv是一个免费的图片浏览器，可以在MS-Windows、Linux 和BSD 等众多平台上运行。它包括标准的功能（文件/目录浏览，幻灯片显视，调焦，翻转/旋转，等等）和特殊的功能（多视图，可调整的索引图大小，图片播放列表，远程图片装载，MPEG电影支持，一个可定制的接口，等等）。\n7、Inkscape [代替： Adobe Illustrator]\n[caption id=\u0026quot;attachment_601\u0026quot; align=\u0026quot;aligncenter\u0026quot; width=\u0026quot;500\u0026quot; caption=\u0026quot;Inkscape\u0026quot;][/caption]\nInkscape是一套矢量图形编辑器，以自由软件授权发布与使用。该软件的开发目标是成为一套强力的绘图工具软件，且能完全遵循与支持XML、SVG及CSS等开放性的标准格式。此外Inkscape同时也是一套跨平台性的应用程序，针对不同的操作系统它都有能搭配对应执行的版本，如Windows版、Mac OS X版、Linux版、以及类UNIX版等操作系统，不过主要仍是以Linux为开发平台。\n8、Gimpshop [ 代替：Adobe Photoshop]\n[caption id=\u0026quot;attachment_602\u0026quot; align=\u0026quot;aligncenter\u0026quot; width=\u0026quot;500\u0026quot; caption=\u0026quot;Gimpshop\u0026quot;][/caption]\nGimpshop是一套功能十分优异的免费图像处理软件，它其实是以著名图像处理软件GIMP为基础所发展而来，功能相当多元；除了强大的图像编辑功能以外，更包含了滤镜、屏蔽、图层，色彩处理、多种特殊图形处理模式等等。\n9、Xara LX[代替： CoralDRAW ]\n[caption id=\u0026quot;attachment_603\u0026quot; align=\u0026quot;aligncenter\u0026quot; width=\u0026quot;500\u0026quot; caption=\u0026quot;Xara LX \u0026quot;][/caption]\n一款矢量绘图软件，与CorelDraw较为相似。它是Xara Xtreme 的 Linux 版本，所以只支持Unix平台，包括Linux, FreeBSD 和 (开发中的) OS-X。\n10、Avidemux[代替：Final CutPro]\n[caption id=\u0026quot;attachment_604\u0026quot; align=\u0026quot;aligncenter\u0026quot; width=\u0026quot;500\u0026quot; caption=\u0026quot;Avidemux \u0026quot;][/caption]\nAvidemux是一个免费的视频编辑器，可以进行剪切、过滤和编码等任务。它支持广泛的文件格式，包括AVI文件的编辑，DVD的MPEG文件、MP4和ASF，并能将声音从文件中分解出来。支持强大的队列任务处理和脚本功能。\n11、Pencil [代替： Toon Boom Studio]\n[caption id=\u0026quot;attachment_605\u0026quot; align=\u0026quot;aligncenter\u0026quot; width=\u0026quot;500\u0026quot; caption=\u0026quot;Pencil\u0026quot;][/caption]\nPencil 是一个用于卡通制作手绘图像编辑软件，支持Mac OS X, Windows, 和 Linux，支持位图和矢量图形格式。\n","date":"2009-02-21","description":"","lastmod":"2009-02-21T05:13:48Z","slug":"594","tags":["freeware","opensource","software"],"title":"【转】译文：11个免费开源的设计软件","url":"https://blog.zengrong.net/post/594/"},{"categories":["technology"],"content":"环境：Flex Builder 3.02，Flex SDK 3.2，AIR SDK 1.5\n编译AIR文件中使用了自定义图标，编译过程中Flex Builder报错如下：\nError creating AIR file:303:Error,……\n百思不得其解。因为我分别自定义了 16x16、32x32、48x48、128x128 这4种大小的图标，后两个没有报错，为什么前两个报错呢？\n4个图标都使用Fireworks制作，应该也不存在编码问题。\ngoogle上搜索了两条相关的问题讨论，未果：\n其一 其二 swf文件是可以编译成功的，只是在最后一步AIR打包的时候出错。AIR其实本质就是zip。查看打包前的文件内容，发现icon16和icon32这两个文件并没有被载入进入需要打包的列表中。\n在打包前手工添加这两个文件，打包可以成功。\n既然手工添加可以，那为什么在编译的时候，Flex Builder不自动把这两个文件加入资源包中呢？文件名称路径都没有任何错误啊！\n忽然想到前几天orphen告诉我，“苍蝇不叮无缝的蛋”，计算机本没有错，如果出错，那一定是我错了。\n此时灵光一现，“冲突”二字突然浮现与我的脑海，世上万物此消彼长，因此才有 汉正街大火 与 投诉物业公司 ，若无冲突，怎可体现和谐之美？\n想到此处，我茅塞顿开，原来问题还是出于我身！不禁自责不已，深为自己对业余代码民工这个如此有前途之职业之不敬而后悔。想到代码中在AIR窗口图标与任务栏图标中确实引用过这两个图标文件，应该是在最终打包的过程中，由于代码与配置文件两处同时引用这两个文件，造成冲突，导致无法将这两个文件打包。\n解决方法很简单，把icon16.png与icon32.png复制并改名即可。我是这么做的：\n1\u0026lt;icon\u0026gt; 2 \u0026lt;image16x16\u0026gt;assets/icon1616.png\u0026lt;/image16x16\u0026gt; 3 \u0026lt;image32x32\u0026gt;assets/icon3232.png\u0026lt;/image32x32\u0026gt; 4 \u0026lt;image48x48\u0026gt;assets/icon48.png\u0026lt;/image48x48\u0026gt; 5 \u0026lt;image128x128\u0026gt;assets/icon128.png\u0026lt;/image128x128\u0026gt; 6\u0026lt;/icon\u0026gt; kao！白白浪费我2小时……\n","date":"2009-02-17","description":"","lastmod":"2009-02-17T15:49:22Z","slug":"air-compile-error-303","tags":["air","flexbuilder"],"title":"编译AIR文件303 ERROR问题的解决","url":"https://blog.zengrong.net/post/air-compile-error-303/"},{"categories":["others"],"content":"小区名：崇仁新都（中山大道崇仁路口）\n物业公司：武汉市房开物业管理有限责任公司\n1、物业公司代收水费，每吨2.3元，且不提供发票；\n2、物业公司没有专门的保洁人员，过年过节、周六周日无人做清洁；\n3、小区院内垃圾箱太小，垃圾经常溢出；\n4、升降车位系统一直没有启用，也无人管理；\n5、小区院内汽车乱停乱放，无人管理；\n6、小区门卫对进出小区的人员从不过问。\n武汉市国土资源和房产管理局投诉页面：\nhttp://www.whgtfcj.gov.cn/dzdc/zxfw.aspx\n投诉编码：2009216232330278\n硚口区政府投诉页面\nhttp://219.140.178.210:8000/egweb/addproject.aspx\n投诉编码等信息：\n受理号: 090216108\n密 码: 4035\n(请记好您的受理号和密码,稍后会有一条短信发送到您的手机,凭借此受理号和密码,您可以通过网站 www.qk.gov.cn 或打电话到83777777查询该办件的信息和处理过程)\n2009年2月17日更新：\n早上收到83777777发来的短信，内容如下：\n您的办件已经受理，您可以在2009-02-26登录www.qk.gov.cn或拨打83777777进行查询。\n发短信的效率够高，但干嘛我要10天后才能查询？唉……办事效率低下，ZF的通病啊 :em40:\n2009年2月20日更新：\n前面刚说ZF效率低下，今天下午就给我来电话了。下午4点半左右，硚口区房产管理局物业科（房号411，物业科欢迎大家去咨询）打来电话，说他们已经去小区现场进行了勘察，并就我反映的问题与物业进行了沟通，对他们进行了批评，并就我投诉的6个问题进行了回复：\n1、水费高出的价格是物业公司将水抽到顶楼的电费、管理费。没有发票的原因是因为水务集团统一开的一张发票给物业公司，业主没有分表无法计量（没分表？），不能拆散给业主。\n（zrong：这些都不是理由，物业可以要求水务集团开分户发票，即使这不可能，多出的4毛钱也应该开发票把？）\n2、物业将进一步加强管理，确保小区的清洁卫生；\n3、物业将与环卫部门联系，将清理垃圾的次数改为一天两次；\n4、车库没有启用的原因是开发商还没有对车库进行验收**（都多长时间了？）**，物业将联系开发商尽快实现车库的验收。\n56、物业将对门卫进一步进行培训，增强管理。\n（zrong：我不相信门卫的素质和物业的素质，真能管好？）\n沟通过程中，物业科不止一次提到，他们只对物业公司负有监管、建议、指导权，言语之中也颇多无奈。其实，这一切的一切，都是制度的原因。崇仁新都的物业公司就是原来的硚口区委招待所的原班人马，思路没有转变，还是事业单位那一套，怎么可能做好服务工作？我不相信他们能做好，确切的说，我坚信他们永远不可能做好。\n看来，即使是诉诸媒体，也没什么用。下面要做的，只能是联系业主，把这个鬼公司赶走。\n","date":"2009-02-16","description":"","lastmod":"2009-02-16T15:26:41Z","slug":"572","tags":["chongrenxindu"],"title":"关于崇仁新都物业公司不作为、越权收费的投诉","url":"https://blog.zengrong.net/post/572/"},{"categories":["technology"],"content":"转自：http://www.adobe.com/cn/products/flashplayer/features/\n3D 效果 (新增) 使用内建 3D 效果支持, 创建更直观、引人入胜的界面。通过在 2D 环境中设计并在 3D 环境中轻松实现变形和动画, 快速入门而无需成为 3D 高手。快速、轻量级、简单易用的 API 以及 Adobe® Flash® CS4 Professional 软件中的 3D 工具使每个人都能通过 ActionScript® 语言或自定第三方库创建出之前只有专家才能制作出的动画。\n自定滤镜和效果 (新增) 为快速吸引用户的电影体验创建高性能的实时效果。借助新的 Adobe Pixel Bender™ - 支持 Adobe After Effects® 软件中众多滤镜和效果的同一技术, 这些动态交互效果可用于 After Effects CS4 中的制作以及 Flash Player 10 中的实时操作。Pixel Bender 即时 (JIT) 编译器还可用于处理声音或数学函数等其它类型的数据, 在一个单独线程中异步进行。\n高级文本支持 (新增) 充分利用新增、灵活的文本布局引擎, 它以 Adobe 超过 25 年的排版专业技术为基础, 将印刷质量的发布引入 Web。使用 ActionScript 3.0 文本组件的可扩展库增强对文本布局的控制, 排列文本和复杂的排版元素, 如跨多列、绕排内联图像、双向、垂直或连在一起的连字。使用现在可以消除锯齿、旋转和自定样式的设备字体创建多语言富 Internet 应用程序 (RIA) 或构建自己的独特文本组件。\n生成动态声音 (新增) 使用增强的声音 API 动态生成音频并创建新的音频应用程序类型, 如混音器和音序器、游戏实时音频、甚至音频观测仪。通过提取音频数据并将它提供给声音缓冲区, 以更低的级别与加载的 MP3 音频配合。通过 Pixel Bender JIT 编译器进行音频实时处理、过滤和混合操作, 令创作自由度超越视觉体验。\n绘制 API (增强) 借助可重新设置样式的属性、3D API 以及无需逐行编码即可绘制复杂形状的新方式, 更轻松地完成运行时绘制。开发人员可以调整部分曲线、更改样式、替换部分以及使用自定义滤镜和效果, 实现更高的吞吐量、工作效率以及创新控制。绘制 API 的增强内容在增加内存和提高性能的同时, 添加了 z 尺寸、真实透视图、3D 空间中的纹理网格、保留的图形模型、读/写呈现以及通过 UV 坐标实现的三角形绘制。\n硬件加速 (增强) 利用图形卡的硬件处理能力将 SWF 文件绘制到浏览器中并加快位图、滤镜、混合模式和视频叠加的合成计算, 与软件处理相比速度更快。\n矢量数据类型 (新增) 使用新的阵列类实现更高的数据性能、效率和错误检查效果。\n动态流 (新增) 借助根据不断变化的网络条件自动调整的流显示出众的视频效果。利用新的服务质量衡量标准提供更出色的流体验。\nSpeex 音频编解码器 (新增) 充分利用新的、高清晰、开放源代码语音编解码器, 它为语音编码提供了等待时间较短的备选方案。Flash Player 还支持 ADPCM、HE-AAC、MP3 和 Nellymoser 音频。\n文件上载和下载 API (增强) 允许用户从您的 Web 应用程序上载并保存文件, 为他们带来真实体验。新的文件参考运行时访问无需往返服务器, 即可实现本地数据处理。\n","date":"2008-11-06","description":"","lastmod":"2008-11-06T08:09:09Z","slug":"565","tags":["flashplayer"],"title":"【转】Flash Player 10 主要功能","url":"https://blog.zengrong.net/post/565/"},{"categories":["others"],"content":"今天装修的时候工人告知停水了，由于使用的是智能水表，只有找物业了解是怎么回事。物业说必须通过他们才能交水费，而且水费是2.3元/吨。这比正常的1.9元/吨的水费贵了0.4元。物业解释说因为是高层，自来水必须用水泵抽上去。但这抽的费用也贵的离谱了把？而且我们交的物业管理费1.4元，也应该包含公共用电的费用啊。最让人不爽的是，这2.3元的水费居然不开发票，我还被理直气壮的告知，没有发票！就是没有发票！没有理由！\n于是开始投诉，下面是投诉过程流水账：\n第一次投诉情况：\n1、拨打12315，被告知他们不管水费的事情，要找水务集团，水务集团如果也不管，就打市长热线12345；\n2、拨打12315提供的水务集团电话被告知是空号，拨打114查询水务集团投诉电话；\n3、水务集团投诉电话打通后，告知这事要打水务集团服务电话；\n4、拨打水务集团服务电话96510，被告知水务集团不管这事情，要我们业主自己与物业公司协商，说他们只会收1.9/吨，不会多收的。多收的让我们自己找物业；\n5、拨打市长热线12345，打不通……\n第一次投诉失败……\n下一步，准备先找媒体试试看，然后继续打市长热线\n","date":"2008-11-04","description":"","lastmod":"2008-11-04T08:50:29Z","slug":"562","tags":["chongrenxindu"],"title":"关于高水价问题的投诉过程","url":"https://blog.zengrong.net/post/562/"},{"categories":["technology"],"content":"偶尔发现原来写的打字测试有问题了，仔细研究了一下，发现是FlashPlayer的原因。\n打字游戏中，对文字输入正确与否的判断，我采用的是TextField的textInput事件和change事件。问题就出在这里：将FlashPlayer升级到10以后，这两个事件的行为发生了改变。\n在FlashPlaye r10以前，如果在TextField中输入整句的文字，文字有多少个，textInput事件和change事件就会发生多少次。而对于FlashPlayer来说，则是不论文字有多少个，只要是一次输入的，就只会发生一次。这种情况对于英文来说倒是无所谓，但对于中文来讲，由于常用的是词语输入，就会有很大影响。看下面的代码：\ntype.mxml\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;absolute\u0026#34;\u0026gt; 3 \u0026lt;mx:TextArea textInput=\u0026#34;textInput(event)\u0026#34; change=\u0026#34;change(event)\u0026#34; width=\u0026#34;100%\u0026#34; height=\u0026#34;100%\u0026#34;/\u0026gt; 4 \u0026lt;mx:Script\u0026gt; 5 \u0026lt;![CDATA[ 6 private function textInput(evt:TextEvent):void 7 { 8 trace(evt.text, evt.target.length); 9 } 10 11 private function change(evt:Event):void 12 { 13 trace(evt.target.text, evt.target.length); 14 } 15 ]]\u0026gt; 16 \u0026lt;/mx:Script\u0026gt; 17\u0026lt;/mx:Application\u0026gt; 以输入“白日依山尽”（整句输入）为例，在FlashPlayer 9中，trace的信息如下：\n白 0\n白 1\n日 1\n白日 2\n依 2\n白日依 3\n山 3\n白日依山 4\n尽 4\n白日依山尽 5\n在FlashPlayer 10中，trace的信息如下：\n白日依山尽 0\n白日依山尽 5\n","date":"2008-10-07","description":"","lastmod":"2008-10-07T08:51:14Z","slug":"flash-player-10-textfield-textinput-change","tags":["actionscript","flashplayer","flex"],"title":"Flash Player 10中关于TextField的textInput与change事件行为的改变","url":"https://blog.zengrong.net/post/flash-player-10-textfield-textinput-change/"},{"categories":["technology"],"content":"通常，我认为只要绑定了一个ArrayCollection，它内部的值更新时，被绑定组件中的数据都会自动更新。但是下面的测试说明并非如此。\n如果更新了ArrayCollection中某个项目的值（并不是通过AddItem等方法更新这个项目，而是直接更新这个项目中的某个值），那么虽然ArrayCollection中的值更新了，但是被绑定的组件并不会接收到更新通知。这时，就要用itemUpdated方法来发送更新事件。\n看下面的例子：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n当输入新的site值并单击“改变rong的site”按钮时，虽然已经改变了 _list 的值，但是dg并没有收到通知，只有使用 _list.itemUpdated 方法发出更新事件，才会更新dg中的显示。\n代码：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;vertical\u0026#34; fontSize=\u0026#34;12\u0026#34; width=\u0026#34;500\u0026#34; height=\u0026#34;160\u0026#34;\u0026gt; 3 \u0026lt;mx:ArrayCollection id=\u0026#34;_list\u0026#34;\u0026gt; 4 \u0026lt;mx:source\u0026gt; 5 \u0026lt;mx:Object index=\u0026#34;1\u0026#34; name=\u0026#39;zrong\u0026#39; site=\u0026#39;cai.mediasky.cn\u0026#39;/\u0026gt; 6 \u0026lt;mx:Object index=\u0026#34;2\u0026#34; name=\u0026#39;orphen\u0026#39; site=\u0026#39;www.98pc.net\u0026#39;/\u0026gt; 7 \u0026lt;/mx:source\u0026gt; 8 \u0026lt;/mx:ArrayCollection\u0026gt; 9 \u0026lt;mx:DataGrid id=\u0026#34;dg\u0026#34; dataProvider=\u0026#34;{_list}\u0026#34; width=\u0026#34;440\u0026#34; height=\u0026#34;100\u0026#34;/\u0026gt; 10 \u0026lt;mx:HBox\u0026gt; 11 \u0026lt;mx:Label text=\u0026#34;输入新Site：\u0026#34; color=\u0026#34;#ffff00\u0026#34; fontWeight=\u0026#34;bold\u0026#34;/\u0026gt; 12 \u0026lt;mx:TextInput id=\u0026#34;ti\u0026#34; width=\u0026#34;100\u0026#34;/\u0026gt; 13 \u0026lt;mx:Button label=\u0026#34;改变zrong的site\u0026#34; click=\u0026#34;_list[0].site=ti.text\u0026#34;/\u0026gt; 14 \u0026lt;mx:Button label=\u0026#34;itemUpdate\u0026#34; click=\u0026#34;_list.itemUpdated(_list[0])\u0026#34;/\u0026gt; 15 \u0026lt;/mx:HBox\u0026gt; 16\u0026lt;/mx:Application\u0026gt; 1 文件 ","date":"2008-08-30","description":"","lastmod":"2008-08-30T04:58:25Z","slug":"update-single-item-in-arraycollection","tags":["collection","flex"],"title":"关于ArrayCollction中单个项目的更新","url":"https://blog.zengrong.net/post/update-single-item-in-arraycollection/"},{"categories":["technology"],"content":"在FMS中变量的作用域一文中，我曾经研究过这个问题，得到的结论是：\n保存在Client中的变量对于每个客户机都是不同的，application中的变量则可以在instance中共享，instance之间不能共享变量。（使用一些变通的方法是可以的）\n这里要讨论的，是FMS中到底有哪几种变量作用域（注：这里不讨论函数内部的变量），对于FMS代码中的自定义类，变量的作用域又是什么呢？在FMS的自定义类中，如何调用其他作用域的变量？\n首先看一段代码：User.asc\n1function User() 2{ 3 varInGlobal = 0; 4 this.varInUser = 0; 5 User.varStatic = 0; 6 application.varInApp = 0; 7} 8 9User.prototype.userTrace = function() 10{ 11 trace(\u0026#39;=====================User.prototype.userTrace\u0026#39;); 12 trace(\u0026#39;varInGlobal:\u0026#39; + varInGlobal); 13 trace(\u0026#39;this.varInUser:\u0026#39; + this.varInUser); 14 trace(\u0026#39;user.varInUser:\u0026#39; + user.varInUser); 15 trace(\u0026#39;User.varStatic:\u0026#39; + User.varStatic); 16 trace(\u0026#39;application.varInApp:\u0026#39; + application.varInApp); 17} 18 19User.userTraceStatic = function() 20{ 21 trace(\u0026#39;=====================User.userTraceStatic\u0026#39;); 22 trace(\u0026#39;varInGlobal:\u0026#39; + varInGlobal); 23 trace(\u0026#39;this.varInUser:\u0026#39; + this.varInUser); 24 trace(\u0026#39;user.varInUser:\u0026#39; + user.varInUser); 25 trace(\u0026#39;User.varStatic:\u0026#39; + User.varStatic); 26 trace(\u0026#39;application.varInApp:\u0026#39; + application.varInApp); 27} 这段代码写在User.asc文件中，与main.asc处于同一个FMS程序var_test下。User是一个自定义对象，在建立User对象的时候，建立了4个变量，第1个没有用任何的修饰符，第2个用this修饰符，第3个用User修饰符，第4个application修饰符。\n另外，建立了两个方法，一个是User类的静态方法userTraceStatic，另一个是实例方法userTrace，两个方法中的代码相同，都是显示这4个变量的值。其中对varInUser变量采用不同的引用方式显示了两遍。\nmain.asc的内容如下：\n1load(\u0026#34;User.asc\u0026#34;); 2 3application.onAppStart = function() 4{ 5 application.registerClass(\u0026#34;User\u0026#34;, User); 6 user = new User(); 7} 8 9application.onConnect = function(client) 10{ 11 trace(\u0026#34;登录成功！\u0026#34;); 12 application.acceptConnection(client); 13 varInGlobal ++; 14 user.varInUser += 10; 15 User.varStatic += 100; 16 application.varInApp += 1000; 17 18 globalTrace(); 19 user.userTrace(); 20 User.userTraceStatic(); 21} 22 23function globalTrace() 24{ 25 trace(\u0026#39;=====================globalTrace\u0026#39;); 26 trace(\u0026#39;varInGlobal:\u0026#39; + varInGlobal); 27 trace(\u0026#39;user.varInUser:\u0026#39; + user.varInUser); 28 trace(\u0026#39;User.varStatic:\u0026#39; + User.varStatic); 29 trace(\u0026#39;application.varInApp:\u0026#39; + application.varInApp); 30} 这段代码中定义了一个全局函数globalTrace，仍然是显示这4个变量的值。在onConnect事件中，改变这4个变量的值，并调用3个显示值的函数。\nFlex 客户端代码：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;vertical\u0026#34;\u0026gt; 3 \u0026lt;mx:Script\u0026gt; 4 \u0026lt;![CDATA[ 5 import flash.net.NetConnection; 6 7 private var nc:NetConnection; 8 9 private function ncClick():void 10 { 11 nc = new NetConnection(); 12 nc.addEventListener(NetStatusEvent.NET_STATUS, ncHandler); 13 nc.connect(\u0026#39;rtmp:/var_test/1\u0026#39;); 14 } 15 16 private function ncHandler(evt:NetStatusEvent):void 17 { 18 trace(evt.info.code) 19 } 20 ]]\u0026gt; 21 \u0026lt;/mx:Script\u0026gt; 22 \u0026lt;mx:Button id=\u0026#34;ncBTN\u0026#34; label=\u0026#34;nc\u0026#34; click=\u0026#34;ncClick()\u0026#34;/\u0026gt; 23\u0026lt;/mx:Application\u0026gt; 执行结果如下（FMS Admin Console中的显示）：\n登录成功！\n=====================globalTrace\nuser.varInUser:10\nvarInGlobal:1\nUser.varStatic:100\napplication.varInApp:1000\n=====================User.prototype.userTrace\nvarInGlobal:1\nthis.varInUser:10\nuser.varInUser:10\nUser.varStatic:100\napplication.varInApp:1000\n=====================User.userTraceStatic\nvarInGlobal:1\nthis.varInUser:undefined\nuser.varInUser:10\nUser.varStatic:100\napplication.varInApp:1000\n由此可见，没有使用任何标示符定义的变量varInGlobal，确实是一个全局变量，不但在自定义类中，还是在其他事件中，都是可以直接访问的。application中的变量其实和user实例中的变量是一样的，必须指定属主才能访问。而User中定义的变量和方法都是静态的，静态的方法无法使用this关键字访问实例中的变量，但可以通过全局变量user访问。\n1 文件 ","date":"2008-08-27","description":"","lastmod":"2008-08-27T03:38:23Z","slug":"fms-variable-scope","tags":["actionscript","fms"],"title":"FMS中变量的作用域-之二","url":"https://blog.zengrong.net/post/fms-variable-scope/"},{"categories":["technology"],"content":" 1. 只为特定的平台开发 2. 不考虑程序的升级性 3. 在程序发布后修改 application ID 4. 没有考虑离线支持 5. 没有在基于AIR平台进行考虑 6. Using custom chrome to create confusing interfaces 7. Not using the seamless install badge 8. Not encrypting sensitive data 9. Not preserving native interaction 10. Assuming performance doesn't matter outside of the browser 详见原文：10 common mistakes when building AIR applications\n","date":"2008-08-20","description":"","lastmod":"2008-08-20T01:57:48Z","slug":"520","tags":["air"],"title":"开发AIR程序的10个常见错误","url":"https://blog.zengrong.net/post/520/"},{"categories":["technology"],"content":"程序框架是这样的一个结构：\n其中Root就是Application，Window和Widget都是Module。Window由Application载入，而Widget由Window载入。\n部分的代码片段：\n1private static const STATUS_WIDGET:String = \u0026#39;widget/status/view/components/StatusWidget.swf\u0026#39;; 2public function loadr():void 3{ 4 _statusMI = ModuleManager.getModule(STATUS_WIDGET); 5 _statusMI.addEventListener(ModuleEvent.READY, _statusReady); 6 _statusMI.load(); 7} 8private function _statusReady(evt:ModuleEvent):void 9{ 10 //此句出错 11 _statusM = StatusWidget((evt.target as IModuleInfo).factory.create()); 12 _statusM.setParent(mediator); 13 top.addChild(_statusM); 14} 就在我要将由Window载入的Module转换成Widget的相关类时（注释下方代码），奇怪的事情出现了：\nTypeError: Error #1034: 强制转换类型失败:无法将 widget.info.view.components::StatusWidget@18ebc29 转换为 widget.info.view.components.StatusWidget\n还有这等事？不能将自己转换成自己？\n略一思考，想起Flex帮助中关于Module域（Module domains）的介绍，找到之后翻了一下，这段话解释了原因：\nBecause a module is loaded into a child domain, it owns class definitions that are not in the main application's domain. For example, the first module to load the PopUpManager class becomes the owner of the PopUpManager class for the entire application because it registers the manager with the SingletonManager. If another module later tries to use the PopUpManager, Adobe ® Flash® Player throws an exception.\n同时提供了解决方法：\nThe solution is to ensure that managers such as PopUpManager and DragManager and any other shared services are defined by the main application (or loaded late into the shell's application domain). When you promote one of those classes to the shell, the class can then be used by all modules. Typically, this is done by adding the following to a script block:\nimport mx.managers.PopUpManager;\nimport mx.managers.DragManager;\nprivate var popUpManager:PopUpManager;\nprivate var dragManager:DragManager;\n简单的说，这种情况是由于主文件的域与子文件的域相对独立，对于同一个类，会分别进行载入，这就造成了在比较时，两个类并不相同。解决的办法，就是在主文件中，先对这个类进行一次引用。例如，我在Root.mxml中加入这段代码，程序就没问题了：\n1import mx.modules.ModuleManager; 2import mx.modules.Module; 3import widget.status.view.components.StatusWidget; 4 5private var _moduleManager:ModuleManager; 6private var _module:Module; 7private var _status:StatusWidget; ","date":"2008-08-14","description":"","lastmod":"2008-08-14T17:25:18Z","slug":"module-convert-class-a-to-a","tags":["as3","flex","module"],"title":"Module的域问题：无法将类A转换成类A？","url":"https://blog.zengrong.net/post/module-convert-class-a-to-a/"},{"categories":["others"],"content":"购买HP MiniNote 2133的原因，是经常会出差（旅途漫漫无心睡眠），而14寸的本本带着太累，就买了这个小点的。但2133原配的3芯电池续航能力不到2小时，用来看电影的话，只看一部片子就没电了，而出差时经常会在火车上呆一整天。所以一直考虑买块大点的电池。原装的6芯电池号称可以续航6小时（我认为没那么夸张，看电影肯定撑不了6小时），就是价格太贵，要600块（抢钱啊），而且通用性不强。于是考虑买一块外置移动电源。\n在淘宝上入手Traveller APC-20000移动电源，可以外接大部分笔记本电脑，为手机充电等等。接头也非常齐全。这样就充分保护了投资，万一以后这台笔记本电脑不用了，还可以用到另一台上。\n到手之后就准备测试一下这块号称3.7V / 20000mAh / 74WH的电池，究竟有多弓虽！ :cool:\n先看张图把：\n该产品没有厂名、厂址，包装盒内只有一张说明书，说明书上有一个联系电话，再就是电池的本体和一大堆接头。明显是山寨厂的产品了 :em06: ，不过我倒不在乎这个，毕竟便宜、好用、经用才是正道。\n我的测试非常简单：把笔记本原配电池卸下，只使用外接移动电源（充满电），正常使用电脑，包括文本处理、上网浏览、视频播放混合进行，一直到外接移动电源彻底没电为止。由于没有连续工作很长时间，因此测试是在两天内分4次完成的，每次关机都关闭移动电源。\n结果见下：\n第一次：网页操作 10-30%处理器占用，49分钟（100%电量指示灯5分钟后熄灭）； 第二次：下载操作，10-20%处理器占用，45分钟（75%点亮指示灯30分钟后熄灭）； 第三次：播放视频，50-70%处理器占用，60分钟； 第四次：网页操作和下载，20-100%处理器占用，52分钟（彻底无电）。 共耗时206分钟，约3.4小时。\n原配电池（3芯，10.8V / 28WH）使用时间约1.6小时。\n从标注容量上看，74÷28≈2.64，而3.4÷1.6≈2.12，但由于移动电源工作在19V，而原配电池工作在10.8V，因此移动电源的损耗应该更大一些。所以，该电池的标注还是比较合理的，虚标的量并不多，在10%左右。\n后来又换了一台笔记本（神州优雅 HP520）测试，主要进行下载工作，使用时间是3小时。需要注意的是，神州优雅HP520与HP MiniNote 2133配置完全不是一个级别的，前者是Intel酷睿双核处理器，2G内存、DVD刻录机，而后者是VIA1.2G单核低功耗处理器，不带光驱。\n","date":"2008-07-25","description":"","lastmod":"2008-07-25T15:11:44Z","slug":"503","tags":[],"title":"Traveller APC-20000 移动电源简单评测","url":"https://blog.zengrong.net/post/503/"},{"categories":["others"],"content":"Show一下：我正在用的联想昭阳260（14寸）、HP MiniNote 2133（8.9寸）和多普达D900（3.7寸，手机）的对比图。\n多普达900上面放的是5号（AA）充电电池。\n","date":"2008-07-24","description":"","lastmod":"2008-07-24T14:26:24Z","slug":"484","tags":["mobile","hp"],"title":"HP MiniNote 2133图","url":"https://blog.zengrong.net/post/484/"},{"categories":["tutorial"],"content":" 2008年7月23日9:53:35更新：添加第一期班所有录屏文件 2008年7月24日9:25:10更新：添加第二期班部分录屏文件 2008年7月25日23:54:03更新：添加第二期所有录屏文件和源程序 经过培训内容调查和在线报名，2008动态网站快速架设与开发培训班已经火热开班！目前第一期班已经结束，第二期班正在进行中。 进行了二期培训并圆满结束。为了方便参加培训的学员进行复习，也方便没有来参加培训的老师能学到对自己有用的东西，现将培训过程的录屏文件予以发布。录屏文件包括教师授课过程中的电脑屏幕上所有的操作和教师的同步语音，希望对大家有帮助。\n注意：两期培训的内容是基本相同的，只是根据学员的接收程度，第二期进行了少许调整。建议大家下载第二期的录屏文件，同时，第一期班的第一天（7月14日）由于设备没有准备好，没有进行录屏。\n如果有不能下载的，请留言或者E-mail联系我：\n录屏文件播放器，下载其中之一即可：\n2 文件 源文件下载：\n1 文件 第二期班（7月20-24日）录屏文件：\n17 文件 第一期班（7月14-18日）录屏文件：\n10 文件 ","date":"2008-07-22","description":"","lastmod":"2008-07-22T15:38:54Z","slug":"463","tags":["mysql","php","wordpress","training","capture"],"title":"2008动态网站快速架设与开发培训班录屏文件发布","url":"https://blog.zengrong.net/post/463/"},{"categories":["others"],"content":"刚到香港一游，这次的运气非常好，每天出门的时候都遇到暴雨，到了但到了景点后一般都停止了，所以在海洋公园坐过山车都不用排队，“跳楼机”甚至是我一个人坐的。迪斯尼的人也不多。\n现在把一些经验和体会写出来，不一定完全正确，但起码是点经验：\n一、关于港币\n不用带太多的港币，甚至根本不用带港币。因为大多数的店都可以直接收人民币。但是收人民币的店又分为几种情况：\n只收100的人民币，不收其他面额的人民币，而且只找回港币；（比如SaSa） 收任何面额的人人民币，但只找回港币； 收取人民币，但仅按照1比1的面值收取，这对于目前升值的人们币来说是非常不划算的； 收取人民币，也找回人民币。 购大件建议刷卡，刷Visa或者Master都可以，当然最好是刷银联。如果用信用卡的话，大店的店员都会问你是刷信用卡还是刷银联。我都是选择银联，因为银联会按当时的汇率折算成人民币。如果是储蓄卡，就直接下账，如果是信用卡，回到内地直接还人民币就可以了。如果刷Visa或者Master，那么回内地后就要购汇还款，要麻烦一点。\n如果一定要换港币的话，在香港2天换500应该就足够了。如果忘了在内地换，香港也到处都是兑换点，不过有的兑换点可能要收手续费。\n我是7月6日在广州换的1000元港币，当时的买入价是0.88左右。所以，在香港买什么东西都相当于八八折了。当时换人民币的是农行，要用身份证，还要填一大堆单子，很麻烦。在香港时由于基本上都是刷卡，到了最后一天手上还有800多港币。后来在深圳换回的人民币。深圳换人民币很方便，很多兑换点，在兑换点兑换也不用身份证。\n二、关于购物\n化妆品当选SaSa。铜锣湾、旺角到处都是。电子产品最好是去连锁店买。时代广场7楼的百老汇、国美都有电子产品卖。我感觉百老汇的店子多些。在旺角，甚至一条街有3、4个百老汇。店子有大有小，但东西都差不多。店员大都对产品比较熟悉，如果你对电子产品不懂，也可以请他们帮你挑。在标价的基础上，很多都有折扣。始终记住一点，不管买什么，在折扣的基础上，你买的都是八八折。^_^\n比如我买的HP MiniNote 2133笔记本，在广州标价6999，我还价到人民币6050；在香港标价港币4999，打了个折扣4847拿下，折合人民币不到4300；回到深圳我去了华强（电脑城真大啊！起码有3个南极电脑城那么大），标价6999，HP做活动到5999，还价到5600；回到武汉，南极电脑城标价6999，还价到5900。好大差价啊！！！深圳比武汉贵300，比香港贵700（不考虑汇率）。不过，香港版的2133CPU是1.2G，带的是VISTA系统，内地的CPU是1.6G，带XP系统。其他一模一样。\n香港的电脑城和内地的差不多，但不还价（也许因为我是内地人不给我还价），价格什么的没仔细看，但东西比武汉的电脑城要全。比如USB HUB，武汉只能买到20元左右的，做工差不说，也不好看，而我在香港电脑城一个小店，就看到一面墙挂的全部都是USB HUB，居然还有三星产的（不知道是不是真的），做工非常精致，各种样子、形状、颜色的都有。\n买电器还要注意的一点就是保修。建议买国际品牌，很多品牌是支持全球保修的，这样到了内地保修也没问题。如果买的品牌不支持全球保修，可以去香港国美买，到了内地一样可以保修的。\n三、关于穿衣\n去之前就听说香港室内空调会打得很低，于是带了件长袖衣服。但最后还是一次也没穿。并非是传闻错误……空调确实打得很低……而且和武汉不同，香港的司机似乎并不在乎省油的问题，车里的空调也是开得低低的，很冷。但我觉得还在可以承受的范围之内，就没有加衣服了。同去的几个MM倒是冷得受不了，买了衣服加上（这也是买衣服的一个理由么 :mrgreen: ）\n四、关于语言\n酒店服务员和商场售货员基本都会说普通话，而且说得还不错，至少我都能听懂。他们见面第一句问候一般都是广东话，如果你用普通话回应的话，他们马上就会转成普通话。相对而言，香港的服务人员比较热情。我在酒店的餐厅喝咖啡时，找服务员打听地方，那服务员前前后后和我说了有十几分钟，总算说清楚了，最后来了一句“我普通话讲的不好……”，其实讲的已经很好了，如果我的普通话能打10分的话，她的也能打6分了。\n当然，也有服务态度不好的。有个香港朋友陪我去百老汇看蓝牙耳机。柜台上的售货员就爱理不理。我听不懂他们的对话，最后同行的香港朋友拉我走了。他说让售货员开包装看看，可售货员不愿意开，又不是只这一家，我们换一家。结果换了一家，也是百老汇，就在一条街上，售货员态度就天壤之别，买了蓝牙耳机，最后还说有赠品，拿了Adidas的零钱袋和几个小饰品让我挑选，自始至终都很热情。\n不过我这位香港朋友的普通话水平就不敢恭维了，我和他交流，很多词要重复好几遍才能搞懂，实在不懂就写字或用英文 :em06: 说一段话急得满头汗。我给他的普通话水平打2分。普通香港市民的普通话也很差，大街上找人问路就比较困难，所以出门最好带上GPS。\n即使是普通话好的，由于很多常用语不同，也要注意。我买笔记本电脑的时候，售货员普通话不错，告诉我系统自带的vista如何安装，说看到提示的时候按“F Ten”，我一下子没听懂，停了1秒才想过来，原来他说按“F10”啊，我从来没想过把“F10”读成“F Ten”的 :em02:\n","date":"2008-07-13","description":"","lastmod":"2008-07-13T15:43:17Z","slug":"461","tags":["journey"],"title":"香港自由行经验","url":"https://blog.zengrong.net/post/461/"},{"categories":["tutorial"],"content":"经过5天的在线调查，已经确定了培训内容和时间，详情请见关于举办“动态网站快速架设与开发培训班”的通知\n请需要培训的老师直接在下面的表单中报名，报名后，不必再进行电话报名或电子邮件报名。通知的纸质版我们会寄送给市（州）县电教馆站，如需要单独寄送，请留言提出并留下详细通信地址、邮编和联系人。\n2008年7月13日更新：\n第一期报名已经停止，第二期7月19日报到，如希望参加第二期班，可电话报名。\n","date":"2008-06-25","description":"","lastmod":"2008-06-25T04:00:57Z","slug":"460","tags":["training"],"title":"2008动态网站快速架设与开发培训班在线报名","url":"https://blog.zengrong.net/post/460/"},{"categories":["tutorial"],"content":"调查已经结束，现公布截止至2008-6-24 03:42:36 的暑期培训调查结果：\n","date":"2008-06-20","description":"","lastmod":"2008-06-20T08:07:45Z","slug":"459","tags":["training"],"title":"2008暑期培训内容调查表（已结束调查，公布结果）","url":"https://blog.zengrong.net/post/459/"},{"categories":["technology"],"content":"pureMVC的官方文档，介绍pureMVC用法、特点、结构。\n介绍 下载\n我已经使用pureMVC2个月左右，感觉确实是很不错的框架，上手也不难，当时是看了几个官方范例之后就开始动手改原来写的项目了。强烈推荐！\n","date":"2008-06-15","description":"","lastmod":"2008-06-15T03:48:03Z","slug":"458","tags":["framework","mvc","design-pattern"],"title":"pureMVC实现、术语阐述及最佳实践（中文版）","url":"https://blog.zengrong.net/post/458/"},{"categories":["technology"],"content":"今天重装了系统，Subversion服务也得重装。照例输入代码：\nsc create svnserve binpath=\u0026quot;d:\\PortableApps\\svn\\svnserve.exe --service -r e:\\svn\u0026quot; displayname=\u0026quot;Subversion\u0026quot; depend=tcpip start=auto 可是居然出现了下面的提示：\nD:\\PortableApps\\svn\u0026gt;sc create svnserve binpath=\u0026quot;d:\\PortableApps\\svn\\svnserve.exe --service -r e:\\svn\u0026quot; displayname=\u0026quot;Subversion\u0026quot; depend=tcpip start=auto Creates a service entry in the registry and Service Database. SYNTAX: sc create [service name] [binPath= ] ... CREATE OPTIONS: NOTE: The option name includes the equal sign. type= (default = own) start= (default = demand) error= (default = normal) binPath= group= tag= depend= obj= (default = LocalSystem) DisplayName= password=\n我仔细检查注册表，没有同名的服务啊！浪费了半小时，才搞清楚，binpath=、dispalyname=、depend=后面是必须要有一个空格的。否则就会执行不成功。这语法，太奇怪了……\n所以，正确的代码应该是这样：\nsc create svnserve binpath= \u0026quot;d:\\PortableApps\\svn\\svnserve.exe --service -r e:\\svn\u0026quot; displayname= \u0026quot;Subversion\u0026quot; depend= tcpip start= auto 那么，原来我是怎么执行成功的呢？仔细想了想，以前的代码是直接从SVN帮助中复制的 :em23:\n","date":"2008-06-14","description":"","lastmod":"2008-06-14T14:08:52Z","slug":"457","tags":["svn","windows"],"title":"使用sc命令创建svn服务的问题","url":"https://blog.zengrong.net/post/457/"},{"categories":["technology"],"content":"在使用mx.validator包的时候，我一直都希望检测没有通过的错误信息能够直接显示出来，而不是等我把鼠标移动到没有通过的组件上再显示。我认为这能带给用户更好的体验。因为用户能在第一时间就知道自己到底填错了什么，况且，并不是所有的用户都知道必须把鼠标移动到出错的组件上才能看到错误信息的。很多用户在控件的边框变红，并且不能够继续提交之后不知所措。（我认为，那个红色并不明显，还不至于让用户知道必须把鼠标移动到组件上去，尤其是在页面中的组件相当多的时候……）\n今天翻看Flex 3的帮助，找到了解决的办法。\n关键在于ToolTipManager类的使用。使用ToolTipManager类的createToolTip方法可以直接生成一个ToolTip进行显示。语法很简单，只需要指定要显示的文字，x、y值即可。如果坐标值是基于组件的，则需要使用localToGlobal来转换坐标系。\n一个有趣的地方是ToolTip的箭头方向。createToolTip的第4个参数是指定箭头。如果为空，就不显示箭头。如果为下面三个字符串值中的一个，则会显示箭头：\nerrorTipAbove errorTipRight errotTipBelow 是的，没有 errorTipLeft。\n另一个有趣的地方是关于ToolTip的样式。对于createToolTip生成的ToolTip，可以用ToolTip选择符控制它的样式。但是这还取决于createToolTip的第4个参数。如果这个参数有值，那么就必须使用.errorTip这个css类来控制它的样式了。\n1.errorTip 2{ 3 font-size: 12; 4} .errorTip会影响Validator的样式，所以，可以重新定义专有样式，并在生成toolTip的时候赋值给它。\n1.testTip 2{ 3 font-size: 12; 4 border-color: #ffffdd; 5 color: #ff0000; 6 font-weight: bold; 7} 8_tip.styleName = \u0026#39;testTip\u0026#39;; createToolTip生成的ToolTip，必须用destoryToolTip来清除。如果在清楚之前再次调用createToolTip，则会生成重复的ToolTip。ToolTipManager有一个currentToolTip属性来保存当前显示的ToolTip，但这个属性对于使用createToolTip创建的ToolTip并没有效果。因此还是老老实实的在生成的时候用变量把它保存下来把。\n源码：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;vertical\u0026#34; width=\u0026#34;400\u0026#34; height=\u0026#34;150\u0026#34; horizontalAlign=\u0026#34;center\u0026#34; verticalAlign=\u0026#34;middle\u0026#34;\u0026gt; 3 \u0026lt;mx:Style\u0026gt; 4 global 5 { 6 font-size: 12; 7 theme-color: haloSilver; 8 } 9 Application 10 { 11 background-color: #dddddd; 12 } 13 .errorTip 14 { 15 font-size: 12; 16 } 17 .testTip 18 { 19 font-size: 12; 20 border-color: #ffffdd; 21 color: #ff0000; 22 font-weight: bold; 23 } 24 \u0026lt;/mx:Style\u0026gt; 25 \u0026lt;mx:Script\u0026gt; 26 \u0026lt;![CDATA[ 27 import mx.managers.ToolTipManager; 28 import mx.controls.ToolTip; 29 30 private var _tip:ToolTip; 31 32 private function _showTip($txt:String):void 33 { 34 trace(_tip); 35 if(_tip == null) 36 { 37 var __point:Point = new Point(emailTI.x, emailTI.y); 38 trace(__point) 39 __point = emailTI.localToGlobal(__point); 40 trace(__point); 41 _tip = ToolTipManager.createToolTip( $txt, 42 __point.x - emailTI.x, 43 __point.y - 40 , 44 \u0026#39;errorTipAbove\u0026#39;) as ToolTip; 45 _tip.styleName = \u0026#39;testTip\u0026#39;; 46 } 47 } 48 49 private function _destoryTip():void 50 { 51 if(_tip) 52 { 53 ToolTipManager.destroyToolTip(_tip); 54 } 55 _tip = null; 56 } 57 ]]\u0026gt; 58 \u0026lt;/mx:Script\u0026gt; 59 \u0026lt;mx:EmailValidator id=\u0026#34;emailV\u0026#34; source=\u0026#34;{emailTI}\u0026#34; property=\u0026#34;text\u0026#34; trigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34;/\u0026gt; 60 \u0026lt;mx:Form horizontalCenter=\u0026#34;0\u0026#34;\u0026gt; 61 \u0026lt;mx:FormItem label=\u0026#34;电子邮件：\u0026#34; width=\u0026#34;100%\u0026#34;\u0026gt; 62 \u0026lt;mx:TextInput id=\u0026#39;emailTI\u0026#39; width=\u0026#34;100%\u0026#34;/\u0026gt; 63 \u0026lt;/mx:FormItem\u0026gt; 64 \u0026lt;mx:FormItem horizontalAlign=\u0026#34;center\u0026#34; width=\u0026#34;100%\u0026#34; direction=\u0026#34;horizontal\u0026#34;\u0026gt; 65 \u0026lt;mx:Button id=\u0026#34;btn\u0026#34; label=\u0026#34;提交\u0026#34;/\u0026gt; 66 \u0026lt;mx:Button id=\u0026#39;btn2\u0026#39; label=\u0026#34;显示Tip\u0026#34; click=\u0026#34;_showTip(\u0026#39;测试弹出Tip\u0026#39;)\u0026#34;/\u0026gt; 67 \u0026lt;mx:Button id=\u0026#39;btn3\u0026#39; label=\u0026#34;取消Tip\u0026#34; click=\u0026#34;_destoryTip()\u0026#34;/\u0026gt; 68 \u0026lt;/mx:FormItem\u0026gt; 69 \u0026lt;/mx:Form\u0026gt; 70\u0026lt;/mx:Application\u0026gt; 范例效果：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2008-05-25","description":"","lastmod":"2008-05-25T14:14:43Z","slug":"show-tooltip-and-errortip-whithout-mousemove","tags":["as3","flex","validator"],"title":"直接显示ToolTip和errorTip（不需要将鼠标移到组件上）","url":"https://blog.zengrong.net/post/show-tooltip-and-errortip-whithout-mousemove/"},{"categories":["news"],"content":"这里列举了10个受欢迎的AIR程序。其中居然有可以在AIR中编辑Joomla!后台的Joomledit。看来WordPressEdit也不远了。 :wink:\n","date":"2008-04-09","description":"","lastmod":"2008-04-09T13:46:52Z","slug":"454","tags":["air","cms","joomla"],"title":"10个受欢迎的AIR程序","url":"https://blog.zengrong.net/post/454/"},{"categories":["technology"],"content":"看这里，包含了很多有用的调试工具。其中我比较感兴趣的是Debugging AS3 with firebug’s console，可以在Firebug中显示调试信息。另外Flash Switcher extension for Firefox我一直在用了，目前最新版本是2.1.5。alcon是第一次看到，貌似用起来很简单。\n不过，Flex的mx.logging包已经足够强大，有时间的话，自己写个调试器也是不错的。\n2008年5月1日16:07:22更新：\nPlum调试框架\n","date":"2008-04-08","description":"","lastmod":"2008-04-08T15:58:00Z","slug":"452","tags":["debug","flash","flex"],"title":"Flex和Flash调试工具列表","url":"https://blog.zengrong.net/post/452/"},{"categories":["web"],"content":"今天把Blog程序升级到了WordPress2.5，它多了许多新的特性，确实值得升级。\n具体的内容，可以看看香草吧噗翻译的官方介绍：WordPress 2.5\n","date":"2008-04-02","description":"","lastmod":"2008-04-02T01:43:05Z","slug":"449","tags":["wordpress"],"title":"升级到WordPress2.5","url":"https://blog.zengrong.net/post/449/"},{"categories":["impressions"],"content":"昨天听群里的朋友说可以开始收房了，但复式楼可能要晚几天。我打电话给售楼部，售楼部说：“本来我们要晚几天的，现在都提前做好了，你来收房吧，把合同带上。”。我说收房公告上不是说4月30号前收房都可以的吗？售楼部的小姐说：“不是的，我们一个星期就会撤的，如果你等我们走了再来收房，就很麻烦，到时候我们很可能无法帮你。”。\n这段话很有点威胁的意味，但也确实起了作用。于是我约上武汉验房网的两个验房师，下午请假兴冲冲的去验房。\n验房师很准点到了。我却找半天都找不到收楼的办公室。因为没有任何横幅和指示。问了半天，才找到了原来的售楼部。售楼部里面坐着一男一女。男的就是一年多以前卖给我房子的人。一见面，他们就要我办手续。我说：“我要先看看房子”，他们却说先办完手续再去看房不迟。我不上这个当，要求他们立刻带我去看房，看完没问题我再收。\n他们带我到三楼的物业管理公司。里面一堆人。一个太婆要我马上办手续，我说先收房。她问我是几号。我说16楼14号，她让我等。我很耐心的等了十分钟。验房师也在外面等我。我见没有理我的意思，再问他们要钥匙去看房。那太婆再次问我是几号，我再说一遍，她就去找钥匙。找了一会儿没动静，我再催。终于找到钥匙去等电梯。电梯来了，她再问我是几号，我又说一遍。她说，你不是10楼的么？我说我是16楼，你记错了。她回去换钥匙，让我等。\n我等了5分钟，发现她没有出来的迹象，便进去找她。她再次（我已记不清是第几次）问我是几号房。我很平静的告诉她房号。她找了半天，说：“没有16楼的钥匙，复式的全部都没有交房，钥匙在施工队手上。你去找售楼处。”我当时就晕了，这物业公司到哪里找的这么晕的人！我说你没钥匙你不早点说？施工队还在施工你要我来收房？她说那没办法，你去找售楼处，我管不了。然后就是一副与其无关的表情，不再理我了。\n因为验房师还在等，我立刻和他们上了16楼。偏偏不巧，除了我的房子门是锁的以外，其他房门全部开着。我再次去售楼处找人。我说：“复式还没交给你们钥匙，你们干嘛找我来收房？”他们说：“复式没钥匙么？”我说：“你们居然不知道？”他们说：“那你就过几天来把，6号复式的才能弄好。”我本来想发点脾气，教训他们几句，想想也就算了，何必和自己过不去呢？难得有个好心情。\n我回到16楼，给两个验房师道歉，让他们先回去。我走到17、18楼我的户型看了。虽然过年前我也来看过，但当时没有隔中间的楼板。这景象简直让我大吃一惊。楼板看来是没有指望，中间用工字钢架起来，工字钢在两边墙上凿出的洞还没有补。工字钢秘密麻麻，看起来很有点恐怖。\n最要命的是，工字钢架到我2楼的厨房，就没有了，导致我从2楼根本无法去厨房，除非我在一楼搭个绳梯上去。\n我看了复式的所有房型。基本上都差不多，很多房型的窗户还没有装。想起自己这么着急想来验房，实在是浪费时间。下楼的时候，突然想起今天是4月1日，才知道自己被开发商给“愚”了一把。 :em47:\n","date":"2008-04-01","description":"","lastmod":"2008-04-01T14:19:52Z","slug":"448","tags":["house"],"title":"验房，愚人节的玩笑？","url":"https://blog.zengrong.net/post/448/"},{"categories":["news"],"content":"转自：www.52wordpress.cn\n1.高度决定视眼。所处的位置不同，你关注的问题不同，与你打交道，生活的圈子也不相同，你的心态也不相同。\n2.成功者总是被研究而不是研究别人。所以我们千万别相信所谓的it评论家。\n3.决定你工资的不是老板，而是你自己。所以不管什麽时候，什么待遇，首先你必须证明你的价值，然后一切纷至沓来。\n4.公司成功了，没你什么事，公司做坏了，也没您什么事情。这样的工作，是无趣的。所以我们应该更相信那些新兴的，不断有创造力和发展潜力的公司。而不是一个已经成功已经被称之伟大的公司。大家看吧，最近Google流失了多少人才，他们去了那里？\n5.能量越高越不稳定。这条物理规律在很多领域同样实用。\n6.”你对你自己的职业生涯有什么规划，你对你自己以年后的工作情况有什么期望和想法？”这是我面试的时候几乎必问的一个问题。一个连自己的职业生涯都没有规划，对自己的未来都没有想法和期望的人，你怎么相信他能对一个网站有好的规划，不断的有好的想法？\n7.世界上有三种客户，一种是挑剔者，这种人合作起来很郁闷和痛苦，稍微有些地方服务不好，就要大发雷霆；一种是理性的客户，对于他的不满意，如果你能给出合理的解释，他们也很容易接受；第三种是，温顺忍受型客户。即使你服务不好，他也不会主动地给你发表不满的意见。 我们最需要服务好的是第3种客户。我们现实生活种这类人最多。他们不会用最嘴说话，不会提出抗议或者建议，而是用脚说话。做网站也一样，如果我们不注重那些背后隐藏的不说话的用户的体验，他们悄然离开，连一只批评的意见都不会留下，这是最可怕的。\n8.不要害怕别人说你傻。尤其是当你的客户都觉得你傻，他们占了便宜的时候，你就不用担心他们以后不跟你合作了。在客户面前，尤其是利益方面，傻可能比聪明更重要。\n9.要留住一个员工，最好的办法是让满足他的兴趣，刺激他的成就感并让他有不断学习和提升的机会。\n10.模式是可以轻易抄袭的。无法抄袭的是思维方法和创新体系。所以方法论比具体的解决方法更重要，所以建立一个良好的创新体系比挖一个厉害的创新天才更重要。\n别把任何人当作竞争对手。只要你一只往前，一直爬山，回过头看，发现以前的所有的竞争对手，不过是你的一个路标或者一个山峰而已。\n","date":"2008-03-22","description":"","lastmod":"2008-03-22T17:50:53Z","slug":"447","tags":["live"],"title":"【转】几点感悟","url":"https://blog.zengrong.net/post/447/"},{"categories":["technology"],"content":" Singleton 单件模式 Abstract Factory 抽象工厂模式 Builder 生成器模式 Factory Method 工厂方法模式 Prototype 原型模式 Adapter 适配器模式 Bridge 桥接模式 Composite 组合模式 Decorator 装饰模式 Facade 外观模式 Flyweight 享元模式 Proxy 代理模式 Template Method模板方法 Command 命令模式 Interpreter 解释器模式 Mediator 中介者模式 Iterator 迭代器模式 Observer 观察者模式 Chain Of Responsibility 职责链模式 Memento 备忘录模式 State 状态模式 Strategy 策略模式 Visitor 访问者模式 （转自）\n","date":"2008-03-09","description":"","lastmod":"2008-03-09T04:30:32Z","slug":"design-pattern-english-chinese","tags":["design-pattern"],"title":"【转】23种设计模式中英文对照","url":"https://blog.zengrong.net/post/design-pattern-english-chinese/"},{"categories":["technology"],"content":"Installing and running AIR applications from a web page\nDeploying Adobe AIR applications seamlessly with badge install\n","date":"2008-03-09","description":"","lastmod":"2008-03-09T03:20:26Z","slug":"air-express-install-resources","tags":["air","flashplayer","ria"],"title":"AIR在线快速安装资源","url":"https://blog.zengrong.net/post/air-express-install-resources/"},{"categories":["technology"],"content":"在上例“用Validator检测必填项”中，我记录了Validator最简单的用法。但mx.validators包中的类并非只有Validator一个，他们可以实现信用卡号码格式检测（mx.validators.CreditCardValidator）、货币格式检测（mx.validators.CurrencyValidator）、E-mail格式检测（mx.validators.EmailValidator）等等功能，所有的检测器列表可以看这里。这些类都是Validator的子类。\n先看效果。下面的范例演示了StringValidator、NumberValidator和EmailValidator的用法。由于这三个类都继承自Validator，因此都拥有requiredFieldError属性，用于自定义没有值的时候的错误信息。但是这三个类拥有更多的错误信息。要检测的值越复杂，需要定义的错误信息就越多，例如EmailValidator，本例中共定义了9个错误信息。\n如果不定义错误信息，Flex会显示默认的英文错误信息，这显然也不是我们所需要的。如果想偷懒的话，可以定义几个最可能出现的错误，例如本例的NumberValidator，就没有定义所有的错误信息。\n顺便还要说一句的是，NumberValidator可以指定要检测的数字是整数还是实数，这需要用domain属性来指定。同时，它还可以指定千分位分隔符。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n这个效果的源码如下：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;vertical\u0026#34; fontSize=\u0026#34;12\u0026#34; width=\u0026#34;300\u0026#34; height=\u0026#34;200\u0026#34;\u0026gt; 3 \u0026lt;mx:Style\u0026gt; 4 \u0026lt;![CDATA[ 5 .errorTip 6 { 7 fontSize: 12; 8 } 9 ]]\u0026gt; 10 \u0026lt;/mx:Style\u0026gt; 11 \u0026lt;mx:StringValidator id=\u0026#34;nameV\u0026#34; source=\u0026#34;{nameTI}\u0026#34; property=\u0026#34;text\u0026#34; 12 minLength=\u0026#34;2\u0026#34; 13 maxLength=\u0026#34;5\u0026#34; 14 requiredFieldError=\u0026#34;必须输入姓名！\u0026#34; 15 tooShortError=\u0026#34;姓名过短！\u0026#34; 16 tooLongError=\u0026#34;姓名过长！\u0026#34; 17 trigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34;/\u0026gt; 18 \u0026lt;mx:NumberValidator id=\u0026#34;ageV\u0026#34; source=\u0026#34;{ageTI}\u0026#34; property=\u0026#34;text\u0026#34; 19 domain=\u0026#34;int\u0026#34; 20 minValue=\u0026#34;6\u0026#34; 21 maxValue=\u0026#34;100\u0026#34; 22 lowerThanMinError=\u0026#34;年龄过小！\u0026#34; 23 exceedsMaxError=\u0026#34;年龄过大！\u0026#34; 24 integerError=\u0026#34;年龄必须是整数！\u0026#34; 25 invalidCharError=\u0026#39;输入了非数字字符！\u0026#39; 26 requiredFieldError=\u0026#34;必须输入年龄！\u0026#34; 27 trigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34;/\u0026gt; 28 \u0026lt;mx:Validator id=\u0026#34;sexV\u0026#34; source=\u0026#34;{sexRBG}\u0026#34; property=\u0026#34;selectedValue\u0026#34; 29 requiredFieldError=\u0026#34;必须选择性别！\u0026#34; 30 trigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34; 31 listener=\u0026#34;{maleRB}\u0026#34;/\u0026gt; 32 \u0026lt;mx:EmailValidator id=\u0026#34;emailV\u0026#34; source=\u0026#34;{emailTI}\u0026#34; property=\u0026#34;text\u0026#34; 33 requiredFieldError=\u0026#34;必须输入E-mail\u0026#34; 34 invalidCharError=\u0026#34;E-mail地址中有错误字符。\u0026#34; 35 invalidDomainError=\u0026#34;E-mail地址中的域名不符合规范。\u0026#34; 36 invalidIPDomainError=\u0026#34;E-mail地址中的IP格式域名不符合规范。\u0026#34; 37 invalidPeriodsInDomainError=\u0026#34;域名中的“.”错误。\u0026#34; 38 missingAtSignError=\u0026#34;E-mail地址缺少“@”符号。\u0026#34; 39 missingPeriodInDomainError=\u0026#34;域名中缺少“.”\u0026#34; 40 missingUsernameError=\u0026#34;E-mail地址缺少用户名。\u0026#34; 41 tooManyAtSignsError=\u0026#34;E-mail地址中的“@”符号太多。\u0026#34; 42 trigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34;/\u0026gt; 43 44 \u0026lt;mx:FormItem label=\u0026#34;姓名：\u0026#34; width=\u0026#34;200\u0026#34;\u0026gt; 45 \u0026lt;mx:TextInput id=\u0026#34;nameTI\u0026#34;/\u0026gt; 46 \u0026lt;/mx:FormItem\u0026gt; 47 \u0026lt;mx:FormItem label=\u0026#34;年龄：\u0026#34; width=\u0026#34;200\u0026#34;\u0026gt; 48 \u0026lt;mx:TextInput id=\u0026#34;ageTI\u0026#34;/\u0026gt; 49 \u0026lt;/mx:FormItem\u0026gt; 50 \u0026lt;mx:FormItem label=\u0026#34;性别：\u0026#34; direction=\u0026#34;horizontal\u0026#34; width=\u0026#34;200\u0026#34;\u0026gt; 51 \u0026lt;mx:RadioButtonGroup id=\u0026#34;sexRBG\u0026#34;/\u0026gt; 52 \u0026lt;mx:RadioButton id=\u0026#34;maleRB\u0026#34; groupName=\u0026#34;sexRBG\u0026#34; label=\u0026#34;男\u0026#34; value=\u0026#34;1\u0026#34;/\u0026gt; 53 \u0026lt;mx:RadioButton id=\u0026#34;femaleRB\u0026#34; groupName=\u0026#34;sexRBG\u0026#34; label=\u0026#34;女\u0026#34; value=\u0026#34;0\u0026#34;/\u0026gt; 54 \u0026lt;/mx:FormItem\u0026gt; 55 \u0026lt;mx:FormItem label=\u0026#34;E-mail：\u0026#34; width=\u0026#34;200\u0026#34;\u0026gt; 56 \u0026lt;mx:TextInput id=\u0026#34;emailTI\u0026#34;/\u0026gt; 57 \u0026lt;/mx:FormItem\u0026gt; 58 \u0026lt;mx:Button id=\u0026#34;btn\u0026#34; label=\u0026#34;提交\u0026#34; /\u0026gt; 59\u0026lt;/mx:Application\u0026gt; 写本篇标题时，本来想写写PhoneNumberValidator，但是写到这里觉得PhoneNumberValidator还是太简单了，不必再费口舌。只是本篇不免有些挂挂羊头卖狗肉之嫌了。\n:em20:\n下载源码：\n1 文件 ","date":"2008-03-08","description":"","lastmod":"2008-03-08T03:02:05Z","slug":"mxvalidators2","tags":["flex","validator"],"title":"mx.validators包的使用实例之二-用Validator检测数字、字符串、Email、电话号码等","url":"https://blog.zengrong.net/post/mxvalidators2/"},{"categories":["news"],"content":"Phun是什么？Phun是Umeå大学的学生Emil Ernerfeld为自己的计算机作业而开发的一个基于物理规律的工具软件. 使用者可以随意而且轻易的创建物体，并给它们之间创建弹簧、链条等联系，重力、摩擦力、弹力、加速度等等，一切都符合物理规律. 这个软件的开发目的是为了让物理教学、研究更直观有效. 看完下文的视频你就能了解它的神奇之处了。\n作者博客下载页面 : http://www.acc.umu.se/~emilk/downloads.html\n把视频放下面，不可不看哈： （转自）\n","date":"2008-03-06","description":"","lastmod":"2008-03-06T14:47:27Z","slug":"phun","tags":[],"title":"【转】Phun,让人惊讶的小程序","url":"https://blog.zengrong.net/post/phun/"},{"categories":["technology"],"content":"本文转自李锟的Blog，原文地址：http://blog.csdn.net/mozilla/archive/2007/08/23/1756761.aspx\n**zrong注：**我原来还转过一篇d.CAT介绍Flex学习的文章，在这里\n目前Adobe Flex的中文版学习资料比较少，而且大多都很零散，不适合用来系统地学习。我在这里罗列一下在我的学习过程中读过的一些资料。当然是以Adobe的官方文档为主，这些文档内容非常详尽，应该作为学习的首选。\n首先下载并安装好Flex开发工具Flex Builder 2。Flex Builder 3的Beta版目前还不是很稳定，不推荐使用。\n**zrong注：**截止至转载时，Flex Builder 3已经发布了\n我发现这篇文档非常适合新手用来入门：重庆大坪的刘刚所翻译改编的《Flex中文帮助》，pdf格式。\n这篇中文文档有4章，按照其中的例子一路做下来，对于Flex开发过程、开发工具、开发环境可以有一个较为全面的感受。\n接下来开始打基础，千里之行，始于足下，下盘不稳，以后你吃苦头的时候会很多。\nFlex开发的基础就是ActionScript 3，这门语言与JavaScript很相似，但是差异也很大（过一段时间空闲了我来详细罗列一下这两门语言之间的差异）。ActionScript 3是基于ECMAScript 4来设计的，未来的JavaScript 2也会基于ECMAScript 4来设计，但是从现在的JavaScript 1.5（目前所有主流的浏览器所支持的JavaScript版本）迁移到ActionScript 3却需要付出很多学习。不过一旦熟练掌握了ActionScript 3，未来掌握JavaScript 2易如反掌。\n以下3本中文文档来自Flash 9的官方文档：\n《ActionScript 3.0编程》 ，pdf格式。务必熟读。 《使用ActionScript 3.0组件》，pdf格式。参考、查阅。 《ActionScript 3.0语言和组件参考》，chm格式。参考、查阅。 然后就只能读英文文档了，Flex 2的官方文档都是英文版的：\n《Getting Started with Flex 2》，pdf格式。读过了《Flex中文帮助》，这本书就不需要看的很仔细了，因为《Flex中文帮助》的内容基本上就是翻译这本书。 《Using Flex Builder 2》，pdf格式。工欲善其事，必先利其器，对于开发工具的详细介绍。 《Programming ActionScript 3.0》，pdf格式。和Flash 9那本中文版的《ActionScript 3.0编程》内容几乎完全相同。还是读中文版的吧，可以节省很多时间。 《Flex 2 Developer’s Guide》，pdf格式。最后这本书是开发过程中需要时时查阅的文档。读到这本书的时候，你已经完全入门了。如果能熟读这本书（1858页），你已经是高级Flex开发人员了。 学习路线中必读的书：《Flex中文帮助》-\u0026gt;《ActionScript 3.0编程》-\u0026gt;《Flex 2 Developer’s Guide》 ，这基本上就是目前最快的学习路线了。前两本书读完大约需要两周时间，最后一本书可以在开发过程中一边做开发一边阅读。Flex入门仅需要两周时间，成为高手大约需要一年时间。\n","date":"2008-03-04","description":"","lastmod":"2008-03-04T07:25:05Z","slug":"adobe-flex-study","tags":["as3","flex","study"],"title":"【转】Adobe Flex最佳学习路线","url":"https://blog.zengrong.net/post/adobe-flex-study/"},{"categories":["technology"],"content":"mx.validators包，在Flex框架中是相当的有用，但由于其功能复杂，我总是边用边忘。现将使用方法记录在这里把……\n第一个例子 请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n上面这个例子演示了最简单的用法。如果在“用户名：”输入框中不输入任何文字，那么当使用Tab键把输入焦点动TextInput改变到Button的时候，TextInput的边框会变红，鼠标移动到边框上，就会出现提示文字。\n这个效果的源码如下：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;vertical\u0026#34; fontSize=\u0026#34;12\u0026#34; width=\u0026#34;300\u0026#34; height=\u0026#34;120\u0026#34;\u0026gt; 3 \u0026lt;mx:Validator id=\u0026#34;nameV\u0026#34; source=\u0026#34;{nameTI}\u0026#34; property=\u0026#34;text\u0026#34; requiredFieldError=\u0026#34;必须输入用户名！\u0026#34;/\u0026gt; 4 \u0026lt;mx:FormItem label=\u0026#34;用户名：\u0026#34;\u0026gt; 5 \u0026lt;mx:TextInput id=\u0026#34;nameTI\u0026#34;/\u0026gt; 6 \u0026lt;/mx:FormItem\u0026gt; 7 \u0026lt;mx:Button id=\u0026#34;btn\u0026#34; label=\u0026#34;提交\u0026#34; /\u0026gt; 8\u0026lt;/mx:Application\u0026gt; 从上面的源码可以看出，要使用mx.validators包中的功能真的很简单。Validator组件的功能是检测必填项。我们把要检测的组件的名字写在source属性中，把要检测的组件的属性写在property属性中，然后自定义requiredFieldError属性的值即可。当然requiredFieldError属性的值也可以不定义，那么就会使用默认的提示文字（英文）。\n上面的错误提示的文字有些看不清，只需要加上这样一句就OK了：\n1\u0026lt;mx:Style\u0026gt; 2 .errorTip 3 { 4 fontSize: 12; 5 } 6\u0026lt;/mx:Style\u0026gt; 上面是一个小小的热身，让我们再来看几个简单的例子。\n控制检测时机 默认的情况下，Flex当我们切换组件焦点的时候检测，能不能改变这个检测时机？看看下面这个例子：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n在上面这个例子中，按Tab键切换焦点已经不能触发检测动作了，只有单击“提交”按钮才会触发检测。源码如下：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;vertical\u0026#34; fontSize=\u0026#34;12\u0026#34; width=\u0026#34;300\u0026#34; height=\u0026#34;150\u0026#34;\u0026gt; 3\t\u0026lt;mx:Style\u0026gt; 4\t\u0026lt;![CDATA[ 5\t.errorTip 6\t{ 7\tfontSize: 12; 8\t} 9\t]]\u0026gt; 10\t\u0026lt;/mx:Style\u0026gt; 11\t\u0026lt;mx:Validator id=\u0026#34;nameV\u0026#34; source=\u0026#34;{nameTI}\u0026#34; property=\u0026#34;text\u0026#34; 12\trequiredFieldError=\u0026#34;必须输入姓名！\u0026#34; 13\ttrigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34;/\u0026gt; 14\t\u0026lt;mx:Validator id=\u0026#34;ageV\u0026#34; source=\u0026#34;{ageTI}\u0026#34; property=\u0026#34;text\u0026#34; 15\trequiredFieldError=\u0026#34;必须输入年龄！\u0026#34; 16\ttrigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34;/\u0026gt; 17\t\u0026lt;mx:FormItem label=\u0026#34;姓名：\u0026#34;\u0026gt; 18\t\u0026lt;mx:TextInput id=\u0026#34;nameTI\u0026#34;/\u0026gt; 19\t\u0026lt;/mx:FormItem\u0026gt; 20\t\u0026lt;mx:FormItem label=\u0026#34;年龄：\u0026#34;\u0026gt; 21\t\u0026lt;mx:TextInput id=\u0026#34;ageTI\u0026#34;/\u0026gt; 22\t\u0026lt;/mx:FormItem\u0026gt; 23\t\u0026lt;mx:Button id=\u0026#34;btn\u0026#34; label=\u0026#34;提交\u0026#34; /\u0026gt; 24\u0026lt;/mx:Application\u0026gt; 这是因为，在默认情况下，Validator 会在Flex发出valueCommit事件的时候进行检测，因此当焦点改变的时候，会自动进行检测。而上面的源码中，则手动指定了进行检测的事件是btn按钮的click事件。\n控制错误显示的目标 Validator还有一个listen属性，这个属性有什么用呢？它用来指定检测的错误信息显示在哪个组件上。这有什么作用呢？难道我们检测到错误信息之后，不显示在发生错误的组件上，反而显示到其它组件上不成？看看例子把：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n这是源码：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;vertical\u0026#34; fontSize=\u0026#34;12\u0026#34; width=\u0026#34;300\u0026#34; height=\u0026#34;200\u0026#34;\u0026gt; 3\t\u0026lt;mx:Style\u0026gt; 4\t\u0026lt;![CDATA[ 5\t.errorTip 6\t{ 7\tfontSize: 12; 8\t} 9\t]]\u0026gt; 10\t\u0026lt;/mx:Style\u0026gt; 11\t\u0026lt;mx:Validator id=\u0026#34;nameV\u0026#34; source=\u0026#34;{nameTI}\u0026#34; property=\u0026#34;text\u0026#34; 12\trequiredFieldError=\u0026#34;必须输入姓名！\u0026#34; 13\ttrigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34;/\u0026gt; 14\t\u0026lt;mx:Validator id=\u0026#34;ageV\u0026#34; source=\u0026#34;{ageTI}\u0026#34; property=\u0026#34;text\u0026#34; 15\trequiredFieldError=\u0026#34;必须输入年龄！\u0026#34; 16\ttrigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34;/\u0026gt; 17\t\u0026lt;mx:Validator id=\u0026#34;sexV\u0026#34; source=\u0026#34;{sexRBG}\u0026#34; property=\u0026#34;selectedValue\u0026#34; 18\trequiredFieldError=\u0026#34;必须选择性别！\u0026#34; 19\ttrigger=\u0026#34;{btn}\u0026#34; triggerEvent=\u0026#34;click\u0026#34; 20\tlistener=\u0026#34;{maleRB}\u0026#34;/\u0026gt; 21\t\u0026lt;mx:FormItem label=\u0026#34;姓名：\u0026#34; width=\u0026#34;150\u0026#34;\u0026gt; 22\t\u0026lt;mx:TextInput id=\u0026#34;nameTI\u0026#34;/\u0026gt; 23\t\u0026lt;/mx:FormItem\u0026gt; 24\t\u0026lt;mx:FormItem label=\u0026#34;年龄：\u0026#34; width=\u0026#34;150\u0026#34;\u0026gt; 25\t\u0026lt;mx:TextInput id=\u0026#34;ageTI\u0026#34;/\u0026gt; 26\t\u0026lt;/mx:FormItem\u0026gt; 27\t\u0026lt;mx:FormItem label=\u0026#34;性别：\u0026#34; direction=\u0026#34;horizontal\u0026#34; width=\u0026#34;150\u0026#34;\u0026gt; 28\t\u0026lt;mx:RadioButtonGroup id=\u0026#34;sexRBG\u0026#34;/\u0026gt; 29\t\u0026lt;mx:RadioButton id=\u0026#34;maleRB\u0026#34; groupName=\u0026#34;sexRBG\u0026#34; label=\u0026#34;男\u0026#34; value=\u0026#34;1\u0026#34;/\u0026gt; 30\t\u0026lt;mx:RadioButton id=\u0026#34;femaleRB\u0026#34; groupName=\u0026#34;sexRBG\u0026#34; label=\u0026#34;女\u0026#34; value=\u0026#34;0\u0026#34;/\u0026gt; 31\t\u0026lt;/mx:FormItem\u0026gt; 32\t\u0026lt;mx:Button id=\u0026#34;btn\u0026#34; label=\u0026#34;提交\u0026#34; /\u0026gt; 33\u0026lt;/mx:Application\u0026gt; 在上面的例子中，对于性别的选择，由于检测两个RadioButton比较麻烦，采用了检测RadioButtonGroup的selectedValue属性的方法，如果这个属性为空，就说明两个RadioButton都没有选择。但RadioButtonGroup并不是一个可视组件，检测的错误信息无法显示出来，所以这里就使用了listen属性将显示信息转到maleRB组件上进行显示了。\n本例源码下载\n1 文件 ","date":"2008-03-03","description":"","lastmod":"2008-03-03T16:05:55Z","slug":"mxvalidators1","tags":["flex","validator"],"title":"mx.validators包的使用实例之一-用Validator检测必填项","url":"https://blog.zengrong.net/post/mxvalidators1/"},{"categories":["news"],"content":" Flex 3.0 AIR 1.0 进一步解读AIR 1.0 1 文件 [download id=\u0026quot;18\u0026quot;]\n1 文件 ","date":"2008-02-25","description":"","lastmod":"2008-02-25T13:29:12Z","slug":"flex3-air1","tags":["adobe","air","flex","ria"],"title":"Flex 3.0 AIR 1.0 正式发布","url":"https://blog.zengrong.net/post/flex3-air1/"},{"categories":["news"],"content":"去年曾经写过一篇慨叹Director即将升级的文章，当时Adobe说要在07年底发布新版，可是就在我们即将要淡忘她时，Director 11 真的就要来了。\n","date":"2008-02-25","description":"","lastmod":"2008-02-25T13:17:20Z","slug":"director-11","tags":["adobe","director"],"title":"Director 11居然来了","url":"https://blog.zengrong.net/post/director-11/"},{"categories":["technology"],"content":"碰到一个奇怪的问题，最后终于解决。其实是自己的疏忽。记在这里了：\n下面的代码中，vs是一个ViewStack实例，removeVS的功能是移除VS中多余的子显示对象，以便对其中的子显示对象进行重建。在vs中原来有4个子显示对象copyright、step1、step2、stepUpload，它们不在移除范围之内。\n1private function removeVS():void{ 2 Logger.info(\u0026#39;removeVS运行，删除前的vs子数量：{1}\u0026#39;, vs.numChildren); 3 for(var i:int=0; i \u0026lt; vs.numChildren; i++) 4 { 5 Logger.debug(\u0026#39;当前序号：{1}\u0026#39;,i); 6 var __step:UIComponent = vs.getChildAt(i) as UIComponent; 7 var __isInitStep:Boolean = __step == copyright || __step == step1 || __step == step2 || __step == stepUpload; 8 Logger.info(\u0026#39;当前的step name:{1}，是否保留:{2},当前序号{3}\u0026#39;, __step.name, __isInitStep, i); 9 if(!__isInitStep) 10 { 11 vs.removeChild(__step); 12 } 13 } 14 Logger.info(\u0026#39;removeVS运行，删除后的vs子数量：{1}\u0026#39;, vs.numChildren); 15} 在没有添加子显示对象之前，vs的子显示对象应该有4个，而一旦添加的其他的显示对象，当调用removeVS的时候，却只能移除一个添加的显示对象！\n例如：我在vs中添加了一个step3和step4显示对象，在移除的时候，却仅仅移除了step3，而step4仍然保留！！！\n这是怎么回事？经检查，发现问题出在vs.numChildren上。\n因为for循环中引用的是vs.numChildren，而当__inInitStep为false的时候，vs移除了一个子显示对象。这时vs.numchildren就发生了变化，变为vs.numchildren-1。这导致循环少执行了一次，没有删掉step4。\n找到了问题的原因，修改成如下代码：\n1private function removeVS():void{ 2 var __toRemovedArr:Array = new Array(); 3 for(var i:int=0; i \u0026lt; vs.numChildren; i++) 4 { 5 Logger.debug(\u0026#39;当前序号：{1}\u0026#39;,i); 6 var __step:UIComponent = vs.getChildAt(i) as UIComponent; 7 var __isInitStep:Boolean = __step == copyright || __step == step1 || __step == step2 || __step == stepUpload; 8 if(!__isInitStep) 9 { 10 __toRemovedArr.push(__step); 11 } 12 } 13 for each(var k:UIComponent in __toRemovedArr) 14 { 15 vs.removeChild(k); 16 } 17} 2011-09-26更新：还有更新简单的两个办法\n使用递减的for循环 使用while循环 详见评论中的回复1和回复4\n","date":"2008-02-20","description":"","lastmod":"2008-02-20T06:54:35Z","slug":"for-removechild","tags":["as3","flex"],"title":"使用for进行removeChild的问题","url":"https://blog.zengrong.net/post/for-removechild/"},{"categories":["technology"],"content":"这是我发在ActionScript3天地会上的帖子，既然问题已经解决了，就记在这里分享一下。\n用Flex Builder 3一段时间了，关于编译有一些问题要问大家：\n一、如何配置Run和Debug的输出文件？\n在Flex Builder 2中，Run和Debug输出的文件是不同的。前者编译后的文件名和mxml主文件相同，后者编译出来的后面带一个-debug后缀。\n而Flex Builder 3则使用Run或者Debug编译的swf文件都位于bin-debug目录中，且大小都相同。这是怎么回事呢？\n二、为何Flex Builder 3编译出的swf文件比较大？\n在使用Flex Builder 2的时候，我一直是用Ant进行编译的，但现在我发现用Ant编译出来的文件与Flex Builder 3编译出来的文件大小有很大的差异，后者编译出来的文件大了将近一倍。我想这可能也与上一个问题有关。\n使用Ant编译，如果用3.0的sdk，编译出来的文件也比使用2.0.1sdk要大，但比直接使用Flex Builder 3编译出来的文件要小。\n附各种编译方法输出的swf文件大小：\n使用Ant+sdk 2.0.1编译，swf文件大小291KB 使用Ant+sdk3.0.0编译，swf文件大小330KB 使用Flex Builder 3直接编译，swf文件大小460KB（Flex Builder 3编译采用内嵌方式（no RSL）） 解决方法：\nhttp://livedocs.adobe.com/labs/flex3/html/help.html?content=build_05.html\n原来，Flex Builder 3把release版本的编译独立出来做了一个功能:File-Export-Release Build...另外还有一个打包的功能。不过在使用之前，先需要指定默认的主文件（如下图）。\n","date":"2008-02-14","description":"","lastmod":"2008-02-14T14:27:18Z","slug":"flex-builder-3-b3-release-build","tags":["as3","flex","flexbuilder"],"title":"Flex Builder 3 b3编译的swf文件为什么那么大？","url":"https://blog.zengrong.net/post/flex-builder-3-b3-release-build/"},{"categories":["technology"],"content":"\n很多老师和我反映，NOC系统中的XAMPP启动不正常。我也碰到过这种情况，但稍候即告正常。总以为是RP问题，今天总算叫我找到了原因。 环境：\nWindows XP Professional 、xampp Lite1.6.5\n情况如下：\n双击xampp-control.exe，启动apache，出错，提示如图1：\n图1\n双击xampp_start.exe，出错，提示如图2\n图2\n双击xampp-portcheck.exe，发现80端口被迅雷占用，如图3：\n图3\n把apache改为监听8080端口，问题解决。\n","date":"2008-02-13","description":"","lastmod":"2008-02-13T13:58:27Z","slug":"432","tags":["apache"],"title":"xampp中的apache不能启动，原来是迅雷作怪","url":"https://blog.zengrong.net/post/432/"},{"categories":["technology"],"content":"本文转自网谈，原文地址\n【字符指南】\nCharacter Reference HTMLPlayground.com Special Characters W3Schools.com Webmonkey.com\n【工具】\nAlleycode.com Amaya Aptana Bluefish Coffee Cup HTML Editor HTMLKit.com PageBreeze.com Pingdom Tools Test Everything TextFixer.com TheJackolsDen.com 【教程】\nBare Bones Guide to HTML EchoEcho.com HTML.net HTMLBasix.com HTMLCodeTutorial.com HTMLDog.com HTMLGoodies.com\nHTMLTutorials.ca PageTutor.com YourHTMLSource.com 【验证工具】\nAlwaysValid.com AnyBrowser.com Tidy HTML W3C Markup Validator ","date":"2008-01-24","description":"","lastmod":"2008-01-24T14:39:01Z","slug":"html-tools-30","tags":["html"],"title":"【转】30余款HTML工具和教程","url":"https://blog.zengrong.net/post/html-tools-30/"},{"categories":["technology"],"content":"本文是ActionScript3 Tips and Tricks系列阅读笔记之一Loading Text and XML with URLLoader，查看所有的笔记。\n在AS3以前的版本中，要载入外部文本，可以使用LoadVars类和XML类。但是在AS3中，这些功能被移到了一个类中，这就是URLLoader(flash.net.URLLoader)类，这个类与LoadVars很相似。而AS3的XML类中，已经没有了载入外部XML文件的功能。我们可以使用URLLoader类获取外部的文本，并将其解析成普通文本或者XML对象。\n就像LoadVars类一样，URLLoader类也有一个load()方法载入外部源的数据。这个方法允许一个URLRequest类的实例（而不是一个字符串）作为它的为一个参数。你可以使用URLLoader的事件决定载入完成之后该做什么。载入成功之后，URLLoader中的data属性将包含载入的文本值。\n1var loader:URLLoader; 2// ... 3loader = new URLLoader(); 4loader.addEventListener(Event.COMPLETE, xmlLoaded); 5 6var request:URLRequest = new URLRequest(\u0026#34;file.xml\u0026#34;); 7loader.load(request); 8//... 9function xmlLoaded(event:Event):void { 10 var myXML:XML = new XML(loader.data); 11 //... 12} ","date":"2007-12-30","description":"","lastmod":"2007-12-30T07:44:11Z","slug":"loading-text-and-xml-with-urlloader","tags":["as3","general","tipsandtricks"],"title":"使用URLLoader载入文本和XML-ActionScript3 Tips and Tricks","url":"https://blog.zengrong.net/post/loading-text-and-xml-with-urlloader/"},{"categories":["technology"],"content":"本文是ActionScript3 Tips and Tricks系列阅读笔记之一Multiple Arguments in trace()，查看所有的笔记。\ntrace函数可以在Flash的输出面板（Flex Builder的Console面板）中显示信息。在ActionScript1和ActionScript2中，trace函数只能接收一个参数，而在ActionScript3中，trace函数可以接收多个参数：\n1trace(value1, value2, value3); 实际上，在ActionScript1/2中也可以做到。只需要使用数组：\n1trace([value1, value2, value3]); ZRong注：实际上，以上的两段代码的显示结果是不同的，ActionScript3 的 trace ，会将每隔参数的值以空格分开，而ActionScript1/2使用数组作为参数，显示的结果是以逗号分隔的。\n","date":"2007-12-30","description":"","lastmod":"2007-12-30T07:23:26Z","slug":"trace-multi-param","tags":["as3","flash","general","tipsandtricks"],"title":"trace的多参数支持-ActionScript3 Tips and Tricks","url":"https://blog.zengrong.net/post/trace-multi-param/"},{"categories":["impressions"],"content":"作者： CNET科技资讯网 CNETNews.com.cn\n原文地址：http://cnetnews.com.cn/2007/1101/596420.shtml\n文/ 孙振耀\n我有个有趣的观察，外企公司多的是25-35岁的白领，40岁以上的员工很少，二三十岁的外企员工是意气风发的，但外企公司40岁附近的经理人是很尴尬的。我见过的40岁附近的外企经理人大多在一直跳槽，最后大多跳到民企，比方说，唐骏。外企员工的成功很大程度上是公司的成功，并非个人的成功，西门子的确比国美大，但并不代表西门子中国经理比国美的老板强，甚至可以说差得很远。而进外企的人往往并不能很早理解这一点，把自己的成功90％归功于自己的能力，实际上，外企公司随便换个中国区总经理并不会给业绩带来什么了不起的影响。好了问题来了，当这些经理人40多岁了，他们的薪资要求变得很高，而他们的才能其实又不是那么出众，作为外企公司的老板，你会怎么选择？有的是只要不高薪水的，要出位的精明强干精力冲沛的年轻人，有的是，为什么还要用你？\n从上面这个例子，其实可以看到我们的工作轨迹，二三十岁的时候，生活的压力还比较小，身体还比较好，上面的父母身体还好，下面又没有孩子，不用还房贷，也没有孩子要上大学，当个外企小白领还是很光鲜的，挣得不多也够花了。但是人终归要结婚生子，终归会老，到了40岁，父母老了，要看病要吃药，要有人看护，自己要还房贷，要过基本体面的生活，要养小孩……那个时候需要挣多少钱才够花才重要。所以，看待工作，眼光要放远一点，一时的谁高谁低并不能说明什么。\n从这个角度上来说，我不太赞成过于关注第一份工作的薪水，更没有必要攀比第一份工作的薪水，这在刚刚出校园的学生中间是很常见的。正常人大概要工作35年，这好比是一场马拉松比赛，和真正的马拉松比赛不同的是，这次比赛没有职业选手，每个人都只有一次机会。要知到，有很多人甚至坚持不到终点，大多数人最后是走到终点的，只有少数人是跑过终点的，因此在刚开始的时候，去抢领先的位置并没有太大的意义。刚进社会的时候如果进500强公司，大概能拿到3k-6k/月的工资，有些特别技术的人才可能可以到8k/月，可问题是，5年以后拿多少？估计5k-10k了不起了。起点虽然高，但增幅有限，而且，后面的年轻人追赶的压力越来越大。\n我前两天问我的一个销售，你会的这些东西一个新人2年就都学会了，但新人所要求的薪水却只是你的一半，到时候，你怎么办？\n职业生涯就像一场体育比赛，有初赛、复赛、决赛。初赛的时候大家都刚刚进社会，大多数都是实力一般的人，这时候努力一点认真一点很快就能让人脱颖而出，于是有的人二十多岁做了经理，有的人迟些也终于赢得了初赛，三十多岁成了经理。然后是复赛，能参加复赛的都是赢得初赛的，每个人都有些能耐，在聪明才智上都不成问题，这个时候再想要胜出就不那么容易了，单靠一点点努力和认真还不够，要有很强的坚忍精神，要懂得靠团队的力量，要懂得收服人心，要有长远的眼光……\n看上去赢得复赛并不容易，但，还不是那么难。因为这个世界的规律就是给人一点成功的同时让人骄傲自满，刚刚赢得初赛的人往往不知道自己赢得的仅仅是初赛，有了一点小小的成绩大多数人都会骄傲自满起来，认为自己已经懂得了全部，不需要再努力再学习了，他们会认为之所以不能再进一步已经不是自己的原因了。虽然他们仍然不好对付，但是他们没有耐性，没有容人的度量，更没有清晰长远的目光。就像一只愤怒的斗牛，虽然猛烈，最终是会败的，而赢得复赛的人则象斗牛士一样，不急不躁，跟随着自己的节拍，慢慢耗尽对手的耐心和体力。赢得了复赛以后，大约已经是一位很了不起的职业经理人了，当上了中小公司的总经理，大公司的副总经理，主管着每年几千万乃至几亿的生意。\n最终的决赛来了，说实话我自己都还没有赢得决赛，因此对于决赛的决胜因素也只能凭自己的猜测而已，这个时候的输赢或许就像武侠小说里写得那样，大家都是高手，只能等待对方犯错了，要想轻易击败对手是不可能的，除了使上浑身解数，还需要一点运气和时间。世界的规律依然发挥着作用，赢得复赛的人已经不只是骄傲自满了，他们往往刚愎自用，听不进去别人的话，有些人的脾气变得暴躁，心情变得浮躁，身体变得糟糕，他们最大的敌人就是他们自己，在决赛中要做的只是不被自己击败，等着别人被自己击败。这和体育比赛是一样的，最后高手之间的比赛，就看谁失误少谁就赢得了决赛。\n根源 你工作快乐么？你的工作好么？\n有没有觉得干了一段时间以后工作很不开心？有没有觉得自己入错了行？有没有觉得自己没有得到应有的待遇？有没有觉得工作像一团乱麻每天上班都是一种痛苦？有没有很想换个工作？有没有觉得其实现在的公司并没有当初想象得那么好？有没有觉得这份工作是当初因为生存压力而找的，实在不适合自己？你从工作中得到你想要得到的了么？你每天开心么？\n天涯上愤怒的人很多，你有没有想过，你为什么不快乐？你为什么愤怒？\n其实，你不快乐的根源，是因为你不知道要什么！你不知道要什么，所以你不知道去追求什么，你不知道追求什么，所以你什么也得不到。\n我总觉得，职业生涯首先要关注的是自己，自己想要什么？大多数人大概没想过这个问题，唯一的想法只是——我想要一份工作，我想要一份不错的薪水，我知道所有人对于薪水的渴望，可是，你想每隔几年重来一次找工作的过程么？你想每年都在这种对于工作和薪水的焦急不安中度过么？不想的话，就好好想清楚。饮鸩止渴，不能因为口渴就拼命喝毒药。越是焦急，越是觉得自己需要一份工作，越饥不择食，越想不清楚，越容易失败，你的经历越来越差，下一份工作的人看着你的简历就皱眉头。于是你越喝越渴，越渴越喝，陷入恶性循环。最终只能哀叹世事不公或者生不逢时，只能到天涯上来发泄一把，在失败者的共鸣当中寻求一点心理平衡罢了。大多数人都有生存压力，我也是，有生存压力就会有很多焦虑，积极的人会从焦虑中得到动力，而消极的人则会因为焦虑而迷失方向。所有人都必须在压力下做出选择，这就是世道，你喜欢也罢不喜欢也罢。\n一般我们处理的事情分为重要的事情和紧急的事情，如果不做重要的事情就会常常去做紧急的事情。比如锻炼身体保持健康是重要的事情，而看病则是紧急的事情。如果不锻炼身体保持健康，就会常常为了病痛烦恼。又比如防火是重要的事情，而救火是紧急的事情，如果不注意防火，就要常常救火。找工作也是如此，想好自己究竟要什么是重要的事情，找工作是紧急的事情，如果不想好，就会常常要找工作。往往紧急的事情给人的压力比较大，迫使人们去赶紧做，相对来说重要的事情反而没有那么大的压力，大多数人做事情都是以压力为导向的，压力之下，总觉得非要先做紧急的事情，结果就是永远到处救火，永远没有停歇的时候。（很多人的工作也像是救火队一样忙碌痛苦，也是因为工作中没有做好重要的事情。）那些说自己活在水深火热为了生存顾不上那么多的朋友，今天找工作困难是当初你们没有做重要的事情，是结果不是原因。如果今天你们还是因为急于要找一份工作而不去思考，那么或许将来要继续承受痛苦找工作的结果。\n我始终觉得我要说的话题，沉重了点，需要很多思考，远比唐笑打武警的话题来的枯燥乏味，但是，天下没有轻松的成功，成功，要付代价。请先忘记一切的生存压力，想想这辈子你最想要的是什么？所以，最要紧的事情，先想好自己想要什么。\n什么是好工作 当初微软有个唐骏，很多大学里的年轻人觉得这才是他们向往的职业生涯，我在清华bbs里发的帖子被这些学子们所不屑，那个时候学生们只想出国或者去外企，不过如今看来，我还是对的，唐骏去了盛大，陈天桥创立的盛大，一家民营公司。一个高学历的海归在500强的公司里拿高薪水，这大约是很多年轻人的梦想，问题是，每年毕业的大学生都在做这个梦，好的职位却只有500个。\n人都是要面子的，也是喜欢攀比的，即使在工作上也喜欢攀比，不管那是不是自己想要的。大家认为外企公司很好，可是好在哪里呢？好吧，他们在比较好的写字楼，这是你想要的么？他们出差住比较好的酒店，这是你想要的么？别人会羡慕一份外企公司的工作，这是你想要的么？那一切都是给别人看的，你干吗要活得那么辛苦给别人看？另一方面，他们薪水福利一般，并没有特别了不起，他们的晋升机会比较少，很难做到很高阶的主管，他们虽然厌恶常常加班，却不敢不加班，因为“你不干有得是人干”，大部分情况下会找个台湾人香港人新加坡人来管你，而这些人又往往有些莫名其妙的优越感。你想清楚了么？500强一定好么？找工作究竟是考虑你想要什么，还是考虑别人想看什么？\n我的大学同学们大多数都到美国了，甚至毕业这么多年了，还有人最近到国外去了。出国真的有那么好么？我的大学同学们，大多数还是在博士、博士后、访问学者地挣扎着，至今只有一个正经在一个美国大学里拿到个正式的教职。国内的教授很难当么？我有几个表亲也去了国外了，他们的父母独自在国内，没有人照顾，有好几次人在家里昏倒都没人知道，出国，真的这么光彩么？就像有人说的“很多事情就像看A片，看的人觉得很爽，做的人未必。”\n人总想找到那个最好的，可是，什么是最好的？你觉得是最好的那个，是因为你的确了解，还是因为别人说他是最好的？即使他对于别人是最好的，对于你也一定是最好的么？\n对于自己想要什么，自己要最清楚，别人的意见并不是那么重要。很多人总是常常被别人的意见所影响，亲戚的意见，朋友的意见，同事的意见……问题是，你究竟是要过谁的一生？人的一生不是父母一生的续集，也不是儿女一生的前传，更不是朋友一生的外篇，只有你自己对自己的一生负责，别人无法也负不起这个责任。自己做的决定，至少到最后，自己没什么可后悔。对于大多数正常智力的人来说，所做的决定没有大的对错，无论怎么样的选择，都是可以尝试的。比如你没有考自己上的那个学校，没有入现在这个行业，这辈子就过不下去了？就会很失败？不见得。\n我想，好工作，应该是适合你的工作，具体点说，应该是能给你带来你想要的东西的工作，你或许应该以此来衡量你的工作究竟好不好，而不是拿公司的大小，规模，外企还是国企，是不是有名，是不是上市公司来衡量。小公司，未必不是好公司，赚钱多的工作，也未必是好工作。你还是要先弄清楚你想要什么，如果你不清楚你想要什么，你就永远也不会找到好工作，因为你永远只看到你得不到的东西，你得到的，都是你不想要的。\n可能，最好的，已经在你的身边，只是，你还没有学会珍惜。人们总是盯着得不到的东西，而忽视了那些已经得到的东西。\n普通人 我发现中国人的励志和国外的励志存在非常大的不同，中国的励志比较鼓励人立下大志愿，卧薪尝胆，有朝一日成富成贵。而国外的励志比较鼓励人勇敢面对现实生活，面对普通人的困境，虽然结果也是成富成贵，但起点不一样，相对来说，我觉得后者在操作上更现实，而前者则需要用999个失败者来堆砌一个成功者的故事。\n我们都是普通人，普通人的意思就是，概率这件事是很准的。因此，我们不会买彩票中500万，我们不会成为比尔盖茨或者李嘉诚，我们不会坐飞机掉下来，我们当中很少的人会创业成功，我们之中有30％的人会离婚，我们之中大部分人会活过65岁……\n所以请你在想自己要什么的时候，要得“现实”一点，你说我想要做李嘉诚，抱歉，我帮不上你。成为比尔盖茨或者李嘉诚这种人，是靠命的，看我写的这篇文章绝对不会让你成为他们，即使你成为了他们，也绝对不是我这篇文章的功劳。“王侯将相宁有种乎”但真正当皇帝的只有一个人，王侯将相，人也不多。目标定得高些对于喜欢挑战的人来说有好处，但对于大多数普通人来说，反而比较容易灰心沮丧，很容易就放弃了。\n回过头来说，李嘉诚比你有钱大致50万倍，他比你更快乐么？或许。有没有比你快乐50万倍，一定没有。他比你最多也就快乐一两倍，甚至有可能还不如你快乐。寻找自己想要的东西不是和别人比赛，比谁要得更多更高，比谁的目标更远大。虽然成为李嘉诚这个目标很宏大，但你并不见得会从这个目标以及追求目标的过程当中获得快乐，而且基本上你也做不到。你必须听听你内心的声音，寻找真正能够使你获得快乐的东西，那才是你想要的东西。\n你想要的东西，或者我们把它称之为目标，目标其实并没有高低之分，你不需要因为自己的目标没有别人远大而不好意思，达到自己的目标其实就是成功，成功有大有小，快乐却是一样的。我们追逐成功，其实追逐的是成功带来的快乐，而非成功本身。职业生涯的道路上，我们常常会被攀比的心态蒙住眼睛，忘记了追求的究竟是什么，忘记了是什么能使我们更快乐。\n社会上一夜暴富的新闻很多，这些消息，总会在我们的心里面掀起很多涟漪，涟漪多了就变成惊涛骇浪，心里的惊涛骇浪除了打翻承载你目标的小船，并不会使得你也一夜暴富。“只见贼吃肉，不见贼挨揍。”我们这些普通人既没有当贼的勇气，又缺乏当贼的狠辣绝决，虽然羡慕吃肉，却更害怕挨揍，偶尔看到几个没挨揍的贼就按奈不住，或者心思活动，或者大感不公，真要叫去做贼，却也不敢。\n我还是过普通人的日子，要普通人的快乐，至少，晚上睡得着觉。\n跳槽与积累 首先要说明，工作是一件需要理智的事情，所以不要在工作上耍个性，天涯上或许会有人觉得你很有个性而叫好，煤气公司电话公司不会因为觉得你很有个性而免了你的帐单。当你很帅地炒掉了你的老板，当你很酷地挖苦了一番招聘的HR，账单还是要照付，只是你赚钱的时间更少了，除了你自己，没人受损失。\n我并不反对跳槽，但跳槽决不是解决问题的办法，而且频繁跳槽的后果是让人觉得没有忠诚度可言，而且不能安心工作。现在很多人从网上找工作，很多找工作的网站常常给人出些馊主意，要知道他们是盈利性企业，当然要从自身盈利的角度来考虑，大家越是频繁跳槽频繁找工作他们越是生意兴隆，所以鼓动人们跳槽是他们的工作。所以他们会常常告诉你，你拿的薪水少了，你享受的福利待遇差了，又是“薪情快报”又是“赞叹自由奔放的灵魂”。至于是否会因此让你不能安心，你跳了槽是否解决问题，是否更加开心，那个，他们管不着。\n要跳槽肯定是有问题，一般来说问题发生了，躲是躲不开的，很多人跳槽是因为这样或者那样的不开心，如果这种不开心，在现在这个公司不能解决，那么在下一个公司多半也解决不掉。你必须相信，90%的情况下，你所在的公司并没有那么烂，你认为不错的公司也没有那么好。就像围城里说的，“城里的人拼命想冲出来，而城外的人拼命想冲进去。”每个公司都有每个公司的问题，没有问题的公司是不存在的。换个环境你都不知道会碰到什么问题，与其如此，不如就在当下把问题解决掉。很多问题当你真的想要去解决的时候，或许并没有那么难。有的时候你觉得问题无法解决，事实上，那只是“你觉得”。\n人生的曲线应该是曲折向上的，偶尔会遇到低谷但大趋势总归是曲折向上的，而不是象脉冲波一样每每回到起点，我见过不少面试者，30多岁了，四五份工作经历，每次多则3年，少则1年，30多岁的时候回到起点从一个初级职位开始干起，拿基本初级的薪水，和20多岁的年轻人一起竞争，不觉得有点辛苦么？这种日子好过么？\n资本靠的就是积累，这种积累包括人际关系，经验，人脉，口碑……如果常常更换行业，代表几年的积累付之东流，一切从头开始，如果换了两次行业，35岁的时候大概只有5年以下的积累，而一个没有换过行业的人至少有了10年的积累，谁会占优势？工作到2-3年的时候，很多人觉得工作不顺利，好像到了一个瓶颈，心情烦闷，就想辞职，乃至换一个行业，觉得这样所有一切烦恼都可以抛开，会好很多。其实这样做只是让你从头开始，到了时候还是会发生和原来行业一样的困难，熬过去就向上跨了一大步，要知道每个人都会经历这个过程，每个人的职业生涯中都会碰到几个瓶颈，你熬过去了而别人没有熬过去你就领先了。跑长跑的人会知道，开始的时候很轻松，但是很快会有第一次的难受，但过了这一段又能跑很长一段，接下来会碰到第二次的难受，坚持过了以后又能跑一段，如此往复，难受一次比一次厉害，直到坚持不下去了。大多数人第一次就坚持不了了，一些人能坚持到第二次，第三次虽然大家都坚持不住了，可是跑到这里的人也没几个了，这点资本足够你安稳活这一辈子了。\n一份工作到两三年的时候，大部分人都会变成熟手，这个时候往往会陷入不断的重复，有很多人会觉得厌倦，有些人会觉得自己已经搞懂了一切，从而懒得去寻求进步了。很多时候的跳槽是因为觉得失去兴趣了，觉得自己已经完成比赛了。其实这个时候比赛才刚刚开始，工作两三年的人，无论是客户关系，人脉，手下，和领导的关系，在业内的名气……还都是远远不够的，但稍有成绩的人总是会自我感觉良好的，每个人都觉得自己跟客户关系铁得要命，觉得自己在业界的口碑好得很。其实可以肯定地说，一定不是，这个时候，还是要拿出前两年的干劲来，稳扎稳打，积累才刚刚开始。\n你足够了解你的客户吗？你知道他最大的烦恼是什么吗？你足够了解你的老板么？你知道他最大的烦恼是什么吗？你足够了解你的手下么？你知道他最大的烦恼是什么吗？如果你不知道，你凭什么觉得自己已经积累够了？如果你都不了解，你怎么能让他们帮你的忙，做你想让他们做的事情？如果他们不做你想让他们做的事情，你又何来的成功？\n等待 这是个浮躁的人们最不喜欢的话题，本来不想说这个话题，因为会引起太多的争论，而我又无意和人争论这些，但是考虑到对于职业生涯的长久规划，这是一个躲避不了的话题，还是决定写一写，不爱看的请离开吧。\n并不是每次穿红灯都会被汽车撞，并不是每个罪犯都会被抓到，并不是每个错误都会被惩罚，并不是每个贪官都会被枪毙，并不是你的每一份努力都会得到回报，并不是你的每一次坚持都会有人看到，并不是你每一点付出都能得到公正的回报，并不是你的每一个善意都能被理解……这个，就是世道。好吧，世道不够好，可是，你有推翻世道的勇气么？如果没有，你有更好的解决办法么？有很多时候，人需要一点耐心，一点信心。每个人总会轮到几次不公平的事情，而通常，安心等待是最好的办法。\n有很多时候我们需要等待，需要耐得住寂寞，等待属于你的那一刻。周润发等待过，刘德华等待过，周星驰等待过，王菲等待过，张艺谋也等待过……看到了他们如今的功成名就的人，你可曾看到当初他们的等待和耐心？你可曾看到金马奖影帝在街边摆地摊？你可曾看到德云社一群人在剧场里给一位观众说相声？你可曾看到周星驰的角色甚至连一句台词都没有？每一个成功者都有一段低沉苦闷的日子，我几乎能想象得出来他们借酒浇愁的样子，我也能想象得出他们为了生存而挣扎的窘迫。在他们一生最中灿烂美好的日子里，他们渴望成功，但却两手空空，一如现在的你。没有人保证他们将来一定会成功，而他们的选择是耐住寂寞。如果当时的他们总念叨着“成功只是属于特权阶级的”，你觉得他们今天会怎样？\n曾经我也不明白有些人为什么并不比我有能力却要坐在我的头上，年纪比我大就一定要当我的领导么？为什么有些烂人不需要努力就能赚钱？为什么刚刚改革开放的时候的人能那么容易赚钱，而轮到我们的时候，什么事情都要正规化了？有一天我突然想，我还在上学的时候他们就在社会里挣扎奋斗了，他们在社会上奋斗积累了十几二十年，我们新人来了，他们有的我都想要，我这不是在要公平，我这是在要抢劫。因为我要得太急，因为我忍不住寂寞。二十多岁的男人，没有钱，没有事业，却有蓬勃的欲望。\n人总是会遇到挫折的，人总是会有低潮的，人总是会有不被人理解的时候的，人总是有要低声下气的时候，这些时候恰恰是人生最关键的时候，因为大家都会碰到挫折，而大多数人过不了这个门槛，你能过，你就成功了。在这样的时刻，我们需要耐心等待，满怀信心地去等待，相信，生活不会放弃你，机会总会来的。至少，你还年轻，你没有坐牢，没有生治不了的病，没有欠还不起的债。比你不幸的人远远多过比你幸运的人，你还怕什么？路要一步步走，虽然到达终点的那一步很激动人心，但大部分的脚步是平凡甚至枯燥的，但没有这些脚步，或者耐不住这些平凡枯燥，你终归是无法迎来最后的那些激动人心。\n逆境，是上帝帮你淘汰竞争者的地方。要知道，你不好受，别人也不好受，你坚持不下去了，别人也一样，千万不要告诉别人你坚持不住了，那只能让别人获得坚持的信心，让竞争者看着你微笑的面孔，失去信心，退出比赛。胜利属于那些有耐心的人。\n在最绝望的时候，我会去看电影《The Pursuit of Happyness》《Jerry Maguire》，让自己重新鼓起勇气，因为，无论什么时候，我们总还是有希望。当所有的人离开的时候，我不失去希望，我不放弃。每天下班坐在车里，我喜欢哼着《隐形的翅膀》看着窗外，我知道，我在静静等待，等待属于我的那一刻。\n原贴里伊吉网友的话我很喜欢，抄录在这里：\n每个人都希望，自己是独一无二的特殊者 含着金匙出生、投胎到好家庭、工作安排到电力局拿1w月薪这样的小概率事件，当然最好轮到自己 红军长征两万五、打成右派反革命、胼手胝足牺牲尊严去奋斗，最好留给祖辈父辈和别人 自然，不是每个吃过苦的人都会得到回报 但是，任何时代，每一个既得利益者身后，都有他的祖辈父辈奋斗挣扎乃至流血付出生命的身影 羡慕别人有个好爸爸，没什么不可以 问题是，你的下一代，会有一个好爸爸吗？ 至于问到为什么不能有同样的赢面概率？我只能问：为什么物种竞争中，人和猴子不能有同样的赢面概率？ 物竞天择。猴子的灵魂不一定比你卑微，但你身后有几十万年的类人猿进化积淀。\n入对行跟对人 在中国，大概很少有人是一份职业做到底的，虽然如此，第一份工作还是有些需要注意的地方，有两件事情格外重要，第一件是入行，第二件事情是跟人。第一份工作对人最大的影响就是入行，现代的职业分工已经很细，我们基本上只能在一个行业里成为专家，不可能在多个行业里成为专家。很多案例也证明即使一个人在一个行业非常成功，到另外一个行业，往往完全不是那么回事情，“你想改变世界，还是想卖一辈子汽水？”是乔布斯邀请百事可乐总裁约翰·斯考利加盟苹果时所说的话，结果这位在百事非常成功的约翰，到了苹果表现平平。其实没有哪个行业特别好，也没有哪个行业特别差，或许有报道说哪个行业的平均薪资比较高，但是他们没说的是，那个行业的平均压力也比较大。看上去很美的行业一旦进入才发现很多地方其实并不那么完美，只是外人看不见。\n说实话，我自己都没有发大财，所以我的建议只是让人快乐工作的建议，不是如何发大财的建议，我们只讨论一般普通打工者的情况。我认为选择什么行业并没有太大关系，看问题不能只看眼前。比如，从前年开始，国家开始整顿医疗行业，很多医药公司开不下去，很多医药行业的销售开始转行。其实医药行业的不景气是针对所有公司的，并非针对一家公司，大家的日子都不好过，这个时候跑掉是非常不划算的，大多数正规的医药公司即使不做新生意撑个两三年总是能撑的，大多数医药销售靠工资撑个两三年也是可以撑的，国家不可能永远捏着医药行业不放的，两三年以后光景总归还会好起来的，那个时候别人都跑了而你没跑，那时的日子应该会好过很多。有的时候觉得自己这个行业不行了，问题是，再不行的行业，做得人少了也变成了好行业，当大家都觉得不好的时候，往往却是最好的时候。大家都觉得金融行业好，金融行业门槛高不说，有多少人削尖脑袋要钻进去，竞争激励，进去以后还要时时提防，一个疏忽，就被后来的人给挤掉了，压力巨大，又如何谈得上快乐？也就未必是“好”工作了。\n太阳能这个东西至今还不能进入实际应用的阶段，但是中国已经有7家和太阳能有关的公司在纽交所上市了，国美苏宁永乐其实是贸易型企业，也能上市，鲁泰纺织连续10年利润增长超过50%，卖茶的一茶一座，卖衣服的海澜之家都能上市……其实选什么行业真的不重要，关键是怎么做。事情都是人做出来的，关键是人。\n有一点是需要记住的，这个世界上，有史以来直到我们能够预见得到的未来，成功的人总是少数，有钱的人总是少数，大多数人是一般的，普通的，不太成功的。因此，大多数人的做法和看法，往往都不是距离成功最近的做法和看法。因此大多数人说好的东西不见得好，大多数人说不好的东西不见得不好。大多数人都去炒股的时候说明跌只是时间问题，大家越是热情高涨的时候，跌的日子越近。大多数人买房子的时候，房价不会涨，而房价涨的差不多的时候，大多数人才开始买房子。不会有这样一件事情让大家都变成功，发了财，历史上不曾有过，将来也不会发生。有些东西即使一时运气好得到了，还是会在别的时候别的地方失去的。\n年轻人在职业生涯的刚开始，尤其要注意的是，要做对的事情，不要让自己今后几十年的人生总是提心吊胆，更不值得为了一份工作赔上自己的青春年华。我的公司是个不行贿的公司，以前很多人不理解，甚至自己的员工也不理解，不过如今，我们是同行中最大的企业，客户乐意和我们打交道，尤其是在国家打击腐败的时候，每个人都知道我们做生意不给钱的名声，都敢于和我们做生意。而勇于给钱的公司，不是倒了，就是跑了，要不就是每天睡不好觉，人还是要看长远一点。很多时候，看起来最近的路，其实是最远的路，看起来最远的路，其实是最近的路。\n跟对人是说，入行后要跟个好领导好老师，刚进社会的人做事情往往没有经验，需要有人言传身教。对于一个人的发展来说，一个好领导是非常重要的。所谓“好”的标准，不是他让你少干活多拿钱，而是以下三个。\n首先，好领导要有宽广的心胸，如果一个领导每天都会发脾气，那几乎可以肯定他不是个心胸宽广的人，能发脾气的时候却不发脾气的领导，多半是非常厉害的领导。中国人当领导最大的毛病是容忍不了能力比自己强的人，所以常常可以看到的一个现象是，领导很有能力，手下一群庸才或者手下一群闲人。如果看到这样的环境，还是不要去的好。\n其次，领导要愿意从下属的角度来思考问题，这一点其实是从面试的时候就能发现的，如果这位领导总是从自己的角度来考虑问题，几乎不听你说什么，这就危险了。从下属的角度来考虑问题并不代表同意下属的说法，但他必须了解下属的立场，下属为什么要这么想，然后他才有办法说服你，只关心自己怎么想的领导往往难以获得下属的信服。\n第三，领导敢于承担责任，如果出了问题就把责任往下推，有了功劳就往自己身上揽，这样的领导不跟也罢。选择领导，要选择关键时刻能抗得住的领导，能够为下属的错误买单的领导，因为这是他作为领导的责任。\n有可能，你碰不到好领导，因为，中国的领导往往是屁股决定脑袋的领导，因为他坐领导的位置，所以他的话就比较有道理，这是传统观念官本位的误区，可能有大量的这种无知无能的领导，只是，这对于你其实是好事，如果将来有一天你要超过他，你希望他比较聪明还是比较笨？相对来说这样的领导其实不难搞定，只是你要把自己的身段放下来而已。多认识一些人，多和比自己强的人打交道，同样能找到好的老师，不要和一群同样郁闷的人一起控诉社会，控诉老板，这帮不上你，只会让你更消极。和那些比你强的人打交道，看他们是怎么想的，怎么做的，学习他们，然后跟更强的人打交道。\n选择 我们每天做的最多的事情，其实是选择，因此在谈职业生涯的时候不得不提到这个话题。\n我始终认为，在很大的范围内，我们究竟会成为一个什么样的人，决定权在我们自己，每天我们都在做各种各样的选择，我可以不去写这篇文章，去别人的帖子拍拍砖头，也可以写下这些文字，帮助别人的同时也整理自己的思路，我可以多注意下格式让别人易于阅读，也可以写成一堆，我可以就这样发上来，也可以在发以前再看几遍，你可以选择不刮胡子就去面试，也可以选择出门前照照镜子……每天，每一刻我们都在做这样那样的决定，我们可以漫不经心，也可以多花些心思，成千上万的小选择累计起来，就决定了最终我们是个什么样的人。\n从某种意义上来说我们的未来不是别人给的，是我们自己选择的，很多人会说我命苦啊，没得选择阿，如果你认为“去微软还是去IBM”“上清华还是上北大”“当销售副总还是当厂长”这种才叫选择的话，的确你没有什么选择，大多数人都没有什么选择。但每天你都可以选择是否为客户服务更周到一些，是否对同事更耐心一些，是否把工作做得更细致一些，是否把情况了解得更清楚一些，是否把不清楚的问题再弄清楚一些……你也可以选择在是否在痛苦中继续坚持，是否抛弃掉自己的那些负面的想法，是否原谅一个人的错误，是否相信我在这里写下的这些话，是否不要再犯同样的错误……生活每天都在给你选择的机会，每天都在给你改变自己人生的机会，你可以选择赖在地上撒泼打滚，也可以选择咬牙站起来。你永远都有选择。有些选择不是立杆见影的，需要累积，比如农民可以选择自己常常去浇地，也可以选择让老天去浇地，诚然你今天浇水下去苗不见得今天马上就长出来，但常常浇水，大部分苗终究会长出来的，如果你不浇，收成一定很糟糕。\n每天生活都在给你机会，他不会给你一叠现金也不会拱手送你个好工作，但实际上，他还是在给你机会。我的家庭是一个普通的家庭，没有任何了不起的社会关系，我的父亲在大学毕业以后就被分配到了边疆，那个小县城只有一条马路，他们那一代人其实比我们更有理由抱怨，他们什么也没得到，年轻的时候文化大革命，书都没得读，支援边疆插队落户，等到老了，却要给年轻人机会了。他有足够的理由象成千上万那样的青年一样坐在那里抱怨生不逢时，怨气冲天。然而在分配到边疆的十年之后，国家恢复招研究生，他考回了原来的学校。研究生毕业，他被分配到了安徽一家小单位里，又是3年以后，国家第一届招收博士生，他又考回了原来的学校，成为中国第一代博士，那时的他比现在的我年纪还大。生活并没有放弃他，他也没有放弃生活。10年的等待，他做了他自己的选择，他没有放弃，他没有破罐子破摔，所以时机到来的时候，他改变了自己的人生。你最终会成为什么样的人，就决定在你的每个小小的选择之间。\n你选择相信什么？你选择和谁交朋友？你选择做什么？你选择怎么做？……我们面临太多的选择，而这些选择当中，意识形态层面的选择又远比客观条件的选择来得重要得多，比如选择做什么产品其实并不那么重要，而选择怎么做才重要。选择用什么人并不重要，而选择怎么带这些人才重要。大多数时候选择客观条件并不要紧，大多数关于客观条件的选择并没有对错之分，要紧的是选择怎么做。一个大学生毕业了，他要去微软也好，他要卖猪肉也好，他要创业也好，他要做游戏代练也好，只要不犯法，不害人，都没有什么关系，要紧的是，选择了以后，怎么把事情做好。\n除了这些，你还可以选择时间和环境，比如，你可以选择把这辈子最大的困难放在最有体力最有精力的时候，也可以走一步看一步，等到了40岁再说，只是到了40多岁，那正是一辈子最脆弱的时候，上有老下有小，如果在那个时候碰上了职业危机，实在是一件很苦恼的事情。与其如此不如在20多岁30多岁的时候吃点苦，好让自己脆弱的时候活得从容一些。你可以选择在温室里成长，也可以选择到野外磨砺，你可以选择在办公室吹冷气的工作，也可以选择40度的酷热下，去见你的客户，只是，这一切最终会累积起来，引导你到你应得的未来。\n我不敢说所有的事情你都有得选择，但是绝大部分事情你有选择，只是往往你不把这当作一种选择。认真对待每一次选择，才会有比较好的未来。\n选择职业 职业的选择，总的来说，无非就是销售、市场、客服、物流、行政、人事、财务、技术、管理几个大类，有个有趣的现象就是，500强的CEO当中最多的是销售出身，第二多的人是财务出身，这两者加起来大概超过95％。现代IT行业也有技术出身成为老板的，但实际上，后来他们还是从事了很多销售和市场的工作，并且表现出色，公司才获得了成功，完全靠技术能力成为公司老板的，几乎没有。这是有原因的，因为销售就是一门跟人打交道的学问，而管理其实也是跟人打交道的学问，这两者之中有很多相通的东西，他们的共同目标就是“让别人去做某件特定的事情。”而财务则是从数字的层面了解生意的本质，从宏观上看待生意的本质，对于一个生意是否挣钱，是否可以正常运作有着最深刻的认识。\n公司小的时候是销售主导公司，而公司大的时候是财务主导公司，销售的局限性在于只看人情不看数字，财务的局限性在于只看数字不看人情。公司初期，运营成本低，有订单就活得下去，跟客户也没有什么谈判的条件，别人肯给生意做已经谢天谢地了，这个时候订单压倒一切，客户的要求压倒一切，所以当然要顾人情。公司大了以后，一切都要规范化，免得因为不规范引起一些不必要的风险，同时运营成本也变高，必须提高利润率，把有限的资金放到最有产出的地方。对于上市公司来说，股东才不管你客户是不是最近出国，最近是不是那个省又在搞严打，到了时候就要把业绩拿出来，拿不出来就抛股票，这个时候就是数字压倒一切。\n前两天听到有人说一句话觉得很有道理，开始的时候我们想“能做什么？”，等到公司做大了有规模了，我们想“不能做什么。”很多人在工作中觉得为什么领导这么保守，这也不行那也不行，错过很多机会。很多时候是因为，你还年轻，你想的是“能做什么”，而作为公司领导要考虑的方面很多，他比较关心“不能做什么”。\n我并非鼓吹大家都去做销售或者财务，究竟选择什么样的职业，和你究竟要选择什么样的人生有关系，有些人就喜欢下班按时回家，看看书听听音乐，那也挺好，但就不适合找个销售的工作了，否则会是折磨自己。有些人就喜欢出风头，喜欢成为一群人的中心，如果选择做财务工作，大概也干不久，因为一般老板不喜欢财务太积极，也不喜欢财务话太多。先想好自己要过怎样的人生，再决定要找什么样的职业。有很多的不快乐，其实是源自不满足，而不满足，很多时候是源自于心不定，而心不定则是因为不清楚究竟自己要什么，不清楚要什么的结果就是什么都想要，结果什么都没得到。\n我想，我们还是因为生活而工作，不是因为工作而生活，生活是最要紧的，工作只是生活中的一部分。我总是觉得生活的各方方面都是相互影响的，如果生活本身一团乱麻，工作也不会顺利。所以要有娱乐、要有社交、要锻炼身体，要有和睦的家庭……最要紧的，要开心，我的两个销售找我聊天，一肚子苦水，我问他们，2年以前，你什么都没有，工资不高，没有客户关系，没有业绩，处于被开的边缘，现在的你比那时条件好了很多，为什么现在却更加不开心了？如果你做得越好越不开心，那你为什么还要工作？首先的首先，人还是要让自己高兴起来，让自己心态好起来，这种发自内心的改变会让你更有耐心，更有信心，更有气质，更能包容……否则，看看镜子里的你，你满意么？\n有人会说，你说得容易，我每天加班，不加班老板就会把我炒掉，每天累得要死，哪有时间娱乐、社交、锻炼？那是人们把目标设定太高的缘故，如果你还在动不动就会被老板炒掉的边缘，那么你当然不能设立太高的目标，难道你还想每天去打高尔夫？你没时间去健身房锻炼身体，但是上下班的时候多走几步可以吧，有楼梯的时候走走楼梯不走电梯可以吧？办公的间隙扭扭脖子拉拉肩膀做做俯卧撑可以吧？谁规定锻炼就一定要拿出每天2个小时去健身房？你没时间社交，每月参加郊游一次可以吧，周末去参加个什么音乐班，绘画班之类的可以吧，去尝试认识一些同行，和他们找机会交流交流可以吧？开始的时候总是有些难的，但迈出这一步就会向良性循环的方向发展。而每天工作得很苦闷，剩下的时间用来咀嚼苦闷，只会陷入恶性循环，让生活更加糟糕。\nZRong注：从行文风格上看，上面的文字并不像是一个惠普总裁写出来的（下面这段才比较像），但不管是谁写的，仍是一篇好文。\n孙振耀撰文谈退休并畅谈人生\n文/中国惠普前总裁 孙振耀\n虽然离开惠普仅有十五天，但感觉上惠普已经离我很远。我的心思更多放在规划自己第二阶段的人生，这并非代表我对惠普没有任何眷恋，主要还是想以此驱动自己往前走。\n万科王石登珠穆朗玛峰的体验给我很多启发，虽然在出发时携带大量的物资，但是登顶的过程中，必须不断减轻负荷，最终只有一个氧气瓶和他登上峰顶。登山如此，漫长的人生又何尝不是。\n我宣布退休后，接到同事朋友同学的祝贺。大部分人都认为我能够在这样的职位上及年龄选择退休，是一种勇气，也是一种福气。\n还有一部分人怀疑我只是借此机会换个工作，当然还有一些人说我在HP做不下去了，趁此机会离开。\n我多年来已经习惯别人对我的说三道四，但对于好友，我还是挺关心大家是否真正理解我的想法，这也是写这篇文章的目的。\n由于受我父亲早逝的影响，我很早就下定决心，要在有生之年实现自己的愿望，我不要像我父亲一样，为家庭生活忙碌一辈子，临终前感伤，懊恼自己有很多没有实现的理想。\n一本杂志的文章提到我们在生前就应该思考自己的墓志铭，因为那代表你自己对完美人生的定义，我们应该尽可能在有生之年去实现它。\n我希望我的墓志铭上除了与家人及好友有关的内容外，是这样写着：\n1.这个人曾经服务于一家全球最大的IT公司（HP）25年，和她一起经历过数次重大的变革，看着她从以电子仪表为主要的业务变革成全球最大的IT公司。\n2.这个人曾经在全球发展最快的国家（中国）工作16年，并担任HP中国区总裁7年，见证及经历过中国改革开放的关键 最新突破阶段，与中国一起成长。\n3.这个人热爱飞行，曾经是一个有执照的飞行员，累积飞行时数超过X小时，曾经在X个机场起降过。\n4. 这个人曾经获得管理硕士学位，在领导管理上特别关注中国企业的组织行为及绩效，并且在这个领域上获得中国企业界的认可。\n我费时25年才总结1和2两项成果，我不知还要费时多久才能达成3和4的愿望，特别是第4个愿望需要经历学术的训练，才能将我的经验总结成知识。\n否则我的经验将无法有效影响及传授他人。因此重新进入学校学习，拿一个管理学位是有必要的，更何况这是我一个非常重要的愿望。\n另一方面，我25年的时间都花在运营(operation) 的领域，兢兢业业的做好职业人士的工作，它是一份好工作，特别是在HP，这份工作也帮助我建立财务的基础，支持家庭的发展。\n但是我不想终其一生，都陷入在运营的领域，我想象企业家一样，有机会靠一些点子 (ideas)赚钱，虽然风险很高，但是值得一试，即使失败，也不枉走一回，这也是第4个愿望其中的一部份。\nCarly Fiorina 曾经对我说过“这个世界上有好想法的人很多，但有能力去实现的人很少”，2007 年5月21日在北大演讲时，有人问起那些书对我影响较大，我想对我人生观有影响的其中一本书叫“Trigger Point”，它的主要观点是：人生最需要的不是规划，而是在适当的时机掌握机会，采取行动。\n我这些愿望在我心中已经酝酿一段很长的时间，开始的时候，也许一年想个一两次，过了也就忘掉，但逐渐的，这个心中的声音，愈来愈大，出现的频率也愈来愈高，当它几乎每一个星期都会来与我对话时，我知道时机已经成熟。\n但和任何人一样，要丢掉自己现在所拥有的，所熟悉的环境及稳定的收入，转到一条自己未曾经历过，存在未知风险的道路，需要绝大的勇气，家人的支持和好友的鼓励。有舍才有得，真是知易行难，我很高兴自己终于跨出了第一步。\n我要感谢HP的EER提前退休优惠政策，它是其中一个关键的Trigger Points,另一个关键因素是在去年五六月发生的事。\n当时我家老大从大学毕业，老二从高中毕业，在他们继续工作及求学前，这是一个黄金时段，让我们全家可以相聚一段较长的时间，我为此很早就计划休一个长假，带着他们到各地游玩。\n但这个计划因为工作上一件重要的事情（Mark Hurd 访华）不得不取消。这个事件刺激了我必须严肃的去对待那心中的声音，我会不会继续不断的错失很多关键的机会?\n我已经年过50，我会不会走向和我父亲一样的道路？人事部老总Charles跟我说，很多人在所有对他有利的星星都排成一列时，还是错失时机。\n我知道原因，因为割舍及改变对人是多么的困难，我相信大部分的人都有自己人生的理想，但我也相信很多人最终只是把这些理想当成是 幻想，然后不断的为自己寻找不能实现的藉口，南非前总统曼德拉曾经说过，“与改变世界相比，改变自己更困难”，真是一针见血。\n什么是快乐及有意义的人生？我相信每一个人的定义都不一样，对我来说，能实现我墓志铭上的内容就是我的定义。\n在中国惠普总裁的位置上固然可以吸引很多的关注及眼球，但是我太太及较亲近的好友，都知道那不是我追求的，那只是为扮演好这个角色必须尽力做好的地方。\n做一个没有名片的人士，虽然只有十多天的时间，但我发现我的脑袋里已经空出很多空间及能量，让我可以静心的为我Chapter II的新生活做细致的调研及规划。\n我预订以两年的时间来完成转轨的准备工作，并且花多点时间与家人共处。这两年的时间我希望拿到飞行执照，拿到管理有关的硕士学位，提升英文的水平，建立新的网络，多认识不同行业的人，保持与大陆的联系。希望两年后，我可以顺利回到大陆去实现我第四个愿望。\n毫不意外，在生活上，我发现很多需要调整的地方。\n二十多年来，我生活的步调及节奏，几乎完全被公司及工作所左右，不断涌出的deadline及任务驱动我每天的安排，一旦离开这样的环境，第一个需要调整的就是要依靠自己的自律及意志力来驱动每天的活动，睡觉睡到自然醒的态度绝对不正确，放松自己，不给事情设定目标及时间表，或者对错失时间目标无所谓，也不正确，没有年度，季度，月及周计划也不正确。\n担任高层经理多年，已经养成交待事情的习惯，自己的时间主要花在思考，决策及追踪项目的进展情况，更多是依靠一个庞大的团队来执行具体的事项及秘书来处理很多协调及繁琐的事情。\n到美国后，很多事情需要打800号电话联系，但这些电话很忙，常让你在waiting line上等待很长的时间，当我在等待时，我可以体会以前秘书工作辛苦的地方，但同时也提醒我自己，在这个阶段要改变态度，培养更大的耐性及自己动手做的能力。\n生活的内容也要做出很大的调整，多出时间锻炼身体，多出时间关注家人，多出时间关注朋友，多出时间体验不同的休闲活动及飞行，一步步的，希望生活逐步调整到我所期望的轨道上，期待这两年的生活既充实又充满乐趣及意义。\n第一个快乐的体验就是准备及参加大儿子的订婚礼，那种全心投入，不需担忧工作数字的感觉真好。同时我也租好了公寓，买好了家具及车子，陪家人在周末的时候到Reno 及Lake Tahoe玩了一趟，Lake Tahoe我去了多次，但这次的体验有所不同，我从心里欣赏到它的美丽。\n但同时我也在加紧调研的工作，为申请大学及飞行学校做准备，这段时间也和在硅谷的朋友及一些风险投资公司见面，了解不同的产业。\n我的人生观是“完美的演出来自充分的准备”，“勇于改变自己，适应不断变化的环境，机会将不断出现”，“快乐及有意义的人生来自于实现自己心中的愿望，而非外在的掌声”。\n我离开时，有两位好朋友送给我两个不同的祝语，Baron的是“多年功过化烟尘”，杨华的是“莫春者，风乎舞雩，咏而归”，它们分别代表了我离开惠普及走向未来的心情。\n我总结人生有三个阶段，一个阶段是为现实找一份工作，一个阶段是为现实，但可以选择一份自己愿意投入的工作，一个阶段是为理想去做一些事情。\n我珍惜我的福气，感激HP及同事、好朋友给我的支持，鼓励及协助，这篇文字化我心声的文章与好友分享。\n","date":"2007-12-23","description":"","lastmod":"2007-12-23T12:52:41Z","slug":"416","tags":["life","live"],"title":"【转】中国惠普前总裁孙振耀谈工作与生活","url":"https://blog.zengrong.net/post/416/"},{"categories":["web"],"content":"作者：青松 来源：飞腾电脑网络科技（原网站已经不能访问，2012-05-18撤下链接）\nZRong略加改动\n2012-05-18更新:加入VPI/VCI截图\n电信“我的e家”配套的中兴531B蓝猫（固件版本 ZXDSL 531BIIV2.2.1c_E17），本身集成ADSL猫、路由器、4口HUB、无线AP为一体，功能较全较强大。但中国电信已将路由菜单屏弊，使用户无法使用路由功能。现本站将带您如何将该设备的所有功能打开。\n中兴531B是中国电信是向中兴定制的专用设备，早期中兴531B是有以上所有功能的，中国电信定制后本设备后就只有中国电信才有了，经本站技术人员测试,只要将该设备的固件升级为中兴原固件程序即可。因为官方网站没有提供固件升级软件下载，所以本站将升级方法及中兴早期的官方固件提供下载！\n升级前的中兴531B\n升级后的中兴531B\n1、下载本站提供的官方固件升级程序（固件版本为 ZXDSL 531BIIV2.2.3a_E17）。\n1 文件 下载后的文件是7z格式压缩文件，可以使用WinRAR解压缩，得到ZXDSL 531BIIV2.2.3a_E17.tar；\n2、登陆中兴531B管理页面。（默认地址是：192.168.1.1，默认用户名及密码都是：admin)\n3、选择升级后浏览您刚才下载的升级文件：ZXDSL 531BIIV2.2.3a_E17.tar（注意：本升级固件不是tar压缩文件，无需再解压，选择直接升级即可）\n4、请等待约3分钟左右升级过过程，此时请不要关闭网页或执行其它操作。\n5、升级成功！请重启并关闭本网页，重新打开新页面，大功告成！\nVPI/VCI设置\n刷新固件后，默认的VPI/VCI设置是0/38，这样的设置是无法正常拨号上网的，武汉需要将VCI改为32才能正常拨号成功，见下图：\n==================================================\n中兴无线ADSL路由器531B，是ADSL路由器与无线AP二合一设备，可以提供ADSL接口、以太网接口和无线接口，上行通过DSLAM设备接入宽带城域网，下面通过无线接口和以太网接口为用户提供灵活的宽带接入方式。\n功能特点\n(1) 基于IEEE802.11b标准的无线ADSL MODEM,最高速率11M, 11,5.5,2,1M自适应；\n(2) 易于操作的中文配置界面程序；\n(3) 64/128位WEP加密；\n(4) 室内覆盖范围30-70米；\n(5) 内置天线；\n(6) 220V交流供电；\n(7) 支持远程版本在线升级；\n(8) 接口类型：一个WAN口、无线（WLAN）口、四个以太网口；\n(9) 兼容G.dmt (G.992.1)、G.lite (G.992.2)和T1.413标准，速率自适应，步长为32Kbps；\n(10) 支持ATM AAL5业务，UBR,CBR,VBR-nrt；\n(11) 支持桥接模式下支持8条PVCs, 路由模式下支持5条PVCs；\n(12) 支持SNMP网管,SNMP TRAP；\n(13) 支持Telnet,WEB,SNMP方式的配置管理,支持配置文件上传下载；\n(14) 支持IEEE 802.1d桥接和生成数协议；\n(15) 支持IP,IGMP,PPPoE包过滤；\n(16) 支持静态路由，RIP1/2动态路由，支持NAT；\n(17) 支持DHCP CLIENT/SERVER。\n","date":"2007-12-08","description":"","lastmod":"2007-12-08T13:39:49Z","slug":"zte-adsl-modem-531b2","tags":["adsl","wifi","router"],"title":"【转】中兴531B（蓝色）固件升级 恢复原厂路由功能","url":"https://blog.zengrong.net/post/zte-adsl-modem-531b2/"},{"categories":["technology"],"content":"Flash Player 9.0.115 发布了，这是一个支持H.264的正式版本。关于H.264，已经说得太多，播放H.264的视频也很简单，用NetStream即可（也就是播放FLV的那一套，想了解的可以看 这篇官方提供的教程），但是怎么把现有的视频压缩成Flash Player支持的H.264格式呢？\n首先，要了解Flash Player 9.0.115对视频编码到底支持到哪个程度。 下面是Adobe官方提供的一个Flash Player支持的视频编码列表（原文）：\n视频编码 SWF 文件格式版本(发布的版本) 支持播放的最小Flash Player版本 Sorenson Spark 6 6 On2 VP6 6 8 H.264 (MPEG-4 Part 10) 9 9.0.115.0 音频编码 SWF 文件格式版本(发布的版本) 支持播放的最小Flash Player版本 ADPCM 6 6 MP3 6 6 HE-AAC (MPEG-4 Part 3) 9 9.0.115.0 Flash Player 可以播放视频编码为H.264，音频编码为AAC的标准MPEG-4文件。文件的扩展名可以是：F4V, MP4, M4A, MOV, MP4V, 3GP, 3G2等等。\n注意：如果没有特别说明，下面的Flash Player都指Flash Player 9(v 9.0.115.0)\nFlash Player 9 (v 9.0.115.0) 支持下面的 MPEG-4 标准的子集：\nMPEG-4 标准 Flash Player Update 3 ISO/IEC 14496-3 (Audio AAC) AAC Main; AAC LC; SBR ISO/IEC 14496-10 (Video AVC) Base (BP); Main (MP); High (HiP). All levels are supported. ISO/IEC 14496-12 (Container) 1 Audio track; 1 Video track 3GPP TS 26.245 (Timed text format) Full support 那么，什么是H.264？“MPEG4 Part 10”是什么？它和“Part 3”、“Part 2” 有什么关系？可以看这篇文章。\n简单的讲，H.264、MPEG4 Part 10、MPEG4 AVC和ISO/IEC 14496-10 都是一个东东，就是一种视频编码格式，同时也是高清电影采用的视频编码格式之一（另外两种是MPEG2和VC-1）。\n而平常我们熟悉的DivX以及XviD编码，都属于MPEG4标准的范畴，但它们属于MPEG4 Part 2，Flash Player是不支持它们的。\n了解了基础知识之后，我们首先需要找到一个编码工具 选择一个优秀的压缩工具至关重要。Flash CS3自带的用于压缩FLV的压缩器自然是无法胜任了。可是网上的视频转换工具多如牛毛，要找一款好用的真的很难。本着 “免费、易用、专业、通吃” 这四个标准，经过大量测试，终于找到一款优秀的国产编码软件MediaCoder（中文名：影音转码宝盒），以下是来自MediaCoder官方网站的介绍：\nMediaCoder是一个免费的通用音频/视频批量转码工具，它将众多来自开源社区的优秀音频视频编解码器和工具整合为一个通用的解决方案，可以将音频、视频文件在各种格式之间进行转换。\n功能和特点\n基于优秀的众多的开源编解码后台，能够解码和编码的格式多 极为丰富的可调整的编码参数 全部编解码器自带，不依赖于系统的编解码器和任何组件 良好的可扩展的程序架构，快速适应新的需求，不断增加新的格式的支持 利用脚本语言扩展的界面，有支持众多影音设备（如PSP、iPod）的专用界面 高性能，特别在双核处理器上表现优异 典型应用\n提高影音文件压缩率，减小其文件尺寸 转换至可在各种影音设备上播放的影音文件，如MP3播放器、MP4播放器、手机、PDA、VCD/DVD播放机 提取视频文件中的音轨并转换成MP3、AAC、WMA等音频文件 修复和改善一些损坏的、部分下载的或质量不佳的影音文件 支持格式\nMP3, Vorbis, AAC, AAC+, AAC+v2, MusePack, Speex, AMR, WMA, RealAudio, mp3PRO* FLAC, WavPack, Monkey's Audio, OptimFrog, AAC Lossless, WMA Lossless, WAV/PCM H.264, Xvid, MPEG 1/2/4, Theora, Flash Video, Dirac, 3ivx*, RealVideo*, Windows Media Video AVI, MPEG/VOB, Matroska, MP4, RealMedia*, ASF, Quicktime*, OGM* CD, VCD, DVD,CUE Sheet* * 仅支持输入\n编码工具搞定，接着就可以“制作”影片了 我采用的源片是著名的Backkom Assa Game Contest片段，片源编码如下：\nG:\\Movie\\Backkom\\Assa Game Contest.wmv\nGeneral\nComplete name : G:\\Movie\\Backkom\\Assa Game Contest.wmv\nFormat : Windows Media\nFile size : 1.89 MiB\nPlayTime : 33s 991ms\nBit rate : 467 Kbps\nMovie name : Assa\nPerformer : Aaron Lim\nCopyright : rg animation studios\nComment : rg animation studios\nVideo\nCodec : WMV3\nCodec/Info : Windows Media Video 9\nBit rate : 408 Kbps\nWidth : 320 pixels\nHeight : 240 pixels\nAspect ratio : 4/3\nAudio\nCodec : WMA2\nCodec/Info : Windows Media Audio 2\nBit rate : 48 Kbps\nChannel(s) : 2 channels\nSampling rate : 44 KHz\n在压缩前，最重要的就是在MediaCoder中选择视频编码和音频编码。根据上面的表格我们知道，Flash Player 9.0.115支持H.264视频编码和HE-AAC音频编码。下图是我在MediaCoder中的设置。\nH.264视频设置 HE-AAC音频设置\n视频大小设置\n将这三个设置调整好即可。H.264的编码优于WMV9，所以在压缩的时候，选择的视频和音频码率都小于源文件的码率。\n转换完后的视频扩展名为MP4，详细编码信息如下：\nG:\\Movie\\Backkom\\Assa Game Contest.mp4\nGeneral\nComplete name : G:\\Movie\\Backkom\\Assa Game Contest.mp4\nFormat : MPEG-4\nFormat/Info : ISO 14496-1 Base Media\nFormat/Family : MPEG-4\nFile size : 1.40 MiB\nPlayTime : 33s 920ms\nBit rate : 347 Kbps\nStreamSize : 11.2 KiB\nEncoded date : UTC 2007-12-06 15:51:38\nTagged date : UTC 2007-12-06 15:51:38\nVideo #1\nCodec : H.264\nCodec/Info : H.264 (3GPP)\nPlayTime : 33s 920ms\nBit rate : 314 Kbps\nWidth : 320 pixels\nHeight : 240 pixels\nAspect ratio : 4/3\nFrame rate : 25.000 fps\nBits/(Pixel*Frame) : 0.160\nStreamSize : 1.27 MiB\nEncoded date : UTC 2007-12-06 15:51:38\nTagged date : UTC 2007-12-06 15:51:38\nAudio #2\nCodec : AAC LC-SBR\nCodec/Info : AAC Low Complexity with Spectral Band Replication\nPlayTime : 32s 415ms\nBit rate : 32 Kbps\nBit rate mode : VBR\nChannel(s) : 2 channels\nSampling rate : 44 KHz\nResolution : 16 bits\nStreamSize : 126 KiB\nEncoded date : UTC 2007-12-06 15:51:38\nTagged date : UTC 2007-12-06 15:51:38\n此视频在Flash Player 9.0.115中顺利播放，声音也没有问题。\n研究一下视频和音频的组合 到了这里，就有一些问题了。H.264视频是否一定要搭配AAC音频呢？搭配MP3行么？我压缩了一个采用H.264视频编码，Mp3音频编码（采用LAME MP3）的影片，用终极解码播放正常，但是在Flash Player中，只有图像可以显示，却听不到声音。下面是这个文件的编码信息：\nF:\\Material\\Flash Platform\\Flash\\Cases\\播放H.264视频\\333.mp4\nGeneral\nComplete name : F:\\Material\\Flash Platform\\Flash\\Cases\\播放H.264视频\\333.mp4\nFormat : MPEG-4\nFormat/Info : ISO 14496-1 Base Media\nFormat/Family : MPEG-4\nFile size : 2.59 MiB\nPlayTime : 33s 920ms\nBit rate : 641 Kbps\nStreamSize : 13.9 KiB\nEncoded date : UTC 2007-12-05 09:21:21\nTagged date : UTC 2007-12-05 09:21:21\nVideo #1\nCodec : H.264\nCodec/Info : H.264 (3GPP)\nPlayTime : 33s 920ms\nBit rate : 516 Kbps\nWidth : 320 pixels\nHeight : 240 pixels\nAspect ratio : 4/3\nFrame rate : 25.000 fps\nBits/(Pixel*Frame) : 0.260\nStreamSize : 2.09 MiB\nEncoded date : UTC 2007-12-05 09:21:21\nTagged date : UTC 2007-12-05 09:21:21\nAudio #2\nCodec : MPEG-1 Audio\nPlayTime : 32s 365ms\nBit rate : 128 Kbps\nBit rate mode : CBR\nChannel(s) : 2 channels\nSampling rate : 44 KHz\nResolution : 16 bits\nStreamSize : 506 KiB\nEncoded date : UTC 2007-12-05 09:21:21\nTagged date : UTC 2007-12-05 09:21:21\n又测试了H.264+MP3用AVI封装和3GP封装，在Flash Player中都无法播放声音。看来只有H.264+AAC可以被Flash Player支持。封装可以使用3GP或者MP4，但不支持MKV封装。\n关于封装容器和视频文件后缀 我们知道，我们不能仅仅从视频文件的扩展名判断文件的编码。因为很多扩展名是支持多种编码的。例如AVI就只是一种封装容器，它里面的视频和音频编码可以有很多种组合。可以是DivX，也可以是XviD，还可以是MPEG-1。下面的表格（表格来源，ZRong做部分调整）简单的描述了几种封装容器和视频、音频编码的对应情况：\n封装容器 视频流编码格式 音频流编码格式 Flash Player是否支持 AVI Xvid MP3 不支持 AVI Divx MP3 不支持 MKV Xvid MP3 不支持 MKV Xvid AAC 不支持 MKV H.264 AAC 不支持 MP4 H.264 AAC 支持 3GP H.263 AAC 不支持 3GP H.264 AAC 支持 FLV Sorenson Spark MP3 支持 FLV On2 VP6 MP3 支持 从上表可以看出，要让Flash Player成功播放H.264视频，最好采用3GP或者MP4封装容器。\nAAC编码的混乱情况 上面的压缩，AAC编码器使用的都是Nero Encoder，其中“规格”有这样几个选项：Auto、LC-AAC、LE-AAC和HE-AAC v2。经过测试，这几个规格压缩的音频都可以被Flash Player支持。\nNero Encoder的选项\n如果采用CT AAC+编码器，选项就变成了下面这样，更加复杂了。经过测试，这几个选项的组合也都可以被Flash Player支持。不论选择MPEG-4 AAC还是MPEG-2 AAC，都没有问题。\nCT AAC+的选项\n如果采用FAAC编码器，选项就变成了下面这样。经过测试，这几个选项的组合也都可以被Flash Player支持，MPEG版本的选择也没有关系。\nFAAC的选项\n事实上，NERO Encoder和CT AAC+中的LC-AAC，就是FAAC中的Low Complexity（无长时预测的AAC）；而NERO Encoder中的HE-AAC，就是CT AAC+中的aacPlus，它加了SBR(spectral band replication)，HE代表high efficiency。这也是为什么前面压缩WMV视频的时候，选择的是HE-AAC编码，在编码之后的文件信息里面显示的却是下面这些内容的原因了：\nCodec : AAC LC-SBR\nCodec/Info : AAC Low Complexity with Spectral Band Replication\n根据Adobe公布的信息，Flash Player支持这些AAC编码：AAC Main; AAC LC; SBR ，因此，我一般选择使用NERO Encoder的HE-AAC。\n参考链接 http://bbs.lmtw.com/dispbbs.asp?boardID=111\u0026amp;ID=141583\u0026amp;page=1 http://mediacoder.sourceforge.net/index_zh.htm http://www.sxzkw.com/1Qm/MediaCoder_jiaocheng_cn.swf http://www.adobe.com/devnet/flashplayer/articles/http://www.adobe.com/devnet/flashplayer/articles/hd_video_flash_player.html http://tech.163.com/05/0624/11/1N0Q0HJ800091589.html http://kb.adobe.com/selfservice/viewContent.do?externalId=kb402866\u0026amp;sliceId=1 ","date":"2007-12-06","description":"","lastmod":"2007-12-06T16:17:18Z","slug":"h264-and-flv","tags":["aac","codec","flashplayer","flv","h264","flash"],"title":"抛弃FLV，迎接MP4——制作Flash Player支持的H.264视频格式","url":"https://blog.zengrong.net/post/h264-and-flv/"},{"categories":["impressions"],"content":"有这样一则故事：\n乌鸦站在树上，整天无所事事，兔子看见乌鸦，就问：我能像你一样，整天什么事都不用干吗？乌鸦说：当然，有什么不可以呢？于是，兔子在树下的空地上开始休息，忽然，一只狐狸出现了，它跳起来抓住兔子，把它吞了下去。\n如果你想站着什么事都不做，那你必须站的很高，非常高。\n我认为，在不会飞以前，做一只忙碌的兔子好过做一只站得不高的乌鸦。\n","date":"2007-12-02","description":"","lastmod":"2007-12-02T12:11:54Z","slug":"392","tags":[],"title":"做兔子还是做乌鸦？","url":"https://blog.zengrong.net/post/392/"},{"categories":["tutorial"],"content":" 1 文件 ","date":"2007-11-27","description":"","lastmod":"2007-11-27T17:32:04Z","slug":"390","tags":["training"],"title":"07-11-28 枝江竞赛培训演示资料下载","url":"https://blog.zengrong.net/post/390/"},{"categories":["technology"],"content":"配置好的一台FMS，转移到了另一台主机上，只是目录发生了变化。我更新了配置文件中的路径，但仍然不能使用。在core Log中找到这样的错误提示：\nAsynchronous I/O operation failed (Failed to attach to completion port: 參數錯誤。 87)\n查了配置都没有什么错，于是就在google上搜了一下，找到这条信息。和文中提到的一样，碰巧新的主机上也安装了NOD32杀毒软件。于是卸载之，重启服务器，测试成功。\n","date":"2007-11-19","description":"","lastmod":"2007-11-19T15:10:02Z","slug":"fms-nod32-error","tags":["fms"],"title":"NOD32造成FMS出现“...參數錯誤。 87”错误","url":"https://blog.zengrong.net/post/fms-nod32-error/"},{"categories":["technology"],"content":"Alessandro Crugnola在博客中提到了9个Flex开发框架：\nCairngorm、 PureMVC、ARP、MVCS、Flest、Model-Glue:Flex、ServerBox Foundry、Guasax和Slide。Luke Bayes 和 Ali Mills对这9个框架进行了比较，认为PureMVC是较好的开发框架。\n同时，有网友在这篇文章的留言中介绍了这个框架：VEGAS，除了支持Flex开发，还支持FMS和FCS。\nAS2 : Flash MX2004 or Flash8. AS2 : MTASC AS2 : Eclipse and FTD AS3 : Flex 2 SDK. SSAS : FC 1.5 or Flash Media Server 2 SSAS : FMS Eclipse SSAS : JSEclipse ","date":"2007-11-14","description":"","lastmod":"2007-11-14T05:34:43Z","slug":"388","tags":["flex"],"title":"Flex开发框架","url":"https://blog.zengrong.net/post/388/"},{"categories":["web"],"content":"早就听说WordPress2.3原生支持Tag，只是一直没有时间升级。昨天终于有点空，把]WordPress升级到了2.3.1。\n升级过程没有什么问题，但备份是一定要做好的。升级之后，由于已经直接支持Tag，本想取消原来使用的Simple Tagging，却没曾想Wordpress自带的Tag系统并不支持Tag管理、相关文章等等功能，也不支持在写文章的时候，自动显示已有的相关Tag。这就让人郁闷了。于是找到了Simple Tagging的升级版Simple Tags，除了支持以上功能外，还支持批量管理Tag（这个很方便给原来没有加Tag的文章添加Tag）、自动在页面中加入Tag显示（不需要改模版哦）以及根据文章内容自动生成Tag等等，真是“好用得不得了”啊！ :em23:\n给几张图：\n另外，把模版换成了Sandbox，顺便可以练练我荒废多年的CSS。想想当初做网站设计时全手写HTML和CSS代码的疯狂，不由得慨叹岁月如歌，青春易逝啊 :em17:\n","date":"2007-11-12","description":"","lastmod":"2007-11-12T15:03:24Z","slug":"wordpress231-sandbox","tags":["cms","sandbox","wordpress","plugin"],"title":"WordPress2.3.1与Sandbox","url":"https://blog.zengrong.net/post/wordpress231-sandbox/"},{"categories":["others"],"content":"作者:xbeta　版本:070420/070110　出自:善用佳软http://xbeta.info\n上接【转】点评“最好的300款免费软件”(3)\n系统工具类：续\nRainlendar Lite - 桌面日历兼记事。相当好，此类软件第一名。界面美观，支持换肤，体积小巧，占资源少，功能实用。目前版本2.2，下载2MB，有中文界面。\nWeather Watcher - 天气预报\nSubtitle Workshop - 字幕编辑\nAnt Movie Catalog - 影片管理\nDisclib - CD管理\nDexpot - 虚拟桌面，另外有几款也不错，比如《MS的免费虚拟桌面工具 Virtual Desktop Manager》。\nDriveImage XML - 制作分区镜像，代替收费的Ghost，详见此文。\nMozBackup - 备份和恢复书签\nSyncBack - 系统备份\nAtomic Cock Sync - 时钟同步。xbeta要推荐的是一个仅7kb的软件Neutron，参见《介绍bosskey \u0026amp; keir.net》。\nCitrus Alarm Clock - 闹钟工具\nTaskSwitchXP - 替代Alt-Tab切换工具\nLaunchy - 程序启动\nallSnap - make all windows snap\nSysinternals Tools - 多种系统工具\nStrokeIt - 鼠标手势工具。此领域独一无二的精品，你很难想象这个100KB的软件能有这么多方便和实用的功能。甚至有人不用Maxthon和Firefox的鼠标手势，而一律用它。晃一下鼠标，一个程序随之启动，确实很酷。那就试试它吧！\nNet Profiles - 为网络设置创建参数文件。\nResourceHacker - 查看、修改、更名、添加、删除资源\nJava Runtime Environment - java运行文件\n【小结】程序启动，xbeta现在的观点是《用win+r启动常用程序及文档：谁比它更绿色》。有一个不知名但实力相当强的《无名高手HotkeyP:小巧强大热键工具》，另外几款在《xbeta精心荐精品之免费软件列表》中相关部分做了点评。另有一些特别小巧的exe，如下：\n图3 xbeta收藏的一些软件\nUI Enhancements/界面强化\nRocketDock - 程序启动工具\nAveDesk - 桌面强化\nIconPhile - 个性化windows系统图标\nCursorXP Free - 更改鼠标指针\nMacSound - 控制音量\nLClock - Windows Vista的时钟\nY’z Dock - 程序启动工具\nY’z Shadow - 窗口阴影效果\nY’z Toolbar - 改变资源管理器和IE工具栏图标\nTaskbar Shuffle - 重新排列任务栏程序的位置\nVisual Task Tips - 为任务栏程序显示缩略图\nBadges - 为任何文件或目录增加装饰\nFolderico - 改变目录的图标\nFolder Marker - 标记目录\nFolder2MyPC - 为“我的电脑”加入常用目录。自从俺用上Total Commander后，就再不用这类软件了，参见《TotalCommander/ 文件管理》。\nMicrosoft TweakUI - 系统设置。这款软件是比较有名的。\nBricoPacks - shell packs\nShellPacks - shell packs。说到其他shell，不能不提BBlean和《LiteStep 试用记(开源免费shell)》。\nTango Shell Patcher - shell patcher\nXPize - 界面强化\nVista Transformation Pack - 完整的视觉效果\nVista Sound Scheme - Windows Vista声效\nRoyale Theme - 视觉主题\nHardware monitoring\u0026amp;Benchmarking/硬件\nCPU-Z - cpu检测\nCrystalCPUID - cpu检测\nCentral Brain Identifier - cpu检测\nEverest - 系统信息\nSiSoft Sandra - 系统信息\nSpeedFan - 硬件监测\nMemtest86 - 内存测试\nPowerMax - 硬盘检测\n3Dmark 06 - 3D游戏性能测评\nAquamark - 性能测评\nrthdribl - 3D测评\nFraps - 3D测评，帧频查看和录屏\nPrime 95 - cpu测评\nSuperPI - cpu测评\nCPU Rightmark - cpu overclock\nCore Temp - cpu温度监测\nATiTool - video overclock\nATI Tray Tools - Radeon tweaker\naTuner - GeForce and Radeontweaker\nRivaTuner - video overclock\nNokia Monitor Test - 调整显示器\nUDPixel - 修复坏点\nGames/游戏\n123 Free Solitaire - solitaire游戏包\nArcade Pack - 经典arcade游戏\nLive For Speed - 在线赛车模拟器\nEnigma - 猜谜\nFreeciv - 多人对战\nTux Racer - 雪山赛\nEducation/教育\nSpeQ Mathematics - 数学软件，详细介绍见这里\nDia - 比较经典的绘图软件，虽然不太好用，但此类软件免费的不多，凑和着吧\nGoogle Earth - Google出品的卫星地图软件，能看到你家的小区吗？有人说能看到小区门口的报亭。\nNASA World Wind - 3D虚拟地球。性质同上，NASA出品\nCelestia - 太空探索\nStellarium - 借助它来仰视星空吧，象康德那样面对它和心中的道德一起警醒吧。\nMiscellaneous/杂项\nnLite - 让Windows安装可以自定义\nVirtualPC - 虚拟机软件，VMware也不错。\ngrabMotion - 摄像头软件\niDailyDiary - 日记软件，每天一页\nPivot Stickfigure Animator - 这个很有趣，制作火柴棍样式的动画\nWink - 录屏软件，可以归入视频类。隆重推荐，搜索前文吧，详见《善用wink，制作声画并茂的falsh教程》。\nScribus - 一个专业的开源桌面排版系统,可以输出商用级别的PDF和 Postscript文件\nFreeMind - 开源的思维导图(MindMap)软件，画出来的图形漂亮简洁。可用于方案管理、整理思路、提纲要点。\n图4 FreeMind做的思维导图示例\nWindows Live Writer - 看名字就明白了。\nWallpapers/壁纸\nMichael Swanson - 1920 x 1200; 1600 x 1200;\nMikhail Arkhipov - 1920 x 1200; 1600 x 1200;\n【全文总结】本文总体不错，但有些很好的软件乃至类别确实遗漏了，比如剪贴板类《CLCL ClipX等几款相当好的剪贴板增强程序》，精品热键工具Hoekey，仅12KB，却功能超强…… 或者说，按xbeta的标准，如果也选出100余种免费软件，那就是《xbeta精心荐精品之免费软件列表》。\n欢迎指正。希望本文能有助您及周围的人减少一些盗版——这才是本文意义所在。\n","date":"2007-11-10","description":"","lastmod":"2007-11-10T08:51:17Z","slug":"xbeta-freeware300-4","tags":["freeware","software","xbeta"],"title":"【转】点评“最好的300款免费软件”(4)","url":"https://blog.zengrong.net/post/xbeta-freeware300-4/"},{"categories":["news"],"content":"作者:xbeta　版本:070622/070110　出自:善用佳软blog.sina.com.cn/xbeta\n上接【转】点评“最好的300款免费软件”(2)\nVideo/视频 Windows Media Player - 播放器，Windows自带，无须解释。\nVLC - 视频播放器，不错。\nMedia Player Classic - 视频播放器。最经典的一款，界面简洁，体积小巧。\nMV2Player - 视频播放器。\nCrystalPlayer 1.95 - 视频播放器。\nZoom Player - 视频播放器。不错。\nGOM Player - 视频播放器。不错。\nviPlay - 视频播放器。\nDSPlayer - 视频播放器。\nVirtualDub - 视频播放器。\nCamStudio - 录屏软件。这种功能的免费软件不多，所以它算不错。但是，不推荐用，因为有更好的wink和webex，见本段小结。\n图1 CamStudio的主界面\nAviSplit - Avi分割。\nVideo mp3 Extractor - 从视频文件中抽取音频mp3。\nFree iPod Converter - 把常见的视频格式转为iPod支持的格式。\nMediaPortal - 把你的PC变为一个媒体中心。\nThe FilmMachine\n【小结】播放器，也是各取所爱。很知名的Kmplayer，mplayer未收录，它们当然不比上述所列软件差。\n要强调的是，录屏软件xbeta推荐wink。它的功能有：录制屏幕操作，可插入文字注释、同步或事后录音，导出为flash或pdf，更多内容见《善用wink，制作声画并茂的falsh教程》。\n当然，wink作为个人开发的软件，有个小缺点就是：录制时间过长（比如1小时）会占用资源过多，而失去响应。而webex则是一款商用软件（但在版 权方面未做非free的界定），也可以录屏带录音，但编辑功能较弱。xbeta曾用它录过一小时的操作，生成的文件（独有格式，只能用webex播放）不 到 5MB，更多介绍见《小巧的商用级录屏软件：WebEx Recorder》、《avi格式录屏工具Freez Screen Video Capture》、《screen2exe:小巧的录屏软件（附:录屏软件总览）》。\nImage/图像 Gimp - 图像编辑器。它被称为“Linux下PhotoShop”，功能强大，内含几乎所有图象处理所需的功能。 GIMP在Linux系统推出时就成为许多绘图爱好者的首选，它的接口相当轻巧，但其功能却不输于专业的绘图软件；它提供了各种的影像处理工具、滤镜，还 有许多的组件模块，对于要制作一个又酷又炫的网页按钮或网站Logo来说是一个非常方便好用的绘图软件，因为它也提供了许多的组件模块，你只要稍加修改一 下，便可制作出一个属于你的网页按钮或网站Logo。如今推出了For Windows版本，还不赶快试试更多介绍见专文《善用GIMP，图像处理轻松又自由》。\nxbeta一直认为，PhotoShop是专业级的图像处理工具，售价人民币5000元绝对物有所值。而相当多“聪明” 的国内用户却盗来做一些最最简单，分明有更方便的软件可以完成的编辑工作，实在是盗版积习已久。甚至有些人把图片处理称为“PS一下，甚可悲也。\nPhotoFiltre - 图像编辑器。如果觉得GIMP功能太多，体积还大（比Photoshop小多了），不妨试一下这个功能不少，但体积更加精简的工具。?它的安装包 1.5MB，安装后3.6MB。?它插件丰富，自带多个图像特效滤镜，使用它们可方便地做出各式各样的图像特效；文本输入功能颇具特色，有多种效果可供选 择，并能自由地调整文本角度；内置PhotoMasque（图像蒙板）编辑功能。常用的锐化，模糊，对比度，亮度调节一个都不缺，而仿制图章工具，艺术画 笔，套索也是应有尽有。\nPaint.net - 图像编辑器。当然不错，为华盛顿大学学生所开发，后受到微软支持，虽然时间不长，但功能强大，且最近更新很快。\nArtRage - 谢网友指正。这是一个绘画软件，像painter一样，用它在电脑上模拟油画比较。\nArtweaver - 图像编辑器。有点象photoshop和painter的杂交品,可以试试，有中文版。\n【小结】以上为编辑器。xbeta基本认可作者的观点，把GIMP、PhotoFiltre、Paint.net列为前3也较恰当。如果补充一个，就 是 Pixia，?它是产于日本，有中英文版，包揽了图形处理软件绝大部分常用功能，操作起来也很方便，详见《》。如果你要选一个，xbeta的建议是：求经 典功能丰富就选GIMP；小巧一些就PhotoFiltre；重视未来的更新和与新版OS的兼容性就Paint.Net。Pixia前几年还比较热的，现 在大概要让给位给Paint.Net了。\nIrfanView - 图像浏览器。xbeta至为推崇，它的功能、快捷键、小巧的体积、强大的批处理、不断更新的追求完美精神，都让xbeta不得不推荐它。当然，也承认XnView 和它一样好，但更喜欢这它。参见《经典看图软件测评：Irfanview/XnView/AcdSee2.4X》和《善用IrfanView，不仅仅是看图》，以及发布于PCOnline的《别了，ACDSee！选择 IrfanView/XnView的十大理由》。\nPicasa - 图像浏览器。原为独立产品，后为google收购后免费发布。称它为照片管理器更准确。\nXnView - 图像浏览器。与IrfanView一样是历史悠久、最顶级的免费图像浏览器。同样小巧、支持插件、界面比IrfanView华丽些。\nFastStone Image Viewer- 图像浏览器。图片领域的新秀，下面还有源于同门的也是相当优秀的Capture。读者朋友们，本软件的作者是我们的同胞，但为什么官网和软件皆以英文发 布，并且从来不在国内宣传呢？想想你为共享国产软件做过什么就不难理解了。需要说明的是，此软件，及下面的FastStone Capture，及IrfanView和XnView的免费许可仅限于非商用，即不能在公司使用。\nFuturixImager - 图像浏览器。总体上比上述几个差一些，但它可以在公司使用，有中文版。\n【小结】上述为图片浏览器，xbeta的推荐是全能IrfanView或XnView，在公司可用Imagine或FuturixImager或imgview。可参见《谈IrfanView商用非免费》和《用什么代替IrfanView：Alternate Pic View》。\nEasy Thumbnails - 从图片制作缩略图\nJoJoThumb - 从图片制作缩略图\niWebAlbum - 制作web像册\nJAlbum - 制作web像册。这类软件很少用，偶有所需，IrfanView足够了。提醒大家，有时选对一个通用软件，会节省N个专门软件。\n3D Box Shot Maker - 似乎是做包装盒效果图的\nFastStone Capture - 截屏工具，这个相当不错，也是xbeta推荐之一，详见《FastStone Capture官方详细介绍（新发布5.1版）》。\nWinSnap - 截屏工具。这个一般，只是有些特别，界面比较有味道。\n【小结】xbeta认为作者对截屏工具了解还不够。xbeta的推荐是：全能综合就用Screenshot Captor，见《新一代截屏大师 Screenshot Captor》和《功能强大的截屏精品 Screenshot Captor》；喜国产就用红蜻蜓抓图精灵；喜小巧就用EPSnap，见《推荐一款小巧的国产截屏软件EPSnap》；喜经典就用MWSnap，见《[mwsnap]小巧实用的免费截屏软件》；而FastStone Capture各方面都不错。综合比较见《简评几款免费截屏软件的优缺点(上)、(下)》，截图注意事项见《再谈截屏：软件之外五原则》，一个提醒就是《为什么软件界面截图不存为jpg？》。\n图2 值得尝试的几款截图软件\n3D（略） Blender3D - 3D renderer\n3Delight Free - 3D renderer\nSketchUp - 3D modeling\nMaya Learning Edition - 3D modeling\nDevelopers/开发工具 AutoIt - 自动化工具。此类软件的老大。现在的版本称为AutoIt3.\nSciTE4AutoIt3 - AutoIt量身改造的编辑器，确实好用。\nAutoHotkey - 自动化工具。此软件可以算是AutoIt2的改造升级，它的开发者本是AutoIt团队成员，后因意见不合而独立。与AutoIt比，它的语法更简单。更详细的比较见《AutoIT3 vs AutoHotkey》。为了便于初学者掌握，xbeta写了《AutoHotkey 0级入门教程》。想看一个脚本的实例，这里有2个：《AutoHotkey｜win run加它更方便》、《AutoHotkey脚本：用Irfanview把24位真彩图片优化到实际色深》。\nPHP Designer - PHP编辑器\nNotepad++ - 文本编辑器。小巧，支持语法加亮，最近比较热门。见《notepad++：优秀的文本编辑器之一》。\nConTEXT Editor - 历史比较悠久的多功能文本编辑器，国内用得比较少。\nPSPad - 相当不错的文本编辑器，被Donationcoder（就是Screenshot Captor开发网站）评为免费类第一，基本可以代替UltraEdit，参见《用免费的Crimson Editor、PSPad替换UltraEdit》。\nFoxEditor - 文本编辑器。没听说过。\nCrimson Editor - 源代码编辑器。更象是UltraEdit，但很久没更新了，未来会更新吗？不敢保证。也参见《用免费的Crimson Editor、PSPad替换UltraEdit》。\nElfima Notepad - 文本编辑器。\nNotepad2 - 文本编辑器。也很不错。\nNvu - HTML编辑器。读作N-view，就是New View“新视图”的意思。如果你觉得此文有些价值，那就感谢它吧，因为xbeta写这篇文章用的就是它。开源世界最好的编辑器，代替昂贵的 Frontpage或DreamWeaver。它明显是和Firefox一路，参见《网页编辑器,我推荐Nvu(0326更新)》、《各阵营的兼容与支持:IE Firefox MSoffice OpenOffice Nvu》。\nAlleycode - HTML编辑器，未用过。\nBlockNote - 网页编辑器，前几天刚看过介绍。但有了Nvu，就没再试。\nWeaverslave - 网页编辑器。未听说过。有了NVU，不再关注。\n【小结】只说一下文本编辑器，上面根本没有列出此类最好的软件！\n根本没有。世界只有3种（类）文本编辑器，一是Emacs，这是神用的编辑器，可以用来做很多BT的事，比如上网，收发邮件什么的，牛人用的Emacs至少要占100MB空间吧。\n二是VIM，这是编辑器之神，也是xbeta极为崇拜的软件。本文开始算得免费300，就是用它，更多介绍见xbeta写的系列文章，如《普通人的编辑利器——VIM(全)》《VIM应用实例:用g制作目录或摘录》《我这样记笔记:gvim + 强化txt\u0026amp; nbsp;+ 简化htm》《水木社区VIM版推荐VIM插件列表》。\n对了，第三类文本编辑器就是其他了，那些更为通俗易用的 PsPad，Editpad Lite，CrimsonEdiotr，Editor2（《editor2：优秀的文本编辑器之一》）及上文所述诸款。第3类编辑器的更多比较，可参见xbeta译的《评选最佳文本编辑器(上)、(中)、(下)》，文中列举了几十种编辑器。不过，再次强调：有兴趣试试VIM吧！号称通晓编辑器，不识VIM也枉然。\nCD \u0026amp; DVD/光盘类 DeepBurner - CD/DVD刻录软件。DeepBurner和CDBurner XP Pro为此类软件中免费世界之卧龙凤雏，得一足以，无特殊情况，无须再试其他。\nCDBurner XP Pro - CD/DVD刻录软件。评论见上一条。更深入内容见本文后Amelia网友评论。\nBurnAtOnce - CD/DVD刻录软件。\nExpress Burn - CD/DVD刻录软件。\nZilla CD-DVD Rip’n’Burn - CD/DVD刻录软件。\nImgBurn - ISO, BIN刻录软件。也很不错。\nDaemon tools - 虚拟光驱。说它是第二，谁敢称第一？不过在公司是不能用的。有一个微软出的70多K的虚拟光驱软件也可小试一把。\nDVD Decrypter - DVD ripper\nDVD Shrink - DVD ripper\nNero CD-DVD Speed - CD/DVD info and quality test\nCodecs/编解码器 GSpot - 查看编码信息\nAC3Filter - 音频编解码\nXvid - 视频编解码\nQuickTime Alternative - 视频QuickTime解码\nReal Alternative - 视频Real解码\nK-Lite Codec Pack - 解码器套件，在国内也比较知名\nSysinternals Tools - 多种系统工具 CCleaner - 系统清理\nxp-AntiSpy - OS setup\njv16 Powertools - 系统工具\nXP SysPad - 系统监视工具，较为知名\nWhat’s Running - 查看进程\nRegistrar Lite - 注册表编辑器\nWinIPConfig - IP工具，代替ipconfig.exe和route.exe\nUnlocker - 文件解锁，相当好用。什么文件删不掉，就用它。\nEraser - 安全地删除文件\nUndelete Plus - 恢复删除文件\nfreeCommander - 文件管理器，就是替代资源管理器的，功能更多，更加方便，推荐。xbeta收集了50余种文件管理器，见《N款文件管理器大展示(56个，多图，2006-12-13更新)》。\nExplorerXP - 文件管理器，见上一条。\nDuplicate File Finder - 文件查重，可以根据内容查重，比较准确；多线程，比较快。见《Duplicate File Finder 清除重复文件(1exe,freeware,200kb)》\nAnt Renamer - 文件重命名。这类软件太多，xbeta一般用TC来做这事，专用工具推荐拖把更名器和chgname，见《几个批量改名软件》。\nReNamer - 文件重命名。见上一条。\nIcons From File - 从文件中提取图标\nChaos MD5 - 生成MD5\nHashTab - MD5, SHA1 and CRC-32 file hashes\n下转【转】点评“最好的300款免费软件”(4)\n","date":"2007-11-10","description":"","lastmod":"2007-11-10T08:49:36Z","slug":"xbeta-freeware300-3","tags":["freeware","software","xbeta"],"title":"【转】点评“最好的300款免费软件”(3)","url":"https://blog.zengrong.net/post/xbeta-freeware300-3/"},{"categories":["others"],"content":"转载须保留本段信息！作者：Samer　译者：xbeta　出处：http://blog.sina.com.cn/s/blog_46dac66f01000crp.html\n如图片无法显示，请先打开原文然后再读本文，或下载chm版。\n2007年10月底，freewaregenius发表了题为《重装Windows，只用53款全免费软件》(原文)的文章。此文源于作者Samer在重装Windows后，只安装免费 /开源软件而满足应用需求的实际经历。\nxbeta（免费软件宣传志愿者，善用佳软站 长）对此文进行了译评，供国内读者参考。需要说明的是，本文既不是建议你重装Windows，也不是建议你安装全部软件。而只是从技术层面多了解一些好软 件，从思想层面，提升反盗版信念。在译者看来，文中提到的个别软件算不得同类最优，另有一些杰出的软件，尤其是国产软件，并没有列入。所以，仅供参考，而 非照搬。适合国内用户使用的免费软件，可阅读《xbeta精心荐精品之免费软件列表》。\n以下为译文及评注。\n一、前言 最近，我在笔记本上重装了WinXP。借此机会，写了这篇文章，分享我“100%使用免费或开源软件，完成所有重要（或非重要）需求”的解决办法。本文也可称为：\n日常工作，完全无需付费软件（Windows除外） 53款免费软件，全面满足日常所需 本文全基于我的实际经验而写成，文章较长，写来费力。如果你喜欢，请以收藏、推荐的形式进行支持。（译者注：鼓励署名转载）\n二、格式化之前的准备\n格式化原有系统盘之前，用如下软件进行准备：\nGparted Live CD\n重装系统而保留数据，最简单的方式就是将所有数据转移到新建分区中。Gparted Live CD 就是这样一款优秀工具，来创建和管理分区，与任何同类工具，包括收费软件，相媲美。 2. Unstoppable Copier\n我用此工具把C分区的文件和数据复制到其他分区。它特别适用于复制或转移大量文件。如其名称所述，它不会停下来问用户“请确认：移动只读文件 xxx？”你可以离开计算机，让它慢慢复制。\n3. Amic Email Backup\n把C盘存放的邮件数据转移到非系统盘。支持Outlook等多种邮件客户端。但不支持Thunderbird。Thunderbird用户可用 Mozbackup。\n同类免费工具：EZ Email Backup。\n4. DriverMax\n备份全部驱动程序，并可用它恢复安装驱动。\n5. Produkey\n用来备份所有MS产品的注册信息，包括 Windows XP 和 Office。可打印出来或保存到其他分区。与同类工具相比，优点是不会引起安全软件的警报。\n三、安装Windows\n利用正版的Windows安装盘进行安装。如果中间需要驱动，请通过网络或DriverMax备份进行安装。然后，进行 Windows update。再后，安装Microsoft .NET 和 Java RTE。\n四、安装应用软件\n装完windows，再安装应用软件——这才是最美好的过程。\n6. PC Decrapifier\n如果你是用电脑制造商提供的Windows安装盘，则极可能会安装很多“多余”的软件。（xbeta补充：越是品牌机，越要体验增值，结果是装了无数多自启动的软件、自启动的服务）此工具可以将它们统统删除。不过，要小心检查卸载清单。\n7. DriveImage XML\n为刚安装好的系统制作镜像，以便随时恢复系统。就象ghost一样，不过此工具为免费软件，也非常好用。\n译者注：中文介绍见《用免费的DriveImage XML代替Ghost来备份硬盘》。\n8. Launchy\n美观方便的小工具，让你启动程序更方便。同类工具还有 Key Launch 和 Keybreeze。\n译者注：我坚守经典的win run方式，参见《最绿色最高效，用win+r启动常用程序和文档》\n9. AVG Antivirus\nAVG成为杀毒首选的原因：①占用资源极少；②效果好；③可以扫描邮件（我需要此功能，所以没有选优秀的Antivir。\n第2选择：Antivir。第3选择：Avast。\n10. Spyware Terminator\n实时抵御恶意软件。系统扫描时，还集成了开源杀毒软件 ClamAV。安装时会试图增加一个浏览器工具条，我通常会取消此项。\n11. Comodo Firewall\n它不仅是好的免费防火墙，还是 PC Magazine 编辑推荐产品，可能还是最好的个人防火墙——无论与免费软件还是与付费商品比。 Matousec.com 最新防火墙评测中，它取得了综合防火墙最高分、防漏洞最高分。（本文所指最新评测是截止到本文写作时的2007年10月20日）\nTweakUI\n利用它来个性化windows界面，并尽量把数据路径（我的文档、桌面）从C盘转向其他分区。此外，它还能改变Windows的打开/保存对话框的侧栏。\nOpenOffice\n影响最大、功能最强的免费开源办公套件。xbeta极力推荐。请远离昂贵的MS Office，换用全面模仿和兼容MSOffie的WPS 2007，或独立开源的OOo。\n译者注：支持OpenOffice.org\n14. Forcevision Image Viewer\n简洁好用的看图工具。看图工具可分为(a) 小巧简单而具备基本功能； (b) 中量级看图工具，有一定的编辑功能及选项，能转换文件格式； (c) 更大体积，具备丰富的功能，通常支持插件，支持非常多格式的读写。\n我知道很多人选 c 类的 Irfranview 或 Xnview，但我用此软件实现了99%的需求。\n替代选择：Faststone Image Viewer。\n译者注：没什么好说的，最强超小Irfanview，中文介绍《善用Irfanview，不仅仅是看图》。\n15. JZip\n基于 7-Zip 的开源压缩工具。这些也不错：TugZip, IZArc, 和 ALzip 。\n译者注：不二选择 7-Zip。\n16. CDBurnerXP 4\n免费工具，用来刻录 CD和DVDs。功能全面，刻录音乐CD，复制CD/DVD，烧录ISO，支持多种格式，如双层DVD、Blu-Ray、HD-DVDs。\n第2选择： InfraRecorder\n17. JKDefrag GUI\n免费磁盘整理软件JKDefrag的图形化界面。\n支持理由： (a) JKDefrag 是近评测的多款免费与收费磁盘整理工具中最好的一款； (b)可以设定为屏保，因此，当你计算机处于空闲时它会自动工作； (c) 速度快、效果好。\n18. Folder Size\n为资源管理器添加“文件夹大小”的附加列。第2选择是“Aurionix FileUsage”，它提供更多功能，但占用稍多资源，且需要.Net。\n译者注：最好的文件管理器是Total Commander，免费可选Free Commander。\n19. Pidgin\n将多种聊天工具集成在一起，比如QQ，AIM, MSN, Yahoo!, XMPP, ICQ, IRC, SILC, SIP/SIMPLE, Novell GroupWise, Lotus Sametime, Bonjour, Zephyr, MySpaceIM, Gadu-Gadu等。占用资源小，且无广告。\n第2选择：Miranda IM，也是很优秀的软件。此外，基于web的 Meebo也很好。\n译者注：以前用Miranda IM，现用Meebo。\n20. Google Toolbar\n这是我唯一安装的工具条。它为浏览器提供了搜索框、填表工具、快速翻译网页、拼写检查功能。\nCCleaner\n相当不错的硬盘清理工具，处理注册表、临时文件、浏览历史及隐私文件、各种无用文件和数据。安装程序可能带有Yahoo工具条，请注意。\n译者注：好习惯很重要，好工具也有益。这是视频演示。\n22. Shock Sticker\n很实用的桌面便贴工具。其他同类工具只支持txt，它还支持rtf格式。可以将笔记缩为图标——这是我喜欢它的重要理由。此外，Stickies也不错，功能更多。\n译者注：此类工具很多，自己喜欢就好。\n23. FolderICO\n我喜欢将不同目录用不同图标/颜色进行区分。此工具在系统右键菜单上添加这项功能，操作方便。另外，它将设置信息放于各目录下，因此，即便由其他操作系统通过网络访问此目录，或Windows重装后，个性化设定仍然有效。\nBeCyIconGrabber\n喜欢收集和更换图标者必备工具。它不仅能从文件中提取图标，还能把图标反过来把图标存为图标库——这在同类工具中并不多见。\n25. Alpass\n很好的密码管理工具（只适用于IE），可保存、加密、自动填入密码。类似功能的还有 Keepass。\n译者注：Keepass可不是第二选择，而是最好的选择。参见十项免费之道，全面管理你的密码(译)。\n26. Picasa\n来自Google的免费图片管理软件，可以在线分享/上传图片，提供很多图片增强功能，也是优秀的看图工具。\n译者注：我只用IrfanView看图，用目录管理图片。\n27. Faststone Capture\n很多人已经知道这款优秀的截图+编辑工具了。最新版不再免费，最后的免费版是V5.3。此外，Screenshot Captor 也极好。\n译者注：参见新一代截屏大师 Screenshot Captor、简评几款免费截屏软件的优缺点(上)、（下）。\n28. GOM Media Player\n视频播放器，支持 DVD，Real Media, Quicktime, DivX, Xvid 和 FLV。优点是它内置了解码器且不安装为系统解码器。如果遇到不支持的文件格式，可以自动下载新解码器。我也常用 VLC media player 。但GOM支持 FLV 格式更好，比如跳转到FLV的任一位置，而目前VLC还做不到。并且，它的界面很漂亮，尤且在播放DVD时。此外，解码器 CodecInstaller也值得一用。\n29. Quintessential Media Player\n支持多种音频。兼为 (a) 优秀的播放器；(b) 出色的 tag 编辑器；(c) 支持CDDB数据库的CD ripper；(d) 音频格式转换器。通过插件还支持均衡、可视效果、皮肤。此外一大优点是自动标签功能，需插件或CD Art Display支持。另，Mediamonkey 也很好。\n30. MP3Tag\n很棒的MP3 标签管理工具，可以从Amazon下载专辑信息，并保存到音乐文件中。我试过多种软件，但最喜欢此款，主要是界面直观，用户体验特别好。\n此外，也可用批量改名工具The Godfather 处理类似工作，或用 #29的播放工具管理tag。\n31. MusicBrainz Picard\n如果音乐文件无tag信息或不完整，可用它来补全。它使用最先进的数字指纹技术，与社区提供的MusicBrainz数据库进行比较，并补全tag。它用的是与 Quintessential Media Player (#29) 不同的技术，效果很好。\n32. Exact Audio Copy\n完美地从CD提取音乐文件，比如高质量MP3格式，支持多种格式。我还喜欢 BonkEnc。另，#29播放器也支持提取音频功能。\n如果你要找实用的音频格式转换工具，请使用Any Audio Converter ，它还支持FLV，并能从视频中提取音频。\n33. MP3gain\n自动检查多个MP3，并对它们的音量进行均衡。以免播放时，这首歌声音太大，而下一首又弱不可闻。重要的是，它并不改变MP3文件本身，所以，音量均衡处理也是可逆的。另一款同类软件是：MP3Trim.\n34. Unlocker\n删除某个文件，却被提示被锁定？就用它来解决。极其小巧，却很实用。经常折腾系统的网友必备工具。\n译者注：很实用。\n35. Orbit Downloader\n非常出色的下载管理工具，并且支持流媒体（音乐、视频、SWF）格式的下载。另一款优秀工具是FlashGet.\n译者注：下载工具，中国第一。\n36. WinSCP\n需要FTP客户端吗，请不要错过 WinSCP。它还支持 SFTP 及先前的SCP协议，支持安全传送，双窗口界面。支持断点保存、书签，可以集成到右键“发送到”菜单。\n此外，FileZilla 也是不错的选择，免费且不断更新改进，支持 FTP, SFTP, 和 FTPS。如果你偏好FTP通过右键与资源管理器集成，则可选择 RightLoad。\n37. Local Website Archive\n此软件可以将网页原样保存在本地，包括图片与格式，以便于日后浏览。它比较好的一点是按原有格式保存，这样便于在笔记工具中引用本地url。另一种替代选择，也是极好的工具，是 Evernote。\n译者注：请尝试杰出的EverNote，参见顶级免费笔记软件EverNote 2.2发布；更多笔记软件则参见寻找最好的笔记软件:三强篇。\n38. Flashnote\n轻便的笔记工具，按快捷键则出现，记录完毕后，最小化（或按下快捷键）则回到系统托盘。或许它的功能并不是很多，但对我而言，它是必装软件。\n39. Revo Uninstaller\n我选择的卸载工具，可以在常规的卸载后，仍能把多余的文件和注册表信息进行清除，效果明显。当然，在使用中仍要对清除内容进行谨慎确认。Revo还提供了自启动程序管理器、硬盘清理工具等产品。此前我用过的ZSoft Uninstaller也不错，它清理效果或许没有Revo干净，但也不会象它那样有误删风险。\n40. BitTyrant\n我用了很长时间的出色的BT工具。这是改进版的 Azureus，通过被称为“自私”的下载方式实现更快的速度。另外的优秀BT客户端有 uTorrent, Azureus.\n译者注：我极少用此类工具，支持uTorrent，参见uTorrent：史上最省资源BT客户端。\n41. Starter\n小巧、免安装的杰出软件，管理自启动程序。此类软件有很多，但试过之后选定了这一款。说明一下，Revo Uninstaller (#39) 也含有内置的启动项管理功能。\n译者注：此类首选，见Autoruns与Sysinternals。\n42. Send To Toys\n用此工具，可将任意目录加入“发送到”菜单中，便于快速复制或移动文件到相应目录中。\n译者注：用了Total Commander，再无此类烦恼。另，好象手工方式也能修改“发送到”菜单实现此功能吧。\n43. Returnil\n安全工具。利用它可以浏览不安全的站点，或安装危险软件，或进行任何有风险的操作。然后，重启计算机就回到了初始状态。\nSysTrayMeter\n在系统托盘中直观显示当前资源消耗情况。便于查出问题所在。\n45. SweepRAM\n极小巧且免安装的小工具，释放和优化内存。\n46. VSO Image Resizer\n在资源管理器添加右键菜单，实现图片缩放或转换格式功能。特别之处是，可以把一些设置保存起来，这样日后就能直接调用。Easy Thumbnails也不错，我也用过很长时间。\n译者注：我只用IrfanView。\n47. Photoscape\n集多种功能于一身的图片管理和处理套件，包括图片编辑、截屏、格式转换、看图、GIF动画、批量图像改名、页面创建多种工具，此外还有其他功能。它功能多多，而我最爱用它合成图片，并方便地添加注释。如果你在工作中经常用片进行演示，则它再方便不过。\n译者注：早就知道这款软件，但一向不喜欢用/推荐大体积工具。我推荐的组合：Irfanview+Screenshot Captor+GIMP。参见善用GIMP(Linux下的Photoshop)，图像处理轻松又自由、GIMP文字特效。\n48. PDF-XChange Viewer\n比Adobe体积更小更快，比Foxit Reader功能更多，支持多种注释、多页签、打开预览的优秀pdf阅读工具。要说它有什么缺点，就是关联pdf后的图标不敢恭维，但是可以用 Icon Phile进行更改。\n译者注：确实不错，值得一试。中文介绍见功能更多的PDF阅读软件PDF-XChange Viewer.\n49. Primo PDF\n优秀的pdf虚拟打印机。如需打印为图片格式，PDFCreator将是首选。另，DoPDF也不错。\n译者注：关于pdf，关于pdf相关软件，尽在全面接触PDF:最好用的PDF软件汇总.\n50. HobComment\n想为文件或目录添加注释吗？用此软件。它能在资源管理器详细视图中，加入“文件(夹)注释”列。并在资源管理器右键菜单中新建“添加注释”项（只限于 NTFS分区）。\n译者注：添加注释不是好习惯。\n51. I.Mage\n我用它替代windows的画图工具。它简洁实用，足以满足我偶尔的图片处理工作。如果你需要更强大的PhotoShop替代工具，请试用 Gimpshop 或 Paint.net ，都是极品。\n译者注：当然经典的GIMP。参见善用GIMP(Linux下的Photoshop)，图像处理轻松又自由、GIMP文字特效。\nFlashfolder\n资源管理器增强工具，给windows的打开/保存对话框，增加自定义的收藏文件夹、最近文件夹。我的最爱软件之一，新机必装。\n译者注：我因为使用了强大的Total Commander(共享软件)，所以不再有管理文件的不便了。参见《TC学堂：Total Commander系列教学》。\n53. JOCR\n捕捉屏幕任一区域（或加载图片），并即刻识别出其中的文字。不过呢，把它列入推荐全免费软件的本文或许有点不太合适，因为它要用到MS Office的库。我本来已经把 OpenOffice (#13) 推荐为 MS Office 替代品了。但因为我经常用它，所以还是收录于本文最后。\n五、总结\n进行到这里，我已在计算机上装完了所有软件。所以，我再次用 DriveImage XML 创建了镜像文件。也就是说，我拥有了2个镜像文件：一是干净的Windowsso加驱动；二是包括所有应用软件。\n在必要的情况下，我都可以快速恢复到任一状态。（译文完）\n上面就是全部译文及评注，不知您读完是否有所收获？有人问：如果用的Windows都是盗版，再用这些免费软件有意义吗？当然。如果你的目标是逐渐减少盗版——难道你5年后、10年后、或者终一生都背负盗版之名？——则每一步都有意义。\n","date":"2007-11-10","description":"","lastmod":"2007-11-10T08:17:08Z","slug":"windows-reinstall-freeware","tags":["freeware","software","xbeta"],"title":"【转】重装Windows，只用53款全免费软件","url":"https://blog.zengrong.net/post/windows-reinstall-freeware/"},{"categories":["impressions"],"content":"有句老话叫“大难不死，必有后福”；有句俗语叫“不经一事，不长一智”；还有句古文叫“故天将降大任于是人也，必先苦其心志，劳其筋骨，饿其体肤，空乏其身，行拂乱其所为，所以动心忍性，曾益其所不能。”\n或许老天认为我受的苦还不够多,希望能再苦一苦、劳一劳、饿一饿我也说不定？或许上帝认为爱人小时候太过享福，让她跟了我受受苦？或许上天认为淇淇是个可造之材，让她幼年时受尽痛苦，“曾益其所不能”？\n爱人也去信佛。佛说，让我们找来三条毒蛇去高处放生，她去做了；佛说，让我们找来活的蚕茧，放去山野里让它们破茧而出，我去做了；佛说，让我们每天对着他的方向磕头一百零八次，爱人每天都做。\n佛还说，淇淇过了六岁，就会一生无病无灾。\n我不信佛，但爱人信。我信的是缘分，我觉得和淇淇有缘分，所以我们救她。我们做一切我们能做的，所以我们从来没有放弃。北京来的专家对我们说，一岁以后发现的，治愈率只有百分之十。但我不信淇淇就是那百分之九十中的一个。手术这么成功，第一步走的这么好，我和淇淇这么有缘分，难道就不能换来一个奇迹？\n伤心过了，也就不必再伤心。伤心又有何用？这件事到了现在，对我对爱人对淇淇都是好事。我为什么不认为它是好事呢？一个家庭经历了风雨就会更加亲密，一个孩子经历了病痛就会更加勇敢，一个父亲经历了这一切就会更加坚强。\n","date":"2007-10-22","description":"","lastmod":"2007-10-22T13:41:05Z","slug":"the-first-step","tags":["qiqi"],"title":"第一步","url":"https://blog.zengrong.net/post/the-first-step/"},{"categories":["others"],"content":"作者:xbeta　版本:070910/070910　出自:善用佳软blog.sina.com.cn/xbeta\n这是国外较有影响的 www.teknobites.com\n于2007年9月8日发布的新文章，算是windows下20款开源软件的续篇。发出不久，digg数已上千。xbeta进行简译，以便于国内用户在免费软件方面有更多选择。\n安全： 1. Open Antivirus:\n不是杀毒软件，而是为关注安全的人士提供的交流平台。\n2. Truecrypt:\n开源的磁盘加密软件，用户评价很高。\n3. ClamAV:\n反病毒软件，原用于UNIX，现在有windows版了。\n4. RogueScanner:\n网络安全，发现哪些设备连入你的网络。5. Keepass:\n开源的密码管理软件，算是此类中最好的。\n6. Clipperz:\n在线的密码管理方案。参见《十项免费之道，全面管理你的密码(译)》。\n7. SoftPerfect\nPersonal Firewall: 防火墙软件，简单易用，有学习机制。\n文件传输与共享： 1. WinSCP: 支持SSH(Secure\nSHell)的SCP(Secure CoPy)文件传输软件，双窗口，便于操作。2. PuTTy:\n开源的telnet工具，支持SSH。\n3. ShowMyPC:\n提供远程支持的社区，起源于一个开源的桌面共享和远程接入项目。\n4. Ares:\n开源的文件共享软件。\n多媒体 1. Media\nPlayer Classic: 经典播放器，界面极象Windows Media Player\n6.4，但提供了更多功能。最大特色是一个exe文件。由VOBSUB的作者编写。2. Media Coder:\n集成的解码器。\n[\n3.](http://www.teknobites.com/2007/08/17/media-coder-audiovideo-transcoder/) Hanbrake:\n开源多平台的DVD转 MPEG-4工具。\n4. musikCube:\n代替iTunes的音乐管理软件。\n5. popCorn:\n这可不是以前那个邮件检查程序，而是一个桌面端播放器，可以直接播放来自网络的媒体。\n6. ImgBurn: 轻量级的免费\nCD / DVD / HD DVD 烧录工具。\n7. AVS\nDisc Creator: 另一款 CD, DVD 烧灵工具。\n8. DVDStyler:\n跨平台的DVD选单制作工具。\n9. mp3mymp3:\n把计算机的一切声音录制为mp3/wav。\n10. Youtube\nCatcher: 把 Youtube, Google Video, Myspace Video, Yahoo video\nDailymotion Stage6 Veoh 的视频下载到本地，并转为其他格式。\n11. Media Portal:\n开源工具，把PC转化为多功能媒体管理中心，有预录功能。.\n12. VirtualDub：虽然VirtualDub是一套免费的多媒体剪辑软件，但它的功能可一点也不输给Premiere以及Media\nStudio等专业等级产品的功能。在VirtualDub中主要的功能可以区分为两大部份，一是可以让您针对现有的电影短片文件如。AVI以及。MPG\n等做编辑的工作，另一项则是可以搭配您的影像补捉卡做即时的动态影像捕捉的功能。\n视频编辑 1. Vivia-The Video Editor:\nLinux和Windows下的视频编辑工具。易用，支持处理多镜头影像。2. MiNi\nVideo Studio: 非线性编辑工具，可批量或按帧处理已有影像。\n3. DVD Flick:\n可把多个视频文件合成一张DVD，并加入额外的音轨、字幕。\n4. OpenVIP:\n易用的图形化界面，基于时间线的编辑模式，为视频增加滤镜和过渡效果。\n5. Avidemux:\n支持简单的剪辑、滤镜、编码处理，支持 AVI, DVD 兼容 MPEG 文件, MP4 ,\nASF。\n图像类： 1. Irfanview:\n极小极强大的经典看图工具，替代收费的ACDSee，参见《善用IrfanView，不仅仅是看图》\n2. Paint.net:\n华盛顿大学开发后被MS支持的基于.Net的图像处理工具，十分强大。\n3. XnView:\n和IrfanView类似，也是替代ACDSee的不错选择。\n3D图像: 1. Art Of Illusion:\n免费开源的3D建模和渲染工具，Java写成，需要 J2SE 1.4\n及以下版本支持。2. OpenFX:\n开源的3D建模、动画、渲染套装，功能较强，多种扩展API。\n办公软件： 1. AbiWord:\n国外用得比较多的轻理级字处理工具，对中文支持不太好。国人建议用WPS。\n2. Launchy:\n最近很热门的启动程序工具。这类工具有很多，但xbeta认为TC和win+r足够了。参见《最绿色最高效，用win+r启动常用程序和文档》。\n3. Cygwin:\n在windows下享受Linux的工作方式。不能算是很好用。4. Pandora\nRecovery: 恢复已删除的文件。\n5. CCleaner:\n功能全面，评价很高的垃圾清理、系统优化工具。\n6. Wink:\nxbeta极为推荐的免费录屏工具，且能方便的进行编辑、加入注释、声音。详见《善用Wink，将电脑操作录屏为flash》。杨氏电脑教程和即将开始的Total Commander教学录屏都是用它做的。\nFreemind:\n极好的思维导图制作工具，Java开发，做出的效果见此。\n8. jetToolbar:\n替代MS的开始菜单，安装后默认有14个类，及推荐的若干网站。\n9. MyUninstaller:\n卸载软件的工具，可把安装的程序记录存为txt或htm。\n10. Flashget:\n中国人还用介绍吗？\n11. ZScreen:\n开源截屏工具，可上载到FTP。单说截屏，还是这些工具比较好：简评主流免费截屏软件的优缺点。.\n12. CamStudio:\n把屏幕操作录为avi或flash。相对而言，更推荐wink。\n13. Libra\n：管理图书、CD、电影、游戏。可以用摄像头作为条码扫描仪，记下这些资料的信息。非商用免费。\n网络开发 PSPad:Winodws风格的综合性能、功能最好的免费编辑器。见《用免费的Crimson Editor、PSPad替换UltraEdit》。\n2. HTTrack:\n最好的网站下载、离线浏览工具。\n3. NOTEPAD++:\n开源的编辑器新秀，支持多种语法文件。\nGoogle Pack 较为熟知，不再列出。在这里下载\n杂项 Virtual Box:\n功能强大性能出色的x86虚拟机软件 (完)\n","date":"2007-09-20","description":"","lastmod":"2007-09-20T15:11:45Z","slug":"teknobites","tags":["freeware","software","xbeta"],"title":"【转】teknobites精选40款免费Windows软件(xbeta简译)","url":"https://blog.zengrong.net/post/teknobites/"},{"categories":["technology"],"content":"本文是ActionScript3 Tips and Tricks系列阅读笔记之一Determine Instance Class or Superclass，这里是原文地址。\n在ActionScript3中，可以使用getQualifiedClassName函数（ flash.utils.getQualifiedClassName）获取实例的类名：\n1var sprite:Sprite = new Sprite(); 2trace(getQualifiedClassName(sprite)); // 输出\u0026#34;flash.display::Sprite\u0026#34; 还可以使用getQualifiedSuperclassName函数 （ flash.utils.getQualifiedSuperclassName ） 获取超类的名称：\n1// 输出\u0026#34;flash.display::DisplayObjectContainer\u0026#34; 2trace(getQualifiedSuperclassName(sprite)); 如果想把字符串转换成类引用，可以使用getDefinitionByName函数（ flash.utils.getDefinitionByName ）：\n1// 输出[class Sprite] 2trace(getDefinitionByName(\u0026#34;flash.display::Sprite\u0026#34;)); 而使用describeType函数（ flash.utils.describeType ）\n则可以获取非常详细的类的信息：\n1var s:String = \u0026#34;hello\u0026#34;; 2trace(describeType(s)); 输出:\n1\u0026lt;type name=\u0026#34;String\u0026#34; base=\u0026#34;Object\u0026#34; isDynamic=\u0026#34;false\u0026#34; isFinal=\u0026#34;true\u0026#34; isStatic=\u0026#34;false\u0026#34;\u0026gt; 2 \u0026lt;extendsClass type=\u0026#34;Object\u0026#34;/\u0026gt; 3 \u0026lt;constructor\u0026gt; 4 \u0026lt;parameter index=\u0026#34;1\u0026#34; type=\u0026#34;*\u0026#34; optional=\u0026#34;true\u0026#34;/\u0026gt; 5 \u0026lt;/constructor\u0026gt; 6 \u0026lt;accessor name=\u0026#34;length\u0026#34; access=\u0026#34;readonly\u0026#34; type=\u0026#34;int\u0026#34; declaredBy=\u0026#34;String\u0026#34;/\u0026gt; 7\u0026lt;/type\u0026gt; 下载源文件：\n1 文件 ","date":"2007-09-15","description":"","lastmod":"2007-09-15T02:30:39Z","slug":"determine-instance-class-or-superclass","tags":["as3","general","tipsandtricks"],"title":"获取实例的类或超类名-ActionScript3 Tips and Tricks","url":"https://blog.zengrong.net/post/determine-instance-class-or-superclass/"},{"categories":["technology"],"content":"本文是ActionScript3 Tips and Tricks系列阅读笔记之一“Deep Object Copies with ByteArray”，这里是原文地址。\n使用ActionScript3中新提供的 ByteArray类(flash.utils.ByteArray)可以创建一个对象的深度拷贝。“深度”的意思是可以拷贝一个对象（object）的所有引用，这意味着如果你拷贝一个包含对象的数组，那么数组中的对象也被拷贝（而不是引用）。下面是一个clone方法：\n1function clone(source:Object):* { 2var copier:ByteArray = new ByteArray(); 3copier.writeObject(source); 4copier.position = 0; 5return(copier.readObject()); 6} 注意：你需要先导入 flash.utils.ByteArray ；\n要使用这个方法，可以使用下面的代码：\n1newObjectCopy = clone(originalObject); 虽然这个方法可以深度拷贝你的对象，但是它不会一同拷贝类的类型定义。所以，如果查需要拷贝一个MyClass实例，拷贝出的对象将不在是MyClass类型。这个方法最好在普通对象上使用。\n下面是我的一些测试代码：\n1import flash.utils.ByteArray; 2flash.utils.getQualifiedClassName; 3var a1:Array = [1,2,3]; 4var a2:Array = clone(a1); //克隆a1，a2中保存的是a1的复制品，与a1不存在任何关系了 5var a3:Array = a1; //由于a1是数组，所以a3是引用a1 6a1.push(4); //改变a1的值 7trace(a1); //输出 1,2,3,4 8trace(a2); //输出 1,2,3 9trace(a3); //输出 1,2,3,4 10trace(\u0026#34;===============\u0026#34;); 11var aa1:Array = [1,2,3]; 12var aa2:Array = [11,22,aa1] //aa2中的一个元素是aa1 13var aa3:Array = clone(aa2); //克隆aa2，aa3中保存的是aa2的复制品，aa2中的aa1也是复制品 14trace(aa1); //输出 1,2,3 15trace(aa2); //输出 11,22,1,2,3 16aa1.push(4); //修改aa1的值 17trace(aa2); //输出 11,22,1,2,3,4 18trace(aa3); //输出 11,22,1,2,3 19trace(getQualifiedClassName(aa2)); //输出 Array 20trace(getQualifiedClassName(aa3)); //输出 Array，这说明对Array的克隆可以保持克隆结果的Array类型不变 21trace(\u0026#34;===============\u0026#34;); 22var aaa1:MyClass = new MyClass(); //建立一个自定义类型 23var aaa2 = clone(aaa1); //克隆自定义类型 24trace(aaa2.a); //输出 1 25trace(getQualifiedClassName(aaa2)); //输出 Object，这说明aaa2的MyClass类型丢失了 26function clone($source:Object):* 27{ 28var _copier:ByteArray = new ByteArray(); 29_copier.writeObject($source); 30_copier.position = 0; 31return _copier.readObject(); 32} MyClass.as的内容\n1package{ 2public class MyClass{ 3public var a:int = 1; 4public var b:int = 2; 5public function MyClass(){} 6} 7} 注意：\n不能为aaa2强制转换类型，如果使用下面的语句：\n1var aaa2 = MyClass(clone(aaa1)); 则会报错：\n1TypeError: Error #1034: 强制转换类型失败:无法将 Object@138c0341 转换为 MyClass。 2at DeepObjectCopieswithByteArray_fla::MainTimeline/DeepObjectCopieswithByteArray_fla::frame1() 如果使用as语句转换，则得到的aaa2值是null。\n下载源文件：\n1 文件 ","date":"2007-09-13","description":"","lastmod":"2007-09-13T15:59:58Z","slug":"general-deep-object-copies-with-bytearray","tags":["as3","general","tipsandtricks"],"title":"使用ByteArray进行对象深度拷贝-ActionScript3 Tips and Tricks","url":"https://blog.zengrong.net/post/general-deep-object-copies-with-bytearray/"},{"categories":["technology"],"content":"本文是ActionScript3 Tips and Tricks系列阅读笔记之一，这里是原文地址。\n很不幸，ActionScript3不支持抽象类（abstract classes），抽象类不能被实例化，并且只能扩展一次。所以，你不能在Flash中创建自己的抽象类。让我们先来了解一下Flash Player中内置的抽象类：\nDisplayObject InteractiveObject DisplayObjectContainer Graphics 对于抽象类来说，你不能使用new关键字来建立它们的实例。\nActionScript 代码:\n1var myObj:InteractiveObject = new InteractiveObject(); // 错误 当然，你也不可以继承它们并试图建立子类的代码。\nActionScript 代码:\n1package { 2 import flash.display.DisplayObject; 3 public class MyDisplay extends DisplayObject{ 4 public function MyDisplay (){ 5 // 错误 6 } 7 } 8} 不过，你可以继承内部抽象类的子类。例如，如果需要继承DisplayObject，你可以继承Shape类。Shape就是DisplayObject的子类。\n","date":"2007-09-13","description":"","lastmod":"2007-09-13T15:54:57Z","slug":"general-abstract-classes","tags":["as3","general","tipsandtricks"],"title":"抽象类-ActionScript3 Tips and Tricks","url":"https://blog.zengrong.net/post/general-abstract-classes/"},{"categories":["technology"],"content":"本文来自Flex白皮书\n导航条容器内置推迟实例化\nFlex中的导航条容器（ViewStack、Accordin、TabNavigator）中内置了推迟实例化。默认情况下，推迟实例化意味着Flex在启动时不会创建任何子视图，但用户通过导航到该容器可触发所有子视图的创建。下面的代码显示了两个导航条容器 TabNavigator 和 ViewStack 的使用：\n1\u0026lt;mx:tabnavigator\u0026gt; 2\u0026lt;mx:vbox id=\u0026#34;tabNavView1\u0026#34;\u0026gt; 3\u0026lt;mx:linkbar dataprovider=\u0026#34;myViewStack\u0026#34;\u0026gt; 4\u0026lt;mx:viewstack id=\u0026#34;myViewStack\u0026#34;\u0026gt; 5\u0026lt;mx:vbox id=\u0026#34;view1\u0026#34;\u0026gt; 6\u0026lt;/mx:vbox\u0026gt; 7\u0026lt;mx:vbox id=\u0026#34;view2\u0026#34;\u0026gt; 8\u0026lt;/mx:vbox\u0026gt; 9\u0026lt;mx:vbox id=\u0026#34;view3\u0026#34;\u0026gt; 10\u0026lt;/mx:vbox\u0026gt; 11\u0026lt;/mx:viewstack\u0026gt; 12\u0026lt;/mx:linkbar\u0026gt; 13\u0026lt;mx:vbox id=\u0026#34;tabNavView2\u0026#34;\u0026gt; 14\u0026lt;/mx:vbox\u0026gt; 15\u0026lt;/mx:vbox\u0026gt; 16\u0026lt;/mx:tabnavigator\u0026gt; TabNavigator 容器创建了 tabNavView1，因为它是 Flex 实例化 TabNavigator容器时显示的第一个视图。实例化 tabNavView1 导致 LinkBar 和 ViewStack的第一个视图 view1 被实例化。当用户与 LinkBar 交互以选择 ViewStack中的另一个视图时，Flex 将创建该视图。Flex 如此继续，在调用时创建导航条的所有后代对象。\n容器标签中的 creationPolicy 属性控制子视图的创建。下面的列表将解释 Flex导航条容器每个 creationPolicy 属性设置的含义。\ncreationPolicy=“auto”\nFlex创建导航条容器时将不会立即创建所有的后代对象，只创建开始时可见的后代对象。这一推迟实例化行为的结果是带有导航条容器的MXML应用程序加载很快，但从一个视图浏览至另一视图时，用户将等待短暂的停顿时间。可用性研究表明，与应用程序启动时为创建所有导航条子视图而等待较长时间相比，这种用户体验更好。同样，用户可能从不访问某些子视图，因而在启动时创建这些子视图会浪费资源。注意，如果在一个非导航条容器上设置creationPolicy为“auto”，则必须加入额外的代码来指示何时创建该容器的子对象。这些额外代码已经内置进导航条容器中，因而可在导航条容器上设置creationPolicy 为“auto”而无需任何额外的工作。 creationPolicy=“all”\nFlex创建导航条容器时将创建所有子视图中的所有控件。这一设置导致程序启动时有一个延迟，但在视图之间切换的响应时间将会较短。 creationPolicy=“none”\nFlex将不会实例化导航条容器中或者其子视图中的任何组件，直到实例化方法被显式调用。可用createComponents() 方法显式地实例化视图。Flex文档中有更多关于设置自定义组件创建计划的信息。 通过设置 creationPolicy属性，可手工创建子视图，并决定在应用程序架构的何处发生创建导航条容器子视图所需的资源占用。可用性研究显示，使用auto设置可取得较好的用户体验。一个常见的错误会延长应用程序启动时间，即错误地在某一个导航条容器上设定creationPolicy=“all”。仅当能确保组件创建计划足够高效时可设置creationPolicy 为“all”。\n","date":"2007-08-19","description":"","lastmod":"2007-08-19T03:37:44Z","slug":"creationpolicy","tags":["flex"],"title":"导航条容器内置推迟实例化-creationPolicy属性详细介绍","url":"https://blog.zengrong.net/post/creationpolicy/"},{"categories":["technology"],"content":"Flex对版面的自动管理功能相当强大，在网页中，Flex程序会自动适应页面的更改，始终保持最大化显示，如果网页太小不能容纳程序的所有界面，就会自动出现滚动条。这样的效果，在Flash中只能自己手动写代码完成。\n现在不必了，不信？下面这个例子是一个senocular.com提供的版面管理类，源码可以在这里下载:\nLayout.zip\n当然，还有一些例子可以看：\nStageResize ScalingForm DynamicLayout 文档:\nLayout LayoutManager LayoutConstraint 请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2007-08-13","description":"","lastmod":"2007-08-13T05:30:39Z","slug":"flash-cs3-layout","tags":["as3","class","flash","layout","library"],"title":"在Flash CS3中进行版面管理","url":"https://blog.zengrong.net/post/flash-cs3-layout/"},{"categories":["technology"],"content":"原来有人问我：怎样学好Flash？我的回答一般就是：仔细看帮助、多做练习、多看优秀的源码、多上专业论坛参加讨论。\n可是Flex来了，于是又有人问：怎样学好Flex？\n我不知如何回答，因为我也是Flex新手，也在“仔细看帮助、做练习、看源码、上论坛......”。现在d.CAT的这篇优秀的文章，详细的回答了这个问题。\n下面的文章转自d.CAT RIA Blog，由于原文是繁体中文的，所以转载过来的时候我对文章的繁体字部分进行了替换，对一些词语进行了修改以符合简体中文语言习惯，对一些术语进行了注释。\n最后，文中所有第一人称处所指的都是原文作者而不是“我”，有麻烦可以找他 :em61:\n以下为转载：\n==================================================================\n＊Flex 的基础架构\n关于 flex 基本上常被问到的不外乎就是“如何可以学好它？”，要了解这个问题的答案基本上只要看懂下面这个图就OK了。\n＊Actionscript 该学的重点\n从最底层看起，最下面的 actionscript 3是一切的基础，它是 flash/flex 编程使用的唯一程式语言，因此任何人想学好 flex 第一件事绝对是先摸熟 actionscript 这个语言，包含：\n它的基本语法与结构(array, hash, loop, if else…) DisplayList (DisplayObject, DisplayObjectContainer)与 Event system(bubbling, propagating…) Sound, Video, NetConnection 与 Graphics class 掌握 as3 的精华后，接下来就可以进入 flex framework。\n＊Flex framework 的重点\n基本上 flex framework 就是用 actionscript 写成的框架，因此也可以把它看成是 as3的最好示范，看着 framework source 学 actionscript 也是挺不错的，只是路会变很长。\nFlex Framework 整个体系非常博大精深，通常一般人不太可能完整把它学完，只需要针对最常用到的部份熟悉就好，图中列出的那三块(component, managers, style/skin)就是我个人认为所有初学者最优先该学会的。\n＊Component 该学些什么\nComponent 是整个 flex framework 的基础，几乎80% 的元素都是由 UIComponent 继承而来，例如最根本的它本身就是一个 UIComponent，因此，熟悉 component 就成为学好 flex framework 最根本也最重要的基本功\nFlex 内建了 二十几个 UI controls, 例如 Button, DataGrid, HBox等，以种类来分，这些 components 可以概分为三大类：\nControls: Button, DateChooser, Slider… Containers: Box, DividedBox, Panel… List: DataGrid, Tree, TileList… 初学者第一步至少该学会怎么用这些元件，了解每个元件的 properties, events, styles, effects…，知道怎么在手册里查它的 API 文件，以及何时该用何种元件。\n进一步，则是学会怎么修改这些元件，例如继承一个 Button 下来加上不同的功能，或是写不同的 skin border 来改变它的外观。\n再进一步，则是开始研究元件的生命周期，了解每个元件是何时初始化，元件内部有那些关键指令与它们个别的功能，然后可以试着建立自已的 custom component。\n这一关看起来容易但实际上最困难，因为 flex 的 component framework 写的非常庞大，虽然乱中有序但要在混沌中看出隐藏的架构然后抓住重点整串提起，就非得有人带着指引正确的途径才比较可能完成。\n＊manager 是什么\n图中最上方的第二块就是 manager。\nflex 里有很多的 managers，负责做各种不同的工作(废话…)，几个比较重要的包含：\nSystemManager:\n它是每个 flex 程序的根源，最先被下载，也最早启动，由它进行一连串的 app boot流程 StyleManager:\n它负责整支app 的 css style 套用与 skin 生成，如果想玩动态 css 载换也靠它 DragManager:\nFlex最大的卖点就是 drag and drop（拖放），这个 manager 就是背后的英雄，初学者至少要学会怎么处理 drag 行为的五个事件，以及如何在不同元件间做拖放；进阶的玩家则应该深入研究这支 manager 是怎么写成的，详细阅读它的 source 会得到意想不到的无穷乐趣(如果你读完却没有这种感觉，呃，那代表你该再多读几次，如果还是没有，那请私下联络我 :D ) ModuleManager:\n使用 Flex 开发大型应用程式时，往往会将程式切割成许多小的 module, 这个 manager 就是负责载入并管理所有的 module (包含它的 class partition)，初心者或许用不到，但有志深入的玩家一定要很熟。 CursorManager:\n这个用到的时机不是很多，但偶尔要换一下 cursor 时还是会用到，初学者至少要知道怎么用指定的图案去换掉系统cursor。 ＊Style/Skin 的重点\nCSS style 与 skinning 是 Flex 最大的卖点之一，也是开发过程中较为麻烦也最耗时的部份。\n初学者应该要彻底了解如何使用 CSS style 来打点一支 flex app 的外观，换颜色、素材，使用外部 assets 修饰介面。\n中阶玩家则应该了解 skinning 的系统，包含 programmatic skinning 与 graphical skin，它们俩的差别？使用时机？如何客制化（zrong注1）？\n更高阶的玩家则应该熟悉整个 Styling system 的运作模式，外加如何动态载入 css 在 runtime 换掉整个介面。\n简而言之，flex app 写的好不好，外行人其实看不太出来，但一支 app UI 美不美则是一翻两瞪眼，比较漂亮的那就先加十分\n(当然，有一种情况是刻意用心去美化了介面结果弄巧成拙搞的怨声载道人人喊打，但那种比较不多见，也不是每家公司都会搞到这步田地，就先不讨论)\n＊学完基本功后下一步\n在我的标准里，当一个 developer 对上图内每一块都有中等程度的了解后，就算是完成 flex 养成教育，可以迈向下一个阶段。\n也就是开始熟悉 application 的制作手法，这包含\n了解至少一种以上的开发框架，例如 Cairngorm，老实说我对这个框架没什么好感(因为手法太复杂，只适合超复杂登月计画或火星探勘时使用)，但它结构设计良好，又是业界公认的圣杯，等于是专家们共通的语言，因此至少要先了解它在做什么，将来在专业场合才好沟通(俗话说 know the rules so you know what you are breaking, 就是指这情况) 接着可以看看比较简单的手法，像 Riawave, Model-Glue:Flex, PureMVC…等，基本上这些框架设计方式都大同小异，每个都有不同的应用场合，可以挑一个喜欢的再自行修改。 了解基本的概念，例如 Value Object, DAO, MVC 等，它们在大部份的程式框架里都会出现，早点学会日子比较轻松。 接着就是开始实际 coding，写一个中小型规模的app，不论是单纯的 CRUD (zrong注2)程序，或是留言版、电话簿、进销存管理都可以，籍由多写来强化编程的概念，然后透过大量的 peer code review 来找出可改进的地方。 ＊结论\n结论还是老话一句：要入门 flex 超级简单，只要不是白痴应该一小时就行，但要成为可独当一面的专业开发者，路就很长，如果没有走对方向很容易就迷失甚至最后放弃。\n换句话说，要能成为职场上真正需要的 professional developer，并不如表面上想象的容易(其实我想每种技术领域跟产业都一样吧)，这也是我过去半年来协助很多公司做 recruiting 后的感想。\nzrong注1：按客人要求不同定义\nzrong注2：CRUD是指在做计算处理时的增加、查询（重新得到数据）、更新和删除（create, retrieve, update, and delete）几个单词的首字母简写。主要被用在描述软件系统中数据库或者持久层的基本操作功能\n","date":"2007-08-10","description":"","lastmod":"2007-08-10T06:28:21Z","slug":"leaning-flex","tags":["as3","flex","study"],"title":"怎样学好Flex？","url":"https://blog.zengrong.net/post/leaning-flex/"},{"categories":["technology"],"content":"在编写FMS程序的时候，相信很多朋友后和我一样郁闷。没有一款好的编辑器来支持，不便于查错，没有语法自动完成，对于多个文件管理不方便...\n我一直是使用SE|PY ActionScript Editor写FMS程序的，虽然它比用Flash IDE来写ASC文件要方便许多，但是还是有很多细节没有考虑到。不过，现在我不必再郁闷下去了。\nfczone.com为我们带来了这样一个优秀的插件，让我们能够在Eclipse或者Flex Builder中开发FMS程序。\n首先看看它的功能特点把（图片来自于fczone.com）：\n大纲视图 代码自动完成 输出控制台（可监视trace的内容） 自动插入 导入和转换ActionScript2类文件 转换前 转换后\n语法和错误检查 下载插件：\nfor Flex Builder 3\nfor Eclipse 3.2\nfor Eclipse 3.3\n详细安装介绍 视频介绍\n","date":"2007-08-07","description":"","lastmod":"2007-08-07T07:49:39Z","slug":"eclipse-flex-builder-fms","tags":["eclipse","flexbuilder","fms"],"title":"在Eclipse或Flex Builder中开发FMS程序","url":"https://blog.zengrong.net/post/eclipse-flex-builder-fms/"},{"categories":["technology"],"content":"其实，对DatatGrid进行筛选，本质是对DataGrid的dataProvider进行筛选。筛选功能通过ICollectionView接口的filterFunction属性实现。\n通常，DataGrid的dataProvider会是一个ArrayCollection，而ArrayCollection实现了ICollectionView接口，因此可以直接使用filterFunction属性。\n因此，要实现对一个ArrayCollection的筛选，只需要把一个自定义函数名指定给这个要被筛选的ArrayCollection的filterFunction属性，然后刷新ArrayCollection即可，例如：\n1cityAC.filterFunction = filterFunc; 2cityAC.refresh(); 在这里例子里面，我使用的数据是武汉市和宜昌市的下级行政区。可以使用三种条件进行筛选：\n显示武汉市、宜昌市、或者全部的行政区 显示区、县或者县级市 根据名称或者名称的拼音进行筛选 所以，这个自定义函数就是筛选功能实现的核心：\n1private function filterFunc(item:Object):Boolean 2{ 3 var _csv:int = citySelectCOB.selectedItem.data; //csv = citySelectValue 4 var _lsv:String = levelSelectCOB.selectedLabel; //lsv = levelSelectValue 5 var _sv:String = searchTI.text; //sv - seachValue 6 var _lre:RegExp = new RegExp(_lsv, \u0026#34;\u0026#34;); 7 var _sre:RegExp = new RegExp(_sv, \u0026#34;gi\u0026#34;); 8 var _cb:Boolean = (_csv == -1) ? true : (item.parent == _csv); 9 var _lb:Boolean = (_lsv == \u0026#34;全部\u0026#34;) ? true : _lre.test(item.label); 10 var _sb:Boolean = (_sv == \u0026#34;\u0026#34;) ? true : (_sre.test(item.label) || _sre.test(item.data)); 11 trace(\u0026#34;_sv:\u0026#34;, _sv) 12 trace(\u0026#34;_sre.test(item.label):\u0026#34;, _sre.test(item.data)); 13 trace(\u0026#34;item.label:\u0026#34;, item.data); 14 return _cb \u0026amp;\u0026amp; _lb \u0026amp;\u0026amp; _sb; 15} filterFunc这个函数的参数就是ArrayCollection的一项，通过在判断相应项目的值，返回true的项目会显示，返回false的项目则不会显示在DataGrid中。由于是多种条件，因此在函数中进行了三重判断，最后对三重判断的结果进行 与 操作或者最终判断值。\n范例效果：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n1 文件 ","date":"2007-08-06","description":"","lastmod":"2007-08-06T16:02:17Z","slug":"datagrid-filter","tags":["as3","datagrid","flex"],"title":"在DataGrid中实现多条件筛选和搜索","url":"https://blog.zengrong.net/post/datagrid-filter/"},{"categories":["technology"],"content":"汉英对照：\n接口 interface\n集合 collection\n看了Flex 2.01HelpUsing Data Providers and Collections章节的一些感受，可能有不对的。\nIList、ICollectionView与IViewCursor，这是Flex提供的三个接口，它们都位于mx.collections包下，作用与集合密切相关。\nIList 这个接口提供的方法和属性用来操作集合。它的方法都是基于索引的，例如：addItem()、addItemAt()等等，它不能提供排序、筛选等功能。IList的方法会直接影响原始数据的值。\nICollectionView 这个接口提供了对数据进行排序、筛选、功能。同时它的createCursor()方法还可以创建一个IViewCursor的实例。需要注意的是，这个接口的操作不会直接影响原始数据的值。排序不会影响原始数据的顺序，而筛选也不会删除原始数据的内容。\nIViewCursor 这个接口的功能更强大，它提供了完整的指针功能，可以对数据进行查找find()、定位seek()操作，可以利用书签保存当前的操作位置。同时，它也可以使用insert()、remove()等方法来修改原始数据。\n提到这三个接口，就不得不提到这两个类：ArrayCollection和XMLListCollection\n这两个类都是用于集合的，它们都实现了IList和ICollectionView接口，因此可以直接使用这两个接口的所有方法。不过，如果要使用书签等功能，就需要用createCursor()方法来创建一个IViewCursor的实例来实现。\n","date":"2007-08-04","description":"","lastmod":"2007-08-04T16:10:20Z","slug":"ilist-icollectionview-iviewcursor","tags":["as3","collection","flex","list"],"title":"我对IList、ICollectionView与IViewCursor的一点理解","url":"https://blog.zengrong.net/post/ilist-icollectionview-iviewcursor/"},{"categories":["technology"],"content":"这几天在学习 ActionScript3 的正则表达式，就顺便用Flex做了一个测试器来测试，如下：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n源文件下载：\n1 文件 ","date":"2007-07-30","description":"","lastmod":"2007-07-30T16:03:54Z","slug":"actionscript3-regexp-tester","tags":["as3","flex","regexp"],"title":"ActionScript3正则表达式测试器","url":"https://blog.zengrong.net/post/actionscript3-regexp-tester/"},{"categories":["technology"],"content":" 2010年9月4日：在Flash Player 10中，已经可以使用新的 TLF ；框架原生支持图片的嵌入和精确的控制了，详见以下几篇文章：\nFlash Text Engine、Text Layout Framework在Flex、Flash中的实现 在Flex中实现聊天表情图片支持-资料篇 在Flex中实现聊天表情图片支持-实战篇 从Flash Player7开始，我们就可以通过动态文本框的htmlText属性，使用 \u0026lt;img\u0026gt; 标签来嵌入图像或者swf影片。在Flex的LiveDoc中，可以找到这样一段描述 \u0026lt;img\u0026gt; 标签的“id”属性的文字：\nid Specifies the identifier for the imported image. This is useful if you want to control the embedded content with ActionScript.\n这说明，可以通过制定id属性，让ActionScript控制嵌入的图像。\n但是，怎样控制呢？我在Flex的帮助中并没有找到相关的信息，倒是在Flash CS3的“学习 Adobe Flash 中的 ActionScript 2.0”章节中找到了相关的说明：\nFlash 为每个 标签创建一个新的影片剪辑并在 TextField 对象中嵌入该影片剪辑。 标签的 id 属性允许您将实例名称分配到创建的影片剪辑。这允许您使用 ActionScript 控制该影片剪辑。\nFlash 创建的影片剪辑作为子级影片剪辑添加到包含该图像的文本字段中。\n在帮助中使用这样的代码来控制嵌入TextField中的swf文件：\n1this.createTextField(\u0026#34;textField_txt\u0026#34;, 10, 0, 0, 300, 200); 2textField_txt.html = true; 3textField_txt.htmlText = \u0026#34;Here\u0026#39;s an interesting animation: \u0026#34;; 4stop_btn.onRelease = function() { 5 textField_txt.animation_mc.stop(); 6}; 但是，这毕竟是ActionScript2的方法，在ActionScript3是行不通的。在ActionScript3中如何做呢？有人知道么？\n:neutral:\n","date":"2007-07-29","description":"","lastmod":"2007-07-29T15:16:47Z","slug":"actionscript3-htmltext","tags":["actionscript","as2","as3","flash","flex"],"title":"ActionScript3如何控制使用htmlText属性嵌入动态文本框中的图像？","url":"https://blog.zengrong.net/post/actionscript3-htmltext/"},{"categories":["technology"],"content":"此笔记是我阅读Flex 2.01 Help后的笔记，并不是帮助的翻译。\n创建正则表达式有两种方式：\n1var re1:RegExp = new RegExp(\u0026#34;zrong\u0026#34;, \u0026#34;i\u0026#34;); 2var re2:RegExp = /zrong/i; 在第一种方式中，第二个参数是正则表达式的参数，如果正则表达式不需要参数，则可以将第二个参数使用空字符串代替。\n首选的是第二种方式，书写的代码较少，而且对引号不需要转义。但是第二种方式需要使用 \\ 对 / 进行转义。例如：\n1var re1:RegExp = new RegExp(\u0026#34;1/2 \\\u0026#34;ZRong\u0026#39;s Blog\\\u0026#34;\u0026#34;, \u0026#34;\u0026#34;); 2var re2:RegExp = /1\\/2 \u0026#34;ZRong\u0026#39;s Blog\u0026#34;/; 在第一种方式中，针对已经包含 \\ 的操作符（例如 /d 、/w ），也需要用 \\ 转义，例如：\n1var re1:RegExp = new RegExp(\u0026#34;\\\\d+\u0026#34;, \u0026#34;\u0026#34;); 2var re2:RegExp = /\\d+/; ","date":"2007-07-22","description":"","lastmod":"2007-07-22T02:11:11Z","slug":"actionscript3-regular-expression-1","tags":["as3","regexp","study"],"title":"ActionScript3正则表达式学习笔记（1）-创建正则表达式","url":"https://blog.zengrong.net/post/actionscript3-regular-expression-1/"},{"categories":["news"],"content":"今天收到Director产品推广经理Rick Jones发来的邀请参与Director调查的邮件，告知Director将在2007年末发布新版本。多么激动人心的一个消息！ :em10:\nDirector最近的一个版本还是Director MX 2004，3年了，Flex 3 beta都已来到我们身边，Flash也升级到9.0，但Director这个多媒体开发工具的老大哥却迟迟没有更新，导致大部分使用Director+Flash进行开发的人们（也包括我）望眼欲穿，很多人投向其他开发工具的怀抱。\nDirector+Flash、Lingo+ActionScript的组合，可以完成Windows平台上任何多媒体内容的开发，绝对是最优秀的组合。但是，随着Flash的快速升级，Director逐渐无法支持新版本的swf格式，我也只能离开Director转而纯粹使用Flash开发课件。由于Flash基于WEB的先天特性，让课件与桌面的交互成为不可能完成的任务，要实现记录游戏成绩、保存屏幕截图等功能都需要WEB服务器支持，这太不方便。我也不太愿意使用MDM、SWFKit等加壳工具，因为每种工具的语法都不太一样，学习成本高，不划算，兼容性也不太好。AIR（Apollo）对系统的要求太高，而且需要环境支持，并非最好的多媒体开发平台。Director的回归，能解决这一切问题。\nAdobe最终没有放弃Director，就好像它没有放弃Fireworks一样。这是绝对正确的决定。\n不过，Authorware.......永别了 :neutral:\n","date":"2007-07-21","description":"","lastmod":"2007-07-21T14:33:47Z","slug":"director-new","tags":["adobe","director"],"title":"Director终于要升级了","url":"https://blog.zengrong.net/post/director-new/"},{"categories":["others"],"content":"8月1日更新\n8月7日更新\n今天打开台式机后发现显示器不亮，先以为是主板出问题了（刚买的690G呀 :cry: ），结果发现在局域网中可以访问台式机，于是怀疑到显示器头上。折腾了一阵发现打开显示器的时候，居然连显示器启动的BenQ Logo都没有了，现在可以断定是显示器的问题了。\n上google狂搜一番，发现和我一样情况的大有人在呀！原来明基的液晶显示器黑屏是个普遍现象！更有趣的是大多数都是刚过质保期就挂了。\n搜了一下3CReport上关于明基液晶显示器黑屏的投诉，19条......\n又去BenQ的官方液晶显示器论坛看了一下，第一页就有3条投诉液晶显示器黑屏的。\n我的FP91G是2005年7月23买的，到现在还不到两年，当时价格是RMB2950（我的银子呀 :em10: ）。上明基网站一查，19寸的显示器除了液晶屏外是三年保，看来我还没过保修期。可怜了17寸显示器的朋友们，只有1年保呀。。。\n买显示器的时候，为了便宜几十块钱没要发票，得想点办法才能保修了，看来发票这个东东还是很重要DI呀！\n所以，强烈建议各位看到本文的网友，千万别买BenQ明基的液晶显示器了！ :em12:\n保修的过程，我会更新在博客上的，感兴趣的朋友可以关注。\n===================\n7月19日，明基在武汉的维修部上门取走了显示器，声明6日之内可以修好并送回；\n7月27日，明基维修部打电话给我，说显示器修好后准备送出时又出现故障，需要再等两天；\n7月30日，我打电话给明基客服投诉，客服记下我的显示器序列号后说帮我催促维修部；我接着打电话给维修部，维修部说显示器已经修好，但由于送货之前未联系上我本人，因此今天不能送出，明后天可以送出；\n8月1日，继续等待中。。。\n===================\n8月3日，维修好的显示器终于送到。前后用了16天时间，维修期间不提供代用品。 :sad:\n","date":"2007-07-16","description":"","lastmod":"2007-07-16T15:59:38Z","slug":"benq-lcd","tags":["benq","monitor"],"title":"千万别买BenQ明基的液晶显示器","url":"https://blog.zengrong.net/post/benq-lcd/"},{"categories":["tutorial"],"content":" 2012年7月6日更新： 有网友反映说在Win7下录屏文件没有声音，于是加入最新版播放器下载地址。 2007年7月9日更新： 添加第二期班的录屏文件，更新源文件压缩包。大家久等了 :em31: 2007年7月16日更新： 添加第三期班的录屏文件，更新源文件压缩包。弄了两个多小时，辛苦辛苦 这里公布的录屏文件采用 Cisco WebEx Recoding Editor 录制，也可以使用该软件播放。下面的“录屏文件播放器”就提供了该软件的下载地址。\n1 文件 如果在Windows 7上出现一些播放上的问题，可以下载最新版的WebEx Player，下载地址：\nhttp://www.webex.com/play-webex-recording.html\n本次培训班授课大致内容参见这里\n3 文件 注意：每期班内容类似，不必重复下载。其中：\n第三期每个录屏文件都有内容简介；\n第一期班仅提供了6月28日一天的录屏文件；\n如果有不能下载的，请留言或者E-mail联系我：\nE-mail\n第三期班（7月10-14日） 7月10日的录屏文件：\n6 文件 7月11日的录屏文件：\n8 文件 7月12日的录屏文件：\n0712-0840-0905.wrf.exe（12.2MB）\n制作“幻灯片”影片剪辑，加入“数据流”声音\n0712-0916-0926.wrf.exe（3.9MB）\n用ActionScript控制“幻灯片”影片剪辑\n0712-1000-1030.wrf.exe（8.9MB）\n用ActionScript控制外部FLV视频\n0712-1100-1200.wrf.exe（17.8MB）\n使用ActionScript控制视频、使用嵌入视频、使用组件控制视频\n0712-1410-1530.wrf.exe（5MB）\n学习Date对象，学习if...else if结构\n0712-1600-1730.wrf.exe（6.6MB）\n学习用function自定义函数 制作奥运会倒计时器\n7月13日的录屏文件：\n0713-0840-1145.wrf.exe（21.3MB）\n熟悉影片剪辑属性，用ActionScript控制影片剪辑\n0713-1420-1530.wrf.exe（12.4MB）\n蝴蝶游戏，for循环的使用\n0713-1600-1730.wrf.exe（3.5MB）\n20以内的四则混合运算，学习while循环，嵌套if结构\n7月14日的录屏文件：\n0714-0840-1140.wrf.exe（10.1MB）\n20以内的四则混合运算课件完成\n第二期班（7月2-6日） 7月2日的录屏文件：\n0702-1015-1110.wrf.exe（48MB）\n0702-1140-1200.wrf.exe（7.6MB）\n0702-1410-1450.wrf.exe（17.2MB）\n0702-1530-1600.wrf.exe（61MB）\n0702-1630-1730.wrf.exe（22.8MB）\n7月3日的录屏文件：\n0703-0840-1030.wrf.exe（17.5MB）\n0703-1100-1200.wrf.exe（11.4MB）\n0703-1430-1530.wrf.exe（9.9MB）\n0703-1540-1610.wrf.exe（12.8MB）\n0703-1630-1730.wrf.exe（8.4MB）\n7月4日的录屏文件：\n0704-0840-0900.wrf.exe（6.8MB）\n0704-0920-1000.wrf.exe（14.9MB）\n0704-1030-1200.wrf.exe（8MB）\n0704-1440-1700.wrf.exe（13.9MB）\n7月5日的录屏文件：\n0705-0850-1140.wrf.exe（21MB）\n0705-1430-1730.wrf.exe（10.7MB）\n7月6日的录屏文件：\n0706-0900-1040.wrf.exe（11.5MB）\n第一期班（6月25-29日） 6月28日的录屏文件：\n0628_0830-0910.wrf.exe（8.8MB）\n0628_0930-0940.wrf.exe（1.4MB）\n0628_1000-1030.wrf.exe（3.2MB）\n0628_1100-1140.wrf.exe（14MB）\n0628_1300-1530.wrf.exe（29MB）\n0628_1630-1940.wrf.exe（21.8MB）\n全文完 ","date":"2007-06-30","description":"","lastmod":"2026-03-19T09:50:54Z","slug":"07flash-training","tags":["flash","training","capture"],"title":"07Flash课件培训班授课内容录屏及源文件","url":"https://blog.zengrong.net/post/07flash-training/"},{"categories":["technology"],"content":"2015-01-12更新： 由于 Dreamer 的网站已经不能访问了，我来提供 源码 下载。\n在 Dreamer 那里看到为DataGrid加背景色的方法，于是在项目中实验了一下，结果在编译的时候出错，错误内容为：\nCould not resolve \u0026lt;mx:columns\u0026gt; to a component implementation.\n看来是 \u0026lt;columns\u0026gt; 标签不能使用了。怎么办？将 columns 的包名改为与继承类的包名相同即可，例如：\n1\u0026lt;av:ColoredDataGrid id=\u0026#34;dg\u0026#34; 2\twidth=\u0026#34;100%\u0026#34; height=\u0026#34;100%\u0026#34; 3\tdataProvider=\u0026#34;{emceeList}\u0026#34; 4\tvariableRowHeight=\u0026#34;true\u0026#34; 5\twordWrap=\u0026#34;true\u0026#34; 6\ttoolTip=\u0026#34;{intro}\u0026#34;\u0026gt; 7\t\u0026lt;av:columns\u0026gt; 8\t\u0026lt;mx:DataGridColumn headerText=\u0026#34;昵称\u0026#34; dataField=\u0026#34;name\u0026#34;/\u0026gt; 9\t\u0026lt;mx:DataGridColumn headerText=\u0026#34;状态\u0026#34; labelFunction=\u0026#34;type\u0026#34; width=\u0026#34;60\u0026#34;/\u0026gt; 10\t\u0026lt;/av:columns\u0026gt; 11\u0026lt;/av:ColoredDataGrid\u0026gt; ","date":"2007-06-16","description":"","lastmod":"2007-06-16T13:51:47Z","slug":"could-not-resolve-to-a-component-implementation","tags":["as3","datagrid","flex"],"title":"继承DataGrid时出现错误：Could not resolve \u003cmx:columns\u003e to a component implementation.","url":"https://blog.zengrong.net/post/could-not-resolve-to-a-component-implementation/"},{"categories":["web"],"content":" 问：一次在局域网中访问其他电脑时，弹出窗口需要我输入用户名和密码，结果我选择了“保存密码”项，以后每次访问那台电脑时都不需要输入密码了，这台电脑马上将会让给其他人用了，我现在想清除先前保存的那个密码，不知如何操作？\n解决：要想清除保存的密码可以这样来操作：单击“开始→控制面板”，双击其中的“用户账户”图标，在弹出的窗口中选择你的用户名，然后再单击“管理我的网络密码”项，在弹出的窗口中清除保存的密码项即可。\n我也经常遇到这样的问题。不过试过这个方法好像不太好用。删除不会立即生效，也许要重启计算机才能生效。\n所以就找到了下面这个方法，利用强大的net命令，一行代码即可。\nnet use //192.168.16.2 /delete 其中双斜杠后面可以用主机名，也可以用ip地址。\n这个命令的作用是“取消一个网络连接，并且从永久连接列表中删除该连接。”\n关于net use的其他用法，可以使用“net help use”来查看。\n","date":"2007-06-16","description":"","lastmod":"2007-06-16T05:15:25Z","slug":"net-use-delete","tags":["windows"],"title":"清除访问局域网时保存的密码","url":"https://blog.zengrong.net/post/net-use-delete/"},{"categories":["technology"],"content":"碰到这样一个奇怪的问题：\n一个Flex 2项目，其中算上Base state一共有三个state（以下简称baseState、state1、state2），从baseState到state2的互相切换都很正常，切换的state1也很正常。但是一旦切换到state1后，就不能再切换回state2或者baseState了，报错如下：\n1ArgumentError: Error #2025: 提供的 DisplayObject 必须是调用者的子级。 2\tat flash.display::DisplayObjectContainer/removeChild() 3\tat mx.core::Container/removeChild() 4\tat mx.states::AddChild/remove() 5\tat mx.core::UIComponent/::removeState() 6\tat mx.core::UIComponent/::commitCurrentState() 7\tat mx.core::UIComponent/setCurrentState() 8\tat mx.core::UIComponent/set currentState() 9\tat stateTest2/::baseHandler() 10\tat stateTest2/___Button1_click() 百思不得其解，进行了无数次测试后发现问出出在ApplicationControlBar组件上。\n原来，在项目设计中，baseState是一个loading效果，state1则是主要界面内容。在state1中，有一个ApplicationControlBar组件，这个组件有一个dock属性。当dock属性值为true的时候，ApplicationControlBar会自动贴紧在界面上边缘，为了实现这个功能，或许Flex自动将ApplicationControlBar组件的层级提升至最高层。这样，当切换到其他state需要自动删除此组件的时候，由于组件处于最高层而不能被删除，就出现了上面的错误。\n知道错误的原因，解决方法也就非常简单了：将dock属性设置为false或者直接删除dock属性即可。\n也可以把ApplicationControlBar组件作为其他组件（例如VBox）的子级添加，然后通过操作其父组件来显示它。\n如果是Debug版本的Flash Player，测试下面的Demo就可以看到出错信息：\nApplicationControlBar组件对State的影响 未修改前效果\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\nApplicationControlBar组件对State的影响 修改后效果\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n源码（未修改前）：\n1\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;utf-8\u0026#34;?\u0026gt; 2\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; layout=\u0026#34;vertical\u0026#34;\u0026gt; 3\t\u0026lt;mx:Script\u0026gt; 4\t\u0026lt;![CDATA[ 5\tprivate function baseHandler():void 6\t{ 7\tcurrentState = \u0026#34;\u0026#34;; 8\t} 9\tprivate function state1Handler():void 10\t{ 11\tcurrentState = \u0026#34;state1\u0026#34;; 12\t} 13\tprivate function state2Handler():void 14\t{ 15\tcurrentState = \u0026#34;state2\u0026#34;; 16\t} 17\t]]\u0026gt; 18\t\u0026lt;/mx:Script\u0026gt; 19\t\u0026lt;mx:Style\u0026gt; 20\tApplication 21\t{ 22\tfont-size:12; 23\t} 24\t\u0026lt;/mx:Style\u0026gt; 25\t\u0026lt;mx:states\u0026gt; 26\t\u0026lt;mx:State name=\u0026#34;state1\u0026#34;\u0026gt; 27\t\u0026lt;mx:AddChild position=\u0026#34;lastChild\u0026#34;\u0026gt; 28\t\u0026lt;mx:ApplicationControlBar id=\u0026#34;appBar\u0026#34; dock=\u0026#34;true\u0026#34; width=\u0026#34;100%\u0026#34;\u0026gt; 29\t\u0026lt;mx:Label text=\u0026#34;state1 in ApplicationControlBar\u0026#34;/\u0026gt; 30\t\u0026lt;/mx:ApplicationControlBar\u0026gt; 31\t\u0026lt;/mx:AddChild\u0026gt; 32\t\u0026lt;mx:RemoveChild target=\u0026#34;{label1}\u0026#34;/\u0026gt; 33\t\u0026lt;/mx:State\u0026gt; 34\t\u0026lt;mx:State name=\u0026#34;state2\u0026#34;\u0026gt; 35\t\u0026lt;mx:AddChild position=\u0026#34;lastChild\u0026#34;\u0026gt; 36\t\u0026lt;mx:Label text=\u0026#34;state2\u0026#34;/\u0026gt; 37\t\u0026lt;/mx:AddChild\u0026gt; 38\t\u0026lt;mx:RemoveChild target=\u0026#34;{label1}\u0026#34;/\u0026gt; 39\t\u0026lt;/mx:State\u0026gt; 40\t\u0026lt;/mx:states\u0026gt; 41\t\u0026lt;mx:HBox\u0026gt; 42\t\u0026lt;mx:Button label=\u0026#34;切换到baseState\u0026#34; click=\u0026#34;baseHandler()\u0026#34;/\u0026gt; 43\t\u0026lt;mx:Button label=\u0026#34;切换到state1\u0026#34; click=\u0026#34;state1Handler()\u0026#34;/\u0026gt; 44\t\u0026lt;mx:Button label=\u0026#34;切换到state2\u0026#34; click=\u0026#34;state2Handler()\u0026#34;/\u0026gt; 45\t\u0026lt;/mx:HBox\u0026gt; 46\t\u0026lt;mx:Label text=\u0026#34;base state\u0026#34; id=\u0026#34;label1\u0026#34;/\u0026gt; 47\u0026lt;/mx:Application\u0026gt; ","date":"2007-06-13","description":"","lastmod":"2007-06-13T11:01:45Z","slug":"applicationcontrolbar-state","tags":["flex"],"title":"ApplicationControlBar组件对State的影响","url":"https://blog.zengrong.net/post/applicationcontrolbar-state/"},{"categories":["technology"],"content":"在NetStream信息对象提供的大量信息中，我常用到的有这么几个：\n信息 类型 范围 说明 NetStream.Play.PublishNotify 发布流 所有订阅者 当发布者的流开始发布的时候，所有的订阅者都会收到这个消息。但发布者不会收到这个消息。 NetStream.Play.UnpublishNotify 发布流 所有订阅者 当发布者的流停止发布的时候，所有的订阅者都会收到这个消息。但发布者不会收到这个消息。 NetStream.Publish.BadName 发布流 发布者 当发布者试图发布一个已经存在的流时会收到这个消息。只有发布者可以收到这个消息。 NetStream.Publish.Start 发布流 发布者 当发布者开始发布流的时候收到这个消息。只有发布者可以收到这个消息。 NetStream.Unpublish.Success 发布流 发布者 当发布者结束发布流的时候收到这个消息。只有发布者可以收到这个消息。 NetStream.Play.Reset 播放流 播放者 当播放者开始播放流之前会收到这个消息。只有播放者可以收到这个消息 。 NetStream.Play.Start 播放流 播放者 当播放者开始播放流的时候会收到这个消息。只有播放者可以收到这个消息 。 NetStream.Play.Stop 播放流 播放者 当播放者停止播放流的时候会收到这个消息。只有播放者可以收到这个消息 。\n假设我们有2个流，一个叫做PU，用于发布，一个叫做PL，用于播放PU发布的流。那么当PU开始发布的时候（如果发布成功），消息的发布情况是这样的：\nPU会收到 NetStream.Publish.Start消息，PL会收到 NetStream.Play.PublishNotify消息。\n当PU停止发布的时候，情况是这样的：\nPU会收到 NetStream.Unpublish.Success，PL会收到 NetStream.Play.UnpublishNotify。\n当PL开始播放的时候，情况是这样的：\nPL会收到 NetStream.Play.Reset、 NetStream.Play.Start，PU不会收到任何消息。\n当PL停止播放的时候，情况是这样的：\nPL会收到 NetStream.Play.Stop消息，PU不会收到任何消息。\n由此可见，发布者的发布和停止发布操作，所有的订阅者都可以收到，而订阅者对流的播放操作则并不会通知流的发布者。因此，对于流的控制上，还是要盯紧发布者。 :shock:\n","date":"2007-06-01","description":"","lastmod":"2007-06-01T14:47:23Z","slug":"netstream-information-objects","tags":["fms","netstream"],"title":"NetStream information objects","url":"https://blog.zengrong.net/post/netstream-information-objects/"},{"categories":["others"],"content":"这是个迟到的录音。本来28号凌晨看完演唱者就应该传上来的，可惜那时网络不通，所以就迟了两天。\n用我的DOPOD 900录的，效果就别提了，只是感受一下气氛好了。 :em20:\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2007-05-29","description":"","lastmod":"2007-05-29T13:57:03Z","slug":"jacky2007","tags":["Jacky-Cheung"],"title":"张学友2007好久不见演唱会武汉站全程录音","url":"https://blog.zengrong.net/post/jacky2007/"},{"categories":["others"],"content":"7月22日更新：三期培训班的教师操作录屏文件（含声音）、授课用实例源文件下载地址\n时间：\n第一期：6月25-29日 第二期：7月2-6日 第三期：7月10-14日 如果大家对内容和时间有意见，请直接留言。\n时间 培训主题 主要内容 课件实例 第一天上午 预备知识 多媒体要素、常用开发工具简介、Act 第一天下午 媒体整合使用 Photoshop、Firework 英语课件-Breakfasts for Pit and Pat 对图片的处理 第二天上午 媒体整合使用 Goldwave对声音的处理 课件结构搭建 Video Encoder的使用、视频素材转换 用ActionScript控制FLV 视频 第二天 下午 控制声音 Sound类的详细用法，控制内部和外部声音 数学－百分数的意义和写法（省级比赛课使用） 使用载入音频的技巧和注意事项 绑定Attach音频到影片剪辑 控制音量、音量滑块 第三天 上午 控制时间 Date类的详细用法、倒计时功能制作 英语－It's time for Lunch（全国比赛课使用） 时钟的制作 奥运会倒计时器 将常用的功能封装成组件（Component) 第三天 下午 影片剪辑控制 实时改变影片剪辑的大小、角度、位置、深度 美术——景物素描构图（全国获奖课件） 第四天全天 高级开发 课件中的游戏制作 英语－Colours 英语－Pets 第五天 取得不重复的随机数、控制影片剪辑的运动 上午 多用户游戏管理 使用SharedObject类，在本地保存游戏结果 第五天 技术展望 Flash 9.0介绍 下午 Flex 2.0 技术介绍 ActionScript3.0介绍 OOP、OOD介绍 全文完 ","date":"2007-05-22","description":"","lastmod":"2007-05-22T03:01:13Z","slug":"flash-training","tags":["flash","training"],"title":"基于Flash的教学辅助软件制作技术培训班日程","url":"https://blog.zengrong.net/post/flash-training/"},{"categories":["technology"],"content":" 作者：flashlizi 来自：经典论坛 在AS2中我们已经知道有 public, private 这2个访问控制符，在AS3中增加了 internal 和 protected，并且对类、方法的访问控制更加严格，也更加合理和方便。另外，AS3中还增加了命名空间 namespace ，这样我们的访问控制更是灵活无比。（public和private相信大家已经熟悉了，故不赘述）如有错误，敬请指正。\ninternal指明类、变量、常数、方法等在包package级别可访问到。大家可能会问它和public与private的区别。public只能在package{}中才能使用，但public控制的类或方法可以在任何地方访问到；private只能在class{}中才能使用，它控制的类或方法仅能在相应的class中才能访问到。而internal可以在package{}外使用，但它只能作用于这个as文件内部。比如如下DDD.as中代码为：\n1package flashrek.example{ 2 public class DDD { 3 function DDD() { 4 var e:EEE=new EEE(); 5 6 } 7 } 8} 9internal var internalStr:String=\u0026#34;internalStr\u0026#34;; 10class EEE { 11 function EEE() { 12 trace(internalStr); 13 } 14} 那么如下代码运行结果为：\n1import flashrek.example.DDD; 2var d=new DDD();//输出：internalStr 3trace(internalStr);//输出：错误Err 可以看出，类DDD可以访问到package外的类EEE（默认就是internal，因此可以省略），而类EEE也可以访问到变量internalStr。而DDD.as文件外的其他地方就无法访问到EEE和internalStr了。\n和internal不同，protected只能应用于类class内。它指明变量、常数、方法等在类class级别可访问到。与private区别在于，protected指定的方法、变量等是可以继承的，在子类中能访问到他们。\nAS3中还引入了命名空间namespace。当你有一些有特殊用途的方法分布在不同的包package里，你想要这些方法在所有package里都可以应用，但你又不想把这些方法设置为public。这个时候，namespace就能达到你的目的。\n首先你要定义一个namespace，（flashrek.as）：\n1package flashrek.example{ 2 public namespace flashrek= \u0026#34;www.flashrek.com\u0026#34;; 3} 然后在AAA.as文件中的代码为：\n1package flashrek.example{ 2 import flashrek.example.flashrek; 3 public class AAA { 4 function AAA() { 5 var b:BBB=new BBB(); 6 //flashrek::NStest(); 7 } 8 flashrek function NStest() { 9 trace(\u0026#34;namespace test\u0026#34;); 10 } 11 } 12} 13//package外的类、命名空间不能默认且只能是internal的，且仅能为在此package内使用。 14//package外是不能使用public关键字的。 15internal class BBB { 16 function BBB() { 17 trace(\u0026#34;BBB\u0026#34;); 18 var c:CCC=new CCC(); 19 } 20} 21class CCC { 22 function CCC() { 23 trace(\u0026#34;CCC\u0026#34;); 24 } 25} 接下来再看运行结果：\n1import flashrek.example.AAA; 2import flashrek.example.flashrek; 3var a=new AAA(); 4a.flashrek::NStest(); 5//输出： 6BBB 7CCC 8namespace test 由此我们可以看出，通过a.flashrek::NStest();这样的方法，我们可以访问到NStest方法，而其他地方是无法直接访问到这个方法的。\n在上例中大家发现，namespace很像一个类。确实有点像：）但namespace可以在package外，class内进行定义，而且也可以用public、internal等来进行访问控制。\n比如去掉上例中的flashrek.as，而把AAA.as改成如下：\n1package flashrek.example{ 2 public class AAA { 3 function AAA() { 4 var b:BBB=new BBB(); 5 flashrek::NStest(); 6 } 7 flashrek function NStest() { 8 trace(\u0026#34;namespace test\u0026#34;); 9 } 10 } 11} 12//package外的类、命名空间不能默认且只能是internal的，且仅能为在此package内使用。 13//package外是不能使用public关键字的。 14namespace flashrek= \u0026#34;www.flashrek.com\u0026#34; 15internal class BBB { 16 function BBB() { 17 trace(\u0026#34;BBB\u0026#34;); 18 var c:CCC=new CCC(); 19 } 20} 21class CCC { 22 function CCC() { 23 trace(\u0026#34;CCC\u0026#34;); 24 } 25} 在这里 namespace flashrek 定义在package外，它只能在package级别访问到。因此用a.flashrek::NStest();是错误的。\n","date":"2007-05-13","description":"","lastmod":"2007-05-13T07:12:27Z","slug":"as2toas3-2","tags":["actionscript","as2","as3"],"title":"【转】从AS2到AS3之二 ——访问控制以及命名空间","url":"https://blog.zengrong.net/post/as2toas3-2/"},{"categories":["technology"],"content":" 作者：flashlizi 来自：经典论坛 随着Flash CS3正式版的发布，估计很多朋友都开始从AS2迁移到AS3了。不过AS3比AS2改变的实在太多，在as2中很多方法属性在as3中并不相同，甚至消失，而且目前帮助文档是E文的，现下的as3基础文章也并不多，因此特开此贴，集合一些基础性的要点，尽量以例子来说明，让大家更快更好的往AS3迁移。文中如有错误，敬请指正。有兴趣的朋友也可以一起来添加\n1、常量\n先看AS2代码：\n1var str:String; 2var num:Number; 3var boo:Boolean; 4var obj:Object; 5var notyped; 6trace(str+newline+num+newline+boo+newline+obj+newline+notyped); 7//return 8undefined 9undefined 10undefined 11undefined 12undefined 再看AS3代码：\n1var str:String; 2var num:Number; 3var boo:Boolean; 4var obj:Object; 5var notyped; 6trace(str+\u0026#34;\\n\u0026#34;+num+\u0026#34;\\n\u0026#34;+boo+\u0026#34;\\n\u0026#34;+obj+\u0026#34;\\n\u0026#34;+notyped); 7//return 8null 9NaN 10false 11null 12undefined 从上面2个例子可以清楚的看到：在as2中 String、Number、Boolean、Object 的默认值都是undefined，而在as3中则分别为 null、NaN、false、null 。只有未作声明的变量的默认值才是 undefined 。因此，在 as2 中一个 undefined 打天下的时代已经过时了，在as3中更加明确。同时as2中还有一个常量 newline 在as3中已经取消，由 \\n 替换。另外特别说明就是 trace 方法的功能也加强了，参数个数不限，因此上面可以改成 trace(str,num,boo,obj,notyped) 了。\n2、操作符\n这里只讨论instanceof。虽然as3中还保留了这个操作符，但as3中推荐用is代替它。而且is的用法更为灵活。\n代码：\n1var mystr:String=\u0026#34;test\u0026#34;; 2var mytest:Test = new Test();//Test is extends SuperTest 3trace(mytest instanceof SuperTest); //AS2 return false //AS3 return true 4trace(mystr instanceof String);//AS2 return false 5trace(mystr instanceof String);//AS3 return true 6trace(mystr is String);//AS3 return true 从此例可以看出 instanceof 在as3中的比as2中有了变化。在as2中 instanceof 不会将原始类型转换为包装对象，因此在上面例子中的 String 验证中返回 false ，并且它对超类（如SuperTest）不起作用。而在as3中它只检查原型链，所以在第一个 trace 中会返回 true。但是as3中 instanceof 并不支持接口，而 is 支持。\n代码：\n1var mytest:Test = new Test();//Test implements InterfaceTest 2trace(mytest is InterfaceTest); //AS3 return true 3trace(mytest instanceof InterfaceTest); //AS3 return false 至于 add、eq、gt、ge、\u0026lt;\u0026gt; 、and、not、or、ne、lt、le 等AS1的语法在as3中统统取消了。\n3、参数\n在AS3中增加了一个 ...(rest) 这样的参数。它的作用是指明函数可以接收任意多个以逗号分隔的参数。AS3代码：\n1function testfun1(param0, param1, ...arg) 2{ 3 trace(arg is Array,arg,arg.length); 4} 5testfun1(\u0026#34;param0\u0026#34;,\u0026#34;param1\u0026#34;,\u0026#34;param2\u0026#34;,\u0026#34;param3\u0026#34;); 6//return: true param2,param3 2 在这里 ...arg 表示一个名为arg的数组。\n当运行testfun1后， arg=[\u0026quot;param2\u0026quot;,\u0026quot;param3\u0026quot;] 。还记得function有个 arguments 类么，当使用了 ...(rest) 后，argumnets 就无法获取了。但是如上例所示 arg.length 同样可以使用。但是并没有类似 arguments.callee 的这种方法，所以要使用 ...arg 的时候确保不会使用 arguments.callee 的方法。\n","date":"2007-05-13","description":"","lastmod":"2007-05-13T07:07:30Z","slug":"as2toas2-1","tags":["actionscript","as2","as3"],"title":"【转】从 AS2 到 AS3 之一——常量\u0026操作符\u0026参数","url":"https://blog.zengrong.net/post/as2toas2-1/"},{"categories":["technology"],"content":"在使用ActionScript3的NetConnection类连接Flash Media Server或者Remoting的时候，需要注意以下几点：\n1.注意设定AMF版本\nActionScript3默认使用的是AMF3，而ActionScript2和ActionScript1使用的是AMF0，如果不指定NetConnection的AMF版本，就会出现连接失败。指定的方法如下：\n1NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0; 上面的代码会修改所有的NetConnection实例的默认AMF版本。\n如果只需要修改一个NetConnection实例的AMF版本，代码如下：\n1public var NC:flash.net.NetConnection; 2NC = new NetConnection(); 3NC.objectEncoding = flash.net.ObjectEncoding.AMF0; 2.获取onStatus状态值\n在ActionScript2中的方法已经不能使用，要获取NetConnection实例的连接状态，必须使用NetStatusEvent事件类，代码如下：\n1import flash.events.NetStatusEvent; 2import flash.net.NetConnection; 3 4private function statusHandler(evt:NetStatusEvent):void{ 5 trace(evt.info.code); 6 switch(evt.info.code){ 7 case \u0026#34;NetConnection.Connect.Rejected\u0026#34;: 8 var appmsg:String = (evt.info.application == undefined) ? \u0026#34;\u0026#34; : evt.info.application; 9 mx.controls.Alert(appmsg); 10 break; 11 case \u0026#34;NetConnection.Connect.Failed\u0026#34;: 12 mx.controls.Alert(\u0026#34;连接失败\u0026#34;); 13 break; 14 case \u0026#34;NetConnection.Connect.Closed\u0026#34;: 15 // 16 break; 17 case \u0026#34;NetConnection.Connect.Success\u0026#34;: 18 trace(\u0026#34;连接成功！\u0026#34;); 19 break; 20 } 21} 22 23private function login():void{ 24 adminNC = new NetConnection(); 25 adminNC.addEventListener(NetStatusEvent.NET_STATUS, statusHandler); 26 adminNC.connect(\u0026#34;http://localhost/test/test1\u0026#34;, userName, userPWD); 27} 3.在NetConnection实例中定义被呼叫方法\n在ActionScript2中，我们可以使用myNC.functionName=function(){}来定义NetConnection对象中被呼叫的方法。但是，在ActionScript3中，NetConnection并非动态类，不能用这样的方法添加被呼叫方法，而必须使用client属性实现(接上例)：\n1import flash.events.NetStatusEvent; 2import flash.net.NetConnection; 3 4public function loginSuccess($adminLevel:int):void{ 5 trace(\u0026#34;loginSuccess被调用,$adminLevel:\u0026#34; + $adminLevel); 6 this.dispatchEvent(new Event(\u0026#34;loginSuccess\u0026#34;)); 7} 8 9private function login():void{ 10 adminNC = new NetConnection(); 11 adminNC.client = this; 12 adminNC.addEventListener(NetStatusEvent.NET_STATUS, statusHandler); 13 adminNC.connect(\u0026#34;http://localhost/test/test1\u0026#34;, userName, userPWD); 14} 当登录成功后，FMS端会调用客户端NC的loginSuccess方法，并传回管理员的级别。在这里，为client属性赋值，告知被调用的loginSuccess方法在哪个对象中。被调用的方法必须被声明为public。\n还有一些和SharedObject和NetStream类相关的注意事项，可以看这里。\n","date":"2007-03-26","description":"","lastmod":"2007-03-26T12:37:53Z","slug":"flex-netconnection","tags":["as3","fms","netconnection"],"title":"在Flex2中使用NetConnection的一些注意事项","url":"https://blog.zengrong.net/post/flex-netconnection/"},{"categories":["technology"],"content":"Flash Player 8可以支持100MB以下的大文件上传。目前，已经有许多网站利用Flash Player的这个特性来上传大文件了。例如著名的youtube，Box.net国内的G宝盘等等。在互联网上，很早我就找到了这个不错的开源swf上传组件：SWFUpload，但是由于它不太符合项目需求，并且没有帮助材料，我对它进行了一些修改。\n注意：SWFUpload组件的版权归原作者所有\n修改主要体现在以下几个方面：\n由“一选择就上传”改为“先选择，后上传”； 加入了选择成功的回调函数设定 支持单文件上传和多文件上传模式 可以删除待上传队列中的文件 支持按照设定的顺序上传特定类型的文件 以下是针对此组件所写的一个比较详细的中文说明：\n1使用说明： 2 3将jscripts目录上传至服务器，不要修改其中的文件路径 4mmSWFUpload.js是调用swf的核心文件，不要修改其路径和内容 5upload.swf是上传主功能所在文件，不要修改其路径和内容 6progressbar.png是上传进度条图像文件，可以替换和修改 7 8example.js是定义本例所使用的JavaScript函数的文件，可以修改 9example.css定义本例的外观，可以修改 10index.htm是本文件，可以修改 11 12进度条、选择文件的列表等所有效果全部由JavaScript实现，具体的实现未知详见sample.js中相关函数 13 14============================================================= 15swf提供的供JavaScript调用的函数 16 17mmSWFUpload.callSWF() 18此函数打开一个选择文件对话框 19当选择成功时，swf会回调select_done_callback参数中定义的JavaScript函数， 20当选择不成功（取消）时，swf会回调upload_cancel_callback参数中定义的JavaScript函数 21使用示例：\u0026lt;a href=\u0026#34;javascript:mmSWFUpload.callSWF();\u0026#34;\u0026gt;选择文件\u0026lt;/a\u0026gt; 22 23mmSWFUpload.doDelete() 24此函数删除待上传文件队列中的最后一个文件 25当删除成功时，swf会回调delete_done_callback中定义的JavaScript函数 26使用示例：\u0026lt;a href=\u0026#34;javascript:mmSWFUpload.doDelete();\u0026#34;\u0026gt;删除\u0026lt;/a\u0026gt; 27 28mmSWFUpload.doUpload() 29此函数告知swf开始上传文件队列 30当上传开始时，swf会回调upload_start_callback中定义的函数 31当上传正在进行中时，swf会回调upload_progress_callback中定义的函数 32当上传出错时，swf会回调upload_error_callback中定义的函数 33当上传完成时，swf会回调upload_complete_callback中定义的函数 34当队列中的所有文件上传完成时，swf会回调upload_queue_complete_callback中定义的函数 35使用示例：\u0026lt;a href=\u0026#34;javascript:mmSWFUpload.doUpload();\u0026#34;\u0026gt;上传\u0026lt;/a\u0026gt; 36 37============================================================= 38参数介绍： 39 40debug 41是否使用调试模式，在调试模式下会显示mmSWFUpload的设置信息 42 43upload_backend 44用于上传的后台程序地址。这个地址是相对于mmSWFUpload.js的 45 46target 47swf显示于那个div中？这里指定div的id 48 49link_mode 50如果不希望自动显示上传链接，将值设为standalone 51 52link_text 53上传链接的文字。如果使用了link_mode，则参数无效。 54如果希望用一个图片代替上传链接，则注释此行，设定css_class参数值 55 56css_class 57用于修饰上传链接。这是一个CSS类的名称 58 59allowed_filesize 60允许的最大文件大小。单位KB 61 62select_mode 63值为空： 只能选择一个文件上传，每次选择的新文件会替换旧文件 64值为multifile： 每次选择的新文件会加在待上传文件队列的末尾 65值为multifileLimit：这种情况下，必须定义select_multifile_limit， 66根据select_multifile_limit定义的扩展名顺序选择上传文件， 67如果选择的文件顺序与select_multifile_limit定义的顺序不匹配， 68则会禁止选择并调用upload_error_callback，错误代码为-60。 69当按照定义的顺序选择文件完毕后，会调用select_multifile_done_callback定义的JavaScript函数。 70 71select_multifile_limit 72此参数仅当select_mode值为multifileLimit时有效 73定义要按顺序上传的文件扩展名，扩展名间用英文半角逗号分隔 74例如要上传三个文件，第一个为jpg图片，第二个为doc文档，第三个为zip压缩包， 75则定义为“.jpg,.doc,.zip” 76 77allowed_filetypes 78允许的文件扩展名 79 80============================================================== 81函数介绍： 82 83此处的参数定义的是当一个事件发生时swf调用的JavaScript函数名称 84 85upload_start_callback 86上传开始的时候调用，参数介绍见sample.js中的uploadStart定义 87 88upload_progress_callback 89上传进行中调用，参数介绍见sample.js中的uploadProgress定义 90 91upload_complete_callback 92上传完成调用，参数介绍见sample.js中的uploadComplete定义 93 94upload_error_callback 95上传失败调用，参数介绍见sample.js中的uploadError定义 96 97upload_cancel_callback 98上传取消调用，参数介绍见sample.js中的uploadCancel定义 99 100upload_queue_complete_callback 101所有上传队列完成后调用，参数介绍见sample.js中的uploadQueueComplete定义 102 103select_done_callback 104选择一个文件成功后回调，参数介绍见sample.js中的selectedDone定义 105 106select_multifile_done_callback 107此函数仅当select_mode值为multifileLimit时有效 108当按照select_multifile_limit定义的顺序选择完成后调用 109参数介绍见sample.js中的selectMultifileDone定义 110 111delete_done_callback 112当在上传列表中删除一个已经选择的等待上传的文件时调用 113删除待上传文件使用mmSWFUpload.doDelete() 114参数介绍见sample.js中的deleteDone定义 修改后的组件下载：\n1 文件 =======================================================================\nPS.\n本文的修改是基于0.8.5版本进行的，写完这篇文章，又去SWFUpload主页看了看，发现作者已经将其更新到1.0.2版本，并且改用FlashDevelop开发（原来使用Flash IDE开发）。新版本提供了一个选项auto_upload来控制是自动上传还是先选择后上传，网站上还提供了详细的帮助文档。\n","date":"2007-03-25","description":"","lastmod":"2007-03-25T04:10:40Z","slug":"swfupload","tags":["actionscript","flash"],"title":"利用Flash上传大文件：swfupload修改以及详细说明","url":"https://blog.zengrong.net/post/swfupload/"},{"categories":["news"],"content":"此贴由周戈(DarkStone)编写, 转贴请写明来源!\n请注意, 要下载以下项目, 请首先 登录Adobe网站\n客户端: Flex Builder 2 with Charting (集成 Flex Builder 2 + Flex 2 SDK + Flex 2 Charting, 大小:168.78MB)\nFlex Builder 2 Eclipse plug-in (集成 Flex Builder 2 + Flex 2 SDK + Flex 2 Charting 作为 Eclipse 的插件, 大小:168.78MB)\nFlex Builder 2.01 Updater (Flex Builder 重要更新 2.01, 大小:144MB)\n服务器端:\nFlex Data Services 2 Express (完全免费, 但只能用于单核心CPU电脑上, 大小:93.73MB)\n组件单独下载:\nFlex 2 SDK (包含 Flex 2 所有组件的类库 和 Flex编译器, 大小:38.07MB)\nFlex Charting 2 (Flex 2 图表组件, 大小:17.04MB)\nFlex Automation Package (Flex 测试包, 仅提供给 Flex Data Services 2 企业版用户使用, 大小:104.24MB)\n","date":"2007-03-07","description":"","lastmod":"2007-03-07T12:44:28Z","slug":"330","tags":["flex"],"title":"【转】ADOBE FLEX 2 全套下载!","url":"https://blog.zengrong.net/post/330/"},{"categories":["technology"],"content":"非常值得一看的教程，由AsWing的作者iiley所写。点击下面的链接下载：\nActionScript2转ActionScript3\n下面是目录：\nAS2 转AS3.......................................................................................................................................1\n第 语言相关的转换.........................................................................................................3\n1． 类（Class）的转换...........................................................................................3\n2． 方法（Method）的转换....................................................................................4\n3． 变量（Variable）的转换................................................................................5\n4． 语言上其他方面的转换注意事项.......................................................................6\n第 可视元素（MovieClip, Button…）的转换......................................................7\n1． 主要机制的变化...................................................................................................7\n2． 主要的可视元素类介绍.......................................................................................7\n3． 使用注意...............................................................................................................8\n第 事件的转换...............................................................................................................10\n1． 监听 Handling the events）...........................................10\n2． 事件类（Event classes）..........................................................................11\n3． 事件流（Event flow）.................................................................................11\n第 其他转换...................................................................................................................14\n1． 数（Number）的转换......................................................................................14\n2． 映射（Map）的更好实现方法..........................................................................14\n3． Interval timer的实现有更多选择...........................................................15\n4． 更多强大的新功能.............................................................................................15\n第 总结与建议...............................................................................................................16\n","date":"2007-02-27","description":"","lastmod":"2007-02-27T13:43:56Z","slug":"329","tags":["actionscript","as2","as3"],"title":"ActionScript2转ActionScript3","url":"https://blog.zengrong.net/post/329/"},{"categories":["use"],"content":"仿照全拼风格为搜狗拼音输入法制作的一款皮肤，与上个版本(windows经典)相比，重新制作了竖排同窗口、竖排分窗口模式以及“全拼/双拼”转换按钮，使其更像“全拼”。\n效果截图：\n下载地址：\nhttp://www.sogou.com/pinyin/skins/view_skin.php?skin_id=112\n","date":"2007-02-26","description":"","lastmod":"2007-02-26T16:39:55Z","slug":"328","tags":["ime"],"title":"又为搜狗拼音输入法做了一款模仿全拼输入法风格的皮肤","url":"https://blog.zengrong.net/post/328/"},{"categories":["use"],"content":"搜狗拼音输入法推出3.0预览版后，我换了几个皮肤都感觉不如意。个人比较喜欢类似于全拼输入法的经典皮肤风格，不过在下载了这款皮肤之后感觉它还“经典”的不够。于是就仿照全拼输入法的风格自己制作了一个，制作时以全拼输入法的状态栏为模版，根据个人的需求调整了状态栏按钮的分布，去除了微软的logo。\n由于搜狗拼音输入法皮肤制作需要4种模式，为了偷懒，除横排单窗口之外的其他的三种模式就直接使用了这款皮肤提供的模式。\n效果截图：\n下载地址：http://www.sogou.com/pinyin/skins/view_skin.php?skin_id=108\n","date":"2007-02-26","description":"","lastmod":"2007-02-26T07:56:29Z","slug":"327","tags":["ime","windows"],"title":"我制作的搜狗拼音输入法Windows经典风格皮肤","url":"https://blog.zengrong.net/post/327/"},{"categories":["web"],"content":"由于将Blog升级到WordPress2.1，原来通过修改admin-functions.php在后台中加入的表情符号不见了，找到自己原来写的两篇文章：如何在WordPress后台中加入表情符号和在WordPress后台中增加自定义表情，发现已经不管用。因为在admin-functions.php文件中已经找不到edToolbar这个函数，怎么办？\n找到wp-gian作者的Blog，发现他已经将此插件更新，目前插件支持WordPress2.1，并且已经不需要手动修改代码就能将表情加入到wordpress后台和评论页面中了。\n该插件的详细介绍　下载插件\n安装该插件时，只需要覆盖原来的插件即可，但有一点要注意，就是如果你的blog版本是2.1，就要把压缩包中自带的prototype.js覆盖掉wp-includes/js/ 目录下的同名文件。\n最后，如果已经按照如何在WordPress后台中加入表情符号一文所述修改过comments.php文件，记得要改回来。 :em24:\n","date":"2007-02-25","description":"","lastmod":"2007-02-25T04:37:13Z","slug":"wordpress21-wpgian","tags":["wordpress","plugin","emotion"],"title":"在WordPress2.1后台和评论页面中加入表情符号","url":"https://blog.zengrong.net/post/wordpress21-wpgian/"},{"categories":["use"],"content":"我常用的输入法是搜狗拼音输入法，但当Word2003开启时，会自动打开微软拼音输入法。这个功能让人讨厌。今天在网上google到了解决方法：\n通常只要一打开Word 2003就会自动切换到“微软拼音输入法”，如果你惯用的不是这种输入法恐怕就会觉得很困扰了。能不能让Word启动时自动打开我们惯用的输入法呢？这其实并不难做到。\n如果你不希望Word在启动时自动打开中文输入法，那么还有另一个更简单的办法可以使用。依次选择“工具→选项”菜单命令，在出现的对话框中选择“编辑”选项卡，取消勾选其中的“输入法控制处于活动状态”复选框即可。\n这里还有一些在word2003中修改其他默认设置的小技巧，可以看看。\n","date":"2007-02-25","description":"","lastmod":"2007-02-25T02:54:00Z","slug":"word2003-ime","tags":["ime","office"],"title":"打开word2003时使用常用输入法","url":"https://blog.zengrong.net/post/word2003-ime/"},{"categories":["technology"],"content":"看了一下Flex builder 2的帮助文件，原来建立DataGrid的方法非常灵活，大致有下面几种：\n1.最简单的方法 1\u0026lt;mx:DataGrid\u0026gt; 2 \u0026lt;mx:ArrayCollection\u0026gt; 3 \u0026lt;mx:Object\u0026gt; 4 \u0026lt;mx:姓名\u0026gt;zrong\u0026lt;/mx:姓名\u0026gt; 5 \u0026lt;mx:邮箱\u0026gt;zrongzrong@gmail.com\u0026lt;/mx:邮箱\u0026gt; 6 \u0026lt;mx:主页\u0026gt;www.zengrong.net\u0026lt;/mx:主页\u0026gt; 7 \u0026lt;/mx:Object\u0026gt; 8 \u0026lt;mx:Object\u0026gt; 9 \u0026lt;mx:姓名\u0026gt;orphen\u0026lt;/mx:姓名\u0026gt; 10 \u0026lt;mx:邮箱\u0026gt;orphen123@gmail.com\u0026lt;/mx:邮箱\u0026gt; 11 \u0026lt;mx:主页\u0026gt;www.orphen123.net\u0026lt;/mx:主页\u0026gt; 12 \u0026lt;/mx:Object\u0026gt; 13 \u0026lt;/mx:ArrayCollection\u0026gt; 14\u0026lt;/mx:DataGrid\u0026gt; 在ArrayCollection中定义Object标签来实现数据的添加。每个Object标签代表一行，而Object中的每个子标签代表一列。\n同样的，也可以使用Object标签的属性来设置行和列，就像下面这样：\n1\u0026lt;mx:DataGrid\u0026gt; 2 \u0026lt;mx:ArrayCollection\u0026gt; 3 \u0026lt;mx:Object 姓名=\u0026#34;zrong\u0026#34; 邮箱=\u0026#34;zrongzrong@gmail.com\u0026#34; 主页=\u0026#34;www.zengrong.net\u0026#34;/\u0026gt; 4 \u0026lt;mx:Object 姓名=\u0026#34;orphen\u0026#34; 邮箱=\u0026#34;orphen123@gmail.com\u0026#34; 主页=\u0026#34;www.orphen123.net\u0026#34;/\u0026gt; 5 \u0026lt;/mx:ArrayCollection\u0026gt; 6\u0026lt;/mx:DataGrid\u0026gt; 至于两者的效果，自然是一样的。\n实际上，上面的两种写法都省略了两个标签：source和dataProvider。由于dataProvider是dataGrid的默认属性，因此在这里可以省略。同样的，由于source是ArrayCollection的默认属性，在这里也被省略掉了。如果加上这两个标签，就算是最复杂（但最完整）的写法了：\n2.最复杂的方法 1\u0026lt;mx:DataGrid\u0026gt; 2 \u0026lt;mx:dataProvider\u0026gt; 3 \u0026lt;mx:ArrayCollection\u0026gt; 4 \u0026lt;mx:source\u0026gt; 5 \u0026lt;mx:Object\u0026gt; 6 \u0026lt;mx:姓名\u0026gt;zrong\u0026lt;/mx:姓名\u0026gt; 7 \u0026lt;mx:邮箱\u0026gt;zrongzrong@gmail.com\u0026lt;/mx:邮箱\u0026gt; 8 \u0026lt;mx:主页\u0026gt;www.zengrong.net\u0026lt;/mx:主页\u0026gt; 9 \u0026lt;/mx:Object\u0026gt; 10 \u0026lt;mx:Object\u0026gt; 11 \u0026lt;mx:姓名\u0026gt;orphen\u0026lt;/mx:姓名\u0026gt; 12 \u0026lt;mx:邮箱\u0026gt;orphen123@gmail.com\u0026lt;/mx:邮箱\u0026gt; 13 \u0026lt;mx:主页\u0026gt;www.orphen123.net\u0026lt;/mx:主页\u0026gt; 14 \u0026lt;/mx:Object\u0026gt; 15 \u0026lt;/mx:source\u0026gt; 16 \u0026lt;/mx:ArrayCollection\u0026gt; 17 \u0026lt;/mx:dataProvider\u0026gt; 18\u0026lt;/mx:DataGrid\u0026gt; 19\u0026lt;/pre\u0026gt; 20\u0026lt;h3\u0026gt;3.使用ActionScript的写法\u0026lt;/h3\u0026gt; 21\u0026lt;pre lang=\u0026#34;actionscript\u0026#34;\u0026gt; 22\u0026lt;mx:Application xmlns:mx=\u0026#34;http://www.adobe.com/2006/mxml\u0026#34; 23 layout=\u0026#34;vertical\u0026#34; fontSize=\u0026#34;12\u0026#34; 24 initialize=\u0026#34;init();\u0026#34;\u0026gt; 25 \u0026lt;mx:Script\u0026gt; 26 \u0026lt;![CDATA[ 27 import mx.collections.ArrayCollection; 28 private var DGArray:Array =[ 29 {name:\u0026#34;zrong\u0026#34;, email:\u0026#34;zrongzrong@gmamil.com\u0026#34;, site:\u0026#34;www.zengrong.net\u0026#34;}, 30 {name:\u0026#34;orphen\u0026#34;, email:\u0026#34;orphen123@gmail.com\u0026#34;, site:\u0026#34;www.orphen123.com\u0026#34;}]; 31 [Bindable] 32 public var initDG:ArrayCollection; 33 public function init():void{ 34 initDG = new ArrayCollection(DGArray); 35 } 36 ]]\u0026gt; 37 \u0026lt;/mx:Script\u0026gt; 38 \u0026lt;mx:DataGrid dataProvider=\u0026#34;{initDG}\u0026#34;/\u0026gt; 39\u0026lt;/mx:Application\u0026gt; 3.使用ActionScript的写法 1 2 3 4 import mx.collections.ArrayCollection; 5 private var DGArray:Array =[ 6 {name:\u0026#34;zrong\u0026#34;, email:\u0026#34;zrongzrong@gmamil.com\u0026#34;, site:\u0026#34;www.zengrong.net\u0026#34;}, 7 {name:\u0026#34;orphen\u0026#34;, email:\u0026#34;orphen123@gmail.com\u0026#34;, site:\u0026#34;www.orphen123.com\u0026#34;}]; 8 [Bindable] 9 public var initDG:ArrayCollection; 10 public function init():void{ 11 initDG = new ArrayCollection(DGArray); 12 } 13 14 15 个人认为，这种方法上面两种要更加简单灵活一些。这个方法实现的关键是把Array转换成ArrayCollection类型。\n4.控制列的显示 我们定义的数据一共有三列，在DataGrid中默认是全部显示的。要控制某列不显示，可以使用columns标签和DataGridColumn标签。在下面这个例子中，数据沿用上例。仅改变DataGrid的定义。\n1\u0026lt;mx:DataGrid dataProvider=\u0026#34;{initDG}\u0026#34;\u0026gt; 2 \u0026lt;mx:columns\u0026gt; 3 \u0026lt;mx:DataGridColumn headerText=\u0026#34;姓名\u0026#34; dataField=\u0026#34;name\u0026#34;/\u0026gt; 4 \u0026lt;mx:DataGridColumn headerText=\u0026#34;邮箱\u0026#34; dataField=\u0026#34;email\u0026#34; width=\u0026#34;200\u0026#34;/\u0026gt; 5 \u0026lt;/mx:columns\u0026gt; 6\u0026lt;/mx:DataGrid\u0026gt; 效果就是这样：\n当然，还可以为每一列定义id，从而互动性的控制某列的显示：\n1\u0026lt;mx:DataGrid dataProvider=\u0026#34;{initDG}\u0026#34;\u0026gt; 2 \u0026lt;mx:columns\u0026gt; 3 \u0026lt;mx:DataGridColumn headerText=\u0026#34;姓名\u0026#34; dataField=\u0026#34;name\u0026#34;/\u0026gt; 4 \u0026lt;mx:DataGridColumn headerText=\u0026#34;邮箱\u0026#34; dataField=\u0026#34;email\u0026#34; width=\u0026#34;200\u0026#34;/\u0026gt; 5 \u0026lt;mx:DataGridColumn id=\u0026#34;site\u0026#34; headerText=\u0026#34;主页\u0026#34; dataField=\u0026#34;site\u0026#34; width=\u0026#34;150\u0026#34; visible=\u0026#34;false\u0026#34;/\u0026gt; 6 \u0026lt;/mx:columns\u0026gt; 7\u0026lt;/mx:DataGrid\u0026gt; 8\u0026lt;mx:Button label=\u0026#34;显示/隐藏{site.headerText}列\u0026#34; click=\u0026#34;site.visible=!site.visible;\u0026#34;/\u0026gt; 查看效果:\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2007-02-21","description":"","lastmod":"2007-02-21T04:40:06Z","slug":"create-datagrid-in-flex","tags":["as3","datagrid","flex"],"title":"在Flex中建立DataGrid的方法","url":"https://blog.zengrong.net/post/create-datagrid-in-flex/"},{"categories":["impressions"],"content":"看到十款背单词软件测评报告一文之前，我只用过敏特记易（敏特智能英语教室），感觉确实还不错。不过看到文中提到了奇迹英语智能记忆（后面简称“奇迹”），感觉和敏特差不多（作者的感觉也是这样），就顺便去了一下该软件的网站。这一下子让我发现了一个大“秘密”：\n“奇迹”的广告词和敏特差不多，“奇迹”是48小时背诵2880单词，敏特是60小时背诵3000单词。 “奇迹”背单词的方法和敏特基本上是相同的 如果说从上面两条还不能判断是谁抄袭谁的话，下面发生的事情就更加让我感觉不可思议了：\n在“奇迹”的用户体会页面中的某条体会居然和敏特的用户体会一模一样！不同之处就是体会者是“家庭主妇”。可是，我清楚的记得，这个体会是我的同事在辅导他女儿使用敏特背诵单词之后写的。\n这让我感觉很恶心。平心而论，敏特确实卖的很贵，“奇迹”抄袭它一下，拉低一下它的价格，先不说法律上的因素，对于大多数学生用户来说，未必不是件好事。不过可惜了，抄也要有点风格，连别人的用户体会都一起抄过来而且只字不改，就有点抄的过分了。\n","date":"2007-02-16","description":"","lastmod":"2007-02-16T13:38:36Z","slug":"320","tags":["english"],"title":"敏特记易和奇迹英语智能记忆-谁是抄袭者？","url":"https://blog.zengrong.net/post/320/"},{"categories":["others"],"content":"本文转自电脑爱好者论坛\n原帖地址：http://bbs.cfan.com.cn/thread-454681-1-1.html\n以下为转贴： 背英语单词是最令人头疼的事儿，如果能选择一套适合的背单词软件，对于单词记忆会有事半功倍的效果，然而目前市场上背单词软件到处都是，我们应该选择什么软件呢，这确实是件另人头疼的事，选到好的，对我们的学习也许有很大帮助，找到不好的，可能反而会耽误了我们的学习，为此，我亲自对目前几款流行的背单词软件做了一个大概测评，纯属个人测评，不受拖于任何人，主要参数是：记多少单词/1小时，记忆正确率，第二天的遗忘率，是否有一套精确的复习安排等几个方面进行测试。\n1、 疯狂单词无忧记忆：（http://www.wainsoft.com）\n简介：据说是背单词有种疯狂的感觉，而且说是智能化程度最高，。。\n一个小时测试结果：\n总学习：238词\n已知： 121词\n生词： 117词\n测验结果：记住35词/小时\n正确率：33%\n个人测评：感觉效率低，没有循环记忆安排。据说使用这套软件背单词时，给人一种疯狂的感觉，但我似乎感觉不到哦。~ 2、新东方背单词：（http://www.neworiental.org ）\n简介：新东方，大家都熟悉，感觉这套系统跟《我爱背单词》差不多，浏览记忆，如同背词典。\n一个小时测试结果：\n总学习：175词\n已知： 47词\n生词： 128词\n测验结果：记住40词/小时\n正确率：31%\n评价：感觉效率还可以，记忆效率与自己背词典效率差不多，没有循环记忆安排。有几个不错的单词测验游戏。 3、奇迹英语智能记忆（\n）\n简介：这套软件我在广告邮件中找到，最初都当垃圾删了，后来试用一下却很惊异，这是一套全新的单词速记系统，感觉相当不错，对于智能循环记忆管理和复习方式有相当好的模式。\n一个小时测试结果：\n总学：111 词，\n已知：9 词\n生词：102 词\n测验结果：记住101词/小时\n正确率： 99%\n复习系统：能有效复习遗忘部分，复习效率较高。\n测评：这是一套相当简洁高效的速记系统，单词循环错位记忆相当精确，较为准确的利用了遗忘曲线进行循环记忆和复习，是一套不错的单词记忆软件，目前最新版本支持真人发音，功能强大，不可多得。\n=================================================================\n4、我爱背单词 （http://www.flyenglish.com/）\n简介：历史较久的单词助记软件。\n一个小时测试结果：\n总学：187 词，\n已知：52 词\n生词：135 词\n测验结果：记住42词/小时\n正确率： 30%\n复习系统：浏览复习，复习效率一般。\n评价：这套软件历时顶悠久，该套软件功能很多，但对于记忆效率来说效果一般，因为是浏览记忆，跟自己背词典差不多，没有循环记忆安排。 5、敏特记忆引擎 （http://www.mintel.com）\n简介：一套风靡全国的网上学习系统，敏特英语教室。\n一个小时测试结果：\n总学：115 词，\n已知：11 词\n生词：104 词\n测验结果：记住98词/小时\n正确率： 96%\n测评：这套系统是留美科学家研究成功，这是一套高效的速记系统，是真正较为准确的利用了遗忘曲线规律，是一套不错的单词记忆系统，但收费较高，按课程收费，而且必须依赖网络学习。 6、轻轻松松背单词（http://www.pgy.com.cn）\n一个小时测试结果：\n总学：283 词，\n已知：56 词\n生词：227 词\n测验结果：记住43词/小时\n测评：是一套单词助记系统，优点是可以浮于桌面自动阅读记忆，用户有是可以轻松的边做事边背单词，缺点是没有安排适当的循环记忆，使得记忆效率不是很高。 7、单词快车 （http://www.soft520.net/index.htm）\n一个小时测试结果：\n总学：68 词，\n已知：25 词\n生词：43 词\n测验结果：记住29词/小时\n测评：该单词记忆软件的优点是，在记忆单词同时让用户多次拼写单词。但这个优点却也是该软件最大的缺点，使得他的记忆效率很低。我主张背单词的时候要有意识的训练自己在蒙住中文的情况下，最快的说出中文，越快越好。这样的步骤以后你会比用记忆法记单词的同学做题更快，理解的更准确。 8、词汇大爆炸 （http://www.netful.net/）\n一个小时测试结果：\n总学：189 词，\n已知：58 词\n生词：131词\n测验结果：51词/小时\n正确率： 39%\n=================================================================\n测评：该软件的优点是，单词记忆界面较为有趣，基本能按遗忘曲线循环，但还不是很准，缺点是，系统初始化过于繁杂，可能很多用户会无法使用。\n另外还有《我也爱背单词》《我们都爱背单词》《不得不背单词》《不要背单词》等软件，测试了一下，没坚持到一个小时，学习效果一般，测试到20分钟即放弃未坚持测试，我想效果也不是很好，大家有兴趣可以测试。 五星推荐：奇迹英语智能记忆（http://ee.qjnet.net，http://61.143.225.27/ee.qjnet.net），根据实验，这确实是一套非常简洁高效的速记系统，对于遗忘曲线的循环错位记忆还是相当的准确，并且有一套比较精确的自动安排复习系统。\n五星推荐：敏特记忆引擎，这套系统的记忆效率丝毫不亚于奇迹英语智能记忆，而且是通过网页学习，这使得用户可以在任何能够上网的地方学习，然而这也是他的缺点，必须依靠网络学习，而且价格比较昂贵。\n四星推荐：我爱背单词，这套软件的记忆效率不是很高，但对于习惯于背词典的用户，这套软件是很好的替代品，他基本上能模拟你以前自己背单词的情况。 后记：对于英语单词记忆软件，按照记忆的研究情况，我们基本上可以分为“背词典”，“单词助记”和“记忆管理”三个层次，其中“背词典”属于较低层次，软件基本上只是代替了我们人工拿着词典去背，第二层次是“单词助记”，即软件对我们的记忆有较好的推助作用，我觉得奇迹英语，敏特记忆，新东方和我爱背单词等可以达到此层，而对于“记忆管理”，则是对于大量单词记忆进行有效的循环记忆管理和对于大量单词记忆之后能够进行精确的复习安排，一般我们学习100多个单词以内，我们还可以控制今天背哪些单词，明天复习哪些单词，但对于大量的单词记忆，比如记忆10000个单词，那么我们将很难精确的安排记忆的循环和复习的循环，假如前面30天学习了5000个单词，那么明天我们是应该学习新单词，还是复习旧单词，是复习哪天的旧单词，哪些单词是我们最难记的，等等，我们必须依靠电脑软件对我们学习进行详细记录，然后才有可能对这些记忆进行较为有效而精确的管理，而目前能够基本做到这点的，目前国内基本上只有《奇迹英语智能记忆》和《敏特记忆引擎》能够粗略做到。\n另外，很多单词记忆软件宣称自己的软件是尊照艾宾浩斯遗忘规律进行科学安排记忆复习，但其实并不是那样的，大家也不要被这些说法所蒙蔽，艾宾浩斯当年只是针对没有任何规律的事物进行了多次实验得到的遗忘曲线，但这个遗忘曲线并不适合于单词记忆，因为单词是有规律的，而且不同的单词，针对不同的个人，记忆的遗忘曲线也是完全不同的，因此要使单词记忆尊照记忆遗忘曲线安排记忆和复习，那么必须需要几万个单词，几百种不同的人群进行无数次的实验才能获得，很少有个人或企业能够有条件做到。根据测试，目前众多产品中，智能教育研发中心开发的 “奇迹英语智能记忆”和美国心理学家设计的“敏特记忆引擎”能够较为准确的利用了遗忘曲线规律。\n","date":"2007-02-16","description":"","lastmod":"2007-02-16T13:13:22Z","slug":"319","tags":["english"],"title":"【转】◆◆十款背单词软件测评报告◆◆","url":"https://blog.zengrong.net/post/319/"},{"categories":["technology"],"content":"昨天得到andot的消息，CoolPlayer插件已经更新到版本9了，于是在升级Blog到WordPress2.1的同时，把coolplayer也更新了。\ncoolplayer9的改动相当大，不仅开始支持多国语言，整合的flv和mp3播放器也进行了升级。插件激活后会自动在编辑器中添加quicktag“酷播”，看来这就是coolplayer的中文名称了。比我原来自己修改quicktags.js要方便许多了。\n在coolplayer的压缩包中看到，原来的mp3player已经更新成了jeroenwijering的FLASH MP3 PLAYER 3.3，效果可以看这里：\n顺便看了一下这个Flash Mp3 Player，作者已经将其开源，网站上可以下载到源代码，使用ActionScript2开发。我下载了最新的3.5版本希望更新coolplayer自带的3.3版本，结果更新后发现不能正常调用。也许是升级增加了一些功能导致调用方法改变把。等有时间好好研究一下源码再说了。\n","date":"2007-02-15","description":"","lastmod":"2007-02-15T07:09:43Z","slug":"coolplayer-flashmp3player","tags":["coolplayer","flash","wordpress","plugin"],"title":"coolplayer、Flash MP3 Player以及源码","url":"https://blog.zengrong.net/post/coolplayer-flashmp3player/"},{"categories":["news"],"content":"把WordPress升级到2.1有点点麻烦，具体可以看这里。相比WordPress2.1版本的强大功能来讲，这点小麻烦又算得了什么？\n2.1版的简体中文语言包\n","date":"2007-02-04","description":"","lastmod":"2007-02-04T02:43:16Z","slug":"worpress21","tags":["wordpress"],"title":"升级到WordPress2.1","url":"https://blog.zengrong.net/post/worpress21/"},{"categories":["others"],"content":"上接 【转】点评“最好的300款免费软件”(1)\nSecurity/安全\nAVG free - 反病毒。此类软件不太了解：原因1：公司统一装了商业软件诺顿；原因2：好习惯比好软件更重要，详见安全为重即不安全：年度软件评选感受之二。其要点为：\n在安全方面我发自内心的结论——也是本文的主张，更是善用佳软一贯倡导——乃是：好习惯比好软件更重要。洁身自好的应用习惯，才是真正的安全之道，尤其是免于人祸的安全之道，比任何安全软件都更加有效。小而言之，不装非法软件、不上破解网站，将使风险降低80%。大而言之，尊重版权行为合法的用户才能得到法律的保护。否则只是小偷控诉流氓，难以博得同情。\nAvast Home Free - 反病毒。\nAntiVir PersonalEdition -反病毒。\nBitDefender Free -反病毒。\nClamWin -反病毒。此类软件太多，一篇形象的对比就是《对四大杀软的有趣评价》\nCyberDifender - 网络安全套件。\nAd-aware - 反间谍软件。\nSpybot: Search \u0026amp; Destroy - 反间谍软件。\nWindows Defender - 反间谍软件。\nSpywareBlaster - 反间谍软件。\nSpyware Terminator - 反间谍软件。\nTootkit Reveaker - rootkit检测工具。\nWinpooch - 系统保护。\nHiJack Free - 系统保护。\nHighJackThis - 小巧知名的工具，反绑架（中文翻译真不习惯）好助手。\nKerio Personal Firewall - 防火墙。\nSygate Personal Firewall - 防火墙。\nZoneAlarm - 防火墙。最著名的一款。\nAxCrypt - 文件加密。\nSimple File Shredder -安全删除文件。\nPuTTy - SSH客户端。怎么列入安全软件了？应该归入Internet类啊。如果你不用Unix，它对你的用途就是代替Cterm、Sterm。\nKeePass - 密码管理。此类中名气最大。\nLockNote - 密码管理。\nnPassword - 密码管理。\nMicrosoft Baseline Security Analyzer - MS出品，发现安全方面配置缺陷。\n【小结】安全习惯比安全软件重要得多得多！远离黄赌毒网站，远离盗版破解网站，不用非法软件，风险降低99%。Network/网络(略)\nHamachi - VPN client\nRealVNC - remote control\nUltraVNC - remote control\nEthereal - local area network administration\nThe Dude - network administration\nWireshark - network administration\nAngry IP Scanner - IP scanner\nIP-Tools - IP scanner\nFree Port Scanner - IP scanner\nNetMeter - network bandwidth monitoring\nServers/服务器\nFileZilla - FTP客户端。开源世界第一名。推荐。\nFileZilla Server - FTP服务器，开源世界第一名。相对竞争对手而言，Server比上面的客户端更领先，至于所谓的中文乱码，见“FileZilla的乱码相关文章”。而此款软件的入门教程，及更多介绍，见《用FileZilla Server开FTP：看图入门》。\nEFTP - FTP 客户端和服务器。有了Filezilla和下文补充的几款，就无须尝试这个了。\nXAMPP - Apache, mySQL, PHP and Perl的集成服务器。一般人用不到。\nWAMP - Apache, PHP5、MySQL 服务器。一般人用不到。\n【小结】其实，还有一些相当不错的FTP server，比如TYPSoft FTP Server，小巧多能的免费FTP服务器。说到强大，就属Xlight FTP Server 2.24（小巧强大，个人免费）和超级强大的ioFTPD（小巧、现已开源）。还有一款非常好的用http方式共享文件的HFS，参见《HFS让web共享文件如此容易，可分权限上传下载》，还有一款经newsmth网友修改过的不足100KB的web服务器，见《一个精巧实用的HTTP Server(开源 70KB)》。\nAudio/音频\nFoobar2000 -音频播放器。从WinAmp开始，如果你要求新，求酷，求更大的个性化，那么就是Foobar2000。这就是它们的区别，区别在于风格，至于谁的音质更好，我认为是心理作用。\nWinAmp -音频播放器。这就是经典。经典得久了，就有人求新，于是出了Foobar。\n1by1 -音频播放器。国内用得很少，国外用得多一些（其实是我猜的），100KB级的mp3播放器，界面可定制，功能很独特。称为“目录播放器”。\n图4 异常小巧的1by1\nJetAudio - 音频播放器，不错。\nXMPlay - 音频播放器\nXion - 音频播放器\nApollo - 音频播放器\nMediaMonkey - 音乐管理\nThe GodFather - 音乐管理\ndBpowerAMP - 音频转换\nAudacity- 音频转换。本文原作者称之为音频转换，它在xbeta眼中就是一全能音频处理工具。从功能看，它可以录音、混音、制作特效…支持多种格式（wav,mp3,ogg……）。而本身则体积小巧，下载2MB稍多。在国外软件站点，长期以来都是同类第一，并且更新迅速，功能日益改进。xbeta把它装在笔记本上后，培训、会议录音、或者在活动中做一些场景声音都一直用它。更多介绍见《善用Audacity：免费开源的录音编辑软件》。\n图5 免费开源的Audacity\nWavePad -音频转换。也不错，但是还是推荐Audacity。\nKristal Audio Engine - 音频编辑。\nExact Audio Copy - CD ripper\nAudiograbber - CD ripper\nCDex - CD ripper\nMp3 Tag Tools - tag编辑器\nMp3tag - tag编辑器\nTaggin’ MP3 - tag编辑器\nMonkey’s Audio - APE 压缩/解压\nmpTrim - mp3 编辑器\nWavTrim -wave编辑器。推荐Audacity。\nEncSpot Basic - 分析 mp3\n【小结】播放器，各取所爱。录音编辑，首荐Audacity。\n下转【转】点评“最好的300款免费软件”(3)\n","date":"2007-01-24","description":"","lastmod":"2007-01-24T15:06:55Z","slug":"xbeta-freeware300-2","tags":["freeware","software","xbeta"],"title":"【转】点评“最好的300款免费软件”(2)","url":"https://blog.zengrong.net/post/xbeta-freeware300-2/"},{"categories":["others"],"content":"出处：PConline [2007-01-10]　作者：xbeta\n***　编者按：**“工作之余推广免费软件的IT志愿者”xbeta今天带来300款免费软件，并花了一番功夫加上了自己的注释。本文的链接较多，几乎都是链到对应软件的官方网页。推荐先读完全文，再去下载，免得过多的英文网页把头搞晕。*\n国外winaddons网站选出了300个顶级的自由软件（TOP 300 Freeware），一时间国内转载颇多。但所谓三思而后行，本人在转载到善用佳软之前，先考查了一下，发现三个疑问：\n①真是300个软件？这个问题意义不大，不过，既然有方便的工具，不数一下真是浪费。经计算，实为296个。（如何计算，用过VIM7.0的一看就明白了:%s/-//gn。谁还有更好方法，不妨分享一下）当然了，再加几个想来不难，但并无必要。\n②某些软件因为时代原因（比如离线浏览器），我认为收录意义不大；还有一些软件好象不太知名（虽然我用过的有限，但也常逛国外共享软件下载站点）或质量一般，或并不一定适于中国人应用，应该注明一下，以与真正的顶级软件进行区分。\n③一些国产软件，因为老外不了解，所以未做收录。值得做一个提醒，或与同类软件简单对比一下。\n总之，来一番点评，更有助于普通用户了解它们，应用它们；更有助于真正的推广免费，减少盗版。点评的原则：实用、负责、简短。某些软件，用过多年，心得很多；某些软件，虽未常用，但关注以久。这两种情况，将多做点评。对于不了解的，也无须google（这事读者自己会做）“知之为知之，不知为不知”，这才是做事之道。能减少一点盗版行为和盗版习惯，则深感欣慰。\nOffice/办公类\n说明两点：一是为何把这类软件列于首位？xbeta认为，只有这类软件才是计算机普及之初的目的，也是最能直接造生产力的工具。说得再明白点，常用这些软件至少证明你在工作。而常用一些下载工具、音频视频，多是娱乐一类，毕竟不是长久之道。二是这个Office是广义概念，指与办公（或公务、工作）相关的各类工具，不局限于MS Office、OpenOffice包括的内容（也就是字处理+表格+演示）。\n**OpenOffice -**办公套件。这是第一类中的第一个，当之无愧。它简称OOo，可谓全球范围内最好的开源/免费办公套件。\nxbeta处理公务用MS Office，但其他情况都用OOo。简言之，\n原因1，bug少一些；\n原因2，开放而内容独立实为zip的文档格式；\n原因3，喜爱并支持自由软件。详见[再度启用\nOpenOffice！及几项原则](http://blog.sina.com.cn/u/46dac66f010002or)。作为最最推荐的软件，也是名列第一的软件，附截屏（截屏为英文，而OOo有中文版本；截屏为处理\nhtm，而OOo可处理文字、电子表格、演示，还有一个画图），更多介绍见这里。 还有一个国内的OOo论坛专版，在 http://ooo.yjrg.net\n图1 大名鼎鼎的OOo\nPC Suite 602 - 办公套件。在国内几乎没什么影响。国外呢？也比上面的OOo差很多，不推荐。AbiWord - 字处理类。作为文字处理工具，也就是替代你盗版的word，也算不错。中文貌似有问题，实际上设一下字体就可以了。不太推荐。\nAtlantis Nova -字处理工具。这个没用过，不推荐。\n【小结】上面4种是狭义的Office软件，也就是替换盗版MS\nOffice的选择。我的建议是：用OOo或WPS。没错，国产的WPS真是不错，可惜早期的推广工作出现重大失误，现在只好投向MS\nOffice。\nMicrosoft PowerPoint Viewer - 播放MS ppt文件的，其实除了ppt，MS还出了word和visio对应的viewer，都是免费。顺便提一下，MS Project没有对应的免费reader，xbeta找了一些免费软件替代方案供参考。\nAdobe Reader - pdf阅读器。不用说了，自家出品，最稳定，最兼容。当然了，体积也最大。不过，很多年未装未用了，原因就是下面的foxit。\nFoxit PDF Reader xbeta的口号是，抛弃Adobe Reader庞然大物，换用foxit轻盈掌上舞。貌似国外软件，实为国人出品，体积不足3MB，启动快如闪电，运用方便免安装，详细介绍见这里。\nPDFCreator 想把任何可打印的文档“打印”成pdf吗？当然了，如果你知道PDFFactory，那是一个极其优秀的软件，标准版约500元人民币。想免费，\nPDFCreator就是最佳选择。称之为全球影响最大、用户最多的免费pdf制作方案，一点不为过。还带有多种加密安全选项，可多文档合为一个pdf，详细介绍见这里。\n【小结】上面3种都是pdf相关软件，要了解pdf更多内容，这篇文章不可不读：PDF转之有道：制作\npdf的软件总结。鉴于经常有人问到pdf相关问题，所以粗略明确一下：pdf通常不是做出来的，而是转换出来的；它易读，但不易再加工；凡是能打印的文件，都很容易转成\npdf；pdf转回其他格式或原格式（比如word），那是相当难；word转pdf，其实用OOo和WPS都可以（并且比打印方式更好）。\nDoc Convertor -文档格式转换，不关心，未用过。\nConvert -度量单位转换，未用过。下面一个也是。其实，用google或baidu不就可以了吗？\nConverber - 同上\nSunbird -日程安排，出自Mozilla家族，也就是大名鼎鼎的Firefox和Thunderbird的同门小弟。不过影响小很多，经常会把它用作\nThunderbird的扩展。如果单独安装，你多半会埋怨它体积大而功能不够多。\nEssentialPIM Free -国外屡获大奖，同类中名列前茅的作品。免费版的功能也不少，基本够用了。与上一个相比，尤显小巧可爱。值得一试。\nPhraseExpress - 未用过，“提高打字速度”？早期或许有些市场，但现在的中文输入法早就有这样的功能了。据xbeta所知，很多热键类工具也有此功能。\nATnotes - 桌面记事贴类。此类软件多如牛毛，但谁能经久不衰？谁称经典？谁为传奇？那就是ATnotes。\nArchive managers/文件管理\n7-Zip - 无须多说，如果你的winzip和winrar是盗版的，请卸载它们，装上更加小巧（1M左右），不会有版权问题的7-zip。在xbeta近几年所用的计算机上，winzip和winrar从来未出现过，详见不用Winrar Winzip，用7zip（附一个右键菜单技巧）。下面的软件也属此类，但有的体积太大，有的界面不太习惯，有的发展前途不如开源的7-zip有保证，所以，都未做介绍。\n图2 免费开源的7-Zip\nIZArc - 压缩工具\nTugZIP-压缩工具\nCabPack - 压缩工具\nUniversal Extractor -从多种文件类型中解压\n【小结】：大多数正经网站的下载包都是zip格式，就算是rar也不会在注释里写内容。我给别人发文件，只用zip。\nInternet/互联网\nFirefox 不用介绍了吧，相关文章太多，没用过也该听说过。目前xbeta的当家浏览器。如果你觉得IE不安全（很可能是你的浏览习惯不安全），那你换\nFirefox试试。它有便携版的，见一个portable apps网站。喜欢浏览器试新的，经常会在Firefox、IE（及shell）、Opera间迷茫，我的意见：首先是个人需求不同，每种产品的细节不同，这导致了别人的结论仅供参考；其次是，由于它们的可配置性，只有用过半年以上才会得出较为客观的结论；最后才是它们在一般意义上的区别。区别为：不爱折腾求兼容，选IE（及shell）；喜潮流、有开源情节、功能单纯情节，选Firefox；求小巧、功能多而集成、个性靠配置，选Opera。\n图3 备受追捧的Firefox\nInternet Explorer -如果喜欢它，可称之为免费。如果不喜欢它，可称为流氓插件：你装Windows，就得装IE。\nMaxthon -国人的骄傲，不仅指产品（很多国产品都很不错），更指它的商业模式和发展前景。当年我曾不舍Netcaptor，现在认为Maxthon全面胜出。但是，谁让它基于IE呢？所以自改用Firefox后Maxthon就退居二线了。提醒新手：Maxthon不是浏览器，而是浏览器的Shell。\nOpera - 原为共享（不注册带广告）后免费。\nAvant Browser - IE Shell。与Maxthon、GreenBrowser一样，它也属changeyou系列。从推广来看，有主攻国外的（比如这个），有国内知名的（如\nGreenBrowser），有内外兼修的（当然是Maxthon大赢家），各有不同，因此人气不同。但从产品自身讲，都属此类顶级，但很难说谁是最好。可参见“IE maxthon FireFox等　我用过的浏览器（及外壳）”。\nThunderbird - 邮件客户端。xbeta目前在用。不过客观说来，它在邮件客户端中的地位，比起Firefox之于浏览器，差很多。参见推荐的Thunderbird扩展。xbeta应用邮件客户端的经验参见“哪款邮件客户端最好？之一、之二、之三、之四。”\nPopTray - 检测信箱工具。没用过，不信它比SimpleCheck、nPOP、mmm更好。\nFree Download Manager -下载工具。总体上xbeta很少用此类软件，这一款用过一段时间。但试过之后发现，下载软件中国人做得最好。所以，机器上保留的下载工具是Netants。（别嘲笑我）。\nFlashGet - 下载工具，谁用谁知道。\nWellGet - 下载工具。\nDownload Master - 下载工具。\nWGET -命令行式的下载工具，应该是很牛的软件。\nHTTrack - 离线浏览器。我本来认为这种工具在当今没什么用处，有读者补充说，实际上可以作为网站下载工具，比如下载论坛精华区等。\nWebReaper - 同上。\nYeah Reader - RSS reader。目前xbeta用Firefox+Sage实现rss浏览，偶尔也用“抓虾”（善用佳软在zhuaxia有300多订阅了，受此虚荣心推动，所以偏爱zhuaxia）\nGreatNews - RSS reader\nRSSOwl - RSS reader\n【小结】网络，可用来娱乐或工作。对上面软件挑剔的，多数是前者。呵呵\nP2P/点点传输\nμTorrent - xbeta注：基本不用此类软件，但对此一款有很深的好感。那叫一个小巧啊……参见《uTorrent：史上最省资源BT客户端》\nAzureus - torrent client\nBitComet - 大家都了解吧？不了解也没办法，没用过，好象中国用户较多。八卦一下，前一段出事了，呵呵，骗人被封，见BitTornado封杀BitComet用户。\nABC - torrent client\nBitTornado - torrent client，上面对BitComet下手的就是它。\neMule - p2p client\nSoulSeek - p2p client\nShareaza - p2p client\nDC++ - Direct Connect network client\nPeerGuardian - IP blocker\n【小结】点对点传输，BT，传的都是啥？以前看过一句话：入世后，既享受降价的汽车，又继续看盗版片——太贪心了吧？\nChat/聊天\nMiranda - 聊天客户端。Miranda不列第一，谁敢列第一？！体积那是相当小巧，1M就能用。界面，那是相当可以定制。协议，这才是真正的强项，开一个Miranda，可以和MSN、yahoo、QQ、ICQ等客户端互通，强吧？\nSN Messenger -聊天客户端。学生用QQ，上班了就用MSN。\nahoo Messenger -聊天客户端。去年实现了与MSN Messenger互联互通了。\nQIP - 聊天客户端。\nGaim - 聊天客户端。与Miranda功能类似，但跨平台，体积稍大。\nJAJC - 聊天客户端。\nHydraIRC - IRC客户端。IRC好象是技术人员常用的，xbeta从未用过。\nTalkative IRC - IRC 客户端。\nIceChat - IRC 客户端。\nSkype - VOIP 客户端。\nGoogle Talk - VOIP 客户端。\nVoipStunt - VOIP 客户端。\nGizmo - VOIP 客户端。\nWengo - VOIP 客户端。\n【小结】：用什么聊天工具，不是取决于产品和你的口味，主要取决于你要沟通的人群用什么。MSN Messenger与QQ就是最好的例子。\n下转【转】点评“最好的300款免费软件”(2)\n","date":"2007-01-24","description":"","lastmod":"2007-01-24T15:05:14Z","slug":"xbeta-freeware300-1","tags":["freeware","software","xbeta"],"title":"【转】点评“最好的300款免费软件”(1)","url":"https://blog.zengrong.net/post/xbeta-freeware300-1/"},{"categories":["technology"],"content":"本文是ActionScript3 Tips and Tricks系列阅读笔记之一New Variable Types，这里是原文地址。\n新的变量类型\nActionScript3支持更多的变量类型，基本的类型包括：\n简单类型:\n* Boolean\n* int\n* null\n* Number\n* String\n* uint\n* undefined\n合成类型:\n* Object\n* Array\n* Date\n* Error\n* Function\n* RegExp\n* XML\n* XMLList\n其他的类型涉及它们的类，例如：Matrix (flash.geom.Matrix), Shape (flash.display.Shape), URLRequest (flash.net.URLRequest)等等。\n一些提示：\nVoid类型在AS3中改为小写（是void，不再是Void） 用*来表示任意类型。例如：var anything:*; XML类型与ActionScript1、2中的定义不同了，旧的XML类型现在定义为XMLObject。XML目前基于E4X对象。 int和uint是两个新的简单数字类型，分别针对整数和非负整数（正整数和0）。它们多被用在需要十进制数值的地方（例如循环步进值）。int类型可以用在大多数需要数字的地方，而uint类型只能用在诸如颜色值这类必要的地方。 ==================================================\nNew Variable Types\nActionScript 3 supports a wide range of variable types including some which were not present in previous versions of ActionScript. Basic types for AS3 include:\nPrimitive:\n* Boolean\n* int\n* null\n* Number\n* String\n* uint\n* undefined\nComplex:\n* Object\n* Array\n* Date\n* Error\n* Function\n* RegExp\n* XML\n* XMLList\nAdditional types also exist that relate to their classes; ex: Matrix (flash.geom.Matrix), Shape (flash.display.Shape), URLRequest (flash.net.URLRequest), etc.\nThings to note:\n* The special Void type has changed in AS3 to be lowercase (void not Void)\n* There's a new * type that is used to represent any data type. This should be used instead of ommitting typing information for your variables.\nActionScript Code:\nvar anything:*;\n* The XML type is not the same as the XML type in ActionScript 1 and 2. The old XML type (object) is now defined as XMLObject. XML now references the new E4X-based XML object.\n* int and uint are new primitive number data types for integer (numbers without decimal values) and unsigned integer (numbers without decimal values that also cannot be negative). These can be useful for values which are not supposed to have decimal values such as loop iterators. The int data type will provide a small perfomance boost when used over Number in most cases, but uint should only be used when necessary such as with color values.\n","date":"2007-01-11","description":"","lastmod":"2007-01-11T15:56:02Z","slug":"new-variable-types","tags":["as3","tipsandtricks"],"title":"新的变量类型-ActionScript3 Tips and Tricks","url":"https://blog.zengrong.net/post/new-variable-types/"},{"categories":["technology"],"content":"本文是ActionScript3 Tips and Tricks系列阅读笔记之一Graphics Object and the Drawing API，这里是原文地址。\n图形对象和绘图API\n就像ActionScript1和2一样，ActionScript3也有绘图API，你可以使用它们在影片剪辑和sprite对象中绘制矢量线条或图形。使用ActionScript3，绘图API总是在显示对象（例如影片剪辑、sprite等等）的graphics属性（flash.display.Graphics）中使用。graphics属性表现为绘图API绘制出的一个动态层。就像以前一样，这个动态层位于绘图目标对象的所有的子对象之下。在ActionScript3中，提供了一些新的方法让你绘制矩形、圆形以及圆角矩形变得更加容易。它们包括：\ndrawCircle(x:Number, y:Number, radius:Number):void drawEllipse(x:Number, y:Number, width:Number, height:Number):void drawRect(x:Number, y:Number, width:Number, height:Number):void drawRoundRect(x:Number, y:Number, width:Number, height:Number, ellipseWidth:Number, ellipseHeight:Number):void 范例：\n1// draw a blue rounded rectangle: 2var square:Sprite = new Sprite(); 3square.graphics.beginFill(0xFF); 4square.graphics.drawRoundRect(0, 0, 100, 50, 10, 10); 5square.graphics.endFill(); 6addChild(square); 原文如下：\nGraphics Object and the Drawing API\nLike ActionScript 1 and 2, ActionScript 3 also has a drawing API that allows you to draw vector lines and shapes dynamically in movie clips and sprites. With ActionScript 3, however, the drawing API is now used off of an object within display objects (movie clips, sprites, etc.)\ndefined as graphics\n(flash.display.Graphics).\nThis graphics property represents the dynamic drawing layer where drawing API drawings exist. Like before, it is placed below all children of the target object. Also, in ActionScript 3, you have new methods that help you more easily create rectangles, circles, and even rounded rectangles. These include:\n以下内容同上。\n","date":"2007-01-10","description":"","lastmod":"2007-01-10T15:30:09Z","slug":"graphics-object-and-the-drawing-api","tags":["as3","display-objects","tipsandtricks"],"title":"图形对象和绘图API-ActionScript3 Tips and Tricks","url":"https://blog.zengrong.net/post/graphics-object-and-the-drawing-api/"},{"categories":["technology"],"content":"本文是ActionScript3 Tips and Tricks系列阅读笔记之一Class scope is now bound to class methods，这里是原文地址。\n类的活动范围已经绑定到类方法(Class scope is now bound to class methods)\n（也不知标题这么翻译是否正确，应该大致是这个意思把，简单的说，这个技巧就是讲解this的引用的问题。ActionScript2中，如果位于某个对象的事件处理函数中，this就指向发出这个事件的对象，而在ActionScript3中，this始终都指向类）\nActionScript3是完全基于类的。当你创建类时，你创建的变量和函数（方法）对这个类以及实例起作用。与ActionScript2不同，方法在被调用的时候保留它所属的类的活动范围，即使这个方法被指派给另一个对象并从另一个对象调用，或者使用Function.call和Function.apply也是如此。范例如下：\n（上面一段翻译很可能不准，还是看代码和原文更好）\n1package { 2import flash.display.Sprite; 3 4public class ClassScope extends Sprite { 5public function ClassScope() { 6traceThis(); // \u0026#34;Class Instance\u0026#34; 7var obj:Object = new Object(); 8obj.traceThis = traceThis; 9obj.traceThis(); // \u0026#34;Class Instance\u0026#34; 10traceThis.call(new Sprite()); // \u0026#34;Class Instance\u0026#34; 11} 12public override function toString():String { 13return \u0026#34;Class Instance\u0026#34;; 14} 15public function traceThis():void { 16trace(this); 17} 18} 19} 原文如下：\nClass scope is now bound to class methods\nActionScript 3 is entirely class-based. When you create classes, you create variables and functions (methods) which relate to and work with that class and instances of that class. Unlike ActionScript 2, methods in ActionScript 3 now retain their class scope when called, even if assigned to another object and called from that object, or if used with Function.call and Function.apply. Example:\nActionScript Code:(同上)\n","date":"2007-01-09","description":"","lastmod":"2007-01-09T15:20:07Z","slug":"class-scope-is-now-bound-to-class-methods","tags":["as3","tipsandtricks"],"title":"类的活动范围已经绑定到类方法-ActionScript3 Tips and Tricks","url":"https://blog.zengrong.net/post/class-scope-is-now-bound-to-class-methods/"},{"categories":["technology"],"content":"由于ActionScript 3 Tip of the Day(ActionScript3 Tips and Tricks)一文原帖地址实在太慢，准备从今天起，慢慢将原帖转到Blog上，以免以后哪天原帖不能访问了，我还可以拿来参考。\n本文是ActionScript3 Tips and Tricks系列阅读笔记之一Change the frame rate of your movie，这里是原文地址。\n实时改变影片帧频(Change the frame rate of your movie)\n使用ActionScript3中的Stage类，可以动态改变影片的帧频。\nStage类可以通过主影片中sprite或影片剪辑的stage属性访问（也可以通过位于同一安全沙箱其他影片中的相关属性访问）。stage对象有一个frameRate属性，可以包含0.1至1000之间的任何值。改变这个属性的值，可以让Flash Player实时改变被播放的影片的帧频。\n1// 改变帧频到12帧/秒 2stage.frameRate = 12; 原文内容：\nUsing ActionScript 3, you can dynamically change the frame rate of your movie using the Stage class.\nThe Stage class (flash.display.Stage) is the class assigned to the stage object which is accessible from your main movie sprite/movie clip (or others within the same security sandbox) using the stage property. The stage object has a frameRate property which can contain any value between 0.01 and 1000 and determines the frame rate at which the Flash player plays back your movie. Changing this value lets you change the frame rate at runtime.\n1// change frame rate to 12 fps: 2stage.frameRate = 12; 原文地址\n","date":"2007-01-09","description":"","lastmod":"2007-01-09T15:02:50Z","slug":"actionscript3-tip1","tags":["as3","general","tipsandtricks"],"title":"实时改变影片帧频-ActionScript 3 Tips and Tricks","url":"https://blog.zengrong.net/post/actionscript3-tip1/"},{"categories":["technology"],"content":"Beginners Guide to Getting Started with AS3 (Without Learning Flex)\n这是篇相当不错的文章，介绍如何开始学习ActionScript3，其中包括如何配置ActionScript3环境，如何把ActionScript3类编译成swf文件等等。\n网速慢的朋友，可以在这里下载整篇文章以及文章中提供的范例：Beginners_Guide_to_Getting_Started.zip\n","date":"2007-01-07","description":"","lastmod":"2007-01-07T13:00:13Z","slug":"beginners-guide-to-getting-started-with-as3","tags":["as3"],"title":"开始AS3（不需要学习Flex）","url":"https://blog.zengrong.net/post/beginners-guide-to-getting-started-with-as3/"},{"categories":["technology"],"content":"为了写一个纯ActionScript3项目，在google中翻找[SWF] metadata的详细用法，结果找到了这个好东东：\nActionScript 3 Tip of the Day\n粗略看了一下，里面所写的技巧都非常有用，留待以后慢慢看。\n在第59个tip中提到：\nThe SWF metadata tag supports 4 properties\n* width\n* height\n* frameRate\n* backgroundColor\n难道仅仅支持4个属性么？那么“Description”、“Title”属性都不支持么？没时间考证了，反正用编译参数也是可以支持这些属性的。\n","date":"2007-01-07","description":"","lastmod":"2007-01-07T12:37:02Z","slug":"actionscript-3-tip-of-the-day","tags":["as3","tipsandtricks"],"title":"ActionScript 3 Tip of the Day","url":"https://blog.zengrong.net/post/actionscript-3-tip-of-the-day/"},{"categories":["others"],"content":"本文转自网谈 Toptenreviews 已经发布了2007年度的世界杀毒软件排名，与2006年名单相比，这两个排名几乎没有什么区别。也许你会注意到其中惟一的区别是第九名由原来的 eTrust EZ Antivirus 变成了 CA Antivirus，但这两个不同的名字其实还是同一款产品，或许只是因为 CA 公司认为 CA Antivirus 记起来方便得多，于是改了个名字而已。所以，这两个名单还是完全一致的。\n2007 年世界顶级杀毒软件排名：\n金奖：BitDefender\nBitDefender 杀毒软件是来自罗马尼亚的老牌杀毒软件，二十四万超大病毒库，它将为你的计算机提供最大的保护，具有功能强大的反病毒引擎以及互联网过滤技术，为你提供即时信息保护功能，通过回答几个简单的问题，你就可以方便的进行安装，并且支持在线升级。\n它包括：1. 永久的防病毒保护；2. 后台扫描与网络防火墙；3. 保密控制；4. 自动快速升级模块；5. 创建计划任务；6. 病毒隔离区。\n银奖：Kaspersky\nKaspersky （卡巴斯基）杀毒软件来源于俄罗斯，是世界上最优秀、最顶级的网络杀毒软件，查杀病毒性能远高于同类产品。卡巴斯基杀毒软件具有超强的中心管理和杀毒能力，能真正实现带毒杀毒！提供了一个广泛的抗病毒解决方案。它提供了所有类型的抗病毒防护：抗病毒扫描仪、监控器、行为阻段、完全检验、E-mail 通路和防火墙。它支持几乎是所有的普通操作系统。卡巴斯基控制所有可能的病毒进入端口，它强大的功能和局部灵活性以及网络管理工具为自动信息搜索、中央安装和病毒防护控制提供最大的便利和最少的时间来建构你的抗病毒分离墙。卡巴斯基抗病毒软件有许多国际研究机构、中立测试实验室和 IT 出版机构的证书，确认了卡巴斯基具有汇集行业最高水准的突出品质。\n铜奖：F-Secure Anti-Virus\n来自 Linux 的故乡芬兰的杀毒软件，集合 AVP、LIBRA、ORION、DRACO 四套杀毒引擎,其中一个就是 Kaspersky 的杀毒内核，而且青出于蓝胜于蓝，个人感觉杀毒效率比 Kaspersky 要好，该软件采用分布式防火墙技术，对网络流行病毒尤其有效。在《PC Utilites》评测中超过 Kaspersky 名列第一，但后来 Kaspersky 增加了扩展病毒库，反超 F-secure。鉴于普通用户用不到扩展病毒库，因此 F-secure 还是普通用户很不错的一个选择。F-Secure AntiVirus 是一款功能强大的实时病毒监测和防护系统，支持所有的 Windows 平台，它集成了多个病毒监测引擎，如果其中一个发生遗漏，就会有另一个去监测。可单一扫描硬盘或是一个文件夹或文件，软件更提供密码的保护性，并提供病毒的信息。\n第四名：PC-cillin\n趋势科技网络安全个人版集成了包括个人防火墙、防病毒、防垃圾邮件等功能于一体，最大限度地提供对桌面机的保护并不需要用户进行过多的操作。在用户日常使用及上网浏览时，进行\u0026quot;实时的安全防御监控\u0026quot;；内置的防火墙不仅更方便您使用因地制宜的设定，\u0026quot;专业主控式个人防火墙\u0026quot;及\u0026quot;木马程序损害清除还原技术\u0026quot;的双重保障还可以拒绝各类黑客程序对计算机的访问请求；趋势科技全新研发的病毒阻隔技术，包含\u0026quot;主动式防毒应变系统\u0026quot;以及\u0026quot;病毒扫瞄逻辑分析技术\u0026quot;不仅能够精准侦测病毒藏匿与化身并予以彻底清除外，还能针对特定变种病毒进行封锁与阻隔，让病毒再无可趁之机；强有力的\u0026quot;垃圾邮件过滤功能\u0026quot;为您全面封锁不请自来的垃圾邮件。\n趋势科技网络安全个人版的诸多功能确保您的电脑系统运作正常，从此摆脱病毒感染的恶梦。\n第五名：ESET Nod32\n国外很权威的防病毒软件评测给了 NOD32 很高的分数，在全球共获得超过 40 多个奖项，包括Virus Bulletin、PC Magazine、ICSA、Checkmark认证等，更加是全球唯一通过 26 次 VB 100% 测试的防毒软件，高据众产品之榜首！产品线很长，从 DOS、Windows 9x/Me、Windows NT/XP/2000，到Novell Netware Server、Linux、BSD 等，都有提供。可以对邮件进行实时监测，占用内存资源较少，清除病毒的速度效果都令人满意。\n第六名：McAfee VirusScan\n全球最畅销的杀毒软件之一，McAfee 防毒软件，除了操作介面更新外，也将该公司的 WebScanX 功能合在一起，增加了许多新功能！除了帮你侦测和清除病毒，它还有 VShield 自动监视系统，会常驻在 System Tray，当你从磁盘、网络上、E-mail 夹文件中开启文件时便会自动侦测文件的安全性，若文件内含病毒，便会立即警告，并作适当的处理，而且支持鼠标右键的快速选单功能，并可使用密码将个人的设定锁住让别人无法乱改你的设定。\n第七名：Norton AntiVirus\nNorton AntiVirus 是一套强而有力的防毒软件，它可帮你侦测上万种已知和未知的病毒，并且每当开机时，自动防护便会常驻在 System Tray，当你从磁盘、网路上、E-mail 夹档中开启档案时便会自动侦测档案的安全性，若档案内含病毒，便会立即警告，并作适当的处理。另外它还附有 LiveUpdate 的功能，可帮你自动连上 Symantec 的 FTP Server 下载最新的病毒码，於下载完后自动完成安装更新的动作。\n第八名：AVG Anti-Virus\nAVG Anti-Virus 欧洲有名的杀毒软件，AVG Anti-Virus System 功能上相当完整，可即时对任何存取文件侦测，防止电脑病毒感染；可对电子邮件和附加文件进行扫瞄，防止电脑病毒透过电子邮件和附加文件传播； \u0026quot;病毒资料库\u0026quot;里面则记录了一些电脑病毒的特性和发作日期等相关资讯；\u0026quot;开机保护\u0026quot;可在电脑开机时侦测开机型病毒，防止开机型病毒感染。在扫毒方面，可扫瞄磁碟片、硬盘、光盘机外，也可对网络磁碟进行扫瞄。在扫瞄时也可只对磁碟片、硬盘、光盘机上的某个目录进行扫瞄。可扫瞄文件型病毒、巨集病毒、压缩文件 (支持 ZIP、ARJ、RAR 等压缩文件即时解压缩扫描)。在扫瞄时如发现文件感染病毒时会将感染病毒的文件隔离至 AVG Virus VauIt，待扫瞄完成后在一并解毒。支持在线升级。现在提供了最新的免费版供大家使用，安装之前先去官方网站填个表，从回信中得到一个序列号。AVG Anti-Virus 有三个版本(专业、服务器、免费)，其中有个人非营利使用的免费版本，功能完整，但是仅某部份功能是无法设定的，例如扫毒排程只能每天一次等等。\n第九名：CA Antivirus\n就是反病毒软件 eTrust EZ Antivirus 已经获得了国际计算机安全协会（ICSA:International Computer Security Association）的认证。ICSA 专门负责检测和认证产品对来自病毒及恶意代码的攻击的有效性。CA 公司表示，在 ICSA 的测试中，CA Antivirus 软件甚至连\u0026quot;In-The-Wild\u0026quot;恶性病毒也可以 100% 地检测出来。\nCA Antivirus 是一种主要为中小型企业及 SOHO 用户提供解决方案的反病毒软件。该产品支持的操作系统包括 Windows 98、Windows ME、Windows NT 以及 Windows 2000 Professional 等。除此以外，CA 公司还提供包括 CA Antivirus 在内的反病毒解决方案组件\u0026quot;eTrust EZ Armor\u0026quot;。新版本采用全新用户界面，更加易于使用；新的文件隔离功能可有效防止系统文件被误删；改进了帮助系统；增强了\u0026quot;闪动\u0026quot;系统托盘图标功能。\n第十名：Norman Virus Control\nNorman Virus Control 是欧洲名牌杀毒软件，为了确保您的计算机系统得到最好的保护，Norman 数据安全系统提供了多种防毒工具供您选择，以满足您的不同需要。此产品结合了先进的病毒扫描引擎、启发式分析技术以及宏验证技术，可有效查杀已知和未知病毒。NVC 可以查杀所有类型的病毒，包括文件和引导扇区病毒而无需使用杀毒软件重新启动开机。\n第十一名：AntiVirusKi\nAntiVirusKit 2006 是德国G-Data公司产品，英文全名是 GData AntiVirusKit，简称 AVK，这是一款采用 KAV 和 BitDefender(BD) 罗马尼亚杀毒软件的双引擎杀毒软件，具有超强的杀毒能力，在国外拥有非常高的知名度，运行速度稳定，具有病毒监控、EMAIL 病毒拦截器、EMAIL 防护、支持在线自动更新等功能，可以阻挡来自互联网的病毒、蠕虫、黑客后门、特洛伊木马、拨号程序、广告软件、间谍软件等所有威胁，支持对压缩文件、电子邮件即时扫描，支持启发式病毒扫描，支持密码保护，有详细的日志方便查询，对计算机提供永久安全防护。AVK 最大优点是，只要病毒或木马录入病毒库，它在病毒运行前拦截，不会出现中毒后再杀毒的情况。 AVK2006 目前病毒库已经超过 33W 卡吧 / BD 双杀毒引擎效果绝对一流！\n第十二名：AVAST\n第十三名：Panda Titanium\n第十四名：F-Protv\n第十五名：PCTools AntiVirus\n第十六名：ViRobot Expert\n第十七名：WinAntiVirus\n","date":"2007-01-03","description":"","lastmod":"2007-01-03T14:03:53Z","slug":"anti-virus-software-review","tags":["antivirus"],"title":"【转】2007年度的世界杀毒软件排名","url":"https://blog.zengrong.net/post/anti-virus-software-review/"},{"categories":["technology"],"content":"控制载入的外部影片 在学习Recipe 6.7. Loading and Interacting with External Movies 一节时发现问题，如果按照书中所讲，在Event.INIT事件时就可以使用_loader.content来引用载入的影片了。但是，我在运行的时候却发生了这样的错误：\nTypeError: Error #1009: 无法访问空对象引用的属性或方法。\nat LoadExternalMovie/::onMovieInit()\n这也就意味着，在INIT事件发生的时候，_loader.content并没有初始化成功。本以为是书中的代码有错，就使用了Timer类来进行延迟处理，1秒钟之后再调用_loader.content与影片进行交互。虽然是成功了，但总感觉有些不对，书中的代码应该不会有错。后来仔细检查才发现，在注册事件的时候，我误将_loader.contentLoaderInfo写成了_loader.loaderInfo。前者来自Loader类，而后者继承自DisplayObject类。虽然后者也会触发INIT事件，却与被载入的影片无关，自然，当INIT发生的时候，_loader.content是没有准备好的，出错也是理所当然了。\n除了这点，还有三点需要注意的：\n本节中所讲的控制载入影片的方法仅适用与ActionScript3编写的swf\n要调用外部载入的影片的方法，需要将_loader.content保存在类型生命为*****的实例中，否则会出现运行时错误。\n我感觉这就好像在ActionScript2中将不确定类型的值设置为Object类型以规避编译器的类型检验。只不过ActionScript2时是在规避编译错误，而这是规避运行时的错误罢了。\n在试图调用外部影片中本来不存在的方法或属性时，会抛出ReferenceError异常\n","date":"2007-01-02","description":"","lastmod":"2007-01-02T15:43:15Z","slug":"as3-cookbook-63","tags":["as3","cookbook","note"],"title":"《ActionScript 3.0 Cookbook》读书笔记6.3-控制载入的外部影片","url":"https://blog.zengrong.net/post/as3-cookbook-63/"},{"categories":["technology"],"content":"SimpleButton SimpleButton，相当于Flash中的按钮元件。在Flash中，它有“up”、“over”、“down”和“hit”四种状态，“hit”状态是用来控制按钮的可点击区域的。而在ActionScript 3中，SimpleButton类的实例同样有四种状态“upState”、“overState”、“downState”和“hitTestState”。\n前面的三种状态都很好理解，正好和按钮元件的前三种状态对应，只需要将DisplayObject指定给它们即可。而“hitTestState”状态，则是让我郁闷了一下。\n按照Flash中的原理，按钮的四种状态中，其实仅仅设置up状态，按钮就可以工作。但是在SimpleButton中，这个惯例行不通。如果不设定hitTestState状态，按钮根本不响应。\n因此，要使用SimpleButton，必须至少设定两种状态，而hitTestState状态是必须设定的。而实际上，要实现一个正常的按钮效果，四种状态都必须设定。因为如果某个状态不被设定，当按钮显示这种状态时，按钮中就没有任何内容。\n可以用下面的代码做个实验，注释10-13行中不需要状态，然后编译看效果：\n1package { 2 import flash.display.SimpleButton; 3 import flash.display.Sprite; 4 import flash.display.Shape; 5 import flash.events.MouseEvent; 6 7 public class SimpleButtonSample extends Sprite{ 8 public function SimpleButtonSample(){ 9 var __button:SimpleButton = new SimpleButton(); 10 __button.upState = createCircle(0xff0000, 50); 11 //__button.downState = createCircle(0x00ff00, 50); 12 //__button.overState = createCircle(0x0000ff, 50); 13 __button.hitTestState = createCircle(0x000000, 100); 14 15 __button.x = 100; 16 __button.y = 100; 17 18 __button.addEventListener(MouseEvent.CLICK, onClickHandler); 19 20 addChild(__button); 21 } 22 23 public function createCircle(color:uint, radius:Number):Shape{ 24 var __shape:Shape = new Shape(); 25 __shape.graphics.beginFill(color); 26 __shape.graphics.drawCircle(0, 0, radius); 27 __shape.graphics.endFill(); 28 return __shape; 29 } 30 31 private function onClickHandler(evt:MouseEvent):void{ 32 trace(\u0026#34;按下了按钮！\u0026#34; + evt); 33 } 34 } 35} ","date":"2007-01-02","description":"","lastmod":"2007-01-02T12:09:29Z","slug":"actionscript3-cookbook-62","tags":["as3","cookbook","note"],"title":"《ActionScript 3.0 Cookbook》读书笔记6.2-SimpleButton","url":"https://blog.zengrong.net/post/actionscript3-cookbook-62/"},{"categories":["technology"],"content":"由于工作太忙，2-5章的内容又相对简单，因此一直没有来得及整理学习笔记，现在正在学习第6章，因此笔记也从这里开始。前面的笔记，有时间再补上。\nDisplay List ActionScript3的Display List是我很感兴趣的一部分内容，它完全改变了ActionScript1和ActionScript2的层级结构。确实，新的层级结构非常清晰，更加便于使用。\nFlash Player 9由两个部分组成：ActionScript虚拟机（AVM）和渲染引擎。要显示内容，先由AVM创建显示对象、再由渲染引擎将其显示在屏幕上。\n例如，要将一个文本框显示在屏幕上，先使用new关键字来创建TextField类的实例，这个工作是由AVM处理；然后用addChild方法将这个实例显示在屏幕上（由渲染引擎处理）。\nDisplayList是非常灵活的。最上层的容器就是Stage，Stage可以包含显示对象（DisplayObject），也可以包含容器（DisplayObjectContainer），而容器又可以包含显示对象或者子容器。实际上，Stage就是一个容器。\n关于Stage 一个swf文件只有一个Stage。可以使用两种方法来调用它：\nDisplayObjectContainer(stage) 显示对象实例.stage 但无论采取哪种方式调用，无论在哪个显示对象实例上调用，它们所代表的Stage都是同一个Stage。\n关于深度管理 ActionScript3的深度管理是自动的。和ActionScript2不同，在addChild的时候，不需要为加入DisplayList的显示对象制定深度。Depth已经成为历史。\n深度管理器会自动为新添加的显示对象指定一个最高的深度，在新的深度管理中，最低的深度就是0 。深度为0的对象显示在最下面。如果在添加显示对象的时候为其指定了一个深度，而这个深度原来有显示对象，则深度管理器会自动将原来的内容的深度值增加，其他的显示对象深度值则相应地增加。\n深度值只能是正整数和0。\n每个容器的深度值是相对独立的。\n","date":"2006-12-31","description":"","lastmod":"2006-12-31T16:13:31Z","slug":"actionscript3-cookbook-61","tags":["as3","cookbook","note"],"title":"《ActionScript 3.0 Cookbook》读书笔记6.1-关于Display List","url":"https://blog.zengrong.net/post/actionscript3-cookbook-61/"},{"categories":["impressions"],"content":"本文内容来自网络，作者佚名\n读什么书，对一个人的精神成长，实在太重要了。初涉读书界，应该由浅入深，跨入四扇大门：\n第一扇大门是时间之门，也可以说是历史之门。读一读林汉达、曹余章先生的《上下五千年》。这本书从开天辟地的神话，钻木取火的传说到乾隆禁书修书，涉及到中国文明发展史的方方面面。该书编排精心，文笔畅达，可读性强。细细读两遍，对历史便有了一个大致的了解。还可以读一读黄仁宇的《万历十五年》，该书截取历史的一个横断面，清晰地展开历史的画卷，仿佛让你直接触摸历史，具有一种历史的现场感。这对于形成你的历史观，提高你的史识，无疑将十分有益。另外，唐德刚的《袁世凯当国》、《晚清七十年》有独特的观点，使人耳目一新。斯蒂芬·茨威格的《昨日的世界》翻开了世界历史上的最重要一页，他以个人的亲身遭遇写出一个和平的欧洲，是怎样被拖入战争的泥潭的。作者的思想和语言都是一流的，读了该书，你将与他的情感产生强烈的共鸣，那实在是一种高级的精神享受。钱钢的《报告文学选》关注现实中的重大问题，读后我们自然会将现实与历史联系起来，从而历练出一双洞察事物、明辨是非的眼睛。\n第二扇门是空间之门，我推荐美国作家房龙的《地球的故事》。本书向我们介绍了世界各地的天文地理生态环境，乃至由此而形成的人文环境。其立意之高，文笔之妙，联想之奇，材料之详，都堪称一流。每一个读它的人，都将爱不释手。并且会对自己生活的这个地球肃然而起敬意，从而明白，作为同一星球上的伙伴，我们祸福与共，休戚相关！读了这本书“狭隘”将离你远去。房龙的其他著作，如《人类的故事》、《与世界伟人谈心》、《宽容》等书都是值得一读的好书。\n第三扇大门是感情之门。人的感情是需要激发、熏陶的。文学作品最能陶冶人的心灵。美国作家怀特的《夏洛的网》是一本童话。一般人认为“童话”只是给孩子们看的。其实不然。青年人、成年人照样应该读童话，这对培养爱心，留住童心，几乎有不可替代的作用。尤其是过去很少读童话的人，一定要补上这一课。读散文小说也是提高自己情趣、品位、修养的一个途径，读一读胡适杂文、梁实秋的《雅舍小品》、林语堂的《中国人》、茨威格的《人类群星照耀时》、史铁生的小说《随想录》、《丰子恺随笔》。因此，你将更加热爱生活，热爱生命。\n第四扇大门是理性之门。建议读一读《苏菲的世界》，这是一本通俗的哲学通俗读物，问世以来，受到全世界青少年的喜爱，本书通过书信的形式把深奥的哲学问题说得十分浅近，简明易懂，让孩子思考一下生命问题，思考活着是为什么，思考一下如何找到安身立命之所。要提高自己思想的深度，必须读一点哲学。这是一本哲学入门书，也许将来你进一步深造以后，会对这本书的某些观点不赞同，但对于初学者无疑是不可多得的好书。另外，朱光潜关于谈美的书，如《美学书简》，茨威格的《异端的权利》，都对形成你“自由的思想，独立的精神”有极大的帮助。\n这是本文的扫描版：\n","date":"2006-12-26","description":"","lastmod":"2006-12-26T08:44:25Z","slug":"read-book","tags":["reading"],"title":"【转】怎样读好书 请叩开这四扇大门","url":"https://blog.zengrong.net/post/read-book/"},{"categories":["technology"],"content":"要使用外部类，就必须在编译时提供类的路径。在Flash中，可以使用“编辑-首选参数-ActionScript-ActionScript2.0设置”对话框来添加外部类库的路径。而在编译ActionScript 3.0时该如何处理？\n我没有用Flash 9预览版编译过ActionScript 3.0，想来它添加外部类路径的方法大概也和Flash 8一样把。这里所讲的，是如何在Flex Builder 2和mxmlc命令行编译时使用外部类。\nFlex Builder 2 要将外部类路径加入到Flex Builder 2中，使用“Project-Properties”对话框中的ActionScript Build Path项，将包含ActionScript 3.0包的路径加入其中即可。如下图所示。\n加入链接路径后，Navigator面板会加入被链接的目录：\n右键单击这个目录查看它的Properties，可以看到这个目录的链接情况：\n然后再直接使用Flex builder 2编译就可以了。\nmxmlc 在使用mxmlc命令行进行编译的时候，要使用--source-path参数加入类文件夹的路径，并使用--file-specs参数指明要编译的文件，例如：\nE:\\study\\Flash\\as3cookbook\u0026gt;mxmlc --source-path F:\\Material\\ASClasses\\AS3\\ --file-specs MathSample.as ","date":"2006-12-25","description":"","lastmod":"2006-12-25T17:01:38Z","slug":"build-path","tags":["as3","flex","flexbuilder","mxmlc"],"title":"在编译ActionScript3时使用外部类","url":"https://blog.zengrong.net/post/build-path/"},{"categories":["technology"],"content":"管理事件 在ActionScript2.0中，我已经习惯使用addEventListener()来为V2版组件添加事件处理器。同时也习惯了使用EventDispatcher来发布事件。没想到ActionScript 3.0已经把addEventListener作为标准的事件机制了。这真是个好消息。\nActionScript 3.0使用flash.events包把所有的事件类集中在一起。例如，enterFrame事件被放在flash.events.Event类中；鼠标事件被放在flash.events.MouseEvent类中；键盘事件则被放在flash.events.KeyboardEvent类中，等等。\n现在，要使用ActionScript 2.0时代的onEnterFrame事件，需要这么写：\n1//构造类... 2import flash.events.Event; 3//其他代码... 4addEventListener(Event.ENTER_FRAME, onEnterFrame); 5//其他代码... 6private function onEnterFrame(event:Event) { 7//处理代码 8} 当然，事件管理函数名称倒不一定必须是“onEnterFrame”。至于鼠标事件和按键事件，使用方法也都差不多。Adobe把所有的事件都集中在一个包中管理，并且全部用常量来描述事件名称，确实比原来松散的管理方式要方便许多了。\n时间间隔 在ActionScript 1、2时代，对于要按照特定的时间间隔重复的程序，我们似乎只能使用setInterval或onEnterFrame。现在好了，有个更好的东东可以用，那就是Timer类。\n首先导入Timer类并建立Timer类的实例，Timer类在flash.utils包中。下面的程序创建一个每500毫秒重复一次的timer实例，一共重复100次。\n1import flash.utils.Timer; 2var timer:Timer = new Timer(500, 100); Timer类的实例在工作间隔中会发出TIMER事件，通过为这个事件指定事件管理函数，就可以达到按特定事件间隔执行程序的目的。当然，这个事件也在flash.events包中。\n加入事件侦听器后,还需要使用start()方法来启动timer实例的运行。\n1import flash.utils.Timer; 2import flash.events.TimerEvent; 3//其他代码... 4var timer:Timer = new Timer(500, 100); 5timer.addEventListener(TimerEvent.TIMER, onTimer); 6timer.start(); 7//其他代码.. 8private function onTimer(evt:TimerEvent):void{ 9//事件处理代码... 10} 如果希望执行没有次数限制的Timer，则在建立timer实例时将第二个参数设为0即可。要停止timer实例的运行，则可以使用stop()方法。\n尽管Timer类如此好用，但本书的作者还是给了我们两条忠告：\nDon't rely on timers to be extremely precise. Don't rely on timer intervals to be smaller than approximately 10 milliseconds. 10毫秒？我们会用Flash做这么精确的事情么？\n","date":"2006-12-25","description":"","lastmod":"2006-12-25T14:53:16Z","slug":"cookbook1-3","tags":["as3","cookbook","note"],"title":"《ActionScript 3.0 Cookbook》读书笔记1.3","url":"https://blog.zengrong.net/post/cookbook1-3/"},{"categories":["web"],"content":"从网谈那里知道了bbPress，这个由WordPress团队开发的轻量级论坛程序。去中文站看了看，感觉相当不错，中文团队翻译了许多相关文档，为使用程序提供了方便。另外由于和WordPress是一家，bbPress可以和WordPress整合，界面也保持了WordPress的简洁风格。\n不过，目前的程序也有一些问题，例如不支持中文tag等。由于目前我暂时不需要BBS服务，先关注一段时间再说啦。\n","date":"2006-12-25","description":"","lastmod":"2006-12-25T06:31:37Z","slug":"bbpress","tags":["bbpress","cms","wordpress"],"title":"bbPress：轻量级的论坛程序","url":"https://blog.zengrong.net/post/bbpress/"},{"categories":["technology"],"content":"关于trace() 由于不是在Flash IDE中进行编辑，在调试程序过程中，如何获取trace()的值是一个问题。目前主要有两个方法：\n1、利用Debug版本的Flash Player+mm.cfg 首先确认自己的Flash Player是Debug版本，然后用记事本建立一个mm.cfg文件，写入以下内容：\nTraceOutPutFileName=C:\\Documents and Settings\\{your name}\\Application Data\\Macromedia\\Flash Player\\Logs\\flashlog.txt ErrorReportingEnable=1 TraceOutputFileEnable=1 MaxWarnings=0 然后保存在“C:\\Documents and Settings\\{your name}\\”下即可。当在浏览器中打开swf文件时，所有的trace内容都会被写入到flashlog.txt中。\n其中的几个参数的意义分别是这样的：\nTraceOutputFileEnable\n值为1则将trace内容导出到log文件\nTraceOutputFileName\n导出的log文件的路径和文件名，如果不定义，则会将log内容写入到mm.cfg文件相同目录下的flashlog.txt\n注意：如果是Flash Player 9.0.28或更新版本，则log路径必须为下面的内容：\nX:\\Documents and Settings\\{your name}\\Application Data\\Macromedia\\Flash Player\\Logs\\flashlog.txt （将X改为你的系统盘符）\nErrorReportingEnable\n如果值为1，则在导出文件中写入错误报告。默认值为0\nMaxWarnings\n写入到log文件中的最大的错误条数。如果希望不限制错误条数，将其值设置为0即可\n如果不希望如此麻烦，推荐使用Firefox的FlashTracer插件。\n2、使用Flex Builder 2中 将Flex Builder 2的console面板打开，然后Debug项目，当swf导出完成时会自动打开浏览器窗口显示swf文件。这时切换会Flash Builder 2界面，就可以看到trace的内容了。如下图所示\n","date":"2006-12-22","description":"","lastmod":"2006-12-22T03:22:24Z","slug":"as3-cookbook-12","tags":["as3","cookbook","note"],"title":"《ActionScript 3.0 Cookbook》读书笔记1.2-关于trace()","url":"https://blog.zengrong.net/post/as3-cookbook-12/"},{"categories":["others"],"content":"本文转自portableapps.com，由zrong中文化（未完成）。\n下面的所有软件均可免费使用，部分软件还是开源的。\n开发工具 Nvu 便携版 - 易于使用的web编辑器 XAMPP - web服务器整合包，包含Apache, mySQL, PHP, phpMyAdmin 以及更多 游戏 Sudoku 便携版 - 流行的益智游戏 图形图像 GIMP 便携版 - 照片和图像编辑器（类似于Photoshop） 互联网 FileZilla 便携版 - 功能全面的FTP客户端 FireFTP Extension (for Firefox) - 支持火狐（firefox）浏览器的轻量级的FTP插件 Firefox 便携版 - the award-winning web browser that's safe and secure Gaim Portable - chat with AOL, MSN and Yahoo users in an easy-to-use interface Miranda IM Portable - chat with AOL, MSN and Yahoo users in a customizable interface Sage Extension (for Firefox) - A full-featured RSS extension Thunderbird 便携版 - 电子邮件客户端 Nvu 便携版 - 易用的网页编辑器 音频视频 Audacity 便携版 - 一个简单易用的声音编辑和录制软件 VLC Media Player Portable - An easy to use media player that plays most audio and video formats 办公软件 AbiWord 便携版 - 轻量级的电子文档处理软件，可以编辑Microsoft Word格式 Lightning Extension (for Thunderbird) - A lightweight extension for your calendar and tasks OpenOffice.org 便携版 - 文档处理、电子表格、幻灯片和简报处理，支持Microsoft Office文档格式 Sunbird Portable - Calendar and task management with a familiar interface Thunderbird Portable (Address Book) - Email client's built-in address book with import/export functions 操作系统 Mac-on-Stick - Run Mac OS Classic 7 portably 通用软件 7-Zip 便携版 - 文件压缩利器，压缩比高于WinRAR ClamWin 便携版 - 免费杀毒软件 PStart - 快捷程序菜单管理 (非开源) ","date":"2006-12-18","description":"","lastmod":"2006-12-18T02:46:50Z","slug":"portableapps-com","tags":["freeware","opensource","portable","software"],"title":"推荐一组常用 绿色 便携 免费软件","url":"https://blog.zengrong.net/post/portableapps-com/"},{"categories":["technology"],"content":"《ActionScript 3.0 Cookbook》（下载）的确是一本AS3入门的好书，今天开始把学习心得写出来把。\n其他的关于本书的读书心得\n书中目前推荐的学习AS3的平台是使用Flex Builder 2，这是一个非常优秀的开发平台（当然价格也不菲），可以在Adobe官方网站下载试用版。\n如果不使用Flex Builder 2，则可以直接用Flex SDK 2进行开发，它是免费的（Flex Builder 2已包含Flex SDK 2）。\n编译AS3程序可以用Flex SDK 2中的mxmlc，也可以直接用Flex Builder 2编译。不过就我看来，还是用前者比较好，因为我的计算机配置较差（815EPT C1.2G 512MBPC133），在使用Flex Builder 2编译时经常会消耗掉400MB以上的内存，整个系统犹如死掉一样，编译完成之后占用的内存也很难释放。而mxmlc编译的速度明显要快一些，编译完成后会立刻释放占用的内存。\n将as编译成swf，其实很简单，只需要使用下面的语法即可：\n1mxmlc mySampleFile.as 这句代码会在mySampleFile.as同目录下编译出mySamplefile.swf文件。在编译之前，注意将mxmlc所在的路径加入到path环境变量中。\nRecipe 1.2 中讲到，在编译时加上参数可以定义swf的属性。例如，使用下面的命令行进行编译，将得到一个 800*600 大小，背景色为白色、帧率是30帧的swf。\n1mxmlc -default-size 800 600 -default-background-color 0xffffff -default-frame-rate 30 使用元数据，也可以定义swf文件的属性，例如，将下面的元数据写入mySampleFile.as文件中，不带任何参数编译它，取得的效果与上例相同。\n1[SWF(width=\u0026#34;800\u0026#34;, height=\u0026#34;600\u0026#34;, backgroundColor=\u0026#34;#ffffff\u0026#34;, frameRate=\u0026#34;30\u0026#34;)] 那么，元数据应该放在as文件中何处呢？书中写到，它应该处于package块中，在class定义之外，通常放在import声明之前或之后。\nThis line is placed inside the main package block, but outside any class definitions (usually just before or after any import statements).\n但是，经过试验，我发现元数据需要放在import声明之后才会有效。另外，如果设置了元数据，同时又使用参数进行编译，则元数据会覆盖掉参数的设置。\n","date":"2006-12-17","description":"","lastmod":"2006-12-17T15:30:39Z","slug":"as3-cookbook-11","tags":["as3","cookbook","note"],"title":"《ActionScript 3.0 Cookbook》读书笔记1.1","url":"https://blog.zengrong.net/post/as3-cookbook-11/"},{"categories":["technology"],"content":" ActionScript 3.0 CookBook ActionScript 3.0 help Flex 2 help Adobe Flex 2 Training from the Source Programming ActionScript 3.0 ","date":"2006-12-14","description":"","lastmod":"2006-12-14T02:19:18Z","slug":"as3-books","tags":["as3","book","flex"],"title":"学习ActionScript 3.0 和Flex的几本好书（下载）","url":"https://blog.zengrong.net/post/as3-books/"},{"categories":["others"],"content":"替朋友发一则招聘启示，内容如下：\n湖北嘟嘟网络技术有限公司是以一家大型游戏娱乐门户网站为平台，从事网络媒体、虚拟社区、网络增值服务、以及相关产品建设与运营的网络公司。随着公司规模的不断扩大，因业务需要，现向互联网业内广纳英才.\n嘟嘟网络将为您提供富有竞争力的薪酬福利待遇，广阔的个人发展空间，以及完善的培训平台。 是您一展身手、实现自我价值的沃土! 期待您的加盟!\n招聘职位：ASP.NET程序员\n职位要求：\n1.有丰富的asp.net开发经验,计算机专业优先;\n2.熟悉html代码和客户端浏览器脚本的书写;\n3.熟悉sql server 2000 存储过程的编写和测试;\n4.良好的编程习惯,能经受较大的工作压力;\n5.有团队精神,有团体开发经验者优先考虑;\n6.有基本的面向对象编程思想,对新知识有强烈的学习欲望和学习能力.\n招聘职位：界面设计师\n职位要求：\n1、美术专业毕业或有扎实的美术功底，理解市场潮流和大众口味；\n2、能够使用手工或软件设计创作卡通形象、界面、图形、插画等；\n3、有良好的理解能力和表达能力；\n4、有成形或已被社会采纳的作品者优先；\n5、可兼职；\n招聘职位：AS设计师\n职位要求：\n1.能够实现swf与后台程序与数据的对话，熟悉Flash Remoting 和 Web Service；\n2.精通ActionScript2.0语言，有在线的作品发布。\n3.了解AS3.0并具有主动高效的学习能力；\n4.熟悉Flash、Flex与周边相关的开发软件；\n5.有RIA(Rich Internet Applications)和FMS(Flash Media Service)开发经验和作品者优先；\n以上职位薪资待遇：1000--5000/月+效益奖金\n公司地址:武汉市江汉区新华路186号福星商会大厦1202室 邮编:430022\nEmail: hr@corp.hubei.com QQ：180443421\n","date":"2006-12-10","description":"","lastmod":"2006-12-10T12:26:29Z","slug":"job","tags":["actionscript","aspnet","job","ui"],"title":"招聘ASP.net程序员、界面设计师、ActionScript程序员","url":"https://blog.zengrong.net/post/job/"},{"categories":["others"],"content":"2006武汉热门火锅店\n大众点评网于2006年11月编辑。转载请注明出处。\n点击 http://www.dianping.com/search_m/16/10_c508 查看武汉161家火锅店的详细信息。\n1. 重庆打渔人家\n地址：汉口京汉大道111号一品天下4018A 83777779\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/1886452\n口味 26 环境 18 服务 19 人均 37元\n在好友的“热烈推荐”下来到他家。环境真不错，既有“朴实”的八人餐桌，也有“专供情侣”的双人卡座，更不用说“各有特色”的包房了。点的鸳鸯锅，“中间”清汤，“四周”红汤。“对半剖开”的鱼“据说先用蛋清等调味料腌 制过”，一投入汤汁中，就“害羞地卷曲起来”。尝一口，果然“又滑嫩又进味”，配着“海大一碗”的调味料，真叫一个爽！\n2. 胖锅轩\n地址：汉口新华下路北湖三五屯正对面 85777555\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/549201\n口味 19 环境 18 服务 12 人均 38元\n环境“让人吓一跳”，“一点都不像火锅店”，倒是“有西餐厅的感觉”。传说中的香辣虾果然不错——个头“够大”，没有“虾爷爷、虾孙子齐上阵”的状况；而且“够新鲜”，“须须都炸得脆脆的”，可以一起嚼下去；锅底里的黄 瓜和藕“一抢而光”；最后往里下热干面，“煮到胖胖的捞出来吃”，“好香”。价格“算贵的”，不过“值啊”！“排一小时的队”都认了。\n3. 新一代自助火锅\n地址：武昌和平大道629号新一佳超市1楼 86727776\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/580724\n口味 20 环境 16 服务 13 人均 36元\n“完全模仿‘凯威’的风格”，整体质量却高于“凯威”。锅底“考虑到不同人的口味”，除了有“清汤的”，还有“酸菜鱼和孜然羊肉的”；甜品“花样不少”；菜“更新较快”；“抢手”的水果“补充及时”；饮料不是“淡而无味 ”的那种，有冰块可以自己加，想得“挺周到”；还“不时有优惠活动”。这样的水准29元/人“真是不贵”。\n4. 百岁鱼\n临江大道店：武昌临江大道52号(近中华路码头) 88866222\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/562401\n口味 21 环境 16 服务 13 人均 39元\n菜单上提供三种鱼（鲶鱼、财鱼、草鱼），“当场选好”，“现杀现做”。餐前“奉上四碟小菜”，“自制”的腌萝卜“酸甜可口”。“二十分钟左右”，鱼端上来了，“红彤彤一片”，“看着就有食欲”。鱼片“厚薄适宜”，“火候 恰当”，“鲜香滑嫩”。锅里的魔芋和花菜也“很进味”。吃完了“点火加些汤”可以涮菜吃，记得要试下他家“特制的拉面”。价格“小贵”。\n雄楚店：武昌雄楚大街沈家湾车站 http://www.dianping.com/shop/1926592\n5. 谭鱼头\n中南店：武昌中南路14号(近世纪中商百货) 87257727\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/579366\n口味 17 环境 15 服务 12 人均 44元\n源自四川的谭鱼头。鱼头“不错”，“味很鲜”，汤底“口味重”。配菜“品种不多”，量“较小”。环境“一般”，不过“可以看到变脸表演”，蛮有特色。价格和其他火锅店相比“算贵的”。\n香港路店：汉口香港北路257号 http://www.dianping.com/shop/549381\n6. 老渔翁鱼头火锅\n地址：汉口京汉大道792-800号(近前进一路) 85851077\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/548989\n口味 19 环境 16 服务 14 人均 41元\n据说“请了‘谭鱼头’的原班厨师”过来做火锅。鱼头烫起来“又鲜又嫩”，整锅汤经这么一涮，变得“鲜美无比”，比那些“入口只剩辣味”的火锅“不晓得好吃多少倍”。香辣虾也值得推荐，往里下热干面很香哟！\n7. 北京东来顺\n中北路店：武昌中北路138号(近银苑大厦) 86778778\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/549400\n口味 20 环境 18 服务 16 人均 47元\n“地道”的北京涮羊肉。锅子是“老式烧炭的那种”，“很怀旧”；羊肉“又薄又嫩”；清汤锅底“吃起来也比较清爽”。羊肉串“喷香喷香”，总是“让人口水哗哗的”。套餐也“很实惠”，“逛街累了可以来这里混个饱”。\n全部8家分店信息：http://www.dianping.com/search_b/49555\n8. 毛哥老鸭汤\n地址：武昌珞狮路劳动局附近\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/565897\n口味 22 环境 14 服务 17 人均 27元\n去吃的话“一般都点鸭汤火锅”。鸭子“很肥”，鸭肉“烂烂的”，汤熬得“很浓、很香”，“下配菜前一定要多喝几碗”。量挺足，两个人“要半只鸭子就够了”。\n9. 锅加锅\n循礼门总店：汉口解放大道910号 85510132\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/549669\n口味 18 环境 17 服务 12 人均 34元\n“比较大”的火锅连锁，“朋友聚餐的好去处”。他家的火锅，锅底“很香”，小料和肉菜也都“比较让人满意”。店的装饰风格“挺讨人喜欢”，就是“常常人满为患”，“吵”。地面也“比较滑”，“要自己小心”。\n全部10家分店信息：http://www.dianping.com/search_b/49706\n10. 凯威啤酒屋\n洪山店：武昌中北路1号家乐福洪山店1楼 87276211\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/548914\n口味 14 环境 13 服务 11 人均 30元\n自助火锅连锁。锅底“太淡”，想味道好些“就看各人往里加料的本事”；羊肉、肥牛“随便吃”，但“别黑心拿太多”，浪费是要“另算钱的”；冰淇淋“敞开供应”，就是“硬得像石头”，要挖动“得练过内功才行”……味道真的 “不怎么样”，不过如果带着平常心去吃的话，“还算过得去”。价格又“便宜”，碰上聚会什么的，胡吃海喝一顿也蛮爽。\n全部9家分店信息：http://www.dianping.com/search_b/49501\n11. 靓家火锅\n地址：武昌徐东路6号 51871536\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/1905994\n口味 20 环境 18 服务 14 人均 33元\n12. 重庆特色冷锅鱼\n地址：武昌八一路483号武汉大学对面\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/579465\n口味 17 环境 5 服务 15 人均 17元\n13. 成都常一口火锅\n紫阳路店：武昌紫阳路281号安华酒店1楼(武昌火车站对面) 62659999\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/574048\n口味 20 环境 16 服务 16 人均 31元\n“口字形”的鸳鸯锅，“中间一个小口”是白汤，可以喝；“四周包围着红汤”。烧开后，服务员会去掉浮沫，感觉“锅底很干净”，“涮不同的东西都能保持原味”。菜准备得“很用心”，黄喉、毛肚、鸭肠什么的都“非常干净”， “没有被漂白过”；牛肝菌是“武汉不多见的”。送的豆浆“极浓”，味道“很好”。\n腰路提店：汉阳腰路提197号 http://www.dianping.com/shop/1928794\n14. 红玫瑰火锅\n地址：汉阳郭茨口知音西村\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/548929\n口味 21 环境 7 服务 7 人均 25元\n“每隔一段时间就会去”的一家店。“每次都点弯弯+牛肉”，弯弯汤的“香醇”搭配牛肉的“微辣”等于“恰到好处”的美味。全部吃完后，就着锅底再涮些配菜，那叫一个爽！价格“不贵”，生意“特别好”，排队是常有的事。\n15. 满旗楼涮羊肉\n地址：汉口沿江大道136号 82791563\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/548353\n口味 19 环境 13 服务 10 人均 48元\n“挺地道”的老北京涮羊肉店。服务员“都穿唐装”，“背条毛巾”，“操着京片子”，让人不知不觉也“跟着说起普通话来”。火锅“和‘东来顺’一样”，“白水涮”，羊肉“鲜嫩”，价钱却比后者便宜。炸酱面和手工北方水饺也 是“招牌”——面条“Q劲”，饺子“实在”。天冷时去吃，看着热腾腾的空气里大家红扑扑的脸，真是“冬日的一道风景”。\n16. 水货火锅城\n前进四路店：汉口前进四路60号 82808568\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/548791\n口味 15 环境 3 服务 8 人均 30元\n吃来吃去还是“财鱼片火锅最好”，鱼肉“很嫩”，口味“一直都蛮稳定”。鸡和牛蛙的火锅“原先也好好吃”，“现在不如从前了”。香辣虾“感觉一般”，虾子“大小不一”，有时候还会“不进味”。前进四路的老店环境是“几十 年不变的糟糕”，不过人还是“出奇的多”，可能因为这里有“武汉人自己的味道”吧。\n硚口店：汉口中山大道1号 http://www.dianping.com/shop/548792\n新华路店：汉口新华路299号 http://www.dianping.com/shop/1928992\n17. 津鱼头火锅店\n地址：武昌八一路洪山广场 87303103\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/549005\n口味 18 环境 9 服务 12 人均 35元\n鱼头锅底的味道“确实不错”。白汤里“有小参、桂圆、红枣”，“挺滋补”，涮之前先喝一些，“蛮鲜的”；红汤“麻辣”却“不伤胃”。味碟“有特色”，配菜也“不贵”，用代金券更加划算。至于环境，火锅店都是“如出一辙的 嘈杂和乌烟瘴气”，适合一堆人去吃，“图个热闹”。\n18. 重庆香牌坊火锅\n沿江大道店：汉口沿江大道181号(近粤汉码头) 82775250\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/549199\n口味 20 环境 12 服务 12 人均 33元\n锅底是“蛮正的四川口味”。招牌鸭肠“很脆”，“越煮越好吃”。味碟用“油和大蒜泥”配制，“很有回味”。价钱“便宜”，“适合老百姓的荷包”。就是卫生状况“不怎么好”。\n彭刘杨路店：武昌彭刘杨路首义园对面 http://www.dianping.com/shop/569046\n19. 江胖子猪弯弯火锅\n水厂店：汉口宗关水厂\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/548928\n口味 17 环境 3 服务 7 人均 24元\n鸳鸯锅，辣的一半是“牛肉和竹笋”，另一半就是“猪弯弯煨藕”。口感“还不错”，牛肉“够辣”，莲藕“够烂”，猪弯弯汤“够浓”，“喝了粘嘴唇”。环境“就那样”。最喜欢的还是“几个人露天吃火锅的感觉”，那叫一个酣畅 淋漓。夏天去“别有一番滋味”。\n古田店：汉口古田二路 http://www.dianping.com/shop/565351\n知音店：汉阳郭茨口知音西村 http://www.dianping.com/shop/1910614\n20. 小肥羊火锅\n体育馆路店：武昌体育馆路2号 87320708\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/549690\n口味 21 环境 15 服务 14 人均 44元\n“红遍大江南北”的火锅店。锅底“加了中药材”，闻起来就“很香”，让人“口水直流”；涮起来清汤“回味悠长”，红汤“辣得刺激”。羊肉更是一绝，“肉质细嫩”，“久煮不老”，而且“一点膻味都没有”。价格虽然“贵一些 ”，但为了体验这种“与别家明显不同的感觉”，多花点money也值了。\n徐东村店：武昌徐东村车站 http://www.dianping.com/shop/586331\n21. 三国英雄\n鲁磨路店：武昌鲁磨路\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/1893346\n口味 17 环境 10 服务 10 人均 34元\n火锅“其实就那么回事”。他家“味道还不错”，就是“辣得有点过了头”，“胃有时受不了”。店从开张起就没见怎么冷清过，“客流量很大”，毕竟“一个便宜三个爱嘛”！\n全部10家分点信息：http://www.dianping.com/search_b/81370\n22. 小尾羊\n彭刘杨路店：武昌彭刘杨路196号 88057977\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/549886\n口味 17 环境 13 服务 13 人均 33元\n和“小肥羊”类似的连锁火锅店。清汤锅底“鲜而不腻”，红汤锅底“辣而不麻”，涮上“鲜嫩”的羊肉，“挺好吃的”。其他菜“一般般”。常有各种优惠活动，送送“代金券”，来点“特价菜”什么的，“比较划算”。P.S.青年路 店还有手抓龙虾供应。\n崇仁店：汉口崇仁北路 http://www.dianping.com/shop/569896\n青年路店：汉口青年路155号长源大厦 http://www.dianping.com/shop/1911052\n23. 天府山珍\n江大店：汉口江大路30号 82611008\n推荐菜、图片\u0026gt; http://www.dianping.com/shop/548931\n口味 18 环境 9 服务 14 人均 52元\n武汉“不多见的清汤火锅”。材料“以菌类为主”，大约“几十个品种”，价格“高低不等”。“先喝汤”，“再吃菌”，“最后下配菜”。有“专门”的服务员“介绍各种菌类的功效”。总体感觉“很清淡”，“没什么油水”，不过 “挺滋补”，对肠胃来说也“比较温和”。吃多了辣火锅，可以来这儿换换口味。\n丁字桥店：武昌丁字桥27号 http://www.dianping.com/shop/567274\n＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝\n点击 http://www.dianping.com/search_m/16/10_c508 查看武汉161家火锅店的详细信息。\n查看武汉2820家餐馆的详细信息？请登陆 http://www.dianping.com/wuhan/food\n","date":"2006-11-28","description":"","lastmod":"2006-11-28T00:48:57Z","slug":"wuhan-huoguo","tags":["wuhan"],"title":"【转】2006武汉热门火锅店","url":"https://blog.zengrong.net/post/wuhan-huoguo/"},{"categories":["web"],"content":"wablet是一个在线提供webIM服务的网站，和meebo一样，它也可以支持ICQ、MSN、AIM和Yahoo Messenger，但也有相当的不同：\nwablet不支持Gtalk/Jabber协议 采用Flash技术，而非AJAX 界面比meebo更漂亮，功能也更强大...... wablet目前还使用邀请制度，有需要邀请的朋友请留言。\n由于太久没有登录，已经往忘记了wablet的帐号，所以没法发送邀请了。\n以下是wablet和meebo的一些截图：\nwablet登录界面\nwablet聊天界面\nmeebo登录界面\nmeebo聊天界面\n","date":"2006-11-18","description":"","lastmod":"2006-11-18T03:14:11Z","slug":"wablet","tags":["flash"],"title":"收到wablet的邀请","url":"https://blog.zengrong.net/post/wablet/"},{"categories":["technology"],"content":" Tips for testing a Flash-based interface Adobe to open source Flash Player scripting engine Making Flash websites searchable ","date":"2006-11-16","description":"","lastmod":"2006-11-16T01:43:43Z","slug":"tips-for-testing-a-flash-based-interface","tags":["actionscript","flash","ria"],"title":"Tips for testing a Flash-based interface","url":"https://blog.zengrong.net/post/tips-for-testing-a-flash-based-interface/"},{"categories":["use"],"content":"熬夜配置电脑共享多普达900的GPRS上网成功，写一些经验吧：\n首先转载一篇写的简单明了的文章：\n转自小草\n先下载安装驱动和拨号程序：\n1 文件 1、拨掉手机数据线，然后调出2K或XP的任务管理器，结束wcescomm.EXE的任务\n2、然后在手机里面选择：开始－附件－调制解压器连接，选择你与电脑的连接方式（USB模式或者红外模式），然后右软键菜单里选择“激活”（千万不要点完成，否则调制解调器又关上了）\n3、连上数据线，这时候电脑就会检查到新设备，装好驱动（驱动就是那个Inf文件）。\n4、最后双击刚才下载的那个压缩包里面的UModem_Dialer拨号程序，在最下面那行“internet”改为“cmwap”（一般不用改，默认就是了），不需要输入用户名和密码直接拨号！成功后右下角有网络图标。提示连接成功230K。呵呵，赶快试一下QQ吧，打开IE试试。\n注意：第一次安装驱动时可能要重新插拨一次数据线。另外，我提供下载的UModem_Dialer拨号软件已将internet改为默认cmwap了，不用担心忘记改又用CMNET方式上网。\n使用上面的内容就可以配置成功了，但还有几点需要强调的：\n我使用的是基于windows Mobile 5.0操作系统的手机，估计上文作者的手机也应该是Windows Mobile操作系统的多普达系列手机。windows将手机识别为HTC USB Modem（如图所示），因此不能确定此驱动程序是否适合多普达系列以外的Windows Mobile操作系统。\n此方法不适用于Symbian或者Linux系统的手机\n拨号软件可以不用上文作者提及的USBModem_Dialer.exe，因为Windows 自带拨号程序，可以使用“开始－设置－网络连接－新建网络连接向导－连接到Internet－手动设置我的连接－用拨号调制解调器连接”来添加即可。我在操作的时候并未手动添加连接，因为一打开firefox，就自动弹出了一个连接对话框，连要拨的号码*99#都自动设置好了（如图所示）。我的操作系统是在Window XP sp2。\n连接成功之后，显示的速度是230.4kbps，经过测试，我在使用的过程中最高速度可以达到7.1KBps，大约是56.8kbps，和猫的速度差不多。\n用电脑上网和用手机上网的流量完全不在一个档次，我上淘宝逛了一圈，就发现已经用了5M流量……平常我每个月的流量也不过6MB而已呀……所以，如果你不是非常的有钱，最好把浏览器的图片显示和Flash显示关闭。\n","date":"2006-11-05","description":"","lastmod":"2006-11-05T23:44:09Z","slug":"dopod900-gprs","tags":["gprs","mobile","pda","ppc"],"title":"让电脑通过多普达900上网","url":"https://blog.zengrong.net/post/dopod900-gprs/"},{"categories":["use"],"content":"今天做会议预算时需要将最终金额转换到中文大写数字，发现Excel帮助中找不到相关资料，google了一下，大致了解了应该使用“[DBNum2]”数字类型将数字转换为中文大写。\n一些常见的用法如下：\n1.在单元格格式中设定自定义格式:\n2.使用TEXT函数进行转换：\n至于结合其它函数实现自动转换元角分等功能，请看下面的几篇文章：\nEXCLE实现货币金额大小写转换 教你如何将日期变“大” 手把手教你转换出中文大写金额 在EXCEL中将数字转换为人民币大写的三种方式 Excel中将数字转换成中文大写 顺便说一句，EditGrid并不支持[DBnum2]\n","date":"2006-10-31","description":"","lastmod":"2006-10-31T08:42:34Z","slug":"dbnum2","tags":["editgrid","excel","office","spreadsheet"],"title":"利用excel的[DBNum2]数字类型实现中文大写货币金额","url":"https://blog.zengrong.net/post/dbnum2/"},{"categories":["technology"],"content":"sephiroth.it的站长好像爱上了开发firefox插件，自从推出Flash Tracer插件以来，这是他开发的第三个firefox插件了。\n这个插件的功能是可以随意选择Flash Player的版本。例如，我通常需要使用Debug版本的Flash Player，但是前段时间测试新版Flash Player的全屏效果时，我又必须安装测试版的Flash Player r9.0.18。现在好了，有了这个插件，可以随意在各个版本的Flash Player之间切换。\n在这里可以下载这个插件，安装后，插件自带8个版本的Flash Player(从2.0到9.0)。\n使用的方法非常简单，\n只需要在插件上单击某个需要切换到的Flash Player版本即可，插件会弹出一个对话框告诉你切换成功，然后自动刷新页面让切换的插件生效。\n我大致了解了一下原理：firefox(windows版本)的Flash Player插件其实只有一个文件：NPSWF32.dll，这个文件保存在firefox安装目录的plugins文件夹下，要切换firefox的Flash Player插件，只需要替换掉这个文件即可。平常我都是通过替换这个dll文件来更新firefox的Flash Player的。\n在安装插件时，插件将各个版本的Flash Player的NPSWF32.dll解压缩到下面的文件夹中：\nX:\\{userAppData}\\Mozilla\\Firefox\\Profiles\\{userProfile}\\extensions\\{2b5cfade-d133-429c-aea5-865911de4e1d}\\chrome\\plugins 当执行切换动作时，插件将对应的dll文件复制到firefox安装目录的plugins文件夹，替换原来的插件。\n因此，要添加一个新的插件，只需要添加一个目录，并将相应的NPSWF32.dll复制到这个目录中即可。下图是我添加了两个插件之后的插件目录：\n","date":"2006-10-28","description":"","lastmod":"2006-10-28T14:27:47Z","slug":"flash-switcher","tags":["firefox","flashplayer","plugin"],"title":"可以随意切换Flash Player版本的firefox插件-Flash Switcher","url":"https://blog.zengrong.net/post/flash-switcher/"},{"categories":["use"],"content":"今天在制作第六届湖北省教师教育软件大奖赛获奖名单时，要分类算出各个组别、各个项目的一、二、三等奖的数量。掰起指头算了一下：中小学和幼儿园分别有4类项目，每个项目3个奖项，那么最终要进行计算的奖项数量就是3*4*2=24项，实在大大超出我这种懒人能够承受的极限，因此准备研究如何偷懒。\n虽然我已经用数据透视表将这24个奖项的数目统计出来了，但是要我把这些数字一个个抄到获奖作品表中，也未免工作量“太大”(其实也并不是那么的大 :em20: 而是人太懒)了把，怎么能想个办法写段代码，然后粘贴24次就OK？\n思路是这样的，由于我已经为每个作品都设定了组别(幼儿园、中小学)、奖项(1、2、3)以及作品类别(课件、课例、论文、教学设计、视频展台教材)，因此只需根据特定的条件就能够得到特定的项目的个数。\n首先想到的是COUNTIF函数，我需要知道幼儿园的所有作品和中小学的所有作品数量，只需要COUNTIF($D$6:$D$575,D6)即可，获取中小学作品的数量也可以如法炮制。\n但是，COUNTIF函数只能接收一个条件。如果我要获取幼儿园的课例作品的数量呢？如果我还要获取中小学课件一等奖的作品的数量呢？\n这需要使用数组公式。\n我还找不到一句简洁的话来说明什么是数组公式。可能这句话贴切一些把：将公式的结果作为数组进行计算。\n那么，我要计算中小学的课例一等奖作品数量，代码就是：\n=sum(($C$6:$C$575=C6)($D$6:$D$575=D6)($E$6:$E$575=E6))\n当然，最重要的一点是在结束公式的时候按下Ctrl+Shift+Enter，这样Excel会在公式的两端自动加上大括号，将此公式作为数组公式对待。\n上面这句的数组公式如下所示：\n{=sum(($C$6:$C$575=C6)*($D$6:$D$575=D6)*($E$6:$E$575=E6))} 需要注意的几点是：\n你不能手动加上大括号，这没有作用 sum中必须使用嵌套括号，否则会出错。也就是说，($C$6:$C$575=C6)这句外面的括号不能去掉 单元格的引用不能从C1开始。也就是说，($C$1:$C$575=C6)会出错 单元格的引用不能到空单元格结束。也就是说($C$6:$C$1000=C6)会出错 在这里可以下载这个电子表格：第六届湖北省教师教育软件大奖赛获奖名单.xls，不过下载之后，当你用Excel打开它时，你需要在所有的数组公式上按F2快捷键，然后按Ctrl+Shift+Enter刷新它们。EditGrid直接支持数组公式，我不知道它是怎么做到的。\n在下面可以在线查看这个电子表格，你可以查看公式和所有的值，但你不能编辑它:\nEditGrid Spreadsheet by user/zrong.\n","date":"2006-10-26","description":"","lastmod":"2006-10-26T15:56:14Z","slug":"excel-count","tags":["excel","office"],"title":"利用数组公式进行多条件计数","url":"https://blog.zengrong.net/post/excel-count/"},{"categories":["technology"],"content":"验证用户名格式：\n^[a-zA-Z0-9]([\\.]?[\\-]?[_]?[a-zA-Z0-9]){2,19}$ 英文数字打头，中间可包含不连续的 . 和 - 和 _ ，长度为 3 -20 位\n验证英文内容：\n^[a-zA-Z]{1,}$ 长度至少1位\n验证Email：经过我改良的表达式，很好用\n^[a-zA-Z0-9]([\\.]?[\\-]?[_]?[a-zA-Z0-9])*@\\w+([\\.-]?\\w+)*(\\.\\w{2,4})+$ 英文数字打头，中间可包含不连续的 . 和 - 和 _ ，不限域名格式\n验证QQ：\n^[1-9]\\d{4,10}$ 正数开头，5 - 10 位\n验证日期：格式为 xxxx-xx-xx ，超强，剔除了无效日期（如非闰年的2-29 号）\n^(?:([0-9]{4}-(?:(?:0?[1,3-9]|1[0-2])-(?:29|30)|((?:0?[13578]|1[02])-31)))|([0-9]{4}-(?:0?[1-9]|1[0-2])-(?:0?[1-9]|1\\d|2[0-8]))|(((?:(\\d\\d(?:0[48]|[2468][048]|[13579][26]))|(?:0[48]00|[2468][048]00|[13579][26]00))-0?2-29)))$ 验证域名：\n^[a-zA-z0-9]+(\\.)[a-zA-z0-9]+(\\.)[a-zA-z0-9]+$|^[a-zA-z0-9]+(\\.)[a-zA-z0-9]+(\\.)[a-zA-z0-9]+(\\.)[a-zA-z0-9]+$|^(localhost)$ 可验证 x.x.x.x 和 x.x.x 以及 localhost 格式\n验证协议地址：ftp://1.2.3.4\n^(ftp://)(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$ 协议可自行修改\n验证IPV4地址：剔除无效格式，如 333.3.4.5\n^(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d{1,2}|1\\d\\d|2[0-4]\\d|25[0-5])$ ","date":"2006-10-23","description":"","lastmod":"2006-10-23T08:51:33Z","slug":"regular-expression","tags":["regexp"],"title":"几个常用的正则表达式（大部分原创）","url":"https://blog.zengrong.net/post/regular-expression/"},{"categories":["technology"],"content":"著名的sephiroth站长开发了他的第一个firefox插件：FlashTracer。可以毫不过分的说，这是一个我梦寐以求的插件。\n作为一个Flash开发者（姑且这么说），我经常需要在非Flash IDE环境下显示trace()信息。为此我曾经寻找过许多具有类似功能的小软件，并写了非IDE环境显示trace()内容一文。这篇文章中需要下载一个名为Flash7 Debug的程序，这个小程序会驻留在系统图标栏中，显示swf影片中的trace()信息。\nFlashTracer插件，则可以完全代替Flash7 Debug，并且功能要强大数倍。\n来看看FlashTracer的界面以及我的介绍（以下图片均来自mozilla）：\n插件采用侧边栏的方式显示\n工作时的效果\n插件设置\n可以设置log文件保存的地址，以及是否显示warning类调试信息;\n可以在插件中设置warning的条数。不需要再去手动修改mm.cfg了；:em20:\n可以设置log文件的编码字符集，设置为UTF-8可避免在显示中文信息时出现乱码（Flash7 Debug会将调试信息中的中文信息显示为乱码。因为它不支持UTF-8）。\n支持正则表达式\n这是一个非常有用的功能，再也不用在几千条调试信息中去苦苦搜寻了……:em52:\n当然，还有两个小小的要求，你才能使用这个插件：\n你使用firefox浏览器 你使用的Flash Player是Debug版本 不过，为了这么强大的功能，上面这两个小小的要求又算什么呢？\n安装FlashTracer\n顺便说一句：\n如果由于某些原因，你一定要在IE下查看swf动画，则可以借助IE Tab这个插件。\n12月3日更新\nFirefox插件FlashTracer支持中文和flashplayer9,0,28下失灵的解决办法\n","date":"2006-10-16","description":"","lastmod":"2006-10-16T14:18:26Z","slug":"flashtracer-firefox","tags":["firefox","flash","flashplayer","plugin"],"title":"FlashTracer插件介绍","url":"https://blog.zengrong.net/post/flashtracer-firefox/"},{"categories":["technology"],"content":"代码和效果展示的原作者是Ric Ewing，Robert Penner, Eric Mueller and Michael，黑羽将其转为AS2类。\n1 文件 1 文件 详细介绍\n效果展示：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2006-10-15","description":"","lastmod":"2006-10-15T03:59:08Z","slug":"graphictools-class","tags":["as2","class","graphic"],"title":"AS2高级绘图工具类 GraphicTools class","url":"https://blog.zengrong.net/post/graphictools-class/"},{"categories":["technology"],"content":"终于有时间试试Flash Player 9.0.18.60的全屏功能了。\n下载新的Flash Player并安装之后。制作了这个小小的影片查看效果（如果单击“全屏”按钮无效，则需要安装Flash Player 9.0.18.60）：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n1 文件 相关文档\n几个注意事项：\nfor IE的Flash Player 9.0.18.60我怎么也没有安装成功，所以我是在firefox下测试的； 在全屏模式下，文本输入框不起作用（来自Danger）; 需要在嵌入swf的 \u0026lt;object\u0026gt; 标签和 \u0026lt;embed\u0026gt; 标签中加入 AllowFullScreen 属性并使其值为 true ； 如果使用swfobject，使用这个代码加入AllowFullScreen属性： 1so.addParam(\u0026#34;AllowFullScreen\u0026#34;, \u0026#34;true\u0026#34;); 效果图：\n全屏、不缩放、居中对齐效果\n全屏、不缩放、右上角对齐效果\n","date":"2006-10-13","description":"","lastmod":"2006-10-13T14:29:49Z","slug":"flash-player-fullscreen","tags":["flash","flashplayer"],"title":"Flash Player的全屏","url":"https://blog.zengrong.net/post/flash-player-fullscreen/"},{"categories":["others"],"content":"今天照旧打开writely做笔记的时候，发现域名转向了http://docs.google.com。看来writely彻底消失了:em22:。\n在新的域名中，google将writely和spreadsheets进行了整合，在一个界面中可以选择是进行文本或是电子表格的编辑。仔细看了看新特色，发现并没有太大的变化：\n改进：\n改进了界面，将Tag的显示放在了右上角； 改进了界面，使其更符合google风格，与spreadsheets统一； 列表页面就可以查看某篇文章的预览了。这样可以不必为了看一篇文章而进入编辑模式，节省了不少时间； 会自动将文章的第一行作为文章名称保存。 问题：\n对中文标题的支持仍然有问题，预览和编辑的时候会显示中文的unicode码； 仍然不支持显示中文tag的文章列表； 在列表页面添加Tag时没有显示出所有的Tab； 编辑状态下不能添加Tag 下面是两张截图：\n","date":"2006-10-11","description":"","lastmod":"2006-10-11T13:17:16Z","slug":"writely-googledocs","tags":["google","office","spreadsheet","weboffice"],"title":"writely彻底消失","url":"https://blog.zengrong.net/post/writely-googledocs/"},{"categories":["others"],"content":"正在gmail里面收信收的不亦乐乎，结果突然就发现不能访问了。google给了这样的提示：\nLockdown in sector 4!\nOur system indicates unusual usage of your account. In order to protect Gmail users from potentially harmful use of Gmail, this account has been disabled for up to 24 hours.\nIf you are using any third party software that interacts with your Gmail account, please disable it or adjust it so that its use complies with the Gmail Terms of Use. If you feel that you have been using your Gmail account according to the Terms of Use or otherwise normally, please contact us using this form to report this problem.\n我faint！我好像没干什么坏事嘛，也就是用FlashGet下载几个邮件附件而已……当然，如果它也算“third party software”的话。\n试了一下writely，还好，没有被封掉。\n没办法，只有等了。\n","date":"2006-10-07","description":"","lastmod":"2006-10-07T03:39:06Z","slug":"264","tags":["gmail","google"],"title":"gmail帐户被google停了","url":"https://blog.zengrong.net/post/264/"},{"categories":["news"],"content":"wallop是一个全Flash网络社区。\n收到Dreamer的邀请后，就立刻开始了体验。\n总的感觉还是十分不错。可能是由于beta版的原因，社区对中文支持不太好，显示的中文字号只有10pt，看起来很费劲。在发布Blog的时候可以选择字体的大小，但是那只限于Blog中。在整个社区的选项中，也看不到设置字号的地方。另外，在上传音乐文件以及对音乐文件发布评论时，有些中文会显示乱码。当然，可以通过对id3进行修改来改正乱码信息。\n音乐上传仅仅支持mp3文件，却不支持微软自家的wma，这是因为Flash Player 的限制。上传的速度不错。\n不过，Flash Player的效率是在是让人不能恭维，我测试的半小时过程中，就出现了两次死循环。另外，在社区开启的时候，CPU占用率一直都在80%上下。当然，我的机器太烂也是原因之一了。 :em36:\n如果有需要邀请的朋友，可以在本文的评论中留下Email地址。(还剩7个邀请邀请似乎愈发愈多，因此不限量了)\n以下是几张截图，这里看所有大图 ：\n登录\n登录\n主页\n音乐和播放器\n修改mp3 id3信息\n设置图片属性\n上传音乐文件\n发布与查看Blog\n","date":"2006-10-06","description":"","lastmod":"2006-10-06T04:12:27Z","slug":"wallop","tags":["flashplayer"],"title":"收到Wallop邀请","url":"https://blog.zengrong.net/post/wallop/"},{"categories":["technology"],"content":"改用vim后，我把所有用过的编辑器统统抛弃了。但是在用vim写ActionScript的时候出现了问题。\n我用vim写的ActionScript2.0类，居然无法编译！Flash IDE提示“无法载入类”。而我反复检测代码，都没有语法问题。\n最后用Flash IDE打开这个类文件并保存，就编辑成功了。\n检查Flash IDE另存前后的文件，发现另存后的文件多了3个字节（如图1）。\n用TotalCommand比较另存前后的文件，发现两个文件的第一个字符有些出入。应该就是这个原因影响了Flash IDE对ActionScript文件的识别，导致import类失败。（如图2）\n","date":"2006-10-03","description":"","lastmod":"2006-10-03T15:36:58Z","slug":"vim-as2","tags":["actionscript","vim"],"title":"用vim编辑ActionScript出现编译失败的问题","url":"https://blog.zengrong.net/post/vim-as2/"},{"categories":["use"],"content":"今天在编辑一个有表格的文档时，发现这样的问题：\n表格完后的一个页面是空页面，其中只有一个空行，但是却始终无法删除。如图所示。\n看了看这个空行的段落属性，发现了问题所在。把此行的段前和段后间距全部改为0，问题解决！:em20:\n","date":"2006-09-28","description":"","lastmod":"2006-09-28T08:37:16Z","slug":"word-del-blank-line","tags":["office","word"],"title":"Word中无法删除最后一个空页面的问题","url":"https://blog.zengrong.net/post/word-del-blank-line/"},{"categories":["others"],"content":"今天在新网终于看到了他们关于DNS问题的公告：http://www.xinnet.com/News/Text/t1787.htm\n我公司在此郑重声明，严厉谴责一切网络黑客攻击的现象，对无视用户损失和利益的黑客攻击行为，我公司表示最强烈的谴责和声讨，并将运用法律手段与之进行斗争，保护所有用户的正当权益。同时，我公司也呼吁大家，为了净化我们的网络空气，一起携手努力！\n说的多么冠冕堂皇！等等，好像掉了什么？为什么没有一句道歉的话？如果你无视自己的问题，我凭什么要和你“一起携手努力”？\n","date":"2006-09-27","description":"","lastmod":"2006-09-27T00:53:32Z","slug":"xinnet-dns-2","tags":["xinnet"],"title":"新网域名解析服务器DNS故障恢复通告(09-26)","url":"https://blog.zengrong.net/post/xinnet-dns-2/"},{"categories":["news"],"content":"周四16时至周五12时，我的Blog一直不能访问，但同在一台服务器上的多媒体空间却一切正常，后来才知道原来是新网被黑客攻击所致。\n我觉得很难过，这不是因为我是新网的代理商；其实对于我的Blog来说，一天不能访问没有什么损失；也暂且不论其他商业网站的索赔问题。我看到的是，一直到现在，新网的官方网站上都没有任何关于这次事件的任何声明。对于这样一个标榜着“权威、专业品牌”的老牌互联网服务商来说，连这样一点点起码的职业道德和素养都没有，怎么不让人难过呢？\n","date":"2006-09-23","description":"","lastmod":"2006-09-23T09:14:51Z","slug":"xinnet-dns","tags":["xinnet"],"title":"关于新网DNS遭攻击事件","url":"https://blog.zengrong.net/post/xinnet-dns/"},{"categories":["use"],"content":" 受同事所托，帮忙挑选几款智能手机，要求：\nQVGA屏幕 可以读取Word等office文件 可以流畅播放QVGA视频 用触控笔操作 价格2000元左右 根据分析，对Word等office文件支持最好的当然是Window Mobile PocketPC平台，例如我的900就支持word、excel文件的编辑和PowerPoint文件的读取和播放。而要流畅播放QVGA视频、CPU频率必须在300MHZ以上，因此挑选了下面5台手机供选择：\n型号 详细介绍 参考\\ 屏幕\\ 处理器 上市\\ 操作系统\\ 推荐度 价格 英寸 时间 均为Windows Mobile for Pocket PC\\ Phone Edition\n夏新E850 查看 2600 2.4 312MHz 2006年05月 Windows Mobile 5.0\\ ★★★ （最新的操作系统，自带的word、excel的功能比2003版更强）\n明基 P50 查看 3000 2.8 416MHz 2005年10月 2003简体中文第一版 ★★★☆\n联想 ET960 查看 3000 2.8 312Mhz 2004年 2003简体中文第二版 ★★\n联想 ET280 查看 2000 3.0 300MHz 2004年08月 2003简体中文第一版 ★\n多普达 818 查看 2600 2.8 416MHz 2004年12月 2003简体中文第二版 ★★☆\n多普达 699 查看 2500 3.5 520MHz 2005年 2003简体中文第二版 ★★★★\n===============================================================\n在2000元这个价位，可以买到的符合以上条件的手机确实少之又少，因此我将价位定在了2000-3000，这样以来选择的余地就大了许多。\n如果是我自己来挑选，我应该会选择夏新E850（当然要试用一下才决定），支持一下国货。 :mrgreen:\n","date":"2006-09-22","description":"","lastmod":"2006-09-22T01:09:52Z","slug":"3000-ppc","tags":["mobile","pda","ppc","windows"],"title":"我推荐的2000-3000元PPC平台智能手机","url":"https://blog.zengrong.net/post/3000-ppc/"},{"categories":["technology"],"content":"本文转自逐浪者\n将Joomla!修改为UTF-8编码之后，发现使用部分模版（如Joomla! 1.03附带的rhuk_solarflare_ii模版）时偶尔会发生打开页面为空白的现象，特别是浏览器未设置为“自动选择”编码的时候。\n在windows操作系统上使用IE作为浏览器时。常常会发生这样的问题：在浏览使用UTF-8编码的网页时，浏览器无法自动侦测（即没有设定“自动选择”编码格式时）该页面所用的编码。即使网页已经声明过编码格式：\n\u0026lt;meta http-equiv=\u0026quot;Content-Type\u0026quot; content=\u0026quot;text/html; charset=UTF-8\u0026quot; /\u0026gt;\n由此造成某些含有中文UTF-8编码的页面产生空白输出。\n如果使用的是Mozilla、Mozilla Firefox、Sarafi的浏览器这不会造成这个问题。这是由于IE解析网页编码时以HTML内的标签优先，而后才是HTTP header内的讯息；而mozilla系列的浏览器则刚刚相反。\n由于UTF-8为3个字节表示一个汉字，而普通的GB2312或BIG5是两个。页面输出时，由于上述原因，使浏览器解析、输出\u0026lt;title\u0026gt; \u0026lt;/title\u0026gt;的内容时，如果在\u0026lt;/title\u0026gt;前有奇数个全角字符时，IE把UTF-8当作两个字节解析时出现半个汉字的情况，这时该半个汉字会和\u0026lt;/title\u0026gt;的\u0026lt;结合成一个乱码字，导致IE无法读完\u0026lt;title\u0026gt;部分，使整个页面为空百输出。而这个时候如果察看源文件的话，会发现实际上整个页面全部已经输出了。\n因此最简单的解决办法是在网页文件的标签中一定要把字符定义\u0026lt;meta http-equiv=\u0026quot;Content-Type\u0026quot; content=\u0026quot;text/html; charset=UTF-8\u0026quot; /\u0026gt;放在\u0026lt;title\u0026gt;\u0026lt;/title\u0026gt;之前。\nps:涉及到编码问题的方式\n服务器httpd.conf文件：AddDefaultCharset UTF-8\n这种方法不推荐，因为服务器上很可能有其他编码的网站存在，所以可以使用AddDefaultCharset off来关闭默认文件编码，服务器依靠html文件投中设置的语言来决定编码。\n脚本php.ini文件：default_charset = \u0026quot;UTF-8\u0026quot;\n作用同httpd.conf文件，也不推荐，可以把该行注释掉，原因同上\n.htaccess文档：AddDefaultCharset UTF-8\n一般人都无法修改httpd.cnf和php.ini，同时也不推荐修改这两个文件。这时也可以在网站根目录下写入.htaccess文档，在该文档中设定网站字符集\n网页文件字符集：\n\u0026lt;meta http-equiv=\u0026quot;Content-Type\u0026quot; content=\u0026quot;text/html; charset=UTF-8\u0026quot; /\u0026gt;\n","date":"2006-09-21","description":"","lastmod":"2006-09-21T00:52:52Z","slug":"ie-utf8","tags":["cms","ie","joomla","utf-8"],"title":"【转】IE不能主动识别UTF-8编码，导致空白页面的问题","url":"https://blog.zengrong.net/post/ie-utf8/"},{"categories":["design"],"content":"九寨沟是人间仙境，九寨沟旅游回来的照片如果不PS一下，怎么对得起观众？可今天同事 深绿色的树 偏偏告诉我，PS过的图片不能上传到内部网络相册，否则会无法显示缩略图。\n来到内部相册网站，果然看到这样的提示：\n原来，相册系统使用的是PHP的EXIF库读取照片的EXIF信息生成缩图，如果EXIF信息被破坏，则无法正常显示缩图。\n据我所知，图像处理软件一般会对EXIF信息造成一定破坏，但不会完全清除。我检查了一下被我修改的照片，果然EXIF信息还在，但是发生了变化。\n下表比较了同一张照片用Photoshop处理前后的EXIF信息：\n+--------------------------------------+--------------------------------------+ | 处理前 | 处理后 | +--------------------------------------+--------------------------------------+ | File: - | File: - | | Y:\\Material\\Photo\\100_7113.JPGMa | Y:\\Material\\Photo\\100_71131.jpgM | | ke - | ake - | | EASTMAN KODAK COMPANY\\ | EASTMAN KODAK COMPANY\\ | | Model - KODAK CX7330 ZOOM DIGITAL | Model - KODAK CX7330 ZOOM DIGITAL | | CAMERA\\ | CAMERA\\ | | Orientation - Top left\\ | Orientation - Top left\\ | | XResolution - 230\\ | XResolution - 230\\ | | YResolution - 230\\ | YResolution - 230\\ | | ResolutionUnit - Inch\\ | ResolutionUnit - Inch\\ | | YCbCrPositioning - Centered\\ | \u0026lt;span | | ExifOffset - 506\\ | style=\u0026quot;color: #ff0000\u0026quot;\u0026gt;Software - | | ExposureTime - 1/60 seconds\\ | Adobe Photoshop CS Windows\\ | | FNumber - 3.00\\ | \u0026lt;span | | ExposureProgram - Normal program\\ | style=\u0026quot;color: #ff0000\u0026quot;\u0026gt;DateTime - | | ExifVersion - 0221\\ | 2006:09:18 20:49:03\\ | | DateTimeOriginal - 2006:09:11 | YCbCrPositioning - Centered\\ | | 21:06:51\\ | ExifOffset - 252\\ | | DateTimeDigitized - 2006:09:11 | ExposureTime - 1/60 seconds\\ | | 21:06:51\\ | FNumber - 3.00\\ | | ComponentsConfiguration - YCbCr\\ | ExposureProgram - Normal program\\ | | ShutterSpeedValue - 1/64 seconds\\ | ExifVersion - 0221\\ | | ApertureValue - F 3.03\\ | DateTimeOriginal - 2006:09:11 | | ExposureBiasValue - 0.00\\ | 21:06:51\\ | | MaxApertureValue - F 3.03\\ | DateTimeDigitized - 2006:09:11 | | MeteringMode - Multi-segment\\ | 21:06:51\\ | | LightSource - Auto\\ | ComponentsConfiguration - YCbCr\\ | | Flash - Flash fired, auto mode\\ | ShutterSpeedValue - 1/64 seconds\\ | | FocalLength - 7.00 mm\\ | ApertureValue - F 3.03\\ | | FlashPixVersion - 0100\\ | ExposureBiasValue - 0.00\\ | | ColorSpace - sRGB\\ | MaxApertureValue - F 3.03\\ | | ExifImageWidth - 2032\\ | MeteringMode - Multi-segment\\ | | ExifImageHeight - 1524\\ | LightSource - Auto\\ | | InteroperabilityOffset - 2380\\ | Flash - Flash fired, auto mode\\ | | ExposureIndex - 140\\ | FocalLength - 7.00 mm\\ | | SensingMethod - One-chip color area | FlashPixVersion - 0100\\ | | sensor\\ | ColorSpace - sRGB\\ | | FileSource - Other\\ | ExifImageWidth - 2032\\ | | SceneType - Other\\ | ExifImageHeight - 1524\\ | | CustomRendered - Normal process\\ | InteroperabilityOffset - 792\\ | | ExposureMode - Auto\\ | ExposureIndex - 140\\ | | WhiteBalance - Auto\\ | SensingMethod - One-chip color area | | DigitalZoomRatio - 0.00 x\\ | sensor\\ | | FocalLengthIn35mmFilm - 46 mm\\ | FileSource - Other\\ | | SceneCaptureType - Standard\\ | SceneType - Other\\ | | GainControl - Low gain up\\ | CustomRendered - Normal process\\ | | Contrast - Normal\\ | ExposureMode - Auto\\ | | Saturation - Normal\\ | WhiteBalance - Auto\\ | | Sharpness - Normal\\ | DigitalZoomRatio - 0.00 x\\ | | SubjectDistanceRange - Unknown | FocalLengthIn35mmFilm - 46 mm\\ | | | SceneCaptureType - Standard\\ | | | GainControl - Low gain up\\ | | Maker Note (Vendor): - | Contrast - Normal\\ | | | Saturation - Normal\\ | | | Sharpness - Normal\\ | | | SubjectDistanceRange - Unknown | +--------------------------------------+--------------------------------------+\n从上表可以看出，PS过后的照片，加入了编辑软件和编辑时间等信息。\n为了确认问题所在，我用Photoshop CS采用保存、另存为和存储副本的方式保存的照片，EXIF信息都没有丢失。\n那么，深绿色的树提到的EXIF信息丢失的问题是怎么产生的呢？通过分析，我估计主要是下面两种情况：\n使用了较老的Photoshop版本。\n通过实验，可以证明Photoshop CS是不会破坏照片的EXIF信息的，CS2当然也不会。但是CS以下的版本（Photoshop 7、6）我没有测试 使用了“存储为Web所用格式”命令保存照片\n使用这个命令保存的jpeg照片会丢失EXIF信息 所以，\nPhotoshop CS不会破坏照片的EXIF信息\n另外还发现内部网络相册的一个问题，当使用另存方式保存相册中的照片时，照片的EXIF信息会丢失。\n","date":"2006-09-20","description":"","lastmod":"2006-09-20T01:15:57Z","slug":"photoshop-cs-exif","tags":["exif","photoshop"],"title":"Photoshop CS不会破坏照片的EXIF信息","url":"https://blog.zengrong.net/post/photoshop-cs-exif/"},{"categories":["others"],"content":"今天收到Writely的邮件，原文如下：\nnoreply@writely.com\nto me More options 3:11 am (5 hours ago)\nDear zrongzrong,\nIn a few days, we will update your Writely account to use your zrongzrong@gmail.com Google Account registration settings.\nThis means you'll need to use your Google Account password when signing in, not your Writely password (if they're different).\nIf you've forgotten your Google Account password, just go to https://www.google.com/accounts/ForgotPasswd\nThanks!\nGoogle Writely Team\n这意味着，今后登录Writely必须使用Google帐户的密码，就像Picasaweb、Spreadsheets、Calendar一样。\n这才像Google的风格嘛。\n","date":"2006-09-20","description":"","lastmod":"2006-09-20T01:15:09Z","slug":"writely-account","tags":["google","weboffice","word"],"title":"Writely将与Google帐户合并","url":"https://blog.zengrong.net/post/writely-account/"},{"categories":["use"],"content":"一直担心900不支持4GB的卡，在论坛和友人上搜了搜，有前辈说可以支持，就去淘宝上买了个kingmax的，520元。\n拿回来一试，确实支持的不错。可是当我通过读卡器向卡里拷大文件的时候，问题发生了。\n我用的是SSK飚王18合一读卡器，复制小文件没有问题。可是当我复制一个400MB的电影时，到200MB左右就复制失败了，提示“参数不正确”。\n我开始怀疑计算机问题，换了台计算机，问题依旧。\n怀疑读卡器的问题，找同事借了个读卡器，她的更加厉害，连文件都不能复制，直接提示“参数不正确”。\n这样可以大致确定是读卡器不支持（虽然读卡器可以正确识别出卡的容量），打电话给kingmax的客服，证实了我这个猜测。\n于是带着卡杀向电脑城，去买新的读卡器。让js帮我做了个1G左右的压缩包，拷贝没问题。30块钱就搞定了。\n需要注意的是，并不一定越贵的读卡器越好。我先测试的一个80块的20合一读卡器（USB2.0），就不支持拷贝文件。如果大家要买读卡器，一定要带上卡去测试，并复制1GB以上的文件试试（大家在900上播放的视频没有超过1G的吧 :em25:）\n","date":"2006-09-18","description":"","lastmod":"2006-09-18T14:42:54Z","slug":"4gsd","tags":["mobile","pda","ppc"],"title":"多普达900与4GB SD卡的故事","url":"https://blog.zengrong.net/post/4gsd/"},{"categories":["technology"],"content":"声明：本人对a6flashmambot的修改，纯属个人兴趣，此插件的版权归原作者所有。\na6flashmambot是一个joomla/mambo插件，用于在joomla/mambo中插入Flash动画。这东东好是好，就是老了点，到现在还是使用微软不推荐的嵌入方式显示Flash动画，在IE下会出现讨厌的“单击以激活此控件”提示。\n我对a6flashmambot做了一下修改，让它更好用，修改的项目如下：\n改用swfobject1.42嵌入Flash动画 支持swfobject的所有参数，包括快速安装 支持FlashVars参数，可将传入的FlashVars分割成多个变量（灵感和实现来自kml_flashembed） 汉化了部分界面，加入了一个“version”默认参数（见图） 至于修改后的版本，就暂定1.3吧，呵呵，希望作者不要介意。\n用法：\n{flash file=\u0026quot;filename.swf\u0026quot; width=\u0026quot;200\u0026quot; height= \u0026quot;100\u0026quot; align=\u0026quot;left\u0026quot; bgcolor=\u0026quot;FFFFFF\u0026quot; base=\u0026quot;images/flash/ quality=\u0026quot;best\u0026quot; play=\u0026quot;true\u0026quot; loop=\u0026quot;true\u0026quot; menu=\u0026quot;false\u0026quot; useExpressInstall=\u0026quot;true\u0026quot; version=\u0026quot;9\u0026quot; FlashVars=\u0026quot;x=1 ; y=2 ; z=3\u0026quot;}\n截图：\n源码下载：\n1 文件 ","date":"2006-09-06","description":"","lastmod":"2006-09-06T08:13:16Z","slug":"a6flashmambot13","tags":["mambo","swfobject","plugin"],"title":"修改a6flashmambot，使用swfobject调用Flash影片","url":"https://blog.zengrong.net/post/a6flashmambot13/"},{"categories":["news"],"content":"SX1用了两年，现在换成多普达900.\n这个巨无霸式的东东已经不能算“手机”了，也许称它为“带电话功能的PDA更好些”。\n我用过的所有手机：\n爱立信T18 - 诺基亚8210 - 诺基亚8250 - 波导（型号不明） - 西门子SX1 - 多普达575 - 多普达900\nSX1之前的手机已经找不到了:em26: 下面是SX1、575和900的合影 :em52:\n","date":"2006-09-01","description":"","lastmod":"2006-09-01T09:47:53Z","slug":"d900","tags":["mobile","pda"],"title":"入手多普达900","url":"https://blog.zengrong.net/post/d900/"},{"categories":["others"],"content":"什么是GFW？\n转自追忆似水年华◆我心思→念\n前几天google上不了，接触到一个新名词，GFW，这个词语是什么意思呢。在网上查了一下，解释如下：\n防火长城\n防火长城，正式名称中国防火墙或中国国家防火墙，指中华人民共和国政府在其管辖互联网内部建立的多套网络审查系统的总称，包括金盾系统和相关行政审查系统。一般情况下主要指中国对互联网内容进行自动审查和过滤监控、由计算机与网络设备等软硬件所构成的系统。\n其英文名称Great Firewall of China（与长城 Great Wall 相谐的效果），简写为Great Firewall，缩写GFW。\n主要技术\n1.国家入口网关的IP封锁\n从90年代初期，中国大陆只有教育网、高能所和公用数据网3个国家级网关出口，中国政府对认为具有颠覆性质的站点进行IP封锁，这是有效的封锁手段。对于IP封锁，用普通Proxy技术就可以绕过。只要找到一个普通的海外Proxy，然后通过Proxy就可以浏览自己平时看不到的资讯了。但网络封锁部门也就开始把人们常用的Proxy加入了IP封锁列表。\n2.主干路由器关键字过滤阻断\n在2002年左右，中国大陆研发了一套系统，并规定各个因特网服务提供商必须使用。思科等公司的高级路由设备帮助中国大陆实现了关键字过滤，最主要的就是IDS（Intrusion Detection System）--- 入侵检测系统。它能够从计算机网络系统中的关键点（如国家级网关）收集分析信息，过滤、嗅探指定的关键字，并进行智能识别，检查网络中是否有违反安全策略的行为。利用这些设备主要进行网址的过滤和网页内容的过滤，如果符合即定的规则，则向用户发送ACK-FIN，自动打断用户与服务器的会话连接，使数据流中断，而在终端电脑上会显示主机无法识别。不同的IDS甚至有可能在一段预定或随机的时间内试图阻止从用户主机发出的所有通信。\n所以在访问境外网站时，如果数据流里敏感字符过多时，会出现网页一闪而过，随后提示“无法访问”的情况，例如Google。而且在接下来的一段时间内，有可能无法访问任何网页。\n关键字过滤的弱点就是对已加密的信息无能为力，而网址的关键字和网页的关键字都可以用不同的手段来加密，从而使这样的信息过滤系统从根本上失去作用。不同的加密手段也是后来所有突破网络封锁软件的基础。\n“思科公司为中国特制了数据包级别的内容过滤路由器（content filtering router），而中国的路由器80％是思科公司的。”正在进行中的“金盾工程”是一个与Novell的合作项目。这个工程将包括生化监控、人工智能、自动识别等技术。\n3.域名劫持\n在世界上一共有十几个根（Root）级别的域名服务器，到目前没有一个安装在中国大陆，所以中国大陆方面不能从根本上控制修改域名。\n2002年左右，中国大陆开始采用域名劫持手段，他们用路由器提供的IDS监测系统来进行域名劫持，防止了人们访问被过滤的网站。同时，为了防止高级用户自己直接使用有正常功能的境外的域名服务器，中国大陆也开始不断地封锁海外的DNS服务器，已经封锁了几百个北美的DNS服务器。\n什么是?？它与?的区别\n转自追忆似水年华◆我心思→念\nTM是TRADEMARK的缩写，美国的商标通常加注TM，并不一定是指已注册商标。而R是REGISTER的缩写，用在商标上是指注册商标的意思，我国商标法实施条例规定，使用注册商标，可以在商品、商品包装、说明书或者其他附着物上标明“注册商标”或者注册标记。注册标记包括（注外加○）和（R外加 ○）。使用注册标记，应当标注在商标的右上角或者右下角。因此，TM与R是不同国家的商标标记，没有特别的关系，也有一些国内公司不了解法律规定，一味模仿美国公司，在商标上使用TM标记\n在中国，商标上的TM也有其特殊含义，其实TM标志并非对商标起到保护作用，它与R不同，TM表示的是该商标已经向国家商标局提出申请，并且国家商标局也已经下发了《受理通知书》，进入了异议期，这样就可以防止其他人提出重复申请，也表示现有商标持有人有优先使用权。\n圆圈R，是“注册商标”的标记，意思是该商标已在国家商标局进行注册申请并已经商标局审查通过，成为注册商标。圆圈里的R是英文register注册的开头字母。 注册商标具有排他性、独占性、唯一性等特点，属于注册商标所有人所独占，受法律保护，任何企业或个人未经注册商标所有权人许可或授权，均不可自行使用，否则将承担侵权责任。 用TM则是商标符号的意思，即标注TM的文字、图形或符号是商标，但不一定已经注册（未经注册的不受法律保护）。TM是英文trademark的缩写。\n为什么说大学是象牙塔？\n转自追忆似水年华◆我心思→念\n象牙塔可不是新词..\n现在多指高等学府，其实详细的解释在下面这个大段...里..\nIvory Tower(象牙塔)根据圣经《旧约雅歌》(the Old Testament, song of songs)第7章第4节，睿智富有的以色列王所罗门(Solomon)曾作诗歌1005首，其中《雅歌》都是爱情之歌。在第五首歌中，新郎是这样赞美新娘的，\u0026quot;…Your neck is like an ivory tower. Your eyes are pools in Heshbon,by the gate of Bath-rabbim….\u0026quot;(……你的颈项如象牙塔；你的眼目像希实本巴特那拉并门旁的水池；……)。很清楚这里的\u0026quot;象牙塔\u0026quot;只是用来描述新娘美丽的颈项。这个词后来被逐渐运用到社会生活的各方面，主要是指\u0026quot;与世隔绝的梦幻境地、逃避现实生活的世外桃源、隐居之地\u0026quot;。\n在汉语中，象牙塔的外延涵义主要是指\u0026quot;比喻脱离现实生活的文学家和艺术家的小天地\u0026quot;。大学，研究院正是这种地方。\n中国的手机号为什么以13开头？以159开头的新手机号开始发号了。\n转自追忆似水年华◆我心思→念\n大家都知道以前电信移动邮政还没有分家时，邮电资源是邮电部（现信息产业部）统一调度的。在分配号段的时候，做了一些细致的规划，大体是这样：\n10开头，电信服务号码，如103国际半自动挂号，108国际对方付费电话，1000电信服务中心，10010联通服务中心等等。\n11开头，赋予特种服务号码，如110匪警，111电信内部测试，112报修， 113、115国内人工长途挂号，114查号台，116国内人工长途查询，117报时，119火警等\n12开头，赋予民用特殊号码，如120（医院），121（天气预报），122交 通事故告警，126、127、128、129寻呼台（BP机时代）。\n所以分配到手机用户时，以13开始做号段。后来分配130~133 为联通，134~139为移动。\n其他的特殊号段号码有：\n16，声讯类，如160中国电信工人信息服务接入码，166语音信箱业务， 167吉通计算机互联网业务接入码，168声讯服务，中国电信公众多媒 体网接入码等 \n17，长途电话服务，如170国内长途全自动话费查询台，173国内立接制长 途半自动挂号台，176国内长途半自动查询台，177国内长途半自动班 长台，179IP语音服务接入码等\n18，部分服务台，如180邮政服务，184邮政编码查询接入码，185国家邮 政局电话信息服务接入码，186移动服务中心，188固定电话交费台， 189中国电信业务受理特服台等。\n19，寻呼接入，191联通无线电人工寻呼接入码，192联通无线电自动寻呼 接入码等等 20，30，电话卡服务\n另外，8、9开头的号段部分也作电信及民用服务号码\n以上号码均作分配，内容太多未详细列出，部分号码目前已经升级，如电信服务台，天气预报，邮政服务台等等\n上网查了一下，据说159是3G手机用户的号码，移动159，联通153。四川，重庆，西安，北京，上海，石家庄，山东，郑州等省市已经开始159号段放号了。\n“159”号段是“中国移动”自2004年底发放“134”后再次扩充的号码资源。到目前为止，我国“13”开头的号码已经全部发放完毕，其中134-139属于“中国移动”GSM网络，130-132为“中国联通”GSM网络，133属于“中国联通”CDMA网络。\n目前全国手机用户已突破4亿户，用户数稳居全球第一。发放159号段的原因，首先是号码资源紧张。记者从杭州移动了解到，除了134目前入网用户还不多以外，其他几个号段的用户量正在接近饱和。整个中国移动目前拥有2.2亿多户用户，并且这个数字还以每月300多万户的速度增长，号码资源明显不足，因此需要新的号段。\n业界预计，159号段今后主要用于3G手机的放号。对此中国移动的人士表示，159首先将用于2G手机，但未来也会应用于3G，属于兼容两代网络的手机号段。\n中国移动工作人员还表示，无论是13开头的手机还是15开头的，今后都可以从2G网过渡到3G，享受3G网络的关键并不在于号码，而是得有一部3G手机。\n","date":"2006-08-24","description":"","lastmod":"2006-08-24T01:36:09Z","slug":"little-knowledge","tags":[],"title":"几个小知识，我一直都想知道的","url":"https://blog.zengrong.net/post/little-knowledge/"},{"categories":["others"],"content":"没什么好说的了，看把:\n(新华网)在国内外的一片反对声中，日本首相小泉纯一郎还是在8月15日参拜了供奉着二战甲级战犯的靖国神社。小泉以下台前的这次最后表演，再次挑战国际正义、践踏人类良知。\n众所周知，靖国神社里面供奉着14名二战甲级战犯的牌位，具有浓重的军国主义色彩。小泉对双手沾满鲜血的甲级战犯顶礼膜拜，不仅伤害了二战受害国人民的感情，违背了日本宪法政教分离的原则，也是对远东国际军事法庭审判结果的公然蔑视和对国际公理的挑衅。\n面对全世界人民的质疑，小泉一再声称参拜是他的“个人心灵问题”，希望以此来模糊人们对参拜问题的是非判断。但中韩等国人民和政府的坚定态度已经说明，参拜靖国神社问题，是日本与邻国和睦相处绕不开的话题。对这一点，小泉心知肚明，只不过他更希望通过参拜靖国神社，争取日本右翼势力支持，捞取个人政治资本。\n(新华网)中国外交部强烈抗议日首相小泉再次参拜靖国神社\n中华人民共和国外交部15日发表声明，强烈抗议日本首相小泉纯一郎再次参拜靖国神社。声明说 今天，日本首相小泉纯一郎又一次参拜供奉有二战甲级战犯的靖国神社。中国政府对这一严重伤害日本军国主义侵略战争受害国人民感情、破坏中日关系政治基础的行径表示强烈抗议。 (新华网)韩国称对小泉再次参拜靖国神社表示\u0026quot;失望和愤怒\u0026quot;\n韩国外交通商部发言人秋圭昊１５日代表韩国政府发表声明，对日本首相小泉纯一郎当天再次参拜靖国神社表示十分“失望和愤怒”。 声明说，小泉不顾国际社会一再表示担忧和反对的立场，多次参拜供奉有二战甲级战犯的靖国神社，导致韩日两国关系恶化，损害了东北亚地区国家间的友好合作关系。\n(新华网)朝鲜表示决不容忍日本过去的罪恶\n朝鲜劳动党中央机关报《劳动新闻》１５日发表评论说，朝鲜人民决不会忘记也决不会容忍日本过去的罪恶。评论说，日本在对朝鲜半岛实行的殖民统治期间扼杀了朝鲜民族的灵魂和智慧，破坏了朝鲜民族悠久的历史文化传统，掠夺了朝鲜的一切，使朝鲜民族远离了现代文明发展的轨道。尤其是日本当年强征８４０万朝鲜青壮年充当苦役和炮灰，强征２０万朝鲜妇女充当“慰安妇”，犯下了“不可饶恕的罪行”。\n网易网友 发表ip：60.16.75.* 认为：回复 精华推荐 支持(5986) 反对(130) 2006-08-15 08:09:14\n8-15,是日本投降日，中国搞什么庆祝活动我不知道。但近年日本首相要做什么我是知道的，小犬蠢一狼去靖国神社拜鬼。接下来的反应我也是知道的，中国提抗议或者强烈抗议。\n多说一句。在中国各地有太多抗日的忠烈祠，各地的官员有去吗？！8-15这天就现得更冷清。由于宣传低调，像安葬有140万抗日烈士的衡山忠烈祠的名字都鲜有人知，因为那里是“国军”，让我们站在人民英雄纪念碑下，朗读碑文吧！-----“三年以来，。。。三十年以来，。。。由此上溯到一千八百四十年来。。。。的人民英雄们永垂不朽！”\n爱国不分民族党派，这一天我们也该国家公祭！！！\n网易网友 发表ip：61.185.128.* 认为：回复 精华推荐 支持(576) 反对(5) 2006-08-15 08:42:11\n小鬼子，我等着你！\n你第一次到我这里，\n那时我叫大唐，\n威震世界我强盛无比。\n你赤著双脚、衣衫褴凇\n诚惶诚恐你走进我的光辉大殿里。\n我记得我叫秦的时候曾让徐福，\n带领三千童男童女，\n远渡东海，\n扎根到你那里。\n因此我认定：\n有我的血液流到你的血管里。\n对於你的潦倒，\n我没有嫌弃。\n我给了你锦衣朝服，\n我盛唐全部礼仪、\n和那双你穿到现在的木屐。\n你千恩万谢，\n满口“哈依、哈依”。\n你藏不住那贪婪的目光，\n我告诫自己--\n这是一个人面兽心的家夥，\n就象在图上：\n它的形状象一只可恶的虫蚁。\n仁至义尽我送你回去，\n还教给你我盛世的全部礼仪。\n你走之後我轻声自语：“\n它还会回来”--\n我等著你。\n当我叫明的时候我等到了你。\n你手拿倭刀，\n穿著我教你做的唐衣。\n你说你并不得已，\n因爲後面有驱赶你的丰臣秀吉。\n杀人放火你奸淫掠虏，\n戚家儿郎把你赶下海去。\n用东海水我洗著伤口，\n贼心不死的禽兽--\n我等著你！\n日月如梭我身染重疾，\n东方的巨人渐渐不能自己。\n围攻撕咬我的兽群中，\n我又看见了你：\n强盗火拼你咬走了俄国熊罴，\n独占我北方要地。\n贪心不足你膨胀的恶欲。\n终於到了“九.一八”那是一九三一，\n血肉从我身上分离，\n於是有了僞满供你驱骑。\n欲壑难平你得寸进尺，\n疯狂的野兽你竞妄想把世界归己。\n一九三七的七月七，\n我的胸膛上你印上了铁蹄。\n作威作福你那麽得意，\n心在淌血我把仇恨铭记。\n多行不义你必自毙，\n自作自受--\n蘑菇云中你看见了自己的广岛和长崎。\n夹著尾巴你滚了回去，\n还有那面沾满血腥的膏药旗。\n跟在霸强後面，\n你又觉得有势可倚。\n投机取巧你开始发迹，\n一夜之间你觉得富得无人可比。\n不改的本性让你又暗藏杀人的利器，\n打著自卫的幌子想把世人蒙弊。\n爲富不仁你开始觉得自己家里挤，\n又妄想到我的岛上来“钓鱼”！\n伤疤犹在你就忘了痛，\n参拜亡灵的政客们啊，\n在靖国神社你们是否看见了东条英机？\n往日的屈辱我怎能忘记？\n昨天的病夫现在已有了强壮的身躯！\n睁大眼睛我看你要向何处去？\n还想再来吗？\n--世世代代我等著你！\n--我等著你！！！\n","date":"2006-08-15","description":"","lastmod":"2006-08-15T05:06:01Z","slug":"jingguoshenshe","tags":[],"title":"小泉又拜鬼","url":"https://blog.zengrong.net/post/jingguoshenshe/"},{"categories":["technology"],"content":"现在，许多播客网站都提供了在线上传视频的服务，而大多数都是将上传的视频转换成FLV格式，然后利用Flash Player播放。大名鼎鼎的Google Video就是其中代表。可惜的是，Google Video并不支持RM和RMVB格式的上传转换。但是，配合是用我们今天介绍的两个软件，却可以转换几乎所有流行的视频格式。:em68:\n第一个软件，MEncoder，这是一个从Linux移植过来的免费视频转换工具。\n相关资源：\n在线转FLV，支持RM，RMVB，RV，WMV9等所有媒体格式。 在线一次性转FLV，支持RM，RMVV等所有媒体格式。 WisMencoder，基于MEncoder的国产免费视频转换软件 WinMEnc，基于MEncoder的绿色免费视频转换软件 MEncoder图形界面WinMEnc快速压制MP4.AVI教程(体现N80细腻屏幕的完美解决方案) WinMEnc 0.61 beta - 自製Windows Movie Encoder (mencoder frontend) 第二个软件，FFMPEG，利用它可以将AVI转换成FLV，同时可以实现从FLV抓图等功能。许多网站中的录制FLV后显示缩略图的功能就是用它实现的。\n相关资源：\nffmpegGUI(英文) FFMPEG tutorial(英文) FFMPEG功能和参数 从FLV中截图以及相关PHP代码 Flash 视频(FLV)编码,转换,录制,播放方案一网打尽 ","date":"2006-08-12","description":"","lastmod":"2006-08-12T11:12:32Z","slug":"mencoder-ffmpeg","tags":["ffmpeg","flashplayer","flv","mencoder"],"title":"两个免费转换视频的软件，实现服务器端Video2FLV","url":"https://blog.zengrong.net/post/mencoder-ffmpeg/"},{"categories":["technology"],"content":".Net 所使用的ADO.Net较前身ADO在性能上有较大的提升，但是操作起来也较为繁琐，在 Asp时代，对数据进行分页使用 RecordSet 的 PageSize 和 AbsolutePage即可完成对数据的分页操作，非常简单，进入 .Net 后，RecordSet 对象没有了，取而代之的是 DataSet/DataTable，没有了 PageSize/AbsolutePage，分页成了一件非常头痛的事。\n所有的 Asp.Net 初级教程中都会讲解如何使用 DataGrid 控件进行基于 Post 的分页，当我练习完这个例子后从来没有在实战中使用过，原因有二：\n代码过于复杂； 不支持url分页，不利于搜索引擎和调试，定制度不高。 所以建议大家也都不要用这个分页了，呵呵，真垃圾。\nAsp.Net 能不能像 Asp 一样简单地分页呢？答案是可以的，要使用 PageDataSource 类，这里有篇很完整的教程给大家参考，一位高手的Blog。\n使用 PageDataSource 很方便，但它也有个致命的缺点，就是性能上的下降，偶对一张10000条数据的表格进行了 Asp/Asp.Net(DataGrid)/Asp.Net(PageDataSource) 三项分页速度测试，结果分别是 600ms-700ms / 500ms / 900ms-1000ms ，使用了 PageDataSource 分页速度竟然比 Asp 都慢，郁闷，原因大概是因为作了二次数据绑定造成的吧，大家可以自行测试，看是否和我测试的结果相同。\n精华部分到啦……\n要提高分页的性能，还得使用自定义分页，首先我们来看看传统的 Asp 分页时需要哪些参数？\nPageSize（每页显示记录条数） RecordCount（总记录数） PageCount（总页数） AbsolutePage（绝对页）。 回到 Asp.Net，我们来做一个例子，我们对 /NewsList.asp?Page=3 进行分页，可以这样做：\n1int PageSize=20; //定义每页显示的记录条数 2int RecordCount=”select count(*) from news”; //这句不完整，大家自行完善 3int PageCount=RecordCount/PageSize; //总页数算出来啦 4int CurrentPage=Convert.ToInt32(Request.QueryString[“Page”].ToString()); 关键时候到了，需要向 DataSet 中填充数据，注意这个技巧：\n1dAdapter.Fill(dSet, (CurrentPage - 1) * PageSize, PageSize, \u0026#34;Table\u0026#34;); 这一句可以向DataSet中填充指定位置的数据，这样就可以达到分页的效果啦。\n经测试，这样的分页因向DataSet中填充数据量的减少，降低了服务器内存消耗，页面执行速度大约在 120ms-170ms，爽啊！:em31:\n","date":"2006-08-10","description":"","lastmod":"2006-08-10T13:31:39Z","slug":"asp-net-pages","tags":["aspnet"],"title":"Asp.Net 数据分页","url":"https://blog.zengrong.net/post/asp-net-pages/"},{"categories":["technology"],"content":"基于fczone改写的framework.asc（详见让FMS中的trace支持Object），我稍作修改，加入了对Array和Object的判断，做成了这个trace2()函数，使用前只需先load(\u0026quot;trace2.acs\u0026quot;)即可，不需要载入framework.asc了：\n8月13日更新：解决了不处理Object的问题\n1function trace2(data){ 2if(typeof(data) == \u0026#34;object\u0026#34;){ 3if(data instanceof Stream || data instanceof File || data instanceof LoadVars || 4data instanceof XML || data instanceof XMLSocket || data == application) { 5// 6trace(data.toString()); 7}else if(data instanceof SharedObject){ 8var dat = new Object(); 9// 10trace(\u0026#34;[object SharedObject name=\u0026#34;+data.name+\u0026#34;]\u0026#34;); 11// 12var props = data.getPropertyNames(); 13for(var i in props) { 14if(props[i]){ 15var o = data.getProperty(props[i]); 16trace(\u0026#34;\\t\u0026#34;+props[i]+\u0026#34; = \u0026#34; + objString(o)); 17} 18} 19// 20}else{ 21trace(objString(data)); 22} 23}else{ 24trace(data.toString()); 25} 26} 27 28/*將內容對象轉換成字符串 29* @param data 要被轉換的對象 30* @param ignore 需要跳過的屬性 31*/ 32function objString(data, ignore){ 33var msg; 34if(typeof(data) == \u0026#34;object\u0026#34;){ 35if(data instanceof Array){ 36msg = objectString(data, ignore, \u0026#34;Array\u0026#34;); 37}else if(data instanceof Object){ 38msg = objectString(data, ignore, \u0026#34;Object\u0026#34;); 39}else if(data instanceof Client){ 40msg = \u0026#34;[object Client ip=\u0026#34; + data.ip + \u0026#34; uri=\u0026#34;+data.uri+\u0026#34; args=\u0026#34; + objString(data.__ARGUMENTS__, data)+\u0026#34;]\u0026#34;; 41}else if(data instanceof NetConnection){ 42msg = \u0026#34;[object NetConnection connected=\u0026#34; + data.isConnected + \u0026#34; uri=\u0026#34;+data.uri+\u0026#34;]\u0026#34;; 43}else{ 44msg = data; 45} 46}else{ 47msg = data.toString(); 48} 49// 50return msg; 51} 52 53/* 54將參數中的對象轉換成字符串。此函數與objString的區別是，objString轉換所有的對象類型，而objectString僅轉換Object對象類型 55* @param data 要被轉換的對象 56*/ 57function objectString(data, ignore, type){ 58/*由于Remoting返回的實際上是關聯數組而非對象，因此這些“對象”其實屬于Array類型 59这样就给判断真正的类型造成了难度。因为联合数组实际上是没有length属性的，必须用for in循环来获取其中的值 60通过判断length是否存在，是用不同的循环获取 61*/ 62if(type == \u0026#34;Array\u0026#34;){ 63if(data.length){ 64var msg = \u0026#34;[\u0026#34;; 65for(var i=0; i 66if(data[i] == ignore){ 67continue; 68} 69msg += objString(data[i]); 70if(i != data.length - 1){ 71msg += \u0026#34;, \u0026#34;; 72} 73} 74msg += \u0026#34;]\u0026#34;; 75return msg; 76}else{ 77//如果是关联数组，就把它变成一个真正的对象，并调用对象处理程序 78var o = new Object(); 79for(var i in data){ 80o[i] = data[i]; 81} 82return objectString(o, null, \u0026#34;Object\u0026#34;); 83} 84}else if(type == \u0026#34;Object\u0026#34;){ 85//如果是真正的Object 86var msg = \u0026#34;{\u0026#34;; 87for(var i in data){ 88if(data[i] == ignore){ 89continue; 90} 91msg += i + \u0026#34;:\u0026#34; + objString(data[i]); 92msg += \u0026#34;, \u0026#34;; 93} 94msg = msg.slice(0, -2); //去掉最后的一个逗号和一个空格 95msg += \u0026#34;}\u0026#34;; 96return msg; 97} 98} ","date":"2006-08-10","description":"","lastmod":"2006-08-10T01:12:25Z","slug":"trace2-in-fms","tags":["fms","trace"],"title":"trace2 in FMS","url":"https://blog.zengrong.net/post/trace2-in-fms/"},{"categories":["technology"],"content":"今天在 fczone 看到，它通过改写scriptlib中的framework.asc，让FMS中的trace()可以支持打印Object，如下图所示。\n同时还让load()支持一次载入多个文件，就像下面这样：\n1load(”components.chat”, “webservices.WebServices”); 这确实是很方便的功能，大家有兴趣的话可以去 fczone 下载。\n","date":"2006-08-06","description":"","lastmod":"2006-08-06T02:11:52Z","slug":"fms-new-trace","tags":["actionscript","fms","trace"],"title":"让FMS中的trace支持Object","url":"https://blog.zengrong.net/post/fms-new-trace/"},{"categories":["technology"],"content":"发现一个挺有趣的网站http://www.guidebookgallery.org/，居然搜集了大量软件的历史截图。在上面找到了Flash1.0－8.0的所有启动界面，缅怀一下吧……因为从9.0开始，Flash就改姓Adobe了。\n其实，Flash3.0之前也不姓Macromedia的，这是不是又是一个轮回？Macromedia当年收购Future Splash的时候，有没有想到自己因为Flash被收购的一天？\n以下图片来自http://www.guidebookgallery.org/splashes/flash\n还有Photoshop所有版本的截图以及Windows所有版本界面。挺有趣的……\n","date":"2006-08-05","description":"","lastmod":"2006-08-05T14:11:49Z","slug":"236","tags":["flash"],"title":"Flash1.0－8.0启动界面","url":"https://blog.zengrong.net/post/236/"},{"categories":["technology"],"content":"由于项目中经常要使用Remoting，便写了这个测试程序来测试Remoting中的函数执行是否成功。最先写了个基于Service类的，随后写了基于NetConnection类的。\n其实，个人感觉基于NetConnection的更方便使用，如果要在FMS中连接Remoting，就只能选择NetConnection了。\n以下是两个测试程序，可以填入自己的gateway地址、className和函数名称进行测试。也可以填入参数，如果有多个参数，用英文半角逗号分隔。\nzrong.testClass中可以用的函数列表如下：\nBackString(msg:String):String BackArray(Void):Array BackBool(Void):Bool Test(Void):Array 请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2006-08-04","description":"","lastmod":"2006-08-04T09:21:44Z","slug":"remoting-test","tags":["flash","flash-remoting"],"title":"两个Remoting测试器","url":"https://blog.zengrong.net/post/remoting-test/"},{"categories":["technology"],"content":"我一直认为，在FMS中，没有定义在Client对象中的变量都是全局的（不包括函数中定义的局部变量）。但是我发现我的理解大错特错了。 :(\n为了测试\u0026quot;全局\u0026quot;变量的作用域，我写了下面这段代码（服务器端）：\n1var aa = 1; 2var bb = 2; 3var cc = 3; 4application.onConnect = function(newClient){ 5this.acceptConnection(newClient); 6} 7Client.prototype.test = function(a, b, c){ 8trace(\u0026#34;更改之前aa:\u0026#34;+aa); 9trace(\u0026#34;更改之前bb:\u0026#34;+bb); 10trace(\u0026#34;更改之前cc:\u0026#34;+cc); 11aa = a; 12bb = b; 13cc = c; 14trace(\u0026#34;更改之后aa:\u0026#34;+aa); 15trace(\u0026#34;更改之后bb:\u0026#34;+bb); 16trace(\u0026#34;更改之后cc:\u0026#34;+cc); 17} 对应的客户端代码：\n1var nc1:NetConnection = new NetConnection(); 2var nc2:NetConnection = new NetConnection(); 3nc1.connect(\u0026#34;rtmp:/test/1\u0026#34;); 4nc2.connect(\u0026#34;rtmp:/test/2\u0026#34;); 5btn1.onPress = function() { 6nc1.call(\u0026#34;test\u0026#34;, null, 4, 5, 6); 7}; 8btn2.onPress = function() { 9nc2.call(\u0026#34;test\u0026#34;, null, 7, 8, 9); 10}; 执行的结果如下：\ntest/1的执行结果\ntest/2的执行结果\n从上面的结果看，我以前认为的\u0026quot;全局\u0026quot;变量，仅仅只是初始值是\u0026quot;全局\u0026quot;的！ :em11: 而当某个Client改变它之后，就\u0026quot;变成\u0026quot;了Client变量了。\n那么，application中的变量又如何？\n再看看修改过的服务器端代码（客户端代码不变）：\n1application.onAppStart = function(){ 2this.aaa = 1; 3this.bbb = 2; 4this.ccc = 3; 5} 6application.onConnect = function(newClient){ 7this.acceptConnection(newClient); 8} 9Client.prototype.test = function(a, b, c){ 10trace(\u0026#34;更改之前application.aaa:\u0026#34;+application.aaa); 11trace(\u0026#34;更改之前application.bbb:\u0026#34;+application.bbb); 12trace(\u0026#34;更改之前application.ccc:\u0026#34;+application.ccc); 13application.aaa = a; 14application.bbb = b; 15application.ccc = c; 16trace(\u0026#34;更改之后application.aaa:\u0026#34;+application.aaa); 17trace(\u0026#34;更改之后application.bbb:\u0026#34;+application.bbb); 18trace(\u0026#34;更改之后application.ccc:\u0026#34;+application.ccc); 19} 执行的结果如下：\ntest/1的执行结果\ntest/2的执行结果\n看来，application中的变量是一样的\u0026quot;下场\u0026quot;。 :em10:\n8月16日，小结\n经过上面的测试已经弄明白，保存在Client中的变量对于每个客户机都是不同的，application中的变量则可以在instance中共享，instance之间不能共享变量。（使用一些变通的方法是可以的）\n","date":"2006-08-02","description":"","lastmod":"2006-08-02T01:33:43Z","slug":"fms-variable","tags":["actionscript","fms"],"title":"FMS中变量的作用域","url":"https://blog.zengrong.net/post/fms-variable/"},{"categories":["others"],"content":"上周六帮老爸朋友弄电脑，说是什么经常掉线，有时上几分钟掉，有时几个小时都不掉，但电信人员上门又没查出问题…… 有点棘手啊！~\n去现场查看了一下，一个路由器带三家，查看路由器工作状态，没发现异常，开始勘察三台电脑，其中一家没有人，电脑关着，另一家小孩在玩网游，自家这台在看网页，个把小时过去了，没出现掉线的情况，于是准备去吃饭……\n就在这时，老爸同事说，我现在找个电影下载，吃完饭回来看下载中断没有就知道是不是掉线了，于是，他启动了BT，开始下载电影，结果没有一个字节下载成功，网卡指示灯的下行灯不闪了，我突然想到网上一篇文章，北京网通禁BT一事，赶紧开网页，呵呵，都打不开了，看邻居小孩家网游也断了，TNND，原因找到了，电信把BT给禁了……\n断线重拨再试BT，百试不爽，就是这个原因，唉！~~~ 被中国电信忽悠了\n","date":"2006-07-28","description":"","lastmod":"2006-07-28T15:37:23Z","slug":"220","tags":[],"title":"暴强的中国电信, 汗!~~~","url":"https://blog.zengrong.net/post/220/"},{"categories":["technology"],"content":"最近聊天联盟系统要升级到 Asp.Net 环境，.Net环境提供了更为强大的应用程序通讯能力，那么原来系统所采用的基于 http 的 post 方式我们决定舍弃。\n聊天室程序与Asp.Net的通讯包括两个方面，一是客户端swf与Asp.Net的通讯，二是服务器端FMS与Asp.Net的通讯，所谓通讯，就是访问Asp.Net来获取数据，在Asp环境下只能通过http的post/get获取返回的纯文本格式的数据，再根据需要进行拆分数组及类型转换，但在.Net环境下，Flash能与Asp.Net直接交换不同的数据类型，而不需要再作拆分和转换。\n这种新的通讯方式有两种方案，一种是比较热门的SOAP方式，通过在.Net端编写WebService可以向Flash返回各种类型的数据，不过这个方案立即被我们否决了，原因有二，首先，WebService一个文件只能返回一个数据，交互多了文件不好维护；其次，WebService不认Session，对于需要Session认证的客户端swf而言不安全……\n以上是一点辅垫……\nFlash Remoting是Flash的一个组件，在Asp.Net中，需要在站点的/bin/目录下放入flashgateway.dll，并在站点的web.config文件的httpModules节点中加入\u0026lt;add name=\u0026quot;GatewayController\u0026quot; type=\u0026quot;FlashGateway.Controller.GatewayController,flashgateway\u0026quot; /\u0026gt;，完成这两项工作后就可以在Flash中编写Remoting来访问Asp.Net了（这部分教程由zrong提供，偶只写Asp.Net部分，呵呵）\nFlash Remoting在与Asp.Net交换数据时有多种方式，归纳起来为：\n一、直接访问Asp.Net文件，aspx文件中有Flash控件\n二、访问带cs后代码的Asp.Net文件，aspx文件中有Flash控件\n三、访问WebService，即asmx 文件\n四、访问dll文件\n前两种本质上没有区别，而且不支持在一个文件中定义多个函数，不作介绍，第三种有点脱XX放X的感觉，重点看第四种，第四种其实是第二种的升级版本，我们可以在cs文件中将一个普通的aspx.cs编译为dll进行调用，这样就不需要实际存在一个aspx文件，因为访问是通过代理的方式访问一个空白aspx页面实现的，这个代理是操作是通过httpModules实现的，核心代码在flashgateway.dll中……具体的过程不是很清楚，但原理相信是一种基于http的特殊封包协议，与SOAP比较相近，可能也正是因为这个原因，和Flash进行交互的aspx程序中的Request对象就失效了（即程序中无法通过Request对象下的各个集合如Form/QueryString/Url进行取值了）\n完全不了解Flash Remoting的朋友看到这篇文章可能会很糊涂，有时间偶做张图大家看看应该会比较清晰了\n","date":"2006-07-24","description":"","lastmod":"2006-07-24T13:11:38Z","slug":"219","tags":["aspnet","flash","flash-remoting"],"title":"初探 Flash Remoting+Asp.Net","url":"https://blog.zengrong.net/post/219/"},{"categories":["technology"],"content":"做的一个项目需要加入换肤功能，为了方便维护，将皮肤做成swf放在主文件外部，调用主文件时，首先会载入皮肤文件。但是，项目的主要功能做在影片剪辑中，并继承MovieClip类。如果要在这个影片剪辑中载入皮肤文件，则会冲掉原影片剪辑中的所有内容（如题）。\n我的影片剪辑中并没有内容，它绑定一个类文件，所有的组件都是利用类文件中的方法从库中动态载入的。既然影片剪辑中没有内容，能否就直接载入皮肤文件呢？首先做个实验再说：\n1createEmptyMovieClip(\u0026#34;a\u0026#34;, 0); 2mcl = new MovieClipLoader(); 3a.fun = function() { 4 trace(\u0026#34;A!\u0026#34;); 5}; 6onLoadInit = function (mc) { 7 trace(\u0026#34;load b.swf ok\u0026#34;); 8 trace(\u0026#34;load done, a.fun:\u0026#34;+mc.fun); 9}; 10mcl.addListener(this); 11mcl.loadClip(\u0026#34;b.swf\u0026#34;, a); 12trace(\u0026#34;load start, a.fun:\u0026#34;+a.fun); 测试的结果如下：\nload start, a.fun:[type Function]\nload b.swf ok\nload done, a.fun:undefined\n可见，loadMovie不仅替换掉了影片剪辑中的所有视觉内容，还清除掉了所有的变量、函数。看来项目必须从继承改为合成了。\n","date":"2006-07-21","description":"","lastmod":"2006-07-21T01:48:42Z","slug":"loadmovie","tags":["flash"],"title":"将影片loadMovie入一个影片剪辑后，会替换原影片剪辑中的所有内容","url":"https://blog.zengrong.net/post/loadmovie/"},{"categories":["technology"],"content":"一直有网友反映在多媒体空间上下载的文件不全而导致无法解压缩。由于我无法重现错误，就以为是网友的个别现象。但是自从发布了Flash 8 教学辅助软件制作培训班所有相关资源下载后，有许多老师反映下载的录屏文件无法解压。我自己实验了一下，也确实有这样的问题。\n问题主要表现在文件总是下载到4MB就停了，不论是使用IE直接下载还是使用迅雷等下载工具，下载下来的文件都不完整，导致无法解压。但是，直接引用文件名称下载，则没有这个问题。\n我的下载系统是DOCMAN1.3RC2，直接使用下载系统中的链接下载，就无法完整下载；而直接使用文件地址下载则可以完整下载。\n这个奇怪的问题，最终在orphen的帮助下得以解决。\n解决方法：\n在php.ini中把max_execution_time调整为更大的数值就可以了，我改成了1200。\nmax_execution_time的作用是限制最长程序执行时间（单位秒），用于帮助服务器结束死循环等程序。可能DOCMAN是利用PHP实现文件下载，php.ini的默认值是30秒，如果文件很大，30秒以内下载不完，PHP就会自动结束文件流而导致下载无法成功。我估计20分钟的时间，对于下载一个100MB以内的文件就够用了。如果要下载更大的文件，将其值改为0（0代表无限制）即可。\n","date":"2006-07-18","description":"","lastmod":"2006-07-18T08:11:11Z","slug":"docman-2","tags":["cms","mambo","mysql","php","plugin"],"title":"解决使用DOCMAN下载的文件不全的问题","url":"https://blog.zengrong.net/post/docman-2/"},{"categories":["tutorial"],"content":"注：本文为Flash8辅助教学软件制作技术培训班7月14日下午授课内容\n推荐图书：\n书名：《ActionScript 权威指南》\n购买网址：http://www.china-pub.com/computers/common/info.asp?id=9295\n作者网站：http://moock.org/\n推荐度：★★★★\n难易度：★★★\n书名：《Flash第一步：基础篇》\n购买网址：http://www.china-pub.com/computers/common/info.asp?id=29678\n推荐度：★★\n难易度：★\n书名：《Flash第一步：ActionScript编程篇》\n购买网址：http://www.china-pub.com/computers/common/info.asp?id=29677\n推荐度：★★\n难易度：★★\n书名：《FLASH MX 2004 ACTIONSCRIPT 2.0与RIA应用程序开发》\n作者：颜金桫 KCLY小土豆工作室\n购买网址：http://www.china-pub.com/computers/common/info.asp?id=24417\n作者的博客：http://www.luar.com.hk/flashbook/\n推荐度：★★★\n难易度：★★★★\n推荐网站：\nhttp://www.dengjie.com/\n★★★★★ http://ria.richtechmedia.com/\n★★★ http://www.klstudio.com/\n★★★ http://www.webstudio.com.cn/blog/\n★★★★★ http://www.kingda.org/blog/\n★★★ http://www.sephiroth.it/\n★★★★ http://www.blueidea.com/bbs/\n★★★★ http://bbs.flash8.net/forums/\n★★★ 软件下载：\nhttp://www.onegreen.net/ http://www.crsky.com/ http://www.hanzify.org/ 软件介绍：\n图像处理：Fireworks 音频处理：Goldwave ","date":"2006-07-14","description":"","lastmod":"2006-07-14T13:08:37Z","slug":"actionscript-site-book","tags":["actionscript","book","flash","training"],"title":"推荐给ActionScript初学者的书籍和网站","url":"https://blog.zengrong.net/post/actionscript-site-book/"},{"categories":["impressions"],"content":"共50个常见和不常见的骗局，希望大家看后不要受骗。\n我也曾经介绍过几个我亲身经历的骗局和碰到的骗子：\n健力宝拉环骗局 伯汉网络不要脸 请不要相信这个网站（1） 请不要相信这个网站（2） 以下内容转自闪客天空的Blog\n骗术一：通常是一男一女带个小孩，遇到模样老实的( 比如我 )，上前说，钱丢了，没钱回家，给几块钱坐车吧。还有说是来深圳找亲戚，没找着，钱用光了，给几块钱给他们买点吃的。第一次遇到这种骗术往往容易上当，我在大学时就上当过。骗子骗了几块钱，马上又去骗别人。在深圳路边遇到过多次，广州一般出现在好又多，万佳等超市附近。\n骗术二：路边某个青年男子，带着个包，坐在地下，用粉笔在地下写一些什么“找不到工作，太饿了，请好心人给点钱买东西吃”。一样是骗子，如果到深圳、广州所有地方走一圈，保证可以找到上百个这样的骗子。\n骗术三：大学时室友遇到过一次，来深圳后本人遇到过三次。大概流程是这样的：当你从银行取钱出来，或者到邮政局存钱的话，旁边会有两个串通好的男子，一个假装把一捆钱丢在地上，往前走，后面的骗子故意在你面前将钱捡起来，然后把你拉到一边，把捡到的那叠钱放到你身上。而前面丢钱的骗子则返回，问后面的骗子是否捡到了钱，并且要后面的骗子搜身。后面的骗子说没有，然后把你拉到一边，说捡到的钱平分，现在钱在你身上，我现在跟他去搜身，我怕你在我去搜身后逃之夭夭，所以你要把你身上的部分钱押在我这里。如果你真的将你自己的部分钱押给他们，就再也找不到人了。你打开那捡到的一叠钱，会发现，表面是一张人民币，里面全是白纸。\n骗术四：就是手机短消息了，说什么你中了……奖( 比如笔记本电脑) 云云，而你如果一个电话打过去，她们会叫你将钱寄到某个帐号，说是奖品的邮寄费。而你真的相信了，寄出去的钱就如同石沉大海。\n骗术五：路边的押注。几个碗倒盖地下，骗子凭借很快的手法，将一粒棋子放在某个碗下，让你去猜。如果你押５０元，猜中了他倒赔你５０，猜错了这５０元就输了。这样的骗子，往往会有一群骗子来故意充当观众，起哄或者押钱。最近广州太平洋电脑城附近很多。\n骗术六：想起一个行骗的例子，尤其逢年过节，大家要警惕。我遇到一次，还真的上当了。－－有一个人抗一袋米，然后敲你家门，一张口就说：有一个６０多岁的老太太在我们店买了米，让我先送过来，她还要去其他地方买东西。米５０块还没给。我当时心里觉得挺奇怪，我妈没这么老吧？？后来想想可能显老，而且又有这么一袋米，就给了钱。老妈回来一问，才知道上当受骗了。而且米很次，最多也就３０搞定。这个骗术比较隐蔽。大家小心。\n骗术七：坐车去广州， 半路上来一家伙， 说自己做铅笔生意的， 在车上跟别人攀谈， 然后开始赌， 手上拿红蓝铅笔各一枝， 猜橡皮圈套在那枝铅笔上。 亲眼看见我旁边那家伙把手表也输了。 快到广州， 开始参与赌的人全下了车， 才知道是骗局。\n骗术八：还有一种，就是路上卖水果或者别的什么的，当你买他东西的时候，他会找你换钱。他零钱换成整钱。说零钱太多不好放。然后在数钱给了你的时候就少给你一张。如果你自己不细心，看着他明明数了十张给你，你自己一数就只有９张了。\n骗术九：今天坐公车， 到站开门后， 一个男人突然堵住车门说自己手机不见了， 不让人下车。 人群哗然。 这时旁边有人说打那个男人的手机， 看在谁身上响谁就是贼。 这个男人就向边上的一个人借了手机要拨自己的号码， 突然靠近门口的一个人拔腿挤下车就跑， 这个男人也没还人家的手机就叫嚣着追了过去， 转眼都不见了。 于是， 这次真的有人丢了手机……\n骗术十：还是几年前， 我们住在家的时候。 就我家婆一人在家， 也是一个男的敲门， 说是ＸＸ( 我ＬＧ的名字) 的同事， 刚从上海出差回来， ＬＧ托他带了双皮鞋， ３８０元， 他现在要去办事身上带的钱不够， 叫家婆能不能先把钱给他， 并拿出一双包装的很漂亮的鞋子， 家婆说没那么多钱， 刚好我媳妇快下班了，你就等一下吧 。那人一听， 赶紧说去ＬＧ单位找他就走了。 幸亏没上当， 不过也挺险的， 家婆随便让陌生人进门， 要是强盗就完了。\n骗术十一：这是本人亲历。前两天晚上打的，从罗湖到福田一共花了二十多块吧，我给了一张一百块的给司机，他掏摸了半天，说没有零钱，要我凑零钱给他。我大惑问“你干了一天怎么一百块都找不开”。但我还是凑够零钱给他了，他就把一百块还给我。我一拿过手，大怒：“你小子找死呀！”，那小子赶快换了一张给我。嘿，那小子竟然偷龙转凤，给了我一张假钞。他不知道，我刚刚给他的那一张是我半个小时前才从提款机摁出来的，全新的。要不，还真给他蒙了。后悔没留意他的车牌，只知道是湖南攸县人。\n骗术十二：和同事去服装店，同事看好了一衣服，很大方的将百元大钞递过去，自己却还站在镜子前比试比试，这时，服务小姐说话了：小姐，麻烦换一张，这张是假钞。我俩纳闷，我们刚从银行取的钱呢（那时还未听说过银行会出假钞），我们接过钱看了看，很镇静地说“小姐，别骗了，不然我们报警了，我们刚发工资，钱的号码是都连在一起的”，然后我们拿出荷包中所有的钞票，我还将号码念出来了（其实我当时就记下了那张钞票号码），小姐可能心虚，就将另张钞票退给了我们，还白送了我们一件衣服，哈，幸好我俩聪明，下次遇上这样的骗子，大家可以效仿。\n骗术十三：在路上遇到一个人，问一条较偏的路，然后说车子开不进去了，要你帮忙看一下车上的货，他去接人来卸货。如果你答应了，你就准备着上当吧。因为，你在守货时，会有有来将车开车，说是那人要他们来的，车刚走，那人和一大帮人就过来找你麻烦了，如果不拿出你身上的钱，你是走不掉了。\n骗术十五：在路上要有人问你银行怎么走， 别搭理他， 他下一句肯定是问如何将手中外汇换成人民币， 再后面就有骗子的同伙过来， 不知不觉掉进一个骗局; 或者在银行门口， 有人问你换外汇的事， 千万别理。 这种骗局经常是天衣无缝， 我一个朋友被骗走好几十万， 平时看这小子还挺聪明的， 也不贪财， 可就是上当了， 道高一尺， 魔高一丈。 听公安局的讲， 在广东某镇， 有好几千人在外从事这种骗术， 做案后就换地方， 很难抓到。\n骗术十六：在人来人往，热闹非凡的商铺，大家迫不及待地想快点买了东西就走，商家利用顾客这样的心理，当找回散钱时，竟会在其中夹着十元钱的假币！细心一点的顾客或许能发现什么，但很多人只是瞄一眼就走人了，哎，我就是差点上了那黑心老板的当！！！还好及时拿回了真家伙！各位女士们先生们购物时千万要留意哦！！！\n骗术十七：我前几天也给人骗了，不过有点无所谓而已，有一男一女学生模样来我家（梅林）推销洗发水，说是刚毕业的深圳大学国际贸易的学生，当时我们俩都在家里，我是深圳大学毕业的，我老婆也是深圳大学毕业的，而且我老婆还是国际贸易的，那个男的当时有点傻了，后来有点苦苦哀求的味道，看他们那么可怜我也就买了他们的产品（说家乐福要买１４０）给我３０，呵呵，可能不好意思给我揭穿吧，骗少一些，我也无所谓，就给了他们３０元，按照他们说的家乐福会上市这些洗发水，到现在也没有看到，呵呵，还是给他骗了。\n骗术十八：深圳华为一员工在外地出差的时候，收到深圳的一部手机发送的短信息：“请注意：我们将连续六小时内不断地拨打你的电话，请作好心里准备。你可以选择关机，谢谢！”在收到这条短信息后，便有一部深圳的小灵通不打来电话，康＊＊一接听，对方就说：“我们将在六小时内连续不停拨打你的电话，你可以选择关机”。康＊＊问对方为什么这样做，对方回答说是测试；问对方是什么单位，对方不回答，然后挂机。此后，康大约每分钟接到一次由这部小灵通打来的电话，康＊＊每次接听，对方就叫他关机。对方不断地拨打，康＊＊终于不堪忍受，只好关机。就在关机之后不大一会儿，康远在成都的家属打他的另外一部手机，说有人打电话到家里。来电人声称是华为公司人员，有关于康＊＊的急事找他的父母。康＊＊不解，于是向安全管理部总控中心员工求助热线求助。这是一起典型的诈骗未遂案件，其诈骗方法：犯罪嫌疑人狂打当事人的电话，当当事人不堪忍受而关机或拔线（固定电话），然后，犯罪嫌疑人将打电话给当事人的亲友，称当事人突发事故正在抢救，请对方立即汇多少钱到某某帐号，并声称，如果在多长时间内收不到钱将停止抢救。这时，接到电话的亲友会在第一时间内打当事人的电话，而当事人的电话恰恰因前述原因而关机或无法接听这恰恰佐证了当事人出了事故的“事实”，“救人”心切的亲友往往因此而上当。\n启发及忠告：１、当您碰到类似的事情时，请不要慌张，您可以在电话中主动揭穿犯罪嫌疑人的伎俩，以免对方心存幻想而不断来电骚扰。\n２、立即打电话给自已的亲友，告之此类事件的真伪，提醒亲友切勿上当受骗\n骗术十九：２００３年０２月２１日傍晚１８：００左右，某公司一员工行走在广州市北京路步行街上， 马路边的众多推销人员不断向其递来传单或宣传资料；某员工出于礼貌便接了几张传单，但当其拿着那几张传单数分钟之后，忽然觉得头脑昏眩起来，继而感到头前所未有地痛，此时某员工感到情况不妙，急忙跌跌撞撞地走到文明路口， 拦了部出租车直奔附近的省人民医院。医院诊断，该员工是由于接触或吸入了一种混合性麻醉药( 学名： 达克罗宁，医生称如果过量容易导致死亡 ！)而导致以上症状。案例分析：这是一起有预谋的抢劫未遂事件，一些图谋不轨的不法分子向路人发放预先喷上类似以上麻醉性药物的传单或宣传资料，然后锁定一些目标( 特别是女性) ，紧随其后，一旦药物发挥作用，不法分子便伺机对事主进行抢劫。\n建议：１、尽量不要接受街头派发的传单或宣传资料，特别是在独自一人的情况下，女性尤其要警惕；\n２、万一您误接了上述资料，并感觉身体不适，请立即向就近的警察求助，尽快就医。\n骗术二十：女生一定要看——可怕的广州火车站和几乎被拐的ＭＭ，小姑娘们，小心呀！\n时间：２００３年３月３日；地点：广州火车站广场\n人物：一个２００２年毕业的涉世不深的女孩\n事件：今天下午，我的一个留校的同学给我突然给我打来电话，说有一个师妹去深圳找工作了，没有找到要从广州回家。让我帮忙请她吃顿饭，把她送上回家的火车我当时就答应了，然后根据同学留给我的电话，马上就给她联系了，电话打通后她告诉我现在已经进站了！因为我住处离火车站比较远，接站已经来不及了，于是就问她以前在广州站下过车吗？她说，每次去深圳都要在广州下车，她问我住哪儿，她下车后直接打的来找我。我听她的口气很自信，而且因为是大白天，我就同意了， 然后告诉她一个广州很有名的大厦的名字， 告诉她我在那下面等她挂了电话我就开始换衣服，准备出发了。此时这个女孩开始随着人流出站了。出站后她发现她的手机经过刚才的电话已经没有钱了！而在此之前她从来没有见过我，这是她在车站广场上发现了ＩＣ卡电话机，她想先给我说一声手机没钱了，然后在问问我穿什么衣服，于是她便拉着箱子走到一部话机旁边。她绝对没有想到，这时一个阴谋已经在悄悄的向她逼近了！\n她把箱子放在身前，开始拨电话了，这时她后面站了一个男的手里拿着卡，好像在排队打电话。正当她刚刚把我的号码拨完，突然后面那个人伸出手“啪”的一声把话机的压簧按了下去，电话没有接通。这时女孩转过身很气愤的看着那个人，那个男人立即满脸堆笑，连声说对不起！说不是故意的。然后告诉女孩，你按重播键就可以了！！！这个阴谋像一张网此刻已经张开了！！！女孩满脸疑惑的转过来，按了重播键。这次没有再挂断，通了！但是，是另外一个陌生的声音。女孩问：“**在吗？”那个声音：“对不起，他刚才有急事出去了，连手机都没有拿，他让我去接你。”女孩又问“他不是让我打的去××大厦吗？”那个声音：“是这样子，我们有车，现在我们去车站接你，你到车站旁边的××等我！”这是一直站在女孩身后的那个男子说话了：“我知道××这个地方，离这儿很近的刚才不好意思啊，为了表示歉意我送你去哪儿！！”说着就拉起女孩的行李，这是电话那边已经挂了！那个可怜的女孩就傻乎乎的相信了那个人的话，跟着那个男子走，还不时的给那个男子道谢！！！ 当走到流花车站旁边的时候，那个男子好像遇到了一个熟人，打了个招呼。那个人笑嘻嘻的说：“行啊你，这么靓的都搞得到！”那个男子立刻板起脸，骂了一句。这时小师妹突然间像是意识到了什么，经过十几秒钟的思考，立刻明白了自己处境。她的腿都软了，但是那个人拉着她的行李，而行李里面装着她的学历学位证。小师妹装作很关心的说，你看你累得，满脸是汗！给擦擦汗吧！说着，便递过一沓纸巾！那个男子装作很憨厚，一边接过纸巾把行李放下，开始擦汗！！！小师妹这时突然拉起行李，像疯子一样飞快的跑起来，一边跑一边叫：“救命啊，救命！！！”那个男子也反应过来开始追，人很多，小师妹根本跑不快。但是就在这千钧一发之际，一个保安从前面听到喊声跑了过来，跑到小师妹跟前问：“怎么了！”可是我那可怜的小师妹已经神经高度亢奋了，根本不理保安还是一直跑。但是那个男子已经不敢再追了。小师妹看到前面停有一辆的士，这次作罢。惊魂未定的上了！而我在大厦下面已经等了１５分钟了。终于我看到一个披头散发的女孩拉着行李朝我走过来了。我赶过去，问她是不是**。 呵呵，她用近乎呆滞的眼神死死盯着我，要我出示身份证。幸好我的身份证总是放在钱包里面随身携带。看过之后，她接过我的手机，给我那个同学打通电话，让我接。之后又拿过电话，给我同学说了几句。挂机之后，拉着我的胳膊好一阵哭啊！！从来没有见到女孩子掉那么多眼泪！当时真把我吓坏了。不管怎么问怎么哄就是一直抱着我的胳膊哭，一句话都没有！！！引得过路的人纷纷侧目。没有办法，我只好带着她回到我的住处，她真的哭了一路！到家里，我让她洗了把脸给她到了杯茶，她的情绪才稳定下来，这时眼睛已经哭肿了。等她边哭边说，把经过讲完之后，我要带着她报警！她说死也不去广州火车站了。不管我怎么劝都不去报警了。歇了一会她让我带着她去东站上车。于是我带她到东站买了火车票，我一直把她送到窝铺车厢，把她安顿好！然后在她感激的眼神中下了车！她给我说的最后一句话是：我再也不来这儿！\n注意：那ＩＣ电话是做了手脚的！据称武昌、成都、西安火车站都有这样的事情。不要轻易和陌生人说话，不然不久以后你就可能出现在河南的山沟沟里面。\n骗术二十一：大约一个月前， 我看到一个女子站在大型购物中心的入口。女子写了个自己的经历， 好警告其它的女子们。几天前， 这个女子购物完后走出购物中心， 在上车前发现车胎泄气了， 于是她从后车箱中拿出千斤顶准备换车胎。一个穿著西装手拿公文包的男士走到她旁边对她说？ 我看到你在换车胎，需要我帮忙吗？ 女子欣然的接受了男士的帮助。换车胎时女子与男士相谈甚欢，男士换好新车胎后帮女子将泄气的车胎及千斤顶放入后车箱并盖上车盖，并将手上的尘拍掉。当女子谢谢男士的帮助要进入车内时，男士告诉女子他的车子就在购物中心的附近，希望女子能让男士搭个便车送男士去自己停车的地方。女子有点意外并问男士怎么会将车停在另一边。男士解释说来购物中心和一个不常见面的老朋友饭、见面，离开时却走错出口。但他现在快迟到， 而他的车子就在附近而已。女子不想拒绝男士，因为男士才帮自己更换车胎，而换车胎对女子并不容易。女子忽然想起男士在盖上后车盖前，将自己的公文包放入后车箱中，而那是在男士开口请女子让他搭便车之前。女子于是对男士说：自己很乐意载男士去他停车的地方， 但自己忘了买一样东西。女子接着说只需要几分钟，男子可以坐在车内等，她很快就会回来。女子进入购物中心后找了安全人员，并将刚刚的事告诉他，安全人员与女子一起回到车旁时，男士已经离开了。女子及安全人员一起将后车箱中男士的公文包带到警察局。警察将公文包打开……( 表面上而言是可以看看有没有证件好将公文包还给男士)，他们发现里面装的竟然是绳子、胶带及刀子。当警察检查女子泄气的旧胎时，发现根本没有问题，只是被放气而已。男士有什么意图很明显，而且事前已经小心计划过。女子幸运的逃过一劫。如果女子当时坐在车里等男士替她换车胎，或者女子有小孩坐在系安全带的儿童安全座椅上，或者当时女子拒绝男士的要求将会发生多可怕的伤害。\n骗术二十二：是我的高中同学告诉我的。他的一个同事，前几天骑车回家路上，碰到２个男的问路。说完路线之后，男的说记不住。要求用笔记下来，便掏出纸笔给这位朋友。当时这个朋友就觉得有一股怪味从笔里传出来。写完，觉得有些头晕。这是两个男的还找不到，拉这位朋友带路。当时这位朋友心中有一个想法，一定要离开。边说，不行，我一定要走，便骑车走了。随后失去了记忆。好像睡着了。当他醒来，发现自己在去八达岭的京昌高速路上，还骑着车，被后面汽车的喇叭吵醒。已经是一个多小时之后了。于是往回走。在高速入口，管理人员见了他就骂，说刚才让他停他不听话，还以为他要骑车去长城……好了，大家明白了，关键就是那支笔散发出来的气体，可能导致你任人摆布或者暂时失去记忆。\n骗术二十三：今天在南山图书馆旁边碰到一个摆象棋摊下棋的，应该都是那种残局吧很难破的。以前也是小有研究吧。就在我刚刚走到那个摊旁边，一个好像端详棋局好久了的人走过去，说，我和你下，赌５０块钱/ 我赢了你给我５０我输给你。那个摊主说。你先拿押金出来，然后那个人就给了１００。并问：你的押金呢？摊主说我现在港出来，身上只有１０块钱。不然我们赌小点儿九１０块钱。吃个快餐，行么？要不我去前面给朋友借……那个\u0026lt; 最后我感觉倒是传说的托~\u0026gt;人说，我肯定瀛你然后笔画怎么走怎么走。摊主没有反应，只是说：其实两个人下的。然后自己低头走棋\u0026lt; 那个人趁机走了。但摊主毫无觉察。仿佛是下给我看的\u0026gt; 比划了半天，我看摊主的棋会瀛，他抬头。啊！怎么走了~ 叫到~老板回来。那个人又回来。他还是比划，摊主还是不反对。我谗言了一句：它不是你说得那么走的。然后他就朝我来了，你说怎么走的，你走走看看。摊主就说了，好。小伙子，你看到我刚刚走的了。你下~ 这样吧，就１０块钱。怎么样，小伙子，你帮我赚１０块钱。那个人也说，你走，你走走看看，……我感觉不对劲了。我就闪了\u0026lt; 当时真的还有事儿。\u0026gt; 那个人还在背后大声的说：他根本就不会下棋……我离开了。有后悔了。应该看看他们的骗局是如何的……明天我还去那里走。不解穿他们，也要在看一次。看看他们是什么把戏！\n骗术二十四：十年前，在深圳还有这样的把戏：有一次我到宝安办事，有一伙打工妹模样的人问邮局怎么走，我正好一抬头就看到不远处就是，于是指给她们看。于是她们又问什么东西不能寄，并神神秘秘的掏出个象金元宝一样的东西来，说是从工地上挖出来的。我当时很认真的告诉她们：地下挖出来的东西是国家的，要上交政府，政府会奖励她们的。这帮人显得很没趣的就走开了。当时我还没有意识到这就是个骗局，过后又碰到同样的事情，大约有三四次，于是细加思量，骗子接下来可能会这样演戏的：这个金元宝怕到邮局会碰到什么麻烦寄不了，急于出手，愿意贱价卖出，于是就可以连骗带抢的把你的钱财搞光。都说单纯容易上当，我就是好在单纯不起贪心，所以避过了一劫。\n骗术二十五：还想起一件事，也差不多是１０年前了。有一次到宿舍楼下的发廊洗头，进来了一男一女，问洗头多少钱，之后就在沙发上坐下来。一会儿，女的说口渴，就去“买”了一瓶健力宝，之后就和那个男的唱起了双簧：好象是中了头奖了，可以到三水领奖几万元。那个女的于是提出她的身份正办暂住证了，怎么办？男的说可以帮她去领。女的说跟你不认识，怎么信的过你。男的说我回家拿８千元押你身上。并用大家都听得到的音量说：“不要让别人知道”。这时候，男的离开，女的挺镇定的坐那喝饮料。男的一走，老板娘却坐不住了，跟那个女的商量，说我给你２万元，你把那个奖让给我。那个女的不露声色，老板娘正要兴冲冲的上楼拿钱时，我突然间觉得不对劲，于是提出了我的疑问——凉爽的阴雨天，怎么会一进门就口渴？奖那么容易得？打工妹得了大奖还那么镇定，一付若无其事状？他们的一举一动象在演戏，十有八九是在骗人。于是大伙都不理她了，后来，那个女的也没趣的走了。不过，虽然为老板娘挽回了２万元的损失，但洗完头后，还是照样买单。仿佛刚才什么都没有发生过。以后，还去洗头，又听她们说起这样的骗局：什么寻亲不遇，身无分文，想把身上的“金首饰”贱卖出去的，什么的。不过，老板娘经过那天晚上的事情之后，都警惕起来了：贪小便宜肯定是要上当的。\n骗术二十六：上面说的骗术，我竟然会都亲身经历过，但都是有惊无险，现在回想起来可能是我的不贪的够镇静才不上当吧。第一次受骗是１０年前我刚毕业参加工作的时候，在一次坐摩托车时给一张１００元让找钱，他说没零钱，然后旁边有几个卖水果的妇女就很热心的说帮我换，我马上说谢谢，接过她给的零钱数一数，不多不少刚好，但她说：不对，好像给多１０元你了，于是她从我手上拿不定过钱数了数，说：没多，不好意思，并将钱退回给我，我由于是看着她数钱的，所以也没有再数一次，当我将零钱给车费时，才惊奇地发现手上的钱少了３０元，回头一看那些卖水果的妇女都溜一边去了，我气的大叫，说一定不会放过她的，并作状要打电话叫救兵。一会她可能是害怕我真的有后台，她过来说：小妹妹，你看你的钱掉地上了，要小心点呀！\n骗术二十七：一个骗子不知道从那儿弄到了我们经理的名片，便说他那儿要帮一栋厂房要我经理去宝安跟他面谈。到了宝安，他先是叫他“弟弟”来接我们，接着他“弟弟”把我们带到一个小餐厅里，没多久他打电话给我经理，接着说叫他弟弟听电话，他“弟弟：接过电话后便”喂喂喂……，信号不好……“接着出门，然后一溜烟跑了！！！！我的经理的ＭＯＴＯＶ６６不见了！\n骗术二十八：因为长着一张娃娃脸，着休闲装时老被骗子瞄上！在八卦二路口，我老是能看到一个厚厚的钱包掉在我跟前，接着会一人很快把它拾起，然后暗示我别张声，等会一快分！不久那个掉钱的便会跑前来问有没有捡到钱，然后就哭着说里头有几万元呢……不光在八卦二路口，在深圳书城旁边的发展银行大厦前面我也碰过好几次，大多时候我都是狠狠地盯一下他们算了；有一次我心情特别好，想帮帮警察叔叔立个功，便装着很贪心的样子，“听他们安排”先把手机关机，然后到一个少人的地方，接着我用另一手机打１１０，无奈１１０很难打通，眼见一个巡警骑着摩托路过便大喊……那两个骗子抓了一个，另一个跑了！另一次是我装着很想同他们分钱的样子，接着跑到巡警亭去叫巡警把他们给捉了！\n骗术二十九：有一帮冒充是北大学生来深圳考察的骗子在深圳行骗很久了！他们先是问到深大的路怎么走（以后的话同深大一点联系都没有了），然后说他们是北大学生来深圳考察掉队了……然后说借个电话打一打，（我看了他们拨的区号是山东的）然后电话里头另一骗子会叫你听电话，说谢谢你关照她的学生！然后以老师的口气来表扬你是个好同志……（现在想来那声音同农妇的差不多，还老师呢）最后他说要向你借钱，到时一定会还的。我那时买了一大袋东西，身上只有１２块钱。最后他们恶心得要命说１０块就１０块给他们打的到机场（我看他们一点什么北大的影子都没有就走了）一转身我就给家里打电话那伙人以为我是报警一下子就无影无踪了……后来在东门还遇到他们一次，还是同样的开场白……\n骗术三十：我和我的一个同学都被骗过，在东门买东西，讲好了价，给零钱的时候，她们趁机撕去一个角，专门引诱你拿出百元大钞，结果我上当了，钱给了她们后，她们说那个没有角的钱可以花出去，就要了回去，把１００元给了我，我没有细看，回到家才知道是假钱。\n骗术三十一：事情经过： ９月１９日我到合肥出差， 晚上８： ０３分在宾馆房间里接到一个电话， 以下是对话内容： 当时我问： “找谁？ ”， 对方( 骗子)说： “找的就是你”， 我问： “你是哪位？ ” 。骗子开始卖关子： “你事情办完吗？ 你贵人多忘事吧， 你猜猜！ ”， 这时候， 我想起我有个同学在合肥国税局工作， 有八年没有见过面了， 我就问： “你是不是在国税局工作的老同学ｘｘ？ ”， 骗子马上附和： “是啊， 终于想起来吧， 这样， 要是你不忙， 咱们见个面吧！你把手机号告诉我，我把我的手机号打到你的手机上，不用记了”。 过了一会儿， 骗子又打来电话说： “我现在有些事情， 我让我的朋友小刘来接你， 我随后就到”。 过了十分钟后， 所谓的“小刘”打来电话： “现在堵车， 你还是自己打的士过来吧， 到银河大厦门口见”。 我打的到了银河大厦门口等了２０分钟后“小刘”出现了， 我问： 我的同学ＸＸ怎么还没到 。“小刘”说： “他还要一会才到， 我们先到楼上夜总会等他。 ”骗子“小刘”带我上楼上大厅等候， 大厅里非常吵闹， 这个时候我的手机响了， 骗子打来电话： “你让小刘听个电话”， 我把手机给了所谓的小刘， 碰巧我的手机没电了， 骗子的第一步行动没有成功， 但是骗子毕竟是骗子， 他又随机应变让我把手机给他去充电， 当时我还有点警惕性， 一直跟着他， 我和他到楼下时他说他的朋友今天没有上班。 又回到楼上， 其间他出去了一会儿， 回来没有多久， 我的手机收到一个短信让我把手机交给小刘去充电， 这个时候我已经等的没有耐心了， 又急着解手， 就把手机给他了， 我去解手去了出厕所后我刹那间明白了所有， 但已经晚了， 手机和小刘都消失了。 我迅速到楼下共用电话报警， 警察３０分钟后来了， 说了一些没有任何意义的话。 让我回去等着。 我回到宾馆以后立即提醒其他学员。 第二天上午在我手机电话簿中的小王就接到了行骗电话( 当然没有得逞)。所以我要提醒大家， 现在的骗子防不胜防， 花样繁多， 有些骗术并不高明， 但是你总会有想不到的时候， 总有失去耐心的时候。 我感到庆幸的是没有被抢劫和绑架。 永远保持警惕！\n骗术三十二：深圳还有这种骗子， 两个人， 一个在前， 一个在后， 在前面的那个一手拿着个黑袋子， 里面装的是早已破碎的瓶， 然后找个路人， 故意撞你一下， 手上的黑袋子就掉在地上， 可以听到玻璃碎的声音， 然后说这是什么贵重的药， 要你怎么办。，是赔钱还是到大药店去？？？？？ 这些家伙在东门和布心经常出现。\n骗术三十三：三年前， 我遇到类似“冒充北大学生来深圳考察”的骗子。 那天我刚下班， 准备去附近的超市买东西， 有两个中年男子很着急的样子向我走来， 并声称是北京某一公司的员工， 这次来深办事， 车子出了交通事故， 被扣住了， 现在身上有没钱， 没办法联系公司， 叫我帮打电话( 而不是叫我给钱他们打电话)。我当时也没多心， 就跟他们一起到不远的报刊亭打电话。 他们拨了一个长途电话， 但当时我没注意是哪的， 他们跟电话那边的人说了约两分钟， 并把电话递给我， 说是他们的“老总”要感谢我， 我接过电话， 只听到一个操着北方口音的男子声音， 先是一些感激的话， 接着就提要求了， 说是他们这边的两个人一天没吃东西， 晚上又没地方住， 叫我可不可以先借给他们两百块钱， 等他们公司那边派人到深圳后再还给我。 但当时我身上就没那么多钱， 我就跟他说明了， 但他又帮我想了办法， 说是可以去银行取或是向朋友借。 这时我想了想， 便开始怀疑，既然是交通事故了， 这种情况， 求助交警部门应该没什么问题吧。 我就跟电话那边的人说， 先让我想想办法， 又是一堆感激的话， 挂了电话， 我还是付了几块钱电话费， 然后我就跟那两个人说， 我刚看到前面有我的同事， 你们在这等一下，我去问他有没有钱， 我就这样走了……后来， 我几位住宝安的朋友也遇到过同类的事。\n骗术三十四：近段时间，在草埔天桥有很多人用扑克牌骗钱，面且有很多人是一伙儿的，每次下班时间从哪儿经过时总会看见他们，没有长驻，不知道有没有上当受骗，要是稍微有点头脑的人都不会上当，因为这种骗术是很古老的了，我几年前在家乡就见过，当是在船上，有很多人上当。其实呀，人只要不贪心，相信不会上当的啦\n骗术三十五：刚来深圳不久，第一次发工资，去科技园的中国银行取钱，取钱一出来就碰到一开着一辆车的家伙说有ＤＶＤ（假ＳＯＮＹ的）卖，然后故装鬼祟地叫我去他车上看看，他称这ＤＶＤ是他们公司送给中国银行的，因为公司让他送，总共有十台，他就偷偷拿出了一台，说便宜卖给我，还把收据拿给我看（收据上写的是１６００块一台），并把他的工作证及驾驶证给我看，刚说完，他的手机就响了，听他们对话是“他们公司老总”打来的，问他ＤＶＤ送给中国银行没有，我当时也是发懵，真的信以为真。然后他说现在便宜卖给我就算８００块，后来好说歹说我还到４００块买了，还说让我买了壬万不要从中国银行门口经过。真得很佩服现在的骗子，这都能想出，不知是不是我太傻了。结果回到家一看，才知上当了，虽然ＤＶＤ能用，但拆开一看，就知是假的，也不知是哪儿的地下工厂出的。\n骗术三十六：中兴的一名员工，晚上的晚班车到科技园之后，发现在１号楼前面有一个中年男子专门找从１号楼出来的人搭腔。常在大冲、科技园出没的兄弟姐妹们要特别留神了。很恐怖的事情，本来不想说了，为了提醒大家，还是说出来好。昨天中午去科技园北区邮局办事，拎一个公文包，从建行门口走过约二十多米，一穿米黄衣服的男人，三十五六年纪，站在路上抽烟。在与他擦肩而过时，笑着朝我说句了什么，声音很小，根本听不清，我一愣，稍一停顿未理会继续往前走。可怕的事情发生了，走了不到十步，头突然很晕很麻，有点失控，胸部很闷，喉咙象吃了槟榔的感觉，当下太惊，立即想起前几日网上说的迷药事情，摒住呼吸拨脚狂跑，想找到有保安的地方，转了一个弯后跑到维用科技大门保安处大口喘气，过了约十分钟，身体状态才恢复过来。真的好可怕，那种感觉真的是头部又晕又麻，无法自控，任人摆布的感觉。本来想报警，因有事要办也就罢了。提醒大家，外出一定要小心，现在特别时期最好不要与陌生人搭话，特别是从银行出来的时候，一定要看清周围。\n骗术三十七：新闻报道过，Ａ碰上时明白是假的根本就不理那骗子继续往前走，过一会又来一个人说刚刚掉了钱问见着没，还一口咬定是Ａ捡了，还一下子跟上了几个人。因为当时路况偏僻，Ａ也不敢跟它们起冲突，只好答应让它们搜包（因为也没带什么大捆现金），那些人看到Ａ包里有银行卡，就一口咬定Ａ把钱捡了存卡里去了，逼Ａ去银行打单来证明。Ａ就跟它们去了最近的自助银行，在柜员机上给它们看了帐户* 作，这时那些人就连连道歉说搞错就走了。Ａ拿回卡走人，过一会才觉得不对，再检查卡，发现给掉包了，再去查帐，卡里的钱给取了，就一分钟内的事情。\n骗术三十八：来自内蒙古的王金刚，１６日清晨７点到达合肥火车站等待联系好的学校派车来接，此有两名出租车司机抢着要送他去学校，后来两个司机甚至动起了手，王金刚说：“当时我差点被合肥司机的热情所感动，但还是觉得为５元钱打架很不值。”鹬蚌相争，渔翁得利。正当两位司机打得不可开交时，另外一位司机凑上前，趁机将王金刚“劝”上了自己的车子，但这位“好心”的司机并没有将王金刚送到他所说的学校，而是送到另一所不知名的电脑学校。\n“一进学校，校方便再三叮嘱我呆在办公室，别乱跑，并安排一位老师一刻不离地照顾我，说是怕我出意外。后来，老师怕我走丢了，还陪我到农行取了７０００多块钱交给学校。”傍晚，王金刚正准备去食堂吃饭，同寝室的室友回来了，一室友小声问他：“你怎么也给弄过来了？”另外一个人则说：“别让他知道被骗了，他家远，他会更难过。”明白被骗的王金刚什么也不说，饭也没吃。当晚，王金刚寻思对策如何外逃，于是假借欣赏合肥夜景走出校园，“怕他出意外”的那位老师主动提出陪他。\n第二天，王金刚偷偷将此事投诉到教育主管部门。后来，在主管部门干涉下，王金刚要回了学费，“逃”了出来。\n合肥市劳动局培训处负责人表示，合肥市针对培训市场的欺骗现象，早有相关规定出台，目前尚未发现有违规情况，但他们将“发现一起，查处一起”，严格按照规定严肃处理。\n骗术三十九：类似的骗局是这样的，在深圳华强北附近，一个人悄悄的向路人低声问到“手机要吗”，当你停下来的时候，他会向你推销说，手机是刚抢来的，联想到经常的飞车抢劫，你可能会相信，\n然后是他开价，接下来肯定是你还价，为了表演的更真实一点，骗子会假装拨个电话，然后对你说，你和我老大谈谈价格吧，这时你便彻底的入套了，骗子会在旁边催促您，电话快没电了，快点说，等你谈好了价格，这时“老大”让你把电话给骗子，此时骗子装摸做样说两句，然后问你要钱，给你手机，注意：这是他给你的手机已经是假的了，但是为了逼真，这个手机开机时会响一声就关掉，也就是玩具手机\n骗术四十：我一个阿姨，来县城车站，正好碰到有人偷偷卖袁大头，说是外地人，来这儿打工的，从工地上挖了来的。因急着脱手，就便宜处理了。那个阿姨比较精明，就到银行里让人检验了一下。但由于钱不够就到我家来借钱，我妈妈也带着样品（记住是一块样品）去附近争行让人看了一下，确实是真的。而且那个样品就让银行内部人买下来了。\n最后快付钱时，妈妈多了个心，从一堆银元中抽出几块来，借口去借钱，又去检验了一下。当时那个阿姨还以为我们挡她发财，不高兴。但结果让她哑口无言了，是假的。\n当时那个骗子还抱怨我们家浪费了他的时间，在我们未反应过来之时，大大方方地走了。\n这样的骗局很多，在我们老家，就有这样一群人，俗称“赶三八”（发音是这样的），不过据说兔子不吃窝边草，村人就不说什么了，有的人人缘还不错的，当村干部的都有，县公安局里也知道这些人的存在，定期来带走一些人，但只要贡钱，过几天就放回来了，逢年过节，他们就这样增收。他们是讲道义的，据说进去只要不供什么，外面的砸锅卖铁也要扒出落难的弟兄来。\n骗术四十一：有一次我去市场买东西，卖东西的老女人很慈祥，和我有说有笑，事后想来，这纯粹是为了消除我的戒心，然后就是我挑选，她不厌其烦地给我讲解，最后讨价还价买了一条腰带。是３５一条，然后她说用不用打眼，大家想买上的腰带哪有那么顺啊自然要打的，于是她热心地要给你打眼，就在这时，旁边来了几个人问：这腰带多少钱啊？老女人回答８５，这也是她一开始给我报的价格，我也就没有多想。另外那几个人也没走，在那里一个劲地说８５一条皮带非常值，老女人微笑着告诉他们说我是老客户？当时我就觉得有点不对劲，我第一次来怎么是老客户了？但是也没有多想，这时她打好眼了，我掏出一张１００给了她，她先找了我５块，又找了我１０块然后说：“８５，对了吧？”\n我当时立刻反应过来，也佩服这些家伙居然设的圈套这样周密，刚才她明明当着别人说这皮带８５一条，可是我没有反对！这时再和她争论是３５还是８５是绝对没有意义了，对方有旁证啊！不过老婆子估计没有想到我反应快，也没有想到我就立刻从她手里将我的百元大钞夺了过去，同时转身直接跑了哈哈。等我确信没有人跟着我的时候才发现，我不仅分文不取有了条腰带，还多赚了１５块钱哈哈。\n提醒大家：以后支付时一定要先把货拿到自己手里，同时最好用零钱支付，省得麻烦；二，当老板对别人说你价格的时候，一定要更正，不然吃亏的是你。\n骗术四十二：也是一个网友的经历我上个星期碰到这样的骗局：我当时从德兴花园做２２２世界之窗的世界花园站下，由坐的时间长，当时头晕晕的，我边走边给老婆发短信息。这时一个中年妇和一个年轻的女人走过，还有点不好意思的说，问我能不能行行好，请她吃个盒饭，那时已是晚上了，她们说还没吃中饭。她们过来找朋友，钱包丢了，朋友的电话又打不通。这两年女人都是那种清爽的人，不像骗子。于是我就带她们去吃饭，马上，她们又问能不能给点钱，她们好买点吃的，好边走边吃，去火车站赶火车。当时头晕，也就给了她们二十块钱。\n过后越想越不对劲，估计是上当了会ＳＢ了。\n这事我同一个朋友讲时，他告诉我一个好方法：碰到这种情况，不管他是真有困难，还是假的，有困难找警察去。\n骗术四十三：一次在名典咖啡，很晚了，和几个朋友一起聊天，当时喝了蛮多酒。有一老外，象是西亚或斯拉夫人，拿着一张１００美金钞票说着听不懂的英语，那意思就是没有人民币，前台不收美圆，问我们能不能兑换人民币。\n我当时是处于好心，尽管我的也喝的五迷三倒的朋友还劝了我两句。我问他兑换比率多少，老外比画着１：８，我想与人方便自己方便，咱也不是存心占他的便宜，就把他的钱接过来，捏呀看的也看不出什么破绽，别人也不是很懂。我就点给他８００元给你他，那家伙又掏出几长问我继续换不。我说ＮＯ。\n我发现这事上当是之后不到１０秒钟，那老外的几个同伙也围过来问我他也要换。靠，上当了。径直冲出去找那个死老外，人已经没影了。\n只好暗叫晦气。一是喝点酒，二是确实想帮他，ＴＭＤ，如果我是贪心就跟他说１：２，你换不换。我多换点。\n骗术四十四：是在我公司楼下不远，就在投资大厦旁边那条路上。下班时候，我一同事在汽车站附近，忽然被一个鸟人指着鼻子说：就是他就是他！就是他抢了我手机！！旁边有一１１０警车（那种半截子５０铃，后面是大笼子装人的。）一个没着装的自称警察的也下来了，我哥们诧异辩解，那鸟人说的有鼻子有眼，你怎么把我按住的，和我说了什么，手机怎么被抢的，你还揍了我什么，要不是我护着上衣（衬衫上衣口袋里捅着１００元的一打，有２０００左右的样子）口袋，这里的钱都没了。然后警察摸样的说上车，回局子里说。注意，这时候，旁边也人起哄，要作证或同去看热闹等。\n我同事坚持不去并给公司打电话，我们公司就在商会大厦，当时我们十来个小伙子就冲下楼，２、３分钟就到了那。\n那鸟人见我们来了这么多人，有点退怯，但兀自口硬。我过去看的时候单纯以为就是认错人了，没往别处想，就是对那家伙损了几句，并且让他道歉，警察摸样的（没着装，我是通过分析认为他是警察）说，要不去局子里。我说，好我们都去，如果认定是我同事，那我们认了；如果不是我同事，怎么解释？给我什么补偿！（这时旁边有不相干的人在旁边转悠四下打量着附和着什么象和他有什么关系。）\n那家伙满脸通红兀自罗嗦说，以为我没钱吗！拍着口袋说，我有的是钱！！（当时我们就有点奇怪，在深圳这个地方还有人明目张胆地把钱揣在白衬衫上衣口袋里，给人看吗？而且还是在他刚遭抢劫惊弓之鸟后？）这个地方我认识谁谁，黑白都罩的住云云。我手机丢了，被人打了，你们还威胁我？！我电话里的号码丢了，我一分钟千千万的生意？！（一听就是吹ＮＢ，就他那个熊样还千千万？）\n我逼着他道歉，说你要看错了，那原谅你，但你不能一句话就拉倒，必须道歉。那警察摸样的半天没吱声，这会儿说，你是不是看错了？\n那家伙还在作戏，好象是，当时３个人，我要不是紧护着钱，钱都被抢了。（想想，你口袋里明目张胆的真金白银３个人都抢不走？可能吗？）\n后来鸟人还是道了歉，我们才作罢。但回公司路上越想越不对劲，折回去，那警车已经走了。\n如果我们是外地人或身上没有手机，或没在公司楼下，一时找不到帮手，我同事上了警车就不知道发生什么事，就在警车后备箱里把你打劫了，或提出私了吧。因为周围都是他的人，假装作证的托儿，你在被封闭围攻下能不就范？然后不知道把你拉到哪打个半死或一脚踹下车。你自己个想办法回深圳吧。靠。\n真是疑点很多：\n１、１１０警察没有着装。（１１０警察出警必须要着装吧？）而且，假设被抢的真的，对方抢劫者有３个人，你一个未着装警察能制服吗？\n２、上衣口袋里钱，你会这样揣钱吗，而且被抢了手机还敢这么揣？其目的就是掩人耳目，给人种我有钱不是骗钱的假象认识。\n３、煞有介事，描述细节。实际做给别人看。\n４、周围的看客里有三四个是托，当时双方互辩的时候，我挤出去打了电话，和本事无关，但不经意发现有好几个人的目光不是在关注圈里面的争论而是偷看我在打什么电话，（是不是报警或叫人？）\n事后一个星期，当时我在场的一个同事在华强北看到了那个自称被抢手机的人，在大街上对每个经过身边的人小声说：要手机不？？\n竟然连１１０也是假的，听说１１０警车的很多司机是雇佣的，难保不是这些人搞的名堂？真是恨人哪！！\n哪有什么安全感！\n后来我们还报警请他们核实过，但也没有消息。\n以后遇到这种事，第一反应就是千万别跟他上车，也不用和他们纠缠你是不是无辜的；你就这样说，我不跟你解释，也不跟你走，你要争取时间等围观人越来越多的时候，立刻播打１１０，你也报警，你说如果我要去驹子里也跟我叫的１１０走。围观人太多，他们一般不会强行无礼。也就悻悻作罢。\n但前提是手机一定在身上，且一定保持它有电。\n骗术四十五：　就是路上卖水果或者别的什么的，当你买他东西的时候，他会找你换钱。他零钱换成整钱。说零钱太多不好放。然后在数钱给了你的时候就少给你一张。如果你自己不细心，看着他明明数了十张给你，你自己一数就只有９张了。然后当场提出，她就拿回去再数过：\u0026quot; 哦，不好意思，还真少了一张！诺，我这就给你添上！\u0026quot; 语罢添上一张十块的\u0026quot; 这下对了吧。\u0026quot; 一般的人就想我明明是看着她数又看着她添上的，肯定不会错了，于是接过钱走人。偏偏爸爸的同事多长了个心眼，重数过，将一叠钱一摊开：两张十块，中间夹着８张１块的！！这一切就在我们的眼皮下进行的也~ 也部知道她们是怎么狸猫换太子的！\n骗术四十六：就是盘子下面压瓜子，一共三个，让你猜哪个有瓜子。一看就是一帮人骗钱，有好几个托，一个学生模样的人，去掏钱，旁边的家伙一下子把他的所有钱都抓过来，只嚷嚷“压了，压了”那个学生还没来得及说什么，摆局的家伙就把钱接过来放兜里了，结果当然没猜对。想找个电话报警（那时侯我还没手机），找了一圈也没找到，回来看，人早散了，真来气！！\n骗术四十七：有一次在南京也遇到了在地上拣钱要我我抵押东西的。\n不过骗子遇到我也挺倒霉的，那天我是刚买的卷饼，装卷饼的塑料袋坏了我去招行里要个信封装卷饼……\n结果我卷饼被骗走了，我得到一打学校用的财务练功券。\n回家一想要是论价值算———今天骗子赔了\n骗术四十八：我的一个朋友开了一间水暖店，一天一个骗子来店里说是有人介绍这里东西好，他要买很多东西还出示了一叠钞票给朋友的父母看，说是要抻金，朋友在上班接了电话后赶来，那家伙收起钱，（没让朋友看见），让朋友载他去家里看看帮他设计一下。到了一个镇，问我朋友借了一千块说是他某亲戚的女儿十六岁要包红包。并且他打电话让家里人送钱去店里了。钱到手后此人上楼一去无回。朋友这才想起往店里打电话。至今，他还不明白那人怎知道前几天的确有一人买了店里的淋浴房价钱也如那骗子所说。他是轻信那骗子说是那人的朋友。不过事后想想花钱消灾吧，要是没有给钱也许小巷里会冒出打手来。光天化日的竟也上了一回当，事后分析一下，这骗术也并不高明。\n骗术四十九：一次打车回家，偶坐在副驾驶位置，上车后不久，司机摆动了一下我前面放的一个杯子，我没看清怎么弄，因为我有上车后往外看的习惯，然后就听见“扑扑”的声音，我以为司机把音响打开了，但仔细听了一下没有什么声音，还是只有“仆仆”的声音，然后我看到这个司机好象往嘴里放了什么东西，接着把他左边的玻璃摇上了，我这边的玻璃本来就是关着的，因为我以前看过类似的文章，这时我立马警惕起来，再看前面档风玻璃，发现上面什么东西都没有，什么年检查证、驾驶证统统没有，这时我感觉有些晕，我立即想起会不会是迷药，想到这我立即把我这边的玻璃全部摇开，也就短短一分钟时间，我就感觉神志不清，脑子里一片空白，全身发热，就象刚刚晕到醒过来一样，什么都记不起来，全身无力，手都抬不起来，说话也说不出（还好我提前把玻璃摇开），这时我的唯一的信念就是清醒清醒，之后我用尽全身力量，把头探出窗外，狠狠的呼吸了足有一分多钟，才清醒了一些，然后我赶紧喊：我要下车。那个司机把车停下来，我下来后，那个司机在原地犹豫了两分钟左右才走，可能是看我会不会报警什么的吧……\n想想真是后怕，如果我没有看过类似的文章，或者当时是晚上１１、１２点，或者是在很偏僻的地方，真的不知道会发生什么……\n希望以后大家打车时也一定要警惕啊\n骗术五十：一次经过一栋大楼门口，门口有一提款机，有一个老阿伯，一直看着我走过他身边，突然叫住我，他说他不识字，拿一张提款卡要我帮他在大楼门口的提款机领钱，我回答我无法帮你领，叫警卫伯伯帮你，结果，他就回答我说不用了，继续找其他路人帮他领钱。\n朋友们要记住提款机可是有摄影机耶，万一他说我抢劫或是偷他的提款卡，甚至他的卡片是偷来的，帮他领钱会在提款机留下影像，绝对会让你百口莫辩！\n会怕！是因为已有同事上当，目前仍官司缠身。\n显然这是诈骗集团在找替身了！\n骗案真是层出不穷，一不小心就会踏入陷阱，真是令人防不胜防。\n如果因为同情心而令自己犯下官非，真的是无妄之灾呵！\n其实骗子骗钱，主要两种途径：\n１、你的贪念，这种防不胜防，骗子往往成功。对付绝招：天下没有不劳而获的东西，只有经过汗水的东西才最真实 。\n２、你的疏忽和善意，常常也能奏效对付绝招：害人之心不可有，防人之心不可无。\n还有最多的骗术就是广告骗术，明明不能达到广告上说的效果，却花大本钱打广告，让人掏钱时毫不考虑。其实骗子的骗术并不高超，为什么我们会上当受骗，应该与我们还存在一丝贪念有关，还有就是自己的不设防心理。遇到这种情况，镇定、细心、警惕、不贪都是可以让我们避免上当的良方。\n","date":"2006-07-11","description":"","lastmod":"2006-07-11T13:32:44Z","slug":"a-put-up-job","tags":[],"title":"【转】小心骗局（共50个）","url":"https://blog.zengrong.net/post/a-put-up-job/"},{"categories":["technology"],"content":"Asp.Net 版块开张啦！先献上一篇初级教程热热手，嘿嘿！\nViewState和IsPostBack是什么，这里就不多作介绍了，我们今天讲是在什么情况下使用这两个好东东\n在Asp.Net中，因为引入了ViewState，使程序能够在表单提交的时候，将表单元素，如TextBox/DropDownList中的值保留下来，这在用户注册程序时特别有用，比如用户输入了一个不合法的用户名，提交后程序判断不全法，页面仍然停留在原来的表单填写界面，如果是 Asp/Php 程序，之前填写的值就丢了，但 Asp.Net 而可以将之前填写的值保存下来，超方便地说……\n默认情况下，ViewState 对于每个页面都是自动启动的，实际上是相当没有必要的，这会加重服务器上内存的耗用，所以还是建议在 web.config 中\u0026lt;system.web\u0026gt;节点中加一段\u0026lt;pages enableViewState=\u0026quot;false\u0026quot; validateRequest=\u0026quot;false\u0026quot; /\u0026gt;来全局禁用所有页面的 ViewState ，再在需要的页面中来开启。\n以上两段很多网页中都有介绍，算是一点铺垫……\n之前讲到 ViewState 用于维护表单状态，但朋友们在试的时候却发现，就算页面中禁用了ViewState，仍然可以保留表单元素的值，那ViewState有什么意义呢？\n很多情况下，ViewState是和IsPostBack配合使用的，我们可能会遇到这样一种情况，就是注册表单中的下拉菜单选项可能是从数据库中动态加载的，比如国家/省份/城市，加载的事件是Page_Onload ，这样的话，只要重新访问页面时，下拉菜单中的数据就会复位，就无法达到保值的效果，这个时候 IsPostBack 就派上用场了。\nIsPostBack 全称是Page.IsPostBack属性，它用于判断当前页面在访问时是否有一个Post行为，简单地说就是是否是一个被提交过的页面，他返回 true/false。\n我们可以在动态绑定下拉菜单的代码的外部嵌入一个 if(IsPostBack) 来决定是否需要动态加载菜单项，这样一来，即能保值，也避免了页面提交时需要重新访问数据库来加载菜单项，一举两得，呵呵……\n附上源码：\n注意，请用记事本编辑后另存为 utf-8 格式保存，以免出现乱码问题\n第一个文件:\n1\u0026lt;%@ Page Language=\u0026#34;C#\u0026#34; AutoEventWireup=\u0026#34;true\u0026#34; EnableViewState=\u0026#34;true\u0026#34; CodeFile=\u0026#34;IsPostBack.aspx.cs\u0026#34; Inherits=\u0026#34;lesson_IsPostBack\u0026#34; %\u0026gt; 2 3 4 5 6 第二个文件:\n1using System; 2using System.Configuration; 3using System.Collections; 4using System.Web; 5using System.Web.UI; 6using System.Web.UI.WebControls; 7 8public partial class lesson_IsPostBack : System.Web.UI.Page 9{ 10protected void Page_Load(object sender, EventArgs e) 11{ 12if (!IsPostBack) 13{ 14int[] ary ={ 1, 2, 3, 4 }; //这里将数组作为数据源绑定，读者可自行修改为数据库调用 15dl.DataSource = ary; 16dl.DataBind(); 17} 18else 19{ 20Response.Write(\u0026#34;页面提交时间 : \u0026#34; + DateTime.Now); 21} 22} 23} ","date":"2006-07-09","description":"","lastmod":"2006-07-09T08:09:40Z","slug":"viewstate-ispostback","tags":["aspnet"],"title":"利用 ViewState 和 IsPostBack 维护表单状态","url":"https://blog.zengrong.net/post/viewstate-ispostback/"},{"categories":["technology"],"content":"此类由 petex 制作，版权归原作者所有。\nzrong(www.zengrong.net)修改，加入识别嵌套的xml文档的功能以及支持 type=\u0026quot;object\u0026quot; 类型。\n详情见 http://www.tweenpix.net/blog/index.php?2003/09/24/43-xmltoobject-as20-class\n使用方法：\n1import cn.mediasky.utils.XmlToObject; 2obj = new Object(); 3obj.onMade = function() { 4 trace(this.main.url); 5} 6XmlToObject.loadData(\u0026#39;test.xml\u0026#39;, obj); xml文件：\n1\u0026lt;root\u0026gt; 2 \u0026lt;main type=\u0026#34;object\u0026#34;\u0026gt; 3 \u0026lt;url type=\u0026#34;string\u0026#34;\u0026gt;http://localhost/abc.php\u0026lt;/url\u0026gt; 4 \u0026lt;level type=\u0026#34;number\u0026#34;\u0026gt;3\u0026lt;/level\u0026gt; 5 \u0026lt;/main\u0026gt; 6 \u0026lt;allowdomains type=\u0026#34;array\u0026#34;\u0026gt;\u0026#39;abc.net\u0026#39;, \u0026#39;abc.com\u0026#39;, \u0026#39;localhost\u0026#39;\u0026lt;/allowdomains\u0026gt; 7\u0026lt;/root\u0026gt; 类文件：\n1/*-----------------------------------------------------------------------------------*/ 2// XmlToObject 2.0 (09.23.2003) 3// (c) petepx aka Francis Bourre (peterphonix@usa.net) 4/*-----------------------------------------------------------------------------------*/ 5 6/*-----------------------------------------------------------------------------------*/ 7// XmlToObject 3.0 (07.06.2006) 8//此类由petex制作，版权归原作者所有 9//zrong(www.zengrong.net)修改，加入识别嵌套的xml文档的功能以及支持type=\u0026#34;object\u0026#34;类型 10//详情见http://www.tweenpix.net/blog/index.php?2003/09/24/43-xmltoobject-as20-class 11//原来的版本以及离线帮助见utils.xml.XmlToObject 12/*-----------------------------------------------------------------------------------*/ 13import mx.events.EventDispatcher; 14 15/* // - Custom objects import example - 16import com.robertpenner.Vector; */ 17 18class cn.mediasky.utils.XmlToObject { 19 20 static var dispatchEvent:Function; 21 static var addEventListener:Function; 22 static var removeEventListener:Function; 23 24 private static function _stripSpaces(sE:String) : String { 25 var sR : String = \u0026#34;\u0026#34;; 26 for (var x = 0; x \u0026lt; sE.length; x++) if (sE.charCodeAt(x) \u0026lt;\u0026gt; 32) sR += sE.charAt(x); 27 return sR; 28 } 29 30 private static function _ignoreSpaces(sE:String) : String { 31 var sR : String = \u0026#34;\u0026#34;; 32 var canDel : Boolean = true; 33 for (var x = 0; x \u0026lt; sE.length; x++) { 34 if (sE.charCodeAt(x) == 34 || sE.charCodeAt(x) == 39) canDel = !canDel; 35 if (sE.charCodeAt(x) != 32) { 36 sR += sE.charAt(x); 37 } else { 38 if (!canDel) sR += sE.charAt(x); 39 } 40 } 41 return sR; 42 } 43 44 private static function _explode(sE:String) : Array { 45 var t:Array = _ignoreSpaces(sE).split(\u0026#34;,\u0026#34;); 46 var aR:Array = new Array(); 47 for (var y=0; y\u0026lt;t.length; y++) t[y].charCodeAt(0) == 34 || t[y].charCodeAt(0) == 39 ? aR.push(t[y].substr(1,t[y].length-2)) : aR.push(Number(t[y])); 48 return aR; 49 } 50 51 private static function _made(xml:Array, rO:Object) : Object{ 52 var o:Object = new Object(); 53 var n:Number = xml.length; 54\tfor (var x = 0; x \u0026lt; xml.length; x++) { 55 o[xml[x].nodeName] = {}; 56 o[xml[x].nodeName][\u0026#34;data\u0026#34;] =xml[x].firstChild.nodeValue; 57 o[xml[x].nodeName][\u0026#34;varType\u0026#34;] = xml[x].attributes.type; 58 o[xml[x].nodeName][\u0026#34;nodeNum\u0026#34;] = x;\t//保存当前xmlnode在父Node中的序号 59 } 60 for(var x in o) { 61 switch (o[x].varType) { 62 case \u0026#34;number\u0026#34; : 63 rO[x] = Number( _stripSpaces(o[x].data) ); 64 break; 65 case \u0026#34;string\u0026#34; : 66 rO[x] = o[x].data; 67 break; 68 case \u0026#34;array\u0026#34; : 69 rO[x] = _explode(o[x].data); 70 break; 71 case \u0026#34;boolean\u0026#34; : 72 _stripSpaces(o[x].data) == \u0026#34;true\u0026#34; || Number( _stripSpaces(o[x].data) ) == 1 ? rO[x] = true : rO[x] = false; 73 break; 74 case \u0026#34;object\u0026#34; : 75 rO[x]= new Object(); 76 //trace(\u0026#39;xml[\u0026#39;+o[x].nodeNum+\u0026#39;].childNodes:\u0026#39;+xml[o[x].nodeNum].childNodes); 77 rO[x] = _made(xml[o[x].nodeNum].childNodes, rO[x]); 78 break; 79\t/* // - Custom objects deserialization example - 80\tcase \u0026#34;vector\u0026#34; : 81\tvar t:Array = _explode(o[x].data); 82\trO[x] = new Vector(t[0], t[1]); 83\tbreak; */ 84\tcase \u0026#34;default\u0026#34; : 85\t// do nothing, specify something if u want ... 86\tbreak; 87 } 88 n--; 89 } 90 return rO; 91 } 92 93 public static function loadData(s:String, rO:Object) { 94 EventDispatcher.initialize(rO); 95 rO.addEventListener(\u0026#34;onMade\u0026#34;, rO); 96 var myXml = new XML(); 97 myXml.ignoreWhite = true; 98 myXml.onLoad = function(success) { 99 if (success) { 100\trO = _made(myXml.firstChild.childNodes, rO); 101 rO.dispatchEvent({type: \u0026#34;onMade\u0026#34;, target: rO}); 102 } 103 } 104 myXml.load(s); 105 } 106} ","date":"2006-07-06","description":"","lastmod":"2006-07-06T14:55:30Z","slug":"xmltoobject","tags":["actionscript","xml"],"title":"XmlToObject类","url":"https://blog.zengrong.net/post/xmltoobject/"},{"categories":["technology"],"content":"在对客户的摄像头和麦克风进行探测时，有许多的不确定性。例如，客户可能会禁用摄像头，可能有一个以上的摄像头，还可能先允许使用摄像头，再使用“属性”菜单禁用摄像头。因此，前几天升级聊天室程序的时候，就就写了这样一个类来进行处理，还可以方便在使用了摄像头和麦克风的程序中使用。\n其中，Checker类仅用于检测摄像头和麦克风状态并返回；Selecter类继承Checker类，它不仅和Checker一样返回状态，同时也回根据当前的状态做出反映。例如，如果禁用了摄像头，就会显示一个按钮，单击可以打开“设置”对话框。只有当允许使用摄像头时，此按钮才会消失。如果有一个以上的摄像头，则会显示一个下拉列表并提供选择按钮。\n用法可以看下面的示例，还提供了一些方法大家可以自行察看as文件。其中，检测麦克风的功能还不完善。\n使用示例：\n1import cn.mediasky.media.*; 2//建立selecter实例，必须使用main方法创建。 3//四个参数分别为，selecter上级的影片剪辑，Depth，x位置，y位置 4var my_selecter = Selecter.main(_root, 0, 50,20); 5//注册为侦听器 6my_selecter.addListener(_root); 7//检测摄像头状态 8my_selecter.checkCam(); 9//处理摄像头状态 10function onCamStatus(iInfo:Info):Void{ 11 if(iInfo.code == \u0026#34;UnMuted\u0026#34;){ 12 _root.my_video.attachVideo(iInfo.cam); 13 }else{ 14 _root.my_video.clear(); 15 } 16} 范例演示：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n1 文件 ","date":"2006-07-04","description":"","lastmod":"2006-07-04T12:43:45Z","slug":"check-camera-and-microphone","tags":["camera","netstream"],"title":"检测摄像头和麦克风状态并自动进行处理的类","url":"https://blog.zengrong.net/post/check-camera-and-microphone/"},{"categories":["technology"],"content":"我曾经介绍过使用swfobject在页面中加入Flash影片的方法，也制作了一个基于swfobject的发布模版。实际上，除了swfobject，Adobe也有对应的解决方案，但是由于我感觉没有swfobject好用，就没有介绍，也从未使用过。\n现在，Adobe发布了Flash Player Detection Kit 1.5，使用它的方式在页面中嵌入Flash影片，并支持Flash Player 8快速安装（ExpessInstall），我详细研究了Adobe的源码，发现使用起来非常麻烦。那么，swfobject能实现快速安装么？当然可以！而且非常的简单！看看下面的JavaScript代码：\n1\u0026lt;script src=\u0026#34;/swfobject.js\u0026#34; type=\u0026#34;text/javascript\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 2\u0026lt;p id=\u0026#34;flashcontent\u0026#34;\u0026gt; 您的浏览器不支持JavaScript，或者您禁用了JavaScript。 3请启用浏览器的JavaScript支持。 4如果您确认启用了JavaScript，请使用下面的链接升级Flash Player到最新版本 5\u0026lt;a href=\u0026#34;http://www.adobe.com/go/getflash/\u0026#34;\u0026gt;安装最新的Flash Player插件\u0026lt;/a\u0026gt; 6\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; var so = new SWFObject(\u0026#34;/flashmovie.swf\u0026#34;, \u0026#34;mymovie\u0026#34;, \u0026#34;200\u0026#34;, \u0026#34;100\u0026#34;, \u0026#34;8\u0026#34;, \u0026#34;#336699\u0026#34;, \u0026#34;true\u0026#34;); so.write(\u0026#34;flashcontent\u0026#34;); \u0026lt;/script\u0026gt; 和swfobject的使用一文中，最大的改变就是在声明so时，在最后面添加了一个参数true。这个参数告诉swfobject：我们要使用快速安装功能。当然，我们要把需要的Flash Player版本设置成8。\n如果希望在升级成功之后跳转到一个特定的页面，可以使用下面的代码（加在so的定义下方）：\n1so.setAttribute(\u0026#39;xiRedirectUrl\u0026#39;, \u0026#39;http://www.zengrong.net\u0026#39;); //必须使用绝对URL，即以http://开头 如果不指定xiRedirectUrl，在升级完毕后，会重新跳转到当前页面。\n同时，下载这个as文件，将它放在fla文件相同的目录：\n1 文件 在fla的 _root 第一帧写上下面的代码：\n1#include \u0026#34;expressinstall.as\u0026#34; 2var install = new ExpressInstall(); 3if (install.needsUpdate) { 4stop(); 5install.init(); 6} 就这样，简单把？\n","date":"2006-06-15","description":"","lastmod":"2006-06-15T14:24:52Z","slug":"swfobject-express-install","tags":["flashplayer","swfobject"],"title":"用swfobject实现FlashPlayer8的快速安装（Express Install）","url":"https://blog.zengrong.net/post/swfobject-express-install/"},{"categories":["others"],"content":" ","date":"2006-06-11","description":"","lastmod":"2006-06-11T03:25:22Z","slug":"huge","tags":[],"title":"胡戈的最新短片：鸟笼山剿匪记","url":"https://blog.zengrong.net/post/huge/"},{"categories":["technology"],"content":"下面这行代码中，this是一个影片剪辑，实例名为 upload_mc ，up_btn 是库中的一个按钮的标识符。此按钮中有一个动态文本域关联label变量。按道理说，下面的代码会把按钮attach到舞台上并且将其中的动态文本值设置为“浏览...”。\n1browse_btn = this.attachMovie(\u0026#34;up_btn\u0026#34;,\u0026#34;browse_btn\u0026#34;,1); 2browse_btn.label = \u0026#34;浏览...\u0026#34;; 可是，没有作用。按钮中的动态文本域仍然为空。反而是下面的代码起了作用：\n1browse_btn = this.attachMovie(\u0026#34;up_btn\u0026#34;,\u0026#34;browse_btn\u0026#34;,1); 2label = \u0026#34;浏览...\u0026#34;; 很奇怪把？我试试把库中的按钮类型更改为影片剪辑，老的代码就有用了。而且也可以这样使用了：\n1browse_btn = this.attachMovie(\u0026#34;up_btn\u0026#34;,\u0026#34;browse_btn\u0026#34;,1,{label:\u0026#34;浏览...\u0026#34;}); 查看一下调试器，发现当 up_btn 为按钮时，它居然不会出现在舞台中！也就是说，label直接就暴露在 upload_mc 中了。按钮居然没有实体，只有一个引用！\n下图是当 up_btn 为按钮时的调试器截图：\n下图时当 up_btn 为影片剪辑时的调试器截图：\n看了看帮助，原来Button类并不像我以前所想的继承于MovieClip类，而是继承自Object类。难怪了……:em15:\n","date":"2006-06-09","description":"","lastmod":"2006-06-09T10:04:21Z","slug":"button-movieclip","tags":["actionscript","flash"],"title":"同一行代码，Button和MovieClip的比较","url":"https://blog.zengrong.net/post/button-movieclip/"},{"categories":["others"],"content":"有点像以前的“图标大战”，不过这次把战场搬到Flash界面上了:smile:\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2006-06-08","description":"","lastmod":"2006-06-08T12:54:17Z","slug":"flash8-war","tags":["flash"],"title":"Flash另类玩法","url":"https://blog.zengrong.net/post/flash8-war/"},{"categories":["use"],"content":"从幻灭的麦克风那里得到消息，Google Spreadsheet已经发布了（见这里），就跑去注册了一个。又“顺便”看到了Thumbstacks的消息，也去注册了一个。\n到现在为止，我就已经拥有了在线Word、Excel和PowerPoint的账号了……嘿嘿:em02:\n下面是几个截图：\n在线文字处理器Writely（Word）：\n在线电子表格SpreadSheet（Excel）：\n在线幻灯片制作Thumbstacks（PowerPoint）：\n","date":"2006-06-07","description":"","lastmod":"2006-06-07T01:42:20Z","slug":"spreadsheet-thumbstacks-writely-word-excel-powerpoint","tags":["excel","weboffice","word"],"title":"在线版的Word、Excel和PowerPoint","url":"https://blog.zengrong.net/post/spreadsheet-thumbstacks-writely-word-excel-powerpoint/"},{"categories":["impressions"],"content":"每天超过6小时对着显示器，眼睛难受呀（俺还没能跨入液晶一族）！这不，俺们领导也意识到这个问题，委托俺做一个十几分钟的眼保健操＋轻音乐，每天10点和16点的时候放一次，让忘记休息的同事可以趁机休息一下眼睛，也顺便换掉目前的上下班音乐。感谢小婧子提供音乐 :cool:\n音乐一共16分24秒，先听听：\n您的浏览器不支持音频！！！ 为了方便在线播放，我已经转成了32000HZ采样率，64kbps码率。实际的音乐是44100HZ采样，立体声的无压缩WAV音频。\n基于小婧子的要求，已经改成44100HZ采样率，128kbps码率。文件大小15.7MB，采用LAME压缩，应该可以满足大多数没有经过训练的耳朵（就像我这样）的需求。\n:mrgreen:\n00'00-02'25 竖琴独奏Swallow TaiReal 一段欢快的音乐，大家可以去洗手或者闭目养神 02‘35-08’09 晶体浴面操 由于是从磁带翻录的，质量不太好，虽然我做过降噪处理。顺便说一句，由于第一句“同学们，保护视力...”中的“同学们”没有办法改成“同事们”，因此删掉了这句让人倍感亲切的话。 感谢斧门人提供了高质量的晶体浴面操音乐！:em02: 08’10-11‘06 长笛独奏Cuchulainn 很煽情，很热切，很悠扬 11'07-16'24 竖琴协奏Simply Beautiful 舒缓绵长 我这里的眼保健操有两种，一种是我们小学时做的第一节是“揉天阴穴”的那种，一种就是现在学校里面做的“晶体浴面操”。前者的音质更好，但我用的是后者，主要是下面几个原因：\n会做的人更多。新的眼保操大约是从我初中开始换的，所以现在中心30岁以下的同事应该都会做 新的总比旧的好 晶体浴面操前面两节是睁眼做的 上学的时候就觉得这两节设计的特别好，知道我们不想做眼保操，给我们两节的时间用来偷懒 :em01:\n下面是旧的眼保操和新的眼保操音乐：\n晶体浴面操\n您的浏览器不支持音频！！！ 旧眼保健操\n您的浏览器不支持音频！！！ 另外，为了找到一休哥说的“休息，休息一会儿”原声，我google了N个论坛，终于在一段动画片里面把它截了下来，真是辛苦呀……:em17:\n放在这里，谁愿意要的拿去了，不收费的 :em06:\n一休哥说：休息，休息一会儿\n您的浏览器不支持音频！！！ ","date":"2006-06-06","description":"","lastmod":"2006-06-06T01:51:16Z","slug":"have-a-rest","tags":[],"title":"聪明的一休说：休息，休息一会儿眼睛","url":"https://blog.zengrong.net/post/have-a-rest/"},{"categories":["technology"],"content":"DOCMAN是Mambo/Joomla!的插件，我认为它是最好的下载插件。此插件的效果可以看这里。\n但是，从我升级到Joomla1.0.8（DOCMAN版本1.3RC2）以来，如果上传的文件名包含中文，那么上传到服务器的文件就会变成乱码。但是，这个文件名在DOCMAN的管理界面中却显示正常。\n这个问题的原因，是因为我使用了UTF-8编码，因此，我需要修改DOCMAN，让它自动更改我上传的文件名。\n找到DOCMAN_file.class.php文件中的_upload函数定义（大约在381行），修改它为如下所示：\n1function _upload($name, $temp_name, $path) 2{ 3if (is_uploaded_file($temp_name)) { 4$name = time().stristr($name,\u0026#34;.\u0026#34;); 5if (move_uploaded_file($temp_name, $path.\u0026#34;/\u0026#34;.$name)) { 6$file = new DOCMAN_File($name, $path); 7return $file; 8} else { 9$this-\u0026gt;_err = _DML_DIRPROBLEM.\u0026#34; \u0026#34;; 10} 11} else { 12$this-\u0026gt;_err = _DML_DIRPROBLEM2.\u0026#34; \u0026#34;; 13} 14return false; 15} 只是在原代码中加入了第4行。\n","date":"2006-06-05","description":"","lastmod":"2006-06-05T01:40:41Z","slug":"docman","tags":["cms","joomla","mambo","mysql","php","plugin"],"title":"自动修改DOCMAN上传的文件名称","url":"https://blog.zengrong.net/post/docman/"},{"categories":["technology"],"content":"今天改过服务器的php.ini文件后，发现所有的页面都不能访问，显示为：“No input file specified.”\n由于要同时支持.NET，服务器采用的是IIS。我改用Apache就没有这个问题。\n这个问题很让人头疼。google了一阵之后没有找到好的方法。大都是说因为CGI的安全认证问题，而使用ISAPI模块方式安装PHP则没有这个问题。可是我恰恰就是用ISAPI模块方式安装的，为什么还有这个问题？\n在微软的网站找到这篇文章How IIS launches a CGI application，里面讲到在注册表中添加CreateProcessAsUser值的方法（如下）：\nNOTE: The following is the full path to the registry key:\nHKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\\nW3SVC\\Parameters\n• Modify the registry to run all applications as the system.\nUnder the W3SVC/Parameters, place the value CreateProcessAsUser as a REG_DWORD and give it a value of 0. This causes the CGI to be ran with the CreateProcess API and run in the system context. This has serious security implications because CGI scripts will have much greater access to the system than they normally would.\n我照上面的说明添加了，仍然没有用。\n于是把ISAPI删除，改成CGI模式安装PHP，然后修改PHP.ini文件中的cgi.force_redirect值为0，重启IIS。错误依旧。\n到现在，我大致已经知道出现这个错误可能是因为没有找到PHP ISAPI模块的DLL文件，但是我配置的所有路径都正确呀！\n接着试验。禁用IIS6中的php扩展，此时访问网站出现404错误。为什么会出现404错误？这是没有找到网页文件的错误呀！看来可能是配置问题了。\n想起来刚才改过PHP.ini中的doc_root行，打开ini文件注释掉此行，然后重启IIS，一切正常了。\n其实，在虚拟主机中配置php.ini中的doc_root，实际上没有什么意义，因为提供虚拟主机的IIS，一般都不会有实际的doc_root，而是每个虚拟主机一个root。我煞费苦心的跑去设置doc_root，实在是多此一举，浪费表情 :roll:。\n","date":"2006-06-04","description":"","lastmod":"2006-06-04T15:12:53Z","slug":"no-input-file-specified","tags":["php"],"title":"No input file specified.错误的解决","url":"https://blog.zengrong.net/post/no-input-file-specified/"},{"categories":["web"],"content":"把多媒体空间程序升级到Joomla1.08后，发现用户在登录之后会显示成这样：\n这是个不大不小的问题，但是一定要解决它。今天在joom.org.ru发现了解决方法：\n在使用1.08时，大家有没有发现在登陆之后，出现的欢迎词不对，是hi,0什么用户进去都是这个提示，（谢谢autoit的提醒，在joomla论坛找一下就发现了这个补丁。\n出处：http://forum.joomla.org/index.php/topic,46684.0.html\n补丁下载：1.0.8-solution-login_module_problems 5.80 Kb\n用法很简单\nsef.php文件覆盖　/includes/sef.php mod_login.php文件覆盖　/modules/mod_login.php 由于我的网站使用UTF-8编码，因此将上面的两个补丁文件进行了转码。转码后的文件可以在这里下载：\n1.0.8-solution-login_module_problems.zip\n","date":"2006-06-04","description":"","lastmod":"2006-06-04T03:29:02Z","slug":"195","tags":["cms","joomla"],"title":"解决Joomla1.08的登录问题","url":"https://blog.zengrong.net/post/195/"},{"categories":["technology"],"content":"在制作大头贴程序时，发现直接使用BitmapData类绘制Video对象的像素值时，虽然已经设定了Video的 _width 、_height 属性，BitmapData还是只能绘制160×120个像素。\n后来发现，Video的实际像素值是和width与height属性相关。这两个属性都是只读属性，不能设置。它们的值来源于Camera。使用 Camera.setMode 可以设置这两个值。\n但是，即使使用setMode设定了Camera的属性，BitmapData仍然只能绘制160×120像素！代码如下：\n1import flash.display.BitmapData; 2var cam:Camera = Camera.get(); 3var bmp:BitmapData = new BitmapData(200, 200, false); 4my_video.attachVideo(cam); 5test_btn.onPress = function() { 6cam.setMode(200, 200, 15); 7bmp.draw(my_video); 8my_mc.attachBitmap(bmp, 0); 9}; 在这里可以看到效果（需要摄像头和Flash Player 8）：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n怎么办？我的方法是使用一个影片剪辑包含Video对象。代码如下：\n1import flash.display.BitmapData; 2var cam:Camera = Camera.get(); 3var bmp:BitmapData = new BitmapData(200, 200, false); 4myVideo_mc.my_video.attachVideo(cam); 5test_btn.onPress = function() { 6cam.setMode(200, 200, 15); 7bmp.draw(myVideo_mc); 8my_mc.attachBitmap(bmp, 0); 9}; 下面是效果：\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n下载源文件：\n1 文件 ","date":"2006-06-01","description":"","lastmod":"2006-06-01T16:29:56Z","slug":"video2bitmapdata","tags":["as2","bitmapdata","camera","flash"],"title":"BitmapData类不能正常获取Video中的像素","url":"https://blog.zengrong.net/post/video2bitmapdata/"},{"categories":["technology"],"content":" setMode（Camera.setMode 方法）\npublic setMode([width:Number], [height:Number], [fps:Number], [favorArea:Boolean]) : Void\n参数\nwidth:Number [可选] - 请求的捕获宽度，以像素为单位。默认值为 160。\nheight:Number [可选] - 请求的捕获高度，以像素为单位。默认值为 120。\nfps:Number [可选] - 摄像头捕获数据应使用的请求速率，以每秒帧数为单位。默认值为 15。\nfavorArea:Boolean [可选] - 布尔值，指定如果摄像头不具有满足指定要求的本机模式时如何控制宽度、高度和帧频。默认值为 true，这意味着支持保持捕获大小；使用此参数选择与 width 和 height 值最匹配的模式，即使这样做会由于降低帧频而对性能有不利影响。若要最大限度地提高帧频，而不考虑摄像头的高度和宽度，请将 false 传递给 favorArea 参数。\n上面是从Flash 8 中文版帮助中摘录的，从文字中可以看到setMode方法的每一个参数都是可选的。但实际上，必须至少提供前面三个参数，此方法才会工作。同样的，在FMS的帮助中，描述也是错误的。\n利用下面这段代码可以检验一下：\n1var cam:Camera = Camera.get(); 2my_video.attachVideo(cam); 3//btn1和btn2是两个按钮元件 4btn2.onPress = function() { 5trace(my_video.width); //my_video是时间轴上的一个视频元件 6}; 7btn1.onPress = function() { 8cam.setMode(320, 240); 9//cam.setMode(320, 240, 24); 10}; 交替注释8、9行代码，可以看到效果。第8行代码并不会起作用。如果不使用第9行，那么显示的width值始终都是160（默认值）。\n同样描述错误的还有Camera.setMotionLevel 以及Camera.setQuality 方法。\n","date":"2006-06-01","description":"","lastmod":"2006-06-01T14:52:02Z","slug":"camerasetmode","tags":["camera","flash"],"title":"Flash8帮助中关于Camera.setMode方法的描述错误","url":"https://blog.zengrong.net/post/camerasetmode/"},{"categories":["technology"],"content":"SWFObjcet，原名为FlashObject，本站 曾经做过介绍 。为了使用方便，我仿照Adobe公布的模版制作了一个使用swfObject显示Flash内容的模版，只需要在发布影片的时候选择这个HTML模版，就可以免于在HTML页面中手工输入代码显示Flash影片。\n把下面的模版保存为 swfObject.html ，复制到下面的路径（使用你的具体设置）：\nX:\\Documents and Settings\\Your Name\\Local Settings\\Application Data\\Macromedia\\Flash 8\\zh_cn\\Configuration\\HTML\\ 然后在发布模版中选择swfObjcet并发布即可。如下图所示：\n模版的内容如下：\n1$TTSWFObject 2$DXDefaultDetectionActiveContent.html 3$DS 4在更新用于活动内容的 HTML 文件中显示 Macromedia Flash 电影。 注意: 使用此模板时，必须将 swfObject.js 复制到 HTML 输出文件夹。 5$DF 6 7\u0026lt;html xmlns=\u0026#34;http://www.w3.org/1999/xhtml\u0026#34; xml:lang=\u0026#34;zh-CN\u0026#34; lang=\u0026#34;zh-CN\u0026#34;\u0026gt; 8\u0026lt;head\u0026gt; 9$CS 10\u0026lt;title\u0026gt;$TI\u0026lt;/title\u0026gt; 11\u0026lt;script src=\u0026#34;swfObject.js\u0026#34; language=\u0026#34;javascript\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 12\u0026lt;/head\u0026gt; 13\u0026lt;body bgcolor=\u0026#34;$BG\u0026#34;\u0026gt; 14\u0026lt;div id=\u0026#34;flashContent\u0026#34;\u0026gt; 15\t\u0026lt;object classid=\u0026#34;clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\u0026#34; codebase=\u0026#34;http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=$FV,$JR,$NR,0\u0026#34; width=\u0026#34;$WI\u0026#34; height=\u0026#34;$HE\u0026#34; id=\u0026#34;$TI\u0026#34; align=\u0026#34;$HA\u0026#34;\u0026gt; 16\t\u0026lt;param name=\u0026#34;allowScriptAccess\u0026#34; value=\u0026#34;sameDomain\u0026#34; /\u0026gt; 17\t$PO 18\t\u0026lt;embed $PEwidth=\u0026#34;$WI\u0026#34; height=\u0026#34;$HE\u0026#34; name=\u0026#34;$TI\u0026#34; align=\u0026#34;$HA\u0026#34; allowScriptAccess=\u0026#34;sameDomain\u0026#34; type=\u0026#34;application/x-shockwave-flash\u0026#34; pluginspage=\u0026#34;http://www.macromedia.com/go/getflashplayer\u0026#34; /\u0026gt; 19\t\u0026lt;/object\u0026gt; 20\u0026lt;/div\u0026gt; 21\u0026lt;script language=\u0026#34;javascript\u0026#34;\u0026gt; 22\tvar so = new SWFObject($MO, \u0026#34;$TI\u0026#34;, \u0026#34;$WI\u0026#34;, \u0026#34;$HE\u0026#34;, \u0026#34;$FV.$JR.$NR\u0026#34;, \u0026#34;$BG\u0026#34;); 23\tso.addParam(\u0026#34;quality\u0026#34;, \u0026#34;$QU\u0026#34;); 24\tso.addParam(\u0026#34;wmode\u0026#34;, \u0026#34;$WM\u0026#34;); 25\tso.addParam(\u0026#34;salign\u0026#34;, \u0026#34;$SA\u0026#34;); 26\tso.addParam(\u0026#34;align\u0026#34;, \u0026#34;$HA\u0026#34;); 27\tso.addParam(\u0026#34;play\u0026#34;, \u0026#34;$PL\u0026#34;); 28\tso.addParam(\u0026#34;loop\u0026#34;, \u0026#34;$LO\u0026#34;); 29\tso.addParam(\u0026#34;scale\u0026#34;, \u0026#34;$SC\u0026#34;); 30\tso.addParam(\u0026#34;devicefont\u0026#34;, \u0026#34;$DE\u0026#34;); 31\tso.addParam(\u0026#34;name\u0026#34;, \u0026#34;$TI\u0026#34;); 32\tso.addParam(\u0026#34;menu\u0026#34;, \u0026#34;$ME\u0026#34;); 33\tso.addParam(\u0026#34;allowScriptAccess\u0026#34;, \u0026#34;sameDomain\u0026#34;); 34\tso.write(\u0026#34;flashcontent\u0026#34;); 35\u0026lt;/script\u0026gt; 36\u0026lt;/body\u0026gt; 37\u0026lt;/html\u0026gt; ","date":"2006-05-28","description":"","lastmod":"2006-05-28T06:41:51Z","slug":"swfobject-template","tags":["flash","swfobject"],"title":"基于SWFObject的Flash发布模版","url":"https://blog.zengrong.net/post/swfobject-template/"},{"categories":["web"],"content":"在Mambo/Joomla!系统中，可以使用{mospagebreak}标签对长文章进行分页。默认的情况下，被分页的文章中会自动显示一个标题为“跳转”的DIV，其中包含名称为“页面1”、“页面2”...的跳转链接。\n通常，我们会按照文章的含义，或者是长文章的“节”来分页。如果我要将默认的“页面1”、“页面2”改成当前每个分页面的节名称，或者一个有意义的标题，该怎么做？\n{mospagebreak}为我们提供了一个title参数，格式如下：\n{mospagebreak title=自定义标题}\n就是这么简单，“跳转”框中就会显示我们自定义的标题了。\n不过还有一个问题。默认的，在“跳转”框中，第一个分页链接总是自动的显示为整篇文章的标题，如何定义这第一个链接呢？\n{mospagebreak}还为我们提供了heading参数。它和title参数连用，可以完美的解决我们的问题：\n{mospagebreak heading=第一个标题\u0026amp;title=第二个自定义标题}\n请注意，两个参数中间用“\u0026amp;”来连接，而不是我们常用的空格。另外，最好不要在参数值外面加上英文引号，不信你试试就知道了 :cool:\n至于最后的效果，就看这里把：学科网站设计与制作培训班 讲义在线阅读\n","date":"2006-05-25","description":"","lastmod":"2006-05-25T12:46:39Z","slug":"mospagebreak","tags":["mambo"],"title":"{mospagebreak}的用法","url":"https://blog.zengrong.net/post/mospagebreak/"},{"categories":["impressions"],"content":"刚看过电影一球成名（Goal!）。\n不错的励志影片，值得每一个热爱足球，渴望成功的男人和男孩观看。影片吸引我的，不是贝克汉姆、齐达内、劳尔和阿兰·希勒的“倾力出演”，而是主人公自始至终对足球的热爱和自强不息的精神。尤其是片中的几场球赛，强烈的的现场感让人热血沸腾。\n主人公的成功与他的过去、家庭、亲情、友情、爱情的交织在一起，最后进球的那一刹那，我感动得热泪盈眶。\n不求成名，但求成功 成功无法衡量，但至少可以感受 最后，还是不能免俗贴几张剧照：:em05:\n另外：这是影片的第一部，第二部将在9月上映，第三部则在2006德国世界杯期间实景拍摄。期待中！\n","date":"2006-05-24","description":"","lastmod":"2006-05-24T06:08:28Z","slug":"goal","tags":[],"title":"一球成名","url":"https://blog.zengrong.net/post/goal/"},{"categories":["news"],"content":"由于SE|PY ActionScript Editor一直都更新频繁，我又比较懒，不想每个版本都更新。没想到今天突然从RC7升级到RC23，却给我带来了不小的惊喜。\n目前已发现的对我有用的新特性如下：\n中文界面回来了。 好像从1.5版本开始，即使有中文语言包，界面上仍然会显示英文。但是现在却正常了。 增加了正则表达式工具和SharedObject工具，不用借用其他软件了。 增加了在文件中进行“搜索/替换”操作的功能，我想在文件中搜索时不用再切换到Editplus了。 搜索菜单中加入了便签功能，类似于OutLook的便签，挺有趣的（虽然不一定有用）。 顺便配置了一下mtasc，发现在sepy中用它配合进行语法确实是“相当的”快，远远强过Flash IDE的语法检查。\nSE|PY ActionScript Editor同样也支持PHP、XML的编辑。如果再加上类似于Editplus的HTML工具栏就堪称完美了。\n还有一些出色的ActionScript编辑器，请看这里。\n","date":"2006-05-21","description":"","lastmod":"2006-05-21T15:17:59Z","slug":"sepy-asesepy-actionscript-editor","tags":["actionscript"],"title":"SE|PY ActionScript Editor v.1.5.2RC23的更新","url":"https://blog.zengrong.net/post/sepy-asesepy-actionscript-editor/"},{"categories":["news"],"content":"Blog和网站都转到了新的主机。同时把多媒体空间程序从Mambo4.5.2升级到Joomla!1.0.8，毕竟Joomla更加符合开源社区精神 :smile:。\n感谢 orphen 提供的主机空间 :em02:，感谢我自己的辛苦 :em06:……网站数据和文件倒来倒去很累呀 :em16:\n伯汉网络，我依然强烈地╭∩╮（︶︿︶）╭∩╮鄙视你！！！\n","date":"2006-05-18","description":"","lastmod":"2006-05-18T16:14:19Z","slug":"sitemove-2","tags":["joomla"],"title":"网站和Blog的主机迁移完成","url":"https://blog.zengrong.net/post/sitemove-2/"},{"categories":["news"],"content":"在写了Blog更换主机（千万不要购买伯汉网络的虚拟主机）之后的第二天，也就是今天，我发现我的网站多媒体空间仍然不能访问。于是去伯汉网络使用我的账号登录主机管理界面，发现我的账号已经不能登录！继而登录FTP，发现FTP也不能登录！\n就这样，在没有给我任何形式（我注册资料中的电话和E-mail都是真实的，伯汉曾经使用电话和E-mail和我联系过，证明他们并不是不知道我的联系方式）的通知的情况下，在没有取得我本人任何形式授权的情况下，伯汉网络非常无耻的停止了我2006年4月20日刚刚续费一年的虚拟主机空间。\n我无话可说，这可能是伯汉网络这个不负责任的主机商先前在发给我的邮件中声明的“主动采取措施”吧？也好，在不通知我的情况下采取这样的“主动措施”确实是非常的厉害，非常的解恨！但是，这确实不是一个上档次的主机提供商能够做出的举动。\n我只能说，伯汉网络“相当的”不要脸。\n","date":"2006-05-16","description":"","lastmod":"2006-05-16T14:42:52Z","slug":"woowoo2","tags":["host"],"title":"伯汉网络不要脸","url":"https://blog.zengrong.net/post/woowoo2/"},{"categories":["others"],"content":"由于伯汉网络虚拟主机经常无法访问，我不得不考虑换主机了。\n伯汉的虚拟主机已经使用了两年，去年的情况还比较好。但是自从将主机转到上海他们自己的机房后，我的网站就经常无法访问，不是数据库连接数太多禁止访问，就是出现Apache Service Temporarily Unavailable错误。论坛投诉，没有人回答，伯汉网页上的QQ在线咨询更是形同虚设，从来就没有回复过。虽然刚刚交了今年的年费，还是准备换掉了。\n我的Blog开启了“搜索引擎友好”功能（即URL重写），但是新的主机并不支持。因为新主机同时支持ASP和PHP，注定是使用IIS，而URL重写目前只有Apache支持。因此，网站上所有的文章地址都会改变。以前的文章 https://blog.zengrong.net/173/ ，现在的访问地址应该是 https://blog.zengrong.net/post/173.html。这导致使用搜索引擎搜索到的本站文章将无法访问。为了让网友不至于认为本站消失掉了，我自定义了一个404错误页面，当网友观看先前存在但现在地址已经改变的文章时，会看到这个提示。\n其他的，就不想多说了，只是很伤心，我曾经推荐那么多人去购买伯汉的主机（我并不是伯汉的代理商，也不使用伯汉的广告代码），现在看来是个错误。自己一直都认为伯汉是功能最强大的虚拟主机，自定义功能繁多，安装模块齐全。但无论多么强大的功能，只要有下面这两个缺点中间的任意一个，都不值得使用！而伯汉的虚拟主机却恰恰两个都有！\n1.非常不稳定！！ 2.基本上没有售后服务的概念 总之，我最想说的一点就是：\n千万不要购买伯汉网络的虚拟主机！ 另外：\n伯汉今天终于给我发来邮件，看来是我在论坛上发的帖子有了作用 :???:（虽然帖子已经被删除了），内容如下：\n尊敬的 media 用户：\n周末时您网站程序（inetd）对服务器造成很大压力，要求改进，如果继续出现情\n况的话，我们可能会主动采取措施。\n感谢您对我们工作的理解和支持。\n上海伯汉网络有限公司\n2006-05-15\n再去伯汉论坛逛逛，发现我发的所有帖子都已经被删除了，还包括其他用户投诉数据库不稳定等帖子，全部都消失不见。呵呵，不删才怪了。\n我给伯汉的回信：\n那就怪了，你们能否把流量记录出示一下？我的网站每日访问IP从来没有超过500，怎么可能对服务器造成“很大压力”？如果你们不相信，我可以提供51la的网站统计账号给你们。\n我在伯汉主机上的两个网站的统计账号：\nhttp://www.51.la/?313689\nhttp://www.51.la/?313691\n查看密码都是1111\n退一万步讲，即使是我网站的问题，你们这几天连一个邮件都没有？这就是你们的服务么？做主机服务，不能做到7×24，至少也可以做到7×8把？你们难道只能做到5×8？\n希望你们能够正视自己的问题，不要用某些不成立的理由来推搪。我不会再使用伯汉的虚拟主机了，不论你们采取什么措施，都与我无关。\n另外：\n发表这篇文章后，伯汉网络立刻将我刚刚续费一年的空间删除了，没有给我任何的提示。\n","date":"2006-05-14","description":"","lastmod":"2006-05-14T10:03:07Z","slug":"woowoo","tags":["host"],"title":"Blog更换主机（千万不要购买伯汉网络的虚拟主机）","url":"https://blog.zengrong.net/post/woowoo/"},{"categories":["impressions"],"content":"没想到，今天我居然亲身经历到这个骗局。\n早上9：10分，737公交车行至居仁门，突然听到我左后侧传来“嘭”的一声，泡沫居然溅到我身边。紧接着就有一男子声音：“你不长眼睛，开易拉罐小心一点撒，都溅到我身上！”。一位表情木纳的大嫂操着外地口音一劲儿陪不是，男子却是不依不饶。我本来就不喜看热闹，转回了头。哪知突然听到那男子的激动的声音：“你的易拉罐中奖了！八万呀！”马上就有一群人附和，坐在我前面的两人马上开始帮腔，说要买拉环。\n我心中暗笑，这托儿也太假了把！明眼人一看就穿呀。但暗自留心，看他们怎么来唱这出戏。\n我没有再回头，就听到车上的人七嘴八舌的出主意，说要去商场兑奖，有人又说兑奖要身份证，也有要她卖掉的。“大嫂”委屈的说自己没有带身份证，说自己只要一头买牛的钱，或者要一台电视机。前面两个托儿开始晒笑，又怀疑这个拉环过期了。我faint，怎么就不说这个拉环是假的？“大嫂”带着拉环走过来，要前面的两个托儿帮忙辨别，前面两个托儿看了后煞有介事的说是没有过期，其中一个掏出身上所有的钱要买。因为交易就在我眼前，我瞟了一眼，大约150元把。“大嫂”当然不乐意，嘴里不停说，“我就要买头牛……”。\n这是，坐在我前面的一个外地人心动了（由于他背对我，我没看出他是不是托），好像要买的样子，但说身上没有钱。前面的两个托说，用银行卡也可以呀！前面那人掏出银行卡，要“大嫂”和他一起下车，去银行取钱。“大嫂”不干，重复要她的牛或者现钱。一个托儿说：“那就让他把银行卡密码告诉你，你自己去取”。前面那人好像有些心动，说：“我把身份证也给你把。”。我开始感觉这人是不是真上当了，或者就是个托。这时，后面又有三个托围上来，把他和我围在中间。\n我坐的是单人的座位，后面就是车的后门，我前面就是那个正在上钩的白痴（或者也是一个托），所以我就处在托儿们的中心地带。前面两个托儿盯着那个白痴托的银行卡和钱包，后面三个围上来的托儿盯着我，让我打消了用手机把这出闹剧拍下来的想法。车到站了，我实在难受，推开盯着我的两个托下车了，下车前，重重的撞了那个离我最近的托一下。他没动，也没看我，估计是太投入了，或者是不想惹事。\n下车后立刻拨打110：“我刚从737公交车上下来，现在车在仁寿路车站，车牌是......”，还没说完，110接警的大姐就打断我的话：“是不是有人用健力宝诈骗？我们就是不知道车号。”“好了，你现在知道了，赶快去处理把。”挂断电话一会儿，就看到一辆警车呼啸而过。不是把，效率这么高？\n我上了一辆TAXI，追上737。737在太平洋站停下，但没看到警车在侧。我原来座的那个位置也没有那么多人围着了。我突然觉得不对劲：难道，他们是把我当作诈骗对象？不会把……\nTAXI开到737前面，我又一次拨打110报告情况。\n过了1分钟，我的电话响了。\n“你是刚才报警的人把！你在哪里？”\n“我在TAXI上。”\n“什么？车上车下？”\n“我在的士上，不在公交车上。737公交车现在到宗关，我在它前面。”\n我faint！找我干吗，车号也告诉你们了，地点也告诉你们了，你们找我有什么用？赶快去找车呀！\n的哥说话了：“你就不要指望他们了，他们效率特低的。”。\n我在古田四路看到那辆呼啸而过的警车，他正在对一辆黑面的执法。Kao，你巡警管交警的事干吗！真后悔当时没有拦下那辆警车。\n也后悔自己没有把当时的对话录下来，真的很搞笑，好像演话剧。就是几个演员太烂了。\n不知道那个白痴（托）怎么样了。\n","date":"2006-05-13","description":"","lastmod":"2006-05-13T02:46:56Z","slug":"jianlibao","tags":[],"title":"健力宝拉环骗局","url":"https://blog.zengrong.net/post/jianlibao/"},{"categories":["others"],"content":"韩国rg animation studios制作的Backkom系列动画，讲述一个可爱却有些倒霉的北极熊的故事，非常搞笑！\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2006-05-10","description":"","lastmod":"2006-05-10T05:41:41Z","slug":"flickr","tags":[],"title":"韩国的倒霉熊Backkom","url":"https://blog.zengrong.net/post/flickr/"},{"categories":["impressions"],"content":"几天前接到一个同学的电话，说她有个“熟人”找我帮孩子介绍工作。这个“熟人”我也在很久之前见过面，仅仅是认识，但也有数年没有联系了。\n挂掉电话，我突然觉得很搞笑。我既不是公司老总，也不是业界精英，干嘛找到我这里来？况且我还是一个仅仅是知道她名字的人。不过看在同学的面子，还有这人的孩子也是同我一样学习设计专业，算是有缘，就答应了我的同学。\n昨天接到这个“熟人”的电话，说她儿子装潢设计专业毕业，会使用CorelDraw、CAD、Photoshop等等，当时打电话时她儿子就在旁边，她不清楚的东西就问她儿子，她儿子再转告她，然后她转告我。我当时就比较纳闷：干嘛不叫你儿子直接找我说？大学毕业了，已经不是小孩子。\n她问我是不是自己开公司，我告诉她不是，她似乎很失望，然后问我能否把他的孩子安排在我身边跟我实习，能学到更多的东西，我也可以多照顾他。\n我确实应该承担这个责任，为了我们祖国下一代的栋梁，我应该照顾他让他免受风雨，避免让他幼小的心灵受到创伤，然后把我的所学倾囊相授，再帮他物色一个满意的工作。\n可是我没法这么做。我们公司早就过了接纳实习生的时间，我自己的工作尚且还要加班完成，我又如何能够带一个人在身边，还要教导他？暂且不论他的设计水平如何或者行为习惯如何，我所知道的是，对于设计专业的优秀学生来说，还没有毕业就一定在自己赚钱糊口了。\n我还记得，有一次我带两个研究生实习，其中一个在我整理书籍材料时，坐在椅子上用办公电话打给他在外地实习的同学，告诉他在这里无事可做，每天很闲。\n不过，我还是答应下来，帮他的儿子留意。\n说是“留意”，其实是主动出击了。\n我打电话给一个做平面多年的老同学，问她有没有类似的要求。她给我一个包装设计公司负责人的电话，说该公司可能需要兼职的平面设计人员。我很高兴，毕竟我可以兑现自己的诺言。\n打电话给我的“熟人”，是她儿子接电话，我暂且把她儿子称为“A”，下面是电话实录：\n我：你好！×××在么？\nA：我是她儿子，你是谁？\n我：你妈妈前天电话给我，要我帮你找工作，我联系了一家公司，你有没有兴趣？\nA：他们是做什么的？\n我：做礼品包装。\nA：是不是用CorelDraw和Photoshop设计？\n我：应该是，如果你有兴趣，我把公司负责人电话给你。\nA：他们公司在什么地方？\n我：我不太清楚，你可以直接问他。\nA：......\n我：我把他电话告诉你把，你在记吗？\nA：......\n我（无法忍受无语）：他的电话是×××××××××，名字×××，你告诉他是××介绍的就行。\nA：哦\n我：你不清楚的，就直接问他好了，他是公司老板。\nA：哦\n我：......\nA：你姓什么？\n我：曾\n我还想说什么，可对方没有动静，我只好说声再见挂断了电话。\n说实话，挂断电话之后的30秒钟时间里，我很生气，因为我觉得他对我缺少起码的礼貌和尊重，连一句最基本的谢谢都没有！整个电话过程，我感觉像和一个90年代初的邮局工作人员大姐讲话，好像我该他一大笔钱没还，语气生硬平淡，到最后我差点以为是我在求他办事了。可是想想我又冷静下来，我做这件事情就是为了一声谢谢么？是为了他因为我的帮助而热烈和奉承的语气么？说是为了祖国的未来未免可笑，也许我只是为了一个面子而已。\n今天，我又接到A的电话，以下是实录：\nA：你是那个昨天帮我找工作的姓曾把？\n我：是（我只能回答是）\nA：你有装潢设计的工作么？\n我：你可以直接问他们公司\nA：我就在他们公司，他们公司在武昌，我觉得太远了。\n我：......\nA：他们的工作我做不了，你在汉口有工作吗？\n我：没有\nA：你不是开公司的？\n我：不是\n我平静但果断的挂断了电话，甚至没有说声再见。但我的心里却久久无法平静，我感觉被人愚弄了，我的努力全部都是白费，我竟然帮这么个白痴找工作！\n中华民族是礼仪之邦，我可以无偿帮人找工作，甚至也不需要一声谢谢，但我不能容忍我帮助的对象是一个没有一点点基本的社会经验，永远生活在父母的保护伞之下的“孩子”！\n我只希望，我碰到的这个大学生，仅仅是我们千千万万孩子中的一个特例，我不敢想像，这样的孩子今后会如何。他们是“花朵”，但我觉得他们更应该认为自己是“小草”！只有小草，才能够不怕大雨淋湿了自己的身体，不怕狂风吹断了自己腰身，茁壮倔强地努力生长，真正实现自己的价值。\n但愿他们能一路走好。\n","date":"2006-05-09","description":"","lastmod":"2006-05-09T13:46:49Z","slug":"our-children","tags":[],"title":"我们的孩子","url":"https://blog.zengrong.net/post/our-children/"},{"categories":["impressions"],"content":"前前后后写了一年的《互动教学课件制作提高教程》，可能无法使用传统渠道出版了，心里感觉怪怪的，就好像自己辛辛苦苦教出的学生，毕业后忽然无法找到出路；或是自己一力抚养的孩子，长大了却没有归宿。\n不过，我并不感到难过，一本著作，或者一套教程，用来发表的方式还有许多，对于“课件”这个我研究了数年的东西，不论现在如何，我始终都有一种怀旧的感情。但愿这本书能为多媒体课件的“复兴”或者“进步”做一点点贡献。\n书的内容会不定时的发布在多媒体空间网站上，我总要为我这一年来的懒惰羞愧一下了 :wink:\n另外：\n今天在华储网和互动出版网看到断货已久的《Flash MX互动教学课件制作实例教程》重新到货了，很开心，这是个好兆头，嘿嘿 :em01:\n","date":"2006-05-07","description":"","lastmod":"2006-05-07T07:06:07Z","slug":"cai3","tags":["authorware","director","flash"],"title":"关于《互动教学课件制作提高教程》","url":"https://blog.zengrong.net/post/cai3/"},{"categories":["use"],"content":"Excel的自定格式是很有用的功能，为了方便使用，我直接把Excel中相关的帮助复制出来了，见这里。\n现在要利用自定格式实现两个功能：\n让Excel能够根据电话号码识别手机和座机； 让Excel可以在日期后面加上星期； 第一个好办，在数字格式中使用条件即可，代码如下：\n[\u0026gt;9999999999]\u0026quot;手机：\u0026quot;0;[\u0026lt;9999999999]\u0026quot;座机：0\u0026quot;0;\n我不知道在自定义格式的条件中如何确定数字的位数，好像不能使用表达式？所以只能用这个笨办法，大于9999999999（10位）的就是手机，否则就是座机了（当然也包含小灵通 :em06:）。\n如下图所示：\n至于第二个，使用下面的代码来显示2006-5-5的星期数。\nyyyy-m-d dddd 但是，显示出来的英文（:em17:），怎么换成中文？谁知道？只好将就了。\n范例文件下载地址：\n1 文件 ","date":"2006-05-05","description":"","lastmod":"2006-05-05T09:57:52Z","slug":"number-custom","tags":["excel","office"],"title":"Excel自定数字格式-处理电话号码和日期","url":"https://blog.zengrong.net/post/number-custom/"},{"categories":["web"],"content":"在 如何在WordPress后台中加入表情符号 一文中，我介绍了使用 wp-grins 插件在WordPress后台中添加表情符号的方法。如果不满足于WordPress自带的表情符号，可以自行添加。\n例如，要把 Emotions 插件中的表情符号增加到WordPress后台，只需要进行下面的步骤：\n注意： 要实现同本文相同的效果，请在进行下面的步骤之前，阅读 如何在WordPress后台中加入表情符号 一文。\n1. 下载Emotions插件，解压后将images文件夹下的所有文件上传到/wp-includes/images/smilies/中；\n2. 编辑/wp-includes/vars.php，搜索 ':mrgreen:' =\u0026gt; 'icon\\_mrgreen.gif' ,（大致在91行），在其下方添加如下代码：\n1\u0026#39;:em01:\u0026#39; =\u0026gt; \u0026#39;em01.gif\u0026#39;, 2\u0026#39;:em02:\u0026#39; =\u0026gt; \u0026#39;em02.gif\u0026#39;, 3\u0026#39;:em03:\u0026#39; =\u0026gt; \u0026#39;em03.gif\u0026#39;, 4\u0026#39;:em04:\u0026#39; =\u0026gt; \u0026#39;em04.gif\u0026#39;, 5\u0026#39;:em05:\u0026#39; =\u0026gt; \u0026#39;em05.gif\u0026#39;, 6\u0026#39;:em06:\u0026#39; =\u0026gt; \u0026#39;em06.gif\u0026#39;, 7\u0026#39;:em07:\u0026#39; =\u0026gt; \u0026#39;em07.gif\u0026#39;, 8\u0026#39;:em08:\u0026#39; =\u0026gt; \u0026#39;em08.gif\u0026#39;, 9\u0026#39;:em09:\u0026#39; =\u0026gt; \u0026#39;em09.gif\u0026#39;, 10\u0026#39;:em10:\u0026#39; =\u0026gt; \u0026#39;em10.gif\u0026#39;, 11\u0026#39;:em11:\u0026#39; =\u0026gt; \u0026#39;em11.gif\u0026#39;, 12\u0026#39;:em12:\u0026#39; =\u0026gt; \u0026#39;em12.gif\u0026#39;, 13\u0026#39;:em13:\u0026#39; =\u0026gt; \u0026#39;em13.gif\u0026#39;, 14\u0026#39;:em14:\u0026#39; =\u0026gt; \u0026#39;em14.gif\u0026#39;, 15\u0026#39;:em15:\u0026#39; =\u0026gt; \u0026#39;em15.gif\u0026#39;, 16\u0026#39;:em16:\u0026#39; =\u0026gt; \u0026#39;em16.gif\u0026#39;, 17\u0026#39;:em17:\u0026#39; =\u0026gt; \u0026#39;em17.gif\u0026#39;, 18\u0026#39;:em18:\u0026#39; =\u0026gt; \u0026#39;em18.gif\u0026#39;, 3. 一切搞定！\n下载修改好的vars.php：\n1 文件 修改后的后台效果截图：\n","date":"2006-05-04","description":"","lastmod":"2006-05-04T09:05:33Z","slug":"addemotions","tags":["wordpress","plugin","emotion"],"title":"在WordPress后台中增加自定义表情","url":"https://blog.zengrong.net/post/addemotions/"},{"categories":["web"],"content":"一直比较懒，也比较忙。Wordpress2.0.2出了这么长时间，却没有升级。五一终于有了时间，升级啦 :em02:\n官方的升级说明在这里（English）。\n这是一个安全更新，升级不需要运行 install.php 或者 upgrade.php，也不会修改数据库。对于所有的WordPress更新， /wp-content/ 目录都会保留并且不会改变。\n删除 /wp-admin/ 文件夹； 删除 /wp-includes/ 文件夹；注意：如果有/wp-includes/languages/ 目录，请先行备份。 删除根目录下除 wp-config.php 外的所有文件； 下载新版本WordPress2.02并解压缩； 上传 /wp-admin/ 和 /wp-includes/ 目录； 上传根目录所有文件； 恢复备份的 /wp-includes/languages/ 文件夹。 一切搞定！\n","date":"2006-05-03","description":"","lastmod":"2006-05-03T05:53:03Z","slug":"wp201to202","tags":["wordpress"],"title":"把WordPress从2.0.1升级到2.0.2","url":"https://blog.zengrong.net/post/wp201to202/"},{"categories":["technology"],"content":"WAMP，就是：Windows、Apache、MySQL和PHP，用于快速在Windows系统上搭建PHP开发和运行平台。\n当然，也有LAMP（Linux、Apache、MySQL和PHP）。\nXAMPP推荐！\n有for Linux、Windows、Mac OS X、Solaris等操作系统版本，还有一个Lite版本\n最新版本1.5.2，包括Apache HTTPD 2.2.0, MySQL 5.0.20, PHP 5.1.2 + 4.4.2-pl1 + PEAR + Switch, MiniPerl 5.8.7, Openssl 0.9.8a, PHPMyAdmin 2.8.0.3, XAMPP Control Panel 2.2, eAccelerator 0.9.4-rc1, Webalizer 2.01-10, Mercury Mail Transport System for Win32 and NetWare Systems v4.01a, FileZilla FTP Server 0.9.14a, SQLite 2.8.15, ADODB 4.80, Zend Optimizer 3.0.0, XAMPP Security\nLite版本包括Apache 2.2.0 + PHP 5.1.2 (without PEAR) + MySQL 5.0.20 + phpMyAdmin 2.8.0.3 + Openssl 0.9.8 + SQLite 2.8.15 + eAccelerator 0.9.4 rc1 PHPnow（国产!）推荐！\nWin32 下绿色免费的 Apache + PHP + MySQL 环境套件包。\n简易安装、快速搭建支持虚拟主机的 PHP 环境。附带 PnCp.cmd 控制面板，帮助你快速配置你的套件，使用非常方便。\nPHPnow 是绿色的，解压后执行 Setup.cmd 初始化，即可得到一个 PHP + MySQL 环境。\n然后就可以直接安装 Discuz!, PHPWind, DeDe, WordPress 等程序。\n* 支持虚拟主机：便捷的虚拟主机管理\n* 虚拟主机代理：可与 IIS 共存 (泛解析代理)\n* 插件支持：ASP | ASP.NET | JSP\n喜欢自己配置 PHP 环境？\nPHPnow 仅使用了 DOS 命令脚本 和 PHP 程序，完全可以自己修改，打造自己的套件！\n手动配置 PHP 还是有必要掌握的，PHPnow 只是为你减少重复的动作。 EasyPHP\n当前最新版本1.8，包含Apache1.33、PHP4.3.10、Mysql4.1.9和PHPmyAdmin2.6.1 Uniform Server(miniServer)\n最新版本3.3，包括Apache2, Perl5, PHP5, MySQL5，可以以插件方式支持PHP4 AppServ\n最新版本2.4.5，包括Apache 1.3.34、PHP 4.4.1、MySQL 5.0.16、phpMyAdmin-2.5.7-pl1 2009年3月3日11:23:48更新\n","date":"2006-05-02","description":"","lastmod":"2006-05-02T06:29:38Z","slug":"wamp","tags":["apache","cms","mysql","php","server"],"title":"几个常用的WAMP套件","url":"https://blog.zengrong.net/post/wamp/"},{"categories":["use"],"content":"使用表达式进行计算时，往往需要提供单元格，而对单元格的引用包括绝对引用和相对引用两种：\n相对引用\n例如下图的求和函数SUM，使用的即是相对引用，计算出B2至D2之间所有单元格的值。\n当使用自动填充功能对所有的行进行计算时，Excel就会自动将引用的单元格根据行号进行改变。例如将B2改为B3、D2改为D3。如下图所示。\n绝对引用\n有时，我们不需要Excel根据行号来自动改变单元格，就需要使用绝对引用。绝对引用只需要在被引用的单元格前面加上$符号即可。例如对B2单元格进行绝对引用，就是$B$2。如下图所示。\n混合引用\n当然，也可以灵活使用$符号，让行绝对引用、列相对引用，例如B$2；或者行相对引用，列绝对引用，例如$B2。\n","date":"2006-04-29","description":"","lastmod":"2006-04-29T13:43:53Z","slug":"excel-quote","tags":["excel"],"title":"Excel中的绝对引用和相对引用","url":"https://blog.zengrong.net/post/excel-quote/"},{"categories":["web"],"content":"最近用firefox比较多，主要是因为IE7 beta有时会莫名其妙的出问题，例如Google Calendar，使用IE7 beta就不能访问。而使用firefox，内存占用特别大，随便开几个页面，内存就使用上百MB。在幻灭这里找到了这个方法，试试还挺管用。\n1.打开FireFox，在地址栏里输入about:config\n2.按右键，选择新建——\u0026gt;布尔\n3.在弹出的框里输入config.trim_on_minimize\n4.然后选True\n5.重新启动FireFox\n2006年9月26日更新\n上面的config.trim_on_minimized代表在firefox最小化时自动释放内存。\n以下转自Techer's Blog\n修改browser.sessionhistory.max_total_viewers修改值为5或更小，这个值页面快进/快退功能中保存的页面总数，默认是-1(无限)\n2006年11月18日更新\nFirefox超速绝密方法(转自firefox.hk)：\n第一步，在URL栏输入“about:config”， 将打开一个参数的命令行清单，这就是我们以下进行编辑的第一步\n第二步，在下面的清单中找到“network.http.pipelining” 然后双击network.http.pipelining将其值改成“true” 下面我们要做的就是找到“network.http.pipelining.maxrequests”并把它的值改的高一些，如果你改成30，意思就是要求它每次发送30个请求。\n第三步就是在Firefox任何一个地方点右键，选择“新建”—“整数”在弹出的对话框中输入“nglayout.initialpaint.delay”并将其值改为“0”，表示当浏览器收到网站回复的信息后能够快速的反应。\n完全关闭FireFox后退出，再重新打开FireFox，你将会有不一样的速度体验，会有2—3倍的速度提升，有兴趣的朋友可以实验一下。\n","date":"2006-04-21","description":"","lastmod":"2006-04-21T15:22:54Z","slug":"firefox-memory","tags":["firefox"],"title":"搞定Firefox内存占用问题以及提速","url":"https://blog.zengrong.net/post/firefox-memory/"},{"categories":["web"],"content":"关于google提供的所有服务以及如何使用Google的卡片，adelaide整理。\n卡片1\nGoogle提供的所有服务的URL、Google域名和IP地址 卡片2\nGoogle提供的大型服务以及如何使用Google PDF格式卡片下载 中文版 ","date":"2006-04-21","description":"","lastmod":"2006-04-21T14:36:29Z","slug":"google-cheat-sheets","tags":[],"title":"Google Cheat Sheets","url":"https://blog.zengrong.net/post/google-cheat-sheets/"},{"categories":["impressions"],"content":"由于我这一个月都不停加班，没有一天休息，所以看到这组漫画感受特别强烈。就转过来了\n:???:\n","date":"2006-04-20","description":"","lastmod":"2006-04-20T14:03:18Z","slug":"office-life","tags":[],"title":"【转】上班族的真实写照（组图）","url":"https://blog.zengrong.net/post/office-life/"},{"categories":["news"],"content":"IE7默认会隐藏菜单栏。这样界面会更加简洁，记住几个快捷键会让操作更简便了。\n要显示菜单栏，只需要在IE7界面右侧的按钮上单击右键，选择“classic Menu”即可。\nAlt+左箭头：上一页 Alt+右箭头：下一页 Ctrl+B：开启整理收藏夹 Ctrl+D：加到我的收藏夹 Ctrl+I：显示或隐藏收藏夹 Ctrl+J：显示或隐藏RSS聚合 Ctrl+H：开启历史纪录文件夹 Ctrl+E：切换到搜索框 Ctrl+F：打开页面搜索 Ctrl+N：打开新窗口 Ctrl+T：打开新标签 Ctrl+Tab：在标签之间切换 Ctrl+L：用来输入网址 Ctrl+O：也可用来输入网址 Ctrl+R：刷新，等同于按F5 Ctrl+W：关闭当前的标签或窗口（如果窗口只有一个标签，即关闭窗口） Alt+Enter：搜索搜索框中的关键字，将结果显示在新标签中 单击链接时按住Ctrl键：在新标签中打开页面 用中键单击链接：在新标签中打开页面 用中键单击页面标签：关闭单击的标签 比较郁闷的是，WordPress中的快速链接快捷键是Alt+A，居然和IE7的Favorites Center快捷键冲突！\n","date":"2006-04-12","description":"","lastmod":"2006-04-12T12:57:39Z","slug":"ie7","tags":[],"title":"IE7快捷键","url":"https://blog.zengrong.net/post/ie7/"},{"categories":["web"],"content":"1．打开 DirectX 的 D3D 硬件加速：\n看桌面属性，设置 -\u0026gt; 高级 -\u0026gt; 疑难问答 -\u0026gt; 硬件加速 -\u0026gt; 完全。或开始-à运行键入 dxdiag—\u0026gt;回车，打开Display选项卡，可看到 3 项全部启用了。最后，利用Windows updates 在Windows Server 2003中安装DirectX 9.0a。\n2. 启用声卡：\n系统安装后，声卡是禁止状态，所以要在 控制面板 -\u0026gt; 声音 -\u0026gt; 启用，重启之后再设置它在任务栏显示。现在我们还要启用音频加速。在开始/运行键入Services.msc然后按回车 ，会出现Services 窗口，找到Windows Audio服务，双击打开，把启动类型设置为Automatic，点击Apply，然后点击Start启动该服务。最后我们还要使用DirectX诊断工具，在运 行中输入dxdiag并回车，打开Sound选项卡，把Hardware Sound Acceleration Level的滑块拖动到Full。\n3. 如何启用 ASP 支持：\nWindows Server 2003 默认安装，是不安装 IIS 6 的，需要另外安装。安装完 IIS 6，还需要单独开启对于 ASP 的支持。方法是：\n控制面板 -\u0026gt; 管理工具 -\u0026gt; Web服务扩展 -\u0026gt; Active Server Pages -\u0026gt; 允许。关于安装php服务的方法与之相似，但要注意设置。\n4. 如何启用 XP 的主题：\n首先我们需要回到服务设置对话框启用Themes服务，在运行中输入Services.msc并按回车，找到并双击Themes这个服务，设置启动类型为Automatic，点击Apply后点Start打开这个 服务。\n5. 禁止关机时出现的关机理由选择项：\n开始 -\u0026gt; 运行 -\u0026gt; gpedit.msc -\u0026gt; Computer configuration -\u0026gt; Administrative Templates -\u0026gt;System-\u0026gt;Display shutdown event tracker -\u0026gt; 设置为 Disable。\n如果是中文版，则：gpedit.msc，计算机配置 -\u0026gt; 管理模板 -\u0026gt; 系统 -\u0026gt; 显示关机事件跟踪 -\u0026gt; 禁用。\n6. 如何启用摄像机，摄像头或者扫描仪等设备：\n在运行中输入Services.msc并回车，找到并双击Windows Image Acquisition (WIA) 服务，设置启动类型为Automatic点击Apply后点击Start然后点击OK。\n7. 在控制面板里显示全部组件：\n把 Windows\\inf 目录中的 sysoc.inf 文件里的 \u0026quot;hide\u0026quot; 替换掉。\n8．禁用配置服务器向导\n由于不需要服务器设置功能，首先我们先禁止“配置你的服务器”（Manage Your Server）向导的出现，你可以在控制面板（Control Panel） -\u0026gt; 管理员工具（Administrative Tools ）-\u0026gt; 管理你的服务器（Manage Your Server）运行它，然后在窗口的左下角复选“登录时不要显示该页”（Don't display this page at logon）。\n9．禁用Internet Explorer Enhanced Security\n作为新windows组件出现的IE安全插件－－Internet Explorer Enhanced Security默认把你IE安全设置为最高。这样你将在访问站点弹出询问框并对你浏览网页及文件下载做出阻 止的行为。我们其实不一定需要这个组件。我们首先禁止询问框的出现,在弹出的对话框中复选”以后不要显示这个信息“ （In the future, do not show this message）然后， 我们可以在IE工具选项中自定义设置IE的安全级别。在”安全“（Security）选项卡上拉动滚动条把Internet区域安全设置为”中“（Medium），这个级别将适合大多数人，要是 你有特别要求，这个步骤将不适合你。通过对IE安全的设置，你现在安装可以上Sun's Java VM! 当然，你甚至可以在控制面板－－添加程序－－添加或删除Windows组件中卸载 Internet Explorer Enhanced Security\n10．USB设备作为Removable Storage服务，默认是没有启动的，得手动打开才可以使用USB硬盘等。\n11．禁用开机 CTRL+ALT+DEL和实现自动登陆\n管理工具 -\u0026gt; Local Security Settings（本地安全策略） -\u0026gt; 本地策略 -\u0026gt; 安全选项 -\u0026gt; interactive logon: Do not require CTRL+ALT+DEL，启用之。\n12．处理IE的最大化设置：\n1、运行注册表编辑器，删除HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Main分支下的Window_Placement键值，它设置IE窗口最后出现在桌面的位置；再删除 HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Desktop\\Old WorkAreas分支下OldWorkAreaRects子键；还有HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Document Windows分支，它记录着是否最大化信息。\n2、右键点击任务栏-\u0026gt;平铺窗口。\n13. 安装“视频压缩”组件\n在2003下动画没有图像，只有声音，提示“视频无法使用，找不到vids:cvid解压缩程序”。这都是Windows server 2003中没有安装“视频压缩”组件造成的。\n第一步：找一张Windows XP的安装光盘，在其中的i386目录下找到ir32_32.dl_和iccvid.dl_两个文件，用WinRAR将它们解压缩到C:\\Windows\\System32下。解压缩后的两个文件 分别为：ir32_32.dll和iccvid.dll。\n第二步：打开“注册表编辑器”，找到[HKEY_LOCAL_ MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32]，在右侧窗格中新建名为vidc.cvid的字符串值，将 其数值设置为：iccvid.dll；再新建一个名为vidc.iv31的字符串值，将其值设置为ir32_32.dll；接着新建名为vidc.iv32的字符串值，其值为ir32_32.dll。\n14. 如何移除并重新安裝NetMeeting？\n1、从 %SystemRoot%\\Inf 目錄中複製Msnetmtg.inf 到您的桌面\n2、依序按一下 [開始]、[執行]，在 [開啟] 方塊中輸入： :\n%SystemRoot%\\System32\\rundll32.exe setupapi,InstallHinfSection NetMtg.Remove 132 msnetmtg.inf\nNOTE: 此依命令行字元有大小寫的分別，請務必確實輸入。\n3、在步驟1.中複製到桌面的Msnetmtg.inf 檔案，滑鼠按右鍵, 按下安裝. 這時您可能需要放入Windows Server 2003安裝光碟至光碟機.\n4、安裝完成後請重新開機。\n15. 如何修复系统文件？\n要中使用“系统文件检查器”，先要单击“开始→所有程序→附件→命令提示符”，然后在“命令提示符”窗口的光标提示符后键入“Sfc”并按下回车键，“系统文件检查”程序 会给出参数的中文提示。\n通过Windows安装盘来修复被损坏了的文件,恢复的具体过程如下：\n在Windows的安装盘中搜索被破坏的文件，需要注意的是，文件名的最后一个字符用底线“_”代替，例如：如果要搜索“Notepad.exe”则需要用“Notepad.ex_”来进行搜索。搜 索到了之后，打开命令行模式(在“运行”中输入“cmd”)，然后输入：“EXPAND 源文件的完整路径 目标文件的完整路径”。例如：EXPAND D:\\SETUP\\NOTEPAD.EX_ C:\\Windows\\NOTEPAD.EXE。有一点需要注意的是，如果路径中有空格的话，那么需要把路径用双引号(英文引号)包括起来。\n找到当然是最好的，但有时我们在Windows盘中搜索的时候找不到我们需要的文件。产生这种情况的一个原因是要找的文件是在“CAB”文件中。由于Windows把“CAB”当作一个文 件夹，所以对于Windows系统来说，只需要把“CAB”文件右拖然后复制到相应目录即可。\n如果使用的是其他Windows平台，搜索到包含目标文件名的“CAB”文件。然后打开命令行模式，输入：“EXTRACT /L 目标位置 CAB文件的完整路径”，例如：EXTRACT /L C:\\Windows D:\\I386\\Driver.cab Notepad.exe。同前面一样，如同路径中有空格的话，则需要用双引号把路径包括起来。\n16. 如何实现ADSL共享？\n1、服务器需要双网卡。\n2、新建ADSL拨号连接。\n3、点击2003安装光盘\\SUPPORT\\TOOLS\\NETSETUP.EXE执行安装，重新启动机器即可。\n17. 出现AVI文件无法删除的情况如何处理？\n可能是Explorer的预览造成的。如果不需要预览影音文件，把shmedia.dll文件解除注册就可以解决这个问题。解除方法：regsvr32 /u shmedia.dll\n18. 如何设置代理？\n1、打开IE工具栏的“Internet选项”。\n2、在“连接”页面选择“拨号设置”，然后单击“设置”。\n3、在“设置”页面中，选中“使用代理服务器”然后在“地址”栏中填上代理服务器地址和“端口”，单击“确定”。\n4、可使用的代理地址：\n19. 打SP1补丁如何选择不备份？\n用WinRAR把下载的SP解开，进入cmd模式，进入i386\\update目录，然后运行update /n，就不会备份啦。另：运行update /?就能看到详细的定义安装参数。\n20. 清除Thumbs.db文件的方法？\n在“文件夹选项”里选中“不缓存缩略图”，这样可以禁止生成新的Thumbs.db文件，至于旧的Thumbs.db文件，直接用系统的搜索功能，找出硬盘上的所有Thumbs.db文件，全部删 除即可。\n21. Win2003如何实现自动登录？\n开始-\u0026gt;运行-\u0026gt;输入“rundll32 netplwiz.dll,UsersRunDll”命令打开帐户窗口，先选中某帐户，再去除“要使用本机，用户必须输入用户名密码”复选框中的勾号，输入该帐户的密码即可。\n22. 如何取消Winnt/2000/xp/2003每次开机的默认共享？\n对于服务器而言 在 HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters 下，增加一个名为“AutoShareServer”的双字节值，设为“0”。然后去掉共享，重新启动。\n对于工作站而言 在 HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters 下，增加一个名为“AutoShareWks”的双字节值，设为“0”。然后去掉共享，重新启动。\n23. 事件查看器里报DCOM出错，如何解决？\n根据提示信息，实际上是说NETWORK SERVICE没权限激活CLSID为{BA126AD1-2166-11D1-B1D0-00805FC1270E}的应用程序。可以通过使用组件服务管理工具修改此安全权限。\n1、如果按上面的提示去使用组件服务管理工具找CLSID为{BA126AD1-2166-11D1-B1D0-00805FC1270E}的应用程序是找不到的。\n2、需要先运行regedit.exe在注册表中查找出{BA126AD1-2166-11D1-B1D0-00805FC1270E}对应的AppID值{27AF75ED-20D9-11D1-B1CE-00805FC1270E}\n3、然后再打开组件服务，查看方式为详细信息，找到DCOM 配置里的netman，选中按鼠标右建选属性。\n4、在netman属性里的安全 -\u0026gt; “启动和激活权限” -\u0026gt; 自定义编辑，在启动权限里加入NETWORK SERVICE用户，允许本地启动和本地激活，确定后就不会再报这个DCOM错了。\n","date":"2006-04-09","description":"","lastmod":"2006-04-09T02:55:26Z","slug":"win2003","tags":[],"title":"【转】Win2003下常见问题整理","url":"https://blog.zengrong.net/post/win2003/"},{"categories":["impressions"],"content":"在武汉热线上看到这段视频后，心中不由的一震，自己仿佛回到了那段难忘的时光\n:neutral:\n时间，真的就这样一去不回了 我经过的那些事，都已经无法改变 这世界变化太快，快得让我不得不急急向前赶路 不过，我至少可以停下来一会儿，好好追忆一下过去的时光 献给75－89年生人\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n这让我想起原来听过的“生于70年代”，也是深深触动过我，一并放在这里把\n您的浏览器不支持音频！！！ ","date":"2006-03-29","description":"","lastmod":"2006-03-29T06:45:30Z","slug":"born75-89","tags":[],"title":"献给75－89年生人","url":"https://blog.zengrong.net/post/born75-89/"},{"categories":["use"],"content":"（本文基于Microsoft Word 2003）\n“段落”（工具-\u0026gt;段落）是排版中常用的命令，这个对话框有三个选项卡，其中“换行和分页”这个选项卡我原来是不用的。今天就来研究一下。如图 1所示。\n图 1 换行和分页选项卡\n孤行控制 首先来看看“孤行控制”。孤行控制防止在页首或者页末遗留有某段的末行或者首行。拆开来说，就是在一页的页首有上一个段落的最后一行；在一页的页末有下一个段落的第一行。图 2中有黄色底纹的部分就是“一个孤行”。\n将鼠标定位在有孤行的段落中，按Alt+O+P打开段落对话框，切换到“换行和分页”选项卡，勾选“孤行控制”，即可将此行调整到下一页中。如图 3所示。\n有趣的是，对于页首的孤行，Word并不会将其移动到上一页中，而是将上一页中调整一行到页首，使页首变成2行。而对于页末的孤行，则是将其调整到下一页中。\n图 2 孤行调整之前\n图 3 孤行调整之后\n与下段同页 这个功能是将本页中的最后一个段落归并到下一页。需要注意的是，可以归并到下一页中的段落必须在本页结束，而且此段落之下不能再有其他段落。如图 4所示拥有黄色底纹部分的段落即符合以上条件。图 5展示了使用命令之后的效果。\n图 4 与下段同页之前\n图 5 与下段同页之后\n段中不分页 如果不希望某个段落分页显示，则可以使用此功能。如图 6所示。段落会整体移动到下一页。如图 7所示。\n图 6 段中不分页之前\n图 7 段中不分页之后\n段前分页 强制在一个段落前分页，不论本页中此段落下还有没有其他段落。如针对图 8所示的黄色底纹段落进行段前分页操作，执行后效果如图 9所示。虽然此段落后还有一个段落，但仍进行了分页。\n图 8 段前分页之前\n图 9 段前分页之后\n取消行号 计算机类书籍和文章往往有大量代码需要添加行号，但除了代码部分的正文内容又不需要行号，这就可以使用“取消行号”这个功能。图 10显示的是未加入行号的页面。首先要给这个页面加入行号。\n图 10 未加入行号\n使用“文件－页面设置－版式－行号”命令，勾选“添加行号”，即可以将行号添加到页面中。如图 11所示。添加了行号的效果如图 12所示。\n图 11 添加行号\n图 12 添加了行号的效果\n选中非代码部分，使用“取消行号”功能，得到如图 13效果。\n图 13 取消行号后的效果\n取消断字 这个功能对中文没有什么作用，因此不讲解。\n下载本文：advanced-paragraph.swf\n","date":"2006-03-28","description":"","lastmod":"2006-03-28T00:56:48Z","slug":"advanced-paragraph","tags":["office","word"],"title":"段落的高级控制-换行和分页","url":"https://blog.zengrong.net/post/advanced-paragraph/"},{"categories":["use"],"content":"在TotalCommand的控制台插件中使用MiniQQ，倒是别有一番风味呢！\n","date":"2006-03-20","description":"","lastmod":"2006-03-20T05:42:07Z","slug":"totalcommand-minqq","tags":[],"title":"在TotalComman中使用MiniQQ","url":"https://blog.zengrong.net/post/totalcommand-minqq/"},{"categories":["use"],"content":"碰到一个超Cool的东东，迷你QQ（MiniQQ）\n本站下载：MiniQQ2.1.zip\n70KB的QQ，内存占用仅不超过1MB，包含QQ的基本功能。最重要的是，它很Cool！发信息要敲send，获取好友列表要list，仿佛回到了DOS时代……\n下面是两张截图：\n","date":"2006-03-18","description":"","lastmod":"2006-03-18T14:23:44Z","slug":"miniqq","tags":[],"title":"迷你QQ（MiniQQ） 重回DOS时代","url":"https://blog.zengrong.net/post/miniqq/"},{"categories":["use"],"content":"把手机换到Dopod 575之后，发现SmartPhone手机和Symbian连接蓝牙的方式不太一样，刚开始碰到许多问题。但是因为常用的计算机有三台，在一遍又一遍的使用过程中总结出了一点点经验，就分享一下把。\n第一步，安装蓝牙驱动 我用的是Widcomm蓝牙驱动中文版v5.0.1.801。如果你的系统是Winxp SP2，并且不想使用系统自带的驱动，请参阅彻底删除WinXP SP2自带的蓝牙驱动一文。蓝牙安装过程网上有许多文章介绍，在此略过。\n第二步，安装Microsoft ActiveSync 4.1 先下载Microsoft ActiveSync 4.2，安装后一定要重新启动计算机。安装过程略。\n第三步，设置电脑端的蓝牙设备。 打开蓝牙设备的高级设置，确认“可访问性”选项卡选中了“允许其他Bluttooth设备发现该计算机”。如图 1、图 2所示。\n图1\n图2\n切换到Bluetooth配置选项卡，确认其中有一个“Bluetooth串行端口”服务，并将COM端口设置成“COM6”（因为Dopod 575只能添加COM6和COM7两个端口，其他的机型我没有试过）。如果要保留原来的端口，也可以使用“添加串行服务”添加一个COM6端口。如图 3、图 4所示。\n图3\n图4\n第四步，ActiveSync设置 在任务栏中的ActiveSync图标上右击并选择“连接设置”，如图 5所示。\n图5\n在弹出的对话框中设置“允许连接到以下其中一个端口”的值为“COM6”。如图 6所示。\n图6\n第五步，手机与电脑的配对 打开“移动Outlook”，选择“ActiveSync”，选择“设定蓝芽ActiveSync”。如图 7所示。\n图7\n按提示进行操作，将蓝牙设置为“可发现”，并选择设备命令。如图8所示。\n图8\n由于我和其他电脑同步过，因此手机中有一个设备。选择“新建”菜单命令开始搜索设备，搜索到设备（确认是电脑）后，选择这个设备。\n图9\n同步前需要先输入密钥进行配对这里可以随意输入，例如1234，然后单击完成。如图 10所示。\n图10\n此时电脑右下角蓝牙图标上会出现如图 11所示的提示。单击它，弹出如图 12所示的对话框，在其中输入图 10中设置的密钥单击确定。\n图11\n图12\n在手机上输入要显示的设备名称。一般保持默认即可。单击完成。配对成功。如图 13所示。\n图13\n第六步，手机端口的配置 回到蓝牙配置界面，选择“COM端口”命令，选择“新建传出端口”。如图 14所示。\n图14\n选择刚刚配对成功的“ZR”设备，然后选择“COM6”端口，建立一个COM端口。如图 15所示。\n图15\n回到ActiveSync界面，选择“通过蓝牙连接”命令，开始进行COM口连接。如图16所示。\n图16\n电脑屏幕出现“已请求Bluetooth授权”提示，单击确认授权。如图 17、图 18所示。\n图17\n图18\n连接成功后，电脑屏幕会给出提示，同时手机屏幕也会有相应提示。接着ActiveSync程序会自动启动，右下角的图标变成绿色，开始建立关系。如图 19、图 20、图 21所示。\n图19\n图20\n图21\n下载本文：使用ActiveSync通过蓝牙连接smartphone\n","date":"2006-03-15","description":"","lastmod":"2006-03-15T01:03:48Z","slug":"activesync-smartphone","tags":["smartphone","bluetooth"],"title":"使用ActiveSync通过蓝牙连接smartphone手机详解","url":"https://blog.zengrong.net/post/activesync-smartphone/"},{"categories":["technology"],"content":"今天整理硬盘，找到这个原来做的小工具，用于删除源代码中以 // 标记的注释。用法很简单，找一段有注释的代码粘贴到“源代码”TextArea中，单击“转换”按钮即可。这个版本并不支持 /*....*/ 标记的注释。\n要做试验，可以用下面这段代码：\n1 import mx.containers.Window; 2 import mx.managers.PopUpManager; 3 import mx.controls.Alert; 4 import mx.controls.Button; 5 import mx.events.EventDispatcher; 6 class view.child.PopWindow{ 7 private var pw; //Window 8 private var pa; //Alert 9 private var para:Object; 10 private var winType:String; 11 private var alert:Alert; 12 private var dispatchEvent:Function; 13 private var dispatchQueue:Function; 14 public var addEventListener:Function; 15 public var removeEventListener:Function; 16 public var chatRoom; //chatRoom在pop实例化的时候由Actor类初始化，是为了给弹出的Alert设定Parent值，以便接收弹出的Alert的按钮事件 17 private var parent; 18 public function PopWindow(){ 19 EventDispatcher.initialize(this); 20 //owner = this; 21 } 22 private function showSettings():Void { 23 //显示系统设置框 24 System.showSettings(0); 25 } 请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n源文件：\n1 文件 ","date":"2006-03-10","description":"","lastmod":"2006-03-10T13:43:50Z","slug":"delete-comment","tags":["actionscript","flash"],"title":"删除源代码中的注释","url":"https://blog.zengrong.net/post/delete-comment/"},{"categories":["others"],"content":"我就不用说了把，大家一听就知道是在说谁呢，外地网友可以先看看2005 CWT-4 全国武汉话四级考寺，通过了再听把。:cool:\n下载地址\n信了你的邪（I服了U）\n你又冒得（没有）实力 还要出来当歌星\n以为会闹几个眼子（闹几下子） 就带（在）那里装精（装聪明）\n台上闪光灯一打 你就发了病\n我说老大你要考虑到 别个（人家）的心情\n对到（面对）你的FANS 说话要讲良心\n带（在）那里咿咿啊啊咿咿啊啊裹个么鬼经（罗嗦啥）\n那些伢们（孩子们）都蛮驯 思想蛮单纯\n当个偶像你就要控制一言一行\n你说噢噢噢 老子偶你个大象\n这个年代到处都是呕吐的对象\n你不是那个事 就不要出来晃\n晃来晃克（去）晃进晃出晃个么鬼（什么）名堂\n做人不要太嚣张 说话莫（别）骂别个（人家）娘\n闹点绯闻上个头版开个裸会到处唱\n不过话一说转来（话说回来） 就是不要耍大牌\n以为有个靠山你就可以瞎瞎瞎拽\n上个国际封面 觉得名声兑了现\n你要变变变 但是千万不能jian（第四声）\n可以jianjianjian 莫（别）丢中国人的脸\n一个字 差 多个字 zha（第一声，恶心）\n如果你要红的话 就不要到处岔\n得了便宜卖了乖 还要找人打架\n我说的是哪个（谁）你心里有数撒\n信了你的邪 红得像个番茄\n信了你的邪 搞得人格分裂\n老子信了你的邪 可以坚持不懈\n唧唧歪歪都是口水还说那是音乐\n信了你的邪 红得像个番茄\n信了你的邪 搞得人格分裂\n老子信了你的邪 瘦得像个撇撇（形容很瘦）\n毁了自己没了自我还你大爷\n人正不怕影子歪 走路就不会搭达（摔跤）\n群众的眼睛个个雪亮不会瞎撒\n你装你是麦霸 会丢两句普通话\n混点西伯利亚语言就带(在）那里牛X\n如果你听不进克(去) 寻找自己的快乐\n我就丢了一句这： 你去死吧\n一点本事都冒的（没有） 走路扭得像个蛇\n捧哈子（一下）你就 飞得像个火影忍者\n登上领奖台说说你的那些么事（什么）V\n谢谢Channel V CCTV MTV\n谢谢你的经济人 还有你的歌迷\n其实努力的人和实力派都带底哈（在下面）哈气\n究竟是不是你的 我们都蛮怀疑\n不是你的东西 你就回克（回去）休息\n别的你不行 你还蛮会打太极\n看到不费什么力 实际上汗直滴（直冒汗）\n领了奖你就可以准备新专辑\n随几（不管多么）烂的歌也可以拿个第一\n排行榜你到底排的什么东西\n现在宣传的效应 全是唱片公司定\n信了你的邪 红得像个番茄\n信了你的邪 搞得人格分裂\n老子信了你的邪 可以坚持不懈\n唧唧歪歪都是口水还说那是音乐\n信了你的邪 红得像个番茄\n信了你的邪 搞得人格分裂\n老子信了你的邪 瘦得像个撇撇\n毁了自己没了自我还你大爷\n实际冒的么人（没什么人）买 也冒的么人（没什么人）听\n冒的么人（没什么人）欣赏一个花痴念经\n如果大哥觉得我 伤了你的自尊心\n让我来 找找你有什么值得同情\n其实你蛮风光 不用暗自神伤\n说话莫（别）装善良 也不要娘娘腔\n头发可以剃光 MONEY可以赔光\n只是大姐不要动不动就全部脱光\n如果心灵受了伤 绯闻闹得心里慌\n多抽点时间出来制造积极的印象\n要找努力的榜样 学哈（学一下）别个（人家）刘德华\n走到哪里瞎杀都是一片姑娘伢\n最后我想说的是 我么事（什么）都不是\n说来说克（去）总是那几个破裸事\n我们心中有个刺 疼得不呛粮食\n这个社会太多虚假太少真实\n站到（站着）说话不腰疼 这种想法蛮自私\n说不定我最后也会变成其中的一分子\n也许我会麻木 会去妥协融入\n但是永远逃不脱 心中那根刺\n信了你的邪 红得像个番茄\n信了你的邪 搞得人格分裂\n老子信了你的邪 可以坚持不懈\n唧唧歪歪都是口水还说那是音乐\n信了你的邪红得像个番茄\n信了你的邪 搞得人格分裂\n老子信了你的邪 瘦得像个撇撇\n毁了自己没了自我还你大爷\n","date":"2006-03-10","description":"","lastmod":"2006-03-10T03:35:23Z","slug":"ifuleu","tags":[],"title":"信鸟你滴邪","url":"https://blog.zengrong.net/post/ifuleu/"},{"categories":["design"],"content":"\n","date":"2006-03-09","description":"","lastmod":"2006-03-09T08:33:59Z","slug":"reticule","tags":[],"title":"无限创意的手提袋","url":"https://blog.zengrong.net/post/reticule/"},{"categories":["technology"],"content":"关于修改Blog中的swf嵌入方式碰到问题，CoolCode插件的作者andot已经做了回复，如下：\nto zrong：\nCoolPlayer 本来就是按照微软所说的方式插入的各种媒体，所以所有 CoolPlayer 所支持的媒体在 IE 上都没有问题。完全没有必要使用 Kimili Flash Embed 插件，它最多只能避免插入 Flash 不需要激活，而 CoolPlayer 可以插入任何媒体都不需要激活。\n留言 由 andot — 2006-03-08 @ 09:30:58\n151 楼嗯，我已经查看过页面HTML源码了，coolplayer确实是使用JavaScript插入的。谢谢andot ，不过还是想问问：\n这个冲突，实际上应该是FlashObject和coolplayer中JavaScript的冲突把，是不是因为使用了同样的处理方式，所以造成了这样的冲突？\n留言 由 zrong — 2006-03-08 @ 09:37:58\nCoolCode 的脚本和 FlashObject 不存在冲突，冲突的可能是过滤级别的设置（也就是过滤器的顺序）。CoolCode 里是第一步的级别是0，第二步的级别是5000，不知道 FlashObject 对第一步替换后的内容作了些什么。\n留言 由 andot — 2006-03-08 @ 10:09:00\n通过查看CoolPlayer生成的HTML源码，发现CoolPlayer确实采用的是Microsoft推荐的方式，安装了KB912945补丁后，也不会出现那个讨厌的“单击以激活并使用此控件”提示:lol:，（如下图所示）\n当然，CoolPlayer不仅解决了网页中的Flash动画问题，也应该同样解决了下面这些Active的问题：\nApple QuickTime Player Microsoft Windows Media Player Real Networks RealPlayer Microsoft公布的受影响的ActiveX还有Adobe Reader和Sun Java Virtual Machine，但它们不在CoolPlayer“管辖”的范畴之内，CoolPlayer也无能为力了。:?\n根据andot在留言中的提示进行了测试，CoolPlayer同样支持Adobe Reader。\nCoolPlayer生成的代码：\n1\u0026lt;span class=\u0026#34;coolplayer_wrapper\u0026#34;\u0026gt;\u0026lt;span id=\u0026#34;coolplayer_container_54187641\u0026#34;\u0026gt;\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt;\u0026lt;!-- load_coolplayer(\u0026#39;\u0026lt;a href=\u0026#34;/uploads/2006/03/beijing2008timer.swf\\\u0026#34;\u0026gt;北京2008奥运会开幕倒计时\u0026lt;/a\u0026gt;\u0026#39;, \u0026#39;54187641\u0026#39;, \u0026#39;300\u0026#39;, \u0026#39;100\u0026#39;, \u0026#39;false\u0026#39;, \u0026#39;false\u0026#39;, \u0026#39;GBK\u0026#39;); //--\u0026gt;\u0026lt;/script\u0026gt;\u0026lt;/span\u0026gt;\u0026lt;span class=\u0026#34;coolplayer_info\u0026#34; id=\u0026#34;coolplayer_info_54187641\u0026#34; style=\u0026#34;width: 298px\u0026#34; ondblclick=\u0026#34;coolplayer_input(this, \u0026#39;300\u0026#39;, \u0026#39;100\u0026#39;, \u0026#39;false\u0026#39;, \u0026#39;false\u0026#39;, \u0026#39;GBK\u0026#39;);\u0026#34; title=\u0026#34;Double click to input your media URL, and press enter to play it.\u0026#34;\u0026gt;Loading...\u0026lt;/span\u0026gt;\u0026lt;/span\u0026gt; 倒是Maxthon确实受了影响，CPU占用达到100％。\nBTW：\n如果只是为了解决IE的KB912945补丁对Flash的限制，可以采用FlashObject。\n","date":"2006-03-08","description":"","lastmod":"2006-03-08T03:43:02Z","slug":"kb912945-coolplayer","tags":["flashplayer","wordpress","plugin"],"title":"CoolPlayer可以解决Microsoft的KB912945补丁对ActiveX的限制","url":"https://blog.zengrong.net/post/kb912945-coolplayer/"},{"categories":["web"],"content":"Microsoft在2月28日发布的IE6补丁 (KB912945) 会影响嵌入式内容ActiveX，具体表现为在使用交互功能时需要“激活”一次才可以观看，详细的介绍可以看 Danger的文章。\n于是下载并安装了 Kimili Flash Embed 插件。Kimili Flash Embed是一个用于在WordPress中插入Flash动画的插件，使用方式为：\n1\u0026lt;kml_flashembed movie=\u0026#34;filename.swf\u0026#34; height=\u0026#34;150\u0026#34; width=\u0026#34;300\u0026#34; /\u0026gt; 或者也可以使用方括号代替尖括号。它使用了 FlashObject 内核。\n为什么要使用 FlashObject 插入Flash动画呢？因为它可以避免进行上述激活操作。Danger也做了介绍 。\n但问题随之而来，原来安装的 coolcode 插件居然和 Kimili Flash Embed 插件冲突？在后台激活 Kimili Flash Embed 插件后，原来使用了 coolcode 的代码就变成了 ++CoolCode_BLOCK_1++、++CoolCode_BLOCK_2++ 等等字样（如下图所示）。\n。\nPS：\n今天还收到了 Maxthon 的提示(如下图所示)，告知KB912945补丁会引起Maxthon占用CPU100%云云，看来这个Microsoft的这个小补丁还真让不少人伤脑筋了。\n","date":"2006-03-07","description":"","lastmod":"2006-03-07T16:59:55Z","slug":"kb912945","tags":["swfobject","wordpress"],"title":"修改Blog中的swf嵌入方式碰到问题","url":"https://blog.zengrong.net/post/kb912945/"},{"categories":[],"content":"WordPress大全\n本文原来的位置在这里，后来内容越来越多，就作为一个页面把。\n最后更新：2007-12-9\n官方\nwordpress.org 官方文档\nConditional_Tags\nTheme_Development\nTemplates 插件\n八个无价的WordPress插件 dev.wp-plugins.org wp-plugins.net coolcode.cn 大量的原创WordPress插件和文章 http://www.lesterchan.net/ 大量wordPress插件 基于WordPress的Flash Blog，开源 桑林志的中文WordPress资源 探花@Tinn Walk 之 我使用的wordpress 插件 WoerdPress插件及应用列表 Blogging Pro China Quicktags Changing the WordPress Quicktag Buttons 如何使用wp-grins插件 代码高亮插件 之一 之二 万能播放器插件 之一 之二 之三 添加Flash内容 之一 之二 在Windows系统如何使用WP-cache 启用WP-Cache后，出现blank page情况的解决方案 评论使用所见即所得编辑器 Did You Pass Math? 要进行评论，必须先做对一道简单的数学题 Simple Tags 个人认为是最好的TAG插件 虽然WordPress 2.3已经官方支持Tag，但是功能很不完善。可以用这个插件代替它。 Peter’s Math Anti-Spam Image for WordPress 验证码插件 Click Counter Plugin For WordPress 记录链接的点击次数 Some Chinese Please “Some Chinese Please”是为用中文写作的wordpress blogger准备的拦截spam的plugin。 Spam Comment Killer Math Comment Spam Protection Plugin 修改\nWordPress 2.0.2上传图片标题乱码解决 如何在WordPress后台中加入表情符号 再增加一些表情符号 给wordpress增加quicktags按钮的方法 如何使用SWFObject(FlashObject)插入swf动画 为留言增加序号 教学\nThe Tamba2 WordPress Guides 大量的教学文章，提供codex站点文章打包下载 升级到WordPress2.1 中文\nWordPress最好的中文文档 WordPress2.1中文包 WordPress2.1发布 WordPress 2.1简体中文语言包 其他\n通用的语法高亮程序GeSHi ","date":"2006-03-07","description":"","lastmod":"2006-03-07T15:41:31Z","slug":"wordpressfavorite","tags":[],"title":"WordPress大全","url":"https://blog.zengrong.net/wordpressfavorite/"},{"categories":["technology"],"content":"受好友冷雨之托制作一个倒计时程序，一时心血来潮干脆就做成了北京2008奥运倒计时了。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n脚本很简单，就下面几句而已:\n1var beijing2008_date:Date = new Date(2008, 7, 8, 8); 2refreshDate(beijing2008_date); 3function refreshDate(old_date:Date):Void { 4 var spareTime = Math.floor((old_date-(new Date()))/1000); 5 var newDay = Math.floor(spareTime/3600/24); 6 var newHour = Math.floor(spareTime/3600%24); 7 var newMinute = Math.floor((spareTime%3600)/60); 8 var newSecond = (spareTime%3600)%60; 9 time = newDay+\u0026#34;天\u0026#34;+newHour+\u0026#34;小时\u0026#34;+newMinute+\u0026#34;分钟\u0026#34;+newSecond+\u0026#34;秒\u0026#34;; 10} 11setInterval(refreshDate, 1000, beijing2008_date); 1 文件 ","date":"2006-03-07","description":"","lastmod":"2006-03-07T07:49:43Z","slug":"beijing2008-timer","tags":["flash"],"title":"北京2008奥运会倒计时程序","url":"https://blog.zengrong.net/post/beijing2008-timer/"},{"categories":["web"],"content":"07-02-25加入：\nWP-Grins插件已经更新，目前可以不需要修改代码就实现表情符号的添加了，详情请见：在WordPress2.1后台和评论页面中加入表情符号\n06-08-24加入：\n如果希望加入更多的表情，可以看这里：在WordPress后台中增加自定义表情\n这应该是比较老的话题了，请参阅Smilies in WP (wp中的表情)\nWordPress自带了二十多个表情符号：\n表情 写法一 写法二 写法三 表情 写法一 写法二 写法三 :D ' :D' ' :-D' ':grin:' :) ' :)' ' :-)' ':smile:' :( ' :(' ' :-(' ':sad:' :o ' :o' ' :-o' ':eek:' 8O ' 8O' ' 8-O' ':shock:' :? ' :?' ' :-?' ' :???:' 8) ' 8)' ' 8-)' ':cool:' :x ' :x' ' :-x' ':mad:' :P ' :P' ' :-P' ':razz:' ;) ' ;)' ' ;-)' ':wink:' :| ' :|' ' :-|' ':neutral:' 表情 写法 表情 写法 表情 写法 表情 写法 :lol: ':lol:' :oops: ':oops:' :cry: ':cry:' :mrgreen: ':mrgreen:' :arrow: ':arrow:' :evil: ':evil:' :twisted: ':twisted:' :roll: ':roll:' :!: ':!:' :?: ':?:' :idea: ':idea:' 这些符号是使用代码插入的，要记住这些代码可不太容易，因此如果将这些表情符号嵌入到后台就方便了。效果如图所示：\n方法也并不麻烦，首先下载WP-Grins这个插件，按正常方式安装。由于此插件并不是针对2.0开发，因此要先修改一下。找到wp-grins.php中的下面这句（大约在34行）：\n1$grins .= \u0026#39;\u0026lt;img src=\u0026#34;\u0026#39;.get_settings(\u0026#39;siteurl\u0026#39;).\u0026#39;/wp-images/smilies/\u0026#39;.$grin.\u0026#39;\u0026#34; onclick=\u0026#34;grin(\\\u0026#39;\u0026#39;.$tag.\u0026#39;\\\u0026#39;);\u0026#34; alt=\u0026#34;\u0026#39;.$tag.\u0026#39;\u0026#34; /\u0026gt; \u0026#39;; 将其中的 /wp-images/smilies/ 修改为 /wp-includes/images/smilies/ 。\n安装后在插件管理器中激活它，然后编辑“/wp-admin/admin-functions.php”文件，搜索 edToolbar(); ，大约在1079行，找到下面这句：\n1if (!strstr($_SERVER[\u0026#39;HTTP_USER_AGENT\u0026#39;], \u0026#39;Safari\u0026#39;)) 2echo \u0026#39; \u0026lt;p id=\u0026#34;quicktags\u0026#34;\u0026gt; \u0026lt;script src=\u0026#34;../wp-includes/js/quicktags.js\u0026#34; type=\u0026#34;text/javascript\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 3\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt;if ( typeof tinyMCE == \u0026#34;undefined\u0026#34; || tinyMCE.configs.length \u0026lt; 1 ) edToolbar();\u0026lt;/script\u0026gt;\u0026#39;; 再上面这句之下加入：\n1if (function_exists(\u0026#34;wp_grins\u0026#34;)) { echo \u0026#34;\u0026#34;; wp_grins(); }\t//zrong added 修改完毕上传，打开后台撰写文章即可看到，可爱的表情已经加入到编辑器中了。\n如果要将这些表情也加入到评论页面中，可以在主模版的comments.php中搜索如下语句（可能不完全相同）：\n1\u0026lt;textarea class=\u0026#34;textform\u0026#34; name=\u0026#34;comment\u0026#34; id=\u0026#34;comment\u0026#34; cols=\u0026#34;100\u0026#34; rows=\u0026#34;10\u0026#34; tabindex=\u0026#34;4\u0026#34;\u0026gt;\u0026lt;/textarea\u0026gt; ","date":"2006-03-05","description":"","lastmod":"2006-03-05T15:07:09Z","slug":"smilies-in-wp","tags":["wordpress","plugin","emotion"],"title":"如何在WordPress后台中加入表情符号","url":"https://blog.zengrong.net/post/smilies-in-wp/"},{"categories":["impressions"],"content":"原来曾经写过请不要相信这个网站（以及类似的网站），看来现在要写续集了。\n今天在QQ群里收到一条消息：\n免费主页空间1G\nhttp://web.thec.cn/sysweb/RegTrans.aspx?CodeProvider=hbnu\n刚看到就觉得这个网站有嫌疑，于是去注册了一下，看看现在骗子的手段是否高明了些。\n首先不理会原来的连接（后面跟一个用户ID，肯定是积分用的），直接来到首页，看看开出的条件还是很诱人的（图1），直接注册，看到注册协议（图2）称自己是国内最大的免费空间站 :o 吹把……反正不用上税\n进入后台后，立刻发现了问题，（如图3）这个网站，居然要求网友提供身份证号码以及复印件以及照片！对于一个连备案信息都没有，联系地址都无法找到的网站，凭什么要求网友提供自己的隐私信息？万一这些信息被利用，责任由谁承担？\n在解释其“采用实名注册的原因”时，该网站居然“承诺”：“凡因本站管理疏忽使用户身份证资料外泄而造成的损失，本站全额赔偿”。（图4）**你拿什么赔偿？你怎么赔偿？**我相信这个“管理疏忽”是一定会有的。\n另外，这个免费空间默认的流量限制是1G，而超过流量就要使用它的“金币”购买，做过网站的朋友都清楚，1G的流量根本做不了什么事情。而且这个1G，并没有说清楚时间的限制，是每月1G？每周1G？或者所有流量加起来是1G？（图5）\n[![10202.png](/uploads/2006/03/10202.png)](/uploads/2006/03/10202.png \"10202.png\") 图1 [![10201.png](/uploads/2006/03/10201.png)](/uploads/2006/03/10201.png \"10201.png\") 图2 [![10203.png](/uploads/2006/03/10203.png)](/uploads/2006/03/10203.png \"10203.png\") 图3 [![10204.png](/uploads/2006/03/10204.png)](/uploads/2006/03/10204.png \"10204.png\") 图4 [![10205.png](/uploads/2006/03/10205.png)](/uploads/2006/03/10205.png \"10205.png\") 图5 再看看这个网站的[主站点](http://www.thec.cn/)把，已经可以很清楚这个网站的意图了，这是个“**赚钱**”的网站，至于你是用它赚钱还是被它骗钱，就取决于怎么理解了。 下面这段话是它的主页上的，我怎么看都好笑……这个世界的秘密，看来是无法掌握在我的手中了。\n当今，全世界的人都知道，IT行业是最赚钱的行业，但是它是如何赚钱的，则是这个世界的秘密。 那么，拥有了一个属于自己的网站，如何赚钱？您只需展示自我，剩下的事情我们做。\n请不要相信这个网站（以及类似的网站）一文中所提到的网站，现在已经不存在了。由此可知，这群骗人的家伙们是打一枪换一个地方，骗了钱就闪人的。希望这些看到本文的朋友们，并不在被骗的行列中。\n还是那句话：\n不要相信有免费的午餐","date":"2006-03-04","description":"","lastmod":"2006-03-04T11:50:18Z","slug":"web-cheater2","tags":[],"title":"请不要相信这个网站（2）","url":"https://blog.zengrong.net/post/web-cheater2/"},{"categories":["technology"],"content":"SWFObject，原名FlashObject，全称是：Javascript Flash Player detection and embed script，用于将swf嵌入到网页中的JavaScript脚本。挺好用，准备将其加入到Blog到编辑器中。代替原来使用的 Flash Quicktag。\nSWFObject(FlashObject)的使用方法：\n在这里下载它 解压后将其中的swfobject.js(flashobject.js)上传到网站中 在页面中输入下面的代码即可，不用输入那些麻烦的 \u0026lt;object\u0026gt; 、\u0026lt;embed\u0026gt; 了（假设swfobject.js(flashobject.js)和flashmovie.swf都处于网站根目录中）： 1\u0026lt;script src=\u0026#34;/swfobject.js\u0026#34; type=\u0026#34;text/javascript\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; 2\u0026lt;p id=\u0026#34;flashcontent\u0026#34;\u0026gt; 这些文字将被Flash影片替换。\u0026lt;/p\u0026gt; 3\u0026lt;script type=\u0026#34;text/javascript\u0026#34;\u0026gt; var fo = new SWFObject(\u0026#34;/flashmovie.swf\u0026#34;, \u0026#34;mymovie\u0026#34;, \u0026#34;200\u0026#34;, \u0026#34;100\u0026#34;, \u0026#34;7\u0026#34;, \u0026#34;#336699\u0026#34;); fo.write(\u0026#34;flashcontent\u0026#34;); \u0026lt;/script\u0026gt; 其中设置影片属性的是这句：\n1var fo = new SWFObject(\u0026#34;/flashmovie.swf\u0026#34;, \u0026#34;mymovie\u0026#34;, \u0026#34;200\u0026#34;, \u0026#34;100\u0026#34;, \u0026#34;7\u0026#34;, \u0026#34;#336699\u0026#34;); 这6个参数是必须的，具体介绍如下：\nswf - swf路径和文件名； id - 对象ID； width - 影片宽度； height - 影片高度； version - 指定Flash Player的版本。可以指定具体版本，例如： \u0026quot;6.0.65\u0026quot;。也可以指定主版本，例如：\u0026quot;6\u0026quot;； background color - 指定Flash影片的背景色 另外：\n我基于SWFObject制作了一个 Flash发布模版 ，使用它来发布影片就更方便了。\n","date":"2006-03-03","description":"","lastmod":"2006-03-03T07:48:03Z","slug":"flashobject","tags":["flashplayer","swfobject"],"title":"SWFObjcet(FlashObject)","url":"https://blog.zengrong.net/post/flashobject/"},{"categories":["impressions"],"content":" 花瓣雨音乐论坛 花露新世纪音乐 极品音乐吧 雅燃音乐伊甸园 雅燃音乐天空 美丽元素 m4mu.com 感谢小婧子提供 ;-)\nPS：看见什么是震撼的美，我只能震撼了\n","date":"2006-03-01","description":"","lastmod":"2006-03-01T05:36:40Z","slug":"music-site","tags":[],"title":"几个音乐网站","url":"https://blog.zengrong.net/post/music-site/"},{"categories":["technology"],"content":"这三个表格来自Macromedia网站，分别展示了不同设置和上网方式下FLV的帧频、下载时间与数据流量。\nFPS 一半(Half) 三分之一(Third) 四分之一(Quarter)\n\u0026quot;Thirty\u0026quot; 30 15 10 7.5 NTSC 29.97 14.985 9.990 7.493 PAL 25 12.500 8.333 6.250 24p Progressive 24 12 8 6 Film 23.98 11.990 7.993 5.995\n: 表一：不同视频标准下的帧频\n**表二：不同上网方式的视频下载时间** 500 Kbit Video 2,000 Kbit Video **Kbits** **1 min** **5 min** **1 min** **5 min** T3 1500 20 sec 2 min 80 sec 7 min T1 768 39 sec 3 min 3 min 13 min DSL 512 59 sec 5 min 4 min 20 min DSL 384 1 min 7 min 5 min 26 min Dialup 56 9 min 45 min 36 min 179 min Connection Connection Rate(Kbps) Recommended Maximum Bit Rate\nDSL 1024 800 kbps DSL 512 450 kbps DSL 384 350 kbps DSL 256 225 kbps Dialup 56.6 40 kbps\n: 表三：不同上网方式的视频流速率\n**顺便说一句：**[ActionScript 3.0 语言规范](http://livedocs.macromedia.com/specs/actionscript/3/wwhelp/wwhimpl/js/html/wwhelp.htm)在今天发布了。此消息来自[Danger's Blog](http://www.dengjie.com/)。 ","date":"2006-03-01","description":"","lastmod":"2006-03-01T01:21:10Z","slug":"flv-table","tags":["flv"],"title":"和FLV相关的三个表格","url":"https://blog.zengrong.net/post/flv-table/"},{"categories":["impressions"],"content":"SX1里的MP3换了无数次，这首曲子却始终没有删掉。以前曾经用Saxphone演奏过，但始终都觉得演奏不出里查德.克莱德曼表现出的这种淡淡的哀愁。今天，看着窗外飘扬的雪花，听着这久违的曲子，这种情绪就更加强烈。不是哀伤，不是难过，只是淡淡的，飘扬在雪花里，和着风……提琴响起，我的心突然感到释然，我的眉头舒展开来，我的嘴角泛起微笑。\nCOULEUR TENDRESSE－－这就是老友给我的感觉呵……\n您的浏览器不支持播放音乐！ ","date":"2006-02-28","description":"","lastmod":"2006-02-28T01:04:57Z","slug":"couleur-tendresse","tags":[],"title":"柔如彩虹(COULEUR TENDRESSE)","url":"https://blog.zengrong.net/post/couleur-tendresse/"},{"categories":["use"],"content":"看看标题有些难懂，不过看下面的图就好懂了。\n图1\n主要实现的效果是：有项目符号的列表，但是列表中的内容如果换行，与第一行的左缩进对齐。\n从无格式的文本开始，选中文本然后单击格式工具栏上的按钮，如图2所示\n图2\n选中所有文本打开“段落”面板，将“左缩进”调整为3字符，如图3所示\n图3\n设置之后，项目列表的左缩进变得不对齐了。如图4所示。\n图4\n要让其再次对齐，需要调整“制表位”，让“默认制表位”与“左缩进”数值相同即可。如图5所示。\n图5\n","date":"2006-02-27","description":"","lastmod":"2006-02-27T06:59:55Z","slug":"word01","tags":["office","word"],"title":"左缩进对齐的项目符号列表","url":"https://blog.zengrong.net/post/word01/"},{"categories":["impressions"],"content":"挺有意思，刚刚在Google桌面搜索的新闻中看到了时尚女性为人处世十秘诀，就有同事发来了男士十大“守婚”规则，看来还真实“无巧不成网”呀。\n时尚女性为人处世十秘诀 这十大秘诀是当代聪明的时尚女性所必须掌握的，绝不要忽视它们：\n培养自身神秘感，不要随便泄露你的隐私或者过去。 给对方一定的暗示，而不要毫无保留地和盘托出。 在适当的时候出现。 举止优雅，直白的语言和易怒的性格都会暴露出你女性魅力的不足。 保持新鲜感，让自己每天都显得和昨日有所与众不同。 由你来决定你的人生价值，你的生活是你自己的，你是自己命运的主人。 你不想被人当作永远套在脚上甩不掉的旧拖鞋吧，让他享有一定的私人空间。 任何时尚都没有比自然的女性特质更能吸引男人。 拥有自己的金融资产，进行个人的资产管理是明智的选择。 你永远不会绝望，也不会依赖别人。 其实，最重要的秘诀是控制自己的情感，否则你的生活将会崎岖不平。你应该每天早晨起床时对着镜子问一问：“我真正想要的是什么？”，然后在接下来的一天中就坚定地按照你的计划行事。\n总是认为自己很失败的女人常常会试图在心理上控制男人。她们有时会表现出一些过激而令人窒息的行为，比如说探听别人隐私，疯狂地给人打电话聊天，这些都表现出了她们内心的恐惧。应该把这些抛在脑后，在头脑中为你的未来规划一个蓝图。记住一点：如果你以欺骗的方式让一个男人和你结婚，那么以后你会有不断的麻烦，因为他的不满迟早会爆发出来。\n你必须时刻关注你的工作和生活方式。寻找一些良师益友，多交一些能够鼓励你，让你增强信心的朋友。\n在你蓝图的每一个部分都要努力追求新的知识，逐条列出行动的步骤，以实际行动来代替幻想吧。活力可以扫除障碍。不能总是用巧克力和胡思乱想来逃避现实，这样并不会起到什么作用，纵容自己是很危险的。成天让你拉帮结伙的朋友不是真朋友。故作小女孩的可怜状来逃避责任是愚蠢可笑的，这太单纯了。你可以摆脱控制并做出一些严肃的决策吗？总有一天在生活中你会意识到你必须为自己做出决定。\n在心里想想几个问题。还有工作挣钱还要供养谁？你能削减你的银行存款吗？你会宣称自己不可救药来逃避成人的责任吗（事实上是因为你的懒惰）？当有人说你昏头昏脑孩子气时你是否会感到高兴呢？是时候让自己变得成熟了。\n如果你想和一个男人谈恋爱，不要表现出情感上的饥渴，只能接受你理想中的男人。顺便说一句，这世界并不缺乏优秀的男人，但是你必须主宰自己生活，总有一天会发现你期待出现的人。\n男士十大“守婚”规则 不要指望她永远青春靓丽，人老珠黄是你必须接受的现实； 学会睁一只眼(看住自己的钱包)，闭一只眼(她卸妆的时候)； 努力让自己保持浪漫。想让聪明的女人变得愚蠢，浪漫是最好的催化剂； 记住几个特殊的日子，千万不要吝啬，不过要保留小票，方便的时候请她报销； 一周晚归最好不超过一次，晚归时间最好不超过凌晨一点。万 一超标，最好的办法是装醉，越醉越好。除了可以免去挨骂之苦，还可顺便享受一次久违的细心呵护； 如果她坚持让你陪同上街的话，一定要记住把钱包落家里。但买单时一定要奋不顾身，然后一脸懊丧：咦，忘带钱包了！ 不要忘记在出门两小时前提醒她一次，最好把家里的钟啊、表啊拔前两格。当她在镜前环顾左右时，趁她还没有发问前赶紧美言几句，否则你就再等两小时吧； 万一你实在想在外面开个小店或办事处什么的，一定要记住：悄悄地进村，打枪的不要，而且千万不能把枪落下； 千万不要在她心情不好的时候提起你的女同事、女下属、女网友。平时注意搜集一些贤妻良母的范例，在她心情好的时候娓娓道来，可能达到事半功倍的效果； 千万不要在她和网友热聊的时候打扰她，也不要在她和网友见面的时候跟踪她。如果有小孩的话，告诉小孩“你妈上超市去了，赶紧跟去帮个手”，如果没有小孩的话，你就赶紧在她回来之前灌半瓶白酒，作受伤状，保准她悬崖勒马…… ","date":"2006-02-27","description":"","lastmod":"2006-02-27T02:56:34Z","slug":"man-woman","tags":[],"title":"男士十大“守婚”规则\u0026时尚女性为人处世十秘诀","url":"https://blog.zengrong.net/post/man-woman/"},{"categories":["web"],"content":"为Blog安装了andot的CoolCode插件。但是由于我写的大多数都是ActionScript代码，但是这个插件中偏偏又没有ActionScript高亮支持的文件，无奈只有自己做一个了。\n目前的文件包括Flash 8 ActionScript和Flash Communicotion Server的绝大部分关键字、方法、构造器和对象，但不包含对组件方法的支持。\n和我有同样需求的朋友可以这样做：\n1. 首先在WordPress中安装CoolCode； 2. 下载后面的文件：\n1 文件 3. 将其解压缩后复制到如下文件夹中（视具体安装路径而定）；\n1/wordpress/wp-content/plugins/coolcode/PEAR/Text/Highlighter/ 4. 打开coolcode.php，搜索下面的语句：\n1var $acceptable_lang = array(\u0026#39;php\u0026#39;, \u0026#39;cpp\u0026#39;, \u0026#39;css\u0026#39;, \u0026#39;diff\u0026#39;, \u0026#39;dtd\u0026#39;, \u0026#39;javascript\u0026#39;, \u0026#39;html\u0026#39;, \u0026#39;mysql\u0026#39;, \u0026#39;perl\u0026#39;, \u0026#39;python\u0026#39;, \u0026#39;ruby\u0026#39;, \u0026#39;sql\u0026#39;, \u0026#39;xml\u0026#39;, \u0026#39;java\u0026#39;); 将“actionscript”支持加入其中：\n1var $acceptable_lang = array(\u0026#39;php\u0026#39;, \u0026#39;cpp\u0026#39;, \u0026#39;css\u0026#39;, \u0026#39;diff\u0026#39;, \u0026#39;dtd\u0026#39;, \u0026#39;javascript\u0026#39;, \u0026#39;actionscript\u0026#39;, \u0026#39;html\u0026#39;, \u0026#39;mysql\u0026#39;, \u0026#39;perl\u0026#39;, \u0026#39;python\u0026#39;, \u0026#39;ruby\u0026#39;, \u0026#39;sql\u0026#39;, \u0026#39;xml\u0026#39;, \u0026#39;java\u0026#39;); 想偷懒的朋友可以直接下载已经修改好的\n1 文件 5. 修改完毕。效果可以看 这里 。\n另：\n如果要自行编辑高亮语法文件，可以参考 这里 。\n","date":"2006-02-26","description":"","lastmod":"2006-02-26T13:40:10Z","slug":"coolcode-wordpress","tags":["wordpress","plugin"],"title":"加入了代码高亮插件","url":"https://blog.zengrong.net/post/coolcode-wordpress/"},{"categories":["web"],"content":"转自永远的UNIX\nUNIX命令\n（一）基本命令\n命令格式： 命令 参数\n1.ls 显示文件名，等同于dos下dir命令\n命令格式：ls [option] file\noption：\n-l 显示详细列表\n域1 ：文件类型和文件权限\n域2 ：文件连接数\n域3 ：文件所有者名字\n域4 ：文件用户组名字\n域5 ：文件长度\n域6-8 ：最近修改日期\n域9 ：文件名\n-a 显示所有文件，包含隐藏文件（以. 起头的文件名）\n-R 显示文件及所有子目录\n-F 显示文件（后跟*）和目录（后跟/）\n-d 与l选项合用，显示目录名而非其内容\n2.cd 目录转换，等同于dos下cd命令\n注意目录分隔符为??#65292;与dos相反\n命令格式：cd dirname\n3.pwd 显示当前路径\n4.cat 显示文件内容,等同于dos下type命令\n命令格式：cat filename\n5.more 以分页方式查看文件内容.\n命令格式：more filename\n6.rm 删除文件\n命令格式： rm [-r] filename (filename 可为档名，或档名缩写符号.)\n例子：\nrm file1 删除档名为 file1 之文档.\nrm file? 删除档名中有五个字元，前四个字元为file 之所有文档.\nrm f* 删除档名中，以 f 为字首之所有文档.\nrm -r dir1 删除目录 dir1，及其下所有文档及子目录.\n7.mkdir 创建目录\n命令格式： mkdir [-p] directory-name\nExmaple ：\nmkdir dir1 建立一新目录 dir1.\nmkdir -p dir/subdir 直接创建多级目录\n8.rmdir 删除目录\n目录必须首先为空\n命令格式: rmdir directory\n9.cp 文档复制\n命令格式: cp [-r] source destination\n例子:\ncp file1 file2 将文档 file1 复制成 file2\ncp file1 dir1 将文档 file1 复制到目录 dir1 下，文件名仍为 file1.\ncp /tmp/file1 . 将目录 /tmp 下的文档 file1复制到现行目录下，档名仍为 file1.\ncp /tmp/file1 file2 将目录 /tmp 下的文档 file1现行目录下，档名为file2\ncp -r dir1 dir2 (recursive copy) 复制整个目录.\n若目录 dir2 不存在，则将目录dir1，及其所有文档和子目录，复制到目录 dir2 下，新目录名称为dir1.若目录dir2不存在，则将dir1，及其所有文档和子目录，复制为目录 dir2.\n10.mv 文件移动\n命令格式： mv source destination\n例子:\nmv file1 file2 将文档 file1，更改档名为 file2.\nmv file1 dir1 将文档 file1，移到目录 dir1 下，档名仍为 file1.\nmv dir1 dir2 若目录 dir2 不存在，则将目录 dir1，及其所有档案和子目录，移到目录 dir2 下，新目录名称为 dir1.\n若目录 dir2 不存在，则将dir1，及其所有文档和子目录，更改为目录 dir2.\n11.du 查看目录所占磁碟容量\n命令格式: du [-sk] directory\n例子 :\ndu dir1 显示目录 dir1 的总容量及其次目录的容量\ndu -sk dir1 显示目录 dir1 的总容量,以k bytes为计量\n12.find 文件查找\n命令格式: find dir -name filename command\n例子:\nfind . -name hello -print 寻找目前目录及所有的子目录内叫hello的文档.\nfind . -ctime +7 -print 找出七天内未被更动的文档\nfind . -size +2000m -print 找出大小超过2000 bytes的文档\nfind /tmp -user b1234567 -print 在/tmp下属於b1234567的文档\nfind . -name '*.c' -exec rm {} 删除所有的.c档\nfind . -name test\\* -print 显示当前目录及其子目录文件名前4位为test的文件名\n13.vi 编辑器\n命令状态：\nj,k,h,l:上下左右\n0：行首\n$: 行尾\ni,I :插入命令，i 在当前光标处插入 I 行首插入\na,A:追加命令，a 在当前光标后追加，A 在行末追加\no,O:打开命令，o 在当前行下打开一行，O在当前行上插入一行\nr,R :替换命令，r 替换当前光标处字符，R从光标处开始替换数字s: 替换指定数量字符\nx: 删除光标处字符\ndd: 删除当前行\nd0: 删除光标前半行\nd$: 删除光标后半行\nctrl+f :后翻页\nctrl+b:前翻页\nG : 文件尾\n数字G: 数字所指定行\n/string 查找字符串\nn 继续查找\nN 反向继续查找\n% 查找对应括号\nu 取消上次操作\nex命令状态\n：set number 显示行号\n：set smd 显示显示状态\n：0 文件首\n：1,5 copy 7 块拷贝\n：1，5 del 块删除\n：1，5 move 7 块移动\n：1，$s/string1/string2/g 全文件查找string1并替换为string2\n：wq! 存盘退出\n（二） 增强命令\n1. ln 文档连结\n命令格式：ln -s oldname newname ( Hard link )\n同一文档，可拥有一个以上之名称，可将文档做数个连结.\n例子：\nln -s file1 file2 将名称 file2，连结至文档 file1.\n2.grep 搜索字符串\n命令格式:\ngrep string filename\n寻找字串的方法很多，比如说我想找所有以M开头的行.此时必须引进pattern的观念.以下是一些简单的□例，以及说明：\n^M 以M开头的行，^表示开始的意思\nM$ 以M结尾的行，$表示结束的意思\n^[0-9] 以数字开始的行，[]内可列举字母\n^[124ab] 以1,2,4,a,或b开头的行\n^b.503 句点表示任一字母\n* 星号表示0个以上的字母(可以没有)\n加号表示1个以上的字母\n\\. 斜线可以去掉特殊意义\ncat passwd | grep ^b 列出大学部有申请帐号者名单\ncat passwd | grep ^s 列出交换学生申请帐号者名单\ncat passwd | grep '^b.503' 列出电机系各年级...\ngrep '^\\.' myfile.txt 列出所有以句点开头的行 3.fgrep 搜索字符串\n命令格式：fgrep string file\n4.file 显示文件类型\n命令格式：file fileall\n文件类型为shell s cript,ELF 32bit,ASCII text,data or tar file\n5.diff 比较文档或目录之不同内容\n命令格式：diff [-r] name1 name2 ( name1 name2 可同时为档名，或目录名称.)\n例子 :\n%diff file1 file2\n比较文档 file1 与 file2 内，各行之不同处.\n%diff -r dir1 dir2\n比较目录 dir1 与 dir2 内，各文档之不同处.\n6.cmp 比较文档相同部分\n命令格式：cmp file1 file2\n7.ftp 远程文件传输\n命令格式： ftp [hostname|IP address]\n在进入 ftp 之後，如果与 remote host 连接上了，它将会询问你 username与密码，如果输入对了就可以开始进行文档传输.\n注意：如用户无密码，无法注册\n(1) ftp 命令\nascii 将传输模式设为 ascii 模式.通常用於传送文字档.\nbinary 将传输模式设为 binary 模式，通常用於传送执行档，压缩档与影像档等.\ncd remote-directory 将远程主机上的工作目录改变.\nlcd [ directory ] 更改本地主机的工作目录.\nls [ remote-directory ] [ local-file ] 列出远程主机上的文档.\nget remote-file [ local-file ] 取得远方的文档.\nmget remote-files 可使用通用字元一次取得多个文档.\nput local-file [ remote-file] 将本地主机的文档送到远程主机.\nmput local-files 可使用通用字元一次将多个文档放到远程主机上.\nhelp [ command ] 线上辅助指令.\nmkdir directory-name 在远程主机创建一个目录.\nprompt 更改交谈模式，若为 on 则在 mput 与 mget 时每作一个文档之传输时均会询问.\nquit/bye 离开ftp .\n(2) 后台执行ftp\n1.首先，将过程所用到的指令依顺序放入文档中，如下：\n%cat ftp_command\n!mkdir test\nlcd test\ncd test\nprompt\nbinary\nmget *.*\nbye\n2.其次，建一个.netrc档，属性为400，让ftp 自动到此读取Username与Password，方可顺利login 到的主机，如下：\n%cat .netrc\nmachine remote login anonymous password guest\n3.最後再执行下面指令即可.\n%nohup ftp remote \u0026lt; ftp_command \u0026gt; message \u0026amp;\n8.telnet 远程终端访问\n命令格式：\ntelnet [hostname|IP address]\n9.IO 重新导向\nUNIX所有的程式执行时，均需要资料的输入以及输出资料.一般而言，资料是从键盘输入，并将资料输出到萤幕上，这就叫做标准输入及标准输出，而我们可以更改标准出输出入.\nA. 更改标准输入 棗 在命令後方加\u0026quot;\u0026lt; \u0026quot; ，即可从这个文档输入资料.\nB. 更改标准输出 棗 在命令後方加\u0026quot;\u0026gt; \u0026quot; ，即可将萤幕输出的资料导向到这个文档上.\nC. 更改标准输出 棗 在命令後方加\u0026quot;\u0026gt;\u0026gt; \u0026quot;，功能与B.相似，只不过这会将资料加在文档後方.\nD. 管道 棗 在两个命令中间加上'|'，即可将前方指令的输出当成後方指令\nD. 管道 棗 在两个命令中间加上'|'，即可将前方指令的输出当成後方指令的输入.\n例:\ncd /tmp\nls -l \u0026gt; /tmp/ls.out\ncat /tmp/ls.out\nmore /tmp/ls.out\nrm /tmp/ls.out\nls | more\nls | wc -l (word count, count line number,算出文档数\n","date":"2006-02-25","description":"","lastmod":"2006-02-25T13:14:19Z","slug":"unix-commands","tags":[],"title":"【转】常用UNIX命令","url":"https://blog.zengrong.net/post/unix-commands/"},{"categories":["web"],"content":"闲来无事，跑到google上做了个个人主页。还是那个感叹：Google总是那么与众不同，\n预览我的主页\n","date":"2006-02-25","description":"","lastmod":"2006-02-25T10:43:44Z","slug":"googlepages","tags":[],"title":"Google的主页服务","url":"https://blog.zengrong.net/post/googlepages/"},{"categories":["use"],"content":"Windows XP从SP2开始自带蓝牙驱动，但是功能非常不完善。就拿我的SX1来说，使用Widcomm驱动，可以用“玩转手机”软件管理手机RAM和MMC卡中的文件，实现OutLook与手机的联系人同步，但是使用SP2自带的驱动，则提示“SX1不支持此功能”。\n但是，将蓝牙适配器插入USB口的时候，XP会自动安装自带的驱动，即使计算机中已经安装了第三方驱动。\n要完全删除XP SP2自带的蓝牙驱动，不能仅删除或重命名 WINDOWS\\inf\\bth.inf 文件即可，还应该删除下面的文件：\nWindows\\system32\\drivers\\bthenum.sys、Bthusb.sys、Bthprors.cpl; Windows\\system32\\Fsquirt.exe; Windows\\inf\\bth.inf、bth.pnf、bthpan.inf、bthpan.pnf、bthprint.inf、bthprint.pnf、bthspp.inf、bthspp.pnf。 如果你还想找回这些文件，我已经将它们打包：\n1 文件 删除完成后，重新启动计算机，再安装第三方驱动。\nWidcomm驱动可以去google搜索。\n","date":"2006-02-25","description":"","lastmod":"2006-02-25T07:59:46Z","slug":"sp2-bluetooth","tags":["windows","bluetooth"],"title":"彻底删除WinXP SP2自带的蓝牙驱动","url":"https://blog.zengrong.net/post/sp2-bluetooth/"},{"categories":["news"],"content":"去opera主页下载了opera mini装到了我的SX1上，发现这个小东西挺好用。支持我BLOG的大部分功能，后台的JS也支持，真的很意外。\n不过，要不断在n多按钮中切换，确实是辛苦的事：)\n","date":"2006-02-24","description":"","lastmod":"2006-02-24T10:42:53Z","slug":"phone-blog1","tags":["mobile"],"title":"在手机上发的第一篇BLOG","url":"https://blog.zengrong.net/post/phone-blog1/"},{"categories":["others"],"content":" 杜永道　发布时间： 2006-02-23 07:24　来源：光明网\n问：“那是甘露sǎ向茁壮的新苗”一句中的sǎ字应为“撒”还是“洒”？有什么区别？（网友：高高）\n答：\n“洒”跟“撒”(sǎ ) 的用法有所不同。“洒”用于液体。例如:\n洒水车过来了\n别把汤洒了\n“撒”(sǎ )用于固体。例如:\n豆子撒了一地\n沙子都撒出来了\n另外,指“阳光”“爱心”等,一般用“洒”。例如:\n阳光洒在大地上\n让爱心洒满人间\n所以，“甘露sǎ向茁壮的新苗”中应当用“洒”。\n","date":"2006-02-23","description":"","lastmod":"2006-02-23T00:48:59Z","slug":"sa","tags":[],"title":"【转】“洒”跟“撒”有什么不同","url":"https://blog.zengrong.net/post/sa/"},{"categories":["others"],"content":"今天用拼音加加4打xiangxiang一词时，偶尔发现默认词库中只有“想像”而没有“想象”。我是偏重于使用“想像”的，但是身边有许多人都是使用“想象”，于是google一番，找到下面这篇文章。\n【咬文嚼字】想象？想像？\n汪启喜 作于 2004年12月3日\n记得在我的意识里，这个问题是早就解决的了。可如今由于从事编辑工作的缘故，又被翻了出来。因为大家有些不同。\n说早就解决了，是因为我记得在哪一年有一期《小学语文教师》有篇文章就谈到这个问题。\n文章大意是：\n“xiangxiang”这个词，在以前，解放前，无论是白话文，还是古文（文言文），用的都是“想像”，而没有“想象”一词。\n可到了解放后，进行第一次汉字简化，把“像”与“象”给同化了，统一用象来代替“像”。\n好象是到了七几年还是八几年，第二次进行汉字简化工作时（暂用此提法），又恢复了“像”与“象”的分工。可人们已经习惯了用“想像”来代替“想象”了，所以有人就提出，也就没有进行修正。\n因此，“想像”与“想象”就成了异体并用的词了。\n这种并用状态到了前年我国推行《第一批异形次整理表》，也没有把它们列入规范行列，可见在学界关于“想像”与“想象”应用谁还是没有达成共识。因此，可以说它们是可以并用的。\n我接受了这种观点，更认同其中的“本来用的应该是‘想像’”。于是，在我撰写《想像作文类型研究报告》时，我就全部统一用“想像”。因为我认同大脑思维“想”的只能是“像”的东西，从单字解释来说，“像”的义项比“象”更帖近“xiangxiang”的意思。\n可文章交上去，虽然发表了，但却被全部改成了“想象”。这回我可纳闷了。一对通用的异形词，他们为何偏向于“想象”呢？依据？我一直存疑。\n今年八月起，我到了编辑部工作。校对一篇文章时，我就顺着他们原先的做法把一篇文章的“想像”统一改成“想象”。可同事却说，应该改成“想像”，这个问题他们早就解决了。我说，那以前我那篇……她说，对，就是那次以后，我们正式把这个问题解决了，用“像”。\n这回，可让我苦笑不得了。\n那么，这个问题真就这么解决了？\n今年，那本我盼了六年的《现代汉语规范词典》终于出版了。翻开里面的解释，我又傻眼了——\n【想象】：①名词，指在知觉材料的基础上，经过头脑加工而创造出新形象的心理过程。△～丰富。②动词，推想出不在眼前的事物的具体形象或发展结果△不难～他当时该有何等的痛苦。\n【想像】：全国科学技术名词审定委员会和国家语言文字工作委员会已确定“想象”为推荐词形。\n已成定论？不知这个根据（相关文件）从何而来。但也是好事，因为，现在的许多输入法，根本就只认“想象”而不认“想像”。看来，大家用“想象”的人还是比较多的。我也准备接受同化了。\n做个游戏把，用搜索引擎看谁的多——\n想象　想像\n〖百度〗4,780,000 篇 ●2,770,000 篇\n〖一搜〗1,752,553 项 ● 832,592 项\n〖新浪〗2,702,019 个 ● 993,229 个\n〖搜狐〗2,452,882 个 ●1,037,766 个\n看来,结论是不言而喻的.那我们就用“想象”吧！\n","date":"2006-02-23","description":"","lastmod":"2006-02-23T00:45:58Z","slug":"xiangxiang","tags":[],"title":"是“想像”还是“想象”？","url":"https://blog.zengrong.net/post/xiangxiang/"},{"categories":["web"],"content":"本文转自阿辛的Blog\n1. coolcode\n代码高亮插件\n官方说明：\nhttp://www.coolcode.cn/?p=26\n2. wp-dbmanager201\n数据库备份插件\n官方说明：\nhttp://www.lesterchan.net/portfolio/programming.php\n3. pagepost\n长文章分页插件\n官方说明：\nhttp://www.coolcode.cn/?p=27\n4. wp-stats201\nblog状态统计插件\n官方说明：\nhttp://www.lesterchan.net/portfolio/programming.php\n5. jeromes-keywords\ntag、keywords插件\n官方说明：\nhttp://vapourtrails.ca/wp-keywords\n6. Trackping Separator Plugin Instruction\n将trackback和comment分开显示插件\n官方说明：\nhttp://notes.mk-notes.com/tsp\n7. phpexec\n在日志中运行PHP程序插件\n官方说明：\nhttp://priyadi.net/archives/2005/03/02/wordpress-PHP-exec-plugin/\n8. random-quotes\n随机名人名言显示插件\n官方说明：\nhttp://xinple.org/?p=37\n9. get-recent-comments-1.2\n显示最新评论和trackback\n官方说明：\nhttp://blog.jodies.de/archiv/2004/11/13/recent-comments/\n注意：使用此插件之后，查看网页源代码会出现乱码\n10. random-posts\n随机日志显示插件\n官方说明：\nhttp://yanfeng.org/blog/index.php?p=373\n注意，使用最新版。旧版会使网页源文件乱码\n11. Media_Filter\n媒体播放插件\n官方说明：\nhttp://last.kmip.net/wp/?p=97\n12. sk2_final\n垃圾过滤插件\n官方说明：\nhttp://unknowngenius.com/blog/wordpress/spam-karma/\n13. adhesive\n日志置顶插件\n官方说明：\nhttp://redalt.com/wiki/Adhesive\n注意：使用此插件之后和jeromes-keywords插件发生冲突，主要表现是tag失效，无法显示\n14. codefilter\n屏蔽code标签中的HTML代码\n官方说明：\nhttp://redalt.com/downloads/\n15. wp-grins\n表情插件\n官方说明：\nhttp://www.alexking.org/software/wordpress/\n需要的地方插入\ntheme中的：comments.php，wp-admin中的edit-form-advance.php/edit-form.php\n16. acronyms\n缩写解释插件\n官方说明：\nhttp://www.huddledmasses.org/2004/04/19/wordpress-plugin-acronym-replacer/\n17. Auto Links\n自动添加连接插件\n官方说明：\nhttp://autolinks.headzoo.com/\n最新版二月份推出，支持中文，关注中。\n旧版仍旧可以使用，不过不支持中文。\n18. comment_hacks.php\n最新评论、文章、积极网友、热门文章\n官方说明：\nhttp://yanfeng.org/blog/479/\n","date":"2006-02-22","description":"","lastmod":"2006-02-22T06:15:06Z","slug":"wordpress-plugin","tags":["wordpress","plugin"],"title":"【转】常用的WordPress插件","url":"https://blog.zengrong.net/post/wordpress-plugin/"},{"categories":["technology"],"content":"原文来自http://www.5ilinux.com/php01.html\n[PHP]\n; PHP还是一个不断发展的工具，其功能还在不断地删减\n; 而php.ini的设置更改可以反映出相当的变化，\n; 在使用新的PHP版本前，研究一下php.ini会有好处的\n;;;;;;;;;;;;;;;;;;;\n; 关于这个文件 ;\n;;;;;;;;;;;;;;;;;;;\n; 这个文件控制了PHP许多方面的观点。为了让PHP读取这个文件，它必须被命名为\n; 'php.ini'。PHP 将在这些地方依次查找该文件：当前工作目录；环境变量PHPRC\n; 指明的路径；编译时指定的路径。\n; 在windows下，编译时的路径是Windows安装目录。\n; 在命令行模式下，php.ini的查找路径可以用 -c 参数替代。\n; 该文件的语法非常简单。空白字符和用分号';'开始的行被简单地忽略（就象你可能\n; 猜到的一样）。 章节标题（例如 : [Foo]）也被简单地忽略，即使将来它们可能\n; 有某种的意义。\n;\n; 指示被指定使用如下语法：\n; 指示标识符 = 值\n; directive = value\n; 指示标识符 是 *大小写敏感的* - foo=bar 不同于 FOO = bar。\n;\n; 值可以是一个字符串，一个数字，一个 PHP 常量 (如： E_ALL or M_PI), INI 常量中的\n; 一个 (On, Off, True, False, Yes, No and None) ，或是一个表达式\n; (如: E_ALL \u0026amp; ~E_NOTICE), 或是用引号括起来的字符串(\u0026quot;foo\u0026quot;).\n;\n; INI 文件的表达式被限制于位运算符和括号。\n; | bitwise OR\n; \u0026amp; bitwise AND\n; ~ bitwise NOT\n; ! boolean NOT\n;\n; 布尔标志可用 1, On, True or Yes 这些值置于开的状态。\n; 它们可用 0, Off, False or No 这些值置于关的状态。\n;\n; 一个空字符串可以用在等号后不写任何东西表示，或者用 None 关键字:\n;\n; foo = ; 将foo置为空字符串\n; foo = none ; 将foo置为空字符串\n; foo = \u0026quot;none\u0026quot; ; 将foo置为字符串'none'\n;\n; 如果你值设置中使用常量，而这些常量属于动态调入的扩展库（不是 PHP 的扩展，就是\n; Zend 的扩展），你仅可以调入这些扩展的行*之后*使用这些常量。\n;\n; 所有在 php.ini-dist 文件里设定的值与内建的默认值相同（这是说，如果 php.ini\n; 没被使用或者你删掉了这些行，默认值与之相同）。\n;;;;;;;;;;;;;;;;;;;;\n; 语言选项 ;\n;;;;;;;;;;;;;;;;;;;;\nengine = On\n; 使 PHP scripting language engine（PHP 脚本语言引擎）在 Apache下有效。\nshort_open_tag = On\n; 允许 tags 将被识别。\nasp_tags = Off\n; 允许ASP-style tags\nprecision = 14\n; 浮点类型数显示时的有效位数\ny2k_compliance = Off\n; 是否打开 2000年适应 (可能在非Y2K适应的浏览器中导致问题)\noutput_buffering = Off\n; 输出缓存允许你甚至在输出正文内容之后发送 header（标头，包括cookies）行\n; 其代价是输出层减慢一点点速度。你可以使用输出缓存在运行时打开输出缓存，\n; 或者在这里将指示设为 On 而使得所有文件的输出缓存打开。\noutput_handler = ; 你可以重定向你的脚本的所有输出到一个函数，\n; 那样做可能对处理或以日志记录它有用。\n; 例如若你将这个output_handler 设为\u0026quot;ob_gzhandler\u0026quot;,\n; 则输出会被透明地为支持gzip或deflate编码的浏览器压缩。\n; 设一个输出处理器自动地打开输出缓冲。\nimplicit_flush = Off\n; 强制flush（刷新）让PHP 告诉输出层在每个输出块之后自动刷新自身数据。\n; 这等效于在每个 print() 或 echo() 调用和每个 HTML 块后调用flush()函数。\n; 打开这项设置会导致严重的运行时冲突，建议仅在debug过程中打开。\nallow_call_time_pass_reference = On\n; 是否让强迫函数调用时按引用传递参数。这一方法遭到抗议，\n; 并可能在将来版本的PHP/Zend里不再支持。\n; 受到鼓励的指定哪些参数按引用传递的方法是在函数声明里。\n; 你被鼓励尝试关闭这一选项并确认你的脚本仍能正常工作，以保证在将来版本的语言里\n; 它们仍能工作。（你将在每次使用该特点时得到一个警告，而参数将按值而不是按引用\n; 传递）。\n; Safe Mode 安全模式\nsafe_mode = Off\nsafe_mode_exec_dir =\nsafe_mode_allowed_env_vars = PHP_\n; ？Setting certain environment variables\n; ？may be a potential security breach.\n; 该指示包含用逗号分隔的前缀列表。安全模式中，用户仅可以替换\n; 以在此列出的前缀开头的环境变量的值。\n; 默认地，用户将仅能 设定以PHP_开头的环境变量，（如: PHP_FOO=BAR）。\n; 注意: 如果这一指示为空，PHP 将让用户更改任意环境变量!\nsafe_mode_protected_env_vars = LD_LIBRARY_PATH\n; 这条指示包含一个用逗号分隔的环境变量列表，那是最终用户将不能用putenv () 更改的。\n; 这些变量甚至在safe_mode_allowed_env_vars 设置为允许的情况下得到保护。\ndisable_functions =\n; 这条指示让你可以为了安全的原因让特定函数失效。\n; 它接受一个用逗号分隔的函数名列表。\n; 这条指示 *不受* 安全模式是否打开的影响。\n; 语法高亮模式的色彩。\n; 只要能被接受的东西就能工作。\nhighlight.string = #DD0000\nhighlight.comment = #FF8000\nhighlight.keyword = #007700\nhighlight.bg = #FFFFFF\nhighlight.default = #0000BB\nhighlight.html = #000000\n; Misc 杂项\nexpose_php = Off\n; 决定 PHP 是否标示它装在服务器上的事实（例如：加在它 —PHP—给Web服务\n; 发送的信号上）。\n; （我个人的意见，在出现什么power-by的header的时候，把这关掉。）\n; 它不会有安全上的威胁, 但它使检查你的服务器上是否安装了PHP成为了可能。\n;;;;;;;;;;;;;;;;;;;\n; Resource Limits ;\n;;;;;;;;;;;;;;;;;;;\nmax_execution_time = 30 ; 每个脚本的最大执行时间, 按秒计\nmemory_limit = 8388608 ; 一个脚本最大可使用的内存总量 (这里是8MB)\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n; Error handling and logging ;\n; 出错控制和登记 ;\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n; 错误报告是按位的。或者将数字加起来得到想要的错误报告等级。\n; E_ALL - 所有的错误和警告\n; E_ERROR - 致命性运行时错\n; E_WARNING - 运行时警告（非致命性错）\n; E_PARSE - 编译时解析错误\n; E_NOTICE - 运行时提醒(这些经常是是你的代码的bug引起的，\n;也可能是有意的行为造成的。(如：基于未初始化的变量自动初始化为一个\n;空字符串的事实而使用一个未初始化的变量)\n; E_CORE_ERROR - 发生于PHP启动时初始化过程中的致命错误\n; E_CORE_WARNING - 发生于PHP启动时初始化过程中的警告(非致命性错)\n; E_COMPILE_ERROR - 编译时致命性错\n; E_COMPILE_WARNING - 编译时警告(非致命性错)\n; E_USER_ERROR - 用户产生的出错消息\n; E_USER_WARNING - 用户产生的警告消息\n; E_USER_NOTICE - 用户产生的提醒消息\n; 例子:\n; error_reporting = E_ALL \u0026amp; ~E_NOTICE ; 显示所有的错误，除了提醒\n; error_reporting = E_COMPILE_ERROR|E_ERROR|E_CORE_ERROR ; 仅显示错误\nerror_reporting = E_ALL \u0026amp; ~E_NOTICE ; 显示所有的错误，除了提醒\ndisplay_errors = On ; 显示出错误信息(作为输出的一部分)\n; 在最终发布的web站点上，强烈建议你关掉这个特性，并使用\n; 错误日志代替（参看下面）。\n; 在最终发布的web站点继续让 display_errors 有效可能\n; 暴露一些有关安全的信息，例如你的web服务上的文件路径、\n; 你的数据库规划或别的信息。\ndisplay_startup_errors = Off ; 甚至当display_erroes打开了，发生于PHP的启动的步骤中\n; 的错误也不会被显示。\n; 强烈建议保持使 display_startup_errors 关闭，\n; 除了在改错过程中。\nlog_errors = Off ; 在日志文件里记录错误（服务器指定的日志，stderr标准错误输出，或error_log(下面的））\n; 正如上面说明的那样，强烈建议你在最终发布的web站点以日志记录错误\n; 取代直接错误输出。\ntrack_errors = Off ; 保存最近一个 错误/警告 消息于变量 $php_errormsg (boolean)\n;error_prepend_string = \u0026quot;\u0026quot; ; 于错误信息前输出的字符串\n;error_append_string = \u0026quot;\u0026quot; ; 于错误信息后输出的字符串\n;error_log = filename ; 记录错误日志于指定文件\n;error_log = syslog ; 记录错误日志于系统日志 syslog (NT 下的事件日志, Windows 95下无效)\nwarn_plus_overloading = Off ; 当将‘+’用于字符串时警告\n;;;;;;;;;;;;;;;;;\n; Data Handling ;\n;;;;;;;;;;;;;;;;;\nvariables_order = \u0026quot;EGPCS\u0026quot; ; 这条指示描述了PHP 记录\n; GET, POST, Cookie, Environment and Built-in 这些变量的顺序。\n; （以 G, P, C, E \u0026amp; S 代表，通常以 EGPCS 或 GPC 的方式引用）。\n; 按从左到右记录，新值取代旧值。\nregister_globals = On ; 是否将这些 EGPCS 变量注册为全局变量。\n; 若你不想让用户数据不在全局范围内混乱的话，你可能想关闭它。\n; 这和 track_vars 连起来用更有意义 — 这样你可以通过\n; $HTTP_*_VARS[] 数组访问所有的GPC变量。\nregister_argc_argv = On ; 这条指示告诉 PHP 是否声明 argv和argc 变量\n; （注：这里argv为数组,argc为变量数）\n; （其中包含用GET方法传来的数据）。\n; 若你不想用这些变量，你应当关掉它以提高性能。\ntrack_vars = On ; 使$HTTP_*_VARS[]数组有效，这里*在使用时用\n; ENV, POST, GET, COOKIE or SERVER替换\npost_max_size = 8M ; PHP将接受的POST数据最大大小。\ngpc_order = \u0026quot;GPC\u0026quot; ; 这条指示被人反对。用 variables_order 代替。\n; Magic quotes\nmagic_quotes_gpc = On ; 在输入的GET/POST/Cookie数据里使用魔术引用\n; （原文就这样，呵呵，所谓magic quotes 应该是指用转义符加在引用性的控制字符上，如 '....）\nmagic_quotes_runtime= Off ; 对运行时产生的数据使用魔术引用，\n; 例如：用SQL查询得到的数据，用exec()函数得到的数据，等等\nmagic_quotes_sybase = Off ; 采用 Sybase形式的魔术引用（用 '' 脱出 ' 而不用 '）\n; 自动在 PHP 文档之前和之后添加文件\nauto_prepend_file =\nauto_append_file =\n; 象4.04b4一样，PHP 默认地总是在 “Content-type:” 头标输出一个字符的编码方式。\n; 让输出字符集失效，只要设置为空。\n; PHP 的内建默认值是 text/html\ndefault_mimetype = \u0026quot;text/html\u0026quot;\n;default_charset = \u0026quot;iso-8859-1\u0026quot;\n;;;;;;;;;;;;;;;;;;;;;;;;;\n; Paths and Directories ;\n;;;;;;;;;;;;;;;;;;;;;;;;;\ninclude_path = ; include 路径设置，UNIX: \u0026quot;/path1:/path2\u0026quot; Windows: \u0026quot;\\path1;\\path2\u0026quot;\ndoc_root = ; php 页面的根路径，仅在非空时有效\nuser_dir = ; 告知 php 在使用 /~username 打开脚本时到哪个目录下去找，仅在非空时有效\n;upload_tmp_dir = ; 存放用HTTP协议上载的文件的临时目录（在没指定时使用系统默认的）\nupload_max_filesize = 2097152 ; 文件上载默认地限制为2 Meg\nextension_dir = c:\\php\\ ; 存放可加载的扩充库（模块）的目录\nenable_dl = On ; 是否使dl()有效。\n; 在多线程的服务器上 dl()函数*不能*很好地工作，\n; 例如IIS or Zeus，并在其上默认为禁止\n;;;;;;;;;;;;;;;;\n; File Uploads ;\n;;;;;;;;;;;;;;;;\nfile_uploads = On ; 是否允许HTTP方式文件上载\n;upload_tmp_dir = ; 用于HTTP上载的文件的临时目录（未指定则使用系统默认）\nupload_max_filesize = 2M ; 上载文件的最大许可大小\n; Fopen wrappers ;\n;;;;;;;;;;;;;;;;;;\nallow_url_fopen = On ; 是否允许把URLs当作http:.. 或把文件当作ftp:...\n;;;;;;;;;;;;;;;;;;;;;;\n; 动态扩展 ;\n; Dynamic Extensions ;\n;;;;;;;;;;;;;;;;;;;;;;\n; 若你希望一个扩展库自动加载，用下面的语法：\n; extension=modulename.extension\n; 例如，在windows上，\n; extension=msql.dll\n; or 在UNIX下,\n; extension=msql.so\n; 注意，这只应当是模块的名字，不需要目录信息放在里面。\n; 用上面的 extension_dir 指示指定扩展库的位置。\n;Windows 扩展\n;extension=php_nsmail.dll\nextension=php_calendar.dll\n;extension=php_dbase.dll\n;extension=php_filepro.dll\nextension=php_gd.dll\n;extension=php_dbm.dll\n;extension=php_mssql.dll\n;extension=php_zlib.dll\n;extension=php_filepro.dll\n;extension=php_imap4r2.dll\n;extension=php_ldap.dll\n;extension=php_crypt.dll\n;extension=php_msql2.dll\n;extension=php_odbc.dll\n; 注意， MySQL的支持现在是内建的，因此，不需要用它的dll\n;;;;;;;;;;;;;;;;;;;\n; 模块设定 ;\n; Module Settings ;\n;;;;;;;;;;;;;;;;;;;\n[Syslog]\ndefine_syslog_variables = Off ; 是否定义各种的系统日志变量\n; 如：$LOG_PID, $LOG_CRON, 等等。\n; 关掉它是个提高效率的好主意。\n; 运行时，你可以调用函数define_syslog_variables()，来定义这些变量\n[mail function]\nSMTP = localhost ;仅用于win32系统\nsendmail_from = me@localhost.com ;仅用于win32系统\n;sendmail_path = ;仅用于unix, 也可支持参数（默认的是'sendmail -t -i'）\n[Debugger]\ndebugger.host = localhost\ndebugger.port = 7869\ndebugger.enabled = False\n[Logging]\n; 这些配置指示用于示例的日志记录机制。\n; 看 examples/README.logging 以得到更多的解释\n;logging.method = db\n;logging.directory = /path/to/log/directory\n[Java]\n;java.class.path = .\\php_java.jar\n;java.home = c:\\jdk\n;java.library = c:\\jdk\\jre\\bin\\hotspot\\jvm.dll\n;java.library.path = .\\\n[SQL]\nsql.safe_mode = Off\n[ODBC]\n;uodbc.default_db = Not yet implemented\n;uodbc.default_user = Not yet implemented\n;uodbc.default_pw = Not yet implemented\nuodbc.allow_persistent = On ; 允许或禁止 持久连接\nuodbc.check_persistent = On ; 在重用前检查连接是否还可用\nuodbc.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\nuodbc.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\nuodbc.defaultlrl = 4096 ; 控制 LONG 类型的字段。返回变量的字节数，0 代表通过（？）0 means passthru\nuodbc.defaultbinmode = 1 ; 控制 二进制数据。0 代表?????Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char\n; 见有关 odbc_binmode 和 odbc_longreadlen 的文档以得到 uodbc.defaultlrl 和 uodbc.defaultbinmode 的解释。\n[MySQL]\nmysql.allow_persistent = On ; 允许或禁止 持久连接\nmysql.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\nmysql.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\nmysql.default_port = ; mysql_connect() 使用的默认端口，如不设置，mysql_connect()\n; 将使用变量 $MYSQL_TCP_PORT，或在/etc/services 下的mysql-tcp 条目(unix)，\n; 或在编译是定义的 MYSQL_PORT(按这样的顺序)\n; Win32环境，将仅检查MYSQL_PORT。\nmysql.default_socket = ; 用于本地 MySql 连接的默认的套接字名。为空，使用 MYSQL 内建值\nmysql.default_host = ; mysql_connect() 默认使用的主机（安全模式下无效）\nmysql.default_user = ; mysql_connect() 默认使用的用户名（安全模式下无效）\nmysql.default_password = ; mysql_connect() 默认使用的密码（安全模式下无效）\n; 注意，在这个文件下保存密码通常是一个*坏*主意\n; *任何*可以使用PHP访问的用户可以运行\n; 'echo cfg_get_var(\u0026quot;mysql.default_password\u0026quot;)'来显示那个密码!\n; 而且当然地，任何有读该文件权力的用户也能看到那个密码。\n[mSQL]\nmsql.allow_persistent = On ; 允许或禁止 持久连接\nmsql.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\nmsql.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\n[PostgresSQL]\npgsql.allow_persistent = On ; 允许或禁止 持久连接\npgsql.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\npgsql.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\n[Sybase]\nsybase.allow_persistent = On ; 允许或禁止 持久连接\nsybase.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\nsybase.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\n;sybase.interface_file = \u0026quot;/usr/sybase/interfaces\u0026quot;\nsybase.min_error_severity = 10 ; 显示的错误的最低严重性\nsybase.min_message_severity = 10 ; 显示的消息的最低重要性\nsybase.compatability_mode = Off ; 与旧版的PHP 3.0 兼容的模式。若打开，这将导致 PHP 自动地\n; 把根据结果的 Sybase 类型赋予它们，\n; 而不是把它们全当成字符串。\n; 这个兼容模式不会永远留着，\n; 因此，将你的代码进行需要的修改，\n; 并将该项关闭。\n[Sybase-CT]\nsybct.allow_persistent = On ; 允许或禁止 持久连接\nsybct.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\nsybct.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\nsybct.min_server_severity = 10 ; 显示的错误的最低严重性\nsybct.min_client_severity = 10 ; 显示的消息的最低重要性\n[bcmath]\nbcmath.scale = 0 ; 用于所有bcmath函数的10十进制数数字的个数number of decimal digits for all bcmath functions\n[browscap]\n;browscap = extra/browscap.ini\nbrowscap = C:\\WIN\\SYSTEM\\inetsrv\\browscap.ini\n[Informix]\nifx.default_host = ; ifx_connect() 默认使用的主机（安全模式下无效）\nifx.default_user = ; ifx_connect() 默认使用的用户名（安全模式下无效）\nifx.default_password = ; ifx_connect() 默认使用的密码（安全模式下无效）\nifx.allow_persistent = On ; 允许或禁止 持久连接\nifx.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\nifx.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\nifx.textasvarchar = 0 ; 若打开，select 状态符返回一个 ‘text blob’字段的内容，而不是它的id\nifx.byteasvarchar = 0 ; 若打开，select 状态符返回一个 ‘byte blob’字段的内容，而不是它的id\nifx.charasvarchar = 0 ; 追踪从固定长度的字符列里剥离的空格。\n; 可能对 Informix SE 用户有效。\nifx.blobinfile = 0 ; 若打开，text和byte blobs 的内容被导出到一个文件\n; 而不是保存到内存。\nifx.nullformat = 0 ; NULL（空）被作为空字段返回，除非，这里被设为1。\n; 这种情况下（为1），NULL作为字串NULL返回。\n[Session]\nsession.save_handler = files ; 用于保存/取回数据的控制方式\nsession.save_path = C:\\win\\temp ; 在 save_handler 设为文件时传给控制器的参数，\n; 这是数据文件将保存的路径。\nsession.use_cookies = 1 ; 是否使用cookies\nsession.name = PHPSESSID\n; 用在cookie里的session的名字\nsession.auto_start = 0 ; 在请求启动时初始化session\nsession.cookie_lifetime = 0 ; 为按秒记的cookie的保存时间，\n; 或为0时，直到浏览器被重启\nsession.cookie_path = / ; cookie的有效路径\nsession.cookie_domain = ; cookie的有效域\nsession.serialize_handler = php ; 用于连接数据的控制器\n; php是 PHP 的标准控制器。\nsession.gc_probability = 1 ; 按百分比的'garbage collection（碎片整理）'进程\n; 在每次 session 初始化的时候开始的可能性。\nsession.gc_maxlifetime = 1440 ; 在这里数字所指的秒数后，保存的数据将被视为\n; '碎片(garbage)'并由gc 进程清理掉。\nsession.referer_check = ; 检查 HTTP引用以使额外包含于URLs中的ids无效\nsession.entropy_length = 0 ; 从文件中读取多少字节\nsession.entropy_file = ; 指定这里建立 session id\n; session.entropy_length = 16\n; session.entropy_file = /dev/urandom\nsession.cache_limiter = nocache ; 设为{nocache,private,public},以决定 HTTP 的\n; 缓存问题\nsession.cache_expire = 180 ; 文档在 n 分钟后过时\nsession.use_trans_sid = 1 ; 使用过渡性的 sid 支持，若编译时许可了\n; --enable-trans-sid\nurl_rewriter.tags = \u0026quot;a=href,area=href,frame=src,input=src,form=fakeentry\u0026quot;\n[MSSQL]\n;extension=php_mssql.dll\nmssql.allow_persistent = On ; 允许或禁止 持久连接\nmssql.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\nmssql.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\nmssql.min_error_severity = 10 ; 显示的错误的最低严重性\nmssql.min_message_severity = 10 ; 显示的消息的最低重要性\nmssql.compatability_mode = Off ; 与旧版的PHP 3.0 兼容的模式。\n[Assertion]\n; ？？？？？\n;assert.active = On ; ？assert(expr); active by default\n;assert.warning = On ; issue a PHP warning for each failed assertion.\n;assert.bail = Off ; don't bail out by default.\n;assert.callback = 0 ; user-function to be called if an assertion fails.\n;assert.quiet_eval = 0 ; eval the expression with current error_reporting(). set to true if you want error_reporting(0) around the eval().\n[Ingres II]\nii.allow_persistent = On ; 允许或禁止 持久连接\nii.max_persistent = -1 ; 持久连接的最大数。-1 代表无限制\nii.max_links = -1 ; 连接的最大数目（持久和非持久）。-1 代表无限制\nii.default_database = ; 默认 database (format : [node_id::]dbname[/srv_class]\nii.default_user = ; 默认 user\nii.default_password = ; 默认 password\n[Verisign Payflow Pro]\npfpro.defaulthost = \u0026quot;test.signio.com\u0026quot; ; 默认的 Signio 服务器\npfpro.defaultport = 443 ; 连接的默认端口\npfpro.defaulttimeout = 30 ; 按秒计的默认超时时间\n; pfpro.proxyaddress = ; 默认的代理的 IP 地址（如果需要）\n; pfpro.proxyport = ; 默认的代理的端口\n; pfpro.proxylogon = ; 默认的代理的登录（logon 用户名）\n; pfpro.proxypassword = ; 默认的代理的密码\n[Sockets]\nsockets.use_system_read = On ; 使用系统的read() 函数替代 php_read()封装\n; Local Variables: （局部变量）\n; tab-width: 4\n; End\n","date":"2006-02-18","description":"","lastmod":"2006-02-18T09:37:18Z","slug":"phpini-chinese","tags":["php"],"title":"【转】php.ini 中文版","url":"https://blog.zengrong.net/post/phpini-chinese/"},{"categories":["technology"],"content":"根据网友 冷雨 的要求写了一个类，用于检测swf是否存在于我们希望的网站中。\n提供两个方法：\ndetectUrl(allow_url:String, is_unload:Boolean):Boolean\ndetectUrl\t接受两个参数，第一个字符串参数为要检测的域名，域名不要带“http://”，如果处于此域名中，返回true，否则返回false。第二个布尔参数为是否则在返回ture时载影片。\ndetectUrlInTxt(txt_url:String, is_unload:Boolean):Void\ndetectUrlInTxt 接受两个参数，第一个字符串参数为包含要检测域名的txt文本文件，如果处于此域名中，调用onResult处理器，并传入值true，否则传入false。如果载入txt文件失败，传入值null。第二个参数作用同detectUrl。txt文件中域名定义的格式为：\u0026amp;allowUrl=www.abc.com\u0026amp;\n1class Domain extends String{ 2\tprivate var txtLV:LoadVars; 3\tprivate var isUnLoad:Boolean; 4\tpublic var onResult; 5\tfunction Domain(Void){}; 6\tpublic function detectUrl(allow_url:String, is_unload:Boolean):Boolean{ 7\tvar theDomain = _root._url; 8\tif((theDomain.indexOf(allow_url) != -1) \u0026amp;\u0026amp; (theDomain.indexOf(\u0026#34;http://\u0026#34;) == 0)){ 9\treturn true; 10\t}else{ 11\tisUnLoad = is_unload; 12\tif(isUnLoad){ 13\t_root.unloadMovie(); 14\t} 15\treturn false; 16\t} 17\t} 18\tpublic function detectUrlInTxt(txt_url:String, is_unload:Boolean):Void{ 19\tvar owner = this; 20\towner.isUnLoad = is_unload; 21\ttxtLV = new LoadVars(); 22\ttxtLV.load(txt_url); 23\ttxtLV.onLoad = function(success:Boolean){ 24\tif(success){ 25\towner.onResult(owner.detectUrl(this.allowUrl)); 26\t} else { 27\towner.onResult(null); 28\tif(owner.isUnLoad){ 29\t_root.unloadMovie(); 30\t} 31\t} 32\t} 33\t} 34} 预览\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n1 文件 ","date":"2006-02-18","description":"","lastmod":"2006-02-18T08:17:04Z","slug":"domain-class","tags":["actionscript","flash"],"title":"检测swf所在网站的域名","url":"https://blog.zengrong.net/post/domain-class/"},{"categories":["technology"],"content":"图片来自：http://fczone.com/cheat/\n","date":"2006-02-13","description":"","lastmod":"2006-02-13T02:48:45Z","slug":"fmsss-cheat-sheet","tags":["actionscript","fms","server"],"title":"服务器端ActionScript表","url":"https://blog.zengrong.net/post/fmsss-cheat-sheet/"},{"categories":["technology"],"content":"问题：\n将一个视频聊天室系统从FCS升级到FMS，目前的客户端是Flash player 8，客人从浏览器登陆后，选择一个聊天室进入，此时会弹出一个新窗口，聊天室就在新窗口中。但是当关闭聊天室的时候，却并没有触发FMS程序中的onDisConnect事件，客人仍然可以听到聊天室中的声音，必须关闭所有的浏览器窗口，才会完全退出聊天室。\n又一个测试：\n后来又做了一个实验。先开一个带有Flash内容的窗口（和聊天室无关的站点），然后打开聊天室站点页面，再打开聊天室页面。关闭的时候先关闭聊天室页面，再关闭聊天室站点页面，发现NC仍然没有断开。直到关闭所有含有Flash内容的页面，才真正断开。\n分析原因：\n虽然是3个窗口，但是仍然使用的是一个ie进程，而且默认情况下所有的窗口都是一个进程。虽然聊天室窗口关闭了，但是因为其他窗口没有关闭，IE进程也没有关闭。swf文件仍然存在于ie的缓存中，所以NC连接仍然保持。如果关闭所有的窗口，IE的进程关闭了，那么NC就自然断开了。\n证明分析：\n为了验证上面的分析，我通过修改注册表强制IE每次打开都是单独的进程，上面的问题不复存在。\n解决方法：\n首先就是考虑将整个聊天室网站改成纯Flash版本，这样就不需要弹出窗口，聊天室直接就在主页，当关闭了聊天室窗口也就关闭了所有当前网站的相关窗口。不过由于上面实验发现的原因，即使是关闭了聊天室网站的窗口，如果没有关闭其他带有Flash的窗口，这个问题仍然存在，因此放弃这个思路。\n又考虑到弹出窗口的时候弹出一个没有任何关闭按钮的窗口，在Flash中提供一个关闭按钮，这个关闭按钮在关闭聊天室窗口的同时也关闭NC对象。不过目前的浏览器种类繁多，不能保证用户使用的就一定是IE浏览器。\n最后考虑到，窗口在关闭的时候会触发一个JavaScript的onUnLoad事件，为何不在这个事件中通知Flash关闭NC连接？按照这个思路更改程序，解决了此问题。\n","date":"2006-02-07","description":"","lastmod":"2006-02-07T03:29:43Z","slug":"nc-break","tags":["fcs","fms","netconnection"],"title":"关闭聊天室窗口NetConnection不断开的问题及其解决","url":"https://blog.zengrong.net/post/nc-break/"},{"categories":["impressions"],"content":"The furthest distance in the world\nIs not between life and death\nBut when I stand in front of you\nYet you don’t know that\nI love you\nThe furthest distance in the world\nIs not when I stand in font of you\nYet you can’t see my love\nBut when undoubtedly knowing the love from both\nYet cannot\nBe togehter\nThe furthest distance in the world\nIs not being apart while being in love\nBut when plainly can not resist the yearning\nYet pretending\nYou have never been in my heart\nThe furthest distance in the world\nIs not\nBut using one’s indifferent heart\nTo dig an uncrossable river\nFor the one who loves you\n世界上最遥远的距离\n世界上最遥远的距离，\n不是生与死，\n而是我就站在你面前你\n却不知道我爱你。\n世界上最遥远的距离，\n不是我就站在你面前\n你却不知道我爱你，\n而是明明知道彼此相爱\n却不能在一起。\n世界上最遥远的距离，\n不是明明知道彼此相爱\n却不能在一起，\n而是明明无法抵挡这种思念，\n却还得故意装做丝毫没有把你放在心里。\n世界上最遥远的距离，\n不是明明无法抵挡这种思念\n却还得故意装做丝毫没有把你放在心里。\n而是用自己冷漠的心对爱你的人，\n掘了一条无法跨越的沟渠。\n","date":"2006-01-23","description":"","lastmod":"2006-01-23T16:29:27Z","slug":"the-furthest-distance-in-the-world","tags":[],"title":"世界上最遥远的距离","url":"https://blog.zengrong.net/post/the-furthest-distance-in-the-world/"},{"categories":["technology"],"content":"在话说ColorMatrixFilter一文中，我粗浅的研究了ColorMatrixFilter的使用，并说到它另我失望的话。\n事实证明，这个ColorMaxtrixFilter确实就是我要找的，可是它让我失望。\n而看了Using Matrices for Transformations, Color Adjustments, and Convolution Effects in Flash一文后，我这种失望情绪一扫而光，文中详细介绍了Maxtrix类的用法，并提供了一个将亮度、饱和度、对比度和色调值转为响应矩阵的Demo程序，值得好好研究。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n","date":"2006-01-13","description":"","lastmod":"2006-01-13T01:52:40Z","slug":"colormatrixfilter2","tags":["actionscript","color","filter"],"title":"话说ColorMatrixFilter(2)","url":"https://blog.zengrong.net/post/colormatrixfilter2/"},{"categories":["technology"],"content":"今天去Macromedia网站下载了Flash lite2.0，很可惜的是，它并不支持我的西门子SX1，QD等手机也应该不会支持，而7610等symbian7.0系统手机则可以安装。\n顺便说一句：\nAdobe Reader LE for symbian官方下载地址：\nhttp://www.adobe.com/products/acrobat/readerforsymbian.html[](http://www.adobe.com/products/acrobat/readerforsymbian.html)\n","date":"2006-01-06","description":"","lastmod":"2006-01-06T02:23:07Z","slug":"flashlite2symbian","tags":["flash","mobile"],"title":"Flash lite 2.0 不支持SX1","url":"https://blog.zengrong.net/post/flashlite2symbian/"},{"categories":["technology"],"content":"有下面一段代码：\n1var arr:Array = [1,2,3,4]; 2for(var i in arr){ 3\ttrace(i); 4\t_root.attachMovie(\u0026#34;mc\u0026#34;,\u0026#34;mc\u0026#34;+i, i); 5} 执行会返回下面的错误：\n错误 场景=场景 1, 图层=图层 1, 帧=1:第 4 行: 类型不匹配。 _root.attachMovie(\u0026quot;mc\u0026quot;,\u0026quot;mc\u0026quot;+i, i); ActionScript 错误总数:1 报错:1\n我开始百思不得其解，是什么类型不匹配？如果注释attachMovie一行，则不会出现错误。i值正常。 接着使用下面的代码测试：\n1var arr:Array = [1,2,3,4]; 2for(var i in arr){ 3\ttrace(typeof i); 4\t//_root.attachMovie(\u0026#34;mc\u0026#34;,\u0026#34;mc\u0026#34;+i, i); 5} 发现i的数据类型居然是String！难怪flash会报错，因为attachMovie方法的第三个参数Depth数据类型必须是Number。\n","date":"2006-01-05","description":"","lastmod":"2006-01-05T13:00:26Z","slug":"for-in-setp","tags":["actionscript"],"title":"for...in循环的步进值类型是String","url":"https://blog.zengrong.net/post/for-in-setp/"},{"categories":["technology"],"content":"我们知道，使用trace()语句只能在flash IDE环境下看到调试信息，而很多时候需要在实际使用的环境中进行调试。怎样让trace()中的内容能够显示在实际使用环境中呢？\n可以使用的方法有许多，例如利用LocalConnection类，或者fscommand，都可以在swf运行期间显示调试信息。我也使用过类似的工具，例如DebugIT,Debug和Flash Remoting自带的NetConnection Debugger等等，但还是认为flash 7 trace viewer是最好用的一个。(本站下载)\nflash 7 trace viewer使用非常简单，下载之后运行它，程序会显示在任务栏中，当调试的swf碰到trace()时，右下角便会弹出调试信息。\n虽然程序名叫flash 7 trace viewer，但据我测试，flash 8也同样适用。\n惟一需要注意的是，必须使用Debug版本的flash Player才可以。\n关于此工具的原理请看Danger、luar和Pawaca的介绍。\n","date":"2005-11-30","description":"","lastmod":"2005-11-30T06:38:36Z","slug":"f7debug","tags":["actionscript","trace"],"title":"非IDE环境显示trace()内容","url":"https://blog.zengrong.net/post/f7debug/"},{"categories":["news"],"content":"服务器已经搬迁到上海，前段时间不能下载文件不完整的问题可以解决了。国内访问网站的速度有大幅度的提升。\n","date":"2005-11-27","description":"","lastmod":"2005-11-27T15:02:04Z","slug":"sitemove","tags":[],"title":"服务器搬迁成功","url":"https://blog.zengrong.net/post/sitemove/"},{"categories":["technology"],"content":" 2011-09-09更新：ColorMatrixFilter色彩矩阵滤镜 2011-01-24更新：smithfox提供了一个更详细的介绍，同时提供了自己实现的AdjustColorFilter。 2006-01-13更新：话说ColorMatrixFilter(2) 很欣慰的在 Flash 8 的ActionScript2.0语言参考中找到 GradientGlowFilter（渐变发光滤镜）和GradientBevelFilter（渐变斜角滤镜）时，我想我应该离镜效果中的“调整颜色”滤镜所代表的Class不远了。可是，我并没有找到我预想中的那个 “AdjustColorFilter” （或者类似的名字）Class，却发现了一个 “ColorMatrixFilter” 。\n事实证明，这个ColorMaxtrixFilter确实就是我要找的，可是它让我失望。\n在“调整颜色滤镜”中，将影片剪辑的颜色按照“亮度、对比度、饱和度和色相”进行调整，这个功能大大增强了Flash的处理能力。在Flash8 以前，我只能使用影片剪辑实例的“高级”颜色属性对影片剪辑进行变色处理。但是这样的处理有个最大的弊端－－不能保证被调整对象的饱和度、对比度或者亮度不发生改变。这样的功能让人无法满意，不得以只能借助图像处理软件。而现在的“调整颜色滤镜”，终于可以让Photoshop歇歇了。\n可惜的是，ColorMaxtrixFilter 却没有使用“调整颜色滤镜”的这种使用“亮度、对比度、饱和度、色相”来标明数值的方式，而是要使用矩阵！\nColorMatrixFilter 类使您可以将 4 x 5 矩阵转换应用于输入图像上的每个像素的 RGBA 颜色和 Alpha 值，以产生具有一组新的 RGBA 颜色和 Alpha值的结果。该类允许饱和度更改、色相旋转、亮度为 Alpha以及各种其它效果。可以将此滤镜应用于位图和 MovieClip 实例。\n将使用下列公式，其中 a[0] 到 a[19] 对应于 20 个元素的数组属性矩阵中的条目 0 到 19。\n1redResult = a[0] * srcR + a[1] * srcG + a[2] * srcB + a[3] * srcA + a[4] 2greenResult = a[5] * srcR + a[6] * srcG + a[7] * srcB + a[8] * srcA + a[9] 3blueResult = a[10] * srcR + a[11] * srcG + a[12] * srcB + a[13] * srcA + a[14] 4alphaResult = a[15] * srcR + a[16] * srcG + a[17] * srcB + a[18] * srcA + a[19] 这有点匪夷所思。我不得不承认这种方式确实更加灵活和高效，但为什么不能简单一点？而现在，我得到一个色相为-80的“调整颜色滤镜”效果，该怎么做？\n我只能来进行反推了。先在IDE环境中给一个影片剪辑实例“testMc”加上“调整颜色”滤镜，并将其值设置成我需要的，然后使用下面的代码：\ntestMC.filters[0].matrix; 反推的过程中，找到一点小小的规律（ 这里有大大的规律 ）。其实，亮度是最简单的，矩阵的其他值不变，只需要改动a[4]、a[9]、a[14]、a[19]即可，值的范围是-100到100,正好于滤镜中相同。而其他三个就麻烦些了。\n","date":"2005-11-25","description":"","lastmod":"2005-11-25T16:05:48Z","slug":"colormatrixfilter","tags":["actionscript","color","filter"],"title":"话说ColorMatrixFilter","url":"https://blog.zengrong.net/post/colormatrixfilter/"},{"categories":["web"],"content":"客串一下UNIX命令，只是为了用Telnet\n[转贴]chmod命令详细用法\n指令名称 : chmod\n使用权限 : 所有使用者\n使用方式 : chmod [-cfvR] [--help] [--version] mode file...\n说明 : Linux/Unix 的文件调用权限分为三级 : 文件拥有者、群组、其他。利用 chmod 可以藉以控制文件如何被他人所调用。\n参数 :\nmode : 权限设定字串，格式如下 : [ugoa...][[+-=][rwxX]...][,...]，其中\nu 表示该文件的拥有者，g 表示与该文件的拥有者属于同一个群体(group)者，o 表示其他以外的人，a 表示这三者皆是。\n+ 表示增加权限、- 表示取消权限、= 表示唯一设定权限。\nr 表示可读取，w 表示可写入，x 表示可执行，X 表示只有当该文件是个子目录或者该文件已经被设定过为可执行。\n-c : 若该文件权限确实已经更改，才显示其更改动作\n-f : 若该文件权限无法被更改也不要显示错误讯息\n-v : 显示权限变更的详细资料\n-R : 对目前目录下的所有文件与子目录进行相同的权限变更(即以递回的方式逐个变更)\n--help : 显示辅助说明\n--version : 显示版本\n范例 :\n将文件 file1.txt 设为所有人皆可读取 :\nchmod ugo+r file1.txt\n将文件 file1.txt 设为所有人皆可读取 :\nchmod a+r file1.txt\n将文件 file1.txt 与 file2.txt 设为该文件拥有者与其所属同一个群体者可写入，但其他以外的人则不可写入 :\nchmod ug+w,o-w file1.txt file2.txt\n将 ex1.py 设定为只有该文件拥有者可以执行 :\nchmod u+x ex1.py\n将目前目录下的所有文件与子目录皆设为任何人可读取 :\nchmod -R a+r *\n此外chmod也可以用数字来表示权限如 chmod 777 file 语法为：chmod abc file ，其中a,b,c各为一个数字，分别表示User、Group、及Other的权限。\nr=4，w=2，x=1\n若要rwx属性则4+2+1=7；\n若要rw-属性则4+2=6；\n若要r-x属性则4+1=7。\n范例：\nchmod a=rwx file 和 chmod 777 file 效果相同\nchmod ug=rwx,o=x file 和　chmod 771 file 效果相同\n若用chmod 4755 filename可使此程序具有root的权限\n","date":"2005-11-25","description":"","lastmod":"2005-11-25T15:34:57Z","slug":"unixchmod","tags":[],"title":"【转】chmod的用法","url":"https://blog.zengrong.net/post/unixchmod/"},{"categories":["technology"],"content":"在使用Flash MX 2004和Flash Communication Server开发程序的过程中，为了便于调试，通常会在客户端程序中加上这么一句：\n#include \u0026quot;NetDebug.as\u0026quot; 然后配合NetConnection Debugger，就可以随时看到调试信息。可是，将程序转到Flash 8之后，这招却不灵了，即使我已经安装了Flash Remoting Components for Flash 8。\n找到Flash安装文件夹下的“language\\First Run\\Classes\\mx”，发现remoting的文件一个不少，那为什么不能include呢？\n原来，用于include的“NetDebug.as”与“language\\First Run\\Classes\\mx\\remoting\\debug”中的类并非同一个，前者其实在“language\\First Run\\Include”目录中，与后者同名。使用#include \u0026quot;NetDebug.as\u0026quot;指令调用的则是前者。\n而Flash 8的Include目录中并没有“NetDebug.as”，要使用原来的方法，还要将Flash mx 2004的Include目录中的内容复制过来：\n原以为这样就可以高枕无忧，却发现在Class中，好像不能直接使用Netdebug.trace()方法，编译时报错。但是同样的程序，在Flash MX 2004却没有问题。\n**错误** L:\\work\\......\\view\\child\\ActorList.as: 第 51 行: 没有名为'NetDebug'的方法。 NetDebug.trace(\u0026quot;data:\u0026quot;+temp.data); 于是在Class中加入下面的代码，一切恢复正常。\nclass view.child.ActorList extends MovieClip{ ...... private var NetDebug; function ActorList(){ NetDebug = _global.NetDebug; } ...... ","date":"2005-11-24","description":"","lastmod":"2005-11-24T05:02:07Z","slug":"flash8netdebug","tags":["actionscript","flash"],"title":"flash8下的#include NetDebug.as","url":"https://blog.zengrong.net/post/flash8netdebug/"},{"categories":["news"],"content":"这是“google分析”的统计，Flash Player 8的普及率比我预想的要高。\n","date":"2005-11-23","description":"","lastmod":"2005-11-23T01:24:06Z","slug":"flashplayerversion","tags":["flashplayer"],"title":"访问者Flash Player版本统计","url":"https://blog.zengrong.net/post/flashplayerversion/"},{"categories":["others"],"content":"这是是一定要转的\n这几天在卖本本，帖子连接在此。\n今天早上突然有人打电话过来说要我的本本，我看了下手机，没有来电显示，于是就接了起来，对方就问了我一下机器的成色，配件什么的，就让我砍个价，我说玩后他小砍100，我也觉得可以接受，于是就基本谈下来了，然后他说交易方式，我说先款或...，还没等我说完他就说taobao什么的他不是很熟，要先款也可以，但让我给他身份证号码，我想了一下，就是拿去了他也没什么什么用，就给他了，后来就很莫名其妙的打电话说问我要QQ号码，我正好也有个小号，就让他试了下，心想估计他没有QQ，然后上网和我聊一下本本的具体情况，搞笑的是他说密码不对，他登不上去，于是让我改一下密码，我改了再告诉他，他说还是登不上去，再让我改一下，还要是改6位的，还说什么改的太容易了怕被盗(真郁闷了，我都不怕他帮我瞎担心什么啊) 就这样一连让我改了好几次，最后一个电话说有事情先出去下，晚点再联系。\n到了晚上无聊了，在论坛上看看帖子，突然发现一个兄弟的遭遇和我非常相似，于是仔细的看完了楼下的回贴，这才发现今天是碰到PZ了，他的帖子连接在此。\n于是马上穿好衣服拼了命的冲到ATM机上把农行里的钱全部取出，没什么意外，但到了工行就感觉不对了，输入密码进去，到查询余额那个版块后就进不去了，上面显示的大概意思就是一天输入的错误密码次数超过限额，让我明天拿身份证去银行重新设定密码，也就是说1天内最少有输入3次密码错误，但是很奇怪的是：我的卡不离身啊，再说网上银行的密码和ATM的密码也是2回事，就算他知道我ATM的密码，不把卡放到ATM机上试也不会出现密码被锁(至少3次密码输入错误)的现象啊，看来拿钱出来是没戏了，还好回家还能登陆网上银行，钱没被冻结，马上转走。仔细分析了下，有点头绪，但也有事情至今没想通\n事后分析：\n前面不是提了PZ要你给他个QQ嘛，还让你反复的改密码，说什么自己登不上去，还指定要6位的密码(ATM机上就是6位的)，后来仔细想想很有可能就是想让你在无意见透露了自己的银行卡密码，毕竟改个密码也总得让自己好记点，可好记敏感的数字也就那么几个，银行卡的密码很有可能就无意中掺杂在里面了。\n一开始PZ问你要身份证号码估计是要注册网银，就工行这个系统虽然方便，但漏洞很多，不需要安全证书，也就是说你在任何地方只要知道登陆和支付密码就可以网上划帐，这给PZ可乘之机，只要你还没注册网银，只要他有你的身份证和名字，通过你的身份证上的出生年月，猜出你的卡号密码，但这种几率也不是很大，于是就有了上面所说的QQ密码修改泄密法，结合这2点来猜密码命中率就非常高，一但攻破完全可以“代替”你注册，那时候你里面的米就全归他管了，想想实在太可怕了，工行的具体流程就是这样：\n登入www.ICBC.COM.CN--自助注册--输入卡号--就会出现:\n注册卡密码------PZ集中攻破点\n证件类型 身份证 ..... 其它\n证件号码\n保密信息\n网上银行密码\n请再输入一遍\n请输入验证码\n以上也是我能想到的PZ的一种手段，但是还有点想不通的是，他不知道我工行的登陆密码，又怎么能修改我的资料和密码什么的呢，还有就是他拿不到我的银行卡，怎么能在ATM机里搞出个当天输入至少3次的错误密码提示呢\n对方还说自己是福清人，由于没有来电号码，也没办法打过去，于是就登陆了www.zj.chinamobile.com查询了即时话单，移动的网今天还贼慢，搞了十几分钟，终于把他的号码找到了，全号为0791-2907160\n马上再登陆了www.google.com查询了0791一下这个号段，是江西南昌的，在好奇的想知道google的强大功能外，试了下全号07912907160查询，竟然查到了一个网友被骗传奇装备的帖子，google连接入下,搜出来就一个帖子，大家可以看一下。\n按照里面的楼主说的，PZ的资料应该就是：\n骗子的固定电话号码是:07912907160\n银行帐号是:1502206601200825986\n胡小燕\n江西南昌\n由于号码为同一个，很有可能是一人所为 不知道这种事情公安受理不？要是能上去把他揪出来那可是大块人心啊\n从12点多写到现在，不为别的，就是想让兄弟多留个心眼，不能掉以轻心啊，防人之心不可无啊，这帖子不能沉，我会PM更多都来看看的，谨防受骗\n","date":"2005-11-21","description":"","lastmod":"2005-11-21T07:01:55Z","slug":"pz1","tags":[],"title":"转一个现在出现的新骗局，希望大家都能注意防范了","url":"https://blog.zengrong.net/post/pz1/"},{"categories":["technology"],"content":"Flash 8新增的On2 vp6编码当然需要更新的播放器来支持。原来的FLV播放器都不能再使用了，在网上下载了几个又都不好用。突然想到，如果用flash.net.FileReference类，再配合最新的FLV Playback组件，就能很容易就能做出一个flv播放器来。\n注意：此播放器必须下载到本地使用，需要Flash Player 8\n使用方法：\n如果没有Flash Player 8，先下载：SAFlashPlayer.rar； 下载播放器\n下载后请保持压缩包中的ClearOverAll.swf和flvplayer.swf处于同一个文件夹，然后用Flash Player 8打开flvplayer.swf； 单击播放器右上角的三角箭头，弹出对话框选择一个flv视频文件进行播放。 下载源文件：flvplayer.rar\n","date":"2005-11-18","description":"","lastmod":"2005-11-18T13:27:19Z","slug":"flvplayer","tags":["flash","flv"],"title":"Flash8自制FLV Player","url":"https://blog.zengrong.net/post/flvplayer/"},{"categories":["impressions"],"content":"把电信的宽带包年费用交了，居然意外获赠一中国电信专用版的瑞星杀毒软件。仔细看看说明书，原来只能够免费升级一年的，并且必须和互联星空账号绑定。想想当初买的瑞星2002，到现在都还能顺利升级。不知道中国电信此举对用户来说是否真的有益，倒不如帮我把带宽提到2MB好了，我宁愿多交一个正版瑞星的钱…… :P\nPS：多普达575的Camera效果还不错\n","date":"2005-11-17","description":"","lastmod":"2005-11-17T08:18:47Z","slug":"chinatelecomrising","tags":[],"title":"免费的正版瑞星","url":"https://blog.zengrong.net/post/chinatelecomrising/"},{"categories":["news"],"content":" 试用版下载 组件下载 Bing的评论 luar的评论 ","date":"2005-11-15","description":"","lastmod":"2005-11-15T15:32:21Z","slug":"fms-published","tags":["fms","server"],"title":"Flash Media Server 2正式发布","url":"https://blog.zengrong.net/post/fms-published/"},{"categories":["news"],"content":"这个更新的级别被Macromedia列为important。\n更新下载 更新说明 ","date":"2005-11-15","description":"","lastmod":"2005-11-15T14:28:29Z","slug":"fcsfix3","tags":["fcs"],"title":"Flash Communication Server Updater Release 3","url":"https://blog.zengrong.net/post/fcsfix3/"},{"categories":["news"],"content":"继Google maps 之后，Yahoo！也推出了自己的地图服务Beta版。与Google不同的是，Yahoo!的地图服务采用Flash技术制作。\nhttp://maps.yahoo.com/beta/\n","date":"2005-11-04","description":"","lastmod":"2005-11-04T01:11:22Z","slug":"yahoomap","tags":["flash"],"title":"Yahoo的Flash地图服务","url":"https://blog.zengrong.net/post/yahoomap/"},{"categories":["news"],"content":"http://www.osflash.org/red5\nFlash Communication Server以外的选择，基于JAVA环境，遵循GNU协议。\n","date":"2005-11-02","description":"","lastmod":"2005-11-02T02:28:59Z","slug":"red5","tags":["fcs","server"],"title":"Red5","url":"https://blog.zengrong.net/post/red5/"},{"categories":[],"content":"Flash＆Flex大全\n本文不再更新，详见： Goodbye, Flash!\n目录\n[TOC]\n更新历史 2010年11月29日更新：修改服务器软件部分，加入SmartFoxServer、QuickServer、Cindy、MINA的介绍。 2010年11月24日更新：修改服务器软件部分，加入Wowza、ErlyVideo的介绍，修改Red5的介绍。 2010年9月25日更新：修改编译与反编译器、加密与混淆器部分。 2010年9月18日更新：加入调试器。 2010年9月4日更新：删除数个无效的explorer。 2010年8月27日更新：加入数个Flash游戏、物理、3D引擎和UI组件，加入1个Flash开发框架。 2010年8月25日更新：继续删除和修正链接，合并类别，加入了Flash游戏引擎，增加官方在线中文帮助。 2010年8月24日更新：删除和修正了许多链接，修改了介绍，合并了类别，加入了Flash物理引擎类别。 2011年5月15日更新：在UI组件部分，加入几个轻量级纯AS组件的介绍。 2011年5月13日更新：在游戏引擎部分，修过多个引擎的介绍。 2011年4月6日更新：在游戏引擎部分，加入Flixel Power Tools等几个引擎的介绍。 2011年3月3日更新：在服务器部分，加入Openfire的介绍。 2011年1月30日更新：在混淆器部分，加入C Preprocessor for ActionScript的介绍。 2011年1月7日更新：修改调试器部分，加入Kap Inspect的介绍。 2012年2月25日更新：加入As3-Bloom的介绍。 2012年3月12日更新：在服务器部分，加入CRTMPServer和Mammoth Server的介绍。 2013年9月10日更新：文章采用markdown重新排版，便于编辑；删除部分无效内容；从游戏引擎中拆分出2.5D引擎；在UI组件部分加入flexlite和morn UI。 2013年11月28日更新：加入Nape引擎。 官方在线帮助（没标英文的都是中文） 用于 Adobe Flash Platform 的 ActionScript 3.0 参考 更多参考 使这样的链接下载离线版：http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/standalone.zip 用于 Adobe Flash Professional CS5 的ActionScript 3.0 参考 使用 Flash Professional CS5 使用 Flash Builder 4 Adobe Flex 4.1 语言参考 ActionScript 3.0 开发人员指南（这个一定要仔细看完） 使用Flex 4（英文） Flex 的 Adobe AIR 开发人员中心-快速入门 Flash Media Server 4在线文档（英文） Flash 3D(FlashPlayer11加入原生3D后的更多资料) Minko native3D 来自\n比其他3d引擎动辄几百k，无位图资源时，只有3k+的native3d引擎在做一些轻量级的网页效果时，它甚至比five3d的体积还要小，确实有它一定的优势。 Away3d http://code.google.com/p/away3d/ http://away3d.com/ Sandy\nActionScript 3D引擎，有AS2、AS3、haXe版 Alternativa 3D ASCOLLADA\n可以解析Collada文件格式的AS3类库，Collada 是一个开放原始码的专案,它可让3D资料以XML的型态储存,并让3D人员可以轻易携带和互换资料 Five3D Papervision3D（很久未更新，不支持新的3DAPI，zrong不推荐使用） http://blog.davr.org/pv3d-examples/ 官方网站：http://www.papervision3d.org/ 博客：http://blog.papervision3d.org 下载页面：http://code.google.com/p/papervision3d/downloads/list Flash物理引擎 Nape Physics Engine\n一个Haxe实现的物理引擎，可以用于AS3和Haxe。开源、免费，可用于商业项目。在AS3中使用时，提供一个swc库。 APE (Aionscript Physics Engine) The Fisix Engine\n不开源，但文档和示例比较齐全，zrong也用的就是这个 Box2DFlashAS3\n脱自C++引擎，强大且复杂 FOAM Rigid Body Physics Engine Motor JigLib 3D物理引擎 WOW Engine 3D物理引擎 Flash游戏引擎(参考来源：1|2) PixelBlitz Engine这个引擎提供位图管理、碰撞检测、像素字体、游戏相关数学计算、键盘和鼠标管理等等功能。但它是一个未完成版本，它的开发者现在都去开发Flixel Power Tools了。 flixel\n这是一个全位图引擎。所谓全位图，就是游戏场景中所有元件最终均绘制在一个位图对象上，在游戏过程中每帧进行重绘。也正因为如此，此引擎非常擅长处理同屏同时出现大量的游戏元件，其高效的渲染会让你激动不已。当你需要创建2D卷轴游戏或者场景中需要大量运动元素的游戏，Flixel引擎是你的首选。 特性： 采用QuadTree的对象链，高效的碰撞检测 位图动画Sprite 通过文本及图片创建Tiles地图 简单易用的粒子系统 高效的滚屏 自定义的鼠标光标 方便的debug显示终端 典型案例： Canabalt该游戏在作者的网站上每月会消耗2.5T的流量，可见流行的程度。游戏中高速流畅的滚屏会让你惊叹Flash的效率。此游戏还移植到iOS上，并在App Store中销量不菲。 Omega Crisis 这个塔防类游戏，画面、游戏性与操作性都相当不错。 用此引擎的游戏展示 简单介绍|功能列表|中文教程 Flixel Power Tools 在Flixel的基础上加入了一些工具。 Bold Pixel engine 以copyPixels方法写的位图引擎。里面实现了缓存BitmapData贴图管理等优化，不过没有对不在显示区域的对象进行过滤，以及其它优化。 FlashPunk（要翻墙才能访问）\nFlashPunk同样是个针对位图的开源引擎。它具有清晰的框架以及创建游戏需要的动画、碰撞等类库，让开发者更专注与游戏的设计与测试中。 其主要特性包括： 相对独立与固定的帧频时间步长控制 像素、矩形区及网格的碰撞检测 运动tweening sorted的渲染列表，方便深度排序 粒子系统 典型案例： Tiny Hawk类似超级玛丽，不过这次你脚下踩着的是滑板，一共32关。 Mr. Fat Snake贪吃蛇的横轴飞速版。：） 更多采用此引擎的游戏展示 pushbuttonengine\n简单介绍，Pushbutton引擎的特色有很多，包括建立游戏的模块片段，而不是整体式的应用；使用第三方类库更加容易；提供核心功能比如资源管理器、日志管理、调试检测、序列号、时间管理、全局命名对象等等；相关的组件包括基于Box2D的物理引擎、Rendering2D引擎、游戏常用的健康值组件、团队组件、状态机组件、贴图系统、路径查找类库、基本的网络联机和通讯服务； 将来会提供编辑器，以及网络联机组件，这两个组件都是收费组件； 该游戏引擎的官方网站还列出了组件商店的介绍，作为该组织出售组件和广大开发者出售组件的场所，这不失为一个好的商业模式。典型案例： Social City这个在Facebook上月活跃用户超过一千万的游戏，采用了PushButton引擎。 The Incredible Machine Mega Pack不可思议的机器系列想必大家不陌生，这个版本的近400兆大小的单机游戏也出自该引擎。 mecheye-as3-libraries\n作者可能已经放弃开发了，zrong不推荐使用 2.5D引擎 下面是一些2.5D游戏引擎。所谓2.5D，我们也称之为Isometric，也就是游戏视角采取倾斜视角（如斜45度角等），以平面的方式展现固定视角的3D效果。目前很多网页游戏均采用2.5D的方式。需要注意的是，前面为大家介绍的位图2D引擎同样可以用来开发2.5D游戏。运用这些引擎，你可以把一些烦人的2.5D相关的坐标转换交给引擎处理，专注在你的游戏逻辑及设计上。\nPixas是一个开源ActionScript引擎，它能够使程序员利用纯粹的Actionscript3来构建等距像素Flash应用程序。利用Pixas你可以很容易的将等距像素元素，比如块、立方体、锥体和图层，添加到你的应用程序中。 AS3 ISO LIB\nAs3isolib是一个基于ActionScript 3的 Isometric库，开发者运用它可以方便的开发2.5D的游戏或应用。 主要特性： 简易的2.5D场景创建方式 方便的于各种缓动（tween）引擎交互 增强的2.5D元件深度排序 场景显示渲染的性能优化 典型案例： Zex Lex DuelFacebook上的一个机器对战小游戏。 Down TownFacebook上的虚拟城市交友。 另外，还有开发者将这个2.5D的库制作成 PushButton引擎的一个组件 OpenSpace\nOpenSpace是一个非常不错的引擎，用户可以非常快速方便的创建2.5D游戏。配合该公司的另外一款通信服务器SmartFoxServer，可以搭建多人实时交互的虚拟场景。 其特点包括： 完善的地图编辑方式 可缩放的场景 自定义地图滚屏方式 自定义的游戏角色 地图自动寻径 典型案例： The Settlers–My City殖民者的网页版，你可以创建属于自己的殖民国 。 Petpet Park很可爱的宠物公园虚拟社区。 更多的案例展示 TheoWorlds\nTheoWorlds 除了包含Iso引擎之外，还包含聊天、地图编辑器等组件，可以帮助开发者快速的开发2.5D的虚拟世界。 主要特性有： 8方向的运动角色 自定义角色形象 自定义角色动作 快速寻径及自动滚屏 与SmartFox Server及ElectroServer等第三方即时通信服务器通信 聊天历史、表情图标等 相关演示： 场景演示 地图编辑器演示 FFlimation\n这个项目的主要目的是提供一个稳定的开发平台，这样游戏设计师就可以忘记游戏渲染引擎把精力集中在游戏内容的细节方面。从“关卡制作”的角度来看，这个引擎的可用性非常的高。 Citrus Engine\nCitrus 引擎是一种基于as3和box2d的flash滚屏平台游戏引擎。Cirus引擎能让设计师和开发者非常快速的容易的创建滚屏平台游戏（又叫横版过关游戏）象超级玛丽。团队可以用citrus引擎给游戏门户制作广告游戏，市场推广游戏，搏逸游戏等等。 Yogurt3D\nYogurt3d的核心部分，swiftgl，是开源并且与opengl兼容。这意味着有opengl开发经验的开发者很容易的就可以开发出3d flash游戏和应用程序。他还可以轻易的将opengl代码转化成swiftgl并在flashplayer中运行。 UI组件与布局管理 flex\n原本是Adobe的商品，后来被捐献给Apache。 flex lite FlexLite是一个为游戏而生的开源轻量级UI框架，旨在为游戏开发提供一个更加高效的UI工作流。 FlexLite Studio是针对FlexLite框架开发的所见即所得的可视化UI编辑软件，与传统纯AS游戏项目无缝集成。 Morn UI 轻量级，可视化，高性能，易扩展的flash UI解决方案 Morn UI库以精简，直观为设计理念，代码轻量，能快速上手，减少学习成本，Morn UI全部库总大小不到30K Morn UI提供强大的可视化编辑器，布局及属性均可在编辑器直观设置，实现UI和逻辑分离，让美术和程序轻松合作 Morn UI设计之初就以高性能为主要目标，以位图为基础，利用延迟渲染机制，实现了高性能 无论UI组件还是编辑器插件，都非常易于扩展，编辑器支持即改即用，轻松实现个性化，甚至使用自己的UI库 Flash UI Component 基于Flash CS3的UI组件，可用于纯ActionScript项目。 AsWing\nAsWing是一套UI组件框架，纯ActionScript开发的组件框架（有ActionScript2和ActionScript3版本），也包含一些常用的工具类，目的是让Flash/Flex开发人员方便的开发出想要的应用程序界面。另外AsWing还提供SkinBuilder和GuiBuilder工具用于制作Skin和可视化编辑生成界面。目前AsWing团队专注于ActionScript3版本的开发和维护。AsWing以 BSD协议发布，不管你是商业还是非商业，都可以自由免费使用. zrong用过一段时间AsWing。看完AsWing的架构才发现，Flex4的spark组件引以为傲大肆宣传的layout，其实AsWing早就这么做了。 但zrong在使用AsWing开发的时候，碰到过许多莫名其妙的问题，找不到什么解决方案，目前已经放弃。 Gfl\n一个轻量级的基于纯AS的独立组件库，可以使用CSS语法。感谢smithfox的推荐。 Minimal Comps\n一套小巧可爱的纯AS组件，除AsWing外的又一选择。zrong发现纯AS的UI组件并不多，貌似除了AsWing也就只有这套了。中文使用说明 As3-Bloom\nAs-Bloom 是为开发者提供的一个轻量级用户界面。简要介绍其特性： 主题编辑器 边缘布局系统 画刷皮肤系统，轻松改变组件风格 类结构更为清晰，易于初学者上手 保持短小精悍的文件尺寸，内存占用低 Skinnable Minimal Components\nMinimalComps的官方版本是不支持皮肤的，而这个就是它支持皮肤的版本。 razor components\n一套支持皮肤的纯AS组件。 MadCommponents\n一套轻量级的纯AS组件，适合用在移动设备上。 AS3Flobile\n这一套也是比较轻量级的 Base UI\n纯AS实现的布局框架，功能很全，配合Minimal Comps再好不过了。快速查看布局效果 miniui 这是一个开源的flash ui 框架。支持主流框架的skin和layout等功能，但是体积却非常小。 FlexLib\n一套包含很多FLEX高级组件的开源类库。包含这些组件：AdvancedForm, Base64Image,EnhancedButtonSkin, CanvasButton, ConvertibleTreeList, Draggable，Slider, Fire, Highlighter, HorizontalAxisDataSelector ImageMap,PromptingTextInput, Scrollable Menu Controls, SuperTabNavigator,Alternative Scrolling Canvases, Horizontal Accordion, TreeGrid,FlowBox, Docking ToolBar 。 FlexMDI\n是一个在Flex中轻松创建多窗口（MDI）的一个框架，提供了很多功能，包括拖拽，最大化，最小化，各种效果等。\n现在FlexMDI已经整合进入FlexLib组件，成为其中的一个包flexlib.mdi MDIManager介绍 flexmdi中的效果 vancura-AS3-libs\n提供纯AS3组件的皮肤和样式的集合。支持Scale9Bitmap BrowserCanvas\n提供容易的方式动态修改Flash尺寸大小 senocular Layout class\n除了布局工具，还有其他许多有用的工具 Yahoo ASTRA: ActionScript Toolkit for Rich Applications\n这是Yahoo开发的一套RIA组件包，包含以下内容 Flash Components Flex Components Utilities Library 包含动画工具Animation Utility和布局工具 Layout Utility 还有几个这里就不介绍了，大家自己看 EnFlash 仅支持AS2 XMCA 仅支持AS2 BIT Component Set 商业组件 $99 GhostWire Components 商业组件 标准版$149 精简版$99 Tween ByteTween TweenLite(TweenMax) TweensyZero gTween AS3 Animation System Go KitchenSync Twease Tweener Tweensy Yahoo ASTRA Animation Utility asinmotion ActionScript3.0 API swfupload 类库 如果想对SWF中的动态文本应用非系统的字体，方法当然是在本身的SWF中嵌入相应的字体，另外一种方法是把字体嵌入到另外的SWF中，当需要对应的字体时，把这个SWF载入，并引用相应的字体。FontLoader是一个字体载入类，它帮助你实现这个过程。 CASALib CASA库是为了简化一些通用的编码而设计，包含collection、display、layout、math、time、load、transitions等包，也有对Tween的实现。 as3corelib 用于AS3开发的一套类库，里面有很多很有用的东西。例如MD5,SHA1加密方法，图片格式转换类（将图片转为位JPG,PNG等格式）还有JSON序 列化等等有用的东西。 FlexUnit Syndication library as3awss3lib ActionScript 3 Amazon S3库 as3soundeditorlib Actionscript 3声音编辑库 as3ds AS3数据结构库，适用于游戏开发 As3Crypto ActionScript 3 加密库 ebay API facebook-as3 在伟大的中国基本上是用不到了 FZip使用AS3解压zip文件 lastfm-as3 Last.fm是一个音乐网站，这个库让你可以存取Last.fm公开的数据 MapQuest Popforge AS3 audio library allows you to create a valid flash.media.Sound object with your own samples Salesforce Flex Toolkit Twitter AS3 API XIFFXMPP client library Yahoo AS3 APIs 这个上面也介绍过，可以参考上面的介绍 Flare Visualization Toolkit Flare 是一个用来做Data Visualization的 AS3 类库，可以用来实现图表，动画效果等 Adobe官方开源站点 Yahoo maps 的AS3组件 Graffiti Library-ActionScript 3 Bitmap Drawing Library Graffiti 是一个AS3库，可以让你方便地在Flex/Flash/AIR中使用画图功能。 OpenRIA提供的开源Flex/AS3项目 Degrafa开源的图形框架 ActionScript编辑器 Flash Builder Adobe官方提供的编辑器，没什么好说的。 InteliJ IDEA\n据说是最好的JAVA IDE，Google的AndroidStudio也基于它开发。只有收费版才支持Flex和AS3开发。 FDT FDT是Flash Development Tool 的简称，是非常优秀的ActionScript编辑器。与FlashBuilder相同，它也是基于Eclipse开发。它支持高级的代码自动完成功能，具有强大的实时错误检测和除错功能，可以导入Flash的帮助文件，实现同Flash一样方便的帮助信息等等。有免费版。 FlashDevelop 小巧免费快速的AS编辑器，支持Flex和AIR开发，基于.NET，启动快速，免费。zrong在用这个。 SEPY ActionScript Editor 强大的开源AS编辑器，使用python开发。最近一次更新是在2007年2月10日，估计没戏了。 调试器 来自 评测 Kap Inspect如果你没用过spy工具，你可曾想实时监控swf application的的所有事件？ 你可曾想查看swf有没有内存泄漏问题？你可曾想看看到底DisplayObject tree是什么样的？你可曾想查看所有控件的属性，甚至在运行时改一下？来自 ThunderBolt 是个面向ActionScript 2和3的Firebug轻量级记录器扩展，无法使用Firebug的AIR程序，ThunderBolt有ThunderBolt AS3 Console可以使用。 Arthropod 是个面向Flash和AIR开发的调试工具。其易用性非常好，下载后直接就可以使用， 开发者可以在运行期轻松调试应用。 Alcon是面向ActionScript开发者的一个轻量级调试工具，提供直接且快捷的方法来调试任何ActionScript 2或ActionScript 3应 用，无论这些ActionScript是来自于Web浏览器、独立的Flash Player还是AIR运行时都没有问题。 De MonsterDebugger 是个面向Flash、Flex及AIR项目的开源、轻量级的调试器，功能完善，完全使用Adobe AIR开发。 reflexutil是个Flex调试工具,可以在运行时时实改变控件的属性。 Flex Explorer Flex3 Component Explorer Felx2 Component Explorer Style Explorer Style Explorer with Kuler Import Charting Explorer Filter Explorer Style Creator Enhanced Button Skin Explorer Kuler Flex开发框架 Cairngorm\n是为方便FLEX开发企业级应用而开发的一个微架构。假如项目比较复杂，需要3个开发员以上来共同开发，Cairngorm是一个最正统的选择（官方推 荐），虽然开始时有点难学。而做小型项目或项目是由你自己一个人开发的话，那就用PureMVC吧。 不过即使开发不使用它，也可以参考它的源码，毕竟Iteration:two的大量企业级应用的design patterns还是很值得学习的。 PureMVC zrong就用这个 ARP MVCS Flest Model-Glue:Flex ServerBox Foundry Guasax Slide Luke Bayes Ali Mills SomaUI 编译与反编译器 部分转自 硕思闪客精灵（商业软件） imperator（商业软件） Action Script Viewer（商业软件） Flasm（自由软件）反编译swf成字节码（bytecode），将修改的字节码再编译成swf。理论上可以反编译任何加密方式的swf，用汇编语言来写ACTION SCRIPT，FLASM能帮你将SWF里面的AS转换成汇编语言，然后你要做的是优化这些代码，最后交由FLASM再把他转回SWF，FLASM的语法与汇编类似，但只能支持到Flash 8。 Flare（自由软件）Flare是一个免费的swf反编译器. 目前最高只支持Flash MX 2004 和Flash 8。 swfparser（开源软件）一个 Java 编写的简单的用来反编译 swf 的工具，只支持到Flash 8。 swftools是一个方便，易于使用的实用程序收集专门设计，使您与Adobe的Flash文件（SWF文件）工作变得更容易，目前支持Windows和Linux。 PDF2SWF是一个PDF格式到SWF格式转换器。每页生成一帧。使你有完整的格式化文本，包括表格，在你的Flash电影上。它基于PDF格式的解析器。结合FlexPaper可以实现类似Baidu文库/豆丁网的Flash文档阅读器，不过要达到上面两种一样应用还需要不少改进 SWFCombine工具插入一个对pdf2swf转成文件的显示封装。 （模板）例如见，包括在一些浏览SWF的排序pdf2swf。 SWFString搜索出SWF里的文本数据。 SWFDump列出有关swf文件里的各种信息如：Sprite， Shape， String等。 JPEG2SWF添加一个或多个JPEG图片，并产生一个SWF幻灯片。 PNG2SWF 同JPEG2SWF相似，支持png格式。 GIF2SWF转换的GIF到SWF。还能够处理GIF动画。 WAV2SWF WAV音频文件转换为SWF文件，使用的LAME MP3编码器库。 AVI2SWF的AVI动画文件转换为SWF。它支持Flash MX中的H.263压缩。有些例子可以找到examples.html。 Font2SWF转换字体成为SWF文件。 SWFBBox允许调整SWF的封装Viwer。 SWFC的工具，从简单的脚本文件创建的SWF文件。 SWFExtract允许提取影片剪辑，声音，图像等从SWF文件。 RFXSWF 一个功能齐全的Flash库，可用于独立的SWF。包括位图，按钮，形状，文字，字体，声音等的支持，也为ActionScript支持使用明ActionCompiler。 AS3Compile ActionScript 3.0编译器，与官方的Flex SDK 里的mxmlc相比功能很少，你可以输入as3compile –help查看参数 Ming可以用来生成swf文件,包括在swf文件内增加图片,声音,视频等素材,也可以在文件内增加代码,使用滤镜.可以使用php,perl,python,ruby,java生成swf文件,php5安装的时候自带,php5帮助里面用完整的函数说明。 swftophp - SWF to PHP converter makefdb - Font Definition Ripper listfdb - List Font Definition listjpeg – List JPEGs listswf - SWF Disassembler listaction – Actions Script Disassembler png2dbl - PNG convert gif2dbl - GIF converter gif2mask – GIF Mask extractor raw2adpcm - Audio Converter Swfmill是一个功能可靠使用方便的命令行工具，可以使用SWFML实现的xml和swf之间的转换，还可以利用xslt生成swf文件，也是FAMES生成SWF密不可分的一部分。SWFML是一种在SWF文件格式制定后制定的XML语言。 Nemo 440（免费软件）AIR编写的ActionScript 3/ABC2/Flex 2/Flex 3/Flex 4/AIR反编译器，并不能还原成AS文件，只是反编译成类似字节码的代码。 swfdump和swfutils.jar，包含在Flex4 SDK中，swfdump调用swfutils.jar工作，将swf编译成字节码。可以看看这篇文章的介绍。 加密与混淆器 C Preprocessor for ActionScript开源的处理AS源码的混淆器，简单的介绍 asdec Flashincrypt Swf Encrypt Flash Encryption Genius irrfuscator（商业软件）一个AS3源码混淆器。 外壳 mprojector swfKit swfkit打包方面的一些问题可以看这里 ZINC 视频 FLV MetaData Injector Riva FLV Encoder FLVtool2 VH Screen Capture Driver\n免费的抓屏驱动，可以配合Flash Communication Server实现屏幕共享 H.264 MPEG AVC Video Codec comparison Flash Video比特率估算 服务器软件 C++ RTMP Server(crtmpserver/rtmpd)\n一个C++实现的媒体服务器，支持RTMP,RTMPE, RTMPS, RTMPT, RTMPTE协议和易懂设备，支持MPEG-TS/RTSP/RTCP/RTP协议。 Mammoth Server\n也是一个C++实现的支持RTMP协议的流媒体服务器。 Red5\n使用Java编写的开源软件，可以用来替代Flash Media Server（原Flash Communication Server） Wowza Media Server\n商业软件，又一个FMS替代品，除了RTMP外，还支持多种协议和多种客户端（Silverlight、QuickTime等等） ErlyVideo一个使用Erlang语言编写的FMS替代品，支持HTTP MPEG-TS流、RTMP流和IPhone流。 SmartFoxServer\n商业软件。它是专门为Adobe Flash设计的跨平台socket服务器，让开发者高效地开发多人应用及游戏。服务器端可以使用Actionscript, Javascript, Python和Java语言进行扩展。自带数据库和HTTP服务器引擎。\n中文介绍 中文文档 Openfire\n使用Java开发聊天和IM服务器，实现了XMPP协议。据说Google Wave的协议也是基于它的，底层使用Apache MINA（下面有介绍）。 QuickServer 它是一个免费的开源Java库，用于快速创建健壮的多线程、多客户端TCP服务器应用程序。使用QuickServer，用户可以只集中处理应用程序的逻辑/协议。 中文开发指南 MINA\nApache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目，它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发、串口通讯程序（只在最新的预览版中提供），MINA 所支持的功能也在进一步的扩展中。 Cindy\nCindy是一个强壮，可扩展，高效的异步I/O框架。支持TCP,SSL-TCP, UDP和Pipe。 OneTeam Media Server又一个使用Erlang语言编写的开源FMS替代品 来自 支持流式播放实时或已录制好的媒体内容 支持录制实时内容 支持AS3 SharedObject共享对象 支持Clustering集群 支持用Erlang/OTP编写应用程序模块 TightVNC\n并非Flash专用，提供远程控制服务\n参见FlashVNC vnc2swf\n将VNC的内容保存成SWF 开发 FlashTextEditor\n一个基于Flash的在线编辑器，内建文件器，非常有趣和强大。但对中文支持不够好。 Flash Text Formatter\n基于Flash的语法着色器，支持ActionScript、PHP、JavaScript和Python语法 swfmill\nswf2xml和xml2swf mtasc\n编译为swf SWFObject（原名FlashObject）\n将swf嵌入到网页中的JavaScript脚本 * SWFObject的用法 基于SWFObject的Flash发布模版 Xray (Flash Debugger) 应用 FlashTextArea Flash Remoting webORB\n包含.NET、JAVA、PHP和 Ruby on Rails版本的Remoting。 FluorineFx\nFlash Remoting for .NET，开源 Zend AMF\nZend出品，算是官方支持了。Flash Builder自带了这套框架。 OpenAMF on sourceforge\nJAVA Flash Remoting rubyamf\nRubyAMF is an open source flash remoting gateway for rails. It plugs?directly into your controllers with render :amf. amfphp（不推荐）\nFlash Remoting for PHP，开源 ActionScript 1.0/2.0 AS2 Libiary Flash prototype functions\nDownload all prototype functions in?PDF format ActionScript Class AS2 to AS3 ","date":"2005-11-02","description":"","lastmod":"2005-11-02T02:27:07Z","slug":"flashassistant","tags":[],"title":"Flash＆Flex大全","url":"https://blog.zengrong.net/flashassistant/"},{"categories":["others"],"content":"1、平结\n平结为最多男士选用的领结打法之一，几乎适用于各种材质的领带。要诀：领结下方所形成的凹洞需让两边均匀且对称。\n2、交叉结\n这是对于单色素雅质料且较薄领带适合选用的领结，对于喜欢展现流行感的男士不妨多加使用“交叉结”。\n3、双环结\n一条质地细致的领带再搭配上双环结颇能营造时尚感，适合年轻的上班族选用。该领结完成的特色就是第一圈会稍露出于第二圈之外，可别刻意给盖住了。\n4、 温莎结\n温莎结适合用于宽领型的衬衫，该领结应多往横向发展。应避免材质过厚的领带，领结也勿打得过大。\n5、双交叉结\n这样的领结很容易让人有种高雅且隆重的感觉，适合正式之活动场合选用。该领结应多运用在素色且丝质领带上，若搭配大翻领的衬衫不但适合且有种尊贵感。\n6、亚伯特王子结\n适用於浪漫扣领及尖领系列衬衫，搭配浪漫质料柔软的细款领带。正确打法是在宽边先预留较长的空间，并在绕第二圈时尽量贴合在一起。\n7、四手结(单结)\n是所有领结中最容易上手的，适用於各种款式的浪漫系列衬衫及领带。\n8、浪漫结\n浪漫是一种完美的结型，故适合用於各种浪漫系列的领口及衬衫。完成後将领结下方之宽边压以绉摺可缩小其结型，窄边亦可将它往左右移动使其小部份出现於宽边领带旁。\n9、简式结(马车夫结)\n适用於质料较厚的领带，最适合打在标准式及扣式领口之衬衫。将其宽边以180度由上往下翻转，并将折叠处隐藏於後方待完成後可再调整其领带长度。\n10、十字结(半温莎结)\n此款结型十分优雅及罕见，其打法亦较复杂。使用细款领带较容易上手，最适合搭配在浪漫的尖领及标准式领口系列衬。\n","date":"2005-11-01","description":"","lastmod":"2005-11-01T04:04:23Z","slug":"tie","tags":[],"title":"【转】打领带全教程","url":"https://blog.zengrong.net/post/tie/"},{"categories":["web"],"content":"原文地址\nRFC812定义了一个非常简单的Internet信息查询协议——WHOIS协议。其基本内容是，先向服务器的TCP端口43建立一个连接，发送查询关键字并加上回车换行，然后接收服务器的查询结果。\n世界上各级Internet管理机构秉承公开、公正、共享的原则，设立了可以查知IP地址和域名所有者登记资料的WHOIS服务器，以便所有Internet的使用者排除故障、打击网上非法活动。全世界国际区域性的IP地址管理机构有四个：ARIN、RIPE、APNIC、LACNIC，他们负责的IP地址的地理区域如下图所示。\n四个国际区域性IP地址管理机构所负责的区域\n(此图摘自《RIPE 2002年度报告》)\n重要的Internet管理机构和常用的WHOIS服务器\n机构缩写 WHOIS服务器地址 机构全名及地点 提供查询内容 CERNIC whois.edu.cn 中国教育与科研计算机网网络信息中心，(清华大学·中国北京) 中国教育网内的IP地址和.edu.cn域名信息 CNNIC whois.cnnic.net.cn 中国互联网络信息中心，(中国科学院计算机网络信息中心·中国北京) .cn域名(除.edu.cn)信息 INTERNIC whois.internic.net 互联网络信息中心 ， (美国洛杉矶市Marina del Rey镇) .com,.net,.org,.biz,.info,.name 域名的注册信息(只给出注册代理公司) ARIN whois.arin.net 美国Internet号码注册中心 ， (美国弗吉尼亚州Chantilly市) 全世界早期网络及现在的美国、加拿大、撒哈拉沙漠以南非洲的IP地址信息 APNIC whois.apnic.net 亚洲与太平洋地区网络信息中心 ，(澳大利亚昆士兰州密尔顿镇) 东亚(包括中国大陆和台湾)、南亚、大洋洲IP地址注信息 RIPE whois.ripe.net 欧州IP地址注册中心(荷兰阿姆斯特丹) ","date":"2005-10-31","description":"","lastmod":"2005-10-31T06:18:25Z","slug":"whois","tags":["netconnection","http"],"title":"【转】IP地址信息查询(WHOIS)服务的原理","url":"https://blog.zengrong.net/post/whois/"},{"categories":["web"],"content":"一个2000-2005年RIA网站的大集合：http://www.thefwa.com/\nhttp://www.rie.com.cn http://appleshow.cc/ http://new.iceberry.co.kr/main/main.php RIAcn Sherwin-willams:Color visualizer Mediatuner结合了聚合Rss和podcast等功能的RIA应用,需要注册. flash8音频贺卡多用户音乐播放器需要注册 RIA地图-Interactive Maps 在线人才资源网-crew9 http://www.collab.nl http://web.sunjpg.com/-Style http://www.ego7.com/-Flash CMS? 中国闪客发展组织Flash的RIA应用-配电系统用户名和密码都是guest http://mci.name/petzonlinestore/main.html 以下这些出自luar的RIA网站收集报告 Financial Content Australia Vote Federal Election Google News Map Restaurant Finder Iokio Camerafinder Canon PowerComparision Tools Userplane Major League Baseball Amazon Search ClockLink 以下来自阿修的部落格 NameVoyagerJava applet RIA... 冬季戀歌FlashLite動態桌布產生器 BackBase Art Pad flickr需要注册. Cell Phone Finder When She Hot 自制广告片 GeoClip 地图RIA 數位塗鴉－The Scribbler Cymbal Configurator給鼓手配置銅鈸用的??... NID Gallery Shop Composition AIGA Design Archives Habitat Perspectives 個人雪板製造機 fotologue日本的全Flash相簿網站 澳洲大選開票結果RIA 10x10每個小時會把世界上發生的大事彙整，以100個詞、100個影像呈現出來. wallop http://mywallop.com/ system在线试衣 Vancouver Culture Guide溫哥華互動地圖 howies Audi Certified pre-owned Aanbodmanager－房屋中介 Nike iD: Make It Personal San Emeterio Macromedia的RIA范例 紐約證券交易所的RIA NYSE MarkeTrac imagevuex Iokio Camerafinder NIKERunning.com PHP for Flash The Broadmoor Pearle Vision - Interactive Frame Selector MINIUSA Nikon Digital Buyer Guide introNetworks shaker: flash-based presentation builder ","date":"2005-10-27","description":"","lastmod":"2005-10-27T05:53:28Z","slug":"riasites","tags":[],"title":"RIA站点收集","url":"https://blog.zengrong.net/post/riasites/"},{"categories":["web"],"content":"我喜爱的ColdFusion，终于进入了TIOBE 程序语言使用排行榜的前20位！希望ColdFusion在中国能成为主流。\n","date":"2005-10-27","description":"","lastmod":"2005-10-27T03:54:18Z","slug":"coldfusionintiobe","tags":[],"title":"ColdFusion的TIOBE 排名进入前20位","url":"https://blog.zengrong.net/post/coldfusionintiobe/"},{"categories":["design"],"content":"作者：∣来源：博客园综合区∣原文地址∣2005-10-19\n以下内容为转载：\n现在网页设计方面的站点越来越多，究竟哪个才是经典；根据网页设计中牵涉到的：网页制作，平面设计，动画制作，素材安排等，我特地找了些好的站点，发表在这里，如果大家有好的站点，也希望能一起交流。\n一、网页设计类\n1、 网站名称：有风的日子\n网站地址：http://www.windstudio.net/\n简介：\n很不错的一个设计网站，我刚开始学习网页设计的时候就知道她了，她最有特色的内容就算是精品插件栏目了，里面包括有Photoshop 滤镜、Dreamweaver 插件、Flash 相关，都是很实用的好东东。同时网站的符号字体栏目也挺有自己的特色。\n2、 网站名称：蓝色理想\n网站地址：http://www.blueidea.com/\n简介：\n网页编辑方面的大哥大人物，网站关于网页制作方面的教程既全面又专业，而且各类教程的更新速度很快。她的经典论坛的人气也是相当的旺。同时该网站的一个很大的特点就是会时不时推出一些好书的介绍，这些都是不容易买到的。\n3、 网站名称：七色鸟设计\n网站地址：http://www.colorbird.com/\n简介：\n七色鸟设计是一个致力于传播设计文化，研究视觉设计艺术、提高大众审美意识的非盈利性的艺术指导网站，里面的设计资源相当丰富而且有特色\n4、 网站名称：视觉中国\n网站地址：http://www.chinavisual.com/\n简介：\n这个是我最爱的一个网站了，我第一次把网页制作和网页设计区分开来，就是通过看了这个网站上的很多文章得出来的，里面的很多知识以平面为主，以前她的服务器不怎么好，经常出现服务器负载过重的情况,但是现在已经好了。\n5、 网站名称：5D多媒体\n网站地址：http://www.5dmedia.com/\n简介：\n5D的含义是指其探讨五个方面的内容：声音、图象、动画、多媒体交互、Internet网开发。该网站是目前国内最大的多媒体技术站点，要想了解多媒体方面的信息还真的是要进去看看。\n6、 网站名称：设计联盟\n网站地址：http://www.szdesign.org/\n简介：\n我非常喜欢她的作品栏目，可以看到很多人的创作，欣赏别人的创作的同时，自己也可以得到很多的启示\n7、 网站名称：点燃灵感\n网站地址：http://www.fwcn.com/\n简介：\n这里是Firework的天堂，教程，插件，资源，应有尽有\n二、图象处理类\n1、 网站名称：中国PHOTOSHOP联盟\n网站地址：http://www.photoshopcn.com/\n简介：\n网页设计中的平面处理软件，PS是绝对的老大，中国PHOTOSHOP联盟就是PS教学方面的老大\n2、 网站名称：图象谷\n网站地址：http://pstxg.com/\n简介：\n搞图象处理的好去处，她的资源下载栏目很出色\n三、动画设计类\n1、 网站名称：闪客帝国\n网站地址：http://www.flashempire.com/\n简介：\n我很早以前做的第一个个人网站就是仿照该网站做的，呵呵，我记得那个时候的帝国还不是现在这个样子。里面关于Flash的教程都很经典。而且论坛里面是高手如云。\n2、 网站名称：闪吧\n网站地址：http://www.flash8.net/\n简介：\n我第一次知道这个网站是通过电脑爱好者介绍的，具体哪期忘记了，我记得有整整一个版面来介绍这个网站的故事，所以就跑来了，一看就喜欢上了，觉得里面的资料真是太丰富了，和帝国难分高下。\n3、 网站名称：数码动力国际\n网站地址：http://www.showgood.com/\n简介：\n该网站已经不需要我们再说，她推出的Flash实在是太棒了！\n4、 网站名称：闪盟在线\n网站地址：http://www.flashsun.com/\n简介：\n网站的优秀以不需多说，我很喜欢她的竞技场栏目\n四、网页素材类\n1、 网站名称：桌面城市\n网站地址：http://www.deskcity.com/\n简介：\n一流的图象资料宝库\n2、 网站名称：素材精品屋\n网站地址：http://www.sucaiw.com/\n简介：\n也是网页设计素材库，她和桌面城市主要在于一个偏全，一个偏精！\n五、网页代码类\n1、 网站名称：代码中国\n网站地址：http://www.codechina.net\n简介：\n一个专业、全面的源代码下载网站。简洁实用的页面，专业详尽的分类，丰富全面的代码资源，合理的得体的技术运用，处处透露出专业性。无论哪一方面的代码，在这里几乎都找得到，而且在代码社区中，你可以发布一些求助信息，讨论一些技术话题，让你充分体会到网络的互动性。\n2、 网站名称：天新网源码下载\n网站地址：http://www.21tx.com/src\n简介：\n天新网是一个较早的个人网站，也是提供源码下载较早的一个网站。分类较为详尽：首先按语言（或技术）分成大类，每一大类再按代码的用途分为小类，这样，使用起来方便快捷。\n3、 网站名称：波士源码下载\n网站地址：http://www.boss8u8.com/scdown\n简介：\n主要提供WEB后台程序代码下载。尽管包含的类别有限，代码的丰富程度比不上前两个网站，但是它分类很详细，几乎包含了目前网站上常见的所有程序代码，如果你正在寻找一些WEB后台程序代码，这应该是个好去处。\n六、WEB技术类\n1、 网站名称：goEway.com\n网站地址：http://www.goeway.com\n简介：\n“从不懂得上网，到网络高手”，这是goEway.com的口号。当然，口号就是口号，重要的是，从这个站合理的栏目设置、人性化的设计、丰富的内容、大量实用的文章来看，他们在切切实实地这样做着。\n2、 网站名称：HTML.CN\n网站地址：http://www.htmlcn.com\n简介：\n不用我多说，从站点和网址你就知道这是一个什么类型的网站了。不过，这里可不仅仅有HTML。还有网页制作软件。CSS、JavaScript以及ASP、PHP等等，可以说从网页制作到web编程都涉及到了。尽管它的内容还不能说十分丰富，但都很实用。\n3、 网站名称：流媒体中国技术频道\n网站地址：http://www.liumeiti.com/technology\n简介：\n流媒体指在Internet/Intranet中使用流式传输技术的连续时基媒体（如relsystem.MPEG等）。顾名思义，流媒体中国就是一个以“流媒体”为主题的网站，在它的技术频道中，有大量的相关文章。对于从事或有心从事这方面技术的网友来说，绝对是一个不可多得的好去处。另外，有什么疑问和心得，你还可以去“流氏论坛”去讨论。\n4、 网站名称：虚拟无忌\n网站地址：http://www.86vr.com\n简介：\n虚拟现实（简称VR）技术虽然出现已经有几年，但一直没有普及起来。随着宽带的普及以及它自身特有的魅力，将来一定会有发展。虚拟现实的方方面面在这都可以找到。\n七、加盟网站类\n做网页的人都希望把自己的网站推销出去，推销的方法有很多，通过加盟品网网站就是个不错的方法，在那里你推销自己的网站，通过品网的评价后，发布出来，这样可以让更多的人知道你的网站。下面这些就是我找的几个在国内比较有名的品网网站,网站的评价我就没写了，因为性质都差不多。\n1\u0026gt;　网站名称：零刻联盟\n网站地址：http://www.linkmeng.com\n2\u0026gt;　网站名称：贵族品网\n网站地址：http://www.21pick.com/index.asp\n3\u0026gt;　网站名称：汕头网页设计师联盟\n网站地址：http://www.0754design.com\n4\u0026gt;　网站名称：浪掏沙\n网站地址：http://www.langtaosha.net/pick/index.asp\n5\u0026gt;　网站名称：中原品网\n网站地址：http://www.zpick.com/pick\n6\u0026gt;　网站名称：文化空间\n网站地址：http://www.xiaoxiang.net/web\n7\u0026gt;　网站名称：94cool\n网站地址：http://94cool.org/meng/default.asp\n8\u0026gt;　网站名称：星期天网站联盟社区\n网站地址：http://www.7sunday.com/\n9\u0026gt;　网站名称：蚁盟\n网站地址：http://www.yimeng.org/\n10\u0026gt; 网站名称：中国网页设计师联盟\n网站地址：http://www.dreamofdesign.com/chinese/\n八、FLASH站点\n随着macromedia公司推出FLASH这款动画制作软件后，FLASH动画疯狂般的在网络蔓延开来，无论是在大型的商业网站，还是小规模的个人网站，到处都有它的身影。而关于FLASH教学的站点也越来越多，今天我就推荐给大家一些不错的不错的FLASH教程和设计网站。\n1\u0026gt;　网站名称：闪客帝国\n网站地址：http://www.flashempire.com/\n简介：\n说这里是国内最具人气的flash专业技术站点，一点也不过分。这里的\u0026quot;闪客交流中心\u0026quot;每天都吸引了大量的爱好者前来讨论有关flash的问题。该站的主要栏目有\u0026quot;闪客演练场\u0026quot;\u0026quot;闪客资源\u0026quot;\u0026quot;原始代码\u0026quot;\u0026quot;在线教程\u0026quot;\u0026quot;闪客交流中心\u0026quot;等。\n该站另一个卖点就是他成立了\u0026quot;中国闪客大联盟\u0026quot;，团结了一批闪客高手，大大提高网站在国内的影响力。\n我认为\u0026quot;闪客帝国\u0026quot;应利用其知名度高的优点，组建一个社区。这样不但能满足爱好者的要求，还能增加网站的浏览人数。\n2\u0026gt;　网站名称：Flashsun\n网站地址：http://www.flashsun.com/\n简介：\nflashsun中的内容包罗万象。有flash问答，flash教学，档案下载，flash游戏，flash咨询等。是一个名副其实的flash技术综合网站。当然内容丰富是该网站受欢迎的主要原因，在档案下载栏目里有大量的声音文件，flash源文件让用户下载，为广大flash爱好者提供制作素材。\n3\u0026gt;　网站名称：杰克资讯站\n网站地址：http://tacocity.com.tw/flash\n简介：\n近期，台海局势变得紧张起来，仿佛战争将要展开。然而这并没有影响到两岸的flash技术交流。这期为大家介绍一个元老级的flash网站，是台湾同胞 Rock Jack制作的“杰克资讯站－flash”。我相信闪客同志们一定看过他写的flash教程吧！该教程已成为中文flash教程的典范。内地许多flash教学网站都会用到这套教程。至于网站的设计便显得平淡一点，只是站长的名气和资历为该站带来不少的访问量。可能是jack同志忙于处理台海问题，不能抽空管理网页吧，很长一段时间不见更新了。而且网站里的教程只是面向初学者，没有照顾flash进阶者和高手的需要，这些是站长要改善的地方。\n4\u0026gt;　网站名称：Flash8\n网站地址：http://www.flash8.net/\n简介：\nflash8是一个大型的综合性flash技术网站，凡是与flash有关的资讯均能在这里找到。其中有关flash 3D效果制作的内容最吸引人。在网页中详细介绍了3D效果的制作，以及相关软件的应用和技巧等。Flash8的主要栏目有“flash动画”－提供源文件的下载和制作教程，“flash资源”－字体资源·声音资源等，“flash论坛”，“flash教程”，“flash评论文章”。其信息量大，遍及面广，是同类型网站无法比拟的。\n5\u0026gt;　网站名称：we're here\n网站地址：http://www.were-here.com/\n简介：\n这次介绍的又是一个综合性flash技术网站，但we're here与以往的综合型网站相比，其最大的特点是他的论坛。据非官方统计，we're here的论坛是人气最鼎盛的flash技术论坛。英文水平高的flash爱好者应到这里看看，你会收益不少。\n6\u0026gt;　网站名称：Break out\n网站地址：http://www.breakout.com/\n简介：\n为什么要介绍 Break out 这个网站？是因为他的制作技术高明，音效震撼人心。想知道有多高明，多震撼，欣赏一下就知道。\n7\u0026gt;　网站名称：Cruddog\n网站地址：http://www.cruddog.com/\n简介：\n不错的一个教学站点。版面的设计充满创意，令你佩服设计者的制作手段。站内链接许多相关的站点，为flash爱好者提供大量的资源。\n8\u0026gt;　网站名称：matinee\n网站地址：http://www.matinee.co.uk/start.htm\n简介：\n你一定会被matinee的制作技术吸引住，逼真的3D效果令人叹为观止。没有制作灵感吗，matinee有你所需要的。在那里你会有意想不到的收获。\n9\u0026gt;　网站名称：shockwave\n网站地址：http://www.shockwave.com/\n简介：\n这里可说是flash及director的大本营。站内有许多卡通动画，精彩的线上音乐和游戏，还有flash贺卡，作为flash的老家，你一定不会错过吧！\n10\u0026gt; 网站名称：EYE4U\n网站地址：http://www.eye4u.com/home/index.html\n简介：\n非常有名的flash酷站。简单的图形变化加上充满动感的音效，让人叹为观止。\n11\u0026gt; 网站名称：LDG\n网站地址：http://www.ldg.be/\n简介：\n一个很cool的音效资源站，他的风格与Break out十分相似。充满动感的音效让人热血沸腾。既然是音效资源站，当然有大量的音效下载。我强烈推荐国人学习老外的先进技术。\n12\u0026gt; 网站名称：macromedia 中国官方站\n网站地址：http://www.macromediachina.com/\n简介：\nmacromedia的中国官方站，主要介绍macromedia产品的最新动态。有新版flash；dreamweaver ultradev等新软件的介绍。另外还有技术支持，教学等栏目，内容也算不错，十分适合不精通E文的macromedia的用户。\n13\u0026gt; 网站名称：lightningaudio\n网站地址：http://www.lightningaudio.com/\n简介：\n相信“The X files”这部电视剧你一定看过吧，不少观众被剧中离奇古怪的案件所吸引，这次推荐的网站就是介绍这部电视剧的。网页中有剧中人物的介绍，剧情介绍，音乐欣赏等，“The X files”的fans一定不要错失良机。\n14\u0026gt; 网站名称：flashcool\n网站地址：http://www.macromediachina.com/\n简介：\n一个十分充实的flash资源站，有大量flash的源文件，素材，动画文件等资源下载；还有许多精美的flash贺卡提供给网友观赏呢。\n15\u0026gt; 网站名称：searchbots\n网站地址：http://www.searchbots.net/\n简介：\n搜索引擎你应该很熟悉吧，但这个flash制作的搜索器你用过了么？网页以火箭为主题，象征全球搜索。如果你喜欢与众不同的搜索引擎，searchbots是不错的选择。\n","date":"2005-10-20","description":"","lastmod":"2005-10-20T13:58:04Z","slug":"webdesignsites","tags":[],"title":"【转】网页设计者值得一去的地方！","url":"https://blog.zengrong.net/post/webdesignsites/"},{"categories":["technology"],"content":" Programming ActionScript 3 ActionScript 2.0 to ActionScript 3.0 Migration AS3语言参考 ActionScript 3 From Macromedia Labs Actionscript 3 - First Steps Brief overview of AS3 htspectrum code PNG Encoder in AS3 Actionscript 3 - New Capabilities ","date":"2005-10-19","description":"","lastmod":"2005-10-19T01:30:15Z","slug":"as3","tags":["as3"],"title":"ActionScript 3.0的一些资料","url":"https://blog.zengrong.net/post/as3/"},{"categories":["others"],"content":"Blog转换到WordPress平台后，就一直在研究WordPress的强大功能，不知什么原因，wordPress的知识库却一直无法访问。换了n个地方上网，又找到网友帮忙，都是无法连接。后来去代理中国找了n*2个代理服务器，终于能够访问了，原来是电信在作怪。\n没有办法，谁叫我没得选择呢？免费代理服务器的生命总是短暂的让人失望，还好今天碰到了Google Secure Access。用了之后感觉不错，原来不能上的网站能上了；在Macromedia下载软件的速度也加快了－－下载Flash 8试用版从4KB/s增到了32KB/s……\n不过，除了原帖作者所说，我感觉在使用中还有几点要注意的地方。\n如果安装了防火墙软件，在连接前一定要关掉。因为根据我的测试，如果防火墙开着是肯定无法连接成功的（我使用瑞星个人防火墙2005）。当然，连接成功之后，就可以再打开防火墙了。\n注意：此处的防火墙是指网络防火墙而非病毒防火墙。 连接成功之后，好像VeryCD.com就无法访问。虽然这个软件是发布在VeryCD.com社区中的。 ;) 安全性要求较高的操作（例如网上银行的使用等等）建议还是不要使用此软件。 软件下载\n本文转自VeryCD.com 分享互联网社区 \u0026gt; 网络资源 \u0026gt; eMule资源自由发布台\n原文名称：[分享]突破一切网络封锁Google Secure Access v1.0绿色汉化版, AIR FORCE精选小软件系列\n原文地址：http://bbs.verycd.com/topics/234216/\n原文作者信息：\nAIR FORCE [顶楼] 2005-10-5, 08:02 AM\nVF-1S\n组别: 会员\n现金: 763 CD\n发贴数量: 4075\n精华贴数: 14\n注册时间: 2004-5-11\n来自: Fairy星球--FAF部队--第5战术侦察小队--编号503[雪风]\n用户编号: 155825\n以下内容为引用文字\nGoogle刚推出了一个小巧的VPN软件Google Secure Access，安装后Google Secure Access 将会帮助你连接到一个Google的VPN(虚拟私人网络)服务器上，你的电脑和Google的VPN服务器这间传输的数据是加密的，以此来达到增强安全性的目的。安装后，会在右下角出现一个绿色小图标，只要在小图标上用鼠标点击右键，在出现的菜单上点\u0026quot;Connect now\u0026quot;,你就可以链接到Google的VPN服务器上了，这时你可以到测试IP的网站上测一下，你会发现你的IP竟然在美国，乐了吧，进入Google网站后就可以随便搜索了，再也不能有什么该死限制了，还可以达到隐IP的效果，数据全程加密，非常安全，还能做什么，自己去试试。\n软件的原理就在于原理就是Google 设立了VPN（虚拟私有网－－或虚拟专用网）服务器，客户可以连到他们的服务器上，众多客户组成一个局域网（虚拟的，在因特网上建立）,原理如下：\n软件实际是一个VPN拨号管理器。打开“网络与拨号连接”会看到软件加入了VPN拨号项“VPN.google.com”，实际这个才是主要的东西，软件只是管理这个的拨号。在屏幕右下角系统托盘区有一灰色图标，鼠标右键单击可以看到菜单。\n注意默认“Connect Automatically”是选上的，就是自动管理拨号。测试时这个好像不太好使，很长时间也不拨号。可以点击取消自动拨号，每次选“Connect Now”手动拨号，这个拨号好像需要点时间，和那个破网软件差不多。拨号后系统托盘区多了一个拨号指示器（就是那个两台计算机形象的图标 ）。\n拨号后一切网络通信都被VPN程序截取（嗅探软件观测，没有与除VPN服务器IP外的其它出站通信－－入站通信是有的，很少，但得不到答复）。那么你在计算机里使用任何软件就完全在那个虚拟网络里活动了，真正对外的是VPN服务器，泄漏的也是服务器IP，不会泄漏本地公网IP。－－特殊方式也可能泄漏本地IP吧，但这个一般不用考虑，应该很特殊情况才能发生。\n那么通过它浏览网页、聊天、下载等等任何网络操作都公开的是VPN服务器的IP－－就像是在单位局域网里操作，任何外面留下的IP都是局域网共享的一个IP地址，找不到具体是哪个人。注意DNS解析都不是在本地的，因为一是任何网络通信都被截取到VPN服务器了，二是你查看那个VPN拨号的“状态”，可以看到它分配到的内网IP（如192.168.252.3），还可以看到所用的DNS服务器地址。\n拨号成功、失败如下图指示：\n如果失败了重新拨号即可。\n此软件在测试中，本人也是尽可能的弄清楚，有很多东西还是不懂的或是以前没接触的，所以这种方法安全性尚待验证。\n昨天晚上使用过了，在骡子上传的过程中，突然开始使用Google Secure Access会导致暂时性的上传量下降，上传列表里会全部更换为国外IP，也许是因为有很多外国IP在排队下载我共享的东西吧...上传量会在一段时期后恢复。\n没有试过使用Google Secure Access后在链接骡子会怎样。\n附件里是绿色中文版，解压缩之后即可以使用。\n这个帖子已被 AIR FORCE 于 2005-10-5, 08:06 AM 编辑\n","date":"2005-10-15","description":"","lastmod":"2005-10-15T01:12:11Z","slug":"google-secure-access","tags":["google","proxy"],"title":"突破一切网络封锁Google Secure Access","url":"https://blog.zengrong.net/post/google-secure-access/"},{"categories":["technology"],"content":"先来看下面的这段服务器端代码（so.asc）：\n1application.onAppStart=function(){ 2so = SharedObject.get(\u0026#34;test1\u0026#34;,true); 3so.onSync = function(l){ 4trace(\u0026#34;SO已经更新！\u0026#34;); 5} 6} 7application.onConnect = function(c, appid, cid, p1, p2, p3){ 8application.acceptConnection(c); 9so.setProperty(cid, p1); 10trace(so.size()); 11} 无论我怎样努力，我都无法让so.onSync执行，可是客户端的onSync却工作的很好！我想尽办法，用了2个小时的时间，最终还是失败。直到我在FCS的帮助中找到这段话：\nNote: Changing or deleting a property on the server side using the SharedObject.setProperty() method always succeeds, so there is no notification of these changes.\n看来，仔细研究帮助文件的每句话是非常有帮助的。我一直不知道在服务器端改变或者删除SharedObject属性是不会有通告的。\n那么，如果是proxy SharedObject又会有怎样的情况？下面是一个例子，这个例子由一个客户端、两个服务器端程序组成。客户端的代码如下：\n1var nc = new NetConnection(); 2login_btn.onPress = function() { 3nc.connect(\u0026#34;rtmp:/\u0026#34;+ins0+\u0026#34;/\u0026#34;+ins1, ins1,ins2,ins3); 4nc.onStatus = function(e) { 5_root.info+=newline+e.code+\u0026#34; － \u0026#34;+e.description; 6}; 7so = SharedObject.getRemote(\u0026#34;test1\u0026#34;,nc.uri,true); 8so.connect(nc); 9so.onSync = function(){ 10trace(\u0026#34;SO已经更新！-\u0026#34;+so.data.length); 11} 12}; 其中，ins0、ins1、ins2、和ins3这四个变量分别关联着舞台上的四个输入文本域。而info这个变量则关联着舞台上的一个用户观看调试信息的动态文本域。\n服务器端的程序proxyso中的代码如下（proxyso.acs）：\n1application.onAppStart=function(){ 2cc=this; 3nc = new NetConnection(); 4nc.connect(\u0026#34;rtmp://localhost/so/a\u0026#34;); 5nc.onStatus = function(info){ 6trace(info.code + \u0026#34; － \u0026#34; + info.description); 7} 8proxySO = SharedObject.get(\u0026#34;test1\u0026#34;, true, nc); 9proxySO.onSync = function(l){ 10trace(\u0026#34;代理SO已经更新:\u0026#34;+proxySO.getPropertyNames()); 11} 12proxySO.onStatus = function(info){ 13trace(info.code + \u0026#34; － \u0026#34; + info.description); 14} 15} 16application.onConnect = function(c, appid, cid, p1, p2, p3){ 17application.acceptConnection(c); 18proxySO.setProperty(cid, p1); 19} onAppStart事件中，我建立的SharedObject其实是连接到服务器端程序so中的，它被称为“代理SO（Proxied shared objects）”。在onConnect事件中，对这个“代理SO”，进行了更改，这个更改其实是作用到服务器端名为so的应用程序中的，我想这个代理SO应该可以接收到onSync事件，因为虽然它是位于服务器端，但是对于so这个应用程序来说，它却是一个客户机。\n服务器端的程序so中的代码如下（so.acs）：\n1function getRandom(){ 2return new String(Math.random()*1000).split(\u0026#34;.\u0026#34;)[0]; 3} 4application.onAppStart=function(){ 5proxySO = SharedObject.get(\u0026#34;test1\u0026#34;,true); 6proxySO.onSync = function(l){ 7trace(\u0026#34;proxySO已经更新:\u0026#34;+proxySO.getPropertyNames()); 8proxySO.setProperty(\u0026#34;t\u0026#34;+getRandom(),\u0026#34;bb\u0026#34;); 9localSO.setProperty(\u0026#34;t\u0026#34;+getRandom(),\u0026#34;bb\u0026#34;); 10trace(\u0026#34;localSO\u0026#34;+localSO.getPropertyNames()); 11} 12localSO = SharedObject.get(\u0026#34;test2\u0026#34;,true); 13localSO.onSync = function(l){ 14trace(\u0026#34;localSO已经更新:\u0026#34;+localSO.getPropertyNames()); 15} 16} 17application.onConnect = function(c){ 18application.acceptConnection(c); 19} 为了测试，在appStart中建立了两个SharedObject，proxySO就是被应用程序proxyso调用的代理SO，localSO则是应用程序so建立的本地SO，用于测试服务器端修改本地SO是否会触发onSync事件。\nproxySO.onSync是一定会触发的。因为对它的更改是由proxyso进行的。在onSync触发的时候在本地对proxySO再进行一次更改，然后对localSO也进行一次更改。\n如果判断正确，情况应该是这样：\n在so程序中的proxySO.onSync仅当被外部的客户机修改的时候才会触发，而本地修改的时候不触发。因此，当客户机swf连接的时候只会触发1次。 在so程序中的localSO.onSync不会被触发。 在proxyso程序中的so.onSync则只要test1被修改都会触发，因此，当客户机swf连接的时候，应该触发2次。 客户机swf中的onSync因为是连接到proxyso的共享SO的，因此，当客户机swf连接的时候，应该也会触发2次（事实证明并不是）。 运行程序监测，得出以下结论：\n在服务器端修改或删除服务器程序本地SO不会触发onSync事件 代理SO所在的服务器程序对代理SO修改或删除会触发代理SO的onSync事件；代理SO所连接的本地SO如果被本地程序修改，也会触发代理SO的onSync事件 连接到代理SO的swf客户机中的SO对象的onSync事件仅当代理SO被更改的时候触发，但是能够显示出对代理SO的所有更改 上面的最后一点值得解释一下。在刚才的猜测中，我判断客户机swf中so的onSync会执行两次。但经过试验发现仅为一次，这一次会反映出两次的修改，说明SO确实被修改了两次。至于这一次onSync是谁触发的，我认为是代理SO。\n1 文件 ","date":"2005-10-14","description":"","lastmod":"2005-10-14T05:59:05Z","slug":"sharedobjectonsync","tags":["fms","sharedobject"],"title":"SharedObject.onSync的研究","url":"https://blog.zengrong.net/post/sharedobjectonsync/"},{"categories":["technology"],"content":"在ActionScript中Array和Object的异同一文中，我提到\n在检测Flash的数据类型时发现，Object和Array的数据类型都是“object”。\n可是，怎样得知一个对象是否是Array呢？可以使用下面的代码\n1is_obj={a:1,b:2,c:3}; 2is_array=[4,5,6]; 3trace(is_obj instanceof Array); 4//显示false 5trace(is_array instanceof Array); 6//显示 true 另外还有一个特性。当trace一个Object，显示的是[object object]；而trace一个Array，返回的则是这个Array的值。可以通过这个特性判断这个对象是Array还是Object。\n","date":"2005-10-11","description":"","lastmod":"2005-10-11T06:58:45Z","slug":"test-array-type","tags":["actionscript"],"title":"如何确认一个对象是Array？","url":"https://blog.zengrong.net/post/test-array-type/"},{"categories":["impressions"],"content":"这篇文章是从我的旧Blog中转过来，本来也考虑过不转这篇文章，干脆删掉好了，也能顾及某些人的面子问题。但鉴于这篇文章中的主角对我的Blog非常的不感兴趣，我也就还是保留下来了。包括这位（荷风晓雨）老师给我的回复，我也一字不差的转过来。非常欢迎她能再来我的Blog与我共同讨论。\n这段时间各种“网络传销”的骗局也逐渐见诸报端，许多传销者打着各种幌子行骗，甚至把鬼点子打到了我们的教育事业上，这实在是要引起我们警觉的。\n附：\n“网络传销”是一种新型的传销形式，其主要载体是以互联网为平台的各种服务的使用权，包括接受远程教育、个人网站空间、电子商务平台等服务。武汉市公安局去年破获了湖北省首例网上传销案，犯罪嫌疑人伍莉华、吕萍利用一家全球远程教育网站，在武汉、荆州等地，以租用网站的服务器空间为名，以高额回报为诱饵，在武汉、荆州等地发展下线会员１千余人，涉案金额达１３５万元。\n假直销之名做传销，当然难以逃过执法者的“火眼金睛”，可如果打着直销的幌子来“拉人头”呢？是不是又增添了一剂诱饵？传销已有其自身的“传销秘诀”，诸如“赚钱是生活，挣钱是生存；21世纪是花花绿绿的，而赚钱的渠道也是花花绿绿的”的赚钱“理论”，并有“连爹妈的钱也想点子骗”的赚钱窍门，金字塔式的销售结构又注定会让人越陷越深。诸多的“传销秘诀”正是其诱惑力、危害性所在。\n2005年6月6日22:46星期一 [想法观点]\n朋友被一MM缠着做网络直销，这是对话记录……呵呵，到底什么是传销？大致能看清楚。不明白这位MM头脑怎么如此简单，抑或她认为别人的脑袋比她的还要简单？\n消息对象:(荷风晓雨) 2005-05-06 20:33:53 荷风晓雨\n请先看看这个网站：www.huaboo.com用户名：yf1密码：123456789\n2005-05-06 20:40:31 荷风晓雨\n是yf2\n2005-06-06 20:34:57 荷风晓雨\n你好\n2005-06-06 21:08:38 荷风晓雨\n在吗\n2005-06-06 21:21:56 朋友\n在写东西呢，不好意思\n2005-06-06 21:09:05 荷风晓雨\n你是不是做过网站推广\n2005-06-06 21:22:23 朋友\n没有\n2005-06-06 21:10:04 荷风晓雨\n哦\n2005-06-06 21:10:17 荷风晓雨\n可能是记错了\n2005-06-06 21:23:56 朋友\n[:)]\n2005-06-06 21:11:40 荷风晓雨\n我有一个很不错的信息，愿意了解一下吗\n2005-06-06 21:25:07 朋友\n瞧瞧先\n2005-06-06 21:12:46 荷风晓雨\n你觉得教育网有推广价值吗\n2005-06-06 21:26:21 朋友\n没有怎么关注这方面的东东\n2005-06-06 21:14:20 荷风晓雨\n请你先看看这个教育及娱乐网站www.huaboo.com用户名昮.....56789,等会我再回来！\n2005-06-06 21:14:27 荷风晓雨\n那你先看看\n2005-06-06 21:14:43 荷风晓雨\n380元的华博中文在线，集历史评书、电子书库、音乐电影、教育教学、电子商务、网络电话于一体的综合性网站\n2005-06-06 21:17:27 荷风晓雨\n重要的是用这个网站还获得一个赚钱的商机\n2005-06-06 21:19:17 荷风晓雨\n６月１５日前注册还送１００Ｍ的网络空间终身使用\n2005-06-06 21:32:19 朋友\n[图片]什么意思？\n2005-06-06 21:20:52 荷风晓雨\n我们视频讲解\n2005-06-06 21:20:50 荷风晓雨\n你有麦吗\n2005-06-06 21:34:07 朋友\n没有\n2005-06-06 21:34:28 朋友\n收费注册吧？\n2005-06-06 21:22:05 荷风晓雨\n是的\n2005-06-06 21:22:12 荷风晓雨\n终身３８０元\n2005-06-06 21:22:35 荷风晓雨\n不用在交费\n2005-06-06 21:35:47 朋友\n然后向其他人推荐，满1000人，而且必须满足什么1：2什么的才能有奖金？\n2005-06-06 21:23:05 荷风晓雨\n不是的，那是另外一块奖金］\n2005-06-06 21:36:35 朋友\n不懂，那我花380注册了，怎么挣钱？\n2005-06-06 21:23:48 荷风晓雨\n如果你公司推荐三个人，左右形成１：２这样一个比例，你就有１００元奖金\n2005-06-06 21:24:10 荷风晓雨\n如果你下面的会员再向别人推荐用这个网站\n2005-06-06 21:24:50 荷风晓雨\n又推荐了三个人也是１：２这样的比例，你又可以赚一百\n2005-06-06 21:25:51 荷风晓雨\n当然，你现在做的话，不需要做两个区，只做一个区就可以了\n2005-06-06 21:26:33 荷风晓雨\n能明白吗\n2005-06-06 21:39:42 朋友\n明白，\n2005-06-06 21:40:00 朋友\n有点向传销，呵呵\n2005-06-06 21:27:01 荷风晓雨\n你也许没接触过直销行业\n2005-06-06 21:27:26 荷风晓雨\n你知道吗．２００５年是直销的春天\n2005-06-06 21:28:03 荷风晓雨\n现在国家开放直销市场，很多公司很多产品都是以这种模式进行销售\n2005-06-06 21:29:19 荷风晓雨\n传销也好直销也好其实就是一种销售模式，问题是你销售的产品是不是物有所值，\n2005-06-06 21:29:50 荷风晓雨\n如果是欺骗的，暴利的行业，那可能要被国家打击\n2005-06-06 21:46:17 朋友\n我不注册就不能向其他人推荐么？\n2005-06-06 21:34:45 荷风晓雨\n那你没有资格拿到奖金啊．只有你注册了，你的银行卡才输到网上，有奖金直接打到你的卡上\n2005-06-06 21:35:37 荷风晓雨\n这个网站有很多卖点的\n2005-06-06 21:48:46 朋友\n这个公司会挣钱，不管会员能否挣钱，反正它先捞到钱了\n2005-06-06 21:35:52 荷风晓雨\n教学内容很好\n2005-06-06 21:36:16 荷风晓雨\n会员只要给它推广了就能赚钱\n2005-06-06 21:49:33 朋友\n问题是要有人肯注册才行哦\n2005-06-06 21:36:36 荷风晓雨\n我上面的一个老师一天就赚几千\n2005-06-06 21:36:55 荷风晓雨\n当然有人愿意注册啊\n2005-06-06 21:37:12 荷风晓雨\n我现在又九十几个会员了\n2005-06-06 21:37:40 荷风晓雨\n我现在进一个人就可以赚１００\n2005-06-06 21:38:08 荷风晓雨\n我上面的那个老师有好几百个会员了\n2005-06-06 21:51:16 朋友\n不对吧，你的下线发展起来，怎么可能就100？\n2005-06-06 21:38:45 荷风晓雨\n你可能没搞懂\n2005-06-06 21:39:18 荷风晓雨\n因为我的右边是大区，八十几个人了\n2005-06-06 21:40:26 荷风晓雨\n我现在只用做左区，我的左区进来一个人，我就可以赚１００啊\n2005-06-06 21:41:50 荷风晓雨\n你现在进来也是放在我的左边，，你也只用做一个区，因为我会帮你做一个左区，进来一个人或者两个人，你也可以赚１００\n2005-06-06 21:42:29 荷风晓雨\n这是团队创业，不是你一个人单枪匹马干\n2005-06-06 21:43:01 荷风晓雨\n明白吗\n2005-06-06 21:56:28 朋友\n不是很明白\n2005-06-06 21:44:35 荷风晓雨\n我讲话你能听到吗\n2005-06-06 21:57:46 朋友\n？？？\n2005-06-06 21:45:18 荷风晓雨\n我跟你讲解一下啊\n2005-06-06 21:58:27 朋友\n我这是音箱耶，\n2005-06-06 21:45:48 荷风晓雨\n可以听就行啊\n2005-06-06 21:59:12 朋友\n老爹老妈看电视呢，还是不要\n2005-06-06 21:46:49 荷风晓雨\n还真是个孝子[:D]\n2005-06-06 22:00:05 朋友\n[:D]\n2005-06-06 21:47:53 荷风晓雨\n这个网站的网络电话打长途只要０.１２元一分钟\n2005-06-06 21:48:12 荷风晓雨\n都是会员的话．打电话就是免费的\n2005-06-06 21:48:55 荷风晓雨\n你打过网络电话吗\n2005-06-06 21:56:13 荷风晓雨\n我周五在电大学习，你也去吗\n2005-06-06 21:56:27 荷风晓雨\n我到时可以跟你讲一下\n2005-06-06 22:11:58 朋友\n哦\n以下为评论内容\nzrong(61.242.172.12)在 2005年9月27日15:09星期二 评论: [删除] [回复]\n你的力量确实很有限，要不岂不是有更多人成了你的下线？ “而作为一个信息技术教师把最好最先进的远程教育推荐给学生使用”，这句话说的真是冠冕堂皇呀，可惜了，你把你的学生也变成了你的工具\n我欢迎任何与我讨论技术和经验的朋友，不过对于你这样的“信息技术教师”来说，我并不在乎你这辈子是否看到我的网站，不过希望你下辈子能看到。\n顺便说一句：我做的是技术网站，我的网站永远开放、免费、共享，不会删除任何非反动、非色情、非政治的留言，你的帖子不会让我很光彩，不过如果你觉得让你不光彩了，我倒是可以考虑删除。\n回复(61.183.91.60)在 2005年9月27日10:31星期二 评论: [删除] [回复]\n你还蛮会教训人呢，你又教出了什么高材生呢？我想我不会比你没有责任感，也不会比你用在教学上的心思要少，更不会比你差！\n我一个人的力量有限，我再怎么努力也只能教有限的几个学生，而作为一个信息技术教师把最好最先进的远程教育推荐给学生使用，所产生的效果我想是远远大于一个人的力量的。我不是什么心虚，如果不是公司通知我，说我把密码公布出来影响了公司的利益，告诉我这个网站，我想你这个网站我一辈子也看不到！你觉得保留这个帖子让你很光彩的话你就继续保留吧，反正密码我也改了，无所谓了。\nzrong(219.139.235.80)在 2005年9月24日17:20星期六 评论: [删除] [回复]\n心虚了呀\n你的精力应该更多花在怎样教育你的学生身上，应该比你做传销更有意义\n我只是写自己的Blog，你可以选择不看。不过幸亏你看到了，否则我不就白写了？\n谢谢支持，你还是我的Blog改版以来留言字数最多的人\n荷风晓雨(219.139.238.156)在 2005年9月21日16:20星期三 评论: [删除] [回复]\n朋友：别在这里丢人显眼了，你懂什么叫直销吗？什么叫传销吗？我怀疑你的头脑才是太简单了！传销也好，直销也好，只是一种销售模式，是一种世界通行的先进的销售模式，本身没有对错。好比一把菜刀，你能说它就是一个坏东西吗？菜刀在厨师的手里，可以用来做出美味的佳肴，在一个杀人犯手里却成了杀人凶器。你能说这是菜刀不好吗？我在这里告诉你长点见识，世界直销协会联盟主席狄克.狄维士先生说：区别正当传销与非法传销的关键在于产品基础，如果你出售的产品是物有所值，那就是正当的合法的传销，如果你出售的产品物不所值，非法谋取暴利，或者根本没有什么产品，只是靠拉人头进来赚钱，那就是非法的传销，是国家打击的对象。华博教育网从小学到高中全套课程都有，还有英语学习、电脑学习、成功学、电子书库等很多超值内容，而这么丰富的学习网站你只需一次支付480元，就可以终身在上面学习，享受该网站提供的多种服务，可以说太物超所值了，简直太划算了！你去打听一下，由全国特级教师主讲的新课标的全套课程的光碟要花多少钱才可以买到？而在华博网站上只要你成为公司的会员，你可以随意点击学习这些课程。你说花480元买价值几万元的产品值不值？我们宣传教育网也是为了把一个好的学习网站推荐给身边的孩子使用，让他们花最少的钱接受最优秀的教育，提高全民族的素质，本身是为国家做贡献，是利国利民的好事，公司因为我们给它做了宣传获得了赢利而给我们一点回报，这有什么大逆不道的吗？我怀疑你的身体虽然进入了21世纪，头脑还停留在80年代，对新生事物不了解，也不学习，孤陋寡闻，却还在这里说这种没水平的话，简直是丢人，有闲工夫多学点东西！！\n把与别人的聊天记录不经他人允许，到处乱发，简直就是小人所为，真为你这种行为感到羞耻！谁与你这样的小人聊天简直就是谁的不幸！\n","date":"2005-10-10","description":"","lastmod":"2005-10-10T05:41:06Z","slug":"chuanxiao","tags":[],"title":"直销？传销？","url":"https://blog.zengrong.net/post/chuanxiao/"},{"categories":["technology"],"content":"在制作Chat Union系统时，有一个功能暂时不打算使用Flash实现，但这个功能又必须出现在Flash中。因此考虑使用一个div标签，让其漂浮在Flash动画上方，提供这个暂时不用Flash实现的功能。\n但是默认情况下，Flash影片是处于最上层的，无法将 \u0026lt;div\u0026gt;置于其上。查阅Flash的帮助文件，发现这样一段描述：\nwmode 属性/参数\n值\nWindow | Opaque | Transparent\n模板变量：$WM\n说明\n（可选）使您可以使用 Internet Explorer 4.0 中的透明 Flash 内容、绝对定位和分层显示的功能。此标记/属性仅在带有 Flash Player ActiveX 控件的 Windows 中有效。\n“Window”在 Web 页上用影片自己的矩形窗口来播放应用程序。“Window”表明 Flash 应用程序与 HTML 层没有任何交互，并且始终位于最顶层。\n“Opaque” 使应用程序隐藏页面上位于它后面的所有内容。\n“Transparent”使 HTML 页的背景可以透过应用程序的所有透明部分进行显示，这样可能会降低动画性能。\n“Opaque windowless”和“Transparent windowless” 都可与 HTML 层交互，并允许 SWF 文件上方的层遮蔽应用程序。这两种选项之间的差异在于“Transparent”允许透明，因此，如果 SWF 文件的某一部分是透明的，则 SWF 文件下方的 HTML 层可以透过该部分显示出来。\n如果忽略此属性，默认值为 Window。仅适用于 object。\n因此，在HTML中将 \u0026lt;object\u0026gt; 的wmode参数设成这样：\n然后将一个HTML的 \u0026lt;div\u0026gt; 置于Flash影片之上，测试成功。\n但是，问题随之出现。许多客人在聊天室中打字的时候发现，输入法的选字框会跑到页面的左上角，而且会影响网页的排版，将Flash影片挤到下面。有时甚至无法将文字输入到Flash中。为了还原错误，我使用了多款输入法，发现微软的所有输入法都有这个问题。因为微软输入法在选字的时候都有一个虚线选择，我怀疑是这个虚线选择功能出现问题所致。\n可是，不能单单怀疑微软输入法的兼容性不好。因为，同样的程序，在昨天就没有出现这个情况。和开发伙伴测试了其他输入法，发现智能ABC输入法也存在这个问题，只是在我的计算机上没有出现。\n开始怀疑加入的 \u0026lt;div\u0026gt;，将其屏蔽，问题仍然存在。\n继续怀疑到 wmode 属性的头上。删除 \u0026lt;param name=\u0026quot;wmode\u0026quot; value=\u0026quot;Opaque\u0026quot;\u0026gt; 语句，问题消失。\n仔细思考，因为**“Opaque” 使应用程序隐藏页面上位于它后面的所有内容**，也就是说使用了这个参数之后，在网页中不是Flash位于最上而是 \u0026lt;div\u0026gt; 位于最上了，某些输入法会将焦点设定为网页中位于最上的对象（也就是 \u0026lt;div\u0026gt;中）。而这个 \u0026lt;div\u0026gt; 又是使用绝对定位“漂浮”在网页上的，这就造成了输入法的选字框定位不准确，也就发生了刚才的问题了。\n从帮助文件中看来，使用“Opaque windowless”参数应该会好一些，不过，我们实在不愿意再试\n","date":"2005-10-08","description":"","lastmod":"2005-10-08T13:53:40Z","slug":"wmodeopaque","tags":["flashplayer"],"title":"慎用wmode属性的Opaque参数（输入中文时输入法选字框跑到左上角的问题 ）","url":"https://blog.zengrong.net/post/wmodeopaque/"},{"categories":[],"content":"提问的智慧 How To Ask Questions The Smart Way\nCopyright © 2001,2006,2014 Eric S. Raymond, Rick Moen\n本指南英文版版权为 Eric S. Raymond, Rick Moen 所有。\n原文网址：http://www.catb.org/~esr/faqs/smart-questions.html\nCopyleft 2001 by D.H.Grand(nOBODY/Ginux), 2010 by Gasolin, 2015 by Ryan Wu\n本中文指南是基于原文 3.10 版以及 2010 年由 Gasolin 所翻译版本的最新翻译；\n协助指出翻译问题，请发 Issue，或直接发 Pull Request 给我。\n本文另有繁體中文版。\n原文版本历史 目录 声明 简介 在提问之前 当你提问时 慎选提问的论坛 Stack Overflow 网站和 IRC 论坛 第二步，使用项目邮件列表 使用有意义且描述明确的标题 使问题容易回复 用清晰、正确、精准并合法语法的语句 使用易于读取且标准的文件格式发送问题 精确地描述问题并言之有物 话不在多而在精 别动辄声称找到 Bug 低声下气不能代替你的功课 描述问题症状而非你的猜测 按发生时间先后列出问题症状 描述目标而不是过程 别要求使用私人电邮回复 清楚明确的表达你的问题以及需求 询问有关代码的问题时 别把自己家庭作业的问题贴上来 去掉无意义的提问句 即使你很急也不要在标题写紧急 礼多人不怪，而且有时还很有帮助 问题解决后，加个简短的补充说明 如何解读答案 RTFM 和 STFW：如何知道你已完全搞砸了 如果还是搞不懂 处理无礼的回应 如何避免扮演失败者 不该问的问题 好问题与蠢问题 如果得不到回答 如何更好地回答问题 相关资源 鸣谢 声明 许多项目在他们的使用协助/说明网页中链接了本指南，这么做很好，我们也鼓励大家都这么做。但如果你是负责管理这个项目网页的人，请在超链接附近的显著位置上注明：\n本指南不提供此项目的实际支持服务！\n我们已经深刻领教到少了上述声明所带来的痛苦。因为少了这点声明，我们不停地被一些白痴纠缠。这些白痴认为既然我们发布了这本指南，那么我们就有责任解决世上所有的技术问题。\n如果你是因为需要某些协助而正在阅读这本指南，并且最后离开是因为发现从本指南作者们身上得不到直接的协助，那么你就是我们所说的那些白痴之一。别问我们问题，我们只会忽略你。我们在这本指南中是教你如何从那些真正懂得你所遇到软件或硬件问题的人取得协助，而 99% 的情况下那不会是我们。除非你确定本指南的作者之一刚好是你所遇到的问题领域的专家，否则请不要打扰我们，这样大家都会开心一点。\n简介 在黑客的世界里，当你拋出一个技术问题时，最终是否能得到有用的回答，往往取决于你所提问和追问的方式。本指南将教你如何正确的提问以获得你满意的答案。\n不只是黑客，现在开源（Open Source）软件已经相当盛行，你常常也可以由其他有经验的使用者身上得到好答案，这是件好事；使用者比起黑客来，往往对那些新手常遇到的问题更宽容一些。然而，将有经验的使用者视为黑客，并采用本指南所提的方法与他们沟通，同样也是能从他们身上得到满意回答的最有效方式。\n首先你应该明白，黑客们喜爱有挑战性的问题，或者能激发他们思维的好问题。如果我们并非如此，那我们也不会成为你想询问的对象。如果你给了我们一个值得反复咀嚼玩味的好问题，我们自会对你感激不尽。好问题是激励，是厚礼。好问题可以提高我们的理解力，而且通常会暴露我们以前从没意识到或者思考过的问题。对黑客而言，\u0026quot;好问题！\u0026quot;是诚挚的大力称赞。\n尽管如此，黑客们有着蔑视或傲慢面对简单问题的坏名声，这有时让我们看起来对新手、无知者似乎较有敌意，但其实不是那样的。\n我们不讳言我们对那些不愿思考、或者在发问前不做他们该做的事的人的蔑视。那些人是时间杀手 —— 他们只想索取，从不付出，消耗我们可用在更有趣的问题或更值得回答的人身上的时间。我们称这样的人为 失败者（撸瑟） （由于历史原因，我们有时把它拼作 lusers）。\n我们意识到许多人只是想使用我们写的软件，他们对学习技术细节没有兴趣。对大多数人而言，电脑只是种工具，是种达到目的的手段而已。他们有自己的生活并且有更要紧的事要做。我们了解这点，也从不指望每个人都对这些让我们着迷的技术问题感兴趣。尽管如此，我们回答问题的风格是指向那些真正对此有兴趣并愿意主动参与解决问题的人，这一点不会变，也不该变。如果连这都变了，我们就是在降低做自己最擅长的事情上的效率。\n我们（在很大程度上）是自愿的，从繁忙的生活中抽出时间来解答疑惑，而且时常被提问淹没。所以我们无情的滤掉一些话题，特别是拋弃那些看起来像失败者的家伙，以便更高效的利用时间来回答赢家（winner）的问题。\n如果你厌恶我们的态度，高高在上，或过于傲慢，不妨也设身处地想想。我们并没有要求你向我们屈服 —— 事实上，我们大多数人非常乐意与你平等地交流，只要你付出小小努力来满足基本要求，我们就会欢迎你加入我们的文化。但让我们帮助那些不愿意帮助自己的人是没有效率的。无知没有关系，但装白痴就是不行。\n所以，你不必在技术上很在行才能吸引我们的注意，但你必须表现出能引导你变得在行的特质 -- 机敏、有想法、善于观察、乐于主动参与解决问题。如果你做不到这些使你与众不同的事情，我们建议你花点钱找家商业公司签个技术支持服务合同，而不是要求黑客个人无偿地帮助你。\n如果你决定向我们求助，当然你也不希望被视为失败者，更不愿成为失败者中的一员。能立刻得到快速并有效答案的最好方法，就是像赢家那样提问 -- 聪明、自信、有解决问题的思路，只是偶尔在特定的问题上需要获得一点帮助。\n（欢迎对本指南提出改进意见。你可以 email 你的建议至 esr@thyrsus.com 或 respond-auto@linuxmafia.com。然而请注意，本文并非网络礼节的通用指南，而我们通常会拒绝无助于在技术论坛得到有用答案的建议）。\n在提问之前 在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前，请先做到以下事情：\n尝试在你准备提问的论坛的旧文章中搜索答案。 尝试上网搜索以找到答案。 尝试阅读手册以找到答案。 尝试阅读常见问题文件（FAQ）以找到答案。 尝试自己检查或试验以找到答案。 向你身边的强者朋友打听以找到答案。 如果你是程序开发者，请尝试阅读源代码以找到答案。 当你提出问题的时候，请先表明你已经做了上述的努力；这将有助于树立你并不是一个不劳而获且浪费别人的时间的提问者。如果你能一并表达在做了上述努力的过程中所学到的东西会更好，因为我们更乐于回答那些表现出能从答案中学习的人的问题。\n运用某些策略，比如先用 Google 搜索你所遇到的各种错误信息（既搜索 Google 论坛，也搜索网页），这样很可能直接就找到了能解决问题的文件或邮件列表线索。即使没有结果，在邮件列表或新闻组寻求帮助时加上一句 我在 Google 中搜过下列句子但没有找到什么有用的东西 也是件好事，即使它只是表明了搜索引擎不能提供哪些帮助。这么做（加上搜索过的字串）也让遇到相似问题的其他人能被搜索引擎引导到你的提问来。\n别着急，不要指望几秒钟的 Google 搜索就能解决一个复杂的问题。在向专家求助之前，再阅读一下常见问题文件（FAQ）、放轻松、坐舒服一些，再花点时间思考一下这个问题。相信我们，他们能从你的提问看出你做了多少阅读与思考，如果你是有备而来，将更有可能得到解答。不要将所有问题一股脑拋出，只因你的第一次搜索没有找到答案（或者找到太多答案）。\n准备好你的问题，再将问题仔细的思考过一遍，因为草率的发问只能得到草率的回答，或者根本得不到任何答案。越是能表现出在寻求帮助前你为解决问题所付出的努力，你越有可能得到实质性的帮助。\n小心别问错了问题。如果你的问题基于错误的假设，某个普通黑客（J. Random Hacker）多半会一边在心里想着蠢问题…， 一边用无意义的字面解释来答复你，希望着你会从问题的回答（而非你想得到的答案）中汲取教训。\n绝不要自以为够格得到答案，你没有；你并没有。毕竟你没有为这种服务支付任何报酬。你将会是自己去挣到一个答案，靠提出有内涵的、有趣的、有思维激励作用的问题 —— 一个有潜力能贡献社区经验的问题，而不仅仅是被动的从他人处索取知识。\n另一方面，表明你愿意在找答案的过程中做点什么是一个非常好的开端。谁能给点提示？、我的这个例子里缺了什么？以及我应该检查什么地方比请把我需要的确切的过程贴出来更容易得到答复。因为你表现出只要有人能指个正确方向，你就有完成它的能力和决心。\n当你提问时 慎选提问的论坛 小心选择你要提问的场合。如果你做了下述的事情，你很可能被忽略掉或者被看作失败者：\n在与主题不合的论坛上贴出你的问题。 在探讨进阶技术问题的论坛张贴非常初级的问题；反之亦然。 在太多的不同新闻群组上重复转贴同样的问题（cross-post）。 向既非熟人也没有义务解决你问题的人发送私人电邮。 黑客会剔除掉那些搞错场合的问题，以保护他们沟通的渠道不被无关的东西淹没。你不会想让这种事发生在自己身上的。\n因此，第一步是找到对的论坛。再说一次，Google 和其它搜索引擎还是你的朋友，用它们来找到与你遭遇到困难的软硬件问题最相关的网站。通常那儿都有常见问题（FAQ）、邮件列表及相关说明文件的链接。如果你的努力（包括阅读 FAQ）都没有结果，网站上也许还有报告 Bug（Bug-reporting）的流程或链接，如果是这样，链过去看看。\n向陌生的人或论坛发送邮件最可能是风险最大的事情。举例来说，别假设一个提供丰富内容的网页的作者会想充当你的免费顾问。不要对你的问题是否会受到欢迎做太乐观的估计 -- 如果你不确定，那就向别处发送，或者压根别发。\n在选择论坛、新闻群组或邮件列表时，别太相信名字，先看看 FAQ 或者许可书以弄清楚你的问题是否切题。发文前先翻翻已有的话题，这样可以让你感受一下那里的文化。事实上，事先在新闻组或邮件列表的历史记录中搜索与你问题相关的关键词是个极好的主意，也许这样就找到答案了。即使没有，也能帮助你归纳出更好的问题。\n别像机关枪似的一次\u0026quot;扫射\u0026quot;所有的帮助渠道，这就像大喊大叫一样会使人不快。要一个一个地来。\n搞清楚你的主题！最典型的错误之一是在某种致力于跨平台可移植的语言、套件或工具的论坛中提关于 Unix 或 Windows 操作系统程序界面的问题。如果你不明白为什么这是大错，最好在搞清楚这之间差异之前什么也别问。\n一般来说，在仔细挑选的公共论坛中提问，会比在私有论坛中提同样的问题更容易得到有用的回答。有几个理由可以支持这点，一是看潜在的回复者有多少，二是看观众有多少。黑客较愿意回答那些能帮助到许多人的问题。\n可以理解的是，老练的黑客和一些热门软件的作者正在接受过多的错发信息。就像那根最后压垮骆驼背的稻草一样，你的加入也有可能使情况走向极端 —— 已经好几次了，一些热门软件的作者从自己软件的支持中抽身出来，因为伴随而来涌入其私人邮箱的无用邮件变得无法忍受。\nStack Overflow 搜索，然后 在 Stack Exchange 问。\n近年来，Stack Exchange community 社区已经成为回答技术及其他问题的主要渠道，尤其是那些开放源码的项目。\n因为 Google 索引是即时的，在看 Stack Exchange 之前先在 Google 搜索。有很高的机率某人已经问了一个类似的问题，而且 Stack Exchange 网站们往往会是搜索结果中最前面几个。如果你在 Google 上没有找到任何答案，你再到特定相关主题的网站去找。用标签（Tag）搜索能让你更缩小你的搜索结果。\nStack Exchange 已经成长到超过一百个网站，以下是最常用的几个站：\nSuper User 是问一些通用的电脑问题，如果你的问题跟代码或是写程序无关，只是一些网络连线之类的，请到这里。 Stack Overflow 是问写程序有关的问题。 Server Fault 是问服务器和网管相关的问题。 网站和 IRC 论坛 本地的使用者群组（user group），或者你所用的 Linux 发行版本也许正在宣传他们的网页论坛或 IRC 频道，并提供新手帮助（在一些非英语国家，新手论坛很可能还是邮件列表）， 这些地方是开始提问的好首选，特别是当你觉得遇到的也许只是相对简单或者很普通的问题时。有广告赞助的 IRC 频道是公开欢迎提问的地方，通常可以即时得到回应。\n事实上，如果程序出的问题只发生在特定 Linux 发行版提供的版本（这很常见），最好先去该发行版的论坛或邮件列表中提问，再到程序本身的论坛或邮件列表提问。（否则）该项目的黑客可能仅仅回复 \u0026quot;用我们的版本\u0026quot;。\n在任何论坛发文以前，先确认一下有没有搜索功能。如果有，就试着搜索一下问题的几个关键词，也许这会有帮助。如果在此之前你已做过通用的网页搜索（你也该这样做），还是再搜索一下论坛，搜索引擎有可能没来得及索引此论坛的全部内容。\n通过论坛或 IRC 频道来提供使用者支持服务有增长的趋势，电子邮件则大多为项目开发者间的交流而保留。所以最好先在论坛或 IRC 中寻求与该项目相关的协助。\n在使用 IRC 的时候，首先最好不要发布很长的问题描述，有些人称之为频道洪水。最好通过一句话的问题描述来开始聊天。\n第二步，使用项目邮件列表 当某个项目提供开发者邮件列表时，要向列表而不是其中的个别成员提问，即使你确信他能最好地回答你的问题。查一查项目的文件和首页，找到项目的邮件列表并使用它。有几个很好的理由支持我们采用这种办法：\n任何好到需要向个别开发者提出的问题，也将对整个项目群组有益。反之，如果你认为自己的问题对整个项目群组来说太愚蠢，也不能成为骚扰个别开发者的理由。 向列表提问可以分散开发者的负担，个别开发者（尤其是项目领导人）也许太忙以至于没法回答你的问题。 大多数邮件列表都会被存档，那些被存档的内容将被搜索引擎索引。如果你向列表提问并得到解答，将来其它人可以通过网页搜索找到你的问题和答案，也就不用再次发问了。 如果某些问题经常被问到，开发者可以利用此信息来改进说明文件或软件本身，以使其更清楚。如果只是私下提问，就没有人能看到最常见问题的完整场景。 如果一个项目既有\u0026quot;使用者\u0026quot; 也有\u0026quot;开发者\u0026quot;（或\u0026quot;黑客\u0026quot;）邮件列表或论坛，而你又不会动到那些源代码，那么就向\u0026quot;使用者\u0026quot;列表或论坛提问。不要假设自己会在开发者列表中受到欢迎，那些人多半会将你的提问视为干扰他们开发的噪音。\n然而，如果你确信你的问题很特别，而且在\u0026quot;使用者\u0026quot; 列表或论坛中几天都没有回复，可以试试前往\u0026quot;开发者\u0026quot;列表或论坛发问。建议你在张贴前最好先暗地里观察几天以了解那里的行事方式（事实上这是参与任何私有或半私有列表的好主意）\n如果你找不到一个项目的邮件列表，而只能查到项目维护者的电子邮件地址，尽管向他发信。即使是在这种情况下，也别假设（项目）邮件列表不存在。在你的电子邮件中，请陈述你已经试过但没有找到合适的邮件列表，也提及你不反对将自己的邮件转发给他人（许多人认为，即使没什么秘密，私人电子邮件也不应该被公开。通过允许将你的电子邮件转发他人，你给了相应人员处置你邮件的选择）。\n使用有意义且描述明确的标题 在邮件列表、新闻群组或论坛中，大约 50 字以内的标题是抓住资深专家注意力的好机会。别用喋喋不休的帮帮忙、跪求、急（更别说救命啊！！！！这样让人反感的话，用这种标题会被条件反射式地忽略）来浪费这个机会。不要妄想用你的痛苦程度来打动我们，而应该是在这点空间中使用极简单扼要的描述方式来提出问题。\n一个好标题范例是目标 —— 差异式的描述，许多技术支持组织就是这样做的。在目标部分指出是哪一个或哪一组东西有问题，在差异部分则描述与期望的行为不一致的地方。\n蠢问题：救命啊！我的笔记本电脑不能正常显示了！\n聪明问题：X.org 6.8.1 的鼠标光标会变形，某牌显卡 MV1005 芯片组。\n更聪明问题：X.org 6.8.1 的鼠标光标，在某牌显卡 MV1005 芯片组环境下 - 会变形。\n编写目标 —— 差异 式描述的过程有助于你组织对问题的细致思考。是什么被影响了？ 仅仅是鼠标光标或者还有其它图形？只在 X.org 的 X 版中出现？或只是出现在 6.8.1 版中？ 是针对某牌显卡芯片组？或者只是其中的 MV1005 型号？ 一个黑客只需瞄一眼就能够立即明白你的环境和你遇到的问题。\n总而言之，请想像一下你正在一个只显示标题的存档讨论串（Thread）索引中查寻。让你的标题更好地反映问题，可使下一个搜索类似问题的人能够关注这个讨论串，而不用再次提问相同的问题。\n如果你想在回复中提出问题，记得要修改内容标题，以表明你是在问一个问题， 一个看起来像 Re: 测试 或者 Re: 新 bug 的标题很难引起足够重视。另外，在不影响连贯性之下，适当引用并删减前文的内容，能给新来的读者留下线索。\n对于讨论串，不要直接点击回复来开始一个全新的讨论串，这将限制你的观众。因为有些邮件阅读程序，比如 mutt ，允许使用者按讨论串排序并通过折叠讨论串来隐藏消息，这样做的人永远看不到你发的消息。\n仅仅改变标题还不够。mutt 和其它一些邮件阅读程序还会检查邮件标题以外的其它信息，以便为其指定讨论串。所以宁可发一个全新的邮件。\n在网页论坛上，好的提问方式稍有不同，因为讨论串与特定的信息紧密结合，并且通常在讨论串外就看不到里面的内容，故通过回复提问，而非改变标题是可接受的。不是所有论坛都允许在回复中出现分离的标题，而且这样做了基本上没有人会去看。不过，通过回复提问，这本身就是暧昧的做法，因为它们只会被正在查看该标题的人读到。所以，除非你只想在该讨论串当前活跃的人群中提问，不然还是另起炉灶比较好。\n使问题容易回复 以请将你的回复发送到……来结束你的问题多半会使你得不到回答。如果你觉得花几秒钟在邮件客户端设置一下回复地址都麻烦，我们也觉得花几秒钟思考你的问题更麻烦。如果你的邮件程序不支持这样做，换个好点的；如果是操作系统不支持这种邮件程序，也换个好点的。\n在论坛，要求通过电子邮件回复是非常无礼的，除非你认为回复的信息可能比较敏感（有人会为了某些未知的原因，只让你而不是整个论坛知道答案）。如果你只是想在有人回复讨论串时得到电子邮件提醒，可以要求网页论坛发送给你。几乎所有论坛都支持诸如追踪此讨论串、有回复时发送邮件提醒等功能。\n用清晰、正确、精准并语法正确的语句 我们从经验中发现，粗心的提问者通常也会粗心的写程序与思考（我敢打包票）。回答粗心大意者的问题很不值得，我们宁愿把时间耗在别处。\n正确的拼写、标点符号和大小写是很重要的。一般来说，如果你觉得这样做很麻烦，不想在乎这些，那我们也觉得麻烦，不想在乎你的提问。花点额外的精力斟酌一下字句，用不着太僵硬与正式 —— 事实上，黑客文化很看重能准确地使用非正式、俚语和幽默的语句。但它必须很准确，而且有迹象表明你是在思考和关注问题。\n正确地拼写、使用标点和大小写，不要将its混淆为it's，loose搞成lose或者将discrete弄成discreet。不要全部用大写，这会被视为无礼的大声嚷嚷（全部小写也好不到哪去，因为不易阅读。Alan Cox 也许可以这样做，但你不行）。\n更白话的说，如果你写得像是个半文盲[译注：小白]，那多半得不到理睬。也不要使用即时通信中的简写或火星文，如将的简化为d会使你看起来像一个为了少打几个键而省字的小白。更糟的是，如果像个小孩似地鬼画符那绝对是在找死，可以肯定没人会理你（或者最多是给你一大堆指责与挖苦）。\n如果在使用非母语的论坛提问，你可以犯点拼写和语法上的小错，但决不能在思考上马虎（没错，我们通常能弄清两者的分别）。同时，除非你知道回复者使用的语言，否则请使用英语书写。繁忙的黑客一般会直接删除用他们看不懂语言写的消息。在网络上英语是通用语言，用英语书写可以将你的问题在尚未被阅读就被直接删除的可能性降到最低。\n如果英文是你的外语（Second language），提示潜在回复者你有潜在的语言困难是很好的： [译注：以下附上原文以供使用]\nEnglish is not my native language; please excuse typing errors.\n英文不是我的母语，请原谅我的错字或语法。 If you speak $LANGUAGE, please email/PM me; I may need assistance translating my question.\n如果你说某语言，请寄信/私讯给我；我需要有人协助我翻译我的问题。 I am familiar with the technical terms, but some slang expressions and idioms are difficult for me.\n我对技术名词很熟悉，但对于俗语或是特别用法比较不甚了解。 I've posted my question in $LANGUAGE and English. I'll be glad to translate responses, if you only use one or the other.\n我把我的问题用某语言和英文写出来，如果你只用一种语言回答，我会乐意将其翻译成另一种。 使用易于读取且标准的文件格式发送问题 如果你人为地将问题搞得难以阅读，它多半会被忽略，人们更愿读易懂的问题，所以：\n使用纯文字而不是 HTML (关闭 HTML 并不难）。 使用 MIME 附件通常是可以的，前提是真正有内容（譬如附带的源代码或 patch），而不仅仅是邮件程序生成的模板（譬如只是信件内容的拷贝）。 不要发送一段文字只是一行句子但自动换行后会变成多行的邮件（这使得回复部分内容非常困难）。设想你的读者是在 80 个字符宽的终端机上阅读邮件，最好设置你的换行分割点小于 80 字。 但是，对一些特殊的文件不要设置固定宽度（譬如日志档案拷贝或会话记录）。数据应该原样包含，让回复者有信心他们看到的是和你看到的一样的东西。 在英语论坛中，不要使用Quoted-Printable MIME 编码发送消息。这种编码对于张贴非 ASCII 语言可能是必须的，但很多邮件程序并不支持这种编码。当它们处理换行时，那些文本中四处散布的=20符号既难看也分散注意力，甚至有可能破坏内容的语意。 绝对，永远不要指望黑客们阅读使用封闭格式编写的文档，像微软公司的 Word 或 Excel 文件等。大多数黑客对此的反应就像有人将还在冒热气的猪粪倒在你家门口时你的反应一样。即便他们能够处理，他们也很厌恶这么做。 如果你从使用 Windows 的电脑发送电子邮件，关闭微软愚蠢的智能引号功能 （从[选项] \u0026gt; [校订] \u0026gt; [自动校正选项]，勾选掉智能引号单选框），以免在你的邮件中到处散布垃圾字符。 在论坛，勿滥用表情符号和HTML功能（当它们提供时）。一两个表情符号通常没有问题，但花哨的彩色文本倾向于使人认为你是个无能之辈。过滥地使用表情符号、色彩和字体会使你看来像个傻笑的小姑娘。这通常不是个好主意，除非你只是对性而不是对答案感兴趣。 如果你使用图形用户界面的邮件程序（如微软公司的 Outlook 或者其它类似的），注意它们的默认设置不一定满足这些要求。大多数这类程序有基于选单的查看源代码命令，用它来检查发送文件夹中的邮件，以确保发送的是纯文本文件同时没有一些奇怪的字符。\n精确地描述问题并言之有物 仔细、清楚地描述你的问题或 Bug 的症状。 描述问题发生的环境（机器配置、操作系统、应用程序、以及相关的信息），提供经销商的发行版和版本号（如：Fedora Core 4、Slackware 9.1等）。 描述在提问前你是怎样去研究和理解这个问题的。 描述在提问前为确定问题而采取的诊断步骤。 描述最近做过什么可能相关的硬件或软件变更。 尽可能的提供一个可以重现这个问题的可控环境的方法。 尽量去揣测一个黑客会怎样反问你，在你提问之前预先将黑客们可能遇到的问题回答一遍。\n以上几点中，当你报告的是你认为可能在代码中的问题时，给黑客一个可以重现你的问题的环境尤其重要。当你这么做时，你得到有效的回答的机会和速度都会大大的提升。\nSimon Tatham 写过一篇名为《如何有效的报告 Bug》的出色文章。强力推荐你也读一读。\n话不在多而在精 你需要提供精确有内容的信息。这并不是要求你简单的把成堆的出错代码或者资料完全转录到你的提问中。如果你有庞大而复杂的测试样例能重现程序挂掉的情境，尽量将它剪裁得越小越好。\n这样做的用处至少有三点。 第一，表现出你为简化问题付出了努力，这可以使你得到回答的机会增加； 第二，简化问题使你更有可能得到有用的答案； 第三，在精炼你的 bug 报告的过程中，你很可能就自己找到了解决方法或权宜之计。\n别动辄声称找到 Bug 当你在使用软件中遇到问题，除非你非常、非常的有根据，不要动辄声称找到了 Bug。提示：除非你能提供解决问题的源代码补丁，或者提供回归测试来表明前一版本中行为不正确，否则你都多半不够完全确信。这同样适用在网页和文件，如果你（声称）发现了文件的Bug，你应该能提供相应位置的修正或替代文件。\n请记得，还有许多其它使用者没遇到你发现的问题，否则你在阅读文件或搜索网页时就应该发现了（你在抱怨前已经做了这些，是吧？）。这也意味着很有可能是你弄错了而不是软件本身有问题。\n编写软件的人总是非常辛苦地使它尽可能完美。如果你声称找到了 Bug，也就是在质疑他们的能力，即使你是对的，也有可能会冒犯到其中某部分人。当你在标题中嚷嚷着有Bug时，这尤其严重。\n提问时，即使你私下非常确信已经发现一个真正的 Bug，最好写得像是你做错了什么。如果真的有 Bug，你会在回复中看到这点。这样做的话，如果真有 Bug，维护者就会向你道歉，这总比你惹恼别人然后欠别人一个道歉要好一点。\n低声下气不能代替你的功课 有些人明白他们不该粗鲁或傲慢的提问并要求得到答复，但他们选择另一个极端 —— 低声下气：我知道我只是个可悲的新手，一个撸瑟，但...。这既使人困扰，也没有用，尤其是伴随着与实际问题含糊不清的描述时更令人反感。\n别用原始灵长类动物的把戏来浪费你我的时间。取而代之的是，尽可能清楚地描述背景条件和你的问题情况。这比低声下气更好地定位了你的位置。\n有时网页论坛会设有专为新手提问的版面，如果你真的认为遇到了初学者的问题，到那去就是了，但一样别那么低声下气。\n描述问题症状而非你的猜测 告诉黑客们你认为问题是怎样造成的并没什么帮助。（如果你的推断如此有效，还用向别人求助吗？），因此要确信你原原本本告诉了他们问题的症状，而不是你的解释和理论；让黑客们来推测和诊断。如果你认为陈述自己的猜测很重要，清楚地说明这只是你的猜测，并描述为什么它们不起作用。\n蠢问题\n我在编译内核时接连遇到 SIG11 错误， 我怀疑某条飞线搭在主板的走线上了，这种情况应该怎样检查最好？\n聪明问题\n我的组装电脑是 FIC-PA2007 主机板搭载 AMD K6/233 CPU（威盛 Apollo VP2 芯片组）， 256MB Corsair PC133 SDRAM 内存，在编译内核时，从开机 20 分钟以后就频频产生 SIG11 错误， 但是在头 20 分钟内从没发生过相同的问题。重新启动也没有用，但是关机一晚上就又能工作 20 分钟。 所有内存都换过了，没有效果。相关部分的标准编译记录如下…。\n由于以上这点似乎让许多人觉得难以配合，这里有句话可以提醒你：所有的诊断专家都来自密苏里州。 美国国务院的官方座右铭则是：让我看看（出自国会议员 Willard D. Vandiver 在 1899 年时的讲话：我来自一个出产玉米，棉花，牛蒡和民主党人的国家，滔滔雄辩既不能说服我，也不会让我满意。我来自密苏里州，你必须让我看看。） 针对诊断者而言，这并不是一种怀疑，而只是一种真实而有用的需求，以便让他们看到的是与你看到的原始证据尽可能一致的东西，而不是你的猜测与归纳的结论。所以，大方的展示给我们看吧！\n按发生时间先后列出问题症状 问题发生前的一系列操作，往往就是对找出问题最有帮助的线索。因此，你的说明里应该包含你的操作步骤，以及机器和软件的反应，直到问题发生。在命令行处理的情况下，提供一段操作记录（例如运行脚本工具所生成的），并引用相关的若干行（如 20 行）记录会非常有帮助。\n如果挂掉的程序有诊断选项（如 -v 的详述开关），试着选择这些能在记录中增加调试信息的选项。记住，多不等于好。试着选取适当的调试级别以便提供有用的信息而不是让读者淹没在垃圾中。\n如果你的说明很长（如超过四个段落），在开头简述问题，接下来再按时间顺序详述会有所帮助。这样黑客们在读你的记录时就知道该注意哪些内容了。\n描述目标而不是过程 如果你想弄清楚如何做某事（而不是报告一个 Bug），在开头就描述你的目标，然后才陈述重现你所卡住的特定步骤。\n经常寻求技术帮助的人在心中有个更高层次的目标，而他们在自以为能达到目标的特定道路上被卡住了，然后跑来问该怎么走，但没有意识到这条路本身就有问题。结果要费很大的劲才能搞定。\n蠢问题\n我怎样才能从某绘图程序的颜色选择器中取得十六进制的的 RGB 值？\n聪明问题\n我正试着用替换一幅图片的色码（color table）成自己选定的色码，我现在知道的唯一方法是编辑每个色码区块（table slot）， 但却无法从某绘图程序的颜色选择器取得十六进制的的 RGB 值。\n第二种提问法比较聪明，你可能得到像是建议采用另一个更合适的工具的回复。\n别要求使用私人电邮回复 黑客们认为问题的解决过程应该公开、透明，此过程中如果更有经验的人注意到不完整或者不当之处，最初的回复才能够、也应该被纠正。同时，作为提供帮助者可以得到一些奖励，奖励就是他的能力和学识被其他同行看到。\n当你要求私下回复时，这个过程和奖励都被中止。别这样做，让回复者来决定是否私下回答 —— 如果他真这么做了，通常是因为他认为问题编写太差或者太肤浅，以至于对其它人没有兴趣。\n这条规则存在一条有限的例外，如果你确信提问可能会引来大量雷同的回复时，那么这个神奇的提问句会是向我发电邮，我将为论坛归纳这些回复。试着将邮件列表或新闻群组从洪水般的雷同回复中解救出来是非常有礼貌的 —— 但你必须信守诺言。\n清楚明确的表达你的问题以及需求 漫无边际的提问是近乎无休无止的时间黑洞。最有可能给你有用答案的人通常也正是最忙的人（他们忙是因为要亲自完成大部分工作）。这样的人对无节制的时间黑洞相当厌恶，所以他们也倾向于厌恶那些漫无边际的提问。\n如果你明确表述需要回答者做什么（如提供指点、发送一段代码、检查你的补丁、或是其他等等），就最有可能得到有用的答案。因为这会定出一个时间和精力的上限，便于回答者能集中精力来帮你。这么做很棒。\n要理解专家们所处的世界，请把专业技能想像为充裕的资源，而回复的时间则是稀缺的资源。你要求他们奉献的时间越少，你越有可能从真正专业而且很忙的专家那里得到解答。\n所以，界定一下你的问题，使专家花在辨识你的问题和回答所需要付出的时间减到最少，这技巧对你有用答案相当有帮助 —— 但这技巧通常和简化问题有所区别。因此，问我想更好的理解 X，可否指点一下哪有好一点说明？通常比问你能解释一下 X 吗？更好。如果你的代码不能运作，通常请别人看看哪里有问题，比要求别人替你改正要明智得多。\n询问有关代码的问题时 别要求他人帮你调试有问题的代码，不提示一下应该从何入手。张贴几百行的代码，然后说一声：它不能工作会让你完全被忽略。只贴几十行代码，然后说一句：在第七行以后，我期待它显示 \u0026lt;x\u0026gt;，但实际出现的是 \u0026lt;y\u0026gt;比较有可能让你得到回应。\n最有效描述程序问题的方法是提供最精简的 Bug 展示测试用例（bug-demonstrating test case）。什么是最精简的测试用例？那是问题的缩影；一小个程序片段能刚好展示出程序的异常行为，而不包含其他令人分散注意力的内容。怎么制作最精简的测试用例？如果你知道哪一行或哪一段代码会造成异常的行为，复制下来并加入足够重现这个状况的代码（例如，足以让这段代码能被编译/直译/被应用程序处理）。如果你无法将问题缩减到一个特定区块，就复制一份代码并移除不影响产生问题行为的部分。总之，测试用例越小越好（查看话不在多而在精一节）。\n一般而言，要得到一段相当精简的测试用例并不太容易，但永远先尝试这样做的是种好习惯。这种方式可以帮助你了解如何自行解决这个问题 —— 而且即使你的尝试不成功，黑客们也会看到你在尝试取得答案的过程中付出了努力，这可以让他们更愿意与你合作。\n如果你只是想让别人帮忙审查（Review）一下代码，在信的开头就要说出来，并且一定要提到你认为哪一部分特别需要关注以及为什么。\n别把自己家庭作业的问题贴上来 黑客们很擅长分辨哪些问题是家庭作业式的问题；因为我们中的大多数都曾自己解决这类问题。同样，这些问题得由你来搞定，你会从中学到东西。你可以要求给点提示，但别要求得到完整的解决方案。\n如果你怀疑自己碰到了一个家庭作业式的问题，但仍然无法解决，试试在使用者群组，论坛或（最后一招）在项目的使用者邮件列表或论坛中提问。尽管黑客们会看出来，但一些有经验的使用者也许仍会给你一些提示。\n去掉无意义的提问句 避免用无意义的话结束提问，例如有人能帮我吗？或者这有答案吗？。\n首先：如果你对问题的描述不是很好，这样问更是画蛇添足。\n其次：由于这样问是画蛇添足，黑客们会很厌烦你 —— 而且通常会用逻辑上正确，但毫无意义的回答来表示他们的蔑视， 例如：没错，有人能帮你或者不，没答案。\n一般来说，避免用 是或否、对或错、有或没有类型的问句，除非你想得到是或否类型的回答。\n即使你很急也不要在标题写紧急 这是你的问题，不是我们的。宣称紧急极有可能事与愿违：大多数黑客会直接删除无礼和自私地企图即时引起关注的问题。更严重的是，紧急这个字（或是其他企图引起关注的标题）通常会被垃圾信过滤器过滤掉 —— 你希望能看到你问题的人可能永远也看不到。\n有半个例外的情况是，如果你是在一些很高调，会使黑客们兴奋的地方，也许值得这样去做。在这种情况下，如果你有时间压力，也很有礼貌地提到这点，人们也许会有兴趣回答快一点。\n当然，这风险很大，因为黑客们兴奋的点多半与你的不同。譬如从 NASA 国际空间站（International Space Station）发这样的标题没有问题，但用自我感觉良好的慈善行为或政治原因发肯定不行。事实上，张贴诸如紧急：帮我救救这个毛绒绒的小海豹！肯定让你被黑客忽略或惹恼他们，即使他们认为毛绒绒的小海豹很重要。\n如果你觉得这点很不可思议，最好再把这份指南剩下的内容多读几遍，直到你弄懂了再发文。\n礼多人不怪，而且有时还很有帮助 彬彬有礼，多用请和谢谢您的关注，或谢谢你的关照。让大家都知道你对他们花时间免费提供帮助心存感激。\n坦白说，这一点并没有比清晰、正确、精准并合法语法和避免使用专用格式重要（也不能取而代之）。黑客们一般宁可读有点唐突但技术上鲜明的 Bug 报告，而不是那种有礼但含糊的报告。（如果这点让你不解，记住我们是按问题能教给我们什么来评价问题的价值的）\n然而，如果你有一串的问题待解决，客气一点肯定会增加你得到有用回应的机会。\n（我们注意到，自从本指南发布后，从资深黑客那里得到的唯一严重缺陷反馈，就是对预先道谢这一条。一些黑客觉得先谢了意味着事后就不用再感谢任何人的暗示。我们的建议是要么先说先谢了，然后事后再对回复者表示感谢，或者换种方式表达感激，譬如用谢谢你的关注或谢谢你的关照。）\n问题解决后，加个简短的补充说明 问题解决后，向所有帮助过你的人发个说明，让他们知道问题是怎样解决的，并再一次向他们表示感谢。如果问题在新闻组或者邮件列表中引起了广泛关注，应该在那里贴一个说明比较恰当。\n最理想的方式是向最初提问的话题回复此消息，并在标题中包含已修正，已解决或其它同等含义的明显标记。在人来人往的邮件列表里，一个看见讨论串问题 X和问题 X - 已解决的潜在回复者就明白不用再浪费时间了（除非他个人觉得问题 X的有趣），因此可以利用此时间去解决其它问题。\n补充说明不必很长或是很深入；简单的一句你好，原来是网线出了问题！谢谢大家 – Bill比什么也不说要来的好。事实上，除非结论真的很有技术含量，否则简短可爱的小结比长篇大论更好。说明问题是怎样解决的，但大可不必将解决问题的过程复述一遍。\n对于有深度的问题，张贴调试记录的摘要是有帮助的。描述问题的最终状态，说明是什么解决了问题，在此之后才指明可以避免的盲点。避免盲点的部分应放在正确的解决方案和其它总结材料之后，而不要将此信息搞成侦探推理小说。列出那些帮助过你的名字，会让你交到更多朋友。\n除了有礼貌和有内涵以外，这种类型的补充也有助于他人在邮件列表/新闻群组/论坛中搜索到真正解决你问题的方案，让他们也从中受益。\n至少，这种补充有助于让每位参与协助的人因问题的解决而从中得到满足感。如果你自己不是技术专家或者黑客，那就相信我们，这种感觉对于那些你向他们求助的大师或者专家而言，是非常重要的。问题悬而未决会让人灰心；黑客们渴望看到问题被解决。好人有好报，满足他们的渴望，你会在下次提问时尝到甜头。\n思考一下怎样才能避免他人将来也遇到类似的问题，自问写一份文件或加个常见问题（FAQ）会不会有帮助。如果是的话就将它们发给维护者。\n在黑客中，这种良好的后继行动实际上比传统的礼节更为重要，也是你如何透过善待他人而赢得声誉的方式，这是非常有价值的资产。\n如何解读答案 RTFM 和 STFW：如何知道你已完全搞砸了 有一个古老而神圣的传统：如果你收到RTFM （Read The Fucking Manual）的回应，回答者认为你应该去读他妈的手册。当然，基本上他是对的，你应该去读一读。\nRTFM 有一个年轻的亲戚。如果你收到STFW（Search The Fucking Web）的回应，回答者认为你应该到他妈的网上搜索。那人多半也是对的，去搜索一下吧。（更温和一点的说法是 Google 是你的朋友！）\n在论坛，你也可能被要求去爬爬论坛的旧文。事实上，有人甚至可能热心地为你提供以前解决此问题的讨论串。但不要依赖这种关照，提问前应该先搜索一下旧文。\n通常，用这两句之一回答你的人会给你一份包含你需要内容的手册或者一个网址，而且他们打这些字的时候也正在读着。这些答复意味着回答者认为\n你需要的信息非常容易获得； 你自己去搜索这些信息比灌给你，能让你学到更多。 你不应该因此不爽；依照黑客的标准，他已经表示了对你一定程度的关注，而没有对你的要求视而不见。你应该对他祖母般的慈祥表示感谢。\n如果还是搞不懂 如果你看不懂回应，别立刻要求对方解释。像你以前试着自己解决问题时那样（利用手册，FAQ，网络，身边的高手），先试着去搞懂他的回应。如果你真的需要对方解释，记得表现出你已经从中学到了点什么。\n比方说，如果我回答你：看来似乎是 zentry 卡住了；你应该先清除它。，然后，这是一个很糟的后续问题回应：zentry 是什么？ 好的问法应该是这样：哦~~~我看过说明了但是只有 -z 和 -p 两个参数中提到了 zentries，而且还都没有清楚的解释如何清除它。你是指这两个中的哪一个吗？还是我看漏了什么？\n处理无礼的回应 很多黑客圈子中看似无礼的行为并不是存心冒犯。相反，它是直接了当，一针见血式的交流风格，这种风格更注重解决问题，而不是使人感觉舒服而却模模糊糊。\n如果你觉得被冒犯了，试着平静地反应。如果有人真的做了出格的事，邮件列表、新闻群组或论坛中的前辈多半会招呼他。如果这没有发生而你却发火了，那么你发火对象的言语可能在黑客社区中看起来是正常的，而你将被视为有错的一方，这将伤害到你获取信息或帮助的机会。\n另一方面，你偶尔真的会碰到无礼和无聊的言行。与上述相反，对真正的冒犯者狠狠地打击，用犀利的语言将其驳得体无完肤都是可以接受的。然而，在行事之前一定要非常非常的有根据。纠正无礼的言论与开始一场毫无意义的口水战仅一线之隔，黑客们自己莽撞地越线的情况并不鲜见。如果你是新手或外人，避开这种莽撞的机会并不高。如果你想得到的是信息而不是消磨时光，这时最好不要把手放在键盘上以免冒险。\n（有些人断言很多黑客都有轻度的自闭症或亚斯伯格综合症，缺少用于润滑人类社会正常交往所需的神经。这既可能是真也可能是假的。如果你自己不是黑客，兴许你认为我们脑袋有问题还能帮助你应付我们的古怪行为。只管这么干好了，我们不在乎。我们喜欢我们现在这个样子，并且通常对病患标记都有站得住脚的怀疑）。\nJeff Bigler 的观察总结和这个相关也值得一读 (tact filters)。\n在下一节，我们会谈到另一个问题，当你行为不当时所会受到的冒犯。\n如何避免扮演失败者 在黑客社区的论坛中有那么几次你可能会搞砸 —— 以本指南所描述到的或类似的方式。而你会在公开场合中被告知你是如何搞砸的，也许攻击的言语中还会带点夹七夹八的颜色。\n这种事发生以后，你能做的最糟糕的事莫过于哀嚎你的遭遇、宣称被口头攻击、要求道歉、高声尖叫、憋闷气、威胁诉诸法律、向其雇主报怨、忘了关马桶盖等等。相反地，你该这么做：\n熬过去，这很正常。事实上，它是有益健康且合理的。\n社区的标准不会自行维持，它们是通过参与者积极而公开地执行来维持的。不要哭嚎所有的批评都应该通过私下的邮件传送，它不是这样运作的。当有人评论你的一个说法有误或者提出不同看法时，坚持声称受到个人攻击也毫无益处，这些都是失败者的态度。\n也有其它的黑客论坛，受过高礼节要求的误导，禁止参与者张贴任何对别人帖子挑毛病的消息，并声称如果你不想帮助用户就闭嘴。 结果造成有想法的参与者纷纷离开，这么做只会使它们沦为毫无意义的唠叨与无用的技术论坛。\n夸张的讲法是：你要的是“友善”（以上述方式）还是有用？两个里面挑一个。\n记着：当黑客说你搞砸了，并且（无论多么刺耳）告诉你别再这样做时，他正在为关心你和他的社区而行动。对他而言，不理你并将你从他的生活中滤掉更简单。如果你无法做到感谢，至少要表现得有点尊严，别大声哀嚎，也别因为自己是个有戏剧性超级敏感的灵魂和自以为有资格的新来者，就指望别人像对待脆弱的洋娃娃那样对你。\n有时候，即使你没有搞砸（或者只是在他的想像中你搞砸了），有些人也会无缘无故地攻击你本人。在这种情况下，抱怨倒是真的会把问题搞砸。\n这些来找麻烦的人要么是毫无办法但自以为是专家的不中用家伙，要么就是测试你是否真会搞砸的心理专家。其它读者要么不理睬，要么用自己的方式对付他们。这些来找麻烦的人在给他们自己找麻烦，这点你不用操心。\n也别让自己卷入口水战，最好不要理睬大多数的口水战 -- 当然，这是在你检验它们只是口水战，并且未指出你有搞砸的地方，同时也没有巧妙地将问题真正的答案藏于其后（这也是有可能的）。\n不该问的问题 以下是几个经典蠢问题，以及黑客没回答时心中所想的：\n问题：我能在哪找到 X 程序或 X 资源？\n问题：我怎样用 X 做 Y？\n问题：如何设定我的 shell 提示？\n问题：我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗？\n问题：我的程序/设定/SQL 语句没有用\n问题：我的 Windows 电脑有问题，你能帮我吗？\n问题：我的程序不会动了，我认为系统工具 X 有问题\n问题：我在安装 Linux（或者 X ）时有问题，你能帮我吗？\n问题：我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢？\n问题：我能在哪找到 X 程序或 X 资源？\n回答：就在我找到它的地方啊，白痴 —— 搜索引擎的那一头。天哪！难道还有人不会用 Google 吗？\n问题：我怎样用 X 做 Y？\n回答：如果你想解决的是 Y ，提问时别给出可能并不恰当的方法。这种问题说明提问者不但对 X 完全无知，也对 Y 要解决的问题糊涂，还被特定形势禁锢了思维。最好忽略这种人，等他们把问题搞清楚了再说。\n问题：如何设定我的 shell 提示？？\n回答：如果你有足够的智慧提这个问题，你也该有足够的智慧去 RTFM，然后自己去找出来。\n问题：我可以用 Bass-o-matic 文件转换工具将 AcmeCorp 档案转换为 TeX 格式吗？\n回答：试试看就知道了。如果你试过，你既知道了答案，就不用浪费我的时间了。\n问题：我的{程序/设定/SQL 语句}不工作\n回答：这不算是问题吧，我对要我问你二十个问题才找得出你真正问题的问题没兴趣 —— 我有更有意思的事要做呢。在看到这类问题的时候，我的反应通常不外如下三种\n你还有什么要补充的吗？ 真糟糕，希望你能搞定。 这关我有什么屁事？ 问题：我的 Windows 电脑有问题，你能帮我吗？\n回答：能啊，扔掉微软的垃圾，换个像 Linux 或 BSD 的开源操作系统吧。\n注意：如果程序有官方版 Windows 或者与 Windows 有互动（如 Samba），你可以问与 Windows 相关的问题， 只是别对问题是由 Windows 操作系统而不是程序本身造成的回复感到惊讶， 因为 Windows 一般来说实在太烂，这种说法通常都是对的。\n问题：我的程序不会动了，我认为系统工具 X 有问题\n回答：你完全有可能是第一个注意到被成千上万用户反复使用的系统调用与函数库档案有明显缺陷的人，更有可能的是你完全没有根据。不同凡响的说法需要不同凡响的证据，当你这样声称时，你必须有清楚而详尽的缺陷说明文件作后盾。\n问题：我在安装 Linux（或者 X ）时有问题，你能帮我吗？\n回答：不能，我只有亲自在你的电脑上动手才能找到毛病。还是去找你当地的 Linux 使用群组者寻求实际的指导吧（你能在这儿找到使用者群组的清单）。\n注意：如果安装问题与某 Linux 的发行版有关，在它的邮件列表、论坛或本地使用者群组中提问也许是恰当的。此时，应描述问题的准确细节。在此之前，先用 Linux 和所有被怀疑的硬件作关键词仔细搜索。\n问题：我怎么才能破解 root 帐号/窃取 OP 特权/读别人的邮件呢？\n回答：想要这样做，说明了你是个卑鄙小人；想找个黑客帮你，说明你是个白痴！\n好问题与蠢问题 最后，我将透过举一些例子，来说明怎样聪明的提问；同一个问题的两种问法被放在一起，一种是愚蠢的，另一种才是明智的。\n蠢问题：\n我可以在哪儿找到关于 Foonly Flurbamatic 的资料？\n这种问法无非想得到 STFW 这样的回答。\n聪明问题：\n我用 Google 搜索过 \u0026quot;Foonly Flurbamatic 2600\u0026quot;，但是没找到有用的结果。谁知道上哪儿去找对这种设备编程的资料？\n这个问题已经 STFW 过了，看起来他真的遇到了麻烦。\n蠢问题：\n我从 foo 项目找来的源码没法编译。它怎么这么烂？\n他觉得都是别人的错，这个傲慢自大的提问者。\n聪明问题：\nfoo 项目代码在 Nulix 6.2 版下无法编译通过。我读过了 FAQ，但里面没有提到跟 Nulix 有关的问题。这是我编译过程的记录，我有什么做的不对的地方吗？\n提问者已经指明了环境，也读过了 FAQ，还列出了错误，并且他没有把问题的责任推到别人头上，他的问题值得被关注。\n蠢问题：\n我的主机板有问题了，谁来帮我？\n某黑客对这类问题的回答通常是：好的，还要帮你拍拍背和换尿布吗？，然后按下删除键。\n聪明问题：\n我在 S2464 主机板上试过了 X 、 Y 和 Z ，但没什么作用，我又试了 A 、 B 和 C 。请注意当我尝试 C 时的奇怪现象。显然 florbish 正在 grommicking，但结果出人意料。通常在 Athlon MP 主机板上引起 grommicking 的原因是什么？有谁知道接下来我该做些什么测试才能找出问题？\n这个家伙，从另一个角度来看，值得去回答他。他表现出了解决问题的能力，而不是坐等天上掉答案。\n在最后一个问题中，注意告诉我答案和给我启示，指出我还应该做什么诊断工作之间微妙而又重要的区别。\n事实上，后一个问题源自于 2001 年 8 月在 Linux 内核邮件列表（lkml）上的一个真实的提问。我（Eric）就是那个提出问题的人。我在 Tyan S2464 主板上观察到了这种无法解释的锁定现象，列表成员们提供了解决这一问题的重要信息。\n通过我的提问方法，我给了别人可以咀嚼玩味的东西；我设法让人们很容易参与并且被吸引进来。我显示了自己具备和他们同等的能力，并邀请他们与我共同探讨。通过告诉他们我所走过的弯路，以避免他们再浪费时间，我也表明了对他们宝贵时间的尊重。\n事后，当我向每个人表示感谢，并且赞赏这次良好的讨论经历的时候， 一个 Linux 内核邮件列表的成员表示，他觉得我的问题得到解决并非由于我是这个列表中的名人，而是因为我用了正确的方式来提问。\n黑客从某种角度来说是拥有丰富知识但缺乏人情味的家伙；我相信他是对的，如果我像个乞讨者那样提问，不论我是谁，一定会惹恼某些人或者被他们忽视。他建议我记下这件事，这直接导致了本指南的出现。\n如果得不到回答 如果仍得不到回答，请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。没有回应不代表你被忽视，虽然不可否认这种差别很难区分。\n总的来说，简单的重复张贴问题是个很糟的点子。这将被视为无意义的喧闹。有点耐心，知道你问题答案的人可能生活在不同的时区，可能正在睡觉，也有可能你的问题一开始就没有组织好。\n你可以通过其他渠道获得帮助，这些渠道通常更适合初学者的需要。\n有许多网上的以及本地的使用者群组，由热情的软件爱好者（即使他们可能从没亲自写过任何软件）组成。通常人们组建这样的团体来互相帮助并帮助新手。\n另外，你可以向很多商业公司寻求帮助，不论公司大还是小。别为要付费才能获得帮助而感到沮丧！毕竟，假使你的汽车发动机汽缸密封圈爆掉了 —— 完全可能如此 —— 你还得把它送到修车铺，并且为维修付费。就算软件没花费你一分钱，你也不能强求技术支持总是免费的。\n对像是 Linux 这种大众化的软件，每个开发者至少会对应到上万名使用者。根本不可能由一个人来处理来自上万名使用者的求助电话。要知道，即使你要为这些协助付费，和你所购买的同类软件相比，你所付出的也是微不足道的（通常封闭源代码软件的技术支持费用比开源软件的要高得多，且内容也没那么丰富）。\n如何更好地回答问题 态度和善一点。问题带来的压力常使人显得无礼或愚蠢，其实并不是这样。\n对初犯者私下回复。对那些坦诚犯错之人没有必要当众羞辱，一个真正的新手也许连怎么搜索或在哪找常见问题都不知道。\n如果你不确定，一定要说出来！一个听起来权威的错误回复比没有还要糟，别因为听起来像个专家很好玩，就给别人乱指路。要谦虚和诚实，给提问者与同行都树个好榜样。\n如果帮不了忙，也别妨碍他。不要在实际步骤上开玩笑，那样也许会毁了使用者的设置 —— 有些可怜的呆瓜会把它当成真的指令。\n试探性的反问以引出更多的细节。如果你做得好，提问者可以学到点东西 —— 你也可以。试试将蠢问题转变成好问题，别忘了我们都曾是新手。\n尽管对那些懒虫抱怨一声 RTFM 是正当的，能指出文件的位置（即使只是建议个 Google 搜索关键词）会更好。\n如果你决定回答，就请给出好的答案。当别人正在用错误的工具或方法时别建议笨拙的权宜之计（wordaround），应推荐更好的工具，重新界定问题。\n正面的回答问题！如果这个提问者已经很深入的研究而且也表明已经试过 X 、 Y 、 Z 、 A 、 B 、 C 但没得到结果，回答 试试看 A 或是 B 或者 试试 X 、 Y 、 Z 、 A 、 B 、 C 并附上一个链接一点用都没有。\n帮助你的社区从问题中学习。当回复一个好问题时，问问自己如何修改相关文件或常见问题文件以免再次解答同样的问题？，接着再向文件维护者发一份补丁。\n如果你是在研究一番后才做出的回答，展现你的技巧而不是直接端出结果。毕竟授人以鱼不如授人以渔。\n相关资源 如果你需要个人电脑、Unix 系统和网络如何运作的基础知识，参阅 Unix 系统和网络基本原理。\n当你发布软件或补丁时，试着按软件发布实践操作。\n鸣谢 Evelyn Mitchel 贡献了一些愚蠢问题例子并启发了编写如何更好地回答问题这一节， Mikhail Ramendik 贡献了一些特别有价值的建议和改进。\n","date":"2005-10-08","description":"","lastmod":"2005-10-08T03:20:04Z","slug":"howtoask-cn","tags":[],"title":"提问的智慧","url":"https://blog.zengrong.net/page/howtoask-cn/"},{"categories":[],"content":"提问的智慧\n在技术论坛提问的时候，为什么没人理你呢？\n花10分钟时间看看把，绝对对你有帮助。\n英文版： How To Ask Questions The Smart Way\n中文版： 提问的智慧\n有时候，你可能会收到这样的回答：LMGTFY1，不要生气，他们是善意的。\n(abbreviation) Let Me Google That For You\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2005-10-08","description":"","lastmod":"2005-10-08T03:20:04Z","slug":"howtoask","tags":[],"title":"提问的智慧","url":"https://blog.zengrong.net/howtoask/"},{"categories":["news"],"content":"这就是传说中的Flash Communication Server 2.0！可惜，当我下载Beta版的时候，却得到这样的提示：\nAttention!\nSorry, the Flash Media Server Public Beta is only available to persons residing in USA, Japan or Europe. Based on the information you provided in your Macromedia Membership profile, we have determined that you do not meet this requirement.\n这真的是很过分！还好，我可以先在这里看看最新的文档，再决定是否试用。\n顺便说一句，如果想下载Beta，办法也很简单，只需要登入Macromedia网站后进入Account Details选项更改一下国籍即可：\n","date":"2005-09-30","description":"","lastmod":"2005-09-30T13:51:01Z","slug":"flash-media-server","tags":["fms"],"title":"Flash Media Server 2 Beta","url":"https://blog.zengrong.net/post/flash-media-server/"},{"categories":["web"],"content":"目前上Codex需要代理服务器。我正在使用的：210.0.200.4:80\nConditional_Tags\nTheme_Development\nTemplates\nhttp://www.coolcode.cn/\n大量的原创WordPress插件和文章 http://www.lesterchan.net/\n大量wordPress插件 基于WordPress的Flash Blog，开源 桑林志的中文WordPress资源 探花@Tinn Walk 之 我使用的wordpress 插件 Blogging Pro China 给wordpress增加quicktags按钮的方法 Quicktags Changing the WordPress Quicktag Buttons 如何使用wp-grins插件 The Tamba2 WordPress Guides ","date":"2005-09-30","description":"","lastmod":"2005-09-30T02:34:16Z","slug":"wordpress-link","tags":["wordpress"],"title":"常用的WordPress链接","url":"https://blog.zengrong.net/post/wordpress-link/"},{"categories":["technology"],"content":"chat Union项目中，使用Accordion组件显示主持人列表和客人列表，这个组件放在一个Movie Clip中，实例名称为actorListac。这个Movie Clip关联ActorList类。它将2个List组件作为自己的child Symbol。一个实例名称为emceelist，一个实例名称为roomlist。\nActorList类的主要内容如下：\n1 import mx.controls.List; 2 import mx.containers.Accordion; 3 class ActorList { 4\tprivate var actorListac:Accordion; 5\tfunction ActorList(){ 6\t} 7\tprivate function onLoad(){ 8\tvar accw = actorListac.width-2; 9\tvar acch = actorListac.height-22*2;\t//每个标题22像素高，共两2个标题 10\tactorListac.roomlist.setSize(accw,acch); 11\tactorListac.emceelist.setSize(accw,acch); 12\t} 13\tpublic function reFresh(arr:Array, at:String):Void{ 14\t//at:要刷新的列表类型，值为emcee或room 15\tactorListac[at+\u0026#34;list\u0026#34;].removeAll(); 16\tfor(var i in arr){ 17\tactorListac[at+\u0026#34;list\u0026#34;].addItem(arr[i]); 18\t} 19\t} 20} 编译的时候出现了错误：\n错误 第 26 行: 没有名为'roomlist'的属性。 actorListac.roomlist.setSize(accw,acch);\n这是很让人费解的。因为使用调试器查看程序结构的时候，明明是可以看到roomlist是处于actorListac之中，但却不能够访问？\n为了确定Accordion组件确实可以使用这种访问方式，我做了一个简单程序进行测试：\n1var accw = actorListac.width-2; 2var acch = actorListac.height-22*2;\t//每个标题22像素高，共两2个标题 3actorListac.list1.setSize(accw,acch); 4actorListac.list2.setSize(accw,acch); 仅就这个程序而言，我的访问方式没有一点问题。\n无奈改了一种访问方式，将 actorListac.roomlist.setSize(accw,acch); 改为了 actorListac[\u0026quot;roomlist\u0026quot;].setSize(accw,acch); ，没想到居然就可以了！\n","date":"2005-09-29","description":"","lastmod":"2005-09-29T07:55:43Z","slug":"accordion01","tags":["actionscript"],"title":"在Class中的使用Accordion的child Symbol问题","url":"https://blog.zengrong.net/post/accordion01/"},{"categories":["technology"],"content":":em26: chat Union站点开发过程中，碰到在线列表问题：\n在主持人方面，应该能够看到自己房间中的所有客人；而在客人方面，应该能够看到自己所在房间的客人，同时也能看到所有在线的主持人一方便切换房间。对于这个功能，我打算这样实现：\n每个主持人房间是一个应用程序实例，都有自己的共享对象（SO），保存自己房间中的在线用户名称。当用户注销或离开，就更新SO并通知当前主持人和所有在此房间中的客人更新自己的在线列表。 而对于主持人在线列表，则不能放在主持人自己的房间中。因为所有的客人都是共享同一个主持人在线列表的，而这些客人可能处于不同的房间之中。我考虑在应用程序中使用一个单独的（common）实例保存所有主持人的在线状态，所有的客人都需要连接到online实例来获取主持人在线状态 对于这个设计，我首先想到的是使用两个NetConnection，一个连到主持人房间实例，一个连接到common实例，但是这样的设计未免麻烦，两个NetConnection都要监视连接状态，增加了客户端程序的复杂性。\n突然想到代理共享对象(proxy shared object)，使用它，连接到common实例的工作就可以交给服务器端，与客户端无关了。\n以下是main.acs的内容：\n1application.onAppStart = function(){ 2nc = new NetConnection(); 3nc.connect(\u0026#34;rtmp://localhost/proxy/common\u0026#34;); 4nc.onStatus = function(e){ 5trace(e.code+\u0026#34; - \u0026#34;+e.description); 6} 7so = SharedObject.get(\u0026#34;proxy\u0026#34;,true,nc); 8so.onSync = function(e){ 9trace(\u0026#34;so更新事件：\u0026#34;+e[0].code); 10} 11so.onStatus = function(e){ 12trace(\u0026#34;so状态事件:\u0026#34;+e.code); 13} 14} 需要注意的一点，是nc.connect(\u0026quot;rtmp://localhost/proxy/common\u0026quot;);，这里的连接地址必须是绝对路径。我最早的尝试是rtmp:/proxy/common，结果是不能连接成功。\n以下是客户端的脚本：\n1nc = new NetConnection(); 2nc.connect(\u0026#34;rtmp://localhost/proxy/room1\u0026#34;); 3myso = SharedObject.getRemote(\u0026#34;proxy\u0026#34;, nc.uri, true); 4myso.connect(nc); 一开始我犯了一个错误，认为客户端连接的共享对象应该是服务器端的代理共享对象的实例名称，因此我的连接语句是这样\n1myso = SharedObject.getRemote(\u0026#34;so\u0026#34;, nc.uri, true); 结果在app Inspector面板中发现有proxy/room1实例中竟然有两个共享对象，一个是so，一个是proxy。其实，客户端连接的共享对象应该与服务器端连接的共享对象名称相同。\n测试后发现，虽然服务器提示nc连接成功，但是却看不到共享对象被写入。郁闷了好长时间，考虑了多种情况，最后发现问题在于是代理共享对象所在的应用程序与“雇主”所在的应用程序是同一个。其实早就该想到这点，帮助文档中虽然没有明确强调，但是也已经指明了。\n将main.asc代码改为如下所示，可以看到“雇主”的共享对象被代理共享对象写入了。\n1application.onAppStart = function(){ 2...... 3nc.connect(\u0026#34;rtmp://localhost/proxy1/common\u0026#34;); 4...... 5} ","date":"2005-09-28","description":"","lastmod":"2005-09-28T07:56:41Z","slug":"proxy-sharedobject","tags":["fms","sharedobject"],"title":"代理共享对象的使用","url":"https://blog.zengrong.net/post/proxy-sharedobject/"},{"categories":["technology"],"content":"正在扩展shikar的ASPService，想把Flash的Array和Object都直接传给ASP，在ASP端，Flash的Array被转换成ASP的Array，而Object被转换成ASP的Dictionary。\n在检测Flash的数据类型时发现，Object和Array的数据类型都是“object”。代码如下：\n1arr = [3,4,5]; 2obj ={a:3,b:4,c:5}; 3trace(arr); 4trace(obj); 5//返回 object 6//返回 object 虽然这个问题早就知道，但一直没有深究。现在要区分Array和Object，就要进一步研究了。\n再测试：\n1for (var i in arr){ 2trace(typeof(i)); 3trace(i) 4} 返回的值居然是：\nstring 2 string 1 string 0 这说明数组的索引在ActionScript看来实际上不是数字，而是字符串！也就是说，下面这两句代码是等价的：\n1var t = arr[0]; 2var t = arr[\u0026#34;0\u0026#34;]; 为了证明我这个观点，继续测试\n1for (var i in obj){ 2trace(typeof(i)); 3trace(i) 4} 返回：\nstring a string b string c 它于Array所不同的好像就是for in循环返回的顺序，Array是从最大索引值开始递减，而Object则是递增。\n这样看来，在ActionScript中，Array和Object都是作为Object类型看待的，他们的结构应该相同，都使用字符串作为索引（Array可以使用数字索引读取，但内部结构中，索引还是以字符串形式存储的）。\n回到文章开头，要实现我需要的功能，发现Flash端无论是Array或者Object意义已经不大。我还是采用简单的方法：将Array和Object都转换成ASP端的二维数组，第一个是Object标识符，第二个是值。\n","date":"2005-09-24","description":"","lastmod":"2005-09-24T05:27:33Z","slug":"object-array","tags":["actionscript"],"title":"ActionScript中Array和Object的异同","url":"https://blog.zengrong.net/post/object-array/"},{"categories":["technology"],"content":" 解决PHP存取MySQL 4.1乱码问题 MySQL 4.1x 中文乱码问题 WordPress的架设指南 hsuyo blog 'WP 速記' 中文WordPress Smilies in WP (wp中的表情) 如何保持连接的稳定 大量WordPress的样式下载 ","date":"2005-09-24","description":"","lastmod":"2005-09-24T03:26:53Z","slug":"mysql41andwordpress","tags":["cms","mysql","php","wordpress"],"title":"MySQL4.1乱码问题与WordPress","url":"https://blog.zengrong.net/post/mysql41andwordpress/"},{"categories":["others"],"content":"安全经过广州火车站攻略(去广州的必看)\n赵鹏\n初级用户\n积分：2023\n发贴：6331\n来自：福建福州\n注册：2001-04-02\n发表于 2005-09-21 04:18:54\n其一： 千万不要相信任何主动向你打招呼的人。尤其是那些号称是你朋友的 朋友的人，很多情况下，他们都是骗子。 其二：不要在车站 周围打公用电话。私人的公用电话一般都做过手脚，随便一个电话，\n收你几百元，不给就会挨打。车站广州的IC卡电话有的也做过手脚 ，其中也会有各种各样的骗局，因此，最好是自己带手机来广州，或 者根本别在这里打电话。\n其三：千万要拉着你的小孩子的手不放 ，数不清的人贩子在你出站的那一刻起便盯上了你的宝宝。\n其四 ：千万别拣地上的钱包或者值钱的东西。如果有人拣了想和你平分，\n你也千万别以为那是天上掉下来的馅饼。\n其五：尽量不要在车站 广场的地下广场买东西。那里所有的档口都在卖假货，你一旦问了价 钱不买的话，你就休想轻易脱身。\n其六：出站前，把你所有的首 饰都摘掉。小心的耳垂和你的耳环一起被人抢走。\n其七：不要同 情任何人。也许有的人真值得你去同情，但多数情况下，被你同情的 人会反过来伤害你。\n其八：千万要随手拉着你的行李，你的行李 随时都可能不翼而飞。\n其九：不要随便问路。最好在广场打的直 接到你的目的地，因为广州的士比较正规。在广场的左侧是公共汽车 站，你也可以在这里直接乘公共汽车到你的目的地。\n其十：万一 有人捅了你一刀或者几刀，记着打120，救护车最快会在10分钟 内到达。\n增加几条：\n1、如果事先有人接你，小心接你的人是 骗子，他们会克隆一个接人的牌子到出口处把你骗跑！\n2、在广 州任何一个地方打手机一定要到安全的地方去打，否则很有可能会被 抢，打手机的时候，身子要不停的转，注意身边的人！\n3、火车 站里见人就发东西的一定不要接，否则一张地图会要你100元的或 者更多！\n4、一个人一定不要去火车站特别是早晚，否则抢定你 了！\n5、身上的现金一定不要带多了，车票和现金一定要分开放 ，免得钱没了车也上不了啦！\n6、车站里至少有2000名以上 的坏人在广场上！\n7、在广场走路最好找人多的地方走！\n8 、下车的时候最好找人多的地方下！\n9、遇到手拿着注射器的，\n你一定要离远点，别看，小心那人发狂，注射到你的体内。这些人一 般都在进入地下停车室的通道台阶上！\n补充：\n1.看到摩托仔 跟年轻搭客关系密切且眼神不定、四处游弋的，离远点；\n2.不要 在马路上玩弄手机，看时间也不行；\n3.在车站等车退到路基内侧 掏钱包拿钱；\n4.在公共汽车上一手捂住钱包，一手捂住手机，不 要在非空调车靠窗位置打手机；\n5.将行李放在出租车后排时，一 定要将车后门、后窗锁死；\n6.尽量不住在城中村社区，很多人下 班回家后会以为走错了房门；\n7.还是不要买笔记本了；\n8.住 在某些地方天黑以后就不要出门了；\n9.没有医保和寿险的不要在 天黑后管闲事；\n10.不要在街上问路，问警察也没用，没人会告 诉你，有人主动给你带路的，必须马上远离此人；\n11.一定随身 带身份证，可能的话连毕业证也带上，碰到一群壮汉围住你，在斩马 刀砍掉你手指头前一定要及时报上姓名、籍贯，并对其发送消息：“ 大哥们，你们真的是找错人了。”\n12.在街上遇到未成年维族小 同胞立即看顾好你的包；\n13.上了街就提醒自己已身在战场，危 机四伏；\n14.非生活所迫，离开广州是终极方案。\n","date":"2005-09-21","description":"","lastmod":"2005-09-21T00:48:41Z","slug":"guangzhou","tags":[],"title":"【转】安全经过广州火车站攻略(去广州的必看)","url":"https://blog.zengrong.net/post/guangzhou/"},{"categories":["technology"],"content":"我的MySQL本来安装在“G:\\sites\\mysql”路径下。今天突发奇想，将原来的sites文件夹改名为Inetpub，那么MySQL的安装路径也就改为”“G:\\Inetpub\\mysql”了。这样，MySQL服务当然无法成功启动，于是开始下面的漫漫征程：\n1.来到我们可爱的Dos窗口，运行“G:\\Inetpub\\mysql\\bin\\mysqld-nt -remove”，卸载MySQL服务\n2.接着运行“G:\\Inetpub\\mysql\\bin\\mysqld-nt -install MySQL --defaults-file=\u0026quot;G:\\Inetpub\\mysql\\my.ini”重新安装MySQL服务，并指定当前的ini文件为MySQL安装目录下的my.ini\n3.本来以为大功告成，继续执行“net start MySQL”，结果出现“错误1067：进程意外中止”。这个错误可是伴随了我好长时间了，每次安装MySQL都可能碰到，但是不知怎么重装一次就又好了。所以碰到它我并不感到特别奇怪。\n4.去“C:\\winnt”看看，找到一个名为“my.ini”的文件，删掉它，再次启动MySQL服务。错误依然故我……我晕！！！\n5.检查“G:\\Inetpub\\mysql\\my.ini”，看到这样红色的两句，修改basedir和datadir的值，将其改成相应的目录（我已经将其修改为正确的目录）。再次启动MySQL服务（心里面那个紧张呀……），在等待了N\u0026lt;2秒之后，错误继续出现。我的心凉了。\n[mysqld] # The TCP/IP Port the MySQL Server will listen on port=3306 #Path to installation directory. All paths are usually resolved relative to this. basedir=\u0026quot;G:/Inetpub/mysql/\u0026quot; #Path to the database root datadir=\u0026quot;G:/Inetpub/mysql/Data/\u0026quot; 6.使出最后一招，重装MySQL！！！我依然选择安装到“G:\\inetpub\\mysql”目录。当然，保留了原来的“Data”目录，其它的一律删除。\n7.安装完后，一切试用默认设置，可是MySQL依然无法启动！错误还是1067！！！！这个顽固的错误，怎么会这样！！！\n8.查找注册表，删除所有与MySQL有关的键值。我就不信了！每操作一次，我就重启一次Windows！\n9.第三次安装MySQL，错误还是错误，并没有因为我的努力而消失。\n10.我开始烦躁，开始Google，那么多帖子，说的方法都无法解决我的问题。不过倒是给我一点启示，就是在my.ini上做文章。我复制了多份my.ini，分别放在安装目录、Winnt目录、C:\\，我还把mysql的安装目录复制到了C:\\，可是仍然无济于事。\n11.最后一遍安装。我没有更改任何设置，包括安装路径都采用默认的（我极其讨厌MySQL4.1的默认安装路径）。可是，这次，MySQL启动成功了！\n12.我欣喜若狂，我检查了My.ini的值，basedir的值是安装目录，datadir的值是安装目录下的data目录。我立刻卸载了MySQL，再次安装，安装目录为“G:\\inetpub\\mysql”。这次启动不成功。这并没有让我沮丧，我好像找到了问题的根源。\n13.再次卸载，再次安装，安装目录为默认。启动成功后，将my.ini中的datadir的值更改为“G:/inetpub/mysql/data/”，重新启动MySQL，不成功！！！这说明，MySQL的启动失败的原因是数据库或者数据库路径问题！数据库路径当然是没有问题的，所以我怀疑数据库。\n14.进入到data目录中，发现data目录下有许多文件，删除根目录下的所有文件，仅保留子文件夹（每个文件夹就是一个数据库，这个当然是不能删除的），然后启动MySQL服务。终于成功了。\n15.google上找到的相关资源迷惑了我的眼睛。我在My.ini上耗费了太多的时间，却没有注意数据库的问题。也许是上一个版本留下了太多与路径相关的数据导致MySQL启动失败？可能。无论如何，我已经解决了这个该死的1067错误了。\n以下为网友评论\nJeff(210.72.237.66)在 2005年9月20日18:05星期二 评论: [删除] [回复] 非常感谢你的经验，否则我还不知道怎么解决！ 另外再补充一下我的发现： 我的数据库中有InnoDB类型的表，所以不能将/mysql/data/ibdata1文件删除，否则数据库中InnoDB类型的表都不能访问，所以需要保留。其它文件删除。\n","date":"2005-08-21","description":"","lastmod":"2005-08-21T15:04:56Z","slug":"mysql1067","tags":["mysql","server"],"title":"mysql启动 1067错误","url":"https://blog.zengrong.net/post/mysql1067/"},{"categories":["tutorial"],"content":"8月8日开始的面向全省教师的“学科网站制作培训班（点这里查看讲义）”终于结束了，老师们的热情让我感动，同时也感到肩上担子的沉重。老师需要快速制作出网站，但究竟怎样的平台能够满足他们的需求？\n无疑，Mambo是我见过的最强大的CMS系统，但是对于这个系统，没有一个老师不觉得难。暂且不说模板的制作，单是网站架构的搭建，插件的下载安装就够忙活一阵子了。而且，Mambo基于PHP＋MySQL，让老师们首先搭建一个支持PHP＋MySQL的开发平台就必须费好大功夫。还有Mambo的备份、上传等等工作，都由于MySQL的原因变得更加复杂。虽然我提供了文字和视频教程，但还是不能保证老师们在培训班课程结束之后能否自行解决这些问题。\n再说风讯。它确实是国内最优秀的基于ASP的CMS。喜欢他的原因是自定义标签。用动易2年多，看到参数超过20个的标签就想吐。开始，老师们对这个可爱的东东还挺喜欢，认为它“比Mambo简单多了”。但是到了后来，又不满足风讯不带留言簿、没有整合论坛、模板太少.......需要自定义的功能太多......\n其实，就目前的情况看，要用好一个CMS系统，必须熟悉HTML和CSS，否则是不可能出制作符合自己口味的模板来的。而我们的老师又没有那么多时间和精力去学习这些和课堂“无关”的东西。我本人也不赞成老师们为了制作网站而去学习这些东西。毕竟网站是为了教学而用，为教学服务，应该将更多的精力放到网站内容的组织已经如何迎合学生的需求上去。\n我的观点是：不论是制作学科网站、学校网站还是教师个人网站，都以够用就好为原则，将主要精力放在网站内容的组织上。对于网站的美化，多找几个模板把，总有适合自己的。\n","date":"2005-08-15","description":"","lastmod":"2005-08-15T06:47:05Z","slug":"teacher_site","tags":["training"],"title":"学科网站制作培训班结束的思考","url":"https://blog.zengrong.net/post/teacher_site/"},{"categories":["technology"],"content":"使用NetConnection.getRemote()方法可以得到对于Flash Communication Server永久的服务器端ShareObject(SO)。这种永久的SO实际上是以一个二进制文件的形式存在于服务器上的，扩展名为fso。但是据我对此文件的观察，当客户端或者服务器程序对SO值进行了修改、添加、删除等操作后，fso文件并不会立刻更新。经过多次的实验发现，FCS会在以下某种情况发生的时候更新fso文件：\n客户端NC关闭 服务器端Client关闭 服务器端Application退出 调用SharedObject.flush()方法 调用SharedObject.close()方法 ","date":"2005-07-26","description":"","lastmod":"2005-07-26T07:21:03Z","slug":"soflush","tags":["fcs","fms","sharedobject"],"title":"Flash Communication Server并不会将修改的SO值立刻写入文件","url":"https://blog.zengrong.net/post/soflush/"},{"categories":["technology"],"content":"最近为了方便多人聊天室项目的开发，写了一个查看Flash Communication Server端ShareObject的小程序。\n请使用下面的链接升级 Flash Player 到最新版本：\n安装最新的Flash Player插件\n1 文件 ","date":"2005-07-25","description":"","lastmod":"2005-07-25T07:03:38Z","slug":"fsoviewer","tags":["fcs","flash","fms","sharedobject"],"title":"FSO查看器程序","url":"https://blog.zengrong.net/post/fsoviewer/"},{"categories":["others"],"content":"2005 CWT-4 全国武汉话四级考寺\n第一部分　听力　（略，哪过要考，来早我就行）\n第二部分　单选－词汇(2*15)\n1.”老特”是什么意思？\nA.老特产B.老特务C.发语词，无实际意思D.爸爸\n2.”老俩”是什么意思？\nA.老两口B.妈妈C.感叹词D.呵呵\n3.”蛮扎实”是什么意思？\nA.很结实B.很厉害C.很强D.垃圾\n4.”灶妈子”是什么意思？\nA.蟑螂B.赵家的阿姨C.罩子D.妈妈\n5.”麻木”是什么意思？\nA.麻木不仁B.人力三轮车C.木头D.森林\n6.”外外”是什么意思？\nA.外国人B.外星人C.外甥D.外甥女\n7.”灵醒”是什么意思？\nA.神灵显灵B.整洁、好看，也有聪明之意C.神气活现D.睡醒了\n8.”豆里”是什么意思？\nA.里面B.大豆C.黄豆D.豆子坏了\n9.”称透”是什么意思？\nA.称呼太客气了B.透明C.称职D.整洁，好看\n10.”浮子”是什么意思？\nA.一位武汉的古代名人B.孔夫子C.毛巾抹布D.测浮力浮球\n11.”晕”是什么意思？\nA.慢性子B.形容很吃惊C.形容很失望D.头晕\n12.”鬼款/瞎款”是什么意思？\nA.鬼吓人B.胡说乱讲C.付款D.发语词,无实际意思\n13.”嘀哆”是什么意思？\nA.逗小孩的话B.奇多(以前一种食品)C.感叹词D.罗唆烦人\n14.”装精”是什么意思？\nA.没有什么意思B.强打精神C.特别自以为是D.假的精品\n15.”是说唦”是什么意思？\nA.表示赞同B.实在是说啊C.是在说啊D.没有什么意思\n第三部分 情景(4*5)\n1.如果别人说”我拐子麽昂麽昂厉害……”,你该回答:\nA.那四那四B.打赤巴C.扁担D.条子\n2.如果别人说”7饭聊冒?”,你该回答:\nA.摸司B.7聊啊C.高小D.裹金个摸司\n3.如果别人说”莫不耳我撒?”,你该回答:\nA.对不锯啊,恩,哪敢啊B.明天C.等哈子D.苕吃哈胀\n4.如果别人说”我抬你嘀庄啊”,你该回答:\nA.翘胯子B.岔巴子C.谢了啊D.是说唦\n5.如果别人说”您家屋里的猪养得好肥呀，么时候杀您家?”,你该回答:\nA.饿饭B.明天杀您家C.明天撒D.忽然\n第四部分 翻译(5*5)\n1.他这个人几怀哦!\n2.你这过人好闪哦，勒些都懂咧!\n3.他今藏三不三嘀到我屋里来玩.\n4.天冷勒，你不跟劳资报道，不把自己身体弄小心，不出现在偶面前那就是欠铲\n5.代一过核区妈区低晚上，投兰一资老虎蹦鸟渠来，把李扑代地上但冒把李其他。为冒斯\n？它冒把李其他咧?因为它四回轴低，不其居楼！\n第四部分 阅读(25)\n老张开车克武汉——擂鸟！\n造四司机抖个狠——闪鸟！\n多亏一个武汉伢，\n送克医院钌五针——好鸟！\n老张丢他两活烟，牌子怀鸟他不要，\n他说——我们乐里都是武汉嫩，\n我们乐人讲胃口的很，\n我们乐里冒的乐种嫩，\n擂鸟人那还欣抖狠。\n我们乐里都是武汉嫩，\n我们乐里麻木多嘀很，\n我们过早都吃热干面，\n我们抽淹只抽”硬长城”！\n拐子，借个火~~！\n１．”擂鸟”是抹意思？(2)\n２．热干面是武汉哪方面的特产？(3)\n３．请结活补聪材料以及武汉话分析武汉人的性格特点．(20)\n补聪材料(普通话版):\n入乡随俗学说武汉话\n武汉是中国古代繁华的商埠，近代民主革命的中心，保存着十分丰富的历史文化遗产\n，在漫长的历史岁月中形成了独特的方言“汉腔”。\n武汉话节奏频率快，这跟他们的热情奔放的性格，火爆急躁的脾气有关，当然也跟武\n汉的天气有关。\n你如果初到武汉，又逢火炉盛夏的话，再恰巧碰上一个急脾气的人问路，你会一句都听不懂，不但你急着冒汗，连这个武汉人也跟着毛焦火辣呢。所以呢，想到武汉一游，须得先弄懂一些武汉方言，这就是我下面要说的汉腔了。\n武汉人热情，尤其对待外地人。这种热情在武汉叫作礼性大（蛮讲客气的意思），不过武汉人虽然十分讲礼，却并不虚伪，相反，他们还极为憎恶虚情假意、装模做样，称之为“鬼做”（也叫作秀。故意做作的意思）。所以武汉人逢人便称“您家”（当然吵架时是例外的），相当于北京人的“您”，实际上也是“您”字的音变，不同的是，武汉话的“您家”还可以用于第三人称，比如“他您家”，相当于“他老人家”。有时，一句话说完，也总要带一个“您家”，作为结尾的语气并表示尊敬，也相当于北京人的“您哪”。\n北京人讲究礼数，开口闭口，每句话后面都得跟个“您哪”，不过，在北京人那里，“您”是“您”，“您哪”是“您哪”，一用于称呼，一用于后缀，是不会混乱的。而武汉人则不论是“您”还是“您哪”，通通都是“您家”。结果就闹出这样的笑话来，一个武汉人问：“您家屋里的猪养得好肥呀，么时候杀您家？”对方答：“明天杀您家。”两个人都很客气、讲礼，但结果却两个人都挨了骂。\n呵呵，有时候太讲客气也是会闹出笑话的。\n武汉还喜欢逗散放（善意的开玩笑，捉弄人），所以武汉人的业余生活过得蛮滋润。有名的相声作曲家夏雨田老先生就是正宗的武汉人，他的小品素材都缘于武汉；还有说评书的何祚欢老先生；唱湖北大鼓的张明智（张明智说的是武汉黄陂话）；特别是田克兢的小品，用武汉话说来更是别有一番风味啊。\n武汉人称身材为条子，脸蛋为脉子，比如形容一个女孩漂亮，武汉人会说：这个伢，长得蛮是那个事，又灵醒，条子又正，脉子又好。如果形容年纪又大却爱打扮的人则说：个老菜苔打扮得像个妖精，这大把年纪，还不回去洗了睡。\n这充分说明武汉人耿直，不虚伪，有什么说什么。\n武汉常用的方言还有：起篓子（运气好）、尖板眼（稀奇、古怪）、抬庄（给面子）、过点细（小心，注意）、你黑我（黑同吓）、挑土（代替别人做事）、扎实（厉害）、晕（慢性子）、谍务（故意地）、鬼款（瞎说）、掉底子（丢人）、岔巴子（多管闲事）。等等。\n当然，武汉人因为爽直，心里憋不住话，又加上环境的原因，所以文化程度不高的武汉人有的喜欢骂人。(当然别的城市也是这样啊)比如最常听到也是最通用的就是“个*子养的”，使用频率就好象国骂（***）一样，不过武汉人是不会无缘无故骂人的。其实武汉人是出了名的热心快肠，喜欢交友，而且交友不图名也不图利，只要对脾气，讲义气就行。再者武汉话也不难懂，只要他们说得慢些，而你呢又讲普通话，交流起来还是非常容易的。\n","date":"2005-07-25","description":"","lastmod":"2005-07-25T03:36:00Z","slug":"cwt4","tags":[],"title":"【转】2005 CWT-4 全国武汉话四级考寺","url":"https://blog.zengrong.net/post/cwt4/"},{"categories":["web"],"content":"1\u0026lt;div\u0026gt; 2\u0026lt;img src=\u0026#34;http://www.mydeskcity.com/DESK/NX100/MISS2005_5/MISS2005_5002.JPG\u0026#34; onmousemove=\u0026#39;a.scrollLeft=event.x*2.5-a.offsetLeft-100;a.scrollTop=event.y*2.5-a.offsetTop-100\u0026#39;/\u0026gt; 3\u0026lt;/div\u0026gt; ","date":"2005-07-24","description":"","lastmod":"2005-07-24T05:23:05Z","slug":"bigpic","tags":[],"title":"【转】浏览大图时鼠标感应控制层滚动条的移动","url":"https://blog.zengrong.net/post/bigpic/"},{"categories":["web"],"content":"我们一般都用Marquee标签控制元素的滚动。但是单向的Marquee滚动是不连续的，每滚完一幕，就会出现一次空白。而下面介绍中的滚动则是连续的，毫不间断。 下面小阳为你介绍这是如何实现的。 为了滚动能够“连续”，我们需要将字幕的内容复制多遍，直到内容的高度不小于滚动区高度的两倍。然后我们将溢出的滚动条隐藏掉，用代码控制滚动条向下移动(这时内容将向上移动)。当滚动条滚动到最下方时，理论上不能再往下滚动了，于是我们立刻调整滚动条，将它向上滚动到一个和当前画面一样的位置。结果我们看到的就是连续的滚动了。呵呵，说的就是这么简单，那做起来如何呢？我们看看是如何逐步实现的。\n1\u0026lt;div id=\u0026#34;marquees\u0026#34;\u0026gt; 2\u0026lt;!-- 这些是字幕的内容，你可以任意定义 --\u0026gt; 3\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;链接一\u0026lt;/a\u0026gt; 4\u0026lt;br\u0026gt; \u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;链接二\u0026lt;/a\u0026gt; 5\u0026lt;br\u0026gt; \u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;链接三\u0026lt;/a\u0026gt; 6\u0026lt;br\u0026gt; \u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;链接四\u0026lt;/a\u0026gt; 7\u0026lt;br\u0026gt; \u0026lt;!-- 字幕内容结束 --\u0026gt; 8\u0026lt;/div\u0026gt; 9\u0026lt;!-- 以下是java-script代码 --\u0026gt; 10\u0026lt;script language=\u0026#34;javascript\u0026#34;\u0026gt; 11\u0026lt;!-- 12marqueesHeight=200; //内容区高度 13stopscroll=false; //这个变量控制是否停止滚动 14with(marquees){ 15noWrap=true; //这句表内容区不自动换行 16style.width=0; //于是我们可以将它的宽度设为0，因为它会被撑大 17style.height=marqueesHeight; 18style.overflowY=\u0026#34;hidden\u0026#34;; //滚动条不可见 19onmouseover=new Function(\u0026#34;stopscroll=true\u0026#34;); //鼠标经过，停止滚动 20onmouseout=new Function(\u0026#34;stopscroll=false\u0026#34;); //鼠标离开，开始滚动 21} 22//这时候，内容区的高度是无法读取了。下面输出一个不可见的层\u0026#34;templayer\u0026#34;，稍后将内容复制到里面： 23document.write(\u0026#39;\u0026lt;div id=\u0026#34;templayer\u0026#34; 24style=\u0026#34;position:absolute;z-index:1;visibility:hidden\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026#39;); 25function init(){ //初始化滚动内容 26//多次复制原内容到\u0026#34;templayer\u0026#34;，直到\u0026#34;templayer\u0026#34;的高度大于内容区高度： 27while(templayer.offsetHeight\u0026lt;marqueesHeight){ 28templayer.innerHTML+=marquees.innerHTML; 29} //把\u0026#34;templayer\u0026#34;的内容的“两倍”复制回原内容区： 30marquees.innerHTML=templayer.innerHTML+templayer.innerHTML; 31//设置连续超时，调用\u0026#34;scrollUp()\u0026#34;函数驱动滚动条： 32setInterval(\u0026#34;scrollUp()\u0026#34;,10); 33} 34document.body.onload=init; 35preTop=0; //这个变量用于判断滚动条是否已经到了尽头 36function scrollUp(){ //滚动条的驱动函数 37if(stopscroll==true) return; //如果变量\u0026#34;stopscroll\u0026#34;为真，则停止滚动 38preTop=marquees.scrollTop; //记录滚动前的滚动条位置 39marquees.scrollTop+=1; //滚动条向下移动一个像素 40//如果滚动条不动了，则向上滚动到和当前画面一样的位置 41//当然不仅如此，同样还要向下滚动一个像素(+1)： 42if(preTop==marquees.scrollTop){ 43marquees.scrollTop=templayer.offsetHeight-marqueesHeight+1; 44} 45} 46--\u0026gt; 47\u0026lt;/script\u0026gt; ","date":"2005-07-24","description":"","lastmod":"2005-07-24T05:20:47Z","slug":"javascriptmarquee","tags":[],"title":"【转】使用Javascript制作连续滚动字幕","url":"https://blog.zengrong.net/post/javascriptmarquee/"},{"categories":["web"],"content":"转自吴慧丰的工作室\nDEC 多国字符集 (MCS) -ASCII 码表\nDEC 多国字符集 (MCS) 包括由十六进制值 00 至FF 标识的字符定义，是过去数字设备公司 (Digital Equipment Corporation) 建立和使用的。DEC MCS 划分为两个部分，即 ASCII 7 位字符集 (通过十六进制值 00 至 7F 标识) 和 8 位字符集 (通过十六进制值 80 至 FF 标识)。对于使用 DIGITAL 建立和销售的大多数软件的用户来说，他们对 DEC MCS 是熟悉的 。\nUnicode 标准字符集 (UCS-2) 是由 Unicode 协会定义的 16 位字符集，可以通过十六进制值 0000 至 FFFF 标识。\nISO Latin-1 字符集是通过十六进制值 00 至 FF 标识的 8 位字符的 UCS-2 定义。ISO Latin-1 字符集定义稍微不同于十六进制值 80 至 FF 的 DEC MCS 定义。\n表 A-1 包含 DEC 多国字符集(MCS)。表 A-1 指出在两个字符集中的不同字符。\n有关 Unicode (UCS-2) 字符集的详情，请参阅 Unicode Consortium 的 The Unicode Standard\n介绍 ASCII 控制字符 1 ASCII 特殊和数字字符 ASCII 字母字符 控制字符 其他字符 十六进制代码 MCS 字符或缩写 DEC 多国字符名 ASCII 控制字符 ^1^ 00 NUL 空字符 01 SOH 标题起始 (Ctrl/A) 02 STX 文本起始 (Ctrl/B) 03 ETX 文本结束 (Ctrl/C) 04 EOT 传输结束 (Ctrl/D) 05 ENQ 询问 (Ctrl/E) 06 ACK 认可 (Ctrl/F) 07 BEL 铃 (Ctrl/G) 08 BS 退格 (Ctrl/H) 09 HT 水平制表栏 (Ctrl/I) 0A LF 换行 (Ctrl/J) 0B VT 垂直制表栏 (Ctrl/K) 0C FF 换页 (Ctrl/L) 0D CR 回车 (Ctrl/M) 0E SO 移出 (Ctrl/N) 0F SI 移入 (Ctrl/O) 10 DLE 数据链接丢失 (Ctrl/P) 11 DC1 设备控制 1 (Ctrl/Q) 12 DC2 设备控制 2 (Ctrl/R) 13 DC3 设备控制 3 (Ctrl/S) 14 DC4 设备控制 4 (Ctrl/T) 15 NAK 否定接受 (Ctrl/U) 16 SYN 同步闲置符 (Ctrl/V) 17 ETB 传输块结束 (Ctrl/W) 18 CAN 取消 (Ctrl/X) 19 EM 媒体结束 (Ctrl/Y) 1A SUB 替换 (Ctrl/Z) 1B ESC 换码符 1C FS 文件分隔符 1D GS 组分隔符 1E RS 记录分隔符 1F US 单位分隔符 ASCII 特殊和数字字符 20 SP 空格 21 ! 感叹号 22 \" 引号 (双引号) 23 \\# 数字符号 24 \\$ 美元符 25 % 百分号 26 \u0026 和号 27 ' 省略号 (单引号) 28 ( 左圆括号 29 ) 右圆括号 2A \\* 星号 2B + 加号 2C , 逗号 2D -- 连字号或减号 2E . 句点或小数点 2F / 斜杠 30 0 零 31 1 1 32 2 2 33 3 3 34 4 4 35 5 5 36 6 6 37 7 7 38 8 8 39 9 9 3A : 冒号 3B ; 分号 3C \\\u003c 小于 3D = 等于 3E \\\u003e 大于 3F ? 问号 ASCII 字母字符 40 @ 商业 at 符号 41 A 大写字母 A 42 B 大写字母 B 43 C 大写字母 C 44 D 大写字母 D 45 E 大写字母 E 46 F 大写字母 F 47 G 大写字母 G 48 H 大写字母 H 49 I 大写字母 I 4A J 大写字母 J 4B K 大写字母 K 4C L 大写字母 L 4D M 大写字母 M 4E N 大写字母 N 4F O 大写字母 O 50 P 大写字母 P 51 Q 大写字母 Q 52 R 大写字母 R 53 S 大写字母 S 54 T 大写字母 T 55 U 大写字母 U 56 V 大写字母 V 57 W 大写字母 W 58 X 大写字母 X 59 Y 大写字母 Y 5A Z 大写字母 Z 5B [ 左中括号 5C \\ 反斜杠 5D ] 右中括号 5E \\^ 音调符号 5F \\_ 下划线 60 \\` 重音符 61 a 小写字母 a 62 b 小写字母 b 63 c 小写字母 c 64 d 小写字母 d 65 e 小写字母 e 66 f 小写字母 f 67 g 小写字母 g 68 h 小写字母 h 69 i 小写字母 i 6A j 小写字母 j 6B k 小写字母 k 6C l 小写字母 l 6D m 小写字母 m 6E n 小写字母 n 6F o 小写字母 o 70 p 小写字母 p 71 q 小写字母 q 72 r 小写字母 r 73 s 小写字母 s 74 t 小写字母 t 75 u 小写字母 u 76 v 小写字母 v 77 w 小写字母 w 78 x 小写字母 x 79 y 小写字母 y 7A z 小写字母 z 7B { 左大括号 7C | 垂直线 7D } 右大括号 (ALTMODE) 7E \\~ 代字号 (ALTMODE) 7F DEL 擦掉 (DELETE) 控制字符 80 [保留] 81 [保留] 82 [保留] 83 [保留] 84 IND 索引 85 NEL 下一行 86 SSA 被选区域起始 87 ESA 被选区域结束 88 HTS 水平制表符集 89 HTJ 对齐的水平制表符集 8A VTS 垂直制表符集 8B PLD 部分行向下 8C PLU 部分行向上 8D RI 反向索引 8E SS2 单移 2 8F SS3 单移 3 90 DCS 设备控制字符串 91 PU1 专用 1 92 PU2 专用 2 93 STS 设置传输状态 94 CCH 取消字符 95 MW 消息等待 96 SPA 保护区起始 97 EPA 保护区结束 98 [保留] 99 [保留] 9A [保留] 9B CSI 控制序列引导符 9C ST 字符串终止符 9D OSC 操作系统命令 9E PM 秘密消息 9F APC 应用程序 其他字符 A0 [保留] ^2^ A1 ¡ 反向感叹号 A2 ¢ 分币符 A3 £ 英磅符 A4 [保留] ^2^ A5 ¥ 人民币符 A6 [保留] ^2^ A7 § 章节符 A8 ¤ 通用货币符号 ^2^ A9 © 版权符号 AA ª 阴性顺序指示符 AB « 左角引号 AC [保留] ^2^ AD [保留] ^2^ AE [保留] ^2^ AF [保留] ^2^ B0 ° 温度符 B1 ± 加/减号 B2 ² 上标 2 B3 ³ 上标 3 B4 [保留] ^2^ B5 µ 微符 B6 ¶ 段落符，pilcrow B7 · 中点 B8 [保留] ^2^ B9 ¹ 上标 1 BA º 阳性顺序指示符 BB » 右角引号 BC ¼ 分数四分之一 BD ½ 分数二分之一 BE [保留] ^2^ BF ¿ 反向问号 C0 À 带重音符的大写字母 A C1 Á 带尖锐重音的大写字母 A C2 Â 带音调符号的大写字母 A C3 Ã 带代字号的大写字母 A C4 Ä 带元音变音 (分音符号) 的大写字母 A C5 Å 带铃声的大写字母 A C6 Æ 大写字母 AE 双重元音 C7 Ç 带变音符号的大写字母 C C8 È 带重音符的大写字母 E C9 É 带尖锐重音的大写字母 E CA Ê 带音调符号的大写字母 E CB Ë 带元音变音 (分音符号) 的大写字母 E CC Ì 带重音符的大写字母 I CD Í 带尖锐重音的大写字母 I CE Î 带音调符号的大写字母 I CF Ï 带元音变音 (分音符号) 的大写字母 I D0 [保留] ^2^ D1 Ñ 带代字号的大写字母 N D2 Ò 带重音符的大写字母 O D3 Ó 带尖锐重音的大写字母 O D4 Ô 带音调符号的大写字母 O D5 Õ 带代字号的大写字母 O D6 Ö 带元音变音 (分音符号) 的大写字母 O D7 OE 大写字母 OE 连字 ^2^ D8 Ø 带斜杠的大写字母 O D9 Ù 带重音符的大写字母 U DA Ú 带尖锐重音的大写字母 U DB Û 带音调符号的大写字母 U DC Ü 带元音变音 (分音符号) 的大写字母 U DD Y 带元音变音 (分音符号) 的大写字母 Y DE [保留] ^2^ DF ß 德语高调小写字母 s E0 à 带重音符的小写字母 a E1 á 带尖锐重音的小写字母 a E2 â 带音调符号的小写字母 a E3 ã 带代字号的小写字母 a E4 ä 带元音变音 (分音符号) 的小写字母 a E5 å 带铃声的小写字母 a E6 æ 小写字母 ae 双重元音 E7 ç 带变音符号的小写字母 c E8 è 带重音符的小写字母 e E9 é 带尖锐重音的小写字母 e EA ê 带音调符号的小写字母 e EB ë 带元音变音 (分音符号) 的小写字母 e EC ì 带重音符的小写字母 i ED í 带尖锐重音的小写字母 i EE î 带音调符号的小写字母 i EF ï 带元音变音 (分音符号) 的小写字母 i F0 [保留] ^2^ F1 ñ 带代字号的小写字母 n F2 ò 带重音符的小写字母 o F3 ó 带尖锐重音的小写字母 o F4 ô 带音调符号的小写字母 o F5 õ 带代字号的小写字母 o F6 ö 带元音变音 (分音符号) 的小写字母 o F7 oe 小写字母 oe 连字 ^2^ F8 ø 带斜杠的小写字母 o F9 ù 带重音符的小写字母 u FA ú 带尖锐重音的小写字母 u FB û 带音调符号的小写字母 u FC ü 带元音变音 (分音符号) 的小写字母 u FD ÿ 带元音变音 (分音符号) 的小写字母 y ^2^ FE [保留] ^2^ FF [保留] ^2^ ^1^ALTMODE 和 DELETE 字符 (十进制 125、126 和 127) 也是控制字符。\n^2^在 ISO Latin-1中的不同字符。\n","date":"2005-07-24","description":"","lastmod":"2005-07-24T05:06:14Z","slug":"dec","tags":[],"title":"【转】DEC 多国字符集 (MCS) -ASCII 码表","url":"https://blog.zengrong.net/post/dec/"},{"categories":["web"],"content":"\n","date":"2005-07-24","description":"","lastmod":"2005-07-24T04:50:45Z","slug":"acsii","tags":[],"title":"ACSII码表","url":"https://blog.zengrong.net/post/acsii/"},{"categories":["design"],"content":" 1 文件 1 文件 CorelDraw11兼容CorelDraw9中文版补丁，安装后直接打开CD9文件 本文转自 pictograph online\n去年底公司的电脑全部更新,因为要和 HK/TW 等地区经常保持业务联系.所以把我们设计课的WIN系统全部改成了繁体版,不改不知道,一改麻烦就来了,因为之前课内电脑是简体版时我们用的软件是也全都是简体版的啦,用corel DRAW 9.0简体版本保存的文档,结果,现在改成繁体后,所有原来的简体版本文档全部打不开,试了繁体的 9.0/10.0/11.0 都不可以,这下可急死我了,在网上游了几天,进了N个论坛,注册了N个用户名,还是一无所获,最后在 自由设计新家园 找到了解决的方法,就是下载Corel Draw 11.0兼容Corel Draw 9.0中文版补丁来打开文档,哈哈,果然搞定,不敢独享这份喜悦,上传上来和有需要的朋友一起分享吧!\n一、补丁安装步骤\n首先解压缩下载文件,内附两个文件CdrCore110.dll和IECDR110.flt; 关掉Corel Draw 11.0; 拷贝文件IECDR110.flt到 C:\\Program Files\\Corel\\Corel Graphics 11\\Filters 下取代原文件 ; 拷贝文件CdrCore110.dll到 C:\\Program Files\\Corel\\Corel Graphics 11\\Programs 打开Corel Draw 11.0看看,是不是可以打开Corel Draw 9.0简体的文档了!哈哈 就这么简单... 另外，最近在网上发现有 Corel Draw 12 -\u0026gt; Corel Draw 9.0 中文版的补丁,一起上传上来给大家,安装步骤同11.0\nCorelDraw9发布已经有一段日子了，不过现在使用还是很广，主要是因为CD10的稳定性实在不怎样，而最新的CorelDraw11又对于机器要求比较高导致了使用者也不多。\n目前使用的最多的一个版本便是俗称的\u0026quot;CorelDraw9.03简体中文版\u0026quot;，其实Corel公司从来没有承认过这个版本的存在，而Corel公司自从推出简体中文版的CorelDraw8.0图形套装以后产品目录中再也见不到任何简体中文版的软件了。\n那么这个特别版本的软件又是任何出现在市面上的呢？\n主要问题\n1.兼容性问题\n这个问题恐怕凡是用过这个版本的人都知道，使用\u0026quot;CorelDraw9.03简体中文版\u0026quot;保存的文件，任何之后的版本都无法打开，也就是说，它的文件格式无法为其他版本所认可。同样的，该版本也无法打开英文版CorelDraw9的文件。\n2.存盘问题\n有的时候存盘会不起作用，只会在屏幕上做作一下样子而已，必须使用\u0026quot;另存为\u0026quot;命令才会乖乖的将文件保存进去。\n3.打开问题\n即使是使用\u0026quot;CorelDraw9.03简体中文版\u0026quot;创建文件，打开的时候居然会说\u0026quot;过滤器不支持，而使用双击直接打开却又可以了。\n4.输入/输出问题\n有些格式输出就发生非法操作，比如TTF,EPS等，有些格式输入老是死机，如PS、EPS等，还有的格式生成以后往往不能够被其它软件识别，比如PDF。\n解决方法\n1.最彻底的方法当然是使用正式版本的CorelDraw9了，不少人推荐使用英文版的，不过市面上的英文版同样有存在上述问题的版本，具体的判断方法是这样的：在安装的时候，到了输入序列号的步骤，如果当前版本允许你不输入序列号而进行下一步安装的话，建议你就不要使用这个版本了。可能有些朋友的英文不怎样，那也没关系，安装英文版的软件，然后使用YC-周作的汉化就可以了。下面一段是当初YC-周对于版本的\n说明：\nCorelDRAW 9 的版本兼容问题:\n根我所知，目前大家使用的 CorelDRAW 9 有两种互不兼容的版本，一种是我汉化的 9.397 和9.439，另一种是大部分人使用的另一个 9.397。它们保存的版本 9的文件相互不能打开。通过用版本 10、11 的 CorelDRAW 对它们保存的版本 9的文件进行比较，发现我汉化的 9.397 和 9.439 所保存的版本 9的文件都可以打开（CorelDRAW 9、CorelDRAW 10、CorelDRAW 11之间版本向下兼容），但另一种版本的就不能打开或导入（不知道是什么版本，暂时叫它为特别版9。）。所以请使用特别版的朋友在卸载特别版 9 之前将由它保存的版本 9的文件打开后另存为版本 8 的文件，否则一旦卸载特别版 9将无法打开或导入这些文件。\n如果有条件的话，还可以使用繁体中文版CorelDRAW 9，这个版本目前还没有发现有任何被动过手脚的版本存在。\n如果手头有很多\u0026quot;CorelDraw9.03简体中文版\u0026quot;格式的文件的话，也没有关系，可以通过补丁来转换一下，就可以被后面的版本打开了。\n2.这个问题多发生在刚安装好CorelDraw9的时候，其实这与安装的过程非常有关系，这个版本的软件实在太那个，安装的时候最好不要自定义安装，一旦在安装过程中自定义了安装项（字体除外），那么安装出来的软件就会有以上所有问题。在安装中选择默认安装是最好的办法。\n3.这个问题的解决办法就是完整卸载当前的CorelDraw，然后重新按照前面的方法安装。另外，使用发生问题2的CorelDraw保存的文件也会这样，解决方法就是在资源管理器中通过双击使用正常的CorelDraw9打开后，再另存一个就可以了。\n4.这个问题同样是由于不规范的汉化过程造成的，不规范的汉化造成了组件的结构错误，在CorelDraw调用这些Plugin的时候就发生了错误，可以通过下载补丁覆盖原文件来解决。\n版本\n目前，市面上的\u0026quot;CorelDraw9.03简体中文版\u0026quot;有好几种不同的名称：CorelDraw9.02SC、CorelDraw9.03简体中文版、CorelDraw9完全正式中文版………。可以说是应有尽有，其实大凡都是一个版本。\n使用上有问题的真实版本是9.397 CT4，而版本为9.439的CorelDraw9则没有这样的问题。可能不少资深的设计者都推荐使用CorelDraw9的英文版本，其实英文版本同样会存在这样的问题的。不知道是否注意到，在CorelDraw的安装中，进行到序列号输入的部分，序列号是可以随意甚至不用输入的，其实这就是被动了手脚的地方，真正不存在问题的CorelDraw9基本上都是要求正确输入序列号的。\n真正被Corel公司承认的中文版CorelDraw9只有\u0026quot;CorelDraw9.03繁体中文版\u0026quot;，\n目前，市面上能够看到的CorelDraw9的版本有三类：CorelDraw9繁体中文版、CorelDraw9英文正式版、“CorelDraw9简体中文正式版”。前两者是官方承认的发行版本，而最后的那个种类很杂。\n可以通过查看CorelDraw9安装光盘下的volinfo.txt文件来判断版本，以下是分别是英文版、繁体中文版、9.03版本一、9.03版本二的 volinfo.txt内容：\n－－－－－－－－－－－－－－\n英文版：\nProduct Name: CorelDRAW 9\nLanguage: US English\nMedia No: CD 1\nMF No: 337\nRTM Date: April 15, 1999\nDRAW\nBuild No: 337 (\\\\Programs\\Coreldrw.exe)\nPHOTO-PAINT\nBuild No: 337 (\\\\Programs\\Photopnt.exe)\n－－－－－－－－－－－－－－－－－－－－－－－\n繁体中文版\nProduct Name: CorelDRAW 9\nLanguage: Chinese Traditional\nMedia No: CD 1\nBuild No: 397\nMF No: 397\nDate: July 26, 1999\nCorelDRAW\nBuild No: 397(\\programs\\coreldrw.exe)\nCorel PHOTO-PAINT\nBuild No: 397(\\programs\\photopnt.exe)\nCorel TRACE\nBuild No: 397(\\programs\\trace.exe)\n－－－－－－－－－－－－－－－－－－－－－－－\n9.03版本一\nProduct Name: CorelDRAW 9\nLanguage: US English\nMedia No: CD 1\nMF No: 337\nRTM Date: April 15, 1999\nDRAW\nBuild No: 337 (\\\\Programs\\Coreldrw.exe)\nPHOTO-PAINT\nBuild No: 337 (\\\\Programs\\Photopnt.exe)\n－－－－－－－－－－－－－－－－－－－－－－－\n9.03版本二\nProduct Name: CorelDRAW 9\nLanguage: US CHINESE\nMedia No: CD 1\nMF No: 337\nRTM Date: April 15, 1999\nDRAW\nBuild No: 337 (\\\\Programs\\Coreldrw.exe)\nPHOTO-PAINT\nBuild No: 337 (\\\\Programs\\Photopnt.exe)\n－－－－－－－－－－－－－－－－－－－－－－－－－－－\nCorelDraw9汉化版与CorelDraw12汉化版、中文版文件兼容性列表 本文转自中国Photoshop联盟，原作者coco_lv\nCorelDraw9汉化版与CorelDraw12汉化版、中文版文件兼容性列表\nCorelDraw一个又一个新版不断推出，但由于9.0汉化版文件与高版本不兼容的问题，象我一样只能看着新版流口水的相信一定不在少数。前几天偶然发现了可以打开9.0文件的CorelDraw 12汉化版，兴高采烈地安装好，使用中发现还是有些问题，就花了些时间将CorelDraw9汉化版与CorelDraw12汉化版、CorelDraw12中文版文件做了个比较，想知道到底用哪个版本、怎么用更好。列表及说明如下：\n说明：\n1、CorelDraw 9汉化版不能打开高版本文件.不能打开CD12中文版存的9.0版文件，经工具转换后可。\n2、CorelDraw 12汉化版与12中文版存的12.0版文件可相互打开，但不能打开CD12中文版存的9.0版文件（但是谁会这样存呢？），经工具转换后可。CD12汉化版与CD9汉化版存的9.0版文件可相互打开。\n3、CorelDraw 12中文版不能打开CD9汉化版及CD12汉化版存的9.0版文件，经工具转换后可。\n4、转换工具可转换所有9.0版文件（包括高版本低存的），但不能转换非9.0版文件。所转换文件格式可逆。\n分析：\n综上分析，就目前版本兼容性来看，CorelDraw 12汉化版最好，但其因打9.0补丁导致的喷罐不能正常使用等问题未能得到根本解决，使用起来终究还是让人心存疑虑的，不知道会不会又出现与更高版本冲突的问题。从长远的角度看，还是不要把问题留到将来，麻烦一点，早些转换掉CD9汉化版的文件，使用CorelDraw 12中文版吧。\n但是如果你有太多CD9汉化版的文件，而且经常需要与人交流，鉴于目前使用CD9汉化版的还很多，CD12汉化版不失为一个不错的过渡，注意保存的时候要选择9.0以下版本。\n总的说来，个人认为最顺畅的做法还是使用Coreldraw 12中文版，配两个转换工具，需交流的文件存8.0版，似乎所有的问题都能解决了，就是不知道8.0版会不会损失文件的某些新特性呢？\n","date":"2005-07-21","description":"","lastmod":"2005-07-21T00:45:28Z","slug":"cd9","tags":[],"title":"【转】解决中文CorelDraw 9格式不能被读取的问题","url":"https://blog.zengrong.net/post/cd9/"},{"categories":["others"],"content":"转自吴慧丰工作室\n这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念，增进知识，类似于打RPG游戏的升级。整理这篇文章的动机是两个问题：\n问题一：\n使用Windows记事本的“另存为”，可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件，Windows是怎样识别编码方式的呢？\n我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节，分别是FF、FE（Unicode）,FE、FF（Unicode big endian）,EF、BB、BF（UTF-8）。但这些标记是基于什么标准呢？\n问题二：\n最近在网上看到一个ConvertUTF.c，实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式，我原来就了解。但这个程序让我有些糊涂，想不起来UTF-16和UCS2有什么关系。\n查了查相关资料，总算将这些问题弄清楚了，顺带也了解了一些Unicode的细节。写成一篇文章，送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂，但要求读者知道什么是字节，什么是十六进制。\n0、big endian和little endian\nbig endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时，究竟是将6C写在前面，还是将49写在前面？如果将6C写在前面，就是big endian。还是将49写在前面，就是little endian。\n“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，其中一个皇帝送了命，另一个丢了王位。\n我们一般将endian翻译成“字节序”，将big endian和little endian称作“大尾”和“小尾”。\n1、字符编码、内码，顺带介绍汉字编码\n字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码，为了处理汉字，程序员设计了用于简体中文的GB2312和用于繁体中文的big5。\nGB2312(1980年)一共收录了7445个字符，包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7，低字节从A1-FE，占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。\nGB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号，它分为汉字区和图形符号区。汉字区包括21003个字符。2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字，同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030，对嵌入式产品暂不作要求。所以手机、MP3一般只支持GB2312。\n从ASCII、GB2312、GBK到GB18030，这些编码方法是向下兼容的，即同一个字符在这些方案中总是有相同的编码，后面的标准支持更多的字符。在这些编码中，英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼，GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。\n有的中文Windows的缺省内码还是GBK，可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符，普通人是很难用到的，通常我们还是用GBK指代中文Windows内码。\n这里还有一些细节：\nGB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。\n在DBCS中，GB内码的存储格式始终是big endian，即高位在前。\nGB2312的两个字节的最高位都是1。但符合这个条件的码位只有 128*128=16384 个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析：在读取DBCS字符流时，只要遇到高位为1的字节，就可以将下两个字节作为一个双字节编码，而不用管低字节的高位是什么。\n2、Unicode、UCS和UTF\n前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容（更准确地说，是与ISO-8859-1兼容），与GB码不兼容。例如“汉”字的Unicode编码是6C49，而GB码是BABA。\nUnicode也是一种字符编码方法，不过它是由国际组织设计，可以容纳全世界所有语言文字的编码方案。Unicode的学名是\u0026quot;Universal Multiple-Octet Coded Character Set\u0026quot;，简称为UCS。UCS可以看作是\u0026quot;Unicode Character Set\u0026quot;的缩写。\n根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载：历史上存在两个试图独立设计Unicode的组织，即国际标准化组织（ISO）和一个软件制造商的协会（unicode.org）。ISO开发了ISO 10646项目，Unicode协会开发了Unicode项目。\n在1991年前后，双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果，并为创立一个单一编码表而协同工作。从Unicode2.0开始，Unicode项目采用了与ISO 10646-1相同的字库和字码。\n目前两个项目仍都存在，并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是10646-3:2003。\nUCS规定了怎么用多个字节表示各种文字。怎样传输这些编码，是由UTF(UCS Transformation Format)规范规定的，常见的UTF规范包括UTF-8、UTF-7、UTF-16。\nIETF的RFC2781和RFC3629以RFC的一贯风格，清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。\n3、UCS-2、UCS-4、BMP\nUCS有两种格式：UCS-2和UCS-4。顾名思义，UCS-2就是用两个字节编码，UCS-4就是用4个字节（实际上只用了31位，最高位必须为0）编码。下面让我们做一些简单的数学游戏：\nUCS-2有2^16=65536个码位，UCS-4有 2^31=2147483648 个码位。\nUCS-4根据最高位为0的最高字节分成 2^7=128个group 。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行(rows)，每行包含256个cells。当然同一行的cells只是最后一个字节不同，其余都相同。\ngroup 0的plane 0被称作Basic Multilingual Plane,即BMP。或者说UCS-4中，高两个字节为0的码位被称作BMP。\n将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节，就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。\n4、UTF编码\nUTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下：\nUCS-2编码(16进制) UTF-8 字节流(二进制) 0000 - 007F 0xxxxxxx 0080 - 07FF 110xxxxx 10xxxxxx 0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx 例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间，所以肯定要用3字节模板了：1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是：0110 110001 001001，用这个比特流依次代替模板中的x，得到：11100110 10110001 10001001，即E6 B1 89。\n读者可以用记事本测试一下我们的编码是否正确。\nUTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码，UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码，定义了一个算法。不过由于实际使用的UCS2，或者UCS4的BMP必然小于0x10000，所以就目前而言，可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案，UTF-16却要用于实际的传输，所以就不得不考虑字节序的问题。\n5、UTF的字节序和BOM\nUTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E，“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”，那么这是“奎”还是“乙”？\nUnicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表，而是Byte Order Mark。BOM是一个有点小聪明的想法：\n在UCS编码中有一个叫做\u0026quot;ZERO WIDTH NO-BREAK SPACE\u0026quot;的字符，它的编码是FEFF。而FFFE在UCS中是不存在的字符，所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前，先传输字符\u0026quot;ZERO WIDTH NO-BREAK SPACE\u0026quot;。\n这样如果接收者收到FEFF，就表明这个字节流是Big-Endian的；如果收到FFFE，就表明这个字节流是Little-Endian的。因此字符\u0026quot;ZERO WIDTH NO-BREAK SPACE\u0026quot;又被称作BOM。\nUTF-8不需要BOM来表明字节顺序，但可以用BOM来表明编码方式。字符\u0026quot;ZERO WIDTH NO-BREAK SPACE\u0026quot;的UTF-8编码是EF BB BF（读者可以用我们前面介绍的编码方法验证一下）。所以如果接收者收到以EF BB BF开头的字节流，就知道这是UTF-8编码了。\nWindows就是使用BOM来标记文本文件的编码方式的。\n6、进一步的参考资料\n本文主要参考的资料是： Short overview of ISO-IEC 10646 and Unicode 。\n","date":"2005-07-19","description":"","lastmod":"2005-07-19T05:02:12Z","slug":"unicode","tags":[],"title":"【转】谈谈Unicode编码，简要解释UCS、UTF、BMP、BOM等名词","url":"https://blog.zengrong.net/post/unicode/"},{"categories":["technology"],"content":"在使用txtSQL的过程中，发现一处帮助文档错误。\n在使用altertable命令改变表名称时，发现如果按照帮助文档所说，使用如下代码无法改变表的名称：\n$sql-\u0026gt;altertable(array('db'=\u0026gt;$db_name,'table'=\u0026gt;$table_name,'name'=\u0026gt;$table_name,'action'=\u0026gt;'rename table','value'=\u0026gt;array('name'=\u0026gt;$table_rename_name)) 后来查阅范例程序，才发现如果修改表名必须使用如下代码才可以，也就是说，第三个参数“name”的值应该是新的表名。不需要设定第五个参数“value”：\n$sql-\u0026gt;altertable(array('db'=\u0026gt;$db_name,'table'=\u0026gt;$table_name,'name'=\u0026gt;$table_rename_name,'action'=\u0026gt;'rename table') 以下是帮助文档中的描述：\naltertable Purpose: To alter a txtSQL-table's column definitions txtSQL \u0026gt;= 2.2.2 RC2 void altertable ( array ('table' =\u0026gt; $table, 'action' =\u0026gt; $action, 'name' =\u0026gt; $column, 'values' =\u0026gt; $values [, 'after' =\u0026gt; $afterColumn [, 'db' =\u0026gt; $db]]) ) This function will alter a txtSQL-$table's column defintions. It will only work with the specified column, which is $column. The $action can be either insert- Inserts a new column, $column, and if specified, after the column $afterColumn modify- Modifies an existing $column drop- Drops an existing $column rename col- Renames a $column. Expects array('name' =\u0026gt; $newcolname) in the $values //此句错误 rename table- Renames a $table. Expects array('name' =\u0026gt; $newTableName) in the $values addkey- Sets $column as the primary key ( must be integer and auto_increment ). Expects array('name' =\u0026gt; $colName) in the $values dropkey- Does opposite of 'addkey' The $values element is an array containing information about the column, it must be in the following format array( [$colType =\u0026gt; $value]... ) unless otherwise noted above ","date":"2005-07-04","description":"","lastmod":"2005-07-04T05:43:56Z","slug":"php-2-txtsql","tags":["mysql","php"],"title":"PHP学习笔记(2)txtSQL文档错误","url":"https://blog.zengrong.net/post/php-2-txtsql/"},{"categories":["others"],"content":"文献类型标识方法\n文献类型标识方法为:\n[M] 专著， [C] 论文集, [N] 报纸文章, [J] 期刊文章， [D] 学位论文, [R] 报告, [S] 标准， [P] 专利， [A] 专著、论文集中的析出文献， [Z] 其它未说明的文献。 参考文献按在正文中出现的先后次序列表于文后,以\u0026quot;参考文献\u0026quot;标识；\n参考文献的序号左顶格,用数字加方括号表示。与正文中的指示序号一致。例:\nA、 专著、论文集、学位论文、报告:\n[序号]主要责任者.文献题名[文献类型标识].出版地:出版者,出版年:起止页码.例:\n[1]孙启林.战后韩国教育研究[M].南昌:江西教育出版社,1995:321-322.\nB、 期刊文章:\n[序号]主要责任者.文献题名[J]刊名,年/卷(期):起止页码.例:\n[2]何龄修.读顾城《南明史》[J].中国史研究,1998(3):167一13.\nC、 论文集中的析出文献：\n[序号]析出文献主要责任者.析出文献名[A].原文献主要责任者.原文献题名[C].出版地:出版者、出版年.析出文献起止页码。例：\n[3]辛希俊.信息技术的应用展望[A].赵玮.原文信息技术与信息服务国际研讨会论文集[C].北京：中国社会科学出版社,1994.468-470.\nD、报纸文章:\n[序号]主要责任者.文献题名[N].报纸名,出版日期(版次).例:\n[4]谢希德.创造学习的新思路[N].人民日报,1998-12-25(lO).\nE、国际、国家标准:\n[序号]标准编号,标准名称[S]。例:\n[5]GB/T16159-1996,汉语拼音法正词法基本规则[S].\n","date":"2005-06-16","description":"","lastmod":"2005-06-16T14:04:59Z","slug":"lunwen","tags":[],"title":"【转】文献类型标识方法（对写论文的朋友有用）","url":"https://blog.zengrong.net/post/lunwen/"},{"categories":["technology"],"content":"开始我的PHP学习之路，慢慢学习。\n这个计数器坚持使用fopen函数制作，不过还是打开关闭了文件两次，不知道怎么简化才好。\n1/* 2*counter 3*一个基于文本文件的简单计数器 4*v0.1 5*2005-06-14 6*arong 7*不保留权利 8*/ 9$countFile=\u0026#34;ab.txt\u0026#34;; 10function displayCounter($cFile){ 11$fp = fopen($cFile,\u0026#34;rb\u0026#34;); 12$countNum = fgets($fp); 13if($countNum==\u0026#34;\u0026#34;){ 14$countNum = \u0026#34;0\u0026#34;; 15} 16$countNum += 1; 17echo(\u0026#34;您是第\u0026#34;.$countNum.\u0026#34;个访问的客人\u0026#34;); 18fclose($fp); 19$fp = fopen($cFile,\u0026#34;wb\u0026#34;); 20fwrite($fp,$countNum); 21fclose($fp); 22} 23displayCounter($countFile); 这个计数器的另一个版本，晕，居然用了3次打开文件操作。没办法，菜鸟就是这样的。\n1/* 2*counter 3*一个基于文本文件的简单计数器 4*v0.1 5*2005-06-01 6*arong 7*不保留权利 8*/ 9$countFile=\u0026#34;ab.txt\u0026#34;; 10function displayCounter($cFile){ 11if(!file_exists($cFile)){ 12$fp = fopen($cFile,\u0026#34;w\u0026#34;); 13fwrite($fp,\u0026#34;0\u0026#34;); 14fclose($fp); 15} 16$fp = fopen($cFile,\u0026#34;r+\u0026#34;); 17$countNum = fgets($fp); 18fclose($fp); 19$countNum += 1; 20echo(\u0026#34;您是第\u0026#34;.$countNum.\u0026#34;个访问的客人\u0026#34;); 21unlink($cFile); 22$fp = fopen($cFile,\u0026#34;w\u0026#34;); 23fwrite($fp,$countNum); 24fclose($fp); 25} 26displayCounter($countFile); ","date":"2005-06-14","description":"","lastmod":"2005-06-14T05:49:42Z","slug":"php-1","tags":["php"],"title":"php学习笔记(1)文本计数器","url":"https://blog.zengrong.net/post/php-1/"},{"categories":["web"],"content":"恺迪尔多媒体\n海虹景国际社区\n武当国际园\n金色港湾\nCCN华商科技\n宜驰主流\n","date":"2005-06-11","description":"","lastmod":"2005-06-11T03:13:53Z","slug":"flash-sites","tags":["flash"],"title":"Flash网站作品","url":"https://blog.zengrong.net/post/flash-sites/"},{"categories":["web"],"content":"可以自定义标题栏，隐藏边框的全屏代码\n1\u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=gb2312\u0026#34;\u0026gt; 2 3\u0026lt;title\u0026gt; 4Chromeless Window 5\u0026lt;/title\u0026gt; 6\u0026lt;script language=\u0026#34;JScript\u0026#34;\u0026gt; 7/* 8This following code are designed and writen by Windy_sk \u0026lt;windy_sk @126.com\u0026gt; 9You can use it freely, but u must held all the copyright items! 10Special Thanks For andot 11*/ 12var CW_width = 400; 13var CW_height = 300; 14var CW_top = 100; 15var CW_left = 100; 16var CW_url = \u0026#34;/\u0026#34;; 17var New_CW = window.createPopup(); 18var CW_Body = New_CW.document.body; 19var content = \u0026#34;\u0026#34;; 20var CSStext = \u0026#34;margin:1px;color:black; border:2px outset;border-style:expression(onmouseout=onmouseup=function(){ this.style.borderStyle=\u0026#39;outset\u0026#39; }, onmousedown=function(){ if(event.button!=2)this.style.borderStyle=\u0026#39;inset\u0026#39; });background-color:buttonface;width:16px;height:14px;font-size:12px;line-height:11px;cursor:Default;\u0026#34;; 21 22function insert_content(){ 23var temp = \u0026#34;\u0026#34;; 24CW_Body.style.overflow = \u0026#34;hidden\u0026#34;; 25CW_Body.style.backgroundColor = \u0026#34;white\u0026#34;; 26CW_Body.style.border = \u0026#34;solid black 1px\u0026#34;; 27temp += \u0026#34;\u0026lt;table width=100% height=100% cellpadding=0 cellspacing=0 border=0\u0026gt;\u0026#34;; 28temp += \u0026#34;\u0026lt;tr style=\u0026#39;;font-size:12px;background:#0099CC;height:20;cursor:default\u0026#39; ondblclick=\\\u0026#34;Max.innerText=Max.innerText==\u0026#39;1\u0026#39;?\u0026#39;2\u0026#39;:\u0026#39;1\u0026#39;;parent.if_max=!parent.if_max;parent.show_CW();\\\u0026#34; onmouseup=\u0026#39;parent.drag_up(event)\u0026#39; onmousemove=\u0026#39;parent.drag_move(event)\u0026#39; onmousedown=\u0026#39;parent.drag_down(event)\u0026#39; onselectstart=\u0026#39;return false\u0026#39; oncontextmenu=\u0026#39;return false\u0026#39;\u0026gt;\u0026#34;; 29temp += \u0026#34;\u0026lt;td style=\u0026#39;color:#ffffff;padding-left:5px\u0026#39;\u0026gt;Chromeless Window For IE6 SP1\u0026lt;/td\u0026gt;\u0026#34;; 30temp += \u0026#34;\u0026lt;td style=\u0026#39;color:#ffffff;padding-right:5px;\u0026#39; align=right\u0026gt;\u0026#34;; 31temp += \u0026#34;\u0026lt;span id=Help onclick=\\\u0026#34;alert(\u0026#39;Chromeless Window For IE6 SP1 - Ver 1.0\\\\n\\\\nCode By Windy_sk\\\\n\\\\nSpecial Thanks For andot\u0026#39;)\\\u0026#34; style=\\\u0026#34;\u0026#34;+CSStext+\u0026#34;font-family:System;padding-right:2px;\\\u0026#34;\u0026gt;?\u0026lt;/span\u0026gt;\u0026#34;; 32temp += \u0026#34;\u0026lt;span id=Min onclick=\u0026#39;parent.New_CW.hide();parent.blur()\u0026#39; style=\\\u0026#34;\u0026#34;+CSStext+\u0026#34;font-family:Webdings;\\\u0026#34; title=\u0026#39;Minimum\u0026#39;\u0026gt;0\u0026lt;/span\u0026gt;\u0026#34;; 33temp += \u0026#34;\u0026lt;span id=Max onclick=\\\u0026#34;this.innerText=this.innerText==\u0026#39;1\u0026#39;?\u0026#39;2\u0026#39;:\u0026#39;1\u0026#39;;parent.if_max=!parent.if_max;parent.show_CW();\\\u0026#34; style=\\\u0026#34;\u0026#34;+CSStext+\u0026#34;font-family:Webdings;\\\u0026#34; title=\u0026#39;Maximum\u0026#39;\u0026gt;1\u0026lt;/span\u0026gt;\u0026#34;; 34temp += \u0026#34;\u0026lt;span id=Close onclick=\u0026#39;parent.opener=null;parent.close()\u0026#39; style=\\\u0026#34;\u0026#34;+CSStext+\u0026#34;font-family:System;padding-right:2px;\\\u0026#34; title=\u0026#39;Close\u0026#39;\u0026gt;x\u0026lt;/span\u0026gt;\u0026#34;; 35temp += \u0026#34;\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt;\u0026lt;tr\u0026gt;\u0026lt;td colspan=2\u0026gt;\u0026#34;; 36temp += \u0026#34;\u0026lt;div\u0026gt;\u0026#34;; 37temp += \u0026#34;\u0026lt;object id=\u0026#39;include\u0026#39; style=\u0026#39;overflow:scroll; HEIGHT: 100%; width:\u0026#34;+CW_width+\u0026#34;\u0026#39;;border:0px\u0026#39; type=\u0026#39;text/html\u0026#39; data=\u0026#39;\u0026#34;+CW_url+\u0026#34;\u0026#39;\u0026gt;\u0026lt;/object\u0026gt;\u0026#34;; 38temp += \u0026#34;\u0026lt;/div\u0026gt;\u0026#34;; 39temp += \u0026#34;\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt;\u0026lt;/table\u0026gt;\u0026#34;; 40CW_Body.innerHTML = temp; 41} 42insert_content(); 43 44var if_max = true; 45function show_CW(){ 46window.moveTo(10000, 10000); 47if(if_max){ 48New_CW.show(CW_top, CW_left, CW_width, CW_height); 49if(typeof(New_CW.document.all.include)!=\u0026#34;undefined\u0026#34;){ 50New_CW.document.all.include.style.width = CW_width; 51New_CW.document.all.Max.innerText = \u0026#34;1\u0026#34;; 52} 53 54}else{ 55New_CW.show(0, 0, screen.width, screen.height); 56New_CW.document.all.include.style.width = screen.width; 57} 58} 59 60window.onfocus = show_CW; 61window.onresize = show_CW; 62 63// Move Window 64var drag_x,drag_y,draging=false 65 66function drag_move(e){ 67if (draging){ 68New_CW.show(e.screenX-drag_x, e.screenY-drag_y, CW_width, CW_height); 69return false; 70} 71} 72 73function drag_down(e){ 74if(e.button==2)return; 75if(New_CW.document.body.offsetWidth==screen.width \u0026amp;\u0026amp; New_CW.document.body.offsetHeight==screen.height)return; 76drag_x=e.clientX; 77drag_y=e.clientY; 78draging=true; 79e.srcElement.setCapture(); 80} 81 82function drag_up(e){ 83draging=false; 84e.srcElement.releaseCapture(); 85if(New_CW.document.body.offsetWidth==screen.width \u0026amp;\u0026amp; New_CW.document.body.offsetHeight==screen.height) return; 86CW_top = e.screenX-drag_x; 87CW_left = e.screenY-drag_y; 88} 89\u0026lt;/script\u0026gt; 90\u0026lt;/meta\u0026gt; 普通全屏代码\n1 2function fullscreen() 3{ 4var Pop; 5 if (self.screen) { 6 sw = screen.width; 7 sh = screen.height; 8 w = sw-10; // to center: use desired width 9 h = sh - 70; // to center: use desired height 10 cx = 0; // to center: (.5*sw) - (w*.5) 11 cy = 0; // to center: (.5*sh) - (h*.5) 12 13 var dimentions_and_such = \u0026#39;width=\u0026#39;+w+\u0026#39;,\u0026#39;+\u0026#39;height=\u0026#39;+h+\u0026#39;,\u0026#39; + \u0026#39;screenX=\u0026#39; +cx+\u0026#39;,\u0026#39;+\u0026#39;screenY=\u0026#39;+cy+\u0026#39;,\u0026#39;+\u0026#39;left=\u0026#39;+cx+\u0026#39;,\u0026#39;+\u0026#39;top=\u0026#39;+cy+\u0026#39;,toolbar=0,status=0,menubar=0,resizable=0\u0026#39;; 14 var dimentions_and_such1 = \u0026#39;scrollbars=0,width=\u0026#39;+w+\u0026#39;,\u0026#39;+\u0026#39;height=\u0026#39;+h+\u0026#39;,\u0026#39; + \u0026#39;screenX=\u0026#39; +cx+\u0026#39;,\u0026#39;+\u0026#39;screenY=\u0026#39;+cy+\u0026#39;,\u0026#39;+\u0026#39;left=\u0026#39;+cx+\u0026#39;,\u0026#39;+\u0026#39;top=\u0026#39;+cy+\u0026#39;,toolbar=0,status=0,fullscreen=0,menubar=0,resizable=0\u0026#39;; 15 if (screen.width \u0026lt; 900) 16 { 17 Pop=window.open(\u0026#34;love.htm\u0026#34;,\u0026#34;\u0026#34;,\u0026#34;toolbar=0,location=0,fullscreen=1,directories=0,status=0,menubar=0,scrollbars=0,resizable=0\u0026#34;); 18 } 19 else if (screen.width \u0026gt; 1200) 20 { 21 Pop=window.open(\u0026#34;love.htm\u0026#34;,\u0026#34;\u0026#34;,dimentions_and_such1); 22 } 23 else 24 { 25 26 Pop=window.open(\u0026#34;love.htm\u0026#34;,\u0026#34;\u0026#34;,dimentions_and_such1); 27 } 28} 29 30} 31fullscreen(); ","date":"2005-06-07","description":"","lastmod":"2005-06-07T05:57:44Z","slug":"javascriptcode-1","tags":["javascript"],"title":"两个全屏JavaScript代码","url":"https://blog.zengrong.net/post/javascriptcode-1/"},{"categories":["technology"],"content":"今天做站的时候客户要求实现网站全屏，使用JavaScript：\n1\u0026lt; !DOCTYPE HTML PUBLIC \u0026#34;-//W3C//DTD HTML 4.0 Transitional//EN\u0026#34;\u0026gt; 2\u0026lt;html\u0026gt; 3\u0026lt;head\u0026gt; 4\u0026lt;script language=\u0026#34;javascript\u0026#34;\u0026gt; 5 \u0026lt;!-- 6function fullscreen(){ 7window.open(\u0026#34;/index.htm\u0026#34;,\u0026#34;\u0026#34;,\u0026#34;scrollbars=0,toolbar=0,location=0,fullscreen=1,directories=0,status=0,menubar=0,resizable=0\u0026#34;); 8} 9 // --\u0026gt; 10\u0026lt;/script\u0026gt; 11\u0026lt;/head\u0026gt; 12\u0026lt;body\u0026gt; 13\u0026lt;h3 align=\u0026#34;center\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;j avascript:fullscreen()\u0026#34;\u0026gt;打开\u0026lt;/a\u0026gt;\u0026lt;/h3\u0026gt; 14\u0026lt;/body\u0026gt; 15\u0026lt;/html\u0026gt; 可是，即使scrollbars值为0，在全屏的时候仍然会出现滚动条，后来发现，要完全取消滚动条，还需要在CSS里面加上：\n1overflow:hidden; 有网友说可以在Body里面加上scroll=no实现，例如\n1\u0026lt;body scroll=no\u0026gt;\u0026lt;/body\u0026gt; 我偏爱CSS，所以没有实验，不是是否能成功。\n","date":"2005-06-07","description":"","lastmod":"2005-06-07T05:55:43Z","slug":"javascriptcode-2","tags":["css","javascript"],"title":"完全去除滚动条","url":"https://blog.zengrong.net/post/javascriptcode-2/"},{"categories":["news"],"content":"一个支持多用户的Flash日记本，采用ASP作为后台技术。我试用了一下，功能还是非常强大，只是界面上有些乱，另外速度有待提高。现在还没有研究其Flash内核以及如何与ASP通信。\n详细介绍\n日记本演示地址\n","date":"2005-05-30","description":"","lastmod":"2005-05-30T07:01:42Z","slug":"flashdiary","tags":["asp","flash"],"title":"一个基于Flash＋ASP＋ACCESS的日记本","url":"https://blog.zengrong.net/post/flashdiary/"},{"categories":["technology"],"content":"纯CSS菜单 1\u0026lt; !DOCTYPE html PUBLIC \u0026#34;-//W3C//DTD XHTML 1.1//EN\u0026#34; 2 \u0026#34;http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\u0026#34; 3[ \u0026lt;!ELEMENT a (#PCDATA | table)* \u0026gt; ]\u0026gt; 4\u0026lt;html xmlns=\u0026#34;http://www.w3.org/1999/xhtml\u0026#34; xml:lang=\u0026#34;en\u0026#34; \u0026gt; 5\u0026lt;head\u0026gt; 6\u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;application/xhtml+xml; charset=UTF-8\u0026#34; /\u0026gt; 7\u0026lt;title\u0026gt; Drop Down Menu \u0026lt;/title\u0026gt; 8\u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; 9body {font-size:1%; color:#fff;} /*get rid of the IE bug that prints the the end of the !doctype */ 10.menu {display:none;} 11.holder {color:#000; width:90px; height:18px; display:block; background:#dca; border:1px solid #000; margin-right:1px; text-align:center; float:left; text-decoration:none; font-family:tahoma, vardana, arial, sans-serif; font-size:10px; line-height:18px; overflow:hidden;} 12.holder:hover {height:auto; cursor:pointer;color:#fff; background:#000;} 13a.inner, a.inner:visited {display:block; width:89px; height:18px; border-bottom:1px solid #000; text-decoration:none; color:#000; background:#eee;} 14a.inner:hover {background:#add;} 15p { color:#000; font-size:16px;} 16\u0026lt;/style\u0026gt; 17\u0026lt;!--[if IE]\u0026gt; 18 \u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; 19 /*\u0026lt; ![CDATA[*/ 20.holder {display:none;} 21.menu {display:block;} 22a.outer, a.outer:visited {color:#000; width:90px; height:18px; display:block; background:#dca; border:1px solid #000; margin-right:1px; text-align:center; float:left; text-decoration:none; font-family:tahoma, vardana, arial, sans-serif; font-size:10px; line-height:18px; overflow:hidden;} 23a.outer:hover {color:#fff; background:#000; overflow:visible;} 24a.outer:hover table {display:block; background:#eee; border-collapse:collapse;} 25a.inner, a.inner:visited {display:block; width:88px; height:18px; border-bottom:1px solid #000; text-decoration:none; color:#000;font-family:tahoma, vardana, arial, sans-serif; font-size:10px; text-align:center;} 26a.inner:hover {background:#add;} 27 /*]]\u0026gt;*/ 28 \u0026lt;/style\u0026gt; 29\u0026lt; ![endif]--\u0026gt; 30\u0026lt;/head\u0026gt; 31\u0026lt;body\u0026gt; 32\u0026lt;p\u0026gt;For another demo with page content see \u0026lt;a href=\u0026#34;http://www.stunicholls.myby.co.uk/menus/drop_down.html\u0026#34; title=\u0026#34;another demo\u0026#34;\u0026gt;this page\u0026lt;/a\u0026gt; 33\u0026lt;div class=\u0026#34;menu\u0026#34;\u0026gt; 34\u0026lt;a class=\u0026#34;outer\u0026#34; href=\u0026#34;page1.html\u0026#34;\u0026gt;MENU 1 35\u0026lt;table\u0026gt;\u0026lt;tr\u0026gt;\u0026lt;td\u0026gt; 36\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1a.html\u0026#34;\u0026gt;Page 1a\u0026lt;/a\u0026gt; 37\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1b.html\u0026#34;\u0026gt;Page 1b\u0026lt;/a\u0026gt; 38\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1c.html\u0026#34;\u0026gt;Page 1c\u0026lt;/a\u0026gt; 39\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1d.html\u0026#34;\u0026gt;Page 1d\u0026lt;/a\u0026gt; 40\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1e.html\u0026#34;\u0026gt;Page 1e\u0026lt;/a\u0026gt; 41\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt;\u0026lt;/table\u0026gt; 42\u0026lt;/a\u0026gt; 43\u0026lt;a class=\u0026#34;outer\u0026#34; href=\u0026#34;page1.html\u0026#34;\u0026gt;MENU 2 44\u0026lt;table\u0026gt;\u0026lt;tr\u0026gt;\u0026lt;td\u0026gt; 45\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page2a.html\u0026#34;\u0026gt;Page 2a\u0026lt;/a\u0026gt; 46\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page2b.html\u0026#34;\u0026gt;Page 2b\u0026lt;/a\u0026gt; 47\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page2c.html\u0026#34;\u0026gt;Page 2c\u0026lt;/a\u0026gt; 48\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page2d.html\u0026#34;\u0026gt;Page 2d\u0026lt;/a\u0026gt; 49\u0026lt;/td\u0026gt;\u0026lt;/tr\u0026gt;\u0026lt;/table\u0026gt; 50\u0026lt;/a\u0026gt; 51\u0026lt;/div\u0026gt; 52\u0026lt;div class=\u0026#34;holder\u0026#34;\u0026gt; 53MENU 1\u0026lt;br /\u0026gt; 54\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1a.html\u0026#34;\u0026gt;Page 1a\u0026lt;/a\u0026gt; 55\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1b.html\u0026#34;\u0026gt;Page 1b\u0026lt;/a\u0026gt; 56\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1c.html\u0026#34;\u0026gt;Page 1c\u0026lt;/a\u0026gt; 57\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1d.html\u0026#34;\u0026gt;Page 1d\u0026lt;/a\u0026gt; 58\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page1e.html\u0026#34;\u0026gt;Page 1e\u0026lt;/a\u0026gt; 59\u0026lt;/div\u0026gt; 60\u0026lt;div class=\u0026#34;holder\u0026#34;\u0026gt; 61MENU 2\u0026lt;br /\u0026gt; 62\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page2a.html\u0026#34;\u0026gt;Page 2a\u0026lt;/a\u0026gt; 63\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page2b.html\u0026#34;\u0026gt;Page 2b\u0026lt;/a\u0026gt; 64\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page2c.html\u0026#34;\u0026gt;Page 2c\u0026lt;/a\u0026gt; 65\u0026lt;a class=\u0026#34;inner\u0026#34; href=\u0026#34;page2d.html\u0026#34;\u0026gt;Page 2d\u0026lt;/a\u0026gt; 66\u0026lt;/div\u0026gt; 67\u0026lt;/p\u0026gt;\u0026lt;/body\u0026gt; 68\u0026lt;/html\u0026gt; JS+CSS 1\u0026lt; !DOCTYPE html PUBliC \u0026#34;-//W3C//DTD XHTML 1.0 Transitional//EN\u0026#34; 2\u0026#34;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\u0026#34;\u0026gt; 3\u0026lt;html xmlns=\u0026#34;http://www.w3.org/1999/xhtml\u0026#34; lang=\u0026#34;zh-CN\u0026#34;\u0026gt; 4\u0026lt;head\u0026gt; 5\u0026lt;meta http-equiv=\u0026#34;Content-Type\u0026#34; content=\u0026#34;text/html; charset=gb2312\u0026#34; /\u0026gt; 6\u0026lt;title\u0026gt;css菜单演示\u0026lt;/title\u0026gt; 7\u0026lt;style type=\u0026#34;text/css\u0026#34;\u0026gt; 8\u0026lt;!-- 9*{margin:0;padding:0;border:0;} 10body { 11\tfont-family: arial, 宋体, serif; 12 font-size:12px; 13} 14 15 16#nav { 17\tline-height: 24px; list-style-type: none; background:#666; 18} 19 20#nav a { 21\tdisplay: block; width: 80px; text-align:center; 22} 23 24#nav a:link { 25\tcolor:#666; text-decoration:none; 26} 27#nav a:visited { 28\tcolor:#666;text-decoration:none; 29} 30#nav a:hover { 31\tcolor:#FFF;text-decoration:none;font-weight:bold; 32} 33 34#nav li { 35\tfloat: left; width: 80px; background:#CCC; 36} 37#nav li a:hover{ 38\tbackground:#999; 39} 40#nav li ul { 41\tline-height: 27px; list-style-type: none;text-align:left; 42\tleft: -999em; width: 180px; position: absolute; 43} 44#nav li ul li{ 45\tfloat: left; width: 180px; 46\tbackground: #F6F6F6; 47} 48 49 50#nav li ul a{ 51\tdisplay: block; width: 180px;width: 156px;text-align:left;padding-left:24px; 52} 53 54#nav li ul a:link { 55\tcolor:#666; text-decoration:none; 56} 57#nav li ul a:visited { 58\tcolor:#666;text-decoration:none; 59} 60#nav li ul a:hover { 61\tcolor:#F3F3F3;text-decoration:none;font-weight:normal; 62\tbackground:#C00; 63} 64 65#nav li:hover ul { 66\tleft: auto; 67} 68#nav li.sfhover ul { 69\tleft: auto; 70} 71#content { 72\tclear: left; 73} 74--\u0026gt; 75\u0026lt;/style\u0026gt; 76\u0026lt;script type=text/j avascript\u0026gt;\u0026lt;!--//--\u0026gt;\u0026lt; ![CDATA[//\u0026gt;\u0026lt;!-- 77function menuFix() { 78\tvar sfEls = document.getElementById(\u0026#34;nav\u0026#34;).getElementsByTagName(\u0026#34;li\u0026#34;); 79\tfor (var i=0; i\u0026lt;sfEls.length; i++) { 80\tsfEls[i].onmouseover=function() { 81\tthis.className+=(this.className.length\u0026gt;0? \u0026#34; \u0026#34;: \u0026#34;\u0026#34;) + \u0026#34;sfhover\u0026#34;; 82\t} 83\tsfEls[i].onMouseDown=function() { 84\tthis.className+=(this.className.length\u0026gt;0? \u0026#34; \u0026#34;: \u0026#34;\u0026#34;) + \u0026#34;sfhover\u0026#34;; 85\t} 86\tsfEls[i].onMouseUp=function() { 87\tthis.className+=(this.className.length\u0026gt;0? \u0026#34; \u0026#34;: \u0026#34;\u0026#34;) + \u0026#34;sfhover\u0026#34;; 88\t} 89\tsfEls[i].onmouseout=function() { 90\tthis.className=this.className.replace(new RegExp(\u0026#34;( ?|^)sfhover\\\\b\u0026#34;), 91 92\u0026#34;\u0026#34;); 93\t} 94\t} 95} 96window.onload=menuFix; 97//--\u0026gt;\u0026lt; !]]\u0026gt;\u0026lt;/script\u0026gt; 98\u0026lt;/head\u0026gt; 99\u0026lt;body\u0026gt; 100\u0026lt;ul id=\u0026#34;nav\u0026#34;\u0026gt; 101\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;产品介绍\u0026lt;/a\u0026gt; 102\t\u0026lt;ul\u0026gt; 103\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;产品一\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 104\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;产品一\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 105\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;产品一\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 106\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;产品一\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 107\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;产品一\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 108\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;产品一\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 109\t\u0026lt;/ul\u0026gt; 110\u0026lt;/li\u0026gt; 111\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;服务介绍\u0026lt;/a\u0026gt; 112\t\u0026lt;ul\u0026gt; 113\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;服务二\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 114\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;服务二\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 115\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;服务二\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 116\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;服务二服务二\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 117\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;服务二服务二服务二\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 118\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;服务二\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 119\t\u0026lt;/ul\u0026gt; 120\u0026lt;/li\u0026gt; 121\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;成功案例\u0026lt;/a\u0026gt; 122\t\u0026lt;ul\u0026gt; 123\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;案例三\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 124\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;案例\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 125\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;案例三案例三\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 126\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;案例三案例三案例三\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 127\t\u0026lt;/ul\u0026gt; 128\u0026lt;/li\u0026gt; 129\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;关于我们\u0026lt;/a\u0026gt; 130\t\u0026lt;ul\u0026gt; 131\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;我们四\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 132\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;我们四\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 133\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;我们四\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 134\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;我们四111\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 135\t\u0026lt;/ul\u0026gt; 136\u0026lt;/li\u0026gt; 137 138\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;在线演示\u0026lt;/a\u0026gt; 139\t\u0026lt;ul\u0026gt; 140\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;演示\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 141\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;演示\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 142\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;演示\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 143\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;演示演示演示\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 144\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;演示演示演示\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 145\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;演示演示\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 146\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;演示演示演示\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 147\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;演示演示演示演示演示\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 148\t\u0026lt;/ul\u0026gt; 149\u0026lt;/li\u0026gt; 150\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;联系我们\u0026lt;/a\u0026gt; 151\t\u0026lt;ul\u0026gt; 152\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;联系联系联系联系联系\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 153\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;联系联系联系\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 154\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;联系\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 155\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;联系联系\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 156\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;联系联系\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 157\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;联系联系联系\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 158\t\u0026lt;li\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#34;\u0026gt;联系联系联系\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; 159\t\u0026lt;/ul\u0026gt; 160\u0026lt;/li\u0026gt; 161\u0026lt;/ul\u0026gt; 162\u0026lt;/body\u0026gt; 163\u0026lt;/html\u0026gt; ","date":"2005-05-30","description":"","lastmod":"2005-05-30T06:02:46Z","slug":"csscode-1","tags":["css"],"title":"几个CSS弹出菜单源码","url":"https://blog.zengrong.net/post/csscode-1/"},{"categories":["others"],"content":"转自Cay's Blog\n熬夜，对现代人来说已经习以为常了。有时候是因为工作，有时候是因为学习，有时候是因为贪玩，有时候是因为别的原因，总之，很多人都有过熬夜的经历。有的人因为工作或职业的原因，都是昼夜颠倒着工作。昼夜颠倒对人体的生理功能和代谢都会产生一定的负面影响。熬夜是对我们身体生物钟正常运做的严重破坏，会干扰正常的新陈代谢与内分泌，许多人因此失眠，然后导致一连串的问题，对工作和生活产生了很坏的影响。但有些朋友因为工作原因，不得不熬夜，所以，这里给这些朋友一些建议，让熬夜带给他们的伤害减少到最低。\n熬夜前的准备\n1、熬夜时要吃热的东西，哪怕是一碗热的方便面也是很好的，当然热牛奶也是很不错的，但是不要吃难以消化的食物，以免因给肠胃增加过重的负担而使得大脑缺氧，这样我们就会产生困意，就没有办法做要做的事情了。\n2、 晚饭不能吃太饱，原因和①中所说的吃难消化的食物产生的情况一样。\n3、一定要注意保暖，特别要注意肚子的保暖，防止冻着了肚子。\n4、 要多喝白开水，熬夜身体会很缺水的。\n熬夜过程中的注意事项\n1、熬夜的时候我们会感觉很累，但是无论多累，中间最好不要上床休息，就像机器一样，突然开突然关的，对身体非常不好，一定要等事情忙完再休息。\n2、若困乏的时候事情还没有忙完，则可喝咖啡或茶水等有一定的刺激性的饮品来提神，但要注意应热饮，浓度不要太高，以免伤胃。\n3、 熬夜时，大脑需氧量会增大，应时时做深呼吸。\n4、 事情忙完后，一定要收心，即使不睡觉，也要坐在椅子上收心。\n熬夜后的补救措施\n睡前或起床后利用五至十分钟敷一下脸(可使用保湿面膜)，来补充缺水的肌肤。 起床后洗脸时利用冷、热交替剌激脸部血液循环 涂抹保养品时，先按摩脸部五分钟； 早上起床后先喝一杯枸杞茶，有补气养身之效。 做个简易柔软操，活动一下筋骨，让精神旺起来。 早饭一定要吃饱，但是不能吃凉的食物。 枸杞茶的做法：\n1.材料：枸杞一小把、红枣3～4粒\n2.做法：直接将枸杞一小把及红枣放入大水杯中，以开水冲泡服用，或以锅水煮服用。\n3.提醒：\na.如口干舌燥很严重，或火气很大可另加菊花1～2朵一起冲服。\nb.冲服时，请在冲完热开水后不要立即服用，让其入味再喝，效果较好。\nc.红枣可先剪开\n熬夜的人吃什么\n熬夜的人，最先想到的就是喝咖啡或喝茶提神。咖啡因的确会让人精神振奋，不过，美国圣路加医院（St. Luke's Hospital）睡眠医药研究中心的实验发现，咖啡因对提升工作效率不见得有效，即使有用，也仅能维持短时间效果。\n另外，咖啡因虽然提神，相对地会消耗体内与神经、肌肉协调有关的维他命B群，缺乏维他命B群的人本来就比较容易累，更可能形成恶性循环，养成酗茶、酗咖啡的习惯，需要量愈来愈多，效果却愈来愈差。因此，必须熬夜时，多补充些维他命B群，反而比较有效。\n熬夜时，有人认为吃甜食可以补充热量，其实甜食也是熬夜大忌。晚餐后或熬夜时，不要吃太多甜食，高糖虽有高热量，刚开始让人兴奋，却会消耗维他命B群，导致反效果，也容易引来肥胖问题。\n整体说来，熬夜的预防保健仍取决于日常饮食。熬夜的人多半是做文字工作或经常操作电脑的人，在昏黄灯光下苦战一夜容易使眼肌疲劳、视力下降。维生素A及维生素B对预防视力减弱有一定效果，维生素A可调节视网膜感光物质——视紫的合成，能提高熬夜工作者对昏暗光线的适应力，防止视觉疲劳。所以要多吃胡萝卜、韭菜、鳗鱼等富含维生素A的食物，以及富含维生素B的瘦肉、鱼肉、猪肝等动物性食品。此外，还应适当补充热量，吃一些水果、蔬菜及蛋白质食品如肉、蛋等来补充体力消耗，但千万不要大鱼大肉地猛吃。花生米、杏仁、腰果、胡桃等干果类食品，它们含有丰富的蛋白质、维生素B、维生素E、钙和铁等矿物质以及植物油，而胆固醇的含量很低，对恢复体能有特殊的功效。\n","date":"2005-05-27","description":"","lastmod":"2005-05-27T03:37:11Z","slug":"situp","tags":[],"title":"【转】熬夜的朋友看一看","url":"https://blog.zengrong.net/post/situp/"},{"categories":["others"],"content":"原文地址\n中文翻译转自老康之家\nTIOBE 程序语言使用排行榜是为了显示程序语言的最新流行程度，每月更新一次。统计数据基于世界范围内的软件工程师，课程和第三方提供者所使用语言的数量。计算中采用了Google，MSN和Yahoo！三大搜索引擎的数据。TIOBE 程序语言使用排行榜并不是为了指出哪种语言是最好的或者大多数的代码是用哪种语言写就的，它只是为了帮助你评估你所使用的语言能否跟得上时代或者帮助你在进行新的软件开发时选择合适的语言。\n图例\n一些字段的解释：\n(排名)：与去年同期相比的变化情况。\n使用率： web搜索'+\u0026quot;关键字(某种语言)' 被用来计算本排行榜数据，采用过去12个月Google，MSN和Yahoo!的web搜索数量和Google新闻组的点击量。所采用的计算方法是 #(规范的Google web点击量)+#(规范的Yahoo! web点击量)+#(规范的Google 新闻组点击量)。术语\u0026quot;规范的\u0026quot; 是指前50名语言的web点击都会发生并且均匀分布。\n(使用率)： 过去12个月的使用率变化。\n评价：评价\u0026quot;A\u0026quot; 表示主流语言。评价\u0026quot;A-\u0026quot; and \u0026quot;A--\u0026quot;表示语言评价介于 \u0026quot;A\u0026quot; and \u0026quot;B\u0026quot;之间。如果一种语言的使用率至少3个月高于0.7%，它就可以获得评价 \u0026quot;A\u0026quot;，此前的两个月分别获得评价\u0026quot;A--\u0026quot;和\u0026quot;A-\u0026quot; 。从评价\u0026quot;A\u0026quot; 到评价\u0026quot;B\u0026quot;的情况则是相反。\n其他的候选语言\n除了上面提及的50种语言, 我们也对未来有可能进入榜单的语言保持关注。按字母顺序列在下面：\nABC, Algol, APL, AppleScript, BCPL, Beta, Bourne shell, Clarion, Clean, Eiffel, Erlang, Euphoria, Haskell, Inform, Io, Lua, Mantis, Maple, Mathematica, Modula-2, Moto, MS-DOS batch, MUMPS, Oberon, Occam, OPL, Oz, Pike, Powerbuilder, Progress, Q, Scala, Slate, Verilog, Visual FoxPro, Whitespace, and XSLT.\n常见问题解答\n问: 你们采用的语言定义是什么?\n答: 我们采用的定义是\u0026quot;任何能够操作数据的语言，无论是解释型的还是编译型的\u0026quot;。基于此定义，例如HTML和XML就没有被考虑在内。ASP也没有，因为它被认为采用了其他语言例如 JavaScript和VBScript。\n问: 什么是语言组的方言(一类语言)?\n答: 一些语言由于他们非常相似因此被归为一类。Visual Basic, QBasic, Microsoft Basic等被归为一类就是一个例子。VB.NET是一个例外因为它与经典的Visual Basic有太多的不同。一类语言排名的计算是采用其中使用量最大的语言。顺便说一句，在我们看来，汇编语言有很多的不同，因此它们被分别计算。\n","date":"2005-05-27","description":"","lastmod":"2005-05-27T03:33:10Z","slug":"tiobe","tags":[],"title":"TIOBE 程序语言使用排行榜","url":"https://blog.zengrong.net/post/tiobe/"},{"categories":["news"],"content":"转自：http://lis186.4dwebhosting.com/?p=1337\nMusic Mixer\nBarcadi DJ 3.0\nCoca-Cola Chill 2005\n","date":"2005-05-18","description":"","lastmod":"2005-05-18T06:57:37Z","slug":"flashmusicmixer","tags":["flash"],"title":"【转】几个Flash Music Mixer","url":"https://blog.zengrong.net/post/flashmusicmixer/"},{"categories":["impressions"],"content":"这个骗子网站的网址：http://www.wtjj.info/\n首先看看这个消息：\n各位网络朋友们，我发现一个超大我网站空间，200至800M的空间由你选择，真正免费注册网址:http://web2.wtjj.info/reg.asp\n还有就是推荐人那里要记得填czwl，不要填free,否则你将有可能注册不了，你可得记得哦！\n机不可失，赶紧把握吧\n注册确实是可以成功的，就算推荐人填写free也很容易成功，但是注册成功之后你会看到下面的消息：\n注册成功之后，空间是不能使用的：\n想想也对，别人开免费空间总是要有些限制的嘛！那么去找几个人来注册也就可以了。但是，请注意最后一句话：“你也可以在后台直接开通你的FTP”。\n好的，让我们进入后台，单击“开通FTP帮助”，看到如下的对话框。\n为了得到如此大的”免费”空间，就让我们铤而走险一下，发送一个短信把！\n发送短信成功，马上就会收到两个回信：\n湖北移动提醒您已经成功订制由263首都在线提供的健康男女业务20元/月 退订发00000到5263557 客服02787806263\n您已经成功获得登录密码：05123，用此密码即可登录网站，享受网站提供的服务！\n骗子的丑恶嘴脸终于暴露。每月要20元！而且，网站事先并没有说明这个服务需要付费！\n好的，先不管这个包月短信把，看看我们的FTP是否已经开通了。\n这个时候，可以单击“点此开通FTP”按钮了，这个时候，我们看到这个骗局的真正模样了。\n碰到这种情况，请马上登录移动梦网（移动用户），删除订制的这个服务，如果在72小时以内退订，应该是不会收费的。\n所以，不要相信有免费的午餐。 ","date":"2005-05-17","description":"","lastmod":"2005-05-17T00:19:59Z","slug":"webcheat","tags":[],"title":"请不要相信这个网站（以及类似的网站）","url":"https://blog.zengrong.net/post/webcheat/"},{"categories":["technology"],"content":"txtSQL\ntxtSQLAdmin 帮助文档 测试报告 速度快（有四个主要的文本数据库速度比较） 基本上模拟了SQL的所有语法 强大的错误处理 完善的帮助文档 免费使用 txtdb\n国人开发的一个小型文本数据库系统。\nMyupb的TextDB\ntxt db api\nFlat-File SQL\nTextDB\n","date":"2005-05-16","description":"","lastmod":"2005-05-16T02:04:36Z","slug":"phptxtdb","tags":["php","sql"],"title":"PHP文本数据库引擎","url":"https://blog.zengrong.net/post/phptxtdb/"},{"categories":["web"],"content":"准确的说，应该不是这个mod的bug，而是limbo的bug。具体表现在，mos_latest_news的Category ID参数不能按照在文章分类中的id设置。\n如下图的设置，category ID值为31，但是实际对应的并不是内容分类中的第31号分类。因为在limbo的内容分类中，并没有第31号分类。\n例如下图中的编号为1的分类，查看链接的属性，发现实际编号却是4。\n因此，如果按照limbo中内容分类显示的编号来设置mos_latest_news中的Category ID参数，是无法正常显示种类中的最新文章的。必须按照实际的参数进行设置。\n这个bug在limbo官方网站发布的1.03beta中发现，在limbo1.03 beta 6中也同样存在。\n","date":"2005-05-13","description":"","lastmod":"2005-05-13T02:03:14Z","slug":"limbo-mos_latest_news","tags":["cms"],"title":"limbo mos_latest_news的Bug","url":"https://blog.zengrong.net/post/limbo-mos_latest_news/"},{"categories":["others"],"content":"正常卸载一个带有服务的程序可以安全删除它。\n无论如何，都可以使用下面的方法删除服务：\n1.运行注册表编辑器\n2.找到下面的键值：\nHKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services 3.找到服务名称并删除它。\nYou may wish to look at the keys and see what files the service was using and perhaps delete them also.\nNote: You will have to reboot before the list gets updated in server manager.\nThese notes have been tested with Windows NT 4, Windows 2000 and Windows XP\n原文\n","date":"2005-05-10","description":"","lastmod":"2005-05-10T03:13:20Z","slug":"delservice","tags":[],"title":"删除一个Windows服务","url":"https://blog.zengrong.net/post/delservice/"},{"categories":["impressions"],"content":"今天在路上拍的 无语......\n","date":"2005-05-03","description":"","lastmod":"2005-05-03T03:43:10Z","slug":"majiang","tags":[],"title":"麻将要从娃娃抓起","url":"https://blog.zengrong.net/post/majiang/"},{"categories":["others"],"content":"在家里自己组局域网。原来装修的时候已经在两个卧室和书房预留了RJ45接口，网线（呵呵，超五类屏蔽双绞线）已经埋在墙里面了。只是自己偷懒一直没有连起来。五一终于有时间了……\n当时买的所有插座和开关都是飞雕的，也算是国内名牌。 :)拆开接口基板，发现接口设计相当人性化，每根双绞线都对应一个插槽，只需要将双绞线嵌入到插槽中即可，甚至不需要事先剥线。接口上贴着568A和568B两种接法，由于我的网线使用568B标准，因此采用B标准接线。\n如图所示（我的siemens SX1照的，效果差点 :(）\n这张图好一点，我处理过了\n顺便找了两篇关于网线的资料，怕自己以后忘了……\n转自猫猫的垃圾收藏室\nEIA/TIA的布线标准中规定了两种双绞线的线序568A与568B。标准568B：橙白-1，橙-2，绿白-3，蓝-4，蓝白-5，绿-6，褐白-7，褐-8；标准568A：绿白-1，绿-2，橙白-3，蓝-4，蓝白-5，橙-6，褐白-7，褐-8。在整个网络布线中应用一种布线方式，但两端都有RJ45端头的网络连线无论是采用端接方式A，还是端接方式B，在网络中都是通用的。实际应用中，大多数都使用T568B的标准，通常认为该标准对电磁干扰的屏蔽更好。\n转自chinaunix.net\n局域网就是将单独的微机或终端，利用网络相互连接起来，遵循一定的协议，进行信息交换，实现资源共享。网线常用的有：双绞线、同轴电缆、光纤等。双绞线可按其是否外加金属网丝套的屏蔽层而区分为屏蔽双绞线（ＳＴＰ）和非屏蔽双绞线（ＵＴＰ）。从性价比和可维护性出发，大多数局域网使用非屏蔽双绞线（ＵＴＰ-\nUnshielded Twisted pair） 作为布线的传输介质来组网。\nＵＴＰ网线由一定长度的双绞线和ＲＪ４５水晶头组成。\n双绞线由８很不同颜色的线分成４对绞合在一起，成对扭绞的作用是尽可能减少电磁辐射与外部电磁干扰的影响。在ＥＩＡ／ＴＩＡ－５６８标准中，将双绞线按电气特性区分为：\n三类、四类、五类线。网络中最常用的是三类线和五类线，目前已有六类以上的。\n做好的网线要将ＲＪ４５水晶头接入网卡或ＨＵＢ等网络设备的ＲＪ４５插座内。相应地ＲＪ４５插头座也区分为三类或五类电气特性。ＲＪ４５水晶头由金属片和塑料构成，制作网线所需要的ＲＪ一４５水晶接头前端有８个凹僧，简称“ＳＥ”（Position，位置）。\n凹槽内的金属触点共有 ８个，简称“8Ｃ”（ Contact，触点），因此业界对此有“8Ｐ8Ｃ”的别称。特别需要注意的是ＲＪ４５水晶头引脚序号，当金属片面对我们的时候从左至右引脚序号是１～８，序号对于网络连线菲常重要，不能搞错。\n双绞线的最大传输距离为 １００ｍ。如果要加大传输距离，在两段双绞线之间可安装中继器，最多可安装４个中继器。如安装４个中继器连接５个网段，则最大传输距离可达５００ｍ。\nＥＩＡ／ＴＩＡ的布线标准中规定了两种双绞线的线序５６８Ａ\n与５６８Ｂ。\n标准５６８Ａ：\n绿白——１，绿——２，橙白——３，蓝——４，蓝白——５，\n橙——６，棕白——７，棕——８\n标准５６８Ｂ：\n橙白——１，橙——２，绿白——３，蓝——４，蓝白——５，\n绿——６，棕白——７，棕——８\n为了保持最佳的兼容性，普遍采用ＥＩＡ／ＴＩＡ ５６８Ｂ标准来制作网线。在整个网络布线中应用一种布线方式，但两端都有ＲＪ－４５插口的网络连线无论是采用５６８Ａ标准，还是５６８Ｂ标准，在网络中都是可行的。双绞线的顺序与ＲＪ４５头的引脚序号—一对应。 １０Ｍ以太网的网线使用 １、２、３、６编号的芯线传递数据，而 １００Ｍ网卡需要使用四对线。由于１０Ｍ网卡能够使用按 １００Ｍ方式制作的网线；而且双绞线又提供有四对线，因而即使使用 １０Ｍ网卡，一般也按 １００Ｍ方式制作网线。\n标准中要求１、２、３、６、４、５、７、８线必须是双绞。这是因为，在数据的传输中，为了减少和抑制外界的干扰，发送和接收的数据均以差分方式传输，即每一对线互相扭在一起传输一路差分信\n号（这也是双绞线名称的由来）。\n所谓的差分信号是指一根线以正电平方式传输信号，另外一根线\n以负电平方式传输同一信号，当线路中出现干扰信号时，其对两根线的影响是相同的，因而在接收端还原差分信号时就可以屏蔽掉该干扰信号（可以理解为差分的两路信号执行减运算）。从双绞线抑制干扰的原理可以看出，每对线进行双绞的目的是为了抑制干扰信号，提高传输质量；因而我们在制作双绞线的接头时，一定不要将传输差分信号的一对线分开，否则将大大影响网络的传输质量。\n下面介绍几种应用环境下双绞线的制作方法。\nＭＤＩ表示此口是级连口，而ＭＤＩ－Ｘ时表示此口是普通口。\n１．以太网网卡和 ＨＵＢ之间连接：\nＰＣ等网络设备连接到ＨＵＢ时，用的网线为直通线，双绞线的两头连线要—一对应，此时，ＨＵＢ为ＭＤＩＸ口，ＰＣ为ＭＤＩ口。 １０Ｍbps网线只要双绞线两端—一对应即可，不必考虑不同颜色的线的排序，而如果使用 １００Ｍ速率相连的话，则必须严格按照ＥＩＡ／ＴＩＡ５６８Ａ或５６８Ｂ布线标准制作，连线参考如下。\nname pin cablecolor pin name\nTX+ 1 白桔 1 TX+\nTX- 2 桔 2 TX-\nRX+ 3 白绿 3 RX+\n4 兰 4\n5 白兰 5\nRX- 6 绿 6 RX-\n7 白棕 7\n8 棕 8\n２．ＨＵＢ之间连接，或两台计算机直连：\n在进行ＨＵＢ间级连时，应把级连口控制开关放在ＭＤＩ（ＵＰｌｉｎｋ）上，同时用直通线相连。如果ＨＵＢ没有专用级连口，或者无法使用级连口，必须使用ＭＤＩ—Ｘ口级连，这时，我们可用交叉线来达到目的，连线参考如下。\nname nic1 nic2 name\nTX+ 1 3 RX+\nTX- 2 6 RX-\nRX+ 3 1 TX+\nRX- 6 2 TX-\n３．１００Ｍ　ＨＵＢ之间连接，或两台计算机直连：\n我们也应该知道，级连ＨＵＢ间的网线长度不应超过１００ｍ，ＨＵＢ的级连不应超过４级。因交叉线较少用到，故应做特别标记，以免日后误作直通线用，造成线路故障。另外交叉网线也可用于两台微机直连，连线参考下表。\nname pin pin name\nTX_D1+ 1 3 RX_D2+\nTX_D1- 2 6 RX_D2-\nRX_D2+ 3 1 TX_D1+\nRX_D2- 4 2 TX_D1-\nBI_D3+ 5 7 BI_D4+\nBI_D3- 6 8 BI_D4-\nBI_D4+ 7 4 BI_D3+\nBI_D4- 8 5 BI_D3-\n最后须对线路进行通断测试，用电缆测试仪测试时，个个绿灯都应依次闪烁。软件调试最常用的办法就是采用Ｗｉｎｄｏｗｓ９５、 Ｗｉｎｄｏｗｓ ９８自带的Ｐｉｎg命令。如果工作站得到服务器的响应则表明线路正常和网络协议安装正常，而这正是网络应用软件能正常工作的基础。\n","date":"2005-05-02","description":"","lastmod":"2005-05-02T03:53:12Z","slug":"568a568b","tags":[],"title":"568A＆568B","url":"https://blog.zengrong.net/post/568a568b/"},{"categories":["technology"],"content":"一直没有仔细的研究localToGlobal()和globalToLocal()，今天看了Luar的文章之后，自己也做了一些分析和总结。\nlocalToGlobal()比较容易理解，指的是子、孙或者重孙影片剪辑在_root中的绝对坐标。而globalToLocal()是一种假设，指的是一个影片剪辑（无论在何处）在另一个影片剪辑中的相对坐标。 两个method都必须现将要转换的坐标代入到一个普通对象中。 localToGlobal()，指定的是被转换的影片剪辑所在的父影片剪辑的路径；对于globalToLocal()指定是被转换的影片剪辑假设的父影片剪辑中的路径。 ","date":"2005-04-30","description":"","lastmod":"2005-04-30T06:53:30Z","slug":"localtoglobal","tags":["actionscript"],"title":"分析localToGlobal()和globalToLocal()","url":"https://blog.zengrong.net/post/localtoglobal/"},{"categories":["web"],"content":"使用Mambo集成环境调试的时候，Mambo总是会报错，大致的意思是某个变量没有定义或者某个变量已经定义了。我实在是不了解PHP，没有办法调试错误，但是为什么使用Apache集成开发环境却没有错误？不得已比较了N遍自己的PHP.ini和集成开发环境的PHP.ini，一个偶然的机会发现了display_errors这个参数。发现只要改成Off，所有错误都不显示了。\n只怪自己对PHP了解太少，原来一切就是那么简单。\n","date":"2005-04-27","description":"","lastmod":"2005-04-27T15:14:22Z","slug":"display_errors","tags":["mambo","php"],"title":"终于解决了Mambo出错的问题","url":"https://blog.zengrong.net/post/display_errors/"},{"categories":["impressions"],"content":"创作共用（Creative Commons）\n创作共用，介绍一种新的授权方式。\n长久以来，网上充斥了大量的转载文章，许多Blog抄来抄去，没有新意。甚至有的文章并没有征得作者的同意。\n创作共用是一种新的授权方式，看看其中文站的介绍：\n从“保留所有权利”到“保留部分权利”\n我们通常熟知的唯一一种版权声明方式“保留所有权利”(All Rights Reserved)已经被滥用到任何人都可能触犯版权法律，人们只知道在自己的任何大小的作品（甚至本身也是在别人的成果基础上的再创造）都无以复加地声明为“保留所有权利”。这样的结果一方面导致原创者应有的权利得不到尊重，另一方面是很多优秀的作品（无论是艺术作品、文学作品、个人思想还是其他数字作品等）都无法得到最大价值利用或最广泛传播。虽然在很多国家的版权法律默认地赋予原创者很多权利，但是越来越多的人们开始意识到自己并非需要保留所有权利。相反他们更愿意选择“保留部分权利”(Some Rights Reserved)或“不保留权利”(No Rights Reserved)。\n十一种授权选择\n“创作共用”协议机制提供了由4个最常见的授权选择的组合方式，任何作品都可以通过选择这性地组合声明自己的作品授权，实际上可以有11种常见的组合方式。这些组合方式构成了从“松”到“紧”的授权限制，给作品的创造者更加灵活便利的选择。为网络上的数字作品提供了新的“护身符”。\n详细了解请浏览：http://creativecommons.cn/（中文站）\n或者：http://creativecommons.org/（英文站）\n另外，繁体中文站点http://creativecommons.org.tw/的资料更加详细一些。\n另：\n维基百科中关于版权的资料：\n点击察看\n其中包括：\nCopyLeft\nGNU自由文档许可证\nGNU宽通用公共许可证\nGNU通用公共许可证\n","date":"2005-04-25","description":"","lastmod":"2005-04-25T05:21:48Z","slug":"creative-commons","tags":[],"title":"创作共用（Creative Commons）","url":"https://blog.zengrong.net/post/creative-commons/"}]