Jump to content

Search the Community

Showing results for tags 'гайд'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Russian Section
    • Новости и объявления
    • Пиратия: Документация
    • Пиратия: Релизы
    • Пиратия: Разработка
    • Пиратия: Web
    • Пиратия: Помощь
    • Совместные проекты / набор команды
    • Доска объявлений
    • Программирование
    • Оффтопик
    • Корзина
  • English Section
    • News & Announcements
    • Guides
    • Releases
    • Development
    • Web
    • Questions & Help
    • Shared Projects / Team search
    • Paid services & Requests
    • Programming
    • Offtopic
    • Recycle bin
  • Portuguese Section
    • Dúvidas & Ajuda
  • Spanish Section
    • Preguntas y Ayuda
  • Servers
    • Russian servers
    • English servers

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Found 12 results

  1. Дистанционная атака у Целительниц и Колдуний Автор: @Am*n9ma Открываем файл SkillInfo.txt и находим строку: 0036 Короткий посох 1 -1,10 1,-1 1,9 1,-1 -1 0 0 0 -1 -1 1 1 3 900 4 1 0 0 0 0 0 0 0 0 0 0 Skill_Melee_End 0 0 0 atk=atk(0)+5 0 0 0 0 0 0 0 0 0 0 0 2 1 7,8,9 0 128 -1 0 0 -1 0,0 0,0 0 6 -1 1015 4000 -1 2 131 0 0 0 0 1 0 0 0 0 0 Заменяем на: 0036 Короткий посох 1 -1,10 1,-1 1,9 1,-1 -1 0 0 0 -1 -1 1 1 3 900 4 1 0 0 0 0 0 0 0 0 0 0 Skill_lackart_End 0 0 0 atk=atk(0)+5 0 0 0 0 0 0 0 0 0 0 0 2 1 7,8,9 0 128 -1 0 0 -1 0,0 0,0 0 6 -1 1015 4000 -1 2 131 0 0 0 0 1 0 0 0 0 0 Далее открываем SkillEffect.lua и просматриваем есть ли у нас функция Skill_lackart_End(). Если нету то добавляем, если есть то заменяем: --Дистанционная атака у Цилительниц и Колдуний function Skill_lackart_End ( ATKER , DEFER , sklv ) if ValidCha(ATKER) == 0 then LG ( "luascript_err" , "function Skill_lackart_End : ATKER as null" ) return end if ValidCha(DEFER) == 0 then LG ( "luascript_err" , "function Skill_lackart_End : DEFER as null" ) return end local ChaName_ATKER = GetChaDefaultName ( ATKER ) local ChaName_DEFER = GetChaDefaultName ( DEFER ) dmg = Atk_Dmg( ATKER, DEFER ) Hp_Endure_Dmg( DEFER , dmg ) Take_Atk_ItemURE ( ATKER ) Take_Def_ItemURE ( DEFER ) Check_Ys_Rem ( ATKER , DEFER) end P.S.: Просьба отписаться как получилось. Спасибо @insider за намёк Спасибо @lyon за реализацию у нас на сервере © Pastuh
  2. Установка сервера Пиратии (Tales of Pirates, Pirate King Online) Всем привет! В данном гайде я расскажу как установить и настроить сервер Пиратии шаг за шагом на примере серверных файлов версии 1.3х. Под версией 1.3x подразумеваются самые распространенные версии игры, которые сегодня используются большинством проектов: 1.35, 1.36, 1.38 и 1.39. Это деление условно и все версии 1.3x совместимы между собой. Например, клиент версии 1.36 может работать с сервером версии 1.39. Также существуют серверные файлы версии 2.x и скомпилированные из исходных кодов, например, файлы Corsairs Online. Их так же можно установить и настроить следуя настоящей инструкции, но процесс установки может в определенной мере отличаться. Гайд предназначен для новичков в нашем Сообществе администраторов и разработчиков серверов Пиратии и содержит подробную инструкцию с изображениями, которая позволит установить сервер с нуля. По прочтении статьи читатель сможет запустить свой игровой мир и войти в него используя игровой клиент. Содержание гайда: 1. Вам понадобится; 2. Архитектура сервера Пиратии; 3. Установка Microsoft SQL Server и SQL Server Management Studio; 4. Установка и настройка серверных файлов; 5. Запуск сервера; 6. Проверка соединения с игровым сервером; 7. Остановка игрового сервера; 8. Подключение игрового клиента к серверу; 9. Распространенные ошибки и их исправление. Приступим к установке и настройке нового игрового мира. Первым шагом определим инструменты, которые понадобятся для успешного выполнения поставленной задачи. 1. Вам понадобится 0. Теоретическая подготовка. Основы работы с операционной системой семейства Windows, базовые знания архитектуры "клиент-сервер", понимание терминов "База данных (БД)" и "Система управления базами данных (СУБД)". Поскольку те или иные программы, а также сообщения об ошибках могут быть на английском языке, то понадобится знание английского языка начального уровня. 1. Компьютер под управлением операционной системы семейства Windows (серверные редакции Server и пользовательские XP/Vista/7/8/10). Исполняемые файлы сервера скомпилированы под архитектуру x86 (Win32), поэтому система должна поддерживать 32-разрядный режим работы. К аппаратной части предъявляются следующие требования: многоядерный процессор с частотой от 2.0 ГГц; от 4 Гб оперативной памяти; 2 Гб свободного места на диске для серверных файлов, баз данных и файлов журнала ("логов"); сетевое подключение со скоростью от 10 Мбит/с на 1000 клиентов. Таким образом, исходя из современных реалий, можно сделать вывод, что игровой сервер предъявляет невысокие требования к железу, поэтому вышеприведенные характеристики носят скорее рекомендательный и условный характер. Например, Вы можете запустить игровой сервер и на более слабом компьютере, но это, в свою очередь, может привести к замедлению его работы, а значит и к задержкам у игроков, что отразится на комфорте процесса игры. Если же Вы планируете запустить сервер только для себя (одиночная игра, проведение различных тестов, написание скриптов), то Вам не нужно заботиться о производительности сервера, а требования к железу будут менее строгими. 2. Система управления базами данных (СУБД) Microsoft SQL Server. Именно такую СУБД выбрали разработчики игрового сервера Пиратии для хранения состояния игрового процесса: учетных записей пользователей, персонажей, гильдий, кораблей и других объектов. Существует много версий Microsoft SQL Server. В гайде я буду использовать бесплатную версию Microsoft SQL Server 2017 Express, которую можно скачать с официального сайта Microsoft. Несмотря на ограничения Express редакции, её возможностей вполне хватит для обеспечения работы игрового сервера. Для управления SQL-сервером Вам понадобится SQL Server Management Studio, который также можно найти на сайте Microsoft. 3. Серверные файлы ("сборка"). Это приложения, файлы и скрипты, которые служат для создания и поддержания игрового процесса. В минимальной конфигурации должны включать в себя серверные приложения AccountServer.exe, GroupServer.exe, GameServer.exe и GateServer.exe. Функции и задачи данных приложений будут рассмотрены далее. Найти сборку сервера можно в разделе "Сборки серверов" на нашем форуме или в Архиве полезных файлов для сервера и клиента. В качестве примера я буду использовать серверные файлы Pirate King Online 1.38. 4. Базы данных. В соответствии с пунктом (2) определяют структуру хранения игровых объектов на диске и используются игровым сервером для хранения информации о состоянии игрового мира. Представлены файлами GameDB_Data.mdf, GameDB_log.ldf, AccountServer_Data.mdf и AccountServer_log.ldf. Как правило, поставляются вместе с серверными файлами. Если в скачанных Вами серверных файлах нет баз данных, то их можно найти в другой сборке, либо в Архиве полезных файлов для сервера и клиента. 5. Генератор паролей для .cfg. Кроме прочих настроек, в конфигурационных файлах сервера (.cfg) указываются данные для подключения к SQL-серверу, в том числе и пароли пользователей баз данных GameDB и AccountServer. В целях безопасности пароли в конфигурационных файлах должны быть зашифрованы специальным алгоритмом, для чего служит данная программа. 6. Текстовый редактор для редактирования конфигурационных файлов сервера. Подойдет стандартный блокнот (notepad.exe), но я рекомендую программу Notepad++, которая на порядок удобнее обычного блокнота. 7. Игровой клиент для подключения к установленному игровому серверу и его тестирования. Вы можете подобрать игровой клиент к выбранной сборке сервера в теме "Официальные клиенты" на нашем форуме. 8. IP Changer. Перед подключением к серверу игровому клиенту необходимо задать сетевой адрес по которому подключаться. Данная программа предназначена для настройки адреса игрового сервера в клиенте. 9. Программа для создания аккаунтов. Для того, чтобы войти в игровой мир, Вам нужна учетная запись игрока или администратора, которую можно зарегистрировать с помощью приведенной программы. После того, как мы сформулировали основные требования и определили необходимые инструменты для установки сервера Пиратии и подключения к нему клиента, проведем небольшой экскурс по архитектуре игрового сервера, чтобы получить понимание процесса его настройки и запуска в будущем. 2. Архитектура сервера Пиратии Архитектура сервера приведена на рисунке ниже. Она позволяет понять как в общем случае устроены серверные файлы и каковы взаимосвязи между серверным программным обеспечением: что в него входит, что является сервером и что является клиентом по отношению друг к другу. Как видно из схемы в состав системы входят AccountServer, GameServer, GroupServer, GateServer, Microsoft SQL Server с базами данных AccountServer и GameDB, а также игровой клиент (Client). Всё клиент-серверное взаимодействие между приложениями осуществляется с помощью сетевых пакетов по протоколу TCP. Изучим для чего нужен каждый элемент системы. 1. Игровой клиент (Client) - приложение с которым взаимодействует конечный пользователь (игрок). Визуализирует состояние игрового мира вокруг персонажа игрока, полученное от сервера через сеть Интернет. Под визуализацией понимается отрисовка игровой карты с объектами на ней (например, суша и море, деревья, здания, дороги), персонажей других игроков, монстров, неигровых персонажей (NPC) и так далее - всего того, что на своем мониторе видит игрок в процессе игры. Кроме визуализации клиент посредством графического интерфейса пользователя (GUI) предоставляет последнему возможность влиять на состояние игрового мира и процесс игры с помощью клавиатуры и мыши, отправляя на сервер различные действия, например, создание персонажа, перемещение персонажа в игровом мире, атака монстров, взаимодействие с NPC, отправка сообщений в чат и другие действия, которые разрешены на сервере правилами игры. Для пользователя клиент является игрой в обычном понимании. В рассматриваемой системе игровых клиентов может быть от 1 до нескольких тысяч. Подключается через 1973 порт к GateServer. 2. GateServer - принимает входящие соединения через 1973 порт от игровых клиентов и выступает в роли шлюза, через который игровой клиент взаимодействует с GroupServer, AccountServer и GameServer. Является сервером на 1971 порту по отношению к GameServer и клиентом на 1975 порту по отношению к GroupServer. Для распределения нагрузки в системе может быть несколько GateServer. Например, можно настроить сервер таким образом, что игроки из России подключаются к GateServer, который расположен в Москве, а игроки из Европы подключается к GateServer, который запущен на хостинге в дата-центре, расположенном в Германии, что позволит снизить задержки (пинг) при передаче сетевых пакетов от клиента на сервер и обратно. В целях безопасности игрового процесса GateServer шифрует весь сетевой трафик, который передается между сервером и клиентами. 3. GroupServer - отвечает за создание и удаление персонажей с аккаунта пользователя, систему друзей и отрядов, общение между игроками в чатах (мировой, торговый, отряд, гильдия, ЛС) и диалогах. GroupServer также участвует в процессе аутентификации игроков на сервере, передавая пакеты аутентификации, полученные от GateServer, далее в AccountServer. Является сервером на 1975 порту по отношению GateServer и клиентом по отношению к AccountServer на 1978 порту. Кроме того, подключается в роли клиента через 1433 порт к Microsoft SQL Server для работы с базой данных GameDB. Как и в случае с GateServer, в системе может быть несколько GroupServer для распределения нагрузки, но, как правило, в реальных проектах используется один экземпляр GroupServer. 4. AccountServer - нужен для аутентификации игроков на сервере путем сравнения полученных от пользователя логина и пароля с соответствующими логином и паролем в базе данных. Если игрок ввел верные логин и пароль, то он допускается к игре на сервере, в противном случае получает сообщение об ошибке авторизации и отключается от сервера. Является сервером по отношению к GroupServer на порту 1978. Работает с базой данных AccountServer, поэтому является клиентом Microsoft SQL Server на порту 1433. Как и в случае с GateServer, в системе может быть несколько AccountServer для распределения нагрузки, но обычно в реальных проектах используется один экземпляр AccountServer. 5. GameServer - основной игровой сервер, где осуществляется вся логика игры. Обеспечивает игровой процесс и синхронизацию состояния игрового мира с игровыми клиентами: является клиентом на 1971 порту по отношению к GateServer, через который принимает действия от игровых клиентов, проверяет их корректность, и, в случае соблюдения установленных правил игры, вносит изменения в состояние игрового мира, после чего оповещает об этом все остальные игровые клиенты. Параллельно с обработкой действий игровых клиентов, с определенной частотой (обычно 20 Гц) обновляет игровой мир, например, для создания погодных явлений или управления монстрами с помощью искусственного интеллекта (AI). Для сохранения состояния игрового мира использует базу данных GameDB и является клиентом Microsoft SQL Server на порту 1433. GameServer является самым высоконагруженным элементом в системе и потребляет больше всего вычислительных ресурсов и памяти, поэтому в реальных проектах нагрузка распределяется между от 3 до 5 GameServer. В игре существует три основные локации: Аскарон, Магический Океан и Великий Синий Океан размером 4096 x 4096 условных единиц - это самые большие локации в игре. Помимо того факта, что они будут занимать много памяти в ОЗУ, в этих локациях одновременно могут быть тысячи игроков, монстров и NPC, следовательно, целесообразно распределить нагрузку между несколькими экземплярами GameServer и запускать основные локации по одной вместе с частью второстепенных на разных GameServer. 6. Microsoft SQL Server и базы данных AccountServer, GameDB - Microsoft SQL Server является механизмом, с помощью которого AccountServer работает с одноименной базой данных AccountServer, а GameServer и GroupServer работают с базой данных GameDB, используя язык SQL. Является сервером на порту 1433 по отношению к AccountServer, GameServer и GroupServer. В базе данных AccountServer хранятся игровые аккаунты и различная служебная информация, например, логи. В базе данных GameDB хранятся персонажи, предметы, гильдии, корабли, списки друзей и менторов, дублируется список аккаунтов для их связи с персонажами, кроме того, каждому аккаунту здесь присваивается уровень GM и секретный код, так же присутствует и служебная информация. Следует отметить, что рассмотренная архитектура является самой распространенной и применяется в большинстве проектов, но Вы можете перестраивать её под свои нужды: 1. Вы можете изменить стандартные порты 1433, 1971, 1973, 1975, 1978 на любые другие не зарезервированные порты; 2. Вы можете переименовывать базы данных AccountServer и GameDB (потребуется редактирование некоторых исполняемых файлов сервера); 3. Вы можете запускать несколько экземпляров Microsoft SQL Server, GateServer, GroupServer, AccountServer, GameServer и настраивать взаимосвязи между ними в целях распределения нагрузки и повышения производительности игрового процесса; 4. В систему могут быть добавлены новые элементы, например, сервер встроенного в клиент магазина игровых предметов (IGS), сервер для фильтрации исходящих от клиентов пакетов (FilterServer), сервер "оффлайн" ларьков (StallServer), который позволяет игрокам торговать в ларьках не находясь в игре. Теперь, когда Вы получили представление об архитектуре и основных элементах сервера Пиратии, приступим к его установке и настройке. Начнем с установки Microsoft SQL Server. 3. Установка Microsoft SQL Server и SQL Server Management Studio 3.1 Установка Microsoft SQL Server Перейдите на страницу загрузки Microsoft SQL Server 2017 Express и нажмите кнопку "Скачать". На Ваш компьютер будет загружен дистрибутив SQLServer2017-SSEI-Expr.exe. Запустите дистрибутив SQLServer2017-SSEI-Expr.exe от имени администратора и выберите тип установки "Пользовательский". Выберите директорию на диске, куда будет загружен пакет установки, необходимый для установки экземпляра Microsoft SQL Server, и нажмите кнопку "Установить". Начнется процесс загрузки и распаковки пакета установки. Дождитесь его окончания. После загрузки и распаковки пакета установки должно открыться окно "Центр установки SQL Server". Если оно по какой-либо причине не открылось, перейдите в директорию, которую Вы указали в процессе загрузки пакета установки (расположение носителя), и запустите центр установки SQL Server вручную (файл SETUP.exe). В окне центра установки SQL Server на вкладке "Установка" выберите пункт меню "Новая установка изолированного экземпляра SQL Server или добавление компонентов к существующей установке". Начнется процесс установки экземпляра SQL Server 2017. Примите условия лицензионного соглашения и нажмите кнопку "Далее". Убедитесь, что в системе соблюдены все условия для успешной установки SQL Server на вкладке "Глобальные правила", после чего нажмите кнопку "Далее". При возникновении проблем примите меры для их устранения и продолжите установку. На странице "Центр обновления Майкрософт" по Вашему усмотрению установите флажок "Использовать Центр обновления Майкрософт для проверки наличия обновлений" и нажмите кнопку "Далее". Программа установки проверит наличие обновлений и при необходимости их установит. Далее программа установки еще раз проведет диагностику системы на наличие потенциальных проблем, которые могут возникнуть в процессе установки Microsoft SQL Server 2017. Убедитесь, что потенциальных проблем не обнаружено. Правило "Брандмауэр Windows" может быть в состоянии "Предупреждение", если включен Брандмауэр Windows. Для продолжения установки это не критично, но могут возникнуть проблемы в будущем, если Вы захотите обеспечить удаленный доступ к экземпляру SQL Server. Чтобы их избежать, Вам нужно будет открыть порт для подключения к SQL Server (по умолчанию 1433) в фаерволе Windows. Нажмите кнопку "Далее". На странице "Выбор компонентов" отметьте только компонент "Службы ядра СУБД" и укажите корневой каталог экземпляра. Нажмите кнопку "Далее". Затем Вам будет предложено указать имя и идентификатор экземпляра SQL Server. Выберите флажок "Экземпляр по умолчанию", а в поле "Идентификатор экземпляра" введите название экземпляра SQL Server по Вашему усмотрению. Нажмите кнопку "Далее". Страницу "Конфигурация сервера" оставьте без изменений и нажмите кнопку "Далее". При настройке ядра СУБД выберите флажок "Режим проверки подлинности Windows" и убедитесь, что в список администраторов SQL Server добавлен текущий пользователь. На остальных вкладках страницы "Настройка ядра СУБД" изменения не требуются. Нажмите кнопку "Далее". Далее начнется процесс копирования файлов, требующихся для работы экземпляра SQL Server, и их конфигурирование. Дождитесь окончания данного процесса. В результате экземпляр SQL Server должен быть успешно установлен. Закройте Программу установки SQL Server 2017. 3.2 Установка SQL Server Management Studio Перейдите на страницу загрузки Microsoft SQL Server Management Studio и нажмите ссылку "Скачайте SQL Server Management Studio (SSMS)". На Ваш компьютер будет загружен дистрибутив SSMS-Setup-RUS.exe. Запустите дистрибутив SSMS-Setup-RUS.exe от имени администратора. Появится окно программы установки среды Microsoft SQL Server Management Studio. Выберите директорию, в которую будет установлена среда, и нажмите кнопку "Установить". Начнется процесс загрузки и установки требуемых пакетов и компонентов, а также непосредственная установка SQL Server Management Studio. Дождитесь окончания процесса. После завершения процесса необходимо перезагрузить компьютер. 3.3 Проверка работоспособности Microsoft SQL Server и его настройка После перезагрузки компьютера установленный экземпляр SQL Server должен автоматически начать работу. Запустите программу Microsoft SQL Server Management Studio 18 от имени администратора и подключитесь ко вновь установленному экземпляру используя проверку подлинности Windows. Соединение должно быть успешно установлено, и в окне "Обозреватель объектов" появится текущий экземпляр SQL Server. Запомните название текущего экземпляра (1), оно понадобится далее при настройке конфигурационных файлов сервера (.cfg). Название экземпляра SQL Server: DESKTOP-XXXXXXX Кликните по серверу правой кнопкой мыши и в контекстном меню выберите пункт "Свойства". В появившемся окне "Свойства сервера" перейдите на страницу "Безопасность" и в группе "Серверная проверка подлинности" выберите "Проверка подлинности SQL Server и Windows". Нажмите кнопку "ОК". Для того, чтобы произведенные изменения вступили в силу, необходимо перезапустить SQL Server. Для этого снова кликните по серверу правой кнопкой мыши в окне "Обозреватель объектов" и в контекстном меню выберите пункт "Перезапустить". На вопрос о перезапуске службы SQL Server ответьте "Да". Экземпляр сервера будет перезапущен. Установка и настройка Microsoft SQL Server завершена. Далее установим и настроим серверные файлы игры. 4. Установка и настройка серверных файлов Выбор серверных файлов Скачайте интересующие Вас серверные файлы на Ваш компьютер и распакуйте их в любое удобное для Вас место на диске. Прикрепление баз данных к Microsoft SQL Server Вместе с серверными файлами должны поставляться базы данных AccountServer и GameDB. Если в загруженной Вами сборке сервера базы данных не обнаружены, то найдите их в другой сборке или в Архиве полезных файлов для сервера и клиента. Эти базы данных необходимо прикрепить к ранее установленному экземпляру SQL Server перед настройкой и запуском исполняемых файлов сервера Пиратии. Для этого запустите программу Microsoft SQL Server Management Studio 18 от имени администратора и подключитесь к Вашему экземпляру SQL Server используя проверку подлинности Windows. В обозревателе объектов кликните правкой кнопкой по папке "Базы данных" и в контекстном меню выберите задачу "Присоединить...". Появится окно "Присоединение баз данных", в котором Вам необходимо по очереди указать пути до баз данных AccountServer и GameDB (1), убедиться, что очередная база данных добавлена в список для присоединения (2) и нажать кнопку "ОК" (3). В папке "Базы данных" текущего экземпляра SQL Server в окне "Обозреватель объектов" должны появиться AccountServer и GameDB. Создание пользователей для баз данных Теперь необходимо создать пользователей баз данных AccountServer и GameDB для приложений AccountServer, GroupServer и GameServer. С помощью этих пользователей GroupServer и GameServer будут подключаться к Microsoft SQL Server и работать с базой данных GameDB, а AccountServer будет работать с одноименной базой данных AccountServer. В рамках гайда создадим двух пользователей: одного для базы данных GameDB, второго для базы данных AccountServer. Из-за особенностей шифрования паролей пользователей баз данных в конфигурационных файлах сервера Пиратии, пароль каждого пользователя должен быть длиной ровно 9 символов. Придумаем данные пользователей: GameDB База данных: GameDB Логин: PKODev_Game Пароль: pkodevnet AccountServer База данных: AccountServer Логин: PKODev_Account Пароль: netpkodev Далее по очереди добавим этих пользователей в экземпляр SQL Server и делегируем им доступ к соответствующим базам данных. Кликните правой кнопкой мыши по папке "Безопасность" текущего экземпляра SQL Server в окне "Обозреватель объектов" и в контекстном меню выберите команду "Создать - Вход..." (либо папка "Имена для входа" и команда "Создать имя для входа..."). В окне "Создание имени для входа" на странице "Общие" введите имя пользователя (1), выберите переключатель "Проверка подлинности SQL Server" (2), введите пароль и подтверждение пароля (3), снимите флажок "Требовать использование политики паролей" (4). Выберите базу данных по умолчанию: для пользователя PKODev_Game база данных GameDB и для пользователя PKODev_Account база данных AccountServer (5). Далее перейдите на страницу "Сопоставление пользователей" (6). На странице "Сопоставление пользователей" отметьте флажком базу данных GameDB для пользователя PKODev_Game или базу данных AccountServer для пользователя PKODev_Account (1). В списке "Членство в роли базы данных для: GameDB (AccountServer)" снимите флажок с db_owner (2) и установите флажки db_datawriter, db_datareader, db_ddladmin (3). На этом создание очередного пользователя завершено. Нажмите кнопку "ОК" (4). В подпапке "Имена для входа" папки "Безопасность" текущего экземпляра SQL Server в окне обозревателя объектов должны появиться два новых пользователя: PKODev_Game и PKODev_Account. В результате мы прикрепили базы данных GameDB и AccountServer к экземпляру SQL Server и создали для них пользователей PKODev_Game и PKODev_Account соответственно. Сейчас сервер Пиратии может работать с базами данных, поэтому приступаем к его настройке. Шифрование паролей пользователей баз данных для конфигурационных файлов (.cfg) сервера Как было отмечено выше, пароли пользователей баз данных указываются в конфигурационных файлах сервера (.cfg) в зашифрованном виде, соответственно их необходимо зашифровать. Скачайте Генератор паролей для .cfg, распакуйте загруженный архив и откройте файл passgen.ini в любом текстовом редакторе. Присвойте параметру "pass" пароль для пользователя базы данных GameDB и сохраните файл: pass=pkodevnet Запустите приложение passgen.exe. В консольном окне Вы получите зашифрованный пароль. Скопируйте зашифрованный пароль и сохраните его в текстовом файле. Он понадобится далее в процессе настройки конфигурационных файлов игрового сервера. Повторите операцию шифрования пароля для пользователя базы данных AccountServer: pass=netpkodev В итоге мы получили данные серверного программного обеспечения Пиратии для подключения к Microsoft SQL Server: Название экземпляра SQL Server: DESKTOP-XXXXXXX База данных: GameDB Пользователь: PKODev_Game Зашифрованный пароль для .cfg: SUOh8nJKCe125gImJdapNA== Название экземпляра SQL Server: DESKTOP-XXXXXXX База данных: AccountServer Пользователь: PKODev_Account Зашифрованный пароль для .cfg: 7y2CBBN+se+T5+9Lzp7IIw== Настройка AccountServer Начнем настройку игрового сервера с настройки AccountServer. Откройте конфигурационный файл AccountServer.cfg в любом текстовом редакторе. Найдите секцию db, которая отвечает за подключение к Microsoft SQL Server, и заполните соответствующие параметры: dbserver - адрес или название экземпляра Micrososft SQL Server; db - название базы данных для хранения информации об игровых аккаунтах. По умолчанию AccountServer; userid - Логин пользователя базы данных AccountServer; passwd - Зашифрованный пароль пользователя базы данных AccountServer. [db] dbserver = DESKTOP-XXXXXXX db = AccountServer userid = PKODev_Account passwd = 7y2CBBN+se+T5+9Lzp7IIw== В секции net укажите адрес и порт, на которых AccountServer будет принимать входящие соединения от GroupServer: listen_ip - IP-адрес AccountServer по отношению к GroupServer. Если AccountServer и GroupServer будут запускаться на одном и том же компьютере, то введите IP-адрес 127.0.0.1; listen_port - Сетевой TCP порт, на котором AccountServer будет принимать входящие соединения от GroupServer. По умолчанию 1978. [net] listen_port = 1978 listen_ip = 127.0.0.1 Сохраните файл AccountServer.cfg. Более изменений данного файла не требуется. Настройка GroupServer Откройте конфигурационный файл GroupServer.cfg в любом текстовом редакторе. По аналогии с AccountServer.cfg в данном конфигурационном файле необходимо настроить подключение к серверу баз данных. Для этого используется секция Database со следующими параметрами: IP - адрес или название экземпляра Micrososft SQL Server; DB - название базы данных для хранения информации о состоянии игрового мира. По умолчанию GameDB; Login - Логин пользователя базы данных GameDB; Password - Зашифрованный пароль пользователя базы данных GameDB. [Database] IP = DESKTOP-XXXXXXX DB = GameDB Login = PKODev_Game Password = SUOh8nJKCe125gImJdapNA== Настройте параметры подключения GroupServer к AccountServer в секции AccountServer: IP - IP-адрес AccountServer; Port - Сетевой TCP порт AccountServer. [AccountServer] IP = 127.0.0.1 Port = 1978 Как Вы помните, GroupServer является сервером по отношению к GateServer. В секции Main указываются адрес и порт, через которые GroupServer будет принимать входящие соединения от GateServer: Listen_IP - IP-адрес GroupServer по отношению к GateServer. Если GroupServer и GateServer будут запускаться на одном и том же компьютере, то введите IP-адрес 127.0.0.1; Listen_Port - Сетевой TCP порт, на котором GroupServer будет принимать входящие соединения от GateServer . По умолчанию 1975. [Main] Listen_Port = 1975 Listen_IP = 127.0.0.1 Сохраните файл GroupServer.cfg. Настройка GroupServer завершена. Настройка GateServer Откройте конфигурационный файл GateServer.cfg в любом текстовом редакторе. Настройте параметры подключения GateServer к GroupServer в секции GroupServer: IP - IP-адрес GroupServer; Port - Сетевой TCP порт GroupServer. [GroupServer] IP = 127.0.0.1 Port = 1975 В секции ToClient необходимо указать параметры подключения игровых клиентов к GateServer: IP - IP-адрес GateServer и соответственно адрес Вашего сервера, через который будут подключаться игровые клиенты (игроки). Введите адрес 0.0.0.0, который обозначает все IP-адреса данного компьютера. Например, к серверу можно будет подключиться одновременно через локальный адрес 127.0.0.1, локальную сеть 192.168.0.99 и внешний IP-адрес 54.36.6.113 через сеть Интернет (адреса выдуманы для примера); Port - Сетевой TCP порт, на котором GateServer будет принимать входящие соединения от игровых клиентов, то есть внешний порт Вашего сервера. По умолчанию 1973. Данный порт должен быть открыт в Брандмауэре Windows (и иных программах-межсетевых экранах), а также на стороне Вашего Интернет-провайдера или поставщика услуг хостинга. Если Ваш компьютер подключен к Интернету через маршрутизатор, то в маршрутизаторе необходимо пробросить внешний порт на Ваш компьютер для доступа к игровому серверу из сети Интернет. CommEncrypt - параметр, который отвечает за шифрование входящего и исходящего трафика, то есть сетевых пакетов данных, которые передаются от GateServer к игровым клиентам и от игровых клиентов к GateServer. При значении 1 шифрование включено, при значении 0 - выключено. Рекомендуется включить шифрование для повышения уровня защищенности сервера по отношению к различным вредоносным программам (боты, программы для редактирования и флуда пакетами типа WPE Pro/RPE, твики позволяющие отдельным игрокам получать преимущество над другими и так далее). [ToClient] IP = 0.0.0.0 Port = 1973 CommEncrypt = 1 Настройте адрес и порт, через которые GateServer будет принимать входящие соединения от GameServer в секции ToGameServer: IP - IP-адрес GateServer по отношению к GameServer. Если GateServer и GameServerбудут запускаться на одном и том же компьютере, то введите IP-адрес 127.0.0.1; Port - Сетевой TCP порт, на котором GateServer будет принимать входящие соединения от GameServer. По умолчанию 1971. [ToGameServer] IP = 127.0.0.1 Port = 1971 В секции Main в параметре Version укажите версию клиента, которая допускается для подключения к серверу. На стороне клиента данная версия "зашита" в Game.exe и передается двумя байтами в пакете аутентификации. Самое распространенное значение версии клиента 136: [Main] Version = 136 При несовпадении версии сервера и клиента пользователь получит сообщение об ошибке "Неверная версия игры": Сохраните файл GateServer.cfg. Настройка GateServer завершена. Настройка GameServer Первоначальная настройка GameServer сводится к указанию его уникального имени (идентификатора), параметров подключения к GateServer, параметров подключения к Micrososft SQL Server и списка локаций, которые будут запущены на текущем экземпляре GameServer. В начале данного гайда мы сделали вывод, что GameServer должно быть в системе от 3 до 5 экземпляров для распределения между ними нагрузки, поэтому необходимо создать несколько конфигурационных файлов (.cfg) - для каждого экземпляра GameServer отдельный .cfg-файл. В качестве примера создадим 3 конфигурационных файла: GameServer.cfg, GameServer2.cfg и GameServer3.cfg. Чтобы получить новые .cfg-файлы, скопируйте GameServer.cfg два раза и переименуйте копии в GameServer2.cfg и GameServer3.cfg. По очереди настройте каждый конфигурационный файл GameServer используя текстовый редактор. Так как к GateServer подключается более одного GameServer, GateServer необходимо однозначно различать подключенные GameServer для маршрутизации сетевых пакетов данных от игровых клиентов. Для этого у каждого GameServer есть свой уникальный идентификатор, который указывается в секции ID в виде произвольной текстовой строки. Для GameServer.cfg задайте идентификатор, который Вы можете выбрать по своему усмотрению: [ID] name = GameServer00 Для GameServer2.cfg: [ID] name = GameServer01 Для GameServer3.cfg: [ID] name = GameServer02 Далее настройте параметры подключения к GateServer в секции Gate, которые указываются с помощью ключа gate в виде строки "<IP-адрес>, <порт>", где <IP-адрес> это адрес GateServer, а <порт> соответственно порт, на котором GateServer принимает входящие соединения от GameServer: [Gate] gate = 127.0.0.1, 1971 Затем необходимо указать параметры подключения к Microsoft SQL Server в секции DB с помощью следующих параметров: db_ip - адрес или название экземпляра Micrososft SQL Server; db_usr - Логин пользователя базы данных GameDB; db_pass - Зашифрованный пароль пользователя базы данных GameDB. [DB] db_ip = DESKTOP-XXXXXXX db_usr = PKODev_Game db_pass = SUOh8nJKCe125gImJdapNA== Осталось указать список локаций в секции Map, которые будут запущены на текущем экземпляре GameServer. В отличие от игрового наименования локаций, в серверных файлах локации носят кодовые имена. К примеру, Аскарон называется garner, Магический Океан это magicsea, а Великий Синий Океан - darkblue. Очередная локация включается ключом map c новой строки: [Map] map = <локация 1> map = <локация 2> . . . map = <локация N> Например, следующая секция Map запустит карты Аскарон, Арену для командных сражений, Мир демонов и Мир демонов 2: [Map] map = garner map = teampk map = puzzleworld map = puzzleworld2 Полный список наименований игровых локаций, который связывает их с кодовыми именами, приведен ниже: garner - Аскарон magicsea - Магический Океан darkblue - Великий Синий Океан lonetower - Одинокая башня eastgoaf - Серебрянные шахты, заброшенные шахты, логово черного дракона, логово черного дракона 2 secretgarden - Сад Эдель darkswamp - Темная топь abandonedcity - Забытый Город abandonedcity2 - Забытый Город 2 abandonedcity3 - Забытый Город 3 puzzleworld - Мир Демонов puzzleworld2 - Мир Демонов 2 teampk - Арена jialebi - Небеса, база флота, остров сокровищ, остров скелетов garner2 - Серебряный Хаос hell(2-5) - Абаддон (2-5) guildwar - Священная война guildwar2 - Маленькая священная война leiting2 - Мираж Громограда shalan2 - Мираж Шайтана binglang2 - Мираж Ледыни yschurch - Церковь 07xmas - Новогодняя деревня 2007 07xmas2 - Снежная война prisonisland - Тюрьма winterland - Остров Зимы mjing1 - 1-ый край Авроры mjing2 - 2-ой край Авроры mjing3 - 1-ый край Тьмы mjing4 - 2-ой край Тьмы starena* - Арены heilong - Сундук черного дракона darkhouse - Дом тьмы mingyun - Комната судьбы Примечание: карты garner, magicsea и darkblue необходимо запускать на разных экземплярах GameServer. Определите для каждого конфигурационного файла GameServer набор локаций. Локации garner, maigcsea и darkblue в соответствии с примечанием должны быть записаны в разных конфигурационных файлах. Для примера можно настроить конфигурационные файлы таким образом: GameServer.cfg map = garner map = garner2 map = teampk map = lonetower map = puzzleworld map = puzzleworld2 map = eastgoaf map = leiting2 map = shalan2 map = binglang2 GameServer2.cfg [Map] map = magicsea map = abandonedcity map = abandonedcity2 map = abandonedcity3 map = darkswamp map = secretgarden map = guildwar map = guildwar2 GameServer3.cfg [Map] map = darkblue map = jialebi map = hell map = hell2 map = hell3 map = hell4 map = hell5 Сохраните очередной конфигурационный файл (.cfg) GameServer. После внесения изменений в последний конфигурационный файл, настройка экземпляров GameServer окончена. Процесс установки и настройки серверного программного обеспечения Пиратии завершен. Далее приступим к запуску игрового мира. 5. Запуск сервера Общие принципы и порядок запуска исполняемых файлов сервера Поскольку игровой сервер состоит из нескольких исполняемых файлов (AccountServer.exe, GateServer.exe, GameServer.exe, GroupServer.exe), то их необходимо запускать в определенном порядке: 1. AccountServer; 2. Экземпляры GameServer; 3. GroupServer; 4. GateServer. Для запуска AccountServer, GroupServer и GateServer достаточно запустить их исполняемые файлы из проводника. Если таким же образом запустить GameServer, то он будет использовать конфигурационный файл по умолчанию GameServer.cfg, поэтому при запуске очередного экземпляра GameServer необходимо дополнительно указать в параметрах его запуска конфигурационный файл, который необходимо использовать экземпляру для работы: GameServer.exe <Конфигурационный файл> Это можно сделать из Командной строки (cmd.exe), либо с помощью пакетного .bat-файла. Запуск GameServer из Командной строки (cmd.exe) cd C:\PKO Server\GameServer GameServer GameServer2.cfg Данная последовательность команд запустит исполняемый файл GameServer.exe с конфигурационным файлом GameServer2.cfg, которые находятся на диске в директории C:\PKO Server\GameServer. Запуск GameServer с помощью .bat-файла start GameServer.exe GameServer3.cfg Данный .bat-файл запустит исполняемый файл GameServer с конфигурационным файлом GameServer3.cfg при условии, что GameServer.exe и .bat-файл находятся в одной и той же директории. Чтобы запустить сразу несколько экземпляров GameServer с различными конфигурационными файлами, можно написать простой .bat-скрипт: @echo off start GameServer.exe GameServer.cfg start GameServer.exe GameServer2.cfg start GameServer.exe GameServer3.cfg По аналогии можно сделать .bat-файл, который запустит весь игровой сервер, например, с рабочего стола: @echo off cd C:\PKO Server\AccountServer start AccountServer.exe cd C:\PKO Server\GameServer start GameServer.exe GameServer.cfg start GameServer.exe GameServer2.cfg start GameServer.exe GameServer3.cfg cd C:\PKO Server\GroupServer start GroupServer.exe cd C:\PKO Server\GateServer start GateServer.exe Примечание: в данном примере исполняемые файлы сервера находятся в соответствующих папках в директории C:\PKO Server\. Запуск AccountServer Запустите исполняемый файл AccountServer.exe любым удобным для Вас способом. В результате должно появиться два окна: форма со служебной информацией и консольное окно. В консольном окне не должно быть никаких ошибок. После запуска GroupServer, в окне со служебной информацией в списке GroupServer должен быть отображен подключенный GroupServer (поле Status имеет значение connected). Запуск экземпляров GameServer Запустите исполняемый файл GameServer.exe для каждого конфигурационного файла (.cfg) удобным для Вас способом. Появится несколько консольных окон и начнется процесс подключения к Microsoft SQL Server и загрузки каждого экземпляра GameServer. В процессе загрузки очередного экземпляра не должны возникать ошибки в виде записей в консоли или окон сообщений. Соединение с Microsoft SQL Server должно быть успешно установлено, что подтверждается соответствующей записью в консоли: "Database Connected!". После успешного завершения загрузки GameServer должно появиться окно со служебной информацией: ID процесса (PID), частота обновления игрового мира (FPS), число персонажей игроков в игре (Player), загруженный конфигурационный файл (Config) и другая. Проверьте, что напротив каждой карты в списке "Map List" стоит надпись "ok", которая говорит об успешной загрузке локации. После запуска GateServer в списке "Gate" появится IP-адрес GateServer с надписью напротив "connected", к которому подключился данный экземпляр GameServer. Запуск GroupServer Запустите исполняемый файл GroupServer.exe. Появится консольное окно GroupServer. В окне не должно быть никаких сообщений об ошибках. После запуска AccountServer и GateServer появятся сообщения об успешном установлении соединения с данными приложениями. Запуск GateServer Запустите исполняемый файл GateServer.exe. Появится консольное окно GateServer в котором должно быть сообщение об успешном запуске приложения. Игровой сервер Пиратии успешно запущен! Проверим его доступность для подключения к нему игровых клиентов. 6. Проверка соединения с игровым сервером Для определения возможности подключения к Вашему игровому серверу из сети Интернет можно воспользоваться сервисами в Интернете, которые позволяют выполнить проверку внешнего порта сервера на доступность, например, 2ip - Проверка порта. В поле "Порт" введите внешний порт GateServer из секции ToClient конфигурационного файла GateServer.cfg. По умолчанию это порт 1973. Нажмите кнопку "Проверить". При этом, исполняемый файл GateServer.exe должен быть запущен. Вы увидите сообщение "Порт открыт" если сервер доступен для подключения к нему, либо "Порт закрыт" в противном случае. Еще одним способом проверки доступности игрового сервера является подключение к нему используя клиент Telnet. Для этого запустите клиент Telnet, например, встроенный в операционную систему Windows (сочетание клавиш Windows + R, открыть telnet.exe) и выполните в нем команду для подключения к игровому серверу: open <IP-адрес> <Порт> <IP-адрес> - это IP-адрес игрового сервера, подключение через который Вы хотите проверить; <Порт> - внешний порт игрового сервера. При доступности игрового сервера по указанному адресу Вы увидите строку с датой и временем подключения к серверу. 7. Остановка игрового сервера Для корректного отключения игроков и сохранения состояния игрового мира в базах данных на диске, рекомендуется останавливать сервер в следующей последовательности: 1. Все экземпляры GameServer; 2. GroupServer; 3. GateServer; 4. AccountServer. Отключать сервер через Диспетчер задач нельзя - можно потерять несохранённые игровые данные в памяти, что приведет к откату состояния игрового мира в прошлое при следующем запуске сервера. Перед остановкой сервера считается хорошим тоном предупредить игроков о предстоящем отключении. Чтобы завершить работу всех экземпляров GameServer, откройте служебное окно очередного экземпляра и введите в поле ввода команд (1) команду Stop(X), где X - время в секундах, через которое текущий GameServer будет остановлен, после чего нажмите кнопку "Execute" (2). Когда все экземпляры GameServer завершат свою работу, остановите поочередно GroupServer, затем GateServer и после AccountServer с помощью сочетания клавиш CTRL + C в консольном окне. Процесс завершения работы очередного сервера может занять время, то есть приложения закроются не сразу. 8. Подключение игрового клиента к серверу Чтобы войти во вновь созданный игровой мир, Вам понадобится игровой клиент, который можно скачать в данной теме на нашем форуме: Выберите из списка доступных клиентов тот, который подойдет по версии к Вашим серверным файлам. Например, для сборки Pirate King Online 1.38 подойдет официальный русский клиент версии 1.38.1: Скачайте выбранный дистрибутив (архив) и установите (распакуйте) его на Ваш диск. Перейдите в корневую директорию игрового клиента и создайте там .bat-файл с названием start.bat. Он будет необходим для запуска клиента в обход программы автообновления. start system\Game.exe startgame Затем скачайте программу IP Changer, с помощью которой нужно задать игровому клиенту IP-адрес для подключения к серверу. Поместите IPChanger.exe в корневую директорию игрового клиента и запустите. В поля ввода текста "Server Name" (1) и "Server Region" (2) введите название Вашего сервера и регион соответственно. В поле "Server Address" (3) укажите IP-адрес игрового сервера, через который клиент будет к нему подключаться. Выпадающий список "Client Version" оставьте без изменений. Нажмите кнопку "ОК" (4) и после сообщения "Address of server was successfully changed!" закройте программу. Первичная настройка клиента завершена. Для подключения к серверу необходим игровой аккаунт, который можно создать с помощью специальной программы. Скачайте и запустите программу Создать учетную запись. Нажмите кнопку "Настройки" (1) на главной форме приложения. В окне "Настройки" укажите адрес или имя экземпляра Microsoft SQL Server (1), отметьте флажок "Использовать проверку подлинности Windows" и проверьте подключение (3). При успешном подключении к Microsoft SQL Server нажмите кнопку "ОК" (4). Далее введите желаемые логин (2) и пароль (3) для новой учетной записи, а также выберите уровень доступа к системе (4). После заполнения всех полей нажмите кнопку "Создать" (5). Вы увидите сообщение о том, что учетная запись была успешно создана. Запустите игровой клиент с помощью .bat-файла start.bat и подключитесь к серверу используя созданную учетную запись. После успешного входа в учетную запись, создайте нового персонажа и убедитесь что создание персонажей работает исправно. Войдите вновь созданным персонажем в игровой мир. На этом этапе задача установки игрового сервера выполнена, и гайд подходит к своему логическому завершению, но в процессе Вы могли столкнуться с различными ошибками. В следующем разделе будут рассмотрены самые распространенные ошибки сервера и клиента, а также методы их устранения. 9. Распространенные ошибки и их исправление В процессе установки сервера и подключения к игровому миру могут возникнуть различные ошибки. Далее рассмотрим самые распространенные и исправим их. GameServer.exe сразу закрывается после запуска Заполните все данные для подключения к Microsoft SQL Server в конфигурационном файле GameServer.cfg (секция DB). [DB] db_ip = db_usr = db_pass = Ошибки GameServer "Database [GameDB] Connect Failed!", GroupServer "Unable to connect database", AccountServer "Main database handler create failed, AccountServer hang!" Данные ошибки говорят о том, что серверное программное обеспечение (GameServer, GroupServer, AccountServer) не может установить соединение с Microsoft SQL Server и получить доступ к соответствующим базам данных. Возможные пути решения: 1. Проверьте, что Microsoft SQL Server запущен и Вы указали его верный адрес или имя экземпляра в конфигурационных файлах игрового сервера; 2. Проверьте, что в настройках безопасности Microsoft SQL Server Вы включили проверку подлинности SQL Server и Windows и перезапустили после этого SQL Server; 3. Убедитесь, что Вы используете верные логин и пароль пользователей для соответствующей базы данных в конфигурационных файлах игрового сервера. Пользователям баз данных должны быть выданы следующие права: db_datareader, db_datawriter, db_ddladmin, public. Пароли пользователей должны быть указаны в конфигурационных файлах в зашифрованном виде; 4. Убедитесь, что названия баз данных совпадают в Micrososft SQL Server и в конфигурационных файлах сервера, а так же в исполняемом файле GameServer.exe; 5. (Спасибо @MrSharp) Проверьте, что протокол TCP/IP включен для Вашего MSSQL сервера, и включите его, если он отключен: 5.1 Запустите программу SQL Server Configuration Manager: 5.2 В левой части окна программы раскройте список SQL Server Network Configuration и нажмите на Protocols for MSSQLSERVER (1): 5.3 Нажмите правой кнопкой мыши на строке TCP/IP (2) и в контекстном меню выберите Enable: 5.4 В диалоговом окне "Warning" нажмите кнопку ОК: 5.5 Перезапустите MSSQL сервер: Ошибка GameServer "SubMap::LoadNpc: Obtain Map [<название локации>] ID fail!" Данная ошибка может возникнуть в процессе загрузки локаций игрового мира при запуске экземпляра GameServer. Чтобы её исправить откройте файл GameServer\resource\script\MisScript\ScriptDefine.lua и добавьте в конец строку: AddMap("<Кодовое название локации>", "<Игровое название локации>") Например: AddMap("yschurch", "Церковь") Ошибка игрового клиента "Соединение прервано" Такая ошибка возникает в случае, когда игровой клиент не может установить сетевое соединение с сервером. Возможные пути решения: 1. Проверьте, что GateServer запущен. Если Вы подключаетесь к серверу через сеть Интернет, то проверьте доступность GateServer. Внешний порт игрового сервера (по умолчанию 1973) должен быть открыт, а в конфигурационном файле GateServer.cfg должен быть указан соответствующий внешний IP-адрес (0.0.0.0 или адрес выделенного сервера). Внешний IP-адрес сервера должен быть публичным ("белым"); 2. Убедитесь, что игровой клиент настроен на верный адрес игрового сервера: с помощью программы IPChanger (а также иных способов настройки клиента) указан верный IP-адрес GateServer, а в Game.exe "зашит" верный порт GateServer (по умолчанию 1973). Чтобы убедиться в последнем, можно взять Game.exe из официального русского клиента без каких-либо модификаций, либо использовать специальную программу; 3. Соединению клиента с сервером могут препятствовать различные программы-фаерволы, брандмауэр Windows, а также сетевые маршрутизаторы (роутеры), в которых не был проброшен внешний порт GateServer на компьютер с игровым сервером. Ошибка игрового клиента "Account server has encountered a malfunction" Запустите AccountServer и убедитесь что к нему успешно подключился GroupServer. Ошибка игрового клиента "Discovered exceptional line error on GateServer" Запустите GroupServer и убедитесь что к нему успешно подключился GateServer. Ошибка игрового клиента "Неверная версия игры" Убедитесь, что версии в конфигурационном файле GateServer.cfg (секция Main, параметр Version) и в Game.exe совпадают. Обычно в Game.exe "зашита" версия 136. Узнать версию Game.exe можно с помощью специальной программы на нашем форуме. Ошибка игрового клиента "illegal birth place" при создании персонажа Откройте файл игрового клиента Клиент\scripts\table\StringSet.txt и замените строки [39] "Аргент" [40] "Шайтан" [41] "Ледынь" на [39] "Argent City" [40] "Shaitan City" [41] "Icicle Castle" Значения Argent City, Shaitan City и Icicle Castle определяются соответствующими значениями в конфигурационном файле GroupServer.cfg игрового сервера. [bird] Argent City = garner Shaitan City = magicsea Icicle Castle = darkblue Ошибка игрового клиента "target map cannot be reached" при входе в игровой мир Выбранный персонаж находится на локации, которая не запущена на сервере. Запустите все экземпляры GameServer с полным набором локаций и убедитесь, что в каждом служебном окне текущего экземпляра GameServer отображается успешно загруженный список карт игрового мира (список Map List). Если вы столкнулись с проблемой, которой нет в рассмотренном выше списке ошибок, то воспользуйтесь разделом "Пиратия: Помощь", в котором Вам придут на помощь более опытные администраторы и разработчики серверов Пиратии. Также за много лет на нашем форуме накоплен большой пласт опыта Ваших предшественников, поэтому целесообразно воспользоваться поиском по форуму: с большой долей вероятности Ваша проблема уже обсуждалась и решена в прошлом. Благодарю Вас за внимание и надеюсь, что статья действительно была Вам интересна и помогла в установке сервера! Я был рад поделиться с Вами своим опытом. Гайд написал V3ct0r специально для форума PKODev.NET Копирование статьи в том или ином виде на сторонние ресурсы без согласования с автором ЗАПРЕЩЕНО!
  3. Исправление SQL-инъекции в AccountServer MAC-адрес — уникальный идентификатор, присваиваемый каждой единице активного оборудования или некоторым их интерфейсам в компьютерных сетях Ethernet. Википедия - Свободная энциклопедия Привет! В данной статье я хочу поговорить об уязвимости в AccountServer.exe, которая позволяет злоумышленникам проводить внедрение вредоносного SQL-кода и редактировать базу данных AccountServer по их усмотрению. Таким образом, злоумышленник может, например, заблокировать все аккаунты на сервере, из-за чего игроки не смогут попасть в игровой мир, либо вообще удалить все учетные записи пользователей. 1. Суть и причины уязвимости После того, как игрок успешно вошел в свою учетную запись используя логин и пароль, AccountServer.exe выполняет SQL-запрос к одноименной базе данных AccountServer, чтобы обновить IP- и MAC-адреса клиента, время входа пользователя, название экземпляра GroupServer, от которого был получен пакет аутентификации, и установить флаг по которому можно определить авторизован ли пользователь в данный момент времени или нет: UPDATE account_login SET login_status = %d, login_group = '%s', enable_login_time = getdate(), last_login_time = getdate(), last_login_mac = '%s', last_login_ip = '%s' WHERE id = %d Уязвимость заключена в параметре last_login_mac, которому с помощью строкового маркера %s может присваивается любая текстовая строка: last_login_mac = '%s' В базе данных AccountServer в таблице аккаунтов account_login хранятся MAC-адреса всех клиентов. Поскольку через сеть Интернет нельзя узнать MAC-адрес сетевого адаптера пользователя, в отличие от IP-адреса, то, по идее разработчиков, игровой клиент, запущенный на пользовательском компьютере, должен получить MAC-адрес сетевого интерфейса и записать его в пакет аутентификации (ID: 431), после чего отравить MAC вместе с пакетом аутентификации на сервер. Далее адрес передается в AccountServer.exe и сохраняется в базе данных. Проблема в том, что в AccountServer.exe принятая строка с MAC-адресом никак не проверяется на соответствие формату MAC (XX-XX-XX-XX-XX-XX-XX-XX, где X символы 0 - 1 или A - F), то есть на сервер можно отправить любую строку и она будет вставлена в вышеприведенный SQL-запрос вместо маркера %s и сохранена в базе данных, что, в свою очередь, предоставляет возможность внедрения произвольного SQL-кода в запрос. В качестве примера внедрим в SQL-запрос вредоносный код, который заблокирует все учетные записи пользователей. Для этого вместо MAC-адреса необходимо отправить на сервер следующую строку: 1'; update account_login set ban=1;-- Далее она будет подставлена в изучаемый SQL-запрос и в результате будет сформирован новый запрос: UPDATE account_login SET login_status = %d, login_group = '%s', enable_login_time = getdate(), last_login_time = getdate(), last_login_mac = '1'; update account_login set ban=1;--', last_login_ip = '%s' WHERE id = %d Проанализируем что делает с базой данных SQL-запрос с внедренным вредоносным кодом: 1) Обновляет поля login_status. login_group, enable_login_time, last_login_time для ВСЕХ пользователей, что некорректно, так как эти поля должны обновляться только для текущего пользователя; 2) Присваивает значение "1" MAC-адресам ВСЕХ пользователей, что во-первых, так же некорректно в соответствии с пунктом (1), во-вторых, значение "1" не отражает какой-либо реальный MAC-адрес; 3) Присваивает значение "1" полям ban ВСЕХ пользователей, что означает блокировку всех учетных записей; 4) Поле last_login_ip не обновляется, так как часть запроса, в том числе с условием обновления данных только текущего пользователя, была закомментирована и не обрабатывается SQL Server. В итоге, после исполнения такого SQL-запроса, базе данных аккаунтов будет нанесен определенный ущерб. Последствия рассмотренного примера вполне обратимы, но злоумышленнику ничего не мешает использовать во внедряемом SQL-коде оператор DELETE, который служит для удаления записей из таблиц. В таком случае Вы можете безвозвратно потерять все учетные записи пользователей. 2. Эксплойт Для проверки наличия уязвимости в AccountServer.exe я разработал специальную программу-эксплойт, с помощью которой можно провести атаку на базу данных AccountServer. Также эксплойт понадобится для тестирования защиты, которую мы разработаем в будущем. Скачать эксплойт Исходный код эксплойта (в архиве идет проект Visual Studio 2019 Community) ВНИМАНИЕ! Данная программа не предназначена для взлома игровых серверов и должна использоваться только в учебных и испытательных целях. Автор не несет никакой ответственности за совершенные Вами действия и понесенный кем-либо ущерб. Используйте на свой страх и риск! Чтобы провести попытку внедрения SQL-инъекции запустите программу accountexploit.exe со следующими параметрами: accountexploit ip:<IP-адрес сервера> port:<Порт сервера> login:<Логин> password:<Пароль> version:<Версия сервера> Например: accountexploit ip:127.0.0.1 port:1973 login:V3ct0r password:112233 version:136 Эксплойт запустится и, при условии корректности информации, необходимой для подключения к серверу, начнется процесс тестирования Вашего сервера на уязвимость. В результате Вы должны увидеть одно из двух сообщений. 1. SQL INJECTION SUCCESFULLY DONE! ALL ACCOUNTS ARE BANNED. YOU NEED TO TAKE CARE OF THE SECURITY OF YOUR ACCOUNTSERVER! - В Вашем AccountServer.exe обнаружена уязвимость и Вам необходимо предпринять меры для её устранения; 2. SQL INJECTION FAILED! YOUR ACCOUNTSERVER IS SECURED! - Уязвимость не обнаружена. Ваш AccountServer.exe защищен. Если уязвимость была обнаружена, то нужно её незамедлительно исправить. Как это сделать будет рассмотрено далее. 3. Исправление уязвимости в AccountServer.exe Существует несколько методов исправления уязвимости: 1) Использовать специальные программы-посредники между GateServer и GroupServer, например, Gemini.X.Fail, SQLGuard, Filterserver, которые анализируют пакеты аутентификации на наличие SQL-инъекций; 2) Написать заплатку ("патч") и внедрить её в AccountServer.exe; 3) Разработать библиотеку динамической компоновки (.dll), исправляющую уязвимость, и подключить её к AccountServer.exe. В рамках данной статьи я выберу вариант (2) с написанием и внедрением заплатки как самый оптимальный ввиду относительной простоты исправления уязвимости. Вариант (1) требует разработки и применения дополнительного серверного программного обеспечения, а вариант (3) порождает дополнительные зависимости для AccountServer.exe. По моему мнению, оба последних варианта избыточны. Для заплатки потребуется разработать функцию, которая будет проверять корректность полученной от клиента строки с MAC-адресом. На вход функции будет подаваться строка, MAC-адрес для проверки соответственно, а на выходе функция будет выдавать логическое значение true в случае, если строка корректна, или false, если строка не прошла проверку и вероятно содержит вредоносный код. Чтобы разработать такую функцию, необходимо определить правила валидации. Поскольку в базе данных MAC-адреса хранятся в формате XX-XX-XX-XX-XX-XX-XX-XX, где X символы 0 - 1 или A - F, то будем использовать такой же формат для проверки: 1) Длина строки должна быть ровно 23 символа; 2) Каждый третий символ должен быть дефисом (-); 3) Каждый байт адреса должен быть представлен шестнадцатеричным значением (символы 0 - 1 и A - F). Теперь можно написать функцию проверки MAC-адресов на соответствие установленным правилам. Я применю язык C++: /* Функция для проверки MAC-адреса 1) Длина строки должна быть ровно 23 символа; 2) Каждый третий символ должен быть дефисом(-); 3) Каждый байт адреса должен быть представлен шестнадцатеричным значением(символы 0 - 1 и A - F). */ bool check_mac_address(const char* mac) { // Длина строки MAC-адреса unsigned int length = 0; // Скопируем указатель на строку const char* p = mac; // Получим длину строки MAC-адреса while (*p++) { // Увеличиваем счетчик символов length++; // Проверим длину строки if (length > 23) { // Слишком длинная строка return false; } } // Проверим длину строки if (length != 23) { // Слишком короткая строка return false; } // Проверим каждый символ в строке for (unsigned int i = 0; i < length; i++) { // Берем текущий символ unsigned char c = static_cast<unsigned char>(mac[i]); // Каждый 3-й символ должен быть дефисом (-) if (((i + 1) % 3 == 0) && c != '-') { // Некорретный формат MAC! return false; } // Остальные символы могут принимать значения // 0 - 1 // A - F if ( ((i + 1) % 3 != 0) && ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) == false) { // Некорретный формат MAC! return false; } } // Строка прошла проверку return true; } Скомпилируем разработанную функцию с помощью Microsoft Visual Studio 2019 Community и получим код на языке ассемблера, который можно внедрить в AccountServer.exe: 00F51000 >/$ 55 PUSH EBP 00F51001 |. 8BEC MOV EBP,ESP 00F51003 |. 83EC 18 SUB ESP,18 00F51006 |. B8 CCCCCCCC MOV EAX,CCCCCCCC 00F5100B |. 8945 E8 MOV DWORD PTR SS:[EBP-18],EAX 00F5100E |. 8945 EC MOV DWORD PTR SS:[EBP-14],EAX 00F51011 |. 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX 00F51014 |. 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX 00F51017 |. 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX 00F5101A |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX 00F5101D |. C745 FC 000000>MOV DWORD PTR SS:[EBP-4],0 00F51024 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] 00F51027 |. 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX 00F5102A |> 8B4D F8 /MOV ECX,DWORD PTR SS:[EBP-8] 00F5102D |. 0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 00F51030 |. 8955 EC |MOV DWORD PTR SS:[EBP-14],EDX 00F51033 |. 8B45 F8 |MOV EAX,DWORD PTR SS:[EBP-8] 00F51036 |. 83C0 01 |ADD EAX,1 00F51039 |. 8945 F8 |MOV DWORD PTR SS:[EBP-8],EAX 00F5103C |. 837D EC 00 |CMP DWORD PTR SS:[EBP-14],0 00F51040 |. 74 18 |JE SHORT 00F5105A 00F51042 |. 8B4D FC |MOV ECX,DWORD PTR SS:[EBP-4] 00F51045 |. 83C1 01 |ADD ECX,1 00F51048 |. 894D FC |MOV DWORD PTR SS:[EBP-4],ECX 00F5104B |. 837D FC 17 |CMP DWORD PTR SS:[EBP-4],17 00F5104F |. 76 07 |JBE SHORT 00F51058 00F51051 |. 32C0 |XOR AL,AL 00F51053 |. E9 B0000000 |JMP 00F51108 00F51058 |>^EB D0 \JMP SHORT 00F5102A 00F5105A |> 837D FC 17 CMP DWORD PTR SS:[EBP-4],17 00F5105E |. 74 07 JE SHORT 00F51067 00F51060 |. 32C0 XOR AL,AL 00F51062 |. E9 A1000000 JMP 00F51108 00F51067 |> C745 F4 000000>MOV DWORD PTR SS:[EBP-C],0 00F5106E |. EB 09 JMP SHORT 00F51079 00F51070 |> 8B55 F4 /MOV EDX,DWORD PTR SS:[EBP-C] 00F51073 |. 83C2 01 |ADD EDX,1 00F51076 |. 8955 F4 |MOV DWORD PTR SS:[EBP-C],EDX 00F51079 |> 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C] 00F5107C |. 3B45 FC |CMP EAX,DWORD PTR SS:[EBP-4] 00F5107F |. 0F83 81000000 |JNB 00F51106 00F51085 |. 8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8] 00F51088 |. 034D F4 |ADD ECX,DWORD PTR SS:[EBP-C] 00F5108B |. 8A11 |MOV DL,BYTE PTR DS:[ECX] 00F5108D |. 8855 F3 |MOV BYTE PTR SS:[EBP-D],DL 00F51090 |. 8B45 F4 |MOV EAX,DWORD PTR SS:[EBP-C] 00F51093 |. 83C0 01 |ADD EAX,1 00F51096 |. 33D2 |XOR EDX,EDX 00F51098 |. B9 03000000 |MOV ECX,3 00F5109D |. F7F1 |DIV ECX 00F5109F |. 85D2 |TEST EDX,EDX 00F510A1 |. 75 0D |JNZ SHORT 00F510B0 00F510A3 |. 0FB655 F3 |MOVZX EDX,BYTE PTR SS:[EBP-D] 00F510A7 |. 83FA 2D |CMP EDX,2D 00F510AA |. 74 04 |JE SHORT 00F510B0 00F510AC |. 32C0 |XOR AL,AL 00F510AE |. EB 58 |JMP SHORT 00F51108 00F510B0 |> 8B45 F4 |MOV EAX,DWORD PTR SS:[EBP-C] 00F510B3 |. 83C0 01 |ADD EAX,1 00F510B6 |. 33D2 |XOR EDX,EDX 00F510B8 |. B9 03000000 |MOV ECX,3 00F510BD |. F7F1 |DIV ECX 00F510BF |. 85D2 |TEST EDX,EDX 00F510C1 |. 74 3E |JE SHORT 00F51101 00F510C3 |. 0FB655 F3 |MOVZX EDX,BYTE PTR SS:[EBP-D] 00F510C7 |. 83FA 30 |CMP EDX,30 00F510CA |. 7C 09 |JL SHORT 00F510D5 00F510CC |. 0FB645 F3 |MOVZX EAX,BYTE PTR SS:[EBP-D] 00F510D0 |. 83F8 39 |CMP EAX,39 00F510D3 |. 7E 1B |JLE SHORT 00F510F0 00F510D5 |> 0FB64D F3 |MOVZX ECX,BYTE PTR SS:[EBP-D] 00F510D9 |. 83F9 41 |CMP ECX,41 00F510DC |. 7C 09 |JL SHORT 00F510E7 00F510DE |. 0FB655 F3 |MOVZX EDX,BYTE PTR SS:[EBP-D] 00F510E2 |. 83FA 46 |CMP EDX,46 00F510E5 |. 7E 09 |JLE SHORT 00F510F0 00F510E7 |> C745 E8 000000>|MOV DWORD PTR SS:[EBP-18],0 00F510EE |. EB 07 |JMP SHORT 00F510F7 00F510F0 |> C745 E8 010000>|MOV DWORD PTR SS:[EBP-18],1 00F510F7 |> 837D E8 00 |CMP DWORD PTR SS:[EBP-18],0 00F510FB |. 75 04 |JNZ SHORT 00F51101 00F510FD |. 32C0 |XOR AL,AL 00F510FF |. EB 07 |JMP SHORT 00F51108 00F51101 |>^E9 6AFFFFFF \JMP 00F51070 00F51106 |> B0 01 MOV AL,1 00F51108 |> 8BE5 MOV ESP,EBP 00F5110A |. 5D POP EBP 00F5110B \. C3 RETN Далее открываем AccountServer.exe в OllyDbg v1.10, переходим в модуль AccountServer.exe и ищем участок нулей в памяти приложения, который никогда не используется им во время исполнения - так называемый Code Cave. В AccountServer.exe из сборки Pirate King Online 1.38 (на его примере я рассмотрю процесс внедрения заплатки) Code Cave начинается с адреса 0x00452B8B. Скопируйте байты функции check_mac_address() и вставьте их в Code Cave. 55 8B EC 83 EC 18 B8 CC CC CC CC 89 45 E8 89 45 EC 89 45 F0 89 45 F4 89 45 F8 89 45 FC C7 45 FC 00 00 00 00 8B 45 08 89 45 F8 8B 4D F8 0F BE 11 89 55 EC 8B 45 F8 83 C0 01 89 45 F8 83 7D EC 00 74 18 8B 4D FC 83 C1 01 89 4D FC 83 7D FC 17 76 07 32 C0 E9 B0 00 00 00 EB D0 83 7D FC 17 74 07 32 C0 E9 A1 00 00 00 C7 45 F4 00 00 00 00 EB 09 8B 55 F4 83 C2 01 89 55 F4 8B 45 F4 3B 45 FC 0F 83 81 00 00 00 8B 4D 08 03 4D F4 8A 11 88 55 F3 8B 45 F4 83 C0 01 33 D2 B9 03 00 00 00 F7 F1 85 D2 75 0D 0F B6 55 F3 83 FA 2D 74 04 32 C0 EB 58 8B 45 F4 83 C0 01 33 D2 B9 03 00 00 00 F7 F1 85 D2 74 3E 0F B6 55 F3 83 FA 30 7C 09 0F B6 45 F3 83 F8 39 7E 1B 0F B6 4D F3 83 F9 41 7C 09 0F B6 55 F3 83 FA 46 7E 09 C7 45 E8 00 00 00 00 EB 07 C7 45 E8 01 00 00 00 83 7D E8 00 75 04 32 C0 EB 07 E9 6A FF FF FF B0 01 8B E5 5D C3 Запомните адрес, начиная с которого Вы вставили байты. Это будет адрес функции check_mac_address(). В моем случае это адрес 0x00452B90 (1). Задействуем внедренную функцию для проверки MAC-адресов. Найдите последовательность байт: 8B 8D C4 FC FF FF 89 81 9C 00 00 00 6A 00 8D 4D 08 Вы должны увидеть следующие инструкции: MOV ECX,DWORD PTR SS:[EBP-33C] MOV DWORD PTR DS:[ECX+9C],EAX PUSH 0 LEA ECX,DWORD PTR SS:[EBP+8] CALL 0041C410 PUSH EAX MOV ECX,DWORD PTR SS:[EBP-33C] ADD ECX,0A4 Инструкцию PUSH EAX (1) необходимо заменить на инструкцию безусловного перехода JMP <Адрес Code Cave> в Code Cave, например, после внедренной функции check_mac_address() (1). В моем случае это адрес 0x00452C9E. Также запомните адрес инструкции ADD ECX, 0A4 (2) - программа будет возвращаться сюда при успешной проверке MAC-адреса. В примере это адрес 0x00403EB3. Далее нужно найти адрес инструкций (1), на которые программа будет переходить при некорректном MAC-адресе. Найдите последовательность байт: 8B 85 C4 FC FF FF C6 40 14 00 C7 45 FC FF FF FF FF 8D 4D 08 В AccountServer.exe из серверных файлов Pirate King Online 1.38 они будут записаны по адресу 0x00403E4E (1). В результате мы получили 5 адресов: 1. Адрес функции check_mac_address() в Code Cave (0x00452B90); 2. Адрес инструкций проверки MAC-адреса в Code Cave (0x00452C9E); 3. Адрес перехода на инструкции проверки MAC-адреса в Code Cave из обработчика пакета аутентификации (0x00403EAC); 4. Адрес перехода из Code Cave на инструкции продолжения процесса обработки пакета аутентификации (0x00403EB3); 5. Адрес перехода из Code Cave на инструкции завершения процесса обработки пакета аутентификации (0x00403E4E); Осталось записать в Code Cave инструкции для проверки MAC-адреса и подставить в них требуемые адреса: PUSHAD PUSH EAX CALL <Адрес функции check_mac_address()> ADD ESP,4 TEST AL,AL JE SHORT <FAIL> POPAD PUSH EAX MOV ECX,DWORD PTR SS:[EBP-33C] JMP <Адрес перехода при успешной проверке MAC-адреса> <FAIL> POPAD JMP <Адрес перехода при неккоректном MAC-адресе> Сохраните проделанные изменения в файле AccountServer.exe. На этом внедрение заплатки и исправление уязвимости в AccountServer.exe завершено. Перейдем к процессу тестирования. Скачать исправленный AccountServer.exe из серверных файлов Pirate King Online 1.38 4. Проверка AccountServer.exe После того, как Вы внедрили заплатку в AccountServer.exe, необходимо убедиться что она работает, а уязвимость к внедрению вредоносного SQL-кода исчезла. Чтобы это проверить примените эксплойт к исправленному AccountServer.exe по инструкции, приведенной в разделе "Эксплойт". Вы должны увидеть сообщение: SQL INJECTION FAILED! YOUR ACCOUNTSERVER IS SECURED! Благодарю за внимание!
  4. [Гайд] Пример создания модификации: Вывод дополнительных характеристик персонажа Привет! В данной статье я расскажу как создать мод для клиента игры, который позволит выводить дополнительные характеристики персонажа на форме "Персонаж" (см. скриншот выше). Благодарю участника нашего форума @Graf за идею мода и гайда: Вам понадобится 1) Game.exe для которого будет создаваться модификация; 2) OllyDbg v1.10; 3) IDA Pro (необязательно, можно пользоваться только OllyDbg); 4) Visual Studio 2022 Community Edition и библиотека MS Detours; 5) Исходный код клиента; 6) Знание основ программирования на C/C++ и ассемблере. Навыки написания DLL-библиотек. Опыт работы с вышеуказанными программами. Создание мода В качестве примера реализуем вывод трех дополнительных характеристик: удача (ATTR_LUK ), шанс критического удара (ATTR_CRT ) и шанс дропа (ATTR_MF). 1) Поместим на форму с характеристиками (frmState) персонажа 3 текстовые метки: удача (labLukShow), шанс критического удара (labCriticalShow), шанс дропа (labMfShow). preperty.clu: -- Lucky labLukShow = UI_CreateCompent( frmState, LABELEX_TYPE, "labLukShow", 26, 8, 16, 370 ) UI_SetCaption( labLukShow, "L" ) UI_SetTextColor( labLukShow, COLOR_PURPLE ) UI_SetLabelExFont( labLukShow, DEFAULT_FONT, TRUE, COLOR_WHITE ) -- Critical chance labCriticalShow = UI_CreateCompent( frmState, LABELEX_TYPE, "labCriticalShow", 26, 8, 80, 370 ) UI_SetCaption( labCriticalShow, "C" ) UI_SetTextColor( labCriticalShow, COLOR_PURPLE ) UI_SetLabelExFont( labCriticalShow, DEFAULT_FONT, TRUE, COLOR_WHITE ) -- Drop chance labMfShow = UI_CreateCompent( frmState, LABELEX_TYPE, "labMfShow", 26, 8, 144, 370 ) UI_SetCaption( labMfShow, "M" ) UI_SetTextColor( labMfShow, COLOR_PURPLE ) UI_SetLabelExFont( labMfShow, DEFAULT_FONT, TRUE, COLOR_WHITE ) 2) Изучив исходный код клиента, находим функцию void CStateMgr::RefreshStateFrm(), в которой происходит обновление меток с характеристиками персонажа. Далее нам будет необходимо перехватывать её вызов и обновлять наши новые метки. Так же есть функция void CStateMgr::_evtMainShow(CGuiData *pSender), она вызывается при открытии формы характеристик персонажа и её можно использовать для получения указателей на объекты текстовых меток в памяти игры. Параметр pSender это указатель на форму frmState, объекта класса CCompent, у которого есть метод CCompent* CForm::Find(const char* str), этот метод понадобится для получения указателей на метки по их именам, указанных в lua скриптах GUI клиента. Чтобы установить текст меток, нам будет нужен метод void CLabel::SetCaption(const char * str). Наконец, для доступа к параметрам нашего персонажа, нам будет нужен указатель на персонажа, которым управляет игрок. 3) Для поиска адресов в Game.exe будем использовать программу IDA Pro. void CStateMgr::RefreshStateFrm(). Данный метод можно найти по какой-либо уникальной строке, например, "%4.2f%%". Ищем эту строку в IDA Pro и находим её использование только в одной функции по адресу 0x0047F190 - это и есть искомый метод. void CStateMgr::_evtMainShow(CGuiData *pSender). Это обработчик события открытия формы характеристик персонажа, значит он где-то "вешается" на объект в формы. Проверим в исходном коде клиента игры в методе инициализации формы frmState: bool CStateMgr::Init() { CFormMgr &mgr = CFormMgr::s_Mgr; frmState = _FindForm("frmState"); if( !frmState ) return false; frmState->evtShow = _evtMainShow; labStateName = dynamic_cast<CLabelEx*>(frmState->Find("labStateName")); if( !labStateName ) return Error( g_oLangRec.GetString(45), frmState->GetName(), "labStateName" ); labStateName->SetIsCenter(true); Теперь необходимо найти этот код в Game.exe. Для этого выполняем поиск по строке "frmState". В этот раз нам повезло меньше, найдено 3 использования такой строки: Смотрим первое использование: Рядом так же видно использование строк "btnState", "btnSkill", и "frmSkill". Но в искомом коде таких строк рядом нет. Очевидно, этот адрес нам не подходит. Смотрим второе использование: Строка находится в самом начале функции и рядом есть строка "labStateName". Судя по всему, это наш искомый метод инициализации. Здесь видно, как загружается указатель на форму frmState в регистр EAX, а далее по смещению 0x78, относительно адреса в регистре EAX, помещается какая-то функция. Наверняка, это и есть метод void CStateMgr::_evtMainShow(CGuiData *pSender). Итак, адрес метода void CStateMgr::_evtMainShow(CGuiData *pSender) - 0x0047F7F0. CCompent* CForm::Find(const char* str). Этот метод скорее всего находится в функции инициализации формы, такую мы уже видели: bool CStateMgr::Init(). Просмотрев исходный код, видим следующее: labStateName = dynamic_cast<CLabelEx*>(frmState->Find("labStateName")); if( !labStateName ) return Error( g_oLangRec.GetString(45), frmState->GetName(), "labStateName" ); labStateName->SetIsCenter(true); Это код получения указателя на метку labStateName, нам предстоит сделать то же самое с нашими новыми метками. Идем по адресу 0x0047F9F0 метода bool CStateMgr::Init() в IDA Pro и видим следующий код: .text:0047FA25 push offset aLabstatename ; "labStateName" .text:0047FA2A call dword ptr [eax+48h] Похоже на вызов метода CCompent* CForm::Find(const char* str) для получения указателя на метку labStateName, но его адрес не указан явно. Это говорит о том, что метод виртуальный. Проверим это в исходном коде: virtual CCompent* Find( const char* str ) { return _frmOwn->Find( str ); } Метод действительно является виртуальным. Как же получить его адрес? Как вариант, открыть Game.exe в отладчике, поставить точку останова на адрес 0x0047FA2A и проверить адрес вызываемой функции, но есть проблема - мы не можем запустить клиент напрямую через отладчик, а после запуска эта функция уже не вызывается. Есть второй вариант: поставить точку останова на метод void CStateMgr::_evtMainShow(CGuiData *pSender) (его адрес мы уже знаем), получить адрес формы frmState через параметр pSender, прибавить к нему смещение 0x48 и посмотреть адрес искомого метода. Идем в игру и открываем форму характеристик персонажа: На стеке смотрим адрес параметра pSender - 0x109D5568. По этому адресу находится адрес объекта CStateMgr - 0x006089B4. Прибавляем к этому адресу смещение 0x48, получаем адрес 0x006089FC и переходим по данному адресу в окне отображения памяти процесса: Итак, адрес метода CCompent* CForm::Find(const char* str) равен 0x004941F0. void CLabel::SetCaption(const char * str), в исходном коде вызов этого метода встретится в методе void CStateMgr::RefreshStateFrm(), например: if ( labStateEXP) { if (max!=0) sprintf( pszCha , "%4.2f%%" , num*100.0f/max ); else sprintf( pszCha , "0.00%"); labStateEXP->SetCaption( pszCha ); } Снова ищем строку "%4.2f%%" в IDA Pro и переходим по адресу её использования: Очевидно, что вызов неизвестной функции после двух вызовов библиотечной функции sprintf устанавливает текст метки labStateEXP. Функция является виртуальной, поэтому вновь открываем отладчик, ставим точку останова по адресу 0x0047F2A1 и открываем форму характеристик персонажа в игре: Отладчик подсказывает нам, что по адресу [EDX+3C] находится адрес метода void CLabel::SetCaption(const char * str) - 0x0042B1A0. Указатель на персонажа игрока можно посмотреть в том же методе void CStateMgr::RefreshStateFrm(): CForm * f = g_stUIState.frmState; if( !f->GetIsShow() ) return; CCharacter* pCha = g_stUIBoat.GetHuman(); if( !pCha ) return; SGameAttr* pCChaAttr = pCha->getGameAttr(); if (!pCChaAttr ) return; Переходим на начало метода в IDA Pro: Анализ ассемблерного листинга показывает, что указатель на персонажа игрока находится по адресу 0x00668B6C. Мы нашли все необходимые адреса: 0x0047F7F0 void CStateMgr::_evtMainShow(CGuiData *pSender) 0x0047F190 void CStateMgr::RefreshStateFrm() 0x004941F0 CCompent* CForm::Find(const char* str) 0x0042B1A0 void CLabel::SetCaption(const char * str) 0x00668B6C CCharacter* CBoatMgr::_pHuman 4) Все готово для создания DLL. В качестве языка программирования будем использовать C++. Создаем новый проект Dynamic-Link Library (DLL) в Visual Studio 2022 Community. Поскольку мы будем перехватывать вызовы void CStateMgr::_evtMainShow(CGuiData *pSender) и void CStateMgr::RefreshStateFrm(), то нам понадобится библиотека MS Detours, подключаем её к проекту. Записываем код DLL: #include <windows.h> #include <detours.h> #include <cstdio> namespace pkodev { // Addresses of imported functions from Game.exe namespace address { // void CStateMgr::_evtMainShow(CGuiData *pSender) const unsigned int CStateMgr___evtMainShow = 0x0047F7F0; // void CStateMgr::RefreshStateFrm() const unsigned int CStateMgr__RefreshStateFrm = 0x0047F190; // CCompent* CForm::Find( const char* str ) const unsigned int CForm__Find = 0x004941F0; // void CLabel::SetCaption( const char * str ) const unsigned int CLabel__SetCaption = 0x0042B1A0; // CCharacter* CBoatMgr::_pHuman const unsigned int CBoatMgr___pHuman = 0x00668B6C; } namespace pointer { // void CStateMgr::_evtMainShow(CGuiData *pSender) typedef void(__cdecl* CStateMgr___evtMainShow__Ptr)(void*); CStateMgr___evtMainShow__Ptr CStateMgr___evtMainShow = (CStateMgr___evtMainShow__Ptr)(void*)(address::CStateMgr___evtMainShow); // void CStateMgr::RefreshStateFrm() typedef void(__cdecl* CStateMgr__RefreshStateFrm__Ptr)(void*); CStateMgr__RefreshStateFrm__Ptr CStateMgr__RefreshStateFrm = (CStateMgr__RefreshStateFrm__Ptr)(void*)(address::CStateMgr__RefreshStateFrm); // CCompent* CForm::Find( const char* str ) typedef void* (__thiscall* CForm__Find__Ptr)(void*, const char*); CForm__Find__Ptr CForm__Find = (CForm__Find__Ptr)(void*)(address::CForm__Find); // void CLabel::SetCaption( const char * str ) typedef void (__thiscall* CLabel__SetCaption__Ptr)(void*, const char*); CLabel__SetCaption__Ptr CLabel__SetCaption = (CLabel__SetCaption__Ptr)(void*)(address::CLabel__SetCaption); } namespace hook { // void CStateMgr::_evtMainShow(CGuiData *pSender) void __cdecl CStateMgr___evtMainShow(void* pSender); // void CStateMgr::RefreshStateFrm() void __fastcall CStateMgr__RefreshStateFrm(void* This, void* NotUsed); } // Label "labLukShow" void* labLukShow = nullptr; // Label "labCriticalShow" void* labCriticalShow = nullptr; // Label "labMfShow" void* labMfShow = nullptr; } // Entry point BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { // DLL attached to the proccess case DLL_PROCESS_ATTACH: // Enable hooks DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)pkodev::pointer::CStateMgr___evtMainShow, pkodev::hook::CStateMgr___evtMainShow); DetourAttach(&(PVOID&)pkodev::pointer::CStateMgr__RefreshStateFrm, pkodev::hook::CStateMgr__RefreshStateFrm); DetourTransactionCommit(); break; // DLL detached from the proccess case DLL_PROCESS_DETACH: // Disable hooks DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)pkodev::pointer::CStateMgr___evtMainShow, pkodev::hook::CStateMgr___evtMainShow); DetourDetach(&(PVOID&)pkodev::pointer::CStateMgr__RefreshStateFrm, pkodev::hook::CStateMgr__RefreshStateFrm); DetourTransactionCommit(); break; } return TRUE; } // void CStateMgr::_evtMainShow(CGuiData *pSender) void __cdecl pkodev::hook::CStateMgr___evtMainShow(void* pSender) { // Get pointers to the labels pkodev::labLukShow = pkodev::pointer::CForm__Find(pSender, "labLukShow"); pkodev::labCriticalShow = pkodev::pointer::CForm__Find(pSender, "labCriticalShow"); pkodev::labMfShow = pkodev::pointer::CForm__Find(pSender, "labMfShow"); // Call original CStateMgr___evtMainShow() function pkodev::pointer::CStateMgr___evtMainShow(pSender); } // void CStateMgr::RefreshStateFrm() void __fastcall pkodev::hook::CStateMgr__RefreshStateFrm(void* This, void* NotUsed) { // Call original function CStateMgr::RefreshStateFrm() pkodev::pointer::CStateMgr__RefreshStateFrm(This); // Get pointer to the main character // CCharacter* pCha = g_stUIBoat.GetHuman(); void* pCha = reinterpret_cast<void*>( *reinterpret_cast<unsigned int*>(pkodev::address::CBoatMgr___pHuman) ); if (pCha == nullptr) { // Exit from the hook return; } // Define ATTR constants const unsigned int ATTR_LUK = 30; const unsigned int ATTR_MF = 38; const unsigned int ATTR_CRT = 39; // Get offset to the attribute auto attr_get = [&pCha](unsigned int id) -> unsigned int { return *reinterpret_cast<unsigned int*>( (0xD0 + 4 * id + reinterpret_cast<unsigned int>(pCha)) ); }; // Buffer for labels captions char buf[128]{ 0x00 }; // Show character's ATTR_LUK if (pkodev::labLukShow != nullptr) { sprintf_s(buf, sizeof(buf), "LUK: %d", attr_get(ATTR_LUK)); pkodev::pointer::CLabel__SetCaption(pkodev::labLukShow, buf); } // Show character's ATTR_CRT if (pkodev::labCriticalShow != nullptr) { sprintf_s(buf, sizeof(buf), "CRT: %d", attr_get(ATTR_CRT)); pkodev::pointer::CLabel__SetCaption(pkodev::labCriticalShow, buf); } // Show character's ATTR_MF if (pkodev::labMfShow != nullptr) { sprintf_s(buf, sizeof(buf), "MF: %d", attr_get(ATTR_MF)); pkodev::pointer::CLabel__SetCaption(pkodev::labMfShow, buf); } } 1. В начале определяем найденные нами в пункте 3 адреса методов, функций и объектов; 2. Далее определяем указатели на необходимые функции и методы; 3. Затем определяем функции-перехватчики и глобальные переменные под указатели на текстовые метки; 4. В функции DllMain() записываем код для установки и снятия перехватчиков с помощью библиотеки Detours; 5. Реализуем перехватчик pkodev::hook::CStateMgr___evtMainShow(). В этой функции получаем указатели на объекты новых меток labLukShow, labCriticalShow и labMfShow с помощью метода CCompent* CForm::Find(const char* str); 6. Реализуем перехватчик pkodev::hook::CStateMgr__RefreshStateFrm(). Сначала получаем указатель на персонажа, которым управляет игрок. Далее записываем константы, обозначающие ID требуемых характеристик. Теперь стоит задача получения этих характеристик из памяти игры. В исходном коде клиента для этого используется указатель на объект типа SGameAttr, связанный с персонажем: SGameAttr* pCChaAttr = pCha->getGameAttr(); if (!pCChaAttr ) return; Анализ ассемблерного листинга показывает, что этот указатель находится по смещению 0xD0 относительно указателя на персонажа: .text:0047F1D2 mov ebx, dword_668B6C . . . .text:0047F1E4 add ebx, 0D0h Посмотрим что представляет собой объект SGameAttr в исходном коде: struct SGameAttr { long lAttr[MAX_ATTR_CLIENT]; // MAX_ATTR_CLIENT = 74 . . . }; Это структура, которая содержит только одно поле - массив из 74 целых чисел по 4 байта каждое, индексы элементов которого соответствует ID характеристик персонажа (см. файл AttrType.lua из скриптов GameServer). Из курса программирования мы знаем, что все элементы массива хранятся в памяти друг за другом. Значит, чтобы получить адрес характеристики персонажа с определенным ID, мы можем воспользоваться следующей формулой: 0xD0 + 4 * <ID> + <Адрес персонажа в памяти> Осталось самое простое: вывести требуемые характеристики персонажа в новые текстовые метки с помощью метода void CLabel::SetCaption(const char * str). 5) Компилируем проект и присоединяем DLL библиотеку к процессу игры. В результате мы получим следующее: После успешного тестирования, процесс создания модификации клиента завершен. Отмечу, что в примере я использовал GAME_13X_2, в Вашем Game.exe найденные адреса могут отличаться. Если у Вас возникли вопросы, то смело задавайте их в этой теме. Ниже прикладываю проект DLL-библиотеки и DLL-библиотеку мода. Скачать Архив с проектом DLL библиотеки модификации для Visual Studio 2022 Community и DLL библиотекой модификации из примера (286 Кб) Также на основе данной статьи был создан полноценный мод для вывода характеристик персонажа, который не ограничивается тремя характеристиками:
  5. Всем привет. Хочу поделиться функцией 100% поднятие уровня феи. Как я заметил что ковыряясь по разным сборкам пиратии я видел только поднятие уровня феи с каким ни то шансом. Да и много лишних надписей там имеется. Видел здесь было какое то описание про уровень но он у меня не сработал. Так вот я хочу вам помочь может кому ни то пригодиться ---Фрукт +1 к стату function Elf_LvUp ( role , Item_Num , Item_Traget , attr_type ) local str = GetItemAttr( Item_Traget ,ITEMATTR_VAL_STR ) local con = GetItemAttr( Item_Traget ,ITEMATTR_VAL_CON ) local agi = GetItemAttr( Item_Traget ,ITEMATTR_VAL_AGI ) local dex = GetItemAttr( Item_Traget ,ITEMATTR_VAL_DEX ) local sta = GetItemAttr( Item_Traget ,ITEMATTR_VAL_STA ) local Elf_MaxEXP = GetItemAttr(Item_Traget,ITEMATTR_MAXENERGY) local Elf_MaxURE = GetItemAttr(Item_Traget,ITEMATTR_MAXURE) local Lv = str + agi + dex + con + sta if Lv == 50 then --->Максимальный лвл одиночным фруктом(Можно поставить свое значение) SystemNotice (role , "Фея достигла максимального уровня развития.") return end local attr_type_num = GetItemAttr( Item_Traget ,attr_type ) local item_energe = GetItemAttr( Item_Traget ,ITEMATTR_ENERGY ) AddItemEffect(role , Item_Traget , 0 ) item_energe = 0 SystemNotice (role , "\212\229\255 \243\241\239\229\248\237\238 \226\251\240\238\241\235\224 \226 \243\240\238\226\237\229!") --SystemNotice (role , "Фея успешно выросла в уровне!") attr_type_num = attr_type_num + 1 ---> + сколько дает бига(можно поставить любое значение) SetItemAttr ( Item_Traget , attr_type , attr_type_num ) local Item_MAXENERGY = 240 * ( Lv + 1 ) ---> + сколько дает фрукт(ставить тоже значение что и в attr_type_num = attr_type_num + 1 ) if Item_MAXENERGY > 6480 then Item_MAXENERGY = 6480 end local Item_MAXURE_NUM = Elf_MaxURE + 1000 if Item_MAXURE_NUM > 32000 then Item_MAXURE_NUM = 32000 end SetItemAttr ( Item_Traget , ITEMATTR_MAXENERGY , Item_MAXENERGY ) SetItemAttr ( Item_Traget , ITEMATTR_MAXURE , Item_MAXURE_NUM ) ResetItemFinalAttr(Item_Traget) AddItemEffect(role , Item_Traget , 1 ) SetItemAttr ( Item_Traget , ITEMATTR_ENERGY , item_energe ) end --Фрукт +2 к стату! function Elf_LvUp_1 ( role , Item_Num , Item_Traget , attr_type ) local str = GetItemAttr( Item_Traget ,ITEMATTR_VAL_STR ) local con = GetItemAttr( Item_Traget ,ITEMATTR_VAL_CON ) local agi = GetItemAttr( Item_Traget ,ITEMATTR_VAL_AGI ) local dex = GetItemAttr( Item_Traget ,ITEMATTR_VAL_DEX ) local sta = GetItemAttr( Item_Traget ,ITEMATTR_VAL_STA ) local Elf_MaxEXP = GetItemAttr(Item_Traget,ITEMATTR_MAXENERGY) local Elf_MaxURE = GetItemAttr(Item_Traget,ITEMATTR_MAXURE) local Lv = str + agi + dex + con + sta if Lv == 150 then -----> Максимальный лвл фруктом +2(Можно поставить свое значение) SystemNotice (role , "Фея достигла максимального уровня развития.") return end local attr_type_num = GetItemAttr( Item_Traget ,attr_type ) local item_energe = GetItemAttr( Item_Traget ,ITEMATTR_ENERGY ) AddItemEffect(role , Item_Traget , 0 ) item_energe = 0 SystemNotice (role , "\212\229\255 \243\241\239\229\248\237\238 \226\251\240\238\241\235\224 \226 \243\240\238\226\237\229!") --SystemNotice (role , "Фея успешно выросла в уровне!") attr_type_num = attr_type_num + 2 ---> + сколько дает фрукт(можно поставить любое значение) SetItemAttr ( Item_Traget , attr_type , attr_type_num ) local Item_MAXENERGY = 240 * ( Lv + 2 ) ---> + сколько дает фрукт(ставить тоже значение что и в attr_type_num = attr_type_num + 2 ) if Item_MAXENERGY > 6480 then Item_MAXENERGY = 6480 end local Item_MAXURE_NUM = Elf_MaxURE + 1000 if Item_MAXURE_NUM > 32000 then Item_MAXURE_NUM = 32000 end SetItemAttr ( Item_Traget , ITEMATTR_MAXENERGY , Item_MAXENERGY ) SetItemAttr ( Item_Traget , ITEMATTR_MAXURE , Item_MAXURE_NUM ) ResetItemFinalAttr(Item_Traget) AddItemEffect(role , Item_Traget , 1 ) SetItemAttr ( Item_Traget , ITEMATTR_ENERGY , item_energe ) end Надеюсь данная тема поможет. Спасибо этому форуму
  6. Друзья хочу с вами поделить данной информацией про заточку в нашей любимой игре Пиратия. Данный гайд меня попросили выложить наша Уважаемая Администрация этого чудесного форума. И так приступим разбирать данную проблему многих создателей серверов. Открываем файл файл forge.lua ( Он находиться \resource\script\calculate ) И ищем первую данную функцию --- function can_beuplv_item_main ( щас ее разберем ) function can_beuplv_item_main ( Table ) local role = 0 local ItemBag = {} local ItemCount = {} local ItemBagCount = {} local Get_Count = 4 local ItemReadCount = 0 local ItemReadNow = 1 local ItemReadNext = 0 local ItemBag_Now = 0 local ItemCount_Now = 0 local ItemBagCount_Num = 0 role , ItemBag , ItemCount , ItemBagCount , ItemBag_Now , ItemCount_Now , ItemBagCount_Num = Read_Table ( Table ) local ItemBagCount_beuplv = ItemBagCount [1] local ItemBag_beuplv = ItemBag [1] local ItemNum_beuplv = ItemCount [1] local Item_beuplv = GetChaItem ( role , 2 , ItemBag_beuplv ) local Item_beuplv_Type = GetItemType ( Item_beuplv ) local Item_beuplv_ID = GetItemID ( Item_beuplv ) local Item_ScItem = GetChaItem ( role , 2 , ItemBag [0] ) local Item_YxItem = GetChaItem ( role , 2 , ItemBag [2] ) local Item_ScItem_ID = GetItemID ( Item_ScItem ) local Item_YxItem_ID = GetItemID ( Item_YxItem ) local Item_ScItem_Type = GetItemType ( Item_ScItem ) local Item_YxItem_Type = GetItemType ( Item_YxItem ) local Item_beuplv_Lv = Get_Itembeuplv_Lv ( Item_beuplv ) local ItemAttr_Val_Fusionid = GetItemAttr ( Item_beuplv , ITEMATTR_VAL_FUSIONID ) if Item_beuplv_ID < 5001 or Item_beuplv_ID > 6000 then SystemNotice( role ,"Этот предмет нельзя плавить ") return 0 end if ItemAttr_Val_Fusionid == 0 then SystemNotice( role , "Предмет невозможно улучшить ") return 0 end if Item_beuplv_Lv >= 50 then -- Максимальный уровень улучшения --( расчет идет +4 то есть 50*4=200 заточка получается 200%) SystemNotice( role ,"Вы достигли максимального уровня заточки ") return 0 end if ItemBagCount_beuplv ~= 1 then SystemNotice( role , "Недопустимый уровень улучшения ") return 0 end if Item_ScItem_Type ~= 62 then SystemNotice( role , "У вас нет Улучшающего свитка ") return 0 end if Item_YxItem_Type ~= 63 then SystemNotice( role , "У вас нет Усиливающего кристалла ") return 0 end if ItemNum_beuplv ~= 1 then SystemNotice( role , "Неверный номер улучшения ") return 0 end if ItemCount [0] ~= 1 or ItemCount [2] ~= 1 or ItemBagCount [0] ~= 1 or ItemBagCount [2] ~= 1 then SystemNotice ( role ,"Ошибка предмета из ИМ или игрового материала ") return 0 end local Money_Need = getupgrade_money_main ( Table ) local Money_Have = GetChaAttr ( role , ATTR_GD ) if Money_Need > Money_Have then SystemNotice( role ,"У вас не хватает золота. Невозможно провести улучшение ") return 0 end return 1 end Далее находим вот эту функцию --- function begin_upgrade_item (...) function begin_upgrade_item (...) local Check_Canbeuplv = 0 Check_Canbeuplv = can_beuplv_item_main ( arg ) if Check_Canbeuplv == 0 then return 0 end local role = 0 local ItemBag = {} local ItemCount = {} local ItemBagCount = {} local Get_Count = 4 local ItemReadCount = 0 local ItemReadNow = 1 local ItemReadNext = 0 local ItemBag_Num = 0 local ItemCount_Num = 0 local ItemBagCount_Num = 0 role , ItemBag , ItemCount , ItemBagCount , ItemBag_Num , ItemCount_Num , ItemBagCount_Num = Read_Table ( arg ) local ItemBag_beuplv = ItemBag [1] local Item_beuplv = GetChaItem ( role , 2 , ItemBag_beuplv ) local Item_ScItem = GetChaItem ( role , 2 , ItemBag [0] ) local Item_YxItem = GetChaItem ( role , 2 , ItemBag [2] ) local Item_ScItem_ID = GetItemID ( Item_ScItem ) local Item_YxItem_ID = GetItemID ( Item_YxItem ) local Item_beuplv_Lv = Get_Itembeuplv_Lv ( Item_beuplv ) local R1 = 0 local R2 = 0 R1 = RemoveChaItem ( role , Item_ScItem_ID , 1 , 2 , ItemBag [0] , 2 , 1 , 0 ) R2 = RemoveChaItem ( role , Item_YxItem_ID , 1 , 2 , ItemBag [2] , 2 , 1 , 0 ) if R1 == 0 or R2 == 0 then LG( "beuplv" , "Удалить предмет не удалось " ) end local Money_Need = getupgrade_money_main ( arg ) local Money_Have = GetChaAttr ( role , ATTR_GD ) Money_Have = Money_Have - Money_Need SetCharaAttr ( Money_Have , role , ATTR_GD ) ALLExAttrSet( role ) local a = Check_CG_beuplv ( Item_beuplv_Lv ) if a == 0 then R1 = RemoveChaItem ( role , Item_ScItem_ID , 1 , 2 , ItemBag [0] , 2 , 1 , 0 ) R2 = RemoveChaItem ( role , Item_YxItem_ID , 1 , 2 , ItemBag [2] , 2 , 1 , 0 ) if R1 == 0 or R2 == 0 then LG( "beuplv" , "Удалить предмет не удалось " ) end local cha_name = GetChaDefaultName ( role ) LG( "JingLian_ShiBai" , "Игрок "..cha_name.." не смог улучшить предмет " ) SystemNotice( role , "Извините, заточить аппарель не удалась ") return end Item_beuplv_Lv = Item_beuplv_Lv + 50 --(это сколько уровней сразу поднимется то есть 1 уровень это 2% а 50 уровней это 100% сразу ) SetChaKitbagChange( role , 1 ) Set_Itembeuplv_Lv ( Item_beuplv , Item_beuplv_Lv ) SynChaKitbag( role, 4 ) local LvD = GetItemAttr( Item_beuplv , ITEMATTR_VAL_LEVEL ) SynChaKitbag(role,13) SystemNotice( role , "Вы заточили шмотку ") local cha_name = GetChaDefaultName ( role ) LG( "JingLian_ShiBai" , "Игрок "..cha_name.." успешно провел усиление " ) return 1 end function Get_Itembeuplv_Lv ( Item ) local Lv = GetItemAttr ( Item , ITEMATTR_VAL_LEVEL ) return Lv end function Set_Itembeuplv_Lv ( Item , Item_Lv ) local i = 0 i = SetItemAttr ( Item , ITEMATTR_VAL_LEVEL , Item_Lv ) if i == 0 then LG( "Hecheng_BS","Failed to set gem level" ) end end А теперь разберем шанс заточки находим функцию function Check_CG_beuplv ( Item_Lv ) function Check_CG_beuplv ( Item_Lv ) local ran = math.random ( 1, 100 ) if Item_Lv <= 10 then return 1 -- 100% до первых 10 уровней end if Item_Lv>10 and Item_Lv <= 15 then if ran <= 50 then -- 50% после 10 уровня 5 уровней дают 50 процентов шанс return 1 else return 0 end end if Item_Lv>15 and Item_Lv <= 20 then --- также меняется на ваше количество уровней if ran <= 10 then --10% ну тут уже ясно что это уже ясно после 15 еще 5 лвл будут с процентом 10 return 1 else return 0 end end end Но чтоб сделать все качественно с шансом заточки и не сталкиваться с какой ни то проблемой в этих функциях делайте все внимательно и не торопитесь. Желаю всем удачи и спасибо этому форуму .
  7. Диалог с NPC в определенное время Привет! В данном гайде я расскажу как можно сделать диалог с NPC доступным только в определенное время. Например, с 18:00 до 19:45. Для начала напишем скелет скрипта NPC, который для примера назовем pn_FuncName: function pn_FuncName() InitTrigger() TriggerCondition(1, CheckTime, 1800, 1945) TriggerAction(1, JumpPage, 1) TriggerFailure(1, JumpPage, 2) Start(GetMultiTrigger(), 1) Talk( 1, "Я буду с тобой говорить!" ) Talk( 2, "Я НЕ буду с тобой говорить! Приходи в другое время!" ) end В самом начале скрипта мы определяем условие времени TriggerCondition(1, CheckTime, 1800, 1945) Для этого служит функция CheckTime(t1, t2), которую мы опишем позже. Она принимает два параметра: t1 - начальное время; t2 - конечное время. В данном примере, t1 и t2 соответствуют значения 1800 и 1945, что означает интервал времени от 18:00 до 19:45 включительно. Как Вы могли догадаться, время кодируется следующим образом: t = часы * 100 + минуты Итак, мы определили условие времени. Если оно выполняется, то игрок попадает на страницу 1 и видит сообщение "Я буду с тобой говорить!", в противном случае, игрок попадает на страницу 2 и видит сообщение "Я НЕ буду с тобой говорить! Приходи в другое время!". Все просто! Далее нам необходимо написать функцию CheckTime(t1, t2). Для этого открываем файл functions.lua (GameServer\resource\script\calculate) и помещаем в него следующий код: -- Функция для проверки времени -- Если текущее время попадает в интервал [t1, t2] -- , то функция возвращает true, в противном случае -- функция возвращает false function CheckTime(character, t1, t2) -- Получим текущее время local hour = tonumber(os.date("%H")) * 100 local minute = tonumber(os.date("%M")) -- Закодируем его по формуле -- t = часы * 100 + минуты local check = hour + minute -- Определяем, находится ли текущее время -- в интервале [t1, t2] if (check >= t1 and check <= t2) then -- Время находится в интервале return LUA_TRUE end -- Время не находится в интервале return LUA_FALSE end Чтобы функцию можно было использовать в диалогах NPC, а именно в функции TriggerCondition(...), которая определяет различные условия, её необходимо зарегистрировать в файле MissionSdk.lua (GameServer\resource\script\MisSdk). Найдите в этом файле функцию ConditionsTest(...), а в ней следующий код: if conditions[i].func == NoMission then PRINT( "ConditionsTest:NoMission, p1 = ", conditions[i].p1 ) local Ret = NoMission( character, conditions[i].p1 ) if Ret ~= LUA_TRUE then PRINT( "ConditionsTest: NoMission = false" ) return LUA_FALSE end После данного блока кода вставьте код для функции CheckTime(t1, t2): elseif conditions[i].func == CheckTime then PRINT( "ConditionsTest: CheckTime") local ret = CheckTime(character) if ret ~= LUA_TRUE then PRINT( "ConditionsTest: CheckTime = false" ) return LUA_FALSE end На этом все, теперь Вы можете создать NPC c функцией pn_FuncName (название взято для примера) и протестировать его. P.S. За идею были взяты NPC с о. Весны, которые говорят с игроком, только когда он прошел квест "Языковой барьер"; P.S.S. В функцию CheckTime(t1, t2) можно так же добавить проверку на день недели. См. Справочное руководство по языку Lua 5.1 - 5.8 – Функции операционной системы; P.S.S.S. По такому же принципу можно делать любые проверки, например, на расу и профессию персонажа игрока.
  8. Бонусы и свечение от Колец 85 уровня + Death-сет Привет! В данном гайде мы введем на Ваш сервер бонусы и свечения от сочетания колец 85 уровня с дет-сетом. Сначала познакомимся с бонусами и видами свечения: Чемпион (Сила Хардина) +26 силы, +14 телосложения, +300 макс. ЖЗ, +10 шанс попадания, +10 уклонения, +100 защиты, +6 рег. ЖЗ, +4 физ. защиты Воитель (Сила Тьмы) +20 силы, +14 телосложения, +6 ловкости, +220 макс. ЖЗ, +10 шанс попадания, +30 уклонения, +80 защиты, +8 рег. ЖЗ, +4 физ. защиты Стрелок (Адское Перышко) +5 скорости атаки, +10 телосложения, +8 ловкости, +22 точности, +250 макс. ЖЗ, +30 шанс попадания, +15 уклонения, +75 защиты, +6 рег. ЖЗ, +3 физ. защиты Покоритель морей (Душа Асуры) +18 телосложения, +22 духа, +160 макс. ЖЗ, +110 макс. МН, +30 шанс попадания, +75 защиты, +4 рег ЖЗ, +2 рег МН, +2 физ. защиты Целительница (Кристалл Духа Бездны) +6 телосложения, +4 точности, +30 духа, +280 макс. МН, +30 шанс попадания, +55 защиты, +6 рег МН, +2 физ. защиты Ведьма (Очернение Стикса) +12 телосложения, +28 духа, +180 макс. МН, +30 шанс попадания, +55 защиты, +8 рег МН, +2 физ. защиты Далее введем на сервер кольца, если у Вас их еще нет. Для этого откроем файл ItemInfo.txt (GameServer\resource) и допишем в него следующие строки: 2577 Сила Хардина n2216 10130005 0 0 0 0 0 0 26 0 0 0 0 0 0 0 0 1 1 1 500000 -1,-2,-2,-2 85 8,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 8,-2,-2,-2,-2,-2,-2,-2,-2,-2 -1,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 16,16 0,0 0,0 6,6 0,0 0,0 0,0 0,0 0,0 0,0 0,0 300,300 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0 7000,7000 10000,10000 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0 0 0 0 2578 Сила Тьмы n2215 10130005 0 0 0 0 0 0 26 0 0 0 0 0 0 0 0 1 1 1 500000 -1,-2,-2,-2 85 9,10,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 8,-2,-2,-2,-2,-2,-2,-2,-2,-2 -1,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12,12 3,3 0,0 6,6 0,0 0,0 0,0 0,0 0,0 0,0 0,0 220,220 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0 7000,7000 10000,10000 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0 0 0 0 2579 Адское Перышко n2219 10130005 0 0 0 0 0 0 26 0 0 0 0 0 0 0 0 1 1 1 500000 -1,-2,-2,-2 85 12,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 8,-2,-2,-2,-2,-2,-2,-2,-2,-2 -1,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 4,4 14,14 4,4 0,0 0,0 5,5 0,0 0,0 0,0 0,0 250,250 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0 7000,7000 10000,10000 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0 0 0 0 2580 Душа Азуры n2217 10130005 0 0 0 0 0 0 26 0 0 0 0 0 0 0 0 1 1 1 500000 -1,-2,-2,-2 85 16,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 8,-2,-2,-2,-2,-2,-2,-2,-2,-2 -1,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0,0 0,0 10,10 12,12 0,0 0,0 0,0 0,0 0,0 0,0 300,300 110,110 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0 7000,7000 10000,10000 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0 0 0 0 2581 Очернение Стикса n2220 10130005 0 0 0 0 0 0 26 0 0 0 0 0 0 0 0 1 1 1 500000 -1,-2,-2,-2 85 14,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 8,-2,-2,-2,-2,-2,-2,-2,-2,-2 -1,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0,0 0,0 5,5 18,18 0,0 0,0 0,0 0,0 0,0 0,0 0,0 180,180 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0 7000,7000 10000,10000 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0 0 0 0 2582 Кристалл Духа Бездны n2218 10130005 0 0 0 0 0 0 26 0 0 0 0 0 0 0 0 1 1 1 500000 -1,-2,-2,-2 85 13,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 8,-2,-2,-2,-2,-2,-2,-2,-2,-2 -1,-2,-2,-2,-2,-2,-2,-2,-2,-2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0,0 2,2 3,3 18,18 0,0 0,0 0,0 0,0 0,0 0,0 0,0 280,280 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0,0 0 7000,7000 10000,10000 0 0 0 0 0 0 0 0 0 0 0 0 0 0,0 0 0 0 0 Затем добавим эффекты (свечение и бонусы) для колец в файл SkillEff.txt (GameServer\resource): 200 Lv85 BB Cow Кольцо -1 0 State_BBRING1_Add State_BBRING1_Rem 3 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 -1 0 0 0 574 2 0 0 0 0 0 0 0 201 Lv85 BB Twined Кольцо -1 0 State_BBRING2_Add State_BBRING2_Rem 3 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 -1 0 0 0 576 2 0 0 0 0 0 0 0 202 Lv85 BB Hunting Кольцо -1 0 State_BBRING3_Add State_BBRING3_Rem 3 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 -1 0 0 0 575 2 0 0 0 0 0 0 0 203 Lv85 BB Navigating Кольцо -1 0 State_BBRING4_Add State_BBRING4_Rem 3 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 -1 0 0 0 573 2 0 0 0 0 0 0 0 204 Lv85 BB Sealing Кольцо -1 0 State_BBRING5_Add State_BBRING5_Rem 3 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 -1 0 0 0 572 2 0 0 0 0 0 0 0 205 Lv85 BB Holy Кольцо -1 0 State_BBRING6_Add State_BBRING6_Rem 3 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 -1 0 0 0 577 2 0 0 0 0 0 0 0 После открываем файл variable.lua (GameServer\resource\script\calculate) и допишем в него переменные, которые будут обозначать ID эффектов из SkillEff.txt: STATE_BBRING1 = 200 STATE_BBRING2 = 201 STATE_BBRING3 = 202 STATE_BBRING4 = 203 STATE_BBRING5 = 204 STATE_BBRING6 = 205 Далее в файл SkillEffect.lua (GameServer\resource\script\calculate) добавим скрипты, которые будут давать персонажу бонусы от кольца с дэт-сетом: --Lv85 BB Cow Кольцо function State_BBRING1_Add ( role , statelv ) local strsb_dif = 10 local strsb = StrSb( role ) + strsb_dif SetCharaAttr( strsb , role , ATTR_STATEV_STR ) local consb_dif = 8 local consb = ConSb( role ) + consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local defsb_dif = 100 local defsb = DefSb(role) + defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local fleesb_dif = 10 local fleesb = FleeSb(role) + fleesb_dif SetCharaAttr( fleesb , role , ATTR_STATEV_FLEE ) local hitsb_dif = 10 local hitsb = HitSb(role) + hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local hrecsb_dif = 6 local hrecsb = HrecSb(role) + hrecsb_dif SetCharaAttr( hrecsb , role , ATTR_STATEV_HREC ) local pdef_dif = 4 local pdef = ResistSb(role) + pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end function State_BBRING1_Rem ( role , statelv ) local strsb_dif = 10 local strsb = StrSb( role ) - strsb_dif SetCharaAttr( strsb , role , ATTR_STATEV_STR ) local consb_dif = 8 local consb = ConSb( role ) - consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local defsb_dif = 100 local defsb = DefSb(role) - defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local fleesb_dif = 10 local fleesb = FleeSb(role) - fleesb_dif SetCharaAttr( fleesb , role , ATTR_STATEV_FLEE ) local hitsb_dif = 10 local hitsb = HitSb(role) - hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local hrecsb_dif = 6 local hrecsb = HrecSb(role) - hrecsb_dif SetCharaAttr( hrecsb , role , ATTR_STATEV_HREC ) local pdef_dif = 4 local pdef = ResistSb(role) - pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end --Lv85 BB Twined Кольцо function State_BBRING2_Add ( role , statelv ) local strsb_dif = 8 local strsb = StrSb( role ) + strsb_dif SetCharaAttr( strsb , role , ATTR_STATEV_STR ) local agisb_dif = 3 local agisb = AgiSb( role ) + agisb_dif SetCharaAttr( agisb , role , ATTR_STATEV_AGI ) local consb_dif = 8 local consb = ConSb( role ) + consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local defsb_dif = 80 local defsb = DefSb(role) + defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local fleesb_dif = 30 local fleesb = FleeSb(role) + fleesb_dif SetCharaAttr( fleesb , role , ATTR_STATEV_FLEE ) local hitsb_dif = 10 local hitsb = HitSb(role) + hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local hrecsb_dif = 8 local hrecsb = HrecSb(role) + hrecsb_dif SetCharaAttr( hrecsb , role , ATTR_STATEV_HREC ) local pdef_dif = 4 local pdef = ResistSb(role) + pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end function State_BBRING2_Rem ( role , statelv ) local strsb_dif = 8 local strsb = StrSb( role ) - strsb_dif SetCharaAttr( strsb , role , ATTR_STATEV_STR ) local agisb_dif = 3 local agisb = AgiSb( role ) - agisb_dif SetCharaAttr( agisb , role , ATTR_STATEV_AGI ) local consb_dif = 8 local consb = ConSb( role ) - consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local defsb_dif = 80 local defsb = DefSb(role) - defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local fleesb_dif = 30 local fleesb = FleeSb(role) - fleesb_dif SetCharaAttr( fleesb , role , ATTR_STATEV_FLEE ) local hitsb_dif = 10 local hitsb = HitSb(role) - hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local hrecsb_dif = 8 local hrecsb = HrecSb(role) - hrecsb_dif SetCharaAttr( hrecsb , role , ATTR_STATEV_HREC ) local pdef_dif = 4 local pdef = ResistSb(role) - pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end --Lv85 BB Hunting Кольцо function State_BBRING3_Add ( role , statelv ) local agisb_dif = 4 local agisb = AgiSb( role ) + agisb_dif SetCharaAttr( agisb , role , ATTR_STATEV_AGI ) local dexsb_dif = 8 local dexsb = DexSb( role ) + dexsb_dif SetCharaAttr( dexsb , role , ATTR_STATEV_DEX ) local consb_dif = 6 local consb = ConSb( role ) + consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local defsb_dif = 75 local defsb = DefSb(role) + defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local fleesb_dif = 15 local fleesb = FleeSb(role) + fleesb_dif SetCharaAttr( fleesb , role , ATTR_STATEV_FLEE ) local hitsb_dif = 30 local hitsb = HitSb(role) + hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local hrecsb_dif = 6 local hrecsb = HrecSb(role) + hrecsb_dif SetCharaAttr( hrecsb , role , ATTR_STATEV_HREC ) local pdef_dif = 3 local pdef = ResistSb(role) + pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end function State_BBRING3_Rem ( role , statelv ) local agisb_dif = 4 local agisb = AgiSb( role ) - agisb_dif SetCharaAttr( agisb , role , ATTR_STATEV_AGI ) local dexsb_dif = 8 local dexsb = DexSb( role ) - dexsb_dif SetCharaAttr( dexsb , role , ATTR_STATEV_DEX ) local consb_dif = 6 local consb = ConSb( role ) - consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local defsb_dif = 75 local defsb = DefSb(role) - defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local fleesb_dif = 15 local fleesb = FleeSb(role) - fleesb_dif SetCharaAttr( fleesb , role , ATTR_STATEV_FLEE ) local hitsb_dif = 30 local hitsb = HitSb(role) - hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local hrecsb_dif = 6 local hrecsb = HrecSb(role) - hrecsb_dif SetCharaAttr( hrecsb , role , ATTR_STATEV_HREC ) local pdef_dif = 3 local pdef = ResistSb(role) - pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end --Lv85 BB Navigating Кольцо function State_BBRING4_Add ( role , statelv ) local consb_dif = 8 local consb = ConSb( role ) + consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local stasb_dif = 10 local stasb = StaSb( role ) + stasb_dif SetCharaAttr( stasb , role , ATTR_STATEV_STA ) local defsb_dif = 75 local defsb = DefSb(role) + defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local hitsb_dif = 30 local hitsb = HitSb(role) + hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local hrecsb_dif = 4 local hrecsb = HrecSb(role) + hrecsb_dif SetCharaAttr( hrecsb , role , ATTR_STATEV_HREC ) local srecsb_dif = 2 local srecsb = SrecSb(role) + srecsb_dif SetCharaAttr( srecsb , role , ATTR_STATEV_SREC ) local pdef_dif = 3 local pdef = ResistSb(role) + pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end function State_BBRING4_Rem ( role , statelv ) local consb_dif = 8 local consb = ConSb( role ) - consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local stasb_dif = 10 local stasb = StaSb( role ) - stasb_dif SetCharaAttr( stasb , role , ATTR_STATEV_STA ) local defsb_dif = 75 local defsb = DefSb(role) - defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local hitsb_dif = 30 local hitsb = HitSb(role) - hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local hrecsb_dif = 4 local hrecsb = HrecSb(role) - hrecsb_dif SetCharaAttr( hrecsb , role , ATTR_STATEV_HREC ) local srecsb_dif = 2 local srecsb = SrecSb(role) - srecsb_dif SetCharaAttr( srecsb , role , ATTR_STATEV_SREC ) local pdef_dif = 3 local pdef = ResistSb(role) - pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end --Lv85 BB Sealing Кольцо function State_BBRING5_Add ( role , statelv ) local consb_dif = 7 local consb = ConSb( role ) + consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local stasb_dif = 10 local stasb = StaSb( role ) + stasb_dif SetCharaAttr( stasb , role , ATTR_STATEV_STA ) local defsb_dif = 55 local defsb = DefSb(role) + defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local hitsb_dif = 30 local hitsb = HitSb(role) + hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local srecsb_dif = 8 local srecsb = SrecSb(role) + srecsb_dif SetCharaAttr( srecsb , role , ATTR_STATEV_SREC ) local pdef_dif = 2 local pdef = ResistSb(role) + pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end function State_BBRING5_Rem ( role , statelv ) local consb_dif = 7 local consb = ConSb( role ) - consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local stasb_dif = 10 local stasb = StaSb( role ) - stasb_dif SetCharaAttr( stasb , role , ATTR_STATEV_STA ) local defsb_dif = 55 local defsb = DefSb(role) - defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local hitsb_dif = 30 local hitsb = HitSb(role) - hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local srecsb_dif = 8 local srecsb = SrecSb(role) - srecsb_dif SetCharaAttr( srecsb , role , ATTR_STATEV_SREC ) local pdef_dif = 2 local pdef = ResistSb(role) - pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end --Lv85 BB Holy Кольцо function State_BBRING6_Add ( role , statelv ) local dexsb_dif = 2 local dexsb = DexSb( role ) + dexsb_dif SetCharaAttr( dexsb , role , ATTR_STATEV_DEX ) local consb_dif = 3 local consb = ConSb( role ) + consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local stasb_dif = 12 local stasb = StaSb( role ) + stasb_dif SetCharaAttr( stasb , role , ATTR_STATEV_STA ) local defsb_dif = 55 local defsb = DefSb(role) + defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local hitsb_dif = 30 local hitsb = HitSb(role) + hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local srecsb_dif = 6 local srecsb = SrecSb(role) + srecsb_dif SetCharaAttr( srecsb , role , ATTR_STATEV_SREC ) local pdef_dif = 2 local pdef = ResistSb(role) + pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end function State_BBRING6_Rem ( role , statelv ) local dexsb_dif = 2 local dexsb = DexSb( role ) - dexsb_dif SetCharaAttr( dexsb , role , ATTR_STATEV_DEX ) local consb_dif = 3 local consb = ConSb( role ) - consb_dif SetCharaAttr( consb , role , ATTR_STATEV_CON ) local stasb_dif = 12 local stasb = StaSb( role ) - stasb_dif SetCharaAttr( stasb , role , ATTR_STATEV_STA ) local defsb_dif = 55 local defsb = DefSb(role) - defsb_dif SetCharaAttr( defsb , role , ATTR_STATEV_DEF ) local hitsb_dif = 30 local hitsb = HitSb(role) - hitsb_dif SetCharaAttr( hitsb , role , ATTR_STATEV_HIT ) local srecsb_dif = 6 local srecsb = SrecSb(role) - srecsb_dif SetCharaAttr( srecsb , role , ATTR_STATEV_SREC ) local pdef_dif = 2 local pdef = ResistSb(role) - pdef_dif SetCharaAttr( pdef , role , ATTR_STATEV_PDEF ) ALLExAttrSet(role) end Осталось добавить наложение эффекта от сочетания кольца с дет-сетом на персонажа. Для этого открываем файл functions.lua (GameServer\resource\script\calculate) и ищем в нем функцию cha_timer(role, freq, time). В конец этой функции (перед последним закрывающим end) добавляем следующий код: if (is_role_living == 1 and math.mod(now_tick, resume_freq) == 0 and now_tick > 0) then local Dina_xl = GetEquipItemP(role, 8) local Dina_xl_ID = GetItemID (Dina_xl) local Dina_x2 = GetEquipItemP(role, 2) local Dina_x2_ID = GetItemID (Dina_x2) if (Dina_xl_ID == 2577 and Dina_x2_ID == 2817) then local statelv = 1 local statetime = 3600 AddState(role, role, STATE_BBRING1, statelv, statetime) elseif (Dina_xl_ID == 2578 and Dina_x2_ID == 2820) then local statelv = 1 local statetime = 3600 AddState(role, role, STATE_BBRING2, statelv, statetime) elseif (Dina_xl_ID == 2579 and Dina_x2_ID == 2823) then local statelv = 1 local statetime = 3600 AddState(role, role, STATE_BBRING3, statelv, statetime) elseif (Dina_xl_ID == 2580 and Dina_x2_ID == 2826) then local statelv = 1 local statetime = 3600 AddState(role, role, STATE_BBRING4, statelv, statetime) elseif (Dina_xl_ID == 2581 and Dina_x2_ID == 2832) then local statelv = 1 local statetime = 3600 AddState(role, role, STATE_BBRING5, statelv, statetime) elseif (Dina_xl_ID == 2582 and Dina_x2_ID == 2829) then local statelv = 1 local statetime = 3600 AddState(role, role, STATE_BBRING6, statelv, statetime) else local statelv_bbring1 = GetChaStateLv(role, STATE_BBRING1) if (statelv_bbring1 ~= 0) then RemoveState(role, STATE_BBRING1) end local statelv_bbring2 = GetChaStateLv(role, STATE_BBRING2) if (statelv_bbring2 ~= 0) then RemoveState(role, STATE_BBRING2) end local statelv_bbring3 = GetChaStateLv(role, STATE_BBRING3) if (statelv_bbring3 ~= 0) then RemoveState(role, STATE_BBRING3) end local statelv_bbring4 = GetChaStateLv(role, STATE_BBRING4) if (statelv_bbring4 ~= 0) then RemoveState(role, STATE_BBRING4) end local statelv_bbring5 = GetChaStateLv(role, STATE_BBRING5) if (statelv_bbring5 ~= 0) then RemoveState(role, STATE_BBRING5) end local statelv_bbring6 = GetChaStateLv(role, STATE_BBRING6) if (statelv_bbring6 ~= 0) then RemoveState(role, STATE_BBRING6) end end end На этом все! Теперь у Вашего сервера есть еще одна уникальная особенность :D P.S. Под сочетанием "85 кольцо и дет-сет" в данном гайде понимается одновременно экипированные кольцо и тело. Вы можете добавить проверку на руковицы и ботинки в скрипт для функции cha_timer(role, freq, time). Тогда свечение и бонусы будут работать только при условии полного набора дет-экипировки (тело + руковицы + ботинки); P.S.S. Не забудьте скомпилировать ItemInfo.txt и SkillEff.txt для клиента; P.S.S.S. Текстуры и анимации для свечения можно взять из последнего TOP II клиента.
  9. Защита GM команд Привет! Предположим, Ваш сервер был взломан и злоумышленник получил доступ к GM-аккаунту. В данной статье я расскажу как обезопасить Ваш сервер от его действий, то есть сделать взлом бессмысленным. Внимание! Особое внимание уделите командам &lua и &lua_all. С их помощью злоумышленник может получить контроль над машиной, где запущен GameServer.exe, со всеми вытекающими из этого последствиями: кража Вашей сборки либо её удаление, вайп базы данных, потеря доступа к машине и так далее. Обсуждение этих команд проходит в данной теме: Все прелести команды &lua. Для обеспечения безопасности Вашего сервера, не используйте имена из примеров данной статьи на своем сервере! Способ №1. Нет GM-команд - нет проблем! Если Вы редко или вообще не пользуетесь GM-командами, то имеет смысл их отключить. Редактировать персонажей и выдавать предметы можно с помощью специальных программ или вручную в базе данных, когда персонаж не в игре. Чтобы отключить GM-команды нужно внести изменения в GameServer.exe. Версия | Размер (КБ) | Адрес --------+-------------+------------ 1.36 | 2 040 | 0x000DE1E8 1.38 | 2 088 | 0x000E6852 2.0 | 3 000 | 0x00161349 Откройте GameServer.exe в любом HEX редакторе и перейдите по адресу из таблицы. Я буду работать с 1.38 GameServer.exe в редакторе HxD. Замените 23 байта начиная с этого адреса на 0x90 Сохраните проделанные изменения и убедитесь, что в игре не работают GM-команды. Способ №2. Переименовывание GM-команд. Злоумышленник не сможет воспользоваться GM-командами, если он не будет знать их названия. Итак, Вам придется переименовать каждую команду. Чтобы изменить название GM-команды, откройте GameServer.exe в любом HEX-редакторе. Затем найдите GM-команду и измените ее название. Длина нового названия должна быть такой же, как и длина старого. Для примера, переименуем команду &make в команду &give. Обратите внимание, что длины строк "make" и "give" равны (по 4 символа). Открываем GameServer.exe в HEX-редакторе и ищем строку "make": Вам будут попадаться строки, которые содержат в себе подстроку "make" (например, "MakeItem", "make failed!", "GMmakeLog" и др.). Игнорируйте их и продолжайте поиск. Когда Вы найдете нужную строку, Вы увидите рядом названия других GM-команд: Заменяем на "give": Сохраните проделанные изменения. Повторите данные действия для каждой GM-команды. Список адресов GM-команд GameServer.exe версии 1.36 (2 040 КБ): GameServer.exe версии 1.38 (2 088 КБ): GameServer.exe версии 2.4 (3 000 КБ): Способ N3. GameServer.exe с функциями HandleChat(), GetGmLv() и SetGmLv() Для реализации данного способа Вам потребуется модифицированный GameServer.exe с функциями HandleChat(), GetGmLv() и SetGmLv(). Функция HandleChat(userdata role, string message) срабатывает, когда игрок пишет сообщение в местный чат. Так как GM-команды отправляются также в местный чат, Вы можете написать скрипт, который будет контролировать выполнение команд. Например, Вы можете сделать так, чтобы GM-команды срабатывали только при условии, что их отправляет персонаж с определенным именем или ID. Плюс ко всему, можно сделать чтобы этот персонаж должен еще состоять в определенной гильдии, в которую входят только администраторы и GM Вашего сервера. Все зависит от Вашей фантазии. Чтобы узнать, что персонаж является GM, используйте функцию GetGmLv(userdata role). С помощью функции SetGmLv(userdata role, number level) можно изменять GM-уровень аккаунта, к которому прикреплен персонаж. Например, в той же функции HandleChat() можно сбрасывать GM-уровень аккаунта в 0, если персонаж не прошел проверку. Реализуем простую систему контроля GM-команд: 1) GM-команды могут выполнять только персонажи с именами: "V3ct0r", "pkodev" и "Administrator"; 2) В случае, если персонаж GM и он не прошел проверку, сбросить GM-уровень до 0, кикнуть персонажа с сервера и отправить сообщение администратору в консоль GameServer.exe. Для начала создадим в файле variable.lua массив с именами персонажей, которые могут использовать GM-команды. Назовем его PlayerCanUseCmd: PlayerCanUseCmd = {} PlayerCanUseCmd["V3ct0r"] = 1 PlayerCanUseCmd["pkodev"] = 1 PlayerCanUseCmd["Administrator"] = 1 Затем напишем скрипт для функции HandleChat() в файле functions.lua: -- Обработчик местного чата function HandleChat(role, message) -- Проверяем что персонаж GM if (GetGmLv(role) > 0) then -- Проверяем что персонаж отправил GM-команду if (string.find(message, "&") == 1) then -- Проверяем имя персонажа local cha_name = GetChaDefaultName(role) if (PlayerCanUseCmd[cha_name] ~= nil) then -- Персонаж может использовать GM-команду return 1 end -- Персонаж не может использовать GM-команду -- Сбрасываем GM-уровень до 0 SetGmLv(role, 0) -- Кикаем персонажа с сервера KickCha(role) -- Пишем сообщение администратору в консоль сервера print("Player [" .. cha_name .."] trying to use GM command!") -- Запрещаем выполнение GM-команды return 0 end end return 1 end Для кика персонажей нам понадобится функция KickCha(), добавьте ее также в functions.lua: function KickCha(character) local pkt = GetPacket() WriteCmd(pkt, 1505) SendPacket(character,pkt) end Тема открыта для обсуждения. Пишите свои вопросы и идеи в комментариях!
  10. Много у кого были проблемы с подключением 2.х клиента к серверу. В этом гайде я расскажу все подробно, как сделать это. Настройка выбора сервера/региона 1) Заходим в наш клиент. Идем в папку "Flash" 2) Находим файл "111.xml" и открываем его при помощи текстового редактора 3) Видим следующие строчки: <?xml version="1.0" encoding="utf-8"?> <data1 name="imagebear"><data2 pic="1111.png" con1="Tortuga" con2="Kortes" con3=""/></data1> <data1 name="imagebear"><data2 pic="2222.png" con1="Tortuga" con2="Morgan" con3=""/></data1> Где: con1="Tortuga" - Сервер con2="Kortes" и con2="Morgan" - Регионы P.S: Дальше я буду рассказывать только про 1 регион, т.к. рассказывать конфиг для 2х и более регионов нету смысла. 4) Вводим свои названия для Сервера и Региона. Вот мои: con1="Piratia" con2="Lagoon" В итоге все должно выйти так: <?xml version="1.0" encoding="utf-8"?> <data1 name="imagebear"><data2 pic="1111.png" con1="[COLOR=DarkOrchid]Piratia[/COLOR]" con2="[COLOR=Blue]Lagoon[/COLOR]" con3=""/></data1> 5) Находим файл "flash.xml" и открываем его текстовым редактором 6) Видим следующие строки: <?xml version="1.0" encoding="utf-8"?> <data1 name="imagebear"> <data2 pic="1111.png" con1="Tortuga" con2="Kortes" con3=""/> <data2 pic="2222.png" con1="" con2="undefined" con3=""/> <data2 pic="3333.png" con1="" con2="undefined" con3=""/> <data2 pic="4444.png" con1="" con2="undefined" con3=""/> <data2 pic="5555.png" con1="" con2="undefined" con3=""/> <data2 pic="6666.png" con1="" con2="undefined" con3=""/> <data2 pic="7777.png" con1="" con2="undefined" con3=""/> <data2 pic="8888.png" con1="" con2="undefined" con3=""/></data1> Где: con1="Tortuga" con2="Kortes" Меняем на свои: con1="Piratia" con2="Lagoon" В итоге получаем такой код: <?xml version="1.0" encoding="utf-8"?> <data1 name="imagebear"> <data2 pic="1111.png" con1="Piratia" con2="Lagoon" con3=""/> <data2 pic="2222.png" con1="" con2="undefined" con3=""/> <data2 pic="3333.png" con1="" con2="undefined" con3=""/> <data2 pic="4444.png" con1="" con2="undefined" con3=""/> <data2 pic="5555.png" con1="" con2="undefined" con3=""/> <data2 pic="6666.png" con1="" con2="undefined" con3=""/> <data2 pic="7777.png" con1="" con2="undefined" con3=""/> <data2 pic="8888.png" con1="" con2="undefined" con3=""/></data1> 7) Находим файл "text01.xml" и открываем его текстовым редактором. Видим следующий код: <?xml version="1.0" encoding="utf-8"?> <data1 name="imagebear"> <data2 con="Kortes"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/></data1> Где: <data2 con="Kortes"/> Меняем на свой регион: <data2 con="Lagoon"/> В итоге получаем следующий код: <?xml version="1.0" encoding="utf-8"?> <data1 name="imagebear"> <data2 con="Lagoon"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/> <data2 con="undefined"/></data1> 8) Переходим обратно в папку с клиентом, а затем переходим по пути "Пиратия Online II\scripts\txt\" 9) Там находим файл "server.tx". Открываем его текстовым редактором и видим следующий код: 1, Tortuga Меняем сервер на свой: 1, Piratia Создаем ServerSet.bin для 2.X клиента P.S: Сразу говорю, программ для генерации IP + своих регионов (Типа IPChanager) используйте "Тык" или "Тык" или по старинке все будем делать в ручную компилятором. 1) Создаем новый txt файл и называем его ServerSet.txt 2) Открываем его текстовым редактором и пишем туда: 1 Lagoon Piratia ipсервера ipсервера 0 0 0 P.S: У себя я буду использовать локальный ip адрес - 127.0.0.1, а Вы можете поставить свой ip адрес, будь то Hamachi, будь то внешний IP адрес 3) В итоге что у меня вышло: 1 Lagoon Piratia 127.0.0.1 127.0.0.1 0 0 0 4) не забываем расставлять "Табы" и пользоваться "Notepade++" P.S на любителя.... 5) Сохраняем файл. Удаляем старый ServerSet.bin и компилируем новый файл. (Компиляция происходит так же как и на версии 1.3х и тем же компилятором) Компилятор для DLT клиента - Disc.Yandex Внимание!!! P.S: Если у Вас что-то не получилось, то не надо писать в эту тему, что у Вас не получилось. Для этого специально создан раздел - "Помощь". Если будут замечены ошибки и недочеты, то смело пишите их в комментариях. Так же приветствуется адекватная критика. Гайд by ElectroKiller - @MrSharp Копирование на сторонние ресурсы только с размещением копирайтов на оригинал
  11. Лут с монстров Всем привет! В этом гайде я расскажу как настраивать лут (дроп) с монстров. Лут (дроп) - это список предметов, которые с определенной вероятностью выпадают с монстра при его убийстве. У каждого монстра в characterinfo.txt есть 2 поля, которые нас интересуют: список предметов (47 колонка) и список шансов их выпадения (48 колонка). Список предметов выглядит следующим образом: ID1,ID2,ID3,ID4,ID5,ID6,ID7,ID8,ID9,ID10 IDn - это ID предмета. Всего у монстра может быть максимум 10 предметов. ID предметов указываются через запятую без пробелов. Если, например, из монстра выпадает всего 5 предметов, то через запятую указывается 5 ID предметов. Список шансов выпадения предметов выглядит аналогично: ШАНС1,ШАНС2,ШАНС3,ШАНС4,ШАНС5,ШАНС6,ШАНС7,ШАНС8,ШАНС9,ШАНС10 ШАНСn представляет собой число, которое определяет вероятность выпадения соответствующего n-го предмета и рассчитывается по формуле: ШАНСn = 100 / %n * MF_RAID, где 100 - константа %n - вероятность выпадения n-го предмета MF_RAID - рейт сервера на выпадение предметов из монстров (определен в variable.lua) из этой формулы можно выразить вероятность выпадения предмета (%): %n = 100 / ШАНСn * MF_RAID Шансы выпадения предметов так же указываются через запятую без каких-либо пробелов и их должно быть столько же, сколько и предметов. ШАНСn указывается для предмета с id IDn, то есть: ID1 = ШАНС1 -- Предмет с id ID1 выпадет с вероятностью (%) 100 / ШАНС1 * MF_RAID ID2 = ШАНС2 -- Предмет с id ID2 выпадет с вероятностью (%) 100 / ШАНС2 * MF_RAID ID3 = ШАНС3 -- Предмет с id ID3 выпадет с вероятностью (%) 100 / ШАНС3 * MF_RAID ID4 = ШАНС4 -- Предмет с id ID4 выпадет с вероятностью (%) 100 / ШАНС4 * MF_RAID ID5 = ШАНС5 -- Предмет с id ID5 выпадет с вероятностью (%) 100 / ШАНС5 * MF_RAID ID6 = ШАНС6 -- Предмет с id ID6 выпадет с вероятностью (%) 100 / ШАНС6 * MF_RAID ID7 = ШАНС7 -- Предмет с id ID7 выпадет с вероятностью (%) 100 / ШАНС7 * MF_RAID ID8 = ШАНС8 -- Предмет с id ID8 выпадет с вероятностью (%) 100 / ШАНС8 * MF_RAID ID9 = ШАНС9 -- Предмет с id ID9 выпадет с вероятностью (%) 100 / ШАНС9 * MF_RAID ID10 = ШАНС10 -- Предмет с id ID10 выпадет с вероятностью (%) 100 / ШАНС10 * MF_RAID Примеры: Для первого примера возьмем монстра Свинокрыл (ID: 237). Рейт сервера на выпадение предметов будет равен x1 (MF_RAID = 1).тОткрываем characterinfo.txt и ищем Свинокрыла: 237 Свинокрыл Свинокрыл 4 5 180 0 1 1 0 0 0 0 0 0 0 0 0 0 1 130 100 353 354 355 0 0 0 0 1.754 1.772 2.983 60 1,5 2,5 399 398 0 0 0 1 1 0 0 25 100 1680,4416,1777,1779,1618,0648,0472,1000 172,370,769,10000,3333,19607,39215,1000 0 0 0 3968 200 5 1 1000 0 0 1 0 10 180 0 10000 0 28 42 5 20 32 32 10 188 2 0 2000 200 1500 250 2 20 10 10 20 10 10 20 0 0 0 55 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1,1,1 Далее находим список выпадающих предметов и список шансов выпадения этих предметов. Предметы (8): 1680,4416,1777,1779,1618,0648,0472,1000 Соответствующие шансы выпадения предметов: 172,370,769,10000,3333,19607,39215,1000 По формуле определения вероятности выпадения предметов определим вероятность выпадения каждого предмета: Свиной хвостик (ID 1680), шанс 172: % = 100 / 172 * 1 = 0.58 (58%) Мягкая свиная шерстка (ID 4416), шанс 370: % = 100 / 370 * 1 = 0.27 (27%) Стакан (ID 1777), шанс 769: % = 100 / 769 * 1 = 0.13 (13%) Свиной хвостик (ID 1779), шанс 10000: % = 100 / 10000 * 1 = 0.01 (1%) Флакон (ID 1618), шанс 3333: % = 100 / 3333 * 1 = 0.03 (3%) Сапоги из мягкой кожи (ID 0648), шанс 19607: % = 100 / 19607 * 1 = 0.0051 (0.51%) Перчатки из мягкой кожи (ID 0472), шанс 39215: % = 100 / 39215 * 1 = 0,0025 (0.25%) Чертеж Пачкули (ID 1000), шанс 1000: % = 100 / 1000 * 1 = 0.1 (10%) Полученные значения совпали с теми, что мы видим на скриншоте из базы данных монстров. В качестве второго примера создадим список предметов для нашего нового монстра. Пусть будет 3 предмета и при рейте на выпадение предметов x5 (MF_RAID = 5) нам нужны следующие вероятности выпадения: Нержавеющая сталь (ID1: 3370) - 30% (или 0.3) Коробочка феи (ID2: 262) - 50% (или 0.5) Покровительство богини (ID3: 938) - 33.3% (или 0.333) Сначала составляем список предметов через запятую: 3370,262,938 Затем рассчитываем шансы выпадения: Нержавеющая сталь (ID1: 3370) - 30% ШАНС1 = 100 / 0.3 * 5 = 1666 Коробочка феи (ID2: 262) - 50% ШАНС2 = 100 / 0.5 * 5 = 1000 Покровительство богини (ID3: 938) - 33.3% ШАНС3 = 100 / 0.333 * 5 = 1502 Далее составляем список шансов выпадения: 1666,1000,1502 Спасибо за внимание!
  12. [FYI] Разному времени суток - разный дроп Автор: @insider Итак, добавляем в дроп лист кальмарчика некий предмет. Шанс выпадения предмета ставим 0. Далее, идем в skilleffect.lua, ищем функцию Check_Baoliao и следующий кусок в ней: if arg[i] >= 100 then mf = math.min ( 1, 100 / arg[i] * bsmf ) * MF_RAID * MF_RAID_STATE a = Percentage_Random(mf) if a == 1 then count = count + 1 item[count] = i end end И меняем на что-то вроде: if arg[i] >= 100 then mf = math.min ( 1, 100 / arg[i] * bsmf ) * MF_RAID * MF_RAID_STATE a = Percentage_Random(mf) if a == 1 then count = count + 1 item[count] = i end elseif arg[i] == 0 if время >= 7 часов and время <= 8 часов and id_моба == кальмарчик mf = math.min ( 1, 100 / настоящий_%_дропа * bsmf ) * MF_RAID * MF_RAID_STATE a = Percentage_Random(mf) if a == 1 then count = count + 1 item[count] = i end end end В итоге кальмарчик каждый день с 7 до 8ми будет дропать некий предмет с заданным шансом (шанс рассчитывается как для таблицы), а в остальное время - не будет. Сюда можно добавить проверку не только на время, но и на наличие у игрока определенного предмета или части экипировки, его уровень, принадлежность к определенной гильдии, да и вообще на что только фантазии хватит. Вопросы приветствуются. Enjoy! © insider
×
×
  • Create New...