func (u *User) Name() string {
	return u.name
}
Name gets the name of the User.
package core
import (
	"errors"
	"github.com/gorilla/websocket"
	"github.com/hewiefreeman/GopherGameServer/database"
	"github.com/hewiefreeman/GopherGameServer/helpers"
	"sync"
)
type User struct {
	name       string
	databaseID int
	isGuest    bool
	mux     sync.Mutex
	status  int
	friends map[string]*database.Friend
	conns   map[string]*userConn
}
type userConn struct {
	clientMux *sync.Mutex
	user      **User
	socket *websocket.Conn
	room *Room
	vars map[string]interface{}
}
var (
	users    map[string]*User = make(map[string]*User)
	usersMux sync.Mutex
	LoginCallback func(string, int, map[string]interface{}, map[string]interface{}) bool
	LogoutCallback func(string, int)
)
const (
	StatusAvailable = iota  
	StatusInGame            
	StatusIdle              
	StatusOffline           
)
const (
	errorDenied         = "Action was denied"
	errorRequiredName   = "A user name is required"
	errorRequiredID     = "An ID is required"
	errorRequiredSocket = "A socket is required"
	errorNameUnavail    = "Username is unavailable"
	errorUnexpected     = "Unexpected error"
	errorAlreadyLogged  = "User is already logged in"
	errorServerPaused   = "Server is paused"
)
func Login(userName string, dbID int, autologPass string, isGuest bool, remMe bool, socket *websocket.Conn,
	connUser **User, clientMux *sync.Mutex) (string, helpers.GopherError) {
	if serverPaused {
		return "", helpers.NewError(errorServerPaused, helpers.ErrorServerPaused)
	} else if len(userName) == 0 {
		return "", helpers.NewError(errorRequiredName, helpers.ErrorAuthRequiredName)
	} else if userName == serverName {
		return "", helpers.NewError(errorNameUnavail, helpers.ErrorAuthNameUnavail)
	} else if dbID < -1 {
		return "", helpers.NewError(errorRequiredID, helpers.ErrorAuthRequiredID)
	} else if socket == nil {
		return "", helpers.NewError(errorRequiredSocket, helpers.ErrorAuthRequiredSocket)
	}
	databaseID := dbID
	if isGuest {
		databaseID = -1
	}
	if LoginCallback != nil && !LoginCallback(userName, dbID, nil, nil) {
		return "", helpers.NewError(errorDenied, helpers.ErrorActionDenied)
	}
	var connID string
	var connErr error
	var userExists bool = false
	usersMux.Lock()
	if userOnline, ok := users[userName]; ok {
		userExists = true
		if kickOnLogin {
			userOnline.mux.Lock()
			for connKey, conn := range userOnline.conns {
				userRoom := (*conn).room
				if userRoom != nil && userRoom.Name() != "" {
					userOnline.mux.Unlock()
					userRoom.RemoveUser(userOnline, connKey)
					userOnline.mux.Lock()
				}
				(*(*conn).clientMux).Lock()
				*((*conn).user) = nil
				(*(*conn).clientMux).Unlock()
				clientResp := helpers.MakeClientResponse(helpers.ClientActionLogout, nil, helpers.NoError())
				(*conn).socket.WriteJSON(clientResp)
			}
			userOnline.mux.Unlock()
			delete(users, userName)
			connID = "1"
			userExists = false
		} else if multiConnect {
			for {
				connID, connErr = helpers.GenerateSecureString(5)
				if connErr != nil {
					usersMux.Unlock()
					return "", helpers.NewError(errorUnexpected, helpers.ErrorAuthUnexpected)
				}
				userOnline.mux.Lock()
				if _, found := (*userOnline).conns[connID]; !found {
					userOnline.mux.Unlock()
					break
				}
				userOnline.mux.Unlock()
			}
		} else {
			usersMux.Unlock()
			return "", helpers.NewError(errorAlreadyLogged, helpers.ErrorAuthAlreadyLogged)
		}
	} else if multiConnect {
		connID, connErr = helpers.GenerateSecureString(5)
		if connErr != nil {
			usersMux.Unlock()
			return "", helpers.NewError(errorUnexpected, helpers.ErrorAuthUnexpected)
		}
	} else {
		connID = "1"
	}
	vars := make(map[string]interface{})
	conn := userConn{socket: socket, room: nil, vars: vars, user: connUser, clientMux: clientMux}
	var u *User
	var friends []map[string]interface{}
	var friendsMap map[string]*database.Friend
	if userExists {
		(*users[userName]).mux.Lock()
		(*users[userName]).conns[connID] = &conn
		friendsMap = (*users[userName]).friends
		(*users[userName]).mux.Unlock()
		friends = makeFriendsResponse(friendsMap)
	} else {
		if dbID != -1 && sqlFeatures {
			var friendsErr error
			if friendsMap, friendsErr = database.GetFriends(dbID); friendsErr == nil {
				friends = makeFriendsResponse(friendsMap)
			}
		}
		conns := map[string]*userConn{
			connID: &conn,
		}
		newUser := User{name: userName, databaseID: databaseID, isGuest: isGuest, status: 0,
			friends: friendsMap, conns: conns}
		u = &newUser
		users[userName] = u
	}
	(*conn.clientMux).Lock()
	*(conn.user) = users[userName]
	(*conn.clientMux).Unlock()
	usersMux.Unlock()
	statusMessage := map[string]map[string]interface{}{
		helpers.ServerActionFriendStatusChange: {
			"n": userName,
			"s": 0,
		},
	}
	u.sendToFriends(statusMessage)
	var responseVal map[string]interface{}
	if rememberMe && len(autologPass) > 0 && remMe {
		responseVal = map[string]interface{}{
			"n": userName,
			"f": friends,
			"ai": dbID,
			"ap": autologPass,
		}
	} else {
		responseVal = map[string]interface{}{
			"n": userName,
			"f": friends,
		}
	}
	clientResp := helpers.MakeClientResponse(helpers.ClientActionLogin, responseVal, helpers.NoError())
	socket.WriteJSON(clientResp)
	return connID, helpers.NoError()
}
func makeFriendsResponse(friendsMap map[string]*database.Friend) []map[string]interface{} {
	friends := make([]map[string]interface{}, len(friendsMap), len(friendsMap))
	i := 0;
	for _, val := range friendsMap {
		frs := val.RequestStatus()
		friendEntry := map[string]interface{}{
			"n":  val.Name(),
			"rs": frs,
		}
		if frs == database.FriendStatusAccepted {
			if friend, ok := users[val.Name()]; ok {
				friendEntry["s"] = friend.Status()
			} else {
				friendEntry["s"] = StatusOffline
			}
		}
		friends[i] = friendEntry
		i++;
	}
	return friends
}
func AutoLogIn(tag string, pass string, newPass string, dbID int, conn *websocket.Conn, connUser **User, clientMux *sync.Mutex) (string, helpers.GopherError) {
	if serverPaused {
		return "", helpers.NewError(errorServerPaused, helpers.ErrorServerPaused)
	}
	userName, autoLogErr := database.AutoLoginClient(tag, pass, newPass, dbID)
	if autoLogErr.ID != 0 {
		return "", autoLogErr
	}
	connID, userErr := Login(userName, dbID, newPass, false, true, conn, connUser, clientMux)
	if userErr.ID != 0 {
		return "", userErr
	}
	return connID, helpers.NoError()
}
func (u *User) Logout(connID string) {
	if multiConnect && len(connID) == 0 {
		return
	} else if !multiConnect {
		connID = "1"
	}
	u.mux.Lock()
	if _, ok := u.conns[connID]; !ok {
		u.mux.Unlock()
		return
	}
	currRoom := (*u.conns[connID]).room
	if currRoom != nil && currRoom.Name() != "" {
		u.mux.Unlock()
		currRoom.RemoveUser(u, connID)
		u.mux.Lock()
	}
	if len(u.conns) == 1 {
		statusMessage := map[string]map[string]interface{}{
			helpers.ServerActionFriendStatusChange: {
				"n": u.name,
				"s": StatusOffline,
			},
		}
		u.sendToFriends(statusMessage)
	}
	(*u.conns[connID]).clientMux.Lock()
	if *((*u.conns[connID]).user) != nil {
		*((*u.conns[connID]).user) = nil
	}
	(*u.conns[connID]).clientMux.Unlock()
	socket := (*u.conns[connID]).socket
	delete(u.conns, connID)
	if len(u.conns) == 0 {
		u.mux.Unlock()
		usersMux.Lock()
		delete(users, u.name)
		usersMux.Unlock()
	} else {
		u.mux.Unlock()
	}
	clientResp := helpers.MakeClientResponse(helpers.ClientActionLogout, nil, helpers.NoError())
	socket.WriteJSON(clientResp)
	if LogoutCallback != nil {
		LogoutCallback(u.Name(), u.DatabaseID())
	}
}
func (u *User) Kick() {
	u.mux.Lock()
	statusMessage := map[string]map[string]interface{}{
		helpers.ServerActionFriendStatusChange: {
			"n": u.name,
			"s": StatusOffline,
		},
	}
	u.sendToFriends(statusMessage)
	clientResp := helpers.MakeClientResponse(helpers.ClientActionLogout, nil, helpers.NoError())
	for connID, conn := range u.conns {
		currRoom := (*conn).room
		if currRoom != nil && currRoom.Name() != "" {
			u.mux.Unlock()
			currRoom.RemoveUser(u, connID)
			u.mux.Lock()
		}
		(*conn).clientMux.Lock()
		if *((*conn).user) != nil {
			*((*conn).user) = nil
		}
		(*conn).clientMux.Unlock()
		(*conn).socket.WriteJSON(clientResp)
	}
	u.mux.Unlock()
	usersMux.Lock()
	delete(users, u.name)
	usersMux.Unlock()
	if LogoutCallback != nil {
		LogoutCallback(u.Name(), u.DatabaseID())
	}
}
func GetUser(userName string) (*User, error) {
	if len(userName) == 0 {
		return &User{}, errors.New("users.Get() requires a user name")
	} else if serverPaused {
		return &User{}, errors.New(errorServerPaused)
	}
	var user *User
	var ok bool
	usersMux.Lock()
	if user, ok = users[userName]; !ok {
		usersMux.Unlock()
		return &User{}, errors.New("User '" + userName + "' is not logged in")
	}
	usersMux.Unlock()
	return user, nil
}
func (u *User) Join(r *Room, connID string) error {
	if multiConnect && len(connID) == 0 {
		return errors.New("Must provide a connID when MultiConnect is enabled")
	} else if !multiConnect {
		connID = "1"
	}
	u.mux.Lock()
	if _, ok := u.conns[connID]; !ok {
		u.mux.Unlock()
		return errors.New("Invalid connID")
	}
	currRoom := (*u.conns[connID]).room
	if currRoom != nil && currRoom.Name() == r.Name() {
		u.mux.Unlock()
		return errors.New("User '" + u.name + "' is already in room '" + r.Name() + "'")
	} else if currRoom != nil && currRoom.Name() != "" {
		u.mux.Unlock()
		u.Leave(connID)
		u.mux.Lock()
	}
	u.mux.Unlock()
	addErr := r.AddUser(u, connID)
	if addErr != nil {
		return addErr
	}
	return nil
}
func (u *User) Leave(connID string) error {
	if multiConnect && len(connID) == 0 {
		return errors.New("Must provide a connID when MultiConnect is enabled")
	} else if !multiConnect {
		connID = "1"
	}
	u.mux.Lock()
	if _, ok := u.conns[connID]; !ok {
		u.mux.Unlock()
		return errors.New("Invalid connID")
	}
	currRoom := (*u.conns[connID]).room
	u.mux.Unlock()
	if currRoom != nil && currRoom.Name() != "" {
		removeErr := currRoom.RemoveUser(u, connID)
		if removeErr != nil {
			return removeErr
		}
	} else {
		return errors.New("User '" + u.name + "' is not in a room.")
	}
	return nil
}
func (u *User) SetStatus(status int) {
	u.mux.Lock()
	u.status = status
	u.mux.Unlock()
	message := map[string]map[string]interface{}{
		helpers.ServerActionFriendStatusChange: {
			"n": u.name,
			"s": status,
		},
	}
	u.sendToFriends(message)
}
func (u *User) Invite(invUser *User, connID string) error {
	if multiConnect && len(connID) == 0 {
		return errors.New("Must provide a connID when MultiConnect is enabled")
	} else if !multiConnect {
		connID = "1"
	}
	u.mux.Lock()
	if _, ok := u.conns[connID]; !ok {
		u.mux.Unlock()
		return errors.New("Invalid connID")
	}
	currRoom := (*u.conns[connID]).room
	u.mux.Unlock()
	rType := GetRoomTypes()[currRoom.Type()]
	if currRoom == nil || currRoom.Name() == "" {
		return errors.New("The user '" + u.name + "' is not in a room")
	} else if !currRoom.IsPrivate() {
		return errors.New("The room '" + currRoom.Name() + "' is not private")
	} else if currRoom.Owner() != u.name {
		return errors.New("The user '" + u.name + "' is not the owner of the room '" + currRoom.Name() + "'")
	} else if rType.ServerOnly() {
		return errors.New("Only the server can manipulate that type of room")
	}
	addErr := currRoom.AddInvite(invUser.name)
	if addErr != nil {
		return addErr
	}
	invMessage := map[string]map[string]interface{}{
		helpers.ServerActionRoomInvite: {
			"u": u.name,
			"r": currRoom.Name(),
		},
	}
	invUser.mux.Lock()
	for _, conn := range invUser.conns {
		(*conn).socket.WriteJSON(invMessage)
	}
	invUser.mux.Unlock()
	return nil
}
func (u *User) RevokeInvite(revokeUser string, connID string) error {
	if multiConnect && len(connID) == 0 {
		return errors.New("Must provide a connID when MultiConnect is enabled")
	} else if !multiConnect {
		connID = "1"
	}
	u.mux.Lock()
	if _, ok := u.conns[connID]; !ok {
		u.mux.Unlock()
		return errors.New("Invalid connID")
	}
	currRoom := (*u.conns[connID]).room
	u.mux.Unlock()
	rType := GetRoomTypes()[currRoom.Type()]
	if currRoom == nil || currRoom.Name() == "" {
		return errors.New("The user '" + u.name + "' is not in a room")
	} else if !currRoom.IsPrivate() {
		return errors.New("The room '" + currRoom.Name() + "' is not private")
	} else if currRoom.Owner() != u.name {
		return errors.New("The user '" + u.name + "' is not the owner of the room '" + currRoom.Name() + "'")
	} else if rType.ServerOnly() {
		return errors.New("Only the server can manipulate that type of room")
	}
	removeErr := currRoom.RemoveInvite(revokeUser)
	if removeErr != nil {
		return removeErr
	}
	return nil
}
func UserCount() int {
	usersMux.Lock()
	length := len(users)
	usersMux.Unlock()
	return length
}