add custom items, (untested probably not working entities)
This commit is contained in:
parent
ea6dea7953
commit
32f1e8019a
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue