OpenAPI definition

BulletForceHax provides documentation of the Bullet Force HTTP API surface in the form of an OpenAPI definition with accompanying Swagger UI interface. BulletForceHax developers maintain this OpenAPI definition on a best-effort basis, meaning it may not be up to date or may miss various significant endpoints.

Generating OpenAPI clients

One use for OpenAPI definitions is to automatically generate API clients. These clients allow programmatic access to the API through a more high-level API with code that's generated at build-time.

The Bullet Force API is problematic in this regard because it returns a text/html content-types for JSON-formatted responses. OpenAPI client generators can't reasonably predict this, and some use a generic byte stream or string response type instead. You can work around this in several ways:

  • Modify the OpenAPI definition to specify application/json responses instead. This tricks the client generator into generating JSON parsing code, although this causes problems with generated clients that check the response's return type at runtime.
  • Manually parse the returned byte stream or string into the correct data type. The OpenAPI definition still contains all return types, so client generators should include those models in their generate code too.

OpenAPI definition

Below is a copy of the full OpenAPI definition as it was while during compilation of this book. You can also find this hosted online or in the GitHub repository.

openapi: 3.0.3 # latest version supported by progenitor
info:
  title: Bullet Force
  description: |-
    This OpenAPI definition describes the internal Bullet Force API.

    Note that this is a best-effort replication of the API. It may be inaccurate or incomplete.
  contact:
    name: Variant9
    url: https://github.com/holly-hacker/bulletforcehaxv3
  # TODO: license
  version: 1.0.0
servers:
  - url: https://server.blayzegames.com/OnlineAccountSystem
tags:
  - name: Account
    description: API calls related to accounts and authentication
  - name: Chat
    description: API calls related to lobby chat
paths:
  # Login
  /login.php:
    post:
      summary: Checks if a login is valid
      description: |-
        Checks whether a login is valid and returns some info about the account.
      operationId: login
      tags: ["Account"]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                username:
                  $ref: "#/components/schemas/UserName"
                password:
                  $ref: "#/components/schemas/UserPassword"
                store:
                  type: string
                  description: Which game client is used to execute this request
                  example: "BALYZE_WEB" # typo is expected
                useJSON:
                  type: boolean
                  description: Determines the response type
                  example: true
                locale:
                  type: string
                  description: The user's language
                  example: "english"
                tutorialr:
                  type: number
                  example: 1
                crazyGamesToken:
                  type: string
                  description: Key is present but has no value
      responses:
        "200":
          description: Success and error responses?
          content:
            text/html:
              schema:
                type: object
                properties:
                  acnumber:
                    type: string
                    format: number
                    example: "123456789"
                  isTutorial:
                    type: number
                    format: boolean
                    example: 0
                  locale:
                    type: string
                    example: "english"
                  popup:
                    type: boolean
                    example: false
                  status:
                    type: number
                    example: 1
  /get_multiplayer_auth_code.php:
    post:
      summary: Get a code to authenticate a user in a match
      description: |-
        Returns an authentication code that should be sent using the [RpcSendMultiplayerAuthToken](https://variant9.dev/BulletForceHaxV3/BulletForceInternal/PUN/ListOfRpcCalls.html#RpcSendMultiplayerAuthToken) RPC call.

        This token is only valid until a new token is generated.
      operationId: getMultiplayerAuthCode
      tags: ["Account"]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                username:
                  $ref: "#/components/schemas/UserName"
                  required: true
                password:
                  $ref: "#/components/schemas/UserPassword"
                  required: true
      responses:
        "200":
          description: This request always returns 200, regardless of success.
          content:
            text/html:
              schema:
                type: string
              examples:
                success:
                  description: "Success response, containing a hex-encoded token"
                  value: "12345678abcde"
                error:
                  description: "Error response, containing an empty string"
                  value: ""
  # Chat
  /get_lobby_chatV2.php:
    post:
      summary: Fetch in-game lobby chat
      description: |-
        Returns the in-game lobby chat which is seen when loading into the game.

        Note that this is not the chat you see while in a match. In a match, chat messages are received using the [RpcSendChatMessage](https://variant9.dev/BulletForceHaxV3/BulletForceInternal/PUN/ListOfRpcCalls.html#RpcSendChatMessage) RPC call.
      operationId: getLobbyChatV2
      tags: ["Chat"]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                platform:
                  type: string
                  description: The platform the user is using
                  example: WebGLPlayer
                username:
                  $ref: "#/components/schemas/UserName"
                  required: true # works without this but prints error
                password:
                  $ref: "#/components/schemas/UserPassword"
                server:
                  type: string
                  format: uri
                  description: The lobby server the user is using
                  example: wss://game-ca-1.blayzegames.com
      responses:
        "200":
          description: |-
            Contains JSON-encoded messages and some metadata.

            Note that the content type is `text/html`, but the content is encoded as JSON.
          content:
            text/html: # BF does not set correct content type headers
              schema:
                type: object
                properties:
                  messages:
                    type: array
                    maxItems: 50
                    items:
                      $ref: "#/components/schemas/ChatMessage"
                  playersOnline:
                    type: string
                    format: number
                    minimum: 0
                  playersOnlineInServer:
                    type: number
                    minimum: 0
  /send_lobby_chatV2.php:
    post:
      summary: Send a chat message
      description: |-
        Used to send a chat message. Returns a list of the new chat messages in response.
      operationId: sendLobbyChatV2
      tags: ["Chat"]
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                platform:
                  type: string
                  description: The platform the user is using
                  example: WebGLPlayer
                username:
                  $ref: "#/components/schemas/UserName"
                password:
                  $ref: "#/components/schemas/UserPassword"
                chat:
                  type: string
                  format: uri
                  description: The chat message to send
                  example: "Hello world!"
                server:
                  type: string
                  format: uri
                  description: The lobby server the user is using
                  example: wss://game-ca-1.blayzegames.com
      responses:
        "200":
          description: |-
            Contains JSON-encoded messages, including the newly sent message.

            Note that the content type is `text/html`, but the content is encoded as JSON.
          content:
            text/html: # BF does not set correct content type headers
              schema:
                type: object
                properties:
                  messages:
                    type: array
                    maxItems: 50
                    items:
                      $ref: "#/components/schemas/ChatMessage"
components:
  schemas:
    UserName:
      type: string
      description: The player's username
      example: "PC-Username"
    UserPassword:
      type: string
      description: The player's password hashed with SHA512, encoded as uppercase hexadecimal.
      example: "B109F3BBBC244EB82441917ED06D618B9008DD09B3BEFD1B5E07394C706A8BB980B1D7785E5976EC049B46DF5F1326AF5A2EA6D103FD07C95385FFAB0CACBC86"
    ChatMessage:
      type: object
      properties:
        message:
          type: string
          description: The content of the chat message. This includes the sender's username.
          example: "PlayerName: <color=silver>Hello world!</color>"
        time:
          type: number
          format: timestamp
          description: The Unix timestamp when the message was sent, in seconds.
          example: 1735689600
        username:
          type: string
          description: The username of the person that sent the chat message.
          example: "PlayerName"
externalDocs:
  url: https://variant9.dev/BulletForceHaxV3/