Compare commits

...

9 Commits

36 changed files with 1357 additions and 1504 deletions

View File

@@ -1,16 +0,0 @@
[flint.py]
version = feature_git
base_url = https://downloads.mtgames.nl/release/flint
[flint.py-plugins]
[git-vulkan-headers]
url = https://github.com/KhronosGroup/Vulkan-Headers
revision = master
commit = 42ad3f90faec009b9435383ee89910846d6a91ba
[git-glfw]
url = https://github.com/glfw/glfw
revision = 3.3-stable
commit = b1309dd42a72c8f7cd58a6f75329c4328679aed2

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*
### premake-gmake ###
Makefile
*.make
build/
### Vim ###
# swap
[._]*.s[a-v][a-z]
@@ -28,12 +33,7 @@ Session.vim
.netrwhist
# auto-generated tag files
tags
# flint
.flint
# Custom
.clangd/
compile_commands.json
test/ids
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> <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
#include "sol.hpp"
#include <limits>
#include "sol/sol.hpp"
#include "ecs.h"
#include <iostream>
#include <utility>
namespace ecs::lua {
struct Wrapper : Component {
struct LuaComponent : Component {
// @todo Figure out a more elegant way
Wrapper(sol::table _table) : Component(ComponentID::id<Wrapper>), table(_table) {}
Wrapper() : Component(ComponentID::id<Wrapper>) {}
LuaComponent() {}
sol::table table;
};

View File

@@ -2,6 +2,10 @@
#include "ecs.h"
// #ifdef _OPTIONAL_ECS_SERIAL
#include "ecs-serial.h"
// #endif
namespace sol {
template<>
struct is_container<ecs::View<>> : std::true_type {};
@@ -18,32 +22,25 @@ namespace ecs::lua {
"__tostring", [] (Entity* thiz) {
return uuids::to_string(thiz->uuid);
},
"add_component", [] (Entity* thiz, Component* component, std::string name) {
return thiz->add_component(ComponentID::get_id({name})[0], component);
"add_component", [] (Entity* thiz, size_t id, Component* component) {
return thiz->add_component(id, component);
},
"has_components", [] (Entity* thiz, sol::variadic_args args) {
std::vector<std::string> names;
for (std::string name : args) {
names.push_back(name);
std::vector<size_t> ids;
for (sol::table table : args) {
// @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 {
// Convert to the correct component type
Component* component = thiz->get_component(ComponentID::get_id({name})[0]);
if (!component->_runtime) {
// @todo Figure out a more elegant way to convert
auto f1 = lua["_internal_to_" + name];
"get_component", [&lua] (Entity* thiz, sol::table table) -> sol::object {
// @todo Check that the table has id and convert()
Component* component = thiz->get_component(table["_id"]);
auto f1 = table["_convert"];
if (f1.valid()) {
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");
}
);
@@ -67,37 +64,162 @@ namespace ecs::lua {
return thiz->get_entity(uuids::uuid::from_string(uuid).value());
},
"view", [] (Manager* thiz, sol::variadic_args args) {
std::vector<std::string> names;
for (std::string name : args) {
names.push_back(name);
std::vector<size_t> ids;
for (sol::table table : args) {
// @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;
};
preload["ecs.Wrapper"] = [&lua] {
sol::table component = lua.create_table();
component.new_usertype<Wrapper>("Wrapper",
"__index", [] (Wrapper* thiz, std::string key) {
preload["ecs.LuaComponent"] = [&lua] {
// @todo Do we want to register this into the global namespace?
lua.new_usertype<LuaComponent>("LuaComponent",
"__index", [] (LuaComponent* thiz, std::string 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;
}
},
sol::base_classes, sol::bases<ecs::Component>()
);
component.set_function("new", sol::factories([](std::string _name, sol::table _table) {
return std::make_pair(new Wrapper(_table), _name);
sol::table comp = lua.create_table();
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';
std::unordered_map<std::string, sol::type> map;
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);
}));
lua.set_function("_internal_to_Wrapper", [] (ecs::Component* component) {
return (Wrapper*)component;
component.set_function("_convert", [] (ecs::Component* component) {
return static_cast<LuaComponent*>(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 <ostream>
#pragma once
#include "ecs.h"
#include "iohelper/write.h"
#include "iohelper/read.h"
#include "io/write.h"
#include "io/read.h"
#include <iostream>
@@ -20,7 +20,7 @@ namespace ecs::serial {
template <typename T, typename M, typename... 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...);
}
@@ -32,7 +32,7 @@ namespace ecs::serial {
template <typename T, typename M, typename... 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...);
}
@@ -41,32 +41,25 @@ namespace ecs::serial {
template <typename T, typename... Args>
void register_component(Args... args) {
// Serialize component
auto func1 = [args...] (std::ostream& os, ecs::Component* component) {
T* t = (T*)component;
auto serialize = [args...] (std::ostream& os, ecs::Component* component) {
T* t = static_cast<T*>(component);
serialize_member(os, t, args...);
};
// Deserialize component
auto func2 = [args...] (std::istream& is, ecs::Component* component) {
T* t = (T*)component;
auto deserialize = [args...] (std::istream& is, ecs::Component* component) {
T* t = static_cast<T*>(component);
deserialize_member(is, t, args...);
};
auto func3 = [] () -> Component* {
auto create = [] () -> Component* {
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(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 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 serialize(std::ostream& os, Entity* entity);
void deserialize(std::istream& is, Manager& manager);

View File

@@ -1,7 +1,5 @@
#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 <ostream>
@@ -14,41 +12,31 @@ namespace ecs::serial {
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) {
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();
iohelper::write_length(os, components.size());
io::write<size_t>(os, components.size());
for (auto [id, component] : components) {
if (!component->_runtime) {
auto functions = internal::functions.find(id);
if (functions == internal::functions.end()) {
std::cerr << "ID: " << id << '\n';
throw std::runtime_error("No known serializer for id");
}
iohelper::write_length(os, id);
iohelper::write<bool>(os, false);
io::write<size_t>(os, id);
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);
}
}
}
void deserialize(std::istream& is, Manager& manager) {
auto uuid_vector = iohelper::read_vector<uint8_t>(is, 16);
uuids::uuid uuid(uuid_vector.begin(), uuid_vector.end());
auto uuid_array = io::read<std::array<uint8_t, 16>>(is);
uuids::uuid uuid(uuid_array.begin(), uuid_array.end());
Entity* entity = nullptr;
if (manager.has_entity(uuid)) {
@@ -59,15 +47,10 @@ namespace ecs::serial {
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';
for (size_t i = 0; i < component_count; ++i) {
size_t new_id = conversion[iohelper::read_length(is)];
bool runtime = iohelper::read<bool>(is);
size_t id = new_id;
if (runtime) {
id = conversion[iohelper::read_length(is)];
}
size_t id = conversion[io::read<size_t>(is)];
// @todo We also need to be able to remove components
// Sending a component with length 0 -> remove component
@@ -77,22 +60,14 @@ namespace ecs::serial {
// @todo What if the function does not exist?
if (entity->has_components({id})) {
// Update the component
std::cout << "Updating component: " << id << " [" << ComponentID::get_name(id) << "]";
if (new_id != id) {
std::cout << " (base: " << new_id << " [" << ComponentID::get_name(new_id) << "])";
}
std::cout << '\n';
std::cout << "Updating component: " << id << " [" << ComponentID::reverse_resolve(id) << "]\n";
Component* component = entity->get_component(id);
// @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 {
// Add new component
std::cout << "Adding component: " << id << " [" << ComponentID::get_name(id) << "]";
if (new_id != id) {
std::cout << " (base: " << new_id << " [" << ComponentID::get_name(new_id) << "])";
}
std::cout << '\n';
auto func = internal::functions.find(new_id);
std::cout << "Adding component: " << id << " [" << ComponentID::reverse_resolve(id) << "]\n";
auto func = internal::functions.find(id);
if (func == internal::functions.end()) {
throw std::runtime_error("No known serializers for component");
}
@@ -104,22 +79,22 @@ namespace ecs::serial {
}
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) {
iohelper::write<std::string>(os, name);
iohelper::write_length(os, id);
io::write<std::string>(os, name);
io::write<size_t>(os, id);
}
}
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) {
std::string name = iohelper::read<std::string>(is);
size_t id = iohelper::read_length(is);
std::string name = io::read<std::string>(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) {

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 <algorithm>
#include <list>
#include <vector>
#include <iostream>
#include <typeinfo>
#if __has_include(<cxxabi.h>)
#include <cxxabi.h>
#endif
#define UUID_SYSTEM_GENERATOR
#include "uuid.h"
namespace ecs {
template <typename T>
std::string get_typename() {
#if __has_include(<cxxabi.h>)
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;
}
// @todo Redo the component id system to require registering the components
// This would hopefully clear up some of the jank related to this part of the codebase (No weird lookups)
// It would also make runtime generated / meta components much easier
class ComponentID {
private:
static size_t _id;
static size_t next_id;
static std::unordered_map<std::string, size_t> _ids;
public:
// This needs to be a function because otherwise it might not be initialized on time
static std::unordered_map<std::string, size_t>& _ids();
static std::vector<size_t> get_id(std::vector<std::string> names);
// @todo This is slow...
static std::string get_name(size_t id) {
for (auto it = _ids().begin(); it != _ids().end(); ++it) {
if (it->second == id) {
return it->first;
}
template <typename T>
static size_t add(std::string name) {
_ids.insert({name, id<T>});
// @todo Do some other register stuff
return id<T>;
}
throw std::runtime_error("ID does not exist");
static size_t add(std::string name) {
size_t id = next_id++;
_ids.insert({name, id});
return id;
}
// This looks kind of ugly
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 {
Component() : _runtime(false), _id(0) {}
// @todo Would be nice if this worked with templates
Component(size_t id) : _runtime(true), _id(id) {}
Component() {}
virtual ~Component() {}
const bool _runtime;
const size_t _id;
//
// @todo Store parent entity and also make a vector for all components of this type
// 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 {
@@ -73,52 +85,41 @@ namespace ecs {
Entity(uuids::uuid _uuid) : uuid(_uuid) {}
~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>
void add_component(Args... args) {
T* add_component(Args... args) {
size_t id = ComponentID::id<T>;
T* component = new T(args...);
if (_components.find(id) != _components.end()) {
throw std::runtime_error("Component already exists");
}
_components[id] = new T(args...);
return static_cast<T*>(add_component(id, component));
}
template <typename... Ts>
bool has_components() const {
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>
T* get_component() const {
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 {
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;
private:
std::unordered_map<size_t, Component*> _components;
// @todo Store manager, this allows the entity to register components to the manager
};
template <typename... Ts>
@@ -133,34 +134,106 @@ namespace ecs {
}
}
class ViewIterator;
size_t size() const {
return _entities.size();
}
auto begin() {
return _entities.begin();
return ViewIterator(_entities.begin());
}
auto begin() const {
return _entities.begin();
return ConstViewIterator(_entities.begin());
}
auto cbegin() const {
return _entities.cbegin();
return ConstViewIterator(_entities.cbegin());
}
auto end() {
return _entities.end();
return ViewIterator(_entities.end());
}
auto end() const {
return _entities.end();
return ConstViewIterator(_entities.end());
}
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:
std::unordered_map<uuids::uuid, Entity*> _entities;
};
@@ -198,6 +271,15 @@ namespace ecs {
Entity* create_entity(uuids::uuid uuid);
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) {

View File

@@ -3,29 +3,8 @@
#include <iostream>
namespace ecs {
size_t ComponentID::_id;
// This needs to be a function because otherwise it might not be initialized on time
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;
}
size_t ComponentID::next_id = 1;
std::unordered_map<std::string, size_t> ComponentID::_ids;
Entity::~Entity() {
// @todo This does not work...
@@ -36,15 +15,20 @@ namespace ecs {
_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()) {
throw std::runtime_error("Component already exists");
}
_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) {
if (_components.find(id) == _components.end()) {
return false;
@@ -54,10 +38,10 @@ namespace ecs {
return true;
}
Component* Entity::get_component(size_t id) {
Component* Entity::get_component(size_t id) const {
auto it = _components.find(id);
if (it == _components.end()) {
throw std::runtime_error("Component does not exist");
return nullptr;
}
return it->second;

102
flint.lua
View File

@@ -1,102 +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/")
git("https://github.com/glfw/glfw", "3.3-stable", "glfw")
lib "glfw"
include "@glfw@/include"
src "@glfw@/src/{context,init,input,monitor,window}.c"
src "@glfw@/src/egl_context.c"
if config.platform.target == "linux" then
src "@glfw@/src/glx_context.c"
src "@glfw@/src/x11_{init,monitor,window}.c"
src "@glfw@/src/posix_{time,thread}.c"
src "@glfw@/src/linux_joystick.c"
src "@glfw@/src/xkb_unicode.c"
src "@glfw@/src/osmesa_context.c"
define "_GLFW_X11"
-- @todo This is needed for strdup to work
define "_POSIX_C_SOURCE=200809L"
link("dl", "X11", "Xrandr", "Xinerama", "Xcursor", "GL", "GLU")
elseif config.platform.target == "windows" then
end
lang "c11"
warnings(false)
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"
dependency "glfw"
threads()
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,225 +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"
#include <queue>
#include <mutex>
#include <thread>
#include <GLFW/glfw3.h>
// template <typename T>
// class ThreadQueue {
// public:
// void push(const T& value) {
// std::lock_guard<std::mutex> lock(_mutex);
// _queue.push(value);
// }
//
// void pop() {
// std::lock_guard<std::mutex> lock(_mutex);
// _queue.pop();
// }
//
// void front() {
//
// }
//
// private:
// std::queue<T> _queue;
// std::mutex _mutex;
// };
int main() {
std::queue<std::array<uint8_t, 1024>> thread_queue;
GLFWwindow* window;
if (!glfwInit()) {
return -1;
}
window = glfwCreateWindow(640, 480, "Network Client", nullptr, nullptr);
if (!window) {
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
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!");
}
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
throw std::runtime_error("Failed to set timeout");
}
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);
// @todo Resend connect request until we have an ack (or reach timeout)
// @todo Same for requesting initial ids and entities
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");
}
bool connected = true;
uint8_t timeout = TIMEOUT;
std::thread receive([&thread_queue, sock, &from, &from_size, &window, &connected, &timeout] {
while (!glfwWindowShouldClose(window) && connected) {
std::array<uint8_t, 1024> buff;
int bytes_received = recvfrom(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, &from_size);
if (bytes_received < 0) {
timeout--;
if (timeout == 0) {
std::cout << "Connection to server lost!\n";
connected = false;
} else if (timeout < TIMEOUT/3) {
std::cout << "Server connection interrupted\n";
}
} else {
timeout = TIMEOUT;
thread_queue.push(std::move(buff));
}
}
});
while (!glfwWindowShouldClose(window) && connected) {
glfwSwapBuffers(window);
glfwPollEvents();
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
buff_stream_out.seekp(0, std::ios::beg);
iohelper::write<uint8_t>(buff_stream_out, PACKET::EVENT);
iohelper::write<std::string>(buff_stream_out, "up");
if (sendto(sock, buff.data(), buff.size(), 0, to, to_size) < 0) {
throw std::runtime_error("Failed to connect to server");
}
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
buff_stream_out.seekp(0, std::ios::beg);
iohelper::write<uint8_t>(buff_stream_out, PACKET::EVENT);
iohelper::write<std::string>(buff_stream_out, "down");
if (sendto(sock, buff.data(), buff.size(), 0, to, to_size) < 0) {
throw std::runtime_error("Failed to connect to server");
}
}
while (!thread_queue.empty()) {
std::array<uint8_t, 1024> buff = thread_queue.front();
iohelper::imemstream_fixed<buff.size()> buff_stream_in(buff);
buff_stream_in.seekg(0, std::ios::beg);
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;
}
case PACKET::PING: {
buff_stream_out.seekp(0, std::ios::beg);
iohelper::write<uint8_t>(buff_stream_out, PACKET::PING);
if (sendto(sock, buff.data(), buff.size(), 0, to, to_size) < 0) {
throw std::runtime_error("Failed to connect to server");
}
break;
}
default:
std::cout << "Unknown packet type: " << (uint32_t)type << '\n';
}
// dump_buffer<buff.size()>(buff);
std::cout << "PACKET TYPE: " << type << '\n';
thread_queue.pop();
}
}
std::cout << "Disconnecting\n";
buff_stream_out.seekp(0, std::ios::beg);
iohelper::write<uint8_t>(buff_stream_out, PACKET::DISCONNECT);
if (sendto(sock, buff.data(), buff.size(), 0, to, to_size) < 0) {
throw std::runtime_error("Failed to connect to server");
}
std::cout << "Waiting for thread to join\n";
shutdown(sock, SHUT_RDWR);
receive.join();
std::cout << "Shutting down\n";
glfwTerminate();
return 0;
}

View File

@@ -1,270 +0,0 @@
#include <iostream>
#include <thread>
#include <functional>
#include <array>
#include <unordered_set>
#include <sstream>
#include <netinet/in.h>
#include "ecs-serial.h"
#include "ecs_network_components.h"
#include "iohelper/memstream.h"
#include "packets.h"
#include "debug.h"
bool operator==(const sockaddr_in& lhs, const sockaddr_in& rhs) {
return lhs.sin_addr.s_addr == rhs.sin_addr.s_addr && lhs.sin_port == rhs.sin_port;
}
namespace std {
template <>
struct hash<sockaddr_in> {
size_t operator()(const sockaddr_in& from) const {
size_t addr_hash = hash<unsigned long>()(from.sin_addr.s_addr);
size_t port_hash = hash<unsigned short>()(from.sin_port);
addr_hash ^= port_hash + 0x9e3779b9 + (addr_hash << 6) + (addr_hash >> 2);
return addr_hash;
}
};
}
struct Client {
uint8_t timeout = TIMEOUT;
ecs::Entity* player = nullptr;
};
ecs::Manager manager;
std::unordered_map<sockaddr_in, Client> clients = {};
std::string get_ip(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::stringstream ss;
ss << bytes[0] << '.' << bytes[1] << '.' << bytes[2] << '.' << bytes[3];
return ss.str();
}
void send_entities(int sock, sockaddr_in from) {
std::array<uint8_t, 1024> buff;
iohelper::omemstream_fixed<buff.size()> buff_stream(buff);
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");
}
}
void send_ids(int sock, sockaddr_in from) {
std::array<uint8_t, 1024> buff;
iohelper::omemstream_fixed<buff.size()> buff_stream(buff);
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");
}
}
void on_connect(int sock, sockaddr_in from) {
std::cout << "Client connected: " << std::dec << get_ip(from) << ':' << from.sin_port << '\n';
ecs::Entity* player = manager.create_entity();
player->add_component<Position>(0, 0);
Client client = {TIMEOUT, player};
clients.insert({from, client});
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");
}
send_ids(sock, from);
send_entities(sock, from);
}
void send_ping(int sock) {
for (auto i = clients.begin(), last = clients.end(); i != last; ) {
auto& from = i->first;
auto& timeout = i->second.timeout;
timeout--;
if (timeout < TIMEOUT/2) {
std::array<uint8_t, 1024> buff;
iohelper::omemstream_fixed<buff.size()> buff_stream(buff);
iohelper::write<uint8_t>(buff_stream, PACKET::PING);
if (sendto(sock, buff.data(), buff.size(), 0, (sockaddr*)&from, sizeof(from)) < 0) {
throw std::runtime_error("Failed to send ping to client");
}
}
if (timeout == 0) {
std::cout << "Client timed-out: " << std::dec << get_ip(from) << ':' << from.sin_port << '\n';
manager.remove_entity(i->second.player);
i = clients.erase(i);
} else {
++i;
}
}
}
// 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!");
}
auto it = clients.find(from);
if (it != clients.end()) {
it->second.timeout = TIMEOUT;
} else {
std::cout << "Unknown client\n";
}
switch (iohelper::read<uint8_t>(buff_stream)) {
case PACKET::CONNECT:
on_connect(sock, from);
break;
case PACKET::DISCONNECT: {
auto it = clients.find(from);
if (it == clients.end()) {
std::cout << "Unknown client tried to disconnect\n";
break;
}
std::cout << "Client disconnected: " << std::dec << get_ip(from) << ':' << from.sin_port << '\n';
manager.remove_entity(it->second.player);
it = clients.erase(it);
break;
}
case PACKET::IDS:
send_ids(sock, from);
break;
case PACKET::ENTITIES:
send_entities(sock, from);
break;
case PACKET::EVENT: {
std::string event = iohelper::read<std::string>(buff_stream);
auto player = it->second.player;
if (!player) {
break;
}
if (!event.compare("up")) {
std::cout << "Moving up\n";
player->get_component<Position>()->x += 0.1f;
} else if (!event.compare("down")) {
std::cout << "Moving down\n";
player->get_component<Position>()->x -= 0.1f;
}
for (auto [from, timeout] : clients) {
send_entities(sock, from);
}
break;
}
// Maybe calculate the ping time here?
case PACKET::PING: {
break;
}
default:
std::cout << "Unknown packet\n";
break;
}
}
}
int main() {
/// Setup ECS
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);
// }
/// Setup socket
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!");
}
/// Start listener
std::thread listen(std::bind(listener, sock));
// @todo Add ping thread
std::cout << "Server started!\n";
/// Run server logic
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
send_ping(sock);
manager.view<Position>().for_each([](ecs::Entity* entity, Position* position) {
std::cout << entity->uuid << ' ' << position->x << ' ' << position->y << '\n';
});
}
/// Waint for threads to join
listen.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,15 +0,0 @@
#pragma once
#include <cstdint>
#define TIMEOUT 20
enum PACKET : uint8_t {
CONNECT,
DISCONNECT,
PING,
ACK,
IDS,
ENTITIES,
EVENT
};

View File

@@ -1,5 +1,6 @@
#! /usr/bin/env python3
import sys
import os
import clang.cindex
from mako.template import Template
@@ -63,21 +64,35 @@ def build_components(cursor, filename):
return result
def main(argv):
if len(sys.argv) != 2:
print("Usage: {} [header filename]".format(argv[0]))
if len(sys.argv) != 3:
print("Usage: {} [header filename] [output filename]".format(argv[0]))
sys.exit()
filename = argv[1]
output = argv[2]
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)
tpl = Template(filename='template.mako')
tpl = Template(filename=f'template.mako')
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__":
main(sys.argv)

View File

@@ -1,7 +1,7 @@
#include "${include_file}"
#if __has_include("ecs-lua.h")
#include "sol.hpp"
#include "sol/sol.hpp"
namespace generated {
void init(sol::state& lua) {
@@ -10,20 +10,23 @@ namespace generated {
preload["ecs.${c.name}"] = [&lua] {
sol::table component = lua.create_table();
component["_id"] = ecs::ComponentID::id<${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:
% if len(con.parameters) > 0:
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
% endfor
lua.set_function("_internal_to_${c.name}", [] (ecs::Component* component) {
return (${c.name}*)component;
component.set_function("_convert", [] (ecs::Component* component) {
return static_cast<${c.name}*>(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
#include <string>
#include "ecs.h"
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 "ecs-lua.h"
#include "ecs-serial.h"
#include <bits/stdint-intn.h>
#include <bits/stdint-uintn.h>
#include <cstddef>
#include <iostream>
#include <fstream>
@@ -16,6 +12,14 @@
#include "ecs.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) {
std::cerr << "Lua error, aborting!\n";
if (maybe_msg) {
@@ -40,6 +44,10 @@ int main(int argc, const char** argv) {
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>);
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();
// @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) {
ecs::Manager manager;
@@ -149,6 +68,14 @@ int main(int argc, const char** argv) {
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";
manager.view<Position, Velocity>().for_each([](ecs::Entity*, Position* pos, Velocity* vel) {
pos->x += vel->x;
@@ -161,19 +88,12 @@ int main(int argc, const char** argv) {
std::cout << "Y: " << pos->y << '\n';
});
// These are really just an internal api that should not be used
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;
random["a"] = 21;
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';
};
std::cout << "Adding Orb!\n";
manager.create_entity<Orb>(1.2f, 3.4f);
for (auto [ent, pos, vel] : manager.view<Position, Velocity>()) {
std::cout << "X: " << pos->x << " + " << vel->x << '\n';
std::cout << "Y: " << pos->y << " + " << vel->y << '\n';
}
}
// Test serialization
@@ -199,8 +119,8 @@ int main(int argc, const char** argv) {
std::ofstream file("entities", std::ios::out | std::ios::trunc);
ecs::serial::serialize_ids(file);
iohelper::write_length(file, manager.view<>().size());
for (auto [uuid, entity] : manager.view<>()) {
io::write<size_t>(file, manager.view<>().size());
for (auto [entity] : manager.view<>()) {
ecs::serial::serialize(file, entity);
}
file.close();
@@ -216,12 +136,20 @@ int main(int argc, const char** argv) {
{
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
std::ifstream file("entities", std::ios::in);
if (!file.is_open()) {
std::cerr << "Failed to open: 'entities'!\n";
}
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();
for (size_t i = 0; i < entity_count; ++i) {
ecs::serial::deserialize(file, manager);
@@ -250,11 +178,13 @@ int main(int argc, const char** argv) {
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';
}
}

View File

@@ -1,23 +1,39 @@
local ecs = require "ecs"
-- local test = require "ecs_test"
require "ecs"
local Position = require "ecs.Position"
local Velocity = require "ecs.Velocity"
local Meta = require "ecs.Meta"
local Wrapper = require 'ecs.Wrapper'
local LuaComponent = require 'ecs.LuaComponent'
function init()
print("Creating LuaComponents")
LuaData = LuaComponent.create("LuaData", {
speed = "number",
something = "string",
alive = "boolean"
})
TestThing = LuaComponent.create("TestThing", {
test = "string"
})
end
function run()
print("Running!")
manager = get_manager()
ent = manager:create_entity()
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 = {
ent:add_component(LuaData.new({
speed = 10,
something = "Hello, World!",
alive = true
}
ent:add_component(Wrapper.new("LuaData", data))
ent:add_component(Wrapper.new("TestThing", { test = 1.23563 }))
}))
ent:add_component(TestThing.new({
test = "This is a test!"
}))
-- @todo Make this happen...
-- stats = {
@@ -27,18 +43,18 @@ ent:add_component(Wrapper.new("TestThing", { test = 1.23563 }))
--
-- ent:add_component(ecs.Wrapper.new("Stats", stats))
print(ent:has_components("Position", "Velocity"))
pos = ent:get_component("Position")
print(ent:has_components(Position, Velocity))
pos = ent:get_component(Position)
print("x: " .. pos.x)
print("y: " .. pos.y)
vel = ent:get_component("Velocity")
vel = ent:get_component(Velocity)
print("v_x: " .. vel.x)
print("v_y: " .. vel.y)
print("View test")
manager:view("Position", "Velocity"):for_each(function(ent)
pos = ent:get_component("Position")
vel = ent:get_component("Velocity")
manager:view(Position, Velocity):for_each(function(ent)
pos = ent:get_component(Position)
vel = ent:get_component(Velocity)
pos.x = pos.x + vel.x
pos.y = pos.y + vel.y
@@ -51,8 +67,8 @@ manager:view("Position", "Velocity"):for_each(function(ent)
print("v_x: " .. vel.x)
print("v_y: " .. vel.y)
if ent:has_components("Meta") then
meta = ent:get_component("Meta")
if ent:has_components(Meta) then
meta = ent:get_component(Meta)
print("name: " .. meta.name)
end
end)
@@ -62,26 +78,25 @@ end)
-- print "TEST"
-- end
manager:view("LuaData"):for_each(function(ent)
d = ent:get_component("LuaData")
manager:view(LuaData):for_each(function(ent)
d = ent:get_component(LuaData)
print(d.speed)
print("speed: " .. d.speed)
d.speed = 11
print(d.speed)
print(data.speed)
data.speed = 20
print(d.speed)
print(data.speed)
print("speed: " .. d.speed)
print(d.alive)
print("alive: ") print(d.alive)
d.alive = false
print(d.alive)
print("alive: ") print(d.alive)
print(d.something)
print("Something: " .. d.something)
end)
manager:view("TestThing"):for_each(function(ent)
t = ent:get_component("TestThing")
manager:view(TestThing):for_each(function(ent)
t = ent:get_component(TestThing)
print(t.test)
print("test: " .. t.test)
end)
print("Done running!")
end

View File

@@ -1,16 +1,34 @@
require "ecs"
require "ecs.Wrapper"
local LuaComponent = require "ecs.LuaComponent"
function init()
print("Registering components")
-- Swapped compared to test.lua
TestThing = LuaComponent.create("TestThing", {
test = "string"
})
LuaData = LuaComponent.create("LuaData", {
speed = "number",
something = "string",
alive = "boolean"
})
end
function run()
print("Running!")
manager = get_manager()
manager:view("TestThing"):for_each(function(ent)
data = ent:get_component("TestThing")
manager:view(TestThing):for_each(function(ent)
data = ent:get_component(TestThing)
print("test: " .. data.test)
end)
manager:view("LuaData"):for_each(function(ent)
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")
data = ent:get_component(LuaData)
print("speed: " .. data.speed)
print("something: " .. data.something)
-- @todo You cannot concatenate bool to string
@@ -23,4 +41,8 @@ end)
-- 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