[Exploit Fix]Ready Room Hydra Block Script
If you haven't seen it yet, there's an exploit that allows Gorges to build Hydras in the Ready Room. Some idiots decide to spam this exploit and it ends up lagging the server. Here's a fix I wrote to block this and punish exploiters. This is a modification of a NS2 game file so make sure you back up the original before modifying it. This script is for Server Ops only.
First backup "ns2\lua\weapons\alien\HydraAbility.lua". Now open the original file and find "function HydraAbility:CreateHydra(player)", it should be at line 112. Replace this ENTIRE function (all the way down to it's last "end" statement) with the following code.
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function HydraAbility:CreateHydra(player)
// If we have enough plasma
if Server then
local coords, valid = self:GetPositionForHydra(player)
local cost = LookupTechData(kTechId.Hydra, kTechDataCostKey)
if valid and (player:GetPlasma() >= cost) then
//Ready Room check
local entRRSpawn = Server.readyRoomSpawnList[1]
if entRRSpawn~=nil then
local vecDifference = entRRSpawn:GetOrigin() - player:GetOrigin()
local flDistance = vecDifference:GetLengthXZ()
if flDistance <= 30 then
//****PUNISHMENT START
//Log the player's user id (converts to steam id)
Print("[EXPLOITER] Name:%s (UserId:%d). Tried to build Hydras in the Ready Room",player:GetName(),client:GetUserId())
//Show a server message to everyone
Server.Broadcast(nil,string.format("EXPLOITER ALERT: %s attempted to build a Hydra in the Ready Room",player:GetName()))
//Show a message in the player's tooltip
player:AddTooltip("Stop exploiting...")
//Remove player's Plasma
player:SetPlasma(0)
//Kill the player
player:Kill(player, player, player:GetOrigin())
//***PUNISHMENT END
return
else
// Create structure
local hydra = CreateEntity( Hydra.kMapName, coords.origin, player:GetTeamNumber() )
hydra:SetOwner(player)
// Check for space
if hydra:SpaceClearForEntity(coords.origin) then
local angles = Angles()
angles:BuildFromCoords(coords)
hydra:SetAngles(angles)
hydra:TriggerEffects("hydra_spawn")
player:AddPlasma( -cost )
else
DestroyEntity(hydra)
end
end
end
end
end
end<!--c2--></div><!--ec2-->
If someone tries to build a Hydra in the Ready Room, they will be killed, have their plasma removed, and there will be a server announcement so everyone will know about the attempted exploit. If you want to disable any of those punishements, just add "//" in front of the corresponding line or you can even just delete it.
This script uses a kind of "hacky" method to detect if the player is in the Ready Room. It simply compares the distance from the player to a Ready Room spawn point. That means that this may not work right on a custom map with an abnormally close Ready Room to the map. All that would happen is that a very, very, very small part of the map wouldn't let you build Hydras on it. Everything else would work fine.
A better way to detect if the player is in the Ready Room is by simply checking if player:GetLocationName() == "Ready Room", however, for some strange reason, no current maps have a "Ready Room" location zone. Does anyone know why not? It seems like that location zone would be useful for other things.
I tested this on 161: listenserver, hlds, ns2_junction, ns2_rockdown, ns2_tram, ns2_basic , ns2_sample-build160_01. Everything worked.
First backup "ns2\lua\weapons\alien\HydraAbility.lua". Now open the original file and find "function HydraAbility:CreateHydra(player)", it should be at line 112. Replace this ENTIRE function (all the way down to it's last "end" statement) with the following code.
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->function HydraAbility:CreateHydra(player)
// If we have enough plasma
if Server then
local coords, valid = self:GetPositionForHydra(player)
local cost = LookupTechData(kTechId.Hydra, kTechDataCostKey)
if valid and (player:GetPlasma() >= cost) then
//Ready Room check
local entRRSpawn = Server.readyRoomSpawnList[1]
if entRRSpawn~=nil then
local vecDifference = entRRSpawn:GetOrigin() - player:GetOrigin()
local flDistance = vecDifference:GetLengthXZ()
if flDistance <= 30 then
//****PUNISHMENT START
//Log the player's user id (converts to steam id)
Print("[EXPLOITER] Name:%s (UserId:%d). Tried to build Hydras in the Ready Room",player:GetName(),client:GetUserId())
//Show a server message to everyone
Server.Broadcast(nil,string.format("EXPLOITER ALERT: %s attempted to build a Hydra in the Ready Room",player:GetName()))
//Show a message in the player's tooltip
player:AddTooltip("Stop exploiting...")
//Remove player's Plasma
player:SetPlasma(0)
//Kill the player
player:Kill(player, player, player:GetOrigin())
//***PUNISHMENT END
return
else
// Create structure
local hydra = CreateEntity( Hydra.kMapName, coords.origin, player:GetTeamNumber() )
hydra:SetOwner(player)
// Check for space
if hydra:SpaceClearForEntity(coords.origin) then
local angles = Angles()
angles:BuildFromCoords(coords)
hydra:SetAngles(angles)
hydra:TriggerEffects("hydra_spawn")
player:AddPlasma( -cost )
else
DestroyEntity(hydra)
end
end
end
end
end
end<!--c2--></div><!--ec2-->
If someone tries to build a Hydra in the Ready Room, they will be killed, have their plasma removed, and there will be a server announcement so everyone will know about the attempted exploit. If you want to disable any of those punishements, just add "//" in front of the corresponding line or you can even just delete it.
This script uses a kind of "hacky" method to detect if the player is in the Ready Room. It simply compares the distance from the player to a Ready Room spawn point. That means that this may not work right on a custom map with an abnormally close Ready Room to the map. All that would happen is that a very, very, very small part of the map wouldn't let you build Hydras on it. Everything else would work fine.
A better way to detect if the player is in the Ready Room is by simply checking if player:GetLocationName() == "Ready Room", however, for some strange reason, no current maps have a "Ready Room" location zone. Does anyone know why not? It seems like that location zone would be useful for other things.
I tested this on 161: listenserver, hlds, ns2_junction, ns2_rockdown, ns2_tram, ns2_basic , ns2_sample-build160_01. Everything worked.
Comments
Also your check would occur every time anyone drops a hydra in the server... wouldn't these calculations also cause lag? Anyway you can remove the costly sqrt by using a "Euclidean-Squared-Distance Metric" as opposed to a vector:length function.
I must say I'm surprised you actually did this, good work. It is true that people can just build on the roof, which is unfortunate. And having a script that tells them they are exploiting will only make them learn quicker, would it be possible to have this script output the users steamid (for each attempted hydra build) to a logfile on the server, but take no ingame action?
I plan to ban them at a later stage because im vengeful like that.
Also your check would occur every time anyone drops a hydra in the server... wouldn't these calculations also cause lag? Anyway you can remove the costly sqrt by using a "Euclidean-Squared-Distance Metric" as opposed to a vector:length function.<!--QuoteEnd--></div><!--QuoteEEnd-->
Well it's not so much that they are trying to lag the server, it's just that they are amused by spamming the Ready Room with Hydras. When someone joins the server, they start getting attacked by 100 Hydras. They enjoy that.
The calculations are actually very, very, fast. Memory access is direct(there's no searching) and then there's just a few simple calculations. This only happens when someone actually places a Hydra after all validity checks pass. There are far more complex operations done ~60 times per second.
<!--quoteo(post=1823906:date=Jan 13 2011, 02:15 AM:name=endar)--><div class='quotetop'>QUOTE (endar @ Jan 13 2011, 02:15 AM) <a href="index.php?act=findpost&pid=1823906"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->Hi Camron,
I must say I'm surprised you actually did this, good work. It is true that people can just build on the roof, which is unfortunate. And having a script that tells them they are exploiting will only make them learn quicker, would it be possible to have this script output the users steamid (for each attempted hydra build) to a logfile on the server, but take no ingame action?
I plan to ban them at a later stage because im vengeful like that.<!--QuoteEnd--></div><!--QuoteEEnd-->
I've updated the script to log the player's userId
<!--c1--><div class='codetop'>CODE</div><div class='codemain'><!--ec1-->//Log the player's user id (converts to steam id)
Print("[EXPLOITER] Name:%s (UserId:%d). Tried to build Hydras in the Ready Room",player:GetName(),client:GetUserId())<!--c2--></div><!--ec2-->
The userId can be converted to the steamId like this STEAM_0:n:x -> 2x+n (thanks <a href="http://www.unknownworlds.com/ns2/forums/index.php?s=&showtopic=112026&view=findpost&p=1823503" target="_blank">player</a>). You can also then find their steam community ID like this STEAM_X:Y:Z -> [SteamCommunityID = (Z*2) + 76561197960265728 + Y] at steamcommunity.com/profiles/SteamCommunityID. I didn't make the script convert the userID into the Steam ID because a NS2 banlist will be made up of UserIDs (I think). I wanted to add a timestamp to the log message but I don't think there's any way to do that yet (Can't use Lua's os.date).
Do you have any other script requests? I could make a Hydra counter, Hydra alert(announce if more than X Hydras are in the map), Hydra clean up script(if there are more than X hydras, then remove a bunch). I was looking into making a script to identify mic spammers, but it seems like there isn't enough of the voice chat functionality done yet to get anything done in a decent way.
The whole logging etc might be overkill, we are talking about a fix for a a bug in a beta game, finding/exploiting bugs is part of the playtesting (that can go out of hands like this hydra spam).
and i don't see performance problems either, its not like every second somebody would place a hydra.
...
...isn't enough of the voice chat functionality done yet to get anything done in a decent way.<!--QuoteEnd--></div><!--QuoteEEnd-->
I'll re-read this when im not sleepy!
<!--quoteo(post=1823956:date=Jan 13 2011, 11:53 PM:name=Asraniel)--><div class='quotetop'>QUOTE (Asraniel @ Jan 13 2011, 11:53 PM) <a href="index.php?act=findpost&pid=1823956"><{POST_SNAPBACK}></a></div><div class='quotemain'><!--quotec-->sounds good.
The whole logging etc might be overkill, we are talking about a fix for a a bug in a beta game, finding/exploiting bugs is part of the playtesting (that can go out of hands like this hydra spam).
and i don't see performance problems either, its not like every second somebody would place a hydra.<!--QuoteEnd--></div><!--QuoteEEnd-->
Finding and exploiting IS part of the game, but doing it repeatedly like I have seen happen? I believe that people can not be rehabilitated, and sometimes there is only one option. It's reasons like this why I should not hold any political power, ever. Actions need consequences, and on the internet unfortunately there often isn't any.