Files
go-mc/server/dimension/loadmanager.go
2022-05-22 14:46:07 +08:00

103 lines
2.5 KiB
Go

package dimension
import (
"container/list"
"github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/level"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/server"
"github.com/Tnze/go-mc/server/clientinfo"
"github.com/Tnze/go-mc/server/internal/bvh"
)
type vec2d = bvh.Vec2[float64]
type sphere2d = bvh.Sphere[float64, vec2d]
type manager struct {
storage
activeChunks *list.List
chunkElement map[level.ChunkPos]*list.Element
players bvh.Tree[float64, sphere2d, *server.Player]
clients *clientinfo.ClientInformation
}
type chunkHandler struct {
level.ChunkPos
*level.Chunk
// records all players loaded this chunk
players map[*server.Player]struct{}
}
func (m *manager) refresh() error {
all := func(b sphere2d) bool { return true }
m.players.Find(all, func(n *bvh.Node[float64, sphere2d, *server.Player]) bool {
p := n.Value
v := m.clients.Players[p.UUID].ViewDistance
for i := 1 - v; i < v; i++ {
for j := 1 - v; j < v; j++ {
pos := level.ChunkPos{
X: int(n.Box.Center[0]) >> 4,
Z: int(n.Box.Center[1]) >> 4,
}
point := vec2d{float64(pos.X + i), float64(pos.Z + j)}
if _, exist := m.chunkElement[pos]; !exist && n.Box.WithIn(point) {
c, err := m.GetChunk(pos)
if err != nil {
return false
}
m.chunkElement[pos] = m.activeChunks.PushBack(c)
}
}
}
return true
})
for e := m.activeChunks.Front(); e != nil; {
ch := e.Value.(*chunkHandler)
point := vec2d{float64(ch.X), float64(ch.Z)}
filter := bvh.TouchPoint[vec2d, sphere2d](point)
newPlayers := make(map[*server.Player]struct{})
m.players.Find(filter, func(n *bvh.Node[float64, sphere2d, *server.Player]) bool {
p := n.Value
if _, ok := ch.players[p]; ok {
delete(ch.players, p)
} else {
playerLoadChunk(p, ch)
}
newPlayers[p] = struct{}{}
return true
})
for p := range ch.players {
playerUnloadChunk(p, ch)
}
if len(newPlayers) > 0 {
ch.players = newPlayers
e = e.Next()
} else {
// no player around this chunk, unload it
if err := m.PutChunk(ch.ChunkPos, ch.Chunk); err != nil {
return err
}
next := e.Next()
m.activeChunks.Remove(e)
delete(m.chunkElement, ch.ChunkPos)
e = next
}
}
return nil
}
func playerLoadChunk(p *server.Player, c *chunkHandler) {
p.WritePacket(server.Packet758(pk.Marshal(
packetid.ClientboundLevelChunkWithLight,
c.ChunkPos, c.Chunk,
)))
}
func playerUnloadChunk(p *server.Player, c *chunkHandler) {
p.WritePacket(server.Packet758(pk.Marshal(
packetid.ClientboundForgetLevelChunk,
c.ChunkPos,
)))
}