{
  "name": "TamKungZ_ Blog",
  "description": "Technical articles, dev logs, release notes, and project notes from TamKungZ_.",
  "canonicalUrl": "https://dev.tamkungz.me/blog",
  "languageSupport": [
    "en",
    "th",
    "any language supported by Unicode Markdown"
  ],
  "attribution": {
    "name": "TamKungZ_",
    "canonicalUrl": "https://dev.tamkungz.me",
    "github": "https://github.com/TamKungZ"
  },
  "usage": {
    "permission": "Public content may be indexed, summarized, quoted in short excerpts, and cited with canonical URLs.",
    "attributionPreference": "When using this content, cite TamKungZ_ and link to the canonical post URL.",
    "warning": "Do not fabricate facts beyond the published content."
  },
  "feeds": {
    "rss": "https://dev.tamkungz.me/blog/feed.xml",
    "sitemap": "https://dev.tamkungz.me/sitemap.xml",
    "llms": "https://dev.tamkungz.me/blog/llms.txt",
    "siteLlms": "https://dev.tamkungz.me/llms.txt"
  },
  "posts": [
    {
      "id": "5",
      "title": "NilLoaderSDK Is Becoming NilKit API — A Temporary Pause and a Necessary Change",
      "canonicalUrl": "https://dev.tamkungz.me/blog/2026-08-06-nilloadersdk-is-becoming-nilkit-api-a-temporary-pause-and-a-necessary-change",
      "author": "TamKungZ_",
      "language": "en",
      "languageName": "English",
      "textDirection": "ltr",
      "category": "Project Update",
      "tags": [
        "NilKit API",
        "NilLoader",
        "Minecraft modding",
        "open source",
        "project update",
        "branding"
      ],
      "publishedDate": "2026-08-06",
      "updatedDate": "2026-08-06",
      "readingTimeMinutes": 6,
      "wordCount": 1219,
      "image": "https://ik.imagekit.io/TamKungZ/rename-nilloadersdk-and-nilloadersdk-is-not-nilloader-official.png",
      "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.",
      "excerpt": "NilLoaderSDK Is Becoming NilKit API — A Temporary Pause and a Necessary Change An update about the project name, its relationship with NilLoader, and what happens next. --- A Ch...",
      "keywords": [
        "NilKit API",
        "NilLoaderSDK",
        "NilLoader",
        "Minecraft modding",
        "open source",
        "TamKungZ_"
      ],
      "markdown": "# NilLoaderSDK Is Becoming NilKit API — A Temporary Pause and a Necessary Change\n\n*An update about the project name, its relationship with NilLoader, and what happens next.*\n\n---\n\n## A Change I Need to Make\n\nI am temporarily taking **NilLoaderSDK** offline while I make a number of changes to the project.\n\nWhen it returns, it will no longer be called NilLoaderSDK. The project will be renamed to **NilKit API**.\n\nThis change follows a discussion with the maintainer of [NilLoader](https://git.sleeping.town/Nil/NilLoader) about how the project was being presented and how easily the name and branding could cause confusion.\n\nI want to be clear about something from the beginning: **I did not intend to impersonate NilLoader, claim ownership of it, or present my project as an official component of NilLoader.**\n\nBut intent does not erase the fact that the name I chose could reasonably give people that impression.\n\nThat part is on me.\n\n---\n\n## I Got the Naming Wrong\n\nWhen I originally chose the name **NilLoaderSDK**, my reasoning was simple: it was an SDK I had built for development around NilLoader.\n\nFrom my perspective, the name described what the project did.\n\nWhat I did not consider carefully enough was how the name would look from the outside.\n\nNames such as \"Fabric API\" and \"Fabric Loader\" have trained Minecraft modders to associate similarly named projects with an official ecosystem. Because of that, **NilLoaderSDK** could easily be interpreted as the official SDK or API for NilLoader, or even as something required for NilLoader mods to work.\n\nThat was never what the project was meant to be.\n\nI 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.\n\nSo I accept that I made a mistake in how I named and presented the project.\n\nIt was not intentional, but it was still my responsibility.\n\n---\n\n## The New Name: NilKit API\n\nAfter discussing the issue, I proposed the name **NilKit API**, and the NilLoader maintainer confirmed that this name was fine.\n\nThe 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.\n\nMost importantly, it removes the implication that this is an official part of NilLoader itself.\n\nGoing forward, the distinction will be much clearer:\n\n- **NilLoader** is the original mod loader and Java agent project maintained by its own developers.\n- **NilKit API** is my independent community project that builds optional tooling around it.\n- NilKit API is **not official, not required by NilLoader, and not a replacement for NilLoader**.\n\nThe original NilLoader project can be found here:\n\n**[git.sleeping.town/Nil/NilLoader](https://git.sleeping.town/Nil/NilLoader)**\n\n---\n\n## Why the Project Is Going Private Temporarily\n\nRather 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.\n\nFor that reason, I am temporarily making the project unavailable while I review and update everything associated with the old identity.\n\nThis includes, where applicable:\n\n- The project name\n- Repository names and descriptions\n- Package and artifact naming\n- Mod IDs and metadata\n- Documentation\n- GitHub organization branding\n- Modrinth information\n- Logos and visual identity\n- References that could imply an official relationship with NilLoader\n- Internal implementation choices that deserve another review\n\nI would rather take the project offline for a short period and bring it back consistently than leave half-renamed pieces scattered across different services.\n\nThis is a pause for cleanup, **not the end of the project**.\n\n---\n\n## The Technical Side Also Deserves Another Look\n\nThe discussion was not only about the name.\n\nSome parts of the project relied heavily on NilLoader internals through reflection and attempted to provide additional bootstrapping behavior around NilLoader's normal machinery.\n\nEven 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.\n\nI am willing to maintain compatibility when things change, but that does not mean every workaround is a good architectural decision.\n\nSo while the project is private, I will also use the opportunity to reconsider those parts instead of treating this as a cosmetic rename only.\n\nThe goal remains the same: **make developing with NilLoader more convenient without trying to become NilLoader itself.**\n\n---\n\n## Why I Built This in the First Place\n\nNilKit API exists because I personally encountered problems and repetitive work while developing with NilLoader.\n\nMy reaction was the same one that has started countless open-source projects: build the tool I wished I already had.\n\nI 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.\n\nI 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.\n\nBut building around someone else's project also means being careful about where that project ends and mine begins.\n\nThat distinction should have been clearer from day one.\n\n---\n\n## About AI\n\nI also want to address this directly because it came up during the discussion.\n\nYes, I use AI tools as part of my development workflow.\n\nI 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.\n\nAt the same time, **I am responsible for what I publish**.\n\nIf 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.\n\nThat applies here as well.\n\n---\n\n## What Happens Next\n\nFor now, the GitHub and Modrinth presence associated with the project will be temporarily restricted while I complete the transition.\n\nWhen the project returns, it will return as **NilKit API**, with a new identity and clearer separation from NilLoader.\n\nI will also review related tooling individually rather than assuming that the same naming or branding decisions are appropriate for every project.\n\nThere is no exact reopening date yet. I want to finish the cleanup properly before making everything public again.\n\n---\n\n## A Final Note\n\nI want to apologize to the NilLoader maintainer and to anyone who was confused by the original name or presentation.\n\nThere was no intention to take over the project, pass my work off as official, or diminish the work of the people who created NilLoader.\n\nI should have contacted the maintainer earlier instead of making assumptions about how close my project's identity could be to the upstream project.\n\nI appreciate that the concerns were raised directly, and I would rather correct the problem now than allow the confusion to continue.\n\n**NilLoaderSDK is going away as a name. NilKit API will take its place.**\n\nThe idea behind the project is continuing — just with a clearer identity, a cleaner boundary, and more respect for the project it was built around.\n\n---\n\n*Built and maintained by TamKungZ_.*"
    },
    {
      "id": "4",
      "title": "TrueMoney สแกนหน้ายืนยันตัวตนไม่ได้บน Huawei ที่ใช้ microG: วิธีที่ผมลองแล้วหาย",
      "canonicalUrl": "https://dev.tamkungz.me/blog/2026-07-16-truemoney-huawei-microg-safetynet-face-verification",
      "author": "TamKungZ_",
      "language": "th",
      "languageName": "Thai",
      "textDirection": "ltr",
      "category": "Troubleshooting",
      "tags": [
        "TrueMoney",
        "Huawei",
        "microG",
        "SafetyNet",
        "Device Attestation",
        "GMS"
      ],
      "publishedDate": "2026-07-16",
      "updatedDate": "2026-08-06",
      "readingTimeMinutes": 3,
      "wordCount": 623,
      "image": "https://ik.imagekit.io/TamKungZ/gsm-truemonney-thumbnail.webp",
      "summary": "บันทึกวิธีแก้จากประสบการณ์ตรง เมื่อ TrueMoney Wallet สแกนหน้ายืนยันตัวตนไม่ได้บน Huawei ที่ใช้ microG โดยลองปิด Allow requests ของ TrueMoney ใน Device Attestation.",
      "excerpt": "TrueMoney สแกนหน้ายืนยันตัวตนไม่ได้บน Huawei ที่ใช้ microG: วิธีที่ผมลองแล้วหาย บทความนี้เป็นประสบการณ์ตรงของผมเอง อาจจะไม่ได้เกิดกับทุกคน และสาเหตุจริงอาจไม่ได้เหมือนกันทุกเครื...",
      "keywords": [
        "TrueMoney สแกนหน้าไม่ได้",
        "TrueMoney Huawei",
        "Huawei Y73 TrueMoney",
        "microG SafetyNet",
        "Device Attestation",
        "GMS Huawei",
        "ยืนยันตัวตน TrueMoney ไม่ได้",
        "Face verification",
        "ยืนยันตัวตน",
        "สแกนหน้า",
        "ยืนยันตัวตน TrueMoney",
        "สแกนหน้า TrueMoney",
        "Face Scan",
        "Huawei microG",
        "Huawei"
      ],
      "markdown": "# TrueMoney สแกนหน้ายืนยันตัวตนไม่ได้บน Huawei ที่ใช้ microG: วิธีที่ผมลองแล้วหาย\n\nบทความนี้เป็นประสบการณ์ตรงของผมเอง อาจจะไม่ได้เกิดกับทุกคน และสาเหตุจริงอาจไม่ได้เหมือนกันทุกเครื่อง\n\nแต่ผมเจอเคสหนึ่งที่หาวิธีแก้ในเน็ตค่อนข้างยาก เลยอยากจดไว้ เผื่อมีประโยชน์กับใครบางคนที่เจอปัญหาคล้ายกัน\n\nปัญหาคือ **TrueMoney Wallet สแกนหน้าเพื่อยืนยันตัวตนไม่ได้** บนมือถือ Huawei\n\nในกรณีของผมคือ **Huawei Y73** ซึ่งเป็นเครื่องที่ต้องใช้ GMS ผ่าน microG ในการใช้งานบางอย่างของ Google services\n\n> หมายเหตุ: หลายคนอาจพิมพ์หรือเรียกสลับกันระหว่าง GSM/GMS แต่ในบริบทนี้ผมหมายถึงฝั่ง Google Mobile Services และ microG\n\n## อาการที่เจอ\n\nตอนใช้งาน TrueMoney Wallet แล้วต้องสแกนหน้าเพื่อยืนยันตัวตน แอปไม่สามารถผ่านขั้นตอนนี้ได้\n\nข้อความที่ผมเจอประมาณนี้:\n\n> ไม่สามารถทำรายการได้  \n> กรุณาทำรายการใหม่ภายหลัง\n\nผมลองหาข้อมูลอยู่พักหนึ่ง แต่ไม่ค่อยเจอวิธีแก้ที่ตรงกับเคส Huawei + microG เท่าไร โดยเฉพาะเคสที่เกี่ยวกับการยืนยันตัวตนในแอปการเงิน\n\nสุดท้ายผมสงสัยว่าอาจเกี่ยวกับ **SafetyNet / Device Attestation** เพราะแอปบางตัวจะตรวจสถานะอุปกรณ์ก่อนให้ใช้งานฟีเจอร์บางอย่าง\n\n## วิธีที่ผมลองแล้วใช้ได้\n\nขั้นตอนนี้เป็นสิ่งที่ผมทำบนเครื่องของผมเอง เมนูอาจต่างกันตามรุ่น Huawei, เวอร์ชันระบบ, launcher, หรือเวอร์ชันของ microG\n\n1. เปิด **Settings**\n2. เข้า **Apps**\n3. ค้นหา **microG Services**\n4. กดเข้าไปที่แอปนั้น\n5. มองหาปุ่มตั้งค่ารูป **Gear**\n6. กดเข้าไป จะเข้าสู่หน้า **microG Settings**\n7. หาเมนู **Device Attestation**\n8. เข้าไปที่รายการ **Apps using SafetyNet**\n9. หา **TrueMoney**\n10. กดเข้าไป\n11. ถ้าเห็นตัวเลือก **Allow requests** และมันเปิดอยู่ ให้ลองปิดมัน\n12. จากนั้นลองกลับไปสแกนหน้าใน TrueMoney อีกครั้ง\n\nในเคสของผม หลังจากปิด **Allow requests** ของ TrueMoney ใน Device Attestation แล้ว การสแกนหน้าเพื่อยืนยันตัวตนกลับมาใช้งานได้\n\n## ทำไมวิธีนี้อาจช่วยได้\n\nผมยังไม่อยากสรุปแบบฟันธงว่า TrueMoney มีปัญหากับ SafetyNet เสมอ หรือ microG เป็นสาเหตุหลักเสมอ\n\nแต่จากที่เจอกับเครื่องของผม ดูเหมือนว่า TrueMoney อาจติดขั้นตอนที่เกี่ยวกับการเรียก SafetyNet หรือ Device Attestation ผ่าน microG แล้วทำให้ flow การยืนยันตัวตนมีปัญหา\n\nพอปิดไม่ให้ TrueMoney ส่ง request ตรงนี้ผ่าน microG การยืนยันตัวตนเลยผ่านได้\n\nอย่างไรก็ตาม นี่อาจเป็นแค่ workaround เฉพาะบางเครื่อง บางเวอร์ชัน หรือบางสภาพแวดล้อมเท่านั้น\n\n## ข้อควรระวัง\n\nถ้าคุณใช้แอปการเงินหรือแอปที่เกี่ยวข้องกับความปลอดภัย ควรระวังเวลาแก้ค่าเกี่ยวกับ SafetyNet, Device Attestation หรือ microG\n\nวิธีนี้ไม่ได้แปลว่าควรปิดทุกอย่างในทุกแอป และไม่ได้รับประกันว่าจะใช้ได้กับทุกคน\n\nถ้าลองแล้วไม่ดีขึ้น ให้เปิดค่ากลับเหมือนเดิม แล้วลองตรวจอย่างอื่นต่อ เช่น เวอร์ชัน TrueMoney, เวอร์ชัน microG, permission ของกล้อง, network, หรือสถานะบริการของแอป\n\n## สรุป\n\nถ้าคุณใช้ Huawei ที่พึ่งพา microG/GMS และเจอปัญหา **TrueMoney Wallet สแกนหน้ายืนยันตัวตนไม่ได้** วิธีนี้อาจเป็นอีกหนึ่งทางที่น่าลอง:\n\n**microG Settings > Device Attestation > Apps using SafetyNet > TrueMoney > ปิด Allow requests**\n\nนี่เป็นแค่ประสบการณ์จากเครื่องของผม แต่ถ้ามันช่วยประหยัดเวลาคนอื่นที่กำลังหาวิธีแก้ได้บ้าง ก็น่าจะคุ้มที่จะเผยแพร่ไว้\n"
    },
    {
      "id": "3",
      "title": "Why I Built NilLoaderSDK — A Classic \"Scratch Your Own Itch\" Story",
      "canonicalUrl": "https://dev.tamkungz.me/blog/2026-04-08-why-i-built-nilloadersdk-a-classic-scratch-your-own-itch-story",
      "author": "TamKungZ_",
      "language": "en",
      "languageName": "English",
      "textDirection": "ltr",
      "category": "Project Story",
      "tags": [
        "NilLoaderSDK",
        "NilLoader",
        "Minecraft modding",
        "Java",
        "reflection",
        "mod loader SDK"
      ],
      "publishedDate": "2026-04-08",
      "updatedDate": "2026-08-06",
      "readingTimeMinutes": 4,
      "wordCount": 708,
      "image": "https://dev.tamkungz.me/assets-image/nilloadersdk-logo.png",
      "summary": "A project story about why TamKungZ_ built NilLoaderSDK to make NilLoader-based Minecraft mod development less repetitive while keeping mapping distribution legally clean.",
      "excerpt": "Why I Built NilLoaderSDK — A Classic \"Scratch Your Own Itch\" Story A utility SDK for NilLoader-based Minecraft mods, born out of frustration and necessity. --- The Problem With ...",
      "keywords": [
        "NilLoaderSDK",
        "NilLoader",
        "Minecraft modding",
        "Java reflection",
        "mod loader SDK",
        "TamKungZ_"
      ],
      "markdown": "# Why I Built NilLoaderSDK — A Classic \"Scratch Your Own Itch\" Story\n\n*A utility SDK for NilLoader-based Minecraft mods, born out of frustration and necessity.*\n\n---\n\n## The Problem With NilLoader (And Why That's Also Its Superpower)\n\nIf you've ever worked with [NilLoader](https://git.sleeping.town/Nil/NilLoader), 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.\n\nThat flexibility is genuinely powerful. But it comes with a cost.\n\nBecause 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.\n\nFor seasoned modders, that's fine. For anyone newer to the space, it's a steep cliff.\n\nThat's exactly why I built **NilLoaderSDK**.\n\n---\n\n## What NilLoaderSDK Actually Is\n\nNilLoaderSDK 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.\n\nHere's what it bundles:\n\n**Reflection Helpers** — Working with private fields and methods in Minecraft's obfuscated codebase is tedious. `ReflectHelper` wraps the boilerplate so you're not writing the same try-catch reflection blocks over and over. `ProxyHelper` extends this for dynamic proxy use cases.\n\n**Structured Logging** — A consistent logging standard built around NilLoader's `NilLogger`. There's a root SDK logger (`NilLoaderSDK`) and a class-scoped variant (`NilLoaderSDK/<ClassName>`). Small thing, but it keeps logs readable across a whole project.\n\n**Entrypoint Dispatcher** — NilLoader has `premain` and `hijack` phases, and coordinating those across a larger project gets messy fast. `EntrypointDispatcher` 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.\n\n**Java NIO Networking** — A full non-blocking networking layer: `NioServer`, `NioClient`, `PacketCodec`, `PacketRegistry`, and a `MinecraftAutoNetworkBridge` that can be wired up purely through JVM properties. Useful for mods that need to talk to external services or other game instances.\n\n**Utilities** — `CooldownTracker` for rate-limiting logic and `TargetFinder` for locating entities or objects in the world.\n\nMost of these features exist because I needed them. That's the honest origin story.\n\n---\n\n## The Elephant in the Room: Mappings\n\nIf you're modding old Minecraft — pre-1.7 especially — one of the biggest headaches is **mappings**. Modern toolchains make this almost invisible: configure Gradle, pick a mapping set, done. Back then, it was a whole ordeal.\n\nThe historical go-to was **Mod Coder Pack (MCP)**, 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.\n\nNilLoaderSDK includes a `SimpleRemap` utility — but I want to be clear about what it *doesn't* do. It doesn't ship bundled mappings, and it won't. The reason is simple: **licensing**. 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.\n\nSo the approach here is intentional: **developers bring their own mappings locally**. 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.\n\n---\n\n## Who Is This For?\n\nRealistically, 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.\n\nBut 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.\n\nIt's a small thing. But small things add up.\n\n---\n\n## Getting Started\n\nAdd the SDK to your project via Maven:\n\n```kotlin\nrepositories {\n    maven { url = uri(\"https://repo.tamkungz.me\") }\n}\n\ndependencies {\n    implementation(\"me.tamkungz.nilloadersdk:nilloadersdk:2.0.1\")\n}\n```\n\nThe source is available on GitHub, licensed under **LGPL-3.0-or-later**.\n\n- **GitHub**: [TamKungZ/NilLoaderSDK](https://github.com/TamKungZ/NilLoaderSDK)\n- **Maven Repository**: [repo.tamkungz.me](https://repo.tamkungz.me)\n- **NilLoader upstream**: [git.sleeping.town/Nil/NilLoader](https://git.sleeping.town/Nil/NilLoader)\n\n---\n\n*Built by TamKungZ_ — mostly because I needed it.*"
    },
    {
      "id": "2",
      "title": "VLiva Devlog #1 — Why I Started Building a Linux VTuber App",
      "canonicalUrl": "https://dev.tamkungz.me/blog/2026-03-26-vliva-devlog-1-why-i-started-building-a-linux-vtuber-app",
      "author": "TamKungZ_",
      "language": "en",
      "languageName": "English",
      "textDirection": "ltr",
      "category": "Devlog",
      "tags": [
        "VLiva",
        "Linux",
        "VTuber app",
        "Live2D",
        "C++",
        "OpenGL"
      ],
      "publishedDate": "2026-03-26",
      "updatedDate": "2026-08-06",
      "readingTimeMinutes": 3,
      "wordCount": 551,
      "image": "https://dev.tamkungz.me/assets-image/vliva-screenshot.png",
      "summary": "The first VLiva devlog explaining why TamKungZ_ started building a native Linux VTuber app instead of relying on Windows tools or compatibility layers.",
      "excerpt": "VLiva Devlog #1 — Why I Started Building a Linux VTuber App I didn’t originally plan to build this. VLiva started as a small side experiment — something I made mostly out of fru...",
      "keywords": [
        "VLiva",
        "Linux VTuber app",
        "Live2D Linux",
        "C++ OpenGL",
        "VTuber software",
        "TamKungZ_"
      ],
      "markdown": "# VLiva Devlog #1 — Why I Started Building a Linux VTuber App\n\nI didn’t originally plan to build this.\n\nVLiva started as a small side experiment — something I made mostly out of frustration.\nI work with Live2D occasionally, and my main OS is Linux. Switching between Linux and Windows just to run VTuber software quickly became annoying.\n\nSo I looked for alternatives.\n\nThere *are* ways to run existing tools on Linux, but most of them involve:\n\n* compatibility layers\n* workarounds\n* unstable setups\n* or noticeable performance loss\n\nNone of them felt clean.\n\nAt some point I stopped searching and thought:\n“What if I just build something myself?”\n\n## The Early Stage\n\nAt the beginning, I didn’t take this project seriously at all.\nIt was more like: “let’s see if this is even possible.”\n\nThe main challenge was obvious:\n\n* C++\n* OpenGL\n* real-time rendering\n* Live2D runtime behavior\n\nThis wasn’t something I could just throw together in a weekend.\n\nProgress was slow.\nI spent around 5 months on the project — although realistically, I paused for more than half of that time.\n\n## Coming Back\n\nRecently, I came back to the project with a different mindset.\n\nInstead of experimenting, I wanted to actually make it work.\n\nSurprisingly, things started to click:\n\n* rendering pipeline became stable\n* model loading worked\n* basic interaction started functioning\n\nAnd at some point, I realized:\n\n> “This is actually usable.”\n\nNot perfect. Not polished.\nBut real.\n\n## What VLiva Is (for now)\n\nVLiva is a native Linux VTuber app prototype that focuses on simplicity:\n\n* No Wine\n* No virtual machines\n* No external dependencies (as much as possible)\n* Just install and run\n\nThat’s the core idea.\n\n## What’s Next\n\nRight now, the project is still far from finished.\n\nThings that still need work:\n\n* UI/UX\n* performance optimization\n* input / tracking improvements\n* overall stability\n\nI’m planning to release a **demo version around April** to gather feedback and bug reports.\n\n👉 https://vliva.tamkungz.me/\n\nIf the project reaches a stable and complete state, I may release it on Steam in the future.\nPricing is undecided, but the demo will remain free.\n\n## Why I’m Sharing This\n\nOriginally, this was just a personal tool.\n\nBut now I’m curious:\n\nIs there actually a demand for VTubing on Linux?\nAre there other people dealing with the same problems?\n\nIf yes, then this project might be worth pushing further.\n\nIf not… well, at least I built something interesting.\n\nEither way, I’ll keep going for now.\n\n---\n\n## FAQ (from Reddit feedback)\n\n### Is this similar to Inochi2D?\nSome people suggested Inochi2D, and yes — I’ve looked into it before. Their source code is clean and their optimization approach is great.\n\nBut my main target for VLiva is **Live2D support**, so Inochi2D can only help as a partial reference, not a direct solution.\n\n### Does VTube Studio already work on Linux?\nFor some users, yes. One Linux user reported that VTube Studio + VBridger was basically plug-and-play.\n\nThat said, Linux experiences can still vary by distro and setup (Proton, drivers, etc.).\n\n### Then why build VLiva?\nThe goal is not to replace VTube Studio.\n\nVLiva is being built as a **fully native Linux alternative** — no compatibility layer in between, and a simpler path for people who prefer native workflows.\n\n### Will VLiva support VBridger compatibility?\nI’m currently exploring that idea. It’s not confirmed yet, but compatibility with existing VBridger workflows could make adoption much easier for Linux VTubers.\n"
    },
    {
      "id": "1",
      "title": "How a Minecraft mod led me to build my own Java media library from scratch",
      "canonicalUrl": "https://dev.tamkungz.me/blog/2026-03-14-how-a-minecraft-mod-led-me-to-build-my-own-java-media-library-from-scratch",
      "author": "TamKungZ_",
      "language": "en",
      "languageName": "English",
      "textDirection": "ltr",
      "category": "Project Story",
      "tags": [
        "CodecMedia",
        "Java",
        "media parsing",
        "Minecraft modding",
        "OpenAL"
      ],
      "publishedDate": "2026-03-14",
      "updatedDate": "2026-08-06",
      "readingTimeMinutes": 3,
      "wordCount": 542,
      "image": "https://dev.tamkungz.me/assets-image/blog/banner.webp",
      "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.",
      "excerpt": "I wasn't trying to build another media converter. What I actually wanted was something that could answer a much simpler question: \"Is this file really what it claims to be?\" Tha...",
      "keywords": [
        "CodecMedia",
        "Java media library",
        "media validation",
        "MP3 parser",
        "Minecraft modding",
        "OpenAL",
        "TamKungZ_"
      ],
      "markdown": "I wasn't trying to build another media converter.\n\nWhat I actually wanted was something that could answer a much simpler question:\n\n**\"Is this file really what it claims to be?\"**\n\nThat sounds obvious until you start looking at media files beyond their extensions.\n\nA file named `song.mp3` 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.\n\nThat realization eventually became **CodecMedia**.\n\n---\n\nIt started while I was building a Minecraft mod.\n\nI 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 **Zoom JLayer**, a pure Java decoder from the early 2000s.\n\nIt worked...\n\n...until it didn't.\n\nSome MP3 files decoded perfectly. Others failed completely, even though they played fine in normal media players.\n\nInstead of assuming it was a bug, I started reading JLayer's source code.\n\nThat was when I discovered something I had never really thought about before.\n\nNot every MP3 file is structured the same way.\n\nDifferent bitrate modes, different frame layouts, optional headers, metadata blocks—everything is described by the bytes inside the file, not by its filename.\n\nThe more I looked, the more interesting it became.\n\nEventually I wasn't trying to decode MP3 anymore.\n\nI was trying to understand how media formats actually describe themselves.\n\n---\n\nThat curiosity eventually expanded beyond MP3.\n\nWAV.\n\nFLAC.\n\nOGG.\n\nAIFF.\n\nEach format has its own signature, internal structure, metadata layout, and validation rules.\n\nInstead of relying on file extensions, I started reading the actual binary structures directly.\n\nThat became the foundation of CodecMedia.\n\n---\n\nToday, CodecMedia focuses primarily on **probing and validating media files** by parsing their actual byte structure.\n\nRather than asking \"Does this filename end with `.mp3`?\", it asks questions like:\n\n* Does the file contain a valid format signature?\n* Is the container structure correct?\n* Are the headers internally consistent?\n* Can the streams actually be identified?\n* Is the metadata structurally valid?\n\nBecause of that, CodecMedia can identify media types from the file itself instead of trusting the extension.\n\nUsing it is straightforward.\n\n```java\nCodecMediaEngine engine = CodecMedia.createDefault();\n\nProbeResult probe = engine.probe(Path.of(\"song.mp3\"));\n\nSystem.out.println(probe.mimeType());\nSystem.out.println(probe.durationMillis());\nSystem.out.println(probe.streams().get(0).codec());\nSystem.out.println(probe.streams().get(0).sampleRate());\n```\n\nIf you want to verify that a file is structurally valid before processing it, validation is equally simple.\n\n```java\nValidationResult validation = engine.validate(\n    Path.of(\"song.mp3\"),\n    new ValidationOptions(true, 64L * 1024L * 1024L)\n);\n\nSystem.out.println(validation.valid());\nSystem.out.println(validation.errors());\n```\n\nCodecMedia currently understands formats including MP3, WAV, FLAC, OGG (Vorbis and Opus), AIFF/AIFC, and several others.\n\n---\n\nCodecMedia also contains basic media conversion features.\n\nHowever, conversion is not its primary goal.\n\nThere are already excellent projects like FFmpeg that provide far more complete transcoding capabilities.\n\nCodecMedia is designed differently.\n\nIts primary purpose is to inspect, identify, and validate media by understanding the file's binary format directly, while providing lightweight utilities around that information.\n\n---\n\nI don't expect CodecMedia to replace existing media frameworks.\n\nThere are plenty of mature solutions available.\n\nThe reason I built it wasn't because those tools weren't good enough.\n\nI built it because I wanted to understand what happens underneath them.\n\nReading binary structures, interpreting headers, validating file layouts, and implementing support myself taught me far more than simply calling an external library ever could.\n\nCodecMedia is the result of that exploration.\n\nIf it also ends up being useful to someone else, that's a nice bonus.\n\nMaven Central: https://central.sonatype.com/artifact/me.tamkungz.codecmedia/codecmedia\n\nGitHub: https://github.com/CodecMediaLib/codecmedia-java\n"
    }
  ]
}