mirror of
				https://github.com/chev2/gmod-addons.git
				synced 2025-10-30 06:31:35 +00:00 
			
		
		
		
	TraceNormal was being sent as a vector and read as a normal--now it is written & read as a normal. Also changed HitNormal to be sent as a normal while I'm at it
		
			
				
	
	
		
			215 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| include("spraymesh/sh_init.lua")
 | |
| 
 | |
| util.AddNetworkString("SprayMesh.SV_SendSpray")
 | |
| util.AddNetworkString("SprayMesh.SV_ClearSpray")
 | |
| 
 | |
| -- Remove various HTML/JS characters from the URL
 | |
| local function SanitizeURL(url)
 | |
|     local ban = [=[{}[]:'",<>()]=]
 | |
|     local bad = string.Explode("", ban, false)
 | |
| 
 | |
|     for k, v in pairs(bad) do
 | |
|         url = string.Replace(url, v, "") -- Gsub uses patterns that conflict with the special characters
 | |
|     end
 | |
| 
 | |
|     return url
 | |
| end
 | |
| 
 | |
| -- Called when the player sprays something--their set URL will be sanitized and checked to ensure it's valid.
 | |
| local function FixURL(url, ply)
 | |
|     -- If the URL is nil, invalid data type or an empty string
 | |
|     if url == nil or type(url) ~= "string" or url == "" then
 | |
|         spraymesh.DebugPrint("URL is wrong type or is empty data!")
 | |
| 
 | |
|         url = spraymesh.SPRAY_URL_DEFAULT
 | |
|     end
 | |
| 
 | |
|     -- If the URL contains bad/exploitable characters
 | |
|     if url ~= SanitizeURL(url) then
 | |
|         spraymesh.DebugPrint("URL is not equal to its sanitized counterpart!")
 | |
| 
 | |
|         url = spraymesh.SPRAY_URL_DEFAULT
 | |
|     end
 | |
| 
 | |
|     -- Check to see if the spray is a valid image or video spray
 | |
|     -- This is where the whitelisted domains/extensions for the spray is checked
 | |
|     local allowed = false
 | |
|     local sprayType = spraymesh.GetURLInfo(url)
 | |
|     spraymesh.DebugPrint("FixURL spray type: " .. sprayType)
 | |
|     if sprayType ~= SPRAYTYPE_INVALID then allowed = true end
 | |
| 
 | |
|     spraymesh.DebugPrint("FixURL allowed?: " .. tostring(allowed))
 | |
| 
 | |
|     if not allowed then
 | |
|         spraymesh.DebugPrint("INVALID URL: " .. url)
 | |
| 
 | |
|         url = spraymesh.SPRAY_URL_DEFAULT
 | |
|     end
 | |
| 
 | |
|     return url
 | |
| end
 | |
| 
 | |
| function spraymesh.RemoveSpray(id64)
 | |
|     if not spraymesh.SPRAYDATA[id64] then return end
 | |
| 
 | |
|     net.Start("SprayMesh.SV_ClearSpray")
 | |
|         net.WriteString(id64)
 | |
|         net.Broadcast()
 | |
| 
 | |
|     spraymesh.SPRAYDATA[id64] = nil
 | |
| end
 | |
| 
 | |
| function spraymesh.SendSpray(hitpos, hitnormal, tracenormal, ply)
 | |
|     if IsValid(ply) and ply.SteamID64 then
 | |
|         local id64 = ply:SteamID64()
 | |
|         local url = FixURL(ply:GetInfo("spraymesh_url"), ply)
 | |
|         local sprayInfo = spraymesh.SPRAYDATA[id64] or {}
 | |
| 
 | |
|         sprayInfo.url = url
 | |
|         sprayInfo.pos = hitpos
 | |
|         sprayInfo.normal = hitnormal
 | |
|         sprayInfo.TraceNormal = -tracenormal
 | |
|         sprayInfo.PlayerName = ply:Nick()
 | |
|         sprayInfo.Time = ply.LastSprayTime or CurTime()
 | |
| 
 | |
|         local coordDist = spraymesh.COORD_DIST_DEFAULT
 | |
| 
 | |
|         sprayInfo.CoordDistance = coordDist
 | |
| 
 | |
|         hook.Run("SprayMesh.OnSpraySent", ply, url, hitpos)
 | |
| 
 | |
|         spraymesh.DebugPrint("sending spray: " .. sprayInfo.url)
 | |
| 
 | |
|         net.Start("SprayMesh.SV_SendSpray")
 | |
|             net.WriteString(id64)
 | |
|             net.WriteString(sprayInfo.PlayerName)
 | |
|             net.WriteVector(hitpos)
 | |
|             net.WriteNormal(hitnormal)
 | |
|             net.WriteNormal(tracenormal)
 | |
|             net.WriteString(url)
 | |
|             net.WriteFloat(sprayInfo.CoordDistance)
 | |
|             net.WriteFloat(sprayInfo.Time)
 | |
|             net.Broadcast()
 | |
| 
 | |
|         spraymesh.SPRAYDATA[id64] = sprayInfo
 | |
|     end
 | |
| end
 | |
| 
 | |
| local PLAYER = FindMetaTable("Player")
 | |
| local OldAllowImmediateDecalPainting = PLAYER.AllowImmediateDecalPainting
 | |
| 
 | |
| -- We need to adjust this metamethod to interface with SprayMesh.
 | |
| function PLAYER:AllowImmediateDecalPainting(allow)
 | |
|     local id64 = self:SteamID64()
 | |
|     spraymesh.SPRAYDATA[id64] = spraymesh.SPRAYDATA[id64] or {}
 | |
|     spraymesh.SPRAYDATA[id64].immediate = allow
 | |
| 
 | |
|     return OldAllowImmediateDecalPainting(self, allow)
 | |
| end
 | |
| 
 | |
| local TOOL_TEXTURES = {
 | |
|     ["tools/toolsinvisible"] = true,
 | |
|     ["tools/toolsskybox"] = true,
 | |
|     ["tools/toolsskybox2d"] = true,
 | |
|     ["tools/toolsclip"] = true
 | |
| }
 | |
| 
 | |
| -- The default, built-in spray gets overriden here
 | |
| hook.Add("PlayerSpray", "SprayMesh.OverrideNativeSpray", function(ply)
 | |
|     spraymesh.DebugPrint("playerspray")
 | |
| 
 | |
|     local id64 = ply:SteamID64()
 | |
|     local sprayInfo = spraymesh.SPRAYDATA[id64] or {}
 | |
| 
 | |
|     -- The player must be alive, and the spray must not be on cooldown
 | |
|     if (not sprayInfo.delay or sprayInfo.immediate) and ply:Alive() then
 | |
|         -- Apply spray cooldown
 | |
|         if not sprayInfo.immediate then
 | |
|             sprayInfo.delay = true
 | |
| 
 | |
|             timer.Simple(spraymesh.SPRAY_COOLDOWN, function()
 | |
|                 if sprayInfo then
 | |
|                     sprayInfo.delay = false
 | |
|                 end
 | |
|             end)
 | |
|         end
 | |
| 
 | |
|         -- Perform trace
 | |
|         local tr = util.TraceLine({
 | |
|             start = ply:EyePos(),
 | |
|             endpos = ply:EyePos() + (ply:GetAimVector() * (4096 * 8)),
 | |
|             filter = function(ent)
 | |
|                 if ent:IsWorld() then return true end
 | |
|             end
 | |
|         })
 | |
| 
 | |
|         -- No spraying on invisible walls/tool textures
 | |
|         if TOOL_TEXTURES[tr.HitTexture:lower()] then return true end
 | |
| 
 | |
|         ply.LastSprayTime = CurTime()
 | |
| 
 | |
|         -- The spray must hit the world
 | |
|         local sprayHitValidSpot = tr.Hit and tr.Entity:IsWorld()
 | |
| 
 | |
|         -- Give other code a chance to block the spray
 | |
|         local shouldAllowSpray = hook.Run("SprayMesh.ShouldAllowSpray", ply, tr) ~= false
 | |
| 
 | |
|         if sprayHitValidSpot and shouldAllowSpray then
 | |
|             spraymesh.SendSpray(tr.HitPos, tr.HitNormal, tr.Normal, ply)
 | |
|         end
 | |
|     end
 | |
| 
 | |
|     spraymesh.SPRAYDATA[id64] = sprayInfo
 | |
| 
 | |
|     -- Disables regular sprays
 | |
|     return true
 | |
| end)
 | |
| 
 | |
| -- See https://wiki.facepunch.com/gmod/GM:PlayerInitialSpawn
 | |
| -- for why this is needed.
 | |
| local LOAD_QUEUE = {}
 | |
| 
 | |
| local function SendSpraysToClient(ply)
 | |
|     for id64, data in pairs(spraymesh.SPRAYDATA) do
 | |
|         if data.pos and data.normal then
 | |
|             spraymesh.DebugPrint("- Sending spray: " .. data.url)
 | |
| 
 | |
|             net.Start("SprayMesh.SV_SendSpray")
 | |
|                 net.WriteString(id64)
 | |
|                 net.WriteString(data.PlayerName)
 | |
|                 net.WriteVector(data.pos)
 | |
|                 net.WriteNormal(data.normal)
 | |
|                 net.WriteNormal(data.TraceNormal)
 | |
|                 net.WriteString(data.url)
 | |
|                 net.WriteFloat(data.CoordDistance)
 | |
|                 net.WriteFloat(data.Time)
 | |
|                 net.Send(ply)
 | |
|         else
 | |
|             spraymesh.DebugPrint(("Invalid SprayMesh data found for %s, skipping..."):format(id64))
 | |
|         end
 | |
|     end
 | |
| end
 | |
| 
 | |
| -- Send existing spraymesh sprays to joining players.
 | |
| hook.Add("PlayerInitialSpawn", "SprayMesh.SendExistingSpraysToConnectedPlayer", function(ply)
 | |
|     LOAD_QUEUE[ply] = true
 | |
| end)
 | |
| 
 | |
| hook.Add("SetupMove", "SprayMesh.NetworkSpraysOnceReady", function(ply, mv, cmd)
 | |
|     if LOAD_QUEUE[ply] and not cmd:IsForced() then
 | |
|         LOAD_QUEUE[ply] = nil
 | |
| 
 | |
|         spraymesh.DebugPrint("Player " .. ply:Nick() .. " is ready to receive networking")
 | |
|         SendSpraysToClient(ply)
 | |
|     end
 | |
| end)
 | |
| 
 | |
| -- Spraymesh chat command, using the provided prefixes
 | |
| hook.Add("PlayerSay", "SprayMesh.OpenSprayMeshConfigurationMenu", function(ply, text, isTeam)
 | |
|     for _, prefix in ipairs(spraymesh.CHAT_COMMAND_PREFIXES) do
 | |
|         if string.StartsWith(text:lower(), prefix .. "spraymesh") then
 | |
|             ply:ConCommand("spraymesh_settings")
 | |
|             return ""
 | |
|         end
 | |
|     end
 | |
| end)
 |