C# Events, passing a Dictionary object?


#1

I have a script I’m building, which loads character information from a database. I have this information stored server side. Once the player loads into the world, I’m wanting to copy over his specific characters data from the server to the client for reference in UI menu’s.

However, this data is stored in a Dictionary container, and is throwing
System.InvalidCastException: Could not cast event argument from ExpandoObject to Dictionary2`

From what I’ve read around, this could have something to do with the interop for lua/js ect, that I can’t pass these kinds of containers? Which leaves me a bit at a loss for any /non/ hackish way of slinging the characters data over, which would be the character data for all the characters the account currently owns.

Any help would be greatly appreciated.


#2

Posting the actual code would be a good start!

But I assume youre trying to do new Action<ExpandoObject, CallbackDelegate>((body, resultCallback) ?


#3

Cast to IDictionary.


#4
        private void _onPlayerSpawned([FromSource]Player srcPlayer)
        {
            Int32 accId = AccountInterface.getAccountId(srcPlayer.Identifiers.First());
            srcPlayer.TriggerEvent("XCI.OnLoadCharacters", CharacterInterface.GetCharacters(accId));
        }

Characters are stored in
Dictionary<Int32, CharacterData>

So, ignore the event name, that’s a server event, trying to send the storage to the clientside script. Casting to IDictionary seemed to yield the same result =(.


#5

This is not the best way, but after trying to get something similar working and everything else seeming to fail, this seemed to work for me. Try receiving it as dynamic then looping through it using:

foreach (KeyValuePair<Int32, CharacterData> item in receivedObjectVariablename)
{
    // add each item.Key and item.Value to a new dictionary.
}

#6

will dynamic actually be able to un-parse to such? that’s somewhat doubtful, actually.

it should be noted that the c# msgpack deserialization layer has NO NOTION OF TARGET TYPES, so you will only get the following types out:

  • primitives
  • string
  • byte[]
  • callback reference
  • IList<object> (actually List<object>)
  • IDictionary<string, object> (actually ExpandoObject)

if there’s a type conversion defined by the framework (IConvertible + Convert.ChangeType on original types, or Type.IsAssignableFrom), you can also alternate arguments of this type, but any other types are not supported as argument to callback/event handlers, and you’ll have to write your own deserialization logic for such purposes.

of course, a limited serializer does exist, but it will fail on any slightly complex data types.

this would be documented elsewhere but we currently do not have any easily-editable place for quick snippets of such documentation so no such thing is happening right now.

it is also uncertain why this decision was made, it apparently was made by the original FiveM developers in 2014 without real motivation being in commit messages or comments, but it’s imaginable that msgpack-cli’s deserialization code didn’t work in restricted Mono at the time.


#7

Actually meant that one instead ^.
Still not sure if that’d allow for CharacterData. I’ve only tried it myself with <string, bool> dictionaries.


#8

Thanks for the insight guys, had to do some research on the dynamic/ExpandoObject dealio. Initial test seems like I’ll be able to accomplish what I wanted using ExpandoObject.

This was just a “does this work” attempt so the code is messy, but worked pretty well

serverside

        private void _onPlayerSpawned([FromSource]Player srcPlayer)
        {
            Int32 accId = AccountInterface.getAccountId(srcPlayer.Identifiers.First());
            var xciCharacters = new ExpandoObject() as IDictionary<string, dynamic>;
            dynamic charDataz = new ExpandoObject();
    
            charDataz.id = 1;
            xciCharacters.Add("Bobby Bobers", charDataz);
            srcPlayer.TriggerEvent("XCI.OnLoadCharacters", xciCharacters);
        }

clientside

        private void _onLoadCharacters(ExpandoObject xciCharacters)
        {

            foreach (KeyValuePair<string, dynamic> item in xciCharacters)
            {
                _sendConsoleMsg("Character " + item.Key + " And Id was " + item.Value.id);
            }
        }

Thinking I can convert my CharacterData members to an ExpandoObject, send to the client and convert them back to get around the restriction.