Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acb5fe8d90 | ||
|
|
d9bca7e517 | ||
|
|
b162d0d401 | ||
|
|
3273244c42 | ||
|
|
f579645b88 | ||
|
|
33754d7c71 | ||
|
|
8bd54a04f2 | ||
|
|
492dae8e83 | ||
|
|
d9543de951 | ||
|
|
f1f5374a5c | ||
|
|
0d6b6e1ab9 | ||
|
|
56c97a2b3f | ||
|
|
9063a0328d | ||
|
|
84b28d4c12 | ||
|
|
30cfc507ba | ||
|
|
d84618c116 | ||
|
|
5a55520126 | ||
|
|
8ac5e00c8e | ||
|
|
d46c678b89 | ||
|
|
0b714edcf5 | ||
|
|
45151add64 | ||
|
|
b90830881f | ||
|
|
94301a4a50 | ||
|
|
aa5d15a4dc | ||
|
|
d388e2c350 | ||
|
|
126ff5bca0 | ||
|
|
a347aedfc2 | ||
|
|
e324a95727 | ||
|
|
7f878f40a1 | ||
|
|
7baa9990cb | ||
|
|
5c5c391aa6 | ||
|
|
b966925212 | ||
|
|
efd7049859 | ||
|
|
3ef9446f50 | ||
|
|
68612969c9 | ||
|
|
b4712ff5c6 | ||
|
|
eab050e291 | ||
|
|
a8b090227b | ||
|
|
201e0dfa44 | ||
|
|
666f250b3e | ||
|
|
b2784574bd | ||
|
|
c0b1501f23 | ||
|
|
4de76d8023 | ||
|
|
f08684fb67 | ||
|
|
9e4f9409c5 | ||
|
|
4d3fbed957 | ||
|
|
b338888b58 | ||
|
|
4956c1d874 | ||
|
|
d4fe362d9f | ||
|
|
5d8ff870df | ||
|
|
58c9a60236 | ||
|
|
3e5b141ec7 | ||
|
|
efeb5ecda6 | ||
|
|
624e8ad4de | ||
|
|
764cfd8c4d | ||
|
|
d43ff7f83d | ||
|
|
a39999b344 | ||
|
|
8ee48e1a45 | ||
|
|
bf6affeb97 |
@@ -1,5 +1,4 @@
|
||||
.devres/
|
||||
.idea/
|
||||
.failed_packets/
|
||||
tools/.*/
|
||||
.vscode/
|
||||
@@ -1,10 +0,0 @@
|
||||
package api
|
||||
|
||||
import "github.com/admin-else/strom/crypto"
|
||||
|
||||
func NewOfflineAccount(name string) *Account {
|
||||
return &Account{
|
||||
Name: name,
|
||||
Uuid: crypto.FromOfflinePlayer(name),
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/admin-else/strom/proto"
|
||||
"github.com/admin-else/strom/proto_base"
|
||||
)
|
||||
|
||||
// Connect connects to the given address note that it does not resolve the SRV records it's just a raw net.Dial
|
||||
// You must close the connection when you're done with it
|
||||
func Connect(addr string) (ret *proto.Conn, err error) {
|
||||
s, err := Status(addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ret, err = ConnectVersionLess(addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = ret.SetProtocolVersion(s.Version.Protocol)
|
||||
return
|
||||
}
|
||||
|
||||
func ConnectVersionLess(addr string) (ret *proto.Conn, err error) {
|
||||
return ConnectVersionLessCtx(context.Background(), addr)
|
||||
}
|
||||
|
||||
// ConnectVersionLess connects to the given address without setting the version you should set it
|
||||
// when using this function.
|
||||
// You must close the connection when you're done with it
|
||||
func ConnectVersionLessCtx(ctx context.Context, addr string) (ret *proto.Conn, err error) {
|
||||
ret = proto.NewConnCtx(ctx)
|
||||
ret.Actor = proto_base.Client
|
||||
ret.Conn, err = (&net.Dialer{}).DialContext(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ret.R = ret.Conn
|
||||
ret.W = ret.Conn
|
||||
return
|
||||
}
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DNSCheckFunc func(addr string) error
|
||||
|
||||
// DoDns resolves a DNS name to an IP address
|
||||
// It should resolve a minecraft compatible address into something net.Dial
|
||||
func DoDns(in string) (out string, host string, portN uint16, err error) {
|
||||
return DoDNSChecked(in, nil)
|
||||
}
|
||||
|
||||
// DoDNSChecked resolves a DNS name to an IP address
|
||||
// It should resolve a minecraft compatible address into something net.Dial
|
||||
// It also optionally accepts a function to "check" the address
|
||||
func DoDNSChecked(in string, f DNSCheckFunc) (connectTo string, host string, portN uint16, err error) {
|
||||
// https://mcsrc.dev/1/26.1-pre-1/net/minecraft/client/multiplayer/resolver/ServerAddress#L34
|
||||
host, port, err := net.SplitHostPort(in)
|
||||
if err != nil {
|
||||
var addrErr *net.AddrError
|
||||
if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
|
||||
err = nil
|
||||
host = in
|
||||
port = "25565"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
porti, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if f == nil {
|
||||
f = func(addr string) error { return nil }
|
||||
}
|
||||
|
||||
err = f(host)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
portN = uint16(porti)
|
||||
outIP, err := net.ResolveIPAddr("ip", host)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = f(outIP.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
connectTo = outIP.String() + ":" + port
|
||||
if portN != 25565 {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // otherwise this takes 10+ seconds
|
||||
defer cancel()
|
||||
|
||||
r := &net.Resolver{}
|
||||
|
||||
_, srvs, _ := r.LookupSRV(ctx, "minecraft", "tcp", host)
|
||||
if len(srvs) == 0 {
|
||||
|
||||
return
|
||||
}
|
||||
host = strings.TrimSuffix(srvs[0].Target, ".")
|
||||
portN = srvs[0].Port
|
||||
outIP, err = net.ResolveIPAddr("ip", host)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = f(outIP.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
connectTo = outIP.String() + ":" + port
|
||||
return
|
||||
}
|
||||
|
||||
// DoDnsSimple resolves a DNS name to an IP address
|
||||
// It should resolve a minecraft compatible address into something net.Dial
|
||||
// It does not check the address
|
||||
func DoDnsSimple(in string) (out string, err error) {
|
||||
host, port, err := net.SplitHostPort(in)
|
||||
if err != nil {
|
||||
var addrErr *net.AddrError
|
||||
if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
|
||||
err = nil
|
||||
host = in
|
||||
port = "25565"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
porti, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
portN := uint16(porti)
|
||||
_, err = net.ResolveIPAddr("ip", host)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out = host + ":" + strconv.Itoa(int(portN))
|
||||
if portN != 25565 {
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // otherwise this takes 10+ seconds
|
||||
defer cancel()
|
||||
|
||||
r := &net.Resolver{}
|
||||
|
||||
_, srvs, _ := r.LookupSRV(ctx, "minecraft", "tcp", host)
|
||||
if len(srvs) == 0 {
|
||||
return
|
||||
}
|
||||
host = strings.TrimSuffix(srvs[0].Target, ".")
|
||||
portN = srvs[0].Port
|
||||
out = host + ":" + strconv.Itoa(int(portN))
|
||||
return
|
||||
}
|
||||
-179
@@ -1,179 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/admin-else/strom/api"
|
||||
"github.com/admin-else/strom/crypto"
|
||||
"github.com/admin-else/strom/event"
|
||||
"github.com/admin-else/strom/proto"
|
||||
"github.com/admin-else/strom/proto_base"
|
||||
"github.com/admin-else/strom/text"
|
||||
|
||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||
)
|
||||
|
||||
type LoginClient struct {
|
||||
*proto.Conn
|
||||
Account *api.Account
|
||||
ServerHost string
|
||||
ServerPort uint16
|
||||
|
||||
GivenAccount *v1_21_8.LoginToClientPacketSuccess
|
||||
}
|
||||
|
||||
var (
|
||||
NoTokenErr = errors.New("the account has no token so we cant join online servers")
|
||||
BadPublicKeyTypeErr = errors.New("the public key is not rsa")
|
||||
FailedToLoginErr = errors.New("failed to login")
|
||||
)
|
||||
|
||||
type KickedDuringLoginErr struct {
|
||||
text.Component
|
||||
}
|
||||
|
||||
func (k KickedDuringLoginErr) Error() string {
|
||||
return k.String()
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnDefault(event event.Unhandled) (err error) {
|
||||
if _, ok := event.Val.(proto_base.EncodeDecodeAble); !ok {
|
||||
return
|
||||
}
|
||||
s.Log.Warn("unexpected packet during login client", "packet", event.Val)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnClose(_ event.Close) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnCompress(compress *v1_21_8.LoginToClientPacketCompress) (err error) {
|
||||
s.SetCompressionThreshold(compress.Threshold)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnEncrypt(packet *v1_21_8.LoginToClientPacketEncryptionBegin) (err error) {
|
||||
sharedSecret := make([]byte, 16)
|
||||
_, _ = rand.Read(sharedSecret) //never fails
|
||||
|
||||
if packet.ShouldAuthenticate {
|
||||
if s.Account.Ygg == "" {
|
||||
err = NoTokenErr
|
||||
return
|
||||
}
|
||||
serverId := crypto.AuthDigest([]byte(packet.ServerId), sharedSecret, packet.PublicKey)
|
||||
err = s.Account.JoinServer(serverId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pubAny, err := x509.ParsePKIXPublicKey(packet.PublicKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
pub, ok := pubAny.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
err = BadPublicKeyTypeErr
|
||||
return
|
||||
}
|
||||
verifyTokenEnc, err := rsa.EncryptPKCS1v15(rand.Reader, pub, packet.VerifyToken)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sharedSecretEnc, err := rsa.EncryptPKCS1v15(rand.Reader, pub, sharedSecret)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = s.Send(&v1_21_8.LoginToServerPacketEncryptionBegin{SharedSecret: sharedSecretEnc, VerifyToken: verifyTokenEnc})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = s.SetSecret(sharedSecret)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnDisconnect(packet *v1_21_8.LoginToClientPacketDisconnect) (err error) {
|
||||
var reason text.Component
|
||||
err = json.Unmarshal([]byte(packet.Reason), &reason)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = KickedDuringLoginErr{reason}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnSuccess(success *v1_21_8.LoginToClientPacketSuccess) (err error) {
|
||||
s.GivenAccount = success
|
||||
err = s.Send(&v1_21_8.LoginToServerPacketLoginAcknowledged{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.SetState(proto_base.Configuration)
|
||||
err = event.HandlerDoneErr{}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *LoginClient) OnStart() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func LoginRaw(c *proto.Conn, account *api.Account) (err error) {
|
||||
lc := &LoginClient{
|
||||
Conn: c,
|
||||
Account: account,
|
||||
}
|
||||
lc.RegisterCritical(lc.OnDefault)
|
||||
lc.RegisterCritical(lc.OnClose)
|
||||
lc.RegisterCriticalUntilLatest(lc.OnCompress)
|
||||
lc.RegisterCriticalUntilLatest(lc.OnEncrypt)
|
||||
lc.RegisterCriticalUntilLatest(lc.OnDisconnect)
|
||||
lc.RegisterCriticalUntilLatest(lc.OnSuccess)
|
||||
|
||||
p, err := MakeHandshakePacket(lc.Conn, proto_base.Login)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = lc.Send(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
lc.SetState(proto_base.Login)
|
||||
err = lc.Send(&v1_21_8.LoginToServerPacketLoginStart{Username: lc.Account.Name, PlayerUUID: lc.Account.Uuid})
|
||||
|
||||
err = lc.StartConn()
|
||||
if err != nil {
|
||||
err = errors.Join(FailedToLoginErr, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func LoginNoSrv(connectTo string, account *api.Account) (c *proto.Conn, err error) {
|
||||
c, err = Connect(connectTo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = LoginRaw(c, account)
|
||||
return
|
||||
}
|
||||
|
||||
func Login(connectTo string, account *api.Account) (c *proto.Conn, err error) {
|
||||
connectTo, err = DoDnsSimple(connectTo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c, err = Connect(connectTo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = LoginRaw(c, account)
|
||||
return
|
||||
}
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/admin-else/strom/api"
|
||||
"github.com/admin-else/strom/mc/api"
|
||||
)
|
||||
|
||||
var cmd = flag.NewFlagSet("api", flag.ContinueOnError)
|
||||
@@ -52,7 +53,7 @@ func Run(args []string) (err error) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = PleaseProvideArgumentErr
|
||||
slog.Info("account", "name", a.Name, "uuid", a.Uuid)
|
||||
return
|
||||
}
|
||||
err = json.NewEncoder(os.Stdout).Encode(data)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package cmd_util
|
||||
|
||||
import "github.com/admin-else/strom/mc/api"
|
||||
|
||||
func Account(s string) (a *api.Account, err error) {
|
||||
if len(s) > 16 {
|
||||
return api.NewAccountFromYGG(s)
|
||||
}
|
||||
a = api.NewOfflineAccount(s)
|
||||
return
|
||||
}
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"github.com/admin-else/strom/data"
|
||||
"github.com/admin-else/strom/proto_generated"
|
||||
"github.com/admin-else/strom/util"
|
||||
mcdata "github.com/admin-else/strom/mc/data"
|
||||
"github.com/admin-else/strom/mc/proto_generated"
|
||||
"github.com/admin-else/strom/mc/util"
|
||||
)
|
||||
|
||||
// strom data 1.21.11 blocks id 0 name => air
|
||||
@@ -39,11 +39,11 @@ func Run(args []string) (err error) {
|
||||
case 0: // print available versions
|
||||
dataValues = proto_generated.SupportedVersions
|
||||
case 1: // print available data for version
|
||||
dataValues = slices.Collect(maps.Keys(data.Paths.Data[args[0]]))
|
||||
dataValues = slices.Collect(maps.Keys(mcdata.Paths.Data[args[0]]))
|
||||
case 2:
|
||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||
case 3:
|
||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func Run(args []string) (err error) {
|
||||
}
|
||||
dataValues = result
|
||||
case 4, 5:
|
||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/admin-else/strom/level/anvil"
|
||||
"github.com/admin-else/strom/nbt"
|
||||
"github.com/admin-else/strom/mc/level/anvil"
|
||||
"github.com/admin-else/strom/mc/nbt"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
+43
-20
@@ -1,52 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/buildinfo"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
|
||||
"github.com/admin-else/strom/cmd/strom/api"
|
||||
"github.com/admin-else/strom/cmd/strom/data"
|
||||
"github.com/admin-else/strom/cmd/strom/extract_mca"
|
||||
"github.com/admin-else/strom/cmd/strom/mca_to_tmcpr"
|
||||
"github.com/admin-else/strom/cmd/strom/messenger"
|
||||
"github.com/admin-else/strom/cmd/strom/offline_uuid"
|
||||
"github.com/admin-else/strom/cmd/strom/packet_info"
|
||||
"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_replay"
|
||||
// "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/serve_tmcpr"
|
||||
"github.com/admin-else/strom/cmd/strom/serve_world"
|
||||
"github.com/admin-else/strom/cmd/strom/status"
|
||||
)
|
||||
|
||||
var ExpectedASubcommandErr = errors.New("expected a subcommand")
|
||||
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
|
||||
|
||||
func Version(args []string) (err error) {
|
||||
selfPath, err := os.Executable()
|
||||
if err != nil {
|
||||
return
|
||||
func ToolVersion() string {
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return "devel"
|
||||
}
|
||||
info, err := buildinfo.ReadFile(selfPath)
|
||||
if err != nil {
|
||||
return
|
||||
return info.Main.Version
|
||||
}
|
||||
|
||||
func ToolSum() string {
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
fmt.Println("strom cli ", info.Main.Version, " (", info.Main.Sum, ")")
|
||||
return
|
||||
return info.Main.Sum
|
||||
}
|
||||
|
||||
func Version(args []string) error {
|
||||
fmt.Println("strom cli ", ToolVersion(), " (", ToolSum(), ")")
|
||||
return nil
|
||||
}
|
||||
|
||||
var subcommands = map[string]func(args []string) error{
|
||||
"offline-uuid": offline_uuid.Run,
|
||||
"packet-info": packet_info.Run,
|
||||
"print-nbt": print_nbt.Run,
|
||||
"status": status.Run,
|
||||
"packet-spy": packet_inspector.Run,
|
||||
"api": api.Run,
|
||||
"version": Version,
|
||||
"extract-mca": extract_mca.Run,
|
||||
"data": data.Run,
|
||||
"print-replay": print_replay.Run,
|
||||
"offline-uuid": offline_uuid.Run,
|
||||
"packet-info": packet_info.Run,
|
||||
"print-nbt": print_nbt.Run,
|
||||
"status": status.Run,
|
||||
"packet-spy": packet_inspector.Run,
|
||||
"api": api.Run,
|
||||
"version": Version,
|
||||
"extract-mca": extract_mca.Run,
|
||||
"data": data.Run,
|
||||
"print-replay": print_replay.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,
|
||||
"serve-world": serve_world.Run,
|
||||
"mca-to-tmcpr": mca_to_tmcpr.Run,
|
||||
"serve-tmcpr": serve_tmcpr.Run,
|
||||
"messenger": messenger.Run,
|
||||
}
|
||||
|
||||
func Help(args []string) (err error) {
|
||||
@@ -59,6 +81,7 @@ func Help(args []string) (err error) {
|
||||
func mainE() (err error) {
|
||||
subcommands["help"] = Help
|
||||
args := os.Args
|
||||
//args = []string{"TEST", "messenger", "-addr", "localhost"}
|
||||
|
||||
if len(args) < 2 {
|
||||
err = ExpectedASubcommandErr
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
package mca_to_tmcpr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/admin-else/strom/mc/level"
|
||||
"github.com/admin-else/strom/mc/level/anvil"
|
||||
"github.com/admin-else/strom/mc/nbt"
|
||||
"github.com/admin-else/strom/mc/proto/replay"
|
||||
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
|
||||
"github.com/admin-else/strom/mc/util"
|
||||
)
|
||||
|
||||
var (
|
||||
cmd = flag.NewFlagSet("mca-to-tmcpr", flag.ContinueOnError)
|
||||
worldPath = cmd.String("world", "", "Path to world directory")
|
||||
output = cmd.String("output", "", "Output .tmcpr file path")
|
||||
radius = cmd.Int("radius", 14, "Chunk radius around spawn")
|
||||
spawnX = cmd.Float64("spawn-x", 0.5, "Spawn X coordinate")
|
||||
spawnY = cmd.Float64("spawn-y", 100, "Spawn Y coordinate")
|
||||
spawnZ = cmd.Float64("spawn-z", 0.5, "Spawn Z coordinate")
|
||||
)
|
||||
|
||||
var FullLight [][]uint8
|
||||
|
||||
func init() {
|
||||
light := make([]uint8, 2048)
|
||||
for i := range light {
|
||||
light[i] = 0xFF
|
||||
}
|
||||
sectionsPerChunk := 384 / 16
|
||||
FullLight = make([][]uint8, sectionsPerChunk)
|
||||
for i := range sectionsPerChunk {
|
||||
FullLight[i] = light
|
||||
}
|
||||
}
|
||||
|
||||
func GetChunkFromMca(x, z int32, version string) (b []byte, err error) {
|
||||
xmca := util.FloorDiv(x, anvil.ChunksWidthRegionFile)
|
||||
zmca := util.FloorDiv(z, anvil.ChunksWidthRegionFile)
|
||||
x = x % anvil.ChunksWidthRegionFile
|
||||
if x < 0 {
|
||||
x += anvil.ChunksWidthRegionFile
|
||||
}
|
||||
z = z % anvil.ChunksWidthRegionFile
|
||||
if z < 0 {
|
||||
z += anvil.ChunksWidthRegionFile
|
||||
}
|
||||
|
||||
f, err := os.Open(fmt.Sprintf("./.devres/anvil_testdata/securechattestworld/region/r.%v.%v.mca", xmca, zmca))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
mca, err := anvil.ReadChunkHeader(f)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var n *nbt.Tag
|
||||
n, err = mca.ChunkAt(f, x, z)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var chunk anvil.Chunk
|
||||
err = nbt.Format.Decode(n.Value, &chunk)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var packetChunk *level.Chunk
|
||||
packetChunk, err = chunk.ToStorage(version)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
err = packetChunk.WriteChunkData(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
b = buffer.Bytes()
|
||||
return
|
||||
}
|
||||
|
||||
func Run(args []string) error {
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
|
||||
err := cmd.Parse(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *worldPath == "" {
|
||||
slog.Error("no world path specified")
|
||||
return nil
|
||||
}
|
||||
if *output == "" {
|
||||
slog.Error("no output path specified")
|
||||
return nil
|
||||
}
|
||||
|
||||
outFile, err := os.Create(*output)
|
||||
if err != nil {
|
||||
slog.Error("failed to create output file", "error", err)
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
w := replay.NewWriter(outFile)
|
||||
|
||||
err = w.WritePacket(&v1_21_11.PlayToClientPacketLogin{
|
||||
EntityId: 73,
|
||||
IsHardcore: false,
|
||||
WorldNames: []string{"minecraft:overworld", "minecraft:the_end", "minecraft:the_nether"},
|
||||
MaxPlayers: 100,
|
||||
ViewDistance: 10,
|
||||
SimulationDistance: 10,
|
||||
ReducedDebugInfo: false,
|
||||
EnableRespawnScreen: true,
|
||||
DoLimitedCrafting: false,
|
||||
WorldState: v1_21_11.PlayToClientSpawnInfo{
|
||||
Dimension: 0,
|
||||
Name: "minecraft:overworld",
|
||||
HashedSeed: -8365436745629239539,
|
||||
Gamemode: "survival",
|
||||
PreviousGamemode: 0xff,
|
||||
IsDebug: false,
|
||||
IsFlat: false,
|
||||
Death: nil,
|
||||
PortalCooldown: 0,
|
||||
SeaLevel: 63,
|
||||
},
|
||||
EnforcesSecureChat: false,
|
||||
}, 0)
|
||||
if err != nil {
|
||||
slog.Error("failed to write login packet", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.WritePacket(&v1_21_11.PlayToClientPacketPosition{
|
||||
TeleportId: 0,
|
||||
X: *spawnX,
|
||||
Y: *spawnY,
|
||||
Z: *spawnZ,
|
||||
Dx: 0,
|
||||
Dy: 0,
|
||||
Dz: 0,
|
||||
Yaw: 0,
|
||||
Pitch: 0,
|
||||
Flags: v1_21_11.PlayToClientPositionUpdateRelatives{},
|
||||
}, 0)
|
||||
if err != nil {
|
||||
slog.Error("failed to write position packet", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
chunkX := int32(*spawnX) >> 4
|
||||
chunkZ := int32(*spawnZ) >> 4
|
||||
|
||||
err = w.WritePacket(&v1_21_11.PlayToClientPacketUpdateViewPosition{
|
||||
ChunkX: chunkX,
|
||||
ChunkZ: chunkZ,
|
||||
}, 0)
|
||||
if err != nil {
|
||||
slog.Error("failed to write view position packet", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.WritePacket(&v1_21_11.PlayToClientPacketGameStateChange{
|
||||
Reason: "level_chunks_load_start",
|
||||
}, 0)
|
||||
if err != nil {
|
||||
slog.Error("failed to write game state packet", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
radius32 := int32(*radius)
|
||||
|
||||
for x := -radius32; x < radius32; x++ {
|
||||
for z := -radius32; z < radius32; z++ {
|
||||
var chunkData []byte
|
||||
chunkData, err = GetChunkFromMca(x, z, "1.21.11")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if chunkData == nil {
|
||||
continue
|
||||
}
|
||||
OLDpacket := &v1_21_11.PlayToClientPacketMapChunk{}
|
||||
OLDpacket.X = x
|
||||
OLDpacket.Z = z
|
||||
OLDpacket.ChunkData = v1_21_11.ByteArray{Val: chunkData}
|
||||
OLDpacket.BlockLight = FullLight
|
||||
OLDpacket.BlockLightMask = []int64{(1 << 24) - 1}
|
||||
err = w.WritePacket(OLDpacket, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info("conversion complete", "output", *output)
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package messenger
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/admin-else/strom/cmd/strom/cmd_util"
|
||||
"github.com/admin-else/strom/mc/bot/chat"
|
||||
"github.com/admin-else/strom/mc/bot/keepalive"
|
||||
"github.com/admin-else/strom/mc/bot/world"
|
||||
"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_generated/v1_21_11"
|
||||
"github.com/admin-else/strom/mc/proto_generated/v1_8"
|
||||
)
|
||||
|
||||
func stror(a, b string) string {
|
||||
if a != "" {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
var (
|
||||
cmd = flag.NewFlagSet("status", flag.ContinueOnError)
|
||||
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")
|
||||
versionFlag = cmd.String("version", "26.2", "minecraft version to use (e.g. 1.8, 1.21.11)")
|
||||
)
|
||||
|
||||
type Messenger struct {
|
||||
*proto.Conn
|
||||
chat *chat.Module
|
||||
world *world.Module
|
||||
legacy bool
|
||||
}
|
||||
|
||||
func (m *Messenger) OnChat(e *v1_21_11.PlayToClientPacketPlayerChat) (err error) {
|
||||
m.Log.Info("chat", "author", e.SenderUuid, "m", e.PlainMessage)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Messenger) OnChatUnsigned(e *v1_21_11.PlayToClientPacketProfilelessChat) (err error) {
|
||||
m.Log.Info("chatP", "author", e.Name, "m", e.Message)
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Messenger) OnChatSystem(e *v1_21_11.PlayToClientPacketSystemChat) (err error) {
|
||||
m.Log.Info("chatS", "m", e.Content)
|
||||
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) {
|
||||
input := strings.TrimSpace(e.Val)
|
||||
if strings.HasPrefix(input, ">say ") {
|
||||
return m.sendChat(strings.TrimSpace(strings.TrimPrefix(input, ">say")))
|
||||
}
|
||||
if !m.legacy && strings.HasPrefix(input, ">getblock ") {
|
||||
return m.handleGetBlock(strings.TrimSpace(strings.TrimPrefix(input, ">getblock")))
|
||||
}
|
||||
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) {
|
||||
fields := strings.Fields(args)
|
||||
if len(fields) != 3 {
|
||||
m.Log.Info("getblock", "error", "expected >getblock <x> <y> <z>")
|
||||
return nil
|
||||
}
|
||||
|
||||
coords := make([]int64, 3)
|
||||
for i, f := range fields {
|
||||
coords[i], err = strconv.ParseInt(f, 10, 32)
|
||||
if err != nil {
|
||||
m.Log.Info("getblock", "error", fmt.Sprintf("bad coordinate %q", f))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
stateId, err := m.world.GetBlock(int32(coords[0]), int32(coords[1]), int32(coords[2]))
|
||||
if err != nil {
|
||||
m.Log.Info("getblock", "error", err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
block, ok := data.LookupBlockByStateId(m.Version, stateId)
|
||||
if !ok {
|
||||
m.Log.Info("getblock", "stateId", stateId, "name", "unknown")
|
||||
return nil
|
||||
}
|
||||
|
||||
m.Log.Info("getblock", "x", coords[0], "y", coords[1], "z", coords[2], "name", block.Name, "stateId", stateId)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Run(args []string) (err error) {
|
||||
err = cmd.Parse(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
acc, err := cmd_util.Account(*accFlag)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c, err := client.Login(*connectToFlag, acc, client.WithVersion(*versionFlag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
isLegacy := c.ProtocolVersion < 764
|
||||
|
||||
var chatMod *chat.Module
|
||||
if !isLegacy {
|
||||
chatMod, err = chat.Start(c, acc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
m := &Messenger{
|
||||
Conn: c,
|
||||
chat: chatMod,
|
||||
legacy: isLegacy,
|
||||
}
|
||||
|
||||
if !isLegacy {
|
||||
w := world.NewWorld(c.Version, -64, 384)
|
||||
m.world = world.Start(c, w)
|
||||
}
|
||||
|
||||
event.StartListingStdin(m.Loop)
|
||||
m.Register(m.OnStdin)
|
||||
|
||||
if isLegacy {
|
||||
m.Register(m.OnChatLegacy)
|
||||
} else {
|
||||
m.RegisterUntilLatest(m.OnChat, m.OnChatUnsigned, m.OnChatSystem)
|
||||
}
|
||||
|
||||
if !isLegacy {
|
||||
keepalive.Start(m.Conn)
|
||||
}
|
||||
|
||||
return m.StartConn()
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/admin-else/strom/crypto"
|
||||
"github.com/admin-else/strom/mc/crypto"
|
||||
)
|
||||
|
||||
var cmd = flag.NewFlagSet("offline-uuid", flag.ContinueOnError)
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/admin-else/strom/data"
|
||||
"github.com/admin-else/strom/proto"
|
||||
"github.com/admin-else/strom/proto_base"
|
||||
"github.com/admin-else/strom/proto_generated"
|
||||
"github.com/admin-else/strom/mc/data"
|
||||
"github.com/admin-else/strom/mc/proto"
|
||||
"github.com/admin-else/strom/mc/proto_base"
|
||||
"github.com/admin-else/strom/mc/proto_generated"
|
||||
)
|
||||
|
||||
var cmd = flag.NewFlagSet("packet-info", flag.ContinueOnError)
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
package packet_inspector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"log/slog"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/admin-else/strom/api"
|
||||
"github.com/admin-else/strom/client"
|
||||
"github.com/admin-else/strom/event"
|
||||
"github.com/admin-else/strom/nbt"
|
||||
"github.com/admin-else/strom/proto"
|
||||
"github.com/admin-else/strom/proto_base"
|
||||
"github.com/admin-else/strom/proto_generated/v1_21_11"
|
||||
"github.com/admin-else/strom/server"
|
||||
"github.com/admin-else/strom/text"
|
||||
"github.com/admin-else/strom/mc/api"
|
||||
"github.com/admin-else/strom/mc/client"
|
||||
"github.com/admin-else/strom/mc/event"
|
||||
"github.com/admin-else/strom/mc/nbt"
|
||||
"github.com/admin-else/strom/mc/proto"
|
||||
"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("packet-inspector", flag.ContinueOnError)
|
||||
@@ -22,6 +25,7 @@ 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 TrueForward = Cmd.Bool("true-forward", false, "forward packets without modification -name and custom server status wont work")
|
||||
|
||||
type Proxy struct {
|
||||
Client, Servee *proto.Conn
|
||||
@@ -33,6 +37,10 @@ func (p *Proxy) Start() (err error) {
|
||||
p.Servee.RegisterCritical(p.OnAnything)
|
||||
|
||||
p.Servee.RegisterCriticalUntilLatest(p.OnFinishConfiguration)
|
||||
p.Servee.RegisterCriticalUntilLatest(p.OnFinishLogin)
|
||||
p.Servee.RegisterCriticalUntilLatest(p.OnHandshake)
|
||||
//p.Servee.RegisterCriticalUntilLatest(p.OnCompress)
|
||||
p.Client.RegisterCriticalUntilLatest(p.OnCompress)
|
||||
|
||||
errChan := make(chan error)
|
||||
go func() {
|
||||
@@ -45,6 +53,22 @@ func (p *Proxy) Start() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type PacketLogEvent struct {
|
||||
Time time.Time
|
||||
Packet proto_base.EncodeDecodeAble
|
||||
Info PacketInfoNoTypeJson
|
||||
ClientAddr string
|
||||
}
|
||||
|
||||
type PacketInfoNoTypeJson struct {
|
||||
Type proto_base.EncodeDecodeAble `json:"-"`
|
||||
Name string
|
||||
Direction proto_base.Direction
|
||||
State proto_base.State
|
||||
PacketId int32
|
||||
ProtocolVersion int32
|
||||
}
|
||||
|
||||
func (p *Proxy) OnAnything(e event.Anything) (err error) {
|
||||
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
|
||||
if !ok {
|
||||
@@ -54,6 +78,19 @@ func (p *Proxy) OnAnything(e event.Anything) (err error) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
jsonBytes, err := json.Marshal(PacketLogEvent{
|
||||
Time: time.Now(),
|
||||
Packet: packet,
|
||||
ClientAddr: p.Client.RemoteAddr().String(),
|
||||
Info: PacketInfoNoTypeJson(packetInfo),
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// :pray:
|
||||
_, _ = os.Stdout.Write(jsonBytes)
|
||||
_, _ = os.Stdout.Write([]byte("\n"))
|
||||
|
||||
switch packetInfo.Direction {
|
||||
case proto_base.ToServer:
|
||||
err = p.Client.Send(packet)
|
||||
@@ -74,6 +111,40 @@ func (p *Proxy) OnFinishConfiguration(packet *v1_21_11.ConfigurationToServerPack
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Proxy) OnFinishLogin(packet *v1_21_11.LoginToServerPacketLoginAcknowledged) (err error) {
|
||||
err = p.Client.Send(packet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.Servee.SetState(proto_base.Configuration)
|
||||
p.Client.SetState(proto_base.Configuration)
|
||||
err = event.DontForwardErr
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Proxy) OnHandshake(packet *v1_21_11.HandshakingToServerPacketSetProtocol) (err error) {
|
||||
err = p.Client.Send(packet)
|
||||
state := proto_base.State(packet.NextState)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.Servee.SetState(state)
|
||||
p.Client.SetState(state)
|
||||
err = event.DontForwardErr
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Proxy) OnCompress(packet *v1_21_11.LoginToClientPacketCompress) (err error) {
|
||||
err = p.Servee.Send(packet)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.Servee.SetCompressionThreshold(packet.Threshold)
|
||||
p.Client.SetCompressionThreshold(packet.Threshold)
|
||||
err = event.DontForwardErr
|
||||
return
|
||||
}
|
||||
|
||||
var StatusResponse = server.StatusResponse{
|
||||
Version: server.StatusResponseVersion{
|
||||
Name: text.Pretty("STROM"),
|
||||
@@ -90,38 +161,41 @@ func Run(args []string) (err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
slog.Info("Starting packet inspector", "listen", *ListenAddr)
|
||||
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
|
||||
serveeConn.DebugPrintPackets = []string{""}
|
||||
p := &Proxy{Servee: serveeConn, Client: nil}
|
||||
var acc = api.NewOfflineAccount(*OfflineNameFlag)
|
||||
if *Token != "" {
|
||||
acc, err = api.NewAccountFromYGG(*Token)
|
||||
if !*TrueForward {
|
||||
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
|
||||
}
|
||||
}
|
||||
_, err = server.ServeLogin(serveeConn, server.WithOtherAccount(acc), server.WithStatus(StatusResponse), server.WithoutOnlineMode())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
*TargetAddr, err = client.DoDnsSimple(*TargetAddr)
|
||||
*TargetAddr, _, _, err = client.DoDNSChecked(nil, *TargetAddr, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c, err := client.Connect(*TargetAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.DebugPrintPackets = []string{""}
|
||||
err = client.LoginRaw(c, acc)
|
||||
c, err := client.Connect(nil, *TargetAddr, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
if !*TrueForward {
|
||||
err = client.LoginRaw(c, acc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
p.Client = c
|
||||
p.Client.AlwaysDecode = true
|
||||
p.Servee.AlwaysDecode = true
|
||||
err = p.Start()
|
||||
return
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/admin-else/strom/nbt"
|
||||
"github.com/admin-else/strom/mc/nbt"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
generated "github.com/admin-else/strom/proto_generated/%v"
|
||||
generated "github.com/admin-else/strom/mc/proto_generated/%v"
|
||||
)
|
||||
|
||||
// Error: %v
|
||||
|
||||
@@ -8,15 +8,16 @@ import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/admin-else/strom/data"
|
||||
"github.com/admin-else/strom/proto"
|
||||
"github.com/admin-else/strom/proto_base"
|
||||
"github.com/admin-else/strom/mc/data"
|
||||
"github.com/admin-else/strom/mc/proto"
|
||||
"github.com/admin-else/strom/mc/proto_base"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -24,7 +25,8 @@ var (
|
||||
FileFlag = cmd.String("file", "", "The tmcpr file to print")
|
||||
ProtocolVersion = cmd.Int("protocol", -1, "The protocol version")
|
||||
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)")
|
||||
)
|
||||
|
||||
// TODO: maybe use the proto replay api
|
||||
@@ -32,32 +34,43 @@ var (
|
||||
//go:embed failed_packet.go.tmpl
|
||||
var TestSrcF string
|
||||
|
||||
func SaveUnCodeAbleAsTest(packet *proto.UnCodablePacket) {
|
||||
func SaveUnCodeAbleAsTest(packet *proto.UnCodablePacket) (err error) {
|
||||
if packet.Info.Type == nil {
|
||||
slog.Warn("skipping test for unknown packet id", "packet_id", packet.Info.PacketId, "state", packet.Info.State, "error", packet.Err)
|
||||
return
|
||||
}
|
||||
|
||||
hUntrimmed := sha256.Sum256(packet.Data)
|
||||
h := hUntrimmed[:8]
|
||||
f, err := os.Create(fmt.Sprintf(*CreateTestsPath+"/%v_%10x_test.go", len(packet.Data), h))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if err = os.MkdirAll(*CreateTestsPath, 0755); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ret, err := data.LookUpVersionByProtocolVersion(packet.Info.ProtocolVersion)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return
|
||||
}
|
||||
goPackage := "v" + strings.ReplaceAll(ret.MinecraftVersion, ".", "_")
|
||||
typeName := reflect.TypeOf(packet.Info.Type).Elem().Name()
|
||||
|
||||
//goland:noinspection GoUnhandledErrorResult
|
||||
defer f.Close()
|
||||
_, err = fmt.Fprintf(f, TestSrcF, goPackage, packet.Err, h, reflect.TypeOf(packet.Info.Type).Elem().Name(), packet.Data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
src := fmt.Sprintf(TestSrcF, goPackage, packet.Err, h, typeName, packet.Data)
|
||||
formatted, formatErr := format.Source([]byte(src))
|
||||
if formatErr != nil {
|
||||
formatted = []byte(src)
|
||||
}
|
||||
slog.Info("Packet failed to parse", "error", packet.Err, "saved", f.Name(), "data", fmt.Sprintf("%q", packet.Data))
|
||||
|
||||
filePath := fmt.Sprintf(*CreateTestsPath+"/%v_%10x_test.go", len(packet.Data), h)
|
||||
if err = os.WriteFile(filePath, formatted, 0644); err != nil {
|
||||
return
|
||||
}
|
||||
slog.Info("Packet failed to parse", "error", packet.Err, "saved", filePath, "data", fmt.Sprintf("%q", packet.Data))
|
||||
return
|
||||
}
|
||||
|
||||
var state = proto_base.Login
|
||||
var direction = proto_base.ToClient
|
||||
|
||||
func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, err error) {
|
||||
func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, i proto_base.PacketInfo, err error) {
|
||||
var timestamp, length uint32
|
||||
err = binary.Read(r, binary.BigEndian, ×tamp)
|
||||
if err != nil {
|
||||
@@ -87,7 +100,8 @@ func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, err error) {
|
||||
// indexafterid represents how many bytes we've read so far
|
||||
indexafterid := int(b.Size()) - b.Len()
|
||||
|
||||
i, ok := proto.LookUpTypeByPacketInfo(proto_base.ToClient, state, packetId, int32(*ProtocolVersion))
|
||||
var ok bool
|
||||
i, ok = proto.LookUpTypeByPacketInfo(direction, state, packetId, int32(*ProtocolVersion))
|
||||
if !ok {
|
||||
packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: packetData, Info: i}
|
||||
return
|
||||
@@ -98,13 +112,42 @@ func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, err error) {
|
||||
packet = &proto.UnCodablePacket{Err: err, Data: packetData[indexafterid:], Info: i}
|
||||
return
|
||||
}
|
||||
if b.Len() != 0 {
|
||||
packet = &proto.UnCodablePacket{Err: proto.PacketNotFullyDecodedErr, Data: packetData[indexafterid:], Info: i}
|
||||
return
|
||||
}
|
||||
trackState(packet, i)
|
||||
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":
|
||||
state = proto_base.Configuration
|
||||
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
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Run(args []string) (err error) {
|
||||
@@ -119,15 +162,27 @@ func Run(args []string) (err error) {
|
||||
return fmt.Errorf("no file specified")
|
||||
}
|
||||
switch *StateFlag {
|
||||
case "handshake":
|
||||
state = proto_base.Handshaking
|
||||
case "login":
|
||||
state = proto_base.Login
|
||||
case "config":
|
||||
state = proto_base.Configuration
|
||||
case "play":
|
||||
state = proto_base.Play
|
||||
case "status":
|
||||
state = proto_base.Status
|
||||
default:
|
||||
return fmt.Errorf("unknown state: %s", *StateFlag)
|
||||
}
|
||||
switch *DirectionFlag {
|
||||
case "clientbound", "to-client":
|
||||
direction = proto_base.ToClient
|
||||
case "serverbound", "to-server":
|
||||
direction = proto_base.ToServer
|
||||
default:
|
||||
return fmt.Errorf("unknown direction: %s", *DirectionFlag)
|
||||
}
|
||||
f, err := os.Open(*FileFlag)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -137,21 +192,22 @@ func Run(args []string) (err error) {
|
||||
var totalPackets, failedPackets int
|
||||
for {
|
||||
var packet proto_base.EncodeDecodeAble
|
||||
packet, err = UnpackPacket(f)
|
||||
var info proto_base.PacketInfo
|
||||
packet, info, err = UnpackPacket(f)
|
||||
unCodablePacket, ok := packet.(*proto.UnCodablePacket)
|
||||
if ok {
|
||||
slog.Error("failed to decode packet", "error", err, "packet_name", info.Name)
|
||||
if *CreateTestsPath != "" {
|
||||
SaveUnCodeAbleAsTest(unCodablePacket)
|
||||
err = SaveUnCodeAbleAsTest(unCodablePacket)
|
||||
if err != nil {
|
||||
slog.Error("failed to save uncodable packet as test", "error", err)
|
||||
}
|
||||
}
|
||||
failedPackets++
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Printf("Packet: %#v\n", packet)
|
||||
}
|
||||
if errors.Is(err, io.EOF) {
|
||||
fmt.Printf("Packet: %#v\n", packet)
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
break
|
||||
}
|
||||
totalPackets++
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2d90ee976987678593a6b1d09136bb9b4fd131390796ea6e413376a6ddc63226
|
||||
size 75035693
|
||||
@@ -1 +0,0 @@
|
||||
[{"realTimestamp":9845034,"value":{"position":{"x":-31.946051078575692,"y":93.0,"z":29.523387113769626,"yaw":-627.44653,"pitch":37.573395,"roll":0.0}}},{"realTimestamp":9845138,"value":{"position":{"x":-31.946051078575692,"y":93.0,"z":29.523387113769626,"yaw":-627.44653,"pitch":37.573395,"roll":0.0}}},{"realTimestamp":9844902,"value":{"position":{"x":-31.946051078575692,"y":93.0,"z":29.523387113769626,"yaw":-627.44653,"pitch":37.573395,"roll":0.0}}},{"realTimestamp":9908896,"value":{"position":{"x":-1.6053634059630886,"y":91.672484820985,"z":-10.594536888795163,"yaw":-1151.6353,"pitch":16.142092,"roll":0.0}}},{"realTimestamp":9844750,"value":{"position":{"x":-31.946051078575692,"y":93.0,"z":29.523387113769626,"yaw":-627.44653,"pitch":37.573395,"roll":0.0}}},{"realTimestamp":9853735,"value":{"position":{"x":-31.05268144454021,"y":88.0,"z":-25.371850030366133,"yaw":-624.37897,"pitch":16.105839,"roll":0.0}}},{"realTimestamp":9845690,"value":{"position":{"x":-31.946051078575692,"y":93.0,"z":29.523387113769626,"yaw":-627.44653,"pitch":37.573395,"roll":0.0}}},{"realTimestamp":9938194,"value":{"position":{"x":39.88206777447001,"y":103.87657142499006,"z":58.09267383764874,"yaw":-1781.1023,"pitch":37.354122,"roll":0.0}}},{"realTimestamp":9846991,"value":{"position":{"x":-31.946051078575692,"y":93.0,"z":29.523387113769626,"yaw":-628.46875,"pitch":36.04,"roll":0.0}}},{"realTimestamp":9908638,"value":{"position":{"x":-1.5870303355717672,"y":91.672484820985,"z":-11.359450846926011,"yaw":-1142.6902,"pitch":20.997843,"roll":0.0}}},{"realTimestamp":9845490,"value":{"position":{"x":-31.946051078575692,"y":93.0,"z":29.523387113769626,"yaw":-627.44653,"pitch":37.573395,"roll":0.0}}},{"realTimestamp":9845990,"value":{"position":{"x":-31.946051078575692,"y":93.0,"z":29.523387113769626,"yaw":-627.44653,"pitch":37.573395,"roll":0.0}}}]
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d972b9b5f9d962199fe262eccef32a4b94504283fae97f9963702e7ab1550129
|
||||
size 437123
|
||||
@@ -1 +0,0 @@
|
||||
{"singleplayer":false,"serverName":"protect-test-server.anygate.cloud","customServerName":"proxied test server","duration":3368,"date":1776531741320,"mcversion":"1.21.11","fileFormat":"MCPR","fileFormatVersion":14,"protocol":774,"generator":"ReplayMod v1.21.11-2.6.26","selfId":-1,"players":[]}
|
||||
@@ -1 +0,0 @@
|
||||
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cef58ec0e3d1d25e34c2effc5c09241ee4c1b39c085ed9e11b7097dfd5c2d033
|
||||
size 3429878
|
||||
@@ -1 +0,0 @@
|
||||
234002168
|
||||
@@ -1 +0,0 @@
|
||||
{"singleplayer":false,"serverName":"protect-beta.anygate.cloud","customServerName":"protect-beta.anygate.cloud","duration":10198341,"date":1776535673280,"mcversion":"1.21.11","fileFormat":"MCPR","fileFormatVersion":14,"protocol":774,"generator":"ReplayMod v1.21.11-2.6.26","selfId":-1,"players":["fe2d6f64-7ab0-3373-a526-af097abbcb39"]}
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:be007aa681f7959708e89e39e05390e228753291823a18274068d310a50aa78b
|
||||
size 526306397
|
||||
@@ -1 +0,0 @@
|
||||
270566013
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0da58f5cdd65ef241df8c61f9e633af7317faa77034183d4fb648b039c1e14ed
|
||||
size 842
|
||||
@@ -1 +0,0 @@
|
||||
{"singleplayer":false,"serverName":"protect-test-server.anygate.cloud","customServerName":"proxied test server","duration":476,"date":1776803431635,"mcversion":"1.21.11","fileFormat":"MCPR","fileFormatVersion":14,"protocol":774,"generator":"ReplayMod v1.21.11-2.6.26","selfId":-1,"players":[]}
|
||||
@@ -1 +0,0 @@
|
||||
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:52fa049f97918289e018fa4c3bf5ff789d4f99f44f590ad875ce1f65e5fa9563
|
||||
size 37
|
||||
@@ -1 +0,0 @@
|
||||
1105715802
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package registry_data_hunter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -6,16 +6,16 @@ import (
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/admin-else/strom/api"
|
||||
"github.com/admin-else/strom/client"
|
||||
"github.com/admin-else/strom/data"
|
||||
"github.com/admin-else/strom/event"
|
||||
"github.com/admin-else/strom/nbt"
|
||||
"github.com/admin-else/strom/proto"
|
||||
"github.com/admin-else/strom/proto_base"
|
||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||
"github.com/admin-else/strom/server"
|
||||
"github.com/admin-else/strom/text"
|
||||
"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/nbt"
|
||||
"github.com/admin-else/strom/mc/proto"
|
||||
"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/server"
|
||||
"github.com/admin-else/strom/mc/text"
|
||||
)
|
||||
|
||||
func wontFail[T any](v T, err error) T {
|
||||
@@ -25,10 +25,7 @@ func wontFail[T any](v T, err error) T {
|
||||
return v
|
||||
}
|
||||
|
||||
// THIS IS REDUNDANT USE LOGIN PACKET IN MINECRAFT DATA
|
||||
|
||||
var (
|
||||
// Edit these two
|
||||
Version = "1.21.11"
|
||||
SaveAs = "data/registry/" + Version + ".nbt"
|
||||
|
||||
@@ -113,7 +110,6 @@ func (p *Proxy) OnTags(data *v1_21_8.ConfigurationToClientPacketTags) (err error
|
||||
func handleClient(c *proto.Conn) (err error) {
|
||||
acc := api.NewOfflineAccount("RegistryHunter")
|
||||
|
||||
// ignore resource leak warning we own c conn so we should not close it here
|
||||
_, err = server.ServeLogin(c, server.WithOtherAccount(acc), server.WithCompatibleVersions(CompatibleProtocolVersion), server.WithRawStatus(Status))
|
||||
if err != nil {
|
||||
return
|
||||
@@ -150,10 +146,7 @@ func handleClient(c *proto.Conn) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
func Run(args []string) error {
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
err := server.StartServerWithOnConn(":25566", handleClient)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return server.StartServerWithOnConn(":25566", handleClient)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package serve_tmcpr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"io"
|
||||
"log/slog"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"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_generated/v1_21_8"
|
||||
"github.com/admin-else/strom/mc/server"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
*proto.Conn
|
||||
*event.Timer
|
||||
}
|
||||
|
||||
var (
|
||||
cmd = flag.NewFlagSet("serve-tmcpr", flag.ContinueOnError)
|
||||
tmcprFile = cmd.String("file", "/tmp/world.tmcpr", "Path to .tmcpr replay file")
|
||||
bindAddr = cmd.String("bind", ":25566", "Address to listen on")
|
||||
)
|
||||
|
||||
func Limbo(c *proto.Conn, tmcprPath string) (err error) {
|
||||
s := &Server{Conn: c}
|
||||
|
||||
_, err = server.ServeLogin(s.Conn, server.WithoutOnlineMode())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = server.ServeConfig(s.Conn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Open(tmcprPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r := replay.NewReader(f)
|
||||
r.Version = c.ProtocolVersion
|
||||
|
||||
start := time.Now()
|
||||
|
||||
for {
|
||||
var packetData []byte
|
||||
var timestamp uint32
|
||||
packetData, timestamp, _, err = r.ReadRaw()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
time.Sleep(time.Duration(timestamp)*time.Millisecond - time.Since(start))
|
||||
err = s.SendRaw(packetData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.Timer = &event.Timer{}
|
||||
s.Timer.Every(time.Second*15, s.SendKeepAlive)
|
||||
s.Timer.Start(c.Loop)
|
||||
|
||||
return s.StartConn()
|
||||
}
|
||||
|
||||
func (s *Server) SendKeepAlive() error {
|
||||
return s.Send(&v1_21_8.PlayToClientPacketKeepAlive{KeepAliveId: rand.Int64()})
|
||||
}
|
||||
|
||||
func Run(args []string) error {
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
|
||||
err := cmd.Parse(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if *tmcprFile == "" {
|
||||
slog.Error("no file specified")
|
||||
return nil
|
||||
}
|
||||
|
||||
return server.StartServerWithOnConn(*bindAddr, func(c *proto.Conn) error {
|
||||
return Limbo(c, *tmcprFile)
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package serve_world
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/admin-else/strom/event"
|
||||
"github.com/admin-else/strom/level"
|
||||
"github.com/admin-else/strom/level/anvil"
|
||||
"github.com/admin-else/strom/nbt"
|
||||
"github.com/admin-else/strom/proto"
|
||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||
"github.com/admin-else/strom/server"
|
||||
"github.com/admin-else/strom/util"
|
||||
"github.com/admin-else/strom/mc/event"
|
||||
"github.com/admin-else/strom/mc/level"
|
||||
"github.com/admin-else/strom/mc/level/anvil"
|
||||
"github.com/admin-else/strom/mc/nbt"
|
||||
"github.com/admin-else/strom/mc/proto"
|
||||
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
|
||||
"github.com/admin-else/strom/mc/server"
|
||||
"github.com/admin-else/strom/mc/util"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
@@ -28,7 +28,7 @@ type Server struct {
|
||||
var FullLight [][]uint8
|
||||
|
||||
func init() {
|
||||
light := make([]uint8, 2048) // 4096 values 2 per uint8
|
||||
light := make([]uint8, 2048)
|
||||
for i := range light {
|
||||
light[i] = 0xFF
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func GetChunkFromMca(x, z int32, version string) (b []byte, err error) {
|
||||
z += anvil.ChunksWidthRegionFile
|
||||
}
|
||||
|
||||
f, err := os.Open(fmt.Sprintf("./level/anvil/testdata/securechattestworld/region/r.%v.%v.mca", xmca, zmca))
|
||||
f, err := os.Open(fmt.Sprintf("./.devres/anvil_testdata/securechattestworld/region/r.%v.%v.mca", xmca, zmca))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -95,13 +95,11 @@ func Limbo(c *proto.Conn) (err error) {
|
||||
s := &Server{
|
||||
Conn: c,
|
||||
}
|
||||
//s.DebugPrintPackets = []string{""}
|
||||
_, err = server.ServeLogin(s.Conn, server.WithoutOnlineMode())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
//goland:noinspection GoResourceLeak captures the conn object which should NOT be closed
|
||||
err = server.ServeConfig(s.Conn)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -176,10 +174,7 @@ func (s *Server) SendKeepAlive() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
func Run(args []string) error {
|
||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||
err := server.StartServerWithOnConn(":25566", Limbo)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return server.StartServerWithOnConn(":25566", Limbo)
|
||||
}
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/admin-else/strom/client"
|
||||
"github.com/admin-else/strom/event"
|
||||
"github.com/admin-else/strom/proto"
|
||||
"github.com/admin-else/strom/proto_base"
|
||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
||||
"github.com/admin-else/strom/mc/client"
|
||||
"github.com/admin-else/strom/mc/event"
|
||||
"github.com/admin-else/strom/mc/proto"
|
||||
"github.com/admin-else/strom/mc/proto_base"
|
||||
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
|
||||
)
|
||||
|
||||
var cmd = flag.NewFlagSet("status", flag.ContinueOnError)
|
||||
@@ -46,7 +46,7 @@ func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
|
||||
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
|
||||
// It does not resolve SRV records.
|
||||
func StatusRaw(addr string) (s *StatusClient, err error) {
|
||||
c, err := client.ConnectVersionLess(addr)
|
||||
c, err := client.ConnectVersionLess(nil, addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func Run(args []string) (err error) {
|
||||
return
|
||||
}
|
||||
if *srvFlag {
|
||||
*addrFlag, err = client.DoDnsSimple(*addrFlag)
|
||||
*addrFlag, _, _, err = client.DoDNSChecked(nil, *addrFlag, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,7 +2,5 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
-20753
File diff suppressed because it is too large
Load Diff
Vendored
BIN
Binary file not shown.
-26884
File diff suppressed because it is too large
Load Diff
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:14cd0df55488907baf26a6e8da9548d614d044a15259a526bb1f0be44a712764
|
||||
size 10113024
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
-215
@@ -1,215 +0,0 @@
|
||||
{
|
||||
"minecraft:recipes/building_blocks/polished_deepslate": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tile_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_brick_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/combat/stone_sword": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_axe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:story/mine_stone": {
|
||||
"criteria": {
|
||||
"get_stone": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/furnace": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/chiseled_deepslate_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/crafting_table": {
|
||||
"criteria": {
|
||||
"unlock_right_away": "2026-01-18 19:40:40 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_slab": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/cobbled_deepslate_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_campfire_cooking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-01-18 19:40:40 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/cobbled_deepslate_wall": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/polished_deepslate_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/deepslate_brick_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-01-18 19:40:40 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_pickaxe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_brick_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tile_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/misc/charcoal": {
|
||||
"criteria": {
|
||||
"has_log": "2026-01-18 19:41:43 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_stairs": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/deepslate_tile_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/oak_planks": {
|
||||
"criteria": {
|
||||
"has_logs": "2026-01-18 19:41:43 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_smoking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-01-18 19:40:40 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_bricks_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/adventuring_time": {
|
||||
"criteria": {
|
||||
"minecraft:forest": "2026-01-18 19:40:40 +0100",
|
||||
"minecraft:deep_dark": "2026-01-18 19:41:56 +0100",
|
||||
"minecraft:stony_shore": "2026-01-18 19:40:46 +0100"
|
||||
},
|
||||
"done": false
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tiles_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_hoe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_shovel": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-01-18 19:41:36 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"DataVersion": 4556
|
||||
}
|
||||
Vendored
-17
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"minecraft:recipes/decorations/crafting_table": {
|
||||
"criteria": {
|
||||
"unlock_right_away": "2026-04-23 22:08:36 +0200"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/adventuring_time": {
|
||||
"criteria": {
|
||||
"minecraft:taiga": "2026-04-23 22:10:55 +0200",
|
||||
"minecraft:grove": "2026-04-23 22:09:03 +0200",
|
||||
"minecraft:stony_shore": "2026-04-23 22:08:37 +0200"
|
||||
},
|
||||
"done": false
|
||||
},
|
||||
"DataVersion": 4671
|
||||
}
|
||||
Vendored
-17
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"minecraft:recipes/decorations/crafting_table": {
|
||||
"criteria": {
|
||||
"unlock_right_away": "2026-05-08 20:07:02 +0200"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/adventuring_time": {
|
||||
"criteria": {
|
||||
"minecraft:forest": "2026-05-08 20:07:06 +0200",
|
||||
"minecraft:taiga": "2026-05-08 20:07:05 +0200",
|
||||
"minecraft:grove": "2026-05-08 20:07:03 +0200"
|
||||
},
|
||||
"done": false
|
||||
},
|
||||
"DataVersion": 4671
|
||||
}
|
||||
Vendored
-220
@@ -1,220 +0,0 @@
|
||||
{
|
||||
"minecraft:recipes/building_blocks/polished_deepslate": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tile_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_brick_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/combat/stone_sword": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_axe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:story/mine_stone": {
|
||||
"criteria": {
|
||||
"get_stone": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/furnace": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/chiseled_deepslate_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/crafting_table": {
|
||||
"criteria": {
|
||||
"unlock_right_away": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_slab": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/cobbled_deepslate_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_campfire_cooking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/cobbled_deepslate_wall": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/polished_deepslate_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/deepslate_brick_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_pickaxe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_brick_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tile_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/misc/charcoal": {
|
||||
"criteria": {
|
||||
"has_log": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_stairs": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/combat/stone_spear": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/deepslate_tile_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/oak_planks": {
|
||||
"criteria": {
|
||||
"has_logs": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_smoking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_bricks_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/adventuring_time": {
|
||||
"criteria": {
|
||||
"minecraft:stony_shore": "2026-03-11 19:22:59 +0100",
|
||||
"minecraft:plains": "2026-03-11 19:22:38 +0100"
|
||||
},
|
||||
"done": false
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tiles_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_hoe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_shovel": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-11 19:22:37 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"DataVersion": 4671
|
||||
}
|
||||
Vendored
-222
@@ -1,222 +0,0 @@
|
||||
{
|
||||
"minecraft:recipes/building_blocks/polished_deepslate": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tile_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_brick_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/combat/stone_sword": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_axe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:story/mine_stone": {
|
||||
"criteria": {
|
||||
"get_stone": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/furnace": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/chiseled_deepslate_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/crafting_table": {
|
||||
"criteria": {
|
||||
"unlock_right_away": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_slab": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/cobbled_deepslate_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_campfire_cooking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/cobbled_deepslate_wall": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/polished_deepslate_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/deepslate_brick_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_pickaxe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_brick_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tile_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/misc/charcoal": {
|
||||
"criteria": {
|
||||
"has_log": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_stairs": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/combat/stone_spear": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/deepslate_tile_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/oak_planks": {
|
||||
"criteria": {
|
||||
"has_logs": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_smoking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_bricks_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/adventuring_time": {
|
||||
"criteria": {
|
||||
"minecraft:forest": "2026-02-07 19:19:04 +0100",
|
||||
"minecraft:river": "2026-02-07 19:20:26 +0100",
|
||||
"minecraft:stony_shore": "2026-02-07 19:20:11 +0100",
|
||||
"minecraft:plains": "2026-02-07 19:20:12 +0100"
|
||||
},
|
||||
"done": false
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tiles_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_hoe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_shovel": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-02-07 19:19:03 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"DataVersion": 4671
|
||||
}
|
||||
Vendored
-119
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"minecraft:recipes/decorations/crafting_table": {
|
||||
"criteria": {
|
||||
"unlock_right_away": "2025-11-30 09:32:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/cherry_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/kill_all_mobs": {
|
||||
"criteria": {
|
||||
"minecraft:zombie": "2025-11-30 10:06:33 +0100"
|
||||
},
|
||||
"done": false
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_campfire_cooking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2025-11-30 10:06:57 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/bamboo_raft": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/spruce_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/birch_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/oak_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/mangrove_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2025-11-30 10:06:57 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/acacia_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/root": {
|
||||
"criteria": {
|
||||
"killed_something": "2025-11-30 10:06:33 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_smoking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2025-11-30 10:06:57 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/kill_a_mob": {
|
||||
"criteria": {
|
||||
"minecraft:zombie": "2025-11-30 10:06:33 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/pale_oak_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/adventuring_time": {
|
||||
"criteria": {
|
||||
"minecraft:old_growth_birch_forest": "2025-11-30 10:03:58 +0100",
|
||||
"minecraft:forest": "2025-11-30 10:04:40 +0100",
|
||||
"minecraft:taiga": "2025-11-30 09:32:29 +0100",
|
||||
"minecraft:deep_dark": "2025-11-30 10:05:12 +0100",
|
||||
"minecraft:river": "2025-11-30 10:09:03 +0100",
|
||||
"minecraft:grove": "2025-11-30 09:32:32 +0100",
|
||||
"minecraft:meadow": "2025-11-30 09:32:44 +0100",
|
||||
"minecraft:birch_forest": "2025-11-30 10:05:33 +0100",
|
||||
"minecraft:frozen_peaks": "2025-11-30 09:32:55 +0100"
|
||||
},
|
||||
"done": false
|
||||
},
|
||||
"minecraft:recipes/transportation/jungle_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/transportation/dark_oak_boat": {
|
||||
"criteria": {
|
||||
"in_water": "2025-11-30 10:05:13 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"DataVersion": 4556
|
||||
}
|
||||
Vendored
-220
@@ -1,220 +0,0 @@
|
||||
{
|
||||
"minecraft:recipes/building_blocks/polished_deepslate": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tile_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_brick_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/combat/stone_sword": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_axe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:story/mine_stone": {
|
||||
"criteria": {
|
||||
"get_stone": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/furnace": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/chiseled_deepslate_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/crafting_table": {
|
||||
"criteria": {
|
||||
"unlock_right_away": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_slab": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/cobbled_deepslate_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_campfire_cooking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/cobbled_deepslate_wall": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/polished_deepslate_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_stairs_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/deepslate_brick_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/polished_deepslate_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_pickaxe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_brick_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tile_slab_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/misc/charcoal": {
|
||||
"criteria": {
|
||||
"has_log": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/cobbled_deepslate_stairs": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/combat/stone_spear": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/decorations/deepslate_tile_wall_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/oak_planks": {
|
||||
"criteria": {
|
||||
"has_logs": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/food/cooked_porkchop_from_smoking": {
|
||||
"criteria": {
|
||||
"has_porkchop": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_bricks_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:adventure/adventuring_time": {
|
||||
"criteria": {
|
||||
"minecraft:stony_shore": "2026-03-12 21:17:48 +0100",
|
||||
"minecraft:plains": "2026-03-12 21:17:31 +0100"
|
||||
},
|
||||
"done": false
|
||||
},
|
||||
"minecraft:recipes/building_blocks/deepslate_tiles_from_cobbled_deepslate_stonecutting": {
|
||||
"criteria": {
|
||||
"has_cobbled_deepslate": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_hoe": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"minecraft:recipes/tools/stone_shovel": {
|
||||
"criteria": {
|
||||
"has_cobblestone": "2026-03-12 21:17:28 +0100"
|
||||
},
|
||||
"done": true
|
||||
},
|
||||
"DataVersion": 4671
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1 +0,0 @@
|
||||
https://github.com/cabaletta/baritone
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fc0958e0912637fd74710a15fe7f1272f3cd2af3bcce3c23fed19736388c00ad
|
||||
size 753664
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:26d5f8cd8bd1c860416914bc4c02d0b70607656beba85dba2263e0e79a06e7b8
|
||||
size 348160
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:28c10367fb1e46056999833e17d07551e5bf76dd913551e09f8d43595a2b3fb2
|
||||
size 12288
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:19a35fe09fba69dfe85874a548d28b77c4c70664e338530b9f3f08f24ad4abba
|
||||
size 598016
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:30adf5f8dec1ff527794b72ec7f6e647b5501c851dc988210daf354b6d56f03b
|
||||
size 143360
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:65c4290f6e9cc93cf73a91a47437484b94d014a0b9ad648d896c12674faf1f10
|
||||
size 405504
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c68c40572cb6667a818d6982a1c2cd40f95c3ab25551b7d70fbc35cb0146ee12
|
||||
size 290816
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2c6387f0f76c625bf167373094a7628efad713158de30bb2bdfc06ad4f44acab
|
||||
size 192512
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:34a1d263910364f6bd0173e0ec96e8f45a0a5c2c74e4c5a3e771612685376d34
|
||||
size 77824
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:490e215ae40f8928646cfd09e2ccbed852e9a0b43a48234a5e8d5a8e01b56a1a
|
||||
size 32768
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:870c2d34a6e041f46786167f5c077ce515d0daa616242d4a8065861e376aaf54
|
||||
size 24576
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7473ac38f9943d9e1ed907faa2997d2833c29f326bd9829f7d0dc49b77797486
|
||||
size 724992
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:adba24770ab0983249c2b327fa9ae675107a34549651d870c5d0011ea8cbcabc
|
||||
size 294912
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user