79 lines
4.8 KiB
C++
79 lines
4.8 KiB
C++
#include "MetaCoreRuntimeInput/MetaCoreInputMap.h"
|
|
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <span>
|
|
#include <unordered_set>
|
|
|
|
namespace MetaCore {
|
|
|
|
std::vector<MetaCoreInputMapIssue> MetaCoreValidateInputMap(const MetaCoreInputMapDocument& document) {
|
|
std::vector<MetaCoreInputMapIssue> issues;
|
|
std::unordered_set<std::string> actionIds, axisIds, bindingIds, contextIds;
|
|
for (const auto& value : document.Actions) {
|
|
if (value.Id.empty() || !actionIds.insert(value.Id).second) issues.push_back({true, value.Id, "Action ID 为空或重复"});
|
|
}
|
|
for (const auto& value : document.Axes) {
|
|
if (value.Id.empty() || !axisIds.insert(value.Id).second) issues.push_back({true, value.Id, "Axis ID 为空或重复"});
|
|
if (value.DeadZone < 0.0F || value.DeadZone >= 1.0F || value.Sensitivity < 0.0F || value.Minimum >= value.Maximum)
|
|
issues.push_back({true, value.Id, "Axis 死区、灵敏度或取值范围无效"});
|
|
}
|
|
for (const auto& value : document.Bindings) {
|
|
if (value.BindingId.empty() || !bindingIds.insert(value.BindingId).second) issues.push_back({true, value.BindingId, "Binding ID 为空或重复"});
|
|
if (value.ControlPath.empty() || value.ControlPath.front() != '<' || value.ControlPath.find("/", 1) == std::string::npos) issues.push_back({true, value.BindingId, "控制路径无效"});
|
|
const bool targetExists = value.Target == MetaCoreInputBindingTarget::Action ? actionIds.contains(value.TargetId) : axisIds.contains(value.TargetId);
|
|
if (!targetExists) issues.push_back({true, value.BindingId, "Binding 目标不存在"});
|
|
}
|
|
for (const auto& value : document.Contexts) {
|
|
if (value.Id.empty() || !contextIds.insert(value.Id).second) issues.push_back({true, value.Id, "Context ID 为空或重复"});
|
|
for (const auto& binding : value.BindingIds) if (!bindingIds.contains(binding)) issues.push_back({true, value.Id, "Context 引用了不存在的 Binding: " + binding});
|
|
}
|
|
return issues;
|
|
}
|
|
|
|
bool MetaCoreWriteInputMap(const std::filesystem::path& path, const MetaCoreInputMapDocument& document, const MetaCoreTypeRegistry& registry) {
|
|
const auto bytes = MetaCoreSerializeToBytes(document, registry);
|
|
if (!bytes) return false;
|
|
std::error_code error;
|
|
std::filesystem::create_directories(path.parent_path(), error);
|
|
std::ofstream output(path, std::ios::binary | std::ios::trunc);
|
|
if (!output) return false;
|
|
output.write(reinterpret_cast<const char*>(bytes->data()), static_cast<std::streamsize>(bytes->size()));
|
|
return output.good();
|
|
}
|
|
|
|
std::optional<MetaCoreInputMapDocument> MetaCoreReadInputMap(const std::filesystem::path& path, const MetaCoreTypeRegistry& registry) {
|
|
std::ifstream input(path, std::ios::binary | std::ios::ate);
|
|
if (!input) return std::nullopt;
|
|
const auto size = static_cast<std::size_t>(input.tellg()); input.seekg(0);
|
|
std::vector<std::byte> bytes(size);
|
|
if (size && !input.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(size))) return std::nullopt;
|
|
MetaCoreInputMapDocument result;
|
|
if (!MetaCoreDeserializeFromBytes(std::span<const std::byte>(bytes.data(), bytes.size()), result, registry)) return std::nullopt;
|
|
return result;
|
|
}
|
|
|
|
MetaCoreInputMapDocument MetaCoreBuildDefaultInputMap() {
|
|
MetaCoreInputMapDocument map;
|
|
map.Actions = {{"Interact", "Interact"}, {"Cancel", "Cancel"}};
|
|
map.Axes = {{"MoveX", "Move X", 0.15F, 1.0F, -1.0F, 1.0F}, {"MoveY", "Move Y", 0.15F, 1.0F, -1.0F, 1.0F}, {"LookX", "Look X", 0.0F, 1.0F, -1.0F, 1.0F}, {"LookY", "Look Y", 0.0F, 1.0F, -1.0F, 1.0F}};
|
|
map.Bindings = {
|
|
{"game.interact.mouse", "Interact", MetaCoreInputBindingTarget::Action, "<Mouse>/leftButton", 1.0F, {}},
|
|
{"game.cancel.escape", "Cancel", MetaCoreInputBindingTarget::Action, "<Keyboard>/Escape", 1.0F, {}},
|
|
{"game.move.left", "MoveX", MetaCoreInputBindingTarget::Axis, "<Keyboard>/A", -1.0F, {}},
|
|
{"game.move.right", "MoveX", MetaCoreInputBindingTarget::Axis, "<Keyboard>/D", 1.0F, {}},
|
|
{"game.move.back", "MoveY", MetaCoreInputBindingTarget::Axis, "<Keyboard>/S", -1.0F, {}},
|
|
{"game.move.forward", "MoveY", MetaCoreInputBindingTarget::Axis, "<Keyboard>/W", 1.0F, {}},
|
|
{"game.look.x", "LookX", MetaCoreInputBindingTarget::Axis, "<Mouse>/delta/x", 1.0F, {}},
|
|
{"game.look.y", "LookY", MetaCoreInputBindingTarget::Axis, "<Mouse>/delta/y", 1.0F, {}},
|
|
{"game.pad.move.x", "MoveX", MetaCoreInputBindingTarget::Axis, "<Gamepad>/leftStick/x", 1.0F, {}},
|
|
{"game.pad.move.y", "MoveY", MetaCoreInputBindingTarget::Axis, "<Gamepad>/leftStick/y", 1.0F, {}}
|
|
};
|
|
MetaCoreInputContextDefinition gameplay{"Gameplay", 0, true, false, {}};
|
|
for (const auto& binding : map.Bindings) gameplay.BindingIds.push_back(binding.BindingId);
|
|
map.Contexts.push_back(std::move(gameplay));
|
|
return map;
|
|
}
|
|
|
|
} // namespace MetaCore
|