JavaRush /Java блог /Архив info.javarush /Многопользовательская консольная игра на java
timurnav
21 уровень

Многопользовательская консольная игра на java

Статья из группы Архив info.javarush
Всем привет, давно уже дописал игру, всё никак не доходили руки до написания статьи, она является логическим продолжение вот этой писулины Если вы пока еще не пробовали делать что-либо кроме задач JavaRush, то знакомство с игрой будет как раз именно тем, с чего нужно будет начать подготовку к тестовым заданиям реального проекта, в котором я настоятельно рекомендую поучаствовать каждому. Да и вообще пора уже перестать быть сферическим программистом в вакууме и начать изучать что нибудь за пределами java-core. Для того, чтобы просто посмотреть игру, у вас должен быть установлен MySQL, если вы пока еще таким не пользовались, не стесняйтесь – ставьте, это одна из тех баз данных, которые вы будете использовать в своей работе и личных проектах! Я не буду приводить описание установки и работы с базой, в интернете навалом туториалов и видео, рекомендую разобраться с этим вопросом самостоятельно, это тоже один из самых важных скилов программиста – разбираться самостоятельно :) В жизни вам пригодится умение писать запросы в sql, как нативно, так и через JDBC, hibernate, spring, spring data, возможно список можно продолжить, но мои знания на этом оканчиваются. А теперь отложите чтение этой статьи и разберитесь с MySQL, это на самом деле совсем не сложно, вам нужно поставить сам сервер, из настроек там только логин и пароль. после почитайте про то какие команды используются при работе. Команды для работы с сервером: create, show, use, и прочие, команда help – даст полный список команд. Запросы для работы с конкретной таблицей: select, insert, delete и прочие. особо сильно не углубляйтесь, есть вероятность,что от простого чтения команд запомнится лишь малая часть. вы всё изучите, со временем. Можете поиграть в терминале MySQL, создайте БД, создайте таблички, заполните их, сделайте запрос на вывод данных, добавьте критерии запросов. скорей всего, у вас на этой уйдет не более 2-3 часов, если дольше – не расстраивайтесь, с опытом вы будете осваивать новый материал быстрее. Если с базами у вас проблем нет, то можно приступать к разработке игры, за основу взята игра, о которой я уже писал крестики-нолики. Я очень долго не мог разобраться с тем, как же реализовать мультиплейер, выход я нашел в использовании БД. Процесс игры предполагает что игроки ходят по очереди, все изменения в игре фиксируются в базе данных. уже исходя из этого мы понимаем что у нас есть игрок и есть игровое поле, которое содержит ссылки на игроков, именно в игровом поле должна быть завязана логика, в которой один игрок находится в ожидании хода второго игрока и после чего, их роли меняются и уже первый игрок делает ход, а второй ждет. А так как все изменения должны дублироваться в БД, то после каждого хода нам нужно сохранять поле. Итак мы пришли к первому выводу, в Базе данных должно быть игровое поле и раз уж мы говорим о мультиплеере, то нужно туда добавить и игроков. Создадим таблицы в MySQL, я сделал это нативно, через окно терминала mysql. игровое поле содержит ссылки на игроков, значит логично будет сначала создать таблицу с игроками. Игроки у нас имеют:
  • id – порядковый номер, мы его делаем первичным ключом;
  • name – обычное имя, строка;
  • wins – количество побед;
  • loses – количество поражений;
  • games – общее количество проведенных игр.
Таблица с играми:
  • id – порядковый номер, мы его делаем первичным ключом;
  • x – id игрока играющего х – вторичный ключ;
  • o – id игрока играющего o – вторичный ключ;
  • field – само поле, о его формате будет написано ниже;
  • status – это нужно для корректной работы мультиплеера, статус характеризует состояние игры:
    создана, играем, игра окончена

  • current – тоже для мультиплеера, конкретно во время игры это поле управляет тем чей сейчас ход,
    а после ее окончания оно объявляет победителя или ничью

С таблицами разобрались, теперь нужно создать Java-классы с соответствующими полями – Game и User.

public class Game {
    private Integer id;
    private Integer x;
    private Integer y;
    private Integer field;
    private String status;
    private String current;
}

public class User {
    private Integer id;
    private String name;
    private Integer wins;
    private Integer loses;
    private Integer games;
    private Boolean busy;
}
Добавим пустой конструктор без параметров – для работы с БД и еще один конструктор, которым мы будем создавать объекты. добавим сеттеры и геттеры для всех полей. Теперь разберемся с hibernate :) час от часу не легче. тут немного сложнее, чем с MySQL, я бегло пройдусь по общей структуре. Опять же не всё так сложно, основы можно усвоить через любой туториал за пару-тройку часов, а углублено уже лучше изучать в процессе написания своих проектиков. Работа с БД из JAVA подразумевает использование JDBC, почитайте о нем в вики. Но если пользоваться им в написании кода, то это принесет очень много боли в заднем проходе некоторую сложность в написании классов DAO (тоже вики), hibernate немного улучшит ситуацию, с ним у вас будет гораздо меньше повторяющегося (шаблонного) кода. Для того, чтобы работать hibernate, к проекту необходимо подключить библиотеку, делается это очень просто: Ctrl+Alt+Shift+S(File-Project Structure), заходим во вкладку Libraries, нажимаем "+" и добавляем предварительно скачанную библиотеку (как вариант, отсюда). Для того, чтобы связать классы User и Game вам нужно использовать аннотации – они очень просты в применении, с ними код выглядит так:

@Entity
@Table(name="games")
public class Game {
    private Integer id;
    private Integer x;
    
    @Id
    @GeneratedValue
    @Column(name = "id")
    public Integer getId() {
        return id;
    }

    @Column(name = "x")
    public Integer getX() {
        return x;
    }
}
Тут всё просто,
  • @Entity – говорит, о том что класс является "сущностью", простыми словами, она привязана к таблице в БД.
  • @Table(name="games") – говорит от том к какой именно таблице, games – название таблицы в БД
  • @Id, @GeneratedValue, @Column(name = "id") – этими тремя аннотациями обозначаем, что это поле является идентификационным, генерируется автоматически, а столбец в БД называется id.
  • @Column(name = "x") – имя столбца в БД.
Далее необходимо выстроить уровни – уровень DAO и сервис уровень. Если упростить всё до условий вакуума, то работа с данными идет через сервисный уровень, это один из уровней абстракции, он позволяет сделать работу приложения более независимой, чтобы разработчик логики игры не вдавался в подробности настройки доступа к БД, или например если вдруг мы вместо простого hibernate решили использовать spring, изменения не пойдут дальше сервисного слоя, без которого пришлось бы переписывать половину приложения! это является одним из паттернов проектирования. Начинаем писать уровень DAO.

public class UserDAO {

    public void addUser(User user) throws SQLException {
        Session session = null;
        try{
            session = HibernateUtil.getSessionFactory().openSession();
            session.beginTransaction();
            session.save(user);
            session.getTransaction().commit();
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }
}
Так выглядит операция добавления нового пользователя в бд, обратите внимание, что в метод передается только объект класса-сущности, никакой дополнительной информации тут не требуется. это обеспечивается тем, что мы получаем уже готовую сессию связи с БД из класса HibernateUtil. Рассмотрим его.

public class HibernateUtil {
    private static SessionFactory sessionFactory = null;

    static {
        try {

            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}
Как вы видите здесь тоже всё очень просто, SessionFactory – это интерфейс из библиотеки hibernate, которую мы подключили к нашему проекту. Для корректной работы, остается только заполнить конфигурационный файл hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/tictactoe</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <property name="connection.pool_size">100</property>
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
        <property name="show_sql">false</property>
        <property name="hbm2ddl.auto">update</property>
        <property name="hibernate.connection.autocommit">false</property>
        <property name="current_session_context_class">thread</property>
        <property name="hibernate.enable_lazy_load_no_trans">true</property>
        <mapping class="entity.User" />
        <mapping class="entity.Game" />
    </session-factory>
</hibernate-configuration>
Если пробежать глазами по тегам, становится ясно что и как мы тут настраиваем. Еще одна особенность hibernate, если мы вдруг решим сменить ДБ с MySQL, на какую-либо другую, нам нужно будет только сменить драйвер внутри тега property name="connection.driver_class" Слой DAO готов, делаем сервисный слой. чтобы не создавать объектов DAO в сервисном слое применим паттерн фабрика.

public class Factory {
    private static UserDAO userDAO = null;
    private static Factory instance = null;
    private Factory() {
    }

    public static synchronized Factory getInstance() {
        if (instance == null) {
            instance = new Factory();
        }
        return instance;
    }

    public UserDAO getUserDAO() {
        if (userDAO == null) {
            userDAO = new UserDAO();
        }
        return userDAO;
    }
}
А вот один из методов сервисного уровня

public class UserService {
    static void setUserBusy(User user){
        user.setBusy(true); //делаем его занятым
        //и обновляем его в БД
        Factory.getInstance().getUserDAO().updateUser(user);
    }

}
Код для работы с БД завершен, переписываем игровую логику с учетом изменений. сначала выделим запускающий метод маин в отдельный класс Main, он будет только управляющим классом – игровым меню в котором можно будет запустить игру или посмотреть статистику. Создадим класс GameLogic, в нем будет описана вся логика игры и проверки игрового поля. Сохранение всех изменений на игровом поле и статистики игроков после игры он будет отдавать на откуп сервисному слою. Есть интересная особенность, игровое поле у нас содержится в виде массивов, в базу данных их можно сохранять, но я решил на этом этапе изобрести велосипед и в базе данных поле у меня содержится как int, а именно 9-тизначное число, парсинг которого производится двумя методами класса GameLogic, так делать не рекомендую, в следующих релизах игры я буду исправляться :) Всем удачи в изучении JAVA! Скачать проект можно тут.
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
IgorBrest Уровень 33
1 мая 2015
спасибо автору за очередную статью. очень познавательно. теперь «hibernate» звучит уже не так страшно )))
но такой вопрос: MySQL в данном проекте — это для тренировки?.. ведь можно без него было реализовать. ну там структуру данных сделать в виде xml или еще как?… или я не понял чего-то?