From 42d9ed78ee686997c63072c41bc8d43b39188713 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 3 Sep 2019 23:44:24 -0400 Subject: [PATCH] Core/DataStores: Implemented WorldStateExpression Port From (https://github.com/TrinityCore/TrinityCore/commit/548d79bb06cfba3517347070dfe606748dda55ab) --- Source/Framework/Constants/CliDBConst.cs | 82 +++++- Source/Framework/Constants/PlayerConst.cs | 4 +- .../Database/Databases/HotfixDatabase.cs | 5 + Source/Game/Chat/Commands/DebugCommands.cs | 91 ++++++- Source/Game/Conditions/ConditionManager.cs | 235 +++++++++++++++++- Source/Game/DataStorage/CliDB.cs | 4 + Source/Game/DataStorage/Structs/W_Records.cs | 6 + .../Game/Network/Packets/CharacterPackets.cs | 6 - Source/Game/Time/GameTime.cs | 9 + 9 files changed, 419 insertions(+), 23 deletions(-) diff --git a/Source/Framework/Constants/CliDBConst.cs b/Source/Framework/Constants/CliDBConst.cs index dd622dac8..36332beff 100644 --- a/Source/Framework/Constants/CliDBConst.cs +++ b/Source/Framework/Constants/CliDBConst.cs @@ -1421,7 +1421,7 @@ namespace Framework.Constants } // PhaseUseFlags fields in different db2s - public enum PhaseUseFlagsValues : byte + public enum PhaseUseFlagsValues : byte { None = 0x0, AlwaysVisible = 0x1, @@ -1819,4 +1819,84 @@ namespace Framework.Constants Micro = 5, Orphan = 6 } + + public enum WorldStateExpressionValueType + { + Constant = 1, + WorldState = 2, + Function = 3 + } + + public enum WorldStateExpressionLogic + { + None = 0, + And = 1, + Or = 2, + Xor = 3, + } + + public enum WorldStateExpressionComparisonType + { + None = 0, + Equal = 1, + NotEqual = 2, + Less = 3, + LessOrEqual = 4, + Greater = 5, + GreaterOrEqual = 6, + } + + public enum WorldStateExpressionOperatorType + { + None = 0, + Sum = 1, + Substraction = 2, + Multiplication = 3, + Division = 4, + Remainder = 5, + } + + public enum WorldStateExpressionFunctions + { + None = 0, + Random, + Month, + Day, + TimeOfDay, + Region, + ClockHour, + OldDifficultyId, + HolidayStart, + HolidayLeft, + HolidayActive, + TimerCurrentTime, + WeekNumber, + Unk13, + Unk14, + DifficultyId, + WarModeActive, + Unk17, + Unk18, + Unk19, + Unk20, + Unk21, + WorldStateExpression, + KeystoneAffix, + Unk24, + Unk25, + Unk26, + Unk27, + KeystoneLevel, + Unk29, + Unk30, + Unk31, + Unk32, + MersenneRandom, + Unk34, + Unk35, + Unk36, + UiWidgetData, + + Max, + } } diff --git a/Source/Framework/Constants/PlayerConst.cs b/Source/Framework/Constants/PlayerConst.cs index 3bfb49b97..de9580318 100644 --- a/Source/Framework/Constants/PlayerConst.cs +++ b/Source/Framework/Constants/PlayerConst.cs @@ -375,8 +375,8 @@ namespace Framework.Constants Unk7 = 0x80, ContestedPVP = 0x100, InPVP = 0x200, - HideHelm = 0x400, - HideCloak = 0x800, + WarModeActive = 0x400, + WarModeDesired = 0x800, PlayedLongTime = 0x1000, PlayedTooLong = 0x2000, IsOutOfBounds = 0x4000, diff --git a/Source/Framework/Database/Databases/HotfixDatabase.cs b/Source/Framework/Database/Databases/HotfixDatabase.cs index b42bf650c..68fec74cc 100644 --- a/Source/Framework/Database/Databases/HotfixDatabase.cs +++ b/Source/Framework/Database/Databases/HotfixDatabase.cs @@ -1080,6 +1080,9 @@ namespace Framework.Database // WorldSafeLocs.db2 PrepareStatement(HotfixStatements.SEL_WORLD_SAFE_LOCS, "SELECT ID, AreaName, LocX, LocY, LocZ, MapID, Facing FROM world_safe_locs ORDER BY ID DESC"); PrepareStatement(HotfixStatements.SEL_WORLD_SAFE_LOCS_LOCALE, "SELECT ID, AreaName_lang FROM world_safe_locs_locale WHERE locale = ?"); + + // WorldStateExpression.db2 + PrepareStatement(HotfixStatements.SEL_WORLD_STATE_EXPRESSION, "SELECT ID, Expression FROM world_state_expression ORDER BY ID DESC"); } } @@ -1628,6 +1631,8 @@ namespace Framework.Database SEL_WORLD_SAFE_LOCS, SEL_WORLD_SAFE_LOCS_LOCALE, + SEL_WORLD_STATE_EXPRESSION, + MAX_HOTFIXDATABASE_STATEMENTS } } diff --git a/Source/Game/Chat/Commands/DebugCommands.cs b/Source/Game/Chat/Commands/DebugCommands.cs index 66493eb35..093b1a98d 100644 --- a/Source/Game/Chat/Commands/DebugCommands.cs +++ b/Source/Game/Chat/Commands/DebugCommands.cs @@ -640,7 +640,7 @@ namespace Game.Chat [Command("raidreset", RBACPermissions.CommandInstanceUnbind)] static bool HandleDebugRaidResetCommand(StringArguments args, CommandHandler handler) { - string map_str = args.NextString(); + string map_str = args.NextString(); string difficulty_str = args.NextString(); if (!int.TryParse(map_str, out int map) || map <= 0) @@ -809,6 +809,82 @@ namespace Game.Chat return true; } + [CommandNonGroup("wpgps", RBACPermissions.CommandWpgps)] + static bool HandleWPGPSCommand(StringArguments args, CommandHandler handler) + { + Player player = handler.GetSession().GetPlayer(); + + Log.outInfo(LogFilter.SqlDev, "(@PATH, XX, {0}, {1}, {2}, 0, 0, 0, 100, 0),", player.GetPositionX(), player.GetPositionY(), player.GetPositionZ()); + + handler.SendSysMessage("Waypoint SQL written to SQL Developer log"); + return true; + } + + [Command("worldstate", RBACPermissions.CommandDebug)] + static bool HandleDebugWorldStateCommand(StringArguments args, CommandHandler handler) + { + if (args.Empty()) + return false; + + string worldStateIdStr = args.NextString(); + string valueStr = args.NextString(); + + if (worldStateIdStr.IsEmpty()) + return false; + + Player target = handler.getSelectedPlayerOrSelf(); + if (target == null) + { + handler.SendSysMessage(CypherStrings.PlayerNotFound); + return false; + } + + uint worldStateId = uint.Parse(worldStateIdStr); + uint value = valueStr.IsEmpty() ? 0 : uint.Parse(valueStr); + + if (value != 0) + { + Global.WorldMgr.setWorldState(worldStateId, value); + target.SendUpdateWorldState(worldStateId, value); + } + else + handler.SendSysMessage($"Worldstate {worldStateId} actual value : {Global.WorldMgr.getWorldState(worldStateId)}"); + + return true; + } + + [Command("wsexpression", RBACPermissions.CommandDebug)] + static bool HandleDebugWSExpressionCommand(StringArguments args, CommandHandler handler) + { + if (args.Empty()) + return false; + + string expressionIdStr = args.NextString(); + + if (expressionIdStr.IsEmpty()) + return false; + + uint expressionId = uint.Parse(expressionIdStr); + + Player target = handler.getSelectedPlayerOrSelf(); + if (target == null) + { + handler.SendSysMessage(CypherStrings.PlayerNotFound); + return false; + } + + WorldStateExpressionRecord wsExpressionEntry = CliDB.WorldStateExpressionStorage.LookupByKey(expressionId); + if (wsExpressionEntry == null) + return false; + + if (ConditionManager.IsPlayerMeetingExpression(target, wsExpressionEntry)) + handler.SendSysMessage($"Expression {expressionId} meet"); + else + handler.SendSysMessage($"Expression {expressionId} not meet"); + + return true; + } + [CommandGroup("send", RBACPermissions.CommandDebugSend)] class SendCommands { @@ -948,7 +1024,7 @@ namespace Game.Chat if (args.Empty()) return false; - if (!byte.TryParse(args.NextString(), out byte failNum) ||failNum == 0) + if (!byte.TryParse(args.NextString(), out byte failNum) || failNum == 0) return false; int failArg1 = args.NextInt32(); @@ -1066,16 +1142,5 @@ namespace Game.Chat return true; } } - - [CommandNonGroup("wpgps", RBACPermissions.CommandWpgps)] - static bool HandleWPGPSCommand(StringArguments args, CommandHandler handler) - { - Player player = handler.GetSession().GetPlayer(); - - Log.outInfo(LogFilter.SqlDev, "(@PATH, XX, {0}, {1}, {2}, 0, 0, 0, 100, 0),", player.GetPositionX(), player.GetPositionY(), player.GetPositionZ()); - - handler.SendSysMessage("Waypoint SQL written to SQL Developer log"); - return true; - } } } diff --git a/Source/Game/Conditions/ConditionManager.cs b/Source/Game/Conditions/ConditionManager.cs index ae6d8bd46..0423e1fb9 100644 --- a/Source/Game/Conditions/ConditionManager.cs +++ b/Source/Game/Conditions/ConditionManager.cs @@ -26,6 +26,7 @@ using Game.Spells; using System; using System.Collections.Generic; using System.Linq; +using Framework.IO; namespace Game { @@ -1946,7 +1947,17 @@ namespace Game } // TODO: time condition - // TODO (or not): world state expression condition + + if (condition.WorldStateExpressionID != 0) + { + var worldStateExpression = CliDB.WorldStateExpressionStorage.LookupByKey(condition.WorldStateExpressionID); + if (worldStateExpression == null) + return false; + + if (!IsPlayerMeetingExpression(player, worldStateExpression)) + return false; + } + // TODO: weather condition if (condition.Achievement[0] != 0) @@ -2034,6 +2045,228 @@ namespace Game return true; } + public static bool IsPlayerMeetingExpression(Player player, WorldStateExpressionRecord expression) + { + ByteBuffer buffer = new ByteBuffer(expression.Expression.ToByteArray()); + if (buffer.GetSize() == 0) + return false; + + bool enabled = buffer.ReadBool(); + if (!enabled) + return false; + + bool finalResult = EvalRelOp(buffer, player); + WorldStateExpressionLogic resultLogic = (WorldStateExpressionLogic)buffer.ReadUInt8(); + + while (resultLogic != WorldStateExpressionLogic.None) + { + bool secondResult = EvalRelOp(buffer, player); + + switch (resultLogic) + { + case WorldStateExpressionLogic.And: + finalResult = finalResult && secondResult; + break; + case WorldStateExpressionLogic.Or: + finalResult = finalResult || secondResult; + break; + case WorldStateExpressionLogic.Xor: + finalResult = finalResult != secondResult; + break; + default: + break; + } + + if (buffer.GetCurrentStream().Position < buffer.GetSize()) + break; + + resultLogic = (WorldStateExpressionLogic)buffer.ReadUInt8(); + } + + return finalResult; + } + + static int EvalSingleValue(ByteBuffer buffer, Player player) + { + WorldStateExpressionValueType valueType = (WorldStateExpressionValueType)buffer.ReadUInt8(); + int value = 0; + + switch (valueType) + { + case WorldStateExpressionValueType.Constant: + { + value = buffer.ReadInt32(); + break; + } + case WorldStateExpressionValueType.WorldState: + { + uint worldStateId = buffer.ReadUInt32(); + value = (int)Global.WorldMgr.getWorldState(worldStateId); + break; + } + case WorldStateExpressionValueType.Function: + { + var functionType = (WorldStateExpressionFunctions)buffer.ReadUInt32(); + int arg1 = EvalSingleValue(buffer, player); + int arg2 = EvalSingleValue(buffer, player); + + if (functionType >= WorldStateExpressionFunctions.Max) + return 0; + + value = WorldStateExpressionFunction(functionType, player, arg1, arg2); + break; + } + default: + break; + } + + return value; + } + + static int WorldStateExpressionFunction(WorldStateExpressionFunctions functionType, Player player, int arg1, int arg2) + { + switch (functionType) + { + case WorldStateExpressionFunctions.Random: + return (int)RandomHelper.URand(Math.Min(arg1, arg2), Math.Max(arg1, arg2)); + case WorldStateExpressionFunctions.Month: + return GameTime.GetDateAndTime().Month + 1; + case WorldStateExpressionFunctions.Day: + return GameTime.GetDateAndTime().Day + 1; + case WorldStateExpressionFunctions.TimeOfDay: + DateTime localTime = GameTime.GetDateAndTime(); + return localTime.Hour * Time.Minute + localTime.Minute; + case WorldStateExpressionFunctions.Region: + return Global.WorldMgr.GetRealmId().Region; + case WorldStateExpressionFunctions.ClockHour: + int currentHour = GameTime.GetDateAndTime().Hour + 1; + return currentHour <= 12 ? (currentHour != 0 ? currentHour : 12) : currentHour - 12; + case WorldStateExpressionFunctions.OldDifficultyId: + var difficulty = CliDB.DifficultyStorage.LookupByKey(player.GetMap().GetDifficultyID()); + if (difficulty != null) + return difficulty.OldEnumValue; + + return -1; + case WorldStateExpressionFunctions.HolidayActive: + return Global.GameEventMgr.IsHolidayActive((HolidayIds)arg1) ? 1 : 0; + case WorldStateExpressionFunctions.TimerCurrentTime: + return (int)GameTime.GetGameTime(); + case WorldStateExpressionFunctions.WeekNumber: + long now = GameTime.GetGameTime(); + uint raidOrigin = 1135695600; + Cfg_RegionsRecord region = CliDB.CfgRegionsStorage.LookupByKey(Global.WorldMgr.GetRealmId().Region); + if (region != null) + raidOrigin = region.Raidorigin; + + return (int)(now - raidOrigin) / Time.Week; + case WorldStateExpressionFunctions.DifficultyId: + return (int)player.GetMap().GetDifficultyID(); + case WorldStateExpressionFunctions.WarModeActive: + return player.HasPlayerFlag(PlayerFlags.WarModeActive) ? 1 : 0; + case WorldStateExpressionFunctions.WorldStateExpression: + var worldStateExpression = CliDB.WorldStateExpressionStorage.LookupByKey(arg1); + if (worldStateExpression != null) + return IsPlayerMeetingExpression(player, worldStateExpression) ? 1 : 0; + + return 0; + case WorldStateExpressionFunctions.MersenneRandom: + if (arg1 == 1) + return 1; + + //todo fix me + // init with predetermined seed + //std::mt19937 mt(arg2? arg2 : 1); + //value = mt() % arg1 + 1; + return 0; + case WorldStateExpressionFunctions.None: + case WorldStateExpressionFunctions.HolidayStart: + case WorldStateExpressionFunctions.HolidayLeft: + case WorldStateExpressionFunctions.Unk13: + case WorldStateExpressionFunctions.Unk14: + case WorldStateExpressionFunctions.Unk17: + case WorldStateExpressionFunctions.Unk18: + case WorldStateExpressionFunctions.Unk19: + case WorldStateExpressionFunctions.Unk20: + case WorldStateExpressionFunctions.Unk21: + case WorldStateExpressionFunctions.KeystoneAffix: + case WorldStateExpressionFunctions.Unk24: + case WorldStateExpressionFunctions.Unk25: + case WorldStateExpressionFunctions.Unk26: + case WorldStateExpressionFunctions.Unk27: + case WorldStateExpressionFunctions.KeystoneLevel: + case WorldStateExpressionFunctions.Unk29: + case WorldStateExpressionFunctions.Unk30: + case WorldStateExpressionFunctions.Unk31: + case WorldStateExpressionFunctions.Unk32: + case WorldStateExpressionFunctions.Unk34: + case WorldStateExpressionFunctions.Unk35: + case WorldStateExpressionFunctions.Unk36: + case WorldStateExpressionFunctions.UiWidgetData: + default: + return 0; + } + } + + static int EvalValue(ByteBuffer buffer, Player player) + { + int leftValue = EvalSingleValue(buffer, player); + + WorldStateExpressionOperatorType operatorType = (WorldStateExpressionOperatorType)buffer.ReadUInt8(); + if (operatorType == WorldStateExpressionOperatorType.None) + return leftValue; + + int rightValue = EvalSingleValue(buffer, player); + + switch (operatorType) + { + case WorldStateExpressionOperatorType.Sum: + return leftValue + rightValue; + case WorldStateExpressionOperatorType.Substraction: + return leftValue - rightValue; + case WorldStateExpressionOperatorType.Multiplication: + return leftValue * rightValue; + case WorldStateExpressionOperatorType.Division: + return rightValue == 0 ? 0 : leftValue / rightValue; + case WorldStateExpressionOperatorType.Remainder: + return rightValue == 0 ? 0 : leftValue % rightValue; + default: + break; + } + + return leftValue; + } + + static bool EvalRelOp(ByteBuffer buffer, Player player) + { + int leftValue = EvalValue(buffer, player); + + WorldStateExpressionComparisonType compareLogic = (WorldStateExpressionComparisonType)buffer.ReadUInt8(); + if (compareLogic == WorldStateExpressionComparisonType.None) + return leftValue != 0; + + int rightValue = EvalValue(buffer, player); + + switch (compareLogic) + { + case WorldStateExpressionComparisonType.Equal: + return leftValue == rightValue; + case WorldStateExpressionComparisonType.NotEqual: + return leftValue != rightValue; + case WorldStateExpressionComparisonType.Less: + return leftValue < rightValue; + case WorldStateExpressionComparisonType.LessOrEqual: + return leftValue <= rightValue; + case WorldStateExpressionComparisonType.Greater: + return leftValue > rightValue; + case WorldStateExpressionComparisonType.GreaterOrEqual: + return leftValue >= rightValue; + default: + break; + } + + return false; + } + Dictionary> ConditionStore = new Dictionary>(); MultiMap ConditionReferenceStore = new MultiMap(); Dictionary> VehicleSpellConditionStore = new Dictionary>(); diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index bc14b12fe..3239b54b5 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -63,6 +63,7 @@ namespace Game.DataStorage BattlePetSpeciesStateStorage = DBReader.Read("BattlePetSpeciesState.db2", HotfixStatements.SEL_BATTLE_PET_SPECIES_STATE); BattlemasterListStorage = DBReader.Read("BattlemasterList.db2", HotfixStatements.SEL_BATTLEMASTER_LIST, HotfixStatements.SEL_BATTLEMASTER_LIST_LOCALE); BroadcastTextStorage = DBReader.Read("BroadcastText.db2", HotfixStatements.SEL_BROADCAST_TEXT, HotfixStatements.SEL_BROADCAST_TEXT_LOCALE); + CfgRegionsStorage = DBReader.Read("Cfg_Regions.db2", HotfixStatements.SEL_CFG_REGIONS); CharacterFacialHairStylesStorage = DBReader.Read("CharacterFacialHairStyles.db2", HotfixStatements.SEL_CHARACTER_FACIAL_HAIR_STYLES); CharBaseSectionStorage = DBReader.Read("CharBaseSection.db2", HotfixStatements.SEL_CHAR_BASE_SECTION); CharSectionsStorage = DBReader.Read("CharSections.db2", HotfixStatements.SEL_CHAR_SECTIONS); @@ -273,6 +274,7 @@ namespace Game.DataStorage WorldEffectStorage = DBReader.Read("WorldEffect.db2", HotfixStatements.SEL_WORLD_EFFECT); WorldMapOverlayStorage = DBReader.Read("WorldMapOverlay.db2", HotfixStatements.SEL_WORLD_MAP_OVERLAY); WorldSafeLocsStorage = DBReader.Read("WorldSafeLocs.db2", HotfixStatements.SEL_WORLD_SAFE_LOCS, HotfixStatements.SEL_WORLD_SAFE_LOCS_LOCALE); + WorldStateExpressionStorage = DBReader.Read("WorldStateExpression.db2", HotfixStatements.SEL_WORLD_STATE_EXPRESSION); Global.DB2Mgr.LoadStores(); @@ -406,6 +408,7 @@ namespace Game.DataStorage public static DB6Storage BattlePetSpeciesStateStorage; public static DB6Storage BattlemasterListStorage; public static DB6Storage BroadcastTextStorage; + public static DB6Storage CfgRegionsStorage; public static DB6Storage CharacterFacialHairStylesStorage; public static DB6Storage CharBaseSectionStorage; public static DB6Storage CharSectionsStorage; @@ -616,6 +619,7 @@ namespace Game.DataStorage public static DB6Storage WorldEffectStorage; public static DB6Storage WorldMapOverlayStorage; public static DB6Storage WorldSafeLocsStorage; + public static DB6Storage WorldStateExpressionStorage; #endregion #region GameTables diff --git a/Source/Game/DataStorage/Structs/W_Records.cs b/Source/Game/DataStorage/Structs/W_Records.cs index 3967026e7..bc3231e74 100644 --- a/Source/Game/DataStorage/Structs/W_Records.cs +++ b/Source/Game/DataStorage/Structs/W_Records.cs @@ -75,4 +75,10 @@ namespace Game.DataStorage public ushort MapID; public float Facing; } + + public sealed class WorldStateExpressionRecord + { + public uint Id; + public string Expression; + } } diff --git a/Source/Game/Network/Packets/CharacterPackets.cs b/Source/Game/Network/Packets/CharacterPackets.cs index 3d4536109..517f4cadb 100644 --- a/Source/Game/Network/Packets/CharacterPackets.cs +++ b/Source/Game/Network/Packets/CharacterPackets.cs @@ -121,12 +121,6 @@ namespace Game.Network.Packets if (atLoginFlags.HasAnyFlag(AtLoginFlags.Resurrect)) playerFlags &= ~PlayerFlags.Ghost; - if (playerFlags.HasAnyFlag(PlayerFlags.HideHelm)) - Flags |= CharacterFlags.HideHelm; - - if (playerFlags.HasAnyFlag(PlayerFlags.HideCloak)) - Flags |= CharacterFlags.HideCloak; - if (playerFlags.HasAnyFlag(PlayerFlags.Ghost)) Flags |= CharacterFlags.Ghost; diff --git a/Source/Game/Time/GameTime.cs b/Source/Game/Time/GameTime.cs index 4653e3df7..fd9715718 100644 --- a/Source/Game/Time/GameTime.cs +++ b/Source/Game/Time/GameTime.cs @@ -12,6 +12,8 @@ public class GameTime static DateTime _gameTimeSystemPoint = DateTime.MinValue; static DateTime _gameTimeSteadyPoint = DateTime.MinValue; + static DateTime _dateTime; + public static long GetStartTime() { return StartTime; @@ -42,11 +44,18 @@ public class GameTime return (uint)(_gameTime - StartTime); } + public static DateTime GetDateAndTime() + { + return _dateTime; + } + public static void UpdateGameTimers() { _gameTime = Time.UnixTime; _gameMSTime = Time.GetMSTime(); _gameTimeSystemPoint = DateTime.Now; _gameTimeSteadyPoint = DateTime.Now; + + _dateTime = Time.UnixTimeToDateTime(_gameTime); } }