Smerg Development Journal (original) (raw)

Raizy-Khtall

July 23rd, 2016, 05:53 pm

Adventures in SSL

One thing that ought to have been an easy selling point on Smerg is that most MUDs were written back in the days when people didn't really care as much about security or privacy. Most of them run on Telnet for ease of use and compatibility with just about any computer ever; even the ancient computer you dig out of your parents' garage these days could run Telnet.

Unfortunately, the thing about Telnet is that it is so simple that it just sends everything in plaintext; there's no security and it doesn't pretend to have any. This was fine in the 80's, but not today. So, although I intended to still allow Telnet connections since MUD clients are built on it, for the security-conscious I'd offer a way to have secure, encrypted connections. The best way to do this without a custom-made Smerg client (which I DO want to make some day, just not now) would be to support SSL, the Secure Sockets Layer.

I didn't know how deep the rabbit hole went when I tried to figure that out.

First off, I wrote a while back that I moved from plain Sockets to SocketChannels, the non-blocking variant. Which was fantastic. Now, Sockets have a cousin, the SSLSocket, which does pretty much what it says on the tin. It acts just like a Socket and uses the same API, but under the hood it handles all the SSL stuff for you, which is great because the SSL specification is gigantic and only huge crypto nerds know how it works. And the ServerSocket that Smerg used to listen for connections has an SSLServerSocket cousin. Of course, being Socket implementations, they are blocking. But that's okay, because there's an SSLSocketChannel, right?

WRONG.

No, someone at Oracle decided that that would make too much sense. So instead we have a monstrosity called the SSLEngine, which requires you to manually initiate all the various parts of the SSL protocol such as the opening handshake and negotiation of ciphers and who knows what else. It's impossible to use unless you know the SSL protocol inside and out. Amazingly enough, there are no open-source libraries that have picked up the slack either, and the only closed-source one has a website that has a link to its Javadoc but not an actual download link for the library itself. I wish I was making this up.

Okay, fine.

Non-blocking IO, while cool, isn't super critical. I can use Socket and SSLSocket and just check the size of the InputStream on the socket, like I did before switching to NIO, right?

WRONG.

Socket works fine. But SSLSocket's available method will happily return 0 even when there's input available. So the only thing you can do is just attempt a read and catch the SocketTimeoutException that will occur if there's nothing to read.

So, although I have a working implementation of SSL, there's one more problem: the typical command-line client, OpenSSL, doesn't support ANSI colors. Although I could have an option to turn them off, this thing is just becoming one gigantic headache I don't want to deal with any more. This won't scale; having to use blocking I/O means at most a millisecond wait for every user, which doesn't sound like much but can add up if you have a popular MUD. If you hit a thousand concurrent players, the game will be delayed by an entire second for all of them as they all wait for everyone else's timouts. When Smerg was multithreaded this problem went away, but now it's back in full force.

This isn't worth it.

I give. I'll come back in some nonzero number of months and just write a custom client for Smerg that handles encryption. Or maybe someone will submit a pull request and do it for me. I'm beyond caring at this point. I just want to get this thing released.

Rock Kh'tall

February 18th, 2016, 09:10 pm

A monumental day

The alternate form of player generation, in-game, is now completed, at least in its first iteration. There are three commands: 'status' shows you your current player generation status by showing all the fields you need to fill in, their current values (if any), and the values you can pick from. Both the fields themselves and the values you can select have special user-visible identifiers separate from the internally-used identifiers so that you can use whatever naming system you want (if you want to prefix everything in a content pack for distribution to avoid conflicts, for instance).

Then, 'select' is used to actually make a choice, something like 'select gender male'. It's case-insensitive and uses the same user-visible identifiers.

Finally, you use the 'complete' command to finish the process. If there are still fields to be filled in, it'll tell you what you have to do. Otherwise it ends the player generation process, which prevents you from using any of these commands again, and sets a flag that programs and such can use. For a quick-and-dirty completion gate, there's also a completeOnly flag you can set on an Exit that prevents anyone who does not have that flag set from going through it; this can be used both for the in-game generation and for a custom one, giving them some small freedom to wander around and perhaps interact with some tutorial rooms or NPCs first.

With this, Smerg is just about ready - my goals for the initial release are met. I might want to do a little bit more polish, and perhaps some final adjustments. Notably, I think I want to scrap the idea of automatic half-breeds; it was a nice concept but the eternally-recurring question of "how do you combine the two?" is making it just too much of a headache - and this is even without stats, traits, or racial abilities - so perhaps it can return at some date in the future. More to the point, creating more Races is a lot easier than it would be in, say, Smaug.

I also should try and make a quick example area to demonstrate a few basic features. I won't bother with the entire original idea of the Hall of Mirrors and the City of Assallus, but a few simple rooms for people to look at and experiment with if they clone the repository would be nice.

Other things can probably wait - for instance, I need a way for a race to specify what genders are available for it, or to take a more general approach to it, the character generation process needs to know what fields should come up in what order and how previous selections restrict the availability of later ones. Perhaps a simple concept of Inclusions/Exclusions? A set of ContentObjectIdentifiers would be sufficient if combined with a flag for whether it's an Inclusion set (ie, only values from that set are allowed) or an Exclusion set (everything but values from that set are allowed).

If I did this, the idea of an Attribute Map from the previous entry would make more sense, and the hard part would simply be figuring out how an admin sets up all this information.

Sprite

February 15th, 2016, 04:22 am

The long path of player generation

I finally got around to tackling Player generation. I cut out Jobs for now since I'll probably rename them to Professions later but they're also not necessary for Smerg 1.0 which won't have any combat.

I've got the prompt-driven generation working, which wasn't so bad in the end. If it's enabled, then during Player creation you'll get bumped into the generation step where you get asked to pick a value for the next @PlayerGen-annotated field that does not have a value yet. It'll tell you what your options are (based on ContentObjects of the appropriate type) and set the value to what you want. It can also show an extra message of info, which I have used on the half-race to explain what it is and how to opt out of making a halfbreed.

Then I pulled up some stuff from Race into a common parent class, PlayerAttribute, which itself is a ContentObject subclass, and got Gender to extend it. I'll make all the attributes extend this class, which contains some naming variables as well as a property to disqualify it from character generation so that only admins can pick it (it can still be assigned by builders to NPCs, too).

However, now I'm considering abstracting this further out now that a common parent class is in play. I would be able to give Mobile a Map<Class<? extends PlayerAttribute>, ContentObjectIdentifier> - that is, a map of the type of PlayerAttribute to the ContentObjectIdentifier for the actual instance of it in use. For example, mapping from Gender.class to Core:Gender:Female. This would combine and simplify all the attribute-related stuff for a little extra work ahead of time, which is the design philosophy Smerg is embracing.

That said, there are still details that need to be worked out. Admins would need a way to specify what attributes a player starts with, and thus picks during character generation. There would need to be a way to handle defaults or unassigned values. And there need to be conditional cases, such as when races can't be halfbreeds and thus the halfrace step should be skipped. Heck, that brings up another problem, since halfraces would require two Race.class keys (I may need to abandon automatic halfbreeds altogether for now, or go the other way and allow a LIST of races so that you can be a mix of arbitrarily many). I'd have to handle the case where someone legitimately does not have a particular attribute at all.

This may not be worth it - it's a parallel to ContentObjects, but the thing is that new ContentObjects are created within the game itself, but new PlayerAttributes are created in the code, as they are entire classes of ContentObjects.

In addition to all this, I added a logout command that does what it says on the tin and lets you switch characters without having to disconnect and reconnect.

NikoCodelizard

January 12th, 2016, 12:11 pm

Banned of Brothers

Friends have been implemented now, with the simple requirement of having both players add each other to their friends list.

Additionally, some simple ban commands have been added: ban, unban, ipban, and ipunban. They ban an account (and by extension, all Players owned by that account), or an IP address, from connecting to the game. An IP ban will kick someone off as soon as their IP attempts to connect (thus mitigating the damage of Denial-of-Service attacks as much as possible), and an account ban will kick off anyone who attempts to log into that account.

IP bans are always permanent; account bans can be temporary (with a duration in hours) or permanent. Permanent bans aren't, strictly speaking, permanent - but they are 1,000 years long, so they might as well be.

With this, I've completed my goal of some basic administration commands, enough to get started for a 1.0 release. One step closer!

Roren-Khtall

January 8th, 2016, 12:02 pm

I make a Craft(Friends) check...

I'm putting aside character generation briefly to work on another feature I'd like to have: friends lists.

From a user perspective, they'll initiate it by adding someone as a friend, and if the other person reciprocates, they'll be mutual friends. Then they can get benefits, such as knowing when their friend has come online or logged out. So the commands will just be 'friend [name]' and 'unfriend [name]' to modify the friends list, and 'friends' to view it.

As long as both characters are online, it's pretty straightforward. One character friends the other, they get a notification, something like "Codelizard wants to be your friend. Use 'friend Codelizard' if you want to be their friend, or do nothing to ignore them.", and then they get to act on it.

It gets more complicated if someone is NOT online, though. If could be disallowed - you can only friend people who are online. This is by far the simplest option. Alternately, you could be allowed to friend people who are not online, and they can return the favor in their own time. They'll probably get some kind of notification when they next sign on.

This is reasonable from a usability standpoint, but is more complicated to implement. The most complicated scenario is where one person friends another while offline, then they are both offline, then the recipient comes online. This would require the friends-list state to be saved externally to either character, which could be done, but does mean more work.

It's also possible to follow the model of certain other sites that split the concepts of "I want to know if this person is around" (a Follow/Bookmark) and "This person is my friend and we want to do stuff together" (a Friend). This separation also allows a distinction between following someone on the Account level (a Follow) where you always want to know if a particular person is around, and on the Player level (a Friend) where you want to know if they are around with that specific character because you adventure together.

A substantial difference between these is that a Follow is inherently one-way, but a Friendship is mutual. This can allow for the best of both worlds - a Follow only requires that the target exists, but a Friend requires them to be online so that they can (potentially) reciprocate, or at least get the notification.

Admittedly this is rather complicated if the only benefit of follows/friends is to know when someone comes online. So for the time being, I'm merely going to implement a friends list, which will require reciprocation to have any effect. In summary, it'll be the following commands:

NikoCodelizard

October 14th, 2015, 01:55 am

Meanwhile in another dimension...

Following up to the previous post on the subject of Dimensions...

Rather than simply adding another flag, it might be best to enumerate a ResourceType for the different types - we'd already have Normal, Currency, and Dimension, and more might come in the future, so another delicious enum would make this easier to modify in the future. Then we can add Resources for Weight and Volume (or just one if you prefer simplicity) and then BAM, every Item would have its dimensions expressed as Resources.

But then, first problem: containers. This can be solved with a collection of ResourceValues that strictly contain Dimension resources, representing the capacities of the item. If it's not present it can be assumed to be irrelevant/infinite. We also need to know if the container's own dimension should include that of everything in it or not (a la bags of holding). It might be best to make a Capacity object to encapsulate this information.

But then, what of inventories? Mobiles need to have some limits expressed on what they can carry. And heck, they have a weight too. In fact, they act very similarly to items - so why not bump all of the Resource related stuff up to InstantiableEntity?

This would allow Items, Mobiles and Exits to all have Resources attached to them. Since Resources can represent many things, this isn't a bad idea; a currency value on an Item can represent its actual value, and a hit point value on an Exit can represent how much damage it takes to break it open (or collapse it, if it's a portal). Hit points on items could allow for inventory destruction too - perhaps items and doors have Structure Points or some other analogue that functions exactly like HP except it doesn't regenerate.

But then they can also have Dimensions and Capacities. On a Mobile it represents the limits of what they can carry (probably as a Formula, so it can scale with their strength, level, or whatever). On an Item, it represents how much stuff can be put in the Item. On an Exit... it doesn't seem helpful at first, but then you can give it some thought: maybe only Players of up to the specified volume can get through (necessitating being a small race, or having a Shrink Person spell or similar). Or there's a weight limit, if it's a fragile rope bridge or something. Or both, if it's a narrow air duct.

EDIT: Afterthought - what if you wanted to limit the number of things a person can carry? Well, just add a Quantity resource, and have everything have a value of 1. But also make all containers NOT count the quantity of their contents and just offer 1 as their own quantity. Set the Quantity capacity for a player to 10, and presto! 10-item limit in your inventory, encouraging use of containers.

What if you want a Cataclysm style inventory where equipping backpacks and the like gives you extra volume but you don't have to explicitly nest items? Give backpacks a ResourceValue that takes effect when they are equipped to alter the wearer's volume maximum. Done!

Though that does remind me that I'll need to figure out how bonuses work - some might take effect merely by having the thing in your inventory ('bonus' being subjective; carrying a radioactive isotope would modify your Radiation regeneration rate, for instance). Some won't work unless you equip them. Some require the item to be consumed. The best way to do this is probably to have a Bonus object encapsulating the info (like I will do for capacities) that can dictate when its effects take hold (likely via an enum). Of course, then the problem becomes actually editing these things, so I could just make them ContentObjects and be done with it; an Item links to a preconstructed Bonus. (Capacities could do the same, so you can easily share them between Races and such if you wanted)

Rock Kh'tall

October 3rd, 2015, 01:01 am

Another quick fun idea

Suppose you want to make a post-apoc setting. You'll likely have Radiation as a Resource that makes Bad Stuff happen if it gets too high.

Or you're doing something like ADOM where Corruption is a Bad Thing that you don't want to let get too high, and it too is a Resource.

You might want to have a room that irradiates/corrupts you over time. So... Rooms should be able to have ResourceValues on them that will affect the values of all Resources for everyone in the Room when they enter. You could have a Room that will irradiate you (represented as giving a positive regen rate on the Radiation resource) or one that will drain your life (a negative regen rate on HP) or one that makes you weak (a negative current/max value on your Strength), and so on. Heck, you could even make an instant death trap, Smaug style, with a Room that causes current HP to decrease by 1 million, if you wanted.

Huntsman

September 28th, 2015, 03:36 pm

Enter the new Exits

Exits have been overhauled as previously mentioned. They are now InstantiableEntities and are inherently one-way, using the keywords that all InstantiableEntities have instead of cardinal directions. So you could have 'a curtain door to the east' respond to 'go curtain', 'go door', and 'go east' if you wanted. This ended up changing quite a lot of things:

I've got a small list of miscellaneous stuff to do following this up, but this is a nice change as it gives Exits just as much flexibility as other objects. The individual exit messages and shortdescs can let you give a lot of flavor to an area if you are so inclined. Or you can just stick to purely compass directions and that'll work perfectly fine too.

Raizy-Khtall

September 26th, 2015, 02:28 am

Oh, right, that.

Okay, so, a few things occurred to me that will change my plans a bit.

I mentioned needing a way to modify the edit command to work with collections. Problem is I just realized this is literally impossible because of a little thing called 'type erasure'. When you declare a List in the code, that part is gone by the time you're working with a running program, and you can't ask a List what it contains - that information was erased by the compiler.

However, I ALSO realized that there are very, very few collections that should be legitimately edited by admins. The biggest one is the list of keywords on an InstantiableEntity, but the vast majority of collections being used should not be able to be reflectively edited (or there's no real need for them to be). Thus, adding a few extra commands like 'setkeywords' is perfectly acceptable, and more to the point, absolutely necessary.

Rock Kh'tall

September 23rd, 2015, 12:13 am

ALL the resources!

Two more things that can be converted to Resources: Weight and Volume. They're just numbers...

But when you generalize it like this, containers end up being able to contain some quantity of Items with another particular resource, others ignored. This could allow you to model something like batteries or energy/magic cells. Or an inventory system like Pokemon where items can be put into computers and taken back out; each item can then have a Megabytes count or something like that.

Further thoughts: In the same way that some Resources are marked as currencies, you would need to mark some as Dimensions. Then, basically, each container can contain some maximum amount of any given Dimension, but if it doesn't have one listed, it can ignore it. So for instance, you could have a magic Backpack of Carrying that can carry any weight of stuff but can only carry a fixed volume (it has finite space). Or a Portable Hole that can carry any amount of anything. Working with the computer-held-items example, while they're in your inventory the Megabytes count doesn't matter at all, but once you store them inside of a computer, their volume and weight no longer matters but the Megabytes count does.