75 Commits
Author SHA1 Message Date
adman e324a95727 cmd take account with env var 2026-07-16 22:16:04 +02:00
adman 7f878f40a1 add world module for in-memory block storage and integrate getblock command in messenger 2026-07-16 16:05:42 +02:00
adman 7baa9990cb yes222 2026-07-16 14:02:28 +02:00
adman 5c5c391aa6 yes 2026-07-16 01:13:31 +02:00
adman b966925212 move data stuff 2026-07-11 23:08:03 +02:00
adman efd7049859 biome map stuff 2026-07-11 22:08:29 +02:00
adman 3ef9446f50 biome depth stuff 2026-07-06 11:49:45 +02:00
adman 68612969c9 fix spline test 2026-07-05 22:32:39 +02:00
adman b4712ff5c6 spline 2026-07-05 20:53:59 +02:00
adman eab050e291 fixes 2026-07-05 10:49:45 +02:00
adman a8b090227b mc gen start 2026-07-05 10:25:43 +02:00
adman 201e0dfa44 fix moving pains 2026-06-27 14:10:31 +02:00
adman 666f250b3e mc root source folder cleanup 2026-06-27 13:54:54 +02:00
adman b2784574bd move tools to strom cli 2026-06-27 13:06:05 +02:00
adman c0b1501f23 add packet spy json logging 2026-06-17 11:45:54 +02:00
adman 4de76d8023 regenerate proto 2026-06-16 23:08:00 +02:00
adman f08684fb67 fix bug in entity metadata encoding 2026-06-16 23:07:33 +02:00
adman 9e4f9409c5 add TestEntityMetadata 2026-06-16 23:06:58 +02:00
adman 4d3fbed957 add SimplePacketToBytesLenPrefix 2026-06-16 23:06:42 +02:00
adman b338888b58 add SimpleBytesToPacket utility function for decoding packets 2026-06-16 22:29:26 +02:00
adman 4956c1d874 add OnFinishLogin handling to packet inspector 2026-06-16 22:28:01 +02:00
adman d4fe362d9f add MustLookupProtocolVersion utility function for testing purposes 2026-06-16 22:27:41 +02:00
adman 5d8ff870df add true-forward mode to packet inspector 2026-06-02 21:06:41 +02:00
adman 58c9a60236 add optional encryption flag to login server and update ServeLogin behavior 2026-05-30 15:53:35 +02:00
adman 3e5b141ec7 add ToolSum function for build info and integrate it into Version outputs 2026-05-29 01:27:35 +02:00
adman efeb5ecda6 add file comment generation and hashing for reproducible output in proto_generator 2026-05-29 01:14:56 +02:00
adman 624e8ad4de add SimplePacketToBytes utility function for encoding packets 2026-05-28 23:59:54 +02:00
adman 764cfd8c4d lpvec3d 2026-05-14 10:42:38 +02:00
adman d43ff7f83d add seek-back handling in entity metadata decoding 2026-05-14 00:06:45 +02:00
adman a39999b344 update Decode function signatures to use io.ReadSeeker instead of io.Reader 2026-05-13 23:54:06 +02:00
adman 8ee48e1a45 add EntityMetadataLoop handling 2026-05-13 23:21:31 +02:00
adman bf6affeb97 remove unused protodef_types file from proto_generator 2026-05-13 22:20:32 +02:00
adman ede9a47ca2 add placeholder replacement and formatting utility functions 2026-05-12 16:31:00 +02:00
adman 2fb290572a add mutex for thread-safe XORKeyStream in CFB8 2026-05-11 23:27:14 +02:00
adman 021b8ddc1a add support for initial state configuration in print_replay tool 2026-05-10 20:35:58 +02:00
adman 05e8da2ff4 add packet id lookup and write to replay writer 2026-05-10 20:35:49 +02:00
adman a8936d92a5 add replay reader and writer for handling tmcpr files 2026-05-10 18:41:04 +02:00
adman c56898e787 refactor print_replay to improve unprocessable packet test creation 2026-05-10 17:29:52 +02:00
adman c483bf9553 if ctx is nil, set it to background context 2026-05-10 14:30:28 +02:00
adman b5ee17d1ae remove uncodeable_packet_hunter_proxy tool and integrate its functionality into print_replay 2026-05-10 14:09:24 +02:00
adman 57cef25e27 manage state transitions in print_replay tool 2026-05-10 13:01:40 +02:00
adman 148302c94d medium sized replay test data 2026-05-09 23:07:27 +02:00
adman 02c402251d add packet-info subcommand 2026-05-09 21:56:50 +02:00
adman c800c5d471 improve chunk section palette handling for blocks 2026-05-09 16:34:31 +02:00
adman 477f51cce7 fix biome palette initialization in chunk sections 2026-05-08 22:17:32 +02:00
adman 60103ebf47 enable Git LFS in checkout step 2026-05-08 21:41:46 +02:00
adman 21eeebf0f7 fix lighting 2026-05-08 21:33:05 +02:00
adman bb6f6548d5 serve world tool 2026-05-08 20:58:08 +02:00
adman d6c07cf428 replay testdata 2026-05-08 20:50:42 +02:00
adman e98921755d replay testdata 2026-05-08 20:50:37 +02:00
adman 60383fede1 replay testdata 2026-05-08 20:50:34 +02:00
adman 3bfa70d384 replay testdata 2026-05-08 19:17:09 +02:00
adman 60147fd4b6 correct "Properties" field case 2026-05-08 19:16:42 +02:00
adman fe51287cc6 add FIXME comments for mapstructure usage in go.mod and text.go 2026-05-08 12:23:27 +02:00
adman a44c5f8606 ci(workflows): add go vet step to CI pipeline 2026-05-08 12:11:36 +02:00
adman d5979b2c3d feat(util, anvil): add FloorDiv utility and nil-check in ChunkAt 2026-05-06 22:38:24 +02:00
admin-elseandGitHub 4b95ea9ae2 Merge pull request #1 from admin-else/feat/mca2packet
start work on mca to packet conversion
2026-05-06 21:14:53 +02:00
admin-elseandGitHub a80fd29297 Merge branch 'master' into feat/mca2packet 2026-05-06 21:13:49 +02:00
adman 589f35d147 delete the failing test because it works now i tested in game 2026-05-06 21:13:02 +02:00
adman c7b5f6ba0f refactor(util): remove unused utility functions 2026-05-06 18:47:18 +02:00
adman 0600315e69 refactor: move utility functions to dedicated util package and adjust references 2026-05-06 16:51:45 +02:00
adman 12005df68b Merge remote-tracking branch 'origin/feat/mca2packet' into feat/mca2packet 2026-05-06 16:48:48 +02:00
adman 600842a679 refactor storage format and bpe handling; add redirecting bpe support and utility function for next largest number 2026-05-06 16:48:39 +02:00
adman c93d907d46 refactor storage format and bpe handling; add redirecting bpe support and utility function for next largest number 2026-05-06 16:47:50 +02:00
adman be6d2fb461 update Go version to 1.26.2 in go.mod and CI workflow 2026-05-06 15:55:54 +02:00
adman b2df88203d fix block count calculation in chunk sections; improve variable naming in tests 2026-05-05 23:12:02 +02:00
adman 62d12386e1 improve chunk comparison by adding custom storage comparer and ignoring unexported fields 2026-05-05 13:57:02 +02:00
adman 00ea02a529 refactor chunk version handling; improve logging in equality checks and storage import logic 2026-05-05 13:30:21 +02:00
adman e5231dfdb6 improve chunk palette handling and logging; fix biome format and storage comparison logic 2026-05-04 00:27:00 +02:00
adman 3b1b436ecf refactor block state handling and storage initialization in litematic structures 2026-05-03 22:26:02 +02:00
adman 47180bf650 fix incorrect equality check in storage comparison logic 2026-05-03 19:02:06 +02:00
adman 325db98b99 add fuzz test data for chunk reading validation 2026-05-03 18:59:49 +02:00
adman ceae543352 read chunk section pallet fix 2026-05-03 18:59:42 +02:00
adman aef9615476 add equality checks and fuzz testing for chunk and storage data comparison 2026-05-03 18:34:21 +02:00
adman 5d3860b91e start work on mca to packet conversion 2026-05-03 18:20:36 +02:00
597 changed files with 738871 additions and 2870 deletions
+4
View File
@@ -1,2 +1,6 @@
**/*_test.go 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
+9 -2
View File
@@ -14,15 +14,22 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkout with LFS
uses: actions/checkout@v4
with:
lfs: true
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.25'
go-version: '1.26.2'
- name: Build
run: go build -v ./...
- name: Vet
run: go vet -v ./...
- name: Test
run: go test -v ./...
-1
View File
@@ -1,5 +1,4 @@
.devres/
.idea/
.failed_packets/
tools/.*/
.vscode/
-43
View File
@@ -1,43 +0,0 @@
package client
import (
"context"
"net"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
)
// Connect connects to the given address note that it does not resolve the SRV records it's just a raw net.Dial
// You must close the connection when you're done with it
func Connect(addr string) (ret *proto.Conn, err error) {
s, err := Status(addr)
if err != nil {
return
}
ret, err = ConnectVersionLess(addr)
if err != nil {
return
}
err = ret.SetProtocolVersion(s.Version.Protocol)
return
}
func ConnectVersionLess(addr string) (ret *proto.Conn, err error) {
return ConnectVersionLessCtx(context.Background(), addr)
}
// ConnectVersionLess connects to the given address without setting the version you should set it
// when using this function.
// You must close the connection when you're done with it
func ConnectVersionLessCtx(ctx context.Context, addr string) (ret *proto.Conn, err error) {
ret = proto.NewConnCtx(ctx)
ret.Actor = proto_base.Client
ret.Conn, err = (&net.Dialer{}).DialContext(ctx, "tcp", addr)
if err != nil {
return
}
ret.R = ret.Conn
ret.W = ret.Conn
return
}
-126
View File
@@ -1,126 +0,0 @@
package client
import (
"context"
"errors"
"net"
"strconv"
"strings"
"time"
)
type DNSCheckFunc func(addr string) error
// DoDns resolves a DNS name to an IP address
// It should resolve a minecraft compatible address into something net.Dial
func DoDns(in string) (out string, host string, portN uint16, err error) {
return DoDNSChecked(in, nil)
}
// DoDNSChecked resolves a DNS name to an IP address
// It should resolve a minecraft compatible address into something net.Dial
// It also optionally accepts a function to "check" the address
func DoDNSChecked(in string, f DNSCheckFunc) (connectTo string, host string, portN uint16, err error) {
// https://mcsrc.dev/1/26.1-pre-1/net/minecraft/client/multiplayer/resolver/ServerAddress#L34
host, port, err := net.SplitHostPort(in)
if err != nil {
var addrErr *net.AddrError
if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
err = nil
host = in
port = "25565"
} else {
return
}
}
porti, err := strconv.Atoi(port)
if err != nil {
return
}
if f == nil {
f = func(addr string) error { return nil }
}
err = f(host)
if err != nil {
return
}
portN = uint16(porti)
outIP, err := net.ResolveIPAddr("ip", host)
if err != nil {
return
}
err = f(outIP.String())
if err != nil {
return
}
connectTo = outIP.String() + ":" + port
if portN != 25565 {
return
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // otherwise this takes 10+ seconds
defer cancel()
r := &net.Resolver{}
_, srvs, _ := r.LookupSRV(ctx, "minecraft", "tcp", host)
if len(srvs) == 0 {
return
}
host = strings.TrimSuffix(srvs[0].Target, ".")
portN = srvs[0].Port
outIP, err = net.ResolveIPAddr("ip", host)
if err != nil {
return
}
err = f(outIP.String())
if err != nil {
return
}
connectTo = outIP.String() + ":" + port
return
}
// DoDnsSimple resolves a DNS name to an IP address
// It should resolve a minecraft compatible address into something net.Dial
// It does not check the address
func DoDnsSimple(in string) (out string, err error) {
host, port, err := net.SplitHostPort(in)
if err != nil {
var addrErr *net.AddrError
if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
err = nil
host = in
port = "25565"
} else {
return
}
}
porti, err := strconv.Atoi(port)
if err != nil {
return
}
portN := uint16(porti)
_, err = net.ResolveIPAddr("ip", host)
if err != nil {
return
}
out = host + ":" + strconv.Itoa(int(portN))
if portN != 25565 {
return
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // otherwise this takes 10+ seconds
defer cancel()
r := &net.Resolver{}
_, srvs, _ := r.LookupSRV(ctx, "minecraft", "tcp", host)
if len(srvs) == 0 {
return
}
host = strings.TrimSuffix(srvs[0].Target, ".")
portN = srvs[0].Port
out = host + ":" + strconv.Itoa(int(portN))
return
}
+3 -2
View File
@@ -4,9 +4,10 @@ import (
"encoding/json"
"errors"
"flag"
"log/slog"
"os"
"github.com/admin-else/strom/api"
"github.com/admin-else/strom/mc/api"
)
var cmd = flag.NewFlagSet("api", flag.ContinueOnError)
@@ -52,7 +53,7 @@ func Run(args []string) (err error) {
return
}
} else {
err = PleaseProvideArgumentErr
slog.Info("account", "name", a.Name, "uuid", a.Uuid)
return
}
err = json.NewEncoder(os.Stdout).Encode(data)
+11
View File
@@ -0,0 +1,11 @@
package cmd_util
import "github.com/admin-else/strom/mc/api"
func Account(s string) (a *api.Account, err error) {
if len(s) > 16 {
return api.NewAccountFromYGG(s)
}
a = api.NewOfflineAccount(s)
return
}
+7 -7
View File
@@ -7,9 +7,9 @@ import (
"os"
"slices"
"github.com/admin-else/strom/data"
"github.com/admin-else/strom/proto_generated"
"github.com/admin-else/strom/util"
mcdata "github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/proto_generated"
"github.com/admin-else/strom/mc/util"
)
// strom data 1.21.11 blocks id 0 name => air
@@ -39,11 +39,11 @@ func Run(args []string) (err error) {
case 0: // print available versions
dataValues = proto_generated.SupportedVersions
case 1: // print available data for version
dataValues = slices.Collect(maps.Keys(data.Paths.Data[args[0]]))
dataValues = slices.Collect(maps.Keys(mcdata.Paths.Data[args[0]]))
case 2:
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
case 3:
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
if err != nil {
return
}
@@ -68,7 +68,7 @@ func Run(args []string) (err error) {
}
dataValues = result
case 4, 5:
err = data.LoadVersionedJson(args[0], args[1], &dataValues)
err = mcdata.LoadVersionedJson(args[0], args[1], &dataValues)
if err != nil {
return
}
+2 -2
View File
@@ -6,8 +6,8 @@ import (
"log/slog"
"os"
"github.com/admin-else/strom/level/anvil"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/mc/level/anvil"
"github.com/admin-else/strom/mc/nbt"
)
var (
+42 -25
View File
@@ -1,48 +1,70 @@
package main
import (
"debug/buildinfo"
"errors"
"fmt"
"log/slog"
"maps"
"os"
"runtime/debug"
"slices"
"github.com/admin-else/strom/cmd/strom/api"
"github.com/admin-else/strom/cmd/strom/data"
"github.com/admin-else/strom/cmd/strom/extract_mca"
"github.com/admin-else/strom/cmd/strom/mca_to_tmcpr"
"github.com/admin-else/strom/cmd/strom/messenger"
"github.com/admin-else/strom/cmd/strom/offline_uuid"
"github.com/admin-else/strom/cmd/strom/packet_info"
"github.com/admin-else/strom/cmd/strom/packet_inspector"
"github.com/admin-else/strom/cmd/strom/print_nbt"
"github.com/admin-else/strom/cmd/strom/print_replay"
"github.com/admin-else/strom/cmd/strom/registry_data_hunter"
"github.com/admin-else/strom/cmd/strom/serve_tmcpr"
"github.com/admin-else/strom/cmd/strom/serve_world"
"github.com/admin-else/strom/cmd/strom/status"
)
var ExpectedASubcommandErr = errors.New("expected a subcommand")
var UnknownSubcommandErr = errors.New("unknown subcommand try 'strom help' for a list of subcommands")
func Version(args []string) (err error) {
selfPath, err := os.Executable()
if err != nil {
return
func ToolVersion() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return "devel"
}
info, err := buildinfo.ReadFile(selfPath)
if err != nil {
return
return info.Main.Version
}
func ToolSum() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return ""
}
fmt.Println("strom cli ", info.Main.Version, " (", info.Main.Sum, ")")
return
return info.Main.Sum
}
func Version(args []string) error {
fmt.Println("strom cli ", ToolVersion(), " (", ToolSum(), ")")
return nil
}
var subcommands = map[string]func(args []string) error{
"offline-uuid": offline_uuid.Run,
"print-nbt": print_nbt.Run,
"status": status.Run,
"packet-spy": packet_inspector.Run,
"api": api.Run,
"version": Version,
"extract-mca": extract_mca.Run,
"data": data.Run,
"offline-uuid": offline_uuid.Run,
"packet-info": packet_info.Run,
"print-nbt": print_nbt.Run,
"status": status.Run,
"packet-spy": packet_inspector.Run,
"api": api.Run,
"version": Version,
"extract-mca": extract_mca.Run,
"data": data.Run,
"print-replay": print_replay.Run,
"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) {
@@ -53,14 +75,9 @@ func Help(args []string) (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
// tools/cli/main.go:17:5: subcommands refers to Help
// tools/cli/main.go:22:6: Help refers to subcommands
subcommands["help"] = Help
args := os.Args
// Debug stuff
slog.SetLogLoggerLevel(slog.LevelDebug)
//args = []string{"TEST", "version"}
//args = []string{"TEST", "messenger", "-addr", "localhost"}
if len(args) < 2 {
err = ExpectedASubcommandErr
+211
View File
@@ -0,0 +1,211 @@
package mca_to_tmcpr
import (
"bytes"
"flag"
"fmt"
"log/slog"
"os"
"github.com/admin-else/strom/mc/level"
"github.com/admin-else/strom/mc/level/anvil"
"github.com/admin-else/strom/mc/nbt"
"github.com/admin-else/strom/mc/proto/replay"
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
"github.com/admin-else/strom/mc/util"
)
var (
cmd = flag.NewFlagSet("mca-to-tmcpr", flag.ContinueOnError)
worldPath = cmd.String("world", "", "Path to world directory")
output = cmd.String("output", "", "Output .tmcpr file path")
radius = cmd.Int("radius", 14, "Chunk radius around spawn")
spawnX = cmd.Float64("spawn-x", 0.5, "Spawn X coordinate")
spawnY = cmd.Float64("spawn-y", 100, "Spawn Y coordinate")
spawnZ = cmd.Float64("spawn-z", 0.5, "Spawn Z coordinate")
)
var FullLight [][]uint8
func init() {
light := make([]uint8, 2048)
for i := range light {
light[i] = 0xFF
}
sectionsPerChunk := 384 / 16
FullLight = make([][]uint8, sectionsPerChunk)
for i := range sectionsPerChunk {
FullLight[i] = light
}
}
func GetChunkFromMca(x, z int32, version string) (b []byte, err error) {
xmca := util.FloorDiv(x, anvil.ChunksWidthRegionFile)
zmca := util.FloorDiv(z, anvil.ChunksWidthRegionFile)
x = x % anvil.ChunksWidthRegionFile
if x < 0 {
x += anvil.ChunksWidthRegionFile
}
z = z % anvil.ChunksWidthRegionFile
if z < 0 {
z += anvil.ChunksWidthRegionFile
}
f, err := os.Open(fmt.Sprintf("./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
}
+134
View File
@@ -0,0 +1,134 @@
package messenger
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
"github.com/admin-else/strom/cmd/strom/cmd_util"
"github.com/admin-else/strom/mc/bot/chat"
"github.com/admin-else/strom/mc/bot/keepalive"
"github.com/admin-else/strom/mc/bot/world"
"github.com/admin-else/strom/mc/client"
"github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
)
func stror(a, b string) string {
if a != "" {
return a
}
return b
}
var (
cmd = flag.NewFlagSet("status", flag.ContinueOnError)
connectToFlag = cmd.String("addr", "", "server address to connect to")
accFlag = cmd.String("acc", stror(os.Getenv("STROM_ACC"), "strom_messenger"), "the name to use if longer will be treated as ygg token")
)
type Messenger struct {
*proto.Conn
chat *chat.Module
world *world.Module
}
func (m *Messenger) OnChat(e *v1_21_11.PlayToClientPacketPlayerChat) (err error) {
m.Log.Info("chat", "author", e.SenderUuid, "m", e.PlainMessage)
return
}
func (m *Messenger) OnChatUnsigned(e *v1_21_11.PlayToClientPacketProfilelessChat) (err error) {
m.Log.Info("chatP", "author", e.Name, "m", e.Message)
return
}
func (m *Messenger) OnChatSystem(e *v1_21_11.PlayToClientPacketSystemChat) (err error) {
m.Log.Info("chatS", "m", e.Content)
return
}
func (m *Messenger) OnStdin(e event.Stdin) (err error) {
input := strings.TrimSpace(e.Val)
if strings.HasPrefix(input, ">say ") {
return m.chat.SendMessage(strings.TrimSpace(strings.TrimPrefix(input, ">say")))
}
if strings.HasPrefix(input, ">getblock ") {
return m.handleGetBlock(strings.TrimSpace(strings.TrimPrefix(input, ">getblock")))
}
return m.chat.SendMessage(input)
}
func (m *Messenger) handleGetBlock(args string) (err error) {
fields := strings.Fields(args)
if len(fields) != 3 {
m.Log.Info("getblock", "error", "expected >getblock <x> <y> <z>")
return nil
}
coords := make([]int64, 3)
for i, f := range fields {
coords[i], err = strconv.ParseInt(f, 10, 32)
if err != nil {
m.Log.Info("getblock", "error", fmt.Sprintf("bad coordinate %q", f))
return nil
}
}
stateId, err := m.world.GetBlock(int32(coords[0]), int32(coords[1]), int32(coords[2]))
if err != nil {
m.Log.Info("getblock", "error", err.Error())
return nil
}
block, ok := data.LookupBlockByStateId(m.Version, stateId)
if !ok {
m.Log.Info("getblock", "stateId", stateId, "name", "unknown")
return nil
}
m.Log.Info("getblock", "x", coords[0], "y", coords[1], "z", coords[2], "name", block.Name, "stateId", stateId)
return nil
}
func Run(args []string) (err error) {
err = cmd.Parse(args)
if err != nil {
return
}
acc, err := cmd_util.Account(*accFlag)
if err != nil {
return
}
c, err := client.Login(*connectToFlag, acc)
if err != nil {
return
}
defer c.Close()
chatMod, err := chat.Start(c, acc)
if err != nil {
return
}
// Shareable world storage. Other bots can subscribe to the same World.
w := world.NewWorld(c.Version, -64, 384)
m := &Messenger{
Conn: c,
chat: chatMod,
world: world.Start(c, w),
}
event.StartListingStdin(m.Loop)
m.Register(m.OnStdin)
m.RegisterUntilLatest(m.OnChat, m.OnChatUnsigned, m.OnChatSystem)
keepalive.Start(m.Conn)
return m.StartConn()
}
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"flag"
"fmt"
"github.com/admin-else/strom/crypto"
"github.com/admin-else/strom/mc/crypto"
)
var cmd = flag.NewFlagSet("offline-uuid", flag.ContinueOnError)
+169
View File
@@ -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"
}
}
+98 -24
View File
@@ -1,18 +1,21 @@
package packet_inspector
import (
"encoding/json"
"flag"
"log/slog"
"os"
"time"
"github.com/admin-else/strom/api"
"github.com/admin-else/strom/client"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/proto_generated/v1_21_11"
"github.com/admin-else/strom/server"
"github.com/admin-else/strom/text"
"github.com/admin-else/strom/mc/api"
"github.com/admin-else/strom/mc/client"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/nbt"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_11"
"github.com/admin-else/strom/mc/server"
"github.com/admin-else/strom/mc/text"
)
var Cmd = flag.NewFlagSet("packet-inspector", flag.ContinueOnError)
@@ -22,6 +25,7 @@ var Token = Cmd.String("token", "", "token to use")
var TargetAddr = Cmd.String("addr", "127.0.0.1:25565", "address to connect to")
var ListenAddr = Cmd.String("listen", "127.0.0.1:25566", "address to listen on")
var TrueForward = Cmd.Bool("true-forward", false, "forward packets without modification -name and custom server status wont work")
type Proxy struct {
Client, Servee *proto.Conn
@@ -33,6 +37,10 @@ func (p *Proxy) Start() (err error) {
p.Servee.RegisterCritical(p.OnAnything)
p.Servee.RegisterCriticalUntilLatest(p.OnFinishConfiguration)
p.Servee.RegisterCriticalUntilLatest(p.OnFinishLogin)
p.Servee.RegisterCriticalUntilLatest(p.OnHandshake)
//p.Servee.RegisterCriticalUntilLatest(p.OnCompress)
p.Client.RegisterCriticalUntilLatest(p.OnCompress)
errChan := make(chan error)
go func() {
@@ -45,6 +53,22 @@ func (p *Proxy) Start() (err error) {
return
}
type PacketLogEvent struct {
Time time.Time
Packet proto_base.EncodeDecodeAble
Info PacketInfoNoTypeJson
ClientAddr string
}
type PacketInfoNoTypeJson struct {
Type proto_base.EncodeDecodeAble `json:"-"`
Name string
Direction proto_base.Direction
State proto_base.State
PacketId int32
ProtocolVersion int32
}
func (p *Proxy) OnAnything(e event.Anything) (err error) {
packet, ok := e.Val.(proto_base.EncodeDecodeAble)
if !ok {
@@ -54,6 +78,19 @@ func (p *Proxy) OnAnything(e event.Anything) (err error) {
if !ok {
return
}
jsonBytes, err := json.Marshal(PacketLogEvent{
Time: time.Now(),
Packet: packet,
ClientAddr: p.Client.RemoteAddr().String(),
Info: PacketInfoNoTypeJson(packetInfo),
})
if err != nil {
return
}
// :pray:
_, _ = os.Stdout.Write(jsonBytes)
_, _ = os.Stdout.Write([]byte("\n"))
switch packetInfo.Direction {
case proto_base.ToServer:
err = p.Client.Send(packet)
@@ -74,6 +111,40 @@ func (p *Proxy) OnFinishConfiguration(packet *v1_21_11.ConfigurationToServerPack
return
}
func (p *Proxy) OnFinishLogin(packet *v1_21_11.LoginToServerPacketLoginAcknowledged) (err error) {
err = p.Client.Send(packet)
if err != nil {
return
}
p.Servee.SetState(proto_base.Configuration)
p.Client.SetState(proto_base.Configuration)
err = event.DontForwardErr
return
}
func (p *Proxy) OnHandshake(packet *v1_21_11.HandshakingToServerPacketSetProtocol) (err error) {
err = p.Client.Send(packet)
state := proto_base.State(packet.NextState)
if err != nil {
return
}
p.Servee.SetState(state)
p.Client.SetState(state)
err = event.DontForwardErr
return
}
func (p *Proxy) OnCompress(packet *v1_21_11.LoginToClientPacketCompress) (err error) {
err = p.Servee.Send(packet)
if err != nil {
return
}
p.Servee.SetCompressionThreshold(packet.Threshold)
p.Client.SetCompressionThreshold(packet.Threshold)
err = event.DontForwardErr
return
}
var StatusResponse = server.StatusResponse{
Version: server.StatusResponseVersion{
Name: text.Pretty("STROM"),
@@ -90,38 +161,41 @@ func Run(args []string) (err error) {
if err != nil {
return
}
slog.SetLogLoggerLevel(slog.LevelDebug)
slog.Info("Starting packet inspector", "listen", *ListenAddr)
err = server.StartServerWithOnConn(*ListenAddr, func(serveeConn *proto.Conn) (err error) {
serveeConn.DebugPrintPackets = []string{""}
p := &Proxy{Servee: serveeConn, Client: nil}
var acc = api.NewOfflineAccount(*OfflineNameFlag)
if *Token != "" {
acc, err = api.NewAccountFromYGG(*Token)
if !*TrueForward {
if *Token != "" {
acc, err = api.NewAccountFromYGG(*Token)
if err != nil {
return
}
}
_, err = server.ServeLogin(serveeConn, server.WithOtherAccount(acc), server.WithStatus(StatusResponse), server.WithoutOnlineMode(), server.WithoutEncryption())
if err != nil {
return
}
}
_, err = server.ServeLogin(serveeConn, server.WithOtherAccount(acc), server.WithStatus(StatusResponse), server.WithoutOnlineMode())
if err != nil {
return
}
*TargetAddr, err = client.DoDnsSimple(*TargetAddr)
*TargetAddr, _, _, err = client.DoDNSChecked(nil, *TargetAddr, nil)
if err != nil {
return
}
c, err := client.Connect(*TargetAddr)
if err != nil {
return
}
c.DebugPrintPackets = []string{""}
err = client.LoginRaw(c, acc)
c, err := client.Connect(nil, *TargetAddr, "")
if err != nil {
return
}
defer c.Close()
if !*TrueForward {
err = client.LoginRaw(c, acc)
if err != nil {
return
}
}
p.Client = c
p.Client.AlwaysDecode = true
p.Servee.AlwaysDecode = true
err = p.Start()
return
})
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"fmt"
"os"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/mc/nbt"
)
var (
@@ -4,13 +4,13 @@ import (
"bytes"
"testing"
"github.com/admin-else/strom/proto_generated/v1_21_8"
generated "github.com/admin-else/strom/proto_generated/%v"
)
// Error: %v
func TestFailedPacket%10X(t *testing.T) {
p := v1_21_8.Play%vPacket{}
b := bytes.NewBuffer(%#v)
p := &generated.%v{}
b := bytes.NewBuffer(%#v)
err := p.Decode(b)
if err != nil {
t.Fatal(err)
+161
View File
@@ -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, &timestamp)
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
+1
View File
@@ -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
+1
View File
@@ -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":[]}
+1
View File
@@ -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
+1
View File
@@ -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
View File
@@ -0,0 +1 @@
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be007aa681f7959708e89e39e05390e228753291823a18274068d310a50aa78b
size 526306397
+1
View File
@@ -0,0 +1 @@
270566013
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0da58f5cdd65ef241df8c61f9e633af7317faa77034183d4fb648b039c1e14ed
size 842
+1
View File
@@ -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":[]}
+1
View File
@@ -0,0 +1 @@
{"requiredMods":[{"modID":"viafabricplus","modName":"ViaFabricPlus","modVersion":"4.4.4"}]}
+3
View File
@@ -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 (
"encoding/json"
@@ -6,16 +6,16 @@ import (
"log/slog"
"os"
"github.com/admin-else/strom/api"
"github.com/admin-else/strom/client"
"github.com/admin-else/strom/data"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/nbt"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/proto_generated/v1_21_8"
"github.com/admin-else/strom/server"
"github.com/admin-else/strom/text"
"github.com/admin-else/strom/mc/api"
"github.com/admin-else/strom/mc/client"
"github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/nbt"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/server"
"github.com/admin-else/strom/mc/text"
)
func wontFail[T any](v T, err error) T {
@@ -25,10 +25,7 @@ func wontFail[T any](v T, err error) T {
return v
}
// THIS IS REDUNDANT USE LOGIN PACKET IN MINECRAFT DATA
var (
// Edit these two
Version = "1.21.11"
SaveAs = "data/registry/" + Version + ".nbt"
@@ -113,7 +110,6 @@ func (p *Proxy) OnTags(data *v1_21_8.ConfigurationToClientPacketTags) (err error
func handleClient(c *proto.Conn) (err error) {
acc := api.NewOfflineAccount("RegistryHunter")
// ignore resource leak warning we own c conn so we should not close it here
_, err = server.ServeLogin(c, server.WithOtherAccount(acc), server.WithCompatibleVersions(CompatibleProtocolVersion), server.WithRawStatus(Status))
if err != nil {
return
@@ -150,10 +146,7 @@ func handleClient(c *proto.Conn) (err error) {
return
}
func main() {
func Run(args []string) error {
slog.SetLogLoggerLevel(slog.LevelDebug)
err := server.StartServerWithOnConn(":25566", handleClient)
if err != nil {
panic(err)
}
return server.StartServerWithOnConn(":25566", handleClient)
}
+97
View File
@@ -0,0 +1,97 @@
package serve_tmcpr
import (
"errors"
"flag"
"io"
"log/slog"
"math/rand/v2"
"os"
"time"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto/replay"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/server"
)
type Server struct {
*proto.Conn
*event.Timer
}
var (
cmd = flag.NewFlagSet("serve-tmcpr", flag.ContinueOnError)
tmcprFile = cmd.String("file", "/tmp/world.tmcpr", "Path to .tmcpr replay file")
bindAddr = cmd.String("bind", ":25566", "Address to listen on")
)
func Limbo(c *proto.Conn, tmcprPath string) (err error) {
s := &Server{Conn: c}
_, err = server.ServeLogin(s.Conn, server.WithoutOnlineMode())
if err != nil {
return
}
err = server.ServeConfig(s.Conn)
if err != nil {
return
}
f, err := os.Open(tmcprPath)
if err != nil {
return
}
defer f.Close()
r := replay.NewReader(f)
r.Version = c.ProtocolVersion
start := time.Now()
for {
var packetData []byte
var timestamp uint32
packetData, timestamp, _, err = r.ReadRaw()
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return err
}
time.Sleep(time.Duration(timestamp)*time.Millisecond - time.Since(start))
err = s.SendRaw(packetData)
if err != nil {
return err
}
}
s.Timer = &event.Timer{}
s.Timer.Every(time.Second*15, s.SendKeepAlive)
s.Timer.Start(c.Loop)
return s.StartConn()
}
func (s *Server) SendKeepAlive() error {
return s.Send(&v1_21_8.PlayToClientPacketKeepAlive{KeepAliveId: rand.Int64()})
}
func Run(args []string) error {
slog.SetLogLoggerLevel(slog.LevelDebug)
err := cmd.Parse(args)
if err != nil {
return err
}
if *tmcprFile == "" {
slog.Error("no file specified")
return nil
}
return server.StartServerWithOnConn(*bindAddr, func(c *proto.Conn) error {
return Limbo(c, *tmcprFile)
})
}
+180
View File
@@ -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)
}
+7 -7
View File
@@ -6,11 +6,11 @@ import (
"log/slog"
"time"
"github.com/admin-else/strom/client"
"github.com/admin-else/strom/event"
"github.com/admin-else/strom/proto"
"github.com/admin-else/strom/proto_base"
"github.com/admin-else/strom/proto_generated/v1_21_8"
"github.com/admin-else/strom/mc/client"
"github.com/admin-else/strom/mc/event"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/proto_generated/v1_21_8"
)
var cmd = flag.NewFlagSet("status", flag.ContinueOnError)
@@ -46,7 +46,7 @@ func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
// It does not resolve SRV records.
func StatusRaw(addr string) (s *StatusClient, err error) {
c, err := client.ConnectVersionLess(addr)
c, err := client.ConnectVersionLess(nil, addr)
if err != nil {
return
}
@@ -82,7 +82,7 @@ func Run(args []string) (err error) {
return
}
if *srvFlag {
*addrFlag, err = client.DoDnsSimple(*addrFlag)
*addrFlag, _, _, err = client.DoDNSChecked(nil, *addrFlag, nil)
if err != nil {
return
}
+2 -2
View File
@@ -1,9 +1,9 @@
module github.com/admin-else/strom
go 1.25
go 1.26.2
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/uuid v1.6.0
)
-2
View File
@@ -2,7 +2,5 @@ github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9L
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-80
View File
@@ -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
}
}
BIN
View File
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.
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.
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.

Some files were not shown because too many files have changed in this diff Show More