dim manager test 2

This commit is contained in:
Tnze
2022-05-22 14:46:07 +08:00
parent 562836ac5e
commit a5dd70d1ea
6 changed files with 183 additions and 55 deletions

View File

@ -0,0 +1,73 @@
package clientinfo
import (
"context"
"github.com/google/uuid"
"github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/server"
)
type ClientInformation struct {
Players map[uuid.UUID]*Info
}
type Info struct {
Locale string
ViewDistance int
ChatMode byte
ChatColors bool
DisplayedSkinParts byte
MainHand byte // 0: Left, 1: Right.
EnableTextFiltering bool
AllowServerListings bool
}
func (c *ClientInformation) Init(g *server.Game) {
c.Players = make(map[uuid.UUID]*Info)
g.AddHandler(&server.PacketHandler{
ID: packetid.ServerboundClientInformation,
F: func(player *server.Player, p server.Packet758) error {
var (
Locale pk.String
ViewDistance pk.Byte
ChatMode pk.VarInt
ChatColors pk.Boolean
DisplayedSkinParts pk.UnsignedByte
MainHand pk.VarInt
EnableTextFiltering pk.Boolean
AllowServerListings pk.Boolean
)
err := pk.Packet(p).Scan(
&Locale,
&ViewDistance,
&ChatMode,
&ChatColors,
&DisplayedSkinParts,
&MainHand,
&EnableTextFiltering,
&AllowServerListings,
)
if err != nil {
return err
}
c.Players[player.UUID] = &Info{
Locale: string(Locale),
ViewDistance: int(ViewDistance),
ChatMode: byte(ChatMode),
ChatColors: bool(ChatColors),
DisplayedSkinParts: byte(DisplayedSkinParts),
MainHand: byte(MainHand),
EnableTextFiltering: bool(EnableTextFiltering),
AllowServerListings: bool(AllowServerListings),
}
return nil
},
})
}
func (c *ClientInformation) Run(ctx context.Context) {}
func (c *ClientInformation) AddPlayer(p *server.Player) {}
func (c *ClientInformation) RemovePlayer(p *server.Player) {}

View File

@ -3,51 +3,100 @@ package dimension
import ( import (
"container/list" "container/list"
"github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/level" "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 { type manager struct {
storage storage
elements map[level.ChunkPos]*list.Element
activeChunks *list.List activeChunks *list.List
chunkElement map[level.ChunkPos]*list.Element
chunkLoadQueue []level.ChunkPos players bvh.Tree[float64, sphere2d, *server.Player]
chunkUnloadQueue []level.ChunkPos clients *clientinfo.ClientInformation
} }
type chunkHandler struct { type chunkHandler struct {
level.ChunkPos level.ChunkPos
*level.Chunk *level.Chunk
// records all players loaded this chunk
players map[*server.Player]struct{}
} }
func (m *manager) refresh(players [][2]int) { func (m *manager) refresh() error {
m.chunkLoadQueue = m.chunkLoadQueue[:0] all := func(b sphere2d) bool { return true }
m.chunkUnloadQueue = m.chunkUnloadQueue[:0] m.players.Find(all, func(n *bvh.Node[float64, sphere2d, *server.Player]) bool {
p := n.Value
newActives := list.New() v := m.clients.Players[p.UUID].ViewDistance
const N = 16 for i := 1 - v; i < v; i++ {
for _, p := range players { for j := 1 - v; j < v; j++ {
for i := 1 - N; i < N; i++ {
for j := 1 - N; j < N; j++ {
pos := level.ChunkPos{ pos := level.ChunkPos{
X: p[0] + i, X: int(n.Box.Center[0]) >> 4,
Z: p[1] + j, Z: int(n.Box.Center[1]) >> 4,
} }
if e := m.elements[pos]; e != nil { point := vec2d{float64(pos.X + i), float64(pos.Z + j)}
// chunk exist, move into newActives if _, exist := m.chunkElement[pos]; !exist && n.Box.WithIn(point) {
v := m.activeChunks.Remove(e) c, err := m.GetChunk(pos)
m.elements[pos] = newActives.PushBack(v) if err != nil {
} else { return false
// not exist, load from storage }
m.chunkLoadQueue = append(m.chunkLoadQueue, pos) 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
}
} }
for e := m.activeChunks.Front(); e != nil; e = e.Next() { return nil
pos := e.Value.(chunkHandler).ChunkPos }
m.chunkUnloadQueue = append(m.chunkUnloadQueue, pos)
m.elements[pos] = newActives.PushBack(e.Value) func playerLoadChunk(p *server.Player, c *chunkHandler) {
} p.WritePacket(server.Packet758(pk.Marshal(
m.activeChunks = newActives packetid.ClientboundLevelChunkWithLight,
c.ChunkPos, c.Chunk,
)))
}
func playerUnloadChunk(p *server.Player, c *chunkHandler) {
p.WritePacket(server.Packet758(pk.Marshal(
packetid.ClientboundForgetLevelChunk,
c.ChunkPos,
)))
} }

View File

@ -0,0 +1,7 @@
package dimension
import "testing"
func Test_manager(t *testing.T) {
}

View File

@ -56,4 +56,4 @@ func (s Sphere[I, V]) Union(other Sphere[I, V]) Sphere[I, V] {
R: d + s.R + other.R, R: d + s.R + other.R,
} }
} }
func (s Sphere[I, V]) Surface() I { return I(2 * math.Pi * s.R) } func (s Sphere[I, V]) Surface() I { return 2 * math.Pi * s.R }

View File

@ -10,7 +10,7 @@ type Node[I constraints.Float, B interface {
Union(B) B Union(B) B
Surface() I Surface() I
}, V any] struct { }, V any] struct {
box B Box B
Value V Value V
parent *Node[I, B, V] parent *Node[I, B, V]
children [2]*Node[I, B, V] children [2]*Node[I, B, V]
@ -35,17 +35,14 @@ func (n *Node[I, B, V]) findChildPointer(child *Node[I, B, V]) **Node[I, B, V] {
panic("unreachable, please make sure the 'not' is the n's child") panic("unreachable, please make sure the 'not' is the n's child")
} }
func (n *Node[I, B, V]) each(test func(bound B) bool, foreach func(v V)) { func (n *Node[I, B, V]) each(test func(bound B) bool, foreach func(n *Node[I, B, V]) bool) bool {
if n == nil { if n == nil {
return return true
} }
if n.isLeaf { if n.isLeaf {
if test(n.box) { return !test(n.Box) || foreach(n)
foreach(n.Value)
}
} else { } else {
n.children[0].each(test, foreach) return n.children[0].each(test, foreach) && n.children[1].each(test, foreach)
n.children[1].each(test, foreach)
} }
} }
@ -58,7 +55,7 @@ type Tree[I constraints.Float, B interface {
func (t *Tree[I, B, V]) Insert(leaf B, value V) (n *Node[I, B, V]) { func (t *Tree[I, B, V]) Insert(leaf B, value V) (n *Node[I, B, V]) {
n = &Node[I, B, V]{ n = &Node[I, B, V]{
box: leaf, Box: leaf,
Value: value, Value: value,
parent: nil, parent: nil,
children: [2]*Node[I, B, V]{nil, nil}, children: [2]*Node[I, B, V]{nil, nil},
@ -71,7 +68,7 @@ func (t *Tree[I, B, V]) Insert(leaf B, value V) (n *Node[I, B, V]) {
// Stage 1: find the best sibling for the new leaf // Stage 1: find the best sibling for the new leaf
sibling := t.root sibling := t.root
bestCost := t.root.box.Union(leaf).Surface() bestCost := t.root.Box.Union(leaf).Surface()
parentTo := &t.root // the parent's children pointer which point to the sibling parentTo := &t.root // the parent's children pointer which point to the sibling
var queue searchHeap[I, Node[I, B, V]] var queue searchHeap[I, Node[I, B, V]]
@ -81,8 +78,8 @@ func (t *Tree[I, B, V]) Insert(leaf B, value V) (n *Node[I, B, V]) {
for queue.Len() > 0 { for queue.Len() > 0 {
p := heap.Pop(&queue).(searchItem[I, Node[I, B, V]]) p := heap.Pop(&queue).(searchItem[I, Node[I, B, V]])
// determine if node p has the best cost // determine if node p has the best cost
mergeSurface := p.pointer.box.Union(leaf).Surface() mergeSurface := p.pointer.Box.Union(leaf).Surface()
deltaCost := mergeSurface - p.pointer.box.Surface() deltaCost := mergeSurface - p.pointer.Box.Surface()
cost := p.inheritedCost + mergeSurface cost := p.inheritedCost + mergeSurface
if cost <= bestCost { if cost <= bestCost {
bestCost = cost bestCost = cost
@ -107,7 +104,7 @@ func (t *Tree[I, B, V]) Insert(leaf B, value V) (n *Node[I, B, V]) {
// Stage 2: create a new parent // Stage 2: create a new parent
*parentTo = &Node[I, B, V]{ *parentTo = &Node[I, B, V]{
box: sibling.box.Union(leaf), // we will calculate in Stage3 Box: sibling.Box.Union(leaf), // we will calculate in Stage3
parent: sibling.parent, parent: sibling.parent,
children: [2]*Node[I, B, V]{sibling, n}, children: [2]*Node[I, B, V]{sibling, n},
isLeaf: false, isLeaf: false,
@ -117,7 +114,7 @@ func (t *Tree[I, B, V]) Insert(leaf B, value V) (n *Node[I, B, V]) {
// Stage 3: walk back up the tree refitting AABBs // Stage 3: walk back up the tree refitting AABBs
for p := *parentTo; p != nil; p = p.parent { for p := *parentTo; p != nil; p = p.parent {
p.box = p.children[0].box.Union(p.children[1].box) p.Box = p.children[0].Box.Union(p.children[1].Box)
t.rotate(p) t.rotate(p)
} }
return return
@ -140,7 +137,7 @@ func (t *Tree[I, B, V]) Delete(n *Node[I, B, V]) interface{} {
*p = sibling *p = sibling
sibling.parent = grand sibling.parent = grand
for p := sibling.parent; p.parent != nil; p = p.parent { for p := sibling.parent; p.parent != nil; p = p.parent {
p.box = p.children[0].box.Union(p.children[1].box) p.Box = p.children[0].Box.Union(p.children[1].Box)
t.rotate(p) t.rotate(p)
} }
} }
@ -153,23 +150,23 @@ func (t *Tree[I, B, V]) rotate(n *Node[I, B, V]) {
} }
// trying to swap n's sibling and children // trying to swap n's sibling and children
sibling := n.parent.findAnotherChild(n) sibling := n.parent.findAnotherChild(n)
current := n.box.Surface() current := n.Box.Surface()
if n.children[1].box.Union(sibling.box).Surface() < current { if n.children[1].Box.Union(sibling.Box).Surface() < current {
// swap n.children[0] and sibling // swap n.children[0] and sibling
t1 := [2]*Node[I, B, V]{n, n.children[0]} t1 := [2]*Node[I, B, V]{n, n.children[0]}
t2 := [2]*Node[I, B, V]{sibling, n.children[1]} t2 := [2]*Node[I, B, V]{sibling, n.children[1]}
n.parent.children, n.children, n.children[0].parent, sibling.parent = t1, t2, n.parent, n n.parent.children, n.children, n.children[0].parent, sibling.parent = t1, t2, n.parent, n
n.box = n.children[0].box.Union(n.children[1].box) n.Box = n.children[0].Box.Union(n.children[1].Box)
} else if n.children[0].box.Union(sibling.box).Surface() < current { } else if n.children[0].Box.Union(sibling.Box).Surface() < current {
// swap n.children[1] and sibling // swap n.children[1] and sibling
t1 := [2]*Node[I, B, V]{n, n.children[1]} t1 := [2]*Node[I, B, V]{n, n.children[1]}
t2 := [2]*Node[I, B, V]{sibling, n.children[0]} t2 := [2]*Node[I, B, V]{sibling, n.children[0]}
n.parent.children, n.children, n.children[1].parent, sibling.parent = t1, t2, n.parent, n n.parent.children, n.children, n.children[1].parent, sibling.parent = t1, t2, n.parent, n
n.box = n.children[0].box.Union(n.children[1].box) n.Box = n.children[0].Box.Union(n.children[1].Box)
} }
} }
func (t *Tree[I, B, V]) Find(test func(bound B) bool, foreach func(v V)) { func (t *Tree[I, B, V]) Find(test func(bound B) bool, foreach func(n *Node[I, B, V]) bool) {
t.root.each(test, foreach) t.root.each(test, foreach)
} }

View File

@ -23,8 +23,9 @@ func TestTree2_Insert(t *testing.T) {
// visualize // visualize
t.Log(bvh) t.Log(bvh)
} }
bvh.Find(TouchPoint[Vec2[float64], AABB[float64, Vec2[float64]]](Vec2[float64]{0.5, 0.5}), func(v int) { bvh.Find(TouchPoint[Vec2[float64], AABB[float64, Vec2[float64]]](Vec2[float64]{0.5, 0.5}), func(n *Node[float64, AABB[float64, Vec2[float64]], int]) bool {
t.Logf("find! %v", v) t.Logf("find! %v", n.Value)
return true
}) })
} }
@ -46,8 +47,9 @@ func TestTree2_Find_vec(t *testing.T) {
} }
find := func(test func(bound AABBVec2d) bool) []int { find := func(test func(bound AABBVec2d) bool) []int {
var result []int var result []int
bvh.Find(test, func(i int) { bvh.Find(test, func(n *Node[float64, AABBVec2d, int]) bool {
result = append(result, i) result = append(result, n.Value)
return true
}) })
return result return result
} }
@ -108,7 +110,7 @@ func BenchmarkTree2_Find_random(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for _, v := range poses { for _, v := range poses {
bvh.Find(TouchPoint[Vec2d, AABBVec2d](v), func(v any) {}) bvh.Find(TouchPoint[Vec2d, AABBVec2d](v), func(n *Node[float64, AABBVec2d, any]) bool { return true })
} }
} }