Dory
Dory

Reputation: 139

C# (Craft.Net.Client) ServerList Exception

I'm using Craft.Net.Client library but I've got an error when trying to use the ServerList.SaveTo(string file) method : Unable to read beyond the end of the stream (EndOfStreamException)

ServerList.cs :

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using fNbt;

namespace Craft.Net.Client
{
/// <summary>
/// Provides functionality for interacting with
/// the saved vanilla server list.
/// </summary>
public class ServerList
{
    public static string ServersDat
    {
        get
        {
            return Path.Combine(DotMinecraft.GetDotMinecraftPath(), "servers.dat");
        }
    }

    public ServerList()
    {
        Servers = new List<Server>();
    }

    public List<Server> Servers { get; set; }

    public void Save()
    {
        SaveTo(ServersDat);
    }

    public void SaveTo(string file)
    {
        var nbt = new NbtFile(file); // ERROR : Unable to read beyond the end of the  stream (EndOfStreamException)
        nbt.RootTag = new NbtCompound("");
        var list = new NbtList("servers", NbtTagType.Compound);
        foreach (var server in Servers)
        {
            var compound = new NbtCompound();
            compound.Add(new NbtString("name", server.Name));
            compound.Add(new NbtString("ip", server.Ip));
            compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
            compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
            list.Add(compound);
        }
        nbt.RootTag.Add(list);
        nbt.SaveToFile(file, NbtCompression.None);
    }

    public static ServerList Load()
    {
        return LoadFrom(ServersDat);
    }

    public static ServerList LoadFrom(string file)
    {
        var list = new ServerList();
        var nbt = new NbtFile(file);
        foreach (NbtCompound server in nbt.RootTag["servers"] as NbtList)
        {
            var entry = new Server();
            if (server.Contains("name"))
                entry.Name = server["name"].StringValue;
            if (server.Contains("ip"))
                entry.Ip = server["ip"].StringValue;
            if (server.Contains("hideAddress"))
                entry.HideAddress = server["hideAddress"].ByteValue == 1;
            if (server.Contains("acceptTextures"))
                entry.AcceptTextures = server["acceptTextures"].ByteValue == 1;
            list.Servers.Add(entry);
        }
        return list;
    }

    public class Server
    {
        public string Name { get; set; }
        public string Ip { get; set; }
        public bool HideAddress { get; set; }
        public bool AcceptTextures { get; set; }

        public override string ToString()
        {
            return Name;
        }
    }
}

}

And where I call the method :

ServerList.Server server = new ServerList.Server();
server.Name = "x";
server.Ip = "x";
server.HideAddress = true;
server.AcceptTextures = true;

ServerList list = new ServerList();
list.Servers.Add(server);
if (!File.Exists(RuntimeInfo.getMinecraftDir() + @"\servers.dat"))
{
File.Create(RuntimeInfo.getMinecraftDir() + @"\servers.dat");
}
list.SaveTo(RuntimeInfo.getMinecraftDir() + @"\servers.dat");

One thing I've noticed is that I only got the error when servers.dat is empty but not if it has already servers saved.

Can anyone help me?

Thanks in advance,

EDIT : Thanks to steveg89, the solution below solves the problem (updating SaveTo method) :

    public void SaveTo(string file)
    {
    NbtFile nbt;
    if (File.Exists(RuntimeInfo.getMinecraftDir() + @"\servers.dat"))
    {
        nbt = new NbtFile();
        nbt.SaveToFile(RuntimeInfo.getMinecraftDir() + @"\servers.dat", NbtCompression.None);
    }
    else
        nbt = new NbtFile();

    nbt.RootTag = new NbtCompound("");
    var list = new NbtList("servers", NbtTagType.Compound);
    foreach (var server in Servers)
    {
        var compound = new NbtCompound();
        compound.Add(new NbtString("name", server.Name));
        compound.Add(new NbtString("ip", server.Ip));
        compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
        compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
        list.Add(compound);
    }
    nbt.RootTag.Add(list);
    nbt.SaveToFile(file, NbtCompression.None);           
}

Upvotes: 3

Views: 531

Answers (1)

steveg89
steveg89

Reputation: 1827

I notice their constructor for an NbtFile tries to read from the file. It's assuming the file is in the correct format already. That means you'd have to create it and save it. Their API SHOULD handle this for you but doesn't, so try this

if (!File.Exists(RuntimeInfo.getMinecraftDir() + @"\servers.dat"))
{
   NbtFile nbt = new NbtFile();
   nbt.SaveToFile(RuntimeInfo.getMinecraftDir() + @"\servers.dat", Nbt.Compression.None);
}

I think a smarter way to do it would be to correct the SaveTo method of the ServerList. This method would mean you wouldn't have to check it in your code, but it does mean you'd be using your own flavor of Craft.Net. I'd do it like so:

public void SaveTo(string file)
        {
            NbtFile nbt;
            if( File.Exists( file ) )
            {
              nbt = new NbtFile(file);
            }
            else
            {
              nbt = new NbtFile();
            }
            nbt.RootTag = new NbtCompound("");
            var list = new NbtList("servers", NbtTagType.Compound);
            foreach (var server in Servers)
            {
                var compound = new NbtCompound();
                compound.Add(new NbtString("name", server.Name));
                compound.Add(new NbtString("ip", server.Ip));
                compound.Add(new NbtByte("hideAddress", (byte)(server.HideAddress ? 1 : 0)));
                compound.Add(new NbtByte("acceptTextures", (byte)(server.AcceptTextures ? 1 : 0)));
                list.Add(compound);
            }
            nbt.RootTag.Add(list);
            nbt.SaveToFile(file, NbtCompression.None);
        }

Upvotes: 3

Related Questions