Build the Metin2 Android client for your own test server
Build the pinned Cemre Android client, stage compatible data, configure a test server, and troubleshoot ABI, protocol and port issues.
Selected client and scope
The most useful public starting point we found for a full Metin2 Android client is cemreefe/metin2-android. It contains the original C++ client, an Android launcher, touch input, native libraries and a self-hosting guide. This tutorial follows revision 4ac33651d49000de95e520e6de4373213dc54405; the client, data and server must match, so this is not a universal APK for every private server.
There is an important licensing caveat: the main repository did not declare a license for the complete client when checked on 25 October 2026. Public code is not automatically redistributable. OpenGr2ndma has an MPL-2.0 license, but that covers a component, not all client code or original game data. This guide includes neither game data nor an APK containing them. Selected source revision, OpenGr2ndma license.

Illustration: a recognisable Metin2 character on a phone while a client is being prepared. This is not a screenshot of a build or gameplay.
Why this project, and what we found on the forums
The Metin2 Mobile & PC Cross Platform Source thread on Turkmmo exposes BGFX/OpenGL archives after sign-in. The inspected opengl.zip contains an Android project and C++ client, but lacks the Android .a/.so libraries required to build it; the included Windows Granny library cannot replace them. We found no finished APK, full Gradle wrapper or license covering the whole client. We inspected the files but did not run any bundled program.
The newer thread titled Mobile Source … Full describes a Unity prototype. It lists touch controls and Android/iOS as future work. We have not inspected the separate large archive linked there; its title alone does not prove a finished client.
| Project | What the public evidence shows | Fit for this tutorial |
|---|---|---|
| Cemre Android port | C++/Java, libraries for two ABIs, profiles, data installer, touch UI and local-server source | Strongest candidate reviewed; license for the main client is unresolved |
| Shared Turkmmo OpenGL archive | Android skeleton and substantial client code, but no Android libraries | Needs more porting and dependency work |
| Metin2ToUnity on M2Dev | Tools to convert game assets | An importer is not a full network client |
| metin2-godot | MIT for its own code and documented development milestones | A promising development base, not a ready Android port |
The port author reports logging in, entering the world, fighting, opening inventory and saving. Most evidence is from an x86_64 emulator; physical arm64 devices and long sessions need separate testing. See our Metin2 mobile client overview for the broader project landscape.
Choose one testable setup
The main procedure connects an Android client to your own test server over a LAN. Run the server in a separate Linux machine or VM, with the phone on the same Wi-Fi. The alternative, embedding the server in the APK, is covered near the end.
| Component | Pinned revision/version |
|---|---|
| Android client | 4ac33651d49000de95e520e6de4373213dc54405 |
| m2dev-server-src | 66068c1 |
| m2dev-server: configuration, maps, SQL and quests | ad30738 |
| Expected m2dev-client data layout | 98d2c1af; use only data you may use |
| Java / Gradle | JDK 17 / Gradle 8.1.1 |
| Android tools | platform 33, Build Tools 33.0.1, NDK 25.2.9519653, CMake 3.22.1 |
| Target ABI | arm64-v8a for a matching phone; x86_64 for a matching emulator |
Build Tools 33.0.1 is specified in app/build.gradle, although the upstream install example lists a different patch version. Do not automatically swap pinned tools for the latest versions.
1. Install the tools and check out the exact revision
These commands are for Bash on Linux. Set the actual Android SDK path and a separate Gradle 8.1.1 installation. JDK 17 must be active in JAVA_HOME. Install the SDK through Android’s official tools, and read and accept any new license terms yourself. SDK Manager documentation.
java -version
gradle --version
sdkmanager "platforms;android-33" "build-tools;33.0.1" \
"ndk;25.2.9519653" "cmake;3.22.1" "platform-tools"
mkdir -p "$HOME/metin2-lab"
cd "$HOME/metin2-lab"
git clone https://github.com/cemreefe/metin2-android.git
cd metin2-android
git checkout --detach 4ac33651d49000de95e520e6de4373213dc54405
git submodule update --init --recursive
git submodule status
export M2_REPO="$PWD"
export M2_ANDROID="$M2_REPO/r10dev.net OPENGL-ITJA/android"
export ANDROID_SDK_ROOT="$HOME/Android/Sdk"
export GRADLE="/opt/gradle-8.1.1/bin/gradle"
printf 'sdk.dir=%s\n' "$ANDROID_SDK_ROOT" > "$M2_ANDROID/local.properties"
OpenGr2ndma is pinned at 210f5c326531297495c24e7bb1e24dc1b1a77833. If you skip the submodule, CMake fails; downloading a GitHub ZIP does not initialize it. Keep r10dev.net OPENGL-ITJA in quotes because the path contains spaces.
2. Create your profile and disable the external catalog
Save this as profiles/home.properties. Replace 192.168.1.50 with your Linux server’s LAN address. On a phone 127.0.0.1 points to the phone itself, not another computer.
m2.abi=arm64-v8a
m2.serverMode=remote
m2.serverHost=192.168.1.50
m2.authPort=11000
m2.channelPort=11011
m2.gamePortOffset=0
m2.applicationIdSuffix=.lab
m2.appLabel=Metin2 Local Lab
m2.versionName=home
m2.dataBundled=false
m2.dataUrl=
m2.dataVersion=1
m2.serverListUrl=
m2.devUrl=
m2.updateChannel=
Put only your own test server in servers/servers.json:
{
"version": 1,
"servers": [
{
"name": "Home Lab",
"host": "192.168.1.50",
"authPort": 11000,
"channelPort": 11011
}
]
}
The empty m2.serverListUrl= is intentional. If the setting is absent, the build falls back to the author’s remote catalog. Empty m2.devUrl= disables optional diagnostic uploads and update checks. That keeps the lab build from listing someone else’s development server. See ServerCatalog.java and DevReporter.java.
3. Build an APK before adding game data
This separates toolchain and native-library failures from data or server problems. A successful build alone does not mean the client can enter the game world.
cd "$M2_ANDROID"
"$GRADLE" --no-daemon --max-workers=4 assembleDebug \
-Pm2profile=home -Pm2abi=arm64-v8a
ls -lh app/build/outputs/apk/debug/app-debug.apk
For an x86_64 emulator, change the last argument to -Pm2abi=x86_64. The ABI must match the target. No finished public APK was available in the repository during this check; this command creates your own debug build.
Build result we reproduced
On Windows, the unmodified revision first stopped during C++ compilation: UserInterface/Locale.h shadowed the system <locale.h>. In a separate working copy, we removed ${CMAKE_CURRENT_SOURCE_DIR}/UserInterface from the include_directories(...) list in clientsource/CMakeLists.txt and added the following immediately after it:
add_compile_options("-iquote${CMAKE_CURRENT_SOURCE_DIR}/UserInterface")
The game directory remains available to quoted includes while leaving system-header lookup clear. With this change, assembleDebug succeeded for x86_64 and produced an APK containing the native client library. This is a patched working-copy result, not proof that unmodified upstream builds on every Windows machine. The Linux procedure above follows the author’s source and guide. We did not verify world entry, a physical phone, performance or the embedded-server build.
4. Stage compatible data with the supplied script
Put a data checkout with the expected layout in ~/metin2-lab/client-data and use only data you are entitled to use. This guide grants no rights to the original models, maps or music. A random pack from a different private server is not a verified substitute; protos, Python UI and file formats may differ.
sudo apt install rsync patch python3-pil libsodium23 liblzo2-2 zip imagemagick ninja-build
export M2_DATA="$HOME/metin2-lab/client-data"
export M2_STAGE="$HOME/metin2-lab/staged-data"
test -d "$M2_DATA/assets/root"
"$M2_ANDROID/tools/stage_client_data.sh" "$M2_DATA" "$M2_STAGE" en
stage_client_data.sh merges packs in order, overlays root, converts item_proto and mob_proto, and adds compatibility scripts, scaling and the touch HUD. Check the staging output before blaming the renderer. Do not bulk-rename Korean file names to lowercase.
The client also expects the Python 2.7.18 standard library directory Lib, not the host’s Python 3. Extract it from the official Python 2.7.18 source archive into a separate work directory. For example, set M2_PYLIB=~/metin2-lab/python-2.7.18/Lib. Python 2 is not a supported runtime for ordinary host services; it is a dependency of this legacy client.
To include the data in the APK, set m2.dataBundled=true in home.properties, then run:
export M2_PYLIB="$HOME/metin2-lab/python-2.7.18/Lib"
test -f "$M2_PYLIB/os.py"
M2_CLIENT_DATA="$M2_STAGE" M2_PYLIB="$M2_PYLIB" \
M2_BUNDLE_OUT="$HOME/metin2-lab/bundle" GRADLE="$GRADLE" \
"$M2_ANDROID/tools/make_bundle.sh" home 1
This outputs ~/metin2-lab/bundle/metin2-home-1.apk and a versioned data ZIP. Reserve disk space for the staged data, ZIP and APK. After changing the data, increment m2.dataVersion; removing a ZIP alone does not make an installed app re-extract the same version. The APK build number is separate. See make_bundle.sh.
5. Prepare a compatible server and reachable core addresses
This follows the author’s self-hosting guide. Use a separate Linux test VM, not a production server. The upstream guide lists Ubuntu 22.04, g++ 11, CMake 3.22 and MariaDB 10.6.
sudo apt install build-essential cmake git mariadb-server python3
cd "$HOME/metin2-lab"
git clone https://github.com/d1str4ught/m2dev-server-src.git
git -C m2dev-server-src checkout --detach 66068c1
git -C m2dev-server-src apply "$M2_REPO/server/m2dev-server-src-linux.patch"
cmake -S m2dev-server-src -B m2dev-server-src/build -DCMAKE_BUILD_TYPE=Release
cmake --build m2dev-server-src/build -j4
git clone https://github.com/d1str4ught/m2dev-server.git m2srv
git -C m2srv checkout --detach ad30738
cp m2dev-server-src/build/bin/{db,game,qc} m2srv/share/bin/
cp m2srv/share/bin/qc m2srv/share/locale/english/quest/qc
(cd m2srv/share/locale/english/quest && python3 make.py)
Create the databases and a restricted user before starting. This SQL is for a new, separate VM with no existing databases of these names. In sudo mariadb, replace both DOPLN_VLASTNI_HESLO placeholders with the same strong password; do not run it with that literal text.
CREATE DATABASE account;
CREATE DATABASE common;
CREATE DATABASE player;
CREATE DATABASE log;
CREATE DATABASE hotbackup;
CREATE USER 'm2lab'@'127.0.0.1' IDENTIFIED BY 'DOPLN_VLASTNI_HESLO';
CREATE USER 'm2lab'@'localhost' IDENTIFIED BY 'DOPLN_VLASTNI_HESLO';
GRANT ALL ON account.* TO 'm2lab'@'127.0.0.1', 'm2lab'@'localhost';
GRANT ALL ON common.* TO 'm2lab'@'127.0.0.1', 'm2lab'@'localhost';
GRANT ALL ON player.* TO 'm2lab'@'127.0.0.1', 'm2lab'@'localhost';
GRANT ALL ON log.* TO 'm2lab'@'127.0.0.1', 'm2lab'@'localhost';
GRANT ALL ON hotbackup.* TO 'm2lab'@'127.0.0.1', 'm2lab'@'localhost';
Then import the four supplied schemas. The runtime guide says to leave hotbackup empty initially:
cd "$HOME/metin2-lab/m2srv"
for db in account common player log; do
sudo mariadb "$db" < "sql/$db.sql"
done
In share/conf/db.txt, replace the user/password pair on SQL_ACCOUNT, SQL_PLAYER, SQL_COMMON and SQL_HOTBACKUP with your m2lab credentials. Do the same in share/conf/game.txt for ACCOUNT_SQL, PLAYER_SQL, COMMON_SQL and LOG_SQL. Keep the other values and field order intact. Never commit real credentials to a public repository. DB config, game config.
Keep MariaDB bound to 127.0.0.1; the phone must not connect to it. In this VM add the following under [mysqld] in /etc/mysql/mariadb.conf.d/99-metin2-lab.cnf:
[mysqld]
bind-address=127.0.0.1
sql_mode=NO_ENGINE_SUBSTITUTION
After sudo systemctl restart mariadb, test mariadb -h 127.0.0.1 -u m2lab -p account. Enter the password only at the prompt. This older runtime needs the SQL mode setting; do not apply it to a database shared with another project.
Create a new test game account in this database; the dump also includes preset accounts. Replace the example values with your own:
INSERT INTO account.account (login, password, social_id, status)
VALUES ('VLASTNI_TEST_LOGIN', PASSWORD('VLASTNI_TEST_HESLO'), '1234567', 'OK');
After preparing the databases, run python3 install.py in m2srv. It generates configs. Set the phone-reachable address in each game channels/channel1/coreN/CONFIG file:
BIND_IP: 192.168.1.50
Default detection may skip the server’s LAN address. Each core also advertises its address to clients. Recheck the setting after rerunning install.py, which regenerates the files.
| Connection | Default TCP port | Phone reachability |
|---|---|---|
| Login | 11000 | Yes, on the test LAN only |
| Channel 1 game cores | 11011, 11012, 11013 | All three, since map changes can redirect the client |
| DB process | 9000 | Internal server communication |
| P2P between cores | 12000 and above | Internal server communication |
In m2srv, start the channel with python3 start.py 1; stop it with python3 stop.py. Check auth and all three core logs. If login works but selecting a character returns to the server list, check the destination core’s address and port first. Reaching auth alone is not enough.
Do not update the server blindly. From commit 0cc595bf097dcf89ddba60615e56fe790823cd22, the handshake changes to 0xf8 using libsodium/XChaCha20. This client expects 0xfb/0xfa. A newer server requires porting its protocol to the client; changing the IP or password does not fix it.
6. Install on the phone and diagnose the first run
adb devices
adb shell getprop ro.product.cpu.abilist
adb install -r "$HOME/metin2-lab/bundle/metin2-home-1.apk"
adb shell pm path com.metin2.client.lab
Open the app, select your Home Lab and sign in only with the server’s test account. This profile’s package is com.metin2.client.lab; its external data path is usually:
adb shell ls /sdcard/Android/data/com.metin2.client.lab/files/
adb shell cat /sdcard/Android/data/com.metin2.client.lab/files/syserr.txt
adb logcat -d > metin2-android-logcat.txt
The base package is com.metin2.client; upstream’s offline profile is com.metin2.client.offline. Their paths differ. The app uses getExternalFilesDir(null), so confirm the actual app directory on the device. If an update fails, check ABI, package ID and APK signature. Blind uninstallation can delete offline characters.
| Symptom | First thing to check |
|---|---|
| CMake cannot find OpenGr2ndma | Submodule initialization and pinned commit |
| APK will not install for this ABI | ro.product.cpu.abilist against -Pm2abi |
| Disconnect just after login | Server handshake version, then logs |
| Return after character selection | Redirect IP, BIND_IP and all core ports |
| Missing UI or black scene | Staged data, protos, Python Lib and syserr.txt |
| Old data after updating | Higher m2.dataVersion, correct profile and ZIP |
Put the whole server inside the APK
The bundled profile builds Android server processes, adds SQLite instead of MySQL and packages server data with the client. The phone then needs no remote server. It takes a separate build and data pack; it is not automatic in the APK from step 3.
For offline play, replace servers/servers.json with this local catalog:
{
"version": 2,
"servers": [
{ "name": "Single Player", "embedded": true }
]
}
Otherwise the LAN entry from the earlier steps remains the only server shown. In profiles/bundled.properties, set m2.serverListUrl=, m2.devUrl= and m2.updateChannel= to empty. Keep m2.serverMode=embedded, m2.dataBundled=true and the .offline suffix. Increment m2.dataVersion for a new pack. With the source/runtime checkouts prepared, use:
"$M2_REPO/r10dev.net OPENGL-ITJA/server/tools/make-server-pack.sh" \
"$HOME/metin2-lab/m2srv" \
"$HOME/metin2-lab/m2dev-server-src" \
"$HOME/metin2-lab/server-pack"
M2_SERVER_PACK="$HOME/metin2-lab/server-pack" \
M2_CLIENT_DATA="$M2_STAGE" M2_PYLIB="$M2_PYLIB" \
M2_BUNDLE_OUT="$HOME/metin2-lab/bundle" GRADLE="$GRADLE" \
"$M2_ANDROID/tools/make_bundle.sh" bundled 1
Beforehand, check bundled.properties, your catalog, dataVersion and the seed database’s test account. make-server-pack.sh expects runtime checkout, source checkout and output directory, in that order. Do not copy the different example from the upstream web guide. The embedded-server Gradle task uses Linux NDK paths, so this has not been verified as a Windows offline build.
A successful build, app launch, login and continuous gameplay are different results. For your own test, create a character, enter a map, fight, move between cores, relaunch and check that inventory saved. Without these checks, you cannot claim a particular phone/server combination has been verified.
Frequently Asked Questions
Will this client connect to my favourite private server?
Is this also an iPhone guide?
Can I publicly share the APK with game data?
Related Pages
Find Your Perfect Server
Browse the top-ranked Metin2 private servers on METIN2.GG or submit your own server to the rankings.

