make worlds a proper handler

This commit is contained in:
olebeck 2023-04-04 02:42:17 +02:00
parent f1cb7df05e
commit 4e7cfeb3b4
19 changed files with 632 additions and 362 deletions

6
go.sum
View File

@ -10,6 +10,7 @@ git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0 h1:bGG/g4ypjrCJoSvFrP5hafr
git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo= git.wow.st/gmp/jni v0.0.0-20210610011705-34026c7e22d0/go.mod h1:+axXBRUTIDlCeE73IKeD/os7LoEnTKdkp8/gQOFjqyo=
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/TeaEntityLab/fpGo/v2 v2.3.2 h1:qlGjFzXukp9IgVQl32cz4PosbiFEnwXa2EpDbwddHS8= github.com/TeaEntityLab/fpGo/v2 v2.3.2 h1:qlGjFzXukp9IgVQl32cz4PosbiFEnwXa2EpDbwddHS8=
github.com/TeaEntityLab/fpGo/v2 v2.3.2/go.mod h1:b06fRNLSnNiUwfSskBC3f3cocA4mAC2iySdtaP/T9uA= github.com/TeaEntityLab/fpGo/v2 v2.3.2/go.mod h1:b06fRNLSnNiUwfSskBC3f3cocA4mAC2iySdtaP/T9uA=
github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE= github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE=
@ -19,6 +20,8 @@ github.com/benoitkugler/textlayout-testdata v0.1.1 h1:AvFxBxpfrQd8v55qH59mZOJOQj
github.com/benoitkugler/textlayout-testdata v0.1.1/go.mod h1:i/qZl09BbUOtd7Bu/W1CAubRwTWrEXWq6JwMkw8wYxo= github.com/benoitkugler/textlayout-testdata v0.1.1/go.mod h1:i/qZl09BbUOtd7Bu/W1CAubRwTWrEXWq6JwMkw8wYxo=
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 h1:/G0ghZwrhou0Wq21qc1vXXMm/t/aKWkALWwITptKbE0= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9 h1:/G0ghZwrhou0Wq21qc1vXXMm/t/aKWkALWwITptKbE0=
github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9/go.mod h1:TOk10ahXejq9wkEaym3KPRNeuR/h5Jx+s8QRWIa2oTM= github.com/brentp/intintmap v0.0.0-20190211203843-30dc0ade9af9/go.mod h1:TOk10ahXejq9wkEaym3KPRNeuR/h5Jx+s8QRWIa2oTM=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/changkun/lockfree v0.0.1 h1:5WefVJLglY4IHRqOQmh6Ao6wkJYaJkarshKU8VUtId4= github.com/changkun/lockfree v0.0.1 h1:5WefVJLglY4IHRqOQmh6Ao6wkJYaJkarshKU8VUtId4=
github.com/changkun/lockfree v0.0.1/go.mod h1:3bKiaXn/iNzIPlSvSOMSVbRQUQtAp8qUAyBUtzU11s4= github.com/changkun/lockfree v0.0.1/go.mod h1:3bKiaXn/iNzIPlSvSOMSVbRQUQtAp8qUAyBUtzU11s4=
github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:Yg2hDs4b13Evkpj42FU2idX2cVXVFqQSheXYKM86Qsk= github.com/cloudfoundry-attic/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:Yg2hDs4b13Evkpj42FU2idX2cVXVFqQSheXYKM86Qsk=
@ -102,6 +105,8 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/repeale/fp-go v0.11.1 h1:Q/e+gNyyHaxKAyfdbBqvip3DxhVWH453R+kthvSr9Mk= github.com/repeale/fp-go v0.11.1 h1:Q/e+gNyyHaxKAyfdbBqvip3DxhVWH453R+kthvSr9Mk=
github.com/repeale/fp-go v0.11.1/go.mod h1:4KrwQJB1VRY+06CA+jTc4baZetr6o2PeuqnKr5ybQUc= github.com/repeale/fp-go v0.11.1/go.mod h1:4KrwQJB1VRY+06CA+jTc4baZetr6o2PeuqnKr5ybQUc=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49 h1:LuxslTBxJrrNeKfqoywIERWWhH43TgiVAiPEVlhgNBA= github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49 h1:LuxslTBxJrrNeKfqoywIERWWhH43TgiVAiPEVlhgNBA=
github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49/go.mod h1:fY313ZGG810aWruFYcyq3coFpHDrWJVoMfSRI81y1r4= github.com/sanbornm/go-selfupdate v0.0.0-20210106163404-c9b625feac49/go.mod h1:fY313ZGG810aWruFYcyq3coFpHDrWJVoMfSRI81y1r4=
github.com/sandertv/go-raknet v1.12.0 h1:olUzZlIJyX/pgj/mrsLCZYjKLNDsYiWdvQ4NIm3z0DA= github.com/sandertv/go-raknet v1.12.0 h1:olUzZlIJyX/pgj/mrsLCZYjKLNDsYiWdvQ4NIm3z0DA=
@ -110,6 +115,7 @@ github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQ
github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M= github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

View File

@ -10,7 +10,7 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
func (w *worldsServer) processChangeDimension(pk *packet.ChangeDimension) { func (w *worldsHandler) processChangeDimension(pk *packet.ChangeDimension) {
if len(w.worldState.chunks) > 0 { if len(w.worldState.chunks) > 0 {
w.SaveAndReset() w.SaveAndReset()
} else { } else {
@ -18,13 +18,14 @@ func (w *worldsServer) processChangeDimension(pk *packet.ChangeDimension) {
w.Reset(w.CurrentName()) w.Reset(w.CurrentName())
} }
dimensionID := pk.Dimension dimensionID := pk.Dimension
if w.serverState.ispre118 { if w.serverState.ispre118 && dimensionID == 0 {
dimensionID += 10 dimensionID += 10
} }
w.worldState.Dim = dimensionIDMap[uint8(dimensionID)] d, _ := world.DimensionByID(int(dimensionID))
w.worldState.dimension = d
} }
func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) { func (w *worldsHandler) processLevelChunk(pk *packet.LevelChunk) {
// ignore empty chunks THANKS WEIRD SERVER SOFTWARE DEVS // ignore empty chunks THANKS WEIRD SERVER SOFTWARE DEVS
if len(pk.RawPayload) == 0 { if len(pk.RawPayload) == 0 {
logrus.Info(locale.Loc("empty_chunk", nil)) logrus.Info(locale.Loc("empty_chunk", nil))
@ -41,7 +42,7 @@ func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) {
subChunkCount = int(pk.SubChunkCount) subChunkCount = int(pk.SubChunkCount)
} }
ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, w.worldState.Dim.Range(), w.serverState.ispre118, w.bp.HasBlocks()) ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, w.worldState.dimension.Range(), w.serverState.ispre118, w.bp.HasBlocks())
if err != nil { if err != nil {
logrus.Error(err) logrus.Error(err)
return return
@ -53,21 +54,19 @@ func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) {
} }
w.worldState.chunks[pk.Position] = ch w.worldState.chunks[pk.Position] = ch
max := w.worldState.Dim.Range().Height() / 16 max := w.worldState.dimension.Range().Height() / 16
switch pk.SubChunkCount { switch pk.SubChunkCount {
case protocol.SubChunkRequestModeLimited: case protocol.SubChunkRequestModeLimited:
max = int(pk.HighestSubChunk) max = int(pk.HighestSubChunk)
fallthrough fallthrough
case protocol.SubChunkRequestModeLimitless: case protocol.SubChunkRequestModeLimitless:
var offsetTable []protocol.SubChunkOffset var offsetTable []protocol.SubChunkOffset
r := w.worldState.Dim.Range() r := w.worldState.dimension.Range()
for y := int8(r.Min() / 16); y < int8(r.Max()); y++ { for y := int8(r.Min() / 16); y < int8(r.Max()); y++ {
offsetTable = append(offsetTable, protocol.SubChunkOffset{0, y, 0}) offsetTable = append(offsetTable, protocol.SubChunkOffset{0, y, 0})
} }
dimId, ok := world.DimensionID(w.worldState.Dim) dimId, _ := world.DimensionID(w.worldState.dimension)
_ = ok
w.proxy.Server.WritePacket(&packet.SubChunkRequest{ w.proxy.Server.WritePacket(&packet.SubChunkRequest{
Dimension: int32(dimId), Dimension: int32(dimId),
Position: protocol.SubChunkPos{ Position: protocol.SubChunkPos{
@ -86,7 +85,7 @@ func (w *worldsServer) processLevelChunk(pk *packet.LevelChunk) {
} }
} }
func (w *worldsServer) processSubChunk(pk *packet.SubChunk) { func (w *worldsHandler) processSubChunk(pk *packet.SubChunk) {
posToRedraw := make(map[protocol.ChunkPos]bool) posToRedraw := make(map[protocol.ChunkPos]bool)
for _, sub := range pk.SubChunkEntries { for _, sub := range pk.SubChunkEntries {
@ -124,14 +123,16 @@ func blockPosInChunk(pos protocol.BlockPos) (uint8, int16, uint8) {
return uint8(pos.X() & 0x0f), int16(pos.Y() & 0x0f), uint8(pos.Z() & 0x0f) return uint8(pos.X() & 0x0f), int16(pos.Y() & 0x0f), uint8(pos.Z() & 0x0f)
} }
func (w *worldsServer) ProcessChunkPackets(pk packet.Packet) packet.Packet { func (w *worldsHandler) ProcessChunkPackets(pk packet.Packet) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.ChangeDimension: case *packet.ChangeDimension:
w.processChangeDimension(pk) w.processChangeDimension(pk)
case *packet.LevelChunk: case *packet.LevelChunk:
w.processLevelChunk(pk) w.processLevelChunk(pk)
w.proxy.SendPopup(locale.Locm("popup_chunk_count", locale.Strmap{
w.proxy.SendPopup(locale.Locm("popup_chunk_count", locale.Strmap{"Count": len(w.worldState.chunks), "Name": w.worldState.Name}, len(w.worldState.chunks))) "Count": len(w.worldState.chunks),
"Name": w.worldState.Name,
}, len(w.worldState.chunks)))
case *packet.SubChunk: case *packet.SubChunk:
w.processSubChunk(pk) w.processSubChunk(pk)
case *packet.BlockActorData: case *packet.BlockActorData:

View File

@ -61,7 +61,7 @@ func (e serverEntity) Type() world.EntityType {
return e.EntityType return e.EntityType
} }
func (w *worldsServer) processAddActor(pk *packet.AddActor) { func (w *worldsHandler) processAddActor(pk *packet.AddActor) {
e, ok := w.worldState.entities[pk.EntityRuntimeID] e, ok := w.worldState.entities[pk.EntityRuntimeID]
if !ok { if !ok {
e = &entityState{ e = &entityState{
@ -141,47 +141,49 @@ func entityMetadataToNBT(metadata protocol.EntityMetadata, nbt map[string]any) {
} }
} }
if metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagNoAI) { if _, ok := metadata[protocol.EntityDataKeyFlags]; ok {
nbt["IsAutonomous"] = false if metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagNoAI) {
} nbt["IsAutonomous"] = false
for k, v := range flagNames { }
nbt[v] = metadata.Flag(protocol.EntityDataKeyFlags, k) for k, v := range flagNames {
} nbt[v] = metadata.Flag(protocol.EntityDataKeyFlags, k)
}
AlwaysShowName := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagAlwaysShowName) AlwaysShowName := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagAlwaysShowName)
if AlwaysShowName { if AlwaysShowName {
nbt["CustomNameVisible"] = true nbt["CustomNameVisible"] = true
} }
type effect struct { type effect struct {
Id byte Id byte
Amplifier byte Amplifier byte
Duration int32 Duration int32
DurationEasy int32 DurationEasy int32
DurationNormal int32 DurationNormal int32
DurationHard int32 DurationHard int32
Ambient bool Ambient bool
ShowParticles bool ShowParticles bool
DisplayOnScreenTextureAnimation bool DisplayOnScreenTextureAnimation bool
} }
activeEffects := []effect{} activeEffects := []effect{}
addEffect := func(id int, showParticles bool) { addEffect := func(id int, showParticles bool) {
activeEffects = append(activeEffects, effect{ activeEffects = append(activeEffects, effect{
Id: byte(id), Id: byte(id),
Amplifier: 1, Amplifier: 1,
Duration: 10000000, Duration: 10000000,
ShowParticles: false, ShowParticles: false,
}) })
} }
invisible := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible) invisible := metadata.Flag(protocol.EntityDataKeyFlags, protocol.EntityDataFlagInvisible)
if invisible { if invisible {
addEffect(packet.EffectInvisibility, false) addEffect(packet.EffectInvisibility, false)
} }
if len(activeEffects) > 0 { if len(activeEffects) > 0 {
nbt["ActiveEffects"] = activeEffects nbt["ActiveEffects"] = activeEffects
}
} }
} }
@ -214,7 +216,7 @@ func (s *entityState) ToServerEntity() serverEntity {
return e return e
} }
func (w *worldsServer) ProcessEntityPackets(pk packet.Packet) packet.Packet { func (w *worldsHandler) ProcessEntityPackets(pk packet.Packet) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.AddActor: case *packet.AddActor:
w.processAddActor(pk) w.processAddActor(pk)

View File

@ -17,7 +17,7 @@ type itemContainer struct {
Content *packet.InventoryContent Content *packet.InventoryContent
} }
func (w *worldsServer) processItemPacketsServer(pk packet.Packet) packet.Packet { func (w *worldsHandler) processItemPacketsServer(pk packet.Packet) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.ContainerOpen: case *packet.ContainerOpen:
// add to open containers // add to open containers
@ -116,7 +116,7 @@ func (w *worldsServer) processItemPacketsServer(pk packet.Packet) packet.Packet
return pk return pk
} }
func (w *worldsServer) processItemPacketsClient(pk packet.Packet, forward *bool) packet.Packet { func (w *worldsHandler) processItemPacketsClient(pk packet.Packet, forward *bool) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.ItemStackRequest: case *packet.ItemStackRequest:
var requests []protocol.ItemStackRequest var requests []protocol.ItemStackRequest
@ -187,7 +187,7 @@ func stackToItem(it protocol.ItemStack) item.Stack {
return nbtconv.ReadItem(it.NBTData, &s) return nbtconv.ReadItem(it.NBTData, &s)
} }
func (w *worldsServer) playerData() (ret map[string]any) { func (w *worldsHandler) playerData() (ret map[string]any) {
ret = map[string]any{ ret = map[string]any{
"format_version": "1.12.0", "format_version": "1.12.0",
"identifier": "minecraft:player", "identifier": "minecraft:player",

View File

@ -10,6 +10,7 @@ import (
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/ui/messages" "github.com/bedrock-tool/bedrocktool/ui/messages"
"github.com/bedrock-tool/bedrocktool/utils" "github.com/bedrock-tool/bedrocktool/utils"
"github.com/go-gl/mathgl/mgl32"
"golang.design/x/lockfree" "golang.design/x/lockfree"
"github.com/df-mc/dragonfly/server/world/chunk" "github.com/df-mc/dragonfly/server/world/chunk"
@ -79,10 +80,10 @@ type MapUI struct {
l sync.RWMutex l sync.RWMutex
ticker *time.Ticker ticker *time.Ticker
w *worldsServer w *worldsHandler
} }
func NewMapUI(w *worldsServer) *MapUI { func NewMapUI(w *worldsHandler) *MapUI {
m := &MapUI{ m := &MapUI{
img: image.NewRGBA(image.Rect(0, 0, 128, 128)), img: image.NewRGBA(image.Rect(0, 0, 128, 128)),
zoomLevel: 16, zoomLevel: 16,
@ -265,14 +266,29 @@ func (m *MapUI) SetChunk(pos protocol.ChunkPos, ch *chunk.Chunk, complete bool)
m.SchedRedraw() m.SchedRedraw()
} }
func (w *worldsServer) ProcessAnimate(pk *packet.Animate) { func (w *worldsHandler) ProcessAnimate(pk *packet.Animate) {
if pk.ActionType == packet.AnimateActionSwingArm { if pk.ActionType == packet.AnimateActionSwingArm {
w.mapUI.ChangeZoom() w.mapUI.ChangeZoom()
w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.mapUI.zoomLevel})) w.proxy.SendPopup(locale.Loc("zoom_level", locale.Strmap{"Level": w.mapUI.zoomLevel}))
} }
} }
func (w *worldsServer) processMapPacketsClient(pk packet.Packet, forward *bool) packet.Packet { func (w *worldsHandler) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) {
last := w.serverState.PlayerPos
current := TPlayerPos{
Position: Position,
Pitch: Pitch,
Yaw: Yaw,
HeadYaw: HeadYaw,
}
w.serverState.PlayerPos = current
if int(last.Position.X()) != int(current.Position.X()) || int(last.Position.Z()) != int(current.Position.Z()) {
w.mapUI.SchedRedraw()
}
}
func (w *worldsHandler) processMapPacketsClient(pk packet.Packet, forward *bool) packet.Packet {
switch pk := pk.(type) { switch pk := pk.(type) {
case *packet.MovePlayer: case *packet.MovePlayer:
w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw) w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw)

View File

@ -41,7 +41,7 @@ type TPlayerPos struct {
// the state used for drawing and saving // the state used for drawing and saving
type worldSettings struct { type WorldSettings struct {
// settings // settings
voidGen bool voidGen bool
withPacks bool withPacks bool
@ -50,7 +50,7 @@ type worldSettings struct {
} }
type worldState struct { type worldState struct {
Dim world.Dimension dimension world.Dimension
chunks map[protocol.ChunkPos]*chunk.Chunk chunks map[protocol.ChunkPos]*chunk.Chunk
blockNBT map[protocol.SubChunkPos][]map[string]any blockNBT map[protocol.SubChunkPos][]map[string]any
entities map[uint64]*entityState entities map[uint64]*entityState
@ -69,7 +69,7 @@ type serverState struct {
Name string Name string
} }
type worldsServer struct { type worldsHandler struct {
ctx context.Context ctx context.Context
proxy *utils.ProxyContext proxy *utils.ProxyContext
mapUI *MapUI mapUI *MapUI
@ -78,53 +78,10 @@ type worldsServer struct {
worldState worldState worldState worldState
serverState serverState serverState serverState
settings worldSettings settings WorldSettings
} }
func NewWorldsServer(ctx context.Context, proxy *utils.ProxyContext, ServerName string, ui utils.UI) *worldsServer { var black16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16))
w := &worldsServer{
ctx: ctx,
proxy: proxy,
mapUI: nil,
gui: ui,
bp: behaviourpack.New(ServerName),
serverState: serverState{
ispre118: false,
worldCounter: 0,
ChunkRadius: 0,
playerInventory: nil,
PlayerPos: TPlayerPos{},
Name: ServerName,
},
settings: worldSettings{},
}
w.mapUI = NewMapUI(w)
w.Reset(w.CurrentName())
w.gui.Message(messages.Init{
Handler: nil,
})
return w
}
var dimensionIDMap = map[uint8]world.Dimension{
0: world.Overworld,
1: world.Nether,
2: world.End,
// < 1.18
10: world.Overworld_legacy,
11: world.Nether,
12: world.End,
}
var (
black16x16 = image.NewRGBA(image.Rect(0, 0, 16, 16))
)
func init() { func init() {
for i := 3; i < len(black16x16.Pix); i += 4 { for i := 3; i < len(black16x16.Pix); i += 4 {
@ -161,24 +118,87 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error {
return err return err
} }
w := NewWorldsServer(ctx, proxy, hostname, ui) proxy.AlwaysGetPacks = true
w.settings = worldSettings{ proxy.AddHandler(NewWorldsHandler(ctx, ui, WorldSettings{
voidGen: c.EnableVoid, voidGen: c.EnableVoid,
withPacks: c.Packs, withPacks: c.Packs,
saveImage: c.SaveImage, saveImage: c.SaveImage,
} }))
proxy.AlwaysGetPacks = true ui.Message(messages.SetUIState(messages.UIStateConnect))
proxy.GameDataModifier = func(gd *minecraft.GameData) { err = proxy.Run(ctx, serverAddress, hostname)
gd.ClientSideGeneration = false if err != nil {
return err
} }
ui.Message(messages.SetUIState(messages.UIStateFinished))
return nil
}
proxy.AddHandler(&utils.ProxyHandler{ func NewWorldsHandler(ctx context.Context, ui utils.UI, settings WorldSettings) *utils.ProxyHandler {
Name: "Worlds", w := &worldsHandler{
ConnectCB: w.OnConnect, ctx: ctx,
mapUI: nil,
gui: ui,
bp: nil,
serverState: serverState{
ispre118: false,
worldCounter: 0,
ChunkRadius: 0,
playerInventory: nil,
PlayerPos: TPlayerPos{},
},
settings: WorldSettings{},
}
w.mapUI = NewMapUI(w)
w.Reset(w.CurrentName())
return &utils.ProxyHandler{
Name: "Worlds",
ProxyRef: func(pc *utils.ProxyContext) {
w.proxy = pc
w.proxy.AddCommand(utils.IngameCommand{
Exec: func(cmdline []string) bool {
return w.setWorldName(strings.Join(cmdline, " "), false)
},
Cmd: protocol.Command{
Name: "setname",
Description: locale.Loc("setname_desc", nil),
Overloads: []protocol.CommandOverload{{
Parameters: []protocol.CommandParameter{{
Name: "name",
Type: protocol.CommandArgTypeString,
Optional: false,
}},
}},
},
})
w.proxy.AddCommand(utils.IngameCommand{
Exec: func(cmdline []string) bool {
return w.setVoidGen(!w.settings.voidGen, false)
},
Cmd: protocol.Command{
Name: "void",
Description: locale.Loc("void_desc", nil),
},
})
},
AddressAndName: func(address, hostname string) error {
w.bp = behaviourpack.New(hostname)
w.serverState.Name = hostname
return nil
},
OnClientConnect: func(conn *minecraft.Conn) { OnClientConnect: func(conn *minecraft.Conn) {
w.gui.Message(messages.SetUIState(messages.UIStateConnecting)) w.gui.Message(messages.SetUIState(messages.UIStateConnecting))
}, },
GameDataModifier: func(gd *minecraft.GameData) {
gd.ClientSideGeneration = false
},
ConnectCB: w.OnConnect,
PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) { PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) {
forward := true forward := true
@ -203,34 +223,13 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error {
} }
return pk, nil return pk, nil
}, },
}) OnEnd: func() {
w.SaveAndReset()
w.gui.Message(messages.SetUIState(messages.UIStateConnect)) },
err = w.proxy.Run(ctx, serverAddress, hostname)
if err != nil {
return err
}
w.SaveAndReset()
ui.Message(messages.SetUIState(messages.UIStateFinished))
return nil
}
func (w *worldsServer) SetPlayerPos(Position mgl32.Vec3, Pitch, Yaw, HeadYaw float32) {
last := w.serverState.PlayerPos
current := TPlayerPos{
Position: Position,
Pitch: Pitch,
Yaw: Yaw,
HeadYaw: HeadYaw,
}
w.serverState.PlayerPos = current
if int(last.Position.X()) != int(current.Position.X()) || int(last.Position.Z()) != int(current.Position.Z()) {
w.mapUI.SchedRedraw()
} }
} }
func (w *worldsServer) setVoidGen(val bool, fromUI bool) bool { func (w *worldsHandler) setVoidGen(val bool, fromUI bool) bool {
w.settings.voidGen = val w.settings.voidGen = val
var s = locale.Loc("void_generator_false", nil) var s = locale.Loc("void_generator_false", nil)
if w.settings.voidGen { if w.settings.voidGen {
@ -247,7 +246,7 @@ func (w *worldsServer) setVoidGen(val bool, fromUI bool) bool {
return true return true
} }
func (w *worldsServer) setWorldName(val string, fromUI bool) bool { func (w *worldsHandler) setWorldName(val string, fromUI bool) bool {
w.worldState.Name = val w.worldState.Name = val
w.proxy.SendMessage(locale.Loc("worldname_set", locale.Strmap{"Name": w.worldState.Name})) w.proxy.SendMessage(locale.Loc("worldname_set", locale.Strmap{"Name": w.worldState.Name}))
@ -260,7 +259,7 @@ func (w *worldsServer) setWorldName(val string, fromUI bool) bool {
return true return true
} }
func (w *worldsServer) CurrentName() string { func (w *worldsHandler) CurrentName() string {
worldName := "world" worldName := "world"
if w.serverState.worldCounter > 1 { if w.serverState.worldCounter > 1 {
worldName = fmt.Sprintf("world-%d", w.serverState.worldCounter) worldName = fmt.Sprintf("world-%d", w.serverState.worldCounter)
@ -268,9 +267,9 @@ func (w *worldsServer) CurrentName() string {
return worldName return worldName
} }
func (w *worldsServer) Reset(newName string) { func (w *worldsHandler) Reset(newName string) {
w.worldState = worldState{ w.worldState = worldState{
Dim: nil, dimension: nil,
chunks: make(map[protocol.ChunkPos]*chunk.Chunk), chunks: make(map[protocol.ChunkPos]*chunk.Chunk),
blockNBT: make(map[protocol.SubChunkPos][]map[string]any), blockNBT: make(map[protocol.SubChunkPos][]map[string]any),
entities: make(map[uint64]*entityState), entities: make(map[uint64]*entityState),
@ -280,23 +279,63 @@ func (w *worldsServer) Reset(newName string) {
w.mapUI.Reset() w.mapUI.Reset()
} }
// SaveAndReset writes the world to a folder, resets all the chunks func (w *worldState) cullChunks() {
func (w *worldsServer) SaveAndReset() { keys := make([]protocol.ChunkPos, 0, len(w.chunks))
for cp := range w.chunks {
// cull empty chunks
keys := make([]protocol.ChunkPos, 0, len(w.worldState.chunks))
for cp := range w.worldState.chunks {
keys = append(keys, cp) keys = append(keys, cp)
} }
for _, cp := range fp.Filter(func(cp protocol.ChunkPos) bool { for _, cp := range fp.Filter(func(cp protocol.ChunkPos) bool {
return !fp.Some(func(sc *chunk.SubChunk) bool { return !fp.Some(func(sc *chunk.SubChunk) bool {
return !sc.Empty() return !sc.Empty()
})(w.worldState.chunks[cp].Sub()) })(w.chunks[cp].Sub())
})(keys) { })(keys) {
delete(w.worldState.chunks, cp) delete(w.chunks, cp)
}
}
func (w *worldState) Save(folder string) (*mcdb.Provider, error) {
provider, err := mcdb.New(logrus.StandardLogger(), folder, opt.DefaultCompression)
if err != nil {
return nil, err
} }
// save chunk data
for cp, c := range w.chunks {
provider.SaveChunk((world.ChunkPos)(cp), c, w.dimension)
}
// save block nbt data
blockNBT := make(map[world.ChunkPos][]map[string]any)
for scp, v := range w.blockNBT { // 3d to 2d
cp := world.ChunkPos{scp.X(), scp.Z()}
blockNBT[cp] = append(blockNBT[cp], v...)
}
for cp, v := range blockNBT {
err = provider.SaveBlockNBT(cp, v, w.dimension)
if err != nil {
logrus.Error(err)
}
}
// save entities
chunkEntities := make(map[world.ChunkPos][]world.Entity)
for _, es := range w.entities {
cp := world.ChunkPos{int32(es.Position.X()) >> 4, int32(es.Position.Z()) >> 4}
chunkEntities[cp] = append(chunkEntities[cp], es.ToServerEntity())
}
for cp, v := range chunkEntities {
err = provider.SaveEntities(cp, v, w.dimension)
if err != nil {
logrus.Error(err)
}
}
return provider, err
}
// SaveAndReset writes the world to a folder, resets all the chunks
func (w *worldsHandler) SaveAndReset() {
w.worldState.cullChunks()
if len(w.worldState.chunks) == 0 { if len(w.worldState.chunks) == 0 {
w.Reset(w.CurrentName()) w.Reset(w.CurrentName())
return return
@ -312,42 +351,10 @@ func (w *worldsServer) SaveAndReset() {
folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.serverState.Name, w.worldState.Name)) folder := path.Join("worlds", fmt.Sprintf("%s/%s", w.serverState.Name, w.worldState.Name))
os.RemoveAll(folder) os.RemoveAll(folder)
os.MkdirAll(folder, 0o777) os.MkdirAll(folder, 0o777)
provider, err := w.worldState.Save(folder)
provider, err := mcdb.New(logrus.StandardLogger(), folder, opt.DefaultCompression)
if err != nil { if err != nil {
logrus.Fatal(err) logrus.Error(err)
} return
// save chunk data
for cp, c := range w.worldState.chunks {
provider.SaveChunk((world.ChunkPos)(cp), c, w.worldState.Dim)
}
// save block nbt data
blockNBT := make(map[world.ChunkPos][]map[string]any)
for scp, v := range w.worldState.blockNBT { // 3d to 2d
cp := world.ChunkPos{scp.X(), scp.Z()}
blockNBT[cp] = append(blockNBT[cp], v...)
}
for cp, v := range blockNBT {
err = provider.SaveBlockNBT(cp, v, w.worldState.Dim)
if err != nil {
logrus.Error(err)
}
}
// save entities
chunkEntities := make(map[world.ChunkPos][]world.Entity)
for _, es := range w.worldState.entities {
cp := world.ChunkPos{int32(es.Position.X()) >> 4, int32(es.Position.Z()) >> 4}
chunkEntities[cp] = append(chunkEntities[cp], es.ToServerEntity())
}
for cp, v := range chunkEntities {
err = provider.SaveEntities(cp, v, w.worldState.Dim)
if err != nil {
logrus.Error(err)
}
} }
err = provider.SaveLocalPlayerData(w.playerData()) err = provider.SaveLocalPlayerData(w.playerData())
@ -366,6 +373,7 @@ func (w *worldsServer) SaveAndReset() {
// set gamerules // set gamerules
ld := provider.LevelDat() ld := provider.LevelDat()
gd := w.proxy.Server.GameData() gd := w.proxy.Server.GameData()
ld.RandomSeed = int64(gd.WorldSeed)
for _, gr := range gd.GameRules { for _, gr := range gd.GameRules {
switch gr.Name { switch gr.Name {
case "commandblockoutput": case "commandblockoutput":
@ -434,8 +442,6 @@ func (w *worldsServer) SaveAndReset() {
} }
} }
ld.RandomSeed = int64(gd.WorldSeed)
// void world // void world
if w.settings.voidGen { if w.settings.voidGen {
ld.FlatWorldLayers = `{"biome_id":1,"block_layers":[{"block_data":0,"block_id":0,"count":1},{"block_data":0,"block_id":0,"count":2},{"block_data":0,"block_id":0,"count":1}],"encoding_version":3,"structure_options":null}` ld.FlatWorldLayers = `{"biome_id":1,"block_layers":[{"block_data":0,"block_id":0,"count":1},{"block_data":0,"block_id":0,"count":2},{"block_data":0,"block_id":0,"count":1}],"encoding_version":3,"structure_options":null}`
@ -545,9 +551,8 @@ func (w *worldsServer) SaveAndReset() {
w.gui.Message(messages.SetUIState(messages.UIStateMain)) w.gui.Message(messages.SetUIState(messages.UIStateMain))
} }
func (w *worldsServer) OnConnect(err error) bool { func (w *worldsHandler) OnConnect(err error) bool {
w.gui.Message(messages.SetUIState(messages.UIStateMain)) w.gui.Message(messages.SetUIState(messages.UIStateMain))
if err != nil { if err != nil {
return false return false
} }
@ -592,40 +597,14 @@ func (w *worldsServer) OnConnect(err error) bool {
dimensionID := gd.Dimension dimensionID := gd.Dimension
if w.serverState.ispre118 { if w.serverState.ispre118 {
logrus.Info(locale.Loc("using_under_118", nil)) logrus.Info(locale.Loc("using_under_118", nil))
dimensionID += 10 if dimensionID == 0 {
dimensionID += 10
}
} }
w.worldState.Dim = dimensionIDMap[uint8(dimensionID)] w.worldState.dimension, _ = world.DimensionByID(int(dimensionID))
} }
w.proxy.SendMessage(locale.Loc("use_setname", nil)) w.proxy.SendMessage(locale.Loc("use_setname", nil))
w.proxy.AddCommand(utils.IngameCommand{
Exec: func(cmdline []string) bool {
return w.setWorldName(strings.Join(cmdline, " "), false)
},
Cmd: protocol.Command{
Name: "setname",
Description: locale.Loc("setname_desc", nil),
Overloads: []protocol.CommandOverload{{
Parameters: []protocol.CommandParameter{{
Name: "name",
Type: protocol.CommandArgTypeString,
Optional: false,
}},
}},
},
})
w.proxy.AddCommand(utils.IngameCommand{
Exec: func(cmdline []string) bool {
return w.setVoidGen(!w.settings.voidGen, false)
},
Cmd: protocol.Command{
Name: "void",
Description: locale.Loc("void_desc", nil),
},
})
w.mapUI.Start() w.mapUI.Start()
return true return true
} }

View File

@ -102,7 +102,6 @@ func (p *Page) Layout(gtx C, th *material.Theme) D {
go func() { go func() {
defer p.Router.Wg.Done() defer p.Router.Wg.Done()
utils.InitDNS() utils.InitDNS()
utils.InitExtraDebug(p.Router.Ctx)
err := cmd.Execute(p.Router.Ctx, utils.CurrentUI) err := cmd.Execute(p.Router.Ctx, utils.CurrentUI)
if err != nil { if err != nil {

View File

@ -28,12 +28,12 @@ func dumpPacket(f io.WriteCloser, toServer bool, payload []byte) {
f.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB}) f.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB})
} }
type PacketCapturer struct { type packetCapturer struct {
proxy *utils.ProxyContext proxy *utils.ProxyContext
fio *os.File fio *os.File
} }
func (p *PacketCapturer) AddressAndName(address, hostname string) error { func (p *packetCapturer) AddressAndName(address, hostname string) error {
os.Mkdir("captures", 0o775) os.Mkdir("captures", 0o775)
fio, err := os.Create(fmt.Sprintf("captures/%s-%s.pcap2", hostname, time.Now().Format("2006-01-02_15-04-05"))) fio, err := os.Create(fmt.Sprintf("captures/%s-%s.pcap2", hostname, time.Now().Format("2006-01-02_15-04-05")))
if err != nil { if err != nil {
@ -44,7 +44,7 @@ func (p *PacketCapturer) AddressAndName(address, hostname string) error {
return nil return nil
} }
func (p *PacketCapturer) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) { func (p *packetCapturer) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) {
IsfromClient := utils.ClientAddr.String() == src.String() IsfromClient := utils.ClientAddr.String() == src.String()
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
@ -54,14 +54,14 @@ func (p *PacketCapturer) PacketFunc(header packet.Header, payload []byte, src, d
} }
func NewPacketCapturer() *utils.ProxyHandler { func NewPacketCapturer() *utils.ProxyHandler {
p := &PacketCapturer{} p := &packetCapturer{}
return &utils.ProxyHandler{ return &utils.ProxyHandler{
Name: "Packet Capturer", Name: "Packet Capturer",
ProxyRef: func(pc *utils.ProxyContext) { ProxyRef: func(pc *utils.ProxyContext) {
p.proxy = pc p.proxy = pc
}, },
PacketFunc: p.PacketFunc,
AddressAndName: p.AddressAndName, AddressAndName: p.AddressAndName,
PacketFunc: p.PacketFunc,
OnEnd: func() { OnEnd: func() {
p.fio.Close() p.fio.Close()
}, },

View File

@ -10,22 +10,12 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type ChatLogger struct { type chatLogger struct {
Verbose bool Verbose bool
fio *os.File fio *os.File
} }
func (c *ChatLogger) AddressAndName(address, hostname string) error { func (c *chatLogger) PacketCB(pk packet.Packet, toServer bool, t time.Time) (packet.Packet, error) {
filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07"))
f, err := os.Create(filename)
if err != nil {
return err
}
c.fio = f
return nil
}
func (c *ChatLogger) PacketCB(pk packet.Packet, toServer bool, t time.Time) (packet.Packet, error) {
if text, ok := pk.(*packet.Text); ok { if text, ok := pk.(*packet.Text); ok {
logLine := text.Message logLine := text.Message
if c.Verbose { if c.Verbose {
@ -42,13 +32,21 @@ func (c *ChatLogger) PacketCB(pk packet.Packet, toServer bool, t time.Time) (pac
} }
func NewChatLogger() *utils.ProxyHandler { func NewChatLogger() *utils.ProxyHandler {
p := &ChatLogger{} c := &chatLogger{}
return &utils.ProxyHandler{ return &utils.ProxyHandler{
Name: "Packet Capturer", Name: "Packet Capturer",
PacketCB: p.PacketCB, PacketCB: c.PacketCB,
AddressAndName: p.AddressAndName, AddressAndName: func(address, hostname string) error {
filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07"))
f, err := os.Create(filename)
if err != nil {
return err
}
c.fio = f
return nil
},
OnEnd: func() { OnEnd: func() {
p.fio.Close() c.fio.Close()
}, },
} }
} }

View File

@ -0,0 +1,96 @@
package seconduser
import (
"github.com/bedrock-tool/bedrocktool/locale"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/chunk"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"github.com/sirupsen/logrus"
)
func (s *secondaryUser) ResetWorld() {
s.chunks = make(map[world.ChunkPos]*chunk.Chunk)
s.blockNBT = make(map[protocol.SubChunkPos][]map[string]any)
}
func (s *secondaryUser) processChangeDimension(pk *packet.ChangeDimension) {
s.ResetWorld()
dimensionID := pk.Dimension
if s.ispre118 && dimensionID == 0 {
dimensionID += 10
}
s.dimension, _ = world.DimensionByID(int(dimensionID))
}
func (s *secondaryUser) processLevelChunk(pk *packet.LevelChunk) {
// ignore empty chunks THANKS WEIRD SERVER SOFTWARE DEVS
if len(pk.RawPayload) == 0 {
logrus.Info(locale.Loc("empty_chunk", nil))
return
}
var subChunkCount int
switch pk.SubChunkCount {
case protocol.SubChunkRequestModeLimited:
fallthrough
case protocol.SubChunkRequestModeLimitless:
subChunkCount = 0
default:
subChunkCount = int(pk.SubChunkCount)
}
ch, blockNBTs, err := chunk.NetworkDecode(world.AirRID(), pk.RawPayload, subChunkCount, s.dimension.Range(), s.ispre118, s.hasCustomBlocks)
if err != nil {
logrus.Error(err)
return
}
if blockNBTs != nil {
s.blockNBT[protocol.SubChunkPos{
pk.Position.X(), 0, pk.Position.Z(),
}] = blockNBTs
}
s.chunks[world.ChunkPos(pk.Position)] = ch
switch pk.SubChunkCount {
case protocol.SubChunkRequestModeLimited:
case protocol.SubChunkRequestModeLimitless:
default:
}
for _, p := range s.server.Players() {
p.Session().ViewChunk(world.ChunkPos(pk.Position), ch, nil)
}
}
func (s *secondaryUser) processSubChunk(pk *packet.SubChunk) {
offsets := make([]protocol.SubChunkOffset, 0, len(pk.SubChunkEntries))
for _, sub := range pk.SubChunkEntries {
offsets = append(offsets, sub.Offset)
var (
absX = pk.Position[0] + int32(sub.Offset[0])
absY = pk.Position[1] + int32(sub.Offset[1])
absZ = pk.Position[2] + int32(sub.Offset[2])
subPos = protocol.SubChunkPos{absX, absY, absZ}
pos = world.ChunkPos{absX, absZ}
)
ch, ok := s.chunks[pos]
if !ok {
logrus.Error(locale.Loc("subchunk_before_chunk", nil))
continue
}
blockNBT, err := ch.ApplySubChunkEntry(uint8(absY), &sub)
if err != nil {
logrus.Error(err)
}
if blockNBT != nil {
s.blockNBT[subPos] = blockNBT
}
chunk.LightArea([]*chunk.Chunk{ch}, 0, 0).Fill()
}
for _, p := range s.server.Players() {
p.Session().ViewSubChunks(world.SubChunkPos(pk.Position), offsets)
}
}

View File

@ -0,0 +1,19 @@
package seconduser
import "github.com/df-mc/dragonfly/server/session"
type fwdlistener struct {
Conn chan session.Conn
}
func (l *fwdlistener) Accept() (session.Conn, error) {
return <-l.Conn, nil
}
func (l *fwdlistener) Disconnect(conn session.Conn, reason string) error {
return conn.Close()
}
func (l *fwdlistener) Close() error {
return nil
}

View File

@ -0,0 +1,64 @@
package seconduser
import (
"sync"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/chunk"
"github.com/google/uuid"
)
type provider struct {
s *secondaryUser
}
func (p *provider) Settings() *world.Settings {
return &world.Settings{
Mutex: sync.Mutex{},
Name: "world",
Spawn: cube.Pos{0, 0, 0},
DefaultGameMode: world.GameModeCreative,
Difficulty: world.DifficultyNormal,
}
}
func (p *provider) SaveSettings(*world.Settings) {
}
func (p *provider) Close() error {
return nil
}
func (p *provider) LoadPlayerSpawnPosition(uuid uuid.UUID) (pos cube.Pos, exists bool, err error) {
return cube.Pos{0, 0, 0}, false, nil
}
func (p *provider) SavePlayerSpawnPosition(uuid uuid.UUID, pos cube.Pos) error {
return nil
}
func (p *provider) LoadChunk(position world.ChunkPos, dim world.Dimension) (c *chunk.Chunk, exists bool, err error) {
c, ok := p.s.chunks[position]
return c, ok, nil
}
func (p *provider) SaveChunk(position world.ChunkPos, c *chunk.Chunk, dim world.Dimension) error {
return nil
}
func (p *provider) LoadEntities(position world.ChunkPos, dim world.Dimension, reg world.EntityRegistry) ([]world.Entity, error) {
return nil, nil
}
func (p *provider) SaveEntities(position world.ChunkPos, entities []world.Entity, dim world.Dimension) error {
return nil
}
func (p *provider) LoadBlockNBT(position world.ChunkPos, dim world.Dimension) ([]map[string]any, error) {
return nil, nil
}
func (p *provider) SaveBlockNBT(position world.ChunkPos, data []map[string]any, dim world.Dimension) error {
return nil
}

View File

@ -0,0 +1,104 @@
package seconduser
import (
"time"
"github.com/bedrock-tool/bedrocktool/utils"
"github.com/df-mc/dragonfly/server"
"github.com/df-mc/dragonfly/server/player"
"github.com/df-mc/dragonfly/server/player/skin"
"github.com/df-mc/dragonfly/server/session"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/chunk"
"github.com/go-gl/mathgl/mgl64"
"github.com/sandertv/gophertunnel/minecraft"
"github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
"github.com/sirupsen/logrus"
)
type secondaryUser struct {
listener *fwdlistener
server *server.Server
ispre118 bool
hasCustomBlocks bool
chunks map[world.ChunkPos]*chunk.Chunk
blockNBT map[protocol.SubChunkPos][]map[string]any
dimension world.Dimension
mainPlayer *player.Player
}
func NewSecondUser() *utils.ProxyHandler {
s := &secondaryUser{
listener: &fwdlistener{
Conn: make(chan session.Conn),
},
chunks: make(map[world.ChunkPos]*chunk.Chunk),
blockNBT: make(map[protocol.SubChunkPos][]map[string]any),
dimension: world.Overworld,
}
s.server = server.Config{
Listeners: []func(conf server.Config) (server.Listener, error){
func(conf server.Config) (server.Listener, error) {
return s.listener, nil
},
},
Log: logrus.StandardLogger(),
Name: "Secondary",
Generator: func(dim world.Dimension) world.Generator { return &world.NopGenerator{} },
WorldProvider: &provider{s: s},
ReadOnlyWorld: true,
}.New()
go s.loop()
return &utils.ProxyHandler{
Name: "Secondary User",
SecondaryClientCB: s.SecondaryClientCB,
OnClientConnect: func(conn *minecraft.Conn) {
id := conn.IdentityData()
s.mainPlayer = player.New(id.DisplayName, skin.New(64, 64), mgl64.Vec3{0, 00})
},
PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) {
switch pk := pk.(type) {
case *packet.LevelChunk:
s.processLevelChunk(pk)
case *packet.SubChunk:
s.processSubChunk(pk)
case *packet.ChangeDimension:
s.processChangeDimension(pk)
case *packet.MovePlayer:
v := mgl64.Vec3{float64(pk.Position.X()), float64(pk.Position.Y()), float64(pk.Position.Z())}
s.mainPlayer.Teleport(v)
case *packet.PlayerAuthInput:
v := mgl64.Vec3{float64(pk.Position.X()), float64(pk.Position.Y()), float64(pk.Position.Z())}
s.mainPlayer.Teleport(v)
for _, p := range s.server.Players() {
p.Teleport(s.mainPlayer.Position())
}
}
return pk, nil
},
}
}
func (s *secondaryUser) SecondaryClientCB(conn *minecraft.Conn) {
s.listener.Conn <- conn
}
func (s *secondaryUser) loop() {
s.server.Listen()
for s.server.Accept(func(p *player.Player) {
logrus.Infof("%s Joined", p.Name())
p.Teleport(s.mainPlayer.Position())
}) {
}
}

View File

@ -60,7 +60,7 @@ func (s *SkinSaver) AddSkin(playerName string, playerID uuid.UUID, playerSkin *p
} }
s.playerNames[playerID] = playerName s.playerNames[playerID] = playerName
skin := &utils.Skin{playerSkin} skin := &utils.Skin{Skin: playerSkin}
if s.OnlyIfHasGeometry && !skin.HaveGeometry() { if s.OnlyIfHasGeometry && !skin.HaveGeometry() {
return "", nil, false return "", nil, false
} }

View File

@ -74,7 +74,6 @@ func (c *InteractiveCLI) Start(ctx context.Context, cancel context.CancelFunc) e
flag.Parse() flag.Parse()
InitDNS() InitDNS()
InitExtraDebug(ctx)
subcommands.Execute(ctx) subcommands.Execute(ctx)

View File

@ -1,15 +1,17 @@
package utils package utils
import ( import (
"bufio"
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"net" "net"
"os"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils/crypt"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
@ -182,52 +184,87 @@ func DumpStruct(data interface{}) {
var ClientAddr net.Addr var ClientAddr net.Addr
var pool = packet.NewPool() var pool = packet.NewPool()
func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { func NewDebugLogger(extraVerbose bool) *ProxyHandler {
var pk packet.Packet var logPlain, logCrypt, logCryptEnc io.WriteCloser
if pkFunc, ok := pool[header.PacketID]; ok { var packetsLogF *bufio.Writer
pk = pkFunc()
} else {
pk = &packet.Unknown{PacketID: header.PacketID, Payload: payload}
}
defer func() { if extraVerbose {
if recoveredErr := recover(); recoveredErr != nil { // open plain text log
logrus.Errorf("%T: %s", pk, recoveredErr.(error)) logPlain, err := os.Create("packets.log")
if err != nil {
logrus.Error(err)
} }
}()
pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0)) // open gpg log
logCrypt, err := os.Create("packets.log.gpg")
if FLog != nil { if err != nil {
dmpLock.Lock() logrus.Error(err)
FLog.Write([]byte(dmpStruct(0, pk, true, false) + "\n\n\n")) } else {
dmpLock.Unlock() // encrypter for the log
} logCryptEnc, err = crypt.Encer("packets.log", logCrypt)
if err != nil {
pkName := reflect.TypeOf(pk).String()[1:] logrus.Error(err)
if slices.Contains(MutedPackets, pkName) { }
return
}
switch pk := pk.(type) {
case *packet.Disconnect:
logrus.Infof(locale.Loc("disconnect", locale.Strmap{"Pk": pk}))
}
dirS2C := color.GreenString("S") + "->" + color.CyanString("C")
dirC2S := color.CyanString("C") + "->" + color.GreenString("S")
var dir string = dirS2C
if ClientAddr != nil {
if src == ClientAddr {
dir = dirC2S
}
} else {
srcAddr, _, _ := net.SplitHostPort(src.String())
if IPPrivate(net.ParseIP(srcAddr)) {
dir = dirS2C
} }
packetsLogF = bufio.NewWriter(io.MultiWriter(logPlain, logCryptEnc))
} }
logrus.Debugf("%s 0x%02x, %s", dir, pk.ID(), pkName) return &ProxyHandler{
Name: "Debug",
PacketFunc: func(header packet.Header, payload []byte, src, dst net.Addr) {
var pk packet.Packet
if pkFunc, ok := pool[header.PacketID]; ok {
pk = pkFunc()
} else {
pk = &packet.Unknown{PacketID: header.PacketID, Payload: payload}
}
defer func() {
if recoveredErr := recover(); recoveredErr != nil {
logrus.Errorf("%T: %s", pk, recoveredErr.(error))
}
}()
pk.Marshal(protocol.NewReader(bytes.NewBuffer(payload), 0))
if extraVerbose {
dmpLock.Lock()
packetsLogF.Write([]byte(dmpStruct(0, pk, true, false) + "\n\n\n"))
dmpLock.Unlock()
}
pkName := reflect.TypeOf(pk).String()[1:]
if !slices.Contains(MutedPackets, pkName) {
dirS2C := color.GreenString("S") + "->" + color.CyanString("C")
dirC2S := color.CyanString("C") + "->" + color.GreenString("S")
var dir string = dirS2C
if ClientAddr != nil {
if src == ClientAddr {
dir = dirC2S
}
} else {
srcAddr, _, _ := net.SplitHostPort(src.String())
if IPPrivate(net.ParseIP(srcAddr)) {
dir = dirS2C
}
}
logrus.Debugf("%s 0x%02x, %s", dir, pk.ID(), pkName)
}
},
OnEnd: func() {
if packetsLogF != nil {
packetsLogF.Flush()
}
if logPlain != nil {
logPlain.Close()
}
if logCryptEnc != nil {
logCryptEnc.Close()
}
if logCrypt != nil {
logCrypt.Close()
}
},
}
} }

View File

@ -55,7 +55,10 @@ type ProxyHandler struct {
Name string Name string
ProxyRef func(*ProxyContext) ProxyRef func(*ProxyContext)
// //
AddressAndName func(address, name string) error AddressAndName func(address, hostname string) error
// called to change game data
GameDataModifier func(*minecraft.GameData)
// called for every packet // called for every packet
PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr) PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr)
@ -85,9 +88,6 @@ type ProxyContext struct {
CustomClientData *login.ClientData CustomClientData *login.ClientData
handlers []*ProxyHandler handlers []*ProxyHandler
// called to change game data
GameDataModifier func(*minecraft.GameData)
} }
func NewProxy() (*ProxyContext, error) { func NewProxy() (*ProxyContext, error) {
@ -284,6 +284,10 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
} }
} }
if Options.Debug || Options.ExtraDebug {
p.AddHandler(NewDebugLogger(Options.ExtraDebug))
}
if strings.HasPrefix(serverAddress, "PCAP!") { if strings.HasPrefix(serverAddress, "PCAP!") {
return createReplayConnection(ctx, serverAddress[5:], p) return createReplayConnection(ctx, serverAddress[5:], p)
} }
@ -373,8 +377,10 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
defer p.Server.Close() defer p.Server.Close()
gd := p.Server.GameData() gd := p.Server.GameData()
if p.GameDataModifier != nil { for _, handler := range p.handlers {
p.GameDataModifier(&gd) if handler.GameDataModifier != nil {
handler.GameDataModifier(&gd)
}
} }
// spawn and start the game // spawn and start the game
@ -427,14 +433,16 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err
if len(wantSecondary) > 0 { if len(wantSecondary) > 0 {
go func() { go func() {
c, err := p.Listener.Accept() for {
if err != nil { c, err := p.Listener.Accept()
logrus.Error(err) if err != nil {
return logrus.Error(err)
} return
}
for _, handler := range wantSecondary { for _, handler := range wantSecondary {
go handler.SecondaryClientCB(c.(*minecraft.Conn)) go handler.SecondaryClientCB(c.(*minecraft.Conn))
}
} }
}() }()
} }

View File

@ -141,10 +141,6 @@ func createReplayConnection(ctx context.Context, filename string, proxy *ProxyCo
dst = client dst = client
} }
if Options.Debug {
PacketLogger(hdr, f.Bytes(), src, dst)
}
for _, handler := range proxy.handlers { for _, handler := range proxy.handlers {
if handler.PacketFunc != nil { if handler.PacketFunc != nil {
handler.PacketFunc(hdr, f.Bytes(), src, dst) handler.PacketFunc(hdr, f.Bytes(), src, dst)

View File

@ -2,14 +2,12 @@
package utils package utils
import ( import (
"bufio"
"bytes" "bytes"
"context" "context"
"crypto/aes" "crypto/aes"
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
"errors" "errors"
"io"
"net" "net"
"os" "os"
"path" "path"
@ -18,7 +16,6 @@ import (
"sync" "sync"
"github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/locale"
"github.com/bedrock-tool/bedrocktool/utils/crypt"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -72,10 +69,6 @@ func connectServer(ctx context.Context, address string, ClientData *login.Client
if header.PacketID == packet.IDRequestNetworkSettings { if header.PacketID == packet.IDRequestNetworkSettings {
ClientAddr = src ClientAddr = src
} }
if Options.Debug {
PacketLogger(header, payload, src, dst)
}
if packetFunc != nil { if packetFunc != nil {
packetFunc(header, payload, src, dst) packetFunc(header, payload, src, dst)
} }
@ -174,50 +167,3 @@ func CfbDecrypt(data []byte, key []byte) []byte {
} }
return data return data
} }
func InitExtraDebug(ctx context.Context) {
if !Options.ExtraDebug {
return
}
Options.Debug = true
var logPlain, logCryptEnc io.WriteCloser = nil, nil
// open plain text log
logPlain, err := os.Create("packets.log")
if err != nil {
logrus.Error(err)
}
// open gpg log
logCrypt, err := os.Create("packets.log.gpg")
if err != nil {
logrus.Error(err)
} else {
// encrypter for the log
logCryptEnc, err = crypt.Encer("packets.log", logCrypt)
if err != nil {
logrus.Error(err)
}
}
b := bufio.NewWriter(io.MultiWriter(logPlain, logCryptEnc))
FLog = b
if err != nil {
logrus.Error(err)
}
go func() {
<-ctx.Done()
b.Flush()
if logPlain != nil {
logPlain.Close()
}
if logCrypt != nil {
logCrypt.Close()
}
if logCryptEnc != nil {
logCryptEnc.Close()
}
}()
}