/*
* Copyright (c) 2003 Shaven Puppy Ltd
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Shaven Puppy' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.puppygames.applet.server;
import java.rmi.Remote;
import java.rmi.server.RemoteServer;
import java.rmi.server.UnicastRemoteObject;
import java.sql.*;
import java.util.*;
import net.puppygames.applet.*;
import net.puppygames.gamecommerce.server.*;
/**
* $Id: AppletHiscoreServer.java,v 1.22 2008/11/12 16:47:32 foo Exp $ Super
* Dudester hiscore server
* <p>
*
* @author $Author: foo $
* @version $Revision: 1.22 $
*/
public class AppletHiscoreServer extends BaseServerImpl implements AppletHiscoreServerRemote, Remote {
/** A Map of TreeSets */
private final Map hiscores = new HashMap();
/** Naughty words */
private static final String[] BAD_WORDS = {"motherfucker", "fucka", "cunty", "cunt", "fuck", "shite", "shitt", "shit", "cock",
"dick", "bitch", "crap", "turd", "ass", "arse", "slut", "penis", "balls", "bastard", "mother"};
private static final String[][] BAD_WORDS2 = { {"hate", "love"}, {"hamas", "hummus"}};
/** Words that get you banned immediately */
private static final String[] BAD_WORDS3 = {"fuck", "cunt", "shit"};
/**
* C'tor
*
* @param server
* @param appTitle
* @param remoteName
* @throws Exception
*/
public AppletHiscoreServer(GamecommerceServerLocal server) throws Exception {
super(server, "Applet hiscore server", AppletHiscoreServerRemote.REMOTE_NAME);
loadHiScores();
}
private synchronized void loadHiScores() throws Exception {
// Load the hiscores
new SQLExec("LoadScores", server.getConnectionCache()) {
@Override
public Object exec(Connection conn) throws SQLException, Exception {
PreparedStatement st = null;
ResultSet rs = null;
try {
st = conn
.prepareStatement("select ahs.game, ahs.scoregroup, ahs.name, ahs.installation, ahs.points, ahs.medals, ahs.registered, ahs.version "
+ " from applethiscores ahs join games on ahs.game = games.game and ahs.version = games.currentversion "
+ " where date > ?");
GregorianCalendar c = new GregorianCalendar();
c.add(Calendar.WEEK_OF_YEAR, -1);
java.util.Date d = c.getTime();
st.setDate(1, new java.sql.Date(d.getTime()));
rs = st.executeQuery();
while (rs.next()) {
Score score = new Score(rs.getString(1), "none", rs.getString(2), rs.getString(3), rs.getLong(4), rs
.getInt(5), rs.getString(6), rs.getBoolean(7));
TreeSet scores = (TreeSet) hiscores.get(score.getGame());
if (scores == null) {
scores = new TreeSet();
hiscores.put(score.getGame(), scores);
}
scores.add(score);
}
return null;
} finally {
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
}
try {
if (st != null) {
st.close();
}
} catch (Exception e) {
}
}
}
}.action();
// Calculate ranks
for (Iterator i = hiscores.values().iterator(); i.hasNext();) {
TreeSet table = (TreeSet) i.next();
int count = 0;
for (Iterator j = table.iterator(); j.hasNext();) {
Score s = (Score) j.next();
if (count == MAX_SCORES) {
j.remove();
} else {
s.setRank(count++);
}
}
}
}
private synchronized void doLoadHiscores(Connection conn, String game) throws SQLException {
PreparedStatement st = null;
ResultSet rs = null;
try {
st = conn
.prepareStatement("select ahs.game, ahs.scoregroup, ahs.name, ahs.installation, ahs.points, ahs.medals, ahs.registered from applethiscores ahs join games on ahs.game = games.game and ahs.version = games.currentversion where ahs.game = ? and ahs.date > ? order by ahs.points desc limit "
+ MAX_SCORES);
GregorianCalendar c = new GregorianCalendar();
c.add(Calendar.WEEK_OF_YEAR, -1);
java.util.Date d = c.getTime();
st.setString(1, game);
st.setDate(2, new java.sql.Date(d.getTime()));
rs = st.executeQuery();
TreeSet scores = new TreeSet();
hiscores.put(game, scores);
while (rs.next()) {
Score score = new Score(rs.getString(1), "none", rs.getString(2), rs.getString(3), rs.getLong(4), rs
.getInt(5), rs.getString(6), rs.getBoolean(7));
scores.add(score);
}
// Calculate ranks
int count = 0;
for (Iterator j = scores.iterator(); j.hasNext();) {
Score s = (Score) j.next();
if (count == MAX_SCORES) {
j.remove();
} else {
s.setRank(count++);
}
}
} finally {
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
}
try {
if (st != null) {
st.close();
}
} catch (Exception e) {
}
}
}
private void loadHiScores(final String game) throws Exception {
// Load the hiscores
new SQLExec("LoadScores", server.getConnectionCache()) {
@Override
public Object exec(Connection conn) throws SQLException, Exception {
doLoadHiscores(conn, game);
return null;
}
}.action();
}
/*
* (non-Javadoc)
*
* @see net.puppygames.applet.AppletHiscoreServerRemote#getHiscores(java.lang.String)
*/
@Override
public synchronized List<Score> getHiscores(String game) {
TreeSet scores = (TreeSet) hiscores.get(game);
if (scores == null) {
scores = new TreeSet();
hiscores.put(game, scores);
} else {
// Clean naughty words
for (Iterator i = scores.iterator(); i.hasNext();) {
Score score = (Score) i.next();
for (int j = 0; j < BAD_WORDS.length; j++) {
score.setName(score.getName().replaceAll(BAD_WORDS[j], "puppy"));
}
for (int j = 0; j < BAD_WORDS2.length; j++) {
score.setName(score.getName().replaceAll(BAD_WORDS2[j][0], BAD_WORDS2[j][1]));
}
}
}
return new ArrayList(scores);
}
private void ban(Connection conn, String game, long installation, String reason) throws SQLException {
System.out.println("Banning user: "+installation+"/"+game+"/"+reason);
PreparedStatement ps = null;
try {
ps = conn.prepareStatement("insert into appletban (installation, date, reason) values (?, now(), ?)");
ps.setLong(1, installation);
ps.setString(2, reason);
ps.executeUpdate();
// Delete all their other scores
ps = conn.prepareStatement("delete from applethiscores where installation = ?");
ps.setLong(1, installation);
ps.executeUpdate();
} finally {
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
}
}
doLoadHiscores(conn, game);
}
private boolean checkAlreadyBanned(Connection conn, long installation) throws SQLException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Check for banned users
ps = conn.prepareStatement("select 1 from appletban where installation=?");
ps.setLong(1, installation);
rs = ps.executeQuery();
if (rs.next()) {
return true;
} else {
return false;
}
} finally {
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
}
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
}
}
}
private void deleteHiScore(Connection conn, Score score) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement("delete from applethiscores where game=? and scoregroup "
+ (score.getGroup() == null ? "is null" : "= ?")
+ " and name=? and installation=? and points<=? and version=?");
int p = 1;
ps.setString(p++, score.getGame());
if (score.getGroup() != null) {
ps.setString(p++, score.getGroup());
}
ps.setString(p++, score.getName());
ps.setLong(p++, score.getInstallation());
ps.setInt(p++, score.getPoints());
ps.setString(p++, score.getVersion());
ps.executeUpdate();
} finally {
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
}
}
}
private void checkBan(Connection conn, Score score) throws SQLException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Check for banned users
ps = conn.prepareStatement("select 1 from appletban where installation=?");
ps.setLong(1, score.getInstallation());
rs = ps.executeQuery();
if (rs.next()) {
throw new SQLException("You have been banned. Please contact support@puppygames.net for assistance.");
}
} finally {
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
}
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
}
}
}
private void checkVersion(Connection conn, String game, String version) throws SQLException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement("select currentversion from games where game=?");
ps.setString(1, game);
rs = ps.executeQuery();
if (!rs.next()) {
throw new SQLException("Game " + game + " not found!");
}
String currentVersion = rs.getString(1);
if (!version.equals(currentVersion)) {
throw new SQLException("You are running an old version of the game. Please go to www.puppygames.net and download the latest version.");
}
} finally {
if (rs != null) { try { rs.close(); } catch (Exception e) {} }
if (ps != null) { try { ps.close(); } catch (Exception e) {} }
}
}
private boolean checkHiScore(Connection conn, Score score) throws SQLException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement("select 1 from applethiscores where game=? and scoregroup "
+ (score.getGroup() == null ? "is null" : "= ?")
+ " and name=? and installation=? and points>? and version=?");
int p = 1;
ps.setString(p++, score.getGame());
if (score.getGroup() != null) {
ps.setString(p++, score.getGroup());
}
ps.setString(p++, score.getName());
ps.setLong(p++, score.getInstallation());
ps.setInt(p++, score.getPoints());
ps.setString(p++, score.getVersion());
rs = ps.executeQuery();
if (!rs.next()) {
return true;
} else {
return false;
}
} finally {
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
}
try {
if (rs != null) {
rs.close();
}
} catch (Exception e) {
}
}
}
private void insertHiScore(Connection conn, Score score) throws SQLException {
PreparedStatement ps = null;
try {
ps = conn
.prepareStatement("insert into applethiscores (game, scoregroup, name, installation, points, medals, date, registered, version) values (?,?,?,?,?,?,?,?,?)");
ps.setString(1, score.getGame());
if (score.getGroup() == null) {
ps.setNull(2, java.sql.Types.VARCHAR);
} else {
ps.setString(2, score.getGroup());
}
ps.setString(3, score.getName());
ps.setLong(4, score.getInstallation());
ps.setInt(5, score.getPoints());
ps.setString(6, score.getMedals());
ps.setDate(7, new java.sql.Date(new java.util.Date().getTime()));
ps.setBoolean(8, score.isRegistered());
ps.setString(9, score.getVersion());
ps.executeUpdate();
} finally {
try {
if (ps != null) {
ps.close();
}
} catch (Exception e) {
}
}
}
private String clean(String in) {
StringBuilder out = new StringBuilder(in.length());
for (int i = 0; i < in.length(); i ++) {
char c = in.charAt(i);
if (Character.isDigit(c) || Character.isLetter(c) || Character.isWhitespace(c)) {
out.append(c);
}
}
return out.toString();
}
private String removeSpaces(String in) {
StringBuilder out = new StringBuilder(in.length());
for (int i = 0; i < in.length(); i ++) {
char c = in.charAt(i);
if (!Character.isWhitespace(c)) {
out.append(c);
}
}
return out.toString();
}
/*
* (non-Javadoc)
*
* @see genesis.HiscoreServer#submit(java.util.List)
*/
@Override
public synchronized HiscoresReturn submit2(final Score score) throws Exception {
System.out.println("Submitting Applet hiscore 2 from " + RemoteServer.getClientHost() + ": " + score);
// Check game version
new SQLExec("CheckVersion", server.getConnectionCache()) {
@Override
public Object exec(Connection conn) throws SQLException, Exception {
checkVersion(conn, score.getGame(), score.getVersion());
return null;
}
}.action();
// Simple anti-cheat protection
if (score.getPoints() % 5 != 0) {
// Ban the fucker
new SQLExec("BAN", server.getConnectionCache()) {
@Override
public Object exec(Connection conn) throws SQLException, Exception {
if (!checkAlreadyBanned(conn, score.getInstallation())) {
ban(conn, score.getGame(), score.getInstallation(), "CHEAT:"+score.getPoints());
}
return null;
}
}.action();
throw new SQLException("Cheat! It is not possible to score "+score.getPoints()+" in "+score.getGame()+". You have been banned from the online hiscore table. Contact support@puppygames.net for assistance.");
}
// Remove punctuation
score.setName(clean(score.getName()));
// Check for sweary people and ban them too
final String swearCheck = removeSpaces(score.getName()).toLowerCase();
for (int i = 0; i < BAD_WORDS3.length; i ++) {
if (swearCheck.contains(BAD_WORDS3[i])) {
throw new SQLException("No swearing on the online hiscores table please.");
}
}
new SQLExec("APPLET.submit", server.getConnectionCache()) {
@Override
public Object exec(Connection conn) throws SQLException, Exception {
checkBan(conn, score);
deleteHiScore(conn, score);
if (checkHiScore(conn, score)) {
insertHiScore(conn, score);
}
return null;
}
}.action();
loadHiScores(score.getGame());
HiscoresReturn ret;
List newScores = getHiscores(score.getGame());
// If the score we just submitted isn't in the list, it's because either it
// wasn't good enough, or because we didn't beat our all-time hiscore
if (score.getName().equals("c4st3st")) {
ret = new HiscoresReturn(newScores, "DIDN'T BEAT YOUR\nALL-TIME BEST");
} else if (!newScores.contains(score)) {
// If this list is full, look at the last score to see if the incoming
// score made the grade.
if (newScores.size() == MAX_SCORES) {
Score worstInList = (Score) newScores.get(MAX_SCORES - 1);
if (worstInList.getPoints() >= score.getPoints()) {
// Didn't make the grade :)
ret = new HiscoresReturn(newScores, null);
} else {
// Old score not beaten
ret = new HiscoresReturn(newScores, "DIDN'T BEAT YOUR\nALL-TIME BEST");
}
} else {
// Old score not beaten
ret = new HiscoresReturn(newScores, "DIDN'T BEAT YOUR\nALL-TIME BEST");
}
} else {
// Got a hiscore!
ret = new HiscoresReturn(newScores, null);
}
return ret;
}
@Override
public List<Score> submit(final Score score) throws Exception {
throw new SQLException("You are running an old version of the game. Please go to www.puppygames.net and download the latest version.");
}
}