dim manager test 2
This commit is contained in:
73
server/clientinfo/clientinfo.go
Normal file
73
server/clientinfo/clientinfo.go
Normal 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) {}
|
@ -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,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
7
server/dimension/loadmanager_test.go
Normal file
7
server/dimension/loadmanager_test.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package dimension
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func Test_manager(t *testing.T) {
|
||||||
|
|
||||||
|
}
|
@ -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 }
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user