Artos 99 Опубликовано 16 Января 2012 Согласен, что код несколько более громоздкий, чем при локальном исполнении, но ... это и именно в формате ACDC и как раз не не делит сущность скелета, а обрабатывается внутри класса(ов) в зависимости от условий. Твой нынешний вариант тоже гонял в игре, рабочий. Оба варианта идентичны по результату. Посто данный подход позволяет внутри класса замыкать именно его нюансы и следовать формату ACDC, а не выводить каждый раз во внешнюю примочку. Это вполне может быть актуальным, если далее подстраивать/расширять коды под версии игры (+ЧН, +ЗП) и дополнять недостающие классы. Собственно дал как информацию, может быть и полезной ... и с целью синхронизации. Отказаться в пользу иного всегда не позно, если на то есть причины. :-) "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 16 Января 2012 (изменено) Не буду продолжать спор о фломастерах ... ;-), хотя можно указать, что тогда уж и всему подобному не место в классах, т.е. все _read/_write из классов поубирать, оставив на откуп net_base и внешним функциям ... (ниже как раз такой "кандидат" на вынос). По делу: Похоже в "-- item packet hooks" (класс cse_alife_item) есть неточность. По условию 'num_items' == 0 должны отсекаться чтение/запись всех(!) последующих пропертей, а в настоящей реализации отсекаются (в skip_updprops) только последние два, обусловленные своими доп.условиями. Более павильно вроде бы так (для _read): --/ item packet hooks local skip_updprops = {} for _,prop in ipairs(self.updprops) do if not skip_updprops[prop.name] then ret[prop.name] = this["_r_"..prop.type](updpk, prop.len) if prop.name == 'num_items' then if ret.num_items == 0 then break else if bit_and(ret.num_items, 64) ~= 64 then skip_updprops.ph_angular_vel = true end if bit_and(ret.num_items, 128) ~= 128 then skip_updprops.ph_linear_vel = true end end end end end (для _write по аналогии) Изменено 16 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 16 Января 2012 (изменено) Тоже пока не обнаруживал вылетов, но обуславливаю это тем, что в игре может вообще не правятся такие объекты (с 'num_items' ~= 0), а может редко читаются/изменяются эти апдейт-параметры, тем более пока экспериментировал не отловил предметов с 'num_items' ~= 0. Но как раз в ACDC (руководствуюсь последней универсальной версией) и прописано, что отсекаются последующие (древние версии игры не рассматриваю), да и по аналогии с подобными параметрами в др.классах - отсекается все что после ... P.S. В приведенном тобою куске кода из ACDC невозможно понять какая версия или хотя бы о каких пропертях идет речь в коде. Кусок из универсального не выкладываю, т.к. ... "много буковок" и много веток. ;-) Изменено 16 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 16 Января 2012 (изменено) xStream, опять ... ;-( Не буду надоедать, но обрати внимание, что пропущенное при чтении - НЕ скипится при записи(!) и из дефолтов вылезут несуществующие в 'data' проперти. (в ЧН/ЗП еще к этим поприбавится) P.S. Тут параллельно идет обсуждение о подписках/отписках, что конечно важно, но ... в теме (пере)записи нет-пакетов каждый байт/бит важен и при ошибке - никакие отписки не помогут. Поэтому и такая с моей стороны дотошность. (сорри, но это я еще не выверял каждый класс начисто, а только в черновую). P.P.S. Пока пишу некоторые куски с заделом/запасом для ЧН/ЗП расширения. Заменить break на return можно в конце (начисто), а вот если потребуется и забыть что завершил - можно долго в код всматриваться ... Сейчас у меня эта строка так: break --/ => return --/> Изменено 16 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 19 Января 2012 (изменено) Рабочий вариант модуля для работы с нет-пакетами в игре. - модуль сделан на базе xs_netpk и m_net_utils; - работа с нет-пакетами версий ТЧ/ЧН/ЗП (патч ТЧ 1.0004 и выше); - наличие единого вызова для всех игровых объектов (не исключающий указание конкретного класса); - раздельная работа с обоими типами пакетов (state|update), что не исключает и объединенный пакет данных; - максимально упрощенная работа с изменением свойств 'cse_abstract'; - облегченная адаптация под моды с новыми классами объектов (настройки в конфиг-файле); - автономность работы (коды необходимых хелперов интегрированы в модуль); - недоделаны только несколько "сложных" классов объектов типа smart_terrain ... ссылка: m_netpk_120120 (~21.2 кБ) Прошу заинтересовавшихся проверить (перед выкладыванием в общую тему по скриптам) и дать комментарии/замечания/пожелания. (в дальнейшем по работе модуля - в топик по скриптам) P.S. Обновил архив, исправлены отдельные ощибки, добавлен функционал классу шейперов. Изменено 20 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Января 2012 (изменено) Andrey07071977, собственно про coroutine в начале топика нам напомнил и дал информацию Gun12 (#301), а тут (#112) конкретный практический пример использования в игре подпрограммы (созданной не движком, а скриптом) для постоянного сканирования всех локаций по всем игровым объектам. Однако, хотя информация и полезная, но именно применительно к движковым потокам коллбэков для модмейкеров малоупотребима, т.к. пока практически из скриптов невозможно получить статусы этих потоков и "облегчить отладку коллбэков". :-( Изменено 21 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 22 Января 2012 (изменено) Unnamed Black Wolf Что-то нередко попадаешь 'не в тему', читая НЕ внимательно написанное. Сказано конкретно: Artos: ... но именно применительно к движковым потокам коллбэков для модмейкеров малоупотребима, т.к. пока практически из скриптов невозможно получить статусы этих потоков ... Подскажи, плз, как получить в скриптах игры для, например, потока actor_binder:update(...) иль stalker_binder:hit_callback(...) его статус (coroutine.status)! Изменено 22 Января 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 6 Февраля 2012 (изменено) Ну а по "нормальному", т.е. с проверками корректности заданных строк: prefetch(new_scr) --/ не обязательно, но не помешает ... if type(_G[new_scr]) == 'table' and type(_G[new_scr][func_1]) == 'function' then _G[new_scr][func_1]() --/ вызов функции end Изменено 6 Февраля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 6 Февраля 2012 malandrinus, я все же исхожу из контекста вопроса, в котором говорится о некоей таблице и как минимум о раздельном хранении строк имен модулей/скриптов и функций. Ну а что более обще иль гибче - все же опять и от конкретного контекста зависит и от вкусов. Вариант с _G[file_name][func_name] использован разработчиками в том же xr_logic.script, что дает возможность "вспомнить" тем, кто подзабыл как вызвать функцию. "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 6 Февраля 2012 Согласен с таким резюме. Может быть добавил бы еще для _G[<имя модуля>]: Недостаток в том, что при наличии модуля с синтаксической ошибкой в нем в игровой папке скриптов ,попытка включить модуль в глобальную метатаблицу может привести к фатальной ошибке движка с "безлоговым" вылетом. Достоинство - возможность из заготовленного массива имен комбинировать имена модулей и функций, переключаясь на функции в разных модулях. "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 10 Февраля 2012 (изменено) Что-то вы заспорились ... *Shoker*, задал, как обычно, вопрос с общей формулировкой для довольно локальной своей задачи и стОит ли копья ломать? Если требуется "изнутри" скрипта (игры) определить имя - лучше script_name() не придумать (чего и *Shoker*'у уже достаточно). Если же "снаружи", то вариантов три: 1. Смотрим, как сами разработчики передавали имя скрипта как аргумент в ту же функцию _g.debug_log ... 2. Используем debug.getinfo (для SHoC имеется расширитель от RvP), что и сами разрабы использовали: local script_тame = debug.getinfo(2).short_src:gsub('%.script', "") 3. Парсим, как предложил Gun12, глобальную метатаблицу _G и сравниваем поля с искомой функцией. Каждый выбирает свой путь, исходя из потребностей и возможностей. ИМХО. Изменено 10 Февраля 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 21 Февраля 2012 alpet, очень интересная инфорамция и обнадеживающая. Обязательно попробую потестровать и проверить наличие многопоточности именно в игре. Однако ... халь, что по сути подобное носит "новостной характер", т.к. без хоть каких-то кодов/исходников остается или только использовать то что доступно или, засучив рукава, торить новый путь по указанному курсу ... чтобы иметь возможность подстраивать под иные версии и варианты расширения движка. "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 17 Марта 2012 (изменено) *Shoker*, в Lua имеется пространство io, которое доступно в версии игры CS (ЧН). 1. Тот же мод "Faction commander" на полную катушку использует запись в свои конфиг и лог. 2. Странно видеть подобный вопрос от того, кто в своем моде использует (по сути) это: function save_to_file(filename) local fname = getFS():update_path("$game_saves$", filename..".msf") --/#~# local file = io.open( fname, "w" ) --/#~# if file then ... Если же вопрос не про открытие/использование своего файла со своими сообщениями, а о том, чтобы (и) "штатые" строки лога сохранять в каком-то вторичном файле помимо "штатного" лог-файла - то так и нужно писать об этом. И вопрос бы не в топик по Lua, а в топик по ковыряниям в файлах игры ... Средств Lua для подобного нет, т.е. 'что и куда' указано - 'то и туда' и пишет. Консоль - это уже не Lua, а движек игры. Т.о. без стороннего модуля, который бы мониторил "штатный" лог (консоль) и дублировал бы куда захотелось - не обойтись. P.S. Ну тогда ты точно топиком обшибся. Средствами Lua можно многое делать, в том числе и тебе потребное, но ... тебе же нужно ИЗ ИГРЫ, т.е. и не самим Lua, а его кастрированным куском и не столь Lua, сколь абы чем лишь бы ... покомандовать на компе пользователя без его участия (хотя бы и в благих целях). Сорри, удаляюсь, подобные задачки в игре считаю моветоном. Изменено 17 Марта 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Апреля 2012 Выскажу ИМХО: Правильнее в данном случае следовать контексту, т.е. делать так как требуется именно тебе в данном конкретном случае. Собственно свойства могут быть (условно) глобальными и частными, т.е. зависеть от тех или иных условий (как, например, в твоем случае "не нужно этого делать до определенного времени"). Смотрим аналогии в тех же классах игры: При создании объекта (метод "__init") далеко не все свойства сразу же присваиваются. Какие-то добавляются в методе 'on_register' и т.д. Т.о. класс "обрастает" свойствами по мере его вхождения в игру. "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 14 Мая 2012 (изменено) Dennis_Chikin, наверное чтобы был ясен предмет и предпосылки 'этого' разговора, стОит дать ссылку: #1246. Честно говоря, "убить неделю" для того чтобы сделать вывод о недопустимости удалений "из таблиц вообще" - дело конечно скорее субективное, но ... не для модмейкеров, которые работают над общим проектом (ИМХО). Но это лирика ... не будем углубляться. ;-) Ну а с точки зрения программирования, то чем же так штатные табличные методы Lua (и в частности движковые) провинились, что ими даже пользоваться нельзя? Предлагая понятие "гуманизма" не поминать, к программированию иль логике игры это понятие не имеет никакого отношения. Итак, как я понял, основная затыка в том, что видите ли при внесении изменений в таблицы, эти таблицы "рушатся", т.е. данные становятся непредсказуемыми при выборке. Как решение - предлагается не удалять ничего из таблиц, а просто копировать в новую таблицу 'остающиеся' поля и заменять прежнюю таблицу на вновь созданную. ИМХО, основная ошибка и подобных действий и рассуждений в том, что игнорируется тип таблиц, и к индексированным таблицам ( типа: { v1, v2, ... } ) начинают применяться недопустимые методы изменения, что приводит к смене типа => { [1]=v1, [2]=v2, ... }, т.е. таблица становится хеш-массивом. Программист, работая с таблицами, НЕ имеет права допускать чтобы: Dennis_Chikin: ... когда операция t[n] = v делает внутри таблицы нечто непредсказуемое . Если он работает с индексированными таблицами, то все его действия не должны нарушать структуру таблицы и тогда любые table.insert()/table.remove() и выборки v = t[n] работают безукоризненно. Если же в любом месте, и тем более внутри цикла итерации по таблице, происходит приравнивание поля таблицы к nil - то и работать с такой таблицей следует как с хеш-таблицей! Но это все общие выкладки, о который уже и тут говорилось. Dennis_Chikin, предлагаю привести конкретные куски кодов с индексированными таблицами, в которыех по твоему мнению использование методов удаления недопустимо. Тогда можно будет говорить о проблемах Lua или модмейкера и решать их ... ;-) Судя по обрывку кодов из поста в теме по ссылке: t1000s = t1000s + 1 t1000[t1000s] = { ["fn"] = task, ["name"] = task_name } for i = 1, t1000s do if t1000[i]["name"] == task_name then table_remove( t1000, i ) t1000s = t1000s - 1 return end end - можно однозначно сказать, что таблица t1000 изначально может не являеться индексированной, т.к. ЛЮБОЙ сбой или нештатное вмешательство при ее заполнении подобным методом чреват нарушением индексов. Раз уж используется table_remove, то и наполнение таблицы должно быть по аналогии (а не как с чеш-таблицей): table_insert( t1000, { ["fn"] = task, ["name"] = task_name } ) t1000s = #t1000 Т.е. не задавать исскуственно индекс, а отслеживать его конечное значение. И при удалении, не считать t1000s = t1000s - 1, а просто определять оставшейся об'ем таблицы: t1000s = #t1000 Изменено 14 Мая 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 14 Мая 2012 (изменено) Dennis_Chikin, хочется донести две мысли: 1. Если программист работает с хеш-таблицами - то категорически не рекомендуется использовать методы для индексированных таблиц. Ваши коды в bind_stalker.script формируют хеш-таблицы, хотя то, что используются в качестве ключей искуственно задаваемые упорядоченные ключи-индексы и позволяет c натяжкой говорить о псевдо-индексации. Понятно желание обращаться к нужному полю по ключу (да еще и по понятному числу), и получать размер таблички одним штатным методом (#), но первое - свойство хеш-таблиц, а второе - индексированных! При строгом контроле сбоев в принципе не должно быть, но(!) раз ты пишешь что таблица сыпется - значит у вас в солянке есть гнилое место и следует не говорить о недопустимости удаления, а о недопустимости использования методов применимых только для индексированных таблиц. Раз и формируете хеш-таблицы, то и итерацию проводите как с хеш-таблицами (if k,v in pairs(tab) do) и удаляйте на здоровье tab[k] = nil. Подмена вами понятия 'ключ' (k) на 'индекс' (i) - просто иллюзия, которая врядли облегчает понимание и работу с кодами, но ... из-за возникшего где-то бага - и служит вам плохую службу. ИМХО, вариантов тут три: а) работать именно с индексируемыми таблицами, создавая из и изменяя по всем канонам языка. б) перейти на коды для хеш-таблиц и итерации и чистки проводить соотв.методами, т.е. отказаться и табле remove и #. и if i=1,n do ... 2/ Ну и третий с) вариант: Глядя на вашу систему 'watchdog' и кучу табличек для коллбэков, хочется не про язык Lua говорить, а напомнить: "Количество должно переходить в качество", тем более для солянки, в которой уже чрезмерно много ... Ведь то, о чем сейчас говорим - это сердце/каркас всего мода, на котором он держится и от надежности и качества его зависит очень многое. Совсем без таблиц конечно же не обойтись, но стоить ли пытаться об'ять все и вся, вместо того, чтобы распределить это по самим скриптам/модулям?! Тут уже давались варианты организации системы коллбэков и даже с 'watchdog'. Очень бы рекомендовал вам сменить кучу (хотя и небольших) таблиц на одну предсказуемую и контролируемую. Добавлено через 26 мин.: P.S. И, кстати, не тут ли ваша проблема(?): По всем канонам языка переменные для циклов являются локальными для циклов(!). Ваша конструкция некорректна(!): local t1000s = 0 --/ это локальная переменная для скрипта for i = 1, t1000s do --/< об'явлены переменные для цикла! if t1000[i]["name"] == task_name then table_remove( t1000, i ) t1000s = t1000s - 1 --/< !!! а это уже локальная для цикла!!! return end end Об'яви для цикла именно свою переменную типа n = t1000s и сам цикл for i = 1, n do - может перестанет сыпаться? ;-) Изменено 14 Мая 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 14 Мая 2012 (изменено) malandrinus, согласен с замечанием про t1000s, это уже из темы "раз есть ошибка - гадаем на всем что к ней может относиться". Насчет "и та и другая часть в таблице присутствуют всегда" - это несколько перебор, т.к. "часть с интексом" может отсутствовать.Пример: пустая таблица не имеет ни одной части, пример2: {k1=v1,k2=v2,...} при k1 ~= 1, и т.п. Т.е. любая таблица с t[1]==nil - может иметь только хеш-часть, хотя ... можно и говорить "а почему индекс только в единицы? должен начинаться" ... Если таблица имеет вид {v1,v2,...} и подразумевается, что ни одно поле не пустое - наверное правомочно говорить именно о не-хеш таблице, а о индексированной. Кому такое не по вкусу, то следует избавиться от употребления table.insert/remove и #. Собственно не столь о строении таблиц затеян разговор, а о допустимости/целесообразности применения именно методов для таблиц с индексами. Категоричное отрицание этого для любых, ИМХО, неверно, и обедняет как коды самого программиста, так и нередко идет вразрез с оптимизацией кода. Просто нужно соблюдать правила языка, т.е. применения методов, и не мешать в одну кучу как методы так и таблицы с разными типами. Ну а если возникли ошибки при использовании индексированной части - то это повод или искать и исправлять ошибку или перейти полностью на методы с хеш-частями. Изменено 14 Мая 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 14 Мая 2012 (изменено) Dennis_Chikin, совершенно верно, итерация при помощи pairs не то что не гарантирует, а даже не предусматривает какую либо последовательность. Как в данный момент "карта легла" - так и выдает из таблицы. Это один из минусов пи пользовании такими таблицами и склоняет порою чашу весом именно на индексированные. Однако, к чему эти коды? Повторю, почему, применяя методы именно для хеш-таблиц, ты все одно используешь удаление как для индексированных и продолжаешь упорно сетовать? Сравни: function test_tbl() local t, s = {}, 0 for i=1, 10 do table.insert( t, i ) --/ каждое значение добавляется в конец таблицы end table.remove( t, 10 ) --/ удаляется поле под 10-ым индексом table.insert( t, 10 ) --/ добавляется 10-ка в конец таблицы table.remove( t, 10 ) --/ удаляется поле под 10-ым индексом --/ ... end - и НИКАКИХ пробелов (потерь значений) в индексах. Повторю, если используешь именно свойства индексированной таблицы - то обеспечь работу с этой таблицей именно как с индексированной, а не хеш-таблицей. Ну а насчет солянки: все же не вправе жестко высказываться, трудов многие положили немало и это уже одно заслуживает уважения. А вот то, что ее в порядок приводить нужно " потихоньку" - вот с этим не соглашусь. В ней (ИМХО) требуется кардинально заменить многое и не потихоньку, а сразу, т.к. тягомотина в конце концов приведет к тому, что энтузиазм улетучится а в сухом остатке так и будет ... сырая солянка. Добавлено через 1 мин.: Gun12, нет _util["list_tbl"]( t, "test_tbl" ) тут не при чем ... Просто вроде как банальное t[10] = 10 на самом деле не в конец таблицы (на десятый индекс) добавляет, а уже в хеш-часть, разбивая таблицу на два типа. А вот table.remove( t, 10 ) и даже table.remove( t, 11 ) - удаляет поле с ПОСЛЕДНИМ индексом, а это как раз 9-ка. Изменено 14 Мая 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 14 Мая 2012 (изменено) Gun12, Gun12 Я пробую как миниум именно на компиляторе игры (SciTE- это в черне), поэтому и результат именно таков, каков он в игре. Про некоторые особенности именно сталкеровского Lua мы уже гвоворили (вспомним arg.n ...) Вот и тут, table.remove( t, NN ) - удаляет не именно индекс NN, а если отсутствует, то первый имеющийся до него. Ну и t[10] = 10 - именно разбивает таблицу на две части разного типа, а не вроде как продолжает последовательную индексацию. Добавлено через 8 мин.: Gun12, говорим о том, что в солянке используются таблицы. Эти таблицы заполняются способом для хеш-таблиц, хотя и упорядоченной индексацией: for i = 1, 10 do t = i end. Далее, из этих таблиц удаляются некоторые индексы методом table.remove. Ну и ... как говорит Dennis_Chikin, таблицы начали сыпаться ... Возникло подозрение, что виноват именно table.remove. Так вот, если у них в моде хоть где-то после создания и удаления хоть раз проскакивает t[10] = 10 - то и не удивительно ... т.к. таблица уже НЕ чисто индексная и начинается удаление того, чего и не подразумевается. Т.е. на примере данного кода, поле под индексом 9 вроде как и не удалялось, но из таблицы оно исчезло, что и приводит к путанице. Dennis_Chikin, не используй добавление таблицы по ключам (что меняет структуру таблицы!), хотя и это вроде как и удобно - и не будет проблем. Изменено 14 Мая 2012 пользователем Artos "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 14 Мая 2012 Gun12, но мы в форуме именно по Сталкеру и имеем то, что дадено. И хотя общие правила не должны нарушаться, но и против исключений бесполезно трепыхаться, сколь мы не кривили бы ... лицо. Ну и, как ранее говорилось - кто выбырает 'безопасные' для себя варианты, отметая "подозрительные", а кто, балансируя между правилами и исключениями использует всю оставшуюся палитру методов. "Но иногда найдется вдруг чудак, этот чудак все сделает не так ..."© Машина времени Поделиться этим сообщением Ссылка на сообщение