<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>崎径 其镜</title>
  
  <subtitle>求学道路上的行车记录仪</subtitle>
  <link href="http://www.z16388.top/atom.xml" rel="self"/>
  
  <link href="http://www.z16388.top/"/>
  <updated>2026-05-29T05:52:47.783Z</updated>
  <id>http://www.z16388.top/</id>
  
  <author>
    <name>Anqi Zhao</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Unity CVE-2025-59489 漏洞修复实践（Google Play 合规）</title>
    <link href="http://www.z16388.top/2026/05/14/android/unity-cve-patch/"/>
    <id>http://www.z16388.top/2026/05/14/android/unity-cve-patch/</id>
    <published>2026-05-14T17:54:00.000Z</published>
    <updated>2026-05-29T05:52:47.783Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Google Play 要求所有 Unity 应用完成对 CVE-2025-59489 漏洞的修补，否则将无法通过审核。本文记录实际操作流程与注意事项。</p></blockquote><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>Unity 官方安全公告：<a href="https://unity.com/security/sept-2025-01">https://unity.com/security/sept-2025-01</a></p><p><strong>注意</strong>：虽然部分新版 Unity Editor 的更新日志中声称已内置修复，但实测使用 <strong>2022.3.62f3c1</strong> 提审 Google Play 时仍会收到不合规提示。因此，<strong>无论 Editor 版本如何，都建议使用 Unity 官方 Patch 工具进行修补</strong>。</p><hr><h2 id="修补工具"><a href="#修补工具" class="headerlink" title="修补工具"></a>修补工具</h2><p>工具下载页面：<a href="https://unity.com/security/sept-2025-01/remediation">https://unity.com/security/sept-2025-01/remediation</a></p><ul><li><strong>Windows</strong>：使用 <code>UnityApplicationPatcherCLI.exe</code></li><li><strong>macOS</strong>：使用 <code>UnityApplicationPatcher.app/Contents/MacOS/UnityApplicationPatcherCLI</code></li></ul><hr><h2 id="Android-APK-AAB-修补（最常用）"><a href="#Android-APK-AAB-修补（最常用）" class="headerlink" title="Android APK&#x2F;AAB 修补（最常用）"></a>Android APK&#x2F;AAB 修补（最常用）</h2><h3 id="基本命令"><a href="#基本命令" class="headerlink" title="基本命令"></a>基本命令</h3><figure class="highlight bat"><table><tr><td class="code"><pre><span class="line">UnityApplicationPatcherCLI -android ^</span><br><span class="line">  -applicationPath &lt;<span class="built_in">path</span>/to/application.apk&gt; ^</span><br><span class="line">  -versionCode &lt;新版本号&gt;</span><br></pre></td></tr></table></figure><blockquote><p><code>versionCode</code> 必须大于当前版本号，否则 Google Play 会拒绝上传。</p></blockquote><h3 id="使用正式签名（发布包必须）"><a href="#使用正式签名（发布包必须）" class="headerlink" title="使用正式签名（发布包必须）"></a>使用正式签名（发布包必须）</h3><figure class="highlight bat"><table><tr><td class="code"><pre><span class="line">UnityApplicationPatcherCLI -android ^</span><br><span class="line">  -applicationPath &lt;<span class="built_in">path</span>/to/application.apk&gt; ^</span><br><span class="line">  -versionCode &lt;新版本号&gt; ^</span><br><span class="line">  -versionName &lt;版本名称&gt; ^</span><br><span class="line">  -keystore &lt;<span class="built_in">path</span>/to/your.keystore&gt; ^</span><br><span class="line">  -keystorePassword &lt;密钥库密码&gt; ^</span><br><span class="line">  -keyAlias &lt;密钥别名&gt; ^</span><br><span class="line">  -keyAliasPassword &lt;别名密码&gt;</span><br></pre></td></tr></table></figure><h3 id="仅查看当前版本号（不修补）"><a href="#仅查看当前版本号（不修补）" class="headerlink" title="仅查看当前版本号（不修补）"></a>仅查看当前版本号（不修补）</h3><figure class="highlight bat"><table><tr><td class="code"><pre><span class="line">UnityApplicationPatcherCLI -android -printApplicationVersion ^</span><br><span class="line">  -applicationPath &lt;<span class="built_in">path</span>/to/application.apk&gt;</span><br></pre></td></tr></table></figure><h3 id="参数说明"><a href="#参数说明" class="headerlink" title="参数说明"></a>参数说明</h3><table><thead><tr><th>参数</th><th>必填</th><th>说明</th></tr></thead><tbody><tr><td><code>-applicationPath</code></td><td>✅</td><td>APK 或 AAB 文件路径</td></tr><tr><td><code>-versionCode</code></td><td>✅</td><td>修补后的版本号，必须高于当前值</td></tr><tr><td><code>-versionName</code></td><td>可选</td><td>用户看到的版本名称，省略则保持原值</td></tr><tr><td><code>-keystore</code></td><td>可选</td><td><code>.keystore</code> 或 <code>.jks</code> 文件路径，省略则使用调试签名</td></tr><tr><td><code>-keystorePassword</code></td><td>可选</td><td>密钥库密码</td></tr><tr><td><code>-keyAlias</code></td><td>可选</td><td>密钥别名</td></tr><tr><td><code>-keyAliasPassword</code></td><td>可选</td><td>别名密码</td></tr><tr><td><code>-verbose</code></td><td>可选</td><td>输出详细日志，排查问题时使用</td></tr></tbody></table><hr><h2 id="Windows-macOS-应用修补"><a href="#Windows-macOS-应用修补" class="headerlink" title="Windows &#x2F; macOS 应用修补"></a>Windows &#x2F; macOS 应用修补</h2><figure class="highlight bat"><table><tr><td class="code"><pre><span class="line"># Windows - 修补整个应用目录</span><br><span class="line">UnityApplicationPatcherCLI -windows -applicationPath &lt;<span class="built_in">path</span>/to/MyApp&gt;</span><br><span class="line"></span><br><span class="line"># Windows - 只修补 UnityPlayer.dll</span><br><span class="line">UnityApplicationPatcherCLI -windows -unityPlayerLibrary &lt;<span class="built_in">path</span>/to/UnityPlayer.dll&gt;</span><br><span class="line"></span><br><span class="line"># macOS - 修补整个 .app</span><br><span class="line">UnityApplicationPatcherCLI -macos -applicationPath &lt;<span class="built_in">path</span>/to/MyApp.app&gt;</span><br><span class="line"></span><br><span class="line"># macOS - 只修补 UnityPlayer.dylib（需加 -allowStandaloneLibrary）</span><br><span class="line">UnityApplicationPatcherCLI -macos -unityPlayerLibrary &lt;<span class="built_in">path</span>/to/UnityPlayer.dylib&gt; -allowStandaloneLibrary</span><br></pre></td></tr></table></figure><blockquote><p><code>-allowStandaloneLibrary</code>：允许修补不在 <code>.app</code> 包内的独立 <code>UnityPlayer.dylib</code>，默认不允许，因为工具可能遗漏同目录下的其他需修补文件。</p></blockquote><hr><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ol><li>修补后的包需要重新签名才能上传 Google Play，务必填写正式签名参数</li><li><code>versionCode</code> 每次提审必须递增，不要忘记同步修改</li><li>如果 CI&#x2F;CD 流程有自动打包，建议将 Patch 步骤也加入流水线</li></ol>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Google Play 要求所有 Unity 应用完成对 CVE-2025-59489 漏洞的修补，否则将无法通过审核。本文记录实际操作流程与注意事项。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="游戏开发" scheme="http://www.z16388.top/categories/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Unity" scheme="http://www.z16388.top/tags/Unity/"/>
    
    <category term="Android" scheme="http://www.z16388.top/tags/Android/"/>
    
    <category term="Google Play" scheme="http://www.z16388.top/tags/Google-Play/"/>
    
    <category term="CVE" scheme="http://www.z16388.top/tags/CVE/"/>
    
  </entry>
  
  <entry>
    <title>从零配置 VS Code C++ 环境</title>
    <link href="http://www.z16388.top/2026/04/12/dev/cpp-vscode/"/>
    <id>http://www.z16388.top/2026/04/12/dev/cpp-vscode/</id>
    <published>2026-04-12T22:32:49.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<p>本文档记录在 Windows 系统上从零配置 VS Code C++ 开发环境的全过程，包括编译、调试和运行。</p><h2 id="前置条件"><a href="#前置条件" class="headerlink" title="前置条件"></a>前置条件</h2><ul><li>Windows 10&#x2F;11 系统</li><li>已安装 VS Code（<a href="https://code.visualstudio.com/">下载链接</a>）</li></ul><h2 id="需要的工具"><a href="#需要的工具" class="headerlink" title="需要的工具"></a>需要的工具</h2><h3 id="1-MSYS2"><a href="#1-MSYS2" class="headerlink" title="1. MSYS2"></a>1. MSYS2</h3><p>MSYS2 是一个 Windows 下的 Linux-like 环境，提供 g++ 编译器和 gdb 调试器。</p><ul><li><strong>下载链接</strong>：<a href="https://www.msys2.org/">https://www.msys2.org/</a></li><li><strong>安装路径</strong>：推荐安装到 <code>C:\msys64</code>（默认路径）</li></ul><h3 id="2-VS-Code-扩展"><a href="#2-VS-Code-扩展" class="headerlink" title="2. VS Code 扩展"></a>2. VS Code 扩展</h3><ul><li><strong>C&#x2F;C++ 扩展</strong>：由 Microsoft 提供，支持 IntelliSense、调试等功能<ul><li>在 VS Code 中搜索安装：<code>ms-vscode.cpptools</code></li></ul></li></ul><h2 id="安装步骤"><a href="#安装步骤" class="headerlink" title="安装步骤"></a>安装步骤</h2><h3 id="步骤-1：安装-MSYS2"><a href="#步骤-1：安装-MSYS2" class="headerlink" title="步骤 1：安装 MSYS2"></a>步骤 1：安装 MSYS2</h3><ol><li>下载 MSYS2 安装包并运行安装程序</li><li>选择安装路径为 <code>C:\msys64</code></li><li>安装完成后，运行 MSYS2 UCRT64 终端（从开始菜单启动）</li></ol><h3 id="步骤-2：更新-MSYS2-并安装工具"><a href="#步骤-2：更新-MSYS2-并安装工具" class="headerlink" title="步骤 2：更新 MSYS2 并安装工具"></a>步骤 2：更新 MSYS2 并安装工具</h3><p>在 MSYS2 UCRT64 终端中执行以下命令：</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 更新包数据库</span></span><br><span class="line">pacman -Syu</span><br><span class="line"></span><br><span class="line"><span class="comment"># 安装 g++ 编译器和 gdb 调试器</span></span><br><span class="line">pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-gdb</span><br></pre></td></tr></table></figure><h3 id="步骤-3：安装-VS-Code-扩展"><a href="#步骤-3：安装-VS-Code-扩展" class="headerlink" title="步骤 3：安装 VS Code 扩展"></a>步骤 3：安装 VS Code 扩展</h3><ol><li>打开 VS Code</li><li>按 <code>Ctrl+Shift+X</code> 打开扩展面板</li><li>搜索 <code>C/C++</code> 并安装 Microsoft 的 C&#x2F;C++ 扩展</li></ol><h2 id="VS-Code-配置"><a href="#VS-Code-配置" class="headerlink" title="VS Code 配置"></a>VS Code 配置</h2><p>在你的 C++ 项目根目录（例如 <code>C:\Dev\leetcode</code>）创建 <code>.vscode</code> 文件夹，并添加以下配置文件：</p><h3 id="1-c-cpp-properties-json"><a href="#1-c-cpp-properties-json" class="headerlink" title="1. c_cpp_properties.json"></a>1. c_cpp_properties.json</h3><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;configurations&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;windows-gcc-x64&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;includePath&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                <span class="string">&quot;$&#123;workspaceFolder&#125;/**&quot;</span></span><br><span class="line">            <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;compilerPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C:\\msys64\\ucrt64\\bin\\gcc&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;cStandard&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$&#123;default&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;cppStandard&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$&#123;default&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;intelliSenseMode&quot;</span><span class="punctuation">:</span> <span class="string">&quot;windows-gcc-x64&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;compilerArgs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                <span class="string">&quot;&quot;</span></span><br><span class="line">            <span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="number">4</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="2-settings-json"><a href="#2-settings-json" class="headerlink" title="2. settings.json"></a>2. settings.json</h3><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp.default.compilerPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;c:\\msys64\\ucrt64\\bin\\gcc.exe&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.cCompilerPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;gcc&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.cppCompilerPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;g++&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.debuggerPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;gdb&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.cStandard&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.cppStandard&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.msvcBatchPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C:/Program Files/Microsoft Visual Studio/VR_NR/Community/VC/Auxiliary/Build/vcvarsall.bat&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.useMsvc&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.warnings&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;-Wall&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;-Wextra&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;-Wpedantic&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;-Wshadow&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;-Wformat=2&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;-Wcast-align&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;-Wconversion&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;-Wsign-conversion&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;-Wnull-dereference&quot;</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.msvcWarnings&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;/W4&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/permissive-&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14242&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14287&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14296&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14311&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14826&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w44062&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w44242&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14905&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14906&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14263&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w44265&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;/w14928&quot;</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.enableWarnings&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.warningsAsError&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.compilerArgs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.linkerArgs&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.includePaths&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.includeSearch&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;*&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;**/*&quot;</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.excludeSearch&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="string">&quot;**/build&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;**/build/**&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;**/.*&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;**/.*/**&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;**/.vscode&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;**/.vscode/**&quot;</span></span><br><span class="line">  <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.useAddressSanitizer&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.useUndefinedSanitizer&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.useLeakSanitizer&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.showCompilationTime&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.useLinkTimeOptimization&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;C_Cpp_Runner.msvcSecureNoWarnings&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;terminal.external.windowsExec&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C:\\Windows\\System32\\cmd.exe&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="3-tasks-json"><a href="#3-tasks-json" class="headerlink" title="3. tasks.json"></a>3. tasks.json</h3><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;2.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;tasks&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;cppbuild&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;label&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C/C++: g++.exe build active file&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;command&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C:\\msys64\\ucrt64\\bin\\g++.exe&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;args&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                <span class="string">&quot;-fdiagnostics-color=always&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">&quot;-g&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">&quot;$&#123;file&#125;&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">&quot;-o&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">&quot;$&#123;workspaceFolder&#125;\\build\\Debug\\outDebug.exe&quot;</span></span><br><span class="line">            <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;options&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;cwd&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$&#123;workspaceFolder&#125;&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;problemMatcher&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                <span class="string">&quot;$gcc&quot;</span></span><br><span class="line">            <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;group&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;kind&quot;</span><span class="punctuation">:</span> <span class="string">&quot;build&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;isDefault&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;detail&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Compile current C++ file to build/Debug/outDebug.exe&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;shell&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;label&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Run C++ Program&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;command&quot;</span><span class="punctuation">:</span> <span class="string">&quot;cmd.exe&quot;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;args&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                <span class="string">&quot;/c&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="string">&quot;start \&quot;\&quot; cmd.exe /k \&quot;$&#123;workspaceFolder&#125;\\build\\Debug\\outDebug.exe\&quot;&quot;</span></span><br><span class="line">            <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;dependsOn&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">                <span class="string">&quot;C/C++: g++.exe build active file&quot;</span></span><br><span class="line">            <span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;options&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;shell&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                    <span class="attr">&quot;executable&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C:\\Windows\\System32\\cmd.exe&quot;</span></span><br><span class="line">                <span class="punctuation">&#125;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;group&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;kind&quot;</span><span class="punctuation">:</span> <span class="string">&quot;test&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;isDefault&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;presentation&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">                <span class="attr">&quot;echo&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;reveal&quot;</span><span class="punctuation">:</span> <span class="string">&quot;always&quot;</span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;focus&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">                <span class="attr">&quot;panel&quot;</span><span class="punctuation">:</span> <span class="string">&quot;shared&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">            <span class="attr">&quot;problemMatcher&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="4-launch-json"><a href="#4-launch-json" class="headerlink" title="4. launch.json"></a>4. launch.json</h3><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;0.2.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;configurations&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C/C++ Runner: Debug Session&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;cppdbg&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;request&quot;</span><span class="punctuation">:</span> <span class="string">&quot;launch&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;args&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;stopAtEntry&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;externalConsole&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;cwd&quot;</span><span class="punctuation">:</span> <span class="string">&quot;c:/Dev/leetcode&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;program&quot;</span><span class="punctuation">:</span> <span class="string">&quot;$&#123;workspaceFolder&#125;/build/Debug/outDebug.exe&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;MIMode&quot;</span><span class="punctuation">:</span> <span class="string">&quot;gdb&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;miDebuggerPath&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C:\\msys64\\ucrt64\\bin\\gdb.exe&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;setupCommands&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">        <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Enable pretty-printing for gdb&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;text&quot;</span><span class="punctuation">:</span> <span class="string">&quot;-enable-pretty-printing&quot;</span><span class="punctuation">,</span></span><br><span class="line">          <span class="attr">&quot;ignoreFailures&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">]</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="5-keybindings-json（全局配置）"><a href="#5-keybindings-json（全局配置）" class="headerlink" title="5. keybindings.json（全局配置）"></a>5. keybindings.json（全局配置）</h3><p>在 VS Code 用户设置中添加快捷键绑定（<code>Ctrl+Shift+P</code> -&gt; <code>Preferences: Open Keyboard Shortcuts (JSON)</code>）：</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;key&quot;</span><span class="punctuation">:</span> <span class="string">&quot;f6&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;command&quot;</span><span class="punctuation">:</span> <span class="string">&quot;workbench.action.tasks.runTask&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;args&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;task&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Run C++ Program&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;when&quot;</span><span class="punctuation">:</span> <span class="string">&quot;editorTextFocus&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure><h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><ol><li><strong>编译</strong>：按 <code>Ctrl+Shift+B</code> 或打开命令面板运行 “Tasks: Run Build Task”</li><li><strong>调试</strong>：按 <code>F5</code>，会启动 gdb 调试器</li><li><strong>运行</strong>：按 <code>F6</code>，会打开 cmd 黑框窗口运行程序</li></ol><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><h3 id="1-找不到-g-或-gdb"><a href="#1-找不到-g-或-gdb" class="headerlink" title="1. 找不到 g++ 或 gdb"></a>1. 找不到 g++ 或 gdb</h3><ul><li>确保 MSYS2 已正确安装并更新</li><li>检查 PATH 环境变量是否包含 <code>C:\msys64\ucrt64\bin</code></li></ul><h3 id="2-编译失败"><a href="#2-编译失败" class="headerlink" title="2. 编译失败"></a>2. 编译失败</h3><ul><li>检查代码语法错误</li><li>确保头文件路径正确</li></ul><h3 id="3-调试失败"><a href="#3-调试失败" class="headerlink" title="3. 调试失败"></a>3. 调试失败</h3><ul><li>确保已编译生成 <code>build/Debug/outDebug.exe</code></li><li>检查 gdb 路径是否正确</li></ul><h3 id="4-黑框不弹出"><a href="#4-黑框不弹出" class="headerlink" title="4. 黑框不弹出"></a>4. 黑框不弹出</h3><ul><li>重启 VS Code</li><li>检查 <code>terminal.external.windowsExec</code> 设置</li></ul><h2 id="版本信息"><a href="#版本信息" class="headerlink" title="版本信息"></a>版本信息</h2><ul><li>MSYS2: 最新版本</li><li>g++: 15.2.0</li><li>gdb: 17.1</li><li>VS Code: 最新版本</li><li>C&#x2F;C++ 扩展: 最新版本</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://www.msys2.org/docs/">MSYS2 官方文档</a></li><li><a href="https://code.visualstudio.com/docs/cpp/config-mingw">VS Code C++ 配置指南</a></li><li><a href="https://code.visualstudio.com/docs/cpp/cpp-ide">C&#x2F;C++ 扩展文档</a></li></ul>]]></content>
    
    
    <summary type="html">从零开始在VS Code中配置C++编译调试环境，适合ACM竞赛备赛与日常C++开发使用。</summary>
    
    
    
    <category term="算法竞赛" scheme="http://www.z16388.top/categories/%E7%AE%97%E6%B3%95%E7%AB%9E%E8%B5%9B/"/>
    
    
    <category term="算法" scheme="http://www.z16388.top/tags/%E7%AE%97%E6%B3%95/"/>
    
    <category term="C++" scheme="http://www.z16388.top/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>力扣笔记</title>
    <link href="http://www.z16388.top/2026/04/05/acm/leetcode/"/>
    <id>http://www.z16388.top/2026/04/05/acm/leetcode/</id>
    <published>2026-04-05T21:14:58.000Z</published>
    <updated>2026-05-29T05:52:47.780Z</updated>
    
    <content type="html"><![CDATA[<h2 id="程序模板"><a href="#程序模板" class="headerlink" title="程序模板"></a>程序模板</h2><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;cstring&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="数学模板"><a href="#数学模板" class="headerlink" title="数学模板"></a>数学模板</h2><h3 id="公约数、公倍数"><a href="#公约数、公倍数" class="headerlink" title="公约数、公倍数"></a>公约数、公倍数</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int gcd(int a,int b)  // 最大公约数</span><br><span class="line">&#123;</span><br><span class="line">if(b==0) return a;</span><br><span class="line">else return gcd(b,a%b);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">int lcm(int a,int b)  // 最小公倍数</span><br><span class="line">&#123;</span><br><span class="line">return a/gcd(a,b)*b;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="素数"><a href="#素数" class="headerlink" title="素数"></a>素数</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int prime(int b)      // 素数</span><br><span class="line">&#123;</span><br><span class="line">    for(int i=2;i&lt;=(int)sqrt(b);i++) &#123;</span><br><span class="line">        if(b%i==0) return 0;</span><br><span class="line">    &#125;</span><br><span class="line">    return 1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="排列组合打表"><a href="#排列组合打表" class="headerlink" title="排列组合打表"></a>排列组合打表</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int c[N][N];</span><br><span class="line">void com() &#123;</span><br><span class="line">memset(c,0,sizeof(c));</span><br><span class="line">for(int i=1;i&lt;N;i++) &#123;</span><br><span class="line">c[i][0]=c[i][i]=1;</span><br><span class="line">for(int j=1;j&lt;i;j++) &#123;</span><br><span class="line">c[i][j]=c[i-1][j]+c[i-1][j-1];</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="快速幂"><a href="#快速幂" class="headerlink" title="快速幂"></a>快速幂</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int Fast(int x,int n) &#123;</span><br><span class="line">int tem=x,ans=1;</span><br><span class="line">while(n) &#123;</span><br><span class="line">if(n%2==1) ans*=tem;</span><br><span class="line">tem*=tem;</span><br><span class="line">n&gt;&gt;=1;</span><br><span class="line">&#125;</span><br><span class="line">return ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="快速幂取模"><a href="#快速幂取模" class="headerlink" title="快速幂取模"></a>快速幂取模</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">int pow(int a,int x) &#123;</span><br><span class="line">int ans=1,temp=a%p;</span><br><span class="line">while(x) &#123;</span><br><span class="line">if(x&amp;1) ans=((long long)ans*temp)%p;</span><br><span class="line">temp=((long long)temp*temp)%p;</span><br><span class="line">x&gt;&gt;=1;</span><br><span class="line">&#125;</span><br><span class="line">return ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="十进制转换其他进制"><a href="#十进制转换其他进制" class="headerlink" title="十进制转换其他进制"></a>十进制转换其他进制</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">string trans(int num,int base) &#123;</span><br><span class="line">string str;</span><br><span class="line">while(num&gt;0) &#123;</span><br><span class="line">if(num%base&lt;10) &#123;</span><br><span class="line">str+=num%base+&#x27;0&#x27;;</span><br><span class="line">&#125;</span><br><span class="line">else &#123;</span><br><span class="line">str+=num%base-10+&#x27;A&#x27;;</span><br><span class="line">&#125;</span><br><span class="line">num=num/base;</span><br><span class="line">&#125;</span><br><span class="line">reverse(str.begin(),str.end());</span><br><span class="line">return str;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="常用数学公式"><a href="#常用数学公式" class="headerlink" title="常用数学公式"></a>常用数学公式</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">等差数列求和公式：</span><br><span class="line"></span><br><span class="line">Sn=n*a1+n(n-1)d/2</span><br><span class="line">Sn=n(a1+an)/2</span><br><span class="line"></span><br><span class="line">等比数列求和公式：</span><br><span class="line"></span><br><span class="line">Sn=a1(1-q^n)/(1-q)</span><br></pre></td></tr></table></figure><h2 id="常用函数"><a href="#常用函数" class="headerlink" title="常用函数"></a>常用函数</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">sort（起始地址，终点地址，比较方法）；</span><br><span class="line">lower_bound（起始地址，终点地址，查找元素）；</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">力扣算法题刷题笔记，整理常见题型的解题思路与代码方案。</summary>
    
    
    
    <category term="算法竞赛" scheme="http://www.z16388.top/categories/%E7%AE%97%E6%B3%95%E7%AB%9E%E8%B5%9B/"/>
    
    
    <category term="算法" scheme="http://www.z16388.top/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>一文详解Hexo 博客搭建</title>
    <link href="http://www.z16388.top/2026/04/03/hexo/hexo-github-actions/"/>
    <id>http://www.z16388.top/2026/04/03/hexo/hexo-github-actions/</id>
    <published>2026-04-03T14:06:32.000Z</published>
    <updated>2026-05-29T05:52:47.787Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>捡起了好久没有更新的博客。</p><p>在AI 的帮助下，我完成了整个环境的重新搭建。</p><p>由于我使用的hexo 框架和版本都比较老了，所以有些地方可能不适应于新版本的主题，在接入时还请注意。</p><p>参考视频： <a href="https://www.bilibili.com/video/BV1xTgTemEDU/">https://www.bilibili.com/video/BV1xTgTemEDU/</a></p><p>视频文档： <a href="https://xiamu-ssr.github.io/Hexo/2024/06/19/2024-H1/2024-06-19-12-31-52/">https://xiamu-ssr.github.io/Hexo/2024/06/19/2024-H1/2024-06-19-12-31-52/</a></p><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><p>使用Github Actions 服务，将hexo 部署到 Github Pages。</p><p>与参考内容不同的是，我使用了两个仓库来实现。</p><p>参考内容中，使用了同一个项目，两个分支。其中一个分支用于提交博客源文件，另一个分支用于存放生成的静态网页文件。</p><p>考虑到将博客源文件内容暴露出来会有一定的风险，于是我的方案是使用两个仓库来实现。其中，Private 的仓库用于存放博客源文件，Public 的仓库用于存放页面。</p><p>我之前是有hexo 环境的，会和从头开始实现有所区别，在接下来的内容中，我将会分别说明。</p><p>关于Github Actions 服务的用量，Public 仓库可以免费使用，但是Private 的仓库会有一定的限制。但是如果只是作为hexo的资源站，免费的额度也是可以覆盖的。</p><h2 id="hexo准备"><a href="#hexo准备" class="headerlink" title="hexo准备"></a>hexo准备</h2><p>安装并初始化hexo，如果已有hexo环境，可跳过这一步。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">npm install -g hexo-cli</span><br><span class="line">hexo init blog</span><br><span class="line">cd blog</span><br><span class="line">npm install</span><br></pre></td></tr></table></figure><p>如果是已有hexo环境，在其他地方做过备份的。可以删除node_modules目录和package-lock.json，然后重新安装：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">cd blog</span><br><span class="line">npm install</span><br></pre></td></tr></table></figure><h2 id="仓库准备"><a href="#仓库准备" class="headerlink" title="仓库准备"></a>仓库准备</h2><p>在github上新建两个仓库，一个Private的blog_base，一个Public 的username.github.io；</p><p>已有Github Pages 的，可以不用重新创建username.github.io，只创建博客源文件仓库。</p><h2 id="Token准备"><a href="#Token准备" class="headerlink" title="Token准备"></a>Token准备</h2><p>使用Github Actions ，需要生成Personal access tokens，而且至少要包含repo 权限。把这个Token 配置给Github Action， 它才有权限去执行自动部署。</p><p>首先是Token 的创建：</p><p>单击头像 -&gt; Settings -&gt; Developer Settings -&gt; Personal access tokens -&gt; Tokens (classic)</p><p>这里是所有Tokens的列表，下面我们继续创建：</p><p>Generate new token -&gt; Generate new token (classic)</p><p>新打开的界面中，我们需要给这个token 命名，随便命名即可，比如blog_base。</p><p>在下面的权限列表中，勾选repo，workflow 两项。</p><p>记录token的有效期，有效期到期后需要给Github Actions 配置新的token。</p><p>完成创建后，去到blog_base 仓库进行配置。首先打开该仓库，点击上方的Settings -&gt; Secrets and variables -&gt; Actions</p><p>点击 New repository secret 创建，Name设置为：GH_TOKEN（可随意设置，后面保持一致即可），Secret 粘贴刚才生成出来的token内容，就是一串以ghp_为开头的字符串。</p><h2 id="修改-config-yml"><a href="#修改-config-yml" class="headerlink" title="修改_config.yml"></a>修改_config.yml</h2><p>为了设置推送的地址，需要在_config.yml 中配置推送地址。这里选择Public 的Github pages 仓库地址：</p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="attr">deploy:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">git</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">https://github.com/yourusername/your-repo.git</span></span><br><span class="line">  <span class="attr">branch:</span> <span class="string">master</span></span><br></pre></td></tr></table></figure><h2 id="配置Github-Actions-工作流"><a href="#配置Github-Actions-工作流" class="headerlink" title="配置Github Actions 工作流"></a>配置Github Actions 工作流</h2><p>在blog_base 目录创建**.github<strong>文件夹，再在.github文件夹下创建</strong>workflows<strong>文件夹，然后创建</strong>deploy.yml**，内容如下：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">name: Deploy Hexo to GitHub Pages</span><br><span class="line"></span><br><span class="line">on:</span><br><span class="line">  push:</span><br><span class="line">    branches:</span><br><span class="line">      - main  # 当推送到 main 分支时触发</span><br><span class="line"></span><br><span class="line">jobs:</span><br><span class="line">  build:</span><br><span class="line">    runs-on: ubuntu-latest</span><br><span class="line"></span><br><span class="line">    steps:</span><br><span class="line">      - name: Checkout repository</span><br><span class="line">        uses: actions/checkout@v2</span><br><span class="line">        with:</span><br><span class="line">          submodules: false  # 禁用子模块检查</span><br><span class="line"></span><br><span class="line">      - name: Setup Node.js</span><br><span class="line">        uses: actions/setup-node@v2</span><br><span class="line">        with:</span><br><span class="line">          node-version: &#x27;12&#x27;</span><br><span class="line"></span><br><span class="line">      - name: Install Dependencies</span><br><span class="line">        run: npm install</span><br><span class="line"></span><br><span class="line">      - name: Install Hexo Git Deployer</span><br><span class="line">        run: |</span><br><span class="line">          npm install hexo-deployer-git --save</span><br><span class="line">          npm install hexo-cli -g</span><br><span class="line"></span><br><span class="line">      - name: Clean and Generate Static Files</span><br><span class="line">        run: |</span><br><span class="line">          hexo clean</span><br><span class="line">          hexo generate</span><br><span class="line"></span><br><span class="line">      - name: Configure Git</span><br><span class="line">        run: |</span><br><span class="line">          git config --global user.name &#x27;github-actions[bot]&#x27;</span><br><span class="line">          git config --global user.email &#x27;github-actions[bot]@users.noreply.github.com&#x27;</span><br><span class="line"></span><br><span class="line">      - name: Deploy to GitHub Pages</span><br><span class="line">        env:</span><br><span class="line">          GH_TOKEN: $&#123;&#123; secrets.GH_TOKEN &#125;&#125;</span><br><span class="line">        run: |</span><br><span class="line">          cd public/</span><br><span class="line">          git init</span><br><span class="line">          git add -A</span><br><span class="line">          git commit -m &quot;Create by workflows&quot;</span><br><span class="line">          git branch -M master</span><br><span class="line">          git remote add origin https://$&#123;&#123; secrets.GH_TOKEN &#125;&#125;@github.com/yourusername/your-repo.git</span><br><span class="line">          git push -f origin master</span><br></pre></td></tr></table></figure><p>这里最后一步是将生成的内容，推送到你配置的仓库的master 分支，我的Github Pages就是设置的这个分支。</p><p>配置里的node-version 这个参数需要注意。旧版的hexo 使用新版的node 可能会生成失败。我的主题使用的hexo 版本比较老，使用12版本是可以生成的，最新的24 反而无法生成，这里可以按需修改版本。</p><p>这个工作流的意思就是，使用ubuntu-latest作为基础环境，然后安装各种依赖，随后hexo generate生成博客网站静态文件夹，最后再推送到设定的仓库和分支。</p><h2 id="配置Github-Pages"><a href="#配置Github-Pages" class="headerlink" title="配置Github Pages"></a>配置Github Pages</h2><p>到Github Pages 仓库，点击Settings，设置page来源的分支。</p><p>推送并查看是否能够生效。</p><p>在这里，介绍一下我的.gitignore文件:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">.DS_Store</span><br><span class="line">Thumbs.db</span><br><span class="line">db.json</span><br><span class="line">*.log</span><br><span class="line">public/</span><br><span class="line">.deploy*/</span><br><span class="line">node_modules/</span><br></pre></td></tr></table></figure><p><strong>.DS_Store</strong>文件和<strong>Thumbs.db</strong>文件。这两个文件分别是Mac系统和Win系统生成的垃圾。</p><p>public 目录存放的是本地生成的静态页面，这个也不必上传。Github Actions 会自动生成。</p><p>node_modules 目录一定不能上传，需要排除掉。因为它太大了，不方便版本控制。而且不同平台需求的可能都不同。本地环境和运行Github Actions 的虚拟机环境不可能保证一致的。</p><h2 id="其他注意事项"><a href="#其他注意事项" class="headerlink" title="其他注意事项"></a>其他注意事项</h2><p>如果你之前是有Github Pages的，需要做好备份，防止文件丢失。</p><p>我就出现了CNAME文件和其他文件丢失的情况，这里需要注意。</p><h2 id="自定义域名"><a href="#自定义域名" class="headerlink" title="自定义域名"></a>自定义域名</h2><p>如果嫌username.github.io 的网址不够优雅，可以注册域名，然后配置解析。</p><p>在购买域名后，进行以下设置：</p><h3 id="Github-配置"><a href="#Github-配置" class="headerlink" title="Github 配置"></a>Github 配置</h3><p>进入Github Pages 仓库，单击Settings 切页。</p><p>在左侧边栏点击Pages。</p><p>Custom domain 配置为：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">www.yourdomin.xxx</span><br></pre></td></tr></table></figure><p>然后勾选Enforce HTTPS </p><h3 id="域名解析"><a href="#域名解析" class="headerlink" title="域名解析"></a>域名解析</h3><p>在域名解析中，做出以下设置：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">www  CNAME  yourusername.github.io</span><br><span class="line">@  AAAA  2606:50c0:8003::153</span><br><span class="line">@  AAAA  2606:50c0:8002::153</span><br><span class="line">@  AAAA  2606:50c0:8001::153</span><br><span class="line">@  AAAA  2606:50c0:8000::153</span><br><span class="line">@  A  185.199.111.153</span><br><span class="line">@  A  185.199.110.153</span><br><span class="line">@  A  185.199.108.153</span><br></pre></td></tr></table></figure><p>这里参考Github的文档：<a href="https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site">https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site</a></p><h3 id="创建CNAME"><a href="#创建CNAME" class="headerlink" title="创建CNAME"></a>创建CNAME</h3><p>需要在Github Pages 网站的根目录创建CNAME文件，内容是在第一步中配置的<a href="http://www.yourdomin.xxx/">www.yourdomin.xxx</a></p>]]></content>
    
    
    <summary type="html">详细介绍使用GitHub Actions自动化部署Hexo博客至GitHub Pages的完整配置流程。</summary>
    
    
    
    <category term="博客建站" scheme="http://www.z16388.top/categories/%E5%8D%9A%E5%AE%A2%E5%BB%BA%E7%AB%99/"/>
    
    
    <category term="hexo" scheme="http://www.z16388.top/tags/hexo/"/>
    
    <category term="Github" scheme="http://www.z16388.top/tags/Github/"/>
    
  </entry>
  
  <entry>
    <title>Unity 升级到 2022 踩坑记录（URP / 黑屏 / HTTP）</title>
    <link href="http://www.z16388.top/2026/04/01/unity/unity-up-to-2022/"/>
    <id>http://www.z16388.top/2026/04/01/unity/unity-up-to-2022/</id>
    <published>2026-04-01T17:59:46.000Z</published>
    <updated>2026-05-29T05:52:47.790Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>记录项目从旧版本升级到 Unity 2022 过程中遇到的几个典型问题及解决方案。</p></blockquote><span id="more"></span><h2 id="问题一：Package-Manager-只有-Remove-按钮，无法下载-URP"><a href="#问题一：Package-Manager-只有-Remove-按钮，无法下载-URP" class="headerlink" title="问题一：Package Manager 只有 Remove 按钮，无法下载 URP"></a>问题一：Package Manager 只有 Remove 按钮，无法下载 URP</h2><p><strong>现象</strong>：升级 Unity 版本后，打开 Package Manager，URP 包只显示 Remove，点击后也无法重新下载安装。</p><p><strong>原因</strong>：原项目对 URP 进行过源码修改，升级后残留了本地嵌入式包目录：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&#123;项目根目录&#125;/Packages/com.unity.render-pipelines.universal@xx.x.x</span><br></pre></td></tr></table></figure><p><strong>解决方案</strong>：</p><ol><li>删除 <code>Packages/</code> 目录下的 <code>com.unity.render-pipelines.universal@xx.x.x</code> 文件夹</li><li>从 <code>Library/PackageCache/</code> 目录中找到对应版本的 URP 文件夹，复制到 <code>Packages/</code> 目录下</li><li>将复制过来的文件夹名末尾的 <code>@xx.x.x</code> 版本后缀删除</li></ol><p>这样插件就恢复为本地源码模式，可以正常编辑修改。</p><hr><h2 id="问题二：Android-出包运行黑屏，URP-NullReferenceException"><a href="#问题二：Android-出包运行黑屏，URP-NullReferenceException" class="headerlink" title="问题二：Android 出包运行黑屏，URP NullReferenceException"></a>问题二：Android 出包运行黑屏，URP NullReferenceException</h2><p><strong>现象</strong>：升级后打出 APK，运行时黑屏，Logcat 报错：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Non-fatal Exception: java.lang.Exception: NullReferenceException : Object reference not set to an instance of an object.</span><br><span class="line">at UnityEngine.Rendering.Universal.UniversalRenderPipeline.SetupPerFrameShaderConstants(...)</span><br><span class="line">at UnityEngine.Rendering.Universal.UniversalRenderPipeline.Render(...)</span><br><span class="line">at UnityEngine.Rendering.RenderPipelineManager.DoRenderLoop_Internal(...)</span><br></pre></td></tr></table></figure><p><strong>原因</strong>：Unity 2022 的 URP 在初始化 Blue Noise Texture 时存在空引用问题。</p><p>参考讨论：<a href="https://discussions.unity.com/t/null-reference-in-urps-setupperframeshaderconstants/934080/5">Unity Forum - Null reference in URP’s SetupPerFrameShaderConstants</a></p><p><strong>解决方案</strong>：</p><ol><li>在 Project 窗口中找到 <code>UniversalRenderPipelineAsset.asset</code></li><li>在 Inspector 中取消勾选 <strong>LOD Cross Fade</strong></li><li>确认下方的 <strong>LOD Cross Fade Dithering Type</strong> 变为灰色（不可选）即可</li></ol><hr><h2 id="问题三：HTTP-请求失败（Unity-2022-默认禁用-HTTP）"><a href="#问题三：HTTP-请求失败（Unity-2022-默认禁用-HTTP）" class="headerlink" title="问题三：HTTP 请求失败（Unity 2022 默认禁用 HTTP）"></a>问题三：HTTP 请求失败（Unity 2022 默认禁用 HTTP）</h2><p><strong>现象</strong>：升级后 Android 包内的 HTTP 请求全部失败，网络功能异常。</p><p><strong>原因</strong>：Unity 2022 默认禁用了明文 HTTP 协议，仅允许 HTTPS。</p><p><strong>解决方案</strong>：</p><p>打开 <strong>Edit → Project Settings → Player</strong>，切换到 <strong>Android</strong> 平台，找到：</p><p><strong>Other Settings → Configuration → Allow downloads over HTTP</strong></p><p>将选项改为允许即可。</p><blockquote><p><strong>建议</strong>：长期来看应将接口迁移到 HTTPS，HTTP 存在安全风险，此配置仅作临时过渡使用。</p></blockquote>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;记录项目从旧版本升级到 Unity 2022 过程中遇到的几个典型问题及解决方案。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="游戏开发" scheme="http://www.z16388.top/categories/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Unity" scheme="http://www.z16388.top/tags/Unity/"/>
    
    <category term="Android" scheme="http://www.z16388.top/tags/Android/"/>
    
    <category term="URP" scheme="http://www.z16388.top/tags/URP/"/>
    
  </entry>
  
  <entry>
    <title>Android Google Play 16 KB 页面对齐适配指南</title>
    <link href="http://www.z16388.top/2026/03/25/android/16kb-page-size/"/>
    <id>http://www.z16388.top/2026/03/25/android/16kb-page-size/</id>
    <published>2026-03-25T17:43:34.000Z</published>
    <updated>2026-05-29T05:52:47.782Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Google Play 要求所有应用在 <strong>2026 年 5 月 31 日</strong>前完成 16 KB 内存页面大小适配。本文记录完整的检测与适配流程。</p></blockquote><span id="more"></span><h2 id="1-背景"><a href="#1-背景" class="headerlink" title="1. 背景"></a>1. 背景</h2><p>Android 15 引入了 16 KB 内存页面对齐要求，Google Play 强制要求应用中的所有 <code>.so</code> 文件满足以下三个条件：</p><ol><li><code>PT_LOAD</code> segment 对齐 &gt;&#x3D; 16 KB</li><li><code>RELRO</code> 段必须位于 segment 末尾（suffix）</li><li><code>RELRO</code> 段末尾必须 16 KB 对齐</li></ol><p>官方文档：<a href="https://developer.android.com/guide/practices/page-sizes?hl=zh-cn">Android 页面大小适配指南</a></p><hr><h2 id="2-检测方法"><a href="#2-检测方法" class="headerlink" title="2. 检测方法"></a>2. 检测方法</h2><h3 id="2-1-方法一：NDK-工具手动检测"><a href="#2-1-方法一：NDK-工具手动检测" class="headerlink" title="2.1 方法一：NDK 工具手动检测"></a>2.1 方法一：NDK 工具手动检测</h3><p>解压 APK 取出 <code>.so</code> 文件，使用 NDK 自带的 <code>llvm-objdump</code> 工具检测（<strong>必须用 PowerShell，cmd 不支持管道过滤</strong>）：</p><figure class="highlight powershell"><table><tr><td class="code"><pre><span class="line">llvm<span class="literal">-objdump</span>.exe <span class="literal">-p</span> E:\Test\so\arm64<span class="literal">-v8a</span>\libanogs.so | <span class="built_in">Select-String</span> <span class="literal">-Pattern</span> <span class="string">&quot;LOAD&quot;</span></span><br></pre></td></tr></table></figure><p>查看输出中 <code>LOAD</code> 行末尾的对齐值：</p><ul><li><code>2**14</code>（即 16384）及以上 ✅ 合规</li><li><code>2**12</code>（4096）或 <code>2**13</code>（8192）❌ 不合规</li></ul><blockquote><p>工具路径：<code>{YourNDKPath}\toolchains\llvm\prebuilt\windows-x86_64\bin</code></p><p>注意：此方法只检测 <code>PT_LOAD</code>，不检测 RELRO。</p></blockquote><hr><h3 id="2-2-方法二：Google-官方脚本检测"><a href="#2-2-方法二：Google-官方脚本检测" class="headerlink" title="2.2 方法二：Google 官方脚本检测"></a>2.2 方法二：Google 官方脚本检测</h3><p>Google 提供了完整的 ELF 对齐检测脚本，可同时检测 PT_LOAD 和 RELRO：</p><p>脚本地址：<a href="https://cs.android.com/android/platform/superproject/main/+/main:system/extras/tools/check_elf_alignment.sh">check_elf_alignment.sh</a></p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">check_elf_alignment.sh APK_NAME.apk</span><br></pre></td></tr></table></figure><hr><h3 id="2-3-方法三：Android-Studio-APK-Analyzer"><a href="#2-3-方法三：Android-Studio-APK-Analyzer" class="headerlink" title="2.3 方法三：Android Studio APK Analyzer"></a>2.3 方法三：Android Studio APK Analyzer</h3><p>将 Android Studio 升级到最新版本，使用内置的 <strong>APK Analyzer</strong> 功能即可可视化检测。</p><p>常见报错信息：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># PT_LOAD 不合规</span><br><span class="line">4 KB LOAD section alignment, but 16 KB is required</span><br><span class="line"></span><br><span class="line"># RELRO 不合规</span><br><span class="line">RELRO is not a suffix and its end is not 16 KB aligned</span><br></pre></td></tr></table></figure><hr><h2 id="3-适配方法"><a href="#3-适配方法" class="headerlink" title="3. 适配方法"></a>3. 适配方法</h2><h3 id="3-1-Unity-相关-so"><a href="#3-1-Unity-相关-so" class="headerlink" title="3.1 Unity 相关 .so"></a>3.1 Unity 相关 .so</h3><p>Unity 引擎生成的 <code>.so</code> 不合规时，需要<strong>升级 Unity 编辑器版本</strong>来解决，无法手动修改。</p><p>升级后若 C++ 代码没有重新导出，可以删除以下缓存目录强制重新生成：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Library/Bee/</span><br><span class="line">Library/il2cpp_*/</span><br></pre></td></tr></table></figure><h3 id="3-2-第三方-SDK-的-so"><a href="#3-2-第三方-SDK-的-so" class="headerlink" title="3.2 第三方 SDK 的 .so"></a>3.2 第三方 SDK 的 .so</h3><p>联系 SDK 提供商升级至合规版本。若提供商无法及时支持，应考虑移除该 SDK。</p><h3 id="3-3-自行用-NDK-编译的-so"><a href="#3-3-自行用-NDK-编译的-so" class="headerlink" title="3.3 自行用 NDK 编译的 .so"></a>3.3 自行用 NDK 编译的 .so</h3><h4 id="升级-NDK-并添加链接参数"><a href="#升级-NDK-并添加链接参数" class="headerlink" title="升级 NDK 并添加链接参数"></a>升级 NDK 并添加链接参数</h4><p>在编译命令中增加 <code>max-page-size</code> 和 <code>common-page-size</code> 参数：</p><figure class="highlight bat"><table><tr><td class="code"><pre><span class="line">&#123;NDK <span class="built_in">Path</span>&#125;/ndk-build ^</span><br><span class="line">NDK_PROJECT_PATH=. ^</span><br><span class="line">APP_BUILD_SCRIPT=Android.mk ^</span><br><span class="line">NDK_APPLICATION_MK=Application.mk ^</span><br><span class="line">APP_ABI=&quot;armeabi-v7a x86 arm64-v8a x86_64&quot; ^</span><br><span class="line">APP_LDFLAGS=&quot;-Wl,-z,max-page-size=<span class="number">16384</span> -Wl,-z,common-page-size=<span class="number">16384</span>&quot;</span><br></pre></td></tr></table></figure><h4 id="修改-Android-mk"><a href="#修改-Android-mk" class="headerlink" title="修改 Android.mk"></a>修改 Android.mk</h4><p>在 <code>Android.mk</code> 中增加以下链接标志，确保 RELRO 对齐：</p><figure class="highlight makefile"><table><tr><td class="code"><pre><span class="line">LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384</span><br><span class="line">LOCAL_LDFLAGS += -Wl,-z,common-page-size=16384</span><br></pre></td></tr></table></figure><h4 id="修改-Application-mk"><a href="#修改-Application-mk" class="headerlink" title="修改 Application.mk"></a>修改 Application.mk</h4><p><strong>1. 移除 <code>cmd-strip</code> 调用</strong></p><p>NDK r23+ 中 strip 工具路径改为 LLVM 版本，旧的 <code>cmd-strip</code> 路径会报错。新版 NDK 会自动调用 strip，直接删除该行即可。</p><p><strong>2. 替换 STL 类型</strong></p><figure class="highlight makefile"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 旧（已废弃）</span></span><br><span class="line">APP_STL := gnustl_static</span><br><span class="line"></span><br><span class="line"><span class="comment"># 改为</span></span><br><span class="line">APP_STL := c++_static</span><br></pre></td></tr></table></figure><h4 id="补充缺失的-PAGE-SIZE-宏"><a href="#补充缺失的-PAGE-SIZE-宏" class="headerlink" title="补充缺失的 PAGE_SIZE 宏"></a>补充缺失的 PAGE_SIZE 宏</h4><p>NDK 新版本不再提供 <code>PAGE_SIZE</code> &#x2F; <code>PAGE_MASK</code> 宏，若代码中有使用，需手动定义：</p><figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PAGE_SIZE</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PAGE_SIZE 4096</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PAGE_MASK</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PAGE_MASK (~(PAGE_SIZE - 1))</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Google Play 要求所有应用在 &lt;strong&gt;2026 年 5 月 31 日&lt;/strong&gt;前完成 16 KB 内存页面大小适配。本文记录完整的检测与适配流程。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="移动开发" scheme="http://www.z16388.top/categories/%E7%A7%BB%E5%8A%A8%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Unity" scheme="http://www.z16388.top/tags/Unity/"/>
    
    <category term="Android" scheme="http://www.z16388.top/tags/Android/"/>
    
    <category term="Google Play" scheme="http://www.z16388.top/tags/Google-Play/"/>
    
    <category term="NDK" scheme="http://www.z16388.top/tags/NDK/"/>
    
  </entry>
  
  <entry>
    <title>iOS 应用开启包外存储访问（文件共享）</title>
    <link href="http://www.z16388.top/2026/03/23/ios/ios-get-files/"/>
    <id>http://www.z16388.top/2026/03/23/ios/ios-get-files/</id>
    <published>2026-03-23T17:50:56.000Z</published>
    <updated>2026-05-29T05:52:47.788Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>新版 Xcode 工程中，包外存储（文件共享）选项的 GUI 入口被移除，需要手动修改 <code>Info.plist</code> 才能开启，方便在开发测试阶段通过 Finder &#x2F; iTunes 访问应用沙盒中的文件。</p></blockquote><span id="more"></span><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>iOS 应用默认将数据存储在沙盒中，外部无法直接访问。开启文件共享后，用户可以通过 macOS Finder 或 iTunes 直接读写应用 <code>Documents</code> 目录下的文件，常用于：</p><ul><li>测试阶段导出日志、存档文件</li><li>验证应用写入的资源是否正确</li></ul><h2 id="配置方法"><a href="#配置方法" class="headerlink" title="配置方法"></a>配置方法</h2><h3 id="方法一：直接修改-Info-plist-源码"><a href="#方法一：直接修改-Info-plist-源码" class="headerlink" title="方法一：直接修改 Info.plist 源码"></a>方法一：直接修改 Info.plist 源码</h3><p>打开 Xcode 工程中的 <code>Info.plist</code>，右键选择 <strong>Open As → Source Code</strong>，在 <code>&lt;dict&gt;</code> 内添加以下两个键：</p><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 开启 iTunes/Finder 文件共享，允许访问 Documents 目录 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">key</span>&gt;</span>UIFileSharingEnabled<span class="tag">&lt;/<span class="name">key</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">true</span>/&gt;</span></span><br></pre></td></tr></table></figure><h3 id="方法二：通过-Xcode-属性编辑器添加"><a href="#方法二：通过-Xcode-属性编辑器添加" class="headerlink" title="方法二：通过 Xcode 属性编辑器添加"></a>方法二：通过 Xcode 属性编辑器添加</h3><ol><li>在 Xcode 中打开 <code>Info.plist</code>（默认的图形化编辑器）</li><li>点击任意行末尾的 <strong>+</strong> 按钮新增条目</li><li>搜索并选择 <code>Application supports iTunes file sharing</code>，值设为 <code>YES</code></li><li>可选：再添加 <code>Supports opening documents in place</code>，值设为 <code>YES</code></li></ol><hr><h2 id="Unity-项目注意事项"><a href="#Unity-项目注意事项" class="headerlink" title="Unity 项目注意事项"></a>Unity 项目注意事项</h2><p>Unity 导出 Xcode 工程时，每次导出会覆盖 <code>Info.plist</code>，导致手动添加的配置丢失。</p><p>推荐使用 <strong>后处理脚本</strong>自动写入，在 <code>Editor</code> 目录下创建脚本：</p><figure class="highlight csharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEditor;</span><br><span class="line"><span class="keyword">using</span> UnityEditor.Callbacks;</span><br><span class="line"><span class="keyword">using</span> UnityEditor.iOS.Xcode;</span><br><span class="line"><span class="keyword">using</span> System.IO;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">IOSFileSharingPostProcess</span></span><br><span class="line">&#123;</span><br><span class="line">    [<span class="meta">PostProcessBuild</span>]</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">OnPostProcessBuild</span>(<span class="params">BuildTarget target, <span class="built_in">string</span> path</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (target != BuildTarget.iOS) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">string</span> plistPath = Path.Combine(path, <span class="string">&quot;Info.plist&quot;</span>);</span><br><span class="line">        PlistDocument plist = <span class="keyword">new</span> PlistDocument();</span><br><span class="line">        plist.ReadFromFile(plistPath);</span><br><span class="line"></span><br><span class="line">        plist.root.SetBoolean(<span class="string">&quot;UIFileSharingEnabled&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">        plist.root.SetBoolean(<span class="string">&quot;LSSupportsOpeningDocumentsInPlace&quot;</span>, <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">        plist.WriteToFile(plistPath);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="访问方式"><a href="#访问方式" class="headerlink" title="访问方式"></a>访问方式</h2><p>开启后，通过以下方式在电脑端访问应用文件：</p><ul><li><strong>macOS Finder</strong>：连接 iPhone → 在 Finder 侧边栏选择设备 → 切换到「文件」标签 → 找到对应应用</li><li><strong>iTunes（Windows）</strong>：设备 → 应用 → 文件共享 → 选择应用</li></ul>]]></content>
    
    
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;新版 Xcode 工程中，包外存储（文件共享）选项的 GUI 入口被移除，需要手动修改 &lt;code&gt;Info.plist&lt;/code&gt; 才能开启，方便在开发测试阶段通过 Finder &amp;#x2F; iTunes 访问应用沙盒中的文件。&lt;/p&gt;
&lt;/blockquote&gt;</summary>
    
    
    
    <category term="移动开发" scheme="http://www.z16388.top/categories/%E7%A7%BB%E5%8A%A8%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Unity" scheme="http://www.z16388.top/tags/Unity/"/>
    
    <category term="iOS" scheme="http://www.z16388.top/tags/iOS/"/>
    
    <category term="Xcode" scheme="http://www.z16388.top/tags/Xcode/"/>
    
  </entry>
  
  <entry>
    <title>xlua学习笔记</title>
    <link href="http://www.z16388.top/2021/05/18/languages/xlua/"/>
    <id>http://www.z16388.top/2021/05/18/languages/xlua/</id>
    <published>2021-05-18T19:05:07.000Z</published>
    <updated>2026-05-29T05:52:47.789Z</updated>
    
    <content type="html"><![CDATA[<h1 id="文件加载"><a href="#文件加载" class="headerlink" title="文件加载"></a>文件加载</h1><p>使用LuaEnv中的DoString方法来执行Lua脚本。<br>因为创建一个LuaEnv相当于创建了一个lua虚拟机，所以一个游戏最好只有一个LuaEnv。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 执行lua代码</span></span><br><span class="line">env.DoString(<span class="string">&quot;print(\&quot;Hello World\&quot;)&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过文件执行lua代码</span></span><br><span class="line">TextAsset ta = Resources.Load&lt;TextAsset&gt;(<span class="string">&quot;Lua/helloworld.lua&quot;</span>);</span><br><span class="line">env.DoString(ta.text);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过默认Loader执行lua代码</span></span><br><span class="line">env.DoString(<span class="string">&quot;require &#x27;Lua/helloworld&#x27;&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过自定义Loader执行lua代码</span></span><br><span class="line"><span class="comment">// require实际上是调一个个的Loader去加载，有一个成功就不再往下尝试，全失败则报文件找不到。</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="built_in">byte</span>[] <span class="title">LuaLoader</span>(<span class="params"><span class="keyword">ref</span> <span class="built_in">string</span> filePath</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">string</span> path = <span class="string">&quot;Lua/&quot;</span> + filePath + <span class="string">&quot;.lua&quot;</span>;</span><br><span class="line">    print(path);</span><br><span class="line">    TextAsset ta = Resources.Load&lt;TextAsset&gt;(path);</span><br><span class="line">    <span class="keyword">return</span> System.Text.Encoding.UTF8.GetBytes(ta.text);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">env.AddLoader(LuaLoader);</span><br><span class="line">env.DoString(<span class="string">&quot;require &#x27;helloworld&#x27;&quot;</span>);</span><br></pre></td></tr></table></figure><p>官方建议的加载Lua脚本方式是：整个程序就一个DoString(“require ‘main’”)，然后在main.lua加载其它脚本（类似lua脚本的命令行执行：lua main.lua）。</p><h1 id="C-访问Lua普通全局变量"><a href="#C-访问Lua普通全局变量" class="headerlink" title="C#访问Lua普通全局变量"></a>C#访问Lua普通全局变量</h1><p>先加载lua脚本，再通过LuaEnv.Global.Get获取变量。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line">TextAsset ta = Resources.Load&lt;TextAsset&gt;(<span class="string">&quot;Lua/variable.lua&quot;</span>);</span><br><span class="line">env.DoString(ta.text);</span><br><span class="line"></span><br><span class="line"><span class="built_in">int</span> g_int = env.Global.Get&lt;<span class="built_in">int</span>&gt;(<span class="string">&quot;g_int&quot;</span>);</span><br><span class="line"><span class="built_in">string</span> g_str = env.Global.Get&lt;<span class="built_in">string</span>&gt;(<span class="string">&quot;g_str&quot;</span>);</span><br><span class="line"><span class="built_in">bool</span> g_bool = env.Global.Get&lt;<span class="built_in">bool</span>&gt;(<span class="string">&quot;g_bool&quot;</span>);</span><br></pre></td></tr></table></figure><h1 id="C-访问Lua-table的四种方法"><a href="#C-访问Lua-table的四种方法" class="headerlink" title="C#访问Lua table的四种方法"></a>C#访问Lua table的四种方法</h1><p>需要注意的是，在构建C#中对应的结构时，接口一定要是public修饰，否则会报错。类最好也是，但是经测试，没有报错。</p><p><strong>映射到普通class或struct</strong></p><p>table的属性可以多于或者少于class的属性，也就是不一定都需要做映射。这个过程是值拷贝，如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table，反过来也不会。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Person</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> name;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">int</span> age;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person p = env.Global.Get&lt;Person&gt;(<span class="string">&quot;g_tbl&quot;</span>);</span><br></pre></td></tr></table></figure><p><strong>映射到接口</strong></p><p>一定要加[CSharpCallLua]和public，否则会报错。</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">g_int = <span class="number">1</span></span><br><span class="line">g_str = <span class="string">&quot;Hello World Variable&quot;</span></span><br><span class="line">g_bool = <span class="literal">false</span></span><br><span class="line">g_tbl = &#123;name = <span class="string">&quot;Juhnny&quot;</span>, age = <span class="number">12</span>&#125;</span><br><span class="line">g_tbl.say = <span class="function"><span class="keyword">function</span><span class="params">(this, str)</span></span></span><br><span class="line">    <span class="built_in">print</span>(str)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line">[<span class="meta">CSharpCallLua</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title">IPerson</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">string</span> name &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="built_in">int</span> age &#123; <span class="keyword">get</span>; <span class="keyword">set</span>; &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">say</span>(<span class="params"><span class="built_in">string</span> str</span>)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">IPerson p = env.Global.Get&lt;IPerson&gt;(<span class="string">&quot;g_tbl&quot;</span>);</span><br><span class="line">p.say(<span class="string">&quot;Yahoo&quot;</span>);</span><br></pre></td></tr></table></figure><p><strong>映射到Dictionary和List</strong></p><p>这种方法较方法2更为轻量级，但是要求table下key和value类型一致。</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">g_dic = &#123;apple = <span class="string">&quot;iPhone&quot;</span>, flower = <span class="string">&quot;Huawei&quot;</span>&#125;</span><br><span class="line">g_list = &#123;<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line">Dictionary&lt;<span class="built_in">string</span>, <span class="built_in">string</span>&gt; dic = env.Global.Get&lt;Dictionary&lt;<span class="built_in">string</span>, <span class="built_in">string</span>&gt;&gt;(<span class="string">&quot;g_dic&quot;</span>);</span><br><span class="line"><span class="keyword">foreach</span> (<span class="keyword">var</span> child <span class="keyword">in</span> dic)</span><br><span class="line">&#123;</span><br><span class="line">    print(<span class="built_in">string</span>.Format(<span class="string">&quot;Key is &#123;0&#125;, Value is &#123;1&#125;&quot;</span>, child.Key, child.Value));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">List&lt;<span class="built_in">int</span>&gt; list = env.Global.Get&lt;List&lt;<span class="built_in">int</span>&gt;&gt;(<span class="string">&quot;g_list&quot;</span>);</span><br><span class="line"><span class="keyword">foreach</span> (<span class="keyword">var</span> v <span class="keyword">in</span> list)</span><br><span class="line">&#123;</span><br><span class="line">    print(v);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>映射到LuaTable</strong></p><p>这种方法比较慢，而且没有类型检查，不推荐使用。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line">LuaTable tab = env.Global.Get&lt;LuaTable&gt;(<span class="string">&quot;g_tbl&quot;</span>);</span><br><span class="line"><span class="built_in">string</span> name = tab.Get&lt;<span class="built_in">string</span>&gt;(<span class="string">&quot;name&quot;</span>);</span><br><span class="line"><span class="built_in">int</span> age = tab.Get&lt;<span class="built_in">int</span>&gt;(<span class="string">&quot;age&quot;</span>);</span><br><span class="line">print(<span class="built_in">string</span>.Format(<span class="string">&quot;Name is &#123;0&#125;, age is &#123;1&#125;&quot;</span>, name, age));</span><br><span class="line"><span class="keyword">foreach</span> (<span class="built_in">string</span> key <span class="keyword">in</span> tab.GetKeys())</span><br><span class="line">&#123;</span><br><span class="line">    print(tab.Get&lt;<span class="built_in">object</span>&gt;(key));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="C-访问Lua全局函数"><a href="#C-访问Lua全局函数" class="headerlink" title="C#访问Lua全局函数"></a>C#访问Lua全局函数</h1><p><strong>使用delegate映射</strong></p><p>这种是建议的方式，性能好很多，而且类型安全，但是要生成代码。支持带返回值，多返回值，甚至返回值都可以是delegate。</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">g_func1 = <span class="function"><span class="keyword">function</span><span class="params">()</span></span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;FF&quot;</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">g_func2 = <span class="function"><span class="keyword">function</span><span class="params">(num)</span></span></span><br><span class="line">    <span class="built_in">print</span>(num)</span><br><span class="line">    <span class="keyword">return</span> num, <span class="string">&quot;Yoshida&quot;</span>, <span class="string">&quot;SE&quot;</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line">[<span class="meta">CSharpCallLua</span>]</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="built_in">delegate</span> <span class="built_in">int</span> <span class="title">Func2</span>(<span class="params"><span class="built_in">int</span> num, <span class="keyword">out</span> <span class="built_in">string</span> name, <span class="keyword">out</span> <span class="built_in">string</span> company</span>)</span>;</span><br><span class="line"><span class="comment">// 声明委托时，一定要加public和[CSharpCallLua]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 无返回</span></span><br><span class="line">Action func1 = env.Global.Get&lt;Action&gt;(<span class="string">&quot;g_func1&quot;</span>);</span><br><span class="line">func1();</span><br><span class="line">func1 = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 单返回值</span></span><br><span class="line">Func2 func2 = env.Global.Get&lt;Func2&gt;(<span class="string">&quot;g_func2&quot;</span>);</span><br><span class="line"><span class="built_in">int</span> num = func2(<span class="number">14</span>);</span><br><span class="line">func2 = <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 多返回值</span></span><br><span class="line">Func2 func2 = env.Global.Get&lt;Func2&gt;(<span class="string">&quot;g_func2&quot;</span>);</span><br><span class="line"><span class="built_in">string</span> name;</span><br><span class="line"><span class="built_in">string</span> company;</span><br><span class="line"><span class="built_in">int</span> num = func2(<span class="number">14</span>, <span class="keyword">out</span> name, <span class="keyword">out</span> company);</span><br><span class="line">func2 = <span class="literal">null</span></span><br></pre></td></tr></table></figure><p>在过去的一些版本中，需要在不使用后，将接收到的函数销毁，否则会在LuaEnv.Dispose的时候报错。</p><p><strong>使用LuaFunction</strong></p><p>与上一种方法优缺点相反，不推荐使用。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line">LuaFunction func = env.Global.Get&lt;LuaFunction&gt;(<span class="string">&quot;g_func2&quot;</span>);</span><br><span class="line"><span class="built_in">object</span>[] res = func.Call(<span class="number">14</span>);</span><br></pre></td></tr></table></figure><h1 id="Lua调用C"><a href="#Lua调用C" class="headerlink" title="Lua调用C#"></a>Lua调用C#</h1><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 读静态属性</span></span><br><span class="line">CS.UnityEngine.Time.deltaTime</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写静态属性</span></span><br><span class="line">CS.UnityEngine.Time.timeScale = <span class="number">0.5</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 调用静态方法</span></span><br><span class="line">CS.UnityEngine.GameObject.Find(<span class="string">&#x27;helloworld&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 读写成员属性类似，但是访问成员方法要使用:</span></span><br><span class="line">testobj:DMFunc()</span><br></pre></td></tr></table></figure><h1 id="其它注意事项"><a href="#其它注意事项" class="headerlink" title="其它注意事项"></a>其它注意事项</h1><p><strong>方法的参数处理</strong><br>Lua调用侧的参数处理规则：C#的普通参数算一个输入形参，ref修饰的算一个输入形参，out不算，然后从左往右对应lua 调用侧的实参列表。</p><p>Lua调用侧的返回值处理规则：C#函数的返回值（如果有的话）算一个返回值，out算一个返回值，ref算一个返回值，然后从左往右对应lua的多返回值。</p><p><strong>可变参数的处理</strong></p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 对于C#的如下方法：</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">VariableParamsFunc</span>(<span class="params"><span class="built_in">int</span> a, <span class="keyword">params</span> <span class="built_in">string</span>[] strs</span>)</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="comment">// 可以在lua里头这样调用：</span></span></span><br><span class="line"><span class="function">testobj:<span class="title">VariableParamsFunc</span>(<span class="params"><span class="number">5</span>, <span class="string">&#x27;hello&#x27;</span>, <span class="string">&#x27;john&#x27;</span></span>)</span></span><br></pre></td></tr></table></figure><p><strong>枚举</strong></p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 枚举值就像枚举类型下的静态属性一样。</span></span><br><span class="line"></span><br><span class="line">    testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)</span><br><span class="line"></span><br><span class="line">上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的。</span><br><span class="line"></span><br><span class="line">枚举类支持__CastFrom方法，可以实现从一个整数或者字符串到枚举值的转换，例如：</span><br><span class="line"></span><br><span class="line">    CS.Tutorial.TestEnum.__CastFrom(<span class="number">1</span>)</span><br><span class="line">    CS.Tutorial.TestEnum.__CastFrom(<span class="string">&#x27;E1&#x27;</span>)</span><br></pre></td></tr></table></figure><p><strong>委托</strong></p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="comment">-- 使用+-号来实现委托</span></span><br><span class="line"><span class="comment">-- 比如testobj里头有个事件定义是这样：public event Action TestEvent;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 增加事件回调</span></span><br><span class="line">testobj:TestEvent(<span class="string">&#x27;+&#x27;</span>, lua_event_callback)</span><br><span class="line"></span><br><span class="line"><span class="comment">--移除事件回调</span></span><br><span class="line">testobj:TestEvent(<span class="string">&#x27;-&#x27;</span>, lua_event_callback)</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">xLua热更新框架学习笔记，介绍在Unity中使用xLua实现C#与Lua互调的方法与实践经验。</summary>
    
    
    
    <category term="游戏开发" scheme="http://www.z16388.top/categories/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="游戏" scheme="http://www.z16388.top/tags/%E6%B8%B8%E6%88%8F/"/>
    
    <category term="xlua" scheme="http://www.z16388.top/tags/xlua/"/>
    
    <category term="热更新" scheme="http://www.z16388.top/tags/%E7%83%AD%E6%9B%B4%E6%96%B0/"/>
    
  </entry>
  
  <entry>
    <title>Lua新知</title>
    <link href="http://www.z16388.top/2021/05/10/languages/luanew/"/>
    <id>http://www.z16388.top/2021/05/10/languages/luanew/</id>
    <published>2021-05-10T11:42:01.000Z</published>
    <updated>2026-05-29T05:52:47.789Z</updated>
    
    <content type="html"><![CDATA[<p>两年没有碰过Lua了，总觉得得要捡起来。现在从头到尾再过一遍，总结一些东西。之前根据菜鸟学的时候，使用了5.1.1的版本。与现在常用的版本相悖。因此这次我使用了：zerobrane studio，它可以方便切换版本，能实现很好的练习效果。</p><h1 id="关于多行注释的安全问题"><a href="#关于多行注释的安全问题" class="headerlink" title="关于多行注释的安全问题"></a>关于多行注释的安全问题</h1><p>如果直接使用默认的<code>--[[</code>和<code>--]]</code>来进行多行注释的时候,如果遇到table包table的情况会直接结束注释:</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="comment">--[[</span></span><br><span class="line"><span class="comment">a = [];</span></span><br><span class="line"><span class="comment">b = [a];</span></span><br><span class="line"><span class="comment">a[b[1]]</span></span><br><span class="line">a = <span class="number">1</span></span><br><span class="line"><span class="comment">--]]</span></span><br></pre></td></tr></table></figure><p>可以使用另一种方法来进行多行注释,避免这个问题:</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="comment">--[=[</span></span><br><span class="line"><span class="comment">a = [];</span></span><br><span class="line"><span class="comment">b = [a];</span></span><br><span class="line"><span class="comment">a[b[1]]</span></span><br><span class="line">a = <span class="number">1</span></span><br><span class="line">]=]</span><br></pre></td></tr></table></figure><p>当然,最安全的方法还是使用IDE的快捷键进行注释.IDE会在每行的前面加单行注释,避免错误的发生.</p><h1 id="多行字符串安全问题"><a href="#多行字符串安全问题" class="headerlink" title="多行字符串安全问题"></a>多行字符串安全问题</h1><p>多行字符串,也会出现类似于上面的情况:</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">str = </span><br><span class="line"><span class="string">[[</span></span><br><span class="line"><span class="string">话说</span></span><br><span class="line"><span class="string">这个东西</span></span><br><span class="line"><span class="string">好像打印不出来</span></span><br><span class="line"><span class="string">就是这个</span></span><br><span class="line"><span class="string">table[table[idx]]</span></span><br><span class="line">咋办啊</span><br><span class="line">]]</span><br><span class="line"><span class="built_in">print</span>(str)</span><br></pre></td></tr></table></figure><h1 id="运算符相关"><a href="#运算符相关" class="headerlink" title="运算符相关"></a>运算符相关</h1><p>#运算符取的是最大索引的值，如果删除了中间的值，获得的结果不会改变：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">tb_a = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>&#125;</span><br><span class="line"><span class="built_in">print</span> (#tb_a)</span><br><span class="line"></span><br><span class="line">tb_a[<span class="number">2</span>] = <span class="literal">nil</span></span><br><span class="line"><span class="built_in">print</span> (#tb_a)</span><br></pre></td></tr></table></figure><p>但是这种情况仅限于Lua 5.1 ，Lua5.3和Lua5.4结果是1。</p><p>模拟三目运算符的<code>and...or...</code>，在第二个参数为false时，始终返回c，会出现问题：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">a = <span class="literal">true</span></span><br><span class="line"><span class="built_in">print</span> (a <span class="keyword">and</span> <span class="literal">false</span> <span class="keyword">or</span> <span class="literal">true</span>)</span><br></pre></td></tr></table></figure><p>输出结果会是true 而不是三目运算符应该返回的false。<br>这时候，可以将三目运算符的后面两个部分转换成table，运算结束后再取第一个元素，即可实现：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">a = <span class="literal">true</span></span><br><span class="line">b = (a <span class="keyword">and</span> &#123;<span class="literal">false</span>&#125; <span class="keyword">or</span> &#123;<span class="literal">true</span>&#125;)[<span class="number">1</span>]</span><br><span class="line"><span class="keyword">if</span> b <span class="keyword">then</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;true&quot;</span>)</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;false&quot;</span>)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h1 id="自定义迭代器"><a href="#自定义迭代器" class="headerlink" title="自定义迭代器"></a>自定义迭代器</h1><p>自定义迭代器的格式：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> 变量列表 <span class="keyword">in</span> 迭代函数, 状态变量, 控制变量 <span class="keyword">do</span></span><br><span class="line">    <span class="comment">-- 循环体</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>自定义无状态迭代器</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">square</span><span class="params">(iteratorMaxCount, currentNumber)</span></span></span><br><span class="line">    <span class="keyword">if</span> currentNumber &lt; iteratorMaxCount <span class="keyword">then</span></span><br><span class="line">        currentNumber = currentNumber + <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> currentNumber, currentNumber * currentNumber</span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i, n <span class="keyword">in</span> square, <span class="number">3</span>, <span class="number">0</span></span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">print</span>(i, n)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>多状态迭代器：<br>多个状态可以存放在table，因此只需要一个参数即可：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">array = &#123;<span class="string">&quot;Google&quot;</span>, <span class="string">&quot;Runoob&quot;</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">elementIterator</span> <span class="params">(collection)</span></span></span><br><span class="line">    <span class="keyword">local</span> index = <span class="number">0</span></span><br><span class="line">    <span class="keyword">local</span> count = #collection</span><br><span class="line">    <span class="comment">-- 闭包函数</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="params">()</span></span></span><br><span class="line">        index = index + <span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span> index &lt;= count <span class="keyword">then</span></span><br><span class="line">            <span class="comment">--  返回迭代器的当前元素</span></span><br><span class="line">            <span class="keyword">return</span> collection[index]</span><br><span class="line">        <span class="keyword">end</span></span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> element <span class="keyword">in</span> elementIterator(array) <span class="keyword">do</span></span><br><span class="line">    <span class="built_in">print</span>(element)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h1 id="循环时的goto语句"><a href="#循环时的goto语句" class="headerlink" title="循环时的goto语句"></a>循环时的goto语句</h1><p>可以使用goto语句来实现continue：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> i=<span class="number">1</span>, <span class="number">3</span> <span class="keyword">do</span></span><br><span class="line">    <span class="keyword">if</span> i &lt;= <span class="number">2</span> <span class="keyword">then</span></span><br><span class="line">        <span class="built_in">print</span>(i, <span class="string">&quot;yes continue&quot;</span>)</span><br><span class="line">        <span class="keyword">goto</span> continue</span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line">    <span class="built_in">print</span>(i, <span class="string">&quot; no continue&quot;</span>)</span><br><span class="line">    ::continue::</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">[[i&#x27;m end]]</span>)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h1 id="元表"><a href="#元表" class="headerlink" title="元表"></a>元表</h1><p>Lua可以通过设置元表，定义元方法的方式，改变table的一些行为：</p><p>__index元方法，可以修改按索引取值的逻辑。<br>若__index内容是一个表的话，如果table里没有值，会从元表中对应的索引去取：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">t_table = <span class="built_in">setmetatable</span>(&#123;k1 = <span class="string">&quot;v1&quot;</span>&#125;, &#123;<span class="built_in">__index</span> = &#123;k2 = <span class="string">&quot;v2&quot;</span>&#125;&#125;)</span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="string">&quot;k1&quot;</span>])</span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="string">&quot;k2&quot;</span>])</span><br><span class="line">t_table[<span class="string">&quot;k2&quot;</span>] = <span class="string">&quot;v3&quot;</span></span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="string">&quot;k2&quot;</span>])</span><br><span class="line"><span class="comment">--[[</span></span><br><span class="line"><span class="comment">输出是：</span></span><br><span class="line"><span class="comment">v1</span></span><br><span class="line"><span class="comment">v2</span></span><br><span class="line"><span class="comment">v3</span></span><br><span class="line"><span class="comment">--]]</span></span><br></pre></td></tr></table></figure><p>若__index内容是一个函数，则可以定义返回逻辑：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">t_table = <span class="built_in">setmetatable</span>(&#123;&#125;, &#123;<span class="built_in">__index</span> =</span><br><span class="line"><span class="function"><span class="keyword">function</span><span class="params">(t_table, key)</span></span></span><br><span class="line"><span class="keyword">if</span> key == <span class="number">1</span> <span class="keyword">then</span></span><br><span class="line"><span class="keyword">return</span> <span class="string">&quot;true&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"><span class="keyword">return</span> <span class="string">&quot;false&quot;</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="number">1</span>])</span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="number">2</span>])</span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="number">3</span>])</span><br><span class="line"></span><br><span class="line">t_table = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="number">1</span>])</span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="number">2</span>])</span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="number">3</span>])</span><br><span class="line"><span class="comment">--[[</span></span><br><span class="line"><span class="comment">输出是：</span></span><br><span class="line"><span class="comment">true</span></span><br><span class="line"><span class="comment">false</span></span><br><span class="line"><span class="comment">false</span></span><br><span class="line"><span class="comment">1</span></span><br><span class="line"><span class="comment">2</span></span><br><span class="line"><span class="comment">3</span></span><br><span class="line"><span class="comment">--]]</span></span><br></pre></td></tr></table></figure><p>__newindex元方法，可以修改追加索引时的逻辑</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">t_table = <span class="built_in">setmetatable</span>(&#123;k1 = <span class="string">&quot;v1&quot;</span>&#125;, &#123;</span><br><span class="line"><span class="built_in">__newindex</span> =</span><br><span class="line"><span class="function"><span class="keyword">function</span><span class="params">(t_table, key, value)</span></span></span><br><span class="line"><span class="built_in">rawset</span>(t_table, key, <span class="string">&quot;\&quot;v&quot;</span> .. value .. <span class="string">&quot;\&quot;&quot;</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line">t_table[<span class="string">&quot;k2&quot;</span>] = <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="string">&quot;k1&quot;</span>])</span><br><span class="line"><span class="built_in">print</span>(t_table[<span class="string">&quot;k2&quot;</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment">--[[</span></span><br><span class="line"><span class="comment">输出是：</span></span><br><span class="line"><span class="comment">v1</span></span><br><span class="line"><span class="comment">&quot;v2&quot;</span></span><br><span class="line"><span class="comment">--]]</span></span><br></pre></td></tr></table></figure><p>其它的规则：</p><table><thead><tr><th align="center">元表</th><th align="center">运算符</th></tr></thead><tbody><tr><td align="center">__add</td><td align="center">+</td></tr><tr><td align="center">__sub</td><td align="center">-</td></tr><tr><td align="center">__mul</td><td align="center">*</td></tr><tr><td align="center">__div</td><td align="center">&#x2F;</td></tr><tr><td align="center">__mod</td><td align="center">%</td></tr><tr><td align="center">__unm</td><td align="center">-(相反数)</td></tr><tr><td align="center">__concat</td><td align="center">..</td></tr><tr><td align="center">__eq</td><td align="center">&#x3D;&#x3D;</td></tr><tr><td align="center">__lt</td><td align="center">&lt;</td></tr><tr><td align="center">__le</td><td align="center">&lt;&#x3D;</td></tr></tbody></table><p>__add和__tostring:</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">t_table = <span class="built_in">setmetatable</span>(&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>&#125;, &#123;<span class="built_in">__add</span> =</span><br><span class="line"><span class="function"><span class="keyword">function</span><span class="params">(t_table, data)</span></span></span><br><span class="line">t_table[#t_table + <span class="number">1</span>] = data</span><br><span class="line"><span class="keyword">return</span> t_table</span><br><span class="line"><span class="keyword">end</span>,</span><br><span class="line"><span class="built_in">__tostring</span> =</span><br><span class="line"><span class="function"><span class="keyword">function</span><span class="params">(t_table)</span></span></span><br><span class="line"><span class="keyword">local</span> res = <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">for</span> _, v <span class="keyword">in</span> <span class="built_in">pairs</span>(t_table) <span class="keyword">do</span></span><br><span class="line">res = res .. v</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">print</span>(t_table)</span><br><span class="line"><span class="built_in">print</span>(t_table + <span class="number">4</span>)</span><br><span class="line"><span class="comment">--[[</span></span><br><span class="line"><span class="comment">输出：</span></span><br><span class="line"><span class="comment">123</span></span><br><span class="line"><span class="comment">1234</span></span><br><span class="line"><span class="comment">--]]</span></span><br></pre></td></tr></table></figure><p>需要注意的是，关系运算符，比较的两端，元表必须相同。如果只有一方有元表，另一方没有，又或者是两方拥有不同的元表，会导致比较报错：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">a_metatable =</span><br><span class="line">&#123;</span><br><span class="line">__gt = <span class="function"><span class="keyword">function</span><span class="params">(a_table, b_table)</span></span></span><br><span class="line"><span class="keyword">return</span> a_table[<span class="number">1</span>] &gt; b_table[<span class="number">1</span>]</span><br><span class="line"><span class="keyword">end</span>,</span><br><span class="line"><span class="built_in">__lt</span> = <span class="function"><span class="keyword">function</span><span class="params">(a_table, b_table)</span></span></span><br><span class="line"><span class="keyword">return</span> a_table[<span class="number">1</span>] &lt; b_table[<span class="number">1</span>]</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">&#125;</span><br><span class="line">b_metatable =</span><br><span class="line">&#123;</span><br><span class="line">__gt = <span class="function"><span class="keyword">function</span><span class="params">(a_table, b_table)</span></span></span><br><span class="line"><span class="keyword">return</span> b_table[<span class="number">1</span>] &gt; a_table[<span class="number">1</span>]</span><br><span class="line"><span class="keyword">end</span>,</span><br><span class="line"><span class="built_in">__lt</span> = <span class="function"><span class="keyword">function</span><span class="params">(a_table, b_table)</span></span></span><br><span class="line"><span class="keyword">return</span> b_table[<span class="number">1</span>] &lt; a_table[<span class="number">1</span>]</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">&#125;</span><br><span class="line">a_table = <span class="built_in">setmetatable</span>(&#123;<span class="number">0</span>&#125;, a_metatable)</span><br><span class="line">b_table = <span class="built_in">setmetatable</span>(&#123;<span class="number">1</span>&#125;, b_metatable)</span><br><span class="line"><span class="built_in">print</span>(a_table&lt;b_table)</span><br><span class="line"><span class="comment">--[[</span></span><br><span class="line"><span class="comment">报错内容是：</span></span><br><span class="line"><span class="comment">attempt to compare two table values</span></span><br><span class="line"><span class="comment">--]]</span></span><br></pre></td></tr></table></figure><p>重写取反操作</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">a_metatable =</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">__unm</span> = <span class="function"><span class="keyword">function</span><span class="params">(a_table)</span></span></span><br><span class="line"><span class="keyword">local</span> _temp_table = &#123;&#125;</span><br><span class="line"><span class="keyword">for</span> i = #a_table, <span class="number">1</span>, <span class="number">-1</span> <span class="keyword">do</span></span><br><span class="line">_temp_table[#a_table + <span class="number">1</span> - i] = a_table[i]</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="keyword">return</span> _temp_table</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">&#125;</span><br><span class="line">p_metatable =</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">__tostring</span> =</span><br><span class="line"><span class="function"><span class="keyword">function</span><span class="params">(a_table)</span></span></span><br><span class="line"><span class="keyword">local</span> res = <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">for</span> _, v <span class="keyword">in</span> <span class="built_in">pairs</span>(a_table) <span class="keyword">do</span></span><br><span class="line">res = res .. v</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="keyword">return</span> res</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">&#125;</span><br><span class="line">a_table = <span class="built_in">setmetatable</span>(&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>&#125;, a_metatable)</span><br><span class="line">b_table = <span class="built_in">setmetatable</span>(-a_table, p_metatable)</span><br><span class="line"><span class="built_in">print</span>(b_table)</span><br></pre></td></tr></table></figure><p>__call比较有意思，可以让table可以变得和函数一样可以执行。因此可以写成如下的极其过分的形式：</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line">function_content =</span><br><span class="line">&#123;</span><br><span class="line"><span class="built_in">__call</span> = <span class="function"><span class="keyword">function</span><span class="params">(a_table, a_data)</span></span></span><br><span class="line"><span class="built_in">print</span>(a_data)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">func_print = <span class="built_in">setmetatable</span>(&#123;&#125;, function_content)</span><br><span class="line">b = func_print(<span class="number">4</span>)</span><br></pre></td></tr></table></figure><h1 id="协程"><a href="#协程" class="headerlink" title="协程"></a>协程</h1><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sleep</span><span class="params">(n)</span></span></span><br><span class="line">   <span class="keyword">local</span> t0 = <span class="built_in">os</span>.<span class="built_in">clock</span>()</span><br><span class="line">   <span class="keyword">while</span> <span class="built_in">os</span>.<span class="built_in">clock</span>() - t0 &lt;= n <span class="keyword">do</span> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="comment">-- 这里的sleep会占用大量资源 正常逻辑不能使用</span></span><br><span class="line"></span><br><span class="line">test_coroutine = <span class="built_in">coroutine</span>.<span class="built_in">create</span>(</span><br><span class="line"><span class="function"><span class="keyword">function</span><span class="params">()</span></span></span><br><span class="line"><span class="keyword">for</span> i = <span class="number">1</span>, <span class="number">10</span>, <span class="number">1</span> <span class="keyword">do</span></span><br><span class="line"><span class="built_in">print</span>(i)</span><br><span class="line">sleep(<span class="number">0.1</span>)</span><br><span class="line"><span class="built_in">coroutine</span>.<span class="built_in">yield</span>()</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">do_cro</span><span class="params">(co)</span></span></span><br><span class="line"><span class="keyword">while</span>(<span class="built_in">coroutine</span>.<span class="built_in">status</span>(co) ~=<span class="string">&quot;dead&quot;</span>) <span class="keyword">do</span></span><br><span class="line"><span class="built_in">coroutine</span>.<span class="built_in">resume</span>(co)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">do_cro(test_coroutine)</span><br></pre></td></tr></table></figure><h1 id="面向对象"><a href="#面向对象" class="headerlink" title="面向对象"></a>面向对象</h1><p>用table实现了类与继承</p><figure class="highlight lua"><table><tr><td class="code"><pre><span class="line"><span class="comment">-- 类</span></span><br><span class="line"><span class="comment">-- 类管理器 </span></span><br><span class="line">CClassManager = &#123;&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CClassManager.ctor</span><span class="params">(cls, ...)</span></span></span><br><span class="line">  <span class="keyword">local</span> this = &#123;&#125;</span><br><span class="line">  <span class="built_in">setmetatable</span>(this, cls)</span><br><span class="line">  cls.<span class="built_in">__index</span> = cls  <span class="comment">-- 将元表的__index设为自身，访问表的属性不存在时会搜索元表</span></span><br><span class="line">  cls.init(this, ...)</span><br><span class="line">  <span class="keyword">return</span> this</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CClassManager.VInit</span><span class="params">(self, ...)</span></span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CClassManager.init</span><span class="params">(self, ...)</span></span></span><br><span class="line">  <span class="built_in">self</span>:VInit(...)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CClassManager.dtor</span><span class="params">(cls)</span></span></span><br><span class="line">  <span class="comment">-- do sth</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 通过继承实现的类的定义</span></span><br><span class="line">ClassT = CClassManager:ctor()</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ClassT.VInit</span><span class="params">(self, word)</span></span></span><br><span class="line">  <span class="built_in">self</span>.word = word</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ClassT.say</span><span class="params">(self)</span></span></span><br><span class="line">  <span class="built_in">print</span>(<span class="built_in">self</span>.word)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">test = ClassT:ctor(<span class="string">&quot;B&quot;</span>)</span><br><span class="line">test:say()</span><br><span class="line">test:dtor()</span><br><span class="line">test:say()</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">记录Lua语言的进阶用法与新特性，适合有一定Lua基础的游戏开发者参考。</summary>
    
    
    
    <category term="游戏开发" scheme="http://www.z16388.top/categories/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="游戏" scheme="http://www.z16388.top/tags/%E6%B8%B8%E6%88%8F/"/>
    
    <category term="Lua" scheme="http://www.z16388.top/tags/Lua/"/>
    
  </entry>
  
  <entry>
    <title>EFK日志分析系统的搭建</title>
    <link href="http://www.z16388.top/2021/05/06/dev/efk/"/>
    <id>http://www.z16388.top/2021/05/06/dev/efk/</id>
    <published>2021-05-06T17:22:01.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><blockquote><p>EFK 是三个开源软件的缩写，Elasticsearch，FileBeat，Kibana。其中 ELasticsearch 负责日志分析和存储，FileBeat 负责日志收集，Kibana 负责界面展示。它们之间互相配合使用，完美衔接，高效的满足了很多场合的应用，是目前主流的一种日志分析系统解决方案。</p></blockquote><blockquote><p>EFK 和 ELK 只有一个区别， 收集日志的组件由 Logstash 替换成了 FileBeat，因为 Filebeat 相对于 Logstash 来说有2个好处:<br>1.侵入低，无需修改 elasticsearch 和 kibana 的配置；<br>2.性能高，IO 占用率比 logstash 小太多</p></blockquote><blockquote><p>当然 Logstash 相比于 FileBeat 也有一定的优势，比如 Logstash 对于日志的格式化处理能力，FileBeat 只是将日志从日志文件中读取出来，当然如果收集的日志本身是有一定格式的，FileBeat 也可以格式化，但是相对于Logstash 来说，效果差很多。</p></blockquote><h2 id="部署步骤"><a href="#部署步骤" class="headerlink" title="部署步骤"></a>部署步骤</h2><p><strong>1 .  安装Docker</strong></p><p><strong>2 .  下载所需镜像</strong></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">docker pull elasticsearch:7.12.0</span><br><span class="line">docker pull kibana:7.12.0</span><br><span class="line">docker pull store/elastic/filebeat:7.12.0</span><br></pre></td></tr></table></figure><p><strong>3 .  创建自定义网络</strong></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">docker network create elk_net</span><br></pre></td></tr></table></figure><p><strong>4 .  启动Elasticsearch和Kibana</strong></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">docker run -d --name elasticsearch --net elk_net -p 9200:9200 -p 9300:9300 -e &quot;discovery.type=single-node&quot; elasticsearch:7.12.0</span><br><span class="line">docker run -d --name kibana --net elk_net -p 5601:5601 kibana:7.12.0</span><br></pre></td></tr></table></figure><p>如果希望使用中文界面（中文化不完全，不推荐使用），可以进入kibana的容器里，修改排至文件，在现有配置文件中加一行中文相关的配置即可：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">server.name: kibana</span><br><span class="line">server.host: &quot;0&quot;</span><br><span class="line">elasticsearch.hosts: [ &quot;http://elasticsearch:9200&quot; ]</span><br><span class="line">monitoring.ui.container.elasticsearch.enabled: true</span><br><span class="line">// 设置语言为中文↓</span><br><span class="line">i18n.locale: &quot;zh-CN&quot;</span><br></pre></td></tr></table></figure><p>进入docker容器的指令为：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">docker exec -it kibana /bin/bash</span><br></pre></td></tr></table></figure><p><strong>5 .  配置日志解析规则</strong><br>日志解析的规则，是存放在Elasticsearch中的。只要通过curl命令即可实现向es中插入数据。<br>新建json文件：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  &quot;grok&quot;: &#123;</span><br><span class="line">    &quot;field&quot;: &quot;message&quot;,</span><br><span class="line">    &quot;patterns&quot;: [</span><br><span class="line">      &quot;%&#123;TIMESTAMP_ISO8601:Time&#125;\\s\\s*\\[%&#123;DATA:ServerType&#125;\\]*\\[%&#123;DATA:ServerID&#125;\\]*\\[%&#123;LOGLEVEL:Level&#125;\\s+\\]*\\[%&#123;DATA:Func&#125;\\]%&#123;GREEDYDATA:Log&#125;&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;pattern_definitions&quot;: &#123;</span><br><span class="line">      &quot;ALL_CODE&quot;: &quot;(.|\\n)*&quot;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">  &quot;date&quot;: &#123;</span><br><span class="line">    &quot;field&quot;: &quot;Time&quot;,</span><br><span class="line">    &quot;formats&quot;: [</span><br><span class="line">      &quot;ISO8601&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;target_field&quot;: &quot;@timestamp&quot;,</span><br><span class="line">    &quot;timezone&quot;: &quot;Asia/Shanghai&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">  &quot;remove&quot;: &#123;</span><br><span class="line">    &quot;field&quot;: &quot;message&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;,</span><br><span class="line">&#123;</span><br><span class="line">  &quot;remove&quot;: &#123;</span><br><span class="line">    &quot;field&quot;: &quot;Time&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>patterns部分为解析每行日志的正则表达式，pattern_definitions是处理换行标志。<br>测试可以使用kibana自带的grok测试工具，在设置界面会有。<br>下方的date部分，是通过对上方产生的Time字段，存放到默认的时间戳上，用于处理排序。<br>处理完成后，把多余的字段删除，特别是输入的message，有利于缩减入库数据的量。<br>需要注意的是，json文件里的反斜杠一定要进行转义。</p><p>执行命令：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">curl -H &quot;Content-Type: application/json&quot; -XPUT &#x27;http://192.168.3.67:9200/_ingest/pipeline/lmyzpip&#x27; -d@/home/docker/pipline/pip.json</span><br></pre></td></tr></table></figure><p>除此之外，可以登录Kibana界面，通过可视化界面配置解析。</p><ul><li>点击左侧的三条线按钮，进入Stack Management-&gt;Ingest Node Pipelines-&gt;Create a pipeline</li><li>输入名称和描述</li><li>点击下方Add a processor，增加处理方式</li><li>填写相关参数，点击Add保存</li></ul><p>右侧Add documents可以填写测试用例，测试相关配置是否正确（通过直接写入ES的配置也可以在此测试）。</p><p>测试数据的格式为：</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span><span class="attr">&quot;_index&quot;</span><span class="punctuation">:</span><span class="string">&quot;index&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;_id&quot;</span><span class="punctuation">:</span><span class="string">&quot;id&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;_source&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span><span class="attr">&quot;message&quot;</span><span class="punctuation">:</span><span class="string">&quot;2021-04-29T15:42:05.000000  [Game][4][DEBUG   ][CNetManager::_SendServerConfigRequest]Request for server-config, Stream-&#123;Game: 4-0&#125;. ServerConfig&lt;2&gt; DBConfig&lt;0&gt; RedisConfig&lt;0&gt;&quot;</span><span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">]</span></span><br></pre></td></tr></table></figure><p><strong>6 .  安装filebeat</strong><br>新增配置文件filebeat.docker.yml：</p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="comment">#=========================== Filebeat inputs ==============</span></span><br><span class="line"><span class="attr">filebeat.inputs:</span></span><br><span class="line"></span><br><span class="line"><span class="bullet">-</span> <span class="attr">type:</span> <span class="string">log</span></span><br><span class="line"></span><br><span class="line">  <span class="attr">enabled:</span> <span class="literal">true</span></span><br><span class="line">  <span class="comment">##配置你要收集的日志目录，可以配置多个目录</span></span><br><span class="line">  <span class="attr">paths:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">/home/docker/logs/*.log</span></span><br><span class="line"></span><br><span class="line"><span class="comment">## 设置kibana的地址，开始filebeat的可视化  </span></span><br><span class="line"><span class="attr">setup.kibana.host:</span> <span class="string">&quot;http://kibana:5601&quot;</span></span><br><span class="line"><span class="attr">setup.dashboards.enabled:</span> <span class="literal">true</span></span><br><span class="line"><span class="comment">#-------------------------- Elasticsearch output ---------</span></span><br><span class="line"><span class="attr">output.elasticsearch:</span></span><br><span class="line">    <span class="attr">hosts:</span> [<span class="string">&quot;elasticsearch:9200&quot;</span>]</span><br><span class="line">    <span class="attr">pipeline:</span> <span class="string">&quot;lmyzpip&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="attr">setup.template.name:</span> <span class="string">&quot;my-log&quot;</span></span><br><span class="line"><span class="attr">setup.template.pattern:</span> <span class="string">&quot;my-log-*&quot;</span></span><br><span class="line"><span class="attr">json.keys_under_root:</span> <span class="literal">false</span></span><br><span class="line"><span class="attr">json.overwrite_keys:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>其中需要注意的是，如果filebeat和前面的EK两个东西，没有部署在同一台机器上，需要把容器名改成对应的ip地址。</p><p>运行Filebeat：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">docker run -d --net elk_net -v /home/docker/filebeat/filebeat.docker.yml:/usr/share/filebeat/filebeat.yml -v /home/docker/logs/:/home/docker/logs/ --link elasticsearch:elasticsearch --link kibana:kibana  --name filebeat docker.io/store/elastic/filebeat:7.12.0</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">介绍EFK（Elasticsearch + Fluentd + Kibana）日志收集分析系统的搭建流程与基础配置。</summary>
    
    
    
    <category term="编程技术" scheme="http://www.z16388.top/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="运维" scheme="http://www.z16388.top/tags/%E8%BF%90%E7%BB%B4/"/>
    
  </entry>
  
  <entry>
    <title>使用贝塞尔曲线实现道具随机飞动效果</title>
    <link href="http://www.z16388.top/2020/12/12/game_alg/bezier/"/>
    <id>http://www.z16388.top/2020/12/12/game_alg/bezier/</id>
    <published>2020-12-12T21:03:53.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<p>工作中，遇到了一个需求是要实现获得道具和货币的飞动效果：</p><ol><li>根据道具的多少生成不同数目的道具</li><li>货币首先要有一个炸开的效果，然后向一个点汇集；道具从原位置沿直线飞过去</li><li>balabala</li></ol><p>这里只讨论货币飞行路线的问题。</p><p>贝塞尔曲线(Bézier curve)，又称贝兹曲线或贝济埃曲线，是应用于二维图形应用程序的数学曲线。PS中的钢笔工具就是使用贝塞尔曲线来绘制矢量曲线的。</p><p>这里使用简单的，四个点确定的贝塞尔曲线来实现飞行轨迹。首先p0和p3分别是移动的起点和终点。为了模拟爆炸效果，可以通过随机选取以p0为圆心，长度为R的圆上的点，调整曲线的第一次弯折位置实现。这一点定为P1。最后，根据P1点和P0点之间的关系，进行x轴或者y轴的修正，实现曲线平滑延申到p3，这一点是p2点。</p><p>p1点（爆炸点）的获取方式：</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Vector3 <span class="title">GetRandomPosition</span>(<span class="params">Vector3 vec</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">float</span> sita = UnityEngine.Random.Range(-Mathf.PI, Mathf.PI);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Vector3(vec.x + Mathf.Cos(sita) * Screen.width / <span class="number">8</span>, vec.y + Mathf.Sin(sita) * Screen.width / <span class="number">8</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>p2点（修正点）的获取方式：</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Vector3 <span class="title">GetFixedPoint</span>(<span class="params">Vector3 start, Vector3 end</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Vector3(start.x + <span class="number">3</span>*(end.x - start.x)/<span class="number">4</span>, start.y + <span class="number">3</span> * (end.y - start.y) / <span class="number">4</span> + p1.y &gt; start.y ? Screen.height / <span class="number">15</span> : - Screen.height / <span class="number">15</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>根据已知的四个点，实现物体延贝塞尔曲线移动的效果。</p><figure class="highlight cs"><table><tr><td class="code"><pre><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 延贝塞尔曲线移动</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> IEnumerator <span class="title">MoveBezier</span>(<span class="params">GComponent gcom, <span class="built_in">float</span> time, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="built_in">float</span> t = <span class="number">0</span>; t &lt; time; t += Time.deltaTime)</span><br><span class="line">    &#123;</span><br><span class="line">        gcom.position = CalculateCubicBezierPoint(t / time, p0, p1, p2, p3);</span><br><span class="line">        <span class="keyword">yield</span> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    gcom.position = p3;</span><br><span class="line">    gcom.Dispose();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;summary&gt;</span></span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> 计算贝塞尔曲线点的坐标</span></span><br><span class="line"><span class="comment"><span class="doctag">///</span> <span class="doctag">&lt;/summary&gt;</span></span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Vector3 <span class="title">CalculateCubicBezierPoint</span>(<span class="params"><span class="built_in">float</span> t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">float</span> u = <span class="number">1</span> - t;</span><br><span class="line">    <span class="built_in">float</span> tt = t * t;</span><br><span class="line">    <span class="built_in">float</span> uu = u * u;</span><br><span class="line">    <span class="built_in">float</span> uuu = uu * u;</span><br><span class="line">    <span class="built_in">float</span> ttt = tt * t;</span><br><span class="line"></span><br><span class="line">    Vector3 p = uuu * p0;</span><br><span class="line">    p += <span class="number">3</span> * uu * t * p1;</span><br><span class="line">    p += <span class="number">3</span> * u * tt * p2;</span><br><span class="line">    p += ttt * p3;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">介绍如何在Unity中利用贝塞尔曲线实现道具随机飞动的平滑动画效果，附C#代码实现。</summary>
    
    
    
    <category term="游戏开发" scheme="http://www.z16388.top/categories/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Unity" scheme="http://www.z16388.top/tags/Unity/"/>
    
    <category term="游戏" scheme="http://www.z16388.top/tags/%E6%B8%B8%E6%88%8F/"/>
    
  </entry>
  
  <entry>
    <title>震惊，JS不加分号会造成错误！？</title>
    <link href="http://www.z16388.top/2019/12/02/dev/%E9%9C%87%E6%83%8Ajs/"/>
    <id>http://www.z16388.top/2019/12/02/dev/%E9%9C%87%E6%83%8Ajs/</id>
    <published>2019-12-02T00:12:19.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<p>在之前的工作中，我遇到了一个奇怪的问题。明明在语法上没有问题，找同事也看了，但是程序依旧会产生奇怪的错误。最后通过一步一步断点，定为了错误位置，才找到了造成这个错误的原因———在一个不需要分号的语言中，句末不加分号居然报错了。</p><p>不卖关子了，这个错误是多返回值函数造成了对上一句值的影响。下面举个例子：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">func</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> [<span class="number">1</span>, <span class="number">2</span>]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> b = <span class="number">0</span></span><br><span class="line"><span class="keyword">let</span> c = <span class="number">0</span></span><br><span class="line"><span class="keyword">let</span> a = <span class="number">3</span></span><br><span class="line">[b, c] = <span class="title function_">func</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;a is :&quot;</span> + a)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&quot;b is :&quot;</span> + b + <span class="string">&quot; c is :&quot;</span> + c)</span><br></pre></td></tr></table></figure><p>运行的结果将会是这样:</p><blockquote><p>a is :1,2<br>b is :0 c is :0</p></blockquote><p>很容易看到，我们函数的返回值给了上一行的a。这是因为我们的编译器将代码认为了：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = <span class="number">3</span> ,[b, c] = <span class="title function_">func</span>();</span><br></pre></td></tr></table></figure><p>避免这种情况，还是要多加分号吧——虽然它并不会报错。</p>]]></content>
    
    
    <summary type="html">分析JavaScript不加分号可能引发的潜在错误，通过具体案例说明ASI机制的风险与规避方法。</summary>
    
    
    
    <category term="编程技术" scheme="http://www.z16388.top/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="JavaScript" scheme="http://www.z16388.top/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>Linux升级Python</title>
    <link href="http://www.z16388.top/2019/11/28/dev/linuxuppy/"/>
    <id>http://www.z16388.top/2019/11/28/dev/linuxuppy/</id>
    <published>2019-11-28T12:12:19.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<p>11月底，腾讯云搞了一波双11返场活动，我买了三年的服务器。</p><p>和买了新的电脑或者做了新系统一样，得先把生产环境搞好。</p><p>距离Python2.x停止维护大概只有5个月了吧，所以第一要务是升级Python的版本。但是yum是依赖Python2的，所以升级还是会有一些顾虑的。下面是升级的过程：</p><h1 id="下载、解压"><a href="#下载、解压" class="headerlink" title="下载、解压"></a>下载、解压</h1><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">wget https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tgz</span><br><span class="line">tar -zxvf Python-3.8.0.tgz</span><br></pre></td></tr></table></figure><h1 id="安装编译环境"><a href="#安装编译环境" class="headerlink" title="安装编译环境"></a>安装编译环境</h1><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">yum install make build-essential libssl-dev zlib1g-dev libbz2-dev</span><br><span class="line">yum install zlib</span><br></pre></td></tr></table></figure><h1 id="编译、安装"><a href="#编译、安装" class="headerlink" title="编译、安装"></a>编译、安装</h1><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">yum install -y make build-essential libssl-dev zlib1g-dev libbz2-dev</span><br><span class="line">yum -y install zlib</span><br><span class="line"></span><br><span class="line">cd Python-3.8.0</span><br><span class="line">./configure</span><br><span class="line">make &amp;&amp; make install</span><br></pre></td></tr></table></figure><h1 id="备份与配置"><a href="#备份与配置" class="headerlink" title="备份与配置"></a>备份与配置</h1><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">mv /usr/bin/python /usr/bin/python.bak</span><br><span class="line">ln -s /usr/local/bin/python3 /usr/bin/python</span><br></pre></td></tr></table></figure><h1 id="保证yum可用"><a href="#保证yum可用" class="headerlink" title="保证yum可用"></a>保证yum可用</h1><p>将下面文件中的配置修改为Python2.x版本的路径。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/usr/bin/yum</span><br><span class="line">/usr/libexec/urlgrabber-ext-down</span><br></pre></td></tr></table></figure><h1 id="安装pip"><a href="#安装pip" class="headerlink" title="安装pip"></a>安装pip</h1><p>pip需要依赖setuptools，所以要先安装setuptools。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">wget https://files.pythonhosted.org/packages/ce/1d/96320b9784b04943c924a9f1c6fa49124a1542039ce098a5f9a369227bad/setuptools-42.0.1.zip</span><br><span class="line">unzip setuptools-42.0.1.zip</span><br><span class="line">cd setuptools-42.0.1</span><br><span class="line">python setup.py build</span><br><span class="line">python setup.py install</span><br><span class="line"></span><br><span class="line">wget https://files.pythonhosted.org/packages/ce/ea/9b445176a65ae4ba22dce1d93e4b5fe182f953df71a145f557cffaffc1bf/pip-19.3.1.tar.gz</span><br><span class="line">tar -zxvf pip-19.3.1.tar.gz</span><br><span class="line">cd pip-19.3.1</span><br><span class="line">python setup.py build</span><br><span class="line">python setup.py install</span><br></pre></td></tr></table></figure><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p><a href="https://www.cnblogs.com/zhangym/p/6226435.html">https://www.cnblogs.com/zhangym/p/6226435.html</a><br><a href="https://www.cnblogs.com/fjping0606/p/9156344.html">https://www.cnblogs.com/fjping0606/p/9156344.html</a><br><a href="https://www.cnblogs.com/fyly/p/11112169.html">https://www.cnblogs.com/fyly/p/11112169.html</a></p>]]></content>
    
    
    <summary type="html">记录在腾讯云Linux服务器上升级Python版本的操作过程，包含源码编译安装与兼容性处理。</summary>
    
    
    
    <category term="编程技术" scheme="http://www.z16388.top/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="Python" scheme="http://www.z16388.top/tags/Python/"/>
    
    <category term="Linux" scheme="http://www.z16388.top/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Github图床工具</title>
    <link href="http://www.z16388.top/2019/11/17/hexo/picupdate/"/>
    <id>http://www.z16388.top/2019/11/17/hexo/picupdate/</id>
    <published>2019-11-17T18:32:44.000Z</published>
    <updated>2026-05-29T05:52:47.787Z</updated>
    
    <content type="html"><![CDATA[<p>这是一个上传图片到github的工具，目前还不是很成熟，不过已经可以实现压缩并上传图片的目的了，对于写博客来说已经够用了。</p><h1 id="使用步骤"><a href="#使用步骤" class="headerlink" title="使用步骤"></a>使用步骤</h1><ol><li>创建todo目录</li><li>创建pic目录，在github上创建一个空项目，然后pull到这里</li><li>修改配置文件，将git_url修改为上一步新建的项目</li><li>将图片放到todo文件夹下</li><li>执行工具</li></ol><p>需要注意的是，因为图床需要一个git目录，同时代码也需要。在使用部分git管理工具时会禁止这种目录的嵌套。因此最好将代码独立运行。可以自己打包，也可以使用我打好的可执行程序。</p><p>在这里需要提一句，一般如果只想要下载github上项目的一个目录或者一个文件，可以使用svn进行下载。将文件路径中的<code>文件名/master</code>替换为<code>trunk</code>即可使用下载。</p><p>经过配置之后，以后只需要将图片放到todo目录下，执行脚本即可。</p><p>编译时需要安装以下运行库</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pip install pyyaml</span><br><span class="line">pip install gitpython</span><br><span class="line">pip install pillow</span><br></pre></td></tr></table></figure><h1 id="更新文档"><a href="#更新文档" class="headerlink" title="更新文档"></a>更新文档</h1><p>Ver.0.2 2019-11-17 提交编译的可执行程序；增加配置文件</p><p>Ver.0.1 2019-11-12 提交项目</p>]]></content>
    
    
    <summary type="html">介绍一款基于Python开发的GitHub图床上传工具，实现图片一键上传并自动生成Markdown链接。</summary>
    
    
    
    <category term="博客建站" scheme="http://www.z16388.top/categories/%E5%8D%9A%E5%AE%A2%E5%BB%BA%E7%AB%99/"/>
    
    
    <category term="Python" scheme="http://www.z16388.top/tags/Python/"/>
    
    <category term="Github" scheme="http://www.z16388.top/tags/Github/"/>
    
  </entry>
  
  <entry>
    <title>JS使用replace()函数全部替换</title>
    <link href="http://www.z16388.top/2019/11/16/dev/jsreplace/"/>
    <id>http://www.z16388.top/2019/11/16/dev/jsreplace/</id>
    <published>2019-11-16T22:03:56.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<p>在处理爬虫爬取下来的数据时，遇到了在文字中出现了经过转义的换行符，在文中显示出了\n，很影响观赏效果。因此，我对内容做了处理。</p><p>但是在刷库的过程中，我发现，我总不能一次处理完所有的数据。后来发现是JavaScript的Replace函数的问题，这个函数默认只能替换第一个匹配到的项目。如果需要处理全部的，需要使用正则表达式：</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">string.<span class="title function_">replace</span>(<span class="regexp">/\\n/g</span>, <span class="string">&quot;\n&quot;</span>)</span><br></pre></td></tr></table></figure><p>除此之外，下面是一些处理爬取内容常用操作，包含了html的转义：</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">string.<span class="title function_">replace</span>(<span class="regexp">/&amp;nbsp;/g</span>, <span class="string">&#x27; &#x27;</span>)</span><br><span class="line">string.<span class="title function_">replace</span>(<span class="regexp">/&amp;lt;/g</span>, <span class="string">&#x27;&lt;&#x27;</span>)</span><br><span class="line">string.<span class="title function_">replace</span>(<span class="regexp">/&amp;gt;/g</span>, <span class="string">&#x27;&gt;&#x27;</span>)</span><br><span class="line">string.<span class="title function_">replace</span>(<span class="regexp">/&amp;amp;/g</span>, <span class="string">&#x27;&amp;&#x27;</span>)</span><br><span class="line">string.<span class="title function_">replace</span>(<span class="regexp">/&amp;quot;/g</span>, <span class="string">&#x27;&quot;&#x27;</span>)</span><br><span class="line">   string.<span class="title function_">replace</span>(<span class="regexp">/&amp;#x3D;/g</span>, <span class="string">&#x27;=&#x27;</span>)</span><br><span class="line">   string.<span class="title function_">replace</span>(<span class="regexp">/\[.*?\]/g</span>,<span class="string">&#x27;&#x27;</span>)</span><br><span class="line">   string.<span class="title function_">replace</span>(<span class="string">&quot;\\n&quot;</span>,<span class="string">&quot;\n&quot;</span>)</span><br><span class="line">   string.<span class="title function_">replace</span>(<span class="string">&quot;\\t&quot;</span>,<span class="string">&quot;&quot;</span>)</span><br><span class="line">   string.<span class="title function_">replace</span>(<span class="string">&quot;\\r&quot;</span>,<span class="string">&quot;&quot;</span>)</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">介绍JavaScript中replace()函数实现字符串全局替换的用法，对比正则与字符串参数的区别。</summary>
    
    
    
    <category term="编程技术" scheme="http://www.z16388.top/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="JavaScript" scheme="http://www.z16388.top/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>JS使用Splice()函数操作数组</title>
    <link href="http://www.z16388.top/2019/11/16/dev/jssplice/"/>
    <id>http://www.z16388.top/2019/11/16/dev/jssplice/</id>
    <published>2019-11-16T21:59:56.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<p>在js的使用过程中，有一次需要对数组进行各种操作，一时间迫使我想要去使用链表。后来通过查阅资料，总结了下面的一些方法，主要使用了splice()函数。</p><p>下面的方法主要是使用下标进行操作。如果是用值的话，可以通过<code>indexOf()</code>函数来获取下标。若不存在则返回-1。</p><p>值交换</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">swap_arr</span>(<span class="params">a_list, index1, index2</span>) &#123;</span><br><span class="line">    a_list[index1] = a_list.<span class="title function_">splice</span>(index2, <span class="number">1</span>, a_list[index1])[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">return</span> a_list;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>置顶</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">up_arr</span>(<span class="params">a_list, index</span>)&#123;</span><br><span class="line">    <span class="keyword">if</span>(index!=<span class="number">0</span> &amp;&amp; index!=-<span class="number">1</span>)&#123;</span><br><span class="line">        a_list[index] = a_list.<span class="title function_">splice</span>(index-<span class="number">1</span>, <span class="number">1</span>, a_list[index])[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下移</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">down_arr</span>(<span class="params">a_list, index</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span>(index!=a_list.<span class="property">length</span>-<span class="number">1</span> &amp;&amp; index!=-<span class="number">1</span>)&#123;</span><br><span class="line">        a_list[index] = a_list.<span class="title function_">splice</span>(index+<span class="number">1</span>, <span class="number">1</span>, a_list[index])[<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>插入</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">ins_arr</span>(<span class="params">a_list, index, a_data</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span>(index!=-<span class="number">1</span>)&#123;</span><br><span class="line">        <span class="keyword">if</span> (a_list.<span class="title function_">indexOf</span>(a_data)==-<span class="number">1</span>) &#123;</span><br><span class="line">            a_list.<span class="title function_">splice</span>(index+<span class="number">1</span>, <span class="number">0</span>, a_data);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在顶部插入</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">topins_arr</span>(<span class="params">a_list, a_data</span>) &#123;</span><br><span class="line">    a_list.<span class="title function_">splice</span>(<span class="number">0</span>, <span class="number">0</span>, a_data)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>删除元素</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">del_arr</span>(<span class="params">a_list, a_data_list</span>) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> ele <span class="keyword">of</span> a_data_list) &#123;</span><br><span class="line">        <span class="keyword">let</span> index = a_list.<span class="title function_">indexOf</span>(ele);</span><br><span class="line">        <span class="keyword">if</span>(index!=-<span class="number">1</span>)&#123;</span><br><span class="line">            a_list.<span class="title function_">splice</span>(index, <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">介绍JavaScript中splice()函数对数组的插入、删除和替换操作，含常见用法示例。</summary>
    
    
    
    <category term="编程技术" scheme="http://www.z16388.top/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="JavaScript" scheme="http://www.z16388.top/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>当你的程序连接Mysql然后崩溃时</title>
    <link href="http://www.z16388.top/2019/11/16/dev/mysqltimeout/"/>
    <id>http://www.z16388.top/2019/11/16/dev/mysqltimeout/</id>
    <published>2019-11-16T21:43:56.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<p>之前写过一个监控mysql数据库更新状态的预警程序，总是莫名其妙的报一个连接错误的错，然后程序死掉。后来在系统趋于稳定之后，我就没再继续维护这个工具了。</p><p>但是最近我在写另一个工具时，遇到了一个奇怪的问题，就是：tick总在27000多左右的时候崩溃。</p><p>我进行了一系列的猜测，比如tick的代码，或者是逻辑有问题，最后我把思路放在了之前遇到的这个错误上。查阅资料后发现，MySQL数据库在连接之后，如果超过一个设定的时间戳之后，会断开。这个值叫<code>WAIT_TIMEOUT</code>，默认值是28800，也就是说如果连上MySQL数据库之后，8小时内没有进行操作，这个连接便会断开。</p><p>网上很多连接MySQL数据库的代码没有处理过超时连接的问题，就连JS的官方代码好像也是在17年之后才更新的。以往这个问题，大家都是通过修改这个值来进行规避的。比如改成300天。修改有两种方式，一种是修改配置文件，这样在启动时便会使用这个配置；另一种是修改这个值，或者全局，或者当次生效。</p><p>下面是我在使用JavaScript语言链接MySQL数据库时，处理超时重连问题的代码：</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">config</span> = &#123;</span><br><span class="line">    <span class="string">&quot;host&quot;</span>: <span class="string">&quot;x.x.x.x&quot;</span>,</span><br><span class="line"><span class="string">&quot;port&quot;</span>: xxxx,</span><br><span class="line"><span class="string">&quot;user&quot;</span>: <span class="string">&quot;root&quot;</span>,</span><br><span class="line"><span class="string">&quot;password&quot;</span>: <span class="string">&quot;pass&quot;</span>,</span><br><span class="line"><span class="string">&quot;database&quot;</span>: <span class="string">&quot;name&quot;</span><span class="string">&quot;</span></span><br><span class="line"><span class="string">&#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">async connect() &#123;</span></span><br><span class="line"><span class="string">    let self = this;</span></span><br><span class="line"><span class="string">    console.log(&quot;</span>connect mysql success <span class="keyword">with</span><span class="string">&quot;, JSON.stringify(this.config))</span></span><br><span class="line"><span class="string">    // 创建连接</span></span><br><span class="line"><span class="string">    this.db_mysql = mysql.createConnection(</span></span><br><span class="line"><span class="string">        this.config</span></span><br><span class="line"><span class="string">    );</span></span><br><span class="line"><span class="string">    // 连接数据库</span></span><br><span class="line"><span class="string">    await this.db_mysql.connect();</span></span><br><span class="line"><span class="string">    // 错误处理</span></span><br><span class="line"><span class="string">    this.db_mysql.on(&#x27;error&#x27;, function(err) &#123;</span></span><br><span class="line"><span class="string">        if (err) &#123;</span></span><br><span class="line"><span class="string">            if (err.code === &#x27;PROTOCOL_CONNECTION_LOST&#x27;) &#123;</span></span><br><span class="line"><span class="string">                // 处理超时</span></span><br><span class="line"><span class="string">                console.warning(&quot;</span>start reconnect mysql<span class="string">&quot;);</span></span><br><span class="line"><span class="string">                self.connect();</span></span><br><span class="line"><span class="string">            &#125; else &#123;</span></span><br><span class="line"><span class="string">                console.error(err.stack || err);</span></span><br><span class="line"><span class="string">                console.warning(&quot;</span>start reconnect mysql<span class="string">&quot;);</span></span><br><span class="line"><span class="string">                self.connect();</span></span><br><span class="line"><span class="string">            &#125;</span></span><br><span class="line"><span class="string">        &#125;</span></span><br><span class="line"><span class="string">    &#125;);</span></span><br><span class="line"><span class="string">&#125;</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">记录程序连接MySQL后因连接超时断开导致崩溃的排查过程与连接池配置解决方案。</summary>
    
    
    
    <category term="编程技术" scheme="http://www.z16388.top/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="JavaScript" scheme="http://www.z16388.top/tags/JavaScript/"/>
    
    <category term="MySQL" scheme="http://www.z16388.top/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>安卓应用闪屏</title>
    <link href="http://www.z16388.top/2019/11/16/android/AndroidSplash/"/>
    <id>http://www.z16388.top/2019/11/16/android/AndroidSplash/</id>
    <published>2019-11-16T21:19:56.000Z</published>
    <updated>2026-05-29T05:52:47.783Z</updated>
    
    <content type="html"><![CDATA[<p>去年在接入安卓SDK时，会有部分渠道有要求手写闪屏的情况，下面是当时的笔记，这只是最简单的一种方法。</p><h1 id="参考资料："><a href="#参考资料：" class="headerlink" title="参考资料："></a>参考资料：</h1><p>很好的例子：</p><p><a href="https://www.jianshu.com/p/a609f510b19a">https://www.jianshu.com/p/a609f510b19a</a></p><p><a href="https://blog.csdn.net/l799069596/article/details/47094731">https://blog.csdn.net/l799069596/article/details/47094731</a></p><p>安卓动画：<a href="https://blog.csdn.net/IO_Field/article/details/53101499">https://blog.csdn.net/IO_Field/article/details/53101499</a></p><h1 id="背景："><a href="#背景：" class="headerlink" title="背景："></a>背景：</h1><p>除去游戏本身的闪屏之外，有的渠道会要求，有额外的渠道闪屏。为了使用一套资源出不同渠道包，我们可以对接渠道的AS工程进行处理，单独设置闪屏。</p><p>首先，创建一个闪屏Activity，为你的主Activity，这样在游戏的一开始你就可以看到闪屏了。</p><p>这里需要注意的是，你原先的Activit也需要在Manifest中注册打开日志，否则在打包的时候会找不到，报错：</p><p><a href="https://blog.csdn.net/qq_28301007/article/details/52265775">https://blog.csdn.net/qq_28301007/article/details/52265775</a></p><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">activity</span> <span class="attr">android:name</span>=<span class="string">&quot;...Activity&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure><p>下面是主Activity，也就是闪屏Activity的代码，需要根据AS的提示import缺少的部分。</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> android.animation.ObjectAnimator;</span><br><span class="line"><span class="keyword">import</span> android.animation.ValueAnimator;</span><br><span class="line"><span class="keyword">import</span> android.app.Activity;</span><br><span class="line"><span class="keyword">import</span> android.content.Intent;</span><br><span class="line"><span class="keyword">import</span> android.os.Bundle;</span><br><span class="line"><span class="keyword">import</span> android.os.Handler;</span><br><span class="line"><span class="keyword">import</span> android.view.WindowManager;</span><br><span class="line"><span class="keyword">import</span> android.widget.ImageView;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SplashActivity</span> <span class="keyword">extends</span> <span class="title class_">Activity</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">TimeAnimDurning</span> <span class="operator">=</span> <span class="number">2000</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> displayDeviceWidth;</span><br><span class="line">    ImageView iv_splash;</span><br><span class="line">    <span class="keyword">private</span> ObjectAnimator objAnim;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">onCreate</span><span class="params">(Bundle savedInstanceState)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        displayDeviceWidth = getResources().getDisplayMetrics().widthPixels;</span><br><span class="line">        setContentView(R.layout.activity_splash);</span><br><span class="line">        iv_splash = (ImageView) findViewById(R.id.splash);</span><br><span class="line">        objAnim = ObjectAnimator.ofFloat(iv_splash,<span class="string">&quot;alpha&quot;</span>,<span class="number">1</span>,<span class="number">0</span>);</span><br><span class="line">        objAnim.setDuration(TimeAnimDurning);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Handler</span>().postDelayed(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">                objAnim.addUpdateListener(<span class="keyword">new</span> <span class="title class_">ValueAnimator</span>.AnimatorUpdateListener() &#123;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onAnimationUpdate</span><span class="params">(ValueAnimator animation)</span> &#123;</span><br><span class="line">                        <span class="keyword">if</span>((<span class="type">int</span>)animation.getAnimatedFraction() == <span class="number">1</span>)&#123;</span><br><span class="line">                            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);</span><br><span class="line">                            startActivity(<span class="keyword">new</span> <span class="title class_">Intent</span>(SplashActivity.<span class="built_in">this</span> , .YouActivity.class));</span><br><span class="line">                            finish();</span><br><span class="line">                            <span class="keyword">return</span> ;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line">                objAnim.start();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;, <span class="number">2000</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">RelativeLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:tools</span>=<span class="string">&quot;http://schemas.android.com/tools&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:background</span>=<span class="string">&quot;@color/white&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">tools:context</span>=<span class="string">&quot;com.unity3d.player.SplashActivity&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:visibility</span>=<span class="string">&quot;visible&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">ImageView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">&quot;@+id/splash&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;0dp&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_weight</span>=<span class="string">&quot;1&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:scaleType</span>=<span class="string">&quot;fitXY&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:src</span>=<span class="string">&quot;@drawable/splash&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">LinearLayout</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">RelativeLayout</span>&gt;</span></span><br></pre></td></tr></table></figure><p>在上面控制闪屏格式的style.xml中，可以看到闪屏的背景色设置为了白色。这里有一些常用的颜色xml：<a href="https://blog.csdn.net/sundaysunshine/article/details/53509854">https://blog.csdn.net/sundaysunshine/article/details/53509854</a></p><p>除了这两处之外，还需要根据style.xml中的配置，放好闪屏图片，设置闪屏背景。</p><p>整个闪屏的原理就是创建一个动画，在动画播放完成之后，去执行一个新的activity。在补全报错的部分之后，还是有一些细节部分需要注意的。</p><p>首先是结束时间的判定。判定时机总共有两种，一种是获取动画的进度，就像这里的例子，使用<code>(int)animation.getAnimatedFraction()</code>进行获取一个从0~1的数，来表示目前的动画的播放进度。除此之外还可以获取播放的时间，这个函数是：<code>getAnimatedValue()</code>，它可以获取属性的当前值。使用这两个函数可以很方便地控制动画的时间和动作。</p><p>除此之外，在调起另一个Activity之后，我结束了这个Activity。这是因为如果使用默认的LaunchMode，在重新唤醒应用时，闪屏会再次启动，然后走完动画，应用重启。这就造成了应用无法关闭的状况，只能后台强制杀掉。解决办法就是让闪屏只执行一次。</p>]]></content>
    
    
    <summary type="html">介绍Android应用接入渠道SDK时手写启动闪屏页的最简实现方案与参考资料。</summary>
    
    
    
    <category term="移动开发" scheme="http://www.z16388.top/categories/%E7%A7%BB%E5%8A%A8%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Android" scheme="http://www.z16388.top/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>安卓各渠道SDK接入体验</title>
    <link href="http://www.z16388.top/2019/11/12/android/mostsdk/"/>
    <id>http://www.z16388.top/2019/11/12/android/mostsdk/</id>
    <published>2019-11-12T23:00:23.000Z</published>
    <updated>2026-05-29T05:52:47.783Z</updated>
    
    <content type="html"><![CDATA[<p>去年的这个时候，我在忙于接入各种SDK。接渠道SDK，是一件十分薛定谔的事情。你觉得很容易，的确很容易，但是，也很容易遇到问题。然后我就陷入了长期的自闭状态，再加上偷懒，然后博客就断更了一年。现在回头来回忆一下，去年的这个时候，接入SDK时的那些体验。</p><blockquote><p>1.酷派</p></blockquote><p>充值之后，服务器收不到消息，对接也没有人。</p><p>现在看来很明显，已然黄了。</p><blockquote><p>2.应用宝</p></blockquote><p>传说中的5000人大群只有2个技术的大渠道。每天上午问问题会施舍你两句，下午是肯定不会回答问题的，团建能团建半个月。这个渠道我是使用聚合sdk接入的。需要注意的是，接入时有测试阶段，和正式阶段之分，游戏货币名不能修改，同时还必须接入腾讯的信鸽推送SDK，否则无法过审，手动接入成本极高。</p><blockquote><p>3.金立</p></blockquote><p>高版本会造成初次进入闪退，主动获取权限也不行，必须低版本编译。<br>华为手机会出现渲染错误，游戏变成紫红色。<br>需要安装支付插件。<br>现在也应该没有接入的必要了。</p><blockquote><p>4.华为</p></blockquote><p>相当棒的渠道，文档详细，对接起来体验也很不错。充值错误的时候，每一步骤，原因都会有显示。<br>但是不支持第三方工具接入。<br>需要安装支付插件。</p><blockquote><p>5.魅族</p></blockquote><p>商品id配置不明，会出现莫名的变动，很不靠谱。<br>支付回调生效需要1天的时间，需要对商品进行映射，对接很麻烦的渠道。<br>需要安装支付插件。</p><blockquote><p>6.360</p></blockquote><p>包体最大，足足有50多k的方法数，不分包就是死。</p><blockquote><p>7.百度</p></blockquote><p>方法数排名第二，仅次于360。<br>高版本编译会无法使用闪屏。<br>提审体验极差。说好的SDK不强用更，但是等到提审后告诉你不合格。</p><blockquote><p>8.联想</p></blockquote><p>商品id为自动生成，需要做好映射。<br>AnySDK接入需要注意参数顺序。</p><blockquote><p>9.UC</p></blockquote><p>无法使用第三方工具进行接入。<br>闪屏比较蛋疼，在sdk初始化时自动播放。首次运行时无法正常显示，时机不一定，容易和应用闪屏覆盖。</p><blockquote><p>10.OPPO、VIVO</p></blockquote><p>无法使用第三方工具进行接入。<br>需要安装支付插件。</p><blockquote><p>11.小七</p></blockquote><p>文档描述不明确，注意对登陆回调的处理方式，注重切换账号的测试。<br>SDK的Manifest中，最高宽高比设置为2.2。如果游戏中有对这个参数进行修改，需要进行统一。</p><blockquote><p>12.拇指玩</p></blockquote><p>sdk默认背景为透明，会造成有些版本的手机唤起sdk时会显示桌面为背景。或者是切换到主屏后，再回来，只有单独的sdk页面。<br>解决方式是把style.xml中的windowIsTranslucent值置为为false。<br>回调处理方式与其他sdk略有不同,Log与执行功能部分的代码进行了分离。<br>目前版本，无法使用qq登陆等方式，在登陆界面仍没有去掉该入口。</p><blockquote><p>其他:</p></blockquote><p>在运营过程中，小米、UC、应用宝和魅族会不同频率出现无法登录的问题，属正常现象，是他们的SDK服务器抽风了，会报一些很可怕的错误，比如应用不存在，应用id无效之类的。这时候只需要稳定好用户的心态即可，没有任何解决办法。 </p>]]></content>
    
    
    <summary type="html">记录接入华为、酷派等多个Android游戏渠道SDK时的实际体验、踩坑记录与应对方法。</summary>
    
    
    
    <category term="移动开发" scheme="http://www.z16388.top/categories/%E7%A7%BB%E5%8A%A8%E5%BC%80%E5%8F%91/"/>
    
    
    <category term="Android" scheme="http://www.z16388.top/tags/Android/"/>
    
  </entry>
  
  <entry>
    <title>某微信爬虫工具多开方案</title>
    <link href="http://www.z16388.top/2019/11/12/dev/most_wechat/"/>
    <id>http://www.z16388.top/2019/11/12/dev/most_wechat/</id>
    <published>2019-11-12T21:15:53.000Z</published>
    <updated>2026-05-29T05:52:47.786Z</updated>
    
    <content type="html"><![CDATA[<p>之前因为需求找到了这个超级好用的微信爬虫工具<a href="https://github.com/striver-ing/wechat-spider">https://github.com/striver-ing/wechat-spider</a>，目前已经开源。工具可以很方便地实现爬取微信文章，获取点赞、评论等功能。</p><p>最近，微信针对文章历史接口做了调整：PC版限制了爬取的次数，访问间隔应该控制在8分钟以上，移动端则是在两个月前直接干掉了这个功能。文章评论则没有改变。因此，这个工具目前最好的使用方式就是多开，分别爬不同的文章，再单开一个用来爬需要爬的评论。</p><p>工具的具体使用方法在原工程中都有提到，这里就不再赘述了。</p><p>在工具的使用方法中，我们知道，作者是使用全局代理，将所有的https消息都强制走了本机的8080端口，然后通过Python的mitmproxy来截取消息内容来实现的这个工具。那么，我可以使用局部代理，将制定的微信客户端，走制定端口，即可实现工具的多开。</p><p>有了这个思路，那么我们就只需要解决两个问题：</p><ol><li>微信的多开</li><li>多个微信走多个局部代理</li></ol><p>这里先说一下，我们无法使用微信PC客户端自带的代理功能。因为一旦这个功能开启，微信的所有链接都将进行加密，你得到的只会是一个格式如：<code>&lt;code&gt;</code><a href="https://xxx.xxx.xx.xx/mmtls/xxxxxx%60">https://xxx.xxx.xx.xx/mmtls/xxxxxx`</a></code>&#96;的加密链接。</p><h1 id="微信的多开"><a href="#微信的多开" class="headerlink" title="微信的多开"></a>微信的多开</h1><p>微信多开的实现比较简单，直接使用bat脚本打开多个微信即可。需要注意的是，要以管理员模式运行。</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">@<span class="built_in">echo</span> off</span><br><span class="line">start /d <span class="string">&quot;C:\Program Files (x86)\Tencent\WeChat\&quot; WeChat.exe</span></span><br><span class="line"><span class="string">start /d &quot;</span>C:\Program Files (x86)\Tencent\WeChat\&quot; WeChat.exe</span><br><span class="line">start /d <span class="string">&quot;C:\Program Files (x86)\Tencent\WeChat\&quot; WeChat.exe</span></span><br><span class="line"><span class="string">start /d &quot;</span>C:\Program Files (x86)\Tencent\WeChat\&quot; WeChat.exe</span><br><span class="line">start /d <span class="string">&quot;C:\Program Files (x86)\Tencent\WeChat\&quot; WeChat.exe</span></span><br><span class="line"><span class="string">exit</span></span><br></pre></td></tr></table></figure><p>这样虽然可以打开多个微信，但是在设置局部代理时，工具无法区相同路径下的相同可执行文件。因此，我们需要把微信客户端拷贝多份，以应对后面的步骤。</p><figure class="highlight sh"><table><tr><td class="code"><pre><span class="line">@<span class="built_in">echo</span> off</span><br><span class="line">start /d <span class="string">&quot;C:\Program Files (x86)\Tencent\WeChat\&quot; WeChat.exe</span></span><br><span class="line"><span class="string">start /d &quot;</span>C:\Program Files (x86)\Tencent\WeChatb\&quot; WeChatb.exe</span><br><span class="line">start /d <span class="string">&quot;C:\Program Files (x86)\Tencent\WeChatc\&quot; WeChatc.exe</span></span><br><span class="line"><span class="string">start /d &quot;</span>C:\Program Files (x86)\Tencent\WeChatd\&quot; WeChatd.exe</span><br><span class="line">start /d <span class="string">&quot;C:\Program Files (x86)\Tencent\WeChate\&quot; WeChate.exe</span></span><br><span class="line"><span class="string">exit</span></span><br></pre></td></tr></table></figure><p>需要注意的是，这样配置之后的脚本将不一定百分百执行成功，可能只打开一个客户端。一般第二次即可执行成功。</p><h1 id="局部代理"><a href="#局部代理" class="headerlink" title="局部代理"></a>局部代理</h1><p>这里，我使用了Proxifier工具，进行局部代理。这个工具很容易获取，x度上很容易就可以获取到免费破解汉化的版本。</p><p>安装之后，我们开始配置工作。</p><blockquote><p>1.配置文件-&gt;代理服务器  这里地址填本机127.0.0.1，端口填你想要转发的端口，比如8080，8081，协议类型是HTTPS。你想开多少，就填多少个。</p></blockquote><blockquote><p>2.配置文件-&gt;代理规则    这里我们把默认的全局代理给关闭，双击条目，将“是否有效”取消勾选即可。然后添加微信的代理规则，点击添加，名称随意，应用程序浏览到微信的exe文件，目标主机清空，端口清空，然后在最下方的动作中选择你上一步配置的一个端口。那么这个路径下的客户端执行时，便会代理到这个端口下了。</p></blockquote><p>微信多开之后，我们会发现多开的这几个的微信的进程名是一样的，无法进行区分。这时候可以在任务管理器中使用切换到、最小化等功能确定哪个窗口是哪个进程，转到本地文件来确定他是哪个目录下的。在开多个的时候，一定要注意区分，以防登错账号，影响爬取。</p><p>爬虫的配置文件中，我们最好使用不同的mysql数据库，以免产影响。当然，如果你修改了原工具的代码，那就另当别论了。</p>]]></content>
    
    
    <summary type="html">介绍开源微信爬虫工具的多开方案，通过并行实例提升文章与评论数据的采集效率。</summary>
    
    
    
    <category term="编程技术" scheme="http://www.z16388.top/categories/%E7%BC%96%E7%A8%8B%E6%8A%80%E6%9C%AF/"/>
    
    
    <category term="爬虫" scheme="http://www.z16388.top/tags/%E7%88%AC%E8%99%AB/"/>
    
  </entry>
  
</feed>
