Три гілки продукту і контроль версій

В 2015 році, розробляючи браузерну онлайн-гру, ми зіткнулися з наступною завданням: зробити другий сервер з іншими налаштуваннями і механіками гри, а також вести перспективну розробку нової версії гри, в якій буде ще більше змін, при цьому загальний каркас у всіх версій гри однаковий. Справа ускладнюється тим, що грі потрібні постійні оновлення та виправлення, т. к. ігровий процес йде і зупиняти його з-за розробки паралельної версії ніяк не можна.

Ми використовуємо SVN в якості системи контролю версій, і теорія говорить, що в нашому випадку резонно робити гілки (branch), а потім їх хитро мержить. Ну або робити fork продукту в новий репозиторій, і розвивати нову версію окремо. Але ми пішли своїм шляхом, який біса зручний, і може бути корисним читачам, про нього і моя розповідь.

Крок 1. Беремо статичну таблицю в БД (state), де зберігаються різні параметри стану сервера, наприклад ігрова дата, і додаємо поле server_type

Крок 2. Робимо статично методи, якими будемо визначати тип сервера

public static boolean isRoundServerType() {
return serverType == SERVER_TYPE_ROUND || serverType == SERVER_TYPE_EVOLUTION;
}

public static boolean isEvolutionServerType() {
return serverType == SERVER_TYPE_EVOLUTION;
}

Крок 3. Готуємо методи для веб-інтерфейсу, щоб визначати тип сервера:

public boolean getRoundServer() {
return State.isRoundServerType();
}

public boolean getEvolutionServer() {
return State.isEvolutionServerType();
}

Крок 4. В усі місця бізнес-логіки вставляємо блоки типу:

public String[] getProcessTimes() {
if (State.isEvolutionServerType()) {
String[] times = { "09.00", "11.00", "13.00", "15.00", "17.00", "19.00", "21.00" };
return times;
} else if (State.isRoundServerType()) {
String[] times = { "08.00", "10.00", "12.00", "14.00", "16.00", "18.00", "20.00" };
return times;
} else {
String[] times = { "09.30", "11.30", "13.30", "15.30", "17.30", "19.30", "22.30" };
return times;
}
}

Крок 5. В усі місця веб-інтерфейсу вставляємо перевірку на доступність елемента інтерфейсу.

<item name="corporationtotal" caption="Вплив корпорацій" href="/corporation/controltotal/" hide="game.evolutionServer"/>
<item name="citytotal" caption="Вплив корпорацій" href="/corporation/citycontrol/" show="game.evolutionServer"/>

Тут треба зробити поправку на те, що ми використовуємо самописний фреймворк, який дозволяє приховувати елементи двох видів правил:show/hide — де безпосередньо обчислюється значення якого поля, allow/deny — тоді у користувача, який залягання запитуються права на якісь дії. Останнє правило дає можливість вести розробку прямо в основній гілці репозиторію, просто приховуючи новий функціонал правами наприклад:

<button icon="adddoc" caption="Додати новий місто" action="javascript:dialog('/intercitynew/')" allow="admin:gamemanager"/>

Розвиваючи тему далі можна робити аналогічним чином спліт тестування механік в різних частинах ігрового світу просто підміняючи імплементацію старої логіки на нову.


if (RetailFormula.isEvolutionRetail(currentCity.getId().toInteger())) {
house = new House4(row, date);
}
else {
house = new House2(row, date);
}

Свого роду dependency injection, однак є незаперечні переваги за рахунок гнучкого і прозорого варіанта параметрів алгоритму.

Код виходить трохи пухкенький, зате має мінімальні витрати на підтримку всієї системи, оскільки фактично 1 прапорець в БД змінювати логіку роботи сервера. Досвід показав, що особливих проблем таке співжиття версій не викликає, а перспективні розробки — помітно прискорюються.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.