Unstuck addon

Figured I should post here the unstuck addon that I worked on months ago primarily for my Prop Hunt server.
Should work on other gamemodes too.

The aim for my version was to make it least exploitable as I could make it.
I swear, some players make it their mission to find ways to get out of the map.

https://github.com/MrGrim48/gmod_unstuck

[editline]edit[/editline]

There are different parts to this addon. I’ve seen other addons that simply respawn the player if they call the unstuck function.
Problem: Players can abuse the command to escape Prop Hunters for example.

Another moved the player up a bit to check if they fall just enough to determine they are free to move around.
Problem: Players can exploit this to move up higher or into the ceiling to get out of maps or areas they are not supposed to be.

The last simply got the surrounding positions and used TraceLine to check if there is a free path to the new position.
Problem: High rate of failure (especially with Prop Hunt) because it doesn’t check around corners.

This addon tries to mix all of these to solve most, if not all of the problems when a player gets stuck. (Except the second problem. I used TraceHull instead of moving the player up a bit. Prevent’s moving into the ceiling.)
It will check the surrounding positions to move into. Then, based on the configuration, will recursively check each of the surrounding positions that the player could move to, with the origin of the TraceLine changing to each of these new positions.

In the end, this will attempt to unstuck the player as best as possible, with minimal amount of exploitation.

A sort of quick and kinda drunk demonstration picture.
Random Paint Image

A few things:

  • You don’t have to AddCSLuaFile autorun files
  • Cache GetConVar calls outside of the hook/function, then use the object inside
  • You can use convar:GetBool() for a boolean return instead of checking an int is == 1. This makes it so options other than 0 will work
  • Avoid using net.WriteTable if possible. You can create a separate net message for each chat message to avoid having to guess colours from strings.
  • You should use MASK_PLAYERSOLID instead of MASK_ALL since that will return all things that cause players to be stuck. There are things that would fall under MASK_ALL that don’t affect collision
  • You never clear out the LocalPlayer().Unstuck table/never freeing the memory
  • If you add FCVAR_USERINFO to your unstuck_debug convar, you can check its value serverside with

Player:GetInfoNum and prevent the net message from sending unless the player is debugging. This will save net bloat in the majority case that the convar is disabled. You can also network predefined ints instead of strings for “add”/“new”/“box”/“line” to cut down even more, but since it’s debug code, it doesn’t really matter

  • Use TraceEntity here instead of the player’s hull: it will properly trace the entity in the case that they are resized or crouching. GetHull will return the static hull size if the player is standing.
  • I would create a convar for trace step size instead of hardcoding your FindNewPos function to 32. Otherwise, you should at least scale the step size by the player’s hull: 32 is way too little
  • You cannot cache vectors like this: using userdata as a key will compare its location in memory, not its x/y/z values. You shouldn’t really even cache “clear” positions since that can change at any time
  • You should return integers/bools instead of strings for function return “states” (FindNewPos)

In general, your algorithm for finding a clear position could be a lot more effective and efficient. In general, if a player is stuck, they probably got that way from being pushed into a brush or prop. That means most times, they are not completely inside the world, but rather, only a small portion of their hull. I would start by checking each corner of the hull to see if there is an empty position closest close to them. If so, trace out from there and see if it’s free. Otherwise, trace out in a 3D-analogue cross or asterisk pattern and check the hull corners each time to see if an empty position was found.

[editline]5th September 2017[/editline]

You could do the the actual position testing inside of a coroutine so that the server doesn’t have to halt to find the player a clear position.

https://github.com/MrGrim48/gmod_unstuck/blob/master/lua/unstuck/sv_unstuck_table.lua#L22-L23 Instead of sending a string, send an enumeration as an int or something. Won’t change much, but it’s good practice.

[editline]5th September 2017[/editline]

This being said, it’s really nice work, and it’s nice to see all the functions documented. Maybe in the future, someone will make a github postcommit hook that will automatically create wiki documentation based on the information like you provided.

Thanks for the advice guys.
I’m going to go through the code and make the changes where I can.

I’ll start by changing some one the simpler tasks and push those before I attempt to wrap my head around coroutine and changes to the algorithm in finding an new position.

[editline]update[/editline]

  • AddCSLuaFile() removed from autorun file.
  • Client ConVar no cached.
  • Now using convar:GetBool()
  • Switched to MASK_PLAYERSOLID.
  • The (now called) LocalPlayer().UnstuckDebugData is cleared when the convar is set to false.
  • Convar should now be checked serverside before sending net messages.
  • TraceEntity now used in place of TraceHull.

I will work on the other changes tomorrow.

I have added Coroutines with the latest update.
Coroutines are new to me and took a bit of trial and testing to get it to work.

Most, if not all of the other mentioned concerns should be resolved. Maybe.