diff --git a/subcommands/capture.go b/subcommands/capture.go index b736106..a38a4d1 100644 --- a/subcommands/capture.go +++ b/subcommands/capture.go @@ -72,7 +72,7 @@ func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interfac proxy := utils.NewProxy() proxy.PacketFunc = func(header packet.Header, payload []byte, src, dst net.Addr) { - from_client := src.String() == proxy.Client.LocalAddr().String() + from_client := dst.String() == proxy.Server.RemoteAddr().String() buf := bytes.NewBuffer(nil) header.Write(buf) diff --git a/subcommands/world/chunk.go b/subcommands/world/chunk.go new file mode 100644 index 0000000..652e835 --- /dev/null +++ b/subcommands/world/chunk.go @@ -0,0 +1,112 @@ +package world + +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 (w *WorldState) processChangeDimension(pk *packet.ChangeDimension) { + if len(w.chunks) > 0 { + w.SaveAndReset() + } else { + logrus.Info(locale.Loc("not_saving_empty", nil)) + w.Reset() + } + dim_id := pk.Dimension + if w.ispre118 { + dim_id += 10 + } + w.Dim = dimension_ids[uint8(dim_id)] +} + +func (w *WorldState) processLevelChunk(pk *packet.LevelChunk) { + _, exists := w.chunks[pk.Position] + if exists { + return + } + + ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, int(pk.SubChunkCount), w.Dim.Range(), w.ispre118, w.bp != nil) + if err != nil { + logrus.Error(err) + return + } + if blockNBTs != nil { + w.blockNBT[protocol.SubChunkPos{ + pk.Position.X(), 0, pk.Position.Z(), + }] = blockNBTs + } + + w.chunks[pk.Position] = ch + + if pk.SubChunkRequestMode == protocol.SubChunkRequestModeLegacy { + w.ui.SetChunk(pk.Position, ch) + } else { + w.ui.SetChunk(pk.Position, nil) + // request all the subchunks + + max := w.Dim.Range().Height() / 16 + if pk.SubChunkRequestMode == protocol.SubChunkRequestModeLimited { + max = int(pk.HighestSubChunk) + } + + w.proxy.Server.WritePacket(&packet.SubChunkRequest{ + Dimension: int32(w.Dim.EncodeDimension()), + Position: protocol.SubChunkPos{ + pk.Position.X(), 0, pk.Position.Z(), + }, + Offsets: Offset_table[:max], + }) + } +} + +func (w *WorldState) processSubChunk(pk *packet.SubChunk) { + pos_to_redraw := make(map[protocol.ChunkPos]bool) + + for _, sub := range pk.SubChunkEntries { + var ( + abs_x = pk.Position[0] + int32(sub.Offset[0]) + abs_y = pk.Position[1] + int32(sub.Offset[1]) + abs_z = pk.Position[2] + int32(sub.Offset[2]) + subpos = protocol.SubChunkPos{abs_x, abs_y, abs_z} + pos = protocol.ChunkPos{abs_x, abs_z} + ) + ch := w.chunks[pos] + if ch == nil { + logrus.Error(locale.Loc("subchunk_before_chunk", nil)) + continue + } + blockNBT, err := ch.ApplySubChunkEntry(uint8(abs_y), &sub) + if err != nil { + logrus.Error(err) + } + if blockNBT != nil { + w.blockNBT[subpos] = blockNBT + } + + pos_to_redraw[pos] = true + } + + // redraw the chunks + for pos := range pos_to_redraw { + w.ui.SetChunk(pos, w.chunks[pos]) + } + w.ui.SchedRedraw() +} + +func (w *WorldState) 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.chunks), "Name": w.WorldName}, len(w.chunks))) + case *packet.SubChunk: + w.processSubChunk(pk) + } + return pk +} diff --git a/subcommands/world/entity.go b/subcommands/world/entity.go new file mode 100644 index 0000000..2a22d88 --- /dev/null +++ b/subcommands/world/entity.go @@ -0,0 +1,133 @@ +package world + +import ( + "github.com/bedrock-tool/bedrocktool/utils/behaviourpack" + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/world" + "github.com/go-gl/mathgl/mgl32" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" +) + +type entityState struct { + RuntimeId uint64 + UniqueId int64 + EntityType string + + Position mgl32.Vec3 + Pitch, Yaw float32 + HeadYaw, BodyYaw float32 + Velocity mgl32.Vec3 + + Metadata map[uint32]any +} + +type serverEntity struct { + world.Entity + EntityType serverEntityType +} + +type serverEntityType struct { + world.SaveableEntityType + Encoded string + NBT map[string]any +} + +func (t serverEntityType) EncodeEntity() string { + return t.Encoded +} + +func (t serverEntityType) DecodeNBT(m map[string]any) world.Entity { + return nil // not implemented, and never should +} + +func (t serverEntityType) EncodeNBT(e *serverEntity) map[string]any { + return t.NBT +} + +func (t serverEntityType) BBox(e world.Entity) cube.BBox { + return cube.Box(0, 0, 0, 1, 1, 1) +} + +func (e serverEntity) Type() world.EntityType { + return e.EntityType +} + +func (w *WorldState) processAddActor(pk *packet.AddActor) { + e, ok := w.entities[pk.EntityRuntimeID] + if !ok { + e = &entityState{ + RuntimeId: pk.EntityRuntimeID, + UniqueId: pk.EntityUniqueID, + EntityType: pk.EntityType, + Metadata: make(map[uint32]any), + } + w.entities[pk.EntityRuntimeID] = e + + w.bp.AddEntity(behaviourpack.EntityIn{ + Identifier: pk.EntityType, + Attr: pk.Attributes, + }) + } + + e.Position = pk.Position + e.Pitch = pk.Pitch + e.Yaw = pk.Yaw + e.BodyYaw = pk.BodyYaw + e.HeadYaw = pk.HeadYaw + e.Velocity = pk.Velocity + + for k, v := range pk.EntityMetadata { + e.Metadata[k] = v + } +} + +func (w *WorldState) ProcessEntityPackets(pk packet.Packet) packet.Packet { + switch pk := pk.(type) { + case *packet.AddActor: + w.processAddActor(pk) + case *packet.RemoveActor: + delete(w.entities, uint64(pk.EntityUniqueID)) + case *packet.SetActorData: + e, ok := w.entities[pk.EntityRuntimeID] + if ok { + for k, v := range pk.EntityMetadata { + e.Metadata[k] = v + } + } + case *packet.SetActorMotion: + e, ok := w.entities[pk.EntityRuntimeID] + if ok { + e.Velocity = pk.Velocity + } + case *packet.MoveActorDelta: + e, ok := w.entities[pk.EntityRuntimeID] + if ok { + if pk.Flags&packet.MoveActorDeltaFlagHasX != 0 { + e.Position[0] = pk.Position[0] + } + if pk.Flags&packet.MoveActorDeltaFlagHasY != 0 { + e.Position[1] = pk.Position[1] + } + if pk.Flags&packet.MoveActorDeltaFlagHasZ != 0 { + e.Position[2] = pk.Position[2] + } + if pk.Flags&packet.MoveActorDeltaFlagHasRotX != 0 { + e.Pitch = pk.Rotation.X() + } + if pk.Flags&packet.MoveActorDeltaFlagHasRotY != 0 { + e.Yaw = pk.Rotation.Y() + } + //if pk.Flags&packet.MoveActorDeltaFlagHasRotZ != 0 { + // no roll + //} + } + case *packet.MoveActorAbsolute: + e, ok := w.entities[pk.EntityRuntimeID] + if ok { + e.Position = pk.Position + e.Pitch = pk.Rotation.X() + e.Yaw = pk.Rotation.Y() + } + } + return pk +} diff --git a/subcommands/world/items.go b/subcommands/world/items.go new file mode 100644 index 0000000..be2c1b6 --- /dev/null +++ b/subcommands/world/items.go @@ -0,0 +1,167 @@ +package world + +import ( + "github.com/bedrock-tool/bedrocktool/locale" + "github.com/bedrock-tool/bedrocktool/utils/nbtconv" + "github.com/df-mc/dragonfly/server/block" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/item/inventory" + "github.com/df-mc/dragonfly/server/world" + "github.com/sandertv/gophertunnel/minecraft/protocol" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" +) + +type itemContainer struct { + OpenPacket *packet.ContainerOpen + Content *packet.InventoryContent +} + +func (w *WorldState) processItemPacketsServer(pk packet.Packet) packet.Packet { + switch pk := pk.(type) { + case *packet.ContainerOpen: + if w.experimentInventory { + // add to open containers + existing, ok := w.openItemContainers[pk.WindowID] + if !ok { + existing = &itemContainer{} + } + w.openItemContainers[pk.WindowID] = &itemContainer{ + OpenPacket: pk, + Content: existing.Content, + } + } + case *packet.InventoryContent: + if w.experimentInventory { + // save content + existing, ok := w.openItemContainers[byte(pk.WindowID)] + if !ok { + if pk.WindowID == 0x0 { // inventory + w.openItemContainers[byte(pk.WindowID)] = &itemContainer{ + Content: pk, + } + } + break + } + existing.Content = pk + } + case *packet.ContainerClose: + if w.experimentInventory { + switch pk.WindowID { + case protocol.WindowIDArmour: // todo handle + case protocol.WindowIDOffHand: // todo handle + case protocol.WindowIDUI: + case protocol.WindowIDInventory: // todo handle + default: + // find container info + existing, ok := w.openItemContainers[byte(pk.WindowID)] + if !ok { + logrus.Warn(locale.Loc("warn_window_closed_not_open", nil)) + break + } + + if existing.Content == nil { + break + } + + pos := existing.OpenPacket.ContainerPosition + cp := protocol.SubChunkPos{pos.X() << 4, pos.Z() << 4} + + // create inventory + inv := inventory.New(len(existing.Content.Content), nil) + for i, c := range existing.Content.Content { + item := stackToItem(c.Stack) + inv.SetItem(i, item) + } + + // put into subchunk + nbts := w.blockNBT[cp] + for i, v := range nbts { + nbt_pos := protocol.BlockPos{v["x"].(int32), v["y"].(int32), v["z"].(int32)} + if nbt_pos == pos { + w.blockNBT[cp][i]["Items"] = nbtconv.InvToNBT(inv) + } + } + + w.proxy.SendMessage(locale.Loc("saved_block_inv", nil)) + + // remove it again + delete(w.openItemContainers, byte(pk.WindowID)) + } + } + case *packet.ItemComponent: + w.bp.ApplyComponentEntries(pk.Items) + } + return pk +} + +func (w *WorldState) processItemPacketsClient(pk packet.Packet, forward *bool) packet.Packet { + switch pk := pk.(type) { + case *packet.ItemStackRequest: + var requests []protocol.ItemStackRequest + for _, isr := range pk.Requests { + for _, sra := range isr.Actions { + if sra, ok := sra.(*protocol.TakeStackRequestAction); ok { + if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { + continue + } + } + if sra, ok := sra.(*protocol.DropStackRequestAction); ok { + if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { + continue + } + } + if sra, ok := sra.(*protocol.DestroyStackRequestAction); ok { + if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { + continue + } + } + if sra, ok := sra.(*protocol.PlaceInContainerStackRequestAction); ok { + if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { + continue + } + } + if sra, ok := sra.(*protocol.TakeOutContainerStackRequestAction); ok { + if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { + continue + } + } + if sra, ok := sra.(*protocol.DestroyStackRequestAction); ok { + if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { + continue + } + } + } + requests = append(requests, isr) + } + pk.Requests = requests + case *packet.MobEquipment: + if pk.NewItem.Stack.NBTData["map_uuid"] == int64(VIEW_MAP_ID) { + *forward = false + } + } + return pk +} + +// stackToItem converts a network ItemStack representation back to an item.Stack. +func stackToItem(it protocol.ItemStack) item.Stack { + t, ok := world.ItemByRuntimeID(it.NetworkID, int16(it.MetadataValue)) + if !ok { + t = block.Air{} + } + if it.BlockRuntimeID > 0 { + // It shouldn't matter if it (for whatever reason) wasn't able to get the block runtime ID, + // since on the next line, we assert that the block is an item. If it didn't succeed, it'll + // return air anyway. + b, _ := world.BlockByRuntimeID(uint32(it.BlockRuntimeID)) + if t, ok = b.(world.Item); !ok { + t = block.Air{} + } + } + //noinspection SpellCheckingInspection + if nbter, ok := t.(world.NBTer); ok && len(it.NBTData) != 0 { + t = nbter.DecodeNBT(it.NBTData).(world.Item) + } + s := item.NewStack(t, int(it.Count)) + return nbtconv.ReadItem(it.NBTData, &s) +} diff --git a/subcommands/world/map_item.go b/subcommands/world/map_item.go index c866da0..d861daf 100644 --- a/subcommands/world/map_item.go +++ b/subcommands/world/map_item.go @@ -111,6 +111,21 @@ func (m *MapUI) Start() { } } }() + go func() { // send map item + t := time.NewTicker(1 * time.Second) + for range t.C { + if m.w.ctx.Err() != nil { + return + } + if m.w.proxy.Client != nil { + err := m.w.proxy.Client.WritePacket(&MAP_ITEM_PACKET) + if err != nil { + logrus.Error(err) + return + } + } + } + }() } func (m *MapUI) Stop() { @@ -139,21 +154,6 @@ func (m *MapUI) SchedRedraw() { m.needRedraw = true } -// draw_img_scaled_pos draws src onto dst at bottom_left, scaled to size -func draw_img_scaled_pos(dst *image.RGBA, src *image.RGBA, bottom_left image.Point, size_scaled int) { - sbx := src.Bounds().Dx() - ratio := int(float64(sbx) / float64(size_scaled)) - - for x_out := bottom_left.X; x_out < bottom_left.X+size_scaled; x_out++ { - for y_out := bottom_left.Y; y_out < bottom_left.Y+size_scaled; y_out++ { - x_in := (x_out - bottom_left.X) * ratio - y_in := (y_out - bottom_left.Y) * ratio - c := src.At(x_in, y_in) - dst.Set(x_out, y_out, c) - } - } -} - // draw chunk images to the map image func (m *MapUI) Redraw() { for { @@ -191,7 +191,7 @@ func (m *MapUI) Redraw() { } 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.renderedChunks[_ch], px_pos, sz_chunk) + utils.Draw_img_scaled_pos(m.img, m.renderedChunks[_ch], px_pos, sz_chunk) } } @@ -235,3 +235,20 @@ func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk) { m.renderQueue.Enqueue(&RenderElem{pos, ch}) m.SchedRedraw() } + +func (w *WorldState) 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) + case *packet.PlayerAuthInput: + w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) + case *packet.MapInfoRequest: + if pk.MapID == VIEW_MAP_ID { + w.ui.SchedRedraw() + *forward = false + } + case *packet.Animate: + w.ProcessAnimate(pk) + } + return pk +} diff --git a/subcommands/world/world.go b/subcommands/world/world.go index 7df846e..639a52e 100644 --- a/subcommands/world/world.go +++ b/subcommands/world/world.go @@ -7,24 +7,18 @@ import ( "flag" "fmt" "image" - "image/draw" "image/png" "math/rand" "os" "path" "strconv" "strings" - "time" "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils/behaviourpack" - "github.com/bedrock-tool/bedrocktool/utils/nbtconv" - "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/item" - "github.com/df-mc/dragonfly/server/item/inventory" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/chunk" "github.com/df-mc/dragonfly/server/world/mcdb" @@ -33,10 +27,7 @@ import ( "github.com/google/subcommands" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" - "github.com/sandertv/gophertunnel/minecraft/resource" "github.com/sirupsen/logrus" - //_ "github.com/df-mc/dragonfly/server/block" // to load blocks - //_ "net/http/pprof" ) type TPlayerPos struct { @@ -46,11 +37,6 @@ type TPlayerPos struct { HeadYaw float32 } -type itemContainer struct { - OpenPacket *packet.ContainerOpen - Content *packet.InventoryContent -} - // the state used for drawing and saving type WorldState struct { @@ -60,12 +46,12 @@ type WorldState struct { chunks map[protocol.ChunkPos]*chunk.Chunk blockNBT map[protocol.SubChunkPos][]map[string]any openItemContainers map[byte]*itemContainer + entities map[uint64]*entityState Dim world.Dimension WorldName string ServerName string worldCounter int - packs map[string]*resource.Pack bp *behaviourpack.BehaviourPack withPacks bool @@ -84,6 +70,7 @@ func NewWorldState() *WorldState { chunks: make(map[protocol.ChunkPos]*chunk.Chunk), blockNBT: make(map[protocol.SubChunkPos][]map[string]any), openItemContainers: make(map[byte]*itemContainer), + entities: make(map[uint64]*entityState), Dim: nil, WorldName: "world", PlayerPos: TPlayerPos{}, @@ -111,7 +98,9 @@ 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) + for i := 3; i < len(black_16x16.Pix); i += 4 { + black_16x16.Pix[i] = 255 + } utils.RegisterCommand(&WorldCMD{}) } @@ -139,12 +128,6 @@ func (c *WorldCMD) Usage() string { } func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - /* - go func() { - http.ListenAndServe(":8000", nil) - }() - */ - server_address, hostname, err := utils.ServerInput(ctx, c.Address) if err != nil { logrus.Error(err) @@ -164,12 +147,19 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{ proxy.AlwaysGetPacks = true proxy.ConnectCB = w.OnConnect proxy.PacketCB = func(pk packet.Packet, proxy *utils.ProxyContext, toServer bool) (packet.Packet, error) { - var forward bool + var forward bool = true + if toServer { - pk, forward = w.ProcessPacketClient(pk) + // from client + pk = w.processItemPacketsClient(pk, &forward) + pk = w.processMapPacketsClient(pk, &forward) } else { - pk, forward = w.ProcessPacketServer(pk) + // from server + pk = w.processItemPacketsServer(pk) + pk = w.ProcessChunkPackets(pk) + pk = w.ProcessEntityPackets(pk) } + if !forward { return nil, nil } @@ -185,98 +175,6 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{ return 0 } -func (w *WorldState) setnameCommand(cmdline []string) bool { - w.WorldName = strings.Join(cmdline, " ") - w.proxy.SendMessage(locale.Loc("worldname_set", locale.Strmap{"Name": w.WorldName})) - return true -} - -func (w *WorldState) toggleVoid(cmdline []string) bool { - w.voidgen = !w.voidgen - var s string - if w.voidgen { - s = locale.Loc("void_generator_true", nil) - } else { - s = locale.Loc("void_generator_false", nil) - } - w.proxy.SendMessage(s) - return true -} - -func (w *WorldState) ProcessLevelChunk(pk *packet.LevelChunk) { - _, exists := w.chunks[pk.Position] - if exists { - return - } - - ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, int(pk.SubChunkCount), w.Dim.Range(), w.ispre118, w.bp != nil) - if err != nil { - logrus.Error(err) - return - } - if blockNBTs != nil { - w.blockNBT[protocol.SubChunkPos{ - pk.Position.X(), 0, pk.Position.Z(), - }] = blockNBTs - } - - w.chunks[pk.Position] = ch - - if pk.SubChunkRequestMode == protocol.SubChunkRequestModeLegacy { - w.ui.SetChunk(pk.Position, ch) - } else { - w.ui.SetChunk(pk.Position, nil) - // request all the subchunks - - max := w.Dim.Range().Height() / 16 - if pk.SubChunkRequestMode == protocol.SubChunkRequestModeLimited { - max = int(pk.HighestSubChunk) - } - - w.proxy.Server.WritePacket(&packet.SubChunkRequest{ - Dimension: int32(w.Dim.EncodeDimension()), - Position: protocol.SubChunkPos{ - pk.Position.X(), 0, pk.Position.Z(), - }, - Offsets: Offset_table[:max], - }) - } -} - -func (w *WorldState) ProcessSubChunk(pk *packet.SubChunk) { - pos_to_redraw := make(map[protocol.ChunkPos]bool) - - for _, sub := range pk.SubChunkEntries { - var ( - abs_x = pk.Position[0] + int32(sub.Offset[0]) - abs_y = pk.Position[1] + int32(sub.Offset[1]) - abs_z = pk.Position[2] + int32(sub.Offset[2]) - subpos = protocol.SubChunkPos{abs_x, abs_y, abs_z} - pos = protocol.ChunkPos{abs_x, abs_z} - ) - ch := w.chunks[pos] - if ch == nil { - logrus.Error(locale.Loc("subchunk_before_chunk", nil)) - continue - } - blockNBT, err := ch.ApplySubChunkEntry(uint8(abs_y), &sub) - if err != nil { - logrus.Error(err) - } - if blockNBT != nil { - w.blockNBT[subpos] = blockNBT - } - - pos_to_redraw[pos] = true - } - - // redraw the chunks - for pos := range pos_to_redraw { - w.ui.SetChunk(pos, w.chunks[pos]) - } - w.ui.SchedRedraw() -} - func (w *WorldState) ProcessAnimate(pk *packet.Animate) { if pk.ActionType == packet.AnimateActionSwingArm { w.ui.ChangeZoom() @@ -284,20 +182,6 @@ func (w *WorldState) ProcessAnimate(pk *packet.Animate) { } } -func (w *WorldState) ProcessChangeDimension(pk *packet.ChangeDimension) { - if len(w.chunks) > 0 { - w.SaveAndReset() - } else { - logrus.Info(locale.Loc("not_saving_empty", nil)) - w.Reset() - } - dim_id := pk.Dimension - if w.ispre118 { - dim_id += 10 - } - w.Dim = dimension_ids[uint8(dim_id)] -} - func (w *WorldState) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) { last := w.PlayerPos w.PlayerPos = TPlayerPos{ @@ -342,13 +226,31 @@ func (w *WorldState) SaveAndReset() { } // save block nbt data - blockNBT := make(map[protocol.ChunkPos][]map[string]any) + blockNBT := make(map[world.ChunkPos][]map[string]any) for scp, v := range w.blockNBT { // 3d to 2d - cp := protocol.ChunkPos{scp.X(), scp.Z()} + cp := world.ChunkPos{scp.X(), scp.Z()} blockNBT[cp] = append(blockNBT[cp], v...) } for cp, v := range blockNBT { - err = provider.SaveBlockNBT((world.ChunkPos)(cp), v, w.Dim) + err = provider.SaveBlockNBT(cp, v, w.Dim) + if err != nil { + logrus.Error(err) + } + } + + 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], serverEntity{ + EntityType: serverEntityType{ + Encoded: es.EntityType, + }, + }) + } + + for cp, v := range chunkEntities { + err = provider.SaveEntities(cp, v, w.Dim) if err != nil { logrus.Error(err) } @@ -473,23 +375,7 @@ func (w *WorldState) SaveAndReset() { } } - { - var rdeps []dep - for k, p := range w.packs { - logrus.Infof(locale.Loc("adding_pack", locale.Strmap{"Name": k})) - pack_folder := path.Join(folder, "resource_packs", k) - os.MkdirAll(pack_folder, 0o755) - data := make([]byte, p.Len()) - p.ReadAt(data, 0) - utils.UnpackZip(bytes.NewReader(data), int64(len(data)), pack_folder) - rdeps = append(rdeps, dep{ - PackId: p.Manifest().Header.Name, - Version: p.Manifest().Header.Version, - }) - } - add_packs_json("world_resource_packs.json", rdeps) - } - + // save behaviourpack if w.bp.HasContent() { name := strings.ReplaceAll(w.ServerName, "/", "-") + "_blocks" pack_folder := path.Join(folder, "behavior_packs", name) @@ -505,6 +391,33 @@ func (w *WorldState) SaveAndReset() { PackId: w.bp.Manifest.Header.UUID, Version: w.bp.Manifest.Header.Version, }}) + + // force resource packs for worlds with custom blocks + w.withPacks = true + } + + // add resource packs + if w.withPacks { + packs, err := utils.GetPacks(w.proxy.Server) + if err != nil { + logrus.Error(err) + } else { + var rdeps []dep + for k, p := range packs { + logrus.Infof(locale.Loc("adding_pack", locale.Strmap{"Name": k})) + pack_folder := path.Join(folder, "resource_packs", p.Name()) + os.MkdirAll(pack_folder, 0o755) + data := make([]byte, p.Len()) + p.ReadAt(data, 0) + utils.UnpackZip(bytes.NewReader(data), int64(len(data)), pack_folder) + + rdeps = append(rdeps, dep{ + PackId: p.Manifest().Header.Name, + Version: p.Manifest().Header.Version, + }) + } + add_packs_json("world_resource_packs.json", rdeps) + } } if w.saveImage { @@ -515,7 +428,6 @@ func (w *WorldState) SaveAndReset() { // zip it filename := folder + ".mcworld" - if err := utils.ZipFolder(filename, folder); err != nil { fmt.Println(err) } @@ -531,9 +443,7 @@ func (w *WorldState) OnConnect(proxy *utils.ProxyContext) { world.InsertCustomItems(gd.Items) for _, ie := range gd.Items { - if ie.ComponentBased { - w.bp.AddItem(ie) - } + w.bp.AddItem(ie) } map_item_id, _ := world.ItemRidByName("minecraft:filled_map") @@ -551,12 +461,6 @@ func (w *WorldState) OnConnect(proxy *utils.ProxyContext) { world.InsertCustomBlocks(gd.CustomBlocks) } - if w.withPacks { - go func() { - w.packs, _ = utils.GetPacks(w.proxy.Server) - }() - } - { // check game version gv := strings.Split(gd.BaseGameVersion, ".") var err error @@ -580,26 +484,13 @@ func (w *WorldState) OnConnect(proxy *utils.ProxyContext) { w.proxy.SendMessage(locale.Loc("use_setname", nil)) w.ui.Start() - go func() { // send map item - select { - case <-w.ctx.Done(): - return - default: - t := time.NewTicker(1 * time.Second) - for range t.C { - if w.proxy.Client != nil { - err := w.proxy.Client.WritePacket(&MAP_ITEM_PACKET) - if err != nil { - logrus.Error(err) - return - } - } - } - } - }() proxy.AddCommand(utils.IngameCommand{ - Exec: w.setnameCommand, + Exec: func(cmdline []string) bool { + w.WorldName = strings.Join(cmdline, " ") + w.proxy.SendMessage(locale.Loc("worldname_set", locale.Strmap{"Name": w.WorldName})) + return true + }, Cmd: protocol.Command{ Name: "setname", Description: locale.Loc("setname_desc", nil), @@ -618,179 +509,20 @@ func (w *WorldState) OnConnect(proxy *utils.ProxyContext) { }) proxy.AddCommand(utils.IngameCommand{ - Exec: w.toggleVoid, + Exec: func(cmdline []string) bool { + w.voidgen = !w.voidgen + var s string + if w.voidgen { + s = locale.Loc("void_generator_true", nil) + } else { + s = locale.Loc("void_generator_false", nil) + } + w.proxy.SendMessage(s) + return true + }, Cmd: protocol.Command{ Name: "void", Description: locale.Loc("void_desc", nil), }, }) } - -func (w *WorldState) ProcessPacketClient(pk packet.Packet) (packet.Packet, bool) { - forward := true - 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.SchedRedraw() - forward = false - } - case *packet.ItemStackRequest: - var requests []protocol.ItemStackRequest - for _, isr := range pk.Requests { - for _, sra := range isr.Actions { - if sra, ok := sra.(*protocol.TakeStackRequestAction); ok { - if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { - continue - } - } - if sra, ok := sra.(*protocol.DropStackRequestAction); ok { - if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { - continue - } - } - if sra, ok := sra.(*protocol.DestroyStackRequestAction); ok { - if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { - continue - } - } - if sra, ok := sra.(*protocol.PlaceInContainerStackRequestAction); ok { - if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { - continue - } - } - if sra, ok := sra.(*protocol.TakeOutContainerStackRequestAction); ok { - if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { - continue - } - } - if sra, ok := sra.(*protocol.DestroyStackRequestAction); ok { - if sra.Source.StackNetworkID == MAP_ITEM_PACKET.Content[0].StackNetworkID { - continue - } - } - } - requests = append(requests, isr) - } - pk.Requests = requests - case *packet.MobEquipment: - if pk.NewItem.Stack.NBTData["map_uuid"] == int64(VIEW_MAP_ID) { - forward = false - } - case *packet.Animate: - w.ProcessAnimate(pk) - } - return pk, forward -} - -// stackToItem converts a network ItemStack representation back to an item.Stack. -func stackToItem(it protocol.ItemStack) item.Stack { - t, ok := world.ItemByRuntimeID(it.NetworkID, int16(it.MetadataValue)) - if !ok { - t = block.Air{} - } - if it.BlockRuntimeID > 0 { - // It shouldn't matter if it (for whatever reason) wasn't able to get the block runtime ID, - // since on the next line, we assert that the block is an item. If it didn't succeed, it'll - // return air anyway. - b, _ := world.BlockByRuntimeID(uint32(it.BlockRuntimeID)) - if t, ok = b.(world.Item); !ok { - t = block.Air{} - } - } - //noinspection SpellCheckingInspection - if nbter, ok := t.(world.NBTer); ok && len(it.NBTData) != 0 { - t = nbter.DecodeNBT(it.NBTData).(world.Item) - } - s := item.NewStack(t, int(it.Count)) - return nbtconv.ReadItem(it.NBTData, &s) -} - -func (w *WorldState) ProcessPacketServer(pk packet.Packet) (packet.Packet, bool) { - 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.chunks), "Name": w.WorldName}, len(w.chunks))) - case *packet.SubChunk: - w.ProcessSubChunk(pk) - case *packet.ContainerOpen: - if w.experimentInventory { - // add to open containers - existing, ok := w.openItemContainers[pk.WindowID] - if !ok { - existing = &itemContainer{} - } - w.openItemContainers[pk.WindowID] = &itemContainer{ - OpenPacket: pk, - Content: existing.Content, - } - } - case *packet.InventoryContent: - if w.experimentInventory { - // save content - existing, ok := w.openItemContainers[byte(pk.WindowID)] - if !ok { - if pk.WindowID == 0x0 { // inventory - w.openItemContainers[byte(pk.WindowID)] = &itemContainer{ - Content: pk, - } - } - break - } - existing.Content = pk - } - case *packet.ContainerClose: - if w.experimentInventory { - switch pk.WindowID { - case protocol.WindowIDArmour: // todo handle - case protocol.WindowIDOffHand: // todo handle - case protocol.WindowIDUI: - case protocol.WindowIDInventory: // todo handle - default: - // find container info - existing, ok := w.openItemContainers[byte(pk.WindowID)] - if !ok { - logrus.Warn(locale.Loc("warn_window_closed_not_open", nil)) - break - } - - if existing.Content == nil { - break - } - - pos := existing.OpenPacket.ContainerPosition - cp := protocol.SubChunkPos{pos.X() << 4, pos.Z() << 4} - - // create inventory - inv := inventory.New(len(existing.Content.Content), nil) - for i, c := range existing.Content.Content { - item := stackToItem(c.Stack) - inv.SetItem(i, item) - } - - // put into subchunk - nbts := w.blockNBT[cp] - for i, v := range nbts { - nbt_pos := protocol.BlockPos{v["x"].(int32), v["y"].(int32), v["z"].(int32)} - if nbt_pos == pos { - w.blockNBT[cp][i]["Items"] = nbtconv.InvToNBT(inv) - } - } - - w.proxy.SendMessage(locale.Loc("saved_block_inv", nil)) - - // remove it again - delete(w.openItemContainers, byte(pk.WindowID)) - } - } - case *packet.ItemComponent: - w.bp.ApplyComponentEntries(pk.Items) - } - return pk, true -} diff --git a/utils/behaviourpack/block.go b/utils/behaviourpack/block.go new file mode 100644 index 0000000..42331ab --- /dev/null +++ b/utils/behaviourpack/block.go @@ -0,0 +1,20 @@ +package behaviourpack + +import ( + "github.com/df-mc/dragonfly/server/world" + "github.com/sandertv/gophertunnel/minecraft/protocol" +) + +type blockBehaviour struct { + FormatVersion string `json:"format_version"` + MinecraftBlock world.MinecraftBlock `json:"minecraft:block"` +} + +func (bp *BehaviourPack) AddBlock(block protocol.BlockEntry) { + entry := blockBehaviour{ + FormatVersion: bp.formatVersion, + MinecraftBlock: world.ParseBlock(block), + } + + bp.blocks = append(bp.blocks, entry) +} diff --git a/utils/behaviourpack/bp.go b/utils/behaviourpack/bp.go index 7ddc7cf..72ef9d5 100644 --- a/utils/behaviourpack/bp.go +++ b/utils/behaviourpack/bp.go @@ -2,17 +2,12 @@ package behaviourpack import ( "archive/zip" - "bytes" - "crypto/sha256" "encoding/json" "os" "path" "strings" "github.com/bedrock-tool/bedrocktool/utils" - "github.com/df-mc/dragonfly/server/world" - "github.com/google/uuid" - "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/resource" "github.com/sirupsen/logrus" ) @@ -22,39 +17,7 @@ type BehaviourPack struct { Manifest *resource.Manifest blocks []blockBehaviour items map[string]itemBehaviour -} - -type blockBehaviour struct { - FormatVersion string `json:"format_version"` - MinecraftBlock world.MinecraftBlock `json:"minecraft:block"` -} - -type itemDescription struct { - Category string `json:"category"` - Identifier string `json:"identifier"` - IsExperimental bool `json:"is_experimental"` -} - -type minecraftItem struct { - Description itemDescription `json:"description"` - Components map[string]any `json:"components,omitempty"` -} - -type itemBehaviour struct { - FormatVersion string `json:"format_version"` - MinecraftItem minecraftItem `json:"minecraft:item"` -} - -func check(err error) { - if err != nil { - logrus.Fatal(err) - } -} - -func rand_seeded_uuid(str string) string { - h := sha256.Sum256([]byte(str)) - id, _ := uuid.NewRandomFromReader(bytes.NewBuffer(h[:])) - return id.String() + entities map[string]entityBehaviour } func New(name string) *BehaviourPack { @@ -65,22 +28,23 @@ func New(name string) *BehaviourPack { Header: resource.Header{ Name: "pack.name", Description: "pack.description", - UUID: rand_seeded_uuid(name + "_datapack"), + UUID: utils.Rand_seeded_uuid(name + "_datapack"), Version: [3]int{1, 0, 0}, MinimumGameVersion: [3]int{1, 19, 50}, }, Modules: []resource.Module{ { Type: "data", - UUID: rand_seeded_uuid(name + "_data_module"), + UUID: utils.Rand_seeded_uuid(name + "_data_module"), Version: [3]int{1, 0, 0}, }, }, Dependencies: []resource.Dependency{}, Capabilities: []resource.Capability{}, }, - blocks: []blockBehaviour{}, - items: make(map[string]itemBehaviour), + blocks: []blockBehaviour{}, + items: make(map[string]itemBehaviour), + entities: make(map[string]entityBehaviour), } } @@ -91,48 +55,17 @@ func (bp *BehaviourPack) AddDependency(id string, ver [3]int) { }) } -func (bp *BehaviourPack) AddBlock(block protocol.BlockEntry) { - entry := blockBehaviour{ - FormatVersion: bp.formatVersion, - MinecraftBlock: world.ParseBlock(block), - } - - bp.blocks = append(bp.blocks, entry) -} - -func (bp *BehaviourPack) AddItem(item protocol.ItemEntry) { - entry := itemBehaviour{ - FormatVersion: bp.formatVersion, - MinecraftItem: minecraftItem{ - Description: itemDescription{ - Identifier: item.Name, - IsExperimental: true, - }, - Components: make(map[string]any), - }, - } - bp.items[item.Name] = entry -} - -func (bp *BehaviourPack) ApplyComponentEntries(entries []protocol.ItemComponentEntry) { - for _, ice := range entries { - item, ok := bp.items[ice.Name] - if !ok { - continue - } - item.MinecraftItem.Components = ice.Data - } -} - func (bp *BehaviourPack) CheckAddLink(pack utils.Pack) { z, err := zip.NewReader(pack, int64(pack.Len())) if err != nil { logrus.Error(err) return } - _, err = z.Open("blocks.json") - if err != nil { - return + if len(bp.blocks) > 0 { + _, err = z.Open("blocks.json") + if err != nil { + return + } } h := pack.Manifest().Header bp.AddDependency(h.UUID, h.Version) @@ -142,6 +75,11 @@ func (bp *BehaviourPack) HasContent() bool { return len(bp.blocks) > 0 || len(bp.items) > 0 } +func ns_name_split(identifier string) (ns, name string) { + ns_name := strings.Split(identifier, ":") + return ns_name[0], ns_name[len(ns_name)-1] +} + func (bp *BehaviourPack) Save(fpath string) error { { // write manifest w, err := os.Create(path.Join(fpath, "manifest.json")) @@ -150,43 +88,54 @@ func (bp *BehaviourPack) Save(fpath string) error { } e := json.NewEncoder(w) e.SetIndent("", "\t") - check(e.Encode(bp.Manifest)) + if err = e.Encode(bp.Manifest); err != nil { + return err + } } + + _add_thing := func(base, identifier string, thing any) error { + ns, name := ns_name_split(identifier) + thing_dir := path.Join(base, ns) + os.Mkdir(thing_dir, 0o755) + w, err := os.Create(path.Join(thing_dir, name+".json")) + if err != nil { + return err + } + e := json.NewEncoder(w) + e.SetIndent("", "\t") + return e.Encode(thing) + } + if len(bp.blocks) > 0 { // blocks blocks_dir := path.Join(fpath, "blocks") os.Mkdir(blocks_dir, 0o755) for _, be := range bp.blocks { - ns_name := strings.Split(be.MinecraftBlock.Description.Identifier, ":") - ns := ns_name[0] - name := ns_name[len(ns_name)-1] - block_dir := path.Join(blocks_dir, ns) - os.Mkdir(block_dir, 0o755) - w, err := os.Create(path.Join(block_dir, name+".json")) + err := _add_thing(blocks_dir, be.MinecraftBlock.Description.Identifier, be) if err != nil { return err } - e := json.NewEncoder(w) - e.SetIndent("", "\t") - check(e.Encode(be)) } } if len(bp.items) > 0 { // items items_dir := path.Join(fpath, "items") os.Mkdir(items_dir, 0o755) for _, ib := range bp.items { - ns_name := strings.Split(ib.MinecraftItem.Description.Identifier, ":") - ns := ns_name[0] - name := ns_name[len(ns_name)-1] - item_dir := path.Join(items_dir, ns) - os.Mkdir(item_dir, 0o755) - w, err := os.Create(path.Join(item_dir, name+".json")) + err := _add_thing(items_dir, ib.MinecraftItem.Description.Identifier, ib) if err != nil { return err } - e := json.NewEncoder(w) - e.SetIndent("", "\t") - check(e.Encode(ib)) } } + if len(bp.entities) > 0 { // items + items_dir := path.Join(fpath, "entities") + os.Mkdir(items_dir, 0o755) + for _, eb := range bp.entities { + err := _add_thing(items_dir, eb.MinecraftEntity.Description.Identifier, eb) + if err != nil { + return err + } + } + } + return nil } diff --git a/utils/behaviourpack/entity.go b/utils/behaviourpack/entity.go new file mode 100644 index 0000000..0601f26 --- /dev/null +++ b/utils/behaviourpack/entity.go @@ -0,0 +1,68 @@ +package behaviourpack + +import "github.com/sandertv/gophertunnel/minecraft/protocol" + +type clientEntityDescription struct { + Identifier string `json:"identifier"` + Spawnable bool `json:"is_spawnable"` + Summonable bool `json:"is_summonable"` + Experimental bool `json:"is_experimental"` +} + +type minecraftClientEntity struct { + Description clientEntityDescription `json:"description"` + ComponentGroups map[string]any `json:"component_groups"` + Components map[string]any `json:"components"` + Events map[string]any `json:"events"` +} + +type entityBehaviour struct { + FormatVersion string `json:"format_version"` + MinecraftEntity minecraftClientEntity `json:"minecraft:client_entity"` +} + +type EntityIn struct { + Identifier string + Attr []protocol.AttributeValue +} + +func (bp *BehaviourPack) AddEntity(entity EntityIn) { + ns, _ := ns_name_split(entity.Identifier) + if ns == "minecraft" { + return + } + + if _, ok := bp.entities[entity.Identifier]; ok { + return + } + + entry := entityBehaviour{ + FormatVersion: bp.formatVersion, + MinecraftEntity: minecraftClientEntity{ + Description: clientEntityDescription{ + Identifier: entity.Identifier, + Spawnable: true, + Summonable: true, + Experimental: true, + }, + ComponentGroups: nil, + Components: make(map[string]any), + Events: nil, + }, + } + for _, av := range entity.Attr { + switch av.Name { + case "minecraft:health": + entry.MinecraftEntity.Components["minecraft:health"] = map[string]int{ + "value": int(av.Value), + "max": int(av.Max), + } + case "minecraft:movement": + entry.MinecraftEntity.Components["minecraft:movement"] = map[string]any{ + "value": av.Value, + } + } + } + + bp.entities[entity.Identifier] = entry +} diff --git a/utils/behaviourpack/item.go b/utils/behaviourpack/item.go new file mode 100644 index 0000000..8468a1e --- /dev/null +++ b/utils/behaviourpack/item.go @@ -0,0 +1,48 @@ +package behaviourpack + +import "github.com/sandertv/gophertunnel/minecraft/protocol" + +type itemDescription struct { + Category string `json:"category"` + Identifier string `json:"identifier"` + IsExperimental bool `json:"is_experimental"` +} + +type minecraftItem struct { + Description itemDescription `json:"description"` + Components map[string]any `json:"components,omitempty"` +} + +type itemBehaviour struct { + FormatVersion string `json:"format_version"` + MinecraftItem minecraftItem `json:"minecraft:item"` +} + +func (bp *BehaviourPack) AddItem(item protocol.ItemEntry) { + ns, _ := ns_name_split(item.Name) + if ns == "minecraft" { + return + } + + entry := itemBehaviour{ + FormatVersion: bp.formatVersion, + MinecraftItem: minecraftItem{ + Description: itemDescription{ + Identifier: item.Name, + IsExperimental: true, + }, + Components: make(map[string]any), + }, + } + bp.items[item.Name] = entry +} + +func (bp *BehaviourPack) ApplyComponentEntries(entries []protocol.ItemComponentEntry) { + for _, ice := range entries { + item, ok := bp.items[ice.Name] + if !ok { + continue + } + item.MinecraftItem.Components = ice.Data + } +} diff --git a/utils/crypt/crypt.go b/utils/crypt/crypt.go index fef737a..241af19 100644 --- a/utils/crypt/crypt.go +++ b/utils/crypt/crypt.go @@ -13,22 +13,23 @@ import ( //go:embed key.gpg var key_gpg []byte -var recip *openpgp.Entity +var recipients []*openpgp.Entity func init() { block, err := armor.Decode(bytes.NewBuffer(key_gpg)) if err != nil { panic(err) } - recip, err = openpgp.ReadEntity(packet.NewReader(block.Body)) + recip, err := openpgp.ReadEntity(packet.NewReader(block.Body)) if err != nil { panic(err) } + recipients = append(recipients, recip) } func Enc(name string, data []byte) ([]byte, error) { w := bytes.NewBuffer(nil) - wc, err := openpgp.Encrypt(w, []*openpgp.Entity{recip}, nil, &openpgp.FileHints{ + wc, err := openpgp.Encrypt(w, recipients, nil, &openpgp.FileHints{ IsBinary: true, FileName: name, ModTime: time.Now(), }, nil) if err != nil { @@ -42,7 +43,7 @@ func Enc(name string, data []byte) ([]byte, error) { } func Encer(name string, w io.Writer) (io.WriteCloser, error) { - wc, err := openpgp.Encrypt(w, []*openpgp.Entity{recip}, nil, &openpgp.FileHints{ + wc, err := openpgp.Encrypt(w, recipients, nil, &openpgp.FileHints{ IsBinary: true, FileName: name, ModTime: time.Now(), }, nil) return wc, err diff --git a/utils/images.go b/utils/images.go index e92b52a..b4277c0 100644 --- a/utils/images.go +++ b/utils/images.go @@ -34,3 +34,18 @@ func BlendColors(c1, c2 color.RGBA) (ret color.RGBA) { ret.A = blendAlphaValue(c1.A, c2.A) return ret } + +// Draw_img_scaled_pos draws src onto dst at bottom_left, scaled to size +func Draw_img_scaled_pos(dst *image.RGBA, src *image.RGBA, bottom_left image.Point, size_scaled int) { + sbx := src.Bounds().Dx() + ratio := int(float64(sbx) / float64(size_scaled)) + + for x_out := bottom_left.X; x_out < bottom_left.X+size_scaled; x_out++ { + for y_out := bottom_left.Y; y_out < bottom_left.Y+size_scaled; y_out++ { + x_in := (x_out - bottom_left.X) * ratio + y_in := (y_out - bottom_left.Y) * ratio + c := src.At(x_in, y_in) + dst.Set(x_out, y_out, c) + } + } +} diff --git a/utils/input.go b/utils/input.go index cc7e2e5..1e49118 100644 --- a/utils/input.go +++ b/utils/input.go @@ -55,7 +55,7 @@ func ServerInput(ctx context.Context, server string) (address, name string, err if len(realm_info) == 3 { id = realm_info[2] } - name, address, err = get_realm(context.Background(), GetRealmsApi(), realm_info[1], id) + name, address, err = get_realm(ctx, realm_info[1], id) if err != nil { return "", "", err } diff --git a/utils/realms.go b/utils/realms.go index 8a89cab..652d5e1 100644 --- a/utils/realms.go +++ b/utils/realms.go @@ -8,12 +8,11 @@ import ( "github.com/bedrock-tool/bedrocktool/locale" "github.com/google/subcommands" - "github.com/sandertv/gophertunnel/minecraft/realms" "github.com/sirupsen/logrus" ) -func get_realm(ctx context.Context, api *realms.Client, realm_name, id string) (name string, address string, err error) { - realms, err := api.Realms(ctx) +func get_realm(ctx context.Context, realm_name, id string) (name string, address string, err error) { + realms, err := GetRealmsApi().Realms(ctx) if err != nil { return "", "", err } @@ -44,8 +43,7 @@ func (c *RealmListCMD) Usage() string { } func (c *RealmListCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus { - api := realms.NewClient(GetTokenSource()) - realms, err := api.Realms(ctx) + realms, err := GetRealmsApi().Realms(ctx) if err != nil { logrus.Error(err) return 1 diff --git a/utils/replay.go b/utils/replay.go index 8619064..1f64813 100644 --- a/utils/replay.go +++ b/utils/replay.go @@ -5,6 +5,7 @@ import ( "context" "encoding/binary" "io" + "net" "os" "github.com/sandertv/gophertunnel/minecraft" @@ -84,7 +85,9 @@ func create_replay_connection(ctx context.Context, filename string, onConnect Co b := protocol.NewWriter(f, 0) pk.Marshal(b) - // PacketLogger(packet.Header{PacketID: pk.ID()}, f.Bytes(), &net.UDPAddr{}, &net.UDPAddr{}) + if G_debug { + PacketLogger(packet.Header{PacketID: pk.ID()}, f.Bytes(), &net.UDPAddr{}, &net.UDPAddr{}) + } if game_started { if packetCB != nil { diff --git a/utils/resourcepack-ace.go b/utils/resourcepack-ace.go index 790e6ef..8ac7749 100644 Binary files a/utils/resourcepack-ace.go and b/utils/resourcepack-ace.go differ diff --git a/utils/resourcepack.go b/utils/resourcepack.go index bae1b06..e1bb7d3 100644 --- a/utils/resourcepack.go +++ b/utils/resourcepack.go @@ -13,6 +13,7 @@ type Pack interface { ReadAll() ([]byte, error) Decrypt() ([]byte, error) Encrypted() bool + CanDecrypt() bool UUID() string Name() string Version() string @@ -42,6 +43,10 @@ func (p *Packb) ReadAll() ([]byte, error) { return buf, nil } +func (p *Packb) CanDecrypt() bool { + return false +} + func (p *Packb) Decrypt() ([]byte, error) { return nil, errors.New("no_decrypt") } @@ -59,7 +64,7 @@ func GetPacks(server *minecraft.Conn) (packs map[string]*resource.Pack, err erro packs = make(map[string]*resource.Pack) for _, pack := range server.ResourcePacks() { pack := PackFromBase(pack) - if pack.Encrypted() { + if pack.Encrypted() && pack.CanDecrypt() { data, err := pack.Decrypt() if err != nil { return nil, err diff --git a/utils/utils.go b/utils/utils.go index c70b234..648c6ea 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,7 +1,9 @@ package utils import ( + "bytes" "context" + "crypto/sha256" "encoding/json" "errors" "net" @@ -152,3 +154,10 @@ func Clamp(a, b int) int { } return a } + +func Rand_seeded_uuid(str string) string { + h := sha256.Sum256([]byte(str)) + id, _ := uuid.NewRandomFromReader(bytes.NewBuffer(h[:])) + return id.String() +} +