27 Feb, 2010, KaVir wrote in the 61st comment:
Votes: 0
donky said:
This thread is more a collection of opinions than anything else. I don't think I have gained anything from it, and I can't see it going anywhere different.

Well discussing program languages is a bit like discussing politics or religion; most people have already made up their minds before they start posting.

I don't think there's any "right" answer to be honest, but I do find it interesting to see the views of people who have actually used one approach or another. In particular, I found it interesting that Orrin uses a lot of scripting and says he's found it more of a hindrance than a help, while I use no scripting and find that to be a hindrance. Is the best solution some sort of balance between the two? Or is it just that the grass is always greener on the other side?
27 Feb, 2010, Ix wrote in the 62nd comment:
Votes: 0
KaVir said:
donky said:
This thread is more a collection of opinions than anything else. I don't think I have gained anything from it, and I can't see it going anywhere different.

Well discussing program languages is a bit like discussing politics or religion; most people have already made up their minds before they start posting.

I don't think there's any "right" answer to be honest, but I do find it interesting to see the views of people who have actually used one approach or another. In particular, I found it interesting that Orrin uses a lot of scripting and says he's found it more of a hindrance than a help, while I use no scripting and find that to be a hindrance. Is the best solution some sort of balance between the two? Or is it just that the grass is always greener on the other side?


I think that a balance would definitely be a good way to go about it, and being able to change things at run-time would be good. Like for example, scripts for weapon attacks, with a generic standard script that just does 1-6 damage, and maybe have a script for a special fire-based sword that adds an extra 1-4 fire damage or the such.

Being able to script a weapon would give you more control over weapons than typical MUD systems do, of course the tradeoff would be that in order to set a weapon's damage to something other than say the standard 1-6 you would probably end up writing a script just for that weapon. So I mean it has its advantages and its flaws already, cause you could end up with over 1000 weapon scripts for 1000 weapons.

So more freedom is good, but too much freedom is bad.
27 Feb, 2010, Orrin wrote in the 63rd comment:
Votes: 0
KaVir said:
In particular, I found it interesting that Orrin uses a lot of scripting and says he's found it more of a hindrance than a help, while I use no scripting and find that to be a hindrance. Is the best solution some sort of balance between the two? Or is it just that the grass is always greener on the other side?

I think in my case a large part of it is that I work with a mud partner who is about as clueless as it's possible to be when it comes to programming. In this instance, the python scripting system in NM (and I mean scripting in the sense of the way python code can be generated and attached to objects, all from within the game itself) has proved to be more of a hindrance than a help.

At the start of the project Wade would post up elaborate chains of dialogue and actions on our staff forum and expect me to implement them in script. I found this extremely tedious and not always straightforward. This didn't last long, and we quickly moved to a method of working where I would write an example script and try to explain how it worked and then he would copy and adapt as necessary. This still leads to frustration when I have to debug his scripts, but it's an improvement.

In our case what we really need is a kind of point and click system for him where he can assign pre-made behaviours to game objects and construct mob dialogue/actions. Whether this is the universal "best" approach I don't know, but I'd be inclined to use it in future projects.
27 Feb, 2010, David Haley wrote in the 64th comment:
Votes: 0
That comes down to the approach of capturing common patterns that I was talking about. I think that many kinds of interaction can be reduced to a relatively small number of units that you combine. From watching the travails of reconciling builder wishes against developer time, I am quite inclined to believe that this is the way to go for future projects as well. I think it's also worth considering, sometimes, if it's not possible to simplify the builder wishes to something manageable; in other words, occasionally one needs to prioritize and say "yes, that'd be nice, but no, it's not going to happen soon".

It's too bad that it's hard to find examples of this quickly, but the Jedi Knight series of games had a very interesting approach with their language called COG. You can think of it as a class system of some sort, where you have this 'cog' thing, and you can attach them to game entities (actors, objects, wall surfaces, and so forth). This would drive the vast majority of level behavior. The elevator example I spoke of earlier was very directly inspired by this. The workflow was something along these lines:

- Create start and end position nodes.
- Decide which surface(s) represent the "control".
- Decide which object is the "lift" (the thing that moves).
- Attach the "2stop_button_elevator" cog to the button surfaces.
- Parametrize the above choices into the cog.

The cog itself looks something like this: (this is utter pseudo-code as I don't remember the syntax)

on_use(button_surface) {
if lift.position == start_node {
begin_move(lift, end_node);
} else if lift.position == end_node {
begin_move(lift, start_node);
} else {
/* do nothing, it's busy already */
}
/* you could also do something different based
* on [i]which[/i] button were pressed */
}


So the idea is that somebody (presumably programmers or script-savvy level designers) would write COGs and make a library of them available. Level designers, as they want to add interactive content, can comb through this (rather large) library of scripts, and attach them to their objects. All they had to do was to understand the parameters and input them. Often, you could figure out which COG to use simply by going to a level that did something you liked and seeing which COG they used there.

COG was used for everything from elevators to triggering actor appearances to throbbing lights in cantinas based on timers. In fact, COG even controlled weapons to a large extent, determining what happened when a given weapon was fired (typically something like "create a bullet along this movement vector") and what happened when the bullet made contact with an actor or surface.

Of course, COG itself was fairly limited as a language (it was vaguely C-ish in some ways, and you had simple loops). The interesting functionality was usually implemented in the JK core (presumably in C or C++) and in fact you could see this functionality evolve from JK to JK:Mysteries of the Sith. An example was the carbonite gun, which temporarily froze or slowed down victims; you simply couldn't do that in JK because there was no way to affect those things in the core whereas the MotS core added that function to COG.

This was a long time ago and I've been intrigued by it ever since. The end result was that level designers often didn't have to write much code, and you could have teams of complementary people. I was actually starting to implement something like this on my own MUD although that somewhat fizzled as the overall MUD started losing popularity and staff went their separate ways. But if I were to do it again, I would without a doubt take an approach like this one.

On a higher level I think one of the issues that has been plaguing Dikuland for some time now is simply that areas are supposed to be such self-contained entities, with one person doing everything and only at the end attaching it into the rest of the world. There is very little notion of a catalog of useful things, be it for scripting or even for common objects or actors. (How many times has the "wooden table" been created, even on a single MUD?) The current paradigm requires more discipline on the part of the builder staff to avoid creating redundant objects. Well, anyhow, </tangent> as they say.
27 Feb, 2010, Tyche wrote in the 65th comment:
Votes: 0
JohnnyStarr said:
What about an OLC that redefines the user input, as sort of a sugar coated baby language?
For example, you could put more work into the olc interface, so that it enforces the builder to only use
valid syntax, which is converted to a "real" scripting language, then stored on disk somewhere.


Good idea. I believe that implementing an OLC which constructs LPC code is the approach that Cratylus used in DeadSouls. He's posted on this thread and can surely provide more insight. I know the OLC in ColdCore uses that approach. It walks the builder through a dialog that builds an object and populates its properties. It's very primitive though as most of the participants on the project were programmers and ignored it in preference to programming the objects themselves.

I don't really consider builders creating data different than creating code. It's all data.
28 Feb, 2010, donky wrote in the 66th comment:
Votes: 0
David Haley said:
I think it shows off an extraordinarily cool usage of co-routines, namely being able to have the illusion of "pausing" the function while you wait for input. The engine takes care of resuming stuff under the hood.

This is a rather isolated use case. In my experience, when used in game logic, there are many more factors that can come into play. So I have at one point written something like the following:

def ManageAction(self, info):
self.PrepareAndStartAbilities(info.targetID)
self.FollowObject(info.targetID, range=self.targetingRange * 0.9)
self.WaitUntilWithinRange(self.targetingRange * 0.9)
self.AcquireTarget(info.targetID)
while True:
Sleep(1500)
self.CheckAndMaybeFixActionConditions(info)
self.MaybeDoSomeSpecialShit(info)
Keep in mind that it is a contrived version of the original. Every entity in the game would have that function running on its behalf when it was focusing on an activity.

But this is more like what the function really looked like:

def ManageAction(self, info):
# This function is not scheduled right after creation so treat the situation
# as if blocking had occurred.
if not self.CheckActionValid(info):
return

self.PrepareAndStartAbilities(info.targetID)
self.FollowObject(info.targetID, range=self.targetingRange * 0.9)
if not self.WaitUntilWithinRange(self.targetingRange):
return # Internal CheckActionValid(info) call.
if not self.AcquireTarget(info.targetID):
return # Internal CheckActionValid(info) call.
# ..
while True:
Sleep(1500)
if not self.CheckActionValid(info):
return
if not self.CheckAndMaybeFixActionConditions(info):
return # Internal CheckActionValid(info) call.
self.MaybeDoSomeSpecialShit(info)
Let's take a look at a possible CheckActionValid:

def CheckActionValid(self, actionInfo):
# Superceded by another action. Or the action has been shut down.
if self.actionInfo is not actionInfo:
return False
# Entity is no longer valid. Killed? Removed from game?
if self.released:
return False
if actionInfo.targetID:
# Just in case we did not get notified.
if not self.environment.IsPresent(actionInfo.targetID):
return False
# …
return True
The fact is that when you have many objects interacting, and many coroutines possibly interleaved with each other, the potential for unforseen conditions to arise is extremely high. Here's someone else trying to reconcile checking for relevance after blocking. You can read more detail on my thoughts on this matter in his comments (see the long comment that talks about generator coroutines). In order to develop stable code within this model, you need to understand more than just the code you are writing. Even if someone else writes lots of methods like CheckActionValid, chances are the builder is going to be pasting it in religiously, rather than with a proper understanding.

Now as I mention in my comment, if your coroutines are implemented properly and you can kill them, then you can with a little work remove all these checks and just kill the logic directly in relevant situations. For the action management, that would be when the entity chooses to engage in a different action (or no action at all). This might happen when the target leaves the vicinity. It might happen when the entity is destroyed. With the ability of the entity to get notified of these situations, they are easily covered. If they all stop the entities current action, then that's a good central point to do the killing.

Then there are infinite loops. Let's say that a builder has written a long running loop that does not yield to the other coroutines. Do things suddenly lock up? If the coroutine implementation has a way of setting a bound on how long a given coroutine can run cooperatively before it is decided it is not actually cooperating and needs to be preempted, then you can deal with this as well in a way that makes it more builder approachable.

This is just a sample of the problems that arise. If I would not let builders write scripts that didn't use coroutines, I certainly wouldn't let them write ones that did.
28 Feb, 2010, donky wrote in the 67th comment:
Votes: 0
Tyche said:
I don't really consider builders creating data different than creating code. It's all data.

Sure they might be all data, but when they are creating code that may also be data, the usefulness of the "encoded" data varies depending on the code that defines it.

Let's say I want to modify something related to all entities that use a certain form of aggression. I don't want to do it to the state of the entities that happen to be loaded at that time, I want to do it for all entities in such a way that their definition will be modified and anyone who edits them will work from the new version of the definition.

Now let's say a builder has defined one of those entities using code within a larger script file that also defines a room and other various aspects of that room. The builder has been able to shape code in such a way to get the room and its contents resembling a certain desired appearance, how would you do this?
28 Feb, 2010, Tyche wrote in the 68th comment:
Votes: 0
donky said:
Let's say I want to modify something related to all entities that use a certain form of aggression. I don't want to do it to the state of the entities that happen to be loaded at that time, I want to do it for all entities in such a way that their definition will be modified and anyone who edits them will work from the new version of the definition.


If I wanted that behavior, then I'd implement a version system for the code. New or edited entities would point to the latest version, while existing ones would point to whatever version it was last edited at or loaded. I don't think that behavior is desirable myself, because I would want changes to be reflected across all entities using a particular code or data. If I wanted entities to behave differently I'd create a new aggression form.

I might implement a version system for other practical reasons… tracking changes, repairing broken data, blame, etc.

donky said:
Now let's say a builder has defined one of those entities using code within a larger script file that also defines a room and other various aspects of that room. The builder has been able to shape code in such a way to get the room and its contents resembling a certain desired appearance, how would you do this?


Sorry I don't understand the scenario here.
28 Feb, 2010, donky wrote in the 69th comment:
Votes: 0
Tyche said:
donky said:
Let's say I want to modify something related to all entities that use a certain form of aggression. I don't want to do it to the state of the entities that happen to be loaded at that time, I want to do it for all entities in such a way that their definition will be modified and anyone who edits them will work from the new version of the definition.


If I wanted that behavior, then I'd implement a version system for the code. New or edited entities would point to the latest version, while existing ones would point to whatever version it was last edited at or loaded. I don't think that behavior is desirable myself, because I would want changes to be reflected across all entities using a particular code or data. If I wanted entities to behave differently I'd create a new aggression form.

Let me try again. What I was wondering about, was what exactly you meant by: "I don't really consider builders creating data different than creating code. It's all data." I interpret it as handwaving away the difference between builders creating content by defining it with code, or entering the content in a form where it is stored as data (perhaps in database tables).

But I do not see these two things as being the same. Sure, in both cases the data may serve its purpose, appearing in the game and working as specified. But the barrier to treating the code as data, and manipulating it, is prohibitive. If it were actually data, you could transform it or datamine it readily. I have had many useful situations in the past where I have been able to do this.

For instance, the workings of items were defined in the database. An item could have effects, where the workings of an effect was composed of expressions which were built out of other expressions. You know the drill. But because this logic was defined as data, I could do things with it. I could programmatically identify what expressions were offensive and then taint effects and therefore items as being offensive to use. I could isolate a subset of effects as working in a certain way, so that logic which needed to work in a timely fashion could look up an effect in the generated set that identifies these effects. I could even generate code to be used in the live game in place of the evaluated expressions.

Now consider this data defined as code, how would you do these sorts of operations? Parse the source code and analyse the AST? Try and reverse engineer the data out of it?
28 Feb, 2010, David Haley wrote in the 70th comment:
Votes: 0
donky said:
This is a rather isolated use case.

Actually, getting user input and doing more stuff based on it, or delaying a command until some time has gone by, are really quite common use cases in my experience, and current solutions, at the least in Dikuland, tend to be terrible.

If your point was that you need to be careful when writing such code, that goes without saying, but hey. You have the same problem with any form of delayed execution.

donky said:
Then there are infinite loops. Let's say that a builder has written a long running loop that does not yield to the other coroutines. Do things suddenly lock up? If the coroutine implementation has a way of setting a bound on how long a given coroutine can run cooperatively before it is decided it is not actually cooperating and needs to be preempted, then you can deal with this as well in a way that makes it more builder approachable.

Lua lets you deal with this.

donky said:
This is just a sample of the problems that arise. If I would not let builders write scripts that didn't use coroutines, I certainly wouldn't let them write ones that did.

This is a pretty obvious statement at least as far as I can tell, not sure what the point is. :wink:

Anyhow this wasn't about builders, really, although letting builders put in scripted delays in, e.g., mob dialog is another very common use case and the current solution (e.g., mpsleep) is again quite terrible.
28 Feb, 2010, donky wrote in the 71st comment:
Votes: 0
David Haley said:
donky said:
This is a rather isolated use case.

Actually, getting user input and doing more stuff based on it, or delaying a command until some time has gone by, are really quite common use cases in my experience, and current solutions, at the least in Dikuland, tend to be terrible.

If your point was that you need to be careful when writing such code, that goes without saying, but hey. You have the same problem with any form of delayed execution.

No, I meant it was a rather isolated use case :lol:

That the actions you were doing were ones where the user was not participating with others.
28 Feb, 2010, Scandum wrote in the 72nd comment:
Votes: 0
David Haley said:
Anyhow this wasn't about builders, really, although letting builders put in scripted delays in, e.g., mob dialog is another very common use case and the current solution (e.g., mpsleep) is again quite terrible.

I don't see what's terrible about mpsleep, it's quite useful in several instances.
28 Feb, 2010, David Haley wrote in the 73rd comment:
Votes: 0
donky said:
That the actions you were doing were ones where the user was not participating with others.

I'm not sure why this matters; invariably people do things other than interact exclusively with others. Even when interacting with others, timers are extremely useful. Then you've got the whole administrative side of things; the function I posted was a builder's command, not a gameplay command.

Scandum said:
I don't see what's terrible about mpsleep, it's quite useful in several instances.

I should have been more clear: the implementation is what's not good, not the concept. Indeed the concept is very useful, even though apparently not everybody agrees on that. :wink:
01 Mar, 2010, Tyche wrote in the 74th comment:
Votes: 0
donky said:
Let me try again. What I was wondering about, was what exactly you meant by: "I don't really consider builders creating data different than creating code. It's all data." I interpret it as handwaving away the difference between builders creating content by defining it with code, or entering the content in a form where it is stored as data (perhaps in database tables).


Yes, but handwaving is good. Maybe your objection is to not enough BS along with the handwaving? ;-)

donky said:
But I do not see these two things as being the same. Sure, in both cases the data may serve its purpose, appearing in the game and working as specified. But the barrier to treating the code as data, and manipulating it, is prohibitive. If it were actually data, you could transform it or datamine it readily. I have had many useful situations in the past where I have been able to do this.

For instance, the workings of items were defined in the database. An item could have effects, where the workings of an effect was composed of expressions which were built out of other expressions. You know the drill. But because this logic was defined as data, I could do things with it. I could programmatically identify what expressions were offensive and then taint effects and therefore items as being offensive to use. I could isolate a subset of effects as working in a certain way, so that logic which needed to work in a timely fashion could look up an effect in the generated set that identifies these effects. I could even generate code to be used in the live game in place of the evaluated expressions.

Now consider this data defined as code, how would you do these sorts of operations? Parse the source code and analyse the AST? Try and reverse engineer the data out of it?


Let's say the relationship between data and code is not so rigidly defined in many existing muds like mushes, cold, lpmuds, etc.
Maybe some psuedo code/data examples might help illustrate the problems I think you may be referring to. I'll dub this "script language" MDL or Mud Definition Language.

Consider the following three rooms:
object room1
property description "There is a tree here."
method describe
player:tell description

object room2
property description "There is a tree here. Its leaves glisten in the [day]sunlight[/day][night]moonlight[/night]."
method describe
player:tell(utility:mudmarkup(description))

object room3
property description "There is a tree here."
method describe
if system:time is 'day
player:tell "There is a tree here. Its leaves glisten in the sunlight."
else
player:tell "There is a tree here. Its leaves glisten in the moonlight."


Presumable there's some engine present and protocol that rooms have description properties and describe methods. Now if you wished to data mine for the descriptions of rooms, room1 poses no problem as it's just data, room2 poses a slight problem in that it's data embedded with code, and room3 poses a greater problem in that it's description is in some situation transformed by code with embedded data. Does that illustrate your problem?

MDL might be entered directly via a text editor and uploaded, perhaps an online text editor, maybe it's generated from a GUI building application, or the net result of a series of mud commands like…
$ @create object room2
$ @addprop room2 description "There is a tree here. Its leaves glisten in the [day]sunlight[/day][night]moonlight[/night]."
$ @addmethod room2 describe player:tell(utility:mudmarkup(description))


It looks like it could be YAML (a datastorage format) and looks a bit like Python (a language). Is it data or is code?

It might be that I store everything in s-expressions.

room1
description "There is a tree here."
describe
player.tell description


s(:defobj,
:room1,
s(:defprop,
:description,
s(:str, "There is a tree here.")),
s(:defmeth,
:describe,
s(:scope,
s(:call,
s(:global, :player),
:tell,
s(:arglist, s(:ivar, :description))))))


I can parse that format mining for data. I could generate a GUI dialog from it. I could generate MUSH or MOO commands. I could generate MDL, Python, Ruby, C++ or something else.

Just some thoughts and BS. ;-)
10 Mar, 2010, shasarak wrote in the 75th comment:
Votes: 0
Coming to this discussion very late, but I thought I might mention an approach I favour. If the MUD is written in an OO language that supports reflection, then a possible approach is to allow builders to write OO code, but to have stringent restrictions on exactly what that code can do. So, for example, certain methods on certain classes may be blocked from being invoked, certain ones may be blocked from being overridden on subclasses, certain classes may not be subclassed, etc. The key is that these restrictions are not absolute - they depend on the authorof the code attempting to perform the action.

In other words, it's not just a question of a method being "private"; it needs to behave as either private or public, depending on who is writing the code that tries to call it. Similarly with the ability to override methods, create subclasses, etc.

In addition to this, you could also place checks in certain methods that are evaluated at run-time, and which allow or disallow certain argument values, depending on the author of the code that invoked it.

Different levels of immortal would be given different levels of access. So a very basic "builder" type might not be allowed to subclass anything, and would even be blocked from setting most of an object's values, he would simply be able to create instances of existing classes and string them together. A slightly more advanced builder might be able to configure the values of an object's properties, but only within specified ranges (so he might be able to create an instance of class Orc, and set its level within the range 2-6, but not set its hit points independently of its level). More advanced again, and you might be allowed to subclass standard classes and extend their behaviour (e.g. subclass Sword to create special behaviours when the sword hits something) but the methods he could call within the modified behaviour sections are still strictly limited - there would (e.g.) be a cap on how much damage could be inflicted by the new behaviour; and so on.

This system is certainly complex, but I think it's a good balance between allowing builders flexibility through use of code, and locking down exactly what they can do if they try to code something stupid or malicious.

It's probably fair to say that there will be some builders who simply won't want to code or script at all, and you probably need an entirely different system to cater for them! That might be based on an in-game approach where you create things like monsters and weapons, tell the monster to pick up the weapon and wield it, and then type "save" to save the state of the room and its contents. Or it could be based on some off-line method such as editing XML files to represent objects and their attributes and contents.
60.0/75