initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.idea
|
||||||
4
config.toml
Normal file
4
config.toml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
[Connection]
|
||||||
|
LocalAddress = "0.0.0.0:19132"
|
||||||
|
RemoteAddress = "play.venitymc.com:19132"
|
||||||
31
go.mod
Normal file
31
go.mod
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
module github.com/akmalfairuz/legacy-version
|
||||||
|
|
||||||
|
go 1.23.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/df-mc/dragonfly v0.9.20-0.20241229163702-cc7e4ee0e3ce // indirect
|
||||||
|
github.com/df-mc/goleveldb v1.1.9 // indirect
|
||||||
|
github.com/df-mc/worldupgrader v1.0.18 // indirect
|
||||||
|
github.com/go-gl/mathgl v1.2.0 // indirect
|
||||||
|
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
|
github.com/muhammadmuzzammil1998/jsonc v1.0.0 // indirect
|
||||||
|
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||||
|
github.com/samber/lo v1.47.0 // indirect
|
||||||
|
github.com/sandertv/go-raknet v1.14.2 // indirect
|
||||||
|
github.com/sandertv/gophertunnel v1.43.1 // indirect
|
||||||
|
github.com/segmentio/fasthash v1.0.3 // indirect
|
||||||
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
|
||||||
|
golang.org/x/image v0.21.0 // indirect
|
||||||
|
golang.org/x/mod v0.22.0 // indirect
|
||||||
|
golang.org/x/net v0.33.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.24.0 // indirect
|
||||||
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
)
|
||||||
124
go.sum
Normal file
124
go.sum
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 h1:/G0ghZwrhou0Wq21qc1vXXMm/t/aKWkALWwITptKbE0=
|
||||||
|
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9/go.mod h1:TOk10ahXejq9wkEaym3KPRNeuR/h5Jx+s8QRWIa2oTM=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/df-mc/dragonfly v0.9.20-0.20241229163702-cc7e4ee0e3ce h1:mhBn/ezojzACPHeg+Y9NnU2bbYCozx27RgpfWUPw4i0=
|
||||||
|
github.com/df-mc/dragonfly v0.9.20-0.20241229163702-cc7e4ee0e3ce/go.mod h1:hH1eU9lmucLNLehzxXzOUOmHJQLz3DLpQMUqIKcz8YI=
|
||||||
|
github.com/df-mc/goleveldb v1.1.9 h1:ihdosZyy5jkQKrxucTQmN90jq/2lUwQnJZjIYIC/9YU=
|
||||||
|
github.com/df-mc/goleveldb v1.1.9/go.mod h1:+NHCup03Sci5q84APIA21z3iPZCuk6m6ABtg4nANCSk=
|
||||||
|
github.com/df-mc/worldupgrader v1.0.18 h1:Q34X9ID/hGuDyj9oiq+dpyjOaJdNxhVmVUepf/EPYDA=
|
||||||
|
github.com/df-mc/worldupgrader v1.0.18/go.mod h1:tsSOLTRm9mpG7VHvYpAjjZrkRHWmSbKZAm9bOLNnlDk=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/go-gl/mathgl v1.1.0 h1:0lzZ+rntPX3/oGrDzYGdowSLC2ky8Osirvf5uAwfIEA=
|
||||||
|
github.com/go-gl/mathgl v1.1.0/go.mod h1:yhpkQzEiH9yPyxDUGzkmgScbaBVlhC06qodikEM0ZwQ=
|
||||||
|
github.com/go-gl/mathgl v1.2.0 h1:v2eOj/y1B2afDxF6URV1qCYmo1KW08lAMtTbOn3KXCY=
|
||||||
|
github.com/go-gl/mathgl v1.2.0/go.mod h1:pf9+b5J3LFP7iZ4XXaVzZrCle0Q/vNpB/vDe5+3ulRE=
|
||||||
|
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||||
|
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
|
github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs=
|
||||||
|
github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
|
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
||||||
|
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||||
|
github.com/sandertv/go-raknet v1.14.2 h1:UZLyHn5yQU2Dq2GVq/LlxwAUikaq4q4AA1rl/Pf3AXQ=
|
||||||
|
github.com/sandertv/go-raknet v1.14.2/go.mod h1:/yysjwfCXm2+2OY8mBazLzcxJ3irnylKCyG3FLgUPVU=
|
||||||
|
github.com/sandertv/gophertunnel v1.43.0 h1:zoB0nngGXokBlj+ge4ISvbInEd/1xbrIlclC/POCTgc=
|
||||||
|
github.com/sandertv/gophertunnel v1.43.0/go.mod h1:krvLSeRUNQ2iEYJNEgzrKtWO8W5ybZxN5lFfSCkHoNk=
|
||||||
|
github.com/sandertv/gophertunnel v1.43.1-0.20241229203958-5af8736ae319 h1:yxDqIvO9BkUFtK2SpnngPDnPQ9hnhq3IGZ6qiO2rFHg=
|
||||||
|
github.com/sandertv/gophertunnel v1.43.1-0.20241229203958-5af8736ae319/go.mod h1:XuEJo+ARim+NKiD90Z56sQRcDtCOErz26e2bt3LEd9I=
|
||||||
|
github.com/sandertv/gophertunnel v1.43.1 h1:wY6Fy8dRMKtpZUQzCR35o9k05135vOJZhrz0GF6OXFI=
|
||||||
|
github.com/sandertv/gophertunnel v1.43.1/go.mod h1:XuEJo+ARim+NKiD90Z56sQRcDtCOErz26e2bt3LEd9I=
|
||||||
|
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
|
||||||
|
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
|
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||||
|
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||||
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
|
||||||
|
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
|
golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||||
|
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||||
|
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||||
|
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||||
|
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||||
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
|
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||||
|
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||||
|
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
53
internal/block_states.go
Normal file
53
internal/block_states.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/df-mc/worldupgrader/blockupgrader"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StateHash is a struct that may be used as a map key for block states. It contains the name of the block state
|
||||||
|
// and an encoded version of the properties.
|
||||||
|
type StateHash struct {
|
||||||
|
Name, Properties string
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashState produces a hash for the block properties held by the blockState.
|
||||||
|
func HashState(state blockupgrader.BlockState) StateHash {
|
||||||
|
if state.Properties == nil {
|
||||||
|
return StateHash{Name: state.Name}
|
||||||
|
}
|
||||||
|
keys := make([]string, 0, len(state.Properties))
|
||||||
|
for k := range state.Properties {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Slice(keys, func(i, j int) bool {
|
||||||
|
return keys[i] < keys[j]
|
||||||
|
})
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
for _, k := range keys {
|
||||||
|
switch v := state.Properties[k].(type) {
|
||||||
|
case bool:
|
||||||
|
if v {
|
||||||
|
b.WriteByte(1)
|
||||||
|
} else {
|
||||||
|
b.WriteByte(0)
|
||||||
|
}
|
||||||
|
case uint8:
|
||||||
|
b.WriteByte(v)
|
||||||
|
case int32:
|
||||||
|
a := *(*[4]byte)(unsafe.Pointer(&v))
|
||||||
|
b.Write(a[:])
|
||||||
|
case string:
|
||||||
|
b.WriteString(v)
|
||||||
|
default:
|
||||||
|
// If block encoding is broken, we want to find out as soon as possible. This saves a lot of time
|
||||||
|
// debugging in-game.
|
||||||
|
panic(fmt.Sprintf("invalid block property type %T for property %v", v, k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return StateHash{Name: state.Name, Properties: b.String()}
|
||||||
|
}
|
||||||
119
internal/chunk/chunk.go
Normal file
119
internal/chunk/chunk.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package chunk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Chunk is a segment in the world with a size of 16x16x256 blocks. A chunk contains multiple sub chunks
|
||||||
|
// and stores other information such as biomes.
|
||||||
|
// It is not safe to call methods on Chunk simultaneously from multiple goroutines.
|
||||||
|
type Chunk struct {
|
||||||
|
sync.Mutex
|
||||||
|
// r holds the (vertical) range of the Chunk. It includes both the minimum and maximum coordinates.
|
||||||
|
r cube.Range
|
||||||
|
// air is the runtime ID of air.
|
||||||
|
air uint32
|
||||||
|
// sub holds all sub chunks part of the chunk. The pointers held by the array are nil if no sub chunk is
|
||||||
|
// allocated at the indices.
|
||||||
|
sub []*SubChunk
|
||||||
|
// biomes is an array of biome IDs. There is one biome ID for every column in the chunk.
|
||||||
|
biomes []*PalettedStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
// New initialises a new chunk and returns it, so that it may be used.
|
||||||
|
func New(air uint32, r cube.Range) *Chunk {
|
||||||
|
n := (r.Height() >> 4) + 1
|
||||||
|
sub, biomes := make([]*SubChunk, n), make([]*PalettedStorage, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
sub[i] = NewSubChunk(air)
|
||||||
|
biomes[i] = emptyStorage(0)
|
||||||
|
}
|
||||||
|
return &Chunk{r: r, air: air, sub: sub, biomes: biomes}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range returns the cube.Range of the Chunk as passed to New.
|
||||||
|
func (chunk *Chunk) Range() cube.Range {
|
||||||
|
return chunk.r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub returns a list of all sub chunks present in the chunk.
|
||||||
|
func (chunk *Chunk) Sub() []*SubChunk {
|
||||||
|
return chunk.sub
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block returns the runtime ID of the block at a given x, y and z in a chunk at the given layer. If no
|
||||||
|
// sub chunk exists at the given y, the block is assumed to be air.
|
||||||
|
func (chunk *Chunk) Block(x uint8, y int16, z uint8, layer uint8) uint32 {
|
||||||
|
sub := chunk.subChunk(y)
|
||||||
|
if sub.Empty() || uint8(len(sub.storages)) <= layer {
|
||||||
|
return chunk.air
|
||||||
|
}
|
||||||
|
return sub.storages[layer].At(x, uint8(y), z)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBlock sets the runtime ID of a block at a given x, y and z in a chunk at the given layer. If no
|
||||||
|
// SubChunk exists at the given y, a new SubChunk is created and the block is set.
|
||||||
|
func (chunk *Chunk) SetBlock(x uint8, y int16, z uint8, layer uint8, block uint32) {
|
||||||
|
sub := chunk.sub[chunk.subIndex(y)]
|
||||||
|
if uint8(len(sub.storages)) <= layer && block == chunk.air {
|
||||||
|
// Air was set at n layer, but there were less than n layers, so there already was air there.
|
||||||
|
// Don't do anything with this, just return.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sub.Layer(layer).Set(x, uint8(y), z, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BiomeSub returns a list of all biome sub chunks present in the chunk.
|
||||||
|
func (chunk *Chunk) BiomeSub() []*PalettedStorage {
|
||||||
|
return chunk.biomes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Biome returns the biome ID at a specific column in the chunk.
|
||||||
|
func (chunk *Chunk) Biome(x uint8, y int16, z uint8) uint32 {
|
||||||
|
return chunk.biomes[chunk.subIndex(y)].At(x, uint8(y), z)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBiome sets the biome ID at a specific column in the chunk.
|
||||||
|
func (chunk *Chunk) SetBiome(x uint8, y int16, z uint8, biome uint32) {
|
||||||
|
chunk.biomes[chunk.subIndex(y)].Set(x, uint8(y), z, biome)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HighestBlock iterates from the highest non-empty sub chunk downwards to find the Y value of the highest
|
||||||
|
// non-air block at an x and z. If no blocks are present in the column, 0 is returned.
|
||||||
|
func (chunk *Chunk) HighestBlock(x, z uint8) int16 {
|
||||||
|
for index := int16(len(chunk.sub) - 1); index >= 0; index-- {
|
||||||
|
if sub := chunk.sub[index]; !sub.Empty() {
|
||||||
|
for y := 15; y >= 0; y-- {
|
||||||
|
if rid := sub.storages[0].At(x, uint8(y), z); rid != chunk.air {
|
||||||
|
return int16(y) | chunk.subY(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return int16(chunk.r[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact compacts the chunk as much as possible, getting rid of any sub chunks that are empty, and compacts
|
||||||
|
// all storages in the sub chunks to occupy as little space as possible.
|
||||||
|
// Compact should be called right before the chunk is saved in order to optimise the storage space.
|
||||||
|
func (chunk *Chunk) Compact() {
|
||||||
|
for i := range chunk.sub {
|
||||||
|
chunk.sub[i].compact()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// subChunk finds the correct SubChunk in the Chunk by a Y value.
|
||||||
|
func (chunk *Chunk) subChunk(y int16) *SubChunk {
|
||||||
|
return chunk.sub[chunk.subIndex(y)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// subIndex returns the sub chunk Y index matching the y value passed.
|
||||||
|
func (chunk *Chunk) subIndex(y int16) int16 {
|
||||||
|
return (y - int16(chunk.r[0])) >> 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// subY returns the sub chunk Y value matching the index passed.
|
||||||
|
func (chunk *Chunk) subY(index int16) int16 {
|
||||||
|
return (index << 4) + int16(chunk.r[0])
|
||||||
|
}
|
||||||
145
internal/chunk/decode.go
Normal file
145
internal/chunk/decode.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package chunk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetworkDecode decodes the network serialised data passed into a Chunk if successful. If not, the chunk
|
||||||
|
// returned is nil and the error non-nil.
|
||||||
|
// The sub chunk count passed must be that found in the LevelChunk packet.
|
||||||
|
// noinspection GoUnusedExportedFunction
|
||||||
|
func NetworkDecode(air uint32, buf *bytes.Buffer, count int, oldFormat bool, r cube.Range, pse Encoding, pe PaletteEncoding) (*Chunk, error) {
|
||||||
|
var (
|
||||||
|
c = New(air, r)
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
index := uint8(i)
|
||||||
|
if oldFormat {
|
||||||
|
index += 4
|
||||||
|
}
|
||||||
|
c.sub[index], err = DecodeSubChunk(air, r, buf, &index, NetworkEncoding, pse, pe)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if oldFormat {
|
||||||
|
// Read the old biomes.
|
||||||
|
biomes := make([]byte, 256)
|
||||||
|
if _, err := buf.Read(biomes); err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading biomes: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make our 2D biomes 3D.
|
||||||
|
for x := 0; x < 16; x++ {
|
||||||
|
for z := 0; z < 16; z++ {
|
||||||
|
id := biomes[(x&15)|(z&15)<<4]
|
||||||
|
for y := r.Min(); y <= r.Max(); y++ {
|
||||||
|
c.SetBiome(uint8(x), int16(y), uint8(z), uint32(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var last *PalettedStorage
|
||||||
|
for i := 0; i < len(c.sub); i++ {
|
||||||
|
b, err := decodePalettedStorage(buf, NetworkEncoding, pse, BiomePaletteEncoding)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if b == nil {
|
||||||
|
// b == nil means this paletted storage had the flag pointing to the previous one. It basically means we should
|
||||||
|
// inherit whatever palette we decoded last.
|
||||||
|
if i == 0 {
|
||||||
|
// This should never happen and there is no way to handle this.
|
||||||
|
return nil, fmt.Errorf("first biome storage pointed to previous one")
|
||||||
|
}
|
||||||
|
b = last
|
||||||
|
} else {
|
||||||
|
last = b
|
||||||
|
}
|
||||||
|
c.biomes[i] = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeSubChunk decodes a SubChunk from a bytes.Buffer. The Encoding passed defines how the block storages of the
|
||||||
|
// SubChunk are decoded.
|
||||||
|
func DecodeSubChunk(air uint32, r cube.Range, buf *bytes.Buffer, index *byte, e Encoding, pse Encoding, pe PaletteEncoding) (*SubChunk, error) {
|
||||||
|
ver, err := buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading version: %w", err)
|
||||||
|
}
|
||||||
|
sub := NewSubChunk(air)
|
||||||
|
switch ver {
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown sub chunk version %v: can't decode", ver)
|
||||||
|
case 1:
|
||||||
|
// Version 1 only has one layer for each sub chunk, but uses the format with palettes.
|
||||||
|
storage, err := decodePalettedStorage(buf, e, pse, pe)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sub.storages = append(sub.storages, storage)
|
||||||
|
case 8, 9:
|
||||||
|
// Version 8 allows up to 256 layers for one sub chunk.
|
||||||
|
storageCount, err := buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading storage count: %w", err)
|
||||||
|
}
|
||||||
|
if ver == 9 {
|
||||||
|
uIndex, err := buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading subchunk index: %w", err)
|
||||||
|
}
|
||||||
|
// The index as written here isn't the actual index of the subchunk within the chunk. Rather, it is the Y
|
||||||
|
// value of the subchunk. This means that we need to translate it to an index.
|
||||||
|
*index = uint8(int8(uIndex) - int8(r[0]>>4))
|
||||||
|
}
|
||||||
|
sub.storages = make([]*PalettedStorage, storageCount)
|
||||||
|
|
||||||
|
for i := byte(0); i < storageCount; i++ {
|
||||||
|
sub.storages[i], err = decodePalettedStorage(buf, e, pse, pe)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sub, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePalettedStorage decodes a PalettedStorage from a bytes.Buffer. The Encoding passed is used to read either a
|
||||||
|
// network or disk block storage.
|
||||||
|
func decodePalettedStorage(buf *bytes.Buffer, e Encoding, pse Encoding, pe PaletteEncoding) (*PalettedStorage, error) {
|
||||||
|
blockSize, err := buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading block size: %w", err)
|
||||||
|
}
|
||||||
|
if e == NetworkEncoding && blockSize&1 != 1 {
|
||||||
|
e = pse
|
||||||
|
}
|
||||||
|
|
||||||
|
blockSize >>= 1
|
||||||
|
if blockSize == 0x7f {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
size := paletteSize(blockSize)
|
||||||
|
uint32Count := size.uint32s()
|
||||||
|
|
||||||
|
uint32s := make([]uint32, uint32Count)
|
||||||
|
byteCount := uint32Count * 4
|
||||||
|
|
||||||
|
data := buf.Next(byteCount)
|
||||||
|
if len(data) != byteCount {
|
||||||
|
return nil, fmt.Errorf("cannot read paletted storage (size=%v) %T: not enough block data present: expected %v bytes, got %v", blockSize, pe, byteCount, len(data))
|
||||||
|
}
|
||||||
|
for i := 0; i < uint32Count; i++ {
|
||||||
|
// Explicitly don't use the binary package to greatly improve performance of reading the uint32s.
|
||||||
|
uint32s[i] = uint32(data[i*4]) | uint32(data[i*4+1])<<8 | uint32(data[i*4+2])<<16 | uint32(data[i*4+3])<<24
|
||||||
|
}
|
||||||
|
p, err := e.DecodePalette(buf, paletteSize(blockSize), pe)
|
||||||
|
return newPalettedStorage(uint32s, p), err
|
||||||
|
}
|
||||||
92
internal/chunk/encode.go
Normal file
92
internal/chunk/encode.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package chunk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pool is used to pool byte buffers used for encoding chunks.
|
||||||
|
var pool = sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return bytes.NewBuffer(make([]byte, 0, 1024))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func NetworkEncode(air uint32, c *Chunk, oldFormat bool, pe PaletteEncoding) ([]byte, error) {
|
||||||
|
buf := pool.Get().(*bytes.Buffer)
|
||||||
|
for i := 0; i < len(c.sub); i++ {
|
||||||
|
_, _ = buf.Write(EncodeSubChunk(c.sub[i], NetworkEncoding, pe, SubChunkVersion8, c.r, i))
|
||||||
|
}
|
||||||
|
if oldFormat {
|
||||||
|
biomes := make([]byte, 256)
|
||||||
|
|
||||||
|
// Make our 3D biomes 2D.
|
||||||
|
for x := uint8(0); x < 16; x++ {
|
||||||
|
for z := uint8(0); z < 16; z++ {
|
||||||
|
biomes[(x&15)|(z&15)<<4] = byte(c.Biome(x, c.HighestBlock(x, z), z))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, _ = buf.Write(biomes)
|
||||||
|
} else {
|
||||||
|
_, _ = buf.Write(EncodeBiomes(c, NetworkEncoding))
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeSubChunk encodes a sub-chunk from a chunk into bytes. An Encoding may be passed to encode either for network or
|
||||||
|
// disk purposed, the most notable difference being that the network encoding generally uses varints and no NBT.
|
||||||
|
func EncodeSubChunk(s *SubChunk, e Encoding, pe PaletteEncoding, subChunkVer subChunkVersion, r cube.Range, ind int) []byte {
|
||||||
|
buf := pool.Get().(*bytes.Buffer)
|
||||||
|
defer func() {
|
||||||
|
buf.Reset()
|
||||||
|
pool.Put(buf)
|
||||||
|
}()
|
||||||
|
|
||||||
|
subChunkVer.EncodeHeader(buf, s, r, ind)
|
||||||
|
for _, storage := range s.storages {
|
||||||
|
encodePalettedStorage(buf, storage, nil, e, pe)
|
||||||
|
}
|
||||||
|
sub := make([]byte, buf.Len())
|
||||||
|
_, _ = buf.Read(sub)
|
||||||
|
return sub
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBiomes encodes the biomes of a chunk into bytes. An Encoding may be passed to encode either for network or
|
||||||
|
// disk purposed, the most notable difference being that the network encoding generally uses varints and no NBT.
|
||||||
|
func EncodeBiomes(c *Chunk, e Encoding) []byte {
|
||||||
|
buf := pool.Get().(*bytes.Buffer)
|
||||||
|
defer func() {
|
||||||
|
buf.Reset()
|
||||||
|
pool.Put(buf)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var previous *PalettedStorage
|
||||||
|
for _, b := range c.biomes {
|
||||||
|
encodePalettedStorage(buf, b, previous, e, BiomePaletteEncoding)
|
||||||
|
previous = b
|
||||||
|
}
|
||||||
|
biomes := make([]byte, buf.Len())
|
||||||
|
_, _ = buf.Read(biomes)
|
||||||
|
return biomes
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodePalettedStorage encodes a PalettedStorage into a bytes.Buffer. The Encoding passed is used to write the Palette
|
||||||
|
// of the PalettedStorage.
|
||||||
|
func encodePalettedStorage(buf *bytes.Buffer, storage, previous *PalettedStorage, e Encoding, pe PaletteEncoding) {
|
||||||
|
if storage.Equal(previous) {
|
||||||
|
_, _ = buf.Write([]byte{0x7f<<1 | e.Network()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b := make([]byte, len(storage.indices)*4+1)
|
||||||
|
b[0] = byte(storage.bitsPerIndex<<1) | e.Network()
|
||||||
|
|
||||||
|
for i, v := range storage.indices {
|
||||||
|
// Explicitly don't use the binary package to greatly improve performance of writing the uint32s.
|
||||||
|
b[i*4+1], b[i*4+2], b[i*4+3], b[i*4+4] = byte(v), byte(v>>8), byte(v>>16), byte(v>>24)
|
||||||
|
}
|
||||||
|
_, _ = buf.Write(b)
|
||||||
|
|
||||||
|
e.EncodePalette(buf, storage.palette, pe)
|
||||||
|
}
|
||||||
193
internal/chunk/encoding.go
Normal file
193
internal/chunk/encoding.go
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
package chunk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"github.com/akmalfairuz/legacy-version/mapping"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||||||
|
"github.com/df-mc/worldupgrader/blockupgrader"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/nbt"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SubChunkVersion is the current version of the written sub chunks, specifying the format they are
|
||||||
|
// written on disk and over network.
|
||||||
|
SubChunkVersion = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Encoding is an encoding type used for Chunk encoding. Implementations of this interface are DiskEncoding and
|
||||||
|
// NetworkEncoding, which can be used to encode a Chunk to an intermediate disk or network representation respectively.
|
||||||
|
Encoding interface {
|
||||||
|
EncodePalette(buf *bytes.Buffer, p *Palette, e PaletteEncoding)
|
||||||
|
DecodePalette(buf *bytes.Buffer, blockSize paletteSize, e PaletteEncoding) (*Palette, error)
|
||||||
|
Network() byte
|
||||||
|
}
|
||||||
|
// PaletteEncoding is an encoding type used for Chunk encoding. It is used to encode different types of palettes
|
||||||
|
// (for example, blocks or biomes) differently.
|
||||||
|
PaletteEncoding interface {
|
||||||
|
Encode(buf *bytes.Buffer, v uint32)
|
||||||
|
Decode(buf *bytes.Buffer) (uint32, error)
|
||||||
|
}
|
||||||
|
// Encoding is an encoding type used for Chunk encoding. Implementations of this interface are DiskEncoding and
|
||||||
|
// NetworkEncoding, which can be used to encode a Chunk to an intermediate disk or network representation respectively.
|
||||||
|
subChunkVersion interface {
|
||||||
|
EncodeHeader(buf *bytes.Buffer, s *SubChunk, r cube.Range, ind int)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// SubChunkVersion9 subChunkVersion9
|
||||||
|
SubChunkVersion9 subChunkVersion9
|
||||||
|
// SubChunkVersion8 subChunkVersion8
|
||||||
|
SubChunkVersion8 subChunkVersion8
|
||||||
|
|
||||||
|
// NetworkEncoding is the Encoding used for sending a Chunk over network. It does not use NBT and writes varints.
|
||||||
|
NetworkEncoding networkEncoding
|
||||||
|
// BiomePaletteEncoding is the paletteEncoding used for encoding a palette of biomes.
|
||||||
|
BiomePaletteEncoding biomePaletteEncoding
|
||||||
|
)
|
||||||
|
|
||||||
|
// networkEncoding implements the Chunk encoding for sending over network.
|
||||||
|
type networkEncoding struct{}
|
||||||
|
|
||||||
|
func (networkEncoding) Network() byte { return 1 }
|
||||||
|
func (networkEncoding) EncodePalette(buf *bytes.Buffer, p *Palette, _ PaletteEncoding) {
|
||||||
|
if p.size != 0 {
|
||||||
|
_ = protocol.WriteVarint32(buf, int32(p.Len()))
|
||||||
|
}
|
||||||
|
for _, val := range p.values {
|
||||||
|
_ = protocol.WriteVarint32(buf, int32(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (networkEncoding) DecodePalette(buf *bytes.Buffer, blockSize paletteSize, _ PaletteEncoding) (*Palette, error) {
|
||||||
|
var paletteCount int32 = 1
|
||||||
|
if blockSize != 0 {
|
||||||
|
if err := protocol.Varint32(buf, &paletteCount); err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading palette entry count: %w", err)
|
||||||
|
}
|
||||||
|
if paletteCount <= 0 {
|
||||||
|
return nil, fmt.Errorf("invalid palette entry count %v", paletteCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks, temp := make([]uint32, paletteCount), int32(0)
|
||||||
|
for i := int32(0); i < paletteCount; i++ {
|
||||||
|
if err := protocol.Varint32(buf, &temp); err != nil {
|
||||||
|
return nil, fmt.Errorf("error decoding palette entry: %w", err)
|
||||||
|
}
|
||||||
|
blocks[i] = uint32(temp)
|
||||||
|
}
|
||||||
|
return &Palette{values: blocks, size: blockSize}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// biomePaletteEncoding implements the encoding of biome palettes to disk.
|
||||||
|
type biomePaletteEncoding struct{}
|
||||||
|
|
||||||
|
func (biomePaletteEncoding) Encode(buf *bytes.Buffer, v uint32) {
|
||||||
|
_ = binary.Write(buf, binary.LittleEndian, v)
|
||||||
|
}
|
||||||
|
func (biomePaletteEncoding) Decode(buf *bytes.Buffer) (uint32, error) {
|
||||||
|
var v uint32
|
||||||
|
return v, binary.Read(buf, binary.LittleEndian, &v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockPaletteEncoding implements the encoding of block palettes to disk.
|
||||||
|
type BlockPaletteEncoding struct {
|
||||||
|
block mapping.Block
|
||||||
|
version int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockPaletteEncoding returns a new BlockPaletteEncoding using the block and version passed.
|
||||||
|
func NewBlockPaletteEncoding(block mapping.Block, version int32) BlockPaletteEncoding {
|
||||||
|
return BlockPaletteEncoding{block: block, version: version}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BlockPaletteEncoding) Encode(buf *bytes.Buffer, v uint32) {
|
||||||
|
// Get the block state registered with the runtime IDs we have in the palette of the block storage
|
||||||
|
// as we need the name and data value to store.
|
||||||
|
state, _ := b.block.RuntimeIDToState(v)
|
||||||
|
_ = nbt.NewEncoderWithEncoding(buf, nbt.LittleEndian).Encode(blockupgrader.BlockState{Name: state.Name, Properties: state.Properties, Version: b.version})
|
||||||
|
}
|
||||||
|
func (b BlockPaletteEncoding) Decode(buf *bytes.Buffer) (uint32, error) {
|
||||||
|
var e blockupgrader.BlockState
|
||||||
|
if err := nbt.NewDecoderWithEncoding(buf, nbt.LittleEndian).Decode(&e); err != nil {
|
||||||
|
return 0, fmt.Errorf("error decoding block palette entry: %w", err)
|
||||||
|
}
|
||||||
|
v, ok := b.block.StateToRuntimeID(e)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("cannot get runtime ID of block state %v{%+v}", e.Name, e.Properties)
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetworkPersistentEncoding returns a new NetworkPersistentEncoding using the block and version passed.
|
||||||
|
func NewNetworkPersistentEncoding(block mapping.Block, version int32) NetworkPersistentEncoding {
|
||||||
|
return NetworkPersistentEncoding{block: block, version: version}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkPersistentEncoding implements the Chunk encoding for sending over network with a persistent palette.
|
||||||
|
type NetworkPersistentEncoding struct {
|
||||||
|
block mapping.Block
|
||||||
|
version int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n NetworkPersistentEncoding) Network() byte { return 1 }
|
||||||
|
func (n NetworkPersistentEncoding) EncodePalette(buf *bytes.Buffer, p *Palette, _ PaletteEncoding) {
|
||||||
|
if p.size != 0 {
|
||||||
|
_ = protocol.WriteVarint32(buf, int32(p.Len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := nbt.NewEncoderWithEncoding(buf, nbt.NetworkLittleEndian)
|
||||||
|
for _, val := range p.values {
|
||||||
|
state, _ := n.block.RuntimeIDToState(val)
|
||||||
|
_ = enc.Encode(blockupgrader.BlockState{Name: strings.TrimPrefix("minecraft:", state.Name), Properties: state.Properties, Version: n.version})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (n NetworkPersistentEncoding) DecodePalette(buf *bytes.Buffer, blockSize paletteSize, _ PaletteEncoding) (*Palette, error) {
|
||||||
|
var paletteCount int32 = 1
|
||||||
|
if blockSize != 0 {
|
||||||
|
err := protocol.Varint32(buf, &paletteCount)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if paletteCount <= 0 {
|
||||||
|
return nil, fmt.Errorf("invalid palette entry count %v", paletteCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks := make([]blockupgrader.BlockState, paletteCount)
|
||||||
|
dec := nbt.NewDecoderWithEncoding(buf, nbt.NetworkLittleEndian)
|
||||||
|
for i := int32(0); i < paletteCount; i++ {
|
||||||
|
if err := dec.Decode(&blocks[i]); err != nil {
|
||||||
|
return nil, fmt.Errorf("error decoding block state: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
palette, temp := newPalette(blockSize, make([]uint32, paletteCount)), uint32(0)
|
||||||
|
for i, b := range blocks {
|
||||||
|
temp, ok = n.block.StateToRuntimeID(blockupgrader.BlockState{Name: "minecraft:" + b.Name, Properties: b.Properties, Version: n.version})
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot get runtime ID of block state %v{%+v}", b.Name, b.Properties)
|
||||||
|
}
|
||||||
|
palette.values[i] = temp
|
||||||
|
}
|
||||||
|
return palette, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type subChunkVersion8 struct{}
|
||||||
|
|
||||||
|
func (subChunkVersion8) EncodeHeader(buf *bytes.Buffer, s *SubChunk, _ cube.Range, _ int) {
|
||||||
|
_, _ = buf.Write([]byte{8, byte(len(s.storages))})
|
||||||
|
}
|
||||||
|
|
||||||
|
type subChunkVersion9 struct{}
|
||||||
|
|
||||||
|
func (subChunkVersion9) EncodeHeader(buf *bytes.Buffer, s *SubChunk, r cube.Range, ind int) {
|
||||||
|
_, _ = buf.Write([]byte{SubChunkVersion, byte(len(s.storages)), uint8(ind + (r[0] >> 4))})
|
||||||
|
}
|
||||||
131
internal/chunk/palette.go
Normal file
131
internal/chunk/palette.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package chunk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// paletteSize is the size of a palette. It indicates the amount of bits occupied per value stored.
|
||||||
|
type paletteSize byte
|
||||||
|
|
||||||
|
// Palette is a palette of values that every PalettedStorage has. Storages hold 'pointers' to indices
|
||||||
|
// in this palette.
|
||||||
|
type Palette struct {
|
||||||
|
last uint32
|
||||||
|
lastIndex int16
|
||||||
|
size paletteSize
|
||||||
|
|
||||||
|
// values is a map of values. A PalettedStorage points to the index to this value.
|
||||||
|
values []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPalette returns a new Palette with size and a slice of added values.
|
||||||
|
func newPalette(size paletteSize, values []uint32) *Palette {
|
||||||
|
return &Palette{size: size, values: values, last: math.MaxUint32}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the amount of unique values in the Palette.
|
||||||
|
func (palette *Palette) Len() int {
|
||||||
|
return len(palette.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a values to the Palette. It does not first check if the value was already set in the Palette.
|
||||||
|
// The index at which the value was added is returned. Another bool is returned indicating if the Palette
|
||||||
|
// was resized as a result of adding the value.
|
||||||
|
func (palette *Palette) Add(v uint32) (index int16, resize bool) {
|
||||||
|
i := int16(len(palette.values))
|
||||||
|
palette.values = append(palette.values, v)
|
||||||
|
|
||||||
|
if palette.needsResize() {
|
||||||
|
palette.increaseSize()
|
||||||
|
return i, true
|
||||||
|
}
|
||||||
|
return i, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace calls the function passed for each value present in the Palette. The value returned by the
|
||||||
|
// function replaces the value present at the index of the value passed.
|
||||||
|
func (palette *Palette) Replace(f func(v uint32) uint32) {
|
||||||
|
// Reset last runtime ID as it now has a different offset.
|
||||||
|
palette.last = math.MaxUint32
|
||||||
|
for index, v := range palette.values {
|
||||||
|
palette.values[index] = f(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index loops through the values of the Palette and looks for the index of the given value. If the value could
|
||||||
|
// not be found, -1 is returned.
|
||||||
|
func (palette *Palette) Index(runtimeID uint32) int16 {
|
||||||
|
if runtimeID == palette.last {
|
||||||
|
// Fast path out.
|
||||||
|
return palette.lastIndex
|
||||||
|
}
|
||||||
|
// Slow path in a separate function allows for inlining the fast path.
|
||||||
|
return palette.indexSlow(runtimeID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// indexSlow searches the index of a value in the Palette's values by iterating through the Palette's values.
|
||||||
|
func (palette *Palette) indexSlow(runtimeID uint32) int16 {
|
||||||
|
l := len(palette.values)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
if palette.values[i] == runtimeID {
|
||||||
|
palette.last = runtimeID
|
||||||
|
v := int16(i)
|
||||||
|
palette.lastIndex = v
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value in the Palette at a specific index.
|
||||||
|
func (palette *Palette) Value(i uint16) uint32 {
|
||||||
|
return palette.values[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// needsResize checks if the Palette, and with it the holding PalettedStorage, needs to be resized to a bigger
|
||||||
|
// size.
|
||||||
|
func (palette *Palette) needsResize() bool {
|
||||||
|
return len(palette.values) > (1 << palette.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sizes = [...]paletteSize{0, 1, 2, 3, 4, 5, 6, 8, 16}
|
||||||
|
var offsets = [...]int{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 8: 7, 16: 8}
|
||||||
|
|
||||||
|
// increaseSize increases the size of the Palette to the next palette size.
|
||||||
|
func (palette *Palette) increaseSize() {
|
||||||
|
palette.size = sizes[offsets[palette.size]+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// padded returns true if the Palette size is 3, 5 or 6.
|
||||||
|
func (p paletteSize) padded() bool {
|
||||||
|
return p == 3 || p == 5 || p == 6
|
||||||
|
}
|
||||||
|
|
||||||
|
// paletteSizeFor finds a suitable paletteSize for the amount of values passed n.
|
||||||
|
func paletteSizeFor(n int) paletteSize {
|
||||||
|
for _, size := range sizes {
|
||||||
|
if n <= (1 << size) {
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Should never happen.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// uint32s returns the amount of uint32s needed to represent a storage with this palette size.
|
||||||
|
func (p paletteSize) uint32s() (n int) {
|
||||||
|
uint32Count := 0
|
||||||
|
if p != 0 {
|
||||||
|
// indicesPerUint32 is the amount of indices that may be stored in a single uint32.
|
||||||
|
indicesPerUint32 := 32 / int(p)
|
||||||
|
// uint32Count is the amount of uint32s required to store all indices: 4096 indices need to be stored in
|
||||||
|
// total.
|
||||||
|
uint32Count = 4096 / indicesPerUint32
|
||||||
|
}
|
||||||
|
if p.padded() {
|
||||||
|
// We've got one of the padded sizes, so the storage has another uint32 to be able to store
|
||||||
|
// every index.
|
||||||
|
uint32Count++
|
||||||
|
}
|
||||||
|
return uint32Count
|
||||||
|
}
|
||||||
200
internal/chunk/paletted_storage.go
Normal file
200
internal/chunk/paletted_storage.go
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
package chunk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// uint32ByteSize is the amount of bytes in a uint32.
|
||||||
|
uint32ByteSize = 4
|
||||||
|
// uint32BitSize is the amount of bits in a uint32.
|
||||||
|
uint32BitSize = uint32ByteSize * 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// PalettedStorage is a storage of 4096 blocks encoded in a variable amount of uint32s, storages may have values
|
||||||
|
// with a bit size per block of 0, 1, 2, 3, 4, 5, 6, 8 or 16 bits.
|
||||||
|
// 3 of these formats have additional padding in every uint32 and an additional uint32 at the end, to cater
|
||||||
|
// for the blocks that don't fit. This padding is present when the storage has a block size of 3, 5 or 6
|
||||||
|
// bytes.
|
||||||
|
// Methods on PalettedStorage must not be called simultaneously from multiple goroutines.
|
||||||
|
type PalettedStorage struct {
|
||||||
|
// bitsPerIndex is the amount of bits required to store one block. The number increases as the block
|
||||||
|
// storage holds more unique block states.
|
||||||
|
bitsPerIndex uint16
|
||||||
|
// filledBitsPerIndex returns the amount of blocks that are actually filled per uint32.
|
||||||
|
filledBitsPerIndex uint16
|
||||||
|
// indexMask is the equivalent of 1 << bitsPerIndex - 1.
|
||||||
|
indexMask uint32
|
||||||
|
|
||||||
|
// indicesStart holds an unsafe.Pointer to the first byte in the indices slice below.
|
||||||
|
indicesStart unsafe.Pointer
|
||||||
|
|
||||||
|
// Palette holds all block runtime IDs that the indices in the indices slice point to. These runtime IDs
|
||||||
|
// point to block states.
|
||||||
|
palette *Palette
|
||||||
|
|
||||||
|
// indices contains all indices in the PalettedStorage. This slice has a variable size, but may not be changed
|
||||||
|
// unless the whole PalettedStorage is resized, including the Palette.
|
||||||
|
indices []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// newPalettedStorage creates a new block storage using the uint32 slice as the indices and the palette passed.
|
||||||
|
// The bits per block are calculated using the length of the uint32 slice.
|
||||||
|
func newPalettedStorage(indices []uint32, palette *Palette) *PalettedStorage {
|
||||||
|
var (
|
||||||
|
bitsPerIndex = uint16(len(indices) / uint32BitSize / uint32ByteSize)
|
||||||
|
indexMask = (uint32(1) << bitsPerIndex) - 1
|
||||||
|
indicesStart = (unsafe.Pointer)((*reflect.SliceHeader)(unsafe.Pointer(&indices)).Data)
|
||||||
|
filledBitsPerIndex uint16
|
||||||
|
)
|
||||||
|
if bitsPerIndex != 0 {
|
||||||
|
filledBitsPerIndex = uint32BitSize / bitsPerIndex * bitsPerIndex
|
||||||
|
}
|
||||||
|
return &PalettedStorage{filledBitsPerIndex: filledBitsPerIndex, indexMask: indexMask, indicesStart: indicesStart, bitsPerIndex: bitsPerIndex, indices: indices, palette: palette}
|
||||||
|
}
|
||||||
|
|
||||||
|
// emptyStorage creates a PalettedStorage filled completely with a value v.
|
||||||
|
func emptyStorage(v uint32) *PalettedStorage {
|
||||||
|
return newPalettedStorage([]uint32{}, newPalette(0, []uint32{v}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Palette returns the Palette of the PalettedStorage.
|
||||||
|
func (storage *PalettedStorage) Palette() *Palette {
|
||||||
|
return storage.palette
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the value of the PalettedStorage at a given x, y and z.
|
||||||
|
func (storage *PalettedStorage) At(x, y, z byte) uint32 {
|
||||||
|
return storage.palette.Value(storage.paletteIndex(x&15, y&15, z&15))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets a value at a specific x, y and z. The Palette and PalettedStorage are expanded
|
||||||
|
// automatically to make space for the value, should that be needed.
|
||||||
|
func (storage *PalettedStorage) Set(x, y, z byte, v uint32) {
|
||||||
|
index := storage.palette.Index(v)
|
||||||
|
if index == -1 {
|
||||||
|
// The runtime ID was not yet available in the palette. We add it, then check if the block storage
|
||||||
|
// needs to be resized for the palette pointers to fit.
|
||||||
|
index = storage.addNew(v)
|
||||||
|
}
|
||||||
|
storage.setPaletteIndex(x&15, y&15, z&15, uint16(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal checks if two PalettedStorages are equal value wise. False is returned
|
||||||
|
// if either of the storages are nil.
|
||||||
|
func (storage *PalettedStorage) Equal(other *PalettedStorage) bool {
|
||||||
|
if storage == nil || other == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(storage.indices) == 0 || len(other.indices) == 0 || storage.palette.values[0] == 0 || other.palette.values[0] == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
indicesA := unsafe.Slice((*byte)(unsafe.Pointer(&storage.indices[0])), len(storage.indices)*4)
|
||||||
|
indicesB := unsafe.Slice((*byte)(unsafe.Pointer(&other.indices[0])), len(other.indices)*4)
|
||||||
|
if !bytes.Equal(indicesA, indicesB) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
paletteA := unsafe.Slice((*byte)(unsafe.Pointer(&storage.palette.values[0])), len(storage.palette.values)*4)
|
||||||
|
paletteB := unsafe.Slice((*byte)(unsafe.Pointer(&other.palette.values[0])), len(other.palette.values)*4)
|
||||||
|
return bytes.Equal(paletteA, paletteB)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addNew adds a new value to the PalettedStorage's Palette and returns its index. If needed, the storage is resized.
|
||||||
|
func (storage *PalettedStorage) addNew(v uint32) int16 {
|
||||||
|
index, resize := storage.palette.Add(v)
|
||||||
|
if resize {
|
||||||
|
storage.resize(storage.palette.size)
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
// paletteIndex looks up the Palette index at a given x, y and z value in the PalettedStorage. This palette
|
||||||
|
// index is not the value at this offset, but merely an index in the Palette pointing to a value.
|
||||||
|
func (storage *PalettedStorage) paletteIndex(x, y, z byte) uint16 {
|
||||||
|
if storage.bitsPerIndex == 0 {
|
||||||
|
// Unfortunately our default logic cannot deal with 0 bits per index, meaning we'll have to special case
|
||||||
|
// this. This comes with a little performance hit, but it seems to be the only way to go. An alternative would
|
||||||
|
// be not to have 0 bits per block storages in memory, but that would cause a strongly increased memory usage
|
||||||
|
// by biomes.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
offset := ((uint16(x) << 8) | (uint16(z) << 4) | uint16(y)) * storage.bitsPerIndex
|
||||||
|
uint32Offset, bitOffset := offset/storage.filledBitsPerIndex, offset%storage.filledBitsPerIndex
|
||||||
|
|
||||||
|
w := *(*uint32)(unsafe.Pointer(uintptr(storage.indicesStart) + uintptr(uint32Offset<<2)))
|
||||||
|
return uint16((w >> bitOffset) & storage.indexMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPaletteIndex sets the palette index at a given x, y and z to paletteIndex. This index should point
|
||||||
|
// to a value in the PalettedStorage's Palette.
|
||||||
|
func (storage *PalettedStorage) setPaletteIndex(x, y, z byte, i uint16) {
|
||||||
|
if storage.bitsPerIndex == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
offset := ((uint16(x) << 8) | (uint16(z) << 4) | uint16(y)) * storage.bitsPerIndex
|
||||||
|
uint32Offset, bitOffset := offset/storage.filledBitsPerIndex, offset%storage.filledBitsPerIndex
|
||||||
|
|
||||||
|
ptr := (*uint32)(unsafe.Pointer(uintptr(storage.indicesStart) + uintptr(uint32Offset<<2)))
|
||||||
|
*ptr = (*ptr &^ (storage.indexMask << bitOffset)) | (uint32(i) << bitOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resize changes the size of a PalettedStorage to newPaletteSize. A new PalettedStorage is constructed,
|
||||||
|
// and all values available in the current storage are set in their appropriate locations in the
|
||||||
|
// new storage.
|
||||||
|
func (storage *PalettedStorage) resize(newPaletteSize paletteSize) {
|
||||||
|
if newPaletteSize == paletteSize(storage.bitsPerIndex) {
|
||||||
|
return // Don't resize if the size is already equal.
|
||||||
|
}
|
||||||
|
// Construct a new storage and set all values in there manually. We can't easily do this in a better
|
||||||
|
// way, because all values will be at a different index with a different length.
|
||||||
|
newStorage := newPalettedStorage(make([]uint32, newPaletteSize.uint32s()), storage.palette)
|
||||||
|
for x := byte(0); x < 16; x++ {
|
||||||
|
for y := byte(0); y < 16; y++ {
|
||||||
|
for z := byte(0); z < 16; z++ {
|
||||||
|
newStorage.setPaletteIndex(x, y, z, storage.paletteIndex(x, y, z))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set the new storage.
|
||||||
|
*storage = *newStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
// compact clears unused indexes in the palette by scanning for usages in the PalettedStorage. This is a
|
||||||
|
// relatively heavy task which should only happen right before the sub chunk holding this PalettedStorage is
|
||||||
|
// saved to disk. compact also shrinks the palette size if possible.
|
||||||
|
func (storage *PalettedStorage) compact() {
|
||||||
|
usedIndices := make([]bool, storage.palette.Len())
|
||||||
|
for x := byte(0); x < 16; x++ {
|
||||||
|
for y := byte(0); y < 16; y++ {
|
||||||
|
for z := byte(0); z < 16; z++ {
|
||||||
|
usedIndices[storage.paletteIndex(x, y, z)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRuntimeIDs := make([]uint32, 0, len(usedIndices))
|
||||||
|
conversion := make([]uint16, len(usedIndices))
|
||||||
|
|
||||||
|
for index, set := range usedIndices {
|
||||||
|
if set {
|
||||||
|
conversion[index] = uint16(len(newRuntimeIDs))
|
||||||
|
newRuntimeIDs = append(newRuntimeIDs, storage.palette.values[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Construct a new storage and set all values in there manually. We can't easily do this in a better
|
||||||
|
// way, because all values will be at a different index with a different length.
|
||||||
|
size := paletteSizeFor(len(newRuntimeIDs))
|
||||||
|
newStorage := newPalettedStorage(make([]uint32, size.uint32s()), newPalette(size, newRuntimeIDs))
|
||||||
|
|
||||||
|
for x := byte(0); x < 16; x++ {
|
||||||
|
for y := byte(0); y < 16; y++ {
|
||||||
|
for z := byte(0); z < 16; z++ {
|
||||||
|
// Replace all usages of the old palette indexes with the new indexes using the map we
|
||||||
|
// produced earlier.
|
||||||
|
newStorage.setPaletteIndex(x, y, z, conversion[storage.paletteIndex(x, y, z)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*storage = *newStorage
|
||||||
|
}
|
||||||
66
internal/chunk/sub_chunk.go
Normal file
66
internal/chunk/sub_chunk.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package chunk
|
||||||
|
|
||||||
|
// SubChunk is a cube of blocks located in a chunk. It has a size of 16x16x16 blocks and forms part of a stack
|
||||||
|
// that forms a Chunk.
|
||||||
|
type SubChunk struct {
|
||||||
|
air uint32
|
||||||
|
storages []*PalettedStorage
|
||||||
|
blockLight []uint8
|
||||||
|
skyLight []uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSubChunk creates a new sub chunk. All sub chunks should be created through this function
|
||||||
|
func NewSubChunk(air uint32) *SubChunk {
|
||||||
|
return &SubChunk{air: air}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty checks if the SubChunk is considered empty. This is the case if the SubChunk has 0 block storages or if it has
|
||||||
|
// a single one that is completely filled with air.
|
||||||
|
func (sub *SubChunk) Empty() bool {
|
||||||
|
return len(sub.storages) == 0 || (len(sub.storages) == 1 && len(sub.storages[0].palette.values) == 1 && sub.storages[0].palette.values[0] == sub.air)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer returns a certain block storage/layer from a sub chunk. If no storage at the layer exists, the layer
|
||||||
|
// is created, as well as all layers between the current highest layer and the new highest layer.
|
||||||
|
func (sub *SubChunk) Layer(layer uint8) *PalettedStorage {
|
||||||
|
for uint8(len(sub.storages)) <= layer {
|
||||||
|
// Keep appending to storages until the requested layer is achieved. Makes working with new layers
|
||||||
|
// much easier.
|
||||||
|
sub.storages = append(sub.storages, emptyStorage(sub.air))
|
||||||
|
}
|
||||||
|
return sub.storages[layer]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layers returns all layers in the sub chunk. This method may also return an empty slice.
|
||||||
|
func (sub *SubChunk) Layers() []*PalettedStorage {
|
||||||
|
return sub.storages
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block returns the runtime ID of the block located at the given X, Y and Z. X, Y and Z must be in a
|
||||||
|
// range of 0-15.
|
||||||
|
func (sub *SubChunk) Block(x, y, z byte, layer uint8) uint32 {
|
||||||
|
if uint8(len(sub.storages)) <= layer {
|
||||||
|
return sub.air
|
||||||
|
}
|
||||||
|
return sub.storages[layer].At(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBlock sets the given block runtime ID at the given X, Y and Z. X, Y and Z must be in a range of 0-15.
|
||||||
|
func (sub *SubChunk) SetBlock(x, y, z byte, layer uint8, block uint32) {
|
||||||
|
sub.Layer(layer).Set(x, y, z, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact cleans the garbage from all block storages that sub chunk contains, so that they may be
|
||||||
|
// cleanly written to a database.
|
||||||
|
func (sub *SubChunk) compact() {
|
||||||
|
newStorages := make([]*PalettedStorage, 0, len(sub.storages))
|
||||||
|
for _, storage := range sub.storages {
|
||||||
|
storage.compact()
|
||||||
|
if len(storage.palette.values) == 1 && storage.palette.values[0] == sub.air {
|
||||||
|
// If the palette has only air in it, it means the storage is empty, so we can ignore it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newStorages = append(newStorages, storage)
|
||||||
|
}
|
||||||
|
sub.storages = newStorages
|
||||||
|
}
|
||||||
481
internal/item/1.12.0_item_id_to_block_id_map.json
Normal file
481
internal/item/1.12.0_item_id_to_block_id_map.json
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
{
|
||||||
|
"minecraft:acacia_button": "minecraft:acacia_button",
|
||||||
|
"minecraft:acacia_fence_gate": "minecraft:acacia_fence_gate",
|
||||||
|
"minecraft:acacia_pressure_plate": "minecraft:acacia_pressure_plate",
|
||||||
|
"minecraft:acacia_stairs": "minecraft:acacia_stairs",
|
||||||
|
"minecraft:acacia_standing_sign": "minecraft:acacia_standing_sign",
|
||||||
|
"minecraft:acacia_trapdoor": "minecraft:acacia_trapdoor",
|
||||||
|
"minecraft:acacia_wall_sign": "minecraft:acacia_wall_sign",
|
||||||
|
"minecraft:activator_rail": "minecraft:activator_rail",
|
||||||
|
"minecraft:andesite_stairs": "minecraft:andesite_stairs",
|
||||||
|
"minecraft:anvil": "minecraft:anvil",
|
||||||
|
"minecraft:bamboo": "minecraft:bamboo",
|
||||||
|
"minecraft:bamboo_sapling": "minecraft:bamboo_sapling",
|
||||||
|
"minecraft:barrel": "minecraft:barrel",
|
||||||
|
"minecraft:barrier": "minecraft:barrier",
|
||||||
|
"minecraft:beacon": "minecraft:beacon",
|
||||||
|
"minecraft:bedrock": "minecraft:bedrock",
|
||||||
|
"minecraft:bell": "minecraft:bell",
|
||||||
|
"minecraft:birch_button": "minecraft:birch_button",
|
||||||
|
"minecraft:birch_fence_gate": "minecraft:birch_fence_gate",
|
||||||
|
"minecraft:birch_pressure_plate": "minecraft:birch_pressure_plate",
|
||||||
|
"minecraft:birch_stairs": "minecraft:birch_stairs",
|
||||||
|
"minecraft:birch_standing_sign": "minecraft:birch_standing_sign",
|
||||||
|
"minecraft:birch_trapdoor": "minecraft:birch_trapdoor",
|
||||||
|
"minecraft:birch_wall_sign": "minecraft:birch_wall_sign",
|
||||||
|
"minecraft:black_glazed_terracotta": "minecraft:black_glazed_terracotta",
|
||||||
|
"minecraft:blast_furnace": "minecraft:blast_furnace",
|
||||||
|
"minecraft:blue_glazed_terracotta": "minecraft:blue_glazed_terracotta",
|
||||||
|
"minecraft:blue_ice": "minecraft:blue_ice",
|
||||||
|
"minecraft:bone_block": "minecraft:bone_block",
|
||||||
|
"minecraft:bookshelf": "minecraft:bookshelf",
|
||||||
|
"minecraft:brewingstandblock": "minecraft:brewing_stand",
|
||||||
|
"minecraft:brick_block": "minecraft:brick_block",
|
||||||
|
"minecraft:brick_stairs": "minecraft:brick_stairs",
|
||||||
|
"minecraft:brown_glazed_terracotta": "minecraft:brown_glazed_terracotta",
|
||||||
|
"minecraft:brown_mushroom": "minecraft:brown_mushroom",
|
||||||
|
"minecraft:brown_mushroom_block": "minecraft:brown_mushroom_block",
|
||||||
|
"minecraft:bubble_column": "minecraft:bubble_column",
|
||||||
|
"minecraft:cactus": "minecraft:cactus",
|
||||||
|
"minecraft:carpet": "minecraft:carpet",
|
||||||
|
"minecraft:carrots": "minecraft:carrots",
|
||||||
|
"minecraft:cartography_table": "minecraft:cartography_table",
|
||||||
|
"minecraft:carved_pumpkin": "minecraft:carved_pumpkin",
|
||||||
|
"minecraft:chain_command_block": "minecraft:chain_command_block",
|
||||||
|
"minecraft:chemical_heat": "minecraft:chemical_heat",
|
||||||
|
"minecraft:chemistry_table": "minecraft:chemistry_table",
|
||||||
|
"minecraft:chest": "minecraft:chest",
|
||||||
|
"minecraft:chorus_flower": "minecraft:chorus_flower",
|
||||||
|
"minecraft:chorus_plant": "minecraft:chorus_plant",
|
||||||
|
"minecraft:clay": "minecraft:clay",
|
||||||
|
"minecraft:coal_block": "minecraft:coal_block",
|
||||||
|
"minecraft:coal_ore": "minecraft:coal_ore",
|
||||||
|
"minecraft:cobblestone": "minecraft:cobblestone",
|
||||||
|
"minecraft:cobblestone_wall": "minecraft:cobblestone_wall",
|
||||||
|
"minecraft:cocoa": "minecraft:cocoa",
|
||||||
|
"minecraft:colored_torch_bp": "minecraft:colored_torch_bp",
|
||||||
|
"minecraft:colored_torch_rg": "minecraft:colored_torch_rg",
|
||||||
|
"minecraft:command_block": "minecraft:command_block",
|
||||||
|
"minecraft:composter": "minecraft:composter",
|
||||||
|
"minecraft:concrete": "minecraft:concrete",
|
||||||
|
"minecraft:concrete_powder": "minecraft:concretePowder",
|
||||||
|
"minecraft:conduit": "minecraft:conduit",
|
||||||
|
"minecraft:coral": "minecraft:coral",
|
||||||
|
"minecraft:coral_block": "minecraft:coral_block",
|
||||||
|
"minecraft:coral_fan": "minecraft:coral_fan",
|
||||||
|
"minecraft:coral_fan_dead": "minecraft:coral_fan_dead",
|
||||||
|
"minecraft:coral_fan_hang": "minecraft:coral_fan_hang",
|
||||||
|
"minecraft:coral_fan_hang2": "minecraft:coral_fan_hang2",
|
||||||
|
"minecraft:coral_fan_hang3": "minecraft:coral_fan_hang3",
|
||||||
|
"minecraft:crafting_table": "minecraft:crafting_table",
|
||||||
|
"minecraft:cyan_glazed_terracotta": "minecraft:cyan_glazed_terracotta",
|
||||||
|
"minecraft:dark_oak_button": "minecraft:dark_oak_button",
|
||||||
|
"minecraft:dark_oak_fence_gate": "minecraft:dark_oak_fence_gate",
|
||||||
|
"minecraft:dark_oak_pressure_plate": "minecraft:dark_oak_pressure_plate",
|
||||||
|
"minecraft:dark_oak_stairs": "minecraft:dark_oak_stairs",
|
||||||
|
"minecraft:dark_oak_trapdoor": "minecraft:dark_oak_trapdoor",
|
||||||
|
"minecraft:dark_prismarine_stairs": "minecraft:dark_prismarine_stairs",
|
||||||
|
"minecraft:darkoak_standing_sign": "minecraft:darkoak_standing_sign",
|
||||||
|
"minecraft:darkoak_wall_sign": "minecraft:darkoak_wall_sign",
|
||||||
|
"minecraft:daylight_detector": "minecraft:daylight_detector",
|
||||||
|
"minecraft:daylight_detector_inverted": "minecraft:daylight_detector_inverted",
|
||||||
|
"minecraft:deadbush": "minecraft:deadbush",
|
||||||
|
"minecraft:detector_rail": "minecraft:detector_rail",
|
||||||
|
"minecraft:diamond_block": "minecraft:diamond_block",
|
||||||
|
"minecraft:diamond_ore": "minecraft:diamond_ore",
|
||||||
|
"minecraft:diorite_stairs": "minecraft:diorite_stairs",
|
||||||
|
"minecraft:dirt": "minecraft:dirt",
|
||||||
|
"minecraft:dispenser": "minecraft:dispenser",
|
||||||
|
"minecraft:double_plant": "minecraft:double_plant",
|
||||||
|
"minecraft:double_stone_slab": "minecraft:stone_slab",
|
||||||
|
"minecraft:double_stone_slab2": "minecraft:stone_slab2",
|
||||||
|
"minecraft:double_stone_slab3": "minecraft:stone_slab3",
|
||||||
|
"minecraft:double_stone_slab4": "minecraft:stone_slab4",
|
||||||
|
"minecraft:double_wooden_slab": "minecraft:double_wooden_slab",
|
||||||
|
"minecraft:dragon_egg": "minecraft:dragon_egg",
|
||||||
|
"minecraft:dried_kelp_block": "minecraft:dried_kelp_block",
|
||||||
|
"minecraft:dropper": "minecraft:dropper",
|
||||||
|
"minecraft:element_0": "minecraft:element_0",
|
||||||
|
"minecraft:element_1": "minecraft:element_1",
|
||||||
|
"minecraft:element_10": "minecraft:element_10",
|
||||||
|
"minecraft:element_100": "minecraft:element_100",
|
||||||
|
"minecraft:element_101": "minecraft:element_101",
|
||||||
|
"minecraft:element_102": "minecraft:element_102",
|
||||||
|
"minecraft:element_103": "minecraft:element_103",
|
||||||
|
"minecraft:element_104": "minecraft:element_104",
|
||||||
|
"minecraft:element_105": "minecraft:element_105",
|
||||||
|
"minecraft:element_106": "minecraft:element_106",
|
||||||
|
"minecraft:element_107": "minecraft:element_107",
|
||||||
|
"minecraft:element_108": "minecraft:element_108",
|
||||||
|
"minecraft:element_109": "minecraft:element_109",
|
||||||
|
"minecraft:element_11": "minecraft:element_11",
|
||||||
|
"minecraft:element_110": "minecraft:element_110",
|
||||||
|
"minecraft:element_111": "minecraft:element_111",
|
||||||
|
"minecraft:element_112": "minecraft:element_112",
|
||||||
|
"minecraft:element_113": "minecraft:element_113",
|
||||||
|
"minecraft:element_114": "minecraft:element_114",
|
||||||
|
"minecraft:element_115": "minecraft:element_115",
|
||||||
|
"minecraft:element_116": "minecraft:element_116",
|
||||||
|
"minecraft:element_117": "minecraft:element_117",
|
||||||
|
"minecraft:element_118": "minecraft:element_118",
|
||||||
|
"minecraft:element_12": "minecraft:element_12",
|
||||||
|
"minecraft:element_13": "minecraft:element_13",
|
||||||
|
"minecraft:element_14": "minecraft:element_14",
|
||||||
|
"minecraft:element_15": "minecraft:element_15",
|
||||||
|
"minecraft:element_16": "minecraft:element_16",
|
||||||
|
"minecraft:element_17": "minecraft:element_17",
|
||||||
|
"minecraft:element_18": "minecraft:element_18",
|
||||||
|
"minecraft:element_19": "minecraft:element_19",
|
||||||
|
"minecraft:element_2": "minecraft:element_2",
|
||||||
|
"minecraft:element_20": "minecraft:element_20",
|
||||||
|
"minecraft:element_21": "minecraft:element_21",
|
||||||
|
"minecraft:element_22": "minecraft:element_22",
|
||||||
|
"minecraft:element_23": "minecraft:element_23",
|
||||||
|
"minecraft:element_24": "minecraft:element_24",
|
||||||
|
"minecraft:element_25": "minecraft:element_25",
|
||||||
|
"minecraft:element_26": "minecraft:element_26",
|
||||||
|
"minecraft:element_27": "minecraft:element_27",
|
||||||
|
"minecraft:element_28": "minecraft:element_28",
|
||||||
|
"minecraft:element_29": "minecraft:element_29",
|
||||||
|
"minecraft:element_3": "minecraft:element_3",
|
||||||
|
"minecraft:element_30": "minecraft:element_30",
|
||||||
|
"minecraft:element_31": "minecraft:element_31",
|
||||||
|
"minecraft:element_32": "minecraft:element_32",
|
||||||
|
"minecraft:element_33": "minecraft:element_33",
|
||||||
|
"minecraft:element_34": "minecraft:element_34",
|
||||||
|
"minecraft:element_35": "minecraft:element_35",
|
||||||
|
"minecraft:element_36": "minecraft:element_36",
|
||||||
|
"minecraft:element_37": "minecraft:element_37",
|
||||||
|
"minecraft:element_38": "minecraft:element_38",
|
||||||
|
"minecraft:element_39": "minecraft:element_39",
|
||||||
|
"minecraft:element_4": "minecraft:element_4",
|
||||||
|
"minecraft:element_40": "minecraft:element_40",
|
||||||
|
"minecraft:element_41": "minecraft:element_41",
|
||||||
|
"minecraft:element_42": "minecraft:element_42",
|
||||||
|
"minecraft:element_43": "minecraft:element_43",
|
||||||
|
"minecraft:element_44": "minecraft:element_44",
|
||||||
|
"minecraft:element_45": "minecraft:element_45",
|
||||||
|
"minecraft:element_46": "minecraft:element_46",
|
||||||
|
"minecraft:element_47": "minecraft:element_47",
|
||||||
|
"minecraft:element_48": "minecraft:element_48",
|
||||||
|
"minecraft:element_49": "minecraft:element_49",
|
||||||
|
"minecraft:element_5": "minecraft:element_5",
|
||||||
|
"minecraft:element_50": "minecraft:element_50",
|
||||||
|
"minecraft:element_51": "minecraft:element_51",
|
||||||
|
"minecraft:element_52": "minecraft:element_52",
|
||||||
|
"minecraft:element_53": "minecraft:element_53",
|
||||||
|
"minecraft:element_54": "minecraft:element_54",
|
||||||
|
"minecraft:element_55": "minecraft:element_55",
|
||||||
|
"minecraft:element_56": "minecraft:element_56",
|
||||||
|
"minecraft:element_57": "minecraft:element_57",
|
||||||
|
"minecraft:element_58": "minecraft:element_58",
|
||||||
|
"minecraft:element_59": "minecraft:element_59",
|
||||||
|
"minecraft:element_6": "minecraft:element_6",
|
||||||
|
"minecraft:element_60": "minecraft:element_60",
|
||||||
|
"minecraft:element_61": "minecraft:element_61",
|
||||||
|
"minecraft:element_62": "minecraft:element_62",
|
||||||
|
"minecraft:element_63": "minecraft:element_63",
|
||||||
|
"minecraft:element_64": "minecraft:element_64",
|
||||||
|
"minecraft:element_65": "minecraft:element_65",
|
||||||
|
"minecraft:element_66": "minecraft:element_66",
|
||||||
|
"minecraft:element_67": "minecraft:element_67",
|
||||||
|
"minecraft:element_68": "minecraft:element_68",
|
||||||
|
"minecraft:element_69": "minecraft:element_69",
|
||||||
|
"minecraft:element_7": "minecraft:element_7",
|
||||||
|
"minecraft:element_70": "minecraft:element_70",
|
||||||
|
"minecraft:element_71": "minecraft:element_71",
|
||||||
|
"minecraft:element_72": "minecraft:element_72",
|
||||||
|
"minecraft:element_73": "minecraft:element_73",
|
||||||
|
"minecraft:element_74": "minecraft:element_74",
|
||||||
|
"minecraft:element_75": "minecraft:element_75",
|
||||||
|
"minecraft:element_76": "minecraft:element_76",
|
||||||
|
"minecraft:element_77": "minecraft:element_77",
|
||||||
|
"minecraft:element_78": "minecraft:element_78",
|
||||||
|
"minecraft:element_79": "minecraft:element_79",
|
||||||
|
"minecraft:element_8": "minecraft:element_8",
|
||||||
|
"minecraft:element_80": "minecraft:element_80",
|
||||||
|
"minecraft:element_81": "minecraft:element_81",
|
||||||
|
"minecraft:element_82": "minecraft:element_82",
|
||||||
|
"minecraft:element_83": "minecraft:element_83",
|
||||||
|
"minecraft:element_84": "minecraft:element_84",
|
||||||
|
"minecraft:element_85": "minecraft:element_85",
|
||||||
|
"minecraft:element_86": "minecraft:element_86",
|
||||||
|
"minecraft:element_87": "minecraft:element_87",
|
||||||
|
"minecraft:element_88": "minecraft:element_88",
|
||||||
|
"minecraft:element_89": "minecraft:element_89",
|
||||||
|
"minecraft:element_9": "minecraft:element_9",
|
||||||
|
"minecraft:element_90": "minecraft:element_90",
|
||||||
|
"minecraft:element_91": "minecraft:element_91",
|
||||||
|
"minecraft:element_92": "minecraft:element_92",
|
||||||
|
"minecraft:element_93": "minecraft:element_93",
|
||||||
|
"minecraft:element_94": "minecraft:element_94",
|
||||||
|
"minecraft:element_95": "minecraft:element_95",
|
||||||
|
"minecraft:element_96": "minecraft:element_96",
|
||||||
|
"minecraft:element_97": "minecraft:element_97",
|
||||||
|
"minecraft:element_98": "minecraft:element_98",
|
||||||
|
"minecraft:element_99": "minecraft:element_99",
|
||||||
|
"minecraft:emerald_block": "minecraft:emerald_block",
|
||||||
|
"minecraft:emerald_ore": "minecraft:emerald_ore",
|
||||||
|
"minecraft:enchanting_table": "minecraft:enchanting_table",
|
||||||
|
"minecraft:end_brick_stairs": "minecraft:end_brick_stairs",
|
||||||
|
"minecraft:end_bricks": "minecraft:end_bricks",
|
||||||
|
"minecraft:end_gateway": "minecraft:end_gateway",
|
||||||
|
"minecraft:end_portal": "minecraft:end_portal",
|
||||||
|
"minecraft:end_portal_frame": "minecraft:end_portal_frame",
|
||||||
|
"minecraft:end_rod": "minecraft:end_rod",
|
||||||
|
"minecraft:end_stone": "minecraft:end_stone",
|
||||||
|
"minecraft:ender_chest": "minecraft:ender_chest",
|
||||||
|
"minecraft:farmland": "minecraft:farmland",
|
||||||
|
"minecraft:fence": "minecraft:fence",
|
||||||
|
"minecraft:fence_gate": "minecraft:fence_gate",
|
||||||
|
"minecraft:fire": "minecraft:fire",
|
||||||
|
"minecraft:fletching_table": "minecraft:fletching_table",
|
||||||
|
"minecraft:flowing_lava": "minecraft:flowing_lava",
|
||||||
|
"minecraft:flowing_water": "minecraft:flowing_water",
|
||||||
|
"minecraft:frosted_ice": "minecraft:frosted_ice",
|
||||||
|
"minecraft:furnace": "minecraft:furnace",
|
||||||
|
"minecraft:glass": "minecraft:glass",
|
||||||
|
"minecraft:glass_pane": "minecraft:glass_pane",
|
||||||
|
"minecraft:glazedterracotta.black": "minecraft:black_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.blue": "minecraft:blue_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.brown": "minecraft:brown_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.cyan": "minecraft:cyan_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.gray": "minecraft:gray_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.green": "minecraft:green_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.light_blue": "minecraft:light_blue_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.lime": "minecraft:lime_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.magenta": "minecraft:magenta_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.orange": "minecraft:orange_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.pink": "minecraft:pink_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.purple": "minecraft:purple_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.red": "minecraft:red_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.silver": "minecraft:silver_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.white": "minecraft:white_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.yellow": "minecraft:yellow_glazed_terracotta",
|
||||||
|
"minecraft:glowingobsidian": "minecraft:glowingobsidian",
|
||||||
|
"minecraft:glowstone": "minecraft:glowstone",
|
||||||
|
"minecraft:gold_block": "minecraft:gold_block",
|
||||||
|
"minecraft:gold_ore": "minecraft:gold_ore",
|
||||||
|
"minecraft:golden_rail": "minecraft:golden_rail",
|
||||||
|
"minecraft:granite_stairs": "minecraft:granite_stairs",
|
||||||
|
"minecraft:grass": "minecraft:grass",
|
||||||
|
"minecraft:grass_path": "minecraft:grass_path",
|
||||||
|
"minecraft:gravel": "minecraft:gravel",
|
||||||
|
"minecraft:gray_glazed_terracotta": "minecraft:gray_glazed_terracotta",
|
||||||
|
"minecraft:green_glazed_terracotta": "minecraft:green_glazed_terracotta",
|
||||||
|
"minecraft:grindstone": "minecraft:grindstone",
|
||||||
|
"minecraft:hard_glass": "minecraft:hard_glass",
|
||||||
|
"minecraft:hard_glass_pane": "minecraft:hard_glass_pane",
|
||||||
|
"minecraft:hard_stained_glass": "minecraft:hard_stained_glass",
|
||||||
|
"minecraft:hard_stained_glass_pane": "minecraft:hard_stained_glass_pane",
|
||||||
|
"minecraft:hardened_clay": "minecraft:hardened_clay",
|
||||||
|
"minecraft:hay_block": "minecraft:hay_block",
|
||||||
|
"minecraft:heavy_weighted_pressure_plate": "minecraft:heavy_weighted_pressure_plate",
|
||||||
|
"minecraft:ice": "minecraft:ice",
|
||||||
|
"minecraft:info_update": "minecraft:info_update",
|
||||||
|
"minecraft:info_update2": "minecraft:info_update2",
|
||||||
|
"minecraft:invisiblebedrock": "minecraft:invisibleBedrock",
|
||||||
|
"minecraft:iron_bars": "minecraft:iron_bars",
|
||||||
|
"minecraft:iron_block": "minecraft:iron_block",
|
||||||
|
"minecraft:iron_ore": "minecraft:iron_ore",
|
||||||
|
"minecraft:iron_trapdoor": "minecraft:iron_trapdoor",
|
||||||
|
"minecraft:item.acacia_door": "minecraft:acacia_door",
|
||||||
|
"minecraft:item.bed": "minecraft:bed",
|
||||||
|
"minecraft:item.beetroot": "minecraft:beetroot",
|
||||||
|
"minecraft:item.birch_door": "minecraft:birch_door",
|
||||||
|
"minecraft:item.cake": "minecraft:cake",
|
||||||
|
"minecraft:item.campfire": "minecraft:campfire",
|
||||||
|
"minecraft:item.cauldron": "minecraft:cauldron",
|
||||||
|
"minecraft:item.dark_oak_door": "minecraft:dark_oak_door",
|
||||||
|
"minecraft:item.flower_pot": "minecraft:flower_pot",
|
||||||
|
"minecraft:item.frame": "minecraft:frame",
|
||||||
|
"minecraft:item.hopper": "minecraft:hopper",
|
||||||
|
"minecraft:item.iron_door": "minecraft:iron_door",
|
||||||
|
"minecraft:item.jungle_door": "minecraft:jungle_door",
|
||||||
|
"minecraft:item.kelp": "minecraft:kelp",
|
||||||
|
"minecraft:item.nether_wart": "minecraft:nether_wart",
|
||||||
|
"minecraft:item.reeds": "minecraft:reeds",
|
||||||
|
"minecraft:item.skull": "minecraft:skull",
|
||||||
|
"minecraft:item.spruce_door": "minecraft:spruce_door",
|
||||||
|
"minecraft:item.wheat": "minecraft:wheat",
|
||||||
|
"minecraft:item.wooden_door": "minecraft:wooden_door",
|
||||||
|
"minecraft:jigsaw": "minecraft:jigsaw",
|
||||||
|
"minecraft:jukebox": "minecraft:jukebox",
|
||||||
|
"minecraft:jungle_button": "minecraft:jungle_button",
|
||||||
|
"minecraft:jungle_fence_gate": "minecraft:jungle_fence_gate",
|
||||||
|
"minecraft:jungle_pressure_plate": "minecraft:jungle_pressure_plate",
|
||||||
|
"minecraft:jungle_stairs": "minecraft:jungle_stairs",
|
||||||
|
"minecraft:jungle_standing_sign": "minecraft:jungle_standing_sign",
|
||||||
|
"minecraft:jungle_trapdoor": "minecraft:jungle_trapdoor",
|
||||||
|
"minecraft:jungle_wall_sign": "minecraft:jungle_wall_sign",
|
||||||
|
"minecraft:ladder": "minecraft:ladder",
|
||||||
|
"minecraft:lantern": "minecraft:lantern",
|
||||||
|
"minecraft:lapis_block": "minecraft:lapis_block",
|
||||||
|
"minecraft:lapis_ore": "minecraft:lapis_ore",
|
||||||
|
"minecraft:lava": "minecraft:lava",
|
||||||
|
"minecraft:lava_cauldron": "minecraft:lava_cauldron",
|
||||||
|
"minecraft:leaves": "minecraft:leaves",
|
||||||
|
"minecraft:leaves2": "minecraft:leaves2",
|
||||||
|
"minecraft:lectern": "minecraft:lectern",
|
||||||
|
"minecraft:lever": "minecraft:lever",
|
||||||
|
"minecraft:light_blue_glazed_terracotta": "minecraft:light_blue_glazed_terracotta",
|
||||||
|
"minecraft:light_weighted_pressure_plate": "minecraft:light_weighted_pressure_plate",
|
||||||
|
"minecraft:lime_glazed_terracotta": "minecraft:lime_glazed_terracotta",
|
||||||
|
"minecraft:lit_blast_furnace": "minecraft:lit_blast_furnace",
|
||||||
|
"minecraft:lit_furnace": "minecraft:lit_furnace",
|
||||||
|
"minecraft:lit_pumpkin": "minecraft:lit_pumpkin",
|
||||||
|
"minecraft:lit_redstone_lamp": "minecraft:lit_redstone_lamp",
|
||||||
|
"minecraft:lit_redstone_ore": "minecraft:lit_redstone_ore",
|
||||||
|
"minecraft:lit_smoker": "minecraft:lit_smoker",
|
||||||
|
"minecraft:log": "minecraft:log",
|
||||||
|
"minecraft:log2": "minecraft:log2",
|
||||||
|
"minecraft:loom": "minecraft:loom",
|
||||||
|
"minecraft:magenta_glazed_terracotta": "minecraft:magenta_glazed_terracotta",
|
||||||
|
"minecraft:magma": "minecraft:magma",
|
||||||
|
"minecraft:melon_block": "minecraft:melon_block",
|
||||||
|
"minecraft:melon_stem": "minecraft:melon_stem",
|
||||||
|
"minecraft:mob_spawner": "minecraft:mob_spawner",
|
||||||
|
"minecraft:monster_egg": "minecraft:monster_egg",
|
||||||
|
"minecraft:mossy_cobblestone": "minecraft:mossy_cobblestone",
|
||||||
|
"minecraft:mossy_cobblestone_stairs": "minecraft:mossy_cobblestone_stairs",
|
||||||
|
"minecraft:mossy_stone_brick_stairs": "minecraft:mossy_stone_brick_stairs",
|
||||||
|
"minecraft:movingblock": "minecraft:movingBlock",
|
||||||
|
"minecraft:mycelium": "minecraft:mycelium",
|
||||||
|
"minecraft:nether_brick": "minecraft:nether_brick",
|
||||||
|
"minecraft:nether_brick_fence": "minecraft:nether_brick_fence",
|
||||||
|
"minecraft:nether_brick_stairs": "minecraft:nether_brick_stairs",
|
||||||
|
"minecraft:nether_wart_block": "minecraft:nether_wart_block",
|
||||||
|
"minecraft:netherrack": "minecraft:netherrack",
|
||||||
|
"minecraft:netherreactor": "minecraft:netherreactor",
|
||||||
|
"minecraft:normal_stone_stairs": "minecraft:normal_stone_stairs",
|
||||||
|
"minecraft:noteblock": "minecraft:noteblock",
|
||||||
|
"minecraft:oak_stairs": "minecraft:oak_stairs",
|
||||||
|
"minecraft:observer": "minecraft:observer",
|
||||||
|
"minecraft:obsidian": "minecraft:obsidian",
|
||||||
|
"minecraft:orange_glazed_terracotta": "minecraft:orange_glazed_terracotta",
|
||||||
|
"minecraft:packed_ice": "minecraft:packed_ice",
|
||||||
|
"minecraft:pink_glazed_terracotta": "minecraft:pink_glazed_terracotta",
|
||||||
|
"minecraft:piston": "minecraft:piston",
|
||||||
|
"minecraft:pistonarmcollision": "minecraft:pistonArmCollision",
|
||||||
|
"minecraft:planks": "minecraft:planks",
|
||||||
|
"minecraft:podzol": "minecraft:podzol",
|
||||||
|
"minecraft:polished_andesite_stairs": "minecraft:polished_andesite_stairs",
|
||||||
|
"minecraft:polished_diorite_stairs": "minecraft:polished_diorite_stairs",
|
||||||
|
"minecraft:polished_granite_stairs": "minecraft:polished_granite_stairs",
|
||||||
|
"minecraft:portal": "minecraft:portal",
|
||||||
|
"minecraft:potatoes": "minecraft:potatoes",
|
||||||
|
"minecraft:powered_comparator": "minecraft:powered_comparator",
|
||||||
|
"minecraft:powered_repeater": "minecraft:powered_repeater",
|
||||||
|
"minecraft:prismarine": "minecraft:prismarine",
|
||||||
|
"minecraft:prismarine_bricks_stairs": "minecraft:prismarine_bricks_stairs",
|
||||||
|
"minecraft:prismarine_stairs": "minecraft:prismarine_stairs",
|
||||||
|
"minecraft:pumpkin": "minecraft:pumpkin",
|
||||||
|
"minecraft:pumpkin_stem": "minecraft:pumpkin_stem",
|
||||||
|
"minecraft:purple_glazed_terracotta": "minecraft:purple_glazed_terracotta",
|
||||||
|
"minecraft:purpur_block": "minecraft:purpur_block",
|
||||||
|
"minecraft:purpur_stairs": "minecraft:purpur_stairs",
|
||||||
|
"minecraft:quartz_block": "minecraft:quartz_block",
|
||||||
|
"minecraft:quartz_ore": "minecraft:quartz_ore",
|
||||||
|
"minecraft:quartz_stairs": "minecraft:quartz_stairs",
|
||||||
|
"minecraft:rail": "minecraft:rail",
|
||||||
|
"minecraft:real_double_stone_slab": "minecraft:double_stone_slab",
|
||||||
|
"minecraft:real_double_stone_slab2": "minecraft:double_stone_slab2",
|
||||||
|
"minecraft:real_double_stone_slab3": "minecraft:double_stone_slab3",
|
||||||
|
"minecraft:real_double_stone_slab4": "minecraft:double_stone_slab4",
|
||||||
|
"minecraft:red_flower": "minecraft:red_flower",
|
||||||
|
"minecraft:red_glazed_terracotta": "minecraft:red_glazed_terracotta",
|
||||||
|
"minecraft:red_mushroom": "minecraft:red_mushroom",
|
||||||
|
"minecraft:red_mushroom_block": "minecraft:red_mushroom_block",
|
||||||
|
"minecraft:red_nether_brick": "minecraft:red_nether_brick",
|
||||||
|
"minecraft:red_nether_brick_stairs": "minecraft:red_nether_brick_stairs",
|
||||||
|
"minecraft:red_sandstone": "minecraft:red_sandstone",
|
||||||
|
"minecraft:red_sandstone_stairs": "minecraft:red_sandstone_stairs",
|
||||||
|
"minecraft:redstone_block": "minecraft:redstone_block",
|
||||||
|
"minecraft:redstone_lamp": "minecraft:redstone_lamp",
|
||||||
|
"minecraft:redstone_ore": "minecraft:redstone_ore",
|
||||||
|
"minecraft:redstone_torch": "minecraft:redstone_torch",
|
||||||
|
"minecraft:redstone_wire": "minecraft:redstone_wire",
|
||||||
|
"minecraft:repeating_command_block": "minecraft:repeating_command_block",
|
||||||
|
"minecraft:reserved6": "minecraft:reserved6",
|
||||||
|
"minecraft:sand": "minecraft:sand",
|
||||||
|
"minecraft:sandstone": "minecraft:sandstone",
|
||||||
|
"minecraft:sandstone_stairs": "minecraft:sandstone_stairs",
|
||||||
|
"minecraft:sapling": "minecraft:sapling",
|
||||||
|
"minecraft:scaffolding": "minecraft:scaffolding",
|
||||||
|
"minecraft:sea_pickle": "minecraft:sea_pickle",
|
||||||
|
"minecraft:seagrass": "minecraft:seagrass",
|
||||||
|
"minecraft:sealantern": "minecraft:seaLantern",
|
||||||
|
"minecraft:shulker_box": "minecraft:shulker_box",
|
||||||
|
"minecraft:silver_glazed_terracotta": "minecraft:silver_glazed_terracotta",
|
||||||
|
"minecraft:slime": "minecraft:slime",
|
||||||
|
"minecraft:smithing_table": "minecraft:smithing_table",
|
||||||
|
"minecraft:smoker": "minecraft:smoker",
|
||||||
|
"minecraft:smooth_quartz_stairs": "minecraft:smooth_quartz_stairs",
|
||||||
|
"minecraft:smooth_red_sandstone_stairs": "minecraft:smooth_red_sandstone_stairs",
|
||||||
|
"minecraft:smooth_sandstone_stairs": "minecraft:smooth_sandstone_stairs",
|
||||||
|
"minecraft:smooth_stone": "minecraft:smooth_stone",
|
||||||
|
"minecraft:snow": "minecraft:snow",
|
||||||
|
"minecraft:snow_layer": "minecraft:snow_layer",
|
||||||
|
"minecraft:soul_sand": "minecraft:soul_sand",
|
||||||
|
"minecraft:sponge": "minecraft:sponge",
|
||||||
|
"minecraft:spruce_button": "minecraft:spruce_button",
|
||||||
|
"minecraft:spruce_fence_gate": "minecraft:spruce_fence_gate",
|
||||||
|
"minecraft:spruce_pressure_plate": "minecraft:spruce_pressure_plate",
|
||||||
|
"minecraft:spruce_stairs": "minecraft:spruce_stairs",
|
||||||
|
"minecraft:spruce_standing_sign": "minecraft:spruce_standing_sign",
|
||||||
|
"minecraft:spruce_trapdoor": "minecraft:spruce_trapdoor",
|
||||||
|
"minecraft:spruce_wall_sign": "minecraft:spruce_wall_sign",
|
||||||
|
"minecraft:stained_glass": "minecraft:stained_glass",
|
||||||
|
"minecraft:stained_glass_pane": "minecraft:stained_glass_pane",
|
||||||
|
"minecraft:stained_hardened_clay": "minecraft:stained_hardened_clay",
|
||||||
|
"minecraft:standing_banner": "minecraft:standing_banner",
|
||||||
|
"minecraft:standing_sign": "minecraft:standing_sign",
|
||||||
|
"minecraft:sticky_piston": "minecraft:sticky_piston",
|
||||||
|
"minecraft:stone": "minecraft:stone",
|
||||||
|
"minecraft:stone_brick_stairs": "minecraft:stone_brick_stairs",
|
||||||
|
"minecraft:stone_button": "minecraft:stone_button",
|
||||||
|
"minecraft:stone_pressure_plate": "minecraft:stone_pressure_plate",
|
||||||
|
"minecraft:stone_slab": "minecraft:stone_slab",
|
||||||
|
"minecraft:stone_slab2": "minecraft:stone_slab2",
|
||||||
|
"minecraft:stone_slab3": "minecraft:stone_slab3",
|
||||||
|
"minecraft:stone_slab4": "minecraft:stone_slab4",
|
||||||
|
"minecraft:stone_stairs": "minecraft:stone_stairs",
|
||||||
|
"minecraft:stonebrick": "minecraft:stonebrick",
|
||||||
|
"minecraft:stonecutter": "minecraft:stonecutter",
|
||||||
|
"minecraft:stonecutter_block": "minecraft:stonecutter_block",
|
||||||
|
"minecraft:stripped_acacia_log": "minecraft:stripped_acacia_log",
|
||||||
|
"minecraft:stripped_birch_log": "minecraft:stripped_birch_log",
|
||||||
|
"minecraft:stripped_dark_oak_log": "minecraft:stripped_dark_oak_log",
|
||||||
|
"minecraft:stripped_jungle_log": "minecraft:stripped_jungle_log",
|
||||||
|
"minecraft:stripped_oak_log": "minecraft:stripped_oak_log",
|
||||||
|
"minecraft:stripped_spruce_log": "minecraft:stripped_spruce_log",
|
||||||
|
"minecraft:structure_block": "minecraft:structure_block",
|
||||||
|
"minecraft:sweet_berry_bush": "minecraft:sweet_berry_bush",
|
||||||
|
"minecraft:tallgrass": "minecraft:tallgrass",
|
||||||
|
"minecraft:tnt": "minecraft:tnt",
|
||||||
|
"minecraft:torch": "minecraft:torch",
|
||||||
|
"minecraft:trapdoor": "minecraft:trapdoor",
|
||||||
|
"minecraft:trapped_chest": "minecraft:trapped_chest",
|
||||||
|
"minecraft:tripwire": "minecraft:tripWire",
|
||||||
|
"minecraft:tripwire_hook": "minecraft:tripwire_hook",
|
||||||
|
"minecraft:turtle_egg": "minecraft:turtle_egg",
|
||||||
|
"minecraft:underwater_torch": "minecraft:underwater_torch",
|
||||||
|
"minecraft:undyed_shulker_box": "minecraft:undyed_shulker_box",
|
||||||
|
"minecraft:unlit_redstone_torch": "minecraft:unlit_redstone_torch",
|
||||||
|
"minecraft:unpowered_comparator": "minecraft:unpowered_comparator",
|
||||||
|
"minecraft:unpowered_repeater": "minecraft:unpowered_repeater",
|
||||||
|
"minecraft:vine": "minecraft:vine",
|
||||||
|
"minecraft:wall_banner": "minecraft:wall_banner",
|
||||||
|
"minecraft:wall_sign": "minecraft:wall_sign",
|
||||||
|
"minecraft:water": "minecraft:water",
|
||||||
|
"minecraft:waterlily": "minecraft:waterlily",
|
||||||
|
"minecraft:web": "minecraft:web",
|
||||||
|
"minecraft:white_glazed_terracotta": "minecraft:white_glazed_terracotta",
|
||||||
|
"minecraft:wood": "minecraft:wood",
|
||||||
|
"minecraft:wooden_button": "minecraft:wooden_button",
|
||||||
|
"minecraft:wooden_pressure_plate": "minecraft:wooden_pressure_plate",
|
||||||
|
"minecraft:wooden_slab": "minecraft:wooden_slab",
|
||||||
|
"minecraft:wool": "minecraft:wool",
|
||||||
|
"minecraft:yellow_flower": "minecraft:yellow_flower",
|
||||||
|
"minecraft:yellow_glazed_terracotta": "minecraft:yellow_glazed_terracotta"
|
||||||
|
}
|
||||||
BIN
internal/item/1.12.0_to_1.18.10_blockstate_map.bin
Normal file
BIN
internal/item/1.12.0_to_1.18.10_blockstate_map.bin
Normal file
Binary file not shown.
110
internal/item/registry.go
Normal file
110
internal/item/registry.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package item
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/df-mc/worldupgrader/blockupgrader"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/nbt"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed schemas/*.json
|
||||||
|
schemasFS embed.FS
|
||||||
|
//go:embed 1.12.0_item_id_to_block_id_map.json
|
||||||
|
rawItemToBlockIdMap []byte
|
||||||
|
//go:embed 1.12.0_to_1.18.10_blockstate_map.bin
|
||||||
|
rawblockStateMap []byte
|
||||||
|
// schemaIDs is a list of ids from all registered schemas.
|
||||||
|
schemaIDs []int
|
||||||
|
// schemas is a map of all registered item upgrade schemas.
|
||||||
|
schemas map[uint16]schema
|
||||||
|
// itemToBlockIdMap is a list of all registered item upgrade schemas.
|
||||||
|
itemToBlockIdMap map[string]string
|
||||||
|
// blockStateMap is a list of all legacy block states mapped to an upgraded version of it.
|
||||||
|
blockStateMap map[stateHash]blockupgrader.BlockState
|
||||||
|
|
||||||
|
filenameRegex = regexp.MustCompile(`(\d{4})_[\w.]+\.json`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// stateHash is a struct that may be used as a map key for block states.
|
||||||
|
type stateHash struct {
|
||||||
|
// Name is the name of the block. It looks like 'minecraft:stone'.
|
||||||
|
Name string
|
||||||
|
// Metadata is the metadata value of the block. A lot of blocks only have 0 as data value, but some blocks
|
||||||
|
// carry specific variants or properties encoded in the metadata.
|
||||||
|
Metadata uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// init ...
|
||||||
|
func init() {
|
||||||
|
schemas = make(map[uint16]schema)
|
||||||
|
files, err := schemasFS.ReadDir("schemas")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, f := range files {
|
||||||
|
if f.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subMatches := filenameRegex.FindStringSubmatch(f.Name())
|
||||||
|
if subMatches == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
id, err := strconv.Atoi(subMatches[1])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := schemasFS.ReadFile("schemas/" + f.Name())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var s schema
|
||||||
|
if err = json.Unmarshal(buf, &s); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
schemas[uint16(id)] = s
|
||||||
|
}
|
||||||
|
schemaIDs = make([]int, 0, len(schemas))
|
||||||
|
for k := range schemas {
|
||||||
|
schemaIDs = append(schemaIDs, int(k))
|
||||||
|
}
|
||||||
|
sort.Sort(sort.Reverse(sort.IntSlice(schemaIDs)))
|
||||||
|
|
||||||
|
if err = json.Unmarshal(rawItemToBlockIdMap, &itemToBlockIdMap); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockStateMap = make(map[stateHash]blockupgrader.BlockState)
|
||||||
|
buf := protocol.NewReader(bytes.NewBuffer(rawblockStateMap), 0, false)
|
||||||
|
var length uint32
|
||||||
|
buf.Varuint32(&length)
|
||||||
|
for i := uint32(0); i < length; i++ {
|
||||||
|
var legacyStringId string
|
||||||
|
buf.String(&legacyStringId)
|
||||||
|
|
||||||
|
var pairs uint32
|
||||||
|
buf.Varuint32(&pairs)
|
||||||
|
for y := uint32(0); y < pairs; y++ {
|
||||||
|
var meta uint32
|
||||||
|
buf.Varuint32(&meta)
|
||||||
|
|
||||||
|
var blockStateRaw map[string]any
|
||||||
|
buf.NBT(&blockStateRaw, nbt.LittleEndian)
|
||||||
|
latestBlockState := blockupgrader.Upgrade(blockupgrader.BlockState{
|
||||||
|
Name: blockStateRaw["name"].(string),
|
||||||
|
Properties: blockStateRaw["states"].(map[string]any),
|
||||||
|
Version: blockStateRaw["version"].(int32),
|
||||||
|
})
|
||||||
|
blockStateMap[stateHash{
|
||||||
|
Name: legacyStringId,
|
||||||
|
Metadata: meta,
|
||||||
|
}] = latestBlockState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
internal/item/schema.go
Normal file
7
internal/item/schema.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package item
|
||||||
|
|
||||||
|
// schema represents the schema for loading item upgrade data from a JSON file.
|
||||||
|
type schema struct {
|
||||||
|
RenamedIDs map[string]string `json:"renamedIds,omitempty"`
|
||||||
|
RemappedMetas map[string]map[uint32]string `json:"remappedMetas,omitempty"`
|
||||||
|
}
|
||||||
10
internal/item/schemas/0001_1.6_beta_to_1.6.0.json
Normal file
10
internal/item/schemas/0001_1.6_beta_to_1.6.0.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:nametag": "minecraft:name_tag",
|
||||||
|
"minecraft:prismarineshard": "minecraft:prismarine_shard",
|
||||||
|
"minecraft:stone_slab": "minecraft:double_stone_slab",
|
||||||
|
"minecraft:stone_slab2": "minecraft:double_stone_slab2",
|
||||||
|
"minecraft:stone_slab3": "minecraft:double_stone_slab3",
|
||||||
|
"minecraft:stone_slab4": "minecraft:double_stone_slab4"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
internal/item/schemas/0011_1.11.4_to_1.12.0.json
Normal file
20
internal/item/schemas/0011_1.11.4_to_1.12.0.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:glazedterracotta.black": "minecraft:black_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.blue": "minecraft:blue_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.brown": "minecraft:brown_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.cyan": "minecraft:cyan_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.gray": "minecraft:gray_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.green": "minecraft:green_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.light_blue": "minecraft:light_blue_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.lime": "minecraft:lime_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.magenta": "minecraft:magenta_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.orange": "minecraft:orange_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.pink": "minecraft:pink_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.purple": "minecraft:purple_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.red": "minecraft:red_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.silver": "minecraft:silver_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.white": "minecraft:white_glazed_terracotta",
|
||||||
|
"minecraft:glazedterracotta.yellow": "minecraft:yellow_glazed_terracotta"
|
||||||
|
}
|
||||||
|
}
|
||||||
165
internal/item/schemas/0021_1.16.0_to_1.16.100.json
Normal file
165
internal/item/schemas/0021_1.16.0_to_1.16.100.json
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:banner_pattern": {
|
||||||
|
"0": "minecraft:creeper_banner_pattern",
|
||||||
|
"1": "minecraft:skull_banner_pattern",
|
||||||
|
"2": "minecraft:flower_banner_pattern",
|
||||||
|
"3": "minecraft:mojang_banner_pattern",
|
||||||
|
"4": "minecraft:field_masoned_banner_pattern",
|
||||||
|
"5": "minecraft:bordure_indented_banner_pattern",
|
||||||
|
"6": "minecraft:piglin_banner_pattern"
|
||||||
|
},
|
||||||
|
"minecraft:boat": {
|
||||||
|
"0": "minecraft:oak_boat",
|
||||||
|
"1": "minecraft:spruce_boat",
|
||||||
|
"2": "minecraft:birch_boat",
|
||||||
|
"3": "minecraft:jungle_boat",
|
||||||
|
"4": "minecraft:acacia_boat",
|
||||||
|
"5": "minecraft:dark_oak_boat"
|
||||||
|
},
|
||||||
|
"minecraft:bucket": {
|
||||||
|
"1": "minecraft:milk_bucket",
|
||||||
|
"10": "minecraft:lava_bucket",
|
||||||
|
"2": "minecraft:cod_bucket",
|
||||||
|
"3": "minecraft:salmon_bucket",
|
||||||
|
"4": "minecraft:tropical_fish_bucket",
|
||||||
|
"5": "minecraft:pufferfish_bucket",
|
||||||
|
"8": "minecraft:water_bucket"
|
||||||
|
},
|
||||||
|
"minecraft:coal": {
|
||||||
|
"1": "minecraft:charcoal"
|
||||||
|
},
|
||||||
|
"minecraft:dye": {
|
||||||
|
"0": "minecraft:ink_sac",
|
||||||
|
"1": "minecraft:red_dye",
|
||||||
|
"10": "minecraft:lime_dye",
|
||||||
|
"11": "minecraft:yellow_dye",
|
||||||
|
"12": "minecraft:light_blue_dye",
|
||||||
|
"13": "minecraft:magenta_dye",
|
||||||
|
"14": "minecraft:orange_dye",
|
||||||
|
"15": "minecraft:bone_meal",
|
||||||
|
"16": "minecraft:black_dye",
|
||||||
|
"17": "minecraft:brown_dye",
|
||||||
|
"18": "minecraft:blue_dye",
|
||||||
|
"19": "minecraft:white_dye",
|
||||||
|
"2": "minecraft:green_dye",
|
||||||
|
"3": "minecraft:cocoa_beans",
|
||||||
|
"4": "minecraft:lapis_lazuli",
|
||||||
|
"5": "minecraft:purple_dye",
|
||||||
|
"6": "minecraft:cyan_dye",
|
||||||
|
"7": "minecraft:light_gray_dye",
|
||||||
|
"8": "minecraft:gray_dye",
|
||||||
|
"9": "minecraft:pink_dye"
|
||||||
|
},
|
||||||
|
"minecraft:spawn_egg": {
|
||||||
|
"10": "minecraft:chicken_spawn_egg",
|
||||||
|
"104": "minecraft:evoker_spawn_egg",
|
||||||
|
"105": "minecraft:vex_spawn_egg",
|
||||||
|
"108": "minecraft:pufferfish_spawn_egg",
|
||||||
|
"109": "minecraft:salmon_spawn_egg",
|
||||||
|
"11": "minecraft:cow_spawn_egg",
|
||||||
|
"110": "minecraft:drowned_spawn_egg",
|
||||||
|
"111": "minecraft:tropical_fish_spawn_egg",
|
||||||
|
"112": "minecraft:cod_spawn_egg",
|
||||||
|
"113": "minecraft:panda_spawn_egg",
|
||||||
|
"114": "minecraft:pillager_spawn_egg",
|
||||||
|
"115": "minecraft:villager_spawn_egg",
|
||||||
|
"116": "minecraft:zombie_villager_spawn_egg",
|
||||||
|
"118": "minecraft:wandering_trader_spawn_egg",
|
||||||
|
"12": "minecraft:pig_spawn_egg",
|
||||||
|
"121": "minecraft:fox_spawn_egg",
|
||||||
|
"122": "minecraft:bee_spawn_egg",
|
||||||
|
"123": "minecraft:piglin_spawn_egg",
|
||||||
|
"124": "minecraft:hoglin_spawn_egg",
|
||||||
|
"125": "minecraft:strider_spawn_egg",
|
||||||
|
"126": "minecraft:zoglin_spawn_egg",
|
||||||
|
"127": "minecraft:piglin_brute_spawn_egg",
|
||||||
|
"13": "minecraft:sheep_spawn_egg",
|
||||||
|
"14": "minecraft:wolf_spawn_egg",
|
||||||
|
"15": "minecraft:villager_spawn_egg",
|
||||||
|
"16": "minecraft:mooshroom_spawn_egg",
|
||||||
|
"17": "minecraft:squid_spawn_egg",
|
||||||
|
"18": "minecraft:rabbit_spawn_egg",
|
||||||
|
"19": "minecraft:bat_spawn_egg",
|
||||||
|
"22": "minecraft:ocelot_spawn_egg",
|
||||||
|
"23": "minecraft:horse_spawn_egg",
|
||||||
|
"24": "minecraft:donkey_spawn_egg",
|
||||||
|
"25": "minecraft:mule_spawn_egg",
|
||||||
|
"26": "minecraft:skeleton_horse_spawn_egg",
|
||||||
|
"27": "minecraft:zombie_horse_spawn_egg",
|
||||||
|
"28": "minecraft:polar_bear_spawn_egg",
|
||||||
|
"29": "minecraft:llama_spawn_egg",
|
||||||
|
"30": "minecraft:parrot_spawn_egg",
|
||||||
|
"31": "minecraft:dolphin_spawn_egg",
|
||||||
|
"32": "minecraft:zombie_spawn_egg",
|
||||||
|
"33": "minecraft:creeper_spawn_egg",
|
||||||
|
"34": "minecraft:skeleton_spawn_egg",
|
||||||
|
"35": "minecraft:spider_spawn_egg",
|
||||||
|
"36": "minecraft:zombie_pigman_spawn_egg",
|
||||||
|
"37": "minecraft:slime_spawn_egg",
|
||||||
|
"38": "minecraft:enderman_spawn_egg",
|
||||||
|
"39": "minecraft:silverfish_spawn_egg",
|
||||||
|
"40": "minecraft:cave_spider_spawn_egg",
|
||||||
|
"41": "minecraft:ghast_spawn_egg",
|
||||||
|
"42": "minecraft:magma_cube_spawn_egg",
|
||||||
|
"43": "minecraft:blaze_spawn_egg",
|
||||||
|
"44": "minecraft:zombie_villager_spawn_egg",
|
||||||
|
"45": "minecraft:witch_spawn_egg",
|
||||||
|
"46": "minecraft:stray_spawn_egg",
|
||||||
|
"47": "minecraft:husk_spawn_egg",
|
||||||
|
"48": "minecraft:wither_skeleton_spawn_egg",
|
||||||
|
"49": "minecraft:guardian_spawn_egg",
|
||||||
|
"50": "minecraft:elder_guardian_spawn_egg",
|
||||||
|
"51": "minecraft:npc_spawn_egg",
|
||||||
|
"54": "minecraft:shulker_spawn_egg",
|
||||||
|
"55": "minecraft:endermite_spawn_egg",
|
||||||
|
"56": "minecraft:agent_spawn_egg",
|
||||||
|
"57": "minecraft:vindicator_spawn_egg",
|
||||||
|
"58": "minecraft:phantom_spawn_egg",
|
||||||
|
"59": "minecraft:ravager_spawn_egg",
|
||||||
|
"74": "minecraft:turtle_spawn_egg",
|
||||||
|
"75": "minecraft:cat_spawn_egg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:appleenchanted": "minecraft:enchanted_golden_apple",
|
||||||
|
"minecraft:carrotonastick": "minecraft:carrot_on_a_stick",
|
||||||
|
"minecraft:chorus_fruit_popped": "minecraft:popped_chorus_fruit",
|
||||||
|
"minecraft:clownfish": "minecraft:tropical_fish",
|
||||||
|
"minecraft:cooked_fish": "minecraft:cooked_cod",
|
||||||
|
"minecraft:darkoak_sign": "minecraft:dark_oak_sign",
|
||||||
|
"minecraft:emptymap": "minecraft:empty_map",
|
||||||
|
"minecraft:fireball": "minecraft:fire_charge",
|
||||||
|
"minecraft:fireworks": "minecraft:firework_rocket",
|
||||||
|
"minecraft:fireworkscharge": "minecraft:firework_star",
|
||||||
|
"minecraft:fish": "minecraft:cod",
|
||||||
|
"minecraft:horsearmordiamond": "minecraft:diamond_horse_armor",
|
||||||
|
"minecraft:horsearmorgold": "minecraft:golden_horse_armor",
|
||||||
|
"minecraft:horsearmoriron": "minecraft:iron_horse_armor",
|
||||||
|
"minecraft:horsearmorleather": "minecraft:leather_horse_armor",
|
||||||
|
"minecraft:lodestonecompass": "minecraft:lodestone_compass",
|
||||||
|
"minecraft:map": "minecraft:filled_map",
|
||||||
|
"minecraft:melon": "minecraft:melon_slice",
|
||||||
|
"minecraft:muttoncooked": "minecraft:cooked_mutton",
|
||||||
|
"minecraft:muttonraw": "minecraft:mutton",
|
||||||
|
"minecraft:netherstar": "minecraft:nether_star",
|
||||||
|
"minecraft:record_11": "minecraft:music_disc_11",
|
||||||
|
"minecraft:record_13": "minecraft:music_disc_13",
|
||||||
|
"minecraft:record_blocks": "minecraft:music_disc_blocks",
|
||||||
|
"minecraft:record_cat": "minecraft:music_disc_cat",
|
||||||
|
"minecraft:record_chirp": "minecraft:music_disc_chirp",
|
||||||
|
"minecraft:record_far": "minecraft:music_disc_far",
|
||||||
|
"minecraft:record_mall": "minecraft:music_disc_mall",
|
||||||
|
"minecraft:record_mellohi": "minecraft:music_disc_mellohi",
|
||||||
|
"minecraft:record_pigstep": "minecraft:music_disc_pigstep",
|
||||||
|
"minecraft:record_stal": "minecraft:music_disc_stal",
|
||||||
|
"minecraft:record_strad": "minecraft:music_disc_strad",
|
||||||
|
"minecraft:record_wait": "minecraft:music_disc_wait",
|
||||||
|
"minecraft:record_ward": "minecraft:music_disc_ward",
|
||||||
|
"minecraft:reeds": "minecraft:sugar_cane",
|
||||||
|
"minecraft:sign": "minecraft:oak_sign",
|
||||||
|
"minecraft:speckled_melon": "minecraft:glistering_melon_slice",
|
||||||
|
"minecraft:totem": "minecraft:totem_of_undying",
|
||||||
|
"minecraft:turtle_shell_piece": "minecraft:scute"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
internal/item/schemas/0031_1.16.100_to_1.16.200.json
Normal file
7
internal/item/schemas/0031_1.16.100_to_1.16.200.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:spawn_egg": {
|
||||||
|
"128": "minecraft:goat_spawn_egg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
internal/item/schemas/0041_1.16.200_to_1.17.30.json
Normal file
12
internal/item/schemas/0041_1.16.200_to_1.17.30.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:bucket": {
|
||||||
|
"11": "minecraft:powder_snow_bucket",
|
||||||
|
"12": "minecraft:axolotl_bucket"
|
||||||
|
},
|
||||||
|
"minecraft:spawn_egg": {
|
||||||
|
"129": "minecraft:glow_squid_spawn_egg",
|
||||||
|
"130": "minecraft:axolotl_spawn_egg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
internal/item/schemas/0051_1.17.40_to_1.18.0.json
Normal file
5
internal/item/schemas/0051_1.17.40_to_1.18.0.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:record_otherside": "minecraft:music_disc_otherside"
|
||||||
|
}
|
||||||
|
}
|
||||||
16
internal/item/schemas/0061_1.18.0_to_1.18.10.json
Normal file
16
internal/item/schemas/0061_1.18.0_to_1.18.10.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:banner_pattern": {
|
||||||
|
"7": "minecraft:globe_banner_pattern"
|
||||||
|
},
|
||||||
|
"minecraft:bucket": {
|
||||||
|
"13": "minecraft:tadpole_bucket"
|
||||||
|
},
|
||||||
|
"minecraft:spawn_egg": {
|
||||||
|
"132": "minecraft:frog_spawn_egg",
|
||||||
|
"133": "minecraft:tadpole_spawn_egg",
|
||||||
|
"134": "minecraft:allay_spawn_egg",
|
||||||
|
"135": "minecraft:firefly_spawn_egg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
internal/item/schemas/0071_1.18.10_to_1.18.30.json
Normal file
23
internal/item/schemas/0071_1.18.10_to_1.18.30.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:chest_boat": {
|
||||||
|
"0": "minecraft:oak_chest_boat",
|
||||||
|
"1": "minecraft:spruce_chest_boat",
|
||||||
|
"2": "minecraft:birch_chest_boat",
|
||||||
|
"3": "minecraft:jungle_chest_boat",
|
||||||
|
"4": "minecraft:acacia_chest_boat",
|
||||||
|
"5": "minecraft:dark_oak_chest_boat"
|
||||||
|
},
|
||||||
|
"minecraft:spawn_egg": {
|
||||||
|
"131": "minecraft:warden_spawn_egg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:concretepowder": "minecraft:concrete_powder",
|
||||||
|
"minecraft:invisiblebedrock": "minecraft:invisible_bedrock",
|
||||||
|
"minecraft:movingblock": "minecraft:moving_block",
|
||||||
|
"minecraft:pistonarmcollision": "minecraft:piston_arm_collision",
|
||||||
|
"minecraft:sealantern": "minecraft:sea_lantern",
|
||||||
|
"minecraft:stickypistonarmcollision": "minecraft:sticky_piston_arm_collision"
|
||||||
|
}
|
||||||
|
}
|
||||||
25
internal/item/schemas/0081_1.18.30_to_1.19.30.34_beta.json
Normal file
25
internal/item/schemas/0081_1.18.30_to_1.19.30.34_beta.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:boat": {
|
||||||
|
"6": "minecraft:mangrove_boat"
|
||||||
|
},
|
||||||
|
"minecraft:chest_boat": {
|
||||||
|
"6": "minecraft:mangrove_chest_boat"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:double_stone_slab": "minecraft:stone_block_slab",
|
||||||
|
"minecraft:double_stone_slab2": "minecraft:stone_block_slab2",
|
||||||
|
"minecraft:double_stone_slab3": "minecraft:stone_block_slab3",
|
||||||
|
"minecraft:double_stone_slab4": "minecraft:stone_block_slab4",
|
||||||
|
"minecraft:real_double_stone_slab": "minecraft:double_stone_block_slab",
|
||||||
|
"minecraft:real_double_stone_slab2": "minecraft:double_stone_block_slab2",
|
||||||
|
"minecraft:real_double_stone_slab3": "minecraft:double_stone_block_slab3",
|
||||||
|
"minecraft:real_double_stone_slab4": "minecraft:double_stone_block_slab4",
|
||||||
|
"minecraft:record_5": "minecraft:music_disc_5",
|
||||||
|
"minecraft:stone_slab": "minecraft:stone_block_slab",
|
||||||
|
"minecraft:stone_slab2": "minecraft:stone_block_slab2",
|
||||||
|
"minecraft:stone_slab3": "minecraft:stone_block_slab3",
|
||||||
|
"minecraft:stone_slab4": "minecraft:stone_block_slab4"
|
||||||
|
}
|
||||||
|
}
|
||||||
37
internal/item/schemas/0091_1.19.60_to_1.19.70.26_beta.json
Normal file
37
internal/item/schemas/0091_1.19.60_to_1.19.70.26_beta.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:boat": {
|
||||||
|
"7": "minecraft:bamboo_raft"
|
||||||
|
},
|
||||||
|
"minecraft:chest_boat": {
|
||||||
|
"7": "minecraft:bamboo_chest_raft"
|
||||||
|
},
|
||||||
|
"minecraft:spawn_egg": {
|
||||||
|
"138": "minecraft:camel_spawn_egg",
|
||||||
|
"139": "minecraft:sniffer_spawn_egg",
|
||||||
|
"157": "minecraft:trader_llama_spawn_egg",
|
||||||
|
"20": "minecraft:iron_golem_spawn_egg",
|
||||||
|
"21": "minecraft:snow_golem_spawn_egg",
|
||||||
|
"52": "minecraft:wither_spawn_egg",
|
||||||
|
"53": "minecraft:ender_dragon_spawn_egg"
|
||||||
|
},
|
||||||
|
"minecraft:wool": {
|
||||||
|
"0": "minecraft:white_wool",
|
||||||
|
"1": "minecraft:orange_wool",
|
||||||
|
"10": "minecraft:purple_wool",
|
||||||
|
"11": "minecraft:blue_wool",
|
||||||
|
"12": "minecraft:brown_wool",
|
||||||
|
"13": "minecraft:green_wool",
|
||||||
|
"14": "minecraft:red_wool",
|
||||||
|
"15": "minecraft:black_wool",
|
||||||
|
"2": "minecraft:magenta_wool",
|
||||||
|
"3": "minecraft:light_blue_wool",
|
||||||
|
"4": "minecraft:yellow_wool",
|
||||||
|
"5": "minecraft:lime_wool",
|
||||||
|
"6": "minecraft:pink_wool",
|
||||||
|
"7": "minecraft:gray_wool",
|
||||||
|
"8": "minecraft:light_gray_wool",
|
||||||
|
"9": "minecraft:cyan_wool"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
internal/item/schemas/0101_1.19.70_to_1.19.80.24_beta.json
Normal file
36
internal/item/schemas/0101_1.19.70_to_1.19.80.24_beta.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:boat": {
|
||||||
|
"8": "minecraft:cherry_boat"
|
||||||
|
},
|
||||||
|
"minecraft:chest_boat": {
|
||||||
|
"8": "minecraft:cherry_chest_boat"
|
||||||
|
},
|
||||||
|
"minecraft:fence": {
|
||||||
|
"0": "minecraft:oak_fence",
|
||||||
|
"1": "minecraft:spruce_fence",
|
||||||
|
"2": "minecraft:birch_fence",
|
||||||
|
"3": "minecraft:jungle_fence",
|
||||||
|
"4": "minecraft:acacia_fence",
|
||||||
|
"5": "minecraft:dark_oak_fence"
|
||||||
|
},
|
||||||
|
"minecraft:log": {
|
||||||
|
"0": "minecraft:oak_log",
|
||||||
|
"1": "minecraft:spruce_log",
|
||||||
|
"10": "minecraft:birch_log",
|
||||||
|
"11": "minecraft:jungle_log",
|
||||||
|
"2": "minecraft:birch_log",
|
||||||
|
"3": "minecraft:jungle_log",
|
||||||
|
"5": "minecraft:spruce_log",
|
||||||
|
"6": "minecraft:birch_log",
|
||||||
|
"7": "minecraft:jungle_log",
|
||||||
|
"9": "minecraft:spruce_log"
|
||||||
|
},
|
||||||
|
"minecraft:log2": {
|
||||||
|
"0": "minecraft:acacia_log",
|
||||||
|
"1": "minecraft:dark_oak_log",
|
||||||
|
"5": "minecraft:dark_oak_log",
|
||||||
|
"9": "minecraft:dark_oak_log"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
internal/item/schemas/0111_1.19.80_to_1.20.0.23_beta.json
Normal file
37
internal/item/schemas/0111_1.19.80_to_1.20.0.23_beta.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:carpet": {
|
||||||
|
"0": "minecraft:white_carpet",
|
||||||
|
"1": "minecraft:orange_carpet",
|
||||||
|
"10": "minecraft:purple_carpet",
|
||||||
|
"11": "minecraft:blue_carpet",
|
||||||
|
"12": "minecraft:brown_carpet",
|
||||||
|
"13": "minecraft:green_carpet",
|
||||||
|
"14": "minecraft:red_carpet",
|
||||||
|
"15": "minecraft:black_carpet",
|
||||||
|
"2": "minecraft:magenta_carpet",
|
||||||
|
"3": "minecraft:light_blue_carpet",
|
||||||
|
"4": "minecraft:yellow_carpet",
|
||||||
|
"5": "minecraft:lime_carpet",
|
||||||
|
"6": "minecraft:pink_carpet",
|
||||||
|
"7": "minecraft:gray_carpet",
|
||||||
|
"8": "minecraft:light_gray_carpet",
|
||||||
|
"9": "minecraft:cyan_carpet"
|
||||||
|
},
|
||||||
|
"minecraft:coral": {
|
||||||
|
"0": "minecraft:tube_coral",
|
||||||
|
"1": "minecraft:brain_coral",
|
||||||
|
"10": "minecraft:dead_bubble_coral",
|
||||||
|
"11": "minecraft:dead_fire_coral",
|
||||||
|
"12": "minecraft:dead_horn_coral",
|
||||||
|
"2": "minecraft:bubble_coral",
|
||||||
|
"3": "minecraft:fire_coral",
|
||||||
|
"4": "minecraft:horn_coral",
|
||||||
|
"8": "minecraft:dead_tube_coral",
|
||||||
|
"9": "minecraft:dead_brain_coral"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:record_relic": "minecraft:music_disc_relic"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:concrete": {
|
||||||
|
"0": "minecraft:white_concrete",
|
||||||
|
"1": "minecraft:orange_concrete",
|
||||||
|
"10": "minecraft:purple_concrete",
|
||||||
|
"11": "minecraft:blue_concrete",
|
||||||
|
"12": "minecraft:brown_concrete",
|
||||||
|
"13": "minecraft:green_concrete",
|
||||||
|
"14": "minecraft:red_concrete",
|
||||||
|
"15": "minecraft:black_concrete",
|
||||||
|
"2": "minecraft:magenta_concrete",
|
||||||
|
"3": "minecraft:light_blue_concrete",
|
||||||
|
"4": "minecraft:yellow_concrete",
|
||||||
|
"5": "minecraft:lime_concrete",
|
||||||
|
"6": "minecraft:pink_concrete",
|
||||||
|
"7": "minecraft:gray_concrete",
|
||||||
|
"8": "minecraft:light_gray_concrete",
|
||||||
|
"9": "minecraft:cyan_concrete"
|
||||||
|
},
|
||||||
|
"minecraft:shulker_box": {
|
||||||
|
"0": "minecraft:white_shulker_box",
|
||||||
|
"1": "minecraft:orange_shulker_box",
|
||||||
|
"10": "minecraft:purple_shulker_box",
|
||||||
|
"11": "minecraft:blue_shulker_box",
|
||||||
|
"12": "minecraft:brown_shulker_box",
|
||||||
|
"13": "minecraft:green_shulker_box",
|
||||||
|
"14": "minecraft:red_shulker_box",
|
||||||
|
"15": "minecraft:black_shulker_box",
|
||||||
|
"2": "minecraft:magenta_shulker_box",
|
||||||
|
"3": "minecraft:light_blue_shulker_box",
|
||||||
|
"4": "minecraft:yellow_shulker_box",
|
||||||
|
"5": "minecraft:lime_shulker_box",
|
||||||
|
"6": "minecraft:pink_shulker_box",
|
||||||
|
"7": "minecraft:gray_shulker_box",
|
||||||
|
"8": "minecraft:light_gray_shulker_box",
|
||||||
|
"9": "minecraft:cyan_shulker_box"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:stained_glass": {
|
||||||
|
"0": "minecraft:white_stained_glass",
|
||||||
|
"1": "minecraft:orange_stained_glass",
|
||||||
|
"10": "minecraft:purple_stained_glass",
|
||||||
|
"11": "minecraft:blue_stained_glass",
|
||||||
|
"12": "minecraft:brown_stained_glass",
|
||||||
|
"13": "minecraft:green_stained_glass",
|
||||||
|
"14": "minecraft:red_stained_glass",
|
||||||
|
"15": "minecraft:black_stained_glass",
|
||||||
|
"2": "minecraft:magenta_stained_glass",
|
||||||
|
"3": "minecraft:light_blue_stained_glass",
|
||||||
|
"4": "minecraft:yellow_stained_glass",
|
||||||
|
"5": "minecraft:lime_stained_glass",
|
||||||
|
"6": "minecraft:pink_stained_glass",
|
||||||
|
"7": "minecraft:gray_stained_glass",
|
||||||
|
"8": "minecraft:light_gray_stained_glass",
|
||||||
|
"9": "minecraft:cyan_stained_glass"
|
||||||
|
},
|
||||||
|
"minecraft:stained_glass_pane": {
|
||||||
|
"0": "minecraft:white_stained_glass_pane",
|
||||||
|
"1": "minecraft:orange_stained_glass_pane",
|
||||||
|
"10": "minecraft:purple_stained_glass_pane",
|
||||||
|
"11": "minecraft:blue_stained_glass_pane",
|
||||||
|
"12": "minecraft:brown_stained_glass_pane",
|
||||||
|
"13": "minecraft:green_stained_glass_pane",
|
||||||
|
"14": "minecraft:red_stained_glass_pane",
|
||||||
|
"15": "minecraft:black_stained_glass_pane",
|
||||||
|
"2": "minecraft:magenta_stained_glass_pane",
|
||||||
|
"3": "minecraft:light_blue_stained_glass_pane",
|
||||||
|
"4": "minecraft:yellow_stained_glass_pane",
|
||||||
|
"5": "minecraft:lime_stained_glass_pane",
|
||||||
|
"6": "minecraft:pink_stained_glass_pane",
|
||||||
|
"7": "minecraft:gray_stained_glass_pane",
|
||||||
|
"8": "minecraft:light_gray_stained_glass_pane",
|
||||||
|
"9": "minecraft:cyan_stained_glass_pane"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:concrete_powder": {
|
||||||
|
"0": "minecraft:white_concrete_powder",
|
||||||
|
"1": "minecraft:orange_concrete_powder",
|
||||||
|
"10": "minecraft:purple_concrete_powder",
|
||||||
|
"11": "minecraft:blue_concrete_powder",
|
||||||
|
"12": "minecraft:brown_concrete_powder",
|
||||||
|
"13": "minecraft:green_concrete_powder",
|
||||||
|
"14": "minecraft:red_concrete_powder",
|
||||||
|
"15": "minecraft:black_concrete_powder",
|
||||||
|
"2": "minecraft:magenta_concrete_powder",
|
||||||
|
"3": "minecraft:light_blue_concrete_powder",
|
||||||
|
"4": "minecraft:yellow_concrete_powder",
|
||||||
|
"5": "minecraft:lime_concrete_powder",
|
||||||
|
"6": "minecraft:pink_concrete_powder",
|
||||||
|
"7": "minecraft:gray_concrete_powder",
|
||||||
|
"8": "minecraft:light_gray_concrete_powder",
|
||||||
|
"9": "minecraft:cyan_concrete_powder"
|
||||||
|
},
|
||||||
|
"minecraft:stained_hardened_clay": {
|
||||||
|
"0": "minecraft:white_terracotta",
|
||||||
|
"1": "minecraft:orange_terracotta",
|
||||||
|
"10": "minecraft:purple_terracotta",
|
||||||
|
"11": "minecraft:blue_terracotta",
|
||||||
|
"12": "minecraft:brown_terracotta",
|
||||||
|
"13": "minecraft:green_terracotta",
|
||||||
|
"14": "minecraft:red_terracotta",
|
||||||
|
"15": "minecraft:black_terracotta",
|
||||||
|
"2": "minecraft:magenta_terracotta",
|
||||||
|
"3": "minecraft:light_blue_terracotta",
|
||||||
|
"4": "minecraft:yellow_terracotta",
|
||||||
|
"5": "minecraft:lime_terracotta",
|
||||||
|
"6": "minecraft:pink_terracotta",
|
||||||
|
"7": "minecraft:gray_terracotta",
|
||||||
|
"8": "minecraft:light_gray_terracotta",
|
||||||
|
"9": "minecraft:cyan_terracotta"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:planks": {
|
||||||
|
"0": "minecraft:oak_planks",
|
||||||
|
"1": "minecraft:spruce_planks",
|
||||||
|
"2": "minecraft:birch_planks",
|
||||||
|
"3": "minecraft:jungle_planks",
|
||||||
|
"4": "minecraft:acacia_planks",
|
||||||
|
"5": "minecraft:dark_oak_planks"
|
||||||
|
},
|
||||||
|
"minecraft:stone": {
|
||||||
|
"1": "minecraft:granite",
|
||||||
|
"2": "minecraft:polished_granite",
|
||||||
|
"3": "minecraft:diorite",
|
||||||
|
"4": "minecraft:polished_diorite",
|
||||||
|
"5": "minecraft:andesite",
|
||||||
|
"6": "minecraft:polished_andesite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:hard_stained_glass": {
|
||||||
|
"0": "minecraft:hard_white_stained_glass",
|
||||||
|
"1": "minecraft:hard_orange_stained_glass",
|
||||||
|
"10": "minecraft:hard_purple_stained_glass",
|
||||||
|
"11": "minecraft:hard_blue_stained_glass",
|
||||||
|
"12": "minecraft:hard_brown_stained_glass",
|
||||||
|
"13": "minecraft:hard_green_stained_glass",
|
||||||
|
"14": "minecraft:hard_red_stained_glass",
|
||||||
|
"15": "minecraft:hard_black_stained_glass",
|
||||||
|
"2": "minecraft:hard_magenta_stained_glass",
|
||||||
|
"3": "minecraft:hard_light_blue_stained_glass",
|
||||||
|
"4": "minecraft:hard_yellow_stained_glass",
|
||||||
|
"5": "minecraft:hard_lime_stained_glass",
|
||||||
|
"6": "minecraft:hard_pink_stained_glass",
|
||||||
|
"7": "minecraft:hard_gray_stained_glass",
|
||||||
|
"8": "minecraft:hard_light_gray_stained_glass",
|
||||||
|
"9": "minecraft:hard_cyan_stained_glass"
|
||||||
|
},
|
||||||
|
"minecraft:hard_stained_glass_pane": {
|
||||||
|
"0": "minecraft:hard_white_stained_glass_pane",
|
||||||
|
"1": "minecraft:hard_orange_stained_glass_pane",
|
||||||
|
"10": "minecraft:hard_purple_stained_glass_pane",
|
||||||
|
"11": "minecraft:hard_blue_stained_glass_pane",
|
||||||
|
"12": "minecraft:hard_brown_stained_glass_pane",
|
||||||
|
"13": "minecraft:hard_green_stained_glass_pane",
|
||||||
|
"14": "minecraft:hard_red_stained_glass_pane",
|
||||||
|
"15": "minecraft:hard_black_stained_glass_pane",
|
||||||
|
"2": "minecraft:hard_magenta_stained_glass_pane",
|
||||||
|
"3": "minecraft:hard_light_blue_stained_glass_pane",
|
||||||
|
"4": "minecraft:hard_yellow_stained_glass_pane",
|
||||||
|
"5": "minecraft:hard_lime_stained_glass_pane",
|
||||||
|
"6": "minecraft:hard_pink_stained_glass_pane",
|
||||||
|
"7": "minecraft:hard_gray_stained_glass_pane",
|
||||||
|
"8": "minecraft:hard_light_gray_stained_glass_pane",
|
||||||
|
"9": "minecraft:hard_cyan_stained_glass_pane"
|
||||||
|
},
|
||||||
|
"minecraft:spawn_egg": {
|
||||||
|
"140": "minecraft:breeze_spawn_egg",
|
||||||
|
"142": "minecraft:armadillo_spawn_egg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:scute": "minecraft:turtle_scute"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:leaves": {
|
||||||
|
"0": "minecraft:oak_leaves",
|
||||||
|
"1": "minecraft:spruce_leaves",
|
||||||
|
"2": "minecraft:birch_leaves",
|
||||||
|
"3": "minecraft:jungle_leaves"
|
||||||
|
},
|
||||||
|
"minecraft:leaves2": {
|
||||||
|
"0": "minecraft:acacia_leaves",
|
||||||
|
"1": "minecraft:dark_oak_leaves"
|
||||||
|
},
|
||||||
|
"minecraft:spawn_egg": {
|
||||||
|
"144": "minecraft:bogged_spawn_egg"
|
||||||
|
},
|
||||||
|
"minecraft:wood": {
|
||||||
|
"0": "minecraft:oak_wood",
|
||||||
|
"1": "minecraft:spruce_wood",
|
||||||
|
"10": "minecraft:stripped_birch_wood",
|
||||||
|
"11": "minecraft:stripped_jungle_wood",
|
||||||
|
"12": "minecraft:stripped_acacia_wood",
|
||||||
|
"13": "minecraft:stripped_dark_oak_wood",
|
||||||
|
"2": "minecraft:birch_wood",
|
||||||
|
"3": "minecraft:jungle_wood",
|
||||||
|
"4": "minecraft:acacia_wood",
|
||||||
|
"5": "minecraft:dark_oak_wood",
|
||||||
|
"8": "minecraft:stripped_oak_wood",
|
||||||
|
"9": "minecraft:stripped_spruce_wood"
|
||||||
|
},
|
||||||
|
"minecraft:wooden_slab": {
|
||||||
|
"0": "minecraft:oak_slab",
|
||||||
|
"1": "minecraft:spruce_slab",
|
||||||
|
"2": "minecraft:birch_slab",
|
||||||
|
"3": "minecraft:jungle_slab",
|
||||||
|
"4": "minecraft:acacia_slab",
|
||||||
|
"5": "minecraft:dark_oak_slab"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:grass": "minecraft:grass_block"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:coral_fan": {
|
||||||
|
"0": "minecraft:tube_coral_fan",
|
||||||
|
"1": "minecraft:brain_coral_fan",
|
||||||
|
"2": "minecraft:bubble_coral_fan",
|
||||||
|
"3": "minecraft:fire_coral_fan",
|
||||||
|
"4": "minecraft:horn_coral_fan"
|
||||||
|
},
|
||||||
|
"minecraft:coral_fan_dead": {
|
||||||
|
"0": "minecraft:dead_tube_coral_fan",
|
||||||
|
"1": "minecraft:dead_brain_coral_fan",
|
||||||
|
"2": "minecraft:dead_bubble_coral_fan",
|
||||||
|
"3": "minecraft:dead_fire_coral_fan",
|
||||||
|
"4": "minecraft:dead_horn_coral_fan"
|
||||||
|
},
|
||||||
|
"minecraft:red_flower": {
|
||||||
|
"0": "minecraft:poppy",
|
||||||
|
"1": "minecraft:blue_orchid",
|
||||||
|
"10": "minecraft:lily_of_the_valley",
|
||||||
|
"2": "minecraft:allium",
|
||||||
|
"3": "minecraft:azure_bluet",
|
||||||
|
"4": "minecraft:red_tulip",
|
||||||
|
"5": "minecraft:orange_tulip",
|
||||||
|
"6": "minecraft:white_tulip",
|
||||||
|
"7": "minecraft:pink_tulip",
|
||||||
|
"8": "minecraft:oxeye_daisy",
|
||||||
|
"9": "minecraft:cornflower"
|
||||||
|
},
|
||||||
|
"minecraft:sapling": {
|
||||||
|
"0": "minecraft:oak_sapling",
|
||||||
|
"1": "minecraft:spruce_sapling",
|
||||||
|
"2": "minecraft:birch_sapling",
|
||||||
|
"3": "minecraft:jungle_sapling",
|
||||||
|
"4": "minecraft:acacia_sapling",
|
||||||
|
"5": "minecraft:dark_oak_sapling"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:coral_block": {
|
||||||
|
"0": "minecraft:tube_coral_block",
|
||||||
|
"1": "minecraft:brain_coral_block",
|
||||||
|
"10": "minecraft:dead_bubble_coral_block",
|
||||||
|
"11": "minecraft:dead_fire_coral_block",
|
||||||
|
"12": "minecraft:dead_horn_coral_block",
|
||||||
|
"2": "minecraft:bubble_coral_block",
|
||||||
|
"3": "minecraft:fire_coral_block",
|
||||||
|
"4": "minecraft:horn_coral_block",
|
||||||
|
"8": "minecraft:dead_tube_coral_block",
|
||||||
|
"9": "minecraft:dead_brain_coral_block"
|
||||||
|
},
|
||||||
|
"minecraft:double_plant": {
|
||||||
|
"0": "minecraft:sunflower",
|
||||||
|
"1": "minecraft:lilac",
|
||||||
|
"2": "minecraft:tall_grass",
|
||||||
|
"3": "minecraft:large_fern",
|
||||||
|
"4": "minecraft:rose_bush",
|
||||||
|
"5": "minecraft:peony"
|
||||||
|
},
|
||||||
|
"minecraft:stone_block_slab": {
|
||||||
|
"0": "minecraft:smooth_stone_slab",
|
||||||
|
"1": "minecraft:sandstone_slab",
|
||||||
|
"2": "minecraft:petrified_oak_slab",
|
||||||
|
"3": "minecraft:cobblestone_slab",
|
||||||
|
"4": "minecraft:brick_slab",
|
||||||
|
"5": "minecraft:stone_brick_slab",
|
||||||
|
"6": "minecraft:quartz_slab",
|
||||||
|
"7": "minecraft:nether_brick_slab"
|
||||||
|
},
|
||||||
|
"minecraft:tallgrass": {
|
||||||
|
"0": "minecraft:short_grass",
|
||||||
|
"2": "minecraft:fern",
|
||||||
|
"3": "minecraft:fern"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:record_creator": "minecraft:music_disc_creator",
|
||||||
|
"minecraft:record_creator_music_box": "minecraft:music_disc_creator_music_box",
|
||||||
|
"minecraft:record_precipice": "minecraft:music_disc_precipice"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:anvil": {
|
||||||
|
"10": "minecraft:damaged_anvil",
|
||||||
|
"11": "minecraft:damaged_anvil",
|
||||||
|
"4": "minecraft:chipped_anvil",
|
||||||
|
"5": "minecraft:chipped_anvil",
|
||||||
|
"6": "minecraft:chipped_anvil",
|
||||||
|
"7": "minecraft:chipped_anvil",
|
||||||
|
"8": "minecraft:damaged_anvil",
|
||||||
|
"9": "minecraft:damaged_anvil"
|
||||||
|
},
|
||||||
|
"minecraft:dirt": {
|
||||||
|
"1": "minecraft:coarse_dirt"
|
||||||
|
},
|
||||||
|
"minecraft:double_stone_block_slab": {
|
||||||
|
"0": "minecraft:smooth_stone_double_slab",
|
||||||
|
"1": "minecraft:sandstone_double_slab",
|
||||||
|
"2": "minecraft:petrified_oak_double_slab",
|
||||||
|
"3": "minecraft:cobblestone_double_slab",
|
||||||
|
"4": "minecraft:brick_double_slab",
|
||||||
|
"5": "minecraft:stone_brick_double_slab",
|
||||||
|
"6": "minecraft:quartz_double_slab",
|
||||||
|
"7": "minecraft:nether_brick_double_slab"
|
||||||
|
},
|
||||||
|
"minecraft:double_stone_block_slab2": {
|
||||||
|
"0": "minecraft:red_sandstone_double_slab",
|
||||||
|
"1": "minecraft:purpur_double_slab",
|
||||||
|
"2": "minecraft:prismarine_double_slab",
|
||||||
|
"3": "minecraft:dark_prismarine_double_slab",
|
||||||
|
"4": "minecraft:prismarine_brick_double_slab",
|
||||||
|
"5": "minecraft:mossy_cobblestone_double_slab",
|
||||||
|
"6": "minecraft:smooth_sandstone_double_slab",
|
||||||
|
"7": "minecraft:red_nether_brick_double_slab"
|
||||||
|
},
|
||||||
|
"minecraft:double_stone_block_slab3": {
|
||||||
|
"0": "minecraft:end_stone_brick_double_slab",
|
||||||
|
"1": "minecraft:smooth_red_sandstone_double_slab",
|
||||||
|
"2": "minecraft:polished_andesite_double_slab",
|
||||||
|
"3": "minecraft:andesite_double_slab",
|
||||||
|
"4": "minecraft:diorite_double_slab",
|
||||||
|
"5": "minecraft:polished_diorite_double_slab",
|
||||||
|
"6": "minecraft:granite_double_slab",
|
||||||
|
"7": "minecraft:polished_granite_double_slab"
|
||||||
|
},
|
||||||
|
"minecraft:double_stone_block_slab4": {
|
||||||
|
"0": "minecraft:mossy_stone_brick_double_slab",
|
||||||
|
"1": "minecraft:smooth_quartz_double_slab",
|
||||||
|
"2": "minecraft:normal_stone_double_slab",
|
||||||
|
"3": "minecraft:cut_sandstone_double_slab",
|
||||||
|
"4": "minecraft:cut_red_sandstone_double_slab"
|
||||||
|
},
|
||||||
|
"minecraft:light_block": {
|
||||||
|
"0": "minecraft:light_block_0",
|
||||||
|
"1": "minecraft:light_block_1",
|
||||||
|
"10": "minecraft:light_block_10",
|
||||||
|
"11": "minecraft:light_block_11",
|
||||||
|
"12": "minecraft:light_block_12",
|
||||||
|
"13": "minecraft:light_block_13",
|
||||||
|
"14": "minecraft:light_block_14",
|
||||||
|
"15": "minecraft:light_block_15",
|
||||||
|
"2": "minecraft:light_block_2",
|
||||||
|
"3": "minecraft:light_block_3",
|
||||||
|
"4": "minecraft:light_block_4",
|
||||||
|
"5": "minecraft:light_block_5",
|
||||||
|
"6": "minecraft:light_block_6",
|
||||||
|
"7": "minecraft:light_block_7",
|
||||||
|
"8": "minecraft:light_block_8",
|
||||||
|
"9": "minecraft:light_block_9"
|
||||||
|
},
|
||||||
|
"minecraft:monster_egg": {
|
||||||
|
"0": "minecraft:infested_stone",
|
||||||
|
"1": "minecraft:infested_cobblestone",
|
||||||
|
"2": "minecraft:infested_stone_bricks",
|
||||||
|
"3": "minecraft:infested_mossy_stone_bricks",
|
||||||
|
"4": "minecraft:infested_cracked_stone_bricks",
|
||||||
|
"5": "minecraft:infested_chiseled_stone_bricks"
|
||||||
|
},
|
||||||
|
"minecraft:prismarine": {
|
||||||
|
"1": "minecraft:dark_prismarine",
|
||||||
|
"2": "minecraft:prismarine_bricks"
|
||||||
|
},
|
||||||
|
"minecraft:quartz_block": {
|
||||||
|
"1": "minecraft:chiseled_quartz_block",
|
||||||
|
"2": "minecraft:quartz_pillar",
|
||||||
|
"3": "minecraft:smooth_quartz"
|
||||||
|
},
|
||||||
|
"minecraft:red_sandstone": {
|
||||||
|
"1": "minecraft:chiseled_red_sandstone",
|
||||||
|
"2": "minecraft:cut_red_sandstone",
|
||||||
|
"3": "minecraft:smooth_red_sandstone"
|
||||||
|
},
|
||||||
|
"minecraft:sand": {
|
||||||
|
"1": "minecraft:red_sand"
|
||||||
|
},
|
||||||
|
"minecraft:sandstone": {
|
||||||
|
"1": "minecraft:chiseled_sandstone",
|
||||||
|
"2": "minecraft:cut_sandstone",
|
||||||
|
"3": "minecraft:smooth_sandstone"
|
||||||
|
},
|
||||||
|
"minecraft:stone_block_slab2": {
|
||||||
|
"0": "minecraft:red_sandstone_slab",
|
||||||
|
"1": "minecraft:purpur_slab",
|
||||||
|
"2": "minecraft:prismarine_slab",
|
||||||
|
"3": "minecraft:dark_prismarine_slab",
|
||||||
|
"4": "minecraft:prismarine_brick_slab",
|
||||||
|
"5": "minecraft:mossy_cobblestone_slab",
|
||||||
|
"6": "minecraft:smooth_sandstone_slab",
|
||||||
|
"7": "minecraft:red_nether_brick_slab"
|
||||||
|
},
|
||||||
|
"minecraft:stone_block_slab3": {
|
||||||
|
"0": "minecraft:end_stone_brick_slab",
|
||||||
|
"1": "minecraft:smooth_red_sandstone_slab",
|
||||||
|
"2": "minecraft:polished_andesite_slab",
|
||||||
|
"3": "minecraft:andesite_slab",
|
||||||
|
"4": "minecraft:diorite_slab",
|
||||||
|
"5": "minecraft:polished_diorite_slab",
|
||||||
|
"6": "minecraft:granite_slab",
|
||||||
|
"7": "minecraft:polished_granite_slab"
|
||||||
|
},
|
||||||
|
"minecraft:stone_block_slab4": {
|
||||||
|
"0": "minecraft:mossy_stone_brick_slab",
|
||||||
|
"1": "minecraft:smooth_quartz_slab",
|
||||||
|
"2": "minecraft:normal_stone_slab",
|
||||||
|
"3": "minecraft:cut_sandstone_slab",
|
||||||
|
"4": "minecraft:cut_red_sandstone_slab"
|
||||||
|
},
|
||||||
|
"minecraft:stonebrick": {
|
||||||
|
"0": "minecraft:stone_bricks",
|
||||||
|
"1": "minecraft:mossy_stone_bricks",
|
||||||
|
"2": "minecraft:cracked_stone_bricks",
|
||||||
|
"3": "minecraft:chiseled_stone_bricks"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renamedIds": {
|
||||||
|
"minecraft:yellow_flower": "minecraft:dandelion"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:chemistry_table": {
|
||||||
|
"0": "minecraft:compound_creator"
|
||||||
|
},
|
||||||
|
"minecraft:cobblestone_wall": {
|
||||||
|
"1": "minecraft:mossy_cobblestone_wall",
|
||||||
|
"10": "minecraft:end_stone_brick_wall",
|
||||||
|
"11": "minecraft:prismarine_wall",
|
||||||
|
"12": "minecraft:red_sandstone_wall",
|
||||||
|
"13": "minecraft:red_nether_brick_wall",
|
||||||
|
"2": "minecraft:granite_wall",
|
||||||
|
"3": "minecraft:diorite_wall",
|
||||||
|
"4": "minecraft:andesite_wall",
|
||||||
|
"5": "minecraft:sandstone_wall",
|
||||||
|
"6": "minecraft:brick_wall",
|
||||||
|
"7": "minecraft:stone_brick_wall",
|
||||||
|
"8": "minecraft:mossy_stone_brick_wall",
|
||||||
|
"9": "minecraft:nether_brick_wall"
|
||||||
|
},
|
||||||
|
"minecraft:colored_torch_bp": {
|
||||||
|
"0": "minecraft:colored_torch_blue",
|
||||||
|
"10": "minecraft:colored_torch_purple",
|
||||||
|
"11": "minecraft:colored_torch_purple",
|
||||||
|
"12": "minecraft:colored_torch_purple",
|
||||||
|
"13": "minecraft:colored_torch_purple",
|
||||||
|
"14": "minecraft:colored_torch_purple",
|
||||||
|
"15": "minecraft:colored_torch_purple",
|
||||||
|
"8": "minecraft:colored_torch_purple",
|
||||||
|
"9": "minecraft:colored_torch_purple"
|
||||||
|
},
|
||||||
|
"minecraft:colored_torch_rg": {
|
||||||
|
"0": "minecraft:colored_torch_red",
|
||||||
|
"10": "minecraft:colored_torch_green",
|
||||||
|
"11": "minecraft:colored_torch_green",
|
||||||
|
"12": "minecraft:colored_torch_green",
|
||||||
|
"13": "minecraft:colored_torch_green",
|
||||||
|
"14": "minecraft:colored_torch_green",
|
||||||
|
"15": "minecraft:colored_torch_green",
|
||||||
|
"8": "minecraft:colored_torch_green",
|
||||||
|
"9": "minecraft:colored_torch_green"
|
||||||
|
},
|
||||||
|
"minecraft:purpur_block": {
|
||||||
|
"1": "minecraft:deprecated_purpur_block_1",
|
||||||
|
"2": "minecraft:purpur_pillar",
|
||||||
|
"3": "minecraft:deprecated_purpur_block_2"
|
||||||
|
},
|
||||||
|
"minecraft:sponge": {
|
||||||
|
"1": "minecraft:wet_sponge"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"remappedMetas": {
|
||||||
|
"minecraft:skull": {
|
||||||
|
"0": "minecraft:skeleton_skull",
|
||||||
|
"1": "minecraft:wither_skeleton_skull",
|
||||||
|
"2": "minecraft:zombie_head",
|
||||||
|
"3": "minecraft:player_head",
|
||||||
|
"4": "minecraft:creeper_head",
|
||||||
|
"5": "minecraft:dragon_head",
|
||||||
|
"6": "minecraft:piglin_head"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
109
internal/item/upgrader.go
Normal file
109
internal/item/upgrader.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package item
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/df-mc/worldupgrader/blockupgrader"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Item ...
|
||||||
|
type Item struct {
|
||||||
|
Name string
|
||||||
|
Metadata uint32
|
||||||
|
Version uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade upgrades the given item using the registered item upgrade schemas.
|
||||||
|
func Upgrade(item Item, ver uint16) Item {
|
||||||
|
version := item.Version
|
||||||
|
for id, s := range schemas {
|
||||||
|
if version > id || id > ver {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := item.Name
|
||||||
|
metadata := item.Metadata
|
||||||
|
nameRenamed := false
|
||||||
|
metadataRemapped := false
|
||||||
|
|
||||||
|
if metadataCombinations, ok := s.RemappedMetas[name]; ok {
|
||||||
|
name, metadataRemapped = metadataCombinations[item.Metadata]
|
||||||
|
metadata = 0
|
||||||
|
} else if name, nameRenamed = s.RenamedIDs[item.Name]; !nameRenamed {
|
||||||
|
name = item.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if nameRenamed || metadataRemapped {
|
||||||
|
item = Item{
|
||||||
|
Name: name,
|
||||||
|
Metadata: metadata,
|
||||||
|
Version: id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downgrade downgrades the given item using the registered item upgrade schemas.
|
||||||
|
func Downgrade(item Item, ver uint16) Item {
|
||||||
|
for i, id := range schemaIDs {
|
||||||
|
s := schemas[uint16(id)]
|
||||||
|
if uint16(id) > item.Version {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := item.Name
|
||||||
|
metadata := item.Metadata
|
||||||
|
nameRenamed := false
|
||||||
|
metadataRemapped := false
|
||||||
|
|
||||||
|
for oldName, newName := range s.RenamedIDs {
|
||||||
|
if newName == name {
|
||||||
|
name = oldName
|
||||||
|
nameRenamed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !nameRenamed {
|
||||||
|
for oldName, m := range s.RemappedMetas {
|
||||||
|
for meta, newName := range m {
|
||||||
|
if newName == name {
|
||||||
|
name = oldName
|
||||||
|
metadata = meta
|
||||||
|
nameRenamed = true
|
||||||
|
metadataRemapped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if metadataRemapped {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ver > item.Version || i == len(schemaIDs)-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return Downgrade(Item{
|
||||||
|
Name: name,
|
||||||
|
Metadata: metadata,
|
||||||
|
Version: uint16(schemaIDs[i+1]),
|
||||||
|
}, ver)
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlockStateFromItemName(itemName string, metadata uint32) (blockupgrader.BlockState, bool) {
|
||||||
|
blockId, ok := itemToBlockIdMap[itemName]
|
||||||
|
if !ok {
|
||||||
|
return blockupgrader.BlockState{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
blockState, ok := blockStateMap[stateHash{
|
||||||
|
Name: blockId,
|
||||||
|
Metadata: metadata,
|
||||||
|
}]
|
||||||
|
return blockState, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlockStateFromItem(item Item) (blockupgrader.BlockState, bool) {
|
||||||
|
return BlockStateFromItemName(item.Name, item.Metadata)
|
||||||
|
}
|
||||||
41
internal/iterator.go
Normal file
41
internal/iterator.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
type Iterator[T any] struct {
|
||||||
|
index int
|
||||||
|
values []T
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIterator[T any](values []T) *Iterator[T] {
|
||||||
|
return &Iterator[T]{values: values}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iterator[T]) HasNext() bool {
|
||||||
|
if i.index < len(i.values) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iterator[T]) HasPrevious() bool {
|
||||||
|
if i.index > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iterator[T]) Next() T {
|
||||||
|
if i.HasNext() {
|
||||||
|
value := i.values[i.index]
|
||||||
|
i.index++
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
panic("no more elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Iterator[T]) Previous() T {
|
||||||
|
if i.HasPrevious() {
|
||||||
|
i.index--
|
||||||
|
return i.values[i.index]
|
||||||
|
}
|
||||||
|
panic("no more elements")
|
||||||
|
}
|
||||||
22
legacyver/bitset.go
Normal file
22
legacyver/bitset.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package legacyver
|
||||||
|
|
||||||
|
import "github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
|
||||||
|
func fitBitset(b protocol.Bitset, size int) protocol.Bitset {
|
||||||
|
if b.Len() != size {
|
||||||
|
ret := protocol.NewBitset(size)
|
||||||
|
|
||||||
|
copySize := b.Len()
|
||||||
|
if copySize > size {
|
||||||
|
copySize = size
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < copySize; i++ {
|
||||||
|
if b.Load(i) {
|
||||||
|
ret.Set(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
BIN
legacyver/block_states_748.nbt
Normal file
BIN
legacyver/block_states_748.nbt
Normal file
Binary file not shown.
BIN
legacyver/block_states_766.nbt
Normal file
BIN
legacyver/block_states_766.nbt
Normal file
Binary file not shown.
340
legacyver/block_translator.go
Normal file
340
legacyver/block_translator.go
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
package legacyver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"github.com/akmalfairuz/legacy-version/internal/chunk"
|
||||||
|
"github.com/akmalfairuz/legacy-version/mapping"
|
||||||
|
"github.com/df-mc/dragonfly/server/block/cube"
|
||||||
|
"github.com/df-mc/dragonfly/server/world"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/nbt"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlockVersionLatest int32 = (1 << 24) | (21 << 16) | (50 << 8) | 29
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed block_states_766.nbt
|
||||||
|
latestBlockStateData []byte
|
||||||
|
|
||||||
|
// latestBlockMapping is the BlockMapping used for translating blocks between versions.
|
||||||
|
latestBlockMapping = mapping.NewBlockMapping(latestBlockStateData)
|
||||||
|
// LatestNetworkPersistentEncoding is the Encoding used for sending a Chunk over network. It uses NBT, unlike NetworkEncoding.
|
||||||
|
LatestNetworkPersistentEncoding = chunk.NewNetworkPersistentEncoding(latestBlockMapping, BlockVersionLatest)
|
||||||
|
// LatestBlockPaletteEncoding is the paletteEncoding used for encoding a palette of block states encoded as NBT.
|
||||||
|
LatestBlockPaletteEncoding = chunk.NewBlockPaletteEncoding(latestBlockMapping, BlockVersionLatest)
|
||||||
|
)
|
||||||
|
|
||||||
|
type BlockTranslator interface {
|
||||||
|
// DowngradeBlockPackets downgrades the input block packets to legacy block packets.
|
||||||
|
DowngradeBlockPackets([]packet.Packet, *minecraft.Conn) (result []packet.Packet)
|
||||||
|
// UpgradeBlockPackets upgrades the input block packets to the latest block packets.
|
||||||
|
UpgradeBlockPackets([]packet.Packet, *minecraft.Conn) (result []packet.Packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultBlockTranslator struct {
|
||||||
|
mapping mapping.Block
|
||||||
|
latest mapping.Block
|
||||||
|
pse chunk.Encoding
|
||||||
|
pe chunk.PaletteEncoding
|
||||||
|
oldFormat bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockTranslator(mapping mapping.Block, latestMapping mapping.Block, pse chunk.Encoding, pe chunk.PaletteEncoding, oldFormat bool) *DefaultBlockTranslator {
|
||||||
|
return &DefaultBlockTranslator{mapping: mapping, latest: latestMapping, pse: pse, pe: pe, oldFormat: oldFormat}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultBlockTranslator) DowngradeBlockPackets(pks []packet.Packet, conn *minecraft.Conn) (result []packet.Packet) {
|
||||||
|
for _, pk := range pks {
|
||||||
|
switch pk := pk.(type) {
|
||||||
|
case *packet.LevelChunk:
|
||||||
|
count := int(pk.SubChunkCount)
|
||||||
|
if count == protocol.SubChunkRequestModeLimitless || count == protocol.SubChunkRequestModeLimited {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(pk.RawPayload)
|
||||||
|
writeBuf := bytes.NewBuffer(nil)
|
||||||
|
if !pk.CacheEnabled {
|
||||||
|
c, err := chunk.NetworkDecode(t.latest.Air(), buf, count, false, world.Overworld.Range(), LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
|
||||||
|
if err != nil {
|
||||||
|
//fmt.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c = t.DowngradeChunk(c)
|
||||||
|
|
||||||
|
payload, err := chunk.NetworkEncode(t.mapping.Air(), c, t.oldFormat, t.pe)
|
||||||
|
if err != nil {
|
||||||
|
//fmt.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
writeBuf.Write(payload)
|
||||||
|
pk.SubChunkCount = uint32(len(c.Sub()))
|
||||||
|
}
|
||||||
|
safeBytes := buf.Bytes()
|
||||||
|
|
||||||
|
countBorder, err := buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
pk.RawPayload = append(writeBuf.Bytes(), safeBytes...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
borderBytes := make([]byte, countBorder)
|
||||||
|
if _, err = buf.Read(borderBytes); err != nil {
|
||||||
|
pk.RawPayload = append(writeBuf.Bytes(), safeBytes...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
writeBuf.WriteByte(countBorder)
|
||||||
|
writeBuf.Write(borderBytes)
|
||||||
|
|
||||||
|
enc := nbt.NewEncoderWithEncoding(writeBuf, nbt.NetworkLittleEndian)
|
||||||
|
dec := nbt.NewDecoderWithEncoding(buf, nbt.NetworkLittleEndian)
|
||||||
|
for {
|
||||||
|
var decNbt map[string]any
|
||||||
|
if err = dec.Decode(&decNbt); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.mapping.DowngradeBlockActorData(decNbt)
|
||||||
|
|
||||||
|
if err = enc.Encode(decNbt); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pk.RawPayload = append(writeBuf.Bytes(), buf.Bytes()...)
|
||||||
|
case *packet.SubChunk:
|
||||||
|
r := world.Overworld.Range()
|
||||||
|
if t.oldFormat {
|
||||||
|
r = cube.Range{0, 255}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, entry := range pk.SubChunkEntries {
|
||||||
|
if entry.Result == protocol.SubChunkResultSuccess {
|
||||||
|
buf := bytes.NewBuffer(entry.RawPayload)
|
||||||
|
writeBuf := bytes.NewBuffer(nil)
|
||||||
|
if !pk.CacheEnabled && !conn.ClientCacheEnabled() {
|
||||||
|
ind := byte(i)
|
||||||
|
subChunk, err := chunk.DecodeSubChunk(t.latest.Air(), r, buf, &ind, chunk.NetworkEncoding, LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
|
||||||
|
if err != nil {
|
||||||
|
//fmt.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.DowngradeSubChunk(subChunk)
|
||||||
|
writeBuf.Write(chunk.EncodeSubChunk(subChunk, chunk.NetworkEncoding, t.pe, chunk.SubChunkVersion9, r, int(ind)))
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := nbt.NewEncoderWithEncoding(writeBuf, nbt.NetworkLittleEndian)
|
||||||
|
dec := nbt.NewDecoderWithEncoding(buf, nbt.NetworkLittleEndian)
|
||||||
|
for {
|
||||||
|
var decNbt map[string]any
|
||||||
|
if err := dec.Decode(&decNbt); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
t.mapping.DowngradeBlockActorData(decNbt)
|
||||||
|
|
||||||
|
if err := enc.Encode(decNbt); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.RawPayload = append(writeBuf.Bytes(), buf.Bytes()...)
|
||||||
|
pk.SubChunkEntries[i] = entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *packet.ClientCacheMissResponse:
|
||||||
|
r := world.Overworld.Range()
|
||||||
|
if t.oldFormat {
|
||||||
|
r = cube.Range{0, 255}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, blob := range pk.Blobs {
|
||||||
|
buf := bytes.NewBuffer(blob.Payload)
|
||||||
|
ind := byte(0)
|
||||||
|
subChunk, err := chunk.DecodeSubChunk(t.latest.Air(), r, buf, &ind, chunk.NetworkEncoding, LatestNetworkPersistentEncoding, LatestBlockPaletteEncoding)
|
||||||
|
if err != nil {
|
||||||
|
// Has a possibility to be a biome, ignore then
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.DowngradeSubChunk(subChunk)
|
||||||
|
|
||||||
|
blob.Payload = append(chunk.EncodeSubChunk(subChunk, chunk.NetworkEncoding, t.pe, chunk.SubChunkVersion9, r, int(ind)), buf.Bytes()...)
|
||||||
|
pk.Blobs[i] = blob
|
||||||
|
}
|
||||||
|
case *packet.UpdateSubChunkBlocks:
|
||||||
|
for i, block := range pk.Blocks {
|
||||||
|
block.BlockRuntimeID = t.DowngradeBlockRuntimeID(block.BlockRuntimeID)
|
||||||
|
pk.Blocks[i] = block
|
||||||
|
}
|
||||||
|
for i, block := range pk.Extra {
|
||||||
|
block.BlockRuntimeID = t.DowngradeBlockRuntimeID(block.BlockRuntimeID)
|
||||||
|
pk.Extra[i] = block
|
||||||
|
}
|
||||||
|
case *packet.UpdateBlock:
|
||||||
|
pk.NewBlockRuntimeID = t.DowngradeBlockRuntimeID(pk.NewBlockRuntimeID)
|
||||||
|
case *packet.UpdateBlockSynced:
|
||||||
|
pk.NewBlockRuntimeID = t.DowngradeBlockRuntimeID(pk.NewBlockRuntimeID)
|
||||||
|
case *packet.InventoryTransaction:
|
||||||
|
if transactionData, ok := pk.TransactionData.(*protocol.UseItemTransactionData); ok {
|
||||||
|
transactionData.BlockRuntimeID = t.DowngradeBlockRuntimeID(transactionData.BlockRuntimeID)
|
||||||
|
pk.TransactionData = transactionData
|
||||||
|
}
|
||||||
|
case *packet.LevelEvent:
|
||||||
|
switch pk.EventType {
|
||||||
|
case packet.LevelEventParticleLegacyEvent | 20: // terrain
|
||||||
|
fallthrough
|
||||||
|
case packet.LevelEventParticlesDestroyBlock:
|
||||||
|
fallthrough
|
||||||
|
case packet.LevelEventParticlesDestroyBlockNoSound:
|
||||||
|
pk.EventData = int32(t.DowngradeBlockRuntimeID(uint32(pk.EventData)))
|
||||||
|
case packet.LevelEventParticlesCrackBlock:
|
||||||
|
face := pk.EventData >> 24
|
||||||
|
rid := t.DowngradeBlockRuntimeID(uint32(pk.EventData & 0xffff))
|
||||||
|
pk.EventData = int32(rid) | (face << 24)
|
||||||
|
}
|
||||||
|
case *packet.LevelSoundEvent:
|
||||||
|
switch pk.SoundType {
|
||||||
|
case packet.SoundEventBreak:
|
||||||
|
fallthrough
|
||||||
|
case packet.SoundEventPlace:
|
||||||
|
fallthrough
|
||||||
|
case packet.SoundEventHit:
|
||||||
|
fallthrough
|
||||||
|
case packet.SoundEventLand:
|
||||||
|
fallthrough
|
||||||
|
case packet.SoundEventItemUseOn:
|
||||||
|
pk.ExtraData = int32(t.DowngradeBlockRuntimeID(uint32(pk.ExtraData)))
|
||||||
|
}
|
||||||
|
case *packet.AddActor:
|
||||||
|
if pk.EntityType == "minecraft:falling_block" {
|
||||||
|
pk.EntityMetadata = t.downgradeEntityMetadata(pk.EntityMetadata)
|
||||||
|
}
|
||||||
|
case *packet.SetActorData:
|
||||||
|
pk.EntityMetadata = t.downgradeEntityMetadata(pk.EntityMetadata)
|
||||||
|
case *packet.StartGame:
|
||||||
|
t.latest.Adjust(pk.Blocks)
|
||||||
|
t.mapping.Adjust(pk.Blocks)
|
||||||
|
case *packet.ResourcePackStack:
|
||||||
|
var packs []protocol.StackResourcePack
|
||||||
|
for _, pack := range pk.TexturePacks {
|
||||||
|
if pack.UUID == "0fba4063-dba1-4281-9b89-ff9390653530" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
packs = append(packs, pack)
|
||||||
|
}
|
||||||
|
pk.TexturePacks = packs
|
||||||
|
}
|
||||||
|
result = append(result, pk)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultBlockTranslator) UpgradeBlockPackets(pks []packet.Packet, conn *minecraft.Conn) (result []packet.Packet) {
|
||||||
|
for _, pk := range pks {
|
||||||
|
switch pk := pk.(type) {
|
||||||
|
case *packet.InventoryTransaction:
|
||||||
|
if transactionData, ok := pk.TransactionData.(*protocol.UseItemTransactionData); ok {
|
||||||
|
transactionData.BlockRuntimeID = t.UpgradeBlockRuntimeID(transactionData.BlockRuntimeID)
|
||||||
|
pk.TransactionData = transactionData
|
||||||
|
}
|
||||||
|
case *packet.SetActorData:
|
||||||
|
pk.EntityMetadata = t.upgradeEntityMetadata(pk.EntityMetadata)
|
||||||
|
}
|
||||||
|
result = append(result, pk)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultBlockTranslator) DowngradeBlockRuntimeID(input uint32) uint32 {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
state, ok := t.latest.RuntimeIDToState(input)
|
||||||
|
if !ok {
|
||||||
|
return t.mapping.Air()
|
||||||
|
}
|
||||||
|
runtimeID, ok := t.mapping.StateToRuntimeID(state)
|
||||||
|
if !ok {
|
||||||
|
return t.mapping.Air()
|
||||||
|
}
|
||||||
|
return runtimeID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultBlockTranslator) DowngradeChunk(input *chunk.Chunk) *chunk.Chunk {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
start := 0
|
||||||
|
r := world.Overworld.Range()
|
||||||
|
if t.oldFormat {
|
||||||
|
start = 4
|
||||||
|
r = cube.Range{0, 255}
|
||||||
|
}
|
||||||
|
downgraded := chunk.New(t.mapping.Air(), r)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
// First downgrade the blocks.
|
||||||
|
for _, sub := range input.Sub()[start : len(input.Sub())-start] {
|
||||||
|
t.DowngradeSubChunk(sub)
|
||||||
|
downgraded.Sub()[i] = sub
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
i = 0
|
||||||
|
// Then downgrade the biome ids.
|
||||||
|
for _, sub := range input.BiomeSub()[start : len(input.BiomeSub())-start] {
|
||||||
|
// todo
|
||||||
|
sub.Palette().Replace(func(v uint32) uint32 {
|
||||||
|
return 0 // at least the client doesn't crash now
|
||||||
|
})
|
||||||
|
downgraded.BiomeSub()[i] = sub
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return downgraded
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultBlockTranslator) DowngradeSubChunk(input *chunk.SubChunk) {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, storage := range input.Layers() {
|
||||||
|
storage.Palette().Replace(t.DowngradeBlockRuntimeID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultBlockTranslator) downgradeEntityMetadata(metadata map[uint32]any) map[uint32]any {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
if latestRID, ok := metadata[protocol.EntityDataKeyVariant]; ok {
|
||||||
|
metadata[protocol.EntityDataKeyVariant] = int32(t.DowngradeBlockRuntimeID(uint32(latestRID.(int32))))
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultBlockTranslator) UpgradeBlockRuntimeID(input uint32) uint32 {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
state, ok := t.mapping.RuntimeIDToState(input)
|
||||||
|
if !ok {
|
||||||
|
return t.latest.Air()
|
||||||
|
}
|
||||||
|
runtimeID, ok := t.latest.StateToRuntimeID(state)
|
||||||
|
if !ok {
|
||||||
|
return t.latest.Air()
|
||||||
|
}
|
||||||
|
return runtimeID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultBlockTranslator) upgradeEntityMetadata(metadata map[uint32]any) map[uint32]any {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
if latestRID, ok := metadata[protocol.EntityDataKeyVariant]; ok {
|
||||||
|
metadata[protocol.EntityDataKeyVariant] = int32(t.UpgradeBlockRuntimeID(uint32(latestRID.(int32))))
|
||||||
|
}
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
BIN
legacyver/item_runtime_ids_748.nbt
Normal file
BIN
legacyver/item_runtime_ids_748.nbt
Normal file
Binary file not shown.
BIN
legacyver/item_runtime_ids_766.nbt
Normal file
BIN
legacyver/item_runtime_ids_766.nbt
Normal file
Binary file not shown.
767
legacyver/item_translator.go
Normal file
767
legacyver/item_translator.go
Normal file
@@ -0,0 +1,767 @@
|
|||||||
|
package legacyver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/akmalfairuz/legacy-version/internal/item"
|
||||||
|
"github.com/akmalfairuz/legacy-version/mapping"
|
||||||
|
"github.com/akmalfairuz/legacy-version/packbuilder"
|
||||||
|
"github.com/df-mc/dragonfly/server/world"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ItemTranslator interface {
|
||||||
|
// DowngradeItemType downgrades the input item type to a legacy item type.
|
||||||
|
DowngradeItemType(input protocol.ItemType) protocol.ItemType
|
||||||
|
// DowngradeItemStack downgrades the input item stack to a legacy item stack.
|
||||||
|
DowngradeItemStack(input protocol.ItemStack) protocol.ItemStack
|
||||||
|
// DowngradeItemInstance downgrades the input item instance to a legacy item instance.
|
||||||
|
DowngradeItemInstance(input protocol.ItemInstance) protocol.ItemInstance
|
||||||
|
// DowngradeItemDescriptor downgrades the input item descriptor to a legacy item descriptor.
|
||||||
|
DowngradeItemDescriptor(input protocol.ItemDescriptor) protocol.ItemDescriptor
|
||||||
|
// DowngradeItemDescriptorCount downgrades the input item descriptor (with count) to a legacy item descriptor (with count).
|
||||||
|
DowngradeItemDescriptorCount(input protocol.ItemDescriptorCount) protocol.ItemDescriptorCount
|
||||||
|
DowngradeItemPackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet
|
||||||
|
// UpgradeItemType upgrades the input item type to the latest item type.
|
||||||
|
UpgradeItemType(input protocol.ItemType) protocol.ItemType
|
||||||
|
// UpgradeItemStack upgrades the input item stack to the latest item stack.
|
||||||
|
UpgradeItemStack(input protocol.ItemStack) protocol.ItemStack
|
||||||
|
// UpgradeItemInstance upgrades the input item instance to the latest item instance.
|
||||||
|
UpgradeItemInstance(input protocol.ItemInstance) protocol.ItemInstance
|
||||||
|
// UpgradeItemDescriptor upgrades the input item descriptor to the latest item descriptor.
|
||||||
|
UpgradeItemDescriptor(input protocol.ItemDescriptor) protocol.ItemDescriptor
|
||||||
|
// UpgradeItemDescriptorCount upgrades the input item descriptor (with count) to the latest item descriptor (with count).
|
||||||
|
UpgradeItemDescriptorCount(input protocol.ItemDescriptorCount) protocol.ItemDescriptorCount
|
||||||
|
UpgradeItemPackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet
|
||||||
|
// Register registers a custom item entry.
|
||||||
|
Register(item world.CustomItem, replacement string)
|
||||||
|
// CustomItems lists all custom items used as substitutes, with the runtime id as the key
|
||||||
|
CustomItems() map[int32]world.CustomItem
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultItemTranslator struct {
|
||||||
|
mapping mapping.Item
|
||||||
|
latest mapping.Item
|
||||||
|
blockMapping mapping.Block
|
||||||
|
blockMappingLatest mapping.Block
|
||||||
|
ridToCustomItem map[int32]world.CustomItem
|
||||||
|
originalToCustom map[int32]int32
|
||||||
|
customToOriginal map[int32]int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewItemTranslator(mapping mapping.Item, latestMapping mapping.Item, blockMapping mapping.Block, blockMappingLatest mapping.Block) *DefaultItemTranslator {
|
||||||
|
return &DefaultItemTranslator{mapping: mapping, latest: latestMapping, blockMapping: blockMapping, blockMappingLatest: blockMappingLatest,
|
||||||
|
ridToCustomItem: make(map[int32]world.CustomItem), originalToCustom: make(map[int32]int32), customToOriginal: make(map[int32]int32)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) DowngradeItemType(input protocol.ItemType) protocol.ItemType {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
if input.NetworkID == t.latest.Air() || input.NetworkID == 0 {
|
||||||
|
return protocol.ItemType{
|
||||||
|
NetworkID: t.mapping.Air(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
networkID := input.NetworkID
|
||||||
|
metadata := input.MetadataValue
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
if networkID, ok = t.originalToCustom[input.NetworkID]; !ok {
|
||||||
|
name, _ := t.latest.ItemRuntimeIDToName(input.NetworkID)
|
||||||
|
i := item.Downgrade(item.Item{
|
||||||
|
Name: name,
|
||||||
|
Metadata: input.MetadataValue,
|
||||||
|
Version: t.latest.ItemVersion(),
|
||||||
|
}, t.mapping.ItemVersion())
|
||||||
|
metadata = i.Metadata
|
||||||
|
|
||||||
|
if i.Name == "minecraft:netherstar" {
|
||||||
|
i.Name = "minecraft:nether_star"
|
||||||
|
}
|
||||||
|
|
||||||
|
networkID, ok = t.mapping.ItemNameToRuntimeID(i.Name)
|
||||||
|
if !ok {
|
||||||
|
networkID, _ = t.mapping.ItemNameToRuntimeID("minecraft:info_update")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol.ItemType{
|
||||||
|
NetworkID: networkID,
|
||||||
|
MetadataValue: metadata,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) DowngradeItemStack(input protocol.ItemStack) protocol.ItemStack {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
input.ItemType = t.DowngradeItemType(input.ItemType)
|
||||||
|
|
||||||
|
blockRuntimeId := uint32(0)
|
||||||
|
if input.NetworkID != t.mapping.Air() {
|
||||||
|
name, _ := t.mapping.ItemRuntimeIDToName(input.NetworkID)
|
||||||
|
if latestBlockState, ok := item.BlockStateFromItemName(name, input.MetadataValue); ok {
|
||||||
|
var found bool
|
||||||
|
if blockRuntimeId, found = t.blockMapping.StateToRuntimeID(latestBlockState); !found {
|
||||||
|
blockRuntimeId = t.blockMapping.Air()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return protocol.ItemStack{
|
||||||
|
ItemType: input.ItemType,
|
||||||
|
BlockRuntimeID: int32(blockRuntimeId),
|
||||||
|
Count: input.Count,
|
||||||
|
NBTData: input.NBTData,
|
||||||
|
CanBePlacedOn: input.CanBePlacedOn,
|
||||||
|
CanBreak: input.CanBreak,
|
||||||
|
HasNetworkID: input.HasNetworkID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) DowngradeItemInstance(input protocol.ItemInstance) protocol.ItemInstance {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
input.Stack = t.DowngradeItemStack(input.Stack)
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) DowngradeItemDescriptor(input protocol.ItemDescriptor) protocol.ItemDescriptor {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
switch descriptor := input.(type) {
|
||||||
|
case *protocol.InvalidItemDescriptor:
|
||||||
|
return input
|
||||||
|
case *protocol.DefaultItemDescriptor:
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: int32(descriptor.NetworkID), MetadataValue: uint32(descriptor.MetadataValue)})
|
||||||
|
descriptor.NetworkID, descriptor.MetadataValue = int16(itemType.NetworkID), int16(itemType.MetadataValue)
|
||||||
|
return descriptor
|
||||||
|
case *protocol.MoLangItemDescriptor:
|
||||||
|
return input
|
||||||
|
case *protocol.ItemTagItemDescriptor:
|
||||||
|
return input
|
||||||
|
case *protocol.DeferredItemDescriptor:
|
||||||
|
rid, ok := t.latest.ItemNameToRuntimeID(descriptor.Name)
|
||||||
|
descriptor.Name = "minecraft:air"
|
||||||
|
if !ok {
|
||||||
|
descriptor.MetadataValue = 0
|
||||||
|
return descriptor
|
||||||
|
}
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: rid, MetadataValue: uint32(descriptor.MetadataValue)})
|
||||||
|
descriptor.MetadataValue = int16(itemType.MetadataValue)
|
||||||
|
if name, ok := t.mapping.ItemRuntimeIDToName(itemType.NetworkID); ok {
|
||||||
|
descriptor.Name = name
|
||||||
|
}
|
||||||
|
return descriptor
|
||||||
|
case *protocol.ComplexAliasItemDescriptor:
|
||||||
|
rid, ok := t.latest.ItemNameToRuntimeID(descriptor.Name)
|
||||||
|
descriptor.Name = "minecraft:air"
|
||||||
|
if !ok {
|
||||||
|
return descriptor
|
||||||
|
}
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: rid})
|
||||||
|
if name, ok := t.mapping.ItemRuntimeIDToName(itemType.NetworkID); ok {
|
||||||
|
descriptor.Name = name
|
||||||
|
}
|
||||||
|
return descriptor
|
||||||
|
}
|
||||||
|
panic("unknown item descriptor")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) DowngradeItemDescriptorCount(input protocol.ItemDescriptorCount) protocol.ItemDescriptorCount {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
input.Descriptor = t.DowngradeItemDescriptor(input.Descriptor)
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) UpgradeItemType(input protocol.ItemType) protocol.ItemType {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
if input.NetworkID == t.mapping.Air() || input.NetworkID == 0 {
|
||||||
|
return protocol.ItemType{
|
||||||
|
NetworkID: t.latest.Air(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
networkID := input.NetworkID
|
||||||
|
metadata := input.MetadataValue
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
if networkID, ok = t.customToOriginal[input.NetworkID]; !ok {
|
||||||
|
name, _ := t.mapping.ItemRuntimeIDToName(input.NetworkID)
|
||||||
|
i := item.Upgrade(item.Item{
|
||||||
|
Name: name,
|
||||||
|
Metadata: input.MetadataValue,
|
||||||
|
Version: t.mapping.ItemVersion(),
|
||||||
|
}, t.latest.ItemVersion())
|
||||||
|
networkID, ok = t.latest.ItemNameToRuntimeID(i.Name)
|
||||||
|
if !ok {
|
||||||
|
networkID, _ = t.latest.ItemNameToRuntimeID("minecraft:info_update")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol.ItemType{
|
||||||
|
NetworkID: networkID,
|
||||||
|
MetadataValue: metadata,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) UpgradeItemStack(input protocol.ItemStack) protocol.ItemStack {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
input.ItemType = t.UpgradeItemType(input.ItemType)
|
||||||
|
|
||||||
|
blockRuntimeId := uint32(0)
|
||||||
|
if input.NetworkID != t.latest.Air() {
|
||||||
|
name, _ := t.latest.ItemRuntimeIDToName(input.NetworkID)
|
||||||
|
if latestBlockState, ok := item.BlockStateFromItemName(name, input.MetadataValue); ok {
|
||||||
|
blockRuntimeId, _ = t.blockMappingLatest.StateToRuntimeID(latestBlockState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return protocol.ItemStack{
|
||||||
|
ItemType: input.ItemType,
|
||||||
|
BlockRuntimeID: int32(blockRuntimeId),
|
||||||
|
Count: input.Count,
|
||||||
|
NBTData: input.NBTData,
|
||||||
|
CanBePlacedOn: input.CanBePlacedOn,
|
||||||
|
CanBreak: input.CanBreak,
|
||||||
|
HasNetworkID: input.HasNetworkID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) UpgradeItemInstance(input protocol.ItemInstance) protocol.ItemInstance {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
input.Stack = t.UpgradeItemStack(input.Stack)
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) UpgradeItemDescriptor(input protocol.ItemDescriptor) protocol.ItemDescriptor {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
switch descriptor := input.(type) {
|
||||||
|
case *protocol.InvalidItemDescriptor:
|
||||||
|
return input
|
||||||
|
case *protocol.DefaultItemDescriptor:
|
||||||
|
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: int32(descriptor.NetworkID), MetadataValue: uint32(descriptor.MetadataValue)})
|
||||||
|
descriptor.NetworkID, descriptor.MetadataValue = int16(itemType.NetworkID), int16(itemType.MetadataValue)
|
||||||
|
return descriptor
|
||||||
|
case *protocol.MoLangItemDescriptor:
|
||||||
|
return input
|
||||||
|
case *protocol.ItemTagItemDescriptor:
|
||||||
|
return input
|
||||||
|
case *protocol.DeferredItemDescriptor:
|
||||||
|
rid, ok := t.mapping.ItemNameToRuntimeID(descriptor.Name)
|
||||||
|
descriptor.Name = "minecraft:air"
|
||||||
|
if !ok {
|
||||||
|
descriptor.MetadataValue = 0
|
||||||
|
return descriptor
|
||||||
|
}
|
||||||
|
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: rid, MetadataValue: uint32(descriptor.MetadataValue)})
|
||||||
|
descriptor.MetadataValue = int16(itemType.MetadataValue)
|
||||||
|
if name, ok := t.latest.ItemRuntimeIDToName(itemType.NetworkID); ok {
|
||||||
|
descriptor.Name = name
|
||||||
|
}
|
||||||
|
return descriptor
|
||||||
|
case *protocol.ComplexAliasItemDescriptor:
|
||||||
|
rid, ok := t.mapping.ItemNameToRuntimeID(descriptor.Name)
|
||||||
|
descriptor.Name = "minecraft:air"
|
||||||
|
if !ok {
|
||||||
|
return descriptor
|
||||||
|
}
|
||||||
|
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: rid})
|
||||||
|
if name, ok := t.latest.ItemRuntimeIDToName(itemType.NetworkID); ok {
|
||||||
|
descriptor.Name = name
|
||||||
|
}
|
||||||
|
return descriptor
|
||||||
|
}
|
||||||
|
panic("unknown item descriptor")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) UpgradeItemDescriptorCount(input protocol.ItemDescriptorCount) protocol.ItemDescriptorCount {
|
||||||
|
if t.latest == t.mapping {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
input.Descriptor = t.UpgradeItemDescriptor(input.Descriptor)
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) DowngradeItemPackets(pks []packet.Packet, _ *minecraft.Conn) (result []packet.Packet) {
|
||||||
|
for _, pk := range pks {
|
||||||
|
switch pk := pk.(type) {
|
||||||
|
case *packet.MobEquipment:
|
||||||
|
pk.NewItem = t.DowngradeItemInstance(pk.NewItem)
|
||||||
|
case *packet.MobArmourEquipment:
|
||||||
|
pk.Helmet = t.DowngradeItemInstance(pk.Helmet)
|
||||||
|
pk.Chestplate = t.DowngradeItemInstance(pk.Chestplate)
|
||||||
|
pk.Leggings = t.DowngradeItemInstance(pk.Leggings)
|
||||||
|
pk.Boots = t.DowngradeItemInstance(pk.Boots)
|
||||||
|
pk.Body = t.DowngradeItemInstance(pk.Body)
|
||||||
|
case *packet.ActorEvent:
|
||||||
|
if pk.EventType == packet.ActorEventFeed {
|
||||||
|
value := pk.EventData
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: value >> 16, MetadataValue: uint32(value & 0xf)})
|
||||||
|
pk.EventData = (itemType.NetworkID << 16) | int32(itemType.MetadataValue)
|
||||||
|
}
|
||||||
|
case *packet.AddItemActor:
|
||||||
|
pk.Item = t.DowngradeItemInstance(pk.Item)
|
||||||
|
case *packet.AddPlayer:
|
||||||
|
pk.HeldItem = t.DowngradeItemInstance(pk.HeldItem)
|
||||||
|
case *packet.InventorySlot:
|
||||||
|
pk.NewItem = t.DowngradeItemInstance(pk.NewItem)
|
||||||
|
case *packet.InventoryContent:
|
||||||
|
pk.Content = lo.Map(pk.Content, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
|
||||||
|
return t.DowngradeItemInstance(item)
|
||||||
|
})
|
||||||
|
case *packet.ItemStackRequest:
|
||||||
|
for i, request := range pk.Requests {
|
||||||
|
for i2, action := range request.Actions {
|
||||||
|
if act, ok := action.(*protocol.CraftResultsDeprecatedStackRequestAction); ok {
|
||||||
|
act.ResultItems = lo.Map(act.ResultItems, func(item protocol.ItemStack, _ int) protocol.ItemStack {
|
||||||
|
return t.DowngradeItemStack(item)
|
||||||
|
})
|
||||||
|
pk.Requests[i].Actions[i2] = act
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *packet.CraftingData:
|
||||||
|
for i, recipe := range pk.Recipes {
|
||||||
|
switch recipe := recipe.(type) {
|
||||||
|
case *protocol.ShapelessRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.DowngradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.ShapedRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.DowngradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.FurnaceRecipe:
|
||||||
|
recipe.InputType = t.DowngradeItemType(recipe.InputType)
|
||||||
|
recipe.Output = t.DowngradeItemStack(recipe.Output)
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.FurnaceDataRecipe:
|
||||||
|
recipe.InputType = t.DowngradeItemType(recipe.InputType)
|
||||||
|
recipe.Output = t.DowngradeItemStack(recipe.Output)
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.ShulkerBoxRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.DowngradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.ShapelessChemistryRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.DowngradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.ShapedChemistryRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.DowngradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.DowngradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.SmithingTransformRecipe:
|
||||||
|
recipe.Template = t.DowngradeItemDescriptorCount(recipe.Template)
|
||||||
|
recipe.Base = t.DowngradeItemDescriptorCount(recipe.Base)
|
||||||
|
recipe.Addition = t.DowngradeItemDescriptorCount(recipe.Addition)
|
||||||
|
recipe.Result = t.DowngradeItemStack(recipe.Result)
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.SmithingTrimRecipe:
|
||||||
|
recipe.Template = t.DowngradeItemDescriptorCount(recipe.Template)
|
||||||
|
recipe.Base = t.DowngradeItemDescriptorCount(recipe.Base)
|
||||||
|
recipe.Addition = t.DowngradeItemDescriptorCount(recipe.Addition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, recipe := range pk.PotionRecipes {
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.InputPotionID, MetadataValue: uint32(recipe.InputPotionMetadata)})
|
||||||
|
recipe.InputPotionID, recipe.InputPotionMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
|
||||||
|
itemType = t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.ReagentItemID, MetadataValue: uint32(recipe.ReagentItemMetadata)})
|
||||||
|
recipe.ReagentItemID, recipe.ReagentItemMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
|
||||||
|
itemType = t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.OutputPotionID, MetadataValue: uint32(recipe.OutputPotionMetadata)})
|
||||||
|
recipe.OutputPotionID, recipe.OutputPotionMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
|
||||||
|
pk.PotionRecipes[i] = recipe
|
||||||
|
}
|
||||||
|
for i, recipe := range pk.PotionContainerChangeRecipes {
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.InputItemID})
|
||||||
|
recipe.InputItemID = itemType.NetworkID
|
||||||
|
itemType = t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.ReagentItemID})
|
||||||
|
recipe.ReagentItemID = itemType.NetworkID
|
||||||
|
itemType = t.DowngradeItemType(protocol.ItemType{NetworkID: recipe.OutputItemID})
|
||||||
|
recipe.OutputItemID = itemType.NetworkID
|
||||||
|
pk.PotionContainerChangeRecipes[i] = recipe
|
||||||
|
}
|
||||||
|
for i, recipe := range pk.MaterialReducers {
|
||||||
|
recipe.InputItem = t.DowngradeItemType(recipe.InputItem)
|
||||||
|
for i2, output := range recipe.Outputs {
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{NetworkID: output.NetworkID})
|
||||||
|
output.NetworkID = itemType.NetworkID
|
||||||
|
recipe.Outputs[i2] = output
|
||||||
|
}
|
||||||
|
pk.MaterialReducers[i] = recipe
|
||||||
|
}
|
||||||
|
//case *packet.CraftingEvent:
|
||||||
|
// pk.Input = lo.Map(pk.Input, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
|
||||||
|
// return t.DowngradeItemInstance(item)
|
||||||
|
// })
|
||||||
|
// pk.Output = lo.Map(pk.Output, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
|
||||||
|
// return t.DowngradeItemInstance(item)
|
||||||
|
// })
|
||||||
|
case *packet.PlayerAuthInput:
|
||||||
|
for i, action := range pk.ItemStackRequest.Actions {
|
||||||
|
if act, ok := action.(*protocol.CraftResultsDeprecatedStackRequestAction); ok {
|
||||||
|
act.ResultItems = lo.Map(act.ResultItems, func(item protocol.ItemStack, _ int) protocol.ItemStack {
|
||||||
|
return t.DowngradeItemStack(item)
|
||||||
|
})
|
||||||
|
pk.ItemStackRequest.Actions[i] = act
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, action := range pk.ItemInteractionData.Actions {
|
||||||
|
action.OldItem = t.DowngradeItemInstance(action.OldItem)
|
||||||
|
action.NewItem = t.DowngradeItemInstance(action.NewItem)
|
||||||
|
pk.ItemInteractionData.Actions[i] = action
|
||||||
|
}
|
||||||
|
pk.ItemInteractionData.HeldItem = t.DowngradeItemInstance(pk.ItemInteractionData.HeldItem)
|
||||||
|
case *packet.CreativeContent:
|
||||||
|
for i, creativeItem := range pk.Items {
|
||||||
|
creativeItem.Item = t.DowngradeItemStack(creativeItem.Item)
|
||||||
|
|
||||||
|
pk.Items[i] = creativeItem
|
||||||
|
}
|
||||||
|
case *packet.InventoryTransaction:
|
||||||
|
for i, action := range pk.Actions {
|
||||||
|
action.OldItem = t.DowngradeItemInstance(action.OldItem)
|
||||||
|
action.NewItem = t.DowngradeItemInstance(action.NewItem)
|
||||||
|
pk.Actions[i] = action
|
||||||
|
}
|
||||||
|
switch transactionData := pk.TransactionData.(type) {
|
||||||
|
case *protocol.UseItemTransactionData:
|
||||||
|
transactionData.HeldItem = t.DowngradeItemInstance(transactionData.HeldItem)
|
||||||
|
for i, action := range transactionData.Actions {
|
||||||
|
action.OldItem = t.DowngradeItemInstance(action.OldItem)
|
||||||
|
action.NewItem = t.DowngradeItemInstance(action.NewItem)
|
||||||
|
transactionData.Actions[i] = action
|
||||||
|
}
|
||||||
|
case *protocol.UseItemOnEntityTransactionData:
|
||||||
|
transactionData.HeldItem = t.DowngradeItemInstance(transactionData.HeldItem)
|
||||||
|
case *protocol.ReleaseItemTransactionData:
|
||||||
|
transactionData.HeldItem = t.DowngradeItemInstance(transactionData.HeldItem)
|
||||||
|
}
|
||||||
|
case *packet.LevelEvent:
|
||||||
|
if pk.EventType == packet.LevelEventParticleLegacyEvent|14 { // egg crack
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{
|
||||||
|
NetworkID: pk.EventData >> 16,
|
||||||
|
MetadataValue: uint32(pk.EventData & 0xf),
|
||||||
|
})
|
||||||
|
pk.EventData = (itemType.NetworkID << 16) | int32(itemType.MetadataValue)
|
||||||
|
}
|
||||||
|
case *packet.StartGame:
|
||||||
|
for i, entry := range pk.Items {
|
||||||
|
if !entry.ComponentBased {
|
||||||
|
itemType := t.DowngradeItemType(protocol.ItemType{
|
||||||
|
NetworkID: int32(entry.RuntimeID),
|
||||||
|
MetadataValue: 0,
|
||||||
|
})
|
||||||
|
if itemType.NetworkID == t.mapping.Air() {
|
||||||
|
removeIndex(pk.Items, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entry.RuntimeID = int16(itemType.NetworkID)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
if entry.Name, ok = t.mapping.ItemRuntimeIDToName(itemType.NetworkID); !ok {
|
||||||
|
panic(itemType)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.latest.RegisterEntry(entry.Name)
|
||||||
|
entry.RuntimeID = int16(t.mapping.RegisterEntry(entry.Name))
|
||||||
|
}
|
||||||
|
pk.Items[i] = entry
|
||||||
|
}
|
||||||
|
for rid, i := range t.CustomItems() {
|
||||||
|
name, _ := i.EncodeItem()
|
||||||
|
pk.Items = append(pk.Items, protocol.ItemEntry{
|
||||||
|
Name: name,
|
||||||
|
RuntimeID: int16(rid),
|
||||||
|
ComponentBased: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case *packet.ItemComponent:
|
||||||
|
for _, i := range t.CustomItems() {
|
||||||
|
name, _ := i.EncodeItem()
|
||||||
|
pk.Items = append(pk.Items, protocol.ItemComponentEntry{
|
||||||
|
Name: name,
|
||||||
|
Data: packbuilder.Components(i),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, pk)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) UpgradeItemPackets(pks []packet.Packet, _ *minecraft.Conn) (result []packet.Packet) {
|
||||||
|
for _, pk := range pks {
|
||||||
|
switch pk := pk.(type) {
|
||||||
|
case *packet.MobEquipment:
|
||||||
|
pk.NewItem = t.UpgradeItemInstance(pk.NewItem)
|
||||||
|
case *packet.MobArmourEquipment:
|
||||||
|
pk.Helmet = t.UpgradeItemInstance(pk.Helmet)
|
||||||
|
pk.Chestplate = t.UpgradeItemInstance(pk.Chestplate)
|
||||||
|
pk.Leggings = t.UpgradeItemInstance(pk.Leggings)
|
||||||
|
pk.Boots = t.UpgradeItemInstance(pk.Boots)
|
||||||
|
pk.Body = t.UpgradeItemInstance(pk.Body)
|
||||||
|
case *packet.AddItemActor:
|
||||||
|
pk.Item = t.UpgradeItemInstance(pk.Item)
|
||||||
|
case *packet.AddPlayer:
|
||||||
|
pk.HeldItem = t.UpgradeItemInstance(pk.HeldItem)
|
||||||
|
case *packet.InventorySlot:
|
||||||
|
pk.NewItem = t.UpgradeItemInstance(pk.NewItem)
|
||||||
|
case *packet.InventoryContent:
|
||||||
|
pk.Content = lo.Map(pk.Content, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
|
||||||
|
return t.UpgradeItemInstance(item)
|
||||||
|
})
|
||||||
|
case *packet.ItemStackRequest:
|
||||||
|
for i, request := range pk.Requests {
|
||||||
|
for i2, action := range request.Actions {
|
||||||
|
if act, ok := action.(*protocol.CraftResultsDeprecatedStackRequestAction); ok {
|
||||||
|
act.ResultItems = lo.Map(act.ResultItems, func(item protocol.ItemStack, _ int) protocol.ItemStack {
|
||||||
|
return t.UpgradeItemStack(item)
|
||||||
|
})
|
||||||
|
pk.Requests[i].Actions[i2] = act
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *packet.CraftingData:
|
||||||
|
for i, recipe := range pk.Recipes {
|
||||||
|
switch recipe := recipe.(type) {
|
||||||
|
case *protocol.ShapelessRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.UpgradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.ShapedRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.UpgradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.FurnaceRecipe:
|
||||||
|
recipe.InputType = t.UpgradeItemType(recipe.InputType)
|
||||||
|
recipe.Output = t.UpgradeItemStack(recipe.Output)
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.FurnaceDataRecipe:
|
||||||
|
recipe.InputType = t.UpgradeItemType(recipe.InputType)
|
||||||
|
recipe.Output = t.UpgradeItemStack(recipe.Output)
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.ShulkerBoxRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.UpgradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.ShapelessChemistryRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.UpgradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.ShapedChemistryRecipe:
|
||||||
|
for i2, input := range recipe.Input {
|
||||||
|
recipe.Input[i2] = t.UpgradeItemDescriptorCount(input)
|
||||||
|
}
|
||||||
|
for i2, stack := range recipe.Output {
|
||||||
|
recipe.Output[i2] = t.UpgradeItemStack(stack)
|
||||||
|
}
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.SmithingTransformRecipe:
|
||||||
|
recipe.Template = t.UpgradeItemDescriptorCount(recipe.Template)
|
||||||
|
recipe.Base = t.UpgradeItemDescriptorCount(recipe.Base)
|
||||||
|
recipe.Addition = t.UpgradeItemDescriptorCount(recipe.Addition)
|
||||||
|
recipe.Result = t.UpgradeItemStack(recipe.Result)
|
||||||
|
pk.Recipes[i] = recipe
|
||||||
|
case *protocol.SmithingTrimRecipe:
|
||||||
|
recipe.Template = t.UpgradeItemDescriptorCount(recipe.Template)
|
||||||
|
recipe.Base = t.UpgradeItemDescriptorCount(recipe.Base)
|
||||||
|
recipe.Addition = t.UpgradeItemDescriptorCount(recipe.Addition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, recipe := range pk.PotionRecipes {
|
||||||
|
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.InputPotionID, MetadataValue: uint32(recipe.InputPotionMetadata)})
|
||||||
|
recipe.InputPotionID, recipe.InputPotionMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
|
||||||
|
itemType = t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.ReagentItemID, MetadataValue: uint32(recipe.ReagentItemMetadata)})
|
||||||
|
recipe.ReagentItemID, recipe.ReagentItemMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
|
||||||
|
itemType = t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.OutputPotionID, MetadataValue: uint32(recipe.OutputPotionMetadata)})
|
||||||
|
recipe.OutputPotionID, recipe.OutputPotionMetadata = itemType.NetworkID, int32(itemType.MetadataValue)
|
||||||
|
pk.PotionRecipes[i] = recipe
|
||||||
|
}
|
||||||
|
for i, recipe := range pk.PotionContainerChangeRecipes {
|
||||||
|
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.InputItemID})
|
||||||
|
recipe.InputItemID = itemType.NetworkID
|
||||||
|
itemType = t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.ReagentItemID})
|
||||||
|
recipe.ReagentItemID = itemType.NetworkID
|
||||||
|
itemType = t.UpgradeItemType(protocol.ItemType{NetworkID: recipe.OutputItemID})
|
||||||
|
recipe.OutputItemID = itemType.NetworkID
|
||||||
|
pk.PotionContainerChangeRecipes[i] = recipe
|
||||||
|
}
|
||||||
|
for i, recipe := range pk.MaterialReducers {
|
||||||
|
recipe.InputItem = t.UpgradeItemType(recipe.InputItem)
|
||||||
|
for i2, output := range recipe.Outputs {
|
||||||
|
itemType := t.UpgradeItemType(protocol.ItemType{NetworkID: output.NetworkID})
|
||||||
|
output.NetworkID = itemType.NetworkID
|
||||||
|
recipe.Outputs[i2] = output
|
||||||
|
}
|
||||||
|
pk.MaterialReducers[i] = recipe
|
||||||
|
}
|
||||||
|
//case *packet.CraftingEvent:
|
||||||
|
// pk.Input = lo.Map(pk.Input, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
|
||||||
|
// return t.UpgradeItemInstance(item)
|
||||||
|
// })
|
||||||
|
// pk.Output = lo.Map(pk.Output, func(item protocol.ItemInstance, _ int) protocol.ItemInstance {
|
||||||
|
// return t.UpgradeItemInstance(item)
|
||||||
|
// })
|
||||||
|
case *packet.PlayerAuthInput:
|
||||||
|
for i, action := range pk.ItemStackRequest.Actions {
|
||||||
|
if act, ok := action.(*protocol.CraftResultsDeprecatedStackRequestAction); ok {
|
||||||
|
act.ResultItems = lo.Map(act.ResultItems, func(item protocol.ItemStack, _ int) protocol.ItemStack {
|
||||||
|
return t.UpgradeItemStack(item)
|
||||||
|
})
|
||||||
|
pk.ItemStackRequest.Actions[i] = act
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, action := range pk.ItemInteractionData.Actions {
|
||||||
|
action.OldItem = t.UpgradeItemInstance(action.OldItem)
|
||||||
|
action.NewItem = t.UpgradeItemInstance(action.NewItem)
|
||||||
|
pk.ItemInteractionData.Actions[i] = action
|
||||||
|
}
|
||||||
|
pk.ItemInteractionData.HeldItem = t.UpgradeItemInstance(pk.ItemInteractionData.HeldItem)
|
||||||
|
case *packet.CreativeContent:
|
||||||
|
for i, creativeItem := range pk.Items {
|
||||||
|
creativeItem.Item = t.UpgradeItemStack(creativeItem.Item)
|
||||||
|
pk.Items[i] = creativeItem
|
||||||
|
}
|
||||||
|
case *packet.InventoryTransaction:
|
||||||
|
for i, action := range pk.Actions {
|
||||||
|
action.OldItem = t.UpgradeItemInstance(action.OldItem)
|
||||||
|
action.NewItem = t.UpgradeItemInstance(action.NewItem)
|
||||||
|
pk.Actions[i] = action
|
||||||
|
}
|
||||||
|
switch transactionData := pk.TransactionData.(type) {
|
||||||
|
case *protocol.UseItemTransactionData:
|
||||||
|
transactionData.HeldItem = t.UpgradeItemInstance(transactionData.HeldItem)
|
||||||
|
for i, action := range transactionData.Actions {
|
||||||
|
action.OldItem = t.UpgradeItemInstance(action.OldItem)
|
||||||
|
action.NewItem = t.UpgradeItemInstance(action.NewItem)
|
||||||
|
transactionData.Actions[i] = action
|
||||||
|
}
|
||||||
|
case *protocol.UseItemOnEntityTransactionData:
|
||||||
|
transactionData.HeldItem = t.UpgradeItemInstance(transactionData.HeldItem)
|
||||||
|
case *protocol.ReleaseItemTransactionData:
|
||||||
|
transactionData.HeldItem = t.UpgradeItemInstance(transactionData.HeldItem)
|
||||||
|
}
|
||||||
|
case *packet.LevelEvent:
|
||||||
|
if pk.EventType == packet.LevelEventParticleLegacyEvent|14 { // egg crack
|
||||||
|
itemType := t.UpgradeItemType(protocol.ItemType{
|
||||||
|
NetworkID: pk.EventData >> 16,
|
||||||
|
MetadataValue: uint32(pk.EventData & 0xf),
|
||||||
|
})
|
||||||
|
pk.EventData = (itemType.NetworkID << 16) | int32(itemType.MetadataValue)
|
||||||
|
}
|
||||||
|
case *packet.StartGame:
|
||||||
|
for i, entry := range pk.Items {
|
||||||
|
if !entry.ComponentBased {
|
||||||
|
itemType := t.UpgradeItemType(protocol.ItemType{
|
||||||
|
NetworkID: int32(entry.RuntimeID),
|
||||||
|
MetadataValue: 0,
|
||||||
|
})
|
||||||
|
entry.RuntimeID = int16(itemType.NetworkID)
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
if entry.Name, ok = t.latest.ItemRuntimeIDToName(itemType.NetworkID); !ok {
|
||||||
|
panic(itemType)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.latest.RegisterEntry(entry.Name)
|
||||||
|
entry.RuntimeID = int16(t.mapping.RegisterEntry(entry.Name))
|
||||||
|
}
|
||||||
|
pk.Items[i] = entry
|
||||||
|
}
|
||||||
|
for rid, i := range t.CustomItems() {
|
||||||
|
name, _ := i.EncodeItem()
|
||||||
|
pk.Items = append(pk.Items, protocol.ItemEntry{
|
||||||
|
Name: name,
|
||||||
|
RuntimeID: int16(rid),
|
||||||
|
ComponentBased: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case *packet.ItemComponent:
|
||||||
|
for _, i := range t.CustomItems() {
|
||||||
|
name, _ := i.EncodeItem()
|
||||||
|
pk.Items = append(pk.Items, protocol.ItemComponentEntry{
|
||||||
|
Name: name,
|
||||||
|
Data: packbuilder.Components(i),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, pk)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) Register(item world.CustomItem, replacement string) {
|
||||||
|
name, _ := item.EncodeItem()
|
||||||
|
originalRid, ok := t.latest.ItemNameToRuntimeID(replacement)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("%v not found in latest items", replacement))
|
||||||
|
}
|
||||||
|
if _, ok := t.originalToCustom[originalRid]; ok {
|
||||||
|
panic(fmt.Errorf("%v is already mapped", replacement))
|
||||||
|
}
|
||||||
|
|
||||||
|
nextRID := t.mapping.RegisterEntry(name)
|
||||||
|
t.ridToCustomItem[nextRID] = item
|
||||||
|
t.originalToCustom[originalRid] = nextRID
|
||||||
|
t.customToOriginal[nextRID] = originalRid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultItemTranslator) CustomItems() map[int32]world.CustomItem {
|
||||||
|
return t.ridToCustomItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeIndex[T any](s []T, index int) []T {
|
||||||
|
ret := make([]T, 0)
|
||||||
|
ret = append(ret, s[:index]...)
|
||||||
|
return append(ret, s[index+1:]...)
|
||||||
|
}
|
||||||
27
legacyver/legacypacket/item_stack_response.go
Normal file
27
legacyver/legacypacket/item_stack_response.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package legacypacket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/akmalfairuz/legacy-version/legacyver/proto"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ItemStackResponse is sent by the server in response to an ItemStackRequest packet from the client. This
|
||||||
|
// packet is used to either approve or reject ItemStackRequests from the client. If a request is approved, the
|
||||||
|
// client will simply continue as normal. If rejected, the client will undo the actions so that the inventory
|
||||||
|
// should be in sync with the server again.
|
||||||
|
type ItemStackResponse struct {
|
||||||
|
// Responses is a list of responses to ItemStackRequests sent by the client before. Responses either
|
||||||
|
// approve or reject a request from the client.
|
||||||
|
// Vanilla limits the size of this slice to 4096.
|
||||||
|
Responses []proto.ItemStackResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID ...
|
||||||
|
func (*ItemStackResponse) ID() uint32 {
|
||||||
|
return packet.IDItemStackResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk *ItemStackResponse) Marshal(io protocol.IO) {
|
||||||
|
protocol.Slice(io, &pk.Responses)
|
||||||
|
}
|
||||||
1
legacyver/legacypacket/packet.go
Normal file
1
legacyver/legacypacket/packet.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package legacypacket
|
||||||
210
legacyver/legacypacket/player_auth_input.go
Normal file
210
legacyver/legacypacket/player_auth_input.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
package legacypacket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/akmalfairuz/legacy-version/legacyver/proto"
|
||||||
|
"github.com/go-gl/mathgl/mgl32"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PlayerAuthInputBitsetSize = 65
|
||||||
|
|
||||||
|
const (
|
||||||
|
InputFlagAscend = iota
|
||||||
|
InputFlagDescend
|
||||||
|
InputFlagNorthJump
|
||||||
|
InputFlagJumpDown
|
||||||
|
InputFlagSprintDown
|
||||||
|
InputFlagChangeHeight
|
||||||
|
InputFlagJumping
|
||||||
|
InputFlagAutoJumpingInWater
|
||||||
|
InputFlagSneaking
|
||||||
|
InputFlagSneakDown
|
||||||
|
InputFlagUp
|
||||||
|
InputFlagDown
|
||||||
|
InputFlagLeft
|
||||||
|
InputFlagRight
|
||||||
|
InputFlagUpLeft
|
||||||
|
InputFlagUpRight
|
||||||
|
InputFlagWantUp
|
||||||
|
InputFlagWantDown
|
||||||
|
InputFlagWantDownSlow
|
||||||
|
InputFlagWantUpSlow
|
||||||
|
InputFlagSprinting
|
||||||
|
InputFlagAscendBlock
|
||||||
|
InputFlagDescendBlock
|
||||||
|
InputFlagSneakToggleDown
|
||||||
|
InputFlagPersistSneak
|
||||||
|
InputFlagStartSprinting
|
||||||
|
InputFlagStopSprinting
|
||||||
|
InputFlagStartSneaking
|
||||||
|
InputFlagStopSneaking
|
||||||
|
InputFlagStartSwimming
|
||||||
|
InputFlagStopSwimming
|
||||||
|
InputFlagStartJumping
|
||||||
|
InputFlagStartGliding
|
||||||
|
InputFlagStopGliding
|
||||||
|
InputFlagPerformItemInteraction
|
||||||
|
InputFlagPerformBlockActions
|
||||||
|
InputFlagPerformItemStackRequest
|
||||||
|
InputFlagHandledTeleport
|
||||||
|
InputFlagEmoting
|
||||||
|
InputFlagMissedSwing
|
||||||
|
InputFlagStartCrawling
|
||||||
|
InputFlagStopCrawling
|
||||||
|
InputFlagStartFlying
|
||||||
|
InputFlagStopFlying
|
||||||
|
InputFlagClientAckServerData
|
||||||
|
InputFlagClientPredictedVehicle
|
||||||
|
InputFlagPaddlingLeft
|
||||||
|
InputFlagPaddlingRight
|
||||||
|
InputFlagBlockBreakingDelayEnabled
|
||||||
|
InputFlagHorizontalCollision
|
||||||
|
InputFlagVerticalCollision
|
||||||
|
InputFlagDownLeft
|
||||||
|
InputFlagDownRight
|
||||||
|
InputFlagStartUsingItem
|
||||||
|
InputFlagCameraRelativeMovementEnabled
|
||||||
|
InputFlagRotControlledByMoveDirection
|
||||||
|
InputFlagStartSpinAttack
|
||||||
|
InputFlagStopSpinAttack
|
||||||
|
InputFlagIsHotbarTouchOnly
|
||||||
|
InputFlagJumpReleasedRaw
|
||||||
|
InputFlagJumpPressedRaw
|
||||||
|
InputFlagJumpCurrentRaw
|
||||||
|
InputFlagSneakReleasedRaw
|
||||||
|
InputFlagSneakPressedRaw
|
||||||
|
InputFlagSneakCurrentRaw
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
InputModeMouse = iota + 1
|
||||||
|
InputModeTouch
|
||||||
|
InputModeGamePad
|
||||||
|
InputModeMotionController
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PlayModeNormal = iota
|
||||||
|
PlayModeTeaser
|
||||||
|
PlayModeScreen
|
||||||
|
PlayModeViewer
|
||||||
|
PlayModeReality
|
||||||
|
PlayModePlacement
|
||||||
|
PlayModeLivingRoom
|
||||||
|
PlayModeExitLevel
|
||||||
|
PlayModeExitLevelLivingRoom
|
||||||
|
PlayModeNumModes
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
InteractionModelTouch = iota
|
||||||
|
InteractionModelCrosshair
|
||||||
|
InteractionModelClassic
|
||||||
|
)
|
||||||
|
|
||||||
|
// PlayerAuthInput is sent by the client to allow for server authoritative movement. It is used to synchronise
|
||||||
|
// the player input with the position server-side.
|
||||||
|
// The client sends this packet when the ServerAuthoritativeMovementMode field in the StartGame packet is set
|
||||||
|
// to true, instead of the MovePlayer packet. The client will send this packet once every tick.
|
||||||
|
type PlayerAuthInput struct {
|
||||||
|
// Pitch and Yaw hold the rotation that the player reports it has.
|
||||||
|
Pitch, Yaw float32
|
||||||
|
// Position holds the position that the player reports it has.
|
||||||
|
Position mgl32.Vec3
|
||||||
|
// MoveVector is a Vec2 that specifies the direction in which the player moved, as a combination of X/Z
|
||||||
|
// values which are created using the WASD/controller stick state.
|
||||||
|
MoveVector mgl32.Vec2
|
||||||
|
// HeadYaw is the horizontal rotation of the head that the player reports it has.
|
||||||
|
HeadYaw float32
|
||||||
|
// InputData is a combination of bit flags that together specify the way the player moved last tick. It
|
||||||
|
// is a combination of the flags above.
|
||||||
|
InputData protocol.Bitset
|
||||||
|
// InputMode specifies the way that the client inputs data to the screen. It is one of the constants that
|
||||||
|
// may be found above.
|
||||||
|
InputMode uint32
|
||||||
|
// PlayMode specifies the way that the player is playing. The values it holds, which are rather random,
|
||||||
|
// may be found above.
|
||||||
|
PlayMode uint32
|
||||||
|
// InteractionModel is a constant representing the interaction model the player is using. It is one of the
|
||||||
|
// constants that may be found above.
|
||||||
|
InteractionModel uint32
|
||||||
|
// InteractPitch and interactYaw is the rotation the player is looking that they intend to use for
|
||||||
|
// interactions. This is only different to Pitch and Yaw in cases such as VR or when custom cameras
|
||||||
|
// being used.
|
||||||
|
InteractPitch, InteractYaw float32
|
||||||
|
// Tick is the server tick at which the packet was sent. It is used in relation to
|
||||||
|
// CorrectPlayerMovePrediction.
|
||||||
|
Tick uint64
|
||||||
|
// Delta was the delta between the old and the new position. There isn't any practical use for this field
|
||||||
|
// as it can be calculated by the server itself.
|
||||||
|
Delta mgl32.Vec3
|
||||||
|
// ItemInteractionData is the transaction data if the InputData includes an item interaction.
|
||||||
|
ItemInteractionData protocol.UseItemTransactionData
|
||||||
|
// ItemStackRequest is sent by the client to change an item in their inventory.
|
||||||
|
ItemStackRequest protocol.ItemStackRequest
|
||||||
|
// BlockActions is a slice of block actions that the client has interacted with.
|
||||||
|
BlockActions []protocol.PlayerBlockAction
|
||||||
|
// VehicleRotation is the rotation of the vehicle that the player is in, if any.
|
||||||
|
VehicleRotation mgl32.Vec2
|
||||||
|
// ClientPredictedVehicle is the unique ID of the vehicle that the client predicts the player to be in.
|
||||||
|
ClientPredictedVehicle int64
|
||||||
|
// AnalogueMoveVector is a Vec2 that specifies the direction in which the player moved, as a combination
|
||||||
|
// of X/Z values which are created using an analogue input.
|
||||||
|
AnalogueMoveVector mgl32.Vec2
|
||||||
|
// CameraOrientation is the vector that represents the camera's forward direction which can be used to
|
||||||
|
// transform movement to be camera relative.
|
||||||
|
CameraOrientation mgl32.Vec3
|
||||||
|
// RawMoveVector is the value of MoveVector before it is affected by input permissions, sneaking/fly
|
||||||
|
// speeds and isn't normalised for analogue inputs.
|
||||||
|
RawMoveVector mgl32.Vec2
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID ...
|
||||||
|
func (pk *PlayerAuthInput) ID() uint32 {
|
||||||
|
return packet.IDPlayerAuthInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk *PlayerAuthInput) Marshal(io protocol.IO) {
|
||||||
|
io.Float32(&pk.Pitch)
|
||||||
|
io.Float32(&pk.Yaw)
|
||||||
|
io.Vec3(&pk.Position)
|
||||||
|
io.Vec2(&pk.MoveVector)
|
||||||
|
io.Float32(&pk.HeadYaw)
|
||||||
|
if proto.IsProtoGTE(io, proto.ID766) {
|
||||||
|
io.Bitset(&pk.InputData, PlayerAuthInputBitsetSize)
|
||||||
|
} else {
|
||||||
|
io.Bitset(&pk.InputData, 64)
|
||||||
|
}
|
||||||
|
io.Varuint32(&pk.InputMode)
|
||||||
|
io.Varuint32(&pk.PlayMode)
|
||||||
|
io.Varuint32(&pk.InteractionModel)
|
||||||
|
io.Float32(&pk.InteractPitch)
|
||||||
|
io.Float32(&pk.InteractYaw)
|
||||||
|
io.Varuint64(&pk.Tick)
|
||||||
|
io.Vec3(&pk.Delta)
|
||||||
|
|
||||||
|
if pk.InputData.Load(InputFlagPerformItemInteraction) {
|
||||||
|
io.PlayerInventoryAction(&pk.ItemInteractionData)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pk.InputData.Load(InputFlagPerformItemStackRequest) {
|
||||||
|
protocol.Single(io, &pk.ItemStackRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pk.InputData.Load(InputFlagPerformBlockActions) {
|
||||||
|
protocol.SliceVarint32Length(io, &pk.BlockActions)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pk.InputData.Load(InputFlagClientPredictedVehicle) {
|
||||||
|
io.Vec2(&pk.VehicleRotation)
|
||||||
|
io.Varint64(&pk.ClientPredictedVehicle)
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Vec2(&pk.AnalogueMoveVector)
|
||||||
|
io.Vec3(&pk.CameraOrientation)
|
||||||
|
|
||||||
|
if proto.IsProtoGTE(io, proto.ID766) {
|
||||||
|
io.Vec2(&pk.RawMoveVector)
|
||||||
|
}
|
||||||
|
}
|
||||||
50
legacyver/legacypacket/resource_packs_info.go
Normal file
50
legacyver/legacypacket/resource_packs_info.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package legacypacket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/akmalfairuz/legacy-version/legacyver/proto"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourcePacksInfo is sent by the server to inform the client on what resource packs the server has. It
|
||||||
|
// sends a list of the resource packs it has and basic information on them like the version and description.
|
||||||
|
type ResourcePacksInfo struct {
|
||||||
|
// TexturePackRequired specifies if the client must accept the texture packs the server has in order to
|
||||||
|
// join the server. If set to true, the client gets the option to either download the resource packs and
|
||||||
|
// join, or quit entirely. Behaviour packs never have to be downloaded.
|
||||||
|
TexturePackRequired bool
|
||||||
|
// HasAddons specifies if any of the resource packs contain addons in them. If set to true, only clients
|
||||||
|
// that support addons will be able to download them.
|
||||||
|
HasAddons bool
|
||||||
|
// HasScripts specifies if any of the resource packs contain scripts in them. If set to true, only clients
|
||||||
|
// that support scripts will be able to download them.
|
||||||
|
HasScripts bool
|
||||||
|
// WorldTemplateUUID is the UUID of the template that has been used to generate the world. Templates can
|
||||||
|
// be downloaded from the marketplace or installed via '.mctemplate' files. If the world was not generated
|
||||||
|
// from a template, this field is empty.
|
||||||
|
WorldTemplateUUID uuid.UUID
|
||||||
|
// WorldTemplateVersion is the version of the world template that has been used to generate the world. If
|
||||||
|
// the world was not generated from a template, this field is empty.
|
||||||
|
WorldTemplateVersion string
|
||||||
|
// TexturePacks is a list of texture packs that the client needs to download before joining the server.
|
||||||
|
// The order of these texture packs is not relevant in this packet. It is however important in the
|
||||||
|
// ResourcePackStack packet.
|
||||||
|
TexturePacks []proto.TexturePackInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID ...
|
||||||
|
func (*ResourcePacksInfo) ID() uint32 {
|
||||||
|
return packet.IDResourcePacksInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk *ResourcePacksInfo) Marshal(io protocol.IO) {
|
||||||
|
io.Bool(&pk.TexturePackRequired)
|
||||||
|
io.Bool(&pk.HasAddons)
|
||||||
|
io.Bool(&pk.HasScripts)
|
||||||
|
if proto.IsProtoGTE(io, proto.ID766) {
|
||||||
|
io.UUID(&pk.WorldTemplateUUID)
|
||||||
|
io.String(&pk.WorldTemplateVersion)
|
||||||
|
}
|
||||||
|
protocol.SliceUint16Length(io, &pk.TexturePacks)
|
||||||
|
}
|
||||||
32
legacyver/proto/id.go
Normal file
32
legacyver/proto/id.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
import "github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ID766 = 766
|
||||||
|
ID748 = 748
|
||||||
|
ID729 = 729
|
||||||
|
ID712 = 712
|
||||||
|
ID686 = 686
|
||||||
|
ID685 = 685
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsProtoGTE(io protocol.IO, proto int32) bool {
|
||||||
|
return io.(IO).ProtocolID() >= proto
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsProtoLTE(io protocol.IO, proto int32) bool {
|
||||||
|
return io.(IO).ProtocolID() <= proto
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsProtoLT(io protocol.IO, proto int32) bool {
|
||||||
|
return io.(IO).ProtocolID() < proto
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsProtoGT(io protocol.IO, proto int32) bool {
|
||||||
|
return io.(IO).ProtocolID() > proto
|
||||||
|
}
|
||||||
|
|
||||||
|
func FetchProtoID(io protocol.IO) int32 {
|
||||||
|
return io.(IO).ProtocolID()
|
||||||
|
}
|
||||||
53
legacyver/proto/io.go
Normal file
53
legacyver/proto/io.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IO interface {
|
||||||
|
protocol.IO
|
||||||
|
|
||||||
|
SetProtocolID(protocolID int32)
|
||||||
|
ProtocolID() int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Reader struct {
|
||||||
|
*protocol.Reader
|
||||||
|
|
||||||
|
protocolID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReader(r *protocol.Reader, protocolID int32) *Reader {
|
||||||
|
return &Reader{
|
||||||
|
Reader: r,
|
||||||
|
protocolID: protocolID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) SetProtocolID(protocolID int32) { r.protocolID = protocolID }
|
||||||
|
func (r *Reader) ProtocolID() int32 { return r.protocolID }
|
||||||
|
func (r *Reader) Reads() bool { return true }
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
*protocol.Writer
|
||||||
|
|
||||||
|
protocolID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter(w *protocol.Writer, protocolID int32) *Writer {
|
||||||
|
return &Writer{w, protocolID}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) SetProtocolID(protocolID int32) { w.protocolID = protocolID }
|
||||||
|
func (w *Writer) ProtocolID() int32 { return w.protocolID }
|
||||||
|
func (w *Writer) Reads() bool { return false }
|
||||||
|
|
||||||
|
func IsReader(r protocol.IO) bool {
|
||||||
|
_, ok := r.(*Reader)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsWriter(w protocol.IO) bool {
|
||||||
|
_, ok := w.(*Writer)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
652
legacyver/proto/item_stack.go
Normal file
652
legacyver/proto/item_stack.go
Normal file
@@ -0,0 +1,652 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FilterCauseServerChatPublic = iota
|
||||||
|
FilterCauseServerChatWhisper
|
||||||
|
FilterCauseSignText
|
||||||
|
FilterCauseAnvilText
|
||||||
|
FilterCauseBookAndQuillText
|
||||||
|
FilterCauseCommandBlockText
|
||||||
|
FilterCauseBlockActorDataText
|
||||||
|
FilterCauseJoinEventText
|
||||||
|
FilterCauseLeaveEventText
|
||||||
|
FilterCauseSlashCommandChat
|
||||||
|
FilterCauseCartographyText
|
||||||
|
FilterCauseKickCommand
|
||||||
|
FilterCauseTitleCommand
|
||||||
|
FilterCauseSummonCommand
|
||||||
|
)
|
||||||
|
|
||||||
|
// ItemStackRequest represents a single request present in an ItemStackRequest packet sent by the client to
|
||||||
|
// change an item in an inventory.
|
||||||
|
// Item stack requests are either approved or rejected by the server using the ItemStackResponse packet.
|
||||||
|
type ItemStackRequest struct {
|
||||||
|
// RequestID is a unique ID for the request. This ID is used by the server to send a response for this
|
||||||
|
// specific request in the ItemStackResponse packet.
|
||||||
|
RequestID int32
|
||||||
|
// Actions is a list of actions performed by the client. The actual type of the actions depends on which
|
||||||
|
// ID was present, and is one of the concrete types below.
|
||||||
|
Actions []protocol.StackRequestAction
|
||||||
|
// FilterStrings is a list of filter strings involved in the request. This is typically filled with one string
|
||||||
|
// when an anvil or cartography is used.
|
||||||
|
FilterStrings []string
|
||||||
|
// FilterCause represents the cause of any potential filtering. This is one of the constants above.
|
||||||
|
FilterCause int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes/decodes an ItemStackRequest.
|
||||||
|
func (x *ItemStackRequest) Marshal(r protocol.IO) {
|
||||||
|
r.Varint32(&x.RequestID)
|
||||||
|
protocol.FuncSlice(r, &x.Actions, r.StackRequestAction)
|
||||||
|
protocol.FuncSlice(r, &x.FilterStrings, r.String)
|
||||||
|
r.Int32(&x.FilterCause)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookupStackRequestActionType looks up the ID of a StackRequestAction.
|
||||||
|
func lookupStackRequestActionType(x StackRequestAction, id *uint8) bool {
|
||||||
|
switch x.(type) {
|
||||||
|
case *TakeStackRequestAction:
|
||||||
|
*id = StackRequestActionTake
|
||||||
|
case *PlaceStackRequestAction:
|
||||||
|
*id = StackRequestActionPlace
|
||||||
|
case *SwapStackRequestAction:
|
||||||
|
*id = StackRequestActionSwap
|
||||||
|
case *DropStackRequestAction:
|
||||||
|
*id = StackRequestActionDrop
|
||||||
|
case *DestroyStackRequestAction:
|
||||||
|
*id = StackRequestActionDestroy
|
||||||
|
case *ConsumeStackRequestAction:
|
||||||
|
*id = StackRequestActionConsume
|
||||||
|
case *CreateStackRequestAction:
|
||||||
|
*id = StackRequestActionCreate
|
||||||
|
case *LabTableCombineStackRequestAction:
|
||||||
|
*id = StackRequestActionLabTableCombine
|
||||||
|
case *BeaconPaymentStackRequestAction:
|
||||||
|
*id = StackRequestActionBeaconPayment
|
||||||
|
case *MineBlockStackRequestAction:
|
||||||
|
*id = StackRequestActionMineBlock
|
||||||
|
case *CraftRecipeStackRequestAction:
|
||||||
|
*id = StackRequestActionCraftRecipe
|
||||||
|
case *AutoCraftRecipeStackRequestAction:
|
||||||
|
*id = StackRequestActionCraftRecipeAuto
|
||||||
|
case *CraftCreativeStackRequestAction:
|
||||||
|
*id = StackRequestActionCraftCreative
|
||||||
|
case *CraftRecipeOptionalStackRequestAction:
|
||||||
|
*id = StackRequestActionCraftRecipeOptional
|
||||||
|
case *CraftGrindstoneRecipeStackRequestAction:
|
||||||
|
*id = StackRequestActionCraftGrindstone
|
||||||
|
case *CraftLoomRecipeStackRequestAction:
|
||||||
|
*id = StackRequestActionCraftLoom
|
||||||
|
case *CraftNonImplementedStackRequestAction:
|
||||||
|
*id = StackRequestActionCraftNonImplementedDeprecated
|
||||||
|
case *CraftResultsDeprecatedStackRequestAction:
|
||||||
|
*id = StackRequestActionCraftResultsDeprecated
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookupStackRequestAction looks up the StackRequestAction matching an ID.
|
||||||
|
func lookupStackRequestAction(id uint8, x *StackRequestAction) bool {
|
||||||
|
switch id {
|
||||||
|
case StackRequestActionTake:
|
||||||
|
*x = &TakeStackRequestAction{}
|
||||||
|
case StackRequestActionPlace:
|
||||||
|
*x = &PlaceStackRequestAction{}
|
||||||
|
case StackRequestActionSwap:
|
||||||
|
*x = &SwapStackRequestAction{}
|
||||||
|
case StackRequestActionDrop:
|
||||||
|
*x = &DropStackRequestAction{}
|
||||||
|
case StackRequestActionDestroy:
|
||||||
|
*x = &DestroyStackRequestAction{}
|
||||||
|
case StackRequestActionConsume:
|
||||||
|
*x = &ConsumeStackRequestAction{}
|
||||||
|
case StackRequestActionCreate:
|
||||||
|
*x = &CreateStackRequestAction{}
|
||||||
|
case StackRequestActionPlaceInContainer:
|
||||||
|
*x = &PlaceInContainerStackRequestAction{}
|
||||||
|
case StackRequestActionTakeOutContainer:
|
||||||
|
*x = &TakeOutContainerStackRequestAction{}
|
||||||
|
case StackRequestActionLabTableCombine:
|
||||||
|
*x = &LabTableCombineStackRequestAction{}
|
||||||
|
case StackRequestActionBeaconPayment:
|
||||||
|
*x = &BeaconPaymentStackRequestAction{}
|
||||||
|
case StackRequestActionMineBlock:
|
||||||
|
*x = &MineBlockStackRequestAction{}
|
||||||
|
case StackRequestActionCraftRecipe:
|
||||||
|
*x = &CraftRecipeStackRequestAction{}
|
||||||
|
case StackRequestActionCraftRecipeAuto:
|
||||||
|
*x = &AutoCraftRecipeStackRequestAction{}
|
||||||
|
case StackRequestActionCraftCreative:
|
||||||
|
*x = &CraftCreativeStackRequestAction{}
|
||||||
|
case StackRequestActionCraftRecipeOptional:
|
||||||
|
*x = &CraftRecipeOptionalStackRequestAction{}
|
||||||
|
case StackRequestActionCraftGrindstone:
|
||||||
|
*x = &CraftGrindstoneRecipeStackRequestAction{}
|
||||||
|
case StackRequestActionCraftLoom:
|
||||||
|
*x = &CraftLoomRecipeStackRequestAction{}
|
||||||
|
case StackRequestActionCraftNonImplementedDeprecated:
|
||||||
|
*x = &CraftNonImplementedStackRequestAction{}
|
||||||
|
case StackRequestActionCraftResultsDeprecated:
|
||||||
|
*x = &CraftResultsDeprecatedStackRequestAction{}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ItemStackResponseStatusOK = iota
|
||||||
|
ItemStackResponseStatusError
|
||||||
|
ItemStackResponseStatusInvalidRequestActionType
|
||||||
|
ItemStackResponseStatusActionRequestNotAllowed
|
||||||
|
ItemStackResponseStatusScreenHandlerEndRequestFailed
|
||||||
|
ItemStackResponseStatusItemRequestActionHandlerCommitFailed
|
||||||
|
ItemStackResponseStatusInvalidRequestCraftActionType
|
||||||
|
ItemStackResponseStatusInvalidCraftRequest
|
||||||
|
ItemStackResponseStatusInvalidCraftRequestScreen
|
||||||
|
ItemStackResponseStatusInvalidCraftResult
|
||||||
|
ItemStackResponseStatusInvalidCraftResultIndex
|
||||||
|
ItemStackResponseStatusInvalidCraftResultItem
|
||||||
|
ItemStackResponseStatusInvalidItemNetId
|
||||||
|
ItemStackResponseStatusMissingCreatedOutputContainer
|
||||||
|
ItemStackResponseStatusFailedToSetCreatedItemOutputSlot
|
||||||
|
ItemStackResponseStatusRequestAlreadyInProgress
|
||||||
|
ItemStackResponseStatusFailedToInitSparseContainer
|
||||||
|
ItemStackResponseStatusResultTransferFailed
|
||||||
|
ItemStackResponseStatusExpectedItemSlotNotFullyConsumed
|
||||||
|
ItemStackResponseStatusExpectedAnywhereItemNotFullyConsumed
|
||||||
|
ItemStackResponseStatusItemAlreadyConsumedFromSlot
|
||||||
|
ItemStackResponseStatusConsumedTooMuchFromSlot
|
||||||
|
ItemStackResponseStatusMismatchSlotExpectedConsumedItem
|
||||||
|
ItemStackResponseStatusMismatchSlotExpectedConsumedItemNetIdVariant
|
||||||
|
ItemStackResponseStatusFailedToMatchExpectedSlotConsumedItem
|
||||||
|
ItemStackResponseStatusFailedToMatchExpectedAllowedAnywhereConsumedItem
|
||||||
|
ItemStackResponseStatusConsumedItemOutOfAllowedSlotRange
|
||||||
|
ItemStackResponseStatusConsumedItemNotAllowed
|
||||||
|
ItemStackResponseStatusPlayerNotInCreativeMode
|
||||||
|
ItemStackResponseStatusInvalidExperimentalRecipeRequest
|
||||||
|
ItemStackResponseStatusFailedToCraftCreative
|
||||||
|
ItemStackResponseStatusFailedToGetLevelRecipe
|
||||||
|
ItemStackResponseStatusFailedToFindRecipeByNetId
|
||||||
|
ItemStackResponseStatusMismatchedCraftingSize
|
||||||
|
ItemStackResponseStatusMissingInputSparseContainer
|
||||||
|
ItemStackResponseStatusMismatchedRecipeForInputGridItems
|
||||||
|
ItemStackResponseStatusEmptyCraftResults
|
||||||
|
ItemStackResponseStatusFailedToEnchant
|
||||||
|
ItemStackResponseStatusMissingInputItem
|
||||||
|
ItemStackResponseStatusInsufficientPlayerLevelToEnchant
|
||||||
|
ItemStackResponseStatusMissingMaterialItem
|
||||||
|
ItemStackResponseStatusMissingActor
|
||||||
|
ItemStackResponseStatusUnknownPrimaryEffect
|
||||||
|
ItemStackResponseStatusPrimaryEffectOutOfRange
|
||||||
|
ItemStackResponseStatusPrimaryEffectUnavailable
|
||||||
|
ItemStackResponseStatusSecondaryEffectOutOfRange
|
||||||
|
ItemStackResponseStatusSecondaryEffectUnavailable
|
||||||
|
ItemStackResponseStatusDstContainerEqualToCreatedOutputContainer
|
||||||
|
ItemStackResponseStatusDstContainerAndSlotEqualToSrcContainerAndSlot
|
||||||
|
ItemStackResponseStatusFailedToValidateSrcSlot
|
||||||
|
ItemStackResponseStatusFailedToValidateDstSlot
|
||||||
|
ItemStackResponseStatusInvalidAdjustedAmount
|
||||||
|
ItemStackResponseStatusInvalidItemSetType
|
||||||
|
ItemStackResponseStatusInvalidTransferAmount
|
||||||
|
ItemStackResponseStatusCannotSwapItem
|
||||||
|
ItemStackResponseStatusCannotPlaceItem
|
||||||
|
ItemStackResponseStatusUnhandledItemSetType
|
||||||
|
ItemStackResponseStatusInvalidRemovedAmount
|
||||||
|
ItemStackResponseStatusInvalidRegion
|
||||||
|
ItemStackResponseStatusCannotDropItem
|
||||||
|
ItemStackResponseStatusCannotDestroyItem
|
||||||
|
ItemStackResponseStatusInvalidSourceContainer
|
||||||
|
ItemStackResponseStatusItemNotConsumed
|
||||||
|
ItemStackResponseStatusInvalidNumCrafts
|
||||||
|
ItemStackResponseStatusInvalidCraftResultStackSize
|
||||||
|
ItemStackResponseStatusCannotRemoveItem
|
||||||
|
ItemStackResponseStatusCannotConsumeItem
|
||||||
|
ItemStackResponseStatusScreenStackError
|
||||||
|
)
|
||||||
|
|
||||||
|
// ItemStackResponse is a response to an individual ItemStackRequest.
|
||||||
|
type ItemStackResponse struct {
|
||||||
|
// Status specifies if the request with the RequestID below was successful. If this is the case, the
|
||||||
|
// ContainerInfo below will have information on what slots ended up changing. If not, the container info
|
||||||
|
// will be empty.
|
||||||
|
// A non-0 status means an error occurred and will result in the action being reverted.
|
||||||
|
Status uint8
|
||||||
|
// RequestID is the unique ID of the request that this response is in reaction to. If rejected, the client
|
||||||
|
// will undo the actions from the request with this ID.
|
||||||
|
RequestID int32
|
||||||
|
// ContainerInfo holds information on the containers that had their contents changed as a result of the
|
||||||
|
// request.
|
||||||
|
ContainerInfo []StackResponseContainerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ItemStackResponse) ToLatest() protocol.ItemStackResponse {
|
||||||
|
ret := protocol.ItemStackResponse{
|
||||||
|
Status: x.Status,
|
||||||
|
RequestID: x.RequestID,
|
||||||
|
ContainerInfo: make([]protocol.StackResponseContainerInfo, len(x.ContainerInfo)),
|
||||||
|
}
|
||||||
|
for i, v := range x.ContainerInfo {
|
||||||
|
ret.ContainerInfo[i] = protocol.StackResponseContainerInfo{
|
||||||
|
Container: v.Container,
|
||||||
|
SlotInfo: make([]protocol.StackResponseSlotInfo, len(v.SlotInfo)),
|
||||||
|
}
|
||||||
|
for j, w := range v.SlotInfo {
|
||||||
|
ret.ContainerInfo[i].SlotInfo[j] = protocol.StackResponseSlotInfo{
|
||||||
|
Slot: w.Slot,
|
||||||
|
HotbarSlot: w.HotbarSlot,
|
||||||
|
Count: w.Count,
|
||||||
|
StackNetworkID: w.StackNetworkID,
|
||||||
|
CustomName: w.CustomName,
|
||||||
|
FilteredCustomName: w.FilteredCustomName,
|
||||||
|
DurabilityCorrection: w.DurabilityCorrection,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes/decodes an ItemStackResponse.
|
||||||
|
func (x *ItemStackResponse) Marshal(r protocol.IO) {
|
||||||
|
r.Uint8(&x.Status)
|
||||||
|
r.Varint32(&x.RequestID)
|
||||||
|
if x.Status == ItemStackResponseStatusOK {
|
||||||
|
protocol.Slice(r, &x.ContainerInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackResponseContainerInfo holds information on what slots in a container have what item stack in them.
|
||||||
|
type StackResponseContainerInfo struct {
|
||||||
|
// Container is the FullContainerName that describes the container that the slots that follow are in. For
|
||||||
|
// the main inventory, the ContainerID seems to be 0x1b. Fur the cursor, this value seems to be 0x3a. For
|
||||||
|
// the crafting grid, this value seems to be 0x0d.
|
||||||
|
Container protocol.FullContainerName
|
||||||
|
// SlotInfo holds information on what item stack should be present in specific slots in the container.
|
||||||
|
SlotInfo []StackResponseSlotInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes/decodes a StackResponseContainerInfo.
|
||||||
|
func (x *StackResponseContainerInfo) Marshal(r protocol.IO) {
|
||||||
|
protocol.Single(r, &x.Container)
|
||||||
|
protocol.Slice(r, &x.SlotInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackResponseSlotInfo holds information on what item stack should be present in a specific slot.
|
||||||
|
type StackResponseSlotInfo struct {
|
||||||
|
// Slot and HotbarSlot seem to be the same value every time: The slot that was actually changed. I'm not
|
||||||
|
// sure if these slots ever differ.
|
||||||
|
Slot, HotbarSlot byte
|
||||||
|
// Count is the total count of the item stack. This count will be shown client-side after the response is
|
||||||
|
// sent to the client.
|
||||||
|
Count byte
|
||||||
|
// StackNetworkID is the network ID of the new stack at a specific slot.
|
||||||
|
StackNetworkID int32
|
||||||
|
// CustomName is the custom name of the item stack. It is used in relation to text filtering.
|
||||||
|
CustomName string
|
||||||
|
// FilteredCustomName is a filtered version of CustomName with all the profanity removed. The client will
|
||||||
|
// use this over CustomName if this field is not empty and they have the "Filter Profanity" setting enabled.
|
||||||
|
FilteredCustomName string
|
||||||
|
// DurabilityCorrection is the current durability of the item stack. This durability will be shown
|
||||||
|
// client-side after the response is sent to the client.
|
||||||
|
DurabilityCorrection int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes/decodes a StackResponseSlotInfo.
|
||||||
|
func (x *StackResponseSlotInfo) Marshal(r protocol.IO) {
|
||||||
|
r.Uint8(&x.Slot)
|
||||||
|
r.Uint8(&x.HotbarSlot)
|
||||||
|
r.Uint8(&x.Count)
|
||||||
|
r.Varint32(&x.StackNetworkID)
|
||||||
|
if x.Slot != x.HotbarSlot {
|
||||||
|
r.InvalidValue(x.HotbarSlot, "hotbar slot", "hot bar slot must be equal to normal slot")
|
||||||
|
}
|
||||||
|
r.String(&x.CustomName)
|
||||||
|
if IsProtoGTE(r, ID766) {
|
||||||
|
r.String(&x.FilteredCustomName)
|
||||||
|
}
|
||||||
|
r.Varint32(&x.DurabilityCorrection)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackRequestAction represents a single action related to the inventory present in an ItemStackRequest.
|
||||||
|
// The action is one of the concrete types below, each of which are indicative of a different action by the
|
||||||
|
// client, such as moving an item around the inventory or placing a block. It is an alias of Marshaler.
|
||||||
|
type StackRequestAction interface {
|
||||||
|
protocol.Marshaler
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
StackRequestActionTake = iota
|
||||||
|
StackRequestActionPlace
|
||||||
|
StackRequestActionSwap
|
||||||
|
StackRequestActionDrop
|
||||||
|
StackRequestActionDestroy
|
||||||
|
StackRequestActionConsume
|
||||||
|
StackRequestActionCreate
|
||||||
|
StackRequestActionPlaceInContainer
|
||||||
|
StackRequestActionTakeOutContainer
|
||||||
|
StackRequestActionLabTableCombine
|
||||||
|
StackRequestActionBeaconPayment
|
||||||
|
StackRequestActionMineBlock
|
||||||
|
StackRequestActionCraftRecipe
|
||||||
|
StackRequestActionCraftRecipeAuto
|
||||||
|
StackRequestActionCraftCreative
|
||||||
|
StackRequestActionCraftRecipeOptional
|
||||||
|
StackRequestActionCraftGrindstone
|
||||||
|
StackRequestActionCraftLoom
|
||||||
|
StackRequestActionCraftNonImplementedDeprecated
|
||||||
|
StackRequestActionCraftResultsDeprecated
|
||||||
|
)
|
||||||
|
|
||||||
|
// transferStackRequestAction is the structure shared by StackRequestActions that transfer items from one
|
||||||
|
// slot into another.
|
||||||
|
type transferStackRequestAction struct {
|
||||||
|
// Count is the count of the item in the source slot that was taken towards the destination slot.
|
||||||
|
Count byte
|
||||||
|
// Source and Destination point to the source slot from which Count of the item stack were taken and the
|
||||||
|
// destination slot to which this item was moved.
|
||||||
|
Source, Destination StackRequestSlotInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *transferStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Uint8(&a.Count)
|
||||||
|
StackReqSlotInfo(r, &a.Source)
|
||||||
|
StackReqSlotInfo(r, &a.Destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TakeStackRequestAction is sent by the client to the server to take x amount of items from one slot in a
|
||||||
|
// container to the cursor.
|
||||||
|
type TakeStackRequestAction struct {
|
||||||
|
transferStackRequestAction
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlaceStackRequestAction is sent by the client to the server to place x amount of items from one slot into
|
||||||
|
// another slot, such as when shift clicking an item in the inventory to move it around or when moving an item
|
||||||
|
// in the cursor into a slot.
|
||||||
|
type PlaceStackRequestAction struct {
|
||||||
|
transferStackRequestAction
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapStackRequestAction is sent by the client to swap the item in its cursor with an item present in another
|
||||||
|
// container. The two item stacks swap places.
|
||||||
|
type SwapStackRequestAction struct {
|
||||||
|
// Source and Destination point to the source slot from which Count of the item stack were taken and the
|
||||||
|
// destination slot to which this item was moved.
|
||||||
|
Source, Destination StackRequestSlotInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *SwapStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
StackReqSlotInfo(r, &a.Source)
|
||||||
|
StackReqSlotInfo(r, &a.Destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DropStackRequestAction is sent by the client when it drops an item out of the inventory when it has its
|
||||||
|
// inventory opened. This action is not sent when a player drops an item out of the hotbar using the Q button
|
||||||
|
// (or the equivalent on mobile). The InventoryTransaction packet is still used for that action, regardless of
|
||||||
|
// whether the item stack network IDs are used or not.
|
||||||
|
type DropStackRequestAction struct {
|
||||||
|
// Count is the count of the item in the source slot that was taken towards the destination slot.
|
||||||
|
Count byte
|
||||||
|
// Source is the source slot from which items were dropped to the ground.
|
||||||
|
Source StackRequestSlotInfo
|
||||||
|
// Randomly seems to be set to false in most cases. I'm not entirely sure what this does, but this is what
|
||||||
|
// vanilla calls this field.
|
||||||
|
Randomly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *DropStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Uint8(&a.Count)
|
||||||
|
StackReqSlotInfo(r, &a.Source)
|
||||||
|
r.Bool(&a.Randomly)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroyStackRequestAction is sent by the client when it destroys an item in creative mode by moving it
|
||||||
|
// back into the creative inventory.
|
||||||
|
type DestroyStackRequestAction struct {
|
||||||
|
// Count is the count of the item in the source slot that was destroyed.
|
||||||
|
Count byte
|
||||||
|
// Source is the source slot from which items came that were destroyed by moving them into the creative
|
||||||
|
// inventory.
|
||||||
|
Source StackRequestSlotInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *DestroyStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Uint8(&a.Count)
|
||||||
|
StackReqSlotInfo(r, &a.Source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsumeStackRequestAction is sent by the client when it uses an item to craft another item. The original
|
||||||
|
// item is 'consumed'.
|
||||||
|
type ConsumeStackRequestAction struct {
|
||||||
|
DestroyStackRequestAction
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateStackRequestAction is sent by the client when an item is created through being used as part of a
|
||||||
|
// recipe. For example, when milk is used to craft a cake, the buckets are leftover. The buckets are moved to
|
||||||
|
// the slot sent by the client here.
|
||||||
|
// Note that before this is sent, an action for consuming all items in the crafting table/grid is sent. Items
|
||||||
|
// that are not fully consumed when used for a recipe should not be destroyed there, but instead, should be
|
||||||
|
// turned into their respective resulting items.
|
||||||
|
type CreateStackRequestAction struct {
|
||||||
|
// ResultsSlot is the slot in the inventory in which the results of the crafting ingredients are to be
|
||||||
|
// placed.
|
||||||
|
ResultsSlot byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *CreateStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Uint8(&a.ResultsSlot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlaceInContainerStackRequestAction currently has no known purpose.
|
||||||
|
type PlaceInContainerStackRequestAction struct {
|
||||||
|
transferStackRequestAction
|
||||||
|
}
|
||||||
|
|
||||||
|
// TakeOutContainerStackRequestAction currently has no known purpose.
|
||||||
|
type TakeOutContainerStackRequestAction struct {
|
||||||
|
transferStackRequestAction
|
||||||
|
}
|
||||||
|
|
||||||
|
// LabTableCombineStackRequestAction is sent by the client when it uses a lab table to combine item stacks.
|
||||||
|
type LabTableCombineStackRequestAction struct{}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *LabTableCombineStackRequestAction) Marshal(protocol.IO) {}
|
||||||
|
|
||||||
|
// BeaconPaymentStackRequestAction is sent by the client when it submits an item to enable effects from a
|
||||||
|
// beacon. These items will have been moved into the beacon item slot in advance.
|
||||||
|
type BeaconPaymentStackRequestAction struct {
|
||||||
|
// PrimaryEffect and SecondaryEffect are the effects that were selected from the beacon.
|
||||||
|
PrimaryEffect, SecondaryEffect int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *BeaconPaymentStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Varint32(&a.PrimaryEffect)
|
||||||
|
r.Varint32(&a.SecondaryEffect)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MineBlockStackRequestAction is sent by the client when it breaks a block.
|
||||||
|
type MineBlockStackRequestAction struct {
|
||||||
|
// HotbarSlot is the slot held by the player while mining a block.
|
||||||
|
HotbarSlot int32
|
||||||
|
// PredictedDurability is the durability of the item that the client assumes to be present at the time.
|
||||||
|
PredictedDurability int32
|
||||||
|
// StackNetworkID is the unique stack ID that the client assumes to be present at the time. The server
|
||||||
|
// must check if these IDs match. If they do not match, servers should reject the stack request that the
|
||||||
|
// action holding this info was in.
|
||||||
|
StackNetworkID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *MineBlockStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Varint32(&a.HotbarSlot)
|
||||||
|
r.Varint32(&a.PredictedDurability)
|
||||||
|
r.Varint32(&a.StackNetworkID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftRecipeStackRequestAction is sent by the client the moment it begins crafting an item. This is the
|
||||||
|
// first action sent, before the Consume and Create item stack request actions.
|
||||||
|
// This action is also sent when an item is enchanted. Enchanting should be treated mostly the same way as
|
||||||
|
// crafting, where the old item is consumed.
|
||||||
|
type CraftRecipeStackRequestAction struct {
|
||||||
|
// RecipeNetworkID is the network ID of the recipe that is about to be crafted. This network ID matches
|
||||||
|
// one of the recipes sent in the CraftingData packet, where each of the recipes have a RecipeNetworkID as
|
||||||
|
// of 1.16.
|
||||||
|
RecipeNetworkID uint32
|
||||||
|
// NumberOfCrafts is how many times the recipe was crafted. This field appears to be boilerplate and
|
||||||
|
// has no effect.
|
||||||
|
NumberOfCrafts byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *CraftRecipeStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Varuint32(&a.RecipeNetworkID)
|
||||||
|
r.Uint8(&a.NumberOfCrafts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoCraftRecipeStackRequestAction is sent by the client similarly to the CraftRecipeStackRequestAction. The
|
||||||
|
// only difference is that the recipe is automatically created and crafted by shift clicking the recipe book.
|
||||||
|
type AutoCraftRecipeStackRequestAction struct {
|
||||||
|
// RecipeNetworkID is the network ID of the recipe that is about to be crafted. This network ID matches
|
||||||
|
// one of the recipes sent in the CraftingData packet, where each of the recipes have a RecipeNetworkID as
|
||||||
|
// of 1.16.
|
||||||
|
RecipeNetworkID uint32
|
||||||
|
// NumberOfCrafts is how many times the recipe was crafted. This field is just a duplicate of TimesCrafted.
|
||||||
|
NumberOfCrafts byte
|
||||||
|
// TimesCrafted is how many times the recipe was crafted.
|
||||||
|
TimesCrafted byte
|
||||||
|
// Ingredients is a slice of ItemDescriptorCount that contains the ingredients that were used to craft the recipe.
|
||||||
|
// It is not exactly clear what this is used for, but it is sent by the vanilla client.
|
||||||
|
Ingredients []protocol.ItemDescriptorCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *AutoCraftRecipeStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Varuint32(&a.RecipeNetworkID)
|
||||||
|
r.Uint8(&a.NumberOfCrafts)
|
||||||
|
r.Uint8(&a.TimesCrafted)
|
||||||
|
protocol.FuncSlice(r, &a.Ingredients, r.ItemDescriptorCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftCreativeStackRequestAction is sent by the client when it takes an item out fo the creative inventory.
|
||||||
|
// The item is thus not really crafted, but instantly created.
|
||||||
|
type CraftCreativeStackRequestAction struct {
|
||||||
|
// CreativeItemNetworkID is the network ID of the creative item that is being created. This is one of the
|
||||||
|
// creative item network IDs sent in the CreativeContent packet.
|
||||||
|
CreativeItemNetworkID uint32
|
||||||
|
// NumberOfCrafts is how many times the recipe was crafted. This field appears to be boilerplate and
|
||||||
|
// has no effect.
|
||||||
|
NumberOfCrafts byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *CraftCreativeStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Varuint32(&a.CreativeItemNetworkID)
|
||||||
|
r.Uint8(&a.NumberOfCrafts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftRecipeOptionalStackRequestAction is sent when using an anvil. When this action is sent, the
|
||||||
|
// FilterStrings field in the respective stack request is non-empty and contains the name of the item created
|
||||||
|
// using the anvil or cartography table.
|
||||||
|
type CraftRecipeOptionalStackRequestAction struct {
|
||||||
|
// RecipeNetworkID is the network ID of the multi-recipe that is about to be crafted. This network ID matches
|
||||||
|
// one of the multi-recipes sent in the CraftingData packet, where each of the recipes have a RecipeNetworkID as
|
||||||
|
// of 1.16.
|
||||||
|
RecipeNetworkID uint32
|
||||||
|
// FilterStringIndex is the index of a filter string sent in a ItemStackRequest.
|
||||||
|
FilterStringIndex int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (c *CraftRecipeOptionalStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Varuint32(&c.RecipeNetworkID)
|
||||||
|
r.Int32(&c.FilterStringIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftGrindstoneRecipeStackRequestAction is sent when a grindstone recipe is crafted. It contains the RecipeNetworkID
|
||||||
|
// to identify the recipe crafted, and the cost for crafting the recipe.
|
||||||
|
type CraftGrindstoneRecipeStackRequestAction struct {
|
||||||
|
// RecipeNetworkID is the network ID of the recipe that is about to be crafted. This network ID matches
|
||||||
|
// one of the recipes sent in the CraftingData packet, where each of the recipes have a RecipeNetworkID as
|
||||||
|
// of 1.16.
|
||||||
|
RecipeNetworkID uint32
|
||||||
|
// NumberOfCrafts is how many times the recipe was crafted. This field appears to be boilerplate and
|
||||||
|
// has no effect.
|
||||||
|
NumberOfCrafts byte
|
||||||
|
// Cost is the cost of the recipe that was crafted.
|
||||||
|
Cost int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (c *CraftGrindstoneRecipeStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.Varuint32(&c.RecipeNetworkID)
|
||||||
|
r.Uint8(&c.NumberOfCrafts)
|
||||||
|
r.Varint32(&c.Cost)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftLoomRecipeStackRequestAction is sent when a loom recipe is crafted. It simply contains the
|
||||||
|
// pattern identifier to figure out what pattern is meant to be applied to the item.
|
||||||
|
type CraftLoomRecipeStackRequestAction struct {
|
||||||
|
// Pattern is the pattern identifier for the loom recipe.
|
||||||
|
Pattern string
|
||||||
|
// TimesCrafted is how many times the recipe was crafted.
|
||||||
|
TimesCrafted byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (c *CraftLoomRecipeStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
r.String(&c.Pattern)
|
||||||
|
r.Uint8(&c.TimesCrafted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftNonImplementedStackRequestAction is an action sent for inventory actions that aren't yet implemented
|
||||||
|
// in the new system. These include, for example, anvils.
|
||||||
|
type CraftNonImplementedStackRequestAction struct{}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (*CraftNonImplementedStackRequestAction) Marshal(protocol.IO) {}
|
||||||
|
|
||||||
|
// CraftResultsDeprecatedStackRequestAction is an additional, deprecated packet sent by the client after
|
||||||
|
// crafting. It holds the final results and the amount of times the recipe was crafted. It shouldn't be used.
|
||||||
|
// This action is also sent when an item is enchanted. Enchanting should be treated mostly the same way as
|
||||||
|
// crafting, where the old item is consumed.
|
||||||
|
type CraftResultsDeprecatedStackRequestAction struct {
|
||||||
|
ResultItems []protocol.ItemStack
|
||||||
|
TimesCrafted byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal ...
|
||||||
|
func (a *CraftResultsDeprecatedStackRequestAction) Marshal(r protocol.IO) {
|
||||||
|
protocol.FuncSlice(r, &a.ResultItems, r.Item)
|
||||||
|
r.Uint8(&a.TimesCrafted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackRequestSlotInfo holds information on a specific slot client-side.
|
||||||
|
type StackRequestSlotInfo struct {
|
||||||
|
// Container is the FullContainerName that describes the container that the slot is in.
|
||||||
|
Container protocol.FullContainerName
|
||||||
|
// Slot is the index of the slot within the container with the ContainerID above.
|
||||||
|
Slot byte
|
||||||
|
// StackNetworkID is the unique stack ID that the client assumes to be present in this slot. The server
|
||||||
|
// must check if these IDs match. If they do not match, servers should reject the stack request that the
|
||||||
|
// action holding this info was in.
|
||||||
|
StackNetworkID int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackReqSlotInfo reads/writes a StackRequestSlotInfo x using IO r.
|
||||||
|
func StackReqSlotInfo(r protocol.IO, x *StackRequestSlotInfo) {
|
||||||
|
protocol.Single(r, &x.Container)
|
||||||
|
r.Uint8(&x.Slot)
|
||||||
|
r.Varint32(&x.StackNetworkID)
|
||||||
|
}
|
||||||
79
legacyver/proto/resource_pack.go
Normal file
79
legacyver/proto/resource_pack.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TexturePackInfo represents a texture pack's info sent over network. It holds information about the
|
||||||
|
// texture pack such as its name, description and version.
|
||||||
|
type TexturePackInfo struct {
|
||||||
|
// UUID is the UUID of the texture pack. Each texture pack downloaded must have a different UUID in
|
||||||
|
// order for the client to be able to handle them properly.
|
||||||
|
UUID uuid.UUID
|
||||||
|
// Version is the version of the texture pack. The client will cache texture packs sent by the server as
|
||||||
|
// long as they carry the same version. Sending a texture pack with a different version than previously
|
||||||
|
// will force the client to re-download it.
|
||||||
|
Version string
|
||||||
|
// Size is the total size in bytes that the texture pack occupies. This is the size of the compressed
|
||||||
|
// archive (zip) of the texture pack.
|
||||||
|
Size uint64
|
||||||
|
// ContentKey is the key used to decrypt the behaviour pack if it is encrypted. This is generally the case
|
||||||
|
// for marketplace texture packs.
|
||||||
|
ContentKey string
|
||||||
|
// SubPackName ...
|
||||||
|
SubPackName string
|
||||||
|
// ContentIdentity is another UUID for the resource pack, and is generally set for marketplace texture
|
||||||
|
// packs. It is also required for client-side validations when the resource pack is encrypted.
|
||||||
|
ContentIdentity string
|
||||||
|
// HasScripts specifies if the texture packs has any scripts in it. A client will only download the
|
||||||
|
// behaviour pack if it supports scripts, which, up to 1.11, only includes Windows 10.
|
||||||
|
HasScripts bool
|
||||||
|
// AddonPack specifies if the texture pack is from an addon.
|
||||||
|
AddonPack bool
|
||||||
|
// RTXEnabled specifies if the texture pack uses the raytracing technology introduced in 1.16.200.
|
||||||
|
RTXEnabled bool
|
||||||
|
// DownloadURL is a URL that the client can use to download the pack instead of the server sending it in
|
||||||
|
// chunks, which it will continue to do if this field is left empty.
|
||||||
|
DownloadURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *TexturePackInfo) ToLatest() protocol.TexturePackInfo {
|
||||||
|
return protocol.TexturePackInfo{
|
||||||
|
UUID: x.UUID,
|
||||||
|
Version: x.Version,
|
||||||
|
Size: x.Size,
|
||||||
|
ContentKey: x.ContentKey,
|
||||||
|
SubPackName: x.SubPackName,
|
||||||
|
ContentIdentity: x.ContentIdentity,
|
||||||
|
HasScripts: x.HasScripts,
|
||||||
|
AddonPack: x.AddonPack,
|
||||||
|
RTXEnabled: x.RTXEnabled,
|
||||||
|
DownloadURL: x.DownloadURL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes/decodes a TexturePackInfo.
|
||||||
|
func (x *TexturePackInfo) Marshal(r protocol.IO) {
|
||||||
|
if IsProtoGTE(r, ID766) {
|
||||||
|
r.UUID(&x.UUID)
|
||||||
|
} else {
|
||||||
|
if IsReader(r) {
|
||||||
|
uuidStr := ""
|
||||||
|
r.String(&uuidStr)
|
||||||
|
x.UUID = uuid.MustParse(uuidStr)
|
||||||
|
} else {
|
||||||
|
uuidStr := x.UUID.String()
|
||||||
|
r.String(&uuidStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.String(&x.Version)
|
||||||
|
r.Uint64(&x.Size)
|
||||||
|
r.String(&x.ContentKey)
|
||||||
|
r.String(&x.SubPackName)
|
||||||
|
r.String(&x.ContentIdentity)
|
||||||
|
r.Bool(&x.HasScripts)
|
||||||
|
r.Bool(&x.AddonPack)
|
||||||
|
r.Bool(&x.RTXEnabled)
|
||||||
|
r.String(&x.DownloadURL)
|
||||||
|
}
|
||||||
5
legacyver/proto/type.go
Normal file
5
legacyver/proto/type.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package proto
|
||||||
|
|
||||||
|
type legacyType struct {
|
||||||
|
ProtocolID int32
|
||||||
|
}
|
||||||
215
legacyver/protocol.go
Normal file
215
legacyver/protocol.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
package legacyver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/akmalfairuz/legacy-version/legacyver/legacypacket"
|
||||||
|
"github.com/akmalfairuz/legacy-version/legacyver/proto"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
packetPoolClient packet.Pool
|
||||||
|
packetPoolServer packet.Pool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
packetPoolClient = packet.NewClientPool()
|
||||||
|
packetPoolServer = packet.NewServerPool()
|
||||||
|
|
||||||
|
packetPoolServer[packet.IDItemStackResponse] = func() packet.Packet { return &legacypacket.ItemStackResponse{} }
|
||||||
|
packetPoolServer[packet.IDResourcePacksInfo] = func() packet.Packet { return &legacypacket.ResourcePacksInfo{} }
|
||||||
|
|
||||||
|
packetPoolClient[packet.IDPlayerAuthInput] = func() packet.Packet { return &legacypacket.PlayerAuthInput{} }
|
||||||
|
}
|
||||||
|
|
||||||
|
type Protocol struct {
|
||||||
|
ver string
|
||||||
|
id int32
|
||||||
|
|
||||||
|
blockTranslator BlockTranslator
|
||||||
|
itemTranslator ItemTranslator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) Ver() string {
|
||||||
|
return p.ver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) ID() int32 {
|
||||||
|
return p.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) Packets(listener bool) packet.Pool {
|
||||||
|
if listener {
|
||||||
|
return packetPoolClient
|
||||||
|
}
|
||||||
|
return packetPoolServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) NewReader(r minecraft.ByteReader, shieldID int32, enableLimits bool) protocol.IO {
|
||||||
|
return proto.NewReader(protocol.NewReader(r, shieldID, enableLimits), p.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) NewWriter(w minecraft.ByteWriter, shieldID int32) protocol.IO {
|
||||||
|
return proto.NewWriter(protocol.NewWriter(w, shieldID), p.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) ConvertToLatest(pk packet.Packet, conn *minecraft.Conn) []packet.Packet {
|
||||||
|
return p.blockTranslator.UpgradeBlockPackets(
|
||||||
|
p.itemTranslator.UpgradeItemPackets(p.upgradePackets([]packet.Packet{pk}, conn), conn),
|
||||||
|
conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) ConvertFromLatest(pk packet.Packet, conn *minecraft.Conn) []packet.Packet {
|
||||||
|
return p.downgradePackets(p.blockTranslator.DowngradeBlockPackets(
|
||||||
|
p.itemTranslator.DowngradeItemPackets([]packet.Packet{pk}, conn),
|
||||||
|
conn), conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) downgradePackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet {
|
||||||
|
for pkIndex, pk := range pks {
|
||||||
|
switch pk := pk.(type) {
|
||||||
|
case *packet.StartGame:
|
||||||
|
pk.GameVersion = p.ver
|
||||||
|
pk.BaseGameVersion = p.ver
|
||||||
|
case *packet.PlayerAuthInput:
|
||||||
|
inputData := pk.InputData
|
||||||
|
if p.ID() < proto.ID766 {
|
||||||
|
inputData = fitBitset(inputData, 64)
|
||||||
|
}
|
||||||
|
pks[pkIndex] = &legacypacket.PlayerAuthInput{
|
||||||
|
Pitch: pk.Pitch,
|
||||||
|
Yaw: pk.Yaw,
|
||||||
|
Position: pk.Position,
|
||||||
|
MoveVector: pk.MoveVector,
|
||||||
|
HeadYaw: pk.HeadYaw,
|
||||||
|
InputData: inputData,
|
||||||
|
InputMode: pk.InputMode,
|
||||||
|
PlayMode: pk.PlayMode,
|
||||||
|
InteractionModel: pk.InteractionModel,
|
||||||
|
InteractPitch: pk.InteractPitch,
|
||||||
|
InteractYaw: pk.InteractYaw,
|
||||||
|
Tick: pk.Tick,
|
||||||
|
Delta: pk.Delta,
|
||||||
|
ItemInteractionData: pk.ItemInteractionData,
|
||||||
|
ItemStackRequest: pk.ItemStackRequest,
|
||||||
|
BlockActions: pk.BlockActions,
|
||||||
|
VehicleRotation: pk.VehicleRotation,
|
||||||
|
ClientPredictedVehicle: pk.ClientPredictedVehicle,
|
||||||
|
AnalogueMoveVector: pk.AnalogueMoveVector,
|
||||||
|
CameraOrientation: pk.CameraOrientation,
|
||||||
|
RawMoveVector: pk.RawMoveVector,
|
||||||
|
}
|
||||||
|
case *packet.ItemStackResponse:
|
||||||
|
responses := make([]proto.ItemStackResponse, len(pk.Responses))
|
||||||
|
for i, r := range pk.Responses {
|
||||||
|
containerInfo := make([]proto.StackResponseContainerInfo, len(r.ContainerInfo))
|
||||||
|
for j, c := range r.ContainerInfo {
|
||||||
|
slotInfo := make([]proto.StackResponseSlotInfo, len(c.SlotInfo))
|
||||||
|
for k, s := range c.SlotInfo {
|
||||||
|
slotInfo[k] = proto.StackResponseSlotInfo{
|
||||||
|
Slot: s.Slot,
|
||||||
|
HotbarSlot: s.HotbarSlot,
|
||||||
|
Count: s.Count,
|
||||||
|
StackNetworkID: s.StackNetworkID,
|
||||||
|
CustomName: s.CustomName,
|
||||||
|
FilteredCustomName: s.FilteredCustomName,
|
||||||
|
DurabilityCorrection: s.DurabilityCorrection,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
containerInfo[j] = proto.StackResponseContainerInfo{
|
||||||
|
Container: c.Container,
|
||||||
|
SlotInfo: slotInfo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responses[i] = proto.ItemStackResponse{
|
||||||
|
Status: r.Status,
|
||||||
|
RequestID: r.RequestID,
|
||||||
|
ContainerInfo: containerInfo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pks[pkIndex] = &legacypacket.ItemStackResponse{Responses: responses}
|
||||||
|
case *packet.ResourcePacksInfo:
|
||||||
|
texturePacks := make([]proto.TexturePackInfo, len(pk.TexturePacks))
|
||||||
|
for i, t := range pk.TexturePacks {
|
||||||
|
texturePacks[i] = proto.TexturePackInfo{
|
||||||
|
UUID: t.UUID,
|
||||||
|
Version: t.Version,
|
||||||
|
Size: t.Size,
|
||||||
|
ContentKey: t.ContentKey,
|
||||||
|
SubPackName: t.SubPackName,
|
||||||
|
ContentIdentity: t.ContentIdentity,
|
||||||
|
HasScripts: t.HasScripts,
|
||||||
|
AddonPack: t.AddonPack,
|
||||||
|
RTXEnabled: t.RTXEnabled,
|
||||||
|
DownloadURL: t.DownloadURL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pks[pkIndex] = &legacypacket.ResourcePacksInfo{
|
||||||
|
TexturePackRequired: pk.TexturePackRequired,
|
||||||
|
HasAddons: pk.HasAddons,
|
||||||
|
HasScripts: pk.HasScripts,
|
||||||
|
WorldTemplateUUID: pk.WorldTemplateUUID,
|
||||||
|
WorldTemplateVersion: pk.WorldTemplateVersion,
|
||||||
|
TexturePacks: texturePacks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) upgradePackets(pks []packet.Packet, conn *minecraft.Conn) []packet.Packet {
|
||||||
|
for pkIndex, pk := range pks {
|
||||||
|
switch pk := pk.(type) {
|
||||||
|
case *packet.StartGame:
|
||||||
|
pk.GameVersion = p.ver
|
||||||
|
pk.BaseGameVersion = p.ver
|
||||||
|
case *legacypacket.PlayerAuthInput:
|
||||||
|
pks[pkIndex] = &packet.PlayerAuthInput{
|
||||||
|
Pitch: pk.Pitch,
|
||||||
|
Yaw: pk.Yaw,
|
||||||
|
Position: pk.Position,
|
||||||
|
MoveVector: pk.MoveVector,
|
||||||
|
HeadYaw: pk.HeadYaw,
|
||||||
|
InputData: fitBitset(pk.InputData, packet.PlayerAuthInputBitsetSize),
|
||||||
|
InputMode: pk.InputMode,
|
||||||
|
PlayMode: pk.PlayMode,
|
||||||
|
InteractionModel: pk.InteractionModel,
|
||||||
|
InteractPitch: pk.InteractPitch,
|
||||||
|
InteractYaw: pk.InteractYaw,
|
||||||
|
Tick: pk.Tick,
|
||||||
|
Delta: pk.Delta,
|
||||||
|
ItemInteractionData: pk.ItemInteractionData,
|
||||||
|
ItemStackRequest: pk.ItemStackRequest,
|
||||||
|
BlockActions: pk.BlockActions,
|
||||||
|
VehicleRotation: pk.VehicleRotation,
|
||||||
|
ClientPredictedVehicle: pk.ClientPredictedVehicle,
|
||||||
|
AnalogueMoveVector: pk.AnalogueMoveVector,
|
||||||
|
CameraOrientation: pk.CameraOrientation,
|
||||||
|
RawMoveVector: pk.RawMoveVector,
|
||||||
|
}
|
||||||
|
case *legacypacket.ItemStackResponse:
|
||||||
|
responses := make([]protocol.ItemStackResponse, len(pk.Responses))
|
||||||
|
for i, r := range pk.Responses {
|
||||||
|
responses[i] = r.ToLatest()
|
||||||
|
}
|
||||||
|
pks[pkIndex] = &packet.ItemStackResponse{Responses: responses}
|
||||||
|
case *legacypacket.ResourcePacksInfo:
|
||||||
|
texturePacks := make([]protocol.TexturePackInfo, len(pk.TexturePacks))
|
||||||
|
for i, t := range pk.TexturePacks {
|
||||||
|
texturePacks[i] = t.ToLatest()
|
||||||
|
}
|
||||||
|
pks[pkIndex] = &packet.ResourcePacksInfo{
|
||||||
|
TexturePackRequired: pk.TexturePackRequired,
|
||||||
|
HasAddons: pk.HasAddons,
|
||||||
|
HasScripts: pk.HasScripts,
|
||||||
|
WorldTemplateUUID: pk.WorldTemplateUUID,
|
||||||
|
WorldTemplateVersion: pk.WorldTemplateVersion,
|
||||||
|
TexturePacks: texturePacks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pks
|
||||||
|
}
|
||||||
6998
legacyver/required_item_list_748.json
Normal file
6998
legacyver/required_item_list_748.json
Normal file
File diff suppressed because it is too large
Load Diff
7158
legacyver/required_item_list_766.json
Normal file
7158
legacyver/required_item_list_766.json
Normal file
File diff suppressed because it is too large
Load Diff
36
legacyver/v748.go
Normal file
36
legacyver/v748.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package legacyver
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"github.com/akmalfairuz/legacy-version/internal/chunk"
|
||||||
|
"github.com/akmalfairuz/legacy-version/legacyver/proto"
|
||||||
|
"github.com/akmalfairuz/legacy-version/mapping"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ItemVersion748 ...
|
||||||
|
ItemVersion748 = 221
|
||||||
|
// BlockVersion748 ...
|
||||||
|
BlockVersion748 int32 = (1 << 24) | (21 << 16) | (40 << 8)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed item_runtime_ids_748.nbt
|
||||||
|
itemRuntimeIDData748 []byte
|
||||||
|
//go:embed required_item_list_748.json
|
||||||
|
requiredItemList748 []byte
|
||||||
|
//go:embed block_states_748.nbt
|
||||||
|
blockStateData748 []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func New748(direct bool) *Protocol {
|
||||||
|
itemMapping := mapping.NewItemMapping(itemRuntimeIDData748, requiredItemList748, ItemVersion748, false)
|
||||||
|
blockMapping := mapping.NewBlockMapping(blockStateData748)
|
||||||
|
|
||||||
|
return &Protocol{
|
||||||
|
ver: "1.21.40",
|
||||||
|
id: proto.ID748,
|
||||||
|
blockTranslator: NewBlockTranslator(blockMapping, latestBlockMapping, chunk.NewNetworkPersistentEncoding(blockMapping, BlockVersion748), chunk.NewBlockPaletteEncoding(blockMapping, BlockVersion748), false),
|
||||||
|
itemTranslator: NewItemTranslator(itemMapping, itemMappingLatest, blockMapping, blockMappingLatest),
|
||||||
|
}
|
||||||
|
}
|
||||||
25
legacyver/v766.go
Normal file
25
legacyver/v766.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package legacyver
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"github.com/akmalfairuz/legacy-version/mapping"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ItemVersion766 ...
|
||||||
|
ItemVersion766 = 231
|
||||||
|
// BlockVersion766 ...
|
||||||
|
BlockVersion766 int32 = (1 << 24) | (21 << 16) | (50 << 8)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed item_runtime_ids_766.nbt
|
||||||
|
itemRuntimeIDData766 []byte
|
||||||
|
//go:embed required_item_list_766.json
|
||||||
|
requiredItemList766 []byte
|
||||||
|
//go:embed block_states_766.nbt
|
||||||
|
blockStateData766 []byte
|
||||||
|
|
||||||
|
itemMappingLatest = mapping.NewItemMapping(itemRuntimeIDData766, requiredItemList766, ItemVersion766, false)
|
||||||
|
blockMappingLatest = mapping.NewBlockMapping(blockStateData766)
|
||||||
|
)
|
||||||
146
main.go
Normal file
146
main.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/akmalfairuz/legacy-version/legacyver"
|
||||||
|
"github.com/pelletier/go-toml"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/auth"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following program implements a proxy that forwards players from one local address to a remote address.
|
||||||
|
func main() {
|
||||||
|
config := readConfig()
|
||||||
|
token, err := auth.RequestLiveToken()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
src := auth.RefreshTokenSource(token)
|
||||||
|
|
||||||
|
p, err := minecraft.NewForeignStatusProvider(config.Connection.RemoteAddress)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
listener, err := minecraft.ListenConfig{
|
||||||
|
StatusProvider: p,
|
||||||
|
AcceptedProtocols: []minecraft.Protocol{
|
||||||
|
legacyver.New748(false),
|
||||||
|
},
|
||||||
|
}.Listen("raknet", config.Connection.LocalAddress)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
for {
|
||||||
|
c, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
go handleConn(c.(*minecraft.Conn), listener, config, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleConn handles a new incoming minecraft.Conn from the minecraft.Listener passed.
|
||||||
|
func handleConn(conn *minecraft.Conn, listener *minecraft.Listener, config config, src oauth2.TokenSource) {
|
||||||
|
serverConn, err := minecraft.Dialer{
|
||||||
|
TokenSource: src,
|
||||||
|
ClientData: conn.ClientData(),
|
||||||
|
}.Dial("raknet", config.Connection.RemoteAddress)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var g sync.WaitGroup
|
||||||
|
g.Add(2)
|
||||||
|
go func() {
|
||||||
|
if err := conn.StartGame(serverConn.GameData()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
g.Done()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
if err := serverConn.DoSpawn(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
g.Done()
|
||||||
|
}()
|
||||||
|
g.Wait()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer listener.Disconnect(conn, "connection lost")
|
||||||
|
defer serverConn.Close()
|
||||||
|
for {
|
||||||
|
pk, err := conn.ReadPacket()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := serverConn.WritePacket(pk); err != nil {
|
||||||
|
var disc minecraft.DisconnectError
|
||||||
|
if ok := errors.As(err, &disc); ok {
|
||||||
|
_ = listener.Disconnect(conn, disc.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer serverConn.Close()
|
||||||
|
defer listener.Disconnect(conn, "connection lost")
|
||||||
|
for {
|
||||||
|
pk, err := serverConn.ReadPacket()
|
||||||
|
if err != nil {
|
||||||
|
var disc minecraft.DisconnectError
|
||||||
|
if ok := errors.As(err, &disc); ok {
|
||||||
|
_ = listener.Disconnect(conn, disc.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := conn.WritePacket(pk); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
Connection struct {
|
||||||
|
LocalAddress string
|
||||||
|
RemoteAddress string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfig() config {
|
||||||
|
c := config{}
|
||||||
|
if _, err := os.Stat("config.toml"); os.IsNotExist(err) {
|
||||||
|
f, err := os.Create("config.toml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("create config: %v", err)
|
||||||
|
}
|
||||||
|
data, err := toml.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("encode default config: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := f.Write(data); err != nil {
|
||||||
|
log.Fatalf("write default config: %v", err)
|
||||||
|
}
|
||||||
|
_ = f.Close()
|
||||||
|
}
|
||||||
|
data, err := os.ReadFile("config.toml")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("read config: %v", err)
|
||||||
|
}
|
||||||
|
if err := toml.Unmarshal(data, &c); err != nil {
|
||||||
|
log.Fatalf("decode config: %v", err)
|
||||||
|
}
|
||||||
|
if c.Connection.LocalAddress == "" {
|
||||||
|
c.Connection.LocalAddress = "0.0.0.0:19132"
|
||||||
|
}
|
||||||
|
data, _ = toml.Marshal(c)
|
||||||
|
if err := os.WriteFile("config.toml", data, 0644); err != nil {
|
||||||
|
log.Fatalf("write config: %v", err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
136
mapping/block.go
Normal file
136
mapping/block.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package mapping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/akmalfairuz/legacy-version/internal"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/df-mc/worldupgrader/blockupgrader"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/nbt"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"github.com/segmentio/fasthash/fnv1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Block interface {
|
||||||
|
// StateToRuntimeID converts a block state to a runtime ID.
|
||||||
|
StateToRuntimeID(blockupgrader.BlockState) (uint32, bool)
|
||||||
|
// RuntimeIDToState converts a runtime ID to a name and its state properties.
|
||||||
|
RuntimeIDToState(uint32) (blockupgrader.BlockState, bool)
|
||||||
|
// DowngradeBlockActorData downgrades the input sub chunk to a legacy block actor.
|
||||||
|
DowngradeBlockActorData(map[string]any)
|
||||||
|
// UpgradeBlockActorData upgrades the input sub chunk to the latest block actor.
|
||||||
|
UpgradeBlockActorData(map[string]any)
|
||||||
|
// Adjust adjusts the latest mappings to account for custom states.
|
||||||
|
Adjust([]protocol.BlockEntry)
|
||||||
|
Air() uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultBlockMapping struct {
|
||||||
|
// states holds a list of all possible vanilla block states.
|
||||||
|
states []blockupgrader.BlockState
|
||||||
|
// stateRuntimeIDs holds a map for looking up the runtime ID of a block by the stateHash it produces.
|
||||||
|
stateRuntimeIDs map[internal.StateHash]uint32
|
||||||
|
// runtimeIDToState holds a map for looking up the blockState of a block by its runtime ID.
|
||||||
|
runtimeIDToState map[uint32]blockupgrader.BlockState
|
||||||
|
upgrader, downgrader func(map[string]any) map[string]any
|
||||||
|
|
||||||
|
// airRID is the runtime ID of the air block in the latest version of the game.
|
||||||
|
airRID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockMapping(raw []byte) *DefaultBlockMapping {
|
||||||
|
dec := nbt.NewDecoder(bytes.NewBuffer(raw))
|
||||||
|
|
||||||
|
var states []blockupgrader.BlockState
|
||||||
|
stateRuntimeIDs := make(map[internal.StateHash]uint32)
|
||||||
|
runtimeIDToState := make(map[uint32]blockupgrader.BlockState)
|
||||||
|
var airRID *uint32
|
||||||
|
|
||||||
|
var s blockupgrader.BlockState
|
||||||
|
for {
|
||||||
|
if err := dec.Decode(&s); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
rid := uint32(len(states))
|
||||||
|
states = append(states, s)
|
||||||
|
if s.Name == "minecraft:air" {
|
||||||
|
airRID = &rid
|
||||||
|
}
|
||||||
|
|
||||||
|
stateRuntimeIDs[internal.HashState(blockupgrader.Upgrade(s))] = rid
|
||||||
|
runtimeIDToState[rid] = s
|
||||||
|
}
|
||||||
|
if airRID == nil {
|
||||||
|
panic("couldn't find air")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DefaultBlockMapping{
|
||||||
|
states: states,
|
||||||
|
stateRuntimeIDs: stateRuntimeIDs,
|
||||||
|
runtimeIDToState: runtimeIDToState,
|
||||||
|
airRID: *airRID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultBlockMapping) WithBlockActorRemapper(downgrader, upgrader func(map[string]any) map[string]any) *DefaultBlockMapping {
|
||||||
|
m.downgrader = downgrader
|
||||||
|
m.upgrader = upgrader
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultBlockMapping) StateToRuntimeID(state blockupgrader.BlockState) (uint32, bool) {
|
||||||
|
rid, ok := m.stateRuntimeIDs[internal.HashState(blockupgrader.Upgrade(state))]
|
||||||
|
return rid, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultBlockMapping) RuntimeIDToState(runtimeId uint32) (blockupgrader.BlockState, bool) {
|
||||||
|
state, found := m.runtimeIDToState[runtimeId]
|
||||||
|
return state, found
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultBlockMapping) DowngradeBlockActorData(actorData map[string]any) {
|
||||||
|
if m.downgrader != nil {
|
||||||
|
m.downgrader(actorData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultBlockMapping) UpgradeBlockActorData(actorData map[string]any) {
|
||||||
|
if m.upgrader != nil {
|
||||||
|
m.upgrader(actorData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultBlockMapping) Adjust(entries []protocol.BlockEntry) {
|
||||||
|
if len(entries) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
customStates := convert(entries)
|
||||||
|
var newStates []blockupgrader.BlockState
|
||||||
|
for _, state := range customStates {
|
||||||
|
if _, ok := m.StateToRuntimeID(state); !ok {
|
||||||
|
newStates = append(newStates, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(newStates) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
adjustedStates := append(m.states, customStates...)
|
||||||
|
sort.SliceStable(adjustedStates, func(i, j int) bool {
|
||||||
|
stateOne, stateTwo := adjustedStates[i], adjustedStates[j]
|
||||||
|
return stateOne.Name != stateTwo.Name && fnv1.HashString64(stateOne.Name) < fnv1.HashString64(stateTwo.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
m.stateRuntimeIDs = make(map[internal.StateHash]uint32, len(adjustedStates))
|
||||||
|
m.runtimeIDToState = make(map[uint32]blockupgrader.BlockState, len(adjustedStates))
|
||||||
|
for rid, state := range adjustedStates {
|
||||||
|
m.stateRuntimeIDs[internal.HashState(blockupgrader.Upgrade(state))] = uint32(rid)
|
||||||
|
m.runtimeIDToState[uint32(rid)] = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultBlockMapping) Air() uint32 {
|
||||||
|
return m.airRID
|
||||||
|
}
|
||||||
84
mapping/custom_blocks.go
Normal file
84
mapping/custom_blocks.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package mapping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/akmalfairuz/legacy-version/internal"
|
||||||
|
"github.com/df-mc/worldupgrader/blockupgrader"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
func convert(entries []protocol.BlockEntry) (states []blockupgrader.BlockState) {
|
||||||
|
for _, entry := range entries {
|
||||||
|
propertiesMap := map[string][]any{}
|
||||||
|
if props := jsonCheck[[]any](entry.Properties, "properties"); props != nil {
|
||||||
|
for _, prop := range *props {
|
||||||
|
prop := prop.(map[string]any)
|
||||||
|
name := jsonCheck[string](prop, "name")
|
||||||
|
enum := jsonCheck[[]any](prop, "enum")
|
||||||
|
if enum == nil {
|
||||||
|
int32Enum := jsonCheck[[]int32](prop, "enum")
|
||||||
|
if int32Enum != nil {
|
||||||
|
enum = &[]any{}
|
||||||
|
for _, i := range *int32Enum {
|
||||||
|
*enum = append(*enum, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if name == nil || enum == nil {
|
||||||
|
panic("could not find field `name` and `enum`")
|
||||||
|
}
|
||||||
|
propertiesMap[*name] = *enum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
combinations := make([]map[string]any, 0)
|
||||||
|
generateCombinationsRecursively(propertiesMap, internal.NewIterator(maps.Keys(propertiesMap)), map[string]any{}, &combinations)
|
||||||
|
if len(combinations) == 0 {
|
||||||
|
combinations = append(combinations, map[string]any{})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, combination := range combinations {
|
||||||
|
blockState := blockupgrader.BlockState{
|
||||||
|
Name: entry.Name,
|
||||||
|
Properties: combination,
|
||||||
|
}
|
||||||
|
|
||||||
|
states = append(states, blockState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCombinationsRecursively[K comparable, V any](all map[K][]V, iterator *internal.Iterator[K], current map[K]V, output *[]map[K]V) {
|
||||||
|
if !iterator.HasNext() {
|
||||||
|
entry := map[K]V{}
|
||||||
|
for k, v := range current {
|
||||||
|
entry[k] = v
|
||||||
|
}
|
||||||
|
out := append(*output, entry)
|
||||||
|
*output = out
|
||||||
|
} else {
|
||||||
|
key := iterator.Next()
|
||||||
|
set := all[key]
|
||||||
|
|
||||||
|
for _, value := range set {
|
||||||
|
current[key] = value
|
||||||
|
generateCombinationsRecursively(all, iterator, current, output)
|
||||||
|
delete(current, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Previous()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonCheck[T any](json map[string]any, field string) *T {
|
||||||
|
fieldValue, found := json[field]
|
||||||
|
if !found {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
castedValue, ok := fieldValue.(T)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &castedValue
|
||||||
|
}
|
||||||
109
mapping/item.go
Normal file
109
mapping/item.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package mapping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/nbt"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Item interface {
|
||||||
|
// ItemRuntimeIDToName converts an item runtime ID to a string ID.
|
||||||
|
ItemRuntimeIDToName(int32) (string, bool)
|
||||||
|
// ItemNameToRuntimeID converts a string ID to an item runtime ID.
|
||||||
|
ItemNameToRuntimeID(string) (int32, bool)
|
||||||
|
RegisterEntry(string) int32
|
||||||
|
Air() int32
|
||||||
|
ItemVersion() uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultItemMapping struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
// itemRuntimeIDsToNames holds a map to translate item runtime IDs to string IDs.
|
||||||
|
itemRuntimeIDsToNames map[int32]string
|
||||||
|
// itemNamesToRuntimeIDs holds a map to translate item string IDs to runtime IDs.
|
||||||
|
itemNamesToRuntimeIDs map[string]int32
|
||||||
|
airRID int32
|
||||||
|
itemVersion uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewItemMapping(itemRuntimeIDData []byte, requiredItemList []byte, itemVersion uint16, direct bool) *DefaultItemMapping {
|
||||||
|
itemRuntimeIDsToNames := make(map[int32]string)
|
||||||
|
itemNamesToRuntimeIDs := make(map[string]int32)
|
||||||
|
var airRID *int32
|
||||||
|
|
||||||
|
if direct {
|
||||||
|
var items map[string]int32
|
||||||
|
if err := nbt.Unmarshal(itemRuntimeIDData, &items); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for name, rid := range items {
|
||||||
|
if name == "minecraft:air" {
|
||||||
|
airRID = &rid
|
||||||
|
}
|
||||||
|
|
||||||
|
itemNamesToRuntimeIDs[name] = rid
|
||||||
|
itemRuntimeIDsToNames[rid] = name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var m map[string]struct {
|
||||||
|
RuntimeID int16 `json:"runtime_id"`
|
||||||
|
ComponentBased bool `json:"component_based"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(requiredItemList, &m); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for name, data := range m {
|
||||||
|
rid := int32(data.RuntimeID)
|
||||||
|
if name == "minecraft:air" {
|
||||||
|
airRID = &rid
|
||||||
|
}
|
||||||
|
|
||||||
|
itemNamesToRuntimeIDs[name] = rid
|
||||||
|
itemRuntimeIDsToNames[rid] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if airRID == nil {
|
||||||
|
panic("couldn't find air")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DefaultItemMapping{itemRuntimeIDsToNames: itemRuntimeIDsToNames, itemNamesToRuntimeIDs: itemNamesToRuntimeIDs, itemVersion: itemVersion}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultItemMapping) ItemRuntimeIDToName(runtimeID int32) (name string, found bool) {
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.mu.Lock()
|
||||||
|
name, ok := m.itemRuntimeIDsToNames[runtimeID]
|
||||||
|
return name, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultItemMapping) ItemNameToRuntimeID(name string) (runtimeID int32, found bool) {
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.mu.Lock()
|
||||||
|
rid, ok := m.itemNamesToRuntimeIDs[name]
|
||||||
|
return rid, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultItemMapping) RegisterEntry(name string) int32 {
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.mu.Lock()
|
||||||
|
if rid, ok := m.itemNamesToRuntimeIDs[name]; ok {
|
||||||
|
return rid
|
||||||
|
}
|
||||||
|
nextRID := int32(len(m.itemRuntimeIDsToNames))
|
||||||
|
m.itemNamesToRuntimeIDs[name] = nextRID
|
||||||
|
m.itemRuntimeIDsToNames[nextRID] = name
|
||||||
|
return nextRID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultItemMapping) Air() int32 {
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.mu.Lock()
|
||||||
|
return m.airRID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DefaultItemMapping) ItemVersion() uint16 {
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
m.mu.Lock()
|
||||||
|
return m.itemVersion
|
||||||
|
}
|
||||||
71
packbuilder/builder.go
Normal file
71
packbuilder/builder.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package packbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/df-mc/dragonfly/server/item/category"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ComponentBuilder represents a builder that can be used to construct an item components map to be sent to a client.
|
||||||
|
type ComponentBuilder struct {
|
||||||
|
name string
|
||||||
|
identifier string
|
||||||
|
category category.Category
|
||||||
|
|
||||||
|
properties map[string]any
|
||||||
|
components map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewComponentBuilder returns a new component builder with the provided item data.
|
||||||
|
func NewComponentBuilder(name, identifier string, category category.Category) *ComponentBuilder {
|
||||||
|
return &ComponentBuilder{
|
||||||
|
name: name,
|
||||||
|
identifier: identifier,
|
||||||
|
category: category,
|
||||||
|
|
||||||
|
properties: make(map[string]any),
|
||||||
|
components: make(map[string]any),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProperty adds the provided property to the builder.
|
||||||
|
func (builder *ComponentBuilder) AddProperty(name string, value any) {
|
||||||
|
builder.properties[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddComponent adds the provided component to the builder.
|
||||||
|
func (builder *ComponentBuilder) AddComponent(name string, value any) {
|
||||||
|
builder.components[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct constructs the final item components map and returns it. It also applies the default properties required
|
||||||
|
// for the item to work without modifying the original maps in the builder.
|
||||||
|
func (builder *ComponentBuilder) Construct() map[string]any {
|
||||||
|
properties := maps.Clone(builder.properties)
|
||||||
|
components := maps.Clone(builder.components)
|
||||||
|
builder.applyDefaultProperties(properties)
|
||||||
|
builder.applyDefaultComponents(components, properties)
|
||||||
|
return map[string]any{"components": components}
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyDefaultProperties applies the default properties to the provided map. It is important that this method does
|
||||||
|
// not modify the builder's properties map directly otherwise Empty() will return false in future use of the builder.
|
||||||
|
func (builder *ComponentBuilder) applyDefaultProperties(x map[string]any) {
|
||||||
|
x["minecraft:icon"] = map[string]any{
|
||||||
|
"texture": strings.Split(builder.identifier, ":")[1],
|
||||||
|
}
|
||||||
|
x["creative_group"] = builder.category.Group()
|
||||||
|
x["creative_category"] = int32(builder.category.Uint8())
|
||||||
|
if _, ok := x["max_stack_size"]; !ok {
|
||||||
|
x["max_stack_size"] = int32(64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyDefaultComponents applies the default components to the provided map. It is important that this method does not
|
||||||
|
// modify the builder's components map directly otherwise Empty() will return false in future use of the builder.
|
||||||
|
func (builder *ComponentBuilder) applyDefaultComponents(x, properties map[string]any) {
|
||||||
|
x["item_properties"] = properties
|
||||||
|
x["minecraft:display_name"] = map[string]any{
|
||||||
|
"value": builder.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
119
packbuilder/components.go
Normal file
119
packbuilder/components.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package packbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/df-mc/dragonfly/server/item"
|
||||||
|
"github.com/df-mc/dragonfly/server/world"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Components returns all the components of the given custom item. If the item has no components, a nil map and false
|
||||||
|
// are returned.
|
||||||
|
func Components(it world.CustomItem) map[string]any {
|
||||||
|
category := it.Category()
|
||||||
|
identifier, _ := it.EncodeItem()
|
||||||
|
name := strings.Split(identifier, ":")[1]
|
||||||
|
|
||||||
|
builder := NewComponentBuilder(it.Name(), identifier, category)
|
||||||
|
|
||||||
|
if x, ok := it.(item.Armour); ok {
|
||||||
|
builder.AddComponent("minecraft:armor", map[string]any{
|
||||||
|
"protection": int32(x.DefencePoints()),
|
||||||
|
})
|
||||||
|
builder.AddComponent("minecraft:knockback_resistance", map[string]any{
|
||||||
|
"protection": float32(x.KnockBackResistance()),
|
||||||
|
})
|
||||||
|
|
||||||
|
var slot string
|
||||||
|
switch it.(type) {
|
||||||
|
case item.HelmetType:
|
||||||
|
slot = "slot.armor.head"
|
||||||
|
case item.ChestplateType:
|
||||||
|
slot = "slot.armor.chest"
|
||||||
|
case item.LeggingsType:
|
||||||
|
slot = "slot.armor.legs"
|
||||||
|
case item.BootsType:
|
||||||
|
slot = "slot.armor.feet"
|
||||||
|
}
|
||||||
|
builder.AddComponent("minecraft:wearable", map[string]any{
|
||||||
|
"slot": slot,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if x, ok := it.(item.Consumable); ok {
|
||||||
|
builder.AddProperty("use_duration", int32(x.ConsumeDuration().Seconds()*20))
|
||||||
|
builder.AddComponent("minecraft:food", map[string]any{
|
||||||
|
"can_always_eat": x.AlwaysConsumable(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if y, ok := it.(item.Drinkable); ok && y.Drinkable() {
|
||||||
|
builder.AddProperty("use_animation", int32(2))
|
||||||
|
} else {
|
||||||
|
builder.AddProperty("use_animation", int32(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if x, ok := it.(item.Cooldown); ok {
|
||||||
|
builder.AddComponent("minecraft:cooldown", map[string]any{
|
||||||
|
"category": name,
|
||||||
|
"duration": float32(x.Cooldown().Seconds()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if x, ok := it.(item.Durable); ok {
|
||||||
|
builder.AddComponent("minecraft:durability", map[string]any{
|
||||||
|
"max_durability": int32(x.DurabilityInfo().MaxDurability),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if x, ok := it.(item.MaxCounter); ok {
|
||||||
|
builder.AddProperty("max_stack_size", int32(x.MaxCount()))
|
||||||
|
}
|
||||||
|
if x, ok := it.(item.OffHand); ok {
|
||||||
|
builder.AddProperty("allow_off_hand", x.OffHand())
|
||||||
|
}
|
||||||
|
if x, ok := it.(item.Throwable); ok {
|
||||||
|
// The data in minecraft:projectile is only used by vanilla server-side, but we must send at least an empty map
|
||||||
|
// so the client will play the throwing animation.
|
||||||
|
builder.AddComponent("minecraft:projectile", map[string]any{})
|
||||||
|
builder.AddComponent("minecraft:throwable", map[string]any{
|
||||||
|
"do_swing_animation": x.SwingAnimation(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if x, ok := it.(item.Glinted); ok {
|
||||||
|
builder.AddProperty("foil", x.Glinted())
|
||||||
|
}
|
||||||
|
if x, ok := it.(item.HandEquipped); ok {
|
||||||
|
builder.AddProperty("hand_equipped", x.HandEquipped())
|
||||||
|
}
|
||||||
|
itemScale := calculateItemScale(it)
|
||||||
|
builder.AddComponent("minecraft:render_offsets", map[string]any{
|
||||||
|
"main_hand": map[string]any{
|
||||||
|
"first_person": map[string]any{
|
||||||
|
"scale": itemScale,
|
||||||
|
},
|
||||||
|
"third_person": map[string]any{
|
||||||
|
"scale": itemScale,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"off_hand": map[string]any{
|
||||||
|
"first_person": map[string]any{
|
||||||
|
"scale": itemScale,
|
||||||
|
},
|
||||||
|
"third_person": map[string]any{
|
||||||
|
"scale": itemScale,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return builder.Construct()
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateItemScale calculates the scale of the item to be rendered to the player according to the given size.
|
||||||
|
func calculateItemScale(it world.CustomItem) []float32 {
|
||||||
|
width := float32(it.Texture().Bounds().Dx())
|
||||||
|
height := float32(it.Texture().Bounds().Dy())
|
||||||
|
var x, y, z float32 = 0.1, 0.1, 0.1
|
||||||
|
if _, ok := it.(item.HandEquipped); ok {
|
||||||
|
x, y, z = 0.075, 0.125, 0.075
|
||||||
|
}
|
||||||
|
newX := x / (width / 16)
|
||||||
|
newY := y / (height / 16)
|
||||||
|
newZ := z / (width / 16)
|
||||||
|
return []float32{newX, newY, newZ}
|
||||||
|
}
|
||||||
81
packbuilder/items.go
Normal file
81
packbuilder/items.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package packbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/df-mc/dragonfly/server/world"
|
||||||
|
"golang.org/x/image/colornames"
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
_ "unsafe" // Imported for compiler directives.
|
||||||
|
)
|
||||||
|
|
||||||
|
// buildItems builds all the item-related files for the resource pack. This includes textures, language
|
||||||
|
// entries and item atlas.
|
||||||
|
func buildItems(dir string, customItems []world.CustomItem) (count int, lang []string) {
|
||||||
|
if err := os.Mkdir(filepath.Join(dir, "items"), os.ModePerm); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(filepath.Join(dir, "textures/items"), os.ModePerm); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
textureData := make(map[string]any)
|
||||||
|
for _, item := range customItems {
|
||||||
|
identifier, _ := item.EncodeItem()
|
||||||
|
lang = append(lang, fmt.Sprintf("item.%s.name=%s", identifier, item.Name()))
|
||||||
|
|
||||||
|
name := strings.Split(identifier, ":")[1]
|
||||||
|
textureData[name] = map[string]string{"textures": fmt.Sprintf("textures/items/%s.png", name)}
|
||||||
|
|
||||||
|
buildItemTexture(dir, name, item.Texture())
|
||||||
|
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
buildItemAtlas(dir, map[string]any{
|
||||||
|
"resource_pack_name": "vanilla",
|
||||||
|
"texture_name": "atlas.items",
|
||||||
|
"texture_data": textureData,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildItemTexture creates a PNG file for the item from the provided image and name and writes it to the pack.
|
||||||
|
func buildItemTexture(dir, name string, img image.Image) {
|
||||||
|
texture, err := os.Create(filepath.Join(dir, "textures/items", name+".png"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if img == nil {
|
||||||
|
im := image.NewAlpha(image.Rect(0, 0, 64, 64))
|
||||||
|
for x := 0; x < im.Bounds().Dx(); x++ {
|
||||||
|
for y := 0; y < im.Bounds().Dy(); y++ {
|
||||||
|
im.Set(x, y, colornames.Goldenrod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img = im
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := png.Encode(texture, img); err != nil {
|
||||||
|
_ = texture.Close()
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := texture.Close(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildItemAtlas creates the identifier to texture mapping and writes it to the pack.
|
||||||
|
func buildItemAtlas(dir string, atlas map[string]any) {
|
||||||
|
b, err := json.Marshal(atlas)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "textures/item_texture.json"), b, 0666); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
17
packbuilder/lang.go
Normal file
17
packbuilder/lang.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package packbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// buildLanguageFile creates a lang file and writes all of the language entries to the pack.
|
||||||
|
func buildLanguageFile(dir string, lang []string) {
|
||||||
|
if err := os.Mkdir(filepath.Join(dir, "texts"), os.ModePerm); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "texts/en_US.lang"), []byte(strings.Join(lang, "\n")), 0666); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
52
packbuilder/manifest.go
Normal file
52
packbuilder/manifest.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package packbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/resource"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// buildManifest creates a JSON manifest file for the client to be able to read the resource pack. It creates
|
||||||
|
// basic information and writes it to the pack.
|
||||||
|
func buildManifest(dir, version string, headerUUID, moduleUUID uuid.UUID) {
|
||||||
|
m, err := json.Marshal(resource.Manifest{
|
||||||
|
FormatVersion: 2,
|
||||||
|
Header: resource.Header{
|
||||||
|
Name: "dragonfly auto-generated resource pack",
|
||||||
|
Description: "This resource pack contains auto-generated content from dragonfly",
|
||||||
|
UUID: headerUUID,
|
||||||
|
Version: [3]int{0, 0, 1},
|
||||||
|
MinimumGameVersion: parseVersion(version),
|
||||||
|
},
|
||||||
|
Modules: []resource.Module{
|
||||||
|
{
|
||||||
|
UUID: moduleUUID.String(),
|
||||||
|
Description: "This resource pack contains auto-generated content from dragonfly",
|
||||||
|
Type: "resources",
|
||||||
|
Version: [3]int{0, 0, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "manifest.json"), m, 0666); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseVersion parses the version passed in the format of a.b.c as a [3]int.
|
||||||
|
func parseVersion(ver string) [3]int {
|
||||||
|
frag := strings.Split(ver, ".")
|
||||||
|
if len(frag) != 3 {
|
||||||
|
panic("invalid version number " + ver)
|
||||||
|
}
|
||||||
|
a, _ := strconv.ParseInt(frag[0], 10, 64)
|
||||||
|
b, _ := strconv.ParseInt(frag[1], 10, 64)
|
||||||
|
c, _ := strconv.ParseInt(frag[2], 10, 64)
|
||||||
|
return [3]int{int(a), int(b), int(c)}
|
||||||
|
}
|
||||||
40
packbuilder/resource_pack.go
Normal file
40
packbuilder/resource_pack.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package packbuilder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/df-mc/dragonfly/server/world"
|
||||||
|
"github.com/rogpeppe/go-internal/dirhash"
|
||||||
|
"github.com/sandertv/gophertunnel/minecraft/resource"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BuildResourcePack builds a resource pack based on custom features that have been registered to the server.
|
||||||
|
// It creates a UUID based on the hash of the directory so the client will only be prompted to download it
|
||||||
|
// once it is changed.
|
||||||
|
func BuildResourcePack(customItems []world.CustomItem, version string) (*resource.Pack, bool) {
|
||||||
|
dir, err := os.MkdirTemp("", "dragonfly_resource_pack-")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
var assets int
|
||||||
|
var lang []string
|
||||||
|
|
||||||
|
itemCount, itemLang := buildItems(dir, customItems)
|
||||||
|
assets += itemCount
|
||||||
|
lang = append(lang, itemLang...)
|
||||||
|
|
||||||
|
if assets > 0 {
|
||||||
|
buildLanguageFile(dir, lang)
|
||||||
|
hash, err := dirhash.HashDir(dir, "", dirhash.Hash1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var header, module [16]byte
|
||||||
|
copy(header[:], hash)
|
||||||
|
copy(module[:], hash[16:])
|
||||||
|
buildManifest(dir, version, header, module)
|
||||||
|
return resource.MustReadPath(dir), true
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user