OpenStates, fix some null pointers, many Login Packets, basic script engine

This commit is contained in:
akatatsu27 2022-10-20 18:30:01 +03:00
parent 35236e9b40
commit 823ccee8fc
76 changed files with 1481 additions and 192 deletions

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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<int, MonsterCurveData> MonsterCurveDataMap = new(); // level
public readonly static SortedList<int, MonsterData> MonsterDataMap = new(); // id
public readonly static SortedList<int, MonsterDescribeData> MonsterDescribeDataMap = new(); // id
public readonly static SortedList<int, OpenStateData> OpenStateDataMap = new(); // id
public readonly static SortedList<string, BaseConfigTalent[]> RelicAffixConfigDataMap = new(); // openConfig
public readonly static SortedList<int, ShopData> ShopDataMap = new(); // shopId
public readonly static SortedList<int, RewardData> RewardDataMap = new(); // RewardId
public readonly static SortedList<int, ShopGoodsData> ShopGoodsDataMap = new(); // goodsId
public readonly static ConcurrentDictionary<string, BaseConfigTalent[]> TeamResonanceConfigDataMap = new(); // openConfig
public readonly static ConcurrentDictionary<string, BaseConfigTalent[]> WeaponAffixConfigDataMap = new(); // openConfig
public readonly static SortedList<int, WeaponCurveData> WeaponCurveDataMap = new(); // level
public readonly static SortedList<Tuple<int, int>, WeaponPromoteData> WeaponPromoteDataMap = new(); // <weaponPromoteId, promoteLevel>
public readonly static SortedList<int, ReliquaryAffixData> ReliquaryAffixDataMap = new(); // id
public readonly static SortedList<int, ReliquaryMainPropData> ReliquaryMainPropDataMap = new(); // id
public readonly static SortedList<Tuple<int, int>, ReliquaryLevelData> ReliquaryLevelDataMap = new(); // <rank, level>
public readonly static SortedList<int, ReliquarySetData> ReliquarySetDataMap = new(); // setid
public readonly static SortedList<int, SceneData> SceneDataMap = new(); //id
public readonly static SortedList<int, SceneData> SceneDataMap = new(); // id
public readonly static ConcurrentDictionary<string, ScenePointData> ScenePointDataMap = new(); // filename
public readonly static ConcurrentDictionary<int, SceneNpcBornData> SceneNpcBornDataMap = new(); // sceneId
public readonly static SortedList<int, SceneTagData> SceneTagDataMap = new(); // id
public readonly static SortedList<int, TeamResonanceData> TeamResonanceDataMap = new(); // teamResonanceId
public readonly static ConcurrentDictionary<string, BaseConfigTalent[]> WeaponAffixConfigDataMap = new(); // openConfig
public readonly static SortedList<int, WeaponCurveData> WeaponCurveDataMap = new(); // level
public readonly static SortedList<Tuple<int, int>, WeaponPromoteData> WeaponPromoteDataMap = new(); // <weaponPromoteId, promoteLevel>
public readonly static SortedList<int, WeatherData> WeatherDataMap = new(); // areaId
public readonly static SortedList<int, SceneInfo> 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<Obj, Key>(string path, Func<Obj, Key> keySelector, IDictionary<Key, Obj> 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<ItemData, int, WeaponData>(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");
}
}

View File

@ -156,6 +156,23 @@ namespace Weedwacker.GameServer.Database
return player;
}
// Don't create a player when requested by gameUid
public static async Task<Player?> 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<AvatarManager> SaveAvatarsAsync(AvatarManager avatars)
{
await Avatars.ReplaceOneAsync<AvatarManager>(w => w.OwnerId == avatars.OwnerId, avatars, Replace);

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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,
}
}

View File

@ -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<int,Connection> OnlinePlayers = new(); // <gameUid,connection>
private static HashSet<World> Worlds = new();
@ -68,5 +70,23 @@ namespace Weedwacker.GameServer
//TODO
return int.MaxValue;
}
internal static async Task<SocialDetail?> 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;
}
}
}
}

View File

@ -11,7 +11,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MongoDB.Driver" Version="2.17.1" />
<PackageReference Include="MongoDB.Driver" Version="2.18.0" />
<PackageReference Include="NLua" Version="1.4.32" />
<PackageReference Include="Vim.Math3D" Version="1.6.0" />
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}

View File

@ -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;

View File

@ -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));
}
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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));
}
}
}

View File

@ -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;
}
}
}

View File

@ -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));
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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<Tuple<int, int>> 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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -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));

View File

@ -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<Avatar>))]
[BsonElement] public SortedList<int, Avatar> AvatarInfo { get; private set; } = new(); // <index, avatar>> 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

View File

@ -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;
}

View File

@ -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)
{

View File

@ -10,8 +10,7 @@ namespace Weedwacker.GameServer.Systems.Inventory
{
[BsonElement] public int MainPropId { get; protected set; }
[BsonElement] public HashSet<int>? 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()

View File

@ -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<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
if (Items.TryGetValue(itemId, out GameItem? material))
{

View File

@ -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<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
if (Items.TryGetValue(itemId, out GameItem? material))
{

View File

@ -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<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
if (GameData.ItemDataMap[itemId].itemType == ItemType.ITEM_MATERIAL)

View File

@ -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<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
if (Items.TryGetValue(itemId, out GameItem? material))
{

View File

@ -104,7 +104,7 @@ namespace Weedwacker.GameServer.Systems.Inventory
*/
return false;
}
internal override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
MaterialData data = (MaterialData)GameData.ItemDataMap[itemId];
List<BaseItemUse> ops = new();

View File

@ -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<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
if (Items.TryGetValue(itemId, out GameItem? material))
{

View File

@ -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<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
if (Items.TryGetValue(itemId, out GameItem? material))
{

View File

@ -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<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
if (Items.TryGetValue(itemId, out GameItem? material))
{

View File

@ -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<GameItem?> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem?> AddItemAsync(int itemId, int count = 1)
{
if (Items.TryGetValue(itemId, out GameItem? material))
{

View File

@ -34,7 +34,7 @@ namespace Weedwacker.GameServer.Systems.Inventory
}
}
internal override async Task<GameItem> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem> AddItemAsync(int itemId, int count = 1)
{
if (GameData.ItemDataMap[itemId].itemType == ItemType.ITEM_MATERIAL)
{

View File

@ -16,7 +16,7 @@ namespace Weedwacker.GameServer.Systems.Inventory
}
// return null if no update item
internal abstract Task<GameItem?> AddItemAsync(int itemId, int count = 1);
public abstract Task<GameItem?> AddItemAsync(int itemId, int count = 1);
// Delete if count reaches 0
internal abstract Task<bool> RemoveItemAsync(GameItem item, int count = 1);

View File

@ -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<GameItem> AddItemAsync(int itemId, int count = 1)
public override async Task<GameItem> AddItemAsync(int itemId, int count = 1)
{
if (GameData.ItemDataMap[itemId].itemType == ItemType.ITEM_MATERIAL)

View File

@ -10,13 +10,11 @@ namespace Weedwacker.GameServer.Systems.Inventory
[BsonElement] public List<int>? 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;
}

View File

@ -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<int> DEFAULT_OPEN_STATES = (HashSet<int>)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<Player>.Filter.Where(w => w.AccountUid == Owner.AccountUid);
var update = Builders<Player>.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<bool> 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);
}
}
}
}
}

View File

@ -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<int, int> WorldAreaIds { get; private set; } // <areaID1, areaID2>
[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<PlayerProperty, int> PlayerProperties { get; set; } = new(); // SET ONLY THROUGH THE PROPMANAGER
public Dictionary<OpenStateType, int> 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<bool> 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<Player>.Filter.Where(w => w.AccountUid == AccountUid);
var update = Builders<Player>.Update.Set(w => w.Profile.HeroName, heroName).Set(w => w.Profile.Nickname, heroName).Set(w => w.MainCharacterId, avatarId);
var update = Builders<Player>.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<Player>.Filter.Where(w => w.AccountUid == AccountUid);
var update = Builders<Player>.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<Player>.Filter.Where(w => w.AccountUid == AccountUid);
var update = Builders<Player>.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);

View File

@ -9,10 +9,10 @@ namespace Weedwacker.GameServer.Systems.Player
public string Nickname;
public int Level = 1;
public int WorldLevel = 1;
public Tuple<int, int>? Birthday; // <Day,Month>
public Birthday Birthday = new() { Day = 0, Month = 0 }; // <Day,Month>
public ProfilePicture? HeadImage; // <avatarId, skinId>
public string? HeroName;
public string? Signature;
public string? Signature = "";
public int Achievements;
public int DaysSinceLastLogin;
public int LastActiveTime;

View File

@ -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<int, TeamInfo> Teams { get; private set; } = new(); // <index, team>
[BsonSerializer(typeof(IntSortedListSerializer<TeamInfo>))]
[BsonElement] public SortedList<int, TeamInfo> 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<AvatarEntity> 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<int, AvatarEntity> 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<int, TeamInfo>();
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<int, AvatarEntity> 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<long> 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<bool> 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<bool> HealAvatar(Avatar.Avatar avatar, int healRate, int healAmount)
{
foreach (AvatarEntity entity in ActiveTeam)
foreach (AvatarEntity entity in ActiveTeam.Values)
{
if (entity.Avatar == avatar)
{

View File

@ -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<int, int> blocks; // <index, blockId>
public SortedList<int, Rectangle> block_rects; // <index, Rectangle>
public HashSet<string>? dummy_points; // load dummy points from Scene<sceneId>_<string>.lua
public HashSet<string>? routes_config; // load routes from ???
Lua LuaState;
public SceneConfig? scene_config;
public SortedList<int, int>? blocks; // <index ,blockIds>
public SortedList<int, Rectangle>? block_rects = new(); // <index, rectangle>
public LuaTable dummy_points;// => LuaState.GetTable("dummy_points"); // load dummy points from Scene<sceneId>_<string>.lua
public LuaTable routes_config;// => LuaState.GetTable("routes_config"); // load routes from ???
public static Task<SceneInfo> CreateAsync(Lua lua, int sceneId, string scriptPath)
{
var scene = new SceneInfo();
return scene.InitializeAsync(lua, sceneId, scriptPath);
}
private async Task<SceneInfo> 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<int, int>(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<int, Rectangle>(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;
}
}
}
}

View File

@ -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<int, SceneBlock> Blocks;
public Dictionary<int, SceneGroup> Groups;
public ScriptMonsterSpawnService ScriptMonsterSpawnService;
public static Task<SceneScriptManager> CreateAsync(int sceneId, string scriptPath)
{
var scene = new SceneScriptManager();
scene.LuaState = new();
return scene.InitializeAsync(sceneId, scriptPath);
}
private async Task<SceneScriptManager> InitializeAsync(int sceneId, string scriptPath)
{
return this;
}
internal Task CallEvent(EventType eventType, ScriptArgs scriptArgs)
{
throw new NotImplementedException();
}
}
}

View File

@ -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<int> ShowAvatarList = new(); // avatarId
[BsonElement] public bool IsShowAvatar = false;
[BsonSerializer(typeof(IntDictionarySerializer<Friendship>))]
[BsonElement] public Dictionary<int, Friendship> Friends { get; private set; } = new(); // GameUid
[BsonSerializer(typeof(IntDictionarySerializer<Friendship>))]
[BsonElement] public Dictionary<int, Friendship> PendingRequests { get; private set; } = new(); // GameUid
[BsonSerializer(typeof(IntDictionarySerializer<Friendship>))]
[BsonElement] public Dictionary<int, Friendship> BlackList { get; private set; } = new(); // GameUid why does it use the same proto lol
[BsonElement] public Dictionary<int, Friendship> BlackList { get; private set; } = new(); // GameUid why does it use the Friendship proto lol
[BsonElement] public HashSet<int> EmojiIdList { get; private set; } = new();
public SocialManager(Player.Player owner)
@ -27,5 +31,74 @@ namespace Weedwacker.GameServer.Systems.Social
return Task.CompletedTask;
}
public List<ShowAvatarInfo> GetShowAvatarInfoList()
{
List<ShowAvatarInfo> 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<SocialShowAvatarInfo> 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;
}
}
}

View File

@ -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();
}

View File

@ -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<SpawnInfo> SpawnedEntities;
public readonly HashSet<SpawnInfo> 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<Tuple<int, int>, int> ActiveAreaWeathers; // <areaID1, areaID2> weatherId>
public HashSet<uint> 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<uint>(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<int, int> 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<Player.Player>.Filter.Where(w => w.AccountUid == player.AccountUid);
var update = Builders<Player.Player>.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<AvatarEntity> team = player.TeamManager.ActiveTeam;
await RemoveEntitiesAsync(team, VisionType.Remove);
SortedList<int, AvatarEntity> 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<int, AvatarEntity>(teamInfo.AvatarInfo.Select(
w => new KeyValuePair<int, AvatarEntity>(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;
}

View File

@ -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();
}
}
}

View File

@ -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