Yeah Preach - You Shouldn't Worry About It
#8235 posted by RickyT33 on 2009/01/23 01:11:57
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
#8236 posted by Preach on 2009/01/24 01:44:21
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
#8237 posted by Preach on 2009/01/24 01:45:35
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!
#8238 posted by negke on 2009/01/24 10:07:57
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 ?
#8239 posted by JPL on 2009/01/24 11:32:28
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 !!!
#8240 posted by anonymous user on 2009/01/27 16:48:58
is it possible to teleport weapons in ID1?
Nope
#8241 posted by Spirit on 2009/01/27 17:20:37
Hah
Didn't stop the Reaperbot... impossible CHEATAR!
#8243 posted by Ankh on 2009/01/30 18:05:39
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?
#8244 posted by JneeraZ on 2009/01/30 18:52:38
Hammer. :P
Do You Mean
#8245 posted by necros on 2009/01/30 19:35:30
.map? there's an export to .map option in hammer.
#8246 posted by Ankh on 2009/01/30 22:47:57
The exported map is in a different format. Aguirres tools can handle it but other editors can't I guess.
Valve 220 Texture Data
#8247 posted by RickyT33 on 2009/01/30 23:08:43
Yeah
#8248 posted by ijed on 2009/01/31 00:26:16
It's a pain in the nuts.
Export to .map and use a notepad++ find / replace function?
Probably a long shot though.
Hm
#8249 posted by necros on 2009/01/31 00:28:49
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
#8250 posted by ijed on 2009/01/31 01:37:58
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?
#8251 posted by JneeraZ on 2009/01/31 23:11:39
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
#8252 posted by necros on 2009/01/31 23:28:23
is to set:
'wait' '-1'
'toggle' '1'
and manually retrigger to close after opening.
#8253 posted by metlslime on 2009/02/01 02:45:57
isn't there a "crusher" flag that prevents doors from reopening on impact?
Not For Normal Quake?
#8254 posted by necros on 2009/02/01 02:52:31
#8255 posted by negke on 2009/02/01 10:00:06
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.
#8256 posted by JneeraZ on 2009/02/01 12:00:36
The "team" field didn't seem to work. The "dmg" is set at 10,000 so that should be high enough.
I dunno, I added a new spawnflag (SF_CRUSHER) and added a quick IF condition to the "door_blocked" function. That seemed to do it.
Crusherflag
#8257 posted by Preach on 2009/02/01 15:01:35
Necros had it right first time about the crushing behaviour, it comes from setting wait to a negative number. Quoth the source(door_blocked):
// if a door has a negative wait, it would never come back if blocked,
// so let it just squash the object to death real fast
if (self.wait >= 0)
{
if (self.state == STATE_DOWN)
door_go_up ();
else
door_go_down ();
}
So as long as self.wait is less that 0 the door doesn't change direction.
QMLE
#8258 posted by anonymous user on 2009/02/05 18:20:15
After several bumps in Quake modelling I won't get used to the fact a modelframe exported to dxf has a unique vertice and trianglecount.
It may be my poor knowledge of triangles and vertices but when I import this file in another editor and try to export them back to Qmle the vertices turn out higher and triangles are doubled.
I know it has something to do with triangles turned into quads and vice versa, but why doesn't these counts stay the same?
I don't add anything so that can't be the reason.
Is it the 8bit configuration, and nowadays 3Dstudio's 16 or 32bit?
Don't Think So
#8259 posted by ijed on 2009/02/05 18:28:05
I've never had the issue, to be honest, but many do.
If you're working with editable poly then the 7 key isn't showing you a valid vertice / face count - change to mesh while working and the number practically doubles.
As a typical safety thing I'd export the model, open it, select all verts and do a weld within a distance of 0 then export and see if it opens.
If not then the model is bad from the start - having a vertex spike, multiple vertices in the same position etc.
|