This guide covers how to integrate other server software with BlueDragonMC/Puffin.
In production, BlueDragon runs in a Kubernetes cluster.
- Game servers are controlled by Agones with a fleet.
- Proxies are not currently handled by a fleet, however this may change in the future.
Service name | Purpose | Implemented By |
---|---|---|
agones | Interacting with the Agones SDK | Agones SDK |
gs_client | Client methods which are implemented by each game server | Game server |
party_svc | Handling the BlueDragon party system | Puffin |
player_holder | Client methods which are implemented by each proxy and game server | Game server, Proxy |
player_tracker | Tracking the instance, proxy, and game server of every player | Puffin |
queue | Adding and removing players from the queue | Puffin |
server_tracking | Updating available instances on each server and their current states | Puffin |
service_discovery | Finding an available lobby for a player to join | Puffin |
velocity_message | Private messaging | Puffin |
Inbound messages are messages received from other services in the cluster.
To receive messages, each game server starts its own gRPC server on port 50051. This port is exposed to other services in the cluster.
Every game server's gRPC endpoint should implement all services which have "Game server" in the "Implemented By" column of the table in section 2.1.
See the BlueDragonMC/RPC repository for all of BlueDragon's proto files.
Outbound messages are messages sent from a game server to other services in the cluster.
To send messages, a gRPC client should be set up on each game server.
Game servers can send messages to (and expect RPC responses from) all services which have "Puffin" in the "Implemented By" column of the table in section 2.1. The IP address of Puffin can be found because it is exposed as a Kubernetes service. The DNS name "puffin" should resolve to an existing server address. Puffin uses port 50051 for its gRPC server.
BlueDragon runs MongoDB, a document database with no rigid schema.
In the Kubernetes cluster, it is made available under
the mongo
service, so the hostname mongo
should
resolve to a valid MongoDB server address.
The service should be available on port 27017, the default MongoDB port.
Each player has their own document in the players
collection of the database with the following fields:
_id
: The UUID of the player, represented as a string.username
: The username of the player. Updated whenever the name changes.coins
: The number of coins the player has.experience
: The amount of experience the player has.punishments
: A list of punishments that the player has. Punishments are not removed from the list when revoked or expired.type
: A string from the following list:BAN
,MUTE
.id
: A UUID (represented as a string) that uniquely identifies the punishment.issuedAt
: The time the punishment was issued, represented as a Unix timestamp (milliseconds after Jan 01, 1970, 00:00:00 GMT)expiresAt
: The time the punishment will expire/has expired, represented as a Unix timestamp (milliseconds after Jan 01, 1970, 00:00:00 GMT)moderator
: The UUID of the player which enacted the punishment.reason
: A short string description of why the punishment was enacted, provided by themoderator
.active
: A boolean representing whether the punishment is currently effective. If set tofalse
, the expiration should not be considered. If set totrue
, the expiration should still be checked. Expired punishments will never be active, even though this field may be set totrue
.
achievements
: TBD - Not implementedcosmetics
: A list of cosmetics which the player owns. Non-equipped cosmetics are included in the list.id
: A string identifier for the cosmeticequipped
: A boolean representing whether the player has the cosmetic equipped or not.
- Every time a player log in to a game server, their player document should be fetched using their UUID.
- If their username does not match the name in the document, it should be updated to reflect the username change.
- Map data should be lazily fetched for a map when it is loaded.
- Player documents are fetched by other services (mainly Puffin) to look up players' metadata. This is typically just their usernames (UUID <=> username conversion) and name colors for display in chat.
Every game server is connected to at least one proxy. Connections are registered/handled by the proxies, but player
information forwarding must be setup.
The server receives a Velocity forwarding secret using the PUFFIN_VELOCITY_SECRET
environment variable. If this is
present, Velocity modern forwarding should be enabled using the provided secret. If not, Mojang authentication (online
mode) should be enabled.
Each server has at least one lobby, which is initialized on startup.
When a server starts up, it should contact its local Agones gRPC or HTTP server and send a "Ready" request. Periodically, health pings should be sent to the same endpoint. If they are not sent, the server will be shut down due to a health check failure.
Worlds are currently stored in the Anvil format and mounted into each game server's container. The directory structure looks something like this:
/
server/
worlds/
game_name/
map_name_1/
level.dat
config.yml
region/
other_map_name/
level.dat
config.yml
region/
other_game_name/
map_name_1/
level.dat
config.yml
region/
other_map_name/
level.dat
config.yml
region/
Each map can have a config.yml
file inside the Anvil world folder with a few keys.
All map-specific keys are namespaced under the world
key.
world
: (the parent key)name
: A display name for the mapdescription
: A short description of the map, shown to every player at the start of the game.author
: A string with the names of the map's builders.spawnpoints
: A list of spawnpoints for the map.- Each spawnpoint is an array of numbers with the format: [x, y, z, yaw, pitch]
- The numbers may be integers or doubles. It is the responsibility of the client to convert the numbers to the correct format (usually Double).
additionalLocations
: Each map or game may define additional locations. This field is a list of lists of coordinates. The coordinates are in the same format as above.