Thursday, March 8, 2012

Darting here and there

So another Dart Editor update, now at Build 5104, get it here!. I love how quickly the dart integrated builds have been coming lately. This new version does away with the old 'Libraries' view panel on the left side (by default) and instead implements a Files view. Instead of opening a project you now open the folder containing your project. All of your .dart files are loaded into that. I wasn't sure about it at first, as it is a changes in how it would work previously but already I'm seeing the benefits of it. It does help to encourage a Good folder/subfolder layout, which I was already comfortable with anyways.

I've seen a few people report some difficulties loading their existing projects, however I can't really comment on that as I have not encountered those issues myself. This version did correct the mass of errors I was receiving in the previous version regarding variables hiding other variables or methods. However it did introduce a couple of new ones.

First, this version introduced the changes to the dart:io library, changing all eventHandlers over to be in the form of onEvent, and one-shot methods (such as File.exists) now take the callback function as an argument, as opposed to having to specify an 'existsHandler'. As such I had to go through my code and made updates to the Sockets and Files to properly reflect the changes to the library.

Secondly, I ran into an issue where any print statements, within a class, would generate an error or warning that "print" is not a method in , where ClassName is whatever class happened to contain the print statement. This post in the Dart Group provided an easy, temporary fix, that is to simply import 'dart:builtin' where required. A simple fix and helped to clean up the errors there so I could focus on my own errors that I had to worry about.

I notice that while the IDE and SDK were updated to Build 5104, the Dartium Build remains at 5070. At this time I am still unable to get the Dartium builds to run on my machine due to a version problem with the shared libraries. I receive the following error:

error while loading shared libraries: libgconf-2.so.4: wrong ELF class: ELFCLASS64

I believe this error is related to the fact that I'm running a 64-bit OS, but it's trying to load 32-bit build. This may also have something to do with my early ventures in trying to build DartVM, IDE, and Dartium myself and installing the extra libraries, etc, which may have conflicted with what Dartium is trying to load. In fact, now that I think of it, I should run a good apt-get autoclean and autoremove regardless. I don't believe it will fix it, but certainly clean up my system a little at least. Now I've not been too worried about this as my current project doesn't use or require any client-side work, but eventually it might be interesting to have a working copy of Dartium for when I start working on some client side projects. Hopefully the 64-Bit builds of Dartium will be available by then.

Monday, March 5, 2012

Dart Mud... Progress 2

So I decided it's time for another progress report on my Dart based MUD. This project has be a lot of fun, and giving me experience with the dart:io libraries including Files, ServerSockets, and Sockets. Before I get into too much, I will mention that there is a new Integrated Dart Build available. Version 4760 (though SDK appears to be build number 4759). You can find your download here. Since using this build I have been getting errors and warnings when using the dart:io library. However it doesn't prevent my project from running properly. Just distracting thinking I have an error when I don't.

To follow up on my previous post, I have begun using git locally to archive changes and keep a bare-bones changelog system. Already I see this being helpful in keeping track of what I have changed or done, and additionally, in being able to revert changes easily. Once I clean up some of the code and add a plethora of comments, I may pop the project up onto Github as well.

My User Object now allows for the creation of a new character, automatic saving of a user and loading user (and password) from a save file. To save character information I'm currently placing the information I want saved into a map, and using saving it as a JSON object. In the future I can see some issues I may need to work out, but since I haven't decided on how things such as Objects, equipment etc will be implemented yet I don't know yet how much of an issue they will be.

As part of adding to the User object, I decided to create a separate Login object as well which will handle the accepting of username/password and loading the character and creating a new User object from the loaded information. Additionally the Login object also handles creating a new account, posing the various questions (username, password, password confirmation etc), then creating a new User object based off of the data provided. This way a full User object isn't created until credentials have been verified, and also prevents User from showing up in the user list, or receiving broadcast messages etc before they have completed the Login process.

As part of the work to separate the Login class from the User class, I also created a separate 'Connection' class as a wrapper over top of the raw sockets. Primarily this just creates a couple of convenience functions over the Socket for write, writeLine, readLine, set the lineHandler and close the Socket. I made this its own library, so it is imported into the Server which creates the initial socket and then wraps the Connection around it, and then hands it off to the mudlib (which is its own library). The mudlib also imports the Connection class. The mudlib then passes the Connection off to a new Login object which uses that to communicate to the user (again as opposed to the raw socket). The connection object is then passed off to the newly created User object once it is created. User object adds a couple more convenience functions as well but mostly works with the Connection object as well.

I've added a couple more commands as well. Now able to 'say' to the local room, change the default prompt (which does save to the User file), and of course the always abused 'emote' command. Additionally I've got a good start on a simple line editor (and a simple command wrapper for it to see the results). The Editor is a separate object which will take over the Input/Output streams of a socket from the user. It does not remove the user itself, nor does it take the user's connection object. Rather it just updates the Connection object to use the Editor's lineHandler.

The editor has two modes, Command mode and input put. By default you start in command mode, from which you can either insert or append which will put you into the input mode. To exit the input mode, one just needs type a period (.) on a blank line. The editor works similarly to 'ed' but a little prettier. (For instance a prompt of ': ' in command mode and prompt of '~ ' in input mode). Currently implemented commands in the editor: a - append, i - insert, q - quit, h - help, p - print line, d - delete line. In the future some commands such as p and d will accept a range argument as well however this is yet not implemented. There is no 'save', if you quit it will return whatever is in the buffer.

The editor is called by the User object. The User object in turn is called by some other command or object (most likely a command which will do something with the output). To start the edit the User object has startEdit method called on it with a callback function as an argument. Once the editor finishes it calls the method doneEdit in the User object which resets the proper line handlers, and then calls the previous callback function with the String returned from the Editor.

When I first started working with the Editor I thought the obvious solution to use it would be a StringBuffer. However after only a few minutes of implementing it with the StringBuffer I found it lacked much of the basic functionality I needed, such as getting a specific line from the buffer, or removing a line from the buffer. I also can only add to the end of the Buffer and not to any particular point in the buffer. So that didn't last long at all.
When I looked at the StringBufferImpl class in the library, I saw that it was nothing more than a List of Strings. So I decided to do the same. So I created two List of strings. One for the full Buffer, and one for a temporary buffer. The temporary buffer is used in input/edit mode. Upon leaving input mode back to command mode then the temporary buffer is inserted into the full buffer.
To accomplish this, I originally tried using List#setRange. However I quickly discovered that wouldn't work as it would just overwrite the range, as opposed to inserting at that point. Additionally if the temporary buffer would exceed the length of full buffer, then it would throw an exception. Even though the list is an extendable list. So I tried List#insertRange, and that expanded the List as required, but it will only initialize the new range to the same value. You can't insert the range with with list as the new values. So it required first using insertRange to add the space for the temporary list, then setRange to change those values to be the strings in the temporary buffer.

Another area of interest is that I have implemented a RoomManager. Eventually the RoomManager will be expanded to have time outs so once a room has gone 'stale' (no activity in it for x number of minutes) it will reset the room. And, if still not used after y number of minutes, the object will be unreferenced for the GC to clean up as it will. The first part of this however, was trying to to determine how I can efficiently 'dynamically' load the rooms when someone tries to access them, or alternatively reference an already loaded room. Since I can't use eval to load a file at this point, the code has to be loaded on start up.
So I created two maps, each map has a key of a room identifier. One map holds the rooms themselves if they are loaded. The other, roomFuncs holds functions which create the room. Rooms are added by calling the add method on the RoomManager, which takes a callback as an argument. The add method then runs the call back to create the room. It gets the room's unique identifier to use as the key. Then it stores the callback in the roomFuncs map. Then when it comes time to access the room, we call the method putIfAbsent method on the room map, passing our roomId and a reference to roomFuncs[roomId] which returns the callback to create the room if the object is not already loaded. The putIfAbsent is a nice little method, and quite handy for this.

Well that's about all for now anyways. I must say I'm having a blast writing this simple mud, and while I have a long way to go for a full featured mud, it is virtually usable at this point. Dart is a lot of fun!