|
|
Модератор форума: Dimitro |
Форум TrinityCore [TrinityCore] Help Проблема с SetFacingToObject и KilledMonsterCredit (C++ npc scripts) |
Проблема с SetFacingToObject и KilledMonsterCredit |
Всем привет. Опять же, чтобы не создавать много тем изложу всё в одной. Skyfire 5.4.8 core.
1. Есть скрипт: Код Creature *unnamed_npc = me->SummonCreature(параметры); me->SetFacingToObject(summoned_npc); // Не работает summoned_npc->SetFacingToObject(me); // Работает В чём может быть причина не срабатывания второй строки? Посмотрел в теле метода SetFacingToObject, есть проверка на движение нпц, но в момент вызова метода, нпц стоят на месте. Возможно причина в том, что засуммоненный нпц засуммоннен и методу это не нравится(других методов по спавну что-то не нашёл)? 2. Часть скрипта в методе DamageTaken: Код Unit* victim = me->GetVictim(); if(Player* player = victim->ToPlayer();) player->KilledMonsterCredit(NPC_ID); Ядро крашится при вызове метода KilledMonsterCredit. Разбираться с крашлогом пока скилла не хватает, прошу помочь. Также хотел уточнить, данный метод срабатывает на игроках, находящихся в группе с объектом player? Если нет, то как это реализовать?
Сообщение # 1 написано 20.04.2017 в 12:07
|
По поводу первого не подскажу, нужно копать. Второе - проверьте код метода Player* Unit::ToPlayer(void). Если там используется reinterpret_cast, как раньше, то полагаться на приведенную Вами конструкцию нельзя.
Такое приведение типов не транслируется ни в какие инструкции, компилятор лишь производит изначальную проверку его применимости к контексту. А т.к. дифференцировка полиморфных типов осуществляется во время исполнения, никакая проверка на безопасность выполняемого действия наложена быть не может (т.е. в случае, если приведение применяется к объекту не типа Player, а, скажем, Creature, результирующее значение останется ненулевым адресом, указывающим на ту же область памяти). Интересующие Вас приведения являются static_ или dynamic_ - cast'ами. Для решения проблемы Вам понадобится либо использовать другой способ приведения типов, либо применять устаревшую проверку со сравнением результата выполнения метода GetTypeID. П.С: внутри контекста условных выражений ';' не ставится. |
По типу такого? Не помогло.
Код Unit* victim = me->GetVictim(); Player* player = victim->ToPlayer(); if (player->GetTypeId() == TYPEID_PLAYER) player->KilledMonsterCredit(NPC_ID); //Будет засчитано всей группе? Это случайно туда попало при копипасте)
Сообщение # 3 написано 23.04.2017 в 17:13
|
Теоретически такой вариант безопасен (если там, конечно, GetTypeId не стал виртуальным методом, переопределенным в Player'е), но так лучше не делать. Проверяйте перед тем, как выполнить приведение.
Помните, что Вам не обязательно нужна переменная, чтобы выполнить какое-либо одноразовое действие. Например, если жертва в виде игрока нужна только внутри условного блока для вызова одного метода, можно запросто поступить так: Код Unit* const victim = me->GetVictim(); if(victim->GetTypeId() == TYPEID_PLAYER) victim->ToPlayer()->KilledMonsterCredit(NPC_ID); Я не могу сейчас посмотреть код в человеческих условиях, потому Вам либо придется скидывать мне тела для всего, что здесь использовалось, либо запускать отладчик. П.С: Удостоверьтесь, что Unit* Unit::GetVictim(void) не вернет в данном контексте nullptr, иначе причина краша весьма очевидна. |
Добавлю и от себя:
П.С: Удостоверьтесь, что Unit* Unit::GetVictim(void) не вернет в данном контексте nullptr, иначе причина краша весьма очевидна. Если же крашется, как сказал автор, при вызове метода KilledMonsterCredit, значит victim->ToPlayer() сработал. Только если автор конечно же ничего не перепутал или же ему не выдало не правильную информацию( что у меня часто бывает, столько проблем иногда... не разгрести). А вообще да - Из собственного опыта советую вот этот код Код Unit* victim = me->GetVictim(); if(Player* player = victim->ToPlayer();) player->KilledMonsterCredit(NPC_ID); Цитата if(Unit* victim = me->GetVictim()) if(Player* player = victim->ToPlayer()) player->KilledMonsterCredit(NPC_ID); Речь не о const идёт, а о простой проверке на null всего что может быть, даже в абсурдных случаях и такие указатели как me сюда можно добавить ещё. Ну выше перед ифов if(me)if(Player*...)... В ядре Бог знает что происходит когда оно долго работает...указатели толи удаляются, толи имеют кривые адреса, толи ещё что-то, а в итоге крашатся методы этих классов-указателей. Это было такое отступление, по поводу темы - у меня тоже встречаются краши с этим методом, только я вызываю KilledMonster Иногда крашит, иногда нет, точнее в некотором коде краша вообще нет, а в некоторых бывает..связано это опять же с указателями, с не верными адресами, удалёнными уже указателями, выйденным игроком и прочим..избавился от такого собственнонаписанного краша благодаря кучам проверок на существование указателя, игрока и прочего( в общем ересть, но работает)и то такое было только в одном месте вроде(сам таким образом создал краш с указателями и как раз этим методом)
Сообщение # 5 написано 23.04.2017 в 20:49
|
Еще раз повторю, что в этом нет смысла, если там reinterpret_cast. Он ничего не проверяет, а следовательно nullptr никогда не вернет. Речь не о const идёт, а о простой проверке на null всего что может быть, даже в абсурдных случаях и такие указатели как me сюда можно добавить ещё. Ну выше перед ифов if(me)if(Player*...)... В ядре Бог знает что происходит когда оно долго работает...указатели толи удаляются, толи имеют кривые адреса, толи ещё что-то, а в итоге крашатся методы этих классов-указателей. Это было такое отступление, по поводу темы - у меня тоже встречаются краши с этим методом, только я вызываю KilledMonster Иногда крашит, иногда нет, точнее в некотором коде краша вообще нет, а в некоторых бывает..связано это опять же с указателями, с не верными адресами, удалёнными уже указателями, выйденным игроком и прочим..избавился от такого собственнонаписанного краша благодаря кучам проверок на существование указателя, игрока и прочего( в общем ересть, но работает)и то такое было только в одном месте вроде(сам таким образом создал краш с указателями и как раз этим методом) Это не просто плохой, а очень плохой совет, на самом деле. Вместо того, чтобы выяснить причину нестабильности и избавиться от нее, Вы предлагаете привнести еще и лишние вычисления, не понимая даже того, как и когда они помогают. Добавили краш - дебажте.
Сообщение # 6 написано 23.04.2017 в 21:09
|
Еще раз повторю, что в этом нет смысла, если там reinterpret_cast. Он ничего не проверяет, а следовательно nullptr никогда не вернет. Немного провтыкал) Код if(Unit* victim = me->GetVictim()) if(victim->GetTypeId() == TYPEID_PLAYER) if(Player* player = victim->ToPlayer()) player->KilledMonsterCredit(NPC_ID); Вот так правильный будет, но суть была в том, что if(Unit* victim = me->GetVictim()) данное присвоение нужно проверять на nullptr, собственно что Вы и говорили. А по поводу моего краша подобного..ядро было в релфиздебинфо и под gdb, лог соответствующий выводился, но какой-то часто он кривой был(в цепочки файлах, т.е последовательности вызово методов вконце указывается один файл, а в следующем действии отображается, что был вызван совершенно иной файл), такое бывало часто, но не всегда. Да и вообще бывает бред, ядро работает, а потом после какого-то рестарта игрок подходит к скриптованному нпц, открывает госсип и краш, в исходниках удаляю объектник данного файла со скриптом и после рекомпила краша нет, хотя сам файл не редактировался, а без рекомпила ничего не помогло бы, то есть бока какие-то на уровне системы чтоле.. и достаточно сюрпризов разных за всё время.. А по крашу - дебаг лог уже не показывал далее вызовов методов особо, на кил монстре и останавливалось логирование, мне понятно было что беда с указателем, долго игрался как проверить на то, что указатель существует или он верный, ну вот и сделал кучу проверок разный, дабы было больше вероятности, что будет всё ок, т.к меня лично краш достал за длительное время своего существования, притом не понятно каким образом вызывается краш. Действие это можно легко спровацировать, но краша нет, а этот уникальный эксклюзивный случай не понятен, многое перепробовалось для того, чтобы вызвать его, но успеха не было, осталось в конечном итоге только сделать проверки на то, что если указатель кривой, значит ничего делать не нужно. Вроде я всё правильно и сделал, суть была в том, что нужно было запомнить игрока при определённом действии, а через минуту вызвать для него нужный метод, за минуту игрок может выйти, может что угодно сделать, а указатель то остаётся, поэтому так и получилось) При попытке вызвать краш, как сказал, пробовалось многое, и выход, и достаточно остальных действий, но выход был - создание проверок, поэтому на текущей момент не считаю что это плохо..)
Сообщение # 7 написано 23.04.2017 в 22:32
|
-.- что if(Unit* victim = me->GetVictim()) данное присвоение нужно проверять на nullptr, собственно что Вы и говорили. Это было только предположение, я на данный момент не имею развернутого решения, чтобы это проверить. Но, если бы проблема была здесь, то, как Вы подметили, упало бы скорее всего на ToPlayer, ибо разыменовывание nullptr. Так или иначе, как я уже сказал, необходимо видеть тела всего, что здесь вызывается, чтобы сделать однозначный вывод. Действие это можно легко спровацировать, но краша нет, а этот уникальный эксклюзивный случай не понятен, многое перепробовалось для того, чтобы вызвать его, но успеха не было, осталось в конечном итоге только сделать проверки на то, что если указатель кривой, значит ничего делать не нужно. Вроде я всё правильно и сделал, суть была в том, что нужно было запомнить игрока при определённом действии, а через минуту вызвать для него нужный метод, за минуту игрок может выйти, может что угодно сделать, а указатель то остаётся, поэтому так и получилось) При попытке вызвать краш, как сказал, пробовалось многое, и выход, и достаточно остальных действий, но выход был - создание проверок, поэтому на текущей момент не считаю что это плохо..) Я не уверен, что правильно понял это объяснение, но отсутствие достоверного способа воспроизведения проблемы (т.е. ситуация, когда попытка не всегда приводит к возникновению) без очевидного псевдослучайного ветвления тестируемого алгоритма чаще всего свидетельствует о наличии ошибки, связанной с многопоточной природой отлаживаемой программы (ядра). Это может стать довольно серьезной трудноуловимой проблемой, особенно в столь масштабном проекте, как TrinityCore. По поводу игрока - это очень печально, если TrinityCore не предоставляет возможностей удобно и безопасно обрабатывать такие ситуации. В этом случае рекомендуется написать обработчик самостоятельно, чтобы по сотне раз не вставлять такие вот проверки. Чем безопаснее и компактнее решение - тем проще его сопровождать и развивать. Лучше всего в этом случае запуститься из под отладчика. С его помощью можно просматривать алгоритм в любом темпе, отслеживать и даже контролировать любые изменения в памяти процесса, настраивать точки останова по определенному условию, выражение которого учитывает отладочные символы (т.е. проверять не по адресам, а прямо по именам переменных). Так значительно проще отлавливаются и исправляются проблемы, на заглушение которых затрачиваются драгоценные ресурсы времени исполнения в Вашем случае. |
Это известная проблема, и разработчики TrinityCore рекомендуют никогда не использовать указатели на наследников WorldObject в скриптах именно по этой причине
Решение: Вместо указателей нужно хранить гуиды, а в месте, где нужно обратиться к самому объекту, писать подобный код Код if (Creature* creature = ObjectAccessor::GetCreature(*me, guid)) Таким образом мы получаем либо валидный указатель, либо nullptr, и исключаем кейс с ненулевым указателем содержащим невалидный адрес |
| |||
| |||