A Nimony port of an upstream open-source, GPLv3-licensed Minecraft server
implementation written in Rust. This project translates its crates to
Nimony, keeping the same module boundaries, so it can eventually serve as
the server counterpart to the Minecraft voxel client protocol code
already living in the Jester modpack work (aoughwl.mcnet and friends,
under jester/artifacts/release-src/minecraft/Mods/). That existing code
is client-only and assumes an external server is authoritative; this repo
is the missing server half.
This is a derivative work under the GPLv3 - see LICENSE and NOTICE.md
for the full terms and attribution. A local clone of the upstream source
(not vendored into this repo) is kept as a porting reference; see
NOTICE.md for where to find it.
Smallest/most self-contained crates first, since later crates depend on them:
-
nbt(~3.1k LOC) — NBT tag model, (de)serialization. In progress. Gzip-compressed NBT (nbt_compress.rs→src/nbt/nbt_compress.nim) is now wired to the../compresslibrary and compiles clean, with a round-trip test insrc/nbt/gziptest.nim- but not proven at runtime here:compressbindslibz.so.1by that literal Linux name, which doesn't resolve on this Windows dev machine, and a deeper ABI mismatch surfaced even after working around the name. Expected to work on the project's real Linux deployment target; re-verify there. -
util(~15.6k LOC) — In progress. Ported so far:math/vertical_surface_type.rs,resource_location.rs,resource.rs,identifier.rs,difficulty.rs,gamemode.rs,y_offset.rs,math/vector2.rs,math/vector3.rs(core - see its doc comment for what's skipped),random/legacy_rand.rs(the pre-1.13 Java-Random- compatible LCG),random/xoroshiro128.rs(the modern PRNG),random/worldgen_random.rs(JavaWorldgenRandombit-source wrapper over Xoroshiro),noise/mod.rs(Gradienttype + gradient table),math/mod.rs'slerp/lerp2/lerp3/smoothstep(assrc/util/mathlerp.nim- as concrete float32/float64 overloads, not Rust's single genericlerp<T: Float>: a[T: SomeFloat]Nimony generic hits a real compiler bug resolving-onTinside its own body, filed as feedback, worked around with duplication),noise/perlin.rs's corePerlinNoisepoint-sampling (src/util/perlin.nim-get/sample_and_lerponly;add_to_volume's batched fast path,SmearedPerlinNoise,NoiseStack, andNormalNoise's octave-sum/ normalization machinery are NOT ported yet, see the file's doc comment) →src/util/*.nim. legacy_rand/xoroshiro128/worldgen_random are all verified byte-for-byte against the Rust files' own unit test vectors (worldgen_random's specifically check against real vanilla Minecraft chunk/decorator seeds and draws) - seesrc/util/legacy_randtest.nim,xoroshiro128test.nim,worldgen_randomtest.nim.perlin.nimhas NO upstream test vectors to check against (perlin.rs has no#[cfg(test)]block) -perlintest.nimchecks permutation-table validity, a cross-computed HALF_ROUND_OFF bit-pattern constant, and sampling determinism/plausible range, but this is NOT yet verified bit-for-bit against vanilla world-gen output; treat it as "probably right, not yet proven" until real known-answer vectors are found or derived.identifier.rs's const/compile-time constructors (from_static,parse_static, ...) and allserde(de)serialize impls across the crate are intentionally skipped for now (see doc comments). ~6k of ~15.6k LOC covered;text/mod.rs(2.2k),noise/simplex.rs+volume.rs(1k - simplex noise and the DensityVolume batching type),mod.rs'sRandomImpl/RandomGeneratortrait/enum unification (a real design decision, deferred - each generator is a standalone concrete type for now),gaussian.rs's trait form (each generator has its own inlinednextGaussianinstead), and the remainingmath/files (position.rs,bounds.rs,block_box.rs,boundingbox.rs,int_provider.rs,float_provider.rs,bit_storage.rs,atomic_f32.rs,pool.rs,euler_angle.rs,experience.rs,mod.rs) remain. -
auth(~587 LOC) — Core JWT verification done.jwt/mod.rs's claims model, error kinds, base64 decoding,build_public_key_from_b64,decode_header_get_x5u, the ES384 self-signed verify path (verify_oidc_token_self_signed), and player-claims extraction (including the MD5-based xuid→UUID derivation) are all ported tosrc/auth/jwt.nimand wired to a real P-384 ECDSA implementation - a new standalone sibling library at../jwt(built specifically to unblock this; see its own README for what's implemented and how it's verified), pulled in via this repo'snimony.paths. Proven end-to-end against a real ES384 token insrc/auth/jwttest.nim(nimony c -rit to check). Still not ported: the Mojang-root-of-trust chain walk (needs a security-critical root-key constant sourced properly, not guessed), RS256 (needs RSA, not yet in../jwt), and the issuer-checked/JWKS- fetching OIDC path (needs both RS256 and an HTTP client).client.rs→src/auth/client.nimis now wired to therequestslibrary (../requests, curl-impersonate-based) vianimony.paths, but is currently blocked from compiling:requests's ownprofiles.nimhits a Nimony const-eval limitation unrelated to this port (aseqliteral inside aconst arrayof objects) - not fixed here since that sibling repo wasn't in scope. See doc comments insrc/auth/jwt.nim/src/auth/client.nim. -
scheduler(~662 LOC) — Data model done, driver deferred.domain.rs,error.rs, and the pure-data parts oftask.rs/scheduler.rs(SchedulerTaskId,TaskContext,SchedulerConfig,SchedulerState,SchedulerSnapshot) are ported tosrc/scheduler/*.nim. NOT ported:backend.rs'sTaskExecutortrait,task.rs'sTaskFuture/TaskWork/TaskRequest/TaskHandle(+ itsimpl Future),scheduler.rs'sSchedulerServicetrait, and all ofglobal.rs(326 LOC, the actual admission/poll driver) — these arePin<Box<dyn Future>>/tokio-executor plumbing with no Nimony equivalent (Nimony's async ispassiveprocs + continuations, not poll-based futures). Needs a real design pass once the main server crate (the crate that drives the server tick loop) clarifies what should drive scheduler turns. Seesrc/scheduler/lib.nim's doc comment. 4a.api-macros(~152 LOC) — Not ported; by design, documented instead. This is a Rust proc-macro crate (#[plugin_method],#[plugin_impl],#[with_runtime]) that runs as compiler codegen, not runtime code, so there is no Nimony module for it to become. Its semantics (async-plugin-method wrapping,#[no_mangle]plugin-ABI statics, tokioblock_onbridging) are written up insrc/api_macros/README.mdfor whoever builds the plugin host later - the honest Nimony replacement is a plain template/generic wrapper called by plugin authors plus a small codegen script, not a compiler plugin, once the main server crate's plugin-loading side gives it a concrete target. 4b.macros(~1.15k LOC) — Not ported; documented instead (src/macros/README.md). Single-file proc-macro crate, entirely compiler codegen: anEventderive +cancellable/send_cancellable[_blocking](event-bus dispatch boilerplate),packet/java_packet/block[_from_tag](registration-constant codegen),PacketWrite/PacketRead/PacketReadSlicederives (field-by-field protocol serialize/deserialize codegen), andtranslate_cross!/translate_java!(compile-time translation-key validation). ThePacketWrite/PacketReadderive logic is flagged as the reference spec for whoever hand-ports individualsrc/protocol/packet types next, since Nimony has no derive macros and each packet's serialize/ deserialize will be hand-written the waysrc/nbt/tag.nim's is. -
config(~2.2k LOC) — Mostly done for plain-data configs. Ported:lighting.rs,fun.rs,recipe.rs,advancement.rs,player_data.rs,pvp.rs,logging.rs,whitelist.rs,chunk.rs,world.rs,server_links.rs,telemetry.rs,chat.rs,resource_pack.rs, andnetworking/{lan_broadcast,compression,proxy, packet_limiter,query,rcon}.rs→src/config/*.nim(networking ones undersrc/config/networking/). All type-check clean.UuidandSocketAddrare represented by small local stub types (whitelist.nim,networking/netaddr.nim) since neither is ported anywhere yet. Skipped:op.rs/networking/auth.rs(needutil::PermissionLvl/ProfileAction, not yet ported),networking/{java,bedrock}.rsandnetworking/mod.rs'sNetworkingConfigaggregate (depend on java/bedrock above),plugins.rs, and top-levellib.rs(458 LOC — the actual config-file load/save/merge driver; needs a design pass, not a line-by-line port). -
gametest(~1.9k LOC) — Small ported slice, rest blocked.error.rs→src/gametest/error.nim(full port) andmodel.rs→src/gametest/model.nim(full port,serde/serde_jsonskipped;environment: Valuebecomes a raw JSON-textstringplaceholder).model.nimneededRotationfrom data'sblock_rotation.rs; rather than block on all of data,src/gametest/rotation_stub.nimhand-copies just the 4-way enum + itsthencombinator (see its header for the TODO to de-duplicate once data is ported for real). NOT ported:world.rs(an#[async_trait]interface overutil::math::position::BlockPos/world::world:: BlockFlags/data::BlockStateId, none of which exist in this port yet), andhelper.rs,manager.rs(391 LOC),runner/*(459 LOC),structure/placement.rs(389 LOC),structure/template.rs(223 LOC),block_based/test.rs— all either depend on the above or are async-trait/state-machine driven or the tick loop shape mentioned underschedulerabove. Seesrc/gametest/lib.nim's doc comment. -
codecs/protocol(~40k LOC combined) — In progress.lifecycle.rs,number.rs(JSON interop skipped), and the coreDataResult<R>type fromdata_result.rs(its macro-generatedapply2..applyNapplicative family skipped, no Nimony equivalent) →src/codecs/*.nim. NOT ported:map_like.rs,dynamic_ops.rs,json_ops.rs,codec/*,struct_builder.rs,list_builder.rs— a generic Codec/DynamicOps framework built on Rust trait objects and GATs with no direct Nimony equivalent; deferred until a concrete consumer forces the design (e.g.nbt'snbt_ops.rs, itself stubbed pending this).protocol:codec/var_int.rs+codec/var_uint.rs+codec/var_long.rs/var_ulong.rs→src/protocol/varint.nim(VarInt,VarUInt,VarLong,VarULong), plussrc/protocol/protobase.nim(ReadingError/WritingError/result types, port of the non-serde-trait parts ofser/mod.rs). Same Java-plain-LEB128-vs-Bedrock-ZigZag split assrc/nbt/serializer.nim/deserializer.nim, verified byte-for-byte againstvar_int.rs's own#[cfg(test)]vectors insrc/protocol/varinttest.nim(round-trip acrossi32::MIN/-2/-1/0/1/2/ i32::MAXfor both shapes, boundary values, and both overflow-rejection tests). Asyncdecode_async/encode_asyncvariants (tokio-specific) are not ported. Follow-up pass added the shared codec/framing layer:ser/mod.rs'sNetworkReadExt/NetworkWriteExttraits →src/protocol/netcodec.nim(NetReader/NetWriter, one concrete pair over an ownedseq[byte]instead of Rust's generic-over-Read/Writetrait, matchingsrc/nbt/'s reader/writer shape) — numeric BE reads/writes, bool, var_int/uint/long viavarint.nim, bounded strings (with a from-scratch UTF-16-length bound check decoding UTF-8 by hand, including surrogate pairs),getOption/getListgenerics, UUID as a raw (hi, lo)u64pair (noUuidtype ported anywhere yet).codec/bit_set.rs→src/protocol/bitset.nim, verified byte-for-byte against its own#[cfg(test)]vectors insrc/protocol/bitsettest.nim(all 4 tests, including the exact encoded-byte assertion and the negative-length rejection).codec/uuid.rs→src/protocol/uuid.nim(thin re-export, see above).lib.rs'sConnectionState,IdOr<T>,RawPacket→src/protocol/proto.nim. Skipped:StreamDecryptor/StreamEncryptor(AES-128-CFB8 login encryption over async I/O - tokio-specific, no Nimony equivalent yet) and theClientPacket/ServerPackettraits (Nimony has no traits; concrete packet types will expose their own read/write procs directly).netcodectest.nimround-trips every numeric type, bools, a var_int, and a UTF-8 string with multi-byte/surrogate-pair characters through the new writer/reader - passing. First concrete packet types: 8 Java login/config-state packets (both directions where upstream has both) →src/protocol/loginpackets.nim-CSetCompression,CLoginDisconnect,SKeepAlive(config-state),SConfigPong,CFinishConfig/SAcknowledgeFinishConfig(empty-body),CLoginCookieRequest,SLoginCookieResponse,SLoginPluginResponse. Picked for being small and NOT multi-version-branching (upstream'sJavaMinecraftVersion-gated field differences aren't reproduced yet - these are ported against the current/latest wire shape only; a real multi-version story needspumpkin_util::versionported first). Each exposes its ownwritePacketData/readPacketDataprocs directly instead of upstream'sClientPacket/ServerPackettraits, the same shapesrc/nbt/tag.nim's serialize/deserialize use. All 8 verified with real round-trip encode→decode→compare tests insrc/protocol/loginpacketstest.nim(run vianimony c -r, not just checked) - passing. NOT started:data_component.rs(2.9k),item_stack_seralizer.rs, and the much larger remaining packet surface (most play-state packets,java/client/config/registry_data.rsand other NBT/registry-heavy ones, all ofbedrock/). Follow-up pass added awriteList/writeListResgeneric tonetcodec.nim(the write-side counterpart to the existinggetList) and 4 more config-state packet types:registry_data.rs→src/protocol/configpackets.nim(CRegistryData-RegistryEntryDatamodeled as{entryId, hasData, data: seq[byte]}, the raw NBT blob written/read verbatim as upstream does; the reader's "has data" branch reads to end-of-frame since there's no length prefix on the blob itself, matching upstream's real one-entry-per-packet usage - documented in-file as not safe for a hypothetical multi-entry-with-data packet), andreset_chat.rs/feature_flags.rs/ping.rs→src/protocol/configpackets2.nim(CConfigResetChat,CFeatureFlags,CConfigPing). All with real round-trip tests (configpackets{,2}test.nim,nimony c -rpassing). Explicitly assessed and deferred:codec/item_stack_seralizer.rs(856 lines) - genuinely blocked, not just unstarted: it's built directly oncodec/data_component.rs's full ~2887-lineDataComponentregistry/codec (itself needspumpkin-data's generated component tables),JavaMinecraftVersionmulti-version branching (not ported), and a realItemStack.patch: Map<DataComponent, Option<Data>>model -src/inventory/itemstub.nim's placeholderItemStack(id+count only) isn't shaped for it. Revisit oncedata_component.rshas its own design pass. Still not started (~34k LOC):codec/data_component.rs(2.9k),codec/item_stack_seralizer.rs,serial/*,query.rs,rcon.rs, most of thejava/'s and all ofbedrock/'s several hundred individual packet types,packet_encoder.rs/packet_decoder.rs. Did not end up drawing on Jester's client-sideaoughwl.mcnet/netwire.nimetc. beyond confirming the same varint shape is the right one to match; worth a closer look by whoever portspacket_encoder.rs/packet_decoder.rsnext, since that's where Jester's client-side framing code is the more directly relevant reference. -
plugin-utils(~710 LOC) — Done except HTTP.models.rs,updater.rs,license.rs(lease read/write viastd/json, grace-period evaluation), andlib.rs's global-state glue →src/plugin_utils/*.nim.init(context)'s WASM-guest path is skipped (needs unportedplugin-api).http.rsis a call-shape stub only: Nimony's stdlib has no HTTP client or TLS at all, socheck_license_online/check_for_updatescompile and return a clear "not implemented" error rather than a fake success - needs libcurl/WinHTTP FFI eventually. -
host-bindings(~70 LOC) — Not portable yet; documented instead. The entire file is onewasmtime::component::bindgen!macro call over../plugin-wit/v0.1/*.wit(~14.2k lines of WIT across 60+ files) - there is no hand-written Rust logic to translate, and Nimony has no WASM component-model runtime or WIT-bindgen equivalent. Seesrc/host_bindings/README.mdfor the full explanation and the three real options once the plugin ABI is actually being built (wasmtime C API FFI, a custom WIT→Nimony generator, or dropping the WASM sandbox model for native-loaded plugins). 9b.plugin-runtime(~3.3k LOC) — Not portable yet; documented instead, except one small data type. wasmtimeStoremultiplexing/execution (executor.rs), cross-plugin re-entry tracking (chain.rs), an async admission policy over that (policy.rs), and a runtime-agnostic spawn trait (spawn.rs) are all tokio+wasmtime plumbing with no Nimony equivalent (no WASM runtime, and Nimony's async ispassiveprocs + continuations, not poll-based futures).lifecycle.rs'sDriverState/DriverError— the one piece that's plain data — is ported tosrc/plugin_runtime/lifecycle.nim; theLifecycle/DriverJointypes wrapping it in a tokiowatchchannel are not. Seesrc/plugin_runtime/README.mdfor the three replacement paths, including the option (worth real consideration) of skipping WASM sandboxing entirely and loading plugins natively the way the sibling Jester project'saowliinterpreter already does. 9c.plugin-api(~24k LOC) — Mostly not portable yet (WASM-guest SDK glue); one real data-model piece landed. Grepped every top-level andext/source file forwit::imports: onlypermissions.rshas none. Everything else -item.rs,block.rs,enchantment.rs,team.rs,mobs.rs,forms.rs,display.rs,persistent_data.rs,logging.rs,ai.rs,worldgen.rs,commands.rs,scheduler.rs,datapack.rs,inventory.rs,recipe.rs,lib.rs- is WASM-guest SDK code built directly on the samewasmtime::component::bindgen!-generatedwitmodule that blockshost-bindings/plugin-runtime.generated/block.rs(5.2k LOC) andgenerated/item.rs(6.7k LOC) are build-time-generated registry data, out of scope likedata. Ported:permissions.nim(plugin sandbox capability-string constants, pure data), andeventdata.nim- a real, transport-independentEventDataobject model for 8 of the ~230events/*types, built from the actual field shapes inpumpkin-plugin-wit'sevent.wit(the.rswrapper files themselves carry no fields).nimony checkclean, and its test (eventdatatest.nim) actually runs vianimony c -r- pure data, unaffected by the closure-through-vtable runtime crash. Seesrc/plugin_api/README.md. -
world,inventory,command(~14.1k LOC) — command: tokenizer layer AND the tree/dispatcher design pass done.errors/command_syntax_error.rs(simplified),context/string_range.rs,string_reader.rs, and its numeric parsing →src/command/*.nim. TheArgumentType<S>/CommandSourcetrait pair the rest of the crate builds on got its design decision:cmdsource.nimcollapsesCommandSource's generic-everywhere shape into one concrete manual-vtable ref object (no real Player/console type exists yet to make genericity pay for itself);argtype.nimports theArgumentType/AnyArgumentTypepair plus thecore/*.rsleaf types (bool/integer/long/float/double/string) via a closedArgValuevariant standing in forBox<dyn Any>;cmdtree.nimportsnode/{mod,tree}.rs(arena-of-nodes,Command/Requirementas closures, redirect-by-index for the simple case, no multi-source forking or ambiguity detection);cmddispatch.nimportsnode/dispatcher.rs's real backtracking parse algorithm. All five filesnimony checkclean, includingcmddispatchtest.nimwhich builds a sample "gamemode "/"spawn" tree - but that test only passesnimony check, notnimony c -r: full codegen currently crashes the compiler once real closures flow through the vtables at runtime (a lambda-lifting-stage internal AssertionDefect, distinct from - and not yet reduced/filed like - thefor..in-over-closure-seq bug that WAS found, minimally reproduced, fixed here, and filed as feedback:meetsRequirementsoriginally crashednimony checkitself this way). Treat the tree/dispatcher as semantically-checked, not proven at runtime, until that codegen crash is resolved. Seesrc/command/lib.nim's doc comment for the full breakdown. Follow-up pass ported the coordinate/misc leaf argument types:coordinates/mod.rs→src/command/coordinates.nim(WorldCoordinate/Coordinates, world/local parse+resolve, the local-coordinate forward/up/left rotation math);coordinates/block_pos.rs→blockposarg.nim(integral parse/resolve +is_valid_block_pos);coordinates/vec3.rs→vec3arg.nim;coordinates/angle.rs→angle.nim(Angle+wrap_degrees);uuid.rs→uuidarg.nim(plus a from-scratch UUID hex-string parser - none existed anywhere in this port,src/protocol/uuid.nimonly has the wire shape);gamemode.rs→gamemodearg.nim;identifier.rs→identifierarg.nim. uuid/gamemode/identifier wire into the existingArgValue/ArgumentTypevtable asavkStringholding validated text (a scope call documented in each file, not a fullArgValuewidening); coordinates/blockpos/ vec3/angle stay standalone parsers for now sinceCoordinates/Angledon't reduce to a singleArgValueprimitive.coordtest.nimverifies the coordinate/blockpos/vec3/angle logic against upstream's own documentedexamples()(parses "1 3 5", "-3 ~24-1", "^ ^9 ^56", "~", "0.1 -0.5 .9", integer-centering, "-5", etc.) but is check-only, NOT runtime-proven: importingcmdsource.nimforCommandSourceis enough to hit the closures-through-vtables crash, even thoughnewDummySource()never populates a real closure field - the 10th confirmation of this bug, and the clearest evidence yet that a closure-vtable TYPE merely being linked into the binary triggers it, not any instance actually holding or calling a closure.argvaluetest.nim(uuid/gamemode/identifier, which do touchargtype.nim) is likewise check-only. Skipped:hex_color.rs/team_color.rs(need the unportedTextComponentfor theirRGBColor/error-message types),rotation.rs/swizzle.rs/column_pos.rs/vec2.rs(straightforward extensions of thecoordinates.nimpattern, not yet done). NOT started: block/item/nbt/range/particle/structure argument types (all need real world/registry types),errors/error_types.rs, the SNBT parser, suggestions,argument_builder.rs(a fluent DSLcmdtree.nim's plain builder procs cover already), and real command execution (needs Player/World/Server from the still-mostly-unported main server crate). inventory: core interface + a couple of leaves ported tosrc/inventory/*.nim(6 files, allnimony checkclean):error.rs→invbase.nim,window_property.rs→window_property.nim,viewer.rs→viewer.nim,inventory/inventory.rs→inventory.nim(the coreInventory/Clearabletrait plus the NBT slot-array sync helpers),double.rs→double.nim.itemstub.nimis a placeholderItem/ItemStack(id + count only) standing in fordata::item/item_stack, which don't exist yet — every proc taking/returningItemStackhere will need revisiting oncedata's real item model lands. Nimony has no trait objects (single dispatch, nodyn Trait), soInventoryis a ref object holding a manual vtable of proc fields (each field/closure needs{.closure.}- a Nimony compiler-internal AssertionDefect surfaced when a vtable-field call wasn't marked, so every closure assigned to anInventoryfield is now explicit about it) rather than a trait; concrete inventories (simple/player/double) fill in the vtable. Since then, also ported (re-checked each file's real deps rather than trusting the blanket note below):slot.rs→slot.nim(theSlottrait, same manual-vtable pattern asInventory, plusNormalSlot/ArmorSlot; armor-type restriction (can_insert) is left accepting anything - needsdata'sItem/EquipmentSlottables),crafting/crafting_inventory.rs→crafting.nim(fully self-contained, ported clean),entity_equipment.rs→entity_equipment.nim(keyed by a placeholderEquipmentKindenum instead of the realEquipmentSlot).itemstub.nimgrewsplit/decrement/increment/getMaxStackSize/areItemsAndComponentsEqualto support these.crafting/recipes.rs'sRecipeInputInventorytrait-extension became a plainCraftingInventory{inv, width, height}wrapper rather than a trait hierarchy (Nimony's vtable-Inventoryhas no room to extend); itsrecipe_provider.rssibling stays unported (needspumpkin_protocol::codec::recipe/data::recipes).screen_handler.rs's design decision made (1.3k lines, ~30-method trait): same scoped-down manual-vtable approach assrc/server/block/blockbehaviour.nim's -ScreenHandleris a ref-object vtable coveringquickMove/onClosed(what a proof-case concrete handler needs), not the full click-routing surface (internal_on_slot_clickalone is 434 lines needingPlayer/World). Ported tosrc/inventory/screenhandler.nim:ScreenProperty(full, self-contained, using the existingPropertyDelegatevtable),ScreenHandlerBehaviour(plain struct: slots/syncId/windowType),addSlot/addPlayer{Hotbar,Inventory}Slots, andinsertItem- a full, faithful port of the 86-line slot-merge algorithm, since it's pure slot-index/count arithmetic with no Player/World dependency at all. Concrete proof case:beacon_screen_handler.rs→src/inventory/beaconhandler.nim(newBeaconScreenHandler, full quick-move logic).itemstub.nimgrewsetCount/isStackableto supportinsertItem.screenhandlertest.nimexercisesinsertItemdirectly (closure-free, pure algorithm - passes) and builds a beacon handler shape-check; building/using the handler itself hits the same closures-through-vtables runtime crash documented elsewhere in this port (4th independent confirmation, aftersrc/command/,src/server/entity/,src/server/block/) -nimony checkclean,nimony c -rcrashes ateraiser.nim'sParamsTagIdassertion. Documented, not hidden. Two more concrete handlers ported:lectern_screen_handler.rs→src/inventory/lectern.nim(single book slot, page-number property, button-click page navigation/take-book, via a newonButtonClickImplvtable field added toScreenHandlerandaddProperty/propertiesadded toScreenHandlerBehaviour- both were missing before this handler needed them).generic_container_screen_handler.rs→src/inventory/genericcontainer.nim(the shared grid-container shape behind chests/ender-chests/hoppers/dispensers/droppers/barrels/ crafters - 5 factory procs, one handler struct; upstream's ownquick_moveboundary math (rows * 9, notrows * columns) is preserved as-is, including its narrower-than-9 containers quirk, since that's upstream's real behavior, not a porting bug). Bothlecterntest.nim/genericcontainertest.nimverify the closure-free arithmetic/shape pieces for real (nimony c -rpasses); building either handler hits the closures-through-vtables runtime crash (NIMONY-COMPILER-BUGS.md #1) same as beacon's - confirmed again here, including onLecternController's vtable alone, independent ofScreenHandler. Still ~7.8k of ~11k LOC remain: the rest of the per-container*screen_handler.rsfiles (player/,brewing/,furnace_like/,enchanting/,anvil/,merchant/,mount/stonecutter_/smithing_table_/cartography_table_/grindstone_/loom_handlers),container_click.rs,drag_handler.rs,sync_handler.rs,gui_builder.rs, andinternal_on_slot_click's full click-routing logic — all need either the real item/registry types,Player/Worldtypes from the main server crate, or both. ExtendingScreenHandler's vtable as more handlers land is mechanical from here, perblockbehaviour.nim`'s precedent. -
world(~78.6k LOC) — Just started. The crate is dominated by world generation (noise/structure/feature placement undergeneration/, ~65k of the 78.6k LOC) which needs util's still-incomplete noise/random port and is out of scope for now. Ported instead, from the smaller top-level/tick/chunk-loading surface:cylindrical_chunk_iterator.rs→src/world/cylindrical.nim(view-distance cylinder membership + load/unload diffing; the LUT-basedall_chunks_withinfast path was replaced with a direct bounding-loop recompute, since its LUT lives in unported data - same semantics, no precomputed table),tick/mod.rs→src/world/tick.nim(TickPriority,ScheduledTick/OrderedTick, NBT (de)serialization - specialized to avalue: stringid rather than upstream's genericT: ToResourceLocation, since no block/fluid registry type exists yet; a localBlockPosstand-in covers forutil::math::position, also not ported yet), andtick/scheduler.rs→src/world/tickscheduler.nim(the ring-bufferChunkTickScheduler- de-async'd to a plain single-threaded version, no concurrency model exists yet for the tick loop; same string-specialization as tick.nim).dimension.rsis a two-line stub documenting why it's blocked (Level/data::dimension::Dimensionboth unported).chunk/palette.rs'sPalettedContainer<V, const DIM: usize>got its design pass: ported tosrc/world/palette.nimasPalettedContainerwithdimas a runtimeintfield (Nimony has no const-generic array types, and only two concrete shapes - 16 for blocks, 4 for biomes - are ever used, so forcing generality bought nothing) and the stored value type fixed to a concreteuint32(genericVhit real Nimony friction; both real uses - block-state id, biome id - are plain integers anyway). Dense/Indexed storage, palette dedup, and the swap-remove-on-zero-count shrink path are all ported;nimony c -rtested viapalettetest.nim(fill 4096 cells with 300 distinct values to force an Indexed→Dense upgrade, verify every cell, then refill to one value and verify the palette shrinks back to 1). That test caught a real bug: a first pass wrongly special-cased Dense storage to skip palette/counts bookkeeping, which silently broke the palette-shrink path once upgraded - fixed once the test failed to just report "compiles". Left out: the Bedrock-specificbedrock_palette_bits/bedrock_water_state/has_random_ticking_fluidhelpers (needBlockState/Fluid, not ported) and the on-disk bit-packed (de)serialization (ChunkSectionBlockStates/ChunkSectionBiomes,format/anvil.rs/format/linear.rs, ~3.5k lines) - a separate, more mechanical follow-up now that the in-memory shape is settled.biome/'s climate-parameter math is now ported:data's (generated)quantize_coord/unquantize_coord/Parameter/TargetPoint/ParameterPointplusbiome/multi_noise.rs'sto_longandbiome/mod.rs'shash_seed, all combined intosrc/world/biomeparam.nimsince none of the individual pieces are meaningful alone and all are pure, registry-free math.hash_seeduses thesha256already available via thejwtsibling repo's cross-repo import. Verified withbiomeparamtest.nim(nimony c -r) against upstream's ownhash_seed_testvectors (hash_seed(0) == 8794265229978523055,hash_seed(-777i64 as u64) == -1087248400229165450) plus Parameter-distance/ParameterPoint-fitness sanity checks — both pass. The rest ofbiome/(BiomeSupplier/MultiNoiseBiomeSupplier/ActiveBiomeSupplierinmod.rs, all ofposition_finder.rs,end.rs) needs the unported generatedBiomeTree/biome registry andMultiNoiseSampler(noise sampling -perlin.rs/simplex.rsare unverified even where present), so left for later.biomeparam.nimnow also hasBiomeEntry/nearestBiome, the generic (tree-shape-independent) half ofBiomeTree::get's nearest-fitness lookup, tested against synthetic entries inbiomeparamtest.nim. The REAL per-biomeParameterPointtable (upstream'sOVERWORLD_BIOME_SOURCE/NETHER_BIOME_SOURCE) is generated fromassets/multi_noise_biome_tree.json— 1.8MB, a recursive branch/leaf tree, not the flat listsrc/generated/biome.nimembeds (that file'stemperature/downfallare unrelated rain/snow/color-effect fields frombiome.json, not placement data — the placement preset JSON undermulti_noise_biome_source_parameter_list/is just{"preset": "minecraft:overworld"}). Parsing that tree and wiring it tonearestBiomeis a real, scoped follow-up, not attempted here.lighting/hits the same dead endblock/already documented insrc/server/block/README.md:lighting/storage.rs(the smallest file, 184 lines) already needschunk_system::{Chunk, Cache}, which doesn't exist yet even as a stub —engine.rs/runtime.rs(892/637 lines) are built on the same foundation, so not attempted.format/anvil.rs's on-disk region-file (.mca) layout is now ported at the format-math level:src/world/anvilformat.nimcovers the 8 KiB location/timestamp header (parse/build, with thesectorOffset < 2reserved-sector guard), region/chunk-index math (verified against the floor-division trap negative chunk coordinates hit ->>on a signed int is arithmetic/floor-rounding in both Rust and Nimony, so this had to be checked explicitly, not just trusted), per-chunk sector-count accounting, and the length-prefixed/compression-tagged payload framing. Deliberately NOT ported: the actual gzip/zlib/LZ4 (de)compression bodies (same gap asnbt_compress.nim- no zlib/gzip module in Nimony's stdlib) and the async file I/O / mutex-guarded in-place sector allocator (AnvilChunkFile::write/write_indices- no concurrency model chosen yet, same gap flagged for the scheduler).anvilformattest.nimround-trips real data through every piece (nimony c -r, all pass) using uncompressed payloads to exercise real framing without the missing compression bodies;format/linear.rs(the alternate Linear region format, ~778 lines) not attempted.poi/mod.rs's in-memory model and NBT (de)serialization are ported atsrc/world/poi.nim:PoiEntry/PoiRegion(add/remove/getAll, per-chunk grouping viagetChunkData) andbuildChunkNbt/parseChunkNbt(the NBT-building/parsing halves of upstream'scompress_chunk_data/decompress_chunk_data- the zlib-wrapping half is deferred, same gap asanvilformat.nim's compression bodies; once resolved it covers both).save/load's on-disk MCA read/write (~250 lines) not attempted, since it needs that same zlib plus real file I/O.poitest.nimround-trips real entries through grouping and NBT build/parse (nimony c -r, all pass).chunk_system/(~5.4k lines:dag.rs,chunk_holder.rs,chunk_state.rs,schedule.rs, etc.) is a generational-arena (slotmap) task-scheduling system for chunk generation stages - same shape as the already-deferred scheduler crate (no concurrency model chosen, and it needs its own arena/slotmap equivalent besides). Assessed and left blocked, not attempted.chunk/mod.rs's in-memory chunk-storage data model is now ported atsrc/world/chunkdata.nim:ChunkHeightmaps(the real 9-bit-per-column, 7-columns-per-i64-word packing fromset/get, not a placeholder) andChunkSections(an array ofPalettedContainers - one 16³ block section + one 4³ biome section per vertical slice - replacing upstream'sRwLock<Box<[BlockPalette]>>/RwLock<Box<[BiomePalette]>>with plain fields, since no concurrency model exists yet, same punt as everywhere else).getBlock/setBlockresolve a world-space (x,y,z) to the right section + local cell.chunkdatatest.nimround-trips heightmaps across a full 16×16 column grid (catching any shift/mask/ word-boundary bug) and blocks across 4 points spanning sections 0 through 23 of a -64..319 range (nimony c -r, both pass) - this also surfaced and fixed a bug in the TEST itself, not the implementation (sectionIndex(s, minY + 319)evaluates tosectionIndex(255), not 319 - worth remembering when spot-checking with derived constants). The fullChunkData(light engine, blending data, NBT (de)serialization, tick-scheduler wiring) stays out of scope - it needs the still-unported light engine and the zlib gap already documented foranvilformat.nim/poi.nim.level.rs/world.rs's async chunk-loading pipeline (ticket system, load/unload scheduling) remains blocked on the same concurrency-model decision aschunk_system//scheduler - a real design pass, not attempted here. Its chunk-lookup HALF is done though:src/world/worldchunks.nim'sWorldChunksis exactly thechunkdata.nim+ column-index shape flagged above - aseq[(ChunkPos, ChunkSections)]linear-scan registry (matchingsrc/server/playerregistry.nim's andsrc/server/world/worldstub.nim's own "plain seq, no fake concurrency" convention, deliberately not avar Table[K,V]out-parameter given the crash cataloged for that shape inNIMONY-COMPILER-BUGS.md#12) withisChunkLoaded/loadChunk/unloadChunk/getBlockState/setBlockState. Kept additive alongside (not merged into)src/server/world/worldstub.nim's existing flat-per-sectionWorldtype, which already has live consumers acrosssrc/server/block//src/server/item//src/command/- a future pass can wireWorldChunksin once there's a concrete multi-chunk consumer.worldchunkstest.nimgenuinely runtime-proven (nimony c -r, noentity.nimin its import graph): auto-load-on-write, per-column isolation, negative-coordinate correctness (arithmetic shift-right floors two's-complement negatives, verified not assumed), and unload-removes-exactly-the-target-column all pass. Not started:block/, the rest ofbiome/(registry-dependent parts),chunk/io/,level.rs/world.rs's async pipeline (chunk-lookup done above), the rest oflighting/,world_info/. -
the main server crate (~269k LOC) — Assessed; tiny slice ported, full map written. Ported
error.rs's a shared error trait as overloaded procs →src/server/errorclass.nim(overInventoryError/ReadingError;PlayerDataErrorpending that type's port). Everything else is mapped insrc/server/README.mdby module (entity/83.6k,block/47.6k,plugin/47.4k,command/30.3k,net/19.5k,world/18.7k,item/6.9k,data/6.2k,server/3.4k,enchantment/1.9k) with three concrete blockers identified (unporteddataregistries, no tick-loop/concurrency design yet, andentity/'s scale needing a realEntity-shape design pass before wide porting). Since then:enchantment/- 5 files ported tosrc/server/enchantment/(theLevelBasedValueformula type + 4 pure value-transform effects; ~15 remaining effect files needWorld/Player/Entitystand-ins, not yet stubbed).block/- assessed in detail, confirmed genuinely blocked even at its simplest: tried the two smallest conceivable block impls (an 8-line empty one, a 20-line one-method one) and both need the#[pumpkin_block]registration macro (unportable, seesrc/macros/README.md) and a workingEntityabstraction before any block can port, not just complex ones. One free-standing type (PathComputationType) ported tosrc/server/block/blockmisc.nim; full writeup with the concrete unblock order insrc/server/block/README.md. See both READMEs before starting more work here. 12b.item/(~6.9k LOC, part of the upstream main server crate) — Core interface/registry ported; concrete items blocked.ItemBehaviour/ItemMetadata/ItemRegistry/BlockActionResult/Handported tosrc/server/item/itembehaviour.nim(manual-vtable pattern, same asblock//entity/). No#[pumpkin_item]macro exists here - items self-register via plainmanager.register(...)calls, unlikeblock/. All ~53 concrete item files needPlayer.inventoryand/or aWorldtype (pickup, sound, entity spawning), neither of which exist yet - tried the two smallest-looking candidates (dye.rs,egg.rs) and both hit this wall. Seesrc/server/item/README.mdfor the exact unblock path. 12c.net/(~19.5k LOC, part of the upstream main server crate) — Pure-data slice ofmod.rsported; everything else blocked on the connection-state-machine design anddata.GameProfile/PropertyValue/ProfileAction/ChatMode/Hand/PlayerConfig(+defaultPlayerConfig),PacketHandlerResult,EncryptionErrorKind,MaxPendingBytes/decrementPendingBytes,isValidPlayerName, andofflineUuid(real SHA-256 math via the siblingjwtlibrary, same source ashash_seedinsrc/world/biomeparam.nim) ported tosrc/server/net/netbase.nim.isValidPlayerNameis verified against every one of upstream's own#[cfg(test)]vectors innetbasetest.nim(nimony c -r, all pass) - caught a real bug this way: an off-by-one excluding the space byte itself from the control-character range, only surfaced by the "name containing a space" test vector.PlayerUuidis a(hi, lo): (uint64, uint64)tuple, matchingsrc/protocol/uuid.nim's existing wire-shape convention. NOT ported:ClientPlatform/JavaClient/BedrockClient/PendingConnection(allArc<..>-shared, tokio-task-driven - same concurrency-model gap asscheduler/plugin-runtime), the hundreds of individualjava/play/*.rs/bedrock/play/*.rspacket handlers (needPlayer/World/Server), andhandle_handshake(java/handshake.rs, 51 lines - close to portable, but readsCURRENT_MC_VERSION/translation-key constants from the still-unporteddataregistry and callsutil::text's unportedTextComponent). Seesrc/server/net/README.mdfor the full breakdown and suggested next steps (authentication.rs,chat/mod.rs). -
data(~1.5M LOC — almost entirely generated block/item/registry tables) — Generator path confirmed viable; proof-of-concept done. The generator (the upstream data-codegen tool, ~36.8k lines across ~90 small per-domain submodules driven by onemain.rs) reads JSON already checked into the upstream repo'sassets/(92MB, confirmed present - not a blocker). Rust builds each submodule's output as aproc_macro2::TokenStreamviaquote!; Nimony has no equivalent (and doesn't need one) since a generator can just build its output as a plain string andwriteFileit directly - simpler than the Rust approach, not a gap.std/jsonis present in Nimony's stdlib (unlike gzip/crypto/HTTP, confirmed missing elsewhere in this port) and sufficient.src/data_codegen/gen_sound_category.nimis a complete, run-and-verified port of the smallest submodule (JSON in -> generated, type-checkedsrc/generated/sound_category.nimout, values verified correct) establishing the pattern. The other ~89 submodules (scoreboard_slot.rstrivial, up throughblock.rs/item.rs/biome.rs/recipes.rs/noise_router.rswhich encode real structural complexity) each need the same file-by-file treatment - seesrc/data_codegen/README.mdfor the full breakdown, two open design questions (output layout, whethermain.rs's WIT-generation side duties are in scope), and a note that these submodules are independent of each other and would parallelize well across multiple forks. -
pumpkinmain crate'splugin/module (~38.9k LOC) - Small tractable slice ported, bulk confirmed WASM-bridge-specific.plugin/loader/wasm/ wasm_host/wit/v0_1/**(~35k+ of the total) iswasmtime::component:: bindgen!-generated WIT host bindings, the same unportable category assrc/host_bindings/README.md. Ported:permissions.nim(permission string constants + descriptions) andnative.nim(LoaderError,PLUGIN_API_VERSION,canLoad/canUnload's platform-extension logic - NOT the actual dylib load, which needs Nimony's dynlib FFI investigated and a chosen async model).plugintest.nimhas noentity.nimdependency and actually runs (nimony c -r), proving the ported logic for real.plugin/mod.rs(1336 lines, the real plugin manager/event-dispatch core) and the ~230plugin/api/events/*files (native counterpart tosrc/plugin_api/eventdata.nim's already-ported 8 event types) are documented but not ported this pass - seesrc/server/plugin/README.mdfor the full module map and next steps. -
pumpkinmain crate'scommand/implementation directory (~30.3k LOC, the concrete/gamemode//say/... handlers - distinct from the already-portedpumpkin-commandtokenizer/tree/dispatcher library atsrc/command/) - one command ported as proof./gamemode's self-target form is fully wired end to end (tree registration → argument parse →Player.gamemodemutation) insrc/server/command/, required adding aplayer*: nil Playerfield toCommandSource(src/command/cmdsource.nim) so command handlers can reach the issuing player.gamemodetest.nimdrives it through a real dispatch walk; like everything importingentity.nim, it's check-verified only (hits NIMONY-COMPILER-BUGS.md bug #1 atnimony c -r, same signature). Seesrc/server/command/README.mdfor what's next (the ~30 remaining command files, several trivial).
src/
nbt/
tag.nim -- NbtTag variant type + core ops (port of tag.rs)
compound.nim -- NbtCompound (TODO)
serializer.nim -- writer (TODO)
deserializer.nim -- reader (TODO)
Each .nim file's doc comment names the upstream .rs file it ports from.