На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS

Дорогие друзья! Поздравляем вас с Новым 2018 годом!

Всем удачи, успеха и благополучия!
В новом году ожидаем новых рекордов при подсчёте количества ёлочек на экране ;)


msm.ru
! Предназначение данного раздела
Данный раздел предназначен для публикации уроков и примеров по разработке компьютерных игр любого сорта, в том числе авторских проектов участников нашего форума. Главное условие публикации: необходимо не только выложить свое творение, но и подробно описать процесс его создания, подводные камни и прочие, интересные для собрата-разработчика, вкусности.

Если вы желаете выложить свой проект - пишите в Общеорганизационную тему. Есть какие-то другие вопросы по делу - туда же.

Модераторы: Da$aD
  
> [MMORPG своими руками] 3 Техническая демоверсия, пункты: 3.3, 3.4
    3.3 Система обновления

    Любой клиент начинается с системы обновления. Она позволяет, удобным для пользователя образом, держать функционал клиента в актуальном состоянии. Чтобы написать систему обновления, необходимо определить структуру клиента. Клиент состоит из ядра и игровых модулей. Ядро - базовый, редко изменяемый, функционал клиента (включая как раз и саму систему обновления). Игровые модули отвечают за функционал игры и могут часто меняться. Преполагается, что алгоритм работы таков. Стартует ядро, проверяет актуальность игровых модулей, обновляет их, если это необходимо и запускает начальный модуль. Ядро может обновляться и само, но его обновление потребует перезапуска клиента. Отсюда следует, что система обновления должна знать список файлов клиента с версиями: актуальной и текущей. Поэтому мы можем хранить на сервере некоторый обновлемый файл download.xml, который будет описывать требуемые файлы и номера их версий для актуального состояния клиента:
    ExpandedWrap disabled
      <client>
          <file>
                <name>Имя файла</name>
                <version>Номер актуальной версии</version>      
          </file>    
           ...
      </client>

    В свою очередь у клиента имеется такой же файл resources.xml, имеющий такую же структуру, но в нем записано текущее состояние клиента. Сопоставляя эти два файла система обновления находит недостающие/устаревшие файлы и перезакачивает их. После этого удаляет ненужные.

    Загружаем 2 хмл документа
    ExpandedWrap disabled
      URL url = new URL("http://"+server_host+"/download/download.xml");
      File res_file = new File("clientdata/resources.xml");
      DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      Document actual = parser.parse(url.openStream());
      Document curent = parser.parse(res_file);

    Читаем их в две хеш карты (ключ - имя, значение - версия), а потом сравниваем их
    В результате сравнения в act_map остаются файлы, который надо закачать, а в cur_map - те, что надо удалить
    ExpandedWrap disabled
              HashMap<String,String> act_map = readVersionFile(actual);
              HashMap<String,String> cur_map = readVersionFile(curent);
       
              Iterator<Entry<String,String>> iterator = act_map.entrySet().iterator();
              while(iterator.hasNext())
              {
                  String f = iterator.next().getKey();
                  String act_ver  = act_map.get(f);
                  String cur_ver = cur_map.get(f);
       
                  if(act_ver.equals(cur_ver))
                      iterator.remove();
       
                  cur_map.remove(f);
              }

    После определения списков файлов, осуществляем закачку необходимых файлов стандартными средствами Java
    ExpandedWrap disabled
              need_files = act_map.keySet();
              for(String f:need_files)
              {
                  InputStream from = null;
                  OutputStream to = null;
                  try
                  {
                      URL url2 = new URL("http://"+server_host+"/download/"+f);
                      from = url2.openStream();
                      to = new FileOutputStream("clientdata/"+f);
                      byte[] buf = new byte[1024];
                      int len;
                      while ((len = from.read(buf)) > 0)
                          to.write(buf, 0, len);
                  }
                  finally
                  {
                      from.close();
                      to.close();
                  }
              }

    После этого необходимо удалить файлы, который нам уже не нужны. Для этого тоже используем средства Java
    ExpandedWrap disabled
              need_files = cur_map.keySet();
              for(String f:need_files)
              {
                  File file = new File("clientdata/"+f);
                  file.deleteOnExit();
              }

    И сохранить в файле resources.xml текущее состояние дистрибутива клиента.
    ExpandedWrap disabled
      Transformer t = TransformerFactory.newInstance().newTransformer();
      t.setOutputProperty(OutputKeys.INDENT, "yes");
      t.transform(new DOMSource(actual),new StreamResult(new FileOutputStream(res_file)));


    3.4 Управление игровыми модулями клиента

    Игровой модуль клиента - это одно окно и сопуствующий ему функционал. Графический интерфейс мы будем создавать с помощью дизайнера форм NetBeans. На выходе мы получим готовый класс, наследуемый от JPanel. Однако, для обеспечения связки с ядром клиента, мы между получаемыми классами и JPanel вставим еще одно звено в иерархии - GameModule.

    Первый момент состоит в том, что JPanel - это пока панель, и нам надо эту панель поместить в JFrame, который является контейнером для GUI элементов. Для этого в мы добавим в GameModule метод, который будем вызывать в конструкторе наших панелей.
    ExpandedWrap disabled
      public void createModule(String name)

    Задаем имя для нашего модуля, чтобы потом по нему искать его.
    ExpandedWrap disabled
      this.setName(name);

    создаем окно с размерами установленными редактором панели
    и размещаем его в середине экрана
    ExpandedWrap disabled
      mMyFrame = new JFrame();
                  Dimension scr_size = Toolkit.getDefaultToolkit().getScreenSize();
                  mMyFrame.setSize(this.getPreferredSize());
                  mMyFrame.setLocation(scr_size.width/2 - this.getPreferredSize().width/2,
                                      scr_size.height/2 - this.getPreferredSize().height/2);

    Устанавливаем необходимые свойства окна, в том числе добавляем в него нашу панель
    ExpandedWrap disabled
      mMyFrame.setTitle(name);
                  mMyFrame.add(this);
                  mMyFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                  mMyFrame.setResizable(false);


    Далее необходимо описать добавить метод, который будет вызываться при необходимости показа/обновления окна. Этот обработчик родителя должен быть переопределен в потомках, однако потомки должны буду вызывать это в себе после обработки данных.
    ExpandedWrap disabled
      public void showModule(Node data_node)

    Сначала погасим старое окно
    ExpandedWrap disabled
      if(mCurrentFrame != null)
                      mCurrentFrame.setVisible(false);

    И покажем уже нужное
    ExpandedWrap disabled
      mMyFrame.setVisible(true);
      mCurrentFrame = mMyFrame;


    Для задания определенного дизайна интерфейса мы будем использовать Java LookAndFeel.
    Это пакеты дизайнерских решений для элементов GUI.
    Для этого при старте клиента мы будем загружать один такой пакет
    ExpandedWrap disabled
      UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");


    Данный метод позволяет показать пользователю игровой модуль (окно). При этом предыдущее окно скрывается. Данный метод может использоваться и для обновления текущего окна. data_node - это хмл дерево с данными для показа/обновления, которое пришло от сервера в качестве ответа или null, если клиент запрашивает модуль внутри себя.
    ExpandedWrap disabled
      private static void showModule(String name, Node data_node) throws Exception

    Отдельное имя мы выделим для описания ошибок сервера. Можно сказать, что данный MessageBox - есть игровой модуль ошибки сервера.
    ExpandedWrap disabled
              if(name.equals("server_error"))
                  {
                      Logger.getLogger("ClientLogger").warning("server error");
                      showMessage("Ошибка сервера");
                      showModule("Login",null);
                      return;
                  }

    Если это не ошибка, то сначала надо найти модуль в списке по имени
    ExpandedWrap disabled
              GameModule aim = null;
              for(GameModule m:mModuleList)
                  if(m.getName().equals(name))
                  {
                      aim = m;
                      break;
                  }

    Если мы его не нашли, то необходимо его загрузить и зарегистрировать
    ExpandedWrap disabled
              if(aim == null)
              {
                  aim = (GameModule) Class.forName("clientdata."+
                                                          name+"Panel").newInstance();
                  mModuleList.add(aim);
              }

    И уже после этого вызвать вышеописанный обработчик данного модуля.
    ExpandedWrap disabled
      aim.showModule(data_node);
      А почему ты считаешь, что ядро должно быть неизменным. Например, в первых версиях ты что-то упустил и тебе понадобилось добавить или изменить что-нибудь в файле описания, в результате старое ядро может не суметь его разобрать. Можно конечно отправить игрока загрузить и установить новое ядро, но ему будет удобнее, если ядро само скачает новую версию и перезапустится. Это конечно чуть посложнее, чем обновлять только модули.
      Или оставить за ядром только задачу обновления модулей и запуск основного модуля на выполнение. А задачу проверки актуальности выделить в еще один модуль. Тогда старт игры будет происходить таким образом: Ядро загружает список актуальных модулей, запускает модуль обновления, тот проверяет актуальность всех модулей, в том числе и себя, и передает список подлежащих обновлению модулей ядру, ядро загружает и обновляет их и если все прошло удачно, обновляет список и запускает игру.
      Всё написанное выше это всего лишь моё мнение, возможно ошибочное.
        Немножко подредактировал начало.
        Суть изменений такова.
        Недокументированной фичей была возможность обновления ядра клиента также. Если изменять версии ядра в хмл файле. Файлы ядра благопорлучно закачивались, но их перезагрузка наступала только при втором запуске клиента. Сейчас я добавил код, который проверяет наличие обновления ядра и просит перезапустить клиент.

        ЗЫ для тех у кого уже клиент скачан, первое обновления ядра произойдет по старому.
        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
        0 пользователей:


        Рейтинг@Mail.ru
        [ Script Execution time: 0,1012 ]   [ 20 queries used ]   [ Generated: 20.01.18, 23:08 GMT ]