The Meta Hack
#212 posted by negke on 2010/07/30 14:02:53
Just in, the latest discovery in the hacking business. A trigger_command in id1! Well, sort of.
As it turns out, it's actually possible to execute console commands from within the map by simply adding a linebreak followed by a string of commands in the "map" field of a trigger_changelevel. For example:
"classname" "trigger_changelevel"
"spawnflags" "1"�// no_intermission has to be set for this to work
"map" "\ngod; impulse 9"
The trick works because the trigger uses stuffcmd to change the level which we can exploit to run our own commands. However, there's a catch. In the engine, we have a hardcoded line that prevents the changelevel from being issued multiple times, which means this hack can only be used in this form once, and afterwards the player can't proceed to the next map after exiting the level normally. It will just stay in intermission mode forever - one might regard it as acceptable after a large map with no follow-up; or with a Shub ending.
Even in this state, the hack has a lot of potential, especially for mischief. You could, for instance, rename the player to "Asshole" and make his client connect to a server. :D Or mess with his entire config, unbind all keys etc. But since we're all nice and friendly people (right? :P), rather things like changing map-related cvars come to mind, e.g. setting r_wateralpha for the glass hack or adjusting fog values.
Luckily there's a sort of workaround for the changelevel/next map problem mentioned above. After changing cvars for map compatibility, you can add a "restart" or "map yourmap" command to the end of the string to reload the server with the exit trigger intact. Or, if you set any non-permanent stuff (e.g. cheat codes or gravity), you have to add commands to save and reload the game. ->
Following this line of thought, Preach came up with two very useful setups the hack allows us to use.
The first one is a kind of security measure to make sure all necessary commands are executed before the map is played. Set "serverflags 256" in the middle of your command string and use a func_episodegate with spawnflags 256 as a detector. If the episodegate doesn't spawn, the player (presumably) hasn't run the map yet and needs all the stuffcmd set; if it spawns, there's no need to restart.
The other one is an actual autosave option, albeit not a very transparent one. Place trigger_changelevel entities as checkpoints and set their map fields to "\nsave autosave; wait; load autosave" (you could also use "quick", or better yet, the mapname instead of "autosave"). This will save and instantly reload the game on each checkpoint - however, it's quite 'bumpy' and may even cause annoying delays in larger maps and engines with long loading times (DP). In this sense a button-controlled checkpoint system would probably be the best solution.