diff --git a/Game/Resources_SoC_1.0006/gamedata/scripts/m_netpk_readme.txt b/Game/Resources_SoC_1.0006/gamedata/scripts/m_netpk_readme.txt new file mode 100644 index 0000000000..c28e1994e1 --- /dev/null +++ b/Game/Resources_SoC_1.0006/gamedata/scripts/m_netpk_readme.txt @@ -0,0 +1,252 @@ +Интерфейс чтения/записи net-пакетов (SHoC:6/7(118),CS:8(124),SCoP:12(128)) +m_netpk.script +============================================================================================== + "Правильная" работа с нет-пакетами. (© xStream) +------------------------------------------------ + Интерфейс работы с классами нет-пакетов: + + 0) Что это вообще такое и с чем едят? + 0.1) 'Нет-пакет' (net_packet) - это специальный объект (бинарный буфер), который содежит информацию, + которой серверные объекты в игре обмениваются со своими клиентскими копиями, изменяя соответствующие параметры/свойства друг-друга. + Позиция в пространстве, поворот, текущий режим стрельбы у оружия и т.п. + Просто так эта информация не доступна стандартныи средствами из скриптов. + 0.2) Нет-пакет для каждого объекта в игре имеет две части/типа, независимых друг от друга: 'state' и 'update'. + Возможна работа с каждым типом по отдельности или с обоими вместе. + Все, что представлено в этом модуле (скрипте) - специальные классы, + которые позволяют организовать ПРОСТОЙ доступ к этим данным и, в случае надобности, их изменять. + Итак... + + 1) Сначала необходимо создать объект необходимого класса, передав серверный или клиентский объект: + В случае интеграции модуля в глобальную таблицу скриптов (модуль инициализирован): + а) local pk = get_netpk(obj,flag) -- по классу переданного объекта будет автоматически подобран нужный класс нет-пакета. + При прямых обращених к модулю и/или классу пакета: + б) local pk = m_netpk.get(obj,flag) -- аналогично а). + в) local pk = m_netpk.net_XXXXXX(obj,flag) -- указываем КОНКРЕТНЫЙ класс требуемого пакета, как подобрать, см. ниже + + Использование дополнительного аргумента (flag) при вызове модуля позволяет использовать один из трех вариантов получения типов пакетов + для любого объекта игры или получить доступ к управлению специальным пакетом: + а) flag: nil (опущен) - 'полный' пакет, т.е. оба типа пакетов 'state' и 'update' в едином объекте (нет-пакете); + б) flag: 1 - пакет типа 'state'; + в) flag: 2 - пакет типа 'update'. + г) flag: 0 - пакет для управления свойствами 'abstract' (об этом ниже); + Учитывая, что в большинстве модификаций игры используется в основном работа со свойствами из 'state' пакетов, + гонять в холостую коды обработки получения изменения и записи полного пакета (с 'update' частью) - расточительно. + + 1.1) Чтобы узнать, все ли прошло хорошо, можно вызвать pk:isOk(), он вернет true, если класс пакета успешно создан. false - в случае неудачи. + + 1.2) Все прочитанные/записываемые свойства (параметры) пакета расположены в таблице data (см. п.2). + Если класс пакета содержит тип 'update' - все свойства этого пакета расположены в суб-таблице data.upd. + + 1.3) Имеется возможность: + Даже если пакет не создался правильно, можно это проигнорировать, так как функция возвращает специальный пакет net_dummy, + с ним можно производить те же операции, что и с остальными, только просто он ничего не делает. + Зачем он нужен? Он полезен при условных обработках пакетов. + Пример: + local pk = m_netpk.net_se_monster(obj,2) + if not pk:isOk() then + pk = m_netpk.net_se_stalker(obj,2) + end + local data = pk:get() + data.upd.health = health -- свойство в 'update' пакете + pk:set(data) + + Здесь НЕ проверяется, правильный ли пакет получился, когда и НЕ монстр и НЕ сталкер. + Потому что в результате получаем net_dummy и работаем как с обычным пакетом. + Меньше кода и проверок. Но злоупотреблять этим свойством не рекомендуется. + Используйте лучше isOk() в критичных на ошибки местах. (Код, приведенный в примере, станет понятен ниже) + + 2) Чтение данных из пакета: + local data = pk:get() -- в результате получаем таблицу, содержащую ВСЕ свойства объекта (в зависимости от запрошенных типов!) + -- напоминание: все свойства пакета 'update' расположены в суб-таблице data.upd + + 3) Меняем нужные нам свойства: + data.xxx = yyy -- простое присваивание значения нужному элементу таблицы + 3.1) Для некоторых сложных свойств (custom_data, shapes, ...) имеются встроенные классы со своим интерфейсом доступа/изменения. + local tbl = data.custom_data:getTable() -- получения массива из строки кастом-даты + local status = data.custom_data:setTable(tbl) -- запись таблицы в строку кастом-даты с возможностью контроля выполнения + 3.2) Если мы не знаем, какие свойства есть у объекта, то используем функцию: + local description = pk:dumpDesc() -- возвращает строку, содержащую читабельное описание пакета (его можно, например, вывести в лог) + + 4) После изменения, данные записываем в собственно нет-пакет объекта: + pk:set(data) + + 5) А все :) Работа сделана. + Дополнительный профит: такие "пакеты" - это объекты, их можно передавать в разные функции, хранить в таблицах и т.д. + Они "помнят", с каким игровым объектом они связаны. + + 6) Существует специальный пакет 'abstract', о котором читаем ниже. + +======================================================================= + -- Если требуется работать с объектом 'напрямую', т.е. с конкретным классом, то узнать, какой класс использовать можно очень просто: + 1) смотрим секцию объекта + 2) идем в папку со спавном и открываем ACDC (или качаем его отдельно) скрипт в редакторе + 3) листаем в конец - там есть сопоставление "секция -> пакет" + 4) смотрим пакет, добавляем префикс 'net_' + Все. + ---------------------- + Пример: + В АСДС нашли + physic_object => 'cse_alife_object_physic', + + Значит, чтобы получить обертку для работы с пакетом надо сделать так: + + local pk = m_netpk.net_cse_alife_object_physic(obj) + local data = pk:get() -- прочитали пакет + ... -- тут изменили данные + pk:set(data) -- записали пакет обратно + +======================================================================= + Про специальный нет-пакет 'abstract': + Библиотека может работать с абстрактной частью пакета, которая доступна ТОЛЬКО в момент выхода объекта в онлайн или сохранении. + Поэтому напрямую (методами set и get) прочитать и записать такие данные нельзя. + Для чтения и/или изменения используется специальный пакет 'net_abstract'. + Хотя принцип работы отличается от работы остальных классов, но все же очень похож. Исключение составляет то, как изменять данные в пакете. + Рассмотрим сразу на примере: + а) создаем объект в игре: + local obj = alife():create("exo_outfit", pos, lv, gv) + б) получаем служебный пакет, которым будем менять свойства: + local pk = m_netpk.get(obj,0) -- или m_netpk.net_cse_abstract(obj) + в) + вариант-1: используем функцию: + pk:setCallback( function(data) + data.direction = vector():set(1,1,1) + end ) + вариант-2: задаем желаемые изменения таблицей: + pk:setCallback( {direction = vector():set(1,1,1)} ) + + Здесь мы создали объект и захотели изменить его абстрактную часть. + Мы указали пакету коллбек, который будет передан основному коллбэку, будучи вызванам как только станет доступна абстрактная часть объекта. + В колбек передается таблица с параметрами пакета, мы можем смело их менять, в примере мы наклонили объект по всем осям на 1 радиану. + При спавне нового игрового объекта: + изменения вступят в силу, как только мы выйдем из текущей скриптовой работы и управление вернется движку - он создаст объект и начнет записывать в него параметры. + Для уже существующих объектов в игре: + требуется после задания изменений свойст объекта, перевести объект в оффлайн и обратно в онлайн, что модулем делается автоматически. + Изменения вступят в силу в момент выхода объекта в онлайн. + + Как получить возможность менять абстрактную часть пакетов? + + 1) этот тип объектов должен иметь класс, который представляет такой тип объектов в игре (можно назвать биндером, а можно никак не называть) + (то есть то, что будет вызвано, когда объект появится в игре) + Завязка из class_registrator.script: + cs_register (object_factory, "CStalkerOutfit", "se_item.se_outfit", "E_STLK", "equ_stalker_s") + + 2) обязательно добавить (если таковой отсутствует) метод биндера 'STATE_Write' (см. скрипты типа: 'se_XXX.script') + Для примера с костюмом: + function se_outfit:STATE_Write(packet) + cse_alife_item_custom_outfit.STATE_Write(self, packet) + end + + 3) В метод STATE_Write (см. выше) добавить строку: + function se_outfit:STATE_Write(packet) + if self.cb_netpk then self.cb_netpk(self, packet) end -- коллбэк для изменения свойств пакета + cse_alife_item_custom_outfit.STATE_Write(self, packet) + end + + Выполнение этих действий ОБЯЗАТЕЛЬНО. + Зачем это нужно? Ну, например, можно поменять дирекшн или позишн, так как это read_only свойства объектов. + А позишн при спавне смещается так, чтобы объект находился на ИИ сетке. + То есть, можем спавнить всякие там лампочки, физические объкты так, чтоб они оказывались повернутыми в пространстве. + Так же можно менять те свойства, которые, обычно, только для чтения. Например story_id, или position + +======================================================================= + Дополнительно - классы шейпов, комплексные шейпы состоят из пачки простых + Шейпы бывают сферические и параллелепипеды. + Сфера задается радиусом(число с запятой) и центром(смещение, вектор), параллелепипед - 4-мя векторами: + три вектора (три ребра: длина, ширина, высота; можно делать, получается, "косые" параллелепипеды) и смещение + Пример: + data.shapes:addSphere(10):addSphere(5, vector():set(10,0,0)) + Это пример работы с левелченджером, в свойстве shapes находится complex_shape, добавляем две сферы, + первая - с радиусом 10 м, вторая - с радиусом 5 м и смещенным центром. + +======================================================================= + Заметки: + - Версии нет-пакетов: ТЧ/ЧН/ЗП (SHoC/CS/SCoP), патч для ТЧ: 1.0004 и выше. + - Позволяет максимально повторить работу утилиты ACDC, и правильную структуру пакета. + - Легко расширяется, почти что копипастом, данных из скрипта ACDC. + - Конфиг файл позволяет легко (в тектовом режиме) добавлять/изменять новые классы игровых объектов. + - С целью автономности работы, некоторые сторонние функции (хелперы) включены в код модуля. + - Встроен автоматический перевод объектов из он-лайна в офф-лайн и обратно при изменении свойств секции 'abstract'. + - Наименования некоторых свойств в классах объектов, которые отличаются от имен в текущих версиях ACDC: + (ACDC -> netpk) + + 'cse_abstract' + unknown_id -> object_id + + 'cse_ph_skeleton' + bone_count -> bones_count + + 'se_respawn' + spawned_obj -> spawned_ids + + Важно! Прямая запись пакетов для классов: se_smart_terrain и se_sim_faction в игре невозможно. Требуется правка скриптов, т.к. + при записи пакетов происходит переинициализация объекта заново с чтением конфигов и пр. Т.о. необходимо блокировать переинициализацию + объекта при перезаписи его пакета. + +======================================================================= +Подключение модуля в модификациях игры: + +1) Скопировать m_netpk.script в папку скриптов установленной игры. +2) Скопировать m_netpk.ltx в папку конфигов игры (Внимание! Имя папки для CS/SCoP - 'configs'). +3) (опционально) Для возможности использовать короткое имя 'get_netpk' для вызовов из различных скриптов: + Подключить первичную инициализацию модуля при старте игры для чего в скрипте _g.script + дописать строку в конце функции 'start_game_callback': + + function start_game_callback() + ... + m_netpk.attach() -- инициализация модуля + end + + ... или выполнить аналогичное при помощи функционала сэндбокса или его аналога. +======================================================================= + Благодарности: + Особая благодарность xStream, за идею (ООП+классы) и исходные коды, которые составляют более половины всего модуля. +============================================================================================== +--- Примеры использования: + +function test_netpk() + local spawn_section = "exo_outfit" + local pos,dir = db.actor:position(),db.actor:direction() + local lvid,gvid = db.actor:level_vertex_id(),db.actor:game_vertex_id() + local sobj = alife():create(spawn_section, pos:add(dir), lvid, gvid) + local pk = get_netpk(sobj) --/ запрос нет-пакета ('full') + if pk:isOk() then --/ получен доступ к нет-пакету объекта? + local data = pk:get() --/ читаем данные из нет-пакета + end + -- + spawn_section = "wpn_ak74" + sobj = alife():create(spawn_section, pos:add(dir), lvid, gvid) + local pk = get_netpk(sobj,0) --/ m_netpk.net_cse_abstract(sobj) + if pk:isOk() then + --/ var-1: function + local func = function(data) + data.direction = vector():set(1,1,1) --/ изменение параметра в 'cse_abstract' + end + local status = pk:setCallback(func) + --[[ --/ var-2: table + local params = { ["direction"] = vector():set(1,1,1) } + local status = pk:setCallback(params) + --]] + end +end + +function test_abstract() + --/ Переносим вентилятор из каморки Сидоровича на тумбочку в подвальчике деревни новичков (на Кордоне) + local sobj = alife():object("trader_ventilyator_0000") --/ находим в игре серверный объект вентилятора + if sobj then --/ нашли? + local pk = get_netpk(sobj,0) --/ или m_netpk.net_cse_abstract(sobj) + if pk:isOk() then + local vPos = vector():set(-212.8,-22.28,-127.20) --/ координаты 'на тумбочке в подвальчике' + local vDir = vector():set(0,3.14,0) --/ направление: 'лопастями от стены' + --/ var-1: установка колбэка с таблицей + local status = pk:setCallback( { position = vPos, direction = vDir } ) + --[[ --/ var-2: установка колбэка с функцией + local status = pk:setCallback( function(data) + data.position = vPos --/ change property 'position' in 'cse_abstract' + data.direction = vDir --/ change property 'direction' in 'cse_abstract' + end ) + --]] + end + end +end +============================================================================================== +Artos (09.10.2013)