diff --git a/capture.go b/capture.go index 5db58a4..ffcec33 100644 --- a/capture.go +++ b/capture.go @@ -17,10 +17,13 @@ import ( "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" ) -var SrcIp_client = net.IPv4(127, 0, 0, 1) -var SrcIp_server = net.IPv4(243, 0, 0, 2) +var ( + SrcIp_client = net.IPv4(127, 0, 0, 1) + SrcIp_server = net.IPv4(243, 0, 0, 2) +) func init() { register_command(&CaptureCMD{}) @@ -84,18 +87,15 @@ func (*CaptureCMD) Synopsis() string { return "capture packets in a pcap file" } func (p *CaptureCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&p.server_address, "address", "", "remote server address") } + func (c *CaptureCMD) Usage() string { return c.Name() + ": " + c.Synopsis() + "\n" + SERVER_ADDRESS_HELP } func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - address, hostname, err := server_input(c.server_address) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return 1 - } + log := logrus.New() - listener, serverConn, clientConn, err := create_proxy(ctx, address) + address, hostname, err := server_input(c.server_address) if err != nil { fmt.Fprintln(os.Stderr, err) return 1 @@ -112,44 +112,15 @@ func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interfac _wl := sync.Mutex{} - go func() { - defer listener.Disconnect(clientConn, "connection lost") - defer serverConn.Close() - - for { - pk, err := clientConn.ReadPacket() - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to read packet: %s\n", err) - return - } - - _wl.Lock() - dump_packet(true, w, pk) - _wl.Unlock() - - err = serverConn.WritePacket(pk) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to write packet: %s\n", err) - return - } - } - }() - - for { - pk, err := serverConn.ReadPacket() - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to read packet: %s\n", err) - return 1 - } - + err = create_proxy(ctx, log, address, nil, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) { _wl.Lock() - dump_packet(false, w, pk) + dump_packet(toServer, w, pk) _wl.Unlock() - - err = clientConn.WritePacket(pk) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to write packet: %s\n", err) - return 1 - } + return pk, nil + }) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return 1 } + return 0 } diff --git a/chunk_render.go b/chunk_render.go index cf9a410..ef1f8b8 100644 --- a/chunk_render.go +++ b/chunk_render.go @@ -10,26 +10,7 @@ import ( "github.com/df-mc/dragonfly/server/world/chunk" ) -func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA { - p := cube.Pos{int(x), int(y), int(z)} - have_up := false - p.Side(cube.FaceUp).Neighbours(func(neighbour cube.Pos) { - if neighbour.X() < 0 || neighbour.X() >= 16 || neighbour.Z() < 0 || neighbour.Z() >= 16 { - return - } - if !have_up { - block_rid := c.Block(uint8(neighbour[0]), int16(neighbour[1]), uint8(neighbour[2]), 0) - b, found := world.BlockByRuntimeID(block_rid) - if found { - if _, ok := b.(block.Air); !ok { - if _, ok := b.(block.Water); !ok { - have_up = true - } - } - } - } - }, cube.Range{int(y + 1), int(y + 1)}) - +func blockColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA { col := color.RGBA{0, 0, 0, 255} block_rid := c.Block(x, y, z, 0) if block_rid == 0 && y == 0 { // void @@ -47,6 +28,30 @@ func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA { } */ } + return col +} + +func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA { + p := cube.Pos{int(x), int(y), int(z)} + have_up := false + p.Side(cube.FaceUp).Neighbours(func(neighbour cube.Pos) { + if neighbour.X() < 0 || neighbour.X() >= 16 || neighbour.Z() < 0 || neighbour.Z() >= 16 { + return + } + if !have_up { + block_rid := c.Block(uint8(neighbour[0]), int16(neighbour[1]), uint8(neighbour[2]), 0) + if block_rid > 0 { + b, found := world.BlockByRuntimeID(block_rid) + if found { + if _, ok := b.(block.Air); !ok { + have_up = true + } + } + } + } + }, cube.Range{int(y + 1), int(y + 1)}) + + col := blockColorAt(c, x, y, z) if have_up { if col.R > 10 { @@ -75,11 +80,9 @@ func Chunk2Img(c *chunk.Chunk) *image.RGBA { col := chunkGetColorAt(c, x, height, z) if height_liquid > height { - bw := &block.Water{} - wcol := bw.Color() - col.R = col.R/2 + wcol.R/2 - col.G = col.G/2 + wcol.G/2 - col.B = col.B/2 + wcol.B/2 + bw := (&block.Water{}).Color() + bw.A = uint8(clamp(int(127+(height_liquid-height)*5), 255)) + col = blendColors(col, bw) } img.SetRGBA(int(x), int(z), col) diff --git a/map_item.go b/map_item.go index 8a61c70..1c440be 100644 --- a/map_item.go +++ b/map_item.go @@ -181,7 +181,7 @@ func (m *MapUI) Send(w *WorldState) error { } m.send_lock.Unlock() - return w.ClientConn.WritePacket(&packet.ClientBoundMapItemData{ + return w.proxy.client.WritePacket(&packet.ClientBoundMapItemData{ MapID: VIEW_MAP_ID, Width: 128, Height: 128, diff --git a/proxy.go b/proxy.go new file mode 100644 index 0000000..e52d520 --- /dev/null +++ b/proxy.go @@ -0,0 +1,167 @@ +package main + +import ( + "context" + "errors" + "fmt" + "sync" + + "github.com/sandertv/gophertunnel/minecraft" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sandertv/gophertunnel/minecraft/resource" + "github.com/sirupsen/logrus" +) + +var G_disconnect_reason = "Connection lost" + +type dummyProto struct { + id int32 + ver string +} + +func (p dummyProto) ID() int32 { return p.id } +func (p dummyProto) Ver() string { return p.ver } +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} +} + +type ProxyContext struct { + server *minecraft.Conn + client *minecraft.Conn + listener *minecraft.Listener +} +type ( + PacketCallback func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) + ConnectCallback func(proxy *ProxyContext) +) + +func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCBs []PacketCallback) error { + var c1, c2 *minecraft.Conn + if toServer { + c1 = proxy.client + c2 = proxy.server + } else { + c1 = proxy.server + c2 = proxy.client + } + + for { + if ctx.Err() != nil { + return ctx.Err() + } + + pk, err := c1.ReadPacket() + if err != nil { + return err + } + + for _, packetCB := range packetCBs { + pk, err = packetCB(pk, proxy, toServer) + if err != nil { + return err + } + } + + if pk != nil { + if err := c2.WritePacket(pk); err != nil { + if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { + G_disconnect_reason = disconnect.Error() + } + return err + } + } + } +} + +func create_proxy(ctx context.Context, log *logrus.Logger, server_address string, onConnect ConnectCallback, packetCB PacketCallback) (err error) { + /* + if strings.HasSuffix(server_address, ".pcap") { + return create_replay_connection(server_address) + } + */ + + GetTokenSource() // ask for login before listening + + proxy := ProxyContext{} + + var packs []*resource.Pack + if G_preload_packs { + fmt.Println("Preloading resourcepacks") + serverConn, err := connect_server(ctx, server_address, nil, true) + if err != nil { + return fmt.Errorf("failed to connect to %s: %s", server_address, err) + } + serverConn.Close() + packs = serverConn.ResourcePacks() + fmt.Printf("%d packs loaded\n", len(packs)) + } + + _status := minecraft.NewStatusProvider("Server") + proxy.listener, err = minecraft.ListenConfig{ + StatusProvider: _status, + ResourcePacks: packs, + AcceptedProtocols: []minecraft.Protocol{ + dummyProto{id: 544, ver: "1.19.20"}, + }, + }.Listen("raknet", ":19132") + if err != nil { + return err + } + defer proxy.listener.Close() + + fmt.Printf("Listening on %s\n", proxy.listener.Addr()) + + c, err := proxy.listener.Accept() + if err != nil { + log.Fatal(err) + } + proxy.client = c.(*minecraft.Conn) + + cd := proxy.client.ClientData() + proxy.server, err = connect_server(ctx, server_address, &cd, false) + if err != nil { + return fmt.Errorf("failed to connect to %s: %s", server_address, err) + } + + // spawn and start the game + if err := spawn_conn(ctx, proxy.client, proxy.server); err != nil { + return fmt.Errorf("failed to spawn: %s", err) + } + + defer proxy.server.Close() + defer proxy.listener.Disconnect(proxy.client, G_disconnect_reason) + + if onConnect != nil { + onConnect(&proxy) + } + + wg := sync.WaitGroup{} + + // server to client + wg.Add(1) + go func() { + defer wg.Done() + if err := proxyLoop(ctx, &proxy, false, []PacketCallback{packetCB}); err != nil { + log.Error(err) + return + } + }() + + // client to server + wg.Add(1) + go func() { + defer wg.Done() + if err := proxyLoop(ctx, &proxy, true, []PacketCallback{packetCB}); err != nil { + log.Error(err) + return + } + }() + + wg.Wait() + return nil +} diff --git a/rp.go b/rp.go index 8093f4f..242fb20 100644 --- a/rp.go +++ b/rp.go @@ -4,7 +4,7 @@ import "github.com/sandertv/gophertunnel/minecraft/resource" func (w *WorldState) getPacks() (packs map[string]*resource.Pack, err error) { packs = make(map[string]*resource.Pack) - for _, pack := range w.ServerConn.ResourcePacks() { + for _, pack := range w.proxy.server.ResourcePacks() { packs[pack.Name()] = pack } return diff --git a/skins-proxy.go b/skins-proxy.go index fbe756c..0f61bbc 100644 --- a/skins-proxy.go +++ b/skins-proxy.go @@ -2,13 +2,13 @@ package main import ( "context" - "errors" "flag" "fmt" "os" "github.com/google/subcommands" - "github.com/sandertv/gophertunnel/minecraft" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" ) type SkinProxyCMD struct { @@ -23,76 +23,33 @@ func (c *SkinProxyCMD) SetFlags(f *flag.FlagSet) { f.StringVar(&c.server_address, "address", "", "remote server address") f.StringVar(&c.filter, "filter", "", "player name filter prefix") } + func (c *SkinProxyCMD) Usage() string { return c.Name() + ": " + c.Synopsis() + "\n" + SERVER_ADDRESS_HELP } func (c *SkinProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { + log := logrus.New() + address, hostname, err := server_input(c.server_address) if err != nil { fmt.Fprintln(os.Stderr, err) return 1 } + out_path := fmt.Sprintf("skins/%s", hostname) + os.MkdirAll(out_path, 0o755) - listener, clientConn, serverConn, err := create_proxy(ctx, address) + err = create_proxy(ctx, log, address, nil, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) { + if !toServer { + process_packet_skins(proxy.client, out_path, pk, c.filter) + } + return pk, nil + }) if err != nil { fmt.Fprintln(os.Stderr, err) return 1 } - defer listener.Close() - - out_path := fmt.Sprintf("skins/%s", hostname) - - println("Connected") - println("Press ctrl+c to exit") - - os.MkdirAll(out_path, 0755) - - errs := make(chan error, 2) - go func() { // server -> client - defer serverConn.Close() - defer listener.Disconnect(clientConn, "connection lost") - for { - pk, err := serverConn.ReadPacket() - if err != nil { - if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { - _ = listener.Disconnect(clientConn, disconnect.Error()) - } - return - } - process_packet_skins(clientConn, out_path, pk, c.filter) - - if err = clientConn.WritePacket(pk); err != nil { - return - } - } - }() - - go func() { // client -> server - for { - pk, err := clientConn.ReadPacket() - if err != nil { - return - } - - if err := serverConn.WritePacket(pk); err != nil { - if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { - _ = listener.Disconnect(clientConn, disconnect.Error()) - } - return - } - } - }() - - for { - select { - case err := <-errs: - fmt.Fprintln(os.Stderr, err) - return 1 - case <-ctx.Done(): - return 0 - } - } + return 0 } func init() { diff --git a/utils.go b/utils.go index 4dddd38..ac9db05 100644 --- a/utils.go +++ b/utils.go @@ -23,11 +23,11 @@ import ( "unsafe" "github.com/sandertv/gophertunnel/minecraft" + //"github.com/sandertv/gophertunnel/minecraft/gatherings" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/login" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" - "github.com/sandertv/gophertunnel/minecraft/resource" "golang.org/x/exp/slices" ) @@ -170,94 +170,6 @@ func spawn_conn(ctx context.Context, clientConn *minecraft.Conn, serverConn *min return nil } -type dummyProto struct { - id int32 - ver string -} - -func (p dummyProto) ID() int32 { return p.id } -func (p dummyProto) Ver() string { return p.ver } -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} -} - -func create_proxy(ctx context.Context, server_address string) (l *minecraft.Listener, clientConn, serverConn *minecraft.Conn, err error) { - /* - if strings.HasSuffix(server_address, ".pcap") { - return create_replay_connection(server_address) - } - */ - - GetTokenSource() // ask for login before listening - - var packs []*resource.Pack - if G_preload_packs { - fmt.Println("Preloading resourcepacks") - serverConn, err = connect_server(ctx, server_address, nil, true) - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to connect to %s: %s", server_address, err) - } - serverConn.Close() - packs = serverConn.ResourcePacks() - fmt.Printf("%d packs loaded\n", len(packs)) - } - - _status := minecraft.NewStatusProvider("Server") - listener, err := minecraft.ListenConfig{ - StatusProvider: _status, - ResourcePacks: packs, - AcceptedProtocols: []minecraft.Protocol{ - dummyProto{id: 544, ver: "1.19.20"}, - }, - }.Listen("raknet", ":19132") - if err != nil { - return nil, nil, nil, err - } - l = listener - - fmt.Printf("Listening on %s\n", listener.Addr()) - - c, err := listener.Accept() - if err != nil { - log.Fatal(err) - } - clientConn = c.(*minecraft.Conn) - - cd := clientConn.ClientData() - serverConn, err = connect_server(ctx, server_address, &cd, false) - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to connect to %s: %s", server_address, err) - } - - if err := spawn_conn(ctx, clientConn, serverConn); err != nil { - return nil, nil, nil, fmt.Errorf("failed to spawn: %s", err) - } - - G_exit = append(G_exit, func() { - serverConn.Close() - l.Disconnect(clientConn, "Closing") - 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", - }) - } - }() - */ - return l, clientConn, serverConn, nil -} - var PrivateIPNetworks = []net.IPNet{ { IP: net.ParseIP("10.0.0.0"), @@ -386,7 +298,7 @@ var muted_packets = []string{ "*packet.AddActor", "*packet.UpdateAttributes", "*packet.Interact", - "*packet.LevelEvent", + //"*packet.LevelEvent", "*packet.SetActorData", "*packet.MoveActorDelta", "*packet.MovePlayer", @@ -401,6 +313,7 @@ var muted_packets = []string{ "*packet.SubChunkRequest", "*packet.Animate", "*packet.NetworkStackLatency", + "*packet.InventoryTransaction", } func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { @@ -428,6 +341,10 @@ func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { switch pk := pk.(type) { case *packet.Disconnect: fmt.Printf("Disconnect: %s", pk.Message) + case *packet.Event: + fmt.Printf("Event: %d %+v\n", pk.EventType, pk.EventData) + case *packet.LevelEvent: + fmt.Printf("LevelEvent: %+v\n", pk) } fmt.Printf("%s 0x%x, %s\n", dir, pk.ID(), pk_name) } @@ -438,3 +355,34 @@ func img2rgba(img *image.RGBA) []color.RGBA { header.Cap /= 4 return *(*[]color.RGBA)(unsafe.Pointer(&header)) } + +// LERP is a linear interpolation function +func LERP(p1, p2, alpha float64) float64 { + return (1-alpha)*p1 + alpha*p2 +} + +func blendColorValue(c1, c2, a uint8) uint8 { + return uint8(LERP(float64(c1), float64(c2), float64(a)/float64(0xff))) +} + +func blendAlphaValue(a1, a2 uint8) uint8 { + return uint8(LERP(float64(a1), float64(0xff), float64(a2)/float64(0xff))) +} + +func blendColors(c1, c2 color.RGBA) (ret color.RGBA) { + ret.R = blendColorValue(c1.R, c2.R, c2.A) + ret.G = blendColorValue(c1.G, c2.G, c2.A) + ret.B = blendColorValue(c1.B, c2.B, c2.A) + ret.A = blendAlphaValue(c1.A, c2.A) + return ret +} + +func clamp(a, b int) int { + if a > b { + return b + } + if a < 0 { + return 0 + } + return a +} diff --git a/world.go b/world.go index c580010..28ee8f5 100644 --- a/world.go +++ b/world.go @@ -3,7 +3,6 @@ package main import ( "bytes" "context" - "errors" "flag" "fmt" "hash/crc32" @@ -23,7 +22,6 @@ import ( "github.com/df-mc/goleveldb/leveldb/opt" "github.com/go-gl/mathgl/mgl32" "github.com/google/subcommands" - "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/resource" @@ -42,6 +40,7 @@ type TPlayerPos struct { // the state used for drawing and saving type WorldState struct { + ctx context.Context ispre118 bool voidgen bool chunks map[protocol.ChunkPos]*chunk.Chunk @@ -51,11 +50,11 @@ type WorldState struct { WorldName string ServerName string worldCounter int + withPacks bool packs map[string]*resource.Pack - PlayerPos TPlayerPos - ClientConn *minecraft.Conn - ServerConn *minecraft.Conn + PlayerPos TPlayerPos + proxy *ProxyContext log *logrus.Logger @@ -110,13 +109,13 @@ var IngameCommands = map[string]IngameCommand{ func setnameCommand(w *WorldState, cmdline []string) bool { w.WorldName = strings.Join(cmdline, " ") - send_message(w.ClientConn, fmt.Sprintf("worldName is now: %s", w.WorldName)) + send_message(w.proxy.client, 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)) + send_message(w.proxy.client, fmt.Sprintf("using void generator: %t", w.voidgen)) return true } @@ -166,14 +165,25 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{ return 1 } - listener, clientConn, serverConn, err := create_proxy(ctx, server_address) + w := NewWorldState() + w.log = c.log + w.voidgen = c.enableVoid + w.ServerName = hostname + w.withPacks = c.packs + w.ctx = ctx + + err = create_proxy(ctx, c.log, server_address, w.OnConnect, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) { + if toServer { + pk = w.ProcessPacketClient(pk) + } else { + pk = w.ProcessPacketServer(pk) + } + return pk, nil + }) if err != nil { fmt.Fprintln(os.Stderr, err) return 1 } - - c.handleConn(ctx, listener, clientConn, serverConn, hostname) - return 0 } @@ -215,7 +225,7 @@ func (w *WorldState) ProcessLevelChunk(pk *packet.LevelChunk) { max = int(pk.HighestSubChunk) } - w.ServerConn.WritePacket(&packet.SubChunkRequest{ + w.proxy.server.WritePacket(&packet.SubChunkRequest{ Dimension: int32(w.Dim.EncodeDimension()), Position: protocol.SubChunkPos{ pk.Position.X(), 0, pk.Position.Z(), @@ -352,7 +362,7 @@ func (w *WorldState) SaveAndReset() { // set gamerules ld := provider.LevelDat() - gd := w.ServerConn.GameData() + gd := w.proxy.server.GameData() for _, gr := range gd.GameRules { switch gr.Name { case "commandblockoutput": @@ -453,25 +463,20 @@ func (w *WorldState) SaveAndReset() { w.Reset() } -func (c *WorldCMD) handleConn(ctx context.Context, l *minecraft.Listener, cc, sc *minecraft.Conn, server_name string) { - var err error - w := NewWorldState() - w.ServerName = server_name - w.ClientConn = cc - w.ServerConn = sc - w.voidgen = c.enableVoid - w.log = c.log +func (w *WorldState) OnConnect(proxy *ProxyContext) { + w.proxy = proxy - if c.packs { + if w.withPacks { fmt.Println("reformatting packs") go func() { - w.packs, err = w.getPacks() + w.packs, _ = w.getPacks() }() } { // check game version - gd := w.ServerConn.GameData() + gd := w.proxy.server.GameData() gv := strings.Split(gd.BaseGameVersion, ".") + var err error if len(gv) > 1 { var ver int ver, err = strconv.Atoi(gv[1]) @@ -483,117 +488,75 @@ func (c *WorldCMD) handleConn(ctx context.Context, l *minecraft.Listener, cc, sc if w.ispre118 { fmt.Println("using legacy (< 1.18)") } + + dim_id := gd.Dimension + if w.ispre118 { + dim_id += 10 + } + w.Dim = dimension_ids[uint8(dim_id)] } - send_message(w.ClientConn, "use /setname \nto set the world name") + send_message(w.proxy.client, "use /setname \nto set the world name") G_exit = append(G_exit, func() { w.SaveAndReset() }) - done := make(chan struct{}) - - go func() { // client loop - defer func() { done <- struct{}{} }() - for { - skip := false - pk, err := w.ClientConn.ReadPacket() - if err != nil { - return - } - - switch pk := pk.(type) { - case *packet.MovePlayer: - w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) - case *packet.PlayerAuthInput: - w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) - case *packet.MapInfoRequest: - if pk.MapID == VIEW_MAP_ID { - w.ui.Send(w) - skip = true - } - case *packet.MobEquipment: - if pk.NewItem.Stack.NBTData["map_uuid"] == int64(VIEW_MAP_ID) { - skip = true - } - case *packet.Animate: - w.ProcessAnimate(pk) - case *packet.CommandRequest: - skip = w.ProcessCommand(pk) - } - - if !skip { - if err := w.ServerConn.WritePacket(pk); err != nil { - if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { - _ = l.Disconnect(w.ClientConn, disconnect.Error()) - } - return - } - } - } - }() - go func() { // send map item select { - case <-ctx.Done(): + case <-w.ctx.Done(): return default: - for { - time.Sleep(1 * time.Second) - err := w.ClientConn.WritePacket(&MAP_ITEM_PACKET) + t := time.NewTimer(1 * time.Second) + for range t.C { + err := w.proxy.client.WritePacket(&MAP_ITEM_PACKET) if err != nil { return } } } }() - - go func() { // server loop - defer w.ServerConn.Close() - defer l.Disconnect(w.ClientConn, "connection lost") - defer func() { done <- struct{}{} }() - - gd := w.ServerConn.GameData() - dim_id := gd.Dimension - if w.ispre118 { - dim_id += 10 - } - w.Dim = dimension_ids[uint8(dim_id)] - - for { - pk, err := w.ServerConn.ReadPacket() - if err != nil { - if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok { - _ = l.Disconnect(w.ClientConn, disconnect.Error()) - } - return - } - - switch pk := pk.(type) { - case *packet.ChangeDimension: - w.ProcessChangeDimension(pk) - case *packet.LevelChunk: - w.ProcessLevelChunk(pk) - w.ui.Send(w) - send_popup(w.ClientConn, fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName)) - case *packet.SubChunk: - w.ProcessSubChunk(pk) - case *packet.AvailableCommands: - for _, ic := range IngameCommands { - pk.Commands = append(pk.Commands, ic.cmd) - } - } - - if err := w.ClientConn.WritePacket(pk); err != nil { - return - } - } - }() - - select { - case <-ctx.Done(): - return - case <-done: - return - } +} + +func (w *WorldState) ProcessPacketClient(pk packet.Packet) packet.Packet { + switch pk := pk.(type) { + case *packet.MovePlayer: + w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) + case *packet.PlayerAuthInput: + w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) + case *packet.MapInfoRequest: + if pk.MapID == VIEW_MAP_ID { + w.ui.Send(w) + pk = nil + } + case *packet.MobEquipment: + if pk.NewItem.Stack.NBTData["map_uuid"] == int64(VIEW_MAP_ID) { + pk = nil + } + case *packet.Animate: + w.ProcessAnimate(pk) + case *packet.CommandRequest: + if w.ProcessCommand(pk) { + pk = nil + } + } + return pk +} + +func (w *WorldState) ProcessPacketServer(pk packet.Packet) packet.Packet { + switch pk := pk.(type) { + case *packet.ChangeDimension: + w.ProcessChangeDimension(pk) + case *packet.LevelChunk: + w.ProcessLevelChunk(pk) + w.ui.Send(w) + send_popup(w.proxy.client, fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName)) + case *packet.SubChunk: + w.ProcessSubChunk(pk) + case *packet.AvailableCommands: + for _, ic := range IngameCommands { + pk.Commands = append(pk.Commands, ic.cmd) + } + } + return pk }