gmod-addons/addons/spraymesh_extended/lua/spraymesh/server/sv_init.lua
Chev 7ec7b14b2f
SprayMesh Extended: Fix incorrect network data type
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
2024-02-19 13:05:51 -08:00

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)