DM Guide 13

Материал из Chaotic Onyx
Версия от 01:10, 13 декабря 2016; Architect of fates (обсуждение | вклад) (Откат правок ZLOFENIХ (обсуждение) к версии Viol)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)
Перейти к навигацииПерейти к поиску

Это 13 глава перевода оригинального руководства по Dream Maker от разработчиков.

Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.

Оригинальная 13 глава руководства на английском языке

Глава 13: События в реальном времени

Я — Время, разрушитель миров. — Бхагават Гита

До сих пор, все события, происходившие в мире, зависели от данных, вводимых игроком. Мир по-настоящему оживает, вместе с событиями, генерируемыми самим миром. Их мы и рассмотрим в данной главе.

Обычно код выполняется настолько быстро, насколько быстро компьютер может его обработать. Когда команды запланированы в соответствии с течением времени, это называется выполнением в реальном времени. События, генерируемые миром, обычно выполняются в реальном времени, или происходят в течение некоторого времени после запуска и не имеют большого интереса для игроков.

Команда sleep

Собственно говоря, выполнение в реальном времени – это вопрос откладывания выполнения кода на определенный промежуток времени. DM для этого предусматривает команду sleep. Она останавливает выполнение текущей операции на определенный промежуток времени.

sleep(Delay) 
    Delay – промежуток времени для ожидания в 10-ых секунд 

Мультипользовательское окружение, такое как мир BYOND, не может тратить время впустую: оно всегда должно быть готово отреагировать на ввод информации. Важно осознавать, что sleep только останавливает текущую операцию и вызывающие ее процедуры. В то время как одна операция спит, другие будут нормально работать.

Есть несколько других «спящих» команд в DM, каждая со своим назначением. Фактически, вы уже видели подобную команду – а именно prompt. Она заставляет операцию заснуть до тех пор, пока игрок не закончит ввод данных. Их объединяет то, что они заставляют текущую процедуру и вызывающие ее временно остановить выполнение и продолжить его позднее.

Следующий пример использует sleep для расчета времени движения небес.

proc/Weather()
   for()
      world << "The sun rises in the east."
      sleep(500)
      world << "The noon day sun rises high in the sky."
      sleep(500)
      world << "The sun sinks low in the west."
      sleep(1000)

Команда for без всяких параметров, как оно написано здесь - один из путей создания бесконечного цикла. Если тело такого цикла никогда не останавливается, то это приведет к остановке мира и последующему игнорированию дальнейшего ввода данных. Однако, в качестве предосторожности, код, наподобие бесконечного цикла, который продолжается слишком долго, отменяется для предотвращения блокировки целого мира. Таким образом, желаемый эффект может быть достигнут ожиданием внутри цикла. Периодическое сообщение о времени видно всем в мире.

Команда spawn

Команда spawn схожа с sleep, но не задерживает всю процедуру, а только отсрочивает текущую команду или блок команд. Выполнение просто пропускает выбранный код и возвращается к нему через определенный промежуток времени.

spawn(Delay) Statement 
    Delay – отложенное время в 10-ых секунд. 

Выбор между spawn и sleep зачастую заключается лишь в том, какой командой вам удобнее воспользоваться в конкретной ситуации. В большинстве случаев каждую из них можно использовать для достижения одной и той же цели. Структура кода обычно сама определяет какой из них удобнее воспользоваться. Если вы хотите провести дополнительную обработку после задержанного кода, то лучше использовать spawn.

Следующий пример использует spawn для дезинфицирования комнаты.

area/sickroom
   //how long to wait before clean cycle
   var/const/alarm = 100

   //moment when countdown started
   var/start_time = 0

   Enter(var/mob/entrant)
      if(!start_time)
         start_time = world.time
         spawn(alarm)
            for(entrant in src)
               entrant << "The disinfectant hits you.  Aaahhhh!"
               del entrant
            start_time = 0 //ready for another round

      var/time_left = (alarm - (world.time - start_time))/10
      entrant << "In [time_left] second\s this room will be disinfected."
      entrant << "Have a nice day!"
      return 1

Когда кто-то в первый раз заходит в комнату, триггер устанавливается и смертельный код откладывается. Операция enter продолжает информировать каждого входящего о том, сколько времени осталось. Когда наступает нужный момент, грязное дело совершается внутри отсроченного блока кода. Заметьте, что когда отложенный код выполняется, он автоматически останавливается в конце блока, как будто это совершенно другая операция.

Расчет времени

Когда интенсивно используется выполнение в реальном времени, полезно знать то, как всё это происходит изнутри. Этот раздел описывает шестеренки и пружины, благодаря которым мир тикает.

Потоки выполнения

Мир DM - однопоточный. Это значит, что только один кусок кода может выполняться одновременно. В другом случае, вам бы пришлось заботиться о гораздо большем количестве потенциальных проблем в коде. Например, если бы отсроченный блок кода, наподобие предыдущего примера, удалял объекты из мира, могли бы возникнуть проблемы, в случае, когда эти объекты используются одновременно другим кодом. Если бы код попытался получить доступ к переменным или операциям удаленных объектов, это привело бы к завершению, возможно, оставляя некоторые важные операции незаконченными.

Если хотите параллелей, собственно говоря, вы можете рассекать мир на многочисленные сервера. Если бы у вас было много CPU в одном компьютере или множество компьютеров, вы бы создали параллельные вселенные!

С тех пор, как существует только одиночный непрерывный поток выполнения, вы можете быть увереннее, когда пишете код. Между одной командой и следующей, ничего не происходит. Если объект существует в конце одной команды, то он будет существовать и в начале следующей.

Единственным исключением в этом правиле являются спящие операции. В этом случае, вам следует осознавать, что всё что угодно может произойти во время паузы. Например, переменная, относящаяся к объекту, может обнулиться, потому что объект был удален. Перед тем как полагаться на спящую команду, следует всё перепроверить, чтобы убедиться, что ничего не произошло.

Переменная src используется как раз для этого. Когда src спящей операции удаляется, сама операция отменяется. Это почти всегда дает желаемый результат и проблема точной проверки отпадает. Если вы не хотите, чтобы операция отменилась, вам всегда следует ставить переменную src на ноль перед засыпанием. Это делает операцию независимой от существования src объекта.

Тиканье часов

Сервер разделяет время на сегменты, называемые тиками. При каждом тике серверных часов, любые запланированные спящие операции начинают работать. Если ожидают несколько операций сразу, то они начнут работать друг за другом в том порядке, в каком засыпали.

Обычно сервер завершает обработку всех ждущих операций с запасом времени до конца тика. В оставшееся время он обрабатывает входящие данные, а если их нет - просто ждет. Если есть операции, требующие много времени для завершения, сервер может затрачивать дополнительное время. В этом случае тик будет длиться дольше, чем должен и вводные данные игрока могут притормаживать.

Это называется лагами сервера. С точки зрения игрока, это похоже на лаги сети, но в этом случае отставание может идти в обе стороны — и вводные, и выходные данные могут тормозить во время передачи. Чтобы узнать какие лаги у вас, вы можете проверить переменную world.cpu. Она показывает, сколько процентов тика используется. Если около 100 и больше, то ваш сервер лагает.

Очевидно, что можно поставить более быстрый компьютер или писать более эффективные операции – это поможет снизить лаги сервера. Другой способ – поддаться и увеличить время тика. Это можно сделать, изменив world.tick_lag. Чем длиннее тик, тем меньше лишних ресурсов компьютера используется для работы мира. Кстати, клиенты ограничены тем, что могут отправить только одну команду за тик, так что его удлинение может помочь уменьшить отставание вводных данных. Конечно, вы платите за это ценой замедления клиента, но лучше так, чем иметь жутко тормозящий сервер.

Все расчеты времени в игре основываются на ближайшем тике. Это значит, что если вам нужна быстрая синхронизация, вам нужно уменьшить world.tick_lag до приемлемого уровня. Стандартно - это 1, значит, что сервер тикает 10 раз в секунду. Уменьшая его, вы также можете ускорить темп, в котором игроки движутся и действуют. Однако, делая это, вы хорошо нагружаете сервер и сеть, что может привести к лагам.

Последовательность действий

Как вы уже заметили, и sleep, и spawn можно использовать без параметра. В этом случае задержка настолько короткая, насколько возможно (т.е. только один тик). То же самое верно и в случае, если промежуток времени меньше длины тика. Иногда эта особенность используется, когда задержка нужна только для контроля порядка, в котором происходят события.

Например, предположим, вы хотите отобразить сообщение, когда мобы выходят из комнаты. Допустим, что вы не хотите, чтобы существа, выходящие в течение одного и того же тика с сообщением, видели его. Проблема в том, что вы не знаете, кто захочет выйти в этот момент. В таком случае, вы можете просто отложить сообщение на мгновение. Это позволяет всем желающим выйти из комнаты до того, как сообщение отобразиться для других посетителей.

area/room
   Exit()
      . = ..()
      if(. && usr) spawn src << "[usr] leaves."

Заметьте, как этот пример устанавливает значение для возвращения дополнительно к результату родительской операции. Это гарантирует, что выход операции точно разрешен. В противном случае, нулевое значение для возвращения не даст игроку выйти.

Кстати, обратите внимание на то, как мы проверяли, что usr не равно нулю. Иначе сообщение будет генерироваться не только для мобов, но и для объектов, покидающих комнату тоже.

Бессонный Сервер

Осталось только кое-что прояснить, чтобы у вас не возникло лишних проблем. Спящие операции замораживают текущий процесс, вызывающие его процессы, процессы, которые вызывают вызывающий процесс и так далее, на определенный промежуток времени. Как вы уже видели, можно использовать spawn(), чтобы избежать ожидания за спящей операцией.

То, как это происходит, очень похоже на то, как сервер действует, когда вызывает операцию. Операции, вызванные сервером, включают в себя различные встроенные действия, наподобие Enter, Exit и Login. Если вы заснете в одной из таких операций, выполнение сервера немедленно продолжится, как будто вы возвратились из своей операции. Запомните, что действия сервера, наподобие движения объекта, не откладываются результатом.