first draft is working
This commit is contained in:
parent
e4289ee1ac
commit
b3c4395fac
8
.vscode/tasks.json
vendored
8
.vscode/tasks.json
vendored
@ -9,6 +9,14 @@
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "build Frontend",
|
||||
"type": "shell",
|
||||
"command": "npm run build",
|
||||
"options": {
|
||||
"cwd": "frontend"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -33,11 +33,20 @@ foreach(file IN LISTS TEMPLATE_FILES)
|
||||
configure_file("${file}" "${CMAKE_BINARY_DIR}/${rel_path}" COPYONLY)
|
||||
endforeach()
|
||||
|
||||
# warnings to ignore
|
||||
add_compile_options(-Wno-deprecated-declarations)
|
||||
add_compile_options(-Wno-deprecated-literal-operator)
|
||||
|
||||
# Use Crow from system include (installed via yay -S crow + asio)
|
||||
include_directories(/usr/include src src/htmx src/shadowrun src/database src/login)
|
||||
include_directories(
|
||||
/usr/include
|
||||
include/libs
|
||||
src
|
||||
src/htmx
|
||||
src/shadowrun
|
||||
src/database
|
||||
src/login
|
||||
)
|
||||
|
||||
add_executable(${TARGET_NAME}
|
||||
src/main.cpp
|
||||
@ -47,7 +56,6 @@ add_executable(${TARGET_NAME}
|
||||
src/htmx/HtmxTable.h
|
||||
src/systemd.cpp
|
||||
src/systemd.h
|
||||
src/json.hpp
|
||||
src/htmx/HtmxTableRow.cpp
|
||||
src/htmx/HtmxTableRow.h
|
||||
src/htmx/HtmxObject.cpp
|
||||
@ -56,11 +64,9 @@ add_executable(${TARGET_NAME}
|
||||
src/htmx_helper.h
|
||||
src/json_settings.cpp
|
||||
src/json_settings.h
|
||||
src/json.hpp
|
||||
|
||||
src/database/database.cpp
|
||||
src/database/database.hpp
|
||||
src/database/sqlite_orm.h
|
||||
|
||||
# Shadowrun
|
||||
src/shadowrun/HtmxShItemList.cpp
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { API_BASE } from '$lib/config';
|
||||
|
||||
export let currentCharacter: any;
|
||||
export let currentCharacterData: any;
|
||||
|
||||
// Use character data if provided
|
||||
let characterInfo = currentCharacter?.characterInfo || {
|
||||
//{ name: "Name", value: "", type: ""},
|
||||
let characterData = currentCharacterData ?? {};
|
||||
|
||||
characterData.Info ??= {
|
||||
Metatype : "",
|
||||
Age : 30,
|
||||
Sex : "Man",
|
||||
@ -18,6 +20,20 @@
|
||||
Fame : 0
|
||||
};
|
||||
|
||||
characterData.Attributes ??= {
|
||||
Agility: 1,
|
||||
Body: 1,
|
||||
Charisma: 1,
|
||||
Edge: 1,
|
||||
Essence: 1,
|
||||
Initiative: 1,
|
||||
Intuition: 1,
|
||||
Logic: 1,
|
||||
Reaction: 1,
|
||||
Strength: 1,
|
||||
Willpower: 1,
|
||||
};
|
||||
|
||||
const characterInfoTypes = {
|
||||
Metatype: "text",
|
||||
Age: "number",
|
||||
@ -29,22 +45,7 @@
|
||||
"Street Cred": "number",
|
||||
Notoriety: "number",
|
||||
Fame: "number"
|
||||
};
|
||||
|
||||
// Use character data if provided
|
||||
let attributes = currentCharacter?.attributes || [
|
||||
{ name: 'Body', value: 1 },
|
||||
{ name: 'Agility', value: 1 },
|
||||
{ name: 'Reaction', value: 1 },
|
||||
{ name: 'Strength', value: 1 },
|
||||
{ name: 'Charisma', value: 1 },
|
||||
{ name: 'Intuition', value: 1 },
|
||||
{ name: 'Logic', value: 1 },
|
||||
{ name: 'Willpower', value: 1 },
|
||||
{ name: 'Edge', value: 1 },
|
||||
{ name: 'Essence', value: 0 },
|
||||
{ name: 'Initiative', value: 0}
|
||||
];
|
||||
};
|
||||
|
||||
let skills = currentCharacter?.skills || [
|
||||
{ name: 'Pistols', rating: 0, linked: 'Agility' },
|
||||
@ -53,10 +54,10 @@
|
||||
];
|
||||
|
||||
async function saveCharacterData() {
|
||||
const res = await fetch(`${API_BASE}/api/shadowrun/characters_data`, {
|
||||
const res = await fetch(`${API_BASE}/api/shadowrun/characters_data/${currentCharacter.id}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ characterInfo })
|
||||
body: JSON.stringify(characterData)
|
||||
});
|
||||
}
|
||||
|
||||
@ -64,26 +65,33 @@
|
||||
let inventory = currentCharacter?.inventory || [];
|
||||
</script>
|
||||
|
||||
|
||||
<h1>Name: {currentCharacter.name}</h1>
|
||||
|
||||
<h2>Character Info</h2>
|
||||
{#each Object.entries(characterInfo) as [key, value], i}
|
||||
{#each Object.entries(characterData["Info"]) as [key, value], i}
|
||||
<div class="input-row">
|
||||
<label for={"field-" + i}>{key}</label>
|
||||
<input
|
||||
id={"field-" + i}
|
||||
type={characterInfoTypes[key]}
|
||||
bind:value={value}
|
||||
bind:value={characterData["Info"][key]}
|
||||
min={characterInfoTypes[key] === "number" ? 0 : null} />
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<h2>Attributes</h2>
|
||||
{#each attributes as field, i}
|
||||
<label>
|
||||
{field.name}:
|
||||
<input type="number" min="1" bind:value={field.value} />
|
||||
</label>
|
||||
{#each Object.entries(characterData["Attributes"]) as [key, value], i}
|
||||
<div class="input-row">
|
||||
<label for={"field-" + i}>{key}</label>
|
||||
<input
|
||||
id={"field-" + i}
|
||||
type="number"
|
||||
bind:value={characterData["Attributes"][key]}
|
||||
min=0 />
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<!--
|
||||
<h2>Skills</h2>
|
||||
{#each skills as skill, i}
|
||||
<div>
|
||||
@ -97,3 +105,5 @@
|
||||
<span>Dice Pool: {skill.rating + attributes[skill.linked]}</span>
|
||||
</div>
|
||||
{/each}
|
||||
-->
|
||||
<button on:click={saveCharacterData}>Save</button>
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
};
|
||||
|
||||
let characters: Character[] = [];
|
||||
let currentCharacter: any = null;
|
||||
let currentCharacterData : any = null
|
||||
let selectedCharId: number | null = null;
|
||||
let creatingNew = false;
|
||||
let newCharName = '';
|
||||
@ -20,6 +22,12 @@
|
||||
characters = await res.json();
|
||||
}
|
||||
|
||||
async function loadCharacterData(characterId : number) {
|
||||
const res = await fetch(`${API_BASE}/api/shadowrun/characters_data/${characterId}`);
|
||||
if (res.ok)
|
||||
currentCharacterData = await res.json();
|
||||
}
|
||||
|
||||
onMount(loadCharacters);
|
||||
|
||||
async function loadCharacter() {
|
||||
@ -28,6 +36,7 @@
|
||||
const res = await fetch(`${API_BASE}/api/shadowrun/characters/${selectedCharId}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
await loadCharacterData(data.id)
|
||||
currentCharacter = data;
|
||||
creatingNew = false;
|
||||
}
|
||||
@ -49,7 +58,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
let currentCharacter: any = null;
|
||||
</script>
|
||||
|
||||
{#if !currentCharacter}
|
||||
@ -76,5 +84,5 @@
|
||||
<button on:click={createCharacter}>Create</button>
|
||||
</div>
|
||||
{:else}
|
||||
<CharacterSheet {currentCharacter} />
|
||||
<CharacterSheet {currentCharacter} {currentCharacterData} />
|
||||
{/if}
|
||||
|
||||
1564
include/libs/magic_enum.hpp
Normal file
1564
include/libs/magic_enum.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,9 @@
|
||||
|
||||
|
||||
#include "ShadowrunApi.hpp"
|
||||
#include "ShadowrunCharacterForm.hpp"
|
||||
#include "ShadowrunDb.hpp"
|
||||
#include "login.hpp"
|
||||
#include <format>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
@ -113,7 +111,10 @@ void initApi(crow::SimpleApp& app)
|
||||
CROW_ROUTE(app, "/api/shadowrun/characters")
|
||||
([&]() {
|
||||
auto characters = getCharacters();
|
||||
return utils::toJsonArray(characters);
|
||||
auto res =
|
||||
crow::response(200, utils::toJsonArray(characters));
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/api/shadowrun/characters").methods("POST"_method)
|
||||
@ -124,7 +125,9 @@ void initApi(crow::SimpleApp& app)
|
||||
if(id > 0){
|
||||
auto character = getChracter(id);
|
||||
if (character.has_value()){
|
||||
return crow::response(200, nlohmann::json(character.value()).dump());
|
||||
auto res = crow::response(200, nlohmann::json(character.value()).dump());
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return crow::response(405, "Failed to create character");
|
||||
@ -135,15 +138,53 @@ void initApi(crow::SimpleApp& app)
|
||||
auto optCharacter = getChracter(id);
|
||||
if (!optCharacter.has_value())
|
||||
return crow::response(404, "Character not found");
|
||||
return crow::response(200, nlohmann::json(optCharacter.value()).dump());
|
||||
|
||||
auto res = crow::response(200, nlohmann::json(optCharacter.value()).dump());
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/api/shadowrun/characters_data/<int>")
|
||||
([&](int id) {
|
||||
auto character_data = getChracterData(id);
|
||||
if (!character_data.empty())
|
||||
return crow::response(405, "No character data found");
|
||||
return crow::response(200, utils::toJsonArray(character_data));
|
||||
nlohmann::json j;
|
||||
const auto characterData = getChracterData(id);
|
||||
|
||||
if(characterData.empty())
|
||||
return crow::response(405, "Character not found");
|
||||
|
||||
for(const auto& data : characterData ){
|
||||
const auto& key = magic_enum::enum_cast<Type>(data.type);
|
||||
if(key.has_value()){
|
||||
auto res = utils::parseJson(data.json);
|
||||
if(res){
|
||||
j[magic_enum::enum_name(key.value())] = res.value();
|
||||
} else {
|
||||
CROW_LOG_ERROR << "Failed to parse json: " << res.error();
|
||||
}
|
||||
} else {
|
||||
CROW_LOG_ERROR << "Read invalid type from database: " << data.type;
|
||||
}
|
||||
}
|
||||
|
||||
auto res = crow::response(200, j.dump());
|
||||
res.set_header("Content-Type", "application/json");
|
||||
return res;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/api/shadowrun/characters_data/<int>").methods("POST"_method)
|
||||
([&](const crow::request& req, int id) {
|
||||
nlohmann::json j = nlohmann::json::parse(req.body);
|
||||
|
||||
for (auto type : magic_enum::enum_values<Type>()) {
|
||||
const auto& key = magic_enum::enum_name(type);
|
||||
if (j.contains(key)){
|
||||
storeCharacterData(id, type, j[key].dump());
|
||||
}
|
||||
}
|
||||
|
||||
auto res = crow::response(200, "Saved Character data");
|
||||
return res;
|
||||
//return crow::response(405, ret.error());
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@ -1,13 +1,7 @@
|
||||
#include "ShadowrunDb.hpp"
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include "database.hpp"
|
||||
#include "databasepool.h"
|
||||
#include "crow.h"
|
||||
#include "utils.hpp"
|
||||
#include "sqlite_orm.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace sqlite_orm;
|
||||
@ -28,53 +22,11 @@ int64_t createCharacter(const string& name){
|
||||
if (!character.empty()) {
|
||||
return character[0].id;
|
||||
} else {
|
||||
auto c = createShadowrunCharacter(name);
|
||||
auto c = newShadowrunCharacter(name);
|
||||
return db->insert(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bool storeCharacterData(int64_t characterKey, vector<pair<const string, const string>>& idValues){
|
||||
auto characterData = getCharacterData(characterKey);
|
||||
std::map<string, ShadowrunCharacterData*> dataMap;
|
||||
for(auto& data : characterData) {
|
||||
dataMap[data.name] = &data;
|
||||
}
|
||||
|
||||
auto db = dbpool.acquire();
|
||||
for (auto& idValue : idValues) {
|
||||
const string& name = idValue.first;
|
||||
const string& value = idValue.second;
|
||||
|
||||
// update if already exist
|
||||
auto it = dataMap.find(name);
|
||||
if(it != dataMap.end()){
|
||||
db->update_all(
|
||||
sqlite_orm::set(
|
||||
assign(&ShadowrunData::value, idValue.second),
|
||||
assign(&ShadowrunData::updated_at, utils::currentTime())
|
||||
),
|
||||
where(
|
||||
c(&ShadowrunData::name) == name and
|
||||
c(&ShadowrunData::character_id) == characterKey
|
||||
)
|
||||
);
|
||||
} else {
|
||||
ShadowrunData entry {
|
||||
.id = -1,
|
||||
.character_id = static_cast<int>(characterKey),
|
||||
.name = name,
|
||||
.value = value,
|
||||
.created_at = utils::currentTime(),
|
||||
.updated_at = "",
|
||||
};
|
||||
db->insert(entry);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
std::vector<ShadowrunCharacter> getCharacters(){
|
||||
auto db = dbpool.acquire();
|
||||
return db->get_all<ShadowrunCharacter>();
|
||||
@ -105,8 +57,31 @@ vector<ShadowrunCharacterData> getChracterData(int character_id)
|
||||
return characterData;
|
||||
}
|
||||
|
||||
int storeCharacterData(const ShadowrunCharacterData& data)
|
||||
{
|
||||
int storeCharacterData(int characterId, const Type type, const string& json){
|
||||
auto db = dbpool.acquire();
|
||||
auto characterData = db->get_all<ShadowrunCharacterData>(
|
||||
where(
|
||||
(c(&ShadowrunCharacterData::character_id) == characterId) and
|
||||
(c(&ShadowrunCharacterData::type) == static_cast<int>(type)))
|
||||
);
|
||||
|
||||
if(characterData.empty()){
|
||||
ShadowrunCharacterData character = newShadowrunCharacterData(characterId, type, json);
|
||||
return db->insert(character);
|
||||
}
|
||||
else {
|
||||
if (characterData.size() > 1){
|
||||
CROW_LOG_ERROR << "Character ID: " << characterId << "has mote than 1 type: " << magic_enum::enum_name(type);
|
||||
}
|
||||
auto& character = characterData[0];
|
||||
character.json = json;
|
||||
character.updated_at = utils::currentTime();
|
||||
db->update(character);
|
||||
return character.id;
|
||||
}
|
||||
}
|
||||
|
||||
int storeCharacterData(const ShadowrunCharacterData& data){
|
||||
auto db = dbpool.acquire();
|
||||
auto characterData = db->get_optional<ShadowrunCharacterData>(data.id);
|
||||
|
||||
@ -119,13 +94,4 @@ int storeCharacterData(const ShadowrunCharacterData& data)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
vector<ShadowrunData> getCharacterData(int64_t characterKey) {
|
||||
auto db = dbpool.acquire();
|
||||
return db->get_all<ShadowrunData>(
|
||||
where(c(&ShadowrunData::character_id) == characterKey)
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@ -7,8 +7,17 @@
|
||||
#include <optional>
|
||||
#include "json.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "sqlite_orm.h"
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
namespace shadowrun {
|
||||
enum class Type {
|
||||
Unknown = 0,
|
||||
Info = 1,
|
||||
Attributes = 2,
|
||||
Skills = 3,
|
||||
};
|
||||
|
||||
struct ShadowrunCharacter {
|
||||
int id;
|
||||
std::string name;
|
||||
@ -17,26 +26,40 @@ namespace shadowrun {
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ShadowrunCharacter, id, name, created_at)
|
||||
|
||||
inline ShadowrunCharacter createShadowrunCharacter(const std::string& name){
|
||||
inline ShadowrunCharacter newShadowrunCharacter(const std::string& name){
|
||||
return ShadowrunCharacter {-1, name, utils::currentTime()};
|
||||
}
|
||||
|
||||
struct ShadowrunCharacterData {
|
||||
int id;
|
||||
int character_id;
|
||||
std::string type;
|
||||
int type;
|
||||
std::string json;
|
||||
std::string created_at;
|
||||
std::string updated_at;
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ShadowrunCharacterData, id, character_id, type, json, created_at, updated_at);
|
||||
|
||||
inline ShadowrunCharacterData newShadowrunCharacterData(int character_id, Type type, const std::string json){
|
||||
std::string time = utils::currentTime();
|
||||
return ShadowrunCharacterData {
|
||||
.id = -1,
|
||||
.character_id = character_id,
|
||||
.type = static_cast<int>(type),
|
||||
.json = json,
|
||||
.created_at = time,
|
||||
.updated_at = time,
|
||||
};
|
||||
}
|
||||
|
||||
int64_t createCharacter(const std::string& name);
|
||||
bool storeCharacterData(int64_t characterKey, std::vector<std::pair<const std::string, const std::string>>& idValues);
|
||||
std::vector<ShadowrunCharacter> getCharacters();
|
||||
std::optional<ShadowrunCharacter> getChracter(int id);
|
||||
|
||||
std::vector<ShadowrunCharacterData> getChracterData(int character_id);
|
||||
int storeCharacterData(const ShadowrunCharacterData& data);
|
||||
int storeCharacterData(int characterId, const Type type, const std::string& json);
|
||||
}
|
||||
|
||||
|
||||
#endif // __SHADOWRUNDB_H__
|
||||
@ -143,4 +143,12 @@ string currentTime(){
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::expected<nlohmann::json, std::string> parseJson(const std::string& input) {
|
||||
try {
|
||||
return nlohmann::json::parse(input);
|
||||
}
|
||||
catch (const nlohmann::json::exception& e) {
|
||||
return std::unexpected(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,8 +6,8 @@
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <expected>
|
||||
#include "json.hpp"
|
||||
|
||||
namespace utils {
|
||||
std::map<std::string, std::string> parseBody(const std::string& body);
|
||||
|
||||
@ -33,6 +33,20 @@ namespace utils {
|
||||
}
|
||||
return arr.dump();
|
||||
}
|
||||
|
||||
std::expected<nlohmann::json, std::string> parseJson(const std::string& input);
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::expected<T, std::string> fromJson(const std::string& input) {
|
||||
try {
|
||||
return nlohmann::json::parse(input).get<T>();
|
||||
}
|
||||
catch (const nlohmann::json::exception& e) {
|
||||
return std::unexpected(e.what());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user