понедельник, 31 октября 2011 г.

Командиры

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

 

Каждый командир может овладеть различными специальностями на различном уровне, который можно поднимать, имеет собственный флот, постройки, счет в игровой валюте и может быть участником корпорации (проще говоря - совместно с другими командирами, в том числе других игроков, организовать фирму).

 

Сами игроки анонимны - вы не можете узнать кто стоит за определенным командиром, если он сам об этом не скажет. Или не выдаст себя иным способом, например стилем общения. Общение в игре идет от лица командиров, а не игроков.

Это в частности значит что ваш верный союзник может оказаться в то же время вашим заклятым врагом. И предать в самый ответственный момент. Мораль - больше общайтесь и с тем и с другим, может быть вы сумеете вовремя вывести засланца на чистую воду. С другой стороны - тут больше возможностей проявить свои актерские способности. Сумеете ли вы внедриться в стан врага, не выдать себя ни словом, ни поступками и правильно выбрать решающий момент для нанесения удара? Или получится:

 

Штирлиц шел по городу и никак не мог понять, почему все принимают его за советского шпиона? Толи в этом была повинна шапка ушанка с красной звездой, толи волочившийся сзади парашют...

 

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

 

Еще одна причина для создания нескольких командиров - один человек не может обладать всеми специальностями сразу.  Можно быть зубным врачом, а потом стать популярным певцом, но покажите мне человека которому удается совмещать оба этих занятия? Т.е. конечно есть такие уникумы, которые могут заниматься боксом, играть на скрипке и разбираются в ядерной физике. Но это скорее редкость чем правило. Боксер может бренчать на пианино, а пианист набить морду гопнику, но чтобы сегодня побить Тайсона, а завтра получить первую премию на мировом конкурсе исполнителей классической музыки... не слишком реально, да?

 

Ладно, вернемся к командиру  и что он может.

 

Флот

Флот можно строить самому - если вы владелец верфи. А можно и купить за игровую валюту, которую естественно можно заработать различными способами. Тоже самое относится к зданиям. Уже упоминавшуюся верфь можно построить, а можно купить и даже получить в дар от другого командира (в том числе не своего). Касательно последнего варианта: как вы убедили его сделать такой подарок - целиком и полностью ваше личное дело. 

 

Уровень и размер кораблей, которые вы можете строить в своей верфи - зависит от уровня и размеров самой верфи, а также соответствующих способностей командира (кораблестроение, менеджер). Купить можно любой корабль который соответствует его способностям (пилот, командующий флотом). Т.е. это совершенно разные способности. И развиваются они нелинейно - получить двух командиров, один из которых продвинутый кораблестроитель, а второй отличный командующий флотом быстрее и дешевле чем одного владеющего обеими специальностями на том же уровне.

 

Постройки

Чтобы строить нужно иметь специальность "Строитель". Чем выше уровень  - тем большего уровня постройки вам доступны и тем быстрее они строятся. Есть еще дополнительные специализации, которые позволяют повышать свой уровень для определенных видов построек. Например строитель космической инфраструктуры, строитель оборонных сооружений и т.п.

 

Естественно нужны ресурсы - металл, кристалл, деньги, люди... Меня до сих пор отчего-то немного коробит от отношения к людям как к ресурсу, но такова се ля ви, и не нужно от нее удаляться. Скорее наоборот,  не грех напомнить, что само по себе ничего не строится, сколько бы денег у вас не было. И живая сила не самозарождается в грязном белье - на космических базах придется строить и жилые модули

 

Корпорации

Командиров можно объединять в корпорации. Их способности объединяются и корпорация может намного больше чем отдельный командир - во всем что касается строительства, производства и разработки. Единственное чего не может корпорация это управлять флотом. Владеть может, управлять нет. Но об этом позже.

 

Контракты

Это универсальный способ для получения и предоставления услуг, покупки, продажи и т.д. и т.п. В контракте полностью описыватся все его условия и санкции за неисполнение. Исполнение основных условий контролируется программой автоматически. Командир может заключать контракт только если он соответствует его условиям. Еще он может создавать свои, если владеет специальностью юрист. Подробнее опять таки опишу позже.

 

Обучение

Обучение происходит на контрактной основе в постройках типа "институт", "училище" и т.п. Базовая специальность, которой изначально обладает каждый командир - "пилот транспортного корабля", уровень 0. Это значит он может пилотировать небольшие транспортные корабли. Подробнее о классификации кораблей и их типах в другой раз. Отмечу только что в пределах одного класса кораблей можно создавать свои типы, оптимальные для тех, или иных задач.

 

Кроме того, занимаясь деятельность связанной со специальностью командир получает опыт, который позволяет получить следующий уровень со скидкой (или вовсе бесплатно). Т.е. вы можете сразу пойти в космическое училище и получить уровень 1, 2 и т.д., а можете полетать на небольших кораблях и получить следующие уровни за счет опыта. Если вы налетали половину опыта нужного для следующего уровня  - в училище можно получить этот уровень за полцены.

 

Специальности и специализации
предварительный список, некоторые позиции требуется уточнить

  • пилот
  • строитель
    • организация строительства
      • верфь
      • фабрикатор
      • шахты
    • космическое строительство
      • базы
      • платформы
  • управляющий производством
  • энергетик
    • космическая энергетика
    • термоядерные установки
  • шахтер
    • добыча при повышенной гравитации
    • добыча при пониженной гравитации
    • добыча в невесомости
  • ученый
    • прикладные технологии
    • фундаментальные исследования
    • внеземные технологии
  • инженер
    • космические корабли
    • стационарные космические объекты
    • наземные сооружения
  • военный
    • оборонительные сооружения (требуется строитель)
      • монтаж и обслуживание ракетных установок
      • монтаж и обслуживание лазерных установок
      • монтаж и обслуживание гауссовых установок
      • монтаж и обслуживание плазменных установок
      • монтаж и обслуживание силовых щитов
    • военный пилот (требуется пилот)
      • абордажная техника
      • противодействие абордажу
  • юрист

суббота, 29 октября 2011 г.

Чисто программные заморочки

Думаю не всем кто когда-либо будет читать этот блог интересны такие темы, но все же по ходу дела я буду писать и о таких вещах. Если вам не интересно программирование, не разбираетесь и не хотите - просто не читайте.

 

Два дня убил на рефакторинг. Но надеюсь оно того стоило. Хотя по большому счету явно видно что придется перелопачивать все как минимум еще раз. Строго говоря это немного больше чем рефакторинг, но как еще назвать эту работу непонятно. Знаете - подскажите.

На закуску изменил механизм загрузки данных в клиент. Раньше это был обходной маневр через универсальный интерфейс (_._), только чтобы отладить графику, теперь - почти рабочий механизм по синхронизации модели клиента и сервера. Основная же возня была с рефлексией и аннотациями.

 

Надо сказать что и тем и другим я никогда ранее не пользовался и весьма смутно представлял как это работает. И подозреваю что мало кто с этим реально сталкивается, потому что в интернете вопросов на эту тему больше чем ответов, да и вопросов то немного. Пришлось покурить мануалы, а на затылке образовалась мозоль - никак не избавлюсь от привычки чесать репу. Успехи перемежались со зверскими матами в адрес писателей документации - ей богу, иной раз простенький пример объясняет некоторые вещи проще чем килограмм буковок.

 

Нафига козе боян?

Иными словами - зачем мне понадобилась рефлексия и аннотирование? Ну поскольку стандартный механизм сериализации мне не годится, нужно делать что-то свое. У меня уже больше дюжины классов, которые нужно как-то "сериализовать". Это значит при традиционном подходе для каждого класса нужно написать свой метод сохранения и восстановления. При этом не забыть аналогичные вызовы суперклассов если таковые имеются.

И добавляя каждый новый класс - повторять все это снова и снова. И каждый метод нужно отладить.

А если потом я захочу поменять механизм этой "сериализации", то вот тут и начнется самая мартышкина работа. А я ведь обязательно захочу, наверняка со временем появятся новые идеи... И придется опять прошерстить все классы (которых будет уже и не дюжина) и заново все переделать, и отладить.

 

Это и приводит нас к рефлексии и аннотированию. В данном случае одно органично дополняет другое. Как это работает:

я помечаю в своих классах нужные мне поля аннотациями:

@PersistentField, или  @PersistentField(strict=false) или @PersistentField(strict=false, forowner=true)

class Moon {
@PersistentField
double radius;
@PersistentName
double radius;

// А это поле не сериализуем
JComponent view;
}

и создаю интерфейс аннотаций

@Retention(RetentionPolicy.RUNTIME) // Важно! - эта строчка говорит компилятору сохранить данные об аннотациях в 
// в готовой программе
public @interface PersistentField {
boolean strict() default true; // выдавать ли это поле в ограниченной выдаче
boolean forowner() default true; // выдавать это поле хозяину объекта
}

компилятору на это начхать. Но теперь через рефлексию я могу обнаружить эти пометки и сделать для этих полей соответствующую обработку. А сама рефлексия позволяет сделать одну общую на все классы процедуру "сериализации" и соответственно хватит одной на "десериализацию". В них я могу получить список всех полей объекта и надругаться над ними сохранить/восстановить.

Там еще много разных заморочек с тем как обращаться с разными типами данных (примитивные, массивы, и пр.) но все же проще заморочится один раз. Краткий хелп:

Пусть object - это ваш объект черт его знает какого класса 

Class cl=object.getClass() // узнаем его класс

Fields[] flds=cl.getDeclaredFields() // Узнаем какие поля в нём объявлены (выдает все, включая константы, private и static)

Class scl=cl.getSuperclass(); // получаем суперкласс (null = нету) - его поля нам тоже нужны, getDeclaredFields для потомка их не возвращает, нужно делать рекурсию

f.isAnnotationPresent(PersistentField.class) // f это Field. Проверяем - есть ли наша аннотация

PersistentField annotation=f.getAnnotation(PersistentField.class); // получаем аннотацию - из нее можно теперь добыть параметры strict и forowner

f.setAccessible(true); // чтобы менять значение даже в приватных полях

String name=f.getName();  // думаю понятно

Object value=f.get(object);
f.set(object, data); // Легко засунуть данные в объект, если вы имеете их в виде подходящего объекта
Class fdc=f.getType(); // декларированный тип поля.

String data // данные которые вы  хотите засунуть в поле, преобразовав из строки в double, int и т.п.
fdc=primitives.get(fdc.getName()); // primitives это Map. 
// Задает классы обертки для примитивных типов  "int" -> Integer.class "double"->Double.class и т.п. 
//-  придется сделать самим
obj=fdc.getConstructor(new Class[]{String.class}).newInstance(data); //
// Получаем конструктор с параметром типа String (они есть для типов Boolean, Integer,Long,Float,...)
// и вызываем его с нашей строкой.

Для работы с массивами понадобится еще

Class cc=fdc.getComponentType()
Object arr=Array.newInstance(cc, len);
Array.set(arr,i,data); // data - объект уже подходящего типа, см выше как получать

Все остальное - достаточно хорошо описано или очевидно. Вроде isArray isPrimitive и т.п. Курить документацию JDK на предмет Class, Field, Method, Array, Constructor, Annotation

Да... еще мне пригодилось getInterfaces, но нужно иметь ввиду что возвращаются интерфейсы указанные именно в том классе который запрашивается. Т.е если ваш класс реализует некий Interface1, но это было указано в его суперклассе, а не в нем самом, то этот интерфейс нужно искать именно в суперклассе, что совсем не очевидно.

 

среда, 26 октября 2011 г.

Проблемы распределенности

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

 

Но есть одна проблема. Даже не одна, но обо всем по порядку.

 

Разделение модели и рассчитываемых данных

Модель пересчитывается ежесекундно. Каждую секунду, каждая планета, луна, астероид или искуственный спутник немного перемещаются по своим орбитам. Корабли становятся ближе к своим целям и т.д. 2000 звездных систем, сотни (а может и тысячи) объектов в них. Не опухнет ли сервер раскидывать все эти данные по сотням(в мечтах - тысячам) клиентов? Много ли найдется желающих (и имеющих возможность) принимать каждую секунду несколько сотен килобайт данных? 

 

Такой вариант неработоспособен. Но слава богу это и не нужно. Все эти объекты движутся по своим орбитам, параметры которых меняются намного реже (многие - никогда). Клиентская часть вполне способна просчитать текущее положение объекта исходя из одного единственного параметра - текущего времени. Т.е. текущие координаты частью модели не являются и не передаются. Корабли также движутся по траекториям просчитанным заранее, за исключением разве что режима боя.

 

Клиентам должны транслироваться только изменения модели и текущее время. Конкретные координаты объектов в модель не входят и рассчитываются клиентом самостоятельно.

 

Передача обновлений модели

От сотен килобайтов в секунду уже ушли. Но возникает небольшой нюанс. Предположим мы построили новую ракетную установку на орбитальной базе, вращающейся вокруг луны одной из планет и нужно сообщить об этом всем клиентам. Модель иеррахическая, т.е. эта установка входит в список объектов принадлежащих базе, база числится в списках спутников луны и т.д.

Короче при мизерном изменении, по сути формально меняется вся модель и нужно передавать ее целиком (все те же сотни килобайт). Или по крайней мере часть модели включающую все объекты входящие в изменившийся. Т.е. если переименовали луну - передаем объект луна, а в него входят все лунные постройки, спутники и т.д. - хотя они не менялись. А если учесть что постройки могут ссылаться на своих владельцев, которые нифига не входят в объект луна, а совсем из другой части игровой модели... А еще есть корабли, которые хоть и могут в данный момент находится на лунной орбите, но вообще-то тоже не к луне приписаны.

В конечном итоге приходим к тому, что обычный механизм сериализации объекта не годится, потому что для корректной передачи всех этих ссылок нужно сериализовать модель целиком.

 

Либо на каждое возможное изменение нужно иметь свой вызов updateSomething, который будет как-то находить объект требующий изменения. Т.е. много-много мартышкина труда по кодированию всех этих методов. А потом их все надо отладить... а в сутках только 24 часа и нет готовой на все толпы исполнителей. Так что тоже не годится.

Т.е. нужно как-то все это делать по другому, иначе игра не будет сделана в сколько-нибудь обозримом будущем.

 

Обновление объекта не должно  распространяться на объекты лежащие выше, или ниже его в иерархии. Передается только сам объект и ссылки на принадлежащие ему (или как-то связанные с ним) объекты, но при этом не java-ссылки, которые требуют сериализации всех объектов на которые ссылаются, а ссылки "символьные". Которые позволяют найти эти объекты на стороне клиента. Что в итоге выходит

  • все объекты модели должны иметь уникальные id
  • все объекты модели должны иметь метод сериализации, который в отличие от традиционной сохраняет в поток только данные принадлежащие самому объекту и id объектов на которые он ссылается. Либо в программе для доступа к объектам не должны использоваться прямые ссылки, а только символьные - тогда работает обычная сериализация, но кодирование логики превращается в геморрой.
  • при десериализации на другой стороне ссылки на объекты восстанавливаются по их id
  • должна быть карта объектов, все объекты должны в ней регистрироваться при создании и удаляться из нее, если больше не нужны (иначе это блокирует сборку мусора)

Добавим к этому, что в качестве формата сериализованных данных имеет смысл использовать JSON или что-то подобное (а стоит ли изобетать велосипед?) - это облегчит передачу их поверх HTTP. А еще это облегчит задачу изменения модели неигровым путем и проблему изменения версий.

 

Версии ПО и внесение изменений в модель

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

Значит модель должна храниться в текстовом виде, свободно читаться и редактироваться в обычном редакторе и не должна быть жестко связана с версией ПО.

 

Обнаружение обновлений

Предположим что что-то обновилось, сервер разослал уведомления клиентам, но что-то произошло, и какой-то из клиентов уведомления не получил. Связь прервалась, по причине пятен на солнце или еще какая засада. Клиент  - не знает что он должен был что-то получить. И не спросит. Серверу - накладно требовать от клиентов подтвержления и делать повторные передачи. Где выход?

Выход есть.

  • Все объекты должны иметь время последнего обновления.
  • Перед запросом данных от сервера клиент пробегает по карте объектов и находит самый свежий объект. Это время будет отправлено серверу
  • Сервер должен переслать все объекты имеющие более свежее время. Строго обязательно в хронологическом порядке.
  • Если объекты не влезают все в один ответ, то ждем следующего обращения. По собственной инициативе сервер ничего не передает - иначе возможны потери.

Последний пункт требует разъяснений.

Предположим мы передаем по 3 объекта за раз.

на клиенте у нас объект 1

на сервере 123456789

клиент сообщает серверу: 1

сервер посылает 234 567 89

567 - потерялись

на клиенте 123489

клиент сообщает серверу 9

про 567 он так и не узнает.

Первое приближение

Вот чего у меня получается на текущий момент. Видны 4 внутренних планеты нашей Солнечной системы, само солнышко и кусок пояса астероидов на фоне далеких звезд. Все медленно, но верно вращается, работает увеличение/уменьшение масштаба и перемещение камеры. У Земли видна луна, луны Марса мелкие и при таком масштабе не видны, но если приблизить - тоже проявятся. Если отдалить или передвинуть камеру, можно увидеть и остальные планеты со спутниками.

Сделана регистрация и вход (не показано). Все на данный момент в виде standalone клиента, который общается непосредственно с сервером, но через унифицированный интерфейс, который позволяет без проблем вставить посередине что-то еще. Т.е. от идеи GAE  в качестве кэширующего прокси не отказываюсь.

Вот так на текущий момент выглядит визуальная модель

Все разумеется совершенно сырое, даже заголовок окна прозевал, остался от какой-то демки.

Потом разумеется над графикой придется еще поработать и не мало, но пока сосредоточусь на игровой логике, ну и сопутствующих элементах пользовательского интерфейса.

воскресенье, 23 октября 2011 г.

Реалистичное изображение солнечной системы и полетов

Маленькая проблема при попытке реалистичного изображения солнечной системы - несопоставимый масштаб объектов. Планеты настолько меньше солнца и расстояний между ними, что при изображении в реальном масштабе вы просто ничего не видите.

 

Тут очень доходчиво описана суть проблемы.

Технологическая сингулярность

По мере развития, компьютерные (и не только) технологии становятся все сложнее и сложнее, все меньшее количество людей способны в них разобраться (при этом их использует все большее количество людей, которые вообще в них ничерта не понимают), количество ошибок растет, а количество тех кто их исправляет (или хотя бы способен на это) уменьшается. Чем это закончится?

 

Виснет мобильник - фигня. А если бабахнет какая-нибудь дура вроде большого адронного коллайдера? Из-за того что не осталось людей способных обнаружить ошибку в коде?

 

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

Компании вводят новые технологии, вместо того чтобы довести до ума старые...

 

Рано или поздно.

 

Это закончится.

 

Будет ли после этого жизнь? Хотя бы на Марсе? Я не знаю.

О чем все это

Когда-то давным давно я немного поиграл в Galaxy Plus. Увы, лишившись доступа к интернету, мне не удалось доиграть до конца... Эта игра существует и сейчас, но на мой взгляд она морально устарела. Или у меня изменился темперамент и даже несколько ходов в сутки вряд ли ему соответствуют.
 
Сейчас я играю на battlespace.ru, но  и там медительность некоторых процессов слегка раздражает. А убогий интерфейс раздражает неимоверно. Когда на дворе 11ый год 21ого века обновлять страницу чтобы получить отчет... А тем паче не уведомить игрока НЕМЕДЛЕННО что его атакуют... В то время как про AJAX даже глухой, если не слышал, то по крайней мере читал. Есть конечно же еще ogame, где с этим слегка получше - про AJAX они хоть и краем уха, но слышали и даже, возможно, читали. Но даже этого уже мало...
 
Ну а посмотрев видео о EVE вообще чувствуешь, что тебя обманули. Привезли на какое-то болото и сказали что это море. Купаешься и удивляешься - а почему это все едут куда-то мимо и дальше. А потом подымаешься на холм и прозреваешь - вот оно! Настоящее-то море...
 
И так обидно что эта замечательная игрушка на моей убунте не работает. Даже через wine. Даже через, черт его дери, VirtualBox! При этом нет там ничего такого сверхестественного, что категорически непозволяло бы сделать клиент под Linux, а вот поди ж ты. Не стали.
 
И из всего этого родилась мысль, что не боги ж горшки обжигают... и пусть вторую EVE мне не сделать, но уж galaxy, battlespace и ogame я переплюну. Не от того что я такой хороший программист, скорее от того что их создатели расслабились и почивают на лаврах, не желая шагать в ногу со временем.
 
Есть разумеется проблема - нифига я не художник и тем паче не дизайнер... Уж программную часть я осилю, а в этом конкретно слабоват. Но с другой стороны - слона ведь едят по кусочкам? До того чтобы эта проблема стала насущной еще ой как далеко.
 
Итак - я обладаю временем и некоторыми навыками в программировании. Задача - создать игру которую мне не стыдно будет выложить в общий доступ. Она должна быть кроссплатформенной (Lin,Win,Mac + Android), многопользовательской, с 3D графикой. Жанр - космическая стратегия MMORPG.

 

Первая часть задачи - выбор технологии. Задача не такая тривиальная, варианты есть.