13 Commits
Author SHA1 Message Date
adman 9983062522 fix timer 2026-03-26 22:39:48 +01:00
adman 4fd5324064 new event system (again) 2026-03-26 22:19:03 +01:00
adman df0e5e354b more srv stuff and packet inspetor 2026-03-25 19:00:18 +01:00
adman 23b5a5d9b7 move cmd to more sensible location 2026-03-25 14:53:38 +01:00
adman 46770ed980 Account changes
- now supports custom enviorments/servers
- cleaner code imo
2026-03-25 14:48:42 +01:00
adman 6179c5f6b6 fix proxy 2026-03-24 22:36:14 +01:00
adman f758a10575 performance fix 2026-03-24 21:50:52 +01:00
adman 41d1ab4c45 performance 2026-03-24 17:46:23 +01:00
adman 032fa26927 fix some race conditions 2026-03-22 22:51:29 +01:00
adman f7401a287e minecraft data raw code 2026-03-22 19:49:37 +01:00
adman 2d1e8ef4f6 go get does not support git submodules 2026-03-22 19:48:21 +01:00
adman 2802cc36ce litematic format 2026-03-22 19:45:05 +01:00
adman ea024fe808 look at the code if you want to know what i did 2026-03-22 00:35:07 +01:00
1171 changed files with 14386853 additions and 363 deletions
-3
View File
@@ -1,3 +0,0 @@
[submodule "data/minecraft-data"]
path = data/minecraft-data
url = git@github.com:PrismarineJS/minecraft-data.git
+64 -37
View File
@@ -9,32 +9,40 @@ import (
"strings"
"time"
"github.com/admin-else/strom/crypto"
"github.com/google/uuid"
)
type Servers struct {
ServicesHost string
SessionHost string
ProfilesHost string
}
var DefaultServers = &Servers{
ServicesHost: "https://api.minecraftservices.com",
SessionHost: "https://sessionserver.mojang.com",
ProfilesHost: "https://api.mojang.com",
}
type Account struct {
*Servers
Name string
Uuid uuid.UUID
Ygg string
}
const (
BaseUrlApi = "https://api.minecraftservices.com"
)
func (a *Account) doMcApiRequest(method string, url string, from any, to any) (err error) {
var b []byte
if method != "GET" {
b, err = json.Marshal(from)
if err != nil {
return err
return
}
}
r, err := http.NewRequest(method, url, bytes.NewReader(b))
if err != nil {
return err
return
}
r.Header.Set("Content-Type", "application/json")
@@ -44,21 +52,22 @@ func (a *Account) doMcApiRequest(method string, url string, from any, to any) (e
resp, err := http.DefaultClient.Do(r)
if err != nil {
return err
return
}
defer resp.Body.Close()
if !(resp.StatusCode >= 200 && resp.StatusCode <= 299) {
return fmt.Errorf("bad status code %v", resp.StatusCode)
}
b, err = io.ReadAll(resp.Body)
if err != nil {
return err
return
}
err = json.Unmarshal(b, to)
if err != nil {
return err
return
}
return nil
return
}
type PlayerKeys struct {
@@ -73,7 +82,7 @@ type PlayerKeys struct {
}
func (a *Account) FetchKeys() (keys PlayerKeys, err error) {
err = a.doMcApiRequest("POST", BaseUrlApi+"/player/certificates", nil, &keys)
err = a.doMcApiRequest("POST", a.ServicesHost+"/player/certificates", nil, &keys)
return
}
@@ -90,10 +99,12 @@ func (a *Account) JoinServer(serverId string) (err error) {
if err != nil {
return
}
resp, err := http.Post("https://sessionserver.mojang.com/session/minecraft/join", "application/json", bytes.NewReader(body))
resp, err := http.Post(a.SessionHost+"/session/minecraft/join", "application/json", bytes.NewReader(body))
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode != 204 {
body, err = io.ReadAll(resp.Body)
if err != nil {
@@ -112,17 +123,10 @@ type NameChangResp struct {
}
func (a *Account) NameChangeInfo() (r NameChangResp, err error) {
err = a.doMcApiRequest("GET", "https://api.minecraftservices.com/minecraft/profile/namechange", nil, &r)
err = a.doMcApiRequest("GET", a.ServicesHost+"/minecraft/profile/namechange", nil, &r)
return
}
func NewOfflineAccount(name string) *Account {
return &Account{
Name: name,
Uuid: crypto.FromOfflinePlayer(name),
}
}
// PlayerProfile represents the player's profile information
type PlayerProfile struct {
ID string `json:"id"`
@@ -143,7 +147,7 @@ type PlayerProfile struct {
// QueryProfile queries the player's profile
func (a *Account) QueryProfile() (profile PlayerProfile, err error) {
err = a.doMcApiRequest("GET", BaseUrlApi+"/minecraft/profile", nil, &profile)
err = a.doMcApiRequest("GET", a.ServicesHost+"/minecraft/profile", nil, &profile)
return
}
@@ -181,7 +185,7 @@ type PlayerAttributes struct {
// QueryAttributes queries the player's attributes
func (a *Account) QueryAttributes() (attrs PlayerAttributes, err error) {
err = a.doMcApiRequest("GET", BaseUrlApi+"/player/attributes", nil, &attrs)
err = a.doMcApiRequest("GET", a.ServicesHost+"/player/attributes", nil, &attrs)
return
}
@@ -194,7 +198,7 @@ type ModifyAttributesRequest struct {
// ModifyAttributes modifies the player's attributes
func (a *Account) ModifyAttributes(req ModifyAttributesRequest) (attrs PlayerAttributes, err error) {
err = a.doMcApiRequest("POST", BaseUrlApi+"/player/attributes", req, &attrs)
err = a.doMcApiRequest("POST", a.ServicesHost+"/player/attributes", req, &attrs)
return
}
@@ -205,24 +209,23 @@ type BlockedUsers struct {
// GetBlocklist gets the list of blocked users
func (a *Account) GetBlocklist() (blocklist BlockedUsers, err error) {
err = a.doMcApiRequest("GET", BaseUrlApi+"/privacy/blocklist", nil, &blocklist)
err = a.doMcApiRequest("GET", a.ServicesHost+"/privacy/blocklist", nil, &blocklist)
return
}
// CheckNameAvailability checks if a name is available
type NameAvailability struct {
Status string `json:"status"` // DUPLICATE, AVAILABLE, NOT_ALLOWED
}
// CheckNameAvailability checks if a name is available
func (a *Account) CheckNameAvailability(name string) (avail NameAvailability, err error) {
err = a.doMcApiRequest("GET", BaseUrlApi+"/minecraft/profile/name/"+name+"/available", nil, &avail)
err = a.doMcApiRequest("GET", a.ServicesHost+"/minecraft/profile/name/"+name+"/available", nil, &avail)
return
}
// ChangeName changes the player's name
func (a *Account) ChangeName(newName string) (profile PlayerProfile, err error) {
err = a.doMcApiRequest("PUT", BaseUrlApi+"/minecraft/profile/name/"+newName, nil, &profile)
err = a.doMcApiRequest("PUT", a.ServicesHost+"/minecraft/profile/name/"+newName, nil, &profile)
return
}
@@ -234,19 +237,19 @@ type ChangeSkinRequest struct {
// ChangeSkin changes the player's skin
func (a *Account) ChangeSkin(req ChangeSkinRequest) (profile PlayerProfile, err error) {
err = a.doMcApiRequest("POST", BaseUrlApi+"/minecraft/profile/skins", req, &profile)
err = a.doMcApiRequest("POST", a.ServicesHost+"/minecraft/profile/skins", req, &profile)
return
}
// ResetSkin resets the player's skin to default
func (a *Account) ResetSkin() (profile PlayerProfile, err error) {
err = a.doMcApiRequest("DELETE", BaseUrlApi+"/minecraft/profile/skins/active", nil, &profile)
err = a.doMcApiRequest("DELETE", a.ServicesHost+"/minecraft/profile/skins/active", nil, &profile)
return
}
// HideCape hides the player's active cape
func (a *Account) HideCape() (profile PlayerProfile, err error) {
err = a.doMcApiRequest("DELETE", BaseUrlApi+"/minecraft/profile/capes/active", nil, &profile)
err = a.doMcApiRequest("DELETE", a.ServicesHost+"/minecraft/profile/capes/active", nil, &profile)
return
}
@@ -258,29 +261,53 @@ type ShowCapeRequest struct {
// ShowCape shows a specific cape
func (a *Account) ShowCape(capeID string) (profile PlayerProfile, err error) {
req := ShowCapeRequest{CapeID: capeID}
err = a.doMcApiRequest("PUT", BaseUrlApi+"/minecraft/profile/capes/active", req, &profile)
err = a.doMcApiRequest("PUT", a.ServicesHost+"/minecraft/profile/capes/active", req, &profile)
return
}
// CheckGiftCodeValidity checks if a gift code is valid
func (a *Account) CheckGiftCodeValidity(giftCode string) (valid bool, err error) {
url := BaseUrlApi + "/productvoucher/giftcode"
url := a.ServicesHost + "/productvoucher/" + giftCode
r, err := http.NewRequest("GET", url, nil)
if err != nil {
return false, err
return
}
r.Header.Set("Authorization", "Bearer "+a.Ygg)
resp, err := http.DefaultClient.Do(r)
if err != nil {
return false, err
return
}
defer resp.Body.Close()
// Returns HTTP 200 or 204 if valid, 404 if invalid
if resp.StatusCode == 200 || resp.StatusCode == 204 {
return true, nil
valid = true
return
}
return
}
func NewAccountFromYGG(ygg string) (a *Account, err error) {
a = &Account{
Ygg: ygg,
Servers: DefaultServers,
}
p, err := a.QueryProfile()
if err != nil {
return
}
a.Name = p.Name
a.Uuid, err = uuid.Parse(strings.ReplaceAll(p.ID, "-", ""))
return
}
func NewAccount(name string, uuid uuid.UUID, ygg string) (a *Account) {
return &Account{
Name: name,
Uuid: uuid,
Ygg: ygg,
Servers: DefaultServers,
}
return false, nil
}
+10
View File
@@ -0,0 +1,10 @@
package api
import "github.com/admin-else/strom/crypto"
func NewOfflineAccount(name string) *Account {
return &Account{
Name: name,
Uuid: crypto.FromOfflinePlayer(name),
}
}
+2
View File
@@ -3,6 +3,7 @@ package client
import (
"net"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
)
@@ -30,6 +31,7 @@ func ConnectVersionLess(addr string) (ret *proto.Conn, err error) {
ret.SetState(proto_base.Handshaking)
ret.SetCompressionThreshold(-1)
ret.Actor = proto_base.Client
ret.Loop = event.NewLoop()
ret.Conn, err = net.Dial("tcp", addr)
if err != nil {
return
+3 -3
View File
@@ -19,8 +19,8 @@ func (c *ConfigIgnorer) OnStart() (err error) {
return
}
func (c *ConfigIgnorer) Default(event event.Anything) (err error) {
slog.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", event))
func (c *ConfigIgnorer) Default(e event.Anything) (err error) {
slog.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", e))
return
}
@@ -56,7 +56,7 @@ func IgnoreConfig(c *proto.Conn) (err error) {
ci.RegisterUntilLatest(ci.OnPing)
ci.RegisterUntilLatest(ci.OnKeepAlive)
err = ci.Start()
err = ci.StartConn()
if err != nil {
err = errors.Join(err, errors.New("failed to ignore config"))
}
+1 -1
View File
@@ -106,7 +106,7 @@ func DoDnsSimple(in string) (out string, err error) {
if err != nil {
return
}
out = in
out = host + ":" + strconv.Itoa(int(portN))
if portN != 25565 {
return
}
+1
View File
@@ -13,6 +13,7 @@ func MakeHandshakePacket(c *proto.Conn, nextState proto_base.State) (p *v1_21_8.
return MakeHandshakePacketAddr(c, nextState, c.RemoteAddr().String())
}
// MakeHandshakePacketAddr gets a custom addr
func MakeHandshakePacketAddr(s *proto.Conn, nextState proto_base.State, addr string) (p *v1_21_8.HandshakingToServerPacketSetProtocol, err error) {
p = &v1_21_8.HandshakingToServerPacketSetProtocol{NextState: int32(nextState), ProtocolVersion: s.ProtocolVersion}
var portStr string
+15 -10
View File
@@ -46,10 +46,6 @@ func (s *LoginClient) OnDefault(event event.Unhandled) (err error) {
return
}
func (s *LoginClient) OnCycle(_ event.Tick) (err error) {
return
}
func (s *LoginClient) OnClose(_ event.Close) (err error) {
return
}
@@ -127,13 +123,12 @@ func (s *LoginClient) OnStart() (err error) {
return
}
func Login(c *proto.Conn, account *api.Account) (err error) {
func LoginRaw(c *proto.Conn, account *api.Account, addr string) (err error) {
lc := &LoginClient{
Conn: c,
Account: account,
}
lc.RegisterCritical(lc.OnDefault)
lc.RegisterCritical(lc.OnCycle)
lc.RegisterCritical(lc.OnClose)
lc.RegisterUntilLatest(lc.OnCompress)
lc.RegisterUntilLatest(lc.OnEncrypt)
@@ -152,21 +147,31 @@ func Login(c *proto.Conn, account *api.Account) (err error) {
lc.SetState(proto_base.Login)
err = lc.Send(&v1_21_8.LoginToServerPacketLoginStart{Username: lc.Account.Name, PlayerUUID: lc.Account.Uuid})
err = lc.Start()
err = lc.StartConn()
if err != nil {
err = errors.Join(FailedToLoginErr, err)
}
return
}
func ConnectAndLogin(connectTo string, account *api.Account) (c *proto.Conn, err error) {
func LoginNoSrv(connectTo string, account *api.Account) (c *proto.Conn, err error) {
c, err = Connect(connectTo)
if err != nil {
return
}
err = Login(c, account)
err = LoginRaw(c, account, connectTo)
return
}
func Login(connectTo string, account *api.Account) (c *proto.Conn, err error) {
connectTo, err = DoDnsSimple(connectTo)
if err != nil {
err = errors.Join(FailedToLoginErr, err)
return
}
c, err = Connect(connectTo)
if err != nil {
return
}
err = LoginRaw(c, account, connectTo)
return
}
+15 -9
View File
@@ -19,10 +19,6 @@ type StatusClient struct {
DoPingRoundTripTime bool
}
func (s *StatusClient) OnStart() (err error) {
return
}
func (s *StatusClient) OnStatus(p *v1_21_8.StatusToClientPacketServerInfo) (err error) {
s.Status = p.Response
s.PingSendTime = time.Now()
@@ -41,7 +37,8 @@ func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
}
// StatusRaw returns a StatusClient that is not connected to a server.
// So ignore the resource leak warning. And maybe attach a warn ignore comment
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
// It does not resolve SRV records.
func StatusRaw(addr string) (s *StatusClient, err error) {
c, err := ConnectVersionLess(addr)
if err != nil {
@@ -55,9 +52,8 @@ func StatusRaw(addr string) (s *StatusClient, err error) {
s = &StatusClient{
Conn: c,
}
s.Loop = event.NewLoop()
s.RegisterUntilLatest(s.OnStatus)
s.RegisterUntilLatest(s.OnPong)
s.RegisterCriticalUntilLatest(s.OnStatus)
s.RegisterCriticalUntilLatest(s.OnPong)
p, err := MakeHandshakePacketAddr(s.Conn, proto_base.Status, addr)
if err != nil {
@@ -74,7 +70,8 @@ func StatusRaw(addr string) (s *StatusClient, err error) {
return
}
func Status(addr string) (status server.StatusResponse, err error) {
// StatusNoDns is like Status but does not resolve SRV records.
func StatusNoDns(addr string) (status server.StatusResponse, err error) {
s, err := StatusRaw(addr)
if err != nil {
return
@@ -82,3 +79,12 @@ func Status(addr string) (status server.StatusResponse, err error) {
err = json.Unmarshal([]byte(s.Status), &status)
return
}
// Status resolves the status of the minecraft server at the given address.
func Status(addr string) (status server.StatusResponse, err error) {
addr, err = DoDnsSimple(addr)
if err != nil {
return
}
return StatusNoDns(addr)
}
+5 -3
View File
@@ -8,9 +8,10 @@ import (
"os"
"slices"
"github.com/admin-else/strom/tools/cli/offline_uuid"
"github.com/admin-else/strom/tools/cli/print_nbt"
"github.com/admin-else/strom/tools/cli/status"
"github.com/admin-else/strom/cmd/strom/offline_uuid"
"github.com/admin-else/strom/cmd/strom/packet_inspector"
"github.com/admin-else/strom/cmd/strom/print_nbt"
"github.com/admin-else/strom/cmd/strom/status"
)
var ExpectedASubcommandErr = errors.New("expected a subcommand")
@@ -20,6 +21,7 @@ var subcommands = map[string]func(args []string) error{
"offline-uuid": offline_uuid.Run,
"print-nbt": print_nbt.Run,
"status": status.Run,
"packet-spy": packet_inspector.Run,
}
func Help(args []string) (err error) {
+118
View File
@@ -0,0 +1,118 @@
package packet_inspector
import (
"flag"
"log/slog"
"github.com/admin-else/strom/api"
"github.com/admin-else/strom/client"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/proto_generated/v1_21_11"
"github.com/admin-else/strom/server"
"github.com/admin-else/strom/text"
)
var Cmd = flag.NewFlagSet("packet-inspector", flag.ContinueOnError)
var OfflineNameFlag = Cmd.String("name", "packet_inspector", "offline player name")
var Token = Cmd.String("token", "", "token to use")
var TargetAddr = Cmd.String("addr", "127.0.0.1:25565", "address to connect to")
var ListenAddr = Cmd.String("listen", "127.0.0.1:25566", "address to listen on")
type Proxy struct {
Client, Servee *proto.Conn
Data nbt.Tag
}
func (p *Proxy) Start() (err error) {
p.Client.RegisterCritical(p.OnAnything)
p.Servee.RegisterCritical(p.OnAnything)
p.Servee.RegisterCriticalUntilLatest(p.OnFinishConfiguration)
errChan := make(chan error)
go func() {
errChan <- p.Client.StartConn()
}()
go func() {
errChan <- p.Servee.StartConn()
}()
err = <-errChan
return
}
func (p *Proxy) OnAnything(e event.Anything) (err error) {
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
if !ok {
return
}
packetInfo, ok := proto.LookupPacketInfoByType(packet)
if !ok {
return
}
switch packetInfo.Direction {
case proto_base.ToServer:
err = p.Client.Send(packet)
case proto_base.ToClient:
err = p.Servee.Send(packet)
}
return
}
func (p *Proxy) OnFinishConfiguration(packet *v1_21_11.ConfigurationToServerPacketFinishConfiguration) (err error) {
err = p.Client.Send(packet)
if err != nil {
return
}
p.Servee.SetState(proto_base.Play)
p.Client.SetState(proto_base.Play)
err = event.DontForwardErr
return
}
var StatusResponse = server.StatusResponse{
Version: server.StatusResponseVersion{
Name: text.Pretty("STROM"),
Protocol: 772,
},
Players: server.StatusResponsePlayers{},
Description: text.Pretty("Packet spy forwards to " + *TargetAddr),
Favicon: "",
EnforcesSecureChat: false,
}
func Run(args []string) (err error) {
err = Cmd.Parse(args)
if err != nil {
return
}
slog.SetLogLoggerLevel(slog.LevelDebug)
slog.Info("Starting packet inspector", "listen", *ListenAddr)
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
p := &Proxy{Servee: serveeConn, Client: nil}
var acc = api.NewOfflineAccount(*OfflineNameFlag)
if *Token != "" {
acc, err = api.NewAccountFromYGG(*Token)
if err != nil {
return
}
}
_, err = server.ServeLogin(serveeConn, server.WithOtherAccount(acc), server.WithStatus(StatusResponse))
if err != nil {
return
}
c, err := client.Login(*TargetAddr, acc)
if err != nil {
return
}
defer c.Close()
p.Client = c
err = p.Start()
return
})
return
}
+49
View File
@@ -0,0 +1,49 @@
package print_nbt
import (
"bytes"
"encoding/json"
"flag"
"os"
"github.com/admin-else/strom/nbt"
)
var (
cmd = flag.NewFlagSet("print-nbt", flag.ContinueOnError)
FileFlag = cmd.String("file", "", "The NBT file to print")
HashFormatFlag = cmd.Bool("snbt", false, "will print with go %#v")
)
func Run(args []string) (err error) {
err = cmd.Parse(args)
if err != nil {
return
}
f, err := os.Open(*FileFlag)
if err != nil {
return
}
defer f.Close()
n, err := nbt.ReadFile(f)
if err != nil {
return
}
var b []byte
if *HashFormatFlag {
bb := bytes.NewBuffer(nil)
err = nbt.PrintSNBTAny(n.Value, bb)
if err != nil {
return
}
b = bb.Bytes()
//b = []byte(strings.ReplaceAll(string(b), ",", ",\n"))
} else {
b, err = json.MarshalIndent(n, "", " ")
}
if err != nil {
return
}
_, err = os.Stdout.Write(b)
return
}
+48 -37
View File
@@ -2,6 +2,7 @@ package data
import (
"errors"
"strconv"
)
// {
@@ -52,6 +53,7 @@ type BlockState struct {
}
var BlocksCache = make(map[string][]*Block)
var BlocksCacheIdMap = make(map[string]map[int32]*Block)
func BlocksForVersion(v string) (ret []*Block) {
var ok bool
@@ -65,23 +67,37 @@ func BlocksForVersion(v string) (ret []*Block) {
}
func LookupBlockByStateId(version string, stateId int32) (block *Block, ok bool) {
idmap, found := BlocksCacheIdMap[version]
if !found {
idmap = make(map[int32]*Block)
for _, b := range BlocksForVersion(version) {
for i := range b.MaxStateId - b.MinStateId + 1 {
idmap[b.MinStateId+i] = b
}
}
BlocksCacheIdMap[version] = idmap
}
block, ok = idmap[stateId]
return
}
func LookupBlockByName(version string, name string) (block *Block, ok bool) {
for _, b := range BlocksForVersion(version) {
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
if b.Name == name {
block = b
ok = true
return
}
}
return
}
func LookupBlockByName(version string, stateId int32) (block *Block, ok bool) {
for _, b := range BlocksForVersion(version) {
if b.MinStateId <= stateId && stateId <= b.MaxStateId {
block = b
ok = true
}
func StateIdFromBlocKAndStateMap(version string, name string, stateMap map[string]string) (stateId int32, err error) {
b, ok := LookupBlockByName(version, name)
if !ok {
err = BlockNotFoundErr
return
}
stateId, err = b.IdFromStateMap(stateMap)
return
}
@@ -92,7 +108,7 @@ var BadBlockStateTypeErr = errors.New("bad block state type")
var BadEnumErr = errors.New("bad enum")
var SupplyAllStatesErr = errors.New("supply all states")
func FromBlockState(version string, stateId int32) (b *Block, stateData map[string]any, err error) {
func FromBlockState(version string, stateId int32) (b *Block, stateData map[string]string, err error) {
b, ok := LookupBlockByStateId(version, stateId)
if !ok {
err = BlockNotFoundErr
@@ -101,19 +117,17 @@ func FromBlockState(version string, stateId int32) (b *Block, stateData map[stri
return
}
// TODO: mc stores these as strings so we will do the same
func mcDataBlockStateTypeParse(n int32, s *BlockState) (v any, err error) {
func mcDataBlockStateTypeParse(n int32, s *BlockState) (v string, err error) {
switch s.Type {
case "int":
v = n
v = strconv.Itoa(int(n))
case "enum":
v = s.Values[n]
case "bool":
if n == 0 { // MOJANK: 0 is true, 1 is false
v = true
v = "true"
} else if n == 1 {
v = false
v = "false"
} else {
err = InvalidBoolValueErr
}
@@ -123,35 +137,32 @@ func mcDataBlockStateTypeParse(n int32, s *BlockState) (v any, err error) {
return
}
func mcDataBlockStateTypeSerialize(v any, s *BlockState) (n int32, err error) {
switch t := v.(type) {
case int32:
n = t
case bool:
if t {
n = 0
} else {
n = 1
}
case string:
for i, enumValue := range s.Values {
if enumValue == t {
return int32(i), nil
}
}
err = BadEnumErr
default:
err = BadBlockStateTypeErr
func mcDataBlockStateTypeSerialize(v string, s *BlockState) (n int32, err error) {
if v == "true" {
return 0, nil
}
if v == "false" {
return 1, nil
}
n1, errMabye := strconv.Atoi(v)
if errMabye == nil {
return int32(n1), nil
}
for i, enumValue := range s.Values {
if enumValue == v {
return int32(i), nil
}
}
err = BadEnumErr
return
}
func (b *Block) StateMapFromId(stateId int32) (ret map[string]any, err error) {
func (b *Block) StateMapFromId(stateId int32) (ret map[string]string, err error) {
if stateId < b.MinStateId || stateId > b.MaxStateId {
err = BlockStateOutOfRangeErr
return
}
ret = make(map[string]any)
ret = make(map[string]string)
stateId -= b.MinStateId
a := int32(1)
@@ -166,7 +177,7 @@ func (b *Block) StateMapFromId(stateId int32) (ret map[string]any, err error) {
return
}
func (b *Block) IdFromStateMap(m map[string]any) (ret int32, err error) {
func (b *Block) IdFromStateMap(m map[string]string) (ret int32, err error) {
a := int32(1)
for i := range len(b.States) {
s := b.States[len(b.States)-1-i]
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env sh
# Go get / install does not clone git submodules so we have to do it like this
rm -rf minecraft-data/
git clone --depth 1 git@github.com:PrismarineJS/minecraft-data.git
rm -rf minecraft-data/.git/
+8
View File
@@ -1,5 +1,7 @@
package data
import "sync"
// {
// "id": 2,
// "name": "granite",
@@ -15,15 +17,21 @@ type Item struct {
}
var ItemsCache = make(map[string][]*Item)
var ItemsCacheMutex = &sync.RWMutex{}
func ItemsForVersion(v string) (ret []*Item) {
var ok bool
ItemsCacheMutex.RLock()
if ret, ok = ItemsCache[v]; ok {
ItemsCacheMutex.RUnlock()
return
}
ItemsCacheMutex.RUnlock()
var b []*Item
must(LoadVersionedJson(v, "items", &b))
ItemsCacheMutex.Lock()
ItemsCache[v] = b
ItemsCacheMutex.Unlock()
return b
}
+2
View File
@@ -0,0 +1,2 @@
# These are supported funding model platforms
@@ -0,0 +1,4 @@
---
name: Blank Issue
about: Open a new issue
---
@@ -0,0 +1,14 @@
---
name: New Version
about: Add a new minecraft version to MCD
title: 'Add version: '
---
- [ ] Version folder containing "version.json" created
- [ ] "version.json" filled out appropriately
- [ ] Added version to dataPath.json
- [ ] "version" field of dataPath.json points to new version folder
- [ ] Version added to common/versions.json
- [ ] Version added to ReadMe
Additional Requirements For This Update:
+93
View File
@@ -0,0 +1,93 @@
PrismarineJS/minecraft-data holds Minecraft data including game versions, protocol schemas, block/item/entity states, and more. It provides a comprehensive cross-language set of data files combined with schemas that can be consumed in any language.
## Directory Structure
- `.github/`
- `workflows/`
- `ci.yml` -- Runs CI here (cd tools/js && npm install && npm test)
- `bedrock-ci.yml` -- Runs CI for Bedrock Edition via workflow dispatch to PrismarineJS/bedrock-protocol. This allows for testing and validation of Bedrock-specific changes.
- `commands.yml` -- Slash commands
- `handleMcpcGeneratedArtifacts.js`
- `helper-bot/` -- Files for the GitHub Actions cron helper.
- `data/`
- `dataPaths.json` -- map of { ['pc' | 'bedrock']: { '$version': { 'dataType': 'folderPath' } } }
- `[pc | bedrock]/` -- Minecraft Java Edition data
- `1.20/` -- Versioned data for Minecraft Java Edition 1.20
- `$version/` -- Versioned data for Minecraft Java Edition $version
- `common/` -- Common data for all Minecraft Java Edition versions
- `protocolVersions.json` -- List of protocol versions for Minecraft Java Edition
- `latest/` -- This holds the latest protocol yaml files. They are used to generate the protocol.json files in each versioned folder (more on this below).
- `schemas/` -- JSON schemas for validating data files
- `tools/js` -- JavaScript utilities for working with repo (holds tests, some data generators and helper scripts)
## Setup
No setup is needed as we just hold data. To set up the development environment that runs tests, run the following commands:
```bash
cd tools/js
npm install
```
This will ensure all data matches schema among other checks.
There are no 'releases' in this repo beyond updating the data itself. Instead, we have a workflow that will automatically create tagged releases if the user runs the /makerelease slash command, so you can inform the user about that if a release is needed.
## Testing
Always run tests after data changes to ensure local tests are passing:
```sh
cd tools/js
npm install
npm test -- --bail 2>&1 | tail -100
```
## Data
Most data is generated with data generators. For mcpc, data is generated with [minecraft-data-generator](https://github.com/PrismarineJS/minecraft-data-generator).
Avoid updating data manually unless necessary, like when specific data is not automated yet (for tips on what is and not, see doc/add-data-new-version.md). Instead, update the data generators or add new ones as needed. Since you don't have capability to make changes outside this repo, please inform the user what changes need to be made and be as detailed as possible.
Note we do not currently have any automation setup to generate bedrock edition data.
## Github Workflows
### Automation
Our GH Actions workflow (.github/helper-bot) auto updates the pc protocolVersions.json with new detected versions and also auto opens a scaffolding PR.
cron workflow .github/workflows/update-helper.yml -> .github/helper-bot/.
helper-bot dispatches to minecraft-data-generator so that it can run data generators for new versions. minecraft-data-generator then sends a workflow dispatch back to us so we can then take its generated artifacts and commit them here.
.github/workflows/handle-mcpc-generator.yml ->
.github/helper-bot/handleMcpcGeneratedArtifacts.js
## Notes
Not all data is generated. Some data (like protocol schemas) is manually curated on both pc and bedrock.
### Protocol data
We use a special YAML-like DSL to generate protocol.json files. Refer to doc/protocol.md for info.
These files are stored inside proto.yml (and an imported types.yml support file on bedrock) files in the latest/ folder (like bedrock/latest/proto.yml) for the latest version, otherwise in the versioned folder (like pc/1.20/proto.yml).
👉 Run `npm run build` in tools/js to regenerate protocol.json files after making changes to the protocol yaml files.
❌ Don't make changes to protocol.json files directly. Instead, update the relevant proto.yml file in latest/ and regenerate protocol.json by running `npm run build` in tools/js.
If you need to edit many files at once, consider writing a simple Node.js script to replace. E.g., from `cd tools/js && npm i && node __replace_something.js`):
```js
const cp = require('child_process')
const fs = require('fs')
const glob = require('glob')
const pcVersionsOrdered = require('../../data/pc/common/versions.json')
const after1_20_5 = pcVersionsOrdered.slice(pcVersionsOrdered.indexOf('1.20.5')) // everything after 1.20.5...
for (const version of pcVersionsOrdered) {
// globSync, fs.readFileSync...fs.writeFileSync ; avoid async
}
```
When updating, apply minimum required changes when possible.
1. Do not rename fields or packets unless their value has changed, even if they may have 'officially' changed. If their values *have* significantly changed (varint -> i32 maybe insignificant from primitive standpoint, string to number is), it maybe a good idea to rename the field or packet.
2. Do not rename existing enums or other data, even if they were renamed in the game. You may add or remove data as is needed as this does not unnecessarily affect existing code relying on old names.
3. Always read the information in doc/protocol.md to understand our YAML format.
4. Try to inline types as much as possible--this includes mappers and switch statements. Don't create extra types unless they are actually re-used in multiple places, or very large to warrant seperation.
@@ -0,0 +1,113 @@
const fs = require('fs')
const github = require('gh-helpers')()
const { join } = require('path')
const { extractPcEntityMetadata } = require('../../tools/js/extractPcEntityMetadata')
const { exec, createInitialPR } = require('./utils')
const artifactsDir = join(__dirname, './artifacts')
const root = join(__dirname, '..', '..')
async function handle (ourPR, genPullNo, version, artifactURL, shouldPull) {
const branchNameVersion = version.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()
const branch = ourPR.headBranch || `pc-${branchNameVersion}`
if (shouldPull) exec('git', ['pull', 'origin'])
// if external PR:
// const branch = ourPR.headBranch
// exec('git', ['remote', 'add', 'fo', ourPR.headCloneURL])
// exec('git', ['fetch', 'fo', branch])
// exec('git', ['checkout', '-b', branch, `fo/` + branch])
try {
exec('git', ['checkout', branch])
} catch (err) {
console.error('Error checking out branch:', err)
process.exit(1)
}
const dataPaths = require('../../data/dataPaths.json')
const dataPath = dataPaths.pc[version]
const destDir = join(root, `./data/pc/${version}`)
if (!fs.existsSync(destDir) || !dataPath) {
console.warn(`⚠️ Version ${version} not found (checked ${destDir}) ; cannot continue.`, fs.existsSync(destDir), dataPath)
process.exit(1)
}
if (ourPR.body) {
await github.updateIssue(ourPR.number, {
body: ourPR.body.replace('<!--minecraft-data-generator-placeholder-->', `- https://github.com/PrismarineJS/minecraft-data-generator/pull/${genPullNo}`)
})
}
console.log('Handling PR:', ourPR)
fs.mkdirSync(artifactsDir, { recursive: true })
// https://github.com/PrismarineJS/minecraft-data-generator/actions/runs/17261281146/artifacts/3861320839
const s = artifactURL.split('github.com/')[1]
const [ownerName, repoName, _actions, _runs, _runId, _artifacts, artifactId] = s.split('/') // eslint-disable-line
console.log('Downloading artifacts', { ownerName, repoName, artifactId, artifactsDir })
await github.artifacts.downloadIdFrom(ownerName, repoName, artifactId, artifactsDir)
console.log(fs.readdirSync(artifactsDir))
// Now copy artifacts/${version}/*.json to data/pc/$version/*.json
const versionArtifactsDir = join(artifactsDir, version)
for (const file of fs.readdirSync(versionArtifactsDir)) {
if (file.endsWith('.json')) {
const src = join(versionArtifactsDir, file)
const dest = join(destDir, file)
fs.mkdirSync(destDir, { recursive: true })
console.log(`copy ${src} => ${dest}`)
fs.copyFileSync(src, dest)
dataPath[file.replace('.json', '')] = 'pc/' + version
}
}
// Commit the new dataPath
fs.writeFileSync(join(root, 'data', 'dataPaths.json'), JSON.stringify(dataPaths, null, 2))
try {
process.chdir(join(__dirname, '../../tools/js'))
extractPcEntityMetadata(version, version, { write: true, cloneIfMissing: true })
} catch (e) {
console.log('Failed to extract PC entity metadata', e)
}
// Now, we need to commit the changes
exec('git', ['config', 'user.name', 'github-actions[bot]'])
exec('git', ['config', 'user.email', '41898282+github-actions[bot]@users.noreply.github.com'])
exec('git', ['add', '--all'])
exec('git', ['commit', '-m', `[Auto] Apply generated data from PrismarineJS/minecraft-data-generator#${genPullNo}`])
exec('git', ['push', 'origin', branch])
}
async function main (versions, genPullNo, artifactUrl, createPR) {
const version = versions.at(-1)
const pr = await github.findPullRequest({ titleIncludes: '🎈', author: null })
console.log('Found PR', pr)
if (pr && pr.isOpen) {
const details = await github.getPullRequest(pr.id)
console.log('PR', details)
await handle(details, genPullNo, version, artifactUrl, true)
} else if (createPR) {
const pr = await createInitialPR('pc', '(This issue was created for a minecraft-data-generator PR)', {
version,
protocolVersion: null
})
console.log('Created PR', pr)
const details = await github.getPullRequest(pr.number)
console.log('PR', details)
await handle(details, genPullNo, version, artifactUrl, false)
} else {
process.exit(1)
}
}
main(
JSON.parse(process.env.TRIGGER_MC_VERSIONS),
process.env.TRIGGER_PR_NO,
process.env.TRIGGER_ARTIFACT_URL,
process.env.CREATE_PR_IF_NONE
)
+215
View File
@@ -0,0 +1,215 @@
/* eslint-disable no-return-assign, no-sequences */
const fs = require('fs')
const github = require('gh-helpers')()
const pcManifestURL = 'https://launchermeta.mojang.com/mc/game/version_manifest.json'
const changelogURL = 'https://feedback.minecraft.net/hc/en-us/sections/360001186971-Release-Changelogs'
const { exec, createInitialPR } = require('./utils')
const download = (url, dest) => exec('curl', ['-L', url, '-o', dest])
function buildFirstIssue (title, result, jarData) {
const protocolVersion = jarData?.protocol_version || 'Failed to obtain from JAR'
const name = jarData?.name || result.id
const date = result.releaseTime
return {
title,
body: `
A new Minecraft Java Edition version is available (as of ${date}), version **${result.id}**
## Official Changelog
* ${changelogURL}
## Protocol Details
(I will close this issue automatically if "${result.id}" is added to data/pc/common/versions.json on "master")
<table>
<tr><td><b>Name</b></td><td>${name}</td>
<tr><td><b>Protocol ID</b></td><td>${protocolVersion}</td>
<tr><td><b>Release Date</b></td><td>${date}</td>
<tr><td><b>Release Type</b></td><td>${result.type}</td>
<tr><td><b>Data Version</b></td><td>${jarData?.world_version}</td>
<tr><td><b>Java Version</b></td><td>${jarData?.java_version}</td>
</table>
<hr/>
🤖 I am a bot, I check for updates every 2 hours without a trigger. You can close this issue to prevent any further updates.
<!-- RP: ${JSON.stringify({ title, versionJson: jarData, latestVersionData: result, latestVersion: result.id })} -->
`
}
}
const protocolVersions = {
pc: require('../../data/pc/common/protocolVersions.json'),
bedrock: require('../../data/bedrock/common/protocolVersions.json')
}
const supportedVersions = {
pc: require('../../data/pc/common/versions.json'),
bedrock: require('../../data/bedrock/common/versions.json')
}
async function updateManifestPC () {
const manifest = await fetch(pcManifestURL).then(res => res.json())
// fs.writeFileSync('./manifest.json', JSON.stringify(manifest, null, 2))
const knownVersions = protocolVersions.pc.reduce((acc, cur) => (acc[cur.minecraftVersion] = cur, acc), {})
const latestVersion = manifest.latest.snapshot
const latestReleaseVersion = manifest.latest.release
const latestVersionData = manifest.versions.find(v => v.id === latestVersion)
const latestVersionIsSnapshot = latestVersionData.type !== 'release'
const title = `Support Minecraft PC ${latestReleaseVersion}`
const issueStatus = await github.findIssue({ titleIncludes: title, author: null }) || {}
console.log('issueStatus', issueStatus)
if (issueStatus?.isOpen) {
if (supportedVersions.pc.includes(latestReleaseVersion)) {
// If the issue is open and the latest release version is supported, we can close the issue
github.close(issueStatus.id, `Closing as PC ${latestReleaseVersion} is now supported`)
return
}
}
if (latestVersionIsSnapshot) {
// don't make issues for snapshots
if (supportedVersions.pc.includes(latestVersion) || knownVersions[latestVersion]) {
console.log('Latest version is a known snapshot, no work to do')
return
}
} else if (knownVersions[latestVersion]) {
console.log(`Latest PC version ${latestVersion} is known in protocolVersions.json, but not in versions.json (protocol version ${knownVersions[latestVersion].version})`)
return // We expect to have already handled this version.
} else {
console.log(`Latest PC version ${latestVersion} is not known in protocolVersions.json, adding and making issue`)
}
let versionJson
try {
versionJson = await addEntryFor(latestVersion, latestVersionData)
} catch (e) {
console.error(e)
if (latestVersionIsSnapshot) {
console.warn('Failed to update protocolVersions.json for the snapshot', latestVersion)
} else {
console.log('Latest PC version is not supported and we failed to load data. Opening issue...')
const issuePayload = buildFirstIssue(title, latestVersionData)
github.createIssue(issuePayload)
fs.writeFileSync('./issue.md', issuePayload.body)
console.log('OK, wrote to ./issue.md', issuePayload)
}
}
if (!latestVersionIsSnapshot && !issueStatus.isOpen && !issueStatus.isClosed) {
await openIssueAndDispatch(title, versionJson, latestVersionData, latestVersion)
}
async function addEntryFor (releaseVersion, releaseData) {
const latestReleaseManifest = await fetch(releaseData.url).then(res => res.json())
// Download client jar
if (!fs.existsSync(`./${releaseVersion}.jar`)) {
const clientJarUrl = latestReleaseManifest.downloads.client.url
console.log('Downloading client jar', clientJarUrl)
download(clientJarUrl, `./${releaseVersion}.jar`)
}
// Log the byte size of the client jar
const clientJarSize = fs.statSync(`./${releaseVersion}.jar`).size
console.log(`Downloaded client jar ${releaseVersion}.jar (${clientJarSize} bytes), extracting its version.json...`)
// unzip with tar / unzip, Actions image uses 7z
if (process.platform === 'win32') exec('tar', ['-xf', `./${releaseVersion}.jar`, 'version.json'])
else exec('7z', ['-y', 'e', `./${releaseVersion}.jar`, 'version.json'])
const versionJson = require('./version.json')
let majorVersion
if (versionJson.id && versionJson.id.includes('.')) {
majorVersion = versionJson.id.split('.', 2).join('.')
} else {
console.error(`Version '${versionJson.id}' appears to be a snapshot (no dot in name). Using the last known majorVersion in protocolVersions.json for this version (may be incorrect)`)
majorVersion = protocolVersions.pc[0].majorVersion
}
const newEntry = {
minecraftVersion: versionJson.id,
version: versionJson.protocol_version,
dataVersion: versionJson.world_version,
usesNetty: true,
majorVersion,
releaseType: latestVersionData.type
}
console.log('Adding new entry to pc protocolVersions.json', newEntry)
const updatedProtocolVersions = [newEntry, ...protocolVersions.pc]
fs.writeFileSync('../../data/pc/common/protocolVersions.json', JSON.stringify(updatedProtocolVersions, null, 2))
if (process.env.CI) {
console.log('Committing changes to protocolVersions.json')
// https://github.com/actions/checkout/pull/1184
exec('git', ['config', 'user.name', 'github-actions[bot]'])
exec('git', ['config', 'user.email', '41898282+github-actions[bot]@users.noreply.github.com'])
exec('git', ['add', '../../data/pc/common/protocolVersions.json'])
exec('git', ['commit', '-m', `Add ${versionJson.id} to pc protocolVersions.json`])
exec('git', ['push'])
}
return versionJson
}
}
async function openIssueAndDispatch (title, versionJson, latestVersionData, latestVersion) {
console.log('Opening issue', versionJson)
const issuePayload = buildFirstIssue(title, latestVersionData, versionJson)
const issue = await github.createIssue(issuePayload)
console.log('Created issue', issue)
// Now create an initial PR with the new version data
const pr = await createInitialPR('pc', issue.url, {
minecraftVersion: versionJson.id,
version: latestVersion,
protocolVersion: versionJson.protocol_version
})
console.log('Created PR', pr)
// Ask minecraft-data-generator to handle new update
const dispatchPayload = {
owner: 'PrismarineJS',
repo: 'minecraft-data-generator',
workflow: 'handle-mcdata-update.yml',
branch: 'main',
inputs: {
version: latestVersion,
issue_number: String(issue?.number),
pr_number: String(pr?.number)
}
}
console.log('Sending workflow dispatch', dispatchPayload)
await github.sendWorkflowDispatch(dispatchPayload)
// Ask node-minecraft-protocol to handle new update
const nodeDispatchPayload = {
owner: 'PrismarineJS',
repo: 'node-minecraft-protocol',
workflow: 'update-from-minecraft-data.yml',
branch: 'master',
inputs: {
new_mc_version: latestVersion,
mcdata_branch: pr.branchName,
mcdata_pr_url: pr.url
}
}
console.log('Sending workflow dispatch', nodeDispatchPayload)
await github.sendWorkflowDispatch(nodeDispatchPayload)
// node-minecraft-protocol would then dispatch to mineflayer
return { issue, pr }
}
if (process.env.FORCE_CREATE_PR) {
const payload = JSON.parse(process.env.FORCE_CREATE_PR)
openIssueAndDispatch(payload.title, payload.versionJson, payload.latestVersionData, payload.latestVersion).then(() => {
console.log('Done with manual PR')
}).catch(err => {
console.error('Failed to do manual PR', err)
process.exit(1)
})
} else {
updateManifestPC()
}
module.exports = { openIssueAndDispatch, updateManifestPC }
+11
View File
@@ -0,0 +1,11 @@
{
"scripts": {
"fix": "standard --fix"
},
"dependencies": {
"gh-helpers": "^1.0.0"
},
"devDependencies": {
"standard": "^17.1.2"
}
}
+50
View File
@@ -0,0 +1,50 @@
const cp = require('child_process')
const github = require('gh-helpers')()
const path = require('path')
const pm = process.versions.bun ? process.execPath : 'npm'
function exec (file, args = [], options = {}) {
const opts = { stdio: ['inherit', 'inherit', 'inherit'], ...options }
console.log('> ', file, args.join(' '), options.cwd ? `(cwd: ${options.cwd})` : '')
return github.mock ? undefined : cp.execFileSync(file, args, opts)
}
const toolsJs = path.join(__dirname, '..', '..', 'tools', 'js')
const sanitizeBranch = (branchName) => branchName.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()
async function createInitialPR (edition, issueUrl, { version, protocolVersion }) {
exec(pm, ['install'], { cwd: toolsJs })
if (protocolVersion) exec(pm, ['run', 'version', edition, version, protocolVersion], { cwd: toolsJs })
exec(pm, ['run', 'build'], { cwd: toolsJs })
const branchNameVersion = sanitizeBranch(version)
const branchName = `${edition}_${branchNameVersion}`
const title = `🎈 Add Minecraft ${edition} ${version} data`
// First, delete any existing branch
try {
exec('git', ['branch', '-D', branchName])
} catch (e) {
// Branch doesn't exist, ignore error
}
exec('git', ['checkout', '-b', branchName])
exec('git', ['config', 'user.name', 'github-actions[bot]'])
exec('git', ['config', 'user.email', '41898282+github-actions[bot]@users.noreply.github.com'])
exec('git', ['add', '--all'])
exec('git', ['commit', '--allow-empty', '-m', title])
exec('git', ['push', 'origin', branchName, '--force'])
const body = `
This automated PR sets up the relevant boilerplate for Minecraft ${edition} version ${version}. Fixes ${issueUrl}.
Related:
- Issue: ${issueUrl}
- Protocol Version: ${protocolVersion}
<!--minecraft-data-generator-placeholder-->
* You can help contribute to this PR by opening a PR against this <code branch>${branchName}</code> branch instead of <code>master</code>.
`
const pr = await github.createPullRequest(title, body, branchName, 'master')
pr.branchName = branchName
return pr
}
module.exports = { createInitialPR, exec }
+59
View File
@@ -0,0 +1,59 @@
name: bedrock-ci
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 14
steps:
- name: Use Node.js 22.x
uses: actions/setup-node@v1
with:
node-version: 22.x
# Old versions of bedrock use old libssl that Ubuntu no longer ships with; need manual install
- name: Install libssl 1.1
run: |
wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb
- name: Checkout node-minecraft-data
uses: actions/checkout@v2
with:
repository: PrismarineJS/node-minecraft-data
submodules: recursive
path: node-mcdata
# Erase the submodule for minecraft-data with a checkout of the current minecraft-data repo
- run: rm -rf node-mcdata/minecraft-data
- name: Checkout minecraft-data
uses: actions/checkout@v2
with:
path: node-mcdata/minecraft-data
# Now install local node-minecraft-data. Running "prepare" should gen data and types
- name: Install minecraft-data
run: |
cd node-mcdata
npm install
npm run prepare
npm link
- name: Checkout bedrock-protocol
uses: actions/checkout@v2
with:
repository: PrismarineJS/bedrock-protocol
path: bedrock-protocol
# I forget the correct install order, do both
- name: Install bedrock-protocol
run: |
cd bedrock-protocol
npm install ../node-mcdata
npm i
npm install ../node-mcdata
- name: Running bedrock-protocol tests
run: |
curl -o node_modules/protodef/src/serializer.js https://raw.githubusercontent.com/extremeheat/node-protodef/refs/heads/dlog/src/serializer.js && curl -o node_modules/protodef/src/compiler.js https://raw.githubusercontent.com/extremeheat/node-protodef/refs/heads/dlog/src/compiler.js
npm test
working-directory: bedrock-protocol
@@ -0,0 +1,92 @@
name: Bedrock Version Bump
on:
workflow_dispatch:
inputs:
minecraftVersion:
description: 'Bedrock Minecraft version (e.g., 1.21.124)'
required: true
type: string
protocolVersion:
description: 'Protocol version number (e.g., 860)'
required: true
type: string
createPr:
description: 'Create a pull request with the changes'
required: false
type: boolean
default: true
jobs:
bump-bedrock-version:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.PAT_PASSWORD || secrets.GITHUB_TOKEN }}
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: |
cd tools/js
npm install
- name: Run version bump script
run: |
cd tools/js
npm run version bedrock ${{ github.event.inputs.minecraftVersion }} ${{ github.event.inputs.protocolVersion }}
- name: Build protocol files
run: |
cd tools/js
npm run build
- name: Run tests
run: |
cd tools/js
npm test
- name: Create Pull Request
if: ${{ github.event.inputs.createPr == 'true' }}
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.PAT_PASSWORD || secrets.GITHUB_TOKEN }}
commit-message: |
Add bedrock ${{ github.event.inputs.minecraftVersion }} protocol data
Updates:
- Protocol version → ${{ github.event.inputs.protocolVersion }}
- Minecraft version → ${{ github.event.inputs.minecraftVersion }}
- Moved previous proto.yml to versioned folder
- Updated protocolVersions.json, versions.json, and dataPaths.json
- Regenerated protocol.json files
branch: bedrock-bump-${{ github.event.inputs.minecraftVersion }}
delete-branch: true
title: Add bedrock ${{ github.event.inputs.minecraftVersion }} data
body: |
## Bedrock Version Bump
This PR adds support for Minecraft Bedrock Edition version **${{ github.event.inputs.minecraftVersion }}** with protocol version **${{ github.event.inputs.protocolVersion }}**.
### Changes
- ✅ Protocol version bumped to ${{ github.event.inputs.protocolVersion }}
- ✅ Minecraft version updated to ${{ github.event.inputs.minecraftVersion }}
- ✅ Previous proto.yml files moved to versioned folder
- ✅ Updated protocolVersions.json and versions.json
- ✅ Regenerated all protocol.json files
- ✅ All tests passing
### How this was generated
This PR was automatically generated using the bedrock version bump workflow.
---
🤖 Generated by GitHub Actions workflow
labels: |
bedrock
version-bump
automated
@@ -0,0 +1,22 @@
name: Repository Dispatch
on:
repository_dispatch:
types: [node-mcData-release]
jobs:
myEvent:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
ref: web
- name: Use Node.js 22.x
uses: actions/setup-node@v1
with:
node-version: 22.x
- name: Run tool
run: |
npm install
git config user.name 'rom1504bot'
git config user.email 'rom1504bot@users.noreply.github.com'
npm run gh-publish
+27
View File
@@ -0,0 +1,27 @@
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
working-directory: tools/js
- run: npm test
working-directory: tools/js
+22
View File
@@ -0,0 +1,22 @@
name: Repo Commands
on:
issue_comment: # Handle comment commands
types: [created]
pull_request: # Handle renamed PRs
types: [edited]
jobs:
comment-trigger:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v3
- name: Run command handlers
uses: PrismarineJS/prismarine-repo-actions@master
with:
# NOTE: You must specify a Personal Access Token (PAT) with repo access here. While you can use the default GITHUB_TOKEN, actions taken with it will not trigger other actions, so if you have a CI workflow, commits created by this action will not trigger it.
token: ${{ secrets.PAT_PASSWORD }}
# See `Options` section below for more info on these options
install-command: cd tools/js && npm install
/fixlint.fix-command: cd tools/js && npm run fix
@@ -0,0 +1,39 @@
name: handle-mcpc-generator
on:
workflow_dispatch:
inputs:
versions:
description: 'JSON stringified list of known versions in generator (last is expected to be latest)'
required: true
default: '["1.21.6"]'
mergedArtifactURL:
description: 'Merged artifact download URL'
required: true
prNumber:
description: 'Relevant minecraft-data-generator PR #'
required: false
createPr:
description: 'Whether to create a new PR if none exists'
required: false
default: 'false'
jobs:
update-mcdata:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- run: cd .github/helper-bot && bun install
- run: cd tools/js && bun install
- run: bun handleMcpcGeneratedArtifacts.js
working-directory: .github/helper-bot
env:
TRIGGER_MC_VERSIONS: ${{ github.event.inputs.versions }}
TRIGGER_PR_NO: ${{ github.event.inputs.prNumber }}
TRIGGER_ARTIFACT_URL: ${{ github.event.inputs.mergedArtifactURL }}
GITHUB_TOKEN: ${{ secrets.PAT_PASSWORD }}
CREATE_PR_IF_NONE: ${{ github.event.inputs.createPr }}
+34
View File
@@ -0,0 +1,34 @@
name: 'tag'
on:
push:
branches:
- master
jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-ecosystem/action-regex-match@v2
id: regex-match
with:
text: ${{ github.event.head_commit.message }}
regex: '^Release ([0-9]+\.[0-9]+\.[0-9]+)'
- name: Create Release
if: steps.regex-match.outputs.match != ''
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.regex-match.outputs.group1 }}
release_name: Release ${{ steps.regex-match.outputs.group1 }}
body: ${{ steps.regex-match.outputs.group1 }}
draft: false
prerelease: false
- uses: peter-evans/repository-dispatch@v1
if: ${{ steps.regex-match.outputs.match != '' }}
with:
token: ${{ secrets.PAT_PASSWORD }}
repository: PrismarineJS/node-minecraft-data
event-type: mcData-release
client-payload: '{"tag": "${{ steps.regex-match.outputs.group1 }}"}'
+32
View File
@@ -0,0 +1,32 @@
name: Update Helper
on:
workflow_dispatch:
inputs:
createPr:
description: 'If provided, force create an update PR. JSON encoded payload {title, versionJson, latestVersionData, latestVersion}'
required: false
default: ''
schedule:
- cron: "0 */2 * * *"
jobs:
helper:
name: update-checker
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@master
- name: Set up Node.js
uses: actions/setup-node@master
with:
node-version: 22.0.0
- name: Install Github Actions toolkit
run: npm i
working-directory: .github/helper-bot
# The env vars contain the relevant trigger information, so we don't need to pass it
- name: Runs helper
run: node index.js
working-directory: .github/helper-bot
env:
GITHUB_TOKEN: ${{ secrets.PAT_PASSWORD }}
FORCE_CREATE_PR: ${{ github.event.inputs.createPr }}
+154
View File
@@ -0,0 +1,154 @@
# minecraft-data
[![Discord](https://img.shields.io/badge/chat-on%20discord-brightgreen.svg)](https://discord.gg/GsEFRM8)
[![Gitter](https://img.shields.io/badge/chat-on%20gitter-brightgreen.svg)](https://gitter.im/PrismarineJS/general)
[![Irc](https://img.shields.io/badge/chat-on%20irc-brightgreen.svg)](https://irc.gitter.im/)
[![Build Status](https://github.com/PrismarineJS/minecraft-data/workflows/CI/badge.svg)](https://github.com/PrismarineJS/minecraft-data/actions?query=workflow%3A%22CI%22)
Language independent module providing minecraft data for minecraft clients, servers and libraries.
Supports
* Minecraft PC version 0.30c (classic), 1.7.10, 1.8.8, 1.9 (15w40b, 1.9, 1.9.1-pre2, 1.9.2, 1.9.4),
1.10 (16w20a, 1.10-pre1, 1.10, 1.10.1, 1.10.2), 1.11 (16w35a, 1.11, 1.11.2), 1.12 (17w15a, 17w18b, 1.12-pre4, 1.12, 1.12.1, 1.12.2), 1.13 (17w50a, 1.13, 1.13.1, 1.13.2-pre1, 1.13.2-pre2, 1.13.2), 1.14 (1.14, 1.14.1, 1.14.3, 1.14.4), 1.15 (1.15, 1.15.1, 1.15.2), 1.16 (20w13b, 20w14a, 1.16-rc1, 1.16, 1.16.1, 1.16.2, 1.16.3, 1.16.4, 1.16.5), 1.17, 1.17.1, 1.18 (1.18, 1.18.1, 1.18.2), 1.19 (1.19, 1.19.2, 1.19.3, 1.19.4), 1.20 (1.20, 1.20.1, 1.20.2, 1.20.3, 1.20.4, 1.20.5, 1.20.6), 1.21 (1.21, 1.21.1, 1.21.3), 1.21.8, 1.21.9, 1.21.10
<!--NEXT PC-->
* Minecraft bedrock version 0.14, 0.15, 1.0, 1.16.201, 1.16.210, 1.16.220, 1.17.0, 1.17.10, 1.17.30, 1.17.40, 1.18.0, 1.18.11, 1.18.30, 1.19.1, 1.19.10, 1.19.20, 1.19.21, 1.19.30, 1.19.40, 1.19.50, 1.19.60, 1.19.62, 1.19.63, 1.19.70, 1.19.80, 1.20.0, 1.20.10, 1.20.30, 1.20.40, 1.20.50, 1.20.61, 1.20.71, 1.20.80, 1.21.0, 1.21.2, 1.21.20, 1.21.30, 1.21.42, 1.21.50, 1.21.60, 1.21.70, 1.21.80, 1.21.90, 1.21.93, 1.21.100, 1.21.111, 1.21.120, 1.21.124, 1.21.130, 1.26.0
<!--NEXT BEDROCK-->
## Wrappers
Minecraft-data is language independent, you can use it with these language specific modules :
| Wrapper name | Language | Data provided |
| --- | --- | --- |
| [node-minecraft-data](https://github.com/PrismarineJS/node-minecraft-data) | Node.js | everything |
| [python-minecraft-data](https://github.com/SpockBotMC/python-minecraft-data) | python | everything |
| [minebase](https://github.com/py-mine/minebase) | python | everything |
| [McData](https://github.com/McEx/McData) | Elixir | protocol |
| [ProtocolGen](https://github.com/Johni0702/ProtocolGen) | java | generated java files from protocol.json to read and write minecraft packets |
| [mcdata](https://github.com/wlwanpan/mcdata) | Go | everything |
| [minecraft-data-rs](https://github.com/Trivernis/minecraft-data-rs) | Rust | everything |
If you want to use minecraft-data in a new language, we advise you to [create a new wrapper](doc/make-a-new-wrapper.md)
## Data
Data provided:
| Data | Description |
| --- | --- |
| Biomes | |
| Blocks | |
| Enchantment | |
| Effect | Status/Potion effects |
| Entities | |
| Language | language code and their english strings |
| Instruments | sounds emitted by blocks |
| Items | |
| Map icons | Provides all available map icons as used by (crafted) maps |
| Materials | to be used with blocks data. Provides tool speed multipliers against material types. |
| Particles | Id and names of minecraft particles |
| Protocol | a complete description of the protocol, can be used to automatically implement the protocol, see [protodef doc](https://github.com/ProtoDef-io/ProtoDef) |
| Protocol Versions | the whole list of minecraft protocol versions |
| Recipes | to be used with blocks and items data |
| Windows | the different kind of windows and their characteristics |
| Version | |
| Versions | all the versions supported in minecraft-data |
| Block collision shapes | |
| Foods | list of foods each with there id, saturation, foodpoints and more |
| Commands | a tree structure for vanilla minecraft server commands, and some info needed to implement sub-parsers.
| Legacy | mappings between legacy (1.12) and post-flattening (1.13+) blocks and items ids
| Skin data | (bedrock edition) Skin geometry and texture data for steve skin
| Features | This can be used to check is a specific feature is available in the current Minecraft version. This is usually only required for handling version-specific functionality.
| Sounds | For versions that use a sound id over the network (1.9+) this provides mappings between network sound id and sound name.
See more information about this data in the [documentation](http://prismarinejs.github.io/minecraft-data/)
## Documentation
* See [doc/history.md](doc/history.md)
* [Documentation generated using the json schemas and docson](http://prismarinejs.github.io/minecraft-data)
* [Textual documentation of the recipe format](doc/recipes.md)
## Projects using minecraft-data
These are the projects that use minecraft-data:
| Project name | Language | Project type | Data used |
| --- | --- | --- | --- |
| [mineflayer](https://github.com/PrismarineJS/mineflayer) | Node.js | bot library | biomes, blocks, entities, instruments, items, materials, recipes |
| [node-minecraft-protocol](https://github.com/PrismarineJS/node-minecraft-protocol) | Node.js | protocol serialization | protocol |
| [flying-squid](https://github.com/PrismarineJS/flying-squid) | Node.js | server library | biomes, blocks, materials |
| [SpockBot](https://github.com/SpockBotMC/SpockBot) | Python | bot library | biomes, blocks, items, materials, windows, recipes |
| [PhaseBot](https://github.com/phase/PhaseBot) | Java | bot | blocks, items, materials |
| [McEx](https://github.com/hansihe/McEx) | Elixir | server | blocks |
| [VoxelGamesLib](https://github.com/MiniDigger/VoxelGamesLib) | Java | minigames server library | blocks, items |
| [Phpcraft](https://github.com/Phpcraft/core) | PHP | all-around library | blocks, items, entities, protocol |
| [MineSharp](https://github.com/psu-de/MineSharp) | C# | Bots | Protocol, Blocks, Items, Entities, Biomes, Recipes, Enchantments |
## Extraction
### Java Edition
The recommended way to extract the data is documented at [add-data-new-version.md](https://github.com/PrismarineJS/minecraft-data/blob/master/doc/add-data-new-version.md).
The [minecraft-data-auto-updater](https://github.com/PrismarineJS/minecraft-data-auto-updater) calls [minecraft-data-generator-server](https://github.com/PrismarineJS/minecraft-data-generator-server).
### Bedrock Edition
See [bedrock.md](./doc/bedrock.md) for information on updating data.
Specifically, [minecraft-data-auto-updater](https://github.com/PrismarineJS/minecraft-data-auto-updater) calls [minecraft-data-generator-server](https://github.com/PrismarineJS/minecraft-data-generator-server)
Projects that provide data:
| Project name | Language | Source | Data provided |
| --- | --- | --- | --- |
| [minecraft-wiki-extractor](https://github.com/PrismarineJS/minecraft-wiki-extractor) | Node.js | [minecraft wiki](https://minecraft.wiki) | blocks, items, entities, recipes |
| [mcdevs-wiki-extractor](https://github.com/PrismarineJS/mcdevs-wiki-extractor) | Node.js | [wiki.vg](http://wiki.vg/Protocol) | a partial protocol, entities |
| [node-minecraft-extractor](https://github.com/PrismarineJS/node-minecraft-extractor) | Node.js | merge between wiki.vg and mcwiki | a complete entities file |
| [minecraft-jar-extractor](https://github.com/PrismarineJS/minecraft-jar-extractor) | Node.js | minecraft server jar | a very partial but completely up to date protocol |
| [minecraft-jar-extractor in python](https://github.com/pangeacake/minecraft-jar-extractor) | Python | minecraft server jar | about the same protocol information as minecraft-jar-extractor in node.js |
| [burger](https://github.com/mcdevs/Burger) with [burger-extractor](https://github.com/PrismarineJS/burger-extractor) | Node.js | minecraft server jar | used to provide items, blocks, biomes and recipes |
| [burger 1.16](https://github.com/Pokechu22/Burger/tree/1.16-dev) | --//-- | --// -- | --//-- |
| [command-generator](https://github.com/Miro-Andrin/mc-data-command-generator) | Python | [Minecraft data generator](https://wiki.vg/Data_Generators) | Data used for parsing all vanilla server commands. |
| [minecraft-data-generator](https://github.com/Archengius/minecraft-data-generator) | Java | minecraft client | biomes, block collision shapes, blocks, effects, enchantments, entities, foods, items, particles, tints. Required Fabric updated to the provided minecraft version. |
Pages interesting to manually update the data if necessary:
| Page | Data |
| ---- | ---- |
| [wiki.vg Inventory page](http://wiki.vg/Inventory) | windows |
| [wiki.vg](http://wiki.vg/Block_Actions) | instruments |
| [a mineflayer PR](https://github.com/PrismarineJS/mineflayer/pull/197) | biomes |
| [mcwiki effects page](https://minecraft.wiki/w/Effect) | effects |
| [wiki.vg protocol version numbers](http://wiki.vg/Protocol_version_numbers) | protocolVersions |
## Data quality
Minecraft data provides scripts to audit the data, they can be useful to check the data is correct :
* [audit_blocks](tools/js/test/audit_blocks.js)
* [audit_items](tools/js/test/audit_items.js)
* [audit_recipes](tools/js/test/audit_recipes.js)
* [audit_shapes](tools/js/test/audit_shapes.js)
Minecraft data also provides json schemas in enums_schemas/ that are used in
test/test.js to check the json file are valid relative to these schemas.
These schemas can also be used to understand better how the json files are
formatted in order to use it.
## Contribute
Please read https://github.com/PrismarineJS/prismarine-contribute
## License
MIT
Some of the data was extracted manually or automatically from wiki.vg and minecraft.gamepedia.com.
If required by one of the sources the license might change to something more appropriate.
## Related project
[minecraft-assets](https://github.com/rom1504/minecraft-assets) which provide minecraft assets along with json files that help to use them.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
{
"version":70,
"minecraftVersion":"0.14.3",
"majorVersion":"0.14"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
{
"version":82,
"minecraftVersion":"0.15.6",
"majorVersion":"0.15"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
{
"version":100,
"minecraftVersion":"1.0.0",
"majorVersion":"1.0"
}
@@ -0,0 +1,107 @@
[
{
"name": "followRange",
"resource": "generic.follow_range",
"default": 32,
"min": 0,
"max": 2048
},
{
"name": "knockbackResistance",
"resource": "generic.knockback_resistance",
"default": 0,
"min": 0,
"max": 1
},
{
"name": "movementSpeed",
"resource": "generic.movement_speed",
"default": 0.7,
"min": 0,
"max": 1024
},
{
"name": "attackDamage",
"resource": "generic.attack_damage",
"default": 2,
"min": 0,
"max": 2048
},
{
"name": "horseJumpStrength",
"resource": "horse.jump_strength",
"default": 0.7,
"min": 0,
"max": 2
},
{
"name": "zombieSpawnReinforcements",
"resource": "zombie.spawn_reinforcements",
"default": 0,
"min": 0,
"max": 1
},
{
"name": "flyingSpeed",
"resource": "generic.flying_speed",
"default": 0.4,
"min": 0,
"max": 1024
},
{
"name": "luck",
"resource": "generic.luck",
"default": 0,
"min": -1024,
"max": 1024
},
{
"name": "health",
"resource": "health",
"default": 20,
"min": 0,
"max": 20
},
{
"name": "absorption",
"resource": "absorption",
"default": 0,
"min": 0,
"max": 1024
},
{
"name": "exhaustion",
"resource": "player.exhaustion",
"default": 0,
"min": 0,
"max": 5
},
{
"name": "experienceProgress",
"resource": "player.experience",
"default": 0,
"min": 0,
"max": 1
},
{
"name": "experienceLevel",
"resource": "player.level",
"default": 0,
"min": 0,
"max": 24791
},
{
"name": "hunger",
"resource": "player.hunger",
"default": 20,
"min": 0,
"max": 20
},
{
"name": "saturation",
"resource": "player.saturation",
"default": 20,
"min": 0,
"max": 20
}
]
@@ -0,0 +1,833 @@
[
{
"id": 0,
"name": "protection",
"displayName": "Protection",
"maxLevel": 4,
"minCost": {
"a": 11,
"b": -10
},
"maxCost": {
"a": 11,
"b": 1
},
"treasureOnly": false,
"curse": false,
"exclude": [
"fire_protection",
"blast_protection",
"projectile_protection"
],
"category": "armor",
"weight": 10,
"tradeable": true,
"discoverable": true
},
{
"id": 1,
"name": "fire_protection",
"displayName": "Fire Protection",
"maxLevel": 4,
"minCost": {
"a": 8,
"b": 2
},
"maxCost": {
"a": 8,
"b": 10
},
"treasureOnly": false,
"curse": false,
"exclude": [
"protection",
"blast_protection",
"projectile_protection"
],
"category": "armor",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 2,
"name": "feather_falling",
"displayName": "Feather Falling",
"maxLevel": 4,
"minCost": {
"a": 6,
"b": -1
},
"maxCost": {
"a": 6,
"b": 5
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "armor_feet",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 3,
"name": "blast_protection",
"displayName": "Blast Protection",
"maxLevel": 4,
"minCost": {
"a": 8,
"b": -3
},
"maxCost": {
"a": 8,
"b": 5
},
"treasureOnly": false,
"curse": false,
"exclude": [
"protection",
"fire_protection",
"projectile_protection"
],
"category": "armor",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 4,
"name": "projectile_protection",
"displayName": "Projectile Protection",
"maxLevel": 4,
"minCost": {
"a": 6,
"b": -3
},
"maxCost": {
"a": 6,
"b": 3
},
"treasureOnly": false,
"curse": false,
"exclude": [
"protection",
"fire_protection",
"blast_protection"
],
"category": "armor",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 5,
"name": "thorns",
"displayName": "Thorns",
"maxLevel": 3,
"minCost": {
"a": 20,
"b": -10
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "armor_chest",
"weight": 1,
"tradeable": true,
"discoverable": true
},
{
"id": 6,
"name": "respiration",
"displayName": "Respiration",
"maxLevel": 3,
"minCost": {
"a": 10,
"b": 0
},
"maxCost": {
"a": 10,
"b": 30
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "armor_head",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 7,
"name": "depth_strider",
"displayName": "Depth Strider",
"maxLevel": 3,
"minCost": {
"a": 10,
"b": 0
},
"maxCost": {
"a": 10,
"b": 15
},
"treasureOnly": false,
"curse": false,
"exclude": [
"frost_walker"
],
"category": "armor_feet",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 8,
"name": "aqua_affinity",
"displayName": "Aqua Affinity",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 1
},
"maxCost": {
"a": 0,
"b": 41
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "armor_head",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 9,
"name": "sharpness",
"displayName": "Sharpness",
"maxLevel": 5,
"minCost": {
"a": 11,
"b": -10
},
"maxCost": {
"a": 11,
"b": 10
},
"treasureOnly": false,
"curse": false,
"exclude": [
"smite",
"bane_of_arthropods"
],
"category": "weapon",
"weight": 10,
"tradeable": true,
"discoverable": true
},
{
"id": 10,
"name": "smite",
"displayName": "Smite",
"maxLevel": 5,
"minCost": {
"a": 8,
"b": -3
},
"maxCost": {
"a": 8,
"b": 17
},
"treasureOnly": false,
"curse": false,
"exclude": [
"sharpness",
"bane_of_arthropods"
],
"category": "weapon",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 11,
"name": "bane_of_arthropods",
"displayName": "Bane of Arthropods",
"maxLevel": 5,
"minCost": {
"a": 8,
"b": -3
},
"maxCost": {
"a": 8,
"b": 17
},
"treasureOnly": false,
"curse": false,
"exclude": [
"sharpness",
"smite"
],
"category": "weapon",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 12,
"name": "knockback",
"displayName": "Knockback",
"maxLevel": 2,
"minCost": {
"a": 20,
"b": -15
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "weapon",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 13,
"name": "fire_aspect",
"displayName": "Fire Aspect",
"maxLevel": 2,
"minCost": {
"a": 20,
"b": -10
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "weapon",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 14,
"name": "looting",
"displayName": "Looting",
"maxLevel": 3,
"minCost": {
"a": 9,
"b": 6
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [
"silk_touch"
],
"category": "weapon",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 15,
"name": "efficiency",
"displayName": "Efficiency",
"maxLevel": 5,
"minCost": {
"a": 10,
"b": -9
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "digger",
"weight": 10,
"tradeable": true,
"discoverable": true
},
{
"id": 16,
"name": "silk_touch",
"displayName": "Silk Touch",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 15
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [
"looting",
"fortune",
"luck_of_the_sea"
],
"category": "digger",
"weight": 1,
"tradeable": true,
"discoverable": true
},
{
"id": 17,
"name": "unbreaking",
"displayName": "Unbreaking",
"maxLevel": 3,
"minCost": {
"a": 8,
"b": -3
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "breakable",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 18,
"name": "fortune",
"displayName": "Fortune",
"maxLevel": 3,
"minCost": {
"a": 9,
"b": 6
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [
"silk_touch"
],
"category": "digger",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 19,
"name": "power",
"displayName": "Power",
"maxLevel": 5,
"minCost": {
"a": 10,
"b": -9
},
"maxCost": {
"a": 10,
"b": 6
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "bow",
"weight": 10,
"tradeable": true,
"discoverable": true
},
{
"id": 20,
"name": "punch",
"displayName": "Punch",
"maxLevel": 2,
"minCost": {
"a": 20,
"b": -8
},
"maxCost": {
"a": 20,
"b": 17
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "bow",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 21,
"name": "flame",
"displayName": "Flame",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 20
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "bow",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 22,
"name": "infinity",
"displayName": "Infinity",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 20
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": false,
"curse": false,
"exclude": [
"mending"
],
"category": "bow",
"weight": 1,
"tradeable": true,
"discoverable": true
},
{
"id": 23,
"name": "luck_of_the_sea",
"displayName": "Luck of the Sea",
"maxLevel": 3,
"minCost": {
"a": 9,
"b": 6
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [
"silk_touch"
],
"category": "fishing_rod",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 24,
"name": "lure",
"displayName": "Lure",
"maxLevel": 3,
"minCost": {
"a": 9,
"b": 6
},
"maxCost": {
"a": 10,
"b": 51
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "fishing_rod",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 25,
"name": "frost_walker",
"displayName": "Frost Walker",
"maxLevel": 2,
"minCost": {
"a": 10,
"b": 0
},
"maxCost": {
"a": 10,
"b": 15
},
"treasureOnly": true,
"curse": false,
"exclude": [
"depth_strider"
],
"category": "armor_feet",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 26,
"name": "mending",
"displayName": "Mending",
"maxLevel": 1,
"minCost": {
"a": 25,
"b": 0
},
"maxCost": {
"a": 25,
"b": 50
},
"treasureOnly": true,
"curse": false,
"exclude": [
"infinity"
],
"category": "breakable",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 27,
"name": "binding_curse",
"displayName": "Curse of Binding",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 25
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": true,
"curse": true,
"exclude": [],
"category": "wearable",
"weight": 1,
"tradeable": true,
"discoverable": true
},
{
"id": 28,
"name": "vanishing_curse",
"displayName": "Curse of Vanishing",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 25
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": true,
"curse": true,
"exclude": [],
"category": "vanishable",
"weight": 1,
"tradeable": true,
"discoverable": true
},
{
"id": 29,
"name": "impaling",
"displayName": "Impaling",
"maxLevel": 5,
"minCost": {
"a": 8,
"b": -7
},
"maxCost": {
"a": 8,
"b": 13
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "trident",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 30,
"name": "riptide",
"displayName": "Riptide",
"maxLevel": 3,
"minCost": {
"a": 7,
"b": 10
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": false,
"curse": false,
"exclude": [
"loyalty",
"channeling"
],
"category": "trident",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 31,
"name": "loyalty",
"displayName": "Loyalty",
"maxLevel": 3,
"minCost": {
"a": 7,
"b": 5
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": false,
"curse": false,
"exclude": [
"riptide"
],
"category": "trident",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 32,
"name": "channeling",
"displayName": "Channeling",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 25
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": false,
"curse": false,
"exclude": [
"riptide"
],
"category": "trident",
"weight": 1,
"tradeable": true,
"discoverable": true
},
{
"id": 33,
"name": "multishot",
"displayName": "Multishot",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 20
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": false,
"curse": false,
"exclude": [
"piercing"
],
"category": "crossbow",
"weight": 2,
"tradeable": true,
"discoverable": true
},
{
"id": 34,
"name": "piercing",
"displayName": "Piercing",
"maxLevel": 4,
"minCost": {
"a": 10,
"b": -9
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": false,
"curse": false,
"exclude": [
"multishot"
],
"category": "crossbow",
"weight": 10,
"tradeable": true,
"discoverable": true
},
{
"id": 35,
"name": "quick_charge",
"displayName": "Quick Charge",
"maxLevel": 3,
"minCost": {
"a": 20,
"b": -8
},
"maxCost": {
"a": 0,
"b": 50
},
"treasureOnly": false,
"curse": false,
"exclude": [],
"category": "crossbow",
"weight": 5,
"tradeable": true,
"discoverable": true
},
{
"id": 36,
"name": "soul_speed",
"displayName": "Soul Speed",
"maxLevel": 3,
"minCost": {
"a": 10,
"b": 0
},
"maxCost": {
"a": 10,
"b": 15
},
"treasureOnly": true,
"curse": false,
"exclude": [],
"category": "armor_feet",
"weight": 1,
"tradeable": false,
"discoverable": false
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,978 @@
# !StartDocs: Types
BehaviourPackInfos: []li16
uuid: string
version: string
size: lu64
content_key: string
sub_pack_name: string
content_identity: string
has_scripts: bool
TexturePackInfos: []li16
uuid: string
version: string
size: lu64
content_key: string
sub_pack_name: string
content_identity: string
has_scripts: bool
rtx_enabled: bool
ResourcePackIdVersions: []varint
# The ID of the resource pack.
uuid: string
# The version of the resource pack.
version: string
# The subpack name of the resource pack.
name: string
ResourcePackIds: string[]li16
Experiment:
name: string
enabled: bool
Experiments: Experiment[]li32
GameMode: zigzag32 =>
0: survival
1: creative
2: adventure
3: survival_spectator
4: creative_spectator
5: fallback
GameRule:
name: string
type: varint =>
1: bool
2: int
3: float
value: type?
if bool: bool
if int: zigzag32
if float: lf32
GameRules: GameRule[]varint
# CacheBlob represents a blob as used in the client side blob cache protocol. It holds a hash of its data and
# the full data of it.
Blob:
# Hash is the hash of the blob. The hash is computed using xxHash, and must be deterministic for the same
# chunk data.
hash: lu64
# Payload is the data of the blob. When sent, the client will associate the Hash of the blob with the
# Payload in it.
payload: ByteArray
BlockPalette: []varint
name: string
state: nbt
Itemstates: []varint
name: string
runtime_id: li16
component_based: bool
Item:
network_id: zigzag32
_: network_id?
if 0: void
default:
auxiliary_value: zigzag32
has_nbt: lu16 =>
0xffff: 'true'
0x0000: 'false'
nbt: has_nbt?
if true:
version: u8
nbt: nbt
default: void
can_place_on: string[]zigzag32
can_destroy: string[]zigzag32
_: network_id?
if 355:
blocking_tick: zigzag64
vec3i:
x: zigzag32
y: zigzag32
z: zigzag32
vec3u:
x: varint
y: varint
z: varint
vec3f:
x: lf32
y: lf32
z: lf32
vec2f:
x: lf32
z: lf32
MetadataDictionary: []varint
# https://github.com/pmmp/PocketMine-MP/blob/stable/src/pocketmine/entity/Entity.php#L101
key: varint =>
0: index
1: health #int (minecart/boat)
2: variant #int
3: color #byte
4: nametag #string
5: owner_eid #long
6: target_eid #long
7: air #short
8: potion_color #int (ARGB!)
9: potion_ambient #byte
10: jump_duration #long
11: hurt_time #int (minecart/boat)
12: hurt_direction #int (minecart/boat)
13: paddle_time_left #float
14: paddle_time_right #float
15: experience_value #int (xp orb)
16: minecart_display_block #int (id | (data << 16))
17: minecart_display_offset #int
18: minecart_has_display #byte (must be 1 for minecart to show block inside)
20: old_swell
21: swell_dir
22: charge_amount
23: enderman_held_runtime_id #short
24: entity_age #short
26: player_flags
27: player_index
28: player_bed_position #block coords
29: fireball_power_x #float
30: fireball_power_y
31: fireball_power_z
32: aux_power
33: fish_x
34: fish_z
35: fish_angle
36: potion_aux_value #short
37: lead_holder_eid #long
38: scale
39: interactive_tag #string
40: npc_skin_id #string
41: url_tag #string
42: max_airdata_max_air
43: mark_variant #int
44: container_type #byte
45: container_base_size #int
46: container_extra_slots_per_strength #int
47: block_target
48: wither_invulnerable_ticks #int
49: wither_target_1 #long
50: wither_target_2 #long
51: wither_target_3 #long
52: aerial_attack
53: boundingbox_width
54: boundingbox_height
55: fuse_length
56: rider_seat_position #vector3f
57: rider_rotation_locked #byte
58: rider_max_rotation #float
59: rider_min_rotation #float
60: area_effect_cloud_radius #float
61: area_effect_cloud_waiting #int
62: area_effect_cloud_particle_id #int
63: shulker_peek_id #int
64: shulker_attach_face #byte
65: shulker_attached #short
66: shulker_attach_pos
67: trading_player_eid #long
68: trading_career
69: has_command_block
70: command_block_command #string
71: command_block_last_output #string
72: command_block_track_output #byte
73: controlling_rider_seat_number #byte
74: strength #int
75: max_strength #int
76: spell_casting_color #int
77: limited_life
78: armor_stand_pose_index # int
79: ender_crystal_time_offset # int
80: always_show_nametag # byte
81: color_2 # byte
82: name_author
83: score_tag #String
84: balloon_attached_entity # long
85: pufferfish_size
86: bubble_time
87: agent
88: sitting_amount
89: sitting_amount_previous
90: eating_counter
91: flags_extended
92: laying_amount
93: laying_amount_previous
94: duration
95: spawn_time
96: change_rate
97: change_on_pickup
98: pickup_count
99: interact_text
100: trade_tier
101: max_trade_tier
102: trade_experience
103: skin_id
104: spawning_frames
105: command_block_tick_delay
106: command_block_execute_on_first_tick
107: ambient_sound_interval
108: ambient_sound_interval_range
109: ambient_sound_event_name
110: fall_damage_multiplier
111: name_raw_text
112: can_ride_target
113: low_tier_cured_discount
114: high_tier_cured_discount
115: nearby_cured_discount
116: nearby_cured_discount_timestamp
117: hitbox
118: is_buoyant
119: buoyancy_data
type: varint =>
0: byte
1: short
2: int
3: float
4: string
5: compound
6: vec3i
7: long
8: vec3f
value: type?
if byte: i8
if short: li16
if int: zigzag32
if float: lf32
if string: string
if compound: nbt
if vec3i: vec3i
if long: zigzag64
if vec3f: vec3f
Link:
ridden_entity_id: zigzag64
rider_entity_id: zigzag64
type: u8
immediate: bool
rider_initiated: bool
Links: Link[]varint
EntityAttributes: []varint
name: string
min: lf32
value: lf32
max: lf32
Rotation:
yaw: byterot
pitch: byterot
head_yaw: byterot
BlockCoordinates: # mojang...
x: zigzag32
y: varint
z: zigzag32
PlayerAttributes: []varint
min: lf32
max: lf32
current: lf32
default: lf32
name: string
Transaction:
# LegacyRequestID is an ID that is only non-zero at times when sent by the client. The server should
# always send 0 for this. When this field is not 0, the LegacySetItemSlots slice below will have values
# in it.
# LegacyRequestID ties in with the ItemStackResponse packet. If this field is non-0, the server should
# respond with an ItemStackResponse packet. Some inventory actions such as dropping an item out of the
# hotbar are still one using this packet, and the ItemStackResponse packet needs to tie in with it.
legacy_request_id: zigzag32
# `legacy_transactions` are only present if the LegacyRequestID is non-zero. These item slots inform the
# server of the slots that were changed during the inventory transaction, and the server should send
# back an ItemStackResponse packet with these slots present in it. (Or false with no slots, if rejected.)
legacy_transactions: legacy_request_id?
if 0: void
default: []varint
container_id: u8
changed_slots: []varint
slot_id: u8
transaction_type: varint =>
# Sent for container UI operations depending on if ItemStackNetManager is enabled
0: normal
# Sent from server to client to reject a transaction
1: inventory_mismatch
# Sent for a player performing right click style item use.
# See the contained ItemUseInventoryTransaction::ActionType for the expected use case.
2: item_use
# Sent for a player right clicking on an entity or attacking them.
# See ItemUseInventoryTransaction::ActionType for which it is.
3: item_use_on_entity
# Sent when releasing right click on a chargeable item like a bow or finishing charging like a crossbow.
# This is different than canceling item use early which would be in Player Auth Input.
# See ItemReleaseInventoryTransaction::ActionType for which it is.
4: item_release
network_ids: bool
inventory_actions: []varint
source_type: varint =>
0: container
1: global
2: world_interaction
3: creative
100: craft_slot
99999: craft
_: source_type?
if container or creative:
inventory_id: varint
if world_interaction:
flags: varint
if craft or craft_slot:
action: varint
default: void
slot: varint
old_item: Item
new_item: Item
new_item_stack_id: ../network_ids?
if true: zigzag32
default: void
transaction_data: transaction_type?
if normal or inventory_mismatch: void
# UseItemTransactionData represents an inventory transaction data object sent when the client uses an item on
# a block.
if item_use:
# ActionType is the type of the UseItem inventory transaction. It is one of the action types found above,
# and specifies the way the player interacted with the block.
action_type: varint =>
0: click_block
1: click_air
2: break_block
# BlockPosition is the position of the block that was interacted with. This is only really a correct
# block position if ActionType is not UseItemActionClickAir.
block_position: BlockCoordinates
# BlockFace is the face of the block that was interacted with. When clicking the block, it is the face
# clicked. When breaking the block, it is the face that was last being hit until the block broke.
face: varint
# HotBarSlot is the hot bar slot that the player was holding while clicking the block. It should be used
# to ensure that the hot bar slot and held item are correctly synchronised with the server.
hotbar_slot: varint
# HeldItem is the item that was held to interact with the block. The server should check if this item
# is actually present in the HotBarSlot.
held_item: Item
# Position is the position of the player at the time of interaction. For clicking a block, this is the
# position at that time, whereas for breaking the block it is the position at the time of breaking.
player_pos: vec3f
# ClickedPosition is the position that was clicked relative to the block's base coordinate. It can be
# used to find out exactly where a player clicked the block.
click_pos: vec3f
# BlockRuntimeID is the runtime ID of the block that was clicked. It may be used by the server to verify
# that the player's world client-side is synchronised with the server's.
block_runtime_id: varint
# UseItemOnEntityTransactionData represents an inventory transaction data object sent when the client uses
# an item on an entity.
if item_use_on_entity:
# TargetEntityRuntimeID is the entity runtime ID of the target that was clicked. It is the runtime ID
# that was assigned to it in the AddEntity packet.
entity_runtime_id: varint64
# ActionType is the type of the UseItemOnEntity inventory transaction. It is one of the action types
# found in the constants above, and specifies the way the player interacted with the entity.
action_type: varint =>
# Right click interact with actor.
0: interact
# Left click style attack of actor or elytra spin attack. Server is expected to deal damage to the entity with visuals.
1: attack
# HotBarSlot is the hot bar slot that the player was holding while clicking the entity. It should be used
# to ensure that the hot bar slot and held item are correctly synchronised with the server.
hotbar_slot: zigzag32
# HeldItem is the item that was held to interact with the entity. The server should check if this item
# is actually present in the HotBarSlot.
held_item: Item
# Position is the position of the player at the time of clicking the entity.
player_pos: vec3f
# ClickedPosition is the position that was clicked relative to the entity's base coordinate. It can be
# used to find out exactly where a player clicked the entity.
click_pos: vec3f
# ReleaseItemTransactionData represents an inventory transaction data object sent when the client releases
# the item it was using, for example when stopping while eating or stopping the charging of a bow.
if item_release:
# ActionType is the type of the ReleaseItem inventory transaction. It is one of the action types found
# in the constants above, and specifies the way the item was released.
# As of 1.13, the ActionType is always 0. This field can be ignored, because releasing food (by consuming
# it) or releasing a bow (to shoot an arrow) is essentially the same.
action_type: varint =>
# Release right click and hold style item use, like firing a bow
0: release
# Finish right click and hold style item use, like charging a crossbow
1: consume
# HotBarSlot is the hot bar slot that the player was holding while releasing the item. It should be used
# to ensure that the hot bar slot and held item are correctly synchronised with the server.
hotbar_slot: zigzag32
# HeldItem is the item that was released. The server should check if this item is actually present in the
# HotBarSlot.
held_item: Item
# HeadPosition is the position of the player's head at the time of releasing the item. This is used
# mainly for purposes such as spawning eating particles at that position.
head_pos: vec3f
# An "ItemStack" here represents an Item instance. You can think about it like a pointer
# to an item class. The data for the class gets updated with the data in the `item` field
ItemStack:
# StackNetworkID is the network ID of the item stack. If the stack is empty, 0 is always written for this
# field. If not, the field should be set to 1 if the server authoritative inventories are disabled in the
# StartGame packet, or to a unique stack ID if it is enabled.
runtime_id: zigzag32
# Stack is the actual item stack of the item instance.
item: Item
ItemStacks: ItemStack[]varint
RecipeIngredient:
network_id: zigzag32
_: network_id?
if 0: void
default:
network_data: zigzag32
count: zigzag32
PotionTypeRecipes: []varint
input_item_id: zigzag32
input_item_meta: zigzag32
ingredient_id: zigzag32
ingredient_meta: zigzag32
output_item_id: zigzag32
output_item_meta: zigzag32
PotionContainerChangeRecipes: []varint
input_item_id: zigzag32
ingredient_id: zigzag32
output_item_id: zigzag32
Recipes: []varint
type: zigzag32 =>
'0': 'shapeless' #'ENTRY_SHAPELESS',
'1': 'shaped' #'ENTRY_SHAPED',
'2': 'furnace' # 'ENTRY_FURNACE',
# `furnace_with_metadata` is a recipe specifically used for furnace-type crafting stations. It is equal to
# `furnace`, except it has an input item with a specific metadata value, instead of any metadata value.
'3': 'furnace_with_metadata' # 'ENTRY_FURNACE_DATA', // has metadata
'4': 'multi' #'ENTRY_MULTI', //TODO
'5': 'shulker_box' #'ENTRY_SHULKER_BOX', //TODO
'6': 'shapeless_chemistry' #'ENTRY_SHAPELESS_CHEMISTRY', //TODO
'7': 'shaped_chemistry' #'ENTRY_SHAPED_CHEMISTRY', //TODO
recipe: type?
if shapeless or shulker_box or shapeless_chemistry:
recipe_id: LatinString
input: RecipeIngredient[]varint
output: Item[]varint
uuid: uuid
block: string
priority: zigzag32
network_id: zigzag32
if shaped or shaped_chemistry:
recipe_id: LatinString
width: zigzag32
height: zigzag32
# todo: can this become
# RecipeIngredient[$height][$width] or RecipeIngredient[]$height[]$width ?
input: []$width
_: RecipeIngredient[]$height
output: Item[]varint
uuid: uuid
block: string
priority: zigzag32
network_id: zigzag32
if furnace:
input_id: zigzag32
output: Item
block: string
if furnace_with_metadata:
input_id: zigzag32
input_meta: zigzag32
output: Item
block: string
if multi:
uuid: uuid
network_id: zigzag32
SkinImage:
width: li32
height: li32
data: string
Skin:
skin_id: string
skin_resource_pack: string
skin_data: SkinImage
animations: []li32
skin_image: SkinImage
animation_type: li32
animation_frames: lf32
expression_type: lf32
cape_data: SkinImage
geometry_data: string
animation_data: string
premium: string
persona: bool
cape_on_classic: bool
cape_id: string
full_skin_id: string
arm_size: string
skin_color: string
personal_pieces: []li32
piece_id: string
piece_type: string
pack_id: string
is_default_piece: bool
product_id: string
piece_tint_colors: []li32
piece_type: string
colors: string[]li32
PlayerRecords:
type: u8 =>
0: add
1: remove
records_count: varint
records: []$records_count
_: type?
if add:
uuid: uuid
entity_unique_id: zigzag64
username: string
xbox_user_id: string
platform_chat_id: string
build_platform: li32
skin_data: Skin
is_teacher: bool
is_host: bool
if remove:
uuid: uuid
verified: bool[]$records_count
ScoreEntries:
type: u8 =>
0: change
1: remove
entries: []varint
scoreboard_id: zigzag64
objective_name: string
score: li32
_: type?
if remove:
entry_type: i8 =>
1: player
2: entity
3: fake_player
entity_unique_id: entry_type?
if player or entity: zigzag64
custom_name: entry_type?
if fake_player: string
ScoreboardIdentityEntries:
type: i8 =>
0: TYPE_REGISTER_IDENTITY
1: TYPE_CLEAR_IDENTITY
entries: []varint
scoreboard_id: zigzag64
entity_unique_id: type ?
if TYPE_REGISTER_IDENTITY: zigzag64
default: void
Enchant:
id: u8
level: u8
EnchantOptions: []varint
cost: varint
slot_flags: li32
equip_enchants: Enchant[]varint
held_enchants: Enchant[]varint
self_enchants: Enchant[]varint
name: string
option_id: zigzag32
StackRequestSlotInfo:
container_id: u8
slot_id: u8
stack_id: zigzag32
#
ItemStackRequests: []varint
# 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.
request_id: zigzag32
actions: []varint
type_id: u8 =>
# TakeStackRequestAction is sent by the client to the server to take x amount of items from one slot in a
# container to the cursor.
0: take
# 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.
1: place
# 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.
2: swap
# 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.
3: drop
# DestroyStackRequestAction is sent by the client when it destroys an item in creative mode by moving it
# back into the creative inventory.
4: destroy
# ConsumeStackRequestAction is sent by the client when it uses an item to craft another item. The original
# item is 'consumed'.
5: consume
# 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.
6: create
# LabTableCombineStackRequestAction is sent by the client when it uses a lab table to combine item stacks.
7: lab_table_combine
# 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.
8: beacon_payment
# 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.
9: craft_recipe
# 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.
10: craft_recipe_auto #recipe book?
# 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.
11: craft_creative
# CraftRecipeOptionalStackRequestAction is sent when using an anvil. When this action is sent, the
# CustomNames field in the respective stack request is non-empty and contains the name of the item created
# using the anvil.
12: optional
# CraftNonImplementedStackRequestAction is an action sent for inventory actions that aren't yet implemented
# in the new system. These include, for example, anvils.
13: non_implemented #anvils aren't fully implemented yet
# 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.
14: results_deprecated
_: type_id ?
if take or place:
count: u8
source: StackRequestSlotInfo
destination: StackRequestSlotInfo
if swap:
# 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: StackRequestSlotInfo
destination: StackRequestSlotInfo
if drop:
# Count is the count of the item in the source slot that was taken towards the destination slot.
count: u8
# 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
if destroy or consume:
# Count is the count of the item in the source slot that was destroyed.
count: u8
# Source is the source slot from which items came that were destroyed by moving them into the creative
# inventory.
source: StackRequestSlotInfo
if create:
# ResultsSlot is the slot in the inventory in which the results of the crafting ingredients are to be
# placed.
result_slot_id: u8
if beacon_payment:
# PrimaryEffect and SecondaryEffect are the effects that were selected from the beacon.
primary_effect: zigzag32
secondary_effect: zigzag32
if craft_recipe:
# 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.
recipe_network_id: varint
if craft_recipe_auto:
# 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
recipe_network_id: varint
times_crafted: u8
if craft_creative:
# 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.
creative_item_network_id: varint
if optional:
# For the cartography table, if a certain MULTI recipe is being called, this points to the network ID that was assigned.
recipe_network_id: varint
# Most likely the index in the request's filter strings that this action is using
filtered_string_index: li32
if non_implemented: void
if results_deprecated:
result_items: Item[]varint
times_crafted: u8
# CustomNames is a list of custom names involved in the request. This is typically filled with one string
# when an anvil is used.
# * Used for the server to determine which strings should be filtered. Used in anvils to verify a renamed item.
custom_names: string[]varint
ItemStackResponses: []varint
result: u8
request_id: varint
containers: []varint
container_id: u8
slots: []varint
slot: u8
hotbar_slot: u8
count: u8
item_stack_id: varint
custom_name: string
ItemComponentList: []varint
# Name is the name of the item, which is a name like 'minecraft:stick'.
name: string
# Data is a map containing the components and properties of the item.
nbt: nbt
CommandOrigin:
# Origin is one of the values above that specifies the origin of the command. The origin may change,
# depending on what part of the client actually called the command. The command may be issued by a
# websocket server, for example.
type: varint =>
0: player
1: block
2: minecart_block
3: dev_console
4: test
5: automation_player
6: client_automation
7: dedicated_server
8: entity
9: virtual
10: game_argument
11: entity_server
# UUID is the UUID of the command called. This UUID is a bit odd as it is not specified by the server. It
# is not clear what exactly this UUID is meant to identify, but it is unique for each command called.
uuid: uuid
# RequestID is an ID that identifies the request of the client. The server should send a CommandOrigin
# with the same request ID to ensure it can be matched with the request by the caller of the command.
# This is especially important for websocket servers and it seems that this field is only non-empty for
# these websocket servers.
request_id: string
# PlayerUniqueID is an ID that identifies the player, the same as the one found in the AdventureSettings
# packet. Filling it out with 0 seems to work.
# PlayerUniqueID is only written if Origin is CommandOriginDevConsole or CommandOriginTest.
player_entity_id: type?
if dev_console or test:
player_entity_id: zigzag64
# Some arbitrary definitions from CBMC, Window IDs are normally
# unique + sequential
WindowID: i8 =>
-100: drop_contents
-24: beacon
-23: trading_output
-22: trading_use_inputs
-21: trading_input_2
-20: trading_input_1
-17: enchant_output
-16: enchant_material
-15: enchant_input
-13: anvil_output
-12: anvil_result
-11: anvil_material
-10: container_input
-5: crafting_use_ingredient
-4: crafting_result
-3: crafting_remove_ingredient
-2: crafting_add_ingredient
-1: none
0: inventory
1: first
100: last
119: offhand
120: armor
121: creative
122: hotbar
123: fixed_inventory
124: ui
WindowIDVarint: varint =>
-100: drop_contents
-24: beacon
-23: trading_output
-22: trading_use_inputs
-21: trading_input_2
-20: trading_input_1
-17: enchant_output
-16: enchant_material
-15: enchant_input
-13: anvil_output
-12: anvil_result
-11: anvil_material
-10: container_input
-5: crafting_use_ingredient
-4: crafting_result
-3: crafting_remove_ingredient
-2: crafting_add_ingredient
-1: none
0: inventory
1: first
100: last
119: offhand
120: armor
121: creative
122: hotbar
123: fixed_inventory
124: ui
WindowType: u8 =>
0: container
1: workbench
2: furnace
3: enchantment
4: brewing_stand
5: anvil
6: dispenser
7: dropper
8: hopper
9: cauldron
10: minecart_chest
11: minecart_hopper
12: horse
13: beacon
14: structure_editor
15: trading
16: command_block
17: jukebox
18: armor
19: hand
20: compound_creator
21: element_constructor
22: material_reducer
23: lab_table
24: loom
25: lectern
26: grindstone
27: blast_furnace
28: smoker
29: stonecutter
30: cartography
31: hud
32: jigsaw_editor
33: smithing_table
# TODO: remove?
LegacyEntityType: li32 =>
10: chicken
11: cow
12: pig
13: sheep
14: wolf
15: villager
16: mooshroom
17: squid
18: rabbit
19: bat
20: iron_golem
21: snow_golem
22: ocelot
23: horse
24: donkey
25: mule
26: skeleton_horse
27: zombie_horse
28: polar_bear
29: llama
30: parrot
31: dolphin
32: zombie
33: creeper
34: skeleton
35: spider
36: zombie_pigman
37: slime
38: enderman
39: silverfish
40: cave_spider
41: ghast
42: magma_cube
43: blaze
44: zombie_villager
45: witch
46: stray
47: husk
48: wither_skeleton
49: guardian
50: elder_guardian
51: npc
52: wither
53: ender_dragon
54: shulker
55: endermite
56: agent # LEARN_TO_CODE_MASCOT
57: vindicator
58: phantom
61: armor_stand
62: tripod_camera
63: player
64: item
65: tnt
66: falling_block
67: moving_block
68: xp_bottle
69: xp_orb
70: eye_of_ender_signal
71: ender_crystal
72: fireworks_rocket
73: thrown_trident
74: turtle
75: cat
76: shulker_bullet
77: fishing_hook
78: chalkboard
79: dragon_fireball
80: arrow
81: snowball
82: egg
83: painting
84: minecart
85: fireball
86: splash_potion
87: ender_pearl
88: leash_knot
89: wither_skull
90: boat
91: wither_skull_dangerous
93: lightning_bolt
94: small_fireball
95: area_effect_cloud
96: hopper_minecart
97: tnt_minecart
98: chest_minecart
100: command_block_minecart
101: lingering_potion
102: llama_spit
103: evocation_fang
104: evocation_illager
105: vex
106: ice_bomb
107: balloon
108: pufferfish
109: salmon
110: drowned
111: tropicalfish
112: cod
113: panda
@@ -0,0 +1,6 @@
{
"version": 422,
"minecraftVersion": "1.16.201",
"majorVersion": "1.16",
"releaseType": "release"
}
@@ -0,0 +1,177 @@
[
{
"id": "cursor",
"name": "cursor",
"slots": [
{
"name": "cursor",
"index": 0
}
]
},
{
"id": "armor",
"name": "armor",
"slots": [
{
"name": "armor",
"index": 0,
"size": 4
}
]
},
{
"id": "hand",
"name": "hand",
"slots": [
{
"name": "offhand",
"index": 0
}
]
},
{
"id": "container",
"name": "container",
"slots": [
{
"name": "container",
"index": 0,
"size": 28
}
]
},
{
"id": "inventory",
"name": "Inventory",
"slots": [
{
"name": "hotbar_and_inventory",
"index": 0,
"size": 36
},
{
"name": "inventory",
"index": 9,
"size": 36
},
{
"name": "hotbar",
"index": 0,
"size": 9
}
]
},
{
"id": "workbench",
"name": "Crafting Table",
"slots": [
{
"name": "crafting_input",
"index": 0,
"size": 4
}
]
},
{
"id": "furnace",
"name": "furnace",
"slots": [
{
"name": "furnace_fuel",
"index": 1
},
{
"name": "furnace_ingredient",
"index": 0
}
]
},
{
"id": "enchantment",
"name": "enchantment",
"slots": [
{
"name": "enchanting_lapis",
"index": 15
},
{
"name": "enchanting_input",
"index": 14
}
]
},
{
"id": "brewing_stand",
"name": "brewing_stand",
"slots": [
{
"name": "brewing_input",
"index": 1,
"size": 3
},
{
"name": "brewing_fuel",
"index": 4
}
]
},
{
"id": "anvil",
"name": "anvil",
"slots": [
{
"name": "anvil_input",
"index": 1
},
{
"name": "anvil_material",
"index": 2
}
]
},
{
"id": "dispenser",
"name": "dispenser",
"slots": [
{
"name": "container",
"index": 0,
"size": 9
}
]
},
{
"id": "dropper",
"name": "dropper",
"slots": [
{
"name": "container",
"index": 0,
"size": 9
}
]
},
{
"id": "hopper",
"name": "hopper",
"slots": [
{
"name": "container",
"index": 0,
"size": 4
}
]
},
{
"id": "minecart_hopper",
"name": "minecart_hopper",
"slots": [
{
"name": "container",
"index": 0,
"size": 4
}
]
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,6 @@
{
"version": 428,
"minecraftVersion": "1.16.210",
"majorVersion": "1.16",
"releaseType": "release"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,6 @@
{
"version": 431,
"minecraftVersion": "1.16.220",
"majorVersion": "1.16",
"releaseType": "release"
}
@@ -0,0 +1,968 @@
[
{
"id": 0,
"name": "ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1,
"dimension": "overworld",
"displayName": "Ocean",
"color": 112,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 1,
"name": "plains",
"category": "plains",
"precipitation": "rain",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Plains",
"color": 9286496,
"rainfall": 0.4000000059604645,
"temperature": 0.800000011920929,
"child": 129
},
{
"id": 2,
"name": "desert",
"category": "desert",
"precipitation": "none",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Desert",
"color": 16421912,
"rainfall": 0,
"temperature": 2,
"child": 130
},
{
"id": 3,
"name": "extreme_hills",
"category": "extreme_hills",
"precipitation": "rain",
"depth": 1,
"dimension": "overworld",
"displayName": "Mountains",
"color": 6316128,
"rainfall": 0.30000001192092896,
"temperature": 0.20000000298023224,
"child": 131
},
{
"id": 4,
"name": "forest",
"category": "forest",
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Forest",
"color": 353825,
"rainfall": 0.800000011920929,
"temperature": 0.699999988079071,
"child": 132
},
{
"id": 5,
"name": "taiga",
"category": "taiga",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Taiga",
"color": 747097,
"rainfall": 0.800000011920929,
"temperature": 0.25,
"child": 133
},
{
"id": 6,
"name": "swampland",
"category": "swamp",
"precipitation": "rain",
"depth": -0.2,
"dimension": "overworld",
"displayName": "Swamp",
"color": 522674,
"rainfall": 0.5,
"temperature": 0.800000011920929,
"child": 134
},
{
"id": 7,
"name": "river",
"category": "river",
"precipitation": "rain",
"depth": -0.5,
"dimension": "overworld",
"displayName": "River",
"color": 255,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 8,
"name": "hell",
"category": "",
"precipitation": "rain",
"depth": 0,
"dimension": "overworld",
"displayName": "hell",
"color": 0,
"rainfall": 0,
"temperature": 2
},
{
"id": 9,
"name": "the_end",
"category": "the_end",
"precipitation": "none",
"depth": 0.1,
"dimension": "end",
"displayName": "The End",
"color": 8421631,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 11,
"name": "frozen_river",
"category": "river",
"precipitation": "snow",
"depth": -0.5,
"dimension": "overworld",
"displayName": "Frozen River",
"color": 10526975,
"rainfall": 0.5,
"temperature": 0
},
{
"id": 12,
"name": "ice_plains",
"category": "icy",
"precipitation": "snow",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Snowy Tundra",
"color": 16777215,
"rainfall": 0.5,
"temperature": 0,
"child": 140
},
{
"id": 13,
"name": "ice_mountains",
"category": "icy",
"precipitation": "snow",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Snowy Mountains",
"color": 10526880,
"rainfall": 0.5,
"temperature": 0
},
{
"id": 14,
"name": "mushroom_island",
"category": "mushroom",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Mushroom Fields",
"color": 16711935,
"rainfall": 1,
"temperature": 0.8999999761581421
},
{
"id": 15,
"name": "mushroom_island_shore",
"category": "mushroom",
"precipitation": "rain",
"depth": 0,
"dimension": "overworld",
"displayName": "Mushroom Fields Shore",
"color": 10486015,
"rainfall": 1,
"temperature": 0.8999999761581421
},
{
"id": 16,
"name": "beach",
"category": "beach",
"precipitation": "rain",
"depth": 0,
"dimension": "overworld",
"displayName": "Beach",
"color": 16440917,
"rainfall": 0.4000000059604645,
"temperature": 0.800000011920929
},
{
"id": 17,
"name": "desert_hills",
"category": "desert",
"precipitation": "none",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Desert Hills",
"color": 13786898,
"rainfall": 0,
"temperature": 2
},
{
"id": 18,
"name": "forest_hills",
"category": "forest",
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Wooded Hills",
"color": 2250012,
"rainfall": 0.800000011920929,
"temperature": 0.699999988079071
},
{
"id": 19,
"name": "taiga_hills",
"category": "taiga",
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Taiga Hills",
"color": 1456435,
"rainfall": 0.800000011920929,
"temperature": 0.25
},
{
"id": 20,
"name": "extreme_hills_edge",
"category": "extreme_hills",
"precipitation": "rain",
"depth": 0.8,
"dimension": "overworld",
"displayName": "Mountain Edge",
"color": 7501978,
"rainfall": 0.30000001192092896,
"temperature": 0.20000000298023224
},
{
"id": 21,
"name": "jungle",
"category": "jungle",
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Jungle",
"color": 5470985,
"rainfall": 0.8999999761581421,
"temperature": 0.949999988079071,
"child": 149
},
{
"id": 22,
"name": "jungle_hills",
"category": "jungle",
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Jungle Hills",
"color": 2900485,
"rainfall": 0.8999999761581421,
"temperature": 0.949999988079071
},
{
"id": 23,
"name": "jungle_edge",
"category": "jungle",
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Jungle Edge",
"color": 6458135,
"rainfall": 0.800000011920929,
"temperature": 0.949999988079071,
"child": 151
},
{
"id": 24,
"name": "deep_ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1.8,
"dimension": "overworld",
"displayName": "Deep Ocean",
"color": 48,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 25,
"name": "stone_beach",
"category": "none",
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Stone Shore",
"color": 10658436,
"rainfall": 0.30000001192092896,
"temperature": 0.20000000298023224
},
{
"id": 26,
"name": "cold_beach",
"category": "beach",
"precipitation": "snow",
"depth": 0,
"dimension": "overworld",
"displayName": "Snowy Beach",
"color": 16445632,
"rainfall": 0.30000001192092896,
"temperature": 0.05000000074505806
},
{
"id": 27,
"name": "birch_forest",
"category": "forest",
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Birch Forest",
"color": 3175492,
"rainfall": 0.6000000238418579,
"temperature": 0.6000000238418579,
"child": 155
},
{
"id": 28,
"name": "birch_forest_hills",
"category": "forest",
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Birch Forest Hills",
"color": 2055986,
"rainfall": 0.6000000238418579,
"temperature": 0.6000000238418579,
"child": 156
},
{
"id": 29,
"name": "roofed_forest",
"category": "forest",
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Dark Forest",
"color": 4215066,
"rainfall": 0.800000011920929,
"temperature": 0.699999988079071,
"child": 157
},
{
"id": 30,
"name": "cold_taiga",
"category": "taiga",
"precipitation": "snow",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Snowy Taiga",
"color": 3233098,
"rainfall": 0.4000000059604645,
"temperature": -0.5,
"child": 158
},
{
"id": 31,
"name": "cold_taiga_hills",
"category": "taiga",
"precipitation": "snow",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Snowy Taiga Hills",
"color": 2375478,
"rainfall": 0.4000000059604645,
"temperature": -0.5
},
{
"id": 32,
"name": "mega_taiga",
"category": "taiga",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Giant Tree Taiga",
"color": 5858897,
"rainfall": 0.800000011920929,
"temperature": 0.30000001192092896,
"child": 160
},
{
"id": 33,
"name": "mega_taiga_hills",
"category": "taiga",
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Giant Tree Taiga Hills",
"color": 4542270,
"rainfall": 0.800000011920929,
"temperature": 0.30000001192092896,
"child": 161
},
{
"id": 34,
"name": "extreme_hills_plus_trees",
"category": "extreme_hills",
"precipitation": "rain",
"depth": 1,
"dimension": "overworld",
"displayName": "Wooded Mountains",
"color": 5271632,
"rainfall": 0.30000001192092896,
"temperature": 0.20000000298023224,
"child": 162
},
{
"id": 35,
"name": "savanna",
"category": "savanna",
"precipitation": "none",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Savanna",
"color": 12431967,
"rainfall": 0,
"temperature": 1.2000000476837158,
"child": 163
},
{
"id": 36,
"name": "savanna_plateau",
"category": "savanna",
"precipitation": "none",
"depth": 1.5,
"dimension": "overworld",
"displayName": "Savanna Plateau",
"color": 10984804,
"rainfall": 0,
"temperature": 1,
"child": 164
},
{
"id": 37,
"name": "mesa",
"category": "mesa",
"precipitation": "none",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Badlands",
"color": 14238997,
"rainfall": 0,
"temperature": 2,
"child": 165
},
{
"id": 38,
"name": "mesa_plateau_stone",
"category": "mesa",
"precipitation": "none",
"depth": 1.5,
"dimension": "overworld",
"displayName": "Wooded Badlands Plateau",
"color": 11573093,
"rainfall": 0,
"temperature": 2,
"child": 166
},
{
"id": 39,
"name": "mesa_plateau",
"category": "mesa",
"precipitation": "none",
"depth": 1.5,
"dimension": "overworld",
"displayName": "Badlands Plateau",
"color": 13274213,
"rainfall": 0,
"temperature": 2,
"child": 167
},
{
"id": 40,
"name": "warm_ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1,
"dimension": "overworld",
"displayName": "Warm Ocean",
"color": 172,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 41,
"name": "deep_warm_ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1.8,
"dimension": "overworld",
"displayName": "Deep Warm Ocean",
"color": 80,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 42,
"name": "lukewarm_ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1,
"dimension": "overworld",
"displayName": "Lukewarm Ocean",
"color": 144,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 43,
"name": "deep_lukewarm_ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1.8,
"dimension": "overworld",
"displayName": "Deep Lukewarm Ocean",
"color": 64,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 44,
"name": "cold_ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1,
"dimension": "overworld",
"displayName": "Cold Ocean",
"color": 2105456,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 45,
"name": "deep_cold_ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1.8,
"dimension": "overworld",
"displayName": "Deep Cold Ocean",
"color": 2105400,
"rainfall": 0.5,
"temperature": 0.5
},
{
"id": 46,
"name": "frozen_ocean",
"category": "ocean",
"precipitation": "snow",
"depth": -1,
"dimension": "overworld",
"displayName": "Frozen Ocean",
"color": 7368918,
"rainfall": 0.5,
"temperature": 0
},
{
"id": 47,
"name": "deep_frozen_ocean",
"category": "ocean",
"precipitation": "rain",
"depth": -1.8,
"dimension": "overworld",
"displayName": "Deep Frozen Ocean",
"color": 4210832,
"rainfall": 0.5,
"temperature": 0
},
{
"id": 48,
"name": "bamboo_jungle",
"category": "jungle",
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Bamboo Jungle",
"color": 7769620,
"rainfall": 0.8999999761581421,
"temperature": 0.949999988079071
},
{
"id": 49,
"name": "bamboo_jungle_hills",
"category": "jungle",
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Bamboo Jungle Hills",
"color": 3884810,
"rainfall": 0.8999999761581421,
"temperature": 0.949999988079071
},
{
"id": 129,
"name": "sunflower_plains",
"category": "plains",
"precipitation": "rain",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Sunflower Plains",
"color": 11918216,
"rainfall": 0.4000000059604645,
"temperature": 0.800000011920929,
"parent": "plains"
},
{
"id": 130,
"name": "desert_mutated",
"category": "desert",
"precipitation": "none",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Desert Lakes",
"color": 16759872,
"rainfall": 0,
"temperature": 2,
"parent": "desert"
},
{
"id": 131,
"name": "extreme_hills_mutated",
"category": "extreme_hills",
"precipitation": "rain",
"depth": 1,
"dimension": "overworld",
"displayName": "Gravelly Mountains",
"color": 8947848,
"rainfall": 0.30000001192092896,
"temperature": 0.20000000298023224,
"parent": "mountains"
},
{
"id": 132,
"name": "flower_forest",
"category": "forest",
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Flower Forest",
"color": 2985545,
"rainfall": 0.800000011920929,
"temperature": 0.699999988079071,
"parent": "forest"
},
{
"id": 133,
"name": "taiga_mutated",
"category": "taiga",
"precipitation": "rain",
"depth": 0.3,
"dimension": "overworld",
"displayName": "Taiga Mountains",
"color": 3378817,
"rainfall": 0.800000011920929,
"temperature": 0.25,
"parent": "taiga"
},
{
"id": 134,
"name": "swampland_mutated",
"category": "swamp",
"precipitation": "rain",
"depth": -0.1,
"dimension": "overworld",
"displayName": "Swamp Hills",
"color": 3145690,
"rainfall": 0.5,
"temperature": 0.800000011920929,
"parent": "swamp"
},
{
"id": 140,
"name": "ice_plains_spikes",
"category": "icy",
"precipitation": "snow",
"depth": 0.425,
"dimension": "overworld",
"displayName": "Ice Spikes",
"color": 11853020,
"rainfall": 1,
"temperature": 0,
"parent": "snowy_tundra"
},
{
"id": 149,
"name": "jungle_mutated",
"category": "jungle",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Modified Jungle",
"color": 8102705,
"rainfall": 0.8999999761581421,
"temperature": 0.949999988079071,
"parent": "jungle"
},
{
"id": 151,
"name": "jungle_edge_mutated",
"category": "jungle",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Modified Jungle Edge",
"color": 9089855,
"rainfall": 0.800000011920929,
"temperature": 0.949999988079071,
"parent": "jungle_edge"
},
{
"id": 155,
"name": "birch_forest_mutated",
"category": "forest",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Tall Birch Forest",
"color": 5807212,
"rainfall": 0.800000011920929,
"temperature": 0.699999988079071,
"parent": "birch_forest"
},
{
"id": 156,
"name": "birch_forest_hills_mutated",
"category": "forest",
"precipitation": "rain",
"depth": 0.55,
"dimension": "overworld",
"displayName": "Tall Birch Hills",
"color": 4687706,
"rainfall": 0.800000011920929,
"temperature": 0.699999988079071,
"parent": "birch_forest_hills"
},
{
"id": 157,
"name": "roofed_forest_mutated",
"category": "forest",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Dark Forest Hills",
"color": 6846786,
"rainfall": 0.800000011920929,
"temperature": 0.699999988079071,
"parent": "dark_forest"
},
{
"id": 158,
"name": "cold_taiga_mutated",
"category": "taiga",
"precipitation": "snow",
"depth": 0.3,
"dimension": "overworld",
"displayName": "Snowy Taiga Mountains",
"color": 5864818,
"rainfall": 0.4000000059604645,
"temperature": -0.5,
"parent": "snowy_taiga"
},
{
"id": 160,
"name": "redwood_taiga_mutated",
"category": "taiga",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Giant Spruce Taiga",
"color": 8490617,
"rainfall": 0.800000011920929,
"temperature": 0.25,
"parent": "giant_tree_taiga"
},
{
"id": 161,
"name": "redwood_taiga_hills_mutated",
"category": "taiga",
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Giant Spruce Taiga Hills",
"color": 7173990,
"rainfall": 0.800000011920929,
"temperature": 0.30000001192092896,
"parent": "giant_tree_taiga_hills"
},
{
"id": 162,
"name": "extreme_hills_plus_trees_mutated",
"category": "extreme_hills",
"precipitation": "rain",
"depth": 1,
"dimension": "overworld",
"displayName": "Gravelly Mountains+",
"color": 7903352,
"rainfall": 0.30000001192092896,
"temperature": 0.20000000298023224,
"parent": "wooded_mountains"
},
{
"id": 163,
"name": "savanna_mutated",
"category": "savanna",
"precipitation": "none",
"depth": 0.3625,
"dimension": "overworld",
"displayName": "Shattered Savanna",
"color": 15063687,
"rainfall": 0.5,
"temperature": 1.100000023841858,
"parent": "savanna"
},
{
"id": 164,
"name": "savanna_plateau_mutated",
"category": "savanna",
"precipitation": "none",
"depth": 1.05,
"dimension": "overworld",
"displayName": "Shattered Savanna Plateau",
"color": 13616524,
"rainfall": 0.5,
"temperature": 1,
"parent": "savanna_plateau"
},
{
"id": 165,
"name": "mesa_bryce",
"category": "mesa",
"precipitation": "none",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Eroded Badlands",
"color": 16739645,
"rainfall": 0,
"temperature": 2,
"parent": "badlands"
},
{
"id": 166,
"name": "mesa_plateau_stone_mutated",
"category": "mesa",
"precipitation": "none",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Modified Wooded Badlands Plateau",
"color": 14204813,
"rainfall": 0,
"temperature": 2,
"parent": "wooded_badlands_plateau"
},
{
"id": 167,
"name": "mesa_plateau_mutated",
"category": "mesa",
"precipitation": "none",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Modified Badlands Plateau",
"color": 15905933,
"rainfall": 0,
"temperature": 2,
"parent": "badlands_plateau"
},
{
"id": 178,
"name": "soulsand_valley",
"category": "nether",
"precipitation": "none",
"depth": 0.1,
"dimension": "nether",
"displayName": "Soul Sand Valley",
"color": 6174768,
"rainfall": 0,
"temperature": 2,
"climates": [
{
"temperature": 0,
"humidity": -0.5,
"altitude": 0,
"weirdness": 0,
"offset": 0
}
]
},
{
"id": 179,
"name": "crimson_forest",
"category": "nether",
"precipitation": "none",
"depth": 0.1,
"dimension": "nether",
"displayName": "Crimson Forest",
"color": 14485512,
"rainfall": 0,
"temperature": 2,
"climates": [
{
"temperature": 0.4,
"humidity": 0,
"altitude": 0,
"weirdness": 0,
"offset": 0
}
]
},
{
"id": 180,
"name": "warped_forest",
"category": "nether",
"precipitation": "none",
"depth": 0.1,
"dimension": "nether",
"displayName": "Warped Forest",
"color": 4821115,
"rainfall": 0,
"temperature": 2,
"climates": [
{
"temperature": 0,
"humidity": 0.5,
"altitude": 0,
"weirdness": 0,
"offset": 0.375
}
]
},
{
"id": 181,
"name": "basalt_deltas",
"category": "nether",
"precipitation": "none",
"depth": 0.1,
"dimension": "nether",
"displayName": "Basalt Deltas",
"color": 4208182,
"rainfall": 0,
"temperature": 2,
"climates": [
{
"temperature": -0.5,
"humidity": 0,
"altitude": 0,
"weirdness": 0,
"offset": 0.175
}
]
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,82 @@
[
{
"id": 0,
"sound": "note.harp",
"name": "harp"
},
{
"id": 1,
"sound": "note.bd",
"name": "basedrum"
},
{
"id": 2,
"sound": "note.snare",
"name": "snare"
},
{
"id": 3,
"sound": "note.hat",
"name": "hat"
},
{
"id": 4,
"sound": "note.bass",
"name": "bass"
},
{
"id": 5,
"sound": "note.flute",
"name": "flute"
},
{
"id": 6,
"sound": "note.bell",
"name": "bell"
},
{
"id": 7,
"sound": "note.guitar",
"name": "guitar"
},
{
"id": 8,
"sound": "note.chime",
"name": "chime"
},
{
"id": 9,
"sound": "note.xylophone",
"name": "xylophone"
},
{
"id": 10,
"sound": "note.iron_xylophone",
"name": "iron_xylophone"
},
{
"id": 11,
"sound": "note.cow_bell",
"name": "cow_bell"
},
{
"id": 12,
"sound": "note.didgeridoo",
"name": "didgeridoo"
},
{
"id": 13,
"sound": "note.bit",
"name": "bit"
},
{
"id": 14,
"sound": "note.banjo",
"name": "banjo"
},
{
"id": 15,
"sound": "note.pling",
"name": "pling"
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,6 @@
{
"version": 440,
"minecraftVersion": "1.17.0",
"majorVersion": "1.17",
"releaseType": "release"
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,6 @@
{
"version": 448,
"minecraftVersion": "1.17.10",
"majorVersion": "1.17",
"releaseType": "release"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,6 @@
{
"version": 465,
"minecraftVersion": "1.17.30",
"majorVersion": "1.17",
"releaseType": "release"
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More