// Helper function to create a user (license) and an avatar for that user
func CreateUser(str string) {
	args := strings.Split(str, ",")
	if len(args) != 3 && len(args) != 4 {
		fmt.Println("Usage: server -createuser=email,password,avatar[,licensekey]")
		return
	}
	var up user
	up.New_WLwWLc(args[2])
	up.Email = args[0]
	up.License, up.Password = license.Make(args[1], "")
	up.License = args[3] // Override
	c := ephenationdb.New().C("counters")
	var id struct {
		C uint32
	}
	change := mgo.Change{
		Update: bson.M{"$inc": bson.M{"c": 1}},
	}
	_, err := c.FindId("avatarId").Apply(change, &id)
	if err != nil {
		fmt.Println("Failed to update unique counter 'avatarId' in collection 'counter'", err)
		return
	}
	up.Id = id.C
	db := ephenationdb.New()
	err = db.C("avatars").Insert(&up)
	if err != nil {
		log.Println("Save", up.Name, err)
		return
	}
	fmt.Println("Created avatar number", up.Id, ":", up.Name)
}
Beispiel #2
0
// Some rudimentary tests to get SQL connections
func DoTestSQL() bool {
	db := ephenationdb.New()
	DoTestCheck("DoTestSQL Got connection", db != nil)
	if db == nil {
		// Cancel the other tests
		return false
	}
	ephenationdb.Release(db)
	db2 := ephenationdb.New()
	DoTestCheck("DoTestSQL Got same (cached) connection again", db2 == db)
	db3 := ephenationdb.New()
	DoTestCheck("DoTestSQL Got new connection", db3 != db2 && db3 != nil)
	ephenationdb.Release(db3)
	ephenationdb.Release(db2)
	return true
}
Beispiel #3
0
func main() {
	if !*logOnStdout {
		logFile, _ := os.OpenFile(*logFileName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
		log.SetOutput(logFile)
	}
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

	cnfg, err := config.ReadDefault(*configFileName)
	if err != nil {
		log.Println("Fail to find", *configFileName, err)
		return
	}
	configSection := "db"
	if cnfg.HasSection(configSection) {
		f := func(key string) string {
			value, err := cnfg.String(configSection, key)
			if err != nil {
				log.Println("Config file", *configFileName, "Failt to find key", key, err)
				return ""
			}
			return value
		}
		err = ephenationdb.SetConnection(f)
		if err != nil {
			log.Println("main: open DB:", err)
			return
		}
	} else {
		log.Println("Config file", configFileName, "missing, or no section 'db'")
	}
	db := ephenationdb.New()
	chunkdata(db.C("chunkdata"))
}
Beispiel #4
0
// Return true if ok
func (up *user) Load_WLwBlWLc(email string) bool {
	// Connect to database
	db := ephenationdb.New()
	if db == nil {
		log.Println("No DB cpnnection", email)
		return false
	}
	err := db.C("avatars").Find(bson.M{"email": email}).One(&up.UserLoad)
	if err != nil {
		log.Println("Avatar for", email, err)
		return false
	}

	// Some post processing

	if up.Maxchunks == 0 {
		// This parameter was not initialized.
		up.Maxchunks = CnfgMaxOwnChunk
	}

	up.logonTimer = time.Now()

	if up.ReviveSP.X == 0 && up.ReviveSP.Y == 0 && up.ReviveSP.Z == 0 {
		// Check if there is any spawn point defined.
		up.ReviveSP = up.Coord
		up.HomeSP = up.Coord
	}

	score.Initialize(up.Id)
	return true
}
Beispiel #5
0
// Save the allocated chunks for the specified avatar
func SaveAvatar_Bl(avatar uint32, chunks []CC) bool {
	if *disableSave {
		// Ignore the save, pretend everything is fine
		return true
	}
	db = ephenationdb.New()
	if db == nil {
		return false
	}
	defer ephenationdb.Release(db)
	// First delete the current allocation, and replace with the new
	query := fmt.Sprintf("DELETE FROM chunkdata WHERE avatarID=%d", avatar)
	err := db.Query(query)
	if err != nil {
		log.Println(err)
		return false
	}
	if len(chunks) == 0 {
		return true
	}
	query = "INSERT INTO chunkdata (x,y,z,avatarID) VALUES "
	comma := ""
	for _, ch := range chunks {
		query += fmt.Sprintf("%s(%d,%d,%d,%d)", comma, ch.X, ch.Y, ch.Z, avatar)
		comma = ","
	}
	err = db.Query(query)
	if err != nil {
		log.Println(err)
		return false
	}
	return true
}
Beispiel #6
0
// Iterate over the copy of all scores, and save to SQL DB where needed.
func update(list []*territoryScore) {
	if *disableSQL {
		return
	}
	db := ephenationdb.New()
	if db == nil {
		return // Give it up for this time
	}
	defer ephenationdb.Release(db)
	now := time.Now()
	for _, ts := range list {
		if !ts.initialized {
			log.Println("score.update initialize from DB for", ts.uid)
			loadFromSQL(db, ts)
		}

		if !ts.modified {
			continue
		}

		oldScore := ts.Score
		oldBalance := ts.ScoreBalance
		ts.decay(&now)

		if ts.initialized {
			// Normally, it is initialized, but stay on the safe side if there was a problem.
			saveToSQL(db, ts)
			log.Printf("score.update %s to %.1f (diff %f), Balance %.1f (diff %f)\n", ts.name, ts.Score, ts.Score-oldScore, ts.ScoreBalance, ts.ScoreBalance-oldBalance)
		}
	}
}
Beispiel #7
0
// Load DB score data for territory owned by 'uid' into 'ts'.
func loadFromSQL(ts *territoryScore, uid uint32) {
	var avatarScore struct {
		TScoreTotal, TScoreBalance float64
		TScoreTime                 uint32
		Name                       string
		Territory                  []chunkdb.CC // The chunks allocated for this player.
	}
	db := ephenationdb.New()
	query := db.C("avatars").FindId(uid)
	err := query.One(&avatarScore)
	if err != nil {
		log.Println(err)
		return
	}

	ts.uid = uid
	ts.handicap = computeFactor(len(avatarScore.Territory))
	ts.Score = avatarScore.TScoreTotal
	ts.ScoreBalance = avatarScore.TScoreBalance
	ts.TimeStamp = time.Unix(int64(avatarScore.TScoreTime), 0)
	ts.name = avatarScore.Name
	ts.modified = true
	now := time.Now()
	ts.decay(&now) // Update the decay
}
Beispiel #8
0
// #258 update user last seen
func (lp *License) SaveLogonTime_Bl() {
	start := time.Now()
	db := ephenationdb.New()
	if db == nil {
		log.Println("license.SaveLogonTime: Failed to save logon time")
		return
	}

	defer ephenationdb.Release(db) // Defer database close when we are sure that the db is open

	// Update last seen online --> Moved from Save() due to issue #87: LastSeen not updated in database
	now := time.Now()
	nowstring := fmt.Sprintf("%4v-%02v-%02v", now.Year(), int(now.Month()), now.Day())

	query := "UPDATE users SET lastseen='" + nowstring + "' WHERE email='" + lp.mail + "'"
	//fmt.Printf("%v\n",query)
	if *verboseFlag > 0 {
		log.Println(query)
	}
	err := db.Query(query)

	if err != nil {
		log.Println(err)
		return
	}

	elapsed := time.Now().Sub(start)
	// if elapsed > UPPER_TIME_LIMIT && *verboseFlag > 0 {
	log.Printf("license.SaveLogonTime %d ms\n", elapsed/1e6)
	// }
	return
}
// Check the password of the player.
// Return false if connection shall be disonnected
func (up *user) CmdPassword_WLwWLuWLqBlWLc(encrPass []byte) bool {
	// The password is given by the client as an encrypted byte vector.
	// fmt.Printf("CmdPassword: New player encr passwd%v\n", encrPass)
	// Decrypt the password using the full license key.
	cipher, err := rc4.NewCipher(xorVector([]byte(up.License), up.challenge))
	if err != nil {
		log.Printf("CmdPassword: NewCipher1 returned %v\n", err)
		return false
	}
	passw := make([]byte, len(encrPass))
	cipher.XORKeyStream(passw, encrPass)
	// fmt.Printf("CmdPassword: Decrypted password is %#v\n", string(passw))
	if !license.VerifyPassword(string(passw), up.Password, encryptionSalt) {
		// fmt.Println("CmdPassword: stored password doesn't match the given")
		// CmdLogin_WLwWLuWLqBlWLc(up.Name, index)
		if *verboseFlag > 0 {
			log.Println("Terminate because of bad password")
		}
		return false
	}
	// Save player logon time
	up.Lastseen = time.Now()
	db := ephenationdb.New()
	err = db.C("avatars").UpdateId(up.Id, bson.M{"$set": bson.M{"lastseen": up.Lastseen}})
	if err != nil {
		log.Println("Update lastseen", err)
	}
	up.loginAck_WLuWLqBlWLa()
	return true
}
Beispiel #10
0
// Find the list of all chunks allocated for an avatar
// Return false in case of failure
func ReadAvatar_Bl(avatarID uint32) ([]CC, bool) {
	db = ephenationdb.New()
	if db == nil {
		log.Println("chunkdb.ReadAvatar_Bl failed")
		return nil, false
	}
	defer ephenationdb.Release(db)
	// Build a query for the given chunk coordinate as an argument
	query := fmt.Sprintf("SELECT * FROM chunkdata WHERE avatarID=%d", avatarID)
	err := db.Query(query)
	if err != nil {
		// Fatal error
		log.Println(err)
		os.Exit(1)
	}

	// Store the result
	result, err := db.StoreResult()
	if err != nil {
		log.Println(err)
		return nil, false
	}

	// Fetch all rows
	rows := result.FetchRows()
	numRows := result.RowCount()
	FieldNames := result.FetchFields()

	// fmt.Printf("chunkdb.ReadAvatar rows: %v. numRows: %v\n", rows, numRows)
	if rows == nil || numRows == 0 {
		db.FreeResult()
		return nil, true
	}

	ret := make([]CC, numRows)

	for r, row := range rows {
		cnt := int(result.FieldCount())
		for i := 0; i < cnt; i++ {
			switch FieldNames[i].Name {
			case "x":
				ret[r].X = int32(row[i].(int64))
			case "y":
				ret[r].Y = int32(row[i].(int64))
			case "z":
				ret[r].Z = int32(row[i].(int64))
			}
		}
	}

	db.FreeResult()

	// fmt.Printf("chunkdb.ReadAvatar: Avatar %d (%d,%d,%d)\n", avatarID, x, y, z)
	return ret, true
}
Beispiel #11
0
// Iterate over the copy of the list of all scores, and save to database where needed.
// The map could be used here, in which case a lock would be required. But DB access take
// a long time.
func update(list []*territoryScore) {
	db := ephenationdb.New()
	now := time.Now()
	for _, ts := range list {
		if !ts.modified {
			continue
		}

		// The decay isn't executed unless there has been a change, to save from unnecessary DB updates.
		ts.decay(&now)
		saveToSQL(db, ts)
	}
}
Beispiel #12
0
func (lp *License) Save_Bl() bool {
	start := time.Now()
	db := ephenationdb.New()
	if db == nil {
		return false
	}

	defer ephenationdb.Release(db) // Defer database close when we are sure that the db is open

	// Check if the user exists
	query := "SELECT * FROM users WHERE email='" + lp.mail + "'"
	err := db.Query(query)
	if err != nil {
		log.Println(err)
		return false
	}

	// Store the result
	result, err := db.StoreResult()
	if err != nil {
		log.Println(err)
		return false
	}

	if result.RowCount() == 0 {
		db.FreeResult()

		query = "INSERT INTO users SET email='" + lp.mail + "', password='******', isvalidated=1, "
		query = query + "licensekey='" + lp.License + "'"

		err = db.Query(query)
	} else {
		db.FreeResult()

		// Update password
		// TODO: check if password has changed? Add parameter in struct to know if changed or not?
		query = "UPDATE users SET password='******' WHERE email='" + lp.mail + "'"
		err = db.Query(query)
		if err != nil {
			log.Println(err)
			return false
		}

	}
	elapsed := time.Now().Sub(start)
	// if elapsed > UPPER_TIME_LIMIT && *verboseFlag > 0 {
	log.Printf("license.Save %d ms\n", elapsed/1e6)
	// }
	return true
}
Beispiel #13
0
// This is the main polling function, meant to be executed for ever as a go routine.
func Poll_Bl() {
	if *disableSave {
		log.Println("Saving to 'chunkdata' is disabled")
	}
	db = ephenationdb.New()
	if db == nil {
		log.Println("chunkdb.Poll_Bl requires access to SQL DB")
		return
	}
	for {
		req, ok := <-ch
		if !ok {
			log.Printf("Failed to read from channel\n")
			os.Exit(1)
		}
		avatarId := readChunk_Bl(req.chunk)
		req.cb(req.data, avatarId)
	}
}
Beispiel #14
0
// Return true if the save succeeded.
func (up *user) Save_Bl() bool {
	start := time.Now()
	up.Lastseen = start                                             // Update last seen online
	up.TimeOnline += uint32(start.Sub(up.logonTimer) / time.Second) // Update total time online, in seconds
	up.logonTimer = start
	db := ephenationdb.New()
	err := db.C("avatars").UpdateId(up.Id, bson.M{"$set": &up.player}) // Only update the fields found in 'pl'.

	if err != nil {
		log.Println("Save", up.Name, err)
		log.Printf("%#v\n", up)
		return false
	}

	if *verboseFlag > 1 {
		log.Printf("up.Save_Bl saved %v\n", up.Name)
	}
	elapsed := time.Now().Sub(start)
	if *verboseFlag > 0 {
		log.Printf("up.Save_Bl elapsed %d ms\n", elapsed/1e6)
	}
	return true
}
Beispiel #15
0
// TODO: Improve error handling
// Return true if the save succeeded.
func (pl *player) Save_Bl() bool {
	// Connect to database
	start := time.Now()
	db := ephenationdb.New()
	if db == nil {
		return false
	}

	// Do some preprocessing
	b, err := json.Marshal(pl)
	if err != nil {
		log.Printf("Marshal %s returned %v\n", pl.name, err)
	}

	inventory, err := pl.inventory.Serialize()

	// Update last seen online
	now := time.Now()
	nowstring := fmt.Sprintf("%4v-%02v-%02v", now.Year(), int(now.Month()), now.Day())

	// Update total time online, in seconds
	TempTimer := time.Now()
	pl.timeOnline += uint32(TempTimer.Sub(pl.logonTimer) / time.Second)
	//fmt.Printf("User has been online %v seconds\n", TempTimer - pl.logonTimer)
	pl.logonTimer = TempTimer // Use the stored value to avoid mismatch

	// Write data on alternative format
	// This section makes the following assumptions:
	// - Avatar name cannot be changed from the server
	// - Avatar looks (head/body) cannot be changed from the server TODO: THIS WILL EVENTUALLY NOT BE TRUE!
	// Booleans doesn't work, transform them to numbers
	query := "UPDATE avatars SET jsonstring=?,PositionX=?,PositionY=?,PositionZ=?,isFlying=?,isClimbing=?,isDead=?,DirHor=?,DirVert=?,AdminLevel=?" +
		",Level=?,Experience=?,HitPoints=?,Mana=?,Kills=?,HomeX=?,HomeY=?,HomeZ=?,ReviveX=?,ReviveY=?,ReviveZ=?" +
		",maxchunks=?,BlocksAdded=?,BlocksRemoved=?,TimeOnline=?,HeadType=?,BodyType=?,lastseen=?,inventory=?,TargetX=?,TargetY=?,TargetZ=?" +
		" WHERE name='" + pl.name + "'"

	stmt, err := db.Prepare(query)
	if err != nil {
		log.Println(err)
		return false
	}

	stmt.BindParams(b, pl.coord.X, pl.coord.Y, pl.coord.Z, Q(pl.flying), Q(pl.climbing), Q(pl.dead), pl.dirHor, pl.dirVert, pl.adminLevel, pl.level,
		pl.exp, pl.hitPoints, pl.mana, pl.numKill, pl.homeSP.X, pl.homeSP.Y, pl.homeSP.Z, pl.reviveSP.X, pl.reviveSP.Y, pl.reviveSP.Z, pl.maxchunks,
		pl.blockAdd, pl.blockRem, pl.timeOnline, pl.head, pl.body, nowstring, inventory, pl.targetCoor.X, pl.targetCoor.Y, pl.targetCoor.Z)

	err = stmt.Execute()
	if err != nil {
		log.Println(err)
		return false
	}

	ephenationdb.Release(db)
	if *verboseFlag > 1 {
		log.Printf("up.Save_Bl saved %v\n", pl.name)
	}
	elapsed := time.Now().Sub(start)
	if *verboseFlag > 0 {
		log.Printf("up.Save_Bl elapsed %d ms\n", elapsed/1e6)
	}
	return true
}
Beispiel #16
0
// Load a license. Return nil if error.
func Load_Bl(mail string) *License {
	var license License

	start := time.Now()
	db := ephenationdb.New()
	if db == nil {
		return nil
	}

	// Build a query for the user email sent as an argument
	query := "SELECT * FROM users WHERE email='" + mail + "'"
	if *verboseFlag > 0 {
		log.Println(query)
	}
	err := db.Query(query)
	if err != nil {
		log.Println(err)
		return nil
	}

	defer ephenationdb.Release(db) // Defer database release when we are sure that the db is open

	// Store the result
	result, err := db.UseResult()
	if err != nil {
		log.Println(err)
		return nil
	}

	// Fetch row
	row := result.FetchRow()
	if row == nil {
		if *verboseFlag > 0 {
			log.Printf("No result for %v\n", mail)
		}
		db.FreeResult()
		return nil
	}

	// Assign mail as license.mail
	license.mail = mail
	idnumfields := 0

	const (
		ID_MAIL  = 1 << iota
		ID_LIC   = 1 << iota
		ID_PASSW = 1 << iota
		ID_LAST  = 1 << iota
	)

	// TODO: Read more information: license type, valid until (if license terminates during connection)
	for x := 0; x < int(result.FieldCount()); x++ {
		FieldName := result.FetchField().Name
		switch FieldName {
		case "email":
			license.mail = fmt.Sprint(row[x])
			idnumfields |= ID_MAIL
		case "licensekey":
			license.License = fmt.Sprint(row[x])
			idnumfields |= ID_LIC
		case "password":
			license.Password = fmt.Sprint(row[x])
			idnumfields |= ID_PASSW
		case "lastseen":
			license.LastSeen = fmt.Sprintf("%s", row[x])
			idnumfields |= ID_LAST
		}
	}

	db.FreeResult() // Required to get back in sync

	// If all fields have been identified, continue, otherwise return failure
	if idnumfields != ID_MAIL|ID_LIC|ID_PASSW|ID_LAST {
		if *verboseFlag > 0 {
			log.Printf("Not proper content: %v\n", idnumfields)
		}
		return nil // return fail
	}

	// Read avatar names
	query = "SELECT name FROM avatars WHERE owner='" + license.mail + "'"
	if *verboseFlag > 0 {
		log.Println(query)
	}
	err = db.Query(query)
	if err != nil {
		log.Println(err)
		return nil
	}

	// Store the result
	result, err = db.StoreResult()
	if err != nil {
		log.Println(err)
		return nil
	}

	rowcount := result.RowCount()
	temps := make([]string, rowcount)

	for x := 0; x < int(rowcount); x++ {
		// Fetch row
		row = result.FetchRow()
		if row == nil {
			// TODO: Error handling
		}
		temps[x] = fmt.Sprint(row[0])
	}

	db.FreeResult()

	license.Names = temps

	if *verboseFlag > 0 {
		log.Printf("license.Load done: %v\n", license)
		elapsed := time.Now().Sub(start)
		if elapsed > UPPER_TIME_LIMIT {
			log.Printf("license.Load %d ms\n", elapsed/1e6)
		}
	}

	return &license
}
Beispiel #17
0
// Return true if ok, and the uid of the player.
// TODO: Improve error handlng
func (pl *player) Load_WLwBlWLc(name string) (uint32, bool) {
	// Connect to database
	db := ephenationdb.New()
	if db == nil {
		return 0, false
	}
	defer ephenationdb.Release(db)

	// Build a query for the avatar name sent as an argument
	// TODO: Assert that the avatar name is unique and on this server for the current user?
	query := "SELECT jsonstring,id,PositionX,PositionY,PositionZ,isFlying,isClimbing,isDead,DirHor,DirVert,AdminLevel,Level,Experience,HitPoints,Mana,Kills,HomeX,HomeY,HomeZ,ReviveX,ReviveY,ReviveZ,maxchunks,BlocksAdded,BlocksRemoved,TimeOnline,HeadType,BodyType,inventory,TScoreTotal,TScoreBalance,TScoreTime,TargetX,TargetY,TargetZ FROM avatars WHERE name='" + name + "'"
	stmt, err := db.Prepare(query)
	if err != nil {
		log.Println(err)
		return 0, false
	}

	// Execute statement
	err = stmt.Execute()
	if err != nil {
		log.Println(err)
		return 0, false
	}

	// Some helper variables
	var packedline string
	var uid uint32
	var packedInv []byte
	var terrScore, terrScoreBalance float64
	var terrScoreTimestamp uint32
	// Booleans doesn't work
	var flying, climbing, dead int
	stmt.BindResult(&packedline, &uid, &pl.coord.X, &pl.coord.Y, &pl.coord.Z, &flying, &climbing, &dead, &pl.dirHor, &pl.dirVert, &pl.adminLevel, &pl.level,
		&pl.exp, &pl.hitPoints, &pl.mana, &pl.numKill, &pl.homeSP.X, &pl.homeSP.Y, &pl.homeSP.Z, &pl.reviveSP.X, &pl.reviveSP.Y, &pl.reviveSP.Z, &pl.maxchunks,
		&pl.blockAdd, &pl.blockRem, &pl.timeOnline, &pl.head, &pl.body, &packedInv, &terrScore, &terrScoreBalance, &terrScoreTimestamp,
		&pl.targetCoor.X, &pl.targetCoor.Y, &pl.targetCoor.Z)

	for {
		eof, err := stmt.Fetch()
		if err != nil {
			log.Println(err)
			return 0, false
		}
		if eof {
			break
		}
	}

	// log.Println(pl.targetCoor)

	// Some post processing
	pl.name = name
	if flying == 1 {
		pl.flying = true
	}
	if climbing == 1 {
		pl.climbing = true
	}
	if dead == 1 {
		pl.dead = true
	}

	if pl.maxchunks == -1 {
		// This parameter was not initialized.
		pl.maxchunks = CnfgMaxOwnChunk
	}

	pl.logonTimer = time.Now()

	if err = json.Unmarshal([]uint8(packedline), pl); err != nil {
		log.Printf("Unmarshal player %s: %v (%v)\n", name, err, packedline)
		// TODO: This covers errors when updating the jsonstring, should be handled in a more approperiate way
		//return 0, false
	}

	// If there was data in the inventory "blob", unpack it.
	if len(packedInv) > 0 {
		err = pl.inventory.Unpack([]byte(packedInv))
		if err != nil {
			log.Println("Failed to unpack", err, packedInv)
		}
		// Save what can be saved, and remove unknown objects.
		pl.inventory.CleanUp()
	}
	if *verboseFlag > 1 {
		log.Println("Inventory unpacked", pl.inventory)
	}

	//fmt.Printf("Coord: (%v,%v,%v)\n", pl.coord.X, pl.coord.Y, pl.coord.Z )

	if pl.reviveSP.X == 0 && pl.reviveSP.Y == 0 && pl.reviveSP.Z == 0 {
		// Check if there is any spawn point defined.
		pl.reviveSP = pl.coord
		pl.homeSP = pl.coord
	}

	// Load the allocated territories. This is loaded every time a player logs in, but not at logout or player save.
	// It will however be updated immediately when the player changes his allocation.
	terr, ok := chunkdb.ReadAvatar_Bl(uint32(uid))
	if !ok {
		return 0, false
	}
	pl.territory = terr
	score.Initialize(uid, terrScore, terrScoreBalance, terrScoreTimestamp, name, len(terr))
	// fmt.Printf("User: %#v\n", pl)
	return uint32(uid), true
}
Beispiel #18
0
func DumpSQL() {
	db := ephenationdb.New()
	if db == nil {
		return
	}
	defer ephenationdb.Release(db)

	// Build a query for the avatar name sent as an argument
	// TODO: Assert that the avatar name is unique and on this server for the current user?
	query := "SELECT name,jsonstring,id,PositionX,PositionY,PositionZ,isFlying,isClimbing,isDead,DirHor,DirVert,AdminLevel,Level,Experience,HitPoints,Mana,Kills,HomeX,HomeY,HomeZ,ReviveX,ReviveY,ReviveZ,maxchunks,BlocksAdded,BlocksRemoved,TimeOnline,HeadType,BodyType,inventory,TScoreTotal,TScoreBalance,TScoreTime,TargetX,TargetY,TargetZ FROM avatars"
	stmt, err := db.Prepare(query)
	if err != nil {
		log.Println(err)
		return
	}

	// Execute statement
	err = stmt.Execute()
	if err != nil {
		log.Println(err)
		return
	}

	// Some helper variables
	var packedline string
	var uid uint32
	var packedInv []byte
	var terrScore, terrScoreBalance float64
	var terrScoreTimestamp uint32
	// Booleans doesn't work
	var flying, climbing, dead int
	var pl player
	stmt.BindResult(&pl.name, &packedline, &uid, &pl.coord.X, &pl.coord.Y, &pl.coord.Z, &flying, &climbing, &dead, &pl.dirHor, &pl.dirVert, &pl.adminLevel, &pl.level,
		&pl.exp, &pl.hitPoints, &pl.mana, &pl.numKill, &pl.homeSP.X, &pl.homeSP.Y, &pl.homeSP.Z, &pl.reviveSP.X, &pl.reviveSP.Y, &pl.reviveSP.Z, &pl.maxchunks,
		&pl.blockAdd, &pl.blockRem, &pl.timeOnline, &pl.head, &pl.body, &packedInv, &terrScore, &terrScoreBalance, &terrScoreTimestamp,
		&pl.targetCoor.X, &pl.targetCoor.Y, &pl.targetCoor.Z)

	for {
		eof, err := stmt.Fetch()
		if err != nil {
			log.Println(err)
			return
		}
		if eof {
			break
		}
		// Some post processing
		if flying == 1 {
			pl.flying = true
		}
		if climbing == 1 {
			pl.climbing = true
		}
		if dead == 1 {
			pl.dead = true
		}

		if pl.maxchunks == -1 {
			// This parameter was not initialized.
			pl.maxchunks = CnfgMaxOwnChunk
		}
		DumpSQLPlayer(&pl, packedline, packedInv)
	}
}