diff --git a/go.sum b/go.sum index 41f4706..a97eee9 100644 --- a/go.sum +++ b/go.sum @@ -10,6 +10,7 @@ git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 h1:bGG/g4ypjrCJoSvFrP5hafr git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo= github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/TeaEntityLab/fpGo/v2 v2.3.2 h1:qlGjFzXukp9IgVQl32cz4PosbiFEnwXa2EpDbwddHS8= github.com/TeaEntityLab/fpGo/v2 v2.3.2/go.mod h1:b06fRNLSnNiUwfSskBC3f3cocA4mAC2iySdtaP/T9uA= github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE= @@ -19,6 +20,8 @@ github.com/benoitkugler/textlayout-testdata v0.1.1 h1:AvFxBxpfrQd8v55qH59mZOJOQj github.com/benoitkugler/textlayout-testdata v0.1.1/go.mod h1:i/qZl09BbUOtd7Bu/W1CAubRwTWrEXWq6JwMkw8wYxo= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 h1:/G0ghZwrhou0Wq21qc1vXXMm/t/aKWkALWwITptKbE0= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9/go.mod h1:TOk10ahXejq9wkEaym3KPRNeuR/h5Jx+s8QRWIa2oTM= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/changkun/lockfree v0.0.1 h1:5WefVJLglY4IHRqOQmh6Ao6wkJYaJkarshKU8VUtId4= github.com/changkun/lockfree v0.0.1/go.mod h1:3bKiaXn/iNzIPlSvSOMSVbRQUQtAp8qUAyBUtzU11s4= github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:Yg2hDs4b13Evkpj42FU2idX2cVXVFqQSheXYKM86Qsk= @@ -102,6 +105,8 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/repeale/fp-go v0.11.1 h1:Q/e+gNyyHaxKAyfdbBqvip3DxhVWH453R+kthvSr9Mk= github.com/repeale/fp-go v0.11.1/go.mod h1:4KrwQJB1VRY+06CA+jTc4baZetr6o2PeuqnKr5ybQUc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49 h1:LuxslTBxJrrNeKfqoywIERWWhH43TgiVAiPEVlhgNBA= github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49/go.mod h1:fY313ZGG810aWruFYcyq3coFpHDrWJVoMfSRI81y1r4= github.com/sandertv/go-raknet v1.12.0 h1:olUzZlIJyX/pgj/mrsLCZYjKLNDsYiWdvQ4NIm3z0DA= @@ -110,6 +115,7 @@ github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQ github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/subcommands/world/chunk.go b/subcommands/world/chunk.go index b2d5ae7..7342814 100644 --- a/subcommands/world/chunk.go +++ b/subcommands/world/chunk.go @@ -10,7 +10,7 @@ import ( "github.com/sirupsen/logrus" ) -func (w *worldsServer) processChangeDimension(pk *packet.ChangeDimension) { +func (w *worldsHandler) processChangeDimension(pk *packet.ChangeDimension) { if len(w.worldState.chunks) > 0 { w.SaveAndReset() } else { @@ -18,13 +18,14 @@ func (w *worldsServer) processChangeDimension(pk *packet.ChangeDimension) { w.Reset(w.CurrentName()) } dimensionID := pk.Dimension - if w.serverState.ispre118 { + if w.serverState.ispre118 && dimensionID == 0 { dimensionID += 10 } - w.worldState.Dim = dimensionIDMap[uint8(dimensionID)] + d, _ := world.DimensionByID(int(dimensionID)) + w.worldState.dimension = d } -func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) { +func (w *worldsHandler) processLevelChunk(pk *packet.LevelChunk) { // ignore empty chunks THANKS WEIRD SERVER SOFTWARE DEVS if len(pk.RawPayload) == 0 { logrus.Info(locale.Loc("empty_chunk", nil)) @@ -41,7 +42,7 @@ func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) { subChunkCount = int(pk.SubChunkCount) } - ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, w.worldState.Dim.Range(), w.serverState.ispre118, w.bp.HasBlocks()) + ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, w.worldState.dimension.Range(), w.serverState.ispre118, w.bp.HasBlocks()) if err != nil { logrus.Error(err) return @@ -53,21 +54,19 @@ func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) { } w.worldState.chunks[pk.Position] = ch - max := w.worldState.Dim.Range().Height() / 16 + max := w.worldState.dimension.Range().Height() / 16 switch pk.SubChunkCount { case protocol.SubChunkRequestModeLimited: max = int(pk.HighestSubChunk) fallthrough case protocol.SubChunkRequestModeLimitless: var offsetTable []protocol.SubChunkOffset - r := w.worldState.Dim.Range() + r := w.worldState.dimension.Range() for y := int8(r.Min() / 16); y < int8(r.Max()); y++ { offsetTable = append(offsetTable, protocol.SubChunkOffset{0, y, 0}) } - dimId, ok := world.DimensionID(w.worldState.Dim) - _ = ok - + dimId, _ := world.DimensionID(w.worldState.dimension) w.proxy.Server.WritePacket(&packet.SubChunkRequest{ Dimension: int32(dimId), Position: protocol.SubChunkPos{ @@ -86,7 +85,7 @@ func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) { } } -func (w *worldsServer) processSubChunk(pk *packet.SubChunk) { +func (w *worldsHandler) processSubChunk(pk *packet.SubChunk) { posToRedraw := make(map[protocol.ChunkPos]bool) for _, sub := range pk.SubChunkEntries { @@ -124,14 +123,16 @@ func blockPosInChunk(pos protocol.BlockPos) (uint8, int16, uint8) { return uint8(pos.X() & 0x0f), int16(pos.Y() & 0x0f), uint8(pos.Z() & 0x0f) } -func (w *worldsServer) ProcessChunkPackets(pk packet.Packet) packet.Packet { +func (w *worldsHandler) ProcessChunkPackets(pk packet.Packet) packet.Packet { switch pk := pk.(type) { case *packet.ChangeDimension: w.processChangeDimension(pk) case *packet.LevelChunk: w.processLevelChunk(pk) - - w.proxy.SendPopup(locale.Locm("popup_chunk_count", locale.Strmap{"Count": len(w.worldState.chunks), "Name": w.worldState.Name}, len(w.worldState.chunks))) + w.proxy.SendPopup(locale.Locm("popup_chunk_count", locale.Strmap{ + "Count": len(w.worldState.chunks), + "Name": w.worldState.Name, + }, len(w.worldState.chunks))) case *packet.SubChunk: w.processSubChunk(pk) case *packet.BlockActorData: diff --git a/subcommands/world/entity.go b/subcommands/world/entity.go index ab344f4..49af1eb 100644 --- a/subcommands/world/entity.go +++ b/subcommands/world/entity.go @@ -61,7 +61,7 @@ func (e serverEntity) Type() world.EntityType { return e.EntityType } -func (w *worldsServer) processAddActor(pk *packet.AddActor) { +func (w *worldsHandler) processAddActor(pk *packet.AddActor) { e, ok := w.worldState.entities[pk.EntityRuntimeID] if !ok { e = &entityState{ @@ -141,47 +141,49 @@ func entityMetadataToNBT(metadata protocol.EntityMetadata, nbt map[string]any) { } } - if metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagNoAI) { - nbt["IsAutonomous"] = false - } - for k, v := range flagNames { - nbt[v] = metadata.Flag(protocol.EntityDataKeyFlags, k) - } + if _, ok := metadata[protocol.EntityDataKeyFlags]; ok { + if metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagNoAI) { + nbt["IsAutonomous"] = false + } + for k, v := range flagNames { + nbt[v] = metadata.Flag(protocol.EntityDataKeyFlags, k) + } - AlwaysShowName := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagAlwaysShowName) - if AlwaysShowName { - nbt["CustomNameVisible"] = true - } + AlwaysShowName := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagAlwaysShowName) + if AlwaysShowName { + nbt["CustomNameVisible"] = true + } - type effect struct { - Id byte - Amplifier byte - Duration int32 - DurationEasy int32 - DurationNormal int32 - DurationHard int32 - Ambient bool - ShowParticles bool - DisplayOnScreenTextureAnimation bool - } + type effect struct { + Id byte + Amplifier byte + Duration int32 + DurationEasy int32 + DurationNormal int32 + DurationHard int32 + Ambient bool + ShowParticles bool + DisplayOnScreenTextureAnimation bool + } - activeEffects := []effect{} - addEffect := func(id int, showParticles bool) { - activeEffects = append(activeEffects, effect{ - Id: byte(id), - Amplifier: 1, - Duration: 10000000, - ShowParticles: false, - }) - } + activeEffects := []effect{} + addEffect := func(id int, showParticles bool) { + activeEffects = append(activeEffects, effect{ + Id: byte(id), + Amplifier: 1, + Duration: 10000000, + ShowParticles: false, + }) + } - invisible := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) - if invisible { - addEffect(packet.EffectInvisibility, false) - } + invisible := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) + if invisible { + addEffect(packet.EffectInvisibility, false) + } - if len(activeEffects) > 0 { - nbt["ActiveEffects"] = activeEffects + if len(activeEffects) > 0 { + nbt["ActiveEffects"] = activeEffects + } } } @@ -214,7 +216,7 @@ func (s *entityState) ToServerEntity() serverEntity { return e } -func (w *worldsServer) ProcessEntityPackets(pk packet.Packet) packet.Packet { +func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet { switch pk := pk.(type) { case *packet.AddActor: w.processAddActor(pk) diff --git a/subcommands/world/items.go b/subcommands/world/items.go index 0d387b6..c61b09e 100644 --- a/subcommands/world/items.go +++ b/subcommands/world/items.go @@ -17,7 +17,7 @@ type itemContainer struct { Content *packet.InventoryContent } -func (w *worldsServer) processItemPacketsServer(pk packet.Packet) packet.Packet { +func (w *worldsHandler) processItemPacketsServer(pk packet.Packet) packet.Packet { switch pk := pk.(type) { case *packet.ContainerOpen: // add to open containers @@ -116,7 +116,7 @@ func (w *worldsServer) processItemPacketsServer(pk packet.Packet) packet.Packet return pk } -func (w *worldsServer) processItemPacketsClient(pk packet.Packet, forward *bool) packet.Packet { +func (w *worldsHandler) processItemPacketsClient(pk packet.Packet, forward *bool) packet.Packet { switch pk := pk.(type) { case *packet.ItemStackRequest: var requests []protocol.ItemStackRequest @@ -187,7 +187,7 @@ func stackToItem(it protocol.ItemStack) item.Stack { return nbtconv.ReadItem(it.NBTData, &s) } -func (w *worldsServer) playerData() (ret map[string]any) { +func (w *worldsHandler) playerData() (ret map[string]any) { ret = map[string]any{ "format_version": "1.12.0", "identifier": "minecraft:player", diff --git a/subcommands/world/map_item.go b/subcommands/world/map_item.go index b71cf60..7f95034 100644 --- a/subcommands/world/map_item.go +++ b/subcommands/world/map_item.go @@ -10,6 +10,7 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/ui/messages" "github.com/bedrock-tool/bedrocktool/utils" + "github.com/go-gl/mathgl/mgl32" "golang.design/x/lockfree" "github.com/df-mc/dragonfly/server/world/chunk" @@ -79,10 +80,10 @@ type MapUI struct { l sync.RWMutex ticker *time.Ticker - w *worldsServer + w *worldsHandler } -func NewMapUI(w *worldsServer) *MapUI { +func NewMapUI(w *worldsHandler) *MapUI { m := &MapUI{ img: image.NewRGBA(image.Rect(0, 0, 128, 128)), zoomLevel: 16, @@ -265,14 +266,29 @@ func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk, complete bool) m.SchedRedraw() } -func (w *worldsServer) ProcessAnimate(pk *packet.Animate) { +func (w *worldsHandler) ProcessAnimate(pk *packet.Animate) { if pk.ActionType == packet.AnimateActionSwingArm { w.mapUI.ChangeZoom() w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.mapUI.zoomLevel})) } } -func (w *worldsServer) processMapPacketsClient(pk packet.Packet, forward *bool) packet.Packet { +func (w *worldsHandler) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) { + last := w.serverState.PlayerPos + current := TPlayerPos{ + Position: Position, + Pitch: Pitch, + Yaw: Yaw, + HeadYaw: HeadYaw, + } + w.serverState.PlayerPos = current + + if int(last.Position.X()) != int(current.Position.X()) || int(last.Position.Z()) != int(current.Position.Z()) { + w.mapUI.SchedRedraw() + } +} + +func (w *worldsHandler) processMapPacketsClient(pk packet.Packet, forward *bool) packet.Packet { switch pk := pk.(type) { case *packet.MovePlayer: w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) diff --git a/subcommands/world/world.go b/subcommands/world/world.go index 74d9d57..4266d53 100644 --- a/subcommands/world/world.go +++ b/subcommands/world/world.go @@ -41,7 +41,7 @@ type TPlayerPos struct { // the state used for drawing and saving -type worldSettings struct { +type WorldSettings struct { // settings voidGen bool withPacks bool @@ -50,7 +50,7 @@ type worldSettings struct { } type worldState struct { - Dim world.Dimension + dimension world.Dimension chunks map[protocol.ChunkPos]*chunk.Chunk blockNBT map[protocol.SubChunkPos][]map[string]any entities map[uint64]*entityState @@ -69,7 +69,7 @@ type serverState struct { Name string } -type worldsServer struct { +type worldsHandler struct { ctx context.Context proxy *utils.ProxyContext mapUI *MapUI @@ -78,53 +78,10 @@ type worldsServer struct { worldState worldState serverState serverState - settings worldSettings + settings WorldSettings } -func NewWorldsServer(ctx context.Context, proxy *utils.ProxyContext, ServerName string, ui utils.UI) *worldsServer { - w := &worldsServer{ - ctx: ctx, - proxy: proxy, - mapUI: nil, - gui: ui, - bp: behaviourpack.New(ServerName), - - serverState: serverState{ - ispre118: false, - worldCounter: 0, - ChunkRadius: 0, - - playerInventory: nil, - PlayerPos: TPlayerPos{}, - - Name: ServerName, - }, - - settings: worldSettings{}, - } - w.mapUI = NewMapUI(w) - w.Reset(w.CurrentName()) - - w.gui.Message(messages.Init{ - Handler: nil, - }) - - return w -} - -var dimensionIDMap = map[uint8]world.Dimension{ - 0: world.Overworld, - 1: world.Nether, - 2: world.End, - // < 1.18 - 10: world.Overworld_legacy, - 11: world.Nether, - 12: world.End, -} - -var ( - black16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16)) -) +var black16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16)) func init() { for i := 3; i < len(black16x16.Pix); i += 4 { @@ -161,24 +118,87 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error { return err } - w := NewWorldsServer(ctx, proxy, hostname, ui) - w.settings = worldSettings{ + proxy.AlwaysGetPacks = true + proxy.AddHandler(NewWorldsHandler(ctx, ui, WorldSettings{ voidGen: c.EnableVoid, withPacks: c.Packs, saveImage: c.SaveImage, - } + })) - proxy.AlwaysGetPacks = true - proxy.GameDataModifier = func(gd *minecraft.GameData) { - gd.ClientSideGeneration = false + ui.Message(messages.SetUIState(messages.UIStateConnect)) + err = proxy.Run(ctx, serverAddress, hostname) + if err != nil { + return err } + ui.Message(messages.SetUIState(messages.UIStateFinished)) + return nil +} - proxy.AddHandler(&utils.ProxyHandler{ - Name: "Worlds", - ConnectCB: w.OnConnect, +func NewWorldsHandler(ctx context.Context, ui utils.UI, settings WorldSettings) *utils.ProxyHandler { + w := &worldsHandler{ + ctx: ctx, + mapUI: nil, + gui: ui, + bp: nil, + + serverState: serverState{ + ispre118: false, + worldCounter: 0, + ChunkRadius: 0, + + playerInventory: nil, + PlayerPos: TPlayerPos{}, + }, + + settings: WorldSettings{}, + } + w.mapUI = NewMapUI(w) + w.Reset(w.CurrentName()) + + return &utils.ProxyHandler{ + Name: "Worlds", + ProxyRef: func(pc *utils.ProxyContext) { + w.proxy = pc + + w.proxy.AddCommand(utils.IngameCommand{ + Exec: func(cmdline []string) bool { + return w.setWorldName(strings.Join(cmdline, " "), false) + }, + Cmd: protocol.Command{ + Name: "setname", + Description: locale.Loc("setname_desc", nil), + Overloads: []protocol.CommandOverload{{ + Parameters: []protocol.CommandParameter{{ + Name: "name", + Type: protocol.CommandArgTypeString, + Optional: false, + }}, + }}, + }, + }) + + w.proxy.AddCommand(utils.IngameCommand{ + Exec: func(cmdline []string) bool { + return w.setVoidGen(!w.settings.voidGen, false) + }, + Cmd: protocol.Command{ + Name: "void", + Description: locale.Loc("void_desc", nil), + }, + }) + }, + AddressAndName: func(address, hostname string) error { + w.bp = behaviourpack.New(hostname) + w.serverState.Name = hostname + return nil + }, OnClientConnect: func(conn *minecraft.Conn) { w.gui.Message(messages.SetUIState(messages.UIStateConnecting)) }, + GameDataModifier: func(gd *minecraft.GameData) { + gd.ClientSideGeneration = false + }, + ConnectCB: w.OnConnect, PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) { forward := true @@ -203,34 +223,13 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error { } return pk, nil }, - }) - - w.gui.Message(messages.SetUIState(messages.UIStateConnect)) - err = w.proxy.Run(ctx, serverAddress, hostname) - if err != nil { - return err - } - w.SaveAndReset() - ui.Message(messages.SetUIState(messages.UIStateFinished)) - return nil -} - -func (w *worldsServer) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) { - last := w.serverState.PlayerPos - current := TPlayerPos{ - Position: Position, - Pitch: Pitch, - Yaw: Yaw, - HeadYaw: HeadYaw, - } - w.serverState.PlayerPos = current - - if int(last.Position.X()) != int(current.Position.X()) || int(last.Position.Z()) != int(current.Position.Z()) { - w.mapUI.SchedRedraw() + OnEnd: func() { + w.SaveAndReset() + }, } } -func (w *worldsServer) setVoidGen(val bool, fromUI bool) bool { +func (w *worldsHandler) setVoidGen(val bool, fromUI bool) bool { w.settings.voidGen = val var s = locale.Loc("void_generator_false", nil) if w.settings.voidGen { @@ -247,7 +246,7 @@ func (w *worldsServer) setVoidGen(val bool, fromUI bool) bool { return true } -func (w *worldsServer) setWorldName(val string, fromUI bool) bool { +func (w *worldsHandler) setWorldName(val string, fromUI bool) bool { w.worldState.Name = val w.proxy.SendMessage(locale.Loc("worldname_set", locale.Strmap{"Name": w.worldState.Name})) @@ -260,7 +259,7 @@ func (w *worldsServer) setWorldName(val string, fromUI bool) bool { return true } -func (w *worldsServer) CurrentName() string { +func (w *worldsHandler) CurrentName() string { worldName := "world" if w.serverState.worldCounter > 1 { worldName = fmt.Sprintf("world-%d", w.serverState.worldCounter) @@ -268,9 +267,9 @@ func (w *worldsServer) CurrentName() string { return worldName } -func (w *worldsServer) Reset(newName string) { +func (w *worldsHandler) Reset(newName string) { w.worldState = worldState{ - Dim: nil, + dimension: nil, chunks: make(map[protocol.ChunkPos]*chunk.Chunk), blockNBT: make(map[protocol.SubChunkPos][]map[string]any), entities: make(map[uint64]*entityState), @@ -280,23 +279,63 @@ func (w *worldsServer) Reset(newName string) { w.mapUI.Reset() } -// SaveAndReset writes the world to a folder, resets all the chunks -func (w *worldsServer) SaveAndReset() { - - // cull empty chunks - keys := make([]protocol.ChunkPos, 0, len(w.worldState.chunks)) - for cp := range w.worldState.chunks { +func (w *worldState) cullChunks() { + keys := make([]protocol.ChunkPos, 0, len(w.chunks)) + for cp := range w.chunks { keys = append(keys, cp) } - for _, cp := range fp.Filter(func(cp protocol.ChunkPos) bool { return !fp.Some(func(sc *chunk.SubChunk) bool { return !sc.Empty() - })(w.worldState.chunks[cp].Sub()) + })(w.chunks[cp].Sub()) })(keys) { - delete(w.worldState.chunks, cp) + delete(w.chunks, cp) + } +} + +func (w *worldState) Save(folder string) (*mcdb.Provider, error) { + provider, err := mcdb.New(logrus.StandardLogger(), folder, opt.DefaultCompression) + if err != nil { + return nil, err } + // save chunk data + for cp, c := range w.chunks { + provider.SaveChunk((world.ChunkPos)(cp), c, w.dimension) + } + + // save block nbt data + blockNBT := make(map[world.ChunkPos][]map[string]any) + for scp, v := range w.blockNBT { // 3d to 2d + cp := world.ChunkPos{scp.X(), scp.Z()} + blockNBT[cp] = append(blockNBT[cp], v...) + } + for cp, v := range blockNBT { + err = provider.SaveBlockNBT(cp, v, w.dimension) + if err != nil { + logrus.Error(err) + } + } + + // save entities + chunkEntities := make(map[world.ChunkPos][]world.Entity) + for _, es := range w.entities { + cp := world.ChunkPos{int32(es.Position.X()) >> 4, int32(es.Position.Z()) >> 4} + chunkEntities[cp] = append(chunkEntities[cp], es.ToServerEntity()) + } + for cp, v := range chunkEntities { + err = provider.SaveEntities(cp, v, w.dimension) + if err != nil { + logrus.Error(err) + } + } + + return provider, err +} + +// SaveAndReset writes the world to a folder, resets all the chunks +func (w *worldsHandler) SaveAndReset() { + w.worldState.cullChunks() if len(w.worldState.chunks) == 0 { w.Reset(w.CurrentName()) return @@ -312,42 +351,10 @@ func (w *worldsServer) SaveAndReset() { folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.serverState.Name, w.worldState.Name)) os.RemoveAll(folder) os.MkdirAll(folder, 0o777) - - provider, err := mcdb.New(logrus.StandardLogger(), folder, opt.DefaultCompression) + provider, err := w.worldState.Save(folder) if err != nil { - logrus.Fatal(err) - } - - // save chunk data - for cp, c := range w.worldState.chunks { - provider.SaveChunk((world.ChunkPos)(cp), c, w.worldState.Dim) - } - - // save block nbt data - blockNBT := make(map[world.ChunkPos][]map[string]any) - for scp, v := range w.worldState.blockNBT { // 3d to 2d - cp := world.ChunkPos{scp.X(), scp.Z()} - blockNBT[cp] = append(blockNBT[cp], v...) - } - for cp, v := range blockNBT { - err = provider.SaveBlockNBT(cp, v, w.worldState.Dim) - if err != nil { - logrus.Error(err) - } - } - - // save entities - chunkEntities := make(map[world.ChunkPos][]world.Entity) - for _, es := range w.worldState.entities { - cp := world.ChunkPos{int32(es.Position.X()) >> 4, int32(es.Position.Z()) >> 4} - chunkEntities[cp] = append(chunkEntities[cp], es.ToServerEntity()) - } - - for cp, v := range chunkEntities { - err = provider.SaveEntities(cp, v, w.worldState.Dim) - if err != nil { - logrus.Error(err) - } + logrus.Error(err) + return } err = provider.SaveLocalPlayerData(w.playerData()) @@ -366,6 +373,7 @@ func (w *worldsServer) SaveAndReset() { // set gamerules ld := provider.LevelDat() gd := w.proxy.Server.GameData() + ld.RandomSeed = int64(gd.WorldSeed) for _, gr := range gd.GameRules { switch gr.Name { case "commandblockoutput": @@ -434,8 +442,6 @@ func (w *worldsServer) SaveAndReset() { } } - ld.RandomSeed = int64(gd.WorldSeed) - // void world if w.settings.voidGen { ld.FlatWorldLayers = `{"biome_id":1,"block_layers":[{"block_data":0,"block_id":0,"count":1},{"block_data":0,"block_id":0,"count":2},{"block_data":0,"block_id":0,"count":1}],"encoding_version":3,"structure_options":null}` @@ -545,9 +551,8 @@ func (w *worldsServer) SaveAndReset() { w.gui.Message(messages.SetUIState(messages.UIStateMain)) } -func (w *worldsServer) OnConnect(err error) bool { +func (w *worldsHandler) OnConnect(err error) bool { w.gui.Message(messages.SetUIState(messages.UIStateMain)) - if err != nil { return false } @@ -592,40 +597,14 @@ func (w *worldsServer) OnConnect(err error) bool { dimensionID := gd.Dimension if w.serverState.ispre118 { logrus.Info(locale.Loc("using_under_118", nil)) - dimensionID += 10 + if dimensionID == 0 { + dimensionID += 10 + } } - w.worldState.Dim = dimensionIDMap[uint8(dimensionID)] + w.worldState.dimension, _ = world.DimensionByID(int(dimensionID)) } w.proxy.SendMessage(locale.Loc("use_setname", nil)) - - w.proxy.AddCommand(utils.IngameCommand{ - Exec: func(cmdline []string) bool { - return w.setWorldName(strings.Join(cmdline, " "), false) - }, - Cmd: protocol.Command{ - Name: "setname", - Description: locale.Loc("setname_desc", nil), - Overloads: []protocol.CommandOverload{{ - Parameters: []protocol.CommandParameter{{ - Name: "name", - Type: protocol.CommandArgTypeString, - Optional: false, - }}, - }}, - }, - }) - - w.proxy.AddCommand(utils.IngameCommand{ - Exec: func(cmdline []string) bool { - return w.setVoidGen(!w.settings.voidGen, false) - }, - Cmd: protocol.Command{ - Name: "void", - Description: locale.Loc("void_desc", nil), - }, - }) - w.mapUI.Start() return true } diff --git a/ui/gui/pages/settings/settings.go b/ui/gui/pages/settings/settings.go index 5420967..f50ee2a 100644 --- a/ui/gui/pages/settings/settings.go +++ b/ui/gui/pages/settings/settings.go @@ -102,7 +102,6 @@ func (p *Page) Layout(gtx C, th *material.Theme) D { go func() { defer p.Router.Wg.Done() utils.InitDNS() - utils.InitExtraDebug(p.Router.Ctx) err := cmd.Execute(p.Router.Ctx, utils.CurrentUI) if err != nil { diff --git a/utils/handlers/capture.go b/utils/handlers/capture.go index 9d86b36..c02e7b0 100644 --- a/utils/handlers/capture.go +++ b/utils/handlers/capture.go @@ -28,12 +28,12 @@ func dumpPacket(f io.WriteCloser, toServer bool, payload []byte) { f.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB}) } -type PacketCapturer struct { +type packetCapturer struct { proxy *utils.ProxyContext fio *os.File } -func (p *PacketCapturer) AddressAndName(address, hostname string) error { +func (p *packetCapturer) AddressAndName(address, hostname string) error { os.Mkdir("captures", 0o775) fio, err := os.Create(fmt.Sprintf("captures/%s-%s.pcap2", hostname, time.Now().Format("2006-01-02_15-04-05"))) if err != nil { @@ -44,7 +44,7 @@ func (p *PacketCapturer) AddressAndName(address, hostname string) error { return nil } -func (p *PacketCapturer) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) { +func (p *packetCapturer) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) { IsfromClient := utils.ClientAddr.String() == src.String() buf := bytes.NewBuffer(nil) @@ -54,14 +54,14 @@ func (p *PacketCapturer) PacketFunc(header packet.Header, payload []byte, src, d } func NewPacketCapturer() *utils.ProxyHandler { - p := &PacketCapturer{} + p := &packetCapturer{} return &utils.ProxyHandler{ Name: "Packet Capturer", ProxyRef: func(pc *utils.ProxyContext) { p.proxy = pc }, - PacketFunc: p.PacketFunc, AddressAndName: p.AddressAndName, + PacketFunc: p.PacketFunc, OnEnd: func() { p.fio.Close() }, diff --git a/utils/handlers/chat_log.go b/utils/handlers/chat_log.go index 3f485cc..26f6168 100644 --- a/utils/handlers/chat_log.go +++ b/utils/handlers/chat_log.go @@ -10,22 +10,12 @@ import ( "github.com/sirupsen/logrus" ) -type ChatLogger struct { +type chatLogger struct { Verbose bool fio *os.File } -func (c *ChatLogger) AddressAndName(address, hostname string) error { - filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07")) - f, err := os.Create(filename) - if err != nil { - return err - } - c.fio = f - return nil -} - -func (c *ChatLogger) PacketCB(pk packet.Packet, toServer bool, t time.Time) (packet.Packet, error) { +func (c *chatLogger) PacketCB(pk packet.Packet, toServer bool, t time.Time) (packet.Packet, error) { if text, ok := pk.(*packet.Text); ok { logLine := text.Message if c.Verbose { @@ -42,13 +32,21 @@ func (c *ChatLogger) PacketCB(pk packet.Packet, toServer bool, t time.Time) (pac } func NewChatLogger() *utils.ProxyHandler { - p := &ChatLogger{} + c := &chatLogger{} return &utils.ProxyHandler{ - Name: "Packet Capturer", - PacketCB: p.PacketCB, - AddressAndName: p.AddressAndName, + Name: "Packet Capturer", + PacketCB: c.PacketCB, + AddressAndName: func(address, hostname string) error { + filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07")) + f, err := os.Create(filename) + if err != nil { + return err + } + c.fio = f + return nil + }, OnEnd: func() { - p.fio.Close() + c.fio.Close() }, } } diff --git a/utils/handlers/second-user/chunks.go b/utils/handlers/second-user/chunks.go new file mode 100644 index 0000000..62a05a9 --- /dev/null +++ b/utils/handlers/second-user/chunks.go @@ -0,0 +1,96 @@ +package seconduser + +import ( + "github.com/bedrock-tool/bedrocktool/locale" + "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/chunk" + "github.com/sandertv/gophertunnel/minecraft/protocol" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" +) + +func (s *secondaryUser) ResetWorld() { + s.chunks = make(map[world.ChunkPos]*chunk.Chunk) + s.blockNBT = make(map[protocol.SubChunkPos][]map[string]any) +} + +func (s *secondaryUser) processChangeDimension(pk *packet.ChangeDimension) { + s.ResetWorld() + dimensionID := pk.Dimension + if s.ispre118 && dimensionID == 0 { + dimensionID += 10 + } + s.dimension, _ = world.DimensionByID(int(dimensionID)) +} + +func (s *secondaryUser) processLevelChunk(pk *packet.LevelChunk) { + // ignore empty chunks THANKS WEIRD SERVER SOFTWARE DEVS + if len(pk.RawPayload) == 0 { + logrus.Info(locale.Loc("empty_chunk", nil)) + return + } + + var subChunkCount int + switch pk.SubChunkCount { + case protocol.SubChunkRequestModeLimited: + fallthrough + case protocol.SubChunkRequestModeLimitless: + subChunkCount = 0 + default: + subChunkCount = int(pk.SubChunkCount) + } + + ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, s.dimension.Range(), s.ispre118, s.hasCustomBlocks) + if err != nil { + logrus.Error(err) + return + } + if blockNBTs != nil { + s.blockNBT[protocol.SubChunkPos{ + pk.Position.X(), 0, pk.Position.Z(), + }] = blockNBTs + } + s.chunks[world.ChunkPos(pk.Position)] = ch + + switch pk.SubChunkCount { + case protocol.SubChunkRequestModeLimited: + case protocol.SubChunkRequestModeLimitless: + default: + } + + for _, p := range s.server.Players() { + p.Session().ViewChunk(world.ChunkPos(pk.Position), ch, nil) + } +} + +func (s *secondaryUser) processSubChunk(pk *packet.SubChunk) { + offsets := make([]protocol.SubChunkOffset, 0, len(pk.SubChunkEntries)) + for _, sub := range pk.SubChunkEntries { + offsets = append(offsets, sub.Offset) + var ( + absX = pk.Position[0] + int32(sub.Offset[0]) + absY = pk.Position[1] + int32(sub.Offset[1]) + absZ = pk.Position[2] + int32(sub.Offset[2]) + subPos = protocol.SubChunkPos{absX, absY, absZ} + pos = world.ChunkPos{absX, absZ} + ) + ch, ok := s.chunks[pos] + if !ok { + logrus.Error(locale.Loc("subchunk_before_chunk", nil)) + continue + } + blockNBT, err := ch.ApplySubChunkEntry(uint8(absY), &sub) + if err != nil { + logrus.Error(err) + } + if blockNBT != nil { + s.blockNBT[subPos] = blockNBT + } + + chunk.LightArea([]*chunk.Chunk{ch}, 0, 0).Fill() + } + + for _, p := range s.server.Players() { + p.Session().ViewSubChunks(world.SubChunkPos(pk.Position), offsets) + } +} diff --git a/utils/handlers/second-user/listener.go b/utils/handlers/second-user/listener.go new file mode 100644 index 0000000..92436de --- /dev/null +++ b/utils/handlers/second-user/listener.go @@ -0,0 +1,19 @@ +package seconduser + +import "github.com/df-mc/dragonfly/server/session" + +type fwdlistener struct { + Conn chan session.Conn +} + +func (l *fwdlistener) Accept() (session.Conn, error) { + return <-l.Conn, nil +} + +func (l *fwdlistener) Disconnect(conn session.Conn, reason string) error { + return conn.Close() +} + +func (l *fwdlistener) Close() error { + return nil +} diff --git a/utils/handlers/second-user/provider.go b/utils/handlers/second-user/provider.go new file mode 100644 index 0000000..91d1be8 --- /dev/null +++ b/utils/handlers/second-user/provider.go @@ -0,0 +1,64 @@ +package seconduser + +import ( + "sync" + + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/chunk" + "github.com/google/uuid" +) + +type provider struct { + s *secondaryUser +} + +func (p *provider) Settings() *world.Settings { + return &world.Settings{ + Mutex: sync.Mutex{}, + Name: "world", + Spawn: cube.Pos{0, 0, 0}, + DefaultGameMode: world.GameModeCreative, + Difficulty: world.DifficultyNormal, + } +} + +func (p *provider) SaveSettings(*world.Settings) { +} + +func (p *provider) Close() error { + return nil +} + +func (p *provider) LoadPlayerSpawnPosition(uuid uuid.UUID) (pos cube.Pos, exists bool, err error) { + return cube.Pos{0, 0, 0}, false, nil +} + +func (p *provider) SavePlayerSpawnPosition(uuid uuid.UUID, pos cube.Pos) error { + return nil +} + +func (p *provider) LoadChunk(position world.ChunkPos, dim world.Dimension) (c *chunk.Chunk, exists bool, err error) { + c, ok := p.s.chunks[position] + return c, ok, nil +} + +func (p *provider) SaveChunk(position world.ChunkPos, c *chunk.Chunk, dim world.Dimension) error { + return nil +} + +func (p *provider) LoadEntities(position world.ChunkPos, dim world.Dimension, reg world.EntityRegistry) ([]world.Entity, error) { + return nil, nil +} + +func (p *provider) SaveEntities(position world.ChunkPos, entities []world.Entity, dim world.Dimension) error { + return nil +} + +func (p *provider) LoadBlockNBT(position world.ChunkPos, dim world.Dimension) ([]map[string]any, error) { + return nil, nil +} + +func (p *provider) SaveBlockNBT(position world.ChunkPos, data []map[string]any, dim world.Dimension) error { + return nil +} diff --git a/utils/handlers/second-user/second-user.go b/utils/handlers/second-user/second-user.go new file mode 100644 index 0000000..dad98d6 --- /dev/null +++ b/utils/handlers/second-user/second-user.go @@ -0,0 +1,104 @@ +package seconduser + +import ( + "time" + + "github.com/bedrock-tool/bedrocktool/utils" + "github.com/df-mc/dragonfly/server" + "github.com/df-mc/dragonfly/server/player" + "github.com/df-mc/dragonfly/server/player/skin" + "github.com/df-mc/dragonfly/server/session" + "github.com/df-mc/dragonfly/server/world" + "github.com/df-mc/dragonfly/server/world/chunk" + "github.com/go-gl/mathgl/mgl64" + "github.com/sandertv/gophertunnel/minecraft" + "github.com/sandertv/gophertunnel/minecraft/protocol" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" +) + +type secondaryUser struct { + listener *fwdlistener + server *server.Server + + ispre118 bool + hasCustomBlocks bool + + chunks map[world.ChunkPos]*chunk.Chunk + blockNBT map[protocol.SubChunkPos][]map[string]any + dimension world.Dimension + + mainPlayer *player.Player +} + +func NewSecondUser() *utils.ProxyHandler { + s := &secondaryUser{ + listener: &fwdlistener{ + Conn: make(chan session.Conn), + }, + + chunks: make(map[world.ChunkPos]*chunk.Chunk), + blockNBT: make(map[protocol.SubChunkPos][]map[string]any), + dimension: world.Overworld, + } + + s.server = server.Config{ + Listeners: []func(conf server.Config) (server.Listener, error){ + func(conf server.Config) (server.Listener, error) { + return s.listener, nil + }, + }, + Log: logrus.StandardLogger(), + Name: "Secondary", + Generator: func(dim world.Dimension) world.Generator { return &world.NopGenerator{} }, + WorldProvider: &provider{s: s}, + ReadOnlyWorld: true, + }.New() + + go s.loop() + + return &utils.ProxyHandler{ + Name: "Secondary User", + SecondaryClientCB: s.SecondaryClientCB, + OnClientConnect: func(conn *minecraft.Conn) { + id := conn.IdentityData() + s.mainPlayer = player.New(id.DisplayName, skin.New(64, 64), mgl64.Vec3{0, 00}) + }, + PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) { + + switch pk := pk.(type) { + case *packet.LevelChunk: + s.processLevelChunk(pk) + case *packet.SubChunk: + s.processSubChunk(pk) + case *packet.ChangeDimension: + s.processChangeDimension(pk) + + case *packet.MovePlayer: + v := mgl64.Vec3{float64(pk.Position.X()), float64(pk.Position.Y()), float64(pk.Position.Z())} + s.mainPlayer.Teleport(v) + case *packet.PlayerAuthInput: + v := mgl64.Vec3{float64(pk.Position.X()), float64(pk.Position.Y()), float64(pk.Position.Z())} + s.mainPlayer.Teleport(v) + for _, p := range s.server.Players() { + p.Teleport(s.mainPlayer.Position()) + } + } + + return pk, nil + }, + } +} + +func (s *secondaryUser) SecondaryClientCB(conn *minecraft.Conn) { + s.listener.Conn <- conn +} + +func (s *secondaryUser) loop() { + s.server.Listen() + for s.server.Accept(func(p *player.Player) { + logrus.Infof("%s Joined", p.Name()) + p.Teleport(s.mainPlayer.Position()) + }) { + } +} diff --git a/utils/handlers/skins.go b/utils/handlers/skins.go index 6c6b591..efa76bb 100644 --- a/utils/handlers/skins.go +++ b/utils/handlers/skins.go @@ -60,7 +60,7 @@ func (s *SkinSaver) AddSkin(playerName string, playerID uuid.UUID, playerSkin *p } s.playerNames[playerID] = playerName - skin := &utils.Skin{playerSkin} + skin := &utils.Skin{Skin: playerSkin} if s.OnlyIfHasGeometry && !skin.HaveGeometry() { return "", nil, false } diff --git a/utils/iui.go b/utils/iui.go index ebd034e..0a06c2f 100644 --- a/utils/iui.go +++ b/utils/iui.go @@ -74,7 +74,6 @@ func (c *InteractiveCLI) Start(ctx context.Context, cancel context.CancelFunc) e flag.Parse() InitDNS() - InitExtraDebug(ctx) subcommands.Execute(ctx) diff --git a/utils/packet_logger.go b/utils/packet_logger.go index c3461c5..a56ce4f 100644 --- a/utils/packet_logger.go +++ b/utils/packet_logger.go @@ -1,15 +1,17 @@ package utils import ( + "bufio" "bytes" "fmt" "io" "net" + "os" "reflect" "strings" "sync" - "github.com/bedrock-tool/bedrocktool/locale" + "github.com/bedrock-tool/bedrocktool/utils/crypt" "github.com/fatih/color" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" @@ -182,52 +184,87 @@ func DumpStruct(data interface{}) { var ClientAddr net.Addr var pool = packet.NewPool() -func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { - var pk packet.Packet - if pkFunc, ok := pool[header.PacketID]; ok { - pk = pkFunc() - } else { - pk = &packet.Unknown{PacketID: header.PacketID, Payload: payload} - } +func NewDebugLogger(extraVerbose bool) *ProxyHandler { + var logPlain, logCrypt, logCryptEnc io.WriteCloser + var packetsLogF *bufio.Writer - defer func() { - if recoveredErr := recover(); recoveredErr != nil { - logrus.Errorf("%T: %s", pk, recoveredErr.(error)) + if extraVerbose { + // open plain text log + logPlain, err := os.Create("packets.log") + if err != nil { + logrus.Error(err) } - }() - pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0)) - - if FLog != nil { - dmpLock.Lock() - FLog.Write([]byte(dmpStruct(0, pk, true, false) + "\n\n\n")) - dmpLock.Unlock() - } - - pkName := reflect.TypeOf(pk).String()[1:] - if slices.Contains(MutedPackets, pkName) { - return - } - - switch pk := pk.(type) { - case *packet.Disconnect: - logrus.Infof(locale.Loc("disconnect", locale.Strmap{"Pk": pk})) - } - - dirS2C := color.GreenString("S") + "->" + color.CyanString("C") - dirC2S := color.CyanString("C") + "->" + color.GreenString("S") - var dir string = dirS2C - - if ClientAddr != nil { - if src == ClientAddr { - dir = dirC2S - } - } else { - srcAddr, _, _ := net.SplitHostPort(src.String()) - if IPPrivate(net.ParseIP(srcAddr)) { - dir = dirS2C + // open gpg log + logCrypt, err := os.Create("packets.log.gpg") + if err != nil { + logrus.Error(err) + } else { + // encrypter for the log + logCryptEnc, err = crypt.Encer("packets.log", logCrypt) + if err != nil { + logrus.Error(err) + } } + packetsLogF = bufio.NewWriter(io.MultiWriter(logPlain, logCryptEnc)) } - logrus.Debugf("%s 0x%02x, %s", dir, pk.ID(), pkName) + return &ProxyHandler{ + Name: "Debug", + PacketFunc: func(header packet.Header, payload []byte, src, dst net.Addr) { + var pk packet.Packet + if pkFunc, ok := pool[header.PacketID]; ok { + pk = pkFunc() + } else { + pk = &packet.Unknown{PacketID: header.PacketID, Payload: payload} + } + + defer func() { + if recoveredErr := recover(); recoveredErr != nil { + logrus.Errorf("%T: %s", pk, recoveredErr.(error)) + } + }() + pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0)) + + if extraVerbose { + dmpLock.Lock() + packetsLogF.Write([]byte(dmpStruct(0, pk, true, false) + "\n\n\n")) + dmpLock.Unlock() + } + + pkName := reflect.TypeOf(pk).String()[1:] + if !slices.Contains(MutedPackets, pkName) { + dirS2C := color.GreenString("S") + "->" + color.CyanString("C") + dirC2S := color.CyanString("C") + "->" + color.GreenString("S") + var dir string = dirS2C + + if ClientAddr != nil { + if src == ClientAddr { + dir = dirC2S + } + } else { + srcAddr, _, _ := net.SplitHostPort(src.String()) + if IPPrivate(net.ParseIP(srcAddr)) { + dir = dirS2C + } + } + + logrus.Debugf("%s 0x%02x, %s", dir, pk.ID(), pkName) + } + }, + OnEnd: func() { + if packetsLogF != nil { + packetsLogF.Flush() + } + if logPlain != nil { + logPlain.Close() + } + if logCryptEnc != nil { + logCryptEnc.Close() + } + if logCrypt != nil { + logCrypt.Close() + } + }, + } } diff --git a/utils/proxy.go b/utils/proxy.go index 27f12d5..771fadc 100644 --- a/utils/proxy.go +++ b/utils/proxy.go @@ -55,7 +55,10 @@ type ProxyHandler struct { Name string ProxyRef func(*ProxyContext) // - AddressAndName func(address, name string) error + AddressAndName func(address, hostname string) error + + // called to change game data + GameDataModifier func(*minecraft.GameData) // called for every packet PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr) @@ -85,9 +88,6 @@ type ProxyContext struct { CustomClientData *login.ClientData handlers []*ProxyHandler - - // called to change game data - GameDataModifier func(*minecraft.GameData) } func NewProxy() (*ProxyContext, error) { @@ -284,6 +284,10 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err } } + if Options.Debug || Options.ExtraDebug { + p.AddHandler(NewDebugLogger(Options.ExtraDebug)) + } + if strings.HasPrefix(serverAddress, "PCAP!") { return createReplayConnection(ctx, serverAddress[5:], p) } @@ -373,8 +377,10 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err defer p.Server.Close() gd := p.Server.GameData() - if p.GameDataModifier != nil { - p.GameDataModifier(&gd) + for _, handler := range p.handlers { + if handler.GameDataModifier != nil { + handler.GameDataModifier(&gd) + } } // spawn and start the game @@ -427,14 +433,16 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err if len(wantSecondary) > 0 { go func() { - c, err := p.Listener.Accept() - if err != nil { - logrus.Error(err) - return - } + for { + c, err := p.Listener.Accept() + if err != nil { + logrus.Error(err) + return + } - for _, handler := range wantSecondary { - go handler.SecondaryClientCB(c.(*minecraft.Conn)) + for _, handler := range wantSecondary { + go handler.SecondaryClientCB(c.(*minecraft.Conn)) + } } }() } diff --git a/utils/replay.go b/utils/replay.go index d73f57a..71f2192 100644 --- a/utils/replay.go +++ b/utils/replay.go @@ -141,10 +141,6 @@ func createReplayConnection(ctx context.Context, filename string, proxy *ProxyCo dst = client } - if Options.Debug { - PacketLogger(hdr, f.Bytes(), src, dst) - } - for _, handler := range proxy.handlers { if handler.PacketFunc != nil { handler.PacketFunc(hdr, f.Bytes(), src, dst) diff --git a/utils/utils.go b/utils/utils.go index c6706c0..f14aaed 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,14 +2,12 @@ package utils import ( - "bufio" "bytes" "context" "crypto/aes" "crypto/sha256" "encoding/json" "errors" - "io" "net" "os" "path" @@ -18,7 +16,6 @@ import ( "sync" "github.com/bedrock-tool/bedrocktool/locale" - "github.com/bedrock-tool/bedrocktool/utils/crypt" "github.com/google/uuid" "github.com/sandertv/gophertunnel/minecraft" "github.com/sirupsen/logrus" @@ -72,10 +69,6 @@ func connectServer(ctx context.Context, address string, ClientData *login.Client if header.PacketID == packet.IDRequestNetworkSettings { ClientAddr = src } - - if Options.Debug { - PacketLogger(header, payload, src, dst) - } if packetFunc != nil { packetFunc(header, payload, src, dst) } @@ -174,50 +167,3 @@ func CfbDecrypt(data []byte, key []byte) []byte { } return data } - -func InitExtraDebug(ctx context.Context) { - if !Options.ExtraDebug { - return - } - Options.Debug = true - - var logPlain, logCryptEnc io.WriteCloser = nil, nil - - // open plain text log - logPlain, err := os.Create("packets.log") - if err != nil { - logrus.Error(err) - } - - // open gpg log - logCrypt, err := os.Create("packets.log.gpg") - if err != nil { - logrus.Error(err) - } else { - // encrypter for the log - logCryptEnc, err = crypt.Encer("packets.log", logCrypt) - if err != nil { - logrus.Error(err) - } - } - - b := bufio.NewWriter(io.MultiWriter(logPlain, logCryptEnc)) - FLog = b - if err != nil { - logrus.Error(err) - } - go func() { - <-ctx.Done() - b.Flush() - - if logPlain != nil { - logPlain.Close() - } - if logCrypt != nil { - logCrypt.Close() - } - if logCryptEnc != nil { - logCryptEnc.Close() - } - }() -}