← Back to team overview

mugle-dev team mailing list archive

[Merge] lp:~mugle-dev/mugle/dev-api into lp:mugle

 

Scott Ritchie has proposed merging lp:~mugle-dev/mugle/dev-api into lp:mugle.

Requested reviews:
  MUGLE Developers (mugle-dev)

For more details, see:
https://code.launchpad.net/~mugle-dev/mugle/dev-api/+merge/57993

Proposing to merge with the trunk since all the service calls are set up.  Prageeth and I have decided to make a new branch to work on the UI and platform stuff in regards to the dev-api
-- 
https://code.launchpad.net/~mugle-dev/mugle/dev-api/+merge/57993
Your team MUGLE Developers is requested to review the proposed merge of lp:~mugle-dev/mugle/dev-api into lp:mugle.
=== added file 'doc/dev/api.rst'
--- doc/dev/api.rst	1970-01-01 00:00:00 +0000
+++ doc/dev/api.rst	2011-04-16 09:26:25 +0000
@@ -0,0 +1,36 @@
+.. Melbourne University Game-based Learning Environment
+   Copyright (C) 2011 The University of Melbourne
+
+.. This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+.. This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+.. You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+.. _ref-dev-api:
+
+Developer API
+=============
+
+MUGLE provides a small, simple API which is available to games for
+communicating with the server. This allows limited access to the MUGLE
+database, for both reading and writing information about the current user's
+profile associated with this game.
+
+Each user has a "profile" with each game (the "user-game profile"). This
+profile associates several pieces of information with the user:
+
+* The user's highest score associated with this game.
+* The badges that the user has unlocked.
+* An arbitrary key-value mapping (unique to each user), allowing string keys
+  to be associated with arbitrary object values.
+
+All of the APIs available to games developers are available in the package
+``au.edu.unimelb.csse.mugle.client.api``.

=== modified file 'doc/dev/index.rst'
--- doc/dev/index.rst	2011-03-11 01:15:21 +0000
+++ doc/dev/index.rst	2011-04-16 09:26:25 +0000
@@ -28,3 +28,5 @@
 
 .. toctree::
    :maxdepth: 2
+
+   api.rst

=== added file 'doc/platform/datastore-access.rst'
--- doc/platform/datastore-access.rst	1970-01-01 00:00:00 +0000
+++ doc/platform/datastore-access.rst	2011-04-16 09:26:25 +0000
@@ -0,0 +1,80 @@
+.. Melbourne University Game-based Learning Environment
+   Copyright (C) 2011 The University of Melbourne
+
+.. This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+.. This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+.. You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Accessing the Datastore
+=======================
+
+To access objects in the datastore, a set of classes called *Getters* have
+been provided in the package au.edu.unimelb.csse.mugle.server.model, each
+corresponding to a class in the au.edu.unimelb.csse.mugle.server.model package.
+This set of classes acts as an interface to the datastore, allowing developers
+to get Objects from the datastore based on their name, primary key etc.
+These are then used to pass information over to the client;
+    -   By passing specific information in the class over when called in the
+        developer API services
+    -   By wrapping the object using the `Datastore wrappers
+        <data-wrappers.html>`_ before passing them back to the client
+
+
+A variety of methods have been provided for each Getter - allowing you to
+retrieve an object by its Primary Key, its name, by relations it belongs to
+(for example the UserGameProfile by the current user and the game they're
+playing).   
+
+There are two types of methods in each *Getter*, ones which return the object
+as read only, and ones which return the object and leave it open for editing.
+In cases where you want to update the object or call other datastore objects
+linked to it, you'll need to handle the PersistenceManager yourself; any
+changes made to an object must be made BEFORE the PersistenceManager is closed
+for the datastore to be updated with these new changes.  Similarly if you need
+to access an object in the datastore from the object you've currently
+retrieved, this needs to be done before the PersistenceManager is closed.
+
+Example of updating the email of a User
++++++++++++++++++++++++++++++++++++++++
+
+::
+    
+    public void UpdateEmail(String newEmail) {
+        UserGetter u = new UserGetter();
+        UserData user = null;
+        PersistenceManager pm = PMF.getManager();
+        try {
+            user = u.getCurrentUser();
+            // Any changes must be made before closing the PersistenceManager
+            user.setEmail(newEmail);
+        } finally {
+            pm.close();
+        }
+    }
+
+Get all the Games a DevTeam has produced
+++++++++++++++++++++++++++++++++++++++++++++++++
+
+::
+    
+    public Set<GameData> getGame(String devTeamName) {
+        DevTeamGetter d = new DevTeamGetter();
+        DevTeamData devTeam = null;
+        PersistenceManager pm = PMF.getManager();
+        try {
+            devTeamData = d.getDevTeam(devTeamName);
+            return devTeam.getGames();
+        } finally {
+            pm.close();
+        }
+    }
+

=== modified file 'doc/platform/index.rst'
--- doc/platform/index.rst	2011-03-11 01:50:22 +0000
+++ doc/platform/index.rst	2011-04-16 09:26:25 +0000
@@ -31,3 +31,4 @@
    :maxdepth: 2
 
    data-wrappers.rst
+   datastore-access.rst

=== modified file 'src/au/edu/unimelb/csse/mugle/client/LoginInfo.java'
--- src/au/edu/unimelb/csse/mugle/client/LoginInfo.java	2011-03-06 10:21:39 +0000
+++ src/au/edu/unimelb/csse/mugle/client/LoginInfo.java	2011-04-16 09:26:25 +0000
@@ -27,6 +27,7 @@
   private String logoutUrl;
   private String emailAddress;
   private String nickname;
+  private String UserID; //unique ID provided by google
 
   public boolean isLoggedIn() {
     return loggedIn;
@@ -67,4 +68,12 @@
   public void setNickname(String nickname) {
     this.nickname = nickname;
   }
+
+public void setUserID(String userID) {
+	UserID = userID;
+}
+
+public String getUserID() {
+	return UserID;
+}
 }

=== added directory 'src/au/edu/unimelb/csse/mugle/client/api'
=== added file 'src/au/edu/unimelb/csse/mugle/client/api/BadgeService.java'
--- src/au/edu/unimelb/csse/mugle/client/api/BadgeService.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/BadgeService.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,101 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import au.edu.unimelb.csse.mugle.shared.api.Badge;
+import au.edu.unimelb.csse.mugle.shared.api.BadgeError;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+
+/**
+ * Developer API for accessing the badges (achievements) system.
+ * This allows each game to award the user with badges, as well as update the
+ * progress of badges.
+ */
+@RemoteServiceRelativePath("api-badge")
+public interface BadgeService extends RemoteService
+{
+    /** Get a list of badge names for this game.
+     * @param gameToken The secret token for the current game.
+     * @return List of strings corresponding to the 'name' field of each
+     *  badge in the current game.
+     */
+    public String[] getBadgeNames(String gameToken)
+        throws GameTokenError;
+
+    /** Get the static (user-independent) information about a badge for the
+     * current game.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge.
+     * @return Information about that badge.
+     * @throws BadgeError If no badge by that name.
+     */
+    public Badge getBadgeInfo(String gameToken, String name)
+        throws GameTokenError, BadgeError;
+
+    /** Sets the Badge to the "achieved" state.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @throws BadgeError If no badge by that name.
+     */
+    public void setAchieved(String gameToken, String name)
+        throws GameTokenError, BadgeError;
+
+    /** Increments the progress of the badge by the given amount.
+     * If the progress reaches the maximum for this badge, sets the badge to
+     * achieved. The progress is capped at the badge maximum.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @param amount The amount to increment the badge by. Must not be
+     *  negative.
+     * @return true if the badge was achieved as a result.
+     * @throws BadgeError If no badge by that name.
+     */
+    public boolean incrementProgress(String gameToken, String name,
+        int amount) throws GameTokenError, BadgeError;
+
+    /** Increments the progress of the badge by 1.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @return true if the badge was achieved as a result.
+     * @throws BadgeError If no badge by that name.
+     */
+    public boolean incrementProgress(String gameToken, String name)
+        throws GameTokenError, BadgeError;
+
+    /** Checks whether the badge has been achieved by the player.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @return true if achieved, false otherwise
+     * @throws BadgeError If no badge by that name.
+     */
+    public boolean isAchieved(String gameToken, String name)
+        throws GameTokenError, BadgeError;
+
+    /** Returns the progress of the current badge.
+     * Use getBadgeInfo to retrieve the maximum progress for the badge.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @return The progress of the badge
+     * @throws BadgeError If no badge by that name.
+     */
+    public int getProgress(String gameToken, String name)
+        throws GameTokenError, BadgeError;
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/api/BadgeServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/client/api/BadgeServiceAsync.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/BadgeServiceAsync.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,102 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import au.edu.unimelb.csse.mugle.shared.api.Badge;
+
+/**
+ * Developer async API for accessing the badges (achievements) system.
+ * This allows each game to award the user with badges, as well as update the
+ * progress of badges.
+ */
+public interface BadgeServiceAsync
+{
+    /** Get a list of badge names for this game.
+     * @param gameToken The secret token for the current game.
+     * @param callback Called once the operation finishes. On success, passes
+     *  a list of strings corresponding to the 'name' field of each badge in
+     *  the current game.
+     */
+    void getBadgeNames(String gameToken, AsyncCallback<String[]> callback);
+
+    /** Get the static (user-independent) information about a badge for the
+     * current game.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge.
+     * @param callback Called once the operation finishes. On success, passes
+     *  information about that badge. Fails (BadgeError) if no badge by that
+     *  name.
+     */
+    void getBadgeInfo(String gameToken, String name,
+        AsyncCallback<Badge> callback);
+
+    /** Sets the Badge to the "achieved" state.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @param callback Called once the operation finishes. Fails (BadgeError)
+     * if no badge by that name.
+     */
+    void setAchieved(String gameToken, String name,
+        AsyncCallback<Void> callback);
+
+    /** Increments the progress of the badge by the given amount.
+     * If the progress reaches the maximum for this badge, sets the badge to
+     * achieved. The progress is capped at the badge maximum.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @param amount The amount to increment the badge by. Must not be
+     *  negative.
+     * @param callback Called once the operation finishes. On success, passes
+     * true if the badge was achieved as a result. Fails (BadgeError) if no
+     * badge by that name.
+     */
+    void incrementProgress(String gameToken, String name, int amount,
+            AsyncCallback<Boolean> callback);
+
+    /** Increments the progress of the badge by 1.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @param callback Called once the operation finishes. On success, passes
+     * true if the badge was achieved as a result. Fails (BadgeError) if no
+     * badge by that name.
+     */
+    void incrementProgress(String gameToken, String name,
+            AsyncCallback<Boolean> callback);
+
+    /** Checks whether the badge has been achieved by the player.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @param callback Called once the operation finishes. On success, passes
+     * true if the badge is achieved. Fails (BadgeError) if no badge by that
+     * name.
+     */
+    void isAchieved(String gameToken, String name,
+            AsyncCallback<Boolean> callback);
+
+    /** Returns the progress of the current badge.
+     * Use getBadgeInfo to retrieve the maximum progress for the badge.
+     * @param gameToken The secret token for the current game.
+     * @param name The name of the badge
+     * @param callback Called once the operation finishes. On success, passes
+     * the progress of the badge. Fails (BadgeError) if no badge by that name.
+     */
+    void getProgress(String gameToken, String name,
+            AsyncCallback<Integer> callback);
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/api/HighscoreService.java'
--- src/au/edu/unimelb/csse/mugle/client/api/HighscoreService.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/HighscoreService.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,46 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+/**
+ * Developer API for accessing the current user's high-score.
+ * This allows the game to save the user's highest score.
+ */
+@RemoteServiceRelativePath("api-highscore")
+public interface HighscoreService extends RemoteService
+{
+    /** Saves the score for the current player. If this is better than the
+     * player's current high score, sets the new high score. Otherwise, does
+     * nothing.
+     * @param gameToken The secret token for the current game.
+     * @param score The player's score.
+     */
+    public void saveScore(String gameToken, int score)
+        throws GameTokenError;
+
+    /** Gets the highest score of the current player.
+     * @param gameToken The secret token for the current game.
+     * @return The player's highest score.
+     */
+    public int getHighScore(String gameToken) throws GameTokenError;
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/api/HighscoreServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/client/api/HighscoreServiceAsync.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/HighscoreServiceAsync.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,44 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * Developer async API for accessing the current user's high-score.
+ * This allows the game to save the user's highest score.
+ */
+public interface HighscoreServiceAsync
+{
+    /** Saves the score for the current player. If this is better than the
+     * player's current high score, sets the new high score. Otherwise, does
+     * nothing.
+     * @param gameToken The secret token for the current game.
+     * @param score The player's score.
+     * @param callback Called once the operation finishes.
+     */
+    void saveScore(String gameToken, int score,
+        AsyncCallback<Void> callback);
+
+    /** Gets the highest score of the current player.
+     * @param gameToken The secret token for the current game.
+     * @param callback Called once the operation finishes. On success, passes
+     *  the player's highest score.
+     */
+    void getHighScore(String gameToken, AsyncCallback<Integer> callback);
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/api/KeyValueService.java'
--- src/au/edu/unimelb/csse/mugle/client/api/KeyValueService.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/KeyValueService.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,63 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import java.io.Serializable;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+import au.edu.unimelb.csse.mugle.shared.api.KeyError;
+
+/**
+ * Developer API for accessing the key/value store.
+ * This allows each game to associate arbitrary data with string keys,
+ * independently for each user.
+ */
+@RemoteServiceRelativePath("api-keyvalue")
+public interface KeyValueService extends RemoteService
+{
+    /** Store an object in the key-value store.
+     * The value will be specific to the current user, and will only be able
+     * to be retrieved by the same user.
+     * @param gameToken The secret token for the current game.
+     * @param key The key to associate the object with.
+     * @param value The object to store.
+     */
+    public void put(String gameToken, String key, Serializable value)
+        throws GameTokenError;
+
+    /** Retrieve an object from the key-value store.
+     * The value will be the one stored by the current user.
+     * @param gameToken The secret token for the current game.
+     * @param key The key the object is associated with.
+     * @return The object associated with the key.
+     * @throws KeyError If no object is associated with that key.
+     */
+    public Serializable get(String gameToken, String key)
+        throws GameTokenError, KeyError;
+
+    /** Test whether a key has an associated object for the current user.
+     * @param gameToken The secret token for the current game.
+     * @param key The key the object is associated with.
+     * @return True if a value is associated with the key.
+     */
+    public boolean containsKey(String gameToken, String key)
+        throws GameTokenError;
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/api/KeyValueServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/client/api/KeyValueServiceAsync.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/KeyValueServiceAsync.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,61 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import java.io.Serializable;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * Developer async API for accessing the key/value store.
+ * This allows each game to associate arbitrary data with string keys,
+ * independently for each user.
+ */
+public interface KeyValueServiceAsync
+{
+    /** Store an object in the key-value store.
+     * The value will be specific to the current user, and will only be able
+     * to be retrieved by the same user.
+     * @param gameToken The secret token for the current game.
+     * @param key The key to associate the object with.
+     * @param value The object to store.
+     * @param callback Called once the operation finishes.
+     */
+    public void put(String gameToken, String key, Serializable value,
+        AsyncCallback<Void> callback);
+
+    /** Retrieve an object from the key-value store.
+     * The value will be the one stored by the current user.
+     * @param gameToken The secret token for the current game.
+     * @param key The key the object is associated with.
+     * @param callback Called once the operation finishes. On success, passes
+     *  the object associated with the key. Fails (KeyError) if no object is
+     *  associated with that key.
+     */
+    public void get(String gameToken, String key,
+        AsyncCallback<Serializable> callback);
+
+    /** Test whether a key has an associated object for the current user.
+     * @param gameToken The secret token for the current game.
+     * @param key The key the object is associated with.
+     * @param callback Called once the operation finishes. On success, passes
+     *  true if a value is associated with the key.
+     */
+    public void containsKey(String gameToken, String key,
+        AsyncCallback<Boolean> callback);
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/api/Services.java'
--- src/au/edu/unimelb/csse/mugle/client/api/Services.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/Services.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,50 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.ServiceDefTarget;
+
+/** Provides access to pre-initialised instances of all the asynchronous
+ * services offered by MUGLE.
+ */
+public class Services
+{
+    /** An instance of the asynchronous user service. */
+    public static final UserServiceAsync user =
+        GWT.create(UserService.class);
+    /** An instance of the asynchronous key-value service. */
+    public static final KeyValueServiceAsync keyvalue =
+        GWT.create(KeyValueService.class);
+    /** An instance of the asynchronous badge service. */
+    public static final BadgeServiceAsync badges =
+        GWT.create(BadgeService.class);
+    /** An instance of the asynchronous high score service. */
+    public static final HighscoreServiceAsync highscore =
+        GWT.create(HighscoreService.class);
+
+    static
+    {
+        ((ServiceDefTarget) badges).setServiceEntryPoint("/mugle/api-badge");
+        ((ServiceDefTarget) highscore).setServiceEntryPoint(
+                "/mugle/api-highscore");
+        ((ServiceDefTarget) keyvalue).setServiceEntryPoint(
+                "/mugle/api-keyvalue");
+        ((ServiceDefTarget) user).setServiceEntryPoint("/mugle/api-user");
+    }
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/api/UserService.java'
--- src/au/edu/unimelb/csse/mugle/client/api/UserService.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/UserService.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,41 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+/**
+ * Developer API for accessing information about the current user.
+ */
+@RemoteServiceRelativePath("api-user")
+public interface UserService extends RemoteService
+{
+    /** Gets the the nickname of the user playing the game.
+     * This should be used whenever the user's name needs to be displayed.
+     * Note that this nickname may change, so it should not be used to
+     * permanently associate information with the user (see getUserID).
+     */
+    public String getUserNickName();
+
+    /** Gets the user ID of the user playing the game.
+     * This is guaranteed never to change for this user, so it should be used
+     * any time some permanent information needs to be associated with a user.
+     */
+    public String getUserID();
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/api/UserServiceAsync.java'
--- src/au/edu/unimelb/csse/mugle/client/api/UserServiceAsync.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/api/UserServiceAsync.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,43 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.client.api;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+/**
+ * Developer async API for accessing information about the current user.
+ */
+public interface UserServiceAsync
+{
+    /** Gets the the nickname of the user playing the game.
+     * This should be used whenever the user's name needs to be displayed.
+     * Note that this nickname may change, so it should not be used to
+     * permanently associate information with the user (see getUserID).
+     * @param callback Called once the operation finishes. On success, passes
+     *  the nickname.
+     */
+    void getUserNickName(AsyncCallback<String> callback);
+
+    /** Gets the user ID of the user playing the game.
+     * This is guaranteed never to change for this user, so it should be used
+     * any time some permanent information needs to be associated with a user.
+     * @param callback Called once the operation finishes. On success, passes
+     *  the user ID.
+     */
+    void getUserID(AsyncCallback<String> callback);
+}

=== added directory 'src/au/edu/unimelb/csse/mugle/client/ui'
=== added file 'src/au/edu/unimelb/csse/mugle/client/ui/MugleUiBuilder.java'
--- src/au/edu/unimelb/csse/mugle/client/ui/MugleUiBuilder.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/ui/MugleUiBuilder.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,144 @@
+package au.edu.unimelb.csse.mugle.client.ui;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CellPanel;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+public class MugleUiBuilder {
+	
+	private MugleUiBuilder() { }
+	
+	public static CellPanel buildStringTable(Collection<Collection<String>> list, Collection<String> headers) {
+		return buildStringTable(list, null);
+	}
+	
+	public static CellPanel buildStringTable(Collection<Collection<String>> list, Collection<String> headers, PageNav pageNav) {
+		
+		// build panel		
+		VerticalPanel panel = new VerticalPanel();
+				
+		// build table
+		
+		FlexTable table = new FlexTable();
+		
+		int row = 0;
+		int col = 0;
+		
+		Iterator<String> it = headers.iterator();
+		while (it.hasNext()) {
+			table.setText(row, col, it.next());
+			col++;
+		}
+		row++;
+		
+		Iterator<Collection<String>> iter = list.iterator();
+		while (iter.hasNext()) {
+			Iterator<String> sit = iter.next().iterator();
+			while (sit.hasNext()) {
+				table.setText(row, col, sit.next());
+				col++;
+			}
+			// check the items per page restriction
+			if (pageNav != null && pageNav.getItemsPerPage() >= row) { break; }
+			row++;
+			
+		}			
+			
+		// add table to panel		
+		panel.add(table);
+		
+		// add navigation details		
+		if (pageNav != null) {
+			panel.add(getNagivationDetails(pageNav));
+		}
+		
+		return panel;
+		
+	}
+	
+	public static CellPanel buildWidgetTable(Collection<Collection<Widget>> list, Collection<String> headers) {
+		return buildWidgetTable(list, headers, null);
+	}
+
+	public static CellPanel buildWidgetTable(Collection<Collection<Widget>> list, Collection<String> headers, PageNav pageNav) {
+		
+		// build panel		
+		VerticalPanel panel = new VerticalPanel();
+				
+		// build table
+		
+		FlexTable table = new FlexTable();
+			
+		int row = 0;
+		int col = 0;
+		
+		Iterator<String> it = headers.iterator();
+		while (it.hasNext()) {
+			table.setText(row, col, it.next());
+			col++;
+		}
+		row++;
+		
+		Iterator<Collection<Widget>> iter = list.iterator();
+		while (iter.hasNext()) {
+			Iterator<Widget> wit = iter.next().iterator();
+			while (wit.hasNext()) {
+				table.setWidget(row, col, wit.next());
+				col++;
+			}
+			// check the items per page restriction
+			if (pageNav != null && pageNav.getItemsPerPage() >= row) { break; }
+			row++;
+		}			
+		
+		// add table to panel		
+		panel.add(table);
+		
+		// add navigation details		
+		if (pageNav != null) {
+			panel.add(getNagivationDetails(pageNav));
+		}
+		
+		return panel;
+		
+	}
+	
+	private static Widget getNagivationDetails(PageNav pageNav) {
+		
+		if (pageNav == null) {
+			// TODO throw Exception or handle error
+		}
+		
+		HorizontalPanel hp = new HorizontalPanel();
+		hp.setHorizontalAlignment(HorizontalPanel.ALIGN_RIGHT);
+		
+		addNewButtonToPanel(hp, "<<", pageNav.getFirstClicked());
+		addNewButtonToPanel(hp, "<", pageNav.getPrevClicked());
+		
+		// TODO add links to pages around
+		//hp.add(new Label( String.format("%d of %d", pageNav.getCurrPage(), pageNav.getMaxPages()) ));
+		hp.add(new Label( String.valueOf(pageNav.getCurrPage()) ));
+		
+		addNewButtonToPanel(hp, ">", pageNav.getNextClicked());
+		addNewButtonToPanel(hp, ">>", pageNav.getLastClicked());
+				
+		return hp;
+		
+	}
+	
+	private static void addNewButtonToPanel(CellPanel panel, String text, ClickHandler handler) {
+		Button b = new Button();
+		b.setText(text);
+		if (handler != null) { b.addClickHandler(handler); }
+		panel.add(b);		
+	}
+
+}

=== added file 'src/au/edu/unimelb/csse/mugle/client/ui/PageNav.java'
--- src/au/edu/unimelb/csse/mugle/client/ui/PageNav.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/client/ui/PageNav.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,78 @@
+package au.edu.unimelb.csse.mugle.client.ui;
+
+import com.google.gwt.event.dom.client.ClickHandler;
+
+public class PageNav {
+	
+	private int currPage = 0;
+	private int maxPages = 0;
+	private int itemsPerPage = 0;
+	
+	private ClickHandler firstClicked = null;
+	private ClickHandler prevClicked = null;
+	private ClickHandler nextClicked = null;
+	private ClickHandler lastClicked = null;
+	
+	public PageNav(int currPage, int maxPages, int itemsPerPage) {
+		this.currPage = currPage;
+		this.maxPages = maxPages;
+		this.itemsPerPage = itemsPerPage;
+	}
+
+	public int getCurrPage() {
+		return this.currPage;
+	}
+
+	public void setCurrPage(int currPage) {
+		this.currPage = currPage;
+	}
+
+	public int getMaxPages() {
+		return this.maxPages;
+	}
+
+	public void setMaxPages(int maxPages) {
+		this.maxPages = maxPages;
+	}
+
+	public int getItemsPerPage() {
+		return this.itemsPerPage;
+	}
+
+	public void setItemsPerPage(int itemsPerPage) {
+		this.itemsPerPage = itemsPerPage;
+	}
+
+	public ClickHandler getFirstClicked() {
+		return this.firstClicked;
+	}
+
+	public void setFirstClicked(ClickHandler firstClicked) {
+		this.firstClicked = firstClicked;
+	}
+
+	public ClickHandler getPrevClicked() {
+		return this.prevClicked;
+	}
+
+	public void setPrevClicked(ClickHandler prevClicked) {
+		this.prevClicked = prevClicked;
+	}
+
+	public ClickHandler getNextClicked() {
+		return this.nextClicked;
+	}
+
+	public void setNextClicked(ClickHandler nextClicked) {
+		this.nextClicked = nextClicked;
+	}
+
+	public ClickHandler getLastClicked() {
+		return this.lastClicked;
+	}
+
+	public void setLastClicked(ClickHandler lastClicked) {
+		this.lastClicked = lastClicked;
+	}
+		
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java	2011-04-16 09:26:25 +0000
@@ -22,7 +22,6 @@
 
 import au.edu.unimelb.csse.mugle.client.*;
 import au.edu.unimelb.csse.mugle.server.model.*;
-import au.edu.unimelb.csse.mugle.server.platform.*;
 import au.edu.unimelb.csse.mugle.shared.model.*;
 
 @SuppressWarnings("serial")

=== modified file 'src/au/edu/unimelb/csse/mugle/server/LoginServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/LoginServiceImpl.java	2011-03-06 10:23:58 +0000
+++ src/au/edu/unimelb/csse/mugle/server/LoginServiceImpl.java	2011-04-16 09:26:25 +0000
@@ -36,6 +36,7 @@
       loginInfo.setLoggedIn(true);
       loginInfo.setEmailAddress(user.getEmail());
       loginInfo.setNickname(user.getNickname());
+      loginInfo.setUserID(user.getUserId());
       loginInfo.setLogoutUrl(userService.createLogoutURL(requestUri));
     } else {
       loginInfo.setLoggedIn(false);
@@ -43,5 +44,25 @@
     }
     return loginInfo;
   }
+  
+  /* For server side purposes only,
+   * Gets the currently logged in user as above, but without the
+   * Host's url.  Used in most server side service implementations
+   */
+  public LoginInfo getCurrentUser() {
+	  UserService userService = UserServiceFactory.getUserService();
+	  User user = userService.getCurrentUser();
+	  LoginInfo loginInfo = new LoginInfo();
+	  
+	  if (user != null) {
+	      loginInfo.setLoggedIn(true);
+	      loginInfo.setEmailAddress(user.getEmail());
+	      loginInfo.setNickname(user.getNickname());
+	      loginInfo.setUserID(user.getUserId());
+	  } else {
+	      loginInfo.setLoggedIn(false);
+	  }
+	  return loginInfo;
+  }
 
 }

=== renamed file 'src/au/edu/unimelb/csse/mugle/server/platform/PMF.java' => 'src/au/edu/unimelb/csse/mugle/server/PMF.java'
--- src/au/edu/unimelb/csse/mugle/server/platform/PMF.java	2011-03-06 10:18:13 +0000
+++ src/au/edu/unimelb/csse/mugle/server/PMF.java	2011-04-16 09:26:25 +0000
@@ -15,7 +15,7 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-package au.edu.unimelb.csse.mugle.server.platform;
+package au.edu.unimelb.csse.mugle.server;
 
 import javax.jdo.JDOHelper;
 import javax.jdo.PersistenceManager;

=== added directory 'src/au/edu/unimelb/csse/mugle/server/api'
=== added file 'src/au/edu/unimelb/csse/mugle/server/api/BadgeServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/api/BadgeServiceImpl.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/api/BadgeServiceImpl.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,149 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.api;
+
+import java.util.ArrayList;
+
+import au.edu.unimelb.csse.mugle.client.api.BadgeService;
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.server.model.AchievementData;
+import au.edu.unimelb.csse.mugle.server.model.AchievementGetter;
+import au.edu.unimelb.csse.mugle.server.model.GameData;
+import au.edu.unimelb.csse.mugle.server.model.GameGetter;
+import au.edu.unimelb.csse.mugle.server.model.UserAchievementData;
+import au.edu.unimelb.csse.mugle.server.model.UserAchievementGetter;
+import au.edu.unimelb.csse.mugle.shared.api.Badge;
+import au.edu.unimelb.csse.mugle.shared.api.BadgeError;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.*;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import javax.jdo.PersistenceManager;
+
+
+/**
+ * The server side implementation of KeyValueService.
+ */
+@SuppressWarnings("serial")
+public class BadgeServiceImpl extends RemoteServiceServlet
+    implements BadgeService
+{
+	private UserAchievementData getUserAchievement(String gameToken, String name) throws GameTokenError, BadgeError {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return this.getUserAchievement(pm, gameToken, name);
+		} finally {
+			pm.close();
+		}
+	}
+
+	private UserAchievementData getUserAchievement(PersistenceManager pm, String gameToken, String name) throws GameTokenError, BadgeError {
+		UserAchievementGetter u = new UserAchievementGetter();
+		try {
+			return u.getUserAchievement(pm, name, gameToken);
+		} catch (UserNotExists e) {
+			throw new Error(e);
+		} catch (AchievementNotExists e) {
+			throw new BadgeError(e.getMessage());
+		}
+	}
+
+	@Override
+	public String[] getBadgeNames(String gameToken) throws GameTokenError {
+		ArrayList<String> toReturn = new ArrayList<String>();
+		GameGetter g = new GameGetter();
+		GameData curGame = null;
+		
+		curGame = g.getCurrentGame(gameToken);
+		
+		for (AchievementData a: curGame.getAchievements()) {
+			toReturn.add(a.getName());
+		}
+		return (String[]) toReturn.toArray();
+	}
+
+	@Override
+	public Badge getBadgeInfo(String gameToken, String name)
+			throws GameTokenError, BadgeError {
+		PersistenceManager pm = PMF.getManager();
+		AchievementData a;
+		try {
+			AchievementGetter ag = new AchievementGetter();
+			a = ag.getAchievement(pm, name, gameToken);;
+		} catch (AchievementNotExists e) {
+			throw new BadgeError(e.getMessage());
+		} finally {
+			pm.close();
+		}
+		// XXX The badge API requires both a short name and a display name
+		// Currently use the same name for both; requires changes to
+		// Achievement class.
+		return new Badge(a.getName(), a.getName(), a.getDescription(),
+		                 a.getMaxProgress());
+	}
+
+	@Override
+	public boolean isAchieved(String gameToken, String name) throws GameTokenError, BadgeError {
+		return this.getUserAchievement(gameToken, name).getAchieved();
+	}
+
+	@Override
+	public int getProgress(String gameToken, String name) throws GameTokenError, BadgeError {
+		return this.getUserAchievement(gameToken, name).getProgress();
+	}
+
+	@Override
+	public boolean incrementProgress(String gameToken, String name, int amount) throws GameTokenError, BadgeError {
+    	PersistenceManager pm = PMF.getManager();
+    
+    	if (amount < 0)
+    		throw new BadgeError("incrementProgress called with negative amount");
+    
+    	try {
+    		UserAchievementData ua = this.getUserAchievement(pm, gameToken, name);
+    		if (ua.getAchievement().getMaxProgress() == 0) {
+    			throw new BadgeError("incrementProgress called on non-progress badge " + name);
+    		}
+    		return ua.addProgress(amount);
+    	} finally {
+    		pm.close();
+    	}
+	}
+
+	@Override
+	public boolean incrementProgress(String gameToken, String name) throws GameTokenError, BadgeError{
+		return incrementProgress(gameToken, name, 1);
+	}
+
+	@Override
+	public void setAchieved(String gameToken, String name) throws GameTokenError, BadgeError {
+	    	PersistenceManager pm = PMF.getManager();
+	    
+	    	try {
+	    		UserAchievementData ua = this.getUserAchievement(pm, gameToken, name);
+	    		ua.setAchieved(true);
+	    		// also need to set the progress
+	    		ua.setProgress(ua.getAchievement().getMaxProgress());    		
+	    	} finally {
+	    		pm.close();
+	    	}
+	}
+
+}

=== added file 'src/au/edu/unimelb/csse/mugle/server/api/HighscoreServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/api/HighscoreServiceImpl.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/api/HighscoreServiceImpl.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,65 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.api;
+
+import au.edu.unimelb.csse.mugle.client.api.HighscoreService;
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.server.model.UserGameProfileGetter;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+import au.edu.unimelb.csse.mugle.server.model.UserGameProfileData;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import javax.jdo.PersistenceManager;
+
+
+/**
+ * The server side implementation of KeyValueService.
+ */
+@SuppressWarnings("serial")
+public class HighscoreServiceImpl extends RemoteServiceServlet
+    implements HighscoreService
+{
+    
+	public int getHighScore(String gameToken) throws GameTokenError {
+		UserGameProfileGetter u = new UserGameProfileGetter();
+		UserGameProfileData ugp;
+		try {
+			ugp = u.getCurrentUserGameProfile(gameToken);
+		} catch (UserNotExists e) {
+			throw new Error(e);
+		}
+		return ugp.getHighscore();
+	}
+
+	public void saveScore(String gameToken, int score) throws GameTokenError {
+    	PersistenceManager pm = PMF.getManager();
+    	UserGameProfileGetter u = new UserGameProfileGetter();
+    	UserGameProfileData ugp = null;
+    	try {
+    		ugp = u.getCurrentUserGameProfile(pm, gameToken);
+    		if (score > ugp.getHighscore())
+    			ugp.setHighscore(score);
+    	} catch (UserNotExists e) {
+    		throw new Error(e);
+    	} finally {
+    		pm.close();
+    	}
+	}
+}

=== added file 'src/au/edu/unimelb/csse/mugle/server/api/KeyValueServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/api/KeyValueServiceImpl.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/api/KeyValueServiceImpl.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,89 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.api;
+
+import java.io.Serializable;
+
+import au.edu.unimelb.csse.mugle.client.api.KeyValueService;
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.server.model.KeyValuePairGetter;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+import au.edu.unimelb.csse.mugle.shared.api.KeyError;
+import au.edu.unimelb.csse.mugle.server.model.KeyValuePairData;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.*;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import javax.jdo.PersistenceManager;
+
+/**
+ * The server side implementation of KeyValueService.
+ */
+@SuppressWarnings("serial")
+public class KeyValueServiceImpl extends RemoteServiceServlet
+    implements KeyValueService
+{
+    public void put(String gameToken, String key, Serializable value) 
+    	throws GameTokenError
+    {
+    	PersistenceManager pm = PMF.getManager();
+    	KeyValuePairGetter k = new KeyValuePairGetter();
+    	KeyValuePairData kvp = null;
+    	
+    	try {
+    		kvp = k.getKeyValuePair(pm, key, gameToken, true);
+    		kvp.setValue(value);
+    	} catch (UserNotExists e) {
+    		throw new Error(e);
+    	} catch (KeyError e) {
+    		// XXX This should never happen (since we are using createIfNotFound=true)
+    		throw new Error(e);
+    	} finally {
+    		pm.close();
+    	}
+    }
+
+    public Serializable get(String gameToken, String key) 
+    	throws GameTokenError, KeyError
+    {
+    	KeyValuePairGetter k = new KeyValuePairGetter();
+    	KeyValuePairData kvp;
+    	try {
+    		kvp = k.getKeyValuePair(key, gameToken);
+    	} catch (UserNotExists e) {
+    		throw new Error(e);
+    	}
+        return (Serializable) kvp.getValue();
+    }
+
+    public boolean containsKey(String gameToken, String key) 
+    	throws GameTokenError 
+    {
+    	KeyValuePairGetter k = new KeyValuePairGetter();
+    	try {
+    		k.getKeyValuePair(key, gameToken);
+    	} catch (UserNotExists e) {
+    		throw new Error(e);
+    	} catch (KeyError e) {
+    		return false;
+    	}
+    	return true;
+
+    }
+    
+}

=== added file 'src/au/edu/unimelb/csse/mugle/server/api/UserServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/api/UserServiceImpl.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/api/UserServiceImpl.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,58 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.api;
+
+import au.edu.unimelb.csse.mugle.client.api.UserService;
+import au.edu.unimelb.csse.mugle.server.model.UserGetter;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+
+/**
+ * The server side implementation of KeyValueService.
+ */
+@SuppressWarnings("serial")
+public class UserServiceImpl extends RemoteServiceServlet
+    implements UserService
+{
+
+	public String getUserNickName() {
+		UserGetter u = new UserGetter();
+		try
+		{
+			return u.getCurrentUser().getUrlName();
+		}
+		catch (UserNotExists e)
+		{
+			throw new Error(e);
+		}
+	}
+	
+	public String getUserID() {
+		UserGetter u = new UserGetter();
+		try
+		{
+			return u.getCurrentUser().getGoogleID();
+		}
+		catch (UserNotExists e)
+		{
+			throw new Error(e);
+		}
+	}
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/AchievementData.java'
--- src/au/edu/unimelb/csse/mugle/server/model/AchievementData.java	2011-04-08 11:00:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/AchievementData.java	2011-04-16 09:26:25 +0000
@@ -47,6 +47,10 @@
     @Persistent
     private String name;
 
+    @UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, mappedBy="displayName")
+    @Persistent
+    private String displayName;
+    
     @UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, mappedBy="description")
     @Persistent
     private String description;
@@ -71,6 +75,7 @@
         
         this.userAchievements = new HashSet<UserAchievementData>();
         this.name = null;
+        this.displayName = null;
         this.description = null;
         this.maxProgress = null;
         this.game = null;
@@ -79,11 +84,12 @@
     }
     
     /* Should only be added through developers interface */
-    public AchievementData(String name, String description, Integer maxProgress, Game game) {
+    public AchievementData(String name, String displayName, String description, Integer maxProgress, Game game) {
 
         this.userAchievements = new HashSet<UserAchievementData>();
 
         this.name = name;
+        this.displayName = displayName;
         this.description = description;
         this.maxProgress = maxProgress;
         this.game = game;
@@ -101,6 +107,10 @@
     public String getName() {
         return this.name;
     }
+    
+	public String getDisplayName() {
+		return displayName;
+	}
 
     public String getDescription() {
         return this.description;
@@ -120,23 +130,27 @@
 
     // Setters
 
-    /* Setters are protected - in the case of wanting to change an achievement, better
+    /* in the case of wanting to change an achievement, better
      * to leave the old ID, so that users dont lose their achievements if they ever need
      * updating
      */
-    protected void setName(String newName) {
+    public void setName(String newName) {
         this.name = newName;
     }
+    
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
 
-    protected void setDescription(String newDescription) {
+    public void setDescription(String newDescription) {
         this.description = newDescription;
     }
 
-    protected void setMaxProgress(Integer newProgress) {
+    public void setMaxProgress(Integer newProgress) {
         this.maxProgress = newProgress;
     }
 
-    protected void setGame(Game game) {
+    public void setGame(Game game) {
         this.game = game;
     }
     

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/AchievementGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/AchievementGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/AchievementGetter.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,109 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.model;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.AchievementNotExists;
+
+public class AchievementGetter {
+
+	/**
+	 * Gets the Achievement of the current game based on its name - READ ONLY
+	 * @param name Name of the achievement you're looking for
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return The requested Achievement - READ ONLY
+	 * @throws GameTokenError
+	 * @throws AchievementNotExists
+	 */
+	public AchievementData getAchievement(String name, String gameToken) 
+			throws GameTokenError, AchievementNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getAchievement(pm, name, gameToken);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the Achievement of the current game based on its name, for editing object in datstore
+	 * The PersistenceManager must be handled by the caller
+	 * @param pm The Persistence Manager
+	 * @param name Name of the achievement you're looking for
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return The requested Achievement
+	 * @throws GameTokenError
+	 * @throws AchievementNotExists
+	 */
+	public AchievementData getAchievement(PersistenceManager pm, String name, String gameToken) 
+			throws GameTokenError, AchievementNotExists {
+		GameGetter g = new GameGetter();
+		GameData curGame = g.getCurrentGame(pm, gameToken);
+		
+		Query q = pm.newQuery(AchievementData.class, "game == g && name == n");
+		q.declareParameters("Game g, String n");
+		AchievementData achievement = (AchievementData) q.execute(curGame, name);
+
+		if (achievement == null) {
+			throw new AchievementNotExists(curGame.getUrlName(), name);
+		}
+		
+		return achievement;
+	}
+
+	/**
+	 * Gets the Achievement of the current game based on its Primary Key - READ ONLY
+	 * @param primaryKey the primary key of the Achievement
+	 * @return The requested Achievement - READ ONLY
+	 * @throws GameTokenError
+	 * @throws AchievementNotExists
+	 */
+	public AchievementData getAchievement(Long primaryKey) 
+			throws GameTokenError, AchievementNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getAchievement(pm, primaryKey);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the Achievement of the current game based on its Primary Key, for editing object in datstore
+	 * The PersistenceManager must be handled by the caller
+	 * @param pm The Persistence Manager
+	 * @param primaryKey the primary key of the Achievement
+	 * @return The requested Achievement
+	 * @throws GameTokenError
+	 * @throws AchievementNotExists
+	 */
+	public AchievementData getAchievement(PersistenceManager pm, Long primaryKey) 
+			throws AchievementNotExists {
+		AchievementData achievement = pm.getObjectById(AchievementData.class, primaryKey);
+
+		if (achievement == null) {
+			throw new AchievementNotExists(primaryKey);
+		}
+		
+		return achievement;
+	}
+}

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/DevTeamGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/DevTeamGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/DevTeamGetter.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,78 @@
+package au.edu.unimelb.csse.mugle.server.model;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.DevTeamNotExists;
+
+public class DevTeamGetter {
+
+	/**
+	 * Gets the DevTeamData by its urlName - READ ONLY
+	 * @param name The name of the DevTeamData
+	 * @return The DevTeamData - READ ONLY
+	 * @throws DevTeamDataNotExists
+	 */
+	public DevTeamData getDevTeam(String name) throws DevTeamNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getDevTeam(pm, name);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the DevTeamData by its urlName, for editing in the datastore
+	 * The Persistence Manager must be handled by the caller
+	 * @param pm The PersistenceManager
+	 * @param name The Name of the Dev Team
+	 * @return The DevTeamData
+	 * @throws DevTeamDataNotExists
+	 */
+	public DevTeamData getDevTeam(PersistenceManager pm, String name) throws DevTeamNotExists {
+		DevTeamData devTeam= null;
+		Query q = pm.newQuery(DevTeamData.class, "urlName == u");
+		q.declareParameters("String u");
+		devTeam = (DevTeamData) q.execute(name);
+		
+		if (devTeam == null) {
+			throw new DevTeamNotExists(name);
+		}
+		
+		return devTeam;
+	}
+	/**
+	 * Gets the DevTeamDataData by its primary key - READ ONLY
+	 * @param primaryKey
+	 * @return The DevTeamDataData - READ ONLY
+	 * @throws DevTeamDataDataNotExists
+	 */
+	public DevTeamData getDevTeam(Long primaryKey) throws DevTeamNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getDevTeam(pm, primaryKey);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the DevTeamDataData by its primary key, for editing in the datastore
+	 * The Persistence Manager must be handled by the caller
+	 * @param pm The PersistenceManager
+	 * @param primaryKey
+	 * @return The DevTeamDataData
+	 * @throws DevTeamDataDataNotExists
+	 */
+	public DevTeamData getDevTeam(PersistenceManager pm, Long primaryKey) throws DevTeamNotExists {
+		DevTeamData devTeam = pm.getObjectById(DevTeamData.class, primaryKey);
+		
+		if (devTeam == null) {
+			throw new DevTeamNotExists(primaryKey);
+		}
+		
+		return devTeam;
+	}
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameData.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameData.java	2011-04-08 11:00:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameData.java	2011-04-16 09:26:25 +0000
@@ -48,6 +48,10 @@
     @Persistent
 	private String urlName; //Must be unique
 
+    @UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, mappedBy="gameToken")
+    @Persistent
+	private String gameToken;
+    
     @UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, mappedBy="userRating")
     @Persistent
 	private Integer userRating;
@@ -82,6 +86,7 @@
         this.gameVersions = new HashSet<GameVersionData>();
         this.ugps = new HashSet<Key>();
         this.achievements = new HashSet<AchievementData>();
+        this.gameToken = null;
         this.urlName = null;
         this.userRating = null;
         this.totalRatings = null;
@@ -95,7 +100,9 @@
         this.ugps = new HashSet<Key>();
         this.achievements = new HashSet<AchievementData>();
 
-        //TODO: check that urlName is unique
+        //TODO: generate a gameToken
+        this.gameToken = null;
+  		//TODO: check that urlName is unique
         this.urlName = urlName;
         this.userRating = 0;
         this.totalRatings = 0;
@@ -137,6 +144,10 @@
     public Set<Key> getUserGameProfiles() {
         return this.ugps;
     }
+    
+	public String getGameToken() {
+		return gameToken;
+	}
 
 
     // Setters

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,132 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.model;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;
+
+public class GameGetter {
+	/**
+	 * Gets the Game by its Primary Key - READ ONLY
+	 * @param primaryKey
+	 * @return the Game - read only
+	 * @throws GameNotExists 
+	 */
+	public GameData getGame(Long primaryKey) throws GameNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getGame(pm, primaryKey);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the Game by its Primary Key, for editing object in datstore
+	 * PersistenceManager must be handled by the caller
+	 * @param pm the PersistenceManager
+	 * @param primaryKey
+	 * @return the Game
+	 * @throws GameNotExists 
+	 */
+	public GameData getGame(PersistenceManager pm, Long primaryKey) throws GameNotExists{
+		GameData game = pm.getObjectById(GameData.class, primaryKey);
+		
+		if(game == null) {
+			throw new GameNotExists(primaryKey);
+		}
+		
+		return game;
+	}
+	
+	/**
+	 * Gets the Game by its name - READ ONLY
+	 * @param name
+	 * @return the Game - read only
+	 * @throws GameNotExists 
+	 */
+	public GameData getGame(String name) throws GameNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getGame(pm, name);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the Game by name, for editing object in datstore
+	 * PersistenceManager must be handled by the caller
+	 * @param pm the PersistenceManager
+	 * @param name
+	 * @return the Game
+	 * @throws GameNotExists 
+	 */
+	public GameData getGame(PersistenceManager pm, String name) throws GameNotExists{
+		GameData game = null;
+		Query q = pm.newQuery(GameData.class, "urlName == u");
+		q.declareParameters("String u");
+		game = (GameData) q.execute(name);
+		
+		if(game == null) {
+			throw new GameNotExists(name);
+		}
+		
+		return game;
+	}
+	
+	/**
+	 * Gets the Game from a provided gameToken - READ ONLY
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return the Game - read only
+	 * @throws GameTokenError
+	 */
+	public GameData getCurrentGame(String gameToken) throws GameTokenError {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getCurrentGame(pm, gameToken);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the Game from a provided gameToken, for editing object in datstore
+	 * PersistenceManager must be handled by the caller
+	 * @param pm the PersistenceManager
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return the Game
+	 * @throws GameTokenError
+	 */
+	public GameData getCurrentGame(PersistenceManager pm, String gameToken) throws GameTokenError {
+		GameData curGame = null;
+		Query q = pm.newQuery(GameData.class, "gameToken == g");
+		q.declareParameters("String g");
+		curGame = (GameData) q.execute(gameToken);
+		
+		if(curGame == null) {
+			throw new GameTokenError(gameToken);
+		}
+		
+		return curGame;
+	}
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairData.java'
--- src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairData.java	2011-03-31 09:56:28 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairData.java	2011-04-16 09:26:25 +0000
@@ -109,6 +109,10 @@
 		this.value = value;
 	}
     
+	public void setUserGameProfile(UserGameProfileData ugp) {
+		this.ugp = ugp;
+	}
+    
     @Override
     public Key getServerKey() {
     	return this.id;
@@ -132,4 +136,6 @@
 		return KeyValuePair.class;
 	}
 
+
+
 }

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/KeyValuePairGetter.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,108 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package au.edu.unimelb.csse.mugle.server.model;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+import au.edu.unimelb.csse.mugle.shared.api.KeyError;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
+
+public class KeyValuePairGetter {
+	
+	/**
+	 * Gets the KeyValuePair for the key provided from the datastore for the
+	 * player currently playing - READ ONLY 
+	 * @param key the key to look up of the KeyValuePair
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return The request KeyValuePair - READ ONLY
+	 * @throws UserNotExists
+	 * @throws GameTokenError
+	 * @throws KeyError
+	 */
+	public KeyValuePairData getKeyValuePair(String key, String gameToken) 
+		throws UserNotExists, GameTokenError, KeyError {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getKeyValuePair(pm, key, gameToken);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	
+	/**
+	 * Gets the KeyValuePair for the key provided from the datastore for the
+	 * player currently playing, for editing object in datastore
+	 * The Persistence Manager must be handled by the caller
+	 * @param pm The PersistenceManager
+	 * @param key the key to look up of the KeyValuePair
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return The request KeyValuePair
+	 * @throws UserNotExists
+	 * @throws GameTokenError
+	 * @throws KeyError
+	 */
+	public KeyValuePairData getKeyValuePair(PersistenceManager pm, String key, String gameToken) 
+			throws UserNotExists, GameTokenError, KeyError {
+		return getKeyValuePair(pm, key, gameToken, false);
+	}
+
+	/**
+	 * Gets the KeyValuePair for the key provided from the datastore for the
+	 * player currently playing, for editing object in datastore - if the KeyValuePair isnt
+	 * found the parameter createIfNotFound determines whether an exception will be thrown, or
+	 * if a new KeyValuePair is created and associated to that User's UserGameProfile
+	 * The Persistence Manager must be handled by the caller
+	 * @param pm The PersistenceManager
+	 * @param key the key to look up of the KeyValuePair
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @param createIfNotFound boolean to determine whether a new KeyValuePair is created if its not found.s
+	 * @return The request KeyValuePair
+	 * @throws UserNotExists
+	 * @throws GameTokenError
+	 * @throws KeyError
+	 */
+	public KeyValuePairData getKeyValuePair(PersistenceManager pm, String key, String gameToken, boolean createIfNotFound) 
+			throws UserNotExists, GameTokenError, KeyError {
+		UserGameProfileGetter u = new UserGameProfileGetter();
+		UserGameProfileData ugp = u.getCurrentUserGameProfile(pm, gameToken);
+		
+		KeyValuePairData kvp = null;
+		
+		Query q = pm.newQuery(KeyValuePairData.class, "key == k && ugp == u");
+		q.declareParameters("String k, UserGameProfile u");
+		kvp = (KeyValuePairData) q.execute(key, ugp);
+		
+		/* If the key value pair isn't found - we want to create one if 
+		 * createIfNotFound is set to true
+		 */
+		if (kvp == null) {
+			if (createIfNotFound) {
+				kvp = new KeyValuePairData();
+				kvp.setKey(key);
+				kvp.setUserGameProfile(ugp);
+			} else {
+				throw new KeyError(key);
+			}
+		}
+		
+		return kvp;
+	}
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/ModelDataClass.java'
--- src/au/edu/unimelb/csse/mugle/server/model/ModelDataClass.java	2011-04-08 11:00:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/ModelDataClass.java	2011-04-16 09:26:25 +0000
@@ -24,7 +24,7 @@
 
 import com.google.appengine.api.datastore.Key;
 
-import au.edu.unimelb.csse.mugle.server.platform.PMF;
+import au.edu.unimelb.csse.mugle.server.PMF;
 import au.edu.unimelb.csse.mugle.shared.model.ModelClass;
 
 /**

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/ModelWrapper.java'
--- src/au/edu/unimelb/csse/mugle/server/model/ModelWrapper.java	2011-04-08 11:00:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/ModelWrapper.java	2011-04-16 09:26:25 +0000
@@ -22,7 +22,7 @@
 import javax.jdo.PersistenceManager;
 
 import au.edu.unimelb.csse.mugle.server.model.annotations.*;
-import au.edu.unimelb.csse.mugle.server.platform.PMF;
+import au.edu.unimelb.csse.mugle.server.PMF;
 import au.edu.unimelb.csse.mugle.shared.model.*;
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserPrivilegeException;

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/UserAchievementData.java'
--- src/au/edu/unimelb/csse/mugle/server/model/UserAchievementData.java	2011-03-31 09:56:28 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/UserAchievementData.java	2011-04-16 09:26:25 +0000
@@ -73,7 +73,7 @@
 		this.ugp = ugp;
 		this.achievement = achievement;
 		this.achieved = true;
-		this.progress = 1; //default assume 1/1
+		this.progress = 0;
 	}
 	
 	//Constructor for achievements with progress
@@ -136,15 +136,21 @@
     
     // Misc methods
 	
-	public void addProgress(int additionalProgress) {
+	public boolean addProgress(int additionalProgress) {
 		/*If the progress is greater or equal to the max progress of the achievement,
 		 * set it to be achieved, and set the progress to maxProgress
 		 */
-		if (this.progress + additionalProgress >= this.achievement.getMaxProgress()) {
-			this.progress = this.achievement.getMaxProgress();
+		int maxProgress = this.achievement.getMaxProgress();
+		int prog = this.progress + additionalProgress;
+		
+		if (prog >= maxProgress) {
+			this.progress = maxProgress;
 			this.achieved = true;
+			return true;
 		} else {
-			this.progress += additionalProgress;
+			this.progress = prog;
+			this.achieved = false; // If for some reason, they change the max progress to be less.
+			return false;
 		}
 	}
     

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/UserAchievementGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/UserAchievementGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/UserAchievementGetter.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,84 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.model;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.AchievementNotExists;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
+
+public class UserAchievementGetter {
+	/**
+	 * Gets the UserAchievement for the requested achievement of the user currently
+	 * playing the current game - READ ONLY
+	 * @param achievementName the name of the achievement requested
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return The UserAchievement - READ ONLY
+	 * @throws UserNotExists
+	 * @throws GameTokenError
+	 * @throws AchievementNotExists
+	 */
+	public UserAchievementData getUserAchievement(String achievementName, String gameToken) 
+			throws UserNotExists, GameTokenError, AchievementNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getUserAchievement(pm, achievementName, gameToken);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the UserAchievement for the requested achievement of the user currently
+	 * playing the current game, for editing object in datastore
+	 * The PersistenceManager must be handled by the caller
+	 * @param pm the Persistence Manager
+	 * @param achievementName the name of the achievement requested
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return The UserAchievement
+	 * @throws UserNotExists
+	 * @throws GameTokenError
+	 * @throws AchievementNotExists
+	 */
+	public UserAchievementData getUserAchievement(PersistenceManager pm, String achievementName, String gameToken) 
+			throws UserNotExists, GameTokenError, AchievementNotExists {
+		UserGameProfileGetter u = new UserGameProfileGetter();
+		AchievementGetter a = new AchievementGetter();
+		
+		UserGameProfileData ugp = u.getCurrentUserGameProfile(pm, gameToken);
+		AchievementData achievement = a.getAchievement(pm, achievementName, gameToken);
+		
+		Query q = pm.newQuery(UserAchievementData.class, "ugp == u && achievement == a");
+		q.declareParameters("UserGameProfile ugp, Achievement a");
+		UserAchievementData ua = (UserAchievementData) q.execute(ugp, achievement);
+		
+		/* If we can't find the UserAchievement it makes sense to create one
+		 * if the UserGameProfile and the Achievement exist.
+		 */
+		if (ua == null) {
+			ua = new UserAchievementData();
+			ua.setAchievement(achievement);
+			ua.setUserGameProfile(ugp);
+		}
+		
+		return ua;
+	}
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/UserData.java'
--- src/au/edu/unimelb/csse/mugle/server/model/UserData.java	2011-04-08 11:00:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/UserData.java	2011-04-16 09:26:25 +0000
@@ -42,6 +42,10 @@
     @PrimaryKey
     @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
     private Key id;
+   
+    @UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, mappedBy="googleID")
+    @Persistent	
+	private String googleID; //Unique userID provided by google
     
     @UserLevel(privateView=Role.DEVELOPER, publicView=Role.DEVELOPER, mappedBy="urlName")
     @Persistent	
@@ -80,6 +84,7 @@
 
         this.devTeams = new HashSet<Key>();
         this.ugps = new HashSet<Key>();
+        this.googleID = null;
         this.urlName = null;
         this.fullName = null;
         this.email = null;
@@ -95,6 +100,7 @@
         
 		this.urlName = url_name;
 		this.fullName = full_name;
+		this.googleID = null;
 		//TODO: check that email is unique and valid.
 		this.email = email;
 		this.role = role;
@@ -112,6 +118,10 @@
 	public String getUrlName() {
 		return this.urlName;
 	}
+	
+	public String getGoogleID() {
+		return googleID;
+	}
 
 	public String getFullName() {
 		return this.fullName;
@@ -147,6 +157,10 @@
 		//TODO: check that newName is unique.
 		this.urlName = newName;
 	}
+    
+	public void setGoogleID(String googleID) {
+		this.googleID = googleID;
+	}
 	
 	/* This can only be changed by an Admin, again we need an Admin control panel */
     public void setRole(Role newRole) {

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/UserGameProfileGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/UserGameProfileGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/UserGameProfileGetter.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,113 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.model;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.api.GameTokenError;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserGameProfileNotExists;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
+
+public class UserGameProfileGetter {	
+	/**
+	 * Gets the UserGameProfile by its Primary Key - READ ONLY
+	 * @param primaryKey
+	 * @return The UserGameProfile - READ ONLY
+	 * @throws UserGameProfileNotExists
+	 */
+	public UserGameProfileData getUserGameProfile(Long primaryKey) 
+		throws UserGameProfileNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getUserGameProfile(pm, primaryKey);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the UserGameProfile by its Primary Key, for editing the object in the datastore
+	 * PersistenceManager must be handled by the caller
+	 * @param pm The PersistenceManager
+	 * @param primaryKey
+	 * @return The UserGameProfile
+	 * @throws UserGameProfileNotExists
+	 */
+	public UserGameProfileData getUserGameProfile(PersistenceManager pm, Long primaryKey) 
+		throws UserGameProfileNotExists {
+		UserGameProfileData ugp = pm.getObjectById(UserGameProfileData.class, primaryKey);
+
+		if (ugp == null) {
+			throw new UserGameProfileNotExists(primaryKey);
+		}
+		return ugp;
+	}
+	
+	/**
+	 * Gets the UserGameProfile of the current user based on the game they're playing (read only)
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return the UserGameProfile - READ ONLY
+	 * @throws UserNotExists
+	 * @throws GameTokenError
+	 */
+	public UserGameProfileData getCurrentUserGameProfile(String gameToken) 
+				throws UserNotExists, GameTokenError {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getCurrentUserGameProfile(pm, gameToken);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets the UserGameProfile of the current user based on the game they're playing, 
+	 * for editing object in datstore
+	 * The Persistence Manager must be handled by the caller
+	 * @param pm The Persistence Manager
+	 * @param gameToken the secret game token for the current game, provided by the client
+	 * @return The UserGameProfile of the current user / game.
+	 * @throws UserNotExists
+	 * @throws GameTokenError
+	 */
+	public UserGameProfileData getCurrentUserGameProfile(PersistenceManager pm, String gameToken) 
+				throws UserNotExists, GameTokenError {
+		UserGetter u = new UserGetter();
+		GameGetter g = new GameGetter();
+		UserData curUser = u.getCurrentUser(pm);
+		GameData curGame = g.getCurrentGame(pm, gameToken);
+		UserGameProfileData ugp = null;
+		
+		Query q = pm.newQuery(UserGameProfileData.class, "user == u && game == g");
+		q.declareParameters("User u, Game g");
+		ugp = (UserGameProfileData) q.execute(curUser, curGame);
+		
+		/* If we can't find the UserGameProfile it makes sense to create one
+		 * if the user is playing the game
+		 */
+		if (ugp == null) {
+			ugp = new UserGameProfileData();
+			ugp.setGame(curGame);
+			ugp.setUser(curUser);
+		}
+		return ugp;
+	}
+
+}

=== added file 'src/au/edu/unimelb/csse/mugle/server/model/UserGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/UserGetter.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/UserGetter.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,160 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.server.model;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.Query;
+
+import au.edu.unimelb.csse.mugle.client.LoginInfo;
+import au.edu.unimelb.csse.mugle.server.LoginServiceImpl;
+import au.edu.unimelb.csse.mugle.server.PMF;
+import au.edu.unimelb.csse.mugle.shared.platform.exceptions.UserNotExists;
+
+public class UserGetter {
+
+	/**
+	 * Gets a User based on the Primary Key
+	 * @param primaryKey
+	 * @return The User class - READ ONLY
+	 * @throws UserNotExists
+	 */
+	public UserData getUser(Long primaryKey) throws UserNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getUser(pm, primaryKey);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets a User based on their Primary Key, for editing object in datstore
+	 * Persistence Manager must be handle by caller
+	 * @param pm The Persistence Manager
+	 * @param primaryKey
+	 * @return The User class
+	 * @throws UserNotExists
+	 */
+	public UserData getUser(PersistenceManager pm, Long primaryKey) throws UserNotExists {
+		UserData u = pm.getObjectById(UserData.class, primaryKey);
+		
+		if (u == null) {
+			throw new UserNotExists(primaryKey);
+		}
+		
+		return u;
+	}
+	/**
+	 * Gets a User based on their urlName - READ ONLY
+	 * @param name
+	 * @return The User class - READ ONLY
+	 * @throws UserNotExists
+	 */
+	public UserData getUserByName(String name) throws UserNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getUserByName(pm, name);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets a User based on their urlName, for editing object in datstore
+	 * Persistence Manager must be handle by caller
+	 * @param pm The Persistence Manager
+	 * @param name
+	 * @return The User class
+	 * @throws UserNotExists
+	 */
+	public UserData getUserByName(PersistenceManager pm, String name) throws UserNotExists {
+		Query q = pm.newQuery(UserData.class, "urlName == u");
+		q.declareParameters("String u");
+		UserData u = (UserData) q.execute(name);
+		
+		if (u == null) {
+			throw new UserNotExists(name);
+		}
+		
+		return u;
+	}
+	
+	/**
+	 * Gets a User based on their unique google ID - READ ONLY
+	 * @param googleID
+	 * @return The User class - READ ONLY
+	 * @throws UserNotExists
+	 */
+	public UserData getUser(String googleID) throws UserNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getUser(pm, googleID);
+		} finally {
+			pm.close();
+		}
+	}
+	
+	/**
+	 * Gets a User based on their unique google ID, for editing object in datstore
+	 * Persistence Manager must be handle by caller
+	 * @param pm The Persistence Manager
+	 * @param googleID
+	 * @return The User class
+	 * @throws UserNotExists
+	 */
+	public UserData getUser(PersistenceManager pm, String googleID) throws UserNotExists {
+		Query q = pm.newQuery(UserData.class, "googleID == u");
+		q.declareParameters("String u");
+		UserData u = (UserData) q.execute(googleID);
+		
+		if (u == null) {
+			throw new UserNotExists(googleID);
+		}
+		
+		return u;
+	}
+	
+	/**
+	 * Gets the currently logged in User - READ ONLY
+	 * @return the User - read only
+	 * @throws UserNotExists
+	 */
+	public UserData getCurrentUser() throws UserNotExists {
+		PersistenceManager pm = PMF.getManager();
+		try {
+			return getCurrentUser(pm);
+		} finally {
+			pm.close();
+		}		
+	}
+	
+	/**
+	 * Gets the currently logged in User, for editing object in datstore
+	 * Persistence Manager must be handled by the caller
+	 * @param pm The Persistence Manager
+	 * @return the current User
+	 * @throws UserNotExists
+	 */
+	public UserData getCurrentUser(PersistenceManager pm) throws UserNotExists {
+    	LoginServiceImpl loginService = new LoginServiceImpl();
+    	LoginInfo loginInfo = loginService.getCurrentUser();
+    	
+    	return getUser(pm, loginInfo.getUserID());
+	}
+
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/server/platform/AdminServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/platform/AdminServiceImpl.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/platform/AdminServiceImpl.java	2011-04-16 09:26:25 +0000
@@ -22,6 +22,7 @@
 import au.edu.unimelb.csse.mugle.shared.model.*;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.*;
 
+@SuppressWarnings("serial")
 public class AdminServiceImpl extends DeveloperServiceImpl implements AdminService {
 
 	@Override

=== modified file 'src/au/edu/unimelb/csse/mugle/server/platform/DeveloperServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/platform/DeveloperServiceImpl.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/platform/DeveloperServiceImpl.java	2011-04-16 09:26:25 +0000
@@ -22,6 +22,7 @@
 import au.edu.unimelb.csse.mugle.shared.model.*;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.*;
 
+@SuppressWarnings("serial")
 public class DeveloperServiceImpl extends UserServiceImpl implements DeveloperService {
 
     @Override

=== modified file 'src/au/edu/unimelb/csse/mugle/server/platform/ServiceHelper.java'
--- src/au/edu/unimelb/csse/mugle/server/platform/ServiceHelper.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/server/platform/ServiceHelper.java	2011-04-16 09:26:25 +0000
@@ -25,6 +25,7 @@
 import com.google.appengine.api.datastore.Query;
 import com.google.appengine.api.datastore.Query.FilterOperator;
 
+import au.edu.unimelb.csse.mugle.server.PMF;
 import au.edu.unimelb.csse.mugle.server.model.*;
 import au.edu.unimelb.csse.mugle.shared.model.*;
 import au.edu.unimelb.csse.mugle.shared.platform.exceptions.*;

=== added directory 'src/au/edu/unimelb/csse/mugle/shared/api'
=== added file 'src/au/edu/unimelb/csse/mugle/shared/api/Badge.java'
--- src/au/edu/unimelb/csse/mugle/shared/api/Badge.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/api/Badge.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,85 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.shared.api;
+
+import java.io.Serializable;
+
+/** Provides static (user-independent) information about a badge.
+ * This is the information that the developers have provided to describe a
+ * badge in the MUGLE control panel.
+ */
+public class Badge implements Serializable {
+    private static final long serialVersionUID = 4335025238357031611L;
+
+    private String name;
+    private String displayName;
+    private String description;
+    private int maxProgress;
+
+    public Badge()
+    {
+        this.name = "";
+        this.displayName = "";
+        this.description = "";
+        this.maxProgress = 0;
+    }
+
+    /** Construct a new Badge object. */
+    public Badge(String name, String displayName, String description,
+            int maxProgress)
+    {
+        this.name = name;
+        this.displayName = displayName;
+        this.description = description;
+        this.maxProgress = maxProgress;
+    }
+
+    /** The short name used to identify a badge. */
+    public String getName() {
+        return name;
+    }
+
+    /** The long "friendly" string which names the badge for users. */
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    /** The even longer text which describes how to earn the badge.
+     * This should be in the form of an instruction, such as "rescue the
+     * princess."
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /** For progress badges, the total number of progress units required to
+     * earn the badge. For non-progress badges, 0.
+     */
+    public int getMaxProgress() {
+        return maxProgress;
+    }
+
+    @Override
+    public String toString() {
+        return "Badge [" +
+            this.name + ", " +
+            this.displayName + ", " +
+            this.description + ", " +
+            this.maxProgress + "]";
+    }
+}

=== added file 'src/au/edu/unimelb/csse/mugle/shared/api/BadgeError.java'
--- src/au/edu/unimelb/csse/mugle/shared/api/BadgeError.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/api/BadgeError.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,34 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.shared.api;
+
+/** An error relating to the badge service.
+ */
+public class BadgeError extends Exception {
+    private static final long serialVersionUID = -5517149280375900036L;
+
+    public BadgeError()
+    {
+        super();
+    }
+
+    public BadgeError(String message)
+    {
+        super(message);
+    }
+}

=== added file 'src/au/edu/unimelb/csse/mugle/shared/api/GameTokenError.java'
--- src/au/edu/unimelb/csse/mugle/shared/api/GameTokenError.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/api/GameTokenError.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,34 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.shared.api;
+
+/** An error passing a game token to one of the API methods.
+ */
+public class GameTokenError extends Exception {
+    private static final long serialVersionUID = 4167419343442742185L;
+
+    public GameTokenError()
+    {
+        super();
+    }
+
+    public GameTokenError(String gameToken)
+    {
+        super("A game with the gameToken: " + gameToken + " does not exist.");
+    }
+}

=== added file 'src/au/edu/unimelb/csse/mugle/shared/api/KeyError.java'
--- src/au/edu/unimelb/csse/mugle/shared/api/KeyError.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/api/KeyError.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,43 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License as published
+ *  by the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.shared.api;
+
+/** An error for the key-value store; a key was not found.
+ */
+public class KeyError extends Exception {
+    private static final long serialVersionUID = 2248222166311987494L;
+
+    private String key;
+
+    public KeyError()
+    {
+        super();
+        this.key = null;
+    }
+
+    public KeyError(String key)
+    {
+        super("Key not found: " + key);
+        this.key = key;
+    }
+
+    public String getKey()
+    {
+        return this.key;
+    }
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/Achievement.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/Achievement.java	2011-04-08 10:13:04 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/Achievement.java	2011-04-16 09:26:25 +0000
@@ -23,12 +23,17 @@
 
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
-@SuppressWarnings("serial")
 @MugleDataWrapper
 public class Achievement extends ModelClass<Long, Achievement> {
 
+	private static final long serialVersionUID = -578289425640382697L;
+
+	// Fields
+	
     private String name;
 
+    private String displayName;
+    
     private String description;
 
     private Game game;
@@ -43,14 +48,13 @@
     // Constructors
     
     public Achievement() {
-        
         this.userAchievementKeys = null;
         this.name = null;
+        this.setDisplayName(null);
         this.description = null;
         this.maxProgress = null;
         this.game = null;
-        //TODO: Image
-        
+        //TODO: Image    
     }
 
     // Getters
@@ -59,6 +63,10 @@
         return this.name;
     }
 
+	public String getDisplayName() {
+		return displayName;
+	}
+    
     public String getDescription() {
         return this.description;
     }
@@ -91,6 +99,10 @@
         this.name = newName;
     }
 
+	public void setDisplayName(String displayName) {
+		this.displayName = displayName;
+	}
+    
     public void setDescription(String newDescription) {
         this.description = newDescription;
     }

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/DevTeam.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/DevTeam.java	2011-04-08 10:13:04 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/DevTeam.java	2011-04-16 09:26:25 +0000
@@ -23,12 +23,12 @@
 
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
-@SuppressWarnings("serial")
 @MugleDataWrapper
 public class DevTeam extends ModelClass<Long, DevTeam> {
 
+	private static final long serialVersionUID = -8745604337821752467L;
 	
-    // Fields
+	//Fields
 
 	private String urlName; //Must be unique
 

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/Game.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/Game.java	2011-04-08 10:13:04 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/Game.java	2011-04-16 09:26:25 +0000
@@ -23,11 +23,13 @@
 
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
-@SuppressWarnings("serial")
 @MugleDataWrapper
 public class Game extends ModelClass<Long, Game> {
 
-    /** The maximum possible game rating. */
+	private static final long serialVersionUID = -5139341163256410284L;
+
+
+	/** The maximum possible game rating. */
     public static final int MAX_RATING = 100;
 
     // Fields
@@ -39,6 +41,8 @@
 	private Integer totalRatings; // number of users who have rated this game
 
 	private DevTeam devTeam;
+    
+	private String gameToken;
 
 	private Set<Long> achievementKeys;
 
@@ -50,7 +54,7 @@
     // Constructors
 
 	public Game() {
-
+		this.gameToken = null;
         this.gameVersionKeys = null;
         this.ugpKeys = null;
         this.achievementKeys = null;
@@ -138,4 +142,12 @@
 		this.ugpKeys = ugps;
 	}  
 
+	public void setGameToken(String gameToken) {
+		this.gameToken = gameToken;
+	}
+
+	public String getGameToken() {
+		return gameToken;
+	}
+
 }
\ No newline at end of file

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/GameFile.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/GameFile.java	2011-04-08 10:13:04 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/GameFile.java	2011-04-16 09:26:25 +0000
@@ -24,12 +24,12 @@
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
 @MugleDataWrapper
-@SuppressWarnings("serial")
 public class GameFile extends ModelClass<String, GameFile> {
 
-
-    // Fields
-
+	private static final long serialVersionUID = -4361821523009813698L;
+
+    // Fields	
+	
 	private String path; //path to the file relative to the Game URL
 	
 	private Set<Long> gameVersionKeys; //Game versions this file belongs to

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/GameVersion.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/GameVersion.java	2011-04-08 10:13:04 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/GameVersion.java	2011-04-16 09:26:25 +0000
@@ -24,10 +24,11 @@
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
 @MugleDataWrapper
-@SuppressWarnings("serial")
 public class GameVersion extends ModelClass<Long, GameVersion>  {
 
 
+	private static final long serialVersionUID = -3598516897405942476L;
+	
     // Fields
 	
 	/*

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/InvalidRatingException.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/InvalidRatingException.java	2011-03-10 03:38:23 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/InvalidRatingException.java	2011-04-16 09:26:25 +0000
@@ -17,8 +17,11 @@
 
 package au.edu.unimelb.csse.mugle.shared.model;
 
-@SuppressWarnings("serial")
+
 public class InvalidRatingException extends Exception {
+
+	private static final long serialVersionUID = 8363848579992882462L;
+
 	public InvalidRatingException(int rating) {
 		super("Rating " + rating + " is invalid.  Rating must be between 0 and "
 		      + Game.MAX_RATING + ".");

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/KeyValuePair.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/KeyValuePair.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/KeyValuePair.java	2011-04-16 09:26:25 +0000
@@ -19,12 +19,13 @@
 
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
-@SuppressWarnings("serial")
 @MugleDataWrapper
 public class KeyValuePair extends ModelClass<Long, KeyValuePair> {
 
 
-    // Fields
+	private static final long serialVersionUID = 2222512665326609908L;
+
+	// Fields
 
 	private UserGameProfile ugp;
 
@@ -39,8 +40,7 @@
         this.ugp = null;
         this.value = null;
     }
-
-    
+	
     // Getters
 
     public String getKey() {

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/PromotedGame.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/PromotedGame.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/PromotedGame.java	2011-04-16 09:26:25 +0000
@@ -19,13 +19,13 @@
 
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
-@SuppressWarnings("serial")
 @MugleDataWrapper
 public class PromotedGame extends ModelClass<String, PromotedGame>  {
 
+	private static final long serialVersionUID = -7822465832242079100L;
 
     // Fields
-    
+	
 	private String urlName;
 
 	private GameVersion gameVersion;

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/User.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/User.java	2011-04-08 10:13:04 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/User.java	2011-04-16 09:26:25 +0000
@@ -23,13 +23,15 @@
 
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
-@SuppressWarnings("serial")
 @MugleDataWrapper
 public class User extends ModelClass<Long, User> {
 
-
-    // Fields
-
+	private static final long serialVersionUID = 3216382827435913653L;
+
+	//Fields
+	
+	private String googleID; //Unique id provided by google
+    
 	private String urlName; //Must be unique
 
 	private String fullName;
@@ -48,7 +50,7 @@
     // Constructors
 
     public User() {
-
+        this.googleID = null;
         this.devTeamKeys = null;
         this.ugpKeys = null;
         this.urlName = null;
@@ -102,6 +104,10 @@
 	public Class<User> getClassType() {
 		return User.class;
 	}
+    
+	public String getGoogleID() {
+		return googleID;
+	}
 
     // Setters
     
@@ -130,5 +136,9 @@
 	public void setActive(Boolean active) {
         this.active = active;
     }
-	
+
+	public void setGoogleID(String googleID) {
+		this.googleID = googleID;
+	}
+
 }

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/UserAchievement.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/UserAchievement.java	2011-03-30 15:46:59 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/UserAchievement.java	2011-04-16 09:26:25 +0000
@@ -19,12 +19,10 @@
 
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
-@SuppressWarnings("serial")
 @MugleDataWrapper
 public class UserAchievement extends ModelClass<Long, UserAchievement> {
 
-	
-	// Fields
+	private static final long serialVersionUID = 3314647480043897974L;
 
 	private UserGameProfile ugp;
 	
@@ -34,9 +32,6 @@
 	
 	private Integer progress;
 	
-	//By default assume a lack of this object means no progress towards achievement
-
-
     // Constructors
 
 	public UserAchievement() {
@@ -46,7 +41,6 @@
         this.progress = null;
     }
 	
-	
     // Getters
 	
 	public UserGameProfile getUserGameProfile() {
@@ -80,7 +74,7 @@
         this.achievement = achievement;
     }
 
-	public void setProgress(Integer progress) {
+    public void setProgress(Integer progress) {
         this.progress = progress;
     }
 

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/model/UserGameProfile.java'
--- src/au/edu/unimelb/csse/mugle/shared/model/UserGameProfile.java	2011-04-08 10:13:04 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/model/UserGameProfile.java	2011-04-16 09:26:25 +0000
@@ -23,12 +23,13 @@
 
 import au.edu.unimelb.csse.mugle.shared.model.annotations.*;
 
-@SuppressWarnings("serial")
 @MugleDataWrapper
 public class UserGameProfile extends ModelClass<Long, UserGameProfile> {
 
-
-    // Fields
+	private static final long serialVersionUID = 4735516690085498233L;
+
+	// Fields
+
     private Integer highscore;
 
     private User user;
@@ -87,7 +88,6 @@
 		return UserGameProfile.class;
 	}
     
-
     // Setters
 
 	public void setGame(Game game) {
@@ -101,7 +101,7 @@
     /* can only be set by the current Game */
 	public void setHighscore(Integer newScore) {
     	this.highscore = newScore;
-    }
+    }   
 
 	public void setKeyValuePairKeys(Set<Long> keyValuePair) {
 		this.keyValuePairKeys = keyValuePair;

=== added file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/AchievementNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/AchievementNotExists.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/AchievementNotExists.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,40 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.shared.platform.exceptions;
+
+import java.io.Serializable;
+
+public class AchievementNotExists extends Exception implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1007635148933156162L;
+
+	public AchievementNotExists() {
+		super();
+	}
+	
+	public AchievementNotExists(String gameName, String name) {
+		super("Achievement '" + name + "' does not exist for game '" 
+					+ gameName + "'");
+	}
+
+	public AchievementNotExists(Long primaryKey) {
+		super("Achievement with id '" + primaryKey + "' does not exist");
+	}
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/DevTeamExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/DevTeamExists.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/DevTeamExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a new dev team is to be created but the team already exists.
  */
-@SuppressWarnings("serial")
 public class DevTeamExists extends Exception implements Serializable {
 	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -1358761246676093332L;
+
 	public DevTeamExists() {
 		super();
 	}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/DevTeamNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/DevTeamNotExists.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/DevTeamNotExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a given dev team does not exist.
  */
-@SuppressWarnings("serial")
 public class DevTeamNotExists extends Exception implements Serializable {
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -4803672172249761591L;
+
 	public DevTeamNotExists() {
 		super();
 	}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameExists.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a new game is to be created but the game already exists.
  */
-@SuppressWarnings("serial")
 public class GameExists extends Exception implements Serializable {
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 77823808801952791L;
+
 	public GameExists() {
 		super();
 	}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a given game does not exists.
  */
-@SuppressWarnings("serial")
 public class GameNotExists extends Exception implements Serializable {
 	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -921450195750245805L;
+
 	public GameNotExists() {
 		super();
 	}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameVersionExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameVersionExists.java	2011-03-20 11:06:48 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameVersionExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a new game version is to be created but the game version already exists.
  */
-@SuppressWarnings("serial")
 public class GameVersionExists extends Exception implements Serializable {
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -1838656621043022076L;
+
 	public GameVersionExists() {
 		super();
 	}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameVersionNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameVersionNotExists.java	2011-03-20 11:06:48 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameVersionNotExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a given game version does not exist.
  */
-@SuppressWarnings("serial")
 public class GameVersionNotExists extends Exception implements Serializable {
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8443470039286468097L;
+
 	public GameVersionNotExists() {
 		super();
 	}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/PromotedGameExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/PromotedGameExists.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/PromotedGameExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a new promoted game is to be created but the promoted game already exists.
  */
-@SuppressWarnings("serial")
 public class PromotedGameExists extends Exception implements Serializable{
 	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8952108432578356503L;
+
 	public PromotedGameExists() {
 		super();
 	}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/PromotedGameNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/PromotedGameNotExists.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/PromotedGameNotExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a given promoted game does not exist.
  */
-@SuppressWarnings("serial")
 public class PromotedGameNotExists extends Exception implements Serializable{
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 491619454215655500L;
+
 	public PromotedGameNotExists() {
 		super();
 	}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserExists.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a new user is to be created but the user already exists.
  */
-@SuppressWarnings("serial")
 public class UserExists extends Exception implements Serializable{
 	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -6023406419592834887L;
+
 	public UserExists() {
 		super();
 	}

=== added file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserGameProfileNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserGameProfileNotExists.java	1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserGameProfileNotExists.java	2011-04-16 09:26:25 +0000
@@ -0,0 +1,42 @@
+/*  Melbourne University Game-based Learning Environment
+ *  Copyright (C) 2011 The University of Melbourne
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package au.edu.unimelb.csse.mugle.shared.platform.exceptions;
+
+import java.io.Serializable;
+
+import au.edu.unimelb.csse.mugle.shared.model.Game;
+import au.edu.unimelb.csse.mugle.shared.model.User;
+
+public class UserGameProfileNotExists extends Exception implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -6486386063392954899L;
+
+	public UserGameProfileNotExists() {
+		super();
+	}
+	
+	public UserGameProfileNotExists(User u, Game g) {
+		super("A UserGameProfile for the User " + u.getUrlName() + " for the game " + g.getUrlName() + " does not exist");
+	}
+
+	public UserGameProfileNotExists(Long primaryKey) {
+		super("A UserGameProfile with the primary key: " + primaryKey + " does not exist.");
+	}
+}

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserNotExists.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserNotExists.java	2011-04-16 09:26:25 +0000
@@ -24,9 +24,13 @@
 /**
  * Exception used when a given user does not exist.
  */
-@SuppressWarnings("serial")
 public class UserNotExists extends Exception implements Serializable{
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -209272321411054068L;
+
 	public UserNotExists() {
 		super();
 	}
@@ -36,7 +40,7 @@
     }
     
     public UserNotExists(String urlName) {
-    	super("User + '" + urlName + "' does not exist");
+    	super("User '" + urlName + "' does not exist");
     }
     
     public UserNotExists(Long id) {

=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserPrivilegeException.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserPrivilegeException.java	2011-03-16 12:12:34 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/UserPrivilegeException.java	2011-04-16 09:26:25 +0000
@@ -23,10 +23,14 @@
 import au.edu.unimelb.csse.mugle.shared.model.User;
 import au.edu.unimelb.csse.mugle.shared.model.Role;
 
-@SuppressWarnings("serial")
 public class UserPrivilegeException extends Exception implements Serializable {
 	
 	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 6418600873641304072L;
+
+	/**
      *
      * @param u The user that's trying the operation
      * @param r The required role

=== modified file 'war/WEB-INF/web.xml'
--- war/WEB-INF/web.xml	2011-04-08 09:52:55 +0000
+++ war/WEB-INF/web.xml	2011-04-16 09:26:25 +0000
@@ -4,7 +4,6 @@
     "http://java.sun.com/dtd/web-app_2_3.dtd";>
 
 <web-app>
-    
   <!-- Default page to serve -->
   <welcome-file-list>
     <welcome-file>Mugle.html</welcome-file>
@@ -62,7 +61,7 @@
     <url-pattern>/mugle/users</url-pattern>
   </servlet-mapping>
   
-  <servlet>
+    <servlet>
     <servlet-name>DataService</servlet-name>
     <servlet-class>au.edu.unimelb.csse.mugle.server.model.DataServiceImpl</servlet-class>
   </servlet>
@@ -81,5 +80,47 @@
     <servlet-name>FileIOService</servlet-name>
     <url-pattern>/mugle/files</url-pattern>
   </servlet-mapping>
+  
+  <!-- API servlet mappings -->
+  
+  <servlet>
+    <servlet-name>keyValueServlet</servlet-name>
+    <servlet-class>au.edu.unimelb.csse.mugle.server.api.KeyValueServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>keyValueServlet</servlet-name>
+    <url-pattern>/mugle/api-keyvalue</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>UserServlet</servlet-name>
+    <servlet-class>au.edu.unimelb.csse.mugle.server.api.UserServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>UserServlet</servlet-name>
+    <url-pattern>/mugle/api-user</url-pattern>
+  </servlet-mapping>
+  
+  <servlet>
+    <servlet-name>HighscoreServlet</servlet-name>
+    <servlet-class>au.edu.unimelb.csse.mugle.server.api.HighscoreServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>HighscoreServlet</servlet-name>
+    <url-pattern>/mugle/api-highscore</url-pattern>
+  </servlet-mapping>
+  
+  <servlet>
+    <servlet-name>BadgeServlet</servlet-name>
+    <servlet-class>au.edu.unimelb.csse.mugle.server.api.BadgeServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>BadgeServlet</servlet-name>
+    <url-pattern>/mugle/api-badge</url-pattern>
+  </servlet-mapping>
 
 </web-app>


Follow ups