Nothing major, just figured somebody might be able to use it. A basic INI reader/writer utility.

using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
 
namespace INI
{
    public class INIReader
    {
        private readonly string _filePath;
 
        private Encoding _encoding = Encoding.ASCII;
 
        public INIReader(string filePath)
        {
            _filePath = filePath;
        }
 
        public Encoding Encoding
        {
            get { return _encoding; }
            set { _encoding = value; }
        }
 
        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section, string Key,
                                                          string value, StringBuilder result, int size, string filePath);
 
        public string ReadString(string section, string key, string defaultValue = null)
        {
            var temp = new StringBuilder(500);
            GetPrivateProfileString(section, key, "", temp, 500, _filePath);
            var strValue = temp.ToString();
            return string.IsNullOrEmpty(strValue) && defaultValue != null ? defaultValue : strValue;
        }
 
        public int ReadInt(string section, string key, NumberStyles style, int defaultValue = 0)
        {
            int i;
            return int.TryParse(ReadString(section, key).Replace("0x", ""), style, null, out i) ? i : defaultValue;
        }
 
        public float ReadFloat(string section, string key, float defaultValue = 0)
        {
            float f;
            return float.TryParse(ReadString(section, key), out f) ? f : defaultValue;
        }
 
        public double ReadDouble(string section, string key, double defaultValue = 0)
        {
            double d;
            return double.TryParse(ReadString(section, key), out d) ? d : defaultValue;
        }
 
        public decimal ReadDecimal(string section, string key, decimal defaultValue = 0)
        {
            decimal d;
            return decimal.TryParse(ReadString(section, key), out d) ? d : defaultValue;
        }
 
        public bool ReadBoolean(string section, string key, bool defaultValue = false)
        {
            bool b;
            return bool.TryParse(ReadString(section, key), out b) ? b : defaultValue;
        }
    }
 
    public class INIWriter
    {
        private readonly string _filePath;
 
        public INIWriter(string filePath)
        {
            _filePath = filePath;
        }
 
        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section, string key,
                                                             string val, string filePath);
 
        public void WriteString(string section, string key, string value, string comment = null)
        {
            var concatValue = comment != null ? string.Format("{0} ; {1}", value, comment) : value;
            WritePrivateProfileString(section, key, concatValue, _filePath);
        }
 
        public void WriteInt(string section, string key, int value, string comment = null)
        {
            WriteString(section, key, value.ToString(), comment);
        }
 
        public void WriteFloat(string section, string key, float value, string comment = null)
        {
            WriteString(section, key, value.ToString(), comment);
        }
 
        public void WriteDouble(string section, string key, double value, string comment = null)
        {
            WriteString(section, key, value.ToString(), comment);
        }
 
        public void WriteDecimal(string section, string key, decimal value, string comment = null)
        {
            WriteString(section, key, value.ToString(), comment);
        }
 
        public void WriteBoolean(string section, string key, bool value, bool useNumeric = false, string comment = null)
        {
            if (useNumeric)
                WriteString(section, key, value ? "1" : "0", comment);
            else
                WriteString(section, key, value.ToString(), comment);
        }
    }
}

This is just a small guide on how to setup PySnip on Linux. If you want to set it up on Windows, there is already a featured server here.

This guide assumes basic experience with a terminal and your distro’s package manager.

Since Ubuntu seems to be the most common distro, I’ll provide instructions that use apt-get. If you use a different distro/manager, please refer to your package-manager documentation for the appropriate commands.

First, install the dependencies:

sudo apt-get install python2.7 python2.7-dev python-setuptools python-twisted mercurial gcc g++ zope.interface
sudo python -m easy_install cython
sudo python -m easy_install pil
sudo python -m easy_install jinja2

Next, you can install screen if you’d like:

sudo apt-get install screen

Another optional feature is pygeoip, which will allow you to use the /from command, showing you where individual players are located geographically.

Next you need to either clone the repository using Mercurial.

hg clone https://code.google.com/p/pysnip/
cd pysnip
sh build.sh

If you don’t want to clone the repo, you can download an up-to-date zipped archive of the source here. Just download and extract as normal.

Next you need to edit the configuration:

cd feature_server
nano config.txt

Make sure you change your admin password from the default one.

Finally, run the server:

sh run_server.sh

Or if you are using screen:

screen sh run_server.sh

For help, please refer to the Google Code page or visit the Build and Shoot forums.

Only a tad bit late on this one. Figured I’d post about it though.

Build and Shoot

In light of Jagex pushing out Ace of Spades 1.0 and abandoning 0.x, danheeze, izzy, and myself have decided to make a new community called Build and Shoot. We’re aiming at giving the Voxlap version of Ace of Spades a solid home. So far things have turned out quite well.

Master Server

The other week, I reverse-engineered and re-implemented the master server. That means game servers can connect to our master server to be listed on our serverlist. It also means that we are completely independent of Jagex, which is always a plus. At the time of writing this, we have over 60 servers using our master server.

Since the protocol is exactly the same, server owners don’t have much to do in order to use our master server. To see how to use our master server, please view this thread.

Currently, it supports 0.75. We will likely be adding support for 0.76 RC10 as well later on.

PySnip

Along with everything that has been going on, pyspades has been made closed-source. I have put together a forked version, dubbed “PySnip”. It’s been slightly modified in some areas and is designed to work with our master server. All maps except for random and classicgen have been removed to slim the download size down. All the previous maps can still be downloaded here.

Jagex’s master server has been down for quite some time now and I’m not sure if it’s going to come back. If they do put it back online, I might add the ability to connect to multiple master servers by declaring them in the configuration file like so:

{
    "master_servers" : {
        "enabled" : true,
        "hosts" : {
            "184.172.204.137" : 32886,
            "199.195.254.202" : 32886
        }
    }
}

More information here: https://code.google.com/p/pysnip/

If you would like to join Build and Shoot and play Ace of Spades Voxlap, please visit http://buildandshoot.com.