func (p *Player) SetConnected(flag bool) {
	p.SetVar("connected", flag)
}
SetConnected sets the player's connected status to flag.
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)
}