1
0
mirror of https://github.com/metrostroi-repo/MetrostroiAddon.git synced 2026-05-02 00:42:29 +00:00
Files
MetrostroiAddon/lua/autorun/metrostroi.lua
2021-08-10 00:54:54 +03:00

1025 lines
41 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.
--------------------------------------------------------------------------------
-- 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(datatable) do
if not istable(v) then continue end
for k2,stbl in pairs(v) do
if not istable(stbl) then continue end
if stbl.have_inrerchange then stbl.have_interchange = true end
end
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}
for k,v in pairs(datatable) do
if not istable(v) then continue end
for k2,stbl in pairs(v) do
if not istable(stbl) then continue end
if stbl.have_inrerchange then stbl.have_interchange = true end
end
end
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