github.com/df-mc/dragonfly@v0.9.13/server/world/world.go (about)

     1  package world
     2  
     3  import (
     4  	"errors"
     5  	"github.com/df-mc/goleveldb/leveldb"
     6  	"math/rand"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/df-mc/atomic"
    11  	"github.com/df-mc/dragonfly/server/block/cube"
    12  	"github.com/df-mc/dragonfly/server/event"
    13  	"github.com/df-mc/dragonfly/server/internal/sliceutil"
    14  	"github.com/df-mc/dragonfly/server/world/chunk"
    15  	"github.com/go-gl/mathgl/mgl64"
    16  	"github.com/google/uuid"
    17  	"golang.org/x/exp/maps"
    18  	"slices"
    19  )
    20  
    21  // World implements a Minecraft world. It manages all aspects of what players can see, such as blocks,
    22  // entities and particles.
    23  // World generally provides a synchronised state: All entities, blocks and players usually operate in this
    24  // world, so World ensures that all its methods will always be safe for simultaneous calls.
    25  // A nil *World is safe to use but not functional.
    26  type World struct {
    27  	conf Config
    28  	ra   cube.Range
    29  	// advance is a bool that specifies if this World should advance the current tick, time and weather saved in the
    30  	// Settings struct held by the World.
    31  	advance bool
    32  
    33  	o sync.Once
    34  
    35  	set     *Settings
    36  	handler atomic.Value[Handler]
    37  
    38  	weather
    39  	ticker
    40  
    41  	lastPos   ChunkPos
    42  	lastChunk *Column
    43  
    44  	closing chan struct{}
    45  	running sync.WaitGroup
    46  
    47  	chunkMu sync.Mutex
    48  	// chunks holds a cache of chunks currently loaded. These chunks are cleared from this map after some time
    49  	// of not being used.
    50  	chunks map[ChunkPos]*Column
    51  
    52  	entityMu sync.RWMutex
    53  	// entities holds a map of entities currently loaded and the last ChunkPos that the Entity was in.
    54  	// These are tracked so that a call to RemoveEntity can find the correct entity.
    55  	entities map[Entity]ChunkPos
    56  
    57  	r *rand.Rand
    58  
    59  	updateMu sync.Mutex
    60  	// scheduledUpdates is a map of tick time values indexed by the block position at which an update is
    61  	// scheduled. If the current tick exceeds the tick value passed, the block update will be performed
    62  	// and the entry will be removed from the map.
    63  	scheduledUpdates map[cube.Pos]int64
    64  	neighbourUpdates []neighbourUpdate
    65  
    66  	viewersMu sync.Mutex
    67  	viewers   map[*Loader]Viewer
    68  }
    69  
    70  // New creates a new initialised world. The world may be used right away, but it will not be saved or loaded
    71  // from files until it has been given a different provider than the default. (NopProvider)
    72  // By default, the name of the world will be 'World'.
    73  func New() *World {
    74  	var conf Config
    75  	return conf.New()
    76  }
    77  
    78  // Name returns the display name of the world. Generally, this name is displayed at the top of the player list
    79  // in the pause screen in-game.
    80  // If a provider is set, the name will be updated according to the name that it provides.
    81  func (w *World) Name() string {
    82  	w.set.Lock()
    83  	defer w.set.Unlock()
    84  	return w.set.Name
    85  }
    86  
    87  // Dimension returns the Dimension assigned to the World in world.New. The sky colour and behaviour of a variety of
    88  // world features differ based on the Dimension assigned to a World.
    89  func (w *World) Dimension() Dimension {
    90  	if w == nil {
    91  		return nopDim{}
    92  	}
    93  	return w.conf.Dim
    94  }
    95  
    96  // Range returns the range in blocks of the World (min and max). It is equivalent to calling World.Dimension().Range().
    97  func (w *World) Range() cube.Range {
    98  	if w == nil {
    99  		return cube.Range{}
   100  	}
   101  	return w.ra
   102  }
   103  
   104  // EntityRegistry returns the EntityRegistry that was passed to the World's
   105  // Config upon construction.
   106  func (w *World) EntityRegistry() EntityRegistry {
   107  	return w.conf.Entities
   108  }
   109  
   110  // Block reads a block from the position passed. If a chunk is not yet loaded at that position, the chunk is
   111  // loaded, or generated if it could not be found in the world save, and the block returned. Chunks will be
   112  // loaded synchronously.
   113  func (w *World) Block(pos cube.Pos) Block {
   114  	if w == nil || pos.OutOfBounds(w.Range()) {
   115  		// Fast way out.
   116  		return air()
   117  	}
   118  	c := w.chunk(chunkPosFromBlockPos(pos))
   119  	defer c.Unlock()
   120  
   121  	rid := c.Block(uint8(pos[0]), int16(pos[1]), uint8(pos[2]), 0)
   122  	if nbtBlocks[rid] {
   123  		// The block was also a block entity, so we look it up in the block entity map.
   124  		if nbtB, ok := c.BlockEntities[pos]; ok {
   125  			return nbtB
   126  		}
   127  	}
   128  	b, _ := BlockByRuntimeID(rid)
   129  	return b
   130  }
   131  
   132  // Biome reads the biome at the position passed. If a chunk is not yet loaded at that position, the chunk is
   133  // loaded, or generated if it could not be found in the world save, and the biome returned. Chunks will be
   134  // loaded synchronously.
   135  func (w *World) Biome(pos cube.Pos) Biome {
   136  	if w == nil || pos.OutOfBounds(w.Range()) {
   137  		// Fast way out.
   138  		return ocean()
   139  	}
   140  	c := w.chunk(chunkPosFromBlockPos(pos))
   141  	defer c.Unlock()
   142  
   143  	id := int(c.Biome(uint8(pos[0]), int16(pos[1]), uint8(pos[2])))
   144  	b, ok := BiomeByID(id)
   145  	if !ok {
   146  		w.conf.Log.Errorf("could not find biome by ID %v", id)
   147  	}
   148  	return b
   149  }
   150  
   151  // blockInChunk reads a block from the world at the position passed. The block is assumed to be in the chunk
   152  // passed, which is also assumed to be locked already or otherwise not yet accessible.
   153  func (w *World) blockInChunk(c *Column, pos cube.Pos) Block {
   154  	if pos.OutOfBounds(w.Range()) {
   155  		// Fast way out.
   156  		return air()
   157  	}
   158  	rid := c.Block(uint8(pos[0]), int16(pos[1]), uint8(pos[2]), 0)
   159  	if nbtBlocks[rid] {
   160  		// The block was also a block entity, so we look it up in the block entity map.
   161  		if b, ok := c.BlockEntities[pos]; ok {
   162  			return b
   163  		}
   164  	}
   165  	b, _ := BlockByRuntimeID(rid)
   166  	return b
   167  }
   168  
   169  // HighestLightBlocker gets the Y value of the highest fully light blocking block at the x and z values
   170  // passed in the world.
   171  func (w *World) HighestLightBlocker(x, z int) int {
   172  	if w == nil {
   173  		return w.Range()[0]
   174  	}
   175  	c := w.chunk(ChunkPos{int32(x >> 4), int32(z >> 4)})
   176  	defer c.Unlock()
   177  	return int(c.HighestLightBlocker(uint8(x), uint8(z)))
   178  }
   179  
   180  // HighestBlock looks up the highest non-air block in the world at a specific x and z in the world. The y
   181  // value of the highest block is returned, or 0 if no blocks were present in the column.
   182  func (w *World) HighestBlock(x, z int) int {
   183  	if w == nil {
   184  		return w.Range()[0]
   185  	}
   186  	c := w.chunk(ChunkPos{int32(x >> 4), int32(z >> 4)})
   187  	defer c.Unlock()
   188  	return int(c.HighestBlock(uint8(x), uint8(z)))
   189  }
   190  
   191  // highestObstructingBlock returns the highest block in the world at a given x and z that has at least a solid top or
   192  // bottom face.
   193  func (w *World) highestObstructingBlock(x, z int) int {
   194  	if w == nil {
   195  		return 0
   196  	}
   197  	yHigh := w.HighestBlock(x, z)
   198  	for y := yHigh; y >= w.Range()[0]; y-- {
   199  		pos := cube.Pos{x, y, z}
   200  		m := w.Block(pos).Model()
   201  		if m.FaceSolid(pos, cube.FaceUp, w) || m.FaceSolid(pos, cube.FaceDown, w) {
   202  			return y
   203  		}
   204  	}
   205  	return w.Range()[0]
   206  }
   207  
   208  // SetOpts holds several parameters that may be set to disable updates in the World of different kinds as a result of
   209  // a call to SetBlock.
   210  type SetOpts struct {
   211  	// DisableBlockUpdates makes SetBlock not update any neighbouring blocks as a result of the SetBlock call.
   212  	DisableBlockUpdates bool
   213  	// DisableLiquidDisplacement disables the displacement of liquid blocks to the second layer (or back to the first
   214  	// layer, if it already was on the second layer). Disabling this is not strongly recommended unless performance is
   215  	// very important or where it is known no liquid can be present anyway.
   216  	DisableLiquidDisplacement bool
   217  }
   218  
   219  // SetBlock writes a block to the position passed. If a chunk is not yet loaded at that position, the chunk is
   220  // first loaded or generated if it could not be found in the world save.
   221  // SetBlock panics if the block passed has not yet been registered using RegisterBlock().
   222  // Nil may be passed as the block to set the block to air.
   223  //
   224  // A SetOpts struct may be passed to additionally modify behaviour of SetBlock, specifically to improve performance
   225  // under specific circumstances. Nil should be passed where performance is not essential, to make sure the world is
   226  // updated adequately.
   227  //
   228  // SetBlock should be avoided in situations where performance is critical when needing to set a lot of blocks
   229  // to the world. BuildStructure may be used instead.
   230  func (w *World) SetBlock(pos cube.Pos, b Block, opts *SetOpts) {
   231  	if w == nil || pos.OutOfBounds(w.Range()) {
   232  		// Fast way out.
   233  		return
   234  	}
   235  	if opts == nil {
   236  		opts = &SetOpts{}
   237  	}
   238  
   239  	x, y, z := uint8(pos[0]), int16(pos[1]), uint8(pos[2])
   240  	c := w.chunk(chunkPosFromBlockPos(pos))
   241  
   242  	rid := BlockRuntimeID(b)
   243  
   244  	var before uint32
   245  	if rid != airRID && !opts.DisableLiquidDisplacement {
   246  		before = c.Block(x, y, z, 0)
   247  	}
   248  
   249  	c.modified = true
   250  	c.SetBlock(x, y, z, 0, rid)
   251  	if nbtBlocks[rid] {
   252  		c.BlockEntities[pos] = b
   253  	} else {
   254  		delete(c.BlockEntities, pos)
   255  	}
   256  
   257  	viewers := slices.Clone(c.viewers)
   258  
   259  	if !opts.DisableLiquidDisplacement {
   260  		var secondLayer Block
   261  
   262  		if rid == airRID {
   263  			if li := c.Block(x, y, z, 1); li != airRID {
   264  				c.SetBlock(x, y, z, 0, li)
   265  				c.SetBlock(x, y, z, 1, airRID)
   266  				secondLayer = air()
   267  				b, _ = BlockByRuntimeID(li)
   268  			}
   269  		} else if liquidDisplacingBlocks[rid] && liquidBlocks[before] {
   270  			l, _ := BlockByRuntimeID(before)
   271  			if b.(LiquidDisplacer).CanDisplace(l.(Liquid)) {
   272  				c.SetBlock(x, y, z, 1, before)
   273  				secondLayer = l
   274  			}
   275  		}
   276  		c.Unlock()
   277  
   278  		if secondLayer != nil {
   279  			for _, viewer := range viewers {
   280  				viewer.ViewBlockUpdate(pos, secondLayer, 1)
   281  			}
   282  		}
   283  	} else {
   284  		c.Unlock()
   285  	}
   286  
   287  	for _, viewer := range viewers {
   288  		viewer.ViewBlockUpdate(pos, b, 0)
   289  	}
   290  
   291  	if !opts.DisableBlockUpdates {
   292  		w.doBlockUpdatesAround(pos)
   293  	}
   294  }
   295  
   296  // SetBiome sets the biome at the position passed. If a chunk is not yet loaded at that position, the chunk is
   297  // first loaded or generated if it could not be found in the world save.
   298  func (w *World) SetBiome(pos cube.Pos, b Biome) {
   299  	if w == nil || pos.OutOfBounds(w.Range()) {
   300  		// Fast way out.
   301  		return
   302  	}
   303  	c := w.chunk(chunkPosFromBlockPos(pos))
   304  	defer c.Unlock()
   305  
   306  	c.modified = true
   307  	c.SetBiome(uint8(pos[0]), int16(pos[1]), uint8(pos[2]), uint32(b.EncodeBiome()))
   308  }
   309  
   310  // BuildStructure builds a Structure passed at a specific position in the world. Unlike SetBlock, it takes a
   311  // Structure implementation, which provides blocks to be placed at a specific location.
   312  // BuildStructure is specifically tinkered to be able to process a large batch of chunks simultaneously and
   313  // will do so within much less time than separate SetBlock calls would.
   314  // The method operates on a per-chunk basis, setting all blocks within a single chunk part of the structure
   315  // before moving on to the next chunk.
   316  func (w *World) BuildStructure(pos cube.Pos, s Structure) {
   317  	if w == nil {
   318  		return
   319  	}
   320  	dim := s.Dimensions()
   321  	width, height, length := dim[0], dim[1], dim[2]
   322  	maxX, maxY, maxZ := pos[0]+width, pos[1]+height, pos[2]+length
   323  
   324  	for chunkX := pos[0] >> 4; chunkX <= maxX>>4; chunkX++ {
   325  		for chunkZ := pos[2] >> 4; chunkZ <= maxZ>>4; chunkZ++ {
   326  			// We approach this on a per-chunk basis, so that we can keep only one chunk in memory at a time
   327  			// while not needing to acquire a new chunk lock for every block. This also allows us not to send
   328  			// block updates, but instead send a single chunk update once.
   329  			chunkPos := ChunkPos{int32(chunkX), int32(chunkZ)}
   330  			c := w.chunk(chunkPos)
   331  			f := func(x, y, z int) Block {
   332  				actual := cube.Pos{pos[0] + x, pos[1] + y, pos[2] + z}
   333  				if actual[0]>>4 == chunkX && actual[2]>>4 == chunkZ {
   334  					return w.blockInChunk(c, actual)
   335  				}
   336  				return w.Block(actual)
   337  			}
   338  			baseX, baseZ := chunkX<<4, chunkZ<<4
   339  			subs := c.Sub()
   340  			for i, sub := range subs {
   341  				baseY := (i + (w.Range()[0] >> 4)) << 4
   342  				if baseY>>4 < pos[1]>>4 {
   343  					continue
   344  				} else if baseY >= maxY {
   345  					break
   346  				}
   347  
   348  				for localY := 0; localY < 16; localY++ {
   349  					yOffset := baseY + localY
   350  					if yOffset > w.Range()[1] || yOffset >= maxY {
   351  						// We've hit the height limit for blocks.
   352  						break
   353  					} else if yOffset < w.Range()[0] || yOffset < pos[1] {
   354  						// We've got a block below the minimum, but other blocks might still reach above
   355  						// it, so don't break but continue.
   356  						continue
   357  					}
   358  					for localX := 0; localX < 16; localX++ {
   359  						xOffset := baseX + localX
   360  						if xOffset < pos[0] || xOffset >= maxX {
   361  							continue
   362  						}
   363  						for localZ := 0; localZ < 16; localZ++ {
   364  							zOffset := baseZ + localZ
   365  							if zOffset < pos[2] || zOffset >= maxZ {
   366  								continue
   367  							}
   368  							b, liq := s.At(xOffset-pos[0], yOffset-pos[1], zOffset-pos[2], f)
   369  							if b != nil {
   370  								rid := BlockRuntimeID(b)
   371  								sub.SetBlock(uint8(xOffset), uint8(yOffset), uint8(zOffset), 0, rid)
   372  
   373  								nbtPos := cube.Pos{xOffset, yOffset, zOffset}
   374  								if nbtBlocks[rid] {
   375  									c.BlockEntities[nbtPos] = b
   376  								} else {
   377  									delete(c.BlockEntities, nbtPos)
   378  								}
   379  							}
   380  							if liq != nil {
   381  								sub.SetBlock(uint8(xOffset), uint8(yOffset), uint8(zOffset), 1, BlockRuntimeID(liq))
   382  							} else if len(sub.Layers()) > 1 {
   383  								sub.SetBlock(uint8(xOffset), uint8(yOffset), uint8(zOffset), 1, airRID)
   384  							}
   385  						}
   386  					}
   387  				}
   388  			}
   389  			c.SetBlock(0, 0, 0, 0, c.Block(0, 0, 0, 0)) // Make sure the heightmap is recalculated.
   390  			c.modified = true
   391  
   392  			// After setting all blocks of the structure within a single chunk, we show the new chunk to all
   393  			// viewers once, and unlock it.
   394  			for _, viewer := range c.viewers {
   395  				viewer.ViewChunk(chunkPos, c.Chunk, c.BlockEntities)
   396  			}
   397  			c.Unlock()
   398  		}
   399  	}
   400  }
   401  
   402  // Liquid attempts to return any liquid block at the position passed. This liquid may be in the foreground or
   403  // in any other layer.
   404  // If found, the liquid is returned. If not, the bool returned is false and the liquid is nil.
   405  func (w *World) Liquid(pos cube.Pos) (Liquid, bool) {
   406  	if w == nil || pos.OutOfBounds(w.Range()) {
   407  		// Fast way out.
   408  		return nil, false
   409  	}
   410  	c := w.chunk(chunkPosFromBlockPos(pos))
   411  	defer c.Unlock()
   412  	x, y, z := uint8(pos[0]), int16(pos[1]), uint8(pos[2])
   413  
   414  	id := c.Block(x, y, z, 0)
   415  	b, ok := BlockByRuntimeID(id)
   416  	if !ok {
   417  		w.conf.Log.Errorf("failed getting liquid: cannot get block by runtime ID %v", id)
   418  		return nil, false
   419  	}
   420  	if liq, ok := b.(Liquid); ok {
   421  		return liq, true
   422  	}
   423  	id = c.Block(x, y, z, 1)
   424  
   425  	b, ok = BlockByRuntimeID(id)
   426  	if !ok {
   427  		w.conf.Log.Errorf("failed getting liquid: cannot get block by runtime ID %v", id)
   428  		return nil, false
   429  	}
   430  	liq, ok := b.(Liquid)
   431  	return liq, ok
   432  }
   433  
   434  // SetLiquid sets the liquid at a specific position in the world. Unlike SetBlock, SetLiquid will not
   435  // overwrite any existing blocks. It will instead be in the same position as a block currently there, unless
   436  // there already is a liquid at that position, in which case it will be overwritten.
   437  // If nil is passed for the liquid, any liquid currently present will be removed.
   438  func (w *World) SetLiquid(pos cube.Pos, b Liquid) {
   439  	if w == nil || pos.OutOfBounds(w.Range()) {
   440  		// Fast way out.
   441  		return
   442  	}
   443  	chunkPos := chunkPosFromBlockPos(pos)
   444  	c := w.chunk(chunkPos)
   445  	if b == nil {
   446  		w.removeLiquids(c, pos)
   447  		c.Unlock()
   448  		w.doBlockUpdatesAround(pos)
   449  		return
   450  	}
   451  	x, y, z := uint8(pos[0]), int16(pos[1]), uint8(pos[2])
   452  	if !replaceable(w, c, pos, b) {
   453  		if displacer, ok := w.blockInChunk(c, pos).(LiquidDisplacer); !ok || !displacer.CanDisplace(b) {
   454  			c.Unlock()
   455  			return
   456  		}
   457  	}
   458  	rid := BlockRuntimeID(b)
   459  	if w.removeLiquids(c, pos) {
   460  		c.SetBlock(x, y, z, 0, rid)
   461  		for _, v := range c.viewers {
   462  			v.ViewBlockUpdate(pos, b, 0)
   463  		}
   464  	} else {
   465  		c.SetBlock(x, y, z, 1, rid)
   466  		for _, v := range c.viewers {
   467  			v.ViewBlockUpdate(pos, b, 1)
   468  		}
   469  	}
   470  	c.modified = true
   471  	c.Unlock()
   472  
   473  	w.doBlockUpdatesAround(pos)
   474  }
   475  
   476  // removeLiquids removes any liquid blocks that may be present at a specific block position in the chunk
   477  // passed.
   478  // The bool returned specifies if no blocks were left on the foreground layer.
   479  func (w *World) removeLiquids(c *Column, pos cube.Pos) bool {
   480  	x, y, z := uint8(pos[0]), int16(pos[1]), uint8(pos[2])
   481  
   482  	noneLeft := false
   483  	if noLeft, changed := w.removeLiquidOnLayer(c.Chunk, x, y, z, 0); noLeft {
   484  		if changed {
   485  			for _, v := range c.viewers {
   486  				v.ViewBlockUpdate(pos, air(), 0)
   487  			}
   488  		}
   489  		noneLeft = true
   490  	}
   491  	if _, changed := w.removeLiquidOnLayer(c.Chunk, x, y, z, 1); changed {
   492  		for _, v := range c.viewers {
   493  			v.ViewBlockUpdate(pos, air(), 1)
   494  		}
   495  	}
   496  	return noneLeft
   497  }
   498  
   499  // removeLiquidOnLayer removes a liquid block from a specific layer in the chunk passed, returning true if
   500  // successful.
   501  func (w *World) removeLiquidOnLayer(c *chunk.Chunk, x uint8, y int16, z, layer uint8) (bool, bool) {
   502  	id := c.Block(x, y, z, layer)
   503  
   504  	b, ok := BlockByRuntimeID(id)
   505  	if !ok {
   506  		w.conf.Log.Errorf("failed removing liquids: cannot get block by runtime ID %v", id)
   507  		return false, false
   508  	}
   509  	if _, ok := b.(Liquid); ok {
   510  		c.SetBlock(x, y, z, layer, airRID)
   511  		return true, true
   512  	}
   513  	return id == airRID, false
   514  }
   515  
   516  // additionalLiquid checks if the block at a position has additional liquid on another layer and returns the
   517  // liquid if so.
   518  func (w *World) additionalLiquid(pos cube.Pos) (Liquid, bool) {
   519  	if pos.OutOfBounds(w.Range()) {
   520  		// Fast way out.
   521  		return nil, false
   522  	}
   523  	c := w.chunk(chunkPosFromBlockPos(pos))
   524  	id := c.Block(uint8(pos[0]), int16(pos[1]), uint8(pos[2]), 1)
   525  	c.Unlock()
   526  	b, ok := BlockByRuntimeID(id)
   527  	if !ok {
   528  		w.conf.Log.Errorf("failed getting liquid: cannot get block by runtime ID %v", id)
   529  		return nil, false
   530  	}
   531  	liq, ok := b.(Liquid)
   532  	return liq, ok
   533  }
   534  
   535  // Light returns the light level at the position passed. This is the highest of the sky and block light.
   536  // The light value returned is a value in the range 0-15, where 0 means there is no light present, whereas
   537  // 15 means the block is fully lit.
   538  func (w *World) Light(pos cube.Pos) uint8 {
   539  	if w == nil || pos[1] < w.Range()[0] {
   540  		// Fast way out.
   541  		return 0
   542  	}
   543  	if pos[1] > w.Range()[1] {
   544  		// Above the rest of the world, so full skylight.
   545  		return 15
   546  	}
   547  	c := w.chunk(chunkPosFromBlockPos(pos))
   548  	defer c.Unlock()
   549  	return c.Light(uint8(pos[0]), int16(pos[1]), uint8(pos[2]))
   550  }
   551  
   552  // SkyLight returns the skylight level at the position passed. This light level is not influenced by blocks
   553  // that emit light, such as torches or glowstone. The light value, similarly to Light, is a value in the
   554  // range 0-15, where 0 means no light is present.
   555  func (w *World) SkyLight(pos cube.Pos) uint8 {
   556  	if w == nil || pos[1] < w.Range()[0] {
   557  		// Fast way out.
   558  		return 0
   559  	}
   560  	if pos[1] > w.Range()[1] {
   561  		// Above the rest of the world, so full skylight.
   562  		return 15
   563  	}
   564  	c := w.chunk(chunkPosFromBlockPos(pos))
   565  	defer c.Unlock()
   566  	return c.SkyLight(uint8(pos[0]), int16(pos[1]), uint8(pos[2]))
   567  }
   568  
   569  // Time returns the current time of the world. The time is incremented every 1/20th of a second, unless
   570  // World.StopTime() is called.
   571  func (w *World) Time() int {
   572  	if w == nil {
   573  		return 0
   574  	}
   575  	w.set.Lock()
   576  	defer w.set.Unlock()
   577  	return int(w.set.Time)
   578  }
   579  
   580  // SetTime sets the new time of the world. SetTime will always work, regardless of whether the time is stopped
   581  // or not.
   582  func (w *World) SetTime(new int) {
   583  	if w == nil {
   584  		return
   585  	}
   586  	w.set.Lock()
   587  	w.set.Time = int64(new)
   588  	w.set.Unlock()
   589  
   590  	viewers, _ := w.allViewers()
   591  	for _, viewer := range viewers {
   592  		viewer.ViewTime(new)
   593  	}
   594  }
   595  
   596  // StopTime stops the time in the world. When called, the time will no longer cycle and the world will remain
   597  // at the time when StopTime is called. The time may be restarted by calling World.StartTime().
   598  // StopTime will not do anything if the time is already stopped.
   599  func (w *World) StopTime() {
   600  	w.enableTimeCycle(false)
   601  }
   602  
   603  // StartTime restarts the time in the world. When called, the time will start cycling again and the day/night
   604  // cycle will continue. The time may be stopped again by calling World.StopTime().
   605  // StartTime will not do anything if the time is already started.
   606  func (w *World) StartTime() {
   607  	w.enableTimeCycle(true)
   608  }
   609  
   610  // enableTimeCycle enables or disables the time cycling of the World.
   611  func (w *World) enableTimeCycle(v bool) {
   612  	if w == nil {
   613  		return
   614  	}
   615  	w.set.Lock()
   616  	defer w.set.Unlock()
   617  	w.set.TimeCycle = v
   618  }
   619  
   620  // Temperature returns the temperature in the World at a specific position. Higher altitudes and different biomes
   621  // influence the temperature returned.
   622  func (w *World) Temperature(pos cube.Pos) float64 {
   623  	const (
   624  		tempDrop = 1.0 / 600
   625  		seaLevel = 64
   626  	)
   627  	diff := pos[1] - seaLevel
   628  	if diff < 0 {
   629  		diff = 0
   630  	}
   631  	return w.Biome(pos).Temperature() - float64(diff)*tempDrop
   632  }
   633  
   634  // AddParticle spawns a particle at a given position in the world. Viewers that are viewing the chunk will be
   635  // shown the particle.
   636  func (w *World) AddParticle(pos mgl64.Vec3, p Particle) {
   637  	if w == nil {
   638  		return
   639  	}
   640  	p.Spawn(w, pos)
   641  	for _, viewer := range w.Viewers(pos) {
   642  		viewer.ViewParticle(pos, p)
   643  	}
   644  }
   645  
   646  // PlaySound plays a sound at a specific position in the world. Viewers of that position will be able to hear
   647  // the sound if they're close enough.
   648  func (w *World) PlaySound(pos mgl64.Vec3, s Sound) {
   649  	ctx := event.C()
   650  	if w.Handler().HandleSound(ctx, s, pos); ctx.Cancelled() {
   651  		return
   652  	}
   653  	for _, viewer := range w.Viewers(pos) {
   654  		viewer.ViewSound(pos, s)
   655  	}
   656  }
   657  
   658  var (
   659  	worldsMu sync.RWMutex
   660  	// entityWorlds holds a list of all entities added to a world. It may be used to look up the world that an
   661  	// entity is currently in.
   662  	entityWorlds = map[Entity]*World{}
   663  )
   664  
   665  // AddEntity adds an entity to the world at the position that the entity has. The entity will be visible to
   666  // all viewers of the world that have the chunk of the entity loaded.
   667  // If the chunk that the entity is in is not yet loaded, it will first be loaded.
   668  // If the entity passed to AddEntity is currently in a world, it is first removed from that world.
   669  func (w *World) AddEntity(e Entity) {
   670  	if w == nil {
   671  		return
   672  	}
   673  
   674  	// Remove the Entity from any previous World it might be in.
   675  	e.World().RemoveEntity(e)
   676  
   677  	add(e, w)
   678  
   679  	chunkPos := chunkPosFromVec3(e.Position())
   680  	w.entityMu.Lock()
   681  	w.entities[e] = chunkPos
   682  	w.entityMu.Unlock()
   683  
   684  	c := w.chunk(chunkPos)
   685  	c.Entities = append(c.Entities, e)
   686  	viewers := slices.Clone(c.viewers)
   687  	c.Unlock()
   688  
   689  	for _, v := range viewers {
   690  		// We show the entity to all viewers currently in the chunk that the entity is spawned in.
   691  		showEntity(e, v)
   692  	}
   693  
   694  	w.Handler().HandleEntitySpawn(e)
   695  }
   696  
   697  // add maps an Entity to a World in the entityWorlds map.
   698  func add(e Entity, w *World) {
   699  	worldsMu.Lock()
   700  	entityWorlds[e] = w
   701  	worldsMu.Unlock()
   702  }
   703  
   704  // RemoveEntity removes an entity from the world that is currently present in it. Any viewers of the entity
   705  // will no longer be able to see it.
   706  // RemoveEntity operates assuming the position of the entity is the same as where it is currently in the
   707  // world. If it can not find it there, it will loop through all entities and try to find it.
   708  // RemoveEntity assumes the entity is currently loaded and in a loaded chunk. If not, the function will not do
   709  // anything.
   710  func (w *World) RemoveEntity(e Entity) {
   711  	if w == nil {
   712  		return
   713  	}
   714  	w.entityMu.Lock()
   715  	chunkPos, found := w.entities[e]
   716  	w.entityMu.Unlock()
   717  	if !found {
   718  		// The entity currently isn't in this world.
   719  		return
   720  	}
   721  
   722  	w.Handler().HandleEntityDespawn(e)
   723  
   724  	worldsMu.Lock()
   725  	delete(entityWorlds, e)
   726  	worldsMu.Unlock()
   727  
   728  	c, ok := w.chunkFromCache(chunkPos)
   729  	if !ok {
   730  		// The chunk wasn't loaded, so we can't remove any entity from the chunk.
   731  		return
   732  	}
   733  	c.Entities = sliceutil.DeleteVal(c.Entities, e)
   734  	viewers := slices.Clone(c.viewers)
   735  	c.Unlock()
   736  
   737  	w.entityMu.Lock()
   738  	delete(w.entities, e)
   739  	w.entityMu.Unlock()
   740  
   741  	for _, v := range viewers {
   742  		v.HideEntity(e)
   743  	}
   744  }
   745  
   746  // EntitiesWithin does a lookup through the entities in the chunks touched by the BBox passed, returning all
   747  // those which are contained within the BBox when it comes to their position.
   748  func (w *World) EntitiesWithin(box cube.BBox, ignored func(Entity) bool) []Entity {
   749  	if w == nil {
   750  		return nil
   751  	}
   752  	// Make an estimate of 16 entities on average.
   753  	m := make([]Entity, 0, 16)
   754  
   755  	minPos, maxPos := chunkPosFromVec3(box.Min()), chunkPosFromVec3(box.Max())
   756  
   757  	for x := minPos[0]; x <= maxPos[0]; x++ {
   758  		for z := minPos[1]; z <= maxPos[1]; z++ {
   759  			c, ok := w.chunkFromCache(ChunkPos{x, z})
   760  			if !ok {
   761  				// The chunk wasn't loaded, so there are no entities here.
   762  				continue
   763  			}
   764  			entities := slices.Clone(c.Entities)
   765  			c.Unlock()
   766  
   767  			for _, entity := range entities {
   768  				if ignored != nil && ignored(entity) {
   769  					continue
   770  				}
   771  				if box.Vec3Within(entity.Position()) {
   772  					// The entity position was within the BBox, so we add it to the slice to return.
   773  					m = append(m, entity)
   774  				}
   775  			}
   776  		}
   777  	}
   778  	return m
   779  }
   780  
   781  // Entities returns a list of all entities currently added to the World.
   782  func (w *World) Entities() []Entity {
   783  	if w == nil {
   784  		return nil
   785  	}
   786  	w.entityMu.RLock()
   787  	defer w.entityMu.RUnlock()
   788  	m := make([]Entity, 0, len(w.entities))
   789  	for e := range w.entities {
   790  		m = append(m, e)
   791  	}
   792  	return m
   793  }
   794  
   795  // OfEntity attempts to return a world that an entity is currently in. If the entity was not currently added
   796  // to a world, the world returned is nil and the bool returned is false.
   797  func OfEntity(e Entity) (*World, bool) {
   798  	worldsMu.RLock()
   799  	w, ok := entityWorlds[e]
   800  	worldsMu.RUnlock()
   801  	return w, ok
   802  }
   803  
   804  // Spawn returns the spawn of the world. Every new player will by default spawn on this position in the world
   805  // when joining.
   806  func (w *World) Spawn() cube.Pos {
   807  	if w == nil {
   808  		return cube.Pos{}
   809  	}
   810  	w.set.Lock()
   811  	s := w.set.Spawn
   812  	w.set.Unlock()
   813  	if s[1] > w.Range()[1] {
   814  		s[1] = w.highestObstructingBlock(s[0], s[2]) + 1
   815  	}
   816  	return s
   817  }
   818  
   819  // SetSpawn sets the spawn of the world to a different position. The player will be spawned in the center of
   820  // this position when newly joining.
   821  func (w *World) SetSpawn(pos cube.Pos) {
   822  	if w == nil {
   823  		return
   824  	}
   825  	w.set.Lock()
   826  	w.set.Spawn = pos
   827  	w.set.Unlock()
   828  
   829  	viewers, _ := w.allViewers()
   830  	for _, viewer := range viewers {
   831  		viewer.ViewWorldSpawn(pos)
   832  	}
   833  }
   834  
   835  // PlayerSpawn returns the spawn position of a player with a UUID in this World.
   836  func (w *World) PlayerSpawn(uuid uuid.UUID) cube.Pos {
   837  	if w == nil {
   838  		return cube.Pos{}
   839  	}
   840  	pos, exist, err := w.conf.Provider.LoadPlayerSpawnPosition(uuid)
   841  	if err != nil {
   842  		w.conf.Log.Errorf("failed to get player spawn: %v", err)
   843  		return w.Spawn()
   844  	}
   845  	if !exist {
   846  		return w.Spawn()
   847  	}
   848  	return pos
   849  }
   850  
   851  // SetPlayerSpawn sets the spawn position of a player with a UUID in this World. If the player has a spawn in the world,
   852  // the player will be teleported to this location on respawn.
   853  func (w *World) SetPlayerSpawn(uuid uuid.UUID, pos cube.Pos) {
   854  	if w == nil {
   855  		return
   856  	}
   857  	if err := w.conf.Provider.SavePlayerSpawnPosition(uuid, pos); err != nil {
   858  		w.conf.Log.Errorf("failed to set player spawn: %v", err)
   859  	}
   860  }
   861  
   862  // DefaultGameMode returns the default game mode of the world. When players join, they are given this game
   863  // mode.
   864  // The default game mode may be changed using SetDefaultGameMode().
   865  func (w *World) DefaultGameMode() GameMode {
   866  	if w == nil {
   867  		return GameModeSurvival
   868  	}
   869  	w.set.Lock()
   870  	defer w.set.Unlock()
   871  	return w.set.DefaultGameMode
   872  }
   873  
   874  // SetTickRange sets the range in chunks around each Viewer that will have the chunks (their blocks and entities)
   875  // ticked when the World is ticked.
   876  func (w *World) SetTickRange(v int) {
   877  	if w == nil {
   878  		return
   879  	}
   880  	w.set.Lock()
   881  	defer w.set.Unlock()
   882  	w.set.TickRange = int32(v)
   883  }
   884  
   885  // tickRange returns the tick range around each Viewer.
   886  func (w *World) tickRange() int {
   887  	w.set.Lock()
   888  	defer w.set.Unlock()
   889  	return int(w.set.TickRange)
   890  }
   891  
   892  // SetDefaultGameMode changes the default game mode of the world. When players join, they are then given that
   893  // game mode.
   894  func (w *World) SetDefaultGameMode(mode GameMode) {
   895  	if w == nil {
   896  		return
   897  	}
   898  	w.set.Lock()
   899  	defer w.set.Unlock()
   900  	w.set.DefaultGameMode = mode
   901  }
   902  
   903  // Difficulty returns the difficulty of the world. Properties of mobs in the world and the player's hunger
   904  // will depend on this difficulty.
   905  func (w *World) Difficulty() Difficulty {
   906  	if w == nil {
   907  		return DifficultyNormal
   908  	}
   909  	w.set.Lock()
   910  	defer w.set.Unlock()
   911  	return w.set.Difficulty
   912  }
   913  
   914  // SetDifficulty changes the difficulty of a world.
   915  func (w *World) SetDifficulty(d Difficulty) {
   916  	if w == nil {
   917  		return
   918  	}
   919  	w.set.Lock()
   920  	defer w.set.Unlock()
   921  	w.set.Difficulty = d
   922  }
   923  
   924  // ScheduleBlockUpdate schedules a block update at the position passed after a specific delay. If the block at
   925  // that position does not handle block updates, nothing will happen.
   926  func (w *World) ScheduleBlockUpdate(pos cube.Pos, delay time.Duration) {
   927  	if w == nil || pos.OutOfBounds(w.Range()) {
   928  		return
   929  	}
   930  	w.updateMu.Lock()
   931  	defer w.updateMu.Unlock()
   932  	if _, exists := w.scheduledUpdates[pos]; exists {
   933  		return
   934  	}
   935  	w.set.Lock()
   936  	t := w.set.CurrentTick
   937  	w.set.Unlock()
   938  
   939  	w.scheduledUpdates[pos] = t + delay.Nanoseconds()/int64(time.Second/20)
   940  }
   941  
   942  // doBlockUpdatesAround schedules block updates directly around and on the position passed.
   943  func (w *World) doBlockUpdatesAround(pos cube.Pos) {
   944  	if w == nil || pos.OutOfBounds(w.Range()) {
   945  		return
   946  	}
   947  
   948  	changed := pos
   949  
   950  	w.updateMu.Lock()
   951  	w.updateNeighbour(pos, changed)
   952  	pos.Neighbours(func(pos cube.Pos) {
   953  		w.updateNeighbour(pos, changed)
   954  	}, w.Range())
   955  	w.updateMu.Unlock()
   956  }
   957  
   958  // neighbourUpdate represents a position that needs to be updated because of a neighbour that changed.
   959  type neighbourUpdate struct {
   960  	pos, neighbour cube.Pos
   961  }
   962  
   963  // updateNeighbour ticks the position passed as a result of the neighbour passed being updated.
   964  func (w *World) updateNeighbour(pos, changedNeighbour cube.Pos) {
   965  	w.neighbourUpdates = append(w.neighbourUpdates, neighbourUpdate{pos: pos, neighbour: changedNeighbour})
   966  }
   967  
   968  // Handle changes the current Handler of the world. As a result, events called by the world will call
   969  // handlers of the Handler passed.
   970  // Handle sets the world's Handler to NopHandler if nil is passed.
   971  func (w *World) Handle(h Handler) {
   972  	if w == nil {
   973  		return
   974  	}
   975  	if h == nil {
   976  		h = NopHandler{}
   977  	}
   978  	w.handler.Store(h)
   979  }
   980  
   981  // Viewers returns a list of all viewers viewing the position passed. A viewer will be assumed to be watching
   982  // if the position is within one of the chunks that the viewer is watching.
   983  func (w *World) Viewers(pos mgl64.Vec3) (viewers []Viewer) {
   984  	if w == nil {
   985  		return nil
   986  	}
   987  	c, ok := w.chunkFromCache(chunkPosFromVec3(pos))
   988  	if !ok {
   989  		return nil
   990  	}
   991  	defer c.Unlock()
   992  	return slices.Clone(c.viewers)
   993  }
   994  
   995  // PortalDestination returns the destination world for a portal of a specific Dimension. If no destination World could
   996  // be found, the current World is returned.
   997  func (w *World) PortalDestination(dim Dimension) *World {
   998  	if w.conf.PortalDestination == nil {
   999  		return w
  1000  	}
  1001  	if res := w.conf.PortalDestination(dim); res != nil {
  1002  		return res
  1003  	}
  1004  	return w
  1005  }
  1006  
  1007  // Close closes the world and saves all chunks currently loaded.
  1008  func (w *World) Close() error {
  1009  	if w == nil {
  1010  		return nil
  1011  	}
  1012  	w.o.Do(w.close)
  1013  	return nil
  1014  }
  1015  
  1016  // close stops the World from ticking, saves all chunks to the Provider and updates the world's settings.
  1017  func (w *World) close() {
  1018  	// Let user code run anything that needs to be finished before the World is closed.
  1019  	w.Handler().HandleClose()
  1020  	w.Handle(NopHandler{})
  1021  
  1022  	close(w.closing)
  1023  	w.running.Wait()
  1024  
  1025  	w.conf.Log.Debugf("Saving chunks in memory to disk...")
  1026  
  1027  	w.chunkMu.Lock()
  1028  	w.lastChunk = nil
  1029  	toSave := maps.Clone(w.chunks)
  1030  	maps.Clear(w.chunks)
  1031  	w.chunkMu.Unlock()
  1032  
  1033  	for pos, c := range toSave {
  1034  		w.saveChunk(pos, c)
  1035  	}
  1036  
  1037  	w.set.ref.Dec()
  1038  	if !w.advance {
  1039  		return
  1040  	}
  1041  
  1042  	if !w.conf.ReadOnly {
  1043  		w.conf.Log.Debugf("Updating level.dat values...")
  1044  
  1045  		w.provider().SaveSettings(w.set)
  1046  	}
  1047  
  1048  	w.conf.Log.Debugf("Closing provider...")
  1049  	if err := w.provider().Close(); err != nil {
  1050  		w.conf.Log.Errorf("error closing world provider: %v", err)
  1051  	}
  1052  }
  1053  
  1054  // allViewers returns a list of all loaders of the world, regardless of where in the world they are viewing.
  1055  func (w *World) allViewers() ([]Viewer, []*Loader) {
  1056  	w.viewersMu.Lock()
  1057  	defer w.viewersMu.Unlock()
  1058  
  1059  	viewers, loaders := make([]Viewer, 0, len(w.viewers)), make([]*Loader, 0, len(w.viewers))
  1060  	for k, v := range w.viewers {
  1061  		viewers = append(viewers, v)
  1062  		loaders = append(loaders, k)
  1063  	}
  1064  	return viewers, loaders
  1065  }
  1066  
  1067  // addWorldViewer adds a viewer to the world. Should only be used while the viewer isn't viewing any chunks.
  1068  func (w *World) addWorldViewer(l *Loader) {
  1069  	w.viewersMu.Lock()
  1070  	w.viewers[l] = l.viewer
  1071  	w.viewersMu.Unlock()
  1072  	l.viewer.ViewTime(w.Time())
  1073  	w.set.Lock()
  1074  	raining, thundering := w.set.Raining, w.set.Raining && w.set.Thundering
  1075  	w.set.Unlock()
  1076  	l.viewer.ViewWeather(raining, thundering)
  1077  	l.viewer.ViewWorldSpawn(w.Spawn())
  1078  }
  1079  
  1080  // removeWorldViewer removes a viewer from the world. Should only be used while the viewer isn't viewing any chunks.
  1081  func (w *World) removeWorldViewer(l *Loader) {
  1082  	w.viewersMu.Lock()
  1083  	delete(w.viewers, l)
  1084  	w.viewersMu.Unlock()
  1085  }
  1086  
  1087  // addViewer adds a viewer to the world at a given position. Any events that happen in the chunk at that
  1088  // position, such as block changes, entity changes etc., will be sent to the viewer.
  1089  func (w *World) addViewer(c *Column, loader *Loader) {
  1090  	if w == nil {
  1091  		return
  1092  	}
  1093  	c.viewers = append(c.viewers, loader.viewer)
  1094  	c.loaders = append(c.loaders, loader)
  1095  
  1096  	entities := slices.Clone(c.Entities)
  1097  	c.Unlock()
  1098  
  1099  	for _, entity := range entities {
  1100  		showEntity(entity, loader.viewer)
  1101  	}
  1102  }
  1103  
  1104  // removeViewer removes a viewer from the world at a given position. All entities will be hidden from the
  1105  // viewer and no more calls will be made when events in the chunk happen.
  1106  func (w *World) removeViewer(pos ChunkPos, loader *Loader) {
  1107  	if w == nil {
  1108  		return
  1109  	}
  1110  	c, ok := w.chunkFromCache(pos)
  1111  	if !ok {
  1112  		return
  1113  	}
  1114  	if i := slices.Index(c.loaders, loader); i != -1 {
  1115  		c.viewers = slices.Delete(c.viewers, i, i+1)
  1116  		c.loaders = slices.Delete(c.loaders, i, i+1)
  1117  	}
  1118  	e := slices.Clone(c.Entities)
  1119  	c.Unlock()
  1120  
  1121  	// After removing the loader from the chunk, we also need to hide all entities from the viewer.
  1122  	for _, entity := range e {
  1123  		loader.viewer.HideEntity(entity)
  1124  	}
  1125  }
  1126  
  1127  // provider returns the provider of the world. It should always be used, rather than direct field access, in
  1128  // order to provide synchronisation safety.
  1129  func (w *World) provider() Provider {
  1130  	return w.conf.Provider
  1131  }
  1132  
  1133  // Handler returns the Handler of the world. It should always be used, rather than direct field access, in
  1134  // order to provide synchronisation safety.
  1135  func (w *World) Handler() Handler {
  1136  	if w == nil {
  1137  		return NopHandler{}
  1138  	}
  1139  	return w.handler.Load()
  1140  }
  1141  
  1142  // chunkFromCache attempts to fetch a chunk at the chunk position passed from the cache. If not found, the
  1143  // chunk returned is nil and false is returned.
  1144  func (w *World) chunkFromCache(pos ChunkPos) (*Column, bool) {
  1145  	w.chunkMu.Lock()
  1146  	c, ok := w.chunks[pos]
  1147  	w.chunkMu.Unlock()
  1148  	if ok {
  1149  		c.Lock()
  1150  	}
  1151  	return c, ok
  1152  }
  1153  
  1154  // showEntity shows an entity to a viewer of the world. It makes sure everything of the entity, including the
  1155  // items held, is shown.
  1156  func showEntity(e Entity, viewer Viewer) {
  1157  	viewer.ViewEntity(e)
  1158  	viewer.ViewEntityItems(e)
  1159  	viewer.ViewEntityArmour(e)
  1160  }
  1161  
  1162  // chunk reads a chunk from the position passed. If a chunk at that position is not yet loaded, the chunk is
  1163  // loaded from the provider, or generated if it did not yet exist. Both of these actions are done
  1164  // synchronously.
  1165  // An error is returned if the chunk could not be loaded successfully.
  1166  // chunk locks the chunk returned, meaning that any call to chunk made at the same time has to wait until the
  1167  // user calls Chunk.Unlock() on the chunk returned.
  1168  func (w *World) chunk(pos ChunkPos) *Column {
  1169  	w.chunkMu.Lock()
  1170  	if pos == w.lastPos && w.lastChunk != nil {
  1171  		c := w.lastChunk
  1172  		w.chunkMu.Unlock()
  1173  		c.Lock()
  1174  		return c
  1175  	}
  1176  	c, ok := w.chunks[pos]
  1177  	if !ok {
  1178  		var err error
  1179  		c, err = w.loadChunk(pos)
  1180  		chunk.LightArea([]*chunk.Chunk{c.Chunk}, int(pos[0]), int(pos[1])).Fill()
  1181  		if err != nil {
  1182  			w.chunkMu.Unlock()
  1183  			w.conf.Log.Errorf("load chunk: failed loading %v: %v\n", pos, err)
  1184  			return c
  1185  		}
  1186  		c.Unlock()
  1187  		w.chunkMu.Lock()
  1188  
  1189  		w.calculateLight(pos)
  1190  	}
  1191  	w.lastChunk, w.lastPos = c, pos
  1192  	w.chunkMu.Unlock()
  1193  
  1194  	c.Lock()
  1195  	return c
  1196  }
  1197  
  1198  // setChunk sets the chunk.Chunk passed at a specific ChunkPos without replacing any entities at that
  1199  // position.
  1200  //
  1201  //lint:ignore U1000 This method is explicitly present to be used using compiler directives.
  1202  func (w *World) setChunk(pos ChunkPos, c *chunk.Chunk, e map[cube.Pos]Block) {
  1203  	if w == nil {
  1204  		return
  1205  	}
  1206  	w.chunkMu.Lock()
  1207  	defer w.chunkMu.Unlock()
  1208  
  1209  	col := newColumn(c)
  1210  	maps.Copy(col.BlockEntities, e)
  1211  	if o, ok := w.chunks[pos]; ok {
  1212  		col.viewers = o.viewers
  1213  	}
  1214  	w.chunks[pos] = col
  1215  }
  1216  
  1217  // loadChunk attempts to load a chunk from the provider, or generates a chunk if one doesn't currently exist.
  1218  func (w *World) loadChunk(pos ChunkPos) (*Column, error) {
  1219  	col, err := w.provider().LoadColumn(pos, w.conf.Dim)
  1220  	switch {
  1221  	case err == nil:
  1222  		w.chunks[pos] = col
  1223  		// Iterate through the entities twice and make sure they're added to all relevant maps. Note that this iteration
  1224  		// happens twice to avoid having to lock both worldsMu and entityMu. This is intentional, to avoid deadlocks.
  1225  		worldsMu.Lock()
  1226  		for _, e := range col.Entities {
  1227  			entityWorlds[e] = w
  1228  		}
  1229  		worldsMu.Unlock()
  1230  
  1231  		w.entityMu.Lock()
  1232  		for _, e := range col.Entities {
  1233  			w.entities[e] = pos
  1234  		}
  1235  		w.entityMu.Unlock()
  1236  
  1237  		col.Lock()
  1238  		w.chunkMu.Unlock()
  1239  		return col, nil
  1240  	case errors.Is(err, leveldb.ErrNotFound):
  1241  		// The provider doesn't have a chunk saved at this position, so we generate a new one.
  1242  		col = newColumn(chunk.New(airRID, w.Range()))
  1243  		w.chunks[pos] = col
  1244  
  1245  		col.Lock()
  1246  		w.chunkMu.Unlock()
  1247  
  1248  		w.conf.Generator.GenerateChunk(pos, col.Chunk)
  1249  		return col, nil
  1250  	default:
  1251  		col = newColumn(chunk.New(airRID, w.Range()))
  1252  		col.Lock()
  1253  		return col, err
  1254  	}
  1255  }
  1256  
  1257  // calculateLight calculates the light in the chunk passed and spreads the light of any of the surrounding
  1258  // neighbours if they have all chunks loaded around it as a result of the one passed.
  1259  func (w *World) calculateLight(centre ChunkPos) {
  1260  	for x := int32(-1); x <= 1; x++ {
  1261  		for z := int32(-1); z <= 1; z++ {
  1262  			// For all the neighbours of this chunk, if they exist, check if all neighbours of that chunk
  1263  			// now exist because of this one.
  1264  			pos := ChunkPos{centre[0] + x, centre[1] + z}
  1265  			if _, ok := w.chunks[pos]; ok {
  1266  				// Attempt to spread the light of all neighbours into the ones surrounding them.
  1267  				w.spreadLight(pos)
  1268  			}
  1269  		}
  1270  	}
  1271  }
  1272  
  1273  // spreadLight spreads the light from the chunk passed at the position passed to all neighbours if each of
  1274  // them is loaded.
  1275  func (w *World) spreadLight(pos ChunkPos) {
  1276  	chunks := make([]*Column, 0, 9)
  1277  	for z := int32(-1); z <= 1; z++ {
  1278  		for x := int32(-1); x <= 1; x++ {
  1279  			neighbour, ok := w.chunks[ChunkPos{pos[0] + x, pos[1] + z}]
  1280  			if !ok {
  1281  				// Not all surrounding chunks existed: Stop spreading light as we can't do it completely yet.
  1282  				return
  1283  			}
  1284  			chunks = append(chunks, neighbour)
  1285  		}
  1286  	}
  1287  	for _, neighbour := range chunks {
  1288  		neighbour.Lock()
  1289  	}
  1290  	// All chunks of the current one are present, so we can spread the light from this chunk
  1291  	// to all chunks.
  1292  	c := make([]*chunk.Chunk, 9)
  1293  	for i := range chunks {
  1294  		c[i] = chunks[i].Chunk
  1295  	}
  1296  	chunk.LightArea(c, int(pos[0])-1, int(pos[1])-1).Spread()
  1297  	for _, neighbour := range chunks {
  1298  		neighbour.Unlock()
  1299  	}
  1300  }
  1301  
  1302  // saveChunk is called when a chunk is removed from the cache. We first compact the chunk, then we write it to
  1303  // the provider.
  1304  func (w *World) saveChunk(pos ChunkPos, c *Column) {
  1305  	c.Lock()
  1306  	if !w.conf.ReadOnly && (len(c.BlockEntities) > 0 || len(c.Entities) > 0 || c.modified) {
  1307  		c.Compact()
  1308  		if err := w.provider().StoreColumn(pos, w.conf.Dim, c); err != nil {
  1309  			w.conf.Log.Errorf("save chunk: %v", err)
  1310  		}
  1311  	}
  1312  	ent := c.Entities
  1313  	c.Entities = nil
  1314  	c.Unlock()
  1315  
  1316  	for _, e := range ent {
  1317  		_ = e.Close()
  1318  	}
  1319  }
  1320  
  1321  // chunkCacheJanitor runs until the world is running, cleaning chunks that are no longer in use from the cache.
  1322  func (w *World) chunkCacheJanitor() {
  1323  	t := time.NewTicker(time.Minute * 5)
  1324  	defer t.Stop()
  1325  
  1326  	w.running.Add(1)
  1327  	chunksToRemove := map[ChunkPos]*Column{}
  1328  	for {
  1329  		select {
  1330  		case <-t.C:
  1331  			w.chunkMu.Lock()
  1332  			for pos, c := range w.chunks {
  1333  				c.Lock()
  1334  				v := len(c.viewers)
  1335  				c.Unlock()
  1336  				if v == 0 {
  1337  					chunksToRemove[pos] = c
  1338  					delete(w.chunks, pos)
  1339  					if w.lastPos == pos {
  1340  						w.lastChunk = nil
  1341  					}
  1342  				}
  1343  			}
  1344  			w.chunkMu.Unlock()
  1345  
  1346  			for pos, c := range chunksToRemove {
  1347  				w.saveChunk(pos, c)
  1348  				delete(chunksToRemove, pos)
  1349  			}
  1350  		case <-w.closing:
  1351  			w.running.Done()
  1352  			return
  1353  		}
  1354  	}
  1355  }
  1356  
  1357  // Column represents the data of a chunk including the block entities and loaders. This data is protected
  1358  // by the mutex present in the chunk.Chunk held.
  1359  type Column struct {
  1360  	sync.Mutex
  1361  	modified bool
  1362  
  1363  	*chunk.Chunk
  1364  	Entities      []Entity
  1365  	BlockEntities map[cube.Pos]Block
  1366  
  1367  	viewers []Viewer
  1368  	loaders []*Loader
  1369  }
  1370  
  1371  // newColumn returns a new Column wrapper around the chunk.Chunk passed.
  1372  func newColumn(c *chunk.Chunk) *Column {
  1373  	return &Column{Chunk: c, BlockEntities: map[cube.Pos]Block{}}
  1374  }