Zelena Macka project team's tribes backend project
- Enviroment variables
- Recommended IDEA plugins
- Usage of ModelMapper
- Database migration with Flyway
- Values file
- Naming Conventions
- User
POST /register
POST /login
POST /images/avatar - Kingdom
GET /kingdom/resources
GET /kingdom/{id}
POST /kingdom/fight/{id} - Kingdom's buildings
POST /kingdom/buildings
GET /kingdom/buildings
GET /kingdom/buildings/{id}
PUT /kingdom/buildings/{id}
GET /kingdom/leaderboard - Kingdom's troops
POST /kingdom/troops
GET /kingdom/troops
GET /kingdom/troops/{id}
PUT /kingdom/troops/{id} - General chat
POST /message
GET /messages
Backend part (REST API) of Tribes strategy game, written in Java using Spring framework.
-
Tribes is a classic medieval-style strategy game in which the players can gather resources, spend resources on upgrading abilities of their kingdoms and troops which will help them to be stronger and defeat other players in battle.
-
Each player has one
kingdom
with contains maximum of onetownhall
type building.
The player can grow theirkingdom
by collectingresources
to build and level up newbuildings
.
While leveling up,buildings
cannot exceed the level of thetownhall
.
Higher levelbuildings
offer some advantages, for example, a leveled upacademy
would produce strongertroops
.
Basic environment setup needs to be done for application to work.
-
SERVER_PORT
for setting port that API should listen to. Default8080
MYSQL_URL
URL for database connection in format:jdbc:mysql://<domain_address>/tribes?serverTimezone=UTC
MYSQL_USER
your username for access to database
MYSQL_PASS
password for your database login
FLYWAY_URL
URL for database connection in format:jdbc:mysql://<domain_address>/tribes
FLYWAY_USER
your username for access to database (same as MySQL)
FLYWAY_PASS
password for your database login (same as MySQL)SECRET_KEY
private key string encrypted usingHS256
algorithm
TRIBES_GAMETICK_LEN
for storing length of game tick (passage of time) in secondsCONF_TOKEN_LINK
link used for email address verification
EMAIL_SENDER
email address of the project
EMAIL_USERNAME
username for our email address
EMAIL_PASSWORD
password for our email address
EMAIL_SMTP_SERVER
SMTP server for our email address
EMAIL_SMTP_PORT
SMTP port for our email address -
-
- Install the plugin (
Settings
>Plugins
>Marketplace
) - Go to
Settings
>Tools
>Checkstyle
- Use
+
to add config from this project found atconfig/checkstyle/checkstyle.xml
and mark it as active (this setting is saved per project) - From this point on, CheckStyle will verify code against these rules as you write.
- You can also use CheckStyle window from the bottom toolbar in IDEA to scan the whole project and see all errors.
- Install the plugin (
-
-
ModelMapper
helps You to transition between entities and DTO objects back and forth
- We use
ModelMapper
every time when we are creating a DTO object from a model object - In a controller, there should be
@Autowired ModelMapper modelMapper
and passing in the constructor parameter (if constructor exists).
With that you can transform an entity into DTO withmodelMapper.map(entityObject, EntityDTO.class)
. ModelMapper should take care of the transformation, even when the field is missing or in the case when the field in DTO is a subfield of any field of your entity. - If you want to make more difficult transformation, you can add those in the config file using this cheatsheet.
- Or have a look at ours ModelMapper howto
ModelMapperService
extends functionality ofModelMapper
and can do some basic tasks with mapping commonly used objects for You, see ModelMapperService documentation
- We use
-
With database migrations, our database is able to evolve with the project. We use Flyway for the database migrations. Flyway is an open-sourced tool which allows us to implement automated and version-based database migrations.
First we created a base version of our database, which can be found in theresources/db.migration
package in SQL format. See ``V1__init_migration.sql We can use Java or SQL Scripts to update the database. To add new version scripts:- Create a new class in the
java../db.migration
package. - Class must follow a strict naming convention:
V + Version # + _ _ + short description
Version # = YearYYY + MonthM + DayD + HourHour + MinMin
- Extend Version with BaseMigration class and override the migration method with the changes you wish to make to the database.
- Each update will be added to the
flyway_schema_history
and the database can be easily restored to earlier versions.
- Create a new class in the
-
the purpose of
YAML
filevalues.yml
stored inresources
folder is to hold values and constants that affects gameplay and game logic
- To retrieve values from
values.yml
file You useDefaultVals
static class.
This way You do not need autowire anything and values are available even before spring application lifecycle comes to initialization of services and beans. - Current implementation supports non-decimal number values and string values. To retireve them
You are using corresponding
DefaultVals.getInt(...)
andDefaultVals.getString(...)
methods
values.ymlretrieve intValue:test: values: intValue: 100 stringValue: Hello
retrieve stringValue:DefaultVals.getInt("test.values.intValue")
DefaultVals.getString("test.values.stringValue")
- to keep tree structure of values inside
values.yml
file working, You just need to follow two space indentation rule for each sub-branch
- To retrieve values from
-
We use specific naming conventions when naming
DTOs
and FlywayVersion Scripts
The formats should follow:DTO = Entity + Action + Request/Response + DTO
Version Script = V + Version # + _ _ + short description
Version # = YearYYY + MonthM + DayD + HourHour + MinMin
-
- Application uses stateless and sessionless authentication using JWT token.
- All endpoints except POST /register and POST /login
are secured and cannot be accessed without valid JWT token prefixed with
Bearer
prefix inside (separated by one space)Authorization
header.Bearer <Your_JWT_token>
- JWT token is obtained in response body after successfull login.
- JWT token contains following content (claims):
userId
ID of logged in user
username
name of the logged in user
kingdomId
ID of user's kingdom
kingdomName
name of user's kingdom - error responses:
400
authentication header missing
400
jwt token missing
400
jwt token bearer missing
401
token signature is not valid
401
token has expired
401
token unsupported
400
unknown JWT exception
-
- In each class where you want to use the logger, create an instance of it, e.g.
private static Logger logger = LogManager.getLogger(BuildingController.class);
the name of the specific class must be in brackets - Logger and LogManager should be automatically imported. To be sure, check that they are from the
apache.logging.log4j
class imports:org.apache.logging.log4j.LogManager org.apache.logging.log4j.Logger
- Then you can use the logger, using the following style:
Available log levels:
logger.<type of loglevel>("<log message string>")
- OFF,
- FATAL,
- ERROR,
- WARN,
- INFO,
- DEBUG,
- TRACE,
- ALL
- all logs are stored in
logs.log
file
- In each class where you want to use the logger, create an instance of it, e.g.
-
JSON is used for requests and responses.
-
If an error occurs, the following
JSON response body
and its correspondingHTTP code
is returned (to the user).{ "status" : "error", "message" : "<message describing what happened>" }
-
used to create new user including its kingdom
- request body:
{ "username" : "<string>", "password" : "<string>", "kingdomname" : "<string>", "email" : "<string>" }
201
OK response body:{ "id": <number>, "username": "<string>", "email": "<string>", "kingdomId": <number>, "avatar": "<URL string>", "points": 0 }
- error responses:
400
required parameter is missing
400
password shorter than 8 characters
409
user already exists
- request body:
-
used to log in user and obtain JWT Token
- request body:
{ "username": "<string>", "password" : "<string>", }
200
OK response body:{ "status": "ok", "token": "<JWT token string>" }
- error responses:
400
username or password missing
400
request body malformed
401
username or password wrong
- request body:
-
uploads or updates user's avatar picture.
userId
is provided by JWT Token.
- request body:
standart form-data multipart file
201
OK response- error responses:
400
file missing
400
format not allowed
400
file corrupted
413
uploaded file too big
500
cannot access/create store destination
500
disk IO error
500
cannot convert given file
- request body:
-
used to get resources of kingdom for given user (contained in JWT token claims)
200
OK response body:{ "resources": [ { "type": "food", "amount": <number>, "generation": <number>, "updatedAt": <timestamp> }, { "type": "gold", "amount": <number>, "generation": <number>, "updatedAt": <timestamp> } ] }
- error responses:
404
given kingdom not found
404
wrong number of resources
404
resource not found
-
{id}
ID of given kingdom
get the summarized information about given kingdom (requires JWT token just for authentication)
200
OK response body:{ "id": <number>, "name": "<string>", "userId": <number>, "buildings": [ { "id": <number>, "type": "<string>", "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }, ... ], "resources": [ { "type": "<string>", "amount": <number>, "generation": <number>, "updatedAt": <timestamp> }, ... ], "troops": [ { "id": <number>, "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }, ... ] }
- error responses:
404
kingdom with given id does not exist
-
{id}
ID of kingdom to attack
The initiating kingdom is the one that belongs to the user who is logged in (contained in JWT token claims)*
200
OK response body:{ "result": "Congratulation! coolKingdom19 has conquered tomcatKingdom44", "playerStatistics": { "lostBuildings": 0, "lostTroops": 3, "earnedGold": 100, "earnedFood": 50 }, "opponentStatistics": { "lostBuildings": 4, "lostTroops": 3, "lostGold": 100, "lostFood": 50 } "id": <number>, "type": "<string>", "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> } }
- error responses:
400
parameter is missing
403
Kingdom is not able to fight with itself
403
A kingdom without troops can't initiate a battle
403
Kingdoms cannot battle with another out of range level kingdom
404
Id not found
-
used to create new building in kingdom of given user (contained in JWT token claims)
- request body:
{ "type": "<string>" }
type
- building type, allowed types are:townhall
farm
academy
mine
200
OK response body:{ "id": <number>, "type": "<string>", "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }
- error responses:
400
missing type parameter
404
given kingdom not found
404
resource not found/wrong number of resources
406
invalid building type
409
not enough resources in kingdom
- request body:
-
used to get list of buildings in kingdom of given user (contained in JWT token claims)
200
OK response body:{ "buildings": [ { "id": <number>, "type": "<string>", "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }, ... ] }
- error responses:
404
if the given kingdom cannot be found
-
{id}
ID of requested building
get the details of given building for given user and kingdom (contained in JWT token claims)
200
OK response body:{ "id": <number>, "type": "<string>", "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }
- error responses:
400
parameter missing
403
if the given building does not belong to user that sent request
404
the building with given ID is not found
-
{id}
ID of requested building
upgrade the level of given building for given user and kingdom (contained in JWT token claims) if kingdom has enough resources.
200
OK response body:{ "id": <number>, "type": "<string>", "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }
- error responses:
400
parameter is missing
404
building with given ID not found
406
invalid level for building - level cannot be higher thantownhall
level
409
not enough resources to update the building
-
Responses with leaderboard in given
scoreType
based onpageNo
andPageSize
-
Url parameters
pageNo
is the page number of the page to display
pageSize
is the number od outputs on the page
scoreType
istotalScore
by default, but one may set it tobuildingsScore
,troopsScore
orresourcesScore
to obtain different types of leaderboards
isHistory
true for leaderboard of all time, false for current leaderboard -
200
OK response body:{ "currentPageNumber": <number>, "totalPageNumber": <number>, "pageSize": <number>, "scoreType": "<string>", "leaderboard": [ { "kingdomName": "<string>", "totalScore": <number>, "buildingsScore": <number>, "troopsScore": <number>, "resourcesScore": <number> }, { "kingdomName": "<string>", "totalScore": <number>, "buildingsScore": <number>, "troopsScore": <number>, "resourcesScore": <number> }, ... ] }
-
error responses:
404
pageNo must be positive integer
404
pageSize must be a positive integer
404
scoreType must be a one of prescribed strings
404
not enough kingdoms to display given pageNo
-
-
Creates a troop in academy inside given kingdom of user (contained in JWT token claims).
Troop level is based on academy level.
After the academy levels up, only troops created post level up will start at a higher level.
- request body:
{ "buildingId": <number> }
200
OK response body:{ "id": <number>, "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }
- error responses:
403
given building does not belong to current user
406
building is not academy / does not exist
404
resource not found
409
not enough resources to create troop
- request body:
-
Get the troops in kingdom of given user (contained in JWT token claims).
200
OK response body:{ "troops": [ { "id": <number>, "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }, ... ] }
- error responses:
404
given kingdom not found
-
{id}
ID of requested troop
get the troop detail(s) in kingdom of given user. (contained in JWT token claims)
200
OK response body:{ "id": <number>, "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }
- error responses:
403
given troop does not belong to user that sent request
404
troop with given ID not found
404
given kingdom not found
-
{id}
ID of requested troop
upgrade troop level to the level of academy (academy ID given in request body) for given user (contained in JWT token claims)
- request body:
{ "buildingId": <number> }
buildingId
- kingdom's academy (building) ID
200
OK response body:{ "id": <number>, "level": <number>, "hp": <number>, "startedAt": <timestamp>, "finishedAt": <timestamp> }
- error responses:
400
parameter is missing or troop level equal or higher than academy403
given building (academy) or troop does not belong to user who sent request
404
building (academy) with given ID not found409
not enough resources to update the troop or academy full
- request body:
-
Sends message to general chat
- request body:
{ "message": <string> }
201
OK response without body
- error responses:
400
malformed request
400
message is missing
400
message exceeds maximum allowed length
- request body:
-
Get messages from general chat no older than 24 hours
200
OK response body:{ messages: [ { "message": <string>, "createdAt": <string>, "username": <string> }, ... ] }