Code Monkey home page Code Monkey logo

scgame's Introduction

Крестики-нолики API

TicTakToe

Обзор

Серверная часть мобильной игры крестики-нолики, разработанная по стандарту RESTful API. Игровой процесс построен на взаимодействии с тремя контроллерами, каждый из которых работает в пределах своей зоны ответственности. Стандартные правила игры, игра автоматически заканчивается по совершению 9 ходов, если до этого победить никому из игроков не удалось. Каждый ход проверяется на допустимость - очередность хода, статус игры, возможность выбора данной клетки, наличие прав пользователя на ход в этой партии. Для игры требуется регистрация/авторизация. Реализовано через JWT.

Подключение клиентов и обмен игровыми данными реализованы через бибиотеку SignalR, как альтернатива WebSocket и Long Pooling

Содержимое

installation

Для запуска потребуется VSCode или Visual Studio. Приложение используется MSSQL, пользователь по умолчанию 'sa', указан в appsettings.json. В случае запуска из под другого пользователя подправить строку "GameDbConnection" в файле. Пароль от пользователя храниться в secrets.json. Перед запуском потребуется предварительно создать хранилище и поместить туда пароль.

dotnet user-secrets init
dotnet user-secrets set "DbPassword" "YOUR_PASSWORD"
dotnet run

По умолчанию приложение запускается на 5082 порту.

Документация

После запуска, документация будет доступна по ссылке http://localhost:5182/Index.html, страница отладки доступна по ссылке http://localhost:5182/swagger/index.html

Технологии

  • ASP.NET Core - логика, контроллеры и отладка
  • Entity Framework Core - подключение к БД
  • ASP.NET Core Idenity и JWT - регистрация и получение токена после аутентификации
  • Swagger - отладка контроллеров, тестирование
  • Redoc - документирование API
  • SignalR - реализация возможности игры удаленных пользователей

Реализация

Игра протекат по стандартным правилам. Для начала игры нужно иметь двух зарегистрированных пользователей, логины должны быть уникальными. Большая часть запросов к API доступна только авторизованным пользователям. Для получения авторизованного токена нужно сделать запрос к соответствующему контроллеру и поулчить Bearer токен (действителен 20 минут) и добавить в заголовок запроса.

curl -X 'POST' \
  'http://localhost:5182/User/Create' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -d '{
  "userName": "string",
  "password": "string",
  "email": "string"
}'

После получения токена и добавления в заголовок запроса можно

  • создать новую игру
  • создать приглашение и отправить зарегистрированному пользователю
  • принять запрос и удалить, если вас пригласили
  • делать ходы в партии

Игра начинается только после принятие вторым игроком приглашения, только после этого в игре возможны делать ходы. Игровое поле представляет из себя запись 9 ячеек с индексами [1-9]. Это позволяет все игровое поле хранить в базе данных и выгружать по запросу. После каждого хода проверяется на ступило ли условия окончания игры - истекли ходы или кто-то выиграл. Для этого данные преобразуются в двумерный массив и выполняется проверка, ход заносится в БД, после чего данные возвращаются снова в виде последовательности проиндексированных ячеек. При начале игры создается игровая комната (SignalR Hub), что позволяет игрокам в режиме реального времени совершать ходы и видеть изменения игрового процесса.

Сущности

Основная сущность - игра. Содержит в себе информаци о участниках, очередность и количество ходов, игровое поле (доска). Game
"Game": {
        "type": "object",
        "properties": {
          "gameId": {
            "type": "integer",
            "format": "int32"
          },
          "status": {
            "$ref": "#/components/schemas/GameStatus"
          },
          "queue": {
            "type": "boolean",
            "nullable": true
          },
          "firstPlayerName": {
            "type": "string",
            "nullable": true
          },
          "firstPlayerId": {
            "type": "string",
            "nullable": true
          },
          "winner": {
            "type": "string",
            "nullable": true
          },
          "secondPlayerName": {
            "type": "string",
            "nullable": true
          },
          "secondPlayerId": {
            "type": "string",
            "nullable": true
          },
          "moves": {
            "type": "integer",
            "format": "int32"
          },
          "boardId": {
            "type": "integer",
            "format": "int32"
          },
          "board": {
            "$ref": "#/components/schemas/Board"
          },
          "createTime": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      }
Игровое поле (доска). 9 клеток пронумерованных по порядку. В случае хода первого игрока выбранному полю присваивается значение 1, в случае второго - 2. Board
 "Board": {
      "type": "object",
      "properties": {
        "boardId": {
          "type": "integer",
          "format": "int32"
        },
        "fieldId": {
          "type": "integer",
          "format": "int32"
        },
        "fields": {
          "type": "array",
          "items": {
            "$ref": "#/components/schemas/Field"
          },
          "nullable": true
        }
}
Игровая клетка (ячейка). Field
 "Field": {
      "type": "object",
      "properties": {
        "fieldId": {
          "type": "integer",
          "format": "int32"
        },
        "fieldIndex": {
          "type": "integer",
          "format": "int32"
        },
        "fieldValue": {
          "type": "integer",
          "format": "int32",
          "nullable": true
        }
      },
      "additionalProperties": false
    }
Приглашение. Содержит информаци о игре, на которую пригласили, а также информацию о приглашенном. В случае принятия в сущность игра вносистя информация и принявшем приглашение как о втором игроке, создается игровка комната и игра начиается, в случае отказа - приглашение удаляется Field
"Invite": {
      "type": "object",
      "properties": {
        "inviteId": {
          "type": "integer",
          "format": "int32"
        },
        "gameId": {
          "type": "integer",
          "format": "int32"
        },
        "firstPlayer": {
          "type": "string",
          "nullable": true
        },
        "secondPlayer": {
          "type": "string",
          "nullable": true
        },
        "game": {
          "$ref": "#/components/schemas/Game"
        }
      },
      "additionalProperties": false
    }

Контроллеры

Для игры используется 3 контроллера, полное описание изложено в документации.

  • UserController. Ответственен за пользователя. Позволяет зарегистрировать пользователя, проверить есть ли пользователь с таким именем и получать авторизационный токен.
  • GameController. Отвечает за игровой процесс. Выдает список партий, начинает игру, делает ходы во время активной игры.
  • InviteController. Отвечает за механизм приглашений. Позволяет создать новое, отослать кому-либо из пользователей, принять или пригласить.

Клиенты

Для реализации онлайн игры была использована библиотека SignalR. После начала игры создается игровая комната, являющаяся оберткой Hub класса

Создание комнаты:

RoomRequest room = new(userName+"_room"+game.GameId);
         _registry.CreateRoom(room.Room);

Hub подключается как endpoint:

app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<PlayRoomHub>("/playroom");
                endpoints.MapDefaultControllerRoute();
            });

Для реализации онлайна необходимо реализовать подключение к хабу. Например, на JavaScript подключение к комнате выглядит следующим образом:

var currentRoom = ""

    let connection = new signalR.HubConnectionBuilder()
        .withUrl("/playroom")
        .build();
        
const join = (room) => connection.start()
        .then(() => connection.invoke('JoinRoom', {room}))
        .then((history) => {
            console.log('message history', history)
            currentRoom = room
        })

Все участники игры могут обмениваться ходами через вызов метод SendMessage,

const send = (message) => connection.send('SendMessage', {message, room: currentRoom})

передавая в качестве сообщения сущность Game, которая возвращается от контроллера после каждого сделанного хода. После проверки работоспособности данного подхода, дальнейшая разработка клиента игры не производилась.

scgame's People

Contributors

vasjen avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.