Artos 99 Опубликовано 13 Октября 2011 (изменено) Kirag, насчет оружия и update-пакетов для него - только что в топике по скриптованию есть информация для размышлений и сведений. А вот на вопрос: " а вот получалось ли у кого-нибудь данные туда результативно записать?" - могу ответить утвердительно. Тот же параметр здоровья (health + upd:health) для неписей (хоть в он-лайне, хоть в офлайне) очень помогает и убивать и ранить и оживлять. Изменено 13 Октября 2011 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 16 Ноября 2011 Real Wolf, не нужно путать справочник по 'штаным' классам/функциям оригинальных игр с различными расширителями. Твой вопрос уместен в топике "X-Ray extensions" Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 1 Марта 2012 AndreySol, возможно могу ошибиться в категоричности (надеюсь поправят), но движек не считает данным методом массу объектов, которые в инвентаре/ящике/... Правильнее вначале проверить имеет ли объект физическую оболочку obj:get_physics_shell() и тогда получать массу ... т.е. типа: if obj and obj:get_physics_shell() then ... Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 2 Апреля 2012 (изменено) malandrinus, однако практика показывет обратное ... В моде при старте игры устанавливаю игровое время: --/ установка игрового времени на дату из 'alife.ltx' function Set_Game_StartTime() local tT = ParseStr(system_ini():r_string("alife", "start_time"), ":") --/ 05:30:00 local tD = ParseStr(system_ini():r_string("alife", "start_date"), ".") --/ 01.05.2012 --/ Установка игрового времени. Формат установки: (Y,M,D, h,m,s, ms) oGameTime = game.CTime() --/ объект класса 'CTime' oGameTime:set(tD[3],tD[2],tD[1], tT[3],tT[2],tT[1], 0) --/< 2012,05,01, 05,30,00, 0 if iSSVer < 8 then --/SHOC utils.Set_CTime(oGameTime) --/ установка таймера игрового времени для сэйвов smart_terrain.Set_CTime(oGameTime) --/ установка гулагового таймера игрового времени end end - т.е. объект устанавливается на требуемое время, запоминается и этот же объект вводится для двух основных игровых скриптов. На всем протяжении игры этот объект используется для отсчета времени с начали игры и каких либо сбоев по вине обнуления старших битов не замечалось. Да и врядли (ИМХО) объект меняет свои данные, скорее, если все же тобою отмечены подобные сбои, их можно приписать самой функции вычисления разности ... Просьба уточнить, тобою отмечены некорректные значения для dt или все же что-то более общее? Если учесть в твоих кодах то, что постоянно сменяется/перезаписывается новым объектом previous_check_time, то все же скорее возможно нечто аномальное с новым полученным объектом, чем изменение прежнего. Если попробовать вот так вычислять dt: local basic_time = game.get_game_time() basic_time:set( 2000,1,1,0,0,0,0) --/ constant basic time (дата может быть из конфига) local previous_check_time = 0 function on_spawn() previous_check_time = game.get_game_time():diffSec(basic_time) end function on_update() local current_time = game.get_game_time():diffSec(basic_time) --/ sec local dt = current_time - previous_check_time previous_check_time = current_time -- делаю вычисления на основе dt end - т.е. не от переменной базы, а от постоянной, не исчезнут ли ошибки/сбои или не станут ли "разовыми", если все же есть сбои в функции diffSec? Изменено 2 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 3 Апреля 2012 (изменено) malandrinus, собственно обсуждаем нечто, чего ни ты ни я не "щупали" и только предполагалки можно строить. Все же место для такого сообщения (топик-справочник) требует не просто некоей информации, а реально подтверждаемого факта "для всех", а не ... "у кого-то". Во-первых, если это не некая страшилка, а реальный баг, то стОит его все же по-возможности выявить, т.к. твоя фраза "никто не использует этот класс на регулярной основе" не соответствует реалиям и даже в оригинальной игре (ТЧ) очень немало примеров именно прямого использования объектов CTime. Это и в гулагах (stateBegin, от которого состояния отсчитываются) и в логике (activation_game_time), и в тасках сплошные last_task_time ... и т.д. Так что, использований немало, а значит и ошибка может касаться самых азных аспектов. Отказаться же от использования напрямую объектов - переписывание многих "рабочих" кодов, на что редко кто из модмейкеров идет ... Во-вторых, жаль что не приведены коды проверок "до" и "после", а не в них ли кроется огреха выявления ошибки? Как можно "поконкретнее", когда пока только по дозированной инфрмации можно делать предположения. Да, в трех строках твоего кода нет изъяна, как и в том, что локально сохраненный объект также не может быть изменен. Но тогда, если подвергается некоему сомнению второе - то и первое также может быть оспорено!? Тем более таким образом:"Бред же, но что-то в этом стиле и происходит.". Все же это либо бред либо нет, и это требует выяснения. Так же, подобные сомнения можно распространить и на любой иной объект в игре, тем более утвердительно сказать, что на протяжение многих часов игры у кого-то из игроков хоть какой-то объект не CTime не "самоизменяется" никто не сможет. ИМХО, если ошибка не воспроизводима на других компьютерах - это уже ошибка или софта/железа, а не игры/движка, или особенность "того компа", на котором она якобы появляется. А это уже совершенно иная история и иные последствия. Предлагаю выложить полный комплект кодов (касаемых поднятой информации), на которых тестерами якобы обнаружены сбои, включая код поверки объекта "до" и после и вывод в лог. Также, по-возможности, другая информация от тестеров, которая могла повлиять так или иначе на проявление бага (ОС, CPU, ...). Как минимум, из-за именно смены объекта, соверенно не ясно, баг ли это именно объекта или нечто иное, например локальный сбой именно одного периода апдейта и т.п. Если это баг - то обязан появиться не только у "ваших тестеров", важно идентифицировать его именно относительно CTime. Кто не верит, дело хозяйское.Как говорится: "Доверяй, но проверяй!" Не верю (и конечно же это мое мнение), пока или информация не станет именно фактом для именно бага CТime в игре SHoC или не останется некоей особенностью именно "вашего" варианта игры и именно у отдельных "кого-то из ваших тестеров", а не у всех. Тем более и ты, по сути, из третьих рук этот "факт" получаешь. Плз, уточни, что за on_spawn и on_update подразумеваются в твоем примере, чтобы поставить в аналогичное место коды-ловушки и попробовать отловить баг, подтвердив или опровергнув (хотя бы частично) информацию. Надо также проверить, что не будет копиться существенная ошибка округления О каком накоплении идет речь? Каждый раз значение current_time вычисляется от неизменной базы (basic_time) и текущего игрового времени и из него вычитается предыдущий результат этого же вычисления (previous_check_time), которое было запомнено в предыдущем цикле. Т.о. накопление может относиться только к двум смежным периодам апдейта. Итоговая погрешность не может превышать двойной дискретности измерения (округления), т.е. 2х1 секунды игрового времени. Изменено 3 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 3 Апреля 2012 (изменено) Пока не ясна причина, а нам даже собственно и сам факт сбоя пока неведом, и даже о чем конкретно гласит некое сообщение в каком-то логе о некоей ошибке, мною ставится под сомнение вот это: старшие 4 байта 64-х разрядного счётчика в этом классе могут спонтанно обнуляться Поэтому и использовал 'якобы сбой'. Чтобы все же информация высказанная тобою приобрела некий статус "для всех", а не "дело хозяйское", в подобных случаях проводят тест-проверку дабы получить результаты на самом разном железе/софте и при различных условиях. Естественно, для такой поверки требуются именно те строки кода, 'которые' обнаруживают ошибку. Т.о. получив результаты от различных источников с достаточно разными внешними факторами (версия игры/патча, мод, железо, софт, ...) и можно будет далее говорить, что это "бред" и является локальным сбоем "у юзера" или "в этом что-то есть" и это возникает при неких общих или конкретных частных условиях. И вот уже во втором случае и нужно решать модмейкерам - забить на редкую ошибку, исправить свои коды или перелопатить коды всей игры ... А пока, это все же является сташилкой, ИМХО. "Привидения" никто не видел, и только кем-то виденное пятно на полу дает пищу для слухов. Появись твое сообщение днем раньше - воспринял бы как перво-апрельскую шутку. ;-) Пока поставил у себя на апдейт актору двойную проверку на поиск этого бага, попробую собрать хоть какую-то статистику. Изменено 3 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 3 Апреля 2012 (изменено) Еслит бы не статус форума, как ведущего по моддингу игры (ИМХО) и топика, как именно справочника для руководства модмейкерам (тоже ИМХО), то не стал бы оспаривать иль сомневаться ... Никто никому конечно ничем не обязан, но ... сказав "А" обычно говорят и "Б", а в данном случае возникла неопределенная пауза. Спасибо за конкретные коды, будет с чем попытаться перепроверить наличие бага. Хотя мною пока использовался вариант: local oCTime,oCTimeSav = nil,nil local tSum = {["b"] = 0, ["p"] = 0, ["c"] = 0, ["dt"] = 0} function OnSpawn() oCTimeSav = game.get_game_time() oCTime = game.get_game_time() end local function Check_CTime(time,sFlg,iFlg) local Y,M,D,h,m,s,ms = time:get(0,0,0,0,0,0,0) local iSum = Y+M+D+h if iSum < tSum[sFlg] then log("%s:Check_CTime:[%s]=[%s]<x>[%s]:[%s:%s:%s:%s]:<%s>", sModule, sFlg, iSum, tSum[sFlg], Y, M, D, h, "Error!") table.print(tSum,sModule..":tSum|"..sFlg.."/"..tostring(iFlg)) abort("%s:Check_CTime:[%s]=[%s]:<%s>", sModule, sFlg, iSum, "Error!") else tSum[sFlg] = iSum end end function OnUpdate() Check_CTime(oCTime, "b") --/ базовый (эталонный) объект счетчика времени Check_CTime(oCTimeSav, "p", 1) --/ первичная проверка local oCTimeCur = game.get_game_time() Check_CTime(oCTimeSav, "p", 2) --/ вторичная проверка tSum.dt = oCTimeCur:diffSec(oCTimeSav) --/ sec oCTimeSav = oCTimeCur Check_CTime(oCTimeSav, "c") --/ конечная проверка --log("%s:OnUpdate:[%s]:[%s %s]", sModule, string.format("dt=%5.3f",tSum.dt), oCTimeCur:dateToString(0), oCTimeCur:timeToString(3) ) --/#~# end Суть собственно та же, только на доступных в любой версии игры методах и при ошибке игра прерывается с распечаткой достаточно информативным логом. Времени конечно пошло мало, но пока никаких абортов по багу не было. Метод же time:get(0,0,0,0,0,0,0) вполне адекватен чтению разрядов счетчика, т.к. при сбое аналогично покажет обнуление старших разрядов (точнее любое уменьшение текущего времени относительно предыдущего). Ну а тратить ли время на поиск причины и/или ограничение круга условий возникновения бага - это конечно же дело добровольное, как собственно и существование этого топика, за который многие тебе благодарны и в котором все же должна быть подтвержденная и/или проверенная информация, а не некие частности по непонятным причинам и не воспроизводимые даже при большом желании. (ИМХО) P.S. Пара побочных вопросов: 1. Каков смысл в обертке: game.CTime(game.get_game_time()) вместо просто: game.get_game_time() ? 2. log1 - уже научен в x-Ray Extensions выполнять функционал string.format (сборка строки по шаблонам) или это рудимент упрощения для "показать принцип"? Если 'да' - будет ли доступен и когда в сырцах? Изменено 4 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 4 Апреля 2012 (изменено) Что буду делать? дальше => Учитывая, что наличие бага для объектов класса CTiime довольно существенно для игры (немало кодов от этого зависит), то требуется все же или подтвердить наличие бага хотя бы на достаточном кол-ве различных платформ(компов) у различных игроков, или же "забить" на локальные сбои именно у отдельных тестеров, списав это на особенности их железа/софта/... В отличии от многих модмейкеров/комад, мною в моде SIMBION идет открытая разработка, т.е. различные новшества и тестовые коды выкладываются в очередные версии/фиксы. Наличие в моде игрового и нескольких дебаг- режимов и возможность от(в)ключать различные опциональные модули, вполне позволяет и поиграться и покопаться в логе/кодах. Отмечу, что многие игроки в мод (хотя это присуще не только симбионщикам) предпочитают играть именно в дебаг-режимах, суя нос в консоль и логи игры. Т.о. вставив в дебаговый модуль мода вот этот код: local oCTime,oCTimeErr = nil,nil local tCTimes = {["s"] = 0, ["c"] = 0, ["p"] = 0, ["e"] = 0} function OnSpawn() oCTime = game.get_game_time() end function OnUpdate() --/ проверка наличия 'бага CTime' local function GetCTime(time,typ) local Y,M,D,h,m,s,ms = time:get(0,0,0,0,0,0,0) local sStr = Y.."."..M.."."..D.." "..h..":"..m..":"..s.." :"..ms if type(time.get_value) == 'function' then local lo,hi = time:get_value() --/ читаем биты счетчика времени tCTimes[typ] = {tm = sStr, hi = string.format("%08x",hi), lo = string.format("%08x",lo)} log(string.format("%s:GetCTime:[%s]=>[%s]:(hi=%08x, lo=%08x):<%s>", sModule, typ, sStr, hi, lo, "Info")) else tCTimes[typ] = sStr log("%s:GetCTime:[%s]=>[%s]:<%s>", sModule, typ, sStr, "Info") end end local oCTimeCur = game.get_game_time() --/ текущее игровое время local iDiffSec = oCTimeCur:diffSec(oCTime) --/ дительность паузы между апдейтами (sec: ~0.02...0.05...) if iDiffSec < 0 or iDiffSec > 99 or oCTimeErr then --/ обнаружен 'баг CTime'? log("%s:OnUpdate:[%s %s]:DiffSec=[%s]", sModule, oCTimeCur:dateToString(0), oCTimeCur:timeToString(3), string.format("%5.3f",iDiffSec) ) --/#~# GetCTime(oCTimeCur, "c") --/ запоминаем значение текущего счетчика GetCTime(oCTime, "p") --/ запоминаем значение предыдущегосчетчика if not oCTimeErr then tCTimes.s = iDiffSec --/ запоминаем значение некорректной паузы oCTimeErr = oCTime --/ запоминаем значение предыдущего 'подозрительного' счетчика else log( string.rep("~",64) ) --/#~# GetCTime(oCTimeErr, "e") --/ перепроверка-распечатка 'подозрительного' счетчика print_table(tCTimes,sModule..":tCTimes|") --/ распечатка всех запомненных значений abort("%s:GetCTime:DiffSec=[%s]:<%s>", sModule, iDiffSec, "Error!") log( string.rep("~",64) ) --/#~# end end oCTime = oCTimeCur --/ запоминаем текущий счетчик времени end - можно от самых разных игроков получить информацию о проявлении/наличии бага ... Как видно из кода, он не сильно обременяет ресурсы игры и даже в дебаговом режиме игра практически не потеряет FPS или иного функционала, но при любом проявлении бага - последует вылет с выводом в лог достаточной для подтверждения и анализа информацией. При чем, это не зависит от наличия расширителей x-Ray и, учитывая работу мода на любом патче - можно параллельно получить информацию о наличии бага в любой версии игры/патча. Остается собрать "жалобы" игроков на подобные вылеты и проанализировать ... Ну а далее, если баг все же именно класса, а не причуд железа/софта/... - искать адекватное решение. При достаточно большой вероятности проявления бага - буду переписывать все коды, в которых применяется класс CTime на более стабильные варианты. Если же критичность бага для большинства игроков не подтвердится - вероятно забью на этот "бред", не буду принимать его во внимание и вырежу из мода проверку бага. При желании, указанный выше код любой модмейкер может встроить к себе в коды и также попытаться понять наличие бага именно на своих кодах/версии/патче ... Т.о. и может быть опровергнута или подтверждена информация о наличии бага в классе CTime. Собственно уже требуется только время ... P.S. Небольой коммент по log1 (и log2) Все чаще можно встречать коды с упоминанием log1 ... что говорит и о востребованности x-Ray Extensions и о том, что коды портируются и в другие моды. Однако(!), нередко и у начинающих модмейкеров возникают проблемы/ошибки при обращении к отсутствующей log1, если ими не используется библиотека-расширитель. А почему бы не сделать тем, кто использует расширитель, довольно простое добавление в тот же _g.script: if log1 then log = function(fmt,...) log1( string.format(fmt or "", ...) ) end end тем самым убив даже не двух, а трех зайцев(!): - т.к. функция log имеется в любой версии кодов - исключена ошибка вызова. Это заодно и реанимирует заглушенную штатную функцию; - коды с применением log1 становятся "стандартными" и даже отсутствие/изъятие расширенной библиотеки не повлечет фатальных ошибок; - применим любой вариант вызова как log1 (простых строк), так и log2, т.е. с использованием шаблонов. Итоговая строка в любом случае будет сформирована. ИМХО, подобная простенькая добавка более удобна для совместимости по кодам различных вариантов от самых разных модмейкеров/команд и снижает вероятность описок/ошибок. Те же, кто не имеет добавки (новички) - просто не будут иметь в логе соотв.строк, но и вылетов не будет. :-) Изменено 4 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 11 Апреля 2012 (изменено) Информация по 'mark_item_dropped', полученная на практике: В оригинале разработчики практически не используют метод mark_item_dropped и остался только рудимент в death_manager.script проверки marked_dropped. Почему - можно только предполагать ..., возможно из-за возможности возникновения фатальных ошибок при некорректных применениях 'задежки', которую дает метод mark_item_dropped. В отличии от метода drop_item, при использовании которого 'выброшенный' объектом предмет становится безхозным (item:parent() == nil) уже в следующем апдейте (~20ms) и, если был в слоте - то слот освобождается, то при использовании mark_item_dropped предмет некоторое время (следующий период апдейта) остается 'принадлежать' выбросившему его объекту, а если был в слоте - то даже как бы лежать в слоте, занимая слот (проверка слота показывает его наличие!). При этом метод marked_dropped возвращает (показывает) это состояние временной принадлежности выброшенного предмета, т.е. как бы метки "предмет выброшен, но принадлежит хозяину". Иными словами: drop_item - безусловное выбрасывание предмета из инвентаря НПС. Предмет становится безхозным уже в следующем апдейте НПС. mark_item_dropped - выбрасывание предмета из инвентаря НПС с сохранением признака 'владения' и наличия предмета у НПС в текущем и последующем апдейте НПС. marked_dropped - проверка метки "НПС выбросил данный предмет" в течении текущего и следующего апдейта НПС. А может быть правильнее трактовать метод mark_item_dropped как: пометить и выбросить с задержкой, т.е. в следующем апдейте, а не текущем ... Наличие метки может помочь в синхронизации между различными схемами/функциями, которые манипулируют предметами. Однако, при некорректном применении этих методов, при различных манипуляциях с предметом (например, удалении предмета из игры) - возможны вылеты, т.к. объект (клиентский!) как бы присутствует у его хозяина и проверки на его наличие возвращают 'истину' ... -------------------------------------------------------- Добавлено через 11 мин.: По ранее упоминавшейся ошибке в класса game.CTime: Наличие бага, при котором сбрасываются в 0 старшие разряды счетчика - подтвердилось. При чем, необязательно долго играть. В моем случае достаточно было при старте новой игры телепортировать актора на Армейсие склады и через ~ пару минут последовал вылет по этой ошибке. При запуске автосэйва перехода на Склады - последовал аналогичный вылет. Системы пока не выяснил, общее пока - патч 1.0006 и использование во всех случаях 'zone_informer' (рассматривал строение перехода на Бар). Т.о. коды игры все же следует пересмотреть и возможно исправить. Пока поставил ловушки и на другие места (не только на апдейт), возможно статистика поможет понять причину. Изменено 12 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Апреля 2012 (изменено) Эффект не устойчивый, но из ~10 запусков (пока не было достаточного времени) проявился 3 раза. Время до ошибки варьировалось от 2...10 минут. У меня в моде достаточно много изменений и завязки на сюжет не приводят к фатальным ошибкам, хотя конечно логика объектов хромает. Как писал выше, закономерности пока не понял. Все вылеты были если не убивал никого из НПС (только монстров) и после использования zone_informer'а около перехода на Бар (хотя тоже может быть совпадение). По крайней мере точно то, что именно объект счетчика изменяется, т.к. у себя прерываю игру только после повторного апдейта и "испорченный" (он не забывается) оба раза показывает нулевые старшие байты. Править же похоже немало придется, хотя ... в основном подобные сбои приводят к нефатальным логическим ошибкам и могут быть малозаметны. Типа: респавнер раньше времени сработал или артефакт раньше времени заспанится аномалией, иль работа гулага закончится/освободится не вовремя ... Вот в таких местах сейчас понаставил "ловушек" и попробую оценить частоту/критичность и может быть причину или иную особенность/локальность. Изменено 12 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Апреля 2012 (изменено) malandrinus, Верояно дальнейший обмен информацией более уместно вести где-то в топике по ковырялкам или по скриптам ... Замечу, что не очень ясно зачем в твоем случае использовать такой вариант определения интервала между апдейтами, когда метод(ы) update уже имеют входным аргументом delta, т.е. как раз разницу времени с момента предыдущего апдейта. Хотя эта разница в единицах глобального времени, но ведь достаточно просто перевести в игровое применив поправку на level.get_time_factor(). Ну да это уже вариации и субъективности ... Еще не приступил к сбору статистики по появлению ошибки, т.к. и ловушки асставляю и коды заодно анализирую ... Словил пока еще один раз (из 5 запусков) при тех же условиях. Думаю сегодня-завта приступлю к отлову, пока же доп.ловушки безмолствуют. :-) Под правкой же кодов в основном подразумевал исходные оригинальные коды, а не "из мода", т.к. это само-собою разумеющееся. Вообще анализ оригинальных кодов (касательно CTime) поражает наличием мусота и неоптимальностей. То ли разрабы подозревали о каких-то сбоях, то ли данный сбой не единичен и имеются иные типы сбоев ...? Пока сильно критичных моментов (как тобою описано) не заметил в оригинальных алгоитмах. В основном возможны сбросы интервалов, что приводит к сбою логики (возврат к началным значениям). Вот неполный список (что успел проанализировать) где вообще применяется использование объектов CTime: - db.storage[idActor].disable_input_time --/ _g.script #x#- не критично - db.storage[idObj].activation_game_time --/ xr_logic.script - actor_detector.init_time --/ xr_detector.script #x#- не критично или отключено - tTask.last_task_time --/ task_manager.script - se_respawn.respawn_time --/ se_respawn.script - se_zone.last_spawn_time --/ se_zone.script #x#- не критично - gulag.stateBegin --/ xr_gulag.script - se_smart_terrain.check_time --/ smart_terrain.script #x#- не критично - se_smart_terrain.duration_end --/ smart_terrain.script #?#- не используется - se_smart_terrain.idle_end --/ smart_terrain.script #?#- не используется - tInfoNPC.stay_end --/ smart_terrain.script #?#- не используется Похоже правка кодов только на пользу пойдет, разгрузив от мусора и обезопасив логику. ;-) Изменено 12 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Апреля 2012 Ни в коей мере не оспариваю отсутствие или даже незначительность использования класса CTime. Как уже писал ранее, именно с ним стала возможна игра и после месяца нахождения в ней. И именно то, что за все время применения этого варианта ни разу не отмечал у себя или других игроков в мод сбоек хоть как-то ставящих под подозрение объект этого класса. Ведь подобный сбой времени не мог бы остаться незамеченным ... Согласен и с аргументами, что замена класса на аналоги далеко не везде возможна, и высказал сомненеия в целесообразности именно применительно к описанному тобою случаю (зависимось акторских параметров от некоего "арта" на поясе). Ну да не суть ... Но, то что имеем в игре (в оригинальных кодах с использованием CTime) все же изумляет. Берем тот же smart_terrain.script, в котором помимо трех невосстребованных случаев использования CTime в дальнейших алгоритмах (duration_end,idle_end,stay_end), а это по объекту для каждого смарта/гулага/НПС, даже check_time, который используется для по-минутной (примено) проверке - сделан на CTime!(?) Нафига?!? Учитывая, что поминутно этот счечик обновляется текущим временем - самое разумное сделать его или на game.time() или даже не глобальном. Ведь всего-то проверка освобождения неписей от гулага на этом завязана, ничего страшного, если такая проверка будет давать "раз в месяц" сбой периода в минуту. А задание времени отключения худа у акторра на секунды (в игре не более пол-минуты игрового времени)? Тут подстраховка на совпадение с "месячной" сменой выглядит более чем натянуто на фоне возможного бага сброса в нули, но зато и в сэйв пишем CTime ... ;-) ИМХО, стОит хоя бы ради устранения мусора и чрезмерностей заменить использование объектов CTime не на их числовые эквивалентя, а на более оптимальные варианты. Ну а там, где не обойтись - вполне можно и подстраховаться, используя не один объект, а, например, и его эталонный дублер ... иль иное решение. Сбоев CTime в ловушках в разных скриптах пока не зарегистрировно, хотя контролируются десятки (за сотню) объектов. Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 12 Апреля 2012 (изменено) Стоп. первый процитированный кусок отнисотся к check_time, который является обновляемым счетчиком периода апдейтов. На нем висит ТОЛЬКО периоическая проверка "не освободился ли кто из НПС" в гулаге и ничего более. По сути весь наворот исходных кодов можно выразить этим: local oCTime_0 = game.CTime() --/ эталонное 'нулевое' игровое время (константа) local oCheckPeriod = game.CTime() --/ 'нулевое' игровое время local oCheckPeriod:setHMSms(0,0,50, 0) --/ продолжительность паузы (50 game-seconds) (константа) function se_smart_terrain:update() ... if ( self.check_time or oCTime_0 ) < game.get_game_time() then self.check_time = game.get_game_time() + oCheckPeriod --/ установка времени следующей поверки (+50 game-sec) --/ проверка: не собрался ли кто-то к этому времени уже уходить ... end ... end его почти полный эквивалент на базе game.time() выглядит так: local iCheckPeriod = 50*1000 --/ 50sec == 50000 ms function se_smart_terrain:update() ... local iDiffSec = ( self.check_time or 0 ) - game.time() if iDiffSec < 0 or iDiffSec > iCheckPeriod then self.check_time = game.time() + iCheckPeriod --/ установка времени следующей поверки (+50 game-sec) --/ проверка: не собрался ли кто-то к этому времени уже уходить ... end ... end т.е. при старте тут же происходит проверка (как и в оригинале) и далее задается время следующей поверки относительно текущего времени. В ситуации когда разрядность счетчика (game.time) переполняется имеем срабатывание по условию превышения задаваемого интервала времени (iCheckPeriod) и переустановку на новую разрядность счетчика. В итоге - только раз в месяц несколько раньше периода проверок будет произведена такая проверка. Итого, код ИМХО эквивалентен по результату и даже более оптимален, т.к. исключены операции сравнения двух объектов счетчиков (u64), а работаем только со считыванием одного (u32) и операциями с числами. Заодно и чуть ресурсы экономим на объекте CTime, который не требуется задавать каждому смарту и хранить его, т.е. храним только числа (u32). Если же в аналогичной ситуации потребуется сохранять - то нет никаких проблем сохранить u32 и восстановить. Обращаю внмание, что единого алги=оритма конечно же не может быть, но немало мест, где CTime именно чрезмерен из-за незначительности последствий из-за применения его аналогов, хотя бы и приводящих к переполнению раз в игровой месяц. Как ты заметил, восстебованность CTime именно при необходимости ставит 'базовые' точки времени, которые могут "перешагивать" месячные рубежи времени. В остальном, навскидку почти везде можно заменить эквивалентами и при необходимоси при "переходах" или игнорировать один период апдейта или делать поправку на "месячность". Лично я буду теперь ставить заплатки везде, где найду.Аналогично, но ... скорее из-за того, что пишу коды по сути мультиплатформенно и если на 1.0006 встречается - то и для остальных патчей и даже CS|CoP не помешают (не делать же разовую работу). ;-) Однако, основное где встречается - установленная база времени (из alife.ltx) от которой скрипты берут отсчет, врядли возможно заплаткой закрыть. Тут что-то посерьезнее потребуется, если обнаружится сбой. Добавлено через 35 мин.: P.S. Вообще-то, за исключением вот этой проблемы с багом, я не вижу никаких иных проблем в использовании этого класса.Готов так же присоединиться к сказаному, но ... Гложет вопрос без ответа: "Почему везде вместо задания времени через часы,минуты,секунды - разработчики используют только установку через милисекунды?" Что это, тяга к большому количеству нулей или им известен некий баг при установках иным способом? Ну и ... если доверие к инструменту не 100% и требует перепроверок - удобства от применения такого инструмента уже далеко не ... Тут как раз уместно изречение: "Ложка дегтя бочку меда портит." Изменено 12 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 13 Апреля 2012 (изменено) Ну так я изначально и написал: "... самое разумное сделать его или на game.time() или даже неа глобальном.". Просто поверхностно анализируя, не вдаваясь в глубинные смыслы, которые могут иметь ответвления ... malandrinus: Или ты о чём-то другом? Да, несколько об ином. Смотрим в коды и видим: idle_time:setHMSms( 0, 0, 0, math.random(self.idle_spawn_min, self.idle_spawn_max)*1000) Спрашивается, а почему не так(?): idle_time:setHMSms( 0, 0, math.random(self.idle_spawn_min, self.idle_spawn_max), 0) , учитывая что везде (почти) целочисленные числа и в конфигах заданы именно секундами. И так почти везде. Ведь логичнее - заданы часы => ставим именно часами, заданы секунды - => ну так раз доступен вариант установки именно секундами, то зачем манипулировать нулями? И не ставлю знака равентства или какого-то соответствия между CTime и game.time() ... Моя стратегия была и будет: "Каждому инструменту свое место использования", т.е. работать по контексту в зависимости от задачи. И пока основное - избегать абсолютного времени (от базы CTime) там, где можно задавать и важен только интервал. А уж чем его обрабатывать - зависит от алгоритма и вполне может быть и глобальное или 32-х разрядное игровое время. Важно отсутствие критичных последствий с точки зрения именно игры. В конце концов игра не точный часовой алгоритм и порой именно рандомные коллизии/случайности добавляют в нее реалистичности. В жизни тоже мало что идет по расписанным безошибочным алгоритмам. Добавлено через 57 мин.: P.S. Вот припомнился вариант для подводного камня при вроде как простой периодичности обновления: Если работа в гулаге должна иметь синхронизацию с неким событием из логики "уходящего" НПС, то завязка именно на гейм-секунды может быть важна. Например, многое в логике задается именно игровым временем (секундами) и тот же звук (кстати game.time()) отыгрывается ... Так что при переходе на реальное время именно при смене игроком или скриптом тайм-фактора - поизойдет рассинхронизация прерывания работы иль еще чего. Изменено 13 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 13 Апреля 2012 (изменено) Ну как может быть отброшено то, чего нет??? Какая дробная часть? Ведь речь о целочисленных значениях и никаких операций деления нет! Жаль пожадничал и дал только одну строку из исходного кода se_respawn.script ... Берем простенький пример: self.idle_spawn_min = 15*60 --/ 15 минут, заданные в конфиге как 900 self.idle_spawn_max = 30*60 --/ 30 минут, заданные в конфиге как 1800 math.random(900, 1800) - даст нам только целочисленное число, например 1234 Ну и что потеряется при(?): idle_time:setHMSms( 0,0,1234, 0) В большинсте исходных кодов устанавливаемый счетчик (в данном случае idle_time) вначале берется нулевым ( = game.CTime() ). Какие тут милисекунды? Ведь разрабы манипулируют именно секундами и только при установке счетчика переводят в мили- ... добавляя нули и меняя разряд для изменения счетчика. И, даже если были бы в idle_time какие-то милисекунды - они затираются, т.к. идет именно установка заданных значений, а не их добавление. Пример дан из se_respawn.script, где дробными частями или милисекундами не пахнет и в основном часами-сутками манипуляция идет. И все это все одно задается именно милисекундами. И что интересно, наличие метода setHMS нигде не востребовано, что так же непонятно. Причем в твоем варианте опять же используется разряд секунд для установки времени, а я про то, что разработчики НЕ используют никакие разрыды, кроме как последнего разряда милисекунд! Так же, ты заведомо предполагаешь возможность задания в качестве аргумента нецелочисленных значений (что конечно может быть востребовано), но в кодах о которых веду речь (исходных в игре) такое не востребовано. Причина, почему не просто всё в миллисекунды, потому что теоретически возможно задать добавку времени больше того самого месяца и тогда количество миллисекунд не влезет в 32 бита. Понятное дело, что разработчики об этом не думали ...А вот это как раз и мой вопрос: Почему же имея потенциальную возможность переполнения в счетчике младших разрядов разработчики все свои установки в исходных кодах производят ТОЛЬКО через именно милисекунды, когда тот же результат можно получать без вероятного переполнения через секунды (иль минуты/часы)? И, кстати "месяц" тут несколько меньше: всего 49/2 = 24 дня 11 часов 59 минут 59 секунд. При попытке установить через милисекунды бОльшего значения поисходит переполнение ... чего нет если устанавливать секундами. (это если кто-то из читающих наш оффтопик не очень понял о чем речь) Изменено 13 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 19 Апреля 2012 malandrinus Все же информация о наличии бага в исходном классе CTime скорее всего ошибочна. 1. При каких (общих) условиях проявляется наличие этого бага: сбой старших разрядов счетчика? а) Версия игры SHoC и версия патча 1.0006; б) Использование расширителя x-Ray Extension; Не могу уверждать за ваших тестеров (OGSE), но (судя по выложенным кодам работы с CTime) вероятно также сюда можно отнести: в) Использование функции get_value() добавленной в класс CTime, читающей разряды счетчика. А точнее, ее обязательное использование при каждом(!) определении дельты времени. Примечание: Когда в первые разы получилось отловить баг с использованием твоих кодов, мною вызов чтения разрядов был перенесен в подфункцию уже контроля пойманного сбоя, что исключило проявление бага ... Вернул вызовы в код апдейта - вернулся и баг. 2. В моих попытках выяснить причину сбоя определилось еще одно обязательное условие: г) Работа с "zone_informer", т.е. отображение партиклами различных "шейдерных" объектов. Хотя ... это может быть и неким катализатором, ускоряющем появление сбоя. По крайней мере, именно при наличии всех четырех условий (а...г) мне удается воспроизводить баг практически на каждом запуске. Если же отсутствует условие в) (т.е. не читаются разряды счетчиков), то наличие многочисленных "ловушек" бага во всех кодах игры (это сотни счетчиков класса CTime) ни разу не подтвердило сбоя этого класса (при наличии/отсутствии условий а,б,г). Учитывая что собственно баг из разряда "такого не может быть", но он все же стал появляться - следует отнести его к багу именно изменения класса расширителем библиотеки и сбою именно функции get_value, которая вероятно вкупе с использование из той же библиотеки функций работы с шейдерами/матрицами/... и провоцирует наступление сбоя. Далее - только анализ кода правленной DLL'ки и эксперименты с нею могут подтвердить/опровергнуть ... Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 20 Апреля 2012 (изменено) Однако именно включение данного вызова в код проверки дельты времени в каждый цикл апдейта как раз и дает 100%-ную повторяемость проявления бага. Вот код, эквивалентный твоему, и которым ловлю баг: local oCTime = nil --/ запомненный счетчик времени (CTime) local bGetValue = nil --/ флаг подключения x-Ray Extension function OnSpawn(e) oCTime = game.get_game_time() bGetValue = (type(oCTime.get_value) == 'function') end function OnUpdate(e) --/ проверка наличия 'бага CTime' local oCTimeCur = game.get_game_time() --/ счетчик текущего игрового времени local iDiffSec = oCTimeCur:diffSec(oCTime) --/ длительность паузы между апдейтами (sec: ~0.1 ... 1.xx ...) if iDiffSec > 999 or iDiffSec < 0 then --/ обнаружен сбой (баг CTime)? local CTimeToLog = function(time,typ) local Y,M,D,h,m,s,ms = time:get(0,0,0,0,0,0,0) local sStr = Y.."."..M.."."..D.." "..h..":"..m..":"..s.." :"..ms if type(time.get_value) == 'function' then local lo,hi = time:get_value() --/ читаем биты счетчика времени log("%s:Test_CTime(%s)=>[%s]: hi|lo=[ %s | %s ]", sModule, typ, sStr, string.format("%08x",hi), string.format("%08x",lo) ) --/#~# else log("%s:Test_CTime(%s)=>[%s]:<%s>", sModule, typ, sStr, "Info") --/#~# end end log( string.rep("~",78) ) --/#~# log("%s:Test_CTime:[%s %s]:DiffSec=[%s]/(%s)", sModule, oCTimeCur:dateToString(0), oCTimeCur:timeToString(3), string.format("%5.3f",iDiffSec), string.format("%5.3f", e.delta) ) --/#~# CTimeToLog(oCTime,"pre") --/ вывод в лог значений предыдущего счетчика времени CTimeToLog(oCTimeCur,"cur") --/ вывод в лог значений текущего счетчика времени log( string.rep("~",78) ) --/#~# if os then log("%s:Test_CTime:[%s]:os.clock=[%s]:%s", sModule, os.date(), string.format("%5.3f",os.clock()), Get_MemUsage(true)) --/#~# end abort("%s:Test_CTime:DiffSec=[%s]~?:<%s>", sModule, tostring(iDiffSec), "Error!") end oCTime = oCTimeCur --/ (пере)запоминаем счетчик текущего времени, заменяя прежний --/ #!?!# опционально: предположительно провоцирует баг! ---------- if bGetValue then local lo,hi = oCTime:get_value() --/#?# читаем биты счетчика запомненного (а значит текущего!) времени end --/ --------------------------------------------------------------- end Обращу внимание(!), что "на сейчас" оставил только один последний вызов проверки битов запоминаемого счетчика (по сути баласт). СтОит закомментировать нижние строки с get_value, любые манипуляции в игре, в том числе и с окнами и с zone_informer'ом не дают сбоев. Если же при этих строках на сэйве, который уже упоминал, поиграться с шейпами и инвентарными окнами - вылет считай гарантирован именно по сбою старших битов счетчика. Также, вот этот код: function Check_CTime(oCTime, sMsg, is_abort) local lo,hi = nil,nil if bGetValue then --/#!# опционально: предположительно провоцирует баг! lo,hi = oCTime:get_value() --/#?# читаем биты счетчика времени end local Y,M,D,h,m,s,ms = oCTime:get(0,0,0,0,0,0,0) if Y < 2002 or Y > 2013 then local sTxt = sModule..":Check_CTime=["..Y.."."..M.."."..D.." "..h..":"..m..":"..s.." :"..ms.."]" if lo and hi then sTxt = sTxt..":hi["..string.format("%08x",hi).."]:lo["..string.format("%08x",lo).."]" end sTxt = sTxt..":"..tostring(sMsg)..":<Warning!>" if is_abort then abort( (debug and debug.traceback(sTxt)) or sTxt ) elseif debug then _G.to_log( debug.traceback(sTxt) ) else _G.to_log( sTxt ) end end end - стоИт на всех(!) кодах игры/мода, где проверяется дельта времени по CTime, что по примерным подсчетам дает работу с более сотней-двумя объектов, некоторые из которых почти также апдейтятся, хотя с несколько иными частотами и в других циклах/потоках. И ни разу пока в этих объектах не был зарегистрирован сбой. ИМХО, все же налицо связь бага с именно расширителем библиотеки + использование zone_informer'а. По-меньшей мере оба условия, как ранее выразился - катализатор проявления этого бага. Ну а "очень редкий" баг - следствие одной из этих причин. P.S. Сорри, под "zone_informer"ом имею ввиду несколько модифицированные коды именно твоего "Zones Editor"а. У меня этот скрипт адаптирован под актуальные для меня варианты "xrLua Extensiion" (функционал кейлоггера) и модуль нет-пакетов m_netpk (читалка). Порой в игре/разработке просматриваю (функционал информера!) формы и расположения различных объектов. Функционал редактора не использую, так что в данном контексте только отображение шейпов партиклами и их очистка может играть какую-то роль. Подметил, что чаще и быстрее всего баг с Ctime насупает после показа шейпов перехода со Складов на Бар + их очистке + открытий/закрытий инвентаря. - ну а то, что вашими тестерами не используется - несколько меняет картину и ... остается вероятным виновником именно расширитель, а в моем случае информер (кстати, также использует расширитель) - является именно катализатором. Добавлено через 134 мин.: P.P.S. Еще немного пищи для размышлений: Если заменить вызов проверки бага из циклов апдейта актора на иной - спровоцировать ошибку не удается: function Start_Test_CTime() --/ проверка: CTime --event("actor_update"):register(Test_CTime) --/ for debug CTime --/ заменяем апдейт актора на иной: level.add_call(Test_CTime, function() abort("Test_CTime:~>FATAL_ERROR!") end) end Вероятно все же все дело в стеке. При различных манипуляциях с окнами и пр. наличие в стеке постоянно перечитываемого счетчика приводит к его сбою/затиранию старшего бита (u64 - вероятно хранится не в общих регистрах). Вынос/смещение счетчика по отношению к апдейту актора - изменило условия/структуру стека, сохранив ту же частоту опроса (у меня DiffSec ~0.070 game-second). Хотя ... вероятно теперь что-то иное может давать сбой (???), т.к. если были сбои/затирания стека - то врядли они куда исчезли. Просто в каких то вариантах они не критичны. А вот что корежит стек - можно только предполагать. ;-) Изменено 20 Апреля 2012 пользователем Artos Поделиться этим сообщением Ссылка на сообщение
Artos 99 Опубликовано 26 Октября 2013 (изменено) ... эта функция запускает _precondition в новом потоке и ждет его завершения после чего ... Неверно! Запущенный коллбэк постоянно (аналогично апдейтам актора) вызывает функцию _precondition и, если она возвращает true, коллбэк отключается и вызывается функция _action Изменено 27 Октября 2013 пользователем ColR_iT Поделиться этим сообщением Ссылка на сообщение