Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e324a95727 | ||
|
|
7f878f40a1 | ||
|
|
7baa9990cb | ||
|
|
5c5c391aa6 | ||
|
|
b966925212 | ||
|
|
efd7049859 | ||
|
|
3ef9446f50 | ||
|
|
68612969c9 | ||
|
|
b4712ff5c6 | ||
|
|
eab050e291 | ||
|
|
a8b090227b | ||
|
|
201e0dfa44 | ||
|
|
666f250b3e | ||
|
|
b2784574bd | ||
|
|
c0b1501f23 | ||
|
|
4de76d8023 | ||
|
|
f08684fb67 | ||
|
|
9e4f9409c5 | ||
|
|
4d3fbed957 | ||
|
|
b338888b58 | ||
|
|
4956c1d874 | ||
|
|
d4fe362d9f | ||
|
|
5d8ff870df | ||
|
|
58c9a60236 | ||
|
|
3e5b141ec7 | ||
|
|
efeb5ecda6 | ||
|
|
624e8ad4de | ||
|
|
764cfd8c4d | ||
|
|
d43ff7f83d | ||
|
|
a39999b344 | ||
|
|
8ee48e1a45 | ||
|
|
bf6affeb97 | ||
|
|
ede9a47ca2 | ||
|
|
2fb290572a | ||
|
|
021b8ddc1a | ||
|
|
05e8da2ff4 | ||
|
|
a8936d92a5 | ||
|
|
c56898e787 | ||
|
|
c483bf9553 | ||
|
|
b5ee17d1ae | ||
|
|
57cef25e27 | ||
|
|
148302c94d | ||
|
|
02c402251d | ||
|
|
c800c5d471 | ||
|
|
477f51cce7 | ||
|
|
60103ebf47 | ||
|
|
21eeebf0f7 | ||
|
|
bb6f6548d5 | ||
|
|
d6c07cf428 | ||
|
|
e98921755d | ||
|
|
60383fede1 | ||
|
|
3bfa70d384 | ||
|
|
60147fd4b6 | ||
|
|
fe51287cc6 | ||
|
|
a44c5f8606 | ||
|
|
d5979b2c3d | ||
|
|
4b95ea9ae2 | ||
|
|
a80fd29297 | ||
|
|
589f35d147 | ||
|
|
c7b5f6ba0f | ||
|
|
0600315e69 | ||
|
|
12005df68b | ||
|
|
600842a679 | ||
|
|
c93d907d46 | ||
|
|
be6d2fb461 | ||
|
|
b2df88203d | ||
|
|
62d12386e1 | ||
|
|
00ea02a529 | ||
|
|
e5231dfdb6 | ||
|
|
3b1b436ecf | ||
|
|
47180bf650 | ||
|
|
325db98b99 | ||
|
|
ceae543352 | ||
|
|
aef9615476 | ||
|
|
5d3860b91e |
@@ -1,2 +1,6 @@
|
|||||||
**/*_test.go export-ignore
|
**/*_test.go export-ignore
|
||||||
**/testdata export-ignore
|
**/testdata export-ignore
|
||||||
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tmcpr filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.mcpr filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.mca filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|||||||
@@ -14,15 +14,22 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout with LFS
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
lfs: true
|
||||||
|
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: '1.25'
|
go-version: '1.26.2'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: go build -v ./...
|
run: go build -v ./...
|
||||||
|
|
||||||
|
- name: Vet
|
||||||
|
run: go vet -v ./...
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test -v ./...
|
run: go test -v ./...
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
.devres/
|
.devres/
|
||||||
.idea/
|
.idea/
|
||||||
.failed_packets/
|
.failed_packets/
|
||||||
tools/.*/
|
|
||||||
.vscode/
|
.vscode/
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/admin-else/strom/proto"
|
|
||||||
"github.com/admin-else/strom/proto_base"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Connect connects to the given address note that it does not resolve the SRV records it's just a raw net.Dial
|
|
||||||
// You must close the connection when you're done with it
|
|
||||||
func Connect(addr string) (ret *proto.Conn, err error) {
|
|
||||||
s, err := Status(addr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ret, err = ConnectVersionLess(addr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = ret.SetProtocolVersion(s.Version.Protocol)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ConnectVersionLess(addr string) (ret *proto.Conn, err error) {
|
|
||||||
return ConnectVersionLessCtx(context.Background(), addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectVersionLess connects to the given address without setting the version you should set it
|
|
||||||
// when using this function.
|
|
||||||
// You must close the connection when you're done with it
|
|
||||||
func ConnectVersionLessCtx(ctx context.Context, addr string) (ret *proto.Conn, err error) {
|
|
||||||
ret = proto.NewConnCtx(ctx)
|
|
||||||
ret.Actor = proto_base.Client
|
|
||||||
ret.Conn, err = (&net.Dialer{}).DialContext(ctx, "tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ret.R = ret.Conn
|
|
||||||
ret.W = ret.Conn
|
|
||||||
return
|
|
||||||
}
|
|
||||||
-126
@@ -1,126 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DNSCheckFunc func(addr string) error
|
|
||||||
|
|
||||||
// DoDns resolves a DNS name to an IP address
|
|
||||||
// It should resolve a minecraft compatible address into something net.Dial
|
|
||||||
func DoDns(in string) (out string, host string, portN uint16, err error) {
|
|
||||||
return DoDNSChecked(in, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoDNSChecked resolves a DNS name to an IP address
|
|
||||||
// It should resolve a minecraft compatible address into something net.Dial
|
|
||||||
// It also optionally accepts a function to "check" the address
|
|
||||||
func DoDNSChecked(in string, f DNSCheckFunc) (connectTo string, host string, portN uint16, err error) {
|
|
||||||
// https://mcsrc.dev/1/26.1-pre-1/net/minecraft/client/multiplayer/resolver/ServerAddress#L34
|
|
||||||
host, port, err := net.SplitHostPort(in)
|
|
||||||
if err != nil {
|
|
||||||
var addrErr *net.AddrError
|
|
||||||
if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
|
|
||||||
err = nil
|
|
||||||
host = in
|
|
||||||
port = "25565"
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
porti, err := strconv.Atoi(port)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f == nil {
|
|
||||||
f = func(addr string) error { return nil }
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f(host)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
portN = uint16(porti)
|
|
||||||
outIP, err := net.ResolveIPAddr("ip", host)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = f(outIP.String())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
connectTo = outIP.String() + ":" + port
|
|
||||||
if portN != 25565 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // otherwise this takes 10+ seconds
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
r := &net.Resolver{}
|
|
||||||
|
|
||||||
_, srvs, _ := r.LookupSRV(ctx, "minecraft", "tcp", host)
|
|
||||||
if len(srvs) == 0 {
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
host = strings.TrimSuffix(srvs[0].Target, ".")
|
|
||||||
portN = srvs[0].Port
|
|
||||||
outIP, err = net.ResolveIPAddr("ip", host)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = f(outIP.String())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
connectTo = outIP.String() + ":" + port
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DoDnsSimple resolves a DNS name to an IP address
|
|
||||||
// It should resolve a minecraft compatible address into something net.Dial
|
|
||||||
// It does not check the address
|
|
||||||
func DoDnsSimple(in string) (out string, err error) {
|
|
||||||
host, port, err := net.SplitHostPort(in)
|
|
||||||
if err != nil {
|
|
||||||
var addrErr *net.AddrError
|
|
||||||
if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
|
|
||||||
err = nil
|
|
||||||
host = in
|
|
||||||
port = "25565"
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
porti, err := strconv.Atoi(port)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
portN := uint16(porti)
|
|
||||||
_, err = net.ResolveIPAddr("ip", host)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
out = host + ":" + strconv.Itoa(int(portN))
|
|
||||||
if portN != 25565 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // otherwise this takes 10+ seconds
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
r := &net.Resolver{}
|
|
||||||
|
|
||||||
_, srvs, _ := r.LookupSRV(ctx, "minecraft", "tcp", host)
|
|
||||||
if len(srvs) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
host = strings.TrimSuffix(srvs[0].Target, ".")
|
|
||||||
portN = srvs[0].Port
|
|
||||||
out = host + ":" + strconv.Itoa(int(portN))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -4,9 +4,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
"github.com/admin-else/strom/mc/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = flag.NewFlagSet("api", flag.ContinueOnError)
|
var cmd = flag.NewFlagSet("api", flag.ContinueOnError)
|
||||||
@@ -52,7 +53,7 @@ func Run(args []string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = PleaseProvideArgumentErr
|
slog.Info("account", "name", a.Name, "uuid", a.Uuid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = json.NewEncoder(os.Stdout).Encode(data)
|
err = json.NewEncoder(os.Stdout).Encode(data)
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package cmd_util
|
||||||
|
|
||||||
|
import "github.com/admin-else/strom/mc/api"
|
||||||
|
|
||||||
|
func Account(s string) (a *api.Account, err error) {
|
||||||
|
if len(s) > 16 {
|
||||||
|
return api.NewAccountFromYGG(s)
|
||||||
|
}
|
||||||
|
a = api.NewOfflineAccount(s)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
mcdata "github.com/admin-else/strom/mc/data"
|
||||||
"github.com/admin-else/strom/proto_generated"
|
"github.com/admin-else/strom/mc/proto_generated"
|
||||||
"github.com/admin-else/strom/util"
|
"github.com/admin-else/strom/mc/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// strom data 1.21.11 blocks id 0 name => air
|
// strom data 1.21.11 blocks id 0 name => air
|
||||||
@@ -39,11 +39,11 @@ func Run(args []string) (err error) {
|
|||||||
case 0: // print available versions
|
case 0: // print available versions
|
||||||
dataValues = proto_generated.SupportedVersions
|
dataValues = proto_generated.SupportedVersions
|
||||||
case 1: // print available data for version
|
case 1: // print available data for version
|
||||||
dataValues = slices.Collect(maps.Keys(data.Paths.Data[args[0]]))
|
dataValues = slices.Collect(maps.Keys(mcdata.Paths.Data[args[0]]))
|
||||||
case 2:
|
case 2:
|
||||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||||
case 3:
|
case 3:
|
||||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ func Run(args []string) (err error) {
|
|||||||
}
|
}
|
||||||
dataValues = result
|
dataValues = result
|
||||||
case 4, 5:
|
case 4, 5:
|
||||||
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
|
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/admin-else/strom/level/anvil"
|
"github.com/admin-else/strom/mc/level/anvil"
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
+42
-25
@@ -1,48 +1,70 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/buildinfo"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/admin-else/strom/cmd/strom/api"
|
"github.com/admin-else/strom/cmd/strom/api"
|
||||||
"github.com/admin-else/strom/cmd/strom/data"
|
"github.com/admin-else/strom/cmd/strom/data"
|
||||||
"github.com/admin-else/strom/cmd/strom/extract_mca"
|
"github.com/admin-else/strom/cmd/strom/extract_mca"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/mca_to_tmcpr"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/messenger"
|
||||||
"github.com/admin-else/strom/cmd/strom/offline_uuid"
|
"github.com/admin-else/strom/cmd/strom/offline_uuid"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/packet_info"
|
||||||
"github.com/admin-else/strom/cmd/strom/packet_inspector"
|
"github.com/admin-else/strom/cmd/strom/packet_inspector"
|
||||||
"github.com/admin-else/strom/cmd/strom/print_nbt"
|
"github.com/admin-else/strom/cmd/strom/print_nbt"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/print_replay"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/registry_data_hunter"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/serve_tmcpr"
|
||||||
|
"github.com/admin-else/strom/cmd/strom/serve_world"
|
||||||
"github.com/admin-else/strom/cmd/strom/status"
|
"github.com/admin-else/strom/cmd/strom/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ExpectedASubcommandErr = errors.New("expected a subcommand")
|
var ExpectedASubcommandErr = errors.New("expected a subcommand")
|
||||||
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
|
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
|
||||||
|
|
||||||
func Version(args []string) (err error) {
|
func ToolVersion() string {
|
||||||
selfPath, err := os.Executable()
|
info, ok := debug.ReadBuildInfo()
|
||||||
if err != nil {
|
if !ok {
|
||||||
return
|
return "devel"
|
||||||
}
|
}
|
||||||
info, err := buildinfo.ReadFile(selfPath)
|
return info.Main.Version
|
||||||
if err != nil {
|
}
|
||||||
return
|
|
||||||
|
func ToolSum() string {
|
||||||
|
info, ok := debug.ReadBuildInfo()
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
fmt.Println("strom cli ", info.Main.Version, " (", info.Main.Sum, ")")
|
return info.Main.Sum
|
||||||
return
|
}
|
||||||
|
|
||||||
|
func Version(args []string) error {
|
||||||
|
fmt.Println("strom cli ", ToolVersion(), " (", ToolSum(), ")")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var subcommands = map[string]func(args []string) error{
|
var subcommands = map[string]func(args []string) error{
|
||||||
"offline-uuid": offline_uuid.Run,
|
"offline-uuid": offline_uuid.Run,
|
||||||
"print-nbt": print_nbt.Run,
|
"packet-info": packet_info.Run,
|
||||||
"status": status.Run,
|
"print-nbt": print_nbt.Run,
|
||||||
"packet-spy": packet_inspector.Run,
|
"status": status.Run,
|
||||||
"api": api.Run,
|
"packet-spy": packet_inspector.Run,
|
||||||
"version": Version,
|
"api": api.Run,
|
||||||
"extract-mca": extract_mca.Run,
|
"version": Version,
|
||||||
"data": data.Run,
|
"extract-mca": extract_mca.Run,
|
||||||
|
"data": data.Run,
|
||||||
|
"print-replay": print_replay.Run,
|
||||||
|
"registry-data-hunter": registry_data_hunter.Run,
|
||||||
|
"serve-world": serve_world.Run,
|
||||||
|
"mca-to-tmcpr": mca_to_tmcpr.Run,
|
||||||
|
"serve-tmcpr": serve_tmcpr.Run,
|
||||||
|
"messenger": messenger.Run,
|
||||||
}
|
}
|
||||||
|
|
||||||
func Help(args []string) (err error) {
|
func Help(args []string) (err error) {
|
||||||
@@ -53,14 +75,9 @@ func Help(args []string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mainE() (err error) {
|
func mainE() (err error) {
|
||||||
subcommands["help"] = Help // this is here because if it isn't tools/cli/main.go:17:5: initialization cycle for subcommands
|
subcommands["help"] = Help
|
||||||
// tools/cli/main.go:17:5: subcommands refers to Help
|
|
||||||
// tools/cli/main.go:22:6: Help refers to subcommands
|
|
||||||
args := os.Args
|
args := os.Args
|
||||||
|
//args = []string{"TEST", "messenger", "-addr", "localhost"}
|
||||||
// Debug stuff
|
|
||||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
|
||||||
//args = []string{"TEST", "version"}
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
err = ExpectedASubcommandErr
|
err = ExpectedASubcommandErr
|
||||||
|
|||||||
@@ -0,0 +1,211 @@
|
|||||||
|
package mca_to_tmcpr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/mc/level"
|
||||||
|
"github.com/admin-else/strom/mc/level/anvil"
|
||||||
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
|
"github.com/admin-else/strom/mc/proto/replay"
|
||||||
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
|
||||||
|
"github.com/admin-else/strom/mc/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cmd = flag.NewFlagSet("mca-to-tmcpr", flag.ContinueOnError)
|
||||||
|
worldPath = cmd.String("world", "", "Path to world directory")
|
||||||
|
output = cmd.String("output", "", "Output .tmcpr file path")
|
||||||
|
radius = cmd.Int("radius", 14, "Chunk radius around spawn")
|
||||||
|
spawnX = cmd.Float64("spawn-x", 0.5, "Spawn X coordinate")
|
||||||
|
spawnY = cmd.Float64("spawn-y", 100, "Spawn Y coordinate")
|
||||||
|
spawnZ = cmd.Float64("spawn-z", 0.5, "Spawn Z coordinate")
|
||||||
|
)
|
||||||
|
|
||||||
|
var FullLight [][]uint8
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
light := make([]uint8, 2048)
|
||||||
|
for i := range light {
|
||||||
|
light[i] = 0xFF
|
||||||
|
}
|
||||||
|
sectionsPerChunk := 384 / 16
|
||||||
|
FullLight = make([][]uint8, sectionsPerChunk)
|
||||||
|
for i := range sectionsPerChunk {
|
||||||
|
FullLight[i] = light
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetChunkFromMca(x, z int32, version string) (b []byte, err error) {
|
||||||
|
xmca := util.FloorDiv(x, anvil.ChunksWidthRegionFile)
|
||||||
|
zmca := util.FloorDiv(z, anvil.ChunksWidthRegionFile)
|
||||||
|
x = x % anvil.ChunksWidthRegionFile
|
||||||
|
if x < 0 {
|
||||||
|
x += anvil.ChunksWidthRegionFile
|
||||||
|
}
|
||||||
|
z = z % anvil.ChunksWidthRegionFile
|
||||||
|
if z < 0 {
|
||||||
|
z += anvil.ChunksWidthRegionFile
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(fmt.Sprintf("./level/anvil/testdata/securechattestworld/region/r.%v.%v.mca", xmca, zmca))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
mca, err := anvil.ReadChunkHeader(f)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var n *nbt.Tag
|
||||||
|
n, err = mca.ChunkAt(f, x, z)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var chunk anvil.Chunk
|
||||||
|
err = nbt.Format.Decode(n.Value, &chunk)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var packetChunk *level.Chunk
|
||||||
|
packetChunk, err = chunk.ToStorage(version)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buffer := bytes.NewBuffer(nil)
|
||||||
|
err = packetChunk.WriteChunkData(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b = buffer.Bytes()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(args []string) error {
|
||||||
|
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||||
|
|
||||||
|
err := cmd.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *worldPath == "" {
|
||||||
|
slog.Error("no world path specified")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if *output == "" {
|
||||||
|
slog.Error("no output path specified")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile, err := os.Create(*output)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to create output file", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
|
||||||
|
w := replay.NewWriter(outFile)
|
||||||
|
|
||||||
|
err = w.WritePacket(&v1_21_11.PlayToClientPacketLogin{
|
||||||
|
EntityId: 73,
|
||||||
|
IsHardcore: false,
|
||||||
|
WorldNames: []string{"minecraft:overworld", "minecraft:the_end", "minecraft:the_nether"},
|
||||||
|
MaxPlayers: 100,
|
||||||
|
ViewDistance: 10,
|
||||||
|
SimulationDistance: 10,
|
||||||
|
ReducedDebugInfo: false,
|
||||||
|
EnableRespawnScreen: true,
|
||||||
|
DoLimitedCrafting: false,
|
||||||
|
WorldState: v1_21_11.PlayToClientSpawnInfo{
|
||||||
|
Dimension: 0,
|
||||||
|
Name: "minecraft:overworld",
|
||||||
|
HashedSeed: -8365436745629239539,
|
||||||
|
Gamemode: "survival",
|
||||||
|
PreviousGamemode: 0xff,
|
||||||
|
IsDebug: false,
|
||||||
|
IsFlat: false,
|
||||||
|
Death: nil,
|
||||||
|
PortalCooldown: 0,
|
||||||
|
SeaLevel: 63,
|
||||||
|
},
|
||||||
|
EnforcesSecureChat: false,
|
||||||
|
}, 0)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to write login packet", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.WritePacket(&v1_21_11.PlayToClientPacketPosition{
|
||||||
|
TeleportId: 0,
|
||||||
|
X: *spawnX,
|
||||||
|
Y: *spawnY,
|
||||||
|
Z: *spawnZ,
|
||||||
|
Dx: 0,
|
||||||
|
Dy: 0,
|
||||||
|
Dz: 0,
|
||||||
|
Yaw: 0,
|
||||||
|
Pitch: 0,
|
||||||
|
Flags: v1_21_11.PlayToClientPositionUpdateRelatives{},
|
||||||
|
}, 0)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to write position packet", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkX := int32(*spawnX) >> 4
|
||||||
|
chunkZ := int32(*spawnZ) >> 4
|
||||||
|
|
||||||
|
err = w.WritePacket(&v1_21_11.PlayToClientPacketUpdateViewPosition{
|
||||||
|
ChunkX: chunkX,
|
||||||
|
ChunkZ: chunkZ,
|
||||||
|
}, 0)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to write view position packet", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.WritePacket(&v1_21_11.PlayToClientPacketGameStateChange{
|
||||||
|
Reason: "level_chunks_load_start",
|
||||||
|
}, 0)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to write game state packet", "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
radius32 := int32(*radius)
|
||||||
|
|
||||||
|
for x := -radius32; x < radius32; x++ {
|
||||||
|
for z := -radius32; z < radius32; z++ {
|
||||||
|
var chunkData []byte
|
||||||
|
chunkData, err = GetChunkFromMca(x, z, "1.21.11")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if chunkData == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
OLDpacket := &v1_21_11.PlayToClientPacketMapChunk{}
|
||||||
|
OLDpacket.X = x
|
||||||
|
OLDpacket.Z = z
|
||||||
|
OLDpacket.ChunkData = v1_21_11.ByteArray{Val: chunkData}
|
||||||
|
OLDpacket.BlockLight = FullLight
|
||||||
|
OLDpacket.BlockLightMask = []int64{(1 << 24) - 1}
|
||||||
|
err = w.WritePacket(OLDpacket, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("conversion complete", "output", *output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package messenger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/cmd/strom/cmd_util"
|
||||||
|
"github.com/admin-else/strom/mc/bot/chat"
|
||||||
|
"github.com/admin-else/strom/mc/bot/keepalive"
|
||||||
|
"github.com/admin-else/strom/mc/bot/world"
|
||||||
|
"github.com/admin-else/strom/mc/client"
|
||||||
|
"github.com/admin-else/strom/mc/data"
|
||||||
|
"github.com/admin-else/strom/mc/event"
|
||||||
|
"github.com/admin-else/strom/mc/proto"
|
||||||
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
|
||||||
|
)
|
||||||
|
|
||||||
|
func stror(a, b string) string {
|
||||||
|
if a != "" {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cmd = flag.NewFlagSet("status", flag.ContinueOnError)
|
||||||
|
connectToFlag = cmd.String("addr", "", "server address to connect to")
|
||||||
|
accFlag = cmd.String("acc", stror(os.Getenv("STROM_ACC"), "strom_messenger"), "the name to use if longer will be treated as ygg token")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Messenger struct {
|
||||||
|
*proto.Conn
|
||||||
|
chat *chat.Module
|
||||||
|
world *world.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) OnChat(e *v1_21_11.PlayToClientPacketPlayerChat) (err error) {
|
||||||
|
m.Log.Info("chat", "author", e.SenderUuid, "m", e.PlainMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) OnChatUnsigned(e *v1_21_11.PlayToClientPacketProfilelessChat) (err error) {
|
||||||
|
m.Log.Info("chatP", "author", e.Name, "m", e.Message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) OnChatSystem(e *v1_21_11.PlayToClientPacketSystemChat) (err error) {
|
||||||
|
m.Log.Info("chatS", "m", e.Content)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) OnStdin(e event.Stdin) (err error) {
|
||||||
|
input := strings.TrimSpace(e.Val)
|
||||||
|
if strings.HasPrefix(input, ">say ") {
|
||||||
|
return m.chat.SendMessage(strings.TrimSpace(strings.TrimPrefix(input, ">say")))
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(input, ">getblock ") {
|
||||||
|
return m.handleGetBlock(strings.TrimSpace(strings.TrimPrefix(input, ">getblock")))
|
||||||
|
}
|
||||||
|
return m.chat.SendMessage(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) handleGetBlock(args string) (err error) {
|
||||||
|
fields := strings.Fields(args)
|
||||||
|
if len(fields) != 3 {
|
||||||
|
m.Log.Info("getblock", "error", "expected >getblock <x> <y> <z>")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
coords := make([]int64, 3)
|
||||||
|
for i, f := range fields {
|
||||||
|
coords[i], err = strconv.ParseInt(f, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
m.Log.Info("getblock", "error", fmt.Sprintf("bad coordinate %q", f))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateId, err := m.world.GetBlock(int32(coords[0]), int32(coords[1]), int32(coords[2]))
|
||||||
|
if err != nil {
|
||||||
|
m.Log.Info("getblock", "error", err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
block, ok := data.LookupBlockByStateId(m.Version, stateId)
|
||||||
|
if !ok {
|
||||||
|
m.Log.Info("getblock", "stateId", stateId, "name", "unknown")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Log.Info("getblock", "x", coords[0], "y", coords[1], "z", coords[2], "name", block.Name, "stateId", stateId)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(args []string) (err error) {
|
||||||
|
err = cmd.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
acc, err := cmd_util.Account(*accFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := client.Login(*connectToFlag, acc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
chatMod, err := chat.Start(c, acc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shareable world storage. Other bots can subscribe to the same World.
|
||||||
|
w := world.NewWorld(c.Version, -64, 384)
|
||||||
|
|
||||||
|
m := &Messenger{
|
||||||
|
Conn: c,
|
||||||
|
chat: chatMod,
|
||||||
|
world: world.Start(c, w),
|
||||||
|
}
|
||||||
|
|
||||||
|
event.StartListingStdin(m.Loop)
|
||||||
|
m.Register(m.OnStdin)
|
||||||
|
m.RegisterUntilLatest(m.OnChat, m.OnChatUnsigned, m.OnChatSystem)
|
||||||
|
keepalive.Start(m.Conn)
|
||||||
|
|
||||||
|
return m.StartConn()
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/admin-else/strom/crypto"
|
"github.com/admin-else/strom/mc/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = flag.NewFlagSet("offline-uuid", flag.ContinueOnError)
|
var cmd = flag.NewFlagSet("offline-uuid", flag.ContinueOnError)
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
package packet_info
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"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)
|
||||||
|
var versionFlag = cmd.String("version", "", "protocol version or minecraft version name")
|
||||||
|
var stateFlag = cmd.String("state", "", "handshaking, status, login, configuration, play")
|
||||||
|
var directionFlag = cmd.String("direction", "", "toserver, toclient")
|
||||||
|
var idFlag = cmd.Int("id", -1, "packet id")
|
||||||
|
var nameFlag = cmd.String("name", "", "packet name")
|
||||||
|
|
||||||
|
func Run(args []string) (err error) {
|
||||||
|
err = cmd.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var version int32
|
||||||
|
if *versionFlag != "" {
|
||||||
|
v, err := data.LookUpVersionByName(*versionFlag)
|
||||||
|
if err != nil {
|
||||||
|
v2, err := parseVersion(*versionFlag)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid version: %s", *versionFlag)
|
||||||
|
}
|
||||||
|
v, err = data.LookUpVersionByProtocolVersion(v2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
version = v.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
var state proto_base.State
|
||||||
|
if *stateFlag != "" {
|
||||||
|
state, err = parseState(*stateFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var direction proto_base.Direction
|
||||||
|
if *directionFlag != "" {
|
||||||
|
direction, err = parseDirection(*directionFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *idFlag != -1 && *nameFlag != "" {
|
||||||
|
err = fmt.Errorf("cannot specify both id and name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *idFlag != -1 {
|
||||||
|
if *versionFlag == "" || *stateFlag == "" || *directionFlag == "" {
|
||||||
|
err = fmt.Errorf("version, state, and direction are required when looking up by id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p, ok := proto.LookUpTypeByPacketInfo(direction, state, int32(*idFlag), version)
|
||||||
|
if !ok {
|
||||||
|
err = fmt.Errorf("packet not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
printPacketInfo(p)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *nameFlag != "" {
|
||||||
|
if *versionFlag == "" {
|
||||||
|
err = fmt.Errorf("version is required when looking up by name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if state != 0 || direction != 0 {
|
||||||
|
p, ok := proto.LookupPacketInfoByNameProtocolVersionStateAndDirection(*nameFlag, version, state, direction)
|
||||||
|
if !ok {
|
||||||
|
err = fmt.Errorf("packet not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
printPacketInfo(p)
|
||||||
|
} else {
|
||||||
|
var found bool
|
||||||
|
for _, p := range proto_generated.Packets {
|
||||||
|
if p.Name == *nameFlag && p.ProtocolVersion == version {
|
||||||
|
printPacketInfo(p)
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
err = fmt.Errorf("packet not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fmt.Errorf("must specify either -id or -name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseState(s string) (proto_base.State, error) {
|
||||||
|
switch s {
|
||||||
|
case "handshaking":
|
||||||
|
return proto_base.Handshaking, nil
|
||||||
|
case "status":
|
||||||
|
return proto_base.Status, nil
|
||||||
|
case "login":
|
||||||
|
return proto_base.Login, nil
|
||||||
|
case "configuration":
|
||||||
|
return proto_base.Configuration, nil
|
||||||
|
case "play":
|
||||||
|
return proto_base.Play, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("unknown state: %s", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDirection(s string) (proto_base.Direction, error) {
|
||||||
|
switch s {
|
||||||
|
case "toserver":
|
||||||
|
return proto_base.ToServer, nil
|
||||||
|
case "toclient":
|
||||||
|
return proto_base.ToClient, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("unknown direction: %s", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseVersion(val string) (int32, error) {
|
||||||
|
v, err := strconv.ParseInt(val, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int32(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPacketInfo(p proto_base.PacketInfo) {
|
||||||
|
v, _ := data.LookUpVersionByProtocolVersion(p.ProtocolVersion)
|
||||||
|
fmt.Printf("Name: %s\n", p.Name)
|
||||||
|
fmt.Printf("ID: %d\n", p.PacketId)
|
||||||
|
fmt.Printf("State: %s\n", stateToString(p.State))
|
||||||
|
fmt.Printf("Direction: %s\n", p.Direction)
|
||||||
|
fmt.Printf("Version: %d (%s)\n", p.ProtocolVersion, v.MinecraftVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stateToString(s proto_base.State) string {
|
||||||
|
switch s {
|
||||||
|
case proto_base.Handshaking:
|
||||||
|
return "handshaking"
|
||||||
|
case proto_base.Status:
|
||||||
|
return "status"
|
||||||
|
case proto_base.Login:
|
||||||
|
return "login"
|
||||||
|
case proto_base.Configuration:
|
||||||
|
return "configuration"
|
||||||
|
case proto_base.Play:
|
||||||
|
return "play"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
package packet_inspector
|
package packet_inspector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
"github.com/admin-else/strom/mc/api"
|
||||||
"github.com/admin-else/strom/client"
|
"github.com/admin-else/strom/mc/client"
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/mc/event"
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_11"
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
|
||||||
"github.com/admin-else/strom/server"
|
"github.com/admin-else/strom/mc/server"
|
||||||
"github.com/admin-else/strom/text"
|
"github.com/admin-else/strom/mc/text"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Cmd = flag.NewFlagSet("packet-inspector", flag.ContinueOnError)
|
var Cmd = flag.NewFlagSet("packet-inspector", flag.ContinueOnError)
|
||||||
@@ -22,6 +25,7 @@ var Token = Cmd.String("token", "", "token to use")
|
|||||||
|
|
||||||
var TargetAddr = Cmd.String("addr", "127.0.0.1:25565", "address to connect to")
|
var TargetAddr = Cmd.String("addr", "127.0.0.1:25565", "address to connect to")
|
||||||
var ListenAddr = Cmd.String("listen", "127.0.0.1:25566", "address to listen on")
|
var ListenAddr = Cmd.String("listen", "127.0.0.1:25566", "address to listen on")
|
||||||
|
var TrueForward = Cmd.Bool("true-forward", false, "forward packets without modification -name and custom server status wont work")
|
||||||
|
|
||||||
type Proxy struct {
|
type Proxy struct {
|
||||||
Client, Servee *proto.Conn
|
Client, Servee *proto.Conn
|
||||||
@@ -33,6 +37,10 @@ func (p *Proxy) Start() (err error) {
|
|||||||
p.Servee.RegisterCritical(p.OnAnything)
|
p.Servee.RegisterCritical(p.OnAnything)
|
||||||
|
|
||||||
p.Servee.RegisterCriticalUntilLatest(p.OnFinishConfiguration)
|
p.Servee.RegisterCriticalUntilLatest(p.OnFinishConfiguration)
|
||||||
|
p.Servee.RegisterCriticalUntilLatest(p.OnFinishLogin)
|
||||||
|
p.Servee.RegisterCriticalUntilLatest(p.OnHandshake)
|
||||||
|
//p.Servee.RegisterCriticalUntilLatest(p.OnCompress)
|
||||||
|
p.Client.RegisterCriticalUntilLatest(p.OnCompress)
|
||||||
|
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -45,6 +53,22 @@ func (p *Proxy) Start() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketLogEvent struct {
|
||||||
|
Time time.Time
|
||||||
|
Packet proto_base.EncodeDecodeAble
|
||||||
|
Info PacketInfoNoTypeJson
|
||||||
|
ClientAddr string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketInfoNoTypeJson struct {
|
||||||
|
Type proto_base.EncodeDecodeAble `json:"-"`
|
||||||
|
Name string
|
||||||
|
Direction proto_base.Direction
|
||||||
|
State proto_base.State
|
||||||
|
PacketId int32
|
||||||
|
ProtocolVersion int32
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Proxy) OnAnything(e event.Anything) (err error) {
|
func (p *Proxy) OnAnything(e event.Anything) (err error) {
|
||||||
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
|
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -54,6 +78,19 @@ func (p *Proxy) OnAnything(e event.Anything) (err error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
jsonBytes, err := json.Marshal(PacketLogEvent{
|
||||||
|
Time: time.Now(),
|
||||||
|
Packet: packet,
|
||||||
|
ClientAddr: p.Client.RemoteAddr().String(),
|
||||||
|
Info: PacketInfoNoTypeJson(packetInfo),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// :pray:
|
||||||
|
_, _ = os.Stdout.Write(jsonBytes)
|
||||||
|
_, _ = os.Stdout.Write([]byte("\n"))
|
||||||
|
|
||||||
switch packetInfo.Direction {
|
switch packetInfo.Direction {
|
||||||
case proto_base.ToServer:
|
case proto_base.ToServer:
|
||||||
err = p.Client.Send(packet)
|
err = p.Client.Send(packet)
|
||||||
@@ -74,6 +111,40 @@ func (p *Proxy) OnFinishConfiguration(packet *v1_21_11.ConfigurationToServerPack
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnFinishLogin(packet *v1_21_11.LoginToServerPacketLoginAcknowledged) (err error) {
|
||||||
|
err = p.Client.Send(packet)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Servee.SetState(proto_base.Configuration)
|
||||||
|
p.Client.SetState(proto_base.Configuration)
|
||||||
|
err = event.DontForwardErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnHandshake(packet *v1_21_11.HandshakingToServerPacketSetProtocol) (err error) {
|
||||||
|
err = p.Client.Send(packet)
|
||||||
|
state := proto_base.State(packet.NextState)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Servee.SetState(state)
|
||||||
|
p.Client.SetState(state)
|
||||||
|
err = event.DontForwardErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Proxy) OnCompress(packet *v1_21_11.LoginToClientPacketCompress) (err error) {
|
||||||
|
err = p.Servee.Send(packet)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Servee.SetCompressionThreshold(packet.Threshold)
|
||||||
|
p.Client.SetCompressionThreshold(packet.Threshold)
|
||||||
|
err = event.DontForwardErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var StatusResponse = server.StatusResponse{
|
var StatusResponse = server.StatusResponse{
|
||||||
Version: server.StatusResponseVersion{
|
Version: server.StatusResponseVersion{
|
||||||
Name: text.Pretty("STROM"),
|
Name: text.Pretty("STROM"),
|
||||||
@@ -90,38 +161,41 @@ func Run(args []string) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
|
||||||
slog.Info("Starting packet inspector", "listen", *ListenAddr)
|
slog.Info("Starting packet inspector", "listen", *ListenAddr)
|
||||||
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
|
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
|
||||||
serveeConn.DebugPrintPackets = []string{""}
|
|
||||||
p := &Proxy{Servee: serveeConn, Client: nil}
|
p := &Proxy{Servee: serveeConn, Client: nil}
|
||||||
var acc = api.NewOfflineAccount(*OfflineNameFlag)
|
var acc = api.NewOfflineAccount(*OfflineNameFlag)
|
||||||
if *Token != "" {
|
if !*TrueForward {
|
||||||
acc, err = api.NewAccountFromYGG(*Token)
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err = server.ServeLogin(serveeConn, server.WithOtherAccount(acc), server.WithStatus(StatusResponse), server.WithoutOnlineMode())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*TargetAddr, err = client.DoDnsSimple(*TargetAddr)
|
*TargetAddr, _, _, err = client.DoDNSChecked(nil, *TargetAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c, err := client.Connect(*TargetAddr)
|
c, err := client.Connect(nil, *TargetAddr, "")
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.DebugPrintPackets = []string{""}
|
|
||||||
err = client.LoginRaw(c, acc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
if !*TrueForward {
|
||||||
|
err = client.LoginRaw(c, acc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
p.Client = c
|
p.Client = c
|
||||||
|
p.Client.AlwaysDecode = true
|
||||||
|
p.Servee.AlwaysDecode = true
|
||||||
err = p.Start()
|
err = p.Start()
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
+4
-4
@@ -4,15 +4,15 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
generated "github.com/admin-else/strom/proto_generated/%v"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error: %v
|
// Error: %v
|
||||||
func TestFailedPacket%10X(t *testing.T) {
|
func TestFailedPacket%10X(t *testing.T) {
|
||||||
p := v1_21_8.Play%vPacket{}
|
p := &generated.%v{}
|
||||||
b := bytes.NewBuffer(%#v)
|
b := bytes.NewBuffer(%#v)
|
||||||
err := p.Decode(b)
|
err := p.Decode(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package print_replay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/mc/data"
|
||||||
|
"github.com/admin-else/strom/mc/proto"
|
||||||
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cmd = flag.NewFlagSet("print-replay", flag.ContinueOnError)
|
||||||
|
FileFlag = cmd.String("file", "", "The tmcpr file to print")
|
||||||
|
ProtocolVersion = cmd.Int("protocol", -1, "The protocol version")
|
||||||
|
CreateTestsPath = cmd.String("create-tests", "", "")
|
||||||
|
StateFlag = cmd.String("state", "login", "Initial state: login, config, play")
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: maybe use the proto replay api
|
||||||
|
|
||||||
|
//go:embed failed_packet.go.tmpl
|
||||||
|
var TestSrcF string
|
||||||
|
|
||||||
|
func SaveUnCodeAbleAsTest(packet *proto.UnCodablePacket) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := data.LookUpVersionByProtocolVersion(packet.Info.ProtocolVersion)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
goPackage := "v" + strings.ReplaceAll(ret.MinecraftVersion, ".", "_")
|
||||||
|
|
||||||
|
//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)
|
||||||
|
}
|
||||||
|
slog.Info("Packet failed to parse", "error", packet.Err, "saved", f.Name(), "data", fmt.Sprintf("%q", packet.Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = proto_base.Login
|
||||||
|
|
||||||
|
func UnpackPacket(r io.Reader) (packet proto_base.EncodeDecodeAble, err error) {
|
||||||
|
var timestamp, length uint32
|
||||||
|
err = binary.Read(r, binary.BigEndian, ×tamp)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = binary.Read(r, binary.BigEndian, &length)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the packet data into a byte slice
|
||||||
|
packetData := make([]byte, length)
|
||||||
|
_, err = io.ReadFull(r, packetData)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use bytes.Reader instead of bytes.Buffer
|
||||||
|
b := bytes.NewReader(packetData)
|
||||||
|
|
||||||
|
packetId, err := proto_base.DecodeVarInt(b)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate position after reading the packet ID
|
||||||
|
// 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))
|
||||||
|
if !ok {
|
||||||
|
packet = &proto.UnCodablePacket{Err: proto.BadPacketIdErr, Data: packetData, Info: i}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(args []string) (err error) {
|
||||||
|
err = cmd.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if *ProtocolVersion == -1 {
|
||||||
|
return fmt.Errorf("no protocol version specified")
|
||||||
|
}
|
||||||
|
if *FileFlag == "" {
|
||||||
|
return fmt.Errorf("no file specified")
|
||||||
|
}
|
||||||
|
switch *StateFlag {
|
||||||
|
case "login":
|
||||||
|
state = proto_base.Login
|
||||||
|
case "config":
|
||||||
|
state = proto_base.Configuration
|
||||||
|
case "play":
|
||||||
|
state = proto_base.Play
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown state: %s", *StateFlag)
|
||||||
|
}
|
||||||
|
f, err := os.Open(*FileFlag)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
var totalPackets, failedPackets int
|
||||||
|
for {
|
||||||
|
var packet proto_base.EncodeDecodeAble
|
||||||
|
packet, err = UnpackPacket(f)
|
||||||
|
unCodablePacket, ok := packet.(*proto.UnCodablePacket)
|
||||||
|
if ok {
|
||||||
|
if *CreateTestsPath != "" {
|
||||||
|
SaveUnCodeAbleAsTest(unCodablePacket)
|
||||||
|
}
|
||||||
|
failedPackets++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Packet: %#v\n", packet)
|
||||||
|
}
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
totalPackets++
|
||||||
|
}
|
||||||
|
fmt.Printf("Total packets: %d, failed packets: %d, %%failed: %.2f%%\n", totalPackets, failedPackets, float64(failedPackets)/float64(totalPackets)*100)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:2d90ee976987678593a6b1d09136bb9b4fd131390796ea6e413376a6ddc63226
|
||||||
|
size 75035693
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[{"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}}}]
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d972b9b5f9d962199fe262eccef32a4b94504283fae97f9963702e7ab1550129
|
||||||
|
size 437123
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"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":[]}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:cef58ec0e3d1d25e34c2effc5c09241ee4c1b39c085ed9e11b7097dfd5c2d033
|
||||||
|
size 3429878
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
234002168
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"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
@@ -0,0 +1 @@
|
|||||||
|
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:be007aa681f7959708e89e39e05390e228753291823a18274068d310a50aa78b
|
||||||
|
size 526306397
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
270566013
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0da58f5cdd65ef241df8c61f9e633af7317faa77034183d4fb648b039c1e14ed
|
||||||
|
size 842
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"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":[]}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:52fa049f97918289e018fa4c3bf5ff789d4f99f44f590ad875ce1f65e5fa9563
|
||||||
|
size 37
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1105715802
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package registry_data_hunter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -6,16 +6,16 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/admin-else/strom/api"
|
"github.com/admin-else/strom/mc/api"
|
||||||
"github.com/admin-else/strom/client"
|
"github.com/admin-else/strom/mc/client"
|
||||||
"github.com/admin-else/strom/data"
|
"github.com/admin-else/strom/mc/data"
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/mc/event"
|
||||||
"github.com/admin-else/strom/nbt"
|
"github.com/admin-else/strom/mc/nbt"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
|
||||||
"github.com/admin-else/strom/server"
|
"github.com/admin-else/strom/mc/server"
|
||||||
"github.com/admin-else/strom/text"
|
"github.com/admin-else/strom/mc/text"
|
||||||
)
|
)
|
||||||
|
|
||||||
func wontFail[T any](v T, err error) T {
|
func wontFail[T any](v T, err error) T {
|
||||||
@@ -25,10 +25,7 @@ func wontFail[T any](v T, err error) T {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// THIS IS REDUNDANT USE LOGIN PACKET IN MINECRAFT DATA
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Edit these two
|
|
||||||
Version = "1.21.11"
|
Version = "1.21.11"
|
||||||
SaveAs = "data/registry/" + Version + ".nbt"
|
SaveAs = "data/registry/" + Version + ".nbt"
|
||||||
|
|
||||||
@@ -113,7 +110,6 @@ func (p *Proxy) OnTags(data *v1_21_8.ConfigurationToClientPacketTags) (err error
|
|||||||
func handleClient(c *proto.Conn) (err error) {
|
func handleClient(c *proto.Conn) (err error) {
|
||||||
acc := api.NewOfflineAccount("RegistryHunter")
|
acc := api.NewOfflineAccount("RegistryHunter")
|
||||||
|
|
||||||
// ignore resource leak warning we own c conn so we should not close it here
|
|
||||||
_, err = server.ServeLogin(c, server.WithOtherAccount(acc), server.WithCompatibleVersions(CompatibleProtocolVersion), server.WithRawStatus(Status))
|
_, err = server.ServeLogin(c, server.WithOtherAccount(acc), server.WithCompatibleVersions(CompatibleProtocolVersion), server.WithRawStatus(Status))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -150,10 +146,7 @@ func handleClient(c *proto.Conn) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func Run(args []string) error {
|
||||||
slog.SetLogLoggerLevel(slog.LevelDebug)
|
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||||
err := server.StartServerWithOnConn(":25566", handleClient)
|
return server.StartServerWithOnConn(":25566", handleClient)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package serve_tmcpr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"math/rand/v2"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/mc/event"
|
||||||
|
"github.com/admin-else/strom/mc/proto"
|
||||||
|
"github.com/admin-else/strom/mc/proto/replay"
|
||||||
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
|
||||||
|
"github.com/admin-else/strom/mc/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
*proto.Conn
|
||||||
|
*event.Timer
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cmd = flag.NewFlagSet("serve-tmcpr", flag.ContinueOnError)
|
||||||
|
tmcprFile = cmd.String("file", "/tmp/world.tmcpr", "Path to .tmcpr replay file")
|
||||||
|
bindAddr = cmd.String("bind", ":25566", "Address to listen on")
|
||||||
|
)
|
||||||
|
|
||||||
|
func Limbo(c *proto.Conn, tmcprPath string) (err error) {
|
||||||
|
s := &Server{Conn: c}
|
||||||
|
|
||||||
|
_, err = server.ServeLogin(s.Conn, server.WithoutOnlineMode())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = server.ServeConfig(s.Conn)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(tmcprPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
r := replay.NewReader(f)
|
||||||
|
r.Version = c.ProtocolVersion
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
for {
|
||||||
|
var packetData []byte
|
||||||
|
var timestamp uint32
|
||||||
|
packetData, timestamp, _, err = r.ReadRaw()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(timestamp)*time.Millisecond - time.Since(start))
|
||||||
|
err = s.SendRaw(packetData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Timer = &event.Timer{}
|
||||||
|
s.Timer.Every(time.Second*15, s.SendKeepAlive)
|
||||||
|
s.Timer.Start(c.Loop)
|
||||||
|
|
||||||
|
return s.StartConn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) SendKeepAlive() error {
|
||||||
|
return s.Send(&v1_21_8.PlayToClientPacketKeepAlive{KeepAliveId: rand.Int64()})
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(args []string) error {
|
||||||
|
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||||
|
|
||||||
|
err := cmd.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if *tmcprFile == "" {
|
||||||
|
slog.Error("no file specified")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.StartServerWithOnConn(*bindAddr, func(c *proto.Conn) error {
|
||||||
|
return Limbo(c, *tmcprFile)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
package serve_world
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"math/rand/v2"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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 {
|
||||||
|
*proto.Conn
|
||||||
|
*event.Timer
|
||||||
|
|
||||||
|
reconnectCalled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
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("./level/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 Limbo(c *proto.Conn) (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
|
||||||
|
}
|
||||||
|
login := &v1_21_8.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_8.PlayToClientSpawnInfo{Dimension: 0, Name: "minecraft:overworld", HashedSeed: -8365436745629239539, Gamemode: "survival", PreviousGamemode: 0xff, IsDebug: false, IsFlat: false, Death: nil, PortalCooldown: 0, SeaLevel: 63}, EnforcesSecureChat: false}
|
||||||
|
err = s.Send(login)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = s.Send(&v1_21_8.PlayToClientPacketPosition{
|
||||||
|
TeleportId: 0,
|
||||||
|
X: 0.5,
|
||||||
|
Y: 100,
|
||||||
|
Z: 0.5,
|
||||||
|
Dx: 0,
|
||||||
|
Dy: 0,
|
||||||
|
Dz: 0,
|
||||||
|
Yaw: 0,
|
||||||
|
Pitch: 0,
|
||||||
|
Flags: v1_21_8.PlayToClientPositionUpdateRelatives{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = s.Send(&v1_21_8.PlayToClientPacketUpdateViewPosition{
|
||||||
|
ChunkX: 0,
|
||||||
|
ChunkZ: 0,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = s.Send(&v1_21_8.PlayToClientPacketGameStateChange{
|
||||||
|
Reason: "level_chunks_load_start",
|
||||||
|
})
|
||||||
|
|
||||||
|
radius := int32(10)
|
||||||
|
|
||||||
|
for x := -radius; x < radius; x++ {
|
||||||
|
for z := -radius; z < radius; z++ {
|
||||||
|
var chunkData []byte
|
||||||
|
chunkData, err = GetChunkFromMca(x, z, c.Version)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if chunkData == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
OLDpacket := &v1_21_8.PlayToClientPacketMapChunk{}
|
||||||
|
OLDpacket.X = x
|
||||||
|
OLDpacket.Z = z
|
||||||
|
OLDpacket.ChunkData = v1_21_8.ByteArray{Val: chunkData}
|
||||||
|
OLDpacket.BlockLight = FullLight
|
||||||
|
OLDpacket.BlockLightMask = []int64{(1 << 24) - 1}
|
||||||
|
err = s.Send(OLDpacket)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Timer = &event.Timer{}
|
||||||
|
s.Timer.Every(time.Second*15, s.SendKeepAlive)
|
||||||
|
s.Timer.Start(c.Loop)
|
||||||
|
|
||||||
|
return s.StartConn()
|
||||||
|
}
|
||||||
|
func (s *Server) SendKeepAlive() (err error) {
|
||||||
|
err = s.Send(&v1_21_8.PlayToClientPacketKeepAlive{KeepAliveId: rand.Int64()})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(args []string) error {
|
||||||
|
slog.SetLogLoggerLevel(slog.LevelDebug)
|
||||||
|
return server.StartServerWithOnConn(":25566", Limbo)
|
||||||
|
}
|
||||||
@@ -6,11 +6,11 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/admin-else/strom/client"
|
"github.com/admin-else/strom/mc/client"
|
||||||
"github.com/admin-else/strom/event"
|
"github.com/admin-else/strom/mc/event"
|
||||||
"github.com/admin-else/strom/proto"
|
"github.com/admin-else/strom/mc/proto"
|
||||||
"github.com/admin-else/strom/proto_base"
|
"github.com/admin-else/strom/mc/proto_base"
|
||||||
"github.com/admin-else/strom/proto_generated/v1_21_8"
|
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = flag.NewFlagSet("status", flag.ContinueOnError)
|
var cmd = flag.NewFlagSet("status", flag.ContinueOnError)
|
||||||
@@ -46,7 +46,7 @@ func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
|
|||||||
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
|
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
|
||||||
// It does not resolve SRV records.
|
// It does not resolve SRV records.
|
||||||
func StatusRaw(addr string) (s *StatusClient, err error) {
|
func StatusRaw(addr string) (s *StatusClient, err error) {
|
||||||
c, err := client.ConnectVersionLess(addr)
|
c, err := client.ConnectVersionLess(nil, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ func Run(args []string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if *srvFlag {
|
if *srvFlag {
|
||||||
*addrFlag, err = client.DoDnsSimple(*addrFlag)
|
*addrFlag, _, _, err = client.DoDNSChecked(nil, *addrFlag, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
module github.com/admin-else/strom
|
module github.com/admin-else/strom
|
||||||
|
|
||||||
go 1.25
|
go 1.26.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
github.com/go-viper/mapstructure/v2 v2.4.0 // FIXME: this should be replaced with my version
|
||||||
github.com/google/go-cmp v0.7.0
|
github.com/google/go-cmp v0.7.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,5 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L
|
|||||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
package anvil
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/admin-else/strom/data"
|
|
||||||
"github.com/admin-else/strom/level"
|
|
||||||
"github.com/admin-else/strom/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Chunk struct {
|
|
||||||
DataVersion int32
|
|
||||||
Heightmaps map[string][]int64
|
|
||||||
InhabitedTime int64
|
|
||||||
XPos, YPos, ZPos int32
|
|
||||||
Sections []Section
|
|
||||||
}
|
|
||||||
|
|
||||||
const McPrefix = "minecraft:"
|
|
||||||
|
|
||||||
func (c Chunk) ToStorage(version string) (toChunk *level.Chunk, err error) {
|
|
||||||
f := level.MakeBlockFormat(version)
|
|
||||||
toChunk = new(level.Chunk)
|
|
||||||
|
|
||||||
toChunk.Sections = make([]level.Section, len(c.Sections))
|
|
||||||
for i, s := range c.Sections {
|
|
||||||
pallete := make([]int32, len(s.BlockStates.Palette))
|
|
||||||
for j, p := range s.BlockStates.Palette {
|
|
||||||
pallete[j], err = data.StateIdFromBlocKAndStateMap(version, strings.TrimPrefix(p.Name, McPrefix), p.Properties)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// this is probably fine
|
|
||||||
uintSlice := unsafe.Slice((*uint64)(unsafe.Pointer(unsafe.SliceData(s.BlockStates.Data))), len(s.BlockStates.Data))
|
|
||||||
|
|
||||||
toChunk.Sections[i].Blocks, err = f.Import(uintSlice, util.BpeByNum(float64(len(s.BlockStates.Palette))), pallete)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
toChunk.Sections[i].BlockCount = int16(level.CountStorage(toChunk.Sections[i].Blocks, 0))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, s := range c.Sections {
|
|
||||||
pallete := make([]int32, len(s.Biomes.Palette))
|
|
||||||
for j, p := range s.Biomes.Palette {
|
|
||||||
var b *data.Biome
|
|
||||||
b, ok := data.LookupBiomeByName(version, strings.TrimPrefix(p, McPrefix))
|
|
||||||
if !ok {
|
|
||||||
err = data.UnknownBiomeNameErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pallete[j] = b.Id
|
|
||||||
}
|
|
||||||
// this is probably fine
|
|
||||||
uintSlice := unsafe.Slice((*uint64)(unsafe.Pointer(unsafe.SliceData(s.Biomes.Data))), len(s.Biomes.Data))
|
|
||||||
|
|
||||||
toChunk.Sections[i].Biomes, err = f.Import(uintSlice, util.BpeByNum(float64(len(pallete))), pallete)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Section struct {
|
|
||||||
Y int8
|
|
||||||
BlockStates struct {
|
|
||||||
Data []int64 `nbt:"data,omitempty"`
|
|
||||||
Palette []struct {
|
|
||||||
Name string
|
|
||||||
Properties map[string]string `nbt:"properties,omitempty"`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Biomes struct {
|
|
||||||
Data []int64 `nbt:"data,omitempty"`
|
|
||||||
Palette []string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
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.
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.
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.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
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