Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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/
|
.devres/
|
||||||
.idea/
|
.idea/
|
||||||
.failed_packets/
|
.failed_packets/
|
||||||
tools/.*/
|
|
||||||
.vscode/
|
.vscode/
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -4,9 +4,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
"github.com/admin-else/strom/mc/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = flag.NewFlagSet("api", flag.ContinueOnError)
|
var cmd = flag.NewFlagSet("api", flag.ContinueOnError)
|
||||||
@@ -52,7 +53,7 @@ func Run(args []string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = PleaseProvideArgumentErr
|
slog.Info("account", "name", a.Name, "uuid", a.Uuid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = json.NewEncoder(os.Stdout).Encode(data)
|
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"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
mcdata "github.com/admin-else/strom/mc/data"
|
||||||
"github.com/admin-else/strom/proto_generated"
|
"github.com/admin-else/strom/mc/proto_generated"
|
||||||
"github.com/admin-else/strom/util"
|
"github.com/admin-else/strom/mc/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// strom data 1.21.11 blocks id 0 name => air
|
// 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
|
case 0: // print available versions
|
||||||
dataValues = proto_generated.SupportedVersions
|
dataValues = proto_generated.SupportedVersions
|
||||||
case 1: // print available data for version
|
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:
|
case 2:
|
||||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||||
case 3:
|
case 3:
|
||||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ func Run(args []string) (err error) {
|
|||||||
}
|
}
|
||||||
dataValues = result
|
dataValues = result
|
||||||
case 4, 5:
|
case 4, 5:
|
||||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/admin-else/strom/level/anvil"
|
"github.com/admin-else/strom/mc/level/anvil"
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
+31
-10
@@ -1,39 +1,53 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/buildinfo"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/admin-else/strom/cmd/strom/api"
|
"github.com/admin-else/strom/cmd/strom/api"
|
||||||
"github.com/admin-else/strom/cmd/strom/data"
|
"github.com/admin-else/strom/cmd/strom/data"
|
||||||
"github.com/admin-else/strom/cmd/strom/extract_mca"
|
"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/offline_uuid"
|
||||||
"github.com/admin-else/strom/cmd/strom/packet_info"
|
"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/packet_inspector"
|
||||||
"github.com/admin-else/strom/cmd/strom/print_nbt"
|
"github.com/admin-else/strom/cmd/strom/print_nbt"
|
||||||
"github.com/admin-else/strom/cmd/strom/print_replay"
|
"github.com/admin-else/strom/cmd/strom/print_replay"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/raw_capture"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/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"
|
"github.com/admin-else/strom/cmd/strom/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ExpectedASubcommandErr = errors.New("expected a subcommand")
|
var ExpectedASubcommandErr = errors.New("expected a subcommand")
|
||||||
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
|
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
|
||||||
|
|
||||||
func Version(args []string) (err error) {
|
func ToolVersion() string {
|
||||||
selfPath, err := os.Executable()
|
info, ok := debug.ReadBuildInfo()
|
||||||
if err != nil {
|
if !ok {
|
||||||
return
|
return "devel"
|
||||||
}
|
}
|
||||||
info, err := buildinfo.ReadFile(selfPath)
|
return info.Main.Version
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
fmt.Println("strom cli ", info.Main.Version, " (", info.Main.Sum, ")")
|
|
||||||
return
|
func ToolSum() string {
|
||||||
|
info, ok := debug.ReadBuildInfo()
|
||||||
|
if !ok {
|
||||||
|
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{
|
var subcommands = map[string]func(args []string) error{
|
||||||
@@ -47,6 +61,12 @@ var subcommands = map[string]func(args []string) error{
|
|||||||
"extract-mca": extract_mca.Run,
|
"extract-mca": extract_mca.Run,
|
||||||
"data": data.Run,
|
"data": data.Run,
|
||||||
"print-replay": print_replay.Run,
|
"print-replay": print_replay.Run,
|
||||||
|
"raw-capture": raw_capture.Run,
|
||||||
|
"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) {
|
func Help(args []string) (err error) {
|
||||||
@@ -59,6 +79,7 @@ func Help(args []string) (err error) {
|
|||||||
func mainE() (err error) {
|
func mainE() (err error) {
|
||||||
subcommands["help"] = Help
|
subcommands["help"] = Help
|
||||||
args := os.Args
|
args := os.Args
|
||||||
|
//args = []string{"TEST", "messenger", "-addr", "localhost"}
|
||||||
|
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
err = ExpectedASubcommandErr
|
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,134 @@
|
|||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Messenger struct {
|
||||||
|
*proto.Conn
|
||||||
|
chat *chat.Module
|
||||||
|
world *world.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
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) OnStdin(e event.Stdin) (err error) {
|
||||||
|
input := strings.TrimSpace(e.Val)
|
||||||
|
if strings.HasPrefix(input, ">say ") {
|
||||||
|
return m.chat.SendMessage(strings.TrimSpace(strings.TrimPrefix(input, ">say")))
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(input, ">getblock ") {
|
||||||
|
return m.handleGetBlock(strings.TrimSpace(strings.TrimPrefix(input, ">getblock")))
|
||||||
|
}
|
||||||
|
return m.chat.SendMessage(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
chatMod, err := chat.Start(c, acc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shareable world storage. Other bots can subscribe to the same World.
|
||||||
|
w := world.NewWorld(c.Version, -64, 384)
|
||||||
|
|
||||||
|
m := &Messenger{
|
||||||
|
Conn: c,
|
||||||
|
chat: chatMod,
|
||||||
|
world: world.Start(c, w),
|
||||||
|
}
|
||||||
|
|
||||||
|
event.StartListingStdin(m.Loop)
|
||||||
|
m.Register(m.OnStdin)
|
||||||
|
m.RegisterUntilLatest(m.OnChat, m.OnChatUnsigned, m.OnChatSystem)
|
||||||
|
keepalive.Start(m.Conn)
|
||||||
|
|
||||||
|
return m.StartConn()
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/admin-else/strom/crypto"
|
"github.com/admin-else/strom/mc/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = flag.NewFlagSet("offline-uuid", flag.ContinueOnError)
|
var cmd = flag.NewFlagSet("offline-uuid", flag.ContinueOnError)
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
"github.com/admin-else/strom/mc/data"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated"
|
"github.com/admin-else/strom/mc/proto_generated"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = flag.NewFlagSet("packet-info", flag.ContinueOnError)
|
var cmd = flag.NewFlagSet("packet-info", flag.ContinueOnError)
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
package packet_inspector
|
package packet_inspector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
"github.com/admin-else/strom/mc/api"
|
||||||
"github.com/admin-else/strom/client"
|
"github.com/admin-else/strom/mc/client"
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/mc/event"
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_11"
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
|
||||||
"github.com/admin-else/strom/server"
|
"github.com/admin-else/strom/mc/server"
|
||||||
"github.com/admin-else/strom/text"
|
"github.com/admin-else/strom/mc/text"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Cmd = flag.NewFlagSet("packet-inspector", flag.ContinueOnError)
|
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 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 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 {
|
type Proxy struct {
|
||||||
Client, Servee *proto.Conn
|
Client, Servee *proto.Conn
|
||||||
@@ -33,6 +37,10 @@ func (p *Proxy) Start() (err error) {
|
|||||||
p.Servee.RegisterCritical(p.OnAnything)
|
p.Servee.RegisterCritical(p.OnAnything)
|
||||||
|
|
||||||
p.Servee.RegisterCriticalUntilLatest(p.OnFinishConfiguration)
|
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)
|
errChan := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -45,6 +53,22 @@ func (p *Proxy) Start() (err error) {
|
|||||||
return
|
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) {
|
func (p *Proxy) OnAnything(e event.Anything) (err error) {
|
||||||
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
|
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -54,6 +78,19 @@ func (p *Proxy) OnAnything(e event.Anything) (err error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
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 {
|
switch packetInfo.Direction {
|
||||||
case proto_base.ToServer:
|
case proto_base.ToServer:
|
||||||
err = p.Client.Send(packet)
|
err = p.Client.Send(packet)
|
||||||
@@ -74,6 +111,40 @@ func (p *Proxy) OnFinishConfiguration(packet *v1_21_11.ConfigurationToServerPack
|
|||||||
return
|
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{
|
var StatusResponse = server.StatusResponse{
|
||||||
Version: server.StatusResponseVersion{
|
Version: server.StatusResponseVersion{
|
||||||
Name: text.Pretty("STROM"),
|
Name: text.Pretty("STROM"),
|
||||||
@@ -90,38 +161,41 @@ func Run(args []string) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
|
||||||
slog.Info("Starting packet inspector", "listen", *ListenAddr)
|
slog.Info("Starting packet inspector", "listen", *ListenAddr)
|
||||||
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
|
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
|
||||||
serveeConn.DebugPrintPackets = []string{""}
|
|
||||||
p := &Proxy{Servee: serveeConn, Client: nil}
|
p := &Proxy{Servee: serveeConn, Client: nil}
|
||||||
var acc = api.NewOfflineAccount(*OfflineNameFlag)
|
var acc = api.NewOfflineAccount(*OfflineNameFlag)
|
||||||
|
if !*TrueForward {
|
||||||
if *Token != "" {
|
if *Token != "" {
|
||||||
acc, err = api.NewAccountFromYGG(*Token)
|
acc, err = api.NewAccountFromYGG(*Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = server.ServeLogin(serveeConn, server.WithOtherAccount(acc), server.WithStatus(StatusResponse), server.WithoutOnlineMode())
|
_, err = server.ServeLogin(serveeConn, server.WithOtherAccount(acc), server.WithStatus(StatusResponse), server.WithoutOnlineMode(), server.WithoutEncryption())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*TargetAddr, err = client.DoDnsSimple(*TargetAddr)
|
*TargetAddr, _, _, err = client.DoDNSChecked(nil, *TargetAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c, err := client.Connect(*TargetAddr)
|
c, err := client.Connect(nil, *TargetAddr, "")
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.DebugPrintPackets = []string{""}
|
|
||||||
err = client.LoginRaw(c, acc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
if !*TrueForward {
|
||||||
|
err = client.LoginRaw(c, acc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
p.Client = c
|
p.Client = c
|
||||||
|
p.Client.AlwaysDecode = true
|
||||||
|
p.Servee.AlwaysDecode = true
|
||||||
err = p.Start()
|
err = p.Start()
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
generated "github.com/admin-else/strom/proto_generated/%v"
|
generated "github.com/admin-else/strom/mc/proto_generated/%v"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error: %v
|
// Error: %v
|
||||||
|
|||||||
@@ -8,15 +8,16 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/format"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
"github.com/admin-else/strom/mc/data"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -25,6 +26,7 @@ var (
|
|||||||
ProtocolVersion = cmd.Int("protocol", -1, "The protocol version")
|
ProtocolVersion = cmd.Int("protocol", -1, "The protocol version")
|
||||||
CreateTestsPath = cmd.String("create-tests", "", "")
|
CreateTestsPath = cmd.String("create-tests", "", "")
|
||||||
StateFlag = cmd.String("state", "login", "Initial state: login, config, play")
|
StateFlag = cmd.String("state", "login", "Initial state: login, config, play")
|
||||||
|
DirectionFlag = cmd.String("direction", "clientbound", "Packet direction: clientbound (ToClient) or serverbound (ToServer)")
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: maybe use the proto replay api
|
// TODO: maybe use the proto replay api
|
||||||
@@ -32,32 +34,43 @@ var (
|
|||||||
//go:embed failed_packet.go.tmpl
|
//go:embed failed_packet.go.tmpl
|
||||||
var TestSrcF string
|
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)
|
hUntrimmed := sha256.Sum256(packet.Data)
|
||||||
h := hUntrimmed[:8]
|
h := hUntrimmed[:8]
|
||||||
f, err := os.Create(fmt.Sprintf(*CreateTestsPath+"/%v_%10x_test.go", len(packet.Data), h))
|
if err = os.MkdirAll(*CreateTestsPath, 0755); err != nil {
|
||||||
if err != nil {
|
return
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := data.LookUpVersionByProtocolVersion(packet.Info.ProtocolVersion)
|
ret, err := data.LookUpVersionByProtocolVersion(packet.Info.ProtocolVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return
|
||||||
}
|
}
|
||||||
goPackage := "v" + strings.ReplaceAll(ret.MinecraftVersion, ".", "_")
|
goPackage := "v" + strings.ReplaceAll(ret.MinecraftVersion, ".", "_")
|
||||||
|
typeName := reflect.TypeOf(packet.Info.Type).Elem().Name()
|
||||||
|
|
||||||
//goland:noinspection GoUnhandledErrorResult
|
src := fmt.Sprintf(TestSrcF, goPackage, packet.Err, h, typeName, packet.Data)
|
||||||
defer f.Close()
|
formatted, formatErr := format.Source([]byte(src))
|
||||||
_, err = fmt.Fprintf(f, TestSrcF, goPackage, packet.Err, h, reflect.TypeOf(packet.Info.Type).Elem().Name(), packet.Data)
|
if formatErr != nil {
|
||||||
if err != nil {
|
formatted = []byte(src)
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
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 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
|
var timestamp, length uint32
|
||||||
err = binary.Read(r, binary.BigEndian, ×tamp)
|
err = binary.Read(r, binary.BigEndian, ×tamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -87,22 +100,37 @@ func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, err error) {
|
|||||||
// indexafterid represents how many bytes we've read so far
|
// indexafterid represents how many bytes we've read so far
|
||||||
indexafterid := int(b.Size()) - b.Len()
|
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 {
|
if !ok {
|
||||||
packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: packetData, Info: i}
|
packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: packetData, Info: i}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
switch direction {
|
||||||
|
case proto_base.ToClient:
|
||||||
|
switch i.Name {
|
||||||
|
case "success":
|
||||||
|
state = proto_base.Configuration
|
||||||
|
case "finish_configuration":
|
||||||
|
state = proto_base.Play
|
||||||
|
}
|
||||||
|
case proto_base.ToServer:
|
||||||
|
switch i.Name {
|
||||||
|
case "login_acknowledged":
|
||||||
|
state = proto_base.Configuration
|
||||||
|
case "finish_configuration":
|
||||||
|
state = proto_base.Play
|
||||||
|
}
|
||||||
|
}
|
||||||
packet = reflect.New(reflect.TypeOf(i.Type).Elem()).Interface().(proto_base.EncodeDecodeAble)
|
packet = reflect.New(reflect.TypeOf(i.Type).Elem()).Interface().(proto_base.EncodeDecodeAble)
|
||||||
err = packet.Decode(b)
|
err = packet.Decode(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
packet = &proto.UnCodablePacket{Err: err, Data: packetData[indexafterid:], Info: i}
|
packet = &proto.UnCodablePacket{Err: err, Data: packetData[indexafterid:], Info: i}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch i.Name {
|
if b.Len() != 0 {
|
||||||
case "success":
|
packet = &proto.UnCodablePacket{Err: proto.PacketNotFullyDecodedErr, Data: packetData[indexafterid:], Info: i}
|
||||||
state = proto_base.Configuration
|
return
|
||||||
case "finish_configuration":
|
|
||||||
state = proto_base.Play
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -128,6 +156,14 @@ func Run(args []string) (err error) {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown state: %s", *StateFlag)
|
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)
|
f, err := os.Open(*FileFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -137,21 +173,22 @@ func Run(args []string) (err error) {
|
|||||||
var totalPackets, failedPackets int
|
var totalPackets, failedPackets int
|
||||||
for {
|
for {
|
||||||
var packet proto_base.EncodeDecodeAble
|
var packet proto_base.EncodeDecodeAble
|
||||||
packet, err = UnpackPacket(f)
|
var info proto_base.PacketInfo
|
||||||
|
packet, info, err = UnpackPacket(f)
|
||||||
unCodablePacket, ok := packet.(*proto.UnCodablePacket)
|
unCodablePacket, ok := packet.(*proto.UnCodablePacket)
|
||||||
if ok {
|
if ok {
|
||||||
|
slog.Error("failed to decode packet", "error", err, "packet_name", info.Name)
|
||||||
if *CreateTestsPath != "" {
|
if *CreateTestsPath != "" {
|
||||||
SaveUnCodeAbleAsTest(unCodablePacket)
|
err = SaveUnCodeAbleAsTest(unCodablePacket)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to save uncodable packet as test", "error", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
failedPackets++
|
failedPackets++
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Packet: %#v\n", packet)
|
fmt.Printf("Packet: %#v\n", packet)
|
||||||
}
|
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
|
||||||
if errors.Is(err, io.EOF) {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
totalPackets++
|
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,253 @@
|
|||||||
|
package raw_capture
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/mc/api"
|
||||||
|
"github.com/admin-else/strom/mc/client"
|
||||||
|
"github.com/admin-else/strom/mc/data"
|
||||||
|
"github.com/admin-else/strom/mc/event"
|
||||||
|
"github.com/admin-else/strom/mc/proto"
|
||||||
|
"github.com/admin-else/strom/mc/proto/replay"
|
||||||
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
|
||||||
|
"github.com/admin-else/strom/mc/server"
|
||||||
|
"github.com/admin-else/strom/mc/text"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Cmd = flag.NewFlagSet("raw-capture", flag.ContinueOnError)
|
||||||
|
|
||||||
|
var OfflineNameFlag = Cmd.String("name", "raw_capture", "offline player name")
|
||||||
|
var Token = Cmd.String("token", "", "token to use")
|
||||||
|
|
||||||
|
var TargetAddr = Cmd.String("addr", "127.0.0.1:25565", "address to connect to")
|
||||||
|
var ListenAddr = Cmd.String("listen", "127.0.0.1:25566", "address to listen on")
|
||||||
|
var ClientboundFile = Cmd.String("clientbound-file", "clientbound.tmcpr", "file to write clientbound packets to")
|
||||||
|
var ServerboundFile = Cmd.String("serverbound-file", "serverbound.tmcpr", "file to write serverbound packets to")
|
||||||
|
var Version = Cmd.String("version", "", "client protocol version (e.g. 26.1); if empty, detect from server status")
|
||||||
|
|
||||||
|
var StatusResponse = server.StatusResponse{
|
||||||
|
Version: server.StatusResponseVersion{
|
||||||
|
Name: text.Pretty("STROM raw-capture"),
|
||||||
|
Protocol: 0,
|
||||||
|
},
|
||||||
|
Players: server.StatusResponsePlayers{},
|
||||||
|
Description: text.Pretty("STROM raw-capture proxy"),
|
||||||
|
Favicon: "",
|
||||||
|
EnforcesSecureChat: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Proxy struct {
|
||||||
|
Client, Servee *proto.Conn
|
||||||
|
|
||||||
|
clientboundFile *os.File
|
||||||
|
serverboundFile *os.File
|
||||||
|
|
||||||
|
clientboundWriter *replay.Writer
|
||||||
|
serverboundWriter *replay.Writer
|
||||||
|
|
||||||
|
startTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) Start() (err error) {
|
||||||
|
p.startTime = time.Now()
|
||||||
|
|
||||||
|
p.Client.RegisterCritical(p.OnAnything)
|
||||||
|
p.Servee.RegisterCritical(p.OnAnything)
|
||||||
|
|
||||||
|
p.Client.RegisterCriticalUntilLatest(p.OnCompress)
|
||||||
|
p.Servee.RegisterCriticalUntilLatest(p.OnHandshake)
|
||||||
|
p.Servee.RegisterCriticalUntilLatest(p.OnLoginAcknowledged)
|
||||||
|
p.Servee.RegisterCriticalUntilLatest(p.OnFinishConfiguration)
|
||||||
|
|
||||||
|
errChan := make(chan error)
|
||||||
|
go func() {
|
||||||
|
errChan <- p.Client.StartConn()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
errChan <- p.Servee.StartConn()
|
||||||
|
}()
|
||||||
|
err = <-errChan
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) timestamp() uint32 {
|
||||||
|
return uint32(time.Since(p.startTime).Milliseconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnAnything(e event.Anything) (err error) {
|
||||||
|
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
packetInfo, ok := proto.LookupPacketInfoByType(packet)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if packetInfo.Name == "encryption_begin" {
|
||||||
|
return fmt.Errorf("encryption requested by remote server; raw capture cannot continue")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch packetInfo.Direction {
|
||||||
|
case proto_base.ToServer:
|
||||||
|
err = p.serverboundWriter.WritePacket(packet, p.timestamp())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = p.Client.Send(packet)
|
||||||
|
case proto_base.ToClient:
|
||||||
|
err = p.clientboundWriter.WritePacket(packet, p.timestamp())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = p.Servee.Send(packet)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnCompress(packet *v1_21_11.LoginToClientPacketCompress) (err error) {
|
||||||
|
err = p.clientboundWriter.WritePacket(packet, p.timestamp())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = p.Servee.Send(packet)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Servee.SetCompressionThreshold(packet.Threshold)
|
||||||
|
p.Client.SetCompressionThreshold(packet.Threshold)
|
||||||
|
return event.DontForwardErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnHandshake(packet *v1_21_11.HandshakingToServerPacketSetProtocol) (err error) {
|
||||||
|
state := proto_base.State(packet.NextState)
|
||||||
|
p.Servee.SetState(state)
|
||||||
|
return event.DontForwardErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnLoginAcknowledged(packet *v1_21_11.LoginToServerPacketLoginAcknowledged) (err error) {
|
||||||
|
err = p.serverboundWriter.WritePacket(packet, p.timestamp())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = p.Client.Send(packet)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Servee.SetState(proto_base.Configuration)
|
||||||
|
p.Client.SetState(proto_base.Configuration)
|
||||||
|
return event.DontForwardErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnFinishConfiguration(packet *v1_21_11.ConfigurationToServerPacketFinishConfiguration) (err error) {
|
||||||
|
err = p.serverboundWriter.WritePacket(packet, p.timestamp())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = p.Client.Send(packet)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Servee.SetState(proto_base.Play)
|
||||||
|
p.Client.SetState(proto_base.Play)
|
||||||
|
return event.DontForwardErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(args []string) (err error) {
|
||||||
|
err = Cmd.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clientboundFile, err := os.Create(*ClientboundFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create clientbound file: %w", err)
|
||||||
|
}
|
||||||
|
defer clientboundFile.Close()
|
||||||
|
|
||||||
|
serverboundFile, err := os.Create(*ServerboundFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create serverbound file: %w", err)
|
||||||
|
}
|
||||||
|
defer serverboundFile.Close()
|
||||||
|
|
||||||
|
resolvedAddr, _, _, err := client.DoDNSChecked(nil, *TargetAddr, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetVersion string
|
||||||
|
var targetProtocol int32
|
||||||
|
if *Version == "" {
|
||||||
|
status, err := client.Status(nil, resolvedAddr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to detect server version: %w", err)
|
||||||
|
}
|
||||||
|
targetProtocol = status.Version.Protocol
|
||||||
|
versionInfo, err := data.LookUpVersionByProtocolVersion(targetProtocol)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unknown protocol version %d: %w", targetProtocol, err)
|
||||||
|
}
|
||||||
|
targetVersion = versionInfo.MinecraftVersion
|
||||||
|
} else {
|
||||||
|
versionInfo, err := data.LookUpVersionByName(*Version)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unknown version %s: %w", *Version, err)
|
||||||
|
}
|
||||||
|
targetVersion = versionInfo.MinecraftVersion
|
||||||
|
targetProtocol = versionInfo.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusResponse.Version.Name = text.Pretty("STROM raw-capture " + targetVersion)
|
||||||
|
StatusResponse.Version.Protocol = targetProtocol
|
||||||
|
StatusResponse.Description = text.Pretty(fmt.Sprintf("Raw capture proxy forwards to %s (%s)", resolvedAddr, targetVersion))
|
||||||
|
|
||||||
|
slog.Info("Starting raw capture", "listen", *ListenAddr, "target", resolvedAddr, "version", targetVersion, "protocol", targetProtocol, "clientbound", *ClientboundFile, "serverbound", *ServerboundFile)
|
||||||
|
|
||||||
|
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
|
||||||
|
proxy := &Proxy{
|
||||||
|
Servee: serveeConn,
|
||||||
|
clientboundFile: clientboundFile,
|
||||||
|
serverboundFile: serverboundFile,
|
||||||
|
clientboundWriter: replay.NewWriter(clientboundFile),
|
||||||
|
serverboundWriter: replay.NewWriter(serverboundFile),
|
||||||
|
}
|
||||||
|
|
||||||
|
acc := api.NewOfflineAccount(*OfflineNameFlag)
|
||||||
|
if *Token != "" {
|
||||||
|
acc, err = api.NewAccountFromYGG(*Token)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = server.ServeLogin(serveeConn, server.WithOtherAccount(acc), server.WithStatus(StatusResponse), server.WithoutOnlineMode(), server.WithoutEncryption())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := client.Connect(nil, resolvedAddr, targetVersion)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
err = client.LoginRaw(c, acc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy.Client = c
|
||||||
|
proxy.Client.AlwaysDecode = true
|
||||||
|
proxy.Servee.AlwaysDecode = true
|
||||||
|
|
||||||
|
return proxy.Start()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package registry_data_hunter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -6,16 +6,16 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
"github.com/admin-else/strom/mc/api"
|
||||||
"github.com/admin-else/strom/client"
|
"github.com/admin-else/strom/mc/client"
|
||||||
"github.com/admin-else/strom/data"
|
"github.com/admin-else/strom/mc/data"
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/mc/event"
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
|
||||||
"github.com/admin-else/strom/server"
|
"github.com/admin-else/strom/mc/server"
|
||||||
"github.com/admin-else/strom/text"
|
"github.com/admin-else/strom/mc/text"
|
||||||
)
|
)
|
||||||
|
|
||||||
func wontFail[T any](v T, err error) T {
|
func wontFail[T any](v T, err error) T {
|
||||||
@@ -25,10 +25,7 @@ func wontFail[T any](v T, err error) T {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// THIS IS REDUNDANT USE LOGIN PACKET IN MINECRAFT DATA
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Edit these two
|
|
||||||
Version = "1.21.11"
|
Version = "1.21.11"
|
||||||
SaveAs = "data/registry/" + Version + ".nbt"
|
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) {
|
func handleClient(c *proto.Conn) (err error) {
|
||||||
acc := api.NewOfflineAccount("RegistryHunter")
|
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))
|
_, err = server.ServeLogin(c, server.WithOtherAccount(acc), server.WithCompatibleVersions(CompatibleProtocolVersion), server.WithRawStatus(Status))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -150,10 +146,7 @@ func handleClient(c *proto.Conn) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func Run(args []string) error {
|
||||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||||
err := server.StartServerWithOnConn(":25566", handleClient)
|
return server.StartServerWithOnConn(":25566", handleClient)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -8,14 +8,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/mc/event"
|
||||||
"github.com/admin-else/strom/level"
|
"github.com/admin-else/strom/mc/level"
|
||||||
"github.com/admin-else/strom/level/anvil"
|
"github.com/admin-else/strom/mc/level/anvil"
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
|
||||||
"github.com/admin-else/strom/server"
|
"github.com/admin-else/strom/mc/server"
|
||||||
"github.com/admin-else/strom/util"
|
"github.com/admin-else/strom/mc/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
@@ -28,7 +28,7 @@ type Server struct {
|
|||||||
var FullLight [][]uint8
|
var FullLight [][]uint8
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
light := make([]uint8, 2048) // 4096 values 2 per uint8
|
light := make([]uint8, 2048)
|
||||||
for i := range light {
|
for i := range light {
|
||||||
light[i] = 0xFF
|
light[i] = 0xFF
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ func GetChunkFromMca(x, z int32, version string) (b []byte, err error) {
|
|||||||
z += anvil.ChunksWidthRegionFile
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -95,13 +95,11 @@ func Limbo(c *proto.Conn) (err error) {
|
|||||||
s := &Server{
|
s := &Server{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
}
|
}
|
||||||
//s.DebugPrintPackets = []string{""}
|
|
||||||
_, err = server.ServeLogin(s.Conn, server.WithoutOnlineMode())
|
_, err = server.ServeLogin(s.Conn, server.WithoutOnlineMode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//goland:noinspection GoResourceLeak captures the conn object which should NOT be closed
|
|
||||||
err = server.ServeConfig(s.Conn)
|
err = server.ServeConfig(s.Conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -176,10 +174,7 @@ func (s *Server) SendKeepAlive() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func Run(args []string) error {
|
||||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||||
err := server.StartServerWithOnConn(":25566", Limbo)
|
return server.StartServerWithOnConn(":25566", Limbo)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -6,11 +6,11 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/admin-else/strom/client"
|
"github.com/admin-else/strom/mc/client"
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/mc/event"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = flag.NewFlagSet("status", flag.ContinueOnError)
|
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.
|
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
|
||||||
// It does not resolve SRV records.
|
// It does not resolve SRV records.
|
||||||
func StatusRaw(addr string) (s *StatusClient, err error) {
|
func StatusRaw(addr string) (s *StatusClient, err error) {
|
||||||
c, err := client.ConnectVersionLess(addr)
|
c, err := client.ConnectVersionLess(nil, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ func Run(args []string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if *srvFlag {
|
if *srvFlag {
|
||||||
*addrFlag, err = client.DoDnsSimple(*addrFlag)
|
*addrFlag, _, _, err = client.DoDNSChecked(nil, *addrFlag, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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/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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:29fb706bffac7f1fb93f0fc62c201fdb1a0ca939fb53807362337f1ea2609737
|
|
||||||
size 16384
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:a870e120e4932cbfe231c85e1ca04b7e0dc455b3e4da331b8f3e0baf6c17946f
|
|
||||||
size 344064
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user