-------------------------------------------------------------------------------- -- Add all required clientside files -------------------------------------------------------------------------------- local function resource_AddDir(dir) local files,dirs = file.Find(dir.."/*","GAME") for _, fdir in pairs(dirs) do resource_AddDir(dir.."/"..fdir) end for _,v in pairs(files) do resource.AddFile(dir.."/"..v) end end if SERVER then util.AddNetworkString("metrostroi-mouse-move") util.AddNetworkString("metrostroi-cabin-button") util.AddNetworkString("metrostroi-cabin-reset") util.AddNetworkString("metrostroi-panel-touch") util.PrecacheModel("models/metrostroi_train/reversor/reversor_classic.mdl") util.PrecacheModel("models/metrostroi_train/reversor/reversor_gold.mdl") --[[resource_AddDir("materials/metrostroi/props") resource_AddDir("materials/models/metrostroi_signs") resource_AddDir("materials/models/metrostroi_train") resource_AddDir("materials/models/metrostroi_passengers") resource_AddDir("materials/models/metrostroi_signals") resource_AddDir("models/metrostroi/signs") resource_AddDir("models/metrostroi/81-717") resource_AddDir("models/metrostroi/e") resource_AddDir("models/metrostroi/81") resource_AddDir("models/metrostroi/81-703") resource_AddDir("models/metrostroi/81-508") resource_AddDir("models/metrostroi/metro") resource_AddDir("models/metrostroi/passengers") resource_AddDir("models/metrostroi/signals") resource_AddDir("models/metrostroi/tatra_t3") resource_AddDir("sound/subway_trains") resource_AddDir("sound/subway_announcer") resource_AddDir("sound/subway_stations_test1") resource_AddDir("sound/subway_trains/new")]] end ------ -------------------------------------------------------------------------- -- Create metrostroi global library -------------------------------------------------------------------------------- if not Metrostroi then -- Global library Metrostroi = {} -- Supported train classes Metrostroi.TrainClasses = {} Metrostroi.IsTrainClass = {} -- Supported train classes Metrostroi.TrainSpawnerClasses = {} timer.Simple(0.05, function() for name in pairs(scripted_ents.GetList()) do local prefix = "gmod_subway_" if string.sub(name,1,#prefix) == prefix and scripted_ents.Get(name).Base == "gmod_subway_base" then table.insert(Metrostroi.TrainClasses,name) Metrostroi.IsTrainClass[name] = true end end end) -- List of all systems Metrostroi.Systems = {} Metrostroi.BaseSystems = {} end --List of spawned trains Metrostroi.SpawnedTrains = {} for k,ent in pairs(ents.GetAll()) do if ent.Base == "gmod_subway_base" or ent:GetClass() == "gmod_subway_base" then Metrostroi.SpawnedTrains[ent] = true end end hook.Add("EntityRemoved","MetrostroiTrains",function(ent) if Metrostroi.SpawnedTrains[ent] then Metrostroi.SpawnedTrains[ent] = nil end end) if SERVER then hook.Add("OnEntityCreated","MetrostroiTrains",function(ent) if ent.Base == "gmod_subway_base" or ent:GetClass() == "gmod_subway_base" then Metrostroi.SpawnedTrains[ent] = true end end) else hook.Add("OnEntityCreated","MetrostroiTrains",function(ent) if ent:GetClass() == "gmod_subway_base" or scripted_ents.IsBasedOn(ent:GetClass(), "gmod_subway_base") then Metrostroi.SpawnedTrains[ent] = true end end) end ------------------------ -- Metrostroi version -- ------------------------ Metrostroi.Version = 1623941696 Metrostroi.Loaded = false -------------------------------------------------------------------------------- -- Add skins function -- category - a skin category(pass, cab, train) -- name - name of skin(must be unique) or skin table(table must have a name) -- tbl - skin table -- Skin table: -- { -- typ = "81-717_lvz", (it's a gmod_subway_*(gmod_subway_81-717_lvz)) -- name = "NAME",(or you can send name to function) -- textures = { -- texture_name = "path_to_texture", -- b01a = "myskin/mycoolskin", -- } -- } -- List of trains and manufacturers: -- 81-717_mvm -- 81-717_lvz -- Ezh3 -- Em -- E -------------------------------------------------------------------------------- function Metrostroi.AddSkin(category,name,tbl) if type(name) == "table" then local Table = name name = Table.name Table.name = nil tbl = Table end if CLIENT and tbl.textures then local find for k,v in pairs(tbl.textures) do if not file.Exists("materials/"..v..".vmt","GAME") then find = true ErrorNoHalt(Format("Metrostroi: %s texture: %s, not found. Check folder and addons!\n",category,v)) end end if find then return end end if not Metrostroi.Skins[category] then print(Format("Metrostroi: Added a %s skin category",category)) Metrostroi.Skins[category] = {} end if not tbl.typ then ErrorNoHalt(Format("Metrostroi:Skin error: %s wont have a typ direvtive!\n",tbl.name or name)) return end Metrostroi.Skins[category][name] = tbl Metrostroi.Skins.GetTable = function(id,name,tbl,typ) local SkinsType = ENT and ENT.SkinsType return {id,name,"List",function() if SkinsType then tbl = {} for k,v in pairs(Metrostroi.Skins[typ]) do if v.typ == SkinsType and not hook.Run("MetrostroiSkinsCheck",SkinsType,typ,v.name or k,k) then tbl[k] = v.name or k end end return tbl end end,nil,function(ent,val) if not Metrostroi.Skins or not Metrostroi.Skins[typ] then return end local texture = Metrostroi.Skins[typ][val] if not texture or hook.Run("MetrostroiSkinsCheck",texture.typ,typ,texture.name or val,val) then return end ent:SetNW2String(id,val) --[[ if texture.textures then ent:SetNW2String(id,texture.func and texture.func(ent) or val) elseif texture.func then ent:SetNW2String(id,texture.func(ent)) end--]] end,function(List,VGUI) if not Metrostroi.Skins or not Metrostroi.Skins[typ] then return end local texture = Metrostroi.Skins[typ][List:GetOptionData(List:GetSelectedID())] if not texture or not texture.defaults then return end for k,v in pairs(texture.defaults) do local id = VGUI[k].ID print(List:GetOptionData(List:GetSelectedID()),id,VGUI[id],v) if id and VGUI[id] then VGUI[id](v,true) end end end} end end -------------------------------------------------------------------------------- -- Function for announcer adding -------------------------------------------------------------------------------- Metrostroi.AnnouncementsASNP = {} Metrostroi.ASNPSetup = {} function Metrostroi.AddANSPAnnouncer(name,soundtable,datatable) if not soundtable or not datatable then return end for k,v in pairs(Metrostroi.AnnouncementsASNP) do if v.name == name then Metrostroi.AnnouncementsASNP[k] = soundtable Metrostroi.AnnouncementsASNP[k].name = name Metrostroi.ASNPSetup[k] = datatable Metrostroi.ASNPSetup[k].name = name print("Metrostroi: Changed \""..name.."\" ASNP announcer.") return end end local id = table.insert(Metrostroi.AnnouncementsASNP,soundtable) Metrostroi.AnnouncementsASNP[id].name = name local id = table.insert(Metrostroi.ASNPSetup,datatable) Metrostroi.ASNPSetup[id].name = name print("Metrostroi: Added \""..name.."\" ASNP announcer.") end Metrostroi.AnnouncementsSarmatUPO = {} Metrostroi.SarmatUPOSetup = {} function Metrostroi.AddSarmatUPOAnnouncer(name,soundtable,datatable) if not soundtable or not datatable then return end for k,v in pairs(Metrostroi.AnnouncementsSarmatUPO) do if v.name == name then Metrostroi.AnnouncementsSarmatUPO[k] = soundtable Metrostroi.AnnouncementsSarmatUPO[k].name = name Metrostroi.SarmatUPOSetup[k] = datatable Metrostroi.SarmatUPOSetup[k].name = name print("Metrostroi: Changed \""..name.."\" Sarmat UPO(UPO2) announcer.") return end end local id = table.insert(Metrostroi.AnnouncementsSarmatUPO,soundtable) Metrostroi.AnnouncementsSarmatUPO[id].name = name local id = table.insert(Metrostroi.SarmatUPOSetup,datatable) Metrostroi.SarmatUPOSetup[id].name = name print("Metrostroi: Added \""..name.."\" Sarmat UPO(UPO2) announcer.") end Metrostroi.AnnouncementsUPO = {} Metrostroi.UPOSetup = {} function Metrostroi.SetUPOAnnouncer(soundtable,datatable) if not soundtable or not datatable then return end Metrostroi.AnnouncementsUPO = {soundtable} Metrostroi.UPOSetup = datatable --Generating noise for k,v in pairs(Metrostroi.UPOSetup or {}) do if v.noises and v.noiserandom < math.random() then v.noise = table.Random(v.noises) end end print("Metrostroi: Set UPO announcer and generated nosies.") end Metrostroi.AnnouncementsRRI = {} Metrostroi.RRISetup = {} function Metrostroi.SetRRIAnnouncer(soundtable,datatable) if not soundtable or not datatable then return end Metrostroi.AnnouncementsRRI = {soundtable} Metrostroi.RRISetup = datatable print("Metrostroi: Set RRI announcer.") end function Metrostroi.AddPassSchemeTex(id,name,schTbl) id = id.."_schemes" if not Metrostroi.Skins[id] then Metrostroi.Skins[id] = {} end local tbl = Metrostroi.Skins[id] for k,v in ipairs(tbl) do if name == v.name then tbl[k] = schTbl tbl[k].name = name return end end tbl[table.insert(tbl,schTbl)].name = name end local defaults = { ["702_routes"] = { "models/metrostroi_schemes/destination_table_black/label_cross", "models/metrostroi_schemes/destination_table_black/label_dev", "models/metrostroi_schemes/destination_table_black/label_freight", }, ["710_routes"] = { "models/metrostroi_train/81-710/route_empty", "models/metrostroi_schemes/destination_table_white/label_cross", "models/metrostroi_schemes/destination_table_white/label_dev", "models/metrostroi_schemes/destination_table_white/label_freight", }, ["717_routes"] = { "models/metrostroi_schemes/destination_table_white/label_cross", "models/metrostroi_schemes/destination_table_white/label_dev", "models/metrostroi_schemes/destination_table_white/label_freight", }, ["720_routes"] = { "models/metrostroi_train/81-720/labels/label_empty", "models/metrostroi_schemes/destination_table_white/label_cross", "models/metrostroi_schemes/destination_table_white/label_freight", } } function Metrostroi.AddLastStationTex(id,stIndex,texture) id = id.."_routes" if not Metrostroi.Skins[id] then Metrostroi.Skins[id] = {} if defaults[id] then if type(defaults[id]) == "table" then Metrostroi.Skins[id].default = defaults[id][1] for i=2,#defaults[id] do Metrostroi.AddLastStationTex(id:sub(1,-8),1000+(i-1),defaults[id][i]) end else Metrostroi.Skins[id].default = defaults[id] end end end local tbl = Metrostroi.Skins[id] for k,v in pairs(tbl) do if k == index then tbl[v] = texture return end end tbl[stIndex] = table.insert(tbl,texture) end -------------------------------------------------------------------------------- -- Load core files and skins -------------------------------------------------------------------------------- if SERVER then local OSes = { Windows = "win32", Linux = "linux", BSD = "linux", POSIX = "linux", OSX = "osx", } DISABLE_TURBOSTROI = false if not DISABLE_TURBOSTROI then print(Format("Metrostroi: Trying to load simulation acceleration DLL for %s %s...",jit.os,jit.arch)) --TODO: OS specific check if jit.arch == "x86" and OSes[jit.os] and file.Exists(Format("lua/bin/gmsv_turbostroi_%s.dll",OSes[jit.os]), "GAME") then if not pcall(require,"turbostroi") then if system.IsWindows() then ErrorNoHalt("======================================================\nMetrostroi: Turbostroi library can't be loaded because of missing libraries!\nCheck, that you have Microsoft visual c++ 2010 and 2017 redistributable(x86) installed\nYou can download it from:\n") MsgC(Color(255,0,0),"https://www.microsoft.com/en-us/download/details.aspx?id=5555\nhttps://aka.ms/vs/15/release/vc_redist.x86.exe\n") ErrorNoHalt("======================================================\n") else ErrorNoHalt("Metrostroi: Turbostroi library can't be loaded!\n") end else print("Metrostroi: Turbostroi library loaded successfuly.") end elseif jit.arch ~= "x86" then ErrorNoHalt("Metrostroi: Unsupported architecture "..jit.arch..".\nTurbostroi works only on x86(32 bit) version of server\n") elseif system.IsWindows() then ErrorNoHalt("======================================================\nMetrostroi: Turbostroi DLL not found.\nYou can found turbostroi for Windows at \n") MsgC(Color(255,0,0),"https://metrostroi.net/turbostroi\n") ErrorNoHalt("Just place this .dll to garrysmod/lua/bin folder.\nIf bin folder doesn't exists - create it.\nDon't forget to install Microsoft visual c++ 2010 and 2017 redistributable(x86)\nYou can download it from:\n") MsgC(Color(255,0,0),"https://www.microsoft.com/en-us/download/details.aspx?id=5555\nhttps://aka.ms/vs/15/release/vc_redist.x86.exe\n") ErrorNoHalt("======================================================\n") else ErrorNoHalt("Metrostroi: Turbostroi DLL not found.\n") end else Turbostroi = nil end if Turbostroi then print("Metrostroi: Simulation acceleration ENABLED!") else print("Metrostroi: Simulation acceleration DISABLED") end -- Load all lua translations local files = file.Find("metrostroi_data/languages/*.lua","LUA") for _,filename in pairs(files) do AddCSLuaFile("metrostroi_data/languages/"..filename) end -- Loadall serverside lua files files = file.Find("metrostroi/sv_*.lua","LUA") for _,filename in pairs(files) do include("metrostroi/"..filename) end -- Load all shared files serverside include("metrostroi/convars.lua") files = file.Find("metrostroi/sh_*.lua","LUA") for _,filename in pairs(files) do include("metrostroi/"..filename) end files = file.Find("metrostroi/cl_*.lua","LUA") for _,filename in pairs(files) do AddCSLuaFile("metrostroi/"..filename) end -- Add all shared files AddCSLuaFile("metrostroi/convars.lua") files = file.Find("metrostroi/sh_*.lua","LUA") for _,filename in pairs(files) do AddCSLuaFile("metrostroi/"..filename) end -- Add all system files files = file.Find("metrostroi/systems/sys_*.lua","LUA") for _,filename in pairs(files) do AddCSLuaFile("metrostroi/systems/"..filename) end -- Add skin Metrostroi.Skins = {["717_schemes"]={p={},m={}}} files = file.Find("metrostroi/skins/*.lua","LUA") for _,filename in pairs(files) do AddCSLuaFile("metrostroi/skins/"..filename) include("metrostroi/skins/"..filename) end files = file.Find("metrostroi/maps/*.lua","LUA") for _,filename in pairs(files) do AddCSLuaFile("metrostroi/maps/"..filename) include("metrostroi/maps/"..filename) end else --[[ concommand.Add( "metrostroi_reload_spawnmenu", function() if IsValid( g_SpawnMenu ) then g_SpawnMenu:Remove() g_SpawnMenu = nil end hook.Call("OnGamemodeLoaded") end)]] include("metrostroi/convars.lua") -- Load all clientside files local files = file.Find("metrostroi/cl_*.lua","LUA") for _,filename in pairs(files) do include("metrostroi/"..filename) end -- Load all shared files files = file.Find("metrostroi/sh_*.lua","LUA") for _,filename in pairs(files) do include("metrostroi/"..filename) end -- Add skins Metrostroi.Skins = {["717_schemes"]={p={},m={}}} files = file.Find("metrostroi/skins/*.lua","LUA") for _,filename in pairs(files) do include("metrostroi/skins/"..filename) end --Include map scripts files = file.Find("metrostroi/maps/*.lua","LUA") for _,filename in pairs(files) do include("metrostroi/maps/"..filename) end end Metrostroi.AddLastStationTex("710","obkatka","metrostroi_skins/81-710_names/route_obkatka") Metrostroi.AddLastStationTex("720","obkatka","metrostroi_skins/81-720_names/label_obkatka") -------------------------------------------------------------------------------- -- Load systems -------------------------------------------------------------------------------- if not Metrostroi.TurbostroiRegistered then Metrostroi.TurbostroiRegistered = {} end function Metrostroi.DefineSystem(name) if not Metrostroi.BaseSystems[name] then Metrostroi.BaseSystems[name] = {} end TRAIN_SYSTEM = Metrostroi.BaseSystems[name] TRAIN_SYSTEM_NAME = name end local function loadSystem(filename) -- Get the Lua code include(filename) -- Load train systems if TRAIN_SYSTEM then TRAIN_SYSTEM.FileName = filename end local name = TRAIN_SYSTEM_NAME or "UndefinedSystem" TRAIN_SYSTEM_NAME = nil -- Register system with turbostroi if Turbostroi and (not Metrostroi.TurbostroiRegistered[name]) then Turbostroi.RegisterSystem(name,filename) Metrostroi.TurbostroiRegistered[name] = true end -- Load up the system Metrostroi.Systems["_"..name] = TRAIN_SYSTEM Metrostroi.BaseSystems[name] = TRAIN_SYSTEM Metrostroi.Systems[name] = function(train,...) local tbl = { _base = name } local TRAIN_SYSTEM = Metrostroi.BaseSystems[tbl._base] if not TRAIN_SYSTEM then print("No system: "..tbl._base) return end for k,v in pairs(TRAIN_SYSTEM) do if type(v) == "function" then tbl[k] = function(...) if not Metrostroi.BaseSystems[tbl._base][k] then print("ERROR",k,tbl._base) end return Metrostroi.BaseSystems[tbl._base][k](...) end else tbl[k] = v end end tbl.Initialize = tbl.Initialize or function() end tbl.ClientInitialize = tbl.ClientInitialize or function() end tbl.Think = tbl.Think or function() end tbl.ClientThink = tbl.ClientThink or function() end tbl.ClientDraw = tbl.ClientDraw or function() end tbl.Inputs = tbl.Inputs or function() return {} end tbl.Outputs = tbl.Outputs or function() return {} end tbl.TriggerOutput = tbl.TriggerOutput or function() end tbl.TriggerInput = tbl.TriggerInput or function() end tbl.Train = train if SERVER then tbl:Initialize(...) else tbl:ClientInitialize(...) end tbl.OutputsList = tbl:Outputs() tbl.InputsList = tbl:Inputs() tbl.IsInput = {} for _,v in pairs(tbl.InputsList) do tbl.IsInput[v] = true end return tbl end end -- Load all systems local files = file.Find("metrostroi/systems/sys_*.lua","LUA") for _,short_filename in pairs(files) do local filename = "metrostroi/systems/"..short_filename -- Load the file if SERVER then loadSystem(filename) else timer.Simple(0.05, function() loadSystem(filename) end) end end local function loadAnn () if Metrostroi.WorkingStations then for k, v in pairs(Metrostroi.WorkingStations) do for k1, v1 in pairs(v) do Metrostroi.WorkingStations[k][v1] = k1 end end end if Metrostroi.EndStations then for k, v in pairs(Metrostroi.EndStations) do for k1, v1 in pairs(v) do Metrostroi.EndStations[k][v1] = k1 end end end end if SERVER then loadAnn() else timer.Simple(0.1, loadAnn) end if SERVER then util.AddNetworkString "MetrostroiMessages" local function CheckErr(ply) if not Turbostroi and IsValid(ply) and (ply:IsSuperAdmin() or not game.IsDedicated()) then net.Start "MetrostroiMessages" net.WriteString("Turbostroi is not installed!\nTurbostroi is accelerating train calculations by using multiple\ncores. Check "..(game.IsDedicated() and "server" or "game").." logs for more information.\nYou can download it at:\nhttps://metrostroi.net/turbostroi") net.WriteString("https://metrostroi.net/turbostroi") net.Send(ply) end if not game.IsDedicated() then net.Start "MetrostroiMessages" net.WriteString("For comfort and smooth experience, you need to\njoin to server or host a dedicated server.\nIt's required because Garry's mod using only\n1 core, and now it using it for client and server code.\nWhen you join or host server you can separate server\nand client processes to different cores.\nInformation about how to host server:\nhttp://wiki.garrysmod.com/page/Hosting_A_Dedicated_Server") net.WriteString("http://wiki.garrysmod.com/page/Hosting_A_Dedicated_Server") net.Send(ply) end end local m_adm = {} for _,v in pairs(player.GetHumans()) do if IsValid(v) and v:IsAdmin() then table.insert(m_adm,v) end end CheckErr(m_adm) hook.Add("PlayerInitialSpawn","MetrostroiWarnMessage",CheckErr) else local function err(msg, url) local count = #msg:gsub("[^\n]+","") local size = 83+count*18 local warn = vgui.Create("DFrame") warn:SetDeleteOnClose(true) warn:SetTitle("Warning") warn:SetSize(380, size) warn:SetDraggable(false) warn:SetSizable(false) warn:Center() warn:MakePopup() local wrn = warn.Paint warn.Paint = function(self, w, h) wrn(self, w, h) draw.DrawText(msg, "Trebuchet18", w/2, 30, color_white, 1) end local Close = vgui.Create("DButton", warn) Close:SetText("I understand") Close:SetPos(290, size-30) Close:SetSize(80, 25) Close.DoClick = function() warn:Close() end local Open = vgui.Create("DButton", warn) Open:SetText("Open link") Open:SetPos(15, size-30) Open:SetSize(80, 25) Open.DoClick = function() Close:SetText("Close window") gui.OpenURL(url) end local Copy = vgui.Create("DButton", warn) Copy:SetText("Copy link") Copy:SetPos(100, size-30) Copy:SetSize(80, 25) Copy.DoClick = function() Close:SetText("Close window") SetClipboardText(url) end end net.Receive("MetrostroiMessages", function() err(net.ReadString(),net.ReadString()) end) end hook.Run("MetrostroiLoaded") Metrostroi.Loaded = true if SERVER then --------------------------------------------------- -- Metrostroi reverser ID get -- --===============================================-- -- If you read this, please, don't try to spam -- -- metrostroi.net site with requests -- -- Thanks for understanding -- --===============================================-- -- Если ты читаешь это, пожалуйта, не пытайся -- -- спамить запросами на сайт metrostroi.net -- -- Спасибо за понимание -- --------------------------------------------------- --Cvars local R_Disabled = CreateConVar("metrostroi_reverser_local",0, FCVAR_ARCHIVE,"Switch reverser IDs to local mode") Metrostroi.ReverserIDs = Metrostroi.ReverserIDs or {SS = {},SG = {}} Metrostroi.ReverserID = Metrostroi.ReverserID or 0 local function claimID(ply,id,typ,isLocal) if not id then return end if typ then Metrostroi.ReverserIDs.SG[ply] = id else Metrostroi.ReverserIDs.SS[id] = not isLocal Metrostroi.ReverserIDs.SS[ply] = id end end local function isNotLocal(id) if not id then return end return Metrostroi.ReverserIDs.SS[id] end local function getLocalID(ply) if Metrostroi.ReverserIDs.SS[ply] then return Metrostroi.ReverserIDs.SS[ply] end repeat Metrostroi.ReverserID = Metrostroi.ReverserID + 1 until Metrostroi.ReverserIDs.SS[Metrostroi.ReverserID]==nil claimID(ply,Metrostroi.ReverserID,false,true) return Metrostroi.ReverserID end local function removeGolden(ply) if Metrostroi.ReverserIDs.SG[ply] then Metrostroi.ReverserIDs.SG[ply] = nil end end function Metrostroi.GetReverserID(ply,callback,typ) if not IsValid(ply) or ply:IsBot() then return end if callback == true then return Metrostroi.ReverserIDs.SG[ply] end if game.SinglePlayer() then callback(1) return end if not typ then local ID = hook.Run("MetrostroiGetReverserID",ply) if ID and type(ID)=="number" then callback(ID) return end if R_Disabled:GetBool() then callback(getLocalID(ply)) return end if isNotLocal(Metrostroi.ReverserIDs.SS[ply]) then callback(Metrostroi.ReverserIDs.SS[ply]) return end end http.Post("https://api.metrostroi.net/get_info",{[typ and "SG" or "SS"]=ply:SteamID()},function(result,_,_,statusCode) if not IsValid(ply) then return end local ID = statusCode == 200 and tonumber(result) claimID(ply,ID,typ) callback(ID or not typ and getLocalID(ply)) if typ and not ID then removeGolden(ply) end end,function() if not IsValid(ply) then return end callback(not typ and getLocalID(ply)) if typ then removeGolden(ply) end end) end --------------------------------------------------- -- Metrostroi servers monitoring -- --===============================================-- -- If you read this, please, don't try to break -- -- monitoring by sending wrong data to -- -- metrostroi.net. Monitoring not created for -- -- break it. Thanks for understanding -- --===============================================-- -- Если ты читаешь это, пожалуйта, не пытайся -- -- сломать мониторинг отправкой не достоверных -- -- данных на сайт metrostroi.net. Мониторинг -- -- создан не для того, чтобы его пытались -- -- сломать. Спасибо за понимание -- --------------------------------------------------- --Cvars local CV_Enabled = CreateConVar("metrostroi_monitoring_allow",1, FCVAR_ARCHIVE,"Enables metrostroi servers monitoring for this server. Send only basic info(map,ip,port,hostname) for metrostroi servers list") local CV_Key = CreateConVar("metrostroi_monitoring_key","", FCVAR_ARCHIVE,"Unique key for metrostroi servers monitoring. Used for server identification for servers with dynamic IP") local CV_Pass = GetConVar("sv_password") local State,LastSend,LastRec,LastSucc = 0 local LastErr local metrostroiMonitoring local function monitoringStarted() return timer.Exists("MetrostroiMonitoring") or timer.Exists("MetrostroiMonitoringPass") end local function adjustTimer(time,force) if not force and not timer.Exists("MetrostroiMonitoring") then return end timer.Create("MetrostroiMonitoring",time,0,metrostroiMonitoring) --MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,255,255),"Timer ",time,"\n") end local function destroy() hook.Remove("PlayerInitialSpawn","MetrostroiMonitoring") hook.Remove("PlayerDisconnected","MetrostroiMonitoring") timer.Remove("MetrostroiMonitoring") timer.Remove("MetrostroiMonitoringPass") --cvars.RemoveChangeCallback("metrostroi_monitoring_allow", "metrostroiMonitoring") --MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,255,255),"Destroy\n") State = 0 end local oldPass = CV_Pass:GetString() local function init() if monitoringStarted() then destroy() end if not CV_Enabled:GetBool() then return end adjustTimer(10,true) --Post, if new player joined hook.Add("PlayerInitialSpawn","MetrostroiMonitoring",function(ply) if not ply:IsBot() then metrostroiMonitoring(1,ply) end end) hook.Add("PlayerDisconnected","MetrostroiMonitoring",function(ply) if not ply:IsBot() then metrostroiMonitoring(0,ply) end end) --MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,255,255),"Init\n") State = 1 LastErr = false end local codes = { [402] = "No SourceQuery answer", [403] = "IP rejected", [404] = "Wrong parameters", } local function metrostroiMonitoringSucc(responseText, _, _, statusCode) --if not monitoringStarted() then return end if statusCode == 401 then return end LastRec = CurTime() if monitoringStarted() and State==1 then State=2 end if codes[statusCode] then RunConsoleCommand("metrostroi_monitoring_stop") LastErr = codes[statusCode] elseif statusCode == 500 then --Error in site adjustTimer(30) LastErr = "Internal server error (500). "..responseText elseif statusCode==200 then if #responseText==0 then adjustTimer(60) LastErr = false LastSucc = CurTime() else local code = responseText:sub(1,2) local response = responseText:sub(3,-1) if code == "01" then CV_Key:SetString(response) adjustTimer(30) elseif code=="02" then if LastErr ~= "Server requested update (203)" then LastErr = "Server requested update (203)" metrostroiMonitoring() end adjustTimer(30) elseif code == "03" then MsgC(Color(255,255,255),"Your code: ",Color(255,0,255),response:sub(1,6),"\n") MsgC(Color(255,255,255),"Your ServerID: ",Color(255,0,255),response:sub(7,-1),"\n") MsgC(Color(255,255,255),"Do not show this code and ServerID to third parties!\n") elseif code == "04"then MsgC(Color(255,0,0),"This server is already confirmed\n") MsgC(Color(255,0,0),"If you didn't do it, write to\n") MsgC(Color(255,0,255),"https://metrostroi.net/tickets",Color(255,0,0)," about it.\n") end LastErr = code=="02" and LastErr or false LastSucc = CurTime() end --[[ elseif 200 <= statusCode and statusCode <= 202 then if statusCode == 201 and not responseText:find("SourceQuery") then CV_Key:SetString(responseText) adjustTimer(30) elseif statusCode == 200 then adjustTimer(60) end LastSucc = CurTime() LastErr = false elseif statusCode == 203 then if LastErr ~= "Server requested update (203)" then LastErr = "Server requested update (203)" metrostroiMonitoring() end adjustTimer(30) elseif statusCode == 204 then MsgC(Color(255,255,255),"Your code: ",Color(255,0,255),responseText,"\n") MsgC(Color(255,255,255),"Do not show this code to third parties!\n") elseif statusCode == 205 then MsgC(Color(255,0,0),"This server is already confirmed\n") MsgC(Color(255,0,0),"If you didn't do it, write to\n") MsgC(Color(255,0,255),"https://metrostroi.net/tickets",Color(255,0,0)," about it.\n")]] else --Unhandled error in site adjustTimer(30) LastErr = Format("Unhandled status code (%d)!\n%s",statusCode,responseText) RunConsoleCommand("metrostroi_monitoring_status") end --[[ MsgC(Color(255,0,255),"MetrostroiMon:") MsgC(Color(255,255,255),"\n\tResponse : ",Color(255,255,0),responseText) MsgC(Color(255,255,255),"\n\tStatusCode: ",Color(255,255,0),statusCode) Msg("\n")--]] end local function metrostroiMonitoringFail(errorMessage) --MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,0,0),"Failed\n") adjustTimer(20) LastErr = "Data send error! "..errorMessage end local function post(params) --PrintTable(params) --MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,255,255),"Post\n") http.Post("https://api.metrostroi.net/server_info",params,metrostroiMonitoringSucc,metrostroiMonitoringFail) LastSend = CurTime() end local function shutdown() if not monitoringStarted() then return end local key = CV_Key:GetString("metrostroi_monitoring_key") if #key ~= 128 then key = "" end post{ status = "2", uid = key, } destroy() end metrostroiMonitoring = function(typ,ply) if not monitoringStarted() then return end if game.SinglePlayer() or not CV_Enabled:GetBool() or not Metrostroi.CurrentMap then destroy() return end local ip,port = string.match(game.GetIPAddress(),"([%d.]+):([%d]+)") local key = CV_Key:GetString("metrostroi_monitoring_key") if #key ~= 128 then key = "" end if CV_Pass:GetString("sv_password") ~= "" then --if key ~= "" then post{ --[[ ip = ip, port = port, hostname = "", map = "", maxplayers = "0", players = "0", version = "0",--]] status = "1", uid = key, } --end destroy() --Detect sv_password changes for monitoring status change timer.Create("MetrostroiMonitoringPass",1,0,function() local value = CV_Pass:GetString() if oldPass ~= value and value == "" then init() timer.Remove("MetrostroiMonitoringPass") end oldPass = value end) State = -1 elseif typ==2 then post{ uid = key, owner = ply, status = "0", } else local players if LastErr == "Server requested update (203)" then players = {} for _,v in pairs(player.GetHumans()) do if v:IsBot() then continue end table.insert(players,v:SteamID()) end end post{ ip = ip, port = port, hostname = GetHostName(), map = game.GetMap(), maxplayers = tostring(game.MaxPlayers()), players = tostring(typ==0 and player.GetCount()-1 or player.GetCount()), version = tostring(Metrostroi.Version), status = "0", uid = key, connect = ply and tostring(typ), SID = ply and ply:SteamID(), players_list = players and util.TableToJSON(players), } end end init() concommand.Add("metrostroi_monitoring_start",function(ply) if IsValid(ply) then return end if not monitoringStarted() and CV_Enabled:GetBool() then init() MsgC(Color(255,0,255),"MetrostroiMon: ",Color(0,255,0),"Started.\n") elseif CV_Enabled:GetBool() then MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,255,0),"Already started.\n") else MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,0,0),"Start blocked.", Color(255,255,0),"\n\tIf you want send monitoring data, type\n\t",Color(255,0,255),"metrostroi_monitoring_allow 1",Color(255,255,0)," to console\n") end end,nil,"Try to start monitoring") concommand.Add("metrostroi_monitoring_stop",function(ply) if IsValid(ply) then return end if monitoringStarted() then shutdown() MsgC(Color(255,0,255),"MetrostroiMon: ",Color(0,255,0),"Stopped.\n") else MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,255,0),"Already stopped.\n") end end,nil,"Try to stop monitoring") local function printTime(time) if not time then MsgC(Color(255,255,0),"N/A\n") return end local elapsed = CurTime()-time MsgC( Color(elapsed > 120 and 255 or 0,elapsed < 180 and 255 or 0,0), string.NiceTime(elapsed),"\n" ) end concommand.Add("metrostroi_monitoring_status",function(ply) if IsValid(ply) then return end if State>0 then MsgC(Color(255,0,255),"MetrostroiMon:\t\t",Color(0,255,0),"Watchdog working\n") MsgC(Color(255,255,255),"\tStatus:\t\t") if State == 1 then MsgC(Color(255,255,0),"Initialization...\n") elseif State == 2 then MsgC(Color(0,255,0),"Working") if LastErr then MsgC(Color(255,0,0)," Errored") end MsgC("\n") end MsgC(Color(255,255,255),"\tNext send:\t",string.NiceTime(timer.TimeLeft("MetrostroiMonitoring")),"\n") MsgC(Color(255,255,255),"\tLast send:\t") printTime(LastSend) MsgC(Color(255,255,255),"\tLast receive:\t") printTime(LastRec) MsgC(Color(255,255,255),"\tLast success:\t") printTime(LastSucc) if LastErr then MsgC(Color(255,0,0),"\tLast error:\t",LastErr,"\n") end elseif State == -1 then MsgC(Color(255,0,255),"MetrostroiMon:\t\t",Color(255,255,0),"Suspended by sv_password\n") if LastErr then MsgC(Color(255,0,0),"\tLast error:\t",LastErr,"\n") end elseif CV_Enabled:GetBool() then MsgC(Color(255,0,255),"MetrostroiMon:\t\t",Color(0,255,0),"Watchdog stopped\n") if LastErr then MsgC(Color(255,0,0),"\tCaused by:\t",LastErr,"\n") end else MsgC(Color(255,0,255),"MetrostroiMon: ",Color(255,0,0),"Watchdog blocked.", Color(255,255,0),"\n\tIf you want send monitoring data,enter\n\t",Color(255,0,255),"metrostroi_monitoring_allow 1",Color(255,255,0)," to console\n") if LastErr then MsgC(Color(255,0,0),"\tLast error:\t",LastErr,"\n") end end end,nil,"Monitoring status") --Restart monitoring, if we want to start it concommand.Add("metrostroi_monitoring_restart",function(ply) if IsValid(ply) then return end RunConsoleCommand("metrostroi_monitoring_stop") RunConsoleCommand("metrostroi_monitoring_start") end,nil,"Restart monitoring") concommand.Add("metrostroi_monitoring_confirm",function(ply,_,_,str) if IsValid(ply) then return end if not monitoringStarted() or State<=1 then print("Monitoring is not started. It can be disabled or have a error.") print("You can check monitoring status by metrostroi_monitoring_status console command.") elseif #str==0 then print("Usage of the command:metrostroi_monitoring_confirm SteamID") elseif not str:match("^STEAM_[0-5]:[0-2]:[%d]+$") then print("Invalid argument. First argument must be valid SteamID.") else metrostroiMonitoring(2,str) end end,nil,"Confirm validity of server and set the owner of the server.") cvars.AddChangeCallback("metrostroi_monitoring_allow", function(cvar, old, value) if not monitoringStarted() and CV_Enabled:GetBool() then RunConsoleCommand("metrostroi_monitoring_start") elseif monitoringStarted() and not CV_Enabled:GetBool() then RunConsoleCommand("metrostroi_monitoring_stop") end end, "metrostroiMonitoring") hook.Add("ShutDown","MetrostroiMonitoring",shutdown) end