func Region(x, y int) *region {
	return get(x, y)
}
Region Returns the region that corresponds with the given coordinates.  If it does not exist yet, it will allocate a new onr and store it for the lifetime of the application in the regions map.
package world
import (
	"math"
	"sync"
	"time"
	"github.com/spkaeros/rscgo/pkg/isaac"
	"github.com/spkaeros/rscgo/pkg/tasks"
	"github.com/spkaeros/rscgo/pkg/game/entity"
	"github.com/spkaeros/rscgo/pkg/log"
	rscRand "github.com/spkaeros/rscgo/pkg/rand"
)
const (
	TicksDay         = 135000
	TicksHour        = 5625
	TicksTwentyMin   = 1875
	TicksMinute      = 100
	TickMinute       = TicksMinute * TickMillis
	TickHour         = TicksHour * TickMillis
	TickDay          = TicksDay * TickMillis
	TickMillis       = 640 * time.Millisecond
	ClientTickMillis = TickMillis >> 5
	MaxX = 944
	MaxY = 944 * 4  
)
func CurrentTick() int {
	return int(tasks.Ticks.Load())
}
const (
	RegionSize = 48
	HorizontalPlanes = MaxX/RegionSize + 1
	VerticalPlanes = MaxY/RegionSize + 1
	LowerBound = RegionSize / 2
)
var UpdateTime time.Time
type indexQueue []int
func (q *indexQueue) Push(i int) {
	*q = append([]int{i}, *q...)
}
func (q *indexQueue) Pop() int {
	if len(*q) <= 0 {
		return -1
	}
	idx := (*q)[0]
	if len(*q) == 1 {
		*q = (*q)[:0]
	} else {
		*q = (*q)[1:]
	}
	return idx
}
type PlayersList map[*Player]struct{}
type PlayerList struct {
	sync.RWMutex
	PlayersList
	free   indexQueue
	curIdx int
}
var Players = &PlayerList{free: make(indexQueue, 0, 1250), PlayersList: make(PlayersList)}
func (m *PlayerList) FindHash(hash uint64) (*Player, bool) {
	m.RLock()
	defer m.RUnlock()
	for player := range m.PlayersList {
		if player.UsernameHash() == hash {
			return player, true
		}
	}
	return nil, false
}
func (m *PlayerList) FindIndex(index int) (*Player, bool) {
	if index < 0 {
		return nil, false
	}
	m.RLock()
	defer m.RUnlock()
	for player := range m.PlayersList {
		if player.ServerIndex() == index {
			return player, true
		}
	}
	return nil, false
}
func (m *PlayerList) Find(player *Player) int {
	return m.ForEach(func(p *Player) bool {
		return p == player
	})
}
func (m *PlayerList) Contains(player *Player) bool {
	return m.Find(player) > -1
}
func (m *PlayerList) ContainsHash(hash uint64) bool {
	_, ret := m.FindHash(hash)
	return ret
}
func (m *PlayerList) Put(player *Player) {
	if m.Contains(player) {
		log.Warn("Player list double-entry attempted!")
		return
	}
	player.SetServerIndex(m.nextSlot())
	m.Lock()
	defer m.Unlock()
	m.PlayersList[player] = struct{}{} 
}
func (m *PlayerList) Remove(player *Player) {
	slot := player.ServerIndex()
	if slot == -1 {
		log.Warn("Error: Player with non-valid index being removed!")
		return
	}
	player.SetServerIndex(-1)
	m.Lock()
	defer m.Unlock()
	delete(m.PlayersList, player)
	m.free.Push(slot)
}
func (m *PlayerList) Range(action func(*Player)) {
	m.RLock()
	defer m.RUnlock()
	for p := range m.PlayersList {
		if p != nil {
			action(p)
		}
	}
}
func (m *PlayerList) ForEach(action func(*Player) bool) int {
	m.RLock()
	defer m.RUnlock()
	for p := range m.PlayersList {
		if p != nil {
			if action(p) {
				return p.ServerIndex()
			}
		}
	}
	return -1
}
func (m *PlayerList) Size() int {
	m.RLock()
	defer m.RUnlock()
	return len(m.PlayersList)
}
func (m *PlayerList) nextSlot() int {
	m.Lock()
	defer m.Unlock()
	idx := m.free.Pop()
	if idx == -1 {
		defer func() { m.curIdx += 1 }()
		return m.curIdx
	}
	return idx
}
func (m *PlayerList) Set() []*Player {
	keys := make([]*Player, m.Size())
	i := 0
	for k := range m.PlayersList {
	    keys[i] = k
	    i++
	}
	return keys
}
func (m *PlayerList) AsyncRange(fn func(*Player)) {
	m.RLock()
	sz := len(m.PlayersList)
	done := make(chan struct{}, sz)
	for player := range m.PlayersList {
		go func(player *Player) {
			fn(player)
			done <- struct{}{}
		}(player)
	}
	m.RUnlock()
	defer close(done)
	for i := 0; i < sz; i += 1 {
		select {
		case _, ok := <-done:
			if !ok {
				log.Debug("premature AsyncRange done-signal")
				break
			}
			continue
		}
	}
}
type region struct {
	x       int
	y       int
	Players *MobList
	NPCs    *MobList
	Objects *entityList
	Items   *entityList
}
var regions [HorizontalPlanes][VerticalPlanes]*region
func WithinWorld(x, y int) bool {
	return x <= MaxX && x >= 0 && y >= 0 && y <= MaxY
}
func AddPlayer(p *Player) {
	Players.Put(p)
	Region(p.X(), p.Y()).Players.Add(p)
}
func RemovePlayer(p *Player) {
	p.SetRegionRemoved()
	Region(p.X(), p.Y()).Players.Remove(p)
	Players.Remove(p)
}
func AddNpc(n *NPC) {
	Region(n.X(), n.Y()).NPCs.Add(n)
}
func RemoveNpc(n *NPC) {
	Region(n.X(), n.Y()).NPCs.Remove(n)
}
func AddItem(i *GroundItem) {
	Region(i.X(), i.Y()).Items.Add(i)
}
func GetItem(x, y, id int) *GroundItem {
	region := Region(x, y)
	region.Items.RLock()
	defer region.Items.RUnlock()
	for _, i := range region.Items.set {
		if i, ok := i.(*GroundItem); ok {
			if i.ID == id && i.X() == x && i.Y() == y {
				return i
			}
		}
	}
	return nil
}
func RemoveItem(i *GroundItem) {
	Region(i.X(), i.Y()).Items.Remove(i)
}
type mapBarrier interface {
	Solid() bool
	Door() bool
	Passable() bool
	Width() int
	Height() int
	Defined() bool
}
func AddObject(o *Object) {
	Region(o.X(), o.Y()).Objects.Add(o)
	data := o.TypeData()
	if !data.Defined() {
		return
	}
	if data.Passable() {
		return
	}
	if o.Boundary {
		x,y := o.X(),o.Y()
		areaX := (2304+x) % RegionSize
		areaY := (1776+y - (944*((y+100)/944))) % RegionSize
		switch o.Direction {
		case 0:
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipNorth
			if areaX+areaY > 0 {
				sectorFromCoords(x, y-1).Tiles[areaX*RegionSize+areaY-1] |= ClipSouth
			}
		case 1:
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipEast
			if areaX > 0 || areaY >= 48 {
				sectorFromCoords(x-1, y).Tiles[(areaX-1)*RegionSize+areaY] |= ClipWest
			}
		case 2:
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipSwNe
		case 3:
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipSeNw
		}
		return
	}
	width, height := data.Width(), data.Height()
	if o.Direction&3 != 0 {
		width, height = height, width
	}
	for dx := 0; dx < width; dx++ {
		for dy := 0; dy < height; dy++ {
			x,y := o.X()+dx,o.Y()+dy
			areaX := (2304+x) % RegionSize
			areaY := (1776+y - (944*((y+100)/944))) % RegionSize
			if len(sectorFromCoords(x, y).Tiles) <= 0 {
				return
			}
			if data.Solid() {
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipFullBlock
				continue
			}
			switch int(o.Direction) {
			case 0:  
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipEast
				if len(sectorFromCoords(x-1, y).Tiles) > 0 && (areaX > 0 || areaY >= RegionSize) {
					sectorFromCoords(x-1, y).Tiles[(areaX-1)*RegionSize+areaY] |= ClipWest
				}
			case 2:  
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipSouth
				sectorFromCoords(x, y+1).Tiles[areaX*RegionSize+areaY+1] |= ClipNorth
			case 4:  
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipWest
				if areaX, areaY := (2304+x+1)%RegionSize, (1776+y-(944*((y+100)/944)))%RegionSize; (areaX+1)*RegionSize+areaY > 2304 {
					sectorFromCoords(x+1, y).Tiles[areaX*RegionSize+areaY] |= ClipEast
				}
			case 6:  
				sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] |= ClipNorth
				if len(sectorFromCoords(x, y-1).Tiles) > 0 && areaX+areaY > 0 {
					sectorFromCoords(x, y-1).Tiles[areaX*RegionSize+areaY-1] |= ClipSouth
				}
			}
		}
	}
}
func RemoveObject(o *Object) {
	Region(o.X(), o.Y()).Objects.Remove(o)
	data := o.TypeData()
	if data.Passable() {
		return
	}
	if !o.Boundary {
		width, height := data.Width(), data.Height()
		if o.Direction&3 != 0 {
			width,height = height,width
		}
		for dx := 0; dx < width; dx++ {
			for dy := 0; dy < height; dy++ {
				x,y := o.X()+dx,o.Y()+dy
				areaX := (2304 + x) % RegionSize
				areaY := (1776 + y - (944 * ((y + 100) / 944))) % RegionSize
				if data.Solid() {
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipFullBlock
					continue
				}
				switch o.Direction {
				case 0:
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipEast
					if sectorFromCoords(x-1, y) != nil {
						sectorFromCoords(x-1, y).Tiles[(areaX-1)*RegionSize+areaY] &= ^ClipWest
					}
				case 2:
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipSouth
					if sectorFromCoords(x, y+1) != nil {
						sectorFromCoords(x, y+1).Tiles[areaX*RegionSize+areaY+1] &= ^ClipNorth
					}
				case 4:
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipWest
					if sectorFromCoords(x+1, y) != nil {
						sectorFromCoords(x+1, y).Tiles[(areaX+1)*RegionSize+areaY] &= ^ClipEast
					}
				case 6:
					sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipNorth
					if sectorFromCoords(x, y-1) != nil {
						sectorFromCoords(x, y-1).Tiles[areaX*RegionSize+areaY-1] &= ^ClipSouth
					}
				}
			}
		}
	} else {
		x, y := o.X(), o.Y()
		areaX := (2304 + x) % RegionSize
		areaY := (1776 + y - (944 * ((y + 100) / 944))) % RegionSize
		if o.Direction == 0 {  
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipNorth
			if areaX+areaY > 0 {
				sectorFromCoords(x, y-1).Tiles[areaX*RegionSize+areaY-1] &= ^ClipSouth
			}
		} else if o.Direction == 1 {  
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipEast
			if areaX > 0 || areaY >= 48 {
				sectorFromCoords(x-1, y).Tiles[(areaX-1)*RegionSize+areaY] &= ^ClipWest
			}
		} else if o.Direction == 2 {  
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipSwNe
		} else if o.Direction == 3 {  
			sectorFromCoords(x, y).Tiles[areaX*RegionSize+areaY] &= ^ClipSeNw
		}
	}
}
func ReplaceObject(old *Object, newID int) *Object {
	RemoveObject(old)
	object := NewObject(newID, int(old.Direction), old.X(), old.Y(), old.Boundary)
	AddObject(object)
	return object
}
func GetAllObjects() (list []entity.Entity) {
	regionLock.RLock()
	defer regionLock.RUnlock()
	for _, xR := range regions {
		for _, yR := range xR {
			if yR != nil {
				yR.Objects.Range(func(e entity.Entity) {
					list = append(list, e)
				})
			}
		}
	}
	return list
}
func GetObject(x, y int) *Object {
	r := Region(x, y)
	r.Objects.RLock()
	defer r.Objects.RUnlock()
	for _, o := range r.Objects.set {
		if o, ok := o.(*Object); ok {
			if o.X() == x && o.Y() == y {
				return o
			}
		}
	}
	return nil
}
func GetNpc(index int) *NPC {
	m := Npcs.Get(index)
	if m == nil {
		return nil
	}
	return m.(*NPC)
}
func NpcNearest(id, x, y int) *NPC {
	point := NewLocation(x, y)
	minDelta := 16
	var npc *NPC
	regionLock.RLock()
	defer regionLock.RUnlock()
	for x := 0; x < MaxX; x += RegionSize {
		for y := 0; y < MaxY; y += RegionSize {
			Region(x, y).NPCs.RangeNpcs(func(n *NPC) bool {
				if n.ID == id && n.LongestDelta(point) < minDelta {
					minDelta = n.LongestDelta(point)
					npc = n
				}
				return false
			})
		}
	}
	return npc
}
func NpcVisibleFrom(id, x, y int) *NPC {
	point := NewLocation(x, y)
	minDelta := 16
	var npc *NPC
	for x := 0; x < MaxX; x += RegionSize {
		for y := 0; y < MaxY; y += RegionSize {
			Region(x, y).NPCs.RangeNpcs(func(n *NPC) bool {
				if n.ID == id && n.LongestDelta(point) < minDelta {
					minDelta = n.LongestDelta(point)
					npc = n
				}
				return false
			})
		}
	}
	return npc
}
var regionLock = sync.RWMutex{}
func get(x, y int) *region {
	x = int(math.Min(math.Max(0, float64(x/RegionSize)), HorizontalPlanes - 1))
	y = int(math.Min(math.Max(0, float64(y/RegionSize)), VerticalPlanes - 1))
	regionLock.Lock()
	defer regionLock.Unlock()
	if regions[x][y] == nil {
		regions[x][y] = &region{x, y, NewMobList(), NewMobList(), &entityList{}, &entityList{}}
	}
	return regions[x][y]
}