diff --git a/AscNet.Common/Common.cs b/AscNet.Common/Common.cs
index 7c5c8a10..0d389e9e 100644
--- a/AscNet.Common/Common.cs
+++ b/AscNet.Common/Common.cs
@@ -58,4 +58,14 @@ namespace AscNet.Common
}
}
}
+ public class ServerCodeException : Exception
+ {
+ public int Code { get; set; }
+
+ public ServerCodeException(string message, int code)
+ : base(message)
+ {
+ Code = code;
+ }
+ }
}
diff --git a/AscNet.Common/Database/Character.cs b/AscNet.Common/Database/Character.cs
index 38286f4c..09165dc3 100644
--- a/AscNet.Common/Database/Character.cs
+++ b/AscNet.Common/Database/Character.cs
@@ -7,6 +7,7 @@ using AscNet.Common.MsgPack;
using AscNet.Common.Util;
using Newtonsoft.Json;
using AscNet.Table.V2.share.equip;
+using AscNet.Table.V2.share.character.quality;
namespace AscNet.Common.Database
{
@@ -53,23 +54,36 @@ namespace AscNet.Common.Database
return character;
}
- public void AddCharacter(uint id)
+ ///
+ /// Don't forget to send Equip, Fashion, and the Character notify after using this!
+ ///
+ ///
+ ///
+ public AddCharacterRet AddCharacter(uint id)
{
+ AddCharacterRet ret = new();
CharacterTable? character = CharacterTableReader.Instance.FromId((int)id);
CharacterSkillTable? characterSkill = CharacterSkillTableReader.Instance.FromCharacterId((int)id);
+ CharacterQualityTable? characterQuality = TableReaderV2.Parse().OrderBy(x => x.Quality).FirstOrDefault(x => x.CharacterId == id);
- if (character is null || characterSkill is null)
- throw new ArgumentException("Invalid character id!", nameof(id));
+ if (character is null || characterSkill is null || characterQuality is null)
+ {
+ // CharacterManagerGetCharacterDataNotFound
+ throw new ServerCodeException("Invalid character id!", 20009021);
+ }
if (Characters.FirstOrDefault(x => x.Id == character.Id) is not null)
- throw new ArgumentException("Character already obtained!", nameof(id));
+ {
+ // CharacterManagerCreateCharacterAlreadyExist
+ throw new ServerCodeException("Character already obtained!", 20009022);
+ }
NotifyCharacterDataList.NotifyCharacterDataListCharacterData characterData = new()
{
Id = (uint)character.Id,
Level = 1,
Exp = 0,
- Quality = 1,
- InitQuality = 1,
+ Quality = characterQuality.Quality,
+ InitQuality = characterQuality.Quality,
Star = 0,
Grade = 1,
FashionId = (uint)character.DefaultNpcFashtionId,
@@ -89,15 +103,19 @@ namespace AscNet.Common.Database
Id = uint.Parse(x.ToString().Take(6).ToArray()),
Level = 1
}));
- Fashions.Add(new()
+ FashionList fashion = new()
{
Id = character.DefaultNpcFashtionId,
IsLock = false
- });
+ };
+ Fashions.Add(fashion);
+ ret.Fashion = fashion;
if (character.EquipId > 0)
- AddEquip((uint)character.EquipId, character.Id);
+ ret.Equip = AddEquip((uint)character.EquipId, character.Id);
Characters.Add(characterData);
+ ret.Character = characterData;
+ return ret;
}
public NotifyCharacterDataList.NotifyCharacterDataListCharacterData? AddCharacterExp(int characterId, int exp, int maxLvl = 0)
@@ -167,7 +185,7 @@ namespace AscNet.Common.Database
};
}
- public void AddEquip(uint equipId, int characterId = 0)
+ public NotifyEquipDataList.NotifyEquipDataListEquipData AddEquip(uint equipId, int characterId = 0)
{
NotifyEquipDataList.NotifyEquipDataListEquipData equipData = new()
{
@@ -186,6 +204,7 @@ namespace AscNet.Common.Database
};
Equips.Add(equipData);
+ return equipData;
}
public NotifyEquipDataList.NotifyEquipDataListEquipData? AddEquipExp(int equipId, int exp)
@@ -282,4 +301,11 @@ namespace AscNet.Common.Database
[JsonProperty("TemplateId")]
public int TemplateId { get; set; }
}
+
+ public struct AddCharacterRet
+ {
+ public NotifyCharacterDataList.NotifyCharacterDataListCharacterData Character { get; set; }
+ public NotifyEquipDataList.NotifyEquipDataListEquipData Equip { get; set; }
+ public FashionList Fashion { get; set; }
+ }
}
diff --git a/AscNet.Common/MsgPack/Types.cs b/AscNet.Common/MsgPack/Types.cs
index 038533c5..b75561f8 100644
--- a/AscNet.Common/MsgPack/Types.cs
+++ b/AscNet.Common/MsgPack/Types.cs
@@ -32,7 +32,7 @@ namespace AscNet.Common.MsgPack
public String ServerBean { get; set; }
public Int32 LoginPlatform { get; set; }
public String ClientVersion { get; set; }
- public Int32 UserId { get; set; }
+ public dynamic UserId { get; set; }
}
@@ -2360,6 +2360,13 @@ namespace AscNet.Common.MsgPack
}
+ [global::MessagePack.MessagePackObject(true)]
+ public class EnterChallengeResponse
+ {
+ public Int32 Code { get; set; }
+ }
+
+
[global::MessagePack.MessagePackObject(true)]
public class TeamSetTeamResponse
{
diff --git a/AscNet.GameServer/Handlers/CharacterModule.cs b/AscNet.GameServer/Handlers/CharacterModule.cs
index bf87f26b..e9621eed 100644
--- a/AscNet.GameServer/Handlers/CharacterModule.cs
+++ b/AscNet.GameServer/Handlers/CharacterModule.cs
@@ -22,11 +22,85 @@ namespace AscNet.GameServer.Handlers
{
public int Code;
}
+
+ [MessagePackObject(true)]
+ public class CharacterExchangeRequest
+ {
+ public int TemplateId;
+ }
+
+ [MessagePackObject(true)]
+ public class CharacterExchangeResponse
+ {
+ public int Code;
+ }
+
+ [MessagePackObject(true)]
+ public class FashionSyncNotify
+ {
+ public List FashionList = new();
+ }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#endregion
internal class CharacterModule
{
+ [RequestPacketHandler("CharacterExchangeRequest")]
+ public static void CharacterExchangeRequestHandler(Session session, Packet.Request packet)
+ {
+ CharacterExchangeRequest request = packet.Deserialize();
+ CharacterTable? characterData = TableReaderV2.Parse().FirstOrDefault(x => x.Id == request.TemplateId);
+
+ if (characterData is null)
+ {
+ CharacterExchangeResponse rsp = new()
+ {
+ // CharacterManagerGetCharacterTemplateNotFound
+ Code = 20009001
+ };
+ session.SendResponse(rsp, packet.Id);
+ return;
+ }
+
+ if (!session.inventory.Items.Any(x => x.Id == characterData.ItemId && x.Count >= 50))
+ {
+ CharacterExchangeResponse rsp = new()
+ {
+ // ItemCountNotEnough
+ Code = 20012004
+ };
+ session.SendResponse(rsp, packet.Id);
+ return;
+ }
+
+ NotifyItemDataList notifyItemData = new();
+ // idk if it's always 50, please investigate later...
+ notifyItemData.ItemDataList.Add(session.inventory.Do(characterData.ItemId, 50 * -1));
+ session.SendPush(notifyItemData);
+
+ try
+ {
+ NotifyEquipDataList notifyEquipData = new();
+ FashionSyncNotify fashionSync = new();
+ NotifyCharacterDataList notifyCharacterData = new();
+ var addRet = session.character.AddCharacter((uint)request.TemplateId);
+
+ notifyEquipData.EquipDataList.Add(addRet.Equip);
+ fashionSync.FashionList.Add(addRet.Fashion);
+ notifyCharacterData.CharacterDataList.Add(addRet.Character);
+ session.SendPush(notifyEquipData);
+ session.SendPush(notifyCharacterData);
+ }
+ catch (ServerCodeException ex)
+ {
+ CharacterExchangeResponse rsp = new() { Code = ex.Code };
+ session.SendResponse(rsp, packet.Id);
+ return;
+ }
+
+ session.SendResponse(new CharacterExchangeResponse(), packet.Id);
+ }
+
[RequestPacketHandler("CharacterUpgradeSkillGroupRequest")]
public static void CharacterUpgradeSkillGroupRequestHandler(Session session, Packet.Request packet)
{
diff --git a/AscNet.GameServer/Handlers/FightModule.cs b/AscNet.GameServer/Handlers/FightModule.cs
index b51e0119..ee97001b 100644
--- a/AscNet.GameServer/Handlers/FightModule.cs
+++ b/AscNet.GameServer/Handlers/FightModule.cs
@@ -194,6 +194,12 @@ namespace AscNet.GameServer.Handlers
session.SendResponse(new TeamSetTeamResponse(), packet.Id);
}
+ [RequestPacketHandler("EnterChallengeRequest")]
+ public static void HandleEnterChallengeRequestHandler(Session session, Packet.Request packet)
+ {
+ session.SendResponse(new EnterChallengeResponse(), packet.Id);
+ }
+
[RequestPacketHandler("FightSettleRequest")]
public static void FightSettleRequestHandler(Session session, Packet.Request packet)
{
diff --git a/AscNet.GameServer/Handlers/ItemModule.cs b/AscNet.GameServer/Handlers/ItemModule.cs
new file mode 100644
index 00000000..617153ce
--- /dev/null
+++ b/AscNet.GameServer/Handlers/ItemModule.cs
@@ -0,0 +1,35 @@
+using MessagePack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace AscNet.GameServer.Handlers
+{
+ #region MsgPackScheme
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+ [MessagePackObject(true)]
+ public class GetAndroidOrIosMoneyCardResponse
+ {
+ public int Code;
+ public int MoneyCard;
+ public int Count;
+ }
+#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+ #endregion
+
+ internal class ItemModule
+ {
+ [RequestPacketHandler("GetAndroidOrIosMoneyCardRequest")]
+ public static void GetAndroidOrIosMoneyCardRequestHandler(Session session, Packet.Request packet)
+ {
+ session.SendResponse(new GetAndroidOrIosMoneyCardResponse()
+ {
+ Code = 0,
+ Count = 0,
+ MoneyCard = 0
+ }, packet.Id);
+ }
+ }
+}
diff --git a/AscNet.GameServer/Handlers/PlayerModule.cs b/AscNet.GameServer/Handlers/PlayerModule.cs
index 40a3b0d1..ec40a07f 100644
--- a/AscNet.GameServer/Handlers/PlayerModule.cs
+++ b/AscNet.GameServer/Handlers/PlayerModule.cs
@@ -18,6 +18,17 @@ namespace AscNet.GameServer.Handlers
public int Code;
}
+ [MessagePackObject(true)]
+ public class TouchBoardMutualRequest
+ {
+ public int CharacterId;
+ }
+
+ [MessagePackObject(true)]
+ public class TouchBoardMutualResponse
+ {
+ }
+
[MessagePackObject(true)]
public class ChangeCommunicationRequest
{
@@ -51,5 +62,13 @@ namespace AscNet.GameServer.Handlers
session.SendResponse(new ChangeCommunicationResponse(), packet.Id);
}
+
+ [RequestPacketHandler("TouchBoardMutualRequest")]
+ public static void TouchBoardMutualRequestHandler(Session session, Packet.Request packet)
+ {
+ TouchBoardMutualRequest request = MessagePackSerializer.Deserialize(packet.Content);
+
+ session.SendResponse(new TouchBoardMutualResponse(), packet.Id);
+ }
}
}
diff --git a/AscNet.SDKServer/Controllers/AccountController.cs b/AscNet.SDKServer/Controllers/AccountController.cs
index bea5afac..d5ad8d4d 100644
--- a/AscNet.SDKServer/Controllers/AccountController.cs
+++ b/AscNet.SDKServer/Controllers/AccountController.cs
@@ -108,7 +108,7 @@ namespace AscNet.SDKServer.Controllers
});
});
- app.MapGet("/api/Login/Login", ([FromQuery] int loginType, [FromQuery] int userId, [FromQuery] string token, [FromQuery] string clientIp) =>
+ app.MapGet("/api/Login/Login", ([FromQuery] int loginType, [FromQuery] int userId, [FromQuery] string token, [FromQuery] string? clientIp) =>
{
Account? account = Account.FromToken(token);
diff --git a/AscNet.SDKServer/Controllers/ConfigController.cs b/AscNet.SDKServer/Controllers/ConfigController.cs
index e54a0a8a..081197b4 100644
--- a/AscNet.SDKServer/Controllers/ConfigController.cs
+++ b/AscNet.SDKServer/Controllers/ConfigController.cs
@@ -16,7 +16,7 @@ namespace AscNet.SDKServer.Controllers
public static void Register(WebApplication app)
{
- app.MapGet("/prod/client/config/com.kurogame.punishing.grayraven.en.pc/{version}/standalone/config.tab", (HttpContext ctx) =>
+ app.MapGet("/prod/client/config/{package}/{version}/standalone/config.tab", (HttpContext ctx) =>
{
List remoteConfigs = new();
ServerVersionConfig versionConfig = versions.GetValueOrDefault((string)ctx.Request.RouteValues["version"]!) ?? versions.First().Value;
@@ -27,11 +27,21 @@ namespace AscNet.SDKServer.Controllers
remoteConfigs.AddConfig("ApplicationVersion", (string)ctx.Request.RouteValues["version"]!);
remoteConfigs.AddConfig("Debug", true);
remoteConfigs.AddConfig("External", true);
- remoteConfigs.AddConfig("Channel", 1);
remoteConfigs.AddConfig("PayCallbackUrl", "empty");
- remoteConfigs.AddConfig("PrimaryCdns", "http://prod-encdn-akamai.kurogame.net/prod|http://prod-encdn-aliyun.kurogame.net/prod");
- remoteConfigs.AddConfig("SecondaryCdns", "http://prod-encdn-aliyun.kurogame.net/prod");
- remoteConfigs.AddConfig("CdnInvalidTime", 600);
+ switch ((string?)ctx.Request.RouteValues["package"])
+ {
+ case "com.kurogame.haru.kuro":
+ remoteConfigs.AddConfig("PrimaryCdns", "http://prod-zspnsalicdn.kurogame.com/prod");
+ remoteConfigs.AddConfig("SecondaryCdns", "http://prod-zspnstxcdn.kurogame.com/prod");
+ remoteConfigs.AddConfig("Channel", 2);
+ break;
+ default:
+ remoteConfigs.AddConfig("PrimaryCdns", "http://prod-encdn-akamai.kurogame.net/prod|http://prod-encdn-aliyun.kurogame.net/prod");
+ remoteConfigs.AddConfig("SecondaryCdns", "http://prod-encdn-aliyun.kurogame.net/prod");
+ remoteConfigs.AddConfig("Channel", 1);
+ break;
+ }
+ remoteConfigs.AddConfig("CdnInvalidTime", 60);
remoteConfigs.AddConfig("MtpEnabled", false);
remoteConfigs.AddConfig("MemoryLimit", 2048);
remoteConfigs.AddConfig("CloseMsgEncrypt", false);
@@ -44,12 +54,16 @@ namespace AscNet.SDKServer.Controllers
remoteConfigs.AddConfig("DownloadMethod", 1);
remoteConfigs.AddConfig("PcPayCallbackList", $"{Common.Common.config.GameServer.Host}/api/XPay/KuroPayResult");
+ // 2.9.0
+ remoteConfigs.AddConfig("WatermarkType", 2);
+ remoteConfigs.AddConfig("ChannelServerListStr", $"1#{Common.Common.config.GameServer.RegionName}#{Common.Common.config.GameServer.Host}/api/Login/Login");
+
string serializedObject = TsvTool.SerializeObject(remoteConfigs);
SDKServer.log.Info(serializedObject);
return serializedObject;
});
- app.MapGet("/prod/client/notice/config/com.kurogame.punishing.grayraven.en.pc/{version}/LoginNotice.json", (HttpContext ctx) =>
+ app.MapGet("/prod/client/notice/config/{package}/{version}/LoginNotice.json", (HttpContext ctx) =>
{
LoginNotice notice = new()
{
@@ -66,7 +80,7 @@ namespace AscNet.SDKServer.Controllers
return serializedObject;
});
- app.MapGet("/prod/client/notice/config/com.kurogame.punishing.grayraven.en.pc/{version}/ScrollTextNotice.json", (HttpContext ctx) =>
+ app.MapGet("/prod/client/notice/config/{package}/{version}/ScrollTextNotice.json", (HttpContext ctx) =>
{
ScrollTextNotice notice = new()
{
@@ -86,7 +100,7 @@ namespace AscNet.SDKServer.Controllers
return serializedObject;
});
- app.MapGet("/prod/client/notice/config/com.kurogame.punishing.grayraven.en.pc/{version}/ScrollPicNotice.json", (HttpContext ctx) =>
+ app.MapGet("/prod/client/notice/config/{package}/{version}/ScrollPicNotice.json", (HttpContext ctx) =>
{
ScrollPicNotice notice = new()
{
@@ -117,7 +131,7 @@ namespace AscNet.SDKServer.Controllers
return serializedObject;
});
- app.MapGet("/prod/client/notice/config/com.kurogame.punishing.grayraven.en.pc/{version}/GameNotice.json", (HttpContext ctx) =>
+ app.MapGet("/prod/client/notice/config/{package}/{version}/GameNotice.json", (HttpContext ctx) =>
{
List notices = new();
diff --git a/AscNet.SDKServer/SDKServer.cs b/AscNet.SDKServer/SDKServer.cs
index ca4cb3af..115d8e30 100644
--- a/AscNet.SDKServer/SDKServer.cs
+++ b/AscNet.SDKServer/SDKServer.cs
@@ -56,7 +56,11 @@ namespace AscNet.SDKServer
}
catch (Exception ex)
{
+#if DEBUG
+ log.Error($"{ex} Request below:");
+#else
log.Error($"{ex.Message} Request below:");
+#endif
}
finally
{
diff --git a/Resources/Configs/version_config.json b/Resources/Configs/version_config.json
index cd3c8c54..04772603 100644
--- a/Resources/Configs/version_config.json
+++ b/Resources/Configs/version_config.json
@@ -12,5 +12,12 @@
"IndexMd5": "c5d4baac85a6e37b8109ea43dc045d31",
"IndexSha1": "5e1c9a7213857d9f1c1223f4871feb425a598294",
"LaunchIndexSha1": "def7cf0ae2dbc6a31c5f85632f1d82d1f1d6cbfa"
+ },
+ "2.9.0": {
+ "DocumentVersion": "2.9.15",
+ "LaunchModuleVersion": "2.9.15",
+ "IndexMd5": "c5d4baac85a6e37b8109ea43dc045d31",
+ "IndexSha1": "ee9b1d7242fe7ff9621f3b7451c969b06b8f7638",
+ "LaunchIndexSha1": "7e0de243ba0074fe2e5c194d686c693e29f65b92"
}
}
\ No newline at end of file