<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>TamKungZ_ Blog</title>
  <subtitle>Technical articles, dev logs, release notes, and project notes from TamKungZ_.</subtitle>
  <link rel="alternate" type="text/html" href="https://dev.tamkungz.me/blog" />
  <link rel="self" type="application/atom+xml" href="https://dev.tamkungz.me/blog/atom.xml" />
  <id>https://dev.tamkungz.me/blog</id>
  <updated>2026-11-07T00:00:00.000Z</updated>
  <author><name>TamKungZ_</name><uri>https://dev.tamkungz.me</uri></author>
  <generator>Astro</generator>
  <entry>
    <title>DX12MC: Experimenting with a Direct3D 12 Renderer for Minecraft</title>
    <link rel="alternate" type="text/html" href="https://dev.tamkungz.me/blog/2026-11-03-dx12mc-direct3d12-minecraft-renderer" />
    <link rel="enclosure" type="image/png" href="https://github.com/TamKungZ/DX12MC/raw/main/assets/dxmc-debug-overlay.png" />
    <id>https://dev.tamkungz.me/blog/2026-11-03-dx12mc-direct3d12-minecraft-renderer</id>
    <published>2026-11-03T00:00:00.000Z</published>
    <updated>2026-11-07T00:00:00.000Z</updated>
    <author><name>TamKungZ_</name><uri>https://dev.tamkungz.me</uri></author>
    <category term="Project Story" />
    <category term="DX12MC" />
    <category term="Minecraft" />
    <category term="Direct3D 12" />
    <category term="Fabric" />
    <category term="bgfx" />
    <category term="Java" />
    <summary>The story behind DX12MC, an experimental Fabric mod that renders Minecraft through a Direct3D 12 backend using bgfx, including what works, what does not, and why I built it.</summary>
    <content type="html" xml:lang="en"><![CDATA[<p>DX12MC (or DXMC) is an experimental Fabric mod that renders Minecraft through a Direct3D 12 backend using bgfx, instead of the usual OpenGL path.</p>
<p>This started as a hobby project and a way for me to learn more about graphics rendering. It is not a claim that Direct3D 12 is better than other renderers, and it is not meant to replace anyone else’s work. I had an idea I wanted to try, so I built it.</p>
<h2>How it started</h2>
<p>Seeing people add DLSS to different games got me thinking: could I get Minecraft rendering through Direct3D 12, as directly as Java would allow?</p>
<p>I first experimented with Mesa around version <a href="https://github.com/TamKungZ/DX12MC/blob/main/CHANGELOG.md#030">0.3</a>. That approach did not feel right for this project, and I was not eager to write all the bindings myself. I eventually switched to <a href="https://www.lwjgl.org/">bgfx through LWJGL</a>, which already fits into Minecraft’s Java ecosystem.</p>
<p>There were a few milestones that made the work feel real. <a href="https://github.com/TamKungZ/DX12MC/blob/main/CHANGELOG.md#088---forward-progress-staging--stable-section-residency">Version 0.8.8</a> was the first build that actually opened the game. I was almost in tears when it worked. <a href="https://github.com/TamKungZ/DX12MC/blob/main/CHANGELOG.md#090---low-fps-fix">Version 0.9</a> fixed the very low frame rate, although the mod was still unstable.</p>
<h2>What works today</h2>
<p>The current target is <strong>Minecraft 1.21.1 with Fabric on Windows</strong>. The renderer can currently draw:</p>
<ul>
<li>Terrain, entities, and text</li>
<li>Fog, block outlines, and End portals</li>
<li>Menu blur, glowing outlines, and spectator effects</li>
<li>Screenshots</li>
</ul>
<p>Some existing video settings are also connected to the Direct3D 12 backend, including VSync, mipmap levels, and glint strength. The F3 debug screen shows the GPU, driver, and feature level being used.</p>
<h2>Known limitations</h2>
<p>This is still experimental, so there are important gaps:</p>
<ul>
<li>Fabulous graphics is not supported yet.</li>
<li>Panorama rendering and off-screen screenshots are not supported yet.</li>
<li>Other mods are not guaranteed to work, especially mods that change Minecraft’s rendering pipeline.</li>
<li>If the game needs a rendering path that DX12MC does not implement, it stops with an error rather than silently falling back to OpenGL.</li>
<li>Windows is the intended platform. I have tried Linux through WSL and Zorin OS 17, but Linux support is not something I can promise.</li>
</ul>
<h2>Testing and feedback</h2>
<p>So far, my testing has been limited to a <strong>GT 1030, Ryzen 5 3600, 16 GB of RAM, and Windows 11</strong>. I would especially like to hear from people testing on other GPUs, including RTX cards. If you try it, please share your hardware and results through a <a href="https://github.com/TamKungZ/DX12MC/issues">GitHub issue</a>.</p>
<p>GPT Codex has been my main helper for documentation and debugging. For jars prepared for release on CurseForge or Modrinth, I also use Claude Opus for an additional review. I am still responsible for what I publish. English is not my strongest area, so if you spot a confusing sentence or a documentation mistake, feedback is welcome.</p>
<h2>Where the project is going</h2>
<p>I am focusing on <strong>Fabric 1.21.1</strong> until the project is stable enough, possibly around version 1.0. A Minecraft 1.20.1 build may come later, but it is not ready yet.</p>
<p>I should also set expectations: I tend to ship a project once it works and may not be quick to maintain it afterward. Issues and pull requests are welcome, though replies may be slow. Please use the <code>main</code> branch.</p>
<p>If you want to explore the project or try a build, here are the links:</p>
<ul>
<li><a href="https://github.com/TamKungZ/DX12MC">GitHub repository</a></li>
<li><a href="https://modrinth.com/mod/dx12mc">Modrinth</a></li>
<li><a href="https://www.curseforge.com/minecraft/mc-mods/dx12mc">CurseForge</a></li>
</ul>
<p>This is one idea among many ways to experiment with Minecraft rendering. It is simply the version I wanted to build—and mine uses bgfx.</p>
]]></content>
  </entry>
  <entry>
    <title>NilLoaderSDK Is Becoming NilKit API — A Temporary Pause and a Necessary Change</title>
    <link rel="alternate" type="text/html" href="https://dev.tamkungz.me/blog/2026-08-06-nilloadersdk-is-becoming-nilkit-api-a-temporary-pause-and-a-necessary-change" />
    <link rel="enclosure" type="image/png" href="https://ik.imagekit.io/TamKungZ/rename-nilloadersdk-and-nilloadersdk-is-not-nilloader-official.png" />
    <id>https://dev.tamkungz.me/blog/2026-08-06-nilloadersdk-is-becoming-nilkit-api-a-temporary-pause-and-a-necessary-change</id>
    <published>2026-08-06T00:00:00.000Z</published>
    <updated>2026-11-07T00:00:00.000Z</updated>
    <author><name>TamKungZ_</name><uri>https://dev.tamkungz.me</uri></author>
    <category term="Project Update" />
    <category term="NilKit API" />
    <category term="NilLoader" />
    <category term="Minecraft modding" />
    <category term="open source" />
    <category term="project update" />
    <category term="branding" />
    <summary>NilLoaderSDK is being temporarily taken offline and renamed to NilKit API after concerns that its original name and branding could be mistaken for an official NilLoader project.</summary>
    <content type="html" xml:lang="en"><![CDATA[<p><em>An update about the project name, its relationship with NilLoader, and what happens next.</em></p>
<hr>
<h2>A Change I Need to Make</h2>
<p>I am temporarily taking <strong>NilLoaderSDK</strong> offline while I make a number of changes to the project.</p>
<p>When it returns, it will no longer be called NilLoaderSDK. The project will be renamed to <strong>NilKit API</strong>.</p>
<p>This change follows a discussion with the maintainer of <a href="https://git.sleeping.town/Nil/NilLoader">NilLoader</a> about how the project was being presented and how easily the name and branding could cause confusion.</p>
<p>I want to be clear about something from the beginning: <strong>I did not intend to impersonate NilLoader, claim ownership of it, or present my project as an official component of NilLoader.</strong></p>
<p>But intent does not erase the fact that the name I chose could reasonably give people that impression.</p>
<p>That part is on me.</p>
<hr>
<h2>I Got the Naming Wrong</h2>
<p>When I originally chose the name <strong>NilLoaderSDK</strong>, my reasoning was simple: it was an SDK I had built for development around NilLoader.</p>
<p>From my perspective, the name described what the project did.</p>
<p>What I did not consider carefully enough was how the name would look from the outside.</p>
<p>Names such as “Fabric API” and “Fabric Loader” have trained Minecraft modders to associate similarly named projects with an official ecosystem. Because of that, <strong>NilLoaderSDK</strong> could easily be interpreted as the official SDK or API for NilLoader, or even as something required for NilLoader mods to work.</p>
<p>That was never what the project was meant to be.</p>
<p>I had already tried to explain in the documentation that the project was independent and that NilLoader remained a separate upstream project, but documentation does not completely solve a branding problem. Many people will see a project name, logo, or Modrinth page long before they read a README.</p>
<p>So I accept that I made a mistake in how I named and presented the project.</p>
<p>It was not intentional, but it was still my responsibility.</p>
<hr>
<h2>The New Name: NilKit API</h2>
<p>After discussing the issue, I proposed the name <strong>NilKit API</strong>, and the NilLoader maintainer confirmed that this name was fine.</p>
<p>The new name gives the project its own identity while still making sense for what it is: a collection of APIs, utilities, and development tooling intended for projects that use NilLoader.</p>
<p>Most importantly, it removes the implication that this is an official part of NilLoader itself.</p>
<p>Going forward, the distinction will be much clearer:</p>
<ul>
<li><strong>NilLoader</strong> is the original mod loader and Java agent project maintained by its own developers.</li>
<li><strong>NilKit API</strong> is my independent community project that builds optional tooling around it.</li>
<li>NilKit API is <strong>not official, not required by NilLoader, and not a replacement for NilLoader</strong>.</li>
</ul>
<p>The original NilLoader project can be found here:</p>
<p><strong><a href="https://git.sleeping.town/Nil/NilLoader">git.sleeping.town/Nil/NilLoader</a></strong></p>
<hr>
<h2>Why the Project Is Going Private Temporarily</h2>
<p>Rather than changing only the repository title and leaving a mixture of old and new branding around the internet, I want to do the migration properly.</p>
<p>For that reason, I am temporarily making the project unavailable while I review and update everything associated with the old identity.</p>
<p>This includes, where applicable:</p>
<ul>
<li>The project name</li>
<li>Repository names and descriptions</li>
<li>Package and artifact naming</li>
<li>Mod IDs and metadata</li>
<li>Documentation</li>
<li>GitHub organization branding</li>
<li>Modrinth information</li>
<li>Logos and visual identity</li>
<li>References that could imply an official relationship with NilLoader</li>
<li>Internal implementation choices that deserve another review</li>
</ul>
<p>I would rather take the project offline for a short period and bring it back consistently than leave half-renamed pieces scattered across different services.</p>
<p>This is a pause for cleanup, <strong>not the end of the project</strong>.</p>
<hr>
<h2>The Technical Side Also Deserves Another Look</h2>
<p>The discussion was not only about the name.</p>
<p>Some parts of the project relied heavily on NilLoader internals through reflection and attempted to provide additional bootstrapping behavior around NilLoader’s normal machinery.</p>
<p>Even if those techniques can work today, internal implementation details are not a stable public API. They may change at any time, and relying on them too heavily creates unnecessary compatibility risks for both the project and its users.</p>
<p>I am willing to maintain compatibility when things change, but that does not mean every workaround is a good architectural decision.</p>
<p>So while the project is private, I will also use the opportunity to reconsider those parts instead of treating this as a cosmetic rename only.</p>
<p>The goal remains the same: <strong>make developing with NilLoader more convenient without trying to become NilLoader itself.</strong></p>
<hr>
<h2>Why I Built This in the First Place</h2>
<p>NilKit API exists because I personally encountered problems and repetitive work while developing with NilLoader.</p>
<p>My reaction was the same one that has started countless open-source projects: build the tool I wished I already had.</p>
<p>I still believe there is value in community tooling around a small, flexible project like NilLoader. Things such as development APIs, Gradle tooling, mappings support, installers, documentation, and utilities can make an ecosystem easier to approach without replacing the foundation underneath it.</p>
<p>I also still believe that improving and extending existing open-source software is often more useful than creating another competing implementation simply because something is difficult.</p>
<p>But building around someone else’s project also means being careful about where that project ends and mine begins.</p>
<p>That distinction should have been clearer from day one.</p>
<hr>
<h2>About AI</h2>
<p>I also want to address this directly because it came up during the discussion.</p>
<p>Yes, I use AI tools as part of my development workflow.</p>
<p>I use them to help research ideas, review code, draft documentation, explore implementations, and speed up repetitive work. I do not consider that something that needs to be hidden.</p>
<p>At the same time, <strong>I am responsible for what I publish</strong>.</p>
<p>If an AI-generated suggestion is incorrect, if I choose a bad architecture, if documentation is misleading, or if a project decision causes problems, that responsibility belongs to me. An AI tool does not maintain the repository, make the final decision, or take responsibility for a release. I do.</p>
<p>That applies here as well.</p>
<hr>
<h2>What Happens Next</h2>
<p>For now, the GitHub and Modrinth presence associated with the project will be temporarily restricted while I complete the transition.</p>
<p>When the project returns, it will return as <strong>NilKit API</strong>, with a new identity and clearer separation from NilLoader.</p>
<p>I will also review related tooling individually rather than assuming that the same naming or branding decisions are appropriate for every project.</p>
<p>There is no exact reopening date yet. I want to finish the cleanup properly before making everything public again.</p>
<hr>
<h2>A Final Note</h2>
<p>I want to apologize to the NilLoader maintainer and to anyone who was confused by the original name or presentation.</p>
<p>There was no intention to take over the project, pass my work off as official, or diminish the work of the people who created NilLoader.</p>
<p>I should have contacted the maintainer earlier instead of making assumptions about how close my project’s identity could be to the upstream project.</p>
<p>I appreciate that the concerns were raised directly, and I would rather correct the problem now than allow the confusion to continue.</p>
<p><strong>NilLoaderSDK is going away as a name. NilKit API will take its place.</strong></p>
<p>The idea behind the project is continuing — just with a clearer identity, a cleaner boundary, and more respect for the project it was built around.</p>
<hr>
<p><em>Built and maintained by TamKungZ_.</em></p>
]]></content>
  </entry>
  <entry>
    <title>TrueMoney สแกนหน้ายืนยันตัวตนไม่ได้บน Huawei ที่ใช้ microG: วิธีที่ผมลองแล้วหาย</title>
    <link rel="alternate" type="text/html" href="https://dev.tamkungz.me/blog/2026-07-16-truemoney-huawei-microg-safetynet-face-verification" />
    <link rel="enclosure" type="image/webp" href="https://ik.imagekit.io/TamKungZ/gsm-truemonney-thumbnail.webp" />
    <id>https://dev.tamkungz.me/blog/2026-07-16-truemoney-huawei-microg-safetynet-face-verification</id>
    <published>2026-07-16T00:00:00.000Z</published>
    <updated>2026-11-07T00:00:00.000Z</updated>
    <author><name>TamKungZ_</name><uri>https://dev.tamkungz.me</uri></author>
    <category term="Troubleshooting" />
    <category term="TrueMoney" />
    <category term="Huawei" />
    <category term="microG" />
    <category term="SafetyNet" />
    <category term="Device Attestation" />
    <category term="GMS" />
    <summary>บันทึกวิธีแก้จากประสบการณ์ตรง เมื่อ TrueMoney Wallet สแกนหน้ายืนยันตัวตนไม่ได้บน Huawei ที่ใช้ microG โดยลองปิด Allow requests ของ TrueMoney ใน Device Attestation.</summary>
    <content type="html" xml:lang="th"><![CDATA[<p>บทความนี้เป็นประสบการณ์ตรงของผมเอง อาจจะไม่ได้เกิดกับทุกคน และสาเหตุจริงอาจไม่ได้เหมือนกันทุกเครื่อง</p>
<p>แต่ผมเจอเคสหนึ่งที่หาวิธีแก้ในเน็ตค่อนข้างยาก เลยอยากจดไว้ เผื่อมีประโยชน์กับใครบางคนที่เจอปัญหาคล้ายกัน</p>
<p>ปัญหาคือ <strong>TrueMoney Wallet สแกนหน้าเพื่อยืนยันตัวตนไม่ได้</strong> บนมือถือ Huawei</p>
<p>ในกรณีของผมคือ <strong>Huawei Y73</strong> ซึ่งเป็นเครื่องที่ต้องใช้ GMS ผ่าน microG ในการใช้งานบางอย่างของ Google services</p>
<blockquote>
<p>หมายเหตุ: หลายคนอาจพิมพ์หรือเรียกสลับกันระหว่าง GSM/GMS แต่ในบริบทนี้ผมหมายถึงฝั่ง Google Mobile Services และ microG</p>
</blockquote>
<h2>อาการที่เจอ</h2>
<p>ตอนใช้งาน TrueMoney Wallet แล้วต้องสแกนหน้าเพื่อยืนยันตัวตน แอปไม่สามารถผ่านขั้นตอนนี้ได้</p>
<p>ข้อความที่ผมเจอประมาณนี้:</p>
<blockquote>
<p>ไม่สามารถทำรายการได้<br>
กรุณาทำรายการใหม่ภายหลัง</p>
</blockquote>
<p>ผมลองหาข้อมูลอยู่พักหนึ่ง แต่ไม่ค่อยเจอวิธีแก้ที่ตรงกับเคส Huawei + microG เท่าไร โดยเฉพาะเคสที่เกี่ยวกับการยืนยันตัวตนในแอปการเงิน</p>
<p>สุดท้ายผมสงสัยว่าอาจเกี่ยวกับ <strong>SafetyNet / Device Attestation</strong> เพราะแอปบางตัวจะตรวจสถานะอุปกรณ์ก่อนให้ใช้งานฟีเจอร์บางอย่าง</p>
<h2>วิธีที่ผมลองแล้วใช้ได้</h2>
<p>ขั้นตอนนี้เป็นสิ่งที่ผมทำบนเครื่องของผมเอง เมนูอาจต่างกันตามรุ่น Huawei, เวอร์ชันระบบ, launcher, หรือเวอร์ชันของ microG</p>
<ol>
<li>เปิด <strong>Settings</strong></li>
<li>เข้า <strong>Apps</strong></li>
<li>ค้นหา <strong>microG Services</strong></li>
<li>กดเข้าไปที่แอปนั้น</li>
<li>มองหาปุ่มตั้งค่ารูป <strong>Gear</strong></li>
<li>กดเข้าไป จะเข้าสู่หน้า <strong>microG Settings</strong></li>
<li>หาเมนู <strong>Device Attestation</strong></li>
<li>เข้าไปที่รายการ <strong>Apps using SafetyNet</strong></li>
<li>หา <strong>TrueMoney</strong></li>
<li>กดเข้าไป</li>
<li>ถ้าเห็นตัวเลือก <strong>Allow requests</strong> และมันเปิดอยู่ ให้ลองปิดมัน</li>
<li>จากนั้นลองกลับไปสแกนหน้าใน TrueMoney อีกครั้ง</li>
</ol>
<p>ในเคสของผม หลังจากปิด <strong>Allow requests</strong> ของ TrueMoney ใน Device Attestation แล้ว การสแกนหน้าเพื่อยืนยันตัวตนกลับมาใช้งานได้</p>
<h2>ทำไมวิธีนี้อาจช่วยได้</h2>
<p>ผมยังไม่อยากสรุปแบบฟันธงว่า TrueMoney มีปัญหากับ SafetyNet เสมอ หรือ microG เป็นสาเหตุหลักเสมอ</p>
<p>แต่จากที่เจอกับเครื่องของผม ดูเหมือนว่า TrueMoney อาจติดขั้นตอนที่เกี่ยวกับการเรียก SafetyNet หรือ Device Attestation ผ่าน microG แล้วทำให้ flow การยืนยันตัวตนมีปัญหา</p>
<p>พอปิดไม่ให้ TrueMoney ส่ง request ตรงนี้ผ่าน microG การยืนยันตัวตนเลยผ่านได้</p>
<p>อย่างไรก็ตาม นี่อาจเป็นแค่ workaround เฉพาะบางเครื่อง บางเวอร์ชัน หรือบางสภาพแวดล้อมเท่านั้น</p>
<h2>ข้อควรระวัง</h2>
<p>ถ้าคุณใช้แอปการเงินหรือแอปที่เกี่ยวข้องกับความปลอดภัย ควรระวังเวลาแก้ค่าเกี่ยวกับ SafetyNet, Device Attestation หรือ microG</p>
<p>วิธีนี้ไม่ได้แปลว่าควรปิดทุกอย่างในทุกแอป และไม่ได้รับประกันว่าจะใช้ได้กับทุกคน</p>
<p>ถ้าลองแล้วไม่ดีขึ้น ให้เปิดค่ากลับเหมือนเดิม แล้วลองตรวจอย่างอื่นต่อ เช่น เวอร์ชัน TrueMoney, เวอร์ชัน microG, permission ของกล้อง, network, หรือสถานะบริการของแอป</p>
<h2>สรุป</h2>
<p>ถ้าคุณใช้ Huawei ที่พึ่งพา microG/GMS และเจอปัญหา <strong>TrueMoney Wallet สแกนหน้ายืนยันตัวตนไม่ได้</strong> วิธีนี้อาจเป็นอีกหนึ่งทางที่น่าลอง:</p>
<p><strong>microG Settings &gt; Device Attestation &gt; Apps using SafetyNet &gt; TrueMoney &gt; ปิด Allow requests</strong></p>
<p>นี่เป็นแค่ประสบการณ์จากเครื่องของผม แต่ถ้ามันช่วยประหยัดเวลาคนอื่นที่กำลังหาวิธีแก้ได้บ้าง ก็น่าจะคุ้มที่จะเผยแพร่ไว้</p>
]]></content>
  </entry>
  <entry>
    <title>Why I Built NilLoaderSDK — A Classic &quot;Scratch Your Own Itch&quot; Story</title>
    <link rel="alternate" type="text/html" href="https://dev.tamkungz.me/blog/2026-04-08-why-i-built-nilloadersdk-a-classic-scratch-your-own-itch-story" />
    <link rel="enclosure" type="image/png" href="https://dev.tamkungz.me/assets-image/nilloadersdk-logo.png" />
    <id>https://dev.tamkungz.me/blog/2026-04-08-why-i-built-nilloadersdk-a-classic-scratch-your-own-itch-story</id>
    <published>2026-04-08T00:00:00.000Z</published>
    <updated>2026-11-07T00:00:00.000Z</updated>
    <author><name>TamKungZ_</name><uri>https://dev.tamkungz.me</uri></author>
    <category term="Project Story" />
    <category term="NilLoaderSDK" />
    <category term="NilLoader" />
    <category term="Minecraft modding" />
    <category term="Java" />
    <category term="reflection" />
    <category term="mod loader SDK" />
    <summary>A project story about why TamKungZ_ built NilLoaderSDK to make NilLoader-based Minecraft mod development less repetitive while keeping mapping distribution legally clean.</summary>
    <content type="html" xml:lang="en"><![CDATA[<h2>Why I Built NilLoaderSDK — A Classic “Scratch Your Own Itch” Story</h2>
<p><em>A utility SDK for NilLoader-based Minecraft mods, born out of frustration and necessity.</em></p>
<hr>
<h2>The Problem With NilLoader (And Why That’s Also Its Superpower)</h2>
<p>If you’ve ever worked with <a href="https://git.sleeping.town/Nil/NilLoader">NilLoader</a>, you already know what makes it special: it’s version-agnostic. It doesn’t tie itself to any specific version of Minecraft. As a Java agent, it simply injects your code into the game’s runtime — no strings attached.</p>
<p>That flexibility is genuinely powerful. But it comes with a cost.</p>
<p>Because NilLoader doesn’t bind to any particular Minecraft version, there’s no abstraction layer waiting for you. No event system. No registry helpers. Nothing pre-built the way Forge or Fabric hand things to you. You’re writing directly against Minecraft’s internals — and that means you’d better have a solid grasp of Java fundamentals, bytecode, and reflection before you even get started.</p>
<p>For seasoned modders, that’s fine. For anyone newer to the space, it’s a steep cliff.</p>
<p>That’s exactly why I built <strong>NilLoaderSDK</strong>.</p>
<hr>
<h2>What NilLoaderSDK Actually Is</h2>
<p>NilLoaderSDK isn’t trying to be a new mod loader. It’s not competing with Forge or Fabric. It’s a small utility layer that sits on top of NilLoader and smooths out the rough edges — most of which I ran into myself while working on NilLoader-based projects.</p>
<p>Here’s what it bundles:</p>
<p><strong>Reflection Helpers</strong> — Working with private fields and methods in Minecraft’s obfuscated codebase is tedious. <code>ReflectHelper</code> wraps the boilerplate so you’re not writing the same try-catch reflection blocks over and over. <code>ProxyHelper</code> extends this for dynamic proxy use cases.</p>
<p><strong>Structured Logging</strong> — A consistent logging standard built around NilLoader’s <code>NilLogger</code>. There’s a root SDK logger (<code>NilLoaderSDK</code>) and a class-scoped variant (<code>NilLoaderSDK/&lt;ClassName&gt;</code>). Small thing, but it keeps logs readable across a whole project.</p>
<p><strong>Entrypoint Dispatcher</strong> — NilLoader has <code>premain</code> and <code>hijack</code> phases, and coordinating those across a larger project gets messy fast. <code>EntrypointDispatcher</code> resolves entrypoints in a defined order — JVM properties first, then a properties file, then ServiceLoader — with built-in recursion guards so you don’t accidentally send the loader into an infinite loop.</p>
<p><strong>Java NIO Networking</strong> — A full non-blocking networking layer: <code>NioServer</code>, <code>NioClient</code>, <code>PacketCodec</code>, <code>PacketRegistry</code>, and a <code>MinecraftAutoNetworkBridge</code> that can be wired up purely through JVM properties. Useful for mods that need to talk to external services or other game instances.</p>
<p><strong>Utilities</strong> — <code>CooldownTracker</code> for rate-limiting logic and <code>TargetFinder</code> for locating entities or objects in the world.</p>
<p>Most of these features exist because I needed them. That’s the honest origin story.</p>
<hr>
<h2>The Elephant in the Room: Mappings</h2>
<p>If you’re modding old Minecraft — pre-1.7 especially — one of the biggest headaches is <strong>mappings</strong>. Modern toolchains make this almost invisible: configure Gradle, pick a mapping set, done. Back then, it was a whole ordeal.</p>
<p>The historical go-to was <strong>Mod Coder Pack (MCP)</strong>, which handled remapping Notch’s obfuscated names to human-readable Searge mappings all the way back to Minecraft Alpha 1.1.2_01. But MCP is largely inaccessible today. The tooling has fragmented, mirrors are gone, and finding the original sources is genuinely difficult.</p>
<p>NilLoaderSDK includes a <code>SimpleRemap</code> utility — but I want to be clear about what it <em>doesn’t</em> do. It doesn’t ship bundled mappings, and it won’t. The reason is simple: <strong>licensing</strong>. I couldn’t reliably trace the license terms for existing mapping files, even when digging through documentation and original sources. Without clear licensing, I can’t ship them — and I’m not willing to guess.</p>
<p>So the approach here is intentional: <strong>developers bring their own mappings locally</strong>. You wire them into your project yourself. It’s one more manual step, but it keeps everything legally clean and gives you full control over what you’re working with.</p>
<hr>
<h2>Who Is This For?</h2>
<p>Realistically, NilLoaderSDK isn’t going to change the Minecraft modding ecosystem. It’s a niche tool for a niche loader used by a small community of modders who prefer working close to the metal.</p>
<p>But if you’re in that space — if you’re building NilLoader mods and finding yourself rewriting the same reflection utilities or struggling to wire up consistent logging — this SDK might save you some time.</p>
<p>It’s a small thing. But small things add up.</p>
<hr>
<h2>Getting Started</h2>
<p>Add the SDK to your project via Maven:</p>
<pre><code class="hljs language-kotlin">repositories {
    maven { url = uri(<span class="hljs-string">&quot;https://repo.tamkungz.me&quot;</span>) }
}

dependencies {
    implementation(<span class="hljs-string">&quot;me.tamkungz.nilloadersdk:nilloadersdk:2.0.1&quot;</span>)
}
</code></pre>
<p>The source is available on GitHub, licensed under <strong>LGPL-3.0-or-later</strong>.</p>
<ul>
<li><strong>GitHub</strong>: <a href="https://github.com/TamKungZ/NilLoaderSDK">TamKungZ/NilLoaderSDK</a></li>
<li><strong>Maven Repository</strong>: <a href="https://repo.tamkungz.me">repo.tamkungz.me</a></li>
<li><strong>NilLoader upstream</strong>: <a href="https://git.sleeping.town/Nil/NilLoader">git.sleeping.town/Nil/NilLoader</a></li>
</ul>
<hr>
<p><em>Built by TamKungZ_ — mostly because I needed it.</em></p>
]]></content>
  </entry>
  <entry>
    <title>VLiva Devlog #1 — Why I Started Building a Linux VTuber App</title>
    <link rel="alternate" type="text/html" href="https://dev.tamkungz.me/blog/2026-03-26-vliva-devlog-1-why-i-started-building-a-linux-vtuber-app" />
    <link rel="enclosure" type="image/png" href="https://dev.tamkungz.me/assets-image/vliva-screenshot.png" />
    <id>https://dev.tamkungz.me/blog/2026-03-26-vliva-devlog-1-why-i-started-building-a-linux-vtuber-app</id>
    <published>2026-03-26T00:00:00.000Z</published>
    <updated>2026-11-07T00:00:00.000Z</updated>
    <author><name>TamKungZ_</name><uri>https://dev.tamkungz.me</uri></author>
    <category term="Devlog" />
    <category term="VLiva" />
    <category term="Linux" />
    <category term="VTuber app" />
    <category term="Live2D" />
    <category term="C++" />
    <category term="OpenGL" />
    <summary>The first VLiva devlog explaining why TamKungZ_ started building a native Linux VTuber app instead of relying on Windows tools or compatibility layers.</summary>
    <content type="html" xml:lang="en"><![CDATA[<p>I didn’t originally plan to build this.</p>
<p>VLiva started as a small side experiment — something I made mostly out of frustration.
I work with Live2D occasionally, and my main OS is Linux. Switching between Linux and Windows just to run VTuber software quickly became annoying.</p>
<p>So I looked for alternatives.</p>
<p>There <em>are</em> ways to run existing tools on Linux, but most of them involve:</p>
<ul>
<li>compatibility layers</li>
<li>workarounds</li>
<li>unstable setups</li>
<li>or noticeable performance loss</li>
</ul>
<p>None of them felt clean.</p>
<p>At some point I stopped searching and thought:
“What if I just build something myself?”</p>
<h2>The Early Stage</h2>
<p>At the beginning, I didn’t take this project seriously at all.
It was more like: “let’s see if this is even possible.”</p>
<p>The main challenge was obvious:</p>
<ul>
<li>C++</li>
<li>OpenGL</li>
<li>real-time rendering</li>
<li>Live2D runtime behavior</li>
</ul>
<p>This wasn’t something I could just throw together in a weekend.</p>
<p>Progress was slow.
I spent around 5 months on the project — although realistically, I paused for more than half of that time.</p>
<h2>Coming Back</h2>
<p>Recently, I came back to the project with a different mindset.</p>
<p>Instead of experimenting, I wanted to actually make it work.</p>
<p>Surprisingly, things started to click:</p>
<ul>
<li>rendering pipeline became stable</li>
<li>model loading worked</li>
<li>basic interaction started functioning</li>
</ul>
<p>And at some point, I realized:</p>
<blockquote>
<p>“This is actually usable.”</p>
</blockquote>
<p>Not perfect. Not polished.
But real.</p>
<h2>What VLiva Is (for now)</h2>
<p>VLiva is a native Linux VTuber app prototype that focuses on simplicity:</p>
<ul>
<li>No Wine</li>
<li>No virtual machines</li>
<li>No external dependencies (as much as possible)</li>
<li>Just install and run</li>
</ul>
<p>That’s the core idea.</p>
<h2>What’s Next</h2>
<p>Right now, the project is still far from finished.</p>
<p>Things that still need work:</p>
<ul>
<li>UI/UX</li>
<li>performance optimization</li>
<li>input / tracking improvements</li>
<li>overall stability</li>
</ul>
<p>I’m planning to release a <strong>demo version around April</strong> to gather feedback and bug reports.</p>
<p>👉 <a href="https://vliva.tamkungz.me/">https://vliva.tamkungz.me/</a></p>
<p>If the project reaches a stable and complete state, I may release it on Steam in the future.
Pricing is undecided, but the demo will remain free.</p>
<h2>Why I’m Sharing This</h2>
<p>Originally, this was just a personal tool.</p>
<p>But now I’m curious:</p>
<p>Is there actually a demand for VTubing on Linux?
Are there other people dealing with the same problems?</p>
<p>If yes, then this project might be worth pushing further.</p>
<p>If not… well, at least I built something interesting.</p>
<p>Either way, I’ll keep going for now.</p>
<hr>
<h2>FAQ (from Reddit feedback)</h2>
<h3>Is this similar to Inochi2D?</h3>
<p>Some people suggested Inochi2D, and yes — I’ve looked into it before. Their source code is clean and their optimization approach is great.</p>
<p>But my main target for VLiva is <strong>Live2D support</strong>, so Inochi2D can only help as a partial reference, not a direct solution.</p>
<h3>Does VTube Studio already work on Linux?</h3>
<p>For some users, yes. One Linux user reported that VTube Studio + VBridger was basically plug-and-play.</p>
<p>That said, Linux experiences can still vary by distro and setup (Proton, drivers, etc.).</p>
<h3>Then why build VLiva?</h3>
<p>The goal is not to replace VTube Studio.</p>
<p>VLiva is being built as a <strong>fully native Linux alternative</strong> — no compatibility layer in between, and a simpler path for people who prefer native workflows.</p>
<h3>Will VLiva support VBridger compatibility?</h3>
<p>I’m currently exploring that idea. It’s not confirmed yet, but compatibility with existing VBridger workflows could make adoption much easier for Linux VTubers.</p>
]]></content>
  </entry>
  <entry>
    <title>How a Minecraft mod led me to build my own Java media library from scratch</title>
    <link rel="alternate" type="text/html" href="https://dev.tamkungz.me/blog/2026-03-14-how-a-minecraft-mod-led-me-to-build-my-own-java-media-library-from-scratch" />
    <link rel="enclosure" type="image/webp" href="https://dev.tamkungz.me/assets-image/blog/banner.webp" />
    <id>https://dev.tamkungz.me/blog/2026-03-14-how-a-minecraft-mod-led-me-to-build-my-own-java-media-library-from-scratch</id>
    <published>2026-03-14T00:00:00.000Z</published>
    <updated>2026-11-07T00:00:00.000Z</updated>
    <author><name>TamKungZ_</name><uri>https://dev.tamkungz.me</uri></author>
    <category term="Project Story" />
    <category term="CodecMedia" />
    <category term="Java" />
    <category term="media parsing" />
    <category term="Minecraft modding" />
    <category term="OpenAL" />
    <summary>A project story about how debugging MP3 playback in a Minecraft mod led TamKungZ_ to build CodecMedia, a Java media probing and validation library.</summary>
    <content type="html" xml:lang="en"><![CDATA[<p>I wasn’t trying to build another media converter.</p>
<p>What I actually wanted was something that could answer a much simpler question:</p>
<p><strong>“Is this file really what it claims to be?”</strong></p>
<p>That sounds obvious until you start looking at media files beyond their extensions.</p>
<p>A file named <code>song.mp3</code> isn’t necessarily a valid MP3. Some players will happily play malformed files. Others won’t. Some libraries reject them entirely. Extensions don’t tell the whole story.</p>
<p>That realization eventually became <strong>CodecMedia</strong>.</p>
<hr>
<p>It started while I was building a Minecraft mod.</p>
<p>I wanted to play MP3 files directly through OpenAL instead of Minecraft’s built-in audio system. To do that, I needed a low-level MP3 decoder, so I used <strong>Zoom JLayer</strong>, a pure Java decoder from the early 2000s.</p>
<p>It worked…</p>
<p>…until it didn’t.</p>
<p>Some MP3 files decoded perfectly. Others failed completely, even though they played fine in normal media players.</p>
<p>Instead of assuming it was a bug, I started reading JLayer’s source code.</p>
<p>That was when I discovered something I had never really thought about before.</p>
<p>Not every MP3 file is structured the same way.</p>
<p>Different bitrate modes, different frame layouts, optional headers, metadata blocks—everything is described by the bytes inside the file, not by its filename.</p>
<p>The more I looked, the more interesting it became.</p>
<p>Eventually I wasn’t trying to decode MP3 anymore.</p>
<p>I was trying to understand how media formats actually describe themselves.</p>
<hr>
<p>That curiosity eventually expanded beyond MP3.</p>
<p>WAV.</p>
<p>FLAC.</p>
<p>OGG.</p>
<p>AIFF.</p>
<p>Each format has its own signature, internal structure, metadata layout, and validation rules.</p>
<p>Instead of relying on file extensions, I started reading the actual binary structures directly.</p>
<p>That became the foundation of CodecMedia.</p>
<hr>
<p>Today, CodecMedia focuses primarily on <strong>probing and validating media files</strong> by parsing their actual byte structure.</p>
<p>Rather than asking “Does this filename end with <code>.mp3</code>?”, it asks questions like:</p>
<ul>
<li>Does the file contain a valid format signature?</li>
<li>Is the container structure correct?</li>
<li>Are the headers internally consistent?</li>
<li>Can the streams actually be identified?</li>
<li>Is the metadata structurally valid?</li>
</ul>
<p>Because of that, CodecMedia can identify media types from the file itself instead of trusting the extension.</p>
<p>Using it is straightforward.</p>
<pre><code class="hljs language-java"><span class="hljs-type">CodecMediaEngine</span> <span class="hljs-variable">engine</span> <span class="hljs-operator">=</span> CodecMedia.createDefault();

<span class="hljs-type">ProbeResult</span> <span class="hljs-variable">probe</span> <span class="hljs-operator">=</span> engine.probe(Path.of(<span class="hljs-string">&quot;song.mp3&quot;</span>));

System.out.println(probe.mimeType());
System.out.println(probe.durationMillis());
System.out.println(probe.streams().get(<span class="hljs-number">0</span>).codec());
System.out.println(probe.streams().get(<span class="hljs-number">0</span>).sampleRate());
</code></pre>
<p>If you want to verify that a file is structurally valid before processing it, validation is equally simple.</p>
<pre><code class="hljs language-java"><span class="hljs-type">ValidationResult</span> <span class="hljs-variable">validation</span> <span class="hljs-operator">=</span> engine.validate(
    Path.of(<span class="hljs-string">&quot;song.mp3&quot;</span>),
    <span class="hljs-keyword">new</span> <span class="hljs-title class_">ValidationOptions</span>(<span class="hljs-literal">true</span>, <span class="hljs-number">64L</span> * <span class="hljs-number">1024L</span> * <span class="hljs-number">1024L</span>)
);

System.out.println(validation.valid());
System.out.println(validation.errors());
</code></pre>
<p>CodecMedia currently understands formats including MP3, WAV, FLAC, OGG (Vorbis and Opus), AIFF/AIFC, and several others.</p>
<hr>
<p>CodecMedia also contains basic media conversion features.</p>
<p>However, conversion is not its primary goal.</p>
<p>There are already excellent projects like FFmpeg that provide far more complete transcoding capabilities.</p>
<p>CodecMedia is designed differently.</p>
<p>Its primary purpose is to inspect, identify, and validate media by understanding the file’s binary format directly, while providing lightweight utilities around that information.</p>
<hr>
<p>I don’t expect CodecMedia to replace existing media frameworks.</p>
<p>There are plenty of mature solutions available.</p>
<p>The reason I built it wasn’t because those tools weren’t good enough.</p>
<p>I built it because I wanted to understand what happens underneath them.</p>
<p>Reading binary structures, interpreting headers, validating file layouts, and implementing support myself taught me far more than simply calling an external library ever could.</p>
<p>CodecMedia is the result of that exploration.</p>
<p>If it also ends up being useful to someone else, that’s a nice bonus.</p>
<p>Maven Central: <a href="https://central.sonatype.com/artifact/me.tamkungz.codecmedia/codecmedia">https://central.sonatype.com/artifact/me.tamkungz.codecmedia/codecmedia</a></p>
<p>GitHub: <a href="https://github.com/CodecMediaLib/codecmedia-java">https://github.com/CodecMediaLib/codecmedia-java</a></p>
]]></content>
  </entry>
</feed>
