Showing posts with label javascript. Show all posts
Showing posts with label javascript. Show all posts

Wednesday, 29 January 2014

Roguelike MUD progress #7 - Web-based client

Previous post: Roguelike MUD progress #6

I set a mini-goal for myself, to display a curses-based program on a web page.

The first step was to find a no frills, easy to adopt and extend Python websocket solution. One I've stumbled on  few times, is the simple websockets client/server gist. I used a slightly fixed version of it as a base. The next step was to find decent web-based terminal emulation. There are frameworks available which ditch the terminal window, but they come with larger problems.  The best candidate is term.js.

So taking the simple websockets simple_websocket_client.html and adding term.js, then modifying simple_websocket_server.py, it was easy to get a simple echo working with embedded ANSI control sequences.

First, I tried to proxy the input and output from the nano editor. Unfortunately, doing this with the subprocess module resulted in nano outputting a message about not supporting redirection.  There's probably a way to do this, but too much bother and it is not really my goal anyway.

Next, I went for something simpler.  I did a simple proxying of input and output to a telnet connection. Specifically my Roguelike MUD codebase.  There were some teething problems.  Text incoming from the browser-based terminal through the websocket, is UTF-8 encoded.  And similarly, outgoing text has to be as well.  But responses from the MUD contain both ANSI escape sequences and telnet negotiation sequences.  In order to work around this, I stripped the telnet negotiation out of outgoing text, and modified the relatively basic websocket code to also send binary frames.

A working connection:


In-game display:


As the picture above shows, the ANSI sequences are working perfectly.  But the character set does not seem to contain the line drawing characters.  My game interface already has debugging tools which display accessible character sets.

The main character sets:


As can be seen above, the only character set with even a hint of line drawing characters, is the one exposed through the ESC(0 ANSI sequence.  Most of these sequences are faked by term.js, where known character sets are mocked up from the underlying unicode-supporting font.

One solution is to do what I do with Putty, which is to identify the connected client, and if it's one which supports unicode, then switch unicode on using terminal control sequences.  Modifying term.js to respond to the ENQ character (0x05), and send it's terminal name (xterm) was easy enough.

This is what I should be seeing now:


This is what I am actually seeing:


The correct unicode characters are being sent, but not being displayed correctly by term.js.  I don't expect this will be that difficult to correct when I get some more time to work on this.

Tuesday, 29 November 2011

Open source web-based consoles

I'm looking to bring both my MUD and networked roguelike code-base into the 90's, erm.. that should be 00's these days.  A web-based console with connectivity to the backend server through the web server is the way to go.

I've been collecting candidate "already written wheels" and wanted to collect them in one place.

Forum thread: HTML+JS Tech Demo (demo).

A GPL licensed code-base which uses an array of character cells to emulate a curses-like display.

Advantages:
  • Cleanly written code.
Disadvantages:
  • Restrictively licensed.
  • Code bound to existing game implementation.
Forum thread: js-like beta (first version!) released (demo).
A BSD licensed code-base which implements three different display mechanisms, canvas, array of character cells and image based.

Advantages:
  • Liberally licensed.
  • More library like, than game specific.
Disadvantages:
  • Code is somewhat hard to follow.
Forum thread: DecafMUD (demo)
A unknown licensed code-base which provides a complete out of the box web-based MUD client. Supports different code pages, and able to interpret the complete telnet stream from negotiation to escape codes. Tries to use web sockets for connectivity, but can fall back on flash if they are not usable.

Advantages:
  • Complete solution
  • Straight-forward code.
Disadvantages:
  • Windows-incompatible repository contents, errors when cloned.
  • Official web-site down.
  • Telnet protocol support is superfluous, a modern MUD server should be have a direct connection and not be encumbered by these complications.
While the GPL license is indeed restrictive, and reason enough to avoid using a project.  In this case, given a client is kept separated from the server, it shouldn't encumber the server-side in any way that matters.

Saturday, 19 June 2010

MUDBytes and post bloat

The most active MUD-related forum is currently MUDBytes. One of the good things is that the team there maintain their own forum software, and as the idea or need arises, it gets extended to support new features. However this is both good and bad, as the amount of feature "cruft" that decorates each displayed post increases.


Finding the "cruft" distracting, I decided to write a greasemonkey script to remove as much as possible. Here's the result:


And the greasemonkey script:

// ==UserScript==
// @name MUDBytes Page Cleaner
// @namespace http://disinterest.org
// @description Makes the pages less cluttered.
// @include http://www.mudbytes.net/topic-*
// @include http://www.mudbytes.net/index.php*
// ==/UserScript==

var tds = document.getElementsByTagName("td");
var posterInfoTD = null;
var classNames = "";
for(var i = 0; i < tds.length; i++){
classNames = tds[i].className;
if (classNames == "posterinfo") {
posterInfoTD = tds[i];
} else if (classNames == "posttop") {
for (var j = 0; j < tds[i].childNodes.length; j++) {
var topElement = tds[i].childNodes[j];
if (topElement.className == "button butright") {
topElement.className = "butright";
topElement.innerHTML = " ["+ topElement.innerHTML +"]";
}
}

// Remove the arrows.
var divs = tds[i].getElementsByTagName("div");
divs[0].innerHTML = "";
// Inject the poster name.
var posterText = document.createTextNode(" -- ");
var posterNameB = document.createElement("b");
var as = posterInfoTD.getElementsByTagName("a");
if (as.length > 0) {
posterNameB.appendChild(as[0].cloneNode(true));
} else {
var posterNameText = document.createTextNode("Guest");
posterNameB.appendChild(posterNameText);
}
divs[divs.length-1].appendChild(posterText);
divs[divs.length-1].appendChild(posterNameB);
// Remove the LHS poster info area.
posterInfoTD.parentNode.removeChild(posterInfoTD);
} else if (classNames == "postbottom") {
tds[i].innerHTML = "";
}
}

// Reduce the reading area width to make it more readable.
var tableElements = document.getElementsByTagName("table");
for (var i = 0; i < tableElements.length; i++) {
if (tableElements[i].className == "stand")
tableElements[i].style.width = '700px';
}

As cluttered as MUDBytes posts are getting, at least they haven't added header and footer frame areas like The MUD Connector.

Wednesday, 23 December 2009

DOM key event confusion

There are two standard sets of events that can be used to detect key presses, the first is onkeypress and the second is both onkeyup and onkeydown. Initially I went with onkeypress for my terminal, just to keep it simple.

When you are wanting to receive presses of what are called special keys, using onkeypress becomes a problem. In Firefox you can intercept the pressing of the backspace key, but in Internet Explorer this event never receives it and instead the browser goes back to the previously viewed page. Other keys that onkeypress does not handle in IE, include the cursor keys.

In order to work around this, I switched to onkeyup and onkeydown. Now all key presses, normal or special, work perfectly well in both browsers. However, there is another problem. All keys received are upper case characters, and various keys on the keyboard give symbols rather than their expected characters. The ',' key for instance gives the '¼' character.

Typing the following:

This is a test..,,,.M,,,,
Gives the following output:
THIS IS A TEST¾¾¼¼¼¾M¼¼¼¼
I made countless web searches trying to work out why this was. I even tried manually searching around Stack Overflow. I found web pages where someone was asking how to make the character received from onkeydown upper case, which implied that the problem was on my end. I was pretty much resigned to the existing behaviour, and was planning to use onkeypress to get the entered characters and onkeydown/onkeyup to get the special key presses. However it seemed like a hack, to work around the fact I didn't know what the problem was.

A last ditch question in ##javascript on the FreeNode IRC server yielded the following information:
[16:59] <deltab> chglpnts: keydown and keyup are lower-level, just identifying which *keys* have been pressed; keypress comes later, after the specific combination of keys has been converted into a character
[17:00] <chglpnts> deltab: Ah, I wondered if that was the case, but could find no references. How do I convert for instance 1/4 into , or is the standard way to use keydown/keyup for special keys but still use keypress for normal ones?
[17:00] <deltab> chglpnts: use both
I wonder where this this difference is clearly stated. Searching w3.org for onkeydown does not yield any relevant pages. It is stated in the Detecting keystrokes article on the quirksmode web site, although I was unable to distinguish the meaning until I was already aware of it.

In any case, I am now back on track..

CSS attribute matching and browsers

The goal is to have a blinking cursor. One approach I tried out, was through the use of attribute selectors, where making the cursor visible or not would be a matter of changing an attribute value.

Here's the HTML element:

<font id="cursor" class="cursor" shown="yes">
And the javascript to change the custom "shown" attribute:
if (cursorElement.getAttribute("shown") == "yes")
cursorElement.setAttribute("shown", "no")
else
cursorElement.setAttribute("shown", "yes")
With the following style sheet entries:
.cursor[shown="yes"] {
color: #7799CC;
background-color: rgb(241,128,22);
}

.cursor[shown="no"] {
color: white;
background-color: #7799CC;
}
This works perfectly in Firefox 3.5.6, but Internet Explorer 8.0 completely refuses to recognise the custom attribute. I'm not sure if there is a way in which this can be made to work in IE without defining custom DTD entries, but for now, I've gone back to an approach where I change the element class to make it blink.

Sunday, 20 December 2009

A step towards a Comet-based terminal

While I already have a rough and dirty web page that allows text to be entered, sent to the server and then echoed to anyone viewing the page, it isn't really inspiring enough to take any further. It's overly complicated and hacked together by an advanced manner of cut and pasting, from a selection of code found somewhere on the internet. In order to proceed with any project based on the functionality it is supposed to provide, I need a clean replacement for it.

Motivation

Code does not write itself. Although it was raining today, I needed to find a hook to get myself interested in this project. So, in order to motivate myself to implement a replacement from scratch, I took my inspiration from the AmigaDOS shell.


A thing of marvel, my goal was to get something that looked and appeared to work as close to it as possible.

Challenges

I had a little trouble getting the text to wrap right. Whenever a word which was longer than the width of the window was entered, the text entry continued off unseen outside the right of the window, resulting in the addition of a horizontal scrollbar. With a little CSS research, this was fixed by adding the following two lines:

  word-wrap:   break-word;
overflow-y: scroll;
There is, I have just noticed, one remaining bug. Because the console text is directly manipulated HTML, consecutive spaces are not displayed. I need to add the following:
  white-space:  pre-wrap
And finally, when you've entered enough lines to bring the vertical scrollbar into play, it doesn't automatically scroll. I still need to look into that.

Result

Eventually, I had to decide whether I was going to concentrate on functionality or the detail of its appearance, and functionality was the obvious winner. In its current state, the terminal supports the following:
  • Dragging the "window".
  • Text entry.
  • Movement of the cursor within the current line being entered.
  • The enter key starting a new line.
  • The backspace key removing the character before the cursor.
  • The del key removing the character under the cursor.
You can try it here.. although at this stage, it has only been tested in Firefox 3.5.5.

Thursday, 2 July 2009

Greasemonkey: mudconnect.com

The Mud Connector as a web site does not look that bad. But on a widescreen laptop, there's limited vertical screen space, and mudconnect.com has a header and footer dynamically inserted on every page that sucks up too much of that limited space.

This very simple Greasemonkey script removes that header and footer, giving back the screen space it otherwise sucked up.

// ==UserScript==
// @name MudConnect Page Space Reclaimer
// @namespace http://www.disinterest.org
// @description Removes screen space wastage
// @include http://www.mudconnect.com/discuss/discuss.cgi*
// ==/UserScript==

var theSpaceWaster, altText;
theSpaceWaster = document.getElementById('headerwrap');
if (theSpaceWaster) {
altText = document.createTextNode("");
theSpaceWaster.parentNode.replaceChild(altText, theSpaceWaster);
}
theSpaceWaster = document.getElementById('footerwrap');
if (theSpaceWaster) {
altText = document.createTextNode("");
theSpaceWaster.parentNode.replaceChild(altText, theSpaceWaster);
}

Monday, 1 October 2007

Preventing javascript redirection

For some reason, I wasted a lot of time this afternoon trying to make use of Firefox customisability in order to avoid having to log into Gamasutra in order to view the articles. I have no idea why, the article I was trying to get to, I didn't find interesting when I eventually got to see it. But I feel obliged to document the process for future reference.

When you visit an article on Gamasutra, it loads the page you want to see and in the process executes some javascript which checks if you have a cookie set and if not redirects you to its login page. I wanted to do something to disable this redirection.

var login_link = "https://www.gamasutra.com/php-bin/login.php...

//if the Gama Demo string does not exist, send them to login screen
if (pos == -1)
location.href = login_link
else {
My first attempt was in installing Greasemonkey. This seems to get invoked after the page has been loaded, at which point the redirection is already in progress. I could change the document.location value back to what it was being redirected from, but this just resulted in an infinite loop of redirection. I could catch the unload event and tell the browser to stop with window.stop(), but it seemed to stop after the redirection had started giving a blank page. Unless there is a formal way to stop what a previous assignment to document.location starts, Greasemonkey doesn't get invoked soon enough to help here.

The next attempt was with something I stumbled across when googling for "redirection". Firefox security policies. These have no user interface, but seem to be fully implemented. They enable you to do things like disable javascript for all sites or specified sites. Or disable access to various parts of the DOM, like document.location.

These lines would be added to a user's "prefs.js" file ("nogo" is just the random name I gave the profile myself):
user_pref("capability.policy.nogo.location.set", "noAccess");
user_pref("capability.policy.nogo.sites", "http://www.gamasutra.com");
user_pref("capability.policy.policynames", "nogo");
However, I could see myself wondering why the Gamasutra website wasn't working properly, so I just went with disabling javascript instead:
user_pref("capability.policy.nogo.javascript.enabled", "noAccess");
user_pref("capability.policy.nogo.sites", "http://www.gamasutra.com");
user_pref("capability.policy.policynames", "nogo");
More information on customising Firefox security policies can be found here.