Руководство по миграции
На этой странице собраны шаги, которые хосту нужно выполнить, когда релиз адаптера ломает совместимость. Релизы без записи здесь ставятся без изменений: поднимите версию и перезапустите сервер.
Как объявляются breaking changes
Заголовок раздела «Как объявляются breaking changes»- У каждого модуля своя версия (
core/v…,api/v…,db-sync/v…), и каждый релиз появляется в changelog со своими notes. - Релиз, который требует действий от хоста, получает раздел на этой странице, новые сверху: какой модуль и версия, что изменилось и что делать.
- Документация всегда описывает текущую версию.
nx-gs-adapter-api 0.125.0, nx-gs-adapter-core 0.54.0: APP_BAIT_LEVEL_MISMATCH, APP_NO_ROD
Заголовок раздела «nx-gs-adapter-api 0.125.0, nx-gs-adapter-core 0.54.0: APP_BAIT_LEVEL_MISMATCH, APP_NO_ROD»Только добавления: хосту, который не отправляет ни одну из этих причин, ничего менять не нужно. Берите оба модуля
вместе: новые причины от NxAppSessions.onActionEnded принимает core.
- Новая причина завершения
LogoutReason.APP_BAIT_LEVEL_MISMATCH: выбранная наживка ушла из руки, потому что уровень персонажа вышел из её диапазона. Стопка наживки при этом ещё в инвентаре; если стопки нет, причина остаётсяAPP_OUT_OF_BAIT. - Новая причина завершения
LogoutReason.APP_NO_ROD: удочка ушла из рук, её условия перестали выполняться или её больше нет. APP_INTERRUPTEDбольше не означает потерю снаряжения. Это игра остановила действие поставленного персонажа: его сдвинули со спота, игра закрыла лавку или действие не удалось возобновить. Лавка из приложения, которую закрыла игра, завершается с этой причиной. Хост на старом core до перезапуска отправляетAPP_INTERRUPTEDи для потери снаряжения.- Core 0.54.0 принимает от хоста
APP_TIME_UPдля сессии без плана: лавки из приложения, у которой закончилось оплаченное оффлайн-время. Сессия с планом завершается собственным таймером движка, и для неё coreAPP_TIME_UPотклоняет. Старые core игнорируютAPP_TIME_UPот хоста. - Отправляйте новую причину только когда платформа её принимает: новая причина сначала появляется в адаптере, затем на платформе, затем в хосте.
nx-gs-adapter-api 0.124.0, nx-gs-adapter-core 0.53.0, nx-gs-runtime-sync-core 0.6.0, nx-gs-db-migrations 0.6.0: результаты сессий, снимки предметов, старт runtime-синхронизации
Заголовок раздела «nx-gs-adapter-api 0.124.0, nx-gs-adapter-core 0.53.0, nx-gs-runtime-sync-core 0.6.0, nx-gs-db-migrations 0.6.0: результаты сессий, снимки предметов, старт runtime-синхронизации»Ломающее изменение. Берите все четыре модуля вместе. Хост переходит на него в том релизе, который его берёт.
Вызовите NxAdapter.runtimeSync().hostStarted(), иначе runtime-синхронизация ничего не отправит. Вызов один, сразу
после публикации ServerStartedEvent, ушло событие или ждёт привязки канала событий. Таймера и запасного пути нет:
хост, который не вызвал метод, тиков не получит. NxAppSessions.hostReady() вызывается раньше него.
CharacterPresenceEvent.charIdтеперьcharacterId, и в билдере, и на проводе.- Все
charIdи*CharIdв api теперьcharacterIdи*CharacterId(buyerCharIdсталbuyerCharacterId,targetCharIdсталtargetCharacterId): команды, результаты, события иRatingDbDto. Меняются и билдер, и провод. charNameиtargetCharNameтеперьcharacterNameиtargetCharacterName, вChatMessageEventиTelegramCharacterLinkCommand. Меняются и билдер, и провод.TransferCharToAccountCommandтеперьTransferCharacterToAccountCommand,TelegramCharLinkCommandтеперьTelegramCharacterLinkCommand, вместе с классами*Result. Команды маршрутизируются по простому имени класса, поэтому меняется иNx-Message-Type: зарегистрируйте обработчик на новый класс, отправитель старого имени получитUNSUPPORTED_COMMAND.CharacterPresenceEventпринимаетresult(SessionResult), только приonline = false. Клиентская сессия задаёт его через билдер, app-сессия получает от движка.activityприsource = CLIENTразрешён только какOFFLINE_PRIVATE_STORE, сессия нативного офлайн-магазина.OfflineSessionHost.takeSessionResult(session)— новыйdefault-метод, отвечающийnull. Переопределите его, чтобы передавать результаты. Движок вызывает его один раз после каждогоremoveFromWorld, включая откат старта и персонажа, которого не удалось разместить, так что recorder, хранимый по id сессии, можно отпустить здесь, кроме случая, когда конец гонится со штатной остановкой, уже сохранившей блок: такой конец вызов пропускает. Вызов идёт и при штатной остановке для каждой оставшейся открытой сессии.OfflineSession.getCarriedResult()содержит текущий блок после штатной остановки, в тех представлениях, чтоplaceInWorldиstartActionполучают при восстановлении. Заполните им recorder. Пустой блок значит, что записывать было нечего;null— что блок потерян, и завершение придёт сpartial.- Результат больше
SessionResult.MAX_JSON_BYTESотбрасывается, завершение уходит без него: до этого предела сворачивайтеuniquesвitems. - Новые значения:
LogoutReason.SOLD_OUTиAPP_OVERWEIGHT(второе принимаетonActionEnded), отказыNO_FISHING_EXPERTISE,BAIT_LEVEL_MISMATCH,INVENTORY_FULL,OVERWEIGHT.INVENTORY_FULLиOVERWEIGHTнесут свои числа вCommandProblem.extensionsи вAppSessionCheckResult, где они задаются через билдер.APP_INVENTORY_FULLбольше не покрывает вес. - Формы предметов несут
ItemSnapshotвitemвместо отдельных полей, команды предметов используютcharacterId. Проверка покупки сравниваетBuyLine.attributesForMatch(): пустая карта считаетсяnull. - В
ConnectContextпоявилсяruntimeSync(): собирайте его билдером. - Новый ключ
app.shutdown.result-millis-per-sessionвl2nx-app.propertiesи миграция, добавляющая столбец в таблицу сессий адаптера; применяется при старте.
nx-gs-adapter-api 0.123.0, nx-gs-adapter-core 0.52.0: словари чата как enum, настройки чата, доставка шёпота
Заголовок раздела «nx-gs-adapter-api 0.123.0, nx-gs-adapter-core 0.52.0: словари чата как enum, настройки чата, доставка шёпота»Ломает совместимость для Java-кода (на уровне исходников); строки на проводе не меняются. Классы констант чата
стали enum с теми же именами на проводе, и три конструктора получили новый параметр. Хост переключается в релизе,
который берёт этот api: ни одно сообщение не меняет форму, поэтому платформа, читающая старые строки, читает новый хост
как раньше. С обновлением core чат с приложением по умолчанию выключен: хост, который публикует игровой чат и
обрабатывает SendChatMessageCommand, ставит app.chat.enabled=true в своём l2nx-app.properties, чтобы сохранить
текущее поведение.
Переименованные типы (без deprecated-копий):
| Было | Стало | Примечание |
|---|---|---|
kafka.events.chat.WellKnownChatChannels |
kafka.events.chat.model.ChatChannel |
те же имена констант; ChatChannel.unknownToken(rawType) строит UNKNOWN_<int>, ChatChannel.fromToken читает токен (null, если неизвестен) |
kafka.events.chat.ChatMetadataKeys |
kafka.events.chat.model.ChatMetadataKey |
ключ в map — key() (source, shadowed, rawType, serverWide, undelivered), не name() |
kafka.commands.chat.ChatAudiences |
kafka.commands.chat.model.ChatAudience |
те же имена констант |
ChatMessageEvent.channel, SendChatMessageCommand.channel и .audience остаются строками; их билдеры принимают и
enum. Код, который клал строковую константу в metadata, теперь пишет ChatMetadataKey.X.key().
- Причины отказа. Новый
SendChatMessageRefusalReason, пишется под ключомSendChatMessageRefusal.REASON("reason"):CHAT_FORBIDDEN(FORBIDDEN),NOT_IN_CLAN,NOT_IN_ALLIANCE,NOT_IN_PARTY(INVALID_STATE),TARGET_NOT_FOUND(NOT_FOUND),ITEM_NOT_OWNED(VALIDATION_FAILED) и новыеCHAT_DISABLED,CHANNEL_DISABLED,TARGET_OFFLINE(INVALID_STATE). Строки те же, что хосты уже отправляют: замените свои константы причин наname()этого enum. - Настройки чата. Новые члены
AppRules:chatEnabled(),chatOnlineSendChannels()иchatOfflineSendChannels(), и ключиapp.chat.enabled(по умолчаниюfalse),app.chat.online-send-channels(по умолчаниюCLAN,ALLIANCE,PARTY,WHISPER) иapp.chat.offline-send-channels(по умолчаниюCLAN,ALLIANCE,WHISPER), см. конфигурацию. С выключенным чатом хост не публикует игровой чат и отклоняет любую отправку от персонажа сCHAT_DISABLED; команда безsenderCharacterId(объявление, строка от имени стаффа) — не чат с приложением и уходит как раньше. Отправка в канал вне списка для текущего состояния персонажа отклоняется сCHANNEL_DISABLED; список хост выбирает по тому, в игре ли персонаж в момент выполнения команды. - У
AppSettingsпоявилось полеchat(ChatSettings), новый предпоследний параметр конструктора: код, который сам собираетAppSettings, передаётnullилиChatSettings. - У
SendChatMessageCommandпоявилось полеofflineDelivery(Boolean), последний параметр конструктора (послеinitiator).false:WHISPERадресату, который не в игре, отклоняется сTARGET_OFFLINE, ничего не публикуется;null(более старая платформа) читается какtrue. - У
SendChatMessageResultпоявилось полеtargetInGame(Boolean), последний параметр конструктора. Заполняйте его на каждомWHISPER:true, когда пакет ушёл в игровой клиент адресата,false, когда адресат не в игре и сообщение ушло только в приложение; на других каналах оставляйтеnull. Платформа берёт доставку из него, а не изrecipients. - Новый
ChatMetadataKey.UNDELIVERED: хост ставит"true"вChatMessageEventшёпота, который не дошёл ни до одного игрового клиента адресата (шёпот из игры наName*или отправка из приложения адресату, который не в игре).
nx-gs-adapter-api 0.122.0: слоты частного магазина покупки в runtime-синхронизации
Заголовок раздела «nx-gs-adapter-api 0.122.0: слоты частного магазина покупки в runtime-синхронизации»Ломает совместимость (на уровне исходников). Канонический конструктор CharacterRuntimeDto получает новый
последний параметр privateStoreBuySlots (Integer): максимальное число строк, которое частный магазин покупки
персонажа может держать сейчас, независимо от privateStoreSellSlots. Провайдер считает его по тем же правилам, что и
лимит продажи, и передаёт null, если не сообщает его. Код, который сам собирает CharacterRuntimeDto (провайдеры,
тесты), добавляет аргумент.
nx-gs-adapter-api 0.120.0, nx-gs-adapter-core 0.51.0, nx-gs-gd-sync-core 0.16.0, nx-gs-db-migrations 0.5.0: инициатор команды, проверки сессий, рыболовное снаряжение
Заголовок раздела «nx-gs-adapter-api 0.120.0, nx-gs-adapter-core 0.51.0, nx-gs-gd-sync-core 0.16.0, nx-gs-db-migrations 0.5.0: инициатор команды, проверки сессий, рыболовное снаряжение»Ломает совместимость. Обновите api, core и gd-sync вместе и переключите хост одним релизом.
- Инициатор команды. Каждая команда несёт обязательный
initiator(CommandInitiator): кто её вызвал (source:userили имя отправившего сервиса платформы;userIdзадан ровно для пользователя; необязательныеlabel,email,ip). УNxCommandестьgetInitiator(), его принимают все конструкторы и билдеры команд. Core отклоняет команду без корректного инициатора сVALIDATION_FAILEDдо вызова вашего обработчика, а командеOwnerVerifiedнужен ещё источникuser, поэтому обработчик читает корректный инициатор и не перепроверяет его. Егоipуже прошёлClientIdentifiers. Код, который сам собирает команды (тесты, утилиты), передаёт инициатор. - Удалены
masterAccountIdиinitiatorIpизStartOfflineFishingCommand,StopOfflineFishingCommand,ReviveCharacterCommandи команд старта частного магазина (StartPrivateStoreCommand), deprecated-копии нет. Мастер-аккаунт читайте изinitiator.getUserId(), IP запроса изinitiator.getIp()и передавайте их вAppSessionStartRequest, как раньше.BanCommand.issuedByиSendCaptchaCommand.issuedByостаются: это имя, которое запись показывает в игре, а не атрибуция. - Перенос пакета.
AppSessionStartRequest,AppSessionStartOutcome,ClientLoginOutcome,OfflineSessionиOfflineSessionPlacementпереехали изapp.l2nx.gs.adapter.api.spi.modelвapp.l2nx.gs.adapter.api.spi.model.session. Замените импорты; больше в них ничего не изменилось. - Проверки сессий.
NxAppSessions.check(characterId, spotPool, spotId)отвечаетAppSessionCheckOutcome(ALLOWED,CHARACTER_BUSYс открытой сессией,SPOT_TAKEN,UNAVAILABLE), ничего не записывая, не занимая спот и не вызываяOfflineSessionHost. НоваяCheckOfflineFishingCommand(результатAppSessionCheckResult) спрашивает, стартовала бы рыбалка с этими входными данными сейчас: пройдите свои шаги старта до первого побочного эффекта и вызовитеcheckтам, где старт вызываетstart. Отказ — успешная проверка (OK,allowed = false). Пока вы не зарегистрировали обработчик, адаптер отвечает на команду как на неподдерживаемую. Код, который сам реализуетNxAppSessions, добавляетcheck. - Новые значения отказов.
AlreadyActiveScope.IP(ваш лимит по IP, считается поinitiator.ip; отсутствующий IP ничего не ограничивает) и значенияAppSessionRefusalReasonNO_FISHING_SHOT,INVALID_FISHING_SHOTиFISHING_SHOT_GRADE_MISMATCHдля нового необязательногоStartOfflineFishingCommand.fishingShotItemId. ОтказDAILY_LIMITтеперь может нестиretryAt— момент сброса суточного лимита; если ваша сборка этот момент не знает, не передавайте его. ActivityMetadataKeyназывает ключиmetadataстроки activity usage (elapsed,remaining,rodItemId,baitItemId,fishingShotItemId,spotPool,spotId); пишитеkey()вместо своих строк. Словарь остаётся открытым.- Снимок открытых сессий. Core публикует
AppSessionsSnapshotEvent(id всех открытых сессий приложения сервера) раз в 60 с и при старте модуля app-sessions и переотправляет завершение сессии, пока платформа его не подтвердит. На хосте поднимитеnx-gs-db-migrationsдо 0.5.0 вместе с core: нужную колонку добавляют его changeset-ы. - Каталог рыболовного снаряжения. Новые провайдеры игровых данных
FishingBaitProvider,FishingRodProviderиFishingShotProvider(сущностиfishingbait,fishingrod,fishingshot). Необязательны: сервер без рыбалки не регистрирует ни одного. Хост, который их реализует, регистрирует каждый вMETA-INF/servicesи возвращаетnullизsnapshot(), пока его данные не загружены. - Пустой снимок игровых данных теперь удаляет. Зарегистрированный провайдер, вернувший пустую коллекцию (или
пустой
OptionalизGearScoreRulesetProvider), заставляет платформу удалить все строки этой сущности для сервера. Пока данные не загружены, возвращайтеnull, а не пустую коллекцию.
nx-gs-adapter-api 0.119.0: clanId и allianceId в сообщениях чата
Заголовок раздела «nx-gs-adapter-api 0.119.0: clanId и allianceId в сообщениях чата»Ломает только Java-код. ChatMetadataKeys.CLAN_ID и ChatMetadataKeys.ALLIANCE_ID удалены, deprecated-копии нет.
У ChatMessageEvent появились поля clanId и allianceId (Long, nullable) на каждом канале; конструктор принимает
их двумя новыми последними параметрами, а у билдера есть clanId(...) и allianceId(...).
- Хост, который писал id клана и альянса в
metadataсобытия, задаёт вместо этого два поля и перестаёт писать эти ключи: платформа больше не читает их изmetadata. - Поля хранят клан и альянс говорящего на момент реплики. Сообщение без законного отправителя несёт адресата: на
CLANего клан и альянс, наALLIANCEтолько альянс. Нет клана или альянса:null. - Код, который вызывает конструктор
ChatMessageEventнапрямую, передаёт в новые параметрыnullлибо id.
nx-gs-adapter-api 0.119.0, nx-gs-adapter-core 0.50.0: возрождение мёртвого персонажа
Заголовок раздела «nx-gs-adapter-api 0.119.0, nx-gs-adapter-core 0.50.0: возрождение мёртвого персонажа»Только добавления: хосту, который не обрабатывает новую команду, менять ничего не нужно. Пока обработчика нет,
платформа не предлагает возрождение для сервера (публикуемые настройки приложения не несут группы revive), а более
старый хост отвечает на команду как на любую неизвестную.
- Новые
ReviveCharacterCommand/ReviveCharacterResult: поднимают мёртвого персонажа в точке возрождения (TOWN) при подключённом или уже закрытом клиенте. Хост, которому нужна функция, регистрирует для неё обработчик и отказывает кодомReviveRefusalReason, когда возрождение невозможно. - Новые члены
AppRules:reviveEnabled(),reviveTownMode()иreviveTownSpotPools(), и ключиapp.revive.enabled(по умолчаниюtrue),app.revive.town.mode(по умолчаниюNATIVE, либоSPOT_POOL) иapp.revive.town.spot-pool, см. конфигурацию. Файл сервера без них продолжает работать. УAppSettingsпоявилось полеrevive; публичный конструктор принимает его новым параметром, поэтому код, который сам собираетAppSettings, передаётnullилиReviveSettings.
nx-gs-adapter-api 0.117.1, nx-gs-adapter-core 0.49.1: LifeStoneGrade.JEWELRY, GameSettingsProvider
Заголовок раздела «nx-gs-adapter-api 0.117.1, nx-gs-adapter-core 0.49.1: LifeStoneGrade.JEWELRY, GameSettingsProvider»Ломает только Java-код. Константа ACCESSORY в LifeStoneGrade теперь JEWELRY (слоты шеи, ушей и пальцев);
deprecated-копии нет. Хосту, который собирает данные life stone, достаточно заменить константу; грейд уходит по имени,
поэтому ещё не обновлённый хост продолжает слать ACCESSORY до пересборки.
Только добавления: необязательный GameSettingsProvider (см. api)
публикует конфигурацию движка (лимиты заточки, лимиты атрибутов, плата за почту, плата за офлайн-лавку). Хост без него
работает как раньше и не публикует игровые настройки; хост, который его реализует, регистрирует его в
META-INF/services и возвращает null из gameSettings(...), пока конфигурация не загружена. Две зарегистрированные
реализации роняют модуль.
nx-gs-adapter-api 0.116.0, nx-gs-gd-sync-core 0.15.0: StatModifier, каталог аугментации
Заголовок раздела «nx-gs-adapter-api 0.116.0, nx-gs-gd-sync-core 0.15.0: StatModifier, каталог аугментации»Ломает только Java-код. SkillStatModifier (пакет app.l2nx.gs.adapter.api.kafka.sync.gd.skill.effect) теперь
StatModifier в app.l2nx.gs.adapter.api.kafka.sync.gd: его используют и скиллы, и опции предметов. Поля, геттеры,
билдер и JSON не изменились, deprecated-копии нет. Хосту, который собирает данные скиллов, достаточно заменить импорт и
имя класса.
Только добавления: три новых провайдера игровых данных, ItemOptionProvider, AugmentationTargetGroupProvider и
LifeStoneProvider (сущности itemoption, augmentationtargetgroup, lifestone). Хост без них работает как раньше;
хост, который их реализует, регистрирует каждый в META-INF/services, как остальные провайдеры, и возвращает null из
snapshot(), пока его данные не загружены. Обновляйте nx-gs-gd-sync-core вместе с api: публикует их этот модуль.
nx-gs-adapter-api 0.115.0, nx-gs-adapter-core 0.48.0: открытые сессии типа, APP_INTERRUPTED
Заголовок раздела «nx-gs-adapter-api 0.115.0, nx-gs-adapter-core 0.48.0: открытые сессии типа, APP_INTERRUPTED»Только добавления: хосту, который не использует ни одно из них, ничего менять не нужно.
- Новое
NxAppSessions.findOpen(ActivityType activity): все сессии этого типа, которые держит адаптер (идущие, ждущие восстановления после рестарта, старт в процессе, завершение, которое ещё записывается), поstartedAt, затем по id. Читает только память, поэтому вызывать можно из любого потока; без работающего движка сессий отвечает пустым списком. Вызовите его прямо передhostReady(), чтобы учесть сессии, ждущие восстановления, в своих лимитах на аккаунт и на устройство и исключить их споты (OfflineSession.getSpotPool(),getSpotId()) из выбора свободного спота. - Новая причина завершения
LogoutReason.APP_INTERRUPTED, её принимаетNxAppSessions.onActionEnded: игра остановила действие поставленного персонажа (его телепортировали, пропал инструмент или расходник, без которого действие не идёт, действие не удалось перезапустить). Используйте её вместоKICKED(кик стаффом) иAPP_STOPPED(остановка игроком). Отправляйте её только когда платформа её принимает: новая причина сначала появляется в адаптере, затем на платформе, затем в хосте.
nx-gs-adapter-api 0.114.0, nx-gs-adapter-core 0.47.0: титул по умолчанию у поставленного персонажа
Заголовок раздела «nx-gs-adapter-api 0.114.0, nx-gs-adapter-core 0.47.0: титул по умолчанию у поставленного персонажа»У app.phantom.title.text теперь значение по умолчанию l2nx.app, а не «без титула». Если ключа в файле сервера нет,
над персонажами, которых приложение поставило в мир, виден l2nx.app. Чтобы оставить персонажу его собственный титул,
задайте пустое значение (app.phantom.title.text=). Хостам код менять не нужно.
nx-gs-adapter-api 0.112.0, nx-gs-adapter-core 0.46.0, nx-gs-db-migrations 0.4.0: кулдаун старта частного магазина
Заголовок раздела «nx-gs-adapter-api 0.112.0, nx-gs-adapter-core 0.46.0, nx-gs-db-migrations 0.4.0: кулдаун старта частного магазина»Что изменилось. Частный магазин из приложения больше нельзя сразу перезапустить, чтобы сбить цены: новый старт персонажа ждёт несколько минут после фактического старта предыдущего магазина этого персонажа из приложения. Адаптер несёт правило и данные, соблюдает его хост.
- Новый ключ
app.private-store.start-cooldown-minutesвl2nx-app.properties(по умолчанию5,0отключает правило,0-1440; см. конфигурацию) и его типизированное значениеAppRules.privateStoreStartCooldownMinutes(). - Новый
NxAppSessions.findLastStartedByCharacter(int characterId, ActivityType activity): сессия персонажа этого типа с самым поздним фактическим стартом, открытая или закрытая. Правила те же, что у чтения по мастер-аккаунту: старты, которые на деле не состоялись, не считаются. - Переименование:
NxAppSessions.findLastStarted(UUID, ActivityType)теперь называетсяfindLastStartedByMasterAccount(UUID, ActivityType); устаревшая копия не сохраняется. - Оба чтения завершают future исключением при ошибке базы, а также пока движок сессий адаптера не работает (ещё
загружается, выключен или не запущен). До этого релиза чтение по мастер-аккаунту в таком случае отвечало пустым
Optional. - Новый код отказа
PrivateStoreReason.COOLDOWN(INVALID_STATE, сretryAt). - Снимок настроек
AppSettings: новое полеprivateStoreStartCooldownMinutes, аstartCooldownMinutesпереименовано вfishingStartCooldownMinutes(геттер и метод builder тоже). Конструктор принимает новое поле, поэтому код, собирающийAppSettings(в том числе тесты), пользуется builder. - Новый changeset
0010вnx-gs-db-migrations: индекс таблицы сессий адаптера для чтения по персонажу. Адаптер применяет его при старте, как и прежние changeset.
Что делать.
- В каждом старте частного магазина из приложения (продажа, пакетная продажа, покупка), если
NxAppRules.current().privateStoreStartCooldownMinutes()больше0, прочитайтеfindLastStartedByCharacter(characterId, ActivityType.OFFLINE_PRIVATE_STORE)и отклоните старт сCOOLDOWNиretryAt= егоstartedAtплюс кулдаун, пока этот момент не наступил. Ждите future в своём потоке команды, никогда не в IO-пуле адаптера. - Ошибка чтения отклоняет старт как временно недоступный, а не как «предыдущего магазина нет».
- Никогда не применяйте кулдаун к остановке: закрыть магазин можно всегда.
- Переименуйте вызовы
findLastStarted(masterAccountId, activity)вfindLastStartedByMasterAccount, а обращения кstartCooldownMinutesуAppSettingsвfishingStartCooldownMinutes. - Поднимите
nx-gs-db-migrator-liquibase5до 0.2.2: он приноситnx-gs-db-migrations0.4.0 (changeset 0010).
nx-gs-adapter-api 0.111.0, nx-gs-adapter-core 0.45.0: файл настроек приложения
Заголовок раздела «nx-gs-adapter-api 0.111.0, nx-gs-adapter-core 0.45.0: файл настроек приложения»Что изменилось. Настройки приложения сервера теперь принадлежат адаптеру: файл l2nx-app.properties (см.
конфигурацию), проверяется целиком и перечитывается без
перезапуска. Хост больше не поставляет и не публикует настройки. Хост, который реализует OfflineSessionHost или
вызывает NxAppSessions, должен адаптироваться в том же релизе, что поднимает api и core:
OfflineSessionHost.settings()удалён; удалите реализацию.OfflineSessionSettingsудалён. Его значения теперь ключи файла: порог возврата, срок хранения, размер пачки восстановления и пауза.NxAppSessions.publishSettingsудалён: настройки публикует сам адаптер. Перестаньте его вызывать и уберите код хоста, собиравшийAppSettings.- У
OfflineSessionновое nullable-полеreturnThresholdMs, фиксируемое при старте сессии; флагreturnToOriginвremoveFromWorldсчитается по нему. - Новое:
NxAppSessions.findOpenByMasterAccountиfindLastStarted, а такжеNxAppRules(NxAdapter.appRules(),ConnectContext.appRules(),CommandContext.appRules()) для типизированных правил.
Что делать.
- Удалите
settings()из своегоOfflineSessionHostи вызовыpublishSettings. - Создайте
l2nx-app.propertiesрядом сl2nx.properties, взяв за основу эталонный файл, и перенесите в него прежние значения настроек. Все типы сессий по умолчанию выключены: включите те, что предлагает сервер. - Читайте правила (кулдауны, длительности, окна, пул точек) из
NxAppRules.current()на каждой проверке, а не из собственной конфигурации. - Если вы сами создаёте
OfflineSession(в том числе в тестах), передавайте новыйreturnThresholdMs.
Изменения схемы (nullable-колонка и индекс в таблице сессий адаптера) применяет мигратор адаптера при старте.
nx-gs-adapter-api 0.110.0: контракт частных магазинов
Заголовок раздела «nx-gs-adapter-api 0.110.0: контракт частных магазинов»Что изменилось. Команды и события частных магазинов переработаны. Хост, который обрабатывает команды
StartPrivateStore*, отказывает в открытии магазина или покупке либо читает события покупки, должен адаптироваться в
том же релизе, который обновляет api:
- Новая команда
StartPrivateStoreBuyCommandоткрывает магазин скупки; её строки —WantedLine(itemTemplateId,count,priceAdena).StartPrivateStoreSellCommandиStartPrivateStorePackageSellCommandтеперь наследуют новыйStartPrivateStoreSellingCommand, в которомgetLines()иgetSellerFeePercent(); в общей базеStartPrivateStoreCommandих больше нет. - У
DroppedLineпоявился обязательныйlineIndex(индекс с нуля вlinesкоманды), аitemIdстал nullable. PrivateStoreFees,MAX_FEE_PERCENTиBuyFromPrivateStoreCommand.MAX_LINESудалены: лимиты определяет платформа. Процент комиссии проверяется только на неотрицательность (комиссия продавца ещё и не более 100); слишком большую покупку отклоняйте сTOO_MANY_LINES.- Классы-константы
WellKnown*частных магазинов заменены enum-амиPrivateStoreReason,DroppedLineReasonиPrivateStoreMetadataKey.SellerFeeтеперьPrivateStoreFee, аPrivateStoreMetadataчитает комиссию продавца и покупателя из metadata события покупки. - Метаданные покупки
sourceтеперь имяSessionSource:CLIENTилиAPP(раньшеin_gameилиremote). - Новые мягкие парсеры возвращают
nullдля неизвестного токена:SessionSource.fromTokenиfromTokenновых enum-ов.
Что делать.
- Замените удалённые константы и классы на перечисленные enum-ы и типы;
linesкоманды продажи берите изStartPrivateStoreSellingCommand. - Заполняйте
lineIndexв каждом сообщаемомDroppedLine. - Отправляйте
CLIENTилиAPPвsourceпокупки и читайте его черезSessionSource.fromToken. - Обработайте
StartPrivateStoreBuyCommand, чтобы поддержать магазины скупки.
nx-gs-adapter-core 0.40.0: миграции схемы адаптера
Заголовок раздела «nx-gs-adapter-core 0.40.0: миграции схемы адаптера»Что изменилось. Адаптер теперь сам создаёт и обновляет свои таблицы l2nx_* в игровой базе до старта любого
модуля. Игровой сервер без мигратора схемы в classpath переходит в FAILED при старте.
Что делать на игровом сервере (логин-сервера это не касается):
- Запускайте сервер на Java 17 или новее.
- Добавьте
nx-gs-db-migrator-liquibase5иorg.liquibase:liquibase-core5.x в runtime classpath — в том же релизе, который обновляет core. - Убедитесь, что зарегистрирован ровно один
JdbcConnectionSourceи что у его пользователя базы есть права на запись и DDL для таблицl2nx_*иCREATEв схеме. - Уберите DDL таблиц
l2nx_*из кода хоста, если он там был: теперь ими владеет адаптер. Существующая таблицаl2nx_command_receiptsпринимается как есть, вместе с данными. - Если хост читает таблицы
l2nx_*при своей загрузке доNxAdapter.start(), сначала вызовитеNxAdapter.migrateSchema().
