baseline: OpenMU upstream b5a0961 (fresh source)
This commit is contained in:
433
src/GameServer/RemoteView/AppearanceSerializer.cs
Normal file
433
src/GameServer/RemoteView/AppearanceSerializer.cs
Normal file
@@ -0,0 +1,433 @@
|
||||
// <copyright file="AppearanceSerializer.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Default serializer for the appearance of a player.
|
||||
/// </summary>
|
||||
[Guid("54847CAF-7827-48FB-BF53-AF458A694FAF")]
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.AppearanceSerializer_Name), Description = nameof(PlugInResources.AppearanceSerializer_Description), ResourceType = typeof(PlugInResources))]
|
||||
[MinimumClient(5, 0, ClientLanguage.Invariant)]
|
||||
public class AppearanceSerializer : IAppearanceSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache which holds the results of the serializer.
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<IAppearanceData, byte[]> Cache = new();
|
||||
|
||||
private enum PetIndex
|
||||
{
|
||||
Angel = 0,
|
||||
Imp = 1,
|
||||
Unicorn = 2,
|
||||
Dinorant = 3,
|
||||
DarkHorse = 4,
|
||||
DarkRaven = 5,
|
||||
Fenrir = 37,
|
||||
Demon = 64,
|
||||
SpiritOfGuardian = 65,
|
||||
Rudolph = 66,
|
||||
Panda = 80,
|
||||
PetUnicorn = 106,
|
||||
Skeleton = 123,
|
||||
}
|
||||
|
||||
private enum WingIndex
|
||||
{
|
||||
WingsOfElf = 0,
|
||||
WingsOfHeaven = 1,
|
||||
WingsOfSatan = 2,
|
||||
WingsOfMistery = 41,
|
||||
WingsOfSpirit = 3,
|
||||
WingsOfSoul = 4,
|
||||
WingsOfDragon = 5,
|
||||
WingsOfDarkness = 6,
|
||||
CapeOfLord = 30, // other group, but index not overlapping with other wings
|
||||
WingsOfDespair = 42,
|
||||
CapeOfFighter = 49,
|
||||
WingOfStorm = 36,
|
||||
WingOfEternal = 37,
|
||||
WingOfIllusion = 38,
|
||||
WingOfRuin = 39,
|
||||
CapeOfEmperor = 40,
|
||||
WingOfDimension = 43,
|
||||
CapeOfOverrule = 50,
|
||||
SmallCapeOfLord = 130,
|
||||
SmallWingsOfMistery = 131,
|
||||
SmallWingsOfElf = 132,
|
||||
SmallWingsOfHeaven = 133,
|
||||
SmallWingsOfSatan = 134,
|
||||
SmallCloakOfWarrior = 135,
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int NeededSpace => 18;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InvalidateCache(IAppearanceData appearance)
|
||||
{
|
||||
Cache.TryRemove(appearance, out _);
|
||||
appearance.AppearanceChanged -= this.OnAppearanceOfAppearanceChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void WriteAppearanceData(Span<byte> target, IAppearanceData appearance, bool useCache)
|
||||
{
|
||||
if (target.Length < this.NeededSpace)
|
||||
{
|
||||
throw new ArgumentException($"Target span too small. Actual size: {target.Length}; Required: {this.NeededSpace}.", nameof(target));
|
||||
}
|
||||
|
||||
if (useCache && Cache.TryGetValue(appearance, out var cached))
|
||||
{
|
||||
cached.CopyTo(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.WritePreviewCharSet(target, appearance);
|
||||
if (useCache)
|
||||
{
|
||||
var cacheEntry = target.Slice(0, this.NeededSpace).ToArray();
|
||||
if (Cache.TryAdd(appearance, cacheEntry))
|
||||
{
|
||||
appearance.AppearanceChanged += this.OnAppearanceOfAppearanceChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAppearanceOfAppearanceChanged(object? sender, EventArgs args) => this.InvalidateCache(sender as IAppearanceData ?? throw new ArgumentException($"sender must be of type {nameof(IAppearanceData)}"));
|
||||
|
||||
private void WritePreviewCharSet(Span<byte> target, IAppearanceData appearanceData)
|
||||
{
|
||||
ItemAppearance?[] itemArray = new ItemAppearance[InventoryConstants.EquippableSlotsCount];
|
||||
for (byte i = 0; i < itemArray.Length; i++)
|
||||
{
|
||||
itemArray[i] = appearanceData.EquippedItems.FirstOrDefault(item => item.ItemSlot == i);
|
||||
}
|
||||
|
||||
if (appearanceData.CharacterClass is not null)
|
||||
{
|
||||
target[0] = (byte)(appearanceData.CharacterClass.Number << 3 & 0xF8);
|
||||
}
|
||||
|
||||
target[0] |= (byte)appearanceData.Pose;
|
||||
this.SetHand(target, itemArray[InventoryConstants.LeftHandSlot], 1, 12);
|
||||
|
||||
this.SetHand(target, itemArray[InventoryConstants.RightHandSlot], 2, 13);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.HelmSlot], 3, true, 0x80, 13, false);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.ArmorSlot], 3, false, 0x40, 14, true);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.PantsSlot], 4, true, 0x20, 14, false);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.GlovesSlot], 4, false, 0x10, 15, true);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.BootsSlot], 5, true, 0x08, 15, false);
|
||||
|
||||
this.SetItemLevels(target, itemArray);
|
||||
|
||||
if (appearanceData.FullAncientSetEquipped)
|
||||
{
|
||||
target[11] |= 0x01;
|
||||
}
|
||||
|
||||
this.AddWing(target, itemArray[InventoryConstants.WingsSlot]);
|
||||
|
||||
this.AddPet(target, itemArray[InventoryConstants.PetSlot]);
|
||||
}
|
||||
|
||||
private void SetHand(Span<byte> preview, ItemAppearance? item, int indexIndex, int groupIndex)
|
||||
{
|
||||
if (item?.Definition is null)
|
||||
{
|
||||
preview[indexIndex] = 0xFF;
|
||||
preview[groupIndex] |= 0xF0;
|
||||
}
|
||||
else
|
||||
{
|
||||
preview[indexIndex] = (byte)item.Definition.Number;
|
||||
preview[groupIndex] |= (byte)(item.Definition.Group << 5);
|
||||
}
|
||||
}
|
||||
|
||||
private byte GetOrMaskForHighNibble(int value)
|
||||
{
|
||||
return (byte)((value << 4) & 0xF0);
|
||||
}
|
||||
|
||||
private byte GetOrMaskForLowNibble(int value)
|
||||
{
|
||||
return (byte)(value & 0x0F);
|
||||
}
|
||||
|
||||
private void SetEmptyArmor(Span<byte> preview, int firstIndex, bool firstIndexHigh, byte secondIndexMask, int thirdIndex, bool thirdIndexHigh)
|
||||
{
|
||||
// if the item is not equipped every index bit is set to 1
|
||||
preview[firstIndex] |= firstIndexHigh ? this.GetOrMaskForHighNibble(0x0F) : this.GetOrMaskForLowNibble(0x0F);
|
||||
preview[9] |= secondIndexMask;
|
||||
preview[thirdIndex] |= thirdIndexHigh ? this.GetOrMaskForHighNibble(0x0F) : this.GetOrMaskForLowNibble(0x0F);
|
||||
}
|
||||
|
||||
private void SetArmorItemIndex(Span<byte> preview, ItemAppearance item, int firstIndex, bool firstIndexHigh, byte secondIndexMask, int thirdIndex, bool thirdIndexHigh)
|
||||
{
|
||||
preview[firstIndex] |= firstIndexHigh ? this.GetOrMaskForHighNibble(item.Definition!.Number) : this.GetOrMaskForLowNibble(item.Definition!.Number);
|
||||
byte multi = (byte)(item.Definition.Number / 16);
|
||||
if (multi > 0)
|
||||
{
|
||||
byte bit1 = (byte)(multi % 2);
|
||||
byte byte2 = (byte)(multi / 2);
|
||||
if (bit1 == 1)
|
||||
{
|
||||
preview[9] |= secondIndexMask;
|
||||
}
|
||||
|
||||
if (byte2 > 0)
|
||||
{
|
||||
preview[thirdIndex] |= thirdIndexHigh ? this.GetOrMaskForHighNibble(byte2) : this.GetOrMaskForLowNibble(byte2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetArmorPiece(Span<byte> preview, ItemAppearance? item, int firstIndex, bool firstIndexHigh, byte secondIndexMask, int thirdIndex, bool thirdIndexHigh)
|
||||
{
|
||||
if (item?.Definition is null)
|
||||
{
|
||||
this.SetEmptyArmor(preview, firstIndex, firstIndexHigh, secondIndexMask, thirdIndex, thirdIndexHigh);
|
||||
}
|
||||
else
|
||||
{
|
||||
// item id
|
||||
this.SetArmorItemIndex(preview, item, firstIndex, firstIndexHigh, secondIndexMask, thirdIndex, thirdIndexHigh);
|
||||
|
||||
// exc bit
|
||||
if (this.IsExcellent(item))
|
||||
{
|
||||
preview[10] |= secondIndexMask;
|
||||
}
|
||||
|
||||
// ancient bit
|
||||
if (this.IsAncient(item))
|
||||
{
|
||||
preview[11] |= secondIndexMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetItemLevels(Span<byte> preview, ItemAppearance?[] itemArray)
|
||||
{
|
||||
int levelIndex = 0;
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (itemArray[i] is not null)
|
||||
{
|
||||
levelIndex |= itemArray[i]!.GetGlowLevel() << (i * 3);
|
||||
}
|
||||
}
|
||||
|
||||
preview[6] = (byte)((levelIndex >> 16) & 255);
|
||||
preview[7] = (byte)((levelIndex >> 8) & 255);
|
||||
preview[8] = (byte)(levelIndex & 255);
|
||||
}
|
||||
|
||||
private void AddWing(Span<byte> preview, ItemAppearance? wing)
|
||||
{
|
||||
if (wing?.Definition is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch ((WingIndex)wing.Definition.Number)
|
||||
{
|
||||
case WingIndex.WingsOfElf:
|
||||
case WingIndex.WingsOfHeaven:
|
||||
case WingIndex.WingsOfSatan:
|
||||
case WingIndex.WingsOfMistery:
|
||||
preview[5] |= 0x04;
|
||||
break;
|
||||
case WingIndex.WingsOfSpirit:
|
||||
case WingIndex.WingsOfSoul:
|
||||
case WingIndex.WingsOfDragon:
|
||||
case WingIndex.WingsOfDarkness:
|
||||
case WingIndex.CapeOfLord:
|
||||
case WingIndex.WingsOfDespair:
|
||||
case WingIndex.CapeOfFighter:
|
||||
preview[5] |= 0x08;
|
||||
break;
|
||||
case WingIndex.WingOfStorm:
|
||||
case WingIndex.WingOfEternal:
|
||||
case WingIndex.WingOfIllusion:
|
||||
case WingIndex.WingOfRuin:
|
||||
case WingIndex.CapeOfEmperor:
|
||||
case WingIndex.WingOfDimension:
|
||||
case WingIndex.CapeOfOverrule:
|
||||
case WingIndex.SmallCapeOfLord:
|
||||
case WingIndex.SmallWingsOfMistery:
|
||||
case WingIndex.SmallWingsOfElf:
|
||||
case WingIndex.SmallWingsOfHeaven:
|
||||
case WingIndex.SmallWingsOfSatan:
|
||||
case WingIndex.SmallCloakOfWarrior:
|
||||
preview[5] |= 0x0C;
|
||||
break;
|
||||
default:
|
||||
// nothing to do
|
||||
break;
|
||||
}
|
||||
|
||||
switch ((WingIndex)wing.Definition.Number)
|
||||
{
|
||||
case WingIndex.WingsOfElf:
|
||||
case WingIndex.WingsOfSpirit:
|
||||
case WingIndex.WingOfStorm:
|
||||
preview[9] |= 0x01;
|
||||
break;
|
||||
case WingIndex.WingsOfHeaven:
|
||||
case WingIndex.WingsOfSoul:
|
||||
case WingIndex.WingOfEternal:
|
||||
preview[9] |= 0x02;
|
||||
break;
|
||||
case WingIndex.WingsOfSatan:
|
||||
case WingIndex.WingsOfDragon:
|
||||
case WingIndex.WingOfIllusion:
|
||||
preview[9] |= 0x03;
|
||||
break;
|
||||
case WingIndex.WingsOfMistery:
|
||||
case WingIndex.WingsOfDarkness:
|
||||
case WingIndex.WingOfRuin:
|
||||
preview[9] |= 0x04;
|
||||
break;
|
||||
case WingIndex.CapeOfLord:
|
||||
case WingIndex.CapeOfEmperor:
|
||||
preview[9] |= 0x05;
|
||||
break;
|
||||
case WingIndex.WingsOfDespair:
|
||||
case WingIndex.WingOfDimension:
|
||||
preview[9] |= 0x06;
|
||||
break;
|
||||
case WingIndex.CapeOfFighter:
|
||||
case WingIndex.CapeOfOverrule:
|
||||
preview[9] |= 0x07;
|
||||
break;
|
||||
case WingIndex.SmallCapeOfLord:
|
||||
preview[17] |= 0x20;
|
||||
break;
|
||||
case WingIndex.SmallWingsOfMistery:
|
||||
preview[17] |= 0x40;
|
||||
break;
|
||||
case WingIndex.SmallWingsOfElf:
|
||||
preview[17] |= 0x60;
|
||||
break;
|
||||
case WingIndex.SmallWingsOfHeaven:
|
||||
preview[17] |= 0x80;
|
||||
break;
|
||||
case WingIndex.SmallWingsOfSatan:
|
||||
preview[17] |= 0xA0;
|
||||
break;
|
||||
case WingIndex.SmallCloakOfWarrior:
|
||||
preview[17] |= 0xC0;
|
||||
break;
|
||||
default:
|
||||
// nothing to do
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPet(Span<byte> preview, ItemAppearance? pet)
|
||||
{
|
||||
if (pet?.Definition is null)
|
||||
{
|
||||
preview[5] |= 0b0000_0011;
|
||||
return;
|
||||
}
|
||||
|
||||
switch ((PetIndex)pet.Definition.Number)
|
||||
{
|
||||
case PetIndex.Angel:
|
||||
case PetIndex.Imp:
|
||||
case PetIndex.Unicorn:
|
||||
preview[5] |= (byte)pet.Definition.Number;
|
||||
break;
|
||||
case PetIndex.Dinorant:
|
||||
preview[5] |= 0x03;
|
||||
preview[10] |= 0x01;
|
||||
break;
|
||||
case PetIndex.DarkHorse:
|
||||
preview[5] |= 0x03;
|
||||
preview[12] |= 0x01;
|
||||
break;
|
||||
case PetIndex.Fenrir:
|
||||
preview[5] |= 0x03;
|
||||
preview[10] &= 0xFE;
|
||||
preview[12] &= 0xFE;
|
||||
preview[12] |= 0x04;
|
||||
preview[16] = 0x00;
|
||||
|
||||
if (pet.VisibleOptions.Contains(ItemOptionTypes.BlackFenrir))
|
||||
{
|
||||
preview[16] |= 0x01;
|
||||
}
|
||||
|
||||
if (pet.VisibleOptions.Contains(ItemOptionTypes.BlueFenrir))
|
||||
{
|
||||
preview[16] |= 0x02;
|
||||
}
|
||||
|
||||
if (pet.VisibleOptions.Contains(ItemOptionTypes.GoldFenrir))
|
||||
{
|
||||
preview[17] |= 0x01;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
preview[5] |= 0x03;
|
||||
break;
|
||||
}
|
||||
|
||||
switch ((PetIndex)pet.Definition.Number)
|
||||
{
|
||||
case PetIndex.Panda:
|
||||
preview[16] |= 0xE0;
|
||||
break;
|
||||
case PetIndex.PetUnicorn:
|
||||
preview[16] |= 0xA0;
|
||||
break;
|
||||
case PetIndex.Skeleton:
|
||||
preview[16] |= 0x60;
|
||||
break;
|
||||
case PetIndex.Rudolph:
|
||||
preview[16] |= 0x80;
|
||||
break;
|
||||
case PetIndex.SpiritOfGuardian:
|
||||
preview[16] |= 0x40;
|
||||
break;
|
||||
case PetIndex.Demon:
|
||||
preview[16] |= 0x20;
|
||||
break;
|
||||
default:
|
||||
// no further flag required.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsExcellent(ItemAppearance item)
|
||||
{
|
||||
return item.VisibleOptions.Contains(ItemOptionTypes.Excellent);
|
||||
}
|
||||
|
||||
private bool IsAncient(ItemAppearance item)
|
||||
{
|
||||
return item.VisibleOptions.Contains(ItemOptionTypes.AncientOption);
|
||||
}
|
||||
}
|
||||
159
src/GameServer/RemoteView/AppearanceSerializer075.cs
Normal file
159
src/GameServer/RemoteView/AppearanceSerializer075.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
// <copyright file="AppearanceSerializer075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Serializer for the appearance of a player, compatible with the client of version 0.75.
|
||||
/// </summary>
|
||||
[Guid("D20EEBFA-12C1-4A86-B202-63121EB2A95B")]
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.AppearanceSerializer075_Name), Description = nameof(PlugInResources.AppearanceSerializer075_Description), ResourceType = typeof(PlugInResources))]
|
||||
[MinimumClient(0, 75, ClientLanguage.Invariant)]
|
||||
public class AppearanceSerializer075 : IAppearanceSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache which holds the results of the serializer.
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<IAppearanceData, byte[]> Cache = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int NeededSpace => 9;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InvalidateCache(IAppearanceData appearance)
|
||||
{
|
||||
Cache.TryRemove(appearance, out _);
|
||||
appearance.AppearanceChanged -= this.OnAppearanceOfAppearanceChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void WriteAppearanceData(Span<byte> target, IAppearanceData appearance, bool useCache)
|
||||
{
|
||||
if (target.Length < this.NeededSpace)
|
||||
{
|
||||
throw new ArgumentException($"Target span too small. Actual size: {target.Length}; Required: {this.NeededSpace}.", nameof(target));
|
||||
}
|
||||
|
||||
if (useCache && Cache.TryGetValue(appearance, out var cached))
|
||||
{
|
||||
cached.CopyTo(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.WritePreviewCharSet(target, appearance);
|
||||
if (useCache)
|
||||
{
|
||||
var cacheEntry = target.Slice(0, this.NeededSpace).ToArray();
|
||||
if (Cache.TryAdd(appearance, cacheEntry))
|
||||
{
|
||||
appearance.AppearanceChanged += this.OnAppearanceOfAppearanceChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAppearanceOfAppearanceChanged(object? sender, EventArgs args) => this.InvalidateCache(sender as IAppearanceData ?? throw new ArgumentException($"sender must be of type {nameof(IAppearanceData)}"));
|
||||
|
||||
private void WritePreviewCharSet(Span<byte> target, IAppearanceData appearanceData)
|
||||
{
|
||||
ItemAppearance?[] itemArray = new ItemAppearance[InventoryConstants.BootsSlot + 1];
|
||||
for (byte i = 0; i < itemArray.Length; i++)
|
||||
{
|
||||
itemArray[i] = appearanceData.EquippedItems.FirstOrDefault(item => item.ItemSlot == i && item.Definition?.Number < 16);
|
||||
}
|
||||
|
||||
if (appearanceData.CharacterClass is not null)
|
||||
{
|
||||
target[0] = (byte)(appearanceData.CharacterClass.Number << 3);
|
||||
}
|
||||
|
||||
target[0] |= (byte)appearanceData.Pose;
|
||||
this.SetHand(target, itemArray[InventoryConstants.LeftHandSlot], 1);
|
||||
|
||||
this.SetHand(target, itemArray[InventoryConstants.RightHandSlot], 2);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.HelmSlot], 3, true);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.ArmorSlot], 3, false);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.PantsSlot], 4, true);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.GlovesSlot], 4, false);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.BootsSlot], 5, true);
|
||||
var wing = appearanceData.EquippedItems.FirstOrDefault(item => item.ItemSlot == InventoryConstants.WingsSlot && item.Definition?.Number < 3);
|
||||
var pet = appearanceData.EquippedItems.FirstOrDefault(item => item.ItemSlot == InventoryConstants.PetSlot && item.Definition?.Number < 3);
|
||||
target[5] |= (byte)((wing?.Definition?.Number & 0x03) << 2 ?? 0b1100);
|
||||
target[5] |= (byte)(pet?.Definition?.Number & 0x03 ?? 0b0011);
|
||||
|
||||
this.SetItemLevels(target, itemArray);
|
||||
}
|
||||
|
||||
private void SetHand(Span<byte> preview, ItemAppearance? item, int index)
|
||||
{
|
||||
if (item?.Definition is null)
|
||||
{
|
||||
preview[index] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
preview[index] = (byte)item.Definition.Number;
|
||||
preview[index] |= (byte)(item.Definition.Group << 4);
|
||||
}
|
||||
}
|
||||
|
||||
private byte GetOrMaskForHighNibble(int value)
|
||||
{
|
||||
return (byte)((value << 4) & 0xF0);
|
||||
}
|
||||
|
||||
private byte GetOrMaskForLowNibble(int value)
|
||||
{
|
||||
return (byte)(value & 0x0F);
|
||||
}
|
||||
|
||||
private void SetArmorPiece(Span<byte> preview, ItemAppearance? item, int index, bool highNibble)
|
||||
{
|
||||
if (item?.Definition is null)
|
||||
{
|
||||
// if the item is not equipped every index bit is set to 1
|
||||
preview[index] |= highNibble ? this.GetOrMaskForHighNibble(0x0F) : this.GetOrMaskForLowNibble(0x0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
var number = item.Definition.Number;
|
||||
if (number >= 10)
|
||||
{
|
||||
// Elf items start again at 0. What a stupid logic, as there are only 15 sets anyway.
|
||||
number -= 10;
|
||||
}
|
||||
|
||||
preview[index] |= highNibble ? this.GetOrMaskForHighNibble(number) : this.GetOrMaskForLowNibble(number);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetItemLevels(Span<byte> preview, ItemAppearance?[] itemArray)
|
||||
{
|
||||
int levelIndex = 0;
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (itemArray[i] is not null)
|
||||
{
|
||||
levelIndex |= itemArray[i]!.GetGlowLevel() << (i * 3);
|
||||
}
|
||||
}
|
||||
|
||||
preview[6] = (byte)((levelIndex >> 16) & 255);
|
||||
preview[7] = (byte)((levelIndex >> 8) & 255);
|
||||
preview[8] = (byte)(levelIndex & 255);
|
||||
}
|
||||
}
|
||||
244
src/GameServer/RemoteView/AppearanceSerializer095.cs
Normal file
244
src/GameServer/RemoteView/AppearanceSerializer095.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
// <copyright file="AppearanceSerializer095.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Serializer for the appearance of a player, compatible with the client of version 0.95.
|
||||
/// </summary>
|
||||
[Guid("99616318-38BC-4C0A-A818-29D821B78DE2")]
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.AppearanceSerializer095_Name), Description = nameof(PlugInResources.AppearanceSerializer095_Description), ResourceType = typeof(PlugInResources))]
|
||||
[MinimumClient(0, 95, ClientLanguage.Invariant)]
|
||||
public class AppearanceSerializer095 : IAppearanceSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache which holds the results of the serializer.
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<IAppearanceData, byte[]> Cache = new();
|
||||
|
||||
private enum Pets
|
||||
{
|
||||
Angel = 0,
|
||||
Imp = 1,
|
||||
Unicorn = 2,
|
||||
Dinorant = 3,
|
||||
}
|
||||
|
||||
private enum WingIndex
|
||||
{
|
||||
WingsOfElf = 0,
|
||||
WingsOfHeaven = 1,
|
||||
WingsOfSatan = 2,
|
||||
None = 3,
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int NeededSpace => 11;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InvalidateCache(IAppearanceData appearance)
|
||||
{
|
||||
Cache.TryRemove(appearance, out _);
|
||||
appearance.AppearanceChanged -= this.OnAppearanceOfAppearanceChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void WriteAppearanceData(Span<byte> target, IAppearanceData appearance, bool useCache)
|
||||
{
|
||||
if (target.Length < this.NeededSpace)
|
||||
{
|
||||
throw new ArgumentException($"Target span too small. Actual size: {target.Length}; Required: {this.NeededSpace}.", nameof(target));
|
||||
}
|
||||
|
||||
if (useCache && Cache.TryGetValue(appearance, out var cached))
|
||||
{
|
||||
cached.CopyTo(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.WritePreviewCharSet(target, appearance);
|
||||
if (useCache)
|
||||
{
|
||||
var cacheEntry = target.Slice(0, this.NeededSpace).ToArray();
|
||||
if (Cache.TryAdd(appearance, cacheEntry))
|
||||
{
|
||||
appearance.AppearanceChanged += this.OnAppearanceOfAppearanceChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAppearanceOfAppearanceChanged(object? sender, EventArgs args) => this.InvalidateCache(sender as IAppearanceData ?? throw new ArgumentException($"sender must be of type {nameof(IAppearanceData)}"));
|
||||
|
||||
/// <summary>
|
||||
/// Writes the preview character set.
|
||||
/// </summary>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="appearanceData">The appearance data.</param>
|
||||
private void WritePreviewCharSet(Span<byte> target, IAppearanceData appearanceData)
|
||||
{
|
||||
ItemAppearance?[] itemArray = new ItemAppearance[InventoryConstants.EquippableSlotsCount];
|
||||
for (byte i = 0; i < itemArray.Length; i++)
|
||||
{
|
||||
itemArray[i] = appearanceData.EquippedItems.FirstOrDefault(item => item.ItemSlot == i && item.Definition?.Number < 16);
|
||||
}
|
||||
|
||||
if (appearanceData.CharacterClass is not null)
|
||||
{
|
||||
target[0] = (byte)(appearanceData.CharacterClass.Number << 3);
|
||||
/* 00 Dark Wizard
|
||||
10 Soul Master
|
||||
20 Dark Knight
|
||||
30 Blade Knight
|
||||
40 Elf
|
||||
50 Muse Elf
|
||||
60 Magic Gladiator*/
|
||||
}
|
||||
|
||||
target[0] |= (byte)appearanceData.Pose;
|
||||
this.SetHand(target, itemArray[InventoryConstants.LeftHandSlot], 1);
|
||||
|
||||
this.SetHand(target, itemArray[InventoryConstants.RightHandSlot], 2);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.HelmSlot], 3, true, 0x80);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.ArmorSlot], 3, false, 0x40);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.PantsSlot], 4, true, 0x20);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.GlovesSlot], 4, false, 0x10);
|
||||
|
||||
this.SetArmorPiece(target, itemArray[InventoryConstants.BootsSlot], 5, true, 0x08);
|
||||
|
||||
target[5] |= (byte)((itemArray[InventoryConstants.WingsSlot]?.Definition?.Number & 0x03) << 2 ?? 0b1100);
|
||||
this.SetPet(target, itemArray[InventoryConstants.PetSlot]);
|
||||
|
||||
// index9: upper 5 bits are the equipped flags of SetArmorPiece
|
||||
this.SetItemLevels(target, itemArray);
|
||||
}
|
||||
|
||||
private void SetPet(Span<byte> preview, ItemAppearance? item)
|
||||
{
|
||||
var pet = (Pets?)item?.Definition?.Number;
|
||||
switch (pet)
|
||||
{
|
||||
case Pets.Angel:
|
||||
case Pets.Imp:
|
||||
case Pets.Unicorn:
|
||||
preview[5] |= (byte)pet;
|
||||
break;
|
||||
case Pets.Dinorant:
|
||||
preview[5] |= 0b11;
|
||||
preview[9] |= 0b100;
|
||||
break;
|
||||
default:
|
||||
preview[5] |= 0b11;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetHand(Span<byte> preview, ItemAppearance? item, int index)
|
||||
{
|
||||
if (item?.Definition is null)
|
||||
{
|
||||
preview[index] = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
preview[index] = (byte)item.Definition.Number;
|
||||
preview[index] |= (byte)(item.Definition.Group << 5);
|
||||
}
|
||||
}
|
||||
|
||||
private byte GetOrMaskForHighNibble(int value)
|
||||
{
|
||||
return (byte)((value << 4) & 0xF0);
|
||||
}
|
||||
|
||||
private byte GetOrMaskForLowNibble(int value)
|
||||
{
|
||||
return (byte)(value & 0x0F);
|
||||
}
|
||||
|
||||
private void SetArmorPiece(Span<byte> preview, ItemAppearance? item, int firstIndex, bool firstIndexHigh, byte secondIndexMask)
|
||||
{
|
||||
if (item?.Definition is null)
|
||||
{
|
||||
this.SetEmptyArmor(preview, firstIndex, firstIndexHigh, secondIndexMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
// item id
|
||||
this.SetArmorItemIndex(preview, item, firstIndex, firstIndexHigh, secondIndexMask);
|
||||
|
||||
// exc bit
|
||||
if (this.IsExcellent(item))
|
||||
{
|
||||
preview[10] |= secondIndexMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetEmptyArmor(Span<byte> preview, int firstIndex, bool firstIndexHigh, byte secondIndexMask)
|
||||
{
|
||||
// if the item is not equipped every index bit is set to 1
|
||||
preview[firstIndex] |= firstIndexHigh ? this.GetOrMaskForHighNibble(0x0F) : this.GetOrMaskForLowNibble(0x0F);
|
||||
preview[9] |= secondIndexMask;
|
||||
}
|
||||
|
||||
private void SetArmorItemIndex(Span<byte> preview, ItemAppearance item, int firstIndex, bool firstIndexHigh, byte secondIndexMask)
|
||||
{
|
||||
preview[firstIndex] |= firstIndexHigh ? this.GetOrMaskForHighNibble(item.Definition!.Number) : this.GetOrMaskForLowNibble(item.Definition!.Number);
|
||||
byte multi = (byte)(item.Definition.Number / 16);
|
||||
if (multi > 0)
|
||||
{
|
||||
byte bit1 = (byte)(multi % 2);
|
||||
if (bit1 == 1)
|
||||
{
|
||||
preview[9] |= secondIndexMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsExcellent(ItemAppearance item)
|
||||
{
|
||||
return item.VisibleOptions.Contains(ItemOptionTypes.Excellent);
|
||||
}
|
||||
|
||||
private void SetItemLevels(Span<byte> preview, ItemAppearance?[] itemArray)
|
||||
{
|
||||
int levelIndex = 0;
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (itemArray[i] is not null)
|
||||
{
|
||||
levelIndex |= itemArray[i]!.GetGlowLevel() << (i * 3);
|
||||
}
|
||||
}
|
||||
|
||||
preview[6] = (byte)((levelIndex >> 16) & 255);
|
||||
preview[7] = (byte)((levelIndex >> 8) & 255);
|
||||
preview[8] = (byte)(levelIndex & 255);
|
||||
}
|
||||
|
||||
private void AddWing(Span<byte> preview, ItemAppearance? wing)
|
||||
{
|
||||
if (wing?.Definition is null)
|
||||
{
|
||||
preview[5] |= 0x0C;
|
||||
return;
|
||||
}
|
||||
|
||||
preview[5] |= (byte)((wing?.Definition?.Number & 0x03) << 2 ?? 0b1100);
|
||||
}
|
||||
}
|
||||
284
src/GameServer/RemoteView/AppearanceSerializerExtended.cs
Normal file
284
src/GameServer/RemoteView/AppearanceSerializerExtended.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
// <copyright file="AppearanceSerializerExtended.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel;
|
||||
using MUnique.OpenMU.DataModel.Configuration.Items;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// Extended serializer for the appearance of a player.
|
||||
/// </summary>
|
||||
[Guid("A17EDFF7-236B-4EC9-899A-A6FC8BBA840C")]
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.AppearanceSerializerExtended_Name), Description = nameof(PlugInResources.AppearanceSerializerExtended_Description), ResourceType = typeof(PlugInResources))]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class AppearanceSerializerExtended : IAppearanceSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache which holds the results of the serializer.
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<IAppearanceData, byte[]> Cache = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int NeededSpace => 27;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InvalidateCache(IAppearanceData appearance)
|
||||
{
|
||||
Cache.TryRemove(appearance, out _);
|
||||
appearance.AppearanceChanged -= this.OnAppearanceOfAppearanceChanged;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void WriteAppearanceData(Span<byte> target, IAppearanceData appearance, bool useCache)
|
||||
{
|
||||
if (target.Length < this.NeededSpace)
|
||||
{
|
||||
throw new ArgumentException($"Target span too small. Actual size: {target.Length}; Required: {this.NeededSpace}.", nameof(target));
|
||||
}
|
||||
|
||||
if (useCache && Cache.TryGetValue(appearance, out var cached))
|
||||
{
|
||||
cached.CopyTo(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.WritePreviewCharSet(target, appearance);
|
||||
if (useCache)
|
||||
{
|
||||
var cacheEntry = target.Slice(0, this.NeededSpace).ToArray();
|
||||
if (Cache.TryAdd(appearance, cacheEntry))
|
||||
{
|
||||
appearance.AppearanceChanged += this.OnAppearanceOfAppearanceChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAppearanceOfAppearanceChanged(object? sender, EventArgs args)
|
||||
{
|
||||
this.InvalidateCache(sender as IAppearanceData ?? throw new ArgumentException($"sender must be of type {nameof(IAppearanceData)}"));
|
||||
}
|
||||
|
||||
private void WritePreviewCharSet(Span<byte> target, IAppearanceData appearanceData)
|
||||
{
|
||||
var itemArray = new ItemAppearance?[InventoryConstants.EquippableSlotsCount];
|
||||
for (byte i = 0; i < itemArray.Length; i++)
|
||||
{
|
||||
itemArray[i] = appearanceData.EquippedItems.FirstOrDefault(item => item.ItemSlot == i);
|
||||
}
|
||||
|
||||
if (appearanceData.CharacterClass is not null)
|
||||
{
|
||||
target[0] = appearanceData.CharacterClass.Number;
|
||||
}
|
||||
|
||||
target[1] = (byte)appearanceData.Pose;
|
||||
if (appearanceData.FullAncientSetEquipped)
|
||||
{
|
||||
target[1] |= 0x10;
|
||||
}
|
||||
|
||||
if (appearanceData.CharacterStatus == CharacterStatus.GameMaster)
|
||||
{
|
||||
target[1] |= 0x20;
|
||||
}
|
||||
|
||||
var items = target[2..];
|
||||
this.SetShinyItem(items[0..3], itemArray[InventoryConstants.LeftHandSlot]);
|
||||
this.SetShinyItem(items[3..6], itemArray[InventoryConstants.RightHandSlot]);
|
||||
this.SetShinyItem(items[6..9], itemArray[InventoryConstants.HelmSlot]);
|
||||
this.SetShinyItem(items[9..12], itemArray[InventoryConstants.ArmorSlot]);
|
||||
this.SetShinyItem(items[12..15], itemArray[InventoryConstants.PantsSlot]);
|
||||
this.SetShinyItem(items[15..18], itemArray[InventoryConstants.GlovesSlot]);
|
||||
this.SetShinyItem(items[18..21], itemArray[InventoryConstants.BootsSlot]);
|
||||
this.SetUnshinyItem(items[21..23], itemArray[InventoryConstants.WingsSlot]);
|
||||
this.SetUnshinyItem(items[23..25], itemArray[InventoryConstants.PetSlot]);
|
||||
|
||||
var pet = itemArray[InventoryConstants.PetSlot];
|
||||
if (pet is not null)
|
||||
{
|
||||
if (pet.VisibleOptions.Contains(ItemOptionTypes.BlackFenrir))
|
||||
{
|
||||
items[23] |= 0b10;
|
||||
}
|
||||
|
||||
if (pet.VisibleOptions.Contains(ItemOptionTypes.BlueFenrir))
|
||||
{
|
||||
items[23] |= 0b100;
|
||||
}
|
||||
|
||||
if (pet.VisibleOptions.Contains(ItemOptionTypes.GoldFenrir))
|
||||
{
|
||||
items[23] |= 0b110;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetUnshinyItem(Span<byte> data, ItemAppearance? item)
|
||||
{
|
||||
var unshinyItem = new UnshinyItem(data);
|
||||
if (item?.Definition is null)
|
||||
{
|
||||
unshinyItem.Group = 0xF;
|
||||
unshinyItem.Number = 0xFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
unshinyItem.Group = item.Definition.Group;
|
||||
unshinyItem.Number = (ushort)item.Definition.Number;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetShinyItem(Span<byte> data, ItemAppearance? item)
|
||||
{
|
||||
var shinyItem = new ShinyItem(data);
|
||||
if (item?.Definition is null)
|
||||
{
|
||||
shinyItem.Group = 0xF;
|
||||
shinyItem.Number = 0xFFF;
|
||||
shinyItem.GlowLevel = 0;
|
||||
shinyItem.IsExcellent = false;
|
||||
shinyItem.IsAncient = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
shinyItem.Group = item.Definition.Group;
|
||||
shinyItem.Number = (ushort)item.Definition.Number;
|
||||
shinyItem.GlowLevel = item.GetGlowLevel();
|
||||
shinyItem.IsExcellent = this.IsExcellent(item);
|
||||
shinyItem.IsAncient = this.IsAncient(item);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsExcellent(ItemAppearance item)
|
||||
{
|
||||
return item.VisibleOptions.Contains(ItemOptionTypes.Excellent);
|
||||
}
|
||||
|
||||
private bool IsAncient(ItemAppearance item)
|
||||
{
|
||||
return item.VisibleOptions.Contains(ItemOptionTypes.AncientOption);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Armor/Weapon Item: (3 bytes)
|
||||
/// Group: 4 bit
|
||||
/// Number: 12 bit
|
||||
/// Level: 4 bit
|
||||
/// IsExc: 1 bit
|
||||
/// IsAnc: 1 bit.
|
||||
/// </summary>
|
||||
private readonly ref struct ShinyItem(Span<byte> data)
|
||||
{
|
||||
private readonly Span<byte> _data = data;
|
||||
|
||||
public byte Group
|
||||
{
|
||||
get => (byte)((this._data[0] >> 4) & 0xF);
|
||||
set
|
||||
{
|
||||
value <<= 4;
|
||||
value |= (byte)(this._data[0] & 0xF);
|
||||
this._data[0] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Number
|
||||
{
|
||||
get => (ushort)(((this._data[0] & 0xF) << 8) + this._data[1]);
|
||||
set
|
||||
{
|
||||
// Higher 4 bits of the first byte for the higher bits of the value
|
||||
this._data[0] = (byte)((this._data[0] & 0xF0) | (((value & 0x0F00) >> 8) & 0xF));
|
||||
|
||||
// The lower bits in the second byte
|
||||
this._data[1] = (byte)(value & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the glow level of the item.
|
||||
/// </summary>
|
||||
public byte GlowLevel
|
||||
{
|
||||
get => (byte)((this._data[2] & 0xF0) >> 4);
|
||||
set
|
||||
{
|
||||
value = (byte)((value & 0xF) << 4);
|
||||
value |= (byte)(this._data[2] & 0xF);
|
||||
this._data[2] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExcellent
|
||||
{
|
||||
get => (this._data[2] & 0x08) != 0;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
this._data[2] |= 0b00001000;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._data[2] &= 0b11110111;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAncient
|
||||
{
|
||||
get => (this._data[2] & 0x04) != 0;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
this._data[2] |= 0b00000100;
|
||||
}
|
||||
else
|
||||
{
|
||||
this._data[2] &= 0b11111011;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unshiny Item: (2 bytes)
|
||||
/// Group: 4 bit
|
||||
/// Number: 12 bit.
|
||||
/// </summary>
|
||||
private readonly ref struct UnshinyItem(Span<byte> data)
|
||||
{
|
||||
private readonly Span<byte> _data = data;
|
||||
|
||||
public byte Group
|
||||
{
|
||||
get => (byte)((this._data[0] >> 4) & 0xF);
|
||||
set
|
||||
{
|
||||
value <<= 4;
|
||||
value |= (byte)(this._data[0] & 0xF);
|
||||
this._data[0] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Number
|
||||
{
|
||||
get => (ushort)(((this._data[0] & 0xF) << 8) + this._data[1]);
|
||||
set
|
||||
{
|
||||
this._data[1] = (byte)(value & 0xFF);
|
||||
this._data[0] = (byte)((this._data[0] & 0xF0) | ((value >> 8) & 0xF));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// <copyright file="AddExperienceExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IAddExperiencePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.AddExperienceExtendedPlugIn_Name), Description = nameof(PlugInResources.AddExperienceExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("2C5AAEF1-47C9-498F-B3C1-D0F1B9AF0496")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class AddExperienceExtendedPlugIn : IAddExperiencePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddExperienceExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public AddExperienceExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask AddExperienceAsync(int exp, IAttackable? obj, ExperienceType experienceType)
|
||||
{
|
||||
uint damage = 0;
|
||||
|
||||
// Show damage only for party members.
|
||||
if (obj is not null
|
||||
&& this._player.Id != obj.LastDeath?.KillerId)
|
||||
{
|
||||
damage = (uint)Math.Min(obj.LastDeath?.FinalHit.HealthDamage ?? 0, uint.MaxValue);
|
||||
}
|
||||
|
||||
var killedId = obj?.GetId(this._player) ?? 0;
|
||||
var killerId = obj?.LastDeath?.KillerId ?? 0;
|
||||
if (killerId == this._player.Id)
|
||||
{
|
||||
killerId = ViewExtensions.ConstantPlayerId;
|
||||
}
|
||||
|
||||
await this._player.Connection.SendExperienceGainedExtendedAsync(
|
||||
Convert(experienceType),
|
||||
(uint)exp,
|
||||
damage,
|
||||
killedId,
|
||||
killerId)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static ExperienceGainedExtended.AddResult Convert(ExperienceType experienceType)
|
||||
{
|
||||
return experienceType switch
|
||||
{
|
||||
ExperienceType.Normal => ExperienceGainedExtended.AddResult.Normal,
|
||||
ExperienceType.Master => ExperienceGainedExtended.AddResult.Master,
|
||||
ExperienceType.MaxLevelReached => ExperienceGainedExtended.AddResult.MaxLevelReached,
|
||||
ExperienceType.MaxMasterLevelReached => ExperienceGainedExtended.AddResult.MaxMasterLevelReached,
|
||||
ExperienceType.MonsterLevelTooLowForMasterExperience => ExperienceGainedExtended.AddResult.MonsterLevelTooLowForMasterExperience,
|
||||
_ => ExperienceGainedExtended.AddResult.Undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
52
src/GameServer/RemoteView/Character/AddExperiencePlugIn.cs
Normal file
52
src/GameServer/RemoteView/Character/AddExperiencePlugIn.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// <copyright file="AddExperiencePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IAddExperiencePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.AddExperiencePlugIn_Name), Description = nameof(PlugInResources.AddExperiencePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("cc400edd-3540-4727-9b23-8c0ded4f0b00")]
|
||||
public class AddExperiencePlugIn : IAddExperiencePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddExperiencePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public AddExperiencePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask AddExperienceAsync(int exp, IAttackable? obj, ExperienceType experienceType)
|
||||
{
|
||||
var remainingExperience = exp;
|
||||
ushort damage = 0;
|
||||
if (obj is not null && obj.Id != obj.LastDeath?.KillerId)
|
||||
{
|
||||
damage = (ushort)Math.Min(obj.LastDeath?.FinalHit.HealthDamage ?? 0, ushort.MaxValue);
|
||||
}
|
||||
|
||||
var id = (ushort)(obj.GetId(this._player) | 0x8000);
|
||||
while (remainingExperience > 0)
|
||||
{
|
||||
// We send multiple exp packets if the value is bigger than ushort.MaxValue, because that's all what the packet can carry.
|
||||
// On a normal exp server this should never be an issue, but with higher settings, it fixes the problem that the exp bar
|
||||
// shows less exp than the player actually gained.
|
||||
ushort sendExp = remainingExperience > ushort.MaxValue ? ushort.MaxValue : (ushort)remainingExperience;
|
||||
await this._player.Connection.SendExperienceGainedAsync(id, sendExp, damage).ConfigureAwait(false);
|
||||
damage = 0; // don't send damage again
|
||||
remainingExperience -= sendExp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// <copyright file="ApplyKeyConfigurationPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IApplyKeyConfigurationPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ApplyKeyConfigurationPlugIn_Name), Description = nameof(PlugInResources.ApplyKeyConfigurationPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("6DFF0BF8-2E35-4C1D-9778-3406FCFB4716")]
|
||||
public class ApplyKeyConfigurationPlugIn : IApplyKeyConfigurationPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplyKeyConfigurationPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ApplyKeyConfigurationPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ApplyKeyConfigurationAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var keyConfiguration = this._player.SelectedCharacter?.KeyConfiguration;
|
||||
if (keyConfiguration is null || keyConfiguration.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendApplyKeyConfigurationAsync(keyConfiguration).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// <copyright file="CharacterAppearanceDataAdapter.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using MUnique.OpenMU.DataModel;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
|
||||
/// <summary>
|
||||
/// Adapter which implements <see cref="IAppearanceData"/> and takes a <see cref="Character"/>.
|
||||
/// </summary>
|
||||
internal class CharacterAppearanceDataAdapter : IAppearanceData
|
||||
{
|
||||
private readonly Character _character;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CharacterAppearanceDataAdapter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="character">The character.</param>
|
||||
public CharacterAppearanceDataAdapter(Character character)
|
||||
{
|
||||
this._character = character;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the appearance of the player changed.
|
||||
/// </summary>
|
||||
/// <remarks>This never happens in this implementation.</remarks>
|
||||
public event EventHandler? AppearanceChanged;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CharacterClass? CharacterClass => this._character?.CharacterClass;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CharacterStatus CharacterStatus => this._character?.CharacterStatus ?? default;
|
||||
|
||||
/// <inheritdoc />
|
||||
public CharacterPose Pose => CharacterPose.Standing;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool FullAncientSetEquipped => this._character.HasFullAncientSetEquipped();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ItemAppearance> EquippedItems
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this._character.Inventory != null)
|
||||
{
|
||||
return this._character.Inventory.Items
|
||||
.Where(item => item.Definition is not null)
|
||||
.Where(item => item.ItemSlot <= InventoryConstants.LastEquippableItemSlotIndex)
|
||||
.Select(item => item.GetAppearance());
|
||||
}
|
||||
|
||||
return Enumerable.Empty<ItemAppearance>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// <copyright file="CharacterFocusedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="ICharacterFocusedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.CharacterFocusedPlugIn_Name), Description = nameof(PlugInResources.CharacterFocusedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("f6fa0149-3a2c-41c2-aeb1-94b647641ffb")]
|
||||
public class CharacterFocusedPlugIn : ICharacterFocusedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CharacterFocusedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public CharacterFocusedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask CharacterFocusedAsync(Character character)
|
||||
{
|
||||
return this._player.Connection.SendCharacterFocusedAsync(character.Name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// <copyright file="CharacterStatusExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
using CharacterStatus = MUnique.OpenMU.Network.Packets.ServerToClient.CharacterStatus;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="DataModel.Entities.CharacterStatus"/>.
|
||||
/// </summary>
|
||||
internal static class CharacterStatusExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the status of the data model to the status of the defined packets.
|
||||
/// </summary>
|
||||
/// <param name="status">The status of the character in the type of the data model.</param>
|
||||
/// <returns>The status of the character in the type of the packet definitions.</returns>
|
||||
public static CharacterStatus Convert(this DataModel.Entities.CharacterStatus status)
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
DataModel.Entities.CharacterStatus.Normal => CharacterStatus.Normal,
|
||||
DataModel.Entities.CharacterStatus.Banned => CharacterStatus.Banned,
|
||||
DataModel.Entities.CharacterStatus.GameMaster => CharacterStatus.GameMaster,
|
||||
_ => throw new ArgumentException($"Unknown character status {status}"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="HeroState"/> of the data model to the <see cref="CharacterHeroState"/> of the defined packets.
|
||||
/// </summary>
|
||||
/// <param name="heroState">The <see cref="HeroState"/> of the data model.</param>
|
||||
/// <returns>The <see cref="CharacterHeroState"/> of the defined packets.</returns>
|
||||
public static CharacterHeroState Convert(this HeroState heroState)
|
||||
{
|
||||
return heroState switch
|
||||
{
|
||||
HeroState.Normal => CharacterHeroState.Normal,
|
||||
HeroState.Hero => CharacterHeroState.Hero,
|
||||
HeroState.LightHero => CharacterHeroState.LightHero,
|
||||
HeroState.New => CharacterHeroState.New,
|
||||
HeroState.PlayerKillWarning => CharacterHeroState.PlayerKillWarning,
|
||||
HeroState.PlayerKiller1stStage => CharacterHeroState.PlayerKiller1stStage,
|
||||
HeroState.PlayerKiller2ndStage => CharacterHeroState.PlayerKiller2ndStage,
|
||||
_ => throw new ArgumentException($"Unhandled case of {heroState}."),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// <copyright file="FruitConsumptionResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IFruitConsumptionResponsePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.FruitConsumptionResultPlugIn_Name), Description = nameof(PlugInResources.FruitConsumptionResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("0B4D7CA8-181B-4AB5-9522-C826F6311CD8")]
|
||||
public class FruitConsumptionResultPlugIn : IFruitConsumptionResponsePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FruitConsumptionResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public FruitConsumptionResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowResponseAsync(FruitConsumptionResult result, byte statPoints, AttributeDefinition statAttribute)
|
||||
{
|
||||
await this._player.Connection.SendFruitConsumptionResponseAsync(Convert(result), statPoints, Convert(statAttribute)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static FruitConsumptionResponse.FruitConsumptionResult Convert(FruitConsumptionResult result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
FruitConsumptionResult.PlusSuccess => FruitConsumptionResponse.FruitConsumptionResult.PlusSuccess,
|
||||
FruitConsumptionResult.PlusFailed => FruitConsumptionResponse.FruitConsumptionResult.PlusFailed,
|
||||
FruitConsumptionResult.PlusPrevented => FruitConsumptionResponse.FruitConsumptionResult.PlusPrevented,
|
||||
FruitConsumptionResult.MinusSuccess => FruitConsumptionResponse.FruitConsumptionResult.MinusSuccess,
|
||||
FruitConsumptionResult.MinusFailed => FruitConsumptionResponse.FruitConsumptionResult.MinusFailed,
|
||||
FruitConsumptionResult.MinusPrevented => FruitConsumptionResponse.FruitConsumptionResult.MinusPrevented,
|
||||
FruitConsumptionResult.MinusSuccessCashShopFruit => FruitConsumptionResponse.FruitConsumptionResult.MinusSuccessCashShopFruit,
|
||||
FruitConsumptionResult.PlusPreventedByMaximum => FruitConsumptionResponse.FruitConsumptionResult.PlusPreventedByMaximum,
|
||||
FruitConsumptionResult.MinusPreventedByMaximum => FruitConsumptionResponse.FruitConsumptionResult.MinusPreventedByMaximum,
|
||||
FruitConsumptionResult.MinusPreventedByDefault => FruitConsumptionResponse.FruitConsumptionResult.MinusPreventedByDefault,
|
||||
_ => throw new ArgumentException($"Unknown result {result}", nameof(result)),
|
||||
};
|
||||
}
|
||||
|
||||
private static FruitConsumptionResponse.FruitStatType Convert(AttributeDefinition statAttribute)
|
||||
{
|
||||
if (statAttribute == Stats.BaseEnergy)
|
||||
{
|
||||
return FruitConsumptionResponse.FruitStatType.Energy;
|
||||
}
|
||||
|
||||
if (statAttribute == Stats.BaseAgility)
|
||||
{
|
||||
return FruitConsumptionResponse.FruitStatType.Agility;
|
||||
}
|
||||
|
||||
if (statAttribute == Stats.BaseStrength)
|
||||
{
|
||||
return FruitConsumptionResponse.FruitStatType.Strength;
|
||||
}
|
||||
|
||||
if (statAttribute == Stats.BaseVitality)
|
||||
{
|
||||
return FruitConsumptionResponse.FruitStatType.Vitality;
|
||||
}
|
||||
|
||||
if (statAttribute == Stats.BaseLeadership)
|
||||
{
|
||||
return FruitConsumptionResponse.FruitStatType.Leadership;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Unknown stat {statAttribute}", nameof(statAttribute));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// <copyright file="MasterSkillLevelChangedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IMasterSkillLevelChangedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.MasterSkillLevelChangedPlugIn_Name), Description = nameof(PlugInResources.MasterSkillLevelChangedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("0eba687e-c7af-421e-8e1e-921fcf31c027")]
|
||||
public class MasterSkillLevelChangedPlugIn : IMasterSkillLevelChangedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MasterSkillLevelChangedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public MasterSkillLevelChangedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask MasterSkillLevelChangedAsync(SkillEntry skillEntry)
|
||||
{
|
||||
var character = this._player.SelectedCharacter;
|
||||
if (character?.CharacterClass is null || skillEntry.Skill is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await this._player.Connection.SendMasterSkillLevelUpdateAsync(
|
||||
true,
|
||||
(ushort)character.MasterLevelUpPoints,
|
||||
skillEntry.Skill.GetMasterSkillIndex(character.CharacterClass),
|
||||
(ushort)skillEntry.Skill.Number,
|
||||
(byte)skillEntry.Level,
|
||||
skillEntry.CalculateDisplayValue(),
|
||||
skillEntry.CalculateNextDisplayValue()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ShowCharacterCreationFailedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowCharacterCreationFailedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowCharacterCreationFailedPlugIn_Name), Description = nameof(PlugInResources.ShowCharacterCreationFailedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("8fe59675-8625-4837-9872-e8d9c73471cd")]
|
||||
public class ShowCharacterCreationFailedPlugIn : IShowCharacterCreationFailedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowCharacterCreationFailedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowCharacterCreationFailedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowCharacterCreationFailedAsync()
|
||||
{
|
||||
await this._player.Connection.SendCharacterCreationFailedAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// <copyright file="ShowCharacterDeleteResponsePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowCharacterDeleteResponsePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowCharacterDeleteResponsePlugIn_Name), Description = nameof(PlugInResources.ShowCharacterDeleteResponsePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("303af9ef-4f7d-4e07-9976-a0241311e50d")]
|
||||
public class ShowCharacterDeleteResponsePlugIn : IShowCharacterDeleteResponsePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowCharacterDeleteResponsePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowCharacterDeleteResponsePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowCharacterDeleteResponseAsync(CharacterDeleteResult result)
|
||||
{
|
||||
await this._player.Connection.SendCharacterDeleteResponseAsync(ConvertResult(result)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static CharacterDeleteResponse.CharacterDeleteResult ConvertResult(CharacterDeleteResult result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
CharacterDeleteResult.Successful => CharacterDeleteResponse.CharacterDeleteResult.Successful,
|
||||
CharacterDeleteResult.Unsuccessful => CharacterDeleteResponse.CharacterDeleteResult.Unsuccessful,
|
||||
CharacterDeleteResult.WrongSecurityCode => CharacterDeleteResponse.CharacterDeleteResult.WrongSecurityCode,
|
||||
_ => throw new ArgumentException($"Case {result} is not handled."),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// <copyright file="ShowCharacterListExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowCharacterListPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowCharacterListExtendedPlugIn_Name), Description = nameof(PlugInResources.ShowCharacterListExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("DDDEED0A-9421-4A9B-9ED8-0691B7051666")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class ShowCharacterListExtendedPlugIn : IShowCharacterListPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowCharacterListExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowCharacterListExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowCharacterListAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is not { } account)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var unlockFlags = CreateUnlockFlags(account);
|
||||
await this.SendCharacterListAsync(connection, account, unlockFlags).ConfigureAwait(false);
|
||||
if (unlockFlags > CharacterCreationUnlockFlags.None)
|
||||
{
|
||||
await connection.SendCharacterClassCreationUnlockAsync(unlockFlags).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static CharacterCreationUnlockFlags CreateUnlockFlags(Account account)
|
||||
{
|
||||
byte aggregatedFlags = 0;
|
||||
var result = account.UnlockedCharacterClasses?
|
||||
.Select(c => c.CreationAllowedFlag)
|
||||
.Aggregate(aggregatedFlags, (current, flag) => (byte)(current | flag)) ?? 0;
|
||||
return (CharacterCreationUnlockFlags)result;
|
||||
}
|
||||
|
||||
private async ValueTask SendCharacterListAsync(IConnection connection, Account account, CharacterCreationUnlockFlags unlockFlags)
|
||||
{
|
||||
var guildPositions = new GuildPosition?[account.Characters.Count];
|
||||
int i = 0;
|
||||
foreach (var character in account.Characters)
|
||||
{
|
||||
guildPositions[i] = await this._player.GameServerContext.GuildServer.GetGuildPositionAsync(character.Id).ConfigureAwait(false);
|
||||
i++;
|
||||
}
|
||||
|
||||
var appearanceSerializer = this._player.AppearanceSerializer;
|
||||
int Write()
|
||||
{
|
||||
var size = CharacterListExtendedRef.GetRequiredSize(account.Characters.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new CharacterListExtendedRef(span)
|
||||
{
|
||||
UnlockFlags = unlockFlags,
|
||||
CharacterCount = (byte)account.Characters.Count,
|
||||
IsVaultExtended = account.IsVaultExtended,
|
||||
};
|
||||
|
||||
var j = 0;
|
||||
foreach (var character in account.Characters.OrderBy(c => c.CharacterSlot))
|
||||
{
|
||||
var characterData = packet[j];
|
||||
characterData.SlotIndex = character.CharacterSlot;
|
||||
characterData.Name = character.Name;
|
||||
characterData.Level = (ushort)(character.Attributes.FirstOrDefault(s => s.Definition == Stats.Level)?.Value ?? 1);
|
||||
characterData.Status = character.CharacterStatus.Convert();
|
||||
characterData.GuildPosition = guildPositions[j].Convert();
|
||||
appearanceSerializer.WriteAppearanceData(characterData.Appearance, new CharacterAppearanceDataAdapter(character), false);
|
||||
j++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
101
src/GameServer/RemoteView/Character/ShowCharacterListPlugIn.cs
Normal file
101
src/GameServer/RemoteView/Character/ShowCharacterListPlugIn.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
// <copyright file="ShowCharacterListPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowCharacterListPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowCharacterListPlugIn_Name), Description = nameof(PlugInResources.ShowCharacterListPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("9563dd2c-85cc-4b23-aa95-9d1a18582032")]
|
||||
[MinimumClient(5, 0, ClientLanguage.Invariant)]
|
||||
public class ShowCharacterListPlugIn : IShowCharacterListPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowCharacterListPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowCharacterListPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowCharacterListAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is not { } account)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var unlockFlags = CreateUnlockFlags(account);
|
||||
await this.SendCharacterListAsync(connection, account, unlockFlags).ConfigureAwait(false);
|
||||
if (unlockFlags > CharacterCreationUnlockFlags.None)
|
||||
{
|
||||
await connection.SendCharacterClassCreationUnlockAsync(unlockFlags).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static CharacterCreationUnlockFlags CreateUnlockFlags(Account account)
|
||||
{
|
||||
byte aggregatedFlags = 0;
|
||||
var result = account.UnlockedCharacterClasses?
|
||||
.Select(c => c.CreationAllowedFlag)
|
||||
.Aggregate(aggregatedFlags, (current, flag) => (byte)(current | flag)) ?? 0;
|
||||
return (CharacterCreationUnlockFlags)result;
|
||||
}
|
||||
|
||||
private async ValueTask SendCharacterListAsync(IConnection connection, Account account, CharacterCreationUnlockFlags unlockFlags)
|
||||
{
|
||||
var guildPositions = new GuildPosition?[account.Characters.Count];
|
||||
int i = 0;
|
||||
foreach (var character in account.Characters)
|
||||
{
|
||||
guildPositions[i] = await this._player.GameServerContext.GuildServer.GetGuildPositionAsync(character.Id).ConfigureAwait(false);
|
||||
i++;
|
||||
}
|
||||
|
||||
var appearanceSerializer = this._player.AppearanceSerializer;
|
||||
int Write()
|
||||
{
|
||||
var size = CharacterListRef.GetRequiredSize(account.Characters.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new CharacterListRef(span)
|
||||
{
|
||||
UnlockFlags = unlockFlags,
|
||||
CharacterCount = (byte)account.Characters.Count,
|
||||
IsVaultExtended = account.IsVaultExtended,
|
||||
};
|
||||
|
||||
var j = 0;
|
||||
foreach (var character in account.Characters.OrderBy(c => c.CharacterSlot))
|
||||
{
|
||||
var characterData = packet[j];
|
||||
characterData.SlotIndex = character.CharacterSlot;
|
||||
characterData.Name = character.Name;
|
||||
characterData.Level = (ushort)(character.Attributes.FirstOrDefault(s => s.Definition == Stats.Level)?.Value ?? 1);
|
||||
characterData.Status = character.CharacterStatus.Convert();
|
||||
characterData.GuildPosition = guildPositions[j].Convert();
|
||||
appearanceSerializer.WriteAppearanceData(characterData.Appearance, new CharacterAppearanceDataAdapter(character), false);
|
||||
j++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// <copyright file="ShowCharacterListPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowCharacterListPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowCharacterListPlugIn075_Name), Description = nameof(PlugInResources.ShowCharacterListPlugIn075_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("650A3478-729E-4995-ADAF-BDEB829A92E5")]
|
||||
[MinimumClient(0, 75, ClientLanguage.Invariant)]
|
||||
public class ShowCharacterListPlugIn075 : IShowCharacterListPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowCharacterListPlugIn075"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowCharacterListPlugIn075(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowCharacterListAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var appearanceSerializer = this._player.AppearanceSerializer;
|
||||
|
||||
// 0.75 doesn't support dark lord (number 16) and newer classes yet
|
||||
var supportedCharacters = this._player.Account.Characters.Where(c => c.CharacterClass?.Number < 16).OrderBy(c => c.CharacterSlot).ToList();
|
||||
int Write()
|
||||
{
|
||||
var size = CharacterList075Ref.GetRequiredSize(supportedCharacters.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new CharacterList075Ref(span)
|
||||
{
|
||||
CharacterCount = (byte)supportedCharacters.Count,
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
foreach (var character in supportedCharacters.OrderBy(c => c.CharacterSlot))
|
||||
{
|
||||
var characterBlock = packet[i];
|
||||
characterBlock.SlotIndex = character.CharacterSlot;
|
||||
characterBlock.Name = character.Name;
|
||||
characterBlock.Level = (ushort)(character.Attributes.FirstOrDefault(s => s.Definition == Stats.Level)?.Value ?? 1);
|
||||
characterBlock.Status = character.CharacterStatus.Convert();
|
||||
appearanceSerializer.WriteAppearanceData(characterBlock.Appearance, new CharacterAppearanceDataAdapter(character), false);
|
||||
i++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// <copyright file="ShowCharacterListPlugIn095.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowCharacterListPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowCharacterListPlugIn095_Name), Description = nameof(PlugInResources.ShowCharacterListPlugIn095_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("004E97F5-5817-45F5-BB0E-D4F78007768C")]
|
||||
[MinimumClient(0, 95, ClientLanguage.Invariant)]
|
||||
public class ShowCharacterListPlugIn095 : IShowCharacterListPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowCharacterListPlugIn095"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowCharacterListPlugIn095(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowCharacterListAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var appearanceSerializer = this._player.AppearanceSerializer;
|
||||
|
||||
// 0.95 doesn't support dark lord (number 16) and newer classes yet
|
||||
var supportedCharacters = this._player.Account.Characters.Where(c => c.CharacterClass?.Number < 16).OrderBy(c => c.CharacterSlot).ToList();
|
||||
int Write()
|
||||
{
|
||||
var size = CharacterList095Ref.GetRequiredSize(supportedCharacters.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new CharacterList095Ref(span)
|
||||
{
|
||||
CharacterCount = (byte)supportedCharacters.Count,
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
foreach (var character in supportedCharacters.OrderBy(c => c.CharacterSlot))
|
||||
{
|
||||
var characterBlock = packet[i];
|
||||
characterBlock.SlotIndex = character.CharacterSlot;
|
||||
characterBlock.Name = character.Name;
|
||||
characterBlock.Level = (ushort)(character.Attributes.FirstOrDefault(s => s.Definition == Stats.Level)?.Value ?? 1);
|
||||
characterBlock.Status = character.CharacterStatus.Convert();
|
||||
appearanceSerializer.WriteAppearanceData(characterBlock.Appearance, new CharacterAppearanceDataAdapter(character), false);
|
||||
i++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// <copyright file="ShowCreatedCharacterPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowCreatedCharacterPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowCreatedCharacterPlugIn_Name), Description = nameof(PlugInResources.ShowCreatedCharacterPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("f5494592-ff7b-4f7f-b0c1-bd242a69fb8f")]
|
||||
public class ShowCreatedCharacterPlugIn : IShowCreatedCharacterPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowCreatedCharacterPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowCreatedCharacterPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowCreatedCharacterAsync(Character character)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = CharacterCreationSuccessfulRef.Length;
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new CharacterCreationSuccessfulRef(span)
|
||||
{
|
||||
CharacterName = character.Name,
|
||||
CharacterSlot = character.CharacterSlot,
|
||||
Level = (ushort)(character.Attributes.FirstOrDefault(a => a.Definition == Stats.Level)?.Value ?? 0),
|
||||
Class = (CharacterClassNumber)character.CharacterClass!.Number,
|
||||
CharacterStatus = (byte)character.CharacterStatus,
|
||||
};
|
||||
|
||||
packet.PreviewData.Fill(0xFF);
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Character/ShowDialogPlugIn.cs
Normal file
33
src/GameServer/RemoteView/Character/ShowDialogPlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ShowDialogPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowDialogPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowDialogPlugIn_Name), Description = nameof(PlugInResources.ShowDialogPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("DCAB5737-2B44-408F-A14D-C0FD3B5F6516")]
|
||||
public class ShowDialogPlugIn : IShowDialogPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowDialogPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowDialogPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask ShowDialogAsync(byte categoryNumber, byte dialogNumber)
|
||||
{
|
||||
return this._player.Connection.SendServerCommandAsync(categoryNumber, dialogNumber, 0);
|
||||
}
|
||||
}
|
||||
53
src/GameServer/RemoteView/Character/ShowEffectPlugIn.cs
Normal file
53
src/GameServer/RemoteView/Character/ShowEffectPlugIn.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// <copyright file="ShowEffectPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowEffectPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowEffectPlugIn_Name), Description = nameof(PlugInResources.ShowEffectPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("EC433D54-F9CE-40FD-8848-F3515DDD43CF")]
|
||||
public class ShowEffectPlugIn : IShowEffectPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowEffectPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowEffectPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowEffectAsync(IIdentifiable target, IShowEffectPlugIn.EffectType effectType)
|
||||
{
|
||||
if (effectType == IShowEffectPlugIn.EffectType.Swirl)
|
||||
{
|
||||
await this._player.Connection.SendShowSwirlAsync(target.GetId(this._player)).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await this._player.Connection.SendShowEffectAsync(target.GetId(this._player), Convert(effectType)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private static ShowEffect.EffectType Convert(IShowEffectPlugIn.EffectType effectType)
|
||||
{
|
||||
return effectType switch
|
||||
{
|
||||
IShowEffectPlugIn.EffectType.LevelUp => Network.Packets.ServerToClient.ShowEffect.EffectType.LevelUp,
|
||||
IShowEffectPlugIn.EffectType.ShieldLost => Network.Packets.ServerToClient.ShowEffect.EffectType.ShieldLost,
|
||||
IShowEffectPlugIn.EffectType.ShieldPotion => Network.Packets.ServerToClient.ShowEffect.EffectType.ShieldPotion,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(effectType), $"Unknown value {effectType}."),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// <copyright file="ShowItemDropEffectPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Pathfinding;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="ShowItemDropEffectPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowItemDropEffectPlugIn_Name), Description = nameof(PlugInResources.ShowItemDropEffectPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("AA949D5E-0CC6-424E-8412-DE12EA294E33")]
|
||||
public class ShowItemDropEffectPlugIn : IShowItemDropEffectPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowItemDropEffectPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowItemDropEffectPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowEffectAsync(ItemDropEffect effect, Point targetCoordinates)
|
||||
{
|
||||
var (x, y) = targetCoordinates;
|
||||
switch (effect)
|
||||
{
|
||||
case ItemDropEffect.Fireworks:
|
||||
await this._player.Connection.SendShowFireworksAsync(x, y).ConfigureAwait(false);
|
||||
break;
|
||||
case ItemDropEffect.ChristmasFireworks:
|
||||
await this._player.Connection.SendShowChristmasFireworksAsync(x, y).ConfigureAwait(false);
|
||||
break;
|
||||
case ItemDropEffect.FanfareSound:
|
||||
await this._player.Connection.SendPlayFanfareSoundAsync(x, y).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(effect));
|
||||
}
|
||||
}
|
||||
}
|
||||
214
src/GameServer/RemoteView/Character/SkillListViewPlugIn.cs
Normal file
214
src/GameServer/RemoteView/Character/SkillListViewPlugIn.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
// <copyright file="SkillListViewPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="ISkillListViewPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.SkillListViewPlugIn_Name), Description = nameof(PlugInResources.SkillListViewPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("E67BB791-5BE7-4CC8-B2C9-38E86158A356")]
|
||||
[MinimumClient(3, 0, ClientLanguage.Invariant)]
|
||||
public class SkillListViewPlugIn : ISkillListViewPlugIn
|
||||
{
|
||||
private const short Explosion79SkillId = 79;
|
||||
private const short ForceSkillId = 60;
|
||||
private const short ForceWaveSkillId = 66;
|
||||
private const short ForceWaveStrengSkillId = 509;
|
||||
private const short KillingBlowSkillId = 260;
|
||||
private const short BeastUppercutSkillId = 261;
|
||||
private const short KillingBlowStrengSkillId = 551;
|
||||
private const short BeastUppercutStrengSkillId = 552;
|
||||
private const short KillingBlowMasterySkillId = 554;
|
||||
private const short BeastUppercutMasterySkillId = 555;
|
||||
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// This contains again all available skills. However, we need this to maintain the indexes. It can happen that the list contains holes after a skill got removed!.
|
||||
/// </summary>
|
||||
private IList<Skill?>? _skillList;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SkillListViewPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public SkillListViewPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal skill list.
|
||||
/// </summary>
|
||||
protected IList<Skill?> SkillList => this._skillList ??= new List<Skill?>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player.
|
||||
/// </summary>
|
||||
protected RemotePlayer Player => this._player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask AddSkillAsync(Skill skill)
|
||||
{
|
||||
if (skill.Number == ForceWaveSkillId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (skill.Number == KillingBlowSkillId
|
||||
&& this.SkillList.Any(s => s?.Number == KillingBlowStrengSkillId || s?.Number == KillingBlowMasterySkillId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (skill.Number == BeastUppercutSkillId
|
||||
&& this.SkillList.Any(s => s?.Number == BeastUppercutStrengSkillId || s?.Number == BeastUppercutMasterySkillId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var skillIndex = this.AddSkillToList(skill);
|
||||
await this._player.Connection.SendSkillAddedAsync(skillIndex, (ushort)skill.Number, 0).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask RemoveSkillAsync(Skill skill)
|
||||
{
|
||||
if (skill.Number == ForceWaveSkillId
|
||||
&& this.SkillList.Any(s => s?.Number == ForceWaveStrengSkillId)
|
||||
&& this.SkillList.FirstOrDefault(s => s?.Number == ForceSkillId) is { } forceSkill)
|
||||
{
|
||||
// Force wave strengthener is replacing force skill
|
||||
skill = forceSkill;
|
||||
}
|
||||
|
||||
var skillIndex = this.SkillList.IndexOf(skill);
|
||||
if (skillIndex >= 0)
|
||||
{
|
||||
await this._player.Connection.SendSkillRemovedAsync((byte)skillIndex, (ushort)skill.Number).ConfigureAwait(false);
|
||||
this.SkillList[skillIndex] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual async ValueTask UpdateSkillListAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.BuildSkillList();
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = SkillListUpdateRef.GetRequiredSize(this.SkillList.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new SkillListUpdateRef(span)
|
||||
{
|
||||
Count = (byte)this.SkillList.Count,
|
||||
};
|
||||
|
||||
for (byte i = 0; i < this.SkillList.Count; i++)
|
||||
{
|
||||
var skillEntry = packet[i];
|
||||
skillEntry.SkillIndex = i;
|
||||
|
||||
var skill = this.SkillList[i];
|
||||
if (skill is not null)
|
||||
{
|
||||
skillEntry.SkillNumber = (ushort)skill.Number;
|
||||
if (skill.MasterDefinition is not null)
|
||||
{
|
||||
skillEntry.SkillLevel = (byte)(this._player.SkillList!.GetSkill((ushort)skill.Number)?.Level ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Skill? GetSkillByIndex(byte skillIndex)
|
||||
{
|
||||
if (this._skillList != null && this._skillList.Count > skillIndex)
|
||||
{
|
||||
return this._skillList[skillIndex];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the internal skill list, considering duplicates.
|
||||
/// </summary>
|
||||
protected void BuildSkillList()
|
||||
{
|
||||
this.SkillList.Clear();
|
||||
var skills = this._player.SkillList!.Skills.ToList();
|
||||
|
||||
var replacedSkills = skills.Select(entry => entry.Skill?.MasterDefinition?.ReplacedSkill).Where(skill => skill != null);
|
||||
skills.RemoveAll(s => replacedSkills.Contains(s.Skill));
|
||||
skills.RemoveAll(s => s.Skill?.SkillType == SkillType.PassiveBoost);
|
||||
|
||||
skills.RemoveAll(s => s.Skill?.Number == ForceWaveSkillId || s.Skill?.Number == Explosion79SkillId);
|
||||
if (skills.Any(s => s.Skill?.Number == ForceWaveStrengSkillId))
|
||||
{
|
||||
skills.RemoveAll(s => s.Skill?.Number == ForceSkillId);
|
||||
}
|
||||
|
||||
foreach (var skillEntry in skills.Distinct(default(SkillEqualityComparer)))
|
||||
{
|
||||
this.SkillList.Add(skillEntry.Skill);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the skill to the internal skill list.
|
||||
/// </summary>
|
||||
/// <param name="skill">The skill to add.</param>
|
||||
/// <returns>The index of the added skill.</returns>
|
||||
protected byte AddSkillToList(Skill skill)
|
||||
{
|
||||
for (byte i = 0; i < this.SkillList.Count; i++)
|
||||
{
|
||||
if (this.SkillList[i] is null)
|
||||
{
|
||||
this.SkillList[i] = skill;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
this.SkillList.Add(skill);
|
||||
return (byte)(this.SkillList.Count - 1);
|
||||
}
|
||||
|
||||
private struct SkillEqualityComparer : IEqualityComparer<SkillEntry>
|
||||
{
|
||||
public bool Equals(SkillEntry? left, SkillEntry? right)
|
||||
{
|
||||
return Equals(left?.Skill, right?.Skill);
|
||||
}
|
||||
|
||||
public int GetHashCode(SkillEntry obj)
|
||||
{
|
||||
return obj.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
101
src/GameServer/RemoteView/Character/SkillListViewPlugIn075.cs
Normal file
101
src/GameServer/RemoteView/Character/SkillListViewPlugIn075.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
// <copyright file="SkillListViewPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="ISkillListViewPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.SkillListViewPlugIn075_Name), Description = nameof(PlugInResources.SkillListViewPlugIn075_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("D83A0CD8-AFEB-4782-8523-AF6D093D14CB")]
|
||||
public class SkillListViewPlugIn075 : SkillListViewPlugIn
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SkillListViewPlugIn075"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public SkillListViewPlugIn075(RemotePlayer player)
|
||||
: base(player)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async ValueTask AddSkillAsync(Skill skill)
|
||||
{
|
||||
var skillIndex = this.AddSkillToList(skill);
|
||||
await this.Player.Connection.SendSkillAdded075Async(skillIndex, this.GetSkillNumberAndLevel(skill)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async ValueTask RemoveSkillAsync(Skill skill)
|
||||
{
|
||||
var skillIndex = (byte)this.SkillList.IndexOf(skill);
|
||||
await this.Player.Connection.SendSkillRemoved075Async(skillIndex, this.GetSkillNumberAndLevel(skill)).ConfigureAwait(false);
|
||||
this.SkillList[skillIndex] = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async ValueTask UpdateSkillListAsync()
|
||||
{
|
||||
var connection = this.Player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.BuildSkillList();
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = SkillListUpdate075Ref.GetRequiredSize(this.SkillList.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new SkillListUpdate075Ref(span)
|
||||
{
|
||||
Count = (byte)this.SkillList.Count,
|
||||
};
|
||||
|
||||
for (byte i = 0; i < this.SkillList.Count; i++)
|
||||
{
|
||||
var skillEntry = packet[i];
|
||||
skillEntry.SkillIndex = i;
|
||||
skillEntry.SkillNumberAndLevel = this.GetSkillNumberAndLevel(this.SkillList[i]);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value for the skill number and level.
|
||||
/// </summary>
|
||||
/// <param name="skill">The skill.</param>
|
||||
/// <returns>The value for the skill number and level.</returns>
|
||||
protected ushort GetSkillNumberAndLevel(Skill? skill)
|
||||
{
|
||||
if (skill is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var result = (ushort)((skill.Number & 0xFF) << 8);
|
||||
|
||||
// The next lines seems strange but is correct. The same part of the skill number is already set in the first byte.
|
||||
// Unfortunately it's unclear to us which skill got a level greater than 0 in these early versions.
|
||||
// It might be the item level of a weapon with a skill, but this should not be of interest for the client.
|
||||
// It could be the type of summoning orb skill, too. For now, we just don't send this level.
|
||||
var skillLevel = 0;
|
||||
var secondByte = (byte)((skill.Number & 7) | (skillLevel << 3));
|
||||
return (ushort)(result + secondByte);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// <copyright file="SkillListViewPlugIn095.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="ISkillListViewPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Version 0.97d seems to be compatible to this implementation. This may be true until the end of season 2, but we're not sure.
|
||||
/// </remarks>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.SkillListViewPlugIn095_Name), Description = nameof(PlugInResources.SkillListViewPlugIn095_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("60126898-8668-4774-B879-0F211CDD3617")]
|
||||
[MinimumClient(0, 95, ClientLanguage.Invariant)]
|
||||
public class SkillListViewPlugIn095 : SkillListViewPlugIn075
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SkillListViewPlugIn095"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public SkillListViewPlugIn095(RemotePlayer player)
|
||||
: base(player)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async ValueTask AddSkillAsync(Skill skill)
|
||||
{
|
||||
var skillIndex = this.AddSkillToList(skill);
|
||||
await this.Player.Connection.SendSkillAdded095Async(skillIndex, this.GetSkillNumberAndLevel(skill)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async ValueTask RemoveSkillAsync(Skill skill)
|
||||
{
|
||||
var skillIndex = (byte)this.SkillList.IndexOf(skill);
|
||||
await this.Player.Connection.SendSkillRemoved095Async(skillIndex, this.GetSkillNumberAndLevel(skill)).ConfigureAwait(false);
|
||||
this.SkillList[skillIndex] = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// <copyright file="StatIncreaseResultExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IStatIncreaseResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.StatIncreaseResultExtendedPlugIn_Name), Description = nameof(PlugInResources.StatIncreaseResultExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("4B9AEFF1-B139-45F9-9277-0FBDA7A3C020")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class StatIncreaseResultExtendedPlugIn : IStatIncreaseResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StatIncreaseResultExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public StatIncreaseResultExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask StatIncreaseResultAsync(AttributeDefinition attribute, ushort addedPoints)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendCharacterStatIncreaseResponseExtendedAsync(
|
||||
attribute.GetStatType(),
|
||||
addedPoints,
|
||||
(uint)this._player.Attributes![Stats.MaximumHealth],
|
||||
(uint)this._player.Attributes![Stats.MaximumMana],
|
||||
(uint)this._player.Attributes![Stats.MaximumShield],
|
||||
(uint)this._player.Attributes![Stats.MaximumAbility]).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// <copyright file="StatIncreaseResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.GameLogic.Views.World;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IStatIncreaseResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.StatIncreaseResultPlugIn_Name), Description = nameof(PlugInResources.StatIncreaseResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("ce603b3c-cf25-426f-9cb9-5cc367843de8")]
|
||||
public class StatIncreaseResultPlugIn : IStatIncreaseResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StatIncreaseResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public StatIncreaseResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask StatIncreaseResultAsync(AttributeDefinition attribute, ushort addedPoints)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (addedPoints <= 1)
|
||||
{
|
||||
#pragma warning disable SA1118 // Parameter should not span multiple lines
|
||||
await connection.SendCharacterStatIncreaseResponseAsync(
|
||||
addedPoints > 0,
|
||||
attribute.GetStatType(),
|
||||
attribute == Stats.BaseEnergy
|
||||
? (ushort)this._player.Attributes![Stats.MaximumMana]
|
||||
: attribute == Stats.BaseVitality
|
||||
? (ushort)this._player.Attributes![Stats.MaximumHealth]
|
||||
: default,
|
||||
(ushort)this._player.Attributes![Stats.MaximumShield],
|
||||
(ushort)this._player.Attributes[Stats.MaximumAbility]).ConfigureAwait(false);
|
||||
#pragma warning restore SA1118 // Parameter should not span multiple lines
|
||||
return;
|
||||
}
|
||||
|
||||
// Workaround with multiple points for older clients
|
||||
var player = this._player;
|
||||
var map = player.CurrentMap!;
|
||||
|
||||
await player.InvokeViewPlugInAsync<IObjectsOutOfScopePlugIn>(p => p.ObjectsOutOfScopeAsync(player.GetAsEnumerable())).ConfigureAwait(false);
|
||||
await player.InvokeViewPlugInAsync<IUpdateCharacterStatsPlugIn>(p => p.UpdateCharacterStatsAsync()).ConfigureAwait(false);
|
||||
await player.InvokeViewPlugInAsync<IUpdateInventoryListPlugIn>(p => p.UpdateInventoryListAsync()).ConfigureAwait(false);
|
||||
var currentGate = new Persistence.BasicModel.ExitGate
|
||||
{
|
||||
Map = map.Definition,
|
||||
X1 = player.Position.X,
|
||||
X2 = player.Position.X,
|
||||
Y1 = player.Position.Y,
|
||||
Y2 = player.Position.Y,
|
||||
};
|
||||
|
||||
await player.WarpToAsync(currentGate).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// <copyright file="UpdateCharacterBaseStatsExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IUpdateCharacterBaseStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateCharacterBaseStatsExtendedPlugIn_Name), Description = nameof(PlugInResources.UpdateCharacterBaseStatsExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("851C4579-FB3D-454C-A238-217542E8E6B9")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class UpdateCharacterBaseStatsExtendedPlugIn : IUpdateCharacterBaseStatsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateCharacterBaseStatsExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateCharacterBaseStatsExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateCharacterBaseStatsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendBaseStatsExtendedAsync(
|
||||
(uint)this._player.Attributes![Stats.BaseStrength],
|
||||
(uint)this._player.Attributes[Stats.BaseAgility],
|
||||
(uint)this._player.Attributes[Stats.BaseVitality],
|
||||
(uint)this._player.Attributes[Stats.BaseEnergy],
|
||||
(uint)this._player.Attributes[Stats.BaseLeadership])
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// <copyright file="UpdateCharacterHeroStatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateCharacterHeroStatePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateCharacterHeroStatePlugIn_Name), Description = nameof(PlugInResources.UpdateCharacterHeroStatePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("d1ce36d6-cbdd-4bcb-99c7-c7495d8597d9")]
|
||||
public class UpdateCharacterHeroStatePlugIn : IUpdateCharacterHeroStatePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateCharacterHeroStatePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateCharacterHeroStatePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateCharacterHeroStateAsync(Player affectedPlayer)
|
||||
{
|
||||
if (affectedPlayer.SelectedCharacter is { } character)
|
||||
{
|
||||
await this._player.Connection.SendHeroStateChangedAsync(affectedPlayer.GetId(this._player), character.State.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// <copyright file="UpdateCharacterStatsExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IUpdateCharacterStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateCharacterStatsExtendedPlugIn_Name), Description = nameof(PlugInResources.UpdateCharacterStatsExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("851C4579-FB3D-454C-A238-217542E8E6B8")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class UpdateCharacterStatsExtendedPlugIn : IUpdateCharacterStatsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateCharacterStatsExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateCharacterStatsExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateCharacterStatsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var maxAttackSpeed = this._player.GameContext.Configuration.Attributes.FirstOrDefault(a => a == Stats.AttackSpeed)?.MaximumValue ?? 200;
|
||||
await connection.SendCharacterInformationExtendedAsync(
|
||||
this._player.Position.X,
|
||||
this._player.Position.Y,
|
||||
this._player.SelectedCharacter!.CurrentMap!.Number.ToUnsigned(),
|
||||
(ulong)this._player.SelectedCharacter.Experience,
|
||||
(ulong)this._player.GameServerContext.ExperienceTable[(int)this._player.Attributes![Stats.Level] + 1],
|
||||
(ushort)Math.Max(0, this._player.SelectedCharacter.LevelUpPoints),
|
||||
(ushort)this._player.Attributes[Stats.BaseStrength],
|
||||
(ushort)this._player.Attributes[Stats.BaseAgility],
|
||||
(ushort)this._player.Attributes[Stats.BaseVitality],
|
||||
(ushort)this._player.Attributes[Stats.BaseEnergy],
|
||||
(ushort)this._player.Attributes[Stats.BaseLeadership],
|
||||
(uint)this._player.Attributes[Stats.CurrentHealth],
|
||||
(uint)this._player.Attributes[Stats.MaximumHealth],
|
||||
(uint)this._player.Attributes[Stats.CurrentMana],
|
||||
(uint)this._player.Attributes[Stats.MaximumMana],
|
||||
(uint)this._player.Attributes[Stats.CurrentShield],
|
||||
(uint)this._player.Attributes[Stats.MaximumShield],
|
||||
(uint)this._player.Attributes[Stats.CurrentAbility],
|
||||
(uint)this._player.Attributes[Stats.MaximumAbility],
|
||||
(uint)this._player.Money,
|
||||
this._player.SelectedCharacter.State.Convert(),
|
||||
this._player.SelectedCharacter.CharacterStatus.Convert(),
|
||||
(ushort)this._player.SelectedCharacter.UsedFruitPoints,
|
||||
this._player.SelectedCharacter.GetMaximumFruitPoints(),
|
||||
(ushort)this._player.SelectedCharacter.UsedNegFruitPoints,
|
||||
this._player.SelectedCharacter.GetMaximumFruitPoints(),
|
||||
(ushort)this._player.Attributes[Stats.AttackSpeed],
|
||||
(ushort)this._player.Attributes[Stats.MagicSpeed],
|
||||
(ushort)maxAttackSpeed,
|
||||
(byte)this._player.SelectedCharacter.InventoryExtensions,
|
||||
(ushort)this._player.Attributes[Stats.Resets])
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (this._player.SelectedCharacter.CharacterClass!.IsMasterClass)
|
||||
{
|
||||
await this._player.InvokeViewPlugInAsync<IUpdateMasterStatsPlugIn>(p => p.SendMasterStatsAsync()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// <copyright file="UpdateCharacterStatsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateCharacterStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateCharacterStatsPlugIn_Name), Description = nameof(PlugInResources.UpdateCharacterStatsPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("6eb967c2-b5a2-4510-9d88-5eccc963a6ea")]
|
||||
[MinimumClient(5, 0, ClientLanguage.Invariant)]
|
||||
public class UpdateCharacterStatsPlugIn : IUpdateCharacterStatsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateCharacterStatsPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateCharacterStatsPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateCharacterStatsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendCharacterInformationAsync(
|
||||
this._player.Position.X,
|
||||
this._player.Position.Y,
|
||||
this._player.SelectedCharacter!.CurrentMap!.Number.ToUnsigned(),
|
||||
(ulong)this._player.SelectedCharacter.Experience,
|
||||
(ulong)this._player.GameServerContext.ExperienceTable[(int)this._player.Attributes![Stats.Level] + 1],
|
||||
(ushort)Math.Max(0, this._player.SelectedCharacter.LevelUpPoints),
|
||||
(ushort)this._player.Attributes[Stats.BaseStrength],
|
||||
(ushort)this._player.Attributes[Stats.BaseAgility],
|
||||
(ushort)this._player.Attributes[Stats.BaseVitality],
|
||||
(ushort)this._player.Attributes[Stats.BaseEnergy],
|
||||
(ushort)this._player.Attributes[Stats.CurrentHealth],
|
||||
(ushort)this._player.Attributes[Stats.MaximumHealth],
|
||||
(ushort)this._player.Attributes[Stats.CurrentMana],
|
||||
(ushort)this._player.Attributes[Stats.MaximumMana],
|
||||
(ushort)this._player.Attributes[Stats.CurrentShield],
|
||||
(ushort)this._player.Attributes[Stats.MaximumShield],
|
||||
(ushort)this._player.Attributes[Stats.CurrentAbility],
|
||||
(ushort)this._player.Attributes[Stats.MaximumAbility],
|
||||
(uint)this._player.Money,
|
||||
this._player.SelectedCharacter.State.Convert(),
|
||||
this._player.SelectedCharacter.CharacterStatus.Convert(),
|
||||
(ushort)this._player.SelectedCharacter.UsedFruitPoints,
|
||||
this._player.SelectedCharacter.GetMaximumFruitPoints(),
|
||||
(ushort)this._player.Attributes[Stats.BaseLeadership],
|
||||
(ushort)this._player.SelectedCharacter.UsedNegFruitPoints,
|
||||
this._player.SelectedCharacter.GetMaximumFruitPoints(),
|
||||
(byte)this._player.SelectedCharacter.InventoryExtensions)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (this._player.SelectedCharacter.CharacterClass!.IsMasterClass)
|
||||
{
|
||||
await this._player.InvokeViewPlugInAsync<IUpdateMasterStatsPlugIn>(p => p.SendMasterStatsAsync()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// <copyright file="UpdateCharacterStatsPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateCharacterStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateCharacterStatsPlugIn075_Name), Description = nameof(PlugInResources.UpdateCharacterStatsPlugIn075_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("C180561D-E055-41CA-817C-44E7A985937C")]
|
||||
[MinimumClient(0, 75, ClientLanguage.Invariant)]
|
||||
public class UpdateCharacterStatsPlugIn075 : IUpdateCharacterStatsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateCharacterStatsPlugIn075"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateCharacterStatsPlugIn075(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateCharacterStatsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendCharacterInformation075Async(
|
||||
this._player.Position.X,
|
||||
this._player.Position.Y,
|
||||
(byte)this._player.SelectedCharacter!.CurrentMap!.Number,
|
||||
(uint)this._player.SelectedCharacter.Experience,
|
||||
(uint)this._player.GameServerContext.ExperienceTable[(int)this._player.Attributes![Stats.Level] + 1],
|
||||
(ushort)Math.Max(this._player.SelectedCharacter.LevelUpPoints, 0),
|
||||
(ushort)this._player.Attributes[Stats.BaseStrength],
|
||||
(ushort)this._player.Attributes[Stats.BaseAgility],
|
||||
(ushort)this._player.Attributes[Stats.BaseVitality],
|
||||
(ushort)this._player.Attributes[Stats.BaseEnergy],
|
||||
(ushort)this._player.Attributes[Stats.CurrentHealth],
|
||||
(ushort)this._player.Attributes[Stats.MaximumHealth],
|
||||
(ushort)this._player.Attributes[Stats.CurrentMana],
|
||||
(ushort)this._player.Attributes[Stats.MaximumMana],
|
||||
(uint)this._player.Money,
|
||||
this._player.SelectedCharacter.State.Convert(),
|
||||
this._player.SelectedCharacter.CharacterStatus.Convert())
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await this._player.InvokeViewPlugInAsync<IApplyKeyConfigurationPlugIn>(p => p.ApplyKeyConfigurationAsync()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// <copyright file="UpdateCharacterStatsPlugIn097.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateCharacterStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateCharacterStatsPlugIn097_Name), Description = nameof(PlugInResources.UpdateCharacterStatsPlugIn097_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("8ACD9D6B-6FA7-42C3-8C07-E137655CB92F")]
|
||||
[MinimumClient(0, 97, ClientLanguage.Invariant)]
|
||||
public class UpdateCharacterStatsPlugIn097 : IUpdateCharacterStatsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateCharacterStatsPlugIn097"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateCharacterStatsPlugIn097(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateCharacterStatsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null || this._player.Account is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendCharacterInformation097Async(
|
||||
this._player.Position.X,
|
||||
this._player.Position.Y,
|
||||
(byte)this._player.SelectedCharacter!.CurrentMap!.Number,
|
||||
this._player.Rotation.ToPacketByte(),
|
||||
(uint)this._player.SelectedCharacter.Experience,
|
||||
(uint)this._player.GameServerContext.ExperienceTable[(int)this._player.Attributes![Stats.Level] + 1],
|
||||
(ushort)Math.Max(this._player.SelectedCharacter.LevelUpPoints, 0),
|
||||
(ushort)this._player.Attributes[Stats.BaseStrength],
|
||||
(ushort)this._player.Attributes[Stats.BaseAgility],
|
||||
(ushort)this._player.Attributes[Stats.BaseVitality],
|
||||
(ushort)this._player.Attributes[Stats.BaseEnergy],
|
||||
(ushort)this._player.Attributes[Stats.CurrentHealth],
|
||||
(ushort)this._player.Attributes[Stats.MaximumHealth],
|
||||
(ushort)this._player.Attributes[Stats.CurrentMana],
|
||||
(ushort)this._player.Attributes[Stats.MaximumMana],
|
||||
(ushort)this._player.Attributes[Stats.CurrentAbility],
|
||||
(ushort)this._player.Attributes[Stats.MaximumAbility],
|
||||
(uint)this._player.Money,
|
||||
this._player.SelectedCharacter.State.Convert(),
|
||||
this._player.SelectedCharacter.CharacterStatus.Convert(),
|
||||
(ushort)this._player.SelectedCharacter.UsedFruitPoints,
|
||||
this._player.SelectedCharacter.GetMaximumFruitPoints(),
|
||||
(ushort)this._player.Attributes[Stats.BaseLeadership])
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await this._player.InvokeViewPlugInAsync<IApplyKeyConfigurationPlugIn>(p => p.ApplyKeyConfigurationAsync()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// <copyright file="UpdateLevelExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Properties;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
using PlugInResources = MUnique.OpenMU.GameServer.Properties.PlugInResources;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IUpdateLevelPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateLevelExtendedPlugIn_Name), Description = nameof(PlugInResources.UpdateLevelExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("6F358202-6678-4417-9537-D8739AEF78C2")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class UpdateLevelExtendedPlugIn : IUpdateLevelPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateLevelExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateLevelExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateLevelAsync()
|
||||
{
|
||||
var selectedCharacter = this._player.SelectedCharacter;
|
||||
var charStats = this._player.Attributes;
|
||||
var connection = this._player.Connection;
|
||||
if (selectedCharacter is null || charStats is null || connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendCharacterLevelUpdateExtendedAsync(
|
||||
(ushort)charStats[Stats.Level],
|
||||
(ushort)Math.Max(selectedCharacter.LevelUpPoints, 0),
|
||||
(uint)charStats[Stats.MaximumHealth],
|
||||
(uint)charStats[Stats.MaximumMana],
|
||||
(uint)charStats[Stats.MaximumShield],
|
||||
(uint)charStats[Stats.MaximumAbility],
|
||||
(ushort)selectedCharacter.UsedFruitPoints,
|
||||
selectedCharacter.GetMaximumFruitPoints(),
|
||||
(ushort)selectedCharacter.UsedNegFruitPoints,
|
||||
selectedCharacter.GetMaximumFruitPoints()).ConfigureAwait(false);
|
||||
|
||||
await this._player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.LevelUpCongrats), charStats[Stats.Level]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateMasterLevelAsync()
|
||||
{
|
||||
var selectedCharacter = this._player.SelectedCharacter;
|
||||
var charStats = this._player.Attributes;
|
||||
var connection = this._player.Connection;
|
||||
if (selectedCharacter is null || charStats is null || connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendMasterCharacterLevelUpdateExtendedAsync(
|
||||
(ushort)charStats[Stats.MasterLevel],
|
||||
(ushort)charStats[Stats.MasterPointsPerLevelUp],
|
||||
(ushort)selectedCharacter.MasterLevelUpPoints,
|
||||
(ushort)this._player.GameContext.Configuration.MaximumMasterLevel,
|
||||
(uint)charStats[Stats.MaximumHealth],
|
||||
(uint)charStats[Stats.MaximumMana],
|
||||
(uint)charStats[Stats.MaximumShield],
|
||||
(uint)charStats[Stats.MaximumAbility]).ConfigureAwait(false);
|
||||
|
||||
await this._player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.MasterLevelUpCongrats), charStats[Stats.MasterLevel]).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
81
src/GameServer/RemoteView/Character/UpdateLevelPlugIn.cs
Normal file
81
src/GameServer/RemoteView/Character/UpdateLevelPlugIn.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
// <copyright file="UpdateLevelPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Properties;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
using PlugInResources = MUnique.OpenMU.GameServer.Properties.PlugInResources;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateLevelPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateLevelPlugIn_Name), Description = nameof(PlugInResources.UpdateLevelPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("1ff3709e-d99b-4c00-b926-efce281b3997")]
|
||||
public class UpdateLevelPlugIn : IUpdateLevelPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateLevelPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateLevelPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateLevelAsync()
|
||||
{
|
||||
var selectedCharacter = this._player.SelectedCharacter;
|
||||
var charStats = this._player.Attributes;
|
||||
var connection = this._player.Connection;
|
||||
if (selectedCharacter is null || charStats is null || connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendCharacterLevelUpdateAsync(
|
||||
(ushort)charStats[Stats.Level],
|
||||
(ushort)Math.Max(selectedCharacter.LevelUpPoints, 0),
|
||||
(ushort)charStats[Stats.MaximumHealth],
|
||||
(ushort)charStats[Stats.MaximumMana],
|
||||
(ushort)charStats[Stats.MaximumShield],
|
||||
(ushort)charStats[Stats.MaximumAbility],
|
||||
(ushort)selectedCharacter.UsedFruitPoints,
|
||||
selectedCharacter.GetMaximumFruitPoints(),
|
||||
(ushort)selectedCharacter.UsedNegFruitPoints,
|
||||
selectedCharacter.GetMaximumFruitPoints()).ConfigureAwait(false);
|
||||
|
||||
await this._player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.LevelUpCongrats), charStats[Stats.Level]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateMasterLevelAsync()
|
||||
{
|
||||
var selectedCharacter = this._player.SelectedCharacter;
|
||||
var charStats = this._player.Attributes;
|
||||
var connection = this._player.Connection;
|
||||
if (selectedCharacter is null || charStats is null || connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendMasterCharacterLevelUpdateAsync(
|
||||
(ushort)charStats[Stats.MasterLevel],
|
||||
(ushort)charStats[Stats.MasterPointsPerLevelUp],
|
||||
(ushort)selectedCharacter.MasterLevelUpPoints,
|
||||
(ushort)this._player.GameContext.Configuration.MaximumMasterLevel,
|
||||
(ushort)charStats[Stats.MaximumHealth],
|
||||
(ushort)charStats[Stats.MaximumMana],
|
||||
(ushort)charStats[Stats.MaximumShield],
|
||||
(ushort)charStats[Stats.MaximumAbility]).ConfigureAwait(false);
|
||||
|
||||
await this._player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.MasterLevelUpCongrats), charStats[Stats.MasterLevel]).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// <copyright file="UpdateMasterSkillsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateMasterSkillsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateMasterSkillsPlugIn_Name), Description = nameof(PlugInResources.UpdateMasterSkillsPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("72942fe8-925d-43b0-a908-b814b2baa1f3")]
|
||||
public class UpdateMasterSkillsPlugIn : IUpdateMasterSkillsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateMasterSkillsPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateMasterSkillsPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask UpdateMasterSkillsAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var masterSkills = this._player.SkillList?.Skills.Where(s => s.Skill?.MasterDefinition != null).ToList();
|
||||
if (masterSkills is null || this._player.SelectedCharacter?.CharacterClass is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = MasterSkillListRef.GetRequiredSize(masterSkills.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new MasterSkillListRef(span)
|
||||
{
|
||||
MasterSkillCount = (uint)masterSkills.Count,
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
foreach (var masterSkill in masterSkills)
|
||||
{
|
||||
var skillsBlock = packet[i];
|
||||
skillsBlock.MasterSkillIndex = masterSkill.Skill!.GetMasterSkillIndex(this._player.SelectedCharacter.CharacterClass);
|
||||
skillsBlock.Level = (byte)masterSkill.Level;
|
||||
skillsBlock.DisplayValue = masterSkill.CalculateDisplayValue();
|
||||
skillsBlock.DisplayValueOfNextLevel = masterSkill.CalculateNextDisplayValue();
|
||||
i++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// <copyright file="UpdateMasterStatsExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IUpdateMasterStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateMasterStatsExtendedPlugIn_Name), Description = nameof(PlugInResources.UpdateMasterStatsExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("EF19E11B-AE26-44F0-AB3E-5ADD5CDEBC56")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class UpdateMasterStatsExtendedPlugIn : IUpdateMasterStatsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateMasterStatsExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateMasterStatsExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask SendMasterStatsAsync()
|
||||
{
|
||||
var character = this._player.SelectedCharacter;
|
||||
var connection = this._player.Connection;
|
||||
if (character is null || this._player.Attributes is null || connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendMasterStatsUpdateExtendedAsync(
|
||||
(ushort)this._player.Attributes[Stats.MasterLevel],
|
||||
(ulong)character.MasterExperience,
|
||||
(ulong)this._player.GameServerContext.MasterExperienceTable[(int)this._player.Attributes[Stats.MasterLevel] + 1],
|
||||
(ushort)character.MasterLevelUpPoints,
|
||||
(ushort)this._player.Attributes[Stats.MaximumHealth],
|
||||
(ushort)this._player.Attributes[Stats.MaximumMana],
|
||||
(ushort)this._player.Attributes[Stats.MaximumShield],
|
||||
(ushort)this._player.Attributes[Stats.MaximumAbility]).ConfigureAwait(false);
|
||||
|
||||
await this._player.InvokeViewPlugInAsync<IUpdateMasterSkillsPlugIn>(p => p.UpdateMasterSkillsAsync()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// <copyright file="UpdateMasterStatsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateMasterStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateMasterStatsPlugIn_Name), Description = nameof(PlugInResources.UpdateMasterStatsPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("41b27ec2-5bc6-4acf-b395-ddf9e81a3611")]
|
||||
public class UpdateMasterStatsPlugIn : IUpdateMasterStatsPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateMasterStatsPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateMasterStatsPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask SendMasterStatsAsync()
|
||||
{
|
||||
var character = this._player.SelectedCharacter;
|
||||
var connection = this._player.Connection;
|
||||
if (character is null || this._player.Attributes is null || connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendMasterStatsUpdateAsync(
|
||||
(ushort)this._player.Attributes[Stats.MasterLevel],
|
||||
(ulong)character.MasterExperience,
|
||||
(ulong)this._player.GameServerContext.MasterExperienceTable[(int)this._player.Attributes[Stats.MasterLevel] + 1],
|
||||
(ushort)character.MasterLevelUpPoints,
|
||||
(ushort)this._player.Attributes[Stats.MaximumHealth],
|
||||
(ushort)this._player.Attributes[Stats.MaximumMana],
|
||||
(ushort)this._player.Attributes[Stats.MaximumShield],
|
||||
(ushort)this._player.Attributes[Stats.MaximumAbility]).ConfigureAwait(false);
|
||||
|
||||
await this._player.InvokeViewPlugInAsync<IUpdateMasterSkillsPlugIn>(p => p.UpdateMasterSkillsAsync()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
106
src/GameServer/RemoteView/Character/UpdateStatsBasePlugIn.cs
Normal file
106
src/GameServer/RemoteView/Character/UpdateStatsBasePlugIn.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
// <copyright file="UpdateStatsBasePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Frozen;
|
||||
using System.Threading;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using UpdateAction = System.Func<MUnique.OpenMU.GameServer.RemoteView.RemotePlayer, System.Threading.Tasks.ValueTask>;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
public abstract class UpdateStatsBasePlugIn : Disposable, IUpdateStatsPlugIn
|
||||
{
|
||||
private static readonly int SendDelayMs = 16;
|
||||
|
||||
private static readonly ConcurrentDictionary<
|
||||
FrozenDictionary<AttributeDefinition, UpdateAction>,
|
||||
FrozenDictionary<UpdateAction, int>> ActionIndexMappings = new();
|
||||
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
private readonly FrozenDictionary<UpdateAction, int> _actionIndexMapping;
|
||||
|
||||
private readonly FrozenDictionary<AttributeDefinition, UpdateAction> _changeActions;
|
||||
|
||||
private readonly AutoResetEvent[] _resetEvents;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateStatsBasePlugIn" /> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
/// <param name="changeActions">The change actions.</param>
|
||||
protected UpdateStatsBasePlugIn(RemotePlayer player, FrozenDictionary<AttributeDefinition, UpdateAction> changeActions)
|
||||
{
|
||||
this._player = player;
|
||||
this._changeActions = changeActions;
|
||||
this._actionIndexMapping = GetActionIndexMapping(changeActions);
|
||||
this._resetEvents = new AutoResetEvent[changeActions.Count];
|
||||
for (int i = 0; i < this._resetEvents.Length; i++)
|
||||
{
|
||||
this._resetEvents[i] = new AutoResetEvent(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask UpdateStatsAsync(AttributeDefinition attribute, float value)
|
||||
{
|
||||
if (this._player.Attributes is null
|
||||
|| !(this._player.Connection?.Connected ?? false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._changeActions.TryGetValue(attribute, out var action))
|
||||
{
|
||||
_ = this.SendDelayedUpdateAsync(action);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
foreach (var are in this._resetEvents)
|
||||
{
|
||||
are.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private static FrozenDictionary<UpdateAction, int> GetActionIndexMapping(FrozenDictionary<AttributeDefinition, UpdateAction> changeActions)
|
||||
{
|
||||
return ActionIndexMappings.GetOrAdd(changeActions, CreateNewIndexDictionary);
|
||||
|
||||
FrozenDictionary<UpdateAction, int> CreateNewIndexDictionary(FrozenDictionary<AttributeDefinition, UpdateAction> dict)
|
||||
{
|
||||
return dict.Values.Distinct().Index().ToFrozenDictionary(tuple => tuple.Item, tuple => tuple.Index);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask SendDelayedUpdateAsync(UpdateAction action)
|
||||
{
|
||||
var autoResetEvent = this._resetEvents[this._actionIndexMapping[action]];
|
||||
if (!autoResetEvent.WaitOne(0))
|
||||
{
|
||||
// We're sending an update already.
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(SendDelayMs).ConfigureAwait(false);
|
||||
await action(this._player).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
autoResetEvent.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// <copyright file="UpdateStatsExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Collections.Frozen;
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IUpdateStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateStatsExtendedPlugIn_Name), Description = nameof(PlugInResources.UpdateStatsExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("E9A1CCBE-416F-41BA-8E74-74CBEB7042DD")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class UpdateStatsExtendedPlugIn : UpdateStatsBasePlugIn
|
||||
{
|
||||
private static readonly FrozenDictionary<AttributeDefinition, Func<RemotePlayer, ValueTask>> AttributeChangeActions = new Dictionary<AttributeDefinition, Func<RemotePlayer, ValueTask>>
|
||||
{
|
||||
{ Stats.MaximumHealth, OnMaximumStatsChangedAsync },
|
||||
{ Stats.MaximumShield, OnMaximumStatsChangedAsync },
|
||||
{ Stats.MaximumMana, OnMaximumStatsChangedAsync },
|
||||
{ Stats.MaximumAbility, OnMaximumStatsChangedAsync },
|
||||
{ Stats.CurrentHealth, OnCurrentStatsChangedAsync },
|
||||
{ Stats.CurrentShield, OnCurrentStatsChangedAsync },
|
||||
{ Stats.CurrentMana, OnCurrentStatsChangedAsync },
|
||||
{ Stats.CurrentAbility, OnCurrentStatsChangedAsync },
|
||||
{ Stats.AttackSpeed, OnCurrentStatsChangedAsync },
|
||||
{ Stats.MagicSpeed, OnCurrentStatsChangedAsync },
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateStatsExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateStatsExtendedPlugIn(RemotePlayer player)
|
||||
: base(player, AttributeChangeActions)
|
||||
{
|
||||
}
|
||||
|
||||
private static async ValueTask OnMaximumStatsChangedAsync(RemotePlayer player)
|
||||
{
|
||||
await player.Connection.SendMaximumStatsExtendedAsync(
|
||||
(uint)player.Attributes![Stats.MaximumHealth],
|
||||
(uint)player.Attributes[Stats.MaximumShield],
|
||||
(uint)player.Attributes[Stats.MaximumMana],
|
||||
(uint)player.Attributes[Stats.MaximumAbility]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async ValueTask OnCurrentStatsChangedAsync(RemotePlayer player)
|
||||
{
|
||||
await player.Connection.SendCurrentStatsExtendedAsync(
|
||||
(uint)player.Attributes![Stats.CurrentHealth],
|
||||
(uint)player.Attributes[Stats.CurrentShield],
|
||||
(uint)player.Attributes[Stats.CurrentMana],
|
||||
(uint)player.Attributes[Stats.CurrentAbility],
|
||||
(ushort)player.Attributes[Stats.AttackSpeed],
|
||||
(ushort)player.Attributes[Stats.MagicSpeed]).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
71
src/GameServer/RemoteView/Character/UpdateStatsPlugIn.cs
Normal file
71
src/GameServer/RemoteView/Character/UpdateStatsPlugIn.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
// <copyright file="UpdateStatsPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Character;
|
||||
|
||||
using System.Collections.Frozen;
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Character;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateStatsPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateStatsPlugIn_Name), Description = nameof(PlugInResources.UpdateStatsPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("2A8BFB0C-2AFF-4A52-B390-5A68D5C5F26A")]
|
||||
public class UpdateStatsPlugIn : UpdateStatsBasePlugIn
|
||||
{
|
||||
private static readonly FrozenDictionary<AttributeDefinition, Func<RemotePlayer, ValueTask>> AttributeChangeActions = new Dictionary<AttributeDefinition, Func<RemotePlayer, ValueTask>>
|
||||
{
|
||||
{ Stats.CurrentHealth, OnCurrentHealthOrShieldChangedAsync },
|
||||
{ Stats.CurrentShield, OnCurrentHealthOrShieldChangedAsync },
|
||||
{ Stats.MaximumHealth, OnMaximumHealthOrShieldChangedAsync },
|
||||
{ Stats.MaximumShield, OnMaximumHealthOrShieldChangedAsync },
|
||||
{ Stats.CurrentMana, OnCurrentManaOrAbilityChangedAsync },
|
||||
{ Stats.CurrentAbility, OnCurrentManaOrAbilityChangedAsync },
|
||||
{ Stats.MaximumMana, OnMaximumManaOrAbilityChangedAsync },
|
||||
{ Stats.MaximumAbility, OnMaximumManaOrAbilityChangedAsync },
|
||||
}.ToFrozenDictionary();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateStatsPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateStatsPlugIn(RemotePlayer player)
|
||||
: base(player, AttributeChangeActions)
|
||||
{
|
||||
}
|
||||
|
||||
private static async ValueTask OnMaximumHealthOrShieldChangedAsync(RemotePlayer player)
|
||||
{
|
||||
await player.Connection.SendMaximumHealthAndShieldAsync(
|
||||
(ushort)Math.Max(player.Attributes![Stats.MaximumHealth], 0f),
|
||||
(ushort)Math.Max(player.Attributes[Stats.MaximumShield], 0f)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async ValueTask OnMaximumManaOrAbilityChangedAsync(RemotePlayer player)
|
||||
{
|
||||
await player.Connection.SendMaximumManaAndAbilityAsync(
|
||||
(ushort)Math.Max(player.Attributes![Stats.MaximumMana], 0f),
|
||||
(ushort)Math.Max(player.Attributes[Stats.MaximumAbility], 0f)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async ValueTask OnCurrentHealthOrShieldChangedAsync(RemotePlayer player)
|
||||
{
|
||||
await player.Connection.SendCurrentHealthAndShieldAsync(
|
||||
(ushort)Math.Max(player.Attributes![Stats.CurrentHealth], 0f),
|
||||
(ushort)Math.Max(player.Attributes[Stats.CurrentShield], 0f)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async ValueTask OnCurrentManaOrAbilityChangedAsync(RemotePlayer player)
|
||||
{
|
||||
await player.Connection.SendCurrentManaAndAbilityAsync(
|
||||
(ushort)Math.Max(player.Attributes![Stats.CurrentMana], 0f),
|
||||
(ushort)Math.Max(player.Attributes[Stats.CurrentAbility], 0f)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
53
src/GameServer/RemoteView/CharacterStatTypeExtensions.cs
Normal file
53
src/GameServer/RemoteView/CharacterStatTypeExtensions.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// <copyright file="CharacterStatTypeExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using MUnique.OpenMU.AttributeSystem;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="CharacterStatAttribute"/>.
|
||||
/// </summary>
|
||||
public static class CharacterStatTypeExtensions
|
||||
{
|
||||
private static readonly Dictionary<AttributeDefinition, CharacterStatAttribute> AttributesToStatTypes = new()
|
||||
{
|
||||
{ Stats.BaseAgility, CharacterStatAttribute.Agility },
|
||||
{ Stats.BaseEnergy, CharacterStatAttribute.Energy },
|
||||
{ Stats.BaseStrength, CharacterStatAttribute.Strength },
|
||||
{ Stats.BaseVitality, CharacterStatAttribute.Vitality },
|
||||
{ Stats.BaseLeadership, CharacterStatAttribute.Leadership },
|
||||
};
|
||||
|
||||
private static readonly Dictionary<CharacterStatAttribute, AttributeDefinition> StatTypesToAttributes = new()
|
||||
{
|
||||
{ CharacterStatAttribute.Agility, Stats.BaseAgility },
|
||||
{ CharacterStatAttribute.Energy, Stats.BaseEnergy },
|
||||
{ CharacterStatAttribute.Strength, Stats.BaseStrength },
|
||||
{ CharacterStatAttribute.Vitality, Stats.BaseVitality },
|
||||
{ CharacterStatAttribute.Leadership, Stats.BaseLeadership },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute definition of a <see cref="CharacterStatAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="statType">Type of the stat.</param>
|
||||
/// <returns>The corresponding <see cref="AttributeDefinition"/>.</returns>
|
||||
public static AttributeDefinition GetAttributeDefinition(this CharacterStatAttribute statType)
|
||||
{
|
||||
return StatTypesToAttributes[statType];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CharacterStatAttribute"/> of the specified <see cref="AttributeDefinition"/>.
|
||||
/// </summary>
|
||||
/// <param name="attributeDefinition">The attribute definition.</param>
|
||||
/// <returns>The corresponding <see cref="CharacterStatAttribute"/>.</returns>
|
||||
public static CharacterStatAttribute GetStatType(this AttributeDefinition attributeDefinition)
|
||||
{
|
||||
return AttributesToStatTypes[attributeDefinition];
|
||||
}
|
||||
}
|
||||
46
src/GameServer/RemoteView/ChatViewPlugIn.cs
Normal file
46
src/GameServer/RemoteView/ChatViewPlugIn.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
// <copyright file="ChatViewPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the chat view which is forwarding everything to the game client which specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ChatViewPlugIn_Name), Description = nameof(PlugInResources.ChatViewPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("F0B5BAD4-B97C-49F1-84E0-25EDC796B0E4")]
|
||||
public class ChatViewPlugIn : IChatViewPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ChatViewPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ChatViewPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ChatMessageAsync(string message, string sender, ChatMessageType type)
|
||||
{
|
||||
await this._player.Connection.SendChatMessageAsync(ConvertChatMessageType(type), sender, message).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static ChatMessage.ChatMessageType ConvertChatMessageType(ChatMessageType type)
|
||||
{
|
||||
if (type == ChatMessageType.Whisper)
|
||||
{
|
||||
return Network.Packets.ServerToClient.ChatMessage.ChatMessageType.Whisper;
|
||||
}
|
||||
|
||||
return Network.Packets.ServerToClient.ChatMessage.ChatMessageType.Normal;
|
||||
}
|
||||
}
|
||||
52
src/GameServer/RemoteView/ConsumeSpecialItemPlugIn.cs
Normal file
52
src/GameServer/RemoteView/ConsumeSpecialItemPlugIn.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// <copyright file="ConsumeSpecialItemPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IConsumeSpecialItemPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ConsumeSpecialItemPlugIn_Name), Description = nameof(PlugInResources.ConsumeSpecialItemPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("a31546d8-bf79-43dd-872c-52f24ea9bca9")]
|
||||
public class ConsumeSpecialItemPlugIn : IConsumeSpecialItemPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsumeSpecialItemPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ConsumeSpecialItemPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ConsumeSpecialItemAsync(Item item, ushort effectTimeInSeconds)
|
||||
{
|
||||
if (item.Definition is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var itemIdentifier = new ItemIdentifier(item.Definition.Number, item.Definition.Group);
|
||||
if (itemIdentifier == ItemConstants.Alcohol)
|
||||
{
|
||||
await this._player.Connection.SendConsumeItemWithEffectAsync(ConsumeItemWithEffect.ConsumedItemType.Ale, effectTimeInSeconds).ConfigureAwait(false);
|
||||
}
|
||||
else if (itemIdentifier == ItemConstants.SiegePotion && item.Level == 1)
|
||||
{
|
||||
await this._player.Connection.SendConsumeItemWithEffectAsync(ConsumeItemWithEffect.ConsumedItemType.PotionOfSoul, effectTimeInSeconds).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/GameServer/RemoteView/Duel/DuelEndedPlugIn.cs
Normal file
38
src/GameServer/RemoteView/Duel/DuelEndedPlugIn.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
// <copyright file="DuelEndedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.GameServer.RemoteView.World;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IDuelEndedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.DuelEndedPlugIn_Name), Description = nameof(PlugInResources.DuelEndedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("4FBC822B-F35B-4CB1-AFE6-180243171074")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class DuelEndedPlugIn : IDuelEndedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DuelEndedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public DuelEndedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask DuelEndedAsync()
|
||||
{
|
||||
await this._player.Connection.SendDuelEndAsync(0, string.Empty).ConfigureAwait(false);
|
||||
await this._player.Connection.SendMagicEffectStatusAsync(false, this._player.GetId(this._player), EffectNumbers.DuelSpectatorHealthBar).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
36
src/GameServer/RemoteView/Duel/DuelFinishedPlugIn.cs
Normal file
36
src/GameServer/RemoteView/Duel/DuelFinishedPlugIn.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// <copyright file="DuelFinishedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IDuelFinishedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.DuelFinishedPlugIn_Name), Description = nameof(PlugInResources.DuelFinishedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("0DA9F6D1-3DC2-4A75-BA9C-BB77C2A7EB62")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class DuelFinishedPlugIn : IDuelFinishedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DuelFinishedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public DuelFinishedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask DuelFinishedAsync(Player winner, Player loser)
|
||||
{
|
||||
await this._player.Connection.SendDuelFinishedAsync(winner.Name, loser.Name).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
53
src/GameServer/RemoteView/Duel/DuelHealthUpdatePlugIn.cs
Normal file
53
src/GameServer/RemoteView/Duel/DuelHealthUpdatePlugIn.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// <copyright file="DuelHealthUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IDuelHealthUpdatePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.DuelHealthUpdatePlugIn_Name), Description = nameof(PlugInResources.DuelHealthUpdatePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("40CE7F73-F9DF-4F4E-BBCE-04938604A72C")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class DuelHealthUpdatePlugIn : IDuelHealthUpdatePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DuelHealthUpdatePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public DuelHealthUpdatePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask UpdateHealthAsync(DuelRoom duelRoom)
|
||||
{
|
||||
var player1 = this._player == duelRoom.Opponent ? duelRoom.Opponent : duelRoom.Requester;
|
||||
var player2 = this._player == duelRoom.Opponent ? duelRoom.Requester : duelRoom.Opponent;
|
||||
|
||||
var player1Health = player1.Attributes![Stats.CurrentHealth] / (player1.Attributes[Stats.MaximumHealth] / 100f);
|
||||
var player1Shield = player1.Attributes[Stats.CurrentShield] / (player1.Attributes[Stats.MaximumShield] / 100f);
|
||||
var player2Health = player2.Attributes![Stats.CurrentHealth] / (player1.Attributes[Stats.MaximumHealth] / 100f);
|
||||
var player2Shield = player2.Attributes[Stats.CurrentShield] / (player1.Attributes[Stats.MaximumShield] / 100f);
|
||||
|
||||
await this._player.Connection.SendDuelHealthUpdateAsync(
|
||||
player1.GetId(this._player),
|
||||
player2.GetId(this._player),
|
||||
(byte)player1Health,
|
||||
(byte)player2Health,
|
||||
(byte)player1Shield,
|
||||
(byte)player2Shield)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
36
src/GameServer/RemoteView/Duel/DuelSpectatorAddedPlugIn.cs
Normal file
36
src/GameServer/RemoteView/Duel/DuelSpectatorAddedPlugIn.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// <copyright file="DuelSpectatorAddedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IDuelSpectatorAddedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.DuelSpectatorAddedPlugIn_Name), Description = nameof(PlugInResources.DuelSpectatorAddedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("BB91E97F-E1F9-4152-9AA1-573D917ACD35")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class DuelSpectatorAddedPlugIn : IDuelSpectatorAddedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DuelSpectatorAddedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public DuelSpectatorAddedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask SpectatorAddedAsync(Player spectator)
|
||||
{
|
||||
await this._player.Connection.SendDuelSpectatorAddedAsync(spectator.Name).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// <copyright file="DuelSpectatorListUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IDuelSpectatorListUpdatePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.DuelSpectatorListUpdatePlugIn_Name), Description = nameof(PlugInResources.DuelSpectatorListUpdatePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("1A8EC472-6924-4150-9B4C-4352AFF03AC0")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class DuelSpectatorListUpdatePlugIn : IDuelSpectatorListUpdatePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DuelSpectatorListUpdatePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public DuelSpectatorListUpdatePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask UpdateSpectatorListAsync(IList<Player> spectators)
|
||||
{
|
||||
if (this._player.Connection is not { } connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int WritePacket()
|
||||
{
|
||||
var length = DuelSpectatorListRef.Length;
|
||||
var packet = new DuelSpectatorListRef(connection.Output.GetSpan(length)[..length]);
|
||||
for (int i = 0; i < spectators.Count; i++)
|
||||
{
|
||||
var spectator = spectators[i];
|
||||
var spectatorStruct = packet[i];
|
||||
spectatorStruct.Name = spectator.Name;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
await connection.SendAsync(WritePacket).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
36
src/GameServer/RemoteView/Duel/DuelSpectatorRemovedPlugIn.cs
Normal file
36
src/GameServer/RemoteView/Duel/DuelSpectatorRemovedPlugIn.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// <copyright file="DuelSpectatorRemovedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IDuelSpectatorRemovedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.DuelSpectatorRemovedPlugIn_Name), Description = nameof(PlugInResources.DuelSpectatorRemovedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("89DFA45D-FED2-4FAA-BDB0-683EB337629B")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class DuelSpectatorRemovedPlugIn : IDuelSpectatorRemovedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DuelSpectatorRemovedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public DuelSpectatorRemovedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask SpectatorRemovedAsync(Player spectator)
|
||||
{
|
||||
await this._player.Connection.SendDuelSpectatorRemovedAsync(spectator.Name).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
62
src/GameServer/RemoteView/Duel/DuelStatusUpdatePlugIn.cs
Normal file
62
src/GameServer/RemoteView/Duel/DuelStatusUpdatePlugIn.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// <copyright file="DuelStatusUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IDuelStatusUpdatePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.DuelStatusUpdatePlugIn_Name), Description = nameof(PlugInResources.DuelStatusUpdatePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("9C37DF93-7E1C-44BB-914D-DB8B3F96FEE0")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class DuelStatusUpdatePlugIn : IDuelStatusUpdatePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DuelStatusUpdatePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public DuelStatusUpdatePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask UpdateStatusAsync(DuelRoom?[] duelRooms)
|
||||
{
|
||||
if (this._player.Connection is not { } connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int WritePacket()
|
||||
{
|
||||
var length = DuelStatusRef.Length;
|
||||
var packet = new DuelStatusRef(connection.Output.GetSpan(length)[..length]);
|
||||
for (int i = 0; i < duelRooms.Length; i++)
|
||||
{
|
||||
var duelRoom = duelRooms[i];
|
||||
var duelStatus = packet[i];
|
||||
if (duelRoom is not null)
|
||||
{
|
||||
duelStatus.Player1Name = duelRoom.Requester.Name;
|
||||
duelStatus.Player2Name = duelRoom.Opponent.Name;
|
||||
duelStatus.DuelRunning = duelRoom.State == DuelState.DuelStarted;
|
||||
duelStatus.DuelOpen = duelRoom.IsOpen;
|
||||
}
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
await connection.SendAsync(WritePacket).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
52
src/GameServer/RemoteView/Duel/InitializeDuelPlugIn.cs
Normal file
52
src/GameServer/RemoteView/Duel/InitializeDuelPlugIn.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// <copyright file="InitializeDuelPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.GameServer.RemoteView.World;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IInitializeDuelPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.InitializeDuelPlugIn_Name), Description = nameof(PlugInResources.InitializeDuelPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("41ECFA38-3EAE-4408-B7AC-82F26D8DCCD7")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class InitializeDuelPlugIn : IInitializeDuelPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InitializeDuelPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public InitializeDuelPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask InitializeDuelAsync(DuelRoom duelRoom)
|
||||
{
|
||||
var player1 = this._player == duelRoom.Opponent ? duelRoom.Opponent : duelRoom.Requester;
|
||||
var player2 = this._player == duelRoom.Opponent ? duelRoom.Requester : duelRoom.Opponent;
|
||||
await this._player.Connection.SendDuelInitAsync(
|
||||
0,
|
||||
(byte)duelRoom.Index,
|
||||
player1.Name,
|
||||
player2.Name,
|
||||
player1.GetId(this._player),
|
||||
player2.GetId(this._player))
|
||||
.ConfigureAwait(false);
|
||||
if (!duelRoom.IsDuelist(this._player))
|
||||
{
|
||||
await this._player.Connection.SendDuelHealthBarInitAsync().ConfigureAwait(false);
|
||||
await this._player.Connection.SendMagicEffectStatusAsync(true, this._player.GetId(this._player), EffectNumbers.DuelSpectatorHealthBar).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/GameServer/RemoteView/Duel/ShowDuelRequestPlugIn.cs
Normal file
37
src/GameServer/RemoteView/Duel/ShowDuelRequestPlugIn.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
// <copyright file="ShowDuelRequestPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowDuelRequestPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowDuelRequestPlugIn_Name), Description = nameof(PlugInResources.ShowDuelRequestPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("708847EE-B64F-42A3-BDAC-C4FD2417B6A5")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class ShowDuelRequestPlugIn : IShowDuelRequestPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowDuelRequestPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowDuelRequestPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowDuelRequestAsync(Player requester)
|
||||
{
|
||||
await this._player.Connection.SendDuelStartRequestAsync(requester.GetId(this._player), requester.Name).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// <copyright file="ShowDuelRequestResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowDuelRequestResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowDuelRequestResultPlugIn_Name), Description = nameof(PlugInResources.ShowDuelRequestResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("3A389377-71BC-4EEB-922E-00B343DF1893")]
|
||||
public class ShowDuelRequestResultPlugIn : IShowDuelRequestResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowDuelRequestResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowDuelRequestResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowDuelRequestResultAsync(GameLogic.Views.Duel.DuelStartResult result, Player opponent)
|
||||
{
|
||||
await this._player.Connection.SendDuelStartResultAsync(Convert(result), opponent.GetId(this._player), opponent.Name).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static Network.Packets.ServerToClient.DuelStartResult.DuelStartResultType Convert(GameLogic.Views.Duel.DuelStartResult result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
GameLogic.Views.Duel.DuelStartResult.Success => Network.Packets.ServerToClient.DuelStartResult.DuelStartResultType.Success,
|
||||
GameLogic.Views.Duel.DuelStartResult.Refused => Network.Packets.ServerToClient.DuelStartResult.DuelStartResultType.Refused,
|
||||
GameLogic.Views.Duel.DuelStartResult.FailedByError => Network.Packets.ServerToClient.DuelStartResult.DuelStartResultType.FailedByError,
|
||||
GameLogic.Views.Duel.DuelStartResult.FailedByNoFreeRoom => Network.Packets.ServerToClient.DuelStartResult.DuelStartResultType.FailedByNoFreeRoom,
|
||||
GameLogic.Views.Duel.DuelStartResult.FailedByNotEnoughMoney => Network.Packets.ServerToClient.DuelStartResult.DuelStartResultType.FailedByNotEnoughMoney,
|
||||
GameLogic.Views.Duel.DuelStartResult.FailedByTooLowLevel => Network.Packets.ServerToClient.DuelStartResult.DuelStartResultType.FailedByTooLowLevel,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(result)),
|
||||
};
|
||||
}
|
||||
}
|
||||
44
src/GameServer/RemoteView/Duel/ShowDuelScoreUpdatePlugIn.cs
Normal file
44
src/GameServer/RemoteView/Duel/ShowDuelScoreUpdatePlugIn.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// <copyright file="ShowDuelScoreUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Duel;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Duel;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowDuelScoreUpdatePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowDuelScoreUpdatePlugIn_Name), Description = nameof(PlugInResources.ShowDuelScoreUpdatePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("328E366D-B801-4780-B65D-B250C388E6B0")]
|
||||
[MinimumClient(4, 0, ClientLanguage.Invariant)]
|
||||
public class ShowDuelScoreUpdatePlugIn : IShowDuelScoreUpdatePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowDuelScoreUpdatePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowDuelScoreUpdatePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask UpdateScoreAsync(DuelRoom duelRoom)
|
||||
{
|
||||
if (this._player == duelRoom.Opponent)
|
||||
{
|
||||
await this._player.Connection.SendDuelScoreAsync(duelRoom.Opponent.GetId(this._player), duelRoom.Requester.GetId(this._player), duelRoom.ScoreOpponent, duelRoom.ScoreRequester).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await this._player.Connection.SendDuelScoreAsync(duelRoom.Requester.GetId(this._player), duelRoom.Opponent.GetId(this._player), duelRoom.ScoreRequester, duelRoom.ScoreOpponent).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
src/GameServer/RemoteView/Guild/AssignPlayersToGuildPlugIn.cs
Normal file
111
src/GameServer/RemoteView/Guild/AssignPlayersToGuildPlugIn.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
// <copyright file="AssignPlayersToGuildPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IAssignPlayersToGuildPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.AssignPlayersToGuildPlugIn_Name), Description = nameof(PlugInResources.AssignPlayersToGuildPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("f42f571e-0cd1-4c22-ba53-8344848ba998")]
|
||||
[MinimumClient(0, 90, ClientLanguage.Invariant)]
|
||||
public class AssignPlayersToGuildPlugIn : IAssignPlayersToGuildPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssignPlayersToGuildPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public AssignPlayersToGuildPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask AssignPlayersToGuildAsync(ICollection<Player> guildPlayers, bool appearsNew)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// C2 00 11
|
||||
// 65
|
||||
// 01
|
||||
// 34 4B 00 00 80 00 00
|
||||
// A4 F2 00 00 00
|
||||
int Write()
|
||||
{
|
||||
var size = AssignCharacterToGuildRef.GetRequiredSize(guildPlayers.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new AssignCharacterToGuildRef(span)
|
||||
{
|
||||
PlayerCount = (byte)guildPlayers.Count,
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
foreach (var guildPlayer in guildPlayers)
|
||||
{
|
||||
this.SetGuildPlayerBlock(packet[i], guildPlayer, appearsNew);
|
||||
i++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask AssignPlayerToGuildAsync(Player guildPlayer, bool appearsNew)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// C2 00 11
|
||||
// 65
|
||||
// 01
|
||||
// 34 4B 00 00 80 00 00
|
||||
// A4 F2 00 00 00
|
||||
int Write()
|
||||
{
|
||||
var size = AssignCharacterToGuildRef.GetRequiredSize(1);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new AssignCharacterToGuildRef(span)
|
||||
{
|
||||
PlayerCount = 1,
|
||||
};
|
||||
|
||||
this.SetGuildPlayerBlock(packet[0], guildPlayer, appearsNew);
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void SetGuildPlayerBlock(AssignCharacterToGuildRef.GuildMemberRelationRef playerBlock, Player guildPlayer, bool appearsNew)
|
||||
{
|
||||
if (guildPlayer.GuildStatus is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerBlock.GuildId = guildPlayer.GuildStatus.GuildId;
|
||||
playerBlock.Role = guildPlayer.GuildStatus.Position.Convert();
|
||||
playerBlock.PlayerId = guildPlayer.GetId(this._player);
|
||||
playerBlock.IsPlayerAppearingNew = appearsNew;
|
||||
}
|
||||
}
|
||||
166
src/GameServer/RemoteView/Guild/AssignPlayersToGuildPlugIn075.cs
Normal file
166
src/GameServer/RemoteView/Guild/AssignPlayersToGuildPlugIn075.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
// <copyright file="AssignPlayersToGuildPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IAssignPlayersToGuildPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.AssignPlayersToGuildPlugIn075_Name), Description = nameof(PlugInResources.AssignPlayersToGuildPlugIn075_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("ABFA2CBD-1AB0-4F56-97A7-FCF458865ACF")]
|
||||
[MaximumClient(0, 89, ClientLanguage.Invariant)]
|
||||
public class AssignPlayersToGuildPlugIn075 : BaseGuildInfoPlugIn<AssignPlayersToGuildPlugIn075>, IAssignPlayersToGuildPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
private readonly HashSet<uint> _transmittedGuilds = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AssignPlayersToGuildPlugIn075"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public AssignPlayersToGuildPlugIn075(RemotePlayer player)
|
||||
: base(player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask AssignPlayersToGuildAsync(ICollection<Player> guildPlayers, bool appearsNew)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var guildPlayer in guildPlayers)
|
||||
{
|
||||
await this.SendGuildInfoIfRequiredAsync(guildPlayer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = AssignCharacterToGuild075Ref.GetRequiredSize(guildPlayers.Count);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new AssignCharacterToGuild075Ref(span)
|
||||
{
|
||||
PlayerCount = (byte)guildPlayers.Count,
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
foreach (var guildPlayer in guildPlayers)
|
||||
{
|
||||
this.SetGuildPlayerBlock(packet[i], guildPlayer);
|
||||
i++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask AssignPlayerToGuildAsync(Player guildPlayer, bool appearsNew)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await this.SendGuildInfoIfRequiredAsync(guildPlayer).ConfigureAwait(false);
|
||||
|
||||
// C2 00 11
|
||||
// 65
|
||||
// 01
|
||||
// 34 4B 00 00 80 00 00
|
||||
// A4 F2 00 00 00
|
||||
int Write()
|
||||
{
|
||||
var size = AssignCharacterToGuild075Ref.GetRequiredSize(1);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new AssignCharacterToGuild075Ref(span)
|
||||
{
|
||||
PlayerCount = 1,
|
||||
};
|
||||
|
||||
this.SetGuildPlayerBlock(packet[0], guildPlayer);
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Memory<byte> Serialize(Interfaces.Guild guild, uint guildId)
|
||||
{
|
||||
var array = new byte[GuildInformations075.GetRequiredSize(1)];
|
||||
var result = new GuildInformations075(array) { GuildCount = 1 };
|
||||
|
||||
var guildInfo = result[0];
|
||||
guildInfo.GuildId = (ushort)guildId;
|
||||
guildInfo.GuildName = guild.Name ?? string.Empty;
|
||||
guild.Logo.CopyTo(guildInfo.Logo);
|
||||
return array.AsMemory();
|
||||
}
|
||||
|
||||
private async ValueTask SendGuildInfoIfRequiredAsync(Player guildPlayer)
|
||||
{
|
||||
if (guildPlayer.GuildStatus is not { } guildStatus)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._transmittedGuilds.Contains(guildStatus.GuildId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var data = await this.GetGuildDataAsync(guildStatus.GuildId).ConfigureAwait(false);
|
||||
if (data.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var connection = this.Player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this._transmittedGuilds.Add(guildStatus.GuildId);
|
||||
|
||||
// guildInfo is the cached, serialized result of the GuildInformation-Class.
|
||||
int Write()
|
||||
{
|
||||
var target = connection.Output.GetSpan(data.Length);
|
||||
data.Span.CopyTo(target);
|
||||
return data.Length;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void SetGuildPlayerBlock(AssignCharacterToGuild075Ref.GuildMemberRelationRef playerBlock, Player guildPlayer)
|
||||
{
|
||||
if (guildPlayer.GuildStatus is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
playerBlock.GuildId = (ushort)guildPlayer.GuildStatus.GuildId;
|
||||
playerBlock.PlayerId = guildPlayer.GetId(this._player);
|
||||
}
|
||||
}
|
||||
92
src/GameServer/RemoteView/Guild/BaseGuildInfoPlugIn.cs
Normal file
92
src/GameServer/RemoteView/Guild/BaseGuildInfoPlugIn.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
// <copyright file="BaseGuildInfoPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Abstract base class for a <see cref="IShowGuildInfoPlugIn" /> which allows to cache serialized guild infos.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the actual <see cref="IShowGuildInfoPlugIn"/>. Required, so there is one cache per type.</typeparam>
|
||||
// ReSharper disable once UnusedTypeParameter we just use it to get type specific static fields.
|
||||
public abstract class BaseGuildInfoPlugIn<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The cache for already serialized guilds. This data doesn't change, but is requested often.
|
||||
/// </summary>
|
||||
// ReSharper disable once StaticMemberInGenericType That's what we want
|
||||
private static readonly ConcurrentDictionary<uint, Memory<byte>> Cache = new();
|
||||
|
||||
// ReSharper disable once StaticMemberInGenericType That's what we want
|
||||
private static readonly HashSet<IGameServerContext> AppendedGuildDeletedSenders = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseGuildInfoPlugIn{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
protected BaseGuildInfoPlugIn(RemotePlayer player)
|
||||
{
|
||||
this.Player = player;
|
||||
lock (AppendedGuildDeletedSenders)
|
||||
{
|
||||
if (AppendedGuildDeletedSenders.Add(player.GameServerContext))
|
||||
{
|
||||
// to make sure we just add one event handler
|
||||
this.Player.GameServerContext.GuildDeleted += OnGuildChanged;
|
||||
this.Player.GameServerContext.GuildChanged += OnGuildChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the player.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The player.
|
||||
/// </value>
|
||||
protected RemotePlayer Player { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the specified guild.
|
||||
/// </summary>
|
||||
/// <param name="guild">The guild.</param>
|
||||
/// <param name="guildId">The guild identifier.</param>
|
||||
/// <returns>The serialized guild data packet.</returns>
|
||||
protected abstract Memory<byte> Serialize(Guild guild, uint guildId);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Guild Info Data of a Guild. It will either
|
||||
/// take the data out of the Cache, or get it from the database and serializes it.
|
||||
/// </summary>
|
||||
/// <param name="guildId">The id of the guild.</param>
|
||||
/// <returns>
|
||||
/// The data of the guild.
|
||||
/// </returns>
|
||||
protected async ValueTask<Memory<byte>> GetGuildDataAsync(uint guildId)
|
||||
{
|
||||
if (Cache.TryGetValue(guildId, out var guildInfo))
|
||||
{
|
||||
return guildInfo;
|
||||
}
|
||||
|
||||
var guild = await this.Player.GameServerContext.GuildServer.GetGuildAsync(guildId).ConfigureAwait(false);
|
||||
if (guild is null)
|
||||
{
|
||||
return Memory<byte>.Empty;
|
||||
}
|
||||
|
||||
var data = this.Serialize(guild, guildId);
|
||||
Cache.TryAdd(guildId, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static void OnGuildChanged(object? sender, GuildEventArgs args)
|
||||
{
|
||||
Cache.TryRemove(args.GuildId, out _);
|
||||
}
|
||||
}
|
||||
217
src/GameServer/RemoteView/Guild/EnumExtensions.cs
Normal file
217
src/GameServer/RemoteView/Guild/EnumExtensions.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
// <copyright file="EnumExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using static MUnique.OpenMU.Network.Packets.ServerToClient.GuildJoinResponse;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for enum types.
|
||||
/// </summary>
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the view value.
|
||||
/// </summary>
|
||||
/// <param name="playerPosition">The player position.</param>
|
||||
/// <returns>The value which is used in the message for the corresponding enum value.</returns>
|
||||
public static GuildMemberRole Convert(this GuildPosition? playerPosition)
|
||||
{
|
||||
if (playerPosition.HasValue)
|
||||
{
|
||||
return playerPosition.Value.Convert();
|
||||
}
|
||||
|
||||
return GuildMemberRole.Undefined;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view value.
|
||||
/// </summary>
|
||||
/// <param name="playerPosition">The player position.</param>
|
||||
/// <returns>The value which is used in the message for the corresponding enum value.</returns>
|
||||
public static GuildMemberRole Convert(this GuildPosition playerPosition)
|
||||
{
|
||||
return playerPosition switch
|
||||
{
|
||||
GuildPosition.GuildMaster => GuildMemberRole.GuildMaster,
|
||||
GuildPosition.AssistantMaster => GuildMemberRole.AssistantMaster,
|
||||
GuildPosition.NormalMember => GuildMemberRole.NormalMember,
|
||||
GuildPosition.BattleMaster => GuildMemberRole.BattleMaster,
|
||||
_ => GuildMemberRole.Undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the <see cref="GuildRequestAnswerResult"/> into a <see cref="GuildJoinRequestResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The <see cref="GuildRequestAnswerResult"/> which should be converted.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static GuildJoinRequestResult Convert(this GuildRequestAnswerResult result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
GuildRequestAnswerResult.Refused => GuildJoinRequestResult.Refused,
|
||||
GuildRequestAnswerResult.Accepted => GuildJoinRequestResult.Accepted,
|
||||
GuildRequestAnswerResult.GuildFull => GuildJoinRequestResult.GuildFull,
|
||||
GuildRequestAnswerResult.Disconnected => GuildJoinRequestResult.Disconnected,
|
||||
GuildRequestAnswerResult.NotTheGuildMaster => GuildJoinRequestResult.NotTheGuildMaster,
|
||||
GuildRequestAnswerResult.AlreadyHaveGuild => GuildJoinRequestResult.AlreadyHaveGuild,
|
||||
GuildRequestAnswerResult.GuildMasterOrRequesterIsBusy => GuildJoinRequestResult.GuildMasterOrRequesterIsBusy,
|
||||
GuildRequestAnswerResult.MinimumLevel6 => GuildJoinRequestResult.MinimumLevel6,
|
||||
_ => throw new NotImplementedException($"The case {result} is not implemented."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="GuildKickSuccess"/> into a <see cref="GuildKickResponse.GuildKickSuccess"/>.
|
||||
/// </summary>
|
||||
/// <param name="success">The <see cref="GuildKickSuccess"/> which should be converted.</param>
|
||||
/// <returns>The converted <see cref="GuildKickResponse.GuildKickSuccess"/>.</returns>
|
||||
public static GuildKickResponse.GuildKickSuccess Convert(this GuildKickSuccess success)
|
||||
{
|
||||
return success switch
|
||||
{
|
||||
GuildKickSuccess.Failed => GuildKickResponse.GuildKickSuccess.Failed,
|
||||
GuildKickSuccess.FailedBecausePlayerIsNotGuildMaster => GuildKickResponse.GuildKickSuccess.KickFailedBecausePlayerIsNotGuildMaster,
|
||||
GuildKickSuccess.KickSucceeded => GuildKickResponse.GuildKickSuccess.KickSucceeded,
|
||||
GuildKickSuccess.GuildDisband => GuildKickResponse.GuildKickSuccess.GuildDisband,
|
||||
_ => throw new NotImplementedException($"The case {success} is not implemented."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="GuildCreateErrorDetail"/> into a <see cref="GuildCreationResult.GuildCreationErrorType"/>.
|
||||
/// </summary>
|
||||
/// <param name="errorDetail">The <see cref="GuildCreateErrorDetail"/> which should be converted.</param>
|
||||
/// <returns>The converted <see cref="GuildCreationResult.GuildCreationErrorType"/>.</returns>
|
||||
public static GuildCreationResult.GuildCreationErrorType Convert(this GuildCreateErrorDetail errorDetail)
|
||||
{
|
||||
return errorDetail switch
|
||||
{
|
||||
GuildCreateErrorDetail.None => GuildCreationResult.GuildCreationErrorType.None,
|
||||
GuildCreateErrorDetail.GuildAlreadyExist => GuildCreationResult.GuildCreationErrorType.GuildNameAlreadyTaken,
|
||||
_ => throw new NotImplementedException($"The case {errorDetail} is not implemented."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="GameLogic.GuildWar.GuildWarType"/> into a <see cref="Network.Packets.ServerToClient.GuildWarType"/>.
|
||||
/// </summary>
|
||||
/// <param name="guildWarType">The <see cref="GameLogic.GuildWar.GuildWarType"/> which should be converted.</param>
|
||||
/// <returns>The converted <see cref="Network.Packets.ServerToClient.GuildWarType"/>.</returns>
|
||||
public static Network.Packets.ServerToClient.GuildWarType Convert(this GameLogic.GuildWar.GuildWarType guildWarType)
|
||||
{
|
||||
return guildWarType switch
|
||||
{
|
||||
GameLogic.GuildWar.GuildWarType.Normal => Network.Packets.ServerToClient.GuildWarType.Normal,
|
||||
GameLogic.GuildWar.GuildWarType.Soccer => Network.Packets.ServerToClient.GuildWarType.Soccer,
|
||||
_ => throw new NotImplementedException($"The case {guildWarType} is not implemented."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="GameLogic.Views.Guild.GuildWarResult"/> into a <see cref="GuildWarEnded.GuildWarResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="guildWarResult">The <see cref="GameLogic.Views.Guild.GuildWarResult"/> which should be converted.</param>
|
||||
/// <returns>The converted <see cref="GuildWarEnded.GuildWarResult"/>.</returns>
|
||||
public static GuildWarEnded.GuildWarResult Convert(this GuildWarResult guildWarResult)
|
||||
{
|
||||
return guildWarResult switch
|
||||
{
|
||||
GuildWarResult.Lost => GuildWarEnded.GuildWarResult.Lost,
|
||||
GuildWarResult.Won => GuildWarEnded.GuildWarResult.Won,
|
||||
GuildWarResult.CancelledWar => GuildWarEnded.GuildWarResult.CancelledWar,
|
||||
GuildWarResult.OtherGuildMasterCancelledWar => GuildWarEnded.GuildWarResult.OtherGuildMasterCancelledWar,
|
||||
_ => throw new NotImplementedException($"The case {guildWarResult} is not implemented."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="GameLogic.Views.Guild.GuildWarRequestResult"/> into a <see cref="Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The <see cref="GameLogic.Views.Guild.GuildWarRequestResult"/> which should be converted.</param>
|
||||
/// <returns>The converted <see cref="Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult"/>.</returns>
|
||||
public static Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult Convert(this GameLogic.Views.Guild.GuildWarRequestResult result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
GameLogic.Views.Guild.GuildWarRequestResult.AlreadyInWar => Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult.AlreadyInWar,
|
||||
GameLogic.Views.Guild.GuildWarRequestResult.Failed => Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult.Failed,
|
||||
GameLogic.Views.Guild.GuildWarRequestResult.GuildMasterOffline => Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult.GuildMasterOffline,
|
||||
GameLogic.Views.Guild.GuildWarRequestResult.GuildNotFound => Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult.GuildNotFound,
|
||||
GameLogic.Views.Guild.GuildWarRequestResult.NotInGuild => Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult.NotInGuild,
|
||||
GameLogic.Views.Guild.GuildWarRequestResult.NotTheGuildMaster => Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult.NotTheGuildMaster,
|
||||
GameLogic.Views.Guild.GuildWarRequestResult.RequestSentToGuildMaster => Network.Packets.ServerToClient.GuildWarRequestResult.RequestResult.RequestSentToGuildMaster,
|
||||
_ => throw new NotImplementedException($"The case {result} is not implemented."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified relationship type.
|
||||
/// </summary>
|
||||
/// <param name="relationshipType">Type of the relationship.</param>
|
||||
/// <returns>The converted <see cref="Network.Packets.ServerToClient.GuildRelationshipType"/>.</returns>
|
||||
public static Network.Packets.ServerToClient.GuildRelationshipType Convert(this GameLogic.Views.Guild.GuildRelationshipType relationshipType)
|
||||
{
|
||||
return relationshipType switch
|
||||
{
|
||||
GameLogic.Views.Guild.GuildRelationshipType.Undefined => Network.Packets.ServerToClient.GuildRelationshipType.Undefined,
|
||||
GameLogic.Views.Guild.GuildRelationshipType.Alliance => Network.Packets.ServerToClient.GuildRelationshipType.Alliance,
|
||||
GameLogic.Views.Guild.GuildRelationshipType.Hostility => Network.Packets.ServerToClient.GuildRelationshipType.Hostility,
|
||||
_ => throw new NotImplementedException($"The case {relationshipType} is not implemented."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified request type.
|
||||
/// </summary>
|
||||
/// <param name="requestType">Type of the request.</param>
|
||||
/// <returns>The converted <see cref="Network.Packets.ServerToClient.GuildRelationshipRequestType"/>.</returns>
|
||||
public static Network.Packets.ServerToClient.GuildRelationshipRequestType Convert(this GameLogic.Views.Guild.GuildRelationshipRequestType requestType)
|
||||
{
|
||||
return requestType switch
|
||||
{
|
||||
GameLogic.Views.Guild.GuildRelationshipRequestType.Undefined => Network.Packets.ServerToClient.GuildRelationshipRequestType.Undefined,
|
||||
GameLogic.Views.Guild.GuildRelationshipRequestType.Join => Network.Packets.ServerToClient.GuildRelationshipRequestType.Join,
|
||||
GameLogic.Views.Guild.GuildRelationshipRequestType.Leave => Network.Packets.ServerToClient.GuildRelationshipRequestType.Leave,
|
||||
_ => throw new NotImplementedException($"The case {requestType} is not implemented."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified result type.
|
||||
/// </summary>
|
||||
/// <param name="result">The <see cref="GameLogic.Views.Guild.GuildRelationshipChangeResultType"/> which should be converted.</param>
|
||||
/// <returns>The converted <see cref="Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType"/>.</returns>
|
||||
public static Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType Convert(this GameLogic.Views.Guild.GuildRelationshipChangeResultType result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.Failed => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.Failed,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.Success => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.Success,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.GuildNotFound => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.GuildNotFound,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.NoAuthorization => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.NoAuthorization,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.AlreadyInAlliance => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.AlreadyInAlliance,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.FailedDuringCastleSiege => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.FailedDuringCastleSiege,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.AlreadyInHostility => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.AlreadyInHostility,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.GuildAllianceExists => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.GuildAllianceExists,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.HostileGuildExists => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.HostileGuildExists,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.GuildAllianceDoesNotExist => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.GuildAllianceDoesNotExist,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.HostileGuildDoesNotExist => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.HostileGuildDoesNotExist,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.NotMasterOfGuildAlliance => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.NotMasterOfGuildAlliance,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.NotGuildRival => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.NotGuildRival,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.IncompleteRequirementsToCreateAlliance => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.IncompleteRequirementsToCreateAlliance,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.MaximumNumberOfGuildsInAllianceReached => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.MaximumNumberOfGuildsInAllianceReached,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.RequestCancelled => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.RequestCancelled,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.AllianceMasterNotInGens => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.AllianceMasterNotInGens,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.GuildMasterNotInGens => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.GuildMasterNotInGens,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType.DifferentGens => Network.Packets.ServerToClient.GuildRelationshipChangeResult.GuildRelationshipChangeResultType.DifferentGens,
|
||||
_ => throw new NotImplementedException($"The case {result} is not implemented."),
|
||||
};
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Guild/GuildJoinResponsePlugIn.cs
Normal file
33
src/GameServer/RemoteView/Guild/GuildJoinResponsePlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="GuildJoinResponsePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IGuildJoinResponsePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.GuildJoinResponsePlugIn_Name), Description = nameof(PlugInResources.GuildJoinResponsePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("4a8bd97c-a544-4cac-b8cd-2e73945bcfdc")]
|
||||
public class GuildJoinResponsePlugIn : IGuildJoinResponsePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GuildJoinResponsePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public GuildJoinResponsePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowGuildJoinResponseAsync(GuildRequestAnswerResult result)
|
||||
{
|
||||
await this._player.Connection.SendGuildJoinResponseAsync(result.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Guild/GuildKickResultPlugIn.cs
Normal file
33
src/GameServer/RemoteView/Guild/GuildKickResultPlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="GuildKickResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IGuildKickResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.GuildKickResultPlugIn_Name), Description = nameof(PlugInResources.GuildKickResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("0e91e131-12c4-4add-9439-febb7d444083")]
|
||||
public class GuildKickResultPlugIn : IGuildKickResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GuildKickResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public GuildKickResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask GuildKickResultAsync(GuildKickSuccess successCode)
|
||||
{
|
||||
await this._player.Connection.SendGuildKickResponseAsync(successCode.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
36
src/GameServer/RemoteView/Guild/GuildWarScoreUpdatePlugIn.cs
Normal file
36
src/GameServer/RemoteView/Guild/GuildWarScoreUpdatePlugIn.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// <copyright file="GuildWarScoreUpdatePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IGuildWarScoreUpdatePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.GuildWarScoreUpdatePlugIn_Name), Description = nameof(PlugInResources.GuildWarScoreUpdatePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("24E5E0C1-DAE7-4E34-810D-C622F8F9B70F")]
|
||||
public class GuildWarScoreUpdatePlugIn : IGuildWarScoreUpdatePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GuildWarScoreUpdatePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public GuildWarScoreUpdatePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateScoreAsync()
|
||||
{
|
||||
if (this._player.GuildWarContext is { } guildWarContext)
|
||||
{
|
||||
await this._player.Connection.SendGuildWarScoreUpdateAsync(guildWarContext.ThisScore, guildWarContext.EnemyScore).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/GameServer/RemoteView/Guild/PlayerLeftGuildPlugIn.cs
Normal file
39
src/GameServer/RemoteView/Guild/PlayerLeftGuildPlugIn.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// <copyright file="PlayerLeftGuildPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IPlayerLeftGuildPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.PlayerLeftGuildPlugIn_Name), Description = nameof(PlugInResources.PlayerLeftGuildPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("e37868e8-6fdf-41fb-aa7f-01e6f569f5c0")]
|
||||
public class PlayerLeftGuildPlugIn : IPlayerLeftGuildPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerLeftGuildPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public PlayerLeftGuildPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask PlayerLeftGuildAsync(Player player)
|
||||
{
|
||||
await this._player.Connection.SendGuildMemberLeftGuildAsync(
|
||||
player.GetId(this._player),
|
||||
player.GuildStatus?.Position == GuildPosition.GuildMaster)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
64
src/GameServer/RemoteView/Guild/ShowAllianceListPlugIn.cs
Normal file
64
src/GameServer/RemoteView/Guild/ShowAllianceListPlugIn.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
// <copyright file="ShowAllianceListPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowAllianceListPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowAllianceListPlugIn_Name), Description = nameof(PlugInResources.ShowAllianceListPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("A8B9C0D1-E2F3-4A4B-5C6D-7E8F9A0B1C2D")]
|
||||
public class ShowAllianceListPlugIn : IShowAllianceListPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowAllianceListPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowAllianceListPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowListAsync(IEnumerable<AllianceGuildEntry> guilds)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var guildList = guilds.ToList();
|
||||
int guildCount = guildList.Count;
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = AllianceList.GetRequiredSize(guildCount);
|
||||
var packet = new AllianceListRef(connection.Output.GetSpan(size)[..size])
|
||||
{
|
||||
GuildCount = (byte)guildCount,
|
||||
Success = true,
|
||||
};
|
||||
|
||||
for (int i = 0; i < guildCount; i++)
|
||||
{
|
||||
var entry = packet[i];
|
||||
entry.GuildName = guildList[i].GuildName;
|
||||
entry.MemberCount = (byte)guildList[i].MemberCount;
|
||||
guildList[i].Logo.Span.CopyTo(entry.Logo);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ShowGuildCreateResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildCreateResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildCreateResultPlugIn_Name), Description = nameof(PlugInResources.ShowGuildCreateResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("ff6c5a06-4699-461b-9004-756269393c40")]
|
||||
public class ShowGuildCreateResultPlugIn : IShowGuildCreateResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildCreateResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildCreateResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowGuildCreateResultAsync(GuildCreateErrorDetail errorDetail)
|
||||
{
|
||||
await this._player.Connection.SendGuildCreationResultAsync(errorDetail == GuildCreateErrorDetail.None, errorDetail.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ShowGuildCreationDialogPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildCreationDialogPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildCreationDialogPlugIn_Name), Description = nameof(PlugInResources.ShowGuildCreationDialogPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("ed6fbe5f-7a27-477d-b238-e6e77cf113d8")]
|
||||
public class ShowGuildCreationDialogPlugIn : IShowGuildCreationDialogPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildCreationDialogPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildCreationDialogPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowGuildCreationDialogAsync()
|
||||
{
|
||||
await this._player.Connection.SendShowGuildCreationDialogAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
83
src/GameServer/RemoteView/Guild/ShowGuildInfoPlugIn.cs
Normal file
83
src/GameServer/RemoteView/Guild/ShowGuildInfoPlugIn.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
// <copyright file="ShowGuildInfoPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildInfoPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildInfoPlugIn_Name), Description = nameof(PlugInResources.ShowGuildInfoPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("65f9d310-4adf-48b4-ace2-16779b254ecf")]
|
||||
public class ShowGuildInfoPlugIn : BaseGuildInfoPlugIn<ShowGuildInfoPlugIn>, IShowGuildInfoPlugIn
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildInfoPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildInfoPlugIn(RemotePlayer player)
|
||||
: base(player)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowGuildInfoAsync(uint guildId)
|
||||
{
|
||||
var data = await this.GetGuildDataAsync(guildId).ConfigureAwait(false);
|
||||
if (data.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var connection = this.Player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// guildInfo is the cached, serialized result of the GuildInformation-Class.
|
||||
int Write()
|
||||
{
|
||||
var target = connection.Output.GetSpan(data.Length);
|
||||
data.Span.CopyTo(target);
|
||||
return data.Length;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Memory<byte> Serialize(Guild guild, uint guildId)
|
||||
{
|
||||
/*
|
||||
* C1 3C 66 00
|
||||
87 38 00 00 // guild number
|
||||
00 // guild type
|
||||
54 68 65 4F 6E 65 00 00 //TheOne - Maintain
|
||||
41 76 61 6C 6F 6E 00 2B //Avalon - Assistant
|
||||
18 88 88 81 18 66 66 81 18 61 16 81 18 61 16 81 18 66 66 81 18 61 16 81 18 61 16 81 18 61 16 81 //Guild Logo
|
||||
F9 96 7C //?
|
||||
*/
|
||||
var array = new byte[GuildInformation.Length];
|
||||
var result = new GuildInformation(array)
|
||||
{
|
||||
GuildId = guildId,
|
||||
GuildName = guild.Name ?? string.Empty,
|
||||
};
|
||||
if (guild.AllianceGuild != null)
|
||||
{
|
||||
result.AllianceGuildName = guild.AllianceGuild.Name ?? string.Empty;
|
||||
}
|
||||
|
||||
guild.Logo.CopyTo(result.Logo);
|
||||
return array.AsMemory();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// <copyright file="ShowGuildJoinRequestPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildJoinRequestPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildJoinRequestPlugIn_Name), Description = nameof(PlugInResources.ShowGuildJoinRequestPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("521ff03d-c8ad-44d9-a23d-8f98c4c174ae")]
|
||||
public class ShowGuildJoinRequestPlugIn : IShowGuildJoinRequestPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildJoinRequestPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildJoinRequestPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowGuildJoinRequestAsync(Player requester)
|
||||
{
|
||||
await this._player.Connection.SendGuildJoinRequestAsync(requester.Id).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
70
src/GameServer/RemoteView/Guild/ShowGuildListPlugIn.cs
Normal file
70
src/GameServer/RemoteView/Guild/ShowGuildListPlugIn.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
// <copyright file="ShowGuildListPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildListPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildListPlugIn_Name), Description = nameof(PlugInResources.ShowGuildListPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("f72a9968-100b-481f-aba1-1dd597fdad47")]
|
||||
[MinimumClient(0, 90, ClientLanguage.Invariant)]
|
||||
public class ShowGuildListPlugIn : IShowGuildListPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildListPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildListPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowGuildListAsync(IReadOnlyCollection<OpenMU.Interfaces.GuildListEntry> players, Interfaces.Guild guild)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var playerCount = players.Count;
|
||||
int Write()
|
||||
{
|
||||
var size = GuildListRef.GetRequiredSize(playerCount);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new GuildListRef(span)
|
||||
{
|
||||
GuildMemberCount = (byte)playerCount,
|
||||
IsInGuild = playerCount > 0,
|
||||
RivalGuildName = guild.Hostility?.Name ?? string.Empty,
|
||||
CurrentScore = 0, // TODO: What is this? Maybe a rank?
|
||||
TotalScore = (uint)guild.Score,
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
foreach (var member in players)
|
||||
{
|
||||
var memberBlock = packet[i];
|
||||
memberBlock.Name = member.PlayerName;
|
||||
memberBlock.Role = member.PlayerPosition.Convert();
|
||||
memberBlock.ServerId = member.ServerId;
|
||||
memberBlock.ServerId2 = (byte)(member.ServerId == 0xFF ? 0x7F : 0x80 + member.ServerId);
|
||||
i++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
73
src/GameServer/RemoteView/Guild/ShowGuildListPlugIn075.cs
Normal file
73
src/GameServer/RemoteView/Guild/ShowGuildListPlugIn075.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
// <copyright file="ShowGuildListPlugIn075.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildListPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildListPlugIn075_Name), Description = nameof(PlugInResources.ShowGuildListPlugIn075_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("1A7148DF-6E1B-47C7-9148-426F5E35F421")]
|
||||
[MaximumClient(0, 89, ClientLanguage.Invariant)]
|
||||
public class ShowGuildListPlugIn075 : IShowGuildListPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildListPlugIn075"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildListPlugIn075(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowGuildListAsync(IReadOnlyCollection<GuildListEntry> players, Guild guild)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sortedPlayers = players
|
||||
.OrderBy(p => p.PlayerPosition != GuildPosition.GuildMaster)
|
||||
.ThenBy(p => p.PlayerName)
|
||||
.ToList();
|
||||
var playerCount = sortedPlayers.Count;
|
||||
int Write()
|
||||
{
|
||||
var size = GuildList075Ref.GetRequiredSize(playerCount);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new GuildList075Ref(span)
|
||||
{
|
||||
GuildMemberCount = (byte)playerCount,
|
||||
IsInGuild = playerCount > 0,
|
||||
CurrentScore = 0, // TODO
|
||||
TotalScore = (uint)guild.Score,
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
foreach (var member in sortedPlayers)
|
||||
{
|
||||
var memberBlock = packet[i];
|
||||
memberBlock.Name = member.PlayerName;
|
||||
memberBlock.ServerId = member.ServerId;
|
||||
memberBlock.ServerId2 = (byte)(member.ServerId == 0xFF ? 0x7F : 0x80 + member.ServerId);
|
||||
i++;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ShowGuildMasterDialogPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildMasterDialogPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildMasterDialogPlugIn_Name), Description = nameof(PlugInResources.ShowGuildMasterDialogPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("77d430e0-8bed-425b-8bb5-7bbafa9bfbff")]
|
||||
public class ShowGuildMasterDialogPlugIn : IShowGuildMasterDialogPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildMasterDialogPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildMasterDialogPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowGuildMasterDialogAsync()
|
||||
{
|
||||
await this._player.Connection.SendShowGuildMasterDialogAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// <copyright file="ShowGuildRelationshipChangeResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IGuildRelationshipChangeResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildRelationshipChangeResultPlugIn_Name), Description = nameof(PlugInResources.ShowGuildRelationshipChangeResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("E6F7A8B9-C0D1-4E2F-3A4B-5C6D7E8F9A0B")]
|
||||
public class ShowGuildRelationshipChangeResultPlugIn : IGuildRelationshipChangeResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildRelationshipChangeResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildRelationshipChangeResultPlugIn(RemotePlayer player)
|
||||
{
|
||||
this._player = player;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowRemoveResultAsync(
|
||||
bool result,
|
||||
GameLogic.Views.Guild.GuildRelationshipType relationshipType = GameLogic.Views.Guild.GuildRelationshipType.Alliance,
|
||||
GameLogic.Views.Guild.GuildRelationshipRequestType requestType = GameLogic.Views.Guild.GuildRelationshipRequestType.Leave)
|
||||
{
|
||||
await this._player.Connection.SendRemoveAllianceGuildResultAsync(result, requestType.Convert(), relationshipType.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowResultAsync(
|
||||
GameLogic.Views.Guild.GuildRelationshipType relationshipType,
|
||||
GameLogic.Views.Guild.GuildRelationshipRequestType requestType,
|
||||
GameLogic.Views.Guild.GuildRelationshipChangeResultType result,
|
||||
ushort? guildMasterId)
|
||||
{
|
||||
await this._player.Connection.SendGuildRelationshipChangeResultAsync(
|
||||
relationshipType.Convert(),
|
||||
requestType.Convert(),
|
||||
result.Convert(),
|
||||
guildMasterId ?? 0).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// <copyright file="ShowGuildRelationshipRequestPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildRelationshipRequestPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildRelationshipRequestPlugIn_Name), Description = nameof(PlugInResources.ShowGuildRelationshipRequestPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("F7A8B9C0-D1E2-4F3A-4B5C-6D7E8F9A0B1C")]
|
||||
public class ShowGuildRelationshipRequestPlugIn : IShowGuildRelationshipRequestPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildRelationshipRequestPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildRelationshipRequestPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowRequestAsync(Player requestingGuildMaster, GameLogic.Views.Guild.GuildRelationshipType relationshipType, GameLogic.Views.Guild.GuildRelationshipRequestType requestType)
|
||||
{
|
||||
await this._player.Connection.SendGuildRelationshipRequestAsync(
|
||||
relationshipType.Convert(),
|
||||
requestType.Convert(),
|
||||
requestingGuildMaster.GetId(this._player)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// <copyright file="ShowGuildWarDeclaredPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildWarDeclaredPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildWarDeclaredPlugIn_Name), Description = nameof(PlugInResources.ShowGuildWarDeclaredPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("50393D5D-01F2-43F8-B5D7-243D91B905BC")]
|
||||
public class ShowGuildWarDeclaredPlugIn : IShowGuildWarDeclaredPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildWarDeclaredPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildWarDeclaredPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowDeclaredAsync()
|
||||
{
|
||||
if (this._player.GuildWarContext is { } guildWarContext)
|
||||
{
|
||||
await this._player.Connection.SendGuildWarDeclaredAsync(guildWarContext.EnemyTeamName, guildWarContext.WarType.Convert(), (byte)guildWarContext.Team).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Guild/ShowGuildWarRequestPlugIn.cs
Normal file
33
src/GameServer/RemoteView/Guild/ShowGuildWarRequestPlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ShowGuildWarRequestPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildWarRequestPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildWarRequestPlugIn_Name), Description = nameof(PlugInResources.ShowGuildWarRequestPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("128250B3-6CFC-4C89-BF8A-B50C892A78D3")]
|
||||
public class ShowGuildWarRequestPlugIn : IShowGuildWarRequestPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildWarRequestPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildWarRequestPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowRequestAsync(string requestingGuildName, GameLogic.GuildWar.GuildWarType warType)
|
||||
{
|
||||
await this._player.Connection.SendGuildWarRequestAsync(requestingGuildName, warType.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Guild/ShowGuildWarResultPlugIn.cs
Normal file
33
src/GameServer/RemoteView/Guild/ShowGuildWarResultPlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ShowGuildWarResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowGuildWarResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowGuildWarResultPlugIn_Name), Description = nameof(PlugInResources.ShowGuildWarResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("991247B9-D4A3-466D-A7CE-2621843CA94F")]
|
||||
public class ShowGuildWarResultPlugIn : IShowGuildWarResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowGuildWarResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowGuildWarResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowResultAsync(string hostileGuildName, GuildWarResult result)
|
||||
{
|
||||
await this._player.Connection.SendGuildWarEndedAsync(result.Convert(), hostileGuildName).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ShowShowGuildWarRequestResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Guild;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Guild;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IShowShowGuildWarRequestResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ShowShowGuildWarRequestResultPlugIn_Name), Description = nameof(PlugInResources.ShowShowGuildWarRequestResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("7DDF834C-218B-4F35-B66C-54579BE485D5")]
|
||||
public class ShowShowGuildWarRequestResultPlugIn : IShowShowGuildWarRequestResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShowShowGuildWarRequestResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ShowShowGuildWarRequestResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ShowResultAsync(GameLogic.Views.Guild.GuildWarRequestResult result)
|
||||
{
|
||||
await this._player.Connection.SendGuildWarRequestResultAsync(result.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/IAppearanceSerializer.cs
Normal file
33
src/GameServer/RemoteView/IAppearanceSerializer.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="IAppearanceSerializer.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Serializer of <see cref="IAppearanceData"/> objects.
|
||||
/// </summary>
|
||||
public interface IAppearanceSerializer : IViewPlugIn
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the needed space for a serialized <see cref="IAppearanceData"/>.
|
||||
/// </summary>
|
||||
int NeededSpace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the appearance data into the target span.
|
||||
/// </summary>
|
||||
/// <param name="target">The target which should be at least as big as <see cref="NeededSpace"/>.</param>
|
||||
/// <param name="appearance">The appearance which should be serialized.</param>
|
||||
/// <param name="useCache">If set to <c>true</c>, the result is cached and used in subsequent calls.</param>
|
||||
void WriteAppearanceData(Span<byte> target, IAppearanceData appearance, bool useCache);
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates the cache for the given appearance.
|
||||
/// </summary>
|
||||
/// <param name="appearance">The appearance.</param>
|
||||
void InvalidateCache(IAppearanceData appearance);
|
||||
}
|
||||
38
src/GameServer/RemoteView/IItemSerializer.cs
Normal file
38
src/GameServer/RemoteView/IItemSerializer.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
// <copyright file="IItemSerializer.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.Persistence;
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the items into a byte array.
|
||||
/// </summary>
|
||||
public interface IItemSerializer : IViewPlugIn
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the needed space for a serialized item.
|
||||
/// </summary>
|
||||
int NeededSpace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the item into a byte array at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="target">The target span.</param>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>The size of the serialized item.</returns>
|
||||
int SerializeItem(Span<byte> target, Item item);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the byte array into a new item instance.
|
||||
/// </summary>
|
||||
/// <param name="source">The source span.</param>
|
||||
/// <param name="gameConfiguration">The game configuration. Required to determine the item definition.</param>
|
||||
/// <param name="persistenceContext">The persistence context. Required to create new objects.</param>
|
||||
/// <returns>The created item instance.</returns>
|
||||
Item DeserializeItem(Span<byte> source, GameConfiguration gameConfiguration, IContext persistenceContext);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="BuyNpcItemFailedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IBuyNpcItemFailedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.BuyNpcItemFailedPlugIn_Name), Description = nameof(PlugInResources.BuyNpcItemFailedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("915324d5-ccdf-42c0-b7c9-9479969346d8")]
|
||||
public class BuyNpcItemFailedPlugIn : IBuyNpcItemFailedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BuyNpcItemFailedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public BuyNpcItemFailedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask BuyNpcItemFailedAsync()
|
||||
{
|
||||
return this._player.Connection.SendNpcItemBuyFailedAsync();
|
||||
}
|
||||
}
|
||||
109
src/GameServer/RemoteView/Inventory/EnumExtensions.cs
Normal file
109
src/GameServer/RemoteView/Inventory/EnumExtensions.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
// <copyright file="EnumExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.GameLogic.Views.PlayerShop;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods to convert enum values.
|
||||
/// </summary>
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the enum value into the enum type used in the packets.
|
||||
/// </summary>
|
||||
/// <param name="storage">The enum value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static ItemStorageKind Convert(this Storages storage)
|
||||
{
|
||||
return storage switch
|
||||
{
|
||||
Storages.Inventory => ItemStorageKind.Inventory,
|
||||
Storages.ChaosMachine => ItemStorageKind.ChaosMachine,
|
||||
Storages.PersonalStore => ItemStorageKind.PlayerShop,
|
||||
Storages.Trade => ItemStorageKind.Trade,
|
||||
Storages.Vault => ItemStorageKind.Vault,
|
||||
Storages.PetTrainer => ItemStorageKind.PetTrainer,
|
||||
Storages.Refinery => ItemStorageKind.Refinery,
|
||||
Storages.Smelting => ItemStorageKind.Smelting,
|
||||
Storages.ItemRestore => ItemStorageKind.ItemRestore,
|
||||
Storages.ChaosCardMaster => ItemStorageKind.ChaosCardMaster,
|
||||
Storages.CherryBlossomSpirit => ItemStorageKind.CherryBlossomSpirit,
|
||||
Storages.SeedCrafting => ItemStorageKind.SeedCrafting,
|
||||
Storages.SeedSphereCrafting => ItemStorageKind.SeedSphereCrafting,
|
||||
Storages.SeedMountCrafting => ItemStorageKind.SeedMountCrafting,
|
||||
Storages.SeedUnmountCrafting => ItemStorageKind.SeedUnmountCrafting,
|
||||
_ => throw new NotImplementedException($"Unhandled case {storage}."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the enum value into the enum type used in the packets.
|
||||
/// </summary>
|
||||
/// <param name="storage">The enum value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static Storages Convert(this ItemStorageKind storage)
|
||||
{
|
||||
return storage switch
|
||||
{
|
||||
ItemStorageKind.Inventory => Storages.Inventory,
|
||||
ItemStorageKind.ChaosMachine => Storages.ChaosMachine,
|
||||
ItemStorageKind.PlayerShop => Storages.PersonalStore,
|
||||
ItemStorageKind.Trade => Storages.Trade,
|
||||
ItemStorageKind.Vault => Storages.Vault,
|
||||
ItemStorageKind.PetTrainer => Storages.PetTrainer,
|
||||
ItemStorageKind.Refinery => Storages.Refinery,
|
||||
ItemStorageKind.Smelting => Storages.Smelting,
|
||||
ItemStorageKind.ItemRestore => Storages.ItemRestore,
|
||||
ItemStorageKind.ChaosCardMaster => Storages.ChaosCardMaster,
|
||||
ItemStorageKind.CherryBlossomSpirit => Storages.CherryBlossomSpirit,
|
||||
ItemStorageKind.SeedCrafting => Storages.SeedCrafting,
|
||||
ItemStorageKind.SeedSphereCrafting => Storages.SeedSphereCrafting,
|
||||
ItemStorageKind.SeedMountCrafting => Storages.SeedMountCrafting,
|
||||
ItemStorageKind.SeedUnmountCrafting => Storages.SeedUnmountCrafting,
|
||||
_ => throw new NotImplementedException($"Unhandled case {storage}."),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the enum value into the enum type used in the packets.
|
||||
/// </summary>
|
||||
/// <param name="reason">The enum value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static ItemPickUpRequestFailed.ItemPickUpFailReason Convert(this ItemPickFailReason reason)
|
||||
{
|
||||
return reason switch
|
||||
{
|
||||
ItemPickFailReason.General => ItemPickUpRequestFailed.ItemPickUpFailReason.General,
|
||||
ItemPickFailReason.ItemStacked => ItemPickUpRequestFailed.ItemPickUpFailReason.ItemStacked,
|
||||
ItemPickFailReason.MaximumInventoryMoneyReached => ItemPickUpRequestFailed.ItemPickUpFailReason.__MaximumInventoryMoneyReached,
|
||||
_ => throw new NotImplementedException($"Unhandled case {reason}"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the enum value into the enum type used in the packets.
|
||||
/// </summary>
|
||||
/// <param name="result">The enum value.</param>
|
||||
/// <returns>The converted value.</returns>
|
||||
public static PlayerShopSetItemPriceResponse.ItemPriceSetResult Convert(this ItemPriceResult result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
ItemPriceResult.Failed => PlayerShopSetItemPriceResponse.ItemPriceSetResult.Failed,
|
||||
ItemPriceResult.Success => PlayerShopSetItemPriceResponse.ItemPriceSetResult.Success,
|
||||
ItemPriceResult.ItemSlotOutOfRange => PlayerShopSetItemPriceResponse.ItemPriceSetResult.ItemSlotOutOfRange,
|
||||
ItemPriceResult.ItemNotFound => PlayerShopSetItemPriceResponse.ItemPriceSetResult.ItemNotFound,
|
||||
ItemPriceResult.PriceNegative => PlayerShopSetItemPriceResponse.ItemPriceSetResult.PriceNegative,
|
||||
ItemPriceResult.ItemIsBlocked => PlayerShopSetItemPriceResponse.ItemPriceSetResult.ItemIsBlocked,
|
||||
ItemPriceResult.CharacterLevelTooLow => PlayerShopSetItemPriceResponse.ItemPriceSetResult.CharacterLevelTooLow,
|
||||
_ => throw new NotImplementedException($"Unhandled case {result}."),
|
||||
};
|
||||
}
|
||||
}
|
||||
57
src/GameServer/RemoteView/Inventory/ItemAppearPlugIn.cs
Normal file
57
src/GameServer/RemoteView/Inventory/ItemAppearPlugIn.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// <copyright file="ItemAppearPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemAppearPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemAppearPlugIn_Name), Description = nameof(PlugInResources.ItemAppearPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("525105ee-c1bf-4800-b80b-bdcd6c8ce704")]
|
||||
public class ItemAppearPlugIn : IItemAppearPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemAppearPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemAppearPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ItemAppearAsync(Item newItem)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var itemSerializer = this._player.ItemSerializer;
|
||||
var size = ItemAddedToInventoryRef.GetRequiredSize(itemSerializer.NeededSpace);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new ItemAddedToInventoryRef(span)
|
||||
{
|
||||
InventorySlot = newItem.ItemSlot,
|
||||
};
|
||||
var itemSize = itemSerializer.SerializeItem(packet.ItemData, newItem);
|
||||
|
||||
var actualSize = ItemAddedToInventoryRef.GetRequiredSize(itemSize);
|
||||
span.Slice(0, actualSize).SetPacketSize();
|
||||
return actualSize;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Inventory/ItemDropResultPlugIn.cs
Normal file
33
src/GameServer/RemoteView/Inventory/ItemDropResultPlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ItemDropResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemDropResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemDropResultPlugIn_Name), Description = nameof(PlugInResources.ItemDropResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("377cd4cb-7334-4c74-a165-058e6bb46baf")]
|
||||
public class ItemDropResultPlugIn : IItemDropResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemDropResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemDropResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ItemDropResultAsync(byte slot, bool success)
|
||||
{
|
||||
await this._player.Connection.SendItemDropResponseAsync(success, slot).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// <copyright file="ItemDurabilityChangedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemDurabilityChangedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemDurabilityChangedPlugIn_Name), Description = nameof(PlugInResources.ItemDurabilityChangedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("f121286f-2e43-4a66-8f34-5dbe69304e1e")]
|
||||
public class ItemDurabilityChangedPlugIn : IItemDurabilityChangedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemDurabilityChangedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemDurabilityChangedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ItemDurabilityChangedAsync(Item item, bool afterConsumption)
|
||||
{
|
||||
await this._player.Connection.SendItemDurabilityChangedAsync(item.ItemSlot, item.Durability(), afterConsumption).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
58
src/GameServer/RemoteView/Inventory/ItemMoveFailedPlugIn.cs
Normal file
58
src/GameServer/RemoteView/Inventory/ItemMoveFailedPlugIn.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
// <copyright file="ItemMoveFailedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemMoveFailedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemMoveFailedPlugIn_Name), Description = nameof(PlugInResources.ItemMoveFailedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("7fc3b870-a6a2-4751-bfa7-156ed97a1c87")]
|
||||
public class ItemMoveFailedPlugIn : IItemMoveFailedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemMoveFailedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemMoveFailedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ItemMoveFailedAsync(Item? item)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var itemSerializer = this._player.ItemSerializer;
|
||||
var size = ItemMoveRequestFailedRef.GetRequiredSize(itemSerializer.NeededSpace);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new ItemMoveRequestFailedRef(span);
|
||||
if (item != null)
|
||||
{
|
||||
var itemSize = itemSerializer.SerializeItem(packet.ItemData, item);
|
||||
var actualSize = ItemMoveRequestFailedRef.GetRequiredSize(itemSize);
|
||||
span.Slice(0, actualSize).SetPacketSize();
|
||||
return actualSize;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
66
src/GameServer/RemoteView/Inventory/ItemMovedPlugIn.cs
Normal file
66
src/GameServer/RemoteView/Inventory/ItemMovedPlugIn.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
// <copyright file="ItemMovedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemMovedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemMovedPlugIn_Name), Description = nameof(PlugInResources.ItemMovedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("5c4c20fe-763d-42b6-bdfa-2ec943b191bc")]
|
||||
public class ItemMovedPlugIn : IItemMovedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemMovedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemMovedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ItemMovedAsync(Item item, byte toSlot, Storages storage)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var itemSerializer = this._player.ItemSerializer;
|
||||
var targetStorage = storage.Convert();
|
||||
if (targetStorage == ItemStorageKind.PlayerShop)
|
||||
{
|
||||
targetStorage = ItemStorageKind.Inventory;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = ItemMovedRef.GetRequiredSize(itemSerializer.NeededSpace);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var message = new ItemMovedRef(span)
|
||||
{
|
||||
TargetStorageType = targetStorage,
|
||||
TargetSlot = toSlot,
|
||||
};
|
||||
var itemSize = itemSerializer.SerializeItem(message.ItemData, item);
|
||||
|
||||
var actualSize = ItemMovedRef.GetRequiredSize(itemSize);
|
||||
span.Slice(0, actualSize).SetPacketSize();
|
||||
return actualSize;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ItemPickUpFailedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemPickUpFailedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemPickUpFailedPlugIn_Name), Description = nameof(PlugInResources.ItemPickUpFailedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("f73a2cee-14bb-4404-a321-767f848e3571")]
|
||||
public class ItemPickUpFailedPlugIn : IItemPickUpFailedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemPickUpFailedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemPickUpFailedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ItemPickUpFailedAsync(ItemPickFailReason reason)
|
||||
{
|
||||
await this._player.Connection.SendItemPickUpRequestFailedAsync(reason.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// <copyright file="ItemPriceSetResponsePlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.GameLogic.Views.PlayerShop;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemPriceSetResponsePlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemPriceSetResponsePlugIn_Name), Description = nameof(PlugInResources.ItemPriceSetResponsePlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("f83daf12-28cb-47bc-bb23-7f8eba21c97c")]
|
||||
public class ItemPriceSetResponsePlugIn : IItemPriceSetResponsePlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemPriceSetResponsePlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemPriceSetResponsePlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ItemPriceSetResponseAsync(byte itemSlot, ItemPriceResult result)
|
||||
{
|
||||
await this._player.Connection.SendPlayerShopSetItemPriceResponseAsync(itemSlot, result.Convert()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Inventory/ItemRemovedPlugIn.cs
Normal file
33
src/GameServer/RemoteView/Inventory/ItemRemovedPlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ItemRemovedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemRemovedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemRemovedPlugIn_Name), Description = nameof(PlugInResources.ItemRemovedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("06f1b02c-32b5-4d88-8d09-719246c8ebfe")]
|
||||
public class ItemRemovedPlugIn : IItemRemovedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemRemovedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemRemovedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask RemoveItemAsync(byte inventorySlot)
|
||||
{
|
||||
await this._player.Connection.SendItemRemovedAsync(inventorySlot).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// <copyright file="ItemSoldByPlayerShopPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemSoldByPlayerShopPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemSoldByPlayerShopPlugIn_Name), Description = nameof(PlugInResources.ItemSoldByPlayerShopPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("9594f5db-53b3-491f-a99c-11554c077942")]
|
||||
public class ItemSoldByPlayerShopPlugIn : IItemSoldByPlayerShopPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemSoldByPlayerShopPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemSoldByPlayerShopPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ItemSoldByPlayerShopAsync(byte slot, Player buyer)
|
||||
{
|
||||
await this._player.Connection.SendPlayerShopItemSoldToPlayerAsync(slot, buyer.SelectedCharacter!.Name).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Inventory/ItemSoldToNpcPlugIn.cs
Normal file
33
src/GameServer/RemoteView/Inventory/ItemSoldToNpcPlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="ItemSoldToNpcPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemSoldToNpcPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemSoldToNpcPlugIn_Name), Description = nameof(PlugInResources.ItemSoldToNpcPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("8372476a-7fb9-4f6e-a857-41c39c7d377c")]
|
||||
public class ItemSoldToNpcPlugIn : IItemSoldToNpcPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemSoldToNpcPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemSoldToNpcPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ItemSoldToNpcAsync(bool success)
|
||||
{
|
||||
await this._player.Connection.SendNpcItemSellResultAsync(success, (uint)this._player.Money).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
56
src/GameServer/RemoteView/Inventory/ItemUpgradedPlugIn.cs
Normal file
56
src/GameServer/RemoteView/Inventory/ItemUpgradedPlugIn.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
// <copyright file="ItemUpgradedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IItemUpgradedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.ItemUpgradedPlugIn_Name), Description = nameof(PlugInResources.ItemUpgradedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("ce4ed0a2-ec4e-4cbe-aabe-5573df86a659")]
|
||||
public class ItemUpgradedPlugIn : IItemUpgradedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemUpgradedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public ItemUpgradedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask ItemUpgradedAsync(Item item)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Write()
|
||||
{
|
||||
var itemSerializer = this._player.ItemSerializer;
|
||||
var size = InventoryItemUpgradedRef.GetRequiredSize(itemSerializer.NeededSpace);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new InventoryItemUpgradedRef(span)
|
||||
{
|
||||
InventorySlot = item.ItemSlot,
|
||||
};
|
||||
var itemSize = itemSerializer.SerializeItem(packet.ItemData, item);
|
||||
var actualSize = InventoryItemUpgradedRef.GetRequiredSize(itemSize);
|
||||
span.Slice(0, actualSize).SetPacketSize();
|
||||
return actualSize;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
57
src/GameServer/RemoteView/Inventory/NpcItemBoughtPlugIn.cs
Normal file
57
src/GameServer/RemoteView/Inventory/NpcItemBoughtPlugIn.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// <copyright file="NpcItemBoughtPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="INpcItemBoughtPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.NpcItemBoughtPlugIn_Name), Description = nameof(PlugInResources.NpcItemBoughtPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("cf45b5e2-158a-4998-bc73-fed4d4d31c0c")]
|
||||
public class NpcItemBoughtPlugIn : INpcItemBoughtPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NpcItemBoughtPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public NpcItemBoughtPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask NpcItemBoughtAsync(Item newItem)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var itemSerializer = this._player.ItemSerializer;
|
||||
|
||||
int Write()
|
||||
{
|
||||
var size = ItemBoughtRef.GetRequiredSize(itemSerializer.NeededSpace);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new ItemBoughtRef(span)
|
||||
{
|
||||
InventorySlot = newItem.ItemSlot,
|
||||
};
|
||||
var itemSize = itemSerializer.SerializeItem(packet.ItemData, newItem);
|
||||
var actualSize = ItemBoughtRef.GetRequiredSize(itemSize);
|
||||
span.Slice(0, actualSize).SetPacketSize();
|
||||
return actualSize;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// <copyright file="PlayerShopBuyRequestResultExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IPlayerShopBuyRequestResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.PlayerShopBuyRequestResultExtendedPlugIn_Name), Description = nameof(PlugInResources.PlayerShopBuyRequestResultExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("83C89473-5977-4E08-82CF-94BC68C50676")]
|
||||
[MinimumClient(106, 3, ClientLanguage.Invariant)]
|
||||
public class PlayerShopBuyRequestResultExtendedPlugIn : IPlayerShopBuyRequestResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerShopBuyRequestResultExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public PlayerShopBuyRequestResultExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowResultAsync(IIdentifiable? seller, ItemBuyResult result, Item? item)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
var itemSerializer = this._player.ItemSerializer;
|
||||
var array = new byte[itemSerializer.NeededSpace];
|
||||
itemSerializer.SerializeItem(array, item);
|
||||
await connection.SendPlayerShopBuyResultExtendedAsync(seller?.GetId(this._player) ?? ushort.MaxValue, Convert(result), item.ItemSlot, array).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendPlayerShopBuyResultExtendedAsync(seller?.GetId(this._player) ?? ushort.MaxValue, Convert(result), 0, Array.Empty<byte>()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static PlayerShopBuyResultExtended.ResultKind Convert(ItemBuyResult result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
ItemBuyResult.Success => PlayerShopBuyResultExtended.ResultKind.Success,
|
||||
ItemBuyResult.NotAvailable => PlayerShopBuyResultExtended.ResultKind.NotAvailable,
|
||||
ItemBuyResult.ShopNotOpened => PlayerShopBuyResultExtended.ResultKind.ShopNotOpened,
|
||||
ItemBuyResult.InTransaction => PlayerShopBuyResultExtended.ResultKind.InTransaction,
|
||||
ItemBuyResult.InvalidShopSlot => PlayerShopBuyResultExtended.ResultKind.InvalidShopSlot,
|
||||
ItemBuyResult.NameMismatchOrPriceMissing => PlayerShopBuyResultExtended.ResultKind.NameMismatchOrPriceMissing,
|
||||
ItemBuyResult.LackOfMoney => PlayerShopBuyResultExtended.ResultKind.LackOfMoney,
|
||||
ItemBuyResult.MoneyOverflowOrNotEnoughSpace => PlayerShopBuyResultExtended.ResultKind.MoneyOverflowOrNotEnoughSpace,
|
||||
ItemBuyResult.ItemBlock => PlayerShopBuyResultExtended.ResultKind.ItemBlock,
|
||||
_ => PlayerShopBuyResultExtended.ResultKind.Undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// <copyright file="PlayerShopBuyRequestResultPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic;
|
||||
using MUnique.OpenMU.GameLogic.Views;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IPlayerShopBuyRequestResultPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.PlayerShopBuyRequestResultPlugIn_Name), Description = nameof(PlugInResources.PlayerShopBuyRequestResultPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("3b2498f2-3ae8-4700-8a61-1ffe49822caf")]
|
||||
public class PlayerShopBuyRequestResultPlugIn : IPlayerShopBuyRequestResultPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayerShopBuyRequestResultPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public PlayerShopBuyRequestResultPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask ShowResultAsync(IIdentifiable? seller, ItemBuyResult result, Item? item)
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (item is not null)
|
||||
{
|
||||
var itemSerializer = this._player.ItemSerializer;
|
||||
var array = new byte[itemSerializer.NeededSpace];
|
||||
itemSerializer.SerializeItem(array, item);
|
||||
await connection.SendPlayerShopBuyResultAsync(Convert(result), seller?.GetId(this._player) ?? ushort.MaxValue, array, item.ItemSlot).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.SendPlayerShopBuyResultAsync(Convert(result), seller?.GetId(this._player) ?? ushort.MaxValue, Array.Empty<byte>(), 0).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static PlayerShopBuyResult.ResultKind Convert(ItemBuyResult result)
|
||||
{
|
||||
return result switch
|
||||
{
|
||||
ItemBuyResult.Success => PlayerShopBuyResult.ResultKind.Success,
|
||||
ItemBuyResult.NotAvailable => PlayerShopBuyResult.ResultKind.NotAvailable,
|
||||
ItemBuyResult.ShopNotOpened => PlayerShopBuyResult.ResultKind.ShopNotOpened,
|
||||
ItemBuyResult.InTransaction => PlayerShopBuyResult.ResultKind.InTransaction,
|
||||
ItemBuyResult.InvalidShopSlot => PlayerShopBuyResult.ResultKind.InvalidShopSlot,
|
||||
ItemBuyResult.NameMismatchOrPriceMissing => PlayerShopBuyResult.ResultKind.NameMismatchOrPriceMissing,
|
||||
ItemBuyResult.LackOfMoney => PlayerShopBuyResult.ResultKind.LackOfMoney,
|
||||
ItemBuyResult.MoneyOverflowOrNotEnoughSpace => PlayerShopBuyResult.ResultKind.MoneyOverflowOrNotEnoughSpace,
|
||||
ItemBuyResult.ItemBlock => PlayerShopBuyResult.ResultKind.ItemBlock,
|
||||
_ => PlayerShopBuyResult.ResultKind.Undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// <copyright file="RequestedItemConsumptionFailedExtendedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.Network.PlugIns;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The extended implementation of the <see cref="IRequestedItemConsumptionFailedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.RequestedItemConsumptionFailedExtendedPlugIn_Name), Description = nameof(PlugInResources.RequestedItemConsumptionFailedExtendedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("8F98AAF4-E329-4DE2-B8D5-9169B64E20B2")]
|
||||
[MinimumClient(106, 3, ClientLanguage.English)]
|
||||
public class RequestedItemConsumptionFailedExtendedPlugIn : IRequestedItemConsumptionFailedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestedItemConsumptionFailedExtendedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public RequestedItemConsumptionFailedExtendedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>The server sends the current health/shield to the client, with <see cref="ItemConsumptionFailed"/>.</remarks>
|
||||
public async ValueTask RequestedItemConsumptionFailedAsync()
|
||||
{
|
||||
if (this._player.Attributes is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await this._player.Connection.SendItemConsumptionFailedExtendedAsync(
|
||||
(uint)Math.Max(this._player.Attributes[Stats.CurrentHealth], 0f),
|
||||
(uint)Math.Max(this._player.Attributes[Stats.CurrentShield], 0f))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// <copyright file="RequestedItemConsumptionFailedPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Attributes;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IRequestedItemConsumptionFailedPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.RequestedItemConsumptionFailedPlugIn_Name), Description = nameof(PlugInResources.RequestedItemConsumptionFailedPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("c3a03a1c-71c7-4581-a244-0b1b31497f05")]
|
||||
public class RequestedItemConsumptionFailedPlugIn : IRequestedItemConsumptionFailedPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestedItemConsumptionFailedPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public RequestedItemConsumptionFailedPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>The server sends the current health/shield to the client, with <see cref="ItemConsumptionFailed"/>.</remarks>
|
||||
public async ValueTask RequestedItemConsumptionFailedAsync()
|
||||
{
|
||||
if (this._player.Attributes is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await this._player.Connection.SendItemConsumptionFailedAsync(
|
||||
(ushort)Math.Max(this._player.Attributes[Stats.CurrentHealth], 0f),
|
||||
(ushort)Math.Max(this._player.Attributes[Stats.CurrentShield], 0f))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// <copyright file="UpdateInventoryListPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateInventoryListPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateInventoryListPlugIn_Name), Description = nameof(PlugInResources.UpdateInventoryListPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("ba8ca7c7-a497-497e-b2f7-9f9366ff6ac5")]
|
||||
public class UpdateInventoryListPlugIn : IUpdateInventoryListPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateInventoryListPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateInventoryListPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateInventoryListAsync()
|
||||
{
|
||||
var connection = this._player.Connection;
|
||||
if (connection is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// C4 00 00 00 F3 10 ...
|
||||
var items = (this._player.Inventory?.Items is { } inventoryItems
|
||||
? inventoryItems.Concat(this._player.ShopStorage?.Items ?? Enumerable.Empty<Item>())
|
||||
: this._player.SelectedCharacter?.Inventory?.Items ?? Enumerable.Empty<Item>())
|
||||
.OrderBy(item => item.ItemSlot)
|
||||
.ToList();
|
||||
int Write()
|
||||
{
|
||||
var itemSerializer = this._player.ItemSerializer;
|
||||
var lengthPerItem = StoredItemRef.GetRequiredSize(itemSerializer.NeededSpace);
|
||||
var size = CharacterInventoryRef.GetRequiredSize(items.Count, lengthPerItem);
|
||||
var span = connection.Output.GetSpan(size)[..size];
|
||||
var packet = new CharacterInventoryRef(span)
|
||||
{
|
||||
ItemCount = 0,
|
||||
};
|
||||
|
||||
int headerSize = CharacterInventoryRef.GetRequiredSize(0, 0);
|
||||
int actualSize = headerSize;
|
||||
var seenSlots = new HashSet<byte>();
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Definition is null)
|
||||
{
|
||||
this._player.Logger.LogWarning("Item {0} has no definition.", item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!seenSlots.Add(item.ItemSlot))
|
||||
{
|
||||
this._player.Logger.LogWarning(
|
||||
"Duplicate item slot {Slot} detected in inventory list update for player {Player}. Skipping item {Item}.",
|
||||
item.ItemSlot,
|
||||
this._player,
|
||||
item);
|
||||
continue;
|
||||
}
|
||||
|
||||
var storedItem = new StoredItemRef(span[actualSize..]);
|
||||
storedItem.ItemSlot = item.ItemSlot;
|
||||
var itemSize = itemSerializer.SerializeItem(storedItem.ItemData, item);
|
||||
actualSize += StoredItemRef.GetRequiredSize(itemSize);
|
||||
packet.ItemCount++;
|
||||
}
|
||||
|
||||
span.Slice(0, actualSize).SetPacketSize();
|
||||
return actualSize;
|
||||
}
|
||||
|
||||
await connection.SendAsync(Write).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
33
src/GameServer/RemoteView/Inventory/UpdateMoneyPlugIn.cs
Normal file
33
src/GameServer/RemoteView/Inventory/UpdateMoneyPlugIn.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// <copyright file="UpdateMoneyPlugIn.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView.Inventory;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using MUnique.OpenMU.GameLogic.Views.Inventory;
|
||||
using MUnique.OpenMU.Network.Packets.ServerToClient;
|
||||
using MUnique.OpenMU.PlugIns;
|
||||
|
||||
/// <summary>
|
||||
/// The default implementation of the <see cref="IUpdateMoneyPlugIn"/> which is forwarding everything to the game client with specific data packets.
|
||||
/// </summary>
|
||||
[PlugIn]
|
||||
[Display(Name = nameof(PlugInResources.UpdateMoneyPlugIn_Name), Description = nameof(PlugInResources.UpdateMoneyPlugIn_Description), ResourceType = typeof(PlugInResources))]
|
||||
[Guid("7a13a613-7098-4407-8ef5-39bae08ce12d")]
|
||||
public class UpdateMoneyPlugIn : IUpdateMoneyPlugIn
|
||||
{
|
||||
private readonly RemotePlayer _player;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UpdateMoneyPlugIn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="player">The player.</param>
|
||||
public UpdateMoneyPlugIn(RemotePlayer player) => this._player = player;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask UpdateMoneyAsync()
|
||||
{
|
||||
await this._player.Connection.SendInventoryMoneyUpdateAsync((uint)this._player.Money).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
29
src/GameServer/RemoteView/ItemExtensions.cs
Normal file
29
src/GameServer/RemoteView/ItemExtensions.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// <copyright file="ItemExtensions.cs" company="MUnique">
|
||||
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
|
||||
// </copyright>
|
||||
|
||||
namespace MUnique.OpenMU.GameServer.RemoteView;
|
||||
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Message relevant extensions for items.
|
||||
/// </summary>
|
||||
public static class ItemExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the glow level of the item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>The glow level of the item.</returns>
|
||||
public static byte GetGlowLevel(this Item item) => GetGlowLevel(item.Level);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the glow level of the item.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <returns>The glow level of the item.</returns>
|
||||
public static byte GetGlowLevel(this ItemAppearance item) => GetGlowLevel(item.Level);
|
||||
|
||||
private static byte GetGlowLevel(int itemLevel) => (byte)((itemLevel - 1) / 2);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user