Project: The Watson Game



Project: The Watson Game

Function: Server

Subsystem: Scoring and Threads

Author: Justin Phillips

Date: 12/2/04

1 Introduction 4

1.1 Goals and objectives 4

1.1.1 The Watson Adventure Game 4

1.1.2 Scoring Module 4

1.1.3 Thread Support 4

1.2 Statement of scope 4

1.2.1 The Watson Adventure Game and Scoring 4

1.2.2 Thread Support 5

1.3 Software context 5

1.4 Major constraints 5

2 Data design 5

2.1 Internal software data structure 5

2.1.1 Scoring 5

2.1.2 Threads 6

2.2 Global data structure 6

2.3 Temporary data structure 6

2.4 Database description 6

3 Architectural and component-level design 7

3.1 Program Structure 7

3.1.1 Architecture diagram 7

3.1.2 Alternatives 10

3.2 Description for Scorer Component 10

3.2.1 Processing narrative (PSPEC) for scorer component 11

3.2.2 Scorer Component interface description 11

3.2.3 Scorer Component processing detail 12

3.3 Description for WGPlayer Component 13

3.3.1 Processing narrative (PSPEC) for thread component 13

3.3.2 Thread component interface description. 14

3.3.3 Thread component processing detail 15

3.4 Description for WGListener Component 17

3.4.1 Processing narrative (PSPEC) for WGListener component 17

3.4.2 WGListener component interface description. 17

3.4.3 WGListener component processing detail 17

3.5 Software Interface Description 18

3.5.1 External machine interfaces 18

3.5.2 External system interfaces 18

3.5.3 Human interface 18

4 User interface design 19

4.1 Description of the user interface 19

4.1.1 Screen images 19

4.1.2 Objects and actions 19

4.2 Interface design rules 19

4.3 Components available 19

4.4 UIDS description 19

5 Restrictions, limitations, and constraints 19

6 Testing Issues 19

6.1 Classes of tests 19

6.2 Expected software response 20

6.2.1 Expected Scoring Module Response 20

6.2.2 Expected Thread Module Response 21

6.3 Performance bounds 21

6.4 Identification of critical components 21

7 Appendices 21

7.1 Requirements traceability matrix 21

7.1.1 Scoring Module Traceability Matrix 21

7.1.2 Server Threads Traceability Matrix 22

7.2 Packaging and installation issues 22

7.3 Design metrics to be used 22

7.4 Supplementary information (as required) 22

7.4.1 Scoring Requirements 22

7.4.2 Thread Requirements 23

Introduction

1 Goals and objectives

1 The Watson Adventure Game

The “Watson Adventure Game” (WAG) is intended to be a first person role playing game in which the user is able to navigate the corridors of the third floor of the engineering building and complete challenges as they progress through the game. Development of the WAG will continue into the future and the game may expand to include the entire Engineering building and later the entire campus. This game will use a client/server model enabling it to be played online with other players when an internet connection is present. The game may also be played locally using a Microsoft Access database.

2 Scoring Module

The Scoring module is responsible for measuring a player’s progress through the game and determining if a player has completed the game. Several criteria have been identified and implemented in order to measure this progress in a way that is meaningful to the user. Once the player has progressed to a certain point in the game, based on the scoring criteria, the player has won the game. The scoring functionality should be flexible to allow expansion of current game play.

3 Thread Support

The server allows an infinite number of client connections, each running on its own thread. This is a serious design flaw because as the number of client connections increases, performance degradation becomes more noticeable. A goal for the server thread implementation this semester will be to limit the number of simultaneous active connections. Other goals include providing support to other developers that will be working with the server threads.

2 Statement of scope

1 The Watson Adventure Game and Scoring

A player will be able to navigate a three dimensional representation of the third floor of the Watson Engineering Building. As the player moves through this virtual space they will bump into hotspots, which will present challenges. Challenges will be divided into two categories; academic challenges, and student life challenges. Upon completion of an academic challenge the player will receive a score that will be factored into their grade point average (GPA) and if that score is passing they will receive credits. Challenges may be retaken in order to improve the players overall GPA but credits and experience points will be awarded only once. If the challenge is a student life challenge the player will be awarded experience points only. The GPA, experience points, and credits are the three criteria upon which game progression is measured. The player may check current progress by looking at the DARS report, the knapsack, and or the game statistics. The objective of the game is to accumulate a minimum number of credits, a minimum level of experience and a minimum GPA. Once this has been accomplished the player will be notified that they have won the game.

2 Thread Support

The number of threads that the server will spawn will be limited. The number of active connections that will be allowed at one time may be set as a parameter upon starting the server. Once this number has been reached any additional requests by a client to start a connection will be rejected and an error message will be sent back to that client stating that the server is full.

3 Software context

This softwares intended use is as a recruiting tool for Binghamton University to recruit high school seniors into the computer science and engineering Bachelor in Science programs. This software is also going to be demonstrated before a board of directors at Binghamton University in attempt to gain funding for further development of the game. With this in mind it is important to develop a scoring scheme that will make game-play enticing to the age group that this game is targeting. Role playing games are popular and the criteria that is defined to measure game progress was chosen because it mirrored the actual process of earning a degree at the university and it will provide the player with clear rewards for meeting challenges making game- play exciting. Because it is uncertain which hardware the server will be running on in the future and the number of users that it may one day have to accommodate, the number of simultaneous connections allowed should be easily defined by the server administrator.

4 Major constraints

Development of the WAG will be difficult due to the fact that it will span multiple semesters where each semester a new and quite large group of people will act as developers. Each semester, tasks are assigned on an individual basis and unit testing shall be done for each task. Integration and high level testing is done by a project leader.

Data design

1 Internal software data structure

1 Scoring

Arrays will be used to keep track of various data related to a players score on individual challenges, experience points earned for student life challenges, the players overall GPA, and if a challenge has been passed or not. These arrays will be declared in the WGPlayer class and can be accessed indirectly through various public methods. A Boolean value will indicate whether a player has completed the game or not.

2 Threads

A counter will be used to keep track of the number of active connections. This counter will be decremented by the Logout method when a connection is closed and will be incremented when a new connection is established.

2 Global data structure

N/A

3 Temporary data structure

N/A

4 Database description

N/A

Architectural and component-level design

1 Program Structure

1 Architecture diagram

In the following diagrams, TCP/IP connections are represented using red lines. An asterisks next to a class names indicates multiplicity.

1 Watson Adventure Game Architecture Diagram

2 Scoring Module Architecture Diagram

3 Thread Architecture Diagram

2 Alternatives

1 Scoring Module

One alternative to having a single scoring object that is used by all client connections is to have one scoring object for each connection. A benefit to this solution would be that it would allow for the scoring module to determine different winning criteria for each player. For example, if you want the number of experience points to win the game (expToWin field) to be different for each player you would merely have to initialize each scorer object with the desired value. This could be one way to have varying levels of difficulty. Currently, all players will have the same criteria for completing the game.

Because the scoring module is used by multiple threads there is a need for synchronization. This is easy to do in Java but it would not be necessary if there were a separate scorer object for each client connection.

Despite the benefits of having a scorer object for each client connection, there are certain drawbacks to this solution. One is that, with a large number of client connections, the number of scoring objects required would consume large amounts of memory. It seems wasteful to use so much memory on code that is run relatively infrequently when compared to other portions of the server code. The most significant reason for using a common scorer module as opposed to a separate instance for each connection was that this was the design chosen in the spring of 2004 and currently we are not implementing varying levels of difficulty so the common scoring module is the simplest solution that still suits our needs.

2 Server Threads

Instead of using the number of current connections and the number of maximum allowed connections as the criteria for deciding whether or not to accept a request for an incoming connection one might use the current overall load on the server as the criteria. More specifically, look at how much memory is being used, how much of the CPU time is being used, and how much bandwidth is being used. The problem with this is that, because the Watson Adventure Game may not be the only software running on the server, we cannot be sure that the current load will remain constant. If we keep allowing connections and then another application begins to use up resources then we run the risk of performance degradation in our server due to lack of resources. This is why a conservative limit on the number of simultaneous connections allowed should be determined upon startup of the server.

2 Description for Scorer Component

A detailed description of each software component contained within the architecture is presented. Section 3.2 is repeated for each of n components.

1 Processing narrative (PSPEC) for scorer component

Upon instantiation of the scorer class, the criteria for winning the game is set. This criteria may be passed in through the parameters of the constructor or it may take on the default values.

The updateScore method is called by the WGConnection class and is passed the WGCommand and the players name as a String object. The command is parsed and the correct WGPlayer object is gotten using the players name. An overloaded updateScore method is then called and is passed the WGPlayer object, a challenge ID, the score as type double, and a Boolean saying if the challenge is student life or academic.

If the challenge is academic then the score is checked to make sure its between 0.0 and 4.0 and the challenge ID is checked to make sure its valid. The scorer then checks to see if the player should receive credit for the challenge and if so, the credit is awarded. Then the score is averaged into the players GPA.

If the challenge was student life then the score is checked to make sure its higher than the old score for that challenge and if it is, then it replaces the old challenge score and the total experience points are updated.

After correctly updating the score, whether it be student life or academic, the players GPA, experience points, and credits are checked to determine if they meet the criteria for winning the game. A method is called in WGPlayer letting the player know they won the game if and only if the criteria for winning the game has been met and the player hasn’t previously won the game.

2 Scorer Component interface description

The constructor for the Scorer class is overloaded. One constructor takes no parameters and sets the fields used to determine when a player wins the game to the default values. The other constructor takes two ints and a double as parameters. The two ints represent the total experience points needed to win and the total credits needed to win while the double represents the minimum GPA to win. These values are used to initialize the respective fields in the Scorer class.

The updateScore method is overloaded. One updateScore method takes an instance of a WGCommand and a string representing the players name as parameters and returns a void type. The WGCommand contains information on the score, the recently completed challenge ID, and the Boolean denoting whether it is a student life challenge or academic. The players name is used to get a reference to the WGPlayer object for that player. Once the WGCommand is parsed and the WGPlayer object is obtained, this data is used as parameters to the other updateScore method.

The other updateScore method takes a WGPlayer, a score, a challenge ID, and a Boolean telling whether the challenge is academic or student life. This method returns a type void and uses the information to update the WGPlayer’s score.

3 Scorer Component processing detail

1 Interface description

See section 3.2.2

2 Algorithmic model (e.g., PDL)

updateScore(WGCommand, PlayerName){

cmdVector = WGCommand.getAllArgs()

chalID = cmdVector[1]

score = cmdVector[2]

academicBool = cmdVector[3]

WGPlayer = watsonGameServer.getPlayer(PlayerName)

updateScore(WGPlayer, chalID, score, academicBool)

}

updateScore(WGPlayer, chalID, score, academic){

if(academic){

if(chalID >= 0 & chalID = 0.0 & score = 0){

WGPlayer.updadeSLCScore(score)

}

}

if(WGPlayer.hasWon()){

WGPlayer.setWon()

Print( “Player has Won”)

}

}

3 Restrictions/limitations

There were no restrictions or limitations other than those imposed through the design of the WAG server. For example, if you need to get the WGPlayer you must call the accessor method in WatsonGameServer.

4 Local data structures

The fields that hold the winning criteria as well as the fields holding the number of student life challenges are all local data structures. The reference to Watson game server is local but the object itself is accessible to a number of modules. The reference to WGCommand is a local reference but the WGCommand object is accessible from classes other than the Scorer class.

5 Performance issues

There are no performance issues or constraints on the Scorer class.

6 Design constraints

The only design constraint is that there is a single Scorer object that is to be used by all client threads. This can cause problems when writing to the WGPlayer object if the Scorer class is not implemented carefully. When the updateScore method is called and is passed the reference to WGPlayer, there may be another thread executing the code that uses the reference to WGPlayer to update the score in the WGPlayer object. If the reference is changed by one thread then the other thread may be write data to the incorrect instance of WGPlayer. This problem can be solved by using the synchronize keyword in java.

3 Description for WGPlayer Component

1 Processing narrative (PSPEC) for thread component

WGPlayer has a number of accessor and mutator methods that perform operations on the fields in WGPlayer that keep track of information such as that players current cumulative GPA, total experience points, experience points earned for each challenge, total credits, challenges that have been passed, total challenge attempts, and whether they have won the game or not. Each method will be examined and its workings explained.

1 The addACScore method

This method takes a score on a 4.0 scale and a challenge id as parameters. The new score is averaged into the current cumulative GPA.

2 The addSLCScore method

This method takes a challenge id and a score representing the points earned. The points earned for the challenge are recorded if they are higher than the previous score and any new points gained are added to cumulative experience points.

3 The addCredits method

This method increments the total number of credits by the number passed in as a parameter.

4 The setWon method

This method sets a Boolean to true that means the player has won the game.

5 The setPassed method

This method marks the challenge represented by ‘id’ as passed.

6 The getSLCPoints method

This method returns the points earned for the challenge indicated by ‘id’.

7 The getGPA method

This method returns the GPA of the player.

8 The getCredits method

This method returns the total credits the player has accumulated.

9 The getExperience method

This method returns the total experience points the player has accumulated.

10 The hasPassed method

This method returns true if the player has passed the challenge.

11 The won method

This method returns the Boolean indicating whether the player has won the game or not.

2 Thread component interface description.

1 The addACScore method

This method takes a score on a 4.0 scale and a challenge id as parameters and returns nothing.

2 The addSLCScore method

This method takes a challenge id and a score representing the points earned as parameters and returns nothing.

3 The addCredits method

This method takes the number of credits earned as a parameter and returns nothing.

4 The setWon method

This method has no parameters or return types.

5 The setPassed method

This method takes a challenge id as a parameter and has no return type.

6 The getSLCPoints method

This method takes a challenge id as a parameter and returns an int.

7 The getGPA method

This method has no parameters and returns a double.

8 The getCredits method

This method has no parameters and returns an int.

9 The getExperience method

This method has no parameters and returns an int.

10 The hasPassed method

This method takes a challenge id as a parameter and returns a boolean

11 The won method

This method returns the Boolean and has no parameters.

3 Thread component processing detail

1 Interface description

See section 3.3.2

2 Algorithmic model (e.g., PDL)

public void addACScore(int id, double score){

gpa = (gpa*totalChallengeAttempts + score)/(totalChallengeAttempts + 1)

++totalChallengeAttempts

}

public void addSLCScore(int id, double points){

totExp += (points - slcScores[id]);

slcScores[id] = points;

}

public void addCredits(int credits){

this.credits += credits}

public void setWon(){

hasWon = true;

}

public void setPassed(int id){

passedAC[id] = true;

}

public double getSLCPoints(int id){

return slcScores[id];

}

getGPA(){

return gpa

}

public int getCredits(){

return credits;

}

public double getExperience(){

return totExp;

}

public boolean hasPassed(int id){

return passedAC[id];

}

public boolean won(){

return hasWon;

}

3 Restrictions/limitations

This way of implementing the score will not work across sessions. To do that, you must save the data that is used in scoring in the database when a player logs out and retrieve these values when a player logs in. However, this is not part of scoring, this is part of the login and logout procedure.

4 Local data structures

The variables passedAC and slcScores are both arrays. passedAC is an array of booleans and slcScores is of type double. The variables numAC, numSLC, totalChalengeAttempts, and credits are all private ints. The variables gpa and totExp are private doubles and hasWon is a private boolean.

5 Performance issues

There are no performance issues related to this component.

6 Design constraints

There are no design constraints related to this component.

4 Description for WGListener Component

1 Processing narrative (PSPEC) for WGListener component

The WGListener has its port and max connections initialized in the constructor when it is instantiated. When the new thread is started on the WGListener, the run method is envoked. The WGListener waits for a message on the designated port and when a connection request comes along a check is done to see if the total current connections, not including the incoming connection, is less than the maximum allowable connections. If this is true then the incoming connection is accepted, the number of connections is incremented, and a new thread running on a new instance of the WGConnection object is started.

2 WGListener component interface description.

The constructor takes two parameters, the port and the max connections. The incConnection and decrementConnection methods are used to increment and decrement the number of active connections counter. These are used when a player logs in and logs out respectively and have no return type and take no parameters.

3 WGListener component processing detail

1 Interface description

See section 3.4.2.

2 Algorithmic model (e.g., PDL)

WGListener{

Run(){

While(true){

sock = accept()

If(numConnections < maxConnection){

incConnection();

WGConnection wgConn = new WGConnection(wgs, sock);

new Thread(wgConn).start();

}else{

sock.sendErrorMessage()

}

}

}

private void incConnection(){

numConnections++;

}

public void decrementConnection(){

numConnections--;

}

}

3 Restrictions/limitations

There are no restrictions or limitations in designing how to handle the connections.

4 Local data structures

servSock – Of type ServerSocket, a reference to the server socket

wgs – Of type WatsonGameServer, A reference to the watson game server

numConnecitons – Of type int, the number of clients currently connected

maxConnections – Of type int, the maximum number of simultaneous connections

5 Performance issues

There are no performance issues or constraints in the design of this code.

6 Design constraints

There are no design constraints.

5 Software Interface Description

1 External machine interfaces

There are no external machine interfaces for the scoring or thread modules.

2 External system interfaces

The Thread system uses a WGCommand as its interface to the client across the network. Refer to the WGCommand specification for more information about the message format and the types of available messages. The Login message is the message of most significance with respect to threads. This message is the one sent to the server for a new connection request.

3 Human interface

The only human interface is the optional parameter list that is given when the server is started. The number of allowed connections should be specified only after the port. That is, you can have either zero arguments and the port and max connections will initialize to their defaults or you can specify the port alone with the max connections taking on a default value, or you can specify the port and the max connections. The max connections must be an integer and must be the second parameter.

User interface design

1 Description of the user interface

There is no user interface for the Scoring and Thread modules that the user will come into direct contact with.

1 Screen images

N/A

2 Objects and actions

N/A

2 Interface design rules

N/A

3 Components available

N/A

4 UIDS description

N/A

Restrictions, limitations, and constraints

There are no other restrictions, limitations, or constraints that influence the design of the thread and scoring modules other than those mentioned in sections 1.4, 3.3.3.3, and 3.2.3.3.

Testing Issues

1 Classes of tests

I will be performing black box testing to validate all requirements of the scoring and thread modules. There will be no white box or grey box testing although I have access to all source code.

2 Expected software response

1 Expected Scoring Module Response

|Requirement |Test Case |Response |

|Test 1 for Scoring 1 |updateScore(player, 1, 3.0, true), updateScore(player, |Print “Player has Won!” |

| |1, 10000, false), where creditsToWin = 4, expToWin = | |

| |100, gpaToWin = 2.0 | |

|Test 2 for |updateScore(player, 1, 3.0, true), updateScore(player, |Should show the gpa = 3.0 and number of attempts = 2 for |

|Scoring 2 |1, 3.0, true) |academic challenge 1 |

|Test 3 for |updateScore(player, 1, 9.0, false), updateScore(player, |Should show the total experience points at 10 and the |

|Scoring 2 |1, 10.0, false) |experience points for that challenge at 10 |

|Test 4 for |see test cases 2 and 3 |see response 2 and 3 |

|Scoring 3 | | |

|Test 5 for |see test cases 2 and 3 |see response 2 and 3 |

|Scoring 4 | | |

|Test 6 for |updateScore(player, 1, 3.0, true), updateScore(player, |gpa should be 2.75 and cumulative credits should be 8 |

|Scoring 5 |1, 2.0, true), updateScore(player, 2, 3.0, true | |

|Test 7 for |updateScore(player, 1, 10.0, false), updateScore(player,|should have a total of 10 experience points and 10 |

|Scoring 6 |1, 9.0, false) |experience points for challenge 1 |

|Test 8 for |see test case 6 |see response 6 |

|Scoring 7 | | |

|Test 9 for |see test case 3 |see response 3 |

|Scoring 8 | | |

|Test 10 for |see test case 6 |see response 6 |

|Scoring 9 | | |

|Test 11 for |see test case 1 |see response 1 |

|Scoring 10 | | |

|Test 12 for |updateScore(player, 1, 3.0, true), updateScore(player, |Print “Player has Won!” only once |

|Scoring 11 |1, 10000, false), updateScore(player, 1, 3.0, true). | |

| |where creditsToWin = 4, expToWin = 100, gpaToWin = 2.0 | |

|Test 13 for |see test case 6 |see response 6 |

|Scoring 12 | | |

|Test 14 for |updateScore(player, 1, |should have the experience for challenge 1 at 0 |

|Scoring 13 |-10000, false) | |

|Test 15 for |updateScore(player, 1, -2.0, true) |should have the players gpa at 0 |

|Scoring 14 | | |

|Test 16 for |updateScore(player, 1, 4.1, true), |should have the players gpa at 0 |

|Scoring 15 | | |

2 Expected Thread Module Response

|Requirement |Test Case |Response |

|Test 1 for |set maxConnections to 0, try to connect |receive error from server |

|threads 1 | | |

|Test 2 for |set maxConnections to 1, try to connect two times |first connection allowed second |

|threads 2 | |connection denied |

|Test 3 for |call decrementConnections when the current connection count is 0 |connection count should still be 0. |

|threads 3 | | |

3 Performance bounds

There are no special performance requirements.

4 Identification of critical components

Because testing will be done using black box approach it will be impossible to test components individually.

Appendices

1 Requirements traceability matrix

1 Scoring Module Traceability Matrix

|Requirement |Component or Data Structure |

|1 |Scorer::updateScore, WGPlayer::hasWon, WGPlayer::getCredits, WGPlayer::getExperience, WGPlayer::getGPA |

|2 |Scorer::updateScore |

|3 |Scorer::updateScore, WGPlayer |

|4 |Scorer::updateScore |

|5 |Scorer::updateScore |

|6 |Scorer::updateScore |

|7 |Scorer::updateScore, WGPlayer::addACScore |

|8 |Scorer::updateScore, WGPlayer::addSLCScore |

|9 |Scorer::updateScore, WGPlayer::addCredits |

|10 |Scorer::updateScore, WGPlayer::setWon, WGPlayer::won |

|11 |Scorer::updateScore |

|12 |Scorer::updateScore, WGPlayer::addACScore |

|13 |Scorer::updateScore, WGPlayer::addSLCScore |

|14 |Scorer::updateScore, WGPlayer::addCredits |

|15 |Scorer::updateScore |

2 Server Threads Traceability Matrix

|Requirement |Component or Data Structure |

|1 |WGListener::run |

|2 |WGListener::run |

|3 |WGListener::decrementConnection |

2 Packaging and installation issues

N/A

3 Design metrics to be used

N/A

4 Supplementary information (as required)

1 Scoring Requirements

1. The player must achieve a minimum GPA, a minimum level of experience points, and a minimum number of credits to successfully complete the game

2. The scoring module shall allow a player to retake challenges

3. The scoring module shall accommodate both student life and academic challenges

4. The player shall not be able to receive more than the maximum number of credits for a challenge by taking the challenge multiple times

5. Each time a player retakes an academic challenge, the new score shall be averaged into the players GPA.

6. If a player retakes a student life challenge they shall only be awarded experience points if the new score exceeds all past scores

7. The scoring module shall correctly average in a new score to the players GPA

8. The scoring module shall correctly add experience points to the players total experience points

9. The scoring module shall correctly add the number of credits earned by completing a challenge to the players total credits

10. The scoring module shall notify a player when they have completed the game

11. A player shall only be able to complete the game once

12. A players GPA shall be computed on a 4.0 scale

13. A players experience points shall not fall below 0

14. A players credits shall not fall below 0

15. The scores for a challenge must be on a 4.0 scale

2 Thread Requirements

1. There shall me no more connections at any one time than maxConnections as set in the parameters or default at 30.

2. The server shall allow connections until the maximum number of connections has been reached while the server has a connection to the internet and the client is sending the correct connection command.

3. The connection count shall never go below 0.

-----------------------

MySQL Database

Requesting Connection

(possible future Client)

Client

WGConnection

cliSocket

in

out

cmdLine

username

cmdQueue

wgs

writeToClient

setUserName

run

WGListener

servSock

wgs

numConnections

maxConnections

run

incConnection

decrementConnection

WatsonGameServer

challengeDispatcher

databaseObject

gamekeeper

listener

scorer

maxCon

getChallengeDispatcher

getDatabaseObject

getGameKeeper

getListener

getScorer

WGPlayer

characterName

id



numSLC

numAC

passedAC

totalChallengeAttempts

gpa

slcScores

credits

totExp

hasWon

addACScore

addSLCScore

addCredits

setWon

setPassed

getSLCPoints

getGPA

getCredits

getExperience

hasPassed

won

WatsonGameServer

challengeDispatcher

databaseObject

gamekeeper

listener

scorer

maxCon

getChallengeDispatcher

getDatabaseObject

getGameKeeper

getListener

getScorer

Scorer

CreditsToWin

expToWin

gpaToWin

passingScore

numSLC

numAC

updateScore

Client

WGConnection*

WatsonGameServer

Scorer

GameKeeper

databaseObject

ChallengeDispatcher

WGListener

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download