Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 游戏数据库设计_Java_Database_Oop - Fatal编程技术网

Java 游戏数据库设计

Java 游戏数据库设计,java,database,oop,Java,Database,Oop,从我上一篇文章开始已经有一段时间了,但我决定继续尝试游戏开发。所以现在我要看的是实际的数据存储。可能不是在游戏运行时使用的东西,而是在加载、保存和退出时使用的东西。。可能事实是我还不确定。然而,这不是我的时间问题。我面临的真正问题是数据库设计方面。由于某种原因,我无法思考如何设计数据库的items部分。我希望做一些既有效率又有活力的事情。让我谈谈我正在思考的一些事情 假设这是一个幻想的RPG,甚至可能是MMO(尽管这一部分并不重要) 试图保持面向对象的方法 现在问题是: 如果一个物品可以是多

从我上一篇文章开始已经有一段时间了,但我决定继续尝试游戏开发。所以现在我要看的是实际的数据存储。可能不是在游戏运行时使用的东西,而是在加载、保存和退出时使用的东西。。可能事实是我还不确定。然而,这不是我的时间问题。我面临的真正问题是数据库设计方面。由于某种原因,我无法思考如何设计数据库的items部分。我希望做一些既有效率又有活力的事情。让我谈谈我正在思考的一些事情

  • 假设这是一个幻想的RPG,甚至可能是MMO(尽管这一部分并不重要)
  • 试图保持面向对象的方法
现在问题是:

  • 如果一个物品可以是多个物品(例如,一种药剂/食物(我可以得到消耗品)、一把剑/一根棍棒(武器)),那么数据库的方法是什么
  • 作为上述问题的补充。。。所以一把剑可以有不同的规格。。根据基于材料的规格(基本属性)到潜在的基于增强的属性(增益或减益)。。我如何在数据库中封装这些信息,而不让每个项目都需要这些信息呢。(想法是,如果它对本项目不重要,则根本不包括它。
    • 我认为这样做的想法是..如果项目有一个火焰增强,那么在一个表(可能是某种连接表)中定义:
      
      StatID:此stat的主键
      项目ID:受影响的项目
      增强ID:增强,即火焰伤害
      增强效果:将为+/-值
      
  • 如果有人能阐明这一点将是伟大的,或者…如果有人知道一个伟大的位置找到这一信息

    我已经试着浏览了搜索结果。遗憾的是,我不知道到底要搜索什么,因为它会为这个主题和类型返回有用的结果。提前谢谢

    ~1编辑~ 这有点像我所想的那样,根据一个标志来区分不同类型的“项目”,即要查看哪个表。这在实践中有效吗?
    如果你没有一个预定义的模式,或者将来想对你的模式灵活,你可以考虑。如果你考虑使用云提供商,Azure有一个NoSQL存储(披露:我为微软工作,还有其他云提供商的其他选择,以及无云本地部署)。.

    多态类模型自然适合您的问题

    因为您标记了Java;JPA通过@javax.persistence.heritation注释支持继承


    JPA代表java持久性API。它提供了一个用于处理数据库的抽象层。您可以只注释模型类并让框架构建表——如果您要使用RDB,因为有些实现可以与NoSQL一起使用,我可能会通过使用3个值st来扩展它以获得高可伸缩性或,而不是每个修饰符2个

    这将允许您首先“键入”修饰符,其中一个类型是“augment”,然后第二个值将是一个类别,就像“augment”类型的“fire”类别一样,最后是一个值

    我可能会去

    项目ID(键)

    ImageId(因为您的项目可能要映射到图像)

    模式1

    ModCategory1

    ModValue1

    模式2

    ModCategory2

    ModValue2

    模式3

    模式分类3

    ModValue3

    通过确定所有这些值的特定大小,您可以在数据库中简化这一点(从而提高服务器的速度)

    我认为一个好的模式是:

    最多256类修饰符。(2字节)

    最多256种类型的修饰符(2字节)(因为如果这个修饰符是一个属性,我们可能会有超过16个属性)

    如果需要大的统计数据,值可能会增加到65536,所以让我们给它4个字节。实际上,虽然我们希望它是一个有符号的整数,所以我们有值-32768到32768

    现在我们可以使用一个int64并将其转换为一个复杂的修饰符

    我们希望将其拆分为字节,并为这三个值获取[2][2][4]个字节


    现在每个STAT只是一个In64 s的列!

    < p>好的,对于我的实现,我想存储各种项目类型,包括武器、服饰、物品、游戏事件、LootTables、能力等。我不想使用大量的多态性,因为我认为它是一个大系统中的弱点,尤其是我的游戏B有一个弱点。正如您所想象的,这将使非多态项的数据库机制很难创建

    我最后做的是创建一个静态GameDatabase类,该类在运行时初始化,从目录加载,并在整个游戏引擎中使用,而不必传递对数据库的引用。该数据库由每个项目类型的一个列表和一个关联枚举组成,该枚举与tem创建一个伪位掩码密钥。该密钥是一个整数。这意味着现在我们可以为游戏中的所有内容传递一个整数ID,而不是项目本身

    下面是我的游戏数据库的一个例子,它减少了文件加载。我也使用C#,因为我绝对讨厌Java

    using System;
    using System.Collections.Generic;
    using VoidwalkerEngine.Framework.Collections;
    using VoidwalkerEngine.Framework.DataTypes;
    using VoidwalkerEngine.Framework.Entities;
    using VoidwalkerEngine.Framework.Game.Items;
    using VoidwalkerEngine.Framework.Rendering.OpenGL.Modeling;
    
    namespace VoidwalkerEngine.Framework.Game.Managers
    {
    
        public enum DatabaseLocation
        {
            Abilities,
            Apparel,
            Actors,
            Classes,
            Events,
            Items,
            LootTables,
            LootTablePools,
            Models,
            Sounds,
            StatusEffects,
            Weapons
        }
    
        public static class GameDatabase
        {
    
            public static List<Ability> Abilities;
            public static List<Actor> Actors;
            public static List<Birthsign> Classes;
            public static List<GameEvent> Events;
            public static List<GameItem> Items;
            public static List<Weapon> Weapons;
            public static List<Apparel> Apparel;
            public static List<LootTable> LootTables;
            public static List<LootTablePool> LootPools;
            public static List<VoidwalkerModel> Models;
            public static List<GameSound> Sounds;
            public static List<StatusEffect> StatusEffects;
    
            public static void Create()
            {
                Abilities = new List<Ability>();
                Actors = new List<Actor>();
                Classes = new List<Birthsign>();
                Events = new List<GameEvent>();
                Items = new List<GameItem>();
                Weapons = new List<Weapon>();
                Apparel = new List<Apparel>();
                LootTables = new List<LootTable>();
                LootPools = new List<LootTablePool>();
                Models = new List<VoidwalkerModel>();
                Sounds = new List<GameSound>();
                StatusEffects = new List<StatusEffect>();
            }
    
            public static void Initialize(int identifier)
            {
                Initialize(new DatabaseKey(identifier));
            }
    
            /// <summary>
            /// Initializes the Database location with a new Object of that type.
            /// The identifier for the object is automatically added.
            /// </summary>
            /// <param name="key"></param>
            public static void Initialize(DatabaseKey key)
            {
                int identifier = key.Identifier;
                int index = key.Index;
                switch (key.Location)
                {
                    case DatabaseLocation.Abilities:
                        Abilities[index] = new Ability(identifier);
                        break;
                    case DatabaseLocation.Apparel:
                        Apparel[index] = new Apparel(identifier);
                        break;
                    case DatabaseLocation.Actors:
                        Actors[index] = new Actor(identifier);
                        break;
                    case DatabaseLocation.Classes:
                        Classes[index] = new Birthsign(identifier);
                        break;
                    case DatabaseLocation.Events:
                        Events[index] = new GameEvent(identifier);
                        break;
                    case DatabaseLocation.Items:
                        Items[index] = new GameItem(identifier);
                        break;
                    case DatabaseLocation.LootTables:
                        LootTables[index] = new LootTable(identifier);
                        break;
                    case DatabaseLocation.LootTablePools:
                        LootPools[index] = new LootTablePool(identifier);
                        break;
                    case DatabaseLocation.Models:
                        Models[index] = new VoidwalkerModel(identifier);
                        break;
                    case DatabaseLocation.Sounds:
                        Sounds[index] = new GameSound(identifier);
                        break;
                    case DatabaseLocation.StatusEffects:
                        StatusEffects[index] = new StatusEffect(identifier);
                        break;
                    case DatabaseLocation.Weapons:
                        Weapons[index] = new Weapon(identifier);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
    
            public static object Query(int identifier)
            {
                return Query(new DatabaseKey(identifier));
            }
    
            public static object Query(DatabaseKey key)
            {
                int index = key.Index;
                switch (key.Location)
                {
                    case DatabaseLocation.Abilities:
                        return Abilities[index];
                    case DatabaseLocation.Apparel:
                        return Apparel[index];
                    case DatabaseLocation.Actors:
                        return Actors[index];
                    case DatabaseLocation.Classes:
                        return Classes[index];
                    case DatabaseLocation.Events:
                        return Events[index];
                    case DatabaseLocation.Items:
                        return Items[index];
                    case DatabaseLocation.LootTables:
                        return LootTables[index];
                    case DatabaseLocation.LootTablePools:
                        return LootPools[index];
                    case DatabaseLocation.Models:
                        return Models[index];
                    case DatabaseLocation.Sounds:
                        return Sounds[index];
                    case DatabaseLocation.StatusEffects:
                        return StatusEffects[index];
                    case DatabaseLocation.Weapons:
                        return Weapons[index];
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }
    }
    
    这会产生一个整数标识符:
    50025
    或十六进制:
    0xC369
    。这意味着基本游戏项目位于50000-59999范围内。因此,您需要50000+数组索引。这为我们提供了一个映射项目的整数。在内部传递项目将使用项目堆栈实现;而e是我的:

    using System;
    using VoidwalkerEngine.Framework.Maths;
    
    namespace VoidwalkerEngine.Framework.DataTypes
    {
        [Serializable]
        public class ItemStack
        {
    
            /// <summary>
            /// The ID of the Associated Item.
            /// </summary>
            public int Identifier { get; set; }
    
            private int _quantity;
    
            /// <summary>
            /// The Quantity of this ItemStack.
            /// </summary>
            public int Quantity
            {
                get
                {
                    return _quantity;
                }
                set
                {
                    this._quantity = VoidwalkerMath.Clamp(value, MinQuantity, MaxQuantity);
                }
            }
    
            private int _maxQuantity = Int32.MaxValue;
    
            /// <summary>
            /// The Maximum Quantity of this Stack.
            /// </summary>
            public int MaxQuantity
            {
                get
                {
                    return _maxQuantity;
                }
                set
                {
                    this._maxQuantity = VoidwalkerMath.Clamp(value, MinQuantity, Int32.MaxValue);
                    this.Quantity = this.Quantity;
                }
            }
    
            public const int MinQuantity = 0;
    
            public bool IsStackable
            {
                get
                {
                    return this.MaxQuantity != 1;
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            public bool IsEmpty
            {
                get
                {
                    return this.Quantity == MinQuantity;
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            public bool IsFull
            {
                get
                {
                    return this.Quantity == this.MaxQuantity;
                }
            }
    
            public ItemStack()
            {
    
            }
    
            public ItemStack(int identifier, int quantity = 1, int maxQuantity = Int32.MaxValue)
            {
                this.Identifier = identifier;
                this.Quantity = quantity;
                this.MaxQuantity = maxQuantity;
            }
    
            /// <summary>
            /// Adds the specified quantity to this ItemStack. If
            /// the ItemStack is already full or close to being full,
            /// this ItemStack will overflow into a new ItemStack as the
            /// return value.
            /// </summary>
            /// <param name="quantity"></param>
            /// <returns></returns>
            public ItemStack Add(int quantity)
            {
                if (quantity <= MinQuantity)
                {
                    return null;
                }
                int overflow = ComputeOverflow(quantity);
                if (overflow > MinQuantity)
                {
                    this.Quantity += quantity;
                    return new ItemStack(this.Identifier, overflow, this.MaxQuantity);
                }
                this.Quantity += quantity;
                return null;
            }
    
            /// <summary>
            /// Adds the specified quantity to this ItemStack. If
            /// the ItemStack is already full or close to being full,
            /// this ItemStack will overflow into a new ItemStack as the
            /// return value.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public ItemStack Add(ItemStack other)
            {
                ItemStack stack = Add(other.Quantity);
                other.Subtract(other.Quantity);
                return stack;
            }
    
            /// <summary>
            /// Subtracts the specified quantity from this ItemStack. If
            /// the ItemStack is already empty or close to being empty,
            /// this ItemStack will underflow into a new ItemStack as the
            /// return value.
            /// </summary>
            /// <param name="quantity"></param>
            /// <returns></returns>
            public ItemStack Subtract(int quantity)
            {
                if (quantity <= MinQuantity)
                {
                    return null;
                }
                int underflow = ComputeUnderflow(quantity);
                if (underflow > MinQuantity)
                {
                    this.Quantity -= (quantity - underflow);
                }
                this.Quantity -= quantity;
                return new ItemStack(this.Identifier, quantity, this.MaxQuantity);
            }
    
            /// <summary>
            /// Subtracts the specified quantity from this ItemStack. If
            /// the ItemStack is already empty or close to being empty,
            /// this ItemStack will underflow into a new ItemStack as the
            /// return value.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public ItemStack Subtract(ItemStack other)
            {
                ItemStack stack = Subtract(other.Quantity);
                other.Subtract(stack.Quantity);
                return stack;
            }
    
            /// <summary>
            /// Clears the Quantity of this ItemStack to 0. MaxValue, however, remains the same.
            /// </summary>
            public void Clear()
            {
                this._quantity = MinQuantity;
            }
    
            /// <summary>
            /// Makes the currect Quantity of this ItemStack equal to the MaxValue of this ItemStack.
            /// </summary>
            public void Fill()
            {
                this._quantity = MaxQuantity;
            }
    
            /// <summary>
            /// Splits this ItemStack into another, giving half to both stacks.
            /// If the split amount is an odd number, the result gets +1 so no
            /// loss of items happens due to rounding errors.
            /// </summary>
            /// <returns></returns>
            public ItemStack Split()
            {
                if (this.Quantity <= (MinQuantity + 1))
                {
                    return null; // A split is impossible.
                }
                int splitResult = (this.Quantity / 2);
                if (this.Quantity % 2 == 0)
                {
                    this.Quantity = splitResult;
                    return new ItemStack(this.Identifier, splitResult, this.MaxQuantity);
                }
                this.Quantity = splitResult;
                return new ItemStack(this.Identifier, splitResult + 1, this.MaxQuantity);
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="other"></param>
            public void Copy(ItemStack other)
            {
                this.Identifier = other.Identifier;
                this.Quantity = other.Quantity;
                this.MaxQuantity = other.MaxQuantity;
            }
    
            /// <summary>
            /// Creates a new ItemStack which is an exact copy of this ItemStack.
            /// </summary>
            /// <returns></returns>
            public ItemStack MakeCopy()
            {
                return new ItemStack(this.Identifier, this.Quantity, this.MaxQuantity);
            }
    
            /// <summary>
            /// Determines if this ItemStack is stackable with another ItemStack. This function tests
            /// for a match between the string Identifiers, and whether or not this ItemStack is Stackable.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public bool IsStackableWith(ItemStack other)
            {
                return this.Identifier == other.Identifier && this.IsStackable;
            }
    
            /// <summary>
            /// Calculates the amount of overflow that will take place
            /// if the desired ItemStack is added to this one.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public int ComputeOverflow(ItemStack other)
            {
                return ComputeOverflow(other.Quantity);
            }
    
            /// <summary>
            /// Calculates the amount of overflow that will take place
            /// if the desired amount is added to this one.
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public int ComputeOverflow(int amount)
            {
                if (amount <= MinQuantity)
                {
                    return MinQuantity;
                }
                int total = ((this.Quantity + amount) - this.MaxQuantity);
                if (total < MinQuantity)
                {
                    return MinQuantity;
                }
                return total;
            }
    
            /// <summary>
            /// Calculates the amount of underflow that will take place
            /// if the desired ItemStack is subtracted from this one.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public int ComputeUnderflow(ItemStack other)
            {
                return ComputeUnderflow(other.Quantity);
            }
    
            /// <summary>
            /// Calculates the amount of underflow that will take place
            /// if the desired amount is subtracted from this one.
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public int ComputeUnderflow(int amount)
            {
                if (amount <= MinQuantity)
                {
                    return MinQuantity;
                }
                int total = (this.Quantity - amount);
                if (total > MinQuantity)
                {
                    return MinQuantity;
                }
                return Math.Abs(total);
            }
    
            /// <summary>
            /// Determines if this ItemStack has the specified quantity.
            /// If the quantity is less than or equal to 0, this function
            /// will always return false.
            /// </summary>
            /// <param name="quantity"></param>
            /// <returns></returns>
            public bool HasQuantity(int quantity)
            {
                if (quantity <= MinQuantity)
                {
                    return false;
                }
                return this.Quantity >= quantity;
            }
    
            /// <summary>
            /// Determines if this ItemStack can still fit the specified amount
            /// without overflowing into a new ItemStack. If the quantity is less
            /// than or equal to 0, this function will always return false.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public bool CanHold(ItemStack other)
            {
                return CanHold(other.Quantity);
            }
    
            /// <summary>
            /// Determines if this ItemStack can still fit the specified amount
            /// without overflowing into a new ItemStack. If the quantity is less
            /// than or equal to 0, this function will always return false.
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public bool CanHold(int amount)
            {
                if (amount <= MinQuantity)
                {
                    return false;
                }
                return this.Quantity + amount <= MaxQuantity;
            }
    
            /// <summary>
            /// Determines if this ItemStack can subtract the specified amount
            /// without underflowing into a new ItemStack. If the quantity is less
            /// than or equal to 0, this function will always return false.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public bool CanSubtract(ItemStack other)
            {
                return CanSubtract(other.Quantity);
            }
    
            /// <summary>
            /// Determines if this ItemStack can subtract the specified amount
            /// without underflowing into a new ItemStack. If the quantity is less
            /// than or equal to 0, this function will always return false.
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public bool CanSubtract(int amount)
            {
                if (amount <= MinQuantity)
                {
                    return false;
                }
                return this.Quantity - amount >= MinQuantity;
            }
    
            public bool Equals(ItemStack other)
            {
                if (other != null)
                {
                    return 
                        this.Identifier == other.Identifier &&
                        this.Quantity == other.Quantity &&
                        this.MaxQuantity == other.MaxQuantity;
                }
                return false;
            }
    
            public override string ToString()
            {
                return $"{this.Identifier},{this.Quantity},{this.MaxQuantity}";
            }
        }
    }
    
    使用系统;
    使用VoidwalkerEngine.Framework.Math;
    命名空间VoidwalkerEngine.Framework.DataTypes
    {
    [可序列化]
    公共类项目堆栈
    {
    /// 
    ///关联项的ID。
    /// 
    公共整数标识符{get;s
    
    DatabaseKey key = new DatabaseKey(DatabaseLocation.Items,24);
    
    using System;
    using VoidwalkerEngine.Framework.Maths;
    
    namespace VoidwalkerEngine.Framework.DataTypes
    {
        [Serializable]
        public class ItemStack
        {
    
            /// <summary>
            /// The ID of the Associated Item.
            /// </summary>
            public int Identifier { get; set; }
    
            private int _quantity;
    
            /// <summary>
            /// The Quantity of this ItemStack.
            /// </summary>
            public int Quantity
            {
                get
                {
                    return _quantity;
                }
                set
                {
                    this._quantity = VoidwalkerMath.Clamp(value, MinQuantity, MaxQuantity);
                }
            }
    
            private int _maxQuantity = Int32.MaxValue;
    
            /// <summary>
            /// The Maximum Quantity of this Stack.
            /// </summary>
            public int MaxQuantity
            {
                get
                {
                    return _maxQuantity;
                }
                set
                {
                    this._maxQuantity = VoidwalkerMath.Clamp(value, MinQuantity, Int32.MaxValue);
                    this.Quantity = this.Quantity;
                }
            }
    
            public const int MinQuantity = 0;
    
            public bool IsStackable
            {
                get
                {
                    return this.MaxQuantity != 1;
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            public bool IsEmpty
            {
                get
                {
                    return this.Quantity == MinQuantity;
                }
            }
    
            /// <summary>
            /// 
            /// </summary>
            public bool IsFull
            {
                get
                {
                    return this.Quantity == this.MaxQuantity;
                }
            }
    
            public ItemStack()
            {
    
            }
    
            public ItemStack(int identifier, int quantity = 1, int maxQuantity = Int32.MaxValue)
            {
                this.Identifier = identifier;
                this.Quantity = quantity;
                this.MaxQuantity = maxQuantity;
            }
    
            /// <summary>
            /// Adds the specified quantity to this ItemStack. If
            /// the ItemStack is already full or close to being full,
            /// this ItemStack will overflow into a new ItemStack as the
            /// return value.
            /// </summary>
            /// <param name="quantity"></param>
            /// <returns></returns>
            public ItemStack Add(int quantity)
            {
                if (quantity <= MinQuantity)
                {
                    return null;
                }
                int overflow = ComputeOverflow(quantity);
                if (overflow > MinQuantity)
                {
                    this.Quantity += quantity;
                    return new ItemStack(this.Identifier, overflow, this.MaxQuantity);
                }
                this.Quantity += quantity;
                return null;
            }
    
            /// <summary>
            /// Adds the specified quantity to this ItemStack. If
            /// the ItemStack is already full or close to being full,
            /// this ItemStack will overflow into a new ItemStack as the
            /// return value.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public ItemStack Add(ItemStack other)
            {
                ItemStack stack = Add(other.Quantity);
                other.Subtract(other.Quantity);
                return stack;
            }
    
            /// <summary>
            /// Subtracts the specified quantity from this ItemStack. If
            /// the ItemStack is already empty or close to being empty,
            /// this ItemStack will underflow into a new ItemStack as the
            /// return value.
            /// </summary>
            /// <param name="quantity"></param>
            /// <returns></returns>
            public ItemStack Subtract(int quantity)
            {
                if (quantity <= MinQuantity)
                {
                    return null;
                }
                int underflow = ComputeUnderflow(quantity);
                if (underflow > MinQuantity)
                {
                    this.Quantity -= (quantity - underflow);
                }
                this.Quantity -= quantity;
                return new ItemStack(this.Identifier, quantity, this.MaxQuantity);
            }
    
            /// <summary>
            /// Subtracts the specified quantity from this ItemStack. If
            /// the ItemStack is already empty or close to being empty,
            /// this ItemStack will underflow into a new ItemStack as the
            /// return value.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public ItemStack Subtract(ItemStack other)
            {
                ItemStack stack = Subtract(other.Quantity);
                other.Subtract(stack.Quantity);
                return stack;
            }
    
            /// <summary>
            /// Clears the Quantity of this ItemStack to 0. MaxValue, however, remains the same.
            /// </summary>
            public void Clear()
            {
                this._quantity = MinQuantity;
            }
    
            /// <summary>
            /// Makes the currect Quantity of this ItemStack equal to the MaxValue of this ItemStack.
            /// </summary>
            public void Fill()
            {
                this._quantity = MaxQuantity;
            }
    
            /// <summary>
            /// Splits this ItemStack into another, giving half to both stacks.
            /// If the split amount is an odd number, the result gets +1 so no
            /// loss of items happens due to rounding errors.
            /// </summary>
            /// <returns></returns>
            public ItemStack Split()
            {
                if (this.Quantity <= (MinQuantity + 1))
                {
                    return null; // A split is impossible.
                }
                int splitResult = (this.Quantity / 2);
                if (this.Quantity % 2 == 0)
                {
                    this.Quantity = splitResult;
                    return new ItemStack(this.Identifier, splitResult, this.MaxQuantity);
                }
                this.Quantity = splitResult;
                return new ItemStack(this.Identifier, splitResult + 1, this.MaxQuantity);
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="other"></param>
            public void Copy(ItemStack other)
            {
                this.Identifier = other.Identifier;
                this.Quantity = other.Quantity;
                this.MaxQuantity = other.MaxQuantity;
            }
    
            /// <summary>
            /// Creates a new ItemStack which is an exact copy of this ItemStack.
            /// </summary>
            /// <returns></returns>
            public ItemStack MakeCopy()
            {
                return new ItemStack(this.Identifier, this.Quantity, this.MaxQuantity);
            }
    
            /// <summary>
            /// Determines if this ItemStack is stackable with another ItemStack. This function tests
            /// for a match between the string Identifiers, and whether or not this ItemStack is Stackable.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public bool IsStackableWith(ItemStack other)
            {
                return this.Identifier == other.Identifier && this.IsStackable;
            }
    
            /// <summary>
            /// Calculates the amount of overflow that will take place
            /// if the desired ItemStack is added to this one.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public int ComputeOverflow(ItemStack other)
            {
                return ComputeOverflow(other.Quantity);
            }
    
            /// <summary>
            /// Calculates the amount of overflow that will take place
            /// if the desired amount is added to this one.
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public int ComputeOverflow(int amount)
            {
                if (amount <= MinQuantity)
                {
                    return MinQuantity;
                }
                int total = ((this.Quantity + amount) - this.MaxQuantity);
                if (total < MinQuantity)
                {
                    return MinQuantity;
                }
                return total;
            }
    
            /// <summary>
            /// Calculates the amount of underflow that will take place
            /// if the desired ItemStack is subtracted from this one.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public int ComputeUnderflow(ItemStack other)
            {
                return ComputeUnderflow(other.Quantity);
            }
    
            /// <summary>
            /// Calculates the amount of underflow that will take place
            /// if the desired amount is subtracted from this one.
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public int ComputeUnderflow(int amount)
            {
                if (amount <= MinQuantity)
                {
                    return MinQuantity;
                }
                int total = (this.Quantity - amount);
                if (total > MinQuantity)
                {
                    return MinQuantity;
                }
                return Math.Abs(total);
            }
    
            /// <summary>
            /// Determines if this ItemStack has the specified quantity.
            /// If the quantity is less than or equal to 0, this function
            /// will always return false.
            /// </summary>
            /// <param name="quantity"></param>
            /// <returns></returns>
            public bool HasQuantity(int quantity)
            {
                if (quantity <= MinQuantity)
                {
                    return false;
                }
                return this.Quantity >= quantity;
            }
    
            /// <summary>
            /// Determines if this ItemStack can still fit the specified amount
            /// without overflowing into a new ItemStack. If the quantity is less
            /// than or equal to 0, this function will always return false.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public bool CanHold(ItemStack other)
            {
                return CanHold(other.Quantity);
            }
    
            /// <summary>
            /// Determines if this ItemStack can still fit the specified amount
            /// without overflowing into a new ItemStack. If the quantity is less
            /// than or equal to 0, this function will always return false.
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public bool CanHold(int amount)
            {
                if (amount <= MinQuantity)
                {
                    return false;
                }
                return this.Quantity + amount <= MaxQuantity;
            }
    
            /// <summary>
            /// Determines if this ItemStack can subtract the specified amount
            /// without underflowing into a new ItemStack. If the quantity is less
            /// than or equal to 0, this function will always return false.
            /// </summary>
            /// <param name="other"></param>
            /// <returns></returns>
            public bool CanSubtract(ItemStack other)
            {
                return CanSubtract(other.Quantity);
            }
    
            /// <summary>
            /// Determines if this ItemStack can subtract the specified amount
            /// without underflowing into a new ItemStack. If the quantity is less
            /// than or equal to 0, this function will always return false.
            /// </summary>
            /// <param name="amount"></param>
            /// <returns></returns>
            public bool CanSubtract(int amount)
            {
                if (amount <= MinQuantity)
                {
                    return false;
                }
                return this.Quantity - amount >= MinQuantity;
            }
    
            public bool Equals(ItemStack other)
            {
                if (other != null)
                {
                    return 
                        this.Identifier == other.Identifier &&
                        this.Quantity == other.Quantity &&
                        this.MaxQuantity == other.MaxQuantity;
                }
                return false;
            }
    
            public override string ToString()
            {
                return $"{this.Identifier},{this.Quantity},{this.MaxQuantity}";
            }
        }
    }