{
  "openapi": "3.0.3",
  "info": {
    "title": "God Tower Central Server API",
    "version": "0.0.37",
    "description": "API de God Tower pour les messages emojis au sol, les livres d'or de camp, les paniers servez-vous, les voeux publics moderes et l'administration minimale."
  },
  "servers": [
    {
      "url": "http://localhost:8080"
    }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "summary": "Etat du serveur central",
        "responses": {
          "200": {
            "description": "Serveur disponible"
          }
        }
      }
    },
    "/api/openapi.json": {
      "get": {
        "summary": "Specification OpenAPI",
        "responses": {
          "200": {
            "description": "Specification JSON OpenAPI"
          }
        }
      }
    },
    "/api/docs": {
      "get": {
        "summary": "Documentation Swagger UI",
        "responses": {
          "200": {
            "description": "Interface HTML Swagger UI"
          }
        }
      }
    },
    "/api/versions": {
      "get": {
        "summary": "Versions d'API de jeu supportees",
        "responses": {
          "200": {
            "description": "Versions SemVer connues par le serveur"
          }
        }
      }
    },
    "/api/games/{gameVersion}/messages": {
      "post": {
        "summary": "Envoyer un message emoji au sol",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewMessage"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Message cree"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/games/{gameVersion}/maps/{map}/messages": {
      "get": {
        "summary": "Recuperer les messages actifs d'une map",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/Map"
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Nombre de messages au sol adapte par le client a la surface de la map (1 a 100).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Messages de moins de sept jours, du plus ancien au plus recent"
          }
        }
      }
    },
    "/api/games/{gameVersion}/maps/{map}/guestbooks/{guestbookId}/entries": {
      "get": {
        "summary": "Recuperer les entrees recentes d'un livre d'or",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/Map"
          },
          {
            "$ref": "#/components/parameters/GuestbookId"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entrees non expirantes, de la plus ancienne a la plus recente"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "summary": "Signer un livre d'or avec un message emoji",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/Map"
          },
          {
            "$ref": "#/components/parameters/GuestbookId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewGuestbookEntry"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Entree creee"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/games/{gameVersion}/wishes": {
      "post": {
        "summary": "Envoyer un voeu public en moderation",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewWish"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Voeu en attente de moderation"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/games/{gameVersion}/wishes/accepted": {
      "get": {
        "summary": "Lister les voeux publics acceptes",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Voeux publics acceptes"
          }
        }
      }
    },
    "/api/games/{gameVersion}/ghosts": {
      "post": {
        "summary": "Envoyer un fantome K.O.",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewGhost"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Fantome stocke"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/games/{gameVersion}/maps/{map}/ghosts": {
      "get": {
        "summary": "Recuperer des fantomes K.O. disperses pour une map",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/Map"
          },
          {
            "name": "playerName",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Nom du joueur courant a exclure des resultats."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 6
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Fantomes selectionnes"
          }
        }
      }
    },
    "/api/games/{gameVersion}/maps/{map}/baskets/{basketId}/items": {
      "post": {
        "summary": "Deposer un objet dans un panier servez-vous",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/Map"
          },
          {
            "$ref": "#/components/parameters/BasketId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewBasketItem"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Objet depose"
          }
        }
      }
    },
    "/api/games/{gameVersion}/maps/{map}/baskets/{basketId}/draw": {
      "get": {
        "summary": "Tirer jusqu'a 3 objets disponibles pour un panier",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/Map"
          },
          {
            "$ref": "#/components/parameters/BasketId"
          }
        ],
        "responses": {
          "200": {
            "description": "Objets proposes"
          }
        }
      }
    },
    "/api/games/{gameVersion}/maps/{map}/baskets/{basketId}/items/{itemId}/take": {
      "post": {
        "summary": "Prendre un objet precedemment propose par un panier",
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/Map"
          },
          {
            "$ref": "#/components/parameters/BasketId"
          },
          {
            "$ref": "#/components/parameters/ItemId"
          }
        ],
        "responses": {
          "200": {
            "description": "Stock decremente"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/admin/messages": {
      "get": {
        "summary": "Lister les messages pour l'administration",
        "security": [
          {
            "AdminPassword": []
          }
        ],
        "parameters": [
          {
            "name": "gameVersion",
            "in": "query",
            "schema": {
              "type": "string",
                "default": "0.0.37"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Messages admin"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/admin/guestbook-entries": {
      "get": {
        "summary": "Lister les entrees de livres d'or pour l'administration",
        "security": [
          {
            "AdminPassword": []
          }
        ],
        "parameters": [
          {
            "name": "gameVersion",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "0.0.37"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entrees de livres d'or"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/admin/wishes": {
      "get": {
        "summary": "Lister les voeux pour l'administration",
        "security": [
          {
            "AdminPassword": []
          }
        ],
        "parameters": [
          {
            "name": "gameVersion",
            "in": "query",
            "schema": {
              "type": "string",
                "default": "0.0.37"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Voeux admin"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/admin/games/{gameVersion}/messages/{messageId}": {
      "delete": {
        "summary": "Supprimer un message",
        "security": [
          {
            "AdminPassword": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/MessageId"
          }
        ],
        "responses": {
          "200": {
            "description": "Message supprime"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/admin/games/{gameVersion}/guestbook-entries/{entryId}": {
      "delete": {
        "summary": "Supprimer une entree de livre d'or",
        "security": [
          {
            "AdminPassword": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/GuestbookEntryId"
          }
        ],
        "responses": {
          "200": {
            "description": "Entree supprimee"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/admin/games/{gameVersion}/wishes/{wishId}/accept": {
      "post": {
        "summary": "Valider un voeu public",
        "security": [
          {
            "AdminPassword": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/WishId"
          }
        ],
        "responses": {
          "200": {
            "description": "Voeu accepte"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/admin/games/{gameVersion}/wishes/{wishId}": {
      "delete": {
        "summary": "Supprimer un voeu public",
        "security": [
          {
            "AdminPassword": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/GameVersion"
          },
          {
            "$ref": "#/components/parameters/WishId"
          }
        ],
        "responses": {
          "200": {
            "description": "Voeu supprime"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "AdminPassword": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Admin-Password"
      }
    },
    "parameters": {
      "GameVersion": {
        "name": "gameVersion",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "example": "0.0.37"
        }
      },
      "Map": {
        "name": "map",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "example": "relay_camp"
        }
      },
      "BasketId": {
        "name": "basketId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "example": "help_yourself_basket"
        }
      },
      "GuestbookId": {
        "name": "guestbookId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "example": "morning_guestbook"
        }
      },
      "GuestbookEntryId": {
        "name": "entryId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "ItemId": {
        "name": "itemId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "MessageId": {
        "name": "messageId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "WishId": {
        "name": "wishId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Erreur JSON",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "NewMessage": {
        "type": "object",
        "required": ["map", "playerName", "message", "x", "y"],
        "properties": {
          "map": {
            "type": "string"
          },
          "playerName": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "maxLength": 80,
            "description": "Message compose uniquement d'emojis autorises et d'espaces, limite a 10 emojis et 9 espaces internes.",
            "example": "🌟 ✅"
          },
          "kind": {
            "type": "string",
            "enum": ["graffiti", "ko"],
            "default": "graffiti",
            "description": "Type de rendu du message au sol."
          },
          "markerEmoji": {
            "type": "string",
            "description": "Emoji du personnage, requis quand kind vaut ko.",
            "example": "🧑‍🌾"
          },
          "markerColor": {
            "$ref": "#/components/schemas/RgbColor"
          },
          "carriedItems": {
            "type": "array",
            "maxItems": 5,
            "description": "Instantane des objets portes au moment du K.O.; ignore pour un graffiti.",
            "items": {
              "$ref": "#/components/schemas/KoCarriedItem"
            }
          },
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          },
          "rotation": {
            "type": "number",
            "minimum": -3.141592653589793,
            "maximum": 3.141592653589793,
            "default": 0,
            "description": "Rotation en radians, utilisee uniquement pour un graffiti."
          },
          "scale": {
            "type": "number",
            "minimum": 0.5,
            "maximum": 2,
            "default": 1,
            "description": "Echelle visuelle, utilisee uniquement pour un graffiti."
          }
        }
      },
      "KoCarriedItem": {
        "type": "object",
        "required": ["type", "enchanted"],
        "properties": {
          "type": {
            "type": "string",
            "maxLength": 64,
            "pattern": "^[a-z0-9_.:-]+$",
            "example": "honey"
          },
          "enchanted": {
            "type": "boolean",
            "example": false
          }
        }
      },
      "NewGuestbookEntry": {
        "type": "object",
        "required": ["playerName", "message"],
        "properties": {
          "playerName": {
            "type": "string"
          },
          "message": {
            "type": "string",
            "maxLength": 80,
            "description": "Message compose uniquement d'emojis autorises et d'espaces, limite a 10 emojis."
          }
        }
      },
      "RgbColor": {
        "type": "object",
        "required": ["r", "g", "b"],
        "properties": {
          "r": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "g": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "b": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "NewBasketItem": {
        "type": "object",
        "required": ["playerName", "itemType"],
        "properties": {
          "playerName": {
            "type": "string"
          },
          "itemType": {
            "type": "string",
            "example": "honey"
          },
          "stock": {
            "type": "integer",
            "minimum": 1,
            "default": 1
          }
        }
      },
      "NewWish": {
        "type": "object",
        "required": ["playerName", "text"],
        "properties": {
          "playerName": {
            "type": "string"
          },
          "text": {
            "type": "string",
            "maxLength": 180,
            "description": "Voeu texte libre public. Les voeux prives ne doivent pas etre envoyes au serveur."
          }
        }
      },
      "NewGhost": {
        "type": "object",
        "required": ["map", "playerName", "history"],
        "properties": {
          "map": {
            "type": "string"
          },
          "playerName": {
            "type": "string"
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GhostSample"
            },
            "description": "Samples des 10 dernieres secondes. Le serveur retrim l'historique si besoin."
          }
        }
      },
      "GhostSample": {
        "type": "object",
        "required": ["time", "x", "y"],
        "properties": {
          "time": {
            "type": "number"
          },
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          },
          "input": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number"
              },
              "y": {
                "type": "number"
              },
              "action": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  }
}
