func (p *Player) NearbyPlayers() (players []*Player) {
	for _, r := range VisibleRegionsFrom(p) {
		r.Players.RangePlayers(func(p1 *Player) bool  {
			if p.Near(p1, p.ViewRadius()) && p1.ServerIndex() != p.ServerIndex() {
				players = append(players, p1)
			}
			return false
		})
	}
	return
}
NearbyPlayers Returns nearby players.
package world
import (
	"bufio"
	"context"
	"fmt"
	"io"
	"math"
	stdnet "net"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"github.com/spkaeros/rscgo/pkg/definitions"
	"github.com/spkaeros/rscgo/pkg/errors"
	"github.com/spkaeros/rscgo/pkg/game/entity"
	"github.com/spkaeros/rscgo/pkg/game/net"
	"github.com/spkaeros/rscgo/pkg/game/social"
	"github.com/spkaeros/rscgo/pkg/isaac"
	"github.com/spkaeros/rscgo/pkg/log"
	"github.com/spkaeros/rscgo/pkg/strutil"
	"github.com/spkaeros/rscgo/pkg/tasks"
)
type (
	Player struct {
		context.Context
		LocalPlayers      *MobList
		LocalNPCs         *MobList
		LocalObjects      *entityList
		LocalItems        *entityList
		FriendList        *social.FriendsList
		IgnoreList        []uint64
		Appearance        entity.AppearanceTable
		KnownAppearances  map[int]int
		AppearanceReq     []*Player
		Socket            stdnet.Conn
		Attributes        *entity.AttributeList
		Inventory         *Inventory
		bank              *Inventory
		TradeOffer        *Inventory
		DuelOffer         *Inventory
		Duel              struct {
			Rules    [4]bool
			Accepted [2]bool
			Target   *Player
			Inventory
		}
		tickAction        tasks.StatusReturnCall
		ActionLock        sync.RWMutex
		ReplyMenuC        chan int8
		killer            sync.Once
		Cancel            func()
		inFrame			  bool
		hasReader         bool
		Websocket         bool
		InQueue, OutQueue chan *net.Packet
		Reader            *bufio.Reader
		webFrame 			  ws.Header
		Writer            net.WriteFlusher
		DatabaseIndex     int
		OpCiphers         [2]*isaac.ISAAC
		Mob
	}
)
func (p *Player) UsernameHash() uint64 {
	if p == nil {
		return 0
	}
	return p.VarLong("username", strutil.Base37.Encode("nil"))
}
func (p *Player) Bank() *Inventory {
	return p.bank
}
func (p *Player) UpdatedRegions() {
	p.SetVar("needsObjects", true)
}
func (p *Player) CanAttack(target entity.MobileEntity) bool {
	if target := AsNpc(target); target != nil {
		return target.Attackable()
	}
	if p.State()&StateFightingDuel == StateFightingDuel {
		return p.Duel.Target == target && p.DuelMagic()
	}
	targetp := AsPlayer(target)
	ourWild := p.Wilderness()
	targetWild := targetp.Wilderness()
	if ourWild < 1 || targetWild < 1 {
		p.Message("You cannot attack other players outside of the wilderness!")
		return false
	}
	delta := p.CombatDelta(target)
	if delta < 0 {
		delta = -delta
	}
	if delta > ourWild {
		p.Message("You must move to at least level " + strconv.Itoa(delta) + " wilderness to attack " + targetp.Username() + "!")
		return false
	}
	if delta > targetWild {
		p.Message(targetp.Username() + " is not in high enough wilderness for you to attack!")
		return false
	}
	return true
}
func (p *Player) Username() string {
	return strutil.Base37.Decode(p.VarLong("username", strutil.Base37.Encode("NIL")))
}
func (p *Player) CurrentIP() string {
	if strings.HasPrefix(p.RemoteAddress(), "[") {
		return p.RemoteAddress()[1:strings.LastIndex(p.RemoteAddress(), "]:")]
	}
	return strings.Split(p.RemoteAddress(), ":")[0]
}
func (p *Player) RemoteAddress() string {
	if p.Socket == nil {
		return "N/A:N/A"
	}
	return p.Socket.RemoteAddr().String()
}
func (p *Player) IsWebsocket() bool {
	return p.Websocket
}
func (p *Player) Rank() int {
	return p.VarInt("rank", 0)
}
func (p *Player) AppearanceTicket() int {
	if p == nil {
		return 0
	}
	return p.VarInt("appearanceTicket", 0)
}
func (p *Player) String() string {
	return fmt.Sprintf("'%s'[%d]@'%s'", p.Username(), p.ServerIndex(), p.CurrentIP())
}
func (p *Player) SetTickAction(action func() bool) {
	p.SetVar("tickAction", action)
}
func (p *Player) TickAction() func() bool {
	if p.Contains("tickAction") {
		return p.VarChecked("tickAction").(func() bool)
	}
	return nil
}
func (p *Player) ResetTickAction() {
	p.UnsetVar("tickAction")
}
func (p *Player) FriendsWith(other uint64) bool {
	return p.FriendList.ContainsHash(other)
}
func (p *Player) Ignoring(hash uint64) bool {
	for _, v := range p.IgnoreList {
		if v == hash {
			return true
		}
	}
	return false
}
func (p *Player) Attribute(id string) interface{} {
	attr, ok := p.Attributes.Var(id)
	if ok {
		return attr
	}
	return nil
}
func (p *Player) BoolAttribute(id string) bool {
	if ret := p.Attribute(id); ret != nil {
		return ret.(bool)
	}
	return false
}
func (p *Player) ChatBlocked() bool {
	return p.BoolAttribute("chat_block")
}
func (p *Player) FriendBlocked() bool {
	return p.BoolAttribute("friend_block")
}
func (p *Player) TradeBlocked() bool {
	return p.BoolAttribute("trade_block")
}
func (p *Player) DuelBlocked() bool {
	return p.BoolAttribute("duel_block")
}
func (p *Player) UpdateStatus(status bool) {
	Players.Range(func(player *Player) {
		if player.FriendList.Contains(p.Username()) {
			if p.FriendList.Contains(player.Username()) || !p.FriendBlocked() {
				p.WritePacket(FriendUpdate(p.UsernameHash(), status))
			}
		}
	})
}
func (p *Player) WalkingRangedAction(t entity.MobileEntity, fn func()) {
	p.WalkingArrivalAction(t, 5, fn)
}
func (p *Player) WalkingArrivalAction(t entity.MobileEntity, dist int, action func()) {
	p.SetTickAction(func() bool {
		if p.Near(t, dist) {
			if p.Collides(t) {
				if !p.FinishedPath() {
					return false
				}
				pivotList := p.PivotTo(t)
				if len(pivotList[0]) == 0 || len(pivotList[1]) == 0 {
					p.Message("I can't reach that.")
					return false
				}
				p.ResetPath()
				p.SetCoords(pivotList[0][0], pivotList[1][0], false)
				return true
			}
			action()
			return false
		}
		if p.FinishedPath() {
			pivotList := p.PivotTo(t)
			if len(pivotList[0]) == 0 || len(pivotList[1]) == 0 {
				p.Message("I can't reach that.")
				return false
			}
			p.ResetPath()
			p.SetCoords(pivotList[0][0], pivotList[1][0], false)
			return true
		}
		return true
	})
}
func (p *Player) SetPrivacySettings(chatBlocked, friendBlocked, tradeBlocked, duelBlocked bool) {
	p.Attributes.SetVar("chat_block", chatBlocked)
	p.Attributes.SetVar("friend_block", friendBlocked)
	p.Attributes.SetVar("trade_block", tradeBlocked)
	p.Attributes.SetVar("duel_block", duelBlocked)
	Players.Range(func(player *Player) {
		if player.FriendList.Contains(p.Username()) {
			if !p.FriendList.Contains(player.Username()) || p.FriendBlocked() {
				p.WritePacket(FriendUpdate(p.UsernameHash(), !p.FriendBlocked() || p.FriendList.Contains(player.Username())))
			}
		}
	})
}
func (p *Player) SetClientSetting(id int, flag bool) {
	p.Attributes.SetVar("client_setting_"+strconv.Itoa(id), flag)
}
func (p *Player) GetClientSetting(id int) bool {
	return p.BoolAttribute("client_setting_" + strconv.Itoa(id))
}
func (p *Player) ServerSeed() uint64 {
	return p.VarLong("server_seed", 0)
}
func (p *Player) SetServerSeed(seed uint64) {
	p.SetVar("server_seed", seed)
}
func (p *Player) Reconnecting() bool {
	return p.VarBool("reconnecting", false)
}
func (p *Player) SetReconnecting(flag bool) {
	p.SetVar("reconnecting", flag)
}
func (p *Player) Connected() bool {
	return p.VarBool("connected", false)
}
func (p *Player) SetConnected(flag bool) {
	p.SetVar("connected", flag)
}
func (p *Player) FirstLogin() bool {
	return p.Attributes.VarBool("first_login", true)
}
func (p *Player) SetFirstLogin(flag bool) {
	p.Attributes.SetVar("first_login", flag)
}
func (l Location) NextTo(target entity.Location) bool {
	return !l.Collides(target)
}
func (p *Player) NextToCoords(x, y int) bool {
	return p.NextTo(NewLocation(x, y))
}
func (p *Player) TraversePath() {
	path := p.Path()
	if path == nil {
		return
	}
	if p.LongestDelta(path.nextTile()) == 0 {
		path.CurrentWaypoint++
	}
	dst := p.NextTileToward(path.nextTile())
	if p.FinishedPath() {
		p.ResetPath()
		return
	}
	if !p.ReachableCoords(dst.X(), dst.Y()) {
		p.ResetPath()
		return
	}
	p.SetLocation(dst, false)
}
func (l Location) Targetable(other Location) bool {
	return l.Near(other, 5) && !l.Collides(other)
}
func (l Location) WithinReach(other entity.Location) bool {
	return l.Near(other, 2) && !l.Collides(other)
}
func (l Location) Reachable(other entity.Location) bool {
	return !l.Collides(other)
}
func (p *Player) WriteNow(packet net.Packet) {
	if packet.Bare {
		if count, err := p.Writer.Write(packet.FrameBuffer); err != nil || count < packet.Length() {
			log.Warn("Failed to write raw packet to player socket!")
		}
		return
	}
	header := []byte{0, 0}
	frameLength := len(packet.FrameBuffer)
	if cipher := p.OpCiphers[0]; cipher != nil {
		packet.FrameBuffer[0] = byte(uint32(packet.FrameBuffer[0]) + cipher.Uint32()) & 0xFF
	} else {
		log.Debug("nil isaac thingy")
	}
	if frameLength >= 160 {
		header[0] = byte(frameLength>>8 + 160)
		header[1] = byte(frameLength)
	} else {
		header[0] = byte(frameLength)
		if frameLength > 0 {
			frameLength--
			header[1] = packet.FrameBuffer[frameLength]
		}
	}
	if count, err := p.Writer.Write(append(header, packet.FrameBuffer[:frameLength]...)); err != nil || count < packet.Length()+1 {
		log.Warn("Failed to write formatted packet to player socket!")
	}
}
func (p *Player) UpdateRegion(x, y int) {
	UpdateRegions(p, x, y)
}
func (p *Player) DistributeMeleeExp(experience float64) {
	p.IncExp(entity.StatHits, int(experience))
	switch p.FightMode() {
	case 0:
		for i := 0; i < 3; i++ {
			p.IncExp(i, int(experience))
		}
	case 1:
		p.IncExp(entity.StatStrength, int(experience*3.0))
	case 2:
		p.IncExp(entity.StatAttack, int(experience*3.0))
	case 3:
		p.IncExp(entity.StatDefense, int(experience*3.0))
	}
}
func (p *Player) EquipItem(item *Item) {
	reqs := definitions.Items[item.ID].Requirements
	if reqs != nil {
		var needed string
		for skill, lvl := range reqs {
			if p.Skills().Current(skill) < lvl {
				needed += strconv.Itoa(lvl) + " " + entity.SkillName(skill) + ", "
			}
		}
		if len(needed) > 0 {
			p.Message("You must have at least " + needed[:len(needed)-2] + " to wield a " + item.Name())
			return
		}
	}
	def := definitions.Equip(item.ID)
	if def == nil {
		return
	}
	if def.Female == p.Appearance.Male {
		p.Message("You must be a female to wear that")
		return
	}
	p.PlaySound("click")
	p.Inventory.Range(func(otherItem *Item) bool {
		otherDef := definitions.Equip(otherItem.ID)
		if otherItem == item || !otherItem.Worn || otherDef == nil || def.Type&otherDef.Type == 0 {
			return true
		}
		otherItem.Worn = false
		p.IncAimPoints(-def.Aim)
		p.IncPowerPoints(-def.Power)
		p.IncArmourPoints(-def.Armour)
		p.IncMagicPoints(-def.Magic)
		p.IncPrayerPoints(-def.Prayer)
		p.IncRangedPoints(-def.Ranged)
		if otherDef.Type&1 == 1 {
			p.Equips()[otherDef.Position] = p.Appearance.Head
		} else if otherDef.Type&2 == 2 {
			p.Equips()[otherDef.Position] = p.Appearance.Body
		} else if otherDef.Type&4 == 4 {
			p.Equips()[otherDef.Position] = p.Appearance.Legs
		} else {
			p.Equips()[otherDef.Position] = 0
		}
		return true
	})
	item.Worn = true
	p.IncAimPoints(def.Aim)
	p.IncPowerPoints(def.Power)
	p.IncArmourPoints(def.Armour)
	p.IncMagicPoints(def.Magic)
	p.IncPrayerPoints(def.Prayer)
	p.IncRangedPoints(def.Ranged)
	p.Equips()[def.Position] = def.Sprite
	p.UpdateAppearance()
	p.WritePacket(EquipmentStats(p))
	p.WritePacket(InventoryItems(p))
}
func (p *Player) Equips() []int {
	s, ok := p.Var("sprites")
	if !ok || s == nil {
		return []int{}
	}
	return s.([]int)
}
func (p *Player) UpdateAppearance() {
	p.Inc("appearanceTicket", 1)
	p.SetAppearanceChanged()
	p.enqueueArea(playerEvents, p.ViewRadius(), map[string]int {"index": int(p.ServerIndex()), "ticket": int(p.AppearanceTicket())})
}
func (p *Player) ViewRadius() int {
	return p.VarInt("viewRadius", 16) - 1
}
func (p *Player) DequipItem(item *Item) {
	def := definitions.Equip(item.ID)
	if !item.Worn || def == nil {
		return
	}
	item.Worn = false
	p.IncAimPoints(-def.Aim)
	p.IncPowerPoints(-def.Power)
	p.IncArmourPoints(-def.Armour)
	p.IncMagicPoints(-def.Magic)
	p.IncPrayerPoints(-def.Prayer)
	p.IncRangedPoints(-def.Ranged)
	if def.Type&1 == 1 {
		p.Equips()[def.Position] = p.Appearance.Head
	} else if def.Type&2 == 2 {
		p.Equips()[def.Position] = p.Appearance.Body
	} else if def.Type&4 == 4 {
		p.Equips()[def.Position] = p.Appearance.Legs
	} else {
		p.Equips()[def.Position] = 0
	}
	p.UpdateAppearance()
	p.SendEquipBonuses()
	p.SendInventory()
}
func (p *Player) ResetAll() {
	p.ResetFighting()
	p.ResetDuel()
	p.ResetTrade()
	p.ResetTickAction()
	p.CloseOptionMenu()
	p.CloseBank()
	p.CloseShop()
}
func (m *Mob) ResetState() {
	m.UnsetVar("state")
}
func (p *Player) ResetAllExceptDueling() {
	p.ResetTrade()
	p.ResetTickAction()
	p.CloseOptionMenu()
	p.CloseBank()
	p.CloseShop()
}
func (p *Player) Fatigue() int {
	return p.Attributes.VarInt("fatigue", 0)
}
func (p *Player) SetFatigue(i int) {
	p.Attributes.SetVar("fatigue", i)
}