Sign in to follow this  
MatsVed

[.net] Problems with setting bits in a byte [Solved]

Recommended Posts

Hi there! I'm writing an online game, and I'm trying to come up with a good movement system. One of the first things I thought about was packing bits into a byte to register when a movement key is pressed. Well... it doesn't seem to be so easy. :
[source="C#"]        void m_MovementTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            byte MovementUpdate = new byte();

            if (Keyboard.GetState().IsKeyDown(Keys.Left))
                MovementUpdate = Utility.SetFlag(MovementUpdate, 0, 1);
            else
                MovementUpdate = Utility.SetFlag(MovementUpdate, 0, 0);
            if (Keyboard.GetState().IsKeyDown(Keys.Right))
                MovementUpdate = Utility.SetFlag(MovementUpdate, 1, 1);
            else
                MovementUpdate = Utility.SetFlag(MovementUpdate, 1, 0);
            if (Keyboard.GetState().IsKeyDown(Keys.Up))
                MovementUpdate = Utility.SetFlag(MovementUpdate, 2, 1);
            else
                MovementUpdate = Utility.SetFlag(MovementUpdate, 2, 0);
            if (Keyboard.GetState().IsKeyDown(Keys.Down))
                MovementUpdate = Utility.SetFlag(MovementUpdate, 3, 1);
            else
                MovementUpdate = Utility.SetFlag(MovementUpdate, 3, 0);

            if (!Keyboard.GetState().IsKeyDown(Keys.Left) && !Keyboard.GetState().IsKeyDown(Keys.Right) &&
                !Keyboard.GetState().IsKeyDown(Keys.Up) && !Keyboard.GetState().IsKeyDown(Keys.Down))
            {
                //No movement keys are hit, so start timing...
                m_IdleTimer++;

                if (m_IdleTimer == 10)
                {
                    m_IdleTimer = 0;
                    //Set the fourth bit to indicate the player is idle.
                    MovementUpdate = Utility.SetFlag(MovementUpdate, 4, 1);
                }
            }

            RealmPacketHandlers.SendMovementUpdate(m_Client, MovementUpdate);
        }

] The function up there is called every 100 msecs by the client. Also, here is my SetFlag function:
[source="C#"]        public static byte SetFlag(byte Number, byte Flag, byte TrueOrFalse)
        {
            byte Val = (byte)(1 << (Flag - 1));

            if ((Number & Val) == Val && TrueOrFalse == 0)
                Number = (byte)(Number - Val);
            if ((Number & Val) == 0 && TrueOrFalse == 1)
                Number = (byte)(Number + Val);

            return Number;
        }

The server code looks like this:
[source="C#"]        public static void OnMovementUpdate(NetMessage Msg, NetServer Server)
        {
            int ID = Msg.ReadInt();
            byte MovementUpdate = Msg.ReadByte();

            Account PlayerAccount = Globals.AccountMgr.GetAccount(ID);

            if (Utility.ReadFlag(MovementUpdate, 0) == true)
            {
                //Left
                Console.WriteLine("Left key was pressed!");
                PlayerAccount.CurrentCharacter.X--;
            }
            if (Utility.ReadFlag(MovementUpdate, 1) == true)
            {
                //Right
                Console.WriteLine("Right key was pressed!");
                PlayerAccount.CurrentCharacter.X++;
            }
            if (Utility.ReadFlag(MovementUpdate, 2) == true)
            {
                //Up
                Console.WriteLine("Up key was pressed!");
                PlayerAccount.CurrentCharacter.X--;
            }
            if (Utility.ReadFlag(MovementUpdate, 3) == true)
            {
                //Down
                Console.WriteLine("Down key was pressed!");
                PlayerAccount.CurrentCharacter.X++;
            }

            List<Account> Accts = Globals.AccountMgr.GetAccounts(ID);

            //TODO: Send server movement update...
        }

What this results in is that the server constantly spits out "Left key was pressed!" even when it isn't pressed! None of the other keys seems to function... at all! I've been trying to come up with a solution for days now - any ideas? Thanks in advance! [Edited by - MatsVed on August 11, 2008 8:59:20 AM]

Share this post


Link to post
Share on other sites
You didn't include your ReadFlag function, the problem can be there.

I would doubt that this is the source of the problem, but the part where you subtract one frome Flags in SetFlag function seems suspicious, since you also use 0 as the flag index.

Share this post


Link to post
Share on other sites
You can use enumerations as a set of flags. For example:


[Flags]
public enum MovementFlags : byte
{
None = 0,
MoveLeft = 1,
MoveRight = 2,
MoveUp = 4,
MoveDown = 8
}

MovementFlags update = MovementFlags.None;

if (Keyboard.GetState().IsKeyDown(Keys.Left))
update |= MovementFlags.MoveLeft;

if (Keyboard.GetState().IsKeyDown(Keys.Right))
update |= MovementFlags.MoveRight;

if (Keyboard.GetState().IsKeyDown(Keys.Up))
update |= MovementFlags.MoveUp;

if (Keyboard.GetState().IsKeyDown(Keys.Down))
update |= MovementFlags.MoveDown;

if (update == MovementFlags.None)
{
// blah...
}



You could then define an extension method to read the flag quite easily:

public static bool HasFlag(this MovementFlags flags, MovementFlags query)
{
return ((flags & query) == query);
}



Then it's just a simple matter of checking for the flag in the server:

MovementFlags update = (MovementFlags)msg.ReadByte();

if (update.HasFlag(MovementFlags.MoveLeft))
Console.WriteLine("Left key was pressed");

// etc...

Share this post


Link to post
Share on other sites
Thanks alot, but I can't seem to get things to work still. :I must admit I don't really know alot about bits and stuff, but I don't wanna have to send 4 extra bytes when it isn't neccessary! :(

Here are my changes:

        private int m_IdleTimer;

void m_MovementTimer_Elapsed(object sender, ElapsedEventArgs e)
{
MovementFlags Update = MovementFlags.None;

if (Keyboard.GetState().IsKeyDown(Keys.Left))
Update |= MovementFlags.MoveLeft;

if (Keyboard.GetState().IsKeyDown(Keys.Right))
Update |= MovementFlags.MoveRight;

if (Keyboard.GetState().IsKeyDown(Keys.Up))
Update |= MovementFlags.MoveUp;

if (Keyboard.GetState().IsKeyDown(Keys.Down))
Update |= MovementFlags.MoveDown;

if (!Keyboard.GetState().IsKeyDown(Keys.Left) && !Keyboard.GetState().IsKeyDown(Keys.Right) &&
!Keyboard.GetState().IsKeyDown(Keys.Up) && !Keyboard.GetState().IsKeyDown(Keys.Down))
{
//No movement keys are hit, so start timing...
m_IdleTimer++;

if (m_IdleTimer == 10)
{
m_IdleTimer = 0;
//Set the fourth bit to indicate the player is idle.
Update |= MovementFlags.None;
}
}

RealmPacketHandlers.SendMovementUpdate(m_Client, Update);
}


        public static void SendMovementUpdate(NetClient Client, MovementFlags Update)
{
NetMessage Msg = new NetMessage();
Msg.Write((byte)RealmServerOpCode.MOVEMENT_UPDATE_CLIENT);
Msg.Write(Globals.ClientAccount.ID);
Msg.Write((byte)Update);

Client.SendMessage(Msg, NetChannel.Ordered1);
}


    [Flags]
public enum MovementFlags : byte
{
None = 0,
MoveLeft = 1,
MoveRight = 2,
MoveUp = 4,
MoveDown = 8,
}

/// <summary>
/// Class containing various network utility functions.
/// </summary>
public static class Utility
{
public static bool HasFlag(this MovementFlags flags, MovementFlags query)
{
return ((flags & query) == query);
}
}


        public static void OnMovementUpdate(NetMessage Msg, NetServer Server)
{
int ID = Msg.ReadInt();
MovementFlags MovementUpdate = (MovementFlags)Msg.ReadByte();

Account PlayerAccount = Globals.AccountMgr.GetAccount(ID);

if (MovementUpdate.HasFlag(MovementFlags.MoveLeft))
{
//Left
Console.WriteLine("Left key was pressed!");
PlayerAccount.CurrentCharacter.X--;
}
if (MovementUpdate.HasFlag(MovementFlags.MoveRight))
{
//Right
Console.WriteLine("Right key was pressed!");
PlayerAccount.CurrentCharacter.X++;
}
if (MovementUpdate.HasFlag(MovementFlags.MoveUp))
{
//Up
Console.WriteLine("Up key was pressed!");
PlayerAccount.CurrentCharacter.X--;
}
if (MovementUpdate.HasFlag(MovementFlags.MoveDown))
{
//Down
Console.WriteLine("Down key was pressed!");
PlayerAccount.CurrentCharacter.X++;
}

List<Account> Accts = Globals.AccountMgr.GetAccounts(ID);

//TODO: Send server movement update...
}

Share this post


Link to post
Share on other sites
Why don't you just assign each direction a unique value?

Up = 1
Up/Right = 2
Right = 3
...

Unless you actually plan on having people move up, down, left and right all at the same time, that is just a lot of wasted values.

What you might want to look at instead is using a bit stream instead of trying to compact everything yourself. The Lidgren network has a pretty decent one, or you can make your own. This will allow you to specify the number of bits to send and let the bit stream do the work of putting it all together into bytes.

As for your current code, one thing that stood out quickly was that you have your flag parameter in SetFlag / GetFlag shift over by (Flag - 1), but you are also setting the value to 0 for the first bit. This results in (1 << -1), which makes no sense, so get rid of that -1. Also, I'd recommend learning to set/remove bits by using bitwise operations:

set (bit = 1):
Number |= 1 << Flag;

remove (bit = 0):
Number &= !(1 << Flag);

switch (bit = !bit)
Number ^= 1 << Flag;

Share this post


Link to post
Share on other sites
Thanks!
Tried removing the -1, but that didn't seem to work, either.
However, what you said about assigning each direction a unique value sounds like the Right Thing to do in this case, since the player cannot go in a 45 degree angle anyways.
All that's left for me to do then is to find out a way to make sure that the player's character only moves when one movementkey is pressed at a time.
I guess I'll start a new thread if I get completely stomped.

Share this post


Link to post
Share on other sites
Quote:
Original post by Spodi
remove (bit = 0):
Number &= !(1 << Flag);


This should have been "bitwise not" instead of "logical not", ie.

Number &= ~(1 << Flag);

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this