Garry's Mod

Garry's Mod

30 ratings
Полный курс лекций (туториалов) по GLua. Лекция 4
By Stanislav Volkovich
На протяжении около одного года я занимался реализацией проекта гейммода для GMod, однако в связи с техническими проблемами обработки 3D моделей движком Source (я использовал неоригинальные для Source модели, а конвертированные из Gamebryo), он не был завершён. Когда я начинал эту деятельность, я не был осведомлён ни о том, как Source обрабатывает 3D модели, ни о том, что такое движок или гейммод вообще. В связи с тем что разработка гейммода закончилась ничем, я бы хотел поделиться полученными мной в процессе разработки знаниями с теми, кто был бы заинтересован в создании аддонов и гейммодов.

Данный курс позволит с нуля освоить программирование на Lua в целом и на GLua в частности, теорию функционирования Source и Garry’s Mod, что позволит создавать любые аддоны и гейммоды в рамках возможностей движка по желанию прошедшего курс.

Курс будет состоять из 9 лекций:
I - базовый уровень:
1 - переменные, операторы, функции - https://steamcommunity.com/sharedfiles/filedetails/?id=2236011000.
2 - условия, циклы, таймеры - https://steamcommunity.com/sharedfiles/filedetails/?id=2242814101.
3 - классы, объекты - https://steamcommunity.com/sharedfiles/filedetails/?id=2249794255.
4 - контекст, сеть - здесь.
II - стандартный уровень:
5 - основные классы, представляющие объекты в мире, Source и Garry’s Mod, цели и методы взаимодействия с ними - https://steamcommunity.com/sharedfiles/filedetails/?id=2278730936&preview=true;
6 - основные классы Garry’s Mod, представляющие объекты на экране (интерфейс).
7 - создание класса Garry’s Mod с помощью Entity.
8 - создание класса Lua с помощью MetaTable.
III - продвинутый уровень:
9 - создание аддона на примере аддона для смены дня и ночи.
10 - создание гейммода на примере гейммода с противостоянием двух команд.

Лекции будут публиковать по мере написания не реже одного раза в неделю за исключением лекций 8 и 9. Каждая лекция состоит из общей теории, теории применения в Lua и GLua, примеров, заданий. Вы можете задать в комментариях любые вопросы относительно контента лекции.

То, что я сделал с помощью GLua, вы можете увидеть по ссылке: https://www.youtube.com/watch?v=NxVDBptT2to.

Обратная связь. Пожалуйста, не оскорбляйте меня или любых других пользователей в комментариях в руководству. Пожалуйста, не пишите мне с предложениями сходить на прогулку, поиграть в ГМод, купить гараж. Если я захочу сделать что-то из вышеперечисленного, я обращусь в соответствующие инстанции, а не буду писать руководства по программированию для ГМода. Я буду рад ответить на ваши вопросы по программированию в комментариях и личных сообщениях и обсудить что бы то ни было, связанное с этой темой, но не более того.
   
Award
Favorite
Favorited
Unfavorite
Часть 1. Контекст
Контекст - это определённое место выполнения кода: сервер, клиент или и сервер, или клиент. Если вы пользовались или просматривали энциклопедию GLua (https://wiki.facepunch.com/gmod/), то могли заметить, что функции в поиске имеют в начале квадратик, который может быть синим, оранжевым или наполовину синим и наполовину оранжевым. То же самое вы могли заметить при печати сообщений в консоль: одни сообщения синие, другие - жёлтые, и иногда они дублируют друг друга. Синим обозначены функции и сообщения, предназначенные для вызова или являющиеся результатов вызова на сервере. Жёлтым - на клиенте. Таким образом, каждая функция требует определённый контекст - серверный или клиентский для своего выполнения.

В 1 лекции я говорил, что для запуска учебных файлов можно использовать lua_openscript FileName.lua или lua_openscript_cl FileName.lua. Разница между этими командами заключается в том, что первая открывает файл в контексте сервера, а вторая - в контексте клиента. Все предлагаемые ранее задачи имели общий контекст. Файлы, находящиеся в папке autorun запускаются и на сервере, и на клиенте. В аддонах и гейммодах рекомендуется жёстко разделять серверный и клиентский контекст, создавая одни файлы для сервера, а другие - для клиента. Обычно, файлы, созданные для сервера, имеют название "sv_FileName.lua", для клиента - "cl_FileName.lua", общие - "sh_FileName.lua". Однако, это необходимо только для удобства программиста, и на выполнение файлов в определённом контексте не влияет. Для того, чтобы запустить файл в определённом контексте, нам нужен в первую очередь файл общего контекста, который будет запущен автоматически. В аддоне это обычно будет файл "AddonName_autorun.lua", а в гейммоде "GamemodeName_shared.lua". Из этого файлы мы должны запустить все другие файлы, которые нужны для выполнения нашего аддона или гейммода. Однако, необходимо уточнить, что мы должны не только запустить файлы, но и отправить их клиенту, если это файлы, предназначенные для запуска на клиенте.

Таблица 1. Запуск файла:
Код
Описание
include( "FileName.lua" )
include - имя вызываемой функции, FileName.lua - аргумент, содержащий имя файла в формате String и сообщающий функции, какой файл необходимо запустить.

Таблица 2. Отправка файла:
Код
Описание
AddCSLuaFile( "FileName.lua" )
AddCSLuaFile - имя вызываемой функции, FileName.lua - аргумент, содержащий имя файла в формате String и сообщающий функции, какой файл необходимо отправить.

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

Таблица 3. Определение серверного контекста в общем файле:
Код
Описание
if (SERVER) then -- Server Context in Shared File. end
if - постановка условия; (SERVER) - определение контекста, вернёт true на сервере и false на клиенте, и поэтому условие будет выполняться только на сервере; then -- Server Context in Shared File. - место для кода серверного контекста; end - конец условия, возвращение к общему контексту.
Изображение 1. Определение серверного контекста в общем файле:

Таблица 4. Определение клиентского контекста в общем файле:
Код
Описание
if (CLIENT) then -- Client Context in Shared File. end
if - постановка условия; (CLIENT) - определение контекста, вернёт false на сервере и true на клиенте, и поэтому условие будет выполняться только на клиенте; then -- Client Context in Shared File. - место для кода клиентского контекста; end - конец условия, возвращение к общему контексту.
Изображение 2. Определение клиентского контекста в общем файле:

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

Изображение 3. Запуск файлов сервера и клиента из общего файла:
Резюме. Игра происходит на сервере. Клиент только отображает пользователю ситуацию, которую определяет сервер. Поэтому функции разделены на серверные и клиентские. Выполнение скрипта начинается с общего файла. В этом файле с помощью разделения контекста через условие if и глобальные переменную SERVER или CLIENT в формате Bool указывается, какие серверные и клиентские файлы необходимо запустить с помощью функции include. Клиентские файлы необходимо отправлять с сервера клиенту с помощью функции AddCSLuaFile.
Часть 2. Сеть. net Library.
Сеть - это процесс обмена информацией между клиентом и сервером. Информация содержится в переменных. Переменные могут быть сетевыми и несетевыми.

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

Несетевые переменные - это переменные, которые или существуют только на сервере, или только на клиенте, или и там, и там, но независимо друг от друга. Во всех предыдущих частях мы создали только несетевые переменные. По умолчанию все глобальные и локальные переменные являются несетевыми. Однако, они могут стать таковыми при реализации определённого скрипта.

Существует два способа обмена информацией между клиентом: с помощью библиотеки net и с помощью установки сетевой переменной для UserData.

Net использует Network Strings, сообщения с определёнными названиями, по которым их определяют сервер и клиент.

Таблица 1. Создание Network String (серверный контекст).
Код
Описание
util.AddNetworkString( "NetworkStringName" )
util - имя библиотеки, где находится нужная функция; AddNetworkString - функция, создающая новую сетевую строку; NetworkStringName - аргумент в формате String, сообщающий функции имя новое сетевой строки.

После того, как Network String создана на сервере, её можно использовать на сервере и на клиенте. Обратите внимание, что сервер отправляет сообщение определённому клиенту, а клиент - общему для всех клиентов серверу. Один клиент не может отправить сообщение другому клиенту.

Таблица 2. Отправка Network String.
Код (серверный контекст)
Описание
net.Start( "NetworkStringName" )
net - имя библиотеки, где находится нужная функция; Start - функция запуска сетевого сообщения; NetworkStringName - аргумент в формате String, сообщающий функции имя сетевой строки, которое будет использовано для чтения сетевого сообщения на клиенте.
net.Send( Player )
ИЛИ
net.Broadcast( )
net - имя библиотеки, где находится нужная функция; Send - функция отправки сетевого сообщения определённому клиенту; Player - аргумент в формате UserData (Player), сообщающий функции ссылку на объект получателя; Broadcast - функция отправки сетевого сообщения всем клиентам.
Код (клиентский контекст)
Описание
net.Start( "NetworkStringName" )
Аналогично серверному контексту.
net.SendToServer( )
net - имя библиотеки, где находится нужная функция; SenfToServer - функция отправки сетевого сообщения на сервер, единый для всех игроков.

Мы отправили сообщение, но в нём нет никакой информации для клиента. Для того, чтобы добавить информацию, необходимо воспользоваться другими функциями из библиотеки net, начинающиеся с Write, где после Write идёт тип переменной, вставив их между Start и Send.

Таблица 3. Заполнение Network String (общий контекст).
Код
Описание
net.WriteString( "String" )
net - имя библиотеки, где находится нужная функция; WriteString - функция записи переменной в формате String в сетевое сообщение; String - аргумент в формате String, сообщающий функции, что нужно записать.

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

Таблица 4. Получение Network String (общий контекст).
Код
Описание
net.Receive( "NetworkStringName", function( Number_Length, Player_Sender ) -- Operations. end)
net - имя библиотеки, где находится нужная функция; Receive - функция получения сообщения; NetworkStringName - аргумент в формате String, сообщающий функции имя сетевой строки; function - аргумент в формате функции; Number_Length - содержащийся в функции аргумент длины сообщения; Player_Sender - содержащаяся в функции ссылка на объект в формате UserData (Player), доступная только на сервере, так как отправителем при получении сообщения на клиенте является сам сервер; -- Operation. - место, где необходимо указать операции, выполняющиеся при получении сообщения.

По аналогии с net.Write, при получении сообщения необходимо на месте -- Operations. использовать net.Read с указанием типа переменной, чтобы получить отправленную с сервера информацию. Это необходимо делать в том же порядке, в каком информация с сервера была отправлена.

Таблица 5. Чтение Network String (общий контекст).
Код
Описание
local String = net.ReadString( )
local String - объявление новой локальной переменной, куда будет записана полученная в сообщении информация; net - имя библиотеки, где находится нужная функция; ReadString - функция чтения переменной в формате String из сетевого сообщения.
Часть 3. Сеть. Networked Variables
Теперь, когда мы знаем, как обмениваться информацией между сервером и клиентом с помощью библиотеку net, рассмотрим второй способ - с помощью установки сетевой переменной в Entity.

Таблица 6. Взаимодействие с сетевой переменной.
Код
Описание
Entity:SetNWString( "StringName", "String" )
Entity - Entity, которому будет принадлежать новая сетевая переменная; SetNWString - функция установки сетевой переменной типа String, по аналогии с net могут использоваться другим функции, начинающиеся с SetNW, для других типов; "StringName" - аргумент в формате String, сообщающий функции имя переменной, по которому переменная будет изменяться и получаться; "String" - аргумент в формате String, сообщающий функции значение переменной.
local String = Entity:GetNWString( "StringName" )
local String - объявление новой локальной переменной, куда будет сохранено значение, полученной из сетевой переменной; Entity - Entity, которому принадлежит сетевая переменная; GetNWString - функция получения сетевой переменной типа String, по аналогии с net могут использоваться другие функции, начинающиеся с GetNW, для других типов; "StringName" - аргумент в формате String, сообщающий функции имя переменной, по которому переменная будет изменяться и получаться.[/table]
Часть 4. Сеть. Резюме
Резюме. Существует два способа обмена информацией между сервером и клиентом: с помощью библиотеки net и с помощью сетевых переменных в Entity. Библиотека net позволяет создавать именные сетевые сообщения, записывать в них информацию, отправлять, получать и читать, выполняя определённые функции при получении. Сервер отправляет информацию определённому клиенту или всем клиентам. Клиент отправляет информацию только серверу. Отправлять информацию друг другу клиенты не могут. Entity могут иметь сетевые, то есть постоянно синхронизирующиеся между сервером и клиентом с приоритетом сервера, переменные. Функции типа SetNW и GetNW позволяют устанавливать и получать их. Библиотека net необходима или в случае, если у нас нет Entity, или когда при получении сообщения, сервер или клиент должны совершить определённое действие. Сетевая переменная - когда необходимо постоянно и очень быстро обновлять определённую переменную, или совершение никаких действий при её обновлении не требуется.
Дополнительная литература
  1. Библиотека "net":
  2. Сетевые переменные на примере типа String:
  3. Материал для самостоятельного изучения по дополнительной теме "Сетевые энтити"*:

* - я не стал выносить эту тему в части лекции, потому что сам никогда не использовал предложенные в ней функции, ввиду серьёзных ограничений в виде неприменимости к определённым на C++ классам энтити, только к определённым на Lua, а также маленького объёма сетевых данных. Однако, вы можете самостоятельно ознакомиться с этой темой, если не считаете эти ограничения значимыми в вашей работе и если владеете английским языком.
Задание 4
Задача. Скачайте файл аддона "task4" в формате .rar, разархивируйте его и положить в папку addons. Используя файл task4_autorun, создайте в папке lua файлы для выполнения серверного, общего и клиентского кода и подключите их в файле task4_autorun.

Там, где написано "-- Your code here." в файле task4_autorun напишите код, отправляющий с сервера игроку сообщение со строкой и печатающий её на клиенте при получении. Затем добавьте туда отправку обратного сообщения с другой строкой и печатью её на сервере при получении. Если всё сделано правильно, у вас получится прекрасная база для написания любого кода, требующего взаимодействия сервера и клиента.

После этого добавьте в отправку сообщения установку переменной типа Bool в получателя в значение true, а затем её печать в консоль при получении сообщения на клиенте и установку в значение false при получении обратного сообщения на сервере. Если всё сделано правильно, то у игрока будет новая переменная в формате Bool с единым значение на сервере и на клиенте.

Для того, чтобы отправить сообщение с сервера на клиент, необходимо нажать кнопку Backspace. Вы также можете попробовать печатать в консоль Number_Button, нажимая на разные кнопки, чтобы понять, какое число соответствует им. Число 66 соответствует Backspace.

Решение. Решение будет опубликовано в формате файла вместе с выходом следующей лекции курса.

Ссылка на скачивание файла:
13 Comments
Stanislav Volkovich  [author] Nov 6, 2020 @ 7:11am 
Stanislav Volkovich  [author] Nov 6, 2020 @ 5:08am 
В файле, которые я приложил к задаче, содержалась ошибка, из-за чего верное выполнение задания было невозможно, если только вы сами не знаете, как её исправить. Вместе с 5 лекцией будет выложено решение, а этот файл будет обновлён в ближайшее время.
sSpy Nov 4, 2020 @ 3:32pm 
Однако, это всего лишь моё мнение касаемо ситуации, прислушаться к нему или же нет, это безусловно Ваше право :)
sSpy Nov 4, 2020 @ 3:32pm 
Augustus Lupfil Не думаю, что стоит привязываться к цифрам сейчас, людей желающих разобраться в теме не так много по сравнению с теми, кто хочет развлекаться здесь и сейчас.
Я считаю, что более правильным решением было бы довести серию руководств до конца и по итогу сформировать тот круг людей, кто будет действительно в этом заинтересован, сейчас рано ещё делать какие-либо выводы.
Stanislav Volkovich  [author] Nov 1, 2020 @ 7:39am 
Solovey Empty, меня удивляет, почему у всех предыдущих гайдов рейтинг намного выше. То есть все прочли 3 и решили больше не читать? Странно!
Solovei Empty Nov 1, 2020 @ 6:50am 
хотя работа на высоте!
Solovei Empty Nov 1, 2020 @ 6:49am 
Ни чего удивительного что так мало оценок. Сеть очень трудна в понимании для обывателей гмод и даже ее вид пугает! Можно попробовать добавить к примеру видео урок всего того что в гайде есть, что бы люди не пугались большого обьема информации
Stanislav Volkovich  [author] Oct 28, 2020 @ 7:07am 
Популярность данной части по сравнению со всеми предыдущими как-то очень сильно упала. Следующая часть выйдет при условии, что эта часть наберёт достаточно оценок для выставления звёздного рейтинга Стим.
Stanislav Volkovich  [author] Oct 28, 2020 @ 7:06am 
bipedal, благодарю, я исправил.
analysis Oct 24, 2020 @ 9:59am 
SendToServer, не SenFToServer