Clean-room Metin2 server emulator in Go, targeting TMP4-era client compatibility.
This repository is a public rewrite built around owned protocol documentation, small verified slices, and a gradual path from a stable boot flow to a real shared-world game server. It intentionally avoids copying legacy source code: legacy trees and captures are treated only as external behavior oracles.
go-metin2-server is pre-alpha. It is not a playable legacy-compatible server yet, but it is also no longer just a packet experiment. The repository currently has:
- real
authdandgameddaemon entrypoints, - a secure legacy handshake and login/select/game boot path,
- a shared in-process world runtime,
- protocol codecs and fixtures for the owned packet families,
- real-client-oriented integration tests around movement, visibility, chat, items, shops, combat, death, restart, and respawn slices,
- local operator/debug endpoints for runtime inspection, controlled bootstrap actions, static-actor respawn timer plus spawn-group snapshot/map-occupancy/transfer visibility inspection, and strict manifest-backed item-template/account-store backup/restore preflights.
Current repository shape from the latest scan:
- Go version:
1.26 - Go packages: 38
- Go files: 122
- Go test files: 67
- Markdown docs: 115
- protocol docs under
spec/protocol: 68 - CI: GitHub Actions runs
gofmt,go test ./...,go vet ./..., daemon builds, and Docker runtime/debug builds
Legend used below:
[x]implemented enough for the current milestone[~]partial / bootstrap / intentionally narrow[ ]not started or not compatibility-grade yet
-
[x]M0 — Protocol-owned boot path- Frame parsing, session phases, secure legacy handshake, auth/login, character selection, loading, enter-game, initial character/point bootstrap, and basic control packets are owned by Go code, protocol docs, and tests.
-
[~]M1 — Shared-world pre-alpha- Multiple players can exist in the same in-process world, see each other, move, sync position, talk locally, receive notices, route whispers by exact name, transfer through bootstrap map seams, reconnect/cleanup, and rebuild visibility. It is still a single-process bootstrap runtime, not a production channel/shard architecture.
-
[~]M2 — World/entity runtime foundation- The repo has topology, map indexing, AOI/radius-style visibility, player/session directories, entity registries, non-player directories, static actors, spawn groups, runtime scopes, and operator snapshots. Directory and map-index repair paths prune or suppress stale player/static lookup and occupancy remnants after partial teardown instead of leaking ghost actors into visibility/scope readers; ambiguous map-only collisions fail closed, while directory-only player/static collisions are suppressed from snapshot/scope and secondary lookup paths without deleting explicit cleanup handles. Player names now share a runtime-owned fail-closed validator before directory or map-index insertion, matching the account/login-ticket persistence boundary for whitespace, NUL, and UTF-8 rejection. Static-actor names now share one runtime-owned fail-closed validator across worldruntime, persistence, and minimal runtime paths; static-actor entity IDs are validated against the same
uint32visibility-VIDcarrier used by client-visible actor packets before runtime registration or snapshot restore. The next work is depth: richer lifecycle, better spawn policy, stronger transfer/reconnect edges, and long-running production behavior.
- The repo has topology, map indexing, AOI/radius-style visibility, player/session directories, entity registries, non-player directories, static actors, spawn groups, runtime scopes, and operator snapshots. Directory and map-index repair paths prune or suppress stale player/static lookup and occupancy remnants after partial teardown instead of leaking ghost actors into visibility/scope readers; ambiguous map-only collisions fail closed, while directory-only player/static collisions are suppressed from snapshot/scope and secondary lookup paths without deleting explicit cleanup handles. Player names now share a runtime-owned fail-closed validator before directory or map-index insertion, matching the account/login-ticket persistence boundary for whitespace, NUL, and UTF-8 rejection. Static-actor names now share one runtime-owned fail-closed validator across worldruntime, persistence, and minimal runtime paths; static-actor entity IDs are validated against the same
-
[~]M3 — Character, inventory, and item systems- Inventory/equipment bootstrap, carried item movement, counted split/merge, quickslot edits, consumable use,
ITEM_USE_TO_ITEMstack merging with count-only target refreshes, item dropping, ground visibility, pickup, merchant buy/sell, gold mutation, template-backed peer gold-markeranti_giverejection, and item/quickslot persistence slices exist. Full legacy item semantics are still not done: sockets, attributes, refine, anti-flag breadth, storage, trade, ownership timers, and compatibility-grade DB persistence remain future work.
- Inventory/equipment bootstrap, carried item movement, counted split/merge, quickslot edits, consumable use,
-
[~]M4 — NPCs, shops, static actors, and authored content- Static actors can be authored, inspected, imported/exported, and connected to
info,talk,warp, andshop_previewdefinitions. Shops have structured catalogs and first buy/sell behavior. Spawn groups can materialize stationary practice mobs with bootstrap combat profiles and reward descriptors, including canonical portablecombat_profilessnapshots that validation can register before spawn validation, and import previews now report exact added/removed/changed portable combat-profile rows plus per-map static-actor/spawn-group rows before replacement. The loopback runtime also exposes filtered spawn-group snapshots as a flat list, per-map occupancy subsets, per-player visibility subsets, and structured relocation preview/transfer visibility deltas for local QA. This is a useful content seam, not a complete quest/NPC/content scripting system.
- Static actors can be authored, inspected, imported/exported, and connected to
-
[~]M5 — Combat, mobs, death, restart, and rewards- Target selection, normal attack ingress, cadence gates, runtime HP, dead-state rejection, delayed respawn, aggro-lite engagement ownership, retaliation, player death floor, restart-here/restart-town bootstrap recovery, deterministic EXP/gold rewards, and fixed drop-vnum reward seams exist for practice mobs. Real combat formulas, skills, PvP, mob AI, chase/leash/return, loot tables, and full revive choreography are not compatibility-grade yet.
-
[~]M6 — Operations and developer workflow- The project has a Makefile, Dockerfile, CI, pprof/debug mux, health endpoint, local-only runtime-config/player/visibility/map/content endpoints, exact per-map occupancy lookup, static-actor respawn timer inspection, global and per-player spawn-group snapshot inspection, strict manifest-backed account-store and item-template backup validation, and development/testing/debugging docs. The runtime-config endpoint exposes the active bootstrap visibility/AOI policy (
whole_mapvsradius) so local QA can inspect daemon state without reading environment variables. It still needs release/versioning policy, production deployment docs, migrations, broader admin tooling, and multi-channel ops maturity.
- The project has a Makefile, Dockerfile, CI, pprof/debug mux, health endpoint, local-only runtime-config/player/visibility/map/content endpoints, exact per-map occupancy lookup, static-actor respawn timer inspection, global and per-player spawn-group snapshot inspection, strict manifest-backed account-store and item-template backup validation, and development/testing/debugging docs. The runtime-config endpoint exposes the active bootstrap visibility/AOI policy (
-
[ ]M7 — Legacy parity / production server- Not started as a claim. The current goal is to keep landing small verified compatibility slices until the server can support a narrow playable vertical, then broaden toward legacy-grade systems.
Status: [x] strong for a pre-alpha repo.
Already present:
- Go module with daemon entrypoints in
cmd/authdandcmd/gamed. - Clean
internal/*package boundaries for protocol, session flow, stores, world runtime, minimal integrated runtime, and ops. Makefiletargets for format, test, build, and Docker image builds.- GitHub Actions CI for formatting, tests, vet, daemon builds, and Docker builds.
- Development, workflow, testing, QA, debugging/profiling, and clean-room policy docs.
Still missing:
- release/versioning policy,
- production deployment guide outside the current lab environment,
- issue/contribution taxonomy,
- migration/backup/recovery workflow beyond the current local account-store and item-template backup/validation primitives.
Status: [x] owned for the current milestone, [~] incomplete for full legacy coverage.
Already present:
- frame envelope and stream handling,
- session phase model,
- control handshake, phase, ping/pong, and key exchange,
- auth/login/select/loading/game entry choreography,
- character delete/select/bootstrap updates,
- movement, sync, chat, whisper, notice, item, quickslot, interaction, shop, combat, and world packet families needed by current slices,
- packet docs in
spec/protocol/with a maintained index.
Still missing:
- many packet families outside the current verticals,
- deeper evidence for uncertain client behaviors,
- skill, quest, party/guild, messenger, trade/storage, player-shop, GM/admin, and broader world-event ownership.
Status: [x] bootstrap-compatible.
Already present:
authdandgamedsockets,- secure legacy handshake coverage,
- login ticket flow,
- account/character snapshot loading,
- selection and enter-game transitions,
- character delete in selection,
- tolerated client-version path during loading.
Still missing:
- real account database integration,
- production authentication policy,
- account/session security hardening beyond the current clean-room bootstrap,
- multi-channel selection/dispatch semantics.
Status: [~] real in-process runtime, not production world architecture.
Already present:
- connected session registry,
- player directory and map index, including tolerant player/static partial-teardown repair when one owned index is cleared before another and direct per-map or connected-player scope readers need to prune, rehome, or repair ghost buckets before returning occupancy/visibility; duplicate map-only repairs preserve remembered effective-map buckets before older stale source buckets when that effective-map memory survived primary-index loss,
- topology model,
- AOI/radius-style visibility boundaries,
- visibility rebuild helpers,
- local chat/move/sync peer fanout,
- map relocation and transfer bootstrap paths,
- reconnect/quit/logout cleanup,
- player list, map, visibility, transfer, and relocate operator views/actions.
Still missing:
- production channel/shard ownership,
- long-running resource/concurrency policy,
- richer sector behavior,
- robust multi-map content lifecycle,
- world-state persistence and crash recovery.
Status: [~] broad bootstrap coverage with many legacy details still pending.
Already present:
- carried inventory/equipment bootstrap replay,
- item set/delete/update refreshes, including selected-character
ITEM_SETprojection of the currently owned authored item flag metadata (refineable,save,stackable,sell_count_per_gold,slow_query,rare,unique,make_count,irremovable,confirm_when_use,quest_use,quest_use_multiple,log, andapplicable), anti-flag metadata (anti_get, transfer/job/sex/empire guards, stack guard, and storage/shop metadata bitsanti_save,anti_pk_drop,anti_myshop, andanti_safebox), display socket/attribute metadata, and template-authoredhighlighthints while leaving unowned bits zero; partial-stack consumable, compatible merchant/drag-to-item/pickup merge, counted-drop, and compatible carriedITEM_MOVEmergeITEM_UPDATErefreshes preserve authored display sockets/attributes while updating only the count; incompatible occupied-destination carriedITEM_MOVEswaps now require both authored templates when an item-template snapshot is loaded, reject source/target live counts already above authoredmax_count, and project each resolved template's metadata into the swappedITEM_SETrefreshes, - item move, swap, split, and merge cases,
- fail-closed item-template validation for malformed equipment metadata, missing or null template collections, embedded-NUL template names, oversized merchant sell prices, and unguarded authored pickup rejection text, including snapshots that combine stackable item semantics with an authored
equip_slot, - stack compatibility checks and max-stack guards for current slices,
- locked source/target and duplicate occupancy rejection paths,
- template-backed equip/unequip point-effect application and removal guards, including signed equip-effect penalties, authored
irremovableequipment unequip rejection with template-authoredunequip_reject_messageinfo-chat feedback, fail-closed rejection when live equipment-source counts exceed authored templatemax_count, and visible appearance projection for equippedbody,weapon,head, andhairitems, - consumable item use, including template-authored signed
use_effect.point_deltavalues so authored negative consumables can emit self-only point decreases, optionaluse_effect.consume_countvalues so multi-count consumables decrement authored stack units instead of a runtime-hardcoded single item, optionaluse_effect.info_messagetext for the successful-use self-only info-chat placeholder, embedded-NUL rejection foruse_effect.message/use_effect.info_messagetext at item-template load time, and optionaluse_reject_messagetext for guarded direct-use rejection feedback that is emitted before any hypothetical point delta from the rejected item would be applied; signed point overflow/underflow, overdrawn consume-count requests, andconfirm_when_use,quest_use,quest_use_multiple, orapplicableconsumables fail closed, ITEM_USE_TO_ITEMstack merge behavior,- quickslot add/delete/swap persistence, including rejection of item quickslot bindings to malformed or locked carried items, type-scoped retarget cleanup when an item/skill/command tuple is rebound to a new bar position, type-none clear hardening that rejects non-zero stale payload bytes, and fail-closed file-backed snapshot validation for duplicate non-item skill/command tuple bindings,
- quickslot cleanup and retargeting when item mutations remove, move, or fully merge a source slot, including the bootstrap
/inventory_movecompatibility seam, while stale/reclaimed item-drop sockets remain self-local and cannot register bootstrap ground handles or overwrite the fresh authoritative item/quickslot snapshot, - basic persisted account/character snapshots, including fail-closed validation for malformed carried/equipped item instances, duplicate per-character item instance IDs, duplicate equipped-slot occupancy, and duplicate carried-slot occupancy on account save and login-ticket issue/load before new snapshots or login tickets become authoritative.
Still missing:
- full item-type behavior,
- sockets, attributes, refine, metin stones, bonus changers, books/scroll families,
- complete anti-flag/class/sex/level/equipment restrictions,
- storage/safebox/mall,
- player trade/exchange and player shops,
- compatibility-grade database persistence.
Status: [~] useful first vertical, not a real economy yet.
Already present:
- carried item drop and counted drop with template-authored transfer/selected-character guards, including
anti_stack, template-authored drop-rejection info messages for transfer guards and authored selected-character restrictions, plus fail-closed deterministic ground-VIDcollision guards before item or persistence mutation, - temporary ground handles,
- ground-item visibility to peers in scope,
ITEM_GROUND_ADDencoding now uses the TMP4-compatible client-facing payload orderx/y/z/vid/vnum,- pickup into inventory with stack-merge behavior, valid authored equipment-template pickup as carried items rather than auto-equip, and guarded template-authored pickup-rejection info text for transfer/selected-character restriction failures,
- first owner-delivery/notice shape for pickups,
- merchant preview/catalog/open/close/buy/sell slices, including
GC::SHOP STARTcatalog entries backed by template-authored display sockets/attributes, refresh-only merchant-buy success bursts shared by packetSHOP BUYand the local/shop_buydebug harness, template-authoredshop_sell_pricesell-back credit before derived price/tax fallback, template-authoredanti_getplus selected-character buy rejection info text, template-authoredanti_sellfallback/info text plus authoredanti_get/anti_drop/anti_give/anti_stacksell rejection text, selected-character sell rejection info text, malformed carried-item, over-template-max, sell-credit carrier overflow, and resulting-gold carrier overflow fail-closed sell-back rejection, and partial-stack sellITEM_UPDATErefreshes that preserve authored display sockets/attributes, - gold mutation for current merchant and reward cases.
Still missing:
- durable ground ownership timers,
- party ownership rules based on real party state,
- drop permission transitions,
- complete merchant edge cases and shop variants,
- NPC-driven service breadth,
- real economy balancing and persistence.
Status: [~] authored bootstrap content seam.
Already present:
- static actor store,
- interaction definition store,
info,talk,warp, andshop_previewinteraction kinds, with static-actor runtime validation now rejecting unsupported interaction kinds before they can be registered or updated and dead visible interactables now failing closed with a self-only info-chat response instead of resolving authored content,- fail-closed static-actor visibility identity validation:
entity_idmust fit the currentuint32client-visibleVIDcarrier andrace_nummust fit the currentuint16CHARACTER_ADDprojection, - structured merchant catalogs,
- content bundle import/export with commit-only live visibility replay for static-actor replacement, fail-closed rejection of unsupported future interaction kinds in static-actor metadata, and import-preview deltas that include top-level counts, per-map static actor/spawn-group rows, portable combat-profile rows, grouped reward-drop rows, NPC service route rows, and authored warp destination rows,
- loopback-only local endpoints for static actors, interactions, visibility, and content bundles, including Unicode-safe compact summaries with interaction-definition previews, shop catalog details, warp destination details, and import-preview deltas for portable combat-profile snapshots plus grouped reward-drop and warp-destination changes,
- example bootstrap NPC service bundle.
Still missing:
- quest runtime,
- scripted triggers/results,
- richer NPC service kinds,
- live content reload policy,
- content validation tooling beyond current store/bundle checks,
- compatibility-grade regen/drop table ingestion.
Status: [~] first PvE loop exists around practice mobs.
Already present:
- non-player entity directory,
- static/non-player combat profiles,
- target selection, client-originated silent target clear, normal attack packet ingress, and game-flow rejection of unsupported attack types before runtime mutation,
- selected-target snapshot/version checks,
- loopback combat-target introspection for selected-target ownership and HP debugging,
- no-op content-bundle reimports and failed replacement rollbacks that preserve live practice-mob HP, selected-target ownership, combat snapshot/respawn state, and visibility state when authored content has not committed,
- HP mutation and HP percent refreshes,
- self and visible-peer
DAMAGE_INFOhit-effect emission for standalone bootstrap combat-profile and spawn-backed practice-mob non-lethal hits, while broader hit-result policy remains narrow, - dead-state rejection and target clear,
- delayed respawn rebuild path with combat-profile respawn delays validated before they are converted into runtime timers,
- engagement ownership to prevent noisy multi-owner combat loops,
- retaliation ticks against the engaged player for built-in and registered spawn-group combat profiles,
- player death floor with denial gates for several live actions, including self
DEADreplay when enteringGAMEfrom an already-0-HP selected-character snapshot, - restart-here and restart-town slash-command recovery seams,
- deterministic EXP/gold/fixed-drop reward descriptors for accepted non-player deaths,
- extensive TCP-level regression tests around watcher/owner respawn, retarget, cleanup, and reward cases.
Still missing:
- real damage formulas,
- attack animations/types beyond the first normal path,
- skill combat,
- PvP and duel policy,
- mob AI: aggro radius, chase, leash, return, patrol, target switching,
- broad loot/drop tables,
- full death/revive/corpse/menu evidence.
Status: [~] chat works, social systems are bootstrap-only.
Already present:
- local talking chat fanout,
- exact-name whisper routing,
- shout/party/guild bootstrap fanout,
- server notices and info messages,
- some dead-player denial behavior for selected paths.
Still missing:
- party membership state,
- party invite/leave/kick/roles,
- party EXP/drop sharing,
- guild roster/ranks/wars/notices,
- friend/messenger/block systems,
- moderation and permission model.
Status: [~] enough for bootstrap slices, not legacy-grade.
Already present:
- file-backed account/login-ticket snapshots,
- persisted selected character state used by current boot flow,
- persisted position for selected slices,
- persisted inventory/equipment/quickslots/gold for current item and merchant paths,
- item/static/interaction stores and bundle import/export.
Still missing:
- real database-backed schema,
- migrations,
- domain repositories for gameplay systems,
- crash recovery policy,
- backup/restore,
- persistent party/guild/quest/world state.
Status: [~] good lab/debug surface, not production ops.
Already present:
- pprof/debug mux,
/healthz,- local runtime config endpoint,
- local account/login-ticket/item-template validation endpoints, account/item-template backup/restore endpoints, and local crash-temp cleanup endpoints for account and item-template stores,
- local inventory/equipment/currency snapshots,
- local static actor and interaction authoring endpoints,
- local content bundle import/export,
- local notice, relocate, transfer, players, visibility, and maps endpoints,
- Docker runtime and debug image targets.
Still missing:
- authentication/authorization for production admin surfaces,
- release packaging,
- deployment guide,
- metrics/logging policy,
- backup/restore and migration runbooks,
- admin/GM tooling beyond local debug endpoints.
cmd/authd/cmd/gamed— daemon entrypoints.internal/proto/*— owned packet codecs, fixtures, and wire contracts.internal/auth,internal/authboot,internal/boot,internal/handshake,internal/login,internal/worldentry,internal/game— connection/session/auth/select/game flow.internal/service— legacy TCP service runtime and secure session wiring.internal/config— environment-driven daemon configuration.internal/worldruntime— topology, maps, AOI/visibility, entities, sessions, combat-oriented static actor state, and runtime scopes.internal/minimal— current integrated game runtime used by tests and daemons.internal/player,internal/inventory,internal/itemstore— current character, inventory, item template, equipment, quickslot, and currency behavior.internal/accountstore,internal/loginticket— bootstrap persistence stores.internal/staticstore,internal/interactionstore,internal/contentbundle— authored content, static actors, interactions, merchant previews, and bundle import/export.internal/ops— local debug/pprof/operator HTTP mux.db/migrations— placeholder for future database migration work.docs/— engineering notes, QA, roadmaps, workflow, development, and clean-room docs.spec/protocol/— owned protocol contracts and packet inventory.
Run the main checks:
make test
go vet ./...
git diff --checkRun the daemons locally:
go run ./cmd/authd
go run ./cmd/gamedDefault legacy listener addresses are documented in docs/development.md. The important defaults are:
authd::11002gamed::13000- pprof/debug/local ops:
127.0.0.1:6061forauthd,127.0.0.1:6060forgamed; wildcard/non-loopback ops binds are rejected at startup because/local/*operator endpoints share this mux
Useful docs:
- Development guide
- Debugging and profiling
- Manual client QA checklist
- Testing strategy
- Workflow
- Clean-room policy
- Protocol index
- Current project assessment
- Master roadmap
The next challenge is no longer proving that the target client can talk to a clean-room Go server. The next challenge is turning the owned slices into a coherent game loop.
Near-term priorities:
- PvE vertical depth — move practice mobs toward real spawned mobs with authored combat profiles, AI, chase/leash/return, broader rewards, and stable death/restart behavior.
- Item and economy parity — finish item-use families, anti-flags, ownership timers, pickup rules, shop variants, and persistence edges.
- World runtime hardening — make AOI, transfer, reconnect, respawn, static/non-player lifecycle, and visibility replay more robust under multiple clients.
- Content and quest seams — grow NPC services, content validation, spawn/regen/drop data, and the first quest-style state machine.
- Real social systems — replace bootstrap party/guild fanout with membership, permissions, persistence, and gameplay effects.
- Persistence and production ops — introduce DB migrations, backup/restore, release/deploy workflow, observability, and safe admin tooling.
This repository must only contain code, documentation, fixtures, and tests produced for this project.
Do not copy legacy Metin2 server/client source into this repository. Use legacy behavior only as an external oracle for independently written specs, tests, and Go implementations.