123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace Island.StandardLib.Storage
- {
- public class PlayerPackage : IStorable
- {
- public static RunDevice Device { get; private set; }
- static Dictionary<int, AbstractItemBehavior> ItemBehaviors;
- static AbstractController Controller;
- public static void DefineItemBehaviors(AbstractController controller, RunDevice device, params AbstractItemBehavior[] behaviors)
- {
- Device = device;
- Controller = controller;
- if (ItemBehaviors != null)
- throw new Exception("ItemBehavior is already defined.");
- ItemBehaviors = new Dictionary<int, AbstractItemBehavior>();
- foreach (AbstractItemBehavior behavior in behaviors)
- {
- if (ItemBehaviors.ContainsKey(behavior.Id))
- throw new Exception($"Redefined ItemBehavior, Id = {behavior.Id}");
- ItemBehaviors.Add(behavior.Id, behavior);
- }
- }
- public static AbstractItemBehavior GetItemBehavior(int id)
- {
- if (ItemBehaviors == null)
- throw new Exception("ItemBehavior is undefined. Use DefineItemBehaviors(...) to set behaviors first.");
- if (ItemBehaviors.TryGetValue(id, out AbstractItemBehavior behavior))
- return behavior;
- throw new Exception($"GetItemBehavior() Use undefined item behavior Id = {id}");
- }
- public class ItemData : IStorable
- {
- public int DataType;
- public byte[] Data;
- public MultData DataVisitor
- {
- get => new MultData(Data);
- set => Data = value.Data;
- }
- public ItemData()
- {
- DataType = 0;
- Data = new byte[0];
- }
- public ItemData(int dataType, MultData data)
- {
- DataType = dataType;
- Data = data.Data;
- }
- public ItemData(object data, int iStorageType = 10)
- {
- if (data is MultData)
- {
- DataVisitor = (MultData)data;
- DataType = 7;
- }
- else if (data is byte[])
- {
- DataVisitor = new MultData((byte[])data);
- DataType = 7;
- }
- else if (data is int)
- {
- DataVisitor = new MultData((int)data);
- DataType = 1;
- }
- else if (data is float)
- {
- DataVisitor = new MultData((float)data);
- DataType = 2;
- }
- else if (data is string)
- {
- DataVisitor = new MultData((string)data);
- DataType = 0;
- }
- else if (data is long)
- {
- DataVisitor = new MultData((long)data);
- DataType = 3;
- }
- else if (data is uint)
- {
- DataVisitor = new MultData((uint)data);
- DataType = 5;
- }
- else if (data is ulong)
- {
- DataVisitor = new MultData((ulong)data);
- DataType = 6;
- }
- else if (data is double)
- {
- DataVisitor = new MultData((double)data);
- DataType = 4;
- }
- else if (data is IStorable)
- {
- DataVisitor = new MultData((IStorable)data);
- DataType = iStorageType;
- }
- else throw new InvalidCastException();
- }
- public bool IsString => DataType == 0;
- public bool IsInt => DataType == 1;
- public bool IsFloat => DataType == 2;
- public bool IsLong => DataType == 3;
- public bool IsDouble => DataType == 4;
- public bool IsUInt => DataType == 5;
- public bool IsULong => DataType == 6;
- public bool IsByteArray => DataType == 7;
- public bool IsIStorable => DataType >= 10;
- public T As<T>() where T : IStorable, new()
- {
- DataStorage ds = new DataStorage(Data);
- T t = new T();
- t.ReadFromData(ds);
- return t;
- }
- public void SetData<T>(T data) where T : IStorable, new()
- {
- DataStorage ds = new DataStorage();
- data.WriteToData(ds);
- Data = ds.Bytes;
- }
- public void ReadFromData(DataStorage data)
- {
- data.ReadUncheck(out DataType);
- Data = data.Read();
- }
- public void WriteToData(DataStorage data)
- {
- data.WriteUncheck(DataType);
- data.Write(Data);
- }
- }
- public class Item : IStorable
- {
- public Item() : this(0, 0) { }
- public Item(int typeId, int instanceId, int stackCount = 1, StorableFixedArray<ItemData> data = null)
- {
- Data = data ?? new StorableFixedArray<ItemData>();
- TypeId = typeId;
- InstanceId = instanceId;
- StackCount = stackCount;
- }
- public int TypeId;
- public int InstanceId;
- public int StackCount;
- public StorableFixedArray<ItemData> Data;
- public ItemData this[int index] => Data[index];
- public int Length => Data.Length;
- public void ConsumeOne(ConnectionPlayerBase who, PlayerPackage package)
- {
- StackCount--;
- ServerSetModify(who, package);
- }
- public void ServerSetModify(ConnectionPlayerBase who, PlayerPackage package)
- {
- if (Device == RunDevice.Client) throw new Exception("SetModify only can use in Server mode.");
- if (StackCount <= 0)
- {
- lock (package.objects) package.objects.Remove(InstanceId);
- Controller.OnServerSendDeleteCommand(who, InstanceId);
- }
- else Controller.OnServerSendModifyCommand(who, this);
- }
- internal void ClientApplyChange(Item item)
- {
- TypeId = item.TypeId;
- StackCount = item.StackCount;
- Data = item.Data;
- }
- public bool CanUse => GetItemBehavior(TypeId).CanUse(this);
- public void ServerUse(ConnectionPlayerBase who)
- {
- if (!CanUse) return;
- GetItemBehavior(TypeId).ServerUse(who, this);
- }
- public void ClientUse()
- {
- if (!CanUse) return;
- GetItemBehavior(TypeId).ClientUse(this);
- Controller.OnClientSendUseCommand(InstanceId);
- }
- public void WriteToData(DataStorage data)
- {
- data.WriteUncheck(TypeId);
- data.WriteUncheck(InstanceId);
- data.WriteUncheck(StackCount);
- data.Write(Data);
- }
- public void ReadFromData(DataStorage data)
- {
- data.ReadUncheck(out TypeId);
- data.ReadUncheck(out InstanceId);
- data.ReadUncheck(out StackCount);
- data.Read(out Data);
- }
- }
- public void OnServerReceiveUseCommand(ConnectionPlayerBase who, int instanceId)
- {
- Item item = FindItem(instanceId);
- if (item == null)
- Logger.Log(LogLevel.Error, $"Player {who.Nick} trying to use a nonexists item. InstanceId {instanceId}");
- else item.ServerUse(who);
- }
- public abstract class AbstractItemBehavior
- {
- public abstract int Id { get; }
- public abstract void ServerUse(ConnectionPlayerBase who, Item item);
- public abstract void ClientUse(Item item);
- public abstract bool CanUse(Item item);
- public abstract int CanStack(Item root, Item append);
- }
- public abstract class AbstractUnstackableItemBehavior : AbstractItemBehavior
- {
- public override int CanStack(Item root, Item append) => 0;
- }
- public abstract class AbstractStackableItemBehavior : AbstractItemBehavior
- {
- public abstract int MaxStackSize { get; }
- protected virtual bool CompareStackCondition(Item root, Item append)
- {
- return root.Data.GetBytes().ByteEquals(append.Data.GetBytes());
- }
- public override int CanStack(Item root, Item append)
- {
- if (!CompareStackCondition(root, append)) return 0;
- return MaxStackSize - root.StackCount;
- }
- }
- public abstract class AbstractController
- {
- public abstract void OnServerSendModifyCommand(ConnectionPlayerBase who, Item changedItem);
- public abstract void OnServerSendDeleteCommand(ConnectionPlayerBase who, int itemInstanceId);
- public abstract void OnServerSendChangeMaxItemSizeCommand(ConnectionPlayerBase who, int newMaxItemSizeValue);
- public abstract void OnClientSendUseCommand(int itemInstanceId);
- }
- public PlayerPackage() : this(30) { }
- public PlayerPackage(int maxItemSize)
- {
- if (ItemBehaviors == null)
- throw new Exception("ItemBehavior is undefined. Use DefineItemBehaviors(...) to set behaviors first.");
- objects = new StorableDictionary<SInt, Item>();
- idAllocator = 0;
- this.maxItemSize = maxItemSize;
- }
- public StorableDictionary<SInt, Item> objects;
- int maxItemSize;
- int idAllocator;
- public void SetMaxItemSize(int newValue, ConnectionPlayerBase who = null)
- {
- if (Device == RunDevice.Server)
- {
- if (maxItemSize == newValue) return;
- if (maxItemSize > newValue) throw new ArgumentException("MaxItemSize: new value must be upper than current value.");
- maxItemSize = newValue;
- Controller.OnServerSendChangeMaxItemSizeCommand(who, newValue);
- }
- else
- {
- maxItemSize = newValue;
- }
- }
- public int MaxItemSize => maxItemSize;
- public int ItemSpace
- {
- get
- {
- lock (objects)
- return objects.Count;
- }
- }
- public int EmptySpace => maxItemSize - ItemSpace;
- public int CreateItem(ConnectionPlayerBase who, int typeId, int stackCount = 1, StorableFixedArray<ItemData> data = null)
- {
- if (Device == RunDevice.Client) throw new Exception("CreateItem only can use in Server mode.");
- Item thisObject = new Item(typeId, -1, stackCount, data == null ? null : data.MemoryCopy());
- lock (objects)
- {
- foreach (var t in objects)
- {
- if (t.Value.TypeId == thisObject.TypeId)
- {
- int canStackCount = GetItemBehavior(t.Value.TypeId).CanStack(t.Value, thisObject);
- canStackCount = canStackCount <= thisObject.StackCount ? canStackCount : thisObject.StackCount;
- if (canStackCount > 0)
- {
- t.Value.StackCount += canStackCount;
- if (who != null)
- t.Value.ServerSetModify(who, this);
- thisObject.StackCount -= canStackCount;
- }
- if (thisObject.StackCount == 0)
- {
- thisObject = null;
- break;
- }
- }
- }
- if (thisObject != null)
- {
- int singleStk = GetItemBehavior(typeId).CanStack(new Item(typeId, 0, 0, data), new Item(typeId, 0, thisObject.StackCount, data));
- do
- {
- if (objects.Count >= maxItemSize)
- return thisObject.StackCount;
- int curStack = thisObject.StackCount > singleStk ? singleStk : thisObject.StackCount;
- Item curItem = new Item(typeId, idAllocator++, curStack, data);
- objects.Add(curItem.InstanceId, curItem);
- if (who != null)
- curItem.ServerSetModify(who, this);
- thisObject.StackCount -= curStack;
- }
- while (thisObject.StackCount > 0);
- }
- }
- return 0;
- }
- public Item FindItem(int instanceId)
- {
- if (objects.TryGetValue(instanceId, out Item item))
- return item;
- return null;
- }
- public int CreateItem(ConnectionPlayerBase who, Item copyItem)
- {
- return CreateItem(who, copyItem.TypeId, copyItem.StackCount, copyItem.Data);
- }
- public void DeleteItemWithInstance(ConnectionPlayerBase who, int itemInstanceId, int stackCount = 1)
- {
- if (Device == RunDevice.Client) throw new Exception("DeleteItem only can use in Server mode.");
- lock (objects)
- {
- if (objects.TryGetValue(itemInstanceId, out Item it))
- {
- if (it.StackCount > stackCount)
- {
- it.StackCount -= stackCount;
- it.ServerSetModify(who, this);
- }
- else if (it.StackCount == stackCount)
- {
- objects.Remove(itemInstanceId);
- Controller.OnServerSendDeleteCommand(who, itemInstanceId);
- }
- else throw new Exception($"Trying delete InstanceId = {itemInstanceId} with StackCount = {stackCount}, no such stack count.");
- }
- else throw new Exception($"The InstanceId = {itemInstanceId} is not founded.");
- }
- }
- public void DeleteItemWithType(ConnectionPlayerBase who, int typeId, int stackCount = 1, Func<Item, bool> checker = null)
- {
- if (Device == RunDevice.Client) throw new Exception("DeleteItem only can use in Server mode.");
- lock (objects)
- {
- List<int> preDeletes = new List<int>();
- foreach (var obj in objects)
- {
- if (obj.Value.TypeId == typeId)
- {
- if (checker != null)
- {
- if (!checker(obj.Value))
- continue;
- }
- if (obj.Value.StackCount > stackCount)
- {
- obj.Value.StackCount -= stackCount;
- stackCount = 0;
- if (who != null)
- obj.Value.ServerSetModify(who, this);
- break;
- }
- else
- {
- stackCount -= obj.Value.StackCount;
- preDeletes.Add(obj.Key);
- if (who != null)
- Controller.OnServerSendDeleteCommand(who, obj.Key);
- if (stackCount == 0) break;
- }
- }
- }
- if (stackCount != 0)
- throw new Exception("DeleteItem: FATAL ERROR: Count is not enough, but still maybe reduce some stack count, that already caught item lost.");
- for (int i = 0; i < preDeletes.Count; i++)
- objects.Remove(preDeletes[i]);
- }
- }
- public bool CheckItems(int typeId, int stackCount = 1, Func<Item, bool> checker = null)
- {
- lock (objects)
- {
- foreach (var obj in objects)
- {
- if (obj.Value.TypeId == typeId)
- {
- if (checker != null)
- {
- if (!checker(obj.Value))
- continue;
- }
- if (obj.Value.StackCount > stackCount)
- {
- stackCount = 0;
- break;
- }
- else
- {
- stackCount -= obj.Value.StackCount;
- if (stackCount == 0) break;
- }
- }
- }
- return stackCount == 0;
- }
- }
- public class ItemRequest
- {
- public struct ItemQueryInfo
- {
- public ItemQueryInfo(int typeId, int stackCount = 1, Func<Item, bool> checker = null)
- {
- TypeId = typeId;
- StackCount = stackCount;
- Checker = checker;
- }
- public int TypeId { get; private set; }
- public int StackCount { get; private set; }
- public Func<Item, bool> Checker { get; private set; }
- }
- ItemQueryInfo[] Requests;
- public ItemRequest(params ItemQueryInfo[] infos) => Requests = infos;
- public bool CheckIsEnough(PlayerPackage package)
- {
- bool isEnough = true;
- lock (package.objects)
- {
- for (int i = 0; i < Requests.Length; i++)
- isEnough &= package.CheckItems(Requests[i].TypeId, Requests[i].StackCount, Requests[i].Checker);
- }
- return isEnough;
- }
- public bool Consume(ConnectionPlayerBase who, PlayerPackage package)
- {
- lock (package.objects)
- {
- if (CheckIsEnough(package))
- {
- for (int i = 0; i < Requests.Length; i++)
- package.DeleteItemWithType(who, Requests[i].TypeId, Requests[i].StackCount, Requests[i].Checker);
- return true;
- }
- return false;
- }
- }
- }
- public void OnClientReceivedModify(Item changedItem, out Item added)
- {
- lock (objects)
- {
- if (objects.TryGetValue(changedItem.InstanceId, out Item item))
- {
- item.ClientApplyChange(changedItem);
- added = null;
- }
- else
- {
- objects.Add(changedItem.InstanceId, changedItem);
- added = changedItem;
- }
- }
- }
- public void OnClientReceivedDelete(int itemInstanceId)
- {
- lock (objects)
- {
- if (objects.ContainsKey(itemInstanceId))
- objects.Remove(itemInstanceId);
- }
- }
- public void ReadFromData(DataStorage data)
- {
- data.Read(out idAllocator);
- data.Read(out objects);
- data.ReadUncheck(out maxItemSize);
- }
- public void WriteToData(DataStorage data)
- {
- data.Write(idAllocator);
- data.Write(objects);
- data.WriteUncheck(maxItemSize);
- }
- }
- }
|