DM Guide 19: различия между версиями
(Новая страница: «''Это 19 глава перевода оригинального руководства по Dream Maker от разработчиков.'' [http://wiki.ss13.r…») |
ASORGOO (обсуждение | вклад) |
||
(не показаны 32 промежуточные версии 7 участников) | |||
Строка 1: | Строка 1: | ||
− | ''Это 19 глава перевода оригинального руководства по Dream Maker от разработчиков.'' | + | {{Устарело}} |
+ | Данная статья не совсем соответствует действительности и нуждается в новом переводе. | ||
+ | |||
+ | ''Это 19 глава перевода оригинального руководства по Dream Maker от разработчиков. Если вы работаете в Dream Maker - внимательно прочтите её и сообщите о найденных ошибках.'' | ||
[http://wiki.ss13.ru/index.php?title=All_about_the_code Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.] | [http://wiki.ss13.ru/index.php?title=All_about_the_code Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.] | ||
Строка 8: | Строка 11: | ||
=Chapter 19= | =Chapter 19= | ||
− | + | Управление кодом в Больших Проектах | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | До сих пор примеры были небольшими и простыми, умещающимися в один файл .dm кодировки. Однако в дальнейшем может оказаться более удобным разделять и различать код для различных объектов в раздельные файлы. Например систему рукопашного боя в одном месте, описание мобов в другом, работу двигателей в третьем, и так далее. Также это хорошая практика для кодеров - возиться каждый со своим файлом, а не дёргать один общий. | |
− | + | Также разделение кода по различным файлам делает ваши наработки более мобильными - их можно вставлять в другие проекты\сборки\заготовки. Такие файлы кода часто называют библиотеками (как dll) и вы можете найти множество других библиотек, а также делиться своими собственными. | |
− | #include | + | ==Включение файлов (''оператор'' #include)== |
− | |||
− | |||
− | |||
− | |||
− | + | Содержимое одного файла может быть свободно включено в другой, благодарю использованию команды #include <br> | |
+ | Есть две формы записи данного оператора, в зависимости от способа поиска включаемого содержимого. В первом случае компилятор будет искать файл в директории /library, а в другом случае он обратится к текущей директории и если не найдёт искомый файл, то к /library. Например | ||
+ | <!-- The contents of one source file may be inserted into another by using the #include command. There are two forms depending on where you want the compiler to look for the file. In one case it only looks in the library directory and in the other it looks in the current directory first and then in the library directory if necessary. Оригинал--> | ||
+ | : #include <libfile> | ||
+ | : #include "dmfile" | ||
+ | libfile - это включаемая библиотека. <!-- library file --> | ||
+ | "dmfile" - это файл исходного кода. <!-- source code file --> | ||
+ | Если один и тот же файл включается несколько раз, то проводится только первый запрос. Делается это для предотвращения повторов данной библиотеки кода, которую запрашивают несколько файлов в одном проекте.<!-- This prevents trouble in cases where several files in a project all include the same library file. --> | ||
− | + | Все проекты по умолчанию включают файл <stddef.dm> в начало кода. Это библиотека, описывающая несколько постоянных переменных и определителей.<!-- This file defines a few standard constants and makes some basic definitions. --> | |
− | + | Помимо включения файлов типа .dm , команда #include также используется для прикрепления карт уровней (например.dmm) к проекту. Синтакс(написание) оператора точно такое же. Карты уровней вставляются в главную карту мира и распределяются по z-уровню. Икс и Игрик координаты автоматически определяются по размеру наибольшей из вставляемых карт. | |
+ | <!--Besides inserting source code files (ending in .dm), the #include command is also used to attach map files (ending in .dmm) to a project. The syntax is the same in either case. Map files are inserted into the main world map in successive z-levels and the x-y boundaries of the main map are automatically adjusted to fit the largest map which is included.--> | ||
− | + | Рисунок 19.28: Перед Пре-процессингом | |
− | + | Когда вы используете Dream Maker для управления своим проектом, вам крайне редко придётся использовать сами операторы #include и #define FILE_DIR macros. В Dream Maker эти функции реализуются через сам интерфейс. <!--If you are using the Dream Maker interface to manage your project, you will very rarely have to use the standard #include and #define FILE_DIR macros. The reason is that Dream Maker automates these functions through the interface.--> | |
− | + | Как упоминалось ранее, кодовые файлы и файлы карт добавляются через чек-бокс в диспетчере файлов. Привязка файлов происходит также. Все файлы в директории вашего проекта и внутри неё автоматически размещаются без добавления путей к ним. И путь к каждому файлу в Dream Maker упрощается до названия самого файла, например: world/icons/mobs/warrior.dmi = warrior.dmi | |
+ | <!--As mentioned earlier, code and map files are included by selecting the corresponding check-boxes in the file tree. The file referencing is handled with similar ease. All files in the project directory and below are automatically recognized without the need to supply directories or manually create FILE_DIR entries. For instance, if your project is in the directory world, you may access the file world/icons/mobs/warrior.dmi by simply supplying the name, warrior.dmi.--> | ||
+ | В некоторых случаях применение команды #include размещает файл на исполнение выше, чем это указанно в коде. Подробнее об этом в разделе 19.3.1. | ||
+ | <!--One time when you might need to use #include directly is to force a file to be processed before the code which follows. Section 19.3.1 describes the few situations in which the order of DM source code does matter.--> | ||
− | == | + | ==Препроцессор== |
− | The #include command is one of several preprocessor commands. They all begin with "#" and are placed on a line by themselves. (The DM preprocessor is identical to the one used in C and C++ compilers. The commands are often called preprocessor directives by C programmers.) The preprocessor handles such commands while the code file is initially being read and can be used to alter the appearance of the file as seen by the compiler. Additional preprocessor commands will be described in the following sections. | + | Команда #include - одна из нескольких "препроцессорных" команд. Опознать их можно по символу "#" и последовательному расположению в коде (Препроцессор Dream Maker в данном случае подобен компиляторам C и C++. Также эти люди называют команды "препроцессорными директивами). Препроцессор управляет данными командами, пока файл кода начинает исполняться и может изменять поведение файлов, в зависимости от распознавания их компилятором. Другие препроцессорные команды изложены ниже: |
+ | <!--The #include command is one of several preprocessor commands. They all begin with "#" and are placed on a line by themselves. (The DM preprocessor is identical to the one used in C and C++ compilers. The commands are often called preprocessor directives by C programmers.) The preprocessor handles such commands while the code file is initially being read and can be used to alter the appearance of the file as seen by the compiler. Additional preprocessor commands will be described in the following sections.--> | ||
− | ===#define=== | + | ===''оператор'' #define=== |
− | + | Команда #define создаёт макрос(префикс), или иначе говоря переменную в препроцессоре. Любые участки с данным префиксом в коде будут замещены содержанием данного макроса. В местах, где макрос является частью другого слова или используется в текстовом описании, команда не сработает. Примеры: | |
+ | <!--command creates a preprocessor macro. Any subsequent(последующий) occurrences(случай) of the macro in the code will be replaced by the contents of the macro. Places where the macro occurs as part of another word or inside a text string do not count and will not be substituted.--> | ||
− | #define Name Value | + | : #define Name Value |
− | Name | + | Name - название макроса(префикса). |
− | Value | + | Value - значение макроса(переменной). |
− | |||
− | + | Команды препроцессора заканчивают исполнение по завершении строчки кода, в которой они описаны. Если вы хотите расписать свой код на несколько строк, используйте символ \ в конце строки. | |
+ | <!--This and all other preprocessor commands are terminated by the end of the line. Therefore, if you wish to extend it onto multiple lines, you must use \ to escape the end of the line.--> | ||
− | + | Название макроса может состоять из заглавных и строчных букв, цифр и символа подчёркивания, но не может начинаться с цифры. По умолчанию НАЗВАНИЕ МАКРОСОВ часто набирают исключительно заглавными буквами. | |
+ | <!--The name of the macro may consist of upper and lowercase letters as well as digits and the underscore, as long as the first character is not a digit. By convention, macros are often named in all uppercase, but this is not a requirement.--> | ||
− | + | Также можно придавать макросу значения аргументов, для более точного применения их в коде: | |
− | + | <!--It is also possible to have the macro take arguments which may then be substituted into the replacement text as desired.--> | |
− | |||
− | |||
− | + | : #define Name(Arg1,Arg2,...) Value | |
+ | Arg1 - название первого аргумента. | ||
+ | Arg2 - название второго аргумента, и т.д. | ||
+ | Перечисляемые аргументы будут найдены в коде, и им будет подставлено значение(value) данного макроса. Считается что это процедурный уровень, но так как он оперирует текстовыми значениями, его возможности более обширны и гибки. | ||
+ | <!--Wherever the argument names appear in the replacement text, they will be replaced by the values passed to the macro when it is used. Such a macro can be used like a procedure, but since it operates at the textual level, it is possible to do things which would not be possible with a procedure.--> | ||
− | + | Использовать макросы в выражениях следует с осторожностью - компилятор может не успеть посчитать вставленный из макроса текст, особенно в сочетании с другими текстовыми значениями кода. Для безопасности вы можете взять значения макроса в круглые скобки #define Macro (Value) - чтобы значение не пересекалось с другим исполняемым кодом. | |
+ | <!--Care should be taken when using macros in expressions. Since the macro substitution simply inserts text from one place into another there is no guarantee that expressions within the macro will be evaluated before being combined with an outer expression. To be safe, you can put parenthesis around macro expressions to ensure they do not get combined in some unforeseen way with the external code.--> | ||
− | + | Вот пример кода, применяемый для решения проблем с оператором сдвигом разряда << ,без затрагивания остальной части кода после вставления макроса. | |
− | + | <!--The following code, for example, uses this technique to prevent the bitshift operator << from taking a lower order of operations when the macro is used in some larger expression.--> | |
− | |||
− | + | : #define FLAG1 (1<<0) | |
+ | : #define FLAG2 (1<<1) | ||
+ | : #define FLAG3 (1<<2) | ||
− | + | ===Специальные макросы=== | |
+ | Есть несколько макросов, имеющих определённые специальные значения. | ||
====FILE_DIR==== | ====FILE_DIR==== | ||
− | + | Макрос FILE_DIR - определяет поисковый путь для ресурс-паков, включённых в ваш проект. Может быть использван(переопределён) многократно, каждый новый заданный путь просто будет добавлен в другим путям поискам. | |
+ | <!--macro defines the search path for resource files (i.e. files in single quotes). Unlike most macros, it may be defined multiple times in a cumulative fashion. Subsequent definitions simply add to the list of paths to search.--> | ||
#define FILE_DIR Path | #define FILE_DIR Path | ||
− | Path | + | Path - путь к ресурс-пакам. |
− | By using this macro, you can avoid entering the full path to resource files but can instead just enter the name of the file and let the compiler find it for you. Of course this would lead to confusion if the files in all the specified locations do not have unique names. If that happens, the first one found will be used. | + | Используя данный макрос, вы сможете сократить пути поиска ваших ресурсов компилятором. Просто заранее введите возможные пути расположения файлов, и в дальнейшем набирайте только имя искомого файла(ресурс-пака). |
+ | <!--By using this macro, you can avoid entering the full path to resource files but can instead just enter the name of the file and let the compiler find it for you. Of course this would lead to confusion if the files in all the specified locations do not have unique names. If that happens, the first one found will be used.--> | ||
− | + | Как пример - задание макросов для поиска спрайтов(icons) и звуков(sounds): | |
− | #define FILE_DIR icons | + | : #define FILE_DIR icons |
− | #define FILE_DIR sounds | + | : #define FILE_DIR sounds |
====DEBUG==== | ====DEBUG==== | ||
− | + | Макрос DEBUG включает добавление дополнительной информации в файл .dmb ,что плохо сказывается на скорости исполнения процедур кода, но построчно записывает информацию о выполнении всего кодового файла - в том числе где возникают ошибки, баги и отчего крашится код. | |
+ | <!--macro enables the inclusion of extra debugging information in the dmb file. This makes the file bigger and will result in very slightly slower execution of procedures. However, the advantage is that when a proc crashes, it will tell you the source file and line number where the problem occurred. Without the extra debugging information, only the name of the procedure is reported.--> | ||
− | #define DEBUG | + | : #define DEBUG |
− | + | Просто добавьте эту строчку в ваш код, и режим Debug будет включен. | |
====__FILE__==== | ====__FILE__==== | ||
− | + | Макрос __FILE__ показывает файл в котором распологается исполняемый в данный момент код. Полезный макрос для создания Debug-сообщений. | |
+ | <!--macro is replaced by a text string containing the name of the current source file. This may be useful when generating debugging error messages.--> | ||
====__LINE__==== | ====__LINE__==== | ||
− | + | Макрос __LINE__ показывает строчку кода, которая исполняется в данный момент. Тоже полезен для выведения Debug-сообщений. Вот пример: | |
+ | <!--macro is replaced by the number of the current source line being read. This too may be useful when generating debugging error messages. The following example demonstrates this.--> | ||
proc/MyProc() | proc/MyProc() | ||
− | // | + | //Сейчас крашнется. |
world.log << "[__FILE__]:[__LINE__]: We got this far!" | world.log << "[__FILE__]:[__LINE__]: We got this far!" | ||
− | // | + | //Нет? Странно... |
====DM_VERSION==== | ====DM_VERSION==== | ||
− | + | Макрос DM_VERSION показывает номер версии вашего компилятора. Используется для определения устаревшей версии кода, и замены некоторых операторов на новые. | |
+ | <!--macro is the version number of the compiler (217 at the time I am writing). This could be used by library writers when the code requires new language features that were not available before a certain version or if the syntax changed in some way. By using conditional compilation or the #error command, one could make the library code adapt to earlier versions of the compiler just in case someone tries to use one.--> | ||
===#undef=== | ===#undef=== | ||
− | + | Макрос #undef убирает заранее заданный макрос. Его можно расположить после исполненного макроса для переназначения значений, а также в конце кодового файла, чтобы данный макрос не перешёл на исполнение в другие файлы. | |
+ | <!--command removes a macro. In the code that follows, the macro will no longer be substituted. This could be used at the end of library files to prevent any macros that are used internally from taking effect in the code that includes them.--> | ||
− | === | + | ===Компиляция по условиям (Условная компиляция)=== |
− | The preprocessor can be used to skip sections of code conditionally. The condition usually depends on the existence or value of other macros. In this way you can turn on or off features in the code by configuring a few macro definitions at the top of the project. | + | Препроцессоры могут быть использованы для пропуска части кода по заданным усолвиям, которые определяются наличием или значением используемых макросов. Таким образом вы сможете управлять какими-либо добавочными настройками(фичами), размещая условия компиляции в начале кода. |
− | + | <!--The preprocessor can be used to skip sections of code conditionally. The condition usually depends on the existence or value of other macros. In this way you can turn on or off features in the code by configuring a few macro definitions at the top of the project.--> | |
− | |||
+ | Вот данные макросы условий для компиляции. | ||
====#ifdef==== | ====#ifdef==== | ||
− | + | Команда #ifdef разрешает компилировать последующий код, только если определённому макросу было задано определённое значение. Выполнение данной команды завершается оператором #endif | |
+ | <!--command compiles the code which follows only if the specified macro has been defined. The section is terminated by the #endif command.--> | ||
− | #ifdef Macro | + | : #ifdef Macro |
− | // | + | //Условие для дальнейшего выполнения кода. |
− | #endif | + | : #endif |
− | There is also a #ifndef command which has the opposite effect. The code that follows is only compiled if the macro is not defined. | + | Также существует команда #ifndef - она срабатывает если значение определённого макроса не было задано. |
+ | <!--There is also a #ifndef command which has the opposite effect. The code that follows is only compiled if the macro is not defined.--> | ||
− | The DEBUG macro is sometimes used to turn on certain debugging features in the code. The following example demonstrates this technique. | + | Например макрос DEBUG можно включать для определённого участка кода при помощи #ifdef : |
+ | <!--The DEBUG macro is sometimes used to turn on certain debugging features in the code. The following example demonstrates this technique.--> | ||
− | #ifdef DEBUG | + | : #ifdef DEBUG |
− | mob/verb/GotoMob(mob/M in world) | + | : mob/verb/GotoMob(mob/M in world) |
− | + | : set category = "Debugging" | |
− | + | : usr.loc = M.loc | |
− | #endif | + | : #endif |
====#if==== | ====#if==== | ||
− | + | Команда #if - более общая версия #ifdef, так как включает определения не только макросов но и констант. То есть если выражение совпадает (true), то код внутри #if будет скомпилирован, если выражение не совпадает (false), то код не будет выполняться. В случае невыполнения условий применяется команда #elif, а в случае невыполнения никаких условий из заданных вами, используется команда #else. | |
+ | <!--command is a more general version of the #ifdef command because it can take any expression involving other macros and constants. If the expression is true, the code which follows is compiled. Otherwise it is skipped. Alternate conditions can be supplied with the #elif command and a final section to be compiled if all else fails may follow the #else command.--> | ||
− | #if | + | : #if Условие |
− | // | + | //Код условия. |
− | #elif | + | : #elif Условие |
− | // | + | //Код условия. |
− | #else | + | : #else |
− | // | + | //Код условия. |
− | #endif | + | : #endif |
− | The condition may involve any of the basic operators but usually only uses the boolean operators. One addition is the defined instruction which tests if the specified macro has been defined. | + | Условия могут состоять из базовых (простых) операторов, или логических (boolean) операторов. После проверки данного макроса выдаётся результат = 1 , или = 0 |
+ | <!--The condition may involve any of the basic operators but usually only uses the boolean operators. One addition is the defined instruction which tests if the specified macro has been defined.--> | ||
defined (Macro) | defined (Macro) | ||
− | Macro | + | Macro - название макроса. |
− | Returns 1 if macro has been defined | + | Выдаёт 1 (Returns 1) если макрос определён <!--if macro has been defined--> и 0, если не был. |
− | One common use of the #if command is to block out a section of code. This is sometimes done in the course of debugging or possibly to turn off a feature without throwing away the code. The following example demonstrates this technique. | + | Часто команда #if используется для блокировки выполнения куска кода - в случае если происходит краш, или нужно выключить какую-либо фичу без выпиливания куска кода. Вот пример выполнения кода: |
− | + | <!--One common use of the #if command is to block out a section of code. This is sometimes done in the course of debugging or possibly to turn off a feature without throwing away the code. The following example demonstrates this technique.--> | |
− | |||
− | |||
− | |||
− | |||
+ | : #if 0 | ||
+ | : //Неактивный код. | ||
+ | : #endif | ||
+ | Так как Dream Maker поддерживает вложенность команд одна в другую, также возможно взять ненужный\неиспользуемый на данный момент кусок кода в /* комментарий */ - используя разметку /* */ | ||
+ | Оператор #if привычен для использования программистами С, так как в этом языке компилятор плохо поддерживает вложенность команд. | ||
+ | <!--Since DM allows comments to be nested (one inside another) it is also possible to accomplish the same thing by putting /* */ around the disabled code. It is a C programmer's habit to use the #if command because many C compilers get confused by nested comments.--> | ||
===#error=== | ===#error=== | ||
Строка 176: | Строка 200: | ||
The #error command stops compilation and displays the specified error message. Library writers can use this to tell the user of the library if something is wrong. | The #error command stops compilation and displays the specified error message. Library writers can use this to tell the user of the library if something is wrong. | ||
− | #error Message | + | : #error Message |
The following example will refuse to compile if the DM macro is not defined. | The following example will refuse to compile if the DM macro is not defined. | ||
− | #ifndef DM | + | : #ifndef DM |
− | #error You need to define DM as the name of your key! | + | : #error You need to define DM as the name of your key! |
− | #endif | + | : #endif |
+ | |||
+ | ==Типичные проблемы в больших проектах== | ||
− | + | Первая и наиболее важная - поддержка простого кода. Старайтесь не использовать весь ваш код в одном файле. Думайте о возможных будущих правках и старайтесь диверсифицировать строки кода для дальнейшего удобного чтения и поиска возможных ошибок, и задания переменных. Старайтесь придерживаться модульной системы - под каждую задумку отдельный файл кода, это позволит без проблем сохранить и переносить ваши фичи с одной сборки на другую. | |
+ | <!--There are a few things to keep in mind when working with large DM projects. First and foremost one must strive for simplicity. The art of programming is mostly a matter of realizing your own limitations and compensating for them.--> | ||
− | + | По мере роста проекта, весь последующий код накладывается на предыдущий. Кроме того, код постоянно усложняется - от простых переменных - к сложным, от сложных переменных - к процедурным, от процедурных - к объектам, и так далее по нарастающей. Помните об этом и старайтесь не перекрывать и не повторять ранее использованные строки и описания. | |
+ | <!--If, as the project grows, each new piece of code depends upon the details of every previous piece of code, the complexity of the project is growing exponentially. Before you know it, the code will rise up in revolt and stick you in a dark smelly dungeon. End of story. | ||
− | + | Fortunately, most programming tasks do not require exponential complexity. With a good design, you can split the project into pieces which interact with each other in a fairly simple way. These pieces are often called modules which is why this practice is termed modular programming. (It is interesting to note, however, that all such schemes to avoid exponentially complex code ultimately fail. They only move the exponential growth to a higher level--from individual statements to procedures to objects and on and on. It may be true that complexity will always win out in the end and that every project undergoing perpetual growth must periodically be redesigned from scratch in order to remain comprehensible. Or perhaps this tendency is merely the result of a periodic increase in wisdom to offset the inevitable decline in intelligence. In my own case, I know this to be a prominent factor.)--> | |
− | + | Понятие модуля применимо не только к какой-либо части кода, а чаще подразумевает файл или группу файлов. Открытая часть модуля - процедуры, переменные и объекты, свободные для использования в других местах помимо самого модуля. Закрытая часть модуля - это собственно тело самого модуля, сам код, который однокомпонентен. | |
− | + | При разработке большого проекта, хотя бы один из участников(а желательно все) должен быть ознакомлен с тем, что делает каждый из написанных модулей и какое у него применение. Хорошим тоном считается делать простое и понятное описание и название для каждого модуля. | |
− | + | <!--Although the term module can refer to any unit of code, it most often is embodied by a file or group of files. The public parts of the module are those procedures, variables, and object types which are advertised for use by code outside the module. This is called the module interface and defines the syntax for putting information in and getting results out of the module. All other private material is considered internal to the module and is not for use by outside code. | |
+ | When devising a project, one should foresee the function of the different component modules and have a rough idea of the interface to each one. When work commences on a module, it is worth putting a description of the public interface in a comment at the top of the file. This helps focus development along lines consistent with a good clean interface. You will also find it a useful reference in the future when you or someone else needs to use the module. You won't need to page through expanses of code to figure out how to operate your wonderful gadget.--> | ||
− | |||
− | + | ===О порядке написания кода=== | |
− | + | Во многих случаях, последовательность или порядок кода в Dream Maker не имеет особого значения(sic!). Например процедура, переменная или тип объекта могут быть заданы до или после их прямой компиляции. Это непривычная особенность, отличающая код DM от других, где порядок написания имеет важное значение при исполнении программ. | |
+ | <!--In many cases, the sequential order of DM code makes no difference. For example, a procedure, variable, or object type may be defined before or after being used in the code. This is different from some languages which require every symbol to be defined prior to being used.--> | ||
− | + | Последовательность кода важна в нескольких случаях: | |
+ | * Препроцессор читает код исключительно последовательно - с первой строчки до последней. Поэтому макросы (значения макросов) , которые запрашивают препроцессорные команды, должны быть записаны в коде ранее препроцессорных команд. В данном случае более удобно использовать переменные(variables). | ||
− | One might, for example, add functionality to the client.Topic() procedure in several different locations in the code. As long as you remember to execute the parent procedure each time, the additions are cumulative. | + | <!--There are a few cases, however, when the order of code does matter. The preprocessor, for example, operates strictly sequentially from top to bottom of the code. The principle consequence of this is that macro definitions must precede their use. This is one good reason to instead use constant variables for the purpose when it is possible.--> |
+ | * Последовательность кода важна при наследовании процедур или переменных объекта. Если какая-либо процедура повторяется несколько раз, то она наследует последовательность выполнения первого раза. | ||
+ | <!--Another time when code sequence matters is when overriding object procedures or variable initializations. If the same procedure is overridden several times in the same object type, subsequent versions take precedence and will treat previous ones as their parent procedure.--> | ||
+ | Например, при добавлении дополнительных функций к процедуре client.Topic() в разных местах в коде случается эффект сочетания зачений в родительском элементе: | ||
+ | <!--One might, for example, add functionality to the client.Topic() procedure in several different locations in the code. As long as you remember to execute the parent procedure each time, the additions are cumulative.--> | ||
client/Topic(T) | client/Topic(T) | ||
− | if(T == " | + | if(T == "вступление") |
− | usr << " | + | usr << "И вот как-то раз..." |
else ..() | else ..() | ||
client/Topic(T) | client/Topic(T) | ||
− | if(T == " | + | if(T == "подсказка") |
− | usr << " | + | usr << "Буквально позавчера." |
else ..() | else ..() | ||
− | As written, these two definitions of the Topic procedure can fall in any order with any amount of intervening code. If one of them neglected to call ..(), however, it would disable any previous versions of the procedure. It is therefore good practice to always call the parent procedure unless you specifically wish to disable it. Then you don't have to worry about maintaining any special order of the procedures. | + | В таком написании, эти два определения процедуры client/Topic(T) могут быть размещены в коде в любом месте. Если один из них будет выполнен и приведёт к значению ..() ,то другой уже не наступит. Вот в таких случаях и бывает полезно обращаться к процедуре-родителю |
− | + | <!--As written, these two definitions of the Topic procedure can fall in any order with any amount of intervening code. If one of them neglected to call ..(), however, it would disable any previous versions of the procedure. It is therefore good practice to always call the parent procedure unless you specifically wish to disable it. Then you don't have to worry about maintaining any special order of the procedures.--> | |
− | |||
− | |||
− | |||
+ | ===Отладка кода=== | ||
− | + | Или иначе дебаггинг(debugging). | |
− | + | ====Манеры хорошего кодера==== | |
− | The | + | Ошибка начинающих программистов в том, что они слишком полагаются на компилятор. Опытные баголовы и багоюзеры знают, что если код скомпилирован, то не факт что он работает как написано. Уязвимости бывают везде. |
+ | <!--The novice programmer has far too much faith in the compiler. The veteran bug hunter, however, knows that just because the code compiles doesn't mean it works. It could still be infested with potential problems.--> | ||
− | The | + | Первый вызов для отладчика кода - суметь прочитать код и попробовать скомпилировать его в уме, так как его увидит машина. Возможно где-то не хватает переменных, возможно некоторые процессы ведут к нулевому значению, возможно некоторые условия невозможно выполнить в принципе. Думайте логически. |
+ | <!--The first rule for successful debugging is to compile the code yourself. Of course you do not need to generate the byte code by hand; that's what the compiler is for. Compiling the code yourself means reading through the code you have written as though you were the compiler and making sure what the compiler sees matches what you intended.--> | ||
− | + | Второй шаг - запустить код, так чтобы он заработал. Подставить необходимые переменные и запустить его в приложении. Сервер может выявить простые ошибки и баги, но только вы можете понять что работает не так, как оно должно работать, и почему это не так. А что работает именно так как описано в коде. Поиграйте с переменными и кодом, чтобы лучше понять механизмы компиляции и работы вашего кода. | |
+ | <!--The second good debugging habit is to run the code yourself. Initialize the necessary variables to some typical values and step through the procedure in your mind. The server can catch simple errors, but only you know what the code is supposed to do, so only you can tell the difference between code which runs and code which actually works. After doing a typical case, also be sure to think through any exceptional cases which may occur. For example, with a loop, you should verify that the first and last iteration will operate as expected.--> | ||
− | + | Затем проведите нагрузочное тестирование, попробуйте выйти за пределы допустимых параметров, указанных вами при создании объектов и фич для проекта. Как правило на этом этапе всплывают все баги и уязвимости в написанном и вы можете отловить их и пофиксить\оставить для рабочей версии проекта. | |
+ | <!--After doing these pre-checks, it is, of course, vital to test the code for real. This is known as beating on the code. Don't be gentle. Treat it roughly to expose any unforeseen weaknesses. If it is code which responds to user input, try doing the usual things and then try things you wouldn't normally expect. | ||
+ | Code which has passed these three tests will be reasonably sound. By catching bugs early, you save yourself a lot of trouble, because the code is fresh in your mind and therefore easier to decipher. Besides, you will find that deciphering bug reports from other users can be even harder!--> | ||
− | ==== | + | ===="Неуловимые" баги==== |
+ | Существует два типа багов: Краш-баги которые останавливают процесс целиком (proc crashers) и мелкие баги(silent errors), которые ломают отдельные фичи. : Пример Краш-бага - обращение к переменной объекта, отсутствие ответа от объекта, придание переменной по умолчанию равной нулю, что приводит к завершению оператора. | ||
+ | Когда крашится процедура, как правило сохраняются логи ошибки в world.log . При запуске созданного вами проекта в клиенте Dream Seeker, эта информация будет показана в отдельном окошке. При запуске проекта на сервере Dream Daemon - логи сохраняются в выводе серверной информации, либо в отдельном файле. | ||
+ | <!-- | ||
Even when you have been careful, some subtle problems may still occasionally slip through. Hunting them down can be a frustrating experience, so it is good to have a few tricks up the sleeve. | Even when you have been careful, some subtle problems may still occasionally slip through. Hunting them down can be a frustrating experience, so it is good to have a few tricks up the sleeve. | ||
There are two types of bugs: proc crashers and silent errors. Those that crash procs are the result of some exceptional case occurring. For example, the code might be trying to access an object's variable but the object reference is null. Allowing this sort of case to silently slide by (by pretending the variable of the non-existent object is null, for example) might be a convenient thing to do in some cases, but in others it might cover up a genuine error that needs to be corrected by the programmer. Crashing the procedure and reporting the problem therefore makes it much easier for you to discover the problem and find its source. | There are two types of bugs: proc crashers and silent errors. Those that crash procs are the result of some exceptional case occurring. For example, the code might be trying to access an object's variable but the object reference is null. Allowing this sort of case to silently slide by (by pretending the variable of the non-existent object is null, for example) might be a convenient thing to do in some cases, but in others it might cover up a genuine error that needs to be corrected by the programmer. Crashing the procedure and reporting the problem therefore makes it much easier for you to discover the problem and find its source. | ||
− | When the procedure crashes, some diagnostic information is output to world.log. When running the world directly in the client, this information is displayed directly in the terminal window. With a stand-alone server, it is normally in the server's output but may be redirected to a file. | + | When the procedure crashes, some diagnostic information is output to world.log. When running the world directly in the client, this information is displayed directly in the terminal window. With a stand-alone server, it is normally in the server's output but may be redirected to a file.--> |
+ | |||
+ | Самое главное - найти в логах название процедуры, которая вызвала падение выполнения кода, а также значение переменных src и usr на момент выполнения процедуры. Процедуры могут быть показаны цепочкой вызовов - будут показаны обращающиеся к этой и вызванной этой процедурой процедуры. | ||
+ | <!--The most important part of the diagnostic information is the name of the procedure that crashed. The value of the src and usr variables are also included. If there are any procedures in the call stack (that is, the procedure which called this one, and the procedure which in turn called it, and so on) these are displayed. | ||
− | + | Также вы можете воспользоваться макросом DEBUG, описанным выше для отлова багов и точным вычислением их месторасположения в коде исполняемого файла. | |
+ | <!--If this is not enough information for you to locate the source of the problem, try compiling the world with the DEBUG macro defined. This will add source file and line number information to the diagnostic output. | ||
+ | Существует команда, способная показать вам какое именно значение переменной привело к падению кода. Вот она: | ||
+ | <!--One may also need to probe around in the code to see what is going on. This can be accomplished by sending your own diagnostic information to world.log. For example, you might to know the value of a variable at a particular point in the code. This could be done with a line like the following:--> | ||
− | + | world.log << "[__LINE__]: myvar = [myvar]" | |
− | + | Отладка кода проводится как при отдельных случаях падения кода(и тогда удобнее пользоваться примерами приведёнными выше), так и по всем исполняемым строкам и файлам при финальной диагностике. В последнем случае вы можете пользоваться следующими макросами: | |
+ | <!--Sometimes debugging output such as this is simply removed after fixing the problem, but sometimes you may want diagnostic information to appear whenever you are testing the code. In this case, a macro such as the following may be useful.--> | ||
− | world.log | + | : #ifdef DEBUG |
− | + | : #define debug world.log | |
+ | : #else | ||
+ | : #define debug null | ||
+ | : #endif | ||
− | # | + | Вывод информации на отладку будет осуществляться, но он будет игнорироваться до тех пор, пока вы не используете макрос #DEBUG. |
− | + | <!--You can then send output to debug and it will be ignored when not in DEBUG mode.--> | |
− | |||
− | |||
− | |||
− | You can then send output to debug and it will be ignored when not in DEBUG mode. | ||
− | Another tool for hunting bugs is to comment out code. This may be helpful when determining whether a certain piece of code is responsible for an observed problem. By simplifying the procedure and gradually disabling all but the code which causes the glitch, you can save yourself from scrutinizing a lot of irrelevant material. | + | Есть полезный приём при поиске багов - вынести код в комментарии. Код, вынесенный в комментарий перестаёт восприниматься компилятором как исполняемые команды, зато виден разработчику. Это полезно для выделения кусков кода, ответственных за совершение ошибок при исполнении, и временном устранении их, вплоть до отладки проблемного места. |
+ | <!--Another tool for hunting bugs is to comment out code. This may be helpful when determining whether a certain piece of code is responsible for an observed problem. By simplifying the procedure and gradually disabling all but the code which causes the glitch, you can save yourself from scrutinizing a lot of irrelevant material. | ||
− | This is also essential when asking others for help. Nobody wants to read through pages and pages of somebody else's code. If you can't see the problem yourself but can isolate it down to a small piece of code, you will find it much easier (and fruitful) when getting help from other programmers. Sometimes just trying to clearly define the problem enables you to suddenly see the solution yourself--avoiding the embarrassment altogether. | + | This is also essential when asking others for help. Nobody wants to read through pages and pages of somebody else's code. If you can't see the problem yourself but can isolate it down to a small piece of code, you will find it much easier (and fruitful) when getting help from other programmers. Sometimes just trying to clearly define the problem enables you to suddenly see the solution yourself--avoiding the embarrassment altogether.--> |
Текущая версия от 10:06, 1 апреля 2024
A long time ago in a galaxy far, far away... |
Данная статья помечена как устаревшая, её содержание может быть неверным или неактуальным. Если она не будет актуализирована или не появится веского повода для снятия этой плашки, то вскоре она будет перемещена в Священный архив. При желании вы можете помочь проекту Onyx и сообществу SS13 в целом — загляните на наш Портал сообщества. |
Данная статья не совсем соответствует действительности и нуждается в новом переводе.
Это 19 глава перевода оригинального руководства по Dream Maker от разработчиков. Если вы работаете в Dream Maker - внимательно прочтите её и сообщите о найденных ошибках.
Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.
Оригинальная 19 глава руководства на английском языке
Chapter 19
Управление кодом в Больших Проектах
До сих пор примеры были небольшими и простыми, умещающимися в один файл .dm кодировки. Однако в дальнейшем может оказаться более удобным разделять и различать код для различных объектов в раздельные файлы. Например систему рукопашного боя в одном месте, описание мобов в другом, работу двигателей в третьем, и так далее. Также это хорошая практика для кодеров - возиться каждый со своим файлом, а не дёргать один общий.
Также разделение кода по различным файлам делает ваши наработки более мобильными - их можно вставлять в другие проекты\сборки\заготовки. Такие файлы кода часто называют библиотеками (как dll) и вы можете найти множество других библиотек, а также делиться своими собственными.
Включение файлов (оператор #include)
Содержимое одного файла может быть свободно включено в другой, благодарю использованию команды #include
Есть две формы записи данного оператора, в зависимости от способа поиска включаемого содержимого. В первом случае компилятор будет искать файл в директории /library, а в другом случае он обратится к текущей директории и если не найдёт искомый файл, то к /library. Например
- #include <libfile>
- #include "dmfile"
libfile - это включаемая библиотека. "dmfile" - это файл исходного кода. Если один и тот же файл включается несколько раз, то проводится только первый запрос. Делается это для предотвращения повторов данной библиотеки кода, которую запрашивают несколько файлов в одном проекте.
Все проекты по умолчанию включают файл <stddef.dm> в начало кода. Это библиотека, описывающая несколько постоянных переменных и определителей.
Помимо включения файлов типа .dm , команда #include также используется для прикрепления карт уровней (например.dmm) к проекту. Синтакс(написание) оператора точно такое же. Карты уровней вставляются в главную карту мира и распределяются по z-уровню. Икс и Игрик координаты автоматически определяются по размеру наибольшей из вставляемых карт.
Рисунок 19.28: Перед Пре-процессингом
Когда вы используете Dream Maker для управления своим проектом, вам крайне редко придётся использовать сами операторы #include и #define FILE_DIR macros. В Dream Maker эти функции реализуются через сам интерфейс.
Как упоминалось ранее, кодовые файлы и файлы карт добавляются через чек-бокс в диспетчере файлов. Привязка файлов происходит также. Все файлы в директории вашего проекта и внутри неё автоматически размещаются без добавления путей к ним. И путь к каждому файлу в Dream Maker упрощается до названия самого файла, например: world/icons/mobs/warrior.dmi = warrior.dmi
В некоторых случаях применение команды #include размещает файл на исполнение выше, чем это указанно в коде. Подробнее об этом в разделе 19.3.1.
Препроцессор
Команда #include - одна из нескольких "препроцессорных" команд. Опознать их можно по символу "#" и последовательному расположению в коде (Препроцессор Dream Maker в данном случае подобен компиляторам C и C++. Также эти люди называют команды "препроцессорными директивами). Препроцессор управляет данными командами, пока файл кода начинает исполняться и может изменять поведение файлов, в зависимости от распознавания их компилятором. Другие препроцессорные команды изложены ниже:
оператор #define
Команда #define создаёт макрос(префикс), или иначе говоря переменную в препроцессоре. Любые участки с данным префиксом в коде будут замещены содержанием данного макроса. В местах, где макрос является частью другого слова или используется в текстовом описании, команда не сработает. Примеры:
- #define Name Value
Name - название макроса(префикса). Value - значение макроса(переменной).
Команды препроцессора заканчивают исполнение по завершении строчки кода, в которой они описаны. Если вы хотите расписать свой код на несколько строк, используйте символ \ в конце строки.
Название макроса может состоять из заглавных и строчных букв, цифр и символа подчёркивания, но не может начинаться с цифры. По умолчанию НАЗВАНИЕ МАКРОСОВ часто набирают исключительно заглавными буквами.
Также можно придавать макросу значения аргументов, для более точного применения их в коде:
- #define Name(Arg1,Arg2,...) Value
Arg1 - название первого аргумента. Arg2 - название второго аргумента, и т.д. Перечисляемые аргументы будут найдены в коде, и им будет подставлено значение(value) данного макроса. Считается что это процедурный уровень, но так как он оперирует текстовыми значениями, его возможности более обширны и гибки.
Использовать макросы в выражениях следует с осторожностью - компилятор может не успеть посчитать вставленный из макроса текст, особенно в сочетании с другими текстовыми значениями кода. Для безопасности вы можете взять значения макроса в круглые скобки #define Macro (Value) - чтобы значение не пересекалось с другим исполняемым кодом.
Вот пример кода, применяемый для решения проблем с оператором сдвигом разряда << ,без затрагивания остальной части кода после вставления макроса.
- #define FLAG1 (1<<0)
- #define FLAG2 (1<<1)
- #define FLAG3 (1<<2)
Специальные макросы
Есть несколько макросов, имеющих определённые специальные значения.
FILE_DIR
Макрос FILE_DIR - определяет поисковый путь для ресурс-паков, включённых в ваш проект. Может быть использван(переопределён) многократно, каждый новый заданный путь просто будет добавлен в другим путям поискам.
- define FILE_DIR Path
Path - путь к ресурс-пакам. Используя данный макрос, вы сможете сократить пути поиска ваших ресурсов компилятором. Просто заранее введите возможные пути расположения файлов, и в дальнейшем набирайте только имя искомого файла(ресурс-пака).
Как пример - задание макросов для поиска спрайтов(icons) и звуков(sounds):
- #define FILE_DIR icons
- #define FILE_DIR sounds
DEBUG
Макрос DEBUG включает добавление дополнительной информации в файл .dmb ,что плохо сказывается на скорости исполнения процедур кода, но построчно записывает информацию о выполнении всего кодового файла - в том числе где возникают ошибки, баги и отчего крашится код.
- #define DEBUG
Просто добавьте эту строчку в ваш код, и режим Debug будет включен.
__FILE__
Макрос __FILE__ показывает файл в котором распологается исполняемый в данный момент код. Полезный макрос для создания Debug-сообщений.
__LINE__
Макрос __LINE__ показывает строчку кода, которая исполняется в данный момент. Тоже полезен для выведения Debug-сообщений. Вот пример:
proc/MyProc()
//Сейчас крашнется.
world.log << "[__FILE__]:[__LINE__]: We got this far!"
//Нет? Странно...
DM_VERSION
Макрос DM_VERSION показывает номер версии вашего компилятора. Используется для определения устаревшей версии кода, и замены некоторых операторов на новые.
#undef
Макрос #undef убирает заранее заданный макрос. Его можно расположить после исполненного макроса для переназначения значений, а также в конце кодового файла, чтобы данный макрос не перешёл на исполнение в другие файлы.
Компиляция по условиям (Условная компиляция)
Препроцессоры могут быть использованы для пропуска части кода по заданным усолвиям, которые определяются наличием или значением используемых макросов. Таким образом вы сможете управлять какими-либо добавочными настройками(фичами), размещая условия компиляции в начале кода.
Вот данные макросы условий для компиляции.
#ifdef
Команда #ifdef разрешает компилировать последующий код, только если определённому макросу было задано определённое значение. Выполнение данной команды завершается оператором #endif
- #ifdef Macro
//Условие для дальнейшего выполнения кода.
- #endif
Также существует команда #ifndef - она срабатывает если значение определённого макроса не было задано.
Например макрос DEBUG можно включать для определённого участка кода при помощи #ifdef :
- #ifdef DEBUG
- mob/verb/GotoMob(mob/M in world)
- set category = "Debugging"
- usr.loc = M.loc
- #endif
#if
Команда #if - более общая версия #ifdef, так как включает определения не только макросов но и констант. То есть если выражение совпадает (true), то код внутри #if будет скомпилирован, если выражение не совпадает (false), то код не будет выполняться. В случае невыполнения условий применяется команда #elif, а в случае невыполнения никаких условий из заданных вами, используется команда #else.
- #if Условие
//Код условия.
- #elif Условие
//Код условия.
- #else
//Код условия.
- #endif
Условия могут состоять из базовых (простых) операторов, или логических (boolean) операторов. После проверки данного макроса выдаётся результат = 1 , или = 0
defined (Macro) Macro - название макроса. Выдаёт 1 (Returns 1) если макрос определён и 0, если не был. Часто команда #if используется для блокировки выполнения куска кода - в случае если происходит краш, или нужно выключить какую-либо фичу без выпиливания куска кода. Вот пример выполнения кода:
- #if 0
- //Неактивный код.
- #endif
Так как Dream Maker поддерживает вложенность команд одна в другую, также возможно взять ненужный\неиспользуемый на данный момент кусок кода в /* комментарий */ - используя разметку /* */ Оператор #if привычен для использования программистами С, так как в этом языке компилятор плохо поддерживает вложенность команд.
#error
The #error command stops compilation and displays the specified error message. Library writers can use this to tell the user of the library if something is wrong.
- #error Message
The following example will refuse to compile if the DM macro is not defined.
- #ifndef DM
- #error You need to define DM as the name of your key!
- #endif
Типичные проблемы в больших проектах
Первая и наиболее важная - поддержка простого кода. Старайтесь не использовать весь ваш код в одном файле. Думайте о возможных будущих правках и старайтесь диверсифицировать строки кода для дальнейшего удобного чтения и поиска возможных ошибок, и задания переменных. Старайтесь придерживаться модульной системы - под каждую задумку отдельный файл кода, это позволит без проблем сохранить и переносить ваши фичи с одной сборки на другую.
По мере роста проекта, весь последующий код накладывается на предыдущий. Кроме того, код постоянно усложняется - от простых переменных - к сложным, от сложных переменных - к процедурным, от процедурных - к объектам, и так далее по нарастающей. Помните об этом и старайтесь не перекрывать и не повторять ранее использованные строки и описания.
Понятие модуля применимо не только к какой-либо части кода, а чаще подразумевает файл или группу файлов. Открытая часть модуля - процедуры, переменные и объекты, свободные для использования в других местах помимо самого модуля. Закрытая часть модуля - это собственно тело самого модуля, сам код, который однокомпонентен.
При разработке большого проекта, хотя бы один из участников(а желательно все) должен быть ознакомлен с тем, что делает каждый из написанных модулей и какое у него применение. Хорошим тоном считается делать простое и понятное описание и название для каждого модуля.
О порядке написания кода
Во многих случаях, последовательность или порядок кода в Dream Maker не имеет особого значения(sic!). Например процедура, переменная или тип объекта могут быть заданы до или после их прямой компиляции. Это непривычная особенность, отличающая код DM от других, где порядок написания имеет важное значение при исполнении программ.
Последовательность кода важна в нескольких случаях:
- Препроцессор читает код исключительно последовательно - с первой строчки до последней. Поэтому макросы (значения макросов) , которые запрашивают препроцессорные команды, должны быть записаны в коде ранее препроцессорных команд. В данном случае более удобно использовать переменные(variables).
- Последовательность кода важна при наследовании процедур или переменных объекта. Если какая-либо процедура повторяется несколько раз, то она наследует последовательность выполнения первого раза.
Например, при добавлении дополнительных функций к процедуре client.Topic() в разных местах в коде случается эффект сочетания зачений в родительском элементе:
client/Topic(T)
if(T == "вступление") usr << "И вот как-то раз..." else ..()
client/Topic(T)
if(T == "подсказка") usr << "Буквально позавчера." else ..()
В таком написании, эти два определения процедуры client/Topic(T) могут быть размещены в коде в любом месте. Если один из них будет выполнен и приведёт к значению ..() ,то другой уже не наступит. Вот в таких случаях и бывает полезно обращаться к процедуре-родителю
Отладка кода
Или иначе дебаггинг(debugging).
Манеры хорошего кодера
Ошибка начинающих программистов в том, что они слишком полагаются на компилятор. Опытные баголовы и багоюзеры знают, что если код скомпилирован, то не факт что он работает как написано. Уязвимости бывают везде.
Первый вызов для отладчика кода - суметь прочитать код и попробовать скомпилировать его в уме, так как его увидит машина. Возможно где-то не хватает переменных, возможно некоторые процессы ведут к нулевому значению, возможно некоторые условия невозможно выполнить в принципе. Думайте логически.
Второй шаг - запустить код, так чтобы он заработал. Подставить необходимые переменные и запустить его в приложении. Сервер может выявить простые ошибки и баги, но только вы можете понять что работает не так, как оно должно работать, и почему это не так. А что работает именно так как описано в коде. Поиграйте с переменными и кодом, чтобы лучше понять механизмы компиляции и работы вашего кода.
Затем проведите нагрузочное тестирование, попробуйте выйти за пределы допустимых параметров, указанных вами при создании объектов и фич для проекта. Как правило на этом этапе всплывают все баги и уязвимости в написанном и вы можете отловить их и пофиксить\оставить для рабочей версии проекта.
"Неуловимые" баги
Существует два типа багов: Краш-баги которые останавливают процесс целиком (proc crashers) и мелкие баги(silent errors), которые ломают отдельные фичи. : Пример Краш-бага - обращение к переменной объекта, отсутствие ответа от объекта, придание переменной по умолчанию равной нулю, что приводит к завершению оператора. Когда крашится процедура, как правило сохраняются логи ошибки в world.log . При запуске созданного вами проекта в клиенте Dream Seeker, эта информация будет показана в отдельном окошке. При запуске проекта на сервере Dream Daemon - логи сохраняются в выводе серверной информации, либо в отдельном файле.
Самое главное - найти в логах название процедуры, которая вызвала падение выполнения кода, а также значение переменных src и usr на момент выполнения процедуры. Процедуры могут быть показаны цепочкой вызовов - будут показаны обращающиеся к этой и вызванной этой процедурой процедуры.
world.log << "[__LINE__]: myvar = [myvar]"
Отладка кода проводится как при отдельных случаях падения кода(и тогда удобнее пользоваться примерами приведёнными выше), так и по всем исполняемым строкам и файлам при финальной диагностике. В последнем случае вы можете пользоваться следующими макросами:
- #ifdef DEBUG
- #define debug world.log
- #else
- #define debug null
- #endif
Вывод информации на отладку будет осуществляться, но он будет игнорироваться до тех пор, пока вы не используете макрос #DEBUG.
Есть полезный приём при поиске багов - вынести код в комментарии. Код, вынесенный в комментарий перестаёт восприниматься компилятором как исполняемые команды, зато виден разработчику. Это полезно для выделения кусков кода, ответственных за совершение ошибок при исполнении, и временном устранении их, вплоть до отладки проблемного места.