News | Forum | People | FAQ | Links | Search | Register | Log in
Mapping Help
This is the place to ask about mapping problems, techniques, and bug fixing, and pretty much anything else you want to do in the level editor.

For questions about coding, check out the Coding Help thread: https://www.celephais.net/board/view_thread.php?id=60097
First | Previous | Next | Last
Heh 
See, I've been meaning to make a post on exactly the opposite thing - how to code things in such a way to defend against mappers hacking them.

Would people like to read that, or is denying people the fun of entity hacking just too much? 
 
I guess it depends. What would be your reasoning for preventing hacks? 
Versioning 
If you are making a mod which lots of mappers are going to use, but which isn't final - you may release a new version of this mod in the future, then you have to worry that all of the old maps will work in your new version. In an ideal world all you would need to maintain is the advertised features in your documentation.

Of course, the documentation of what you intended is never finished, and it can be hard enough just maintaining things as you add new code (see: quoth 2.0). If people start using "unadvertised features" in maps, then you can get stuck. You would like to make some change to something you thought was internal, but that will actually break the hack in some existing map.

So I guess the message here is that performing hacks in maps for id1 progs is basically safe because there won't ever be another patch for quake, 1.08 is the end. Since it's going to be unchanging, you can get away with a lot more. With a mod that might in the future. With a mod that might come out with a new version, you probably should colour inside the lines for the most part.

In my defence for suggesting the func_wall trick, it is actually surprisingly hard to break with a mod, since the features it uses (think, nextthink, classname) are all hardcoded engine behaviours. The only thing you could really do to wreck it is change info_notnull to set think/nextthink(which seems pointless for an entity designed to do nothing) or change func_wall to do something that isn't safe after worldspawn like precaching things. So I think it's safe to say that will work in future Quoth versions. 
 
Oh, honestly, I think you're off the hook there. I know about best intentions and all but if someone is leveraging a bug or a hack in your mod that you haven't documented and that breaks in the next version - so sorry, but oh well.

That's not an unreasonable position to take. Use the documented features only or risk getting burned. That's totally fair.

And, really, I'd rather have the freedom that the hacks provide rather than mod authors going through the work to lock everything down. 
Yeah Preach - You Shouldn't Worry About It 
because the person who is making the dodgy hacked map has a copy of the original progs he was using anyway, so if a later version breaks the hack then the player/mapper just has to roll back his version.

And you guys have catered for that by releasing the individual parts of quoth, upgrade by upgrade... 
Defending Against Hacks 
It's reassuring to hear that, I'll worry less about people hacking. As a future warning, don't hack anything to do with path_corners in quoth, they don't work like they used to in quake, and they are going to change...

Even so, I'm gonna post how you do it anyway, because at the bottom there are two alternate applications of the ideas. And also because the second way in here is a really cool trick that I've never seen anyone post about before.


So if you do want to defend against a hack, what can you do? Well, to my knowledge there are three ways of defending against things, from the very specific to the very general. Even if you don't think you'd want to actively prevent a hack in your code, you might find some useful information at the bottom of the post about making entities more friendly to modification while maintaining sensible defaults.

If you are concerned about somebody putting an unhelpful value in one specific fields for a specific entity, then the way to prevent it is simply to set that field during the spawn function to a correct value. For example, if you have a monster which gets enraged after firing 10 missile attacks, and you count the shots fired so far in self.shotcount, you probably don't want people to be able to set .shotcount to 10 so that the monster is enraged straight away, so you can add

self.shotcount = 0;

in the spawnfunction. Although it sounds like a trivial thing there, you can prevent people from giving your entity a use function(assuming it normally lacks one) with

self.use = SUB_Null;

You could even preempt people making your entity shootable and killable by forcing

self.takedamage = 0;
self.th_die = SUB_Null;

...although some people might call that paranoid. This method is in a sense exclusive, you chose which fields you are going to prevent being set. If you start doing too much of this stuff, you should instead look at the third suggestion.


The second idea is one I've never really used because the idea only just came to me after discovering a light.exe feature. Rather than preventing people from custom-setting one field in a particular entity, this one lets you prevent a person setting the field in ANY entites. A good example of something that might warrant this protection is .super_damage_finished . The super_damage_finished field makes an attacking entity do quad damage so long as the map time is less than its super_damage_finished value. It's a bit of an ugly hack using that to get monsters with deadly weapons.

The fix is to rename the field to begin with an underscore, like _super_damage_finished. Any field which starts with an underscore gets stripped from an entity as it is loaded by the engine. This is intended so that light tools could read extra fields on lights, which had sensible names preceeded by underscores. These fields then wouldn't cause warnings if the qc omitted their definition. What we are doing is flipping that, creating a field which is only used by the qc and never from the map, rather than always by the map(via tools) and never in the qc.

For those of you who are c++/java fluent, I've come to think of this trick as creating public and private fields on entities(with respect to the map loading). 
Defending Against Long Posts 
If you want to go further in the other direction, protecting almost all of the fields on a specific class of entity, then there is a pattern you can use in your spawn-function:

void() func_protected =
{
local entity template;
precache_sound ("misc/whine.wav");
template = self;
// we spawn a new entity with all fields blank
self = spawn();
self.classname = "func_protected";

//at this point you copy across all the fields you do want to inherit from the mapper
self.model = template.model;
self.health = template.health;
//...
//then you perform the setup on self as you would expect, eg:

setmodel(self,self.model);
//...
//finally, get rid of the template, since it is no longer needed
remove(template);
}

This gives you complete control on what can be set on your entity.


Although you may not see the need to do this for the reason of protecting an entity, there is a related construction which is more useful. Suppose you have a mod where several "rounds" of a game are played without the map restarting, but you want to reset the arena between rounds. How do you handle resetting something like a breakable wall, which may or may not have been destroyed during the round?

Method 1 would be to have a reset function which desperately tries to reset to zero all of the fields which might have been changed during the previous round on the wall. If it was destroyed, then the model field has probably been cleared, so you will need to store that information somewhere else, and you'll need to recall it's starting health from .max_health, and there are tons of things that could go wrong.

Method 2 is to use adapt the func_protected example above. The original entity placed by the mapper remains entire unchanging for the whole game, and we call this the "template". We split the above spawn function into two parts:
- the bit which precaches all the resources, which will run at worldspawn
- the bit which makes a new entity, copies the required info from the template, and sets it up

The latter function is called every time a new round starts, after the entity from the previous round has been removed. You should of course delete the line which removes the template from this version of the function!

(for c++/java people, this is a quake kinda way for doing the factory paradigm)


Finally, the flip side to the first method of protecting things(one field for a particular entity) is how to make a field customisable while still having a sensible default. Here are some methods for making the health of a shootable func_crate alterable (default:45), from the most common to the most bizzare:

if(!self.health)
self.health = 45;

This just takes the case that the mapper has set nothing, and makes it 45. We could fairly easily improve this to also perform a sanity check for negative health:

if(self.health <= 0)
self.health = 45;

We might instead decide to add whatever value of self.health is set by the mapper to 45.

self.health = self.health + 45;

This will still maintain the default at 45, but it's probably less sensible than the above methods in this case, as it would be confusing. There might be cases other than health where it would be a more sensible way. 
WTF, You Double Agent! 
First you lead us to the dark side of the progs, now you advertise methods of preventing the use of hacks... I can't map without them!

But yeah, mod/version conflicts are a potential problem indeed. Take the differences in model/precache handling in Quoth 1 and 2 for example. I had to come up with a crude way of progs detection in my 768 map in order to make the backpacks in the end arena appear correctly in each version. 
Didn't You Understood ? 
Preach is working for an antivirus program provider company: those guys are hackers one day, and the other they sell you the latest update of their tool... swindler !!! 
 
is it possible to teleport weapons in ID1? 
Nope 
 
Hah 
Didn't stop the Reaperbot... impossible CHEATAR! 
 
I have a map file for quake that I have created with Hammer. I would like to continue working on this map using the QuakeED editor. Is there a tool to convert the map file to the right format? 
 
Hammer. :P 
Do You Mean 
.map? there's an export to .map option in hammer. 
 
The exported map is in a different format. Aguirres tools can handle it but other editors can't I guess. 
Valve 220 Texture Data 
 
Yeah 
It's a pain in the nuts.

Export to .map and use a notepad++ find / replace function?

Probably a long shot though. 
Hm 
possibly try using aguire's map converter utility? if you pass the .map through without actually doing anything, maybe it'll strip out the extraneous data when it parses the file? 
Just Checked 
Between the two and it wouldn't work - not only is there more data, but its also rearranged, and alot of it is single numbers.

Convmap might be worth a shot, yeah, otherwise I'd suggest just continuing with WC for now. 
Func_door - Ignore Enroach? 
Is there a way to get a func_door to close regardless of what it's hitting? Say I want 2 doors to crush anything passing between them - at the moment, the one door immediately retreats back it's starting position while the other works as normal.

Is this fixable? 
Only Way 
is to set:

'wait' '-1'
'toggle' '1'
and manually retrigger to close after opening. 
 
isn't there a "crusher" flag that prevents doors from reopening on impact? 
Not For Normal Quake? 
 
 
The crusher flag is called "dmg" and should prevent the door from reopening if the value is high enough. And if not, give both doors the same "team" field. 
First | Previous | Next | Last
You must be logged in to post in this thread.
Website copyright © 2002-2024 John Fitzgibbons. All posts are copyright their respective authors.