Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/NosCore.Packets/ClientPackets/Mates/PtctlPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

using NosCore.Packets.Attributes;
using NosCore.Packets.Enumerations;
using System.Collections.Generic;

namespace NosCore.Packets.ClientPackets.Mates
{
// ptctl 1 2 <id> <x> <y> <id> <x> <y> <id> <speed>: where each mate following the player is, then one id and
// speed (the first mate's in every recording).
[PacketHeader("ptctl", Scope.InGame)]
public class PtctlPacket : PacketBase
{
Expand All @@ -18,7 +21,13 @@ public class PtctlPacket : PacketBase
[PacketIndex(1)]
public short Amount { get; set; }

[PacketIndex(2)]
public string? PacketEnd { get; set; }
[PacketListIndex(2, Length = -1, SpecialSeparator = " ")]
public List<PtctlSubPacket?>? Mates { get; set; }

[PacketIndex(3)]
public long VisualId { get; set; }

[PacketIndex(4)]
public short Speed { get; set; }
}
}
22 changes: 22 additions & 0 deletions src/NosCore.Packets/ClientPackets/Mates/PtctlSubPacket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// __ _ __ __ ___ __ ___ ___
// | \| |/__\ /' _/ / _//__\| _ \ __|
// | | ' | \/ |`._`.| \_| \/ | v / _|
// |_|\__|\__/ |___/ \__/\__/|_|_\___|
// -----------------------------------

using NosCore.Packets.Attributes;

namespace NosCore.Packets.ClientPackets.Mates
{
public class PtctlSubPacket : PacketBase
{
[PacketIndex(0)]
public long VisualId { get; set; }

[PacketIndex(1)]
public short PositionX { get; set; }

[PacketIndex(2)]
public short PositionY { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/NosCore.Packets/NosCore.Packets.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<RepositoryUrl>https://github.com/NosCoreIO/NosCore.Packets.git</RepositoryUrl>
<PackageIconUrl></PackageIconUrl>
<PackageTags>nostale, noscore, chickenapi, nostale private server source, nostale emulator</PackageTags>
<Version>21.1.3</Version>
<Version>22.0.0</Version>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<Description>NosCore's Packets (Nostale packets) defined over classes</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand Down
4 changes: 3 additions & 1 deletion src/NosCore.Packets/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,11 @@ private Expression PacketSerializer(Expression injectedPacket, PacketIndexAttrib
incrementExpr = Expression.Constant(!isFromList || !isOptionalSerie);
}

// A sub-packet with its own separator is written after a single-space discriminator; an absent one
// keeps that space, or "13458 -1 -1" (sc_n with no equipment) runs together as "13458-1-1".
return Expression.Condition(
Expression.Equal(specificTypeExpression, Expression.Constant(null, typeof(object))),
Expression.Constant(indexAttr.IsOptional ? null : "-1", typeof(object)),
Expression.Constant(indexAttr.IsOptional ? null : discriminator == " " ? " -1" : "-1", typeof(object)),
Expression.Convert(propExp, typeof(object))
);
}
Expand Down
9 changes: 9 additions & 0 deletions src/NosCore.Packets/ServerPackets/Mates/ScnPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ public class ScnPacket : PacketBase
[PacketIndex(42)]
public ScSkillDetails? Skill3Details { get; set; }

[PacketIndex(43)]
public short Unknown4 { get; set; }

// 0 or 1 depending on the partner.
[PacketIndex(44)]
public short Unknown5 { get; set; }

public class ScSpDetails : PacketBase
{
[PacketIndex(0)]
Expand Down Expand Up @@ -242,4 +249,6 @@ public class ScSkillDetails : PacketBase
{(SkillId.SkillRank) :) // unknown 23 SpRank1
-1 // unknown 24 SpRank2
-1 // unknown 25 SpRank3
0 // unknown 26
0 // unknown 27 (0 or 1)
*/
24 changes: 24 additions & 0 deletions src/NosCore.Packets/ServerPackets/Mates/ScpPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,29 @@ public class ScpPacket : PacketBase

[PacketIndex(32)]
public bool IsSummonable { get; set; }

// 33 to 39 are always sent; with only 33 fields the client's pet window shows no pictures.
[PacketIndex(33)]
public long Unknow2 { get; set; }

// 1, 2 or 3 depending on the pet (the same value as the pet's in packet field after Unknow6).
[PacketIndex(34)]
public long Unknow3 { get; set; }

[PacketIndex(35)]
public long Unknow4 { get; set; }

[PacketIndex(36)]
public long Unknow5 { get; set; }

[PacketIndex(37)]
public long Unknow6 { get; set; }

[PacketIndex(38)]
public long Unknow7 { get; set; }

// 1 for every pet seen so far.
[PacketIndex(39)]
public long Unknow8 { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,12 @@ public class InNonPlayerSubPacket : PacketBase

[PacketIndex(22)]
public byte Unknow6 { get; set; }

// Sent for every non-player: 1, 2 or 3 for a pet (as in its sc_p), 0 for partners, npcs and monsters.
[PacketIndex(23)]
public byte Unknow7 { get; set; }

[PacketIndex(24)]
public byte Unknow8 { get; set; }
}
}
118 changes: 118 additions & 0 deletions test/NosCore.Packets.Tests/OfficialMatePacketTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// __ _ __ __ ___ __ ___ ___
// | \| |/__\ /' _/ / _//__\| _ \ __|
// | | ' | \/ |`._`.| \_| \/ | v / _|
// |_|\__|\__/ |___/ \__/\__/|_|_\___|
// -----------------------------------

using Microsoft.VisualStudio.TestTools.UnitTesting;
using NosCore.Packets.ClientPackets.Mates;
using NosCore.Packets.Enumerations;
using NosCore.Packets.Interfaces;
using NosCore.Packets.ServerPackets.Mates;
using NosCore.Packets.ServerPackets.Visibility;
using NosCore.Shared.Enumerations;
using System.Linq;

namespace NosCore.Packets.Tests
{
// The expected lines were recorded from the official server in October 2026, for a character with four pets and
// two partners. Server packets are built and compared with what that server wrote; ptctl, which the client
// sends, is read.
[TestClass]
public class OfficialMatePacketTests
{
private static readonly IDeserializer Deserializer = new Deserializer(PacketCorpus.SerializableTypes.ToList());
private static readonly ISerializer Serializer = new Serializer(PacketCorpus.SerializableTypes.ToList());

[TestMethod]
public void APetIsListedAsTheOfficialServerListsIt()
{
var packet = new ScpPacket
{
PetId = 2, NpcMonsterVNum = 2682, TransportId = 1135622, Level = 48, Loyalty = 397, Experience = 86580,
DamageMinimum = 356, DamageMaximum = 480, Concentrate = 271, CriticalChance = 6, CriticalRate = 80,
CloseDefence = 185, DefenceDodge = 274, DistanceDefence = 158, DistanceDefenceDodge = 274, MagicDefence = 135,
Element = 4, FireResistance = 60, WaterResistance = 50, LightResistance = 40, DarkResistance = 70,
Hp = 10638, MaxHp = 10638, Mp = 708, MaxMp = 708, XpLoad = 476535, Name = "@", Unknow3 = 3, Unknow8 = 1
};

Assert.AreEqual(
"sc_p 2 2682 1135622 48 397 86580 0 0 356 480 271 6 80 0 185 274 158 274 135 4 60 50 40 70 10638 10638 708 708 0 476535 0 @ 0 0 3 0 0 0 0 1",
Serializer.Serialize(packet));
}

[TestMethod]
public void APartnerIsListedAsTheOfficialServerListsIt()
{
var packet = new ScnPacket
{
PetId = 0, NpcMonsterVNum = 317, TransportId = 1135623, Level = 28, Loyalty = 1000, Experience = 13458,
Unknown3 = 1, MinimumAttack = 69, MaximumAttack = 69, Precision = 138, Defence = 29, DefenceDodge = 69,
DistanceDefence = 34, DistanceDodge = 69, DodgeRate = 26, Hp = 1902, HpMax = 2165, Mp = 877, MpMax = 877,
LevelXp = 190440, Name = "@", MorphId = -1, Unknown5 = 1
};

Assert.AreEqual(
"sc_n 0 317 1135623 28 1000 13458 -1 -1 -1 -1 0 0 1 0 69 69 138 0 0 0 29 69 34 69 26 0 0 0 0 0 1902 2165 877 877 0 190440 @ -1 0 -1 -1 -1 -1 0 1",
Serializer.Serialize(packet));
}

[TestMethod]
public void AMateSpawnsAsOnTheOfficialServer()
{
var packet = new InPacket
{
VisualType = VisualType.Npc, VNum = "830", VisualId = 1135624, PositionX = 14, PositionY = 11, Direction = 2,
InNonPlayerSubPacket = new InNonPlayerSubPacket
{
InAliveSubPacket = new InAliveSubPacket { Hp = 100, Mp = 100 },
GroupEffect = 3, Owner = 12988838, SpawnEffect = SpawnEffectType.NoEffect, Name = "@", Unknow1 = 2,
Unknow7 = 2
}
};

Assert.AreEqual(
"in 2 830 1135624 14 11 2 100 100 0 0 3 12988838 1 0 -1 @ 2 -1 0 0 0 0 0 0 0 0 0 0 0 2 0",
Serializer.Serialize(packet));
}

[TestMethod]
public void AMonsterSpawnsAsOnTheOfficialServer()
{
var packet = new InPacket
{
VisualType = VisualType.Monster, VNum = "24", VisualId = 2887, PositionX = 27, PositionY = 145, Direction = 2,
InNonPlayerSubPacket = new InNonPlayerSubPacket
{
InAliveSubPacket = new InAliveSubPacket { Hp = 100, Mp = 100 }
}
};

Assert.AreEqual(
"in 3 24 2887 27 145 2 100 100 0 0 0 -1 0 0 -1 - 0 -1 0 0 0 0 0 0 0 0 0 0 0 0 0",
Serializer.Serialize(packet));
}

[TestMethod]
public void TheFollowReportListsEachMateThenOneSpeed()
{
var packet = (PtctlPacket)Deserializer.Deserialize("ptctl 1 2 1135623 50 132 1135626 49 133 1135623 9");

Assert.AreEqual(2, packet.Amount);
Assert.AreEqual((1135623L, (short)50, (short)132),
(packet.Mates![0]!.VisualId, packet.Mates[0]!.PositionX, packet.Mates[0]!.PositionY));
Assert.AreEqual((1135626L, (short)49, (short)133),
(packet.Mates[1]!.VisualId, packet.Mates[1]!.PositionX, packet.Mates[1]!.PositionY));
Assert.AreEqual(1135623, packet.VisualId);
Assert.AreEqual(9, packet.Speed);
}

[TestMethod]
[DataRow("ptctl 1 1 1135626 50 149 1135626 7")]
[DataRow("ptctl 1 2 1135623 50 132 1135626 49 133 1135623 9")]
public void AFollowReportWritesBackUnchanged(string line)
{
Assert.AreEqual(line, Serializer.Serialize(Deserializer.Deserialize(line)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ PslPacket#1 => psl 1
PslPacket#7 => psl 0
PsopPacket#1 => ps_op 21 44 83
PsopPacket#7 => ps_op 74 75 77
PtctlPacket#1 => ptctl 21 44 s12383
PtctlPacket#7 => ptctl 74 75 s19477
PtctlPacket#1 => ptctl 21 44 83 53 21 82 87 55 52 90
PtctlPacket#7 => ptctl 74 75 77 8 43 82 37 53 78 12
SayPPacket#1 => say_p 21 s30144
SayPPacket#7 => say_p 74 s275
SuCtlPacket#1 => suctl 21 44 83 3 21
Expand Down Expand Up @@ -542,10 +542,10 @@ PetskiPacket#1 => petski 21 44 83
PetskiPacket#7 => petski 74 75 77
PsdPacket#1 => psd 21
PsdPacket#7 => psd 74
ScnPacket#1 => sc_n 21 44 83 53 21 82 87.55.52 90.80.24 27.23.69 90.20.18 35 9 75 68 79 97 1 68 41 93 71 31 99 83 94 74 40 54 48 31 23 57 67 95 1 4 s19011 59 0 52 57 5 0 39 2 17 0
ScnPacket#7 => sc_n 74 75 77 8 43 82 37.53.78 12.53.59 96.45.47 58.74.95 71 58 9 5 81 27 96 55 30 7 6 24 78 44 68 41 54 77 86 75 59 33 86 63 0 80 s27111 9 1 85 11 66 0 88 4 58 7
ScpPacket#1 => sc_p 21 44 83 53 21 82 87 55 52 90 80 24 27 23 69 90 20 18 35 9 75 68 79 97 1 68 41 93 0 31 0 s21783 1
ScpPacket#7 => sc_p 74 75 77 8 43 82 37 53 78 12 53 59 96 45 47 58 74 95 71 58 9 5 81 27 96 55 30 7 1 24 1 s17244 1
ScnPacket#1 => sc_n 21 44 83 53 21 82 87.55.52 90.80.24 27.23.69 90.20.18 35 9 75 68 79 97 1 68 41 93 71 31 99 83 94 74 40 54 48 31 23 57 67 95 1 4 s19011 59 0 52 57 5 0 39 2 17 0 42 74
ScnPacket#7 => sc_n 74 75 77 8 43 82 37.53.78 12.53.59 96.45.47 58.74.95 71 58 9 5 81 27 96 55 30 7 6 24 78 44 68 41 54 77 86 75 59 33 86 63 0 80 s27111 9 1 85 11 66 0 88 4 58 7 71 78
ScpPacket#1 => sc_p 21 44 83 53 21 82 87 55 52 90 80 24 27 23 69 90 20 18 35 9 75 68 79 97 1 68 41 93 0 31 0 s21783 1 74 40 54 48 31 23 57
ScpPacket#7 => sc_p 74 75 77 8 43 82 37 53 78 12 53 59 96 45 47 58 74 95 71 58 9 5 81 27 96 55 30 7 1 24 1 s17244 1 41 54 77 86 75 59 33
AtPacket#1 => at 21 44 83 53 21 82 87 55 52 90 80
AtPacket#7 => at 74 75 77 8 43 82 37 53 78 12 53
CMapPacket#1 => c_map 21 44 0
Expand Down Expand Up @@ -788,8 +788,8 @@ ZzimPacket#1 => zzim
ZzimPacket#7 => zzim
ClPacket#1 => cl 21 1 0
ClPacket#7 => cl 74 0 0
InPacket#1 => in 1 s30144 s12383 53 21 82 87 3 0 0 68 4 27.23.69.90.20.18.35.9.75.68 79 97 0 68 41 93 0 31 0 83 9474 4054 48.31 s17923 57 0 95 2 4 11 59 0|1 0 5 4 39 98 17 1 42 74 92 97 6 26 77 25 2 1 1 s15588 41 59 3 23 38 75 57 87 65 0 81 71 12
InPacket#7 => in 9 s275 s19477 8 43 82 37 1 0 2 49 4 96.45.47.58.74.95.71.58.9.5 81 27 1 55 30 7 1 24 1 44 6841 5477 86.75 s10759 33 1 63 2 80 11 9 1|0 0 66 92 88 32 58 0 71 78 69 24 46 93 88 53 1 0 91 s6425 11 90 29 87 63 46 29 70 51 1 181 90 8
InPacket#1 => in 1 s30144 s12383 53 21 82 87 3 0 0 68 4 27.23.69.90.20.18.35.9.75.68 79 97 0 68 41 93 0 31 0 83 9474 4054 48.31 s17923 57 0 95 2 4 11 59 0|1 0 5 4 39 98 17 1 42 74 92 97 6 26 77 25 2 1 1 s15588 41 59 3 23 38 75 57 87 65 0 81 71 12 6 99
InPacket#7 => in 9 s275 s19477 8 43 82 37 1 0 2 49 4 96.45.47.58.74.95.71.58.9.5 81 27 1 55 30 7 1 24 1 44 6841 5477 86.75 s10759 33 1 63 2 80 11 9 1|0 0 66 92 88 32 58 0 71 78 69 24 46 93 88 53 1 0 91 s6425 11 90 29 87 63 46 29 70 51 1 181 90 8 59 93
FStashAllPacket#1 => f_stash_all 21 44.83.53.21.82.87.55 52.90.80.24.27.23.69
FStashAllPacket#7 => f_stash_all 74 75.77.8.43.82.37.53 78.12.53.59.96.45.47
FStashClientPacket#1 => f_stash 21.44.83.53.21.82.87 55.52.90.80.24.27.23
Expand Down
4 changes: 2 additions & 2 deletions test/NosCore.Packets.Tests/SerializerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ public void GenerateInPacketIsNotCorruptedForMonster()
}
};
var packet = Serializer.Serialize(mapMonsterPacket);
Assert.AreEqual("in 3 - 0 0 0 0 0 0 0 0 0 -1 1 0 -1 - 2 -1 0 0 0 0 0 0 0 0 0 0 0", packet);
Assert.AreEqual("in 3 - 0 0 0 0 0 0 0 0 0 -1 1 0 -1 - 2 -1 0 0 0 0 0 0 0 0 0 0 0 0 0", packet);
}

[TestMethod]
Expand Down Expand Up @@ -771,7 +771,7 @@ public void GenerateInPacketIsNotCorruptedForNpc()
}
};
var packet = Serializer.Serialize(mapNpcPacket);
Assert.AreEqual("in 2 - 0 0 0 0 0 0 0 0 0 -1 1 0 -1 - 2 -1 0 0 0 0 0 0 0 0 0 0 0", packet);
Assert.AreEqual("in 2 - 0 0 0 0 0 0 0 0 0 -1 1 0 -1 - 2 -1 0 0 0 0 0 0 0 0 0 0 0 0 0", packet);
}


Expand Down