Initial commit
This commit is contained in:
629
addons/apg/lua/apg/cl_menu.lua
Normal file
629
addons/apg/lua/apg/cl_menu.lua
Normal file
@@ -0,0 +1,629 @@
|
||||
APG_panels = APG_panels or {}
|
||||
|
||||
local pull = include("cl_utils.lua")
|
||||
local utils = pull.utils or {}
|
||||
local menu = pull.menu or {}
|
||||
|
||||
local function showNotice(notifyLevel, notifyMessage)
|
||||
if string.Trim(notifyMessage) == "" then return end
|
||||
icon = notifyLevel == 0 and NOTIFY_GENERIC or notifyLevel == 1 and NOTIFY_CLEANUP or notifyLevel == 2 and NOTIFY_ERROR
|
||||
|
||||
notification.AddLegacy(notifyMessage, icon, 3 + (notifyLevel * 3))
|
||||
|
||||
if APG.cfg[ "notifySounds" ].value then
|
||||
surface.PlaySound(notifyLevel == 1 and "buttons/button10.wav" or notifyLevel == 2 and "ambient/alarms/klaxon1.wav" or "buttons/lightswitch2.wav") -- Maybe let the player choose the sound?
|
||||
end
|
||||
|
||||
MsgC( notifyLevel == 0 and Color( 0, 255, 0 ) or Color( 255, 191, 0 ), "[APG] ", Color( 255, 255, 255 ), notifyMessage,"\n")
|
||||
end
|
||||
|
||||
net.Receive( "apg_notice_s2c", function()
|
||||
local notifyLevel = net.ReadUInt( 3 )
|
||||
local notifyMessage = net.ReadString()
|
||||
showNotice(notifyLevel, notifyMessage)
|
||||
end)
|
||||
|
||||
local function APGBuildHomePanel()
|
||||
local panel = APG_panels[ "home" ]
|
||||
panel.Paint = function( i, w, h ) end
|
||||
|
||||
local github = "https://github.com/NanoAi/gm_apg"
|
||||
|
||||
menu:initPanel( panel, 0, 40, 0, 35 )
|
||||
menu:switch( 568, 20, "Welcome to APG! ( https://git.io/fjCQK )")
|
||||
menu:switch( 568, 20, "Remember to check the github for updates! (Click to Copy)", function()
|
||||
SetClipboardText( github )
|
||||
showNotice(0, "Github URL copied to clipboard!")
|
||||
end)
|
||||
menu:switch( 568, 20, "<-- Select a Module to Configure!")
|
||||
menu:switch( 568, 20, "To see this menu again just say \"!apg\"")
|
||||
menu:switch( 568, 20, "For more help the wiki is available on the Github! (Click to Copy)", function()
|
||||
SetClipboardText( github .. "/wiki" )
|
||||
showNotice(0, "Github URL copied to clipboard!")
|
||||
end)
|
||||
menu:switch( 568, 20, "Sorry for the bad home page, this is hopefully a placeholder. : )")
|
||||
menu:panelDone()
|
||||
end
|
||||
|
||||
local function APGBuildStackPanel()
|
||||
local panel = APG_panels[ "stack_detection" ]
|
||||
panel.Paint = function( i, w, h ) end
|
||||
|
||||
menu:initPanel( panel, 0, 40, 0, 35 )
|
||||
menu:numSlider( 568, 20, "Maximum stacked ents", "stackMax", 3, 50, 0 )
|
||||
menu:numSlider( 568, 20, "Stack distance (gmod units)", "stackArea", 5, 50, 0 )
|
||||
menu:numSlider( 568, 20, "Maximum stacked fading doors", "fadingDoorStackMax", 5, 50, 0 )
|
||||
menu:switch( 568, 20, "Notify player when their fading door is removed.", "fadingDoorStackNotify" )
|
||||
menu:panelDone()
|
||||
end
|
||||
|
||||
local function APGBuildToolsPanel()
|
||||
local panel = APG_panels[ "tools" ]
|
||||
panel.Paint = function( i, w, h ) end
|
||||
|
||||
menu:initPanel( panel, 0, 40, 0, 35 )
|
||||
menu:switch( 568, 20, "Should tools be blocked on APG_CantPickup", "checkCanTool" )
|
||||
menu:switch( 568, 20, "Block players from spamming the toolgun", "blockToolSpam" )
|
||||
menu:numSlider( 568, 20, "Max click's per second(s)", "blockToolRate", 1, 15, 0 ) -- It's really hard to click more then 15 times a second.
|
||||
menu:numSlider( 568, 20, "The aforementioned second(s)", "blockToolDelay", 1, 5, 0 )
|
||||
menu:switch( 568, 20, "Prevent using the toolgun on the world", "blockToolWorld" )
|
||||
menu:switch( 568, 20, "Prevent the toolgun from unfreezing props", "blockToolUnfreeze" )
|
||||
menu:switch( 568, 20, "Block the Creator Tool? (Requires OSS)", "blockCreatorTool" )
|
||||
menu:switch( 568, 20, "Review entities near tool use", "checkTooledEnts" )
|
||||
menu:panelDone()
|
||||
end
|
||||
|
||||
local function APGBuildMiscPanel()
|
||||
local panel = APG_panels[ "misc" ]
|
||||
panel.Paint = function( i, w, h ) end
|
||||
|
||||
menu:initPanel( panel, 0, 40, 0, 35 )
|
||||
menu:switch( 568, 20, "Override Server Settings? (OSS)", "touchServerSettings" )
|
||||
menu:switch( 568, 20, "Auto freeze over time", "autoFreeze" )
|
||||
menu:numSlider( 568, 20, "Auto freeze delay(seconds)", "autoFreezeTime", 5, 600, 0 )
|
||||
menu:switch( 568, 20, "Disable vehicle damages", "vehDamage" )
|
||||
menu:switch( 568, 20, "Disable vehicle collisions (with players)", "vehNoCollide" )
|
||||
menu:numSlider(575, 20, "Physgun maxrange (how far they can reach in gmod units)", "physGunMaxRange", 128, 8192, 0)
|
||||
menu:switch( 568, 20, "Block GravGun throwing", "blockGravGunThrow" )
|
||||
menu:switch( 568, 20, "Block Physgun Reload", "blockPhysgunReload" )
|
||||
menu:switch( 568, 20, "Block players from moving contraptions", "blockContraptionMove" )
|
||||
menu:switch( 568, 20, "Inject custom hooks into Fading Doors", "fadingDoorHook" )
|
||||
menu:switch( 568, 20, "Activate FRZR9K (Sleepy Physics)", "sleepyPhys" )
|
||||
menu:switch( 568, 20, "Hook FRZR9K into collision (Experimental)", "sleepyPhysHook" )
|
||||
menu:switch( 568, 20, "Allow prop killing", "allowPK" )
|
||||
menu:switch( 568, 20, "Activate Turbo Physics (Requires OSS)", "setTurboPhysics" )
|
||||
menu:panelDone()
|
||||
end
|
||||
|
||||
local function APGBuildLagPanel()
|
||||
local panel = APG_panels[ "lag_detection" ]
|
||||
panel.Paint = function( i, w, h ) end
|
||||
|
||||
menu:initPanel( panel, 0, 40, 0, 35 )
|
||||
menu:numSlider( 568, 20, "Lag threshold (%)", "lagTrigger", 5, 200, 0 )
|
||||
menu:numSlider( 568, 20, "Frames lost", "lagsCount", 1, 20, 0 )
|
||||
menu:numSlider( 568, 20, "Heavy lag trigger (seconds)", "bigLag", 1, 5, 1 )
|
||||
menu:comboBox( 568, 20, "Lag fix function", "lagFunc", APG_lagFuncs )
|
||||
menu:numSlider( 568, 20, "Lag func. delay (seconds)", "lagFuncTime", 1, 300, 0 )
|
||||
menu:panelDone()
|
||||
end
|
||||
|
||||
local function APGBuildNotificationPanel()
|
||||
local panel = APG_panels[ "notification" ]
|
||||
panel.Paint = function( i, w, h ) end
|
||||
|
||||
menu:initPanel( panel, 0, 40, 0, 35 )
|
||||
menu:switch( 568, 20, "Notification Sounds", "notifySounds" )
|
||||
menu:comboBox( 568, 20, "Notification Level", "notifyLevel", APG_notifyLevels )
|
||||
menu:switch( 570, 20, "Do you want to show what lag function ran?", "notifyLagFunc" )
|
||||
menu:switch( 568, 20, "Developer logs (shows a notification, is spammy)", "developerDebug" )
|
||||
menu:panelDone()
|
||||
end
|
||||
|
||||
local function APGBuildLogsPanel()
|
||||
local panel = APG_panels[ "logs" ]
|
||||
panel.Paint = function( i, w, h ) end
|
||||
|
||||
menu:initPanel( panel, 0, 40, 0, 35 )
|
||||
menu:switch( 568, 20, "Should we log when there is lag detected?", "logLagDetected" )
|
||||
menu:switch( 568, 20, "Should we log when a player attempts to crash the server", "logStackCrashAttempt" )
|
||||
menu:panelDone()
|
||||
end
|
||||
|
||||
local function APGBuildGhostPanel()
|
||||
local panel = APG_panels[ "ghosting" ]
|
||||
|
||||
panel.Paint = function( i, w, h)
|
||||
draw.RoundedBox( 0, 0, 37, 170, 135, Color( 38, 38, 38, 255 ) )
|
||||
draw.DrawText( "Ghosting color:", "APG_element_font", 5, 37, Color( 189, 189, 189 ), 3 )
|
||||
--draw.RoundedBox(cornerRadius, x, y, width, height, color)
|
||||
draw.RoundedBox( 0, 175, 37, 500, 300, Color( 38, 38, 38, 255) )
|
||||
draw.DrawText( "Bad entities:", "APG_element_font", 180, 37, Color( 189, 189, 189), 3 )
|
||||
draw.DrawText( "(Right-Click to Toggle)", "APG_title2_font", 280, 38, Color( 189, 189, 189), 3 )
|
||||
--draw.DrawText(text, font="DermaDefault", x=0, y=0, color=Color(255,255,255,255), xAlign=TEXT_ALIGN_LEFT)
|
||||
draw.DrawText( "Good entities:", "APG_element_font", 180, 230, Color( 189, 189, 189), 3 )
|
||||
draw.DrawText( "(Right-Click to Toggle)", "APG_title2_font", 285, 232, Color( 189, 189, 189), 3 )
|
||||
end
|
||||
|
||||
menu:initPanel( panel, 0, 180, 0, 35 )
|
||||
menu:switch( 170, 20, "Always frozen", "alwaysFrozen" )
|
||||
menu:switch( 170, 20, "Fading Doors", "fadingDoorGhosting" )
|
||||
menu:switch( 170, 20, "Ignore Vehicles", "vehAntiGhost" )
|
||||
menu:switch( 170, 20, "Enable Color", "ghostColorToggle")
|
||||
local offsets = menu:panelDone()
|
||||
|
||||
local Mixer = vgui.Create( "CtrlColor", panel )
|
||||
Mixer:SetPos( 5, 55 )
|
||||
Mixer:SetSize( 160, 110 )
|
||||
Mixer.Mixer.ValueChanged = function( self, color )
|
||||
APG.cfg[ "ghostColor" ].value = Color( color.r, color.g, color.b, color.a)
|
||||
end
|
||||
|
||||
local badList = vgui.Create( "DListView", panel )
|
||||
badList:Clear()
|
||||
badList:SetPos( 180, 55 )
|
||||
badList:SetSize( panel:GetWide() - 185, panel:GetTall() / 2.5 )
|
||||
badList:SetMultiSelect( false )
|
||||
badList:SetHideHeaders( false )
|
||||
badList:AddColumn( "Class" )
|
||||
badList:AddColumn( "Exact" )
|
||||
|
||||
function badList:OnRowRightClick( id, line )
|
||||
local key = line:GetColumnText(1)
|
||||
local value = not tobool(line:GetColumnText(2))
|
||||
line:SetColumnText( 2, value )
|
||||
APG.cfg[ "badEnts" ].value[key] = value
|
||||
end
|
||||
|
||||
local goodList = vgui.Create( "DListView", panel )
|
||||
goodList:Clear()
|
||||
goodList:SetPos( 180, 250 )
|
||||
goodList:SetSize( panel:GetWide() - 185, panel:GetTall() / 2.5 )
|
||||
goodList:SetMultiSelect( false )
|
||||
goodList:SetHideHeaders( false )
|
||||
goodList:AddColumn( "Class" )
|
||||
goodList:AddColumn( "Exact" )
|
||||
|
||||
function goodList:OnRowRightClick( id, line )
|
||||
local key = line:GetColumnText(1)
|
||||
local value = not tobool(line:GetColumnText(2))
|
||||
line:SetColumnText( 2, value )
|
||||
APG.cfg[ "unGhostingWhitelist" ].value[key] = value
|
||||
end
|
||||
|
||||
|
||||
local function updateTab()
|
||||
|
||||
badList:Clear()
|
||||
for class,complete in pairs(APG.cfg[ "badEnts" ].value) do
|
||||
badList:AddLine(class, complete)
|
||||
end
|
||||
|
||||
goodList:Clear()
|
||||
for class,complete in pairs(APG.cfg[ "unGhostingWhitelist" ].value) do
|
||||
goodList:AddLine(class, complete)
|
||||
end
|
||||
|
||||
end
|
||||
updateTab()
|
||||
|
||||
badList.Paint = function(i,w,h)
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 150, 150, 150, 255 ) )
|
||||
end
|
||||
|
||||
badList.VBar.Paint = function(i,w,h)
|
||||
surface.SetDrawColor( 88, 110, 110, 240 )
|
||||
surface.DrawRect( 0, 0, w, h )
|
||||
end
|
||||
|
||||
badList.VBar.btnGrip.Paint = function(i,w,h)
|
||||
surface.SetDrawColor( 255, 83, 13, 50 )
|
||||
surface.DrawRect( 0, 0, w, h )
|
||||
draw.RoundedBox( 0, 1, 1, w - 2, h / 2, Color( 72, 89, 89, 255 ) )
|
||||
end
|
||||
|
||||
badList.VBar.btnUp.Paint = function(i,w,h)
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 72, 89, 89, 240 ) )
|
||||
end
|
||||
|
||||
badList.VBar.btnDown.Paint = function(i,w,h)
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 72, 89, 89, 240 ) )
|
||||
end
|
||||
|
||||
goodList.Paint = function(i,w,h)
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 150, 150, 150, 255 ) )
|
||||
end
|
||||
|
||||
goodList.VBar.Paint = function(i,w,h)
|
||||
surface.SetDrawColor( 88, 110, 110, 240 )
|
||||
surface.DrawRect( 0, 0, w, h )
|
||||
end
|
||||
|
||||
goodList.VBar.btnGrip.Paint = function(i,w,h)
|
||||
surface.SetDrawColor( 255, 83, 13, 50 )
|
||||
surface.DrawRect( 0, 0, w, h )
|
||||
draw.RoundedBox( 0, 1, 1, w - 2, h / 2, Color( 72, 89, 89, 255 ) )
|
||||
end
|
||||
|
||||
goodList.VBar.btnUp.Paint = function(i,w,h)
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 72, 89, 89, 240 ) )
|
||||
end
|
||||
|
||||
goodList.VBar.btnDown.Paint = function(i,w,h)
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 72, 89, 89, 240 ) )
|
||||
end
|
||||
|
||||
local BadTextEntry = vgui.Create( "DTextEntry", panel )
|
||||
BadTextEntry:SetPos( offsets.x, panel:GetTall() - 100 )
|
||||
BadTextEntry:SetSize( 100, 20 )
|
||||
BadTextEntry:SetText( "Bad Entity class" )
|
||||
BadTextEntry.OnEnter = function( self )
|
||||
chat.AddText( self:GetValue() )
|
||||
end
|
||||
|
||||
local BadAdd = vgui.Create( "DButton" , panel)
|
||||
BadAdd:SetPos( offsets.x + 100, panel:GetTall() - 100 )
|
||||
BadAdd:SetSize( 75,20 )
|
||||
BadAdd:SetText( "Add" )
|
||||
BadAdd.DoClick = function()
|
||||
if BadTextEntry:GetValue() == "Bad Entity class" then return end
|
||||
utils.addBadEntity( BadTextEntry:GetValue() )
|
||||
updateTab()
|
||||
end
|
||||
|
||||
BadAdd:SetTextColor( Color(255, 255, 255) )
|
||||
BadAdd.Paint = function( i, w, h)
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 44, 55, 55, 255 ) )
|
||||
draw.RoundedBox( 0, 1, 1, w-2, h-2, Color( 58, 58, 58, 255 ) )
|
||||
end
|
||||
|
||||
local BadRemove = vgui.Create( "DButton" , panel)
|
||||
BadRemove:SetPos( offsets.x, panel:GetTall() - 80 )
|
||||
BadRemove:SetSize( 175, 20 )
|
||||
BadRemove:SetText( "Remove selected" )
|
||||
BadRemove.DoClick = function()
|
||||
for k, v in pairs(badList:GetSelected()) do
|
||||
local key = v:GetValue(1)
|
||||
APG.cfg[ "badEnts" ].value[key] = nil
|
||||
updateTab()
|
||||
end
|
||||
end
|
||||
|
||||
BadRemove:SetTextColor( Color( 255, 255, 255 ) )
|
||||
BadRemove.Paint = function( i, w, h )
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 58, 58, 58, 255 ) )
|
||||
draw.RoundedBox( 0, 0, 0, w, 1, Color( 30, 30, 30, 125 ) )
|
||||
end
|
||||
|
||||
local GoodTextEntry = vgui.Create( "DTextEntry", panel )
|
||||
GoodTextEntry:SetPos( offsets.x, panel:GetTall() - 45 )
|
||||
GoodTextEntry:SetSize( 100, 20 )
|
||||
GoodTextEntry:SetText( "Good Entity class" )
|
||||
GoodTextEntry.OnEnter = function( self )
|
||||
chat.AddText( self:GetValue() )
|
||||
end
|
||||
|
||||
local GoodAdd = vgui.Create( "DButton" , panel)
|
||||
GoodAdd:SetPos( offsets.x + 100, panel:GetTall() - 45 )
|
||||
GoodAdd:SetSize( 75,20 )
|
||||
GoodAdd:SetText( "Add" )
|
||||
GoodAdd.DoClick = function()
|
||||
if GoodTextEntry:GetValue() == "Good Entity class" then return end
|
||||
utils.addGoodEntity( GoodTextEntry:GetValue() )
|
||||
updateTab()
|
||||
end
|
||||
|
||||
local GoodRemove = vgui.Create( "DButton" , panel)
|
||||
GoodRemove:SetPos( offsets.x, panel:GetTall() - 25 )
|
||||
GoodRemove:SetSize( 175, 20 )
|
||||
GoodRemove:SetText( "Remove selected" )
|
||||
GoodRemove.DoClick = function()
|
||||
for k, v in pairs(goodList:GetSelected()) do
|
||||
local key = v:GetValue(1)
|
||||
APG.cfg[ "unGhostingWhitelist" ].value[key] = nil
|
||||
updateTab()
|
||||
end
|
||||
end
|
||||
|
||||
GoodRemove:SetTextColor( Color( 255, 255, 255 ) )
|
||||
GoodRemove.Paint = function( i, w, h )
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 58, 58, 58, 255 ) )
|
||||
draw.RoundedBox( 0, 0, 0, w, 1, Color( 30, 30, 30, 125 ) )
|
||||
end
|
||||
end
|
||||
|
||||
local main_color = Color( 32, 255, 0, 255 )
|
||||
local main_color_red = Color( 96, 0, 0, 255 )
|
||||
local main_color_darker = Color( 51, 91, 51, 255 )
|
||||
|
||||
local function setScrollerTheme( scroller )
|
||||
scroller:SetSize(1, 0)
|
||||
scroller:SetHideButtons(true)
|
||||
|
||||
function scroller:Paint( w, h )
|
||||
draw.RoundedBox( 0, 0, 0, 1, h, main_color_darker )
|
||||
end
|
||||
|
||||
function scroller.btnGrip:Paint( w, h )
|
||||
draw.RoundedBox( 0, 0, 0, 1, h, main_color )
|
||||
end
|
||||
end
|
||||
|
||||
local function openMenu( len )
|
||||
len = net.ReadUInt( 32 )
|
||||
if len == 0 then return end
|
||||
local settings = net.ReadData( len )
|
||||
settings = util.Decompress( settings )
|
||||
settings = util.JSONToTable( settings )
|
||||
|
||||
APG.cfg = settings.cfg
|
||||
table.Merge(APG, settings)
|
||||
|
||||
local APG_Main = vgui.Create( "DFrame" )
|
||||
APG_Main:SetSize( 800, 450 )
|
||||
APG_Main:SetPos( 800 - APG_Main:GetWide() / 2, 450 - APG_Main:GetTall() / 2)
|
||||
APG_Main:SetTitle( "" )
|
||||
APG_Main:SetVisible( true )
|
||||
APG_Main:SetDraggable( true )
|
||||
APG_Main:MakePopup()
|
||||
APG_Main:ShowCloseButton( false )
|
||||
APG_Main.Paint = function(i,w,h)
|
||||
draw.RoundedBox(4,0,0,w,h,Color(34, 34, 34, 255))
|
||||
draw.RoundedBox(0,0,23,w,1,main_color)
|
||||
|
||||
local name = "A.P.G. - Anti Prop Griefing Solution"
|
||||
draw.DrawText( name, "APG_title_font",8, 5, Color( 204, 204, 204, 255 ), 3 )
|
||||
end
|
||||
|
||||
local closeButton = vgui.Create( "DButton",APG_Main )
|
||||
closeButton:SetPos( APG_Main:GetWide() - 20, 4 )
|
||||
closeButton:SetSize( 18, 18 )
|
||||
closeButton:SetText(" ")
|
||||
closeButton.DoClick = function()
|
||||
APG_Main:Hide()
|
||||
APG_Main:Remove()
|
||||
end
|
||||
|
||||
closeButton.Paint = function(i,w,h)
|
||||
draw.RoundedBox( 0,0,0,w,h, Color( 91, 51, 51, 255 ) )
|
||||
draw.DrawText( "✕", "APG_sideBar_font", 1, -1, Color( 204, 204, 204, 255 ), TEXT_ALIGN_TOP )
|
||||
end
|
||||
|
||||
local saveButton = vgui.Create( "DButton", APG_Main )
|
||||
saveButton:SetPos( APG_Main:GetWide() - 117, 4 )
|
||||
saveButton:SetSize( 77, 18 )
|
||||
saveButton:SetText(" ")
|
||||
|
||||
saveButton.DoClick = function()
|
||||
if not LocalPlayer():IsSuperAdmin() then return end
|
||||
local settings = APG
|
||||
settings = util.TableToJSON( settings )
|
||||
settings = util.Compress( settings )
|
||||
net.Start( "apg_settings_c2s")
|
||||
net.WriteUInt( settings:len(), 32 ) -- Write the length of the data (up to {{ user_id | 76561197972967270 }})
|
||||
net.WriteData( settings, settings:len() ) -- Write the data
|
||||
net.SendToServer()
|
||||
showNotice(1, "APG Settings saved!")
|
||||
end
|
||||
|
||||
saveButton.Paint = function(i,w,h)
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 51, 91, 51, 255 ) )
|
||||
draw.DrawText( "Save Settings", "APG_title2_font",w/2, 1, Color( 204, 204, 204, 255 ), 1 )
|
||||
end
|
||||
|
||||
-- Side bar
|
||||
local sidebar = vgui.Create( "DScrollPanel", APG_Main )
|
||||
setScrollerTheme( sidebar:GetVBar() )
|
||||
|
||||
sidebar:SetSize( APG_Main:GetWide() / 4 , APG_Main:GetTall() - 35)
|
||||
sidebar:SetPos( 0, 30 )
|
||||
sidebar.Paint = function( i, w, h )
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 33, 33, 33, 255 ) )
|
||||
draw.RoundedBox( 0, w-1, 0, 1, h, main_color_darker)
|
||||
end
|
||||
|
||||
local x, y = (APG_Main:GetWide() - sidebar:GetWide()) - 19, APG_Main:GetTall() - 35
|
||||
local px, py = sidebar:GetWide() + 15, 30
|
||||
local first = true
|
||||
|
||||
local modules = APG.modules
|
||||
|
||||
-- Attempt to force essential modules to be enabled.
|
||||
modules["home"] = true
|
||||
modules["canphysgun"] = true
|
||||
|
||||
for k, v in next, APG.modules do
|
||||
if k == "canphysgun" then continue end -- This module doesn't have UI, so it doesn't need a UI button.
|
||||
|
||||
local panel = vgui.Create( "DScrollPanel", APG_Main )
|
||||
setScrollerTheme( panel:GetVBar() )
|
||||
|
||||
panel:SetSize( x, y )
|
||||
panel:SetPos( px, py )
|
||||
panel:SetVisible( first )
|
||||
|
||||
panel.Paint = function() end
|
||||
APG_panels[k] = panel
|
||||
first = false
|
||||
|
||||
local button = vgui.Create( "DButton", panel )
|
||||
button:SetPos( 0, 0 )
|
||||
button:SetSize( panel:GetWide(), 35 )
|
||||
button:SetText("")
|
||||
|
||||
button.UpdateColours = function( label, skin )
|
||||
label:SetTextStyleColor( Color( 189, 189, 189 ) )
|
||||
end
|
||||
|
||||
button.Paint = function( slf, w, h )
|
||||
local enabled = APG.modules[k]
|
||||
draw.RoundedBox( 0, 0, h * 0.85, w-5, 1, enabled and main_color or main_color_red )
|
||||
|
||||
local text = utils.getNiceName(k) .. " module "
|
||||
draw.DrawText( text, "APG_mainPanel_font", 5, 8, Color( 189, 189, 189 ), 3 )
|
||||
menu:mainSwitch( w * 0.90, (h * 0.5) - 16, enabled )
|
||||
end
|
||||
|
||||
button.DoClick = function()
|
||||
APG.modules[k] = not APG.modules[k]
|
||||
end
|
||||
end
|
||||
|
||||
local i = 0
|
||||
local height = ( sidebar:GetTall()/5 )
|
||||
|
||||
for k, v in next, APG.modules do
|
||||
if k == "canphysgun" then continue end
|
||||
|
||||
local button = sidebar:Add( "DButton" )
|
||||
button:SetPos( 5, (height + 5) * i)
|
||||
button:SetSize( sidebar:GetWide() - 10 , height )
|
||||
button:SetText("")
|
||||
|
||||
button.DoClick = function()
|
||||
for l,m in next, APG_panels do
|
||||
if k ~= l then
|
||||
APG_panels[l]:SetVisible( false )
|
||||
else
|
||||
APG_panels[l]:SetVisible( true )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local size = sidebar:GetWide()
|
||||
button.Paint = function( _, w, h )
|
||||
local name = utils.getNiceName( k )
|
||||
if button.Hovered then
|
||||
draw.RoundedBox( 5, 0, 0, w, h, Color( 48, 48, 48, 255 ) )
|
||||
draw.RoundedBox( 0, 2, 2, w - 4, h - 4, Color( 36, 36, 36, 255 ) )
|
||||
end
|
||||
if APG_panels[k]:IsVisible() then
|
||||
draw.RoundedBox( 0, 0, 0, w, h, Color( 51, 51, 51, 255 ) )
|
||||
draw.RoundedBox( 0, w * 0.10, h * 0.60, w * 0.8, 2, main_color_darker )
|
||||
end
|
||||
|
||||
draw.DrawText( name, "APG_sideBar_font", ( size - name:len() ) / 2, h * 0.35, Color( 189, 189, 189 ), 1)
|
||||
end
|
||||
|
||||
if k == "home" then
|
||||
button:DoClick()
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
-- Build all the expected panels.
|
||||
|
||||
APGBuildHomePanel()
|
||||
APGBuildMiscPanel()
|
||||
APGBuildToolsPanel()
|
||||
APGBuildGhostPanel()
|
||||
APGBuildLagPanel()
|
||||
APGBuildStackPanel()
|
||||
APGBuildLogsPanel()
|
||||
APGBuildNotificationPanel()
|
||||
end
|
||||
|
||||
net.Receive( "apg_menu_s2c", openMenu )
|
||||
|
||||
properties.Add( "apgoptions", {
|
||||
MenuLabel = "APG Options", -- Name to display on the context menu
|
||||
Order = 9999, -- The order to display this property relative to other properties
|
||||
MenuIcon = "icon16/fire.png", -- The icon to display next to the property
|
||||
|
||||
Filter = function( self, ent, ply ) -- A function that determines whether an entity is valid for this property
|
||||
if not ply:IsSuperAdmin() then return false end
|
||||
if not IsValid(ent) then return false end
|
||||
if not ent:GetClass() then return false end
|
||||
if ent:EntIndex() < 0 then return false end
|
||||
|
||||
return true
|
||||
end,
|
||||
MenuOpen = function( self, option, ent, tr )
|
||||
local submenu = option:AddSubMenu()
|
||||
local function addoption( str, data )
|
||||
local menu = submenu:AddOption( str, data.callback )
|
||||
|
||||
if data.icon then
|
||||
menu:SetImage( data.icon )
|
||||
end
|
||||
|
||||
return menu
|
||||
end
|
||||
|
||||
addoption( "Sleep entities of this Class", {
|
||||
icon = "icon16/clock.png",
|
||||
callback = function() self:APGcmd( ent, "sleepclass" ) end,
|
||||
})
|
||||
|
||||
addoption( "Freeze entities of this Class", {
|
||||
icon = "icon16/bell_delete.png",
|
||||
callback = function() self:APGcmd( ent, "freezeclass" ) end,
|
||||
})
|
||||
|
||||
submenu:AddSpacer()
|
||||
|
||||
addoption( "Cleanup Owner - Unfrozens", {
|
||||
icon = "icon16/cog_delete.png",
|
||||
callback = function() self:APGcmd( ent, "clearunfrozen" ) end,
|
||||
})
|
||||
|
||||
addoption( "Cleanup Owner", {
|
||||
icon = "icon16/bin_closed.png",
|
||||
callback = function() self:APGcmd( ent, "clearowner" ) end,
|
||||
})
|
||||
|
||||
submenu:AddSpacer()
|
||||
|
||||
addoption( "Get Owner SteamID", {
|
||||
icon = "icon16/user.png",
|
||||
callback = function() self:APGcmd( ent, "getownerid" ) end,
|
||||
})
|
||||
|
||||
addoption( "Get Owner Entity Count", {
|
||||
icon = "icon16/brick.png",
|
||||
callback = function() self:APGcmd( ent, "getownercount" ) end,
|
||||
})
|
||||
|
||||
submenu:AddSpacer()
|
||||
|
||||
addoption( "Add this entity class to the Ghosting List", {
|
||||
icon = "icon16/cross.png",
|
||||
callback = function() self:APGcmd( ent, "addghost" ) end,
|
||||
})
|
||||
|
||||
addoption( "Remove this entity class from the Ghosting List", {
|
||||
icon = "icon16/tick.png",
|
||||
callback = function() self:APGcmd( ent, "remghost" ) end,
|
||||
})
|
||||
|
||||
submenu:AddSpacer()
|
||||
|
||||
addoption( "Ghost this entity", {
|
||||
icon = "icon16/tick.png",
|
||||
callback = function() self:APGcmd( ent, "ghost" ) end,
|
||||
})
|
||||
|
||||
end,
|
||||
Action = function( self, ent ) end,
|
||||
APGcmd = function( self, ent, cmd )
|
||||
if cmd == "getownerid" then
|
||||
local owner, _ = ent:CPPIGetOwner()
|
||||
if IsValid( owner ) then
|
||||
local id = tostring( owner:SteamID() )
|
||||
local name = tostring( owner:Nick() )
|
||||
SetClipboardText( id )
|
||||
showNotice(0, name .. " [ " .. id .. " ]" .. " has been copied to your clipboard.")
|
||||
else
|
||||
showNotice(0, "\nOops, that's not a Player!")
|
||||
end
|
||||
elseif cmd == "getentname" then
|
||||
showNotice(0, ent:GetClass())
|
||||
elseif IsValid( ent ) and ent.EntIndex() then
|
||||
net.Start( "apg_context_c2s" )
|
||||
net.WriteString( cmd )
|
||||
net.WriteEntity( ent )
|
||||
net.SendToServer()
|
||||
end
|
||||
end,
|
||||
})
|
||||
281
addons/apg/lua/apg/cl_utils.lua
Normal file
281
addons/apg/lua/apg/cl_utils.lua
Normal file
@@ -0,0 +1,281 @@
|
||||
surface.CreateFont( "APG_title_font", {
|
||||
font = "Arial",
|
||||
size = 14,
|
||||
weight = 700,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "APG_title2_font", {
|
||||
font = "Arial",
|
||||
size = 13,
|
||||
weight = 700,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "APG_sideBar_font", {
|
||||
font = "Arial",
|
||||
size = 18,
|
||||
weight = 1500,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "APG_mainPanel_font", {
|
||||
font = "Arial",
|
||||
size = 19,
|
||||
weight = 8500,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "APG_tick_font", {
|
||||
font = "Arial",
|
||||
size = 29,
|
||||
weight = 1900,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "APG_element_font", {
|
||||
font = "Arial",
|
||||
size = 17,
|
||||
weight = 1300,
|
||||
} )
|
||||
|
||||
surface.CreateFont( "APG_element2_font", {
|
||||
font = "Arial",
|
||||
size = 17,
|
||||
weight = 2900,
|
||||
} )
|
||||
|
||||
local utils = {}
|
||||
local menu = {}
|
||||
|
||||
function utils.addBadEntity( class )
|
||||
local found = false
|
||||
for k, v in pairs ( ents.GetAll() ) do
|
||||
if class == v:GetClass() then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
for k in pairs (scripted_ents.GetList()) do
|
||||
if class == k then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
APG.cfg["badEnts"].value[ class ] = found
|
||||
end
|
||||
|
||||
function utils.addGoodEntity( class )
|
||||
local found = false
|
||||
for k, v in pairs ( ents.GetAll() ) do
|
||||
if class == v:GetClass() then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
for k in pairs (scripted_ents.GetList()) do
|
||||
if class == k then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
APG.cfg["unGhostingWhitelist"].value[ class ] = found
|
||||
end
|
||||
|
||||
function utils.addInvalidWhitelist( model )
|
||||
local found = false
|
||||
for k, v in pairs ( ents.GetAll() ) do
|
||||
if model == v:GetModel() then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
APG.cfg["invalidPhysicsWhitelist"].value[ model ] = found
|
||||
end
|
||||
|
||||
function utils.getNiceName( str )
|
||||
local nName = string.gsub(str, "^%l", string.upper)
|
||||
nName = string.gsub(nName, "_", " " )
|
||||
return nName
|
||||
end
|
||||
|
||||
function menu:mainSwitch( x, y, on )
|
||||
draw.RoundedBox(10, x, y, 45, 18, Color( 58, 58, 58, 255))
|
||||
if on then
|
||||
draw.RoundedBox(10, x + 1, y + 1, 45 - 2, 18 - 2, Color( 11, 70, 30, 255))
|
||||
draw.DrawText( "ON", "APG_title_font", x + 8, y + 2, Color( 189, 189, 189 ), 3 )
|
||||
draw.RoundedBox(10, x + 27, y, 18, 18, Color( 88, 88, 88, 255))
|
||||
else
|
||||
--draw.RoundedBox(10, x, y, 45, 18, Color( 110, 28, 38, 255))
|
||||
draw.RoundedBox(10, x + 1, y + 1, 43, 16, Color( 34, 34, 34, 255))
|
||||
draw.DrawText( "OFF", "APG_title_font", x + 21, y + 2, Color( 189, 189, 189), 3 )
|
||||
draw.RoundedBox(10, x, y, 18, 18, Color( 88, 88, 88, 255))
|
||||
end
|
||||
--draw.RoundedBox(0, x+20, y, 1, 18, Color( 88, 88, 88, 255))
|
||||
end
|
||||
|
||||
|
||||
function menu:initPanel( panel, x, y, ix, iy )
|
||||
self.panel = panel
|
||||
self.vars = {x = x, y = y, ix = ix, iy = iy}
|
||||
end
|
||||
|
||||
function menu:panelDone()
|
||||
local old = self.vars
|
||||
|
||||
self.panel = {}
|
||||
self.vars = {}
|
||||
|
||||
return old
|
||||
end
|
||||
|
||||
function menu:grabVars()
|
||||
local v = self.vars
|
||||
return self.panel, v.x, v.y, v.ix, v.iy
|
||||
end
|
||||
|
||||
function menu:switch( w, h, text, var )
|
||||
local panel, x, y, ix, iy = menu:grabVars()
|
||||
local button = vgui.Create("DButton", panel)
|
||||
|
||||
local isKey = ( type(var) == "string" )
|
||||
local isFunction = ( type(var) == "function" )
|
||||
|
||||
button:SetPos(x, y)
|
||||
button:SetSize(w, h)
|
||||
button:SetText("")
|
||||
|
||||
button.Paint = function(slf, w, h)
|
||||
local enabled = isKey and APG.cfg[ var ].value or isFunction
|
||||
draw.RoundedBox(0, 0, h * 0.95, w - 5, 1, Color(250, 250, 250, 1))
|
||||
draw.DrawText( text, "APG_element2_font", 0, 0, Color( 189, 189, 189), 3 )
|
||||
menu:mainSwitch( w-45, 0, enabled )
|
||||
end
|
||||
|
||||
if isKey then
|
||||
button.DoClick = function()
|
||||
APG.cfg[ var ].value = not APG.cfg[ var ].value
|
||||
end
|
||||
else
|
||||
if isFunction then
|
||||
button.DoClick = var
|
||||
else
|
||||
button:SetEnabled( false )
|
||||
end
|
||||
end
|
||||
|
||||
self.vars.x = x + ix
|
||||
self.vars.y = y + iy
|
||||
end
|
||||
|
||||
function menu:numSlider( w, h, text, var, minSlider, maxSlider, decimal )
|
||||
local panel, x, y, ix, iy = menu:grabVars()
|
||||
|
||||
local slider = panel:Add( "DNumSlider" )
|
||||
|
||||
slider:SetPos( x, y )
|
||||
slider:SetSize( w, h )
|
||||
slider:SetText( "" )
|
||||
slider:SetMin( minSlider )
|
||||
slider:SetMax( maxSlider )
|
||||
slider:SetDecimals( decimal )
|
||||
slider:SetValue( APG.cfg[ var ].value )
|
||||
slider.OnValueChanged = function( self, newValue )
|
||||
APG.cfg[ var ].value = newValue
|
||||
end
|
||||
|
||||
slider.Paint = function(slf, w, h)
|
||||
draw.RoundedBox( 0, 0, h * 0.97, w - 5, 1, Color(250, 250, 250, 1 ) )
|
||||
draw.DrawText( text, "APG_element2_font", 0, 0, Color( 189, 189, 189), 3 )
|
||||
end
|
||||
|
||||
slider.Slider.Paint = function( slf, w, h)
|
||||
--draw.RoundedBox(cornerRadius, x, y, width, height, color)
|
||||
draw.RoundedBox( 0, 8, 9 - 1, w - 16, 1 + 2, Color( 250, 250, 250, 1))
|
||||
end
|
||||
|
||||
slider.Slider.Knob.Paint = function(slf, w, h)
|
||||
draw.RoundedBox(6, 0, 4, 10, 10, Color( 11, 70, 30, 255))
|
||||
end
|
||||
|
||||
slider.Slider:Dock( NODOCK )
|
||||
slider.Slider:SetPos( panel:GetWide() - 110, 0 )
|
||||
slider.Slider:SetWide( 100 )
|
||||
|
||||
slider.TextArea:Dock( NODOCK )
|
||||
slider.TextArea:SetPos( panel:GetWide() - 145, - 3 )
|
||||
slider.TextArea.m_colText = Color(189, 189, 189)
|
||||
slider.TextArea.Paint = function( self, w, h)
|
||||
draw.RoundedBox(10, 0, 1, w-15, h, Color( 58, 58, 58, 255))
|
||||
derma.SkinHook( "Paint", "TextEntry", self, w, h )
|
||||
end
|
||||
|
||||
self.vars.x = x + ix
|
||||
self.vars.y = y + iy
|
||||
end
|
||||
|
||||
function menu:textEntry( w, h, text, var )
|
||||
local panel, x, y, ix, iy = menu:grabVars()
|
||||
|
||||
local label = panel:Add( "DLabel" )
|
||||
|
||||
label:SetPos( x, y )
|
||||
label:SetSize( w, h )
|
||||
label:SetText( text )
|
||||
label:SetFont("APG_element2_font")
|
||||
label:SetColor( Color( 189, 189, 189) )
|
||||
label.Paint = function(self, w, h)
|
||||
draw.RoundedBox(0, 0, h * 0.97, w, 1, Color(250, 250, 250, 1))
|
||||
end
|
||||
|
||||
local txtEntry = vgui.Create( "DTextEntry", panel ) -- create the form as a child of frame
|
||||
txtEntry:SetPos( panel:GetWide() - 110, y-1 )
|
||||
txtEntry:SetSize( 125, 20 )
|
||||
txtEntry:SetText( "custom" )
|
||||
txtEntry.OnEnter = function( self )
|
||||
end
|
||||
|
||||
self.vars.x = x + ix
|
||||
self.vars.y = y + iy
|
||||
end
|
||||
|
||||
function menu:comboBox( w, h, text, var, content )
|
||||
local panel, x, y, ix, iy = menu:grabVars()
|
||||
|
||||
local label = panel:Add( "DLabel" )
|
||||
|
||||
label:SetPos( x, y )
|
||||
label:SetSize( w, h )
|
||||
label:SetText( text )
|
||||
label:SetFont("APG_element2_font")
|
||||
label:SetColor( Color( 189, 189, 189) )
|
||||
label.Paint = function(self, w, h)
|
||||
draw.RoundedBox(0, 0, h * 0.97, w, 1, Color(250, 250, 250, 1))
|
||||
end
|
||||
|
||||
local comboBox = vgui.Create( "DComboBox", panel )
|
||||
comboBox:SetPos( panel:GetWide() - 145, y-2 )
|
||||
comboBox:SetSize( 125, 20 )
|
||||
comboBox:SetValue( APG.cfg[var].value )
|
||||
for k, v in pairs ( content ) do
|
||||
comboBox:AddChoice(v)
|
||||
end
|
||||
comboBox.OnSelect = function( panel, index, value )
|
||||
APG.cfg[var].value = value
|
||||
end
|
||||
comboBox.Paint = function(i, w, h)
|
||||
draw.RoundedBox(0, 0, 0, w, h, Color(58, 58, 58, 240))
|
||||
end
|
||||
comboBox:SetTextColor(Color( 189, 189, 189))
|
||||
local o_OpenMenu = comboBox.OpenMenu
|
||||
comboBox.OpenMenu = function( pControlOpener )
|
||||
o_OpenMenu(pControlOpener)
|
||||
comboBox.Menu.Paint = function (i, w, h)
|
||||
draw.RoundedBox(0, 0, 0, w, h, Color(58, 58, 58, 240))
|
||||
end
|
||||
end
|
||||
|
||||
self.vars.x = x + ix
|
||||
self.vars.y = y + iy
|
||||
end
|
||||
|
||||
return {utils = utils, menu = menu}
|
||||
97
addons/apg/lua/apg/modules/canphysgun.lua
Normal file
97
addons/apg/lua/apg/modules/canphysgun.lua
Normal file
@@ -0,0 +1,97 @@
|
||||
--[[------------------------------------------
|
||||
|
||||
============================
|
||||
Physgun Permissions Module
|
||||
============================
|
||||
|
||||
Developer informations :
|
||||
---------------------------------
|
||||
Used variables :
|
||||
|
||||
]]--------------------------------------------
|
||||
local mod = "canphysgun"
|
||||
|
||||
--[[ Entity pickup part ]]
|
||||
local GM = GM or GAMEMODE
|
||||
APG._PhysgunPickup = APG._PhysgunPickup or GM.PhysgunPickup
|
||||
|
||||
APG.hookAdd(mod, "PhysgunPickup","APG_PhysgunPickup", function(ply, ent)
|
||||
if not APG.isBadEnt( ent ) then return end
|
||||
if not APG.canPhysGun( ent, ply ) then return false end
|
||||
end)
|
||||
|
||||
function GM:PhysgunPickup( ply, ent )
|
||||
local canPickup = APG._PhysgunPickup( self, ply, ent )
|
||||
hook.Run("APG_PostPhysgunPickup", ply, ent, canPickup)
|
||||
|
||||
if not canPickup then return canPickup end -- Assumed as `false` but returning just incase.
|
||||
|
||||
ent.APG_HeldBy = ent.APG_HeldBy or {}
|
||||
ent.APG_HeldBy.plys = ent.APG_HeldBy.plys or {}
|
||||
ent.APG_Picked = true
|
||||
ent.APG_Frozen = false
|
||||
|
||||
if ent.APG_HeldBy and ent.APG_HeldBy.plys and not ent.APG_HeldBy.plys[sid] then
|
||||
local HasHolder = (#ent.APG_HeldBy.plys > 0)
|
||||
local HeldByLast = ent.APG_HeldBy.last
|
||||
|
||||
if HasHolder then
|
||||
if HeldByLast then
|
||||
for _, ply in next, ent.APG_HeldBy.plys do
|
||||
APG.ForcePlayerDrop(ply, ent)
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ent.APG_HeldBy.plys[ply:SteamID()] = ply
|
||||
ent.APG_HeldBy.last = {ply = ply, id = ply:SteamID()}
|
||||
ply.APG_CurrentlyHolding = ent
|
||||
|
||||
if APG.cfg["blockContraptionMove"].value then
|
||||
local count = 0
|
||||
local noFrozen = true
|
||||
|
||||
for _,v in next, constraint.GetAllConstrainedEntities(ent) do
|
||||
count = count + 1
|
||||
if v.APG_Frozen then
|
||||
noFrozen = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if noFrozen and ( count > 1 ) then
|
||||
timer.Simple(0, function()
|
||||
APG.freezeIt(ent, true)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
return canPickup -- Assumed as `true`
|
||||
end
|
||||
|
||||
--[[ PhysGun Drop and Anti Throw Props ]]
|
||||
APG.hookAdd(mod, "PhysgunDrop", "APG_physGunDrop", function( ply, ent )
|
||||
ent.APG_HeldBy = ent.APG_HeldBy or {}
|
||||
|
||||
if ent.APG_HeldBy.plys then
|
||||
ent.APG_HeldBy.plys[ply:SteamID()] = nil -- Remove the holder.
|
||||
end
|
||||
|
||||
ply.APG_CurrentlyHolding = nil
|
||||
|
||||
if #ent.APG_HeldBy > 0 then return end
|
||||
|
||||
ent.APG_Picked = false
|
||||
|
||||
if APG.isBadEnt( ent ) and not APG.cfg["allowPK"].value then
|
||||
APG.killVelocity(ent,true,false,true) -- Extend to constrained props, and wake target.
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Load hooks and timers ]]
|
||||
|
||||
APG.updateHooks(mod)
|
||||
APG.updateTimers(mod)
|
||||
403
addons/apg/lua/apg/modules/ghosting.lua
Normal file
403
addons/apg/lua/apg/modules/ghosting.lua
Normal file
@@ -0,0 +1,403 @@
|
||||
--[[------------------------------------------
|
||||
|
||||
============================
|
||||
GHOSTING/UNGHOSTING MODULE
|
||||
============================
|
||||
|
||||
Developer informations :
|
||||
---------------------------------
|
||||
Used variables :
|
||||
ghostColor = { value = Color(34, 34, 34, 220), desc = "Color set on ghosted props" }
|
||||
badEnts = {
|
||||
value = {
|
||||
["prop_physics"] = true,
|
||||
["wire_"] = false,
|
||||
["gmod_"] = false },
|
||||
desc = "Entities to ghost/control/secure"}
|
||||
alwaysFrozen = { value = false, desc = "Set to true to auto freeze props on physgun drop" }
|
||||
|
||||
]]--------------------------------------------
|
||||
|
||||
local mod = "ghosting"
|
||||
|
||||
--[[ Override base functions ]]
|
||||
local ENT = FindMetaTable( "Entity" )
|
||||
APG._SetCollisionGroup = APG._SetCollisionGroup or ENT.SetCollisionGroup
|
||||
|
||||
function ENT:SetCollisionGroup( group )
|
||||
local group = group
|
||||
|
||||
local isBadEnt = APG.isBadEnt( self )
|
||||
local hasValidOwner = APG.getOwner( self )
|
||||
local groupIsNone = group == COLLISION_GROUP_NONE
|
||||
local isNotFrozen = not self.APG_Frozen
|
||||
local isWhitelistedEnt = APG.isWhitelistedEnt(self)
|
||||
|
||||
local shouldMakeInteractable = isBadEnt and hasValidOwner and groupIsNone and isNotFrozen and not isWhitelistedEnt
|
||||
|
||||
if shouldMakeInteractable then
|
||||
group = COLLISION_GROUP_INTERACTIVE
|
||||
end
|
||||
|
||||
APG._SetCollisionGroup( self, group )
|
||||
end
|
||||
|
||||
APG._SetColor = APG._SetColor or ENT.SetColor
|
||||
|
||||
function ENT:SetColor( color, ... )
|
||||
local color = color
|
||||
local r, g, b, a
|
||||
|
||||
if type(color) == "number" then
|
||||
color = Color(color, select(1, ...) or 255, select(2, ...) or 255, select(3, ...) or 255)
|
||||
elseif type(color) == "table" and not IsColor(color) then
|
||||
r = color.r or 255
|
||||
g = color.g or 255
|
||||
b = color.b or 255
|
||||
a = color.a or 255
|
||||
color = Color(r, g, b, a)
|
||||
end
|
||||
|
||||
if not IsColor(color) then
|
||||
ErrorNoHalt( "Invalid color passed to SetColor!\nThis error prevents stuff from turning purple/pink." )
|
||||
else
|
||||
APG._SetColor( self, color )
|
||||
end
|
||||
end
|
||||
|
||||
local PhysObj = FindMetaTable( "PhysObj" )
|
||||
APG._EnableMotion = APG._EnableMotion or PhysObj.EnableMotion
|
||||
function PhysObj:EnableMotion( bool )
|
||||
local ent = self:GetEntity()
|
||||
if APG.isBadEnt( ent ) and APG.getOwner( ent ) then
|
||||
ent.APG_Frozen = not bool
|
||||
if not ent.APG_Frozen then
|
||||
ent:SetCollisionGroup(COLLISION_GROUP_INTERACTIVE)
|
||||
end
|
||||
end
|
||||
return APG._EnableMotion( self, bool )
|
||||
end
|
||||
|
||||
function APG.isTrap( ent, fullscan )
|
||||
local check = false
|
||||
local center = ent:LocalToWorld( ent:OBBCenter() )
|
||||
local bRadius = ent:BoundingRadius()
|
||||
local cache = {}
|
||||
|
||||
for _,v in next, ents.FindInSphere( center, bRadius ) do
|
||||
if v:IsPlayer() and v:Alive() then
|
||||
local pos = v:GetPos()
|
||||
local trace = { start = pos, endpos = pos, filter = v }
|
||||
local tr = util.TraceEntity( trace, v )
|
||||
|
||||
if tr.Entity == ent then
|
||||
if fullscan then
|
||||
table.insert( cache, v )
|
||||
else
|
||||
check = v
|
||||
end
|
||||
end
|
||||
elseif APG.IsVehicle(v) then
|
||||
-- Check if the distance between the spheres centers is less than the sum of their radius.
|
||||
local vCenter = v:LocalToWorld( v:OBBCenter() )
|
||||
if center:Distance( vCenter ) < v:BoundingRadius() then
|
||||
check = v
|
||||
end
|
||||
end
|
||||
|
||||
if check then break end
|
||||
end
|
||||
|
||||
if fullscan and ( #cache > 0 ) then
|
||||
return cache
|
||||
else
|
||||
return check or false
|
||||
end
|
||||
end
|
||||
|
||||
function APG.entGhost( ent, noCollide, enforce )
|
||||
if not APG.modules[ mod ] or not APG.isBadEnt( ent ) then return end
|
||||
if APG.cfg["vehAntiGhost"].value and APG.IsVehicle( ent ) then return end
|
||||
if ent.jailWall then return end
|
||||
|
||||
if not ent.APG_Ghosted then
|
||||
ent.FPPAntiSpamIsGhosted = nil -- Override FPP Ghosting.
|
||||
|
||||
DropEntityIfHeld( ent )
|
||||
ent:ForcePlayerDrop()
|
||||
|
||||
ent.APG_oldCollisionGroup = ent:GetCollisionGroup()
|
||||
|
||||
if not enforce then
|
||||
-- If and old collision group was set get it.
|
||||
if ent.OldCollisionGroup then ent.APG_oldCollisionGroup = ent.OldCollisionGroup end -- For FPP
|
||||
if ent.DPP_oldCollision then ent.APG_oldCollisionGroup = ent.DPP_oldCollision end -- For DPP
|
||||
|
||||
ent.OldCollisionGroup = nil
|
||||
ent.DPP_oldCollision = nil
|
||||
end
|
||||
|
||||
ent.APG_Ghosted = true
|
||||
if APG.cfg["ghostColorToggle"].value then
|
||||
timer.Simple(0, function()
|
||||
if not IsValid( ent ) then return end
|
||||
|
||||
if not ent.APG_oldColor then
|
||||
ent.APG_oldColor = ent:GetColor()
|
||||
|
||||
if not enforce then
|
||||
if ent.OldColor then ent.APG_oldColor = ent.OldColor end -- For FPP
|
||||
if ent.__DPPColor then ent.APG_oldColor = ent.__DPPColor end -- For DPP
|
||||
|
||||
ent.OldColor = nil
|
||||
ent.__DPPColor = nil
|
||||
end
|
||||
end
|
||||
|
||||
ent:SetColor( APG.cfg[ "ghostColor" ].value )
|
||||
end)
|
||||
end
|
||||
|
||||
ent.APG_oldRenderMode = ent:GetRenderMode()
|
||||
ent:SetRenderMode( RENDERMODE_TRANSALPHA )
|
||||
ent:DrawShadow( false )
|
||||
|
||||
if noCollide then
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_WORLD )
|
||||
else
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_DEBRIS_TRIGGER )
|
||||
end
|
||||
|
||||
do -- Fix magic surfing
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:EnableCollisions( false )
|
||||
timer.Simple(0, function()
|
||||
phys:EnableCollisions( true )
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
ent:CollisionRulesChanged()
|
||||
end
|
||||
end
|
||||
|
||||
function APG.entUnGhost( ent, ply, failmsg )
|
||||
if not APG.modules[ mod ] or not APG.isBadEnt( ent ) then return end
|
||||
if ent.APG_Picked or (ent.APG_HeldBy and #ent.APG_HeldBy > 1) then return end
|
||||
|
||||
if ent.APG_Ghosted == true then
|
||||
ent.APG_isTrap = APG.isTrap(ent)
|
||||
if not ent.APG_isTrap then
|
||||
ent.APG_Ghosted = false
|
||||
ent:DrawShadow( true )
|
||||
|
||||
ent:SetRenderMode( ent.APG_oldRenderMode or RENDERMODE_NORMAL )
|
||||
if APG.cfg["ghostColorToggle"].value then
|
||||
ent:SetColor( ent.APG_oldColor or Color( 255, 255, 255, 255) )
|
||||
end
|
||||
ent.APG_oldColor = false
|
||||
|
||||
local newCollisionGroup = COLLISION_GROUP_INTERACTIVE
|
||||
if APG.isWhitelistedEnt(ent) then
|
||||
newCollisionGroup = ent.APG_spawnedCollisionGroup
|
||||
elseif ent.APG_oldCollisionGroup == COLLISION_GROUP_WORLD then
|
||||
newCollisionGroup = ent.APG_oldCollisionGroup
|
||||
elseif ent.APG_Frozen then
|
||||
newCollisionGroup = COLLISION_GROUP_NONE
|
||||
end
|
||||
|
||||
ent:SetCollisionGroup( newCollisionGroup )
|
||||
|
||||
ent:CollisionRulesChanged()
|
||||
return true
|
||||
else
|
||||
APG.notify( false, 1, ply, failmsg or "There is something in this prop!" )
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_WORLD )
|
||||
|
||||
ent:CollisionRulesChanged()
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function APG.ConstraintApply( ent, callback )
|
||||
local constrained = constraint.GetAllConstrainedEntities(ent)
|
||||
for _,v in next, constrained do
|
||||
if IsValid( v ) and v ~= ent then
|
||||
callback( v )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[------------------------------------------
|
||||
Hooks/Timers
|
||||
]]--------------------------------------------
|
||||
|
||||
APG.hookAdd( mod, "APG_PostPhysgunPickup", "APG_makeGhost", function( ply, ent, canPickup )
|
||||
if not canPickup then return end
|
||||
if not APG.modules[ mod ] or not APG.isBadEnt( ent ) then return end
|
||||
|
||||
ent.APG_Picked = true
|
||||
|
||||
if not APG.cfg[ "allowPK" ].value then
|
||||
APG.entGhost( ent )
|
||||
APG.ConstraintApply( ent, function( _ent )
|
||||
if not _ent.APG_Frozen then
|
||||
_ent.APG_Picked = true
|
||||
APG.entGhost( _ent )
|
||||
end
|
||||
end) -- Apply ghost to all constrained ents
|
||||
end
|
||||
end)
|
||||
|
||||
APG.hookAdd( mod, "PlayerUnfrozeObject", "APG_unFreezeInteract", function (ply, ent, pObj)
|
||||
if not APG.canPhysGun( ent, ply, "APG_unFreezeInteract" ) then return end
|
||||
if not APG.modules[ mod ] or not APG.isBadEnt( ent ) then return end
|
||||
if APG.cfg[ "alwaysFrozen" ].value then
|
||||
return false
|
||||
end -- Do not unfreeze if Always Frozen is enabled !
|
||||
if ent:GetCollisionGroup( ) ~= COLLISION_GROUP_WORLD then
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_INTERACTIVE )
|
||||
end
|
||||
end)
|
||||
|
||||
APG.dJobRegister( "unghost", 0.1, 50, function( ent )
|
||||
if IsValid(ent) then
|
||||
APG.entUnGhost( ent )
|
||||
end
|
||||
end)
|
||||
|
||||
APG.hookAdd( mod, "PhysgunDrop", "APG_pGunDropUnghost", function( ply, ent )
|
||||
if not APG.modules[ mod ] or not APG.isBadEnt( ent ) then return end
|
||||
ent.APG_Picked = false
|
||||
|
||||
if APG.cfg[ "alwaysFrozen" ].value then
|
||||
APG.freezeIt( ent )
|
||||
end
|
||||
|
||||
APG.entUnGhost( ent, ply )
|
||||
|
||||
APG.ConstraintApply( ent, function( _ent )
|
||||
_ent.APG_Picked = false
|
||||
APG.startDJob( "unghost", _ent )
|
||||
end) -- Apply unghost to all constrained ents
|
||||
end)
|
||||
|
||||
local function SafeSetCollisionGroup( ent, colgroup, pObj )
|
||||
-- If the entity is being held by a player or is ghosted abort.
|
||||
if ent:IsPlayerHolding() then return end
|
||||
if ent.APG_Ghosted then return end
|
||||
|
||||
if pObj then pObj:Sleep() end
|
||||
ent:SetCollisionGroup(colgroup)
|
||||
ent:CollisionRulesChanged()
|
||||
end
|
||||
|
||||
APG.hookAdd( mod, "OnEntityCreated", "APG_noCollideOnCreate", function( ent )
|
||||
if not APG.modules[ mod ] or not APG.isBadEnt( ent ) then return end
|
||||
if not IsValid( ent ) then return end
|
||||
if ent:GetClass() == "gmod_hands" then return end -- Fix shadow glitch
|
||||
|
||||
ent.APG_spawnedCollisionGroup = ent:GetCollisionGroup() -- have to set it before it's ghosted
|
||||
|
||||
timer.Simple( 0, function()
|
||||
if not ent then return end
|
||||
if not ent:IsSolid() then return end -- Don't ghost ghosts.
|
||||
|
||||
local spawnedEnt = tostring(ent)
|
||||
local owner = ""
|
||||
if APG.getOwner(ent) ~= nil then -- incase getowner return's nil (like on reloading)
|
||||
owner = APG.getOwner(ent):Nick()
|
||||
else
|
||||
owner = "console"
|
||||
end
|
||||
|
||||
APG.entGhost( ent )
|
||||
end)
|
||||
|
||||
timer.Simple( 0, function()
|
||||
if not ent then return end
|
||||
if not ent:IsSolid() then return end -- Don't ghost ghosts.
|
||||
|
||||
local owner = APG.getOwner( ent )
|
||||
DropEntityIfHeld( ent )
|
||||
ent:ForcePlayerDrop()
|
||||
|
||||
if IsValid( owner ) and owner:IsPlayer() then
|
||||
local pObj = ent:GetPhysicsObject()
|
||||
if IsValid(pObj) then
|
||||
if APG.cfg[ "alwaysFrozen" ].value then
|
||||
ent.APG_Frozen = true
|
||||
pObj:EnableMotion( false )
|
||||
elseif pObj:IsMoveable() then
|
||||
ent.APG_Frozen = false
|
||||
SafeSetCollisionGroup( ent, COLLISION_GROUP_INTERACTIVE )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
APG.startDJob( "unghost", ent )
|
||||
end)
|
||||
end)
|
||||
|
||||
local BlockedProperties = { "collision", "persist", "editentity", "drive", "ignite", "statue" }
|
||||
APG.hookAdd( mod, "CanProperty", "APG_canProperty", function(ply, property, ent)
|
||||
local property = tostring( property )
|
||||
if ( table.HasValue(BlockedProperties, property) and ent.APG_Ghosted ) then
|
||||
APG.notify( false, 1, ply, "Cannot set", property, "properties on ghosted entities!" )
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
-- Custom Hooks --
|
||||
local function checkDoor(ply, ent)
|
||||
local isTrap = APG.isTrap(ent, true)
|
||||
|
||||
if isTrap and istable(isTrap) then
|
||||
ent.APG_Ghosted = true
|
||||
ent:SetCollisionGroup(COLLISION_GROUP_WORLD)
|
||||
|
||||
for _,v in next, isTrap do
|
||||
if v:IsPlayer() then
|
||||
local push = v:GetForward()
|
||||
push = push * 1200
|
||||
push.z = 60
|
||||
|
||||
v:SetVelocity(push)
|
||||
end
|
||||
end
|
||||
|
||||
timer.Simple(1, function()
|
||||
if IsValid(ply) and IsValid(ent) then
|
||||
ent.APG_Ghosted = false
|
||||
ent:oldFadeDeactivate()
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_INTERACTIVE )
|
||||
|
||||
if IsValid(isTrap) then
|
||||
APG.notify( false, 1, ply, "Unable to unstuck objects from fading door!" )
|
||||
APG.entGhost(ent)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
APG.hookAdd(mod, "APG.FadingDoorToggle", "APG_FadingDoor", function(ent, isFading)
|
||||
if APG.isBadEnt(ent) and APG.cfg["fadingDoorGhosting"].value then
|
||||
local ply = APG.getOwner( ent )
|
||||
|
||||
if (IsValid(ply) and ply:IsPlayer() and not isFading) then
|
||||
-- Delay slightly, this is needed to wait for other things happen before it works.
|
||||
timer.Simple(0.001, function()
|
||||
checkDoor(ply, ent)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Load hooks and timers ]]
|
||||
|
||||
APG.updateHooks(mod)
|
||||
APG.updateTimers(mod)
|
||||
1
addons/apg/lua/apg/modules/home.lua
Normal file
1
addons/apg/lua/apg/modules/home.lua
Normal file
@@ -0,0 +1 @@
|
||||
-- This is a dummy file, for APGs home page! (As the home page is built like a module.)
|
||||
184
addons/apg/lua/apg/modules/lag_detection.lua
Normal file
184
addons/apg/lua/apg/modules/lag_detection.lua
Normal file
@@ -0,0 +1,184 @@
|
||||
--[[------------------------------------------
|
||||
|
||||
============================
|
||||
LAG DETECTION MODULE
|
||||
============================
|
||||
|
||||
Developer informations :
|
||||
---------------------------------
|
||||
Used variables :
|
||||
lagTrigger = { value = 75, desc = "% difference between current lag and average lag."}
|
||||
lagsCount = { value = 8, desc = "Number of consectuives laggy frames in order to run a cleanup."}
|
||||
bigLag = { value = 2, desc = "Time (seconds) between 2 frames to trigger a cleanup"}
|
||||
lagFunc = { value = "cleanUp_unfrozen", desc = "Function ran on lag detected" }
|
||||
lagFuncTime = { value = 20, desc = "Time (seconds) between 2 cleanup (avoid spam)"}
|
||||
|
||||
Ready to hook :
|
||||
APG_lagDetected = Ran on lag detected by the server.
|
||||
Example : hook.Add( "APG_lagDetected", "myLagDetectHook", function() print("[APG] Lag detected (printed from my very own hook)") end)
|
||||
|
||||
]]--------------------------------------------
|
||||
local mod = "lag_detection"
|
||||
local table = table
|
||||
|
||||
--[[ Lag fixing functions ]]
|
||||
|
||||
local lagFix = {
|
||||
cleanup_all = function( ) APG.cleanUp( "all" ) end,
|
||||
cleanup_unfrozen = function( ) APG.cleanUp( "unfrozen" ) end,
|
||||
ghost_unfrozen = APG.ghostThemAll,
|
||||
freeze_unfrozen = APG.freezeProps,
|
||||
smart_cleanup = APG.smartCleanup,
|
||||
custom_function = APG.customFunc,
|
||||
}
|
||||
|
||||
--[[ Lag detection vars ]]
|
||||
|
||||
local lastTick = 0
|
||||
local tickDelta = 0
|
||||
local tickRate = 0
|
||||
|
||||
local lagCount = 0
|
||||
local lagThreshold = math.huge
|
||||
|
||||
local processHault = false
|
||||
local processFunc = false
|
||||
local processExecs = 0
|
||||
|
||||
local sampleData = {}
|
||||
local sampleCount = 0
|
||||
|
||||
local function addSample( data )
|
||||
local index = (sampleCount%66)+1
|
||||
local data = tonumber(data)
|
||||
|
||||
sampleCount = sampleCount + 1
|
||||
if sampleCount >= 66 then
|
||||
sampleCount = 0
|
||||
end
|
||||
|
||||
table.insert(sampleData, index, data)
|
||||
end
|
||||
|
||||
function APG.resetLag(dontResetData)
|
||||
lastTick = 0
|
||||
tickDelta = 0
|
||||
|
||||
lagCount = 0
|
||||
lagThreshold = tickRate
|
||||
|
||||
processHault = false
|
||||
processFunc = false
|
||||
processExecs = 0
|
||||
end
|
||||
|
||||
function APG.calculateLagAverage()
|
||||
local count = 0
|
||||
local total = 0
|
||||
local sampleData = sampleData
|
||||
|
||||
for _, v in next, sampleData do
|
||||
total = total + v
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
if count < 12 then
|
||||
return false -- Not enough data, yet.
|
||||
end
|
||||
|
||||
return (total/count)
|
||||
end
|
||||
|
||||
hook.Add("Think", "APG_initLagDetection", function()
|
||||
tickRate = FrameTime()
|
||||
lagThreshold = tickRate
|
||||
hook.Remove("Think", "APG_initLagDetection")
|
||||
end)
|
||||
|
||||
APG.timerAdd( mod, "APG_process", 3, 0, function()
|
||||
if not APG.modules[ mod ] then return end
|
||||
|
||||
if sampleCount < 12 or tickDelta < lagThreshold then
|
||||
addSample(tickDelta)
|
||||
end
|
||||
|
||||
local average = APG.calculateLagAverage()
|
||||
|
||||
if average then
|
||||
lagThreshold = average * ( 1 + ( APG.cfg[ "lagTrigger" ].value / 100 ) )
|
||||
end
|
||||
|
||||
processExecs = 0
|
||||
end)
|
||||
|
||||
APG.hookAdd( mod, "Tick", "APG_lagDetection", function()
|
||||
if not APG.modules[ mod ] then return end
|
||||
|
||||
local sysTime = SysTime()
|
||||
tickDelta = sysTime - lastTick
|
||||
|
||||
if lagThreshold > tickRate and tickDelta >= lagThreshold then
|
||||
|
||||
lagCount = lagCount + 1
|
||||
|
||||
if (lagCount >= APG.cfg[ "lagsCount" ].value) or ( tickDelta > APG.cfg[ "bigLag" ].value ) then
|
||||
|
||||
lagCount = 0
|
||||
|
||||
if ( not processHault ) and ( not processFunc ) then
|
||||
|
||||
processHault = true
|
||||
|
||||
timer.Simple(APG.cfg["lagFuncTime"].value, function()
|
||||
processHault = false
|
||||
end)
|
||||
|
||||
hook.Run( "APG_lagDetected" )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
lagCount = lagCount - 0.5
|
||||
if lagCount < 0 then
|
||||
lagCount = 0
|
||||
end
|
||||
end
|
||||
|
||||
lastTick = sysTime
|
||||
end)
|
||||
|
||||
--[[ Utils ]]
|
||||
|
||||
hook.Remove( "APG_lagDetected", "main") -- Sometimes, I dream about cheese.
|
||||
hook.Add( "APG_lagDetected", "main", function()
|
||||
if not APG then return end
|
||||
|
||||
APG.notify( true, 2, APG.cfg["notifyLevel"].value, "!WARNING LAG DETECTED!" )
|
||||
|
||||
local funcName = APG.cfg[ "lagFunc" ].value
|
||||
local func = lagFix[ funcName ]
|
||||
|
||||
if not func then return end
|
||||
|
||||
hook.Run("APG_logsLagDetected") -- put it here so it doesn't spam
|
||||
|
||||
processFunc = true
|
||||
|
||||
func(false, function()
|
||||
processFunc = false
|
||||
processExecs = processExecs + 1
|
||||
end)
|
||||
|
||||
if processExecs > 3 then
|
||||
-- If the lag cleanup process runs more then 3 times in 3 seconds, then
|
||||
-- reset our data.
|
||||
APG.resetLag()
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Load hooks and timers ]]
|
||||
|
||||
APG.updateHooks(mod)
|
||||
APG.updateTimers(mod)
|
||||
64
addons/apg/lua/apg/modules/logs.lua
Normal file
64
addons/apg/lua/apg/modules/logs.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
local mod = "logs"
|
||||
|
||||
if GAS then
|
||||
if APG.modules[ mod ] and GAS.Logging then
|
||||
local MODULE = GAS.Logging:MODULE()
|
||||
|
||||
MODULE.Category = "APG"
|
||||
MODULE.Name = "Lag Detection"
|
||||
MODULE.Colour = Color(255,0,0)
|
||||
|
||||
MODULE:Setup(function()
|
||||
|
||||
MODULE:Hook("APG_logsLagDetected", "APG.logs.lagDetected", function()
|
||||
|
||||
if not APG.cfg["logLagDetected"].value then return end
|
||||
|
||||
MODULE:Log("Lag detected, running lag function {1} to prevent further lag.", GAS.Logging:Highlight(APG.cfg["lagFunc"].value))
|
||||
|
||||
end)
|
||||
|
||||
end)
|
||||
|
||||
GAS.Logging:AddModule(MODULE)
|
||||
|
||||
local MODULE = GAS.Logging:MODULE()
|
||||
|
||||
MODULE.Category = "APG"
|
||||
MODULE.Name = "Crash Attempts"
|
||||
MODULE.Colour = Color(255,0,0)
|
||||
|
||||
MODULE:Setup(function()
|
||||
|
||||
MODULE:Hook("APG_stackCrashAttempt", "APG.logs.stackCrashAttempt", function(ply, count)
|
||||
|
||||
if not APG.cfg["logStackCrashAttempt"].value then return end
|
||||
MODULE:Log("{1} stacked {2} props and triggered a detection.", GAS.Logging:FormatPlayer(ply), GAS.Logging:Highlight(count))
|
||||
|
||||
end)
|
||||
|
||||
end)
|
||||
|
||||
GAS.Logging:AddModule(MODULE)
|
||||
end
|
||||
end
|
||||
|
||||
if plogs then
|
||||
if APG.modules[ mod ] and plogs.Register then
|
||||
plogs.Register("APG", true, Color(255,100,0))
|
||||
|
||||
plogs.AddHook("APG_logsLagDetected", function()
|
||||
if not APG.cfg["logLagDetected"].value then return end
|
||||
plogs.PlayerLog("console", "APG", "Lag detected, running lag fix function " .. APG.cfg["lagFunc"].value .. " to prevent further lag.")
|
||||
end)
|
||||
|
||||
|
||||
plogs.AddHook("APG_stackCrashAttempt", function(ply, count)
|
||||
if not APG.cfg["logStackCrashAttempt"].value then return end
|
||||
plogs.PlayerLog(ply, "APG", ply:NameID() .. " stacked " .. count .. " props and triggered a detection.", {
|
||||
["Name"] = ply:Name(),
|
||||
["SteamID"] = ply:SteamID()
|
||||
})
|
||||
end)
|
||||
end
|
||||
end
|
||||
261
addons/apg/lua/apg/modules/misc.lua
Normal file
261
addons/apg/lua/apg/modules/misc.lua
Normal file
@@ -0,0 +1,261 @@
|
||||
--[[------------------------------------------
|
||||
|
||||
============================
|
||||
MISCELLANEOUS MODULE
|
||||
============================
|
||||
|
||||
Developer informations :
|
||||
---------------------------------
|
||||
Used variables :
|
||||
vehDamage = { value = true, desc = "True to enable vehicles damages, false to disable." }
|
||||
vehNoCollide = { value = false, desc = "True to disable collisions between vehicles and players"}
|
||||
autoFreeze = { value = false, desc = "Freeze every unfrozen prop each X seconds" }
|
||||
autoFreezeTime = { value = 120, desc = "Auto freeze timer (seconds)"}
|
||||
|
||||
]]--------------------------------------------
|
||||
local mod = "misc"
|
||||
|
||||
--[[ Helper functions ]]
|
||||
local timerSimple = timer.Simple
|
||||
|
||||
local function isVehDamage( dmg, atk, ent )
|
||||
if not IsValid( ent ) then return false end
|
||||
if dmg:GetDamageType() == DMG_VEHICLE or APG.IsVehicle( atk ) or APG.IsVehicle( ent ) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function getPhys(ent)
|
||||
local phys = ent.GetPhysicsObject and ent:GetPhysicsObject() or false
|
||||
return ( phys and IsValid(phys) ) and phys or false
|
||||
end
|
||||
|
||||
local function wait(callback)
|
||||
timerSimple(0.003, callback)
|
||||
end
|
||||
|
||||
--[[ No Collide vehicles on spawn ]]
|
||||
APG.hookAdd( mod,"OnEntityCreated", "APG_noCollideVeh", function( ent )
|
||||
timerSimple(0.03, function()
|
||||
if APG.cfg[ "vehNoCollide" ].value and APG.IsVehicle( ent ) then
|
||||
ent:SetCollisionGroup( COLLISION_GROUP_WEAPON )
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
--[[ Disable prop damage ]]
|
||||
APG.hookAdd( mod, "EntityTakeDamage","APG_noPropDmg", function( target, dmg )
|
||||
if ( not APG.cfg[ "allowPK" ].value ) then -- Check if prop kill is allowed, before checking anything else.
|
||||
local atk, ent = dmg:GetAttacker(), dmg:GetInflictor()
|
||||
if APG.isBadEnt( ent ) or dmg:GetDamageType() == DMG_CRUSH or ( APG.cfg[ "vehDamage" ].value and isVehDamage( dmg, atk, ent ) ) then
|
||||
dmg:SetDamage(0)
|
||||
return true
|
||||
-- ^ Returning true overrides and blocks all related damage, it also prevents the hook from running any further preventing unintentional damage from other addons.
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Remove Invalid Physics ]]
|
||||
APG.hookAdd( mod, "OnEntityCreated", "APG_removeInvalidPhysics", function( ent )
|
||||
if ( not APG.cfg[ "removeInvalidPhysics" ].value ) then return end
|
||||
|
||||
timerSimple(0, function()
|
||||
if not IsValid( ent ) then return end
|
||||
|
||||
local model = ent:GetModel()
|
||||
local owner = APG.getOwner( ent )
|
||||
local phys = ent:GetPhysicsObject()
|
||||
|
||||
if IsValid( owner ) and owner:IsPlayer() then
|
||||
if ( not model ) or string.lower(string.sub(model, 1, 6)) ~= "models" then
|
||||
SafeRemoveEntity( ent )
|
||||
return
|
||||
end
|
||||
if ( not IsValid( physObj ) ) and ( not APG.cfg["invalidPhysicsWhitelist"].value[model] ) then
|
||||
SafeRemoveEntity( ent )
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
--[[ Block Physgun Reload ]]
|
||||
APG.hookAdd( mod, "OnPhysgunReload", "APG_blockPhysgunReload", function( _, ply )
|
||||
if APG.cfg[ "blockPhysgunReload" ].value then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Block Gravitygun Throwing ]]
|
||||
APG.hookAdd( mod, "GravGunOnDropped", "APG_blockGravGunThrow", function(ply, ent)
|
||||
if ( not APG.cfg["blockGravGunThrow"].value ) then return end
|
||||
APG.killVelocity(ent, false, false, true)
|
||||
end)
|
||||
|
||||
--[[ Auto prop freeze ]]
|
||||
APG.timerAdd( mod, "APG_autoFreeze", APG.cfg[ "autoFreezeTime" ].value, 0, function()
|
||||
if APG.cfg[ "autoFreeze" ].value then
|
||||
APG.freezeProps()
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Fading door management ]]
|
||||
APG.hookAdd(mod, "CanTool", "APG_fadingDoorTool", function(ply, tr, tool)
|
||||
if IsValid(tr.Entity) and tr.Entity.APG_Ghosted then
|
||||
APG.notify(false, 1, ply, "Cannot use tool on ghosted entity!")
|
||||
return false
|
||||
end
|
||||
|
||||
if APG.cfg["fadingDoorHook"].value and tool == "fading_door" then
|
||||
timerSimple(0, function()
|
||||
if IsValid(tr.Entity) and not tr.Entity:IsPlayer() then
|
||||
local ent = tr.Entity
|
||||
|
||||
|
||||
|
||||
if not IsValid(ent) then return end
|
||||
if not ent.isFadingDoor then return end
|
||||
|
||||
local state = ent.fadeActive
|
||||
|
||||
if state then
|
||||
ent:fadeDeactivate()
|
||||
end
|
||||
|
||||
ent.oldFadeActivate = ent.oldFadeActivate or ent.fadeActivate
|
||||
ent.oldFadeDeactivate = ent.oldFadeDeactivate or ent.fadeDeactivate
|
||||
|
||||
function ent:fadeActivate()
|
||||
if hook.Run("APG.FadingDoorToggle", self, true, ply) then return end
|
||||
ent:oldFadeActivate()
|
||||
end
|
||||
|
||||
function ent:fadeDeactivate()
|
||||
if hook.Run("APG.FadingDoorToggle", self, false, ply) then return end
|
||||
ent:oldFadeDeactivate()
|
||||
ent:SetCollisionGroup(COLLISION_GROUP_INTERACTIVE)
|
||||
end
|
||||
|
||||
if state then
|
||||
ent:fadeActivate()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
APG.hookAdd(mod, "APG.FadingDoorToggle", "init", function(ent, state, ply)
|
||||
if ent.APG_Ghosted then
|
||||
APG.entUnGhost(ent, ply, "Your fading door is ghosted! (" .. ( ent.GetModel and ent:GetModel() or "???" ) .. ")")
|
||||
return true
|
||||
end
|
||||
|
||||
ent:ForcePlayerDrop()
|
||||
|
||||
local phys = getPhys(ent)
|
||||
if phys then
|
||||
phys:EnableMotion(false)
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Set sv_turbophysics? ]]--
|
||||
if APG.cfg[ "touchServerSettings" ].value then
|
||||
if APG.cfg[ "setTurboPhysics" ].value then
|
||||
RunConsoleCommand('sv_turbophysics', '1')
|
||||
else
|
||||
RunConsoleCommand('sv_turbophysics', '0')
|
||||
end
|
||||
end
|
||||
|
||||
--[[ FRZR9K ]]--
|
||||
|
||||
local zero = Vector(0,0,0)
|
||||
|
||||
local function sleepyPhys(phys)
|
||||
if not phys:IsAsleep() then
|
||||
local vel = phys:GetVelocity()
|
||||
if vel:Distance(zero) <= 23 then
|
||||
phys:Sleep()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
APG.timerAdd(mod, "frzr9k-p1", 5, 0, function(ent)
|
||||
if APG.cfg["sleepyPhys"].value then
|
||||
for _, v in next, ents.GetAll() do
|
||||
if v.APG_Frozen == false then
|
||||
local phys = getPhys( v )
|
||||
if APG.isBadEnt( v ) and phys then
|
||||
sleepyPhys( phys )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Collision Monitoring --
|
||||
local function collcall(ent, data)
|
||||
local hit = data.HitObject
|
||||
local mep = data.PhysObject
|
||||
|
||||
if IsValid(ent) and IsValid(hit) and IsValid(mep) then
|
||||
ent["frzr9k"] = ent["frzr9k"] or {}
|
||||
|
||||
local obj = ent["frzr9k"]
|
||||
|
||||
obj.Collisions = (obj.Collisions or 0) + 1
|
||||
|
||||
obj.CollisionTime = obj.CollisionTime or (CurTime() + 5)
|
||||
obj.LastCollision = CurTime()
|
||||
|
||||
if obj.Collisions > 23 then
|
||||
obj.Collisions = 0
|
||||
for _,e in next, {mep, hit} do
|
||||
e:SetVelocityInstantaneous(Vector(0,0,0))
|
||||
e:Sleep()
|
||||
end
|
||||
end
|
||||
|
||||
if obj.CollisionTime < obj.LastCollision then
|
||||
local subtract = 1
|
||||
local mem = obj.CollisionTime
|
||||
|
||||
while true do
|
||||
mem = mem + 5
|
||||
subtract = subtract + 1
|
||||
if mem >= obj.LastCollision then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
obj.Collisions = (obj.Collisions - subtract)
|
||||
obj.Collisions = (obj.Collisions > 1) and obj.Collisions or 1
|
||||
|
||||
obj.CollisionTime = (CurTime() + 5)
|
||||
end
|
||||
|
||||
ent["frzr9k"] = obj
|
||||
end
|
||||
end
|
||||
|
||||
APG.hookAdd(mod, "OnEntityCreated", "frzr9k-p2", function(ent)
|
||||
if APG.cfg["sleepyPhys"].value and APG.cfg["sleepyPhysHook"].value then
|
||||
wait(function()
|
||||
if APG.isBadEnt( ent ) and getPhys( ent ) then
|
||||
ent:AddCallback("PhysicsCollide", collcall)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
if APG.cfg[ "physGunMaxRange" ].value then
|
||||
RunConsoleCommand("physgun_maxrange", APG.cfg["physGunMaxRange"].value) -- Can't run SetInt on a convar that wasn't made in lua
|
||||
end
|
||||
|
||||
--[[------------------------------------------
|
||||
Load hooks and timers
|
||||
]]--------------------------------------------
|
||||
|
||||
APG.updateHooks(mod)
|
||||
APG.updateTimers(mod)
|
||||
95
addons/apg/lua/apg/modules/notification.lua
Normal file
95
addons/apg/lua/apg/modules/notification.lua
Normal file
@@ -0,0 +1,95 @@
|
||||
--[[------------------------------------------
|
||||
|
||||
============================
|
||||
NOTIFICATION MODULE
|
||||
============================
|
||||
|
||||
]]--------------------------------------------
|
||||
|
||||
local mod = "notification"
|
||||
|
||||
function APG.notify(log, level, target, ... ) -- The most advanced notification function in the world.
|
||||
|
||||
local log = log -- Should the message be logged?
|
||||
local level = level -- The level of the error.
|
||||
local target = target -- Whos ist this message meant for?
|
||||
local msg = {...} -- Pack the arguments in a table.
|
||||
|
||||
local outMsg = "" -- Concat the message into this variable.
|
||||
local isConsole = ( target == "console" ) -- Is this message meant for the console
|
||||
|
||||
if type(level) == "string" then
|
||||
level = string.lower( level )
|
||||
level = ( level == "notice" and 0 ) or ( level == "warning" and 1 ) or ( level == "alert" and 2 )
|
||||
end
|
||||
|
||||
if target then
|
||||
if type(target) == "string" then
|
||||
(({
|
||||
["all"] = function()
|
||||
target = player.GetHumans()
|
||||
end,
|
||||
["admin"] = function()
|
||||
local data = player.GetHumans()
|
||||
for k, v in next, data do
|
||||
if not v:IsAdmin() then
|
||||
data[k] = nil
|
||||
end
|
||||
end
|
||||
target = data
|
||||
end,
|
||||
["superadmin"] = function()
|
||||
local data = player.GetHumans()
|
||||
for k, v in next, data do
|
||||
if not v:IsSuperAdmin() then
|
||||
data[k] = nil
|
||||
end
|
||||
end
|
||||
target = data
|
||||
end,
|
||||
["console"] = function()
|
||||
-- Just send it to the logs without actually giving out a message.
|
||||
end,
|
||||
})[target])()
|
||||
else
|
||||
if IsEntity( target ) and IsValid( target ) and target:IsPlayer() then
|
||||
target = { target }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local outMsg = ""
|
||||
|
||||
for _, v in next, msg do
|
||||
local data = v and tostring(v) or ""
|
||||
if string.len( outMsg ) == 0 then
|
||||
outMsg = data
|
||||
else
|
||||
outMsg = outMsg .. " " .. data
|
||||
end
|
||||
end
|
||||
|
||||
outMsg = string.Trim( outMsg )
|
||||
|
||||
if string.len( outMsg ) > 0 and ( log or isConsole ) then
|
||||
ServerLog("[APG] " .. outMsg .. "\n")
|
||||
if isConsole then
|
||||
MsgC( Color( 72, 216, 41 ), "[APG]", Color( 255, 255, 255 ), outMsg)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if type(target) ~= "table" then return false end
|
||||
|
||||
for _, v in next, target do
|
||||
if not isentity(v) then continue end
|
||||
if not IsValid(v) then continue end
|
||||
net.Start("apg_notice_s2c")
|
||||
net.WriteUInt(level, 3)
|
||||
net.WriteString(outMsg)
|
||||
net.Send(v)
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
112
addons/apg/lua/apg/modules/stack_detection.lua
Normal file
112
addons/apg/lua/apg/modules/stack_detection.lua
Normal file
@@ -0,0 +1,112 @@
|
||||
--[[------------------------------------------
|
||||
|
||||
============================
|
||||
STACK DETECTION MODULE
|
||||
============================
|
||||
|
||||
Developer informations :
|
||||
---------------------------------
|
||||
Used variables :
|
||||
stackMax = { value = 15, desc = "Max amount of entities stacked on a small area"}
|
||||
stackArea = { value = 15, desc = "Sphere radius for stack detection (gmod units)"}
|
||||
fading
|
||||
|
||||
]]--------------------------------------------
|
||||
local mod = "stack_detection"
|
||||
local SafeRemoveEntity = SafeRemoveEntity
|
||||
|
||||
function APG.checkStack( ent, pcount )
|
||||
if not APG.isBadEnt( ent ) then return end
|
||||
|
||||
local efound = ents.FindInSphere(ent:GetPos(), APG.cfg["stackArea"].value )
|
||||
local count = 0
|
||||
local max_count = APG.cfg["stackMax"].value
|
||||
for k, v in pairs (efound) do
|
||||
if APG.isBadEnt( v ) and APG.getOwner( v ) then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
if count >= (pcount or max_count) then
|
||||
local owner, _ = ent:CPPIGetOwner()
|
||||
ent:Remove()
|
||||
if not owner.APG_CantPickup then
|
||||
APG.blockPickup( owner, 10 )
|
||||
|
||||
APG.notify( false, 2, owner, "You tried to unfreeze a stack of " .. count .. " props! >:(" )
|
||||
hook.Run("APG_stackCrashAttempt", owner, count)
|
||||
APG.notify( true, 2, APG.cfg["notifyLevel"].value, owner:Nick() .. " [" .. owner:SteamID() .. "]" .. " tried to unfreeze a stack of " .. count .. " props!" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
APG.hookAdd(mod, "PhysgunPickup", "APG_stackCheck",function(ply, ent)
|
||||
if not APG.canPhysGun( ent, ply, "APG_stackCheck" ) then return end
|
||||
if not APG.modules[ mod ] or not APG.isBadEnt( ent ) then return end
|
||||
APG.checkStack( ent )
|
||||
end)
|
||||
|
||||
-- Requires Fading Door Hooks --
|
||||
local notify = false
|
||||
local curTime = 0
|
||||
local lastCall = 0
|
||||
|
||||
APG.hookAdd(mod, "APG.FadingDoorToggle", "APG_fadingDoorStackCheck", function(ent, faded)
|
||||
curTime = CurTime()
|
||||
|
||||
if IsValid(ent) and faded then
|
||||
local ply = APG.getOwner(ent)
|
||||
local pos = ent:GetPos()
|
||||
local doors = {}
|
||||
local count = 1 -- Start at 1 to include the original fading door
|
||||
|
||||
for _,v in next, ents.FindInSphere(pos, APG.cfg["stackArea"].value) do
|
||||
if v ~= ent and IsValid(v) and v.isFadingDoor and APG.getOwner(v) == ply then
|
||||
table.insert(doors, v)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
if count >= APG.cfg["fadingDoorStackMax"].value then
|
||||
notify = true
|
||||
for _,v in next, doors do
|
||||
SafeRemoveEntity(v)
|
||||
end
|
||||
end
|
||||
|
||||
if curTime > lastCall then
|
||||
if notify and APG.cfg["fadingDoorStackNotify"].value then
|
||||
notify = false
|
||||
APG.notification(ply:Nick() .. " had a stack of " .. count .. " fading doors that were removed.", APG.cfg["notifyLevel"].value, 2)
|
||||
APG.notification("Some of your fading doors were removed.", ply)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
lastCall = curTime + 0.001
|
||||
end)
|
||||
|
||||
--[[ Stacker Exploit Quick Fix ]]
|
||||
hook.Add( "InitPostEntity", "APG_InitStackFix", function()
|
||||
timer.Simple(60, function()
|
||||
local TOOL = weapons.GetStored("gmod_tool")["Tool"][ "stacker" ] or weapons.GetStored("gmod_tool")["Tool"][ "stacker_v2" ]
|
||||
if not TOOL then return end
|
||||
|
||||
-- Stacker improved (beta) fixed this by setting a maximum number of constraints
|
||||
-- See : https://git.io/vPvJK
|
||||
|
||||
APG.dJobRegister( "weld", 0.3, 20, function( sents )
|
||||
if not IsValid( sents[1] ) or not IsValid( sents[2]) then return end
|
||||
constraint.Weld( sents[1], sents[2], 0, 0, 0 )
|
||||
end)
|
||||
|
||||
function TOOL:ApplyWeld( lastEnt, newEnt )
|
||||
if ( not self:ShouldForceWeld() and not self:ShouldApplyWeld() ) then return end
|
||||
APG.startDJob( "weld", {lastEnt, newEnt} )
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
--[[ Load hooks and timers ]]
|
||||
|
||||
APG.updateHooks(mod)
|
||||
APG.updateTimers(mod)
|
||||
128
addons/apg/lua/apg/modules/tools.lua
Normal file
128
addons/apg/lua/apg/modules/tools.lua
Normal file
@@ -0,0 +1,128 @@
|
||||
--[[------------------------------------------
|
||||
|
||||
============================
|
||||
TOOLS MODULE
|
||||
============================
|
||||
|
||||
Developer informations :
|
||||
---------------------------------
|
||||
Used variables :
|
||||
|
||||
]]--------------------------------------------
|
||||
local mod = "tools"
|
||||
|
||||
function APG.canTool( ply, tool, ent )
|
||||
if IsValid(ent) then
|
||||
if ent.ToolDisabled == false then
|
||||
return false
|
||||
end
|
||||
|
||||
if ent.CPPICanTool then
|
||||
return ent:CPPICanTool(ply, tool)
|
||||
end -- Let CPPI handle things from here.
|
||||
end
|
||||
|
||||
if APG.cfg[ "checkCanTool" ].value and ply.APG_CantPickup == true then-- If we can't pickup we can't tool either.
|
||||
return false
|
||||
end
|
||||
|
||||
return 0 -- return 0 so if all of the check's don't return anything then it doesn't default to disabling toolgun.
|
||||
end
|
||||
|
||||
--[[ APG CanTool Check ]]
|
||||
|
||||
APG.hookAdd(mod, "CanTool", "APG_ToolMain", function(ply, tr, tool)
|
||||
if not APG.canTool(ply, tool, tr.Entity) then
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Tool Spam Control ]]
|
||||
|
||||
APG.hookAdd(mod, "CanTool", "APG_ToolSpamControl", function(ply)
|
||||
if not APG.cfg[ "blockToolSpam" ].value then return end
|
||||
|
||||
ply.APG_ToolCTRL = ply.APG_ToolCTRL or {}
|
||||
|
||||
local ply = ply
|
||||
local data = ply.APG_ToolCTRL
|
||||
local delay = 0
|
||||
local diff = 0
|
||||
|
||||
data.curTime = CurTime()
|
||||
data.toolDelay = data.toolDelay or 0
|
||||
data.toolUseTimes = data.toolUseTimes or 0
|
||||
|
||||
diff = data.curTime - data.toolDelay
|
||||
delay = APG.cfg[ "blockToolDelay" ].value
|
||||
|
||||
if data.toolUseTimes <= 0 or diff > delay then
|
||||
data.toolUseTimes = 0
|
||||
data.toolDelay = 0
|
||||
data.wasNotified = false
|
||||
end
|
||||
|
||||
if diff > 0 then
|
||||
data.toolUseTimes = math.max( data.toolUseTimes - 1, 0 )
|
||||
else
|
||||
data.toolUseTimes = math.min( data.toolUseTimes + 1, APG.cfg[ "blockToolRate" ].value )
|
||||
if data.toolUseTimes >= APG.cfg[ "blockToolRate" ].value then
|
||||
data.toolDelay = data.curTime + delay
|
||||
if not data.wasNotified then
|
||||
data.wasNotified = true
|
||||
APG.notify( false, 1, ply, "You are using the toolgun too fast, slow down!" )
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if data.toolDelay == 0 then
|
||||
data.toolDelay = data.curTime + delay
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Block Tool World ]]
|
||||
|
||||
APG.hookAdd(mod, "CanTool", "APG_ToolWorldControl", function(ply, tr)
|
||||
if not APG.cfg[ "blockToolWorld" ].value then return end
|
||||
if tr.HitWorld then
|
||||
if not timer.Exists("APG-" .. ply:UniqueID() .. "-Notify") then
|
||||
APG.notify( false, 1, ply, "You may not use the toolgun on the world." )
|
||||
timer.Create("APG-" .. ply:UniqueID() .. "-Notify", 5, 1, function() end)
|
||||
end
|
||||
return false
|
||||
end
|
||||
end)
|
||||
|
||||
--[[ Block Tool Unfreeze ]]
|
||||
|
||||
APG.hookAdd(mod, "CanTool", "APG_ToolUnfreezeControl", function(ply, tr)
|
||||
if not APG.cfg[ "blockToolUnfreeze" ].value then return end
|
||||
timer.Simple(0.003, function()
|
||||
local ent = tr.Entity
|
||||
local phys = NULL
|
||||
|
||||
if IsValid(ent) then
|
||||
phys = ent:GetPhysicsObject()
|
||||
if IsValid(phys) and phys:IsMotionEnabled() then
|
||||
phys:EnableMotion( false )
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
if APG.cfg[ "touchServerSettings" ].value then
|
||||
local conVar = GetConVar("toolmode_allow_creator")
|
||||
if conVar then
|
||||
if APG.cfg[ "blockCreatorTool" ].value then
|
||||
conVar:SetBool(false)
|
||||
else
|
||||
conVar:SetBool(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Load hooks and timers ]]
|
||||
|
||||
APG.updateHooks(mod)
|
||||
APG.updateTimers(mod)
|
||||
378
addons/apg/lua/apg/sh_config.lua
Normal file
378
addons/apg/lua/apg/sh_config.lua
Normal file
@@ -0,0 +1,378 @@
|
||||
--[[------------------------------------------
|
||||
====================================================================================
|
||||
/!\ READ ME /!\ /!\ READ ME /!\ /!\ READ ME /!\
|
||||
====================================================================================
|
||||
|
||||
This file is the default config file.
|
||||
If you want to configure APG to fit your server needs, you can edit the config
|
||||
ingame using the chat command : !apg or apg in console.
|
||||
|
||||
]]--------------------------------------------
|
||||
APG.cfg = APG.cfg or {}
|
||||
APG.modules = APG.modules or {}
|
||||
|
||||
--[[----------
|
||||
Your very own custom function
|
||||
This function will run whenever lag is detected on your server!
|
||||
]]------------
|
||||
function APG.customFunc( notification )
|
||||
-- Do something
|
||||
end
|
||||
|
||||
--[[----------
|
||||
Avalaible premade functions - THIS IS INFORMATIVE PURPOSE ONLY !
|
||||
]]------------
|
||||
if CLIENT then
|
||||
APG_lagFuncs = { -- THIS IS INFORMATIVE PURPOSE ONLY !
|
||||
"cleanup_all", -- Cleanup every props/ents protected by APG (not worldprops nor vehicles)
|
||||
"cleanup_unfrozen", -- Cleanup only unfrozen stuff
|
||||
"ghost_unfrozen", -- Ghost unfrozen stuff
|
||||
"freeze_unfrozen", -- Freeze unfrozen stuff
|
||||
"smart_cleanup", -- Cleanup unfrozen fading doors, freeze unfrozens, remove large stacks
|
||||
"custom_function" -- Your custom function (see APG.customFunc)
|
||||
} -- THIS IS INFORMATIVE PURPOSE ONLY !
|
||||
|
||||
APG_notifyLevels = {
|
||||
"everyone",
|
||||
"admin",
|
||||
"superadmin"
|
||||
}
|
||||
end
|
||||
|
||||
--[[------------------------------------------
|
||||
DEFAULT SETTINGS -- You CAN edit this part, but you SHOULDN'T
|
||||
]]--------------------------------------------
|
||||
|
||||
local defaultSettings = {}
|
||||
defaultSettings.modules = { -- Set to true to enable and false to disable module.
|
||||
["home"] = true, -- Essential, do not disable.
|
||||
["ghosting"] = true,
|
||||
["stack_detection"] = true,
|
||||
["lag_detection"] = true,
|
||||
["notification"] = true,
|
||||
["canphysgun"] = true, -- Essential, do not disable.
|
||||
["misc"] = true,
|
||||
["tools"] = true,
|
||||
["logs"] = true,
|
||||
}
|
||||
|
||||
defaultSettings.cfg = {
|
||||
--[[----------
|
||||
Ghosting module
|
||||
]]------------
|
||||
ghostColor = {
|
||||
value = Color(34, 34, 34, 220),
|
||||
desc = "Color set on ghosted props"
|
||||
},
|
||||
|
||||
ghostColorToggle = {
|
||||
value = true,
|
||||
desc = "Toggle ghosting color."
|
||||
},
|
||||
|
||||
badEnts = {
|
||||
value = {
|
||||
["prop_physics"] = true,
|
||||
["wire_"] = false,
|
||||
["gmod_"] = false,
|
||||
["keypad"] = false,
|
||||
},
|
||||
desc = "Entities to ghost/control/secure (true if exact name, false if it is a pattern"
|
||||
},
|
||||
|
||||
unGhostingWhitelist = {
|
||||
value = {
|
||||
["zmlab_"] = false,
|
||||
},
|
||||
desc = "Entities that should be set back to their original (spawned) collision group when frozen/dropped."
|
||||
},
|
||||
|
||||
removeInvalidPhysics = {
|
||||
value = false,
|
||||
desc = "Should we attempt to detect and remove invalid physics objects? (Entities with bad or no physics objects/Physics objects without models.)"
|
||||
},
|
||||
|
||||
invalidPhysicsWhitelist = {
|
||||
value = {},
|
||||
desc = "Entities that shouldn't be removed if they don't have proper physics"
|
||||
},
|
||||
|
||||
alwaysFrozen = {
|
||||
value = true,
|
||||
desc = "Set to true to auto freeze props on physgun drop (aka APG_FreezeOnDrop)"
|
||||
},
|
||||
|
||||
--[[----------
|
||||
Stack detection module
|
||||
]]------------
|
||||
stackMax = {
|
||||
value = 15,
|
||||
desc = "Max amount of entities stacked in a small area"
|
||||
},
|
||||
|
||||
stackArea = {
|
||||
value = 15,
|
||||
desc = "Sphere radius for stack detection (gmod units)"
|
||||
},
|
||||
fadingDoorStackMax = {
|
||||
value = 5,
|
||||
desc = "Maximum amount of fading doors that can be stacked in stackArea."
|
||||
},
|
||||
fadingDoorStackNotify = {
|
||||
value = false,
|
||||
desc = "Notify the players when their fading doors were removed"
|
||||
},
|
||||
|
||||
--[[----------
|
||||
Lag detection module
|
||||
]]------------
|
||||
lagTrigger = {
|
||||
value = 75,
|
||||
desc = "[Default: 75%] Differential threshold between current lag and average lag."
|
||||
},
|
||||
|
||||
lagsCount = {
|
||||
value = 8,
|
||||
desc = "Number of consectuives laggy frames in order to run a cleanup."
|
||||
},
|
||||
|
||||
bigLag = {
|
||||
value = 2,
|
||||
desc = "Maximum time (seconds) between 2 frames to trigger a cleanup"
|
||||
},
|
||||
|
||||
lagFunc = {
|
||||
value = "ghost_unfrozen",
|
||||
desc = "Function ran on lag detected, see APG_lagFuncs."
|
||||
},
|
||||
|
||||
lagFuncTime = {
|
||||
value = 8,
|
||||
desc = "Time (seconds) between 2 anti lag function (avoid spam)"
|
||||
},
|
||||
|
||||
|
||||
|
||||
--[[ Notifications ]] --
|
||||
|
||||
notifySounds = {
|
||||
value = false, -- Might make it where certain ones run sound
|
||||
desc = "When notifications run do you want the sounds to play?"
|
||||
},
|
||||
|
||||
notifyLevel = {
|
||||
value = "admin",
|
||||
desc = "Notification levels, refer to APG_notifyLevels"
|
||||
},
|
||||
|
||||
notifyLagFunc = {
|
||||
value = false,
|
||||
desc = "Do you want the notifyLevel to see the lagFunc that ran? (refer to APG_lagFuncs)"
|
||||
},
|
||||
|
||||
-- TODO: Make a ULX/ULIB module
|
||||
-- notifyULibInheritance = {
|
||||
-- value = true,
|
||||
-- desc = "Do you want to use inheritance for notifyRanks? (only works with ULIB/ULX)"
|
||||
-- },
|
||||
|
||||
-- notifyRanks = {
|
||||
-- value = { "trialmod", "moderator", "admin", "superadmin", "owner" },
|
||||
-- desc = "The ranks that you want to see the notification" -- If you have notifyULibInheritance you only need to do the lowest rank(s)
|
||||
-- },
|
||||
|
||||
--[[ Override Server Settings? ]]
|
||||
touchServerSettings = {
|
||||
value = false,
|
||||
desc = "Should we override Server Settings? (Used for setting ConVars)"
|
||||
},
|
||||
|
||||
--[[ Vehicles ]]--
|
||||
|
||||
vehDamage = {
|
||||
value = false,
|
||||
desc = "True to disable vehicles damages, false to enable."
|
||||
},
|
||||
|
||||
vehNoCollide = {
|
||||
value = false,
|
||||
desc = "True to disable collisions between vehicles and players"
|
||||
},
|
||||
|
||||
vehIncludeWAC = {
|
||||
value = true,
|
||||
desc = "Check for WAC vehicles."
|
||||
},
|
||||
|
||||
vehAntiGhost = {
|
||||
value = false,
|
||||
desc = "Toggle vehicle ghosting"
|
||||
},
|
||||
|
||||
blockGravGunThrow = {
|
||||
value = true,
|
||||
desc = "Toggle GravGun throwing."
|
||||
},
|
||||
|
||||
setTurboPhysics = {
|
||||
value = false,
|
||||
desc = "Toggle sv_turbophysics."
|
||||
},
|
||||
|
||||
--[[ Tool Control ]]--
|
||||
|
||||
checkCanTool = {
|
||||
value = true,
|
||||
desc = "Should tools be blocked on APG_CantPickup?"
|
||||
},
|
||||
|
||||
blockToolSpam = {
|
||||
value = true,
|
||||
desc = "Block players from spamming the toolgun."
|
||||
},
|
||||
|
||||
blockToolRate = {
|
||||
value = 5,
|
||||
desc = "How fast can we use the toolgun before it gets blocked? (Clicks per second(s))"
|
||||
},
|
||||
|
||||
blockToolDelay = {
|
||||
value = 1,
|
||||
desc = "How many seconds should we wait after we were stopped? (The aforementioned second(s))"
|
||||
},
|
||||
|
||||
blockToolWorld = {
|
||||
value = false,
|
||||
desc = "Prevent using the toolgun on the world."
|
||||
},
|
||||
|
||||
blockToolUnfreeze = {
|
||||
value = true,
|
||||
desc = "Prevent the toolgun from unfreezing props."
|
||||
},
|
||||
|
||||
blockCreatorTool = {
|
||||
value = true,
|
||||
desc = "Should we block the creator tool?"
|
||||
},
|
||||
|
||||
checkTooledEnts = {
|
||||
value = true,
|
||||
desc = "Review entities near tool use."
|
||||
},
|
||||
|
||||
physGunMaxRange = {
|
||||
value = 700,
|
||||
desc = "Max range a physics gun can go"
|
||||
},
|
||||
|
||||
--[[ Logs ]]--
|
||||
|
||||
logStackCrashAttempt = {
|
||||
value = true,
|
||||
desc = "Log when someone tries to lag/crash the server with stacker"
|
||||
},
|
||||
|
||||
logLagDetected = {
|
||||
value = true,
|
||||
desc = "Log when the server lags"
|
||||
},
|
||||
|
||||
--[[ Props related ]]--
|
||||
|
||||
blockPhysgunReload = {
|
||||
value = true,
|
||||
desc = "Block players from using physgun reload"
|
||||
},
|
||||
|
||||
blockContraptionMove = {
|
||||
value = true,
|
||||
desc = "Block players from moving contraptions"
|
||||
},
|
||||
|
||||
autoFreeze = {
|
||||
value = false,
|
||||
desc = "Freeze every unfrozen prop each X seconds"
|
||||
},
|
||||
|
||||
autoFreezeTime = {
|
||||
value = 120,
|
||||
desc = "Auto freeze timer (seconds)"
|
||||
},
|
||||
|
||||
fadingDoorHook = {
|
||||
value = true,
|
||||
desc = "Inject custom hooks into Fading Doors"
|
||||
},
|
||||
|
||||
fadingDoorGhosting = {
|
||||
value = true,
|
||||
desc = "Activate fading door ghosting"
|
||||
},
|
||||
|
||||
sleepyPhys = {
|
||||
value = false,
|
||||
desc = "Activate FRZR9K (Sleepy Physics)"
|
||||
},
|
||||
|
||||
sleepyPhysHook = {
|
||||
value = false,
|
||||
desc = "Hook FRZR9K into collision (Experimental)"
|
||||
},
|
||||
|
||||
allowPK = {
|
||||
value = false,
|
||||
desc = "Allow prop killing"
|
||||
},
|
||||
|
||||
developerDebug = {
|
||||
value = false,
|
||||
desc = "Dev Logs (prints stuff)"
|
||||
}
|
||||
}
|
||||
|
||||
--[[------------------------------------------
|
||||
LOADING SAVED SETTINGS -- DO NOT EDIT THIS PART
|
||||
]]--------------------------------------------
|
||||
if SERVER and file.Exists( "apg/settings.txt", "DATA" ) then
|
||||
table.Merge( APG, defaultSettings ) -- Load the default settings first!
|
||||
|
||||
local settings = file.Read( "apg/settings.txt", "DATA" )
|
||||
settings = util.JSONToTable( settings )
|
||||
|
||||
if not settings.modules or not settings.cfg then
|
||||
ErrorNoHalt("Your custom settings have not been loaded because you have a misconfigured settings file! The default settings were used instead!")
|
||||
return
|
||||
end
|
||||
|
||||
local removedSetting = {}
|
||||
|
||||
for k, v in next, settings.modules do
|
||||
if defaultSettings.modules[k] == nil then
|
||||
settings.modules[k] = nil
|
||||
table.insert(removedSetting, k)
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in next, settings.cfg do
|
||||
if defaultSettings.cfg[k] == nil then
|
||||
settings.cfg[k] = nil
|
||||
table.insert(removedSetting, k)
|
||||
end
|
||||
end
|
||||
|
||||
if next(removedSetting) then
|
||||
print("[APG] Settings File Updated. (Conflicts Resolved)")
|
||||
print("[APG] The Following Settings Have Been Removed: ")
|
||||
for _,v in next, removedSetting do
|
||||
print("\t> \"" .. tostring(v) .. "\" has been removed.")
|
||||
end
|
||||
|
||||
removedSetting = nil
|
||||
file.Write("apg/settings.txt", util.TableToJSON(settings))
|
||||
end
|
||||
|
||||
table.Merge( APG, settings )
|
||||
else
|
||||
table.Merge( APG, defaultSettings )
|
||||
end
|
||||
420
addons/apg/lua/apg/sv_apg.lua
Normal file
420
addons/apg/lua/apg/sv_apg.lua
Normal file
@@ -0,0 +1,420 @@
|
||||
util.AddNetworkString("apg_notice_s2c")
|
||||
APG = APG or {}
|
||||
|
||||
local IsValid = IsValid
|
||||
local table = table
|
||||
local isentity = isentity
|
||||
|
||||
--[[ ENTITY Related ]]
|
||||
|
||||
--[[
|
||||
Check if the player can pick up the entity
|
||||
@param {entity} ent
|
||||
@param {player} ply
|
||||
@returns {boolean}
|
||||
]]
|
||||
|
||||
function APG.canPhysGun( ent, ply )
|
||||
-- Predict if the player can pickup an entity.
|
||||
if not IsValid(ent) then return false end -- The entity isn't valid, don't pickup.
|
||||
|
||||
if ent:GetPersistent() then
|
||||
return false
|
||||
end
|
||||
|
||||
if ent.PhysgunDisabled then
|
||||
return false
|
||||
end -- Check if the entity is physgun disabled.
|
||||
|
||||
ent.APG_HeldBy = ent.APG_HeldBy or {}
|
||||
ent.APG_HeldBy.plys = ent.APG_HeldBy.plys or {}
|
||||
|
||||
if ply.APG_CantPickup == true or next( ent.APG_HeldBy.plys ) then
|
||||
ply:ConCommand("-attack") -- Tell the player to stop physgunning.
|
||||
return false
|
||||
end -- Is APG blocking the pickup?
|
||||
|
||||
if ent.CPPICanPhysgun then
|
||||
return ent:CPPICanPhysgun(ply)
|
||||
end -- Let CPPI handle things from here.
|
||||
|
||||
return false -- If everything fails we probably shouldn't be picking this up.
|
||||
end
|
||||
|
||||
function APG.isWhitelistedEnt( ent )
|
||||
|
||||
local class = ent:GetClass()
|
||||
for k, v in pairs (APG.cfg["unGhostingWhitelist"].value) do
|
||||
if ( v and k == class ) or (not v and string.find( class, k ) ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--[[
|
||||
Check if the entity is a bad entity, as defined in badEnts
|
||||
@param {entity} ent
|
||||
@returns {boolean}
|
||||
]]
|
||||
|
||||
function APG.isBadEnt( ent )
|
||||
if ent and not ent.GetClass then return false end -- Ignore if we can't read the class.
|
||||
if not IsValid(ent) then return false end -- Ignore invalid entities.
|
||||
if ent.jailWall == true then return false end -- Ignore ULX jails.
|
||||
if Entity(0) == ent or ent:IsWorld() then return false end -- Ignore worldspawn.
|
||||
if ent:IsWeapon() then return false end -- Ignore weapons.
|
||||
if ent:IsPlayer() then return false end -- Ignore players.
|
||||
if ent:IsNPC() then return false end
|
||||
if ent.ARCBank_MapEntity then return false end --Ignore ARCBank ents
|
||||
|
||||
local h = hook.Run("APGisBadEnt", ent)
|
||||
if isbool(h) then return h end
|
||||
|
||||
local class = ent:GetClass()
|
||||
for k, v in pairs (APG.cfg["badEnts"].value) do
|
||||
if ( v and k == class ) or (not v and string.find( class, k ) ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--[[
|
||||
Check the props owner
|
||||
@return {player} or nil
|
||||
]]
|
||||
|
||||
function APG.getOwner( ent )
|
||||
local owner, _ = ent:CPPIGetOwner() or ent.FPPOwner or nil
|
||||
return owner
|
||||
end
|
||||
|
||||
--[[
|
||||
Add's a timer to the module table
|
||||
@param {string} module
|
||||
@param {string} identifier
|
||||
@param {number} delay
|
||||
@param {number} repetitions
|
||||
@param {function} function
|
||||
@void
|
||||
]]
|
||||
|
||||
local function killVel(phys, freeze)
|
||||
local vec = Vector()
|
||||
if not IsValid(phys) then return end
|
||||
if freeze then phys:EnableMotion(false) return end
|
||||
|
||||
phys:SetVelocity(vec)
|
||||
phys:SetVelocityInstantaneous(vec)
|
||||
phys:AddAngleVelocity(phys:GetAngleVelocity() * -1)
|
||||
|
||||
phys:Sleep()
|
||||
end
|
||||
|
||||
function APG.killVelocity(ent, extend, freeze, wake_target)
|
||||
local vec = Vector()
|
||||
if ent.GetClass and ent:GetClass() == "player" then ent:SetVelocity(ent:GetVelocity() * -1) return end
|
||||
ent:SetVelocity(vec)
|
||||
|
||||
for i = 0, ent:GetPhysicsObjectCount() do killVel(ent:GetPhysicsObjectNum(i), freeze) end -- Includes self?
|
||||
|
||||
if extend then
|
||||
for _,v in next, constraint.GetAllConstrainedEntities(ent) do killVel(v:GetPhysicsObject(), freeze) end
|
||||
end
|
||||
|
||||
if wake_target then
|
||||
timer.Simple(0, function()
|
||||
if not IsValid(ent) then return end
|
||||
local phys = ent:GetPhysicsObject()
|
||||
if IsValid(phys) then
|
||||
phys:Wake()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function APG.freezeIt( ent, extend )
|
||||
local obj = ent:GetPhysicsObject()
|
||||
if extend then
|
||||
for _,v in next, constraint.GetAllConstrainedEntities(ent) do
|
||||
killVel(v:GetPhysicsObject(), true)
|
||||
v.APG_Frozen = true
|
||||
end
|
||||
else
|
||||
if IsValid(obj) then
|
||||
killVel(obj, true)
|
||||
ent.APG_Frozen = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function APG.FindWAC(ent)
|
||||
if not APG.cfg["vehIncludeWAC"].value then return false end
|
||||
|
||||
local e
|
||||
local i = 0
|
||||
if ent.wac_seatswitch or ent.wac_ignore then return true end
|
||||
for _,v in next, constraint.GetAllConstrainedEntities(ent) do
|
||||
if v.wac_seatswitch or v.wac_ignore then e = v break end
|
||||
if i > 12 then break end -- Only check up to 12.
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return IsValid(e)
|
||||
end
|
||||
|
||||
function APG.IsVehicle(v, basic)
|
||||
if not IsValid(v) then return false end
|
||||
|
||||
if v:IsVehicle() then return true end
|
||||
if string.find(v:GetClass(), "vehicle") then return true end
|
||||
if basic then return false end
|
||||
|
||||
if APG.FindWAC(v) then return true end
|
||||
|
||||
local parent = v:GetParent()
|
||||
return APG.IsVehicle(parent, true)
|
||||
end
|
||||
|
||||
function APG.cleanUp( mode, notify, specific )
|
||||
mode = mode or "unfrozen"
|
||||
for _, v in next, specific or ents.GetAll() do
|
||||
APG.killVelocity(v,false)
|
||||
if not APG.isBadEnt(v) or not APG.getOwner( v ) or APG.IsVehicle(v) then continue end
|
||||
if mode == "unfrozen" and v.APG_Frozen then -- Whether to clean only not frozen ents or all ents
|
||||
continue
|
||||
else
|
||||
v:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
if notify or APG.cfg["notifyLagFunc"].value then
|
||||
APG.notify( false, 2, "all", APG.cfg["notifyLevel"].value, "Cleaned up (mode: ", mode, ")" )
|
||||
end
|
||||
end
|
||||
|
||||
function APG.ghostThemAll( notify, callback )
|
||||
if not APG.modules[ "ghosting" ] then
|
||||
return APG.notify( false, 0, "admins", "[APG] Warning: Tried to ghost props but ghosting is disabled!" )
|
||||
end
|
||||
for _, v in next, ents.GetAll() do
|
||||
if ( not APG.isBadEnt(v) ) or ( not APG.getOwner( v ) ) or APG.IsVehicle(v) or v.APG_Frozen then continue end
|
||||
APG.entGhost( v, true, false )
|
||||
end
|
||||
|
||||
if notify or APG.cfg["notifyLagFunc"].value then
|
||||
APG.notify( false, APG.cfg["notifyLevel"].value, "admins", "Some unfrozen entities were ghosted!" )
|
||||
end
|
||||
|
||||
if isfunction(callback) then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
|
||||
function APG.freezeProps( notify, callback )
|
||||
for _, v in next, ents.GetAll() do
|
||||
if not APG.isBadEnt(v) or not APG.getOwner( v ) then continue end
|
||||
APG.freezeIt( v )
|
||||
end
|
||||
|
||||
if notify or APG.cfg["notifyLagFunc"].value then
|
||||
APG.notify(false, APG.cfg["notifyLevel"].value, "all", "Some entities were frozen!")
|
||||
end
|
||||
|
||||
if isfunction(callback) then
|
||||
callback()
|
||||
end
|
||||
end
|
||||
|
||||
local function GetPhysenv()
|
||||
local env = physenv.GetPerformanceSettings()
|
||||
local con = {}
|
||||
local vars = {
|
||||
"phys_upimpactforcescale",
|
||||
"phys_impactforcescale",
|
||||
"phys_pushscale",
|
||||
"sv_turbophysics",
|
||||
}
|
||||
|
||||
for _,v in next, vars do
|
||||
local var = GetConVar(v)
|
||||
con[v] = var and var:GetString() or nil
|
||||
end
|
||||
|
||||
return {con = con, env = env}
|
||||
end
|
||||
|
||||
function APG.smartCleanup( notify, callback )
|
||||
local defaults = GetPhysenv()
|
||||
local phys = table.Copy(defaults.env)
|
||||
|
||||
hook.Add("PlayerSpawnObject", "APG_smartCleanup", function() return false end)
|
||||
|
||||
RunConsoleCommand("phys_upimpactforcescale","0")
|
||||
RunConsoleCommand("phys_impactforcescale", "0")
|
||||
RunConsoleCommand("phys_pushscale", "0")
|
||||
RunConsoleCommand("sv_turbophysics", "1")
|
||||
|
||||
phys.MaxCollisionChecksPerTimestep = 0
|
||||
phys.MaxAngularVelocity = 0
|
||||
phys.MaxVelocity = 0
|
||||
physenv.SetPerformanceSettings(phys)
|
||||
|
||||
local sphere = ents.FindInSphere
|
||||
local all = ents.GetAll()
|
||||
local bad = {}
|
||||
|
||||
for _, v in next, all do
|
||||
if IsValid(v) and v.GetPhysicsObject then
|
||||
local phys = v:GetPhysicsObject()
|
||||
if IsValid(phys) and phys:IsMotionEnabled() then
|
||||
if v.isFadingDoor and APG.isBadEnt(ent) then
|
||||
SafeRemoveEntity(v)
|
||||
else
|
||||
table.insert(bad, {ent = v, phys = phys})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
APG.freezeProps()
|
||||
|
||||
for _, v in next, bad do
|
||||
local count = 0
|
||||
|
||||
local owner = APG.getOwner(v.ent)
|
||||
local space = sphere(v.ent:GetPos(), 7)
|
||||
local cache = {}
|
||||
|
||||
for _, ent in next, space do
|
||||
if owner == APG.getOwner(ent) then
|
||||
count = count + 1
|
||||
table.insert(cache, ent)
|
||||
end
|
||||
end
|
||||
|
||||
if count > 4 then
|
||||
for _, ent in next, cache do
|
||||
if APG.isBadEnt(ent) then
|
||||
SafeRemoveEntity(ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
timer.Simple(1.5, function() -- Give a few seconds for the engine to catch up.
|
||||
for k,v in next, defaults.con do
|
||||
RunConsoleCommand(k, v)
|
||||
end
|
||||
|
||||
physenv.SetPerformanceSettings(defaults.env)
|
||||
hook.Remove("PlayerSpawnObject", "APG_smartCleanup")
|
||||
|
||||
if isfunction(callback) then
|
||||
callback()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function APG.ForcePlayerDrop(ply, ent)
|
||||
if IsValid(ply) then
|
||||
ply:ConCommand("-attack")
|
||||
end
|
||||
if IsValid(ent) then
|
||||
DropEntityIfHeld( ent )
|
||||
ent:ForcePlayerDrop()
|
||||
end
|
||||
end
|
||||
|
||||
function APG.blockPickup( ply )
|
||||
if not IsValid(ply) or ply.APG_CantPickup then return end
|
||||
ply.APG_CantPickup = true
|
||||
timer.Simple(10, function()
|
||||
if IsValid(ply) then
|
||||
ply.APG_CantPickup = false
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--[[--------------------
|
||||
Set when a prop is unfrozen.
|
||||
]]----------------------
|
||||
hook.Add("PlayerUnfrozeObject", "APG_PlayerUnfrozeObject", function(ply, ent, object)
|
||||
if not APG.isBadEnt( ent ) then return end
|
||||
ent.APG_Frozen = false
|
||||
end)
|
||||
|
||||
--[[--------------------
|
||||
Physgun Drop & Freeze
|
||||
]]----------------------
|
||||
hook.Add( "OnPhysgunFreeze", "APG_OnPhysgunFreeze", function( weap, phys, ent, ply )
|
||||
if not APG.isBadEnt( ent ) then return end
|
||||
ent.APG_Frozen = true
|
||||
end)
|
||||
|
||||
--[[--------------------
|
||||
APG job manager
|
||||
--]]----------------------
|
||||
local toProcess = toProcess or {}
|
||||
|
||||
function APG.dJobRegister( job, delay, limit, func, onBegin, onEnd )
|
||||
local tab = {
|
||||
content = {},
|
||||
delay = delay,
|
||||
limit = limit,
|
||||
func = func,
|
||||
onBegin = onBegin or nil,
|
||||
onEnd = onEnd or nil
|
||||
}
|
||||
toProcess[job] = tab
|
||||
end
|
||||
|
||||
local function APG_delayedTick( job )
|
||||
if toProcess[job].processing and toProcess[job].processing == true then return end
|
||||
toProcess[job].processing = true
|
||||
if toProcess[job].onBegin then toProcess[job].onBegin() end
|
||||
local delay, pLimit = toProcess[job].delay, toProcess[job].limit
|
||||
local total = #toProcess[job].content
|
||||
local count = math.Clamp(total,0,pLimit)
|
||||
for i = 1, count do
|
||||
local cur = toProcess[job].content[1]
|
||||
timer.Create( "delay_" .. job .. "_" .. i , ( i - 1 ) * delay , 1, function()
|
||||
toProcess[job].func( cur )
|
||||
end)
|
||||
table.remove(toProcess[job].content, 1)
|
||||
end
|
||||
timer.Create("dJob_" .. job .. "_process", ( count * delay ) + 0.1 , 1, function() toProcess[job].processing = false
|
||||
if #toProcess[job].content < 1 and toProcess[job].onEnd then toProcess[job].onEnd() end
|
||||
end)
|
||||
end
|
||||
|
||||
function APG.startDJob( job, content )
|
||||
if not job or not isstring(job) or not content then return end
|
||||
if not toProcess or not toProcess[job] then
|
||||
ErrorNoHalt("[APG] No Process Found, Attempting Reload!\n---\nThis Shouldn't Happen Concider Restarting!\n")
|
||||
APG.reload()
|
||||
return
|
||||
end
|
||||
|
||||
if table.HasValue(toProcess[job].content, content) then return end
|
||||
|
||||
-- Is it a problem if there is a same ent being unghosted twice ?
|
||||
table.insert( toProcess[job].content, content )
|
||||
hook.Add("Tick", "APG_delayed_" .. job, function()
|
||||
if #toProcess[job].content > 0 then
|
||||
APG_delayedTick( job )
|
||||
else
|
||||
hook.Remove("Tick", "APG_delayed_" .. job)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("InitPostEntity", "APG_Load", function()
|
||||
hook.Add("Think", "APG_Load", function()
|
||||
APG.initialize()
|
||||
hook.Remove("Think", "APG_Load")
|
||||
end)
|
||||
end)
|
||||
150
addons/apg/lua/apg/sv_menu.lua
Normal file
150
addons/apg/lua/apg/sv_menu.lua
Normal file
@@ -0,0 +1,150 @@
|
||||
util.AddNetworkString("apg_settings_c2s")
|
||||
util.AddNetworkString("apg_menu_s2c")
|
||||
util.AddNetworkString("apg_context_c2s")
|
||||
|
||||
local function saveSettings( json )
|
||||
if not file.Exists("apg", "DATA") then file.CreateDir( "apg" ) end
|
||||
file.Write("apg/settings.txt", json)
|
||||
end
|
||||
|
||||
local function recSettings( len, ply)
|
||||
if not ply:IsSuperAdmin() then return end
|
||||
|
||||
len = net.ReadUInt( 32 )
|
||||
if len == 0 then return end
|
||||
|
||||
local settings = net.ReadData( len )
|
||||
settings = util.Decompress( settings )
|
||||
|
||||
saveSettings( settings )
|
||||
|
||||
settings = util.JSONToTable( settings )
|
||||
APG.cfg = settings.cfg
|
||||
table.Merge(APG, settings)
|
||||
APG.reload()
|
||||
end
|
||||
net.Receive( "apg_settings_c2s", recSettings)
|
||||
|
||||
local function sendToClient( ply )
|
||||
local settings = {}
|
||||
settings.cfg = APG.cfg or {}
|
||||
settings.modules = APG.modules or {}
|
||||
|
||||
settings = util.TableToJSON( settings )
|
||||
settings = util.Compress( settings )
|
||||
net.Start("apg_menu_s2c")
|
||||
net.WriteUInt( settings:len(), 32 ) -- Write the length of the data
|
||||
net.WriteData( settings, settings:len() ) -- Write the data
|
||||
net.Send(ply)
|
||||
end
|
||||
|
||||
hook.Add( "PlayerSay", "openAPGmenu", function( ply, text, public )
|
||||
text = string.lower( text )
|
||||
if ply:IsSuperAdmin() and text == "!apg" then
|
||||
sendToClient( ply )
|
||||
return ""
|
||||
end
|
||||
end)
|
||||
|
||||
local function checkOwner(owner, ply)
|
||||
if ( IsValid(owner) and owner:IsPlayer() ) then
|
||||
return true
|
||||
else
|
||||
APG.notification("The owner of this entity is NOT a Player. (Owner: " .. type(owner) .. ")", ply)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- TODO: Revamp this, really don't like how it looks, would rather have a function for each
|
||||
-- it's too clustered.
|
||||
|
||||
local function contextCMD(_,ply)
|
||||
if not ply:IsSuperAdmin() then return end
|
||||
|
||||
local cmd = net.ReadString()
|
||||
local ent = net.ReadEntity()
|
||||
|
||||
ent = IsValid(ent) and ent or ply:GetEyeTraceNoCursor().Entity or nil
|
||||
|
||||
local class = IsValid(ent) and ent.GetClass and ent:GetClass() or nil
|
||||
if not class then return end
|
||||
|
||||
local owner = APG.getOwner(ent)
|
||||
|
||||
if cmd == "addghost" then
|
||||
if not APG.cfg.badEnts.value[class] then
|
||||
APG.cfg.badEnts.value[class] = true
|
||||
APG.notify( false, 0, ply, "\"", class, "\" added to Ghost List!" )
|
||||
else
|
||||
APG.notify( false, 0, ply, "This class is already listed!" )
|
||||
end
|
||||
elseif cmd == "remghost" then
|
||||
APG.cfg.badEnts.value[class] = nil
|
||||
APG.notify( false, 0, ply, "\"", class, "\" removed from Ghost List!" )
|
||||
elseif cmd == "clearowner" then
|
||||
if not checkOwner(owner, ply) then return end
|
||||
cleanup.CC_Cleanup(owner,"gmod_cleanup",{})
|
||||
elseif cmd == "clearunfrozen" then
|
||||
if not checkOwner(owner, ply) then return end
|
||||
|
||||
local count = 0
|
||||
for _,v in next, ents.GetAll() do
|
||||
if not (IsValid(v) and APG.getOwner(v) == owner) then continue end
|
||||
if not APG.isBadEnt(v) then continue end
|
||||
if not v.APG_Frozen then
|
||||
SafeRemoveEntity(v)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
APG.notify( false, 0, ply, count, "entities have been removed!" )
|
||||
elseif cmd == "getownercount" then
|
||||
if not checkOwner(owner, ply) then return end
|
||||
|
||||
local count = 0
|
||||
|
||||
for _,v in next, ents.GetAll() do
|
||||
if IsValid(v) and APG.getOwner(v) == owner then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
APG.notify( false, 0, ply, owner:Nick(), "[", owner:SteamID(), "]", "has", count, (count == 1 and "entity." or "entities.") )
|
||||
elseif cmd == "freezeclass" then
|
||||
local count = 0
|
||||
|
||||
for _,v in next, ents.FindByClass(class) do
|
||||
if IsValid(v) and not v.APG_Frozen then
|
||||
count = count + 1
|
||||
APG.killVelocity(v, false, true, false)
|
||||
end
|
||||
end
|
||||
|
||||
APG.notify( false, 0, ply, (count or 0), (count == 1 and "Entity" or "Entities"), "Frozen" )
|
||||
elseif cmd == "sleepclass" then
|
||||
local count = 0
|
||||
|
||||
for _,v in next, ents.FindByClass(class) do
|
||||
if IsValid(v) and not v.APG_Frozen then
|
||||
count = count + 1
|
||||
APG.killVelocity(v, false, false, false)
|
||||
end
|
||||
end
|
||||
|
||||
APG.notify( false, 0, ply, (count or 0), (count == 1 and "Entity is" or "Entities are"), "now Sleeping!" )
|
||||
elseif cmd == "ghost" then
|
||||
APG.entGhost(ent)
|
||||
APG.notify( false, 0, ply, ent, " was ghosted." )
|
||||
end
|
||||
|
||||
if cmd == "addghost" or cmd == "remghost" then
|
||||
local settings = {}
|
||||
settings.cfg = APG.cfg or {}
|
||||
settings.modules = APG.modules or {}
|
||||
|
||||
saveSettings( util.TableToJSON( settings ) )
|
||||
APG.reload()
|
||||
end
|
||||
end
|
||||
net.Receive("apg_context_c2s", contextCMD)
|
||||
3
addons/apg/lua/autorun/client/cl_apg_init.lua
Normal file
3
addons/apg/lua/autorun/client/cl_apg_init.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
APG = {}
|
||||
include( "apg/sh_config.lua" )
|
||||
include( "apg/cl_menu.lua" )
|
||||
176
addons/apg/lua/autorun/server/sv_apg_init.lua
Normal file
176
addons/apg/lua/autorun/server/sv_apg_init.lua
Normal file
@@ -0,0 +1,176 @@
|
||||
--[[ INITIALIZE APG ]]
|
||||
APG = {}
|
||||
APG.modules = APG.modules or {}
|
||||
|
||||
--[[ Micro Optimization ]]
|
||||
local timer = timer
|
||||
local table = table
|
||||
|
||||
--[[ CLIENT related ]]
|
||||
AddCSLuaFile("apg/sh_config.lua")
|
||||
AddCSLuaFile("apg/cl_utils.lua")
|
||||
AddCSLuaFile("apg/cl_menu.lua")
|
||||
|
||||
--[[ REGISTER Modules ]]
|
||||
local modules, _ = file.Find("apg/modules/*.lua","LUA")
|
||||
for _,v in next, modules do
|
||||
if v then
|
||||
niceName = string.gsub(tostring(v),"%.lua","")
|
||||
APG.modules[ niceName ] = false
|
||||
APG[ niceName ] = { hooks = {}, timers = {}}
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Add's a hook to the module table
|
||||
@param {string} module
|
||||
@param {string} event
|
||||
@param {string} identifier
|
||||
@param {function} function
|
||||
@void
|
||||
]]
|
||||
|
||||
|
||||
function APG.hookAdd( module, event, identifier, func )
|
||||
table.insert( APG[ module ][ "hooks"], { event = event, identifier = identifier, func = func })
|
||||
end
|
||||
|
||||
--[[
|
||||
Adds all the hooks that the module needs
|
||||
@param {string} module
|
||||
@void
|
||||
]]
|
||||
|
||||
function APG.updateHooks( module )
|
||||
for k, v in next, APG[module]["hooks"] do
|
||||
hook.Add( v.event, v.identifier, v.func )
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Add's a timer to the module table
|
||||
@param {string} module
|
||||
@param {string} identifier
|
||||
@param {number} delay
|
||||
@param {number} repetitions
|
||||
@param {function} function
|
||||
@void
|
||||
]]
|
||||
|
||||
function APG.timerAdd( module, identifier, delay, repetitions, func )
|
||||
table.insert( APG[ module ][ "timers"], { identifier = identifier, delay = delay, repetitions = repetitions, func = func } )
|
||||
end
|
||||
|
||||
--[[
|
||||
Add's a the timers a module needs.
|
||||
@param {string} module
|
||||
@void
|
||||
]]
|
||||
function APG.updateTimers(module)
|
||||
for k, v in next, APG[module]["timers"] do
|
||||
timer.Create( v.identifier, v.delay, v.repetitions, v.func )
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Load's a APG module
|
||||
@param {string} module
|
||||
@void
|
||||
]]
|
||||
|
||||
function APG.load( module )
|
||||
APG.modules[ module ] = true
|
||||
include( "apg/modules/" .. module .. ".lua" )
|
||||
print("[APG] " .. module .. " loaded.")
|
||||
end
|
||||
|
||||
--[[
|
||||
Unload's a APG module
|
||||
@param {string} module
|
||||
@void
|
||||
]]
|
||||
|
||||
function APG.unLoad( module )
|
||||
APG.modules[module] = false
|
||||
|
||||
if not (istable(APG[module]) and next(APG[module])) then return end
|
||||
|
||||
local hooks = APG[ module ]["hooks"]
|
||||
for k, v in next, hooks do
|
||||
hook.Remove(v.event, v.identifier)
|
||||
end
|
||||
|
||||
local timers = APG[ module ]["timers"]
|
||||
for k, v in next, timers do
|
||||
timer.Remove(v.identifier)
|
||||
end
|
||||
|
||||
print("[APG] " .. module .. " unloaded.")
|
||||
end
|
||||
|
||||
function APG.reload()
|
||||
for k, v in next, APG.modules do
|
||||
if APG.modules[ k ] == true then
|
||||
APG.unLoad( k )
|
||||
APG.load( k )
|
||||
else
|
||||
APG.unLoad( k )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[ local settings = {}
|
||||
function APG.sampleServerSettings()
|
||||
|
||||
end
|
||||
|
||||
function APG.getServerSettings()
|
||||
|
||||
end ]]
|
||||
|
||||
function APG.initialize()
|
||||
for k, v in next, APG.modules do
|
||||
if APG.modules[k] == true then
|
||||
APG.load(k)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[ LOADING ]]
|
||||
-- Loading config first
|
||||
include( "apg/sh_config.lua" )
|
||||
-- Loading APG main functions
|
||||
include( "apg/sv_apg.lua") -- Modules loaded at the bottom
|
||||
-- Loading APG menu
|
||||
include( "apg/sv_menu.lua" )
|
||||
|
||||
--[[ CVars INIT ]]
|
||||
|
||||
concommand.Add("apg_set", function( ply, cmd, args, argStr )
|
||||
if not ply:IsSuperAdmin() then return end
|
||||
|
||||
if args[1] == "module" then
|
||||
local _module = APG.modules[ args[2] ]
|
||||
if _module != nil then
|
||||
if _module == true then
|
||||
APG.unLoad( args[2] )
|
||||
APG.notification( "[APG] Module " .. args[2] .. " disabled.", ply)
|
||||
else
|
||||
APG.load( args[2] )
|
||||
APG.notification( "[APG] Module " .. args[2] .. " enabled.", ply)
|
||||
end
|
||||
else
|
||||
APG.notification( "[APG] This module does not exist", ply)
|
||||
end
|
||||
|
||||
elseif args[1] == "help" then
|
||||
local cfg = APG.cfg[ args[2] ]
|
||||
if cfg then
|
||||
APG.notification( cfg.desc, ply)
|
||||
else
|
||||
APG.notification( "[APG] Help: This setting does not exist", ply)
|
||||
end
|
||||
else
|
||||
APG.notification( ply, "Error: unknown setting")
|
||||
end
|
||||
end)
|
||||
2
addons/apg/lua/autorun/sh_apg.lua
Normal file
2
addons/apg/lua/autorun/sh_apg.lua
Normal file
@@ -0,0 +1,2 @@
|
||||
-- ULX Admin Commands Coming Soon!
|
||||
--- PLanned Commands for Prop Management, and Server Cleanup.
|
||||
Reference in New Issue
Block a user