Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96481da0a4 | ||
|
|
159437c1ca | ||
|
|
3d34ad59e9 | ||
|
|
4878c6cc7b | ||
|
|
719a3bacd8 | ||
|
|
ce0c58e7c5 | ||
|
|
8a4e44760e | ||
|
|
e810a60848 | ||
|
|
0981339028 | ||
|
|
7746dd8ee9 | ||
|
|
378b78312e | ||
|
|
bd3139754d | ||
|
|
841d91b91d | ||
|
|
895ec53698 | ||
|
|
f6001c8b22 | ||
|
|
d87c362270 | ||
|
|
236e414242 | ||
|
|
5222390b65 | ||
|
|
dc04fdc7d0 | ||
|
|
8de34dfda1 | ||
|
|
997594d7c3 | ||
|
|
c83a777eac | ||
|
|
65fea61667 | ||
|
|
c2777f2ca9 | ||
|
|
dcb42f1970 | ||
|
|
7a2081f0a0 | ||
|
|
acc583f694 | ||
|
|
76409d07a3 | ||
|
|
bb89730781 | ||
|
|
9f6b1f6bcf | ||
|
|
1b8a9677c6 | ||
|
|
79ab13ca4b | ||
|
|
3cc414185a | ||
|
|
fe4a17dce2 | ||
|
|
891551f9dd | ||
|
|
8fb48c6ea9 | ||
|
|
ec25c28061 | ||
|
|
45287f47d2 | ||
|
|
4cc4b6af5b | ||
|
|
09e6760d61 | ||
|
|
ca74a3c3d4 | ||
|
|
fbc3693219 | ||
|
|
fb69fd93ec | ||
|
|
a3a3c3714e | ||
|
|
9294631c6c | ||
|
|
293f0b1eed | ||
|
|
272dcb4d36 | ||
|
|
abb0fa4d37 | ||
|
|
e5212624af | ||
|
|
35772aca03 | ||
|
|
819d89b26e | ||
|
|
958102e43c | ||
|
|
f749d3bfc5 | ||
|
|
d207cf52cf | ||
|
|
420ec5611c | ||
|
|
4bfaf9b7c5 | ||
|
|
48beb861e8 | ||
|
|
9fd2f517d9 | ||
|
|
1b5af63b75 | ||
|
|
dfb847eb3e | ||
|
|
322bd77f7f | ||
|
|
aefe850281 | ||
|
|
57f88f2176 | ||
|
|
8dd566c0da | ||
|
|
e386a45d96 | ||
|
|
8f771166a6 | ||
|
|
5d6a1a25ea | ||
|
|
ca33185f20 | ||
|
|
e921e9e9d2 | ||
|
|
7337068d86 |
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
.devres/
|
.devres/
|
||||||
.idea/
|
.idea/
|
||||||
.failed_packets/
|
.failed_packets/
|
||||||
tools/.*/
|
tools/.*/
|
||||||
|
.vscode/
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# STROM
|
||||||
|
|
||||||
|
# THIS IS WORK IN PROGRESS AND THE API WILL BREAK AS LONG AS THE THE PACKAGE VERSION IS v0.x.x
|
||||||
|
|
||||||
|
feel free to contact me on [discord](https://discord.gg/n2Vjc2vcMf)
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BlockedServers() (s []string, err error) {
|
||||||
|
resp, err := http.Get("https://sessionserver.mojang.com/blockedservers")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
d, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s = strings.Split(string(d), "\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProfileProperty struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Signature string `json:"signature,omitempty"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProfileResp struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Legacy bool `json:"legacy"`
|
||||||
|
Properties []ProfileProperty `json:"properties"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasJoined(name, server, ip string) (p ProfileResp, err error) {
|
||||||
|
url := "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + name + "&serverId=" + server
|
||||||
|
if ip != "" {
|
||||||
|
url += "&ip=" + ip
|
||||||
|
}
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
err = fmt.Errorf("bad status code %v", resp.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type MojangPublicKeysResp struct {
|
||||||
|
ProfilePropertyKeys []string `json:"profilePropertyKeys"`
|
||||||
|
PlayerCertificateKeys []string `json:"playerCertificateKeys"`
|
||||||
|
|
||||||
|
// AuthenticationKeys are undocumented by wiki.vg but they exist as of the time of writing so ill include them
|
||||||
|
AuthenticationKeys []string `json:"authenticationKeys"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func MojangPublicKeys() (keys MojangPublicKeysResp, err error) {
|
||||||
|
resp, err := http.Get("https://sessionserver.mojang.com/session/minecraft/profile/public_key_by_token")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&keys)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Profile(id string, unsinged bool) (p ProfileResp, err error) {
|
||||||
|
url := "https://sessionserver.mojang.com/session/minecraft/profile/" + id
|
||||||
|
if !unsinged {
|
||||||
|
url += "?unsigned=false"
|
||||||
|
}
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdName struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NameToId(name string) (idName IdName, err error) {
|
||||||
|
// may also use these:
|
||||||
|
//https://api.mojang.com/users/profiles/minecraft/<player name>
|
||||||
|
//https://api.minecraftservices.com/minecraft/profile/lookup/name/<player name>
|
||||||
|
//https://api.mojang.com/minecraft/profile/lookup/name/<player name>
|
||||||
|
resp, err := http.Get("https://api.minecraftservices.com/minecraft/profile/lookup/name/" + name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&idName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NameToIdBulk(names []string) (idNames []IdName, err error) {
|
||||||
|
b := bytes.NewBuffer(nil)
|
||||||
|
err = json.NewEncoder(b).Encode(names)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// may also use these:
|
||||||
|
//https://api.mojang.com/profiles/minecraft
|
||||||
|
//https://api.minecraftservices.com/minecraft/profile/lookup/bulk/byname
|
||||||
|
//https://api.mojang.com/minecraft/profile/lookup/bulk/byname
|
||||||
|
r, err := http.NewRequest("POST", "https://api.minecraftservices.com/minecraft/profile/lookup/bulk/byname", b)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Header.Set("Content-Type", "application/json")
|
||||||
|
resp, err := http.DefaultClient.Do(r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&idNames)
|
||||||
|
return
|
||||||
|
}
|
||||||
+8
-7
@@ -1,9 +1,9 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/admin-else/strom/event"
|
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/proto_base"
|
||||||
)
|
)
|
||||||
@@ -23,16 +23,17 @@ func Connect(addr string) (ret *proto.Conn, err error) {
|
|||||||
return
|
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
|
// ConnectVersionLess connects to the given address without setting the version you should set it
|
||||||
// when using this function.
|
// when using this function.
|
||||||
// You must close the connection when you're done with it
|
// You must close the connection when you're done with it
|
||||||
func ConnectVersionLess(addr string) (ret *proto.Conn, err error) {
|
func ConnectVersionLessCtx(ctx context.Context, addr string) (ret *proto.Conn, err error) {
|
||||||
ret = &proto.Conn{}
|
ret = proto.NewConnCtx(ctx)
|
||||||
ret.SetState(proto_base.Handshaking)
|
|
||||||
ret.SetCompressionThreshold(-1)
|
|
||||||
ret.Actor = proto_base.Client
|
ret.Actor = proto_base.Client
|
||||||
ret.Loop = event.NewLoop()
|
ret.Conn, err = (&net.Dialer{}).DialContext(ctx, "tcp", addr)
|
||||||
ret.Conn, err = net.Dial("tcp", addr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package client
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
|
||||||
|
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/event"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/proto"
|
||||||
@@ -20,7 +19,7 @@ func (c *ConfigIgnorer) OnStart() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigIgnorer) Default(e event.Anything) (err error) {
|
func (c *ConfigIgnorer) Default(e event.Anything) (err error) {
|
||||||
slog.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", e))
|
c.Log.Debug("Config ignorer", "packet", fmt.Sprintf("%#v", e))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-6
@@ -6,7 +6,6 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
"github.com/admin-else/strom/api"
|
||||||
"github.com/admin-else/strom/crypto"
|
"github.com/admin-else/strom/crypto"
|
||||||
@@ -42,7 +41,10 @@ func (k KickedDuringLoginErr) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LoginClient) OnDefault(event event.Unhandled) (err error) {
|
func (s *LoginClient) OnDefault(event event.Unhandled) (err error) {
|
||||||
err = fmt.Errorf("unexpected event: %#v", event)
|
if _, ok := event.Val.(proto_base.EncodeDecodeAble); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Log.Warn("unexpected packet during login client", "packet", event.Val)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,10 +132,10 @@ func LoginRaw(c *proto.Conn, account *api.Account, addr string) (err error) {
|
|||||||
}
|
}
|
||||||
lc.RegisterCritical(lc.OnDefault)
|
lc.RegisterCritical(lc.OnDefault)
|
||||||
lc.RegisterCritical(lc.OnClose)
|
lc.RegisterCritical(lc.OnClose)
|
||||||
lc.RegisterUntilLatest(lc.OnCompress)
|
lc.RegisterCriticalUntilLatest(lc.OnCompress)
|
||||||
lc.RegisterUntilLatest(lc.OnEncrypt)
|
lc.RegisterCriticalUntilLatest(lc.OnEncrypt)
|
||||||
lc.RegisterUntilLatest(lc.OnDisconnect)
|
lc.RegisterCriticalUntilLatest(lc.OnDisconnect)
|
||||||
lc.RegisterUntilLatest(lc.OnSuccess)
|
lc.RegisterCriticalUntilLatest(lc.OnSuccess)
|
||||||
|
|
||||||
p, err := MakeHandshakePacket(lc.Conn, proto_base.Login)
|
p, err := MakeHandshakePacket(lc.Conn, proto_base.Login)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
|
|||||||
// StatusRaw returns a StatusClient that is not connected to a server.
|
// StatusRaw returns a StatusClient that is not connected to a server.
|
||||||
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
|
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
|
||||||
// It does not resolve SRV records.
|
// It does not resolve SRV records.
|
||||||
func StatusRaw(addr string) (s *StatusClient, err error) {
|
func StatusRaw(ctx context.Context, addr string) (s *StatusClient, err error) {
|
||||||
c, err := ConnectVersionLess(addr)
|
c, err := ConnectVersionLess(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -72,7 +73,7 @@ func StatusRaw(addr string) (s *StatusClient, err error) {
|
|||||||
|
|
||||||
// StatusNoDns is like Status but does not resolve SRV records.
|
// StatusNoDns is like Status but does not resolve SRV records.
|
||||||
func StatusNoDns(addr string) (status server.StatusResponse, err error) {
|
func StatusNoDns(addr string) (status server.StatusResponse, err error) {
|
||||||
s, err := StatusRaw(addr)
|
s, err := StatusRaw(context.Background(), addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmd = flag.NewFlagSet("api", flag.ContinueOnError)
|
||||||
|
var tokenFlag = cmd.String("token", os.Getenv("MC_TOKEN"), "token to use")
|
||||||
|
|
||||||
|
var renameFlag = cmd.String("rename", "", "rename the player")
|
||||||
|
|
||||||
|
var uuidFlag = cmd.String("uuid", "", "get data about player with uuid")
|
||||||
|
var nameFlag = cmd.String("name", "", "get data about player with name")
|
||||||
|
var unsignedFlag = cmd.Bool("unsigned", true, "use unsigned mode for properties signatures")
|
||||||
|
|
||||||
|
var PleaseProvideArgumentErr = errors.New("please provide an argument")
|
||||||
|
|
||||||
|
func Run(args []string) (err error) {
|
||||||
|
err = cmd.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a := &api.Account{}
|
||||||
|
if *tokenFlag != "" {
|
||||||
|
a, err = api.NewAccountFromYGG(*tokenFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var data any
|
||||||
|
if *renameFlag != "" {
|
||||||
|
data, err = a.ChangeName(*renameFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if *uuidFlag != "" || *nameFlag != "" {
|
||||||
|
if *nameFlag != "" {
|
||||||
|
var idName api.IdName
|
||||||
|
idName, err = api.NameToId(*nameFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*uuidFlag = idName.Id
|
||||||
|
}
|
||||||
|
data, err = api.Profile(*uuidFlag, *unsignedFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = PleaseProvideArgumentErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = json.NewEncoder(os.Stdout).Encode(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
+5
-3
@@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/cmd/strom/api"
|
||||||
"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_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"
|
||||||
@@ -15,13 +16,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var ExpectedASubcommandErr = errors.New("expected a subcommand")
|
var ExpectedASubcommandErr = errors.New("expected a subcommand")
|
||||||
var UnknownSubcommandErr = errors.New("unknown subcommand")
|
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
|
||||||
|
|
||||||
var subcommands = map[string]func(args []string) error{
|
var subcommands = map[string]func(args []string) error{
|
||||||
"offline-uuid": offline_uuid.Run,
|
"offline-uuid": offline_uuid.Run,
|
||||||
"print-nbt": print_nbt.Run,
|
"print-nbt": print_nbt.Run,
|
||||||
"status": status.Run,
|
"status": status.Run,
|
||||||
"packet-spy": packet_inspector.Run,
|
"packet-spy": packet_inspector.Run,
|
||||||
|
"api": api.Run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func Help(args []string) (err error) {
|
func Help(args []string) (err error) {
|
||||||
@@ -36,8 +38,8 @@ func mainE() (err error) {
|
|||||||
args := os.Args
|
args := os.Args
|
||||||
|
|
||||||
// Debug stuff
|
// Debug stuff
|
||||||
//slog.SetLogLoggerLevel(slog.LevelDebug)
|
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||||
//args = []string{"TEST", "status", "-srv", "-addr", "tcpshield.1f2d.net:25565"}
|
//args = []string{"TEST", "api", "-name", "Admin_Else"}
|
||||||
|
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
err = ExpectedASubcommandErr
|
err = ExpectedASubcommandErr
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ func Run(args []string) (err error) {
|
|||||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
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 *Token != "" {
|
if *Token != "" {
|
||||||
@@ -105,7 +106,17 @@ func Run(args []string) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c, err := client.Login(*TargetAddr, acc)
|
|
||||||
|
*TargetAddr, err = client.DoDnsSimple(*TargetAddr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := client.Connect(*TargetAddr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.DebugPrintPackets = []string{""}
|
||||||
|
err = client.LoginRaw(c, acc, *TargetAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func Run(args []string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
n, err := nbt.ReadFile(f)
|
n, err := nbt.ReadUnstructuredFile(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,78 @@ package status
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/admin-else/strom/client"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = flag.NewFlagSet("status", flag.ContinueOnError)
|
var cmd = flag.NewFlagSet("status", flag.ContinueOnError)
|
||||||
var addrFlag = cmd.String("addr", "localhost:25565", "address to connect to")
|
var addrFlag = cmd.String("addr", "localhost:25565", "address to connect to")
|
||||||
var srvFlag = cmd.Bool("srv", false, "use SRV records to resolve the address")
|
var srvFlag = cmd.Bool("srv", false, "use SRV records to resolve the address")
|
||||||
|
var versionFlag = cmd.String("version", "1.21.8", "version to use")
|
||||||
|
|
||||||
|
type StatusClient struct {
|
||||||
|
*proto.Conn
|
||||||
|
Status string
|
||||||
|
PingSendTime, PingReceiveTime time.Time
|
||||||
|
DoPingRoundTripTime bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusClient) OnStatus(p *v1_21_8.StatusToClientPacketServerInfo) (err error) {
|
||||||
|
s.Status = p.Response
|
||||||
|
s.PingSendTime = time.Now()
|
||||||
|
if s.DoPingRoundTripTime {
|
||||||
|
err = s.Send(&v1_21_8.StatusToServerPacketPing{Time: s.PingSendTime.UnixMilli()})
|
||||||
|
} else {
|
||||||
|
err = event.HandlerDoneErr{}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
|
||||||
|
s.PingReceiveTime = time.Now()
|
||||||
|
err = event.HandlerDoneErr{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusRaw returns a StatusClient that is not connected to a server.
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
err = c.SetVersion(*versionFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s = &StatusClient{
|
||||||
|
Conn: c,
|
||||||
|
}
|
||||||
|
s.RegisterCriticalUntilLatest(s.OnStatus)
|
||||||
|
s.RegisterCriticalUntilLatest(s.OnPong)
|
||||||
|
|
||||||
|
p, err := client.MakeHandshakePacketAddr(s.Conn, proto_base.Status, addr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = s.Send(p)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.SetState(proto_base.Status)
|
||||||
|
err = s.Send(&v1_21_8.StatusToServerPacketPingStart{})
|
||||||
|
|
||||||
|
err = s.StartConn()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func Run(args []string) (err error) {
|
func Run(args []string) (err error) {
|
||||||
err = cmd.Parse(args)
|
err = cmd.Parse(args)
|
||||||
@@ -22,8 +87,9 @@ func Run(args []string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
slog.Debug("resolved address to ", "addr", *addrFlag)
|
||||||
|
|
||||||
status, err := client.StatusRaw(*addrFlag)
|
status, err := StatusRaw(*addrFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-6
@@ -1,5 +1,7 @@
|
|||||||
package data
|
package data
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
// [
|
// [
|
||||||
//
|
//
|
||||||
// {
|
// {
|
||||||
@@ -14,7 +16,7 @@ package data
|
|||||||
// },
|
// },
|
||||||
|
|
||||||
type Biome struct {
|
type Biome struct {
|
||||||
Id int
|
Id int32
|
||||||
Name string
|
Name string
|
||||||
Category string
|
Category string
|
||||||
Temperature float64
|
Temperature float64
|
||||||
@@ -24,22 +26,35 @@ type Biome struct {
|
|||||||
Color int
|
Color int
|
||||||
}
|
}
|
||||||
|
|
||||||
var BiomesCache = make(map[string][]Biome)
|
var BiomesCache = make(map[string][]*Biome)
|
||||||
|
|
||||||
func BiomesForVersion(v string) (ret []Biome) {
|
func BiomesForVersion(v string) (ret []*Biome) {
|
||||||
var ok bool
|
var ok bool
|
||||||
if ret, ok = BiomesCache[v]; ok {
|
if ret, ok = BiomesCache[v]; ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var b []Biome
|
var b []*Biome
|
||||||
must(LoadVersionedJson(v, "biomes", &b))
|
must(LoadVersionedJson(v, "biomes", &b))
|
||||||
BiomesCache[v] = b
|
BiomesCache[v] = b
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func LookupBiomeById(version string, id int32) (biome Biome, ok bool) {
|
func LookupBiomeById(version string, id int32) (biome *Biome, ok bool) {
|
||||||
for _, b := range BiomesForVersion(version) {
|
for _, b := range BiomesForVersion(version) {
|
||||||
if b.Id == int(id) {
|
if b.Id == id {
|
||||||
|
biome = b
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var UnknownBiomeNameErr = errors.New("unknown biome Name")
|
||||||
|
|
||||||
|
func LookupBiomeByName(version string, name string) (biome *Biome, ok bool) {
|
||||||
|
for _, b := range BiomesForVersion(version) {
|
||||||
|
if b.Name == name {
|
||||||
biome = b
|
biome = b
|
||||||
ok = true
|
ok = true
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ func LookupBlockByStateId(version string, stateId int32) (block *Block, ok bool)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var UnknownBlockNameErr = errors.New("unknown block Name")
|
||||||
|
|
||||||
func LookupBlockByName(version string, name string) (block *Block, ok bool) {
|
func LookupBlockByName(version string, name string) (block *Block, ok bool) {
|
||||||
for _, b := range BlocksForVersion(version) {
|
for _, b := range BlocksForVersion(version) {
|
||||||
if b.Name == name {
|
if b.Name == name {
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed minecraft-data/data
|
//go:embed minecraft-data/data/pc minecraft-data/data/dataPaths.json
|
||||||
var MinecraftData embed.FS
|
var MinecraftData embed.FS
|
||||||
|
|
||||||
func LoadJson(path string, data any) (err error) {
|
func LoadJson(path string, data any) (err error) {
|
||||||
|
|||||||
+3
-1
@@ -6,6 +6,8 @@ import (
|
|||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/nbt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: this is stupid loginPacket.json exists next version migrate to that
|
||||||
|
|
||||||
//go:embed registry/*
|
//go:embed registry/*
|
||||||
var Registry embed.FS
|
var Registry embed.FS
|
||||||
|
|
||||||
@@ -15,6 +17,6 @@ func LoadRegistry(name string) (n *nbt.Tag, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer r.Close()
|
defer r.Close()
|
||||||
n, err = nbt.ReadFile(r)
|
n, err = nbt.ReadUnstructuredFile(r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
+38
-35
@@ -13,7 +13,7 @@ type Loop struct {
|
|||||||
HandlerFunctions map[reflect.Type][]reflect.Value
|
HandlerFunctions map[reflect.Type][]reflect.Value
|
||||||
|
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
cancel context.CancelFunc
|
Cancel context.CancelFunc
|
||||||
eventSources []reflect.SelectCase
|
eventSources []reflect.SelectCase
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,9 +67,6 @@ func (e MainHandlerErr) Unwrap() error {
|
|||||||
// DontForwardErr will stop the event from being forwarded to other handlers, including anything handlers.
|
// DontForwardErr will stop the event from being forwarded to other handlers, including anything handlers.
|
||||||
var DontForwardErr = errors.New("dont forward")
|
var DontForwardErr = errors.New("dont forward")
|
||||||
var WhileClosingErr = errors.New("error while closing")
|
var WhileClosingErr = errors.New("error while closing")
|
||||||
|
|
||||||
// CloseNonCriticalErr will close even when the event is non-critical
|
|
||||||
var CloseNonCriticalErr = errors.New("close non-critical")
|
|
||||||
var ContextDoneErr = errors.New("context done")
|
var ContextDoneErr = errors.New("context done")
|
||||||
var RecvNotOkErr = errors.New("recv not ok")
|
var RecvNotOkErr = errors.New("recv not ok")
|
||||||
|
|
||||||
@@ -115,40 +112,37 @@ func (l *Loop) RegisterEventSource(ch <-chan any) {
|
|||||||
// Instead, it will log it to slog.Error
|
// Instead, it will log it to slog.Error
|
||||||
// Warning: If a bad handler is passed in the program will panic.
|
// Warning: If a bad handler is passed in the program will panic.
|
||||||
func (l *Loop) Register(h any) {
|
func (l *Loop) Register(h any) {
|
||||||
l.RegisterCustomType(ValidateHandler(h))
|
eventType, hv := ValidateHandler(h)
|
||||||
|
l.RegisterLow(eventType, hv, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterCustomType registers a handler that will not close the loop if it returns an error.
|
// RegisterLow registers a handler that will not close the loop if it returns an error.
|
||||||
// Instead, it will log it to slog.Error
|
// Instead, it will log it to slog.Error
|
||||||
|
// This does not hold true for signaling errors like HandlerDoneErr or DontForwardErr
|
||||||
// Warning: If a bad handler is passed in the program will panic.
|
// Warning: If a bad handler is passed in the program will panic.
|
||||||
func (l *Loop) RegisterCustomType(eventType reflect.Type, hv reflect.Value) {
|
func (l *Loop) RegisterLow(eventType reflect.Type, hv reflect.Value, critical bool) {
|
||||||
handleFunc := func(packet any) error {
|
handleFunc := hv
|
||||||
v := hv.Call([]reflect.Value{reflect.ValueOf(packet)})[0]
|
if !critical {
|
||||||
if !v.IsNil() {
|
handleFunc = reflect.ValueOf(func(packet any) error {
|
||||||
err := v.Interface().(error)
|
v := hv.Call([]reflect.Value{reflect.ValueOf(packet)})[0]
|
||||||
if errors.Is(err, CloseNonCriticalErr) {
|
if !v.IsNil() {
|
||||||
return err
|
err := v.Interface().(error)
|
||||||
|
if errors.As(err, &HandlerDoneErr{}) || errors.Is(err, DontForwardErr) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slog.Error("handler failed", "err", err)
|
||||||
}
|
}
|
||||||
slog.Error("handler failed", "err", err)
|
return nil
|
||||||
}
|
})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
l.RegisterDirect(eventType, reflect.ValueOf(handleFunc))
|
l.HandlerFunctions[eventType] = append(l.HandlerFunctions[eventType], handleFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterCritical registers a handler that will close the loop if it returns an error.
|
// RegisterCritical registers a handler that will close the loop if it returns an error.
|
||||||
// Warning: If a bad handler is passed in the program will panic.
|
// Warning: If a bad handler is passed in the program will panic.
|
||||||
func (l *Loop) RegisterCritical(h any) {
|
func (l *Loop) RegisterCritical(h any) {
|
||||||
eventType, hv := ValidateHandler(h)
|
eventType, hv := ValidateHandler(h)
|
||||||
l.RegisterDirect(eventType, hv)
|
l.HandlerFunctions[eventType] = append(l.HandlerFunctions[eventType], hv)
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Loop) RegisterDirect(k reflect.Type, h reflect.Value) {
|
|
||||||
l.HandlerFunctions[k] = append(l.HandlerFunctions[k], h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Loop) RegisterIgnore(t any) {
|
|
||||||
l.RegisterDirect(reflect.TypeOf(t), reflect.ValueOf(func(any) error { return nil }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FireFound triggers all handlers registered for the specified event type and returns if handlers were found or an error occurred.
|
// FireFound triggers all handlers registered for the specified event type and returns if handlers were found or an error occurred.
|
||||||
@@ -192,26 +186,29 @@ func (l *Loop) Fire(event any) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
_, err = l.FireFound(Unhandled{event})
|
found, err = l.FireFound(Unhandled{event})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = l.FireFound(Anything{event})
|
found, err = l.FireFound(Anything{event})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loop) ContextDoneListener() (chSending <-chan any) {
|
func (l *Loop) ContextDoneListener() (chSending <-chan any) {
|
||||||
ch := make(chan any)
|
ch := make(chan any)
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
<-l.Ctx.Done()
|
||||||
case <-l.Ctx.Done():
|
ch <- ContextDoneErr
|
||||||
ch <- ContextDoneErr
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Loop) IsTypeRegistered(t reflect.Type) bool {
|
||||||
|
_, ok := l.HandlerFunctions[t]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func (l *Loop) StartLoop() (err error) {
|
func (l *Loop) StartLoop() (err error) {
|
||||||
_ = *l // exit early on nil loop
|
_ = *l // exit early on nil loop
|
||||||
l.RegisterEventSource(l.ContextDoneListener())
|
l.RegisterEventSource(l.ContextDoneListener())
|
||||||
@@ -226,19 +223,25 @@ func (l *Loop) StartLoop() (err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if errors.Is(err, HandlerDoneErr{}) {
|
var contextDoneErr HandlerDoneErr
|
||||||
err = errors.Unwrap(err)
|
if errors.As(err, &contextDoneErr) {
|
||||||
|
err = contextDoneErr.Return
|
||||||
}
|
}
|
||||||
closeErr := l.Fire(Close{err})
|
closeErr := l.Fire(Close{err})
|
||||||
if closeErr != nil {
|
if closeErr != nil {
|
||||||
err = errors.Join(err, errors.Join(WhileClosingErr, closeErr))
|
err = errors.Join(err, errors.Join(WhileClosingErr, closeErr))
|
||||||
}
|
}
|
||||||
|
l.Cancel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoop() (l *Loop) {
|
func NewLoop() (l *Loop) {
|
||||||
|
return NewLoopCtx(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoopCtx(ctx context.Context) (l *Loop) {
|
||||||
l = &Loop{}
|
l = &Loop{}
|
||||||
l.Ctx, l.cancel = context.WithCancel(context.Background())
|
l.Ctx, l.Cancel = context.WithCancel(ctx)
|
||||||
l.HandlerFunctions = make(map[reflect.Type][]reflect.Value)
|
l.HandlerFunctions = make(map[reflect.Type][]reflect.Value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-31
@@ -21,33 +21,6 @@ type OnceTask struct {
|
|||||||
type Timer struct {
|
type Timer struct {
|
||||||
Once []OnceTask
|
Once []OnceTask
|
||||||
Interval []IntervalTask
|
Interval []IntervalTask
|
||||||
Active bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Timer) Tick() (err error) {
|
|
||||||
if !t.Active {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i, task := range t.Interval {
|
|
||||||
if time.Since(task.LastTrigger) >= task.Interval {
|
|
||||||
task.LastTrigger = time.Now()
|
|
||||||
err = task.F()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Interval[i] = task
|
|
||||||
}
|
|
||||||
for i, task := range t.Once {
|
|
||||||
if task.Trigger.After(time.Now()) {
|
|
||||||
err = task.F()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Once = slices.Delete(t.Once, i, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Timer) Trigger(ch chan any) {
|
func (t *Timer) Trigger(ch chan any) {
|
||||||
@@ -76,23 +49,26 @@ func (t *Timer) Trigger(ch chan any) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
time.Sleep(triggerAt.Sub(now))
|
sleepFor := triggerAt.Sub(now)
|
||||||
|
//slog.Debug("sleep next", "dur", sleepFor)
|
||||||
|
time.Sleep(sleepFor)
|
||||||
if iOnce >= 0 {
|
if iOnce >= 0 {
|
||||||
ch <- CallFunc{F: reflect.ValueOf(t.Once[iOnce].F), Args: []reflect.Value{}}
|
ch <- CallFunc{F: reflect.ValueOf(t.Once[iOnce].F), Args: []reflect.Value{}}
|
||||||
t.Once = slices.Delete(t.Once, iOnce, iOnce+1)
|
t.Once = slices.Delete(t.Once, iOnce, iOnce+1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if iInterval >= 0 {
|
if iInterval >= 0 {
|
||||||
|
t.Interval[iInterval].LastTrigger = triggerAt
|
||||||
ch <- CallFunc{F: reflect.ValueOf(t.Interval[iInterval].F), Args: []reflect.Value{}}
|
ch <- CallFunc{F: reflect.ValueOf(t.Interval[iInterval].F), Args: []reflect.Value{}}
|
||||||
t.Interval[iInterval].LastTrigger = now
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
func (t *Timer) Every(interval time.Duration, f func() error) {
|
func (t *Timer) Every(interval time.Duration, f func() error) {
|
||||||
t.Interval = append(t.Interval, IntervalTask{
|
t.Interval = append(t.Interval, IntervalTask{
|
||||||
F: f,
|
F: f,
|
||||||
Interval: interval,
|
Interval: interval,
|
||||||
|
LastTrigger: time.Now(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package anvil
|
||||||
|
|
||||||
|
type Chunk struct {
|
||||||
|
DataVersion int32
|
||||||
|
Heightmaps map[string][]int64
|
||||||
|
InhabitedTime int64
|
||||||
|
XPos, YPos, ZPos int32
|
||||||
|
Sections []Section
|
||||||
|
}
|
||||||
|
|
||||||
|
type Section struct {
|
||||||
|
Y int8
|
||||||
|
BlockStates struct {
|
||||||
|
Data []int64
|
||||||
|
Palette []struct {
|
||||||
|
Name string
|
||||||
|
Properties map[string]string `nbt:"properties,omitempty"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Biomes []struct {
|
||||||
|
Data []int64
|
||||||
|
Palette []string
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
package anvil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/nbt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Level struct {
|
||||||
|
ClearWeatherTime int32 `nbt:"clearWeatherTime"`
|
||||||
|
DataPacks DataPacks
|
||||||
|
DayTime int64
|
||||||
|
WanderingTraderSpawnChance int32
|
||||||
|
GameType int32
|
||||||
|
WorldGenSettings WorldGenSettings
|
||||||
|
ScheduledEvents []interface{}
|
||||||
|
WasModded bool
|
||||||
|
ThunderTime int32 `nbt:"thunderTime"`
|
||||||
|
Difficulty int8
|
||||||
|
DragonFight DragonFight
|
||||||
|
Initialized bool
|
||||||
|
AllowCommands bool `nbt:"allowCommands"`
|
||||||
|
Thundering bool
|
||||||
|
LastPlayed int64
|
||||||
|
GameRules map[string]interface{}
|
||||||
|
RainTime int32 `nbt:"rainTime"`
|
||||||
|
DataVersion int32
|
||||||
|
Version Version
|
||||||
|
WanderingTraderSpawnDelay int32
|
||||||
|
Hardcore bool
|
||||||
|
Spawn Spawn
|
||||||
|
Raining bool
|
||||||
|
Player Player
|
||||||
|
DifficultyLocked bool
|
||||||
|
LevelName string
|
||||||
|
Time int64
|
||||||
|
ServerBrands []string
|
||||||
|
CustomBossEvents map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataPacks struct {
|
||||||
|
Enabled []string
|
||||||
|
Disabled []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorldGenSettings struct {
|
||||||
|
BonusChest bool
|
||||||
|
Seed int64
|
||||||
|
GenerateFeatures bool
|
||||||
|
Dimensions map[string]Dimension
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dimensions struct {
|
||||||
|
Overworld Dimension
|
||||||
|
Nether Dimension
|
||||||
|
End Dimension
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dimension struct {
|
||||||
|
Generator Generator
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Generator struct {
|
||||||
|
Type string
|
||||||
|
Settings string
|
||||||
|
BiomeSource BiomeSource
|
||||||
|
}
|
||||||
|
|
||||||
|
type BiomeSource struct {
|
||||||
|
Preset string `nbt:"preset,omitempty"`
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DragonFight struct {
|
||||||
|
NeedsStateScanning bool
|
||||||
|
Gateways []int32
|
||||||
|
DragonKilled bool
|
||||||
|
PreviouslyKilled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Version struct {
|
||||||
|
Snapshot bool
|
||||||
|
Series string
|
||||||
|
Id int32
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Spawn struct {
|
||||||
|
Dimension string
|
||||||
|
Yaw float32
|
||||||
|
Pos [3]int32
|
||||||
|
Pitch float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
XpLevel int32
|
||||||
|
HurtTime int16
|
||||||
|
DeathTime int16
|
||||||
|
XpTotal int32
|
||||||
|
FoodExhaustionLevel float32
|
||||||
|
RecipeBook RecipeBook
|
||||||
|
Fire int16
|
||||||
|
Brain Brain
|
||||||
|
XpSeed int32
|
||||||
|
XpP float32
|
||||||
|
EnderItems []interface{}
|
||||||
|
Invulnerable bool
|
||||||
|
Pos [3]float64
|
||||||
|
Inventory []InventoryItem
|
||||||
|
SleepTimer int16
|
||||||
|
Abilities Abilities
|
||||||
|
PlayerGameType int32
|
||||||
|
SeenCredits bool
|
||||||
|
FoodSaturationLevel float32
|
||||||
|
FallDistance float64
|
||||||
|
OnGround bool
|
||||||
|
Dimension string
|
||||||
|
PortalCooldown int32
|
||||||
|
Motion [3]float64
|
||||||
|
IgnoreFallDamageFromCurrentExplosion bool
|
||||||
|
Rotation [2]float32
|
||||||
|
CurrentImpulseContextResetGraceTime int32
|
||||||
|
WardenSpawnTracker WardenSpawnTracker
|
||||||
|
PreviousPlayerGameType int32
|
||||||
|
Attributes []Attribute
|
||||||
|
HurtByTimestamp int32
|
||||||
|
UUID [4]int32
|
||||||
|
Score int32
|
||||||
|
DataVersion int32
|
||||||
|
FoodLevel int32
|
||||||
|
SpawnExtraParticlesOnFall bool
|
||||||
|
SelectedItemSlot int32
|
||||||
|
FoodTickTimer int32
|
||||||
|
FallFlying bool
|
||||||
|
AbsorptionAmount float32
|
||||||
|
Health float32
|
||||||
|
Air int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecipeBook struct {
|
||||||
|
Recipes []string
|
||||||
|
ToBeDisplayed []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Brain struct {
|
||||||
|
Memories map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryItem struct {
|
||||||
|
Count int32
|
||||||
|
Slot int8
|
||||||
|
Id string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Abilities struct {
|
||||||
|
Invulnerable bool
|
||||||
|
Mayfly bool
|
||||||
|
Instabuild bool
|
||||||
|
WalkSpeed float32
|
||||||
|
MayBuild bool
|
||||||
|
Flying bool
|
||||||
|
FlySpeed float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type WardenSpawnTracker struct {
|
||||||
|
WarningLevel int32
|
||||||
|
TicksSinceLastWarning int32
|
||||||
|
CooldownTicks int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attribute struct {
|
||||||
|
Id string
|
||||||
|
Base float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLevelData(r io.Reader) (l Level, err error) {
|
||||||
|
var lDataWrapped struct{ Data Level }
|
||||||
|
err = nbt.ReadFile(r, &lDataWrapped)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l = lDataWrapped.Data
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package anvil_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/level/anvil"
|
||||||
|
"github.com/admin-else/strom/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLevel(t *testing.T) {
|
||||||
|
f, err := os.Open("./testdata/testworld/level.dat")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
l, err := anvil.ReadLevelData(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegionFile(t *testing.T) {
|
||||||
|
f, err := os.Open("./testdata/testworld/region/r.0.0.mca")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
ch, err := anvil.ReadChunkHeader(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := ch.ChunkAt(f, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(util.MustT(json.MarshalIndent(n, "", " "))))
|
||||||
|
fmt.Println("Chunk loaded successfully")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package anvil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"compress/zlib"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/nbt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CompressionStrategy uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ChunksWidthRegionFile = 32
|
||||||
|
ChunksRegionFile = ChunksWidthRegionFile * ChunksWidthRegionFile
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ CompressionStrategy = iota
|
||||||
|
CompressionStrategyGZIP
|
||||||
|
CompressionStrategyZLIB
|
||||||
|
CompressionStrategyNONE
|
||||||
|
CompressionStrategyLZ4
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c CompressionStrategy) Wrap(r io.ReadCloser) (rNew io.ReadCloser, shouldClose bool, err error) {
|
||||||
|
switch c {
|
||||||
|
case CompressionStrategyGZIP:
|
||||||
|
shouldClose = true
|
||||||
|
rNew, err = gzip.NewReader(r)
|
||||||
|
return
|
||||||
|
case CompressionStrategyZLIB:
|
||||||
|
shouldClose = true
|
||||||
|
rNew, err = zlib.NewReader(r)
|
||||||
|
return
|
||||||
|
case CompressionStrategyNONE:
|
||||||
|
rNew = r
|
||||||
|
return
|
||||||
|
case CompressionStrategyLZ4:
|
||||||
|
err = fmt.Errorf("lz4 compression not supported")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unknown compression strategy %d", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChunkHeader struct {
|
||||||
|
Chunk, Time [ChunksRegionFile]int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadChunkHeader(r *os.File) (ch *ChunkHeader, err error) {
|
||||||
|
var h ChunkHeader
|
||||||
|
err = binary.Read(r, binary.BigEndian, &h)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ch = &h
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChunkHeader) ChunkAt(f *os.File, x, z int32) (n *nbt.Tag, err error) {
|
||||||
|
off, size := chunkLocation(c.Chunk[chunkXZtoIndex(x, z)])
|
||||||
|
_, err = f.Seek(int64(off), 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = size // i dont really know why mc has this maybe to point out how much space is wasted?
|
||||||
|
var realSize int32
|
||||||
|
err = binary.Read(f, binary.BigEndian, &realSize)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var compressionStrategy CompressionStrategy
|
||||||
|
err = binary.Read(f, binary.BigEndian, &compressionStrategy)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r, shouldClose, err := compressionStrategy.Wrap(f)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if shouldClose {
|
||||||
|
defer r.Close()
|
||||||
|
}
|
||||||
|
n = &nbt.Tag{}
|
||||||
|
err = n.Decode(r)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func chunkXZtoIndex(x, z int32) int32 {
|
||||||
|
return (x % ChunksWidthRegionFile) + (z%ChunksWidthRegionFile)*ChunksWidthRegionFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func chunkLocation(v int32) (offset, l int32) {
|
||||||
|
return ((v >> 8) & 0xFFFFFF) * 4096, (v & 0xFF) * 4096
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+215
@@ -0,0 +1,215 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
+220
@@ -0,0 +1,220 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
+222
@@ -0,0 +1,222 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
+119
@@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
+220
@@ -0,0 +1,220 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
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.
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.
@@ -0,0 +1 @@
|
|||||||
|
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.
BIN
Binary file not shown.
BIN
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.
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.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
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.
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.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user