As I slowly gain ground on my not-so-rapid prototype (I can pretty much authenticate and connect, and sit ready to send commands and receive world information), my mind wanders to the meat of the game server, which is vastly more interesting than the boring-but-necessary work of having to write login code.
One of the things that I often think about is the interaction between objects in an MMO, and how the interaction takes place from a code point of view. Though I started to write this post over a week ago, it was a post on a friend's blog that finally made me come back and finish it.
Over five years ago, on this blog, I previously wrote on the idea of how to handle the goings-on in a game world, and how the many objects and the world's rules would deal with them. This was before I learned about Metaplace's extension of Lua with their Trigger functions, or the .NET implementation of Events. As I practice my C#/.NET, and continue to work on MMORF, I realize that my method for dealing with such object interaction is still my preferred one.
I have two examples that I keep in mind when I work out the details of this system, both when I was writing my Metaplace RPG subsystem and now as I work on MMORF. These examples, of course, come from UO, my seminal example of what MMORF should be able to do. The main one is simply picking up an item in the game world.
Attempting to pick up an item in UO can involve a lot more checks than might be otherwise apparent. In no specific order, you need to check: if you're close enough to the object; if the character has something in-hand (not equipped, per se, but already being held/dragged by the mouse cursor); if the object is static, that is, part of the map; if the object is locked down (in someone's house, say); if the character has line-of-sight to the object. I'm likely missing something that UO needs, too, and there could be other checks that a non-UO game might want to make such as "is the object too heavy to be lifted?" (in UO you could move mountains, provided they weren't locked down).
Each of these things are rules of the world. The close-enough check is a variable (it's two tiles in UO). The something in-hand is an oddity in UO, since it's dependent on what can be considered a hidden character slot (the mouse cursor, a "third hand") being full, not either of the character's actual hands. The line-of-sight might be a universal requirement. Because these are all rules that are bolted on based on the design of the game system, the platform needs to support that bolting-on, not hardcoding just distance checks and locked-down checks and the like, but anything a game designer can think of -- you can only pick up this object if you're an elf, if your 12th level, if you have the right device equipped.
Each of these rules would manipulate a "result" object, something at which the framework can look, after everyone has had their say, to determine whether the action should be allowed. Thus, the distance checking rule might verify the user is close enough, and either say "yes, for now" or simply not say "no". The latter is more likely, as picking something up is probably better implemented as allowed-unless-not versus disallowed by default. But just because the distance rulecheck didn't say no doesn't mean that the next rule might not -- oh look, the object is locked down, so THAT rule will say "no", and the action will be prevented.
From my system administration background comes two kinds of permission systems as hinted at above: permissive unless prevented (except when overridden), and not permissive unless allowed (except when overridden). So picking up an item is likely the first case -- you can have it unless the rules say you can't -- but actually connecting to the MMORF game servers is the other way around: you're not allowed on UNLESS you can provide a valid username/password, EXCEPT you'll still be denied if your account has been suspended. The "except" case for picking things up might be the "gamemaster" rule: sure, the object might be too far away or locked down, but I'm a god in this realm, and such little rules don't apply to me. And it's much better to have the "gamemaster" rule as a separate rule that can overrule the "that's too far" rule, instead of having the distance rule say "okay, the character is further than two squares, but he's a gamemaster, so I'll say it's okay". Going this approach would mean that every rule would have to know to check the player's gamemaster status to know how to decide. Instead, having a single gamemaster rule that can overrule is better. It could also be implemented that the world has a function that returns a character's FurthestDistanceCanPickUp(), which the distance rule uses; THAT function might then look to see if the gamemaster flag is set and return "1000"; this would also also allow game worlds where different characters can reach further, because they are playing a race with longer arms, or are reaching with a pole, or reaching with telekinesis. In fact, this is sounding like a much better way to implement such a distance rule, instead of having a single number that a world builder can set. Just because UO has the two-tile reach for everyone doesn't mean the next game will.
Determining that a character CAN pick up an item then leaves the system to worry about what happens if they DO. In UO, it vanishes from the ground or container and goes into this pseudo-inventory slot, and the only thing that is likely to happen is that the character will attempt to drop the item (into his or her backpack, on the ground, on another player), and a whole other set of permission checks will take place. But what about actions that have more consequences than just moving things around? What if picking up the item causes damage to the holder? What if the item's absence from where it was triggers a trap? This starts a whole other line of thinking on the idea of Triggers - that some objects in the world are going to care when any item moves, so a nearby player's screen can update properly, and others when specific items move (moving the idol triggers the fire traps).
The second example I usually refer back to is using an item, but it rehashes a lot of the same ideas on the permission side as picking up an item. It's here where the result of successfully using the item, and the chain of events and rulechecks that take place, can get interesting, specifically (if you know anything about UO) when you go to use a crafting item. There's the obvious "can you reach the item" and "is it your item to use" checks, but then if you are allowed, how about "do you have the skill to use it", "what can you build at your skill level", "what are you going to make", "using what resources", etc. There can be a lot of back and forth with the user as decisions are being made, each question and decision being based on surroundings, inventory, attributes and who knows what else. And after all of the objects who have a say about the choices have said, there's the whole issue of success at crafting the item, based again on attributes, buffs, enchanted tools, magic forges, standing in a pentagram or whatever, each which might have their own say on how successful the character is.
For the most part, anything that the player has their character do is susceptible to modification by the rules, by their environment, by their belongings and by their characteristics, and the MMORF platform needs to be able to accommodate it all.
Showing posts with label triggers. Show all posts
Showing posts with label triggers. Show all posts
Thursday, June 17, 2010
Thursday, April 14, 2005
Triggers and events
While thinking about the scripting language to use (a topic for another day), the idea of event-handling and triggers came to mind. Whatever language I choose to use or write should be able to support them, because I feel that events are a big part of a virtual world.
The best example is speech. A character walks up to the banker and says, "I'd like to access my bank box please." Or he walks onto a magic circle and says a certain word, finding himself teleported to another part of the world. Both of these examples are from Ultima Online, but I'm sure every virtual world has some case of this. Maybe not speech, exactly, but some way for an NPC or object to react to something a character does.
The issue is how to implement it. There are two ways that I can see: each object (which includes PCs and NPCs) has a youJustHeard() function, which is passed a reference to the speaker, and the text spoken; or each speaker has a youJustSaid() function, which takes the text spoken.
In the first case, every object in the world has a listener function. There are really two ways to deal with this: each object, upon creation, must register itself as a listener, to let the world know that it might be interested in things that are said in the world; or whenever something is spoken, the world looks around the speaker to get a list of all the objects within range, and "tells" each of them what was said.
If the world has to check for nearby listeners every time anything is spoken, it should be obvious that there is going to be a lot of this searching. When characters start gabbing in-game, every table, door, dog and character within a given distance is going to be told, whether or not they want to know. In this case, we better hope that we have one hell of an optimized way to get a list of objects within a certain area.
With the registration method, the world would be able to reduce the number of objects that it has to look through, because only objects that can listen are registered. Again, we would still want this to be very optimized, since every character will be in this list, as will many NPCs and probably some inanimate objects.
Both of these methods of listening seem to have a lot of overhead for every word said.
Taking the other approach, it's the responsibility of the speaker to let everyone know what was said. Instead of relying on the server to do all the figuring for finding listeners, the logic for that could be part of the speaker's youJustSaid() script, which would ask the system for a list of things within range (something that seems to be inescapable), and then the speaker's script would call each of the listener's youJustHeard() functions.
At first glance, this seems to be less effective than letting the server do it directly, because the same work is being done (finding a list of listeners, calling each of their functions), but now it's (most likely) being done in an interpreter, which isn't as optimal as a compiled server core. And while that's probably true, having the logic take place in a dynamic spot -- that is, something that the game designer can change -- allows for more flexibility whenever something is spoken.
For instance, perhaps our world allows spells, and instead of clicking an icon or scroll to cast, you simply say the words out loud. As well as having every listener's youJustHeard() being called, we would also want the character to start waving his or her hands, and spell effects to happen, and an apple to appear. Or a certain class of character might be able to open doors with but a word; the doors by themselves have no logic to listen for such things (though it could of course be added), but the speaker's script could not only inform all listeners of the words, but also call the door's use() function. Having the speech handler "local" to the speaker also allows them to control who hears them - they might have a slider that defines how loud they speak (whisper, murmur, talk, yell), or they might be able to speak to one person specifically (telepathy).
All of these things can, of course, be done at the listener's side instead of the speaker's side, and is just as accessible to the world designer, for he or she controls the scripts for everything in the world. And there's no reason that you can't mix the two; the generic "character speaker" script may know how to open doors or cast spells when the character speaks, but there's no need to put the logic for word-activated teleporters in every character when the teleporter itself can have that functionality coded into its listener script. But with this approach, we're saying that it's still the speaker's responsibility to let the teleporter know that something is said -- and what it does with that is up to it.
A third possibility, which I'm not going to entertain, is a "speech parser", which looks at everything everyone says and decides how to handle it. If a sentence contains the word "bank", then search for any bankers nearby and tell them. If one of the four magic words in the game are spoken, check the character is in one of the few places that it matters.
This is very limiting. It basically hard-codes these trigger words, requiring a list of words and their effectual area or audience. The parser might also be responsible for making the effect happen, instead of calling a function on some other object -- again, very awkward.
So, speaker or listener. A decision needs to be made... but not so fast. Triggering and events based on speech is only one possibility. What about the shopkeeper that sees you steal something? What about the citizen that sees you attack another? What about the guard that sees you discard items on the ground - littering!
Speech is only one of the generic actions that we might want world objects to react to. NPCs and monsters should likely defend themselves when you attack them; guards might arrest you if you move somewhere you shouldn't; and the shopkeeper certainly shouldn't sit idly while you get his inventory without paying.
But again, who should handle this? If my character attacks a monster, then it's pretty easy to let that monster (and its beingAttackedBy() function) know this. But what about onlookers? Maybe another monster will step in if you attack its buddy, or a nearby NPC will come to aid you.
If I drop an item on the ground, do I tell every object about this put action I just did, or do I broadcast a "I just put X at Y" message and let the "listeners" do what they will with that knowledge (fined for littering, beggars run over to pick it up). Note that depending on the game interface, this might also "push" a look action to some clients so they now know the item's there, or the world has a constant look going. But it's different to see an object suddenly appear on the ground, and to know that character X put it there. Or at least it should be.
I don't think I've convinced myself either way on who should take care of these messages about the events in the world. I'm hoping as I continue to mull over it that I can find an example that definitively says that one method is better than the other (and I equally hope I don't find two examples that contradict).
The best example is speech. A character walks up to the banker and says, "I'd like to access my bank box please." Or he walks onto a magic circle and says a certain word, finding himself teleported to another part of the world. Both of these examples are from Ultima Online, but I'm sure every virtual world has some case of this. Maybe not speech, exactly, but some way for an NPC or object to react to something a character does.
The issue is how to implement it. There are two ways that I can see: each object (which includes PCs and NPCs) has a youJustHeard() function, which is passed a reference to the speaker, and the text spoken; or each speaker has a youJustSaid() function, which takes the text spoken.
In the first case, every object in the world has a listener function. There are really two ways to deal with this: each object, upon creation, must register itself as a listener, to let the world know that it might be interested in things that are said in the world; or whenever something is spoken, the world looks around the speaker to get a list of all the objects within range, and "tells" each of them what was said.
If the world has to check for nearby listeners every time anything is spoken, it should be obvious that there is going to be a lot of this searching. When characters start gabbing in-game, every table, door, dog and character within a given distance is going to be told, whether or not they want to know. In this case, we better hope that we have one hell of an optimized way to get a list of objects within a certain area.
With the registration method, the world would be able to reduce the number of objects that it has to look through, because only objects that can listen are registered. Again, we would still want this to be very optimized, since every character will be in this list, as will many NPCs and probably some inanimate objects.
Both of these methods of listening seem to have a lot of overhead for every word said.
Taking the other approach, it's the responsibility of the speaker to let everyone know what was said. Instead of relying on the server to do all the figuring for finding listeners, the logic for that could be part of the speaker's youJustSaid() script, which would ask the system for a list of things within range (something that seems to be inescapable), and then the speaker's script would call each of the listener's youJustHeard() functions.
At first glance, this seems to be less effective than letting the server do it directly, because the same work is being done (finding a list of listeners, calling each of their functions), but now it's (most likely) being done in an interpreter, which isn't as optimal as a compiled server core. And while that's probably true, having the logic take place in a dynamic spot -- that is, something that the game designer can change -- allows for more flexibility whenever something is spoken.
For instance, perhaps our world allows spells, and instead of clicking an icon or scroll to cast, you simply say the words out loud. As well as having every listener's youJustHeard() being called, we would also want the character to start waving his or her hands, and spell effects to happen, and an apple to appear. Or a certain class of character might be able to open doors with but a word; the doors by themselves have no logic to listen for such things (though it could of course be added), but the speaker's script could not only inform all listeners of the words, but also call the door's use() function. Having the speech handler "local" to the speaker also allows them to control who hears them - they might have a slider that defines how loud they speak (whisper, murmur, talk, yell), or they might be able to speak to one person specifically (telepathy).
All of these things can, of course, be done at the listener's side instead of the speaker's side, and is just as accessible to the world designer, for he or she controls the scripts for everything in the world. And there's no reason that you can't mix the two; the generic "character speaker" script may know how to open doors or cast spells when the character speaks, but there's no need to put the logic for word-activated teleporters in every character when the teleporter itself can have that functionality coded into its listener script. But with this approach, we're saying that it's still the speaker's responsibility to let the teleporter know that something is said -- and what it does with that is up to it.
A third possibility, which I'm not going to entertain, is a "speech parser", which looks at everything everyone says and decides how to handle it. If a sentence contains the word "bank", then search for any bankers nearby and tell them. If one of the four magic words in the game are spoken, check the character is in one of the few places that it matters.
This is very limiting. It basically hard-codes these trigger words, requiring a list of words and their effectual area or audience. The parser might also be responsible for making the effect happen, instead of calling a function on some other object -- again, very awkward.
So, speaker or listener. A decision needs to be made... but not so fast. Triggering and events based on speech is only one possibility. What about the shopkeeper that sees you steal something? What about the citizen that sees you attack another? What about the guard that sees you discard items on the ground - littering!
Speech is only one of the generic actions that we might want world objects to react to. NPCs and monsters should likely defend themselves when you attack them; guards might arrest you if you move somewhere you shouldn't; and the shopkeeper certainly shouldn't sit idly while you get his inventory without paying.
But again, who should handle this? If my character attacks a monster, then it's pretty easy to let that monster (and its beingAttackedBy() function) know this. But what about onlookers? Maybe another monster will step in if you attack its buddy, or a nearby NPC will come to aid you.
If I drop an item on the ground, do I tell every object about this put action I just did, or do I broadcast a "I just put X at Y" message and let the "listeners" do what they will with that knowledge (fined for littering, beggars run over to pick it up). Note that depending on the game interface, this might also "push" a look action to some clients so they now know the item's there, or the world has a constant look going. But it's different to see an object suddenly appear on the ground, and to know that character X put it there. Or at least it should be.
I don't think I've convinced myself either way on who should take care of these messages about the events in the world. I'm hoping as I continue to mull over it that I can find an example that definitively says that one method is better than the other (and I equally hope I don't find two examples that contradict).
Subscribe to:
Posts (Atom)
