Files
VnUtest/garrysmod/addons/schoolboard_addon/lua/entities/school_board/init.lua
2026-03-31 10:27:04 +03:00

275 lines
10 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")
include("shared.lua")
-- Регистрация всех сетевых сообщений
util.AddNetworkString("SchoolBoard_OpenMenu")
util.AddNetworkString("SchoolBoard_SaveLayout")
util.AddNetworkString("SchoolBoard_SyncLayout")
util.AddNetworkString("SchoolBoard_RequestLayout")
util.AddNetworkString("SchoolBoard_Clear_Req")
util.AddNetworkString("SchoolBoard_Clear_Do")
util.AddNetworkString("SchoolBoard_DrawLine")
util.AddNetworkString("SchoolBoard_SavePosition")
util.AddNetworkString("SchoolBoard_SyncPosition")
util.AddNetworkString("SchoolBoard_SaveRotation")
util.AddNetworkString("SchoolBoard_SyncRotation")
-- helper: only SAM ranks superadmin/curator (or equivalent usergroups) can modify board
local function CanModifyBoard(ply)
if not IsValid(ply) or not ply:IsPlayer() then return false end
-- standard checks
if ply:IsSuperAdmin() then return true end
if ply:GetUserGroup() == "curator" then return true end
-- if SAM is present, consult its API as a fallback
if sam and sam.IsPlayerInRank then
if sam.IsPlayerInRank(ply, "superadmin") or sam.IsPlayerInRank(ply, "curator") then
return true
end
end
return false
end
-- append log entries to a file and console
local function LogBoardChange(ply, ent, action)
local name = IsValid(ply) and ply:Nick() or "Console"
local sid = IsValid(ply) and ply:SteamID() or "N/A"
local id = IsValid(ent) and ent:EntIndex() or 0
local pos = IsValid(ent) and tostring(ent:GetPos()) or "unknown"
local msg = string.format("[%s] %s (%s) %s board #%d at %s\n", os.date(), name, sid, action, id, pos)
print(msg)
file.Append("schoolboard_changes.txt", msg)
end
-- console command for printing current log (admins only)
concommand.Add("schoolboard_printlog", function(ply, cmd, args)
if IsValid(ply) and not CanModifyBoard(ply) then
ply:ChatPrint("Нет доступа к логу доски.")
return
end
local contents = file.Read("schoolboard_changes.txt", "DATA") or ""
if IsValid(ply) then
ply:PrintMessage(HUD_PRINTCONSOLE, contents)
ply:ChatPrint("Лог выведен в консоль.")
else
print(contents)
end
end)
-- очистка лога доски (только для старшего админства)
concommand.Add("schoolboard_clearlog", function(ply, cmd, args)
if IsValid(ply) and not CanModifyBoard(ply) then
ply:ChatPrint("Нет доступа к управлению логом.")
return
end
file.Write("schoolboard_changes.txt", "")
if IsValid(ply) then
ply:ChatPrint("Лог доски очищен.")
ply:PrintMessage(HUD_PRINTCONSOLE, "Лог доски был очищен.")
else
print("schoolboard_changes.txt was cleared by console")
end
end)
function ENT:Initialize()
self:SetModel("models/props/cs_office/offcorkboarda.mdl")
self:PhysicsInit(SOLID_VPHYSICS)
self:SetMoveType(MOVETYPE_VPHYSICS)
self:SetSolid(SOLID_VPHYSICS)
self:SetUseType(SIMPLE_USE) -- Позволяет нажимать 'E' на доску
local phys = self:GetPhysicsObject()
if IsValid(phys) then
phys:Wake()
end
self.BoardLayoutStr = "[]" -- По умолчанию пустой массив JSON
self.PositionOffset = Vector(3713.2, 4595.6, -200) -- Смещение позиции доски в 3D пространстве
self.RotationOffset = Angle(0, -180, 90) -- Смещение поворота доски
end
-- Открываем меню настройки, когда игрок нажимает 'E'
function ENT:Use(activator, caller)
if IsValid(activator) and activator:IsPlayer() then
if not CanModifyBoard(activator) then
activator:ChatPrint("Только superadmin/curator может редактировать доску.")
return
end
net.Start("SchoolBoard_OpenMenu")
net.WriteEntity(self)
net.Send(activator)
end
end
-- Клиент отправляет свой новый макет доски (массив картинок и текстов)
net.Receive("SchoolBoard_SaveLayout", function(len, ply)
local ent = net.ReadEntity()
if len > 64000 then -- Ограничение размера пакета на всякий случай
return
end
local layoutSize = net.ReadUInt(32)
local compressedData = net.ReadData(layoutSize)
if IsValid(ent) and ent:GetClass() == "school_board" then
-- Разрешены только superadmin/curator (SAM ранги) или соответствующие usergroup
if not CanModifyBoard(ply) then
if IsValid(ply) then ply:ChatPrint("У вас нет прав для изменения доски.") end
return
end
-- Проверка расстояния, чтобы школьник с другого конца карты не менял доску
if ply:GetPos():DistToSqr(ent:GetPos()) > 500000 then return end
ent.BoardLayoutStr = util.Decompress(compressedData) or "[]"
-- логируем факт сохранения
LogBoardChange(ply, ent, "saved layout")
-- Рассказываем всем остальным, что доска обновилась
net.Start("SchoolBoard_SyncLayout")
net.WriteEntity(ent)
net.WriteUInt(layoutSize, 32)
net.WriteData(compressedData, layoutSize)
net.Broadcast()
end
end)
-- Когда игрок подключается или загружает сущность, он просит её данные
net.Receive("SchoolBoard_RequestLayout", function(len, ply)
local ent = net.ReadEntity()
if IsValid(ent) and ent:GetClass() == "school_board" then
if not ent.BoardLayoutStr then ent.BoardLayoutStr = "[]" end
local compressedData = util.Compress(ent.BoardLayoutStr)
if compressedData then
local layoutSize = string.len(compressedData)
net.Start("SchoolBoard_SyncLayout")
net.WriteEntity(ent)
net.WriteUInt(layoutSize, 32)
net.WriteData(compressedData, layoutSize)
net.Send(ply)
end
end
end)
-- Принимаем запрос на очистку от игрока и рассылаем всем
net.Receive("SchoolBoard_Clear_Req", function(len, ply)
local ent = net.ReadEntity()
if IsValid(ent) and ent:GetClass() == "school_board" then
if not CanModifyBoard(ply) then
if IsValid(ply) then ply:ChatPrint("У вас нет прав для изменения доски.") end
return
end
LogBoardChange(ply, ent, "cleared board")
net.Start("SchoolBoard_Clear_Do")
net.WriteEntity(ent)
net.Broadcast()
end
end)
-- Принимаем координаты линии и цвет от рисующего и рассылаем остальным
net.Receive("SchoolBoard_DrawLine", function(len, ply)
local ent = net.ReadEntity()
local x1 = net.ReadFloat()
local y1 = net.ReadFloat()
local x2 = net.ReadFloat()
local y2 = net.ReadFloat()
local r = net.ReadUInt(8)
local g = net.ReadUInt(8)
local b = net.ReadUInt(8)
if IsValid(ent) and ent:GetClass() == "school_board" then
-- ограничения на рисование маркером
if not CanModifyBoard(ply) then return end
-- Проверяем, что игрок стоит рядом с доской
if ply:GetPos():DistToSqr(ent:GetPos()) > 50000 then return end
net.Start("SchoolBoard_DrawLine", true) -- unreliable пакет
net.WriteEntity(ent)
net.WriteFloat(x1)
net.WriteFloat(y1)
net.WriteFloat(x2)
net.WriteFloat(y2)
net.WriteUInt(r, 8)
net.WriteUInt(g, 8)
net.WriteUInt(b, 8)
net.SendOmit(ply) -- Отправляем всем, кроме рисующего
end
end)
-- Сохраняем позицию доски (смещение вектора)
net.Receive("SchoolBoard_SavePosition", function(len, ply)
local ent = net.ReadEntity()
local offsetX = net.ReadFloat()
local offsetY = net.ReadFloat()
local offsetZ = net.ReadFloat()
if IsValid(ent) and ent:GetClass() == "school_board" then
if not CanModifyBoard(ply) then
if IsValid(ply) then ply:ChatPrint("У вас нет прав для изменения доски.") end
return
end
-- Проверка расстояния
if ply:GetPos():DistToSqr(ent:GetPos()) > 500000 then return end
ent.PositionOffset = Vector(offsetX, offsetY, offsetZ)
-- логируем изменение позиции
LogBoardChange(ply, ent, "changed position")
-- Синхронизируем позицию со всеми
net.Start("SchoolBoard_SyncPosition")
net.WriteEntity(ent)
net.WriteFloat(offsetX)
net.WriteFloat(offsetY)
net.WriteFloat(offsetZ)
net.Broadcast()
end
end)
-- Сохраняем поворот доски (смещение угла)
net.Receive("SchoolBoard_SaveRotation", function(len, ply)
local ent = net.ReadEntity()
local pitch = net.ReadFloat()
local yaw = net.ReadFloat()
local roll = net.ReadFloat()
if IsValid(ent) and ent:GetClass() == "school_board" then
if not CanModifyBoard(ply) then
if IsValid(ply) then ply:ChatPrint("У вас нет прав для изменения доски.") end
return
end
-- Проверка расстояния
if ply:GetPos():DistToSqr(ent:GetPos()) > 500000 then return end
ent.RotationOffset = Angle(pitch, yaw, roll)
-- логируем изменение поворота
LogBoardChange(ply, ent, "changed rotation")
-- Синхронизируем поворот со всеми
net.Start("SchoolBoard_SyncRotation")
net.WriteEntity(ent)
net.WriteFloat(pitch)
net.WriteFloat(yaw)
net.WriteFloat(roll)
net.Broadcast()
end
end)