Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix several instances of players not receiving rewards #233

Merged
merged 10 commits into from
Apr 6, 2021
2 changes: 1 addition & 1 deletion RakDotNet
1 change: 1 addition & 0 deletions Uchu.Master/Api/MasterCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ public async Task<object> CommissionInstance(string zoneId)
{
throw new TimeoutException("Commission timed out");
}

await Task.Delay(delay);
continue;
}
Expand Down
28 changes: 28 additions & 0 deletions Uchu.World/Enums/LootType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Uchu.World
{
public enum LootType
{
None,
Chest,
Mission,
Mail,
Currency,
Achievement,
Trade,
Quickbuild,
Deletion,
Vendor,
Activity,
Pickup,
Brick,
Property,
Moderation,
Exhibit,
Inventory,
Claimcode,
Consumption,
Crafting,
LevelReward,
Relocate,
}
}
2 changes: 1 addition & 1 deletion Uchu.World/Handlers/GameMessages/LootHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public async Task PickupItemHandler(PickupItemMessage message, Player player)
Logger.Debug($"{player} picking up {loot}");

await player.OnLootPickup.InvokeAsync(loot);
await player.GetComponent<InventoryManagerComponent>().AddLotAsync(loot, 1);
await player.GetComponent<InventoryManagerComponent>().AddLotAsync(loot, 1, lootType: LootType.Pickup);
}

[PacketHandler]
Expand Down
21 changes: 20 additions & 1 deletion Uchu.World/Handlers/MailHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public async Task ClientMailPacketHandler(ClientMailPacket packet, IRakConnectio
await ReadHandler(packet.MailStruct as MailRead, player);
break;
case ClientMailPacketId.NotificationRequest:
await NotificationRequestHandler(player);
break;
default:
throw new ArgumentOutOfRangeException();
Expand Down Expand Up @@ -161,7 +162,7 @@ public static async Task AttachmentCollectHandler(MailAttachmentCollected packet
goto sendResponse;
}

await player.GetComponent<InventoryManagerComponent>().AddLotAsync(mail.AttachmentLot, mail.AttachmentCount);
await player.GetComponent<InventoryManagerComponent>().AddLotAsync(mail.AttachmentLot, mail.AttachmentCount, lootType: LootType.Mail);

mail.AttachmentLot = -1;
mail.AttachmentCount = 0;
Expand Down Expand Up @@ -238,5 +239,23 @@ public static async Task ReadHandler(MailRead packet, Player player)
}
});
}

public static async Task NotificationRequestHandler(Player player)
{
await using var ctx = new UchuContext();

var response = new Notification();

var author = player.GetComponent<CharacterComponent>();
var unreadCount = ctx.Mails.Count(m => m.RecipientId == author.CharacterId && m.Read == false);

response.MailCountDelta = (uint) unreadCount;

player.Message(new ServerMailPacket
{
Id = ServerMailPacketId.Notification,
MailStruct = response
});
}
}
}
91 changes: 81 additions & 10 deletions Uchu.World/Objects/Components/Player/InventoryManagerComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using InfectedRose.Lvl;
Expand Down Expand Up @@ -318,9 +319,10 @@ private static async Task<ItemComponent> GetItemComponentForLotAysnc(Lot lot)
/// <param name="inventoryType">Optional explicit inventory type to add this lot to, if not provided this will be
/// implicitly retrieved from the item lot, generally used for vendor buy back</param>
/// <param name="rootItem">An optional parent item</param>
/// <param name="lootType">The reason this lot is given to the player</param>
/// <returns>The list of items that were created while adding the lot</returns>
public async Task AddLotAsync(Lot lot, uint count, LegoDataDictionary settings = default,
InventoryType inventoryType = default, Item rootItem = default)
InventoryType inventoryType = default, Item rootItem = default, LootType lootType = default)
{
var createdItems = new List<Item>();

Expand Down Expand Up @@ -378,17 +380,86 @@ public async Task AddLotAsync(Lot lot, uint count, LegoDataDictionary settings =
{
var toAdd = (uint) Min(stackSize, (int) totalToAdd);
var item = await Item.Instantiate(GameObject, lot, inventory, toAdd, extraInfo: settings, rootItem: rootItem);

// Might occur if the inventory is full or an error occured during slot claiming
if (item == null)
return; // TODO: Message item to player with mail

Start(item);
item.MessageCreation();
createdItems.Add(item);

totalToAdd -= toAdd;
totalAdded += toAdd;

// Item was successfully instantiated
if (item != null)
{
Start(item);
item.MessageCreation();
createdItems.Add(item);
}
// Might occur if the inventory is full or an error occured during slot claiming
else
{
// Display item-bouncing-off-backpack animation
((Player) GameObject).Message(new NotifyRewardMailed
{
ObjectId = ObjectId.Standalone,
StartPoint = new Vector3(0, 0, 0),
TemplateId = lot,
Associate = GameObject,
});

await using var uchuContext = new UchuContext();

var playerCharacter = uchuContext.Characters
.First(c => c.Id == GameObject.Id);

var mail = new CharacterMail
{
RecipientId = playerCharacter.Id,
AuthorId = 0,
AttachmentLot = lot,
AttachmentCount = (ushort) toAdd,
SentTime = DateTime.Now,
ExpirationTime = DateTime.Now
};

switch (lootType)
{
// Achievements
case LootType.Achievement:
{
mail.Subject = "%[MAIL_ACHIEVEMENT_OVERFLOW_HEADER]";
mail.Body = "%[MAIL_ACHIEVEMENT_OVERFLOW_BODY]";
break;
}
// Activities - not entirely sure when this will be used
// The text also works well enough for missions, though those should not add items to full inventories anyway
case LootType.Mission:
case LootType.Activity:
{
mail.Subject = "%[MAIL_ACTIVITY_OVERFLOW_HEADER]";
mail.Body = "%[MAIL_ACTIVITY_OVERFLOW_BODY]";
break;
}
// Sometimes happens when picking up items.
case LootType.Pickup:
{
// To avoid spamming people's mailboxes, don't send the item if the player has 20+ mails.
var mailCount = uchuContext.Mails.Count(m => m.RecipientId == playerCharacter.Id);
if (mailCount >= 20)
continue;

mail.Subject = "Lost Item";
mail.Body = "You picked up this item, but didn't have room for it in your backpack.";
break;
}
// /gmadditem, item sets, and for any other reason listed in the LootType enum
default:
{
mail.Subject = "Lost Item";
mail.Body = "You received this item, but didn't have room for it in your backpack.";
break;
}
}

await uchuContext.Mails.AddAsync(mail);
await uchuContext.SaveChangesAsync();
}
}
}
finally
Expand Down Expand Up @@ -529,7 +600,7 @@ public async Task MoveItemBetweenInventoriesAsync(Item item, uint count, Invento
return;

await RemoveItemAsync(item, count, source, silent);
await AddLotAsync(item.Lot , count, item.Settings, destination);
await AddLotAsync(item.Lot , count, item.Settings, destination, lootType: LootType.Inventory); // TODO find out if this is correct (what's LootType.Relocate?)
}

#endregion methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public async Task Buy(Lot lot, uint count, Player player)

character.Currency -= cost;

await player.GetComponent<InventoryManagerComponent>().AddLotAsync(lot, count);
await player.GetComponent<InventoryManagerComponent>().AddLotAsync(lot, count, lootType: LootType.Vendor);

player.Message(new VendorTransactionResultMessage
{
Expand Down Expand Up @@ -165,7 +165,7 @@ await inventory.MoveItemBetweenInventoriesAsync(
{
var alternativeCurrencyLot = (Lot) item.ItemComponent.CurrencyLOT;
var returnAlternativeCurrency = (uint) Math.Floor((item.ItemComponent.AltCurrencyCost ?? 0) * sellMultiplier) * count;
await inventory.AddLotAsync(alternativeCurrencyLot, returnAlternativeCurrency);
await inventory.AddLotAsync(alternativeCurrencyLot, returnAlternativeCurrency, lootType: LootType.Vendor);
}

player.Message(new VendorTransactionResultMessage
Expand Down Expand Up @@ -207,7 +207,7 @@ public async Task Buyback(Item item, uint count, Player player)
character.Currency -= cost;

await inventory.RemoveItemAsync(item, count, InventoryType.VendorBuyback);
await inventory.AddLotAsync(item.Lot, count);
await inventory.AddLotAsync(item.Lot, count, lootType: LootType.Vendor);

player.Message(new VendorTransactionResultMessage
{
Expand Down
15 changes: 12 additions & 3 deletions Uchu.World/Objects/GameObjects/Item.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ public static async Task<Item> Instantiate(InventoryItem itemInstance,
/// <param name="rootItem">The root item this item is based on</param>
/// <param name="isEquipped">Whether the game object has this item equipped or not</param>
/// <param name="isBound">Whether the game object has bound this item or not</param>
/// <param name="lootType">Where this item came from</param>
/// <remarks>Note that <c>Start</c> still needs to be called on the item to be registered properly in the world.</remarks>
/// <returns>The instantiated item or <c>null</c> if no slot could be acquired or if the item couldn't be added to the inventory</returns>
public static async Task<Item> Instantiate(GameObject owner, Lot lot,
Inventory inventory, uint count, uint slot = default, LegoDataDictionary extraInfo = default,
ObjectId objectId = default, Item rootItem = default, bool isEquipped = false, bool isBound = false)
ObjectId objectId = default, Item rootItem = default, bool isEquipped = false, bool isBound = false, LootType lootType = LootType.None)
{
// Try to find the slot at which this item should be inserted if no explicit slot is provided
if (inventory != default && slot == default)
Expand Down Expand Up @@ -99,6 +100,7 @@ public static async Task<Item> Instantiate(GameObject owner, Lot lot,
instance.IsEquipped = isEquipped;
instance.IsPackage = instance.Lot.GetComponentId(ComponentId.PackageComponent) != default;
instance.Inventory = inventory;
instance.LootType = lootType;

var skills = (await ClientCache.GetTableAsync<ObjectSkills>()).Where(
s => s.ObjectTemplate == instance.Lot
Expand All @@ -121,6 +123,11 @@ public static async Task<Item> Instantiate(GameObject owner, Lot lot,
return instance;
}

/// <summary>
/// The source of this item
/// </summary>
public LootType LootType { get; set; }

/// <summary>
/// The CdClient item component that contains extra meta information about this item
/// </summary>
Expand Down Expand Up @@ -347,7 +354,8 @@ public void MessageCreation()
InventoryType = (int) Inventory.InventoryType,
ShowFlyingLoot = true,
TotalItems = Count,
ExtraInfo = Settings
ExtraInfo = Settings,
Source = LootType,
});
}
}
Expand All @@ -370,7 +378,8 @@ private void MessageAddItem(uint count)
InventoryType = (int) Inventory.InventoryType,
ExtraInfo = Settings,
Slot = (int) Slot,
ShowFlyingLoot = count != default
ShowFlyingLoot = count != default,
Source = LootType,
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class AddItemToInventoryMessage : ServerGameMessage

public bool IsBoundOnPickup { get; set; }

public int Source { get; set; } = -1;
public LootType Source { get; set; } = LootType.None;

public LegoDataDictionary ExtraInfo { get; set; } = null;

Expand Down
30 changes: 30 additions & 0 deletions Uchu.World/Packets/GameMessages/Server/NotifyRewardMailed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Numerics;
using RakDotNet.IO;
using Uchu.Core;

namespace Uchu.World
{
public class NotifyRewardMailed : ServerGameMessage
{
public override GameMessageId GameMessageId => GameMessageId.NotifyRewardMailed;

public ObjectId ObjectId;

public Vector3 StartPoint;

public ObjectId Subkey = (ObjectId) (-1);

public Lot TemplateId;

public override void SerializeMessage(BitWriter writer)
{
writer.Write(ObjectId);

writer.Write(StartPoint);

writer.Write(Subkey);

writer.Write(TemplateId);
}
}
}
18 changes: 15 additions & 3 deletions Uchu.World/Packets/Server/ServerMailPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,25 @@ public void Serialize(BitWriter writer)

foreach (var mail in Mails)
{
var author = ctx.Characters.First(c => c.Id == mail.AuthorId);

writer.Write(mail.Id);

writer.WriteString(mail.Subject, 50, true);
writer.WriteString(mail.Body, 400, true);
writer.WriteString(author.Name, 32, true);

string authorName;

if (mail.AuthorId == 0)
{
authorName = "LEGO Universe";
}
else
{
var author = ctx.Characters.FirstOrDefault(c => c.Id == mail.AuthorId);
authorName = author?.Name ?? "Deleted Character";
}

writer.WriteString(authorName ?? "LEGO Universe", 32, true);

writer.Write<uint>(0);

Expand All @@ -139,7 +151,7 @@ public void Serialize(BitWriter writer)
}

writer.Write((ulong) ((DateTimeOffset) mail.ExpirationTime).ToUnixTimeSeconds());

writer.Write((ulong) ((DateTimeOffset) mail.SentTime).ToUnixTimeSeconds());

writer.Write((byte) (mail.Read ? 1 : 0));
Expand Down
Loading