mirror of
https://github.com/CosmicStar98/bedrocktool.git
synced 2024-06-16 12:59:46 +00:00
799 lines
20 KiB
Go
799 lines
20 KiB
Go
package world
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"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"
|
|
"github.com/df-mc/goleveldb/leveldb/opt"
|
|
"github.com/go-gl/mathgl/mgl32"
|
|
"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 {
|
|
Position mgl32.Vec3
|
|
Pitch float32
|
|
Yaw float32
|
|
HeadYaw float32
|
|
}
|
|
|
|
type itemContainer struct {
|
|
OpenPacket *packet.ContainerOpen
|
|
Content *packet.InventoryContent
|
|
}
|
|
|
|
// the state used for drawing and saving
|
|
|
|
type WorldState struct {
|
|
ctx context.Context
|
|
ispre118 bool
|
|
voidgen bool
|
|
chunks map[protocol.ChunkPos]*chunk.Chunk
|
|
blockNBT map[protocol.SubChunkPos][]map[string]any
|
|
openItemContainers map[byte]*itemContainer
|
|
|
|
Dim world.Dimension
|
|
WorldName string
|
|
ServerName string
|
|
worldCounter int
|
|
packs map[string]*resource.Pack
|
|
bp *behaviourpack.BehaviourPack
|
|
|
|
withPacks bool
|
|
saveImage bool
|
|
experimentInventory bool
|
|
|
|
PlayerPos TPlayerPos
|
|
proxy *utils.ProxyContext
|
|
|
|
// ui
|
|
ui *MapUI
|
|
}
|
|
|
|
func NewWorldState() *WorldState {
|
|
w := &WorldState{
|
|
chunks: make(map[protocol.ChunkPos]*chunk.Chunk),
|
|
blockNBT: make(map[protocol.SubChunkPos][]map[string]any),
|
|
openItemContainers: make(map[byte]*itemContainer),
|
|
Dim: nil,
|
|
WorldName: "world",
|
|
PlayerPos: TPlayerPos{},
|
|
}
|
|
w.ui = NewMapUI(w)
|
|
return w
|
|
}
|
|
|
|
var dimension_ids = map[uint8]world.Dimension{
|
|
0: world.Overworld,
|
|
1: world.Nether,
|
|
2: world.End,
|
|
// < 1.18
|
|
10: world.Overworld_legacy,
|
|
11: world.Nether,
|
|
12: world.End,
|
|
}
|
|
|
|
var (
|
|
black_16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16))
|
|
Offset_table [24]protocol.SubChunkOffset
|
|
)
|
|
|
|
func init() {
|
|
for i := range Offset_table {
|
|
Offset_table[i] = protocol.SubChunkOffset{0, int8(i), 0}
|
|
}
|
|
draw.Draw(black_16x16, image.Rect(0, 0, 16, 16), image.Black, image.Point{}, draw.Src)
|
|
utils.RegisterCommand(&WorldCMD{})
|
|
}
|
|
|
|
type WorldCMD struct {
|
|
Address string
|
|
packs bool
|
|
enableVoid bool
|
|
saveImage bool
|
|
experimentInventory bool
|
|
}
|
|
|
|
func (*WorldCMD) Name() string { return "worlds" }
|
|
func (*WorldCMD) Synopsis() string { return locale.Loc("world_synopsis", nil) }
|
|
|
|
func (p *WorldCMD) SetFlags(f *flag.FlagSet) {
|
|
f.StringVar(&p.Address, "address", "", locale.Loc("remote_address", nil))
|
|
f.BoolVar(&p.packs, "packs", false, locale.Loc("save_packs_with_world", nil))
|
|
f.BoolVar(&p.enableVoid, "void", true, locale.Loc("enable_void", nil))
|
|
f.BoolVar(&p.saveImage, "image", false, locale.Loc("save_image", nil))
|
|
f.BoolVar(&p.experimentInventory, "inv", false, locale.Loc("test_block_inv", nil))
|
|
}
|
|
|
|
func (c *WorldCMD) Usage() string {
|
|
return c.Name() + ": " + c.Synopsis() + "\n" + locale.Loc("server_address_help", nil)
|
|
}
|
|
|
|
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)
|
|
return 1
|
|
}
|
|
|
|
w := NewWorldState()
|
|
w.voidgen = c.enableVoid
|
|
w.ServerName = hostname
|
|
w.withPacks = c.packs
|
|
w.saveImage = c.saveImage
|
|
w.experimentInventory = c.experimentInventory
|
|
w.ctx = ctx
|
|
|
|
proxy := utils.NewProxy()
|
|
proxy.AlwaysGetPacks = true
|
|
proxy.ConnectCB = w.OnConnect
|
|
proxy.PacketCB = func(pk packet.Packet, proxy *utils.ProxyContext, toServer bool) (packet.Packet, error) {
|
|
var forward bool
|
|
if toServer {
|
|
pk, forward = w.ProcessPacketClient(pk)
|
|
} else {
|
|
pk, forward = w.ProcessPacketServer(pk)
|
|
}
|
|
if !forward {
|
|
return nil, nil
|
|
}
|
|
return pk, nil
|
|
}
|
|
|
|
super_verbose_log := false
|
|
if super_verbose_log {
|
|
utils.F_Log, err = os.Create("packets.log")
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
}
|
|
|
|
err = proxy.Run(ctx, server_address)
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
} else {
|
|
w.SaveAndReset()
|
|
}
|
|
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)
|
|
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()
|
|
w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.ui.zoomLevel}))
|
|
}
|
|
}
|
|
|
|
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{
|
|
Position: Position,
|
|
Pitch: Pitch,
|
|
Yaw: Yaw,
|
|
HeadYaw: HeadYaw,
|
|
}
|
|
|
|
if int(last.Position.X()) != int(w.PlayerPos.Position.X()) || int(last.Position.Z()) != int(w.PlayerPos.Position.Z()) {
|
|
w.ui.SchedRedraw()
|
|
}
|
|
}
|
|
|
|
func (w *WorldState) Reset() {
|
|
w.chunks = make(map[protocol.ChunkPos]*chunk.Chunk)
|
|
w.WorldName = fmt.Sprintf("world-%d", w.worldCounter)
|
|
w.ui.Reset()
|
|
}
|
|
|
|
// writes the world to a folder, resets all the chunks
|
|
func (w *WorldState) SaveAndReset() {
|
|
if len(w.chunks) == 0 {
|
|
w.Reset()
|
|
return
|
|
}
|
|
logrus.Infof(locale.Loc("saving_world", locale.Strmap{"Name": w.WorldName, "Count": len(w.chunks)}))
|
|
|
|
// open world
|
|
folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.ServerName, w.WorldName))
|
|
os.RemoveAll(folder)
|
|
os.MkdirAll(folder, 0o777)
|
|
|
|
provider, err := mcdb.New(logrus.StandardLogger(), folder, opt.DefaultCompression)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
// save chunk data
|
|
for cp, c := range w.chunks {
|
|
provider.SaveChunk((world.ChunkPos)(cp), c, w.Dim)
|
|
}
|
|
|
|
// save block nbt data
|
|
blockNBT := make(map[protocol.ChunkPos][]map[string]any)
|
|
for scp, v := range w.blockNBT { // 3d to 2d
|
|
cp := protocol.ChunkPos{scp.X(), scp.Z()}
|
|
blockNBT[cp] = append(blockNBT[cp], v...)
|
|
}
|
|
for cp, v := range blockNBT {
|
|
err = provider.SaveBlockNBT((world.ChunkPos)(cp), v, w.Dim)
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
}
|
|
|
|
// write metadata
|
|
s := provider.Settings()
|
|
player := w.proxy.Server.GameData().PlayerPosition
|
|
s.Spawn = cube.Pos{
|
|
int(player.X()),
|
|
int(player.Y()),
|
|
int(player.Z()),
|
|
}
|
|
s.Name = w.WorldName
|
|
|
|
// set gamerules
|
|
ld := provider.LevelDat()
|
|
gd := w.proxy.Server.GameData()
|
|
for _, gr := range gd.GameRules {
|
|
switch gr.Name {
|
|
case "commandblockoutput":
|
|
ld.CommandBlockOutput = gr.Value.(bool)
|
|
case "maxcommandchainlength":
|
|
ld.MaxCommandChainLength = int32(gr.Value.(uint32))
|
|
case "commandblocksenabled":
|
|
ld.CommandsEnabled = gr.Value.(bool)
|
|
case "dodaylightcycle":
|
|
ld.DoDayLightCycle = gr.Value.(bool)
|
|
case "doentitydrops":
|
|
ld.DoEntityDrops = gr.Value.(bool)
|
|
case "dofiretick":
|
|
ld.DoFireTick = gr.Value.(bool)
|
|
case "domobloot":
|
|
ld.DoMobLoot = gr.Value.(bool)
|
|
case "domobspawning":
|
|
ld.DoMobSpawning = gr.Value.(bool)
|
|
case "dotiledrops":
|
|
ld.DoTileDrops = gr.Value.(bool)
|
|
case "doweathercycle":
|
|
ld.DoWeatherCycle = gr.Value.(bool)
|
|
case "drowningdamage":
|
|
ld.DrowningDamage = gr.Value.(bool)
|
|
case "doinsomnia":
|
|
ld.DoInsomnia = gr.Value.(bool)
|
|
case "falldamage":
|
|
ld.FallDamage = gr.Value.(bool)
|
|
case "firedamage":
|
|
ld.FireDamage = gr.Value.(bool)
|
|
case "keepinventory":
|
|
ld.KeepInventory = gr.Value.(bool)
|
|
case "mobgriefing":
|
|
ld.MobGriefing = gr.Value.(bool)
|
|
case "pvp":
|
|
ld.PVP = gr.Value.(bool)
|
|
case "showcoordinates":
|
|
ld.ShowCoordinates = gr.Value.(bool)
|
|
case "naturalregeneration":
|
|
ld.NaturalRegeneration = gr.Value.(bool)
|
|
case "tntexplodes":
|
|
ld.TNTExplodes = gr.Value.(bool)
|
|
case "sendcommandfeedback":
|
|
ld.SendCommandFeedback = gr.Value.(bool)
|
|
case "randomtickspeed":
|
|
ld.RandomTickSpeed = int32(gr.Value.(uint32))
|
|
case "doimmediaterespawn":
|
|
ld.DoImmediateRespawn = gr.Value.(bool)
|
|
case "showdeathmessages":
|
|
ld.ShowDeathMessages = gr.Value.(bool)
|
|
case "functioncommandlimit":
|
|
ld.FunctionCommandLimit = int32(gr.Value.(uint32))
|
|
case "spawnradius":
|
|
ld.SpawnRadius = int32(gr.Value.(uint32))
|
|
case "showtags":
|
|
ld.ShowTags = gr.Value.(bool)
|
|
case "freezedamage":
|
|
ld.FreezeDamage = gr.Value.(bool)
|
|
case "respawnblocksexplode":
|
|
ld.RespawnBlocksExplode = gr.Value.(bool)
|
|
case "showbordereffect":
|
|
ld.ShowBorderEffect = gr.Value.(bool)
|
|
// todo
|
|
default:
|
|
logrus.Warnf(locale.Loc("unknown_gamerule", locale.Strmap{"Name": gr.Name}))
|
|
}
|
|
}
|
|
|
|
ld.RandomSeed = int64(gd.WorldSeed)
|
|
|
|
// void world
|
|
if w.voidgen {
|
|
ld.FlatWorldLayers = `{"biome_id":1,"block_layers":[{"block_data":0,"block_id":0,"count":1},{"block_data":0,"block_id":0,"count":2},{"block_data":0,"block_id":0,"count":1}],"encoding_version":3,"structure_options":null}`
|
|
ld.Generator = 2
|
|
}
|
|
|
|
if w.bp != nil {
|
|
if ld.Experiments == nil {
|
|
ld.Experiments = map[string]any{}
|
|
}
|
|
ld.Experiments["data_driven_items"] = 1
|
|
ld.Experiments["experiments_ever_used"] = 1
|
|
ld.Experiments["saved_with_toggled_experiments"] = 1
|
|
}
|
|
|
|
provider.SaveSettings(s)
|
|
provider.Close()
|
|
w.worldCounter += 1
|
|
|
|
type dep struct {
|
|
PackId string `json:"pack_id"`
|
|
Version [3]int `json:"version"`
|
|
}
|
|
add_packs_json := func(name string, deps []dep) {
|
|
f, err := os.Create(path.Join(folder, name))
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
return
|
|
}
|
|
defer f.Close()
|
|
if err := json.NewEncoder(f).Encode(deps); err != nil {
|
|
logrus.Error(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
{
|
|
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)
|
|
}
|
|
|
|
if w.bp != nil {
|
|
name := strings.ReplaceAll(w.ServerName, "/", "-") + "_blocks"
|
|
pack_folder := path.Join(folder, "behavior_packs", name)
|
|
os.MkdirAll(pack_folder, 0o755)
|
|
|
|
for _, p := range w.proxy.Server.ResourcePacks() {
|
|
p := utils.PackFromBase(p)
|
|
w.bp.CheckAddLink(p)
|
|
}
|
|
|
|
w.bp.Save(pack_folder)
|
|
add_packs_json("world_behavior_packs.json", []dep{{
|
|
PackId: w.bp.Manifest.Header.UUID,
|
|
Version: w.bp.Manifest.Header.Version,
|
|
}})
|
|
}
|
|
|
|
if w.saveImage {
|
|
f, _ := os.Create(folder + ".png")
|
|
png.Encode(f, w.ui.ToImage())
|
|
f.Close()
|
|
}
|
|
|
|
// zip it
|
|
filename := folder + ".mcworld"
|
|
|
|
if err := utils.ZipFolder(filename, folder); err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
logrus.Info(locale.Loc("saved", locale.Strmap{"Name": filename}))
|
|
os.RemoveAll(folder)
|
|
w.Reset()
|
|
}
|
|
|
|
func (w *WorldState) OnConnect(proxy *utils.ProxyContext) {
|
|
w.proxy = proxy
|
|
gd := w.proxy.Server.GameData()
|
|
|
|
world.InsertCustomItems(gd.Items)
|
|
|
|
map_item_id, _ := world.ItemRidByName("minecraft:filled_map")
|
|
MAP_ITEM_PACKET.Content[0].Stack.ItemType.NetworkID = map_item_id
|
|
if gd.ServerAuthoritativeInventory {
|
|
MAP_ITEM_PACKET.Content[0].StackNetworkID = rand.Int31n(32)
|
|
}
|
|
|
|
if len(gd.CustomBlocks) > 0 {
|
|
logrus.Info(locale.Loc("using_customblocks", nil))
|
|
|
|
w.bp = behaviourpack.New(w.ServerName + " Custom Blocks")
|
|
for _, be := range gd.CustomBlocks {
|
|
w.bp.AddBlock(be)
|
|
}
|
|
|
|
// telling the chunk code what custom blocks there are so it can generate offsets
|
|
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
|
|
if len(gv) > 1 {
|
|
var ver int
|
|
ver, err = strconv.Atoi(gv[1])
|
|
w.ispre118 = ver < 18
|
|
}
|
|
if err != nil || len(gv) <= 1 {
|
|
logrus.Info(locale.Loc("guessing_version", nil))
|
|
}
|
|
|
|
dim_id := gd.Dimension
|
|
if w.ispre118 {
|
|
logrus.Info(locale.Loc("using_under_118", nil))
|
|
dim_id += 10
|
|
}
|
|
w.Dim = dimension_ids[uint8(dim_id)]
|
|
}
|
|
|
|
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,
|
|
Cmd: protocol.Command{
|
|
Name: "setname",
|
|
Description: locale.Loc("setname_desc", nil),
|
|
Overloads: []protocol.CommandOverload{
|
|
{
|
|
Parameters: []protocol.CommandParameter{
|
|
{
|
|
Name: "name",
|
|
Type: protocol.CommandArgTypeString,
|
|
Optional: false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
proxy.AddCommand(utils.IngameCommand{
|
|
Exec: w.toggleVoid,
|
|
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))
|
|
}
|
|
}
|
|
}
|
|
return pk, true
|
|
}
|