├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── codeql-analysis.yml │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── bot ├── basic │ ├── basic.go │ ├── cookie.go │ ├── events.go │ ├── info.go │ ├── keepalive.go │ ├── ping.go │ ├── settings.go │ └── tags.go ├── client.go ├── configuration.go ├── event.go ├── example_test.go ├── ingame.go ├── login.go ├── mcbot.go ├── msg │ ├── chat.go │ └── events.go ├── pinglist.go ├── playerlist │ └── playerlist.go ├── screen │ ├── chest.go │ ├── events.go │ ├── inventory.go │ └── screen.go └── world │ ├── chunks.go │ └── events.go ├── chat ├── clickevent.go ├── decoration.go ├── hoverevent.go ├── jsonmessage.go ├── jsonmessage_test.go ├── message.go ├── nbtmessage.go ├── nbtmessage_test.go └── sign │ ├── cache.go │ ├── cache_test.go │ ├── session.go │ └── sign.go ├── data ├── README.md ├── entity │ ├── entity.go │ └── gen_entity.go ├── inventory │ └── inventory.go ├── item │ ├── gen_item.go │ └── item.go ├── lang │ ├── af-za │ │ └── af_za.go │ ├── ar-sa │ │ └── ar_sa.go │ ├── ast-es │ │ └── ast_es.go │ ├── az-az │ │ └── az_az.go │ ├── ba-ru │ │ └── ba_ru.go │ ├── bar │ │ └── bar.go │ ├── be-by │ │ └── be_by.go │ ├── bg-bg │ │ └── bg_bg.go │ ├── br-fr │ │ └── br_fr.go │ ├── brb │ │ └── brb.go │ ├── bs-ba │ │ └── bs_ba.go │ ├── ca-es │ │ └── ca_es.go │ ├── cs-cz │ │ └── cs_cz.go │ ├── cy-gb │ │ └── cy_gb.go │ ├── da-dk │ │ └── da_dk.go │ ├── de-at │ │ └── de_at.go │ ├── de-ch │ │ └── de_ch.go │ ├── de-de │ │ └── de_de.go │ ├── el-gr │ │ └── el_gr.go │ ├── en-au │ │ └── en_au.go │ ├── en-ca │ │ └── en_ca.go │ ├── en-gb │ │ └── en_gb.go │ ├── en-nz │ │ └── en_nz.go │ ├── en-pt │ │ └── en_pt.go │ ├── en-ud │ │ └── en_ud.go │ ├── en-us │ │ └── en_us.go │ ├── enp │ │ └── enp.go │ ├── enws │ │ └── enws.go │ ├── eo-uy │ │ └── eo_uy.go │ ├── es-ar │ │ └── es_ar.go │ ├── es-cl │ │ └── es_cl.go │ ├── es-ec │ │ └── es_ec.go │ ├── es-es │ │ └── es_es.go │ ├── es-mx │ │ └── es_mx.go │ ├── es-uy │ │ └── es_uy.go │ ├── es-ve │ │ └── es_ve.go │ ├── esan │ │ └── esan.go │ ├── et-ee │ │ └── et_ee.go │ ├── eu-es │ │ └── eu_es.go │ ├── fa-ir │ │ └── fa_ir.go │ ├── fi-fi │ │ └── fi_fi.go │ ├── fil-ph │ │ └── fil_ph.go │ ├── fo-fo │ │ └── fo_fo.go │ ├── fr-ca │ │ └── fr_ca.go │ ├── fr-fr │ │ └── fr_fr.go │ ├── fra-de │ │ └── fra_de.go │ ├── fur-it │ │ └── fur_it.go │ ├── fy-nl │ │ └── fy_nl.go │ ├── ga-ie │ │ └── ga_ie.go │ ├── gd-gb │ │ └── gd_gb.go │ ├── gen_lang.go │ ├── gl-es │ │ └── gl_es.go │ ├── got-de │ │ └── got_de.go │ ├── gv-im │ │ └── gv_im.go │ ├── haw-us │ │ └── haw_us.go │ ├── he-il │ │ └── he_il.go │ ├── hi-in │ │ └── hi_in.go │ ├── hr-hr │ │ └── hr_hr.go │ ├── hu-hu │ │ └── hu_hu.go │ ├── hy-am │ │ └── hy_am.go │ ├── id-id │ │ └── id_id.go │ ├── ig-ng │ │ └── ig_ng.go │ ├── io-en │ │ └── io_en.go │ ├── is-is │ │ └── is_is.go │ ├── isv │ │ └── isv.go │ ├── it-it │ │ └── it_it.go │ ├── ja-jp │ │ └── ja_jp.go │ ├── jbo-en │ │ └── jbo_en.go │ ├── ka-ge │ │ └── ka_ge.go │ ├── kab-kab │ │ └── kab_kab.go │ ├── kk-kz │ │ └── kk_kz.go │ ├── kn-in │ │ └── kn_in.go │ ├── ko-kr │ │ └── ko_kr.go │ ├── ksh │ │ └── ksh.go │ ├── kw-gb │ │ └── kw_gb.go │ ├── la-la │ │ └── la_la.go │ ├── lb-lu │ │ └── lb_lu.go │ ├── li-li │ │ └── li_li.go │ ├── lmo │ │ └── lmo.go │ ├── lo-la │ │ └── lo_la.go │ ├── lol-us │ │ └── lol_us.go │ ├── lt-lt │ │ └── lt_lt.go │ ├── lv-lv │ │ └── lv_lv.go │ ├── lzh │ │ └── lzh.go │ ├── mi-nz │ │ └── mi_nz.go │ ├── mk-mk │ │ └── mk_mk.go │ ├── mn-mn │ │ └── mn_mn.go │ ├── moh-ca │ │ └── moh_ca.go │ ├── ms-my │ │ └── ms_my.go │ ├── mt-mt │ │ └── mt_mt.go │ ├── nah │ │ └── nah.go │ ├── nds-de │ │ └── nds_de.go │ ├── nl-be │ │ └── nl_be.go │ ├── nl-nl │ │ └── nl_nl.go │ ├── nn-no │ │ └── nn_no.go │ ├── no-no │ │ └── no_no.go │ ├── nuk │ │ └── nuk.go │ ├── oc-fr │ │ └── oc_fr.go │ ├── oj-ca │ │ └── oj_ca.go │ ├── ovd │ │ └── ovd.go │ ├── pl-pl │ │ └── pl_pl.go │ ├── pt-br │ │ └── pt_br.go │ ├── pt-pt │ │ └── pt_pt.go │ ├── qya-aa │ │ └── qya_aa.go │ ├── ro-ro │ │ └── ro_ro.go │ ├── rpr │ │ └── rpr.go │ ├── ru-ru │ │ └── ru_ru.go │ ├── ry-ua │ │ └── ry_ua.go │ ├── sah-sah │ │ └── sah_sah.go │ ├── scn │ │ └── scn.go │ ├── se-no │ │ └── se_no.go │ ├── sk-sk │ │ └── sk_sk.go │ ├── sl-si │ │ └── sl_si.go │ ├── so-so │ │ └── so_so.go │ ├── sq-al │ │ └── sq_al.go │ ├── sr-cs │ │ └── sr_cs.go │ ├── sr-sp │ │ └── sr_sp.go │ ├── sv-se │ │ └── sv_se.go │ ├── swg │ │ └── swg.go │ ├── sxu │ │ └── sxu.go │ ├── szl │ │ └── szl.go │ ├── ta-in │ │ └── ta_in.go │ ├── th-th │ │ └── th_th.go │ ├── tl-ph │ │ └── tl_ph.go │ ├── tlh-aa │ │ └── tlh_aa.go │ ├── tok │ │ └── tok.go │ ├── tr-tr │ │ └── tr_tr.go │ ├── tt-ru │ │ └── tt_ru.go │ ├── tzl-tzl │ │ └── tzl_tzl.go │ ├── uk-ua │ │ └── uk_ua.go │ ├── val-es │ │ └── val_es.go │ ├── vec-it │ │ └── vec_it.go │ ├── vi-vn │ │ └── vi_vn.go │ ├── vp-vl │ │ └── vp_vl.go │ ├── yi-de │ │ └── yi_de.go │ ├── yo-ng │ │ └── yo_ng.go │ ├── zh-cn │ │ └── zh_cn.go │ ├── zh-hk │ │ └── zh_hk.go │ ├── zh-tw │ │ └── zh_tw.go │ └── zlm-arab │ │ └── zlm_arab.go ├── packetid │ ├── clientboundpacketid_string.go │ ├── generator │ │ └── GenPacketId.java │ ├── legacy.go │ ├── packetid.go │ └── serverboundpacketid_string.go ├── registryid │ ├── activity.go │ ├── armormaterial.go │ ├── attribute.go │ ├── block.go │ ├── blockentitytype.go │ ├── blockpredicatetype.go │ ├── blocktype.go │ ├── bootstrap │ │ └── builtinregistries.go │ ├── catvariant.go │ ├── chunkstatus.go │ ├── commandargumenttype.go │ ├── creativemodetab.go │ ├── customstat.go │ ├── datacomponenttype.go │ ├── decoratedpotpattern.go │ ├── enchantmenteffectcomponenttype.go │ ├── enchantmententityeffecttype.go │ ├── enchantmentlevelbasedvaluetype.go │ ├── enchantmentlocationbasedeffecttype.go │ ├── enchantmentprovidertype.go │ ├── enchantmentvalueeffecttype.go │ ├── entitysubpredicatetype.go │ ├── entitytype.go │ ├── floatprovidertype.go │ ├── fluid.go │ ├── frogvariant.go │ ├── gameevent.go │ ├── generator │ │ ├── generate.go │ │ ├── registries.json │ │ └── template.go.tmpl │ ├── heightprovidertype.go │ ├── instrument.go │ ├── intprovidertype.go │ ├── item.go │ ├── itemsubpredicatetype.go │ ├── lootconditiontype.go │ ├── lootfunctiontype.go │ ├── lootnbtprovidertype.go │ ├── lootnumberprovidertype.go │ ├── lootpoolentrytype.go │ ├── lootscoreprovidertype.go │ ├── mapdecorationtype.go │ ├── memorymoduletype.go │ ├── menu.go │ ├── mobeffect.go │ ├── numberformattype.go │ ├── particletype.go │ ├── pointofinteresttype.go │ ├── positionsourcetype.go │ ├── posruletest.go │ ├── potion.go │ ├── recipeserializer.go │ ├── recipetype.go │ ├── ruleblockentitymodifier.go │ ├── ruletest.go │ ├── schedule.go │ ├── sensortype.go │ ├── soundevent.go │ ├── stattype.go │ ├── triggertype.go │ ├── villagerprofession.go │ ├── villagertype.go │ ├── worldgen_biomesource.go │ ├── worldgen_blockstateprovidertype.go │ ├── worldgen_carver.go │ ├── worldgen_chunkgenerator.go │ ├── worldgen_densityfunctiontype.go │ ├── worldgen_feature.go │ ├── worldgen_featuresizetype.go │ ├── worldgen_foliageplacertype.go │ ├── worldgen_materialcondition.go │ ├── worldgen_materialrule.go │ ├── worldgen_placementmodifiertype.go │ ├── worldgen_poolaliasbinding.go │ ├── worldgen_rootplacertype.go │ ├── worldgen_structurepiece.go │ ├── worldgen_structureplacement.go │ ├── worldgen_structurepoolelement.go │ ├── worldgen_structureprocessor.go │ ├── worldgen_structuretype.go │ ├── worldgen_treedecoratortype.go │ └── worldgen_trunkplacertype.go └── soundid │ ├── gen_soundid.go │ └── soundid.go ├── examples ├── autofish │ └── autofish.go ├── daze │ └── daze.go ├── mcadump │ └── mcadump.go ├── mcping │ ├── README.md │ └── mcping.go ├── minimal │ └── minimal.go ├── playerdataconvert │ ├── advancements.go │ ├── main.go │ ├── pets.go │ ├── playerdata.go │ └── utils.go └── pressureTest │ └── main.go ├── go.mod ├── go.sum ├── internal └── generateutils │ └── utils.go ├── level ├── biome │ └── list.go ├── bitstorage.go ├── bitstorage_test.go ├── block │ ├── block.go │ ├── block_entities.nbt │ ├── block_states.nbt │ ├── blockentities.go │ ├── blockentity.go │ ├── blocks.go │ ├── blocks.nbt │ ├── generator │ │ ├── GenBlocks.java │ │ ├── blockentities │ │ │ ├── blockentities.go.tmpl │ │ │ └── main.go │ │ ├── blocks │ │ │ ├── blocks.go.tmpl │ │ │ └── main.go │ │ └── properties │ │ │ ├── main.go │ │ │ └── properties_enum.go.tmpl │ ├── properties.go │ ├── properties_enum.go │ └── utilfuncs.go ├── chunk.go ├── chunkstatus.go ├── component │ ├── attributemodifiers.go │ ├── blockentitydata.go │ ├── bucketentitydata.go │ ├── bundlecontents.go │ ├── canbreak.go │ ├── canplaceon.go │ ├── chargedprojectiles.go │ ├── components.go │ ├── components_test.go │ ├── creativeslotlock.go │ ├── customdata.go │ ├── custommodeldata.go │ ├── customname.go │ ├── damage.go │ ├── debugstickstate.go │ ├── dyedcolor.go │ ├── enchantmentglintoverride.go │ ├── enchantments.go │ ├── entitydata.go │ ├── fireresistant.go │ ├── food.go │ ├── hideadditionaltooltio.go │ ├── hidetooltip.go │ ├── instrument.go │ ├── intangibleprojectile.go │ ├── itemname.go │ ├── jukeboxplayable.go │ ├── lodestonetracker.go │ ├── lore.go │ ├── mapcolor.go │ ├── mapdecorations.go │ ├── mapid.go │ ├── mappostprogressing.go │ ├── maxdamage.go │ ├── maxstacksize.go │ ├── ominousbottleamplifier.go │ ├── potioncontents.go │ ├── rarity.go │ ├── recipes.go │ ├── repaircost.go │ ├── storedenchantments.go │ ├── suspicioussteweffects.go │ ├── tool.go │ ├── trim.go │ ├── unbreakable.go │ └── writablebookcontent.go ├── palette.go └── palette_test.go ├── nbt ├── README.md ├── decode.go ├── decode_test.go ├── dynbt │ ├── bigTest_test.snbt │ ├── decode.go │ ├── decode_test.go │ ├── encode.go │ ├── encode_test.go │ ├── types.go │ └── update.go ├── encode.go ├── encode_test.go ├── example_test.go ├── interface.go ├── nbt.go ├── rawmsg.go ├── rawmsg_test.go ├── snbt.go ├── snbt_decode.go ├── snbt_decode_test.go ├── snbt_encode_test.go ├── snbt_scanner.go ├── snbt_scanner_test.go ├── special_test.go ├── testdata │ ├── 1-dimension_codec.snbt │ ├── 58f6356e-b30c-4811-8bfc-d72a9ee99e73.dat.snbt │ ├── bigTest_test.snbt │ └── level.dat.snbt └── typeinfo.go ├── net ├── CFB8 │ ├── cfb8.go │ └── cfb8_test.go ├── conn.go ├── interface.go ├── packet │ ├── builder.go │ ├── joingame_test.bin │ ├── packet.go │ ├── packet_test.go │ ├── types.go │ ├── types_test.go │ ├── util.go │ └── util_test.go ├── queue │ └── queue.go ├── rcon.go └── rcon_test.go ├── offline ├── uuid.go └── uuid_test.go ├── realms ├── invite.go ├── mco.go ├── realms.go ├── realms_test.go └── server.go ├── registry ├── codec.go ├── network.go └── registry.go ├── save ├── chunk.go ├── chunk_test.go ├── dimension.go ├── level.go ├── level_test.go ├── playerdata.go ├── playerdata_test.go ├── region │ ├── mca.go │ └── mca_test.go └── testdata │ ├── DIM-1 │ └── data │ │ └── raids.dat │ ├── DIM1 │ └── data │ │ └── raids_end.dat │ ├── advancements │ └── 58f6356e-b30c-4811-8bfc-d72a9ee99e73.json │ ├── data │ └── raids.dat │ ├── entities │ ├── r.-1.-1.mca │ ├── r.-1.0.mca │ ├── r.0.-1.mca │ └── r.0.0.mca │ ├── icon.png │ ├── level.dat │ ├── level.dat_old │ ├── playerdata │ ├── 58f6356e-b30c-4811-8bfc-d72a9ee99e73.dat │ └── 58f6356e-b30c-4811-8bfc-d72a9ee99e73.dat_old │ ├── poi │ ├── r.-1.-1.mca │ ├── r.0.-1.mca │ └── r.0.0.mca │ ├── region │ ├── r.-1.-1.mca │ ├── r.-1.0.mca │ ├── r.0.-1.mca │ └── r.0.0.mca │ ├── session.lock │ └── stats │ └── 58f6356e-b30c-4811-8bfc-d72a9ee99e73.json ├── server ├── README.md ├── auth │ ├── auth.go │ └── auth_test.go ├── client.go ├── command │ ├── builders.go │ ├── command.go │ ├── command_test.go │ ├── component.go │ ├── parsers.go │ └── serialize.go ├── configuration.go ├── entity.go ├── gameplay.go ├── handshake.go ├── internal │ └── bvh │ │ ├── bound.go │ │ ├── bound_test.go │ │ ├── bvh.go │ │ ├── bvh_test.go │ │ └── vector.go ├── keepalive.go ├── login.go ├── ping.go ├── playerlist.go └── server.go └── yggdrasil ├── authenticate.go ├── refresh.go ├── signout.go ├── user ├── property.go ├── pubkey.go ├── user.go ├── validator.go └── yggdrasil_session_pubkey.der ├── validate.go ├── yggdrasil.go └── yggdrasil_test.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report something wrong in this library 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | 1. Go Version: 15 | 2. GoMC Version: 16 | 3. Code: 17 | 18 | ```go 19 | package main 20 | 21 | // import "github.com/Tnze/go-mc/xxxxx" 22 | 23 | func main() { 24 | 25 | } 26 | ``` 27 | 28 | **Expected output** 29 | A clear and concise description of what you expected to happen. 30 | 31 | ``` 32 | THE OUTPUT OF PREVIOUS CODE 33 | ``` 34 | 35 | **Current output** 36 | If applicable, add screenshots to help explain your problem. 37 | 38 | **Additional context** 39 | Add any other context about the problem here. 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | Example codes: 17 | 18 | ```go 19 | func main() { 20 | 21 | } 22 | ``` 23 | 24 | **Describe alternatives you've considered** 25 | A clear and concise description of any alternative solutions or features you've considered. 26 | 27 | **Additional context** 28 | Add any other context or screenshots about the feature request here. 29 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '40 2 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'go' ] 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v4 36 | 37 | # Initializes the CodeQL tools for scanning. 38 | - name: Initialize CodeQL 39 | uses: github/codeql-action/init@v2 40 | with: 41 | languages: ${{ matrix.language }} 42 | 43 | - name: Autobuild 44 | uses: github/codeql-action/autobuild@v2 45 | 46 | # ℹ️ Command-line programs to run using the OS shell. 47 | # 📚 https://git.io/JvXDl 48 | 49 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 50 | # and modify them (or add more) to build your code if your project 51 | # uses a compiled language 52 | 53 | #- run: | 54 | # make bootstrap 55 | # make release 56 | 57 | - name: Perform CodeQL Analysis 58 | uses: github/codeql-action/analyze@v2 59 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [ push, pull_request ] 3 | jobs: 4 | 5 | test: 6 | name: Test 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | # match the minimum supported and the latest Go versions 13 | go_version: [ '1.22', '^1.22' ] 14 | 15 | steps: 16 | 17 | - name: Check out code into the Go module directory 18 | uses: actions/checkout@v4 19 | 20 | - name: Setup Go 21 | uses: actions/setup-go@v4 22 | with: 23 | go-version: ${{ matrix.go_version }} 24 | check-latest: true 25 | 26 | - name: Test 27 | run: go test ./... 28 | 29 | - run: mkdir -p ./bin/tools 30 | 31 | - name: Build tools 32 | run: go build -o ./bin/tools ./examples/... 33 | 34 | - name: Upload tools 35 | if: ${{ startsWith(matrix.go_version, '^') }} 36 | uses: actions/upload-artifact@v3 37 | with: 38 | name: tools 39 | path: ./bin/tools 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # MacOS 9 | .DS_Store 10 | 11 | # Test binary, build with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | examples/test/test.go 18 | examples/genmaps/genmaps 19 | examples/genmaps/*.png 20 | data/packetid/generate.go 21 | .idea/ 22 | .vscode/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tnze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go-MC 2 | 3 | ![Version](https://img.shields.io/badge/Minecraft-1.21-blue.svg) 4 | [![Go Reference](https://pkg.go.dev/badge/github.com/Tnze/go-mc.svg)](https://pkg.go.dev/github.com/Tnze/go-mc) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/Tnze/go-mc)](https://goreportcard.com/report/github.com/Tnze/go-mc) 6 | [![Discord](https://img.shields.io/discord/915805561138860063?label=Discord)](https://discord.gg/A4qh8BT8Ue) 7 | 8 | ### [教程 · Tutorial](https://go-mc.github.io/tutorial/) 9 | ### [文档 · Documents](https://pkg.go.dev/github.com/Tnze/go-mc) 10 | 11 | Require Go version: 1.22 12 | 13 | There's some library in Go support you to create your Minecraft client or server. 14 | 这是一些Golang库,用于帮助你编写自己的Minecraft客户端或服务器。 15 | 16 | - [x] 👍 Minecraft network protocol 17 | - [x] 👍 Robot framework 18 | - [x] 👍 Server framework 19 | - [x] 👍 Dual role RCON protocol (Server & Client) 20 | - [x] 👍 Chat Message (Support both Json and old `§` format) 21 | - [x] 👍 NBT (Based on reflection) 22 | - [x] 👌 SNBT ⇋ NBT 23 | - [x] 👍 Regions & Chunks & Blocks 24 | - [x] ⌛ Yggdrasil (Mojang login) 25 | - [x] ⌛ Realms Server 26 | 27 | > We don't promise that API is 100% backward compatible. 28 | 29 | ## Getting start 30 | 31 | Go-MC tag the old version after new version released. For example, 32 | if *1.19.4* is the latest Minecraft version, the newest go-mc tag will be *v1.19.3*. 33 | To get the latest Go-MC that support *1.19.4*, usually you must use `go get -u github.com/Tnze/go-mc@master`. 34 | Special cases are version like *1.19*, the Go-MC support it is tagged `v1.19.0` to avoid automatically upgrade. 35 | 36 | Examples: 37 | To get the latest version: `go get github.com/Tnze/go-mc@master` 38 | To get old versions (e.g. 1.18.2): `go get github.com/Tnze/go-mc@v1.18.2` 39 | To get the first of each primary version: `go get github.com/Tnze/go-mc@v1.19.0` 40 | 41 | ### Run Examples 42 | 43 | - Run `go run github.com/Tnze/go-mc/examples/mcping localhost` to ping and list the localhost mc server. 44 | - Run `go run github.com/Tnze/go-mc/examples/daze` to join the local server at *localhost:25565* as player named Daze on the offline mode. 45 | -------------------------------------------------------------------------------- /bot/basic/cookie.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/Tnze/go-mc/data/packetid" 5 | pk "github.com/Tnze/go-mc/net/packet" 6 | ) 7 | 8 | func (p *Player) handleCookieRequestPacket(packet pk.Packet) error { 9 | var key pk.Identifier 10 | err := packet.Scan(&key) 11 | if err != nil { 12 | return Error{err} 13 | } 14 | cookieContent := p.c.Cookies[string(key)] 15 | err = p.c.Conn.WritePacket(pk.Marshal( 16 | packetid.ServerboundCookieResponse, 17 | key, pk.OptionEncoder[pk.ByteArray]{ 18 | Has: cookieContent != nil, 19 | Val: pk.ByteArray(cookieContent), 20 | }, 21 | )) 22 | if err != nil { 23 | return Error{err} 24 | } 25 | return nil 26 | } 27 | 28 | func (p *Player) handleStoreCookiePacket(packet pk.Packet) error { 29 | var key pk.Identifier 30 | var payload pk.ByteArray 31 | err := packet.Scan(&key, &payload) 32 | if err != nil { 33 | return Error{err} 34 | } 35 | p.c.Cookies[string(key)] = []byte(payload) 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /bot/basic/keepalive.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Tnze/go-mc/data/packetid" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | const keepAliveDuration = time.Second * 20 11 | 12 | func (p *Player) resetKeepAliveDeadline() { 13 | newDeadline := time.Now().Add(keepAliveDuration) 14 | p.c.Conn.Socket.SetDeadline(newDeadline) 15 | } 16 | 17 | func (p *Player) handleKeepAlivePacket(packet pk.Packet) error { 18 | var KeepAliveID pk.Long 19 | if err := packet.Scan(&KeepAliveID); err != nil { 20 | return Error{err} 21 | } 22 | 23 | p.resetKeepAliveDeadline() 24 | 25 | // Response 26 | err := p.c.Conn.WritePacket(pk.Packet{ 27 | ID: int32(packetid.ServerboundKeepAlive), 28 | Data: packet.Data, 29 | }) 30 | if err != nil { 31 | return Error{err} 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /bot/basic/ping.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "github.com/Tnze/go-mc/data/packetid" 5 | pk "github.com/Tnze/go-mc/net/packet" 6 | ) 7 | 8 | func (p *Player) handlePingPacket(packet pk.Packet) error { 9 | var pingID pk.Int 10 | if err := packet.Scan(&pingID); err != nil { 11 | return Error{err} 12 | } 13 | 14 | // Response 15 | err := p.c.Conn.WritePacket(pk.Packet{ 16 | ID: int32(packetid.ServerboundPong), 17 | Data: packet.Data, 18 | }) 19 | if err != nil { 20 | return Error{err} 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /bot/basic/settings.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | // Settings of client 4 | type Settings struct { 5 | Locale string // 地区 6 | ViewDistance int // 视距 7 | ChatMode int // 聊天模式 8 | ChatColors bool // 聊天颜色 9 | DisplayedSkinParts uint8 // 皮肤显示 10 | MainHand int // 主手 11 | 12 | // Enables filtering of text on signs and written book titles. 13 | // Currently, always false (i.e. the filtering is disabled) 14 | EnableTextFiltering bool 15 | AllowListing bool 16 | 17 | // The brand string presented to the server. 18 | Brand string 19 | } 20 | 21 | // Used by Settings.DisplayedSkinParts. 22 | // For each bit set if shows match part. 23 | const ( 24 | _ = 1 << iota 25 | Jacket 26 | LeftSleeve 27 | RightSleeve 28 | LeftPantsLeg 29 | RightPantsLeg 30 | Hat 31 | ) 32 | 33 | // DefaultSettings are the default settings of client 34 | var DefaultSettings = Settings{ 35 | Locale: "zh_CN", // ^_^ 36 | ViewDistance: 15, 37 | ChatMode: 0, 38 | DisplayedSkinParts: Jacket | LeftSleeve | RightSleeve | LeftPantsLeg | RightPantsLeg | Hat, 39 | MainHand: 1, 40 | 41 | EnableTextFiltering: false, 42 | AllowListing: true, 43 | 44 | Brand: "vanilla", 45 | } 46 | -------------------------------------------------------------------------------- /bot/basic/tags.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | func (p *Player) handleUpdateTags(packet pk.Packet) error { 11 | r := bytes.NewReader(packet.Data) 12 | 13 | var length pk.VarInt 14 | _, err := length.ReadFrom(r) 15 | if err != nil { 16 | return Error{err} 17 | } 18 | 19 | var registryID pk.Identifier 20 | for i := 0; i < int(length); i++ { 21 | _, err = registryID.ReadFrom(r) 22 | if err != nil { 23 | return Error{err} 24 | } 25 | 26 | registry := p.c.Registries.Registry(string(registryID)) 27 | if registry == nil { 28 | return Error{errors.New("unknown registry: " + string(registryID))} 29 | } 30 | 31 | _, err = registry.ReadTagsFrom(r) 32 | if err != nil { 33 | return Error{err} 34 | } 35 | } 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /bot/event.go: -------------------------------------------------------------------------------- 1 | package bot 2 | 3 | import ( 4 | "sort" 5 | "strconv" 6 | 7 | "github.com/Tnze/go-mc/data/packetid" 8 | pk "github.com/Tnze/go-mc/net/packet" 9 | ) 10 | 11 | type Events struct { 12 | generic []PacketHandler // for every packet 13 | handlers [][]PacketHandler // for specific packet id only 14 | } 15 | 16 | func (e *Events) AddListener(listeners ...PacketHandler) { 17 | for _, l := range listeners { 18 | // panic if l.ID is invalid 19 | if l.ID < 0 || int(l.ID) >= len(e.handlers) { 20 | panic("Invalid packet ID (" + strconv.Itoa(int(l.ID)) + ")") 21 | } 22 | if s := e.handlers[l.ID]; s == nil { 23 | e.handlers[l.ID] = []PacketHandler{l} 24 | } else { 25 | e.handlers[l.ID] = append(s, l) 26 | sortPacketHandlers(e.handlers[l.ID]) 27 | } 28 | } 29 | } 30 | 31 | // AddGeneric adds listeners like AddListener, but the packet ID is ignored. 32 | // Generic listener is always called before specific packet listener. 33 | func (e *Events) AddGeneric(listeners ...PacketHandler) { 34 | e.generic = append(e.generic, listeners...) 35 | sortPacketHandlers(e.generic) 36 | } 37 | 38 | type ( 39 | PacketHandlerFunc func(p pk.Packet) error 40 | PacketHandler struct { 41 | ID packetid.ClientboundPacketID 42 | Priority int 43 | F func(p pk.Packet) error 44 | } 45 | ) 46 | 47 | func sortPacketHandlers(slice []PacketHandler) { 48 | sort.SliceStable(slice, func(i, j int) bool { 49 | return slice[i].Priority > slice[j].Priority 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /bot/example_test.go: -------------------------------------------------------------------------------- 1 | package bot 2 | 3 | import ( 4 | "encoding/hex" 5 | "log" 6 | 7 | "github.com/Tnze/go-mc/offline" 8 | "github.com/Tnze/go-mc/yggdrasil" 9 | ) 10 | 11 | func ExamplePingAndList() { 12 | resp, delay, err := PingAndList("localhost:25565") 13 | if err != nil { 14 | log.Fatalf("ping and list server fail: %v", err) 15 | } 16 | 17 | log.Println("Status:", string(resp)) 18 | log.Println("Delay:", delay) 19 | } 20 | 21 | func ExampleClient_JoinServer_offline() { 22 | c := NewClient() 23 | c.Auth.Name = "Tnze" // set its name before login. 24 | 25 | id := offline.NameToUUID(c.Auth.Name) // optional, get uuid of offline mode game 26 | c.Auth.UUID = hex.EncodeToString(id[:]) 27 | 28 | // Login 29 | err := c.JoinServer("127.0.0.1") 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | log.Println("Login success") 34 | 35 | // Register event handlers 36 | // c.Events.AddListener(...) 37 | 38 | // JoinGame 39 | err = c.HandleGame() 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | } 44 | 45 | func ExampleClient_JoinServer_online() { 46 | c := NewClient() 47 | 48 | // Login Mojang account to get AccessToken 49 | // To use Microsoft Account, see issue #106 50 | // https://github.com/Tnze/go-mc/issues/106 51 | auth, err := yggdrasil.Authenticate("Your E-mail", "Your Password") 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | // As long as you set these three fields correctly, 57 | // the client can connect to the online-mode server 58 | c.Auth.UUID, c.Auth.Name = auth.SelectedProfile() 59 | c.Auth.AsTk = auth.AccessToken() 60 | 61 | // Connect server 62 | err = c.JoinServer("127.0.0.1") 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | log.Println("Login success") 67 | 68 | // Register event handlers 69 | // c.Events.GameStart = onGameStartFunc 70 | // c.Events.ChatMsg = onChatMsgFunc 71 | // c.Events.Disconnect = onDisconnectFunc 72 | // ... 73 | 74 | // Join the game 75 | err = c.HandleGame() 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /bot/ingame.go: -------------------------------------------------------------------------------- 1 | package bot 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/Tnze/go-mc/data/packetid" 8 | pk "github.com/Tnze/go-mc/net/packet" 9 | ) 10 | 11 | // HandleGame receive server packet and response them correctly. 12 | // Note that HandleGame will block if you don't receive from Events. 13 | func (c *Client) HandleGame() error { 14 | for { 15 | var p pk.Packet 16 | // Read packets 17 | if err := c.Conn.ReadPacket(&p); err != nil { 18 | return err 19 | } 20 | 21 | if p.ID == int32(packetid.BundleDelimiter) { 22 | err := c.handleBundlePackets() 23 | if err != nil { 24 | return err 25 | } 26 | } else { 27 | // handle packets 28 | err := c.handlePacket(p) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | // return the packet buffer 34 | c.Conn.pool.Put(p.Data) 35 | } 36 | } 37 | } 38 | 39 | type PacketHandlerError struct { 40 | ID packetid.ClientboundPacketID 41 | Err error 42 | } 43 | 44 | func (d PacketHandlerError) Error() string { 45 | return fmt.Sprintf("handle packet %v error: %v", d.ID, d.Err) 46 | } 47 | 48 | func (d PacketHandlerError) Unwrap() error { 49 | return d.Err 50 | } 51 | 52 | func (c *Client) handleBundlePackets() (err error) { 53 | var packets []pk.Packet 54 | for i := 0; i < 4096; i++ { 55 | var p pk.Packet 56 | // Read packets 57 | if err := c.Conn.ReadPacket(&p); err != nil { 58 | return err 59 | } 60 | 61 | if p.ID == int32(packetid.BundleDelimiter) { 62 | // bundle finished 63 | goto handlePackets 64 | } 65 | 66 | packets = append(packets, p) 67 | } 68 | return errors.New("packet number of a bundle out of limit") 69 | 70 | handlePackets: 71 | for i := range packets { 72 | if err := c.handlePacket(packets[i]); err != nil { 73 | return err 74 | } 75 | } 76 | return nil 77 | } 78 | 79 | func (c *Client) handlePacket(p pk.Packet) (err error) { 80 | packetID := packetid.ClientboundPacketID(p.ID) 81 | for _, handler := range c.Events.generic { 82 | if err = handler.F(p); err != nil { 83 | return PacketHandlerError{ID: packetID, Err: err} 84 | } 85 | } 86 | for _, handler := range c.Events.handlers[packetID] { 87 | err = handler.F(p) 88 | if err != nil { 89 | return PacketHandlerError{ID: packetID, Err: err} 90 | } 91 | } 92 | return 93 | } 94 | -------------------------------------------------------------------------------- /bot/msg/events.go: -------------------------------------------------------------------------------- 1 | package msg 2 | 3 | import "github.com/Tnze/go-mc/chat" 4 | 5 | // EventsHandler is a collection of event handlers. 6 | // Fill the fields with your handler functions and pass this struct to [New] to create the msg manager. 7 | // The handler functions will be called when the corresponding event is triggered. 8 | // Leave the fields as nil if you don't want to handle the event. 9 | type EventsHandler struct { 10 | // SystemChat handles messages sent by gaming system. 11 | // 12 | // In vanilla client: 13 | // If overlay is false, the message will be displayed in the chat box. 14 | // If overlay is true, the message will be displayed on the top of the hot-bar. 15 | SystemChat func(msg chat.Message, overlay bool) error 16 | 17 | // PlayerChatMessage handles messages sent by players. 18 | // 19 | // Message signing system is added in 1.19. The message and its context could be signed by the player's private key. 20 | // The manager tries to verify the message signature through the player's public key, 21 | // and return the result as validated boolean. 22 | PlayerChatMessage func(msg chat.Message, validated bool) error 23 | 24 | // DisguisedChat handles DisguisedChat message. 25 | // 26 | // DisguisedChat message used to send system chat. 27 | // Now it is used to send messages from "/say" command from server console. 28 | DisguisedChat func(msg chat.Message) error 29 | } 30 | -------------------------------------------------------------------------------- /bot/screen/chest.go: -------------------------------------------------------------------------------- 1 | package screen 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/Tnze/go-mc/chat" 7 | "github.com/Tnze/go-mc/data/inventory" 8 | ) 9 | 10 | type Chest struct { 11 | Type inventory.InventoryID 12 | Title chat.Message 13 | Slots []Slot 14 | Rows int 15 | } 16 | 17 | func (c *Chest) onSetSlot(i int, slot Slot) error { 18 | if i < 0 || i >= len(c.Slots) { 19 | return errors.New("slot index out of bounds") 20 | } 21 | c.Slots[i] = slot 22 | return nil 23 | } 24 | 25 | func (c *Chest) onClose() error { 26 | return nil 27 | } 28 | 29 | func (c *Chest) Container() []Slot { 30 | return c.Slots[0 : c.Rows*9] 31 | } 32 | 33 | func (c *Chest) Main() []Slot { 34 | return c.Slots[c.Rows*9 : c.Rows*9+27] 35 | } 36 | 37 | func (c *Chest) Hotbar() []Slot { 38 | return c.Slots[c.Rows*9+27 : (c.Rows+4)*9] 39 | } 40 | -------------------------------------------------------------------------------- /bot/screen/events.go: -------------------------------------------------------------------------------- 1 | package screen 2 | 3 | import "github.com/Tnze/go-mc/chat" 4 | 5 | type EventsListener struct { 6 | Open func(id int, container_type int32, title chat.Message) error 7 | SetSlot func(id, index int) error 8 | Close func(id int) error 9 | } 10 | -------------------------------------------------------------------------------- /bot/screen/inventory.go: -------------------------------------------------------------------------------- 1 | package screen 2 | 3 | import "errors" 4 | 5 | type Inventory struct { 6 | Slots [46]Slot 7 | } 8 | 9 | func (inv *Inventory) onClose() error { 10 | return nil 11 | } 12 | 13 | func (inv *Inventory) onSetSlot(i int, s Slot) error { 14 | if i < 0 || i >= len(inv.Slots) { 15 | return errors.New("slot index out of bounds") 16 | } 17 | inv.Slots[i] = s 18 | return nil 19 | } 20 | 21 | func (inv *Inventory) CraftingOutput() *Slot { return &inv.Slots[0] } 22 | func (inv *Inventory) CraftingInput() []Slot { return inv.Slots[1 : 1+4] } 23 | 24 | // Armor returns to the armor section of the Inventory. 25 | // The length is 4, which are head, chest, legs and feet. 26 | func (inv *Inventory) Armor() []Slot { return inv.Slots[5 : 5+4] } 27 | func (inv *Inventory) Main() []Slot { return inv.Slots[9 : 9+3*9] } 28 | func (inv *Inventory) Hotbar() []Slot { return inv.Slots[36 : 36+9] } 29 | func (inv *Inventory) Offhand() *Slot { return &inv.Slots[45] } 30 | -------------------------------------------------------------------------------- /bot/world/chunks.go: -------------------------------------------------------------------------------- 1 | package world 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Tnze/go-mc/bot" 7 | "github.com/Tnze/go-mc/bot/basic" 8 | "github.com/Tnze/go-mc/data/packetid" 9 | "github.com/Tnze/go-mc/level" 10 | pk "github.com/Tnze/go-mc/net/packet" 11 | ) 12 | 13 | type World struct { 14 | c *bot.Client 15 | p *basic.Player 16 | events EventsListener 17 | 18 | Columns map[level.ChunkPos]*level.Chunk 19 | } 20 | 21 | func NewWorld(c *bot.Client, p *basic.Player, events EventsListener) (w *World) { 22 | w = &World{ 23 | c: c, p: p, 24 | events: events, 25 | Columns: make(map[level.ChunkPos]*level.Chunk), 26 | } 27 | c.Events.AddListener( 28 | bot.PacketHandler{Priority: 64, ID: packetid.ClientboundLogin, F: w.onPlayerSpawn}, 29 | bot.PacketHandler{Priority: 64, ID: packetid.ClientboundRespawn, F: w.onPlayerSpawn}, 30 | bot.PacketHandler{Priority: 0, ID: packetid.ClientboundLevelChunkWithLight, F: w.handleLevelChunkWithLightPacket}, 31 | bot.PacketHandler{Priority: 0, ID: packetid.ClientboundForgetLevelChunk, F: w.handleForgetLevelChunkPacket}, 32 | ) 33 | return 34 | } 35 | 36 | func (w *World) onPlayerSpawn(pk.Packet) error { 37 | // unload all chunks 38 | w.Columns = make(map[level.ChunkPos]*level.Chunk) 39 | return nil 40 | } 41 | 42 | func (w *World) handleLevelChunkWithLightPacket(packet pk.Packet) error { 43 | var pos level.ChunkPos 44 | currentDimType := w.c.Registries.DimensionType.GetByID(w.p.DimensionType) 45 | if currentDimType == nil { 46 | return fmt.Errorf("dimension type %d not found", w.p.DimensionType) 47 | } 48 | chunk := level.EmptyChunk(int(currentDimType.Height) / 16) 49 | if err := packet.Scan(&pos, chunk); err != nil { 50 | return err 51 | } 52 | w.Columns[pos] = chunk 53 | if w.events.LoadChunk != nil { 54 | if err := w.events.LoadChunk(pos); err != nil { 55 | return err 56 | } 57 | } 58 | return nil 59 | } 60 | 61 | func (w *World) handleForgetLevelChunkPacket(packet pk.Packet) error { 62 | var pos level.ChunkPos 63 | if err := packet.Scan(&pos); err != nil { 64 | return err 65 | } 66 | var err error 67 | if w.events.UnloadChunk != nil { 68 | err = w.events.UnloadChunk(pos) 69 | } 70 | delete(w.Columns, pos) 71 | return err 72 | } 73 | -------------------------------------------------------------------------------- /bot/world/events.go: -------------------------------------------------------------------------------- 1 | package world 2 | 3 | import "github.com/Tnze/go-mc/level" 4 | 5 | type EventsListener struct { 6 | LoadChunk func(pos level.ChunkPos) error 7 | UnloadChunk func(pos level.ChunkPos) error 8 | } 9 | -------------------------------------------------------------------------------- /chat/clickevent.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import "strconv" 4 | 5 | // ClickEvent defines an event that occurs when this component is clicked. 6 | type ClickEvent struct { 7 | Action string `json:"action" nbt:"action"` 8 | Value string `json:"value" nbt:"value"` 9 | } 10 | 11 | // OpenURL create a ClickEvent opens the given URL in the default web browser. 12 | // Ignored if the player has opted to disable links in chat; 13 | // may open a GUI prompting the user if the setting for that is enabled. 14 | // The link's protocol must be set and must be http or https, for security reasons. 15 | func OpenURL(url string) *ClickEvent { 16 | return &ClickEvent{ 17 | Action: "open_url", 18 | Value: url, 19 | } 20 | } 21 | 22 | // RunCommand create a ClickEvent runs the given command. Not required to be a command - 23 | // clicking this only causes the client to send the given content as a chat message, 24 | // so if not prefixed with /, they will say the given text instead. 25 | // If used in a book GUI, the GUI is closed after clicking. 26 | func RunCommand(cmd string) *ClickEvent { 27 | return &ClickEvent{ 28 | Action: "run_command", 29 | Value: cmd, 30 | } 31 | } 32 | 33 | // SuggestCommand create a ClickEvent replaces the content of the chat box with the given text - 34 | // usually a command, but it is not required to be a command 35 | // (commands should be prefixed with /). 36 | // This is only usable for messages in chat. 37 | func SuggestCommand(cmd string) *ClickEvent { 38 | return &ClickEvent{ 39 | Action: "suggest_command", 40 | Value: cmd, 41 | } 42 | } 43 | 44 | // ChangePage create a ClickEvent usable within written books. 45 | // Changes the page of the book to the given page, starting at 1. 46 | // For instance, "value":1 switches the book to the first page. 47 | // If the page is less than one or beyond the number of pages in the book, the event is ignored. 48 | func ChangePage(page int) *ClickEvent { 49 | return &ClickEvent{ 50 | Action: "change_page", 51 | Value: strconv.Itoa(page), 52 | } 53 | } 54 | 55 | // CopyToClipboard create a ClickEvent copies the given text to the client's clipboard when clicked. 56 | func CopyToClipboard(text string) *ClickEvent { 57 | return &ClickEvent{ 58 | Action: "copy_to_clipboard", 59 | Value: text, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /chat/hoverevent.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | // HoverEvent defines an event that occurs when this component hovered over. 4 | type HoverEvent struct { 5 | Action string `json:"action" nbt:"action"` 6 | Contents any `json:"contents" nbt:"contents"` // Didn't handled yet 7 | Value Message `json:"value" nbt:"value"` // Legacy 8 | } 9 | 10 | type HoverSub struct { 11 | Color string `json:"color"` 12 | Text string `json:"text"` 13 | } 14 | 15 | // ShowText show the text to display. 16 | func ShowText(text Message) *HoverEvent { 17 | return &HoverEvent{ 18 | Action: "show_text", 19 | Value: text, 20 | } 21 | } 22 | 23 | // ShowItem show the item to display. 24 | // Item is encoded as the S-NBT format, nbt.StringifiedMessage could help. 25 | // See: https://wiki.vg/Chat#:~:text=show_item,in%20red%20instead. 26 | func ShowItem(item string) *HoverEvent { 27 | return &HoverEvent{ 28 | Action: "show_item", 29 | Value: Text(item), 30 | } 31 | } 32 | 33 | // ShowEntity show an entity describing by the S-NBT, nbt.StringifiedMessage could help. 34 | // See: https://wiki.vg/Chat#:~:text=show_entity,given%20entity%20loaded. 35 | func ShowEntity(entity string) *HoverEvent { 36 | return &HoverEvent{ 37 | Action: "show_entity", 38 | Value: Text(entity), 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chat/jsonmessage.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "io" 8 | 9 | pk "github.com/Tnze/go-mc/net/packet" 10 | ) 11 | 12 | // JsonMessage is Message, unless when it is going to be Json instead of NBT 13 | type JsonMessage Message 14 | 15 | // ReadFrom decode Message in a JSON Text component 16 | func (m *JsonMessage) ReadFrom(r io.Reader) (int64, error) { 17 | var code pk.String 18 | n, err := code.ReadFrom(r) 19 | if err != nil { 20 | return n, err 21 | } 22 | err = json.Unmarshal([]byte(code), (*Message)(m)) 23 | return n, err 24 | } 25 | 26 | // WriteTo encode Message into a JSON Text component 27 | func (m JsonMessage) WriteTo(w io.Writer) (int64, error) { 28 | code, err := json.Marshal(Message(m)) 29 | if err != nil { 30 | panic(err) 31 | } 32 | return pk.String(code).WriteTo(w) 33 | } 34 | 35 | func (m Message) MarshalJSON() ([]byte, error) { 36 | if m.Translate != "" { 37 | return json.Marshal(translateMsg(m)) 38 | } else { 39 | return json.Marshal(rawMsgStruct(m)) 40 | } 41 | } 42 | 43 | func (m *Message) UnmarshalJSON(raw []byte) (err error) { 44 | raw = bytes.TrimSpace(raw) 45 | if len(raw) == 0 { 46 | return io.EOF 47 | } 48 | // The right way to distinguish JSON String and Object 49 | // is to look up the first character. 50 | switch raw[0] { 51 | case '"': 52 | return json.Unmarshal(raw, &m.Text) // Unmarshal as jsonString 53 | case '{': 54 | return json.Unmarshal(raw, (*rawMsgStruct)(m)) // Unmarshal as jsonMsg 55 | case '[': 56 | return json.Unmarshal(raw, &m.Extra) // Unmarshal as []Message 57 | default: 58 | return errors.New("unknown chat message type: '" + string(raw[0]) + "'") 59 | } 60 | } 61 | 62 | func (t *TranslateArgs) UnmarshalJSON(raw []byte) error { 63 | var v []Message 64 | err := json.Unmarshal(raw, &v) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | for _, v := range v { 70 | *t = append(*t, v) 71 | } 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /chat/nbtmessage_test.go: -------------------------------------------------------------------------------- 1 | package chat_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/Tnze/go-mc/chat" 7 | en_us "github.com/Tnze/go-mc/data/lang/en-us" 8 | "github.com/Tnze/go-mc/nbt" 9 | ) 10 | 11 | func TestMessage_UnmarshalJSON_string(t *testing.T) { 12 | snbts := []string{ 13 | "{translate: sleep.players_sleeping, with: [I; 1, 37]}", 14 | } 15 | 16 | texts := []string{ 17 | "1/37 players sleeping", 18 | } 19 | 20 | chat.SetLanguage(en_us.Map) 21 | for i, v := range snbts { 22 | bytes, err := nbt.Marshal(nbt.StringifiedMessage(v)) 23 | if err != nil { 24 | t.Errorf("Invalid SNBT: %v", err) 25 | continue 26 | } 27 | 28 | var cm chat.Message 29 | if err := nbt.Unmarshal(bytes, &cm); err != nil { 30 | t.Error(err) 31 | } 32 | if str := cm.String(); str != texts[i] { 33 | t.Errorf("gets %q, wants %q", str, texts[i]) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chat/sign/cache.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type SignatureCache struct { 8 | signatures [128]*Signature 9 | signIndexes map[Signature]int 10 | cachedBuffer []*Signature 11 | } 12 | 13 | func NewSignatureCache() SignatureCache { 14 | return SignatureCache{ 15 | signIndexes: make(map[Signature]int), 16 | } 17 | } 18 | 19 | func (s *SignatureCache) PopOrInsert(self *Signature, lastSeen []*Signature) { 20 | var tmp *Signature 21 | s.cachedBuffer = s.cachedBuffer[:0] // clear buffer 22 | if self != nil { 23 | s.cachedBuffer = append(s.cachedBuffer, self) 24 | } 25 | for _, v := range lastSeen { 26 | s.cachedBuffer = append(s.cachedBuffer, v) 27 | } 28 | for i := 0; i < len(s.cachedBuffer) && i < len(s.signatures); i++ { 29 | v := s.cachedBuffer[i] 30 | if i, ok := s.signIndexes[*v]; ok { 31 | s.signatures[i] = nil 32 | } 33 | tmp, s.signatures[i] = s.signatures[i], v 34 | s.signIndexes[*v] = i 35 | if tmp != nil { 36 | s.cachedBuffer = append(s.cachedBuffer, tmp) 37 | delete(s.signIndexes, *tmp) 38 | } 39 | } 40 | } 41 | 42 | var UncachedSignature = errors.New("uncached signature") 43 | -------------------------------------------------------------------------------- /chat/sign/cache_test.go: -------------------------------------------------------------------------------- 1 | package sign 2 | 3 | import ( 4 | "crypto/rand" 5 | "testing" 6 | ) 7 | 8 | func TestSignatureCache(t *testing.T) { 9 | cache := NewSignatureCache() 10 | s1 := &Signature{1} 11 | s2 := &Signature{2} 12 | s3 := &Signature{3} 13 | s4 := &Signature{4} 14 | // t.Logf("%p, %p, %p, %p", s1, s2, s3, s4) 15 | cache.PopOrInsert(nil, []*Signature{s1, s2, s3}) 16 | // cache: [s1, s2, s3, nil...] 17 | if cache.signatures[0] != s1 || cache.signatures[1] != s2 || cache.signatures[2] != s3 { 18 | t.Log(cache.signatures) 19 | t.Fatal("insert s1~3 error") 20 | } 21 | cache.PopOrInsert(s4, []*Signature{s3}) 22 | // cache: [s4, s3, s1, s2, nil...] 23 | if cache.signatures[0] != s4 { 24 | t.Log(cache.signatures) 25 | t.Fatal("insert s4 error") 26 | } 27 | if cache.signatures[1] != s3 { 28 | t.Log(cache.signatures) 29 | t.Fatal("pop s3 error") 30 | } 31 | if cache.signatures[2] != s1 || cache.signatures[3] != s2 { 32 | t.Log(cache.signatures) 33 | t.Fatal("s1~2 position error") 34 | } 35 | } 36 | 37 | func TestSignatureCache_2(t *testing.T) { 38 | cache := NewSignatureCache() 39 | signs := make([]*Signature, len(cache.signatures)+5) 40 | for i := range signs { 41 | signs[i] = new(Signature) 42 | _, _ = rand.Read(signs[i][:]) 43 | } 44 | cache.PopOrInsert(nil, signs[:len(cache.signatures)]) 45 | if !signatureEquals(cache.signatures[:], signs[:len(cache.signatures)]) { 46 | t.Fatal("insert error") 47 | } 48 | insert2 := signs[len(cache.signatures)-5:] 49 | cache.PopOrInsert(nil, insert2) 50 | if !signatureEquals(cache.signatures[:10], insert2) { 51 | t.Fatal("insert and pop error") 52 | } 53 | } 54 | 55 | func signatureEquals(a, b []*Signature) bool { 56 | if len(a) != len(b) { 57 | return false 58 | } 59 | for i := range a { 60 | if a[i] != b[i] { 61 | return false 62 | } 63 | } 64 | return true 65 | } 66 | -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | ## Updating `data` 2 | 3 | 1. Go to [https://github.com/PrismarineJS/minecraft-data/tree/master/data/pc/{version}](https://github.com/PrismarineJS/minecraft-data/tree/master/data/pc) 4 | 2. Update `version` in the following files if there is a new corresponding JSON file available: 5 | - [gen_block.go](block/gen_block.go) - `blocks.json` 6 | - [gen_entity.go](entity/gen_entity.go) - `entities.json` 7 | - [gen_item.go](item/gen_item.go) - `items.json` 8 | 3. Update the `URL` in [gen_soundid.go](soundid/gen_soundid.go) (verify the URL returns a response first) 9 | 4. Run `go generate ./...` -------------------------------------------------------------------------------- /data/item/gen_item.go: -------------------------------------------------------------------------------- 1 | //go:build generate 2 | 3 | // gen_item.go generates item information. 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "net/http" 10 | "os" 11 | "text/template" 12 | 13 | "github.com/iancoleman/strcase" 14 | ) 15 | 16 | const ( 17 | version = "1.20.3" 18 | infoURL = "https://raw.githubusercontent.com/PrismarineJS/minecraft-data/master/data/pc/" + version + "/items.json" 19 | // language=gohtml 20 | itemTmpl = `// Code generated by gen_item.go DO NOT EDIT. 21 | // Package item stores information about items in Minecraft. 22 | package item 23 | 24 | // ID describes the numeric ID of an item. 25 | type ID uint32 26 | 27 | // Item describes information about a type of item. 28 | type Item struct { 29 | ID ID 30 | DisplayName string 31 | Name string 32 | StackSize uint 33 | } 34 | 35 | var ( 36 | {{- range .}} 37 | {{.CamelName}} = Item{ 38 | ID: {{.ID}}, 39 | DisplayName: "{{.DisplayName}}", 40 | Name: "{{.Name}}", 41 | StackSize: {{.StackSize}}, 42 | }{{end}} 43 | ) 44 | 45 | // ByID is an index of minecraft items by their ID. 46 | var ByID = map[ID]*Item{ {{range .}} 47 | {{.ID}}: &{{.CamelName}},{{end}} 48 | }` 49 | ) 50 | 51 | type Item struct { 52 | ID uint32 `json:"id"` 53 | CamelName string `json:"-"` 54 | DisplayName string `json:"displayName"` 55 | Name string `json:"name"` 56 | StackSize uint `json:"stackSize"` 57 | } 58 | 59 | func downloadInfo() ([]*Item, error) { 60 | resp, err := http.Get(infoURL) 61 | if err != nil { 62 | return nil, err 63 | } 64 | defer resp.Body.Close() 65 | 66 | var data []*Item 67 | if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { 68 | return nil, err 69 | } 70 | for _, d := range data { 71 | d.CamelName = strcase.ToCamel(d.Name) 72 | } 73 | return data, nil 74 | } 75 | 76 | //go:generate go run $GOFILE 77 | //go:generate go fmt item.go 78 | func main() { 79 | fmt.Println("generating item.go") 80 | items, err := downloadInfo() 81 | if err != nil { 82 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 83 | os.Exit(1) 84 | } 85 | 86 | f, err := os.Create("item.go") 87 | if err != nil { 88 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 89 | os.Exit(1) 90 | } 91 | defer f.Close() 92 | 93 | if err := template.Must(template.New("").Parse(itemTmpl)).Execute(f, items); err != nil { 94 | panic(err) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /data/packetid/generator/GenPacketId.java: -------------------------------------------------------------------------------- 1 | package pers.tnze.gomc.gen; 2 | 3 | import net.minecraft.SharedConstants; 4 | import net.minecraft.network.ProtocolInfo; 5 | import net.minecraft.network.protocol.configuration.ConfigurationProtocols; 6 | import net.minecraft.network.protocol.game.GameProtocols; 7 | import net.minecraft.network.protocol.login.LoginProtocols; 8 | import net.minecraft.network.protocol.status.StatusProtocols; 9 | import net.minecraft.server.Bootstrap; 10 | 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.io.Writer; 14 | 15 | public class GenPacketId { 16 | 17 | public static void main(String[] args) throws Exception { 18 | SharedConstants.tryDetectVersion(); 19 | Bootstrap.bootStrap(); 20 | try (FileWriter w = new FileWriter("packet_names.txt")) { 21 | handlePackets(w, LoginProtocols.CLIENTBOUND_TEMPLATE, "ClientboundLogin"); 22 | handlePackets(w, LoginProtocols.SERVERBOUND_TEMPLATE, "ServerboundLogin"); 23 | handlePackets(w, StatusProtocols.CLIENTBOUND_TEMPLATE, "ClientboundStatus"); 24 | handlePackets(w, StatusProtocols.SERVERBOUND_TEMPLATE, "ServerboundStatus"); 25 | handlePackets(w, ConfigurationProtocols.CLIENTBOUND_TEMPLATE, "ClientboundConfig"); 26 | handlePackets(w, ConfigurationProtocols.SERVERBOUND_TEMPLATE, "ServerboundConfig"); 27 | handlePackets(w, GameProtocols.CLIENTBOUND_TEMPLATE, ""); 28 | handlePackets(w, GameProtocols.SERVERBOUND_TEMPLATE, ""); 29 | } 30 | } 31 | 32 | private static void handlePackets(Writer w, ProtocolInfo.Unbound packets, String prefix) throws IOException { 33 | packets.listPackets((packetType, i) -> { 34 | String packetName = packetType.id().getPath(); 35 | String[] words = packetName.split("_"); 36 | try { 37 | if (prefix != null) { 38 | w.write(prefix); 39 | } 40 | for (String word : words) { 41 | w.write(Character.toUpperCase(word.charAt(0))); 42 | w.write(word.substring(1)); 43 | } 44 | w.write("\n"); 45 | } catch (IOException e) { 46 | throw new RuntimeException(e); 47 | } 48 | }); 49 | w.write('\n'); 50 | } 51 | } -------------------------------------------------------------------------------- /data/packetid/legacy.go: -------------------------------------------------------------------------------- 1 | package packetid 2 | 3 | // This file might be remove in the future go-mc!!! 4 | 5 | // Login Clientbound 6 | // 7 | // Deprecated: Legacy name, might be removed in future version 8 | const ( 9 | LoginDisconnect = ClientboundLoginLoginDisconnect 10 | LoginEncryptionRequest = ClientboundLoginHello 11 | LoginSuccess = ClientboundLoginGameProfile 12 | LoginCompression = ClientboundLoginLoginCompression 13 | LoginPluginRequest = ClientboundLoginCustomQuery 14 | 15 | ClientboundLoginDisconnect = ClientboundLoginLoginDisconnect 16 | ClientboundLoginEncryptionRequest = ClientboundLoginHello 17 | ClientboundLoginSuccess = ClientboundLoginGameProfile 18 | ClientboundLoginCompression = ClientboundLoginLoginCompression 19 | ClientboundLoginPluginRequest = ClientboundLoginCustomQuery 20 | ) 21 | 22 | // Login Serverbound 23 | // 24 | // Deprecated: Legacy name, might be removed in future version 25 | const ( 26 | LoginStart = ServerboundLoginHello 27 | LoginEncryptionResponse = ServerboundLoginKey 28 | LoginPluginResponse = ServerboundLoginCustomQueryAnswer 29 | 30 | ServerboundLoginStart = ServerboundLoginHello 31 | ServerboundLoginEncryptionResponse = ServerboundLoginKey 32 | ServerboundLoginPluginResponse = ServerboundLoginCustomQueryAnswer 33 | ServerboundLoginAcknowledged = ServerboundLoginLoginAcknowledged 34 | ) 35 | 36 | // Status Clientbound 37 | // 38 | // Deprecated: Legacy name, might be removed in future version 39 | const ( 40 | StatusResponse = ClientboundStatusStatusResponse 41 | StatusPongResponse = ClientboundStatusPongResponse 42 | 43 | ClientboundStatusResponse = ClientboundStatusStatusResponse 44 | ) 45 | 46 | // Status Serverbound 47 | // 48 | // Deprecated: Legacy name, might be removed in future version 49 | const ( 50 | StatusRequest = ServerboundStatusStatusRequest 51 | StatusPingRequest = ServerboundStatusPingRequest 52 | 53 | ServerboundStatusRequest = ServerboundStatusStatusRequest 54 | ) 55 | -------------------------------------------------------------------------------- /data/registryid/activity.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var Activity = []string{ 6 | "minecraft:core", 7 | "minecraft:idle", 8 | "minecraft:work", 9 | "minecraft:play", 10 | "minecraft:rest", 11 | "minecraft:meet", 12 | "minecraft:panic", 13 | "minecraft:raid", 14 | "minecraft:pre_raid", 15 | "minecraft:hide", 16 | "minecraft:fight", 17 | "minecraft:celebrate", 18 | "minecraft:admire_item", 19 | "minecraft:avoid", 20 | "minecraft:ride", 21 | "minecraft:play_dead", 22 | "minecraft:long_jump", 23 | "minecraft:ram", 24 | "minecraft:tongue", 25 | "minecraft:swim", 26 | "minecraft:lay_spawn", 27 | "minecraft:sniff", 28 | "minecraft:investigate", 29 | "minecraft:roar", 30 | "minecraft:emerge", 31 | "minecraft:dig", 32 | } 33 | -------------------------------------------------------------------------------- /data/registryid/armormaterial.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var ArmorMaterial = []string{ 6 | "minecraft:leather", 7 | "minecraft:chainmail", 8 | "minecraft:iron", 9 | "minecraft:gold", 10 | "minecraft:diamond", 11 | "minecraft:turtle", 12 | "minecraft:netherite", 13 | "minecraft:armadillo", 14 | } 15 | -------------------------------------------------------------------------------- /data/registryid/attribute.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var Attribute = []string{ 6 | "minecraft:generic.armor", 7 | "minecraft:generic.armor_toughness", 8 | "minecraft:generic.attack_damage", 9 | "minecraft:generic.attack_knockback", 10 | "minecraft:generic.attack_speed", 11 | "minecraft:player.block_break_speed", 12 | "minecraft:player.block_interaction_range", 13 | "minecraft:generic.burning_time", 14 | "minecraft:generic.explosion_knockback_resistance", 15 | "minecraft:player.entity_interaction_range", 16 | "minecraft:generic.fall_damage_multiplier", 17 | "minecraft:generic.flying_speed", 18 | "minecraft:generic.follow_range", 19 | "minecraft:generic.gravity", 20 | "minecraft:generic.jump_strength", 21 | "minecraft:generic.knockback_resistance", 22 | "minecraft:generic.luck", 23 | "minecraft:generic.max_absorption", 24 | "minecraft:generic.max_health", 25 | "minecraft:player.mining_efficiency", 26 | "minecraft:generic.movement_efficiency", 27 | "minecraft:generic.movement_speed", 28 | "minecraft:generic.oxygen_bonus", 29 | "minecraft:generic.safe_fall_distance", 30 | "minecraft:generic.scale", 31 | "minecraft:player.sneaking_speed", 32 | "minecraft:zombie.spawn_reinforcements", 33 | "minecraft:generic.step_height", 34 | "minecraft:player.submerged_mining_speed", 35 | "minecraft:player.sweeping_damage_ratio", 36 | "minecraft:generic.water_movement_efficiency", 37 | } 38 | -------------------------------------------------------------------------------- /data/registryid/blockentitytype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var BlockEntityType = []string{ 6 | "minecraft:furnace", 7 | "minecraft:chest", 8 | "minecraft:trapped_chest", 9 | "minecraft:ender_chest", 10 | "minecraft:jukebox", 11 | "minecraft:dispenser", 12 | "minecraft:dropper", 13 | "minecraft:sign", 14 | "minecraft:hanging_sign", 15 | "minecraft:mob_spawner", 16 | "minecraft:piston", 17 | "minecraft:brewing_stand", 18 | "minecraft:enchanting_table", 19 | "minecraft:end_portal", 20 | "minecraft:beacon", 21 | "minecraft:skull", 22 | "minecraft:daylight_detector", 23 | "minecraft:hopper", 24 | "minecraft:comparator", 25 | "minecraft:banner", 26 | "minecraft:structure_block", 27 | "minecraft:end_gateway", 28 | "minecraft:command_block", 29 | "minecraft:shulker_box", 30 | "minecraft:bed", 31 | "minecraft:conduit", 32 | "minecraft:barrel", 33 | "minecraft:smoker", 34 | "minecraft:blast_furnace", 35 | "minecraft:lectern", 36 | "minecraft:bell", 37 | "minecraft:jigsaw", 38 | "minecraft:campfire", 39 | "minecraft:beehive", 40 | "minecraft:sculk_sensor", 41 | "minecraft:calibrated_sculk_sensor", 42 | "minecraft:sculk_catalyst", 43 | "minecraft:sculk_shrieker", 44 | "minecraft:chiseled_bookshelf", 45 | "minecraft:brushable_block", 46 | "minecraft:decorated_pot", 47 | "minecraft:crafter", 48 | "minecraft:trial_spawner", 49 | "minecraft:vault", 50 | } 51 | -------------------------------------------------------------------------------- /data/registryid/blockpredicatetype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var BlockPredicateType = []string{ 6 | "minecraft:matching_blocks", 7 | "minecraft:matching_block_tag", 8 | "minecraft:matching_fluids", 9 | "minecraft:has_sturdy_face", 10 | "minecraft:solid", 11 | "minecraft:replaceable", 12 | "minecraft:would_survive", 13 | "minecraft:inside_world_bounds", 14 | "minecraft:any_of", 15 | "minecraft:all_of", 16 | "minecraft:not", 17 | "minecraft:true", 18 | "minecraft:unobstructed", 19 | } 20 | -------------------------------------------------------------------------------- /data/registryid/bootstrap/builtinregistries.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "github.com/Tnze/go-mc/data/registryid" 5 | "github.com/Tnze/go-mc/level/block" 6 | "github.com/Tnze/go-mc/registry" 7 | ) 8 | 9 | func RegisterBlocks(reg *registry.Registry[block.Block]) { 10 | reg.Clear() 11 | for i, key := range registryid.Block { 12 | id, val := reg.Put(key, block.FromID[key]) 13 | if int32(i) != id || val == nil || *val == nil { 14 | panic("register blocks failed") 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/registryid/catvariant.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var CatVariant = []string{ 6 | "minecraft:tabby", 7 | "minecraft:black", 8 | "minecraft:red", 9 | "minecraft:siamese", 10 | "minecraft:british_shorthair", 11 | "minecraft:calico", 12 | "minecraft:persian", 13 | "minecraft:ragdoll", 14 | "minecraft:white", 15 | "minecraft:jellie", 16 | "minecraft:all_black", 17 | } 18 | -------------------------------------------------------------------------------- /data/registryid/chunkstatus.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var ChunkStatus = []string{ 6 | "minecraft:empty", 7 | "minecraft:structure_starts", 8 | "minecraft:structure_references", 9 | "minecraft:biomes", 10 | "minecraft:noise", 11 | "minecraft:surface", 12 | "minecraft:carvers", 13 | "minecraft:features", 14 | "minecraft:initialize_light", 15 | "minecraft:light", 16 | "minecraft:spawn", 17 | "minecraft:full", 18 | } 19 | -------------------------------------------------------------------------------- /data/registryid/commandargumenttype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var CommandArgumentType = []string{ 6 | "brigadier:bool", 7 | "brigadier:float", 8 | "brigadier:double", 9 | "brigadier:integer", 10 | "brigadier:long", 11 | "brigadier:string", 12 | "minecraft:entity", 13 | "minecraft:game_profile", 14 | "minecraft:block_pos", 15 | "minecraft:column_pos", 16 | "minecraft:vec3", 17 | "minecraft:vec2", 18 | "minecraft:block_state", 19 | "minecraft:block_predicate", 20 | "minecraft:item_stack", 21 | "minecraft:item_predicate", 22 | "minecraft:color", 23 | "minecraft:component", 24 | "minecraft:style", 25 | "minecraft:message", 26 | "minecraft:nbt_compound_tag", 27 | "minecraft:nbt_tag", 28 | "minecraft:nbt_path", 29 | "minecraft:objective", 30 | "minecraft:objective_criteria", 31 | "minecraft:operation", 32 | "minecraft:particle", 33 | "minecraft:angle", 34 | "minecraft:rotation", 35 | "minecraft:scoreboard_slot", 36 | "minecraft:score_holder", 37 | "minecraft:swizzle", 38 | "minecraft:team", 39 | "minecraft:item_slot", 40 | "minecraft:item_slots", 41 | "minecraft:resource_location", 42 | "minecraft:function", 43 | "minecraft:entity_anchor", 44 | "minecraft:int_range", 45 | "minecraft:float_range", 46 | "minecraft:dimension", 47 | "minecraft:gamemode", 48 | "minecraft:time", 49 | "minecraft:resource_or_tag", 50 | "minecraft:resource_or_tag_key", 51 | "minecraft:resource", 52 | "minecraft:resource_key", 53 | "minecraft:template_mirror", 54 | "minecraft:template_rotation", 55 | "minecraft:heightmap", 56 | "minecraft:loot_table", 57 | "minecraft:loot_predicate", 58 | "minecraft:loot_modifier", 59 | "minecraft:uuid", 60 | } 61 | -------------------------------------------------------------------------------- /data/registryid/creativemodetab.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var CreativeModeTab = []string{ 6 | "minecraft:building_blocks", 7 | "minecraft:colored_blocks", 8 | "minecraft:natural_blocks", 9 | "minecraft:functional_blocks", 10 | "minecraft:redstone_blocks", 11 | "minecraft:hotbar", 12 | "minecraft:search", 13 | "minecraft:tools_and_utilities", 14 | "minecraft:combat", 15 | "minecraft:food_and_drinks", 16 | "minecraft:ingredients", 17 | "minecraft:spawn_eggs", 18 | "minecraft:op_blocks", 19 | "minecraft:inventory", 20 | } 21 | -------------------------------------------------------------------------------- /data/registryid/datacomponenttype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var DataComponentType = []string{ 6 | "minecraft:custom_data", 7 | "minecraft:max_stack_size", 8 | "minecraft:max_damage", 9 | "minecraft:damage", 10 | "minecraft:unbreakable", 11 | "minecraft:custom_name", 12 | "minecraft:item_name", 13 | "minecraft:lore", 14 | "minecraft:rarity", 15 | "minecraft:enchantments", 16 | "minecraft:can_place_on", 17 | "minecraft:can_break", 18 | "minecraft:attribute_modifiers", 19 | "minecraft:custom_model_data", 20 | "minecraft:hide_additional_tooltip", 21 | "minecraft:hide_tooltip", 22 | "minecraft:repair_cost", 23 | "minecraft:creative_slot_lock", 24 | "minecraft:enchantment_glint_override", 25 | "minecraft:intangible_projectile", 26 | "minecraft:food", 27 | "minecraft:fire_resistant", 28 | "minecraft:tool", 29 | "minecraft:stored_enchantments", 30 | "minecraft:dyed_color", 31 | "minecraft:map_color", 32 | "minecraft:map_id", 33 | "minecraft:map_decorations", 34 | "minecraft:map_post_processing", 35 | "minecraft:charged_projectiles", 36 | "minecraft:bundle_contents", 37 | "minecraft:potion_contents", 38 | "minecraft:suspicious_stew_effects", 39 | "minecraft:writable_book_content", 40 | "minecraft:written_book_content", 41 | "minecraft:trim", 42 | "minecraft:debug_stick_state", 43 | "minecraft:entity_data", 44 | "minecraft:bucket_entity_data", 45 | "minecraft:block_entity_data", 46 | "minecraft:instrument", 47 | "minecraft:ominous_bottle_amplifier", 48 | "minecraft:jukebox_playable", 49 | "minecraft:recipes", 50 | "minecraft:lodestone_tracker", 51 | "minecraft:firework_explosion", 52 | "minecraft:fireworks", 53 | "minecraft:profile", 54 | "minecraft:note_block_sound", 55 | "minecraft:banner_patterns", 56 | "minecraft:base_color", 57 | "minecraft:pot_decorations", 58 | "minecraft:container", 59 | "minecraft:block_state", 60 | "minecraft:bees", 61 | "minecraft:lock", 62 | "minecraft:container_loot", 63 | } 64 | -------------------------------------------------------------------------------- /data/registryid/decoratedpotpattern.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var DecoratedPotPattern = []string{ 6 | "minecraft:angler", 7 | "minecraft:archer", 8 | "minecraft:arms_up", 9 | "minecraft:blade", 10 | "minecraft:brewer", 11 | "minecraft:burn", 12 | "minecraft:danger", 13 | "minecraft:explorer", 14 | "minecraft:flow", 15 | "minecraft:friend", 16 | "minecraft:guster", 17 | "minecraft:heart", 18 | "minecraft:heartbreak", 19 | "minecraft:howl", 20 | "minecraft:miner", 21 | "minecraft:mourner", 22 | "minecraft:plenty", 23 | "minecraft:prize", 24 | "minecraft:scrape", 25 | "minecraft:sheaf", 26 | "minecraft:shelter", 27 | "minecraft:skull", 28 | "minecraft:snort", 29 | "minecraft:blank", 30 | } 31 | -------------------------------------------------------------------------------- /data/registryid/enchantmenteffectcomponenttype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var EnchantmentEffectComponentType = []string{ 6 | "minecraft:damage_protection", 7 | "minecraft:damage_immunity", 8 | "minecraft:damage", 9 | "minecraft:smash_damage_per_fallen_block", 10 | "minecraft:knockback", 11 | "minecraft:armor_effectiveness", 12 | "minecraft:post_attack", 13 | "minecraft:hit_block", 14 | "minecraft:item_damage", 15 | "minecraft:attributes", 16 | "minecraft:equipment_drops", 17 | "minecraft:location_changed", 18 | "minecraft:tick", 19 | "minecraft:ammo_use", 20 | "minecraft:projectile_piercing", 21 | "minecraft:projectile_spawned", 22 | "minecraft:projectile_spread", 23 | "minecraft:projectile_count", 24 | "minecraft:trident_return_acceleration", 25 | "minecraft:fishing_time_reduction", 26 | "minecraft:fishing_luck_bonus", 27 | "minecraft:block_experience", 28 | "minecraft:mob_experience", 29 | "minecraft:repair_with_xp", 30 | "minecraft:crossbow_charge_time", 31 | "minecraft:crossbow_charging_sounds", 32 | "minecraft:trident_sound", 33 | "minecraft:prevent_equipment_drop", 34 | "minecraft:prevent_armor_change", 35 | "minecraft:trident_spin_attack_strength", 36 | } 37 | -------------------------------------------------------------------------------- /data/registryid/enchantmententityeffecttype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var EnchantmentEntityEffectType = []string{ 6 | "minecraft:all_of", 7 | "minecraft:apply_mob_effect", 8 | "minecraft:damage_entity", 9 | "minecraft:damage_item", 10 | "minecraft:explode", 11 | "minecraft:ignite", 12 | "minecraft:play_sound", 13 | "minecraft:replace_block", 14 | "minecraft:replace_disk", 15 | "minecraft:run_function", 16 | "minecraft:set_block_properties", 17 | "minecraft:spawn_particles", 18 | "minecraft:summon_entity", 19 | } 20 | -------------------------------------------------------------------------------- /data/registryid/enchantmentlevelbasedvaluetype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var EnchantmentLevelBasedValueType = []string{ 6 | "minecraft:clamped", 7 | "minecraft:fraction", 8 | "minecraft:levels_squared", 9 | "minecraft:linear", 10 | "minecraft:lookup", 11 | } 12 | -------------------------------------------------------------------------------- /data/registryid/enchantmentlocationbasedeffecttype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var EnchantmentLocationBasedEffectType = []string{ 6 | "minecraft:all_of", 7 | "minecraft:apply_mob_effect", 8 | "minecraft:attribute", 9 | "minecraft:damage_entity", 10 | "minecraft:damage_item", 11 | "minecraft:explode", 12 | "minecraft:ignite", 13 | "minecraft:play_sound", 14 | "minecraft:replace_block", 15 | "minecraft:replace_disk", 16 | "minecraft:run_function", 17 | "minecraft:set_block_properties", 18 | "minecraft:spawn_particles", 19 | "minecraft:summon_entity", 20 | } 21 | -------------------------------------------------------------------------------- /data/registryid/enchantmentprovidertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var EnchantmentProviderType = []string{ 6 | "minecraft:by_cost", 7 | "minecraft:by_cost_with_difficulty", 8 | "minecraft:single", 9 | } 10 | -------------------------------------------------------------------------------- /data/registryid/enchantmentvalueeffecttype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var EnchantmentValueEffectType = []string{ 6 | "minecraft:add", 7 | "minecraft:all_of", 8 | "minecraft:multiply", 9 | "minecraft:remove_binomial", 10 | "minecraft:set", 11 | } 12 | -------------------------------------------------------------------------------- /data/registryid/entitysubpredicatetype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var EntitySubPredicateType = []string{ 6 | "minecraft:lightning", 7 | "minecraft:fishing_hook", 8 | "minecraft:player", 9 | "minecraft:slime", 10 | "minecraft:raider", 11 | "minecraft:axolotl", 12 | "minecraft:boat", 13 | "minecraft:fox", 14 | "minecraft:mooshroom", 15 | "minecraft:rabbit", 16 | "minecraft:horse", 17 | "minecraft:llama", 18 | "minecraft:villager", 19 | "minecraft:parrot", 20 | "minecraft:tropical_fish", 21 | "minecraft:painting", 22 | "minecraft:cat", 23 | "minecraft:frog", 24 | "minecraft:wolf", 25 | } 26 | -------------------------------------------------------------------------------- /data/registryid/floatprovidertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var FloatProviderType = []string{ 6 | "minecraft:constant", 7 | "minecraft:uniform", 8 | "minecraft:clamped_normal", 9 | "minecraft:trapezoid", 10 | } 11 | -------------------------------------------------------------------------------- /data/registryid/fluid.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var Fluid = []string{ 6 | "minecraft:empty", 7 | "minecraft:flowing_water", 8 | "minecraft:water", 9 | "minecraft:flowing_lava", 10 | "minecraft:lava", 11 | } 12 | -------------------------------------------------------------------------------- /data/registryid/frogvariant.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var FrogVariant = []string{ 6 | "minecraft:temperate", 7 | "minecraft:warm", 8 | "minecraft:cold", 9 | } 10 | -------------------------------------------------------------------------------- /data/registryid/gameevent.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var GameEvent = []string{ 6 | "minecraft:block_activate", 7 | "minecraft:block_attach", 8 | "minecraft:block_change", 9 | "minecraft:block_close", 10 | "minecraft:block_deactivate", 11 | "minecraft:block_destroy", 12 | "minecraft:block_detach", 13 | "minecraft:block_open", 14 | "minecraft:block_place", 15 | "minecraft:container_close", 16 | "minecraft:container_open", 17 | "minecraft:drink", 18 | "minecraft:eat", 19 | "minecraft:elytra_glide", 20 | "minecraft:entity_damage", 21 | "minecraft:entity_die", 22 | "minecraft:entity_dismount", 23 | "minecraft:entity_interact", 24 | "minecraft:entity_mount", 25 | "minecraft:entity_place", 26 | "minecraft:entity_action", 27 | "minecraft:equip", 28 | "minecraft:explode", 29 | "minecraft:flap", 30 | "minecraft:fluid_pickup", 31 | "minecraft:fluid_place", 32 | "minecraft:hit_ground", 33 | "minecraft:instrument_play", 34 | "minecraft:item_interact_finish", 35 | "minecraft:item_interact_start", 36 | "minecraft:jukebox_play", 37 | "minecraft:jukebox_stop_play", 38 | "minecraft:lightning_strike", 39 | "minecraft:note_block_play", 40 | "minecraft:prime_fuse", 41 | "minecraft:projectile_land", 42 | "minecraft:projectile_shoot", 43 | "minecraft:sculk_sensor_tendrils_clicking", 44 | "minecraft:shear", 45 | "minecraft:shriek", 46 | "minecraft:splash", 47 | "minecraft:step", 48 | "minecraft:swim", 49 | "minecraft:teleport", 50 | "minecraft:unequip", 51 | "minecraft:resonate_1", 52 | "minecraft:resonate_2", 53 | "minecraft:resonate_3", 54 | "minecraft:resonate_4", 55 | "minecraft:resonate_5", 56 | "minecraft:resonate_6", 57 | "minecraft:resonate_7", 58 | "minecraft:resonate_8", 59 | "minecraft:resonate_9", 60 | "minecraft:resonate_10", 61 | "minecraft:resonate_11", 62 | "minecraft:resonate_12", 63 | "minecraft:resonate_13", 64 | "minecraft:resonate_14", 65 | "minecraft:resonate_15", 66 | } 67 | -------------------------------------------------------------------------------- /data/registryid/generator/template.go.tmpl: -------------------------------------------------------------------------------- 1 | // Code generated by {{Generator}}; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | {{ $typeName := .TypeName}} 6 | 7 | var {{$typeName}} = []string{ 8 | {{- range $index, $elem := .Entries}} 9 | {{printf "%q" $elem}}, 10 | {{- end}} 11 | } 12 | -------------------------------------------------------------------------------- /data/registryid/heightprovidertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var HeightProviderType = []string{ 6 | "minecraft:constant", 7 | "minecraft:uniform", 8 | "minecraft:biased_to_bottom", 9 | "minecraft:very_biased_to_bottom", 10 | "minecraft:trapezoid", 11 | "minecraft:weighted_list", 12 | } 13 | -------------------------------------------------------------------------------- /data/registryid/instrument.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var Instrument = []string{ 6 | "minecraft:ponder_goat_horn", 7 | "minecraft:sing_goat_horn", 8 | "minecraft:seek_goat_horn", 9 | "minecraft:feel_goat_horn", 10 | "minecraft:admire_goat_horn", 11 | "minecraft:call_goat_horn", 12 | "minecraft:yearn_goat_horn", 13 | "minecraft:dream_goat_horn", 14 | } 15 | -------------------------------------------------------------------------------- /data/registryid/intprovidertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var IntProviderType = []string{ 6 | "minecraft:constant", 7 | "minecraft:uniform", 8 | "minecraft:biased_to_bottom", 9 | "minecraft:clamped", 10 | "minecraft:weighted_list", 11 | "minecraft:clamped_normal", 12 | } 13 | -------------------------------------------------------------------------------- /data/registryid/itemsubpredicatetype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var ItemSubPredicateType = []string{ 6 | "minecraft:damage", 7 | "minecraft:enchantments", 8 | "minecraft:stored_enchantments", 9 | "minecraft:potion_contents", 10 | "minecraft:custom_data", 11 | "minecraft:container", 12 | "minecraft:bundle_contents", 13 | "minecraft:firework_explosion", 14 | "minecraft:fireworks", 15 | "minecraft:writable_book_content", 16 | "minecraft:written_book_content", 17 | "minecraft:attribute_modifiers", 18 | "minecraft:trim", 19 | "minecraft:jukebox_playable", 20 | } 21 | -------------------------------------------------------------------------------- /data/registryid/lootconditiontype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var LootConditionType = []string{ 6 | "minecraft:inverted", 7 | "minecraft:any_of", 8 | "minecraft:all_of", 9 | "minecraft:random_chance", 10 | "minecraft:random_chance_with_enchanted_bonus", 11 | "minecraft:entity_properties", 12 | "minecraft:killed_by_player", 13 | "minecraft:entity_scores", 14 | "minecraft:block_state_property", 15 | "minecraft:match_tool", 16 | "minecraft:table_bonus", 17 | "minecraft:survives_explosion", 18 | "minecraft:damage_source_properties", 19 | "minecraft:location_check", 20 | "minecraft:weather_check", 21 | "minecraft:reference", 22 | "minecraft:time_check", 23 | "minecraft:value_check", 24 | "minecraft:enchantment_active_check", 25 | } 26 | -------------------------------------------------------------------------------- /data/registryid/lootfunctiontype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var LootFunctionType = []string{ 6 | "minecraft:set_count", 7 | "minecraft:set_item", 8 | "minecraft:enchant_with_levels", 9 | "minecraft:enchant_randomly", 10 | "minecraft:set_enchantments", 11 | "minecraft:set_custom_data", 12 | "minecraft:set_components", 13 | "minecraft:furnace_smelt", 14 | "minecraft:enchanted_count_increase", 15 | "minecraft:set_damage", 16 | "minecraft:set_attributes", 17 | "minecraft:set_name", 18 | "minecraft:exploration_map", 19 | "minecraft:set_stew_effect", 20 | "minecraft:copy_name", 21 | "minecraft:set_contents", 22 | "minecraft:modify_contents", 23 | "minecraft:filtered", 24 | "minecraft:limit_count", 25 | "minecraft:apply_bonus", 26 | "minecraft:set_loot_table", 27 | "minecraft:explosion_decay", 28 | "minecraft:set_lore", 29 | "minecraft:fill_player_head", 30 | "minecraft:copy_custom_data", 31 | "minecraft:copy_state", 32 | "minecraft:set_banner_pattern", 33 | "minecraft:set_potion", 34 | "minecraft:set_instrument", 35 | "minecraft:reference", 36 | "minecraft:sequence", 37 | "minecraft:copy_components", 38 | "minecraft:set_fireworks", 39 | "minecraft:set_firework_explosion", 40 | "minecraft:set_book_cover", 41 | "minecraft:set_written_book_pages", 42 | "minecraft:set_writable_book_pages", 43 | "minecraft:toggle_tooltips", 44 | "minecraft:set_ominous_bottle_amplifier", 45 | "minecraft:set_custom_model_data", 46 | } 47 | -------------------------------------------------------------------------------- /data/registryid/lootnbtprovidertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var LootNbtProviderType = []string{ 6 | "minecraft:storage", 7 | "minecraft:context", 8 | } 9 | -------------------------------------------------------------------------------- /data/registryid/lootnumberprovidertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var LootNumberProviderType = []string{ 6 | "minecraft:constant", 7 | "minecraft:uniform", 8 | "minecraft:binomial", 9 | "minecraft:score", 10 | "minecraft:storage", 11 | "minecraft:enchantment_level", 12 | } 13 | -------------------------------------------------------------------------------- /data/registryid/lootpoolentrytype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var LootPoolEntryType = []string{ 6 | "minecraft:empty", 7 | "minecraft:item", 8 | "minecraft:loot_table", 9 | "minecraft:dynamic", 10 | "minecraft:tag", 11 | "minecraft:alternatives", 12 | "minecraft:sequence", 13 | "minecraft:group", 14 | } 15 | -------------------------------------------------------------------------------- /data/registryid/lootscoreprovidertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var LootScoreProviderType = []string{ 6 | "minecraft:fixed", 7 | "minecraft:context", 8 | } 9 | -------------------------------------------------------------------------------- /data/registryid/mapdecorationtype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var MapDecorationType = []string{ 6 | "minecraft:player", 7 | "minecraft:frame", 8 | "minecraft:red_marker", 9 | "minecraft:blue_marker", 10 | "minecraft:target_x", 11 | "minecraft:target_point", 12 | "minecraft:player_off_map", 13 | "minecraft:player_off_limits", 14 | "minecraft:mansion", 15 | "minecraft:monument", 16 | "minecraft:banner_white", 17 | "minecraft:banner_orange", 18 | "minecraft:banner_magenta", 19 | "minecraft:banner_light_blue", 20 | "minecraft:banner_yellow", 21 | "minecraft:banner_lime", 22 | "minecraft:banner_pink", 23 | "minecraft:banner_gray", 24 | "minecraft:banner_light_gray", 25 | "minecraft:banner_cyan", 26 | "minecraft:banner_purple", 27 | "minecraft:banner_blue", 28 | "minecraft:banner_brown", 29 | "minecraft:banner_green", 30 | "minecraft:banner_red", 31 | "minecraft:banner_black", 32 | "minecraft:red_x", 33 | "minecraft:village_desert", 34 | "minecraft:village_plains", 35 | "minecraft:village_savanna", 36 | "minecraft:village_snowy", 37 | "minecraft:village_taiga", 38 | "minecraft:jungle_temple", 39 | "minecraft:swamp_hut", 40 | "minecraft:trial_chambers", 41 | } 42 | -------------------------------------------------------------------------------- /data/registryid/menu.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var Menu = []string{ 6 | "minecraft:generic_9x1", 7 | "minecraft:generic_9x2", 8 | "minecraft:generic_9x3", 9 | "minecraft:generic_9x4", 10 | "minecraft:generic_9x5", 11 | "minecraft:generic_9x6", 12 | "minecraft:generic_3x3", 13 | "minecraft:crafter_3x3", 14 | "minecraft:anvil", 15 | "minecraft:beacon", 16 | "minecraft:blast_furnace", 17 | "minecraft:brewing_stand", 18 | "minecraft:crafting", 19 | "minecraft:enchantment", 20 | "minecraft:furnace", 21 | "minecraft:grindstone", 22 | "minecraft:hopper", 23 | "minecraft:lectern", 24 | "minecraft:loom", 25 | "minecraft:merchant", 26 | "minecraft:shulker_box", 27 | "minecraft:smithing", 28 | "minecraft:smoker", 29 | "minecraft:cartography_table", 30 | "minecraft:stonecutter", 31 | } 32 | -------------------------------------------------------------------------------- /data/registryid/mobeffect.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var MobEffect = []string{ 6 | "minecraft:speed", 7 | "minecraft:slowness", 8 | "minecraft:haste", 9 | "minecraft:mining_fatigue", 10 | "minecraft:strength", 11 | "minecraft:instant_health", 12 | "minecraft:instant_damage", 13 | "minecraft:jump_boost", 14 | "minecraft:nausea", 15 | "minecraft:regeneration", 16 | "minecraft:resistance", 17 | "minecraft:fire_resistance", 18 | "minecraft:water_breathing", 19 | "minecraft:invisibility", 20 | "minecraft:blindness", 21 | "minecraft:night_vision", 22 | "minecraft:hunger", 23 | "minecraft:weakness", 24 | "minecraft:poison", 25 | "minecraft:wither", 26 | "minecraft:health_boost", 27 | "minecraft:absorption", 28 | "minecraft:saturation", 29 | "minecraft:glowing", 30 | "minecraft:levitation", 31 | "minecraft:luck", 32 | "minecraft:unluck", 33 | "minecraft:slow_falling", 34 | "minecraft:conduit_power", 35 | "minecraft:dolphins_grace", 36 | "minecraft:bad_omen", 37 | "minecraft:hero_of_the_village", 38 | "minecraft:darkness", 39 | "minecraft:trial_omen", 40 | "minecraft:raid_omen", 41 | "minecraft:wind_charged", 42 | "minecraft:weaving", 43 | "minecraft:oozing", 44 | "minecraft:infested", 45 | } 46 | -------------------------------------------------------------------------------- /data/registryid/numberformattype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var NumberFormatType = []string{ 6 | "minecraft:blank", 7 | "minecraft:styled", 8 | "minecraft:fixed", 9 | } 10 | -------------------------------------------------------------------------------- /data/registryid/pointofinteresttype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var PointOfInterestType = []string{ 6 | "minecraft:armorer", 7 | "minecraft:butcher", 8 | "minecraft:cartographer", 9 | "minecraft:cleric", 10 | "minecraft:farmer", 11 | "minecraft:fisherman", 12 | "minecraft:fletcher", 13 | "minecraft:leatherworker", 14 | "minecraft:librarian", 15 | "minecraft:mason", 16 | "minecraft:shepherd", 17 | "minecraft:toolsmith", 18 | "minecraft:weaponsmith", 19 | "minecraft:home", 20 | "minecraft:meeting", 21 | "minecraft:beehive", 22 | "minecraft:bee_nest", 23 | "minecraft:nether_portal", 24 | "minecraft:lodestone", 25 | "minecraft:lightning_rod", 26 | } 27 | -------------------------------------------------------------------------------- /data/registryid/positionsourcetype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var PositionSourceType = []string{ 6 | "minecraft:block", 7 | "minecraft:entity", 8 | } 9 | -------------------------------------------------------------------------------- /data/registryid/posruletest.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var PosRuleTest = []string{ 6 | "minecraft:always_true", 7 | "minecraft:linear_pos", 8 | "minecraft:axis_aligned_linear_pos", 9 | } 10 | -------------------------------------------------------------------------------- /data/registryid/potion.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var Potion = []string{ 6 | "minecraft:water", 7 | "minecraft:mundane", 8 | "minecraft:thick", 9 | "minecraft:awkward", 10 | "minecraft:night_vision", 11 | "minecraft:long_night_vision", 12 | "minecraft:invisibility", 13 | "minecraft:long_invisibility", 14 | "minecraft:leaping", 15 | "minecraft:long_leaping", 16 | "minecraft:strong_leaping", 17 | "minecraft:fire_resistance", 18 | "minecraft:long_fire_resistance", 19 | "minecraft:swiftness", 20 | "minecraft:long_swiftness", 21 | "minecraft:strong_swiftness", 22 | "minecraft:slowness", 23 | "minecraft:long_slowness", 24 | "minecraft:strong_slowness", 25 | "minecraft:turtle_master", 26 | "minecraft:long_turtle_master", 27 | "minecraft:strong_turtle_master", 28 | "minecraft:water_breathing", 29 | "minecraft:long_water_breathing", 30 | "minecraft:healing", 31 | "minecraft:strong_healing", 32 | "minecraft:harming", 33 | "minecraft:strong_harming", 34 | "minecraft:poison", 35 | "minecraft:long_poison", 36 | "minecraft:strong_poison", 37 | "minecraft:regeneration", 38 | "minecraft:long_regeneration", 39 | "minecraft:strong_regeneration", 40 | "minecraft:strength", 41 | "minecraft:long_strength", 42 | "minecraft:strong_strength", 43 | "minecraft:weakness", 44 | "minecraft:long_weakness", 45 | "minecraft:luck", 46 | "minecraft:slow_falling", 47 | "minecraft:long_slow_falling", 48 | "minecraft:wind_charged", 49 | "minecraft:weaving", 50 | "minecraft:oozing", 51 | "minecraft:infested", 52 | } 53 | -------------------------------------------------------------------------------- /data/registryid/recipeserializer.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var RecipeSerializer = []string{ 6 | "minecraft:crafting_shaped", 7 | "minecraft:crafting_shapeless", 8 | "minecraft:crafting_special_armordye", 9 | "minecraft:crafting_special_bookcloning", 10 | "minecraft:crafting_special_mapcloning", 11 | "minecraft:crafting_special_mapextending", 12 | "minecraft:crafting_special_firework_rocket", 13 | "minecraft:crafting_special_firework_star", 14 | "minecraft:crafting_special_firework_star_fade", 15 | "minecraft:crafting_special_tippedarrow", 16 | "minecraft:crafting_special_bannerduplicate", 17 | "minecraft:crafting_special_shielddecoration", 18 | "minecraft:crafting_special_shulkerboxcoloring", 19 | "minecraft:crafting_special_suspiciousstew", 20 | "minecraft:crafting_special_repairitem", 21 | "minecraft:smelting", 22 | "minecraft:blasting", 23 | "minecraft:smoking", 24 | "minecraft:campfire_cooking", 25 | "minecraft:stonecutting", 26 | "minecraft:smithing_transform", 27 | "minecraft:smithing_trim", 28 | "minecraft:crafting_decorated_pot", 29 | } 30 | -------------------------------------------------------------------------------- /data/registryid/recipetype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var RecipeType = []string{ 6 | "minecraft:crafting", 7 | "minecraft:smelting", 8 | "minecraft:blasting", 9 | "minecraft:smoking", 10 | "minecraft:campfire_cooking", 11 | "minecraft:stonecutting", 12 | "minecraft:smithing", 13 | } 14 | -------------------------------------------------------------------------------- /data/registryid/ruleblockentitymodifier.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var RuleBlockEntityModifier = []string{ 6 | "minecraft:clear", 7 | "minecraft:passthrough", 8 | "minecraft:append_static", 9 | "minecraft:append_loot", 10 | } 11 | -------------------------------------------------------------------------------- /data/registryid/ruletest.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var RuleTest = []string{ 6 | "minecraft:always_true", 7 | "minecraft:block_match", 8 | "minecraft:blockstate_match", 9 | "minecraft:tag_match", 10 | "minecraft:random_block_match", 11 | "minecraft:random_blockstate_match", 12 | } 13 | -------------------------------------------------------------------------------- /data/registryid/schedule.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var Schedule = []string{ 6 | "minecraft:empty", 7 | "minecraft:simple", 8 | "minecraft:villager_baby", 9 | "minecraft:villager_default", 10 | } 11 | -------------------------------------------------------------------------------- /data/registryid/sensortype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var SensorType = []string{ 6 | "minecraft:dummy", 7 | "minecraft:nearest_items", 8 | "minecraft:nearest_living_entities", 9 | "minecraft:nearest_players", 10 | "minecraft:nearest_bed", 11 | "minecraft:hurt_by", 12 | "minecraft:villager_hostiles", 13 | "minecraft:villager_babies", 14 | "minecraft:secondary_pois", 15 | "minecraft:golem_detected", 16 | "minecraft:armadillo_scare_detected", 17 | "minecraft:piglin_specific_sensor", 18 | "minecraft:piglin_brute_specific_sensor", 19 | "minecraft:hoglin_specific_sensor", 20 | "minecraft:nearest_adult", 21 | "minecraft:axolotl_attackables", 22 | "minecraft:axolotl_temptations", 23 | "minecraft:goat_temptations", 24 | "minecraft:frog_temptations", 25 | "minecraft:camel_temptations", 26 | "minecraft:armadillo_temptations", 27 | "minecraft:frog_attackables", 28 | "minecraft:is_in_water", 29 | "minecraft:warden_entity_sensor", 30 | "minecraft:sniffer_temptations", 31 | "minecraft:breeze_attack_entity_sensor", 32 | } 33 | -------------------------------------------------------------------------------- /data/registryid/stattype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var StatType = []string{ 6 | "minecraft:mined", 7 | "minecraft:crafted", 8 | "minecraft:used", 9 | "minecraft:broken", 10 | "minecraft:picked_up", 11 | "minecraft:dropped", 12 | "minecraft:killed", 13 | "minecraft:killed_by", 14 | "minecraft:custom", 15 | } 16 | -------------------------------------------------------------------------------- /data/registryid/triggertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var TriggerType = []string{ 6 | "minecraft:impossible", 7 | "minecraft:player_killed_entity", 8 | "minecraft:entity_killed_player", 9 | "minecraft:enter_block", 10 | "minecraft:inventory_changed", 11 | "minecraft:recipe_unlocked", 12 | "minecraft:player_hurt_entity", 13 | "minecraft:entity_hurt_player", 14 | "minecraft:enchanted_item", 15 | "minecraft:filled_bucket", 16 | "minecraft:brewed_potion", 17 | "minecraft:construct_beacon", 18 | "minecraft:used_ender_eye", 19 | "minecraft:summoned_entity", 20 | "minecraft:bred_animals", 21 | "minecraft:location", 22 | "minecraft:slept_in_bed", 23 | "minecraft:cured_zombie_villager", 24 | "minecraft:villager_trade", 25 | "minecraft:item_durability_changed", 26 | "minecraft:levitation", 27 | "minecraft:changed_dimension", 28 | "minecraft:tick", 29 | "minecraft:tame_animal", 30 | "minecraft:placed_block", 31 | "minecraft:consume_item", 32 | "minecraft:effects_changed", 33 | "minecraft:used_totem", 34 | "minecraft:nether_travel", 35 | "minecraft:fishing_rod_hooked", 36 | "minecraft:channeled_lightning", 37 | "minecraft:shot_crossbow", 38 | "minecraft:killed_by_crossbow", 39 | "minecraft:hero_of_the_village", 40 | "minecraft:voluntary_exile", 41 | "minecraft:slide_down_block", 42 | "minecraft:bee_nest_destroyed", 43 | "minecraft:target_hit", 44 | "minecraft:item_used_on_block", 45 | "minecraft:default_block_use", 46 | "minecraft:any_block_use", 47 | "minecraft:player_generates_container_loot", 48 | "minecraft:thrown_item_picked_up_by_entity", 49 | "minecraft:thrown_item_picked_up_by_player", 50 | "minecraft:player_interacted_with_entity", 51 | "minecraft:started_riding", 52 | "minecraft:lightning_strike", 53 | "minecraft:using_item", 54 | "minecraft:fall_from_height", 55 | "minecraft:ride_entity_in_lava", 56 | "minecraft:kill_mob_near_sculk_catalyst", 57 | "minecraft:allay_drop_item_on_block", 58 | "minecraft:avoid_vibration", 59 | "minecraft:recipe_crafted", 60 | "minecraft:crafter_recipe_crafted", 61 | "minecraft:fall_after_explosion", 62 | } 63 | -------------------------------------------------------------------------------- /data/registryid/villagerprofession.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var VillagerProfession = []string{ 6 | "minecraft:none", 7 | "minecraft:armorer", 8 | "minecraft:butcher", 9 | "minecraft:cartographer", 10 | "minecraft:cleric", 11 | "minecraft:farmer", 12 | "minecraft:fisherman", 13 | "minecraft:fletcher", 14 | "minecraft:leatherworker", 15 | "minecraft:librarian", 16 | "minecraft:mason", 17 | "minecraft:nitwit", 18 | "minecraft:shepherd", 19 | "minecraft:toolsmith", 20 | "minecraft:weaponsmith", 21 | } 22 | -------------------------------------------------------------------------------- /data/registryid/villagertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var VillagerType = []string{ 6 | "minecraft:desert", 7 | "minecraft:jungle", 8 | "minecraft:plains", 9 | "minecraft:savanna", 10 | "minecraft:snow", 11 | "minecraft:swamp", 12 | "minecraft:taiga", 13 | } 14 | -------------------------------------------------------------------------------- /data/registryid/worldgen_biomesource.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenBiomeSource = []string{ 6 | "minecraft:fixed", 7 | "minecraft:multi_noise", 8 | "minecraft:checkerboard", 9 | "minecraft:the_end", 10 | } 11 | -------------------------------------------------------------------------------- /data/registryid/worldgen_blockstateprovidertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenBlockStateProviderType = []string{ 6 | "minecraft:simple_state_provider", 7 | "minecraft:weighted_state_provider", 8 | "minecraft:noise_threshold_provider", 9 | "minecraft:noise_provider", 10 | "minecraft:dual_noise_provider", 11 | "minecraft:rotated_block_provider", 12 | "minecraft:randomized_int_state_provider", 13 | } 14 | -------------------------------------------------------------------------------- /data/registryid/worldgen_carver.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenCarver = []string{ 6 | "minecraft:cave", 7 | "minecraft:nether_cave", 8 | "minecraft:canyon", 9 | } 10 | -------------------------------------------------------------------------------- /data/registryid/worldgen_chunkgenerator.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenChunkGenerator = []string{ 6 | "minecraft:noise", 7 | "minecraft:flat", 8 | "minecraft:debug", 9 | } 10 | -------------------------------------------------------------------------------- /data/registryid/worldgen_densityfunctiontype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenDensityFunctionType = []string{ 6 | "minecraft:blend_alpha", 7 | "minecraft:blend_offset", 8 | "minecraft:beardifier", 9 | "minecraft:old_blended_noise", 10 | "minecraft:interpolated", 11 | "minecraft:flat_cache", 12 | "minecraft:cache_2d", 13 | "minecraft:cache_once", 14 | "minecraft:cache_all_in_cell", 15 | "minecraft:noise", 16 | "minecraft:end_islands", 17 | "minecraft:weird_scaled_sampler", 18 | "minecraft:shifted_noise", 19 | "minecraft:range_choice", 20 | "minecraft:shift_a", 21 | "minecraft:shift_b", 22 | "minecraft:shift", 23 | "minecraft:blend_density", 24 | "minecraft:clamp", 25 | "minecraft:abs", 26 | "minecraft:square", 27 | "minecraft:cube", 28 | "minecraft:half_negative", 29 | "minecraft:quarter_negative", 30 | "minecraft:squeeze", 31 | "minecraft:add", 32 | "minecraft:mul", 33 | "minecraft:min", 34 | "minecraft:max", 35 | "minecraft:spline", 36 | "minecraft:constant", 37 | "minecraft:y_clamped_gradient", 38 | } 39 | -------------------------------------------------------------------------------- /data/registryid/worldgen_feature.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenFeature = []string{ 6 | "minecraft:no_op", 7 | "minecraft:tree", 8 | "minecraft:flower", 9 | "minecraft:no_bonemeal_flower", 10 | "minecraft:random_patch", 11 | "minecraft:block_pile", 12 | "minecraft:spring_feature", 13 | "minecraft:chorus_plant", 14 | "minecraft:replace_single_block", 15 | "minecraft:void_start_platform", 16 | "minecraft:desert_well", 17 | "minecraft:fossil", 18 | "minecraft:huge_red_mushroom", 19 | "minecraft:huge_brown_mushroom", 20 | "minecraft:ice_spike", 21 | "minecraft:glowstone_blob", 22 | "minecraft:freeze_top_layer", 23 | "minecraft:vines", 24 | "minecraft:block_column", 25 | "minecraft:vegetation_patch", 26 | "minecraft:waterlogged_vegetation_patch", 27 | "minecraft:root_system", 28 | "minecraft:multiface_growth", 29 | "minecraft:underwater_magma", 30 | "minecraft:monster_room", 31 | "minecraft:blue_ice", 32 | "minecraft:iceberg", 33 | "minecraft:forest_rock", 34 | "minecraft:disk", 35 | "minecraft:lake", 36 | "minecraft:ore", 37 | "minecraft:end_platform", 38 | "minecraft:end_spike", 39 | "minecraft:end_island", 40 | "minecraft:end_gateway", 41 | "minecraft:seagrass", 42 | "minecraft:kelp", 43 | "minecraft:coral_tree", 44 | "minecraft:coral_mushroom", 45 | "minecraft:coral_claw", 46 | "minecraft:sea_pickle", 47 | "minecraft:simple_block", 48 | "minecraft:bamboo", 49 | "minecraft:huge_fungus", 50 | "minecraft:nether_forest_vegetation", 51 | "minecraft:weeping_vines", 52 | "minecraft:twisting_vines", 53 | "minecraft:basalt_columns", 54 | "minecraft:delta_feature", 55 | "minecraft:netherrack_replace_blobs", 56 | "minecraft:fill_layer", 57 | "minecraft:bonus_chest", 58 | "minecraft:basalt_pillar", 59 | "minecraft:scattered_ore", 60 | "minecraft:random_selector", 61 | "minecraft:simple_random_selector", 62 | "minecraft:random_boolean_selector", 63 | "minecraft:geode", 64 | "minecraft:dripstone_cluster", 65 | "minecraft:large_dripstone", 66 | "minecraft:pointed_dripstone", 67 | "minecraft:sculk_patch", 68 | } 69 | -------------------------------------------------------------------------------- /data/registryid/worldgen_featuresizetype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenFeatureSizeType = []string{ 6 | "minecraft:two_layers_feature_size", 7 | "minecraft:three_layers_feature_size", 8 | } 9 | -------------------------------------------------------------------------------- /data/registryid/worldgen_foliageplacertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenFoliagePlacerType = []string{ 6 | "minecraft:blob_foliage_placer", 7 | "minecraft:spruce_foliage_placer", 8 | "minecraft:pine_foliage_placer", 9 | "minecraft:acacia_foliage_placer", 10 | "minecraft:bush_foliage_placer", 11 | "minecraft:fancy_foliage_placer", 12 | "minecraft:jungle_foliage_placer", 13 | "minecraft:mega_pine_foliage_placer", 14 | "minecraft:dark_oak_foliage_placer", 15 | "minecraft:random_spread_foliage_placer", 16 | "minecraft:cherry_foliage_placer", 17 | } 18 | -------------------------------------------------------------------------------- /data/registryid/worldgen_materialcondition.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenMaterialCondition = []string{ 6 | "minecraft:biome", 7 | "minecraft:noise_threshold", 8 | "minecraft:vertical_gradient", 9 | "minecraft:y_above", 10 | "minecraft:water", 11 | "minecraft:temperature", 12 | "minecraft:steep", 13 | "minecraft:not", 14 | "minecraft:hole", 15 | "minecraft:above_preliminary_surface", 16 | "minecraft:stone_depth", 17 | } 18 | -------------------------------------------------------------------------------- /data/registryid/worldgen_materialrule.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenMaterialRule = []string{ 6 | "minecraft:bandlands", 7 | "minecraft:block", 8 | "minecraft:sequence", 9 | "minecraft:condition", 10 | } 11 | -------------------------------------------------------------------------------- /data/registryid/worldgen_placementmodifiertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenPlacementModifierType = []string{ 6 | "minecraft:block_predicate_filter", 7 | "minecraft:rarity_filter", 8 | "minecraft:surface_relative_threshold_filter", 9 | "minecraft:surface_water_depth_filter", 10 | "minecraft:biome", 11 | "minecraft:count", 12 | "minecraft:noise_based_count", 13 | "minecraft:noise_threshold_count", 14 | "minecraft:count_on_every_layer", 15 | "minecraft:environment_scan", 16 | "minecraft:heightmap", 17 | "minecraft:height_range", 18 | "minecraft:in_square", 19 | "minecraft:random_offset", 20 | "minecraft:carving_mask", 21 | "minecraft:fixed_placement", 22 | } 23 | -------------------------------------------------------------------------------- /data/registryid/worldgen_poolaliasbinding.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenPoolAliasBinding = []string{ 6 | "minecraft:random", 7 | "minecraft:random_group", 8 | "minecraft:direct", 9 | } 10 | -------------------------------------------------------------------------------- /data/registryid/worldgen_rootplacertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenRootPlacerType = []string{ 6 | "minecraft:mangrove_root_placer", 7 | } 8 | -------------------------------------------------------------------------------- /data/registryid/worldgen_structurepiece.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenStructurePiece = []string{ 6 | "minecraft:mscorridor", 7 | "minecraft:mscrossing", 8 | "minecraft:msroom", 9 | "minecraft:msstairs", 10 | "minecraft:nebcr", 11 | "minecraft:nebef", 12 | "minecraft:nebs", 13 | "minecraft:neccs", 14 | "minecraft:nectb", 15 | "minecraft:nece", 16 | "minecraft:nescsc", 17 | "minecraft:nesclt", 18 | "minecraft:nesc", 19 | "minecraft:nescrt", 20 | "minecraft:necsr", 21 | "minecraft:nemt", 22 | "minecraft:nerc", 23 | "minecraft:nesr", 24 | "minecraft:nestart", 25 | "minecraft:shcc", 26 | "minecraft:shfc", 27 | "minecraft:sh5c", 28 | "minecraft:shlt", 29 | "minecraft:shli", 30 | "minecraft:shpr", 31 | "minecraft:shph", 32 | "minecraft:shrt", 33 | "minecraft:shrc", 34 | "minecraft:shsd", 35 | "minecraft:shstart", 36 | "minecraft:shs", 37 | "minecraft:shssd", 38 | "minecraft:tejp", 39 | "minecraft:orp", 40 | "minecraft:iglu", 41 | "minecraft:rupo", 42 | "minecraft:tesh", 43 | "minecraft:tedp", 44 | "minecraft:omb", 45 | "minecraft:omcr", 46 | "minecraft:omdxr", 47 | "minecraft:omdxyr", 48 | "minecraft:omdyr", 49 | "minecraft:omdyzr", 50 | "minecraft:omdzr", 51 | "minecraft:omentry", 52 | "minecraft:ompenthouse", 53 | "minecraft:omsimple", 54 | "minecraft:omsimplet", 55 | "minecraft:omwr", 56 | "minecraft:ecp", 57 | "minecraft:wmp", 58 | "minecraft:btp", 59 | "minecraft:shipwreck", 60 | "minecraft:nefos", 61 | "minecraft:jigsaw", 62 | } 63 | -------------------------------------------------------------------------------- /data/registryid/worldgen_structureplacement.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenStructurePlacement = []string{ 6 | "minecraft:random_spread", 7 | "minecraft:concentric_rings", 8 | } 9 | -------------------------------------------------------------------------------- /data/registryid/worldgen_structurepoolelement.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenStructurePoolElement = []string{ 6 | "minecraft:single_pool_element", 7 | "minecraft:list_pool_element", 8 | "minecraft:feature_pool_element", 9 | "minecraft:empty_pool_element", 10 | "minecraft:legacy_single_pool_element", 11 | } 12 | -------------------------------------------------------------------------------- /data/registryid/worldgen_structureprocessor.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenStructureProcessor = []string{ 6 | "minecraft:block_ignore", 7 | "minecraft:block_rot", 8 | "minecraft:gravity", 9 | "minecraft:jigsaw_replacement", 10 | "minecraft:rule", 11 | "minecraft:nop", 12 | "minecraft:block_age", 13 | "minecraft:blackstone_replace", 14 | "minecraft:lava_submerged_block", 15 | "minecraft:protected_blocks", 16 | "minecraft:capped", 17 | } 18 | -------------------------------------------------------------------------------- /data/registryid/worldgen_structuretype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenStructureType = []string{ 6 | "minecraft:buried_treasure", 7 | "minecraft:desert_pyramid", 8 | "minecraft:end_city", 9 | "minecraft:fortress", 10 | "minecraft:igloo", 11 | "minecraft:jigsaw", 12 | "minecraft:jungle_temple", 13 | "minecraft:mineshaft", 14 | "minecraft:nether_fossil", 15 | "minecraft:ocean_monument", 16 | "minecraft:ocean_ruin", 17 | "minecraft:ruined_portal", 18 | "minecraft:shipwreck", 19 | "minecraft:stronghold", 20 | "minecraft:swamp_hut", 21 | "minecraft:woodland_mansion", 22 | } 23 | -------------------------------------------------------------------------------- /data/registryid/worldgen_treedecoratortype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenTreeDecoratorType = []string{ 6 | "minecraft:trunk_vine", 7 | "minecraft:leave_vine", 8 | "minecraft:cocoa", 9 | "minecraft:beehive", 10 | "minecraft:alter_ground", 11 | "minecraft:attached_to_leaves", 12 | } 13 | -------------------------------------------------------------------------------- /data/registryid/worldgen_trunkplacertype.go: -------------------------------------------------------------------------------- 1 | // Code generated by data/registry/generate.go; DO NOT EDIT. 2 | 3 | package registryid 4 | 5 | var WorldgenTrunkPlacerType = []string{ 6 | "minecraft:straight_trunk_placer", 7 | "minecraft:forking_trunk_placer", 8 | "minecraft:giant_trunk_placer", 9 | "minecraft:mega_jungle_trunk_placer", 10 | "minecraft:dark_oak_trunk_placer", 11 | "minecraft:fancy_trunk_placer", 12 | "minecraft:bending_trunk_placer", 13 | "minecraft:upwards_branching_trunk_placer", 14 | "minecraft:cherry_trunk_placer", 15 | } 16 | -------------------------------------------------------------------------------- /examples/mcping/README.md: -------------------------------------------------------------------------------- 1 | # mcping 2 | 3 | A ping tool for Minecraft: Java Edition. 4 | 适用于Minecraft: Java Edition的ping工具。 5 | 6 | Install with go tools: 7 | ```go get -u github.com/Tnze/go-mc/cmd/mcping``` 8 | `$GOPATH/bin` should in your `$PATH`. 9 | 10 | Install with Homebrew: 11 | ```brew tap Tnze/tap && brew install mcping``` 12 | 13 | Usage: 14 | ```mcping [:port]``` 15 | -------------------------------------------------------------------------------- /examples/minimal/minimal.go: -------------------------------------------------------------------------------- 1 | // A minimal bot example that could join minecraft server as a client. 2 | // For a complete bot example, see examples/daze. 3 | package main 4 | 5 | import ( 6 | "errors" 7 | "flag" 8 | "log" 9 | 10 | "github.com/Tnze/go-mc/bot" 11 | "github.com/Tnze/go-mc/bot/basic" 12 | "github.com/Tnze/go-mc/chat" 13 | //_ "github.com/Tnze/go-mc/data/lang/zh-cn" 14 | ) 15 | 16 | var ( 17 | address = flag.String("address", "127.0.0.1:25565", "The server address") 18 | name = flag.String("name", "Daze", "The player's name") 19 | playerID = flag.String("uuid", "", "The player's UUID") 20 | accessToken = flag.String("token", "", "AccessToken") 21 | ) 22 | 23 | var ( 24 | client *bot.Client 25 | player *basic.Player 26 | ) 27 | 28 | func main() { 29 | flag.Parse() 30 | client = bot.NewClient() 31 | client.Auth = bot.Auth{ 32 | Name: *name, 33 | UUID: *playerID, 34 | AsTk: *accessToken, 35 | } 36 | player = basic.NewPlayer(client, basic.DefaultSettings, basic.EventsListener{ 37 | Disconnect: onDisconnect, 38 | }) 39 | 40 | // Login 41 | err := client.JoinServer(*address) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | log.Println("Login success") 46 | 47 | // JoinGame 48 | for { 49 | if err = client.HandleGame(); err == nil { 50 | panic("HandleGame never return nil") 51 | } 52 | 53 | if err2 := new(bot.PacketHandlerError); errors.As(err, err2) { 54 | if err := new(DisconnectErr); errors.As(err2, err) { 55 | log.Print("Disconnect, reason: ", err.Reason) 56 | return 57 | } else { 58 | // normal packet handler error, ignore and continue. 59 | log.Print(err2) 60 | } 61 | } else { 62 | // if the error is not a PacketHandlerError, the connection is broken. 63 | // stop the program 64 | log.Fatal(err) 65 | } 66 | } 67 | } 68 | 69 | type DisconnectErr struct { 70 | Reason chat.Message 71 | } 72 | 73 | func (d DisconnectErr) Error() string { 74 | return "disconnect: " + d.Reason.ClearString() 75 | } 76 | 77 | func onDisconnect(reason chat.Message) error { 78 | // return an error value so that we can stop main loop 79 | return DisconnectErr{Reason: reason} 80 | } 81 | -------------------------------------------------------------------------------- /examples/playerdataconvert/advancements.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/google/uuid" 10 | ) 11 | 12 | func readAdvancements(dir string, m map[uuid.UUID]UserCache) { 13 | entries, err := os.ReadDir(dir) 14 | if err != nil { 15 | fmt.Fprintf(os.Stderr, "Failed to open playerdata folder: %v", err) 16 | return 17 | } 18 | for _, files := range entries { 19 | filename := files.Name() 20 | if ext := filepath.Ext(filename); ext != ".json" { 21 | fmt.Fprintf(os.Stderr, "Unkown file type: %s\n", ext) 22 | continue 23 | } 24 | 25 | // Parse old UUID from filename 26 | oldID, err := uuid.Parse(strings.TrimSuffix(filename, ".json")) 27 | if err != nil { 28 | fmt.Fprintf(os.Stderr, "Unable to parse filename as uuid: %v\n", err) 29 | continue 30 | } 31 | 32 | if ver := oldID.Version(); ver != 3 { // v3 is for offline players 33 | fmt.Printf("Ignoring UUID: %v version: %d\n", oldID, ver) 34 | continue 35 | } 36 | 37 | newUser, ok := m[oldID] 38 | if !ok { 39 | fmt.Printf("Skip user: %v\n", oldID) 40 | continue 41 | } 42 | 43 | content, err := os.ReadFile(filepath.Join(dir, filename)) 44 | if err != nil { 45 | fmt.Fprintf(os.Stderr, "Failed to read json file: %v\n", err) 46 | continue 47 | } 48 | 49 | newFile := newUser.UUID.String() + ".json" 50 | err = os.WriteFile(filepath.Join(dir, newFile), content, 0o666) 51 | if err != nil { 52 | fmt.Fprintf(os.Stderr, "Failed to write json file: %v\n", err) 53 | continue 54 | } 55 | 56 | fmt.Printf("Converted advancement file: %s\n", newFile) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/playerdataconvert/utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | 9 | "github.com/google/uuid" 10 | ) 11 | 12 | func usernameToUUID(name string) (string, uuid.UUID, error) { 13 | var id uuid.UUID 14 | resp, err := http.Get("https://api.mojang.com/users/profiles/minecraft/" + name) 15 | if err != nil { 16 | return "", id, err 17 | } 18 | 19 | var body struct { 20 | Name string `json:"name"` 21 | ID string `json:"id"` 22 | } 23 | err = json.NewDecoder(resp.Body).Decode(&body) 24 | if err != nil { 25 | return "", id, err 26 | } 27 | 28 | id, err = uuid.Parse(body.ID) 29 | return body.Name, id, err 30 | } 31 | 32 | func intArrayToUUID(uuidInts []int32) (id uuid.UUID, err error) { 33 | if uuidLen := len(uuidInts); uuidLen != 4 { 34 | err = fmt.Errorf("invalid UUID len: %d * int32", uuidLen) 35 | return 36 | } 37 | for i, v := range uuidInts { 38 | binary.BigEndian.PutUint32(id[i*4:], uint32(v)) 39 | } 40 | return 41 | } 42 | 43 | func uuidToIntArray(id uuid.UUID) (ints [4]int32) { 44 | for i := range ints { 45 | ints[i] = int32(binary.BigEndian.Uint32(id[i*4:])) 46 | } 47 | return 48 | } 49 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Tnze/go-mc 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 7 | github.com/iancoleman/strcase v0.2.0 8 | golang.org/x/exp v0.0.0-20230321023759-10a507213a29 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 2 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 3 | github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= 4 | github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 5 | golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= 6 | golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 7 | -------------------------------------------------------------------------------- /internal/generateutils/utils.go: -------------------------------------------------------------------------------- 1 | package generateutils 2 | 3 | import ( 4 | "strings" 5 | "unicode" 6 | ) 7 | 8 | func UpperTheFirst(word string) string { 9 | runes := []rune(word) 10 | if len(runes) > 0 { 11 | runes[0] = unicode.ToUpper(runes[0]) 12 | } 13 | return string(runes) 14 | } 15 | 16 | func ToGoTypeName(name string) string { 17 | name = strings.TrimPrefix(name, "minecraft:") 18 | words := strings.Split(name, "_") 19 | for i := range words { 20 | words[i] = UpperTheFirst(words[i]) 21 | } 22 | return strings.Join(words, "") 23 | } 24 | -------------------------------------------------------------------------------- /level/bitstorage_test.go: -------------------------------------------------------------------------------- 1 | package level 2 | 3 | import ( 4 | "math/bits" 5 | "reflect" 6 | "testing" 7 | 8 | pk "github.com/Tnze/go-mc/net/packet" 9 | ) 10 | 11 | var ( 12 | data = []uint64{0x0020863148418841, 0x01018A7260F68C87} 13 | want = []int{1, 2, 2, 3, 4, 4, 5, 6, 6, 4, 8, 0, 7, 4, 3, 13, 15, 16, 9, 14, 10, 12, 0, 2} 14 | ) 15 | 16 | func TestBitStorage_Get(t *testing.T) { 17 | bs := NewBitStorage(5, 24, data) 18 | for i := 0; i < 24; i++ { 19 | if got := bs.Get(i); got != want[i] { 20 | t.Errorf("Decode error, got: %d but expected: %d", got, want[i]) 21 | } 22 | } 23 | } 24 | 25 | func TestBitStorage_Set(t *testing.T) { 26 | bs := NewBitStorage(5, 24, nil) 27 | for i := 0; i < 24; i++ { 28 | bs.Set(i, want[i]) 29 | } 30 | if !reflect.DeepEqual(bs.data, data) { 31 | t.Errorf("Encode error, got %v but expected: %v", bs.data, data) 32 | } 33 | } 34 | 35 | func ExampleNewBitStorage_heightmaps() { 36 | // Create a BitStorage 37 | bs := NewBitStorage(bits.Len(256), 16*16, nil) 38 | // Fill your data 39 | for i := 0; i < 16; i++ { 40 | for j := 0; j < 16; j++ { 41 | bs.Set(i*16+j, 0) 42 | } 43 | } 44 | // Encode as NBT, and this is ready for packet.Marshal 45 | type HeightMaps struct { 46 | MotionBlocking []uint64 `nbt:"MOTION_BLOCKING"` 47 | } 48 | _ = pk.NBT(HeightMaps{bs.Raw()}) 49 | } 50 | -------------------------------------------------------------------------------- /level/block/block.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | _ "embed" 7 | "fmt" 8 | "math/bits" 9 | 10 | "github.com/Tnze/go-mc/nbt" 11 | ) 12 | 13 | type Block interface { 14 | ID() string 15 | } 16 | 17 | // This file stores all possible block states into a TAG_List with gzip compressed. 18 | // 19 | //go:generate go run ./generator/blocks/main.go 20 | //go:embed block_states.nbt 21 | var blockStates []byte 22 | 23 | var ( 24 | ToStateID map[Block]StateID 25 | StateList []Block 26 | ) 27 | 28 | // BitsPerBlock indicates how many bits are needed to represent all possible 29 | // block states. This value is used to determine the size of the global palette. 30 | var BitsPerBlock int 31 | 32 | type ( 33 | StateID int 34 | State struct { 35 | Name string 36 | Properties nbt.RawMessage 37 | } 38 | ) 39 | 40 | func (s *State) Block() (Block, error) { 41 | // Get an empty Block object 42 | block, ok := FromID[s.Name] 43 | if !ok { 44 | return nil, UnknownBlockErr{s.Name} 45 | } 46 | 47 | // Set property values 48 | if s.Properties.Type != nbt.TagEnd { 49 | err := s.Properties.Unmarshal(&block) 50 | if err != nil { 51 | return nil, err 52 | } 53 | } 54 | return block, nil 55 | } 56 | 57 | type UnknownBlockErr struct { 58 | Name string 59 | } 60 | 61 | func (u UnknownBlockErr) Error() string { 62 | return "unknown block: " + u.Name 63 | } 64 | 65 | func init() { 66 | var states []State 67 | // decompress 68 | z, err := gzip.NewReader(bytes.NewReader(blockStates)) 69 | if err != nil { 70 | panic(err) 71 | } 72 | // decode all states 73 | if _, err = nbt.NewDecoder(z).Decode(&states); err != nil { 74 | panic(err) 75 | } 76 | ToStateID = make(map[Block]StateID, len(states)) 77 | StateList = make([]Block, 0, len(states)) 78 | for _, state := range states { 79 | block, err := state.Block() 80 | if err != nil { 81 | panic(err) 82 | } 83 | if _, ok := ToStateID[block]; ok { 84 | panic(fmt.Errorf("state %#v already exists", block)) 85 | } 86 | ToStateID[block] = StateID(len(StateList)) 87 | StateList = append(StateList, block) 88 | } 89 | BitsPerBlock = bits.Len(uint(len(StateList))) 90 | } 91 | -------------------------------------------------------------------------------- /level/block/block_entities.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/level/block/block_entities.nbt -------------------------------------------------------------------------------- /level/block/block_states.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/level/block/block_states.nbt -------------------------------------------------------------------------------- /level/block/blockentity.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | //go:generate go run ./generator/blockentities/main.go 4 | type Entity interface { 5 | ID() string 6 | IsValidBlock(block Block) bool 7 | } 8 | 9 | type ( 10 | FurnaceEntity struct{} 11 | ChestEntity struct{} 12 | TrappedChestEntity struct{} 13 | EnderChestEntity struct{} 14 | JukeboxEntity struct{} 15 | DispenserEntity struct{} 16 | DropperEntity struct{} 17 | SignEntity struct{} 18 | HangingSignEntity struct{} 19 | MobSpawnerEntity struct{} 20 | PistonEntity struct{} 21 | BrewingStandEntity struct{} 22 | EnchantingTableEntity struct{} 23 | EndPortalEntity struct{} 24 | BeaconEntity struct{} 25 | SkullEntity struct{} 26 | DaylightDetectorEntity struct{} 27 | HopperEntity struct{} 28 | ComparatorEntity struct{} 29 | BannerEntity struct{} 30 | StructureBlockEntity struct{} 31 | EndGatewayEntity struct{} 32 | CommandBlockEntity struct{} 33 | ShulkerBoxEntity struct{} 34 | BedEntity struct{} 35 | ConduitEntity struct{} 36 | BarrelEntity struct{} 37 | SmokerEntity struct{} 38 | BlastFurnaceEntity struct{} 39 | LecternEntity struct{} 40 | BellEntity struct{} 41 | JigsawEntity struct{} 42 | CampfireEntity struct{} 43 | BeehiveEntity struct{} 44 | SculkSensorEntity struct{} 45 | CalibratedSculkSensorEntity struct{} 46 | SculkCatalystEntity struct{} 47 | SculkShriekerEntity struct{} 48 | ChiseledBookshelfEntity struct{} 49 | SuspiciousSandEntity struct{} 50 | BrushableBlockEntity struct{} 51 | DecoratedPotEntity struct{} 52 | CrafterEntity struct{} 53 | TrialSpawnerEntity struct{} 54 | VaultEntity struct{} 55 | ) 56 | 57 | type EntityType int32 58 | 59 | var EntityTypes map[string]EntityType 60 | 61 | func init() { 62 | EntityTypes = make(map[string]EntityType, len(EntityList)) 63 | for i, v := range EntityList { 64 | EntityTypes[v.ID()] = EntityType(i) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /level/block/blocks.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/level/block/blocks.nbt -------------------------------------------------------------------------------- /level/block/generator/blockentities/blockentities.go.tmpl: -------------------------------------------------------------------------------- 1 | // Code generated by {{Generator}}; DO NOT EDIT. 2 | 3 | package block 4 | 5 | {{/* type ( 6 | {{- range .}} 7 | {{.Name | ToGoTypeName}}Entity struct {} 8 | {{- end}} 9 | ) */}} 10 | 11 | var EntityList = [...]Entity{ 12 | {{- range .}} 13 | {{.Name | ToGoTypeName}}Entity{}, 14 | {{- end}} 15 | } 16 | 17 | {{- range .}} 18 | func ({{.Name | ToGoTypeName}}Entity) ID() string { return {{.Name | printf "%q"}} } 19 | {{- end}} 20 | 21 | {{range .}} 22 | {{- $v := slice (.Name | ToGoTypeName | ToLower) 0 1 }} 23 | func ({{$v}} {{.Name | ToGoTypeName}}Entity) IsValidBlock(block Block) bool { 24 | {{if eq 1 (len .ValidBlocks)}}return block.ID() == {{index .ValidBlocks 0 | printf "%q"}}{{else}}switch block.ID() { 25 | case {{index .ValidBlocks 0 | printf "%q"}}{{range slice .ValidBlocks 1}}, 26 | {{. | printf "%q"}}{{end}}: 27 | return true 28 | default: 29 | return false 30 | }{{end}} 31 | } 32 | {{end}} 33 | -------------------------------------------------------------------------------- /level/block/generator/blockentities/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | _ "embed" 7 | "go/format" 8 | "log" 9 | "os" 10 | "strings" 11 | "text/template" 12 | 13 | "github.com/Tnze/go-mc/internal/generateutils" 14 | "github.com/Tnze/go-mc/nbt" 15 | ) 16 | 17 | //go:embed blockentities.go.tmpl 18 | var tempSource string 19 | 20 | var temp = template.Must(template. 21 | New("block_template"). 22 | Funcs(template.FuncMap{ 23 | "UpperTheFirst": generateutils.UpperTheFirst, 24 | "ToGoTypeName": generateutils.ToGoTypeName, 25 | "ToLower": strings.ToLower, 26 | "Generator": func() string { return "generator/blockentities/main.go" }, 27 | }). 28 | Parse(tempSource), 29 | ) 30 | 31 | type BlockEntity struct { 32 | Name string 33 | ValidBlocks []string 34 | } 35 | 36 | func main() { 37 | var states []BlockEntity 38 | readBlockEntities(&states) 39 | 40 | // generate go source file 41 | genSourceFile(states) 42 | } 43 | 44 | func readBlockEntities(states *[]BlockEntity) { 45 | f, err := os.Open("block_entities.nbt") 46 | if err != nil { 47 | log.Panic(err) 48 | } 49 | defer f.Close() 50 | 51 | r, err := gzip.NewReader(f) 52 | if err != nil { 53 | log.Panic(err) 54 | } 55 | 56 | // parse the nbt format 57 | if _, err := nbt.NewDecoder(r).Decode(states); err != nil { 58 | log.Panic(err) 59 | } 60 | } 61 | 62 | func genSourceFile(states []BlockEntity) { 63 | var source bytes.Buffer 64 | if err := temp.Execute(&source, states); err != nil { 65 | log.Panic(err) 66 | } 67 | 68 | formattedSource, err := format.Source(source.Bytes()) 69 | if err != nil { 70 | panic(err) 71 | } 72 | 73 | err = os.WriteFile("blockentities.go", formattedSource, 0o666) 74 | if err != nil { 75 | panic(err) 76 | } 77 | log.Print("Generated blockentities.go") 78 | } 79 | -------------------------------------------------------------------------------- /level/block/generator/blocks/blocks.go.tmpl: -------------------------------------------------------------------------------- 1 | // Code generated by {{Generator}}; DO NOT EDIT. 2 | 3 | package block 4 | 5 | type ( 6 | {{- range .}} 7 | {{.Name | ToGoTypeName}} struct { {{- range $key, $elem := .Meta}} 8 | {{$key | UpperTheFirst}} {{$elem | GetType}} `nbt:"{{$key}}"`{{end}} {{- if ne 0 (len .Meta)}} 9 | {{end -}} } 10 | {{- end}} 11 | ) 12 | 13 | {{- range .}} 14 | func ({{.Name | ToGoTypeName}}) ID() string { return {{.Name | printf "%q"}} } 15 | {{- end}} 16 | var FromID = map[string]Block { {{- range .}} 17 | {{.Name | printf "%q"}}: {{.Name | ToGoTypeName}}{},{{end}} 18 | } -------------------------------------------------------------------------------- /level/block/generator/blocks/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | _ "embed" 7 | "go/format" 8 | "log" 9 | "os" 10 | "text/template" 11 | 12 | "github.com/Tnze/go-mc/internal/generateutils" 13 | "github.com/Tnze/go-mc/nbt" 14 | ) 15 | 16 | //go:embed blocks.go.tmpl 17 | var tempSource string 18 | 19 | var temp = template.Must(template. 20 | New("block_template"). 21 | Funcs(template.FuncMap{ 22 | "UpperTheFirst": generateutils.UpperTheFirst, 23 | "ToGoTypeName": generateutils.ToGoTypeName, 24 | "GetType": GetType, 25 | "Generator": func() string { return "generator/blocks/main.go" }, 26 | }). 27 | Parse(tempSource), 28 | ) 29 | 30 | type State struct { 31 | Name string 32 | Meta map[string]string 33 | } 34 | 35 | func main() { 36 | var states []State 37 | readBlockStates(&states) 38 | 39 | // generate go source file 40 | genSourceFile(states) 41 | } 42 | 43 | func readBlockStates(states *[]State) { 44 | // open block_states data file 45 | f, err := os.Open("blocks.nbt") 46 | if err != nil { 47 | log.Panic(err) 48 | } 49 | defer f.Close() 50 | 51 | r, err := gzip.NewReader(f) 52 | if err != nil { 53 | log.Panic(err) 54 | } 55 | 56 | // parse the nbt format 57 | if _, err := nbt.NewDecoder(r).Decode(states); err != nil { 58 | log.Panic(err) 59 | } 60 | } 61 | 62 | func genSourceFile(states []State) { 63 | var source bytes.Buffer 64 | if err := temp.Execute(&source, states); err != nil { 65 | log.Panic(err) 66 | } 67 | 68 | formattedSource, err := format.Source(source.Bytes()) 69 | if err != nil { 70 | panic(err) 71 | } 72 | 73 | err = os.WriteFile("blocks.go", formattedSource, 0o666) 74 | if err != nil { 75 | panic(err) 76 | } 77 | log.Print("Generated blocks.go") 78 | } 79 | 80 | var typeMaps = map[string]string{ 81 | "BooleanProperty": "Boolean", 82 | "DirectionProperty": "Direction", 83 | "IntegerProperty": "Integer", 84 | } 85 | 86 | func GetType(v string) string { 87 | if mapped, ok := typeMaps[v]; ok { 88 | return mapped 89 | } 90 | return v 91 | } 92 | -------------------------------------------------------------------------------- /level/block/generator/properties/properties_enum.go.tmpl: -------------------------------------------------------------------------------- 1 | // Code generated by {{Generator}}; DO NOT EDIT. 2 | 3 | package block 4 | 5 | import ( 6 | "errors" 7 | "strconv" 8 | ) 9 | {{range $prop := .}} 10 | type {{.Name}} byte 11 | 12 | const ( 13 | {{- range $index, $element := .Values}} 14 | {{if not $prop.TrimPrefix}}{{$prop.Name}}{{end}}{{$element | UpperTheFirst}}{{if eq $index 0 }} {{$prop.Name}} = iota{{end}} 15 | {{- end}} 16 | ) 17 | 18 | {{- $v := slice (.Name | ToLower) 0 1 }} 19 | var str{{.Name}} = [...]string{ {{- range $index, $elem := .Values }}{{$elem | printf "%q"}}{{if ne $index (len $prop.Values)}}, {{end}}{{end -}} } 20 | 21 | func ({{$v}} {{.Name}}) String() string { 22 | if int({{$v}}) < len(str{{.Name}}) { 23 | return str{{.Name}}[{{$v}}] 24 | } 25 | return "invalid {{.Name}}" 26 | } 27 | 28 | func ({{$v}} {{.Name}}) MarshalText() (text []byte, err error) { 29 | if int({{$v}}) < len(str{{.Name}}) { 30 | return []byte(str{{.Name}}[{{$v}}]), nil 31 | } 32 | return nil, errors.New("invalid {{.Name}}: " + strconv.Itoa(int({{$v}}))) 33 | } 34 | 35 | func ({{$v}} *{{.Name}}) UnmarshalText(text []byte) error { 36 | switch str := string(text); str { 37 | {{- range .Values}} 38 | case {{. | printf "%q"}}: 39 | *{{$v}} = {{if not $prop.TrimPrefix}}{{$prop.Name}}{{end}}{{. | UpperTheFirst}} 40 | {{- end}} 41 | default: 42 | return errors.New("unknown {{.Name}}: " + str) 43 | } 44 | return nil 45 | } 46 | {{end}} -------------------------------------------------------------------------------- /level/block/properties.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import "strconv" 4 | 5 | //go:generate go run ./generator/properties/main.go 6 | 7 | type Boolean bool 8 | 9 | func (b Boolean) MarshalText() (text []byte, err error) { 10 | return []byte(strconv.FormatBool(bool(b))), nil 11 | } 12 | 13 | func (b *Boolean) UnmarshalText(text []byte) (err error) { 14 | *((*bool)(b)), err = strconv.ParseBool(string(text)) 15 | return 16 | } 17 | 18 | type Integer int 19 | 20 | func (i Integer) MarshalText() (text []byte, err error) { 21 | return []byte(strconv.Itoa(int(i))), nil 22 | } 23 | 24 | func (i *Integer) UnmarshalText(text []byte) (err error) { 25 | *((*int)(i)), err = strconv.Atoi(string(text)) 26 | return 27 | } 28 | 29 | func (f FrontAndTop) Directions() (front, top Direction) { 30 | switch f { 31 | case DownEast: 32 | return Down, East 33 | case DownNorth: 34 | return Down, North 35 | case DownSouth: 36 | return Down, South 37 | case DownWest: 38 | return Down, West 39 | case UpEast: 40 | return Up, East 41 | case UpNorth: 42 | return Up, North 43 | case UpSouth: 44 | return Up, South 45 | case UpWest: 46 | return Up, West 47 | case WestUp: 48 | return West, Up 49 | case EastUp: 50 | return East, Up 51 | case NorthUp: 52 | return North, Up 53 | case SouthUp: 54 | return South, Up 55 | default: 56 | panic("invalid FrontAndTop") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /level/block/utilfuncs.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | func IsAir(s StateID) bool { 4 | return IsAirBlock(StateList[s]) 5 | } 6 | 7 | func IsAirBlock(b Block) bool { 8 | switch b.(type) { 9 | case Air, CaveAir, VoidAir: 10 | return true 11 | default: 12 | return false 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /level/chunkstatus.go: -------------------------------------------------------------------------------- 1 | package level 2 | 3 | type ChunkStatus string 4 | 5 | const ( 6 | StatusEmpty ChunkStatus = "empty" 7 | StatusStructureStarts ChunkStatus = "structure_starts" 8 | StatusStructureReferences ChunkStatus = "structure_references" 9 | StatusBiomes ChunkStatus = "biomes" 10 | StatusNoise ChunkStatus = "noise" 11 | StatusSurface ChunkStatus = "surface" 12 | StatusCarvers ChunkStatus = "carvers" 13 | StatusLiquidCarvers ChunkStatus = "liquid_carvers" 14 | StatusFeatures ChunkStatus = "features" 15 | StatusLight ChunkStatus = "light" 16 | StatusSpawn ChunkStatus = "spawn" 17 | StatusHeightmaps ChunkStatus = "heightmaps" 18 | StatusFull ChunkStatus = "full" 19 | ) 20 | -------------------------------------------------------------------------------- /level/component/attributemodifiers.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*AttributeModifiers)(nil) 6 | 7 | type AttributeModifiers struct{} 8 | 9 | // ID implements DataComponent. 10 | func (AttributeModifiers) ID() string { 11 | return "minecraft:attribute_modifiers" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (a *AttributeModifiers) ReadFrom(r io.Reader) (n int64, err error) { 16 | panic("unimplemented") 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (a *AttributeModifiers) WriteTo(w io.Writer) (n int64, err error) { 21 | panic("unimplemented") 22 | } 23 | -------------------------------------------------------------------------------- /level/component/blockentitydata.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/nbt/dynbt" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | var _ DataComponent = (*BlockEntityData)(nil) 11 | 12 | type BlockEntityData struct { 13 | dynbt.Value 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (BlockEntityData) ID() string { 18 | return "minecraft:block_entity_data" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (b *BlockEntityData) ReadFrom(r io.Reader) (n int64, err error) { 23 | return pk.NBT(&b.Value).ReadFrom(r) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (b *BlockEntityData) WriteTo(w io.Writer) (n int64, err error) { 28 | return pk.NBT(&b.Value).WriteTo(w) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/bucketentitydata.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/nbt/dynbt" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | var _ DataComponent = (*BucketEntityData)(nil) 11 | 12 | type BucketEntityData struct { 13 | dynbt.Value 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (BucketEntityData) ID() string { 18 | return "minecraft:bucket_entity_data" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (b *BucketEntityData) ReadFrom(r io.Reader) (n int64, err error) { 23 | return pk.NBT(&b.Value).ReadFrom(r) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (b *BucketEntityData) WriteTo(w io.Writer) (n int64, err error) { 28 | return pk.NBT(&b.Value).WriteTo(w) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/bundlecontents.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*BundleContents)(nil) 6 | 7 | type BundleContents struct { 8 | // TODO 9 | } 10 | 11 | // ID implements DataComponent. 12 | func (BundleContents) ID() string { 13 | return "minecraft:bundle_contents" 14 | } 15 | 16 | // ReadFrom implements DataComponent. 17 | func (b *BundleContents) ReadFrom(r io.Reader) (n int64, err error) { 18 | panic("unimplemented") 19 | } 20 | 21 | // WriteTo implements DataComponent. 22 | func (b *BundleContents) WriteTo(w io.Writer) (n int64, err error) { 23 | panic("unimplemented") 24 | } 25 | -------------------------------------------------------------------------------- /level/component/canbreak.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*CanBreak)(nil) 6 | 7 | type CanBreak struct{} 8 | 9 | // ID implements DataComponent. 10 | func (CanBreak) ID() string { 11 | return "minecraft:can_break" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (c *CanBreak) ReadFrom(r io.Reader) (n int64, err error) { 16 | panic("unimplemented") 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (c *CanBreak) WriteTo(w io.Writer) (n int64, err error) { 21 | panic("unimplemented") 22 | } 23 | -------------------------------------------------------------------------------- /level/component/canplaceon.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*CanPlaceOn)(nil) 6 | 7 | type CanPlaceOn struct{} 8 | 9 | // ID implements DataComponent. 10 | func (CanPlaceOn) ID() string { 11 | return "minecraft:can_place_on" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (c *CanPlaceOn) ReadFrom(r io.Reader) (n int64, err error) { 16 | panic("unimplemented") 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (c *CanPlaceOn) WriteTo(w io.Writer) (n int64, err error) { 21 | panic("unimplemented") 22 | } 23 | -------------------------------------------------------------------------------- /level/component/chargedprojectiles.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*ChargedProjectiles)(nil) 6 | 7 | type ChargedProjectiles struct { 8 | // TODO 9 | } 10 | 11 | // ID implements DataComponent. 12 | func (ChargedProjectiles) ID() string { 13 | return "minecraft:charged_projectiles" 14 | } 15 | 16 | // ReadFrom implements DataComponent. 17 | func (c *ChargedProjectiles) ReadFrom(r io.Reader) (n int64, err error) { 18 | panic("unimplemented") 19 | } 20 | 21 | // WriteTo implements DataComponent. 22 | func (c *ChargedProjectiles) WriteTo(w io.Writer) (n int64, err error) { 23 | panic("unimplemented") 24 | } 25 | -------------------------------------------------------------------------------- /level/component/components_test.go: -------------------------------------------------------------------------------- 1 | package component_test 2 | 3 | // func TestNewComponent(t *testing.T) { 4 | // for i, want := range registryid.DataComponentType { 5 | // comp := component.NewComponent(int32(i)) 6 | // if got := comp.ID(); got != want { 7 | // t.Errorf("DataComponent type mismatched: %s != %s", got, want) 8 | // } 9 | // } 10 | // } 11 | -------------------------------------------------------------------------------- /level/component/creativeslotlock.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*CreativeSlotLock)(nil) 6 | 7 | type CreativeSlotLock struct{} 8 | 9 | // ID implements DataComponent. 10 | func (c *CreativeSlotLock) ID() string { 11 | return "minecraft:creative_slot_lock" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (c *CreativeSlotLock) ReadFrom(r io.Reader) (n int64, err error) { 16 | return 0, nil 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (c *CreativeSlotLock) WriteTo(w io.Writer) (n int64, err error) { 21 | return 0, nil 22 | } 23 | -------------------------------------------------------------------------------- /level/component/customdata.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/nbt/dynbt" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | var _ DataComponent = (*CustomData)(nil) 11 | 12 | type CustomData struct { 13 | dynbt.Value 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (CustomData) ID() string { 18 | return "minecraft:custom_data" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (c *CustomData) ReadFrom(r io.Reader) (n int64, err error) { 23 | return pk.NBT(c).ReadFrom(r) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (c *CustomData) WriteTo(w io.Writer) (n int64, err error) { 28 | return pk.NBT(c).WriteTo(w) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/custommodeldata.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*CustomModelData)(nil) 10 | 11 | type CustomModelData struct { 12 | Value pk.VarInt 13 | } 14 | 15 | // ID implements DataComponent. 16 | func (CustomModelData) ID() string { 17 | return "minecraft:custom_model_data" 18 | } 19 | 20 | // ReadFrom implements DataComponent. 21 | func (c *CustomModelData) ReadFrom(r io.Reader) (n int64, err error) { 22 | return c.Value.ReadFrom(r) 23 | } 24 | 25 | // WriteTo implements DataComponent. 26 | func (c *CustomModelData) WriteTo(w io.Writer) (n int64, err error) { 27 | return c.Value.WriteTo(w) 28 | } 29 | -------------------------------------------------------------------------------- /level/component/customname.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/chat" 7 | ) 8 | 9 | var _ DataComponent = (*CustomName)(nil) 10 | 11 | type CustomName struct { 12 | Name chat.Message 13 | } 14 | 15 | // ID implements DataComponent. 16 | func (CustomName) ID() string { 17 | return "minecraft:custom_name" 18 | } 19 | 20 | // ReadFrom implements DataComponent. 21 | func (c *CustomName) ReadFrom(r io.Reader) (n int64, err error) { 22 | return c.Name.ReadFrom(r) 23 | } 24 | 25 | // WriteTo implements DataComponent. 26 | func (c *CustomName) WriteTo(w io.Writer) (n int64, err error) { 27 | return c.Name.WriteTo(w) 28 | } 29 | -------------------------------------------------------------------------------- /level/component/damage.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import pk "github.com/Tnze/go-mc/net/packet" 4 | 5 | var _ DataComponent = (*Damage)(nil) 6 | 7 | type Damage struct { 8 | pk.VarInt 9 | } 10 | 11 | // ID implements DataComponent. 12 | func (Damage) ID() string { 13 | return "minecraft:damage" 14 | } 15 | -------------------------------------------------------------------------------- /level/component/debugstickstate.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/level/block" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | var _ DataComponent = (*DebugStickState)(nil) 11 | 12 | type DebugStickState struct { 13 | Data block.State 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (DebugStickState) ID() string { 18 | return "minecraft:debug_stick_state" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (d *DebugStickState) ReadFrom(r io.Reader) (n int64, err error) { 23 | return pk.NBT(&d.Data).ReadFrom(r) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (d *DebugStickState) WriteTo(w io.Writer) (n int64, err error) { 28 | return pk.NBT(&d.Data).WriteTo(w) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/dyedcolor.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*DyedColor)(nil) 10 | 11 | type DyedColor struct { 12 | RGB pk.Int 13 | ShowInTooltip pk.Boolean 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (DyedColor) ID() string { 18 | return "minecraft:dyed_color" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (d *DyedColor) ReadFrom(r io.Reader) (n int64, err error) { 23 | return pk.Tuple{&d.RGB, &d.ShowInTooltip}.ReadFrom(r) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (d *DyedColor) WriteTo(w io.Writer) (n int64, err error) { 28 | return pk.Tuple{&d.RGB, &d.ShowInTooltip}.WriteTo(w) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/enchantmentglintoverride.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*EnchantmentGlintOverride)(nil) 10 | 11 | type EnchantmentGlintOverride struct { 12 | HasGlint pk.Boolean 13 | } 14 | 15 | // ID implements DataComponent. 16 | func (EnchantmentGlintOverride) ID() string { 17 | return "minecraft:enchantment_glint_override" 18 | } 19 | 20 | // ReadFrom implements DataComponent. 21 | func (e *EnchantmentGlintOverride) ReadFrom(r io.Reader) (n int64, err error) { 22 | return e.HasGlint.ReadFrom(r) 23 | } 24 | 25 | // WriteTo implements DataComponent. 26 | func (e *EnchantmentGlintOverride) WriteTo(w io.Writer) (n int64, err error) { 27 | return e.HasGlint.WriteTo(w) 28 | } 29 | -------------------------------------------------------------------------------- /level/component/enchantments.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*Enchantments)(nil) 6 | 7 | type Enchantments struct{} 8 | 9 | // ID implements DataComponent. 10 | func (Enchantments) ID() string { 11 | return "minecraft:enchantments" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (r *Enchantments) ReadFrom(reader io.Reader) (n int64, err error) { 16 | panic("unimplemented") 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (r *Enchantments) WriteTo(writer io.Writer) (n int64, err error) { 21 | panic("unimplemented") 22 | } 23 | -------------------------------------------------------------------------------- /level/component/entitydata.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/nbt/dynbt" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | var _ DataComponent = (*EntityData)(nil) 11 | 12 | type EntityData struct { 13 | dynbt.Value 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (EntityData) ID() string { 18 | return "minecraft:entity_data" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (e *EntityData) ReadFrom(r io.Reader) (n int64, err error) { 23 | return pk.NBT(&e.Value).ReadFrom(r) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (e *EntityData) WriteTo(w io.Writer) (n int64, err error) { 28 | return pk.NBT(&e.Value).WriteTo(w) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/fireresistant.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*FireResistant)(nil) 6 | 7 | type FireResistant struct{} 8 | 9 | // ID implements DataComponent. 10 | func (FireResistant) ID() string { 11 | return "minecraft:fire_resistant" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (f *FireResistant) ReadFrom(r io.Reader) (n int64, err error) { 16 | return 0, nil 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (f *FireResistant) WriteTo(w io.Writer) (n int64, err error) { 21 | return 0, nil 22 | } 23 | -------------------------------------------------------------------------------- /level/component/food.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*Food)(nil) 10 | 11 | type Food struct { 12 | Nutrition pk.VarInt 13 | Saturation pk.Float 14 | CanAlwaysEat pk.Boolean 15 | EatSeconds pk.Float 16 | // TODO: using_converts_to 17 | // TODO: effects 18 | } 19 | 20 | // ID implements DataComponent. 21 | func (Food) ID() string { 22 | return "minecraft:food" 23 | } 24 | 25 | // ReadFrom implements DataComponent. 26 | func (f *Food) ReadFrom(r io.Reader) (n int64, err error) { 27 | pk.Tuple{ 28 | &f.Nutrition, 29 | &f.Saturation, 30 | &f.CanAlwaysEat, 31 | &f.EatSeconds, 32 | // TODO 33 | }.ReadFrom(r) 34 | panic("unimplemented") 35 | } 36 | 37 | // WriteTo implements DataComponent. 38 | func (f *Food) WriteTo(w io.Writer) (n int64, err error) { 39 | pk.Tuple{ 40 | &f.Nutrition, 41 | &f.Saturation, 42 | &f.CanAlwaysEat, 43 | &f.EatSeconds, 44 | // TODO 45 | }.WriteTo(w) 46 | panic("unimplemented") 47 | } 48 | -------------------------------------------------------------------------------- /level/component/hideadditionaltooltio.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*HideAdditionalTooptip)(nil) 6 | 7 | type HideAdditionalTooptip struct{} 8 | 9 | // ID implements DataComponent. 10 | func (HideAdditionalTooptip) ID() string { 11 | return "minecraft:hide_additional_tooltip" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (h *HideAdditionalTooptip) ReadFrom(r io.Reader) (n int64, err error) { 16 | return 0, nil 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (h *HideAdditionalTooptip) WriteTo(w io.Writer) (n int64, err error) { 21 | return 0, nil 22 | } 23 | -------------------------------------------------------------------------------- /level/component/hidetooltip.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*HideTooptip)(nil) 6 | 7 | type HideTooptip struct{} 8 | 9 | // ID implements DataComponent. 10 | func (HideTooptip) ID() string { 11 | return "minecraft:hide_tooltip" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (h *HideTooptip) ReadFrom(r io.Reader) (n int64, err error) { 16 | return 0, nil 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (h *HideTooptip) WriteTo(w io.Writer) (n int64, err error) { 21 | return 0, nil 22 | } 23 | -------------------------------------------------------------------------------- /level/component/instrument.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*Instrument)(nil) 10 | 11 | // TODO 12 | type Instrument struct { 13 | Type pk.VarInt 14 | SoundEvent SoundEvent 15 | UseDuration pk.Float 16 | Range pk.Float 17 | } 18 | 19 | // ID implements DataComponent. 20 | func (Instrument) ID() string { 21 | return "minecraft:instrument" 22 | } 23 | 24 | // ReadFrom implements DataComponent. 25 | func (i *Instrument) ReadFrom(r io.Reader) (n int64, err error) { 26 | return pk.Tuple{ 27 | &i.Type, 28 | pk.Opt{ 29 | Has: func() bool { return i.Type == 0 }, 30 | Field: pk.Tuple{ 31 | &i.SoundEvent, 32 | &i.UseDuration, 33 | &i.Range, 34 | }, 35 | }, 36 | }.ReadFrom(r) 37 | } 38 | 39 | // WriteTo implements DataComponent. 40 | func (i *Instrument) WriteTo(w io.Writer) (n int64, err error) { 41 | return pk.Tuple{ 42 | &i.Type, 43 | pk.Opt{ 44 | Has: func() bool { return i.Type == 0 }, 45 | Field: pk.Tuple{ 46 | &i.SoundEvent, 47 | &i.UseDuration, 48 | &i.Range, 49 | }, 50 | }, 51 | }.WriteTo(w) 52 | } 53 | 54 | // TODO 55 | type SoundEvent struct { 56 | Type pk.VarInt 57 | SoundName pk.Identifier 58 | FixedRange pk.Option[pk.Float, *pk.Float] 59 | } 60 | 61 | func (s *SoundEvent) ReadFrom(r io.Reader) (int64, error) { 62 | return pk.Tuple{ 63 | &s.Type, 64 | pk.Opt{ 65 | Has: func() bool { return s.Type == 0 }, 66 | Field: pk.Tuple{ 67 | &s.SoundName, 68 | &s.FixedRange, 69 | }, 70 | }, 71 | }.ReadFrom(r) 72 | } 73 | 74 | func (s SoundEvent) WriteTo(w io.Writer) (int64, error) { 75 | return pk.Tuple{ 76 | &s.Type, 77 | pk.Opt{ 78 | Has: func() bool { return s.Type == 0 }, 79 | Field: pk.Tuple{ 80 | &s.SoundName, 81 | &s.FixedRange, 82 | }, 83 | }, 84 | }.WriteTo(w) 85 | } 86 | -------------------------------------------------------------------------------- /level/component/intangibleprojectile.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*IntangibleProjectile)(nil) 6 | 7 | type IntangibleProjectile struct{} 8 | 9 | // ID implements DataComponent. 10 | func (IntangibleProjectile) ID() string { 11 | return "minecraft:intangible_projectile" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (i *IntangibleProjectile) ReadFrom(r io.Reader) (n int64, err error) { 16 | return 0, nil 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (i *IntangibleProjectile) WriteTo(w io.Writer) (n int64, err error) { 21 | return 0, nil 22 | } 23 | -------------------------------------------------------------------------------- /level/component/itemname.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/chat" 7 | ) 8 | 9 | var _ DataComponent = (*ItemName)(nil) 10 | 11 | type ItemName struct { 12 | Name chat.Message 13 | } 14 | 15 | // ID implements DataComponent. 16 | func (ItemName) ID() string { 17 | return "minecraft:item_name" 18 | } 19 | 20 | // ReadFrom implements DataComponent. 21 | func (i *ItemName) ReadFrom(r io.Reader) (n int64, err error) { 22 | return i.Name.ReadFrom(r) 23 | } 24 | 25 | // WriteTo implements DataComponent. 26 | func (i *ItemName) WriteTo(w io.Writer) (n int64, err error) { 27 | return i.Name.WriteTo(w) 28 | } 29 | -------------------------------------------------------------------------------- /level/component/jukeboxplayable.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | var _ DataComponent = (*JukeboxPlayable)(nil) 8 | 9 | type JukeboxPlayable struct { 10 | // TODO 11 | } 12 | 13 | // ID implements DataComponent. 14 | func (JukeboxPlayable) ID() string { 15 | return "minecraft:jukebox_playable" 16 | } 17 | 18 | // ReadFrom implements DataComponent. 19 | func (j *JukeboxPlayable) ReadFrom(r io.Reader) (n int64, err error) { 20 | panic("unimplemented") 21 | } 22 | 23 | // WriteTo implements DataComponent. 24 | func (j *JukeboxPlayable) WriteTo(w io.Writer) (n int64, err error) { 25 | panic("unimplemented") 26 | } 27 | -------------------------------------------------------------------------------- /level/component/lodestonetracker.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*LodestoneTracker)(nil) 10 | 11 | type LodestoneTracker struct { 12 | HasGlobalPosition pk.Boolean 13 | Dimension pk.Identifier 14 | Position pk.Position 15 | Tracked pk.Boolean 16 | } 17 | 18 | // ID implements DataComponent. 19 | func (LodestoneTracker) ID() string { 20 | return "minecraft:lodestone_tracker" 21 | } 22 | 23 | // ReadFrom implements DataComponent. 24 | func (l *LodestoneTracker) ReadFrom(r io.Reader) (n int64, err error) { 25 | return pk.Tuple{ 26 | &l.HasGlobalPosition, 27 | &l.Dimension, 28 | &l.Position, 29 | &l.Tracked, 30 | }.ReadFrom(r) 31 | } 32 | 33 | // WriteTo implements DataComponent. 34 | func (l *LodestoneTracker) WriteTo(w io.Writer) (n int64, err error) { 35 | return pk.Tuple{ 36 | &l.HasGlobalPosition, 37 | &l.Dimension, 38 | &l.Position, 39 | &l.Tracked, 40 | }.WriteTo(w) 41 | } 42 | -------------------------------------------------------------------------------- /level/component/lore.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/chat" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | var _ DataComponent = (*Lore)(nil) 11 | 12 | type Lore struct { 13 | Lines []chat.Message 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (Lore) ID() string { 18 | return "minecraft:lore" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (l *Lore) ReadFrom(r io.Reader) (n int64, err error) { 23 | return pk.Array(&l.Lines).ReadFrom(r) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (l *Lore) WriteTo(w io.Writer) (n int64, err error) { 28 | return pk.Array(&l.Lines).WriteTo(w) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/mapcolor.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import pk "github.com/Tnze/go-mc/net/packet" 4 | 5 | var _ DataComponent = (*MapColor)(nil) 6 | 7 | type MapColor struct { 8 | // The RGB components of the color, encoded as an integer. 9 | pk.Int 10 | } 11 | 12 | // ID implements DataComponent. 13 | func (MapColor) ID() string { 14 | return "minecraft:map_color" 15 | } 16 | -------------------------------------------------------------------------------- /level/component/mapdecorations.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/nbt/dynbt" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | var _ DataComponent = (*MapDecorations)(nil) 11 | 12 | type MapDecorations struct { 13 | dynbt.Value 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (MapDecorations) ID() string { 18 | return "minecraft:map_decorations" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (m *MapDecorations) ReadFrom(r io.Reader) (n int64, err error) { 23 | return pk.NBT(&m.Value).ReadFrom(r) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (m *MapDecorations) WriteTo(w io.Writer) (n int64, err error) { 28 | return pk.NBT(&m.Value).WriteTo(w) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/mapid.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import pk "github.com/Tnze/go-mc/net/packet" 4 | 5 | var _ DataComponent = (*MapID)(nil) 6 | 7 | type MapID struct { 8 | pk.VarInt 9 | } 10 | 11 | // ID implements DataComponent. 12 | func (MapID) ID() string { 13 | return "minecraft:map_id" 14 | } 15 | -------------------------------------------------------------------------------- /level/component/mappostprogressing.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*MapPostProcessing)(nil) 10 | 11 | type MapPostProcessing int32 12 | 13 | const ( 14 | Lock MapPostProcessing = iota 15 | Scale 16 | ) 17 | 18 | // ID implements DataComponent. 19 | func (MapPostProcessing) ID() string { 20 | return "minecraft:map_post_processing" 21 | } 22 | 23 | // ReadFrom implements DataComponent. 24 | func (m *MapPostProcessing) ReadFrom(r io.Reader) (n int64, err error) { 25 | return (*pk.VarInt)(m).ReadFrom(r) 26 | } 27 | 28 | // WriteTo implements DataComponent. 29 | func (m *MapPostProcessing) WriteTo(w io.Writer) (n int64, err error) { 30 | return (*pk.VarInt)(m).WriteTo(w) 31 | } 32 | -------------------------------------------------------------------------------- /level/component/maxdamage.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import pk "github.com/Tnze/go-mc/net/packet" 4 | 5 | var _ DataComponent = (*MaxDamage)(nil) 6 | 7 | type MaxDamage struct { 8 | pk.VarInt 9 | } 10 | 11 | // ID implements DataComponent. 12 | func (MaxDamage) ID() string { 13 | return "minecraft:max_damage" 14 | } 15 | -------------------------------------------------------------------------------- /level/component/maxstacksize.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import pk "github.com/Tnze/go-mc/net/packet" 4 | 5 | var _ DataComponent = (*MaxStackSize)(nil) 6 | 7 | type MaxStackSize struct { 8 | pk.VarInt 9 | } 10 | 11 | // ID implements DataComponent. 12 | func (MaxStackSize) ID() string { 13 | return "minecraft:max_stack_size" 14 | } 15 | -------------------------------------------------------------------------------- /level/component/ominousbottleamplifier.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | pk "github.com/Tnze/go-mc/net/packet" 5 | ) 6 | 7 | var _ DataComponent = (*OminousBottleAmplifier)(nil) 8 | 9 | type OminousBottleAmplifier struct { 10 | pk.VarInt 11 | } 12 | 13 | // ID implements DataComponent. 14 | func (OminousBottleAmplifier) ID() string { 15 | return "minecraft:ominous_bottle_amplifier" 16 | } 17 | -------------------------------------------------------------------------------- /level/component/potioncontents.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*PotionContents)(nil) 10 | 11 | type PotionContents struct { 12 | PotionID pk.Option[pk.VarInt, *pk.VarInt] 13 | CustomColor pk.Option[pk.Int, *pk.Int] 14 | PotionEffects []any 15 | } 16 | 17 | // ID implements DataComponent. 18 | func (PotionContents) ID() string { 19 | return "minecraft:potion_contents" 20 | } 21 | 22 | // ReadFrom implements DataComponent. 23 | func (p *PotionContents) ReadFrom(r io.Reader) (n int64, err error) { 24 | panic("unimplemented") 25 | } 26 | 27 | // WriteTo implements DataComponent. 28 | func (p *PotionContents) WriteTo(w io.Writer) (n int64, err error) { 29 | panic("unimplemented") 30 | } 31 | -------------------------------------------------------------------------------- /level/component/rarity.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*Rarity)(nil) 10 | 11 | type Rarity int32 12 | 13 | const ( 14 | Common Rarity = iota 15 | Uncommon 16 | Rare 17 | Epic 18 | ) 19 | 20 | // ID implements DataComponent. 21 | func (Rarity) ID() string { 22 | return "minecraft:rarity" 23 | } 24 | 25 | // ReadFrom implements DataComponent. 26 | func (r *Rarity) ReadFrom(reader io.Reader) (n int64, err error) { 27 | return (*pk.VarInt)(r).ReadFrom(reader) 28 | } 29 | 30 | // WriteTo implements DataComponent. 31 | func (r *Rarity) WriteTo(writer io.Writer) (n int64, err error) { 32 | return (*pk.VarInt)(r).WriteTo(writer) 33 | } 34 | -------------------------------------------------------------------------------- /level/component/recipes.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/nbt/dynbt" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | var _ DataComponent = (*Recipes)(nil) 11 | 12 | type Recipes struct { 13 | Data dynbt.Value 14 | } 15 | 16 | // ID implements DataComponent. 17 | func (Recipes) ID() string { 18 | return "minecraft:recipes" 19 | } 20 | 21 | // ReadFrom implements DataComponent. 22 | func (r *Recipes) ReadFrom(reader io.Reader) (n int64, err error) { 23 | return pk.NBT(&r.Data).ReadFrom(reader) 24 | } 25 | 26 | // WriteTo implements DataComponent. 27 | func (r *Recipes) WriteTo(writer io.Writer) (n int64, err error) { 28 | return pk.NBT(&r.Data).WriteTo(writer) 29 | } 30 | -------------------------------------------------------------------------------- /level/component/repaircost.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import pk "github.com/Tnze/go-mc/net/packet" 4 | 5 | var _ DataComponent = (*RepairCost)(nil) 6 | 7 | type RepairCost struct { 8 | pk.VarInt 9 | } 10 | 11 | // ID implements DataComponent. 12 | func (RepairCost) ID() string { 13 | return "minecraft:repair_cost" 14 | } 15 | -------------------------------------------------------------------------------- /level/component/storedenchantments.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*StoredEnchantments)(nil) 10 | 11 | type StoredEnchantments struct { 12 | Enchantments []struct { 13 | Type pk.VarInt 14 | Level pk.VarInt 15 | } 16 | ShowInTooltip pk.Boolean 17 | } 18 | 19 | // ID implements DataComponent. 20 | func (StoredEnchantments) ID() string { 21 | return "minecraft:stored_enchantments" 22 | } 23 | 24 | // ReadFrom implements DataComponent. 25 | func (s *StoredEnchantments) ReadFrom(r io.Reader) (n int64, err error) { 26 | return pk.Tuple{ 27 | pk.Array(&s.Enchantments), 28 | &s.ShowInTooltip, 29 | }.ReadFrom(r) 30 | } 31 | 32 | // WriteTo implements DataComponent. 33 | func (s *StoredEnchantments) WriteTo(w io.Writer) (n int64, err error) { 34 | return pk.Tuple{ 35 | pk.Array(&s.Enchantments), 36 | &s.ShowInTooltip, 37 | }.WriteTo(w) 38 | } 39 | -------------------------------------------------------------------------------- /level/component/suspicioussteweffects.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | var _ DataComponent = (*SuspiciousStewEffects)(nil) 8 | 9 | type SuspiciousStewEffects struct { 10 | Effects []any 11 | } 12 | 13 | // ID implements DataComponent. 14 | func (SuspiciousStewEffects) ID() string { 15 | return "minecraft:suspicious_stew_effects" 16 | } 17 | 18 | // ReadFrom implements DataComponent. 19 | func (s *SuspiciousStewEffects) ReadFrom(r io.Reader) (n int64, err error) { 20 | panic("unimplemented") 21 | } 22 | 23 | // WriteTo implements DataComponent. 24 | func (s *SuspiciousStewEffects) WriteTo(w io.Writer) (n int64, err error) { 25 | panic("unimplemented") 26 | } 27 | -------------------------------------------------------------------------------- /level/component/tool.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*Tool)(nil) 6 | 7 | type Tool struct{} 8 | 9 | // ID implements DataComponent. 10 | func (Tool) ID() string { 11 | return "minecraft:tool" 12 | } 13 | 14 | // ReadFrom implements DataComponent. 15 | func (t *Tool) ReadFrom(r io.Reader) (n int64, err error) { 16 | panic("unimplemented") 17 | } 18 | 19 | // WriteTo implements DataComponent. 20 | func (t *Tool) WriteTo(w io.Writer) (n int64, err error) { 21 | panic("unimplemented") 22 | } 23 | -------------------------------------------------------------------------------- /level/component/trim.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import "io" 4 | 5 | var _ DataComponent = (*Trim)(nil) 6 | 7 | type Trim struct { 8 | } 9 | 10 | // ID implements DataComponent. 11 | func (Trim) ID() string { 12 | return "minecraft:trim" 13 | } 14 | 15 | // ReadFrom implements DataComponent. 16 | func (t *Trim) ReadFrom(r io.Reader) (n int64, err error) { 17 | panic("unimplemented") 18 | } 19 | 20 | // WriteTo implements DataComponent. 21 | func (t *Trim) WriteTo(w io.Writer) (n int64, err error) { 22 | panic("unimplemented") 23 | } 24 | -------------------------------------------------------------------------------- /level/component/unbreakable.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*Unbreakable)(nil) 10 | 11 | type Unbreakable struct { 12 | ShowInTooltip pk.Boolean 13 | } 14 | 15 | // ID implements DataComponent. 16 | func (Unbreakable) ID() string { 17 | return "minecraft:unbreakable" 18 | } 19 | 20 | // ReadFrom implements DataComponent. 21 | func (u *Unbreakable) ReadFrom(r io.Reader) (n int64, err error) { 22 | return u.ShowInTooltip.ReadFrom(r) 23 | } 24 | 25 | // WriteTo implements DataComponent. 26 | func (u *Unbreakable) WriteTo(w io.Writer) (n int64, err error) { 27 | return u.ShowInTooltip.WriteTo(w) 28 | } 29 | -------------------------------------------------------------------------------- /level/component/writablebookcontent.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "io" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | ) 8 | 9 | var _ DataComponent = (*WritableBookContent)(nil) 10 | 11 | type WritableBookContent struct { 12 | Pages []Page 13 | } 14 | 15 | // ID implements DataComponent. 16 | func (w *WritableBookContent) ID() string { 17 | return "minecraft:writable_book_content" 18 | } 19 | 20 | // ReadFrom implements DataComponent. 21 | func (w *WritableBookContent) ReadFrom(reader io.Reader) (n int64, err error) { 22 | return pk.Array(&w.Pages).ReadFrom(reader) 23 | } 24 | 25 | // WriteTo implements DataComponent. 26 | func (w *WritableBookContent) WriteTo(writer io.Writer) (n int64, err error) { 27 | return pk.Array(&w.Pages).WriteTo(writer) 28 | } 29 | 30 | type Page struct { 31 | Raw pk.String 32 | Filtered pk.Option[pk.String, *pk.String] 33 | } 34 | -------------------------------------------------------------------------------- /level/palette_test.go: -------------------------------------------------------------------------------- 1 | package level 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | 7 | "github.com/Tnze/go-mc/level/block" 8 | ) 9 | 10 | func TestPaletteContainer_seq(t *testing.T) { 11 | container := NewStatesPaletteContainer(4096, 0) 12 | for i := 0; i < 4096; i++ { 13 | container.Set(i, BlocksState(i)) 14 | } 15 | for i := 0; i < 4096; i++ { 16 | if container.Get(i) != BlocksState(i) { 17 | t.Errorf("Get Error, got: %v, but expect: %v", container.Get(i), BlocksState(i)) 18 | } 19 | } 20 | } 21 | 22 | func TestPaletteContainer_rand(t *testing.T) { 23 | data := make([]BlocksState, 4096) 24 | for i := range data { 25 | data[i] = BlocksState(rand.Intn(1 << block.BitsPerBlock)) 26 | } 27 | container := NewStatesPaletteContainer(4096, 0) 28 | for i, v := range data { 29 | container.Set(i, v) 30 | } 31 | for i, v := range data { 32 | if v2 := container.Get(i); v != v2 { 33 | t.Errorf("value not match: got %v, except: %v", v2, v) 34 | } 35 | } 36 | } 37 | 38 | func BenchmarkPaletteContainer(b *testing.B) { 39 | data := make([]BlocksState, 4096) 40 | for i := range data { 41 | data[i] = BlocksState(rand.Intn(1 << block.BitsPerBlock)) 42 | } 43 | rand.Shuffle(len(data), func(i, j int) { data[i], data[j] = data[j], data[i] }) 44 | container := NewStatesPaletteContainer(4096, 0) 45 | b.ResetTimer() 46 | b.Run("Set", func(b *testing.B) { 47 | for i := 0; i < b.N; i++ { 48 | index := i % 4096 49 | container.Set(index, data[index]) 50 | } 51 | }) 52 | b.Run("Get", func(b *testing.B) { 53 | for i := 0; i < b.N; i++ { 54 | index := i % 4096 55 | container.Get(index) 56 | } 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /nbt/README.md: -------------------------------------------------------------------------------- 1 | # NBT [![Go Reference](https://pkg.go.dev/badge/github.com/Tnze/go-mc/nbt.svg)](https://pkg.go.dev/github.com/Tnze/go-mc/nbt) 2 | 3 | This package implements the [Named Binary Tag](https://wiki.vg/NBT) format of Minecraft. 4 | 5 | The API is very similar to the standard library `encoding/json`. 6 | (But fix some its problem) 7 | If you (high probability) have used that, it is easy to use this. 8 | 9 | ## Supported Struct Tags and Options 10 | 11 | - `nbt` - The primary tag name. See below. 12 | - `nbtkey` - The key name of the field (Used to support commas `,` in tag names) 13 | 14 | ### The `nbt` tag 15 | 16 | In most cases, you only need this one to specify the name of the tag. 17 | 18 | The format of `nbt` struct tag: `[,opt]`. 19 | 20 | It's a comma-separated list of options. 21 | The first item is the name of the tag, and the rest are options. 22 | 23 | Like this: 24 | ```go 25 | type MyStruct struct { 26 | Name string `nbt:"name"` 27 | } 28 | ``` 29 | 30 | To tell the encoder not to encode a field, use `-`: 31 | ```go 32 | type MyStruct struct { 33 | Internal string `nbt:"-"` 34 | } 35 | ``` 36 | 37 | To tell the encoder to skip the field if it is zero value, use `omitempty`: 38 | ```go 39 | type MyStruct struct { 40 | Name string `nbt:"name,omitempty"` 41 | } 42 | ``` 43 | 44 | Fields typed `[]byte`, `[]int32` and `[]int64` will be encoded as `TagByteArray`, `TagIntArray` and `TagLongArray` respectively by default. 45 | You can override this behavior by specifying encode them as`TagList` by using `list`: 46 | ```go 47 | type MyStruct struct { 48 | Data []byte `nbt:"data,list"` 49 | } 50 | ``` 51 | 52 | ### The `nbtkey` tag 53 | 54 | Common issue with JSON standard libraries: inability to specify keys containing commas for structures. 55 | (e.g `{"a,b" : "c"}`) 56 | 57 | So this is a workaround for that: 58 | 59 | ```go 60 | type MyStruct struct { 61 | AB string `nbt:",omitempty" nbtkey:"a,b"` 62 | } 63 | ``` -------------------------------------------------------------------------------- /nbt/dynbt/encode.go: -------------------------------------------------------------------------------- 1 | package dynbt 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | 7 | "github.com/Tnze/go-mc/nbt" 8 | ) 9 | 10 | func (v *Value) TagType() byte { return v.tag } 11 | 12 | func (v *Value) MarshalNBT(w io.Writer) (err error) { 13 | switch v.tag { 14 | case nbt.TagEnd: 15 | _, err = w.Write([]byte{0}) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | case nbt.TagByte, nbt.TagShort, nbt.TagInt, nbt.TagLong, nbt.TagFloat, nbt.TagDouble, 21 | nbt.TagByteArray, nbt.TagString, nbt.TagIntArray, nbt.TagLongArray: 22 | _, err = w.Write(v.data) 23 | 24 | case nbt.TagList: 25 | // Take a look at the first element's tag. 26 | // If length == 0, use TagEnd 27 | elemType := nbt.TagEnd 28 | length := len(v.list) 29 | if length > 0 { 30 | elemType = v.list[0].tag 31 | } 32 | 33 | _, err = w.Write([]byte{elemType}) 34 | if err != nil { 35 | return 36 | } 37 | 38 | err = writeInt32(w, int32(length)) 39 | if err != nil { 40 | return 41 | } 42 | 43 | for _, val := range v.list { 44 | err = val.MarshalNBT(w) 45 | if err != nil { 46 | return 47 | } 48 | } 49 | 50 | case nbt.TagCompound: 51 | for _, field := range v.comp.kvs { 52 | err = writeTag(w, field.v.tag, field.tag) 53 | if err != nil { 54 | return 55 | } 56 | 57 | err = field.v.MarshalNBT(w) 58 | if err != nil { 59 | return 60 | } 61 | } 62 | 63 | _, err = w.Write([]byte{nbt.TagEnd}) 64 | if err != nil { 65 | return 66 | } 67 | 68 | default: 69 | err = errors.New("internal: unknown tag") 70 | } 71 | return 72 | } 73 | 74 | func writeTag(w io.Writer, tagType byte, tagName string) error { 75 | if _, err := w.Write([]byte{tagType}); err != nil { 76 | return err 77 | } 78 | bName := []byte(tagName) 79 | if err := writeInt16(w, int16(len(bName))); err != nil { 80 | return err 81 | } 82 | _, err := w.Write(bName) 83 | return err 84 | } 85 | 86 | func writeInt16(w io.Writer, n int16) error { 87 | _, err := w.Write([]byte{byte(n >> 8), byte(n)}) 88 | return err 89 | } 90 | 91 | func writeInt32(w io.Writer, n int32) error { 92 | _, err := w.Write([]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)}) 93 | return err 94 | } 95 | -------------------------------------------------------------------------------- /nbt/dynbt/update.go: -------------------------------------------------------------------------------- 1 | package dynbt 2 | 3 | import "github.com/Tnze/go-mc/nbt" 4 | 5 | func (v *Value) Set(key string, val *Value) { 6 | if v.tag != nbt.TagCompound { 7 | panic("cannot set non-Compound Tag") 8 | } 9 | v.comp.Set(key, val) 10 | } 11 | 12 | func (v *Value) Get(keys ...string) *Value { 13 | for _, key := range keys { 14 | if v.tag == nbt.TagCompound { 15 | v = v.comp.Get(key) 16 | if v == nil { 17 | return nil 18 | } 19 | } else { 20 | return nil 21 | } 22 | } 23 | return v 24 | } 25 | 26 | func (c *Compound) Set(key string, val *Value) { 27 | for i := range c.kvs { 28 | if c.kvs[i].tag == key { 29 | c.kvs[i].v = val 30 | return 31 | } 32 | } 33 | c.kvs = append(c.kvs, kv{key, val}) 34 | } 35 | 36 | func (c *Compound) Get(key string) *Value { 37 | for _, tag := range c.kvs { 38 | if tag.tag == key { 39 | return tag.v 40 | } 41 | } 42 | return nil 43 | } 44 | 45 | func (c *Compound) Len() int { 46 | return len(c.kvs) 47 | } 48 | -------------------------------------------------------------------------------- /nbt/example_test.go: -------------------------------------------------------------------------------- 1 | package nbt 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | func ExampleDecoder_Decode() { 9 | reader := bytes.NewReader([]byte{ 10 | 0x0a, // Start TagCompound("") 11 | 0x00, 0x00, 12 | 13 | 0x08, // TagString("Author"): "Tnze" 14 | 0x00, 0x06, 'A', 'u', 't', 'h', 'o', 'r', 15 | 0x00, 0x04, 'T', 'n', 'z', 'e', 16 | 17 | 0x00, // End TagCompound 18 | }) 19 | 20 | var value struct { 21 | Author string 22 | } 23 | 24 | decoder := NewDecoder(reader) 25 | _, err := decoder.Decode(&value) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | fmt.Println(value) 31 | 32 | // Output: 33 | // {Tnze} 34 | } 35 | 36 | func ExampleDecoder_Decode_singleTagString() { 37 | reader := bytes.NewReader([]byte{ 38 | // TagString 39 | 0x08, 40 | // TagName 41 | 0x00, 0x04, 42 | 0x6e, 0x61, 0x6d, 0x65, 43 | // Content 44 | 0x00, 0x09, 45 | 0x42, 0x61, 0x6e, 0x61, 0x6e, 0x72, 0x61, 0x6d, 0x61, 46 | }) 47 | 48 | var Name string 49 | 50 | decoder := NewDecoder(reader) 51 | tagName, err := decoder.Decode(&Name) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | fmt.Println(tagName) 57 | fmt.Println(Name) 58 | 59 | // Output: 60 | // name 61 | // Bananrama 62 | } 63 | 64 | func ExampleEncoder_Encode_tagCompound() { 65 | value := struct { 66 | Name string `nbt:"name"` 67 | }{"Tnze"} 68 | 69 | var buff bytes.Buffer 70 | encoder := NewEncoder(&buff) 71 | err := encoder.Encode(value, "") 72 | if err != nil { 73 | panic(err) 74 | } 75 | 76 | fmt.Printf("% 02x ", buff.Bytes()) 77 | 78 | // Output: 79 | // 0a 00 00 08 00 04 6e 61 6d 65 00 04 54 6e 7a 65 00 80 | } 81 | 82 | func ExampleEncoder_writeSNBT() { 83 | var buf bytes.Buffer 84 | if err := NewEncoder(&buf).Encode(StringifiedMessage(`{ name: [Tnze, "Xi_Xi_Mi"]}`), ""); err != nil { 85 | panic(err) 86 | } 87 | fmt.Printf("% 02x ", buf.Bytes()) 88 | 89 | // Output: 90 | // 0a 00 00 09 00 04 6e 61 6d 65 08 00 00 00 02 00 04 54 6e 7a 65 00 08 58 69 5f 58 69 5f 4d 69 00 91 | } 92 | -------------------------------------------------------------------------------- /nbt/interface.go: -------------------------------------------------------------------------------- 1 | package nbt 2 | 3 | import "io" 4 | 5 | type Unmarshaler interface { 6 | UnmarshalNBT(tagType byte, r DecoderReader) error 7 | } 8 | 9 | type Marshaler interface { 10 | TagType() byte 11 | MarshalNBT(w io.Writer) error 12 | } 13 | -------------------------------------------------------------------------------- /nbt/nbt.go: -------------------------------------------------------------------------------- 1 | // Package nbt implement the Named Binary Tag format of Minecraft. 2 | // It provides api like encoding/json package. 3 | package nbt 4 | 5 | import ( 6 | "io" 7 | ) 8 | 9 | // Tag type IDs 10 | const ( 11 | TagEnd byte = iota 12 | TagByte 13 | TagShort 14 | TagInt 15 | TagLong 16 | TagFloat 17 | TagDouble 18 | TagByteArray 19 | TagString 20 | TagList 21 | TagCompound 22 | TagIntArray 23 | TagLongArray 24 | ) 25 | 26 | type DecoderReader = interface { 27 | io.ByteReader 28 | io.Reader 29 | } 30 | type Decoder struct { 31 | r DecoderReader 32 | disallowUnknownFields bool 33 | networkFormat bool 34 | } 35 | 36 | func NewDecoder(r io.Reader) *Decoder { 37 | d := new(Decoder) 38 | if br, ok := r.(DecoderReader); ok { 39 | d.r = br 40 | } else { 41 | d.r = reader{r} 42 | } 43 | return d 44 | } 45 | 46 | // DisallowUnknownFields makes the decoder return an error when unmarshalling a compound 47 | // tag item that has a tag name not present in the destination struct. 48 | func (d *Decoder) DisallowUnknownFields() { 49 | d.disallowUnknownFields = true 50 | } 51 | 52 | // NetworkFormat controls wether the decoder parsing nbt in "network format". 53 | // Means it haven't a tag name for root tag. 54 | // 55 | // It is disabled by default. 56 | func (d *Decoder) NetworkFormat(enable bool) { 57 | d.networkFormat = enable 58 | } 59 | 60 | type reader struct { 61 | io.Reader 62 | } 63 | 64 | func (r reader) ReadByte() (byte, error) { 65 | var b [1]byte 66 | n, err := r.Read(b[:]) 67 | if n == 1 { 68 | return b[0], nil 69 | } 70 | return 0, err 71 | } 72 | -------------------------------------------------------------------------------- /nbt/rawmsg_test.go: -------------------------------------------------------------------------------- 1 | package nbt 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestRawMessage_Encode(t *testing.T) { 9 | data := []byte{ 10 | TagCompound, 0, 2, 'a', 'b', 11 | TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12, 12 | TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 4, 'T', 'n', 'z', 'e', 13 | TagEnd, 14 | } 15 | var container struct { 16 | Key int32 17 | Value RawMessage 18 | } 19 | container.Key = 12 20 | container.Value.Type = TagString 21 | container.Value.Data = []byte{0, 4, 'T', 'n', 'z', 'e'} 22 | 23 | var buf bytes.Buffer 24 | if err := NewEncoder(&buf).Encode(container, "ab"); err != nil { 25 | t.Fatalf("Encode error: %v", err) 26 | } else if !bytes.Equal(data, buf.Bytes()) { 27 | t.Fatalf("Encode error: want %v, get: %v", data, buf.Bytes()) 28 | } 29 | } 30 | 31 | func TestRawMessage_Decode(t *testing.T) { 32 | data := []byte{ 33 | TagCompound, 0, 2, 'a', 'b', 34 | TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12, 35 | TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 4, 'T', 'n', 'z', 'e', 36 | TagList, 0, 4, 'L', 'i', 's', 't', TagCompound, 0, 0, 0, 2, 0, 0, 37 | TagEnd, 38 | } 39 | var container struct { 40 | Key int32 41 | Value RawMessage 42 | List RawMessage 43 | } 44 | 45 | if tag, err := NewDecoder(bytes.NewReader(data)).Decode(&container); err != nil { 46 | t.Fatal(tag) 47 | } else { 48 | if tag != "ab" { 49 | t.Fatalf("Decode tag name error: want %s, get: %s", "ab", tag) 50 | } 51 | if container.Key != 12 { 52 | t.Fatalf("Decode Key error: want %v, get: %v", 12, container.Key) 53 | } 54 | if !bytes.Equal(container.Value.Data, []byte{ 55 | 0, 4, 'T', 'n', 'z', 'e', 56 | }) { 57 | t.Fatalf("Decode Key error: get: %v", container.Value) 58 | } 59 | if !bytes.Equal(container.List.Data, []byte{ 60 | TagCompound, 0, 0, 0, 2, 61 | 0, 0, 62 | }) { 63 | t.Fatalf("Decode List error: get: %v", container.List) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /nbt/snbt_encode_test.go: -------------------------------------------------------------------------------- 1 | package nbt 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestStringifiedMessage_Decode(t *testing.T) { 9 | data := []byte{ 10 | TagCompound, 0, 2, 'a', 'b', 11 | TagInt, 0, 3, 'K', 'e', 'y', 0, 0, 0, 12, 12 | TagString, 0, 5, 'V', 'a', 'l', 'u', 'e', 0, 5, 'T', 'n', ' ', 'z', 'e', 13 | TagList, 0, 4, 'L', 'i', 's', 't', TagCompound, 0, 0, 0, 2, 0, 0, 14 | TagEnd, 15 | } 16 | var container struct { 17 | Key int32 18 | Value StringifiedMessage 19 | List StringifiedMessage 20 | } 21 | 22 | if tag, err := NewDecoder(bytes.NewReader(data)).Decode(&container); err != nil { 23 | t.Fatal(tag, err) 24 | } else { 25 | if tag != "ab" { 26 | t.Fatalf("UnmarshalNBT tag name error: want %s, get: %s", "ab", tag) 27 | } 28 | if container.Key != 12 { 29 | t.Fatalf("UnmarshalNBT Key error: want %v, get: %v", 12, container.Key) 30 | } 31 | if container.Value != `"Tn ze"` { 32 | t.Fatalf("UnmarshalNBT Key error: get: %v", container.Value) 33 | } 34 | if container.List != "[{},{}]" { 35 | t.Fatalf("UnmarshalNBT List error: get: %v", container.List) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /net/interface.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import pk "github.com/Tnze/go-mc/net/packet" 4 | 5 | type Writer interface { 6 | WritePacket(p pk.Packet) error 7 | } 8 | 9 | type Reader interface { 10 | ReadPacket() (pk.Packet, error) 11 | } 12 | 13 | type ReadWriter interface { 14 | Reader 15 | Writer 16 | } 17 | -------------------------------------------------------------------------------- /net/packet/builder.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import "bytes" 4 | 5 | type Builder struct { 6 | buf bytes.Buffer 7 | } 8 | 9 | func (p *Builder) WriteField(fields ...FieldEncoder) { 10 | for _, f := range fields { 11 | _, err := f.WriteTo(&p.buf) 12 | if err != nil { 13 | panic(err) 14 | } 15 | } 16 | } 17 | 18 | func (p *Builder) Packet(id int32) Packet { 19 | return Packet{ID: id, Data: p.buf.Bytes()} 20 | } 21 | -------------------------------------------------------------------------------- /net/packet/joingame_test.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/net/packet/joingame_test.bin -------------------------------------------------------------------------------- /net/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "container/list" 5 | "sync" 6 | ) 7 | 8 | type Queue[T any] interface { 9 | Push(v T) (ok bool) 10 | Pull() (v T, ok bool) 11 | Close() 12 | } 13 | 14 | func NewLinkedQueue[T any]() (q Queue[T]) { 15 | return &LinkedListQueue[T]{ 16 | queue: list.New(), 17 | cond: sync.Cond{L: new(sync.Mutex)}, 18 | } 19 | } 20 | 21 | type LinkedListQueue[T any] struct { 22 | queue *list.List 23 | closed bool 24 | cond sync.Cond 25 | } 26 | 27 | func (p *LinkedListQueue[T]) Push(v T) bool { 28 | p.cond.L.Lock() 29 | if p.closed { 30 | panic("push on closed queue") 31 | } 32 | p.queue.PushBack(v) 33 | p.cond.Signal() 34 | p.cond.L.Unlock() 35 | return true 36 | } 37 | 38 | func (p *LinkedListQueue[T]) Pull() (v T, ok bool) { 39 | p.cond.L.Lock() 40 | for { 41 | if elem := p.queue.Front(); elem != nil { 42 | v = p.queue.Remove(elem).(T) 43 | ok = true 44 | break 45 | } else if p.closed { 46 | break 47 | } 48 | p.cond.Wait() 49 | } 50 | p.cond.L.Unlock() 51 | return 52 | } 53 | 54 | func (p *LinkedListQueue[T]) Close() { 55 | p.cond.L.Lock() 56 | p.closed = true 57 | p.cond.Broadcast() 58 | p.cond.L.Unlock() 59 | } 60 | 61 | func NewChannelQueue[T any](n int) (q Queue[T]) { 62 | return make(ChannelQueue[T], n) 63 | } 64 | 65 | type ChannelQueue[T any] chan T 66 | 67 | func (c ChannelQueue[T]) Push(v T) bool { 68 | select { 69 | case c <- v: 70 | return true 71 | default: 72 | return false 73 | } 74 | } 75 | 76 | func (c ChannelQueue[T]) Pull() (v T, ok bool) { 77 | v, ok = <-c 78 | return 79 | } 80 | 81 | func (c ChannelQueue[T]) Close() { 82 | close(c) 83 | } 84 | -------------------------------------------------------------------------------- /offline/uuid.go: -------------------------------------------------------------------------------- 1 | package offline 2 | 3 | import ( 4 | "crypto/md5" 5 | 6 | "github.com/google/uuid" 7 | ) 8 | 9 | // NameToUUID return the UUID from player name in offline mode 10 | func NameToUUID(name string) uuid.UUID { 11 | version := 3 12 | h := md5.New() 13 | h.Write([]byte("OfflinePlayer:")) 14 | h.Write([]byte(name)) 15 | var id uuid.UUID 16 | h.Sum(id[:0]) 17 | id[6] = (id[6] & 0x0f) | uint8((version&0xf)<<4) 18 | id[8] = (id[8] & 0x3f) | 0x80 // RFC 4122 variant 19 | return id 20 | } 21 | -------------------------------------------------------------------------------- /offline/uuid_test.go: -------------------------------------------------------------------------------- 1 | package offline 2 | 3 | import "fmt" 4 | 5 | func ExampleNameToUUID() { 6 | fmt.Println(NameToUUID("Tnze")) 7 | 8 | // output: 9 | // c7b9eece-2f2e-325c-8da8-6fc8f3d0edb0 10 | } 11 | -------------------------------------------------------------------------------- /realms/invite.go: -------------------------------------------------------------------------------- 1 | package realms 2 | 3 | import "fmt" 4 | 5 | // Invite player to Realm 6 | func (r *Realms) Invite(s Server, name, uuid string) error { 7 | pl := struct { 8 | Name string `json:"name"` 9 | UUID string `json:"uuid"` 10 | }{Name: name, UUID: uuid} 11 | 12 | return r.post(fmt.Sprintf("/invites/%d", s.ID), pl, struct{}{}) 13 | } 14 | -------------------------------------------------------------------------------- /realms/mco.go: -------------------------------------------------------------------------------- 1 | package realms 2 | 3 | import "io" 4 | 5 | // Available returns whether the user can access the Minecraft Realms service 6 | func (r *Realms) Available() (ok bool, err error) { 7 | err = r.get("/mco/available", &ok) 8 | return 9 | } 10 | 11 | // Compatible returns whether the clients version is up-to-date with Realms. 12 | // 13 | // if the client is outdated, it returns OUTDATED, 14 | // if the client is running a snapshot, it returns OTHER, 15 | // else it returns COMPATIBLE. 16 | func (r *Realms) Compatible() (string, error) { 17 | resp, err := r.c.Get(Domain + "/mco/client/compatible") 18 | if err != nil { 19 | return "", err 20 | } 21 | defer resp.Body.Close() 22 | 23 | rp, err := io.ReadAll(resp.Body) 24 | 25 | return string(rp), err 26 | } 27 | 28 | // TOS is what to join Realms servers you must agree to. 29 | // Call this function will set this flag. 30 | func (r *Realms) TOS() error { 31 | resp, err := r.c.Post(Domain+"/mco/tos/agreed", "application/json", nil) 32 | if err != nil { 33 | return err 34 | } 35 | defer resp.Body.Close() 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /realms/realms.go: -------------------------------------------------------------------------------- 1 | package realms 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "net/http" 8 | "net/http/cookiejar" 9 | "net/url" 10 | ) 11 | 12 | type Realms struct { 13 | c http.Client 14 | } 15 | 16 | type Error struct { 17 | ErrorCode int 18 | ErrorMsg string 19 | } 20 | 21 | func (e *Error) Error() string { 22 | return fmt.Sprintf("[%d] %s", e.ErrorCode, e.ErrorMsg) 23 | } 24 | 25 | // Domain is the URL of Realms API server 26 | // Panic if it cannot be parsed by url.Parse(). 27 | var Domain = "https://pc.realms.minecraft.net" 28 | 29 | // New create a new Realms c with version, username, accessToken and UUID without dashes. 30 | func New(version, user, astk, uuid string) *Realms { 31 | r := &Realms{ 32 | c: http.Client{}, 33 | } 34 | 35 | var err error 36 | r.c.Jar, err = cookiejar.New(nil) 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | d, err := url.Parse(Domain) 42 | if err != nil { 43 | panic("cannot parse realms.Domain as url: " + err.Error()) 44 | } 45 | 46 | r.c.Jar.SetCookies(d, []*http.Cookie{ 47 | {Name: "user", Value: user}, 48 | {Name: "version", Value: version}, 49 | {Name: "sid", Value: "token:" + astk + ":" + uuid}, 50 | }) 51 | 52 | return r 53 | } 54 | 55 | func (r *Realms) get(endpoint string, resp any) error { 56 | rawResp, err := r.c.Get(Domain + endpoint) 57 | if err != nil { 58 | return err 59 | } 60 | defer rawResp.Body.Close() 61 | 62 | err = json.NewDecoder(rawResp.Body).Decode(resp) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | return nil 68 | } 69 | 70 | func (r *Realms) post(endpoint string, payload, resp any) error { 71 | data, err := json.Marshal(payload) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | rawResp, err := r.c.Post(Domain+endpoint, "application/json", bytes.NewReader(data)) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | err = json.NewDecoder(rawResp.Body).Decode(resp) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /realms/realms_test.go: -------------------------------------------------------------------------------- 1 | package realms 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func ExampleRealms() { 9 | var r *Realms 10 | 11 | r = New( 12 | "1.14.4", 13 | "Name", 14 | "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 15 | "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 16 | ) 17 | fmt.Println(r.Available()) 18 | fmt.Println(r.Compatible()) 19 | 20 | servers, err := r.Worlds() 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | for _, v := range servers { 26 | fmt.Println(v.Name, v.ID) 27 | } 28 | 29 | time.Sleep(time.Second * 5) 30 | if err := r.TOS(); err != nil { 31 | panic(err) 32 | } 33 | 34 | time.Sleep(time.Second * 5) 35 | fmt.Println(r.Address(servers[0])) 36 | } 37 | -------------------------------------------------------------------------------- /registry/network.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "strconv" 7 | 8 | pk "github.com/Tnze/go-mc/net/packet" 9 | ) 10 | 11 | func (reg *Registry[E]) ReadFrom(r io.Reader) (int64, error) { 12 | var length pk.VarInt 13 | n, err := length.ReadFrom(r) 14 | if err != nil { 15 | return n, err 16 | } 17 | 18 | reg.Clear() 19 | 20 | var key pk.Identifier 21 | var hasData pk.Boolean 22 | for i := 0; i < int(length); i++ { 23 | var data E 24 | var n1, n2, n3 int64 25 | 26 | n1, err = key.ReadFrom(r) 27 | if err != nil { 28 | return n + n1, err 29 | } 30 | 31 | n2, err = hasData.ReadFrom(r) 32 | if err != nil { 33 | return n + n1 + n2, err 34 | } 35 | 36 | if hasData { 37 | n3, err = pk.NBTField{V: &data, AllowUnknownFields: true}.ReadFrom(r) 38 | if err != nil { 39 | return n + n1 + n2 + n3, err 40 | } 41 | reg.Put(string(key), data) 42 | } 43 | 44 | n += n1 + n2 + n3 45 | } 46 | return n, nil 47 | } 48 | 49 | func (reg *Registry[E]) ReadTagsFrom(r io.Reader) (int64, error) { 50 | var count pk.VarInt 51 | n, err := count.ReadFrom(r) 52 | if err != nil { 53 | return n, err 54 | } 55 | 56 | var tag pk.Identifier 57 | var length pk.VarInt 58 | for i := 0; i < int(count); i++ { 59 | var n1, n2, n3 int64 60 | 61 | n1, err = tag.ReadFrom(r) 62 | if err != nil { 63 | return n + n1, err 64 | } 65 | 66 | n2, err = length.ReadFrom(r) 67 | if err != nil { 68 | return n + n1 + n2, err 69 | } 70 | 71 | n += n1 + n2 72 | values := make([]*E, length) 73 | 74 | var id pk.VarInt 75 | for i := 0; i < int(length); i++ { 76 | n3, err = id.ReadFrom(r) 77 | if err != nil { 78 | return n + n3, err 79 | } 80 | 81 | if id < 0 || int(id) >= len(reg.values) { 82 | err = errors.New("invalid id: " + strconv.Itoa(int(id))) 83 | return n + n3, err 84 | } 85 | 86 | values[i] = ®.values[id] 87 | n += n3 88 | } 89 | 90 | reg.tags[string(tag)] = values 91 | } 92 | return n, nil 93 | } 94 | -------------------------------------------------------------------------------- /registry/registry.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import "slices" 4 | 5 | type Registry[E any] struct { 6 | keys map[string]int32 7 | values []E 8 | indices map[*E]int32 9 | tags map[string][]*E 10 | } 11 | 12 | func NewRegistry[E any]() Registry[E] { 13 | return Registry[E]{ 14 | keys: make(map[string]int32), 15 | values: make([]E, 0, 256), 16 | indices: make(map[*E]int32), 17 | tags: make(map[string][]*E), 18 | } 19 | } 20 | 21 | func (r *Registry[E]) Clear() { 22 | r.keys = make(map[string]int32) 23 | r.values = r.values[:0] 24 | r.indices = make(map[*E]int32) 25 | r.tags = make(map[string][]*E) 26 | } 27 | 28 | func (r *Registry[E]) Get(key string) (int32, *E) { 29 | id, ok := r.keys[key] 30 | if !ok { 31 | return -1, nil 32 | } 33 | return id, &r.values[id] 34 | } 35 | 36 | func (r *Registry[E]) GetByID(id int32) *E { 37 | if id >= 0 && id < int32(len(r.values)) { 38 | return &r.values[id] 39 | } 40 | return nil 41 | } 42 | 43 | func (r *Registry[E]) Put(key string, data E) (id int32, val *E) { 44 | id = int32(len(r.values)) 45 | r.keys[key] = id 46 | r.values = append(r.values, data) 47 | val = &r.values[id] 48 | r.indices[val] = id 49 | return 50 | } 51 | 52 | // Tags 53 | 54 | func (r *Registry[E]) Tag(tag string) []*E { 55 | return slices.Clone(r.tags[tag]) 56 | } 57 | 58 | func (r *Registry[E]) ClearTags() { 59 | r.tags = make(map[string][]*E) 60 | } 61 | 62 | // func (r *Registry[E]) BindTags(tag string, ids []int32) error { 63 | // values := make([]*E, len(ids)) 64 | // for i, id := range ids { 65 | // if id < 0 || id >= int32(len(r.values)) { 66 | // return errors.New("invalid id: " + strconv.Itoa(int(id))) 67 | // } 68 | // values[i] = &r.values[id] 69 | // } 70 | // r.tags[tag] = values 71 | // return nil 72 | // } 73 | -------------------------------------------------------------------------------- /save/chunk_test.go: -------------------------------------------------------------------------------- 1 | package save 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/Tnze/go-mc/save/region" 8 | ) 9 | 10 | func TestColumn(t *testing.T) { 11 | files, err := filepath.Glob("testdata/region/r.*.*.mca") 12 | if err != nil { 13 | return 14 | } 15 | for _, filename := range files { 16 | r, err := region.Open(filename) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | 21 | for x := 0; x < 32; x++ { 22 | for z := 0; z < 32; z++ { 23 | if !r.ExistSector(x, z) { 24 | continue 25 | } 26 | 27 | data, err := r.ReadSector(x, z) 28 | if err != nil { 29 | t.Fatalf("read %s sec (%d, %d) fail: %v", filepath.Base(filename), x, z, err) 30 | } 31 | 32 | var c Chunk 33 | err = c.Load(data) 34 | if err != nil { 35 | t.Fatalf("read %s sec (%d, %d) fail: %v", filepath.Base(filename), x, z, err) 36 | } 37 | } 38 | } 39 | 40 | err = r.Close() 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | } 45 | } 46 | 47 | func BenchmarkColumn_Load(b *testing.B) { 48 | // Test how many times we load a chunk 49 | var c Chunk 50 | r, err := region.Open("testdata/region/r.-1.-1.mca") 51 | if err != nil { 52 | b.Fatal(err) 53 | } 54 | defer r.Close() 55 | 56 | for i := 0; i < b.N; i++ { 57 | x, z := (i%1024)/32, (i%1024)%32 58 | // x, z := rand.Intn(32), rand.Intn(32) 59 | if !r.ExistSector(x, z) { 60 | continue 61 | } 62 | 63 | data, err := r.ReadSector(x, z) 64 | if err != nil { 65 | b.Fatal(err) 66 | } 67 | 68 | err = c.Load(data) 69 | if err != nil { 70 | b.Fatal(err) 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /save/level_test.go: -------------------------------------------------------------------------------- 1 | package save 2 | 3 | import ( 4 | "compress/gzip" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestLevel(t *testing.T) { 10 | f, err := os.Open("testdata/level.dat") 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | 15 | r, err := gzip.NewReader(f) 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | 20 | data, err := ReadLevel(r) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | //want := PlayerData{ 26 | // Pos: [3]float64{-41.5, 65, -89.5}, 27 | // Motion: [3]float64{0, -0.0784000015258789, 0}, 28 | // Rotation: [2]float32{0,0}, 29 | //} 30 | 31 | t.Logf("%+v", data) 32 | //if data != want { 33 | // t.Errorf("player data parse error: get %v, want %v", data, want) 34 | //} 35 | } 36 | -------------------------------------------------------------------------------- /save/playerdata.go: -------------------------------------------------------------------------------- 1 | package save 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/Tnze/go-mc/nbt" 7 | ) 8 | 9 | type PlayerData struct { 10 | DataVersion int32 11 | 12 | Dimension string 13 | Pos [3]float64 14 | Motion [3]float64 15 | Rotation [2]float32 16 | FallDistance float32 17 | FallFlying byte 18 | OnGround byte 19 | 20 | UUID [4]int32 21 | 22 | PlayerGameType int32 `nbt:"playerGameType"` 23 | Air int16 24 | DeathTime int16 25 | Fire int16 26 | HurtTime int16 27 | Health float32 28 | HurtByTimestamp int32 29 | PortalCooldown int32 30 | 31 | Invulnerable byte 32 | SeenCredits byte `nbt:"seenCredits"` 33 | SelectedItemSlot int32 34 | Score int32 35 | AbsorptionAmount float32 36 | 37 | Inventory, EnderItems []Item 38 | 39 | XpLevel int32 40 | XpP float32 41 | XpTotal int32 42 | XpSeed int32 43 | 44 | FoodExhaustionLevel float32 `nbt:"foodExhaustionLevel"` 45 | FoodLevel int32 `nbt:"foodLevel"` 46 | FoodSaturationLevel float32 `nbt:"foodSaturationLevel"` 47 | FoodTickTimer int32 `nbt:"foodTickTimer"` 48 | 49 | Attributes []struct { 50 | Base float64 51 | Name string 52 | } 53 | 54 | Abilities struct { 55 | FlySpeed float32 `nbt:"flySpeed"` 56 | WalkSpeed float32 `nbt:"walkSpeed"` 57 | Flying byte `nbt:"flying"` 58 | InstantBuild byte `nbt:"instabuild"` 59 | Invulnerable byte `nbt:"invulnerable"` 60 | MayBuild byte `nbt:"mayBuild"` 61 | MayFly byte `nbt:"mayfly"` 62 | } `nbt:"abilities"` 63 | 64 | RecipeBook struct { 65 | IsFilteringCraftable byte `nbt:"isFilteringCraftable"` 66 | IsFurnaceFilteringCraftable byte `nbt:"isFurnaceFilteringCraftable"` 67 | IsFurnaceGUIOpen byte `nbt:"isFurnaceGuiOpen"` 68 | IsGUIOpen byte `nbt:"isGuiOpen"` 69 | } `nbt:"recipeBook"` 70 | } 71 | 72 | type Item struct { 73 | Count byte 74 | Slot byte 75 | ID string `nbt:"id"` 76 | Tag map[string]any `nbt:"tag"` 77 | } 78 | 79 | func ReadPlayerData(r io.Reader) (data PlayerData, err error) { 80 | _, err = nbt.NewDecoder(r).Decode(&data) 81 | return 82 | } 83 | -------------------------------------------------------------------------------- /save/playerdata_test.go: -------------------------------------------------------------------------------- 1 | package save 2 | 3 | import ( 4 | "compress/gzip" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestPlayerData(t *testing.T) { 10 | f, err := os.Open("testdata/playerdata/58f6356e-b30c-4811-8bfc-d72a9ee99e73.dat") 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | 15 | r, err := gzip.NewReader(f) 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | 20 | data, err := ReadPlayerData(r) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | 25 | //want := PlayerData{ 26 | // Pos: [3]float64{-41.5, 65, -89.5}, 27 | // Motion: [3]float64{0, -0.0784000015258789, 0}, 28 | // Rotation: [2]float32{0,0}, 29 | //} 30 | 31 | t.Logf("%+v", data) 32 | //if data != want { 33 | // t.Errorf("player data parse error: get %v, want %v", data, want) 34 | //} 35 | } 36 | -------------------------------------------------------------------------------- /save/testdata/DIM-1/data/raids.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/DIM-1/data/raids.dat -------------------------------------------------------------------------------- /save/testdata/DIM1/data/raids_end.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/DIM1/data/raids_end.dat -------------------------------------------------------------------------------- /save/testdata/advancements/58f6356e-b30c-4811-8bfc-d72a9ee99e73.json: -------------------------------------------------------------------------------- 1 | { 2 | "minecraft:adventure/adventuring_time": { 3 | "criteria": { 4 | "minecraft:forest": "2021-12-19 20:04:35 +0800" 5 | }, 6 | "done": false 7 | }, 8 | "DataVersion": 2865 9 | } -------------------------------------------------------------------------------- /save/testdata/data/raids.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/data/raids.dat -------------------------------------------------------------------------------- /save/testdata/entities/r.-1.-1.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/entities/r.-1.-1.mca -------------------------------------------------------------------------------- /save/testdata/entities/r.-1.0.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/entities/r.-1.0.mca -------------------------------------------------------------------------------- /save/testdata/entities/r.0.-1.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/entities/r.0.-1.mca -------------------------------------------------------------------------------- /save/testdata/entities/r.0.0.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/entities/r.0.0.mca -------------------------------------------------------------------------------- /save/testdata/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/icon.png -------------------------------------------------------------------------------- /save/testdata/level.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/level.dat -------------------------------------------------------------------------------- /save/testdata/level.dat_old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/level.dat_old -------------------------------------------------------------------------------- /save/testdata/playerdata/58f6356e-b30c-4811-8bfc-d72a9ee99e73.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/playerdata/58f6356e-b30c-4811-8bfc-d72a9ee99e73.dat -------------------------------------------------------------------------------- /save/testdata/playerdata/58f6356e-b30c-4811-8bfc-d72a9ee99e73.dat_old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/playerdata/58f6356e-b30c-4811-8bfc-d72a9ee99e73.dat_old -------------------------------------------------------------------------------- /save/testdata/poi/r.-1.-1.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/poi/r.-1.-1.mca -------------------------------------------------------------------------------- /save/testdata/poi/r.0.-1.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/poi/r.0.-1.mca -------------------------------------------------------------------------------- /save/testdata/poi/r.0.0.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/poi/r.0.0.mca -------------------------------------------------------------------------------- /save/testdata/region/r.-1.-1.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/region/r.-1.-1.mca -------------------------------------------------------------------------------- /save/testdata/region/r.-1.0.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/region/r.-1.0.mca -------------------------------------------------------------------------------- /save/testdata/region/r.0.-1.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/region/r.0.-1.mca -------------------------------------------------------------------------------- /save/testdata/region/r.0.0.mca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/save/testdata/region/r.0.0.mca -------------------------------------------------------------------------------- /save/testdata/session.lock: -------------------------------------------------------------------------------- 1 | ☃ -------------------------------------------------------------------------------- /save/testdata/stats/58f6356e-b30c-4811-8bfc-d72a9ee99e73.json: -------------------------------------------------------------------------------- 1 | {"stats":{"minecraft:custom":{"minecraft:time_since_rest":14,"minecraft:leave_game":1,"minecraft:play_time":14,"minecraft:time_since_death":14,"minecraft:total_world_time":77}},"DataVersion":2865} -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # Server 2 | 3 | This package provide a very basic framework for server development. 4 | For more example, go to [this repo](https://github.com/go-mc/server). -------------------------------------------------------------------------------- /server/auth/auth_test.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | "github.com/google/uuid" 8 | ) 9 | 10 | func TestResp(t *testing.T) { 11 | var resp Resp 12 | err := json.Unmarshal([]byte(`{"id":"853c80ef3c3749fdaa49938b674adae6","name":"jeb_","properties":[{"name":"textures","value":"eyJ0aW1lc3RhbXAiOjE1NTk1NDM5MzMwMjUsInByb2ZpbGVJZCI6Ijg1M2M4MGVmM2MzNzQ5ZmRhYTQ5OTM4YjY3NGFkYWU2IiwicHJvZmlsZU5hbWUiOiJqZWJfIiwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdmZDliYTQyYTdjODFlZWVhMjJmMTUyNDI3MWFlODVhOGUwNDVjZTBhZjVhNmFlMTZjNjQwNmFlOTE3ZTY4YjUifSwiQ0FQRSI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzU3ODZmZTk5YmUzNzdkZmI2ODU4ODU5ZjkyNmM0ZGJjOTk1NzUxZTkxY2VlMzczNDY4YzVmYmY0ODY1ZTcxNTEifX19"}]}`), &resp) 13 | if err != nil { 14 | panic(err) 15 | } 16 | wantID := uuid.Must(uuid.Parse("853c80ef3c3749fdaa49938b674adae6")) 17 | 18 | // check UUID 19 | if resp.ID != wantID { 20 | t.Errorf("uuid doesn't match: %v, want %s", resp.ID, wantID) 21 | } 22 | 23 | // check name 24 | if resp.Name != "jeb_" { 25 | t.Errorf("name doesn't match: %s, want %s", resp.Name, "jeb_") 26 | } 27 | 28 | // check texture 29 | texture, err := resp.Texture() 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | 34 | t.Log(texture.TimeStamp) 35 | 36 | if texture.ID != wantID { 37 | t.Errorf("uuid doesn't match: %v, want %s", texture.ID, wantID) 38 | } 39 | 40 | if texture.Name != "jeb_" { 41 | t.Errorf("name doesn't match: %s, want %s", texture.Name, "jeb_") 42 | } 43 | 44 | const ( 45 | wantSKIN = "http://textures.minecraft.net/texture/7fd9ba42a7c81eeea22f1524271ae85a8e045ce0af5a6ae16c6406ae917e68b5" 46 | wantCAPE = "http://textures.minecraft.net/texture/5786fe99be377dfb6858859f926c4dbc995751e91cee373468c5fbf4865e7151" 47 | ) 48 | if texture.Textures.SKIN.URL != wantSKIN { 49 | t.Errorf("skin url not match: %s, want %s", 50 | texture.Textures.SKIN.URL, 51 | wantSKIN) 52 | } 53 | if texture.Textures.CAPE.URL != wantCAPE { 54 | t.Errorf("cape url not match: %s, want %s", 55 | texture.Textures.CAPE.URL, 56 | wantCAPE) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /server/client.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "strconv" 5 | 6 | pk "github.com/Tnze/go-mc/net/packet" 7 | "github.com/Tnze/go-mc/net/queue" 8 | ) 9 | 10 | // Packet758 is a packet in protocol 757. 11 | // We are using type system to force programmers to update packets. 12 | type ( 13 | Packet758 pk.Packet 14 | Packet757 pk.Packet 15 | ) 16 | 17 | type WritePacketError struct { 18 | Err error 19 | ID int32 20 | } 21 | 22 | func (s WritePacketError) Error() string { 23 | return "server: send packet " + strconv.FormatInt(int64(s.ID), 16) + " error: " + s.Err.Error() 24 | } 25 | 26 | func (s WritePacketError) Unwrap() error { 27 | return s.Err 28 | } 29 | 30 | type PacketQueue = queue.Queue[pk.Packet] 31 | -------------------------------------------------------------------------------- /server/command/command_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "testing" 7 | ) 8 | 9 | func TestRoot_Run(t *testing.T) { 10 | handleFunc := func(ctx context.Context, args []ParsedData) error { 11 | log.Printf("Command: args: %v", args) 12 | return nil 13 | } 14 | g := NewGraph() 15 | g.AppendLiteral(g.Literal("me"). 16 | AppendArgument(g.Argument("action", StringParser(2)). 17 | HandleFunc(handleFunc)). 18 | Unhandle(), 19 | ).AppendLiteral(g.Literal("help"). 20 | AppendArgument(g.Argument("command", StringParser(0)). 21 | HandleFunc(handleFunc)). 22 | HandleFunc(handleFunc), 23 | ).AppendLiteral(g.Literal("list"). 24 | AppendLiteral(g.Literal("uuids"). 25 | HandleFunc(handleFunc)). 26 | HandleFunc(handleFunc), 27 | ) 28 | 29 | err := g.Execute(context.TODO(), "me Tnze Xi_Xi_Mi") 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/command/component.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "github.com/Tnze/go-mc/data/packetid" 5 | pk "github.com/Tnze/go-mc/net/packet" 6 | ) 7 | 8 | type Client interface { 9 | SendPacket(p pk.Packet) 10 | } 11 | 12 | // ClientJoin implement server.Component for Graph 13 | func (g *Graph) ClientJoin(client Client) { 14 | client.SendPacket(pk.Marshal( 15 | packetid.ClientboundCommands, g, 16 | )) 17 | } 18 | -------------------------------------------------------------------------------- /server/command/parsers.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "io" 5 | "strconv" 6 | "strings" 7 | 8 | pk "github.com/Tnze/go-mc/net/packet" 9 | ) 10 | 11 | type Parser interface { 12 | Parse(cmd string) (left string, value ParsedData, err error) 13 | } 14 | 15 | type StringParser int32 16 | 17 | func (s StringParser) WriteTo(w io.Writer) (int64, error) { 18 | return pk.Tuple{ 19 | pk.Identifier("brigadier:string"), 20 | pk.VarInt(s), 21 | }.WriteTo(w) 22 | } 23 | 24 | func (s StringParser) Parse(cmd string) (left string, value ParsedData, err error) { 25 | switch s { 26 | case 2: // Greedy Phrase 27 | return "", cmd, nil 28 | case 1: // Quotable Phrase 29 | if len(cmd) > 0 && cmd[0] == '"' { 30 | var sb strings.Builder 31 | var isEscaping bool 32 | for i, v := range cmd[1:] { 33 | if isEscaping { 34 | isEscaping = false 35 | switch v { 36 | case '\\': 37 | sb.WriteRune('\\') 38 | case '"': 39 | sb.WriteRune('"') 40 | } 41 | } else if v == '\\' { 42 | isEscaping = true 43 | } else if v == '"' { 44 | return cmd[:i], sb.String(), nil 45 | } else { 46 | sb.WriteRune(v) 47 | } 48 | } 49 | return cmd, nil, ParseErr{ 50 | Pos: len(cmd) - 1, 51 | Err: "expected '\"'", 52 | } 53 | } 54 | fallthrough 55 | case 0: // Single Word 56 | i := strings.IndexAny(cmd, "\t\n\v\f\r ") 57 | if i == -1 { 58 | return "", cmd, nil 59 | } 60 | return cmd[i:], cmd[:i], nil 61 | default: 62 | panic("StringParser: unknown format 0x" + strconv.FormatInt(int64(s), 16)) 63 | } 64 | } 65 | 66 | type ParseErr struct { 67 | Pos int 68 | Err string 69 | } 70 | 71 | func (p ParseErr) Error() string { 72 | return p.Err 73 | } 74 | -------------------------------------------------------------------------------- /server/command/serialize.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "io" 5 | "unsafe" 6 | 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | ) 9 | 10 | const ( 11 | isExecutable = 1 << (iota + 2) 12 | hasRedirect 13 | hasSuggestionsType 14 | ) 15 | 16 | func (g *Graph) WriteTo(w io.Writer) (int64, error) { 17 | return pk.Tuple{ 18 | pk.Array(g.nodes), 19 | pk.VarInt(0), 20 | }.WriteTo(w) 21 | } 22 | 23 | func (n Node) WriteTo(w io.Writer) (int64, error) { 24 | var flag byte 25 | flag |= n.kind & 0x03 26 | if n.Run != nil { 27 | flag |= isExecutable 28 | } 29 | return pk.Tuple{ 30 | pk.Byte(flag), 31 | pk.Array((*[]pk.VarInt)(unsafe.Pointer(&n.Children))), 32 | pk.Opt{ 33 | Has: func() bool { return n.kind&hasRedirect != 0 }, 34 | Field: nil, // TODO: send redirect node 35 | }, 36 | pk.Opt{ 37 | Has: func() bool { return n.kind == ArgumentNode || n.kind == LiteralNode }, 38 | Field: pk.String(n.Name), 39 | }, 40 | pk.Opt{ 41 | Has: func() bool { return n.kind == ArgumentNode }, 42 | Field: n.Parser, // Parser identifier and Properties 43 | }, 44 | pk.Opt{ 45 | Has: func() bool { return flag&hasSuggestionsType != 0 }, 46 | Field: nil, // TODO: send Suggestions type 47 | }, 48 | }.WriteTo(w) 49 | } 50 | -------------------------------------------------------------------------------- /server/configuration.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/Tnze/go-mc/chat" 5 | "github.com/Tnze/go-mc/data/packetid" 6 | "github.com/Tnze/go-mc/net" 7 | pk "github.com/Tnze/go-mc/net/packet" 8 | "github.com/Tnze/go-mc/registry" 9 | ) 10 | 11 | type ConfigHandler interface { 12 | AcceptConfig(conn *net.Conn) error 13 | } 14 | 15 | type Configurations struct { 16 | Registries registry.Registries 17 | } 18 | 19 | func (c *Configurations) AcceptConfig(conn *net.Conn) error { 20 | err := conn.WritePacket(pk.Marshal( 21 | packetid.ClientboundConfigRegistryData, 22 | pk.NBT(c.Registries), 23 | )) 24 | if err != nil { 25 | return err 26 | } 27 | err = conn.WritePacket(pk.Marshal( 28 | packetid.ClientboundConfigFinishConfiguration, 29 | )) 30 | return err 31 | } 32 | 33 | type ConfigFailErr struct { 34 | reason chat.Message 35 | } 36 | 37 | func (c ConfigFailErr) Error() string { 38 | return "config error: " + c.reason.ClearString() 39 | } 40 | -------------------------------------------------------------------------------- /server/entity.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | type ( 4 | Pos struct{ X, Y, Z float64 } 5 | Rot struct{ Yaw, Pitch float32 } 6 | ) 7 | -------------------------------------------------------------------------------- /server/gameplay.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | _ "embed" 5 | 6 | "github.com/Tnze/go-mc/net" 7 | "github.com/Tnze/go-mc/yggdrasil/user" 8 | 9 | "github.com/google/uuid" 10 | ) 11 | 12 | type GamePlay interface { 13 | // AcceptPlayer handle everything after "LoginSuccess" is sent. 14 | // 15 | // Note: the connection will be closed after this function returned. 16 | // You don't need to close the connection, but to keep not returning while the player is playing. 17 | AcceptPlayer(name string, id uuid.UUID, profilePubKey *user.PublicKey, properties []user.Property, protocol int32, conn *net.Conn) 18 | } 19 | -------------------------------------------------------------------------------- /server/handshake.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/Tnze/go-mc/net" 5 | pk "github.com/Tnze/go-mc/net/packet" 6 | ) 7 | 8 | func (s *Server) handshake(conn *net.Conn) (protocol int32, intention int32, err error) { 9 | var ( 10 | Protocol, Intention pk.VarInt 11 | ServerAddress pk.String // ignored 12 | ServerPort pk.UnsignedShort // ignored 13 | ) 14 | // receive handshake packet 15 | var p pk.Packet 16 | err = conn.ReadPacket(&p) 17 | if err != nil { 18 | return 0, 0, err 19 | } 20 | err = p.Scan(&Protocol, &ServerAddress, &ServerPort, &Intention) 21 | return int32(Protocol), int32(Intention), err 22 | } 23 | -------------------------------------------------------------------------------- /server/internal/bvh/bound.go: -------------------------------------------------------------------------------- 1 | package bvh 2 | 3 | import ( 4 | "math" 5 | 6 | "golang.org/x/exp/constraints" 7 | ) 8 | 9 | type AABB[I constraints.Signed | constraints.Float, V interface { 10 | Add(V) V 11 | Sub(V) V 12 | Max(V) V 13 | Min(V) V 14 | Less(V) bool 15 | More(V) bool 16 | Sum() I 17 | }] struct{ Upper, Lower V } 18 | 19 | func (aabb AABB[I, V]) WithIn(point V) bool { 20 | return aabb.Lower.Less(point) && aabb.Upper.More(point) 21 | } 22 | 23 | func (aabb AABB[I, V]) Touch(other AABB[I, V]) bool { 24 | return aabb.Lower.Less(other.Upper) && other.Lower.Less(aabb.Upper) && 25 | aabb.Upper.More(other.Lower) && other.Upper.More(aabb.Lower) 26 | } 27 | 28 | func (aabb AABB[I, V]) Union(other AABB[I, V]) AABB[I, V] { 29 | return AABB[I, V]{Upper: aabb.Upper.Max(other.Upper), Lower: aabb.Lower.Min(other.Lower)} 30 | } 31 | func (aabb AABB[I, V]) Surface() I { return aabb.Upper.Sub(aabb.Lower).Sum() * 2 } 32 | 33 | type Sphere[I constraints.Float, V interface { 34 | Add(V) V 35 | Sub(V) V 36 | Mul(I) V 37 | Max(V) V 38 | Min(V) V 39 | Less(V) bool 40 | More(V) bool 41 | Norm() I 42 | Sum() I 43 | }] struct { 44 | Center V 45 | R I 46 | } 47 | 48 | func (s Sphere[I, V]) WithIn(point V) bool { 49 | return s.Center.Sub(point).Norm() < s.R 50 | } 51 | 52 | func (s Sphere[I, V]) Touch(other Sphere[I, V]) bool { 53 | return s.Center.Sub(other.Center).Norm() < s.R+other.R 54 | } 55 | 56 | func (s Sphere[I, V]) Union(other Sphere[I, V]) Sphere[I, V] { 57 | d := other.Center.Sub(s.Center).Norm() 58 | r1r2d := (s.R - other.R) / d 59 | return Sphere[I, V]{ 60 | Center: s.Center.Mul(1 + r1r2d).Add(other.Center.Mul(1 - r1r2d)), 61 | R: d + s.R + other.R, 62 | } 63 | } 64 | func (s Sphere[I, V]) Surface() I { return 2 * math.Pi * s.R } 65 | -------------------------------------------------------------------------------- /server/internal/bvh/bound_test.go: -------------------------------------------------------------------------------- 1 | package bvh 2 | 3 | import "testing" 4 | 5 | func TestAABB_WithIn(t *testing.T) { 6 | aabb := AABB[float64, Vec2[float64]]{ 7 | Upper: Vec2[float64]{2, 2}, 8 | Lower: Vec2[float64]{-1, -1}, 9 | } 10 | if !aabb.WithIn(Vec2[float64]{0, 0}) { 11 | panic("(0, 0) should included") 12 | } 13 | if aabb.WithIn(Vec2[float64]{-2, -2}) { 14 | panic("(-2, -2) shouldn't included") 15 | } 16 | 17 | aabb2 := AABB[int, Vec3[int]]{ 18 | Upper: Vec3[int]{1, 1, 1}, 19 | Lower: Vec3[int]{-1, -1, -1}, 20 | } 21 | if !aabb2.WithIn(Vec3[int]{0, 0, 0}) { 22 | panic("(0, 0, 0) should included") 23 | } 24 | if aabb2.WithIn(Vec3[int]{-2, -2, 0}) { 25 | panic("(-2, -2, 0) shouldn't included") 26 | } 27 | 28 | sphere := Sphere[float64, Vec2[float64]]{ 29 | Center: Vec2[float64]{0, 0}, 30 | R: 1.0, 31 | } 32 | if !sphere.WithIn(Vec2[float64]{0, 0}) { 33 | t.Errorf("(0,0) is in") 34 | } 35 | if sphere.WithIn(Vec2[float64]{1, 1}) { 36 | t.Errorf("(1,1) isn't in") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /yggdrasil/refresh.go: -------------------------------------------------------------------------------- 1 | package yggdrasil 2 | 3 | import "fmt" 4 | 5 | type refreshPayload struct { 6 | Tokens 7 | SelectedProfile *Profile `json:"selectedProfile,omitempty"` 8 | 9 | RequestUser bool `json:"requestUser"` 10 | } 11 | 12 | // Refresh refreshes a valid accessToken. 13 | // 14 | // It can be used to keep a user logged in between 15 | // gaming sessions and is preferred over storing 16 | // the user's password in a file 17 | func (a *Access) Refresh(profile *Profile) error { 18 | pl := refreshPayload{ 19 | Tokens: a.ar.Tokens, 20 | SelectedProfile: profile, // used to change profile, don't use now 21 | RequestUser: true, 22 | } 23 | 24 | resp := struct { 25 | *authResp 26 | *Error 27 | }{authResp: &a.ar} 28 | 29 | err := post("/refresh", pl, &resp) 30 | if err != nil { 31 | return fmt.Errorf("post fail: %v", err) 32 | } 33 | 34 | if resp.Error != nil { 35 | return resp.Error 36 | } 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /yggdrasil/signout.go: -------------------------------------------------------------------------------- 1 | package yggdrasil 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | // SignOut invalidates accessTokens using an account's username and password. 9 | func SignOut(user, password string) error { 10 | pl := proof{ 11 | UserName: user, 12 | Password: password, 13 | } 14 | 15 | resp, err := rawPost("/signout", pl) 16 | if err != nil { 17 | return fmt.Errorf("request fail: %v", err) 18 | } 19 | defer resp.Body.Close() 20 | 21 | if resp.StatusCode != 204 { 22 | var err Error 23 | if err := json.NewDecoder(resp.Body).Decode(&err); err != nil { 24 | return fmt.Errorf("unmarshal error fail: %v", err) 25 | } 26 | return fmt.Errorf("invalidate error: %v", err) 27 | } 28 | 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /yggdrasil/user/property.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/google/uuid" 7 | 8 | pk "github.com/Tnze/go-mc/net/packet" 9 | ) 10 | 11 | type Property struct { 12 | Name, Value, Signature string 13 | } 14 | 15 | func (p Property) WriteTo(w io.Writer) (n int64, err error) { 16 | return pk.Tuple{ 17 | pk.String(p.Name), 18 | pk.String(p.Value), 19 | pk.Option[pk.String, *pk.String]{ 20 | Has: p.Signature != "", 21 | Val: pk.String(p.Signature), 22 | }, 23 | }.WriteTo(w) 24 | } 25 | 26 | func (p *Property) ReadFrom(r io.Reader) (n int64, err error) { 27 | var signature pk.Option[pk.String, *pk.String] 28 | n, err = pk.Tuple{ 29 | (*pk.String)(&p.Name), 30 | (*pk.String)(&p.Value), 31 | &signature, 32 | }.ReadFrom(r) 33 | p.Signature = string(signature.Val) 34 | return 35 | } 36 | 37 | // Texture includes player's skin and cape 38 | type Texture struct { 39 | TimeStamp int64 `json:"timestamp"` 40 | ID uuid.UUID `json:"profileId"` 41 | Name string `json:"profileName"` 42 | Textures struct { 43 | SKIN, CAPE struct { 44 | URL string `json:"url"` 45 | } 46 | } `json:"textures"` 47 | } 48 | -------------------------------------------------------------------------------- /yggdrasil/user/pubkey.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "crypto" 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "errors" 8 | "io" 9 | "time" 10 | 11 | pk "github.com/Tnze/go-mc/net/packet" 12 | ) 13 | 14 | type PublicKey struct { 15 | ExpiresAt time.Time 16 | PubKey *rsa.PublicKey 17 | Signature []byte 18 | } 19 | 20 | func (p PublicKey) WriteTo(w io.Writer) (n int64, err error) { 21 | pubKeyEncoded, err := x509.MarshalPKIXPublicKey(p.PubKey) 22 | if err != nil { 23 | return 0, err 24 | } 25 | return pk.Tuple{ 26 | pk.Long(p.ExpiresAt.UnixMilli()), 27 | pk.ByteArray(pubKeyEncoded), 28 | pk.ByteArray(p.Signature), 29 | }.WriteTo(w) 30 | } 31 | 32 | func (p *PublicKey) ReadFrom(r io.Reader) (n int64, err error) { 33 | var ( 34 | ExpiresAt pk.Long 35 | PubKey pk.ByteArray 36 | Signature pk.ByteArray 37 | ) 38 | n, err = pk.Tuple{ 39 | &ExpiresAt, 40 | &PubKey, 41 | &Signature, 42 | }.ReadFrom(r) 43 | if err != nil { 44 | return n, err 45 | } 46 | p.ExpiresAt = time.UnixMilli(int64(ExpiresAt)) 47 | pubKey, err := x509.ParsePKIXPublicKey(PubKey) 48 | if err != nil { 49 | return n, err 50 | } 51 | if key, ok := pubKey.(*rsa.PublicKey); !ok { 52 | return n, errors.New("expect RSA public key") 53 | } else { 54 | p.PubKey = key 55 | } 56 | 57 | p.Signature = Signature 58 | return n, nil 59 | } 60 | 61 | func (p *PublicKey) Verify() bool { 62 | if p.ExpiresAt.Before(time.Now()) { 63 | return false 64 | } 65 | encoded, err := x509.MarshalPKIXPublicKey(p.PubKey) 66 | if err != nil { 67 | return false 68 | } 69 | return VerifySignature(encoded, p.Signature) 70 | } 71 | 72 | func (p *PublicKey) VerifyMessage(hash, signature []byte) error { 73 | return rsa.VerifyPKCS1v15(p.PubKey, crypto.SHA256, hash, signature) 74 | } 75 | -------------------------------------------------------------------------------- /yggdrasil/user/validator.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "crypto" 5 | "crypto/rsa" 6 | "crypto/sha256" 7 | "crypto/x509" 8 | _ "embed" 9 | "encoding/base64" 10 | "io" 11 | ) 12 | 13 | //go:embed yggdrasil_session_pubkey.der 14 | var pubKeyBytes []byte 15 | var pubKey = unwrap(x509.ParsePKIXPublicKey(pubKeyBytes)).(*rsa.PublicKey) 16 | 17 | // VerifySignature has the same functional as 18 | // net.minecraft.world.entity.player.ProfilePublicKey.Data#validateSignature 19 | func VerifySignature(profilePubKey, signature []byte) bool { 20 | hash := sha256.New() 21 | unwrap(hash.Write([]byte("-----BEGIN RSA PRIVATE KEY-----\n"))) 22 | breaker := lineBreaker{out: hash} 23 | enc := base64.NewEncoder(base64.StdEncoding, &breaker) 24 | unwrap(enc.Write(profilePubKey)) 25 | must(enc.Close()) 26 | must(breaker.Close()) 27 | unwrap(hash.Write([]byte("\n-----END RSA PRIVATE KEY-----\n"))) 28 | return rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hash.Sum(nil), signature) != nil 29 | } 30 | 31 | const pemLineLength = 76 32 | 33 | var nl = []byte{'\n'} 34 | 35 | type lineBreaker struct { 36 | line [pemLineLength]byte 37 | used int 38 | out io.Writer 39 | } 40 | 41 | func (l *lineBreaker) Write(b []byte) (n int, err error) { 42 | if l.used+len(b) < pemLineLength { 43 | copy(l.line[l.used:], b) 44 | l.used += len(b) 45 | return len(b), nil 46 | } 47 | 48 | n, err = l.out.Write(l.line[0:l.used]) 49 | if err != nil { 50 | return 51 | } 52 | excess := pemLineLength - l.used 53 | l.used = 0 54 | 55 | n1, err := l.out.Write(b[0:excess]) 56 | if err != nil { 57 | return n + n1, err 58 | } 59 | 60 | n2, err := l.out.Write(nl) 61 | if err != nil { 62 | return n + n1 + n2, err 63 | } 64 | 65 | n3, err := l.Write(b[excess:]) 66 | return n1 + n2 + n3, err 67 | } 68 | 69 | func (l *lineBreaker) Close() (err error) { 70 | if l.used > 0 { 71 | _, err = l.out.Write(l.line[0:l.used]) 72 | if err != nil { 73 | return 74 | } 75 | _, err = l.out.Write(nl) 76 | } 77 | 78 | return 79 | } 80 | 81 | func must(err error) { 82 | if err != nil { 83 | panic(err) 84 | } 85 | } 86 | 87 | func unwrap[T any](v T, err error) T { 88 | if err != nil { 89 | panic(err) 90 | } 91 | return v 92 | } 93 | -------------------------------------------------------------------------------- /yggdrasil/user/yggdrasil_session_pubkey.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tnze/go-mc/539b4a3a7f030332eb58b8a946116ae7907630d2/yggdrasil/user/yggdrasil_session_pubkey.der -------------------------------------------------------------------------------- /yggdrasil/validate.go: -------------------------------------------------------------------------------- 1 | package yggdrasil 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | // Validate checks if an accessToken is usable for authentication with a Minecraft server. 9 | func (a *Access) Validate() (bool, error) { 10 | pl := a.ar.Tokens 11 | 12 | resp, err := rawPost("/validate", pl) 13 | if err != nil { 14 | return false, fmt.Errorf("request fail: %v", err) 15 | } 16 | 17 | return resp.StatusCode == 204, resp.Body.Close() 18 | } 19 | 20 | // Invalidate invalidates accessTokens using a client/access token pair. 21 | func (a *Access) Invalidate() error { 22 | pl := a.ar.Tokens 23 | 24 | resp, err := rawPost("/invalidate", pl) 25 | if err != nil { 26 | return fmt.Errorf("request fail: %v", err) 27 | } 28 | defer resp.Body.Close() 29 | 30 | if resp.StatusCode != 204 { 31 | content, _ := io.ReadAll(resp.Body) 32 | return fmt.Errorf("invalidate error: %v: %s", resp.Status, content) 33 | } 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /yggdrasil/yggdrasil.go: -------------------------------------------------------------------------------- 1 | // Package yggdrasil implement Yggdrasil protocol. 2 | // 3 | // Minecraft 1.6 introduced a new authentication scheme called Yggdrasil 4 | // which completely replaces the previous authentication system. 5 | // Mojang's other game, Scrolls, uses this method of authentication as well. 6 | // Mojang has said that this authentication system should be used by everyone for custom logins, 7 | // but credentials should never be collected from users. ----- https://wiki.vg 8 | package yggdrasil 9 | 10 | import ( 11 | "bytes" 12 | "encoding/json" 13 | "fmt" 14 | "net/http" 15 | ) 16 | 17 | type Error struct { 18 | Err string `json:"error"` 19 | ErrMsg string `json:"errorMessage"` 20 | Cause string `json:"cause"` 21 | } 22 | 23 | func (e Error) Error() string { 24 | return e.Err + ": " + e.ErrMsg + ", " + e.Cause 25 | } 26 | 27 | var AuthURL = "https://authserver.mojang.com" 28 | 29 | var client = http.DefaultClient 30 | 31 | func post(endpoint string, payload any, resp any) error { 32 | rowResp, err := rawPost(endpoint, payload) 33 | if err != nil { 34 | return fmt.Errorf("request fail: %v", err) 35 | } 36 | defer rowResp.Body.Close() 37 | 38 | err = json.NewDecoder(rowResp.Body).Decode(resp) 39 | if err != nil { 40 | return fmt.Errorf("parse resp fail: %v", err) 41 | } 42 | 43 | return nil 44 | } 45 | 46 | func rawPost(endpoint string, payload any) (*http.Response, error) { 47 | data, err := json.Marshal(payload) 48 | if err != nil { 49 | return nil, fmt.Errorf("marshal payload fail: %v", err) 50 | } 51 | 52 | PostRequest, err := http.NewRequest( 53 | http.MethodPost, 54 | AuthURL+endpoint, 55 | bytes.NewReader(data)) 56 | if err != nil { 57 | return nil, fmt.Errorf("make request error: %v", err) 58 | } 59 | 60 | PostRequest.Header.Set("User-agent", "go-mc") 61 | PostRequest.Header.Set("Connection", "keep-alive") 62 | PostRequest.Header.Set("Content-Type", "application/json") 63 | 64 | // Do 65 | return client.Do(PostRequest) 66 | } 67 | -------------------------------------------------------------------------------- /yggdrasil/yggdrasil_test.go: -------------------------------------------------------------------------------- 1 | package yggdrasil 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func ExampleAuthenticate() { 9 | resp, err := Authenticate("", "") 10 | if err != nil { 11 | panic(err) 12 | } 13 | 14 | fmt.Println(resp.SelectedProfile()) 15 | fmt.Println(resp.AccessToken()) 16 | } 17 | 18 | func Example() { 19 | var user, password string // set your proof 20 | 21 | // Sign in 22 | resp, err := Authenticate(user, password) 23 | if err != nil { 24 | fmt.Println(err) 25 | os.Exit(1) 26 | } 27 | id, name := resp.SelectedProfile() 28 | fmt.Println("user:", name) 29 | fmt.Println("uuid:", id) 30 | fmt.Println("astk:", resp.AccessToken()) 31 | 32 | // Refresh access token 33 | if err := resp.Refresh(nil); err != nil { 34 | fmt.Println(err) 35 | os.Exit(1) 36 | } 37 | 38 | id, name = resp.SelectedProfile() 39 | fmt.Println("user:", name) 40 | fmt.Println("uuid:", id) 41 | fmt.Println("astk:", resp.AccessToken()) 42 | 43 | // Check access token 44 | ok, err := resp.Validate() 45 | if err != nil { 46 | fmt.Println(err) 47 | os.Exit(1) 48 | } 49 | fmt.Println("at status: ", ok) 50 | 51 | // Invalidate access token 52 | err = resp.Invalidate() 53 | if err != nil { 54 | fmt.Println(err) 55 | os.Exit(1) 56 | } 57 | 58 | // Check access token 59 | ok, err = resp.Validate() 60 | if err != nil { 61 | fmt.Println(err) 62 | os.Exit(1) 63 | } 64 | fmt.Println("at status: ", ok) 65 | 66 | // Sign out 67 | err = SignOut(user, password) 68 | if err != nil { 69 | fmt.Println(err) 70 | os.Exit(1) 71 | } 72 | } 73 | --------------------------------------------------------------------------------