add sborka

This commit is contained in:
2026-03-31 10:27:04 +03:00
commit f5e5f56c84
2345 changed files with 382127 additions and 0 deletions

View File

@@ -0,0 +1,274 @@
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)