diff --git a/.gitignore b/.gitignore index fe6e067a08..f1a58f605a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ __pycache__ /.bin /build /deps +/.ccls-cache # root dir run script /run @@ -56,7 +57,7 @@ perf.data* .gdb_history # code search -/.agignore +/.ignore /.globalrc /GPATH /GRTAGS diff --git a/CMakeLists.txt b/CMakeLists.txt index 1238df25e8..84f943a75b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ set(CYTHON_MIN_VERSION 0.25) # CMake policies foreach(pol + CMP0074 # use _ROOT vars in find_package() CMP0067 # honor language standard in try_compile() CMP0071 # enable automoc for generated files CMP0072 # prefers GLVND by default FindOpenGL diff --git a/assets/nyan/pong.nyan b/assets/nyan/pong.nyan new file mode 100644 index 0000000000..e2a0f1dc7a --- /dev/null +++ b/assets/nyan/pong.nyan @@ -0,0 +1,30 @@ +PongGame(): + ball : Ball + player1 : Player + player2 : Player + +Ball(): + Color(): + r : int = 0 + g : int = 200 + b : int = 0 + color : Color = Color + +Player(): + lifes : int = 3 + size : int = 200 + +GameTest(PongGame): + ball = Ball + player1 = Player + player2 = Player + +LeftColor(): + r = 0 + g = 20 + b = 230 + +RightColor(): + r = 180 + g = 40 + b = 0 diff --git a/buildsystem/codecompliance/headerguards.py b/buildsystem/codecompliance/headerguards.py index c35090ed83..30427d3f65 100644 --- a/buildsystem/codecompliance/headerguards.py +++ b/buildsystem/codecompliance/headerguards.py @@ -1,4 +1,4 @@ -# Copyright 2014-2020 the openage authors. See copying.md for legal info. +# Copyright 2014-2021 the openage authors. See copying.md for legal info. """ Verifies the guard macros of all C++ header files. @@ -45,7 +45,7 @@ def find_issues(dirname): match = GUARD_RE.match(data) if not match: - raise HeaderIssue("No valid header guard found") + raise HeaderIssue("No valid header guard found (e.g. #pragma once)") except HeaderIssue as exc: yield (f"header guard issue in {fname}", exc.args[0], None) diff --git a/buildsystem/pxdgen.py b/buildsystem/pxdgen.py index 68dc981c67..70d3cfaded 100644 --- a/buildsystem/pxdgen.py +++ b/buildsystem/pxdgen.py @@ -1,4 +1,4 @@ -# Copyright 2015-2020 the openage authors. See copying.md for legal info. +# Copyright 2015-2021 the openage authors. See copying.md for legal info. """ Auto-generates PXD files from annotated C++ headers. @@ -334,7 +334,7 @@ def postprocess_annotation_line(self, annotation): Post-processes each individual annotation line, applying hacks and testing it, etc. - See openage/pyinterface/hacks.h for documentation on the individual + See libopenage/pyinterface/hacks.h for documentation on the individual hacks. """ annotation = annotation.rstrip() @@ -383,7 +383,7 @@ def generate(self, pxdfile, ignore_timestamps=False, print_warnings=True): outfile.write(result) if print_warnings and self.warnings: - print("\x1b[33;1mWARNING\x1b[m pxdgen[" + self.filename + "]:") + print("\x1b[33;1mWARNING\x1b[m pxdgen[%s]:" % self.filename) for warning in self.warnings: print(warning) diff --git a/configure b/configure index a0e6c0341a..d3f2ff520a 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2013-2020 the openage authors. See copying.md for legal info. +# Copyright 2013-2021 the openage authors. See copying.md for legal info. """ openage autocancer-like cmake frontend. @@ -290,7 +290,6 @@ def invoke_cmake(args, bindir, defines, options): invocation.append(raw_cmake_arg) - invocation.append('--') invocation.append(project_root) # look for traces of an in-source build diff --git a/etc/valgrind-python.supp b/etc/valgrind-python.supp index 392c1a9061..c723f91da4 100644 --- a/etc/valgrind-python.supp +++ b/etc/valgrind-python.supp @@ -1,39 +1,49 @@ # -# This is a valgrind suppression file that should be used -# when running Python with valgrind. +# This is a valgrind suppression file that should be used when using valgrind. +# +# It was taken from the cpython project and adapted for openage. +# Upstream URL is: https://raw.githubusercontent.com/python/cpython/master/Misc/valgrind-python.supp +# # # Here's an example of running valgrind: # -# cd python/dist/src -# valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \ +# cd openage/bin/ +# PYTHONMALLOC=malloc valgrind --tool=memcheck --suppressions=../etc/valgrind-python.supp \ # python3 -m openage test -a # -# python can be compiled with --with-valgrind to enhance the experience +# Python can be compiled with --with-valgrind to enhance the valgrind experience: +# valgrind presence is detected and python memory is allocated more valgrind-friendly then. +# # Python 3.6 supports PYTHONMALLOC=malloc environment var to force malloc() - +# +# Disable the suppressions for _PyObject_Free and _PyObject_Realloc below by comment +# if valgrind shall complain about python memory leaks. +# +# See cpython/Misc/README.valgrind for more information +# # all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Addr4 - fun:Py_ADDRESS_IN_RANGE + fun:address_in_range } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Value4 - fun:Py_ADDRESS_IN_RANGE + fun:address_in_range } { ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64 aka amd64) Memcheck:Value8 - fun:Py_ADDRESS_IN_RANGE + fun:address_in_range } { ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value Memcheck:Cond - fun:Py_ADDRESS_IN_RANGE + fun:address_in_range } # @@ -123,61 +133,61 @@ { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Addr4 - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Value4 - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Use of uninitialised value of size 8 Memcheck:Addr8 - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Use of uninitialised value of size 8 Memcheck:Value8 - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value Memcheck:Cond - fun:PyObject_Free + fun:_PyObject_Free } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Addr4 - fun:PyObject_Realloc + fun:_PyObject_Realloc } { ADDRESS_IN_RANGE/Invalid read of size 4 Memcheck:Value4 - fun:PyObject_Realloc + fun:_PyObject_Realloc } { ADDRESS_IN_RANGE/Use of uninitialised value of size 8 Memcheck:Addr8 - fun:PyObject_Realloc + fun:_PyObject_Realloc } { ADDRESS_IN_RANGE/Use of uninitialised value of size 8 Memcheck:Value8 - fun:PyObject_Realloc + fun:_PyObject_Realloc } { ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value Memcheck:Cond - fun:PyObject_Realloc + fun:_PyObject_Realloc } ### diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index c99c0506af..40cf7ad403 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -190,12 +190,13 @@ endif() # ncurses support if(WANT_NCURSES) set(CURSES_NEED_NCURSES TRUE) + set(CURSES_NEED_WIDE TRUE) find_package(Curses) endif() if(WANT_NCURSES AND CURSES_FOUND) have_config_option(ncurses NCURSES true) - target_include_directories(libopenage PRIVATE ${Curses_INCLUDE_DIRS}) + target_include_directories(libopenage PRIVATE ${CURSES_INCLUDE_DIRS}) target_link_libraries(libopenage PRIVATE ${CURSES_LIBRARIES}) else() have_config_option(ncurses NCURSES false) @@ -235,6 +236,8 @@ get_config_option_string() configure_file(config.h.in config.h) configure_file(config.cpp.in config.cpp) +configure_file(version.h.in version.h) +configure_file(version.cpp.in version.cpp) configure_file( "${CMAKE_SOURCE_DIR}/openage/config.py.in" @@ -325,6 +328,7 @@ add_sources(libopenage screenshot.cpp texture.cpp ${CMAKE_CURRENT_BINARY_DIR}/config.cpp + ${CMAKE_CURRENT_BINARY_DIR}/version.cpp ${CODEGEN_SCU_FILE} ) @@ -340,19 +344,21 @@ add_subdirectory("coord") add_subdirectory("curve") add_subdirectory("cvar") add_subdirectory("datastructure") -add_subdirectory("event") -add_subdirectory("gui") add_subdirectory("error") +add_subdirectory("event") add_subdirectory("gamestate") +add_subdirectory("gui") add_subdirectory("input") -add_subdirectory("log") add_subdirectory("job") +add_subdirectory("log") +add_subdirectory("main") add_subdirectory("pathfinding") +add_subdirectory("presenter") add_subdirectory("pyinterface") add_subdirectory("renderer") add_subdirectory("rng") -add_subdirectory("simulation") add_subdirectory("shader") +add_subdirectory("simulation") add_subdirectory("terrain") add_subdirectory("testing") add_subdirectory("unit") diff --git a/libopenage/curve/CMakeLists.txt b/libopenage/curve/CMakeLists.txt index fd797510ab..5a74be80e7 100644 --- a/libopenage/curve/CMakeLists.txt +++ b/libopenage/curve/CMakeLists.txt @@ -1,14 +1,17 @@ add_sources(libopenage + base_curve.cpp continuous.cpp curve.cpp discrete.cpp + interpolated.cpp iterator.cpp + keyframe.cpp keyframe_container.cpp map.cpp map_filter_iterator.cpp queue.cpp queue_filter_iterator.cpp - value_container.cpp + segmented.cpp ) add_subdirectory("tests") diff --git a/libopenage/curve/base_curve.cpp b/libopenage/curve/base_curve.cpp new file mode 100644 index 0000000000..4fd47917cc --- /dev/null +++ b/libopenage/curve/base_curve.cpp @@ -0,0 +1,7 @@ +// Copyright 2017-2019 the openage authors. See copying.md for legal info. + +#include "base_curve.h" + +namespace openage::curve { + +} // openage::curve diff --git a/libopenage/curve/base_curve.h b/libopenage/curve/base_curve.h new file mode 100644 index 0000000000..e1d7b0d289 --- /dev/null +++ b/libopenage/curve/base_curve.h @@ -0,0 +1,193 @@ +// Copyright 2017-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "keyframe_container.h" +#include "../event/evententity.h" +#include "../event/loop.h" + + +namespace openage::curve { + +template +class BaseCurve : public event::EventEntity { +public: + BaseCurve(const std::shared_ptr &loop, + size_t id, + const std::string &idstr="", + const EventEntity::single_change_notifier ¬ifier=nullptr) + : + EventEntity(loop, notifier), + _id{id}, + _idstr{idstr}, + loop{loop}, + last_element{this->container.begin()} {} + + virtual ~BaseCurve() = default; + + virtual T get(const time_t &t) const = 0; + + virtual T operator ()(const time_t &now) { + return get(now); + } + + virtual std::pair frame(const time_t &) const; + virtual std::pair next_frame(const time_t &) const; + + /** + * Insert/overwrite given value at given time and erase all elements + * that follow at a later time. + * If multiple elements exist at the given time, + * overwrite the last one. + */ + virtual void set_last(const time_t &at, const T &value); + + /** + * Insert a value at the given time. + * If there already is a value at this time, + * the value is inserted directly after the existing one. + */ + virtual void set_insert(const time_t &at, const T &value); + + /** + * Insert a value at the given time. + * If there already is a value at this time, + * the given value will replace the first value with the same time. + */ + virtual void set_replace(const time_t &at, const T &value); + + /** + * Remove all values that have the given time. + */ + virtual void erase(const time_t &at); + + /** + * Integrity check, for debugging/testing reasons only. + */ + void check_integrity() const; + + size_t id() const override { + return this->_id; + } + + std::string idstr() const override { + if (this->_idstr.size() == 0) { + return std::to_string(this->id()); + } + return this->_idstr; + } + + /** + * Get a string representation of the curve. + */ + std::string str() const; + +protected: + /** + * Stores all the keyframes + */ + KeyframeContainer container; + + /** + * Identifier for the container + */ + const size_t _id; + + /** + * Human-readable identifier for the container + */ + const std::string _idstr; + + /** + * The eventloop this curve was registered to + */ + const std::shared_ptr loop; + + /** + * Cache the iterator for quickly finding the end + */ + mutable typename KeyframeContainer::iterator last_element; +}; + + +template +void BaseCurve::set_last(const time_t &at, const T &value) { + auto hint = this->container.last(at, this->last_element); + + // erase max one same-time value + if (hint->time == at) { + hint--; + } + + hint = this->container.erase_after(hint); + + this->container.insert_before(at, value, hint); + this->last_element = hint; + + this->changes(at); +} + + +template +void BaseCurve::set_insert(const time_t &at, const T &value) { + this->container.insert_after(at, value, this->last_element); + // TODO: check if this is now the last element, then remember it + this->changes(at); +} + + +template +void BaseCurve::set_replace(const time_t &at, const T &value) { + this->container.insert_overwrite(at, value, this->last_element); + this->changes(at); +} + + +template +void BaseCurve::erase(const time_t &at) { + this->last_element = this->container.erase(at, this->last_element); + this->changes(at); +} + + +template +std::pair BaseCurve::frame(const time_t &time) const { + auto e = this->container.last(time, this->container.end()); + return std::make_pair(e->time, e->value); +} + + +template +std::pair BaseCurve::next_frame(const time_t &time) const { + auto e = this->container.last(time, this->container.end()); + e++; + return std::make_pair(e->time, e->value); +} + +template +std::string BaseCurve::str() const { + std::stringstream ss; + ss << "Curve[" << this->idstr() << "]{" << std::endl; + for (const auto &keyframe : this->container) { + ss << " " << keyframe.time << ": " << keyframe.value << "," << std::endl; + } + ss << "}"; + + return ss.str(); +} + +template +void BaseCurve::check_integrity() const { + time_t last_time = std::numeric_limits::min(); + for (const auto &keyframe : this->container) { + if (keyframe.time < last_time) { + throw Error{ERR << "curve is broken after t=" << last_time << ": " << this->str()}; + } + last_time = keyframe.time; + } +} + + +} // openage::curve diff --git a/libopenage/curve/continuous.h b/libopenage/curve/continuous.h index 77aa5dac59..0d32f14629 100644 --- a/libopenage/curve/continuous.h +++ b/libopenage/curve/continuous.h @@ -1,63 +1,84 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once #include +#include -#include "value_container.h" +#include "interpolated.h" #include "../log/log.h" namespace openage::curve { /** - * Continuous Datatype. - * Stores a value container with continuous access. + * Continuous linear curve. + * + * At one point in time, there can be only one value, + * thus, there can't be jumps. All values are connected through linear + * interpolation. + * * The bound template type T has to implement `operator+(T)` and * `operator*(time_t)`. */ template -class Continuous : public ValueContainer { +class Continuous : public Interpolated { public: - using ValueContainer::ValueContainer; + using Interpolated::Interpolated; + /** - * will interpolate between the keyframes linearly based on the time. + * Insert/overwrite given value at given time and erase all elements + * that follow at a later time. + * If multiple elements exist at the given time, + * overwrite all of them. */ - T get(const time_t &) const override; -}; + void set_last(const time_t &t, const T &value) override; + /** This just calls set_replace in order to guarantee the continuity. */ + void set_insert(const time_t &t, const T &value) override; + + /** human readable identifier */ + std::string idstr() const override; +}; template -T Continuous::get(const time_t &time) const { - const auto &e = this->container.last(time, this->last_element); - this->last_element = e; +void Continuous::set_last(const time_t &at, const T &value) { + auto hint = this->container.last(at, this->last_element); - auto nxt = e; - ++nxt; + // erase all same-time entries + while (hint->time == at) { + hint--; + } - time_t diff_time = 0; + hint = this->container.erase_after(hint); - auto offset = time - e->time; + this->container.insert_before(at, value, hint); + this->last_element = hint; - // If we do not have a next (buffer underrun!!) we assign values - if (nxt == this->container.end()) { - // log::log(WARN << "Continuous buffer underrun. This might be bad! Assuming constant."); - } else { - diff_time = nxt->time - e->time; - } + this->changes(at); +} - if (nxt == this->container.end() // use the last curve value - || offset == 0 // values equal -> don't need to interpolate - || diff_time == 0) { // values at the same time -> division-by-zero-error - return e->value; - } else { - // Interpolation between time(now) and time(next) that has elapsed - double elapsed_frac = offset.to_double() / diff_time.to_double(); +template +void Continuous::set_insert(const time_t &t, const T &value) { + this->set_replace(t, value); +} + - return e->value + (nxt->value - e->value) * elapsed_frac; +template +std::string Continuous::idstr() const { + std::stringstream ss; + ss << "ContinuousCurve["; + if (this->_idstr.size()) { + ss << this->_idstr; + } + else { + ss << this->id(); } + ss << "]"; + return ss.str(); } + } // openage::curve diff --git a/libopenage/curve/discrete.h b/libopenage/curve/discrete.h index 63abf65638..2d95a17b78 100644 --- a/libopenage/curve/discrete.h +++ b/libopenage/curve/discrete.h @@ -1,11 +1,12 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2019 the openage authors. See copying.md for legal info. #pragma once #include #include +#include -#include "value_container.h" +#include "base_curve.h" namespace openage::curve { @@ -15,13 +16,13 @@ namespace openage::curve { * implement `operator=` and copy ctor. */ template -class Discrete : public ValueContainer { +class Discrete : public BaseCurve { static_assert(std::is_copy_assignable::value, "Template type is not copy assignable"); static_assert(std::is_copy_constructible::value, "Template type is not copy constructible"); public: - using ValueContainer::ValueContainer; + using BaseCurve::BaseCurve; /** * Does not interpolate anything, @@ -29,6 +30,9 @@ class Discrete : public ValueContainer { */ T get(const time_t &t) const override; + /** human readable id string */ + std::string idstr() const override; + /** * Return the last time and keyframe with time <= t. */ @@ -49,6 +53,21 @@ T Discrete::get(const time_t &time) const { } +template +std::string Discrete::idstr() const { + std::stringstream ss; + ss << "DiscreteCurve["; + if (this->_idstr.size()) { + ss << this->_idstr; + } + else { + ss << this->id(); + } + ss << "]"; + return ss.str(); +} + + template std::pair Discrete::get_time(const time_t &time) const { auto e = this->container.last(time, this->last_element); diff --git a/libopenage/curve/interpolated.cpp b/libopenage/curve/interpolated.cpp new file mode 100644 index 0000000000..e1dc1fccbc --- /dev/null +++ b/libopenage/curve/interpolated.cpp @@ -0,0 +1,3 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#include "interpolated.h" diff --git a/libopenage/curve/interpolated.h b/libopenage/curve/interpolated.h new file mode 100644 index 0000000000..0a6ef9b45f --- /dev/null +++ b/libopenage/curve/interpolated.h @@ -0,0 +1,76 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "base_curve.h" +#include "../log/log.h" + +namespace openage::curve { + +/** + * Interpolation base class. + * + * Extends the value container to support interpolation between values. + * + * The bound template type T has to implement `operator +(T)` and + * `operator *(time_t)`. + */ +template +class Interpolated : public BaseCurve { +public: + using BaseCurve::BaseCurve; + + /** + * Will interpolate between the keyframes linearly based on the time. + * Picks two adjacent keyframes for interpolation. + * Uses the leftmost element of the right keyframe group with same times + * and the rightmost element of the left keyframe group with same times. + * A curve of [0:0, 1:5, 1:10, 2:20] evaluated at t=0.5 is 2.5, t=1 is 10, + * at t=1.5 is 15. + * + * example for a <= t <= b: + * val([a:x, b:y], t) = x + (y - x)/(b - a) * (t - a) + */ + + T get(const time_t &) const override; +}; + + + +template +T Interpolated::get(const time_t &time) const { + const auto &e = this->container.last(time, this->last_element); + this->last_element = e; + + auto nxt = e; + ++nxt; + + time_t interval = 0; + + auto offset = time - e->time; + + if (nxt != this->container.end()) { + interval = nxt->time - e->time; + } + + // here, offset > interval will never hold. + // otherwise the underlying storage is broken. + + // If the next element is at the same time, just return the value of this one. + if (nxt == this->container.end() // use the last curve value + || offset == 0 // values equal -> don't need to interpolate + || interval == 0) { // values at the same time -> division-by-zero-error + + return e->value; + } + else { + // Interpolation between time(now) and time(next) that has elapsed + double elapsed_frac = offset.to_double() / interval.to_double(); + return e->value + (nxt->value - e->value) * elapsed_frac; + } +} + + +} // openage::curve diff --git a/libopenage/curve/keyframe.cpp b/libopenage/curve/keyframe.cpp new file mode 100644 index 0000000000..c497e7d869 --- /dev/null +++ b/libopenage/curve/keyframe.cpp @@ -0,0 +1,3 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#include "keyframe.h" diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h new file mode 100644 index 0000000000..a0fc39d1e6 --- /dev/null +++ b/libopenage/curve/keyframe.h @@ -0,0 +1,40 @@ +// Copyright 2019-2019 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "curve.h" + +namespace openage::curve { + +/** + * A element of the curvecontainer. This is especially used to keep track of + * the value-timing. + */ +template +class Keyframe { +public: + /** + * New default object at numericlimits