www.open-tager.ru https://open-tager.ru/forum/ |
|
Каустик, объектная модель https://open-tager.ru/forum/viewtopic.php?f=5&t=4943 |
Страница 1 из 2 |
Автор: | LTagKirov [ 11 окт 2016, 18:49 ] |
Заголовок сообщения: | Каустик, объектная модель |
Для разминки мозгов решил побаловаться изучением исходников различных авторов Итак будем сравнивать Tommy vs Caustic, лтк пусть полежит в стороне, так как он будет мной некорректно оценен... Задача: найти и определить как добавить уязвимость к пулям от игрока с конкретным ИД Проект Томми, ------------ Файл начинается с аннотации /* ** main.c: логика ружья ** ** config - конфигурация пользовательского железа и игровые параметры; ** sound_notes - мелодии, используемые в игровой логике ** ** globals - общие макросы и дифайны (uchar = unsigned char и т.д.); ** hardware - общая работа с железом. управляет кнопками, светодиодами, подключает и инициализирует железо - там же находится и main(); ** miles - отвечает за передачу и приём ИК пакетов. использует таймер2 для ШИМа и таймер0 для измерений; ** sound - проигрывает звук и нотные последовательности, плюс дополнительно управляет светодиодом на повязке. таймер1; ** timer - игровые таймеры на таймере0. используются игровой логикой и звуком. точность 1/122сек. ** ** ** в файле не должно быть работы с железом. ** ф-ция init() - вызывается единожды после включения устройства; ** ф-ция main_loop - вызывается постоянно. сейчас работает за 14us, что приемлимо. ** главная задача - работать быстрее чем два минимальных пакета (выстрел - ~27ms). ** всё боолее менее логично идём посмотреть main_loop Код: //если пришёл пакет, то обрабатываем его if (incoming_ir.state != PACKAGE_EMPTY) { if (incoming_ir.state == PACKAGE_BROKEN) { //правило: пролетающая мимо пуля не перебивает звук выстрела //проверяем по выключенности прерывания переполнения таймера1 (так нельзя) if (!(TIMSK & (1<<TOIE2))) { // 5 мая 2015, исправление на Таймер2 if (player.alive==TRUE) { play_sound(sound_nearmiss); } //**** }; } else { if (incoming_ir.state == PACKAGE_COMMAND) { process_ir_command(); } else { //сначала проверям, чтоб игрок не был неуязвим и жив if ((!timer_vulnerable.active) && (player.alive)) { //и не убил сам себя отражением >> дЖва пейдждауна и >> Здесь при желании добавляем нужную нам модификацию !!! ;) if ((incoming_ir.id != player.id) || (incoming_ir.team != player.team) || CONFIG_SELFDAMAGE) { //а потом ещё и проверка на убийство члена команды if ((incoming_ir.team != player.team) || (CONFIG_FRENDLYFIRE) || (CONFIG_SELFDAMAGE)) { process_ir_hit(); }; }; }; }; }; incoming_ir.state = PACKAGE_EMPTY; //всё, пакет обработан, всё готово к следующему }; Всё задачяа решена Теперь переходим к другому образцу, собранному по канонам ООП ------------------------------------------------------------------- файл main Видим что то подобное, убрал лишнее Код: int main(void) { . . . Первая НЁХ DeviceInitializer IAnyDevice* device = deviceInitializer.initDevice("device.ini"); внутри этой инициализации девайса происходит создание устройства допустим c логикой "типа ружъё" ? else if (0 == strcmp(value, "rifle")) info << "Creating RIFLE device"; resultDevice = new Rifle; <кусь> Далее пропущу пусть желающие читают про RTOS ..... } Код ружья с вырезками Код: class Rifle : public IAnyDevice { public: Rifle(); void registerWeapon(); void init(const Pinout& pinout); void setDafaultPinout(Pinout& pinout); bool checkPinout(const Pinout& pinout); FUNCTION_NP(ConfigCodes::Rifle::Functions, Rifle, rifleReset); ///< Make rifle absolutely new FUNCTION_NP(ConfigCodes::Rifle::Functions, Rifle, rifleRespawn); ///< Do all things than needed on respawn (+play sound) FUNCTION_NP(ConfigCodes::Rifle::Functions, Rifle, rifleDie); ///< Say to rifle that player was killed /// Heartbeat head sensor -> rifle FUNCTION_NP(ConfigCodes::Rifle::Functions, Rifle, headSensorToRifleHeartbeat); FUNCTION_1P(ConfigCodes::Rifle::Functions, Rifle, riflePlayEnemyDamaged); ///< Play enemy damaged sound FUNCTION_1P(ConfigCodes::Rifle::Functions, Rifle, rifleShock); ///< Play enemy damaged sound FUNCTION_1P(ConfigCodes::Rifle::Functions, Rifle, rifleChangeHS); ///< Play enemy damaged sound DeviceConfiguration deviceConfig; RifleConfiguration config; RifleOwnerConfiguration rifleOwner; RifleState state{&config}; //DeviceParameters device; PlayerPartialState playerState{config.headSensorAddr}; private: constexpr static uint32_t maxNoHeartbeatDelay = 6500000; ..... void loadConfig(); void initSounds(); void makeShot(bool isFirst); void prepareAndSendShotMsg(); void distortBolt(bool isFirst); void magazineSensor(bool isConnected, uint8_t sensorNumber, bool isFirst); uint8_t getCurrentMagazineNumber(); void reloadAndPlay(); void detectRifleState(); bool isReloading(); bool isSafeSwitchSelected(); bool isShocked(); bool isHSConnected(); void updatePlayerState(); void playDamagerNotification(uint8_t state); void scheduleDamageNotification(uint8_t state); void checkHeartBeat(); void onCardReaded(uint8_t* buffer, uint16_t size); /// This function could be called any time when head sensor is connected. Double calling does not hurt anything void onHSConnected(); void onHSDisconnected(); ButtonManager* m_fireButton = nullptr; ButtonManager* m_reloadButton = nullptr; ... ButtonManager* m_magazine2Sensor = nullptr; IIOPin* m_vibroEngine = nullptr; IIOPin* m_fireFlash = nullptr; IIRTransmitter *m_irPhysicalTransmitter = nullptr; IIRPresentationTransmitter *m_irPresentationTransmitter = nullptr; SoundPlayer m_systemReadySound; SoundPlayer m_RFIDCardReaded; .... SoundPlayer m_hsSwitchRejected; PackageId m_registerWeaponPAckageId = 0; uint8_t m_state = WeaponState::ready; uint8_t m_currentMagazineNumber = 0; ///< zero means no magazine Interrogator m_buttonsInterrogator{"BtnsIntr"}; TasksPool m_tasksPool{"RiflPool"}; Time m_lastHSHeartBeat = 0; .... }; Примерно так приделывается логика обработки нашего волшебного объектного ружья Почему состояние игрока меняет метод оружия, а не игрок даёт команду ружью сменить своё состояние - потратить патроны. Странно игрок же важнее ружья... Код: m_tasksPool.add( [this]() { playerState.print(); updatePlayerState(); }, 5000000 ); и тд Сигнализация о попадании - попробуем определить откуда пришло событие Код: void Rifle::riflePlayEnemyDamaged(uint8_t state) { info << "Player damaged with state: " << state; playDamagerNotification(state); } А пришло оно отсюда из другого устройства HeadSensor ! Код: void HeadSensor::notifyIsDamager(DamageNotification notification) { info << "By the time " << notification.damager << " damaged " << notification.target; if (notification.damager != playerConfig.plyerMT2Id) return; if (!playerState.weaponsList.weapons().empty()) { uint8_t sound = (notification.damagedTeam == playerConfig.teamId) ? // testing for friendly fire >>> И вот сюда(или не сюда?) то мы можем добавить чит с определённым ИД игрока NotificationSoundCase::friendInjured : notification.state; // Now notifying over first attached weapon RCSPStream::remoteCall(playerState.weaponsList.weapons().begin()->first, ConfigCodes::Rifle::Functions::riflePlayEnemyDamaged, sound); } } >> RCSPStream::remoteCall( playerState.weaponsList.weapons().begin()->first, ConfigCodes::Rifle::Functions::riflePlayEnemyDamaged, sound); Почему проигрывание звука поражения игрока зависит от вида оружия которое он держит в руках ? Код: void HeadSensor::playerRespawn() { m_callbackStager.stage("respawn"); if (!playerState.respawn()) { } m_leds.blink(blinkPatterns.respawn); respawnWeapons(); Почему респаун головной повязки, вызывает респаун оружия ? Эти два класса никак особо не связаны между собой, описанны в разных файлах не являются потомками, а мы переплетаем их зависимостями, которые в целом не очевидны - если незнать логики (лазертага) почему должно быть именно так. Логичнее их изолировать, сделать класс игрок у которого есть повязка + оружие. Класс игрок уже вызывает методы повязки(надел) и методы оружия(подобрал с земли, зарядил), а когда повязка сама командует оружию зарядись без участия игрока и тд ... Или это нифига не ООП ... тише тише - глубоко вдохните ... или я просто непонимаю Просто интерестно увидеть картину так сказать более общим взглядом, может быть вначале было что-то похожее на проект, почему выбрали именно такую структуру, а не другую ? Если не пытатся понять объектную модель, а просто лепить методы по видимым доступностям, проблем не возникает, всё более менее складывается. В принципе такая модель(или обвёрка над процедурщиной) писать программу не мешает - работает же |
Автор: | Alexies [ 12 окт 2016, 23:47 ] |
Заголовок сообщения: | Re: Объектная модель Каустика ?! |
LTagKirov, уважаемый, я готов ответить на любые вопросы, давайте только они будут начинаться не со слов: "смотрите все, тут ООП-шная охинея, я нашел элементарную архитектурную ошибку", а со слов "я не понимаю, как взаимодействуют эти модули, и почему тут сделано так-то, объясните". Иначе будет выглядеть нелепо. ООП - не волшебная пилюля, и не гарантирует понимание кода кем угодно за 3 минуты. Также, возможности у девайса Джима и у моего девайса мягко говоря различаются, поэтому и код различается, и его количество. Для первого ознакомления сделайте, как любой программист: запустите doxygen, посмотрите графы взаимоотношений классов и многие вопросы прояснятся сами собой. В критичных местах в моём коде присутствует doxygen-разметка. Ну и задавайте вопросы, пусть даже в теме с таким забавным называнием А теперь поехали. Вопрос 1. LTagKirov писал(а): Примерно так приделывается логика обработки нашего волшебного объектного ружья Почему состояние игрока меняет метод оружия, а не игрок даёт команду ружью сменить своё состояние - потратить патроны. Странно игрок же важнее ружья... Код: m_tasksPool.add( [this]() { playerState.print(); updatePlayerState(); }, 5000000 ); и тд Наше волшебное объектное ружьё не меняет состояние игрока, как можно заметить, если прочитать на йоту больше кода. Эти строки заставляют оружие, грубо говоря, каждые 5 секунд запрашивать у повязки переменные, которые его интересуют, чтобы вывести их потом на дисплей. Эти переменные хранятся в playerState - экземпляре класса PlayerPartialState. Никакого управления состоянием игрока здесь не происходит. Предвосхищая ваш вопрос, отвечу, что повязка сама оповещает оружие, как только что-то меняется. Данный код лишь для повышения надежности. Вопрос 2 LTagKirov писал(а): Сигнализация о попадании - попробуем определить откуда пришло событие Код: void Rifle::riflePlayEnemyDamaged(uint8_t state) { info << "Player damaged with state: " << state; playDamagerNotification(state); } А пришло оно отсюда из другого устройства HeadSensor ! Код: void HeadSensor::notifyIsDamager(DamageNotification notification) { info << "By the time " << notification.damager << " damaged " << notification.target; if (notification.damager != playerConfig.plyerMT2Id) return; if (!playerState.weaponsList.weapons().empty()) { uint8_t sound = (notification.damagedTeam == playerConfig.teamId) ? // testing for friendly fire >>> И вот сюда(или не сюда?) то мы можем добавить чит с определённым ИД игрока NotificationSoundCase::friendInjured : notification.state; // Now notifying over first attached weapon RCSPStream::remoteCall(playerState.weaponsList.weapons().begin()->first, ConfigCodes::Rifle::Functions::riflePlayEnemyDamaged, sound); } } >> RCSPStream::remoteCall( playerState.weaponsList.weapons().begin()->first, ConfigCodes::Rifle::Functions::riflePlayEnemyDamaged, sound); Почему проигрывание звука поражения игрока зависит от вида оружия которое он держит в руках ? В данном случае речь идёт о сигнализации, что ранен враг. Пусть у нас 2 игрока, у каждого одно оружие. Логика такая: - Оружие игрока 1 поражает повязку игрока 2. - Повязка игрока 2 посылает широковещательный пакет: в меня попал игрок с id таким-то из команды такой-то. - Повязка игрока 1 принимает этот пакет, и автоматически вызывается функция HeadSensor::notifyIsDamager. Там проверяется: это моё оружие стреляло, или нет? И видит: да ведь это же моё оружие стреляло! И посылает своему оружию команду "проиграй звук попадания": RCSPStream::remoteCall(playerState.weaponsList.weapons().begin()->first, ConfigCodes::Rifle::Functions::riflePlayEnemyDamaged, sound); - У оружия, когда приходит пакет с такой командой, автоматически вызывается функция Rifle::riflePlayEnemyDamaged(uint8_t state). При этом state хранит информацию о том, что произошло именно: ранили врага, убили врага, или попали в своего. Цитата: Почему проигрывание звука поражения игрока зависит от вида оружия которое он держит в руках ? Не понимаю, где вы это взяли. Уведомление о попадании проигрывается первым оружием в списке присоединенных - это пока заглушка на будущее. Потом сделаю, чтобы именно тем, из которого произведены выстрелы, но это low priority. Вопрос 3 LTagKirov писал(а): Почему респаун головной повязки, вызывает респаун оружия ? Эти два класса никак особо не связаны между собой, описанны в разных файлах не являются потомками, а мы переплетаем их зависимостями, которые в целом не очевидны - если незнать логики (лазертага) почему должно быть именно так. Логичнее их изолировать, сделать класс игрок у которого есть повязка + оружие. Класс игрок уже вызывает методы повязки(надел) и методы оружия(подобрал с земли, зарядил), а когда повязка сама командует оружию зарядись без участия игрока и тд ... У меня почти так и сделано. Информация об игроке хранится в PlayerConfiguration и PlayerState. Инстансы этих классов есть у HeadSensor. Такая иерархия логична. У повязки есть всё об игроке. Список подсоединенного оружия хранится внутри PlayerState - "оружие принадлежит игроку". Принципы ООП здесь не нарушены. Состояние и характеристики игрока инкапсулированы в PlayerConfiguration и PlayerState. Конечно, можно сделать как-то по-другому, но это вряд-ли будет существенно "правильней". Ну есть ещё много аргументов, почему здесь сделано так, но нужно знать много нюансов, чтобы о них говорить. Если повязка ловит команду на респаун (по ИК, или радио), то она: - говорит игроку (=PlayerState), что пора респавниться (он оживает, восстановливает здоровье и т.п.) - говорит всем оружиям, которыми обладает игрок, что пока респавнится, а именно: проигрываеть звук респауна, обновляють патроны, да и просто активирлваться (если было деактивировано в связи со смертью игрока). Не вижу противоречий здравому смыслу. Код: void Rifle::rifleRespawn() { rifleReset(); rifleTurnOn(); updatePlayerState(); m_respawnSound.play(); info << "Rifle respawned\n"; } Таким образом два независимых класса, которые даже инстанциированы на двух независимых устройствах, просто обмениваются сообщением, всё логично. Повязка знает всё об игроке. Оружие - только о своих параметрах и состоянии, плюс - иногда получает информацию об игроке, чтобы вывести на экран и в дебаг-канал, на всякий случай. Ваше желание разделить игрока и повязку в разные классы понятно, в моём случае почти так и сделано. Но игрок, как класс, не может жить без реального физического устройства с микроконтроллером, которым является повязка LTagKirov писал(а): Или это нифига не ООП ... тише тише - глубоко вдохните ... или я просто непонимаю Всё нормально. Это ООП. Рекомендую более вдумчиво читать код. Но с чем я точно соглашусь - это что названия некоторых методов и классов не самые прозрачные. При разработке в одиночку трудно уследить за тем, чтобы всё называется правильно, потому что привыкаешь. В дальнейшем, давайте будем вежливее друг к другу. У меня нет секретов и взаправду всё работает. |
Автор: | LTagKirov [ 13 окт 2016, 00:09 ] |
Заголовок сообщения: | Re: Объектная модель Каустика ?! |
Alexies писал(а): Всё нормально. Это ООП. Рекомендую более вдумчиво читать код. Я давно уже не студент и одним из первых качеств кода считаю работоспособность программы, а как он устроен это уже интересно большей частью только отдельным товарищам А если ещё и автор имеется, самое то спросить напрямую - ответ наверняка получится полезнее если будет опубликован открыто.Цитата: Информация об игроке хранится в PlayerConfiguration и PlayerState. Инстансы этих классов есть у HeadSensor. То-есть получается повязка владеет экземпляром игрока, ну хорошо пусть так и будет - раз они неотделимы. А каким образом предполагалось реализовать (экземпляры классов - для поиска по коду) бронежилет подключаемый к игроку по радио или бронежилет это только одна из зон HeadSensor-а ? Возможно ли механически подобрать бронежилет на поле (буквально) и надеть его на себя как то зарегистрировав в комплекте своего оборудования ?ЗЫ. Ещё раз это не критика, интересна сама мысль проекта |
Автор: | Alexies [ 13 окт 2016, 01:14 ] |
Заголовок сообщения: | Re: Объектная модель Каустика ?! |
LTagKirov писал(а): Я давно уже не студент и одним из первых качеств кода считаю работоспособность программы, а как он устроен это уже интересно большей частью только отдельным товарищам Как код устроен важно в процессе добавления новых возможностей. Чтобы не приходилось всё переделывать. Я, конечно, говорю не о тривиальных модификациях вроде добавления перегрева ствола или изменения схемы мерцания светодиодов, а о серьезных нововведениях вроде смены оружия, удаленного группового редактирования настроек или оповещениях о попадании. LTagKirov писал(а): Цитата: Информация об игроке хранится в PlayerConfiguration и PlayerState. Инстансы этих классов есть у HeadSensor. То-есть получается повязка владеет экземпляром игрока, ну хорошо пусть так и будет - раз они неотделимы. А каким образом предполагалось реализовать (экземпляры классов - для поиска) бронежилет подключаемый к игроку по радио или бронежилет это только одна из зон HeadSensor-а ? Возможно ли механически подобрать бронежилет на поле (буквально) и надеть его на себя как то зарегистрировав в комплекте своего оборудования ?ЗЫ. Ещё раз это не критика, интересна сама мысль проекта Я так понял, под бронежилетом подразумевается жилет с датчиками? Есть несколько вариантов: - Проводной бронежилет с разъёмом - это уже поддерживается, просто одна из зон поражения. Можно установить коэффициент модификации урона на каждую зону. - Беспроводной бронежилет - пока не поддерживается. Он будет также зоной поражения, только реализованой иначе. С точки зрения кода повязки это выглядит так: есть интерфейс (абстракный класс) зоны поражения, который реализуется (наследуется) разными классами. Таким образом остальной код не знает, как там зона поражения внутри работает. Это называется dependency injection - классический шаблон проектирования, с которого, можно сказать, начинается ООП. Это в Caustic используется повсеместно. Для ассоциации любого предмета и игрока я использую RFID-метки. Игрок поднес карточку или браслет к бронежилету, и он законнектился по радио автоматически. Точно также уже работает горячая смена оружия. Поднес карточку к автомату, и он - твой. Повторю, это не теория, эта штука уже оттестирована и работает! - Беспроводной бронежилет без горячей замены - просто прописывается в его конфигах, что присоединен к такой-то повязке. Можно менять конфиг из андроид-приложения. И ни дай бог что-то перепрошивать LTagKirov писал(а): ЗЫ. Ещё раз это не критика, интересна сама мысль проекта Да нет проблем, просто это не выглядело, как интерес Я-то всегда за дружбу А тему можно переименовать как-то по-благозвучней, если форум позволяет |
Автор: | LTagKirov [ 13 окт 2016, 01:36 ] |
Заголовок сообщения: | Re: Объектная модель Каустика ?! |
Alexies писал(а): Поднес карточку к автомату, и он - твой. Повторю, это не теория, эта штука уже оттестирована и работает! И всё втихушку без "прогрессивных прорыв-прорывов" ай-яа-яай
|
Автор: | Alexies [ 13 окт 2016, 10:58 ] |
Заголовок сообщения: | Re: Объектная модель Каустика ?! |
LTagKirov писал(а): Alexies писал(а): Поднес карточку к автомату, и он - твой. Повторю, это не теория, эта штука уже оттестирована и работает! И всё втихушку без "прогрессивных прорыв-прорывов" ай-яа-яай Я описывал эту функциональность в свое ветке) Кстати, об исходном вопросе: LTagKirov писал(а): Задача: найти и определить как добавить уязвимость к пулям от игрока с конкретным ИД Тут есть два пути. Чтобы по-быстрому сделать "грязный хак" - можно добавить лишний if в функцию Код: void HeadSensor::catchShot(ShotMessage msg) Если делать по-нормальному, то нужно создать класс-модификатор урона DamageModificator, который будет при загрузке из конфиг-файла считывать набор id и их модификаторов. Внутри вышеобозначенной функции нужно вызвать что-то вроде DamageModificator::modify(ShotMessage& msg), и он изменит урон. Отвечать за обработку команд по удаленной настройке этих модификаторов тоже будет DamageModificator. Соответственно, в него можно будет добавить и другие условия модификации: по team id, по времени, ещё как-то. |
Автор: | Pacifist [ 13 окт 2016, 12:46 ] |
Заголовок сообщения: | Re: Объектная модель Каустика ?! |
Alexies писал(а): ...Для ассоциации любого предмета и игрока я использую RFID-метки. Игрок поднес карточку или браслет к бронежилету, и он законнектился по радио автоматически... Думал я сделать связь повязка-ружьё через тело (BodyCom). Взял игрок любое в руки - и оно стреляет с его ИД. Поднял коробку с патронами - пополнил боезапас. Даже прикупил у микрочипа специально для этого заточенные MCP2035. Но дальше как то не сложилось, так и ждут лучших времён. |
Автор: | Zorand [ 13 окт 2016, 18:05 ] |
Заголовок сообщения: | Re: Каустик, объектная модель |
Три года назад я слышал про работы над связью с девайсами через маломощное магнитное поле, там какая-то аббревиатура типа Close Range Magnet Field или типа того. Насколько я обрывочно помню - проводились параллели с магнитными карточками в метро и пей-вейв, кажется. Опять же - тогда просили не разглашать, но теперь думаю уже не актуально - все же три года форы и я совершенно не в курсе подробностей об успехе или неудаче в ходе разработки. На первый взгляд выглядит очень привлекательно, по-моему, радиус действия около 10-20 см (настраивается, кажется), т.ч. датчик/приемник на руке будет надежно устанавливать связь с предметом взятым в руки, будь то оружие или любой другой игровой предмет в системе. -Вдруг кому пригодится. |
Автор: | Claw [ 13 окт 2016, 19:03 ] |
Заголовок сообщения: | Re: Каустик, объектная модель |
Near-field magnetic induction communication ? https://en.wikipedia.org/wiki/Near-fiel ... munication |
Автор: | Pacifist [ 13 окт 2016, 20:11 ] |
Заголовок сообщения: | Re: Каустик, объектная модель |
LTagKirov писал(а): 10..20 см это не расстояние. С типовыми дросселями на 100 мкг получалось около 50 см. Но это во первых колхоз допустимый только в пионерском хсл. Во вторых требуется питание и потребляет +20 ма. Обычные рфиде карточки практичнее будет. А для связи нрфки Наверное таки да. Особенно учитывая копеечную стоимость RFID. Положил в кармашек на рукаве - и тот же эффект. |
Страница 1 из 2 | Часовой пояс: UTC + 3 часа [ Летнее время ] |
Powered by phpBB® Forum Software © phpBB Group http://www.phpbb.com/ |