Compare commits

...

11 Commits

36 changed files with 1370 additions and 1220 deletions

View File

@@ -1,6 +0,0 @@
[flint.py]
version = feature_git
base_url = https://downloads.mtgames.nl/release/flint
[flint.py-plugins]

12
.gitignore vendored
View File

@@ -16,6 +16,11 @@
# .nfs files are created when an open file is removed but is still being accessed # .nfs files are created when an open file is removed but is still being accessed
.nfs* .nfs*
### premake-gmake ###
Makefile
*.make
build/
### Vim ### ### Vim ###
# swap # swap
[._]*.s[a-v][a-z] [._]*.s[a-v][a-z]
@@ -28,12 +33,7 @@ Session.vim
.netrwhist .netrwhist
# auto-generated tag files # auto-generated tag files
tags tags
# flint
.flint
# Custom
.clangd/ .clangd/
compile_commands.json compile_commands.json
test/ids
test/entities test/entities

9
.gitmodules vendored
View File

@@ -1,9 +0,0 @@
[submodule "vendor/sol2"]
path = vendor/sol2
url = https://github.com/ThePhD/sol2
[submodule "vendor/lua"]
path = vendor/lua
url = https://github.com/lua/lua
[submodule "vendor/stduuid"]
path = vendor/stduuid
url = https://github.com/mariusbancila/stduuid

View File

@@ -1,3 +1,3 @@
let &makeprg="./flint.py --version ../flint/.flint/build/linux/debug/bin" let &makeprg="cmake --build build -t ecs_test"
map <silent> <F9> :Make<cr> map <silent> <F9> :Make<cr>
map <silent> <F10> :Start ./flint.py --version ../flint/.flint/build/linux/debug/bin -rs && read<cr> map <silent> <F10> :Start cd test && ./../build/test/ecs_test save && read<cr>

16
CMakeLists.txt Normal file
View File

@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(ecs)
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.")
endif()
include(cmake/tools.cmake)
add_subdirectory(ecs)
add_subdirectory(ecs-lua)
add_subdirectory(ecs-serial)
add_subdirectory(test)
set_target_properties(ecs_test PROPERTIES EXCLUDE_FROM_ALL YES)

475
cmake/CPM.cmake Normal file
View File

@@ -0,0 +1,475 @@
# CPM.cmake - CMake's missing package manager
# ===========================================
# See https://github.com/TheLartians/CPM.cmake for usage and update instructions.
#
# MIT License
# -----------
#[[
Copyright (c) 2019 Lars Melchior
Permisson is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
set(CURRENT_CPM_VERSION 0.26.2)
if(CPM_DIRECTORY)
if(NOT CPM_DIRECTORY STREQUAL CMAKE_CURRENT_LIST_DIR)
if (CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION)
message(AUTHOR_WARNING "${CPM_INDENT} \
A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \
It is recommended to upgrade CPM to the most recent version. \
See https://github.com/TheLartians/CPM.cmake for more information."
)
endif()
return()
endif()
get_property(CPM_INITIALIZED GLOBAL "" PROPERTY CPM_INITIALIZED SET)
if (CPM_INITIALIZED)
return()
endif()
endif()
set_property(GLOBAL PROPERTY CPM_INITIALIZED true)
option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" $ENV{CPM_USE_LOCAL_PACKAGES})
option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" $ENV{CPM_LOCAL_PACKAGES_ONLY})
option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL})
option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package" $ENV{CPM_DONT_UPDATE_MODULE_PATH})
option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path" $ENV{CPM_DONT_CREATE_PACKAGE_LOCK})
option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK "Add all packages added through CPM.cmake to the package lock" $ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK})
set(CPM_VERSION ${CURRENT_CPM_VERSION} CACHE INTERNAL "")
set(CPM_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "")
set(CPM_FILE ${CMAKE_CURRENT_LIST_FILE} CACHE INTERNAL "")
set(CPM_PACKAGES "" CACHE INTERNAL "")
set(CPM_DRY_RUN OFF CACHE INTERNAL "Don't download or configure dependencies (for testing)")
if(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE})
else()
set(CPM_SOURCE_CACHE_DEFAULT OFF)
endif()
set(CPM_SOURCE_CACHE ${CPM_SOURCE_CACHE_DEFAULT} CACHE PATH "Directory to downlaod CPM dependencies")
if (NOT CPM_DONT_UPDATE_MODULE_PATH)
set(CPM_MODULE_PATH "${CMAKE_BINARY_DIR}/CPM_modules" CACHE INTERNAL "")
# remove old modules
FILE(REMOVE_RECURSE ${CPM_MODULE_PATH})
file(MAKE_DIRECTORY ${CPM_MODULE_PATH})
# locally added CPM modules should override global packages
set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}")
endif()
if (NOT CPM_DONT_CREATE_PACKAGE_LOCK)
set(CPM_PACKAGE_LOCK_FILE "${CMAKE_BINARY_DIR}/cpm-package-lock.cmake" CACHE INTERNAL "")
file(WRITE ${CPM_PACKAGE_LOCK_FILE} "# CPM Package Lock\n# This file should be committed to version control\n\n")
endif()
include(FetchContent)
include(CMakeParseArguments)
# Initialize logging prefix
if(NOT CPM_INDENT)
set(CPM_INDENT "CPM:")
endif()
function(cpm_find_package NAME VERSION)
string(REPLACE " " ";" EXTRA_ARGS "${ARGN}")
find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET)
if(${CPM_ARGS_NAME}_FOUND)
message(STATUS "${CPM_INDENT} using local package ${CPM_ARGS_NAME}@${VERSION}")
CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}")
set(CPM_PACKAGE_FOUND YES PARENT_SCOPE)
else()
set(CPM_PACKAGE_FOUND NO PARENT_SCOPE)
endif()
endfunction()
# Create a custom FindXXX.cmake module for a CPM package
# This prevents `find_package(NAME)` from finding the system library
function(CPMCreateModuleFile Name)
if (NOT CPM_DONT_UPDATE_MODULE_PATH)
# erase any previous modules
FILE(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake "include(${CPM_FILE})\n${ARGN}\nset(${Name}_FOUND TRUE)")
endif()
endfunction()
# Find a package locally or fallback to CPMAddPackage
function(CPMFindPackage)
set(oneValueArgs
NAME
VERSION
FIND_PACKAGE_ARGUMENTS
)
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN})
if (NOT DEFINED CPM_ARGS_VERSION)
if (DEFINED CPM_ARGS_GIT_TAG)
cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION)
endif()
endif()
if (CPM_DOWNLOAD_ALL)
CPMAddPackage(${ARGN})
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
CPMCheckIfPackageAlreadyAdded(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" "${CPM_ARGS_OPTIONS}")
if (CPM_PACKAGE_ALREADY_ADDED)
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS})
if(NOT CPM_PACKAGE_FOUND)
CPMAddPackage(${ARGN})
cpm_export_variables(${CPM_ARGS_NAME})
endif()
endfunction()
# checks if a package has been added before
function(CPMCheckIfPackageAlreadyAdded CPM_ARGS_NAME CPM_ARGS_VERSION CPM_ARGS_OPTIONS)
if ("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES)
CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION)
if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}")
message(WARNING "${CPM_INDENT} requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION}).")
endif()
if (CPM_ARGS_OPTIONS)
foreach(OPTION ${CPM_ARGS_OPTIONS})
cpm_parse_option(${OPTION})
if(NOT "${${OPTION_KEY}}" STREQUAL "${OPTION_VALUE}")
message(WARNING "${CPM_INDENT} ignoring package option for ${CPM_ARGS_NAME}: ${OPTION_KEY} = ${OPTION_VALUE} (${${OPTION_KEY}})")
endif()
endforeach()
endif()
cpm_get_fetch_properties(${CPM_ARGS_NAME})
SET(${CPM_ARGS_NAME}_ADDED NO)
SET(CPM_PACKAGE_ALREADY_ADDED YES PARENT_SCOPE)
cpm_export_variables(${CPM_ARGS_NAME})
else()
SET(CPM_PACKAGE_ALREADY_ADDED NO PARENT_SCOPE)
endif()
endfunction()
# Download and add a package from source
function(CPMAddPackage)
set(oneValueArgs
NAME
FORCE
VERSION
GIT_TAG
DOWNLOAD_ONLY
GITHUB_REPOSITORY
GITLAB_REPOSITORY
GIT_REPOSITORY
SOURCE_DIR
DOWNLOAD_COMMAND
FIND_PACKAGE_ARGUMENTS
)
set(multiValueArgs
OPTIONS
)
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
# Set default values for arguments
if (NOT DEFINED CPM_ARGS_VERSION)
if (DEFINED CPM_ARGS_GIT_TAG)
cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION)
endif()
endif()
if(CPM_ARGS_DOWNLOAD_ONLY)
set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY})
else()
set(DOWNLOAD_ONLY NO)
endif()
if (DEFINED CPM_ARGS_GITHUB_REPOSITORY)
set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git")
endif()
if (DEFINED CPM_ARGS_GITLAB_REPOSITORY)
list(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git")
endif()
if (DEFINED CPM_ARGS_GIT_REPOSITORY)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY})
if (NOT DEFINED CPM_ARGS_GIT_TAG)
set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION})
endif()
endif()
if (DEFINED CPM_ARGS_GIT_TAG)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG})
endif()
# Check if package has been added before
CPMCheckIfPackageAlreadyAdded(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" "${CPM_ARGS_OPTIONS}")
if (CPM_PACKAGE_ALREADY_ADDED)
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
# Check for manual overrides
if (NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "")
set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE})
set(CPM_${CPM_ARGS_NAME}_SOURCE "")
CPMAddPackage(
NAME ${CPM_ARGS_NAME}
SOURCE_DIR ${PACKAGE_SOURCE}
FORCE True
)
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
# Check for available declaration
if (NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "")
set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}})
set(CPM_DECLARATION_${CPM_ARGS_NAME} "")
CPMAddPackage(${declaration})
cpm_export_variables(${CPM_ARGS_NAME})
# checking again to ensure version and option compatibility
CPMCheckIfPackageAlreadyAdded(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" "${CPM_ARGS_OPTIONS}")
return()
endif()
if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY)
cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS})
if(CPM_PACKAGE_FOUND)
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
if(CPM_LOCAL_PACKAGES_ONLY)
message(SEND_ERROR "CPM: ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})")
endif()
endif()
CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}")
if (CPM_ARGS_OPTIONS)
foreach(OPTION ${CPM_ARGS_OPTIONS})
cpm_parse_option(${OPTION})
set(${OPTION_KEY} ${OPTION_VALUE} CACHE INTERNAL "")
endforeach()
endif()
if (DEFINED CPM_ARGS_GIT_TAG)
set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}")
elseif (DEFINED CPM_ARGS_SOURCE_DIR)
set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}")
else()
set(PACKAGE_INFO "${CPM_ARGS_VERSION}")
endif()
if (DEFINED CPM_ARGS_DOWNLOAD_COMMAND)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND})
elseif (DEFINED CPM_ARGS_SOURCE_DIR)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR})
elseif (CPM_SOURCE_CACHE)
string(TOLOWER ${CPM_ARGS_NAME} lower_case_name)
set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS})
list(SORT origin_parameters)
string(SHA1 origin_hash "${origin_parameters}")
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash})
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory})
if (EXISTS ${download_directory})
# disable the download command to allow offline builds
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND "${CMAKE_COMMAND}")
set(PACKAGE_INFO "${download_directory}")
else()
# remove timestamps so CMake will re-download the dependency
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/_deps/${lower_case_name}-subbuild)
set(PACKAGE_INFO "${PACKAGE_INFO} -> ${download_directory}")
endif()
endif()
CPMCreateModuleFile(${CPM_ARGS_NAME} "CPMAddPackage(${ARGN})")
if (CPM_PACKAGE_LOCK_ENABLED)
if ((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK)
cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}")
elseif(CPM_ARGS_SOURCE_DIR)
cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory")
else()
cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}")
endif()
endif()
cpm_declare_fetch("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}" "${PACKAGE_INFO}" "${CPM_ARGS_UNPARSED_ARGUMENTS}")
cpm_fetch_package("${CPM_ARGS_NAME}" "${DOWNLOAD_ONLY}")
cpm_get_fetch_properties("${CPM_ARGS_NAME}")
SET(${CPM_ARGS_NAME}_ADDED YES)
cpm_export_variables("${CPM_ARGS_NAME}")
endfunction()
# Fetch a previously declared package
macro(CPMGetPackage Name)
if (DEFINED "CPM_DECLARATION_${Name}")
CPMAddPackage(
NAME ${Name}
)
else()
message(SEND_ERROR "Cannot retrieve package ${Name}: no declaration available")
endif()
endmacro()
# export variables available to the caller to the parent scope
# expects ${CPM_ARGS_NAME} to be set
macro(cpm_export_variables name)
SET(${name}_SOURCE_DIR "${${name}_SOURCE_DIR}" PARENT_SCOPE)
SET(${name}_BINARY_DIR "${${name}_BINARY_DIR}" PARENT_SCOPE)
SET(${name}_ADDED "${${name}_ADDED}" PARENT_SCOPE)
endmacro()
# declares a package, so that any call to CPMAddPackage for the
# package name will use these arguments instead.
# Previous declarations will not be overriden.
macro(CPMDeclarePackage Name)
if (NOT DEFINED "CPM_DECLARATION_${Name}")
set("CPM_DECLARATION_${Name}" "${ARGN}")
endif()
endmacro()
function(cpm_add_to_package_lock Name)
if (NOT CPM_DONT_CREATE_PACKAGE_LOCK)
file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name} \"${ARGN}\")\n")
endif()
endfunction()
function(cpm_add_comment_to_package_lock Name)
if (NOT CPM_DONT_CREATE_PACKAGE_LOCK)
file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name} (unversioned)\n# CPMDeclarePackage(${Name} \"${ARGN}\")\n")
endif()
endfunction()
# includes the package lock file if it exists and creates a target
# `cpm-write-package-lock` to update it
macro(CPMUsePackageLock file)
if (NOT CPM_DONT_CREATE_PACKAGE_LOCK)
get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE)
if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH})
include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH})
endif()
if (NOT TARGET cpm-update-package-lock)
add_custom_target(cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE} ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH})
endif()
set(CPM_PACKAGE_LOCK_ENABLED true)
endif()
endmacro()
# registers a package that has been added to CPM
function(CPMRegisterPackage PACKAGE VERSION)
list(APPEND CPM_PACKAGES ${PACKAGE})
set(CPM_PACKAGES ${CPM_PACKAGES} CACHE INTERNAL "")
set("CPM_PACKAGE_${PACKAGE}_VERSION" ${VERSION} CACHE INTERNAL "")
endfunction()
# retrieve the current version of the package to ${OUTPUT}
function(CPMGetPackageVersion PACKAGE OUTPUT)
set(${OUTPUT} "${CPM_PACKAGE_${PACKAGE}_VERSION}" PARENT_SCOPE)
endfunction()
# declares a package in FetchContent_Declare
function (cpm_declare_fetch PACKAGE VERSION INFO)
message(STATUS "${CPM_INDENT} adding package ${PACKAGE}@${VERSION} (${INFO})")
if (${CPM_DRY_RUN})
message(STATUS "${CPM_INDENT} package not declared (dry run)")
return()
endif()
FetchContent_Declare(${PACKAGE}
${ARGN}
)
endfunction()
# returns properties for a package previously defined by cpm_declare_fetch
function (cpm_get_fetch_properties PACKAGE)
if (${CPM_DRY_RUN})
return()
endif()
FetchContent_GetProperties(${PACKAGE})
string(TOLOWER ${PACKAGE} lpackage)
SET(${PACKAGE}_SOURCE_DIR "${${lpackage}_SOURCE_DIR}" PARENT_SCOPE)
SET(${PACKAGE}_BINARY_DIR "${${lpackage}_BINARY_DIR}" PARENT_SCOPE)
endfunction()
# downloads a previously declared package via FetchContent
function (cpm_fetch_package PACKAGE DOWNLOAD_ONLY)
if (${CPM_DRY_RUN})
message(STATUS "${CPM_INDENT} package ${PACKAGE} not fetched (dry run)")
return()
endif()
if(DOWNLOAD_ONLY)
FetchContent_GetProperties(${PACKAGE})
if(NOT ${PACKAGE}_POPULATED)
FetchContent_Populate(${PACKAGE})
endif()
else()
set(CPM_OLD_INDENT "${CPM_INDENT}")
set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:")
FetchContent_MakeAvailable(${PACKAGE})
set(CPM_INDENT "${CPM_OLD_INDENT}")
endif()
endfunction()
# splits a package option
function(cpm_parse_option OPTION)
string(REGEX MATCH "^[^ ]+" OPTION_KEY ${OPTION})
string(LENGTH ${OPTION} OPTION_LENGTH)
string(LENGTH ${OPTION_KEY} OPTION_KEY_LENGTH)
if (OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH)
# no value for key provided, assume user wants to set option to "ON"
set(OPTION_VALUE "ON")
else()
math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1")
string(SUBSTRING ${OPTION} "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE)
endif()
set(OPTION_KEY "${OPTION_KEY}" PARENT_SCOPE)
set(OPTION_VALUE "${OPTION_VALUE}" PARENT_SCOPE)
endfunction()
# guesses the package version from a git tag
function(cpm_get_version_from_git_tag GIT_TAG RESULT)
string(LENGTH ${GIT_TAG} length)
if (length EQUAL 40)
# GIT_TAG is probably a git hash
SET(${RESULT} 0 PARENT_SCOPE)
else()
string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG})
SET(${RESULT} ${CMAKE_MATCH_1} PARENT_SCOPE)
endif()
endfunction()

57
cmake/tools.cmake Normal file
View File

@@ -0,0 +1,57 @@
# this file contains a list of tools that can be activated and downloaded on-demand
# each tool is enabled during configuration by passing an additional `-DUSE_<TOOL>=<VALUE>` argument to CMake
# only activate tools for top level project
if (NOT PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
return()
endif()
include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake)
# enables sanitizers support using the the `USE_SANITIZER` flag
# available values are: Address, Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined'
if (USE_SANITIZER OR USE_STATIC_ANALYZER)
CPMAddPackage(
NAME StableCoder-cmake-scripts
GITHUB_REPOSITORY StableCoder/cmake-scripts
GIT_TAG 3d2d5a9fb26f0ce24e3e4eaeeff686ec2ecfb3fb
)
if (USE_SANITIZER)
include(${StableCoder-cmake-scripts_SOURCE_DIR}/sanitizers.cmake)
endif()
if (USE_STATIC_ANALYZER)
if ("clang-tidy" IN_LIST USE_STATIC_ANALYZER)
SET(CLANG_TIDY ON CACHE INTERNAL "")
else()
SET(CLANG_TIDY OFF CACHE INTERNAL "")
endif()
if ("iwyu" IN_LIST USE_STATIC_ANALYZER)
SET(IWYU ON CACHE INTERNAL "")
else()
SET(IWYU OFF CACHE INTERNAL "")
endif()
if ("cppcheck" IN_LIST USE_STATIC_ANALYZER)
SET(CPPCHECK ON CACHE INTERNAL "")
else()
SET(CPPCHECK OFF CACHE INTERNAL "")
endif()
include(${StableCoder-cmake-scripts_SOURCE_DIR}/tools.cmake)
clang_tidy(${CLANG_TIDY_ARGS})
include_what_you_use(${IWYU_ARGS})
cppcheck(${CPPCHECK_ARGS})
endif()
endif()
# enables CCACHE support through the USE_CCACHE flag
# possible values are: YES, NO or equivalent
if (USE_CCACHE)
CPMAddPackage(
NAME Ccache.cmake
GITHUB_REPOSITORY TheLartians/Ccache.cmake
VERSION 1.1
)
endif()

71
ecs-lua/CMakeLists.txt Normal file
View File

@@ -0,0 +1,71 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(ecs-lua
LANGUAGES CXX
)
include(../cmake/CPM.cmake)
CPMAddPackage(
NAME PackageProject.cmake
GITHUB_REPOSITORY TheLartians/PackageProject.cmake
VERSION 1.3
)
CPMAddPackage(
NAME lua
GITHUB_REPOSITORY lua/lua
VERSION 5.3.5
DOWNLOAD_ONLY YES
)
if (lua_ADDED)
# lua has no CMake support, so we create our own target
FILE(GLOB lua_sources ${lua_SOURCE_DIR}/*.c)
add_library(lua STATIC ${lua_sources})
set_source_files_properties(${lua_sources} PROPERTIES LANGUAGE CXX)
target_include_directories(lua
PUBLIC
$<BUILD_INTERFACE:${lua_SOURCE_DIR}>
)
endif()
CPMAddPackage(
NAME sol2
GIT_REPOSITORY https://github.com/ThePhD/sol2
VERSION 3.2.1
DOWNLOAD_ONLY YES
)
if (sol2_ADDED)
add_library(sol2 INTERFACE IMPORTED)
target_include_directories(sol2 INTERFACE ${sol2_SOURCE_DIR}/single/include)
target_link_libraries(sol2 INTERFACE lua)
target_compile_definitions(sol2 INTERFACE SOL_USING_CXX_LUA=1)
endif()
file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
file(GLOB_RECURSE sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_library(${PROJECT_NAME} ${headers} ${sources})
# being a cross-platform target, we enforce standards conformance on MSVC
target_compile_options(${PROJECT_NAME} PUBLIC "$<$<BOOL:${MSVC}>:/permissive->")
# Link dependencies (if required)
target_link_libraries(${PROJECT_NAME} PUBLIC ecs ecs-serial sol2)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 20
OUTPUT_NAME "${PROJECT_NAME}"
)
string(TOLOWER ${PROJECT_NAME}/version.h VERSION_HEADER_LOCATION)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>
)

View File

@@ -1,16 +1,16 @@
#pragma once #pragma once
#include "sol.hpp" #include <limits>
#include "sol/sol.hpp"
#include "ecs.h" #include "ecs.h"
#include <iostream> #include <iostream>
#include <utility> #include <utility>
namespace ecs::lua { namespace ecs::lua {
struct Wrapper : Component { struct LuaComponent : Component {
// @todo Figure out a more elegant way // @todo Figure out a more elegant way
Wrapper(sol::table _table) : Component(ComponentID::id<Wrapper>), table(_table) {} LuaComponent() {}
Wrapper() : Component(ComponentID::id<Wrapper>) {}
sol::table table; sol::table table;
}; };

View File

@@ -2,6 +2,10 @@
#include "ecs.h" #include "ecs.h"
// #ifdef _OPTIONAL_ECS_SERIAL
#include "ecs-serial.h"
// #endif
namespace sol { namespace sol {
template<> template<>
struct is_container<ecs::View<>> : std::true_type {}; struct is_container<ecs::View<>> : std::true_type {};
@@ -18,31 +22,24 @@ namespace ecs::lua {
"__tostring", [] (Entity* thiz) { "__tostring", [] (Entity* thiz) {
return uuids::to_string(thiz->uuid); return uuids::to_string(thiz->uuid);
}, },
"add_component", [] (Entity* thiz, Component* component, std::string name) { "add_component", [] (Entity* thiz, size_t id, Component* component) {
return thiz->add_component(ComponentID::get_id({name})[0], component); return thiz->add_component(id, component);
}, },
"has_components", [] (Entity* thiz, sol::variadic_args args) { "has_components", [] (Entity* thiz, sol::variadic_args args) {
std::vector<std::string> names; std::vector<size_t> ids;
for (std::string name : args) { for (sol::table table : args) {
names.push_back(name); // @todo Check that the table has id and convert()
ids.push_back(table["_id"]);
} }
return thiz->has_components(ComponentID::get_id(names)); return thiz->has_components(ids);
}, },
"get_component", [&lua] (Entity* thiz, std::string name) -> sol::object { "get_component", [&lua] (Entity* thiz, sol::table table) -> sol::object {
// Convert to the correct component type // @todo Check that the table has id and convert()
Component* component = thiz->get_component(ComponentID::get_id({name})[0]);
if (!component->_runtime) { Component* component = thiz->get_component(table["_id"]);
// @todo Figure out a more elegant way to convert auto f1 = table["_convert"];
auto f1 = lua["_internal_to_" + name]; if (f1.valid()) {
if (f1.valid()) { return f1(component);
return f1(component);
}
} else {
// @todo This call is not very fast...
auto f1 = lua["_internal_to_" + ComponentID::get_name(component->_id)];
if (f1.valid()) {
return f1(component);
}
} }
throw std::runtime_error("Unknown component"); throw std::runtime_error("Unknown component");
} }
@@ -57,6 +54,7 @@ namespace ecs::lua {
// @todo Allow to construct with given uuid // @todo Allow to construct with given uuid
return thiz->create_entity(); return thiz->create_entity();
}, },
"remove_entity", &Manager::remove_entity,
"has_entity", [] (Manager* thiz, std::string uuid) { "has_entity", [] (Manager* thiz, std::string uuid) {
// @todo Check if valid // @todo Check if valid
return thiz->has_entity(uuids::uuid::from_string(uuid).value()); return thiz->has_entity(uuids::uuid::from_string(uuid).value());
@@ -66,37 +64,162 @@ namespace ecs::lua {
return thiz->get_entity(uuids::uuid::from_string(uuid).value()); return thiz->get_entity(uuids::uuid::from_string(uuid).value());
}, },
"view", [] (Manager* thiz, sol::variadic_args args) { "view", [] (Manager* thiz, sol::variadic_args args) {
std::vector<std::string> names; std::vector<size_t> ids;
for (std::string name : args) { for (sol::table table : args) {
names.push_back(name); // @todo Check that the table has id and convert()
ids.push_back(table["_id"]);
} }
return thiz->view(ComponentID::get_id(names)); return thiz->view(ids);
} }
); );
return ecs; return ecs;
}; };
preload["ecs.Wrapper"] = [&lua] { preload["ecs.LuaComponent"] = [&lua] {
sol::table component = lua.create_table(); // @todo Do we want to register this into the global namespace?
component.new_usertype<Wrapper>("Wrapper", lua.new_usertype<LuaComponent>("LuaComponent",
"__index", [] (Wrapper* thiz, std::string key) { "__index", [] (LuaComponent* thiz, std::string key) {
return thiz->table[key]; return thiz->table[key];
}, },
"__newindex", [] (Wrapper* thiz, std::string key, sol::object value) { "__newindex", [] (LuaComponent* thiz, std::string key, sol::object value) {
thiz->table[key] = value; thiz->table[key] = value;
} },
sol::base_classes, sol::bases<ecs::Component>()
); );
component.set_function("new", sol::factories([](std::string _name, sol::table _table) { sol::table comp = lua.create_table();
return std::make_pair(new Wrapper(_table), _name); comp.set_function("create", [&lua] (std::string name, sol::table table) {
})); std::cout << "Creating new component: " << name << '\n';
size_t id = ComponentID::add(name);
std::cout << "ID: " << id << '\n';
lua.set_function("_internal_to_Wrapper", [] (ecs::Component* component) { std::unordered_map<std::string, sol::type> map;
return (Wrapper*)component;
for (auto [skey, stype] : table) {
std::string key = skey.as<std::string>();
std::string type = stype.as<std::string>();
if (!type.compare("string")) {
map.insert({key, sol::type::string});
} else if (!type.compare("number")) {
map.insert({key, sol::type::number});
} else if (!type.compare("boolean")) {
map.insert({key, sol::type::boolean});
}
}
// @todo We need to make this a cmake flag
// #ifdef _OPTIONAL_ECS_SERIAL
auto serialize = [map, name] (std::ostream& os, ecs::Component* component) {
LuaComponent* wrapper = (LuaComponent*)component;
// Write each of the entries
io::write<size_t>(os, map.size());
for (auto [key, type] : map) {
io::write<std::string>(os, key);
switch (type) {
case sol::type::string:
io::write<std::string>(os, wrapper->table.get<std::string>(key));
break;
case sol::type::number:
io::write<float>(os, wrapper->table.get<float>(key));
break;
case sol::type::boolean:
io::write<bool>(os, wrapper->table.get<bool>(key));
break;
default:
break;
}
}
};
auto deserialize = [map] (std::istream& is, Component* component) {
LuaComponent* wrapper = (LuaComponent*)component;
// Write each of the entries
size_t size = io::read<size_t>(is);
for (size_t i = 0; i < size; ++i) {
std::string key = io::read<std::string>(is);
const auto it = map.find(key);
sol::type type = it->second;
std::cout << key << '\n';
switch (type) {
case sol::type::string:
wrapper->table[key] = io::read<std::string>(is);
break;
case sol::type::number:
wrapper->table[key] = io::read<float>(is);
break;
case sol::type::boolean:
wrapper->table[key] = io::read<bool>(is);
break;
default:
break;
}
}
};
auto create = [&lua, map] {
LuaComponent* wrapper = new LuaComponent();
wrapper->table = lua.create_table();
for (auto [key, type] : map) {
switch (type) {
case sol::type::string:
wrapper->table[key] = "";
break;
case sol::type::number:
wrapper->table[key] = 0;
break;
case sol::type::boolean:
wrapper->table[key] = true;
break;
default:
break;
}
}
return wrapper;
};
ecs::serial::register_component_custom(id, serialize, deserialize, create);
// #endif
sol::table component = lua.create_table();
component["_id"] = id;
component.set_function("new", sol::factories([&lua, id, map] (sol::table table) {
LuaComponent* wrapper = new LuaComponent();
wrapper->table = lua.create_table();
for (auto [key, type] : map) {
auto temp = table[key];
wrapper->table[key] = temp;
}
return std::make_pair(id, wrapper);
}));
component.set_function("_convert", [] (ecs::Component* component) {
return static_cast<LuaComponent*>(component);
});
return component;
}); });
return component; return comp;
}; };
} }
} }

43
ecs-serial/CMakeLists.txt Normal file
View File

@@ -0,0 +1,43 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(ecs-serial
LANGUAGES CXX
)
include(../cmake/CPM.cmake)
CPMAddPackage(
NAME PackageProject.cmake
GITHUB_REPOSITORY TheLartians/PackageProject.cmake
VERSION 1.3
)
CPMAddPackage(
NAME io
GIT_REPOSITORY https://git.mtgames.nl/Dreaded_X/io
GIT_TAG master
)
file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
file(GLOB_RECURSE sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_library(${PROJECT_NAME} ${headers} ${sources})
# being a cross-platform target, we enforce standards conformance on MSVC
target_compile_options(${PROJECT_NAME} PUBLIC "$<$<BOOL:${MSVC}>:/permissive->")
# Link dependencies (if required)
target_link_libraries(${PROJECT_NAME} PUBLIC ecs io)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 20
OUTPUT_NAME "${PROJECT_NAME}"
)
string(TOLOWER ${PROJECT_NAME}/version.h VERSION_HEADER_LOCATION)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>
)

View File

@@ -1,10 +1,10 @@
#pragma once
#include <functional> #include <functional>
#include <ostream> #include <ostream>
#pragma once
#include "ecs.h" #include "ecs.h"
#include "iohelper/write.h" #include "io/write.h"
#include "iohelper/read.h" #include "io/read.h"
#include <iostream> #include <iostream>
@@ -20,7 +20,7 @@ namespace ecs::serial {
template <typename T, typename M, typename... Args> template <typename T, typename M, typename... Args>
void serialize_member(std::ostream& os, T* t, M T::*m, Args... args) { void serialize_member(std::ostream& os, T* t, M T::*m, Args... args) {
iohelper::write<M>(os, t->*m); io::write<M>(os, t->*m);
serialize_member(os, t, args...); serialize_member(os, t, args...);
} }
@@ -32,7 +32,7 @@ namespace ecs::serial {
template <typename T, typename M, typename... Args> template <typename T, typename M, typename... Args>
void deserialize_member(std::istream& is, T* t, M T::*m, Args... args) { void deserialize_member(std::istream& is, T* t, M T::*m, Args... args) {
t->*m = iohelper::read<M>(is); t->*m = io::read<M>(is);
deserialize_member(is, t, args...); deserialize_member(is, t, args...);
} }
@@ -41,32 +41,25 @@ namespace ecs::serial {
template <typename T, typename... Args> template <typename T, typename... Args>
void register_component(Args... args) { void register_component(Args... args) {
// Serialize component // Serialize component
auto func1 = [args...] (std::ostream& os, ecs::Component* component) { auto serialize = [args...] (std::ostream& os, ecs::Component* component) {
T* t = (T*)component; T* t = static_cast<T*>(component);
serialize_member(os, t, args...); serialize_member(os, t, args...);
}; };
// Deserialize component // Deserialize component
auto func2 = [args...] (std::istream& is, ecs::Component* component) { auto deserialize = [args...] (std::istream& is, ecs::Component* component) {
T* t = (T*)component; T* t = static_cast<T*>(component);
deserialize_member(is, t, args...); deserialize_member(is, t, args...);
}; };
auto func3 = [] () -> Component* { auto create = [] () -> Component* {
return new T(); return new T();
}; };
internal::functions.insert({ComponentID::id<T>, {func1, func2, func3}}); internal::functions.insert({ComponentID::id<T>, {serialize, deserialize, create}});
} }
template <typename T> void register_component_custom(size_t id, std::function<void(std::ostream&, ecs::Component*)> serialize, std::function<void(std::istream&, Component*)> deserialize, std::function<ecs::Component*()> create);
void register_component_custom(std::function<void(std::ostream&, ecs::Component*)> func1, std::function<void(std::istream&, Component*)> func2) {
auto func3 = [] () -> Component* {
return new T();
};
internal::functions.insert({ComponentID::id<T>, {func1, func2, func3}});
}
void serialize(std::ostream& os, Entity* entity); void serialize(std::ostream& os, Entity* entity);
void deserialize(std::istream& is, Manager& manager); void deserialize(std::istream& is, Manager& manager);

View File

@@ -1,7 +1,5 @@
#include "ecs-serial.h" #include "ecs-serial.h"
#include "/home/tim/Projects/cpp/iohelper/iohelper/include/iohelper/read.h"
#include "/home/tim/Projects/cpp/iohelper/iohelper/include/iohelper/write.h"
#include "ecs.h" #include "ecs.h"
#include <ostream> #include <ostream>
@@ -14,41 +12,31 @@ namespace ecs::serial {
std::unordered_map<size_t, size_t> conversion; std::unordered_map<size_t, size_t> conversion;
void register_component_custom(size_t id, std::function<void(std::ostream&, ecs::Component*)> serialize, std::function<void(std::istream&, Component*)> deserialize, std::function<ecs::Component*()> create) {
internal::functions.insert({id, {serialize, deserialize, create}});
}
void serialize(std::ostream& os, Entity* entity) { void serialize(std::ostream& os, Entity* entity) {
auto uuid_data = reinterpret_cast<const uint8_t*>(entity->uuid.as_bytes().data()); auto uuid_data = reinterpret_cast<const uint8_t*>(entity->uuid.as_bytes().data());
iohelper::write_vector<uint8_t>(os, std::vector<uint8_t>(uuid_data, uuid_data + 16), false); io::write<std::vector<uint8_t>>(os, std::vector<uint8_t>(uuid_data, uuid_data + 16), false);
auto components = entity->get_components(); auto components = entity->get_components();
iohelper::write_length(os, components.size()); io::write<size_t>(os, components.size());
for (auto [id, component] : components) { for (auto [id, component] : components) {
if (!component->_runtime) { auto functions = internal::functions.find(id);
auto functions = internal::functions.find(id); if (functions == internal::functions.end()) {
if (functions == internal::functions.end()) { std::cerr << "ID: " << id << '\n';
throw std::runtime_error("No known serializer for id"); throw std::runtime_error("No known serializer for id");
}
iohelper::write_length(os, id);
iohelper::write<bool>(os, false);
std::get<0>(internal::functions[id])(os, component);
} else {
auto new_id = component->_id;
auto functions = internal::functions.find(new_id);
if (functions == internal::functions.end()) {
throw std::runtime_error("No known serializer for id");
}
iohelper::write_length(os, new_id);
iohelper::write<bool>(os, true);
iohelper::write_length(os, id);
std::get<0>(internal::functions[new_id])(os, component);
} }
io::write<size_t>(os, id);
std::get<0>(internal::functions[id])(os, component);
} }
} }
void deserialize(std::istream& is, Manager& manager) { void deserialize(std::istream& is, Manager& manager) {
auto uuid_vector = iohelper::read_vector<uint8_t>(is, 16); auto uuid_array = io::read<std::array<uint8_t, 16>>(is);
uuids::uuid uuid(uuid_vector.begin(), uuid_vector.end()); uuids::uuid uuid(uuid_array.begin(), uuid_array.end());
Entity* entity = nullptr; Entity* entity = nullptr;
if (manager.has_entity(uuid)) { if (manager.has_entity(uuid)) {
@@ -59,15 +47,10 @@ namespace ecs::serial {
entity = manager.create_entity(uuid); entity = manager.create_entity(uuid);
} }
size_t component_count = iohelper::read_length(is); size_t component_count = io::read<size_t>(is);
// std::cout << "Updating " << component_count << " components in entity: " << uuid << '\n'; // std::cout << "Updating " << component_count << " components in entity: " << uuid << '\n';
for (size_t i = 0; i < component_count; ++i) { for (size_t i = 0; i < component_count; ++i) {
size_t new_id = conversion[iohelper::read_length(is)]; size_t id = conversion[io::read<size_t>(is)];
bool runtime = iohelper::read<bool>(is);
size_t id = new_id;
if (runtime) {
id = conversion[iohelper::read_length(is)];
}
// @todo We also need to be able to remove components // @todo We also need to be able to remove components
// Sending a component with length 0 -> remove component // Sending a component with length 0 -> remove component
@@ -77,22 +60,14 @@ namespace ecs::serial {
// @todo What if the function does not exist? // @todo What if the function does not exist?
if (entity->has_components({id})) { if (entity->has_components({id})) {
// Update the component // Update the component
std::cout << "Updating component: " << id << " [" << ComponentID::get_name(id) << "]"; std::cout << "Updating component: " << id << " [" << ComponentID::reverse_resolve(id) << "]\n";
if (new_id != id) {
std::cout << " (base: " << new_id << " [" << ComponentID::get_name(new_id) << "])";
}
std::cout << '\n';
Component* component = entity->get_component(id); Component* component = entity->get_component(id);
// @note We do not have to check if this exists as the entity already has the component // @note We do not have to check if this exists as the entity already has the component
std::get<1>(internal::functions[new_id])(is, component); std::get<1>(internal::functions[id])(is, component);
} else { } else {
// Add new component // Add new component
std::cout << "Adding component: " << id << " [" << ComponentID::get_name(id) << "]"; std::cout << "Adding component: " << id << " [" << ComponentID::reverse_resolve(id) << "]\n";
if (new_id != id) { auto func = internal::functions.find(id);
std::cout << " (base: " << new_id << " [" << ComponentID::get_name(new_id) << "])";
}
std::cout << '\n';
auto func = internal::functions.find(new_id);
if (func == internal::functions.end()) { if (func == internal::functions.end()) {
throw std::runtime_error("No known serializers for component"); throw std::runtime_error("No known serializers for component");
} }
@@ -104,22 +79,22 @@ namespace ecs::serial {
} }
void serialize_ids(std::ostream& os) { void serialize_ids(std::ostream& os) {
auto& ids = ComponentID::_ids(); auto& ids = ComponentID::get_map();
iohelper::write_length(os, ids.size()); io::write<size_t>(os, ids.size());
for (auto& [name, id] : ids) { for (auto& [name, id] : ids) {
iohelper::write<std::string>(os, name); io::write<std::string>(os, name);
iohelper::write_length(os, id); io::write<size_t>(os, id);
} }
} }
void deserialize_ids(std::istream& is) { void deserialize_ids(std::istream& is) {
size_t id_count = iohelper::read_length(is); size_t id_count = io::read<size_t>(is);
for (size_t i = 0; i < id_count; ++i) { for (size_t i = 0; i < id_count; ++i) {
std::string name = iohelper::read<std::string>(is); std::string name = io::read<std::string>(is);
size_t id = iohelper::read_length(is); size_t id = io::read<size_t>(is);
conversion[id] = ComponentID::get_id({name})[0]; conversion[id] = ComponentID::resolve(name);
} }
for (auto [remote_id, local_id] : conversion) { for (auto [remote_id, local_id] : conversion) {

83
ecs/CMakeLists.txt Normal file
View File

@@ -0,0 +1,83 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(ecs
LANGUAGES CXX
)
include(../cmake/CPM.cmake)
CPMAddPackage(
NAME PackageProject.cmake
GITHUB_REPOSITORY TheLartians/PackageProject.cmake
VERSION 1.3
)
CPMAddPackage(
NAME stduuid
GITHUB_REPOSITORY mariusbancila/stduuid
GIT_TAG master
DOWNLOAD_ONLY YES
)
if (stduuid_ADDED)
add_library(stduuid INTERFACE)
set_target_properties(stduuid PROPERTIES INTERFACE_COMPILE_FEATURES cxx_std_20)
target_include_directories(stduuid INTERFACE ${stduuid_SOURCE_DIR}/include)
if(WIN32)
elseif(APPLE)
find_library(CFLIB CoreFoundation)
target_link_libraries(stduuid INTERFACE ${CFLIB})
else()
FIND_PATH(LIBUUID_INCLUDE_DIRS uuid/uuid.h)
FIND_LIBRARY(LIBUUID_LIBRARIES uuid)
IF (LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS)
SET(LIBUUID_FOUND 1)
IF (NOT LibUuid_FIND_QUIETLY)
MESSAGE(STATUS "Found libuuid: ${LIBUUID_LIBRARIES}")
ENDIF ( NOT LibUuid_FIND_QUIETLY )
ELSE (LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS)
IF (LibUuid_FIND_REQUIRED)
MESSAGE(SEND_ERROR "Could NOT find libuuid")
ELSE (LibUuid_FIND_REQUIRED)
IF (NOT LIBUUID_FIND_QUIETLY)
MESSAGE(STATUS "Could NOT find libuuid")
ENDIF (NOT LIBUUID_FIND_QUIETLY)
ENDIF (LibUuid_FIND_REQUIRED)
ENDIF (LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS)
MARK_AS_ADVANCED(LIBUUID_LIBRARIES LIBUUID_INCLUDE_DIRS)
message("${LIBUUID_LIBRARIES}")
message("${LIBUUID_INCLUDE_DIRS}")
if (LIBUUID_FOUND)
target_include_directories(stduuid INTERFACE ${LIBUUID_INCLUDE_DIRS})
target_link_libraries(stduuid INTERFACE ${LIBUUID_LIBRARIES})
endif()
endif()
endif()
file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
file(GLOB_RECURSE sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_library(${PROJECT_NAME} ${headers} ${sources})
# being a cross-platform target, we enforce standards conformance on MSVC
target_compile_options(${PROJECT_NAME} PUBLIC "$<$<BOOL:${MSVC}>:/permissive->")
# Link dependencies (if required)
target_link_libraries(${PROJECT_NAME} PUBLIC stduuid)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 20
OUTPUT_NAME "${PROJECT_NAME}"
)
string(TOLOWER ${PROJECT_NAME}/version.h VERSION_HEADER_LOCATION)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>
)

View File

@@ -4,68 +4,80 @@
#include <functional> #include <functional>
#include <algorithm> #include <algorithm>
#include <list> #include <list>
#include <vector>
#include <iostream>
#include <typeinfo> #define UUID_SYSTEM_GENERATOR
#if __has_include(<cxxabi.h>)
#include <cxxabi.h>
#endif
#include "uuid.h" #include "uuid.h"
namespace ecs { namespace ecs {
template <typename T> // @todo Redo the component id system to require registering the components
std::string get_typename() { // This would hopefully clear up some of the jank related to this part of the codebase (No weird lookups)
#if __has_include(<cxxabi.h>) // It would also make runtime generated / meta components much easier
std::string name = abi::__cxa_demangle(typeid(T).name(), 0, 0, 0);
#else
std::string name = typeid(T).name();
// This is really janky
name = name.substr(name.find(' ')+1);
#endif
auto index = name.find_last_of(':');
if (index != std::string::npos) {
name = name.substr(index+1);
}
return name;
}
class ComponentID { class ComponentID {
private: private:
static size_t _id; static size_t next_id;
static std::unordered_map<std::string, size_t> _ids;
public: public:
// This needs to be a function because otherwise it might not be initialized on time template <typename T>
static std::unordered_map<std::string, size_t>& _ids(); static size_t add(std::string name) {
static std::vector<size_t> get_id(std::vector<std::string> names); _ids.insert({name, id<T>});
// @todo Do some other register stuff
return id<T>;
}
// @todo This is slow... static size_t add(std::string name) {
static std::string get_name(size_t id) { size_t id = next_id++;
for (auto it = _ids().begin(); it != _ids().end(); ++it) { _ids.insert({name, id});
if (it->second == id) { return id;
return it->first;
}
}
throw std::runtime_error("ID does not exist");
} }
// This looks kind of ugly // This looks kind of ugly
template <typename T> template <typename T>
inline static const size_t id = get_id({get_typename<T>()})[0]; inline static const size_t id = next_id++;
static size_t resolve(std::string name) {
auto it = _ids.find(name);
if (it == _ids.end()) {
return 0;
}
return it->second;
}
static std::vector<size_t> resolve(std::vector<std::string> names) {
std::vector<size_t> ids;
for (const auto& name : names) {
ids.push_back(resolve(name));
}
return ids;
}
static std::string reverse_resolve(size_t id) {
for (auto& [name, _id] : _ids) {
if (id == _id) {
return name;
}
}
return "";
}
static const auto& get_map() {
return _ids;
}
}; };
struct Component { struct Component {
Component() : _runtime(false), _id(0) {} Component() {}
// @todo Would be nice if this worked with templates
Component(size_t id) : _runtime(true), _id(id) {}
virtual ~Component() {} virtual ~Component() {}
//
const bool _runtime; // @todo Store parent entity and also make a vector for all components of this type
const size_t _id; // that way we can do some nice stuff without having to search the entities
// For example we could loop over all sprite components and then draw them (using parent to find location)
}; };
class Entity { class Entity {
@@ -73,52 +85,41 @@ namespace ecs {
Entity(uuids::uuid _uuid) : uuid(_uuid) {} Entity(uuids::uuid _uuid) : uuid(_uuid) {}
~Entity(); ~Entity();
Component* add_component(size_t id, Component* component);
bool has_components(std::vector<size_t> ids) const;
Component* get_component(size_t id) const;
template <typename T, typename... Args> template <typename T, typename... Args>
void add_component(Args... args) { T* add_component(Args... args) {
size_t id = ComponentID::id<T>; size_t id = ComponentID::id<T>;
T* component = new T(args...);
if (_components.find(id) != _components.end()) { return static_cast<T*>(add_component(id, component));
throw std::runtime_error("Component already exists");
}
_components[id] = new T(args...);
} }
template <typename... Ts> template <typename... Ts>
bool has_components() const { bool has_components() const {
std::vector<size_t> ids = {ComponentID::id<Ts>...}; std::vector<size_t> ids = {ComponentID::id<Ts>...};
for (const auto& id : ids) {
if (_components.find(id) == _components.end()) {
return false;
}
}
return true; return has_components(ids);
} }
template <typename T> template <typename T>
T* get_component() const { T* get_component() const {
size_t id = ComponentID::id<T>; size_t id = ComponentID::id<T>;
auto it = _components.find(id);
if (it == _components.end()) {
throw std::runtime_error("Component does not exist");
}
return (T*)it->second; return static_cast<T*>(get_component(id));
} }
const std::unordered_map<size_t, Component*> get_components() const { const std::unordered_map<size_t, Component*> get_components() const {
return _components; return _components;
} }
void add_component(size_t id, Component* component);
bool has_components(std::vector<size_t> ids);
Component* get_component(size_t id);
const uuids::uuid uuid; const uuids::uuid uuid;
private: private:
std::unordered_map<size_t, Component*> _components; std::unordered_map<size_t, Component*> _components;
// @todo Store manager, this allows the entity to register components to the manager
}; };
template <typename... Ts> template <typename... Ts>
@@ -133,34 +134,106 @@ namespace ecs {
} }
} }
class ViewIterator;
size_t size() const { size_t size() const {
return _entities.size(); return _entities.size();
} }
auto begin() { auto begin() {
return _entities.begin(); return ViewIterator(_entities.begin());
} }
auto begin() const { auto begin() const {
return _entities.begin(); return ConstViewIterator(_entities.begin());
} }
auto cbegin() const { auto cbegin() const {
return _entities.cbegin(); return ConstViewIterator(_entities.cbegin());
} }
auto end() { auto end() {
return _entities.end(); return ViewIterator(_entities.end());
} }
auto end() const { auto end() const {
return _entities.end(); return ConstViewIterator(_entities.end());
} }
auto cend() const { auto cend() const {
return _entities.cend(); return ConstViewIterator(_entities.cend());
} }
class ViewIterator {
public:
using Iiterator = std::unordered_map<uuids::uuid, Entity*>::iterator;
ViewIterator(Iiterator it) : it(it) {}
ViewIterator& operator=(Iiterator it) {
this->it = it;
return *this;
}
ViewIterator& operator++() {
it++;
return *this;
}
ViewIterator operator++(int) {
ViewIterator iterator = *this;
++*this;
return iterator;
}
bool operator!=(const ViewIterator& other) const {
return it != other.it;
}
auto operator*() {
return std::make_tuple(it->second, it->second->get_component<Ts>()...);
}
private:
Iiterator it;
};
class ConstViewIterator {
public:
using Iiterator = std::unordered_map<uuids::uuid, Entity*>::const_iterator;
ConstViewIterator(Iiterator it) : it(it) {}
ConstViewIterator& operator=(Iiterator it) {
this->it = it;
return *this;
}
ConstViewIterator& operator++() {
it++;
return *this;
}
ConstViewIterator operator++(int) {
ViewIterator iterator = *this;
++*this;
return iterator;
}
bool operator!=(const ViewIterator& other) const {
return it != other.it;
}
const auto operator*() const {
return std::make_tuple(it->second, it->second->get_component<Ts>()...);
}
private:
Iiterator it;
};
private: private:
std::unordered_map<uuids::uuid, Entity*> _entities; std::unordered_map<uuids::uuid, Entity*> _entities;
}; };
@@ -198,6 +271,17 @@ namespace ecs {
Entity* create_entity(uuids::uuid uuid); Entity* create_entity(uuids::uuid uuid);
Entity* create_entity(); Entity* create_entity();
template <typename T, typename... Args>
T* create_entity(Args... args) {
uuids::uuid uuid = uuids::uuid_system_generator{}();
T* entity = new T(uuid, args...);
_entities.insert({uuid, entity});
return entity;
}
void remove_entity(ecs::Entity* entity);
bool has_entity(uuids::uuid uuid) { bool has_entity(uuids::uuid uuid) {
// @todo c++20 has .contains() // @todo c++20 has .contains()
return _entities.count(uuid); return _entities.count(uuid);

View File

@@ -3,29 +3,8 @@
#include <iostream> #include <iostream>
namespace ecs { namespace ecs {
size_t ComponentID::_id; size_t ComponentID::next_id = 1;
// This needs to be a function because otherwise it might not be initialized on time std::unordered_map<std::string, size_t> ComponentID::_ids;
std::unordered_map<std::string, size_t>& ComponentID::_ids() {
static std::unordered_map<std::string, size_t> ids;
return ids;
};
std::vector<size_t> ComponentID::get_id(std::vector<std::string> names) {
std::vector<size_t> ids;
for (const auto& name : names) {
auto it = _ids().find(name);
if (it != _ids().end()) {
ids.push_back(it->second);
} else {
size_t id = _id++;
_ids()[name] = id;
ids.push_back(id);
}
}
return ids;
}
Entity::~Entity() { Entity::~Entity() {
// @todo This does not work... // @todo This does not work...
@@ -36,15 +15,20 @@ namespace ecs {
_components.clear(); _components.clear();
} }
void Entity::add_component(size_t id, Component* component) { Component* Entity::add_component(size_t id, Component* component) {
if (id == 0) {
throw std::runtime_error("Unknown component!");
}
if (_components.find(id) != _components.end()) { if (_components.find(id) != _components.end()) {
throw std::runtime_error("Component already exists"); throw std::runtime_error("Component already exists");
} }
_components[id] = component; _components[id] = component;
return component;
} }
bool Entity::has_components(std::vector<size_t> ids) { bool Entity::has_components(std::vector<size_t> ids) const {
for (const auto& id : ids) { for (const auto& id : ids) {
if (_components.find(id) == _components.end()) { if (_components.find(id) == _components.end()) {
return false; return false;
@@ -54,10 +38,10 @@ namespace ecs {
return true; return true;
} }
Component* Entity::get_component(size_t id) { Component* Entity::get_component(size_t id) const {
auto it = _components.find(id); auto it = _components.find(id);
if (it == _components.end()) { if (it == _components.end()) {
throw std::runtime_error("Component does not exist"); return nullptr;
} }
return it->second; return it->second;
@@ -77,6 +61,16 @@ namespace ecs {
return create_entity(uuid); return create_entity(uuid);
} }
void Manager::remove_entity(ecs::Entity* entity) {
auto it = _entities.find(entity->uuid);
if (it == _entities.end()) {
throw std::runtime_error("Entity with uuid does not exists");
}
delete it->second;
_entities.erase(it);
}
Entity* Manager::create_entity(uuids::uuid uuid) { Entity* Manager::create_entity(uuids::uuid uuid) {
Entity* entity = new Entity(uuid); Entity* entity = new Entity(uuid);
_entities.insert({uuid, entity}); _entities.insert({uuid, entity});

View File

@@ -1,73 +0,0 @@
lib "lua"
src("*vendor/lua", "-vendor/lua/lua.c")
include "vendor/lua"
include "vendor/headers"
warnings(false)
lib "sol2"
include "vendor/sol2"
dependency "lua"
lib "stduuid"
include "vendor/stduuid/include"
link "uuid"
lib "ecs"
path "ecs"
dependency("sol2", "stduuid")
lib "ecs-lua"
path "ecs-lua"
dependency "ecs"
subfile("../iohelper/flint.lua", "iohelper")
lib "ecs-serial"
path "ecs-serial"
dependency("ecs", "iohelper")
function codegen(path, file)
local handle = io.popen("mkdir " .. config.paths.build .. "/generated")
handle:close()
local command = "python test.py " .. path .. "/" .. file .. " > " .. config.paths.build .. "/generated/ecs_" .. file
handle = io.popen(command)
handle:close()
end
executable "test"
path "test"
dependency "ecs-lua"
dependency "ecs-serial"
hook(step.PRE_BUILD, codegen, "test/include", "components.h")
include(config.paths.build .. "/generated/")
lib "network-shared"
path "network-shared"
dependency "ecs-serial"
hook(step.PRE_BUILD, codegen, "network-shared/include", "network_components.h")
include(config.paths.build .. "/generated/")
executable "server"
path "network-server"
dependency "network-shared"
dependency "iohelper"
threads()
executable "client"
path "network-client"
dependency "network-shared"
run_dir "test"
run_target "test"

279
flint.py
View File

@@ -1,279 +0,0 @@
#!/usr/bin/env python3
import os
import platform
import errno
import configparser
import urllib.request
import shutil
import subprocess
from subprocess import PIPE
import stat
import sys
import argparse
flintlock = ".flintlock"
def install_plugin(url, plugins_folder, config, local = False):
metainfo = configparser.ConfigParser()
if not local:
req = urllib.request.Request(url + "/metainfo")
with urllib.request.urlopen(req) as response:
metainfo.read_string(response.read().decode("ascii"))
else:
with open(os.path.join(url, "metainfo.local"), "r") as f:
metainfo.read_file(f)
plugin_name = metainfo["meta"]["name"] + "@" + metainfo["meta"]["author"]
# Make sure we can't have local and remote version of the same plugin at the same time
# @todo This could be cleaner
if config.has_option("flint.py-plugins", plugin_name if local else plugin_name + ".local"):
config.remove_option("flint.py-plugins", plugin_name if local else plugin_name + ".local")
print("Installing '" + plugin_name + "'")
plugin_folder = os.path.join(plugins_folder, plugin_name)
if os.path.exists(plugin_folder):
shutil.rmtree(plugin_folder)
os.makedirs(plugin_folder)
# Register the plugin so that we can later update
config["flint.py-plugins"][plugin_name if not local else plugin_name + ".local"] = url
with open(flintlock, "w") as f:
config.write(f)
# @todo Use this same system for flint self
# @todo Add multiplatform support
if platform.system() == "Linux" and metainfo.has_section("linux"):
for filename in metainfo["linux"]:
if not local:
req = urllib.request.Request(url + "/" + filename)
with urllib.request.urlopen(req) as response, open(os.path.join(plugin_folder, filename), "wb") as f:
shutil.copyfileobj(response, f)
else:
os.symlink(os.path.abspath(os.path.join(url, metainfo["linux"][filename])), os.path.join(plugin_folder, filename))
elif platform.system() == "Windows" and metainfo.has_section("windows"):
for filename in metainfo["windows"]:
if not local:
req = urllib.request.Request(url + "/" + filename)
with urllib.request.urlopen(req) as response, open(os.path.join(plugin_folder, filename), "wb") as f:
shutil.copyfileobj(response, f)
else:
# @todo This requires admin mode on windows
os.symlink(os.path.abspath(os.path.join(url, metainfo["windows"][filename])), os.path.join(plugin_folder, filename))
else:
print("Plugin not available for current platform")
def remove_plugin(plugin_name, plugins_folder, config):
plugin_folder = os.path.join(plugins_folder, plugin_name)
if config.has_option("flint.py-plugins", plugin_name):
config.remove_option("flint.py-plugins", plugin_name)
elif config.has_option("flint.py-plugins", plugin_name + ".local"):
config.remove_option("flint.py-plugins", plugin_name + ".local")
else:
print("Plugin '" + plugin_name + "' does not exist")
return
if os.path.exists(plugin_folder):
shutil.rmtree(plugin_folder)
def main():
# @todo Add update, gets latest version and stores the specific version
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("--config", action='store_const', const=True, default=False)
parser.add_argument("--local", action='store_const', const=True, default=False)
parser.add_argument("--generate")
parser.add_argument("--version")
parser.add_argument("--install", nargs='?', const="__update__")
parser.add_argument("--remove")
parser.add_argument("--debug-flint", action='store_const', const=True, default=False)
args, unknownargs = parser.parse_known_args()
flintdir_base = ".flint"
flintdir = os.path.join(flintdir_base, "versions")
flintplug = os.path.join(flintdir_base, "plugins")
flintbin = "flint"
if os.name == "nt":
flintbin += ".exe"
# Make sure the flintlock file exists
if not os.path.isfile(flintlock):
# Create a new flintlock file with default values
config = configparser.ConfigParser()
with open(flintlock, "w") as f:
config.write(f)
# Open the flintlock file
config = configparser.ConfigParser()
with open(flintlock, "r") as f:
config.read_file(f)
# Make sure that all required keys exist
if not config.has_section("flint.py"):
config["flint.py"] = {}
if not config.has_option("flint.py", "version"):
config["flint.py"]["version"] = "nightly"
if not config.has_option("flint.py", "base_url"):
config["flint.py"]["base_url"] = "https://downloads.mtgames.nl/release/flint"
if not config.has_section("flint.py-plugins"):
config["flint.py-plugins"] = {}
with open(flintlock, "w") as f:
config.write(f)
# Check if the user has enable config mode
if args.config:
# @todo Improve config mode
if args.version:
config["flint.py"]["version"] = args.version
print("Flint will now use version: " + args.version)
else:
print("Did nothing...")
with open(flintlock, "w") as f:
config.write(f)
elif args.generate:
if not os.path.exists(args.generate + "/src"):
os.makedirs(args.generate + "/src")
if not os.path.exists(args.generate + "/include"):
os.makedirs(args.generate + "/include")
# @todo Also generate a simlpe example cpp file
with open("./flint.lua", "w") as f:
f.write("executable \"" + args.generate + "\"\n")
f.write("\tpath \"" + args.generate + "\"\n")
f.write("\n")
f.write("run_target \"" + args.generate + "\"\n")
elif args.install:
# @todo We need to auto update all plugins
# @todo We need to be able to remove plugins
metainfo = configparser.ConfigParser()
if args.install == "__update__":
for plugin in config["flint.py-plugins"]:
# @todo Make the download functions actual functions so we can call them here
install_plugin(config["flint.py-plugins"][plugin], flintplug, config, plugin.endswith(".local"))
else:
install_plugin(args.install, flintplug, config, args.local)
elif args.remove:
remove_plugin(args.remove, flintplug, config)
with open(flintlock, "w") as f:
config.write(f)
else:
# Check desired version
version = config["flint.py"]["version"]
base_url = config["flint.py"]["base_url"]
if args.version:
version = args.version
print("Flint: " + version)
flintdir = os.path.join(flintdir, version)
if os.path.isfile(os.path.join(version, flintbin)):
# The version contains a path to a flint executable
flintdir = version
else:
# Make sure the flint directory exists
if not os.path.exists(flintdir):
try:
os.makedirs(flintdir)
except OSError as exc:
if exc != errno.EEXIST:
raise
# Check if we need to update
needs_update = False
# Download actual checksum
checksums = configparser.ConfigParser()
try:
req = urllib.request.Request(base_url + "/" + version + "/checksums")
with urllib.request.urlopen(req) as response:
checksums.read_string(response.read().decode("ascii"))
checksums_current = configparser.ConfigParser()
if (not os.path.isfile(os.path.join(flintdir, flintbin))) or (not os.path.isfile(os.path.join(flintdir, "checksums"))):
# We need to download instead of update
needs_update = True
checksums_current["flint"] = {}
print("Downloading flint...")
else:
# Load the current checksum
with open(os.path.join(flintdir, "checksums"), "r") as f:
checksums_current.read_file(f)
# Compare the checksum to determine if updates are needed
# @note This does not detect if the executable is corrupted
if not checksums_current["flint"][flintbin] == checksums["flint"][flintbin]:
needs_update = True
print("Updating flint...")
# Store the new checksum
checksums_current["flint"][flintbin] = checksums["flint"][flintbin]
with open(os.path.join(flintdir, "checksums"), "w") as f:
checksums_current.write(f)
except urllib.error.URLError as error:
print("Unable to update flint!")
needs_update = False
# @todo Show progress bar
if needs_update:
try:
req = urllib.request.Request(base_url + "/" + version + "/" + flintbin)
with urllib.request.urlopen(req) as response, open(flintdir + "/" + flintbin, "wb") as f:
shutil.copyfileobj(response, f)
# Make sure we can execute flint
mode = os.stat(os.path.join(flintdir, flintbin)).st_mode
os.chmod(os.path.join(flintdir, flintbin), mode | stat.S_IEXEC)
with open(flintlock, "w") as f:
config.write(f)
except urllib.error.URLError as error:
print("Flint update failed!")
needs_update = False
command = []
env = os.environ
if os.name == "nt":
env["PATH"] = flintdir + ";.flint/plugins;" + env["PATH"]
else:
env["LD_LIBRARY_PATH"] = flintdir + ":.flint/plugins"
if os.name == "nt":
command.extend([r"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat", "x64", "&&"])
env["VSCMD_START_DIR"] = os.getcwd()
if args.debug_flint:
if platform.system() == "Linux":
command.extend(["gdb", "--args"])
elif platform.system() == "Windows":
command.extend(["devenv", "/debugexe"])
else:
print("Debugging not available for current platform")
command.append(os.path.join(flintdir, flintbin))
command.extend(unknownargs)
# Python pre 3.5 does not support run
return_code = 0
if sys.version_info[1] < 5:
subprocess.call(command, env=env)
else:
return_code = subprocess.run(command, env=env).returncode
exit(return_code)
if __name__ == "__main__":
main()

View File

@@ -1,100 +0,0 @@
#include <iostream>
#include <array>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ecs-serial.h"
#include "ecs_network_components.h"
#include "iohelper/memstream.h"
#include "packets.h"
#include "debug.h"
int main() {
ecs::Manager manager;
generated::init();
// IPv4
int address_family = AF_INET;
// UDP
int type = SOCK_DGRAM;
int protocol = 0;
int sock = socket(address_family, type, protocol);
if (sock < 0) {
throw std::runtime_error("Failed to create socket!");
}
sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_port = htons(9999);
server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
sockaddr* to = (sockaddr*)&server_address;
int to_size = sizeof(server_address);
std::array<uint8_t, 1024> buff;
// @todo Implement iomemstream
iohelper::omemstream_fixed<buff.size()> buff_stream_out(buff);
iohelper::imemstream_fixed<buff.size()> buff_stream_in(buff);
iohelper::write<uint8_t>(buff_stream_out, PACKET::CONNECT);
if (sendto(sock, buff.data(), buff.size(), 0, to, to_size) < 0) {
throw std::runtime_error("Failed to connect to server");
}
sockaddr_in from;
socklen_t from_size = sizeof(from);
int bytes_received = recvfrom(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, &from_size);
if (bytes_received < 0) {
throw std::runtime_error("Failed to receive data!");
}
if (iohelper::read<uint8_t>(buff_stream_in) == PACKET::ACK) {
std::cout << "Connected to server!\n";
} else {
throw std::runtime_error("Unexpected server response");
}
while (true) {
buff_stream_in.seekg(0, std::ios::beg);
int bytes_received = recvfrom(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, &from_size);
if (bytes_received < 0) {
throw std::runtime_error("Failed to receive data!");
}
PACKET type = (PACKET)iohelper::read<uint8_t>(buff_stream_in);
switch (type) {
case PACKET::IDS:
std::cout << "Receiving ids\n";
ecs::serial::deserialize_ids(buff_stream_in);
break;
case PACKET::ENTITIES: {
std::cout << "Receiving entities\n";
size_t entity_count = iohelper::read_length(buff_stream_in);
for (size_t i = 0; i < entity_count; ++i) {
ecs::serial::deserialize(buff_stream_in, manager);
}
manager.view<Position>().for_each([](ecs::Entity* entity, Position* position) {
std::cout << entity->uuid << ' ' << position->x << ' ' << position->y << '\n';
});
break;
}
default:
std::cout << "Unknown packet type: " << (uint32_t)type << '\n';
}
// dump_buffer<buff.size()>(buff);
}
}

View File

@@ -1,155 +0,0 @@
#include <iostream>
#include <thread>
#include <functional>
#include <array>
#include <netinet/in.h>
#include "ecs-serial.h"
#include "ecs_network_components.h"
#include "iohelper/memstream.h"
#include "packets.h"
#include "debug.h"
ecs::Manager manager;
void on_connect(int sock, sockaddr_in from) {
uint32_t ip = from.sin_addr.s_addr;
uint32_t bytes[4];
bytes[0] = ip & 0xff;
bytes[1] = (ip >> 8) & 0xff;
bytes[2] = (ip >> 16) & 0xff;
bytes[3] = (ip >> 24) & 0xff;
std::cout << "Client connected: " << bytes[0] << '.' << bytes[1] << '.' << bytes[2] << '.' << bytes[3] << '\n';
std::array<uint8_t, 1024> buff;
iohelper::omemstream_fixed<buff.size()> buff_stream(buff);
iohelper::write<uint8_t>(buff_stream, PACKET::ACK);
if (sendto(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, sizeof(from)) < 0) {
throw std::runtime_error("Failed to send ack to client");
}
// dump_buffer<buff.size()>(buff);
// Send ids and all entities
// @todo Make everything thread safe
buff_stream.seekp(0, std::ios::beg);
iohelper::write<uint8_t>(buff_stream, PACKET::IDS);
ecs::serial::serialize_ids(buff_stream);
std::cout << "Sending ids\n";
if (sendto(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, sizeof(from)) < 0) {
throw std::runtime_error("Failed to send ids to client");
}
// dump_buffer<buff.size()>(buff);
std::cout << "Sending entities\n";
buff_stream.seekp(0, std::ios::beg);
iohelper::write<uint8_t>(buff_stream, PACKET::ENTITIES);
iohelper::write_length(buff_stream, manager.view<>().size());
for (auto [uuid, entity] : manager.view<>()) {
ecs::serial::serialize(buff_stream, entity);
}
if (sendto(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, sizeof(from)) < 0) {
throw std::runtime_error("Failed to send ids to client");
}
// THIS IS A TEST
std::cout << "Sending update\n";
manager.view<Position>().for_each([](ecs::Entity* /* entity */, Position* position) {
if (int(position->y * 10) % 2 == 0) {
position->y *= 2;
}
});
// @todo Figure out how to delta everything
buff_stream.seekp(0, std::ios::beg);
iohelper::write<uint8_t>(buff_stream, PACKET::ENTITIES);
iohelper::write_length(buff_stream, manager.view<>().size());
for (auto [uuid, entity] : manager.view<>()) {
ecs::serial::serialize(buff_stream, entity);
}
if (sendto(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, sizeof(from)) < 0) {
throw std::runtime_error("Failed to send ids to client");
}
// dump_buffer<buff.size()>(buff);
}
// This is the loop that listens for messages from clients
void listener(int sock) {
std::array<uint8_t, 1024> buff;
iohelper::imemstream_fixed<buff.size()> buff_stream(buff);
sockaddr_in from;
socklen_t from_size = sizeof(from);
while (true) {
buff_stream.seekg(0, std::ios::beg);
int bytes_received = recvfrom(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, &from_size);
if (bytes_received < 0) {
throw std::runtime_error("Failed to receive data!");
}
switch (iohelper::read<uint8_t>(buff_stream)) {
case PACKET::CONNECT:
on_connect(sock, from);
break;
}
}
}
int main() {
generated::init();
for (int i = 0; i < 10; ++i) {
// We can create entities
ecs::Entity* entity = manager.create_entity();
// Then we can add components to them
entity->add_component<Position>(0.1f*i, 0.3f*i);
}
manager.view<Position>().for_each([](ecs::Entity* entity, Position* position) {
std::cout << entity->uuid << ' ' << position->x << ' ' << position->y << '\n';
});
int address_family = AF_INET;
int type = SOCK_DGRAM;
int protocol = 0;
int sock = socket(address_family, type, protocol);
if (sock < 0) {
throw std::runtime_error("Failed to create socket!");
}
sockaddr_in local_address;
local_address.sin_family = AF_INET;
local_address.sin_port = htons(9999);
local_address.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (sockaddr*)&local_address, sizeof(local_address)) < 0) {
throw std::runtime_error("Failed to bind to port!");
}
std::thread lthread(std::bind(listener, sock));
std::cout << "Server started!\n";
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
lthread.join();
}

View File

@@ -1,13 +0,0 @@
#pragma once
#include <array>
#include <iostream>
template<int I>
void dump_buffer(std::array<uint8_t, I> &buff) {
for (uint32_t b : buff) {
std::cout << b << ' ';
}
std::cout << '\n';
}

View File

@@ -1,10 +0,0 @@
#pragma once
#include "ecs.h"
struct Position : ecs::Component {
Position(float _x, float _y) : x(_x), y(_y) {}
Position() {}
float x = 0;
float y = 0;
};

View File

@@ -1,10 +0,0 @@
#pragma once
#include <cstdint>
enum PACKET : uint8_t {
CONNECT,
ACK,
IDS,
ENTITIES
};

View File

@@ -1,5 +1,6 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
import sys import sys
import os
import clang.cindex import clang.cindex
from mako.template import Template from mako.template import Template
@@ -63,21 +64,35 @@ def build_components(cursor, filename):
return result return result
def main(argv): def main(argv):
if len(sys.argv) != 2: if len(sys.argv) != 3:
print("Usage: {} [header filename]".format(argv[0])) print("Usage: {} [header filename] [output filename]".format(argv[0]))
sys.exit() sys.exit()
filename = argv[1] filename = argv[1]
output = argv[2]
index = clang.cindex.Index.create() index = clang.cindex.Index.create()
tu = index.parse(filename, ['-x', 'c++', '-std=c++17', '-Iecs/include']) tu = index.parse(filename, ['-x', 'c++', '-std=c++20', f'-Iecs/include', f'-Ibuild/_deps/stduuid-src/include', '-I/lib/gcc/x86_64-pc-linux-gnu/11.1.0/include'])
components = build_components(tu.cursor, filename) components = build_components(tu.cursor, filename)
tpl = Template(filename='template.mako') tpl = Template(filename=f'template.mako')
include_file = filename.split('/')[-1] include_file = filename.split('/')[-1]
print(tpl.render(components=components, include_file=include_file))
if not os.path.exists(os.path.dirname(output)):
try:
os.makedirs(os.path.dirname(output))
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
output_file = open(output, "w")
output_file.write(tpl.render(components=components, include_file=include_file))
output_file.close()
for d in tu.diagnostics:
print(d)
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv) main(sys.argv)

View File

@@ -1,7 +1,7 @@
#include "${include_file}" #include "${include_file}"
#if __has_include("ecs-lua.h") #if __has_include("ecs-lua.h")
#include "sol.hpp" #include "sol/sol.hpp"
namespace generated { namespace generated {
void init(sol::state& lua) { void init(sol::state& lua) {
@@ -10,20 +10,23 @@ namespace generated {
preload["ecs.${c.name}"] = [&lua] { preload["ecs.${c.name}"] = [&lua] {
sol::table component = lua.create_table(); sol::table component = lua.create_table();
component["_id"] = ecs::ComponentID::id<${c.name}>;
component.new_usertype<${c.name}>("${c.name}", component.new_usertype<${c.name}>("${c.name}",
${', '.join("\"{}\", &{}::{}".format(v.name, c.name, v.name) for v in c.variables if not "hidden" in v.annotations)} ${', '.join("\"{}\", &{}::{}".format(v.name, c.name, v.name) for v in c.variables if not "hidden" in v.annotations)},
sol::base_classes, sol::bases<ecs::Component>()
); );
% for con in c.constructors: % for con in c.constructors:
% if len(con.parameters) > 0: % if len(con.parameters) > 0:
component.set_function("new", sol::factories([](${', '.join("{} {}".format(p.type, p.name) for p in con.parameters)}) { component.set_function("new", sol::factories([](${', '.join("{} {}".format(p.type, p.name) for p in con.parameters)}) {
return std::make_pair(new ${c.name}(${', '.join(p.name for p in con.parameters)}), "${c.name}"); return std::make_pair(ecs::ComponentID::id<${c.name}>, new ${c.name}(${', '.join(p.name for p in con.parameters)}));
})); }));
% endif % endif
% endfor % endfor
lua.set_function("_internal_to_${c.name}", [] (ecs::Component* component) { component.set_function("_convert", [] (ecs::Component* component) {
return (${c.name}*)component; return static_cast<${c.name}*>(component);
}); });
return component; return component;

44
test/CMakeLists.txt Normal file
View File

@@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(ecs_test
LANGUAGES CXX
)
find_package(PythonInterp 3 REQUIRED)
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/include/ecs_components.h
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/..
COMMAND ${PYTHON_EXECUTABLE} scripts/generate.py test/include/components.h ${PROJECT_BINARY_DIR}/include/ecs_components.h
DEPENDS ${PROJECT_SOURCE_DIR}/../template.mako ${PROJECT_SOURCE_DIR}/../scripts/generate.py ${PROJECT_SOURCE_DIR}/include/components.h
COMMENT "Generating headers"
)
file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
file(GLOB_RECURSE sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(${PROJECT_NAME} ${headers} ${sources} ${PROJECT_BINARY_DIR}/include/ecs_components.h)
# being a cross-platform target, we enforce standards conformance on MSVC
target_compile_options(${PROJECT_NAME} PUBLIC "$<$<BOOL:${MSVC}>:/permissive->")
# Link dependencies (if required)
target_link_libraries(${PROJECT_NAME} PUBLIC ecs ecs-lua ecs-serial)
# add_dependencies(${PROJECT_NAME} generate_test)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 20
OUTPUT_NAME "${PROJECT_NAME}"
)
string(TOLOWER ${PROJECT_NAME}/version.h VERSION_HEADER_LOCATION)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>
)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>
)

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#include <string>
#include "ecs.h" #include "ecs.h"
struct Position : ecs::Component { struct Position : ecs::Component {

View File

@@ -1,11 +1,7 @@
#include "/home/tim/Projects/cpp/iohelper/iohelper/include/iohelper/read.h"
#include "/home/tim/Projects/cpp/iohelper/iohelper/include/iohelper/write.h"
#include "components.h" #include "components.h"
#include "ecs-lua.h" #include "ecs-lua.h"
#include "ecs-serial.h" #include "ecs-serial.h"
#include <bits/stdint-intn.h>
#include <bits/stdint-uintn.h>
#include <cstddef> #include <cstddef>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
@@ -16,6 +12,14 @@
#include "ecs.h" #include "ecs.h"
#include "ecs_components.h" #include "ecs_components.h"
class Orb : public ecs::Entity {
public:
Orb(uuids::uuid uuid, float x, float y) : Entity(uuid) {
auto position = add_component<Position>(x, y);
auto velocity = add_component<Velocity>(0, 0);
}
};
inline void handle_error(sol::optional<std::string> maybe_msg) { inline void handle_error(sol::optional<std::string> maybe_msg) {
std::cerr << "Lua error, aborting!\n"; std::cerr << "Lua error, aborting!\n";
if (maybe_msg) { if (maybe_msg) {
@@ -40,6 +44,10 @@ int main(int argc, const char** argv) {
return -1; return -1;
} }
ecs::ComponentID::add<Position>("Position");
ecs::ComponentID::add<Velocity>("Velocity");
ecs::ComponentID::add<Meta>("Meta");
sol::state lua(sol::c_call<decltype(&handle_error), &handle_error>); sol::state lua(sol::c_call<decltype(&handle_error), &handle_error>);
lua.open_libraries(sol::lib::base, sol::lib::package); lua.open_libraries(sol::lib::base, sol::lib::package);
@@ -48,95 +56,6 @@ int main(int argc, const char** argv) {
generated::init(lua); generated::init(lua);
generated::init(); generated::init();
// @todo I am pretty sure that iohelper actually has an api that allows us to write a custom (de)serializer
ecs::serial::register_component_custom<ecs::lua::Wrapper>(
[](std::ostream& os, ecs::Component* component) {
ecs::lua::Wrapper* wrapper = (ecs::lua::Wrapper*)component;
// iohelper::write<std::string>(os, wrapper->name);
// #todo .size() does not work
size_t size = 0;
for (auto [a, b] : wrapper->table) {
size++;
}
iohelper::write_length(os, size);
for (auto [a, b] : wrapper->table) {
iohelper::write<std::string>(os, a.as<std::string>());
sol::type type = b.get_type();
iohelper::write<uint8_t>(os, (uint8_t)type);
switch (type) {
case sol::type::none:
case sol::type::nil:
break;
case sol::type::string:
iohelper::write<std::string>(os, b.as<std::string>());
break;
case sol::type::number:
// @todo These should be doubles instead of floats
iohelper::write<float>(os, b.as<float>());
break;
case sol::type::boolean:
iohelper::write<bool>(os, b.as<bool>());
break;
case sol::type::table:
// @todo Make this happen
break;
// All other types are not supported
default:
throw std::runtime_error("Unsupported type in wrapped lua table");
break;
}
}
},
[&lua](std::istream& is, ecs::Component* component) {
ecs::lua::Wrapper* wrapper = (ecs::lua::Wrapper*)component;
// @todo Only do this if table is not created yet
wrapper->table = lua.create_table();
size_t size = iohelper::read_length(is);
std::cout << "Size: " << size << '\n';
for (size_t i = 0; i < size; ++i) {
std::string name = iohelper::read<std::string>(is);
std::cout << "Name: " << name << '\n';
sol::type type = (sol::type)iohelper::read<uint8_t>(is);
switch (type) {
case sol::type::none:
break;
case sol::type::nil:
break;
case sol::type::string:
wrapper->table[name] = iohelper::read<std::string>(is);
break;
case sol::type::number:
wrapper->table[name] = iohelper::read<float>(is);
break;
case sol::type::boolean:
{
wrapper->table[name] = iohelper::read<bool>(is);
break;
}
default:
break;
}
}
}
);
if (save) { if (save) {
ecs::Manager manager; ecs::Manager manager;
@@ -149,6 +68,14 @@ int main(int argc, const char** argv) {
lua.safe_script_file("test.lua"); lua.safe_script_file("test.lua");
sol::protected_function init = lua["init"];
std::cout << "Running init()\n";
init();
sol::protected_function run = lua["run"];
std::cout << "Running run()\n";
run();
std::cout << "Update position\n"; std::cout << "Update position\n";
manager.view<Position, Velocity>().for_each([](ecs::Entity*, Position* pos, Velocity* vel) { manager.view<Position, Velocity>().for_each([](ecs::Entity*, Position* pos, Velocity* vel) {
pos->x += vel->x; pos->x += vel->x;
@@ -161,19 +88,12 @@ int main(int argc, const char** argv) {
std::cout << "Y: " << pos->y << '\n'; std::cout << "Y: " << pos->y << '\n';
}); });
// These are really just an internal api that should not be used std::cout << "Adding Orb!\n";
for (auto [uuid, entity] : manager.view(ecs::ComponentID::get_id({"Random"}))) { manager.create_entity<Orb>(1.2f, 3.4f);
sol::table random = ((ecs::lua::Wrapper*)entity->get_component(ecs::ComponentID::get_id({"Random"})[0]))->table; for (auto [ent, pos, vel] : manager.view<Position, Velocity>()) {
std::cout << "X: " << pos->x << " + " << vel->x << '\n';
random["a"] = 21; std::cout << "Y: " << pos->y << " + " << vel->y << '\n';
std::cout << random["a"].get<std::string>() << '\n'; }
};
for (auto [uuid, entity] : manager.view(ecs::ComponentID::get_id({"Random"}))) {
sol::table random = ((ecs::lua::Wrapper*)entity->get_component(ecs::ComponentID::get_id({"Random"})[0]))->table;
std::cout << random["a"].get<std::string>() << '\n';
};
} }
// Test serialization // Test serialization
@@ -199,8 +119,8 @@ int main(int argc, const char** argv) {
std::ofstream file("entities", std::ios::out | std::ios::trunc); std::ofstream file("entities", std::ios::out | std::ios::trunc);
ecs::serial::serialize_ids(file); ecs::serial::serialize_ids(file);
iohelper::write_length(file, manager.view<>().size()); io::write<size_t>(file, manager.view<>().size());
for (auto [uuid, entity] : manager.view<>()) { for (auto [entity] : manager.view<>()) {
ecs::serial::serialize(file, entity); ecs::serial::serialize(file, entity);
} }
file.close(); file.close();
@@ -216,12 +136,20 @@ int main(int argc, const char** argv) {
{ {
std::cout << "LOAD\n"; std::cout << "LOAD\n";
lua.safe_script_file("test2.lua");
sol::protected_function init = lua["init"];
std::cout << "Running init()\n";
init();
// Load entities from disk as a test // Load entities from disk as a test
std::ifstream file("entities", std::ios::in); std::ifstream file("entities", std::ios::in);
if (!file.is_open()) {
std::cerr << "Failed to open: 'entities'!\n";
}
ecs::serial::deserialize_ids(file); ecs::serial::deserialize_ids(file);
size_t entity_count = iohelper::read_length(file); size_t entity_count = io::read<size_t>(file);
size_t pos = file.tellg(); size_t pos = file.tellg();
for (size_t i = 0; i < entity_count; ++i) { for (size_t i = 0; i < entity_count; ++i) {
ecs::serial::deserialize(file, manager); ecs::serial::deserialize(file, manager);
@@ -250,11 +178,13 @@ int main(int argc, const char** argv) {
return manager; return manager;
}); });
lua.safe_script_file("test2.lua"); sol::protected_function run = lua["run"];
std::cout << "Running run()\n";
run();
} }
} }
for (auto [name, id] : ecs::ComponentID::_ids()) { for (auto [name, id] : ecs::ComponentID::get_map()) {
std::cout << name << ' ' << id << '\n'; std::cout << name << ' ' << id << '\n';
} }
} }

View File

@@ -1,87 +1,102 @@
local ecs = require "ecs" require "ecs"
-- local test = require "ecs_test"
local Position = require "ecs.Position" local Position = require "ecs.Position"
local Velocity = require "ecs.Velocity" local Velocity = require "ecs.Velocity"
local Meta = require "ecs.Meta" local Meta = require "ecs.Meta"
local Wrapper = require 'ecs.Wrapper' local LuaComponent = require 'ecs.LuaComponent'
manager = get_manager() function init()
ent = manager:create_entity() print("Creating LuaComponents")
ent:add_component(Position.new(1.9, 9.7))
ent:add_component(Velocity.new(0.2, 0.3))
ent:add_component(Meta.new("Soldier"))
data = { LuaData = LuaComponent.create("LuaData", {
speed = 10, speed = "number",
something = "Hello, World!", something = "string",
alive = true alive = "boolean"
} })
ent:add_component(Wrapper.new("LuaData", data)) TestThing = LuaComponent.create("TestThing", {
ent:add_component(Wrapper.new("TestThing", { test = 1.23563 })) test = "string"
})
end
-- @todo Make this happen...
-- stats = {
-- hp = 100,
-- magic = 30
-- }
--
-- ent:add_component(ecs.Wrapper.new("Stats", stats))
print(ent:has_components("Position", "Velocity")) function run()
pos = ent:get_component("Position") print("Running!")
print("x: " .. pos.x) manager = get_manager()
print("y: " .. pos.y) ent = manager:create_entity()
vel = ent:get_component("Velocity") ent:add_component(Position.new(1.9, 9.7))
print("v_x: " .. vel.x) ent:add_component(Velocity.new(0.2, 0.3))
print("v_y: " .. vel.y) ent:add_component(Meta.new("Soldier"))
print("View test") ent:add_component(LuaData.new({
manager:view("Position", "Velocity"):for_each(function(ent) speed = 10,
pos = ent:get_component("Position") something = "Hello, World!",
vel = ent:get_component("Velocity") alive = true
}))
ent:add_component(TestThing.new({
test = "This is a test!"
}))
pos.x = pos.x + vel.x -- @todo Make this happen...
pos.y = pos.y + vel.y -- stats = {
-- hp = 100,
print(ent) -- magic = 30
-- }
--
-- ent:add_component(ecs.Wrapper.new("Stats", stats))
print(ent:has_components(Position, Velocity))
pos = ent:get_component(Position)
print("x: " .. pos.x) print("x: " .. pos.x)
print("y: " .. pos.y) print("y: " .. pos.y)
vel = ent:get_component(Velocity)
print("v_x: " .. vel.x) print("v_x: " .. vel.x)
print("v_y: " .. vel.y) print("v_y: " .. vel.y)
if ent:has_components("Meta") then print("View test")
meta = ent:get_component("Meta") manager:view(Position, Velocity):for_each(function(ent)
print("name: " .. meta.name) pos = ent:get_component(Position)
end vel = ent:get_component(Velocity)
end)
-- @todo Implement this pos.x = pos.x + vel.x
-- for uuid,entity in pairs(view) do pos.y = pos.y + vel.y
-- print "TEST"
-- end
manager:view("LuaData"):for_each(function(ent) print(ent)
d = ent:get_component("LuaData")
print(d.speed) print("x: " .. pos.x)
d.speed = 11 print("y: " .. pos.y)
print(d.speed)
print(data.speed)
data.speed = 20
print(d.speed)
print(data.speed)
print(d.alive) print("v_x: " .. vel.x)
d.alive = false print("v_y: " .. vel.y)
print(d.alive)
print(d.something) if ent:has_components(Meta) then
end) meta = ent:get_component(Meta)
print("name: " .. meta.name)
end
end)
manager:view("TestThing"):for_each(function(ent) -- @todo Implement this
t = ent:get_component("TestThing") -- for uuid,entity in pairs(view) do
-- print "TEST"
-- end
print(t.test) manager:view(LuaData):for_each(function(ent)
end) d = ent:get_component(LuaData)
print("speed: " .. d.speed)
d.speed = 11
print("speed: " .. d.speed)
print("alive: ") print(d.alive)
d.alive = false
print("alive: ") print(d.alive)
print("Something: " .. d.something)
end)
manager:view(TestThing):for_each(function(ent)
t = ent:get_component(TestThing)
print("test: " .. t.test)
end)
print("Done running!")
end

View File

@@ -1,26 +1,48 @@
require "ecs" require "ecs"
require "ecs.Wrapper" local LuaComponent = require "ecs.LuaComponent"
manager = get_manager() function init()
print("Registering components")
manager:view("TestThing"):for_each(function(ent) -- Swapped compared to test.lua
data = ent:get_component("TestThing") TestThing = LuaComponent.create("TestThing", {
print("test: " .. data.test) test = "string"
end) })
LuaData = LuaComponent.create("LuaData", {
speed = "number",
something = "string",
alive = "boolean"
})
end
manager:view("LuaData"):for_each(function(ent) function run()
-- @todo It would be nice if this could somehow be passed in as function arg print("Running!")
data = ent:get_component("LuaData")
print("speed: " .. data.speed)
print("something: " .. data.something)
-- @todo You cannot concatenate bool to string
print("alive: ")
print(data.alive)
end)
-- @todo Allow this manager = get_manager()
-- for i, v in pairs(manager:view("TestThing")) do
-- print(i, v)
-- end
print(manager:has_entity("70bca3cf-33dd-40dc-8b0f-2e19aa0b4a17")) manager:view(TestThing):for_each(function(ent)
data = ent:get_component(TestThing)
print("test: " .. data.test)
end)
manager:view(LuaData):for_each(function(ent)
-- @todo It would be nice if this could somehow be passed in as function arg
data = ent:get_component(LuaData)
print("speed: " .. data.speed)
print("something: " .. data.something)
-- @todo You cannot concatenate bool to string
print("alive: ")
print(data.alive)
end)
-- @todo Allow this
-- for i, v in pairs(manager:view("TestThing")) do
-- print(i, v)
-- end
print("Exists: ")
print(manager:has_entity("70bca3cf-33dd-40dc-8b0f-2e19aa0b4a17"))
print("Done running!")
end

View File

@@ -1,60 +0,0 @@
//========================================================================
// GLFW 3.3 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2010-2016 Camilla Löwy <elmindreda@glfw.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// As glfw_config.h.in, this file is used by CMake to produce the
// glfw_config.h configuration header file. If you are adding a feature
// requiring conditional compilation, this is where to add the macro.
//========================================================================
// As glfw_config.h, this file defines compile-time option macros for a
// specific platform and development environment. If you are using the
// GLFW CMake files, modify glfw_config.h.in instead of this file. If you
// are using your own build system, make this file define the appropriate
// macros in whatever way is suitable.
//========================================================================
// Define this to 1 if building GLFW for X11
// #cmakedefine _GLFW_X11
// Define this to 1 if building GLFW for Win32
// #cmakedefine _GLFW_WIN32
// Define this to 1 if building GLFW for Cocoa
// #cmakedefine _GLFW_COCOA
// Define this to 1 if building GLFW for Wayland
// #cmakedefine _GLFW_WAYLAND
// Define this to 1 if building GLFW for OSMesa
// #cmakedefine _GLFW_OSMESA
// Define this to 1 if building as a shared library / dynamic library / DLL
// #cmakedefine _GLFW_BUILD_DLL
// Define this to 1 to use Vulkan loader linked statically into application
// #cmakedefine _GLFW_VULKAN_STATIC
// Define this to 1 to force use of high-performance GPU on hybrid systems
// #cmakedefine _GLFW_USE_HYBRID_HPG
// Define this to 1 if xkbcommon supports the compose key
// #cmakedefine HAVE_XKBCOMMON_COMPOSE_H
// Define this to 1 if the libc supports memfd_create()
// #cmakedefine HAVE_MEMFD_CREATE

View File

@@ -1,73 +0,0 @@
//========================================================================
// GLFW 3.3 - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// As mappings.h.in, this file is used by CMake to produce the mappings.h
// header file. If you are adding a GLFW specific gamepad mapping, this is
// where to put it.
//========================================================================
// As mappings.h, this provides all pre-defined gamepad mappings, including
// all available in SDL_GameControllerDB. Do not edit this file. Any gamepad
// mappings not specific to GLFW should be submitted to SDL_GameControllerDB.
// This file can be re-generated from mappings.h.in and the upstream
// gamecontrollerdb.txt with the GenerateMappings.cmake script.
//========================================================================
// All gamepad mappings not labeled GLFW are copied from the
// SDL_GameControllerDB project under the following license:
//
// Simple DirectMedia Layer
// Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the
// use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
const char* _glfwDefaultMappings[] =
{
@GLFW_GAMEPAD_MAPPINGS@
"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
NULL
};

View File

@@ -1,3 +0,0 @@
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

1
vendor/lua vendored

Submodule vendor/lua deleted from e354c6355e

1
vendor/sol2 vendored

Submodule vendor/sol2 deleted from 8643dec9e5

1
vendor/stduuid vendored

Submodule vendor/stduuid deleted from dee14f660d