Dream Maker
- Fixed: The icon-editor was sometimes failing to import pixmaps correctly.
(Air Mapster)
- Fixed: a null variable on the left-hand side of the -= operator was
misbehaving. Example:
var/v
v -= 3 //should end up as -3, but it was 3 instead
- Fixed: PayDimes() and SendPage() were mistakingly being classified as
special instructions by the compiler and were therefore behaving strangely when
called without an explicit source variable (like src.PayDimes()). (Guy T.)
- Fixed: DM can now handle paths beginning with tilde (for example
"~/data/users.sav"). It replaces the tilde with the user's home directory,
if one is defined. This is primarily of use in a UNIX environment when you
need to access data files from a CGI application. (Deadron)
- Fixed: length(file(...)) was not converting the file path from canonical to
native format.
- Fixed: html_encode() was producing a mangled escape sequence
for the single quote character.
- Fixed: Division of an icon by a number now has the same effect as
multiplication by the inverse of that number. It was mistakingly
nulling out the icon.
- Fixed: Some statements using division were generating
incorrect error messages from the compiler ("bad variable").
- If you have ever peeked inside a .dmp map file, you may have seen the
syntax used by the object instance editor. That same syntax now works in
DM. It's a handy way to create an instance of an object type with a few
small modifications. Example:
mob
contents = newlist(
/obj {name = "sock"; desc = "This sock stinks."},
/obj {name = "shirt"; desc = "This shirt has kinks."}
)
- You can now define a variable in the initialization of a "for" statement.
Example:
mob/verb/test()
for(var/obj/weapon/W in usr)
usr << W
The variable W in this example is only defined within the scope of the "for"
statement. That's a slight improvement over C++ in my opinion, where W
would still exist in the code following the "for" loop.
- The < and > relative comparison operators now work with text
strings. The comparison is case sensitive (just like sortText()).
- Added world.visibility. Previously, the value of world.hub could be
manipulated to make the world invisible in the hub (setting it to -1), but
that had the disadvantage of preventing the world from having a hub id for
other purposes (like accessing client-side savefiles). Now the recommended
method is to set world.visibility to 0 if you want the game to be
unadvertised.
- Added client.command_prompt and client.command_text. These control the
command line prompt message and the default text placed inside the editing
box. Example:
client
command_prompt = "Instruct me, oh master!"
command_text = "say "
- The preferred method for turning on macro mode is now:
client/command_text = ".alt "
Similarly, to turn on chat mode do this:
client/command_text = "say "
And the preferred method for faking a standard MUD command line is this:
client/command_text = "> "
client/verb/command(cmd as text)
set name = ">"
set hidden = 1
usr << "You typed [cmd]"
If command_text is set, the user can enter normal commands quickly by hitting
any of the '/', ESC, or BACKSPACE keys, which function to clear the command
line in this situation. That should appeal to IRC, VI, and normal users
resepctively. Since many of the puzzle-like games at the GoBs erupt into
chat-sessions, having the chat-command as the default is probably a good
standard.
Note the absence of quotes in the chat commands ("say " vs. "say \""); such
commands function to parse text input but do not require users to escape
quotes. So if the user enters on the command-line:
say hello there, "working" hard?
would work as expected. The quoted version would require escaped text:
say "hello there, \"working\" hard?
- Added log(x,y), where x is the base of the logarithm. It gives a natural
logarithm (base e) if you only give one argument. See the reference for
more information. (Have we got a curve-fitting chemist in our midst?!)
- The roll() function can now take an additional base offset value like
this: roll("3d4+5"), which means to add 5 to the result of summing a 4 sided
dice rolled 3 times. Recall that the purpose of packing this all into a
single text string is for cases where you want to store the dice parameters
in a variable (like sword.damage_dice). It's easier to store them all in
one place, rather than have three separate variables for the purpose. In a
situation where that does not apply, you could just as well do roll(3,4)+5.
- params2list() now automatically handles parameters with multiple values by
converting them into a list. Likewise, list2params() will make multiple
entries for a parameter if it is associated with a list of values. This
makes the HTML form interface for MULTI_SELECT work. It was throwing out all
but the last value before.
- The arrow-keys (up, down, etc) that correspond to the client movement
commands (North(), South(), etc) are now managed as macros. The default user
script (../users/yourname/default.dms) will automatically define these macros
as follows:
macro
North {return ".north"}
South {return ".south"}
East {return ".east"}
West {return ".west"}
Northeast {return ".northeast"}
Northwest {return ".northwest"}
Southeast {return ".southeast"}
Southwest {return ".southwest"}
Center {return ".center"}
But you can redefine them in your own .dms file in order to remove this behavior.
A common case might be #including the following .dms script into your project:
macro
North {return ""}
South {return ""}
East {return ""}
West {return ""}
Northeast {return ""}
Northwest {return ""}
Southeast {return ""}
Southwest {return ""}
Center {return ""}
This will detach the arrows from the normal client movement and free them up to
be used for the normal windows activity. Specifically, the left/right arrows
will be used for moving the cursor on the client command-line, and the
up/down arrows will be used for moving through the command history. (Zilal)
- The Build EXE dialog now retains its settings between sessions.
(Deadron, Air Mapster)
- The reference has been updated with all of the new crap in this release.
- Just so you know, dmb's generated by this version are incompatible with
previous versions. It can still load the old ones, of course.
Dream Seeker
- Fixed: The command line wasn't allowing the insertion of space characters at any
point other than the end of line. (Zilal, Guy T.)
- Fixed: Text tags (like "\improper") were being output as garbage characters to
browse() output, the stat panels, and prompts. (Air Mapster)
- Fixed: Certain symbols (like apostrophes) were being removed from text inside
popup prompts. (Deadron)
- Fixed: Sometimes the focus was getting diverted from modeless dialogs at bootup.
- Fixed: Macros weren't getting cleared between games. (Spuzzum)
- Fixed: Hitting ESC while at prompts was causing unexpected effects. The new
modeless prompts cannot be canceled in this fashion.
- Fixed: Some garbage files were being left behind by the programs whenever things
crashed or went awry. (Gazoot)
- Fixed: Infinite recursions were crashing the world in Windows, because the
callstack was generally smaller than the hard-coded checkpoint. It should
now cut things short before that happens. If you do very deep recursions
and you are willing to live with the possibility of running out of stack
space and crashing, simply set world.loop_checks to 0. (Erocl)
- Fixed: A problem in the icon code was causing occassional infinite
loops. It also introduced a subtle graphical glitch. Possibly the
problem with the DragonSnot icons? We will see.
- Fixed: a problem in the location reporter was occasionally causing a
person to be reported at a new address but with the URL still pointing to
the old address.
- The pager internals and interface have changed a bit. It is now much
more efficient in updating, only doing so when data changes (rather than the
old system of polling every few seconds).
- Alert boxes no longer beep. (Deadron)
- The option to host single-player worlds is now a button on the Dream Seeker
interface. It pops up a dialog that contains the port setting and an option
to add the world to the Live! hub. (Deadron)
- When the arrow keys are attached to the movement commands, you may use
CTRL + arrows to do the above actions of moving the command line and command
history. Unfortunately the SHIFT+arrow combination that used to scroll the
notify pane no longer works (sorry Guy T.), but eventually we will reinstate
a better mechanism using PageUp/PageDown.
- prompt() and alert() boxes are now automatically canceled when the player
is disconnected from the world. This prevents DS from trying to submit the
user's response when the connection is dead. (Spuzzum)
- The command-line now expands compound names from any individual word in
the name. For example, if you have a "red potion of healing", you could
enter "potion", "healing", or just "red" and hit space. The auto-expander
fills in any unambiguous parts and shows you your remaining choices.
This same rule applies to verbs. If you have commands "cast fireball" and
"cast dungball", you can just type "fireball" or "dungball" and hit space
(or even better "fi" or "du" and hit space). If the player just wants to
see a list of spells, typing "cast" and hitting space still does the trick.
Previously, command expansion would always halt on word boundaries, inside
of a compound name. Now it only does this if there are a lot of different
choices, which serves the purpose of reducing clutter in the expansion list
while not making it unnecessarily restrictive when there are only a few
choices.
DM Libraries
- The CGI object (defined in html/CGI.dm) now supports a new form of
authentication. To turn it on, you compile your project with
CGI/authenticate=1. This is a nicer interface than the basic .htaccess
method of authentication, and it is also more secure. For generality, the
old .htaccess method is still supported by DreamDaemon, but we will no
longer be maintaining a global BYOND password file for it on polaris, since
that is now an unnecessary security risk.
- Made some additions to html/form.dm.
Sub-forms can be used to handle frequently re-used components. You just
assign a variable on the main form to a new instance of another form
object. Also added a CHECKLIST interface type. This is useful if you want
the user to be able to select items in a list and you need more control over
the display than a simple multi-select element provides. You could just
define a bunch of CHECKBOX variables, but if the number of items is not
known in advance, that is not an option. The new RADIO_LIST interface
offers similar functionality but only allows the user to select one of the
options. Finally, there is a new _hidden control variable which can be used
to automatically embed a variable in the form with a HIDDEN interface. You
can hide anything, including a MULTI_SELECT or CHECKLIST or even a sub-form.
- UNIX DM now automatically creates .dmbs as executable if world/executor
is set. Also, the CGI library converts the 'params' command-line argument
to Dream Daemon into the default query string. That makes it easier to test
CGI applications from the UNIX command line. Example:
hello_cgi.dmb -params "name=Dan;gender=male"
Hub Packages
- Added a new URL addressing protocol for registered hub games:
seeker://hub:[hubid]
where [hubid] would be the hub registration number of your game. This
allows for more control over what happens when the user tries to access a
game (like when clicking on a game banner).
You can now upload a zipped installation package directly to the hub. If
you do that, users who do not already have your game installed will
automatically downloaded, install, and connect to it. (Yes!) If you
don't provide an installation package in the hub, it will fall back on your
download, login, info, or hub page url in that order.
This feature is primarily for distributed games. The nice thing about it is
that once the user has installed the game on their machine, clicking on the
"seek the hub" URL will connect them to their local version, wherever it
happens to be installed. This works for registered games that were
installed manually too. Once the user connects to them, DS remembers where
they were installed. Right now it doesn't happen to ask where to install
it--it just dumps it into a special "myhub" directory. That can easily
change if anyone cares, although once we add a more accessible bookmark
system that will probably be unnecessary.
Soon, we will provide options for registering byond addresses so that you
could point a more human readable name (such as byond://MyGame) to one of
these "seek the hub" URLs. However, I don't think that's too big of a deal,
because most people will not be typing in URLs but will always be clicking on
something in order to navigate. Kids these days never touch the keyboard.
When a new version of a game is detected, the notification contains a
clickable link that automatically upgrades the game. Previously, it simply
sent the user to the hub page, where they could manually download it. It
still falls back on that behavior if no installation package has been
uploaded to the hub. (Note that currently, upgrade detection only happens
if you host the game, because that causes it to connect up to the hub.)
Hubfiles
- Previously, client.Export() and client.Import() could be used to manage a
savefile attached to the player's key. These procedures have been expanded
(in a manner consistent with world.Export() and world.Import()) to provide
the ability to manage a savefile shared between multiple users. This is
called a hubfile, since it is stored at the hub, for access from anywhere.
The purpose of this is for 'postal-mode' games where people can play against
each other without all being logged in at the same time.
The basic scenario is this: player A saves a hubfile, marking it as
belonging to himself and players B and C, with the status of, say, B
indicating that she is next to move (any number of players may be marked
that way). Next time any of these players logs in, the presence of this
hubfile will be reported, and the player can click on it to open it up and
make a move. That player then saves it back to the hub and the process
continues.
Combine this with the new hub installation package feature, and you have a
pretty slick system. Player B doesn't even have to install the necessary
.dmb in advance--even if it is a distributed game. All she has to
do is click on the hubfile and the necessary software will be installed
before loading the file. Presto!
The new client Export() proc can now take arguments just like world.Export():
client.Export(Url,File)
This flexible arangement will prove useful in the future, but right now, the
only type of Url recognized for client.Export() is:
seeker://hub:123#file=456;title=Testing;turn=Dan;wait=Tom;wait=Spuzzum
That example would checkin a previously opened hubfile (id 456) to be opened
by the game registered as 123 in the hub. It will have the title
'Testing'. It is Dan's turn, with Tom and Spuzzum marked as additional
waiting players.
Having said all that, you don't actually have to work with any of this low
level stuff. The hub library provides a higher level interface and makes
all the calls to client.Import(), client.Export(), and handles
client.Topic() internally. You just have to read and write the savefile and
provide the player interface for creating and saving games in 'postal' mode.
You might be wondering why we don't use world.Export() in place of
client.Export(), since it would have already been possible to send data files
through that channel. The answer is security. There will have to be some
kind of user quotas (applied against the person checking in the file) to
guard against massive upload attacks. Since the hubfile will exist in the
user's hub space, it must be on the user's authority that the file be saved.
Otherwise, someone could write a few lines of DM code that would export a huge
amount of data to somebody else's space, a rather annoying security flaw.
Similarly, importing files into a world must be on the user's authority, or
again, someone could write a short program that would download or modify other
people's files. Since client.Import() and client.Export() channel data
through the player's Dream Seeker, and from there to the hub, the DM code is
restricted to working through the connected users, who are in turn
authenticated by the hub when they submit or access data.
- To accomodate the hubfiles a bit better, the client-side savefile system
has been revised little bit. Previously, the player's key had a single
savefile associated with it. This was intended to be used for saving
information about the player that might be shared between multiple worlds
(such as BYOND Home or any other distributed game). Since no standard
format was inforced, this would make such information unreliable at best.
The problem with enforcing a standard format, however, is that it would
unnecessarily limit what one might want to store there.
In the new system, games with different hub ids write to separate
client-side savefiles. For example, you would have one file configuring
your appearance in BYOND Home and a different one configuring options in
some other game.
Although the savefiles are independent, they should still be treated as
insecure. Client-side information can be tampered with by the player and it
can be accessed by other worlds using an assumed hub id. Do not put
critical or personal data in a client-side savefile. Eventually, there may
be some options for authentication or encryption, but for now, the intention
of client-side savefiles is for low-security data, such as configuration
preferences that need to be shared between multiple locations using the same
hub id.
The syntax for using client-side savefiles is virtually unchanged:
client.Export(F) //export a file
F = client.Import() //import a file
The only difference is that Import() now returns a cached file, whereas it
used to return an open savefile. That is more consistent with
world.Import() and with the new use of client.Import() for loading hubfiles
(in that case, hublib passes client.Import() a parameter telling it which
file to download). It also allows you to handle file types other than just
savefiles.
You can turn the cached file into a savefile like this:
var/savefile/F = new(client.Import()) //create temp file from imported data
And, yes, this new "client-side" savefile stuff works with roving keys. In
that case, the data is stored at the hub along with the hubfiles.
Security Babble: A programatic perspective
- Added world.GetConfig() and world.SetConfig(). These are more generalized
and useful than the getenv() instruction, which has now been removed. These
new procedures are for managing system configuration information that may be
shared between several applications. The parameters include a configuration
"set" and a variable "name". The configuration sets defined so far are:
env //system environment variables (modifications are not persistent)
admin //list of site administrators
keyban //list of banned keys
More will be added to this list soon. Example:
mob/verb
ban(key as text)
world.SetConfig("keyban",key,"reason=fiendish")
lookban(key as null|text)
if(key)
usr << "[key]: [world.GetConfig("keyban",key)]"
else
var/lst[] = world.GetConfig("keyban")
for(key in lst)
usr << "[key]: [world.GetConfig("keyban",key)]"
Notice how it is possible to retrieve the list of entries in an entire
configuration set.
Actually, the world.SetConfig() procedure can write to three different
configuration "spaces": system, user, and application. By default, it
writes to the user space, but if the user is not available to authorize the
change and the world is running in safe mode (like on the "dantom" host
service), it will write to the application space. Basically, this
determines how widely the settings are shared. System settings apply to all
BYOND software on the computer. User settings apply to all worlds run by a
particular user, and application settings apply only to worlds running out
of the same filesystem directory. The whole point of this is to make the
new "keyban" list work in safe mode, but eventually there will be other uses
for it. Since it only needs to write to the current directory, it fits into
the safe security model.
mob/verb/ban(key as text)
world.SetConfig("APP/keyban",ckey(key),"banner=[ckey(usr)]")
Replace "APP" with "SYSTEM" or "USER" to write to the other configuration
spaces. The default behavior is almost always what you would want.
We will probably be adding an "ipban" list too, since keys can currently be
effortlessly created. The trouble with ip bans is if you make it too
specific (matching every digit in the ip address), the user can usually get
a different ip by reconnecting to the internet. And if you make it too
generic (matching only some leading digits in the ip address), you might end
up banning a bunch of other people who go through the same internet provider.
One of the most ingenious banning techniques was mentioned recently by
Deadron: ban them without them even knowing it! If they speak and nobody
pays attention (because nobody else can see what they are saying), they may
just get bored.
Another alternative to overt banning is to apply a connection fee. For
example, if there is a bad user somewhere in 123.456.789.???, instead of
banning everyone in that domain, you can make them pay a small nominal fee
when they first try to connect with a key. Then you can freely ban the bad
guy from connecting and he would have to pay the fee for each new key he
wants to connect with. Don't feel too bad about the good users who have to
pay--you are really doing them a favor by weeding out the jerk who lives in
their neighborhood! I'm thinking there might be demand for a little library
to handle this sort of thing. Jerk.dm anyone?