// #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
}
// 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
}
Exemple #3
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
}
Exemple #4
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)
		}
	}
}
// 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
}
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
}
// 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
}
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)
	}
}
Exemple #9
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
}
Exemple #10
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
}