diff --git a/src/GameServer/Data/Excel/OpenStateData.cs b/src/GameServer/Data/Excel/OpenStateData.cs new file mode 100644 index 00000000..46d6f1ad --- /dev/null +++ b/src/GameServer/Data/Excel/OpenStateData.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using Weedwacker.GameServer.Enums; + +namespace Weedwacker.GameServer.Data.Excel +{ + [Resource("OpenStateConfigData.json")] + internal class OpenStateData + { + [JsonProperty] public readonly int id; // maps to OpenStateType enum + [JsonProperty] public readonly bool defaultState; + [JsonProperty] public readonly bool allowClientOpen; + [JsonProperty] public readonly int systemOpenUiId; + [JsonProperty] public readonly OpenStateCond[] cond; + + public class OpenStateCond + { + [JsonProperty] public readonly OpenStateCondType condType; + [JsonProperty] public readonly int param; + [JsonProperty] public readonly int param2; + } + } +} diff --git a/src/GameServer/Data/Excel/SceneTagData.cs b/src/GameServer/Data/Excel/SceneTagData.cs new file mode 100644 index 00000000..b0d3a4b1 --- /dev/null +++ b/src/GameServer/Data/Excel/SceneTagData.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; +using Weedwacker.GameServer.Enums; + +namespace Weedwacker.GameServer.Data.Excel +{ + [Resource("SceneTagConfigData.json")] + internal class SceneTagData + { + [JsonProperty] public readonly int id; + [JsonProperty] public readonly string sceneTagName; + [JsonProperty] public readonly int sceneId; + [JsonProperty] public readonly bool isDefaultValid; + //TODO Missing one field + [JsonProperty] public readonly TagCond[] cond; + + public class TagCond + { + [JsonProperty] public readonly SceneTagCondType condType; + [JsonProperty] public readonly int param1; + [JsonProperty] public readonly int? param2; + } + } +} diff --git a/src/GameServer/Data/Excel/WeatherData.cs b/src/GameServer/Data/Excel/WeatherData.cs new file mode 100644 index 00000000..1000743d --- /dev/null +++ b/src/GameServer/Data/Excel/WeatherData.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Weedwacker.GameServer.Enums; + +namespace Weedwacker.GameServer.Data.Excel +{ + [Resource("WeatherExcelConfigData.json")] + internal class WeatherData + { + [JsonProperty] public readonly int areaId; + [JsonProperty] public readonly int weatherAreaId; + [JsonProperty] public readonly string maxHeightStr; + [JsonProperty] public readonly int gadgetId; + [JsonProperty] public readonly bool isDefaultValid; + [JsonProperty] public readonly int priority; + [JsonProperty] public readonly string profileName; + [JsonProperty] public readonly ClimateType defaultClimate; + [JsonProperty] public readonly bool isUseDefault; + [JsonProperty] public readonly int sceneId; + } +} diff --git a/src/GameServer/Data/GameData.cs b/src/GameServer/Data/GameData.cs index 78912f9d..4bf208ee 100644 --- a/src/GameServer/Data/GameData.cs +++ b/src/GameServer/Data/GameData.cs @@ -1,11 +1,13 @@ using System.Collections.Concurrent; using System.Text.RegularExpressions; using Newtonsoft.Json; +using NLua; using Weedwacker.GameServer.Data.BinOut.AbilityGroup; using Weedwacker.GameServer.Data.BinOut.Scene.Point; using Weedwacker.GameServer.Data.BinOut.Scene.SceneNpcBorn; using Weedwacker.GameServer.Data.BinOut.Talent; using Weedwacker.GameServer.Data.Excel; +using Weedwacker.GameServer.Systems.Script.Scene; using Weedwacker.Shared.Utils; using static Weedwacker.GameServer.Data.SerializationSettings; @@ -47,24 +49,27 @@ namespace Weedwacker.GameServer.Data public readonly static SortedList MonsterCurveDataMap = new(); // level public readonly static SortedList MonsterDataMap = new(); // id public readonly static SortedList MonsterDescribeDataMap = new(); // id + public readonly static SortedList OpenStateDataMap = new(); // id public readonly static SortedList RelicAffixConfigDataMap = new(); // openConfig public readonly static SortedList ShopDataMap = new(); // shopId public readonly static SortedList RewardDataMap = new(); // RewardId public readonly static SortedList ShopGoodsDataMap = new(); // goodsId public readonly static ConcurrentDictionary TeamResonanceConfigDataMap = new(); // openConfig - public readonly static ConcurrentDictionary WeaponAffixConfigDataMap = new(); // openConfig - public readonly static SortedList WeaponCurveDataMap = new(); // level - public readonly static SortedList, WeaponPromoteData> WeaponPromoteDataMap = new(); // public readonly static SortedList ReliquaryAffixDataMap = new(); // id public readonly static SortedList ReliquaryMainPropDataMap = new(); // id public readonly static SortedList, ReliquaryLevelData> ReliquaryLevelDataMap = new(); // public readonly static SortedList ReliquarySetDataMap = new(); // setid - public readonly static SortedList SceneDataMap = new(); //id + public readonly static SortedList SceneDataMap = new(); // id public readonly static ConcurrentDictionary ScenePointDataMap = new(); // filename public readonly static ConcurrentDictionary SceneNpcBornDataMap = new(); // sceneId + public readonly static SortedList SceneTagDataMap = new(); // id public readonly static SortedList TeamResonanceDataMap = new(); // teamResonanceId + public readonly static ConcurrentDictionary WeaponAffixConfigDataMap = new(); // openConfig + public readonly static SortedList WeaponCurveDataMap = new(); // level + public readonly static SortedList, WeaponPromoteData> WeaponPromoteDataMap = new(); // + public readonly static SortedList WeatherDataMap = new(); // areaId - + public readonly static SortedList SceneScripts = new(); static readonly JsonSerializer Serializer = new() { @@ -83,7 +88,23 @@ namespace Weedwacker.GameServer.Data } }; - + static async Task LoadScripts(string path) + { + var dirs = Directory.GetDirectories(Path.Combine(path, "Scene")); + foreach(var fileAndPath in dirs) + { + string fullPath = Path.GetFullPath(fileAndPath); + int sceneId = int.Parse(fullPath.Split("\\").Last()); + await LoadSceneScripts(sceneId, path); + } + } + + static async Task LoadSceneScripts(int sceneId, string scriptPath) + { + Lua lua = new(); + var SceneX = await SceneInfo.CreateAsync(lua, sceneId, scriptPath); + SceneScripts.Add(sceneId, SceneX); + } static async Task LoadBinOutFolder(string path, Func keySelector, IDictionary map) where Key : notnull @@ -166,6 +187,7 @@ namespace Weedwacker.GameServer.Data { string excelPath = Path.Combine(resourcesPath, "ExcelBinOutput/"); string binPath = Path.Combine(resourcesPath, "BinOutput/"); + string scriptPath = Path.Combine(resourcesPath, "Scripts/"); await Task.WhenAll(new Task[] { LoadExcel(excelPath, o => o.sortId, AvatarCodexDataMap), @@ -194,6 +216,7 @@ namespace Weedwacker.GameServer.Data LoadExcel(excelPath, o => o.level, MonsterCurveDataMap), LoadExcel(excelPath, o => o.id, MonsterDataMap), LoadExcel(excelPath, o => o.id, MonsterDescribeDataMap), + LoadExcel(excelPath, o => o.id, OpenStateDataMap), LoadExcel(excelPath, o => o.fetterId, PhotographExpressionDataMap), LoadExcel(excelPath, o => o.fetterId, PhotographPosenameDataMap), LoadExcel(excelPath, o => o.proudSkillId, ProudSkillDataMap), @@ -204,12 +227,14 @@ namespace Weedwacker.GameServer.Data LoadExcel(excelPath, o => o.setId, ReliquarySetDataMap), LoadExcel(excelPath, o => o.rewardId, RewardDataMap), LoadExcel(excelPath, o => o.id, SceneDataMap), + LoadExcel(excelPath, o => o.id, SceneTagDataMap), LoadExcel(excelPath, o => o.shopId, ShopDataMap), LoadExcel(excelPath, o=> o.goodsId, ShopGoodsDataMap), LoadExcel(excelPath, o => o.teamResonanceId, TeamResonanceDataMap), LoadExcel(excelPath, o => o.id, ItemDataMap), LoadExcel(excelPath, o => o.level, WeaponCurveDataMap), LoadExcel(excelPath, o => Tuple.Create(o.weaponPromoteId, o.promoteLevel), WeaponPromoteDataMap), + LoadExcel(excelPath, o => o.areaId, WeatherDataMap), LoadBinOutFolder(Path.Combine(binPath, "Scene/SceneNpcBorn"), o => o.sceneId, SceneNpcBornDataMap), LoadBinOutFolder(Path.Combine(binPath, "AbilityGroup"), AbilityGroupDataMap), @@ -220,6 +245,8 @@ namespace Weedwacker.GameServer.Data LoadBinOutFolder(Path.Combine(binPath, "Scene/Point"), ScenePointDataMap, false) }); + await LoadScripts(scriptPath); + Logger.DebugWriteLine("Loaded Resources"); } } diff --git a/src/GameServer/Database/DatabaseManager.cs b/src/GameServer/Database/DatabaseManager.cs index 71a38bfb..1be97e78 100644 --- a/src/GameServer/Database/DatabaseManager.cs +++ b/src/GameServer/Database/DatabaseManager.cs @@ -156,6 +156,23 @@ namespace Weedwacker.GameServer.Database return player; } + // Don't create a player when requested by gameUid + public static async Task GetPlayerByGameUidAsync(int gameUid) + { + var player = await Players.Find(w => w.GameUid == gameUid).FirstOrDefaultAsync(); + if (player == null) return null; + + //Attach player systems to the player + player.Avatars = await GetAvatarsByPlayerAsync(player) ?? await SaveAvatarsAsync(new AvatarManager(player)); // Load avatars before inventory, so that we can attach weapons while loading them + player.Inventory = await GetInventoryByPlayerAsync(player) ?? await SaveInventoryAsync(new InventoryManager(player)); + player.Inventory.ShopManager = await GetShopsByPlayerAsync(player) ?? await SaveShopsAsync(new ShopManager(player)); + player.SocialManager = await GetSocialByPlayerAsync(player) ?? await SaveSocialAsync(new SocialManager(player)); + player.TeamManager = await GetTeamsByPlayerAsync(player) ?? await SaveTeamsAsync(new TeamManager(player)); + await player.OnLoadAsync(); + + return player; + } + public static async Task SaveAvatarsAsync(AvatarManager avatars) { await Avatars.ReplaceOneAsync(w => w.OwnerId == avatars.OwnerId, avatars, Replace); diff --git a/src/GameServer/Enums/ClimateType.cs b/src/GameServer/Enums/ClimateType.cs new file mode 100644 index 00000000..21459407 --- /dev/null +++ b/src/GameServer/Enums/ClimateType.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Weedwacker.GameServer.Enums +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum ClimateType + { + CLIMATE_DESERT, + CLIMATE_SUNNY, + CLIMATE_CLOUDY, + CLIMATE_MIST, + CLIMATE_RAIN, + CLIMATE_THUNDERSTORM + } +} diff --git a/src/GameServer/Enums/OpenStateCondType.cs b/src/GameServer/Enums/OpenStateCondType.cs new file mode 100644 index 00000000..f9bdb9f6 --- /dev/null +++ b/src/GameServer/Enums/OpenStateCondType.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Weedwacker.GameServer.Enums +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum OpenStateCondType + { + OPEN_STATE_COND_PARENT_QUEST, + OPEN_STATE_COND_QUEST, + OPEN_STATE_COND_PLAYER_LEVEL, + OPEN_STATE_OFFERING_LEVEL, + OPEN_STATE_CITY_REPUTATION_LEVEL + } +} diff --git a/src/GameServer/Enums/SceneTagCondType.cs b/src/GameServer/Enums/SceneTagCondType.cs new file mode 100644 index 00000000..17307dae --- /dev/null +++ b/src/GameServer/Enums/SceneTagCondType.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Weedwacker.GameServer.Enums +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum SceneTagCondType + { + NONE, // Don't use + SCENE_TAG_COND_TYPE_SPECIFIC_ACTIVITY_OPEN, + SCENE_TAG_COND_TYPE_ACTIVITY_CONTENT_OPEN, + SCENE_TAG_COND_TYPE_QUEST_GLOBAL_VAR_EQUAL, + SCENE_TAG_COND_TYPE_QUEST_FINISH, + } +} diff --git a/src/GameServer/GameServer.cs b/src/GameServer/GameServer.cs index b18b10ee..efbab97c 100644 --- a/src/GameServer/GameServer.cs +++ b/src/GameServer/GameServer.cs @@ -9,6 +9,7 @@ using Weedwacker.GameServer.Data; using Weedwacker.GameServer.Systems.Avatar; using Weedwacker.GameServer.Systems.World; using Weedwacker.Shared.Authentication; +using Weedwacker.Shared.Network.Proto; using Weedwacker.Shared.Utils.Configuration; namespace Weedwacker.GameServer @@ -16,6 +17,7 @@ namespace Weedwacker.GameServer internal static class GameServer { private static readonly HttpClient client = new HttpClient(); + public static GameConfig Configuration; public static SortedList OnlinePlayers = new(); // private static HashSet Worlds = new(); @@ -68,5 +70,23 @@ namespace Weedwacker.GameServer //TODO return int.MaxValue; } + + internal static async Task GetSocialDetailByUid(int askerUid, uint reqUid) + { + SocialDetail socialDetail; + if (OnlinePlayers.TryGetValue((int)reqUid, out Connection session)) + { + return session.Player.SocialManager.GetSocialDetail(askerUid); + } + else + { + var player = await Database.DatabaseManager.GetPlayerByGameUidAsync((int)reqUid); + if (player != null) + return player.SocialManager.GetSocialDetail(askerUid); + else + return null; + } + } + } } diff --git a/src/GameServer/GameServer.csproj b/src/GameServer/GameServer.csproj index 4ce05dce..f72e87bf 100644 --- a/src/GameServer/GameServer.csproj +++ b/src/GameServer/GameServer.csproj @@ -11,7 +11,8 @@ - + + diff --git a/src/GameServer/Packet/Recv/HandleEnterSceneDoneReq.cs b/src/GameServer/Packet/Recv/HandleEnterSceneDoneReq.cs new file mode 100644 index 00000000..1d33713b --- /dev/null +++ b/src/GameServer/Packet/Recv/HandleEnterSceneDoneReq.cs @@ -0,0 +1,34 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; + +namespace Weedwacker.GameServer.Packet.Recv +{ + [OpCode((ushort)OpCode.EnterSceneDoneReq)] + internal class HandleEnterSceneDoneReq : BaseHandler + { + public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) + { + // Finished loading + session.Player.SceneLoadState = SceneLoadState.LOADED; + + // Done + + // Spawn player in world + await session.Player.Scene.SpawnPlayerAsync(session.Player); + + // Spawn other entites already in world + await session.Player.Scene.ShowOtherEntitiesAsync(session.Player); + + // Locations + await session.SendPacketAsync(new PacketWorldPlayerLocationNotify(session.Player.World)); + await session.SendPacketAsync(new PacketScenePlayerLocationNotify(session.Player.Scene)); + await session.SendPacketAsync(new PacketWorldPlayerRTTNotify(session.Player.World)); + + + // Reset timer for sending player locations + session.Player.ResetSendPlayerLocTime(); + //Rsp + await session.SendPacketAsync(new PacketEnterSceneDoneRsp(session.Player)); + } + } +} diff --git a/src/GameServer/Packet/Recv/HandleEnterSceneReadyReq.cs b/src/GameServer/Packet/Recv/HandleEnterSceneReadyReq.cs new file mode 100644 index 00000000..86faa7c7 --- /dev/null +++ b/src/GameServer/Packet/Recv/HandleEnterSceneReadyReq.cs @@ -0,0 +1,22 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Recv +{ + [OpCode((ushort)OpCode.EnterSceneReadyReq)] + internal class HandleEnterSceneReadyReq : BaseHandler + { + public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) + { + EnterSceneReadyReq proto = EnterSceneReadyReq.Parser.ParseFrom(payload); + // Reject player if invalid token + if (session.Player.EnterSceneToken == proto.EnterSceneToken) + { + await session.Player.World.BroadcastPacketAsync(new PacketEnterScenePeerNotify(session.Player)); + + } + await session.SendPacketAsync(new PacketEnterSceneReadyRsp(session.Player, proto.EnterSceneToken)); + } + } +} diff --git a/src/GameServer/Packet/Recv/HandleEnterWorldAreaReq.cs b/src/GameServer/Packet/Recv/HandleEnterWorldAreaReq.cs new file mode 100644 index 00000000..4d3f6713 --- /dev/null +++ b/src/GameServer/Packet/Recv/HandleEnterWorldAreaReq.cs @@ -0,0 +1,27 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Recv +{ + [OpCode((ushort)OpCode.EnterWorldAreaReq)] + internal class HandleEnterWorldAreaReq : BaseHandler + { + public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) + { + /* + * The client determines the AreaID from its position relative to + * the polygonData's min and max Area in sceneX_worldArea.json + * AreaType == 1 => looks in level1Area, use id2 + * AreaType == 2 => looks in level2Areas, use id1 + */ + EnterWorldAreaReq req = EnterWorldAreaReq.Parser.ParseFrom(payload); + uint areaId = req.AreaId; + uint areaType = req.AreaType; + + await session.Player.EnterWorldAreaAsync(areaType, areaId); + + await session.SendPacketAsync(new PacketEnterWorldAreaRsp(session.Player, areaId, areaType)); + } + } +} diff --git a/src/GameServer/Packet/Recv/HandleGetPlayerSocialDetailReq.cs b/src/GameServer/Packet/Recv/HandleGetPlayerSocialDetailReq.cs new file mode 100644 index 00000000..131d3890 --- /dev/null +++ b/src/GameServer/Packet/Recv/HandleGetPlayerSocialDetailReq.cs @@ -0,0 +1,19 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Recv +{ + [OpCode((ushort)OpCode.GetPlayerSocialDetailReq)] + internal class HandleGetPlayerSocialDetailReq : BaseHandler + { + public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) + { + GetPlayerSocialDetailReq req = GetPlayerSocialDetailReq.Parser.ParseFrom(payload); + + SocialDetail? detail = await GameServer.GetSocialDetailByUid(session.Player.GameUid, req.Uid); + + await session.SendPacketAsync(new PacketGetPlayerSocialDetailRsp(detail)); + } + } +} diff --git a/src/GameServer/Packet/Recv/HandleGetPlayerTokenReq.cs b/src/GameServer/Packet/Recv/HandleGetPlayerTokenReq.cs index 5b8544bb..f53ff849 100644 --- a/src/GameServer/Packet/Recv/HandleGetPlayerTokenReq.cs +++ b/src/GameServer/Packet/Recv/HandleGetPlayerTokenReq.cs @@ -12,7 +12,7 @@ using Weedwacker.Shared.Utils; namespace Weedwacker.GameServer.Packet.Recv { - [OpCode("GetPlayerTokenReq")] + [OpCode((ushort)OpCode.GetPlayerTokenReq)] internal class HandleGetPlayerTokenReq : BaseHandler { public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) @@ -39,6 +39,8 @@ namespace Weedwacker.GameServer.Packet.Recv session.Stop(); return; } + else + GameServer.OnlinePlayers.Add(session.Player.GameUid, session); // Update the player's session pointer session.Player.Session = session; diff --git a/src/GameServer/Packet/Recv/HandlePathfindingEnterSceneReq.cs b/src/GameServer/Packet/Recv/HandlePathfindingEnterSceneReq.cs new file mode 100644 index 00000000..0e257231 --- /dev/null +++ b/src/GameServer/Packet/Recv/HandlePathfindingEnterSceneReq.cs @@ -0,0 +1,17 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Recv +{ + [OpCode((ushort)OpCode.PathfindingEnterSceneReq)] + internal class HandlePathfindingEnterSceneReq : BaseHandler + { + public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) + { + PacketHead head = PacketHead.Parser.ParseFrom(header); + PathfindingEnterSceneReq req = PathfindingEnterSceneReq.Parser.ParseFrom(payload); + await session.SendPacketAsync(new PacketPathfindingEnterSceneRsp(head.ClientSequenceId)); + } + } +} diff --git a/src/GameServer/Packet/Recv/HandlePingReq.cs b/src/GameServer/Packet/Recv/HandlePingReq.cs index 7eec11ef..a1f26bb2 100644 --- a/src/GameServer/Packet/Recv/HandlePingReq.cs +++ b/src/GameServer/Packet/Recv/HandlePingReq.cs @@ -4,7 +4,7 @@ using Weedwacker.Shared.Network.Proto; namespace Weedwacker.GameServer.Packet.Recv { - [OpCode("PingReq")] + [OpCode((ushort)OpCode.PingReq)] internal class HandlePingReq : BaseHandler { public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) diff --git a/src/GameServer/Packet/Recv/HandlePlayerLoginReq.cs b/src/GameServer/Packet/Recv/HandlePlayerLoginReq.cs index 0aee3570..a666d28f 100644 --- a/src/GameServer/Packet/Recv/HandlePlayerLoginReq.cs +++ b/src/GameServer/Packet/Recv/HandlePlayerLoginReq.cs @@ -4,7 +4,7 @@ using Weedwacker.Shared.Network.Proto; namespace Weedwacker.GameServer.Packet.Recv { - [OpCode("PlayerLoginReq")] + [OpCode((ushort)OpCode.PlayerLoginReq)] internal class HandlePlayerLoginReq : BaseHandler { public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) diff --git a/src/GameServer/Packet/Recv/HandlePlayerSetPauseReq.cs b/src/GameServer/Packet/Recv/HandlePlayerSetPauseReq.cs index 5a7960e6..8398dcbe 100644 --- a/src/GameServer/Packet/Recv/HandlePlayerSetPauseReq.cs +++ b/src/GameServer/Packet/Recv/HandlePlayerSetPauseReq.cs @@ -4,7 +4,7 @@ using Weedwacker.Shared.Network.Proto; namespace Weedwacker.GameServer.Packet.Recv { - [OpCode("PlayerSetPauseReq")] + [OpCode((ushort)OpCode.PlayerSetPauseReq)] internal class HandlePlayerSetPauseReq : BaseHandler { public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) diff --git a/src/GameServer/Packet/Recv/HandlePostEnterSceneReq.cs b/src/GameServer/Packet/Recv/HandlePostEnterSceneReq.cs new file mode 100644 index 00000000..4c1f51ea --- /dev/null +++ b/src/GameServer/Packet/Recv/HandlePostEnterSceneReq.cs @@ -0,0 +1,19 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; + +namespace Weedwacker.GameServer.Packet.Recv +{ + [OpCode((ushort)OpCode.PostEnterSceneReq)] + internal class HandlePostEnterSceneReq : BaseHandler + { + public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) + { + if (session.Player.Scene.SceneData.type == SceneType.SCENE_ROOM) + { + //await session.Player.QuestManager.TriggerEventAsync(QuestTrigger.QUEST_CONTENT_ENTER_ROOM, session.Player.SceneId, 0); + } + + await session.SendPacketAsync(new PacketPostEnterSceneRsp(session.Player)); + } + } +} diff --git a/src/GameServer/Packet/Recv/HandleSceneInitFinishReq.cs b/src/GameServer/Packet/Recv/HandleSceneInitFinishReq.cs new file mode 100644 index 00000000..4b343376 --- /dev/null +++ b/src/GameServer/Packet/Recv/HandleSceneInitFinishReq.cs @@ -0,0 +1,40 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; + +namespace Weedwacker.GameServer.Packet.Recv +{ + [OpCode((ushort)OpCode.SceneInitFinishReq)] + internal class HandleSceneInitFinishReq : BaseHandler + { + public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) + { + // Info packets + await Task.WhenAll(new Task[] + { + session.SendPacketAsync(new PacketServerTimeNotify()), + session.Player.World.BroadcastPacketAsync(new PacketWorldPlayerInfoNotify(session.Player.World)), + session.SendPacketAsync(new PacketWorldDataNotify(session.Player.World)), + session.SendPacketAsync(new PacketPlayerWorldSceneInfoListNotify(session.Player.Scene)), + session.SendPacketAsync(new PacketSceneForceUnlockNotify()), + session.SendPacketAsync(new BasePacket(OpCode.SceneDataNotify)), + session.SendPacketAsync(new PacketHostPlayerNotify(session.Player.World)), + session.SendPacketAsync(new PacketSceneTimeNotify(session.Player)), + session.SendPacketAsync(new PacketPlayerGameTimeNotify(session.Player)), + session.Player.Scene.BroadcastPacketAsync(new PacketPlayerEnterSceneInfoNotify(session.Player)), + session.Player.Scene.UpdateActiveAreaWeathersAsync(session.Player.WorldAreaIds), + + session.SendPacketAsync(new PacketScenePlayerInfoNotify(session.Player.World)), + session.Player.Scene.BroadcastPacketAsync(new PacketSceneTeamUpdateNotify(session.Player)), + session.Player.Scene.BroadcastPacketAsync(new PacketSyncTeamEntityNotify(session.Player)), + session.Player.Scene.BroadcastPacketAsync(new PacketSyncScenePlayTeamEntityNotify(session.Player)), + + }); ; + + // Done Packet + await session.SendPacketAsync(new PacketSceneInitFinishRsp(session.Player)); + + // Set state + session.Player.SceneLoadState = SceneLoadState.INIT; + } + } +} diff --git a/src/GameServer/Packet/Recv/HandleSetOpenStateReq.cs b/src/GameServer/Packet/Recv/HandleSetOpenStateReq.cs new file mode 100644 index 00000000..c4d4e8b5 --- /dev/null +++ b/src/GameServer/Packet/Recv/HandleSetOpenStateReq.cs @@ -0,0 +1,22 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Recv +{ + [OpCode((ushort)OpCode.SetOpenStateReq)] + internal class HandleSetOpenStateReq : BaseHandler + { + public override async Task HandleAsync(Connection session, byte[] header, byte[] payload) + { + var req = SetOpenStateReq.Parser.ParseFrom(payload); + uint openState = req.Key; + uint value = req.Value; + + if (await session.Player.OpenStateManager.SetOpenStateFromClientAsync(openState, value)) + await session.SendPacketAsync(new PacketSetOpenStateRsp(openState, value)); + else + await session.SendPacketAsync(new PacketSetOpenStateRsp(Retcode.RetFail)); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketEnterSceneDoneRsp.cs b/src/GameServer/Packet/Send/PacketEnterSceneDoneRsp.cs new file mode 100644 index 00000000..73a9e3fe --- /dev/null +++ b/src/GameServer/Packet/Send/PacketEnterSceneDoneRsp.cs @@ -0,0 +1,19 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketEnterSceneDoneRsp : BasePacket + { + public PacketEnterSceneDoneRsp(Player player) : base(Enums.OpCode.EnterSceneDoneRsp) + { + EnterSceneDoneRsp p = new EnterSceneDoneRsp() + { + EnterSceneToken = player.EnterSceneToken + }; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketEnterScenePeerNotify.cs b/src/GameServer/Packet/Send/PacketEnterScenePeerNotify.cs new file mode 100644 index 00000000..92d436a0 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketEnterScenePeerNotify.cs @@ -0,0 +1,22 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketEnterScenePeerNotify : BasePacket + { + public PacketEnterScenePeerNotify(Player player) : base(Enums.OpCode.EnterScenePeerNotify) + { + EnterScenePeerNotify proto = new EnterScenePeerNotify() + { + DestSceneId = (uint)player.SceneId, + PeerId = player.PeerId, + HostPeerId = player.World.GetHostPeerId(), + EnterSceneToken = player.EnterSceneToken + }; + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketEnterSceneReadyRsp.cs b/src/GameServer/Packet/Send/PacketEnterSceneReadyRsp.cs new file mode 100644 index 00000000..6f53e93a --- /dev/null +++ b/src/GameServer/Packet/Send/PacketEnterSceneReadyRsp.cs @@ -0,0 +1,20 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketEnterSceneReadyRsp : BasePacket + { + public PacketEnterSceneReadyRsp(Player player, uint token) : base(Enums.OpCode.EnterSceneReadyRsp, 11) + { + EnterSceneReadyRsp p = new EnterSceneReadyRsp(); + if (token == player.EnterSceneToken) + p.EnterSceneToken = token; + else + p.Retcode = (int)Retcode.RetEnterSceneTokenInvalid; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketEnterWorldAreaRsp.cs b/src/GameServer/Packet/Send/PacketEnterWorldAreaRsp.cs new file mode 100644 index 00000000..b9c5484e --- /dev/null +++ b/src/GameServer/Packet/Send/PacketEnterWorldAreaRsp.cs @@ -0,0 +1,20 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketEnterWorldAreaRsp : BasePacket + { + public PacketEnterWorldAreaRsp(Player player, uint areaId, uint areaType) : base(Enums.OpCode.EnterWorldAreaRsp) + { + EnterWorldAreaRsp p = new() + { + AreaId = areaId, + AreaType = areaType + }; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketGetPlayerSocialDetailRsp.cs b/src/GameServer/Packet/Send/PacketGetPlayerSocialDetailRsp.cs new file mode 100644 index 00000000..2a925700 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketGetPlayerSocialDetailRsp.cs @@ -0,0 +1,24 @@ +using Google.Protobuf; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketGetPlayerSocialDetailRsp : BasePacket + { + public PacketGetPlayerSocialDetailRsp(SocialDetail? detail) : base(Enums.OpCode.GetPlayerSocialDetailRsp) + { + GetPlayerSocialDetailRsp proto = new GetPlayerSocialDetailRsp(); + + if (detail != null) + { + proto.DetailData = detail; + } + else + { + proto.Retcode = (int)Retcode.RetSvrError; + } + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketHostPlayerNotify.cs b/src/GameServer/Packet/Send/PacketHostPlayerNotify.cs new file mode 100644 index 00000000..4a88017f --- /dev/null +++ b/src/GameServer/Packet/Send/PacketHostPlayerNotify.cs @@ -0,0 +1,20 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.World; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketHostPlayerNotify : BasePacket + { + public PacketHostPlayerNotify(World world) : base(Enums.OpCode.HostPlayerNotify) + { + HostPlayerNotify p = new() + { + HostUid = (uint)world.Host.GameUid, + HostPeerId = world.GetHostPeerId() + }; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketOpenStateChangeNotify.cs b/src/GameServer/Packet/Send/PacketOpenStateChangeNotify.cs new file mode 100644 index 00000000..e38002b7 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketOpenStateChangeNotify.cs @@ -0,0 +1,28 @@ +using Google.Protobuf; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketOpenStateChangeNotify : BasePacket + { + public PacketOpenStateChangeNotify(int openState, int value) : base(Enums.OpCode.OpenStateChangeNotify) + { + OpenStateChangeNotify proto = new OpenStateChangeNotify(); + proto.OpenStateMap.Add((uint)openState, (uint)value); + + Data = proto.ToByteArray(); + } + + public PacketOpenStateChangeNotify(IEnumerable> openStates) : base(Enums.OpCode.OpenStateChangeNotify) + { + + OpenStateChangeNotify proto = new OpenStateChangeNotify(); + foreach (var openState in openStates) + { + proto.OpenStateMap.Add((uint)openState.Item1, (uint)openState.Item2); + } + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketOpenStateUpdateNotify.cs b/src/GameServer/Packet/Send/PacketOpenStateUpdateNotify.cs new file mode 100644 index 00000000..ef0362e3 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketOpenStateUpdateNotify.cs @@ -0,0 +1,22 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Data; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketOpenStateUpdateNotify : BasePacket + { + public PacketOpenStateUpdateNotify(Player player) : base(Enums.OpCode.OpenStateUpdateNotify) + { + OpenStateUpdateNotify proto = new OpenStateUpdateNotify(); + + foreach ( var openState in player.OpenStates) + { + proto.OpenStateMap.Add((uint)openState.Key, (uint)openState.Value); + } + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketPathfindingEnterSceneRsp.cs b/src/GameServer/Packet/Send/PacketPathfindingEnterSceneRsp.cs new file mode 100644 index 00000000..ecb773ce --- /dev/null +++ b/src/GameServer/Packet/Send/PacketPathfindingEnterSceneRsp.cs @@ -0,0 +1,11 @@ +using Weedwacker.GameServer.Enums; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketPathfindingEnterSceneRsp : BasePacket + { + public PacketPathfindingEnterSceneRsp(uint clientSequence) : base(OpCode.PathfindingEnterSceneRsp, clientSequence) + { + } + } +} diff --git a/src/GameServer/Packet/Send/PacketPlayerEnterSceneInfoNotify.cs b/src/GameServer/Packet/Send/PacketPlayerEnterSceneInfoNotify.cs new file mode 100644 index 00000000..06234b12 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketPlayerEnterSceneInfoNotify.cs @@ -0,0 +1,53 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Inventory; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.GameServer.Systems.World; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketPlayerEnterSceneInfoNotify : BasePacket + { + public PacketPlayerEnterSceneInfoNotify(Player player) : base(Enums.OpCode.PlayerEnterSceneInfoNotify) + { + PlayerEnterSceneInfoNotify proto = new PlayerEnterSceneInfoNotify() + { + CurAvatarEntityId = player.TeamManager.GetCurrentAvatarEntity().Id, + EnterSceneToken = player.EnterSceneToken + }; + + proto.TeamEnterInfo = new TeamEnterSceneInfo() + { + TeamEntityId = player.TeamManager.EntityId, + TeamAbilityInfo = new(), //player.TeamManager.AbilitySyncState, + AbilityControlBlock = new() //TODO + }; + + proto.MpLevelEntityInfo = new MPLevelEntityInfo() + { + AuthorityPeerId = player.World.GetHostPeerId(), + AbilityInfo = new(), //TODO + EntityId = player.World.LevelEntityId + }; + + foreach (AvatarEntity avatar in player.TeamManager.ActiveTeam.Values) + { + WeaponItem weapon = avatar.Avatar.GetWeapon(); + + AvatarEnterSceneInfo avatarInfo = new AvatarEnterSceneInfo() + { + AvatarGuid = avatar.Avatar.Guid, + AvatarEntityId = avatar.Id, + WeaponGuid = weapon.Guid, + WeaponEntityId = weapon.WeaponEntityId, + AvatarAbilityInfo = new(), //TODO + WeaponAbilityInfo = new() //TODO + }; + + proto.AvatarEnterInfo.Add(avatarInfo); + } + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketPlayerGameTimeNotify.cs b/src/GameServer/Packet/Send/PacketPlayerGameTimeNotify.cs new file mode 100644 index 00000000..248ed222 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketPlayerGameTimeNotify.cs @@ -0,0 +1,21 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketPlayerGameTimeNotify : BasePacket + { + public PacketPlayerGameTimeNotify(Player player) : base(Enums.OpCode.PlayerGameTimeNotify) + { + PlayerGameTimeNotify proto = new PlayerGameTimeNotify() + { + GameTime = player.Scene.Time, + Uid = (uint)player.GameUid, + //IsHome = TODO + }; + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketPlayerStoreNotify.cs b/src/GameServer/Packet/Send/PacketPlayerStoreNotify.cs new file mode 100644 index 00000000..5ec352b4 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketPlayerStoreNotify.cs @@ -0,0 +1,28 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketPlayerStoreNotify : BasePacket + { + public PacketPlayerStoreNotify(Player player) : base(Enums.OpCode.PlayerStoreNotify) + { + BuildHeader(2); + + PlayerStoreNotify p = new PlayerStoreNotify() + { + StoreType = StoreType.Pack, + WeightLimit = 30000 + }; + + foreach (var item in player.Inventory.GuidMap.Values) + { + Item itemProto = item.ToProto(); + p.ItemList.Add(itemProto); + } + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketPlayerWorldSceneInfoListNotify.cs b/src/GameServer/Packet/Send/PacketPlayerWorldSceneInfoListNotify.cs new file mode 100644 index 00000000..485a82ec --- /dev/null +++ b/src/GameServer/Packet/Send/PacketPlayerWorldSceneInfoListNotify.cs @@ -0,0 +1,25 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.World; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketPlayerWorldSceneInfoListNotify : BasePacket + { + public PacketPlayerWorldSceneInfoListNotify(Scene scene) : base(Enums.OpCode.PlayerWorldSceneInfoListNotify) + { + PlayerWorldSceneInfoListNotify p = new(); + + PlayerWorldSceneInfo sceneInfo = new() + { + SceneId = (uint)scene.GetId(), + IsLocked = false, //TODO + }; + sceneInfo.SceneTagIdList.AddRange(scene.SceneTags); + + p.InfoList.Add(sceneInfo); + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketPostEnterSceneRsp.cs b/src/GameServer/Packet/Send/PacketPostEnterSceneRsp.cs new file mode 100644 index 00000000..e9b9792c --- /dev/null +++ b/src/GameServer/Packet/Send/PacketPostEnterSceneRsp.cs @@ -0,0 +1,19 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketPostEnterSceneRsp : BasePacket + { + public PacketPostEnterSceneRsp(Player player) : base(Enums.OpCode.PostEnterSceneRsp) + { + PostEnterSceneRsp p = new PostEnterSceneRsp() + { + EnterSceneToken = player.EnterSceneToken + }; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketSceneAreaWeatherNotify.cs b/src/GameServer/Packet/Send/PacketSceneAreaWeatherNotify.cs new file mode 100644 index 00000000..0ab27ac6 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketSceneAreaWeatherNotify.cs @@ -0,0 +1,25 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Enums; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketSceneAreaWeatherNotify : BasePacket + { + public PacketSceneAreaWeatherNotify(ClimateType climateType, int weatherAreaId) : base(Enums.OpCode.SceneAreaWeatherNotify) + { + SceneAreaWeatherNotify p = new() + { + ClimateType = (uint)climateType, + WeatherAreaId = (uint)weatherAreaId, + /* TODO + TransDuration = , + WeatherGadgetId = , + WeatherValueMap = , + */ + }; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketSceneForceUnlockNotify.cs b/src/GameServer/Packet/Send/PacketSceneForceUnlockNotify.cs new file mode 100644 index 00000000..1d7b9687 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketSceneForceUnlockNotify.cs @@ -0,0 +1,27 @@ +using Google.Protobuf; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketSceneForceUnlockNotify : BasePacket + { + /* + * 1, + * 47, + * 37, + * What do the numbers mean??? + */ + public PacketSceneForceUnlockNotify() : base(Enums.OpCode.SceneForceUnlockNotify) + { + SceneForceUnlockNotify p = new() + { + /* TODO + IsAdd = , + ForceIdList = + */ + }; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketSceneInitFinishRsp.cs b/src/GameServer/Packet/Send/PacketSceneInitFinishRsp.cs new file mode 100644 index 00000000..737abef0 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketSceneInitFinishRsp.cs @@ -0,0 +1,16 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketSceneInitFinishRsp : BasePacket + { + public PacketSceneInitFinishRsp(Player player) : base(Enums.OpCode.SceneInitFinishRsp, 11) + { + SceneInitFinishRsp p = new SceneInitFinishRsp() { EnterSceneToken = player.EnterSceneToken }; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketScenePlayerLocationNotify.cs b/src/GameServer/Packet/Send/PacketScenePlayerLocationNotify.cs new file mode 100644 index 00000000..d0d253ff --- /dev/null +++ b/src/GameServer/Packet/Send/PacketScenePlayerLocationNotify.cs @@ -0,0 +1,26 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.GameServer.Systems.World; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketScenePlayerLocationNotify : BasePacket + { + public PacketScenePlayerLocationNotify(Scene scene) : base(Enums.OpCode.ScenePlayerLocationNotify) + { + ScenePlayerLocationNotify proto = new ScenePlayerLocationNotify() + { + SceneId = (uint)scene.GetId(), + //VehicleLocList = , //TODO + }; + + foreach (Player p in scene.Players) + { + proto.PlayerLocList.Add(p.GetPlayerLocationInfo()); + } + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketSceneTeamUpdateNotify.cs b/src/GameServer/Packet/Send/PacketSceneTeamUpdateNotify.cs index e6bdd3c4..b3c941ef 100644 --- a/src/GameServer/Packet/Send/PacketSceneTeamUpdateNotify.cs +++ b/src/GameServer/Packet/Send/PacketSceneTeamUpdateNotify.cs @@ -14,9 +14,9 @@ namespace Weedwacker.GameServer.Packet.Send IsInMp = player.IsInMultiplayer() }; - foreach (Player p in player.World.Players) + foreach (Player p in player.Scene.Players) { - foreach (AvatarEntity avatar in p.TeamManager.ActiveTeam) + foreach (AvatarEntity avatar in p.TeamManager.ActiveTeam.Values) { //TODO AbilitySyncStateInfo avatarAbilityInfo = new(); diff --git a/src/GameServer/Packet/Send/PacketSceneTimeNotify.cs b/src/GameServer/Packet/Send/PacketSceneTimeNotify.cs new file mode 100644 index 00000000..ac7b965d --- /dev/null +++ b/src/GameServer/Packet/Send/PacketSceneTimeNotify.cs @@ -0,0 +1,21 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketSceneTimeNotify : BasePacket + { + public PacketSceneTimeNotify(Player player) : base(Enums.OpCode.SceneTimeNotify) + { + SceneTimeNotify proto = new SceneTimeNotify() + { + SceneId = (uint)player.SceneId, + SceneTime = player.Scene.Time, + IsPaused = false //TODO + }; + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketServerTimeNotify.cs b/src/GameServer/Packet/Send/PacketServerTimeNotify.cs new file mode 100644 index 00000000..4bdb047b --- /dev/null +++ b/src/GameServer/Packet/Send/PacketServerTimeNotify.cs @@ -0,0 +1,18 @@ +using Google.Protobuf; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketServerTimeNotify : BasePacket + { + public PacketServerTimeNotify() : base(Enums.OpCode.ServerTimeNotify) + { + ServerTimeNotify proto = new ServerTimeNotify() + { + ServerTime = (ulong)DateTimeOffset.Now.ToUnixTimeMilliseconds() + }; + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketSetOpenStateRsp.cs b/src/GameServer/Packet/Send/PacketSetOpenStateRsp.cs new file mode 100644 index 00000000..f701556e --- /dev/null +++ b/src/GameServer/Packet/Send/PacketSetOpenStateRsp.cs @@ -0,0 +1,29 @@ +using Google.Protobuf; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketSetOpenStateRsp : BasePacket + { + public PacketSetOpenStateRsp(uint openState, uint value) : base(Enums.OpCode.SetOpenStateRsp) + { + SetOpenStateRsp proto = new SetOpenStateRsp() + { + Key = openState, + Value = value + }; + + Data = proto.ToByteArray(); + } + + public PacketSetOpenStateRsp(Retcode retcode) : base(Enums.OpCode.SetOpenStateRsp) + { + SetOpenStateRsp proto = new SetOpenStateRsp() + { + Retcode = (int)retcode + }; + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketStoreWeightLimitNotify.cs b/src/GameServer/Packet/Send/PacketStoreWeightLimitNotify.cs new file mode 100644 index 00000000..316cc8da --- /dev/null +++ b/src/GameServer/Packet/Send/PacketStoreWeightLimitNotify.cs @@ -0,0 +1,25 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Inventory; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketStoreWeightLimitNotify : BasePacket + { + public PacketStoreWeightLimitNotify() : base(Enums.OpCode.StoreWeightLimitNotify) + { + StoreWeightLimitNotify p = new StoreWeightLimitNotify() + { + StoreType = StoreType.Pack, + WeightLimit = 30000, + WeaponCountLimit = WeaponTab.InventoryLimit, + ReliquaryCountLimit = RelicTab.InventoryLimit, + MaterialCountLimit = MaterialsTab.InventoryLimit, + FurnitureCountLimit = FurnitureTab.InventoryLimit + + }; + + Data = p.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketWorldDataNotify.cs b/src/GameServer/Packet/Send/PacketWorldDataNotify.cs new file mode 100644 index 00000000..c90147d0 --- /dev/null +++ b/src/GameServer/Packet/Send/PacketWorldDataNotify.cs @@ -0,0 +1,21 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.World; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketWorldDataNotify : BasePacket + { + public PacketWorldDataNotify(World world) : base(Enums.OpCode.WorldDataNotify) + { + int worldLevel = world.WorldLevel; + int isMp = world.IsMultiplayer ? 1 : 0; + + WorldDataNotify proto = new WorldDataNotify(); + proto.WorldPropMap.Add(1, new PropValue() { Type = 1, Ival = worldLevel, Val = worldLevel }); + proto.WorldPropMap.Add(2, new PropValue() { Type = 2, Ival = isMp, Val = isMp }); + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Packet/Send/PacketWorldPlayerLocationNotify.cs b/src/GameServer/Packet/Send/PacketWorldPlayerLocationNotify.cs new file mode 100644 index 00000000..f96445ff --- /dev/null +++ b/src/GameServer/Packet/Send/PacketWorldPlayerLocationNotify.cs @@ -0,0 +1,22 @@ +using Google.Protobuf; +using Weedwacker.GameServer.Systems.Player; +using Weedwacker.GameServer.Systems.World; +using Weedwacker.Shared.Network.Proto; + +namespace Weedwacker.GameServer.Packet.Send +{ + internal class PacketWorldPlayerLocationNotify : BasePacket + { + public PacketWorldPlayerLocationNotify(World world) : base(Enums.OpCode.WorldPlayerLocationNotify) + { + WorldPlayerLocationNotify proto = new WorldPlayerLocationNotify(); + + foreach (Player p in world.Players) + { + proto.PlayerWorldLocList.Add(new PlayerWorldLocationInfo() { SceneId = (uint)p.SceneId, PlayerLoc = p.GetPlayerLocationInfo()}); + } + + Data = proto.ToByteArray(); + } + } +} diff --git a/src/GameServer/Systems/Avatar/AvatarManager.cs b/src/GameServer/Systems/Avatar/AvatarManager.cs index ff27a45b..f7b246c8 100644 --- a/src/GameServer/Systems/Avatar/AvatarManager.cs +++ b/src/GameServer/Systems/Avatar/AvatarManager.cs @@ -77,7 +77,7 @@ namespace Weedwacker.GameServer.Systems.Avatar if(Owner.TeamManager.GetCurrentTeamInfo().AvatarInfo.Count < (Owner.IsInMultiplayer() ? GameServer.Configuration.Server.GameOptions.AvatarLimits.SinglePlayerTeam : GameServer.Configuration.Server.GameOptions.AvatarLimits.SinglePlayerTeam)) { addToTeam = true; - Owner.TeamManager.GetCurrentTeamInfo().AddAvatar(avatar); + await Owner.TeamManager.AddToTeamAsync(avatar); } if (notify) await Owner.SendPacketAsync(new PacketAvatarAddNotify(avatar, addToTeam)); diff --git a/src/GameServer/Systems/Avatar/TeamInfo.cs b/src/GameServer/Systems/Avatar/TeamInfo.cs index 49ec1df9..2c862e50 100644 --- a/src/GameServer/Systems/Avatar/TeamInfo.cs +++ b/src/GameServer/Systems/Avatar/TeamInfo.cs @@ -1,12 +1,14 @@ using MongoDB.Bson.Serialization.Attributes; using Weedwacker.GameServer.Data.Excel; using Weedwacker.GameServer.Database; +using Weedwacker.GameServer.Systems.Player; using Weedwacker.Shared.Network.Proto; namespace Weedwacker.GameServer.Systems.Avatar { internal class TeamInfo { + private TeamManager Manager; public string TeamName; [BsonSerializer(typeof(IntSortedListSerializer))] [BsonElement] public SortedList AvatarInfo { get; private set; } = new(); // > clone avatars for abyss teams @@ -39,7 +41,10 @@ namespace Weedwacker.GameServer.Systems.Avatar return false; } - AvatarInfo.Add(index, IsTowerTeam ? avatar.Clone() : avatar); + if (!AvatarInfo.ContainsKey(index)) + AvatarInfo.Add(index, IsTowerTeam ? avatar.Clone() : avatar); + else + AvatarInfo[index] = IsTowerTeam ? avatar.Clone() : avatar; //TODO update team resonance and add team openConfigs diff --git a/src/GameServer/Systems/Inventory/GameItem.cs b/src/GameServer/Systems/Inventory/GameItem.cs index d014f21b..490f32a2 100644 --- a/src/GameServer/Systems/Inventory/GameItem.cs +++ b/src/GameServer/Systems/Inventory/GameItem.cs @@ -17,14 +17,13 @@ namespace Weedwacker.GameServer.Systems.Inventory public int Count; [BsonIgnore] public ulong Guid; // Player unique id. Generated each session [BsonIgnore] public bool IsNew { get; protected set; } = false; - [BsonIgnore] public ItemData ItemData { get; protected set; } - + [BsonIgnore] public ItemData ItemData => GameData.ItemDataMap[ItemId]; + public GameItem(ulong guid, int itemId, int count) { Guid = guid; ItemId = itemId; - ItemData = GameData.ItemDataMap[itemId]; IsNew = true; } diff --git a/src/GameServer/Systems/Inventory/MaterialItem.cs b/src/GameServer/Systems/Inventory/MaterialItem.cs index 95fc0620..3d786e3d 100644 --- a/src/GameServer/Systems/Inventory/MaterialItem.cs +++ b/src/GameServer/Systems/Inventory/MaterialItem.cs @@ -1,4 +1,5 @@ using MongoDB.Bson.Serialization.Attributes; +using Weedwacker.GameServer.Data; using Weedwacker.GameServer.Data.Excel; using Weedwacker.Shared.Network.Proto; @@ -6,7 +7,7 @@ namespace Weedwacker.GameServer.Systems.Inventory { internal class MaterialItem : GameItem { - [BsonIgnore] public new MaterialData ItemData { get; private set; } + [BsonIgnore] public new MaterialData ItemData => (MaterialData)GameData.ItemDataMap[ItemId]; public MaterialItem(ulong guid, int itemId, int count) : base(guid, itemId, count) { diff --git a/src/GameServer/Systems/Inventory/ReliquaryItem.cs b/src/GameServer/Systems/Inventory/ReliquaryItem.cs index fc604012..78d96e1c 100644 --- a/src/GameServer/Systems/Inventory/ReliquaryItem.cs +++ b/src/GameServer/Systems/Inventory/ReliquaryItem.cs @@ -10,8 +10,7 @@ namespace Weedwacker.GameServer.Systems.Inventory { [BsonElement] public int MainPropId { get; protected set; } [BsonElement] public HashSet? AppendPropIdList { get; private set; } = new(); - [BsonIgnore] public new ReliquaryData ItemData { get; protected set; } - + [BsonIgnore] public new ReliquaryData ItemData => (ReliquaryData)GameData.ItemDataMap[ItemId]; public ReliquaryItem(ulong guid, int itemId, int uniqueId) : base(guid, itemId) { Id = uniqueId; @@ -28,7 +27,6 @@ namespace Weedwacker.GameServer.Systems.Inventory public async Task OnLoadAsync(ulong guid) { - ItemData = (ReliquaryData)GameData.ItemDataMap[ItemId]; Guid = guid; } public EquipType GetEquipSlot() diff --git a/src/GameServer/Systems/Inventory/SubInventories/BattlePassTab.cs b/src/GameServer/Systems/Inventory/SubInventories/BattlePassTab.cs index 1e0804ca..64e38fed 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/BattlePassTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/BattlePassTab.cs @@ -19,10 +19,11 @@ namespace Weedwacker.GameServer.Systems.Inventory foreach(MaterialItem item in Items.Values) { item.Guid = Owner.GetNextGameGuid(); + Inventory.GuidMap.Add(item.Guid, item); } } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (Items.TryGetValue(itemId, out GameItem? material)) { diff --git a/src/GameServer/Systems/Inventory/SubInventories/FoodTab.cs b/src/GameServer/Systems/Inventory/SubInventories/FoodTab.cs index f9a4c50a..f22fdea9 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/FoodTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/FoodTab.cs @@ -13,7 +13,7 @@ namespace Weedwacker.GameServer.Systems.Inventory private static string mongoPathToItems = $"{nameof(InventoryManager.SubInventories)}.{ItemType.ITEM_MATERIAL}.{nameof(FoodTab)}.{nameof(Items)}"; public FoodTab(Player.Player owner, InventoryManager inventory) : base(owner, inventory) { } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (Items.TryGetValue(itemId, out GameItem? material)) { diff --git a/src/GameServer/Systems/Inventory/SubInventories/FurnitureTab.cs b/src/GameServer/Systems/Inventory/SubInventories/FurnitureTab.cs index 76283619..adfc35b5 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/FurnitureTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/FurnitureTab.cs @@ -24,15 +24,17 @@ namespace Weedwacker.GameServer.Systems.Inventory foreach (FurnitureItem item in Items.Values) { item.Guid = Owner.GetNextGameGuid(); + Inventory.GuidMap.Add(item.Guid, item); } foreach (MaterialItem item in Materials.Values) { item.Guid = Owner.GetNextGameGuid(); + Inventory.GuidMap.Add(item.Guid, item); } } //TODO FurnitureItem - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (GameData.ItemDataMap[itemId].itemType == ItemType.ITEM_MATERIAL) diff --git a/src/GameServer/Systems/Inventory/SubInventories/GadgetTab.cs b/src/GameServer/Systems/Inventory/SubInventories/GadgetTab.cs index 6b027503..305b7af1 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/GadgetTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/GadgetTab.cs @@ -12,7 +12,7 @@ namespace Weedwacker.GameServer.Systems.Inventory private static string mongoPathToItems = $"{nameof(InventoryManager.SubInventories)}.{ItemType.ITEM_MATERIAL}.{nameof(GadgetTab)}.{nameof(Items)}"; public GadgetTab(Player.Player owner, InventoryManager inventory) : base(owner, inventory) { } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (Items.TryGetValue(itemId, out GameItem? material)) { diff --git a/src/GameServer/Systems/Inventory/SubInventories/MaterialSubInv.cs b/src/GameServer/Systems/Inventory/SubInventories/MaterialSubInv.cs index 83873b76..2832e334 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/MaterialSubInv.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/MaterialSubInv.cs @@ -104,7 +104,7 @@ namespace Weedwacker.GameServer.Systems.Inventory */ return false; } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { MaterialData data = (MaterialData)GameData.ItemDataMap[itemId]; List ops = new(); diff --git a/src/GameServer/Systems/Inventory/SubInventories/MaterialsTab.cs b/src/GameServer/Systems/Inventory/SubInventories/MaterialsTab.cs index 5f6f2791..0345b600 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/MaterialsTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/MaterialsTab.cs @@ -13,7 +13,7 @@ namespace Weedwacker.GameServer.Systems.Inventory internal class MaterialsTab : InventoryTab { private string mongoPathToItems = $"{nameof(InventoryManager.SubInventories)}.{ItemType.ITEM_MATERIAL}.{nameof(MaterialsTab)}.{nameof(Items)}"; - [BsonIgnore] public new const int InventoryLimit = 9999; + [BsonIgnore] public new const int InventoryLimit = 2000; public MaterialsTab(Player.Player owner, InventoryManager inventory) : base(owner, inventory) { } @@ -24,10 +24,11 @@ namespace Weedwacker.GameServer.Systems.Inventory foreach (MaterialItem item in Items.Values) { item.Guid = Owner.GetNextGameGuid(); + inventory.GuidMap.Add(item.Guid, item); } } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (Items.TryGetValue(itemId, out GameItem? material)) { diff --git a/src/GameServer/Systems/Inventory/SubInventories/PreciousTab.cs b/src/GameServer/Systems/Inventory/SubInventories/PreciousTab.cs index 6c68571c..46259b65 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/PreciousTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/PreciousTab.cs @@ -15,7 +15,7 @@ namespace Weedwacker.GameServer.Systems.Inventory private static string mongoPathToItems = $"{nameof(InventoryManager.SubInventories)}.{ItemType.ITEM_MATERIAL}.{nameof(PreciousTab)}.{nameof(Items)}"; public PreciousTab(Player.Player owner, InventoryManager inventory) : base(owner, inventory) { } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (Items.TryGetValue(itemId, out GameItem? material)) { diff --git a/src/GameServer/Systems/Inventory/SubInventories/PromoteTab.cs b/src/GameServer/Systems/Inventory/SubInventories/PromoteTab.cs index 8815a641..e1f43995 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/PromoteTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/PromoteTab.cs @@ -14,7 +14,7 @@ namespace Weedwacker.GameServer.Systems.Inventory private static string mongoPathToItems = $"{nameof(InventoryManager.SubInventories)}.{ItemType.ITEM_MATERIAL}.{nameof(PromoteTab)}.{nameof(Items)}"; public PromoteTab(Player.Player owner, InventoryManager inventory) : base(owner, inventory) { } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (Items.TryGetValue(itemId, out GameItem? material)) { diff --git a/src/GameServer/Systems/Inventory/SubInventories/QuestTab.cs b/src/GameServer/Systems/Inventory/SubInventories/QuestTab.cs index a0c82542..06c2c586 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/QuestTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/QuestTab.cs @@ -14,7 +14,7 @@ namespace Weedwacker.GameServer.Systems.Inventory private static string mongoPathToItems = $"{nameof(InventoryManager.SubInventories)}.{ItemType.ITEM_MATERIAL}.{nameof(QuestTab)}.{nameof(Items)}"; public QuestTab(Player.Player owner, InventoryManager inventory) : base(owner, inventory) { } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (Items.TryGetValue(itemId, out GameItem? material)) { diff --git a/src/GameServer/Systems/Inventory/SubInventories/RelicTab.cs b/src/GameServer/Systems/Inventory/SubInventories/RelicTab.cs index 4cd56a3c..faed02d9 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/RelicTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/RelicTab.cs @@ -34,7 +34,7 @@ namespace Weedwacker.GameServer.Systems.Inventory } } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (GameData.ItemDataMap[itemId].itemType == ItemType.ITEM_MATERIAL) { diff --git a/src/GameServer/Systems/Inventory/SubInventories/SubInventory.cs b/src/GameServer/Systems/Inventory/SubInventories/SubInventory.cs index a3e648a0..4e787b8a 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/SubInventory.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/SubInventory.cs @@ -16,7 +16,7 @@ namespace Weedwacker.GameServer.Systems.Inventory } // return null if no update item - internal abstract Task AddItemAsync(int itemId, int count = 1); + public abstract Task AddItemAsync(int itemId, int count = 1); // Delete if count reaches 0 internal abstract Task RemoveItemAsync(GameItem item, int count = 1); diff --git a/src/GameServer/Systems/Inventory/SubInventories/WeaponTab.cs b/src/GameServer/Systems/Inventory/SubInventories/WeaponTab.cs index 8060ec47..803bdf85 100644 --- a/src/GameServer/Systems/Inventory/SubInventories/WeaponTab.cs +++ b/src/GameServer/Systems/Inventory/SubInventories/WeaponTab.cs @@ -27,15 +27,17 @@ namespace Weedwacker.GameServer.Systems.Inventory foreach (WeaponItem item in Items.Values) { item.Guid = Owner.GetNextGameGuid(); + Inventory.GuidMap.Add(item.Guid, item); if (item.EquippedAvatar != 0) await inventory.EquipWeapon(owner.Avatars.Avatars[item.EquippedAvatar].Guid, item.Guid); } foreach (MaterialItem item in UpgradeMaterials.Values) { item.Guid = Owner.GetNextGameGuid(); + Inventory.GuidMap.Add(item.Guid, item); } } - internal override async Task AddItemAsync(int itemId, int count = 1) + public override async Task AddItemAsync(int itemId, int count = 1) { if (GameData.ItemDataMap[itemId].itemType == ItemType.ITEM_MATERIAL) diff --git a/src/GameServer/Systems/Inventory/WeaponItem.cs b/src/GameServer/Systems/Inventory/WeaponItem.cs index 40228dec..7e9956db 100644 --- a/src/GameServer/Systems/Inventory/WeaponItem.cs +++ b/src/GameServer/Systems/Inventory/WeaponItem.cs @@ -10,13 +10,11 @@ namespace Weedwacker.GameServer.Systems.Inventory [BsonElement] public List? Affixes { get; protected set; } = new(); [BsonElement] public int Refinement { get; protected set; } = 0; [BsonIgnore] public uint WeaponEntityId; - [BsonIgnore] public new WeaponData ItemData; + [BsonIgnore] public new WeaponData ItemData => (WeaponData)GameData.ItemDataMap[ItemId]; public WeaponItem(ulong guid, int itemId, int uniqueId) : base(guid, itemId) { Id = uniqueId; - - ItemData = (WeaponData)GameData.ItemDataMap[ItemId]; Level = 1; if (ItemData.skillAffix != null) @@ -33,7 +31,6 @@ namespace Weedwacker.GameServer.Systems.Inventory public async Task OnLoadAsync(ulong guid) { - ItemData = (WeaponData)GameData.ItemDataMap[ItemId]; Guid = guid; Count = 1; } diff --git a/src/GameServer/Systems/Player/OpenStateManager.cs b/src/GameServer/Systems/Player/OpenStateManager.cs new file mode 100644 index 00000000..2e4182fe --- /dev/null +++ b/src/GameServer/Systems/Player/OpenStateManager.cs @@ -0,0 +1,150 @@ +using MongoDB.Driver; +using Weedwacker.GameServer.Data; +using Weedwacker.GameServer.Data.Excel; +using Weedwacker.GameServer.Database; +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Packet.Send; + +namespace Weedwacker.GameServer.Systems.Player +{ + internal class OpenStateManager + { + private Player Owner; + + public OpenStateManager(Player owner) + { + Owner = owner; + } + + public async Task OnPlayerCreate() + { + foreach(int openState in DEFAULT_OPEN_STATES) + { + await SetOpenStateAsync(openState, 1); + } + } + + public async Task OnLoginAsync() + { + // Try unlocking open states on player login. This handles accounts where unlock conditions were + // already met before certain open state unlocks were implemented. + await TryUnlockOpenStatesAsync(false); + + // Send notify to the client. + await Owner.SendPacketAsync(new PacketOpenStateUpdateNotify(Owner)); + + } + + // Set of open states that are set per default for all accounts. + public static HashSet DEFAULT_OPEN_STATES = (HashSet)GameData.OpenStateDataMap.Where(w => w.Value.defaultState == true).Select(s => s.Key); + + private async Task SetOpenStateAsync(int openState, int value, bool sendNotify = true) + { + int previousValue = Owner.OpenStates.GetValueOrDefault((OpenStateType)openState, 0); + + if (value != previousValue) + { + Owner.OpenStates.Add((OpenStateType)openState, value); + + // Update Database + var filter = Builders.Filter.Where(w => w.AccountUid == Owner.AccountUid); + var update = Builders.Update.Set($"{nameof(Player.OpenStates)}.{(OpenStateType)openState}", value); + await DatabaseManager.UpdatePlayerAsync(filter, update); + + if (sendNotify) + { + await Owner.SendPacketAsync(new PacketOpenStateChangeNotify(openState, value)); + } + } + } + + /********** + Condition checking for setting open states. + **********/ + private bool AreConditionsMet(OpenStateData openState) + { + // Check all conditions and test if at least one of them is violated. + foreach (var condition in openState.cond) + { + switch (condition.condType) + { + // For level conditions, check if the player has reached the necessary level. + case OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL: + { + if (Owner.PlayerProperties[PlayerProperty.PROP_PLAYER_LEVEL] < condition.param) + { + return false; + } + else continue; + } + case OpenStateCondType.OPEN_STATE_COND_QUEST: + { + // ToDo: Implement. + return false; + } + case OpenStateCondType.OPEN_STATE_COND_PARENT_QUEST: + { + // ToDo: Implement. + return false; + } + case OpenStateCondType.OPEN_STATE_OFFERING_LEVEL: + { + // ToDo: Implement. + return false; + } + case OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL: + { + // ToDo: Implement. + return false; + } + } + } + + // Done. If we didn't find any violations, all conditions are met. + return true; + } + + /********** + Setting open states from the client (via `SetOpenStateReq`). + **********/ + public async Task SetOpenStateFromClientAsync(uint openState, uint value) + { + OpenStateData data = GameData.OpenStateDataMap[(int)openState]; + + // Make sure that this is an open state that the client is allowed to set, + // and that it doesn't have any further conditions attached. + if (data == null || !data.allowClientOpen || !AreConditionsMet(data)) + { + return false; + } + else + { + // Set. + await SetOpenStateAsync((int)openState, (int)value); + return true; + } + } + + /********** + Triggered unlocking of open states (unlock states whose conditions have been met.) + **********/ + public async Task TryUnlockOpenStatesAsync(bool sendNotify) + { + // Get list of open states that are not yet unlocked. + var lockedStates = GameData.OpenStateDataMap.Where(s => Owner.OpenStates.GetValueOrDefault((OpenStateType)s.Key, 0) == 0); + + // Try unlocking all of them. + foreach (var state in lockedStates) + { + // To auto-unlock a state, it has to meet three conditions: + // * it can not be a state that is unlocked by the client, + // * it has to meet all its unlock conditions + if (!state.Value.allowClientOpen && AreConditionsMet(state.Value)) + { + await SetOpenStateAsync(state.Key, 1, sendNotify); + } + } + } + } +} + diff --git a/src/GameServer/Systems/Player/Player.cs b/src/GameServer/Systems/Player/Player.cs index 7a26263c..46d34c80 100644 --- a/src/GameServer/Systems/Player/Player.cs +++ b/src/GameServer/Systems/Player/Player.cs @@ -25,7 +25,8 @@ namespace Weedwacker.GameServer.Systems.Player [BsonIgnore] public World.World? World; [BsonIgnore] public Scene? Scene { get; private set; } [BsonElement] public int SceneId { get; private set; } - [BsonElement] public int RegionId { get; private set; } + [BsonElement] public Tuple WorldAreaIds { get; private set; } // + [BsonElement] public int RegionId { get; private set; } = 1; [BsonIgnore] public uint PeerId; [BsonIgnore] public Vector3 Position; [BsonIgnore] public Vector3 Rotation; @@ -35,11 +36,14 @@ namespace Weedwacker.GameServer.Systems.Player public int NextResinRefresh; public int LastDailyReset; public Dictionary PlayerProperties { get; set; } = new(); // SET ONLY THROUGH THE PROPMANAGER + public Dictionary OpenStates = new(); // SET ONLY THROUGH THE OPENSTATEMANAGER [BsonIgnore] public PlayerPropertyManager PropManager; + [BsonIgnore] public OpenStateManager OpenStateManager; [BsonIgnore] public ResinManager ResinManager; [BsonIgnore] public Connection? Session; // Set by HandleGetPlayerTokenReq [BsonIgnore] public string Token { get; set; } // Obtained and used When Logging in [BsonIgnore] public uint EnterSceneToken; + private long NextSendPlayerLocTime = 0; [BsonIgnore] private bool Paused; [BsonIgnore] public bool HasSentLoginPackets { get; private set; } = false; [BsonIgnore] private ulong NextGuid = 0; @@ -54,6 +58,7 @@ namespace Weedwacker.GameServer.Systems.Player [BsonIgnore] public TeamManager TeamManager; // Loaded by DatabaseManager + public Player(string heroName, string accountUid, int gameUid) { Profile = new(heroName); @@ -67,19 +72,19 @@ namespace Weedwacker.GameServer.Systems.Player PropManager = new(this); ResinManager = new(this); SocialManager = new(this); + OpenStateManager = new(this); } private async Task OnCreate() { await PropManager.SetPropertyAsync(PlayerProperty.PROP_PLAYER_LEVEL, 1, false); + await PropManager.SetPropertyAsync(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, 1, false); await PropManager.SetPropertyAsync(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1, false); await PropManager.SetPropertyAsync(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50, false); await ResinManager.AddResinAsync(160); await PropManager.SetPropertyAsync(PlayerProperty.PROP_IS_FLYABLE, 0, false); await PropManager.SetPropertyAsync(PlayerProperty.PROP_MAX_STAMINA, 10000, false); await PropManager.SetPropertyAsync(PlayerProperty.PROP_CUR_PERSIST_STAMINA, 10000, false); - SceneId = 3; - RegionId = 1; // Pick character Session.State = SessionState.PICKING_CHARACTER; @@ -91,6 +96,11 @@ namespace Weedwacker.GameServer.Systems.Player return ((ulong)GameUid << 32) + nextId; } + public void ResetSendPlayerLocTime() + { + NextSendPlayerLocTime = DateTimeOffset.Now.ToUnixTimeMilliseconds() + 5000; + } + public async Task SetMainCharacter(int avatarId, string heroName) { if (GameData.AvatarHeroEntityDataMap.ContainsKey(avatarId) && MainCharacterId == 0) @@ -98,10 +108,11 @@ namespace Weedwacker.GameServer.Systems.Player MainCharacterId = avatarId; Profile.HeroName = heroName; Profile.Nickname = heroName; + Profile.HeadImage = new() { AvatarId = (uint)avatarId, CostumeId = 0 }; // Update Database var filter = Builders.Filter.Where(w => w.AccountUid == AccountUid); - var update = Builders.Update.Set(w => w.Profile.HeroName, heroName).Set(w => w.Profile.Nickname, heroName).Set(w => w.MainCharacterId, avatarId); + var update = Builders.Update.Set(w => w.Profile, Profile).Set(w => w.MainCharacterId, avatarId); await DatabaseManager.UpdatePlayerAsync(filter, update); return true; } @@ -119,14 +130,31 @@ namespace Weedwacker.GameServer.Systems.Player Scene = scene; if (scene == null) SceneId = 0; else SceneId = scene.SceneData.id; + + // Update Database + var filter = Builders.Filter.Where(w => w.AccountUid == AccountUid); + var update = Builders.Update.Set(w => w.SceneId, SceneId); + await DatabaseManager.UpdatePlayerAsync(filter, update); + } + + public async Task EnterWorldAreaAsync(uint areaType, uint areaID, bool isInit = false) + { + if (areaType == 2) + { + if (!isInit) await Scene.UpdateActiveAreaWeathersAsync(WorldAreaIds); + WorldAreaIds = Tuple.Create(WorldAreaIds.Item1, (int)areaID); + } + else + WorldAreaIds = Tuple.Create((int)areaID, WorldAreaIds.Item2); + + // Update Database + var filter = Builders.Filter.Where(w => w.AccountUid == AccountUid); + var update = Builders.Update.Set(w => w.WorldAreaIds, WorldAreaIds); + await DatabaseManager.UpdatePlayerAsync(filter, update); } public async Task OnLoginAsync() { - // Create world - World.World world = new(this); - - // Show opening cutscene if player has no avatars if (Avatars.GetAvatarCount() == 0) { @@ -134,17 +162,25 @@ namespace Weedwacker.GameServer.Systems.Player return; } - else if (SceneId == 0) + + await SendPacketAsync(new PacketPlayerDataNotify(this)); + await OpenStateManager.OnLoginAsync(); + await SendPacketAsync(new PacketStoreWeightLimitNotify()); + await SendPacketAsync(new PacketPlayerStoreNotify(this)); + await SendPacketAsync(new PacketAvatarDataNotify(this)); + + // Create world + World.World world = new(this); + if (SceneId == 0 || Position == new Vector3(0,0,0)) // new player? { SceneId = 3; + WorldAreaIds = Tuple.Create(1, 109); // beach + await EnterWorldAreaAsync(1, 1, true); await world.AddPlayerAsync(this, EnterReason.Login, EnterType.Self, true); } else await world.AddPlayerAsync(this, EnterReason.Login); - await SendPacketAsync(new PacketPlayerDataNotify(this)); - await SendPacketAsync(new PacketAvatarDataNotify(this)); - // Multiplayer setting await PropManager.SetPropertyAsync(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, (int)MpSettingType.EnterAfterApply, false); await PropManager.SetPropertyAsync(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1, false); @@ -161,6 +197,7 @@ namespace Weedwacker.GameServer.Systems.Player BattlePassManager = new(this); GadgetManager = new(this); EnergyManager = new(this); + OpenStateManager = new(this); } public bool IsInMultiplayer() { return World != null && World.IsMultiplayer; } @@ -207,6 +244,16 @@ namespace Weedwacker.GameServer.Systems.Player return onlineInfo; } + public PlayerLocationInfo GetPlayerLocationInfo() + { + return new PlayerLocationInfo() + { + Uid = (uint)GameUid, + Pos = new() { X = Position.X, Y = Position.Y, Z = Position.Z }, + Rot = new() { X = Rotation.X, Y = Rotation.Y, Z = Rotation.Z } + }; + } + public async Task SendPacketAsync(BasePacket packet) { await Session.SendPacketAsync(packet); diff --git a/src/GameServer/Systems/Player/PlayerProfile.cs b/src/GameServer/Systems/Player/PlayerProfile.cs index 6ee69c53..d1d71723 100644 --- a/src/GameServer/Systems/Player/PlayerProfile.cs +++ b/src/GameServer/Systems/Player/PlayerProfile.cs @@ -9,10 +9,10 @@ namespace Weedwacker.GameServer.Systems.Player public string Nickname; public int Level = 1; public int WorldLevel = 1; - public Tuple? Birthday; // + public Birthday Birthday = new() { Day = 0, Month = 0 }; // public ProfilePicture? HeadImage; // public string? HeroName; - public string? Signature; + public string? Signature = ""; public int Achievements; public int DaysSinceLastLogin; public int LastActiveTime; diff --git a/src/GameServer/Systems/Player/TeamManager.cs b/src/GameServer/Systems/Player/TeamManager.cs index d73edcea..69ac870c 100644 --- a/src/GameServer/Systems/Player/TeamManager.cs +++ b/src/GameServer/Systems/Player/TeamManager.cs @@ -1,4 +1,5 @@ using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; using Vim.Math3d; using Weedwacker.GameServer.Database; using Weedwacker.GameServer.Enums; @@ -18,17 +19,19 @@ namespace Weedwacker.GameServer.Systems.Player [BsonElement] public SortedList Teams { get; private set; } = new(); // [BsonSerializer(typeof(IntSortedListSerializer))] [BsonElement] public SortedList TowerTeams { get; private set; } = new(); // Store Abyss teams separately - [BsonElement] public int CurrentTeamIndex { get; private set; } = 1; // count from 0 - public int CurrentCharacterIndex = 0; // count from 0 - [BsonIgnore] public SortedSet ActiveTeam { get; private set; } = new(); [BsonIgnore] public TeamInfo MpTeam = new(); + [BsonElement] public int CurrentTeamIndex { get; private set; } = 1; // count from 1 + public int CurrentCharacterIndex = 0; // count from 0 + [BsonIgnore] public SortedList ActiveTeam = new(); // index + [BsonIgnore] public uint EntityId; + [BsonIgnore] public AbilitySyncStateInfo AbilitySyncState = new(); //TODO public TeamManager(Player player) { Owner = player; OwnerId = player.GameUid; - for (int i = 0; i < GameServer.Configuration.Server.GameOptions.Constants.DEFAULT_TEAMS; i++) + for (int i = 1; i <= GameServer.Configuration.Server.GameOptions.Constants.DEFAULT_TEAMS; i++) { Teams.Add(i, new TeamInfo()); } @@ -37,16 +40,23 @@ namespace Weedwacker.GameServer.Systems.Player public async Task OnLoadAsync(Player owner) { Owner = owner; + ActiveTeam = new(); // Point to the "real" avatars - Teams.Values.AsParallel().ForAll(async w => { foreach (var entry in w.AvatarInfo) + var reloadedTeams = new SortedList(); + foreach (var team in Teams) + { + reloadedTeams.Add(team.Key, new(team.Value.TeamName)); + foreach(var entry in team.Value.AvatarInfo) { - w.AvatarInfo.Add(entry.Key, owner.Avatars.GetAvatarById(entry.Value.AvatarId)); + reloadedTeams[team.Key].AvatarInfo.Add(entry.Key, owner.Avatars.GetAvatarById(entry.Value.AvatarId)); } - }); + } + Teams = reloadedTeams; + // You stay simple clones >:) TowerTeams.Values.AsParallel().ForAll(async w => await w.OnLoadAsync(owner)); - foreach (ushort avatar in Teams[CurrentTeamIndex].AvatarInfo.Keys) - ActiveTeam.Add(new AvatarEntity(Teams[CurrentTeamIndex], avatar)); + foreach (ushort characterIndex in Teams[CurrentTeamIndex].AvatarInfo.Keys) + ActiveTeam.Add(characterIndex, new AvatarEntity(Teams[CurrentTeamIndex], characterIndex)); } /** @@ -66,6 +76,21 @@ namespace Weedwacker.GameServer.Systems.Player return -1; } + public async Task AddToTeamAsync(Avatar.Avatar avatar, int teamIndex = -1, int cIndex = -1) + { + if(teamIndex == -1) + { + Teams[CurrentTeamIndex].AddAvatar(avatar); + } + else + { + Teams[teamIndex].AddAvatar(avatar, cIndex == -1 ? CurrentCharacterIndex : cIndex); + } + + // Update Database + await DatabaseManager.SaveTeamsAsync(this); + } + private bool SetCurrentTeamId(int currentTeamIndex) { if (Teams.ContainsKey(currentTeamIndex)) @@ -94,7 +119,7 @@ namespace Weedwacker.GameServer.Systems.Player } public TeamInfo GetCurrentSinglePlayerTeamInfo() { return Teams[CurrentTeamIndex]; } - public AvatarEntity GetCurrentAvatarEntity() { return ActiveTeam.ElementAt(CurrentCharacterIndex); } + public AvatarEntity GetCurrentAvatarEntity() { return ActiveTeam[CurrentCharacterIndex]; } public bool IsSpawned() { @@ -116,65 +141,17 @@ namespace Weedwacker.GameServer.Systems.Player return GameServer.Configuration.Server.GameOptions.AvatarLimits.SinglePlayerTeam; } + //TODO public async Task UpdateTeamEntities() { - // If current team has changed - AvatarEntity currentEntity = GetCurrentAvatarEntity(); - Dictionary existingAvatars = new(); - int prevSelectedAvatarIndex = -1; - - foreach (AvatarEntity entity in ActiveTeam) + if (Owner.IsInMultiplayer()) { - existingAvatars.Add(entity.Avatar.AvatarId, entity); } - - - // Add back entities into team - for (int i = 0; i < GetCurrentTeamInfo().AvatarInfo.Count; i++) + else { - int avatarId = GetCurrentTeamInfo().AvatarInfo[i].AvatarId; - AvatarEntity entity; - - if (existingAvatars.ContainsKey(avatarId)) - { - entity = existingAvatars[avatarId]; - existingAvatars.Remove(avatarId); - if (entity == currentEntity) - { - prevSelectedAvatarIndex = i; - } - } - else - { - entity = new AvatarEntity(Owner.Scene, Owner.Avatars.GetAvatarById(avatarId)); - } - - ActiveTeam.Add(entity); } - - // Unload removed entities - foreach (AvatarEntity entity in existingAvatars.Values) - { - Owner.Scene.RemoveEntityAsync(entity); - } - - // Set new selected character index - if (prevSelectedAvatarIndex == -1) - { - // Previous selected avatar is not in the same spot, we will select the current one in the prev slot - prevSelectedAvatarIndex = Math.Min(CurrentCharacterIndex, ActiveTeam.Count - 1); - } - CurrentCharacterIndex = prevSelectedAvatarIndex; - // Packets await Owner.World.BroadcastPacketAsync(new PacketSceneTeamUpdateNotify(Owner)); - - // Check if character changed - if (currentEntity != GetCurrentAvatarEntity()) - { - // Remove and Add - await Owner.Scene.ReplaceEntityAsync(currentEntity, GetCurrentAvatarEntity()); - } } public async Task SetupAvatarTeamAsync(int teamId, List list) @@ -256,10 +233,10 @@ namespace Weedwacker.GameServer.Systems.Player int index = -1; for (int i = 0; i < ActiveTeam.Count; i++) { - if (guid == ActiveTeam.ElementAt(i).Avatar.Guid) + if (guid == ActiveTeam[i].Avatar.Guid) { index = i; - newEntity = ActiveTeam.ElementAt(i); + newEntity = ActiveTeam[i]; } } @@ -301,7 +278,7 @@ namespace Weedwacker.GameServer.Systems.Player for (int i = 0; i < ActiveTeam.Count; i++) { - AvatarEntity entity = ActiveTeam.ElementAt(i); + AvatarEntity entity = ActiveTeam[i]; if (entity.LiveState == LifeState.LIFE_ALIVE) { replaceIndex = i; @@ -326,7 +303,7 @@ namespace Weedwacker.GameServer.Systems.Player public async Task ReviveAvatarAsync(Avatar.Avatar avatar) { - foreach (AvatarEntity entity in ActiveTeam) + foreach (AvatarEntity entity in ActiveTeam.Values) { if (entity.Avatar == avatar) { @@ -349,7 +326,7 @@ namespace Weedwacker.GameServer.Systems.Player public async Task HealAvatar(Avatar.Avatar avatar, int healRate, int healAmount) { - foreach (AvatarEntity entity in ActiveTeam) + foreach (AvatarEntity entity in ActiveTeam.Values) { if (entity.Avatar == avatar) { diff --git a/src/GameServer/Systems/Script/Scene/SceneInfo.cs b/src/GameServer/Systems/Script/Scene/SceneInfo.cs index d6679697..fbc74cc3 100644 --- a/src/GameServer/Systems/Script/Scene/SceneInfo.cs +++ b/src/GameServer/Systems/Script/Scene/SceneInfo.cs @@ -1,37 +1,100 @@ -using Vim.Math3d; +using System.Text.RegularExpressions; +using NLua; +using Vim.Math3d; namespace Weedwacker.GameServer.Systems.Script.Scene { internal class SceneInfo { - public SceneConfig scene_config; - public SortedList blocks; // - public SortedList block_rects; // - public HashSet? dummy_points; // load dummy points from Scene_.lua - public HashSet? routes_config; // load routes from ??? + Lua LuaState; + public SceneConfig? scene_config; + + public SortedList? blocks; // + public SortedList? block_rects = new(); // + public LuaTable dummy_points;// => LuaState.GetTable("dummy_points"); // load dummy points from Scene_.lua + public LuaTable routes_config;// => LuaState.GetTable("routes_config"); // load routes from ??? + + public static Task CreateAsync(Lua lua, int sceneId, string scriptPath) + { + var scene = new SceneInfo(); + return scene.InitializeAsync(lua, sceneId, scriptPath); + } + + private async Task InitializeAsync(Lua lua, int sceneId, string scriptPath) + { + LuaState = lua; + + FileInfo sceneInfo = new(Path.Combine(scriptPath, "Scene", $"{sceneId}", $"scene{sceneId}.lua")); + string script = await File.ReadAllTextAsync(sceneInfo.FullName); + //script = Regex.Unescape(script); + script = Regex.Replace(script, @"\r\n", "; "); // replace new lines with ';' + script = Regex.Replace(script, "\"", "'"); // replace " with ' + lua.DoString($"_SCENE{sceneId} = {{}}"); + lua.DoString("loadScene = load (\"" + @script +"\""+ $", \";_;\", \"bt\" , _SCENE{sceneId})"); + lua.DoString("loadScene()"); + if(lua[$"_SCENE{sceneId}.{nameof(scene_config)}"] != null) + scene_config = new(lua.GetTable($"_SCENE{sceneId}.{nameof(scene_config)}")); + if (lua[$"_SCENE{sceneId}.{nameof(blocks)}"] != null) + { + var bloks = lua.GetTable($"_SCENE{sceneId}.{nameof(blocks)}"); + blocks = new SortedList(lua.GetTableDict(bloks).ToDictionary(w => (int)(long)w.Key, w => (int)(long)w.Value)); + } + if (lua[$"_SCENE{sceneId}.{nameof(block_rects)}"] != null) + { + var rects = lua.GetTable($"_SCENE{sceneId}.{nameof(block_rects)}"); + var rectDict = lua.GetTableDict(rects); + block_rects = new SortedList(rectDict.ToDictionary(w => (int)(long)w.Key, w => new Rectangle(w.Value as LuaTable))); + } + + return this; + } public class SceneConfig { - public Vector2 begin_pos; // x,z - public Vector2 size; // x,z - public Vector3 born_pos; - public Vector3 born_rot; - public int die_y; + private LuaTable Table; + // ONLY X,Z + public Vector3 begin_pos => new Vector3((float?)(double?)Table[$"{nameof(begin_pos)}.x"] ?? 0, default, (float?)(double?)Table[$"{nameof(begin_pos)}.z"] ?? 0); + // ONLY X,Z + public Vector3 size => new Vector3((float?)(double?)Table[$"{nameof(size)}.x"] ?? 0, default, (float?)(double?)Table[$"{nameof(begin_pos)}.z"] ?? 0); + public Vector3 born_pos => new Vector3((float?)(double?)Table[$"{nameof(born_pos)}.x"] ?? 0, (float?)(double?)Table[$"{nameof(born_pos)}.y"] ?? 0, (float?)(double?)Table[$"{nameof(born_pos)}.z"] ?? 0); + public Vector3 born_rot => new Vector3((float?)(double?)Table[$"{nameof(born_rot)}.x"] ?? 0, (float?)(double?)Table[$"{nameof(born_rot)}.y"] ?? 0, (float?)(double?)Table[$"{nameof(born_rot)}.z"] ?? 0); + public float? die_y => (int?)(long?)Table[$"die_y"]; public RoomInfo room_safe_pos; - public Vector2 vision_anchor; // x,z + // ONLY X,Z + public Vector3 vision_anchor => new Vector3((float?)(double?)Table[$"{nameof(vision_anchor)}.x"] ?? 0, default, (float?)(double?)Table[$"{nameof(vision_anchor)}.z"] ?? 0); + + public SceneConfig(LuaTable table) + { + Table = table; + room_safe_pos = new(Table); + } } public class Rectangle { - public Vector2 min; // x,z - public Vector2 max; // x,z + private LuaTable Table; + // ONLY X,Z + public Vector3 min => new Vector3((float?)(double?)Table[$"{nameof(min)}.x"] ?? 0, default, (float?)(double?)Table[$"{nameof(min)}.z"] ?? 0); + // ONLY X,Z + public Vector3 max => new Vector3((float?)(double?)Table[$"{nameof(max)}.x"] ?? 0, default, (float?)(double?)Table[$"{nameof(max)}.z"] ?? 0); + + public Rectangle(LuaTable table) + { + Table = table; + } } public class RoomInfo { - public int scene_id; - public Vector3 safe_pos; - public Vector3 safe_rot; + private LuaTable Table; + public int? scene_id => (int?)(long?)Table[$"{nameof(scene_id)}"] ?? 0; + public Vector3 safe_pos => new Vector3((float?)(double?)Table[$"{nameof(safe_pos)}.x"] ?? 0, (float?)(double?)Table[$"{nameof(safe_pos)}.y"] ?? 0, (float?)(double?)Table[$"{nameof(safe_pos)}.z"] ?? 0); + public Vector3 safe_rot => new Vector3((float?)(double?)Table[$"{nameof(safe_rot)}.x"] ?? 0, (float?)(double?)Table[$"{nameof(safe_rot)}.y"] ?? 0, (float?)(double?)Table[$"{nameof(safe_rot)}.z"] ?? 0); + + public RoomInfo(LuaTable table) + { + Table = table[$"{nameof(SceneConfig.room_safe_pos)}"] as LuaTable; + } } } } diff --git a/src/GameServer/Systems/Script/Scene/SceneScriptManager.cs b/src/GameServer/Systems/Script/Scene/SceneScriptManager.cs new file mode 100644 index 00000000..8a249909 --- /dev/null +++ b/src/GameServer/Systems/Script/Scene/SceneScriptManager.cs @@ -0,0 +1,32 @@ +using NLua; +using Weedwacker.GameServer.Enums; +using Weedwacker.GameServer.Systems.Script; +using Weedwacker.GameServer.Systems.Script.Scene; + +namespace Weedwacker.GameServer.Systems.World +{ + internal class SceneScriptManager + { + private Lua LuaState; + public SceneInfo Info; + public Dictionary Blocks; + public Dictionary Groups; + public ScriptMonsterSpawnService ScriptMonsterSpawnService; + + public static Task CreateAsync(int sceneId, string scriptPath) + { + var scene = new SceneScriptManager(); + scene.LuaState = new(); + return scene.InitializeAsync(sceneId, scriptPath); + } + + private async Task InitializeAsync(int sceneId, string scriptPath) + { + return this; + } + internal Task CallEvent(EventType eventType, ScriptArgs scriptArgs) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/GameServer/Systems/Social/SocialManager.cs b/src/GameServer/Systems/Social/SocialManager.cs index 7ba44760..524cfc6d 100644 --- a/src/GameServer/Systems/Social/SocialManager.cs +++ b/src/GameServer/Systems/Social/SocialManager.cs @@ -1,5 +1,7 @@ using MongoDB.Bson.Serialization.Attributes; using Weedwacker.GameServer.Database; +using Weedwacker.GameServer.Systems.Avatar; +using Weedwacker.Shared.Network.Proto; namespace Weedwacker.GameServer.Systems.Social { @@ -7,12 +9,14 @@ namespace Weedwacker.GameServer.Systems.Social { [BsonId][BsonElement("_id")] public int OwnerId { get; private set; } [BsonIgnore] private Player.Player Owner; + [BsonElement] public HashSet ShowAvatarList = new(); // avatarId + [BsonElement] public bool IsShowAvatar = false; [BsonSerializer(typeof(IntDictionarySerializer))] [BsonElement] public Dictionary Friends { get; private set; } = new(); // GameUid [BsonSerializer(typeof(IntDictionarySerializer))] [BsonElement] public Dictionary PendingRequests { get; private set; } = new(); // GameUid [BsonSerializer(typeof(IntDictionarySerializer))] - [BsonElement] public Dictionary BlackList { get; private set; } = new(); // GameUid why does it use the same proto lol + [BsonElement] public Dictionary BlackList { get; private set; } = new(); // GameUid why does it use the Friendship proto lol [BsonElement] public HashSet EmojiIdList { get; private set; } = new(); public SocialManager(Player.Player owner) @@ -27,5 +31,74 @@ namespace Weedwacker.GameServer.Systems.Social return Task.CompletedTask; } + + public List GetShowAvatarInfoList() + { + List showAvatarInfoList = new(); + + Player.Player player = Owner; + AvatarManager avatars = player.Avatars; + + if (ShowAvatarList != null) + { + foreach (int avatarId in ShowAvatarList) + { + Avatar.Avatar avatar = avatars.GetAvatarById(avatarId); + showAvatarInfoList.Add(avatar.ToShowAvatarInfoProto()); + } + } + return showAvatarInfoList; + } + + public SocialDetail GetSocialDetail(int asker) + { + List socialShowAvatarInfoList = new(); + if (ShowAvatarList != null) + { + foreach (int avatarId in ShowAvatarList) + { + var avatar = Owner.Avatars.GetAvatarById(avatarId); + socialShowAvatarInfoList.Add( + new SocialShowAvatarInfo() + { + AvatarId = (uint)avatarId, + Level = (uint)avatar.Level, + CostumeId = (uint)avatar.Costume + }); + } + } + + + SocialDetail social = new SocialDetail() + { + Uid = (uint)Owner.GameUid, + ProfilePicture = Owner.Profile.HeadImage, + Nickname = Owner.Profile.Nickname, + Signature = Owner.Profile.Signature, + Level = (uint)Owner.PlayerProperties[Enums.PlayerProperty.PROP_PLAYER_LEVEL], + Birthday = Owner.Profile.Birthday, + WorldLevel = (uint)Owner.PlayerProperties[Enums.PlayerProperty.PROP_PLAYER_WORLD_LEVEL], + NameCardId = (uint)Owner.Profile.NameCardId, + IsShowAvatar = IsShowAvatar, + /* TODO + FinishAchievementNum =, + AvatarId = , + IsMpModeAvailable = , + FriendEnterHomeOption =, + IsChatNoDisturb =, + IsFriend =, + OnlineId =, + IsInBlacklist =, + OnlineState =, + Param = , + RemarkName = , + TowerFloorIndex = , + TowerLevelIndex = , + */ + }; + social.ShowAvatarInfoList.AddRange(socialShowAvatarInfoList); + + return social; + } } } diff --git a/src/GameServer/Systems/World/AvatarEntity.cs b/src/GameServer/Systems/World/AvatarEntity.cs index edc0b405..d7b32bdf 100644 --- a/src/GameServer/Systems/World/AvatarEntity.cs +++ b/src/GameServer/Systems/World/AvatarEntity.cs @@ -134,7 +134,7 @@ namespace Weedwacker.GameServer.Systems.World foreach (EquipItem item in Avatar.Equips.Values) { - if ((item.ItemData as MaterialData).itemType == ItemType.ITEM_WEAPON) + if (item.ItemData.itemType == ItemType.ITEM_WEAPON) { avatarInfo.Weapon = (item as WeaponItem).CreateSceneWeaponInfo(); } diff --git a/src/GameServer/Systems/World/Scene.cs b/src/GameServer/Systems/World/Scene.cs index a768db5a..4375b776 100644 --- a/src/GameServer/Systems/World/Scene.cs +++ b/src/GameServer/Systems/World/Scene.cs @@ -1,7 +1,9 @@ -using Vim.Math3d; +using MongoDB.Driver; +using Vim.Math3d; using Weedwacker.GameServer.Data; using Weedwacker.GameServer.Data.BinOut.Scene.Point; using Weedwacker.GameServer.Data.Excel; +using Weedwacker.GameServer.Database; using Weedwacker.GameServer.Enums; using Weedwacker.GameServer.Packet; using Weedwacker.GameServer.Packet.Send; @@ -21,23 +23,29 @@ namespace Weedwacker.GameServer.Systems.World public readonly HashSet SpawnedEntities; public readonly HashSet DeadSpawnedEntities; public int AutoCloseTime; - public int Time { get; private set; } + public uint Time { get; private set; } public SceneScriptManager ScriptManager { get; private set; } public readonly ScenePointData PointData; public readonly DungeonData? DungeonData; public int PrevScene; // Id of the previous scene public int PrevScenePoint; + public Dictionary, int> ActiveAreaWeathers; // weatherId> + public HashSet SceneTags; // TODO apply based on host's data public Scene(World world, SceneData sceneData) { World = world; SceneData = sceneData; - PointData = GameData.ScenePointDataMap["scene" + sceneData.id + "_point.json"]; + PointData = GameData.ScenePointDataMap["scene" + sceneData.id + "_point"]; if (sceneData.type == SceneType.SCENE_DUNGEON) DungeonData = GameData.DungeonDataMap.Where(w => w.Value.sceneId == sceneData.id).First().Value; Time = 8 * 60; PrevScene = 3; - ScriptManager = new SceneScriptManager(this); + + // Always applied tags? + SceneTags = new HashSet(GameData.SceneTagDataMap.Where(w => w.Value.sceneId == GetId() && (w.Value.cond == null || w.Value.cond.Length == 0)).Select(s => (uint)s.Key)); + + //ScriptManager = GameData.SceneScripts[GetId()]; } public int GetId() @@ -51,7 +59,7 @@ namespace Weedwacker.GameServer.Systems.World public void ChangeTime(int time) { - Time = time % 1440; + Time = (uint)time % 1440; } public bool IsInScene(GameEntity entity) @@ -59,7 +67,13 @@ namespace Weedwacker.GameServer.Systems.World return Entities.ContainsKey(entity.Id); } - public async Task AddPlayerAsync(Player.Player player, EnterReason reason, Vector3 newPosition, EnterType type = EnterType.Self, int oldSceneId = default, Vector3 oldPos = default) + public async Task UpdateActiveAreaWeathersAsync(Tuple areaIDs) + { + //TODO update based on host's weather and quest progression + await BroadcastPacketAsync(new PacketSceneAreaWeatherNotify(ClimateType.CLIMATE_SUNNY, 1)); + } + + public async Task AddPlayerAsync(Player.Player player, EnterReason reason, Vector3 newPosition, EnterType type = EnterType.Self, int oldSceneId = default, Vector3 oldPos = default) { // Check if player already in if (Players.Contains(player)) @@ -70,7 +84,14 @@ namespace Weedwacker.GameServer.Systems.World // Add Players.Add(player); await player.SetSceneAsync(this); - await player.SendPacketAsync(new PacketPlayerEnterSceneNotify(player, EnterType.Self, EnterReason.TeamKick, this, player.Position, oldSceneId, oldPos)); + player.Position = newPosition; + + // Update Database + var filter = Builders.Filter.Where(w => w.AccountUid == player.AccountUid); + var update = Builders.Update.Set(w => w.PositionArray, player.PositionArray).Set(w => w.RotationArray, player.RotationArray); + await DatabaseManager.UpdatePlayerAsync(filter, update); + + await player.SendPacketAsync(new PacketPlayerEnterSceneNotify(player, type, reason, this, newPosition, oldSceneId, oldPos)); await SetupPlayerAvatarsAsync(player); } @@ -82,8 +103,8 @@ namespace Weedwacker.GameServer.Systems.World await player.SetSceneAsync(null); // Remove player avatars - SortedSet team = player.TeamManager.ActiveTeam; - await RemoveEntitiesAsync(team, VisionType.Remove); + SortedList team = player.TeamManager.ActiveTeam; + await RemoveEntitiesAsync(team.Values, VisionType.Remove); team.Clear(); // Remove player gadgets @@ -105,18 +126,14 @@ namespace Weedwacker.GameServer.Systems.World TeamInfo teamInfo = player.TeamManager.GetCurrentTeamInfo(); foreach (int avatarId in teamInfo.AvatarInfo.Keys) { + if (avatarId == 0) continue; AvatarEntity entity = new AvatarEntity(player.Scene, player.Avatars.GetAvatarById(avatarId)); - player.TeamManager.ActiveTeam.Add(entity); - } - - // Limit character index in case its out of bounds - if (player.TeamManager.CurrentCharacterIndex >= player.TeamManager.ActiveTeam.Count || player.TeamManager.CurrentCharacterIndex < 0) - { - player.TeamManager.CurrentCharacterIndex = player.TeamManager.CurrentCharacterIndex - 1; + player.TeamManager.ActiveTeam = new SortedList(teamInfo.AvatarInfo.Select( + w => new KeyValuePair(w.Key, new AvatarEntity(player.Scene, w.Value))).ToDictionary(w => w.Key, w => w.Value)); } } - public async Task SpawnPlayer(Player.Player player) + public async Task SpawnPlayerAsync(Player.Player player) { var teamManager = player.TeamManager; if (IsInScene(teamManager.GetCurrentAvatarEntity())) @@ -132,7 +149,7 @@ namespace Weedwacker.GameServer.Systems.World await AddEntityAsync(teamManager.GetCurrentAvatarEntity()); // Notify the client of any extra skill charges - teamManager.ActiveTeam.AsParallel().ForAll(async x => await x.Avatar.GetCurSkillDepot().SendAvatarSkillInfoNotify()); + teamManager.ActiveTeam.AsParallel().ForAll(async x => await x.Value.Avatar.GetCurSkillDepot().SendAvatarSkillInfoNotify()); } public async Task RespawnPlayerAsync(Player.Player player) @@ -140,7 +157,7 @@ namespace Weedwacker.GameServer.Systems.World //player.StaminaManager.stopSustainedStaminaHandler(); // prevent drowning immediately after respawn // Revive all team members - foreach (AvatarEntity entity in player.TeamManager.ActiveTeam) + foreach (AvatarEntity entity in player.TeamManager.ActiveTeam.Values) { entity.FightProps[ FightProperty.FIGHT_PROP_CUR_HP] = @@ -265,12 +282,16 @@ namespace Weedwacker.GameServer.Systems.World if (attacker != null) { // Check codex - if (attacker is ClientGadgetEntity gadgetAttacker) { + if (attacker is ClientGadgetEntity gadgetAttacker) + { var clientGadgetOwner = Entities[gadgetAttacker.OwnerEntityId]; - if (clientGadgetOwner is AvatarEntity) { + if (clientGadgetOwner is AvatarEntity) + { //((ClientGadgetEntity)attacker).Owner.Codex.CheckAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); } - } else if (attacker is AvatarEntity avatarAttacker) { + } + else if (attacker is AvatarEntity avatarAttacker) + { //avatarAttacker.Avatar.Owner.Codex.CheckAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); } } @@ -316,7 +337,8 @@ namespace Weedwacker.GameServer.Systems.World { GameEntity entity = Entities[entityId]; - if (entity == null || !(entity is ClientGadgetEntity)) { + if (entity == null || !(entity is ClientGadgetEntity)) + { return; } diff --git a/src/GameServer/Systems/World/SceneScriptManager.cs b/src/GameServer/Systems/World/SceneScriptManager.cs deleted file mode 100644 index 4a4860fa..00000000 --- a/src/GameServer/Systems/World/SceneScriptManager.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Weedwacker.GameServer.Enums; -using Weedwacker.GameServer.Systems.Script; -using Weedwacker.GameServer.Systems.Script.Scene; - -namespace Weedwacker.GameServer.Systems.World -{ - internal class SceneScriptManager - { - public readonly Scene Scene; - public SceneInfo Info; - public ScriptMonsterSpawnService ScriptMonsterSpawnService; - - public SceneScriptManager(Scene scene) - { - Scene = scene; - //TODO - } - - internal Task CallEvent(EventType eventType, ScriptArgs scriptArgs) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/GameServer/Systems/World/World.cs b/src/GameServer/Systems/World/World.cs index 37b3c8a0..665c13ec 100644 --- a/src/GameServer/Systems/World/World.cs +++ b/src/GameServer/Systems/World/World.cs @@ -23,7 +23,6 @@ namespace Weedwacker.GameServer.Systems.World public World(Player.Player owner) { Host = owner; - Players.Add(owner); LevelEntityId = GetNextEntityId(EntityIdType.MPLEVEL); WorldLevel = owner.Profile.WorldLevel; GameServer.RegisterWorld(this); @@ -109,6 +108,7 @@ namespace Weedwacker.GameServer.Systems.World public async Task RemovePlayerAsync(Player.Player player) { + if (!Players.Contains(player)) return; // Remove team entities await player.SendPacketAsync(new PacketDelTeamEntityNotify(player.SceneId, player.TeamManager.EntityId)); @@ -152,6 +152,7 @@ namespace Weedwacker.GameServer.Systems.World } Scene? oldScene = player.Scene; + int oldSceneId = oldScene == null ? 0 : oldScene.GetId(); Scene newScene = GetSceneById(sceneId); if (oldScene != null) @@ -163,12 +164,12 @@ namespace Weedwacker.GameServer.Systems.World if (useDefaultBornPosition) { - SceneInfo sceneInfo = newScene.ScriptManager.Info; + SceneInfo sceneInfo = GameData.SceneScripts[sceneId]; teleportTo = sceneInfo.scene_config.born_pos; player.Rotation = sceneInfo.scene_config.born_rot; } - await newScene.AddPlayerAsync(player, enterReason, teleportTo, enterType, oldScene.GetId(), player.Position); + await newScene.AddPlayerAsync(player, enterReason, teleportTo, enterType, oldSceneId, player.Position); return true; } @@ -177,18 +178,18 @@ namespace Weedwacker.GameServer.Systems.World { Players.AsParallel().ForAll(async player => { - // Dont send packets if player is logging in and filter out joining player + // Dont send packets if player is logging in and filter out joining player if (!player.HasSentLoginPackets || player == paramPlayer) return; - // Update team of all players since max players has been changed - Probably not the best way to do it + // Update team of all players since max players has been changed - Probably not the best way to do it if (IsMultiplayer) { player.TeamManager.MpTeam.CopyFrom(player.TeamManager.MpTeam, player.TeamManager.GetMaxTeamSize()); await player.TeamManager.UpdateTeamEntities(); } - // Dont send packets if player is loading into the scene + // Dont send packets if player is loading into the scene if (player.SceneLoadState < SceneLoadState.INIT) { await Task.WhenAll(new Task[] @@ -204,12 +205,14 @@ namespace Weedwacker.GameServer.Systems.World }); } }); + + } public async Task BroadcastPacketAsync(BasePacket packet) { // Send to all players - might have to check if player has been sent data packets - Players.AsParallel().ForAll(async p => await p.SendPacketAsync(packet)); + Players.AsParallel().ForAll(async p => await p.SendPacketAsync(packet)); } // Returns false if there are no players in this world