/**
* Copyright 1999-2009 The Pegadi Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Server for recording scores.
*
* @author HÃ¥vard Wigtil <havardw at pvv.org>
* @author Jan-Preben Mossin <jpmossin@underdusken.no>
* @author Marvin B. Lillehaug <lillehau@underdusken.no>
*/
package org.pegadi.server.score;
import no.dusken.common.model.Person;
import org.pegadi.games.Score;
import org.pegadi.games.tetris.TetrisScore;
import org.pegadi.server.ScoreServer;
import org.pegadi.server.user.UserServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import javax.sql.DataSource;
import java.sql.*;
import java.util.*;
import java.util.Date;
public class ScoreServerImpl implements ScoreServer {
/**
* User server to look up user names.
*/
protected UserServer userServer;
private TetrisScoreRowMapper mapper;
private JdbcTemplate template;
private final Logger log = LoggerFactory.getLogger(getClass());
public ScoreServerImpl() {
mapper = new TetrisScoreRowMapper();
}
public void setDataSource(DataSource dataSource) {
template = new JdbcTemplate(dataSource);
}
public void setUserServer(UserServer userServer) {
this.userServer = userServer;
}
/**
* Starts score recording for a new game. The <code>Score</code> object that is
* returned <i>must</i> be used when calling {@link #updateScore } and
* {@link #endGame }, as each score has an unique ID.
*
* @param domain The domain for the game.
* @return A new Score object, with the score set to 0. If the domain is not known,
* this method will return <code>null</code>.
*/
public Score startGame(final Person person, String domain) {
if(domain.equals("tetris")) {
final String insertSql = "insert into score_tetris (userID, score, level, linecount, starttime, active) values (?, ?, ?, ?, ?, true)";
KeyHolder keyHolder = new GeneratedKeyHolder(); // The key/id of the new score is needed
template.update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement statement = con.prepareStatement(insertSql, new String[] {"ID"});
statement.setString(1, person.getUsername());
statement.setLong(2, 0); // score
statement.setInt(3,1); // level
statement.setInt(4,0); // lines
statement.setTimestamp(5, new Timestamp((new GregorianCalendar()).getTimeInMillis()));
return statement;
}
}, keyHolder );
int scoreId = keyHolder.getKey().intValue();
return new TetrisScore(scoreId, person.getUsername(), person.getName(),1);
}
else {
log.error("Domain '{}' not implemented yet!", domain);
return null;
}
}
/**
* Updates the score for a running game. The <code>Score</code> object must have the
* same ID as the object returned by {@link #startGame}, and the client must set the
* new value for score before updating.
*
* @param score The current score.
*/
public void updateScore(Score score) {
if(score instanceof TetrisScore) {
TetrisScore tscore = (TetrisScore) score;
template.update("Update score_tetris Set score=?, level=?, linecount=? Where ID=? And userID=?",
tscore.getScore(),
tscore.getLevel(),
tscore.getLines(),
tscore.getID(),
tscore.getUserID());
}
else {
log.error("Update for Score not implemented yet!");
}
}
/**
* Records the final score for a game.The <code>Score</code> object must have the
* same ID as the object returned by {@link #startGame}, and the client must set the
* final value for the score.
*
* @param score The final score.
* @return The same score object, with the <code>active</code> property set to false.
* @see org.pegadi.games.Score#isActive
*/
public Score endGame(Score score) {
if(score instanceof TetrisScore) {
TetrisScore tscore = (TetrisScore) score;
template.update("update score_tetris set score=?, level=?, linecount=?, starttime=?, active=false where ID=? and userID=?",
tscore.getScore(),
tscore.getLevel(),
tscore.getLines(),
new Timestamp((new GregorianCalendar()).getTimeInMillis()),
tscore.getID(),
tscore.getUserID() );
Date date = new Date();
long rank = getTetrisRank(tscore.getID());
return new TetrisScore(tscore.getID(),
tscore.getUserID(),
tscore.getUserName(),
rank,
tscore.getScore(),
tscore.getLevel(),
tscore.getLines(),
date);
}
else {
log.error("Update for Score not implemented yet!");
return null;
}
}
/**
* Cancels a game in progress.
*
* @param score The game to cancel.
*/
public void cancelGame(String userID, Score score) {
}
/**
* Returns the <code>count</code> best scores ever.
* Because user information is not in the same db as tetris scores
* filtering for actives uses UserServer
*
* @param count Number of scores to return.
* @param domain The game domain
* @return List of scores.
* @see org.pegadi.games.Score
*/
public List<? extends Score> getHighScore(String domain, int count, boolean activesOnly) {
if(domain.equals("tetris")) {
if(activesOnly) {
return getActiveTetrisHighScores(count);
}
else {
return getTetrisHighScores(0, count);
}
}
else {
log.error("Domain not implemented yet!");
return null;
}
}
private List<TetrisScore> getTetrisHighScores(int start, int count) {
String query = "select * from score_tetris where active=? order by score desc limit ?,?";
return template.query(query, mapper, false, start, count);
}
// This method querys the db for more scores until activeScores.size = count
private List<TetrisScore> getActiveTetrisHighScores(int count) {
List<TetrisScore> activeScores = new ArrayList<TetrisScore>(count);
List<TetrisScore> allScores;
int start = 0;
do {
allScores = getTetrisHighScores(start, count);
for(TetrisScore score : allScores) {
String userId = score.getUserID();
if(userServer.isActive(userId)) {
activeScores.add(score);
}
}
start += count;
} while(activeScores.size() < count);
return activeScores;
}
/**
* Returns the <code>count</code> best scores for the given user ID.
*
* @param userID user ID to return scores for.
* @param count Number of scores to return.
* @param domain The game domain
* @return List of scores.
* @see org.pegadi.games.Score
*/
public List<? extends Score> getUserScore(String domain, String userID, int count) {
if(domain.equals("tetris")) {
return template.query("select * from score_tetris where userID=? and active=false order by score Desc limit ?", mapper, userID, count);
}
else {
log.error("Domain not implemented yet!");
return null;
}
}
/**
* Returns the <code>count</code> best scores the given date.
*
* @param day The date to return scores from.
* @param count Number of scores to return.
* @param domain The game domain
* @return List of scores.
* @see org.pegadi.games.Score
*/
public List<? extends Score> getDayScore(String domain, Date day, int count) {
if(domain.equals("tetris")) {
Calendar cal = Calendar.getInstance();
Date start, end;
cal.setTime(day);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
start = cal.getTime();
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
end = cal.getTime();
Timestamp startstamp = new Timestamp(start.getTime());
Timestamp endstamp = new Timestamp(end.getTime());
return template.query("select * from score_tetris where starttime > ? and starttime < ? order by score desc limit ?", mapper, startstamp, endstamp, count);
}
else {
log.error("Domain '{}' not implemented yet.", domain);
return null;
}
}
/**
* Returns the score's rank in the database.
* This is tyhe number of scores in the database that are better
* than <code>score</code> + 1;
*/
private long getTetrisRank(long score) {
long rank = template.queryForLong("select count(*) from score_tetris where score > ?", score);
return (++rank);
}
private class TetrisScoreRowMapper implements ParameterizedRowMapper<TetrisScore> {
public TetrisScore mapRow(ResultSet rs, int i) throws SQLException {
long score = rs.getLong("score");
String username = rs.getString("userID");
Person user = userServer.getUserByUsername(username);
String name = user.getName();
return new TetrisScore(rs.getLong("ID"),
username,
name,
getTetrisRank(score),
score,
rs.getInt("level"),
rs.getInt("linecount"),
rs.getTimestamp("starttime"));
}
}
}