Client-to-dbserver

From Project: Ouroboros
Jump to navigation Jump to search

The game client connects to port 7000 to find dbserver, and uses the common Network protocol.

When a QueueServer is in use, then it listens on port 7000 and clients will connect there. Otherwise, dbserver listens on port 7000 and accepts client connections.

Contents

dbserver game client interface

When a game client disconnects from dbserver, then:

  • if the client is not logged in, dbserver sends AS_QUIT_GAME to authserver with reason 1
  • if the client is logged in but does not have an entity loaded, dbserver sends AS_QUIT_GAME to authserver with reason 2
  • if the client is loaded onto a MapServer, then the MapServer is sent DBSERVER_FORCE_LOGOUT with reason -1
  • if the client is loaded but not connected to a MapServer, then an ACCOUNT_CLIENT_LOGOUT_ACCOUNT is sent to AccountServer and AS_QUIT_GAME is sent to authserver with reason 3

When dbserver receives a command on a link from a client, it first checks if the server is shutting down, and if it is then sends the client DBGAMESERVER_MSG with "ServerShutdown", and quits the client.

When quitting a client:

  • If QueueServer is not used, dbserver closes the link
  • If QueueServer is used, dbserver sends QUEUESERVER_SVR_REMOVECLIENT to QueueServer for this client

If the client is not logged in, then receiving any message other than DBGAMECLIENT_LOGIN will result in DBGAMESERVER_MSG with "NotLogged", and quits the client.

When QueueServer is used, the protocol is modified by prefixing headers onto packets so that QueueServer can proxy them. See Dbserver-protocols#queue for details.

Commands, client to server

DBGAMECLIENT_LOGIN

Header if QueueServer is in use:

autobits client_ip
autobits client_link_id

Body of request:

string account_name
packint(1) auth_id
packint(1) protocol_version
bits(64) test_auth_data
packint(1) dont_check_version
string game_version
bits(64) game_checksum
bits(32) cookie
optional bits(1) no_timeout
optional string patch_value
optional string not_used
optional zipped system_specs
optional packint(1) keyed_access_level
optional string issued_to

If login fails, and QueueServer is not used, the message is ignored.

If login fails, and QueueServer is used, then a QUEUESERVER_SVR_REMOVECLIENT will be sent to QueueServer and the game client destroyed. (Bug: the pointers are not cleaned up so if more messages come back from QueueServer then dbserver will break)

protocol_version must be DBSERVER_CLIENT_PROTOCOL_VERSION (20110614) or dbserver will send DBGAMESERVER_MSG with "WrongProtocol" and fail login.

test_auth_data is used only if fake auth is enabled, and will be used as the auth user data which AuthServer would have set.

If fake auth is not used, and the correct game client version number begins with "dev:", then version checking is disabled. If dont_check_version is 0 in the request, version checking is disabled. Otherwise, the game_version field is checked for case-insensitive equality with the correct client version recorded in dbserver. If it does not match, dbserver will send DBGAMESERVER_MSG with "WrongVersion %s %s" or "WrongVersionPatcher %s %s" and fail login.

If version checking was disabled by the request from the client, then dbserver will instead attempt to match the "Branch" field of the versions. If both versions have a branch and they don't match, dbserver will send DBGAMESERVER_MSG and fail login.

account_name is stored in the GameClientLink struct with the value sent by the client.

If fake auth is not used, dbserver now checks for a valid auth entry from AuthServer. cookie is used to look up an auth entry, which must match cookie and account_name in order to be used. If it does match, then the following pieces of data are copied from the AuthServer data to the GameClientLink:

  • account name
  • loyalty
  • loyaltyLegacy
  • payStat
  • auth user data

vip is then set based on payStat, and vipFlagReady is set to 1.

The auth entry is cleared if a match is made. If a match is not made, dbserver will send DBGAMESERVER_MSG with "DBInvalidLogin" and fail login.

If fake auth is used, then test_auth_data is used from the client, the loyalty points are set based on servers.cfg settings, and vip and vipFlagReady are both set to 0. A fake auth_id is generated.

At this point the client is marked as successfully logged in, and AS_PLAY_GAME is sent to AuthServer.

If no_timeout is not 0 and dbserver is not in production mode, then the client link has timeouts disabled.

patch_value is ignored.

system_specs is logged but otherwise ignored.

keyed_access_level and issued_to are ignored, but issued_to is only present if keyed_access_level is not 0.

If the authname limiter is set in servers.cfg, then the account name is now checked against the names in server/db/auto_allowed_authnames.cfg and server/db/allowed_authnames.cfg. If the limiter is set and the account name is not in the allowed lists, and the auth limiter is configured in servers.cfg to enqueue, then the client is set to be queued. Otherwise, dbserver will send DBGAMESERVER_MSG with "AccountNotAllowed" and fail login. (Bug: this has happened after the client is marked as logged in, so they are rejected but still able to send other commands.)

If queue server is used, the queue is now checked to see if players should be allowed to connect, taking the first match in this list:

  • People in the auto-login list, or reconnecting players, will be allowed
  • Free players are queued if AccountServer is not responding
  • Everybody is queued if overload protection is enabled
  • If nobody is queued, and MaxPlayers is set in servers.cfg, and the number of online people is less than MaxPlayers, the player is allowed
  • If the auth limited is in queueing mode, and the player is allowed to login, and the number of online people is less than MaxPlayers, the player is allowed
  • Otherwise they are queued

QUEUESERVER_SVR_ENQUEUESTATUS is then send to QueueServer with the decision made.

If queue server is not used, the player is directly logged in (this procedure will be invoked by QueueServer in QUEUECLIENT_CLIENTPLAY):

  • The ShardAccount for this auth_id is marked as logged_in
  • DBGAMESERVER_ACCOUNTSERVER_CATALOG is sent to the client
  • ACCOUNT_CLIENT_REGISTER_ACCOUNT is sent to AccountServer
  • ACCOUNT_CLIENT_AUTH_UPDATE is sent to AccountServer
  • ACCOUNT_CLIENT_INVENTORY_REQUEST is sent to AccountServer
  • DBGAMESERVER_ACCOUNTSERVER_INVENTORY is sent to the client
  • DBGAMESERVER_SEND_PLAYERS is sent to the client

DBGAMECLIENT_CHOOSE_PLAYER

packint(1) slot_index
packint(1) local_map_ip
string name
autobits create_location

slot_index must be in the range 0..MAX_PLAYER_SLOTS-1 (currently 0..47), or the client will be quit.

If the dbserver is VIP-only, and the client does not have VIP status, dbserver responds with DBGAMESERVER_MSG message "CantResumeVIPShard" and processing stops.

character creation

slot_index describes the character slot of this player to be selected. If this slot does not have a character in it, then a new character will be created with name name and location create_location.

The create_location field determines the faction (hero or villain) and starting map, including whether the tutorial will be run.

If the player name is already in use, DBGAMESERVER_MSG message 'DuplicateName "%s"' % name is sent. If the name is empty, DBGAMESERVER_MSG message 'CantResumeEmptyChar' is sent.

The name is then temporarily reserved, an SQL barrier is sent, and an SQL read is sent to find an unlocked slot ID.

When this read returns, the account is checked to make sure it has enough slots to create a new character, and DBGAMESERVER_MSG message "NotEnoughSlots" is sent otherwise.

If there are enough slots, then a new entity is created with these fields set:

AuthId // set to the auth_id from AuthServer
AuthName // set to the account name
StaticMapId // set to the starting map ID
Name // set to the character name
AccessLevel // set to the server default access level
PlayerType // set based on starting location
Ents2[0].PlayerSubType // set to kPlayerSubType_Normal
Ents2[0].PraetorianProgress // set based on starting location
Ents2[0].InfluenceType // set to kPlayerSubType_Normal

character loading

If this slot is has a character in it, then dbserver checks the entity for this slot. If it is currently loaded, or there is a pending database fetch for it, then that entity is logged out (sending DBSERVER_FORCE_LOGOUT to its MapServer if it has one) and DBGAMESERVER_MSG is returned to the client choosing a player, with a message of 'CharacterLoggingOut "%s"' % name, and processing stops.

If there is a character in the slot and it is not loaded, then dbserver checks to see if it is an offlined entity. If it is, then that entity is logged out and dbserver responds with DBGAMESERVER_MSG, message "InternalErrorCantChooseOfflineCharacter". Otherwise, dbserver schedules a database fetch for the entity.

common process

After creating or loading a character, the process continues here.

When the entity has been fetched from the database, processing resumed (in handlePlayerContainerLoaded). If the entity could not be found, then dbserver responds with DBGAMESERVER_MSG, message "CantFindPlayer %d" % entity_id.

If fake auth is not being used, then the auth user data field for this client (send by authserver in the login process) is converted to hex and compared with Ents2[0].AuthUserDataEx in the entity. If they are not the same, the entity is updated with the auth user data from authserver.

If fake auth is being used, some legacy processing to convert the AuthUserData field into Ents2[0].AuthUserDataEx is run. This appears to be obsolete.

The entity is then marked as having valid auth user data.

If the entity is banned, then it is logged out, and dbserver responds with DBGAMESERVER_MSG message 'CharacterBlocked "%s"' % name.

If the entity is locked by account server (in the middle of shard transfer), then it is logged out, and dbserver responds with DBGAMESERVER_MSG message 'CharacterAccLocked "%s"' % name.

If the AuthId field in the entity does not match the one from AuthServer, then the container is updated. (Comments indicate this is to fix up containers broken by dbquery)

If QueueServer is in use, the client_ip field in the entity struct is set to the IP address sent by QueueServer. Otherwise, it is set to the address of the link.

If the entity is currently marked active or logging in, it is forcibly logged out, and dbserver responds with DBGAMESERVER_MSG message 'AccountLogging %s "%s"' % account_name, entity_name.

The authname limiter is now checked, in the same manner described in DBGAMECLIENT_LOGIN. If this account is not allowed to login at this time, then the entity is forcibly logged out, and dbserver responds with DBGAMESERVER_MSG message 'AccountNotAllowed %s "%s"' account_name, denied_message.

dbserver will look for a clone of the entity's static map, and select one based on the number of players currently on it. If local_map_ip is set, then the MapServer with that IP address is selected instead. If no such MapServer exists, the entity is logged out, nd dbserver responds with DBGAMESERVER_MSG message "CantFindLocalMapServer %s" % ip.

If the entity loading process is already begin and handed to a MapServer, then dbserver sends DBGAMECLIENT_MSG to this link with "DuplicateLogin" and quits it. If the link used in the loading process is different to this one, then that link is also sent the same message, and disconnected.

A COMMCONTROL_IDLE control message is sent to the client. Comments indicate this is because the loading process can take a long time.

The map loading process is started, launching a new MapServer if necessary. The MapId and, if necessary, StaticMapId fields in the entity container are updated with the map ID.

The entity is added to the list of waiting entities for this MapServer, which is then polled to see if the MapServer is ready to load it. When the MapServer is ready (not marked as starting), then the entity is locked to this MapServer, and DBSERVER_CONTAINERS is sent to it. DBSERVER_ACCOUNTSERVER_LOYALTY might also be sent.

If the map loading fails for whatever reason, some DBGAMESERVER_MSG will be sent with an error message, and the entity will be unloaded. If the character is newly created, the entity will also be deleted.

If the map loading succeeds, then the entity will be marked as logging in and connected. No messages are sent to the client at this time (the next step is handled by MapServer).

DBGAMECLIENT_MAKE_PLAYER_ONLINE

packint(1) slot_index

If the character in slot_index is offlined, then is is restored from offline storage to the database, and DBGAMESERVER_SEND_PLAYERS is sent again (repeating the final step of the login process).

DBGAMECLIENT_DELETE_PLAYER

packint(1) entity_id

The entity is forcibly logged out, with reason -3.

It is then deleted.

DBGAMECLIENT_GET_COSTUME

packint(1) slot_index

DBGAMESERVER_SEND_COSTUME is sent to the client, for the character in this slot

DBGAMECLIENT_GET_POWERSET_NAMES

packint(1) slot_index

DBGAMESERVER_SEND_POWERSET_NAMES is sent to the client, for the character in this slot.

DBGAMECLIENT_ACCOUNTSERVER_UNSECURE_CMD

This command is ignored.

DBGAMECLIENT_ACCOUNTSERVER_CHARCOUNT

This message has no body.

ACCOUNT_CLIENT_CHARCOUNT_REQUEST is sent to AccountServer, using the details of this client.

DBGAMECLIENT_ACCOUNTSERVER_CATALOG

This message has no body.

DBGAMESERVER_ACCOUNTSERVER_CATALOG is sent to the client.

DBGAMECLIENT_ACCOUNTSERVER_LOYALTY_BUY

string auth_name
autobits node_index

auth_name is ignored

If node_index identifies a valid loyalty node, and the client has enough loyalty points to purchase it, then ACCOUNT_CLIENT_LOYALTY_CHANGE is sent to AccountServer. Otherwise, DBGAMESERVER_ACCOUNTSERVER_CHARCOUNT is sent to the client, with values 0 and "AccountClientNoServer" (bug: this is the wrong error message).

DBGAMECLIENT_ACCOUNTSERVER_LOYALTY_REFUND

This message has exactly the same format and behaviour as DBGAMECLIENT_ACCOUNTSERVER_LOYALTY_BUY, with the relevant bit in the message to AccountServer changed.

DBGAMECLIENT_SHARD_XFER_TOKEN_REDEEM

autobits auth_id
string client_name
autobits slot_index
string dest_shard

slot_index must correspond to the entity named client_name on the currently connected account, or dbserver will ignore this message.

dbserver sends ACCOUNT_CLIENT_SHARD_XFER_TOKEN_CLAIM to AccountServer:

autobits auth_id
autobits entity_id
string dest_shard
bits(1) is_vip

Where is_vip is the VIP status of the connected account, dest_shard is copied from the input, and entity_id is the entity corresponding to client_name and slot_index.

DBGAMECLIENT_RENAME_CHARACTER

string old_name
string new_name

dbserver find the entity old_name, and checks the slots on the sending account for that entity.

If no such character is found, dbserver responds with DBGAMESERVER_RENAME_RESPONSE:

string "PaidRenameInvalidCharacter"

Otherwise, dbserver sends DBSERVER_RELAY_CMD to the MapServer for this entity, forcibly loading them if necessary, with command:

cmdrelay_dbid %d
playerrename_paid %s %d "%s" "%s"

populated with the entity id, account name, entity id, old name, and new name.

DBGAMECLIENT_RENAME_TOKEN_REDEEM

autobits auth_id
string client_name
autobits slot_index

slot_index must correspond to the entity named client_name on the currently connected account, or dbserver will ignore this message.

dbserver sends ACCOUNT_CLIENT_GENERIC_CLAIM to AccountServer:

autobits auth_id
autobits type
autobits entity_id

where type is kAccountRequestType_Rename.

DBGAMECLIENT_RESEND_PLAYERS

This message has no body.

dbserver sends DBGAMESERVER_SEND_PLAYERS to the client, the same as during login. Entities are refetched from the database.

DBGAMECLIENT_CHECK_NAME

string name
autobits temp_lock

If name is an existing entity, or has a valid temporary lock (character creation in progress), then dbserver responds with DBGAMESERVER_CHECK_NAME_RESPONSE:

autobits success
autobits locked

where success and locked will be 1 if the temporary lock is held by the sending account (in which case it will be refreshed), and will be 0 in all other scenarios.

Otherwise, if this account has an entity in slot 0, then the MapServer for that entity is sent DBSERVER_RELAY_CMD, forcibly loading it if necessary:

cmdrelay_dbid %d
check_player_name %d %d "%s"

populated with the entity ID for slot 0, auth_id, temp_lock, name.

Otherwise, if this account does not have an entity in slot 0, then dbserver picks a static, active MapServer and sends it DBSERVER_RELAY_CMD:

cmdrelay_db_no_ent
check_player_name %d %d "%s"

populated with auth_id, temp_lock, name.

If dbserver couldn't find an active MapServer, it responds with DBGAMESERVER_CHECK_NAME_RESPONSE, both fields set to 0.

DBGAMECLIENT_SLOT_TOKEN_REDEEM

This message has no body.

dbserver checks the ShardAccount for this connection for redeemable slots.

If the ShardAccount already has the maximum number of slots, or does not have any redeemable slots, dbserver responds with DBGAMESERVER_LOGIN_DIALOG_RESPONSE:

string msg

where msg is redeemSlotsTooMany or redeemSlotsNotEnough.

If a slot can be redeemed, dbserver sends AccountServer ACCOUNT_CLIENT_GENERIC_CLAIM:

autobits auth_id
autobits type
autobits db_id

where type is kAccountRequestType_Slot and db_id is 0.

In the event of an error response, dbserver sends ACCOUNT_CLIENT_INVENTORY_REQUEST to AccountServer:

autobits auth_id

DBGAMECLIENT_UNLOCK_CHARACTER

string name

dbserver looks for an entity called name in a slot of the sending account.

If it finds one, then it checks the database for unlocked characters owned by this account.

dbserver computes the number of owned slots (based on vip status and account inventory), and checks that the number of unlocked characters is strictly less than this amount. If it is, then dbserver sends DBSERVER_RELAY_CMD to the MapServer for this entity, forcibly loading it:

cmdrelay_dbid %d
unlock_character

populated with the ID of the selected entity.

Otherwise, dbserver responds with DBGAMESERVER_MSG, message NotEnoughSlots.

DBGAMECLIENT_QUITCLIENT

This message has no body.

dbserver uses the same procedure as if the link was disconnected.

DBGAMECLIENT_SIGN_NDA

autobits subtype
autobits value

dbserver sends ACCOUNT_CLIENT_SET_INVENTORY to AccountServer:

autobits auth_id
authbits2 sku
autobits value

where sku is the ID for "nda_beta" if subtype is 1, and "nda_test" for all other values of subtype.

dbserver then sends ACCOUNT_CLIENT_INVENTORY_REQUEST to AccountServer.

DBGAMECLIENT_GET_PLAYNC_AUTH_KEY

autobits request_key
string auth_name
string digest_data

dbserver proxies this message to AccountServer as ACCOUNT_CLIENT_GET_PLAYNC_AUTH_KEY, prefixing it with auth_id.

DBGAMECLIENT_CAN_START_STATIC_MAP

autobits create_location

dbserver selects a starting location, using the same process as when creating a new player.

dbserver responds with DBGAMESERVER_CAN_START_STATIC_MAP_RESPONSE:

packint(1) response

where response is 1 if a MapServer for that map is running or can be started, and 0 if no MapServer for that location is available.

DBGAMECLIENT_CHOOSE_VISITING_PLAYER

autobits entity_id

If the entity is loaded or being loaded, then dbserver logs out the current client with reason -1 and sends DBGAMESERVER_MSG with message 'CharacterLoggingOut "%s"' % entity_name.

Otherwise, dbserver continues from the login process after character loading.

DBCLIENT_ACCOUNTSERVER_CMD

In production mode, this command is ignored.

In development mode, this message is proxied to AccountServer as ACCOUNT_CLIENT_SLASHCMD:

autobits message_dbid
autobits auth_id
autobits dbid
string cmd

Commands, server to client

DBGAMESERVER_SEND_PLAYERS

autobits auth_id
string client_commands
bits(1) nag_going_rogue_purchase
bits(1) is_beta_shard
bits(1) is_vip_shard
packint(1) player_slot_count
packint(1) max_slots
packint(1) bonus_slots
packint(1) vip
packint(1) show_premium_slot_lock_nag
repeated max_slots times:
  packint(1) level
  string name
  string class
  string origin
  packint(1) db_flags
  packint(1) player_type
  packint(1) player_sub_type
  autobits player_type_by_location
  packint(1) praetorian_progress
  string primary_powerset
  string secondary_powerset
  packint(1) body_type
  float32 body_scale
  float32 bone_scale
  bits(32) skin_colour
  string map_name
  packint(1) seconds_on_map
  bits(32) seconds_until_offline
  bits(32) last_online
  bits(2) slot_lock
bits(1024) auth_user_data
bits(1024) overridden_auth_bits

map_name will be "OFFLINE" for an entity which is currently offlined.

This message is used by dbserver to send the client details on all entities owned by this account. It is used (at least) to show the character selection screen.

DBGAMESERVER_MSG

string msg

This message returns an error to the client, which is made available to client code in the dbGetError function.

The message will typically be rendered by the client in a UI dialog, applying localisation messages.

DBGAMESERVER_MAP_CONNECT

packint(1) entity_id
packint(1) map_id
packint(1) ip
packint(1) ip
packint(1) udp_port
packint(1) tcp_port
packint(1) login_cookie

dbserver sends this message during map transfer.

The game client uses the first ip, udp_port, and login_cookie and ignores the rest of the message. This is used for the game client to connect to a mapserver.

DBGAMESERVER_DELETE_OK

bits(1) success

dbserver sends this message in response to DBGAMECLIENT_DELETE_PLAYER

DBGAMESERVER_SEND_COSTUME

packint(1) slot_index
packint(1) count
repeated count times:
  bits(32) sub_id
  string geom
  string tex1
  string tex2
  string name
  string fxname
  bits(32) colour1
  bits(32) colour2
  bits(32) colour3
  bits(32) colour4
bits(1) has_appearance
optional if has_appearance is 1:
  packint(1) body_type
  bits(32) skin_colour
  packint(1) 2d_body_scale_count
  packint(1) 3d_body_scale_count
  repeated 2d_body_scale_count times:
    float32 scale
  bits(1) converted_scale
  repeated 3d_body_scale_count times:
    float32 scale

dbserver sends this message in response to DBGAMECLIENT_GET_COSTUME.

The costume is fetched from the database and this message is sent to the client when responses are returned.

DBGAMESERVER_SEND_POWERSET_NAMES

packint(1) slot_index
string primary
string secondary

This message is sent in response to DBGAMECLIENT_GET_POWERSET_NAMES.

It contains the names of the powersets for the entity.

DBGAMESERVER_ACCOUNTSERVER_CHARCOUNT

DBGAMESERVER_ACCOUNTSERVER_CATALOG

DBGAMESERVER_ACCOUNTSERVER_CLIENTAUTH

DBGAMESERVER_ACCOUNTSERVER_PLAYNC_AUTH_KEY

DBGAMESERVER_ACCOUNTSERVER_NOTIFYREQUEST

DBGAMESERVER_ACCOUNTSERVER_INVENTORY

DBGAMESERVER_ACCOUNTSERVER_UNAVAILABLE

DBGAMESERVER_CONPRINT

string msg

This message is never sent by dbserver, and is presumably for debugging. The client prints the message given.

DBGAMESERVER_RENAME_RESPONSE

DBGAMESERVER_UNLOCK_CHARACTER_RESPONSE

DBGAMESERVER_CHECK_NAME_RESPONSE

DBGAMESERVER_LOGIN_DIALOG_RESPONSE

DBGAMESERVER_QUEUE_POSITION

DBGAMESERVER_CAN_START_STATIC_MAP_RESPONSE