Overview
Most of the information on Furcadia comes in numbers: coordinates, item IDs,
dragonspeak lines, variables, the color code - all of this is passed in form
of numbers. Unlike some other systems that use binary or decimal numeric
systems to pass data, Furcadia uses a numeric system of its own and thus, in
order to understand the Furcadia Communication Protocol and/or some of the
commands it uses internally, it is necessary to understand how this Furcadian
numeric system works.
Here we are going to discuss the base95 and base220 numeric
systems - both of which are in use by Furcadia at the time of writing.
General Numeric Systems and Digits
Before getting into Furcadia numbers, let's first look at numeric systems we
currently use when it comes to computers: Decimal, Hexadecimal and Binary. If
you already know how these work, feel free to skip to the next section.
In the real world, when it comes to numbers, we always use the Decimal numeric
system, otherwise known as base10. The base10 system uses 10 digits that
we all know well: 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9. This means that we can
represent 10 numbers with one digit - numbers from 0 to 9. Numbers higher
than 9 will be using more than one digit: 10 (2 digits: 1 and 0), 25 (2 digits:
2 and 5), 128 (3 digits: 1, 2 and 8), etc.
Let's have a look at another numeric system, commonly known to those who program
as Hexadecimal (also called base16). This numeric system differs
from decimal with how many digits it supports. While base10 system supports 10
digits, base16 supports 16 digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E
and F. In this numeric system, we can cram 16 numbers into one digit rather than
just 10, and while it seems like an insignificant difference, in bigger numbers
it can save you several digits and make the number's length look smaller (i.e.
FF in base16 is 255 in base10 - 1 digit saved, FFFFFF in base16 is 16777215 in
base10 - 2 digits saved). That is for base16. There are also base64 numbers
which, needless to say, are a lot more compressed than base10 and base16
together! For a comparison among these systems, check out the table below and
see for yourself!
By now, it might seem pretty confusing to have leters (ABCDEF) as digits - they
are leters afterall - not numbers! The answer to that is simple: we never made
a special look for digits above 9, so there is no special-looking "figure" to
visually represent those digits, but since leters are also figures, and they also
come at a certain order (first A, then B, C, D, etc), we can safely substitute
these extra digits with leters instead, and it will not ruin the meaning of a
number. To complicate the matter - drawn figures don't have to be the only things
used for digits: computers are using electricity and light, which have only two
states (on and off) and convert these states into numbers by assigning the "off"
state the digit 0 and "on" state the digit 1. That's how the Binary numeric system
was "born". This system is now responsible for absolutely everything you see on
your computer screen! Pretty cool, no?
Base95 Numeric System
Now that we know that base10 can represent 10 numbers in a single digit and base16
represents 16, it would probably be no surprise that "base95" allows 95 numbers in
a single digit. But hey, how do they do this? Afterall, there are only 10 digits
and 26 leters in the alphabet!..
Using the ASCII characters as digits
Meet the ASCII Table (and if you don't know what this is, you should probably do
yourself a favor and look into it first). This is a table of characters which can
be used as "digits" - each of these characters takes up one byte when we transfer
data across the network. This would make one realize that using the decimal numeric
system (which consists only of only 10 digits out of the potential 256) here to
transfer numbers, would waste a lot of bandwidth! While being able to cram 256
numbers in one byte, we would be using less than 4% of that potential! The other
96% will still be sent across the network, but never used for anything
program-wise! Isn't it a bit wasteful?..
Why base95 and not base256?
So what made DEP choose the base95 system if it doesn't even cover half the ASCII
table? Why not use a "binary" mode and cover the entire table to achieve maximum
potential? While I don't have the official answer at hand, here are some of the
reasons I would rather stay with the more simplistic protocol and base95:
-
The communication protocol is a lot easier to learn without any kind of official
specifications when it is written in a more "human" form than numbers being
everywhere. This allows people to easily learn the ropes and make 3rd party
tools for Furcadia such as more enhanced dream/patch editors or proxies.
-
When the protocol is so simple that you can easily and fully connect by using
a simple telnet client, it is a lot more simple to debug any possible errors
in communication than using packet capture tools and decoding the numbers
received. The chances for such errors to occur are also lower as a result.
-
Operating Systems often have built-in features to easily parse and manipulate
text. Thus, making a protocol that closely resembles simple text would make it
possible to use the OS for tasks a programmer would otherwise have to write a
separate system for.
-
Simple systems are a lot easier to maintain and enhance than sophisticated
ones.
Conversion
When it comes to converting a decimal digit into a base95 digit (which is an
ASCII character), all you have to do really is to add 32 to the digit.
This would skip the first two rows in the ASCII table, which contain the Control
Characters - those are unreadable and some of them carry special tasks in
text. One should also not forget that we can only use 95 digits in this numeric
system, so we will (technically) be unable to use characters beyond the ~
character. Anything higher than this digit will be using two digits - just like
anything that is higher than 9 in base10 will start using two digits: 10 (1 and
0).
Converting two digits and more will involve a little math, since we will be
splitting a base10 number into several base95 digits - each system with a
different amount of available digits.
From base10 to base95
- Assume base95num as empty string ("").
-
While base10 number is above zero:
- Do (base10num % 95) + 32 and store in memory (% being modulus).
- Convert stored result into an ASCII character and add it to the beginning of base95num (i.e.: "Xxxx").
- Do base10num / 95 and store the result in base10num without remainder!
- base95num now contains the base95 representation of the base10 number.
From base95 to base10
- Assume base10num as a number that equals 0.
- Assume multiplier as a number that equals 1.
-
For each character in base95num FROM RIGHT TO LEFT.
- Convert ASCII character into its numeric value.
- Do (char - 32) * multiplier and add the result to base10num (base10num = base10num + result).
- Multiply multiplier by 95 and store the result within it (multiplier = multiplier * 95).
- base10num now contains the base10 representation of the base95 number.
You might want to check the code samples for real languages - to someone who
writes code it may appear a lot simpler than reading what was written above.
Base220 Numeric System
Officially first utilized on 31-Jan-2008 (The FOX Update), the base220 numeric
system was introduced in order to succeed the base95 system. It was implemented
in newer/upgraded instructions in the protocol such as the color string,
movement, character tracking instructions and wherever character's User ID was
necessary. For now obvious reasons, base220 is more than twice as optimized as
its predecessor, as it can contain 220 numbers per digit. Its ASCII coverage
is as follows:
There are several caveats to remember in order to avoid confusion between base95
and base220:
- In base95, number 0 is a space character (32 or 0x20). In base220, number 0 is the # character (35 or 0x23)!
- In base95, the least significant digit is on the right side (i.e. " '" = 7). In base220, the least significant digit is on the left side (i.e. *### = 7)!
- Specifically in base220, the last digit is 254 (0xFE) and not 255 (0xFF). Digit 255 is off limits!
Conversion
The conversion between a base220 digit and a decimal digit is similar to base95
except for the offset that you add to the number. While in base95 it was 32,
here you need to add 35. The last digit is also not digit 127 (0x7E), but 254
(0xFE). Multiple digits are done the following way:
From base10 to base220
- Assume base220num as empty string ("").
-
While base10 number is above zero:
- Do (base10num % 220) + 35 and store in memory (% being modulus).
- Convert stored result into an ASCII character and add it to the end of base220num (i.e.: "xxxX").
- Do base10num / 220 and store the result in base10num without remainder!
- base220num now contains the base220 representation of the base10 number.
From base220 to base10
- Assume base10num as a number that equals 0.
- Assume multiplier as a number that equals 1.
-
For each character in base220num FROM LEFT TO RIGHT.
- Convert ASCII character into its numeric value.
- Do (char - 35) * multiplier and add the result to base10num (base10num = base10num + result).
- Multiply multiplier by 220 and store the result within it (multiplier = multiplier * 220).
- base10num now contains the base10 representation of the base220 number.
Likewise, if this text is too confusing, you can find a (hopefully) cleaner
version in one of the real code samples.
Code Samples
The following samples should theoretically work in real programming languages
and help you convert numbers to/from Furcadian systems. You are free to use the
code all you wish, but don't hold me responsible if it messes up. Giving credit
where credit is due is also a good idea. ;)
JavaScript
var base95 = new Object;
var base220 = new Object;
/** Base95 Numeric Conversion **/
base95.atoi = function( b95num )
{
var b10num = 0;
var mult = 1;
// Going from last to first character.
for( var i = b95num.length; i > 0; i-- )
{
b10num += ( b95num.charCodeAt( i-1 ) - 32 ) * mult;
mult *= 95;
}
return b10num;
}
base95.itoa = function( b10num, size )
{
var b95num = '';
// Size is optional in case you expect a static length.
if( typeof(size) == 'undefined' )
size = 0;
// Preventing negative numbers
if( b10num < 0 )
b10num = 0;
while( b10num > 0 )
{
b95num = String.fromCharCode( ( b10num % 95 ) + 32 ) + b95num;
b10num = Math.floor( b10num / 95 );
}
// This portion is the optional zero-fill. This makes sure the number
// has a fixed amount of digits if you specify it.
if( size > 0 )
{
var fillWith = ( b95num.length < size ) ? ' ' : '~';
var fillSize = size - b95num.length;
// If the number is bigger than the length can contain, rise it
// as much as possible, but keep to the length.
if( fillSize < 0 )
{
b95num = '';
fillSize = size;
}
while( fillSize > 0 )
{
b95num = fillWith + b95num;
fillSize--;
}
}
return b95num;
}
/** Base220 Numeric Conversion **/
base220.atoi = function( b220num )
{
var b10num = 0;
var mult = 1;
for( var i = 0; i < b220num.length; i++ )
{
b10num += ( b220num.charCodeAt( i ) - 35 ) * mult;
mult *= 220;
}
return b10num;
}
base220.itoa = function( b10num, size )
{
var b220num = '';
// Size is optional in case you expect a static length.
if( typeof(size) == 'undefined' )
size = 0;
// Preventing negative numbers
if( b10num < 0 )
b10num = 0;
while( b10num > 0 )
{
b220num += String.fromCharCode( ( b10num % 220 ) + 35 );
b10num = Math.floor( b10num / 220 );
}
// This portion is the optional zero-fill. This makes sure the number
// has a fixed amount of digits if you specify it.
if( size > 0 )
{
var fillWith = ( b220num.length < size ) ? '#' : String.fromCharCode( 254 );
var fillSize = size - b220num.length;
// If the number is bigger than the length can contain, rise it
// as much as possible, but keep to the length.
if( fillSize < 0 )
{
b220num = '';
fillSize = size;
}
while( fillSize > 0 )
{
b220num += fillWith;
fillSize -= 1;
}
}
return b220num;
}
Python
def base95_itoa( n, zero_fill_digits = None ):
# NOTE: base95 numbers' order is little-endian - smallest number last!
# Not allowing negative numbers.
if n < 0:
n = 0
b95int = ''
while n > 0:
chr = n % 95
b95int = b95int + "%c" % ( chr + 32 )
n /= 95
# /while
# Applying zero-fill as necessary
if zero_fill_digits != None:
# If the amount of digits we've got is bigger than requested, return
# the maximal amount for the amount of digits instead!
if len(b95int) > zero_fill_digits:
return atoi( ('~'*zero_fill_digits) )
b95int = ' '*(zero_fill_digits - len(b95int)) + b95int
return b95int
# /base95_itoa()
def base95_atoi( b95int ):
# NOTE: base95 numbers' order is little-endian - smallest number last!
n = 0
m = 1
for chr in b95int:
n += ( ord(chr) - 32 ) * m
m *= 95
# /for
return n
# /base95_atoi()
def base220_itoa( n, zero_fill_digits = None ):
# NOTE: base220 numbers' order is opposite of base95! "10" means "01"
# Preventing negative numbers.
if n < 0:
n = 0
b220int = ''
while n > 0:
chr = n % 220
b220int += "%c" % ( chr + 35 )
n /= 220
# /while
# Applying zero-fill as necessary
if zero_fill_digits != None:
# If the amount of digits we've got is bigger than requested, return
# the maximal amount for the amount of digits instead!
if len(b220int) > zero_fill_digits:
return atoi( ('\xfe'*zero_fill_digits) )
b220int += '#'*(zero_fill_digits - len(b220int))
return b220int
# /base220_itoa()
def base220_atoi( b220int ):
# NOTE: base220 numbers' order is opposite of base95! "10" means "01"
n = 0
m = 1
for chr in b220int:
n += ( ord(chr) - 35 ) * m
m *= 220
# /for
return n
# /base220_atoi()
Negative Numbers
Perhaps in the entire Furcadia, the only place you can see negative base95 or
base220 numbers would be in the color code - species setting. While normal
species Go from 0 and upwards, localspecies can go below 0: -1 and downwards.
The "signed" version of localspecies can otherwise be used as an unsigned -
positive number (just like everything else) - the first localspecies would be
219 and from there, downwards. Another method is to take the positive number
and subtract it by 220 so the first localspecies (219) becomes -1. 218-220
would be -2, etc. As you can see - not necessarily needing signed base95/220
numbers here.
Numeric System Comparison
The following table shows the various numeric systems and their coverage - how
many numbers can they represent per digit (or per byte if we transfer each digit
as a byte) for the sake of comparison.
System Name |
#/1 digit |
#/2 digits |
#/3 digits |
#/4 digits |
Binary | 2 | 4 | 8 | 16 |
Decimal | 10 | 100 | 1000 | 10000 |
Hexadecimal | 16 | 256 | 4096 | 65536 |
base64 | 64 | 4096 | 262144 | 16777216 |
base95 | 95 | 9025 | 857375 | 81450625 |
base220 | 220 | 48400 | 1064800 | 2342560000 |
base256 | 256 | 65536 | 16777216 | 4294967296 |
Base Converter
If you want to verify your own code for number conversion or a fast way to
convert without the need to make scripts or programs for this, here is a small
tool to help you (assuming your JavaScript support is intact):
NOTE: In case of invalid base95 or base220 numbers, the script will not
update the fields!
Frequently Asked Questions
This section addresses commonly asked questions that were not addressed or are
not meant to be addressed on a global scale.
None yet