<commit_before>package player

import (
    "bytes"
    "sync"

    .   "chunkymonkey/interfaces"
    "chunkymonkey/proto"
    .   "chunkymonkey/types"
)

// Keeps track of chunks that the player is subscribed to, and adds/removes the
// player from chunks as they move around.
// TODO implement the movement add/remove tracking
type chunkSubscriptions struct {
    player *Player
    chunks map[IChunk]bool
}

func (cs *chunkSubscriptions) init(player *Player) {
    cs.player = player
    cs.chunks = make(map[IChunk]bool)
}

// Subscribes to all chunks in MinChunkRadius, then calls nearbySent(), then
// subscribes to the remaining chunks out to ChunkRadius. Note that the bulk of
// this work happens in a seperate goroutine.
func (cs *chunkSubscriptions) subscribeFresh(nearbySent func()) {
    playerChunkLoc := cs.player.position.ToChunkXZ()

    // Work out order to send chunks in. (Nearest to player first)
    chunkLocOrder := chunkOrder(ChunkRadius, playerChunkLoc)

    // Get all the chunks together that we're going to send.
    chunksChan := make(chan []IChunk)
    cs.player.game.Enqueue(func(game IGame) {
        chunks := make([]IChunk, 0, len(chunkLocOrder))
        mgr := cs.player.game.GetChunkManager()
        for _, chunkLoc := range chunkLocOrder {
            chunk := mgr.Get(&chunkLoc)
            if chunk != nil {
                chunks = append(chunks, chunk)
            }
        }
        chunksChan <- chunks
    })
    chunks := <-chunksChan

    // Remember the chunks that we're going to be subscribed to.
    for _, chunk := range chunks {
        cs.chunks[chunk] = true
    }

    // Send all chunks in the background.
    go func() {
        // Warn client to allocate memory for the chunks we're sending.
        buf := &bytes.Buffer{}
        for _, chunk := range chunks {
            proto.WritePreChunk(buf, chunk.GetLoc(), ChunkInit)
        }
        cs.player.TransmitPacket(buf.Bytes())

        // Send the important chunks.
        chunksSent := 0
        waitChunks := &sync.WaitGroup{}
        for _, chunk := range chunks {
            chunkLoc := chunk.GetLoc()
            dx := (chunkLoc.X - playerChunkLoc.X).Abs()
            dz := (chunkLoc.Z - playerChunkLoc.Z).Abs()
            if dx > MinChunkRadius || dz > MinChunkRadius {
                // This chunk isn't important, wait until after other login data
                // has been send before sending this and chunks following.
                break
            }
            chunksSent++
            waitChunks.Add(1)
            chunk.Enqueue(func(chunk IChunk) {
                chunk.AddSubscriber(cs.player)
                waitChunks.Done()
            })
        }
        waitChunks.Wait()

        nearbySent()

        // Send the remaining chunks. We don't need to wait for these to complete
        // before continuing with talking to the client.
        for _, chunk := range chunks[chunksSent:] {
            chunk.Enqueue(func(chunk IChunk) {
                chunk.AddSubscriber(cs.player)
            })
        }
    }()
}

// Removes all subscriptions to chunks.
func (cs *chunkSubscriptions) clear() {
    for chunk := range cs.chunks {
        chunk.RemoveSubscriber(cs.player)
    }
}

func chunkOrder(radius ChunkCoord, center *ChunkXZ) (locs []ChunkXZ) {
    areaEdgeSize := radius*2 + 1
    locs = make([]ChunkXZ, areaEdgeSize*areaEdgeSize)
    locs[0] = *center
    index := 1
    for curRadius := ChunkCoord(1); curRadius <= radius; curRadius++ {
        xMin := ChunkCoord(-curRadius + center.X)<commit_msg>Refactored player chunk subscription to enable accessing chunk by X,Z.<commit_after>package player

import (
    "bytes"
    "sync"

    .   "chunkymonkey/interfaces"
    "chunkymonkey/proto"
    .   "chunkymonkey/types"
)

// Keeps track of chunks that the player is subscribed to, and adds/removes the
// player from chunks as they move around.
// TODO implement the movement add/remove tracking
type chunkSubscriptions struct {
    player *Player
    chunks map[uint64]IChunk // Map from ChunkKeys to chunks
}

func (cs *chunkSubscriptions) init(player *Player) {
    cs.player = player
    cs.chunks = make(map[uint64]IChunk)
}

// Subscribes to all chunks in MinChunkRadius, then calls nearbySent(), then
// subscribes to the remaining chunks out to ChunkRadius. Note that the bulk of
// this work happens in a seperate goroutine.
func (cs *chunkSubscriptions) subscribeFresh(nearbySent func()) {
    playerChunkLoc := cs.player.position.ToChunkXZ()

    // Work out order to send chunks in. (Nearest to player first)
    chunkLocOrder := chunkOrder(ChunkRadius, playerChunkLoc)

    // Get all the chunks together that we're going to send.
    chunksChan := make(chan []IChunk)
    cs.player.game.Enqueue(func(game IGame) {
        chunks := make([]IChunk, 0, len(chunkLocOrder))
        mgr := cs.player.game.GetChunkManager()
        for _, chunkLoc := range chunkLocOrder {
            chunk := mgr.Get(&chunkLoc)
            if chunk != nil {
                chunks = append(chunks, chunk)
            }
        }
        chunksChan <- chunks
    })
    chunks := <-chunksChan

    // Remember the chunks that we're going to be subscribed to.
    for _, chunk := range chunks {
        cs.chunks[chunk.GetLoc().ChunkKey()] = chunk
    }

    // Send all chunks in the background.
    go func() {
        // Warn client to allocate memory for the chunks we're sending.
        buf := &bytes.Buffer{}
        for _, chunk := range chunks {
            proto.WritePreChunk(buf, chunk.GetLoc(), ChunkInit)
        }
        cs.player.TransmitPacket(buf.Bytes())

        // Send the important chunks.
        chunksSent := 0
        waitChunks := &sync.WaitGroup{}
        for _, chunk := range chunks {
            chunkLoc := chunk.GetLoc()
            dx := (chunkLoc.X - playerChunkLoc.X).Abs()
            dz := (chunkLoc.Z - playerChunkLoc.Z).Abs()
            if dx > MinChunkRadius || dz > MinChunkRadius {
                // This chunk isn't important, wait until after other login data
                // has been sent before sending this and chunks following.
                break
            }
            chunksSent++
            waitChunks.Add(1)
            chunk.Enqueue(func(chunk IChunk) {
                chunk.AddSubscriber(cs.player)
                waitChunks.Done()
            })
        }
        waitChunks.Wait()

        nearbySent()

        // Send the remaining chunks. We don't need to wait for these to complete
        // before continuing with talking to the client.
        for _, chunk := range chunks[chunksSent:] {
            chunk.Enqueue(func(chunk IChunk) {
                chunk.AddSubscriber(cs.player)
            })
        }
    }()
}

// Removes all subscriptions to chunks.
func (cs *chunkSubscriptions) clear() {
    for _, chunk := range cs.chunks {
        chunk.RemoveSubscriber(cs.player)
    }
}

func chunkOrder(radius ChunkCoord, center *ChunkXZ) (locs []ChunkXZ) {
    areaEdgeSize := radius*2 + 1
    locs = make([]ChunkXZ, areaEdgeSize*areaEdgeSize)
    locs[0] = *center
    index := 1
    for curRadius := ChunkCoord(1); curRadius <= radius; curRadius++ {
        xMin := ChunkCoord(-curRadius + center.X)
{'fraction_non_alphanumeric': 0.10355315913175722, 'fraction_numerical': 0.0027651043826904466, 'mean_word_length': 2.1952296819787986, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '664037', 'n_tokens_mistral': 2092, 'n_tokens_neox': 2036, 'n_words': 617}