admin管理员组

文章数量:1134247

I'm trying to create a logger for item drops for a game that I'm playing. I can get the item name, id, etc. from the game logs, but I'm having a hard time figuring out how to import and then store that information inside of my .Net app. After some research, I think creating a class with the fields that I want to use and then using a list would be best.

My problem is, I don't know how many different Dropped_From_NPC_# and Dropped_In_Zone_# there will be for each item. Some items might only have 1 NPC/Zone it drops from while others might have hundreds.

What is the simplest way to bring that information into my applications memory so that I can search for specific pieces of it later on?

public class ItemDatabaseInformation
{
    public string Name { get; set; }
    public Int64 LinkID { get; set; }
    public Int64 IconID { get; set; }

    public string Dropped_From_NPC_1 { get; set; }
    public string Dropped_In_Zone_1 { get; set; }
    public string Dropped_From_NPC_2 { get; set; }
    public string Dropped_In_Zone_2 { get; set; }
    public string Dropped_From_NPC_3 { get; set; }
    public string Dropped_In_Zone_3 { get; set; }
}

I'm trying to create a logger for item drops for a game that I'm playing. I can get the item name, id, etc. from the game logs, but I'm having a hard time figuring out how to import and then store that information inside of my .Net app. After some research, I think creating a class with the fields that I want to use and then using a list would be best.

My problem is, I don't know how many different Dropped_From_NPC_# and Dropped_In_Zone_# there will be for each item. Some items might only have 1 NPC/Zone it drops from while others might have hundreds.

What is the simplest way to bring that information into my applications memory so that I can search for specific pieces of it later on?

public class ItemDatabaseInformation
{
    public string Name { get; set; }
    public Int64 LinkID { get; set; }
    public Int64 IconID { get; set; }

    public string Dropped_From_NPC_1 { get; set; }
    public string Dropped_In_Zone_1 { get; set; }
    public string Dropped_From_NPC_2 { get; set; }
    public string Dropped_In_Zone_2 { get; set; }
    public string Dropped_From_NPC_3 { get; set; }
    public string Dropped_In_Zone_3 { get; set; }
}
Share Improve this question edited Jan 7 at 18:12 TylerH 21.2k76 gold badges79 silver badges110 bronze badges asked Jan 7 at 18:10 bjcaseybjcasey 371 silver badge6 bronze badges 2
  • 3 Use an array or a List instead of separate fields for each zone etc. – wohlstad Commented Jan 7 at 18:15
  • 1 Are the NPC/Zone related? I.e. is it conceptually dropped in+by [{NPC, Zone}, {NPC, Zone}, {NPC, Zone}], or dropped in [Zone, Zone, Zone] and dropped by [NPC, NPC, NPC]? – AKX Commented Jan 7 at 18:22
Add a comment  | 

3 Answers 3

Reset to default 1

The classical OOP approach to this would be to create another class that stores the information you care about for each time a drop event occurs. Then, you can store a List<T> of that class and add to it as drop events happen.

Something like:

public class DropInfo 
{
    public string ItemName { get; set; }
    public int NpcId { get; set; }
    public int ZoneId { get; set; }
}

And then, in your database class:

public class ItemDatabaseInformation
{
    public string Name { get; set; }
    public Int64 LinkID { get; set; }
    public Int64 IconID { get; set; }

    public List<DropInfo> DroppedItems = new List<DropInfo>();
    public void RecordItemDropped(string itemName, int npcId, int zoneId)
    {
         DroppedItems.Add(new DropInfo()
         {
             ItemName = itemName,
             NpcId = npcId,
             zoneId = zoneId
         });
    }
}

Edit: For the searching component, there are various mechanisms, such as Linq, that provide ways of searching lists for an element with a desired property.

For example:

public DropInfo FindDroppedItem(string itemName)
{
    return DroppedItems
        .Where(i => i.ItemName == itemName)
        .FirstOrDefault();
}

Your question asks for a simple way to work with "item drops" data. Specifically:

to bring that information into my applications memory so that I can search for specific pieces of it later on.

Placing the emphasis on "search" often the simplest way involves a database, and your additional requirement is for one that is in memory and not persisted on disk. One way to declare one is to install the sqlite-net-pcl NuGet and then use ":memory:" as the database path of the SQLiteConnection that is used throughout.

// <PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
using SQLite;
public partial class MainForm : Form
{
    // IS: "Storing Information in Memory" 
    // IS NOT: Persisted on disk (which requires a database file name instead of ":memory:")
    readonly SQLiteConnection ItemDatabase = new SQLiteConnection(":memory:");

    public MainForm()
    {
        InitializeComponent();
        ItemDatabase.CreateTable<ItemRecord>();
        ItemDatabase.CreateTable<DropRecord>();
        Disposed += (sender, e) => ItemDatabase.Dispose();
        textBox.TextChanged += RefreshQuery;
        _checkboxes = tableLayoutPanel.Controls.OfType<CheckBox>().ToArray();
        foreach (var checkbox in _checkboxes) checkbox.CheckedChanged += RefreshQuery;
    }
    CheckBox[] _checkboxes;

    .
    .
    .
}

Unknown Number of Nodes

You stated THREE requirements:

  1. In Memory
  2. Easily Searchable
  3. Unknown number of nodes.

One common way of dealing with #3 is having two tables in the database.


The first table holds item header information, indexed e.g. by the Name property or any other property that is going to uniquely identify it.


[Table("items")]
class ItemRecord
{
    [PrimaryKey, Indexed]
    public string? Name { get; set; }
    public Int64 LinkID { get; set; } = Guid.NewGuid().ToString().GetHashCode();
    public Int64 IconID { get; set; } = Guid.NewGuid().ToString().GetHashCode();

}

The second table holds instances of drops. The ParentName in this case identifies the master item associated with it. Other metadata info like LinkID or IconID is not repeated. And like you said, some master items might only have 1 NPC/Zone drop while others might have hundreds.

[Table("drops")]
class DropRecord
{
    // Arbitrary value to uniquely identify the record.
    [PrimaryKey, Browsable(false)]
    public string Uid { get; set; } = Guid.NewGuid().ToString().ToUpper();
    [NotNull]
    public string? ParentName {  get; set; }
    public DateTimeOffset TimeStamp { get; set; } = DateTimeOffset.Now;
    public string? DroppedFrom { get; set; }
    public string? DroppedFromValue { get; set; }
    public string? DroppedIn { get; set; }
    public string? DroppedInValue { get; set; }
}

This way, you have flexibility with your search. You can get the items associated with a name, for example.

Or you can filter by NPC/Zone info or a combination of name + drop info.


Basic Search

This shows a basic SQL query as a starting point. It responds to "only" the name text OR "only" the checked boxes OR a combination of both.

public partial class MainForm : Form
{
    .
    .
    .
    private void RefreshQuery(object? sender, EventArgs e)
    {
        var nameClause = textBox.TextLength > 0 ? $"ParentName LIKE '%{textBox.Text}%'": null;
        var drops = 
            _checkboxes
            .Where(_ => _.Checked).Select(_ => _.Name.Replace("checkBox", string.Empty))
            .ToArray();
        var dropsClause = drops.Any()
            ? string.Join(" OR ", drops.Select(_=>$"DroppedFrom='{_}' OR DroppedIn='{_}'"))
            : null;
        var matches = new List<DropRecord>();
        string? sql = null;
        if (nameClause is null ^ dropsClause is null)
        {
            if (nameClause is not null)
                sql = $"SELECT * FROM drops WHERE {nameClause}";
            else 
                sql = $"SELECT * FROM drops WHERE {dropsClause}";
        }
        else if (nameClause is not null)
            sql = $"SELECT * FROM drops WHERE {nameClause} AND ({dropsClause})";
        if (sql is not null) matches = ItemDatabase.Query<DropRecord>(sql);

        FilteredDropRecords.Clear();
        foreach (var item in matches)
        {
            FilteredDropRecords.Add(item);
        }
    }
}

You can ensure database to save nodes to tables and get from db with Entity Framework Core nugget in .net. If you want to store not too large data use SQLite. You can see documentations here

Before you create a database. Classes you recording must have primary keys

public class ItemDatabaseInformation
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public Int64 LinkID { get; set; }
    public Int64 IconID { get; set; }

    public List<Node>nodes { get; set; } = new List<Node>()
}

public class Node
{
    [Key]
    public int Id { get; set; }
    public string Zone { get; set; }
    public string Npc { get; set; }
}

Edit: I figured out the problem is the how to store many points from and in it dropped

本文标签: cStoring Information in Memory with Unknown Number of NodesStack Overflow