RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

World Items/Mobiles: ConcurrentDictionary<TKey, TValue>?

Vorspire

Knight
Oh I'm gonna jump on this one and test it out with my DataStore serialization system... http://vncore.codeplex.com/SourceControl/changeset/view/a1b96b13d68b#IO/DataStores/BinaryDataStore.cs
RunUO SVN isn't written for .NET 4.0 so I doubt this will be considered unless it proves to be beneficial to UOGamers shards.
However, since my RunUO extension is primarily aimed at 4.0 I would be happy to include this kind of feature to make it publicly available and ready-to-use - There are ways to override World Saves without editing the Core directly, so it would be completely optional and compatible.

Maybe the time has come for non-intrusive, non-blocking World Saves, which can be achieved through multiple threading, an exciting prospect.

Thanks for your post. aralith++
 

Vorspire

Knight
Managed to sneak a new commit in to my project with some pretty major updates - your suggestion being one, support for ConcurrentDictionary for my DataStore class, which is a helper for easy reading/writing to files in different formats while retaining a dictionary structure (check out the BinaryDataStore and XmlDataStore, which provide wrappers for Binary and Xml serialization with minimal effort)

http://vncore.codeplex.com/SourceControl/changeset/view/eabb8b91e81d#IO/DataStores/DataStore.cs

Now all that's left to do is create a ConcurrentBinaryDataStore and implement a test save/load scenario that can be applied dynamically without any core or distro edits. (My entire source is drag-drop, but some things are co-dependant)
Let the testing begin... :)
 

mrknowitall

Wanderer
The DynamicSaveStrategy Asayre wrote already uses Concurrent Collections. Concurrency doesnt just magically make it so you don't get a block during a save. The block happens during a save on purpose (not because its multithreaded, infact has nothing to do with threads). I happens so that while saving the data, the data doesnt change. What happens if Mobile A gets saved with Item B in his pack, but trades it to Mobile C during a save before Mobile C gets saved? Problems, thats what. This is why blocking happens, and it would still have to happen if you were to change anything about saving (unless saves happened in real time, like in a database).
 

aralith

Sorceror
My reasoning was simply for iterating over the items safely and without locking from another thread for a different service that wasn't so much concerned with things like that ;\
 

ASayre

RunUO Developer
The DynamicSaveStrategy Asayre wrote already uses Concurrent Collections. Concurrency doesnt just magically make it so you don't get a block during a save. The block happens during a save on purpose (not because its multithreaded, infact has nothing to do with threads). I happens so that while saving the data, the data doesnt change. What happens if Mobile A gets saved with Item B in his pack, but trades it to Mobile C during a save before Mobile C gets saved? Problems, thats what. This is why blocking happens, and it would still have to happen if you were to change anything about saving (unless saves happened in real time, like in a database).


Exactly this. Making a collection concurrent doesn't automatically make all operations on the items in the collection thread-safe.

All this does is allow consumers of the collection to access it without worry in multithreaded environments. It exposes methods like AddOrUpdate and GetOrAdd which will allow you to make sure that the operation completes without it changing between lines of code. In a multithreaded environment, this: if( dictionary.containskey(..)) dictionary.GetValue(..) could fail as the item could then be removed between those two lines of code by another thread.
 

Vorspire

Knight
A lot of my systems run on their own separate thread, which is why I got exciting about this concept of usage, I see now that concurrency isn't needed in this context as RunUO is single-threaded in itself, the proposition was to run a Serialization service on a separate Thread to the main process and use concurrent dictionary as a safety measure. I also kinda like not having to write Contains checks for everything thanks to the new exposed methods, whether I use the concurrent dictionary for thread-safe access or generic use, although it may not perform as well.
 

mumuboy

Sorceror
Um apparently the new Dynamic Save strategy has broken Scripts\Engines\Faction\Core\Reflector.cs - Its (frequently) serializing (or deserializing, not sure which one) out of order.

I use that file to serialize/deserialize types. Maybe I shouldn't.
 

ASayre

RunUO Developer
Could you elaborate on what's going wrong? Is it the current code and faction system, or your code? What are you trying to do?
 

mumuboy

Sorceror
Yes, it is my own code.
I use Reflector.Serialize and Reflector.Deserialize from that file. I don't think anything uses those functions in distro. When I try to use those functions, I get a deserialization error. And from what I can gather, the index is out of order. It goes 0, 2, 2, 1, 0, 1, 1, 2, 2, etc. I am not sure what is causing this. My only guess is that it is the dynamic save strategy, since this problem started recently.

Here is an example:
Serialization:
Index: -1
Name: Server.Items.Pouch
Index: -1
Name: Server.Items.TrashBarrel
Index: -1
Name: (-null-)
Index: 2
Index: 1
Index: 1

Deserialization:
Index: -1
Name: Server.Items.Pouch
Index: 1
<--- Bad Deserialization -->
Index: -1
Name: Server.Items.TrashBarrel
Index: -1
Name: (-null-)
Index: 2
Index: 1
 

mumuboy

Sorceror
I used it because I assumed that it worked.
I guess you can take advantage of the types written to tdb if its recoded to do so. I am not sure how to do that way.
 

Pure Insanity

Sorceror
internal static List<Type> m_ItemTypes = new List<Type>();
internal static List<Type> m_MobileTypes = new List<Type>();

This is located in World. So you use World.m_ItemTypes to get access to a list of every type of item that exists. On world load, this list is populated. Also when new types of items/mobiles are created, they are added to this list.
 

mumuboy

Sorceror
I understand, but how is the order of these types maintained? through the tdb file?

I could do it that way, but the types are saved after the actual items/mobiles. Can they be saved ahead of time?:

Code:
            saveTasks[0] = SaveItems();
            saveTasks[1] = SaveMobiles();
            saveTasks[2] = SaveGuilds();
 
            SaveTypeDatabases();
 

mumuboy

Sorceror
I have a teleporter that checks for a particular type of item in the player's backpack. Since the teleporter is used for many different things, I made an ItemType property and I have it serialize/deserialize via those commands.

Honestly, I don't think it matters, the issue is that using the Serialize/Deserialize from the Reflector file doesn't work.
 

Pure Insanity

Sorceror
When you serialize your item type, I believe it would be a better solution to not use the Reflector class at all.

Just do

Code:
writer.Write(itemType.FullName);

I just don't understand why you'd need to use that class to do something like this. That class is strictly for Factions, if you use it to deserialize or even serialize something, it's going to add to the Types list for Factions...not just save your type.

To read the type back and add it to your own list, do something like this.

Code:
myList.Add( ScriptCompiler.FindTypeByFullName( reader.ReadString(), false ) );
 

mumuboy

Sorceror
The serialize and deserialize functions are not used at all. They can honestly be removed without consequence then.
And yes, I am using the full string name currently instead.
 

Vorspire

Knight
internal static List<Type> m_ItemTypes = new List<Type>();
internal static List<Type> m_MobileTypes = new List<Type>();

This is located in World. So you use World.m_ItemTypes to get access to a list of every type of item that exists. On world load, this list is populated. Also when new types of items/mobiles are created, they are added to this list.

internal properties cannot be accessed outside of their defining class scope, World.m_MobileTypes would only work within the World class or a nested class of World.
internal classes, depending on their parent scope, can not be accessed from other assemblies, nested internal classes cannot be referenced outside of their wrapping class.

The easiest way to access a list of types is by using the ScriptCompiler method; GetTypeCache(Assembly a)

or

Type[] types = Assembly.GetAssembly( typeof(PlayerMobile) ).GetTypes();
//This won't just fetch PlyerMobile Types, it will fetch all Types that belong to the Assembly that owns the PlayerMobile Type.

You can also use:
Type[] types = Assembly.GetCallingAssembly().GetTypes();

To use these methods, you must include a using directive for System.Reflection.
Reflection can be handy, but it is quite slow, not that it's very noticeable.


------
@Topic: It's probably happening because the Serialize/Deserialize methods do not compensate for a prefixed string length, like the BinaryFileWriter class which requires you to specify whether strings should be prefixed with a length.
 
Top