10 Commits
Author SHA1 Message Date
Admin Else acb5fe8d90 port messenger to more versions 2026-08-04 19:28:43 +02:00
Admin Else d9bca7e517 strom: fix generator for pointer-receiver Encode, use nbt.Tag for named NBT
- Delete raw_capture subcommand (use tcpflow + raw-to-tmcpr instead)
- Fix DefaultEncoder to use temp variable for non-addressable value encoding,
  eliminating manual post-generation patches
- Map nbt and optionalNbt types to nbt.Tag (named NBT) instead of nbt.Anon
  (anonymous NBT) — 1.8 uses named NBT with root tag names
2026-08-04 16:40:47 +02:00
Admin Else b162d0d401 strom: add 1.8 protocol support, raw-to-tmcpr, disable raw-capture
- Add minecraft-data for version 1.8 (protocol 47)
- Generate proto_generated/v1_8 from 1.8 protocol.json
- Fix generator compatibility: missing CompareTo natives (i16, i32, u16, u32,
  i64, string, nbt, optionalNbt), parameterized type placeholder handling ()
- Add raw-to-tmcpr subcommand: converts raw tcpflow captures to TMCPR
  with zlib decompression and state-aware compression threshold tracking
- Extend print-replay: 1.8 state transitions (success->Play, login_start->Play,
  handshake/status initial states, set_protocol NextState detection)
- Comment out broken raw-capture subcommand (use tcpflow instead)
- Fix nbt.Anon Encode/Decode on value receivers
2026-08-04 16:10:15 +02:00
Admin Else 3273244c42 format 2026-08-02 20:21:15 +02:00
adman f579645b88 client: add StatusRawWithVersion for version-aware status pings 2026-07-31 19:34:16 +02:00
adman 33754d7c71 server: guard against nil KnownPacks in ServeConfig 2026-07-31 15:49:05 +02:00
adman 8bd54a04f2 client: add ConnectWithConn, WithConn, WithDialer, LoginRawAddr
- ConnectWithConn wraps an existing net.Conn into a versioned proto.Conn
- DialFunc is a context-aware dial function type
- WithConn uses a pre-established net.Conn (version required, no DNS)
- WithDialer replaces net.Dialer with a custom dial function
- LoginRawAddr accepts an explicit handshake host address
- Login() wired to support all three connection modes
2026-07-31 14:30:25 +02:00
adman 492dae8e83 Add WithIgnoreConfig option to skip CONFIGURATION auto-completion 2026-07-31 12:10:32 +02:00
adman d9543de951 add check for bad servers 2026-07-30 02:40:43 +02:00
adman f1f5374a5c more context 2026-07-30 02:36:37 +02:00
41 changed files with 61182 additions and 3148 deletions
+4 -2
View File
@@ -19,7 +19,8 @@ import (
"github.com/admin-else/strom/cmd/strom/packet_inspector" "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/print_nbt"
"github.com/admin-else/strom/cmd/strom/print_replay" "github.com/admin-else/strom/cmd/strom/print_replay"
"github.com/admin-else/strom/cmd/strom/raw_capture" // "github.com/admin-else/strom/cmd/strom/raw_capture" // removed: use tcpflow instead for raw TCP capture
"github.com/admin-else/strom/cmd/strom/raw_to_tmcpr"
"github.com/admin-else/strom/cmd/strom/registry_data_hunter" "github.com/admin-else/strom/cmd/strom/registry_data_hunter"
"github.com/admin-else/strom/cmd/strom/serve_tmcpr" "github.com/admin-else/strom/cmd/strom/serve_tmcpr"
"github.com/admin-else/strom/cmd/strom/serve_world" "github.com/admin-else/strom/cmd/strom/serve_world"
@@ -61,7 +62,8 @@ var subcommands = map[string]func(args []string) error{
"extract-mca": extract_mca.Run, "extract-mca": extract_mca.Run,
"data": data.Run, "data": data.Run,
"print-replay": print_replay.Run, "print-replay": print_replay.Run,
"raw-capture": raw_capture.Run, // "raw-capture": raw_capture.Run, // removed: use tcpflow instead for raw TCP capture, then raw-to-tmcpr
"raw-to-tmcpr": raw_to_tmcpr.Run,
"registry-data-hunter": registry_data_hunter.Run, "registry-data-hunter": registry_data_hunter.Run,
"serve-world": serve_world.Run, "serve-world": serve_world.Run,
"mca-to-tmcpr": mca_to_tmcpr.Run, "mca-to-tmcpr": mca_to_tmcpr.Run,
+47 -17
View File
@@ -16,6 +16,7 @@ import (
"github.com/admin-else/strom/mc/event" "github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/proto" "github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_generated/v1_21_11" "github.com/admin-else/strom/mc/proto_generated/v1_21_11"
"github.com/admin-else/strom/mc/proto_generated/v1_8"
) )
func stror(a, b string) string { func stror(a, b string) string {
@@ -29,12 +30,14 @@ var (
cmd = flag.NewFlagSet("status", flag.ContinueOnError) cmd = flag.NewFlagSet("status", flag.ContinueOnError)
connectToFlag = cmd.String("addr", "", "server address to connect to") connectToFlag = cmd.String("addr", "", "server address to connect to")
accFlag = cmd.String("acc", stror(os.Getenv("STROM_ACC"), "strom_messenger"), "the name to use if longer will be treated as ygg token") accFlag = cmd.String("acc", stror(os.Getenv("STROM_ACC"), "strom_messenger"), "the name to use if longer will be treated as ygg token")
versionFlag = cmd.String("version", "26.2", "minecraft version to use (e.g. 1.8, 1.21.11)")
) )
type Messenger struct { type Messenger struct {
*proto.Conn *proto.Conn
chat *chat.Module chat *chat.Module
world *world.Module world *world.Module
legacy bool
} }
func (m *Messenger) OnChat(e *v1_21_11.PlayToClientPacketPlayerChat) (err error) { func (m *Messenger) OnChat(e *v1_21_11.PlayToClientPacketPlayerChat) (err error) {
@@ -52,15 +55,27 @@ func (m *Messenger) OnChatSystem(e *v1_21_11.PlayToClientPacketSystemChat) (err
return return
} }
func (m *Messenger) OnChatLegacy(e *v1_8.PlayToClientPacketChat) (err error) {
m.Log.Info("chat", "m", e.Message)
return
}
func (m *Messenger) OnStdin(e event.Stdin) (err error) { func (m *Messenger) OnStdin(e event.Stdin) (err error) {
input := strings.TrimSpace(e.Val) input := strings.TrimSpace(e.Val)
if strings.HasPrefix(input, ">say ") { if strings.HasPrefix(input, ">say ") {
return m.chat.SendMessage(strings.TrimSpace(strings.TrimPrefix(input, ">say"))) return m.sendChat(strings.TrimSpace(strings.TrimPrefix(input, ">say")))
} }
if strings.HasPrefix(input, ">getblock ") { if !m.legacy && strings.HasPrefix(input, ">getblock ") {
return m.handleGetBlock(strings.TrimSpace(strings.TrimPrefix(input, ">getblock"))) return m.handleGetBlock(strings.TrimSpace(strings.TrimPrefix(input, ">getblock")))
} }
return m.chat.SendMessage(input) return m.sendChat(input)
}
func (m *Messenger) sendChat(message string) (err error) {
if m.legacy {
return m.Send(&v1_8.PlayToServerPacketChat{Message: message})
}
return m.chat.SendMessage(message)
} }
func (m *Messenger) handleGetBlock(args string) (err error) { func (m *Messenger) handleGetBlock(args string) (err error) {
@@ -105,30 +120,45 @@ func Run(args []string) (err error) {
if err != nil { if err != nil {
return return
} }
c, err := client.Login(*connectToFlag, acc) c, err := client.Login(*connectToFlag, acc, client.WithVersion(*versionFlag))
if err != nil { if err != nil {
return return
} }
defer c.Close() defer c.Close()
chatMod, err := chat.Start(c, acc) isLegacy := c.ProtocolVersion < 764
if err != nil {
return var chatMod *chat.Module
if !isLegacy {
chatMod, err = chat.Start(c, acc)
if err != nil {
return
}
} }
// Shareable world storage. Other bots can subscribe to the same World.
w := world.NewWorld(c.Version, -64, 384)
m := &Messenger{ m := &Messenger{
Conn: c, Conn: c,
chat: chatMod, chat: chatMod,
world: world.Start(c, w), legacy: isLegacy,
}
if !isLegacy {
w := world.NewWorld(c.Version, -64, 384)
m.world = world.Start(c, w)
} }
event.StartListingStdin(m.Loop) event.StartListingStdin(m.Loop)
m.Register(m.OnStdin) m.Register(m.OnStdin)
m.RegisterUntilLatest(m.OnChat, m.OnChatUnsigned, m.OnChatSystem)
keepalive.Start(m.Conn) if isLegacy {
m.Register(m.OnChatLegacy)
} else {
m.RegisterUntilLatest(m.OnChat, m.OnChatUnsigned, m.OnChatSystem)
}
if !isLegacy {
keepalive.Start(m.Conn)
}
return m.StartConn() return m.StartConn()
} }
+36 -17
View File
@@ -25,7 +25,7 @@ var (
FileFlag = cmd.String("file", "", "The tmcpr file to print") FileFlag = cmd.String("file", "", "The tmcpr file to print")
ProtocolVersion = cmd.Int("protocol", -1, "The protocol version") ProtocolVersion = cmd.Int("protocol", -1, "The protocol version")
CreateTestsPath = cmd.String("create-tests", "", "") CreateTestsPath = cmd.String("create-tests", "", "")
StateFlag = cmd.String("state", "login", "Initial state: login, config, play") StateFlag = cmd.String("state", "login", "Initial state: handshake, login, config, status, play")
DirectionFlag = cmd.String("direction", "clientbound", "Packet direction: clientbound (ToClient) or serverbound (ToServer)") DirectionFlag = cmd.String("direction", "clientbound", "Packet direction: clientbound (ToClient) or serverbound (ToServer)")
) )
@@ -106,22 +106,6 @@ func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, i proto_base
packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: packetData, Info: i} packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: packetData, Info: i}
return return
} }
switch direction {
case proto_base.ToClient:
switch i.Name {
case "success":
state = proto_base.Configuration
case "finish_configuration":
state = proto_base.Play
}
case proto_base.ToServer:
switch i.Name {
case "login_acknowledged":
state = proto_base.Configuration
case "finish_configuration":
state = proto_base.Play
}
}
packet = reflect.New(reflect.TypeOf(i.Type).Elem()).Interface().(proto_base.EncodeDecodeAble) packet = reflect.New(reflect.TypeOf(i.Type).Elem()).Interface().(proto_base.EncodeDecodeAble)
err = packet.Decode(b) err = packet.Decode(b)
if err != nil { if err != nil {
@@ -132,9 +116,40 @@ func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, i proto_base
packet = &proto.UnCodablePacket{Err: proto.PacketNotFullyDecodedErr, Data: packetData[indexafterid:], Info: i} packet = &proto.UnCodablePacket{Err: proto.PacketNotFullyDecodedErr, Data: packetData[indexafterid:], Info: i}
return return
} }
trackState(packet, i)
return return
} }
func trackState(packet proto_base.EncodeDecodeAble, i proto_base.PacketInfo) {
switch i.Name {
case "set_protocol":
v := reflect.ValueOf(packet).Elem()
nsField := v.FieldByName("NextState")
if nsField.IsValid() && nsField.Kind() == reflect.Int32 {
switch nsField.Int() {
case 1:
state = proto_base.Status
case 2:
state = proto_base.Login
}
}
case "login_start":
if int32(*ProtocolVersion) <= 47 {
state = proto_base.Play
}
case "success":
if int32(*ProtocolVersion) >= 764 {
state = proto_base.Configuration
} else {
state = proto_base.Play
}
case "finish_configuration":
state = proto_base.Play
case "login_acknowledged":
state = proto_base.Configuration
}
}
func Run(args []string) (err error) { func Run(args []string) (err error) {
err = cmd.Parse(args) err = cmd.Parse(args)
if err != nil { if err != nil {
@@ -147,12 +162,16 @@ func Run(args []string) (err error) {
return fmt.Errorf("no file specified") return fmt.Errorf("no file specified")
} }
switch *StateFlag { switch *StateFlag {
case "handshake":
state = proto_base.Handshaking
case "login": case "login":
state = proto_base.Login state = proto_base.Login
case "config": case "config":
state = proto_base.Configuration state = proto_base.Configuration
case "play": case "play":
state = proto_base.Play state = proto_base.Play
case "status":
state = proto_base.Status
default: default:
return fmt.Errorf("unknown state: %s", *StateFlag) return fmt.Errorf("unknown state: %s", *StateFlag)
} }
-253
View File
@@ -1,253 +0,0 @@
package raw_capture
import (
"flag"
"fmt"
"log/slog"
"os"
"time"
"github.com/admin-else/strom/mc/api"
"github.com/admin-else/strom/mc/client"
"github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto/replay"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
"github.com/admin-else/strom/mc/server"
"github.com/admin-else/strom/mc/text"
)
var Cmd = flag.NewFlagSet("raw-capture", flag.ContinueOnError)
var OfflineNameFlag = Cmd.String("name", "raw_capture", "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")
var ClientboundFile = Cmd.String("clientbound-file", "clientbound.tmcpr", "file to write clientbound packets to")
var ServerboundFile = Cmd.String("serverbound-file", "serverbound.tmcpr", "file to write serverbound packets to")
var Version = Cmd.String("version", "", "client protocol version (e.g. 26.1); if empty, detect from server status")
var StatusResponse = server.StatusResponse{
Version: server.StatusResponseVersion{
Name: text.Pretty("STROM raw-capture"),
Protocol: 0,
},
Players: server.StatusResponsePlayers{},
Description: text.Pretty("STROM raw-capture proxy"),
Favicon: "",
EnforcesSecureChat: false,
}
type Proxy struct {
Client, Servee *proto.Conn
clientboundFile *os.File
serverboundFile *os.File
clientboundWriter *replay.Writer
serverboundWriter *replay.Writer
startTime time.Time
}
func (p *Proxy) Start() (err error) {
p.startTime = time.Now()
p.Client.RegisterCritical(p.OnAnything)
p.Servee.RegisterCritical(p.OnAnything)
p.Client.RegisterCriticalUntilLatest(p.OnCompress)
p.Servee.RegisterCriticalUntilLatest(p.OnHandshake)
p.Servee.RegisterCriticalUntilLatest(p.OnLoginAcknowledged)
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) timestamp() uint32 {
return uint32(time.Since(p.startTime).Milliseconds())
}
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
}
if packetInfo.Name == "encryption_begin" {
return fmt.Errorf("encryption requested by remote server; raw capture cannot continue")
}
switch packetInfo.Direction {
case proto_base.ToServer:
err = p.serverboundWriter.WritePacket(packet, p.timestamp())
if err != nil {
return
}
err = p.Client.Send(packet)
case proto_base.ToClient:
err = p.clientboundWriter.WritePacket(packet, p.timestamp())
if err != nil {
return
}
err = p.Servee.Send(packet)
}
return
}
func (p *Proxy) OnCompress(packet *v1_21_11.LoginToClientPacketCompress) (err error) {
err = p.clientboundWriter.WritePacket(packet, p.timestamp())
if err != nil {
return
}
err = p.Servee.Send(packet)
if err != nil {
return
}
p.Servee.SetCompressionThreshold(packet.Threshold)
p.Client.SetCompressionThreshold(packet.Threshold)
return event.DontForwardErr
}
func (p *Proxy) OnHandshake(packet *v1_21_11.HandshakingToServerPacketSetProtocol) (err error) {
state := proto_base.State(packet.NextState)
p.Servee.SetState(state)
return event.DontForwardErr
}
func (p *Proxy) OnLoginAcknowledged(packet *v1_21_11.LoginToServerPacketLoginAcknowledged) (err error) {
err = p.serverboundWriter.WritePacket(packet, p.timestamp())
if err != nil {
return
}
err = p.Client.Send(packet)
if err != nil {
return
}
p.Servee.SetState(proto_base.Configuration)
p.Client.SetState(proto_base.Configuration)
return event.DontForwardErr
}
func (p *Proxy) OnFinishConfiguration(packet *v1_21_11.ConfigurationToServerPacketFinishConfiguration) (err error) {
err = p.serverboundWriter.WritePacket(packet, p.timestamp())
if err != nil {
return
}
err = p.Client.Send(packet)
if err != nil {
return
}
p.Servee.SetState(proto_base.Play)
p.Client.SetState(proto_base.Play)
return event.DontForwardErr
}
func Run(args []string) (err error) {
err = Cmd.Parse(args)
if err != nil {
return
}
clientboundFile, err := os.Create(*ClientboundFile)
if err != nil {
return fmt.Errorf("create clientbound file: %w", err)
}
defer clientboundFile.Close()
serverboundFile, err := os.Create(*ServerboundFile)
if err != nil {
return fmt.Errorf("create serverbound file: %w", err)
}
defer serverboundFile.Close()
resolvedAddr, _, _, err := client.DoDNSChecked(nil, *TargetAddr, nil)
if err != nil {
return
}
var targetVersion string
var targetProtocol int32
if *Version == "" {
status, err := client.Status(nil, resolvedAddr)
if err != nil {
return fmt.Errorf("failed to detect server version: %w", err)
}
targetProtocol = status.Version.Protocol
versionInfo, err := data.LookUpVersionByProtocolVersion(targetProtocol)
if err != nil {
return fmt.Errorf("unknown protocol version %d: %w", targetProtocol, err)
}
targetVersion = versionInfo.MinecraftVersion
} else {
versionInfo, err := data.LookUpVersionByName(*Version)
if err != nil {
return fmt.Errorf("unknown version %s: %w", *Version, err)
}
targetVersion = versionInfo.MinecraftVersion
targetProtocol = versionInfo.Version
}
StatusResponse.Version.Name = text.Pretty("STROM raw-capture " + targetVersion)
StatusResponse.Version.Protocol = targetProtocol
StatusResponse.Description = text.Pretty(fmt.Sprintf("Raw capture proxy forwards to %s (%s)", resolvedAddr, targetVersion))
slog.Info("Starting raw capture", "listen", *ListenAddr, "target", resolvedAddr, "version", targetVersion, "protocol", targetProtocol, "clientbound", *ClientboundFile, "serverbound", *ServerboundFile)
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
proxy := &Proxy{
Servee: serveeConn,
clientboundFile: clientboundFile,
serverboundFile: serverboundFile,
clientboundWriter: replay.NewWriter(clientboundFile),
serverboundWriter: replay.NewWriter(serverboundFile),
}
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), server.WithoutOnlineMode(), server.WithoutEncryption())
if err != nil {
return
}
c, err := client.Connect(nil, resolvedAddr, targetVersion)
if err != nil {
return
}
defer c.Close()
err = client.LoginRaw(c, acc)
if err != nil {
return
}
proxy.Client = c
proxy.Client.AlwaysDecode = true
proxy.Servee.AlwaysDecode = true
return proxy.Start()
})
return
}
+243
View File
@@ -0,0 +1,243 @@
package raw_to_tmcpr
import (
"bytes"
"compress/zlib"
"errors"
"flag"
"fmt"
"io"
"os"
"time"
"github.com/admin-else/strom/mc/proto/replay"
"github.com/admin-else/strom/mc/proto_base"
)
var Cmd = flag.NewFlagSet("raw-to-tmcpr", flag.ContinueOnError)
var ClientboundIn = Cmd.String("clientbound", "", "raw tcpflow file for clientbound (server->client) direction")
var ServerboundIn = Cmd.String("serverbound", "", "raw tcpflow file for serverbound (client->server) direction")
var ClientboundOut = Cmd.String("clientbound-out", "clientbound.tmcpr", "output TMCPR file for clientbound")
var ServerboundOut = Cmd.String("serverbound-out", "serverbound.tmcpr", "output TMCPR file for serverbound")
var ClientboundState = Cmd.String("clientbound-state", "login", "initial state for clientbound: handshake, login, status, play")
var ServerboundState = Cmd.String("serverbound-state", "handshake", "initial state for serverbound: handshake, login, status, play")
type captureReader struct {
r io.Reader
out *replay.Writer
state proto_base.State
threshold int32
startTime time.Time
haveStartTime bool
direction proto_base.Direction
pendingThreshold int32
}
func (cr *captureReader) setStartTime() {
if !cr.haveStartTime {
cr.startTime = time.Now()
cr.haveStartTime = true
}
}
func (cr *captureReader) timestamp() uint32 {
return uint32(time.Since(cr.startTime).Milliseconds())
}
func readPacketBytes(r io.Reader, threshold int32) ([]byte, error) {
packetLen, err := proto_base.DecodeVarInt(r)
if err != nil {
return nil, fmt.Errorf("read packet length: %w", err)
}
rawBytes := make([]byte, packetLen)
_, err = io.ReadFull(r, rawBytes)
if err != nil {
return nil, fmt.Errorf("read packet data: %w", err)
}
if threshold >= 0 {
buf := bytes.NewReader(rawBytes)
uncompressedLen, err := proto_base.DecodeVarInt(buf)
if err != nil {
return nil, fmt.Errorf("read uncompressed length: %w", err)
}
if uncompressedLen == 0 {
return io.ReadAll(buf)
} else {
zReader, err := zlib.NewReader(buf)
if err != nil {
return nil, fmt.Errorf("zlib reader: %w", err)
}
defer zReader.Close()
decompressed, err := io.ReadAll(zReader)
if err != nil {
return nil, fmt.Errorf("decompress: %w", err)
}
return decompressed, nil
}
}
return rawBytes, nil
}
func (cr *captureReader) trackState(packetBytes []byte) {
buf := bytes.NewReader(packetBytes)
packetId, err := proto_base.DecodeVarInt(buf)
if err != nil {
return
}
switch cr.state {
case proto_base.Handshaking:
if cr.direction == proto_base.ToServer && packetId == 0 {
_, err = proto_base.DecodeVarInt(buf) // protocol version
if err != nil {
return
}
_, err = proto_base.DecodeString(buf) // server address
if err != nil {
return
}
portBytes := make([]byte, 2)
_, err = io.ReadFull(buf, portBytes)
if err != nil {
return
}
nextState, err := proto_base.DecodeVarInt(buf)
if err != nil {
return
}
if nextState == 2 {
cr.state = proto_base.Login
} else if nextState == 1 {
cr.state = proto_base.Status
}
}
case proto_base.Login:
if cr.direction == proto_base.ToClient {
if packetId == 0x03 {
threshold, err := proto_base.DecodeVarInt(buf)
if err == nil {
cr.threshold = threshold
}
} else if packetId == 0x02 {
cr.state = proto_base.Play
}
} else if cr.direction == proto_base.ToServer && packetId == 0 {
if cr.pendingThreshold >= 0 {
cr.threshold = cr.pendingThreshold
}
}
case proto_base.Status:
case proto_base.Play:
}
}
func (cr *captureReader) process() (count int, err error) {
for {
packetBytes, err := readPacketBytes(cr.r, cr.threshold)
if err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
return count, nil
}
return count, err
}
cr.setStartTime()
cr.trackState(packetBytes)
err = cr.out.WriteRaw(packetBytes, cr.timestamp())
if err != nil {
return count, fmt.Errorf("write TMCPR: %w", err)
}
count++
}
}
func Run(args []string) (err error) {
err = Cmd.Parse(args)
if err != nil {
return
}
if *ClientboundIn == "" && *ServerboundIn == "" {
return fmt.Errorf("at least one of -clientbound or -serverbound must be specified")
}
parseState := func(name string) (proto_base.State, error) {
switch name {
case "handshake":
return proto_base.Handshaking, nil
case "login":
return proto_base.Login, nil
case "play":
return proto_base.Play, nil
case "status":
return proto_base.Status, nil
default:
return 0, fmt.Errorf("unknown state: %s (valid: handshake, login, status, play)", name)
}
}
var compressionThreshold int32 = -1
if *ClientboundIn != "" {
state, err := parseState(*ClientboundState)
if err != nil {
return err
}
compressionThreshold, err = processFile(*ClientboundIn, *ClientboundOut, state, proto_base.ToClient, -1)
if err != nil {
return fmt.Errorf("clientbound: %w", err)
}
}
if *ServerboundIn != "" {
state, err := parseState(*ServerboundState)
if err != nil {
return err
}
_, err = processFile(*ServerboundIn, *ServerboundOut, state, proto_base.ToServer, compressionThreshold)
if err != nil {
return fmt.Errorf("serverbound: %w", err)
}
}
return
}
func processFile(inPath, outPath string, initial proto_base.State, dir proto_base.Direction, initialThreshold int32) (finalThreshold int32, err error) {
in, err := os.Open(inPath)
if err != nil {
return initialThreshold, fmt.Errorf("open input: %w", err)
}
defer in.Close()
out, err := os.Create(outPath)
if err != nil {
return initialThreshold, fmt.Errorf("create output: %w", err)
}
defer out.Close()
cr := &captureReader{
r: in,
out: replay.NewWriter(out),
state: initial,
threshold: -1,
direction: dir,
pendingThreshold: initialThreshold,
}
if dir == proto_base.ToClient {
cr.threshold = initialThreshold
}
count, err := cr.process()
if err != nil {
return cr.threshold, err
}
fmt.Printf("%s: %d packets written to %s (threshold=%d)\n", inPath, count, outPath, cr.threshold)
return cr.threshold, nil
}
+25
View File
@@ -2,6 +2,7 @@ package client
import ( import (
"context" "context"
"errors"
"net" "net"
"github.com/admin-else/strom/mc/data" "github.com/admin-else/strom/mc/data"
@@ -21,6 +22,10 @@ func Connect(ctx context.Context, addr string, version string) (ret *proto.Conn,
if err != nil { if err != nil {
return return
} }
if s.Version.Protocol == 0 {
err = errors.New("server didnt return protocol version")
return
}
protocolVersion = s.Version.Protocol protocolVersion = s.Version.Protocol
} else { } else {
var v data.VersionInfo var v data.VersionInfo
@@ -55,3 +60,23 @@ func ConnectVersionLess(ctx context.Context, addr string) (ret *proto.Conn, err
ret.W = ret.Conn ret.W = ret.Conn
return return
} }
// ConnectWithConn wraps an existing net.Conn into a proto.Conn with the given
// Minecraft version. The version must be non-empty.
func ConnectWithConn(ctx context.Context, conn net.Conn, version string) (ret *proto.Conn, err error) {
if ctx == nil {
ctx = context.Background()
}
ret = proto.NewConnCtx(ctx)
ret.Actor = proto_base.Client
ret.Conn = conn
ret.R = conn
ret.W = conn
v, err := data.LookUpVersionByName(version)
if err != nil {
return
}
ret.ProtocolVersion = v.Version
ret.Version = version
return
}
+127 -14
View File
@@ -7,6 +7,7 @@ import (
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"errors" "errors"
"net"
"github.com/admin-else/strom/mc/api" "github.com/admin-else/strom/mc/api"
"github.com/admin-else/strom/mc/crypto" "github.com/admin-else/strom/mc/crypto"
@@ -14,8 +15,10 @@ import (
"github.com/admin-else/strom/mc/proto" "github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base" "github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8" "github.com/admin-else/strom/mc/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/proto_generated/v1_8"
"github.com/admin-else/strom/mc/proto_generated/v26_2" "github.com/admin-else/strom/mc/proto_generated/v26_2"
"github.com/admin-else/strom/mc/text" "github.com/admin-else/strom/mc/text"
"github.com/google/uuid"
) )
type LoginClient struct { type LoginClient struct {
@@ -111,13 +114,42 @@ func (s *LoginClient) OnDisconnect(packet *v1_21_8.LoginToClientPacketDisconnect
return return
} }
func (s *LoginClient) OnSuccess(success *v1_21_8.LoginToClientPacketSuccess) (err error) { func (s *LoginClient) OnDisconnectV1_8(packet *v1_8.LoginToClientPacketDisconnect) (err error) {
s.GivenAccount = success var reason text.Component
err = s.Send(&v1_21_8.LoginToServerPacketLoginAcknowledged{}) err = json.Unmarshal([]byte(packet.Reason), &reason)
if err != nil { if err != nil {
return return
} }
s.SetState(proto_base.Configuration) err = KickedDuringLoginErr{reason}
return
}
func (s *LoginClient) OnCompressV1_8(packet *v1_8.LoginToClientPacketCompress) (err error) {
s.SetCompressionThreshold(packet.Threshold)
return
}
func (s *LoginClient) OnSuccessV1_8(success *v1_8.LoginToClientPacketSuccess) (err error) {
s.GivenAccount = &v1_21_8.LoginToClientPacketSuccess{
Uuid: uuid.MustParse(success.Uuid),
Username: success.Username,
}
s.SetState(proto_base.Play)
err = event.HandlerDoneErr{}
return
}
func (s *LoginClient) OnSuccess(success *v1_21_8.LoginToClientPacketSuccess) (err error) {
s.GivenAccount = success
if s.ProtocolVersion >= 764 {
err = s.Send(&v1_21_8.LoginToServerPacketLoginAcknowledged{})
if err != nil {
return
}
s.SetState(proto_base.Configuration)
} else {
s.SetState(proto_base.Play)
}
err = event.HandlerDoneErr{} err = event.HandlerDoneErr{}
return return
} }
@@ -135,7 +167,14 @@ func (s *LoginClient) OnStart() (err error) {
} }
// LoginRaw performs the login handshake on an already-established connection without DNS resolution or configuration handling. // LoginRaw performs the login handshake on an already-established connection without DNS resolution or configuration handling.
// The handshake address is derived from the connection's RemoteAddr.
func LoginRaw(c *proto.Conn, account *api.Account) (err error) { func LoginRaw(c *proto.Conn, account *api.Account) (err error) {
return LoginRawAddr(c, account, "")
}
// LoginRawAddr performs the login handshake on an already-established connection using the given address for the handshake packet.
// If hostAddr is empty, the handshake address is derived from the connection's RemoteAddr.
func LoginRawAddr(c *proto.Conn, account *api.Account, hostAddr string) (err error) {
lc := &LoginClient{ lc := &LoginClient{
Conn: c, Conn: c,
Account: account, Account: account,
@@ -147,8 +186,16 @@ func LoginRaw(c *proto.Conn, account *api.Account) (err error) {
lc.RegisterCriticalUntilLatest(lc.OnDisconnect) lc.RegisterCriticalUntilLatest(lc.OnDisconnect)
lc.RegisterCriticalUntil("26.1", lc.OnSuccess) lc.RegisterCriticalUntil("26.1", lc.OnSuccess)
lc.RegisterCriticalUntilLatest(lc.OnSuccess26_2) lc.RegisterCriticalUntilLatest(lc.OnSuccess26_2)
lc.RegisterCritical(lc.OnCompressV1_8)
lc.RegisterCritical(lc.OnDisconnectV1_8)
lc.RegisterCritical(lc.OnSuccessV1_8)
p, err := MakeHandshakePacket(lc.Conn, proto_base.Login) var p proto_base.EncodeDecodeAble
if hostAddr != "" {
p, err = MakeHandshakePacketAddr(lc.Conn, proto_base.Login, hostAddr)
} else {
p, err = MakeHandshakePacket(lc.Conn, proto_base.Login)
}
if err != nil { if err != nil {
return return
} }
@@ -158,7 +205,11 @@ func LoginRaw(c *proto.Conn, account *api.Account) (err error) {
return return
} }
lc.SetState(proto_base.Login) lc.SetState(proto_base.Login)
err = lc.Send(&v1_21_8.LoginToServerPacketLoginStart{Username: lc.Account.Name, PlayerUUID: lc.Account.Uuid}) if lc.Conn.ProtocolVersion < 759 {
err = lc.Send(&v1_8.LoginToServerPacketLoginStart{Username: lc.Account.Name})
} else {
err = lc.Send(&v1_21_8.LoginToServerPacketLoginStart{Username: lc.Account.Name, PlayerUUID: lc.Account.Uuid})
}
err = lc.StartConn() err = lc.StartConn()
if err != nil { if err != nil {
@@ -188,11 +239,43 @@ func WithVersion(version string) func(ls *loginSettings) {
} }
} }
func WithIgnoreConfig() func(ls *loginSettings) {
return func(ls *loginSettings) {
ls.IgnoreConfig = true
}
}
// DialFunc is a context-aware dial function used by Login to establish a TCP connection.
type DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
// WithConn returns a login setting that uses an already-established net.Conn instead of dialling.
// WithVersion must also be set; auto-detection is not possible with a pre-established connection.
func WithConn(conn net.Conn) func(ls *loginSettings) {
return func(ls *loginSettings) {
ls.Conn = conn
}
}
// WithDialer returns a login setting that uses the given dial function instead of net.Dialer.
// Auto version detection via status ping is still supported when no explicit version is set.
func WithDialer(d DialFunc) func(ls *loginSettings) {
return func(ls *loginSettings) {
ls.Dialer = d
}
}
var (
ErrWithConnRequiresVersion = errors.New("WithVersion is required when using WithConn")
ErrWithConnNoStatus = errors.New("auto version detection is not supported with WithConn")
)
type loginSettings struct { type loginSettings struct {
NoDns bool NoDns bool
Ctx context.Context Ctx context.Context
Version string Version string
IgnoreConfig bool IgnoreConfig bool
Conn net.Conn
Dialer DialFunc
} }
// Login resolves the address with SRV DNS, connects, performs the login handshake, and handles the configuration phase, returning an authenticated connection ready for play. // Login resolves the address with SRV DNS, connects, performs the login handshake, and handles the configuration phase, returning an authenticated connection ready for play.
@@ -203,22 +286,52 @@ func Login(connectTo string, account *api.Account, settings ...func(ls *loginSet
setting(ls) setting(ls)
} }
if !ls.NoDns { if ls.Conn != nil {
connectTo, _, _, err = DoDNSChecked(ls.Ctx, connectTo, nil) if ls.Version == "" {
return nil, ErrWithConnRequiresVersion
}
c, err = ConnectWithConn(ls.Ctx, ls.Conn, ls.Version)
if err != nil {
return
}
} else if ls.Dialer != nil {
if ls.Version == "" {
return nil, ErrWithConnRequiresVersion
}
if !ls.NoDns {
connectTo, _, _, err = DoDNSChecked(ls.Ctx, connectTo, nil)
if err != nil {
return
}
}
var conn net.Conn
conn, err = ls.Dialer(ls.Ctx, "tcp", connectTo)
if err != nil {
return
}
c, err = ConnectWithConn(ls.Ctx, conn, ls.Version)
if err != nil {
conn.Close()
return
}
} else {
if !ls.NoDns {
connectTo, _, _, err = DoDNSChecked(ls.Ctx, connectTo, nil)
if err != nil {
return
}
}
c, err = Connect(ls.Ctx, connectTo, ls.Version)
if err != nil { if err != nil {
return return
} }
} }
c, err = Connect(ls.Ctx, connectTo, ls.Version) err = LoginRawAddr(c, account, connectTo)
if err != nil { if err != nil {
return return
} }
err = LoginRaw(c, account) if !ls.IgnoreConfig && c.ProtocolVersion >= 764 {
if err != nil {
return
}
if !ls.IgnoreConfig {
err = IgnoreConfig(c) err = IgnoreConfig(c)
} }
return return
+30 -4
View File
@@ -9,6 +9,7 @@ import (
"github.com/admin-else/strom/mc/proto" "github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base" "github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8" "github.com/admin-else/strom/mc/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/proto_generated/v1_8"
"github.com/admin-else/strom/mc/server" "github.com/admin-else/strom/mc/server"
) )
@@ -36,16 +37,38 @@ func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
return return
} }
func (s *StatusClient) OnStatusV1_8(p *v1_8.StatusToClientPacketServerInfo) (err error) {
s.Status = p.Response
s.PingSendTime = time.Now()
if s.DoPingRoundTripTime {
err = s.Send(&v1_21_8.StatusToServerPacketPing{Time: s.PingSendTime.UnixMilli()})
} else {
err = event.HandlerDoneErr{}
}
return
}
func (s *StatusClient) OnPongV1_8(p *v1_8.StatusToClientPacketPing) (err error) {
s.PingReceiveTime = time.Now()
err = event.HandlerDoneErr{}
return
}
// StatusRaw returns a StatusClient that is not connected to a server. // 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. // It does not resolve SRV records.
func StatusRaw(ctx context.Context, addr string) (s *StatusClient, err error) { func StatusRaw(ctx context.Context, addr string) (s *StatusClient, err error) {
return StatusRawWithVersion(ctx, addr, "1.21.8")
}
// StatusRawWithVersion is like StatusRaw but uses the given Minecraft version for the handshake.
func StatusRawWithVersion(ctx context.Context, addr, version string) (s *StatusClient, err error) {
c, err := ConnectVersionLess(ctx, addr) c, err := ConnectVersionLess(ctx, addr)
if err != nil { if err != nil {
return return
} }
defer c.Close() defer c.Close()
err = c.SetVersion("1.21.8") err = c.SetVersion(version)
if err != nil { if err != nil {
return return
} }
@@ -54,6 +77,8 @@ func StatusRaw(ctx context.Context, addr string) (s *StatusClient, err error) {
} }
s.RegisterCriticalUntilLatest(s.OnStatus) s.RegisterCriticalUntilLatest(s.OnStatus)
s.RegisterCriticalUntilLatest(s.OnPong) s.RegisterCriticalUntilLatest(s.OnPong)
s.RegisterCritical(s.OnStatusV1_8)
s.RegisterCritical(s.OnPongV1_8)
p, err := MakeHandshakePacketAddr(s.Conn, proto_base.Status, addr) p, err := MakeHandshakePacketAddr(s.Conn, proto_base.Status, addr)
if err != nil { if err != nil {
@@ -71,11 +96,12 @@ func StatusRaw(ctx context.Context, addr string) (s *StatusClient, err error) {
} }
// StatusNoDns is like Status but does not resolve SRV records. // StatusNoDns is like Status but does not resolve SRV records.
func StatusNoDns(addr string) (status server.StatusResponse, err error) { func StatusNoDns(ctx context.Context, addr string) (status server.StatusResponse, err error) {
s, err := StatusRaw(context.Background(), addr) s, err := StatusRaw(ctx, addr)
if err != nil { if err != nil {
return return
} }
defer s.Close()
err = json.Unmarshal([]byte(s.Status), &status) err = json.Unmarshal([]byte(s.Status), &status)
return return
} }
@@ -86,5 +112,5 @@ func Status(ctx context.Context, addr string) (status server.StatusResponse, err
if err != nil { if err != nil {
return return
} }
return StatusNoDns(addr) return StatusNoDns(ctx, addr)
} }
@@ -0,0 +1,86 @@
[
{
"name": "maxHealth",
"resource": "generic.maxHealth",
"default": 20,
"min": 1,
"max": 1024
},
{
"name": "followRange",
"resource": "generic.followRange",
"default": 32,
"min": 0,
"max": 2048
},
{
"name": "knockbackResistance",
"resource": "generic.knockbackResistance",
"default": 0,
"min": 0,
"max": 1
},
{
"name": "movementSpeed",
"resource": "generic.movementSpeed",
"default": 0.7,
"min": 0,
"max": 1024
},
{
"name": "attackDamage",
"resource": "generic.attackDamage",
"default": 2,
"min": 0,
"max": 2048
},
{
"name": "horseJumpStrength",
"resource": "horse.jumpStrength",
"default": 0.7,
"min": 0,
"max": 2
},
{
"name": "zombieSpawnReinforcements",
"resource": "zombie.spawnReinforcements",
"default": 0,
"min": 0,
"max": 1
},
{
"name": "attackSpeed",
"resource": "generic.attackSpeed",
"default": 4,
"min": 0,
"max": 1024
},
{
"name": "flyingSpeed",
"resource": "generic.flyingSpeed",
"default": 0.4,
"min": 0,
"max": 1024
},
{
"name": "attackKnockback",
"resource": "generic.attackKnockback",
"default": 0,
"min": 0,
"max": 5
},
{
"name": "armorHealth",
"resource": "generic.armor",
"default": 0,
"min": 0,
"max": 30
},
{
"name": "armorToughness",
"resource": "generic.armorToughness",
"default": 0,
"min": 0,
"max": 20
}
]
+817
View File
@@ -0,0 +1,817 @@
[
{
"id": 0,
"name": "ocean",
"name_legacy": "ocean",
"category": "ocean",
"temperature": 0.5,
"precipitation": "rain",
"depth": -1,
"dimension": "overworld",
"displayName": "Ocean",
"color": 112,
"rainfall": 0.5
},
{
"id": 1,
"name": "plains",
"name_legacy": "plains",
"category": "plains",
"temperature": 0.8,
"precipitation": "rain",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Plains",
"color": 9286496,
"rainfall": 0.4
},
{
"id": 2,
"name": "desert",
"name_legacy": "desert",
"category": "desert",
"temperature": 2,
"precipitation": "none",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Desert",
"color": 16421912,
"rainfall": 0
},
{
"id": 3,
"name": "mountains",
"name_legacy": "mountains",
"category": "extreme_hills",
"temperature": 0.2,
"precipitation": "rain",
"depth": 1,
"dimension": "overworld",
"displayName": "Extreme Hills",
"color": 6316128,
"rainfall": 0.3
},
{
"id": 4,
"name": "forest",
"name_legacy": "forest",
"category": "forest",
"temperature": 0.7,
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Forest",
"color": 353825,
"rainfall": 0.8
},
{
"id": 5,
"name": "taiga",
"name_legacy": "taiga",
"category": "taiga",
"temperature": 0.25,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Taiga",
"color": 747097,
"rainfall": 0.8
},
{
"id": 6,
"name": "swamp",
"name_legacy": "swamp",
"category": "swamp",
"temperature": 0.8,
"precipitation": "rain",
"depth": -0.2,
"dimension": "overworld",
"displayName": "Swampland",
"color": 522674,
"rainfall": 0.9
},
{
"id": 7,
"name": "river",
"name_legacy": "river",
"category": "river",
"temperature": 0.5,
"precipitation": "rain",
"depth": -0.5,
"dimension": "overworld",
"displayName": "River",
"color": 255,
"rainfall": 0.5
},
{
"id": 8,
"name": "nether_wastes",
"name_legacy": "nether_wastes",
"category": "nether",
"temperature": 2,
"precipitation": "none",
"depth": 0.1,
"dimension": "nether",
"displayName": "Hell",
"color": 12532539,
"rainfall": 0,
"climates": [
{
"temperature": 0,
"humidity": 0,
"altitude": 0,
"weirdness": 0,
"offset": 0
}
]
},
{
"id": 9,
"name": "the_end",
"name_legacy": "the_end",
"category": "the_end",
"temperature": 0.5,
"precipitation": "none",
"depth": 0.1,
"dimension": "end",
"displayName": "The End",
"color": 8421631,
"rainfall": 0.5
},
{
"id": 10,
"name": "frozen_ocean",
"name_legacy": "frozen_ocean",
"category": "ocean",
"temperature": 0,
"precipitation": "snow",
"depth": -1,
"dimension": "overworld",
"displayName": "Frozen Ocean",
"color": 7368918,
"rainfall": 0.5
},
{
"id": 11,
"name": "frozen_river",
"name_legacy": "frozen_river",
"category": "river",
"temperature": 0,
"precipitation": "snow",
"depth": -0.5,
"dimension": "overworld",
"displayName": "Frozen River",
"color": 10526975,
"rainfall": 0.5
},
{
"id": 12,
"name": "snowy_tundra",
"name_legacy": "snowy_tundra",
"category": "icy",
"temperature": 0,
"precipitation": "snow",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Ice Plains",
"color": 16777215,
"rainfall": 0.5
},
{
"id": 13,
"name": "snowy_mountains",
"name_legacy": "snowy_mountains",
"category": "icy",
"temperature": 0,
"precipitation": "snow",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Ice Mountains",
"color": 10526880,
"rainfall": 0.5
},
{
"id": 14,
"name": "mushroom_fields",
"name_legacy": "mushroom_fields",
"category": "mushroom",
"temperature": 0.9,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Mushroom Island",
"color": 16711935,
"rainfall": 1
},
{
"id": 15,
"name": "mushroom_field_shore",
"name_legacy": "mushroom_field_shore",
"category": "mushroom",
"temperature": 0.9,
"precipitation": "rain",
"depth": 0,
"dimension": "overworld",
"displayName": "Mushroom Island Shore",
"color": 10486015,
"rainfall": 1
},
{
"id": 16,
"name": "beach",
"name_legacy": "beach",
"category": "beach",
"temperature": 0.8,
"precipitation": "rain",
"depth": 0,
"dimension": "overworld",
"displayName": "Beach",
"color": 16440917,
"rainfall": 0.4
},
{
"id": 17,
"name": "desert_hills",
"name_legacy": "desert_hills",
"category": "desert",
"temperature": 2,
"precipitation": "none",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Desert Hills",
"color": 13786898,
"rainfall": 0
},
{
"id": 18,
"name": "wooded_hills",
"name_legacy": "wooded_hills",
"category": "forest",
"temperature": 0.7,
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Forest Hills",
"color": 2250012,
"rainfall": 0.8
},
{
"id": 19,
"name": "taiga_hills",
"name_legacy": "taiga_hills",
"category": "taiga",
"temperature": 0.25,
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Taiga Hills",
"color": 1456435,
"rainfall": 0.8
},
{
"id": 20,
"name": "mountain_edge",
"name_legacy": "mountain_edge",
"category": "extreme_hills",
"temperature": 0.2,
"precipitation": "rain",
"depth": 0.8,
"dimension": "overworld",
"displayName": "Extreme Hills Edge",
"color": 7501978,
"rainfall": 0.3
},
{
"id": 21,
"name": "jungle",
"name_legacy": "jungle",
"category": "jungle",
"temperature": 0.95,
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Jungle",
"color": 5470985,
"rainfall": 0.9
},
{
"id": 22,
"name": "jungle_hills",
"name_legacy": "jungle_hills",
"category": "jungle",
"temperature": 0.95,
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Jungle Hills",
"color": 2900485,
"rainfall": 0.9
},
{
"id": 23,
"name": "jungle_edge",
"name_legacy": "jungle_edge",
"category": "jungle",
"temperature": 0.95,
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Jungle Edge",
"color": 6458135,
"rainfall": 0.8
},
{
"id": 24,
"name": "deep_ocean",
"name_legacy": "deep_ocean",
"category": "ocean",
"temperature": 0.5,
"precipitation": "rain",
"depth": -1.8,
"dimension": "overworld",
"displayName": "Deep Ocean",
"color": 48,
"rainfall": 0.5
},
{
"id": 25,
"name": "stone_shore",
"name_legacy": "stone_shore",
"category": "none",
"temperature": 0.2,
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Stone Beach",
"color": 10658436,
"rainfall": 0.3
},
{
"id": 26,
"name": "snowy_beach",
"name_legacy": "snowy_beach",
"category": "beach",
"temperature": 0.05,
"precipitation": "snow",
"depth": 0,
"dimension": "overworld",
"displayName": "Cold Beach",
"color": 16445632,
"rainfall": 0.3
},
{
"id": 27,
"name": "birch_forest",
"name_legacy": "birch_forest",
"category": "forest",
"temperature": 0.6,
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Birch Forest",
"color": 3175492,
"rainfall": 0.6
},
{
"id": 28,
"name": "birch_forest_hills",
"name_legacy": "birch_forest_hills",
"category": "forest",
"temperature": 0.6,
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Birch Forest Hills",
"color": 2055986,
"rainfall": 0.6
},
{
"id": 29,
"name": "dark_forest",
"name_legacy": "dark_forest",
"category": "forest",
"temperature": 0.7,
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Roofed Forest",
"color": 4215066,
"rainfall": 0.8
},
{
"id": 30,
"name": "snowy_taiga",
"name_legacy": "snowy_taiga",
"category": "taiga",
"temperature": -0.5,
"precipitation": "snow",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Cold Taiga",
"color": 3233098,
"rainfall": 0.4
},
{
"id": 31,
"name": "snowy_taiga_hills",
"name_legacy": "snowy_taiga_hills",
"category": "taiga",
"temperature": -0.5,
"precipitation": "snow",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Cold Taiga Hills",
"color": 2375478,
"rainfall": 0.4
},
{
"id": 32,
"name": "giant_tree_taiga",
"name_legacy": "giant_tree_taiga",
"category": "taiga",
"temperature": 0.3,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Mega Taiga",
"color": 5858897,
"rainfall": 0.8
},
{
"id": 33,
"name": "giant_tree_taiga_hills",
"name_legacy": "giant_tree_taiga_hills",
"category": "taiga",
"temperature": 0.3,
"precipitation": "rain",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Mega Taiga Hills",
"color": 4542270,
"rainfall": 0.8
},
{
"id": 34,
"name": "wooded_mountains",
"name_legacy": "wooded_mountains",
"category": "extreme_hills",
"temperature": 0.2,
"precipitation": "rain",
"depth": 1,
"dimension": "overworld",
"displayName": "Extreme Hills+",
"color": 5271632,
"rainfall": 0.3
},
{
"id": 35,
"name": "savanna",
"name_legacy": "savanna",
"category": "savanna",
"temperature": 1.2,
"precipitation": "none",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Savanna",
"color": 12431967,
"rainfall": 0
},
{
"id": 36,
"name": "savanna_plateau",
"name_legacy": "savanna_plateau",
"category": "savanna",
"temperature": 1,
"precipitation": "none",
"depth": 1.5,
"dimension": "overworld",
"displayName": "Savanna Plateau",
"color": 10984804,
"rainfall": 0
},
{
"id": 37,
"name": "badlands",
"name_legacy": "badlands",
"category": "mesa",
"temperature": 2,
"precipitation": "none",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Mesa",
"color": 14238997,
"rainfall": 0
},
{
"id": 38,
"name": "wooded_badlands_plateau",
"name_legacy": "wooded_badlands_plateau",
"category": "mesa",
"temperature": 2,
"precipitation": "none",
"depth": 1.5,
"dimension": "overworld",
"displayName": "Mesa Plateau F",
"color": 11573093,
"rainfall": 0
},
{
"id": 39,
"name": "badlands_plateau",
"name_legacy": "badlands_plateau",
"category": "mesa",
"temperature": 2,
"precipitation": "none",
"depth": 1.5,
"dimension": "overworld",
"displayName": "Mesa Plateau",
"color": 13274213,
"rainfall": 0
},
{
"id": 129,
"name": "sunflower_plains",
"name_legacy": "mutated_plains",
"category": "plains",
"temperature": 0.8,
"precipitation": "rain",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Plains M",
"color": 11918216,
"rainfall": 0.4
},
{
"id": 130,
"name": "desert_lakes",
"name_legacy": "mutated_desert",
"category": "desert",
"temperature": 2,
"precipitation": "none",
"depth": 0.125,
"dimension": "overworld",
"displayName": "Desert M",
"color": 16759872,
"rainfall": 0
},
{
"id": 131,
"name": "gravelly_mountains",
"name_legacy": "mutated_extreme_hills",
"category": "extreme_hills",
"temperature": 0.2,
"precipitation": "rain",
"depth": 1,
"dimension": "overworld",
"displayName": "Extreme Hills M",
"color": 8947848,
"rainfall": 0.3
},
{
"id": 132,
"name": "flower_forest",
"name_legacy": "mutated_forest",
"category": "forest",
"temperature": 0.7,
"precipitation": "rain",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Forest M",
"color": 2985545,
"rainfall": 0.8
},
{
"id": 133,
"name": "taiga_mountains",
"name_legacy": "mutated_taiga",
"category": "taiga",
"temperature": 0.25,
"precipitation": "rain",
"depth": 0.3,
"dimension": "overworld",
"displayName": "Taiga M",
"color": 3378817,
"rainfall": 0.8
},
{
"id": 134,
"name": "swamp_hills",
"name_legacy": "mutated_swampland",
"category": "swamp",
"temperature": 0.8,
"precipitation": "rain",
"depth": -0.1,
"dimension": "overworld",
"displayName": "Swampland M",
"color": 3145690,
"rainfall": 0.9
},
{
"id": 140,
"name": "ice_spikes",
"name_legacy": "mutated_ice_flats",
"category": "icy",
"temperature": 0,
"precipitation": "snow",
"depth": 0.425,
"dimension": "overworld",
"displayName": "Ice Plains M",
"color": 11853020,
"rainfall": 0.5
},
{
"id": 149,
"name": "modified_jungle",
"name_legacy": "mutated_jungle",
"category": "jungle",
"temperature": 0.95,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Jungle M",
"color": 8102705,
"rainfall": 0.9
},
{
"id": 151,
"name": "modified_jungle_edge",
"name_legacy": "mutated_jungle_edge",
"category": "jungle",
"temperature": 0.95,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "JungleEdge M",
"color": 9089855,
"rainfall": 0.8
},
{
"id": 155,
"name": "tall_birch_forest",
"name_legacy": "mutated_birch_forest",
"category": "forest",
"temperature": 0.6,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Birch Forest M",
"color": 5807212,
"rainfall": 0.6
},
{
"id": 156,
"name": "tall_birch_hills",
"name_legacy": "mutated_birch_forest_hills",
"category": "forest",
"temperature": 0.6,
"precipitation": "rain",
"depth": 0.55,
"dimension": "overworld",
"displayName": "Birch Forest Hills M",
"color": 4687706,
"rainfall": 0.6
},
{
"id": 157,
"name": "dark_forest_hills",
"name_legacy": "mutated_roofed_forest",
"category": "forest",
"temperature": 0.7,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Roofed Forest M",
"color": 6846786,
"rainfall": 0.8
},
{
"id": 158,
"name": "snowy_taiga_mountains",
"name_legacy": "mutated_taiga_cold",
"category": "taiga",
"temperature": -0.5,
"precipitation": "snow",
"depth": 0.3,
"dimension": "overworld",
"displayName": "Cold Taiga M",
"color": 5864818,
"rainfall": 0.4
},
{
"id": 160,
"name": "giant_spruce_taiga",
"name_legacy": "mutated_redwood_taiga",
"category": "taiga",
"temperature": 0.25,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Mega Taiga M",
"color": 8490617,
"rainfall": 0.8
},
{
"id": 161,
"name": "giant_spruce_taiga_hills",
"name_legacy": "mutated_redwood_taiga_hills",
"category": "taiga",
"temperature": 0.25,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Mega Taiga Hills M",
"color": 7173990,
"rainfall": 0.8
},
{
"id": 161,
"name": "giant_spruce_taiga_hills",
"name_legacy": "mutated_redwood_taiga_hills",
"category": "taiga",
"temperature": 0.25,
"precipitation": "rain",
"depth": 0.2,
"dimension": "overworld",
"displayName": "Redwood Taiga Hills M",
"color": 7173990,
"rainfall": 0.8
},
{
"id": 162,
"name": "modified_gravelly_mountains",
"name_legacy": "mutated_extreme_hills_with_trees",
"category": "extreme_hills",
"temperature": 0.2,
"precipitation": "rain",
"depth": 1,
"dimension": "overworld",
"displayName": "Extreme Hills+ M",
"color": 7903352,
"rainfall": 0.3
},
{
"id": 163,
"name": "shattered_savanna",
"name_legacy": "mutated_savanna",
"category": "savanna",
"temperature": 1.1,
"precipitation": "none",
"depth": 0.3625,
"dimension": "overworld",
"displayName": "Savanna M",
"color": 15063687,
"rainfall": 0
},
{
"id": 164,
"name": "shattered_savanna_plateau",
"name_legacy": "mutated_savanna_rock",
"category": "savanna",
"temperature": 1,
"precipitation": "none",
"depth": 1.05,
"dimension": "overworld",
"displayName": "Savanna Plateau M",
"color": 13616524,
"rainfall": 0
},
{
"id": 165,
"name": "eroded_badlands",
"name_legacy": "mutated_mesa",
"category": "mesa",
"temperature": 2,
"precipitation": "none",
"depth": 0.1,
"dimension": "overworld",
"displayName": "Mesa M",
"color": 16739645,
"rainfall": 0
},
{
"id": 166,
"name": "modified_wooded_badlands_plateau",
"name_legacy": "mutated_mesa_rock",
"category": "mesa",
"temperature": 2,
"precipitation": "none",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Mesa Plateau F M",
"color": 14204813,
"rainfall": 0
},
{
"id": 167,
"name": "modified_badlands_plateau",
"name_legacy": "mutated_mesa_clear_rock",
"category": "mesa",
"temperature": 2,
"precipitation": "none",
"depth": 0.45,
"dimension": "overworld",
"displayName": "Mesa Plateau M",
"color": 15905933,
"rainfall": 0
}
]
@@ -0,0 +1,274 @@
{"blocks":{"air":0,
"stone":1,
"grass":1,
"dirt":1,
"cobblestone":1,
"planks":1,
"sapling":0,
"bedrock":1,
"flowing_water":0,
"water":0,
"flowing_lava":0,
"lava":0,
"sand":1,
"gravel":1,
"gold_ore":1,
"iron_ore":1,
"coal_ore":1,
"log":1,
"leaves":1,
"sponge":1,
"glass":1,
"lapis_ore":1,
"lapis_block":1,
"dispenser":1,
"sandstone":1,
"noteblock":1,
"bed":2,
"golden_rail":0,
"detector_rail":0,
"sticky_piston":[1,1,1,1,1,1,0,0,3,4,5,6,7,8,0,0],
"web":0,
"tallgrass":0,
"deadbush":0,
"piston":[1,1,1,1,1,1,0,0,3,4,5,6,7,8,0,0],
"piston_head":[10,11,9,12,13,14,0,0,10,11,9,12,13,14,0,0],
"wool":1,
"piston_extension":0,
"yellow_flower":0,
"red_flower":0,
"brown_mushroom":0,
"red_mushroom":0,
"gold_block":1,
"iron_block":1,
"double_stone_slab":1,
"stone_slab":[15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16],
"brick_block":1,
"tnt":1,
"bookshelf":1,
"mossy_cobblestone":1,
"obsidian":1,
"torch":0,
"fire":0,
"mob_spawner":1,
"oak_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"chest":25,
"redstone_wire":0,
"diamond_ore":1,
"diamond_block":1,
"crafting_table":1,
"wheat":0,
"farmland":26,
"furnace":1,
"lit_furnace":1,
"standing_sign":0,
"wooden_door":[28,29,30,27,29,30,27,28,28,29,30,27,29,30,27,28],
"ladder":[27,27,27,29,30,28,27,27,27,29,30,28,27,27,27,29],
"rail":0,
"stone_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"wall_sign":0,
"lever":0,
"stone_pressure_plate":0,
"iron_door":[28,29,30,27,29,30,27,28,28,29,30,27,29,30,27,28],
"wooden_pressure_plate":0,
"redstone_ore":1,
"lit_redstone_ore":1,
"unlit_redstone_torch":0,
"redstone_torch":0,
"stone_button":0,
"snow_layer":[0,31,32,33,15,34,4,35,0,31,32,33,15,34,4,35],
"ice":1,
"snow":1,
"cactus":36,
"clay":1,
"reeds":0,
"jukebox":1,
"fence":37,
"pumpkin":1,
"netherrack":1,
"soul_sand":35,
"glowstone":1,
"portal":0,
"lit_pumpkin":1,
"cake":[38,39,40,41,42,43,44,0,0,0,0,0,0,0,0,0],
"unpowered_repeater":31,
"powered_repeater":31,
"stained_glass":1,
"trapdoor":[45,45,45,45,27,29,30,28,46,46,46,46,27,29,30,28],
"monster_egg":1,
"stonebrick":1,
"brown_mushroom_block":1,
"red_mushroom_block":1,
"iron_bars":47,
"glass_pane":47,
"melon_block":1,
"pumpkin_stem":0,
"melon_stem":0,
"vine":0,
"fence_gate":[48,49,48,49,0,0,0,0,48,49,48,49,0,0,0,0],
"brick_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"stone_brick_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"mycelium":1,
"waterlily":50,
"nether_brick":1,
"nether_brick_fence":37,
"nether_brick_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"nether_wart":0,
"enchanting_table":4,
"brewing_stand":51,
"cauldron":52,
"end_portal":0,
"end_portal_frame":[81,81,81,81,82,82,82,82,81,81,81,81,82,82,82,82],
"end_stone":1,
"dragon_egg":53,
"redstone_lamp":1,
"lit_redstone_lamp":1,
"double_wooden_slab":1,
"wooden_slab":[15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16],
"cocoa":[55,56,54,57,58,59,60,61,62,63,64,65,0,0,0,0],
"sandstone_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"emerald_ore":1,
"ender_chest":25,
"tripwire_hook":0,
"tripwire":0,
"emerald_block":1,
"spruce_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"birch_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"jungle_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"command_block":1,
"beacon":1,
"cobblestone_wall":66,
"flower_pot":67,
"carrots":0,
"potatoes":0,
"wooden_button":0,
"skull":[69,69,68,70,71,72,69,69,69,69,68,70,71,72,69,69],
"anvil":[73,74,73,74,73,74,73,74,73,74,73,74,0,0,0,0],
"trapped_chest":25,
"light_weighted_pressure_plate":0,
"heavy_weighted_pressure_plate":0,
"unpowered_comparator":31,
"powered_comparator":31,
"daylight_detector":33,
"redstone_block":1,
"quartz_ore":1,
"hopper":75,
"quartz_block":1,
"quartz_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"activator_rail":0,
"dropper":1,
"stained_hardened_clay":1,
"stained_glass_pane":47,
"leaves2":1,"log2":1,"acacia_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"dark_oak_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"slime":1,
"barrier":1,
"iron_trapdoor":[45,45,45,45,27,29,30,28,46,46,46,46,27,29,30,28],
"prismarine":1,
"sea_lantern":1,
"hay_block":1,
"carpet":76,
"hardened_clay":1,
"coal_block":1,
"packed_ice":1,
"double_plant":0,
"standing_banner":0,
"wall_banner":0,
"daylight_detector_inverted":33,
"red_sandstone":1,
"red_sandstone_stairs":[18,19,20,17,21,22,23,24,18,19,20,17,21,22,23,24],
"double_stone_slab2":1,"stone_slab2":[15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16],
"spruce_fence_gate":[48,49,48,49,0,0,0,0,48,49,48,49,0,0,0,0],
"birch_fence_gate":[48,49,48,49,0,0,0,0,48,49,48,49,0,0,0,0],
"jungle_fence_gate":[48,49,48,49,0,0,0,0,48,49,48,49,0,0,0,0],
"dark_oak_fence_gate":[48,49,48,49,0,0,0,0,48,49,48,49,0,0,0,0],
"acacia_fence_gate":[48,49,48,49,0,0,0,0,48,49,48,49,0,0,0,0],
"spruce_fence":37,
"birch_fence":37,
"jungle_fence":37,
"dark_oak_fence":37,
"acacia_fence":37,
"spruce_door":[28,29,30,27,29,30,27,28,28,29,30,27,29,30,27,28],
"birch_door":[28,29,30,27,29,30,27,28,28,29,30,27,29,30,27,28],
"jungle_door":[28,29,30,27,29,30,27,28,28,29,30,27,29,30,27,28],
"acacia_door":[28,29,30,27,29,30,27,28,28,29,30,27,29,30,27,28],
"dark_oak_door":[28,29,30,27,29,30,27,28,28,29,30,27,29,30,27,28]},
"shapes":{"0":[],
"1":[[0.0,0.0,0.0,1.0,1.0,1.0]],
"2":[[0.0,0.0,0.0,1.0,0.5625,1.0]],
"3":[[0.0,0.25,0.0,1.0,1.0,1.0]],
"4":[[0.0,0.0,0.0,1.0,0.75,1.0]],
"5":[[0.0,0.0,0.25,1.0,1.0,1.0]],
"6":[[0.0,0.0,0.0,1.0,1.0,0.75]],
"7":[[0.25,0.0,0.0,1.0,1.0,1.0]],
"8":[[0.0,0.0,0.0,0.75,1.0,1.0]],
"9":[[0.0,0.0,0.0,1.0,1.0,0.25],[0.375,0.375,0.25,0.625,0.625,1.25]],
"10":[[0.0,0.0,0.0,1.0,0.25,1.0],[0.375,0.25,0.375,0.625,1.25,0.625]],
"11":[[0.0,0.75,0.0,1.0,1.0,1.0],[0.375,-0.25,0.375,0.625,0.75,0.625]],
"12":[[0.0,0.0,0.75,1.0,1.0,1.0],[0.375,0.375,-0.25,0.625,0.625,0.75]],
"13":[[0.0,0.0,0.0,0.25,1.0,1.0],[0.25,0.375,0.375,1.25,0.625,0.625]],
"14":[[0.75,0.0,0.0,1.0,1.0,1.0],[-0.25,0.375,0.375,0.75,0.625,0.625]],
"15":[[0.0,0.0,0.0,1.0,0.5,1.0]],
"16":[[0.0,0.5,0.0,1.0,1.0,1.0]],
"17":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.0,1.0,1.0,0.5]],
"18":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.5,0.5,0.0,1.0,1.0,1.0]],
"19":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.0,0.5,1.0,1.0]],
"20":[[0.0,0.0,0.0,1.0,0.5,1.0],[0.0,0.5,0.5,1.0,1.0,1.0]],
"21":[[0.0,0.5,0.0,1.0,1.0,1.0],[0.5,0.0,0.0,1.0,0.5,1.0]],
"22":[[0.0,0.5,0.0,1.0,1.0,1.0],[0.0,0.0,0.0,0.5,0.5,1.0]],
"23":[[0.0,0.5,0.0,1.0,1.0,1.0],[0.0,0.0,0.5,1.0,0.5,1.0]],
"24":[[0.0,0.5,0.0,1.0,1.0,1.0],[0.0,0.0,0.0,1.0,0.5,0.5]],
"25":[[0.0625,0.0,0.0625,0.9375,0.875,0.9375]],
"26":[[0.0,0.0,0.0,1.0,0.9375,1.0]],
"27":[[0.0,0.0,0.8125,1.0,1.0,1.0]],
"28":[[0.0,0.0,0.0,0.1875,1.0,1.0]],
"29":[[0.0,0.0,0.0,1.0,1.0,0.1875]],
"30":[[0.8125,0.0,0.0,1.0,1.0,1.0]],
"31":[[0.0,0.0,0.0,1.0,0.125,1.0]],
"32":[[0.0,0.0,0.0,1.0,0.25,1.0]],
"33":[[0.0,0.0,0.0,1.0,0.375,1.0]],
"34":[[0.0,0.0,0.0,1.0,0.625,1.0]],
"35":[[0.0,0.0,0.0,1.0,0.875,1.0]],
"36":[[0.0625,0.0,0.0625,0.9375,0.9375,0.9375]],
"37":[[0.375,0.0,0.375,0.625,1.5,0.625]],
"38":[[0.0625,0.0,0.0625,0.9375,0.5,0.9375]],
"39":[[0.1875,0.0,0.0625,0.9375,0.5,0.9375]],
"40":[[0.3125,0.0,0.0625,0.9375,0.5,0.9375]],
"41":[[0.4375,0.0,0.0625,0.9375,0.5,0.9375]],
"42":[[0.5625,0.0,0.0625,0.9375,0.5,0.9375]],
"43":[[0.6875,0.0,0.0625,0.9375,0.5,0.9375]],
"44":[[0.8125,0.0,0.0625,0.9375,0.5,0.9375]],
"45":[[0.0,0.0,0.0,1.0,0.1875,1.0]],
"46":[[0.0,0.8125,0.0,1.0,1.0,1.0]],
"47":[[0.4375,0.0,0.4375,0.5625,1.0,0.5625]],
"48":[[0.0,0.0,0.375,1.0,1.5,0.625]],
"49":[[0.375,0.0,0.0,0.625,1.5,1.0]],
"50":[[0.0625,0.0,0.0625,0.9375,0.09375,0.9375]],
"51":[[0.4375,0.0,0.4375,0.5625,0.875,0.5625],[0.0,0.0,0.0,1.0,0.125,1.0]],
"52":[[0.0,0.0,0.0,1.0,0.3125,1.0],[0.0,0.0,0.0,0.125,1.0,1.0],[0.0,0.0,0.0,1.0,1.0,0.125],[0.875,0.0,0.0,1.0,1.0,1.0],[0.0,0.0,0.875,1.0,1.0,1.0]],
"53":[[0.0625,0.0,0.0625,0.9375,1.0,0.9375]],
"54":[[0.375,0.4375,0.0625,0.625,0.75,0.3125]],
"55":[[0.375,0.4375,0.6875,0.625,0.75,0.9375]],
"56":[[0.0625,0.4375,0.375,0.3125,0.75,0.625]],
"57":[[0.6875,0.4375,0.375,0.9375,0.75,0.625]],
"58":[[0.3125,0.3125,0.5625,0.6875,0.75,0.9375]],
"59":[[0.0625,0.3125,0.3125,0.4375,0.75,0.6875]],
"60":[[0.3125,0.3125,0.0625,0.6875,0.75,0.4375]],
"61":[[0.5625,0.3125,0.3125,0.9375,0.75,0.6875]],
"62":[[0.25,0.1875,0.4375,0.75,0.75,0.9375]],
"63":[[0.0625,0.1875,0.25,0.5625,0.75,0.75]],
"64":[[0.25,0.1875,0.0625,0.75,0.75,0.5625]],
"65":[[0.4375,0.1875,0.25,0.9375,0.75,0.75]],
"66":[[0.25,0.0,0.25,0.75,1.5,0.75]],
"67":[[0.3125,0.0,0.3125,0.6875,0.375,0.6875]],
"68":[[0.25,0.25,0.5,0.75,0.75,1.0]],
"69":[[0.25,0.0,0.25,0.75,0.5,0.75]],
"70":[[0.25,0.25,0.0,0.75,0.75,0.5]],
"71":[[0.5,0.25,0.25,1.0,0.75,0.75]],
"72":[[0.0,0.25,0.25,0.5,0.75,0.75]],
"73":[[0.125,0.0,0.0,0.875,1.0,1.0]],
"74":[[0.0,0.0,0.125,1.0,1.0,0.875]],
"75":[[0.0,0.0,0.0,1.0,0.625,1.0],[0.0,0.0,0.0,0.125,1.0,1.0],[0.875,0.0,0.0,1.0,1.0,1.0],[0.0,0.0,0.0,1.0,1.0,0.125],[0.0,0.0,0.875,1.0,1.0,1.0]],
"76":[[0.0,0.0,0.0,1.0,0.0625,1.0]],
"81":[[0.0,0.0,0.0,1.0,0.8125,1.0]],
"82":[[0.0,0.0,0.0,1.0,0.8125,1.0],[0.3125,0.8125,0.3125,0.6875,1.0,0.6875]]}}
File diff suppressed because it is too large Load Diff
+140
View File
@@ -0,0 +1,140 @@
[
{
"id": 1,
"name": "Speed",
"displayName": "Speed",
"type": "good"
},
{
"id": 2,
"name": "Slowness",
"displayName": "Slowness",
"type": "bad"
},
{
"id": 3,
"name": "Haste",
"displayName": "Haste",
"type": "good"
},
{
"id": 4,
"name": "MiningFatigue",
"displayName": "Mining Fatigue",
"type": "bad"
},
{
"id": 5,
"name": "Strength",
"displayName": "Strength",
"type": "good"
},
{
"id": 6,
"name": "InstantHealth",
"displayName": "Instant Health",
"type": "good"
},
{
"id": 7,
"name": "InstantDamage",
"displayName": "Instant Damage",
"type": "bad"
},
{
"id": 8,
"name": "JumpBoost",
"displayName": "Jump Boost",
"type": "good"
},
{
"id": 9,
"name": "Nausea",
"displayName": "Nausea",
"type": "bad"
},
{
"id": 10,
"name": "Regeneration",
"displayName": "Regeneration",
"type": "good"
},
{
"id": 11,
"name": "Resistance",
"displayName": "Resistance",
"type": "good"
},
{
"id": 12,
"name": "FireResistance",
"displayName": "Fire Resistance",
"type": "good"
},
{
"id": 13,
"name": "WaterBreathing",
"displayName": "Water Breathing",
"type": "good"
},
{
"id": 14,
"name": "Invisibility",
"displayName": "Invisibility",
"type": "good"
},
{
"id": 15,
"name": "Blindness",
"displayName": "Blindness",
"type": "bad"
},
{
"id": 16,
"name": "NightVision",
"displayName": "Night Vision",
"type": "good"
},
{
"id": 17,
"name": "Hunger",
"displayName": "Hunger",
"type": "bad"
},
{
"id": 18,
"name": "Weakness",
"displayName": "Weakness",
"type": "bad"
},
{
"id": 19,
"name": "Poison",
"displayName": "Poison",
"type": "bad"
},
{
"id": 20,
"name": "Wither",
"displayName": "Wither",
"type": "bad"
},
{
"id": 21,
"name": "HealthBoost",
"displayName": "Health Boost",
"type": "good"
},
{
"id": 22,
"name": "Absorption",
"displayName": "Absorption",
"type": "good"
},
{
"id": 23,
"name": "Saturation",
"displayName": "Saturation",
"type": "good"
}
]
@@ -0,0 +1,560 @@
[
{
"id": 0,
"name": "protection",
"displayName": "Protection",
"maxLevel": 4,
"minCost": {
"a": 11,
"b": -10
},
"maxCost": {
"a": 11,
"b": 1
},
"exclude": [
"blast_protection",
"fire_protection",
"projectile_protection"
],
"category": "armor",
"weight": 10,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 1,
"name": "fire_protection",
"displayName": "Fire Protection",
"maxLevel": 4,
"minCost": {
"a": 8,
"b": 2
},
"maxCost": {
"a": 8,
"b": 10
},
"exclude": [
"blast_protection",
"protection",
"projectile_protection"
],
"category": "armor",
"weight": 5,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 2,
"name": "feather_falling",
"displayName": "Feather Falling",
"maxLevel": 4,
"minCost": {
"a": 6,
"b": -1
},
"maxCost": {
"a": 6,
"b": 5
},
"exclude": [],
"category": "armor_feet",
"weight": 5,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 3,
"name": "blast_protection",
"displayName": "Blast Protection",
"maxLevel": 4,
"minCost": {
"a": 8,
"b": -3
},
"maxCost": {
"a": 8,
"b": 5
},
"exclude": [
"fire_protection",
"protection",
"projectile_protection"
],
"category": "armor",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 4,
"name": "projectile_protection",
"displayName": "Projectile Protection",
"maxLevel": 4,
"minCost": {
"a": 6,
"b": -3
},
"maxCost": {
"a": 6,
"b": 3
},
"exclude": [
"protection",
"blast_protection",
"fire_protection"
],
"category": "armor",
"weight": 5,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 5,
"name": "respiration",
"displayName": "Respiration",
"maxLevel": 3,
"minCost": {
"a": 10,
"b": 0
},
"maxCost": {
"a": 10,
"b": 30
},
"exclude": [],
"category": "armor_head",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 6,
"name": "aqua_affinity",
"displayName": "Aqua Affinity",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 1
},
"maxCost": {
"a": 0,
"b": 41
},
"exclude": [],
"category": "armor_head",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 7,
"name": "thorns",
"displayName": "Thorns",
"maxLevel": 3,
"minCost": {
"a": 20,
"b": -10
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [],
"category": "armor_chest",
"weight": 1,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 8,
"name": "depth_strider",
"displayName": "Depth Strider",
"maxLevel": 3,
"minCost": {
"a": 10,
"b": 0
},
"maxCost": {
"a": 10,
"b": 15
},
"exclude": [
"frost_walker"
],
"category": "armor_feet",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 16,
"name": "sharpness",
"displayName": "Sharpness",
"maxLevel": 5,
"minCost": {
"a": 11,
"b": -10
},
"maxCost": {
"a": 11,
"b": 10
},
"exclude": [
"smite",
"bane_of_arthropods"
],
"category": "weapon",
"weight": 10,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 17,
"name": "smite",
"displayName": "Smite",
"maxLevel": 5,
"minCost": {
"a": 8,
"b": -3
},
"maxCost": {
"a": 8,
"b": 17
},
"exclude": [
"sharpness",
"bane_of_arthropods"
],
"category": "weapon",
"weight": 5,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 18,
"name": "bane_of_arthropods",
"displayName": "Bane of Arthropods",
"maxLevel": 5,
"minCost": {
"a": 8,
"b": -3
},
"maxCost": {
"a": 8,
"b": 17
},
"exclude": [
"smite",
"sharpness"
],
"category": "weapon",
"weight": 5,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 19,
"name": "knockback",
"displayName": "Knockback",
"maxLevel": 2,
"minCost": {
"a": 20,
"b": -15
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [],
"category": "weapon",
"weight": 5,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 20,
"name": "fire_aspect",
"displayName": "Fire Aspect",
"maxLevel": 2,
"minCost": {
"a": 20,
"b": -10
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [],
"category": "weapon",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 21,
"name": "looting",
"displayName": "Looting",
"maxLevel": 3,
"minCost": {
"a": 9,
"b": 6
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [],
"category": "weapon",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 32,
"name": "efficiency",
"displayName": "Efficiency",
"maxLevel": 5,
"minCost": {
"a": 10,
"b": -9
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [],
"category": "digger",
"weight": 10,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 33,
"name": "silk_touch",
"displayName": "Silk Touch",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 15
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [
"fortune"
],
"category": "digger",
"weight": 1,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 34,
"name": "unbreaking",
"displayName": "Unbreaking",
"maxLevel": 3,
"minCost": {
"a": 8,
"b": -3
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [],
"category": "breakable",
"weight": 5,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 35,
"name": "fortune",
"displayName": "Fortune",
"maxLevel": 3,
"minCost": {
"a": 9,
"b": 6
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [
"silk_touch"
],
"category": "digger",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 48,
"name": "power",
"displayName": "Power",
"maxLevel": 5,
"minCost": {
"a": 10,
"b": -9
},
"maxCost": {
"a": 10,
"b": 6
},
"exclude": [],
"category": "bow",
"weight": 10,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 49,
"name": "punch",
"displayName": "Punch",
"maxLevel": 2,
"minCost": {
"a": 20,
"b": -8
},
"maxCost": {
"a": 20,
"b": 17
},
"exclude": [],
"category": "bow",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 50,
"name": "flame",
"displayName": "Flame",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 20
},
"maxCost": {
"a": 0,
"b": 50
},
"exclude": [],
"category": "bow",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 51,
"name": "infinity",
"displayName": "Infinity",
"maxLevel": 1,
"minCost": {
"a": 0,
"b": 20
},
"maxCost": {
"a": 0,
"b": 50
},
"exclude": [
"mending"
],
"category": "bow",
"weight": 1,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 61,
"name": "luck_of_the_sea",
"displayName": "Luck of the Sea",
"maxLevel": 3,
"minCost": {
"a": 9,
"b": 6
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [],
"category": "fishing_rod",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
},
{
"id": 62,
"name": "lure",
"displayName": "Lure",
"maxLevel": 3,
"minCost": {
"a": 9,
"b": 6
},
"maxCost": {
"a": 10,
"b": 51
},
"exclude": [],
"category": "fishing_rod",
"weight": 2,
"treasureOnly": false,
"curse": false,
"tradeable": true,
"discoverable": true
}
]
+580
View File
@@ -0,0 +1,580 @@
[
{
"id": 48,
"internalId": 48,
"name": "Mob",
"displayName": "Mob",
"type": "mob",
"width": null,
"height": null,
"category": "Generic"
},
{
"id": 49,
"internalId": 49,
"name": "Monster",
"displayName": "Monster",
"type": "mob",
"width": null,
"height": null,
"category": "Generic"
},
{
"id": 50,
"internalId": 50,
"name": "Creeper",
"displayName": "Creeper",
"type": "mob",
"width": 0.6,
"height": 1.8,
"category": "Hostile mobs"
},
{
"id": 51,
"internalId": 51,
"name": "Skeleton",
"displayName": "Skeleton",
"type": "mob",
"width": 0.6,
"height": 1.95,
"category": "Hostile mobs"
},
{
"id": 52,
"internalId": 52,
"name": "Spider",
"displayName": "Spider",
"type": "mob",
"width": 1.4,
"height": 0.9,
"category": "Hostile mobs"
},
{
"id": 53,
"internalId": 53,
"name": "Giant",
"displayName": "Giant",
"type": "mob",
"width": 3.5999999999999996,
"height": 10.8,
"category": "Hostile mobs"
},
{
"id": 54,
"internalId": 54,
"name": "Zombie",
"displayName": "Zombie",
"type": "mob",
"width": 0.6,
"height": 1.8,
"category": "Hostile mobs"
},
{
"id": 55,
"internalId": 55,
"name": "Slime",
"displayName": "Slime",
"type": "mob",
"width": 0.51000005,
"height": 0.51000005,
"category": "Hostile mobs"
},
{
"id": 56,
"internalId": 56,
"name": "Ghast",
"displayName": "Ghast",
"type": "mob",
"width": 4,
"height": 4,
"category": "Hostile mobs"
},
{
"id": 57,
"internalId": 57,
"name": "PigZombie",
"displayName": "Zombie Pigman",
"type": "mob",
"width": 0.6,
"height": 1.8,
"category": "Hostile mobs"
},
{
"id": 58,
"internalId": 58,
"name": "Enderman",
"displayName": "Enderman",
"type": "mob",
"width": 0.6,
"height": 2.9,
"category": "Hostile mobs"
},
{
"id": 59,
"internalId": 59,
"name": "CaveSpider",
"displayName": "Cave Spider",
"type": "mob",
"width": 0.7,
"height": 0.5,
"category": "Hostile mobs"
},
{
"id": 60,
"internalId": 60,
"name": "Silverfish",
"displayName": "Silverfish",
"type": "mob",
"width": 0.4,
"height": 0.3,
"category": "Hostile mobs"
},
{
"id": 61,
"internalId": 61,
"name": "Blaze",
"displayName": "Blaze",
"type": "mob",
"width": 0.6,
"height": 1.8,
"category": "Hostile mobs"
},
{
"id": 62,
"internalId": 62,
"name": "LavaSlime",
"displayName": "Magma Cube",
"type": "mob",
"width": 0.51000005,
"height": 0.51000005,
"category": "Hostile mobs"
},
{
"id": 63,
"internalId": 63,
"name": "EnderDragon",
"displayName": "Ender Dragon",
"type": "mob",
"width": 16,
"height": 8,
"category": "Hostile mobs"
},
{
"id": 64,
"internalId": 64,
"name": "WitherBoss",
"displayName": "Wither",
"type": "mob",
"width": 0.9,
"height": 3.5,
"category": "Hostile mobs"
},
{
"id": 65,
"internalId": 65,
"name": "Bat",
"displayName": "Bat",
"type": "mob",
"width": 0.5,
"height": 0.9,
"category": "Passive mobs"
},
{
"id": 66,
"internalId": 66,
"name": "Witch",
"displayName": "Witch",
"type": "mob",
"width": 0.6,
"height": 1.8,
"category": "Hostile mobs"
},
{
"id": 67,
"internalId": 67,
"name": "Endermite",
"displayName": "Endermite",
"type": "mob",
"width": 0.4,
"height": 0.3,
"category": "Hostile mobs"
},
{
"id": 68,
"internalId": 68,
"name": "Guardian",
"displayName": "Guardian",
"type": "mob",
"width": 0.85,
"height": 0.85,
"category": "Hostile mobs"
},
{
"id": 90,
"internalId": 90,
"name": "Pig",
"displayName": "Pig",
"type": "mob",
"width": 0.9,
"height": 0.9,
"category": "Passive mobs"
},
{
"id": 91,
"internalId": 91,
"name": "Sheep",
"displayName": "Sheep",
"type": "mob",
"width": 0.9,
"height": 1.3,
"category": "Passive mobs"
},
{
"id": 92,
"internalId": 92,
"name": "Cow",
"displayName": "Cow",
"type": "mob",
"width": 0.9,
"height": 1.3,
"category": "Passive mobs"
},
{
"id": 93,
"internalId": 93,
"name": "Chicken",
"displayName": "Chicken",
"type": "mob",
"width": 0.4,
"height": 0.7,
"category": "Passive mobs"
},
{
"id": 94,
"internalId": 94,
"name": "Squid",
"displayName": "Squid",
"type": "mob",
"width": 0.95,
"height": 0.95,
"category": "Passive mobs"
},
{
"id": 95,
"internalId": 95,
"name": "Wolf",
"displayName": "Wolf",
"type": "mob",
"width": 0.6,
"height": 0.8,
"category": "Passive mobs"
},
{
"id": 96,
"internalId": 96,
"name": "MushroomCow",
"displayName": "Mooshroom",
"type": "mob",
"width": 0.9,
"height": 1.3,
"category": "Passive mobs"
},
{
"id": 97,
"internalId": 97,
"name": "SnowMan",
"displayName": "Snow Golem",
"type": "mob",
"width": 0.7,
"height": 1.9,
"category": "Passive mobs"
},
{
"id": 98,
"internalId": 98,
"name": "Ozelot",
"displayName": "Ocelot",
"type": "mob",
"width": 0.6,
"height": 0.8,
"category": "Passive mobs"
},
{
"id": 99,
"internalId": 99,
"name": "VillagerGolem",
"displayName": "Iron Golem",
"type": "mob",
"width": 1.4,
"height": 2.9,
"category": "Passive mobs"
},
{
"id": 100,
"internalId": 100,
"name": "EntityHorse",
"displayName": "Horse",
"type": "mob",
"width": 1.4,
"height": 1.6,
"category": "Passive mobs"
},
{
"id": 101,
"internalId": 101,
"name": "Rabbit",
"displayName": "Rabbit",
"type": "mob",
"width": 0.6,
"height": 0.7,
"category": "Passive mobs"
},
{
"id": 120,
"internalId": 120,
"name": "Villager",
"displayName": "Villager",
"type": "mob",
"width": 0.6,
"height": 1.8,
"category": "NPCs"
},
{
"id": 1,
"internalId": 41,
"name": "Boat",
"displayName": "Boat",
"type": "object",
"width": 1.5,
"height": 0.6,
"category": "Vehicles"
},
{
"id": 2,
"internalId": 1,
"name": "Item",
"displayName": "Dropped item",
"type": "object",
"width": 0.25,
"height": 0.25,
"category": "Drops"
},
{
"id": 10,
"internalId": 42,
"name": "MinecartRideable",
"displayName": "Minecart",
"type": "object",
"width": 0.98,
"height": 0.7,
"category": "Vehicles"
},
{
"id": 11,
"internalId": 42,
"name": "MinecartRideable",
"displayName": "Minecart",
"type": "object",
"width": 0.98,
"height": 0.7,
"category": "Vehicles"
},
{
"id": 12,
"internalId": 42,
"name": "MinecartRideable",
"displayName": "Minecart",
"type": "object",
"width": 0.98,
"height": 0.7,
"category": "Vehicles"
},
{
"id": 50,
"internalId": 20,
"name": "PrimedTnt",
"displayName": "Primed TNT",
"type": "object",
"width": 0.98,
"height": 0.98,
"category": "Blocks"
},
{
"id": 51,
"internalId": 200,
"name": "EnderCrystal",
"displayName": "Ender Crystal",
"type": "object",
"width": 2,
"height": 2,
"category": "Immobile"
},
{
"id": 60,
"internalId": 10,
"name": "Arrow",
"displayName": "Shot arrow",
"type": "object",
"width": 0.5,
"height": 0.5,
"category": "Projectiles"
},
{
"id": 61,
"internalId": 11,
"name": "Snowball",
"displayName": "Thrown snowball",
"type": "object",
"width": 0.25,
"height": 0.25,
"category": "Projectiles"
},
{
"id": 62,
"internalId": 7,
"name": "ThrownEgg",
"displayName": "Thrown egg",
"type": "object",
"width": 0.25,
"height": 0.25,
"category": "Projectiles"
},
{
"id": 63,
"internalId": 12,
"name": "Fireball",
"displayName": "Ghast fireball",
"type": "object",
"width": 1,
"height": 1,
"category": "Projectiles"
},
{
"id": 64,
"internalId": 13,
"name": "SmallFireball",
"displayName": "Blaze fireball",
"type": "object",
"width": 0.3125,
"height": 0.3125,
"category": "Projectiles"
},
{
"id": 65,
"internalId": 14,
"name": "ThrownEnderpearl",
"displayName": "Thrown Ender Pearl",
"type": "object",
"width": 0.25,
"height": 0.25,
"category": "Projectiles"
},
{
"id": 66,
"internalId": 19,
"name": "WitherSkull",
"displayName": "Wither Skull",
"type": "object",
"width": 0.3125,
"height": 0.3125,
"category": "Projectiles"
},
{
"id": 70,
"internalId": 21,
"name": "FallingSand",
"displayName": "Falling block",
"type": "object",
"width": 0.98,
"height": 0.98,
"category": "Blocks"
},
{
"id": 71,
"internalId": 18,
"name": "ItemFrame",
"displayName": "Item Frame",
"type": "object",
"width": null,
"height": null,
"category": "Immobile"
},
{
"id": 72,
"internalId": 15,
"name": "EyeOfEnderSignal",
"displayName": "Thrown Eye of Ender",
"type": "object",
"width": 0.25,
"height": 0.25,
"category": "Projectiles"
},
{
"id": 73,
"internalId": 16,
"name": "ThrownPotion",
"displayName": "Thrown splash potion",
"type": "object",
"width": 0.25,
"height": 0.25,
"category": "Projectiles"
},
{
"id": 74,
"internalId": 21,
"name": "FallingSand",
"displayName": "Falling block",
"type": "object",
"width": 0.98,
"height": 0.98,
"category": "Blocks"
},
{
"id": 75,
"internalId": 17,
"name": "ThrownExpBottle",
"displayName": "Thrown Bottle o' Enchanting",
"type": "object",
"width": 0.25,
"height": 0.25,
"category": "Projectiles"
},
{
"id": 76,
"internalId": 22,
"name": "FireworksRocketEntity",
"displayName": "Firework Rocket",
"type": "object",
"width": 0.25,
"height": 0.25,
"category": "Projectiles"
},
{
"id": 77,
"internalId": 8,
"name": "LeashKnot",
"displayName": "Lead knot",
"type": "object",
"width": 0.5,
"height": 0.5,
"category": "Immobile"
},
{
"id": 78,
"internalId": 30,
"name": "ArmorStand",
"displayName": "Armor Stand",
"type": "object",
"width": 0.5,
"height": 2,
"category": "Immobile"
},
{
"id": 90,
"name": "Fishing Float",
"displayName": "Fishing Float",
"type": "object",
"width": 0.25,
"height": 0.25
}
]
+310
View File
@@ -0,0 +1,310 @@
[
{
"id": 103,
"displayName": "Melon",
"name": "melon_block",
"stackSize": 64,
"foodPoints": 2,
"saturation": 1.2,
"effectiveQuality": 3.2,
"saturationRatio": 0.6
},
{
"id": 141,
"displayName": "Carrot",
"name": "carrots",
"stackSize": 64,
"foodPoints": 3,
"saturation": 4.8,
"effectiveQuality": 7.8,
"saturationRatio": 1.5999999999999999
},
{
"id": 142,
"displayName": "Potato",
"name": "potatoes",
"stackSize": 64,
"foodPoints": 1,
"saturation": 0.6,
"effectiveQuality": 1.6,
"saturationRatio": 0.6
},
{
"id": 260,
"displayName": "Apple",
"stackSize": 64,
"name": "apple",
"foodPoints": 4,
"saturation": 2.4,
"effectiveQuality": 6.4,
"saturationRatio": 0.6
},
{
"id": 282,
"displayName": "Mushroom Stew",
"stackSize": 1,
"name": "mushroom_stew",
"foodPoints": 6,
"saturation": 7.2,
"effectiveQuality": 13.2,
"saturationRatio": 1.2
},
{
"id": 297,
"displayName": "Bread",
"stackSize": 64,
"name": "bread",
"foodPoints": 5,
"saturation": 6,
"effectiveQuality": 11,
"saturationRatio": 1.2
},
{
"id": 319,
"displayName": "Raw Porkchop",
"stackSize": 64,
"name": "porkchop",
"foodPoints": 3,
"saturation": 1.8,
"effectiveQuality": 4.8,
"saturationRatio": 0.6
},
{
"id": 320,
"displayName": "Cooked Porkchop",
"stackSize": 64,
"name": "cooked_porkchop",
"foodPoints": 8,
"saturation": 12.8,
"effectiveQuality": 20.8,
"saturationRatio": 1.6
},
{
"id": 322,
"displayName": "Golden Apple",
"stackSize": 64,
"name": "golden_apple",
"variations": [
{
"metadata": 0,
"displayName": "Golden Apple"
},
{
"metadata": 1,
"displayName": "Enchanted Golden Apple"
}
],
"foodPoints": 4,
"saturation": 9.6,
"effectiveQuality": 13.6,
"saturationRatio": 2.4
},
{
"id": 349,
"displayName": "Raw Fish",
"stackSize": 64,
"name": "fish",
"variations": [
{
"metadata": 0,
"displayName": "Raw Fish"
},
{
"metadata": 1,
"displayName": "Raw Salmon"
},
{
"metadata": 2,
"displayName": "Clownfish"
},
{
"metadata": 3,
"displayName": "Pufferfish"
}
],
"foodPoints": 2,
"saturation": 0.4,
"effectiveQuality": 2.4,
"saturationRatio": 0.2
},
{
"id": 350,
"displayName": "Cooked Fish",
"stackSize": 64,
"name": "cooked_fish",
"foodPoints": 5,
"saturation": 6,
"effectiveQuality": 11,
"saturationRatio": 1.2
},
{
"id": 354,
"displayName": "Cake",
"stackSize": 1,
"name": "cake",
"foodPoints": 2,
"saturation": 0.4,
"effectiveQuality": 2.4,
"saturationRatio": 0.2
},
{
"id": 357,
"displayName": "Cookie",
"stackSize": 64,
"name": "cookie",
"foodPoints": 2,
"saturation": 0.4,
"effectiveQuality": 2.4,
"saturationRatio": 0.2
},
{
"id": 363,
"displayName": "Raw Beef",
"stackSize": 64,
"name": "beef",
"foodPoints": 3,
"saturation": 1.8,
"effectiveQuality": 4.8,
"saturationRatio": 0.6
},
{
"id": 364,
"displayName": "Steak",
"stackSize": 64,
"name": "cooked_beef",
"foodPoints": 8,
"saturation": 12.8,
"effectiveQuality": 20.8,
"saturationRatio": 1.6
},
{
"id": 365,
"displayName": "Raw Chicken",
"stackSize": 64,
"name": "chicken",
"foodPoints": 2,
"saturation": 1.2,
"effectiveQuality": 3.2,
"saturationRatio": 0.6
},
{
"id": 366,
"displayName": "Cooked Chicken",
"stackSize": 64,
"name": "cooked_chicken",
"foodPoints": 6,
"saturation": 7.2,
"effectiveQuality": 13.2,
"saturationRatio": 1.2
},
{
"id": 367,
"displayName": "Rotten Flesh",
"stackSize": 64,
"name": "rotten_flesh",
"foodPoints": 4,
"saturation": 0.8,
"effectiveQuality": 4.8,
"saturationRatio": 0.2
},
{
"id": 375,
"displayName": "Spider Eye",
"stackSize": 64,
"name": "spider_eye",
"foodPoints": 2,
"saturation": 3.2,
"effectiveQuality": 5.2,
"saturationRatio": 1.6
},
{
"id": 393,
"displayName": "Baked Potato",
"stackSize": 64,
"name": "baked_potato",
"foodPoints": 5,
"saturation": 7.2,
"effectiveQuality": 12.2,
"saturationRatio": 1.44
},
{
"id": 394,
"displayName": "Poisonous Potato",
"stackSize": 64,
"name": "poisonous_potato",
"foodPoints": 2,
"saturation": 1.2,
"effectiveQuality": 3.2,
"saturationRatio": 0.6
},
{
"id": 396,
"displayName": "Golden Carrot",
"stackSize": 64,
"name": "golden_carrot",
"foodPoints": 6,
"saturation": 14.4,
"effectiveQuality": 20.4,
"saturationRatio": 2.4
},
{
"id": 400,
"displayName": "Pumpkin Pie",
"stackSize": 64,
"name": "pumpkin_pie",
"foodPoints": 8,
"saturation": 4.8,
"effectiveQuality": 12.8,
"saturationRatio": 0.6
},
{
"id": 411,
"displayName": "Raw Rabbit",
"stackSize": 64,
"name": "rabbit",
"foodPoints": 3,
"saturation": 1.8,
"effectiveQuality": 4.8,
"saturationRatio": 0.6
},
{
"id": 412,
"displayName": "Cooked Rabbit",
"stackSize": 64,
"name": "cooked_rabbit",
"foodPoints": 5,
"saturation": 6,
"effectiveQuality": 11,
"saturationRatio": 1.2
},
{
"id": 413,
"displayName": "Rabbit Stew",
"stackSize": 1,
"name": "rabbit_stew",
"foodPoints": 10,
"saturation": 12,
"effectiveQuality": 22,
"saturationRatio": 1.2
},
{
"id": 423,
"displayName": "Raw Mutton",
"stackSize": 64,
"name": "mutton",
"foodPoints": 2,
"saturation": 1.2,
"effectiveQuality": 3.2,
"saturationRatio": 0.6
},
{
"id": 424,
"displayName": "Cooked Mutton",
"stackSize": 64,
"name": "cooked_mutton",
"foodPoints": 6,
"saturation": 9.6,
"effectiveQuality": 15.6,
"saturationRatio": 1.5999999999999999
}
]
@@ -0,0 +1,22 @@
[
{
"id": 0,
"name": "harp"
},
{
"id": 1,
"name": "doubleBass"
},
{
"id": 2,
"name": "snareDrum"
},
{
"id": 3,
"name": "sticks"
},
{
"id": 4,
"name": "bassDrum"
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,61 @@
{
"rock": {
"257": 6,
"270": 2,
"274": 4,
"278": 8,
"285": 12
},
"wood": {
"258": 6,
"271": 2,
"275": 4,
"279": 8,
"286": 12
},
"plant": {
"258": 6,
"267": 1.5,
"268": 1.5,
"271": 2,
"272": 1.5,
"275": 4,
"276": 1.5,
"279": 8,
"283": 1.5,
"286": 12
},
"melon": {
"267": 1.5,
"268": 1.5,
"272": 1.5,
"276": 1.5,
"283": 1.5
},
"leaves": {
"267": 1.5,
"268": 1.5,
"272": 1.5,
"276": 1.5,
"283": 1.5,
"359": 6
},
"dirt": {
"256": 6,
"269": 2,
"273": 4,
"277": 8,
"284": 12
},
"web": {
"267": 15,
"268": 15,
"272": 15,
"276": 15,
"283": 15,
"359": 15
},
"wool": {
"359": 4.8
}
}
@@ -0,0 +1,170 @@
[
{
"id": 0,
"name": "explode"
},
{
"id": 1,
"name": "largeexplode"
},
{
"id": 2,
"name": "hugeexplosion"
},
{
"id": 3,
"name": "fireworksSpark"
},
{
"id": 4,
"name": "bubble"
},
{
"id": 5,
"name": "splash"
},
{
"id": 6,
"name": "wake"
},
{
"id": 7,
"name": "suspended"
},
{
"id": 8,
"name": "depthsuspend"
},
{
"id": 9,
"name": "crit"
},
{
"id": 10,
"name": "magicCrit"
},
{
"id": 11,
"name": "smoke"
},
{
"id": 12,
"name": "largesmoke"
},
{
"id": 13,
"name": "spell"
},
{
"id": 14,
"name": "instantSpell"
},
{
"id": 15,
"name": "mobSpell"
},
{
"id": 16,
"name": "mobSpellAmbient"
},
{
"id": 17,
"name": "witchMagic"
},
{
"id": 18,
"name": "dripWater"
},
{
"id": 19,
"name": "dripLava"
},
{
"id": 20,
"name": "angryVillager"
},
{
"id": 21,
"name": "happyVillager"
},
{
"id": 22,
"name": "townaura"
},
{
"id": 23,
"name": "note"
},
{
"id": 24,
"name": "portal"
},
{
"id": 25,
"name": "enchantmenttable"
},
{
"id": 26,
"name": "flame"
},
{
"id": 27,
"name": "lava"
},
{
"id": 28,
"name": "footstep"
},
{
"id": 29,
"name": "cloud"
},
{
"id": 30,
"name": "reddust"
},
{
"id": 31,
"name": "snowballpoof"
},
{
"id": 32,
"name": "snowshovel"
},
{
"id": 33,
"name": "slime"
},
{
"id": 34,
"name": "heart"
},
{
"id": 35,
"name": "barrier"
},
{
"id": 36,
"name": "iconcrack_"
},
{
"id": 37,
"name": "blockcrack_"
},
{
"id": 38,
"name": "blockdust_"
},
{
"id": 39,
"name": "droplet"
},
{
"id": 40,
"name": "take"
},
{
"id": 41,
"name": "mobappearance"
}
]
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":47,
"minecraftVersion":"1.8.8",
"majorVersion":"1.8"
}
+229
View File
@@ -0,0 +1,229 @@
[
{
"id": "",
"name": "Player",
"slots": [
{
"name": "craft result",
"index": 0
},
{
"name": "craft grid",
"index": 1,
"size": 4
},
{
"name": "armor",
"index": 5,
"size": 4
},
{
"name": "helmet",
"index": 5
},
{
"name": "chestplate",
"index": 6
},
{
"name": "leggings",
"index": 7
},
{
"name": "boots",
"index": 8
}
]
},
{
"id": "EntityHorse",
"name": "Horse",
"slots": [
{
"name": "saddle",
"index": 0
},
{
"name": "armor",
"index": 1
},
{
"name": "storage",
"index": 2,
"size": 15
}
],
"openedWith": [
{
"type": "entity",
"id": 100
}
]
},
{
"id": "minecraft:anvil",
"name": "Anvil",
"slots": [
{
"name": "tool",
"index": 0
},
{
"name": "combined",
"index": 1
},
{
"name": "result",
"index": 2
}
],
"properties": [
"repair cost"
]
},
{
"id": "minecraft:beacon",
"name": "Beacon",
"slots": [
{
"name": "input",
"index": 0
}
],
"properties": [
"level",
"effect 1",
"effect 2"
],
"openedWith": [
{
"type": "block",
"id": 138
}
]
},
{
"id": "minecraft:brewing_stand",
"name": "Brewing Stand",
"slots": [
{
"name": "ingredient",
"index": 3
},
{
"name": "result",
"index": 0,
"size": 3
}
],
"properties": [
"brew time"
],
"openedWith": [
{
"type": "block",
"id": 117
}
]
},
{
"id": "minecraft:chest",
"name": "Chest"
},
{
"id": "minecraft:container",
"name": "Container"
},
{
"id": "minecraft:crafting_table",
"name": "Workbench",
"slots": [
{
"name": "craft result",
"index": 0
},
{
"name": "craft grid",
"index": 1,
"size": 9
}
]
},
{
"id": "minecraft:dispenser",
"name": "Dispenser"
},
{
"id": "minecraft:dropper",
"name": "Dropper"
},
{
"id": "minecraft:enchanting_table",
"name": "Enchantment Table",
"slots": [
{
"name": "enchanted",
"index": 0
},
{
"name": "lapis",
"index": 1
}
],
"properties": [
"xp 1",
"xp 2",
"xp 3",
"seed",
"tooltip 1",
"tooltip 2",
"tooltip 3"
]
},
{
"id": "minecraft:furnace",
"name": "Furnace",
"slots": [
{
"name": "smelted",
"index": 0
},
{
"name": "fuel",
"index": 1
},
{
"name": "result",
"index": 2
}
],
"properties": [
"fuel",
"fuel max",
"progress",
"progress max"
]
},
{
"id": "minecraft:hopper",
"name": "Hopper"
},
{
"id": "minecraft:villager",
"name": "NPC Trade",
"slots": [
{
"name": "give 1",
"index": 0
},
{
"name": "give 2",
"index": 1
},
{
"name": "take",
"index": 2
}
]
}
]
+1
View File
@@ -346,6 +346,7 @@ func (c *Conn) Receive() (packet proto_base.EncodeDecodeAble, err error) {
} }
return return
} }
// NewConn creates a new Conn with default settings and a background context. // NewConn creates a new Conn with default settings and a background context.
func NewConn() (ret *Conn) { func NewConn() (ret *Conn) {
return NewConnCtx(context.Background()) return NewConnCtx(context.Background())
+1 -1
View File
@@ -113,7 +113,7 @@ func (a Actor) ReceiveDirection() Direction {
type ( type (
// ToDo is a placeholder type for unimplemented packet fields. // ToDo is a placeholder type for unimplemented packet fields.
ToDo struct{} ToDo struct{}
// RestBuffer represents the remaining unread bytes in a packet buffer. // RestBuffer represents the remaining unread bytes in a packet buffer.
RestBuffer []byte RestBuffer []byte
) )
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
+1 -1
View File
@@ -429,7 +429,7 @@ func GenerateVersions(versions []string) (err error) {
} }
func main() { func main() {
err := GenerateVersions([]string{"1.21.8", "1.21.9", "1.21.11", "26.1", "26.2"}) err := GenerateVersions([]string{"1.8", "1.21.8", "1.21.9", "1.21.11", "26.1", "26.2"})
if err != nil { if err != nil {
panic(err) panic(err)
} }
+10
View File
@@ -12,6 +12,10 @@ import (
) )
func (g *Generator) ParseCompareTo(compareTo string) (e ast.Expr, cet CaseExprType, err error) { func (g *Generator) ParseCompareTo(compareTo string) (e ast.Expr, cet CaseExprType, err error) {
if strings.HasPrefix(compareTo, "$") {
err = fmt.Errorf("parameterized type placeholder: %s", compareTo)
return
}
parts := strings.Split(compareTo, "/") parts := strings.Split(compareTo, "/")
downPrefixCount := 0 downPrefixCount := 0
for _, part := range parts { for _, part := range parts {
@@ -124,6 +128,12 @@ func (g *Generator) RegisterCompareToNatives() {
"bitfield": BitfieldCompareTo, "bitfield": BitfieldCompareTo,
"u8": ReturnInputCompareTo, "u8": ReturnInputCompareTo,
"i8": ReturnInputCompareTo, "i8": ReturnInputCompareTo,
"i16": ReturnInputCompareTo,
"i32": ReturnInputCompareTo,
"i64": ReturnInputCompareTo,
"u16": ReturnInputCompareTo,
"u32": ReturnInputCompareTo,
"string": ReturnInputCompareTo,
"bitflags": BitflagsCompareTo, "bitflags": BitflagsCompareTo,
"switch": ReturnInputCompareTo, "switch": ReturnInputCompareTo,
"varlong": ReturnInputCompareTo, "varlong": ReturnInputCompareTo,
+2
View File
@@ -634,6 +634,8 @@ func (g *Generator) RegisterDecoderNatives() {
"varlong": VarLongDecoder, "varlong": VarLongDecoder,
"anonymousNbt": DefaultDecoder, "anonymousNbt": DefaultDecoder,
"anonOptionalNbt": DefaultDecoder, // I have no idea what the difference is between these two "anonOptionalNbt": DefaultDecoder, // I have no idea what the difference is between these two
"optionalNbt": DefaultDecoder,
"nbt": DefaultDecoder,
"lpVec3": DefaultDecoder, "lpVec3": DefaultDecoder,
"UUID": UUIDDecoder, "UUID": UUIDDecoder,
"restBuffer": DefaultDecoder, "restBuffer": DefaultDecoder,
+6 -3
View File
@@ -11,15 +11,16 @@ import (
"github.com/go-viper/mapstructure/v2" "github.com/go-viper/mapstructure/v2"
) )
func DefaultEncoder(_ *Generator, varToSet ast.Expr, _ any, _ string) (s []ast.Stmt, err error) { func DefaultEncoder(_ *Generator, varToSet ast.Expr, _ any, name string) (s []ast.Stmt, err error) {
tmpName := name + "EncTmp"
s = Stmts( s = Stmts(
Define121(Ident(tmpName), varToSet),
Assign121( Assign121(
Ident("err"), Ident("err"),
Call(SelectorExprAndStr(varToSet, "Encode"), Ident("w")), Call(SelectorExprAndStr(Ident(tmpName), "Encode"), Ident("w")),
), ),
IfErrNil(), IfErrNil(),
) )
return return
} }
@@ -402,6 +403,8 @@ func (g *Generator) RegisterEncoderNatives() {
"varlong": VarLongEncoder, "varlong": VarLongEncoder,
"anonymousNbt": DefaultEncoder, "anonymousNbt": DefaultEncoder,
"anonOptionalNbt": DefaultEncoder, // I have no idea what the difference is between these two "anonOptionalNbt": DefaultEncoder, // I have no idea what the difference is between these two
"optionalNbt": DefaultEncoder,
"nbt": DefaultEncoder,
"UUID": UUIDEncoder, "UUID": UUIDEncoder,
"restBuffer": DefaultEncoder, "restBuffer": DefaultEncoder,
"lpVec3": DefaultEncoder, "lpVec3": DefaultEncoder,
+2
View File
@@ -200,6 +200,8 @@ func (g *Generator) RegisterNatives() {
"native": VisitDontGenerateType, "native": VisitDontGenerateType,
"anonymousNbt": MakeSelectorVisitor("nbt", "Anon"), "anonymousNbt": MakeSelectorVisitor("nbt", "Anon"),
"anonOptionalNbt": MakeSelectorVisitor("nbt", "Anon"), // I have no idea what the difference is between these two "anonOptionalNbt": MakeSelectorVisitor("nbt", "Anon"), // I have no idea what the difference is between these two
"optionalNbt": MakeSelectorVisitor("nbt", "Tag"),
"nbt": MakeSelectorVisitor("nbt", "Tag"),
"void": VoidType, "void": VoidType,
"bool": MakeIdentVisitor("bool"), "bool": MakeIdentVisitor("bool"),
"u8": MakeIdentVisitor("uint8"), "u8": MakeIdentVisitor("uint8"),
+9 -7
View File
@@ -27,13 +27,15 @@ func ServeConfig(c *proto.Conn) (err error) {
} }
cs.registryData = n.Value.(map[string]any) cs.registryData = n.Value.(map[string]any)
packet := &v1_21_8.ConfigurationToClientPacketCommonSelectKnownPacks{} packet := &v1_21_8.ConfigurationToClientPacketCommonSelectKnownPacks{}
for _, pAny := range cs.registryData["KnownPacks"].([]any) { if knownPacks, ok := cs.registryData["KnownPacks"]; ok {
p := pAny.(map[string]any) for _, pAny := range knownPacks.([]any) {
packet.Packs = append(packet.Packs, struct { p := pAny.(map[string]any)
Namespace string packet.Packs = append(packet.Packs, struct {
Id string Namespace string
Version string Id string
}{p["Namespace"].(string), p["Id"].(string), p["Version"].(string)}) Version string
}{p["Namespace"].(string), p["Id"].(string), p["Version"].(string)})
}
} }
err = c.Send(packet) err = c.Send(packet)
if err != nil { if err != nil {