diff --git a/.run/Server_Dockerfile.run.xml b/.run/Server_Dockerfile.run.xml
new file mode 100644
index 0000000..a509d2d
--- /dev/null
+++ b/.run/Server_Dockerfile.run.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 70b1290..948de68 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,6 @@
# What is this project about?
This is a continuation and rewrite of the original Project Infinity for Wonderking.
+
+## Important notes for developers
+
+* Avoid using statements with TcpSession, AuthSession or any type that inherits Session outside the session itself.
diff --git a/Server/AuthSession.cs b/Server/AuthSession.cs
index ae0f4c9..788c4c3 100644
--- a/Server/AuthSession.cs
+++ b/Server/AuthSession.cs
@@ -29,14 +29,14 @@ public class AuthSession : TcpSession
return base.Send(buffer);
}
- public void Send(IPacket packet)
+ public Task SendAsync(IPacket packet)
{
var type = packet.GetType();
this._logger.LogInformation("Packet of type {Type} is being serialized", type.Name);
var packetIdAttribute = type.GetCustomAttribute();
if (packetIdAttribute == null)
{
- return;
+ return Task.CompletedTask;
}
var opcode = packetIdAttribute.Code;
@@ -63,7 +63,8 @@ public class AuthSession : TcpSession
this._logger.LogInformation("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray()));
this._logger.LogInformation("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray()));
- this.Send(buffer);
+ this.SendAsync(buffer);
+ return Task.CompletedTask;
}
protected override void OnReceived(byte[] buffer, long offset, long size)
diff --git a/Server/PacketHandlers/ChannelSelectionHandler.cs b/Server/PacketHandlers/ChannelSelectionHandler.cs
index 9e474ff..0a399a9 100644
--- a/Server/PacketHandlers/ChannelSelectionHandler.cs
+++ b/Server/PacketHandlers/ChannelSelectionHandler.cs
@@ -39,7 +39,7 @@ public class ChannelSelectionHandler : IPacketHandler
{
ChannelIsFullFlag = 0,
Endpoint = "127.0.0.1",
- Port = 12345,
+ Port = 2000,
Characters = await _wonderkingContext.Characters.AsNoTracking()
.Where(c => c.Account.Id == authSession.AccountId)
.Select(c =>
@@ -54,20 +54,19 @@ public class ChannelSelectionHandler : IPacketHandler
Stats = c.BaseStats,
Health = c.Health,
Mana = c.Mana,
- EquippedItems =
- c.InventoryItems.Where(item => item.InventoryTab == InventoryTab.WornEquipment)
- .Select(item => item.ItemId)
- .ToArray(),
- EquippedCashItems = c.InventoryItems
+ EquippedItems = GetItemIDsByInventoryTab(c.InventoryItems
+ .Where(item => item.InventoryTab == InventoryTab.WornEquipment)
+ .Select(item => new Tuple(item.ItemId, item.Slot)).ToArray()),
+ EquippedCashItems = GetItemIDsByInventoryTab(c.InventoryItems
.Where(item => item.InventoryTab == InventoryTab.WornCashEquipment)
- .Select(item => item.ItemId)
- .ToArray()
+ .Select(item => new Tuple(item.ItemId, item.Slot)).ToArray()),
})
.ToArrayAsync().ConfigureAwait(true),
};
guildNameResponsePacket.GuildNames = await _wonderkingContext.Characters
.Where(c => c.Account.Id == authSession.AccountId)
+ .Where(c => c.Guild != null)
.Select(character => character.Guild.Name).ToArrayAsync().ConfigureAwait(true);
}
else
@@ -81,11 +80,23 @@ public class ChannelSelectionHandler : IPacketHandler
};
}
- authSession.Send(responsePacket);
+ await authSession.SendAsync(responsePacket).ConfigureAwait(false);
if (guildNameResponsePacket.GuildNames.Length > 0 &&
guildNameResponsePacket.GuildNames.Select(n => n != string.Empty).Any())
{
- authSession.Send(guildNameResponsePacket);
+ await authSession.SendAsync(guildNameResponsePacket).ConfigureAwait(false);
}
}
+
+ private static ushort[] GetItemIDsByInventoryTab(Tuple[] items)
+ {
+ var ids = new ushort[20];
+
+ for (var i = 0; i < 20; i++)
+ {
+ ids[i] = items.FirstOrDefault(item => item.Item2 == i)?.Item1 ?? 0;
+ }
+
+ return ids;
+ }
}
diff --git a/Server/PacketHandlers/CharacterCreationHandler.cs b/Server/PacketHandlers/CharacterCreationHandler.cs
index 7e9dcb4..9a1cbf1 100644
--- a/Server/PacketHandlers/CharacterCreationHandler.cs
+++ b/Server/PacketHandlers/CharacterCreationHandler.cs
@@ -28,8 +28,13 @@ public class CharacterCreationHandler : IPacketHandler
public async Task HandleAsync(CharacterCreationPacket packet, TcpSession session)
{
var authSession = session as AuthSession;
+ if (authSession is null)
+ {
+ return;
+ }
+
var account =
- _wonderkingContext.Accounts.FirstOrDefault(a => authSession != null && a.Id == authSession.AccountId);
+ _wonderkingContext.Accounts.FirstOrDefault(a => a.Id == authSession.AccountId);
var mappedDefaultItems = _characterStatsMapping.DefaultCharacterMapping.Items
.Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray();
@@ -83,10 +88,10 @@ public class CharacterCreationHandler : IPacketHandler
await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
var amountOfCharacters = await _wonderkingContext.Characters.AsNoTrackingWithIdentityResolution()
- .CountAsync(c => authSession != null && c.Account.Id == authSession.AccountId).ConfigureAwait(true);
+ .CountAsync(c => c.Account.Id == authSession.AccountId).ConfigureAwait(true);
var character = await _wonderkingContext.Characters.AsNoTrackingWithIdentityResolution()
- .Where(c => authSession != null && c.Account.Id == authSession.AccountId && c.Name == packet.Name)
+ .Where(c => c.Account.Id == authSession.AccountId && c.Name == packet.Name)
.Select(c =>
new CharacterData
{
@@ -99,20 +104,19 @@ public class CharacterCreationHandler : IPacketHandler
Health = c.Health,
Mana = c.Mana,
EquippedItems =
- c.InventoryItems.Where(item => item.InventoryTab == InventoryTab.WornEquipment)
- .Select(item => item.ItemId)
- .ToArray(),
- EquippedCashItems = c.InventoryItems
+ GetItemIDsByInventoryTab(c.InventoryItems
+ .Where(item => item.InventoryTab == InventoryTab.WornEquipment)
+ .Select(item => new Tuple(item.ItemId, item.Slot)).ToArray()),
+ EquippedCashItems = GetItemIDsByInventoryTab(c.InventoryItems
.Where(item => item.InventoryTab == InventoryTab.WornCashEquipment)
- .Select(item => item.ItemId)
- .ToArray(),
+ .Select(item => new Tuple(item.ItemId, item.Slot)).ToArray()),
}).FirstAsync().ConfigureAwait(true);
- authSession?.Send(new CharacterCreationResponsePacket
+ await authSession.SendAsync(new CharacterCreationResponsePacket
{
Character = character,
- Slot = amountOfCharacters - 1,
+ Slot = packet.Slot,
isDuplicate = false,
- });
+ }).ConfigureAwait(false);
}
private static int CalculateCurrentHealth(ushort level, JobSpecificMapping firstJobConfig)
@@ -126,4 +130,16 @@ public class CharacterCreationHandler : IPacketHandler
return (int)((level - 1) * firstJobConfig.DynamicStats.ManaPerLevel +
firstJobConfig.BaseStats.Wisdom * firstJobConfig.DynamicStats.ManaPerWisdom);
}
+
+ private static ushort[] GetItemIDsByInventoryTab(Tuple[] items)
+ {
+ var ids = new ushort[20];
+
+ for (var i = 0; i < 20; i++)
+ {
+ ids[i] = items.FirstOrDefault(item => item.Item2 == i)?.Item1 ?? 0;
+ }
+
+ return ids;
+ }
}
diff --git a/Server/PacketHandlers/CharacterDeletionHandler.cs b/Server/PacketHandlers/CharacterDeletionHandler.cs
index 920131a..3807d46 100644
--- a/Server/PacketHandlers/CharacterDeletionHandler.cs
+++ b/Server/PacketHandlers/CharacterDeletionHandler.cs
@@ -17,8 +17,7 @@ public class CharacterDeletionHandler : IPacketHandler
public async Task HandleAsync(CharacterDeletePacket packet, TcpSession session)
{
- using var authSession = session as AuthSession;
- if (authSession == null)
+ if (session is not AuthSession authSession)
{
session.Disconnect();
return;
@@ -30,13 +29,13 @@ public class CharacterDeletionHandler : IPacketHandler
var response = new CharacterDeleteResponsePacket { IsDeleted = 0 };
if (character == null)
{
- authSession.Send(response);
+ await authSession.SendAsync(response).ConfigureAwait(false);
return;
}
_wonderkingContext.Characters.Remove(character);
await _wonderkingContext.SaveChangesAsync().ConfigureAwait(false);
- authSession.Send(response);
+ await authSession.SendAsync(response).ConfigureAwait(false);
}
}
diff --git a/Server/PacketHandlers/CharacterNameCheckHandler.cs b/Server/PacketHandlers/CharacterNameCheckHandler.cs
index c97af26..7804da6 100644
--- a/Server/PacketHandlers/CharacterNameCheckHandler.cs
+++ b/Server/PacketHandlers/CharacterNameCheckHandler.cs
@@ -18,8 +18,11 @@ public class CharacterNameCheckHandler : IPacketHandler c.Name == packet.Name);
var responsePacket = new CharacterNameCheckPacketResponse { IsTaken = isTaken };
- var authSession = session as AuthSession;
- authSession?.Send(responsePacket);
+ if (session is AuthSession authSession)
+ {
+ return authSession.SendAsync(responsePacket);
+ }
+
return Task.CompletedTask;
}
}
diff --git a/Server/PacketHandlers/LoginHandler.cs b/Server/PacketHandlers/LoginHandler.cs
index 1d60f1b..86f57ae 100644
--- a/Server/PacketHandlers/LoginHandler.cs
+++ b/Server/PacketHandlers/LoginHandler.cs
@@ -119,6 +119,6 @@ public class LoginHandler : IPacketHandler
}
_logger.LogInformation("LoginResponsePacket: {@LoginResponsePacket}", loginResponsePacket);
- sess?.Send(loginResponsePacket);
+ sess?.SendAsync(loginResponsePacket);
}
}
diff --git a/Server/Program.cs b/Server/Program.cs
index e203b3b..0c67d9b 100644
--- a/Server/Program.cs
+++ b/Server/Program.cs
@@ -42,7 +42,8 @@ builder.Services.AddDbContextPool(o =>
builder.Services.AddSingleton(loggerFactory);
builder.Services.AddSingleton();
builder.Services.AddSingleton();
-builder.Services.AddHostedService();
+builder.Services.AddHostedService(provider =>
+ provider.GetService() ?? throw new InvalidOperationException());
builder.Services.AddHostedService(provider =>
provider.GetService() ?? throw new InvalidOperationException());
builder.Services.AddMassTransit(x =>
diff --git a/Server/Server.csproj b/Server/Server.csproj
index 5745021..95af43f 100644
--- a/Server/Server.csproj
+++ b/Server/Server.csproj
@@ -8,7 +8,6 @@
Server
default
true
- net8.0
true
strict
Timothy (RaiNote) Schenk
@@ -20,6 +19,7 @@
True
LICENSE
latest-recommended
+ net8.0
@@ -82,7 +82,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Server/Services/ItemObjectPoolService.cs b/Server/Services/ItemObjectPoolService.cs
index 6b190e5..69cdfbe 100644
--- a/Server/Services/ItemObjectPoolService.cs
+++ b/Server/Services/ItemObjectPoolService.cs
@@ -71,8 +71,18 @@ public class ItemObjectPoolService : IHostedService
{
ItemId = itemId,
Count = count,
- Slot = 0,
- InventoryTab = InventoryTab.WornEquipment,
+ Slot = (byte)item.SlotNo1,
+ InventoryTab =
+ item.ItemType switch
+ {
+ 1 => InventoryTab.WornCashEquipment,
+ 2 => isWorn ? InventoryTab.WornEquipment : InventoryTab.Equipment,
+ 3 => InventoryTab.Etc,
+ 4 => isWorn ? InventoryTab.WornCashEquipment : InventoryTab.Cash,
+ 5 => InventoryTab.Warehouse,
+ 0 => InventoryTab.WornEquipment,
+ _ => 0
+ },
Level = item.MinimumLevelRequirement,
Rarity = 0,
AddOption = 0,
diff --git a/Server/Services/WonderkingAuthServer.cs b/Server/Services/WonderkingAuthServer.cs
index 30ae4e8..7c79c80 100644
--- a/Server/Services/WonderkingAuthServer.cs
+++ b/Server/Services/WonderkingAuthServer.cs
@@ -27,6 +27,7 @@ public class WonderkingAuthServer : TcpServer, IHostedService
public Task StopAsync(CancellationToken cancellationToken)
{
+ this.DisconnectAll();
this.Stop();
return Task.CompletedTask;
}
diff --git a/Wonderking/Packets/Outgoing/ChannelSelectionResponsePacket.cs b/Wonderking/Packets/Outgoing/ChannelSelectionResponsePacket.cs
index 12b93fc..3245cb2 100644
--- a/Wonderking/Packets/Outgoing/ChannelSelectionResponsePacket.cs
+++ b/Wonderking/Packets/Outgoing/ChannelSelectionResponsePacket.cs
@@ -29,39 +29,41 @@ public class ChannelSelectionResponsePacket : IPacket
// Character Data
for (var i = 0; i < Characters.Length; i++)
{
+ int offset = 20 + (i * 132);
var character = Characters[i];
- BinaryPrimitives.WriteInt32LittleEndian(data.Slice(20 + (i * 132), 4), i);
- Encoding.ASCII.GetBytes(character.Name, data.Slice(24 + (i * 132), 20));
+ // Character Data
+ BinaryPrimitives.WriteInt32LittleEndian(data.Slice(offset, 4), i);
+ Encoding.ASCII.GetBytes(character.Name, data.Slice(offset + 4, 20));
// Job Data
- data[44 + (i * 132)] = character.Job.FirstJob;
- data[45 + (i * 132)] = character.Job.SecondJob;
- data[46 + (i * 132)] = character.Job.ThirdJob;
- data[47 + (i * 132)] = character.Job.FourthJob;
+ data[offset + 24] = character.Job.FirstJob;
+ data[offset + 25] = character.Job.SecondJob;
+ data[offset + 26] = character.Job.ThirdJob;
+ data[offset + 27] = character.Job.FourthJob;
- data[48 + (i * 132)] = (byte)character.Gender;
- BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(49 + (i * 132), 2), character.Level);
- data[51 + (i * 132)] = (byte)character.Experience;
+ data[offset + 28] = (byte)character.Gender;
+ BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(offset + 29, 2), character.Level);
+ data[offset + 31] = (byte)character.Experience;
// Stats
- BinaryPrimitives.WriteInt16LittleEndian(data.Slice(52 + (i * 132), 2), character.Stats.Strength);
- BinaryPrimitives.WriteInt16LittleEndian(data.Slice(54 + (i * 132), 2), character.Stats.Dexterity);
- BinaryPrimitives.WriteInt16LittleEndian(data.Slice(56 + (i * 132), 2), character.Stats.Intelligence);
- BinaryPrimitives.WriteInt16LittleEndian(data.Slice(58 + (i * 132), 2), character.Stats.Vitality);
- BinaryPrimitives.WriteInt16LittleEndian(data.Slice(60 + (i * 132), 2), character.Stats.Luck);
- BinaryPrimitives.WriteInt16LittleEndian(data.Slice(62 + (i * 132), 2), character.Stats.Wisdom);
+ BinaryPrimitives.WriteInt16LittleEndian(data.Slice(offset + 32, 2), character.Stats.Strength);
+ BinaryPrimitives.WriteInt16LittleEndian(data.Slice(offset + 34, 2), character.Stats.Dexterity);
+ BinaryPrimitives.WriteInt16LittleEndian(data.Slice(offset + 36, 2), character.Stats.Intelligence);
+ BinaryPrimitives.WriteInt16LittleEndian(data.Slice(offset + 38, 2), character.Stats.Vitality);
+ BinaryPrimitives.WriteInt16LittleEndian(data.Slice(offset + 40, 2), character.Stats.Luck);
+ BinaryPrimitives.WriteInt16LittleEndian(data.Slice(offset + 42, 2), character.Stats.Wisdom);
- BinaryPrimitives.WriteInt32LittleEndian(data.Slice(64 + (i * 132), 4), character.Health);
- BinaryPrimitives.WriteInt32LittleEndian(data.Slice(68 + (i * 132), 4), character.Mana);
+ BinaryPrimitives.WriteInt32LittleEndian(data.Slice(offset + 44, 4), character.Health);
+ BinaryPrimitives.WriteInt32LittleEndian(data.Slice(offset + 48, 4), character.Mana);
for (var j = 0; j < 20; j++)
{
// Equipped Items
- BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(72 + (i * 132) + (j * 2), 2),
+ BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(offset + 52 + j * 2, 2),
character.EquippedItems.Length > j ? character.EquippedItems[j] : (ushort)0);
// Equipped Cash Items
- BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(112 + (i * 132) + (j * 2), 2),
+ BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(offset + 92 + j * 2, 2),
character.EquippedCashItems.Length > j ? character.EquippedCashItems[j] : (ushort)0);
}
}
diff --git a/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs b/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs
index 6419417..cce221c 100644
--- a/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs
+++ b/Wonderking/Packets/Outgoing/CharacterCreationResponsePacket.cs
@@ -20,6 +20,8 @@ public class CharacterCreationResponsePacket : IPacket
{
Span data = stackalloc byte[1 + 132];
data[0] = isDuplicate ? (byte)1 : (byte)0;
+
+ // Character Data
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(1, 4), Slot);
Encoding.ASCII.GetBytes(Character.Name, data.Slice(5, 20));