Selaa lähdekoodia

Config功能模块

Leo 2 kuukautta sitten
vanhempi
sitoutus
ebd356630a

BIN
Content/Blueprints/BP_InJoyGameInstance.uasset


BIN
Content/Blueprints/BP_InJoyGameMode.uasset


+ 2 - 0
Source/InJoyLab/InJoyLab.Build.cs

@@ -26,6 +26,8 @@ public class InJoyLab : ModuleRules
 			new string[]
 			{
 				"Core",
+                "Json",
+				"JsonUtilities"
 				// ... add other public dependencies that you statically link with here ...
 			}
 			);

+ 145 - 0
Source/InJoyLab/Private/Config/CSVConfigParser.cpp

@@ -0,0 +1,145 @@
+#include "Config/CSVConfigParser.h"
+#include "Misc/FileHelper.h"
+#include "Misc/Paths.h"
+
+CSVConfigParser::CSVConfigParser()
+{
+    ParsedData.Empty();
+}
+
+bool CSVConfigParser::LoadConfig(const FString& FilePath)
+{
+    ParsedData.Empty();
+
+    // 读取 CSV 文件内容
+    FString FileContent;
+    if (!FFileHelper::LoadFileToString(FileContent, *FilePath))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to load CSV file: %s"), *FilePath);
+        return false;
+    }
+
+    // 逐行解析 CSV
+    TArray<FString> Lines;
+    FileContent.ParseIntoArrayLines(Lines);
+
+    for (const FString& Line : Lines)
+    {
+        TArray<FString> Cells;
+        Line.ParseIntoArray(Cells, TEXT(",")); // 以逗号为分隔符解析单元格
+        ParsedData.Add(Cells);
+    }
+
+    return true;
+}
+
+TArray<FString> CSVConfigParser::GetRow(int32 RowIndex) const
+{
+    if (ParsedData.IsValidIndex(RowIndex))
+    {
+        return ParsedData[RowIndex];
+    }
+    return TArray<FString>();
+}
+
+TArray<FString> CSVConfigParser::GetColumn(int32 ColumnIndex) const
+{
+    TArray<FString> Column;
+    for (const TArray<FString>& Row : ParsedData)
+    {
+        if (Row.IsValidIndex(ColumnIndex))
+        {
+            Column.Add(Row[ColumnIndex]);
+        }
+    }
+    return Column;
+}
+
+FString CSVConfigParser::GetCell(int32 RowIndex, int32 ColumnIndex, const FString& DefaultValue) const
+{
+    if (ParsedData.IsValidIndex(RowIndex) && ParsedData[RowIndex].IsValidIndex(ColumnIndex))
+    {
+        return ParsedData[RowIndex][ColumnIndex];
+    }
+    return DefaultValue;
+}
+
+FString CSVConfigParser::GetString(const FString& Key, const FString& DefaultValue) const
+{
+    // Key 格式为 "RowIndex.ColumnIndex"
+    FString RowIndexString, ColumnIndexString;
+    if (Key.Split(TEXT("."), &RowIndexString, &ColumnIndexString))
+    {
+        // 转换行和列的索引
+        int32 RowIndex = FCString::Atoi(*RowIndexString);
+        int32 ColumnIndex = FCString::Atoi(*ColumnIndexString);
+
+        // 使用 GetCell 获取单元格的值
+        return GetCell(RowIndex, ColumnIndex, DefaultValue);
+    }
+
+    // 如果 Key 格式错误,返回默认值
+    return DefaultValue;
+}
+
+    int32 CSVConfigParser::GetInt(const FString & Key, int32 DefaultValue) const
+    {
+        // 获取字符串值并转换为整数
+        FString StringValue = GetString(Key, TEXT(""));
+        if (!StringValue.IsEmpty())
+        {
+            return FCString::Atoi(*StringValue); // 转换为整数
+        }
+        return DefaultValue;
+    }
+
+    float CSVConfigParser::GetFloat(const FString & Key, float DefaultValue) const
+    {
+        // 获取字符串值并转换为浮点数
+        FString StringValue = GetString(Key, TEXT(""));
+        if (!StringValue.IsEmpty())
+        {
+            return FCString::Atof(*StringValue); // 转换为浮点数
+        }
+        return DefaultValue;
+    }
+
+    bool CSVConfigParser::GetBool(const FString & Key, bool DefaultValue) const
+    {
+        // 获取字符串值并转换为布尔值
+        FString StringValue = GetString(Key, TEXT(""));
+        if (!StringValue.IsEmpty())
+        {
+            return StringValue.Equals(TEXT("true"), ESearchCase::IgnoreCase) || StringValue.Equals(TEXT("1"));
+        }
+        return DefaultValue;
+    }
+
+    TArray<FString> CSVConfigParser::GetArray(const FString & Key) const
+    {
+        // 假设 Key 是行号
+        return GetRow(FCString::Atoi(*Key));
+    }
+
+    TSharedPtr<IConfigParser> CSVConfigParser::GetObject(const FString & Key) const
+    {
+        // 假设 Key 是列号
+        TArray<FString> Column = GetColumn(FCString::Atoi(*Key));
+        if (Column.Num() > 0)
+        {
+            TSharedPtr<CSVConfigParser> SubParser = MakeShared<CSVConfigParser>();
+            SubParser->ParsedData.Add(Column);
+            return SubParser;
+        }
+
+        return nullptr;
+    }
+
+    void CSVConfigParser::PrintConfig() const
+    {
+        for (const TArray<FString>& Row : ParsedData)
+        {
+            FString RowString = FString::Join(Row, TEXT(", "));
+            UE_LOG(LogTemp, Log, TEXT("%s"), *RowString);
+        }
+    }

+ 122 - 0
Source/InJoyLab/Private/Config/IniConfigParser.cpp

@@ -0,0 +1,122 @@
+#include "Config/IniConfigParser.h"
+#include "Misc/FileHelper.h"
+#include "Misc/Paths.h"
+
+IniConfigParser::IniConfigParser()
+{
+    ParsedData.Empty();
+}
+
+bool IniConfigParser::LoadConfig(const FString& FilePath)
+{
+    ParsedData.Empty();
+
+    // 读取 INI 文件内容
+    FString FileContent;
+    if (!FFileHelper::LoadFileToString(FileContent, *FilePath))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to load INI file: %s"), *FilePath);
+        return false;
+    }
+
+    // 将每一行解析为键值对
+    TArray<FString> Lines;
+    FileContent.ParseIntoArrayLines(Lines);
+
+    for (const FString& Line : Lines)
+    {
+        if (Line.StartsWith(TEXT(";")) || Line.TrimStart().IsEmpty())
+        {
+            // 跳过注释或空行
+            continue;
+        }
+
+        FString Key, Value;
+        if (Line.Split(TEXT("="), &Key, &Value))
+        {
+            ParsedData.Add(Key.TrimStartAndEnd(), Value.TrimStartAndEnd());
+        }
+    }
+
+    return true;
+}
+
+FString IniConfigParser::GetString(const FString& Key, const FString& DefaultValue) const
+{
+    const FString* FoundValue = ParsedData.Find(Key);
+    return FoundValue ? *FoundValue : DefaultValue;
+}
+
+int32 IniConfigParser::GetInt(const FString& Key, int32 DefaultValue) const
+{
+    const FString* FoundValue = ParsedData.Find(Key);
+    if (FoundValue)
+    {
+        return FCString::Atoi(**FoundValue);
+    }
+    return DefaultValue;
+}
+
+float IniConfigParser::GetFloat(const FString& Key, float DefaultValue) const
+{
+    const FString* FoundValue = ParsedData.Find(Key);
+    if (FoundValue)
+    {
+        return FCString::Atof(**FoundValue);
+    }
+    return DefaultValue;
+}
+
+bool IniConfigParser::GetBool(const FString& Key, bool DefaultValue) const
+{
+    const FString* FoundValue = ParsedData.Find(Key);
+    if (FoundValue)
+    {
+        return (*FoundValue == TEXT("true") || *FoundValue == TEXT("1"));
+    }
+    return DefaultValue;
+}
+
+TArray<FString> IniConfigParser::GetArray(const FString& Key) const
+{
+    const FString* FoundValue = ParsedData.Find(Key);
+    if (FoundValue)
+    {
+        TArray<FString> Result;
+        FoundValue->ParseIntoArray(Result, TEXT(","), true); // 以逗号分隔
+        return Result;
+    }
+    return {};
+}
+
+TSharedPtr<IConfigParser> IniConfigParser::GetObject(const FString& Key) const
+{
+    // INI 文件通常不支持嵌套对象,但可以模拟支持 "Key.SubKey" 的层级结构
+    TMap<FString, FString> SubObjectData;
+
+    for (const auto& Pair : ParsedData)
+    {
+        if (Pair.Key.StartsWith(Key + TEXT(".")))
+        {
+            FString SubKey = Pair.Key.RightChop(Key.Len() + 1); // 去掉 "Key." 部分
+            SubObjectData.Add(SubKey, Pair.Value);
+        }
+    }
+
+    if (SubObjectData.Num() > 0)
+    {
+        TSharedPtr<IniConfigParser> SubParser = MakeShared<IniConfigParser>();
+        SubParser->ParsedData = SubObjectData;
+        return SubParser;
+    }
+
+    return nullptr;
+}
+
+void IniConfigParser::PrintConfig() const
+{
+    for (const auto& Pair : ParsedData)
+    {
+        UE_LOG(LogTemp, Log, TEXT("%s = %s"), *Pair.Key, *Pair.Value);
+    }
+}

+ 251 - 0
Source/InJoyLab/Private/Config/JsonConfigParser.cpp

@@ -0,0 +1,251 @@
+#include "Config/JsonConfigParser.h"
+#include "Serialization/JsonReader.h"
+#include "Serialization/JsonSerializer.h"
+#include "JsonObjectConverter.h"
+#include "Misc/FileHelper.h"
+#include "Misc/Paths.h"
+
+bool JsonConfigParser::LoadConfig(const FString& FilePath)
+{
+    FString FileContent;
+    if (!FFileHelper::LoadFileToString(FileContent, *FilePath))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to load JSON file: %s"), *FilePath);
+        return false;
+    }
+
+    TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(FileContent);
+    if (!FJsonSerializer::Deserialize(Reader, ParsedData) || !ParsedData.IsValid())
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to parse JSON file: %s"), *FilePath);
+        return false;
+    }
+
+    return true;
+}
+
+FString JsonConfigParser::GetString(const FString& Key, const FString& DefaultValue) const
+{
+    TSharedPtr<FJsonValue> Value = FindJsonValue(Key);
+    if (Value.IsValid() && Value->Type == EJson::String)
+    {
+        return Value->AsString();
+    }
+    return DefaultValue;
+}
+
+int32 JsonConfigParser::GetInt(const FString& Key, int32 DefaultValue) const
+{
+    TSharedPtr<FJsonValue> Value = FindJsonValue(Key);
+    if (Value.IsValid() && Value->Type == EJson::Number)
+    {
+        return static_cast<int32>(Value->AsNumber());
+    }
+    return DefaultValue;
+}
+
+float JsonConfigParser::GetFloat(const FString& Key, float DefaultValue) const
+{
+    TSharedPtr<FJsonValue> Value = FindJsonValue(Key);
+    if (Value.IsValid() && Value->Type == EJson::Number)
+    {
+        return Value->AsNumber();
+    }
+    return DefaultValue;
+}
+
+bool JsonConfigParser::GetBool(const FString& Key, bool DefaultValue) const
+{
+    TSharedPtr<FJsonValue> Value = FindJsonValue(Key);
+    if (Value.IsValid() && Value->Type == EJson::Boolean)
+    {
+        return Value->AsBool();
+    }
+    return DefaultValue;
+}
+
+TArray<FString> JsonConfigParser::GetArray(const FString& Key) const
+{
+    TArray<FString> Result;
+    TSharedPtr<FJsonValue> Value = FindJsonValue(Key);
+    if (Value.IsValid() && Value->Type == EJson::Array)
+    {
+        for (const auto& ArrayValue : Value->AsArray())
+        {
+            Result.Add(ArrayValue->AsString());
+        }
+    }
+    return Result;
+}
+
+TSharedPtr<IConfigParser> JsonConfigParser::GetObject(const FString& Key) const
+{
+    TSharedPtr<FJsonValue> Value = FindJsonValue(Key);
+    if (Value.IsValid() && Value->Type == EJson::Object)
+    {
+        TSharedPtr<FJsonObject> SubObject = Value->AsObject();
+        TSharedPtr<JsonConfigParser> SubParser = MakeShareable(new JsonConfigParser());
+        SubParser->ParsedData = SubObject;
+        return SubParser;
+    }
+    return nullptr;
+}
+
+void JsonConfigParser::PrintConfig() const
+{
+    if (ParsedData.IsValid())
+    {
+        FString Output;
+        TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Output);
+        FJsonSerializer::Serialize(ParsedData.ToSharedRef(), Writer);
+        UE_LOG(LogTemp, Log, TEXT("JSON Config: %s"), *Output);
+    }
+}
+
+TSharedPtr<FJsonValue> JsonConfigParser::FindJsonValue(const FString& Key) const
+{
+    if (!ParsedData.IsValid())
+    {
+        return nullptr;
+    }
+
+    // 处理层次化键,支持 "Key.SubKey" 形式
+    TArray<FString> KeyParts;
+    Key.ParseIntoArray(KeyParts, TEXT("."));
+
+    TSharedPtr<FJsonObject> CurrentObject = ParsedData;
+    for (int32 i = 0; i < KeyParts.Num(); ++i)
+    {
+        if (CurrentObject.IsValid() && CurrentObject->HasField(KeyParts[i]))
+        {
+            if (i == KeyParts.Num() - 1)
+            {
+                return CurrentObject->TryGetField(KeyParts[i]);
+            }
+            CurrentObject = CurrentObject->GetObjectField(KeyParts[i]);
+        }
+        else
+        {
+            return nullptr;
+        }
+    }
+    return nullptr;
+}
+
+template <typename StructType>
+bool JsonConfigParser::LoadStruct(const FString& FilePath, StructType& OutStruct)
+{
+    FString FileContent;
+    if (!FFileHelper::LoadFileToString(FileContent, *FilePath))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to load JSON file: %s"), *FilePath);
+        return false;
+    }
+
+    if (!FJsonObjectConverter::JsonObjectStringToUStruct(FileContent, &OutStruct, 0, 0))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to parse JSON into UStruct from file: %s"), *FilePath);
+        return false;
+    }
+
+    return true;
+}
+
+template <typename StructType>
+bool JsonConfigParser::SaveStruct(const StructType& InStruct, const FString& FilePath)
+{
+    FString JsonString;
+    if (!FJsonObjectConverter::UStructToJsonObjectString(InStruct, JsonString))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to convert UStruct to JSON string."));
+        return false;
+    }
+
+    if (!FFileHelper::SaveStringToFile(JsonString, *FilePath))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to save JSON to file: %s"), *FilePath);
+        return false;
+    }
+
+    return true;
+}
+
+template <typename StructType>
+bool JsonConfigParser::LoadArrayStruct(const FString& FilePath, TArray<StructType>& OutArray)
+{
+    FString FileContent;
+    if (!FFileHelper::LoadFileToString(FileContent, *FilePath))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to load JSON file: %s"), *FilePath);
+        return false;
+    }
+
+    // 解析 JSON 数组
+    TArray<TSharedPtr<FJsonValue>> JsonValues;
+    TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(FileContent);
+
+    if (!FJsonSerializer::Deserialize(Reader, JsonValues))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to parse JSON array: %s"), *FilePath);
+        return false;
+    }
+
+    // 将 JSON 数组转换为 TArray<StructType>
+    for (const TSharedPtr<FJsonValue>& Value : JsonValues)
+    {
+        if (Value->Type == EJson::Object)
+        {
+            StructType StructData;
+            if (FJsonObjectConverter::JsonObjectToUStruct(Value->AsObject().ToSharedRef(), &StructData))
+            {
+                OutArray.Add(StructData);
+            }
+            else
+            {
+                UE_LOG(LogTemp, Error, TEXT("Failed to convert JSON object to UStruct."));
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+template <typename StructType>
+bool JsonConfigParser::SaveArrayStruct(const TArray<StructType>& InArray, const FString& FilePath)
+{
+    // 构造 JSON 数组,每个元素对应一个 UStruct 转换后的 JSON 对象
+    TArray<TSharedPtr<FJsonValue>> JsonArray;
+
+    // 遍历并转换每一个结构体元素
+    for (const StructType& Element : InArray)
+    {
+        TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
+        if (!FJsonObjectConverter::UStructToJsonObject(StructType::StaticStruct(), &Element, JsonObject.ToSharedRef(), 0, 0))
+        {
+            UE_LOG(LogTemp, Error, TEXT("Failed to convert element to JSON object."));
+            return false;
+        }
+
+        // 将 JSON 对象包装为 JSON 值后加入数组中
+        JsonArray.Add(MakeShareable(new FJsonValueObject(JsonObject)));
+    }
+
+    // 将 JSON 数组序列化为字符串
+    FString OutJsonString;
+    TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutJsonString);
+    if (!FJsonSerializer::Serialize(JsonArray, Writer))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to serialize JSON array."));
+        return false;
+    }
+
+    // 保存 JSON 字符串到文件
+    if (!FFileHelper::SaveStringToFile(OutJsonString, *FilePath))
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to save JSON to file: %s"), *FilePath);
+        return false;
+    }
+
+    return true;
+}

+ 97 - 0
Source/InJoyLab/Private/Core/InJoyConfigManager.cpp

@@ -0,0 +1,97 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Core/InJoyConfigManager.h"
+#include <Config/JsonConfigParser.h>
+#include <Config/IniConfigParser.h>
+#include <Config/CSVConfigParser.h>
+
+void TestLoadJsonArray()
+{
+    JsonConfigParser Parser;
+
+    // 定义一个 TArray<UStruct> 来存储解析的结果
+    TArray<FPerson> People;
+
+    // 加载 JSON 数组到 TArray
+    if (Parser.LoadArrayStruct(TEXT("Config/People.json"), People))
+    {
+        for (const FPerson& Person : People)
+        {
+            UE_LOG(LogTemp, Log, TEXT("Name: %s, Age: %d, Score: %f"), *Person.Name, Person.Age, Person.Score);
+        }
+    }
+    else
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to load JSON array."));
+    }
+}
+
+void TestSaveJsonArray()
+{
+    JsonConfigParser Parser;
+
+    // 定义一个 TArray<UStruct> 并填充数据
+    TArray<FPerson> People;
+    People.Add({ TEXT("John"), 30, 95.5f });
+    People.Add({ TEXT("Alice"), 25, 88.0f });
+    People.Add({ TEXT("Bob"), 28, 78.0f });
+
+    // 保存 TArray 到 JSON 文件
+    if (Parser.SaveArrayStruct(People, TEXT("Config/GeneratedPeople.json")))
+    {
+        UE_LOG(LogTemp, Log, TEXT("Saved JSON array successfully."));
+    }
+    else
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to save JSON array."));
+    }
+}
+
+void TestIniConfigParser()
+{
+    IniConfigParser Parser;
+    if (Parser.LoadConfig(TEXT("Config/MyConfig.ini")))
+    {
+        FString Name = Parser.GetString(TEXT("Name"), TEXT("DefaultName"));
+        int32 Age = Parser.GetInt(TEXT("Age"), 0);
+        bool IsEnabled = Parser.GetBool(TEXT("IsEnabled"), false);
+
+        UE_LOG(LogTemp, Log, TEXT("Name: %s"), *Name);
+        UE_LOG(LogTemp, Log, TEXT("Age: %d"), Age);
+        UE_LOG(LogTemp, Log, TEXT("IsEnabled: %s"), IsEnabled ? TEXT("True") : TEXT("False"));
+
+        Parser.PrintConfig();
+    }
+}
+
+void TestCSVConfigParser()
+{
+    CSVConfigParser Parser;
+
+    if (Parser.LoadConfig(TEXT("Config/MyConfig.csv")))
+    {
+        // 获取字符串
+        FString Name = Parser.GetString(TEXT("0.0"), TEXT("DefaultName"));
+        UE_LOG(LogTemp, Log, TEXT("Name: %s"), *Name);
+
+        // 获取整数
+        int32 Age = Parser.GetInt(TEXT("1.1"), 0);
+        UE_LOG(LogTemp, Log, TEXT("Age: %d"), Age);
+
+        // 获取浮点数
+        float Score = Parser.GetFloat(TEXT("2.2"), 0.0f);
+        UE_LOG(LogTemp, Log, TEXT("Score: %f"), Score);
+
+        // 获取布尔值
+        bool IsActive = Parser.GetBool(TEXT("0.3"), false);
+        UE_LOG(LogTemp, Log, TEXT("IsActive: %s"), IsActive ? TEXT("True") : TEXT("False"));
+
+        // 打印整个 CSV 内容
+        Parser.PrintConfig();
+    }
+    else
+    {
+        UE_LOG(LogTemp, Error, TEXT("Failed to load CSV file."));
+    }
+}

+ 5 - 0
Source/InJoyLab/Private/Core/InJoyGameInstance.cpp

@@ -0,0 +1,5 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Core/InJoyGameInstance.h"
+

+ 5 - 0
Source/InJoyLab/Private/Core/InJoyGameMode.cpp

@@ -0,0 +1,5 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Core/InJoyGameMode.h"
+

+ 5 - 0
Source/InJoyLab/Private/Interfaces/IConfigParser.cpp

@@ -0,0 +1,5 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Interfaces/IConfigParser.h"
+

+ 5 - 0
Source/InJoyLab/Private/Interfaces/IModule.cpp

@@ -0,0 +1,5 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Interfaces/IModule.h"
+

+ 47 - 0
Source/InJoyLab/Public/Config/CSVConfigParser.h

@@ -0,0 +1,47 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Interfaces/IConfigParser.h"
+
+class INJOYLAB_API CSVConfigParser : public IConfigParser
+{
+private:
+    TArray<TArray<FString>> ParsedData; // 存储 CSV 数据表(二维数组)
+
+public:
+    CSVConfigParser();
+    virtual ~CSVConfigParser() = default;
+
+    // 加载 CSV 配置文件
+    virtual bool LoadConfig(const FString& FilePath) override;
+
+    // 获取某一行的数据
+    TArray<FString> GetRow(int32 RowIndex) const;
+
+    // 获取某一列的数据
+    TArray<FString> GetColumn(int32 ColumnIndex) const;
+
+    // 获取某个单元格的数据
+    FString GetCell(int32 RowIndex, int32 ColumnIndex, const FString& DefaultValue = TEXT("")) const;
+
+    // 获取字符串值(根据 "RowIndex.ColumnIndex")
+    virtual FString GetString(const FString& Key, const FString& DefaultValue = TEXT("")) const override;
+
+    // 获取整数值(根据 "RowIndex.ColumnIndex")
+    virtual int32 GetInt(const FString& Key, int32 DefaultValue = 0) const override;
+
+    // 获取浮点数值(根据 "RowIndex.ColumnIndex")
+    virtual float GetFloat(const FString& Key, float DefaultValue = 0.0f) const override;
+
+    // 获取布尔值(根据 "RowIndex.ColumnIndex")
+    virtual bool GetBool(const FString& Key, bool DefaultValue = false) const override;
+
+    // 获取数组值(返回某一行)
+    virtual TArray<FString> GetArray(const FString& Key) const override;
+
+    // 获取子对象(返回某一列)
+    virtual TSharedPtr<IConfigParser> GetObject(const FString& Key) const override;
+
+    // 打印 CSV 内容(调试用)
+    virtual void PrintConfig() const override;
+};

+ 38 - 0
Source/InJoyLab/Public/Config/IniConfigParser.h

@@ -0,0 +1,38 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Interfaces/IConfigParser.h"
+
+class INJOYLAB_API IniConfigParser : public IConfigParser
+{
+private:
+    TMap<FString, FString> ParsedData; // 存储 INI 配置键值对
+
+public:
+    IniConfigParser();
+    virtual ~IniConfigParser() = default;
+
+    // 加载 INI 配置文件
+    virtual bool LoadConfig(const FString& FilePath) override;
+
+    // 获取字符串值
+    virtual FString GetString(const FString& Key, const FString& DefaultValue = TEXT("")) const override;
+
+    // 获取整数值
+    virtual int32 GetInt(const FString& Key, int32 DefaultValue = 0) const override;
+
+    // 获取浮点数值
+    virtual float GetFloat(const FString& Key, float DefaultValue = 0.0f) const override;
+
+    // 获取布尔值
+    virtual bool GetBool(const FString& Key, bool DefaultValue = false) const override;
+
+    // 获取数组值
+    virtual TArray<FString> GetArray(const FString& Key) const override;
+
+    // 获取子对象
+    virtual TSharedPtr<IConfigParser> GetObject(const FString& Key) const override;
+
+    // 打印配置内容(调试用)
+    virtual void PrintConfig() const override;
+};

+ 55 - 0
Source/InJoyLab/Public/Config/JsonConfigParser.h

@@ -0,0 +1,55 @@
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Interfaces/IConfigParser.h"
+
+class INJOYLAB_API JsonConfigParser : public IConfigParser
+{
+private:
+    TSharedPtr<FJsonObject> ParsedData; // 存储解析后的 JSON 数据
+
+    // 辅助函数:根据层次化键查找 JSON 对象
+    TSharedPtr<FJsonValue> FindJsonValue(const FString& Key) const;
+
+public:
+    virtual ~JsonConfigParser() = default;
+
+    // 加载 JSON 配置文件
+    virtual bool LoadConfig(const FString& FilePath) override;
+
+    // 获取字符串值
+    virtual FString GetString(const FString& Key, const FString& DefaultValue = TEXT("")) const override;
+
+    // 获取整数值
+    virtual int32 GetInt(const FString& Key, int32 DefaultValue = 0) const override;
+
+    // 获取浮点数值
+    virtual float GetFloat(const FString& Key, float DefaultValue = 0.0f) const override;
+
+    // 获取布尔值
+    virtual bool GetBool(const FString& Key, bool DefaultValue = false) const override;
+
+    // 获取数组值
+    virtual TArray<FString> GetArray(const FString& Key) const override;
+
+    // 获取子对象
+    virtual TSharedPtr<IConfigParser> GetObject(const FString& Key) const override;
+
+    // 打印配置内容
+    virtual void PrintConfig() const override;
+
+    // 将 JSON 加载到 UStruct
+    template <typename StructType>
+    bool LoadStruct(const FString& FilePath, StructType& OutStruct);
+
+    // 将 UStruct 保存为 JSON 文件
+    template <typename StructType>
+    bool SaveStruct(const StructType& InStruct, const FString& FilePath);
+
+    // 将 JSON 数组加载到 TArray<UStruct>
+    template <typename StructType>
+    bool LoadArrayStruct(const FString& FilePath, TArray<StructType>& OutArray);
+
+    template <typename StructType>
+    bool SaveArrayStruct(const TArray<StructType>& InArray, const FString& FilePath);
+};

+ 47 - 0
Source/InJoyLab/Public/Core/InJoyConfigManager.h

@@ -0,0 +1,47 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "UObject/NoExportTypes.h"
+#include "InJoyConfigManager.generated.h"
+
+USTRUCT(BlueprintType)
+struct FPerson
+{
+    GENERATED_BODY()
+
+    UPROPERTY(BlueprintReadWrite)
+    FString Name;
+
+    UPROPERTY(BlueprintReadWrite)
+    int32 Age;
+
+    UPROPERTY(BlueprintReadWrite)
+    float Score;
+};
+
+/*
+* CSV
+Name,Age,Score,IsActive
+John,25,89.5,true
+Alice,30,95.0,false
+Bob,22,78.0,true
+*/
+
+/**
+ * 
+ */
+UCLASS()
+class INJOYLAB_API UInJoyConfigManager : public UObject
+{
+	GENERATED_BODY()
+	
+	void TestLoadJsonArray();
+
+	void TestSaveJsonArray();
+
+    void TestIniConfigParser();
+
+    void TestCSVConfigParser();
+};

+ 17 - 0
Source/InJoyLab/Public/Core/InJoyGameInstance.h

@@ -0,0 +1,17 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "Engine/GameInstance.h"
+#include "InJoyGameInstance.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class INJOYLAB_API UInJoyGameInstance : public UGameInstance
+{
+	GENERATED_BODY()
+	
+};

+ 17 - 0
Source/InJoyLab/Public/Core/InJoyGameMode.h

@@ -0,0 +1,17 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameFramework/GameModeBase.h"
+#include "InJoyGameMode.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class INJOYLAB_API AInJoyGameMode : public AGameModeBase
+{
+	GENERATED_BODY()
+	
+};

+ 38 - 0
Source/InJoyLab/Public/Interfaces/IConfigParser.h

@@ -0,0 +1,38 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+
+/**
+ * 
+ */
+class INJOYLAB_API IConfigParser
+{
+public:
+    virtual ~IConfigParser() = default;
+
+    // 加载配置文件
+    virtual bool LoadConfig(const FString& FilePath) = 0;
+
+    // 获取字符串值
+    virtual FString GetString(const FString& Key, const FString& DefaultValue = TEXT("")) const = 0;
+
+    // 获取整数值
+    virtual int32 GetInt(const FString& Key, int32 DefaultValue = 0) const = 0;
+
+    // 获取浮点数值
+    virtual float GetFloat(const FString& Key, float DefaultValue = 0.0f) const = 0;
+
+    // 获取布尔值
+    virtual bool GetBool(const FString& Key, bool DefaultValue = false) const = 0;
+
+    // 获取数组值(返回数组的字符串形式)
+    virtual TArray<FString> GetArray(const FString& Key) const = 0;
+
+    // 获取子对象(嵌套结构)
+    virtual TSharedPtr<IConfigParser> GetObject(const FString& Key) const = 0;
+
+    // 打印配置内容(调试用)
+    virtual void PrintConfig() const = 0;
+};

+ 17 - 0
Source/InJoyLab/Public/Interfaces/IModule.h

@@ -0,0 +1,17 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "UObject/NoExportTypes.h"
+#include "IModule.generated.h"
+
+/**
+ * 
+ */
+UCLASS()
+class INJOYLAB_API UIModule : public UObject
+{
+	GENERATED_BODY()
+	
+};