using System.Text.Json; using System.Text.Json.Serialization; using SPTarkov.Server.Core.Models.Eft.Common.Request; using SPTarkov.Server.Core.Models.Eft.Customization; using SPTarkov.Server.Core.Models.Eft.Health; using SPTarkov.Server.Core.Models.Eft.Hideout; using SPTarkov.Server.Core.Models.Eft.Insurance; using SPTarkov.Server.Core.Models.Eft.Inventory; using SPTarkov.Server.Core.Models.Eft.Notes; using SPTarkov.Server.Core.Models.Eft.Quests; using SPTarkov.Server.Core.Models.Eft.Ragfair; using SPTarkov.Server.Core.Models.Eft.Repair; using SPTarkov.Server.Core.Models.Eft.Trade; using SPTarkov.Server.Core.Models.Eft.Wishlist; using SPTarkov.Server.Core.Models.Enums; namespace SPTarkov.Server.Core.Utils.Json.Converters; public class BaseInteractionRequestDataConverter : JsonConverter { private static readonly Dictionary> _modHandlers = []; public override BaseInteractionRequestData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { using var jsonDocument = JsonDocument.ParseValue(ref reader); // Get request as raw JSON var jsonText = jsonDocument.RootElement.GetRawText(); // Get the underlying 'type' of action the client is requesting we do // Handle nullability here in case action's GetString is null var action = jsonDocument.RootElement.GetProperty("Action").GetString() ?? string.Empty; return ConvertToCorrectType(action, jsonDocument.RootElement, jsonText, options); } /// /// Handle the players action from received from client /// /// e.g. "Eat" /// Root json element of client request /// Raw JSON request text /// Json parsing options /// BaseInteractionRequestData private static BaseInteractionRequestData? ConvertToCorrectType( string action, JsonElement jsonDocumentRoot, string jsonText, JsonSerializerOptions options ) { switch (action) { case ItemEventActions.CUSTOMIZATION_BUY: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.CUSTOMIZATION_SET: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.EAT: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.HEAL: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.RESTORE_HEALTH: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_UPGRADE: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_UPGRADE_COMPLETE: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_PUT_ITEMS_IN_AREA_SLOTS: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_TAKE_ITEMS_FROM_AREA_SLOTS: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_TOGGLE_AREA: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_SINGLE_PRODUCTION_START: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_SCAV_CASE_PRODUCTION_START: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_CONTINUOUS_PRODUCTION_START: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_TAKE_PRODUCTION: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_RECORD_SHOOTING_RANGE_POINTS: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_IMPROVE_AREA: case HideoutEventActions.HIDEOUT_CANCEL_PRODUCTION_COMMAND: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_CIRCLE_OF_CULTIST_PRODUCTION_START: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_DELETE_PRODUCTION_COMMAND: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_CUSTOMIZATION_APPLY_COMMAND: return JsonSerializer.Deserialize(jsonText, options); case HideoutEventActions.HIDEOUT_CUSTOMIZATION_SET_MANNEQUIN_POSE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.INSURE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.ADD_TO_WISHLIST: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.REMOVE_FROM_WISHLIST: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.CHANGE_WISHLIST_ITEM_CATEGORY: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.TRADING_CONFIRM: { switch (jsonDocumentRoot.GetProperty("type").GetString()) { case ItemEventActions.BUY_FROM_TRADER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.SELL_TO_TRADER: return JsonSerializer.Deserialize(jsonText, options); default: throw new Exception( $"Unhandled action type: {action}, make sure BaseInteractionRequestDataConverter has deserialization for this action." ); } } case ItemEventActions.RAGFAIR_BUY_OFFER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.SELL_ALL_FROM_SAVAGE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.REPAIR: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.TRADER_REPAIR: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.RAGFAIR_ADD_OFFER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.RAGFAIR_REMOVE_OFFER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.RAGFAIR_RENEW_OFFER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.QUEST_ACCEPT: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.QUEST_COMPLETE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.QUEST_HANDOVER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.REPEATABLE_QUEST_CHANGE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.ADD_NOTE: case ItemEventActions.EDIT_NOTE: case ItemEventActions.DELETE_NOTE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.MOVE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.REMOVE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.SPLIT: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.MERGE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.TRANSFER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.SWAP: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.FOLD: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.TOGGLE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.TAG: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.BIND: case ItemEventActions.UNBIND: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.EXAMINE: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.READ_ENCYCLOPEDIA: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.APPLY_INVENTORY_CHANGES: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.CREATE_MAP_MARKER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.DELETE_MAP_MARKER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.EDIT_MAP_MARKER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.OPEN_RANDOM_LOOT_CONTAINER: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.HIDEOUT_QTE_EVENT: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.REDEEM_PROFILE_REWARD: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.SET_FAVORITE_ITEMS: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.QUEST_FAIL: return JsonSerializer.Deserialize(jsonText, options); case ItemEventActions.PIN_LOCK: return JsonSerializer.Deserialize(jsonText, options); default: if (_modHandlers.TryGetValue(action, out var handler)) { return handler(jsonText); } throw new Exception( $"Unhandled action type {action}, make sure the BaseInteractionRequestDataConverter has the deserialization for this action handled." ); } } public static void RegisterModDataHandler(string action, Func handler) { if (!_modHandlers.TryAdd(action, handler)) { throw new Exception($"Unable to register action {action} to BaseInteractionRequestDataConverter as it already exists."); } } public override void Write(Utf8JsonWriter writer, BaseInteractionRequestData value, JsonSerializerOptions options) { JsonSerializer.Serialize(writer, value, options); } }