2

I have a C# Windows forms application that runs a Trivia game on an IRC channel, and keeps the questions it asks, and the Leaderboard (scores) in Classes that I serialize to XML to save between sessions. The issue I have been having is best described with the flow, so here it is:

User X Gets entry in Leaderboard class with a score of 1. Class is saved to XML, XML contains one entry for user X.

User Y gets entry in Leaderboard class with a score of 1. Class is saved to XML, XML contains duplicate entries for User X, and one entry for User Y.

After running it for a week with under 20 users, I hoped to be able to write a web backend in PHP to help me use the scores. XML file is 2 megabytes.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;

namespace IRCTriviaBot
{
    [Serializable()]    
    public class LeaderBoard 
    {
        [Serializable()]
        public class Pair
        {
            public string user;
            public int score;
            public Pair(string usr, int scr)
            {
                user = usr;
                score = scr;
            }
            public Pair() { }
        }
        private static List<Pair> pairs = null;
        public List<Pair> Pairs
        {
            get
            {
                if (pairs==null)
                {
                    pairs = new List<Pair>();

                }
                return pairs;
            }
        }
        public LeaderBoard()
        {

        }
        public void newScore(string usr)
        {
            bool found = false;
            for (int i = 0; i < Pairs.Count && !found; ++i)
            {
                if (Pairs[i].user==usr)
                {
                    found = true; 
                    Pairs[i].score++;
                }
            }
            if (!found)
            {
                Pairs.Add(new Pair(usr, 1));
            }
        }

        public int getScore(string usr)
        {
            bool found = false;
            for (int i = 0; i < Pairs.Count && !found; ++i)
            {
                if (Pairs[i].user == usr)
                {
                    return Pairs[i].score;
                }
            }
            if (!found)
            {
                return 0;
            }
            return 0;
        }
    }
}

Here's where the serialization and deserialization happens.

       void parseMessage(string message, string user = "")
    {
        if (message == "-startgame-")
        {
            if (!gameStarted)
            {
                gameStarted = true;
                openScores();
                startGame();
            }
        }
        else if (message == "-hint-")
        {
            if (!hintGiven && gameStarted)
            {
                sendMessage("Here's a better hint: " + Form2.qa.Answers[curQ].Trim());
                hintGiven = true;
            }

        }
        else if (message == "-myscore-")
        {
            sendMessage(user + ", your score is: " + leaderB.getScore(user));
        }

        else if (message.ToLower() == Form2.qa.Answers[curQ].ToLower())
        {
            if (gameStarted)
            {
                sendMessage(user + " got it right! Virtual pat on the back!");
                leaderB.newScore(user);
                saveScores();
                System.Threading.Thread.Sleep(2000);
                startGame();
            }
        }
        else if (message == "-quit-")
        {
            if (gameStarted)
            {
                sendMessage("Sorry to see you go! Have fun without me :'(");
                gameStarted = false;
            }
            else
            {
                sendMessage("A game is not running.");
            }
        }
        else
        {
            if (gameStarted)
            {
                //sendMessage("Wrong.");
            }
        }
    }

    void saveScores()
    {
        //Opens a file and serializes the object into it in binary format.
        Stream stream = System.IO.File.Open("scores.xml", FileMode.Open);
        XmlSerializer xmlserializer = new XmlSerializer(typeof(LeaderBoard));

        //BinaryFormatter formatter = new BinaryFormatter();

        xmlserializer.Serialize(stream, leaderB);
        stream.Close();
    }

    void openScores()
    {
        Stream stream = System.IO.File.OpenRead("scores.xml");
        XmlSerializer xmlserializer = new XmlSerializer(typeof(LeaderBoard));

        //BinaryFormatter formatter = new BinaryFormatter();

        leaderB = (LeaderBoard)xmlserializer.Deserialize(stream);
        stream.Close();
    }
Jeremy
  • 23
  • 4
  • 1
    @Jeremy: I moved the code from pastebin to here. I don't see how this page is inferior to pastebin. – Tomalak Nov 12 '10 at 22:37
  • I couldn't get the markup to work. – Jeremy Nov 12 '10 at 22:39
  • 1
    Might I suggest a small database? – Nate Nov 12 '10 at 22:40
  • Why not store the data in a database instead of XML files? – YWE Nov 12 '10 at 22:40
  • or you could try something along the lines of MTOM? – Mr Shoubs Nov 12 '10 at 22:41
  • @Jeremy: That's because you did not read the formatting help on the right side of the editor. ;-) The SO syntax highlighter is pretty decent, C-style languages work best. BTW: You can always edit your question to add any details or revisit the formatting. – Tomalak Nov 12 '10 at 22:42
  • 2
    Why does writing User Y duplicate the data for user X? – John Saunders Nov 12 '10 at 23:02
  • Why is `private static List pairs = null;` marked as static? If you want the list of scores to be a singleton, I'd make it a regular singleton, rather than instantiating a new object that receives the global data in the background. – David Yaw Nov 12 '10 at 23:27
  • @John Saunders That is why I'm asking :P – Jeremy Nov 12 '10 at 23:49
  • @Jeremy, the code you posted never calls `newScore`, never serializes nor deserializes. If you want us to debug your code you first have to show us the code. – Dour High Arch Nov 13 '10 at 00:19

1 Answers1

1

I think this has to do with pairs being marked static. I don't believe the XmlSerializer will clear a list before adding elements to it, so every time you call openScores() you will create duplicate entries rather than overwrite existing ones.

In general, I've observed that serialization and global variables don't play well together. For this purpose, "global variables" includes private statics, singletons, monostate classes like this, and thread-local variables.

It also looks like there's some waffle here between using XML and binary serialization. They are completely different beasts. XML serialization looks only at a class's public properties, while binary serialization looks only at a class's instance fields. Also, XML serialization ignores the Serializable attribute.

Jeffrey Hantin
  • 35,734
  • 7
  • 75
  • 94
  • Being new to serialization, the Binary area is only there as an artifact from the MSDN article :P – Jeremy Nov 13 '10 at 01:31