Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
  • Need help?

    Create a topic in the appropriate section
    Don't write everything in the chat!
  • Take a look at the marketplace

    There you can buy
    everything related to game servers
  • Don't want a ban?

    Please read our rules
    Don't disturb the order!
  • Sell or buy?

    Use services of the guarantor
    We will make your deal safe
123new

[Enscript] Делаем скрипт для мода и основные принципы скриптинга модов

Recommended Posts

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

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

Предупреждаю, статья длинная, 'много текста'.

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

Предположим, что у нас имеется свой чистый сервер игры, и мы хотим добавить какое-то свое действие, например, после присоединения игрока на сервер игры. Например, некая своя запись в лог-файл сервера.


Темы к ознакомлению, связанные с данным материалом:

 

Небольшое отступление с пояснениями:

Как мы знаем, базовые скрипты игры находятся у нас в 'scripts.pbo' в папке 'dta'. Эти скрипты, на минуточку, на стороне клиента игры и сервера игры идентичные, т.е. вне зависимости от того, что у нас запускается, они одинаковы и там, и там, но с одной лишь разницей, с определенного момента начинается разделение в выполнении кодов. Т.е. для сервера выполняется своя часть кода, для клиента своя. Например, для сервера выполнится код из файла missionServer.c а для клиента игры из missionGameplay.c
Это я к чему - то что вы правите в скрипте для серверного мода не всегда может работать, так как эта часть кода может быть предусмотрена для клиентского мода, или наоборот. Аналогично и касается отдельных функций в коде сервера. Например, комманда GetGame().Chat("MyMessage", "colorAction"); выполняется только на клиентской стороне, а GetGame().Chat("MyMessage"); только на серверной (за исключением случаев, когда авторы игры могут ломать эти самые комманды с патчами обновлений).

Но бывают и комманды, выполняющиеся и на сервере и на клиенте игры. Например, комманда Print("MyLogMessage"); выводящая в 'script.log' нужный нам текст. Вдаваться в особенности взаимодействия между сервером и клиентом игры не буду, скажу лишь то, что работает оно через id (идентификаторы вызова) и процедуру RPC (см. события OnRPC)
Для ищущих и любопытных сразу отмечу, что можно делать одну общую функцию, разделяющую в зависимости от клиента и сервера код на нужное выполнение коммандами:

GetGame().IsMultiplayer() - будет true(истина) если игра запущена для игры по сети, или false(ложь) во всех иных случаях
GetGame().IsClient() - будет true(истина) если игра запущена клиентская часть игры (в том числе и если offline версия тоже), или false(ложь) во всех иных случаях
GetGame().IsServer() - будет true(истина) если игра запущена серверная часть игры, или false(ложь) во всех иных случаях
Не буду вдаваться и в подробности, но думаю эта информация вам пригодится, если вы всерьез решились заняться данной игрой. Добавлю только то, что игра может запускаться в 3 режимах: 1) Сервер 2) Клиент Мультиплеера 3) Клиент Offline версия, и во всех 3 случаях будут работа игры будет отличаться (к примеру, в Offline не будет PlayerIdentify, т.е. идентификатора игрока).

Так вот, вернемся к нашему моду. Игрок с английского у нас Player. Логично предположить, что и название нужных нам событий и функций будут также содержать такой текст.
Сразу отмечу, что у данной игры, как и у любой c++ подобной программы. есть подразделение по классам и подклассам. и практически все скрипты и функции игры разделены именно на них. Т.е. Если мы хотим искать Player - ищем его поиском по нахождению в папке (В Notepad++ есть крайне полезная опция, зажимаем CTRL+F и в 3 вкладке будет такой поиск).

Среди результатов поиска будет много файлов и текстов. Нас интересует файл со строкой, начинающейся на class. В нашем же случае это будет 'class PlayerBase', который вы и найдете.

Это базовый класс, содержащий в себе основные функции и события, связанные с персонажем, например DropItem - скидывает вещь, RemoveAllItems удаляет все вещи с игрока (см перевод текста в переводчике с англ.), а события EEKilled и OnConnect  выполняются на сервере игры соответственно при смерти (и убийстве) игрока, и при присоединении к серверу.

Отступление: Каждая функция имеет свой синтаксис. void - функция без возврата значения, например void OnConnect(), а бывают функции с возвратом значений, например bool IsBleeding(), возвращающая true(истина) если игрок страдает от кровотека, и false если нет.
Также у каждой функции могут быть свои входящие и используемые переменные, например у void OnConnect() их нет, а у void EEKilled( Object killer ) есть.

 

Вернемся к нашим 'интересностям'
Как вы поняли уже, нас интересует OnConnect(). Открываем найденный файл PlayerBase.c. Ищем функцию OnConnect() поиском по файлу

На патче 1.06 она выглядит вот так:

void OnConnect()
	{
		Debug.Log("Player connected:"+this.ToString(),"Connect");

		// analytics
		GetGame().GetAnalyticsServer().OnPlayerConnect( this );
		
		m_PlayerOldPos = GetPosition();
		if( m_AnalyticsTimer )
			m_AnalyticsTimer.Run( 60, this, "UpdatePlayerMeasures", null, true );
		
		//construction action data
		ResetConstructionActionData();
	}

 

Теперь, как вы сами понимаете, нам надо внести нужные нам изменения. Но как же это делать правильно, чтобы не сломать ничего в игре и модах? Объясняю ниже:

Так что же нам делать то? Ведь мы не можем перепаковывать 'scripts.pbo' сервера, иначе включенный параметр verifysignatures перестанет пускать на сервер игроков.

Отвечаю вам. Создайте свой текстовый файл, заполните его следующего вида данными:

modded class PlayerBase extends ManBase
{
	
	override void OnConnect()
	{
		super.OnConnect();
		Pring("[MyTestLog] Player connected:"+this.ToString());		
	}
}

затем сохраните его в формате вместо txt.

Как видите, вверху в файле modded class. Modded означает, что мы будем модифицировать базовый класс игры, или проще говоря писать мод на данный класс. Перед функцией OnConnect появилась override, что в переводе с англ. означает переопределение. Т.е. мы в класс PlayerBase будем переопределять функцию OnConnect.


Отступление про override:

Как некоторые любопытные заметили, в некоторых функциях уже имеются override. Не буду вдаваться в подробности данной структуры, скажу лишь то что в игре если главные базовые классы, есть мелкие наследуемые классы (для понимания можно подкатегориями их назвать). Так вот. один и тот же объект в игре может быть отображен в нескольких переменных под разными классами, но игровым объектом он останется одним и тем же. Делается это для того, чтобы получить доступ к тем или иным функциям. Для примера, в классе EntityAI (общий базовый класс всех объектов в игре) нет функции установки количества вещей SetQuantity (как например количество вещей), а у класса ItemBase такая функция есть. Вот для того чтобы выполнить нужное нам действие используют конверсию из одного класса в другой. хотя сам игровой объект от этого не изменяется никак. Пример такой конверсии в своих init.c миссии сервера можете наблюдать в таком вот виде:

itemEnt = itemTop.GetInventory().CreateInInventory("Rag");
			if ( Class.CastTo(itemBs, itemEnt ) )
				itemBs.SetQuantity(4);

Где сначала создается тряпка и записывается в класс EntityAI, а затем для совершения действий выполняется конверсия класса предмета в подкласс ItemBase с другими функциями, почле чего уже вызывается функция и используется параметр 4, означающий задать предмету количество 4 (Set Quantity в переводе установить количество).
Также, есть у любого класса в игре, как и в c++, конструкторы и деструкторы класса. Указывать их не обязательно, но они могут иметься в классах. Пример своего класса:

class My_MissionServerClassMod
{		
	void My_MissionServerClassMod()
	{
		Print("Build My_MissionServerClassMod TECT");

	}
	void ~My_MissionServerClassMod()
	{
		Write_Log("DeBuild My_MissionServerClassMod");
	}
	
}

Здесь:

void My_MissionServerClassMod() - конструктор класса, выполняется в момент, когда создается объект или переменная этого класса. Т.е. в игре каждый персонаж имеет свой объект в игровом мире. Проще говоря, каждый игровой персонаж (в т.ч. и мертвый это объект класса PlayerBase). переменной такие вещи можно задавать вот так:

ref My_MissionServerClassMod MyPeremennaya = new My_MissionServerClassMod;

void ~My_MissionServerClassMod() - деструктор класса, выполняется в момент, когда объект или переменная удаляются с памяти, например скриптово удаление объекта с карты GetGame().ObjectDelete(MyPeremennaya); или при закрытии сервера если это переменная.

Через override деструкторы и конструкторы переписывать нельзя, их в своем мод-скрипте просто объявляете, например в таком виде:

modded class My_MissionServerClassMod
{		
	void My_MissionServerClassMod()
	{
		Print("Build My_MissionServerClassMod TECT");

	}
	void ~My_MissionServerClassMod()
	{
		Write_Log("DeBuild My_MissionServerClassMod");
	}
	
}

В этом случае они сами допишутся к оригинальным вызовам родного класса игры, если такие вообще были объявлены.

Вернемся к объяснению в нашем классе PlayerBase:

Теперь посмотрите функцию OnConnect . Как видите, куча кода в ней отсутствует, но появились 2 новые строки.
Комманда
super.OnConnect(); позволит нам вызвать оригинальную функцию базового класса игры с сохранением как всех ее оригинальных действий, так и зависимостей из других связанных классов в игре (напомню, в игре один и тот же объект может быть в разных классах и разных переменных). Вы сразу спросите: 'А зачем так, можно же взять оригинальный код и переписать его как нам надо' или 'А можно ли взять и переправить базовый оригинальный код?' - отвечаю - делать так тоже можно, но крайне не желательно. У вас, как мы можем догадываться, в игре могут быть разные модификации, и данный класс может с данной функцией встретиться в другом моде. Если вы вместо такого вызова вставите весь оригинальный код функции игры, вы тем самым можете сломать запуск одного из модов, которые у вас стоят.
Вторая же строка в данном файле -
Print("[MyTestLog] Player connected:"+this.ToString());   - т.е. запись в script.log метки с текстом [MyTestLog] Player connected:{данные о персонаже} (где + this подставит вместо текста текущие данные персонажа из данного класса, а функция-модификатор ToString() преобразует эти данные в формат текстовой строки для вывода(иначе сервер выдаст вам ошибку, так как this в данном случае - это объект текущего класса, т.е. Playerbase)).
Сразу же упомяну для всех страждущих, комманда +this.ToString() не выведет данных о нашем персонаже в логе, даже не выведет никнейм или его данные. Для вывода таких сведений надо писать дополнительный код такого вида:

modded class PlayerBase extends ManBase
{
	
	override void OnConnect()
	{
		super.OnConnect();
		PlayerIdentity identity_player = this.GetIdentity();
		string nickname_player = identity_player.GetName();
		string steam64id_player = identity_player.GetPlainId();
		string bis_id_player = identity_player.GetId()();		
		Pring("[MyTestLog] Player with name " + nickname_player + "(steam64id=" + steam64id_player + " bisid=" + bis_id_player + ")connected!");		
	}
}

Собственно, этот код в готовом и сформированном вами виде вы и кладете в свой серверный мод, он у вас уже в готовом виде.

Отступление на тему:

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


Куда размещать файл в своем моде, спросите вы? Отвечаю - поглядите в каком расположении находится файлик с оригинальным классом PlayerBase (4_World\Entities\ManBase\PlayerBase.c) и разместите в своем моде его по такому же пути, название файла не сильно важно, но желательно сохранять оригинальные имена для упрощения задач самим же себе. (на самом деле сервер читает скрипты в алфавитном порядке из main-разделов игры типа 5_Mission или 4_World)

 

Любителей скриптить и писать свои моды с большим опытом работы с игрой может заинтересовать вопрос 'можно ли в моде добавлять свои переменные и функции, и вызывать их из базовых оригинальных (нативных) классов игры?' - отвечу, да, можно. Вы просто добавляете эти переменные и функции как обычные классы, например, так:

modded class PlayerBase extends ManBase
{
	string myPeremennaya = "";
	
	override void OnConnect()
	{
		super.OnConnect();
		MyFunctia();		
	}
	void MyFunctia()
	{
		Pring("[MyTestLog] Player connected:" + myPeremennaya);	
	}
}

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

 

Доп. информация:
Отмечу сразу, что указанный в теме способ подходит для  дописывания и модификации оригинальных классов скриптов сервера от разработчиков и модификации чужих модов с помощью моддинга. Изменять сам родной код функции без дописывание не рекоммендуется, но если это для вас требуется - используйте следующее:
Изменение оригинальных скриптов, для которых не предусмотрена операция
'modded class' в движке игры, а также классов и функций, которые защищены языком и движком игры от такого рода действий через серверные моды не получится. Если вы столкнулись с этим, вам проще распаковать scripts.pbo и поместить папку scripts в корень сервера игры, добавить в параметры запуска сервера параметр -FilePatching и изменять напрямую в папке необходимые вам файлы.
Также, подобные описанные выше оранжевым цветом действия будут работать, если вы будете использовать
override без использования строки-комманды super в своей функции.
Важно: не выполняйте указанную выше доп. информацию на стороне клиента игры, иначе это может стать причиной неработоспособности вашего клиента игры и входа на сервера, поскольку сервера игры поумолчанию защищены от такого рода действий и будут исключать вас с сервера на попытках входа на него!

 

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

P.S. Прошу извинить меня за излишнюю монотонность чтения и оформления темы, а также неточности, я далеко не эксперт 😉

Edited by 123new (see edit history)

Share this post


Link to post
Share on other sites









Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site, you automaticly agree to our Guidelines and Privacy Policy.
We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.