Gucci Games: VOOGASalad

December 8th 2015

VOOGASalad was the final project for my CS 308 (Software Design and Implementation) class at Duke University. The final project contained more than 17,000 lines of code (not counting whitespace) and is one of the most significant coding projects I’d ever worked on up to that point.

Demo Click Here to Download Archive #

For whatever reason, the code on Github does not run (as I’m guessing someone pushed broken code to master during our analysis period.) I’ve thus created a working archive of the code that’s a slightly earlier build. With this build, one can create games, and one can play games. However, we never did fully get the creating a game and loading it in the game player functionality working. However, there are two sample “games” to play with.

Additionally, the High Scores functionality will not work, and will crash the game. Saving / Loading may do the same, because the server for the cloud utility is no longer running.

In order to run the project, use Eclipse Mars (which is what it is tested under). It’ll likely work under a future version but cannot be sure.

Download archive, go to Import -> Existing Project. Select the archive. There are two run configurations included – Game Authoring and Game Player. If you do not see these run configurations, you can either manually launch the PlayerMain.java or GaeMain.java classes, or create new Java Application run configurations for them. Cheers!

Overview #

This was the final project for my CompSci 308 (Software Design and Implementation) at Duke University. It was a project we worked on for about 6 weeks on a team of ten people. We were supposed to use Java FX to develop a four part project:

  1. Game Development Environment
  2. Game Engine
  3. Game Data System
  4. Game Player System

Out of these four, I developed the Game Player System mostly by myself, as well as significant parts of the Game Data System. For example, I developed an abstracted game data loading system that was used both by the Game Player and the Game Dev Environment to load resources, pictures, sounds, and so on. Additionally, I developed our “Utility”, a two part modular Cloud Data System which communicated with a backend server written in PHP to store and retrieve Games, Game Saves, and High Scores. The server is no longer running so this will not work if it is run now!

I’ve reproduced parts of the project here to showcase it.

I did not work on the Game Dev Environment or Game Engine Teams, though they did interface with my code indirectly. We made an unfortunate decision late on this project to attempt to integrate a custom scripting engine into our engine using Groovy. This turned out to be a mistake as we could never get it working entirely. As such, the “games” included here are hand coded (and thus rather simplistic).

The final project turned out to be around 17000 lines of code (not including white space) of which I estimate I contriuted about 5000-7000 (including whitespace). Some of the code got a bit messy given the tight timeframe, but it’s still a good demonstration of my work on a large scale project on a tight schedule (as of a year ago).

Some things I was proud of developing: Cloud Data system, Minimap in Game Player, speed of game player, attractive looking menu system with custom fonts loaded by game data system, abstract file loading and retrieval system, and game scene loading / switching system. Most of this was thought up on the fly. Knowing what I did at the end of the project, I would’ve probably developed it differently, but it was still fun to work on from the start.

Demo #

This code currently does not work. I suspect someone pushed broken code to master after we presented this project.

When I have a chance (after working on some more important things), I will make it runnable :smiley:.

Analysis #

The rest of this file is excerpts from my ‘analysis’ written for the class which describes aspects of the development process and my design decisions.

Features #

Features I worked on extensively #

  1. Game Player - I wrote all the basic classes for the Game Player’s internal structure and such. Others, such as John and Daniel, added functionality, but I wrote the basic system that held it together, as well as the Map, MiniMap, significant portions of the GameLoader, Menus, scenes, loading bar, overlays, handlers, etc. Every major part of the game player which was necessary was completed. The only things I would have liked to do but didn’t finish:
    1. Better loading bars.
    2. Make end level screen an overlay.
    3. Put a background image behind the map, which can be configured by GAE.
    4. Better display for health/attributes/etc.
    5. Make key handling work a bit better.
  2. Resource Manager Utility - I decided that the game needed a better way to handle resources, so I set out to create a standardized form to do so, which would be as transparent as possible to the GAE. Thus I wrote the ResourceManager class, and integrated it with my genericized ResourceDatabase instances to handle images, sprites, etc (it could be modified to support sound and other types of resources too). I could have fully genericized the class, but instead I created different functions to save/retrieve images and sprites, as the functionality was slightly different.

  3. Game Data - although I did not write the XStreamGameEngine class, I contributed heavily to make this portion of the code easier to use by standardizing the location of saved games, creating a master XML file, and creating helper classes to work with the Game Player to better adhere to the single responsibility principle. I felt that the XStreamGameEngine class should be primarily responsible for working with XStream, and that other features should lie elsewhere. However, I did not add Groovy functionality. That was added by John I believe. They were working on it Tuesday night before the demo and I simply had to go back and sleep because I was exhausted (I also felt that Groovy should be something we just chalked up as unfinished and discussed in our analysis, making games with what we had, because the GAE to player pipeline was working sans Groovy).

  4. Cloud Utility - I wrote the cloud save/retrieve high scores feature, but did not have time to write code to zip games and saves up / back from the cloud (although the path to do so is clear within the current cloud utility).

Development of GamePlayer #

My main contribution to this project was the development of the GamePlayer system. My approximate process for developing it was as follows:

  1. Write base classes and interfaces that I would use for the Design phase of our project.

  2. Write classes to manage Game Windows. This was an early design decision. I decided that the GamePlayer should be able to support multiple windows, whether or not an individual game required them. As it stands, the GamePlayer uses only one window, but it can load more using a standardized interface by adding their class names to a resource file. Any windows added in this manner will have access to other parts of the GamePlayer as needed.

  3. Write classes to manage GameScenes. - I wrote the GameSceneManager and GameScene classes to manage which order game “scenes” were displayed in. I wrote these classes to encapsulate logic along with a JavaFX scene. These scenes are loaded via a master configuration file (for the PrimarySceneManager - there could be others for other windows). They each specify a next scene as well as their name. Some specify a previous scene. They can also be loaded by name, which is a feature I use in some scenes to determine program flow. I decided I wanted to be able to add functionality indirectly, thus the GameScene classes are loaded from a resource file and reflected in.

  4. Begin writing GameScenes for various portions of game - i.e. splash screen, main game scene, etc.

  5. Write GameLoader class - responsible for loading game data into the game player. Basically, this was a GamePlayer class which would be responsible for loading and saving games as needed, preventing me from needing to access the GameData directly.

  6. Write WindowComponent abstract class and some initial components - for example, MainMap and MiniMap are both WindowComponents. Essentially, a WindowComponent is a JavaFX Parent node with logic added. This allowed the GamePlayer to be modular, a design goal. It also meant that they all conformed to a standard interface.

  7. Write GameController class and interfaces - the GameController was responsible for interfacing with the game engine. This allowed me to restrict code modifications mostly to the controller class as the team (predictably) added functionality to the engine or changed its interfaces.

  8. Write test game to test out map - I asked the engine team for help making a test game, which they helped with. This allowed me to test the map.

  9. Get help with sidebars - John and Daniel wrote the two GamePlayer sidebars.

  10. Add new features, once basic player was created - once the GamePlayer could communicate with the engine and all these components were in place, the rest was easier to work on. I just added new GameScenes and modified the flow of the existing ones, or added new WindowComponents. Once in a while, I’d refactor or change functionality, though for the most part those types of changes were in the controller and GameLoader type classes.

I worked on the bulk of the GamePlayer work solo, though I coded frequently with my teammates. This way I could show them progress, ask questions, and get feedback. John and Daniel in particular were helpful because I could explain the design to them and make sure it made sense. They wrote their code to integrate nicely with mine so we had a good dynamic going.

The biggest design choice I faced working on the player was how to make it extensible given the unpredictability in what the engine/GAE would support. Thus I tried to make things generic as much as possible so I could add new instances of classes without changing the superclasses. This worked very well, and I’m very proud of how well divided the GamePlayer is.

Another major design decision was whether to reuse code from the GAE or not. Ultimately, I decided to code my own map because the two of us were using very different systems and had very different goals for our maps. This turned out to be a good decision, even if it resulted in some extra code, because my map ended up being so different from his (Mike’s). We did take inspiration from each other, however, for example when he used my code to stack multiple units on layers.

A final major design decision was how much control to give the user over loading/saving games etc. I eventually decided to use a big menu system controlled by keyboard or mouse over using dropdown menus. I felt that dropdown menus and file browsing dialogs would give the user more control over where their games were saved/loaded from, but I also felt then it would feel more like Microsoft Word than a game program. As I designed the Game Player, foremost in my mind was the “Why Software Sucks” article. I really did not want to burden the user with my implementation details. Thus, when I could avoid it, I preferred just to load all available games from an XML file, or load Game Saves from a preconfigured directory (in resource files for easy changes without recompiling!) This made the interface much more seamless, which was very important to me.

Work needed to be done after it was “done”
The GamePlayer was never entirely “done,” but what was finished was the basic classes supporting it - GameSceneManager, GameWindowManager, GameController, GameScene, etc. For the most part, once the skeleton was in place, I was primarily adding features. I did not have to change much code in the skeleton, except when it made sense to do so to avoid duplication (i.e. adding Overlay functionality to the GameScene class). For example, adding the MiniMap required me to create an extension of DisplayComponent (a subclass of WindowComponent created by John), and to add minimal functions MainMap and MapCell in order to accommodate its data requirements.

Overall Design #

The program is divided into four broad parts:

  1. Game Player (I worked on nearly exclusively)

  2. Game Data System (I worked on extensively)

  3. Game Authoring Environment

  4. Game Engine

Additionally, there are various helper and utility classes outside of these four subsystems, such as the ResourceManager and TwoWayMap, for example. Additionally, there is a package called Demo, which contains DemoMaker.java, a class which will create a working game for testing purposes (and which we had to end up using for our demo because we were still having bugs :-( )

In addition to this, there is some clutter which should be removed rather than be on Master. For example, networkingTest, groovy (which just contains tests), and various other classes left over from our initial design. If this were an enterprise project, we would have to go back and clean up all the chaff.

Each part is designed as follows:

Game Player (My Work) #

Basic Design #

The GamePlayer is a modular Window/Scene system for representing games. It is divided into several broad packages:

  • Config - contains all resource files used by the GamePlayer for configuration, as well as a PlayerConfig helper class which loads them automatically for individual player components.

  • Controller - contains the GameController class which ties all parts of the GamePlayer together, as well as all code for interfacing with the GameEngine. Additionally, it contains several interfaces for the controller that are used by different parts of the player depending on the level of access required. This package confines changes to the Engine locally so that the GamePlayer is largely independent of the design of the engine.

  • EventHandler - code designed to help with handling events. Didn’t end up being used.

  • Exceptions - code designed to help with error handling. Didn’t end up being implemented completely.

  • GameLoader - code which interfaces with the GameData interface to make loading/saving games easy.

  • Scenes - classes for each “scene” of a game - i.e. main menu, main game, save game, splash, etc. Divided into abstract classes, the GameSceneManager which loads scenes in via reflection from a resource file and switches between them, and the concrete package which contains the individual scene definitions. GameScenes are composed of logic and javaFX classes.

  • Windows - code for each of the GameWindow. Contains an abstract GameWindow class representing a window and a GameWindowManager which handles interactions between windows, giving them access to necessary game state information. Right now, only one window is used, but multiple windows are supported and can be added by editing resource files and creating a new subclass of GameWindow.

  • Windows.MainWindow* - contains all the parts of the MainGameWindow class. If I were to refactor the GamePlayer, I would move the components to the root of the GamePlayer, since they’re not really specific to the GamePlayer. All window components extend the WindowComponent superclass, making them interchangeable and interconnectable. Thus, there’s no reason why another window couldn’t load various windowcomponents. Each of the subpackages, components, map, and menubar contain classes used to represent each portion of the MainGameWindow.

  • root - PlayerMain.java is the main class for the GamePlayer

How it Works Together #

The GamePlayer connects to the GameEngine via the GameController class. This keeps it (mostly) insulated from changes to the GameEngine.

The GamePlayer connects to the GameData system via both the GameLoader class, for loading/saving games, and the GameController class for loading resources.

Nothing directly depends on the GamePlayer. Rather, the GamePlayer is designed so that modifications to the GameData stay confined in the GameLoader class, and modifications to the GameEngine stay (mostly) confined to the GameController class. This made my life much easier as I developed the classes for the GamePlayer, as it made adapting the code trivial.

Game Data System #

Basic Design #

The GameData system is divided into several main classes and some helpers. The classes are divided up to keep their responsibilities separate, as much as possible:

  • XStreamGameEngine - responsible for writing and reading games from XML files (.gg files for Game Data, .ggl for Groovy Class Loaders). Also contains code to convert Game Names to File Names, so that any game name can be sanitized. Makes it easy to load a game then - we don’t have to provide a complete path, just the String name of the game.

  • GameDataManager - responsible for managing the other GameData classes. Never was completed - was supposed to shield other classes from directly using XStreamGameEngine. As of now, it’s used extensively by the player, to list available games, to load games, save game states, and manage disk operations with resources (thus it is used by the GameResourceManager).

  • GameFileHelper - helper class for GameDataManager. Performs actually file operations, such as directory listings, copying files to directories, making directories, etc. Created to better conform to single responsibility principle so that the GameDataManager doesn’t need to actually contain all the boilerplate code necessary for safe access of the disk.

  • GameListManager - helper class that manages the gamelist.xml file. I decided that making the user open a file browse dialog to load a game into the player was very old fashioned. I’ve never had a mobile app pop open a file dialog in a game type environment. Thus, I decided to keep a master list of games in an XML, so this class is responsible for maintaining it. Works with GameDataManager.

  • FileLoader - actually reads and saves XML files.

  • GameResourceManager - a helper class that’s really part of the GameData system. Manages resources for the Player and the GAE. Copies them to games if asked (GAE uses this feature). Makes sure that images can be referenced simply by relative paths and abstracts all resource accesses from higher level operations.

  • xxDatabase - various database classes, implementing the generic ResourceDatabase<T> interface. Manage images, sprites, and colors (for images). I could’ve included the fontmanager in here too, but didn’t.

How it Works Together #

The GAE and Game Player both work with the GameData system through various intermediate classes. The Game Player uses the GameLoader class, while the GAE uses the GAEModel class. This gives some separation from the internals of the data system. As for resources, the Game Player uses the GameController as an intermediate, while the GAE again uses the GAEModel class. Resource Requests are simple, the Game Player calls requestImage(“tiles/water.jpg”) for example, and the ResourceManager will look in the folder for that game and retrieve an Image object for that resource. It is smart, however, and will not create a second Image if one has already been created, to avoid memory leaks.

As for saving and loading, we save completed Game Engines to represent our game. We realized early on that our representation of a game was so closely intertwined with the logic that it would be much more work to separate out a Game Engine from the basic data comprising it. Thus we save complete GameEngines to files, wrapped in a class called GameInfo which attaches to the engine information about the GUI layout of the Game Player (this allows a user to select whether they want to display a MiniMap or not, for example. Someone playing chess may not want a minimap).

Game Authoring Environment #

Basic Design #

The GAE is divided into three basic sections: GUI, model, and utilities. The GUI houses a package hierarchy containing code for various elements of the user interface, containing packages such as gaedialog, levels, map, menubar, sidebar, and statusbar. These all represent the various portions of the editing environment. Most extend JavaFX classes for direct integration in the JavaFX scene graph.

The model package contains an in-progress representation of a game, as well as factories necessary to build an engine from the components.

As I wrote earlier, I think it would’ve been better to design the engine in such a way that there was not a need for two different representations of an engine. I think this added needless complexity and ultimately caused us problems later on.

How it Works Together #

The GAE communicates with the rest of the GucciGames system via the Model classes, which are responsible for maintaining a game in progress and converting it into a saveable Game Engine instance. Additionally, via the ResourceManager, the GAE also works with the GamePlayer. The ResourceManager automatically keeps track of what images have been added to a game (it would work for sprites, or with modifications, sounds as well, but we didn’t implement those) and automatically copies them to a game in progress, building folder structure as necessary. This makes games completely independent from the GAE, by making all images use paths relative to the Game Folder. This would allow us to send a game to someone who was not running a copy of the GAE, and they could still play the game.

Lastly, the GAE works with the GameData system to finally package up a game for playing. It does this through the GAEModel.java class, which communicates with the XStreamGameEngine class to write a game to an XML file.

Game Engine #

Basic Design #

Our GameEngine is designed to be a generic representation of a Turn Based Strategy game which houses the rules of the game and the units contained in the game. Thus, we can save our GameEngine to a file and load it back in to play a game.

It’s divided into several broad packages, which are composed together to create the objects and rules making an engine:

  • CommunicationParameters - various classes holding “Parameters” passed between objects and other parts of the engine.

  • defaultCharacteristics - a set of preconfigured characteristics that can be assigned to objects in a game, to give a developer some starting objects to use. For more complicated operations, they will need to make their own using Groovy.

  • gameConditions - a collection of classes which are responsible for checking the state of the game, such as whether its over, whether only one player is left, etc.

  • gamePlayer - a collection of classes representing someone playing the game, including their units, all the players playing the game, etc

  • gameRules - a collection of classes representing rules assigned to the game. For example, the number of attacks a unit is allowed to make before their turn ends.

  • groovyEngine - collection of classes representing custom objects, events, and characteristics scripted using groovy

  • mapObject - collection of classes that represent objects on the map (such as tiles, units, and structures)

  • objectActions - collection of classes representing actions that an object on the map can take, such as building, attacking, etc

  • targetCoordinate - collection of classes relating to coordinates on the map. Can either be single coordinates or collections of coordinates. Used for defining where a unit is located, or where an action affects, etc.

  • root - classes for the GameEngine itself, as well as networked components of it.

How it Works Together #

In order to reduce complexity, we decided to save an entire instance of the Game Engine using XStream. This works well - fields that should not be saved (such as controllers from the GAE) are marked transient and volatile so XStream doesn’t serialize them.

The GamePlayer loads the Engine in and then makes calls on it via its GameController. It then requests the result from the GameEngine and updates the map.

The GAE configures the GameEngine using its internal GAEModel class, which builds a GameEngine using various factories.

GamePlayer Design in Detail #

As stated above, the GamePlayer is a modular system consisting of various GameWindow instances and GameScene instances at the highest level. GameWindows and GameScenes are loaded via reflection from resourcefiles loaded by the PlayerConfig helper.

Each GameWindow is referenced in a resource file as GameWindowX and each GameScene as GameSceneX - this allows simple parsing and reflecting.

Each GameScene is composed of a Parent JavaFX node which is loaded into the superclass. The superclass GameScene then manages key handling, overlaying (such as for menus), and access to other parts of the game, such as the SceneManager, Controller, or Window. This allows each GameScene instance to focus on the logic of running that scene rather than on boilerplate adding to stage, etc. (Although each scene does configure its panes and display methods).

GameScenes often make use of WindowComponent instances. Each WindowComponent consists of a JavaFX parent node and logic associated with it. Each WindowComponent has access to the controller through the WindowComponent superclass. This allows scenes to easily configure themselves using a library of common components, such as menus, overlays, maps, sidebars, loaders, etc. WindowComponents can even add children to themselves if desired (some components use this capability, such as the SplashScreen component, which allows a LoadingBar to be added to it). The primary motivation for creating this was to allow the player to be modular and configurable by the end user (which it is, in the GAE).

A second reason I created WindowComponents was to isolate sections of code to be implemented later. For example, we talked a lot about allowing people to set a background image for the game. Thus, I created a BackgroundComponent class which does nothing right now. If we decided to add background functionality, I only have to modify this one class with the code necessary to display the image. All other components which use BackgroundComponent will then update automatically.

Using WindowComponents also allowed for some other cool tricks, such as allowing a menu to be displayed either as an overlay or as a scene by itself with NO MODIFICATION. Since the MenuScreen is just a WindowComponent, I can display one by itself in a GameScene (as I do for the Main Menu), or I can display it as an overlay, by passing a MenuScreen into an OverlayComponent, which automatically displays it as an overlay. By using composition in this manner, the GamePlayer is very unrestrictive in terms of how it is structured.

The only inheritance relationships in the GamePlayer are genuine is-a relationships, such as each GameWindow inheriting the GameWindow abstract class (because they need the methods from it), each GameScene inheriting GameScene, etc.

The GamePlayer makes use of extensive Observer-Observable patterns. For example, the MiniMap updates itself by observing the ScrollProperty of the MainMap. The SideBars observe the MainMap’s selected units.

The GamePlayer makes use of interfaces, to allow new components to be developed; For example, I wanted the main map to support centering around a coordinate or around a percent. These functions are used right now to keep the MainMap in sync with the MiniMap, but they could easily be used to animate the end of a turn by centering around units which are active.

The GamePlayer makes extensive use of functional interfaces and streams. Ever since learning about streams, I’ve loved using them to quickly parse data or replace loops (where possible). I use a functional interface called MenuAction to store the results of clicking on a menu button. This made it easy to make a menu simply by creating a MenuScreen and passing in a map of Strings to MenuActions.

The GamePlayer trieds to conform to the Single Responsibility Principle. As the GamePlayer was in development, it was impossible to make everything Open/Closed without resorting to the type of reflection that made portions of the GAE completely unintelligible. Thus, my objective was to divide classes according to responsibilities as much as possible. Some classes did get big because I did not have time to refactor them, but for the most part they do one job. For example, MapCell holds code related to displaying and managing map cells. WindowComponents contain code related to drawing one item on the window. GameWindows contain the code necessary to display a window. If I could refactor some classes, I’d divide MainMap, MapCell, GameController, and some others up into subclasses.

Cloud Utility Design (My Code “Masterpiece”) #

Note: this code is somewhat cleaned up from the version in the repo as it did not have to be tested and run to the same standard.

Selected classes - did not include all for brevity.

Purpose #

This utility is designed to help people easily upload and retrieve high scores from the cloud. Many games benefit from fostering a sense of competition between players by having a list of online high scores. By making the creation of such a feature easy, this utility allows people to add it to their games without having to set up a server or really do anything (besides editing a configuration file and calling a function).

I chose to showcase several files from the Cloud Utility, which I believe showcase my strengths in different areas that I’ve learned in this course. Overall, the utility is highly extensible and highly implementation independent. I’ve carefully designed it to use classes such as CloudParameter, configuration files, and interfaces so that the code doesn’t have to be rewritten to use a new server. Right now, the server uses PHP and HTTP, but it could be changed to use sockets with an AWS backend and it would not affect any code other than the particular instance of CloudServer. The new server class would then be specified in a resource file.

(Even in the PHP code, I do not hardcode class names. Rather, the request string sent to the server by the java client is used to dynamically load PHP classes to process the data).

Let’s look at each file individually:

File #1: Cloud.java #

/**
 * Cloud Utility Dispatcher Class
 * 
 * @author Ted Yavuzkurt
 *
 */
public class Cloud {

    private CloudServer myServer;
    private ResourceBundle myConfig;

    // PLEASE CHANGE THIS IF YOU MOVE THIS CLASS SOMEWHERE ELSE
    private static final String CONFIGROOT = "voogasalad.util.cloud.config.";

    /**
     * EXAMPLE: Cloud c = new Cloud(); c.retrieveHighScores("GameName");
     */

    public Cloud() {
        ConfigLoader.setPrefix(CONFIGROOT);
        myConfig = ConfigLoader.mainConfig();
        if (myConfig.getString("GroupName").isEmpty()) {
            throw new CloudException("ERROR: Please put your group name in CloudConfig.properties!");
        }
        myServer = (CloudServer) Reflection.createInstance(ConfigLoader.internalConfig().getString("ServerClass"));
    }

    /**
     * Adds a new high score to the cloud.
     * 
     * @param gameName
     * @param playerName
     *            - pass empty string if your game does not use players
     * @param score
     * @throws CloudException
     */
    public void addHighScore(String gameName, String playerName, double score) throws CloudException {
        new CloudScore(gameName, playerName, score).upload(myServer);
    }

    /**
     * Returns a List<GameScore> of PlayerNames to HighScores Call
     * getPlayerName(), getScore(), and getTitle() to get information about the
     * scores.
     * 
     * @param gameName
     *            - name of the game to query
     * @return
     * @throws CloudException
     */
    public List<GameScore> retrieveHighScores(String gameName) throws CloudException {
        CloudLoader<CloudScore> loader = new CloudLoader<>(myServer, CloudScore.class);
        return Collections.unmodifiableList(loader.retrieve());
    }

}

This class is short and easy to use. It does not burden the user with my implementation details. After several revisions and brainstorming sessions (during one of them I pretty much redid the entire utility) I decided that strings were the best way to ask people for data. Thus this class has few methods. Each argument is optional and can be replaced with an empty string if it’s not desired (even the GameName, but I wouldn’t recommend it). I use ResourceBundles to load configuration for all utility classes. The only constant is thus the CONFIGROOT constant in this class, which sets the folder Config files are located in, in case someone wants to move the utility. Instead of directly loading config files, I use the ConfigLoader helper class, which makes it possible for people to move the code around and not change all the references to the properties files (for example, if their group stored their resource files in a particular folder).

In addition, I have written simple, clear documentation so that anyone can use this class without having to dig through my code. This was a big objective - I don’t want anyone to have to look at any of the other files (other than CloudConfig.properties), unless they are curious.

File #2: CloudServer.java #

public interface CloudServer {

    public int initializeID() throws CloudException;

    public void upload(List<CloudParameter> parameters);

    public List<Map<String, String>> retrieve(List<CloudParameter> parameters, List<String> values);

}

This interface is short and to the point. It gives the CloudObjects just enough access to the server to upload their data, and not too much that they need to worry about how the server is implemented. It returns data in string form to be parsed, as described in the “Small Pieces, Loosely Coupled” article. I felt this was the best way to return data. The server takes a simple list of standardized CloudParameters for any of its requests and then handles them in a way which is implementation dependent, and we do not need to worry about.

File #3: CloudParameter.java #

public class CloudParameter {

    private String myName;
    private String myValue;

    public CloudParameter(String name, String value) {
        myName = name;
        myValue = value;
    }

    public CloudParameter(CloudVariable name, CloudVariable value) {
        this(name.getValue(), value.getValue());
    }

    public CloudParameter(CloudVariable name, String value) {
        this(name.getValue(), value);
    }

    public String getName() {
        return myName;
    }

    public String getValue() {
        return myValue;
    }

    public <T> T getRequest(Function<List<String>, T> converter) {
        return converter.apply(Arrays.asList(myName, myValue));
    }
}

This code is also short and to the point. It’s simply a container class for a Key/Value parameter that will be uploaded to a recipient server. It uses generics and functional interfaces in the getRequest() function so that any server can provide a special converter function and receive the data it needs. Thus, data is again implementation independent. If the server implementation changes, all the CloudObjects can still build requests the same way, and the server instance itself will provide a different processing function. This is good design.

File #4: CloudScore.java #

public class CloudScore extends CloudObject implements GameScore {

    private String myPlayerName;
    private Double myScore;
    private String myTitle;

    /**
     * Used when reflecting a CloudScore
     */
    public CloudScore(){
        super();
    }

    /**
     * Used when one needs to duplicate a CloudScore, or create one from server data
     */
    public CloudScore(Map<String, String> dataMap) {
        this(dataMap.get(CloudVariable.GAMENAME.getValue()), 
            dataMap.get(CloudVariable.PLAYERNAME.getValue()),
            Double.parseDouble(dataMap.get(CloudVariable.SCORE.getValue())), 
            dataMap.get(CloudVariable.TITLE.getValue()));
    }

    public CloudScore(String gameName, String playerName, double score) {
        this(gameName, playerName, score, "");
    }

    public CloudScore(String gameName, String playerName, double score, String title) {
        super(gameName);
        myPlayerName = playerName;
        myScore = score;
        myTitle = title;
        setParameter(CloudVariable.GAMENAME.getValue(), getGameName());
        setParameter(CloudVariable.PLAYERNAME.getValue(), myPlayerName);
        setParameter(CloudVariable.SCORE.getValue(), myScore.toString());
    }

    @Override
    public Double getScore() {
        return myScore;
    }

    @Override
    public String getTitle() {
        return myTitle;
    }

    @Override
    public String getPlayerName() {
        return myPlayerName;
    }

    @Override
    public String toString() {
        return String.format(ConfigLoader.internalConfig().getString("ScoreFormat"), myPlayerName, myScore);
    }

    @Override
    public CloudParameter requestString() {
        return new CloudParameter(CloudVariable.ACTION, CloudVariable.HIGHSCORE);
    }

    @Override
    public List<String> getFields() {
        return Arrays.asList(CloudVariable.GAMENAME.getValue(), CloudVariable.PLAYERNAME.getValue(),
                CloudVariable.SCORE.getValue(), CloudVariable.TITLE.getValue());
    }

    @Override
    public CloudScore cloneFromTemplate(Map<String, String> template) {
        return new CloudScore(template);
    }
}

This is the CloudScore class which extends CloudObject (not included in masterpiece for brevity). The main things I want to highlight here:

  • I use an enumerated class which takes its values from a configuration file. Thus, any attempts to access the wrong field will throw a compile time error. Additionally, if I decide to change the implementation of CloudVariable, I can do so, while if I directly pulled strings from a resourcebundle I would be in trouble.
  • I allow the CloudScore to be constructed from a Mapand allow them to be cloned. This will be used extensively by CloudLoader, which creates in this manner.
  • I use superclass methods to set parameters.
  • The toString() method is specified by a configuration file, loaded in by the config utility.
  • The code implements the GameScore interface, which provides restricted access to game developers who only care about the score, etc.

File #5: CloudLoader.java #

public class CloudLoader<T extends CloudObject> {

    private List<CloudParameter> myParameters;
    private List<String> myFields;
    private CloudObject myTemplate;
    private CloudServer myServer;

    public CloudLoader(CloudServer server, Class<T> templateClass) {
        myTemplate = makeTemplate(templateClass);
        myServer = server;
        myParameters = myTemplate.getParameters();
        myFields = myTemplate.getFields();
    }

    public List<T> retrieve() {
        List<Map<String, String>> rawData = myServer.retrieve(myParameters, myFields);
        return parse(rawData);
    }

    @SuppressWarnings("unchecked")
    private List<T> parse(List<Map<String, String>> rawData) {
        return (List<T>) rawData.stream().map((m) -> myTemplate.cloneFromTemplate(m)).collect(Collectors.toList());
    }

    private T makeTemplate(Class<T> clazz) {
        T result = null;
        try {
            result = clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;
    }
}

This file allows any type of CloudObject to be retrieved and parsed in a generic manner by a CloudServer. It uses streams to parse the data, and uses reflection to instantiate an instance of a CloudObject to use as a template. It is parametrized, and thus works with any type of CloudObject.

Design Challenges #

There are a few major design challenges I contributed to.

Initially, games saved by the GAE used full paths to store images. Images were stored in the same folder for the GAE as they were for the Game Player. This was a problem. This created a major dependency between the GAE and a created game. I wanted to break this dependency, without affecting the code of the GAE as much as possible.

What Made it Challenging #

At first, I wanted the GAE to just copy images to a folder as they were used. Then I realized, this really wasn’t the job of the Game Authoring Environment, so I set out to create a utility that would transparently manage the resources for games.

This was challenging, because it had to be able to support renaming games (and thus renaming all the directories associated with a game), because it had to know which resources to copy to a game, and because it had to run with minimal interruption to the Game Authoring Environment. Plus, it had to be stable.

Additionally, I wanted this system to be able to manage resources without requiring their names to be manually configured. Thus I had to be able to specify in a resource file what directories resources were located in, to be able to provide a listing of subdirectories to the GAE, and to be able to list directory contents depending on the type of resource requested. This had to be done in a manner that conformed to the Single Responsibility Principle - thus the classes responsible for listing files could not be the classes responsible for knowing what types of files were desired, which could not be the classes responsible for creating respective JavaFX instances.

This took some time to develop and led to the creation of the following classes: GameResourceManager, GameDataManager (a significant portion of it), GameFileHelper, and SpriteDatabase (ImageDatabase and others already existed, but I retrofitted them to be usable by the GAE as well).

Why It Is Designed the Way It Is #

I designed the GameResourceManager to be initialized either with a GameInfo class or a string of a GameName. Using this, it would then construct a directory hierachy (specified in a resource file) if one did not exist. It could then copy resources to the game as requested.

Originally, I designed it to transparently copy images as soon as they were requested by the GAE, if this option was toggled. The problem with this is that the GAE would “request” all the images to display them, without them necessarily being used in a game. This meant that all images were being copied to every game - an extremely wasteful operation. Working together with Mike, I decided a compromise would be one function call: copyImageToGame(String URI). When called, this would copy an image. All he had to do was call it when the user dragged an image to the map.

I debated whether to just call this CopyResourceToGame or to give it a more specific name like CopyImageToGame. This also went for code like RequestImage, requestSprite, etc. I decided to give the functions specific names to make it SIMPLER TO USE rather than parametrizing them to reduce the number of functions. This was a design tradeoff. Internally, I actually do use generic functions. Thus a function like this:

public Image getImage(String URI) {
        return getResource(URI, myImageDatabase, myConfig.getString("ImagePath"));
    }

Actually uses this function internally:

private <T> T getResource(String URI, ResourceDatabase<?> database, String path) {
        T result = null;
        try {
            result = (T) database.request(myRootDirectory + path + URI);
        } catch (IllegalArgumentException e) {
            try {
                result = (T) database.request(myConfig.getString("DefaultRoot") + path + URI);
            } catch (IllegalArgumentException e2) {
                throw e2;
            }
        }
        return result;
    }

This idea came from the discussion I had with Prof. Duvall about the Cloud Utility, where we discussed using a class which provides an easy to use interface for a more complicated internal operation. Reading requestImage(), requestSprite(), or later requestSound() in code makes it more readable, like reading English, and allows me to use the more generic parametrized form internally.

Additionally, using the Single Responsibility Principle, I used highly generic file browsing functions, with more specific functions designed to use them. Thus as one goes up the chain, the function calls get more and more specific. For example, getImages() in ResourceManager follows this path:

@Override
    public List<String> getImages() {
        return filterURIs(
                myData.getResources(getExtensions(myConfig.getString("ImageExt")), myConfig.getString("ImagePath")),
                myConfig.getString("ImagePath"));
    }

which calls the following function in GameDataManager

public List<String> getResources(List<String> extensions, String path) {
        List<String> result = myFileHelper.getMatchingFiles(myBasePath + path, extensions);
        return result.stream().map(s -> s.substring(myBasePath.length())).collect(Collectors.toList());
    }

which calls the following function in GameFileHelper

public List<String> getMatchingFiles(String path, List<String> extensions) {
        ArrayList<String> files = new ArrayList<>();
        listf(path, files, extensions);
        return files;
    }

which calls the following recursive function based on one I found online:

private void listf(String directoryName, List<String> files, List<String> extensions) {
        File directory = new File(directoryName);
        File[] fList = directory.listFiles();
        if (fList.length > 0) {
            for (File file : fList) {
                if (file.isFile()) {
                    if (hasExtension(file, extensions)) {
                        files.add(directoryName + file.getName());
                    }
                } else if (file.isDirectory()) {
                    listf(directoryName + file.getName() + "/", files, extensions);
                }
            }
        }
    }

(Changed method signature to private in analysis, don’t know why I had it public before as no external classes directly call listf).

Assumptions or Dependencies that Impact Overall Design #

The big assumption I made designing this is there is no way to add resources to the default libraries from within the GAE. Thus the user is forced to choose from the default images displayed. Although there are many, this is an oversight. A user can add images to the library merely by copying them into the default library folder. However, if I’d had more time, I would’ve developed a way to add ones own images to the library without having to use the OS filesystem.

Beyond that there are no dependencies. Both the GamePlayer and GAE use different interfaces to access the ResourceManager so there is no issue of accessing the wrong functions. GamePlayer cannot copy files to its library or modify its name.

The whole goal was to make a ResourceManager which was extensible and easy to use. By relying on generics and parametrized interfaces, I could easily create a SoundDatabase class and integrate it with the ResourceManager class. I would add methods for getSound etc to this class - although this violates Open/Closed, it is still within the broader domain of SRP, and makes things easier for the user. I value making things easy on the user more than I value never ever changing code - thus I’ll add a method to make it clear what the method is doing.

Design Review #

Code Consistency #

The code is not generally consistent in naming conventions and descriptiveness. It’s divided into two broad categories, which I shall call:

  • J-Style: abbreviations such as “A” for Abstract, “I” for Interface, etc. Much of the GAE and Game Engine is written like this.

  • E-Style: very verbose names such as GamePlayerToGameEngineInterface. Much of the Game Player and Game Engine is written in this style.

Both styles have drawbacks. E-style tends to be overly long. J-style tends to be confusing (what is AMapObject, etc? A also reads like an English Article and makes reading names confusing).

Within portions, the code is very consistent, however it is usually easy to figure out who wrote a given portion. For example, the chat portion of the Game Player looks much like John’s code, while the other areas look much like mine. He prefers to instantiate pieces of code in the constructor to make the flow easy to follow, while I tend to divide everything into methods to keep method length in check. Both have advantages and disadvantages.

Some parts of the code use constants, some use ResourceBundles. it depends which part of the code one is reading.

It would’ve been helpful at the beginning to decide what style to use:

  • naming conventions,
  • this vs my,
  • interface names,
  • etc.

It would’ve also been good to decide on storage locations for configuration/resources. We both developed our own independent resource management systems. It would’ve been good to have one configuration loader for the whole project to cut down on duplication and allow better collaboration.

One major difference was the construction of the GAE vs the Game Player. The GAE primarily uses inheritance, with many of its components extending JavaFX classes. By comparison, the Player extends few, if any, JavaFX classes (I don’t think it was any), instead wrapping them and adding code around them. Personally, I prefer my technique, as I feel its more flexible and doesn’t leave me at the mercy of JavaFX.

Consistency really reared its ugly head when trying to integrate the GameEngine with the GAE. The GAE team had gone ahead and actually created their own special Model package, containing all the code to house the game in progress. Seeing as we decided early on that our GameEngine was to be our model, it would’ve made more sense to just configure components of the engine dynamically. This could’ve been done by making some components cloneable and editing them over time. Instead, we were left with two representations of a Game Engine, dramatically increasing complexity.

Dependencies #

Dependencies are usually very explicit, passed into constructors. Many of the classes make use of resourcebundles, however often these config files are passed in (on the Game Player at least). Sometimes there were order of method call dependencies out of necessity. For example, a GameScene instance could not operate until load() had been called. This makes sense, however, as a GameScene shouldn’t do anything until it is loaded into the scene (it is fully formed in the constructor, but doesn’t beginning running until load() is called). On the GamePlayer side, much was dependent upon the design of the controller. I tried to minimize this by limiting the number of places where it was necessary to call getController().getEngine(). This led to a larger controller, but made it easier to modify behavior without editing code in multiple places. This was often a design tradeoff: adding more methods to a class versus having dependencies elsewhere. This came up in the creation of the ResourceManager class as well (in addition to the concern that I didn’t want to parametrize method calls: I preferred to call getImage() rather than < Image>getResource() for clarity).

Difficulty of Extension #

EASY TO EXTEND #

The GamePlayer is very easy to extend. Everything is modular, and adding new scenes to the flow of the game is as simple as creating a new instance of GameScene and adding a line for it to the PrimarySceneManager.properties file. Then a properties file for the GameScene itself should be created, so that its configuration can be stored without recompiling. This is how I would go about adding new scenes to the game, such as for a level end. Finally, one needs to go and make sure that other scenes properly link to it as you desire (usually, this can be accomplished simply by editing their resource files). Adding a new element to the display of the GamePlayer is also easy. Any WindowComponent has access to the GameController and current GameScene, thus giving it all kinds of data that it needs to function. To make a WindowComponent, one simply extends the class, adds in necessary javaFX code, and calls setParent() on the root node for the component. Boom.

HARD TO EXTEND #

The Game Engine is very hard to extend. The reason for this is there are so many dependencies on it. Changing code in the Game Engine affects the GAE, the factories for the Game Engine, as well as potentially the Game Player if the modification is large enough. This is why modifying the Game Engine to support multiple levels and later Groovy ended up bogging down the rest of the project.

Alternate Designs and Architectures #

How Did Original Design Handle Extensions? #

For the most part, our original design handled extensions pretty well, aside from two glaring areas:

  1. Multiple levels

  2. Groovy code

This was a screwup on our part. We knew both of these extensions was coming and shoved them under the rug to deal with them later. Our design depended on saving GameEngines whole in order to load/save easily. Thus, these two significant modifications to the game engine disrupted code all the way from the GAE to the GamePlayer and the GameData system. It may have taken more time to design the Game Engine to accommodate them from the start, but it would have paid off in less headache down the road.

How Did Original APIs Change? #

Original API’s changed considerably as the GameEngine went through various design stages, primarily due to the issues discussed above. For example, at first we would request the target for an action from a unit. Then we’d request it from the Game Engine. Finally, we moved it back to the unit. This is one of the reasons I was thankful for the mostly modular design of the Game Player - changes were mostly restricted to the Controller or at worst some of the WindowComponents that interacted directly with Units.

Original APIs for the GamePlayer and such also changed fairly extensively, as I just generated class and interface names to fit into broad packages to fit the layout of how I thought I would organize the Game Player. These changed as I learned more about JavaFX, and decided to write code for a Scene Manager and other such modifications.

One thing that led to API changes was the process of developing API’s for the Game Player. Efe asked me to come up with functions I would need. I would specify these. Then the Engine team would decide later that there was a better place to put a given function that made it easier for their implementation. This bothered me, as it reminded me of the “Why Software Sucks” article - just because their implementation makes it easier to put a function in a non-sensical place doesn’t mean I (the end-user) should have to design the Game Player around it. For example, it made sense to have a MapObject provide information on target coordinates for its actions. However, it was easier for the engine team to implement this as a call to the GameEngine, passing the MapObject in along with the String for the action. This required casting (on the engine side) a PlayerMapObjectInterface back to a MapObject. I modified my code to support this, then it was decided later to change to the more sensible design again.

Not blaming the engine team - I understand the major design challenges they faced. But that’s the reason many API’s changed - our design was so generic and so advanced that it was very easy to talk about implementing it “generally” and very hard to go about it in practice.

The API for my Cloud Utility changed extensively too, as I will discuss in the Masterpiece section. I went through several revisions, where I was trying to make it both user friendly and powerful. At first, I was gong to give people direct access to CloudScore, CloudSave, etc objects which they would then configure and upload to the cloud. I figured that, while this would reduce the number of methods I would offer directly in the main API class, that it would burden them to learn more about my implementation. Ultimately, I decided not to make them interact with the classes like this, just using strings to give the name of a game and the name of a player. This was inspired by the “Small Pieces, Loosely Coupled” article about UNIX - text is standard, it’s easy, and it’s not burdensome on the end-user.

Design Decision Discussed in Detail: How To Save Games #

We discussed how to save games extensively with our TA (Lee Weisberger) and my old TA (Siva).

Alternate Designs Proposed #

The big question was whether we should save an instance of our Game Engine or save only the data specific to the types of map objects, rules, and their locations etc.

Trade-Offs of Design Choice (Pros/Cons) #
Advantages of Current Design #
  • Easy. Does not require building a level on the fly - they come prebuilt from XStream.

  • Our game genre is so tightly coupled with the rules of the game that it makes sense to store engine together.

  • Allows us to focus more on other features of the game.

  • Makes saving and loading game state easier.

Disadvantages of Current Design #
  • Big issue: when a level ends, there’s no way to reset it to the starting state without reloading the game.

  • Makes networking inefficient: entire game engine is packaged and sent (this was also to avoid using RMI instead of sockets, which was more complicated)

  • Results in larger game files which could cause overflows - every instance of a MapObject is stored uniquely, as opposed to just storing the definition of an object and where it will be located

  • Makes it impossible to load level data independently - (well, very hard). I wanted to load levels only when they were clicked, but they must all be loaded at once since the Game Engine holds all the levels and interacts with them. I can’t just load them in dynamically (at least, not in a simple manner).

  • Blurs the line between logic and data. Realistically, it’d be smart to have a lightweight “game engine” class that controls the “game data” classes.

Advantages of Alternative Design #
  • It would make more sense. Thus the Game Player would load a Game Engine, and then load Game Data files that the engine would read and give information to the player.

  • Smaller save files. This could make it possible to create bigger games with less overhead.

  • Much more efficient. Map Objects would not need to be stored individually until they were modified. Objects would use templates.

  • Better conformation to Single Responsibility Principle. The Game Engine would be responsible for running the game while data would be responsible for storing it.

Disadvantages of Alternative Design #
  • Much harder to implement. The hell we had making factories for the current Game Engine would only be compounded if we had to make the Game Engine load its data dynamically. The Game Engine would have to be redesigned to better suit this type of modular implementation.

  • Doesn’t leverage XStream. The advantage of XStream is that we can easily save a large class and load it back in. Why not use it?

My Preference #

If we had more time (and I was working on engine): modular, alternative design.
Given the amount of time we had: the current design.

Two Best Features in Current Design #

  1. User can literally create a game in with any rules, outcomes, objects, parameters, etc - unlike some groups which had huge lists of predefined actions, we give the user full freedom to create whatever they want. This makes out engine the most flexible of those displayed.

  2. Games are packaged individually and are independent of GAE. A game file created by our GAE can be zipped up and sent to anyone who has the GamePlayer and GameData installed. Thus, if this were an enterprise project, people would not have to have the SDK or Game Development Environment on their computers to play a game.

Two Biggest Issues Remaining in Current Design #

  1. Game Engine is Excessively Complex with too few “defaults.” While the flexibility is nice, it is important to make our product user friendly. Currently, one has to be familiar with our implementation to develop an extensive game. If we had more time, it would behoove us to make more “default” objects, parameters, outcomes, etc to give people a starting point.

  2. There are many dependencies between Game Engine objects which are not explicit. For example, an AttackCharacteristic requires a HealthCharacteristic, isRemoved() requires a HealthCharacteristic, etc. We should develop a way to add all dependencies along with a given characteristic. The user should not be burdened with the details of our implementation.

If you made it this far, and you aren’t Professor Duvall, I applaud you.

Written with StackEdit.