Open No3371 opened 5 years ago
A Material List of a thing would be:
Thingy { typeID: MaterialList }
- Thingy { typeID: Material }
- Int Value { typeID: MaterialType, Int: 71 }
- Float Value: { typeID: Mass, Float: 1 }
- Byte Value: { typeID: Purity, Byte: 90 }
The core concept of Thingy is "Because it could be". If we rely on C# type system to define concrete class type derived from Thingy, such as Weapon, Tool, Material...
Sure, a marker identifiable by C# is helpful, but if that's the only purpose, Type object works not better then a int Type field, and it will force developer to create a bunch of concrete Thingy class that's basically same, for each Thingy concept in their mind. Even with a code generator, it'd be hard to maintain.
By design, a Thingy can not contains more then 1 Thingy of same type. Though it is technically possible, the complexity of the system will dramatically increase, and is not really necessary.
Instead, a specialized Thingy is created: ThingyGroup, it only accept Thingy of specified type as Modules, and could be used as a List of same type Thingy.
There are 2 types of filter:
Filter is a pattern matching tool. Like Thingy, Filter could be nested, and a nested filter is a pattern. For example, imagine a filter that select anything that
Filter { target: ANY, AND }
- Filter { target: Material, AND }
- IntValueFilter { target: MaterialType, IsEqual, 7 (ID of Iron) }
- Filter { target: Tool, AND }
- Filter { target: Weapon, AND }
- IntValueFilter { target: Damage, IsBigger, 11 }
- IntValueFilter { target: Damage, IsSmaller, 16 }
If you apply the filter to a bunch of Thingy, anything has a structure like this will be selected:
Thingy { TypeNameID: ANY }
- Thingy { TypeNameID: Material }
- IntValueThingy { TypeNameID: MaterialType, 7 } (Must be 7)
- Thingy { TypeNameID: Weapon }
- IntValueThingy { TypeNameID: Damage, 13} ) ( Could be 11 ~ 16)
A wood sword handle is very likely "could be" a torch body. Assuming 2 wood with exact same properties, the only difference is one is used in a Sword Thingy while the other one is used in Torch Thingy.
The Context is different, but nothing different inside the Thingy itself, why don't we use only 1 Thingy so we could reduce memory usage?
Thingy is immutable. We have no need to worry a existed Thingy would change, only have to keep an eye on new Thingy in order to make sure that there are never going to be 2 same Thingy.
This require hashing. If we design the system right, we could quickly check if there's any existed Thingy that is just identical to a creating one.
Understandable string is required for us to interact with the data , but the communication inside the system does not.
To reduce memory usage and query time, I want to use Integer as FilterTargetID/TypeNameID, so we don't have to keep a string object in every Thingy while enjoy the fast comparison of Int.
In order to achieve this, a dynamic string<->int mapping is required.
Technically, clients call GetOrCreateTypeID(string name) of a given ThingyLibrary, which will check if the passed in name is already registered, if not, generate a new unique int ID and register the string-int pair; if registered, return the paired int.
If we always go through the above process for every Thingy every time, a critical problem emerge. If I create a A thingy, the library generate a int, for example, 1121 for this TypeNameID (A). Then I close the application. Will the generated TypeNameID for A still 1121?
If we use the hashcode of A as ID, maybe, that will be the case most of the time, but this is not a reliable design. A minor change in the hash algorithm of string, or a hashcode collision from different name string, the system will explode...
In order to assure consistency between sessions, we have to keep track of previous mapping, so that if a different name is registered, it wont override previous mapping. A copy of the mapping data should be saved along with the database.
public override int GetHashCode ()
{
int output = 109;
HashCodeTraversal(ref output);
return output;
}
protected virtual void HashCodeTraversal (ref int output)
{
output += ThingTypeID * 71;
if (Modules != null)
foreach (Thingy t in Modules)
{
t.HashCodeTraversal(ref output);
}
}
Is essentially a pre-loaded Thingy that act as a prototype, for clients to reference.
How can you discriminate between Thingy of same TypeNameID inside a same parent? I don't want to add meta data to Thingy itself, since it's unnecessary.
ThingyGroup is a specialized Thingy that only accept a specified TypeNameID and Thingy of that TypeNameID as modules.
It store extra (int, string) as index and tag for each Thingy it owns.
Goal
To create a high performance, easy to extend/manage, component oriented data entity framework.
Possible Usage
A + B = C
The formula above are describing that:
Based on this concept, we can consider A/B as Filter, which is to classify a thing. While C is a template, which is a predefined thing with certain modules built in.
Everything is a composition
The core concept of Thingy is Something is something because it can be.
Let's say here we got a thing with these modules:
What could it be? I'd say it's a good old iron sword.
How could it be made? Let's make up a simple and minecraft-ish one:
So what is Wood Stick? If we want it to be simple, this is how you do in traditional crafting mechanic:
So only stuff that is labeled "Wood Stick" can fit the first slot... which is a common approach, boring and inflexible.
If we review the formula Wood Stick + Iron = Iron Sword, we could notice that, it's actually Sword Handle + Body Material = Sword. That's where we can utilize the design of Thingy, Imagine a "Sword Handle" Filter:
Using the filter, a lot of thing would be valid for the first input slot of this Sword recipe, even if you put in a torch, it's possible the torch body could be a valid sword handle.