Отладив несколько скриптов на предмет вылетов решил написать это небольшое руководство, чтобы помочь скриптописателям избежать потенциальных пробем. Сразу оговорюсь - я программист, поэтому к отладке скриптов подхожу со своей профессиональной точки зрения, и то что я тут изложу вам - сие для программиста есть непреложная истина, и если вы хотите уменьшить количество глюков - старайтесь придерживаться нижеприведённого стиля программирования.
Для начала маленький экскурс в историю. Итак, скрипты в сталкере написаны на языке Lua - разработали его бразильцы, он очень гибок, легко встраивается в игры и поэтому очень часто используется для написания скриптов любой степени сложности. Так вот, у этого языка есть несколько исключительно важных особенностей, которые, при их несоблюдении, будут приводить к вылетам вашего скрипта. Итак:
Особенность первая. Типы данных и западло с nil.
Сначала сделаю маленькое отступление, чтобы понятнее было. Итак, в Lua используются следующие операторы:
= оператор присваивания
== сравнение, равно ли значение
~= сравнение, НЕ равно ли значение
< сравнение, меньше ли значение
> сравнение, больше ли значение
<= сравнение, меньше ли значение или равно
>= сравнение, больше ли значение или равно
and логический оператор И
not логический оператор НЕ
or логический оператор ИЛИ
Всё. Теперь поехали дальше... Язык Lua Изначально создавался для работы с большими строчными базами данных, поэтому ВСЕ виды конструкций в языке - это типы данных, т.е. по сути либо переменные, либо константы. Это касается так же и функций! Поэтому даже с функциями в Lua можно и нужно обращатся как с константами. Далее. Касательно, собственно "привычных" переменных. Обычные переменные в Lua получают свой тип данных только в момент присвоения им значения (запомните, это важно). При этом, в Lua есть такой важный и полезный тип данных как nil. nil - Это "пустота", т.е. отсутствие какого-либо значения. При этом этот тип используется компилятором скриптов для "сбора мусора", т.е. для освобождения занимаемой памяти. Смотрите пример, я объясню подробнее, что это значит:
local reminder_count
local exposure_count = 0
Тут у нас 2 локальные переменные, одна из которых просто создана, а вторая - создана и инициализирована. Если сейчас обратиться к переменной reminder_count, считав её значение, то мы получим в качестве значения - nil, т.е. пустоту. Если же мы обратимся к переменной exposure_count - то получим, как и ожидали, 0 - так как мы это значение проинициализировали ЗАРАНЕЕ. При этом не надо путать nil и 0 - так как 0 - это всё-таки какая-то информация, а вот nil - это её полное отсутствие. Так вот, собственно, чем это чревато. Я уже сказал, что nil используется для сбора мусора. Вручную это работает так - когда вам переменная уже не нужна, вы просто пишете:
test_variable = nil
И ваша переменная test_variable сотрётся из памяти, и любые ссылки на неё будут выдавать на выходе nil (типа нет такой переменной, "абонент вне зоны действия сети"). Ну так собственно вот, зачем я это всё рассказываю... если не определить значение переменной сразу, как это было сделано с переменной reminder_count, то любые попытки обратиться к ней внутри скрипта приведут к вылетам. Происходит это следующим образом: допустим, reminder_count - это у нас счётчик напоминаний. Т.е. мы о чём-то напоминаем игроку текстовыми сообщениями, и помечаем, сколько раз мы это сделали. Определили мы переменную именно так, как в примере выше, т.е. не задав ей никакого значения. Значение же её должно присваиваться ниже по коду, после первого оповещения юзера. При этом у нас есть в коде обращение к этой переменной, по которому решается что делать дальше.
Выглядит это примерно так:
function check_antirad_supplies()
if auto_injection_active then
if antirad_check_delay == nil or game.get_game_time():diffSec(antirad_check_delay) > 60 then
if not db.actor:object("antirad") and not (use_scientific_kit and db.actor:object("medkit_scientic")) then
--обратите внимание сюда if reminder and reminder_count == 0 or reminder_count >= mins_till_next_remind then --обратите внимание сюда if use_text then local news_text = "%c[255,160,160,160]Автоматическая система ввода медицинских препаратов\\n".."%c[default]Напоминаю: %c[255,230,0,0]Противорадиационные препараты отстутствуют! %c[default]Автоматический ввод препаратов невозможен." db.actor:give_game_news(news_text, "ui\\ui_iconsTotal", Frect():set(0,188,83,47), 0, 3000) end if use_sounds then local snd_obj if use_custom_sounds then snd_obj = xr_sound.get_safe_sound_object( [[HEV\no-anti-rad]] ) else snd_obj = xr_sound.get_safe_sound_object( [[device\pda\pda_tip]] ) end if snd_obj then snd_obj:play_no_feedback(db.actor, sound_object.s2d, 0, vector(), 1.0) end end reminder_count = 1 elseif reminder then reminder_count = reminder_count + 1 end else radiation_warning = true reminder_count = 0 end antirad_check_delay = game.get_game_time() end end end
Во время игры всё это скорее всего отработает хорошо, но вот при загрузке... тут могут быть проблемы, так как скрипт при запуске может пролететь ту часть, где переменной reminder_count присваивается значение!
В итоге при запуске вышеприведённой функции check_antirad_supplies() нас получится что reminder_count не существует - он равен nil, и в итоге проверка
if reminder and reminder_count == 0 or reminder_count >= mins_till_next_remind then
выдаст ошибку и приведёт к вылету!
Почему? Да потому что НЕЛЬЗЯ сравнивать НИЧТО с вещественным значением. Именно поэтому, когда вы создаёте новую переменную, вы ОБЯЗАНЫ задать ей значение по умолчанию, даже если вы полностью уверены, что она нигде не будет использована до инициализации.
Поверьте - в программировании бывает всё, и запросто может так случиться, что ваша переменная будет использована и спровоцирует вылет.
begin xStream upd. --- Простой способ обойти такую кучу проверок и лишних инициализаций - использование or-оператора. пример
if (reminder_count or 0) >= mins_till_next_remind then
если переменная равна nil, то выражение вычисляется дальше и получается равным 0, а это уже число и сравнение происходит безболезненно end xStream upd. ---
Кроме вышеприведённой ситуации, возможен ещё один неприятный момент, связанный с определением переменных и значением nil. Суть его заключается в том, что при присваивании значений или операциями с переменными мы можем получить кучу проблем... покажу конкретнее каких:
1) "Убийство" переменных
Положим, у нас с reminder_count происходят ещё какие-нибудь занимательные вещи, например мы его значение зачем-нибудь присваиваем другой переменной вот так:
antirad_check_delay = reminder_count
Ну и вот, если у нас reminder_count в момент присвоения ещё не имеет никакого значения, то мы получим занимательную штуку - у нас это выражение сработает как
antirad_check_delay = nil
В результате чего переменная antirad_check_delay "сдохнет" и будет деловито убрана "сборщиком мусора" из памяти, что моментально приведёт к вылету, когда в дальнейшем какая-то часть кода обратится к значению antirad_check_delay.
2) Ошибки деления на 0.
Теперь положим что у нас reminder_count используется в каком-нибудь расчёте вот так:
Как мы помним, у нас reminder_count не инициализирована, и как следствие равна nil.
В итоге мы получим попытку поделить exposure_count на ноль (в нашем случае на nil, что суть одно и то же в нашем случае) и получим "классический" программистский вылет из-за ошибки деления на ноль. 3) Ошибки присвоения.
Предположим что мы написали скрипт, который помогает выбирать оружие при наличии вблизи врагов. И вот, у нас в скрипте есть такой кусок кода, где NPC вынимает из рюкзака оружие:
function weapon_manager:set_weapon(wpn)
local enemy = self.npc:best_enemy() if wpn then --обратите внимание сюда self.weapon_id = wpn:id() --обратите внимание сюда self:return_items(self.weapon_id) -- printf("set_weapon[%s]:set %s[%s]",self.npc:character_name(),wpn:id(),wpn:section()) else printw("set_wpn:weapon not exist") end if self.modes.process_mode == "3" and enemy then for k,v in pairs(self.weapons) do for i,w in ipairs(v) do if w.id ~= self.weapon_id then local item = level.object_by_id(w.id) if item and item:parent() and item:parent():id() == self.npc_id then printw("set_weapon[%s]:process %s[%s]",self.npc:character_name(),w.id,w.sec) self:process_item(item) end end end end end if enemy then self.npc:set_item(object.idle,wpn) end self.weapons = nil
end
В помеченном выражении self.weapon_id = wpn:id(), чтобы было понятнее, self.weapon_id - это оружие, которое сейчас в руках у персонажа, а wpn:id() ссылается на лучшее оружие из инвентаря NPC. Т.е. это выражение заставляет NPC заменить ТЕКУЩЕЕ оружие на лучшее. Всё было бы отлично если бы не одна характерная вещь - когда NPC сидит например у костра отдыхает, У НЕГО НЕТ В РУКАХ СТВОЛА! И разумеется, в этом случае значение self.weapon_id не существует! Оно равно nil. В итоге при подобном выражении
self.weapon_id = wpn:id()
произойдёт попытка присвоить значение ПУСТОТЕ, т.е. nil = wpn:id(), и это МГНОВЕННО приведёт к вылету...
Как с этим бороться.
Бороться с такими ситуациями очень просто на самом деле. Во-первых: ВСЕГДА ИНИЦИАЛИЗИРУЙТЕ ПЕРЕМЕННЫЕ.
Т.е. вот такое описание переменной: local reminder_count
НЕДОПУСТИМО! ВЫ ОБЯЗАТЕЛЬНО ДОЛЖНЫ ЗАДАТЬ ПЕРЕМЕННОЙ ЗНАЧЕНИЕ! Если переменная числовая, сделайте это так:
local reminder_count = 0
Если строчная - то сделайте это так:
local reminder_count = "" (получится вместо nil пустая строка)
Если логическая, то так:
local reminder_count = false
В общем, делайте так, как вам удобнее, но ДЕЛАЙТЕ ОБЯЗАТЕЛЬНО!
И во-вторых: ВСЕГДА ПРОВЕРЯЙТЕ ПЕРЕМЕННУЮ ПЕРЕД ИСПОЛЬЗОВАНИЕМ! Т.е. в кусках кода, чреватых вылетами (к ним относятся АБСОЛЮТНО ВСЕ части, где идёт работа с вещами или оружием) обязательно вставляйте проверки переменных на nil следующим образом:
Код: Выделить всё
if self.weapon_id ~= nil and wpn:id() ~= nil then self.weapon_id = wpn:id() end
Тогда сначала выпонится проверка, и если обе переменные имеют значение, то операция отработает, а если один из параметров пуст - то функция просто пропустит этот код. Точно так же, если вы используете ЛЮБЫЕ математические операции например деление (но не только деление, всего остального тоже касается), тоже ОБЯЗАТЕЛЬНО поверяйте переменную. Вот так:
Код: Выделить всё
if reminder_count ~= nil and reminder_count ~= 0 then antirad_check_delay = exposure_count/reminder_count end
Таким образом потенциальные вылеты будут аккуратно изолированы, и больше не будут создавать проблем. Настоятельно советую всем, кто уже писал скрипты, внимательно их просмотреть, и вставить в "опасные" места подобные проверки, и в будущем делать это сразу, чтобы самим себе облегчить жизнь...
16/02/2009 KamikaZze
Дополнения:
Небольшое дополнение итак, вы вставили обходные проверки вот таким образом:
if self.weapon_id ~= nil and wpn:id() ~= nil then self.weapon_id = wpn:id() end
Или таким:
if reminder_count ~= nil and reminder_count ~= 0 then antirad_check_delay = exposure_count/reminder_count end
И у вас в случае некорректного значения компилятор спокойно пропустил этот код, ничего не сделав ни с self.weapon_id (из первого примера), ни с antirad_check_delay (из второго). Однако вам всё-таки нужно, чтобы с ними что-то происходило, даже если проверяемые переменные неверны. Тогда я бы советовал вам дополнить этот обход отработкой нештатной ситуации. Делается это банально просто:
if self.weapon_id ~= nil and wpn:id() ~= nil then self.weapon_id = wpn:id() elseif self.weapon_id == nil then --вставьте сюда код, что делать если self.weapon_id не существует elseif wpn:id() == nil then --а сюда, если wpn:id() не существует end
И для второго случая аналогично:
if reminder_count ~= nil and reminder_count ~= 0 then antirad_check_delay = exposure_count/reminder_count else --а тут что мы сделаем если reminder_count не существует --я бы сделал например вот так: antirad_check_delay = exposure_count/1 --и все дела end
Более конкретная реализация зависит от того, что именно вы пытаетесь сделать - тут уже вам виднее...
Java — объектно-ориентированный язык программирования, разработанный компанией Sun Microsystems. Приложения Java обычно компилируются в специальный байт-код, поэтому они могут работать на любой виртуальной Java-машине (JVM) независимо от компьютерной архитектуры. Дата официального выпуска — 23 мая 1995 года.
Основные особенности языка Программы на Java транслируются в байт-код, выполняемый виртуальной машиной Java (JVM) — программой, обрабатывающей байтовый код и передающей инструкции оборудованию как интерпретатор, но с тем отличием, что байтовый код, в отличие от текста, обрабатывается значительно быстрее. Достоинство подобного способа выполнения программ — в полной независимости байт-кода от операционной системы и оборудования, что позволяет выполнять Java-приложения на любом устройстве, для которого существует соответствующая виртуальная машина. Другой важной особенностью технологии Java является гибкая система безопасности благодаря тому, что исполнение программы полностью контролируется виртуальной машиной. Любые операции, которые превышают установленные полномочия программы (например, попытка несанкционированного доступа к данным или соединения с другим компьютером) вызывают немедленное прерывание. Часто к недостаткам концепции виртуальной машины относят то, что исполнение байт-кода виртуальной машиной может снижать производительность программ и алгоритмов, реализованных на языке Java. Данное утверждение было справедливо для первых версий виртуальной машины Java, однако в последнее время оно практически потеряло актуальность. Этому способствовал ряд усовершенствований: применение технологии трансляции байт-кода в машинный код непосредственно во время работы программы (JIT-технология) с возможностью сохранения версий класса в машинном коде, широкое использование платформенно-ориентированного кода (native-код) в стандартных библиотеках, аппаратные средства, обеспечивающие ускоренную обработку байт-кода (например, технология Jazelle, поддерживаемая некоторыми процессорами фирмы ARM). Для семи разных задач время выполнения на Java составляет в среднем в полтора-два раза больше, чем для C/C++, в некоторых случаях Java быстрее, а в отдельных случаях в 7 раз медленнее. С другой стороны, для большинства из них потребление памяти Java-машиной было в 10-30 раз больше, чем программой на C/C++. Идеи, заложенные в концепцию и различные реализации среды виртуальной машины Java, вдохновили множество энтузиастов на расширение перечня языков, которые могли бы быть использованы для создания программ, исполняемых на виртуальной машине.
Perl — высокоуровневый интерпретируемый динамический язык программирования общего назначения, созданный Ларри Уоллом, лингвистом по образованию. Название языка представляет собой аббревиатуру, которая расшифровывается как Practical Extraction and Report Language — «практический язык для извлечения данных и составления отчётов». Первоначально аббревиатура состояла из пяти символов и в таком виде в точности совпадала с английским словом pearl («жемчужина»). Но затем стало известно, что такой язык существует (см. PEARL (англ.)), и букву «a» убрали. Талисманом языка Perl является верблюд — не слишком красивое, но очень выносливое животное, способное выполнять тяжёлую работу.
Эта глава посвящена языку Perl и его использованию. После того как вы научитесь программировать, используя Perl, вы сможете использовать его для написания скриптов, с помощью которых решаются разнообразные задачи программирования для Internet и Web. Особый упор будет сделан на тех особенностях, которые используются при написании CGI-скриптов. Если вы никогда не программировали ранее на языке Perl, используйте эту главу как отправную точку в его освоении. Концепции, с которыми вы познакомитесь в этой главе, научат, как писать профессиональные скрипты на языке Perl. В главе рассматриваются не только основы программирования на Perl, но также и несколько других аспектов. К моменту окончания чтения главы вы будете в состоянии усвоить следующие ключевые концепции
· Perl является интерпретируемым языком программирования, специально ориентированным на обработку текста.
· Программисты часто называют программы на языке Perl скриптами.
· Используя Perl, программисты создают CGI-формы
· Perl поддерживает операции с файлами и с базами данных. Это делает его хорошо приспособленным для хранения и извлечения информации в соответствии с потребностями пользователей Web. Perl снабжается отладочной программой, с помощью которой вы тестируете код Perl.
Первые опыты с Perl
Наберите в консольном окошке perl –v и убедитесь, что сиcтема Perl установлена и нужной версии.
Многие языки программирования используются не так, как мыслились их создателям – отчасти или даже полностью. Perl задумывался как язык сценариев для командной оболочки OC, более развитый и переносимый. Сейчас он развился до языка программирования, но полностью цели своего далекого прошлого не изменил. Составим файл scriptexample.pl:
print TMP <<EOF; SET PAGESIZE 0 SET FEEDBACK OFF SET LINESIZE 30 SPOOL $resultfile SELECT table_name FROM user_tables; SPOOL OFF EXIT EOF
print `sqlplus -s scott/tiger <$tempfile`;
print "\nreturn code = ", system "sqlplus -s scott/tiger <$tempfile";
open (TABLES, $resultfile); print "\n\n", <TABLES>;
print "\nend of example\n";
Обратите внимание на следующие особенности:
* При формировании текста задания вместо простого spool scott.tables указан для примера более общий вариант, указывающий на возможность динамического формирования текста. * sqlplus в программе вызывается двумя способами. Первый использует специальные кавычки `...`, а второй – оператор языка system. Второй чуть более длинен, но зато позволяет обрабатывать код завершения программы. * Текст, полученный в sqlplus, тут же в программе пожет прочитываться и обрабатываться (print "\n\n", <TABLES>;). * В случае Unix в качестве первой строки потребуется добавить что-то вроде #!/usr/bin/perl. Это упростит запуск программы, но в Windows этого можно и не делать.
Наберем в командной строке консольного окошка ОС:
Code
scriptexample.pl
или же
Code
perl scriptexample.pl
Именно благодаря возможностям компоновки сценариев (только чуть затронутым приведенным примером) Perl иногда называют «клейкой лентой информационных систем».
Sapiens non ille, quis probare quid-vel, et ille, quis novisse veritas atis, evadere non concertare
РНР (Personal Home Page tools) — это интерпретируемый язык, напоминающий PERL, предназначенный для придания web-страницам элементов интерактивности. Код, написанный на языке РНР, встраивается в документ HTML подобно подпрограмме: в тот участок документа, где необходимо разместить интерактивный элемент, просто вставляется сценарий РНР. Мнемоника данного языка базируется на синтаксисе PERL, Java и С, благодаря чему не вызывает каких-либо трудностей при изучении. Методики, которые позволяют серверам корректно распознавать файлы, содержащие скрипты РНР, различны и зависят в первую очередь от типа конкретного сервера. Считается достаточным назначить такому файлу расширение .php, иногда — с добавлением номера версии используемого языка, например .php2 или .php3.
Технология РНР позволяет организовать на web-странице счетчик посещений, подсчитывать статистику обращений к тем или иным разделам сайта, защитить доступ к какому-либо html-документу паролем и многое другое. Среди недостатков РНР следует отметить то, что данная технология поддерживается далеко не всеми серверами Интернета.
PHP - серверный язык программирования/скриптования.
Code
//функция проверки полей, принимает два аргумента name1, и name2 function check_fields(name1, name2) { //если значения name1 и name2 не пусты if (document.getElementsByName(name1).value != "") && (document.getElementsByName(name2).value != "") { //то пишем, что "Поля не пусты" document.write("Поля не пусты"); } else // иначе { //пишем это document.write("Одно или оба поля пусты"); } }
Sapiens non ille, quis probare quid-vel, et ille, quis novisse veritas atis, evadere non concertare
Материал из Википедии: CSS используется создателями веб-страниц для задания цветов, шрифтов, расположения и других аспектов представления документа. Основной целью разработки CSS являлось разделение содержимого (написанного на HTML или другом языке разметки) и представления документа (написанного на CSS). Это разделение может увеличить доступность документа, предоставить большую гибкость и возможность управления его представлением, а также уменьшить сложность и повторяемость в структурном содержимом. Кроме того, CSS позволяет представлять один и тот же документ в различных стилях или методах вывода, таких как экранное представление, печать, чтение голосом (специальным голосовым браузером или программой чтения с экрана), или при выводе устройствами, использующими шрифт Брайля.
Авторские стили (информация стилей, предоставляемая автором страницы) в виде: Inline-стилей, когда в HTML-документе информация стиля для одного элемента указывается в его атрибуте style. Встроенных стилей — блоков CSS внутри самого HTML-документа. Внешних таблиц стилей, то есть отдельного файла .css, на который делается ссылка в документе. Пользовательские стили Локальный CSS-файл, указанный пользователем в настройках браузера, переопределяющий авторские стили, и применяемый ко всем документам. Стиль браузера Стандартный стиль, используемый браузером по умолчанию для представления элементов.
Таблица стилей состоит из набора правил. Каждое правило, в свою очередь, состоит из одного или нескольких селекторов, разделённых запятыми, и блока определений. Блок определений же обрамляется фигурными скобками, и состоит из набора свойств и их значений.