46 Commits
Author SHA1 Message Date
adman 84b28d4c12 enforce MaxPacketSize constraint for raw and compressed packets 2026-07-28 16:37:45 +02:00
adman 30cfc507ba less pollution 2026-07-27 15:05:50 +02:00
adman d84618c116 update minecraft data to fix bundle delimiter 2026-07-23 22:32:59 +02:00
adman 5a55520126 migrate proto_generated to be runable from repo root 2026-07-23 22:26:23 +02:00
adman 8ac5e00c8e regenerate protocol 2026-07-23 22:23:22 +02:00
adman d46c678b89 make mincraft data sync scripts executable from anywhere 2026-07-23 22:20:53 +02:00
adman 0b714edcf5 Sync minecraft-data 2026-07-23 22:14:19 +02:00
adman 45151add64 Remove proto_base.Void 2026-07-23 22:11:29 +02:00
adman b90830881f Fix 26.2 login_success protocol handling and add convertibility tests
- Server sends v26_2 login_success with SessionId for protocol >= 776

- Client adds v26_2 login_success handler and scopes v1_21_8 handler to <= 26.1

- Fix WithOtherAccount guard in login server

- Add smart-convert assumption tests for login server/client

- Improve translatePacketVersion error messages
2026-07-23 17:00:13 +02:00
adman 94301a4a50 Add 26.2 protocol support to strom 2026-07-23 15:06:49 +02:00
adman aa5d15a4dc Regenerate 26.1 protocol with corrected play packet IDs (clientbound + serverbound) 2026-07-23 00:09:43 +02:00
adman d388e2c350 add 26.1 support, raw-capture subcommand, and local minecraft-data copy script 2026-07-22 23:22:14 +02:00
adman 126ff5bca0 improve packet replay things 2026-07-20 12:56:19 +02:00
adman a347aedfc2 untrack big testdata 2026-07-16 22:56:44 +02:00
adman e324a95727 cmd take account with env var 2026-07-16 22:16:04 +02:00
adman 7f878f40a1 add world module for in-memory block storage and integrate getblock command in messenger 2026-07-16 16:05:42 +02:00
adman 7baa9990cb yes222 2026-07-16 14:02:28 +02:00
adman 5c5c391aa6 yes 2026-07-16 01:13:31 +02:00
adman b966925212 move data stuff 2026-07-11 23:08:03 +02:00
adman efd7049859 biome map stuff 2026-07-11 22:08:29 +02:00
adman 3ef9446f50 biome depth stuff 2026-07-06 11:49:45 +02:00
adman 68612969c9 fix spline test 2026-07-05 22:32:39 +02:00
adman b4712ff5c6 spline 2026-07-05 20:53:59 +02:00
adman eab050e291 fixes 2026-07-05 10:49:45 +02:00
adman a8b090227b mc gen start 2026-07-05 10:25:43 +02:00
adman 201e0dfa44 fix moving pains 2026-06-27 14:10:31 +02:00
adman 666f250b3e mc root source folder cleanup 2026-06-27 13:54:54 +02:00
adman b2784574bd move tools to strom cli 2026-06-27 13:06:05 +02:00
adman c0b1501f23 add packet spy json logging 2026-06-17 11:45:54 +02:00
adman 4de76d8023 regenerate proto 2026-06-16 23:08:00 +02:00
adman f08684fb67 fix bug in entity metadata encoding 2026-06-16 23:07:33 +02:00
adman 9e4f9409c5 add TestEntityMetadata 2026-06-16 23:06:58 +02:00
adman 4d3fbed957 add SimplePacketToBytesLenPrefix 2026-06-16 23:06:42 +02:00
adman b338888b58 add SimpleBytesToPacket utility function for decoding packets 2026-06-16 22:29:26 +02:00
adman 4956c1d874 add OnFinishLogin handling to packet inspector 2026-06-16 22:28:01 +02:00
adman d4fe362d9f add MustLookupProtocolVersion utility function for testing purposes 2026-06-16 22:27:41 +02:00
adman 5d8ff870df add true-forward mode to packet inspector 2026-06-02 21:06:41 +02:00
adman 58c9a60236 add optional encryption flag to login server and update ServeLogin behavior 2026-05-30 15:53:35 +02:00
adman 3e5b141ec7 add ToolSum function for build info and integrate it into Version outputs 2026-05-29 01:27:35 +02:00
adman efeb5ecda6 add file comment generation and hashing for reproducible output in proto_generator 2026-05-29 01:14:56 +02:00
adman 624e8ad4de add SimplePacketToBytes utility function for encoding packets 2026-05-28 23:59:54 +02:00
adman 764cfd8c4d lpvec3d 2026-05-14 10:42:38 +02:00
adman d43ff7f83d add seek-back handling in entity metadata decoding 2026-05-14 00:06:45 +02:00
adman a39999b344 update Decode function signatures to use io.ReadSeeker instead of io.Reader 2026-05-13 23:54:06 +02:00
adman 8ee48e1a45 add EntityMetadataLoop handling 2026-05-13 23:21:31 +02:00
adman bf6affeb97 remove unused protodef_types file from proto_generator 2026-05-13 22:20:32 +02:00
567 changed files with 1286314 additions and 52315 deletions
-1
View File
@@ -1,5 +1,4 @@
.devres/
.idea/
.failed_packets/
tools/.*/
.vscode/
-43
View File
@@ -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
View File
@@ -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
}
+3 -2
View File
@@ -4,9 +4,10 @@ import (
"encoding/json"
"errors"
"flag"
"log/slog"
"os"
"github.com/admin-else/strom/api"
"github.com/admin-else/strom/mc/api"
)
var cmd = flag.NewFlagSet("api", flag.ContinueOnError)
@@ -52,7 +53,7 @@ func Run(args []string) (err error) {
return
}
} else {
err = PleaseProvideArgumentErr
slog.Info("account", "name", a.Name, "uuid", a.Uuid)
return
}
err = json.NewEncoder(os.Stdout).Encode(data)
+11
View File
@@ -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 -7
View File
@@ -7,9 +7,9 @@ import (
"os"
"slices"
"github.com/admin-else/strom/data"
"github.com/admin-else/strom/proto_generated"
"github.com/admin-else/strom/util"
mcdata "github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/proto_generated"
"github.com/admin-else/strom/mc/util"
)
// strom data 1.21.11 blocks id 0 name => air
@@ -39,11 +39,11 @@ func Run(args []string) (err error) {
case 0: // print available versions
dataValues = proto_generated.SupportedVersions
case 1: // print available data for version
dataValues = slices.Collect(maps.Keys(data.Paths.Data[args[0]]))
dataValues = slices.Collect(maps.Keys(mcdata.Paths.Data[args[0]]))
case 2:
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
case 3:
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
if err != nil {
return
}
@@ -68,7 +68,7 @@ func Run(args []string) (err error) {
}
dataValues = result
case 4, 5:
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
if err != nil {
return
}
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"log/slog"
"os"
"github.com/admin-else/strom/level/anvil"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/mc/level/anvil"
"github.com/admin-else/strom/mc/nbt"
)
var (
+31 -10
View File
@@ -1,39 +1,53 @@
package main
import (
"debug/buildinfo"
"errors"
"fmt"
"log/slog"
"maps"
"os"
"runtime/debug"
"slices"
"github.com/admin-else/strom/cmd/strom/api"
"github.com/admin-else/strom/cmd/strom/data"
"github.com/admin-else/strom/cmd/strom/extract_mca"
"github.com/admin-else/strom/cmd/strom/mca_to_tmcpr"
"github.com/admin-else/strom/cmd/strom/messenger"
"github.com/admin-else/strom/cmd/strom/offline_uuid"
"github.com/admin-else/strom/cmd/strom/packet_info"
"github.com/admin-else/strom/cmd/strom/packet_inspector"
"github.com/admin-else/strom/cmd/strom/print_nbt"
"github.com/admin-else/strom/cmd/strom/print_replay"
"github.com/admin-else/strom/cmd/strom/raw_capture"
"github.com/admin-else/strom/cmd/strom/registry_data_hunter"
"github.com/admin-else/strom/cmd/strom/serve_tmcpr"
"github.com/admin-else/strom/cmd/strom/serve_world"
"github.com/admin-else/strom/cmd/strom/status"
)
var ExpectedASubcommandErr = errors.New("expected a subcommand")
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
func Version(args []string) (err error) {
selfPath, err := os.Executable()
if err != nil {
return
func ToolVersion() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return "devel"
}
info, err := buildinfo.ReadFile(selfPath)
if err != nil {
return
return info.Main.Version
}
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{
@@ -47,6 +61,12 @@ var subcommands = map[string]func(args []string) error{
"extract-mca": extract_mca.Run,
"data": data.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) {
@@ -59,6 +79,7 @@ func Help(args []string) (err error) {
func mainE() (err error) {
subcommands["help"] = Help
args := os.Args
//args = []string{"TEST", "messenger", "-addr", "localhost"}
if len(args) < 2 {
err = ExpectedASubcommandErr
+211
View File
@@ -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
}
+134
View File
@@ -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()
}
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"flag"
"fmt"
"github.com/admin-else/strom/crypto"
"github.com/admin-else/strom/mc/crypto"
)
var cmd = flag.NewFlagSet("offline-uuid", flag.ContinueOnError)
+4 -4
View File
@@ -5,10 +5,10 @@ import (
"fmt"
"strconv"
"github.com/admin-else/strom/data"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/proto_generated"
"github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated"
)
var cmd = flag.NewFlagSet("packet-info", flag.ContinueOnError)
+93 -19
View File
@@ -1,18 +1,21 @@
package packet_inspector
import (
"encoding/json"
"flag"
"log/slog"
"os"
"time"
"github.com/admin-else/strom/api"
"github.com/admin-else/strom/client"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/proto_generated/v1_21_11"
"github.com/admin-else/strom/server"
"github.com/admin-else/strom/text"
"github.com/admin-else/strom/mc/api"
"github.com/admin-else/strom/mc/client"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/nbt"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
"github.com/admin-else/strom/mc/server"
"github.com/admin-else/strom/mc/text"
)
var Cmd = flag.NewFlagSet("packet-inspector", flag.ContinueOnError)
@@ -22,6 +25,7 @@ var Token = Cmd.String("token", "", "token to use")
var TargetAddr = Cmd.String("addr", "127.0.0.1:25565", "address to connect to")
var ListenAddr = Cmd.String("listen", "127.0.0.1:25566", "address to listen on")
var TrueForward = Cmd.Bool("true-forward", false, "forward packets without modification -name and custom server status wont work")
type Proxy struct {
Client, Servee *proto.Conn
@@ -33,6 +37,10 @@ func (p *Proxy) Start() (err error) {
p.Servee.RegisterCritical(p.OnAnything)
p.Servee.RegisterCriticalUntilLatest(p.OnFinishConfiguration)
p.Servee.RegisterCriticalUntilLatest(p.OnFinishLogin)
p.Servee.RegisterCriticalUntilLatest(p.OnHandshake)
//p.Servee.RegisterCriticalUntilLatest(p.OnCompress)
p.Client.RegisterCriticalUntilLatest(p.OnCompress)
errChan := make(chan error)
go func() {
@@ -45,6 +53,22 @@ func (p *Proxy) Start() (err error) {
return
}
type PacketLogEvent struct {
Time time.Time
Packet proto_base.EncodeDecodeAble
Info PacketInfoNoTypeJson
ClientAddr string
}
type PacketInfoNoTypeJson struct {
Type proto_base.EncodeDecodeAble `json:"-"`
Name string
Direction proto_base.Direction
State proto_base.State
PacketId int32
ProtocolVersion int32
}
func (p *Proxy) OnAnything(e event.Anything) (err error) {
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
if !ok {
@@ -54,6 +78,19 @@ func (p *Proxy) OnAnything(e event.Anything) (err error) {
if !ok {
return
}
jsonBytes, err := json.Marshal(PacketLogEvent{
Time: time.Now(),
Packet: packet,
ClientAddr: p.Client.RemoteAddr().String(),
Info: PacketInfoNoTypeJson(packetInfo),
})
if err != nil {
return
}
// :pray:
_, _ = os.Stdout.Write(jsonBytes)
_, _ = os.Stdout.Write([]byte("\n"))
switch packetInfo.Direction {
case proto_base.ToServer:
err = p.Client.Send(packet)
@@ -74,6 +111,40 @@ func (p *Proxy) OnFinishConfiguration(packet *v1_21_11.ConfigurationToServerPack
return
}
func (p *Proxy) OnFinishLogin(packet *v1_21_11.LoginToServerPacketLoginAcknowledged) (err error) {
err = p.Client.Send(packet)
if err != nil {
return
}
p.Servee.SetState(proto_base.Configuration)
p.Client.SetState(proto_base.Configuration)
err = event.DontForwardErr
return
}
func (p *Proxy) OnHandshake(packet *v1_21_11.HandshakingToServerPacketSetProtocol) (err error) {
err = p.Client.Send(packet)
state := proto_base.State(packet.NextState)
if err != nil {
return
}
p.Servee.SetState(state)
p.Client.SetState(state)
err = event.DontForwardErr
return
}
func (p *Proxy) OnCompress(packet *v1_21_11.LoginToClientPacketCompress) (err error) {
err = p.Servee.Send(packet)
if err != nil {
return
}
p.Servee.SetCompressionThreshold(packet.Threshold)
p.Client.SetCompressionThreshold(packet.Threshold)
err = event.DontForwardErr
return
}
var StatusResponse = server.StatusResponse{
Version: server.StatusResponseVersion{
Name: text.Pretty("STROM"),
@@ -90,38 +161,41 @@ func Run(args []string) (err error) {
if err != nil {
return
}
slog.SetLogLoggerLevel(slog.LevelDebug)
slog.Info("Starting packet inspector", "listen", *ListenAddr)
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
serveeConn.DebugPrintPackets = []string{""}
p := &Proxy{Servee: serveeConn, Client: nil}
var acc = api.NewOfflineAccount(*OfflineNameFlag)
if !*TrueForward {
if *Token != "" {
acc, err = api.NewAccountFromYGG(*Token)
if err != nil {
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 {
return
}
}
*TargetAddr, err = client.DoDnsSimple(*TargetAddr)
*TargetAddr, _, _, err = client.DoDNSChecked(nil, *TargetAddr, nil)
if err != nil {
return
}
c, err := client.Connect(*TargetAddr)
if err != nil {
return
}
c.DebugPrintPackets = []string{""}
err = client.LoginRaw(c, acc)
c, err := client.Connect(nil, *TargetAddr, "")
if err != nil {
return
}
defer c.Close()
if !*TrueForward {
err = client.LoginRaw(c, acc)
if err != nil {
return
}
}
p.Client = c
p.Client.AlwaysDecode = true
p.Servee.AlwaysDecode = true
err = p.Start()
return
})
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"fmt"
"os"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/mc/nbt"
)
var (
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"bytes"
"testing"
generated "github.com/admin-else/strom/proto_generated/%v"
generated "github.com/admin-else/strom/mc/proto_generated/%v"
)
// Error: %v
+65 -28
View File
@@ -8,15 +8,16 @@ import (
"errors"
"flag"
"fmt"
"go/format"
"io"
"log/slog"
"os"
"reflect"
"strings"
"github.com/admin-else/strom/data"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
)
var (
@@ -25,6 +26,7 @@ var (
ProtocolVersion = cmd.Int("protocol", -1, "The protocol version")
CreateTestsPath = cmd.String("create-tests", "", "")
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
@@ -32,32 +34,43 @@ var (
//go:embed failed_packet.go.tmpl
var TestSrcF string
func SaveUnCodeAbleAsTest(packet *proto.UnCodablePacket) {
func SaveUnCodeAbleAsTest(packet *proto.UnCodablePacket) (err error) {
if packet.Info.Type == nil {
slog.Warn("skipping test for unknown packet id", "packet_id", packet.Info.PacketId, "state", packet.Info.State, "error", packet.Err)
return
}
hUntrimmed := sha256.Sum256(packet.Data)
h := hUntrimmed[:8]
f, err := os.Create(fmt.Sprintf(*CreateTestsPath+"/%v_%10x_test.go", len(packet.Data), h))
if err != nil {
panic(err)
if err = os.MkdirAll(*CreateTestsPath, 0755); err != nil {
return
}
ret, err := data.LookUpVersionByProtocolVersion(packet.Info.ProtocolVersion)
if err != nil {
panic(err)
return
}
goPackage := "v" + strings.ReplaceAll(ret.MinecraftVersion, ".", "_")
typeName := reflect.TypeOf(packet.Info.Type).Elem().Name()
//goland:noinspection GoUnhandledErrorResult
defer f.Close()
_, err = fmt.Fprintf(f, TestSrcF, goPackage, packet.Err, h, reflect.TypeOf(packet.Info.Type).Elem().Name(), packet.Data)
if err != nil {
panic(err)
src := fmt.Sprintf(TestSrcF, goPackage, packet.Err, h, typeName, packet.Data)
formatted, formatErr := format.Source([]byte(src))
if formatErr != nil {
formatted = []byte(src)
}
slog.Info("Packet failed to parse", "error", packet.Err, "saved", f.Name(), "data", fmt.Sprintf("%q", packet.Data))
filePath := fmt.Sprintf(*CreateTestsPath+"/%v_%10x_test.go", len(packet.Data), h)
if err = os.WriteFile(filePath, formatted, 0644); err != nil {
return
}
slog.Info("Packet failed to parse", "error", packet.Err, "saved", filePath, "data", fmt.Sprintf("%q", packet.Data))
return
}
var state = proto_base.Login
var direction = proto_base.ToClient
func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, err error) {
func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, i proto_base.PacketInfo, err error) {
var timestamp, length uint32
err = binary.Read(r, binary.BigEndian, &timestamp)
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 := int(b.Size()) - b.Len()
i, ok := proto.LookUpTypeByPacketInfo(proto_base.ToClient, state, packetId, int32(*ProtocolVersion))
var ok bool
i, ok = proto.LookUpTypeByPacketInfo(direction, state, packetId, int32(*ProtocolVersion))
if !ok {
packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: packetData, Info: i}
return
}
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)
err = packet.Decode(b)
if err != nil {
packet = &proto.UnCodablePacket{Err: err, Data: packetData[indexafterid:], Info: i}
return
}
switch i.Name {
case "success":
state = proto_base.Configuration
case "finish_configuration":
state = proto_base.Play
if b.Len() != 0 {
packet = &proto.UnCodablePacket{Err: proto.PacketNotFullyDecodedErr, Data: packetData[indexafterid:], Info: i}
return
}
return
}
@@ -128,6 +156,14 @@ func Run(args []string) (err error) {
default:
return fmt.Errorf("unknown state: %s", *StateFlag)
}
switch *DirectionFlag {
case "clientbound", "to-client":
direction = proto_base.ToClient
case "serverbound", "to-server":
direction = proto_base.ToServer
default:
return fmt.Errorf("unknown direction: %s", *DirectionFlag)
}
f, err := os.Open(*FileFlag)
if err != nil {
return
@@ -137,21 +173,22 @@ func Run(args []string) (err error) {
var totalPackets, failedPackets int
for {
var packet proto_base.EncodeDecodeAble
packet, err = UnpackPacket(f)
var info proto_base.PacketInfo
packet, info, err = UnpackPacket(f)
unCodablePacket, ok := packet.(*proto.UnCodablePacket)
if ok {
slog.Error("failed to decode packet", "error", err, "packet_name", info.Name)
if *CreateTestsPath != "" {
SaveUnCodeAbleAsTest(unCodablePacket)
err = SaveUnCodeAbleAsTest(unCodablePacket)
if err != nil {
slog.Error("failed to save uncodable packet as test", "error", err)
}
}
failedPackets++
}
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("Packet: %#v\n", packet)
}
if errors.Is(err, io.EOF) {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
break
}
totalPackets++
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2d90ee976987678593a6b1d09136bb9b4fd131390796ea6e413376a6ddc63226
size 75035693
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1 +0,0 @@
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be007aa681f7959708e89e39e05390e228753291823a18274068d310a50aa78b
size 526306397
-1
View File
@@ -1 +0,0 @@
270566013
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0da58f5cdd65ef241df8c61f9e633af7317faa77034183d4fb648b039c1e14ed
size 842
-1
View File
@@ -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
View File
@@ -1 +0,0 @@
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:52fa049f97918289e018fa4c3bf5ff789d4f99f44f590ad875ce1f65e5fa9563
size 37
@@ -1 +0,0 @@
1105715802
+253
View File
@@ -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 (
"encoding/json"
@@ -6,16 +6,16 @@ import (
"log/slog"
"os"
"github.com/admin-else/strom/api"
"github.com/admin-else/strom/client"
"github.com/admin-else/strom/data"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/proto_generated/v1_21_8"
"github.com/admin-else/strom/server"
"github.com/admin-else/strom/text"
"github.com/admin-else/strom/mc/api"
"github.com/admin-else/strom/mc/client"
"github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/nbt"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/server"
"github.com/admin-else/strom/mc/text"
)
func wontFail[T any](v T, err error) T {
@@ -25,10 +25,7 @@ func wontFail[T any](v T, err error) T {
return v
}
// THIS IS REDUNDANT USE LOGIN PACKET IN MINECRAFT DATA
var (
// Edit these two
Version = "1.21.11"
SaveAs = "data/registry/" + Version + ".nbt"
@@ -113,7 +110,6 @@ func (p *Proxy) OnTags(data *v1_21_8.ConfigurationToClientPacketTags) (err error
func handleClient(c *proto.Conn) (err error) {
acc := api.NewOfflineAccount("RegistryHunter")
// ignore resource leak warning we own c conn so we should not close it here
_, err = server.ServeLogin(c, server.WithOtherAccount(acc), server.WithCompatibleVersions(CompatibleProtocolVersion), server.WithRawStatus(Status))
if err != nil {
return
@@ -150,10 +146,7 @@ func handleClient(c *proto.Conn) (err error) {
return
}
func main() {
func Run(args []string) error {
slog.SetLogLoggerLevel(slog.LevelDebug)
err := server.StartServerWithOnConn(":25566", handleClient)
if err != nil {
panic(err)
}
return server.StartServerWithOnConn(":25566", handleClient)
}
+97
View File
@@ -0,0 +1,97 @@
package serve_tmcpr
import (
"errors"
"flag"
"io"
"log/slog"
"math/rand/v2"
"os"
"time"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto/replay"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/server"
)
type Server struct {
*proto.Conn
*event.Timer
}
var (
cmd = flag.NewFlagSet("serve-tmcpr", flag.ContinueOnError)
tmcprFile = cmd.String("file", "/tmp/world.tmcpr", "Path to .tmcpr replay file")
bindAddr = cmd.String("bind", ":25566", "Address to listen on")
)
func Limbo(c *proto.Conn, tmcprPath string) (err error) {
s := &Server{Conn: c}
_, err = server.ServeLogin(s.Conn, server.WithoutOnlineMode())
if err != nil {
return
}
err = server.ServeConfig(s.Conn)
if err != nil {
return
}
f, err := os.Open(tmcprPath)
if err != nil {
return
}
defer f.Close()
r := replay.NewReader(f)
r.Version = c.ProtocolVersion
start := time.Now()
for {
var packetData []byte
var timestamp uint32
packetData, timestamp, _, err = r.ReadRaw()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
time.Sleep(time.Duration(timestamp)*time.Millisecond - time.Since(start))
err = s.SendRaw(packetData)
if err != nil {
return err
}
}
s.Timer = &event.Timer{}
s.Timer.Every(time.Second*15, s.SendKeepAlive)
s.Timer.Start(c.Loop)
return s.StartConn()
}
func (s *Server) SendKeepAlive() error {
return s.Send(&v1_21_8.PlayToClientPacketKeepAlive{KeepAliveId: rand.Int64()})
}
func Run(args []string) error {
slog.SetLogLoggerLevel(slog.LevelDebug)
err := cmd.Parse(args)
if err != nil {
return err
}
if *tmcprFile == "" {
slog.Error("no file specified")
return nil
}
return server.StartServerWithOnConn(*bindAddr, func(c *proto.Conn) error {
return Limbo(c, *tmcprFile)
})
}
@@ -1,4 +1,4 @@
package main
package serve_world
import (
"bytes"
@@ -8,14 +8,14 @@ import (
"os"
"time"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/level"
"github.com/admin-else/strom/level/anvil"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_generated/v1_21_8"
"github.com/admin-else/strom/server"
"github.com/admin-else/strom/util"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/level"
"github.com/admin-else/strom/mc/level/anvil"
"github.com/admin-else/strom/mc/nbt"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/server"
"github.com/admin-else/strom/mc/util"
)
type Server struct {
@@ -28,7 +28,7 @@ type Server struct {
var FullLight [][]uint8
func init() {
light := make([]uint8, 2048) // 4096 values 2 per uint8
light := make([]uint8, 2048)
for i := range light {
light[i] = 0xFF
}
@@ -52,7 +52,7 @@ func GetChunkFromMca(x, z int32, version string) (b []byte, err error) {
z += anvil.ChunksWidthRegionFile
}
f, err := os.Open(fmt.Sprintf("./level/anvil/testdata/securechattestworld/region/r.%v.%v.mca", xmca, zmca))
f, err := os.Open(fmt.Sprintf("./.devres/anvil_testdata/securechattestworld/region/r.%v.%v.mca", xmca, zmca))
if err != nil {
return
}
@@ -95,13 +95,11 @@ func Limbo(c *proto.Conn) (err error) {
s := &Server{
Conn: c,
}
//s.DebugPrintPackets = []string{""}
_, err = server.ServeLogin(s.Conn, server.WithoutOnlineMode())
if err != nil {
return
}
//goland:noinspection GoResourceLeak captures the conn object which should NOT be closed
err = server.ServeConfig(s.Conn)
if err != nil {
return
@@ -176,10 +174,7 @@ func (s *Server) SendKeepAlive() (err error) {
return
}
func main() {
func Run(args []string) error {
slog.SetLogLoggerLevel(slog.LevelDebug)
err := server.StartServerWithOnConn(":25566", Limbo)
if err != nil {
panic(err)
}
return server.StartServerWithOnConn(":25566", Limbo)
}
+7 -7
View File
@@ -6,11 +6,11 @@ import (
"log/slog"
"time"
"github.com/admin-else/strom/client"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/client"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
)
var cmd = flag.NewFlagSet("status", flag.ContinueOnError)
@@ -46,7 +46,7 @@ func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
// It does not resolve SRV records.
func StatusRaw(addr string) (s *StatusClient, err error) {
c, err := client.ConnectVersionLess(addr)
c, err := client.ConnectVersionLess(nil, addr)
if err != nil {
return
}
@@ -82,7 +82,7 @@ func Run(args []string) (err error) {
return
}
if *srvFlag {
*addrFlag, err = client.DoDnsSimple(*addrFlag)
*addrFlag, _, _, err = client.DoDNSChecked(nil, *addrFlag, nil)
if err != nil {
return
}
-2
View File
@@ -2,7 +2,5 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
-3
View File
@@ -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.
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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.
@@ -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