diff --git a/go.mod b/go.mod index 91d8f57..d6bae47 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( //replace github.com/df-mc/dragonfly => ./dragonfly -replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.24.8-2 +replace github.com/sandertv/gophertunnel => github.com/olebeck/gophertunnel v1.24.8-3 replace github.com/df-mc/dragonfly => github.com/olebeck/dragonfly v0.8.2-6 diff --git a/go.sum b/go.sum index 4a50a9c..7173809 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,8 @@ github.com/olebeck/gophertunnel v1.24.8-1 h1:ECZs6y/ZtvUjhIPlu3RjJr8c5QkOdxdQnoO github.com/olebeck/gophertunnel v1.24.8-1/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k= github.com/olebeck/gophertunnel v1.24.8-2 h1:T7WY2M/GrKTOgcH1RcDiB8LNlhK+PEzGdHSaYP4V518= github.com/olebeck/gophertunnel v1.24.8-2/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k= +github.com/olebeck/gophertunnel v1.24.8-3 h1:qR5PCFWUAcUureCt36tLqaXWOjHVsFx66PiKm1obtkY= +github.com/olebeck/gophertunnel v1.24.8-3/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k= github.com/olebeck/gophertunnel v1.24.8 h1:jdqBOABDAE1yISqzm9IxIrI+/lJApLBjTieynXUSalw= github.com/olebeck/gophertunnel v1.24.8/go.mod h1:dMOw79FHxr2azEqiGH20AwdljisAN1kqwu5SjPBnZ5k= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/map_item.go b/map_item.go index a1c8af6..8a61c70 100644 --- a/map_item.go +++ b/map_item.go @@ -3,7 +3,6 @@ package main import ( "bytes" "image" - "image/color" "image/draw" "math" "os" @@ -40,7 +39,7 @@ var MAP_ITEM_PACKET packet.InventoryContent = packet.InventoryContent{ type MapUI struct { img *image.RGBA // rendered image - zoom int // chunks per row + minZoom bool // 1 pixel per block chunks_images map[protocol.ChunkPos]*image.RGBA // prerendered chunks needRedraw bool // when the map has updated this is true send_lock *sync.Mutex @@ -49,7 +48,7 @@ type MapUI struct { func NewMapUI() MapUI { return MapUI{ img: image.NewRGBA(image.Rect(0, 0, 128, 128)), - zoom: 64, + minZoom: true, chunks_images: make(map[protocol.ChunkPos]*image.RGBA), needRedraw: true, send_lock: &sync.Mutex{}, @@ -64,11 +63,7 @@ func (m *MapUI) Reset() { // ChangeZoom adds to the zoom value and goes around to 32 once it hits 128 func (m *MapUI) ChangeZoom() { - if m.zoom >= 128 { - m.zoom = 32 // min - } else { - m.zoom += 32 - } + m.minZoom = !m.minZoom m.SchedRedraw() } @@ -121,15 +116,17 @@ func (m *MapUI) Redraw(w *WorldState) { chunks_x := int(max[0] - min[0] + 1) // how many chunk lengths is x coordinate chunks_y := int(max[1] - min[1] + 1) - chunks_per_line := math.Min(16*math.Ceil(math.Min(float64(chunks_x), float64(m.zoom))/16), 32) // either zoom or how many there actually are - px_per_block := float64(128 / chunks_per_line / 16) // how many pixels per block + chunks_per_line := 16 * math.Ceil(math.Min(float64(chunks_x), 32)/16) + if m.minZoom { + chunks_per_line = 8 + } + px_per_block := float64(128 / chunks_per_line / 16) // how many pixels per block + sz_chunk := int(math.Ceil(px_per_block * 16)) for i := 0; i < len(m.img.Pix); i++ { // clear canvas m.img.Pix[i] = 0 } - // - for _ch := range m.chunks_images { relative_middle_x := float64(_ch.X()*16 - middle.X()) relative_middle_z := float64(_ch.Z()*16 - middle.Z()) @@ -137,9 +134,8 @@ func (m *MapUI) Redraw(w *WorldState) { X: int(math.Floor(relative_middle_x*px_per_block)) + 64, Y: int(math.Floor(relative_middle_z*px_per_block)) + 64, } - sz_chunk := int(math.Ceil(px_per_block * 16)) - px_upper := px_pos.Add(image.Point{sz_chunk, sz_chunk}) - if px_pos.In(m.img.Rect) || px_upper.In(m.img.Rect) { + + if !m.img.Rect.Intersect(image.Rect(px_pos.X, px_pos.Y, px_pos.X+sz_chunk, px_pos.Y+sz_chunk)).Empty() { draw_img_scaled_pos(m.img, m.chunks_images[_ch], image.Point{ px_pos.X, px_pos.Y, }, sz_chunk) @@ -168,7 +164,7 @@ func (m *MapUI) Redraw(w *WorldState) { } buf := bytes.NewBuffer(nil) bmp.Encode(buf, img2) - os.WriteFile("test.bmp", buf.Bytes(), 0777) + os.WriteFile("test.bmp", buf.Bytes(), 0o777) } } @@ -184,21 +180,12 @@ func (m *MapUI) Send(w *WorldState) error { m.Redraw(w) } - // (ugh) - pixels := make([][]color.RGBA, 128) - for y := 0; y < 128; y++ { - pixels[y] = make([]color.RGBA, 128) - for x := 0; x < 128; x++ { - pixels[y][x] = m.img.At(x, y).(color.RGBA) - } - } - m.send_lock.Unlock() return w.ClientConn.WritePacket(&packet.ClientBoundMapItemData{ MapID: VIEW_MAP_ID, Width: 128, Height: 128, - Pixels: pixels, + Pixels: img2rgba(m.img), UpdateFlags: 2, }) } diff --git a/utils.go b/utils.go index d4fedd3..4dddd38 100644 --- a/utils.go +++ b/utils.go @@ -8,6 +8,8 @@ import ( "context" "encoding/base64" "fmt" + "image" + "image/color" "io" "io/fs" "log" @@ -18,7 +20,7 @@ import ( "reflect" "regexp" "strings" - "time" + "unsafe" "github.com/sandertv/gophertunnel/minecraft" //"github.com/sandertv/gophertunnel/minecraft/gatherings" @@ -179,6 +181,7 @@ func (p dummyProto) Packets() packet.Pool { return packet.NewPool() } func (p dummyProto) ConvertToLatest(pk packet.Packet, _ *minecraft.Conn) []packet.Packet { return []packet.Packet{pk} } + func (p dummyProto) ConvertFromLatest(pk packet.Packet, _ *minecraft.Conn) []packet.Packet { return []packet.Packet{pk} } @@ -241,17 +244,17 @@ func create_proxy(ctx context.Context, server_address string) (l *minecraft.List clientConn.Close() l.Close() }) - - go func() { - for i := 0; i < 10; i++ { - time.Sleep(1 * time.Second) - clientConn.WritePacket(&packet.Text{ - TextType: packet.TextTypeTip, - Message: a + "\n\n\n\n\n\n", - }) - } - }() - + /* + go func() { + for i := 0; i < 10; i++ { + time.Sleep(1 * time.Second) + clientConn.WritePacket(&packet.Text{ + TextType: packet.TextTypeTip, + Message: a + "\n\n\n\n\n\n", + }) + } + }() + */ return l, clientConn, serverConn, nil } @@ -337,9 +340,9 @@ func unpack_zip(r io.ReaderAt, size int64, unpack_folder string) { for _, src_file := range zr.File { out_path := path.Join(unpack_folder, src_file.Name) if src_file.Mode().IsDir() { - os.Mkdir(out_path, 0755) + os.Mkdir(out_path, 0o755) } else { - os.MkdirAll(path.Dir(out_path), 0755) + os.MkdirAll(path.Dir(out_path), 0o755) fr, _ := src_file.Open() f, _ := os.Create(path.Join(unpack_folder, src_file.Name)) io.Copy(f, fr) @@ -428,3 +431,10 @@ func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { } fmt.Printf("%s 0x%x, %s\n", dir, pk.ID(), pk_name) } + +func img2rgba(img *image.RGBA) []color.RGBA { + header := *(*reflect.SliceHeader)(unsafe.Pointer(&img.Pix)) + header.Len /= 4 + header.Cap /= 4 + return *(*[]color.RGBA)(unsafe.Pointer(&header)) +} diff --git a/world.go b/world.go index 7fa8a95..3ce99fb 100644 --- a/world.go +++ b/world.go @@ -72,25 +72,60 @@ func NewWorldState() *WorldState { } } -var setname_command = protocol.Command{ - Name: "setname", - Description: "set user defined name for this world", - Overloads: []protocol.CommandOverload{ - { - Parameters: []protocol.CommandParameter{ +type IngameCommand struct { + exec func(w *WorldState, cmdline []string) bool + cmd protocol.Command +} + +var IngameCommands = map[string]IngameCommand{ + "setname": { + exec: setnameCommand, + cmd: protocol.Command{ + Name: "setname", + Description: "set user defined name for this world", + Overloads: []protocol.CommandOverload{ { - Name: "name", - Type: protocol.CommandArgTypeFilepath, - Optional: false, + Parameters: []protocol.CommandParameter{ + { + Name: "name", + Type: protocol.CommandArgTypeFilepath, + Optional: false, + }, + }, }, }, }, }, + "void": { + exec: toggleVoid, + cmd: protocol.Command{ + Name: "void", + Description: "toggle if void generator should be used", + }, + }, } -var black_16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16)) +func setnameCommand(w *WorldState, cmdline []string) bool { + w.WorldName = strings.Join(cmdline, " ") + send_message(w.ClientConn, fmt.Sprintf("worldName is now: %s", w.WorldName)) + return true +} + +func toggleVoid(w *WorldState, cmdline []string) bool { + w.voidgen = !w.voidgen + send_message(w.ClientConn, fmt.Sprintf("using void generator: %t", w.voidgen)) + return true +} + +var ( + black_16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16)) + Offset_table [24]protocol.SubChunkOffset +) func init() { + for i := range Offset_table { + Offset_table[i] = protocol.SubChunkOffset{0, int8(i), 0} + } draw.Draw(black_16x16, image.Rect(0, 0, 16, 16), image.Black, image.Point{}, draw.Src) cs := crc32.ChecksumIEEE([]byte(a)) if cs != 0x9747c04f { @@ -100,9 +135,9 @@ func init() { } type WorldCMD struct { - server_address string - packs bool - enableGenerator bool + server_address string + packs bool + enableVoid bool } func (*WorldCMD) Name() string { return "worlds" } @@ -111,8 +146,9 @@ func (*WorldCMD) Synopsis() string { return "download a world from a server" } func (p *WorldCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&p.server_address, "address", "", "remote server address") f.BoolVar(&p.packs, "packs", false, "save resourcepacks to the worlds") - f.BoolVar(&p.enableGenerator, "gen", false, "if true, doesnt make the saved world a void world") + f.BoolVar(&p.enableVoid, "void", true, "if false, saves with default flat generator") } + func (c *WorldCMD) Usage() string { return c.Name() + ": " + c.Synopsis() + "\n" + SERVER_ADDRESS_HELP } @@ -168,16 +204,7 @@ func (w *WorldState) ProcessLevelChunk(pk *packet.LevelChunk) { } else { // request all the subchunks - var Offset_table = []protocol.SubChunkOffset{ - {0, 0, 0}, {0, 1, 0}, {0, 2, 0}, {0, 3, 0}, - {0, 4, 0}, {0, 5, 0}, {0, 6, 0}, {0, 7, 0}, - {0, 8, 0}, {0, 9, 0}, {0, 10, 0}, {0, 11, 0}, - {0, 12, 0}, {0, 13, 0}, {0, 14, 0}, {0, 15, 0}, - {0, 16, 0}, {0, 17, 0}, {0, 18, 0}, {0, 19, 0}, - {0, 20, 0}, {0, 21, 0}, {0, 22, 0}, {0, 23, 0}, - } - - max := len(Offset_table) - 1 + max := w.Dim.Range().Height() / 16 if pk.SubChunkRequestMode == protocol.SubChunkRequestModeLimited { max = int(pk.HighestSubChunk) } @@ -227,7 +254,6 @@ func (w *WorldState) ProcessSubChunk(pk *packet.SubChunk) { func (w *WorldState) ProcessAnimate(pk *packet.Animate) { if pk.ActionType == packet.AnimateActionSwingArm { w.ui.ChangeZoom() - send_popup(w.ClientConn, fmt.Sprintf("Zoom: %d", w.ui.zoom)) w.ui.Send(w) } } @@ -249,10 +275,9 @@ func (w *WorldState) ProcessChangeDimension(pk *packet.ChangeDimension) { func (w *WorldState) ProcessCommand(pk *packet.CommandRequest) bool { cmd := strings.Split(pk.CommandLine, " ") - if cmd[0] == "/setname" && len(cmd) >= 2 { - w.WorldName = strings.Join(cmd[1:], " ") - send_message(w.ClientConn, fmt.Sprintf("worldName is now: %s", w.WorldName)) - return true + name := cmd[0][1:] + if h, ok := IngameCommands[name]; ok { + return h.exec(w, cmd[1:]) } return false } @@ -284,7 +309,9 @@ func (w *WorldState) SaveAndReset() { // open world folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.ServerName, w.WorldName)) - os.MkdirAll(folder, 0777) + os.RemoveAll(folder) + os.MkdirAll(folder, 0o777) + provider, err := mcdb.New(folder, opt.DefaultCompression) if err != nil { log.Fatal(err) @@ -296,7 +323,7 @@ func (w *WorldState) SaveAndReset() { } // save block nbt data - var blockNBT = make(map[protocol.ChunkPos][]map[string]any) + blockNBT := make(map[protocol.ChunkPos][]map[string]any) for scp, v := range w.blockNBT { // 3d to 2d cp := protocol.ChunkPos{scp.X(), scp.Z()} blockNBT[cp] = append(blockNBT[cp], v...) @@ -403,7 +430,7 @@ func (w *WorldState) SaveAndReset() { for k, p := range w.packs { fmt.Printf("Adding resource pack: %s\n", k) pack_folder := path.Join(folder, "resource_packs", k) - os.MkdirAll(pack_folder, 0755) + os.MkdirAll(pack_folder, 0o755) data := make([]byte, p.Len()) p.ReadAt(data, 0) unpack_zip(bytes.NewReader(data), int64(len(data)), pack_folder) @@ -426,7 +453,7 @@ func (c *WorldCMD) handleConn(ctx context.Context, l *minecraft.Listener, cc, sc w.ServerName = server_name w.ClientConn = cc w.ServerConn = sc - w.voidgen = !c.enableGenerator + w.voidgen = c.enableVoid if c.packs { fmt.Println("reformatting packs") @@ -545,7 +572,9 @@ func (c *WorldCMD) handleConn(ctx context.Context, l *minecraft.Listener, cc, sc case *packet.SubChunk: w.ProcessSubChunk(pk) case *packet.AvailableCommands: - pk.Commands = append(pk.Commands, setname_command) + for _, ic := range IngameCommands { + pk.Commands = append(pk.Commands, ic.cmd) + } } if err := w.ClientConn.WritePacket(pk); err != nil {