Implement basic login

This commit is contained in:
Somebody
2024-03-03 14:13:48 +08:00
parent 961ed73ad6
commit f746425221
67 changed files with 1546 additions and 266 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
[*.cs]
# IDE0022: 使用方法的程序块主体
csharp_style_expression_bodied_methods = false

View File

@@ -15,6 +15,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.8" />
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.8" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,12 @@
namespace EggLink.DanhengServer.Data.Excel
{
[ResourceEntity("AvatarConfig.json", true)]
internal class AvatarConfigExcel : ExcelResource
public class AvatarConfigExcel : ExcelResource
{
public int AvatarID { get; set; } = 0;
public HashName AvatarName { get; set; } = new();
public List<AvatarSkillTreeConfigExcel> DefaultSkillTree = [];
public override int GetId()
{
return AvatarID;

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Data.Excel
{
[ResourceEntity("AvatarSkillTreeConfig.json")]
public class AvatarSkillTreeConfigExcel : ExcelResource
{
public int PointID { get; set; }
public int Level { get; set; }
public int AvatarID { get; set; }
public bool DefaultUnlock { get; set; }
public int MaxLevel { get; set; }
public override int GetId()
{
return (PointID << 4) + Level;
}
public override void AfterAllDone()
{
GameData.AvatarConfigData.TryGetValue(AvatarID, out var excel);
if (excel != null && DefaultUnlock)
{
excel.DefaultSkillTree.Add(this);
}
}
}
}

View File

@@ -0,0 +1,25 @@
namespace EggLink.DanhengServer.Data.Excel
{
[ResourceEntity("MapEntrance.json", true)]
public class MapEntranceExcel : ExcelResource
{
public int ID { get; set; }
public int PlaneID { get; set; }
public int FloorID { get; set; }
public int StartGroupID { get; set; }
public int StartAnchorID { get; set; }
public List<int> FinishMainMissionList { get; set; } = [];
public List<int> FinishSubMissionList { get; set; } = [];
public override int GetId()
{
return ID;
}
public override void Loaded()
{
GameData.MapEntranceData.Add(ID, this);
}
}
}

View File

@@ -3,7 +3,7 @@
namespace EggLink.DanhengServer.Data.Excel
{
[ResourceEntity("StageConfig.json", false)]
internal class StageConfigExcel : ExcelResource
public class StageConfigExcel : ExcelResource
{
public int StageID { get; set; } = 0;
public HashName StageName { get; set; } = new HashName();
@@ -20,7 +20,7 @@ namespace EggLink.DanhengServer.Data.Excel
}
}
internal class StageMonsterList
public class StageMonsterList
{
public int Monster0 { get; set; } = 0;
public int Monster1 { get; set; } = 0;
@@ -29,7 +29,7 @@ namespace EggLink.DanhengServer.Data.Excel
public int Monster4 { get; set; } = 0;
}
internal class HashName
public class HashName
{
public long Hash { get; set; } = 0;
}

View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace EggLink.DanhengServer.Data
{
internal abstract class ExcelResource
public abstract class ExcelResource
{
public abstract int GetId();
@@ -17,5 +17,9 @@ namespace EggLink.DanhengServer.Data
public virtual void Finalized()
{
}
public virtual void AfterAllDone()
{
}
}
}

View File

@@ -3,9 +3,10 @@ using System.Collections.Generic;
namespace EggLink.DanhengServer.Data
{
internal static class GameData
public static class GameData
{
public static Dictionary<int, AvatarConfigExcel> AvatarConfigData { get; private set; } = new Dictionary<int, AvatarConfigExcel>();
public static Dictionary<int, StageConfigExcel> StageConfigData { get; private set; } = new Dictionary<int, StageConfigExcel>();
public static Dictionary<int, AvatarConfigExcel> AvatarConfigData { get; private set; } = [];
public static Dictionary<int, StageConfigExcel> StageConfigData { get; private set; } = [];
public static Dictionary<int, MapEntranceExcel> MapEntranceData { get; private set; } = [];
}
}

View File

@@ -1,14 +1,13 @@
using System;
using System.IO;
using System.Reflection;
using EggLink.DanhengServer.Program;
using EggLink.DanhengServer.Util;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace EggLink.DanhengServer.Data
{
internal class ResourceManager
public class ResourceManager
{
public static Logger Logger { get; private set; } = new Logger("ResourceManager");
public static void LoadGameData()
@@ -21,12 +20,12 @@ namespace EggLink.DanhengServer.Data
var classes = Assembly.GetExecutingAssembly().GetTypes(); // Get all classes in the assembly
foreach (var cls in classes)
{
var attribute = (ResourceEntity)Attribute.GetCustomAttribute(cls, typeof(ResourceEntity));
var attribute = (ResourceEntity)Attribute.GetCustomAttribute(cls, typeof(ResourceEntity))!;
if (attribute != null)
{
var resource = (ExcelResource)Activator.CreateInstance(cls);
var path = EntryPoint.GetConfig().Path.ResourcePath + "/ExcelOutput/" + attribute.FileName;
var resource = (ExcelResource)Activator.CreateInstance(cls)!;
var path = ConfigManager.Config.Path.ResourcePath + "/ExcelOutput/" + attribute.FileName;
var file = new FileInfo(path);
if (!file.Exists)
{
@@ -52,7 +51,7 @@ namespace EggLink.DanhengServer.Data
foreach (var item in jArray)
{
var res = JsonConvert.DeserializeObject(item.ToString(), cls);
((ExcelResource)res).Loaded();
((ExcelResource?)res)?.Loaded();
count++;
}
}
@@ -71,9 +70,9 @@ namespace EggLink.DanhengServer.Data
var nestedObject = JsonConvert.DeserializeObject<JObject>(obj.ToString());
// Process only if it's a top-level dictionary, not nested
if (nestedObject.Count > 0 && nestedObject.First.First.Type != JTokenType.Object)
if (nestedObject?.Count > 0 && nestedObject?.First?.First?.Type != JTokenType.Object)
{
((ExcelResource)instance).Loaded();
((ExcelResource?)instance)?.Loaded();
}
}
else
@@ -84,9 +83,21 @@ namespace EggLink.DanhengServer.Data
}
}
}
resource.Finalized();
Logger.Info($"Loaded {count} {cls.Name}s.");
}
}
foreach (var cls in classes)
{
var attribute = (ResourceEntity)Attribute.GetCustomAttribute(cls, typeof(ResourceEntity))!;
if (attribute != null)
{
var resource = (ExcelResource)Activator.CreateInstance(cls)!;
resource.AfterAllDone();
}
}
}
}

View File

@@ -1,62 +1,53 @@
using EggLink.DanhengServer.Util;
using Microsoft.Data.Sqlite;
using SqlSugar;
namespace EggLink.DanhengServer.Database.Account
{
[DatabaseEntity("account")]
public class AccountData(string username, long uid, string comboToken = "", string dispatchToken = "", string permissions = "") : BaseDatabaseData
[SugarTable("Account")]
public class AccountData() : BaseDatabaseData
{
public string Username { get; set; } = username;
public long Uid { get; set; } = uid;
public string ComboToken { get; set; } = comboToken;
public string DispatchToken { get; set; } = dispatchToken;
public string Permissions { get; set; } = permissions; // type: permission1,permission2,permission3...
public string? Username { get; set; }
public static BaseDatabaseData? GetAccountByUserName(string username)
[SugarColumn(IsNullable = true)]
public string? ComboToken { get; set; }
[SugarColumn(IsNullable = true)]
public string? DispatchToken { get; set; }
[SugarColumn(IsNullable = true)]
public string? Permissions { get; set; } // type: permission1,permission2,permission3...
public static AccountData? GetAccountByUserName(string username)
{
var connection = DatabaseHelper.Instance.connection;
var command = new SqliteCommand("SELECT * FROM account", connection);
var reader = command.ExecuteReader();
AccountData? result = null;
while (reader.Read())
DatabaseHelper.Instance?.GetAllInstance<AccountData>()?.ForEach((account) =>
{
if (reader.GetString(0) == username)
if (account.Username == username)
{
result = new(reader.GetString(0), reader.GetInt64(1), reader.GetString(2), reader.GetString(3), reader.GetString(4));
break;
result = account;
}
}
});
return result;
}
public static BaseDatabaseData? GetAccountByUid(long uid)
public static AccountData? GetAccountByUid(long uid)
{
var connection = DatabaseHelper.Instance.connection;
var command = new SqliteCommand("SELECT * FROM account", connection);
var reader = command.ExecuteReader();
AccountData? result = null;
while (reader.Read())
{
if (reader.GetInt64(1) == uid)
{
result = new(reader.GetString(0), reader.GetInt64(1), reader.GetString(2), reader.GetString(3), reader.GetString(4));
break;
}
}
AccountData? result = DatabaseHelper.Instance?.GetInstance<AccountData>(uid);
return result;
}
public string GenerateDispatchToken()
{
DispatchToken = Crypto.CreateSessionKey(Uid.ToString());
ModifyDatabase(Uid, "dispatchToken", DispatchToken);
DatabaseHelper.Instance?.UpdateInstance(this);
return DispatchToken;
}
public string GenerateComboToken()
{
ComboToken = Crypto.CreateSessionKey(Uid.ToString());
ModifyDatabase(Uid, "comboToken", ComboToken);
DatabaseHelper.Instance?.UpdateInstance(this);
return ComboToken;
}
}

View File

@@ -17,7 +17,7 @@ namespace EggLink.DanhengServer.Database.Account
long newUid = uid;
if (uid == 0)
{
newUid = 10000; // start from 10000
newUid = 10001; // start from 10001
while (AccountData.GetAccountByUid(newUid) != null)
{
newUid++;
@@ -25,8 +25,13 @@ namespace EggLink.DanhengServer.Database.Account
}
var per = ConfigManager.Config.ServerOption.DefaultPermissions;
var perStr = string.Join(",", per);
var account = new AccountData(username,newUid, permissions: perStr);
account.SaveToDatabase();
var account = new AccountData()
{
Uid = newUid,
Username = username,
Permissions = perStr
};
DatabaseHelper.Instance?.SaveInstance(account);
}
}
}

View File

@@ -0,0 +1,97 @@
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Proto;
using SqlSugar;
namespace EggLink.DanhengServer.Database.Avatar
{
[SugarTable("Avatar")]
public class AvatarData : BaseDatabaseData
{
[SugarColumn(IsNullable = true)]
public List<AvatarInfo>? Avatars { get; set; }
public List<int> AssistAvatars { get; set; } = [];
public List<int> DisplayAvatars { get; set; } = [];
}
public class AvatarInfo
{
public int AvatarId { get; set; }
public int Level { get; set; }
public int Exp { get; set; }
public int Promotion { get; set; }
public int Rewards { get; set; }
public long Timestamp { get; set; }
public int CurrentHp { get; set; }
public int CurrentSp { get; set; }
public int ExtraLineupHp { get; set; }
public int ExtraLineupSp { get; set; }
public int Rank { get; set; }
public Dictionary<int, int> SkillTree { get; set; } = [];
public int EquipId { get; set; } = 0;
public Dictionary<int, int> Relic { get; set; } = [];
public AvatarConfigExcel Excel;
public AvatarInfo(AvatarConfigExcel excel)
{
Excel = excel;
SkillTree = [];
excel.DefaultSkillTree.ForEach(skill =>
{
SkillTree.Add(skill.PointID, skill.Level);
});
}
public bool HasTakenReward(int promotion)
{
return (Rewards & (1 << promotion)) != 0;
}
public Proto.Avatar ToProto()
{
var proto = new Proto.Avatar()
{
BaseAvatarId = (uint)AvatarId,
Level = (uint)Level,
Exp = (uint)Exp,
Promotion = (uint)Promotion,
Rank = (uint)Rank,
FirstMetTimestamp = (ulong)Timestamp,
};
foreach (var item in Relic)
{
proto.EquipRelicList.Add(new EquipRelic()
{
RelicUniqueId = (uint)item.Value,
Slot = (uint)item.Key
});
}
if (EquipId != 0)
{
proto.EquipmentUniqueId = (uint)EquipId;
}
foreach (var skill in SkillTree)
{
proto.SkilltreeList.Add(new AvatarSkillTree()
{
PointId = (uint)skill.Key,
Level = (uint)skill.Value
});
}
for (int i = 0; i < Promotion; i++)
{
if (HasTakenReward(i))
{
proto.TakenRewards.Add((uint)i);
}
}
return proto;
}
}
}

View File

@@ -1,77 +1,11 @@
using Microsoft.Data.Sqlite;
using SqlSugar;
namespace EggLink.DanhengServer.Database
{
public abstract class BaseDatabaseData
{
public void SaveToDatabase()
{
var connection = DatabaseHelper.Instance.connection;
var attributes = GetType().GetCustomAttributes(true);
string tableName = "";
foreach ( var attribute in attributes )
{
if (attribute is DatabaseEntity)
{
tableName = (attribute as DatabaseEntity).TableName;
break;
}
}
var command = new SqliteCommand($"INSERT INTO {tableName} (", connection);
var properties = GetType().GetProperties();
foreach (var property in properties)
{
command.CommandText += $"{property.Name}, ";
}
command.CommandText = command.CommandText[..^2];
command.CommandText += ") VALUES (";
foreach (var property in properties)
{
command.CommandText += $"\"{property.GetValue(this)}\", ";
}
command.CommandText = command.CommandText[..^2];
command.CommandText += ")";
command.ExecuteNonQuery();
}
public void ModifyDatabase(long uid, string property, string value)
{
var connection = DatabaseHelper.Instance.connection;
var attributes = GetType().GetCustomAttributes(true);
string tableName = "";
foreach ( var attribute in attributes )
{
if (attribute is DatabaseEntity)
{
tableName = (attribute as DatabaseEntity).TableName;
break;
}
}
var command = new SqliteCommand($"UPDATE {tableName} SET \"{property}\" = \"{value}\" WHERE UID = {uid};", connection);
command.ExecuteNonQuery();
}
public void DeleteFromDatabase()
{
var connection = DatabaseHelper.Instance.connection;
var attributes = GetType().GetCustomAttributes(true);
string tableName = "";
foreach ( var attribute in attributes )
{
if (attribute is DatabaseEntity)
{
tableName = (attribute as DatabaseEntity).TableName;
break;
}
}
var command = new SqliteCommand($"DELETE FROM {tableName} WHERE ", connection);
var properties = GetType().GetProperties();
foreach (var property in properties)
{
command.CommandText += $"{property.Name} = {property.GetValue(this)} AND ";
}
command.CommandText = command.CommandText[..^5];
command.ExecuteNonQuery();
}
[SugarColumn(IsPrimaryKey = true)]
public long Uid { get; set; }
}
}

View File

@@ -1,8 +0,0 @@
namespace EggLink.DanhengServer.Database
{
[AttributeUsage(AttributeTargets.Class)]
public class DatabaseEntity(string tableName) : Attribute
{
public string TableName { get; set; } = tableName;
}
}

View File

@@ -1,6 +1,8 @@
using EggLink.DanhengServer.Configuration;
using EggLink.DanhengServer.Database.Account;
using EggLink.DanhengServer.Util;
using Microsoft.Data.Sqlite;
using SqlSugar;
using System.Reflection;
namespace EggLink.DanhengServer.Database
@@ -9,8 +11,8 @@ namespace EggLink.DanhengServer.Database
{
public Logger logger = new("Database");
public ConfigContainer config = ConfigManager.Config;
public SqliteConnection connection;
public static DatabaseHelper Instance;
public static SqlSugarScope? sqlSugarScope;
public static DatabaseHelper? Instance;
public DatabaseHelper()
{
@@ -19,7 +21,12 @@ namespace EggLink.DanhengServer.Database
{
f.Directory.Create();
}
connection = new SqliteConnection($"Data Source={f.FullName};");
sqlSugarScope = new(new ConnectionConfig()
{
ConnectionString = $"Data Source={f.FullName};",
DbType = DbType.Sqlite,
IsAutoCloseConnection = true,
});
Instance = this;
}
public void Initialize()
@@ -36,70 +43,65 @@ namespace EggLink.DanhengServer.Database
}
}
public void InitializeSqlite()
public static void InitializeSqlite()
{
SQLitePCL.Batteries.Init();
connection.Open();
var classes = Assembly.GetExecutingAssembly().GetTypes();
foreach (var cls in classes) {
var attribute = (DatabaseEntity)Attribute.GetCustomAttribute(cls, typeof(DatabaseEntity));
if (attribute != null)
{
var tableName = attribute.TableName;
var createTable = $"CREATE TABLE IF NOT EXISTS {tableName} (";
var properties = cls.GetProperties();
foreach (var property in properties)
{
createTable += $"{property.Name} {GetSqliteType(property.PropertyType)}, ";
}
createTable = createTable.Substring(0, createTable.Length - 2);
createTable += ")";
var command = new SqliteCommand(createTable, connection);
command.ExecuteNonQuery();
}
var baseType = typeof(BaseDatabaseData);
var assembly = typeof(BaseDatabaseData).Assembly;
var types = assembly.GetTypes().Where(t => t.IsSubclassOf(baseType));
foreach (var type in types)
{
typeof(DatabaseHelper).GetMethod("InitializeSqliteTable")?.MakeGenericMethod(type).Invoke(null, null);
}
}
private string GetSqliteType(Type propertyType)
public static void InitializeSqliteTable<T>() where T : class, new()
{
if (propertyType == typeof(int))
try
{
return "INTEGER";
}
else if (propertyType == typeof(string))
sqlSugarScope?.Queryable<T>().ToList();
} catch
{
return "TEXT";
}
else if (propertyType == typeof(bool))
{
return "INTEGER";
}
else if (propertyType == typeof(float))
{
return "REAL";
}
else if (propertyType == typeof(double))
{
return "REAL";
}
else if (propertyType == typeof(long))
{
return "INTEGER";
}
else if (propertyType == typeof(byte[]))
{
return "BLOB";
}
else
{
logger.Error($"Unsupported type {propertyType}");
return "TEXT";
sqlSugarScope?.CodeFirst.InitTables<T>();
}
}
public void Close()
public T? GetInstance<T>(long uid) where T : class, new()
{
connection.Close();
try
{
return sqlSugarScope?.Queryable<T>().Where(it => (it as BaseDatabaseData).Uid == uid).First();
} catch
{
logger.Error("Unsupported type");
return null;
}
}
public List<T>? GetAllInstance<T>() where T : class, new()
{
try
{
return sqlSugarScope?.Queryable<T>().ToList();
} catch
{
logger.Error("Unsupported type");
return null;
}
}
public void SaveInstance<T>(T instance) where T : class, new()
{
sqlSugarScope?.Insertable(instance).ExecuteCommand();
}
public void UpdateInstance<T>(T instance) where T : class, new()
{
sqlSugarScope?.Updateable(instance).ExecuteCommand();
}
public void DeleteInstance<T>(T instance) where T : class, new()
{
sqlSugarScope?.Deleteable(instance).ExecuteCommand();
}
}
}

View File

@@ -0,0 +1,95 @@
using EggLink.DanhengServer.Proto;
using SqlSugar;
namespace EggLink.DanhengServer.Database.Inventory
{
public class InventoryData : BaseDatabaseData
{
[SugarColumn(IsJson = true)]
public List<ItemData> MaterialItems { get; set; } = [];
[SugarColumn(IsJson = true)]
public List<ItemData> EquipmentItems { get; set; } = [];
[SugarColumn(IsJson = true)]
public List<ItemData> RelicItems { get; set; } = [];
}
public class ItemData
{
[SugarColumn(IsPrimaryKey = true)]
public int UniqueId { get; set; }
public int ItemId { get; set; }
public int Count { get; set; }
public int Level { get; set; }
public int Exp { get; set; }
public int TotalExp { get; set; }
public int Promotion { get; set; }
public int Rank { get; set; } // Superimpose
public bool Locked { get; set; }
public bool Discarded { get; set; }
public int MainAffix { get; set; }
public List<ItemSubAffix> SubAffixes { get; set; } = [];
public int EquipAvatar { get; set; }
public Material ToMaterialProto()
{
return new()
{
Tid = (uint)ItemId,
Num = (uint)Count,
};
}
public Relic ToRelicProto()
{
Relic relic = new()
{
Tid = (uint)ItemId,
UniqueId = (uint)UniqueId,
Level = (uint)Level,
IsProtected = Locked,
IsDiscarded = Discarded,
BaseAvatarId = (uint)EquipAvatar,
MainAffixId = (uint)MainAffix,
};
if (SubAffixes.Count >= 1)
{
foreach (var subAffix in SubAffixes)
{
relic.SubAffixList.Add(subAffix.ToProto());
}
}
return relic;
}
public Equipment ToEquipmentProto()
{
return new()
{
Tid = (uint)ItemId,
UniqueId = (uint)UniqueId,
Level = (uint)Level,
Exp = (uint)Exp,
IsProtected = Locked,
Promotion = (uint)Promotion,
Rank = (uint)Rank,
BaseAvatarId = (uint)EquipAvatar
};
}
}
public class ItemSubAffix
{
public int Id { get; set; } // Affix id
public int Count { get; set; }
public int Step { get; set; }
public RelicAffix ToProto() => new()
{
AffixId = (uint)Id,
Cnt = (uint)Count,
Step = (uint)Step
};
}
}

View File

@@ -0,0 +1,28 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Database.Lineup
{
[SugarTable("Lineup")]
public class LineupData : BaseDatabaseData
{
public int CurLineup { get; set; } // index of current lineup
public string? Lineups { get; set; } // 9 * 4
}
public class LineupInfo
{
public string? Name { get; set; }
public int LineupType { get; set; }
public List<int>? BaseAvatars { get; set; }
}
public class LineupInfoJson
{
public Dictionary<int, LineupInfo>? Lineups { get; set; } = []; // 9 * 4
}
}

View File

@@ -0,0 +1,48 @@
using EggLink.DanhengServer.Enums;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;
using SqlSugar;
namespace EggLink.DanhengServer.Database.Player
{
[SugarTable("Player")]
public class PlayerData : BaseDatabaseData
{
public string? Name { get; set; }
public string? Signature { get; set; }
public int Birthday { get; set; }
public int CurBasicType { get; set; }
public int HeadIcon { get; set; }
public int PhoneTheme { get; set; }
public int ChatBubble { get; set; }
public int CurrentBgm { get; set; }
public Gender? CurrentGender { get; set; }
public int Level { get; set; }
public int Exp { get; set; }
public int WorldLevel { get; set; }
public int Scoin { get; set; } // Credits
public int Hcoin { get; set; } // Jade
public int Mcoin { get; set; } // Crystals
public int TalentPoints { get; set; } // Rogue talent points
public int Stamina { get; set; }
public double StaminaReserve { get; set; }
public long NextStaminaRecover { get; set; }
[SugarColumn(IsNullable = true)]
public Position? Pos { get; set; }
[SugarColumn(IsNullable = true)]
public Position? Rot { get; set; }
[SugarColumn(IsNullable = true)]
public int PlaneId { get; set; }
[SugarColumn(IsNullable = true)]
public int FloorId { get; set; }
[SugarColumn(IsNullable = true)]
public int EntryId { get; set; }
[SugarColumn(IsNullable = true)]
public long LastActiveTime { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
namespace EggLink.DanhengServer.Database.Player
{
public class PlayerUnlockData : BaseDatabaseData
{
public List<int> HeadIcons { get; set; } = [];
public List<int> ChatBubbles { get; set; } = [];
public List<int> PhoneThemes { get; set; } = [];
}
}

View File

@@ -4,13 +4,11 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Game.Player
namespace EggLink.DanhengServer.Enums
{
public class Player
public enum PlayerGender
{
public async Task OnLogoutAsync()
{
}
PLAYER_MALE = 0,
PLAYER_FEMALE = 1,
}
}

View File

@@ -1,4 +1,4 @@
namespace EggLink.DanhengServer.Enums
namespace EggLink.DanhengServer.Common.Enums
{
public enum SessionState
{

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Util
{
public static class GameConstants
{
public const int MATERIAL_HCOIN_ID = 1; // Material id for jades. DO NOT CHANGE
public const int MATERIAL_COIN_ID = 2; // Material id for credits. DO NOT CHANGE
public const int TRAILBLAZER_EXP_ID = 22;
public const int RELIC_REMAINS_ID = 235;
public const int INVENTORY_MAX_EQUIPMENT = 1500;
public const int INVENTORY_MAX_RELIC = 1500;
public const int INVENTORY_MAX_MATERIAL = 2000;
}
}

View File

@@ -107,10 +107,9 @@ namespace EggLink.DanhengServer.Util
}
}
public static Logger GetByClassName()
{
return new Logger(new StackTrace().GetFrame(1).GetMethod().ReflectedType.Name);
}
#pragma warning disable CS8602
public static Logger GetByClassName() => new(new StackTrace().GetFrame(1).GetMethod().ReflectedType.Name);
#pragma warning restore CS8602
}
public enum LoggerLevel

121
Common/Util/Position.cs Normal file
View File

@@ -0,0 +1,121 @@
using EggLink.DanhengServer.Proto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Util
{
public class Position
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
public Position(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
public Position(Vector vector)
{
X = vector.X;
Y = vector.Y;
Z = vector.Z;
}
public Position()
{
X = 0;
Y = 0;
Z = 0;
}
public Position(Position position)
{
X = position.X;
Y = position.Y;
Z = position.Z;
}
public void Set(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
public void Set(Position position)
{
X = position.X;
Y = position.Y;
Z = position.Z;
}
public void Set(Vector vector)
{
X = vector.X;
Y = vector.Y;
Z = vector.Z;
}
public void Add(int x, int y, int z)
{
X += x;
Y += y;
Z += z;
}
public void Add(Position position)
{
X += position.X;
Y += position.Y;
Z += position.Z;
}
public void Sub(int x, int y, int z)
{
X -= x;
Y -= y;
Z -= z;
}
public void Sub(Position position)
{
X -= position.X;
Y -= position.Y;
Z -= position.Z;
}
public void Mul(int x, int y, int z)
{
X *= x;
Y *= y;
Z *= z;
}
public void Mul(Position position)
{
X *= position.X;
Y *= position.Y;
Z *= position.Z;
}
public void Div(int x, int y, int z)
{
X /= x;
Y /= y;
Z /= z;
}
public void Div(Position position)
{
X /= position.X;
Y /= position.Y;
Z /= position.Z;
}
}
}

View File

@@ -14,6 +14,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C822679-4BCC-497A-AF15-F441EC750CCE}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU

View File

@@ -0,0 +1,56 @@
using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Database;
using EggLink.DanhengServer.Database.Avatar;
using EggLink.DanhengServer.Game.Player;
namespace EggLink.DanhengServer.Game.Avatar
{
public class AvatarManager : BasePlayerManager
{
public AvatarData AvatarData { get; private set; }
public AvatarManager(PlayerInstance player) : base(player)
{
var avatars = DatabaseHelper.Instance?.GetInstance<AvatarData>(player.Uid);
if (avatars == null)
{
AvatarData = new()
{
Uid = player.Uid,
Avatars = [],
};
DatabaseHelper.Instance?.SaveInstance(AvatarData);
}
else
{
AvatarData = avatars;
}
}
public void AddAvatar(int avatarId)
{
GameData.AvatarConfigData.TryGetValue(avatarId, out AvatarConfigExcel? avatarExcel);
if (avatarExcel == null)
{
return;
}
var avatar = new AvatarInfo(avatarExcel)
{
AvatarId = avatarId,
Level = 1,
Timestamp = DateTime.Now.Ticks / TimeSpan.TicksPerSecond,
CurrentHp = 10000,
CurrentSp = 0
};
if (AvatarData.Avatars == null)
{
AvatarData.Avatars = [];
}
AvatarData.Avatars.Add(avatar);
DatabaseHelper.Instance?.UpdateInstance(AvatarData);
}
}
}

View File

@@ -0,0 +1,14 @@
using EggLink.DanhengServer.Game.Player;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Game
{
public class BasePlayerManager(PlayerInstance player)
{
public PlayerInstance Player { get; private set; } = player;
}
}

View File

@@ -0,0 +1,26 @@
using EggLink.DanhengServer.Database;
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Game.Player;
namespace EggLink.DanhengServer.Game.Inventory
{
public class InventoryManager : BasePlayerManager
{
public InventoryData Data;
public InventoryManager(PlayerInstance player) : base(player)
{
var inventory = DatabaseHelper.Instance?.GetInstance<InventoryData>(player.Uid);
if (inventory == null)
{
DatabaseHelper.Instance?.SaveInstance(new InventoryData()
{
Uid = player.Uid,
});
inventory = DatabaseHelper.Instance?.GetInstance<InventoryData>(player.Uid);
}
Data = inventory!;
}
}
}

View File

@@ -0,0 +1,95 @@
using EggLink.DanhengServer.Database;
using EggLink.DanhengServer.Database.Lineup;
using EggLink.DanhengServer.Game.Player;
using Newtonsoft.Json;
namespace EggLink.DanhengServer.Game.Lineup
{
public class LineupManager : BasePlayerManager
{
public LineupData LineupData { get; private set; }
public LineupInfoJson LineupInfoJson { get; private set; }
public LineupManager(PlayerInstance player) : base(player)
{
var lineup = DatabaseHelper.Instance?.GetInstance<LineupData>(player.Uid);
if (lineup == null)
{
LineupData = new()
{
Uid = player.Uid,
CurLineup = 0,
Lineups = "{}",
};
DatabaseHelper.Instance?.SaveInstance(LineupData);
}
else
{
LineupData = lineup;
}
LineupInfoJson = JsonConvert.DeserializeObject<LineupInfoJson>(LineupData.Lineups ?? "{}") ?? new();
}
public LineupInfo? GetLineup(int lineupIndex)
{
if (LineupData.Lineups == null)
{
return null;
}
if (lineupIndex < 0 || lineupIndex >= LineupInfoJson.Lineups?.Count)
{
return null;
}
return LineupInfoJson.Lineups?[lineupIndex];
}
public LineupInfo? GetCurLineup()
{
return GetLineup(LineupData.CurLineup);
}
public void SetCurLineup(int lineupIndex)
{
if (lineupIndex < 0 || lineupIndex >= LineupInfoJson.Lineups?.Count)
{
return;
}
LineupData.CurLineup = lineupIndex;
DatabaseHelper.Instance?.UpdateInstance(LineupData);
}
public void AddAvatar(int lineupIndex, int avatarId)
{
if (lineupIndex < 0 || LineupData == null)
{
return;
}
if (LineupData.Lineups == null)
{
LineupData.Lineups = "";
}
LineupInfo? lineup = null;
LineupInfoJson.Lineups?.TryGetValue(lineupIndex, out lineup);
if (lineup == null)
{
lineup = new()
{
Name = "Lineup " + lineupIndex,
LineupType = 0,
BaseAvatars = [avatarId],
};
LineupInfoJson.Lineups?.Add(lineupIndex, lineup);
} else
{
lineup.BaseAvatars?.Add(avatarId);
}
LineupData.Lineups = JsonConvert.SerializeObject(LineupInfoJson);
DatabaseHelper.Instance?.UpdateInstance(LineupData!);
}
public void AddAvatarToCurTeam(int avatarId)
{
AddAvatar(LineupData.CurLineup, avatarId);
}
}
}

View File

@@ -0,0 +1,138 @@
using EggLink.DanhengServer.Database;
using EggLink.DanhengServer.Database.Player;
using EggLink.DanhengServer.Enums;
using EggLink.DanhengServer.Game.Avatar;
using EggLink.DanhengServer.Game.Inventory;
using EggLink.DanhengServer.Game.Lineup;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Server;
using EggLink.DanhengServer.Server.Packet;
using EggLink.DanhengServer.Server.Packet.Send.Player;
namespace EggLink.DanhengServer.Game.Player
{
public class PlayerInstance(PlayerData data)
{
public PlayerData Data { get; set; } = data;
public ushort Uid { get; set; }
public Connection? Connection { get; set; }
public bool Initialized { get; set; } = false;
#region Managers
public AvatarManager AvatarManager { get; private set; }
public LineupManager LineupManager { get; private set; }
public InventoryManager InventoryManager { get; private set; }
#endregion
#region Datas
public PlayerUnlockData PlayerUnlockData { get; private set; }
#endregion
public PlayerInstance() : this(new PlayerData())
{
// new player
Data.Name = "无名客"; // Trailblazer in EN TODO: Add localization
Data.Signature = "";
Data.Birthday = 0;
Data.CurBasicType = 8001;
Data.HeadIcon = 208001;
Data.PhoneTheme = 221000;
Data.ChatBubble = 220000;
Data.CurrentBgm = 210000;
Data.CurrentGender = Gender.Man;
Data.Stamina = 240;
Data.StaminaReserve = 0;
Data.NextStaminaRecover = DateTime.Now.Millisecond;
Data.Level = 1;
Data.Exp = 0;
Data.WorldLevel = 0;
Data.Scoin = 0;
Data.Hcoin = 0;
Data.Mcoin = 0;
Data.TalentPoints = 0;
InitialPlayerManager();
AddAvatar(1005);
LineupManager.SetCurLineup(0);
LineupManager.AddAvatarToCurTeam(1005);
Initialized = true;
}
private void InitialPlayerManager()
{
Uid = (ushort)Data.Uid;
AvatarManager = new(this);
LineupManager = new(this);
InventoryManager = new(this);
var unlock = DatabaseHelper.Instance?.GetInstance<PlayerUnlockData>(Uid);
if (unlock == null)
{
DatabaseHelper.Instance?.SaveInstance(new PlayerUnlockData()
{
Uid = Uid,
});
unlock = DatabaseHelper.Instance?.GetInstance<PlayerUnlockData>(Uid);
}
PlayerUnlockData = unlock!;
}
#region Network
public void OnLogin()
{
if (!Initialized)
{
InitialPlayerManager();
}
SendPacket(new PacketStaminaInfoScNotify(this));
}
public async Task OnLogoutAsync()
{
}
public void SendPacket(BasePacket packet)
{
Connection?.SendPacket(packet);
}
#endregion
#region Extra
public void AddAvatar(int avatarId)
{
AvatarManager.AddAvatar(avatarId);
}
#endregion
#region Proto
public PlayerBasicInfo ToProto()
{
return new()
{
Nickname = Data.Name,
Level = (uint)Data.Level,
Exp = (uint)Data.Exp,
WorldLevel = (uint)Data.WorldLevel,
Scoin = (uint)Data.Scoin,
Hcoin = (uint)Data.Hcoin,
Mcoin = (uint)Data.Mcoin,
Stamina = (uint)Data.Stamina,
};
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Game.Scene
{
public class SceneInstance
{
}
}

View File

@@ -16,11 +16,9 @@
<ItemGroup>
<Folder Include="Server\Packet\Recv\Rogue\" />
<Folder Include="Server\Packet\Recv\Scene\" />
<Folder Include="Server\Packet\Recv\Achievement\" />
<Folder Include="Server\Packet\Recv\ChessRogue\" />
<Folder Include="Server\Packet\Recv\Tutorial\" />
<Folder Include="Server\Packet\Recv\Mission\" />
<Folder Include="Server\Packet\Recv\Quest\" />
<Folder Include="Server\Packet\Recv\Mail\" />
<Folder Include="Server\Packet\Recv\Friend\" />
@@ -31,18 +29,16 @@
<Folder Include="Server\Packet\Send\ChessRogue\" />
<Folder Include="Server\Packet\Send\Friend\" />
<Folder Include="Server\Packet\Send\Mail\" />
<Folder Include="Server\Packet\Send\Mission\" />
<Folder Include="Server\Packet\Send\Others\" />
<Folder Include="Server\Packet\Send\Player\" />
<Folder Include="Server\Packet\Send\Quest\" />
<Folder Include="Server\Packet\Send\Rogue\" />
<Folder Include="Server\Packet\Send\Scene\" />
<Folder Include="Server\Packet\Send\Tutorial\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.8" />
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.8" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
</ItemGroup>
</Project>

View File

@@ -3,7 +3,6 @@ using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Configuration;
using EggLink.DanhengServer.WebServer;
using EggLink.DanhengServer.Database;
using System.Runtime.InteropServices;
using EggLink.DanhengServer.Server;
using EggLink.DanhengServer.Server.Packet;

View File

@@ -1,7 +1,8 @@
using System.Buffers;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using EggLink.DanhengServer.Enums;
using EggLink.DanhengServer.Common.Enums;
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.KcpSharp;
using EggLink.DanhengServer.Program;
@@ -21,16 +22,14 @@ public partial class Connection
private readonly CancellationTokenSource CancelToken;
public readonly IPEndPoint RemoteEndPoint;
public SessionState State { get; set; } = SessionState.INACTIVE;
private bool UseSecretKey { get; set; } = false;
private byte[] SecretKey = new byte[0x1000];
public Player? Player { get; set; }
public PlayerInstance? Player { get; set; }
public uint ClientTime { get; private set; }
public long LastPingTime { get; private set; }
private uint LastClientSeq = 10;
public static readonly List<int> BANNED_PACKETS = [];
private static Logger Logger = new("GameServer");
private static readonly Logger Logger = new("GameServer");
#if DEBUG
private static uint LogIndex = 0;
private static readonly Dictionary<string, string> LogMap = [];
#endif
public Connection(KcpConversation conversation, IPEndPoint remote)
{
@@ -38,11 +37,14 @@ public partial class Connection
RemoteEndPoint = remote;
CancelToken = new CancellationTokenSource();
Start();
#if DEBUG
JsonConvert.DeserializeObject<JObject>(File.ReadAllText("LogMap.json")).Properties().ToList().ForEach(x => LogMap.Add(x.Name, x.Value.ToString()));
#endif
}
private async void Start()
{
Logger.Info($"New connection to {RemoteEndPoint} created with conversation id {Conversation.ConversationId}");
Logger.Info($"New connection from {RemoteEndPoint}.");
State = SessionState.WAITING_FOR_TOKEN;
await ReceiveLoop();
}
@@ -72,14 +74,20 @@ public partial class Connection
#if DEBUG
public static void LogPacket(string sendOrRecv, ushort opcode, byte[] payload)
{
//Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}");
Type? typ = AppDomain.CurrentDomain.GetAssemblies().
SingleOrDefault(assembly => assembly.GetName().Name == "Shared").GetTypes().First(t => t.Name == $"{Enum.GetName(typeof(CmdId), opcode)}"); //get the type using the packet name
MessageDescriptor? descriptor = (MessageDescriptor)typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static).GetValue(null, null); // get the static property Descriptor
IMessage? packet = descriptor.Parser.ParseFrom(payload);
JsonFormatter? formatter = JsonFormatter.Default;
string? asJson = formatter.Format(packet);
Logger.Debug($"{sendOrRecv}: {Enum.GetName(typeof(CmdId), opcode)}({opcode})\r\n{asJson}");
try
{
//Logger.DebugWriteLine($"{sendOrRecv}: {Enum.GetName(typeof(OpCode), opcode)}({opcode})\r\n{Convert.ToHexString(payload)}");
Type? typ = AppDomain.CurrentDomain.GetAssemblies().
SingleOrDefault(assembly => assembly.GetName().Name == "Common").GetTypes().First(t => t.Name == $"{LogMap[opcode.ToString()]}"); //get the type using the packet name
MessageDescriptor? descriptor = (MessageDescriptor)typ.GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static).GetValue(null, null); // get the static property Descriptor
IMessage? packet = descriptor.Parser.ParseFrom(payload);
JsonFormatter? formatter = JsonFormatter.Default;
string? asJson = formatter.Format(packet);
Logger.Debug($"{sendOrRecv}: {LogMap[opcode.ToString()]}({opcode})\r\n{asJson}");
} catch (Exception e)
{
Logger.Debug($"{sendOrRecv}: {LogMap[opcode.ToString()]}({opcode})");
}
}
#endif
@@ -131,8 +139,6 @@ public partial class Connection
{
byte[] gamePacket = data.ToArray();
// Decrypt and turn back into a packet
//Crypto.Xor(gamePacket, UseSecretKey ? SecretKey : Crypto.DISPATCH_KEY);
await using MemoryStream? ms = new(gamePacket);
using BinaryReader? br = new(ms);
@@ -159,8 +165,10 @@ public partial class Connection
uint payloadLength = br.ReadUInt32BE();
byte[] header = br.ReadBytes(headerLength);
byte[] payload = br.ReadBytes((int)payloadLength);
await HandlePacketAsync(opcode, header, payload);
#if DEBUG
LogPacket("Recv", opcode, payload);
#endif
HandlePacket(opcode, header, payload);
}
}
@@ -174,7 +182,7 @@ public partial class Connection
}
}
private async Task<bool> HandlePacketAsync(ushort opcode, byte[] header, byte[] payload)
private bool HandlePacket(ushort opcode, byte[] header, byte[] payload)
{
// Find the Handler for this opcode
Handler? handler = EntryPoint.HandlerManager.GetHandler(opcode);
@@ -185,7 +193,7 @@ public partial class Connection
SessionState state = State;
switch ((int)opcode)
{
case CmdId.PlayerGetTokenCsReq:
case CmdIds.PlayerGetTokenCsReq:
{
if (state != SessionState.WAITING_FOR_TOKEN)
{
@@ -193,7 +201,7 @@ public partial class Connection
}
goto default;
}
case CmdId.PlayerLoginCsReq:
case CmdIds.PlayerLoginCsReq:
{
if (state != SessionState.WAITING_FOR_LOGIN)
{
@@ -204,15 +212,14 @@ public partial class Connection
default:
break;
}
handler.OnHandle(header, payload);
handler.OnHandle(this, header, payload);
return true;
}
return false;
}
public async Task SendPacketAsync(BasePacket packet)
public void SendPacket(BasePacket packet)
{
// Test
if (packet.CmdId <= 0)
@@ -226,10 +233,40 @@ public partial class Connection
{
return;
}
#if DEBUG
LogPacket("Send", packet.CmdId, packet.Data);
#endif
// Header
byte[] packetBytes = packet.BuildPacket();
await Conversation.SendAsync(packetBytes, CancelToken.Token);
#pragma warning disable CA2012
_ = Conversation.SendAsync(packetBytes, CancelToken.Token);
#pragma warning restore CA2012
}
public void SendPacket(int cmdId)
{
// Test
if (cmdId <= 0)
{
Logger.Debug("Tried to send packet with missing cmd id!");
return;
}
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
if (BANNED_PACKETS.Contains(cmdId))
{
return;
}
#if DEBUG
LogPacket("Send", (ushort)cmdId, []);
#endif
// Header
byte[] packetBytes = new BasePacket((ushort)cmdId).BuildPacket();
#pragma warning disable CA2012
_ = Conversation.SendAsync(packetBytes, CancelToken.Token);
#pragma warning restore CA2012
}
}

View File

@@ -1,6 +0,0 @@
namespace EggLink.DanhengServer.Server
{
public class GameSession()
{
}
}

View File

@@ -1,4 +1,6 @@
using System;
using EggLink.DanhengServer.Util;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -6,13 +8,42 @@ using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet
{
public class BasePacket
public class BasePacket(ushort cmdId)
{
public int CmdId;
private const uint HEADER_CONST = 0x9d74c714;
private const uint TAIL_CONST = 0xd7a152c8;
public ushort CmdId { get; set; } = cmdId;
public byte[] Data { get; private set; } = [];
public void SetData(byte[] data)
{
Data = data;
}
public void SetData(IMessage message)
{
Data = message.ToByteArray();
}
public byte[] BuildPacket()
{
return [];
using MemoryStream? ms = new();
using BinaryWriter? bw = new(ms);
bw.WriteUInt32BE(HEADER_CONST);
bw.WriteUInt16BE(CmdId);
bw.WriteUInt16BE(0);
bw.WriteUInt32BE((uint)Data.Length);
if (Data.Length > 0)
{
bw.Write(Data);
}
bw.WriteUInt32BE(TAIL_CONST);
byte[] packet = ms.ToArray();
return packet;
}
}
}

View File

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet
{
public class CmdId
public class CmdIds
{
// None
public const int None = 0;

View File

@@ -1,13 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet
namespace EggLink.DanhengServer.Server.Packet
{
public abstract class Handler
{
public abstract void OnHandle(byte[] header, byte[] data);
public abstract void OnHandle(Connection connection, byte[] header, byte[] data);
}
}

View File

@@ -1,10 +1,4 @@
using EggLink.DanhengServer.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace EggLink.DanhengServer.Server.Packet
{

View File

@@ -0,0 +1,13 @@
using EggLink.DanhengServer.Server.Packet.Send.Avatar;
namespace EggLink.DanhengServer.Server.Packet.Recv.Avatar
{
[Opcode(CmdIds.GetAvatarDataCsReq)]
public class HandlerGetAvatarDataCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
connection.SendPacket(new PacketGetAvatarDataScRsp(connection.Player!));
}
}
}

View File

@@ -0,0 +1,18 @@
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Server.Packet.Send.Mission;
namespace EggLink.DanhengServer.Server.Packet.Recv.Mission
{
[Opcode(CmdIds.GetMissionStatusCsReq)]
public class HandlerGetMissionStatusCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = GetMissionStatusCsReq.Parser.ParseFrom(data);
if (req != null)
{
connection.SendPacket(new PacketGetMissionStatusScRsp(req));
}
}
}
}

View File

@@ -0,0 +1,18 @@
using EggLink.DanhengServer.Server.Packet.Send.Player;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
[Opcode(CmdIds.GetBagCsReq)]
public class HandlerGetBagCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
connection.SendPacket(new PacketGetBagScRsp(connection.Player!));
}
}
}

View File

@@ -0,0 +1,18 @@
using EggLink.DanhengServer.Server.Packet.Send.Player;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
[Opcode(CmdIds.GetBasicInfoCsReq)]
public class HandlerGetBasicInfoCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
connection.SendPacket(new PacketGetBasicInfoScRsp(connection.Player!));
}
}
}

View File

@@ -0,0 +1,13 @@
using EggLink.DanhengServer.Server.Packet.Send.Player;
namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
[Opcode(CmdIds.GetHeroBasicTypeInfoCsReq)]
public class HandlerGetHeroBasicTypeInfoCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
connection.SendPacket(new PacketGetHeroBasicTypeInfoScRsp(connection.Player!));
}
}
}

View File

@@ -0,0 +1,13 @@
using EggLink.DanhengServer.Server.Packet.Send.Player;
namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
[Opcode(CmdIds.GetPlayerBoardDataCsReq)]
public class HandlerGetPlayerBoardDataCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
connection.SendPacket(new PacketGetPlayerBoardDataScRsp(connection.Player!));
}
}
}

View File

@@ -1,20 +1,33 @@
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Common.Enums;
using EggLink.DanhengServer.Database;
using EggLink.DanhengServer.Database.Player;
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Server.Packet.Send.Player;
using EggLink.DanhengServer.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
[Opcode(CmdId.PlayerGetTokenCsReq)]
[Opcode(CmdIds.PlayerGetTokenCsReq)]
public class HandlerPlayerGetTokenCsReq : Handler
{
public override void OnHandle(byte[] header, byte[] data)
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = PlayerGetTokenCsReq.Parser.ParseFrom(data);
Logger.GetByClassName().Debug("OnHandle" + req.ToString());
connection.State = SessionState.WAITING_FOR_LOGIN;
var pd = DatabaseHelper.Instance?.GetInstance<PlayerData>(long.Parse(req.AccountUid));
if (pd == null)
connection.Player = new PlayerInstance()
{
Uid = ushort.Parse(req.AccountUid),
};
else
{
connection.Player = new PlayerInstance(pd);
}
connection.Player.OnLogin();
connection.Player.Connection = connection;
connection.SendPacket(new PacketPlayerGetTokenScRsp(connection));
}
}
}

View File

@@ -0,0 +1,23 @@
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Server.Packet.Send.Player;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
[Opcode(CmdIds.PlayerHeartBeatCsReq)]
public class HandlerPlayerHeartBeatCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = PlayerHeartbeatCsReq.Parser.ParseFrom(data);
if (req != null)
{
connection.SendPacket(new PacketPlayerHeartBeatScRsp((long)req.ClientTimeMs));
}
}
}
}

View File

@@ -0,0 +1,15 @@
using EggLink.DanhengServer.Common.Enums;
using EggLink.DanhengServer.Server.Packet.Send.Player;
namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
[Opcode(CmdIds.PlayerLoginCsReq)]
public class HandlerPlayerLoginCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
connection.State = SessionState.ACTIVE;
connection.SendPacket(new PacketPlayerLoginScRsp(connection));
}
}
}

View File

@@ -0,0 +1,12 @@
namespace EggLink.DanhengServer.Server.Packet.Recv.Player
{
[Opcode(CmdIds.PlayerLoginFinishCsReq)]
public class HandlerPlayerLoginFinishCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
connection.SendPacket(CmdIds.PlayerLoginFinishScRsp);
connection.SendPacket(CmdIds.GetArchiveDataScRsp);
}
}
}

View File

@@ -0,0 +1,13 @@
using EggLink.DanhengServer.Server.Packet.Send.Scene;
namespace EggLink.DanhengServer.Server.Packet.Recv.Scene
{
[Opcode(CmdIds.GetEnteredSceneCsReq)]
public class HandlerGetEnteredSceneCsReq : Handler
{
public override void OnHandle(Connection connection, byte[] header, byte[] data)
{
connection.SendPacket(new PacketGetEnteredSceneScRsp());
}
}
}

View File

@@ -0,0 +1,23 @@
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.Server.Packet.Send.Avatar
{
public class PacketGetAvatarDataScRsp : BasePacket
{
public PacketGetAvatarDataScRsp(PlayerInstance player) : base(CmdIds.GetAvatarDataScRsp)
{
var proto = new GetAvatarDataScRsp()
{
IsGetAll = true,
};
player.AvatarManager?.AvatarData?.Avatars?.ForEach(avatar =>
{
proto.AvatarList.Add(avatar.ToProto());
});
SetData(proto);
}
}
}

View File

@@ -0,0 +1,28 @@
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.Server.Packet.Send.Mission
{
public class PacketGetMissionStatusScRsp : BasePacket
{
public PacketGetMissionStatusScRsp(GetMissionStatusCsReq req) : base(CmdIds.GetMissionStatusScRsp)
{
var proto = new GetMissionStatusScRsp();
foreach (var item in req.MainMissionIdList)
{
proto.FinishedMainMissionIdList.Add(item);
}
foreach (var item in req.SubMissionIdList)
{
proto.SubMissionStatusList.Add(new Proto.Mission()
{
Id = item,
Status = MissionStatus.MissionFinish,
});
}
SetData(proto);
}
}
}

View File

@@ -0,0 +1,30 @@
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketGetBagScRsp : BasePacket
{
public PacketGetBagScRsp(PlayerInstance player) : base(CmdIds.GetBagScRsp)
{
var proto = new GetBagScRsp();
player.InventoryManager.Data.MaterialItems.ForEach(item =>
{
proto.MaterialList.Add(item.ToMaterialProto());
});
player.InventoryManager.Data.RelicItems.ForEach(item =>
{
proto.RelicList.Add(item.ToRelicProto());
});
player.InventoryManager.Data.EquipmentItems.ForEach(item =>
{
proto.EquipmentList.Add(item.ToEquipmentProto());
});
SetData(proto);
}
}
}

View File

@@ -0,0 +1,26 @@
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.Proto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketGetBasicInfoScRsp : BasePacket
{
public PacketGetBasicInfoScRsp(PlayerInstance player) : base(CmdIds.GetBasicInfoScRsp)
{
var proto = new GetBasicInfoScRsp()
{
CurDay = 1,
NextRecoverTime = player.Data.NextStaminaRecover / 1000,
GameplayBirthday = (uint)player.Data.Birthday,
PlayerSettingInfo = new(),
};
SetData(proto);
}
}
}

View File

@@ -0,0 +1,19 @@
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketGetHeroBasicTypeInfoScRsp : BasePacket
{
public PacketGetHeroBasicTypeInfoScRsp(PlayerInstance player) : base(CmdIds.GetHeroBasicTypeInfoScRsp)
{
var proto = new GetHeroBasicTypeInfoScRsp()
{
Gender = player.Data.CurrentGender ?? Gender.None,
CurBasicType = (HeroBasicType)player.Data.CurBasicType,
};
SetData(proto);
}
}
}

View File

@@ -0,0 +1,37 @@
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketGetPlayerBoardDataScRsp : BasePacket
{
public PacketGetPlayerBoardDataScRsp(PlayerInstance player) : base(CmdIds.GetPlayerBoardDataScRsp)
{
var proto = new GetPlayerBoardDataScRsp()
{
CurrentHeadIconId = (uint)player.Data.HeadIcon,
Signature = player.Data.Signature,
};
player.PlayerUnlockData.HeadIcons.ForEach(id =>
{
HeadIcon headIcon = new() { Id = (uint)id };
proto.UnlockedHeadIconList.Add(headIcon);
});
proto.DisplayAvatarVec = new();
var pos = 0;
player.AvatarManager.AvatarData.DisplayAvatars.ForEach(avatar =>
{
DisplayAvatar displayAvatar = new()
{
AvatarId = (uint)avatar,
Pos = (uint)pos++,
};
proto.DisplayAvatarVec.DisplayAvatarList.Add(displayAvatar);
});
SetData(proto);
}
}
}

View File

@@ -0,0 +1,18 @@
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketPlayerGetTokenScRsp : BasePacket
{
public PacketPlayerGetTokenScRsp(Connection connection) : base(CmdIds.PlayerGetTokenScRsp)
{
var rsp = new PlayerGetTokenScRsp()
{
BlackInfo = new BlackInfo(),
Uid = connection.Player?.Uid ?? 0,
};
SetData(rsp);
}
}
}

View File

@@ -0,0 +1,23 @@
using EggLink.DanhengServer.Proto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketPlayerHeartBeatScRsp : BasePacket
{
public PacketPlayerHeartBeatScRsp(long clientTime) : base(CmdIds.PlayerHeartBeatScRsp)
{
var data = new PlayerHeartbeatScRsp()
{
ClientTimeMs = (ulong)clientTime,
ServerTimeMs = (ulong)(DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond),
};
SetData(data);
}
}
}

View File

@@ -0,0 +1,26 @@
using EggLink.DanhengServer.Proto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketPlayerLoginScRsp : BasePacket
{
public PacketPlayerLoginScRsp(Connection connection) : base(CmdIds.PlayerLoginScRsp)
{
var rsp = new PlayerLoginScRsp()
{
CurTimezone = (int)TimeZoneInfo.Local.BaseUtcOffset.TotalHours,
ServerTimestampMs = (ulong)(DateTime.Now.Ticks / 10000),
BasicInfo = connection?.Player?.ToProto(), // should not be null
Stamina = (uint)(connection?.Player?.Data.Stamina ?? 0),
};
SetData(rsp);
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketPlayerSyncScNotify : BasePacket
{
public PacketPlayerSyncScNotify() : base(CmdIds.PlayerSyncScNotify)
{
}
}
}

View File

@@ -0,0 +1,20 @@
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.Server.Packet.Send.Player
{
public class PacketStaminaInfoScNotify : BasePacket
{
public PacketStaminaInfoScNotify(PlayerInstance player) : base(CmdIds.StaminaInfoScNotify)
{
var proto = new StaminaInfoScNotify()
{
Stamina = (uint)player.Data.Stamina,
ReserveStamina = (uint)player.Data.StaminaReserve,
NextRecoverTime = player.Data.NextStaminaRecover,
};
SetData(proto);
}
}
}

View File

@@ -0,0 +1,33 @@
using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.Server.Packet.Send.Scene
{
public class PacketGetEnteredSceneScRsp : BasePacket
{
public PacketGetEnteredSceneScRsp() : base(CmdIds.GetEnteredSceneScRsp)
{
var proto = new GetEnteredSceneScRsp();
foreach (var excel in GameData.MapEntranceData.Values)
{
// Skip these
if (excel.FinishMainMissionList.Count == 0 && excel.FinishMainMissionList.Count == 0)
{
continue;
}
// Add info
var info = new EnteredSceneInfo()
{
FloorId = (uint)excel.FloorID,
PlaneId = (uint)excel.PlaneID,
};
proto.EnteredSceneInfo.Add(info);
}
SetData(proto);
}
}
}

View File

@@ -17,7 +17,7 @@ namespace EggLink.DanhengServer.WebServer.Handler
res.message = "Invalid login data";
return new JsonResult(res);
}
AccountData? account = AccountData.GetAccountByUid(long.Parse(tokenData.uid)) as AccountData;
AccountData? account = AccountData.GetAccountByUid(long.Parse(tokenData.uid));
if (account == null)
{
res.retcode = -201;

View File

@@ -9,7 +9,7 @@ namespace EggLink.DanhengServer.WebServer.Handler
{
public JsonResult Handle(string uid, string token)
{
AccountData? account = AccountData.GetAccountByUid(long.Parse(uid)) as AccountData;
AccountData? account = AccountData.GetAccountByUid(long.Parse(uid));
var res = new LoginResJson();
if (account == null || !account.DispatchToken.Equals(token))
{

View File

@@ -11,14 +11,14 @@ namespace EggLink.DanhengServer.WebServer.Handler
public JsonResult Handle(string account, string password, bool isCrypto)
{
LoginResJson res = new();
AccountData accountData = (AccountData)AccountData.GetAccountByUserName(account);
AccountData? accountData = AccountData.GetAccountByUserName(account);
if (accountData == null)
{
if (ConfigManager.Config.ServerOption.AutoCreateUser)
{
AccountHelper.CreateAccount(account, 0);
accountData = (AccountData)AccountData.GetAccountByUserName(account);
accountData = AccountData.GetAccountByUserName(account);
}
else
{

View File

@@ -14,6 +14,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SQLitePCLRaw.core" Version="2.1.8" />
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.1.8" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.143" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>