From 52cbd6b38ab3ec0bc0bef1842af171f571be5b1b Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 23 Jul 2023 01:18:57 +0200 Subject: [PATCH 01/49] doc: New component doc drafts. --- doc/code/curves.md | 21 +++++ doc/code/event_system.md | 10 +++ doc/code/game_simulation/README.md | 65 +++++++++++++++ doc/code/game_simulation/activity.md | 80 ++++++++++++++++++ doc/code/game_simulation/components.md | 39 +++++++++ doc/code/game_simulation/game_entity.md | 103 ++++++++++++++++++++++++ doc/code/game_simulation/systems.md | 10 +++ 7 files changed, 328 insertions(+) create mode 100644 doc/code/curves.md create mode 100644 doc/code/event_system.md create mode 100644 doc/code/game_simulation/README.md create mode 100644 doc/code/game_simulation/activity.md create mode 100644 doc/code/game_simulation/components.md create mode 100644 doc/code/game_simulation/game_entity.md create mode 100644 doc/code/game_simulation/systems.md diff --git a/doc/code/curves.md b/doc/code/curves.md new file mode 100644 index 0000000000..726cc2d98f --- /dev/null +++ b/doc/code/curves.md @@ -0,0 +1,21 @@ +# Curves + +## Overview + +## Motivation + +## Curve Types + +### Primitive + +#### Discrete + +#### Continuous + +#### Segmented + +### Container + +## Queue + +## Unordered Map diff --git a/doc/code/event_system.md b/doc/code/event_system.md new file mode 100644 index 0000000000..2f0a195872 --- /dev/null +++ b/doc/code/event_system.md @@ -0,0 +1,10 @@ +# Event System + +## Overview + +1. [Event System](#event-system) + 1. [Overview](#overview) + 2. [Architecture](#architecture) + + +## Architecture diff --git a/doc/code/game_simulation/README.md b/doc/code/game_simulation/README.md new file mode 100644 index 0000000000..d5202a80c2 --- /dev/null +++ b/doc/code/game_simulation/README.md @@ -0,0 +1,65 @@ +# Game Simulation + +- Components for simulating gameplay + +## Overview + + + +## Architecture + +- Hierarchy + - game simulation + - game + - universe + - terrain + - world + - game entity + - activity manager + - components + - systems + + +- GameSimulation + - initialized by engine + - controls overarching control parameters + - event loop + - time loop + - factory for creating game entity + - mod manager + - game + - runs the main event loop + - advances game by executing events for time t from time loop + +- Game + - manages current game session + - store information about the running game + - settings + - game data (from nyan database) + - current game state + - players + - game entities + - other objects + - universe + +- universe + - manages "physical" objects inside the game + - terrain + - game entities (via World reference) + - may be integrated into gamestate class in later versions + +- world + - manages game entities inside the game + - may be integrated into gamestate class in later versions + +- terrain + - manages the terrain in the game world + - may be integrated into gamestate class in later versions + +- game entity + - object inside the game world (e.g. unit/building/tree/ambience) + - capabilties defined as components + - game data from nyan db + - runtime data + - controlled by events handled by manager class + - altered/controlled by systems diff --git a/doc/code/game_simulation/activity.md b/doc/code/game_simulation/activity.md new file mode 100644 index 0000000000..451a62c9f9 --- /dev/null +++ b/doc/code/game_simulation/activity.md @@ -0,0 +1,80 @@ +# Activity Control Flow + +- Configurable control flow for game entities +- using a node graph + +1. [Motivation](#motivation) +2. [Architecture](#architecture) +3. [Node Types](#node-types) +4. [Activity](#activity) + + +## Motivation + +- Commands in RTS often result in multiple actions that have to be taken + - command does not translate to single atomic action + - action chains for most commands, e.g. attack command results in actions move + attack + +- action chains can get complex + - branching paths depending on conditions + - waiting for events or triggers + - parallel actions + - canceling an action requires clean up + +- control flow of commands and actions should not be hardcoded! + +## Architecture + +- control flow modeled as a directed node graph + - following along the paths in the graph tells the game entity what to do + - current node == current action + - paths == connections to the next action + - activity == (sub)graph with start and end node + - defines a scope or reusable +![graph example]() + +- inpired by BPMN flow graph + - you don't need to know BPMN because we explain everything relevant in here + - but you can use BPMN draw tools to make graphs + +## Node Types + +- Start + - 0 inputs / 1 output + - no action + - signifies start of activity +- End + - 1 input / 0 outputs + - no action + - signifies end of activity +- Task System + - 1 input / 1 output + - references built-in system that should be executed when visiting + - signifies an action +- Task + - 1 input / 1 output + - references a custom function that should be exectuted when visiting + - signifies an action + - for scripting or debugging +- Exclusive Event Gateway + - 1 input / 1+ outputs + - register event callback(s) when visiting + - choose 1 output when event callback s triggered + - signifies a branch +- Exclusive Gateway + - 1 input / 1+ outputs + - choose 1 output depending on condition function + - i comparison to event gateway, conditions are checked immediately + - signifies a branch +- activity + - 1 input / 1 output + - start a subactivity + - signifies a nested action + + +## Activity + +- encapsulates a node graph with start and end +- defines a scope of interaction +- allows reuse of common control flows + - e.g. movement diff --git a/doc/code/game_simulation/components.md b/doc/code/game_simulation/components.md new file mode 100644 index 0000000000..ea40ed4e7a --- /dev/null +++ b/doc/code/game_simulation/components.md @@ -0,0 +1,39 @@ +# Built-in Components + +Overview of the built-in game entity components in the game simulation. + +1. [Internal](#internal) + 1. [Activity](#activity) + 2. [CommandQueue](#commandqueue) + 3. [Ownership](#ownership) + 4. [Position](#position) +2. [API](#api) + 1. [Idle](#idle) + 2. [Live](#live) + 3. [Move](#move) + 4. [Turn](#turn) + + +## Internal + +- components used by every game entity + +### Activity + +### CommandQueue + +### Ownership + +### Position + +## API + +- components which are assigned depending on whether the game entity defnition has a matching nyan API object (engine.ability.Ability) + +### Idle + +### Live + +### Move + +### Turn diff --git a/doc/code/game_simulation/game_entity.md b/doc/code/game_simulation/game_entity.md new file mode 100644 index 0000000000..f7877400d2 --- /dev/null +++ b/doc/code/game_simulation/game_entity.md @@ -0,0 +1,103 @@ +# Game Entity + +## Architecture + +## Game Entity class + +- content + - entity ID + - map of components + - manager for event handling + - render entity if should be animated (optional) + +- game entity should capture every use case for world object + - composition over inheritance + - game entity attributes/capabilities defined by components + +## Component Data Storage + +- dual role + - signify what game entity can be or do by component type (e.g. move) + - store data for said attribute + - provide common helper functions for interacting with data + - atomic: components never directly depend on another component to be assigned to the entity + +- differentiate between prsistent game data and runtime data + - Persistent game data + - unit stats as defined in the game/modpack files, e.g. movement speed + - usually does not change often during gameplay and changes are predictable + - managed as objects in nyan db + - openage nyan API object engine.ability.Ability + - component holds reference to nyan object ability + - data must always be fetched from nyan + - not all components manage persitant game data + - e.g. Position + - runtime data + - data that changes due to gameplay interactions, e.g. current health + - unpredictable + - managed inside the component on curves + - data structure that tracks values over time + - manipulated by accessing the curve and adding/removing keyframes + - or by helper method + + +## Control Flow + +- doing stuff with the game entity components +- not implemented into game entity class / component + - separation of concerns + - data classes only store data + - control flow is sperate and can be adjusted for different purposes + +- major core parts + - system + - activity + - manager + +### System + +- function that implements game logic + - implemented as static functions of a class with only static methods + - i.e. do not belong to a particular object + - manipulate the components data of one or more entities + +- atomic (like components): focus on one particular action + - don't call other systems (unless its a subroutine) + - no hardcoded transitions from one system to the next, e.g. hardcode attack after move + - managed by activities instead + +- ECS vs. event-based gamestate + - in ECS systems are called every tick + - event-based systems are called on events, so they only run when they need to + - heavy use of prediction due to curves logic + - only calculate the necessary keyframes + - e.g. constructing buildings only needs 2 keyframes for start and finish + +### Activities + +- connect systems together in a node graph to form a control flow for game entity + - models complex behaviour and action chains + - usually, game entity's of the same type use the same node graph + - e.g. all spearman have the same general behaviour + - but can be changed via components + +- workflow + - nodes in graph denote what systems should be called for the game entity + - or decide where to go next based on conditions + - game entity's "action state" defined by the current node in the graph + - activity system follows along the nodes in the graph to execute game logic on an entity + - register callback events + +- action state stored in ActivityComponent + - stores reference to full activity graph used by game entity + - current node in graph + - events that are currently waited for + - resume execution from current node when callback is received + +- see more activities.md + +### Manager + +- listens for events for the game entity +- takes appropriate action when called back by events + - re-enter activity system diff --git a/doc/code/game_simulation/systems.md b/doc/code/game_simulation/systems.md new file mode 100644 index 0000000000..315a2f28b5 --- /dev/null +++ b/doc/code/game_simulation/systems.md @@ -0,0 +1,10 @@ +# Built-in Systems + +Overview of the built-in systems in the game simulation. + +## Overview + + +## Move + +## Idle From 60071d44acf5ad0c571655864293d7da9b7bafd1 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 26 Jul 2023 00:29:21 +0200 Subject: [PATCH 02/49] doc: More drafts for other engine parts. --- doc/code/curves.md | 133 +++++++++++++++++- doc/code/event_system.md | 46 +++++- doc/code/game_simulation/README.md | 3 +- doc/code/game_simulation/components.md | 58 +++++--- doc/code/game_simulation/game_entity.md | 13 ++ doc/code/game_simulation/systems.md | 30 +++- doc/code/time.md | 26 ++++ .../gamestate/component/internal/position.h | 4 +- 8 files changed, 279 insertions(+), 34 deletions(-) create mode 100644 doc/code/time.md diff --git a/doc/code/curves.md b/doc/code/curves.md index 726cc2d98f..234255987e 100644 --- a/doc/code/curves.md +++ b/doc/code/curves.md @@ -1,21 +1,148 @@ # Curves -## Overview +- data structure for keyframe-based simulation + - not just data but manages data over time + + +1. [Motivation](#motivation) +2. [Architecture](#architecture) +3. [Curve Types](#curve-types) + 1. [Primitive](#primitive) + 1. [Discrete](#discrete) + 2. [Continuous](#continuous) + 3. [Segmented](#segmented) + 2. [Container](#container) + 1. [Queue](#queue) + 2. [Unordered Map](#unordered-map) + + ## Motivation +- inspired by a similar implementation for Planetary Annihilation + - https://www.forrestthewoods.com/blog/tech_of_planetary_annihilation_chrono_cam/#.lmxbu3vld +- problem: data is hard to keep consistent in sync (synchronous) + - over network + - across threads + - state should be the same for all components +- solution: instead of only storing *current* data, store as keyframes with (data, time) + - storage becomes a function that tracks data values over time -> timeline + - data in between keyframes can be interpolated + - changing data can be asynchronous -> keyframes can be added for future + - reverting to previous simulation time becomes much easier +- downsides: + - requires more storage + - interpolation is more costly than simple ticks + - can be more complex to integrate +- however: + - RTS actions are often predictable and therefore doesn't require many keyframes + - data doesn't have to be accessed only when necessary + - may be easier to understand in implemention + +## Architecture + +- major components + - BaseCurve: primitive data + - Discrete + - Interpolated + - Continuous + - Segmented + - Queue and UnorderedMap: containers + - Keyframe: time-value pair + - KeyframeContainer: storage for curve keyframes + - curve::time_t: time type -> fixed point that represents simulation time in seconds + +- which curves should be used for + - data that isn't modified or read + - data that must be tracked over time + - ALL game data in game entity components that is required for simulation + +- what curves should not be used for + - data that is modified or read often + - data that doesn't have to be tracked over time + - e.g. temporary variables + - e.g. things that don't affect the simulation like visual settings + ## Curve Types +- overview of built-in curve types + ### Primitive +- curves for storing the change for primitive/single values + - primitive C++ types, e.g. int/float/std::string + - objects + - shared ptr + +- default value is assigned for t = curve::time_t::min + - guarantees that there is always a value to access + - mirrors expected behaviour from declaring primitive values in C++ + - for objects that don't have default constructor, the default must be assigned when initializing curve + +- operations + - read + - get(t): Get the value at time t + - frame(t): Get the previous keyframe (time and value) before t + - next_frame(t): Get the next keyframe (time and value) after t + - modify + - set_insert(t, value): Insert a new keyframe value at t + - set_last(t, value): Insert a new keyframe value at t, delete all keyframes with after t + - set_replace(t, value): Insert a new keyframe value at t, remove all other keyframes with time t + - copy + - sync(Curve, t): copy keyframes from one curve to the other, starting at t + #### Discrete +- value between two keyframes is constant + - returns value of previous keyframe + +- used for values that dont change much + - e.g. current health + #### Continuous +- value between two keyframes is interpolated + - returns interpolated value from requested time between t1 and t2 + - if t > last keyframe, then behave like discrete curve + - currently only supports linear interpolation + +- used for values that change gradually + - e.g. entity position, current construction progress of building + #### Segmented +- value between two keyframes is interpolated + - behaves like continuous but allows "jumps" between intervals + - e.g. intervals are not connected like on continuous curve + - mix between continuous and discrete + +- used for values that change gradually but are not on connected intervals + - e.g. angles (because the value jumps when rolling over from 0 to 360 degrees) + ### Container -## Queue +- curves for storing the changes for collections and containers + - queues + - maps + +- most important distinction between normal containers and curve containers + - modify operations do not erase/overwrite previous elements + - elements are always valid at a specific time + - if element e is erased at t, requesting it at t1>=t will return nothing but requesting it at t2 smaller t will return e + +#### Queue + +- stores elements in insertion order and additionally insertion time + - front: for requested time t, return the element that was in front at t + - iterate: iterate over queue beginning at time t + - no empty check because elements are not deleted + - callee must remember the iterator to the last element it accessed! + +#### Unordered Map -## Unordered Map +- stores key-value pairs and remembers inserion time + - at: return the element for key k that was present at time t + - insert: add pair k, e at time t + - insert: add pair k, e at time t1 and erase at t2 + - kill: erase element at time t + - iterate over elements between time t1 and t2 diff --git a/doc/code/event_system.md b/doc/code/event_system.md index 2f0a195872..98b77f223c 100644 --- a/doc/code/event_system.md +++ b/doc/code/event_system.md @@ -1,10 +1,48 @@ # Event System -## Overview +- event system in openage + - sending and receiving events + - handling events + - integration of events into the simulation -1. [Event System](#event-system) - 1. [Overview](#overview) - 2. [Architecture](#architecture) +1. [Architecture](#architecture) ## Architecture + +![event system UML]() + +- central component: event loop + - main point of interaction to create events + - manage event queue and event store + - manage interdependencies between events + - events are based on simulation time + - time from time loop and its clock + - advance to a point in time + - check for interdendency changes and reorder events if necessary + - execute event handlers in order of execution time + +- event + - represents an event + - always targets an event entity + - executed at specific time which can be modified by event loop based on dependencies + - stores parameters for execution + +- event entity + - inherit from this class + - target OR dependency of event + - as target: + - may be manipulated by event handler on event execution + - as dependency: + - triggers the event execution + +- event handler + - executes if event fires + - can be registered on event loop (if reusable) + - or passed when creating event + - trigger type of handler manages when event is executed + - dependency, once, repeating, etc. + +- state + - object that persists across event executions + - can be anything, usually used for global vars diff --git a/doc/code/game_simulation/README.md b/doc/code/game_simulation/README.md index d5202a80c2..287f52da64 100644 --- a/doc/code/game_simulation/README.md +++ b/doc/code/game_simulation/README.md @@ -2,7 +2,8 @@ - Components for simulating gameplay -## Overview + +1. [Architecture](#architecture) diff --git a/doc/code/game_simulation/components.md b/doc/code/game_simulation/components.md index ea40ed4e7a..4783119964 100644 --- a/doc/code/game_simulation/components.md +++ b/doc/code/game_simulation/components.md @@ -3,37 +3,49 @@ Overview of the built-in game entity components in the game simulation. 1. [Internal](#internal) - 1. [Activity](#activity) - 2. [CommandQueue](#commandqueue) - 3. [Ownership](#ownership) - 4. [Position](#position) 2. [API](#api) - 1. [Idle](#idle) - 2. [Live](#live) - 3. [Move](#move) - 4. [Turn](#turn) ## Internal - components used by every game entity - -### Activity - -### CommandQueue - -### Ownership - -### Position +- no corresponding nyan API object + +- Activity + - reference to the top-level activity for the game entity + - current node in activity + - list of scheduled events the entity waits for to continue the control flow +- CommandQueue + - stores commands from the input system for the game entity + - commands are basically signals from the input system to handle conditional branches in the activity control flow + - commands have type + - payload depends on type +- Owner + - stores ID of player who owns the unit + - allow basic access control +- Position + - 3D position of game entity in the world + - angle of game entity relative to camera, clockwise rotation ## API - components which are assigned depending on whether the game entity defnition has a matching nyan API object (engine.ability.Ability) -### Idle - -### Live - -### Move - -### Turn +- Idle + - nyan API: engine.ability.type.Idle + - represents the ingame "idle" state of a unit, i.e. where it is not active + - only used to access animations, sounds from nyan db +- Live + - nyan API: engine.ability.type.Live + - stores attributes of entity and their current values, e.g. health +- Move + - nyan API: engine.ability.type.Move + - allows the entity to move + - required for issuing move commands + - stores nothing + - speed, animations, sounds from nyan db +- Turn + - nyan API: engine.ability.type.Turn + - allows the entity to turn, i.e. change its angle + - stores nothing + - speed, animations, sounds from nyan db diff --git a/doc/code/game_simulation/game_entity.md b/doc/code/game_simulation/game_entity.md index f7877400d2..1de1cac635 100644 --- a/doc/code/game_simulation/game_entity.md +++ b/doc/code/game_simulation/game_entity.md @@ -2,6 +2,19 @@ ## Architecture +![game entity UML]() + +- major components + - game entity class + - components + - systems + - manager + +- separation of concerns + - components = only data storage + - systems = only game logic + - manager/entity = handles to access data/initiate game logic + ## Game Entity class - content diff --git a/doc/code/game_simulation/systems.md b/doc/code/game_simulation/systems.md index 315a2f28b5..552e8d582b 100644 --- a/doc/code/game_simulation/systems.md +++ b/doc/code/game_simulation/systems.md @@ -2,9 +2,35 @@ Overview of the built-in systems in the game simulation. -## Overview - +1. [Move](#move) +2. [Idle](#idle) ## Move +- handles movement actions for game entities +- move_command + - move the entity using a move command + - uses movement parameters from command to call move_default +- move default + - move the game entity to a new position + - requires Move and Turn components + - workflow + 1. Turn + 1. calculate new angle in move direction + 2. calculate turn time from new angle and turn speed + 3. add keyframe for new angle at t + turn time + 2. Move + 1. calculate move time from path distance and move speed + 2. add keyframe for old position at t and for new position at t + turn time + move time + 3. Update animations if animated + - returns duration of movement (t + turn time + move time) + ## Idle + +- handles idle action for game entities +- since idle means doing nothing, this only updates animations and sounds +- idle + - requires Idle component + - workflow + 1. Update animations if animated + - returns duration of action (always 0) diff --git a/doc/code/time.md b/doc/code/time.md new file mode 100644 index 0000000000..068cf56a23 --- /dev/null +++ b/doc/code/time.md @@ -0,0 +1,26 @@ +# Simulation Time + +- basis for all game simulation and rendering routines + - scheduling and execution of events (event loop) + - data management over time (curves) + - timing animations (renderer) + +![time loop UML]() + +- time type + - curve::time_t + - fixed point value + - time unit: seconds + +- controlled by time loop + - advances time with clock + - runs in its own thread + - accessed by simulation and renderer threads + +- clock manages time + - calculates time using system clock + - can be stopped and resumed + - tracks both real time and simulation time + - real time = cumulative run time of clock + - speed can be adjusted for simulation time + - simulationm time = cumulative run time of clock adjusted by speed diff --git a/libopenage/gamestate/component/internal/position.h b/libopenage/gamestate/component/internal/position.h index 1d2e6bde1a..1fe4a0b6cc 100644 --- a/libopenage/gamestate/component/internal/position.h +++ b/libopenage/gamestate/component/internal/position.h @@ -78,7 +78,9 @@ class Position : public InternalComponent { * Angle the entity is facing over time. * * Represents degrees in the range [0, 360). At angle 0, the entity is facing - * towards the camera. + * towards the camera (direction vector {x, y} = {-1, 1}). + * + * Rotation is clockwise, so at 90 degrees the entity is facing left. */ curve::Segmented angle; }; From ae2f6714cc097dab5ff48213cc28975dde1f88c4 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 26 Jul 2023 01:03:26 +0200 Subject: [PATCH 03/49] refactor: Remove redundant includes. --- libopenage/main.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/libopenage/main.cpp b/libopenage/main.cpp index 40cb48873c..1d17727ea0 100644 --- a/libopenage/main.cpp +++ b/libopenage/main.cpp @@ -2,32 +2,10 @@ #include "main.h" -#include -#include - #include "cvar/cvar.h" #include "engine/engine.h" -#include "event/time_loop.h" -#include "gamestate/simulation.h" -#include "log/log.h" -#include "presenter/presenter.h" #include "util/timer.h" -// TODO: Remove custom jthread definition when clang/libc++ finally supports it -#if __llvm__ -#if !__cpp_lib_jthread -namespace std { -class jthread : public thread { -public: - using thread::thread; // needed constructors - ~jthread() { - this->join(); - } -}; -} // namespace std -#endif -#endif - namespace openage { From 125aa312de5b679e6dcf7d1f623c54ebf91d95f1 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 26 Jul 2023 01:04:41 +0200 Subject: [PATCH 04/49] refactor: Move time type and logic to dedicated subfolder. --- libopenage/CMakeLists.txt | 1 + libopenage/curve/CMakeLists.txt | 1 - libopenage/curve/iterator.h | 31 +++++----- libopenage/curve/keyframe.h | 6 +- libopenage/curve/keyframe_container.h | 2 +- libopenage/curve/map.h | 61 +++++++++---------- libopenage/curve/map_filter_iterator.h | 15 +++-- libopenage/curve/queue.h | 2 +- libopenage/curve/queue_filter_iterator.h | 18 +++--- libopenage/curve/segmented.h | 12 ++-- libopenage/curve/tests/container.cpp | 12 ++-- libopenage/curve/tests/curve_types.cpp | 2 +- libopenage/engine/engine.cpp | 3 +- libopenage/event/CMakeLists.txt | 2 - libopenage/event/demo/physics.h | 2 +- libopenage/event/event.h | 2 +- libopenage/event/event_loop.h | 2 +- libopenage/event/evententity.h | 2 +- libopenage/event/eventhandler.h | 21 ++++--- libopenage/event/eventqueue.h | 2 +- libopenage/gamestate/activity/node.h | 2 +- libopenage/gamestate/game.cpp | 3 +- libopenage/gamestate/game_entity.h | 2 +- libopenage/gamestate/simulation.cpp | 7 +-- libopenage/gamestate/system/activity.h | 2 +- libopenage/gamestate/system/idle.h | 2 +- libopenage/gamestate/system/move.h | 2 +- .../input/controller/game/controller.cpp | 4 +- libopenage/main/tests/gui.h | 2 +- libopenage/main/tests/physics.h | 2 +- libopenage/presenter/presenter.cpp | 2 +- libopenage/renderer/demo/demo_3.cpp | 2 +- libopenage/renderer/demo/demo_4.cpp | 2 +- libopenage/renderer/resources/frame_timing.h | 2 +- .../stages/terrain/terrain_renderer.cpp | 4 +- .../renderer/stages/world/world_object.h | 2 +- .../stages/world/world_render_entity.h | 2 +- .../renderer/stages/world/world_renderer.cpp | 3 +- libopenage/time/CMakeLists.txt | 5 ++ libopenage/{event => time}/clock.cpp | 0 libopenage/{event => time}/clock.h | 3 +- libopenage/{curve/curve.cpp => time/time.cpp} | 5 +- libopenage/{curve/curve.h => time/time.h} | 4 +- libopenage/{event => time}/time_loop.cpp | 3 +- libopenage/{event => time}/time_loop.h | 1 + 45 files changed, 135 insertions(+), 132 deletions(-) create mode 100644 libopenage/time/CMakeLists.txt rename libopenage/{event => time}/clock.cpp (100%) rename libopenage/{event => time}/clock.h (99%) rename libopenage/{curve/curve.cpp => time/time.cpp} (74%) rename libopenage/{curve/curve.h => time/time.h} (83%) rename libopenage/{event => time}/time_loop.cpp (97%) rename libopenage/{event => time}/time_loop.h (99%) diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index dafe823c2e..f1772bfc36 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -364,6 +364,7 @@ add_subdirectory("rng") add_subdirectory("shader") add_subdirectory("terrain") add_subdirectory("testing") +add_subdirectory("time") add_subdirectory("unit") add_subdirectory("util") add_subdirectory("versions") diff --git a/libopenage/curve/CMakeLists.txt b/libopenage/curve/CMakeLists.txt index 8554b14f39..1aa1efb55b 100644 --- a/libopenage/curve/CMakeLists.txt +++ b/libopenage/curve/CMakeLists.txt @@ -1,7 +1,6 @@ add_sources(libopenage base_curve.cpp continuous.cpp - curve.cpp discrete.cpp discrete_mod.cpp interpolated.cpp diff --git a/libopenage/curve/iterator.h b/libopenage/curve/iterator.h index b965b65640..49b36d12eb 100644 --- a/libopenage/curve/iterator.h +++ b/libopenage/curve/iterator.h @@ -1,8 +1,8 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #pragma once -#include "curve.h" +#include "time/time.h" namespace openage::curve { @@ -11,7 +11,7 @@ namespace openage::curve { */ template + typename iterator_t = typename container_t::const_iterator> class CurveIterator { public: /** @@ -28,8 +28,7 @@ class CurveIterator { /** * The iterator needs a reference to the container */ - explicit CurveIterator(const container_t *c) - : + explicit CurveIterator(const container_t *c) : base{}, container{c}, from{-std::numeric_limits::max()}, @@ -42,8 +41,7 @@ class CurveIterator { CurveIterator(const iterator_t &base, const container_t *container, const time_t &from, - const time_t &to) - : + const time_t &to) : base{base}, container{container}, from{from}, @@ -51,21 +49,21 @@ class CurveIterator { public: /** Default copy c'tor */ - CurveIterator (const CurveIterator &) = default; + CurveIterator(const CurveIterator &) = default; virtual ~CurveIterator() = default; /** Default assignment operator */ - CurveIterator &operator= ( + CurveIterator &operator=( const CurveIterator &) = default; /** Dereference will call the virtual function */ - virtual const val_t &operator *() const { + virtual const val_t &operator*() const { return this->value(); } /** Dereference will call the virutal function */ - virtual const val_t *operator ->() const { + virtual const val_t *operator->() const { return &this->value(); } @@ -73,7 +71,7 @@ class CurveIterator { * For equalness only the base iterator will be testet - not the timespans * this is defined in. */ - virtual bool operator ==(const CurveIterator &rhs) const { + virtual bool operator==(const CurveIterator &rhs) const { return this->base == rhs.base; } @@ -81,17 +79,18 @@ class CurveIterator { * For unequalness only the base iterator will be testet - not the timespans * this is defined in. */ - virtual bool operator !=(const CurveIterator &rhs) const { + virtual bool operator!=(const CurveIterator &rhs) const { return this->base != rhs.base; } /** * Advance to the next valid element. */ - virtual CurveIterator &operator ++() { + virtual CurveIterator &operator++() { do { ++this->base; - } while (this->container->end().base != this->base and not this->valid()); + } + while (this->container->end().base != this->base and not this->valid()); return *this; } @@ -132,4 +131,4 @@ class CurveIterator { }; -} // openage::curve +} // namespace openage::curve diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h index a0fc39d1e6..7259745b20 100644 --- a/libopenage/curve/keyframe.h +++ b/libopenage/curve/keyframe.h @@ -1,10 +1,10 @@ -// Copyright 2019-2019 the openage authors. See copying.md for legal info. +// Copyright 2019-2023 the openage authors. See copying.md for legal info. #pragma once #include -#include "curve.h" +#include "time/time.h" namespace openage::curve { @@ -37,4 +37,4 @@ class Keyframe { T value = T{}; }; -} // openage::curve +} // namespace openage::curve diff --git a/libopenage/curve/keyframe_container.h b/libopenage/curve/keyframe_container.h index e025de1a4d..51de426d33 100644 --- a/libopenage/curve/keyframe_container.h +++ b/libopenage/curve/keyframe_container.h @@ -6,9 +6,9 @@ #include #include -#include "curve/curve.h" #include "curve/keyframe.h" #include "error/error.h" +#include "time/time.h" #include "util/compiler.h" namespace openage::curve { diff --git a/libopenage/curve/map.h b/libopenage/curve/map.h index 8fefda4023..49e6e5679c 100644 --- a/libopenage/curve/map.h +++ b/libopenage/curve/map.h @@ -1,14 +1,14 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #pragma once -#include "curve.h" -#include "map_filter_iterator.h" - -#include #include +#include #include +#include "curve/map_filter_iterator.h" +#include "time/time.h" + namespace openage::curve { @@ -20,8 +20,7 @@ template class UnorderedMap { /** Internal container to access all data and metadata */ struct map_element { - map_element (const val_t &v, const time_t &a, const time_t &d) - : + map_element(const val_t &v, const time_t &a, const time_t &d) : value(v), alive(a), dead(d) {} @@ -41,7 +40,7 @@ class UnorderedMap { using const_iterator = typename std::unordered_map::const_iterator; std::optional> - operator ()(const time_t&, const key_t &) const; + operator()(const time_t &, const key_t &) const; std::optional> at(const time_t &, const key_t &) const; @@ -77,39 +76,36 @@ class UnorderedMap { */ void dump() { for (auto i : container) { - std::cout << "Element: " << i.second.value << std::endl;; + std::cout << "Element: " << i.second.value << std::endl; } } }; -template +template std::optional>> UnorderedMap::operator()(const time_t &time, const key_t &key) const { return this->at(time, key); } -template +template std::optional>> -UnorderedMap::at(const time_t & time, - const key_t & key) const { +UnorderedMap::at(const time_t &time, + const key_t &key) const { auto e = this->container.find(key); - if (e != this->container.end() and - e->second.alive <= time and - e->second.dead > time) { - + if (e != this->container.end() and e->second.alive <= time and e->second.dead > time) { return MapFilterIterator>( e, this, time, - std::numeric_limits::max() - ); - } else { + std::numeric_limits::max()); + } + else { return {}; } } -template +template MapFilterIterator> UnorderedMap::begin(const time_t &time) const { return MapFilterIterator>( @@ -119,7 +115,7 @@ UnorderedMap::begin(const time_t &time) const { std::numeric_limits::max()); } -template +template MapFilterIterator> UnorderedMap::end(const time_t &time) const { return MapFilterIterator>( @@ -129,7 +125,7 @@ UnorderedMap::end(const time_t &time) const { time); } -template +template MapFilterIterator> UnorderedMap::between(const time_t &from, const time_t &to) const { auto it = MapFilterIterator>( @@ -144,7 +140,7 @@ UnorderedMap::between(const time_t &from, const time_t &to) const return it; } -template +template MapFilterIterator> UnorderedMap::insert(const time_t &alive, const key_t &key, @@ -156,7 +152,7 @@ UnorderedMap::insert(const time_t &alive, value); } -template +template MapFilterIterator> UnorderedMap::insert(const time_t &alive, const time_t &dead, @@ -171,7 +167,7 @@ UnorderedMap::insert(const time_t &alive, dead); } -template +template void UnorderedMap::birth(const time_t &time, const key_t &key) { auto it = this->container.find(key); @@ -180,14 +176,13 @@ void UnorderedMap::birth(const time_t &time, } } -template +template void UnorderedMap::birth(const time_t &time, - const MapFilterIterator &it) { + const MapFilterIterator &it) { it->second.alive = time; } -template +template void UnorderedMap::kill(const time_t &time, const key_t &key) { auto it = this->container.find(key); @@ -196,15 +191,15 @@ void UnorderedMap::kill(const time_t &time, } } -template +template void UnorderedMap::kill(const time_t &time, const MapFilterIterator &it) { it->second.dead = time; } -template +template void UnorderedMap::clean(const time_t &) { // TODO save everything to a file and be happy. } -} // openage::curve +} // namespace openage::curve diff --git a/libopenage/curve/map_filter_iterator.h b/libopenage/curve/map_filter_iterator.h index 97ff70960e..84f4402a06 100644 --- a/libopenage/curve/map_filter_iterator.h +++ b/libopenage/curve/map_filter_iterator.h @@ -1,13 +1,14 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #pragma once -#include "curve.h" -#include "iterator.h" - #include #include +#include "curve/iterator.h" +#include "time/time.h" + + namespace openage::curve { /** @@ -31,8 +32,7 @@ class MapFilterIterator : public CurveIterator { MapFilterIterator(const iterator_t &base, const container_t *container, const time_t &from, - const time_t &to) - : + const time_t &to) : CurveIterator(base, container, from, to) {} MapFilterIterator(const MapFilterIterator &) = default; @@ -58,7 +58,6 @@ class MapFilterIterator : public CurveIterator { const key_t &key() const { return this->get_base()->first; } - }; -} // openage::curve +} // namespace openage::curve diff --git a/libopenage/curve/queue.h b/libopenage/curve/queue.h index f48213842d..8ba6f7e8e8 100644 --- a/libopenage/curve/queue.h +++ b/libopenage/curve/queue.h @@ -5,9 +5,9 @@ #include #include -#include "curve/curve.h" #include "curve/queue_filter_iterator.h" #include "event/evententity.h" +#include "time/time.h" namespace openage::curve { diff --git a/libopenage/curve/queue_filter_iterator.h b/libopenage/curve/queue_filter_iterator.h index 0c6eaaf18c..5871cf1d96 100644 --- a/libopenage/curve/queue_filter_iterator.h +++ b/libopenage/curve/queue_filter_iterator.h @@ -1,9 +1,9 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #pragma once -#include "curve.h" -#include "iterator.h" +#include "curve/iterator.h" +#include "time/time.h" #include #include @@ -19,8 +19,8 @@ namespace openage::curve { * It depends on val_t as its value type, container_t is the container * to operate on and the function valid_f, that checks if an element is alive. */ -template +template class QueueFilterIterator : public CurveIterator { public: using const_iterator = typename container_t::const_iterator; @@ -31,14 +31,12 @@ class QueueFilterIterator : public CurveIterator(base, base_container, from, to) {} virtual bool valid() const override { if (this->container->end().get_base() != this->get_base()) { - return (this->get_base()->time() >= this->from and - this->get_base()->time() < this->to); + return (this->get_base()->time() >= this->from and this->get_base()->time() < this->to); } return false; } @@ -49,4 +47,4 @@ class QueueFilterIterator : public CurveIterator #include +#include -#include "interpolated.h" -#include "../log/log.h" +#include "curve/interpolated.h" +#include "log/log.h" namespace openage::curve { @@ -24,7 +24,7 @@ namespace openage::curve { * The bound template type T has to implement `operator +(T)` and * `operator *(time_t)`. */ -template +template class Segmented : public Interpolated { public: using Interpolated::Interpolated; @@ -90,4 +90,4 @@ std::string Segmented::idstr() const { } -} // openage::curve +} // namespace openage::curve diff --git a/libopenage/curve/tests/container.cpp b/libopenage/curve/tests/container.cpp index 2d6257f165..5843dd48c9 100644 --- a/libopenage/curve/tests/container.cpp +++ b/libopenage/curve/tests/container.cpp @@ -1,11 +1,11 @@ // Copyright 2017-2023 the openage authors. See copying.md for legal info. -#include "../../testing/testing.h" -#include "../continuous.h" -#include "../curve.h" -#include "../discrete.h" -#include "../map.h" -#include "../queue.h" +#include "curve/continuous.h" +#include "curve/discrete.h" +#include "curve/map.h" +#include "curve/queue.h" +#include "testing/testing.h" +#include "time/time.h" #include #include diff --git a/libopenage/curve/tests/curve_types.cpp b/libopenage/curve/tests/curve_types.cpp index 53b305c055..b102d3828b 100644 --- a/libopenage/curve/tests/curve_types.cpp +++ b/libopenage/curve/tests/curve_types.cpp @@ -1,6 +1,6 @@ // Copyright 2017-2023 the openage authors. See copying.md for legal info. -#include "curve/curve.h" +#include "time/time.h" #include "curve/continuous.h" #include "curve/discrete.h" #include "curve/discrete_mod.h" diff --git a/libopenage/engine/engine.cpp b/libopenage/engine/engine.cpp index fc53c82f8a..b28b0b58d4 100644 --- a/libopenage/engine/engine.cpp +++ b/libopenage/engine/engine.cpp @@ -5,9 +5,10 @@ #include "log/log.h" #include "cvar/cvar.h" -#include "event/time_loop.h" #include "gamestate/simulation.h" #include "presenter/presenter.h" +#include "time/time_loop.h" + namespace openage::engine { diff --git a/libopenage/event/CMakeLists.txt b/libopenage/event/CMakeLists.txt index af5f9b7148..d62326b2ba 100644 --- a/libopenage/event/CMakeLists.txt +++ b/libopenage/event/CMakeLists.txt @@ -1,5 +1,4 @@ add_sources(libopenage - clock.cpp event_loop.cpp event.cpp evententity.cpp @@ -8,7 +7,6 @@ add_sources(libopenage eventstore.cpp state.cpp tests.cpp - time_loop.cpp ) add_subdirectory("demo") diff --git a/libopenage/event/demo/physics.h b/libopenage/event/demo/physics.h index 5210f58832..ae440599be 100644 --- a/libopenage/event/demo/physics.h +++ b/libopenage/event/demo/physics.h @@ -3,7 +3,7 @@ #pragma once #include "gamestate.h" -#include "../../curve/curve.h" +#include "../../time/time.h" #include diff --git a/libopenage/event/event.h b/libopenage/event/event.h index 7458c24bdf..a52b8ec909 100644 --- a/libopenage/event/event.h +++ b/libopenage/event/event.h @@ -4,7 +4,7 @@ #include -#include "../curve/curve.h" +#include "../time/time.h" #include "eventhandler.h" namespace openage::event { diff --git a/libopenage/event/event_loop.h b/libopenage/event/event_loop.h index bdc4287962..959f425f3c 100644 --- a/libopenage/event/event_loop.h +++ b/libopenage/event/event_loop.h @@ -8,10 +8,10 @@ #include #include -#include "curve/curve.h" #include "event.h" #include "eventqueue.h" #include "log/log.h" +#include "time/time.h" namespace openage::event { diff --git a/libopenage/event/evententity.h b/libopenage/event/evententity.h index bef9a467c6..8f971105d6 100644 --- a/libopenage/event/evententity.h +++ b/libopenage/event/evententity.h @@ -6,7 +6,7 @@ #include #include -#include "curve/curve.h" +#include "time/time.h" namespace openage::event { diff --git a/libopenage/event/eventhandler.h b/libopenage/event/eventhandler.h index 966ff1cc68..7c0ee820dc 100644 --- a/libopenage/event/eventhandler.h +++ b/libopenage/event/eventhandler.h @@ -2,7 +2,7 @@ #pragma once -#include "../curve/curve.h" +#include "../time/time.h" #include @@ -75,19 +75,22 @@ class EventHandler { using map_t = std::unordered_map; param_map() {} - param_map(std::initializer_list l) : map(l) {} - param_map(const map_t &map) : map{std::move(map)} {} + param_map(std::initializer_list l) : + map(l) {} + param_map(const map_t &map) : + map{std::move(map)} {} /** * Returns the value, if it exists and is the right type. * defaultval if not. */ - template - T get(const std::string &key, const T &defaultval=T()) const { + template + T get(const std::string &key, const T &defaultval = T()) const { auto it = this->map.find(key); if (it != this->map.end() && this->check_type(it)) { return std::any_cast(it->second); - } else { + } + else { return defaultval; } } @@ -102,7 +105,7 @@ class EventHandler { /** * Check if the type of a map entry is correct. */ - template + template bool check_type(const std::string &key) const { auto it = map.find(key); if (it != map.end()) { @@ -112,7 +115,7 @@ class EventHandler { } private: - template + template bool check_type(const map_t::const_iterator &it) const { return it->second.type() == typeid(T); } @@ -216,4 +219,4 @@ class OnceEventHandler : public EventHandler { }; -} // openage::event +} // namespace openage::event diff --git a/libopenage/event/eventqueue.h b/libopenage/event/eventqueue.h index c3549ee1b8..0fc91501d6 100644 --- a/libopenage/event/eventqueue.h +++ b/libopenage/event/eventqueue.h @@ -7,7 +7,7 @@ #include "eventhandler.h" #include "eventstore.h" -#include "../curve/curve.h" +#include "../time/time.h" namespace openage::event { diff --git a/libopenage/gamestate/activity/node.h b/libopenage/gamestate/activity/node.h index 4abec66e6f..5e8d173523 100644 --- a/libopenage/gamestate/activity/node.h +++ b/libopenage/gamestate/activity/node.h @@ -5,7 +5,7 @@ #include #include -#include "curve/curve.h" +#include "time/time.h" #include "gamestate/activity/types.h" diff --git a/libopenage/gamestate/game.cpp b/libopenage/gamestate/game.cpp index 957f2767e8..c7c1a22a47 100644 --- a/libopenage/gamestate/game.cpp +++ b/libopenage/gamestate/game.cpp @@ -6,9 +6,10 @@ #include #include "assets/mod_manager.h" -#include "event/time_loop.h" #include "gamestate/game_state.h" #include "gamestate/universe.h" +#include "time/time_loop.h" + namespace openage::gamestate { diff --git a/libopenage/gamestate/game_entity.h b/libopenage/gamestate/game_entity.h index a28011d235..09a9c1debb 100644 --- a/libopenage/gamestate/game_entity.h +++ b/libopenage/gamestate/game_entity.h @@ -6,7 +6,7 @@ #include #include "coord/phys.h" -#include "curve/curve.h" +#include "time/time.h" #include "gamestate/component/types.h" #include "gamestate/types.h" diff --git a/libopenage/gamestate/simulation.cpp b/libopenage/gamestate/simulation.cpp index 7de933255a..bb32ac05fd 100644 --- a/libopenage/gamestate/simulation.cpp +++ b/libopenage/gamestate/simulation.cpp @@ -2,16 +2,15 @@ #include "simulation.h" -#include "event/clock.h" -#include "event/event_loop.h" -#include "event/time_loop.h" - #include "assets/mod_manager.h" +#include "event/event_loop.h" #include "gamestate/entity_factory.h" #include "gamestate/event/process_command.h" #include "gamestate/event/send_command.h" #include "gamestate/event/spawn_entity.h" #include "gamestate/event/wait.h" +#include "time/clock.h" +#include "time/time_loop.h" // TODO #include "gamestate/game.h" diff --git a/libopenage/gamestate/system/activity.h b/libopenage/gamestate/system/activity.h index 16696bbed6..b097887140 100644 --- a/libopenage/gamestate/system/activity.h +++ b/libopenage/gamestate/system/activity.h @@ -4,7 +4,7 @@ #include -#include "curve/curve.h" +#include "time/time.h" #include "gamestate/system/types.h" diff --git a/libopenage/gamestate/system/idle.h b/libopenage/gamestate/system/idle.h index b1eaca3c53..0f93cf33d7 100644 --- a/libopenage/gamestate/system/idle.h +++ b/libopenage/gamestate/system/idle.h @@ -4,7 +4,7 @@ #include -#include "curve/curve.h" +#include "time/time.h" namespace openage::gamestate { diff --git a/libopenage/gamestate/system/move.h b/libopenage/gamestate/system/move.h index 6bf63cb1ff..4cd55c41da 100644 --- a/libopenage/gamestate/system/move.h +++ b/libopenage/gamestate/system/move.h @@ -5,7 +5,7 @@ #include #include "coord/phys.h" -#include "curve/curve.h" +#include "time/time.h" namespace openage::gamestate { diff --git a/libopenage/input/controller/game/controller.cpp b/libopenage/input/controller/game/controller.cpp index 29220d4430..41b4926117 100644 --- a/libopenage/input/controller/game/controller.cpp +++ b/libopenage/input/controller/game/controller.cpp @@ -2,10 +2,8 @@ #include "controller.h" -#include "event/clock.h" #include "event/evententity.h" #include "event/state.h" -#include "event/time_loop.h" #include "gamestate/component/internal/commands/types.h" #include "gamestate/event/send_command.h" #include "gamestate/event/spawn_entity.h" @@ -13,6 +11,8 @@ #include "gamestate/game_state.h" #include "gamestate/simulation.h" #include "input/controller/game/binding_context.h" +#include "time/clock.h" +#include "time/time_loop.h" #include "coord/phys.h" diff --git a/libopenage/main/tests/gui.h b/libopenage/main/tests/gui.h index 33763fb3f0..5cbd3d6772 100644 --- a/libopenage/main/tests/gui.h +++ b/libopenage/main/tests/gui.h @@ -6,7 +6,7 @@ #include #include -#include "../../curve/curve.h" +#include "../../time/time.h" #include "../../renderer/renderer.h" #include "../../renderer/opengl/window.h" diff --git a/libopenage/main/tests/physics.h b/libopenage/main/tests/physics.h index 143a20527f..ec65043c67 100644 --- a/libopenage/main/tests/physics.h +++ b/libopenage/main/tests/physics.h @@ -3,7 +3,7 @@ #pragma once #include "gamestate.h" -#include "../../curve/curve.h" +#include "../../time/time.h" #include diff --git a/libopenage/presenter/presenter.cpp b/libopenage/presenter/presenter.cpp index 9bb312e45a..97520a693f 100644 --- a/libopenage/presenter/presenter.cpp +++ b/libopenage/presenter/presenter.cpp @@ -7,7 +7,6 @@ #include #include -#include "event/time_loop.h" #include "gamestate/simulation.h" #include "input/controller/camera/binding_context.h" #include "input/controller/camera/controller.h" @@ -30,6 +29,7 @@ #include "renderer/stages/terrain/terrain_renderer.h" #include "renderer/stages/world/world_renderer.h" #include "renderer/window.h" +#include "time/time_loop.h" #include "util/path.h" namespace openage::presenter { diff --git a/libopenage/renderer/demo/demo_3.cpp b/libopenage/renderer/demo/demo_3.cpp index 9b142d8f7a..49ed021175 100644 --- a/libopenage/renderer/demo/demo_3.cpp +++ b/libopenage/renderer/demo/demo_3.cpp @@ -5,7 +5,6 @@ #include #include -#include "event/clock.h" #include "renderer/camera/camera.h" #include "renderer/gui/integration/public/gui_application_with_logger.h" #include "renderer/opengl/window.h" @@ -20,6 +19,7 @@ #include "renderer/stages/world/world_render_entity.h" #include "renderer/stages/world/world_renderer.h" #include "renderer/uniform_buffer.h" +#include "time/clock.h" namespace openage::renderer::tests { diff --git a/libopenage/renderer/demo/demo_4.cpp b/libopenage/renderer/demo/demo_4.cpp index 300167e652..eaaac65a9e 100644 --- a/libopenage/renderer/demo/demo_4.cpp +++ b/libopenage/renderer/demo/demo_4.cpp @@ -5,7 +5,6 @@ #include #include -#include "event/clock.h" #include "renderer/gui/integration/public/gui_application_with_logger.h" #include "renderer/opengl/window.h" #include "renderer/resources/animation/angle_info.h" @@ -15,6 +14,7 @@ #include "renderer/resources/shader_source.h" #include "renderer/resources/texture_data.h" #include "renderer/shader_program.h" +#include "time/clock.h" namespace openage::renderer::tests { void renderer_demo_4(const util::Path &path) { diff --git a/libopenage/renderer/resources/frame_timing.h b/libopenage/renderer/resources/frame_timing.h index 7ff5fcc2b9..b9742bccc4 100644 --- a/libopenage/renderer/resources/frame_timing.h +++ b/libopenage/renderer/resources/frame_timing.h @@ -5,7 +5,7 @@ #include #include -#include "curve/curve.h" +#include "time/time.h" namespace openage::renderer::resources { diff --git a/libopenage/renderer/stages/terrain/terrain_renderer.cpp b/libopenage/renderer/stages/terrain/terrain_renderer.cpp index 573bcc06f1..cec392eb12 100644 --- a/libopenage/renderer/stages/terrain/terrain_renderer.cpp +++ b/libopenage/renderer/stages/terrain/terrain_renderer.cpp @@ -2,8 +2,6 @@ #include "terrain_renderer.h" - -#include "event/clock.h" #include "renderer/camera/camera.h" #include "renderer/opengl/context.h" #include "renderer/renderer.h" @@ -13,6 +11,8 @@ #include "renderer/stages/terrain/terrain_mesh.h" #include "renderer/stages/terrain/terrain_model.h" #include "renderer/window.h" +#include "time/clock.h" + namespace openage::renderer::terrain { diff --git a/libopenage/renderer/stages/world/world_object.h b/libopenage/renderer/stages/world/world_object.h index 5a87254f86..4e2d3ef23f 100644 --- a/libopenage/renderer/stages/world/world_object.h +++ b/libopenage/renderer/stages/world/world_object.h @@ -8,7 +8,7 @@ #include "coord/scene.h" #include "curve/continuous.h" -#include "curve/curve.h" +#include "time/time.h" #include "curve/discrete.h" #include "curve/segmented.h" #include "renderer/resources/mesh_data.h" diff --git a/libopenage/renderer/stages/world/world_render_entity.h b/libopenage/renderer/stages/world/world_render_entity.h index 25e040fa32..ba371e2a99 100644 --- a/libopenage/renderer/stages/world/world_render_entity.h +++ b/libopenage/renderer/stages/world/world_render_entity.h @@ -8,7 +8,7 @@ #include "coord/phys.h" #include "coord/scene.h" #include "curve/continuous.h" -#include "curve/curve.h" +#include "time/time.h" #include "curve/discrete.h" #include "curve/segmented.h" #include "util/path.h" diff --git a/libopenage/renderer/stages/world/world_renderer.cpp b/libopenage/renderer/stages/world/world_renderer.cpp index 478ae74ba1..038a0aff10 100644 --- a/libopenage/renderer/stages/world/world_renderer.cpp +++ b/libopenage/renderer/stages/world/world_renderer.cpp @@ -2,7 +2,6 @@ #include "world_renderer.h" -#include "event/clock.h" #include "renderer/camera/camera.h" #include "renderer/opengl/context.h" #include "renderer/resources/assets/asset_manager.h" @@ -12,6 +11,8 @@ #include "renderer/stages/world/world_object.h" #include "renderer/texture.h" #include "renderer/window.h" +#include "time/clock.h" + namespace openage::renderer::world { diff --git a/libopenage/time/CMakeLists.txt b/libopenage/time/CMakeLists.txt new file mode 100644 index 0000000000..2a9475a66d --- /dev/null +++ b/libopenage/time/CMakeLists.txt @@ -0,0 +1,5 @@ +add_sources(libopenage + clock.cpp + time.cpp + time_loop.cpp +) diff --git a/libopenage/event/clock.cpp b/libopenage/time/clock.cpp similarity index 100% rename from libopenage/event/clock.cpp rename to libopenage/time/clock.cpp diff --git a/libopenage/event/clock.h b/libopenage/time/clock.h similarity index 99% rename from libopenage/event/clock.h rename to libopenage/time/clock.h index f58c6d3d16..cde5f7a3fa 100644 --- a/libopenage/event/clock.h +++ b/libopenage/time/clock.h @@ -5,7 +5,8 @@ #include #include -#include "curve/curve.h" +#include "time/time.h" + namespace openage::event { diff --git a/libopenage/curve/curve.cpp b/libopenage/time/time.cpp similarity index 74% rename from libopenage/curve/curve.cpp rename to libopenage/time/time.cpp index 4ec84fe407..aa60c91af2 100644 --- a/libopenage/curve/curve.cpp +++ b/libopenage/time/time.cpp @@ -1,9 +1,10 @@ // Copyright 2017-2018 the openage authors. See copying.md for legal info. -#include "curve.h" +#include "time.h" + namespace openage::curve { // This file is intended to be empty -} // openage::curve +} // namespace openage::curve diff --git a/libopenage/curve/curve.h b/libopenage/time/time.h similarity index 83% rename from libopenage/curve/curve.h rename to libopenage/time/time.h index 381ce727b3..0011394ec8 100644 --- a/libopenage/curve/curve.h +++ b/libopenage/time/time.h @@ -4,7 +4,7 @@ #include -#include "../util/fixed_point.h" +#include "util/fixed_point.h" namespace openage::curve { @@ -15,4 +15,4 @@ namespace openage::curve { */ using time_t = util::FixedPoint; -} // openage::curve +} // namespace openage::curve diff --git a/libopenage/event/time_loop.cpp b/libopenage/time/time_loop.cpp similarity index 97% rename from libopenage/event/time_loop.cpp rename to libopenage/time/time_loop.cpp index 6b50e63d19..d6571db820 100644 --- a/libopenage/event/time_loop.cpp +++ b/libopenage/time/time_loop.cpp @@ -4,8 +4,9 @@ #include -#include "event/clock.h" #include "log/log.h" +#include "time/clock.h" + namespace openage::event { diff --git a/libopenage/event/time_loop.h b/libopenage/time/time_loop.h similarity index 99% rename from libopenage/event/time_loop.h rename to libopenage/time/time_loop.h index a5b990cb2b..fb3b6cd063 100644 --- a/libopenage/event/time_loop.h +++ b/libopenage/time/time_loop.h @@ -5,6 +5,7 @@ #include #include + namespace openage { namespace event { From 6e977ce08cdb7e5104fcc60d81cb1ae99b7edc8e Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 26 Jul 2023 01:21:50 +0200 Subject: [PATCH 05/49] refactor: Separate time classes/types into time namespace. --- libopenage/curve/base_curve.h | 38 +++++------ libopenage/curve/continuous.h | 12 ++-- libopenage/curve/discrete.h | 12 ++-- libopenage/curve/discrete_mod.h | 38 +++++------ libopenage/curve/interpolated.h | 8 +-- libopenage/curve/iterator.h | 16 ++--- libopenage/curve/keyframe.h | 6 +- libopenage/curve/keyframe_container.h | 44 ++++++------ libopenage/curve/map.h | 64 ++++++++--------- libopenage/curve/map_filter_iterator.h | 4 +- libopenage/curve/queue.h | 48 ++++++------- libopenage/curve/queue_filter_iterator.h | 4 +- libopenage/curve/segmented.h | 10 +-- libopenage/curve/tests/curve_types.cpp | 6 +- libopenage/engine/engine.cpp | 2 +- libopenage/engine/engine.h | 10 +-- libopenage/event/demo/aicontroller.cpp | 4 +- libopenage/event/demo/aicontroller.h | 4 +- libopenage/event/demo/gamestate.h | 4 +- libopenage/event/demo/gui.cpp | 8 +-- libopenage/event/demo/gui.h | 6 +- libopenage/event/demo/main.cpp | 4 +- libopenage/event/demo/physics.cpp | 68 +++++++++---------- libopenage/event/demo/physics.h | 6 +- libopenage/event/event.cpp | 2 +- libopenage/event/event.h | 16 ++--- libopenage/event/event_loop.cpp | 18 ++--- libopenage/event/event_loop.h | 10 +-- libopenage/event/evententity.cpp | 6 +- libopenage/event/evententity.h | 6 +- libopenage/event/eventhandler.h | 8 +-- libopenage/event/eventqueue.cpp | 14 ++-- libopenage/event/eventqueue.h | 10 +-- libopenage/event/tests.cpp | 46 ++++++------- libopenage/gamestate/activity/event_node.h | 8 +-- libopenage/gamestate/activity/task_node.h | 4 +- libopenage/gamestate/activity/tests.cpp | 16 ++--- libopenage/gamestate/activity/xor_node.h | 4 +- libopenage/gamestate/component/api/live.cpp | 4 +- libopenage/gamestate/component/api/live.h | 4 +- .../gamestate/component/internal/activity.cpp | 8 +-- .../gamestate/component/internal/activity.h | 8 +-- .../component/internal/command_queue.cpp | 4 +- .../component/internal/command_queue.h | 4 +- .../component/internal/ownership.cpp | 4 +- .../gamestate/component/internal/ownership.h | 4 +- .../gamestate/component/internal/position.cpp | 6 +- .../gamestate/component/internal/position.h | 6 +- libopenage/gamestate/entity_factory.cpp | 14 ++-- .../gamestate/event/process_command.cpp | 6 +- libopenage/gamestate/event/process_command.h | 6 +- libopenage/gamestate/event/send_command.cpp | 6 +- libopenage/gamestate/event/send_command.h | 6 +- libopenage/gamestate/event/spawn_entity.cpp | 6 +- libopenage/gamestate/event/spawn_entity.h | 6 +- libopenage/gamestate/event/wait.cpp | 6 +- libopenage/gamestate/event/wait.h | 6 +- libopenage/gamestate/game_entity.cpp | 2 +- libopenage/gamestate/game_entity.h | 2 +- libopenage/gamestate/manager.cpp | 2 +- libopenage/gamestate/manager.h | 2 +- libopenage/gamestate/simulation.cpp | 2 +- libopenage/gamestate/simulation.h | 9 ++- libopenage/gamestate/system/activity.cpp | 10 +-- libopenage/gamestate/system/activity.h | 6 +- libopenage/gamestate/system/idle.cpp | 6 +- libopenage/gamestate/system/idle.h | 4 +- libopenage/gamestate/system/move.cpp | 12 ++-- libopenage/gamestate/system/move.h | 8 +-- .../input/controller/game/controller.cpp | 2 +- libopenage/input/controller/game/controller.h | 10 +-- libopenage/main/tests/aicontroller.cpp | 20 +++--- libopenage/main/tests/aicontroller.h | 4 +- libopenage/main/tests/gamestate.cpp | 4 +- libopenage/main/tests/gamestate.h | 4 +- libopenage/main/tests/gui.cpp | 2 +- libopenage/main/tests/gui.h | 2 +- libopenage/main/tests/physics.cpp | 66 +++++++++--------- libopenage/main/tests/physics.h | 6 +- libopenage/main/tests/pong.cpp | 4 +- libopenage/presenter/presenter.cpp | 4 +- libopenage/presenter/presenter.h | 14 ++-- libopenage/renderer/demo/demo_3.cpp | 2 +- libopenage/renderer/demo/demo_4.cpp | 6 +- .../resources/animation/layer_info.cpp | 4 +- .../renderer/resources/frame_timing.cpp | 16 ++--- libopenage/renderer/resources/frame_timing.h | 16 ++--- .../renderer/resources/terrain/layer_info.cpp | 4 +- .../renderer/stages/terrain/terrain_mesh.cpp | 2 +- .../renderer/stages/terrain/terrain_mesh.h | 2 +- .../renderer/stages/terrain/terrain_model.cpp | 2 +- .../renderer/stages/terrain/terrain_model.h | 2 +- .../stages/terrain/terrain_render_entity.cpp | 2 +- .../stages/terrain/terrain_render_entity.h | 2 +- .../stages/terrain/terrain_renderer.cpp | 2 +- .../stages/terrain/terrain_renderer.h | 7 +- .../renderer/stages/world/world_object.cpp | 4 +- .../renderer/stages/world/world_object.h | 6 +- .../stages/world/world_render_entity.cpp | 6 +- .../stages/world/world_render_entity.h | 8 +-- .../renderer/stages/world/world_renderer.cpp | 2 +- .../renderer/stages/world/world_renderer.h | 6 +- libopenage/time/clock.cpp | 8 +-- libopenage/time/clock.h | 12 ++-- libopenage/time/time.cpp | 6 +- libopenage/time/time.h | 6 +- libopenage/time/time_loop.cpp | 4 +- libopenage/time/time_loop.h | 7 +- 108 files changed, 530 insertions(+), 529 deletions(-) diff --git a/libopenage/curve/base_curve.h b/libopenage/curve/base_curve.h index 5534cf43dc..c5132e3f80 100644 --- a/libopenage/curve/base_curve.h +++ b/libopenage/curve/base_curve.h @@ -35,9 +35,9 @@ class BaseCurve : public event::EventEntity { BaseCurve(BaseCurve &&) = default; - virtual T get(const time_t &t) const = 0; + virtual T get(const time::time_t &t) const = 0; - virtual T operator()(const time_t &now) { + virtual T operator()(const time::time_t &now) { return get(now); } @@ -46,14 +46,14 @@ class BaseCurve : public event::EventEntity { * * @return Keyframe time and value. */ - virtual std::pair frame(const time_t &time) const; + virtual std::pair frame(const time::time_t &time) const; /** * Get the closest keyframe with t > \p time. * * @return Keyframe time and value. */ - virtual std::pair next_frame(const time_t &time) const; + virtual std::pair next_frame(const time::time_t &time) const; /** * Insert/overwrite given value at given time and erase all elements @@ -61,26 +61,26 @@ class BaseCurve : public event::EventEntity { * If multiple elements exist at the given time, * overwrite the last one. */ - virtual void set_last(const time_t &at, const T &value); + virtual void set_last(const time::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); + virtual void set_insert(const time::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); + virtual void set_replace(const time::time_t &at, const T &value); /** * Remove all values that have the given time. */ - virtual void erase(const time_t &at); + virtual void erase(const time::time_t &at); /** * Integrity check, for debugging/testing reasons only. @@ -99,7 +99,7 @@ class BaseCurve : public event::EventEntity { * the keyframes of \p other. */ void sync(const BaseCurve &other, - const time_t &start = std::numeric_limits::min()); + const time::time_t &start = std::numeric_limits::min()); /** * Copy keyframes from another curve (with a different element type) to this curve. @@ -118,7 +118,7 @@ class BaseCurve : public event::EventEntity { template void sync(const BaseCurve &other, const std::function &converter, - const time_t &start = std::numeric_limits::min()); + const time::time_t &start = std::numeric_limits::min()); /** * Get the identifier of this curve. @@ -184,7 +184,7 @@ class BaseCurve : public event::EventEntity { template -void BaseCurve::set_last(const time_t &at, const T &value) { +void BaseCurve::set_last(const time::time_t &at, const T &value) { auto hint = this->container.last(at, this->last_element); // erase max one same-time value @@ -202,7 +202,7 @@ void BaseCurve::set_last(const time_t &at, const T &value) { template -void BaseCurve::set_insert(const time_t &at, const T &value) { +void BaseCurve::set_insert(const time::time_t &at, const T &value) { auto hint = this->container.insert_after(at, value, this->last_element); // check if this is now the final keyframe if (hint->time > this->last_element->time) { @@ -213,28 +213,28 @@ void BaseCurve::set_insert(const time_t &at, const T &value) { template -void BaseCurve::set_replace(const time_t &at, const T &value) { +void BaseCurve::set_replace(const time::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) { +void BaseCurve::erase(const time::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 { +std::pair BaseCurve::frame(const time::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 { +std::pair BaseCurve::next_frame(const time::time_t &time) const { auto e = this->container.last(time, this->container.end()); e++; return std::make_pair(e->time, e->value); @@ -254,7 +254,7 @@ std::string BaseCurve::str() const { template void BaseCurve::check_integrity() const { - time_t last_time = std::numeric_limits::min(); + time::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()}; @@ -265,7 +265,7 @@ void BaseCurve::check_integrity() const { template void BaseCurve::sync(const BaseCurve &other, - const time_t &start) { + const time::time_t &start) { // Copy keyframes between containers for t >= start this->last_element = this->container.sync_after(other.container, start); @@ -284,7 +284,7 @@ template template void BaseCurve::sync(const BaseCurve &other, const std::function &converter, - const time_t &start) { + const time::time_t &start) { // Copy keyframes between containers for t >= start this->last_element = this->container.sync_after(other.get_container(), converter, start); diff --git a/libopenage/curve/continuous.h b/libopenage/curve/continuous.h index 0d32f14629..72825f16b5 100644 --- a/libopenage/curve/continuous.h +++ b/libopenage/curve/continuous.h @@ -1,4 +1,4 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #pragma once @@ -19,7 +19,7 @@ namespace openage::curve { * interpolation. * * The bound template type T has to implement `operator+(T)` and - * `operator*(time_t)`. + * `operator*(time::time_t)`. */ template class Continuous : public Interpolated { @@ -32,10 +32,10 @@ class Continuous : public Interpolated { * If multiple elements exist at the given time, * overwrite all of them. */ - void set_last(const time_t &t, const T &value) override; + void set_last(const time::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; + void set_insert(const time::time_t &t, const T &value) override; /** human readable identifier */ std::string idstr() const override; @@ -43,7 +43,7 @@ class Continuous : public Interpolated { template -void Continuous::set_last(const time_t &at, const T &value) { +void Continuous::set_last(const time::time_t &at, const T &value) { auto hint = this->container.last(at, this->last_element); // erase all same-time entries @@ -61,7 +61,7 @@ void Continuous::set_last(const time_t &at, const T &value) { template -void Continuous::set_insert(const time_t &t, const T &value) { +void Continuous::set_insert(const time::time_t &t, const T &value) { this->set_replace(t, value); } diff --git a/libopenage/curve/discrete.h b/libopenage/curve/discrete.h index 085f85b0fd..e1e9be6302 100644 --- a/libopenage/curve/discrete.h +++ b/libopenage/curve/discrete.h @@ -29,7 +29,7 @@ class Discrete : public BaseCurve { * Does not interpolate anything, * just returns gives the raw value of the last keyframe with time <= t. */ - T get(const time_t &t) const override; + T get(const time::time_t &t) const override; /** * Get a human readable id string. @@ -39,17 +39,17 @@ class Discrete : public BaseCurve { /** * Return the last time and keyframe with time <= t. */ - std::pair get_time(const time_t &t) const; + std::pair get_time(const time::time_t &t) const; /** * Return, if existing, the time and value of keyframe with time < t */ - std::optional> get_previous(const time_t &t) const; + std::optional> get_previous(const time::time_t &t) const; }; template -T Discrete::get(const time_t &time) const { +T Discrete::get(const time::time_t &time) const { auto e = this->container.last(time, this->last_element); this->last_element = e; // TODO if Caching? return e->value; @@ -72,7 +72,7 @@ std::string Discrete::idstr() const { template -std::pair Discrete::get_time(const time_t &time) const { +std::pair Discrete::get_time(const time::time_t &time) const { auto e = this->container.last(time, this->last_element); this->last_element = e; return std::make_pair(e->time, e->value); @@ -80,7 +80,7 @@ std::pair Discrete::get_time(const time_t &time) const { template -std::optional> Discrete::get_previous(const time_t &time) const { +std::optional> Discrete::get_previous(const time::time_t &time) const { auto e = this->container.last(time, this->last_element); this->last_element = e; diff --git a/libopenage/curve/discrete_mod.h b/libopenage/curve/discrete_mod.h index ef16b23088..ddb0ebb95b 100644 --- a/libopenage/curve/discrete_mod.h +++ b/libopenage/curve/discrete_mod.h @@ -30,9 +30,9 @@ class DiscreteMod : public Discrete { // Override insertion/erasure to get interval time - void set_last(const time_t &at, const T &value) override; - void set_insert(const time_t &at, const T &value) override; - void erase(const time_t &at) override; + void set_last(const time::time_t &at, const T &value) override; + void set_insert(const time::time_t &at, const T &value) override; + void erase(const time::time_t &at) override; /** * Get a human readable id string. @@ -42,35 +42,35 @@ class DiscreteMod : public Discrete { /** * Get the raw value of the last keyframe with time <= (t - start) % interval_length. */ - T get_mod(const time_t &t, const time_t &start) const; + T get_mod(const time::time_t &t, const time::time_t &start) const; /** * Get the last time and keyframe with time <= (t - start) % interval_length. */ - std::pair get_time_mod(const time_t &t, const time_t &start) const; + std::pair get_time_mod(const time::time_t &t, const time::time_t &start) const; /** * Get, if existing, the time and value of keyframe with time < (t - start) % interval_length. */ - std::optional> get_previous_mod(const time_t &t, const time_t &start) const; + std::optional> get_previous_mod(const time::time_t &t, const time::time_t &start) const; private: /** * Length of the time interval of this curve (time between first and last keyframe). */ - time_t time_length; + time::time_t time_length; }; template -void DiscreteMod::set_last(const time_t &at, const T &value) { +void DiscreteMod::set_last(const time::time_t &at, const T &value) { BaseCurve::set_last(at, value); this->time_length = at; } template -void DiscreteMod::set_insert(const time_t &at, const T &value) { +void DiscreteMod::set_insert(const time::time_t &at, const T &value) { BaseCurve::set_insert(at, value); if (this->time_length < at) { @@ -80,7 +80,7 @@ void DiscreteMod::set_insert(const time_t &at, const T &value) { template -void DiscreteMod::erase(const time_t &at) { +void DiscreteMod::erase(const time::time_t &at) { BaseCurve::erase(at); if (this->time_length == at) { @@ -105,40 +105,40 @@ std::string DiscreteMod::idstr() const { template -T DiscreteMod::get_mod(const time_t &time, const time_t &start) const { - time_t offset = time - start; +T DiscreteMod::get_mod(const time::time_t &time, const time::time_t &start) const { + time::time_t offset = time - start; if (this->time_length == 0) { // modulo would fail here so return early return Discrete::get(0); } - time_t mod = offset % this->time_length; + time::time_t mod = offset % this->time_length; return Discrete::get(mod); } template -std::pair DiscreteMod::get_time_mod(const time_t &time, const time_t &start) const { - time_t offset = time - start; +std::pair DiscreteMod::get_time_mod(const time::time_t &time, const time::time_t &start) const { + time::time_t offset = time - start; if (this->time_length == 0) { // modulo would fail here so return early return Discrete::get_time(0); } - time_t mod = offset % this->time_length; + time::time_t mod = offset % this->time_length; return Discrete::get_time(mod); } template -std::optional> DiscreteMod::get_previous_mod(const time_t &time, const time_t &start) const { - time_t offset = time - start; +std::optional> DiscreteMod::get_previous_mod(const time::time_t &time, const time::time_t &start) const { + time::time_t offset = time - start; if (this->time_length == 0) { // modulo would fail here so return early return Discrete::get_previous(0); } - time_t mod = offset % this->time_length; + time::time_t mod = offset % this->time_length; return Discrete::get_previous(mod); } diff --git a/libopenage/curve/interpolated.h b/libopenage/curve/interpolated.h index bdb1fe1743..7942a97c54 100644 --- a/libopenage/curve/interpolated.h +++ b/libopenage/curve/interpolated.h @@ -15,7 +15,7 @@ namespace openage::curve { * Extends the value container to support interpolation between values. * * The bound template type T has to implement `operator +(T)` and - * `operator *(time_t)`. + * `operator *(time::time_t)`. */ template class Interpolated : public BaseCurve { @@ -34,19 +34,19 @@ class Interpolated : public BaseCurve { * val([a:x, b:y], t) = x + (y - x)/(b - a) * (t - a) */ - T get(const time_t &) const override; + T get(const time::time_t &) const override; }; template -T Interpolated::get(const time_t &time) const { +T Interpolated::get(const time::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; + time::time_t interval = 0; auto offset = time - e->time; diff --git a/libopenage/curve/iterator.h b/libopenage/curve/iterator.h index 49b36d12eb..c2374e7267 100644 --- a/libopenage/curve/iterator.h +++ b/libopenage/curve/iterator.h @@ -31,8 +31,8 @@ class CurveIterator { explicit CurveIterator(const container_t *c) : base{}, container{c}, - from{-std::numeric_limits::max()}, - to{+std::numeric_limits::max()} {} + from{-std::numeric_limits::max()}, + to{+std::numeric_limits::max()} {} protected: /** @@ -40,8 +40,8 @@ class CurveIterator { */ CurveIterator(const iterator_t &base, const container_t *container, - const time_t &from, - const time_t &to) : + const time::time_t &from, + const time::time_t &to) : base{base}, container{container}, from{from}, @@ -105,14 +105,14 @@ class CurveIterator { /** * Access the lower end value of the defined time frame */ - const time_t &get_from() const { + const time::time_t &get_from() const { return from; } /** * Access the higher end value of the defined time frame */ - const time_t &get_to() const { + const time::time_t &get_to() const { return to; } @@ -124,10 +124,10 @@ class CurveIterator { const container_t *container; /// The time, from where this iterator started to iterate. - time_t from; + time::time_t from; /// The time, to where this iterator will iterate. - time_t to; + time::time_t to; }; diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h index 7259745b20..cc1553f042 100644 --- a/libopenage/curve/keyframe.h +++ b/libopenage/curve/keyframe.h @@ -23,17 +23,17 @@ class Keyframe { /** * New, default-constructed element at the given time */ - Keyframe(const time_t &time) : + Keyframe(const time::time_t &time) : time{time} {} /** * New element fron time and value */ - Keyframe(const time_t &time, const T &value) : + Keyframe(const time::time_t &time, const T &value) : time{time}, value{value} {} - const time_t time = std::numeric_limits::min(); + const time::time_t time = std::numeric_limits::min(); T value = T{}; }; diff --git a/libopenage/curve/keyframe_container.h b/libopenage/curve/keyframe_container.h index 51de426d33..33459045e2 100644 --- a/libopenage/curve/keyframe_container.h +++ b/libopenage/curve/keyframe_container.h @@ -78,7 +78,7 @@ class KeyframeContainer { * Get the last element in the curve which is at or before the given time. * (i.e. elem->time <= time). Given a hint where to start the search. */ - iterator last(const time_t &time, + iterator last(const time::time_t &time, const iterator &hint) const; /** @@ -89,7 +89,7 @@ class KeyframeContainer { * no chance for you to have a hint (or the container is known to be nearly * empty) */ - iterator last(const time_t &time) const { + iterator last(const time::time_t &time) const { return this->last(time, std::end(this->container)); } @@ -97,14 +97,14 @@ class KeyframeContainer { * Get the last element in the curve which is before the given time. * (i.e. elem->time < time). Given a hint where to start the search. */ - iterator last_before(const time_t &time, + iterator last_before(const time::time_t &time, const iterator &hint) const; /** * Get the last element with elem->time < time, without a hint where to start * searching. */ - iterator last_before(const time_t &time) const { + iterator last_before(const time::time_t &time) const { return this->last_before(time, std::end(this->container)); } @@ -134,7 +134,7 @@ class KeyframeContainer { * discouraged, use it only, if your really do not have the possibility to * get a hint. */ - iterator insert_before(const time_t &time, const T &value) { + iterator insert_before(const time::time_t &time, const T &value) { return this->insert_before(keyframe_t(time, value), std::end(this->container)); } @@ -143,7 +143,7 @@ class KeyframeContainer { * If there is a value with identical time, this will insert the new value * before the old one. */ - iterator insert_before(const time_t &time, const T &value, const iterator &hint) { + iterator insert_before(const time::time_t &time, const T &value, const iterator &hint) { return this->insert_before(keyframe_t(time, value), hint); } @@ -164,7 +164,7 @@ class KeyframeContainer { * from the end of the data. The use of this function is discouraged, use it * only, if your really do not have the possibility to get a hint. */ - iterator insert_overwrite(const time_t &time, const T &value) { + iterator insert_overwrite(const time::time_t &time, const T &value) { return this->insert_overwrite(keyframe_t(time, value), std::end(this->container)); } @@ -176,7 +176,7 @@ class KeyframeContainer { * Provide a insertion hint to abbreviate the search for the * insertion point. */ - iterator insert_overwrite(const time_t &time, + iterator insert_overwrite(const time::time_t &time, const T &value, const iterator &hint, bool overwrite_all = false) { @@ -196,7 +196,7 @@ class KeyframeContainer { * time from the end of the data. The use of this function is discouraged, * use it only, if your really do not have the possibility to get a hint. */ - iterator insert_after(const time_t &time, const T &value) { + iterator insert_after(const time::time_t &time, const T &value) { return this->insert_after(keyframe_t(time, value), std::end(this->container)); } @@ -205,7 +205,7 @@ class KeyframeContainer { * Create and insert a new element, which is added after a previous element with * identical time. Provide a insertion hint to abbreviate the search for the insertion point. */ - iterator insert_after(const time_t &time, const T &value, const iterator &hint) { + iterator insert_after(const time::time_t &time, const T &value, const iterator &hint) { return this->insert_after(keyframe_t(time, value), hint); } @@ -225,7 +225,7 @@ class KeyframeContainer { * Variant without hint, starts the search at the end of the container. * Returns the iterator after the deleted elements. */ - iterator erase(const time_t &time) { + iterator erase(const time::time_t &time) { return this->erase(time, std::end(this->container)); } @@ -238,7 +238,7 @@ class KeyframeContainer { * Or, if no elements with this time exist, * the iterator to the first element after the requested time is returned */ - iterator erase(const time_t &time, + iterator erase(const time::time_t &time, const iterator &hint) { return this->erase_group(time, this->last(time, hint)); } @@ -280,7 +280,7 @@ class KeyframeContainer { * the keyframes of \p other. */ iterator sync_after(const KeyframeContainer &other, - const time_t &start = std::numeric_limits::min()); + const time::time_t &start = std::numeric_limits::min()); /** * Copy keyframes from another container (with a different element type) to this container. @@ -297,7 +297,7 @@ class KeyframeContainer { template iterator sync_after(const KeyframeContainer &other, const std::function &converter, - const time_t &start = std::numeric_limits::min()); + const time::time_t &start = std::numeric_limits::min()); /** * Debugging method to be used from gdb to understand bugs better. @@ -313,7 +313,7 @@ class KeyframeContainer { * Erase elements with this time. * The iterator has to point to the last element of the same-time group. */ - iterator erase_group(const time_t &time, + iterator erase_group(const time::time_t &time, const iterator &last_elem); /** @@ -327,7 +327,7 @@ template KeyframeContainer::KeyframeContainer() { // Create a default element at -Inf, that can always be dereferenced - so // there will by definition never be a element that cannot be dereferenced - this->container.push_back(keyframe_t(std::numeric_limits::min(), T())); + this->container.push_back(keyframe_t(std::numeric_limits::min(), T())); } @@ -335,7 +335,7 @@ template KeyframeContainer::KeyframeContainer(const T &defaultval) { // Create a default element at -Inf, that can always be dereferenced - so // there will by definition never be a element that cannot be dereferenced - this->container.push_back(keyframe_t(std::numeric_limits::min(), defaultval)); + this->container.push_back(keyframe_t(std::numeric_limits::min(), defaultval)); } @@ -354,7 +354,7 @@ size_t KeyframeContainer::size() const { * that determines the curve value for a searched time. */ template -typename KeyframeContainer::iterator KeyframeContainer::last(const time_t &time, +typename KeyframeContainer::iterator KeyframeContainer::last(const time::time_t &time, const iterator &hint) const { iterator e = hint; auto end = std::end(this->container); @@ -388,7 +388,7 @@ typename KeyframeContainer::iterator KeyframeContainer::last(const time_t * first element that matches the search time. */ template -typename KeyframeContainer::iterator KeyframeContainer::last_before(const time_t &time, +typename KeyframeContainer::iterator KeyframeContainer::last_before(const time::time_t &time, const iterator &hint) const { iterator e = hint; auto end = std::end(this->container); @@ -512,7 +512,7 @@ KeyframeContainer::erase(KeyframeContainer::iterator e) { template typename KeyframeContainer::iterator KeyframeContainer::sync_after(const KeyframeContainer &other, - const time_t &start) { + const time::time_t &start) { // Delete elements after start time iterator at = this->last_before(start, this->end()); at = this->erase_after(at); @@ -537,7 +537,7 @@ template typename KeyframeContainer::iterator KeyframeContainer::sync_after(const KeyframeContainer &other, const std::function &converter, - const time_t &start) { + const time::time_t &start) { // Delete elements after start time iterator at = this->last_before(start, this->end()); at = this->erase_after(at); @@ -560,7 +560,7 @@ KeyframeContainer::sync_after(const KeyframeContainer &other, template typename KeyframeContainer::iterator -KeyframeContainer::erase_group(const time_t &time, +KeyframeContainer::erase_group(const time::time_t &time, const iterator &last_elem) { iterator at = last_elem; diff --git a/libopenage/curve/map.h b/libopenage/curve/map.h index 49e6e5679c..22145d7226 100644 --- a/libopenage/curve/map.h +++ b/libopenage/curve/map.h @@ -20,14 +20,14 @@ template class UnorderedMap { /** Internal container to access all data and metadata */ struct map_element { - map_element(const val_t &v, const time_t &a, const time_t &d) : + map_element(const val_t &v, const time::time_t &a, const time::time_t &d) : value(v), alive(a), dead(d) {} val_t value; - time_t alive; - time_t dead; + time::time_t alive; + time::time_t dead; }; /** @@ -40,36 +40,36 @@ class UnorderedMap { using const_iterator = typename std::unordered_map::const_iterator; std::optional> - operator()(const time_t &, const key_t &) const; + operator()(const time::time_t &, const key_t &) const; std::optional> - at(const time_t &, const key_t &) const; + at(const time::time_t &, const key_t &) const; MapFilterIterator - begin(const time_t &e = std::numeric_limits::max()) const; + begin(const time::time_t &e = std::numeric_limits::max()) const; MapFilterIterator - end(const time_t &e = std::numeric_limits::max()) const; + end(const time::time_t &e = std::numeric_limits::max()) const; MapFilterIterator - insert(const time_t &birth, const key_t &, const val_t &); + insert(const time::time_t &birth, const key_t &, const val_t &); MapFilterIterator - insert(const time_t &birth, const time_t &death, const key_t &key, const val_t &value); + insert(const time::time_t &birth, const time::time_t &death, const key_t &key, const val_t &value); MapFilterIterator - between(const time_t &start, const time_t &to) const; + between(const time::time_t &start, const time::time_t &to) const; - void birth(const time_t &, const key_t &); - void birth(const time_t &, + void birth(const time::time_t &, const key_t &); + void birth(const time::time_t &, const MapFilterIterator &); - void kill(const time_t &, const key_t &); - void kill(const time_t &, + void kill(const time::time_t &, const key_t &); + void kill(const time::time_t &, const MapFilterIterator &); // remove all dead elements before that point in time - void clean(const time_t &); + void clean(const time::time_t &); /** * gdb helper method. @@ -83,14 +83,14 @@ class UnorderedMap { template std::optional>> -UnorderedMap::operator()(const time_t &time, +UnorderedMap::operator()(const time::time_t &time, const key_t &key) const { return this->at(time, key); } template std::optional>> -UnorderedMap::at(const time_t &time, +UnorderedMap::at(const time::time_t &time, const key_t &key) const { auto e = this->container.find(key); if (e != this->container.end() and e->second.alive <= time and e->second.dead > time) { @@ -98,7 +98,7 @@ UnorderedMap::at(const time_t &time, e, this, time, - std::numeric_limits::max()); + std::numeric_limits::max()); } else { return {}; @@ -107,27 +107,27 @@ UnorderedMap::at(const time_t &time, template MapFilterIterator> -UnorderedMap::begin(const time_t &time) const { +UnorderedMap::begin(const time::time_t &time) const { return MapFilterIterator>( this->container.begin(), this, time, - std::numeric_limits::max()); + std::numeric_limits::max()); } template MapFilterIterator> -UnorderedMap::end(const time_t &time) const { +UnorderedMap::end(const time::time_t &time) const { return MapFilterIterator>( this->container.end(), this, - -std::numeric_limits::max(), + -std::numeric_limits::max(), time); } template MapFilterIterator> -UnorderedMap::between(const time_t &from, const time_t &to) const { +UnorderedMap::between(const time::time_t &from, const time::time_t &to) const { auto it = MapFilterIterator>( this->container.begin(), this, @@ -142,20 +142,20 @@ UnorderedMap::between(const time_t &from, const time_t &to) const template MapFilterIterator> -UnorderedMap::insert(const time_t &alive, +UnorderedMap::insert(const time::time_t &alive, const key_t &key, const val_t &value) { return this->insert( alive, - std::numeric_limits::max(), + std::numeric_limits::max(), key, value); } template MapFilterIterator> -UnorderedMap::insert(const time_t &alive, - const time_t &dead, +UnorderedMap::insert(const time::time_t &alive, + const time::time_t &dead, const key_t &key, const val_t &value) { map_element e(value, alive, dead); @@ -168,7 +168,7 @@ UnorderedMap::insert(const time_t &alive, } template -void UnorderedMap::birth(const time_t &time, +void UnorderedMap::birth(const time::time_t &time, const key_t &key) { auto it = this->container.find(key); if (it != this->container.end()) { @@ -177,13 +177,13 @@ void UnorderedMap::birth(const time_t &time, } template -void UnorderedMap::birth(const time_t &time, +void UnorderedMap::birth(const time::time_t &time, const MapFilterIterator &it) { it->second.alive = time; } template -void UnorderedMap::kill(const time_t &time, +void UnorderedMap::kill(const time::time_t &time, const key_t &key) { auto it = this->container.find(key); if (it != this->container.end()) { @@ -192,13 +192,13 @@ void UnorderedMap::kill(const time_t &time, } template -void UnorderedMap::kill(const time_t &time, +void UnorderedMap::kill(const time::time_t &time, const MapFilterIterator &it) { it->second.dead = time; } template -void UnorderedMap::clean(const time_t &) { +void UnorderedMap::clean(const time::time_t &) { // TODO save everything to a file and be happy. } diff --git a/libopenage/curve/map_filter_iterator.h b/libopenage/curve/map_filter_iterator.h index 84f4402a06..b307239619 100644 --- a/libopenage/curve/map_filter_iterator.h +++ b/libopenage/curve/map_filter_iterator.h @@ -31,8 +31,8 @@ class MapFilterIterator : public CurveIterator { */ MapFilterIterator(const iterator_t &base, const container_t *container, - const time_t &from, - const time_t &to) : + const time::time_t &from, + const time::time_t &to) : CurveIterator(base, container, from, to) {} MapFilterIterator(const MapFilterIterator &) = default; diff --git a/libopenage/curve/queue.h b/libopenage/curve/queue.h index 8ba6f7e8e8..dcdd9ed10a 100644 --- a/libopenage/curve/queue.h +++ b/libopenage/curve/queue.h @@ -20,14 +20,14 @@ namespace openage::curve { template class Queue : public event::EventEntity { struct queue_wrapper { - time_t _time; + time::time_t _time; T value; - queue_wrapper(const time_t &time, const T &value) : + queue_wrapper(const time::time_t &time, const T &value) : _time{time}, value{value} {} - time_t time() const { + time::time_t time() const { return _time; } }; @@ -56,7 +56,7 @@ class Queue : public event::EventEntity { * @param time The time to get the element at. * @return Queue element. */ - const T &front(const time_t &time) const; + const T &front(const time::time_t &time) const; /** * Get the first element in the queue at the given time. @@ -64,14 +64,14 @@ class Queue : public event::EventEntity { * @param time The time to get the element at. * @param value Queue element. */ - const T &pop_front(const time_t &time); + const T &pop_front(const time::time_t &time); /** * Check if the queue is empty at a given time. * * @return true if the queue is empty, false otherwise. */ - bool empty(const time_t &time) const; + bool empty(const time::time_t &time) const; // Modifying access @@ -82,7 +82,7 @@ class Queue : public event::EventEntity { * @return Iterator to the first element. */ QueueFilterIterator> begin( - const time_t &t = -std::numeric_limits::max()) const; + const time::time_t &t = -std::numeric_limits::max()) const; /** * Get an iterator to the last element in the queue at the given time. @@ -91,7 +91,7 @@ class Queue : public event::EventEntity { * @return Iterator to the last element. */ QueueFilterIterator> end( - const time_t &t = std::numeric_limits::max()) const; + const time::time_t &t = std::numeric_limits::max()) const; /** * Get an iterator to elements that are in the queue between two time frames. @@ -101,8 +101,8 @@ class Queue : public event::EventEntity { * @return Iterator to the first element in the time frame. */ QueueFilterIterator> between( - const time_t &begin = std::numeric_limits::max(), - const time_t &end = std::numeric_limits::max()) const; + const time::time_t &begin = std::numeric_limits::max(), + const time::time_t &end = std::numeric_limits::max()) const; /** * Erase an element from the queue. @@ -118,14 +118,14 @@ class Queue : public event::EventEntity { * @param e The element to insert. * @return Iterator to the inserted element. */ - QueueFilterIterator> insert(const time_t &, const T &e); + QueueFilterIterator> insert(const time::time_t &, const T &e); /** * Erase all elements that are at or after the given time. * * @param time The time to clear at. */ - void clear(const time_t &); + void clear(const time::time_t &); /** * Print the queue to stdout. @@ -178,30 +178,30 @@ class Queue : public event::EventEntity { template -const T &Queue::front(const time_t &t) const { +const T &Queue::front(const time::time_t &t) const { return this->begin(t).value(); } template -bool Queue::empty(const time_t &time) const { +bool Queue::empty(const time::time_t &time) const { return this->last_front == this->begin(time).get_base(); } template -inline const T &Queue::pop_front(const time_t &time) { +inline const T &Queue::pop_front(const time::time_t &time) { this->last_front = this->begin(time).get_base(); return this->front(time); } template -QueueFilterIterator> Queue::begin(const time_t &t) const { +QueueFilterIterator> Queue::begin(const time::time_t &t) const { for (auto it = this->container.begin(); it != this->container.end(); ++it) { if (it->time() >= t) { return QueueFilterIterator>( it, this, t, - std::numeric_limits::max()); + std::numeric_limits::max()); } } @@ -210,19 +210,19 @@ QueueFilterIterator> Queue::begin(const time_t &t) const { template -QueueFilterIterator> Queue::end(const time_t &t) const { +QueueFilterIterator> Queue::end(const time::time_t &t) const { return QueueFilterIterator>( container.end(), this, t, - std::numeric_limits::max()); + std::numeric_limits::max()); } template QueueFilterIterator> Queue::between( - const time_t &begin, - const time_t &end) const { + const time::time_t &begin, + const time::time_t &end) const { auto it = QueueFilterIterator>( container.begin(), this, @@ -244,7 +244,7 @@ void Queue::erase(const CurveIterator> &it) { template QueueFilterIterator> Queue::insert( - const time_t &time, + const time::time_t &time, const T &e) { const_iterator insertion_point = this->container.end(); for (auto it = this->container.begin(); it != this->container.end(); ++it) { @@ -262,7 +262,7 @@ QueueFilterIterator> Queue::insert( insertion_point, this, time, - std::numeric_limits::max()); + std::numeric_limits::max()); if (!ct.valid()) { ++ct; @@ -275,7 +275,7 @@ QueueFilterIterator> Queue::insert( template -void Queue::clear(const time_t &time) { +void Queue::clear(const time::time_t &time) { for (auto it = this->container.begin(); it != this->container.end() and it->time() < time; it = this->container.erase(it)) { diff --git a/libopenage/curve/queue_filter_iterator.h b/libopenage/curve/queue_filter_iterator.h index 5871cf1d96..d157a766a9 100644 --- a/libopenage/curve/queue_filter_iterator.h +++ b/libopenage/curve/queue_filter_iterator.h @@ -30,8 +30,8 @@ class QueueFilterIterator : public CurveIterator(base, base_container, from, to) {} virtual bool valid() const override { diff --git a/libopenage/curve/segmented.h b/libopenage/curve/segmented.h index 778a68e1b1..250e2c22c3 100644 --- a/libopenage/curve/segmented.h +++ b/libopenage/curve/segmented.h @@ -22,7 +22,7 @@ namespace openage::curve { * Use the insertion operators of `ValueContainer`: `set_last`, `set_insert` and `set_replace`. * * The bound template type T has to implement `operator +(T)` and - * `operator *(time_t)`. + * `operator *(time::time_t)`. */ template class Segmented : public Interpolated { @@ -34,14 +34,14 @@ class Segmented : public Interpolated { * The right value is used for queries at >= time, * the left value for queries at < time. */ - void set_insert_jump(const time_t &, const T &leftval, const T &rightval); + void set_insert_jump(const time::time_t &, const T &leftval, const T &rightval); /** * Insert/replace a value jump with given left and right values. * All following curve keyframes will be deleted, so the * last two values of the curve will be `leftval` and `rightval`. */ - void set_last_jump(const time_t &, const T &leftval, const T &rightval); + void set_last_jump(const time::time_t &, const T &leftval, const T &rightval); /** human readable identifier */ std::string idstr() const override; @@ -49,7 +49,7 @@ class Segmented : public Interpolated { template -void Segmented::set_insert_jump(const time_t &at, const T &leftval, const T &rightval) { +void Segmented::set_insert_jump(const time::time_t &at, const T &leftval, const T &rightval) { auto hint = this->container.insert_overwrite(at, leftval, this->last_element, true); this->container.insert_after(at, rightval, hint); this->changes(at); @@ -57,7 +57,7 @@ void Segmented::set_insert_jump(const time_t &at, const T &leftval, const T & template -void Segmented::set_last_jump(const time_t &at, const T &leftval, const T &rightval) { +void Segmented::set_last_jump(const time::time_t &at, const T &leftval, const T &rightval) { auto hint = this->container.last(at, this->last_element); // erase all one same-time values diff --git a/libopenage/curve/tests/curve_types.cpp b/libopenage/curve/tests/curve_types.cpp index b102d3828b..7a2d3289e6 100644 --- a/libopenage/curve/tests/curve_types.cpp +++ b/libopenage/curve/tests/curve_types.cpp @@ -1,6 +1,5 @@ // Copyright 2017-2023 the openage authors. See copying.md for legal info. -#include "time/time.h" #include "curve/continuous.h" #include "curve/discrete.h" #include "curve/discrete_mod.h" @@ -9,6 +8,7 @@ #include "event/event_loop.h" #include "log/log.h" #include "testing/testing.h" +#include "time/time.h" #include "util/compiler.h" @@ -33,7 +33,7 @@ void curve_types() { { auto it = c.begin(); TESTEQUALS(it->value, 0); - TESTEQUALS(it->time, std::numeric_limits::min()); + TESTEQUALS(it->time, std::numeric_limits::min()); TESTEQUALS((++it)->time, 0); TESTEQUALS(it->value, 0); TESTEQUALS((++it)->time, 1); @@ -136,7 +136,7 @@ void curve_types() { { auto it = c.begin(); - TESTEQUALS(it->time, std::numeric_limits::min()); + TESTEQUALS(it->time, std::numeric_limits::min()); TESTEQUALS(it->value, 0); TESTEQUALS((++it)->time, 0); diff --git a/libopenage/engine/engine.cpp b/libopenage/engine/engine.cpp index b28b0b58d4..24f1f4ae83 100644 --- a/libopenage/engine/engine.cpp +++ b/libopenage/engine/engine.cpp @@ -28,7 +28,7 @@ Engine::Engine(mode mode, cvar_manager->load_all(); // time loop - this->time_loop = std::make_shared(); + this->time_loop = std::make_shared(); this->simulation = std::make_shared(this->root_dir, this->cvar_manager, diff --git a/libopenage/engine/engine.h b/libopenage/engine/engine.h index be54ab5555..3766db7496 100644 --- a/libopenage/engine/engine.h +++ b/libopenage/engine/engine.h @@ -29,10 +29,6 @@ namespace cvar { class CVarManager; } // namespace cvar -namespace event { -class TimeLoop; -} // namespace event - namespace gamestate { class GameSimulation; } // namespace gamestate @@ -41,6 +37,10 @@ namespace presenter { class Presenter; } // namespace presenter +namespace time { +class TimeLoop; +} // namespace time + namespace engine { @@ -109,7 +109,7 @@ class Engine { /** * Controls and update the clock for time-based measurements. */ - std::shared_ptr time_loop; + std::shared_ptr time_loop; /** * Gameplay simulation. diff --git a/libopenage/event/demo/aicontroller.cpp b/libopenage/event/demo/aicontroller.cpp index dbfcda1a14..e65935f627 100644 --- a/libopenage/event/demo/aicontroller.cpp +++ b/libopenage/event/demo/aicontroller.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #include "aicontroller.h" @@ -6,7 +6,7 @@ namespace openage::event::demo { std::vector get_ai_inputs(const std::shared_ptr &player, const std::shared_ptr &ball, - const curve::time_t &now) { + const time::time_t &now) { std::vector ret; auto position = player->position->get(now); diff --git a/libopenage/event/demo/aicontroller.h b/libopenage/event/demo/aicontroller.h index 892cef3610..6200b1d99d 100644 --- a/libopenage/event/demo/aicontroller.h +++ b/libopenage/event/demo/aicontroller.h @@ -1,4 +1,4 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #pragma once @@ -10,6 +10,6 @@ namespace openage::event::demo { std::vector get_ai_inputs(const std::shared_ptr &player, const std::shared_ptr &ball, - const curve::time_t &now); + const time::time_t &now); } // openage::event::demo diff --git a/libopenage/event/demo/gamestate.h b/libopenage/event/demo/gamestate.h index 55974b577f..e133ad2a6e 100644 --- a/libopenage/event/demo/gamestate.h +++ b/libopenage/event/demo/gamestate.h @@ -95,7 +95,7 @@ class PongPlayer : public EventEntity { } private: - void child_changes(const curve::time_t &time) { + void child_changes(const time::time_t &time) { this->changes(time); } }; @@ -131,7 +131,7 @@ class PongBall : public EventEntity { } private: - void child_changes(const curve::time_t &time) { + void child_changes(const time::time_t &time) { this->changes(time); } size_t _id; diff --git a/libopenage/event/demo/gui.cpp b/libopenage/event/demo/gui.cpp index 3361419d49..beb1aac281 100644 --- a/libopenage/event/demo/gui.cpp +++ b/libopenage/event/demo/gui.cpp @@ -151,7 +151,7 @@ void Gui::clear() { void Gui::get_display_size(const std::shared_ptr &state, - const curve::time_t & /*now*/) { + const time::time_t & /*now*/) { // record the screen dimensions in the game state // TODO: make the display_boundary a curve as well. @@ -160,7 +160,7 @@ void Gui::get_display_size(const std::shared_ptr &state, } -void Gui::draw(const std::shared_ptr &state, const curve::time_t &now) { +void Gui::draw(const std::shared_ptr &state, const time::time_t &now) { attron(COLOR_PAIR(COLOR_DEBUG)); // print the score @@ -182,7 +182,7 @@ void Gui::draw(const std::shared_ptr &state, const curve::time_t &now // ball position predictions, 10s into the future for (int i = 0; i < 10; i++) { - auto i_as_ctt = curve::time_t::from_int(i); + auto i_as_ctt = time::time_t::from_int(i); mvprintw((5 + i), 1, "BALL in %03f: %f | %f; SPEED: %f | %f | PLpos: %f, PRpos: %f", i_as_ctt.to_double(), state->ball->position->get(now + i_as_ctt)[0], state->ball->position->get(now + i_as_ctt)[1], state->ball->speed->get(now + i_as_ctt)[0], state->ball->speed->get(now + i_as_ctt)[1], state->p1->position->get(now + i_as_ctt), state->p2->position->get(now + i_as_ctt)); } @@ -215,7 +215,7 @@ void Gui::draw(const std::shared_ptr &state, const curve::time_t &now // ball position prediction 10s into the future attron(COLOR_PAIR(COLOR_1)); for (int i = 1; i < 100; ++i) { - auto i_as_ctt = curve::time_t::from_double(i / 10.0); + auto i_as_ctt = time::time_t::from_double(i / 10.0); draw_ball(state->ball->position->get(now + i_as_ctt), "x"); } diff --git a/libopenage/event/demo/gui.h b/libopenage/event/demo/gui.h index 64660ad8b3..4f65d81382 100644 --- a/libopenage/event/demo/gui.h +++ b/libopenage/event/demo/gui.h @@ -1,4 +1,4 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once @@ -28,9 +28,9 @@ class Gui { const std::vector &get_inputs(const std::shared_ptr &player); void get_display_size(const std::shared_ptr &state, - const curve::time_t &now); + const time::time_t &now); - void draw(const std::shared_ptr &state, const curve::time_t &now); + void draw(const std::shared_ptr &state, const time::time_t &now); void draw_ball(util::Vector2d ball, const char *str); void log(const std::string &msg); diff --git a/libopenage/event/demo/main.cpp b/libopenage/event/demo/main.cpp index 2f36c1156d..fbadc1d81e 100644 --- a/libopenage/event/demo/main.cpp +++ b/libopenage/event/demo/main.cpp @@ -59,7 +59,7 @@ void curvepong(bool disable_gui, bool no_human) { while (running) { auto loop = std::make_shared(); - curve::time_t now = 1; + time::time_t now = 1; Physics phys; auto state = std::make_shared(loop, enable_gui @@ -163,7 +163,7 @@ void curvepong(bool disable_gui, bool no_human) { // microseconds per frame // 30fps = 1s/30 = 1000000us/30 per frame constexpr static std::chrono::microseconds per_frame = 33333us; - constexpr static curve::time_t per_frame_s = std::chrono::duration_cast(per_frame).count(); + constexpr static time::time_t per_frame_s = std::chrono::duration_cast(per_frame).count(); if (speed == timescale::NOSLEEP) { // increase the simulation loop time a bit diff --git a/libopenage/event/demo/physics.cpp b/libopenage/event/demo/physics.cpp index 6ed8b9b42b..b0509e85ea 100644 --- a/libopenage/event/demo/physics.cpp +++ b/libopenage/event/demo/physics.cpp @@ -46,7 +46,7 @@ class BallReflectWall : public DependencyEventHandler { void invoke(EventLoop &, const std::shared_ptr &target, const std::shared_ptr &gstate, - const curve::time_t &now, + const time::time_t &now, const EventHandler::param_map &/*param*/) override { auto positioncurve = std::dynamic_pointer_cast>(target); @@ -64,14 +64,14 @@ class BallReflectWall : public DependencyEventHandler { return; } - curve::time_t ty = 0; + time::time_t ty = 0; if (speed[1] > 0) { - ty = curve::time_t::from_double( + ty = time::time_t::from_double( (state->display_boundary[1] - pos[1]) / speed[1] ); } else if (speed[1] < 0) { - ty = curve::time_t::from_double( + ty = time::time_t::from_double( pos[1] / -speed[1] ); } @@ -79,9 +79,9 @@ class BallReflectWall : public DependencyEventHandler { state->ball->position->set_last(now + ty, pos + (speed * ty.to_double())); } - curve::time_t predict_invoke_time(const std::shared_ptr &target, + time::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &gstate, - const curve::time_t &now) override { + const time::time_t &now) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); @@ -90,15 +90,15 @@ class BallReflectWall : public DependencyEventHandler { auto pos = positioncurve->get(now); if (speed[1] == 0) { - return std::numeric_limits::max(); + return std::numeric_limits::max(); } - curve::time_t ty = 0; + time::time_t ty = 0; if (speed[1] > 0) { - ty = curve::time_t::from_double((state->display_boundary[1] - pos[1]) / speed[1]); + ty = time::time_t::from_double((state->display_boundary[1] - pos[1]) / speed[1]); } else if (speed[1] < 0) { - ty = curve::time_t::from_double(pos[1] / -speed[1]); + ty = time::time_t::from_double(pos[1] / -speed[1]); } #if WITH_NCURSES @@ -138,7 +138,7 @@ class BallReflectPanel : public DependencyEventHandler { void invoke(EventLoop &mgr, const std::shared_ptr &/*target*/, const std::shared_ptr &gstate, - const curve::time_t &now, + const time::time_t &now, const EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -193,12 +193,12 @@ class BallReflectPanel : public DependencyEventHandler { state->ball->position->set_last(now, pos); } - curve::time_t ty = 0; + time::time_t ty = 0; if (speed[0] > 0) { - ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / speed[0]); + ty = time::time_t::from_double((state->display_boundary[0] - pos[0]) / speed[0]); } else if (speed[0] < 0) { - ty = curve::time_t::from_double(pos[0] / -speed[0]); + ty = time::time_t::from_double(pos[0] / -speed[0]); } auto hit_pos = pos + speed * ty.to_double(); @@ -212,9 +212,9 @@ class BallReflectPanel : public DependencyEventHandler { state->ball->position->set_last(now + ty, hit_pos); } - curve::time_t predict_invoke_time(const std::shared_ptr &target, + time::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &gstate, - const curve::time_t &now) override { + const time::time_t &now) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); @@ -223,15 +223,15 @@ class BallReflectPanel : public DependencyEventHandler { auto pos = positioncurve->get(now); if (speed[0] == 0) - return std::numeric_limits::max(); + return std::numeric_limits::max(); - curve::time_t ty = 0; + time::time_t ty = 0; if (speed[0] > 0) { - ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / speed[0]); + ty = time::time_t::from_double((state->display_boundary[0] - pos[0]) / speed[0]); } else if (speed[0] < 0) { - ty = curve::time_t::from_double(pos[0] / -speed[0]); + ty = time::time_t::from_double(pos[0] / -speed[0]); } #if WITH_NCURSES @@ -275,7 +275,7 @@ class ResetGame : public OnceEventHandler { void invoke(EventLoop &/*mgr*/, const std::shared_ptr &/*target*/, const std::shared_ptr &gstate, - const curve::time_t &now, + const time::time_t &now, const EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -290,7 +290,7 @@ class ResetGame : public OnceEventHandler { } // move ball to the center - state->ball->position->set_last(now - curve::time_t::from_double(0.1), + state->ball->position->set_last(now - time::time_t::from_double(0.1), state->ball->position->get(now)); state->ball->position->set_last(now, state->display_boundary * 0.5); @@ -328,32 +328,32 @@ class ResetGame : public OnceEventHandler { } #endif - curve::time_t ty = 0; + time::time_t ty = 0; // calculate the wall-hit-times if (init_speed[1] > 0) { - ty = curve::time_t::from_double((state->display_boundary[1] - pos[1]) / init_speed[1]); + ty = time::time_t::from_double((state->display_boundary[1] - pos[1]) / init_speed[1]); } else if (init_speed[1] < 0) { - ty = curve::time_t::from_double(pos[1] / -init_speed[1]); + ty = time::time_t::from_double(pos[1] / -init_speed[1]); } else { // currently never happens, but this would be a non-vertically-moving ball // fallback to calculating panel-hit-times if (init_speed[0] > 0) { - ty = curve::time_t::from_double((state->display_boundary[0] - pos[0]) / init_speed[0]); + ty = time::time_t::from_double((state->display_boundary[0] - pos[0]) / init_speed[0]); } else { - ty = curve::time_t::from_double(pos[0] / -init_speed[0]); + ty = time::time_t::from_double(pos[0] / -init_speed[0]); } } state->ball->position->set_last(now + ty, pos + init_speed * ty.to_double()); } - curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + time::time_t predict_invoke_time(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, - const curve::time_t &old_time) override { + const time::time_t &old_time) override { return old_time; } }; @@ -361,7 +361,7 @@ class ResetGame : public OnceEventHandler { void Physics::init(const std::shared_ptr &gstate, const std::shared_ptr &loop, - const curve::time_t &now) { + const time::time_t &now) { auto state = std::dynamic_pointer_cast(gstate); @@ -384,10 +384,10 @@ void Physics::process_input(const std::shared_ptr &state, const std::shared_ptr &player, const std::vector &events, const std::shared_ptr &mgr, - const curve::time_t &now) { + const time::time_t &now) { // seconds into the future - constexpr static auto predicted_movement_time = curve::time_t::from_double(5.0); + constexpr static auto predicted_movement_time = time::time_t::from_double(5.0); // lines per second constexpr static double movement_speed = 8.0; @@ -432,7 +432,7 @@ void Physics::process_input(const std::shared_ptr &state, break; } - curve::time_t move_stop_guess = now + predicted_movement_time; + time::time_t move_stop_guess = now + predicted_movement_time; // change the position by integrating the speed curve. // TODO: add native integral to curves. @@ -488,7 +488,7 @@ void Physics::process_input(const std::shared_ptr &state, void Physics::reset(const std::shared_ptr &gstate, EventLoop &mgr, - const curve::time_t &now) { + const time::time_t &now) { auto state = std::dynamic_pointer_cast(gstate); mgr.create_event("demo.reset", state->ball->position, state, now); diff --git a/libopenage/event/demo/physics.h b/libopenage/event/demo/physics.h index ae440599be..de1b41bb2c 100644 --- a/libopenage/event/demo/physics.h +++ b/libopenage/event/demo/physics.h @@ -18,17 +18,17 @@ class Physics { public: static void init(const std::shared_ptr &, const std::shared_ptr &mgr, - const curve::time_t &); + const time::time_t &); void process_input(const std::shared_ptr &, const std::shared_ptr &, const std::vector &input, const std::shared_ptr &mgr, - const curve::time_t &now); + const time::time_t &now); static void reset(const std::shared_ptr &, EventLoop &mgr, - const curve::time_t &); + const time::time_t &); }; }} // openage::event::demo diff --git a/libopenage/event/event.cpp b/libopenage/event/event.cpp index 07cf8e6d22..cf02d77903 100644 --- a/libopenage/event/event.cpp +++ b/libopenage/event/event.cpp @@ -35,7 +35,7 @@ void Event::depend_on(const std::shared_ptr &dependency) { } -void Event::cancel(const curve::time_t reference_time) { +void Event::cancel(const time::time_t reference_time) { log::log(DBG << "Canceling event from EventHandler " << this->get_eventhandler()->id() << " for time t=" << this->get_time()); diff --git a/libopenage/event/event.h b/libopenage/event/event.h index a52b8ec909..73f2643104 100644 --- a/libopenage/event/event.h +++ b/libopenage/event/event.h @@ -34,17 +34,17 @@ class Event : public std::enable_shared_from_this { * Reschedule will call the predict_invoke_time method to initiate a reschedule * for the event it uses the reference_time as base for its calculation */ - void reschedule(const curve::time_t reference_time); + void reschedule(const time::time_t reference_time); size_t hash() const { return this->myhash; } - const curve::time_t &get_time() const { + const time::time_t &get_time() const { return this->time; } - void set_time(const curve::time_t &t) { + void set_time(const time::time_t &t) { this->time = t; } @@ -63,7 +63,7 @@ class Event : public std::enable_shared_from_this { /** * Cancel the event. */ - void cancel(const curve::time_t reference_time); + void cancel(const time::time_t reference_time); /** * For sorting events by their trigger time. @@ -78,14 +78,14 @@ class Event : public std::enable_shared_from_this { * When changes happen after `last_change_time` in the same time-reaching-round, * they can be ignored since the earlies point in time determines all implications. */ - void set_last_changed(const curve::time_t &t) { + void set_last_changed(const time::time_t &t) { this->last_change_time = t; } /** * Get the time the event was changed the last time. */ - const curve::time_t &get_last_changed() const { + const time::time_t &get_last_changed() const { return this->last_change_time; } @@ -105,10 +105,10 @@ class Event : public std::enable_shared_from_this { * Time this event occurs/occured. * It establishes the order of events in the EventQueue. */ - curve::time_t time; + time::time_t time; /** Time this event was registered to be changed last. */ - curve::time_t last_change_time = curve::time_t::min_value(); + time::time_t last_change_time = time::time_t::min_value(); /** Precalculated std::hash for the event */ size_t myhash; diff --git a/libopenage/event/event_loop.cpp b/libopenage/event/event_loop.cpp index d57e06804b..71e04429e7 100644 --- a/libopenage/event/event_loop.cpp +++ b/libopenage/event/event_loop.cpp @@ -23,7 +23,7 @@ void EventLoop::add_event_handler(const std::shared_ptr eventhandl std::shared_ptr EventLoop::create_event(const std::string name, const std::shared_ptr target, const std::shared_ptr state, - const curve::time_t reference_time, + const time::time_t reference_time, const EventHandler::param_map params) { std::unique_lock lock{this->mutex}; @@ -40,7 +40,7 @@ std::shared_ptr EventLoop::create_event(const std::string name, std::shared_ptr EventLoop::create_event(const std::shared_ptr eventhandler, const std::shared_ptr target, const std::shared_ptr state, - const curve::time_t reference_time, + const time::time_t reference_time, const EventHandler::param_map params) { std::unique_lock lock{this->mutex}; @@ -59,7 +59,7 @@ std::shared_ptr EventLoop::create_event(const std::shared_ptr &state) { std::unique_lock lock{this->mutex}; @@ -98,7 +98,7 @@ void EventLoop::reach_time(const curve::time_t &time_until, } -int EventLoop::execute_events(const curve::time_t &time_until, +int EventLoop::execute_events(const time::time_t &time_until, const std::shared_ptr &state) { log::log(SPAM << "Loop: Pending events in the queue (# = " << this->queue.get_event_queue().size() << "):"); @@ -139,10 +139,10 @@ int EventLoop::execute_events(const curve::time_t &time_until, // if the event is REPEAT, readd the event. if (event->get_eventhandler()->type == EventHandler::trigger_type::REPEAT) { - curve::time_t new_time = event->get_eventhandler()->predict_invoke_time( + time::time_t new_time = event->get_eventhandler()->predict_invoke_time( target, state, event->get_time()); - if (new_time != std::numeric_limits::min()) { + if (new_time != std::numeric_limits::min()) { event->set_time(new_time); log::log(DBG << "Loop: repeating event \"" << event->get_eventhandler()->id() @@ -166,7 +166,7 @@ int EventLoop::execute_events(const curve::time_t &time_until, void EventLoop::create_change(const std::shared_ptr evnt, - const curve::time_t changes_at) { + const time::time_t changes_at) { std::unique_lock lock{this->mutex}; this->queue.add_change(evnt, changes_at); @@ -192,10 +192,10 @@ void EventLoop::update_changes(const std::shared_ptr &state) { auto entity = evnt->get_entity().lock(); if (entity) { - curve::time_t new_time = evnt->get_eventhandler() + time::time_t new_time = evnt->get_eventhandler() ->predict_invoke_time(entity, state, change.time); - if (new_time != std::numeric_limits::min()) { + if (new_time != std::numeric_limits::min()) { log::log(DBG << "Loop: due to a change, rescheduling event of '" << evnt->get_eventhandler()->id() << "' on entity '" << entity->idstr() diff --git a/libopenage/event/event_loop.h b/libopenage/event/event_loop.h index 959f425f3c..96df27948a 100644 --- a/libopenage/event/event_loop.h +++ b/libopenage/event/event_loop.h @@ -63,7 +63,7 @@ class EventLoop { std::shared_ptr create_event(const std::string eventhandler, const std::shared_ptr target, const std::shared_ptr state, - const curve::time_t reference_time, + const time::time_t reference_time, const EventHandler::param_map params = EventHandler::param_map({})); /** @@ -85,7 +85,7 @@ class EventLoop { std::shared_ptr create_event(const std::shared_ptr eventhandler, const std::shared_ptr target, const std::shared_ptr state, - const curve::time_t reference_time, + const time::time_t reference_time, const EventHandler::param_map params = EventHandler::param_map({})); /** @@ -94,7 +94,7 @@ class EventLoop { * @param time_until Maximum time until which events are executed. * @param state Global state. */ - void reach_time(const curve::time_t &time_until, + void reach_time(const time::time_t &time_until, const std::shared_ptr &state); /** @@ -110,7 +110,7 @@ class EventLoop { * @param changes_at Time at which the event should be reevaluated. */ void create_change(const std::shared_ptr event, - const curve::time_t changes_at); + const time::time_t changes_at); /** * Get the event queue. @@ -130,7 +130,7 @@ class EventLoop { * * @returns number of events processed */ - int execute_events(const curve::time_t &time_until, + int execute_events(const time::time_t &time_until, const std::shared_ptr &state); /** diff --git a/libopenage/event/evententity.cpp b/libopenage/event/evententity.cpp index 28549cea37..9f0de69ef0 100644 --- a/libopenage/event/evententity.cpp +++ b/libopenage/event/evententity.cpp @@ -14,7 +14,7 @@ namespace openage::event { -void EventEntity::changes(const curve::time_t &time) { +void EventEntity::changes(const time::time_t &time) { // This target has some change, so we have to notify all dependents // that subscribed on this entity. @@ -46,7 +46,7 @@ void EventEntity::changes(const curve::time_t &time) { case EventHandler::trigger_type::ONCE: // If the dependent is a ONCE-event // forget the change if the once event has been notified already. - if (dependent->get_last_changed() > curve::time_t::min_value()) { + if (dependent->get_last_changed() > time::time_t::min_value()) { it = this->dependents.erase(it); } else { @@ -74,7 +74,7 @@ void EventEntity::changes(const curve::time_t &time) { } -void EventEntity::trigger(const curve::time_t &last_valid_time) { +void EventEntity::trigger(const time::time_t &last_valid_time) { // notify all dependent events that are triggered `on_keyframe` // that the this target changed. // the only events that is "notified" by are TRIGGER. diff --git a/libopenage/event/evententity.h b/libopenage/event/evententity.h index 8f971105d6..04e45f5d50 100644 --- a/libopenage/event/evententity.h +++ b/libopenage/event/evententity.h @@ -26,7 +26,7 @@ class EventEntity { /** Give a human-readable identifier for this target */ virtual std::string idstr() const = 0; - using single_change_notifier = std::function; + using single_change_notifier = std::function; protected: /** @@ -60,12 +60,12 @@ class EventEntity { * Call this whenever some data in the target changes. * This triggers the reevaluation of dependent events. */ - void changes(const curve::time_t &change_time); + void changes(const time::time_t &change_time); /** * Call this when depending TriggerEventHandleres should be invoked. */ - void trigger(const curve::time_t &invoke_time); + void trigger(const time::time_t &invoke_time); private: /** Event loop this target is registered to */ diff --git a/libopenage/event/eventhandler.h b/libopenage/event/eventhandler.h index 7c0ee820dc..5f022a025c 100644 --- a/libopenage/event/eventhandler.h +++ b/libopenage/event/eventhandler.h @@ -160,7 +160,7 @@ class EventHandler { virtual void invoke(EventLoop &loop, const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &time, + const time::time_t &time, const param_map ¶ms) = 0; /** @@ -179,9 +179,9 @@ class EventHandler { * then dependencies may not be resolved perfectly anymore * (if other events have already been calculated before that). */ - virtual curve::time_t predict_invoke_time(const std::shared_ptr &target, - const std::shared_ptr &state, - const curve::time_t &at) = 0; + virtual time::time_t predict_invoke_time(const std::shared_ptr &target, + const std::shared_ptr &state, + const time::time_t &at) = 0; private: /** diff --git a/libopenage/event/eventqueue.cpp b/libopenage/event/eventqueue.cpp index 67437b2b70..1d13997c6a 100644 --- a/libopenage/event/eventqueue.cpp +++ b/libopenage/event/eventqueue.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #include "eventqueue.h" @@ -17,7 +17,7 @@ namespace openage::event { std::shared_ptr EventQueue::create_event(const std::shared_ptr &trgt, const std::shared_ptr &cls, const std::shared_ptr &state, - const curve::time_t &reference_time, + const time::time_t &reference_time, const EventHandler::param_map ¶ms) { auto event = std::make_shared(trgt, cls, params); @@ -31,7 +31,7 @@ std::shared_ptr EventQueue::create_event(const std::shared_ptrset_time(event->get_eventhandler() ->predict_invoke_time(trgt, state, reference_time)); - if (event->get_time() == std::numeric_limits::min()) { + if (event->get_time() == std::numeric_limits::min()) { log::log(DBG << "Queue: ignoring insertion of event " << event->get_eventhandler()->id() << " because no execution was scheduled."); @@ -81,9 +81,9 @@ EventQueue::EventQueue() void EventQueue::add_change(const std::shared_ptr &event, - const curve::time_t &changed_at) { + const time::time_t &changed_at) { - const curve::time_t event_previous_changed = event->get_last_changed(); + const time::time_t event_previous_changed = event->get_last_changed(); // Has the event already been fired in this round? if (event_previous_changed < changed_at) { @@ -161,7 +161,7 @@ const EventStore &EventQueue::get_event_queue() const { } -std::shared_ptr EventQueue::take_event(const curve::time_t &max_time) { +std::shared_ptr EventQueue::take_event(const time::time_t &max_time) { if (this->event_queue.size() == 0) { return nullptr; } @@ -198,7 +198,7 @@ void EventQueue::swap_changesets() { EventQueue::Change::Change(const std::shared_ptr &evnt, - curve::time_t time) + time::time_t time) : time{std::move(time)}, evnt{evnt}, diff --git a/libopenage/event/eventqueue.h b/libopenage/event/eventqueue.h index 0fc91501d6..a6e47ff57a 100644 --- a/libopenage/event/eventqueue.h +++ b/libopenage/event/eventqueue.h @@ -25,9 +25,9 @@ class EventQueue final { class Change { public: Change(const std::shared_ptr &evnt, - curve::time_t time); + time::time_t time); - curve::time_t time; + time::time_t time; std::weak_ptr evnt; const size_t hash; @@ -67,7 +67,7 @@ class EventQueue final { std::shared_ptr create_event(const std::shared_ptr &evententity, const std::shared_ptr &eventhandler, const std::shared_ptr &state, - const curve::time_t &reference_time, + const time::time_t &reference_time, const EventHandler::param_map ¶ms); /** @@ -93,7 +93,7 @@ class EventQueue final { * An event target has changed, and the event shall be retriggered */ void add_change(const std::shared_ptr &event, - const curve::time_t &changed_at); + const time::time_t &changed_at); /** * Get an accessor to the running queue for state output purpose. @@ -103,7 +103,7 @@ class EventQueue final { /** * Obtain the next event from the `event_queue` that happens before `<= max_time`. */ - std::shared_ptr take_event(const curve::time_t &max_time); + std::shared_ptr take_event(const time::time_t &max_time); /** * Get the change_set to process changes. diff --git a/libopenage/event/tests.cpp b/libopenage/event/tests.cpp index 5333ad7a3f..33e7e61c5a 100644 --- a/libopenage/event/tests.cpp +++ b/libopenage/event/tests.cpp @@ -28,9 +28,9 @@ class TestState : public State { _id{id}, number(0) {} - void set_number(int number, const curve::time_t &time) { + void set_number(int number, const time::time_t &time) { this->number = number; - this->changes(time + curve::time_t::from_double(1)); + this->changes(time + time::time_t::from_double(1)); } [[nodiscard]] size_t id() const override { @@ -43,7 +43,7 @@ class TestState : public State { return ss.str(); } - void test_trigger(const curve::time_t &time) { + void test_trigger(const time::time_t &time) { this->trigger(time); } @@ -59,11 +59,11 @@ class TestState : public State { std::shared_ptr objectB; struct traceelement { - traceelement(std::string event, curve::time_t time) : + traceelement(std::string event, time::time_t time) : time{std::move(time)}, name{std::move(event)} {} - curve::time_t time; + time::time_t time; std::string name; }; @@ -105,7 +105,7 @@ class TestEventHandler : public EventHandler { void invoke(EventLoop & /*loop*/, const std::shared_ptr &target, const std::shared_ptr &gstate, - const curve::time_t &time, + const time::time_t &time, const EventHandler::param_map & /*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -126,10 +126,10 @@ class TestEventHandler : public EventHandler { } } - curve::time_t predict_invoke_time(const std::shared_ptr & /*target*/, + time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, const std::shared_ptr & /*state*/, - const curve::time_t &at) override { - return at + curve::time_t::from_double(2); + const time::time_t &at) override { + return at + time::time_t::from_double(2); } }; @@ -148,7 +148,7 @@ class TestEventHandlerTwo : public EventHandler { void invoke(EventLoop & /*loop*/, const std::shared_ptr >arget, const std::shared_ptr &gstate, - const curve::time_t &time, + const time::time_t &time, const EventHandler::param_map & /*param*/) override { auto state = std::dynamic_pointer_cast(gstate); auto target = std::dynamic_pointer_cast(gtarget); @@ -158,11 +158,11 @@ class TestEventHandlerTwo : public EventHandler { state->trace.emplace_back("B", time); } - curve::time_t predict_invoke_time(const std::shared_ptr & /*target*/, + time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, const std::shared_ptr & /*state*/, - const curve::time_t &at) override { + const time::time_t &at) override { // TODO recalculate a hit time - return at + curve::time_t::from_double(1); + return at + time::time_t::from_double(1); } }; @@ -184,7 +184,7 @@ class EventTypeTestClass : public EventHandler { void invoke(EventLoop & /*loop*/, const std::shared_ptr &target, const std::shared_ptr &gstate, - const curve::time_t &time, + const time::time_t &time, const EventHandler::param_map & /*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -195,13 +195,13 @@ class EventTypeTestClass : public EventHandler { state->trace.emplace_back(this->id(), time); } - curve::time_t predict_invoke_time(const std::shared_ptr & /*target*/, + time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, const std::shared_ptr & /*state*/, - const curve::time_t &at) override { + const time::time_t &at) override { switch (this->type) { case EventHandler::trigger_type::DEPENDENCY: // Execute 1 after the change (usually it is neccessary to recalculate a collision - return at + curve::time_t::from_double(1); + return at + time::time_t::from_double(1); case EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY: TESTFAILMSG("DEPENDENCY_IMMEDIATELY does not recalculate time!"); @@ -213,7 +213,7 @@ class EventTypeTestClass : public EventHandler { case EventHandler::trigger_type::REPEAT: // This will force the execution every 5ms - return at + curve::time_t::from_double(5); + return at + time::time_t::from_double(5); case EventHandler::trigger_type::ONCE: return 10; // even if data changed it will happen at the given time! @@ -261,7 +261,7 @@ void eventtrigger() { int i = 0; - curve::time_t last_time = 0; + time::time_t last_time = 0; for (const auto &e : state->trace) { if (last_time > e.time) { TESTFAILMSG("You broke the time continuum: one shall not execute randomly!"); @@ -336,7 +336,7 @@ void eventtrigger() { } int i = 0; - curve::time_t last_time = 0; + time::time_t last_time = 0; for (const auto &e : state->trace) { if (last_time > e.time) { @@ -539,7 +539,7 @@ void eventtrigger() { void invoke(EventLoop & /*loop*/, const std::shared_ptr & /*target*/, const std::shared_ptr & /*state*/, - const curve::time_t & /*time*/, + const time::time_t & /*time*/, const EventHandler::param_map ¶m) override { log::log(DBG << "Testing unknown parameter"); TESTEQUALS(param.contains("tomato"), false); @@ -566,9 +566,9 @@ void eventtrigger() { TESTEQUALS(param.get("testStdString"), "stdstring"); } - curve::time_t predict_invoke_time(const std::shared_ptr & /*target*/, + time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, const std::shared_ptr & /*state*/, - const curve::time_t &at) override { + const time::time_t &at) override { return at; } }; diff --git a/libopenage/gamestate/activity/event_node.h b/libopenage/gamestate/activity/event_node.h index 17c035df56..fedd987568 100644 --- a/libopenage/gamestate/activity/event_node.h +++ b/libopenage/gamestate/activity/event_node.h @@ -30,7 +30,7 @@ using event_store_t = std::vector>; * * @return List of events registered on the event loop. */ -using event_primer_func_t = std::function &, const std::shared_ptr &, const std::shared_ptr &)>; @@ -45,13 +45,13 @@ using event_primer_func_t = std::function &, const std::shared_ptr &, const std::shared_ptr &)>; -static const event_primer_func_t no_event = [](const curve::time_t &, +static const event_primer_func_t no_event = [](const time::time_t &, const std::shared_ptr &, const std::shared_ptr &, const std::shared_ptr &) { @@ -59,7 +59,7 @@ static const event_primer_func_t no_event = [](const curve::time_t &, return event_store_t{}; }; -static const event_next_func_t no_next = [](const curve::time_t &, +static const event_next_func_t no_next = [](const time::time_t &, const std::shared_ptr &, const std::shared_ptr &, const std::shared_ptr &) { diff --git a/libopenage/gamestate/activity/task_node.h b/libopenage/gamestate/activity/task_node.h index bb9a72ebd6..050770594c 100644 --- a/libopenage/gamestate/activity/task_node.h +++ b/libopenage/gamestate/activity/task_node.h @@ -13,10 +13,10 @@ class GameEntity; namespace activity { -using task_func_t = std::function &)>; -static const task_func_t no_task = [](const curve::time_t &, +static const task_func_t no_task = [](const time::time_t &, const std::shared_ptr &) { throw Error{ERR << "No task defined for this node."}; }; diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp index 61d7fe0659..e93c1d92dc 100644 --- a/libopenage/gamestate/activity/tests.cpp +++ b/libopenage/gamestate/activity/tests.cpp @@ -83,15 +83,15 @@ class TestActivityHandler : public event::OnceEventHandler { void invoke(event::EventLoop & /* loop */, const std::shared_ptr &target, const std::shared_ptr & /* state */, - const curve::time_t & /* time */, + const time::time_t & /* time */, const param_map & /* params */) override { auto mgr_target = std::dynamic_pointer_cast(target); mgr_target->run(); } - curve::time_t predict_invoke_time(const std::shared_ptr & /* target */, + time::time_t predict_invoke_time(const std::shared_ptr & /* target */, const std::shared_ptr & /* state */, - const curve::time_t &at) override { + const time::time_t &at) override { return at; } }; @@ -188,7 +188,7 @@ void activity_demo() { // task 1 task1->add_output(xor_node); - task1->set_task_func([](const curve::time_t & /* time */, + task1->set_task_func([](const time::time_t & /* time */, const std::shared_ptr & /* entity */) { log::log(INFO << "Running task 1"); }); @@ -197,7 +197,7 @@ void activity_demo() { size_t counter = 0; xor_node->add_output(task1); xor_node->add_output(event_node); - xor_node->set_condition_func([&](const curve::time_t & /* time */, + xor_node->set_condition_func([&](const time::time_t & /* time */, const std::shared_ptr & /* entity */) { log::log(INFO << "Checking condition (counter < 4): counter=" << counter); if (counter < 4) { @@ -214,7 +214,7 @@ void activity_demo() { // event node event_node->add_output(task2); - event_node->set_primer_func([&](const curve::time_t & /* time */, + event_node->set_primer_func([&](const time::time_t & /* time */, const std::shared_ptr & /* entity */, const std::shared_ptr & /* loop */, const std::shared_ptr & /* state */) { @@ -225,7 +225,7 @@ void activity_demo() { 0); return activity::event_store_t{ev}; }); - event_node->set_next_func([&task2](const curve::time_t & /* time */, + event_node->set_next_func([&task2](const time::time_t & /* time */, const std::shared_ptr & /* entity */, const std::shared_ptr & /* loop */, const std::shared_ptr & /* state */) { @@ -235,7 +235,7 @@ void activity_demo() { // task 2 task2->add_output(end); - task2->set_task_func([](const curve::time_t & /* time */, + task2->set_task_func([](const time::time_t & /* time */, const std::shared_ptr & /* entity */) { log::log(INFO << "Running task 2"); }); diff --git a/libopenage/gamestate/activity/xor_node.h b/libopenage/gamestate/activity/xor_node.h index 907bbf5aca..a4a7b6837c 100644 --- a/libopenage/gamestate/activity/xor_node.h +++ b/libopenage/gamestate/activity/xor_node.h @@ -10,10 +10,10 @@ class GameEntity; namespace activity { -using condition_func_t = std::function &)>; -static const condition_func_t no_condition = [](const curve::time_t &, +static const condition_func_t no_condition = [](const time::time_t &, const std::shared_ptr &) -> node_id { throw Error{MSG(err) << "No condition function set."}; }; diff --git a/libopenage/gamestate/component/api/live.cpp b/libopenage/gamestate/component/api/live.cpp index 068500e029..9a740b7c00 100644 --- a/libopenage/gamestate/component/api/live.cpp +++ b/libopenage/gamestate/component/api/live.cpp @@ -11,13 +11,13 @@ inline component_t Live::get_type() const { return component_t::LIVE; } -void Live::add_attribute(const curve::time_t &time, +void Live::add_attribute(const time::time_t &time, const nyan::fqon_t &attribute, std::shared_ptr> starting_values) { this->attribute_values.insert(time, attribute, starting_values); } -void Live::set_attribute(const curve::time_t &time, +void Live::set_attribute(const time::time_t &time, const nyan::fqon_t &attribute, int64_t value) { auto attribute_value = this->attribute_values.at(time, attribute); diff --git a/libopenage/gamestate/component/api/live.h b/libopenage/gamestate/component/api/live.h index 300df6e1b1..a132e4b045 100644 --- a/libopenage/gamestate/component/api/live.h +++ b/libopenage/gamestate/component/api/live.h @@ -22,7 +22,7 @@ class Live : public APIComponent { * @param attribute Attribute identifier (fqon of the nyan object). * @param starting_values Attribute values at the time of addition. */ - void add_attribute(const curve::time_t &time, + void add_attribute(const time::time_t &time, const nyan::fqon_t &attribute, std::shared_ptr> starting_values); @@ -33,7 +33,7 @@ class Live : public APIComponent { * @param attribute Attribute identifier (fqon of the nyan object). * @param value New attribute value. */ - void set_attribute(const curve::time_t &time, + void set_attribute(const time::time_t &time, const nyan::fqon_t &attribute, int64_t value); diff --git a/libopenage/gamestate/component/internal/activity.cpp b/libopenage/gamestate/component/internal/activity.cpp index 4d284a390d..1562d798a2 100644 --- a/libopenage/gamestate/component/internal/activity.cpp +++ b/libopenage/gamestate/component/internal/activity.cpp @@ -21,16 +21,16 @@ const std::shared_ptr &Activity::get_start_activity() const return this->start_activity; } -const std::shared_ptr Activity::get_node(const curve::time_t &time) const { +const std::shared_ptr Activity::get_node(const time::time_t &time) const { return this->node.get(time); } -void Activity::set_node(const curve::time_t &time, +void Activity::set_node(const time::time_t &time, const std::shared_ptr &node) { this->node.set_last(time, node); } -void Activity::init(const curve::time_t &time) { +void Activity::init(const time::time_t &time) { this->set_node(time, this->start_activity->get_start()); } @@ -38,7 +38,7 @@ void Activity::add_event(const std::shared_ptr &event) { this->scheduled_events.push_back(event); } -void Activity::cancel_events(const curve::time_t &time) { +void Activity::cancel_events(const time::time_t &time) { for (auto &event : this->scheduled_events) { event->cancel(time); } diff --git a/libopenage/gamestate/component/internal/activity.h b/libopenage/gamestate/component/internal/activity.h index e3289f5226..7cd3782e3a 100644 --- a/libopenage/gamestate/component/internal/activity.h +++ b/libopenage/gamestate/component/internal/activity.h @@ -50,7 +50,7 @@ class Activity : public InternalComponent { * @param time Time at which the node is requested. * @return Current node in the flow graph. */ - const std::shared_ptr get_node(const curve::time_t &time) const; + const std::shared_ptr get_node(const time::time_t &time) const; /** * Sets the current node in the activity flow graph at a given time. @@ -58,7 +58,7 @@ class Activity : public InternalComponent { * @param time Time at which the node is set. * @param node Current node in the flow graph. */ - void set_node(const curve::time_t &time, + void set_node(const time::time_t &time, const std::shared_ptr &node); /** @@ -66,7 +66,7 @@ class Activity : public InternalComponent { * * @param time Time at which the node is set. */ - void init(const curve::time_t &time); + void init(const time::time_t &time); /** * Add a scheduled event that is waited for to progress in the node graph. @@ -80,7 +80,7 @@ class Activity : public InternalComponent { * * @param time Time at which the events are cancelled. */ - void cancel_events(const curve::time_t &time); + void cancel_events(const time::time_t &time); private: /** diff --git a/libopenage/gamestate/component/internal/command_queue.cpp b/libopenage/gamestate/component/internal/command_queue.cpp index 5e522d6ea8..354b644ecf 100644 --- a/libopenage/gamestate/component/internal/command_queue.cpp +++ b/libopenage/gamestate/component/internal/command_queue.cpp @@ -15,7 +15,7 @@ inline component_t CommandQueue::get_type() const { return component_t::COMMANDQUEUE; } -void CommandQueue::add_command(const curve::time_t &time, +void CommandQueue::add_command(const time::time_t &time, const std::shared_ptr &command) { this->command_queue.insert(time, command); } @@ -24,7 +24,7 @@ const curve::Queue> &CommandQueue::get_queue() return this->command_queue; } -std::shared_ptr CommandQueue::pop_command(const curve::time_t &time) { +std::shared_ptr CommandQueue::pop_command(const time::time_t &time) { return this->command_queue.pop_front(time); } diff --git a/libopenage/gamestate/component/internal/command_queue.h b/libopenage/gamestate/component/internal/command_queue.h index 72594505de..7009781720 100644 --- a/libopenage/gamestate/component/internal/command_queue.h +++ b/libopenage/gamestate/component/internal/command_queue.h @@ -31,7 +31,7 @@ class CommandQueue : public InternalComponent { * @param time Time at which the command is added. * @param command New command. */ - void add_command(const curve::time_t &time, + void add_command(const time::time_t &time, const std::shared_ptr &command); /** @@ -48,7 +48,7 @@ class CommandQueue : public InternalComponent { * * @return Command in the front of the queue or nullptr if the queue is empty. */ - std::shared_ptr pop_command(const curve::time_t &time); + std::shared_ptr pop_command(const time::time_t &time); private: /** diff --git a/libopenage/gamestate/component/internal/ownership.cpp b/libopenage/gamestate/component/internal/ownership.cpp index 249f7c88a6..5fcfd3c007 100644 --- a/libopenage/gamestate/component/internal/ownership.cpp +++ b/libopenage/gamestate/component/internal/ownership.cpp @@ -9,7 +9,7 @@ namespace openage::gamestate::component { Ownership::Ownership(const std::shared_ptr &loop, const ownership_id_t owner_id, - const curve::time_t &creation_time) : + const time::time_t &creation_time) : owner(loop, 0) { this->owner.set_last(creation_time, owner_id); } @@ -22,7 +22,7 @@ inline component_t Ownership::get_type() const { return component_t::OWNERSHIP; } -void Ownership::set_owner(const curve::time_t &time, const ownership_id_t owner_id) { +void Ownership::set_owner(const time::time_t &time, const ownership_id_t owner_id) { this->owner.set_last(time, owner_id); } diff --git a/libopenage/gamestate/component/internal/ownership.h b/libopenage/gamestate/component/internal/ownership.h index 2e671a97ca..e244b69106 100644 --- a/libopenage/gamestate/component/internal/ownership.h +++ b/libopenage/gamestate/component/internal/ownership.h @@ -20,7 +20,7 @@ class Ownership : public InternalComponent { */ Ownership(const std::shared_ptr &loop, const ownership_id_t owner_id, - const curve::time_t &creation_time); + const time::time_t &creation_time); /** * Creates an Ownership component. @@ -37,7 +37,7 @@ class Ownership : public InternalComponent { * @param time Time at which the owner ID is set. * @param owner_id New owner ID. */ - void set_owner(const curve::time_t &time, const ownership_id_t owner_id); + void set_owner(const time::time_t &time, const ownership_id_t owner_id); /** * Get the owner IDs over time. diff --git a/libopenage/gamestate/component/internal/position.cpp b/libopenage/gamestate/component/internal/position.cpp index 78a58552c2..280880ed39 100644 --- a/libopenage/gamestate/component/internal/position.cpp +++ b/libopenage/gamestate/component/internal/position.cpp @@ -9,7 +9,7 @@ namespace openage::gamestate::component { Position::Position(const std::shared_ptr &loop, const coord::phys3 &initial_pos, - const curve::time_t &creation_time) : + const time::time_t &creation_time) : position(loop, 0, "", nullptr, WORLD_ORIGIN), angle(loop, 0) { this->position.set_insert(creation_time, initial_pos); @@ -49,7 +49,7 @@ const curve::Continuous &Position::get_positions() const { return this->position; } -void Position::set_position(const curve::time_t &time, const coord::phys3 &pos) { +void Position::set_position(const time::time_t &time, const coord::phys3 &pos) { this->position.set_last(time, pos); } @@ -57,7 +57,7 @@ const curve::Segmented &Position::get_angles() const { return this->angle; } -void Position::set_angle(const curve::time_t &time, const coord::phys_angle_t &angle) { +void Position::set_angle(const time::time_t &time, const coord::phys_angle_t &angle) { auto old_angle = this->angle.get(time); this->angle.set_insert_jump(time, old_angle, angle); } diff --git a/libopenage/gamestate/component/internal/position.h b/libopenage/gamestate/component/internal/position.h index 1fe4a0b6cc..a2c5e19a4b 100644 --- a/libopenage/gamestate/component/internal/position.h +++ b/libopenage/gamestate/component/internal/position.h @@ -23,7 +23,7 @@ class Position : public InternalComponent { */ Position(const std::shared_ptr &loop, const coord::phys3 &initial_pos, - const curve::time_t &creation_time); + const time::time_t &creation_time); /** * Create a Position component. @@ -49,7 +49,7 @@ class Position : public InternalComponent { * @param time Time at which the position is set. * @param pos New position. */ - void set_position(const curve::time_t &time, const coord::phys3 &pos); + void set_position(const time::time_t &time, const coord::phys3 &pos); /** * Get the directions in degrees over time. @@ -66,7 +66,7 @@ class Position : public InternalComponent { * @param time Time at which the angle is set. * @param angle New angle. */ - void set_angle(const curve::time_t &time, const coord::phys_angle_t &angle); + void set_angle(const time::time_t &time, const coord::phys_angle_t &angle); private: /** diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index d0d57e2529..5f93af4f5f 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -63,7 +63,7 @@ std::shared_ptr create_test_activity() { condition_moveable->add_output(wait_for_command); condition_moveable->add_output(end); - condition_moveable->set_condition_func([&](const curve::time_t & /* time */, + condition_moveable->set_condition_func([&](const time::time_t & /* time */, const std::shared_ptr &entity) { if (entity->has_component(component::component_t::MOVE)) { return 3; // wait_for_command->get_id(); @@ -73,7 +73,7 @@ std::shared_ptr create_test_activity() { }); wait_for_command->add_output(move); - wait_for_command->set_primer_func([](const curve::time_t & /* time */, + wait_for_command->set_primer_func([](const time::time_t & /* time */, const std::shared_ptr &entity, const std::shared_ptr &loop, const std::shared_ptr &state) { @@ -81,7 +81,7 @@ std::shared_ptr create_test_activity() { entity->get_manager(), state, // event is not executed until a command is available - std::numeric_limits::max()); + std::numeric_limits::max()); auto entity_queue = std::dynamic_pointer_cast( entity->get_component(component::component_t::COMMANDQUEUE)); auto &queue = const_cast> &>(entity_queue->get_queue()); @@ -89,7 +89,7 @@ std::shared_ptr create_test_activity() { return activity::event_store_t{ev}; }); - wait_for_command->set_next_func([](const curve::time_t &time, + wait_for_command->set_next_func([](const time::time_t &time, const std::shared_ptr &entity, const std::shared_ptr &, const std::shared_ptr &) { @@ -114,7 +114,7 @@ std::shared_ptr create_test_activity() { wait_for_move->add_output(idle); wait_for_move->add_output(condition_command); wait_for_move->add_output(end); - wait_for_move->set_primer_func([](const curve::time_t &time, + wait_for_move->set_primer_func([](const time::time_t &time, const std::shared_ptr &entity, const std::shared_ptr &loop, const std::shared_ptr &state) { @@ -125,7 +125,7 @@ std::shared_ptr create_test_activity() { return activity::event_store_t{ev}; }); - wait_for_move->set_next_func([&](const curve::time_t &, + wait_for_move->set_next_func([&](const time::time_t &, const std::shared_ptr &, const std::shared_ptr &, const std::shared_ptr &) { @@ -206,7 +206,7 @@ void EntityFactory::init_components(const std::shared_ptradd_attribute(std::numeric_limits::min(), + live->add_attribute(std::numeric_limits::min(), attribute.get_name(), std::make_shared>(loop, 0, diff --git a/libopenage/gamestate/event/process_command.cpp b/libopenage/gamestate/event/process_command.cpp index cbae35328f..4783fe89c8 100644 --- a/libopenage/gamestate/event/process_command.cpp +++ b/libopenage/gamestate/event/process_command.cpp @@ -18,15 +18,15 @@ void ProcessCommandHandler::setup_event(const std::shared_ptr &target, const std::shared_ptr & /* state */, - const curve::time_t &time, + const time::time_t &time, const param_map & /* params */) { auto mgr = std::dynamic_pointer_cast(target); mgr->run_activity_system(time); } -curve::time_t ProcessCommandHandler::predict_invoke_time(const std::shared_ptr & /* target */, +time::time_t ProcessCommandHandler::predict_invoke_time(const std::shared_ptr & /* target */, const std::shared_ptr & /* state */, - const curve::time_t &at) { + const time::time_t &at) { return at; } diff --git a/libopenage/gamestate/event/process_command.h b/libopenage/gamestate/event/process_command.h index 0e0914f1e5..94ed02ad9f 100644 --- a/libopenage/gamestate/event/process_command.h +++ b/libopenage/gamestate/event/process_command.h @@ -30,12 +30,12 @@ class ProcessCommandHandler : public openage::event::OnceEventHandler { void invoke(openage::event::EventLoop &loop, const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &time, + const time::time_t &time, const param_map ¶ms) override; - curve::time_t predict_invoke_time(const std::shared_ptr &target, + time::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &at) override; + const time::time_t &at) override; }; diff --git a/libopenage/gamestate/event/send_command.cpp b/libopenage/gamestate/event/send_command.cpp index 9325977504..5d4fb2977b 100644 --- a/libopenage/gamestate/event/send_command.cpp +++ b/libopenage/gamestate/event/send_command.cpp @@ -35,7 +35,7 @@ void SendCommandHandler::setup_event(const std::shared_ptr & /* target */, const std::shared_ptr &state, - const curve::time_t &time, + const time::time_t &time, const param_map ¶ms) { auto gstate = std::dynamic_pointer_cast(state); @@ -64,9 +64,9 @@ void SendCommandHandler::invoke(openage::event::EventLoop & /* loop */, } } -curve::time_t SendCommandHandler::predict_invoke_time(const std::shared_ptr & /* target */, +time::time_t SendCommandHandler::predict_invoke_time(const std::shared_ptr & /* target */, const std::shared_ptr & /* state */, - const curve::time_t &at) { + const time::time_t &at) { return at; } diff --git a/libopenage/gamestate/event/send_command.h b/libopenage/gamestate/event/send_command.h index cb49a91cfb..eafc95a8f7 100644 --- a/libopenage/gamestate/event/send_command.h +++ b/libopenage/gamestate/event/send_command.h @@ -40,12 +40,12 @@ class SendCommandHandler : public openage::event::OnceEventHandler { void invoke(openage::event::EventLoop &loop, const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &time, + const time::time_t &time, const param_map ¶ms) override; - curve::time_t predict_invoke_time(const std::shared_ptr &target, + time::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &at) override; + const time::time_t &at) override; }; } // namespace gamestate::event diff --git a/libopenage/gamestate/event/spawn_entity.cpp b/libopenage/gamestate/event/spawn_entity.cpp index c293e995e0..85848ec188 100644 --- a/libopenage/gamestate/event/spawn_entity.cpp +++ b/libopenage/gamestate/event/spawn_entity.cpp @@ -68,7 +68,7 @@ void SpawnEntityHandler::setup_event(const std::shared_ptr & /* target */, const std::shared_ptr &state, - const curve::time_t &time, + const time::time_t &time, const param_map ¶ms) { auto gstate = std::dynamic_pointer_cast(state); @@ -134,9 +134,9 @@ void SpawnEntityHandler::invoke(openage::event::EventLoop & /* loop */, gstate->add_game_entity(entity); } -curve::time_t SpawnEntityHandler::predict_invoke_time(const std::shared_ptr & /* target */, +time::time_t SpawnEntityHandler::predict_invoke_time(const std::shared_ptr & /* target */, const std::shared_ptr & /* state */, - const curve::time_t &at) { + const time::time_t &at) { return at; } diff --git a/libopenage/gamestate/event/spawn_entity.h b/libopenage/gamestate/event/spawn_entity.h index 617e2ace1b..6d12dd001c 100644 --- a/libopenage/gamestate/event/spawn_entity.h +++ b/libopenage/gamestate/event/spawn_entity.h @@ -62,7 +62,7 @@ class SpawnEntityHandler : public openage::event::OnceEventHandler { void invoke(openage::event::EventLoop &loop, const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &time, + const time::time_t &time, const param_map ¶ms) override; /** @@ -81,9 +81,9 @@ class SpawnEntityHandler : public openage::event::OnceEventHandler { * then dependencies may not be resolved perfectly anymore * (if other events have already been calculated before that). */ - curve::time_t predict_invoke_time(const std::shared_ptr &target, + time::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &at) override; + const time::time_t &at) override; private: /** diff --git a/libopenage/gamestate/event/wait.cpp b/libopenage/gamestate/event/wait.cpp index 8c8894644b..2d067086fa 100644 --- a/libopenage/gamestate/event/wait.cpp +++ b/libopenage/gamestate/event/wait.cpp @@ -18,15 +18,15 @@ void WaitHandler::setup_event(const std::shared_ptr & /* void WaitHandler::invoke(openage::event::EventLoop & /* loop */, const std::shared_ptr &target, const std::shared_ptr & /* state */, - const curve::time_t &time, + const time::time_t &time, const param_map & /* params */) { auto mgr = std::dynamic_pointer_cast(target); mgr->run_activity_system(time); } -curve::time_t WaitHandler::predict_invoke_time(const std::shared_ptr & /* target */, +time::time_t WaitHandler::predict_invoke_time(const std::shared_ptr & /* target */, const std::shared_ptr & /* state */, - const curve::time_t &at) { + const time::time_t &at) { return at; } diff --git a/libopenage/gamestate/event/wait.h b/libopenage/gamestate/event/wait.h index 221b73adaf..43686d6575 100644 --- a/libopenage/gamestate/event/wait.h +++ b/libopenage/gamestate/event/wait.h @@ -30,12 +30,12 @@ class WaitHandler : public openage::event::OnceEventHandler { void invoke(openage::event::EventLoop &loop, const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &time, + const time::time_t &time, const param_map ¶ms) override; - curve::time_t predict_invoke_time(const std::shared_ptr &target, + time::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &state, - const curve::time_t &at) override; + const time::time_t &at) override; }; } // namespace gamestate::event } // namespace openage diff --git a/libopenage/gamestate/game_entity.cpp b/libopenage/gamestate/game_entity.cpp index 3ece4f3644..e8b7a9e086 100644 --- a/libopenage/gamestate/game_entity.cpp +++ b/libopenage/gamestate/game_entity.cpp @@ -56,7 +56,7 @@ bool GameEntity::has_component(component::component_t type) { return this->components.contains(type); } -void GameEntity::render_update(const curve::time_t &time, +void GameEntity::render_update(const time::time_t &time, const std::string &animation_path) { if (this->render_entity != nullptr) { const auto &pos = dynamic_pointer_cast( diff --git a/libopenage/gamestate/game_entity.h b/libopenage/gamestate/game_entity.h index 09a9c1debb..d345d59d5d 100644 --- a/libopenage/gamestate/game_entity.h +++ b/libopenage/gamestate/game_entity.h @@ -104,7 +104,7 @@ class GameEntity { * @param time Simulation time of the update. * @param animation_path Animation path used at \p time. */ - void render_update(const curve::time_t &time, + void render_update(const time::time_t &time, const std::string &animation_path); protected: diff --git a/libopenage/gamestate/manager.cpp b/libopenage/gamestate/manager.cpp index 9952beccc5..e71027d97d 100644 --- a/libopenage/gamestate/manager.cpp +++ b/libopenage/gamestate/manager.cpp @@ -20,7 +20,7 @@ GameEntityManager::GameEntityManager(const std::shared_ptrgame_entity->get_id()); system::Activity::advance(this->game_entity, time, this->loop, this->state); } diff --git a/libopenage/gamestate/manager.h b/libopenage/gamestate/manager.h index c36d2958b4..723297a866 100644 --- a/libopenage/gamestate/manager.h +++ b/libopenage/gamestate/manager.h @@ -18,7 +18,7 @@ class GameEntityManager : public openage::event::EventEntity { const std::shared_ptr &game_entity); ~GameEntityManager() = default; - void run_activity_system(const curve::time_t &time); + void run_activity_system(const time::time_t &time); size_t id() const override; std::string idstr() const override; diff --git a/libopenage/gamestate/simulation.cpp b/libopenage/gamestate/simulation.cpp index bb32ac05fd..1c045548bc 100644 --- a/libopenage/gamestate/simulation.cpp +++ b/libopenage/gamestate/simulation.cpp @@ -20,7 +20,7 @@ namespace openage::gamestate { GameSimulation::GameSimulation(const util::Path &root_dir, const std::shared_ptr &cvar_manager, - const std::shared_ptr time_loop) : + const std::shared_ptr time_loop) : running{false}, root_dir{root_dir}, cvar_manager{cvar_manager}, diff --git a/libopenage/gamestate/simulation.h b/libopenage/gamestate/simulation.h index f2a922aa84..aab81c524e 100644 --- a/libopenage/gamestate/simulation.h +++ b/libopenage/gamestate/simulation.h @@ -18,13 +18,16 @@ class CVarManager; namespace event { class EventLoop; -class TimeLoop; } // namespace event namespace renderer { class RenderFactory; } +namespace time { +class TimeLoop; +} // namespace time + namespace gamestate { class EntityFactory; class Game; @@ -50,7 +53,7 @@ class GameSimulation final { */ GameSimulation(const util::Path &root_dir, const std::shared_ptr &cvar_manager, - const std::shared_ptr time_loop); + const std::shared_ptr time_loop); // game simulation should not be copied or moved GameSimulation(const GameSimulation ©) = delete; @@ -163,7 +166,7 @@ class GameSimulation final { /** * Time loop for getting the current simulation time and changing speed. */ - std::shared_ptr time_loop; + std::shared_ptr time_loop; /** * Event loop for processing events in the game. diff --git a/libopenage/gamestate/system/activity.cpp b/libopenage/gamestate/system/activity.cpp index cd6f0eccc3..57b225819d 100644 --- a/libopenage/gamestate/system/activity.cpp +++ b/libopenage/gamestate/system/activity.cpp @@ -21,7 +21,7 @@ namespace openage::gamestate::system { void Activity::advance(const std::shared_ptr &entity, - const curve::time_t &start_time, + const time::time_t &start_time, const std::shared_ptr &loop, const std::shared_ptr &state) { auto activity_component = std::dynamic_pointer_cast( @@ -46,7 +46,7 @@ void Activity::advance(const std::shared_ptr &entity, activity_component->cancel_events(start_time); } - curve::time_t event_wait_time = 0; + time::time_t event_wait_time = 0; auto stop = false; while (not stop) { switch (current_node->get_type()) { @@ -100,8 +100,8 @@ void Activity::advance(const std::shared_ptr &entity, activity_component->set_node(start_time, current_node); } -const curve::time_t Activity::handle_subsystem(const std::shared_ptr &entity, - const curve::time_t &start_time, +const time::time_t Activity::handle_subsystem(const std::shared_ptr &entity, + const time::time_t &start_time, system_id_t system_id) { switch (system_id) { case system_id_t::IDLE: @@ -117,7 +117,7 @@ const curve::time_t Activity::handle_subsystem(const std::shared_ptr(system_id)}; } - return curve::time_t::from_int(0); + return time::time_t::from_int(0); } diff --git a/libopenage/gamestate/system/activity.h b/libopenage/gamestate/system/activity.h index b097887140..d5283fceca 100644 --- a/libopenage/gamestate/system/activity.h +++ b/libopenage/gamestate/system/activity.h @@ -29,7 +29,7 @@ class Activity { * @param start_time Start time of change. */ static void advance(const std::shared_ptr &entity, - const curve::time_t &start_time, + const time::time_t &start_time, const std::shared_ptr &loop, const std::shared_ptr &state); @@ -43,8 +43,8 @@ class Activity { * * @return Runtime of the change in simulation time. */ - static const curve::time_t handle_subsystem(const std::shared_ptr &entity, - const curve::time_t &start_time, + static const time::time_t handle_subsystem(const std::shared_ptr &entity, + const time::time_t &start_time, system_id_t system_id); }; diff --git a/libopenage/gamestate/system/idle.cpp b/libopenage/gamestate/system/idle.cpp index a7bc126953..6c6d1137c0 100644 --- a/libopenage/gamestate/system/idle.cpp +++ b/libopenage/gamestate/system/idle.cpp @@ -13,8 +13,8 @@ namespace openage::gamestate::system { -const curve::time_t Idle::idle(const std::shared_ptr &entity, - const curve::time_t &start_time) { +const time::time_t Idle::idle(const std::shared_ptr &entity, + const time::time_t &start_time) { if (not entity->has_component(component::component_t::IDLE)) [[unlikely]] { throw Error{ERR << "Entity " << entity->get_id() << " has no idle component."}; } @@ -34,7 +34,7 @@ const curve::time_t Idle::idle(const std::shared_ptr &ent // TODO: play sound - return curve::time_t::from_int(0); + return time::time_t::from_int(0); } } // namespace openage::gamestate::system diff --git a/libopenage/gamestate/system/idle.h b/libopenage/gamestate/system/idle.h index 0f93cf33d7..8a9a6e6a9a 100644 --- a/libopenage/gamestate/system/idle.h +++ b/libopenage/gamestate/system/idle.h @@ -25,8 +25,8 @@ class Idle { * * @return Runtime of the change in simulation time. */ - static const curve::time_t idle(const std::shared_ptr &entity, - const curve::time_t &start_time); + static const time::time_t idle(const std::shared_ptr &entity, + const time::time_t &start_time); }; } // namespace system diff --git a/libopenage/gamestate/system/move.cpp b/libopenage/gamestate/system/move.cpp index 6fab22daf1..ede7619c8e 100644 --- a/libopenage/gamestate/system/move.cpp +++ b/libopenage/gamestate/system/move.cpp @@ -16,8 +16,8 @@ namespace openage::gamestate::system { -const curve::time_t Move::move_command(const std::shared_ptr &entity, - const curve::time_t &start_time) { +const time::time_t Move::move_command(const std::shared_ptr &entity, + const time::time_t &start_time) { auto command_queue = std::dynamic_pointer_cast( entity->get_component(component::component_t::COMMANDQUEUE)); auto command = std::dynamic_pointer_cast( @@ -25,19 +25,19 @@ const curve::time_t Move::move_command(const std::shared_ptrget_target(), start_time); } -const curve::time_t Move::move_default(const std::shared_ptr &entity, +const time::time_t Move::move_default(const std::shared_ptr &entity, const coord::phys3 &destination, - const curve::time_t &start_time) { + const time::time_t &start_time) { if (not entity->has_component(component::component_t::MOVE)) [[unlikely]] { log::log(WARN << "Entity " << entity->get_id() << " has no move component."); - return curve::time_t::from_int(0); + return time::time_t::from_int(0); } auto turn_component = std::dynamic_pointer_cast( diff --git a/libopenage/gamestate/system/move.h b/libopenage/gamestate/system/move.h index 4cd55c41da..11d6a6fa5f 100644 --- a/libopenage/gamestate/system/move.h +++ b/libopenage/gamestate/system/move.h @@ -23,8 +23,8 @@ class Move { * * @return Runtime of the change in simulation time. */ - static const curve::time_t move_command(const std::shared_ptr &entity, - const curve::time_t &start_time); + static const time::time_t move_command(const std::shared_ptr &entity, + const time::time_t &start_time); /** * Move a game entity to a destination. @@ -35,9 +35,9 @@ class Move { * * @return Runtime of the change in simulation time. */ - static const curve::time_t move_default(const std::shared_ptr &entity, + static const time::time_t move_default(const std::shared_ptr &entity, const coord::phys3 &destination, - const curve::time_t &start_time); + const time::time_t &start_time); }; } // namespace system diff --git a/libopenage/input/controller/game/controller.cpp b/libopenage/input/controller/game/controller.cpp index 41b4926117..582c0a045b 100644 --- a/libopenage/input/controller/game/controller.cpp +++ b/libopenage/input/controller/game/controller.cpp @@ -85,7 +85,7 @@ bool Controller::process(const event_arguments &ev_args, const std::shared_ptr &ctx, - const std::shared_ptr &time_loop, + const std::shared_ptr &time_loop, const std::shared_ptr &simulation, const std::shared_ptr &camera) { binding_func_t create_entity_event{[&](const event_arguments &args, diff --git a/libopenage/input/controller/game/controller.h b/libopenage/input/controller/game/controller.h index 9c3f50700f..2ef4439087 100644 --- a/libopenage/input/controller/game/controller.h +++ b/libopenage/input/controller/game/controller.h @@ -11,14 +11,14 @@ namespace openage { -namespace event { -class TimeLoop; -} - namespace gamestate { class GameSimulation; } +namespace time { +class TimeLoop; +} + namespace input::game { class BindingContext; @@ -116,7 +116,7 @@ class Controller { * @param camera Active game camera. */ void setup_defaults(const std::shared_ptr &ctx, - const std::shared_ptr &time_loop, + const std::shared_ptr &time_loop, const std::shared_ptr &simulation, const std::shared_ptr &camera); diff --git a/libopenage/main/tests/aicontroller.cpp b/libopenage/main/tests/aicontroller.cpp index 046669e478..63bd369267 100644 --- a/libopenage/main/tests/aicontroller.cpp +++ b/libopenage/main/tests/aicontroller.cpp @@ -1,4 +1,4 @@ -// Copyright 2019-2019 the openage authors. See copying.md for legal info. +// Copyright 2019-2023 the openage authors. See copying.md for legal info. #include "aicontroller.h" @@ -9,7 +9,7 @@ namespace openage::main::tests::pong { std::vector get_ai_inputs(const std::shared_ptr &player, const std::shared_ptr &ball, const std::shared_ptr> &area_size_curve, - const curve::time_t &now, + const time::time_t &now, bool right_player) { std::vector ret; @@ -22,7 +22,7 @@ std::vector get_ai_inputs(const std::shared_ptr &player, double area_width = area_size[0]; double area_height = area_size[1]; - curve::time_t hit_time; + time::time_t hit_time; util::Vector2d hit_pos; // calculate ball trajectory @@ -31,22 +31,22 @@ std::vector get_ai_inputs(const std::shared_ptr &player, // move panel to predicted hit position of panel wall while (true) { - curve::time_t ty_hit = 0, tx_hit = 0; + time::time_t ty_hit = 0, tx_hit = 0; if (speed[0] == 0) { - tx_hit = std::numeric_limits::max(); + tx_hit = std::numeric_limits::max(); } else if (speed[0] > 0) { - tx_hit = curve::time_t::from_double((area_width - ball_pos[0]) / speed[0]); + tx_hit = time::time_t::from_double((area_width - ball_pos[0]) / speed[0]); } else if (speed[0] < 0) { - tx_hit = curve::time_t::from_double(ball_pos[0] / -speed[0]); + tx_hit = time::time_t::from_double(ball_pos[0] / -speed[0]); } if (speed[1] == 0) { - ty_hit = std::numeric_limits::max(); + ty_hit = std::numeric_limits::max(); } else if (speed[1] > 0) { - ty_hit = curve::time_t::from_double((area_height - ball_pos[1]) / speed[1]); + ty_hit = time::time_t::from_double((area_height - ball_pos[1]) / speed[1]); } else if (speed[1] < 0) { - ty_hit = curve::time_t::from_double(ball_pos[1] / -speed[1]); + ty_hit = time::time_t::from_double(ball_pos[1] / -speed[1]); } // actual hit has lowest time: diff --git a/libopenage/main/tests/aicontroller.h b/libopenage/main/tests/aicontroller.h index ea8376fd2e..5066a75f3f 100644 --- a/libopenage/main/tests/aicontroller.h +++ b/libopenage/main/tests/aicontroller.h @@ -1,4 +1,4 @@ -// Copyright 2019-2019 the openage authors. See copying.md for legal info. +// Copyright 2019-2023 the openage authors. See copying.md for legal info. #pragma once @@ -11,7 +11,7 @@ std::vector get_ai_inputs( const std::shared_ptr &player, const std::shared_ptr &ball, const std::shared_ptr> &area_size, - const curve::time_t &now, + const time::time_t &now, bool right_player ); diff --git a/libopenage/main/tests/gamestate.cpp b/libopenage/main/tests/gamestate.cpp index cb5bdb647d..2c2257677b 100644 --- a/libopenage/main/tests/gamestate.cpp +++ b/libopenage/main/tests/gamestate.cpp @@ -54,7 +54,7 @@ std::string PongPlayer::idstr() const { } -void PongPlayer::child_changes(const curve::time_t &time) { +void PongPlayer::child_changes(const time::time_t &time) { this->changes(time); } @@ -87,7 +87,7 @@ std::string PongBall::idstr() const { } -void PongBall::child_changes(const curve::time_t &time) { +void PongBall::child_changes(const time::time_t &time) { this->changes(time); } diff --git a/libopenage/main/tests/gamestate.h b/libopenage/main/tests/gamestate.h index 4ff97e1947..f66fbf1614 100644 --- a/libopenage/main/tests/gamestate.h +++ b/libopenage/main/tests/gamestate.h @@ -57,7 +57,7 @@ class PongPlayer : public event::EventEntity { size_t _id; private: - void child_changes(const curve::time_t &time); + void child_changes(const time::time_t &time); }; @@ -72,7 +72,7 @@ class PongBall : public event::EventEntity { std::shared_ptr> position; private: - void child_changes(const curve::time_t &time); + void child_changes(const time::time_t &time); size_t _id; }; diff --git a/libopenage/main/tests/gui.cpp b/libopenage/main/tests/gui.cpp index 04f3728ce9..2ef0897e2a 100644 --- a/libopenage/main/tests/gui.cpp +++ b/libopenage/main/tests/gui.cpp @@ -188,7 +188,7 @@ void main() { } -void Gui::draw(const std::shared_ptr &state, const curve::time_t &now) { +void Gui::draw(const std::shared_ptr &state, const time::time_t &now) { constexpr float ball_size = 50.0f; constexpr float paddle_width = 20.0f; diff --git a/libopenage/main/tests/gui.h b/libopenage/main/tests/gui.h index 5cbd3d6772..b817d55a27 100644 --- a/libopenage/main/tests/gui.h +++ b/libopenage/main/tests/gui.h @@ -24,7 +24,7 @@ class Gui { const std::vector &get_inputs(const std::shared_ptr &player); - void draw(const std::shared_ptr &state, const curve::time_t &now); + void draw(const std::shared_ptr &state, const time::time_t &now); const util::Vector2s &get_window_size() const; diff --git a/libopenage/main/tests/physics.cpp b/libopenage/main/tests/physics.cpp index e737e40a51..c37f9ae122 100644 --- a/libopenage/main/tests/physics.cpp +++ b/libopenage/main/tests/physics.cpp @@ -32,7 +32,7 @@ class BallReflectWall : public event::DependencyEventHandler { void invoke(event::EventLoop &, const std::shared_ptr &target, const std::shared_ptr &gstate, - const curve::time_t &now, + const time::time_t &now, const event::EventHandler::param_map &/*param*/) override { auto positioncurve = std::dynamic_pointer_cast>(target); @@ -52,14 +52,14 @@ class BallReflectWall : public event::DependencyEventHandler { return; } - curve::time_t ty = 0; + time::time_t ty = 0; if (speed[1] > 0) { - ty = curve::time_t::from_double( + ty = time::time_t::from_double( (screen_size[1] - pos[1]) / speed[1] ); } else if (speed[1] < 0) { - ty = curve::time_t::from_double( + ty = time::time_t::from_double( pos[1] / -speed[1] ); } @@ -67,9 +67,9 @@ class BallReflectWall : public event::DependencyEventHandler { state->ball->position->set_last(now + ty, pos + (speed * ty.to_double())); } - curve::time_t predict_invoke_time(const std::shared_ptr &target, + time::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &gstate, - const curve::time_t &now) override { + const time::time_t &now) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); @@ -79,15 +79,15 @@ class BallReflectWall : public event::DependencyEventHandler { auto screen_size = state->area_size->get(now); if (speed[1] == 0) { - return std::numeric_limits::max(); + return std::numeric_limits::max(); } - curve::time_t ty = 0; + time::time_t ty = 0; if (speed[1] > 0) { - ty = curve::time_t::from_double((screen_size[1] - pos[1]) / speed[1]); + ty = time::time_t::from_double((screen_size[1] - pos[1]) / speed[1]); } else if (speed[1] < 0) { - ty = curve::time_t::from_double(pos[1] / -speed[1]); + ty = time::time_t::from_double(pos[1] / -speed[1]); } util::FString str; @@ -120,7 +120,7 @@ class BallReflectPanel : public event::DependencyEventHandler { void invoke(event::EventLoop &mgr, const std::shared_ptr &/*target*/, const std::shared_ptr &gstate, - const curve::time_t &now, + const time::time_t &now, const event::EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -167,12 +167,12 @@ class BallReflectPanel : public event::DependencyEventHandler { state->ball->position->set_last(now, pos); } - curve::time_t ty = 0; + time::time_t ty = 0; if (speed[0] > 0) { - ty = curve::time_t::from_double((screen_size[0] - pos[0]) / speed[0]); + ty = time::time_t::from_double((screen_size[0] - pos[0]) / speed[0]); } else if (speed[0] < 0) { - ty = curve::time_t::from_double(pos[0] / -speed[0]); + ty = time::time_t::from_double(pos[0] / -speed[0]); } auto hit_pos = pos + speed * ty.to_double(); @@ -200,9 +200,9 @@ class BallReflectPanel : public event::DependencyEventHandler { } } - curve::time_t predict_invoke_time(const std::shared_ptr &target, + time::time_t predict_invoke_time(const std::shared_ptr &target, const std::shared_ptr &gstate, - const curve::time_t &now) override { + const time::time_t &now) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); @@ -212,15 +212,15 @@ class BallReflectPanel : public event::DependencyEventHandler { auto screen_size = state->area_size->get(now); if (speed[0] == 0) - return std::numeric_limits::max(); + return std::numeric_limits::max(); - curve::time_t ty = 0; + time::time_t ty = 0; if (speed[0] > 0) { - ty = curve::time_t::from_double((screen_size[0] - pos[0]) / speed[0]); + ty = time::time_t::from_double((screen_size[0] - pos[0]) / speed[0]); } else if (speed[0] < 0) { - ty = curve::time_t::from_double(pos[0] / -speed[0]); + ty = time::time_t::from_double(pos[0] / -speed[0]); } { @@ -256,7 +256,7 @@ class ResetGame : public event::OnceEventHandler { void invoke(event::EventLoop &/*mgr*/, const std::shared_ptr &/*target*/, const std::shared_ptr &gstate, - const curve::time_t &now, + const time::time_t &now, const event::EventHandler::param_map &/*param*/) override { auto state = std::dynamic_pointer_cast(gstate); @@ -305,32 +305,32 @@ class ResetGame : public event::OnceEventHandler { state->gui->log(str); } - curve::time_t ty = 0; + time::time_t ty = 0; // calculate the wall-hit-times if (init_speed[1] > 0) { - ty = curve::time_t::from_double((screen_size[1] - pos[1]) / init_speed[1]); + ty = time::time_t::from_double((screen_size[1] - pos[1]) / init_speed[1]); } else if (init_speed[1] < 0) { - ty = curve::time_t::from_double(pos[1] / -init_speed[1]); + ty = time::time_t::from_double(pos[1] / -init_speed[1]); } else { // currently never happens, but this would be a non-vertically-moving ball // fallback to calculating panel-hit-times if (init_speed[0] > 0) { - ty = curve::time_t::from_double((screen_size[0] - pos[0]) / init_speed[0]); + ty = time::time_t::from_double((screen_size[0] - pos[0]) / init_speed[0]); } else { - ty = curve::time_t::from_double(pos[0] / -init_speed[0]); + ty = time::time_t::from_double(pos[0] / -init_speed[0]); } } state->ball->position->set_last(now + ty, pos + init_speed * ty.to_double()); } - curve::time_t predict_invoke_time(const std::shared_ptr &/*target*/, + time::time_t predict_invoke_time(const std::shared_ptr &/*target*/, const std::shared_ptr &/*state*/, - const curve::time_t &old_time) override { + const time::time_t &old_time) override { return old_time; } }; @@ -338,7 +338,7 @@ class ResetGame : public event::OnceEventHandler { void Physics::init(const std::shared_ptr &state, const std::shared_ptr &loop, - const curve::time_t &now) { + const time::time_t &now) { loop->add_event_handler(std::make_shared()); loop->add_event_handler(std::make_shared()); @@ -354,7 +354,7 @@ void Physics::init(const std::shared_ptr &state, void Physics::reset(const std::shared_ptr &gstate, event::EventLoop &mgr, - const curve::time_t &now) { + const time::time_t &now) { auto state = std::dynamic_pointer_cast(gstate); mgr.create_event("demo.reset", state->ball->position, state, now); @@ -365,10 +365,10 @@ void Physics::process_input(const std::shared_ptr &state, const std::shared_ptr &player, const std::vector &events, const std::shared_ptr &mgr, - const curve::time_t &now) { + const time::time_t &now) { // seconds into the future - constexpr static auto predicted_movement_time = curve::time_t::from_double(5.0); + constexpr static auto predicted_movement_time = time::time_t::from_double(5.0); // pixels per second for paddle movement constexpr static double movement_speed = 350.0; @@ -415,7 +415,7 @@ void Physics::process_input(const std::shared_ptr &state, break; } - curve::time_t move_stop_guess = now + predicted_movement_time; + time::time_t move_stop_guess = now + predicted_movement_time; // change the position by integrating the speed curve. // TODO: add native integral to curves. diff --git a/libopenage/main/tests/physics.h b/libopenage/main/tests/physics.h index ec65043c67..b29f643376 100644 --- a/libopenage/main/tests/physics.h +++ b/libopenage/main/tests/physics.h @@ -20,17 +20,17 @@ class Physics { public: static void init(const std::shared_ptr &, const std::shared_ptr &mgr, - const curve::time_t &); + const time::time_t &); void process_input(const std::shared_ptr &, const std::shared_ptr &, const std::vector &input, const std::shared_ptr &mgr, - const curve::time_t &now); + const time::time_t &now); static void reset(const std::shared_ptr &, event::EventLoop &mgr, - const curve::time_t &); + const time::time_t &); }; } // openage::main::tests::pong diff --git a/libopenage/main/tests/pong.cpp b/libopenage/main/tests/pong.cpp index 59c7941ce0..9cbc163fdb 100644 --- a/libopenage/main/tests/pong.cpp +++ b/libopenage/main/tests/pong.cpp @@ -62,7 +62,7 @@ void main(const util::Path &path) { log::log(INFO << "initializing new pong game..."); auto loop = std::make_shared(); - curve::time_t now = 0; + time::time_t now = 0; Physics phys; // the window size is fetched in here already, @@ -146,7 +146,7 @@ void main(const util::Path &path) { // microseconds per frame // 30fps = 1s/30 = 1000000us/30 per frame constexpr static std::chrono::microseconds per_frame = 33333us; - constexpr static curve::time_t per_frame_s = std::chrono::duration_cast(per_frame).count(); + constexpr static time::time_t per_frame_s = std::chrono::duration_cast(per_frame).count(); if (speed == timescale::NOSLEEP) { // increase the simulation loop time a bit diff --git a/libopenage/presenter/presenter.cpp b/libopenage/presenter/presenter.cpp index 97520a693f..b1b08a2731 100644 --- a/libopenage/presenter/presenter.cpp +++ b/libopenage/presenter/presenter.cpp @@ -36,7 +36,7 @@ namespace openage::presenter { Presenter::Presenter(const util::Path &root_dir, const std::shared_ptr &simulation, - const std::shared_ptr &time_loop) : + const std::shared_ptr &time_loop) : root_dir{root_dir}, render_passes{}, simulation{simulation}, @@ -80,7 +80,7 @@ void Presenter::set_simulation(const std::shared_ptr this->simulation->attach_renderer(render_factory); } -void Presenter::set_time_loop(const std::shared_ptr &time_loop) { +void Presenter::set_time_loop(const std::shared_ptr &time_loop) { this->time_loop = time_loop; } diff --git a/libopenage/presenter/presenter.h b/libopenage/presenter/presenter.h index 0e0a672586..70ff733d43 100644 --- a/libopenage/presenter/presenter.h +++ b/libopenage/presenter/presenter.h @@ -13,10 +13,6 @@ class GuiApplication; namespace openage { -namespace event { -class TimeLoop; -} - namespace gamestate { class GameSimulation; } @@ -25,6 +21,10 @@ namespace input { class InputManager; } +namespace time { +class TimeLoop; +} + namespace renderer { class RenderPass; class Renderer; @@ -76,7 +76,7 @@ class Presenter { */ Presenter(const util::Path &path, const std::shared_ptr &simulation = nullptr, - const std::shared_ptr &time_loop = nullptr); + const std::shared_ptr &time_loop = nullptr); ~Presenter() = default; @@ -97,7 +97,7 @@ class Presenter { * * @param time_loop Time loop. */ - void set_time_loop(const std::shared_ptr &time_loop); + void set_time_loop(const std::shared_ptr &time_loop); /** * Initialize the Qt application managing the graphical views. Required @@ -211,7 +211,7 @@ class Presenter { /** * Time loop. */ - std::shared_ptr time_loop; + std::shared_ptr time_loop; /** * Input manager. diff --git a/libopenage/renderer/demo/demo_3.cpp b/libopenage/renderer/demo/demo_3.cpp index 49ed021175..449fc45769 100644 --- a/libopenage/renderer/demo/demo_3.cpp +++ b/libopenage/renderer/demo/demo_3.cpp @@ -31,7 +31,7 @@ void renderer_demo_3(const util::Path &path) { // Clock required by world renderer for timing animation frames // (we never advance time in this demo though, so it has no significance) - auto clock = std::make_shared(); + auto clock = std::make_shared(); // Camera // our viewport into the game world diff --git a/libopenage/renderer/demo/demo_4.cpp b/libopenage/renderer/demo/demo_4.cpp index eaaac65a9e..95cc709ccb 100644 --- a/libopenage/renderer/demo/demo_4.cpp +++ b/libopenage/renderer/demo/demo_4.cpp @@ -24,7 +24,7 @@ void renderer_demo_4(const util::Path &path) { auto renderer = window.make_renderer(); /* Clock for timed display */ - auto clock = event::Clock(); + auto clock = time::Clock(); /* Controls whether animations are use "real" time for frame timings (if true) or the actual simulation time (if false). When simulation time is used, @@ -187,11 +187,11 @@ void renderer_demo_4(const util::Path &path) { switch (key) { case Qt::Key_Space: { - if (clock.get_state() == event::ClockState::RUNNING) { + if (clock.get_state() == time::ClockState::RUNNING) { clock.pause(); log::log(INFO << "Stopped simulation at " << clock.get_time() << " (real = " << clock.get_real_time() << ")"); } - else if (clock.get_state() == event::ClockState::PAUSED) { + else if (clock.get_state() == time::ClockState::PAUSED) { clock.resume(); log::log(INFO << "Resumed simulation at " << clock.get_time() << " (real = " << clock.get_real_time() << ")"); } diff --git a/libopenage/renderer/resources/animation/layer_info.cpp b/libopenage/renderer/resources/animation/layer_info.cpp index ed0b87c151..47c23fe990 100644 --- a/libopenage/renderer/resources/animation/layer_info.cpp +++ b/libopenage/renderer/resources/animation/layer_info.cpp @@ -23,8 +23,8 @@ LayerInfo::LayerInfo(std::vector> &angles, if (this->angles.size() > 0) { // set frame timings by calculating when they appear in the animation sequence auto frame_count = this->angles[0]->get_frame_count(); - curve::time_t t = 0; - std::vector keyframes; + time::time_t t = 0; + std::vector keyframes; for (size_t i = 0; i < frame_count; ++i) { keyframes.push_back(t); t += this->time_per_frame; diff --git a/libopenage/renderer/resources/frame_timing.cpp b/libopenage/renderer/resources/frame_timing.cpp index ec6abf6f1b..d734b49447 100644 --- a/libopenage/renderer/resources/frame_timing.cpp +++ b/libopenage/renderer/resources/frame_timing.cpp @@ -5,7 +5,7 @@ namespace openage::renderer::resources { -FrameTiming::FrameTiming(const curve::time_t &total_length, +FrameTiming::FrameTiming(const time::time_t &total_length, const std::vector &&keyframes) : keyframes{keyframes}, total_length{total_length} { @@ -14,11 +14,11 @@ FrameTiming::FrameTiming(const curve::time_t &total_length, } } -void FrameTiming::set_total_length(const curve::time_t &total_length) { +void FrameTiming::set_total_length(const time::time_t &total_length) { this->total_length = total_length; } -void FrameTiming::insert(const curve::time_t &time) { +void FrameTiming::insert(const time::time_t &time) { for (auto it = this->keyframes.begin(); it != this->keyframes.end(); ++it) { if (*it >= time) { this->keyframes.insert(it, time); @@ -27,18 +27,18 @@ void FrameTiming::insert(const curve::time_t &time) { } } -size_t FrameTiming::get_frame(const curve::time_t &time) const { +size_t FrameTiming::get_frame(const time::time_t &time) const { return search_frame(time); } -size_t FrameTiming::get_frame(const curve::time_t &time, const curve::time_t &start) const { - curve::time_t offset = time - start; +size_t FrameTiming::get_frame(const time::time_t &time, const time::time_t &start) const { + time::time_t offset = time - start; if (this->total_length == 0) { // modulo would fail here so return early return 0; } - curve::time_t mod = offset % this->total_length; + time::time_t mod = offset % this->total_length; return this->get_frame(mod); } @@ -46,7 +46,7 @@ size_t FrameTiming::size() const { return this->keyframes.size(); } -size_t FrameTiming::search_frame(const curve::time_t &time) const { +size_t FrameTiming::search_frame(const time::time_t &time) const { size_t left = 0; size_t right = this->keyframes.size(); size_t mid = 0; diff --git a/libopenage/renderer/resources/frame_timing.h b/libopenage/renderer/resources/frame_timing.h index b9742bccc4..c253501900 100644 --- a/libopenage/renderer/resources/frame_timing.h +++ b/libopenage/renderer/resources/frame_timing.h @@ -17,7 +17,7 @@ namespace openage::renderer::resources { */ class FrameTiming { public: - using keyframe_t = curve::time_t; + using keyframe_t = time::time_t; /** * Create a new frame timing sequence. @@ -25,7 +25,7 @@ class FrameTiming { * @param total_length Total length of the sequence (in seconds). * @param keyframes Time of each frame (in seconds). Expected to be sorted. */ - FrameTiming(const curve::time_t &total_length, + FrameTiming(const time::time_t &total_length, const std::vector &&keyframes); ~FrameTiming() = default; @@ -35,7 +35,7 @@ class FrameTiming { * * @param total_length Total length of the sequence (in seconds). */ - void set_total_length(const curve::time_t &total_length); + void set_total_length(const time::time_t &total_length); /** * Insert a new keyframe into the sequence. @@ -46,7 +46,7 @@ class FrameTiming { * * @param time Time of the new keyframe (in seconds). */ - void insert(const curve::time_t &time); + void insert(const time::time_t &time); /** * Get the index of the frame in the sequence that should be displayed at the @@ -55,7 +55,7 @@ class FrameTiming { * @param time Time in the sequence (in seconds). * @return Frame index in the sequence. */ - size_t get_frame(const curve::time_t &time) const; + size_t get_frame(const time::time_t &time) const; /** * Get the index of the frame in the sequence that should be displayed at a specified @@ -71,7 +71,7 @@ class FrameTiming { * @param start Start time of the sequence (in seconds). * @return Frame index in the sequence. */ - size_t get_frame(const curve::time_t ¤t, const curve::time_t &start) const; + size_t get_frame(const time::time_t ¤t, const time::time_t &start) const; /** * Get the number of frames in the sequence. @@ -90,7 +90,7 @@ class FrameTiming { * @param time Time in the sequence (in seconds). * @return Frame index in the sequence. */ - size_t search_frame(const curve::time_t &time) const; + size_t search_frame(const time::time_t &time) const; /** * Time of each frame in the sequence relative to the sequence start (in seconds). @@ -100,7 +100,7 @@ class FrameTiming { /** * Total length of the sequence (in seconds). */ - curve::time_t total_length; + time::time_t total_length; }; } // namespace openage::renderer::resources diff --git a/libopenage/renderer/resources/terrain/layer_info.cpp b/libopenage/renderer/resources/terrain/layer_info.cpp index aa13966b2c..4e4fb20e0e 100644 --- a/libopenage/renderer/resources/terrain/layer_info.cpp +++ b/libopenage/renderer/resources/terrain/layer_info.cpp @@ -20,8 +20,8 @@ TerrainLayerInfo::TerrainLayerInfo(const std::vectorframes.size(); - curve::time_t t = 0; - std::vector frame_timing; + time::time_t t = 0; + std::vector frame_timing; for (size_t i = 0; i < frame_count; ++i) { frame_timing.push_back(t); t += this->time_per_frame; diff --git a/libopenage/renderer/stages/terrain/terrain_mesh.cpp b/libopenage/renderer/stages/terrain/terrain_mesh.cpp index 3e00f126cc..364b12ac39 100644 --- a/libopenage/renderer/stages/terrain/terrain_mesh.cpp +++ b/libopenage/renderer/stages/terrain/terrain_mesh.cpp @@ -54,7 +54,7 @@ void TerrainRenderMesh::set_terrain_path(const curve::Discrete &ter })); } -void TerrainRenderMesh::update_uniforms(const curve::time_t &time) { +void TerrainRenderMesh::update_uniforms(const time::time_t &time) { // TODO: Only update uniforms that changed since last update if (this->uniforms == nullptr) [[unlikely]] { return; diff --git a/libopenage/renderer/stages/terrain/terrain_mesh.h b/libopenage/renderer/stages/terrain/terrain_mesh.h index 3e81d4d7eb..9ad66da2f0 100644 --- a/libopenage/renderer/stages/terrain/terrain_mesh.h +++ b/libopenage/renderer/stages/terrain/terrain_mesh.h @@ -94,7 +94,7 @@ class TerrainRenderMesh { * * @param time Current simulation time. */ - void update_uniforms(const curve::time_t &time = 0.0); + void update_uniforms(const time::time_t &time = 0.0); /** * Check whether a new renderable needs to be created for this mesh. diff --git a/libopenage/renderer/stages/terrain/terrain_model.cpp b/libopenage/renderer/stages/terrain/terrain_model.cpp index 24991eae4c..0dc7e0a641 100644 --- a/libopenage/renderer/stages/terrain/terrain_model.cpp +++ b/libopenage/renderer/stages/terrain/terrain_model.cpp @@ -43,7 +43,7 @@ void TerrainRenderModel::fetch_updates() { this->render_entity->clear_changed_flag(); } -void TerrainRenderModel::update_uniforms(const curve::time_t &time) { +void TerrainRenderModel::update_uniforms(const time::time_t &time) { for (auto &mesh : this->meshes) { mesh->update_uniforms(time); } diff --git a/libopenage/renderer/stages/terrain/terrain_model.h b/libopenage/renderer/stages/terrain/terrain_model.h index dcf4e123c3..0bdfa91532 100644 --- a/libopenage/renderer/stages/terrain/terrain_model.h +++ b/libopenage/renderer/stages/terrain/terrain_model.h @@ -55,7 +55,7 @@ class TerrainRenderModel { * * @param time Current simulation time. */ - void update_uniforms(const curve::time_t &time = 0.0); + void update_uniforms(const time::time_t &time = 0.0); /** * Get the meshes composing the terrain. diff --git a/libopenage/renderer/stages/terrain/terrain_render_entity.cpp b/libopenage/renderer/stages/terrain/terrain_render_entity.cpp index 3aae26644e..a4c915c2b1 100644 --- a/libopenage/renderer/stages/terrain/terrain_render_entity.cpp +++ b/libopenage/renderer/stages/terrain/terrain_render_entity.cpp @@ -18,7 +18,7 @@ TerrainRenderEntity::TerrainRenderEntity() : void TerrainRenderEntity::update(util::Vector2s size, std::vector height_map, const std::string terrain_path, - const curve::time_t time) { + const time::time_t time) { std::unique_lock lock{this->mutex}; // increase by 1 in every dimension because height_map diff --git a/libopenage/renderer/stages/terrain/terrain_render_entity.h b/libopenage/renderer/stages/terrain/terrain_render_entity.h index 3081fc711c..5def8eeef4 100644 --- a/libopenage/renderer/stages/terrain/terrain_render_entity.h +++ b/libopenage/renderer/stages/terrain/terrain_render_entity.h @@ -31,7 +31,7 @@ class TerrainRenderEntity { void update(util::Vector2s size, std::vector height_map, const std::string terrain_path, - const curve::time_t time = 0.0); + const time::time_t time = 0.0); /** * Get the vertices of the terrain. diff --git a/libopenage/renderer/stages/terrain/terrain_renderer.cpp b/libopenage/renderer/stages/terrain/terrain_renderer.cpp index cec392eb12..1b8b2e7a7c 100644 --- a/libopenage/renderer/stages/terrain/terrain_renderer.cpp +++ b/libopenage/renderer/stages/terrain/terrain_renderer.cpp @@ -21,7 +21,7 @@ TerrainRenderer::TerrainRenderer(const std::shared_ptr &window, const std::shared_ptr &camera, const util::Path &shaderdir, const std::shared_ptr &asset_manager, - const std::shared_ptr &clock) : + const std::shared_ptr &clock) : renderer{renderer}, camera{camera}, render_entity{nullptr}, diff --git a/libopenage/renderer/stages/terrain/terrain_renderer.h b/libopenage/renderer/stages/terrain/terrain_renderer.h index 5941cfece0..286567c34a 100644 --- a/libopenage/renderer/stages/terrain/terrain_renderer.h +++ b/libopenage/renderer/stages/terrain/terrain_renderer.h @@ -8,7 +8,8 @@ #include "util/path.h" namespace openage { -namespace event { + +namespace time { class Clock; } @@ -42,7 +43,7 @@ class TerrainRenderer { const std::shared_ptr &camera, const util::Path &shaderdir, const std::shared_ptr &asset_manager, - const std::shared_ptr &clock); + const std::shared_ptr &clock); ~TerrainRenderer() = default; /** @@ -121,7 +122,7 @@ class TerrainRenderer { /** * Simulation clock for timing animations. */ - std::shared_ptr clock; + std::shared_ptr clock; /** * Output texture. diff --git a/libopenage/renderer/stages/world/world_object.cpp b/libopenage/renderer/stages/world/world_object.cpp index 44c49fae0e..57b333d083 100644 --- a/libopenage/renderer/stages/world/world_object.cpp +++ b/libopenage/renderer/stages/world/world_object.cpp @@ -43,7 +43,7 @@ void WorldObject::set_camera(const std::shared_ptr &ca this->camera = camera; } -void WorldObject::fetch_updates(const curve::time_t &time) { +void WorldObject::fetch_updates(const time::time_t &time) { if (not this->render_entity->is_changed()) { // exit early because there is nothing to do return; @@ -72,7 +72,7 @@ void WorldObject::fetch_updates(const curve::time_t &time) { this->last_update = time; } -void WorldObject::update_uniforms(const curve::time_t &time) { +void WorldObject::update_uniforms(const time::time_t &time) { // TODO: Only update uniforms that changed since last update if (this->uniforms == nullptr) [[unlikely]] { return; diff --git a/libopenage/renderer/stages/world/world_object.h b/libopenage/renderer/stages/world/world_object.h index 4e2d3ef23f..dfa7ba20a6 100644 --- a/libopenage/renderer/stages/world/world_object.h +++ b/libopenage/renderer/stages/world/world_object.h @@ -52,14 +52,14 @@ class WorldObject { * * @param time Current simulation time. */ - void fetch_updates(const curve::time_t &time = 0.0); + void fetch_updates(const time::time_t &time = 0.0); /** * Update the uniforms of the renderable associated with this object. * * @param time Current simulation time. */ - void update_uniforms(const curve::time_t &time = 0.0); + void update_uniforms(const time::time_t &time = 0.0); /** * Get the ID of the corresponding game entity. @@ -168,7 +168,7 @@ class WorldObject { /** * Time of the last update call. */ - curve::time_t last_update; + time::time_t last_update; }; } // namespace world } // namespace openage::renderer diff --git a/libopenage/renderer/stages/world/world_render_entity.cpp b/libopenage/renderer/stages/world/world_render_entity.cpp index 3ddf7b8953..2375c127eb 100644 --- a/libopenage/renderer/stages/world/world_render_entity.cpp +++ b/libopenage/renderer/stages/world/world_render_entity.cpp @@ -19,7 +19,7 @@ void WorldRenderEntity::update(const uint32_t ref_id, const curve::Continuous &position, const curve::Segmented &angle, const std::string animation_path, - const curve::time_t time) { + const time::time_t time) { std::unique_lock lock{this->mutex}; this->ref_id = ref_id; @@ -40,7 +40,7 @@ void WorldRenderEntity::update(const uint32_t ref_id, void WorldRenderEntity::update(const uint32_t ref_id, const coord::phys3 position, const std::string animation_path, - const curve::time_t time) { + const time::time_t time) { std::unique_lock lock{this->mutex}; this->ref_id = ref_id; @@ -74,7 +74,7 @@ const curve::Discrete &WorldRenderEntity::get_animation_path() { return this->animation_path; } -curve::time_t WorldRenderEntity::get_update_time() { +time::time_t WorldRenderEntity::get_update_time() { std::shared_lock lock{this->mutex}; return this->last_update; diff --git a/libopenage/renderer/stages/world/world_render_entity.h b/libopenage/renderer/stages/world/world_render_entity.h index ba371e2a99..bd50e10b48 100644 --- a/libopenage/renderer/stages/world/world_render_entity.h +++ b/libopenage/renderer/stages/world/world_render_entity.h @@ -33,7 +33,7 @@ class WorldRenderEntity { const curve::Continuous &position, const curve::Segmented &angle, const std::string animation_path, - const curve::time_t time = 0.0); + const time::time_t time = 0.0); /** * Thus function is for DEBUGGING and should not be used. @@ -48,7 +48,7 @@ class WorldRenderEntity { void update(const uint32_t ref_id, const coord::phys3 position, const std::string animation_path, - const curve::time_t time = 0.0); + const time::time_t time = 0.0); /** * Get the ID of the corresponding game entity. @@ -83,7 +83,7 @@ class WorldRenderEntity { * * @return Time of last update. */ - curve::time_t get_update_time(); + time::time_t get_update_time(); /** * Check whether the render entity has received new updates from the @@ -129,7 +129,7 @@ class WorldRenderEntity { /** * Time of the last update call. */ - curve::time_t last_update; + time::time_t last_update; /** * Mutex for protecting threaded access. diff --git a/libopenage/renderer/stages/world/world_renderer.cpp b/libopenage/renderer/stages/world/world_renderer.cpp index 038a0aff10..5882ef83d8 100644 --- a/libopenage/renderer/stages/world/world_renderer.cpp +++ b/libopenage/renderer/stages/world/world_renderer.cpp @@ -21,7 +21,7 @@ WorldRenderer::WorldRenderer(const std::shared_ptr &window, const std::shared_ptr &camera, const util::Path &shaderdir, const std::shared_ptr &asset_manager, - const std::shared_ptr clock) : + const std::shared_ptr clock) : renderer{renderer}, camera{camera}, asset_manager{asset_manager}, diff --git a/libopenage/renderer/stages/world/world_renderer.h b/libopenage/renderer/stages/world/world_renderer.h index b20eaa3771..0dc7195ebe 100644 --- a/libopenage/renderer/stages/world/world_renderer.h +++ b/libopenage/renderer/stages/world/world_renderer.h @@ -10,7 +10,7 @@ namespace openage { -namespace event { +namespace time { class Clock; } @@ -44,7 +44,7 @@ class WorldRenderer { const std::shared_ptr &camera, const util::Path &shaderdir, const std::shared_ptr &asset_manager, - const std::shared_ptr clock); + const std::shared_ptr clock); ~WorldRenderer() = default; /** @@ -122,7 +122,7 @@ class WorldRenderer { /** * Simulation clock for timing animations. */ - std::shared_ptr clock; + std::shared_ptr clock; /** * Default geometry for every world object. diff --git a/libopenage/time/clock.cpp b/libopenage/time/clock.cpp index 1bf368e870..ca4bf7e7db 100644 --- a/libopenage/time/clock.cpp +++ b/libopenage/time/clock.cpp @@ -7,7 +7,7 @@ #include "log/log.h" -namespace openage::event { +namespace openage::time { Clock::Clock() : state{ClockState::INIT}, @@ -50,14 +50,14 @@ void Clock::update_time() { } } -curve::time_t Clock::get_time() { +time::time_t Clock::get_time() { std::shared_lock lock{this->mutex}; // convert time unit from milliseconds to seconds return this->sim_time / 1000; } -curve::time_t Clock::get_real_time() { +time::time_t Clock::get_real_time() { std::shared_lock lock{this->mutex}; // convert time unit from milliseconds to seconds @@ -126,4 +126,4 @@ void Clock::resume() { << this->sim_real_time << "ms (real)"); } -} // namespace openage::event +} // namespace openage::time diff --git a/libopenage/time/clock.h b/libopenage/time/clock.h index cde5f7a3fa..1021f599cd 100644 --- a/libopenage/time/clock.h +++ b/libopenage/time/clock.h @@ -8,7 +8,7 @@ #include "time/time.h" -namespace openage::event { +namespace openage::time { using simclock_t = std::chrono::steady_clock; using timepoint_t = std::chrono::time_point; @@ -55,7 +55,7 @@ class Clock { * * @return Time passed (in seconds). */ - curve::time_t get_time(); + time::time_t get_time(); /** * Get the current simulation time without speed adjustments. @@ -65,7 +65,7 @@ class Clock { * * @return Time passed (in seconds). */ - curve::time_t get_real_time(); + time::time_t get_real_time(); /** * Get the current speed of the clock. @@ -136,13 +136,13 @@ class Clock { /** * Stores how much time has passed inside the simulation (in milliseconds). */ - curve::time_t sim_time; + time::time_t sim_time; /** * Stores how much time has passed inside the simulation (in milliseconds) * _without_ speed adjustments (i.e. it acts as if speed = 1.0). */ - curve::time_t sim_real_time; + time::time_t sim_real_time; /** * Mutex for protecting threaded access. @@ -150,4 +150,4 @@ class Clock { std::shared_mutex mutex; }; -} // namespace openage::event +} // namespace openage::time diff --git a/libopenage/time/time.cpp b/libopenage/time/time.cpp index aa60c91af2..a9e1d4edb2 100644 --- a/libopenage/time/time.cpp +++ b/libopenage/time/time.cpp @@ -1,10 +1,10 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #include "time.h" -namespace openage::curve { +namespace openage::time { // This file is intended to be empty -} // namespace openage::curve +} // namespace openage::time diff --git a/libopenage/time/time.h b/libopenage/time/time.h index 0011394ec8..a6851f679c 100644 --- a/libopenage/time/time.h +++ b/libopenage/time/time.h @@ -1,4 +1,4 @@ -// Copyright 2017-2018 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #pragma once @@ -7,7 +7,7 @@ #include "util/fixed_point.h" -namespace openage::curve { +namespace openage::time { /** * Defines the type that is used as time index. @@ -15,4 +15,4 @@ namespace openage::curve { */ using time_t = util::FixedPoint; -} // namespace openage::curve +} // namespace openage::time diff --git a/libopenage/time/time_loop.cpp b/libopenage/time/time_loop.cpp index d6571db820..ffc7fd6efd 100644 --- a/libopenage/time/time_loop.cpp +++ b/libopenage/time/time_loop.cpp @@ -8,7 +8,7 @@ #include "time/clock.h" -namespace openage::event { +namespace openage::time { TimeLoop::TimeLoop() : running{false}, @@ -52,4 +52,4 @@ const std::shared_ptr TimeLoop::get_clock() { return this->clock; } -} // namespace openage::event +} // namespace openage::time diff --git a/libopenage/time/time_loop.h b/libopenage/time/time_loop.h index fb3b6cd063..e01d1a2ab1 100644 --- a/libopenage/time/time_loop.h +++ b/libopenage/time/time_loop.h @@ -6,9 +6,7 @@ #include -namespace openage { - -namespace event { +namespace openage::time { class Clock; /** @@ -69,5 +67,4 @@ class TimeLoop { }; -} // namespace event -} // namespace openage +} // namespace openage::time From dee4ab384905e37cb43849667f461b563165af02 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 26 Jul 2023 08:49:06 +0200 Subject: [PATCH 06/49] refactor: Include suggestions from iwyu. --- libopenage/assets/mod_manager.h | 3 + libopenage/curve/base_curve.h | 25 +++- libopenage/curve/continuous.h | 11 +- libopenage/curve/discrete.h | 5 +- libopenage/curve/discrete_mod.h | 9 ++ libopenage/curve/interpolated.h | 6 +- libopenage/curve/iterator.h | 2 + libopenage/curve/keyframe.h | 4 +- libopenage/curve/keyframe_container.h | 7 +- libopenage/curve/map.h | 2 + libopenage/curve/map_filter_iterator.h | 3 - libopenage/curve/queue.h | 15 ++- libopenage/curve/queue_filter_iterator.h | 3 - libopenage/curve/segmented.cpp | 6 +- libopenage/curve/segmented.h | 5 +- libopenage/curve/tests/container.cpp | 19 ++- libopenage/curve/tests/curve_types.cpp | 10 +- libopenage/datastructure/tests.cpp | 72 +++++++---- libopenage/datastructure/tests.h | 14 +-- libopenage/engine/engine.cpp | 1 + libopenage/engine/engine.h | 3 + libopenage/error/demo.cpp | 33 ++--- libopenage/error/error.cpp | 41 +++--- libopenage/error/error.h | 30 +++-- libopenage/error/gl_debug.cpp | 17 +-- libopenage/error/handlers.cpp | 53 +++++--- libopenage/error/stackanalyzer.cpp | 67 +++++----- libopenage/error/stackanalyzer.h | 13 +- libopenage/event/demo/gamestate.h | 15 ++- libopenage/event/demo/gui.cpp | 8 +- libopenage/event/demo/gui.h | 15 ++- libopenage/event/demo/main.cpp | 42 +++---- libopenage/event/demo/physics.cpp | 119 +++++++++--------- libopenage/event/demo/physics.h | 14 ++- libopenage/event/event.cpp | 16 ++- libopenage/event/event.h | 8 +- libopenage/event/event_loop.cpp | 21 +++- libopenage/event/event_loop.h | 9 +- libopenage/event/evententity.cpp | 12 +- libopenage/event/evententity.h | 3 +- libopenage/event/eventhandler.cpp | 24 ++-- libopenage/event/eventhandler.h | 7 +- libopenage/event/eventqueue.cpp | 60 ++++----- libopenage/event/eventqueue.h | 19 ++- libopenage/event/eventstore.cpp | 19 ++- libopenage/event/eventstore.h | 16 +-- libopenage/event/tests.cpp | 34 +++-- libopenage/gamestate/activity/activity.h | 5 +- libopenage/gamestate/activity/end_node.cpp | 3 + libopenage/gamestate/activity/end_node.h | 1 + libopenage/gamestate/activity/event_node.cpp | 2 + libopenage/gamestate/activity/event_node.h | 9 ++ libopenage/gamestate/activity/node.cpp | 5 + libopenage/gamestate/activity/node.h | 4 +- libopenage/gamestate/activity/start_node.cpp | 3 + libopenage/gamestate/activity/start_node.h | 1 + libopenage/gamestate/activity/task_node.cpp | 3 + libopenage/gamestate/activity/task_node.h | 5 + .../gamestate/activity/task_system_node.cpp | 3 + .../gamestate/activity/task_system_node.h | 1 + libopenage/gamestate/activity/tests.cpp | 17 ++- libopenage/gamestate/activity/xor_node.cpp | 2 + libopenage/gamestate/activity/xor_node.h | 9 ++ libopenage/gamestate/api/ability.cpp | 9 ++ libopenage/gamestate/api/animation.cpp | 3 + libopenage/gamestate/api/animation.h | 3 + libopenage/gamestate/api/definitions.h | 4 + libopenage/gamestate/api/patch.cpp | 8 ++ libopenage/gamestate/api/player_setup.cpp | 7 ++ libopenage/gamestate/api/player_setup.h | 2 + libopenage/gamestate/api/property.cpp | 7 ++ libopenage/gamestate/api/property.h | 2 + libopenage/gamestate/api/sound.cpp | 4 + libopenage/gamestate/api/sound.h | 3 + libopenage/gamestate/api/util.cpp | 8 +- libopenage/gamestate/api/util.h | 2 + libopenage/gamestate/component/api/idle.h | 1 + libopenage/gamestate/component/api/live.cpp | 5 + libopenage/gamestate/component/api/live.h | 7 +- libopenage/gamestate/component/api/move.h | 2 + libopenage/gamestate/component/api/turn.h | 2 + .../gamestate/component/api_component.cpp | 3 +- .../gamestate/component/api_component.h | 6 +- .../gamestate/component/internal/activity.cpp | 2 + .../gamestate/component/internal/activity.h | 3 + .../component/internal/command_queue.cpp | 2 + .../component/internal/command_queue.h | 5 + .../component/internal/commands/custom.h | 1 + .../component/internal/commands/idle.h | 1 + .../component/internal/commands/move.h | 1 + .../gamestate/component/internal/ownership.h | 16 ++- .../gamestate/component/internal/position.cpp | 1 + .../gamestate/component/internal/position.h | 15 ++- libopenage/gamestate/entity_factory.cpp | 18 ++- libopenage/gamestate/entity_factory.h | 8 +- libopenage/gamestate/event/process_command.h | 9 +- libopenage/gamestate/event/send_command.cpp | 25 +++- libopenage/gamestate/event/send_command.h | 10 +- libopenage/gamestate/event/spawn_entity.cpp | 12 +- libopenage/gamestate/event/spawn_entity.h | 11 +- libopenage/gamestate/event/wait.h | 9 +- libopenage/gamestate/game.cpp | 12 +- libopenage/gamestate/game.h | 7 +- libopenage/gamestate/game_entity.h | 5 +- libopenage/gamestate/game_state.cpp | 6 +- libopenage/gamestate/game_state.h | 6 + libopenage/gamestate/manager.cpp | 2 +- libopenage/gamestate/manager.h | 13 +- libopenage/gamestate/system/activity.cpp | 17 ++- libopenage/gamestate/system/idle.cpp | 8 +- libopenage/gamestate/system/move.cpp | 18 ++- libopenage/gamestate/terrain.cpp | 4 + libopenage/gamestate/terrain.h | 2 +- libopenage/gamestate/world.cpp | 5 +- libopenage/gamestate/world.h | 4 +- .../gui/integration/private/gui_log.cpp | 12 +- .../input/controller/game/controller.cpp | 1 + libopenage/log/file_logsink.cpp | 13 +- libopenage/log/file_logsink.h | 8 +- libopenage/log/level.h | 38 +++--- libopenage/log/log.cpp | 11 +- libopenage/log/log.h | 11 +- libopenage/log/logsink.h | 14 ++- libopenage/log/logsource.cpp | 17 +-- libopenage/log/logsource.h | 11 +- libopenage/log/message.cpp | 36 +++--- libopenage/log/message.h | 35 +++--- libopenage/log/named_logsource.h | 7 +- libopenage/log/stdout_logsink.cpp | 25 ++-- libopenage/log/stdout_logsink.h | 9 +- libopenage/log/test.cpp | 20 +-- libopenage/main.cpp | 3 +- libopenage/main.h | 3 + libopenage/renderer/animation.h | 2 - libopenage/renderer/camera/camera.cpp | 6 + libopenage/renderer/camera/camera.h | 4 + libopenage/renderer/demo/tests.cpp | 3 + libopenage/renderer/demo/tests.h | 11 +- .../gui/integration/private/gui_log.cpp | 9 +- libopenage/renderer/opengl/debug.cpp | 1 + libopenage/renderer/render_factory.h | 2 - libopenage/renderer/renderer.h | 2 + .../resources/animation/animation_info.h | 13 +- .../resources/animation/layer_info.cpp | 3 + .../resources/assets/asset_manager.cpp | 5 +- .../renderer/resources/assets/asset_manager.h | 3 +- .../renderer/resources/assets/cache.cpp | 19 ++- libopenage/renderer/resources/assets/cache.h | 13 +- .../resources/assets/texture_manager.cpp | 2 +- .../resources/assets/texture_manager.h | 2 + libopenage/renderer/resources/buffer_info.h | 4 + .../renderer/resources/frame_timing.cpp | 8 ++ libopenage/renderer/resources/mesh_data.cpp | 111 +++++----------- libopenage/renderer/resources/mesh_data.h | 31 ++--- libopenage/renderer/resources/palette_info.h | 3 + .../renderer/resources/parser/common.cpp | 3 + .../resources/parser/parse_blendmask.cpp | 15 ++- .../resources/parser/parse_blendmask.h | 14 ++- .../resources/parser/parse_blendtable.cpp | 9 ++ .../resources/parser/parse_blendtable.h | 15 ++- .../resources/parser/parse_palette.cpp | 12 ++ .../renderer/resources/parser/parse_palette.h | 11 +- .../resources/parser/parse_sprite.cpp | 13 +- .../renderer/resources/parser/parse_sprite.h | 20 ++- .../resources/parser/parse_terrain.cpp | 15 ++- .../renderer/resources/parser/parse_terrain.h | 16 ++- .../resources/parser/parse_texture.cpp | 16 ++- .../renderer/resources/parser/parse_texture.h | 17 +-- .../renderer/resources/shader_source.cpp | 25 ++-- libopenage/renderer/resources/shader_source.h | 13 +- .../resources/terrain/blendpattern_info.h | 4 +- .../renderer/resources/terrain/layer_info.cpp | 4 + .../renderer/resources/terrain/layer_info.h | 2 - .../resources/terrain/terrain_info.cpp | 7 ++ .../renderer/resources/terrain/terrain_info.h | 7 +- .../renderer/resources/texture_data.cpp | 13 +- libopenage/renderer/resources/texture_data.h | 16 ++- .../renderer/resources/texture_info.cpp | 5 + libopenage/renderer/resources/texture_info.h | 5 +- libopenage/renderer/shader_program.h | 3 +- libopenage/renderer/stages/camera/manager.h | 2 - .../renderer/stages/terrain/terrain_mesh.cpp | 5 + .../renderer/stages/terrain/terrain_mesh.h | 9 +- .../renderer/stages/terrain/terrain_model.cpp | 16 ++- .../renderer/stages/terrain/terrain_model.h | 4 +- .../stages/terrain/terrain_render_entity.cpp | 7 +- .../stages/terrain/terrain_render_entity.h | 3 + .../renderer/stages/world/world_object.cpp | 12 +- .../renderer/stages/world/world_object.h | 8 +- .../stages/world/world_render_entity.cpp | 4 + .../stages/world/world_render_entity.h | 10 +- libopenage/renderer/texture_array.h | 16 ++- libopenage/renderer/uniform_buffer.h | 4 +- libopenage/renderer/uniform_input.h | 7 +- libopenage/renderer/util.cpp | 15 +-- libopenage/renderer/window_event_handler.cpp | 4 +- 196 files changed, 1477 insertions(+), 819 deletions(-) diff --git a/libopenage/assets/mod_manager.h b/libopenage/assets/mod_manager.h index 6d7ec5277b..b049603f83 100644 --- a/libopenage/assets/mod_manager.h +++ b/libopenage/assets/mod_manager.h @@ -5,8 +5,11 @@ #include #include #include +#include +#include "error/error.h" #include "log/log.h" +#include "log/message.h" #include "assets/modpack.h" #include "util/path.h" diff --git a/libopenage/curve/base_curve.h b/libopenage/curve/base_curve.h index c5132e3f80..77be819139 100644 --- a/libopenage/curve/base_curve.h +++ b/libopenage/curve/base_curve.h @@ -2,14 +2,30 @@ #pragma once +#include +#include +#include +#include #include +#include +#include + +#include "error/error.h" +#include "log/log.h" +#include "log/message.h" #include "curve/keyframe_container.h" -#include "event/event_loop.h" #include "event/evententity.h" +#include "time/time.h" +#include "util/fixed_point.h" + +namespace openage { +namespace event { +class EventLoop; +} -namespace openage::curve { +namespace curve { template class BaseCurve : public event::EventEntity { @@ -257,7 +273,7 @@ void BaseCurve::check_integrity() const { time::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()}; + throw Error{MSG(err) << "curve is broken after t=" << last_time << ": " << this->str()}; } last_time = keyframe.time; } @@ -298,4 +314,5 @@ void BaseCurve::sync(const BaseCurve &other, this->changes(start); } -} // namespace openage::curve +} // namespace curve +} // namespace openage diff --git a/libopenage/curve/continuous.h b/libopenage/curve/continuous.h index 72825f16b5..3eb7170ddb 100644 --- a/libopenage/curve/continuous.h +++ b/libopenage/curve/continuous.h @@ -2,11 +2,12 @@ #pragma once -#include #include +#include + +#include "curve/interpolated.h" +#include "time/time.h" -#include "interpolated.h" -#include "../log/log.h" namespace openage::curve { @@ -21,7 +22,7 @@ namespace openage::curve { * The bound template type T has to implement `operator+(T)` and * `operator*(time::time_t)`. */ -template +template class Continuous : public Interpolated { public: using Interpolated::Interpolated; @@ -81,4 +82,4 @@ std::string Continuous::idstr() const { } -} // openage::curve +} // namespace openage::curve diff --git a/libopenage/curve/discrete.h b/libopenage/curve/discrete.h index e1e9be6302..cdcb3daa31 100644 --- a/libopenage/curve/discrete.h +++ b/libopenage/curve/discrete.h @@ -4,9 +4,12 @@ #include #include +#include +#include #include -#include "base_curve.h" +#include "curve/base_curve.h" +#include "time/time.h" namespace openage::curve { diff --git a/libopenage/curve/discrete_mod.h b/libopenage/curve/discrete_mod.h index ddb0ebb95b..a641541688 100644 --- a/libopenage/curve/discrete_mod.h +++ b/libopenage/curve/discrete_mod.h @@ -2,7 +2,16 @@ #pragma once +#include +#include +#include +#include +#include + +#include "curve/base_curve.h" #include "curve/discrete.h" +#include "time/time.h" +#include "util/fixed_point.h" namespace openage::curve { diff --git a/libopenage/curve/interpolated.h b/libopenage/curve/interpolated.h index 7942a97c54..49a189a124 100644 --- a/libopenage/curve/interpolated.h +++ b/libopenage/curve/interpolated.h @@ -2,10 +2,10 @@ #pragma once -#include +#include "curve/base_curve.h" +#include "time/time.h" +#include "util/fixed_point.h" -#include "../log/log.h" -#include "base_curve.h" namespace openage::curve { diff --git a/libopenage/curve/iterator.h b/libopenage/curve/iterator.h index c2374e7267..2500e083cd 100644 --- a/libopenage/curve/iterator.h +++ b/libopenage/curve/iterator.h @@ -3,6 +3,8 @@ #pragma once #include "time/time.h" +#include "util/fixed_point.h" + namespace openage::curve { diff --git a/libopenage/curve/keyframe.h b/libopenage/curve/keyframe.h index cc1553f042..d6b90af4b5 100644 --- a/libopenage/curve/keyframe.h +++ b/libopenage/curve/keyframe.h @@ -2,9 +2,9 @@ #pragma once -#include - #include "time/time.h" +#include "util/fixed_point.h" + namespace openage::curve { diff --git a/libopenage/curve/keyframe_container.h b/libopenage/curve/keyframe_container.h index 33459045e2..c61ac5105a 100644 --- a/libopenage/curve/keyframe_container.h +++ b/libopenage/curve/keyframe_container.h @@ -2,14 +2,15 @@ #pragma once +#include +#include #include -#include #include #include "curve/keyframe.h" -#include "error/error.h" #include "time/time.h" -#include "util/compiler.h" +#include "util/fixed_point.h" + namespace openage::curve { diff --git a/libopenage/curve/map.h b/libopenage/curve/map.h index 22145d7226..3a8374a128 100644 --- a/libopenage/curve/map.h +++ b/libopenage/curve/map.h @@ -5,9 +5,11 @@ #include #include #include +#include #include "curve/map_filter_iterator.h" #include "time/time.h" +#include "util/fixed_point.h" namespace openage::curve { diff --git a/libopenage/curve/map_filter_iterator.h b/libopenage/curve/map_filter_iterator.h index b307239619..e514330f67 100644 --- a/libopenage/curve/map_filter_iterator.h +++ b/libopenage/curve/map_filter_iterator.h @@ -2,9 +2,6 @@ #pragma once -#include -#include - #include "curve/iterator.h" #include "time/time.h" diff --git a/libopenage/curve/queue.h b/libopenage/curve/queue.h index dcdd9ed10a..3ac8d20d9a 100644 --- a/libopenage/curve/queue.h +++ b/libopenage/curve/queue.h @@ -2,15 +2,25 @@ #pragma once +#include #include #include +#include +#include +#include "curve/iterator.h" #include "curve/queue_filter_iterator.h" #include "event/evententity.h" #include "time/time.h" +#include "util/fixed_point.h" -namespace openage::curve { +namespace openage { +namespace event { +class EventLoop; +} + +namespace curve { /** * A container that manages events on a timeline. Every event has exactly one @@ -285,4 +295,5 @@ void Queue::clear(const time::time_t &time) { } -} // namespace openage::curve +} // namespace curve +} // namespace openage diff --git a/libopenage/curve/queue_filter_iterator.h b/libopenage/curve/queue_filter_iterator.h index d157a766a9..b8ecf77036 100644 --- a/libopenage/curve/queue_filter_iterator.h +++ b/libopenage/curve/queue_filter_iterator.h @@ -5,9 +5,6 @@ #include "curve/iterator.h" #include "time/time.h" -#include -#include - namespace openage::curve { diff --git a/libopenage/curve/segmented.cpp b/libopenage/curve/segmented.cpp index 3431a3dc15..209901da30 100644 --- a/libopenage/curve/segmented.cpp +++ b/libopenage/curve/segmented.cpp @@ -1,9 +1,9 @@ -// Copyright 2019-2021 the openage authors. See copying.md for legal info. +// Copyright 2019-2023 the openage authors. See copying.md for legal info. -#include "continuous.h" +#include "segmented.h" namespace openage::curve { // This file is intended to be empty -} // namespace openage::curve +} // namespace openage::curve diff --git a/libopenage/curve/segmented.h b/libopenage/curve/segmented.h index 250e2c22c3..e02572060f 100644 --- a/libopenage/curve/segmented.h +++ b/libopenage/curve/segmented.h @@ -3,10 +3,11 @@ #pragma once #include -#include +#include #include "curve/interpolated.h" -#include "log/log.h" +#include "time/time.h" + namespace openage::curve { diff --git a/libopenage/curve/tests/container.cpp b/libopenage/curve/tests/container.cpp index 5843dd48c9..a5efbf2887 100644 --- a/libopenage/curve/tests/container.cpp +++ b/libopenage/curve/tests/container.cpp @@ -1,14 +1,21 @@ // Copyright 2017-2023 the openage authors. See copying.md for legal info. -#include "curve/continuous.h" -#include "curve/discrete.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "curve/iterator.h" #include "curve/map.h" +#include "curve/map_filter_iterator.h" #include "curve/queue.h" +#include "curve/queue_filter_iterator.h" +#include "event/event_loop.h" #include "testing/testing.h" -#include "time/time.h" - -#include -#include namespace openage::curve::tests { diff --git a/libopenage/curve/tests/curve_types.cpp b/libopenage/curve/tests/curve_types.cpp index 7a2d3289e6..ab75fb5408 100644 --- a/libopenage/curve/tests/curve_types.cpp +++ b/libopenage/curve/tests/curve_types.cpp @@ -1,16 +1,20 @@ // Copyright 2017-2023 the openage authors. See copying.md for legal info. +#include +#include +#include +#include + #include "curve/continuous.h" #include "curve/discrete.h" #include "curve/discrete_mod.h" +#include "curve/keyframe.h" #include "curve/keyframe_container.h" #include "curve/segmented.h" #include "event/event_loop.h" -#include "log/log.h" #include "testing/testing.h" #include "time/time.h" - -#include "util/compiler.h" +#include "util/fixed_point.h" namespace openage::curve::tests { diff --git a/libopenage/datastructure/tests.cpp b/libopenage/datastructure/tests.cpp index e6f140e810..d481922a80 100644 --- a/libopenage/datastructure/tests.cpp +++ b/libopenage/datastructure/tests.cpp @@ -1,14 +1,18 @@ -// Copyright 2014-2020 the openage authors. See copying.md for legal info. +// Copyright 2014-2023 the openage authors. See copying.md for legal info. #include "tests.h" +#include +#include +#include +#include #include -#include "../testing/testing.h" +#include "testing/testing.h" -#include "constexpr_map.h" -#include "pairing_heap.h" -#include "concurrent_queue.h" +#include "datastructure/concurrent_queue.h" +#include "datastructure/constexpr_map.h" +#include "datastructure/pairing_heap.h" namespace openage::datastructure::tests { @@ -155,33 +159,41 @@ void constexpr_map() { static_assert(create_const_map(std::make_pair(0, 42)).size() == 1, "wrong size"); static_assert(create_const_map(std::make_pair(0, 42), - std::make_pair(13, 37)).size() == 2, + std::make_pair(13, 37)) + .size() + == 2, "wrong size"); static_assert(not create_const_map().contains(9001), "empty map doesn't contain anything"); static_assert(create_const_map(std::make_pair(42, 0), - std::make_pair(13, 37)).contains(42), + std::make_pair(13, 37)) + .contains(42), "contained element missing"); static_assert(create_const_map(std::make_pair(42, 0), - std::make_pair(13, 37)).contains(13), + std::make_pair(13, 37)) + .contains(13), "contained element missing"); static_assert(not create_const_map(std::make_pair(42, 0), - std::make_pair(13, 37)).contains(9001), + std::make_pair(13, 37)) + .contains(9001), "uncontained element seems to be contained."); static_assert(create_const_map(std::make_pair(42, 9001), - std::make_pair(13, 37)).get(42) == 9001, + std::make_pair(13, 37)) + .get(42) + == 9001, "fetched wrong value"); static_assert(create_const_map(std::make_pair(42, 9001), - std::make_pair(13, 37)).get(13) == 37, + std::make_pair(13, 37)) + .get(13) + == 37, "fetched wrong value"); - constexpr ConstMap cmap { + constexpr ConstMap cmap{ std::pair(0, 0), std::pair(13, 37), - std::pair(42, 9001) - }; + std::pair(42, 9001)}; cmap.contains(0) or TESTFAIL; not cmap.contains(18) or TESTFAIL; @@ -194,7 +206,7 @@ void constexpr_map() { /** * A simple class that can be move-constructed but not copy-constructed */ -class MoveOnly{ +class MoveOnly { private: int data; @@ -205,9 +217,11 @@ class MoveOnly{ } public: - MoveOnly(int data): data(data) {} + MoveOnly(int data) : + data(data) {} MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly&& other): data(other.release()) {} + MoveOnly(MoveOnly &&other) : + data(other.release()) {} ~MoveOnly() {} int get() const { @@ -218,13 +232,16 @@ class MoveOnly{ /** * A simple class that can be copy-constructed but not move-constructed */ -class CopyOnly{ +class CopyOnly { private: int data; + public: - CopyOnly(int data): data(data) {} - CopyOnly(const CopyOnly & other): data(other.get()) {} - CopyOnly(CopyOnly&&) = delete; + CopyOnly(int data) : + data(data) {} + CopyOnly(const CopyOnly &other) : + data(other.get()) {} + CopyOnly(CopyOnly &&) = delete; ~CopyOnly() {} int get() const { @@ -235,7 +252,7 @@ class CopyOnly{ /** * A simple class that can be both copy-constructed and move-constructed */ -class CopyMove{ +class CopyMove { private: int data; @@ -254,16 +271,19 @@ class CopyMove{ } public: - CopyMove(int data): data(data) { + CopyMove(int data) : + data(data) { atomic_inc(); } - CopyMove(const CopyMove & other): data(other.get()) { + CopyMove(const CopyMove &other) : + data(other.get()) { atomic_inc(); } // Move constructor does not increase global counter - CopyMove(CopyMove&& other): data(other.release()) {} + CopyMove(CopyMove &&other) : + data(other.release()) {} ~CopyMove() {} @@ -333,4 +353,4 @@ void concurrent_queue() { concurrent_queue_copy_move_elements_compilation(); } -} // openage::datastructure::tests +} // namespace openage::datastructure::tests diff --git a/libopenage/datastructure/tests.h b/libopenage/datastructure/tests.h index 6777c75eab..d615eaf6ba 100644 --- a/libopenage/datastructure/tests.h +++ b/libopenage/datastructure/tests.h @@ -1,10 +1,10 @@ -// Copyright 2014-2020 the openage authors. See copying.md for legal info. +// Copyright 2014-2023 the openage authors. See copying.md for legal info. #pragma once +#include #include -#include -#include + namespace openage::datastructure::tests { @@ -14,11 +14,11 @@ namespace openage::datastructure::tests { struct heap_elem { int data; - bool operator <(const heap_elem &other) const { + bool operator<(const heap_elem &other) const { return this->data < other.data; } - bool operator ==(const heap_elem &other) const { + bool operator==(const heap_elem &other) const { return this->data == other.data; } }; @@ -30,9 +30,9 @@ namespace std { /** * hash function for the simple heap_elem */ -template<> +template <> struct hash { - size_t operator ()(const openage::datastructure::tests::heap_elem &elem) const { + size_t operator()(const openage::datastructure::tests::heap_elem &elem) const { return elem.data; } }; diff --git a/libopenage/engine/engine.cpp b/libopenage/engine/engine.cpp index 24f1f4ae83..1278506629 100644 --- a/libopenage/engine/engine.cpp +++ b/libopenage/engine/engine.cpp @@ -3,6 +3,7 @@ #include "engine.h" #include "log/log.h" +#include "log/message.h" #include "cvar/cvar.h" #include "gamestate/simulation.h" diff --git a/libopenage/engine/engine.h b/libopenage/engine/engine.h index 3766db7496..40fe9882b9 100644 --- a/libopenage/engine/engine.h +++ b/libopenage/engine/engine.h @@ -2,6 +2,9 @@ #pragma once +#include +#include +#include #include #include diff --git a/libopenage/error/demo.cpp b/libopenage/error/demo.cpp index 8e385c7d1d..f330705cd6 100644 --- a/libopenage/error/demo.cpp +++ b/libopenage/error/demo.cpp @@ -1,15 +1,14 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. -#include -#include +#include +#include -#include "../log/log.h" -#include "backtrace.h" -#include "error.h" +#include "error/error.h" +#include "log/log.h" +#include "log/message.h" -namespace openage { -namespace error { +namespace openage::error { // anonymous namespace to prevent linkage for exception demo helper functions. @@ -25,7 +24,8 @@ void bar(int i) { if (i % 5 == 3) { try { foo(); - } catch (...) { + } + catch (...) { // Note: pokemon exception handling is generally discouraged. // This serves as a demo how the Error constructor nevertheless // manages to correctly capture the cause exception. @@ -33,7 +33,8 @@ void bar(int i) { throw Error(MSG(crit).fmt("exception in foo. i=%d", i)); } - } else { + } + else { bar(i + 1); } } @@ -45,17 +46,17 @@ void bar(int i) { void demo() { try { bar(0); - } catch (Error &exc) { + } + catch (Error &exc) { if (exc.backtrace) { exc.trim_backtrace(); } - log::log(MSG(info) << - "exception_demo: captured the following exception:" << std::endl << - exc << std::endl << - "exception_demo: end of exception"); + log::log(MSG(info) << "exception_demo: captured the following exception:" << std::endl + << exc << std::endl + << "exception_demo: end of exception"); } } -}} // openage::error +} // namespace openage::error diff --git a/libopenage/error/error.cpp b/libopenage/error/error.cpp index 5d02074be9..a47e0cccfe 100644 --- a/libopenage/error/error.cpp +++ b/libopenage/error/error.cpp @@ -2,11 +2,12 @@ #include "error.h" +#include #include -#include "../util/compiler.h" +#include "util/compiler.h" -#include "stackanalyzer.h" +#include "error/stackanalyzer.h" namespace openage::error { @@ -18,11 +19,9 @@ void Error::debug_break_on_create(bool state) { enable_break_on_create = state; } -Error::Error(const log::message &msg, bool generate_backtrace, bool store_cause) - : +Error::Error(const log::message &msg, bool generate_backtrace, bool store_cause) : std::runtime_error{runtime_error_message}, msg(msg) { - if (enable_break_on_create) [[unlikely]] { BREAKPOINT; } @@ -49,10 +48,12 @@ void Error::store_cause() { try { throw; - } catch (Error &cause) { + } + catch (Error &cause) { cause.trim_backtrace(); this->cause = std::current_exception(); - } catch (...) { + } + catch (...) { this->cause = std::current_exception(); } } @@ -65,8 +66,7 @@ void Error::trim_backtrace() { } -Error::Error() - : +Error::Error() : std::runtime_error{runtime_error_message} {} @@ -87,28 +87,34 @@ void Error::rethrow_cause() const { } -std::ostream &operator <<(std::ostream &os, const Error &e) { +std::ostream &operator<<(std::ostream &os, const Error &e) { // output the exception cause bool had_a_cause = true; try { e.rethrow_cause(); had_a_cause = false; - } catch (Error &cause) { + } + catch (Error &cause) { os << cause << std::endl; - } catch (std::exception &cause) { + } + catch (std::exception &cause) { os << util::typestring(cause) << ": " << cause.what() << std::endl; - } catch (...) { + } + catch (...) { os << "unknown non std::exception cause!" << std::endl; } if (had_a_cause) { - os << std::endl << "The above exception was the direct cause of the following exception:" << std::endl << std::endl; + os << std::endl + << "The above exception was the direct cause of the following exception:" << std::endl + << std::endl; } // output the exception backtrace if (e.backtrace) { os << *e.backtrace; - } else { + } + else { os << "origin:" << std::endl; } @@ -117,7 +123,8 @@ std::ostream &operator <<(std::ostream &os, const Error &e) { e.msg.filename, e.msg.lineno, e.msg.functionname, - nullptr} << std::endl; + nullptr} + << std::endl; os << e.type_name(); @@ -129,4 +136,4 @@ std::ostream &operator <<(std::ostream &os, const Error &e) { } -} // openage::error +} // namespace openage::error diff --git a/libopenage/error/error.h b/libopenage/error/error.h index 1aabc33390..cbd9b949ac 100644 --- a/libopenage/error/error.h +++ b/libopenage/error/error.h @@ -2,6 +2,8 @@ #pragma once +#include + // pxd: from libcpp cimport bool #include #include @@ -18,13 +20,15 @@ namespace openage { namespace error { // forward-declaration to avoid the header include. class Backtrace; -}} +} // namespace error +} // namespace openage namespace openage { namespace pyinterface { // forward-declaration for use in the 'friend' declaration below. class PyException; -}} +} // namespace pyinterface +} // namespace openage namespace openage { @@ -63,7 +67,7 @@ class OAAPI Error : public std::runtime_error { * If true, a pointer to the causing exception is * collected and stored (default true). */ - Error(const log::message &msg, bool generate_backtrace=true, bool store_cause=true); + Error(const log::message &msg, bool generate_backtrace = true, bool store_cause = true); /** @@ -142,17 +146,21 @@ class OAAPI Error : public std::runtime_error { }; -std::ostream &operator <<(std::ostream &os, const Error &e); +std::ostream &operator<<(std::ostream &os, const Error &e); -[[deprecated("add message to the ENSURE before pushing, please")]] -inline std::string no_ensuring_message() -{ +[[deprecated("add message to the ENSURE before pushing, please")]] inline std::string no_ensuring_message() { return std::string{}; } // ENSURE(condition, errormessage << variable << etcetc) -#define ENSURE(...) do { if (not OPENAGE_ENS_FIRST(__VA_ARGS__)) [[unlikely]] { throw ::openage::error::Error(MSG(err) OPENAGE_ENS_REST(__VA_ARGS__)); } } while (0) +#define ENSURE(...) \ + do { \ + if (not OPENAGE_ENS_FIRST(__VA_ARGS__)) [[unlikely]] { \ + throw ::openage::error::Error(MSG(err) OPENAGE_ENS_REST(__VA_ARGS__)); \ + } \ + } \ + while (0) /* * expands to the first argument @@ -160,7 +168,7 @@ inline std::string no_ensuring_message() * https://stackoverflow.com/a/9338429 */ #define OPENAGE_PP_GLUE(macro, args) macro args -#define OPENAGE_ENS_FIRST(...) OPENAGE_PP_GLUE(OPENAGE_ENS_FIRST_HELPER,(__VA_ARGS__, throwaway)) +#define OPENAGE_ENS_FIRST(...) OPENAGE_PP_GLUE(OPENAGE_ENS_FIRST_HELPER, (__VA_ARGS__, throwaway)) #define OPENAGE_ENS_FIRST_HELPER(first, ...) (first) /* @@ -181,8 +189,8 @@ inline std::string no_ensuring_message() #define OPENAGE_ENS_NUM_IMPL(args) OPENAGE_ENS_SELECT_2ND args #define OPENAGE_ENS_SELECT_2ND(a1, a2, a3, ...) a3 -} // error +} // namespace error using error::Error; -} // openage::error +} // namespace openage diff --git a/libopenage/error/gl_debug.cpp b/libopenage/error/gl_debug.cpp index bad32aede4..fc89d33d74 100644 --- a/libopenage/error/gl_debug.cpp +++ b/libopenage/error/gl_debug.cpp @@ -1,16 +1,17 @@ -// Copyright 2016-2017 the openage authors. See copying.md for legal info. +// Copyright 2016-2023 the openage authors. See copying.md for legal info. #include "gl_debug.h" #include -#include "error.h" +#include "log/message.h" -namespace openage { -namespace error { +#include "error/error.h" + +namespace openage::error { namespace { -void APIENTRY callback(GLenum source, GLenum, GLuint, GLenum, GLsizei, const GLchar *message, const void*) { +void APIENTRY callback(GLenum source, GLenum, GLuint, GLenum, GLsizei, const GLchar *message, const void *) { const char *source_name; switch (source) { @@ -40,7 +41,7 @@ void APIENTRY callback(GLenum source, GLenum, GLuint, GLenum, GLsizei, const GLc throw Error(MSG(err) << "OpenGL error from " << source_name << ": '" << message << "'."); } -} +} // namespace SDL_GLContext create_debug_context(SDL_Window *window) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); @@ -52,7 +53,7 @@ SDL_GLContext create_debug_context(SDL_Window *window) { glGetIntegerv(GL_CONTEXT_FLAGS, &flags); if (!(flags & GL_CONTEXT_FLAG_DEBUG_BIT)) - throw Error(MSG(err)<< "Failed creating a debug OpenGL context."); + throw Error(MSG(err) << "Failed creating a debug OpenGL context."); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE); @@ -66,4 +67,4 @@ SDL_GLContext create_debug_context(SDL_Window *window) { return ctx; } -}} // openage::error +} // namespace openage::error diff --git a/libopenage/error/handlers.cpp b/libopenage/error/handlers.cpp index c10defe678..fe00f2c518 100644 --- a/libopenage/error/handlers.cpp +++ b/libopenage/error/handlers.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2021 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. /* * This file holds handlers for std::terminate and SIGSEGV. @@ -12,9 +12,13 @@ #include "handlers.h" +#include +#include +#include #include +#include #include -#include +#include #ifdef _MSC_VER #include @@ -22,12 +26,14 @@ #include #endif -#include "../util/signal.h" -#include "../util/init.h" -#include "../util/language.h" +#include "util/init.h" +#include "util/language.h" + +#include "error/backtrace.h" +#include "error/error.h" +#include "error/stackanalyzer.h" +#include "util/compiler.h" -#include "error.h" -#include "stackanalyzer.h" namespace openage { namespace error { @@ -73,23 +79,25 @@ util::OnDeInit restore_handlers([]() { std::cout << "\n\x1b[31;1mFATAL: terminate has been called\x1b[m" << std::endl; if (std::exception_ptr e_ptr = std::current_exception()) { - std::cout << "\n\x1b[33muncaught exception\x1b[m\n" << std::endl; + std::cout << "\n\x1b[33muncaught exception\x1b[m\n" + << std::endl; try { std::rethrow_exception(e_ptr); - } catch (Error &exc) { + } + catch (Error &exc) { std::cout << exc << std::endl; - } catch (std::exception &exc) { - std::cout << - "std::exception of type " << - util::typestring(exc) << - ": " << exc.what() << std::endl; - } catch (...) { + } + catch (std::exception &exc) { + std::cout << "std::exception of type " << util::typestring(exc) << ": " << exc.what() << std::endl; + } + catch (...) { std::cout << "non-standard exception object" << std::endl; } } - std::cout << "\n\x1b[33mcurrent stack:\x1b[m\n" << std::endl; + std::cout << "\n\x1b[33mcurrent stack:\x1b[m\n" + << std::endl; StackAnalyzer backtrace; backtrace.analyze(); @@ -98,7 +106,8 @@ util::OnDeInit restore_handlers([]() { // die again to enable debugger functionality. // that maybe print some additional useful info that we forgot about. // TODO: we maybe want to prevent that for end-users. - std::cout << "\x1b[33mhanding over to the system...\x1b[m\n" << std::endl; + std::cout << "\x1b[33mhanding over to the system...\x1b[m\n" + << std::endl; std::terminate(); } @@ -124,9 +133,12 @@ void exit_handler() { // The actual proper way of exiting the running game is via throwing // an exception or similar action. - if (exit_ok) { return; } + if (exit_ok) { + return; + } - std::cout << "\x1b[31;1mexit() was called in an illegal place\x1b[m\n" << std::endl; + std::cout << "\x1b[31;1mexit() was called in an illegal place\x1b[m\n" + << std::endl; } @@ -135,4 +147,5 @@ void set_exit_ok(bool value) { } -}} // openage::error +} // namespace error +} // namespace openage diff --git a/libopenage/error/stackanalyzer.cpp b/libopenage/error/stackanalyzer.cpp index 97b12ce9dc..878cb01025 100644 --- a/libopenage/error/stackanalyzer.cpp +++ b/libopenage/error/stackanalyzer.cpp @@ -1,12 +1,14 @@ -// Copyright 2015-2019 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "stackanalyzer.h" -#include "config.h" -#include "../log/log.h" -#include "../util/compiler.h" -#include "../util/init.h" +#include +#include +#include "config.h" +#include "log/log.h" +#include "util/compiler.h" +#include "util/init.h" namespace openage::error { @@ -23,8 +25,7 @@ constexpr uint64_t skip_entry_frames = 1; */ constexpr uint64_t base_skip_frames = 1; -} // openage::error - +} // namespace openage::error #if WITH_BACKTRACE @@ -57,10 +58,10 @@ struct backtrace_state *bt_state; util::OnInit init_backtrace_state([]() { bt_state = backtrace_create_state( - nullptr, // auto-determine filename - 1, // threaded + nullptr, // auto-determine filename + 1, // threaded backtrace_error_callback, - nullptr // passed to the callback + nullptr // passed to the callback ); // There's no documentaton on how to free the state. @@ -82,7 +83,6 @@ void backtrace_syminfo_callback( const char *symname, uintptr_t /*unused symval*/, uintptr_t /*unused symsize*/) { - // in this fallback case, we can't get filename or line info, but at least we // can get and demangle the symbol name... hopefully. @@ -92,8 +92,7 @@ void backtrace_syminfo_callback( "", 0, (symname == nullptr) ? "" : util::demangle(symname), - reinterpret_cast(pc) - }); + reinterpret_cast(pc)}); } @@ -109,15 +108,14 @@ int backtrace_pcinfo_callback(void *data, uintptr_t pc, const char *filename, in "", 0, util::symbol_name(reinterpret_cast(pc), false, true), - reinterpret_cast(pc) - }); - } else { + reinterpret_cast(pc)}); + } + else { symbol_vector->emplace_back(backtrace_symbol{ (filename == nullptr) ? "" : filename, static_cast(lineno), util::demangle(function), - reinterpret_cast(pc) - }); + reinterpret_cast(pc)}); } return 0; @@ -135,9 +133,9 @@ void backtrace_pcinfo_error_callback(void *data, const char *msg, int errorno) { info_cb_data->pc, backtrace_syminfo_callback, backtrace_error_callback, - info_cb_data - ); - } else { + info_cb_data); + } + else { // invoke the general error callback, which prints the message. backtrace_error_callback(nullptr, msg, errorno); } @@ -149,15 +147,14 @@ void backtrace_pcinfo_error_callback(void *data, const char *msg, int errorno) { void StackAnalyzer::analyze() { backtrace_simple( bt_state, - base_skip_frames, // skip some frames at "most recent call" + base_skip_frames, // skip some frames at "most recent call" backtrace_simple_callback, backtrace_error_callback, - reinterpret_cast(this) - ); + reinterpret_cast(this)); } -void StackAnalyzer::get_symbols(std::function cb, bool reversed) const { +void StackAnalyzer::get_symbols(std::function cb, bool reversed) const { info_cb_data_t info_cb_data; for (void *pc : this->stack_addrs) { @@ -171,8 +168,7 @@ void StackAnalyzer::get_symbols(std::function c info_cb_data.pc, backtrace_pcinfo_callback, backtrace_pcinfo_error_callback, - reinterpret_cast(&info_cb_data) - ); + reinterpret_cast(&info_cb_data)); } if (reversed) { @@ -180,14 +176,16 @@ void StackAnalyzer::get_symbols(std::function c for (size_t idx = info_cb_data.symbols.size(); idx-- > 0;) { cb(&info_cb_data.symbols[idx]); } - } else { + } + else { for (backtrace_symbol &symbol : info_cb_data.symbols) { cb(&symbol); } } } -}} // openage::error +} // namespace error +} // namespace openage #else // WITHOUT_BACKTRACE @@ -207,7 +205,8 @@ void StackAnalyzer::analyze() { this->stack_addrs = std::move(buffer); } -}} // openage::error +} // namespace error +} // namespace openage #else // not _MSC_VER @@ -255,14 +254,14 @@ void StackAnalyzer::analyze() { } } -} // openage::error +} // namespace openage::error #endif // for _MSC_VER or GNU execinfo namespace openage::error { -void StackAnalyzer::get_symbols(std::function cb, +void StackAnalyzer::get_symbols(std::function cb, bool reversed) const { backtrace_symbol symbol; symbol.filename = ""; @@ -290,7 +289,7 @@ void StackAnalyzer::get_symbols(std::function c } -} // openage::error +} // namespace openage::error #endif // WITHOUT_BACKTRACE @@ -313,4 +312,4 @@ void StackAnalyzer::trim_to_current_stack_frame() { } -} // openage::error +} // namespace openage::error diff --git a/libopenage/error/stackanalyzer.h b/libopenage/error/stackanalyzer.h index 290ae82aee..0b801a1f66 100644 --- a/libopenage/error/stackanalyzer.h +++ b/libopenage/error/stackanalyzer.h @@ -1,8 +1,12 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once -#include "backtrace.h" +#include +#include + +#include "error/backtrace.h" + namespace openage { namespace error { @@ -43,9 +47,10 @@ class StackAnalyzer : public Backtrace { std::vector stack_addrs; // Looks up symbol names for the program counter values. - void get_symbols(std::function cb, bool reversed) const override; + void get_symbols(std::function cb, bool reversed) const override; void trim_to_current_stack_frame() override; }; -}} // openage::error +} // namespace error +} // namespace openage diff --git a/libopenage/event/demo/gamestate.h b/libopenage/event/demo/gamestate.h index e133ad2a6e..b92b22ee93 100644 --- a/libopenage/event/demo/gamestate.h +++ b/libopenage/event/demo/gamestate.h @@ -2,20 +2,28 @@ #pragma once +#include #include +#include +#include #include +#include #include "config.h" #include "curve/continuous.h" #include "curve/discrete.h" -#include "event/event_loop.h" #include "event/evententity.h" #include "event/state.h" +#include "time/time.h" +#include "util/fixed_point.h" #include "util/strings.h" #include "util/vector.h" -namespace openage::event::demo { +namespace openage::event { +class EventLoop; + +namespace demo { #if WITH_NCURSES class Gui; @@ -170,4 +178,5 @@ class PongState : public State { #endif }; -} // namespace openage::event::demo +} // namespace demo +} // namespace openage::event diff --git a/libopenage/event/demo/gui.cpp b/libopenage/event/demo/gui.cpp index beb1aac281..100509c0f4 100644 --- a/libopenage/event/demo/gui.cpp +++ b/libopenage/event/demo/gui.cpp @@ -6,7 +6,9 @@ #if WITH_NCURSES #include -#include +#include +#include +#include #ifdef __MINGW32__ #include #else @@ -14,6 +16,10 @@ #endif // __MINGW32__ #include +#include "curve/continuous.h" +#include "curve/discrete.h" +#include "event/demo/gamestate.h" +#include "util/fixed_point.h" namespace openage::event::demo { diff --git a/libopenage/event/demo/gui.h b/libopenage/event/demo/gui.h index 4f65d81382..de21a99d02 100644 --- a/libopenage/event/demo/gui.h +++ b/libopenage/event/demo/gui.h @@ -2,22 +2,21 @@ #pragma once - #include "config.h" #if WITH_NCURSES -#include #include +#include +#include +#include -#include "gamestate.h" +#include "event/demo/gamestate.h" +#include "time/time.h" +#include "util/vector.h" namespace openage::event::demo { -class PongEvent; -class PongState; -class PongPlayer; - class Gui { public: @@ -40,6 +39,6 @@ class Gui { std::deque log_msgs; }; -} // openage::event::demo +} // namespace openage::event::demo #endif diff --git a/libopenage/event/demo/main.cpp b/libopenage/event/demo/main.cpp index fbadc1d81e..442e9910c7 100644 --- a/libopenage/event/demo/main.cpp +++ b/libopenage/event/demo/main.cpp @@ -7,11 +7,12 @@ #include #include "config.h" -#include "../event.h" -#include "aicontroller.h" -#include "gamestate.h" -#include "gui.h" -#include "physics.h" +#include "event/demo/aicontroller.h" +#include "event/demo/gamestate.h" +#include "event/demo/gui.h" +#include "event/demo/physics.h" +#include "event/event.h" +#include "event/event_loop.h" #if WITH_NCURSES #ifdef __MINGW32__ @@ -40,7 +41,6 @@ enum class timescale { void curvepong(bool disable_gui, bool no_human) { - bool enable_gui = not disable_gui; bool human_player = not no_human; @@ -64,7 +64,8 @@ void curvepong(bool disable_gui, bool no_human) { auto state = std::make_shared(loop, enable_gui #if WITH_NCURSES - , gui + , + gui #endif ); Physics::init(state, loop, now); @@ -78,7 +79,7 @@ void curvepong(bool disable_gui, bool no_human) { #if WITH_NCURSES if (state->enable_gui) { gui->clear(); - gui->get_display_size(state, now); // update gui related parameters + gui->get_display_size(state, now); // update gui related parameters } else { #endif @@ -95,9 +96,7 @@ void curvepong(bool disable_gui, bool no_human) { std::vector inputs; // this is the game loop, running while both players live! - while (state->p1->lives->get(now) > 0 and - state->p2->lives->get(now) > 0) { - + while (state->p1->lives->get(now) > 0 and state->p2->lives->get(now) > 0) { auto loop_start = Clock::now(); #if WITH_NCURSES @@ -111,19 +110,13 @@ void curvepong(bool disable_gui, bool no_human) { // player 1 can be AI or human. if (human_player) { - phys.process_input(state, state->p1, - inputs, loop, now); + phys.process_input(state, state->p1, inputs, loop, now); } else { - - phys.process_input(state, state->p1, - get_ai_inputs(state->p1, state->ball, now), - loop, now); + phys.process_input(state, state->p1, get_ai_inputs(state->p1, state->ball, now), loop, now); } - phys.process_input(state, state->p2, - get_ai_inputs(state->p2, state->ball, now), - loop, now); + phys.process_input(state, state->p2, get_ai_inputs(state->p2, state->ball, now), loop, now); // paddle x positions state->p1->paddle_x = 0; @@ -137,12 +130,9 @@ void curvepong(bool disable_gui, bool no_human) { gui->draw(state, now); int pos = 1; - mvprintw(pos++, state->display_boundary[0]/2 + 10, "Enqueued events:"); + mvprintw(pos++, state->display_boundary[0] / 2 + 10, "Enqueued events:"); for (const auto &e : loop->get_queue().get_event_queue().get_sorted_events()) { - mvprintw(pos++, state->display_boundary[0]/2 + 10, - "%f: %s", - e->get_time().to_double(), - e->get_eventhandler()->id().c_str()); + mvprintw(pos++, state->display_boundary[0] / 2 + 10, "%f: %s", e->get_time().to_double(), e->get_eventhandler()->id().c_str()); } gui->update_screen(); @@ -205,4 +195,4 @@ void curvepong(bool disable_gui, bool no_human) { } -} // openage::event::demo +} // namespace openage::event::demo diff --git a/libopenage/event/demo/physics.cpp b/libopenage/event/demo/physics.cpp index b0509e85ea..d43365f042 100644 --- a/libopenage/event/demo/physics.cpp +++ b/libopenage/event/demo/physics.cpp @@ -2,8 +2,11 @@ #include "physics.h" -#include -#include +#include +#include +#include +#include +#include #if WITH_NCURSES #ifdef __MINGW32__ @@ -15,10 +18,25 @@ #include "gui.h" #endif +#include "error/error.h" +#include "log/log.h" +#include "log/message.h" + #include "config.h" -#include "../../error/error.h" -#include "../../rng/global_rng.h" -#include "../../util/stringformatter.h" +#include "curve/continuous.h" +#include "curve/discrete.h" +#include "error/error.h" +#include "event/demo/gamestate.h" +#include "event/event.h" +#include "event/event_loop.h" +#include "event/eventhandler.h" +#include "log/log.h" +#include "log/message.h" +#include "rng/global_rng.h" +#include "time/time.h" +#include "util/fixed_point.h" +#include "util/stringformatter.h" +#include "util/vector.h" namespace openage::event::demo { @@ -26,11 +44,11 @@ namespace openage::event::demo { class BallReflectWall : public DependencyEventHandler { public: - BallReflectWall() : DependencyEventHandler("demo.ball.reflect_wall") {} + BallReflectWall() : + DependencyEventHandler("demo.ball.reflect_wall") {} void setup_event(const std::shared_ptr &evnt, const std::shared_ptr &gstate) override { - auto state = std::dynamic_pointer_cast(gstate); // FIXME dependency to a full ball object so that any curve @@ -47,8 +65,7 @@ class BallReflectWall : public DependencyEventHandler { const std::shared_ptr &target, const std::shared_ptr &gstate, const time::time_t &now, - const EventHandler::param_map &/*param*/) override { - + const EventHandler::param_map & /*param*/) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); auto speedcurve = state->ball->speed; @@ -67,22 +84,19 @@ class BallReflectWall : public DependencyEventHandler { time::time_t ty = 0; if (speed[1] > 0) { ty = time::time_t::from_double( - (state->display_boundary[1] - pos[1]) / speed[1] - ); + (state->display_boundary[1] - pos[1]) / speed[1]); } else if (speed[1] < 0) { ty = time::time_t::from_double( - pos[1] / -speed[1] - ); + pos[1] / -speed[1]); } state->ball->position->set_last(now + ty, pos + (speed * ty.to_double())); } time::time_t predict_invoke_time(const std::shared_ptr &target, - const std::shared_ptr &gstate, - const time::time_t &now) override { - + const std::shared_ptr &gstate, + const time::time_t &now) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); @@ -105,7 +119,9 @@ class BallReflectWall : public DependencyEventHandler { if (state->enable_gui) { util::FString str; str.fmt("WALL TY %f NOW %f, NOWTY %f ", - ty.to_double(), now.to_double(), (now + ty).to_double()); + ty.to_double(), + now.to_double(), + (now + ty).to_double()); state->gui->log(str); }; #endif @@ -116,13 +132,11 @@ class BallReflectWall : public DependencyEventHandler { class BallReflectPanel : public DependencyEventHandler { public: - BallReflectPanel () - : + BallReflectPanel() : DependencyEventHandler("demo.ball.reflect_panel") {} void setup_event(const std::shared_ptr &target, const std::shared_ptr &gstate) override { - auto state = std::dynamic_pointer_cast(gstate); // FIXME dependency to a full ball object @@ -136,11 +150,10 @@ class BallReflectPanel : public DependencyEventHandler { // FIXME we REALLY need dependencies to objects void invoke(EventLoop &mgr, - const std::shared_ptr &/*target*/, + const std::shared_ptr & /*target*/, const std::shared_ptr &gstate, const time::time_t &now, - const EventHandler::param_map &/*param*/) override { - + const EventHandler::param_map & /*param*/) override { auto state = std::dynamic_pointer_cast(gstate); auto pos = state->ball->position->get(now); @@ -160,11 +173,7 @@ class BallReflectPanel : public DependencyEventHandler { } #endif - if (pos[0] <= 1 and - speed[0] < 0 and - (pos[1] < state->p1->position->get(now) - state->p1->size->get(now) / 2 or - pos[1] > state->p1->position->get(now) + state->p1->size->get(now) / 2)) { - + if (pos[0] <= 1 and speed[0] < 0 and (pos[1] < state->p1->position->get(now) - state->p1->size->get(now) / 2 or pos[1] > state->p1->position->get(now) + state->p1->size->get(now) / 2)) { // ball missed the paddle of player 1 auto l = state->p1->lives->get(now); l--; @@ -174,11 +183,7 @@ class BallReflectPanel : public DependencyEventHandler { Physics::reset(state, mgr, now); } - else if (pos[0] >= state->display_boundary[0] - 1 and - speed[0] > 0 and - (pos[1] < state->p2->position->get(now) - state->p2->size->get(now) / 2 or - pos[1] > state->p2->position->get(now) + state->p2->size->get(now) / 2)) { - + else if (pos[0] >= state->display_boundary[0] - 1 and speed[0] > 0 and (pos[1] < state->p2->position->get(now) - state->p2->size->get(now) / 2 or pos[1] > state->p2->position->get(now) + state->p2->size->get(now) / 2)) { // ball missed the paddel of player 2 auto l = state->p2->lives->get(now); l--; @@ -187,7 +192,7 @@ class BallReflectPanel : public DependencyEventHandler { Physics::reset(state, mgr, now); } - else if (pos[0] >= state->display_boundary[0]- 1 || pos[0] <= 1) { + else if (pos[0] >= state->display_boundary[0] - 1 || pos[0] <= 1) { speed[0] *= -1; state->ball->speed->set_last(now, speed); state->ball->position->set_last(now, pos); @@ -213,9 +218,8 @@ class BallReflectPanel : public DependencyEventHandler { } time::time_t predict_invoke_time(const std::shared_ptr &target, - const std::shared_ptr &gstate, - const time::time_t &now) override { - + const std::shared_ptr &gstate, + const time::time_t &now) override { auto positioncurve = std::dynamic_pointer_cast>(target); auto state = std::dynamic_pointer_cast(gstate); @@ -243,7 +247,7 @@ class BallReflectPanel : public DependencyEventHandler { else { #endif log::log(INFO << "predicting panel reflection at t=" << now - << ", next reflection at t=" << (now + ty)); + << ", next reflection at t=" << (now + ty)); #if WITH_NCURSES } #endif @@ -265,19 +269,17 @@ class BallReflectPanel : public DependencyEventHandler { class ResetGame : public OnceEventHandler { public: - ResetGame () - : + ResetGame() : OnceEventHandler("demo.reset") {} - void setup_event(const std::shared_ptr &/*target*/, - const std::shared_ptr &/*state*/) override {} + void setup_event(const std::shared_ptr & /*target*/, + const std::shared_ptr & /*state*/) override {} - void invoke(EventLoop &/*mgr*/, - const std::shared_ptr &/*target*/, + void invoke(EventLoop & /*mgr*/, + const std::shared_ptr & /*target*/, const std::shared_ptr &gstate, const time::time_t &now, - const EventHandler::param_map &/*param*/) override { - + const EventHandler::param_map & /*param*/) override { auto state = std::dynamic_pointer_cast(gstate); // Check if the condition still applies @@ -302,8 +304,7 @@ class ResetGame : public OnceEventHandler { float diry = (rng::random() % 2) ? 1 : -1; auto init_speed = util::Vector2d( dirx * (10 + (rng::random() % 100) / 4.f), - diry * (0.3 + (rng::random() % 100) / 18.f) - ); + diry * (0.3 + (rng::random() % 100) / 18.f)); state->ball->speed->set_last(now, init_speed); auto pos = state->ball->position->get(now); @@ -351,9 +352,9 @@ class ResetGame : public OnceEventHandler { state->ball->position->set_last(now + ty, pos + init_speed * ty.to_double()); } - time::time_t predict_invoke_time(const std::shared_ptr &/*target*/, - const std::shared_ptr &/*state*/, - const time::time_t &old_time) override { + time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, + const std::shared_ptr & /*state*/, + const time::time_t &old_time) override { return old_time; } }; @@ -362,7 +363,6 @@ class ResetGame : public OnceEventHandler { void Physics::init(const std::shared_ptr &gstate, const std::shared_ptr &loop, const time::time_t &now) { - auto state = std::dynamic_pointer_cast(gstate); if (not state->enable_gui) { @@ -385,18 +385,15 @@ void Physics::process_input(const std::shared_ptr &state, const std::vector &events, const std::shared_ptr &mgr, const time::time_t &now) { - // seconds into the future constexpr static auto predicted_movement_time = time::time_t::from_double(5.0); // lines per second constexpr static double movement_speed = 8.0; - for (auto& evnt : events) { - + for (auto &evnt : events) { // Process only if the future has changed if (player->state->get(now).state != evnt.state) { - // log the new input in the state curve player->state->set_last(now, evnt); @@ -408,7 +405,7 @@ void Physics::process_input(const std::shared_ptr &state, extend_previous_prediction = true; } - switch(evnt.state) { + switch (evnt.state) { case PongEvent::UP: case PongEvent::DOWN: { float current_pos = player->position->get(now); @@ -436,10 +433,7 @@ void Physics::process_input(const std::shared_ptr &state, // change the position by integrating the speed curve. // TODO: add native integral to curves. - float new_pos = current_pos + - (((player->speed->get(move_stop_guess) + - player->speed->get(now)) / 2.0) * - predicted_movement_time.to_double()); + float new_pos = current_pos + (((player->speed->get(move_stop_guess) + player->speed->get(now)) / 2.0) * predicted_movement_time.to_double()); // if paddle will get out-of-bounds, calculate the bound hit time @@ -489,9 +483,8 @@ void Physics::process_input(const std::shared_ptr &state, void Physics::reset(const std::shared_ptr &gstate, EventLoop &mgr, const time::time_t &now) { - auto state = std::dynamic_pointer_cast(gstate); mgr.create_event("demo.reset", state->ball->position, state, now); } -} // openage::event::demo +} // namespace openage::event::demo diff --git a/libopenage/event/demo/physics.h b/libopenage/event/demo/physics.h index de1b41bb2c..7b46c250c9 100644 --- a/libopenage/event/demo/physics.h +++ b/libopenage/event/demo/physics.h @@ -2,18 +2,23 @@ #pragma once -#include "gamestate.h" -#include "../../time/time.h" - +#include #include +#include "time/time.h" + namespace openage::event { class EventLoop; +class State; namespace demo { +class PongEvent; +class PongPlayer; +class PongState; + class Physics { public: static void init(const std::shared_ptr &, @@ -31,4 +36,5 @@ class Physics { const time::time_t &); }; -}} // openage::event::demo +} // namespace demo +} // namespace openage::event diff --git a/libopenage/event/event.cpp b/libopenage/event/event.cpp index cf02d77903..67b5cbc814 100644 --- a/libopenage/event/event.cpp +++ b/libopenage/event/event.cpp @@ -2,13 +2,19 @@ #include "event.h" -#include +#include +#include +#include -#include "evententity.h" -#include "eventhandler.h" +#include "log/log.h" +#include "log/message.h" + +#include "event/evententity.h" +#include "event/eventhandler.h" +#include "time/time.h" +#include "util/fixed_point.h" +#include "util/hash.h" -#include "../log/log.h" -#include "../util/hash.h" namespace openage::event { diff --git a/libopenage/event/event.h b/libopenage/event/event.h index 73f2643104..72c943d807 100644 --- a/libopenage/event/event.h +++ b/libopenage/event/event.h @@ -2,14 +2,14 @@ #pragma once +#include #include -#include "../time/time.h" -#include "eventhandler.h" +#include "event/eventhandler.h" +#include "time/time.h" -namespace openage::event { -class EventQueue; +namespace openage::event { class EventEntity; /** diff --git a/libopenage/event/event_loop.cpp b/libopenage/event/event_loop.cpp index 71e04429e7..eb9e8ca6f1 100644 --- a/libopenage/event/event_loop.cpp +++ b/libopenage/event/event_loop.cpp @@ -2,12 +2,21 @@ #include "event_loop.h" -#include "event.h" -#include "evententity.h" -#include "eventhandler.h" -#include "eventqueue.h" +#include +#include +#include +#include -#include "../log/log.h" +#include "log/log.h" +#include "log/message.h" + +#include "error/error.h" +#include "event/event.h" +#include "event/evententity.h" +#include "event/eventhandler.h" +#include "event/eventqueue.h" +#include "event/eventstore.h" +#include "util/fixed_point.h" namespace openage::event { @@ -193,7 +202,7 @@ void EventLoop::update_changes(const std::shared_ptr &state) { if (entity) { time::time_t new_time = evnt->get_eventhandler() - ->predict_invoke_time(entity, state, change.time); + ->predict_invoke_time(entity, state, change.time); if (new_time != std::numeric_limits::min()) { log::log(DBG << "Loop: due to a change, rescheduling event of '" diff --git a/libopenage/event/event_loop.h b/libopenage/event/event_loop.h index 96df27948a..5a5a45bc64 100644 --- a/libopenage/event/event_loop.h +++ b/libopenage/event/event_loop.h @@ -2,17 +2,16 @@ #pragma once -#include -#include #include #include +#include #include -#include "event.h" -#include "eventqueue.h" -#include "log/log.h" +#include "event/eventhandler.h" +#include "event/eventqueue.h" #include "time/time.h" + namespace openage::event { // The demo wants to display internal details diff --git a/libopenage/event/evententity.cpp b/libopenage/event/evententity.cpp index 9f0de69ef0..f5fcfaebf1 100644 --- a/libopenage/event/evententity.cpp +++ b/libopenage/event/evententity.cpp @@ -2,13 +2,15 @@ #include "evententity.h" -#include - -#include "event.h" -#include "event_loop.h" -#include "eventhandler.h" +#include #include "log/log.h" +#include "log/message.h" + +#include "event/event.h" +#include "event/event_loop.h" +#include "event/eventhandler.h" +#include "util/fixed_point.h" namespace openage::event { diff --git a/libopenage/event/evententity.h b/libopenage/event/evententity.h index 04e45f5d50..6dc90c4716 100644 --- a/libopenage/event/evententity.h +++ b/libopenage/event/evententity.h @@ -2,9 +2,11 @@ #pragma once +#include #include #include #include +#include #include "time/time.h" @@ -12,7 +14,6 @@ namespace openage::event { class Event; class EventLoop; -class EventHandler; /** * Every Object in the gameworld that wants to be targeted by events or as diff --git a/libopenage/event/eventhandler.cpp b/libopenage/event/eventhandler.cpp index 1e7575e3b1..162b38cc71 100644 --- a/libopenage/event/eventhandler.cpp +++ b/libopenage/event/eventhandler.cpp @@ -1,12 +1,7 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #include "eventhandler.h" -#include "event.h" -#include "evententity.h" - -#include "../log/log.h" - namespace openage::event { @@ -20,29 +15,24 @@ const std::string &EventHandler::id() { } -DependencyEventHandler::DependencyEventHandler(const std::string &name) - : +DependencyEventHandler::DependencyEventHandler(const std::string &name) : EventHandler(name, EventHandler::trigger_type::DEPENDENCY) {} -DependencyImmediatelyEventHandler::DependencyImmediatelyEventHandler(const std::string &name) - : +DependencyImmediatelyEventHandler::DependencyImmediatelyEventHandler(const std::string &name) : EventHandler(name, EventHandler::trigger_type::DEPENDENCY_IMMEDIATELY) {} -TriggerEventHandler::TriggerEventHandler(const std::string &name) - : +TriggerEventHandler::TriggerEventHandler(const std::string &name) : EventHandler(name, EventHandler::trigger_type::TRIGGER) {} -RepeatEventHandler::RepeatEventHandler(const std::string &name) - : +RepeatEventHandler::RepeatEventHandler(const std::string &name) : EventHandler(name, EventHandler::trigger_type::REPEAT) {} -OnceEventHandler::OnceEventHandler(const std::string &name) - : +OnceEventHandler::OnceEventHandler(const std::string &name) : EventHandler(name, EventHandler::trigger_type::ONCE) {} -} // openage::event +} // namespace openage::event diff --git a/libopenage/event/eventhandler.h b/libopenage/event/eventhandler.h index 5f022a025c..598aa290b7 100644 --- a/libopenage/event/eventhandler.h +++ b/libopenage/event/eventhandler.h @@ -2,14 +2,15 @@ #pragma once -#include "../time/time.h" - - #include +#include #include #include #include #include +#include + +#include "time/time.h" namespace openage::event { diff --git a/libopenage/event/eventqueue.cpp b/libopenage/event/eventqueue.cpp index 1d13997c6a..83a96e6e16 100644 --- a/libopenage/event/eventqueue.cpp +++ b/libopenage/event/eventqueue.cpp @@ -2,14 +2,19 @@ #include "eventqueue.h" -#include +#include +#include #include -#include "event.h" -#include "eventhandler.h" -#include "evententity.h" -#include "../log/log.h" -#include "../util/compiler.h" +#include "log/message.h" + +#include "event/event.h" +#include "event/evententity.h" +#include "event/eventhandler.h" +#include "event/eventstore.h" +#include "log/log.h" +#include "time/time.h" +#include "util/fixed_point.h" namespace openage::event { @@ -19,22 +24,20 @@ std::shared_ptr EventQueue::create_event(const std::shared_ptr &state, const time::time_t &reference_time, const EventHandler::param_map ¶ms) { - auto event = std::make_shared(trgt, cls, params); cls->setup_event(event, state); - switch(cls->type) { + switch (cls->type) { case EventHandler::trigger_type::DEPENDENCY: case EventHandler::trigger_type::REPEAT: case EventHandler::trigger_type::ONCE: event->set_time(event->get_eventhandler() - ->predict_invoke_time(trgt, state, reference_time)); + ->predict_invoke_time(trgt, state, reference_time)); if (event->get_time() == std::numeric_limits::min()) { log::log(DBG << "Queue: ignoring insertion of event " - << event->get_eventhandler()->id() << - " because no execution was scheduled."); + << event->get_eventhandler()->id() << " because no execution was scheduled."); return {}; } @@ -46,12 +49,11 @@ std::shared_ptr EventQueue::create_event(const std::shared_ptrget_eventhandler()->id() << - " into queue to be executed at t=" << event->get_time()); + log::log(DBG << "Queue: inserting event " << event->get_eventhandler()->id() << " into queue to be executed at t=" << event->get_time()); // store the event // or enqueue it for execution - switch(event->get_eventhandler()->type) { + switch (event->get_eventhandler()->type) { case EventHandler::trigger_type::DEPENDENCY: this->dependency_events.insert(event); break; @@ -74,15 +76,13 @@ std::shared_ptr EventQueue::create_event(const std::shared_ptr &event, const time::time_t &changed_at) { - const time::time_t event_previous_changed = event->get_last_changed(); // Has the event already been fired in this round? @@ -94,8 +94,8 @@ void EventQueue::add_change(const std::shared_ptr &event, // Is the new change dated _before_ the old one? if (changed_at < it->time) { log::log(DBG << "Queue: adjusting time in change queue: moving event of " - << event->get_eventhandler()->id() - << " to earlier time"); + << event->get_eventhandler()->id() + << " to earlier time"); // Save the element Change change = *it; @@ -109,24 +109,24 @@ void EventQueue::add_change(const std::shared_ptr &event, else { // this change is to be ignored log::log(DBG << "Queue: skipping change for " << event->get_eventhandler()->id() - << " at " << changed_at - << " because there was already an earlier one at t=" << it->time); + << " at " << changed_at + << " because there was already an earlier one at t=" << it->time); } } else { // the change was not in the to be changed list this->changes->emplace(event, changed_at); log::log(DBG << "Queue: inserting change for event from " - << event->get_eventhandler()->id() - << " to be applied at t=" << changed_at); + << event->get_eventhandler()->id() + << " to be applied at t=" << changed_at); } } else { // the event has been triggered in this round already, so skip it this time this->future_changes->emplace(event, changed_at); log::log(DBG << "Queue: ignoring change at t=" << changed_at - << " for event for handler " << event->get_eventhandler()->id() - << " because it's already processed as change at t=" << event_previous_changed); + << " for event for handler " << event->get_eventhandler()->id() + << " because it's already processed as change at t=" << event_previous_changed); } event->set_last_changed(changed_at); @@ -198,22 +198,22 @@ void EventQueue::swap_changesets() { EventQueue::Change::Change(const std::shared_ptr &evnt, - time::time_t time) - : + time::time_t time) : time{std::move(time)}, evnt{evnt}, hash{evnt->hash()} {} -size_t EventQueue::Change::Equal::operator()(const Change& left, - const Change& right) const { +size_t EventQueue::Change::Equal::operator()(const Change &left, + const Change &right) const { auto left_evnt = left.evnt.lock(); auto right_evnt = right.evnt.lock(); if (left_evnt && right_evnt) { if (left_evnt->get_eventhandler()->id() == right_evnt->get_eventhandler()->id()) { return true; } - } else { + } + else { return false; } diff --git a/libopenage/event/eventqueue.h b/libopenage/event/eventqueue.h index a6e47ff57a..6cc12c93cb 100644 --- a/libopenage/event/eventqueue.h +++ b/libopenage/event/eventqueue.h @@ -2,26 +2,26 @@ #pragma once +#include #include #include -#include "eventhandler.h" -#include "eventstore.h" -#include "../time/time.h" +#include "event/eventhandler.h" +#include "event/eventstore.h" +#include "time/time.h" namespace openage::event { class Event; -class EventLoop; class EventEntity; +class State; /** * The core event handler for execution and execution dependencies. */ class EventQueue final { public: - class Change { public: Change(const std::shared_ptr &evnt, @@ -33,15 +33,15 @@ class EventQueue final { class Hasher { public: - size_t operator ()(const Change& e) const { + size_t operator()(const Change &e) const { return e.hash; } }; class Equal { public: - size_t operator ()(const Change& left, - const Change& right) const; + size_t operator()(const Change &left, + const Change &right) const; }; }; @@ -122,7 +122,6 @@ class EventQueue final { void swap_changesets(); private: - // Implement double buffering around changesets, that we do not run into deadlocks // those point to the `changeset_A` and `changeset_B`. change_set *changes; @@ -154,4 +153,4 @@ class EventQueue final { EventStore event_queue; }; -} // openage::event +} // namespace openage::event diff --git a/libopenage/event/eventstore.cpp b/libopenage/event/eventstore.cpp index dfa8dcc35d..7e88b4e638 100644 --- a/libopenage/event/eventstore.cpp +++ b/libopenage/event/eventstore.cpp @@ -3,11 +3,12 @@ #include "eventstore.h" #include +#include +#include -#include "event.h" +#include "log/message.h" -#include "../util/compiler.h" -#include "../error/error.h" +#include "error/error.h" namespace openage::event { @@ -39,8 +40,8 @@ std::shared_ptr EventStore::pop() { throw Error{ERR << "inconsistent: prev_heap=" << heap_s << " prev_map=" << evnt_s}; } - //ENSURE(this->heap.size() == this->events.size(), - // "heap and event set are inconsistent 1"); + //ENSURE(this->heap.size() == this->events.size(), + // "heap and event set are inconsistent 1"); return event; } @@ -107,18 +108,16 @@ std::vector> EventStore::get_sorted_events() const { std::back_inserter(ret), [](const auto &elem) { return elem.first; - } - ); + }); std::sort( std::begin(ret), std::end(ret), [](const auto &a, const auto &b) { return *a < *b; - } - ); + }); return ret; } -} // openage::event +} // namespace openage::event diff --git a/libopenage/event/eventstore.h b/libopenage/event/eventstore.h index f557303f24..be67743fb0 100644 --- a/libopenage/event/eventstore.h +++ b/libopenage/event/eventstore.h @@ -1,17 +1,18 @@ -// Copyright 2018-2018 the openage authors. See copying.md for legal info. +// Copyright 2018-2023 the openage authors. See copying.md for legal info. #pragma once -#include "../datastructure/pairing_heap.h" -#include "../util/misc.h" - +#include #include #include +#include +#include "datastructure/pairing_heap.h" +#include "event/event.h" +#include "util/misc.h" -namespace openage::event { -class Event; +namespace openage::event { /** * Sorted storage for events. @@ -19,7 +20,6 @@ class Event; */ class EventStore { public: - // TODO: don't store a double-sharedpointer. // instead, use the event-sharedpointer directly. using heap_t = datastructure::PairingHeap, @@ -48,4 +48,4 @@ class EventStore { }; -} // openage::event +} // namespace openage::event diff --git a/libopenage/event/tests.cpp b/libopenage/event/tests.cpp index 33e7e61c5a..779f1ed09f 100644 --- a/libopenage/event/tests.cpp +++ b/libopenage/event/tests.cpp @@ -1,17 +1,25 @@ // Copyright 2017-2023 the openage authors. See copying.md for legal info. +#include #include #include -#include +#include +#include +#include +#include #include #include "log/log.h" +#include "log/message.h" #include "testing/testing.h" -#include "event.h" -#include "event_loop.h" -#include "evententity.h" -#include "state.h" +#include "event/event.h" +#include "event/event_loop.h" +#include "event/evententity.h" +#include "event/eventhandler.h" +#include "event/state.h" +#include "time/time.h" +#include "util/fixed_point.h" namespace openage::event::tests { @@ -127,8 +135,8 @@ class TestEventHandler : public EventHandler { } time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, - const std::shared_ptr & /*state*/, - const time::time_t &at) override { + const std::shared_ptr & /*state*/, + const time::time_t &at) override { return at + time::time_t::from_double(2); } }; @@ -159,8 +167,8 @@ class TestEventHandlerTwo : public EventHandler { } time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, - const std::shared_ptr & /*state*/, - const time::time_t &at) override { + const std::shared_ptr & /*state*/, + const time::time_t &at) override { // TODO recalculate a hit time return at + time::time_t::from_double(1); } @@ -196,8 +204,8 @@ class EventTypeTestClass : public EventHandler { } time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, - const std::shared_ptr & /*state*/, - const time::time_t &at) override { + const std::shared_ptr & /*state*/, + const time::time_t &at) override { switch (this->type) { case EventHandler::trigger_type::DEPENDENCY: // Execute 1 after the change (usually it is neccessary to recalculate a collision @@ -567,8 +575,8 @@ void eventtrigger() { } time::time_t predict_invoke_time(const std::shared_ptr & /*target*/, - const std::shared_ptr & /*state*/, - const time::time_t &at) override { + const std::shared_ptr & /*state*/, + const time::time_t &at) override { return at; } }; diff --git a/libopenage/gamestate/activity/activity.h b/libopenage/gamestate/activity/activity.h index 1a52f2a337..f5be9d254a 100644 --- a/libopenage/gamestate/activity/activity.h +++ b/libopenage/gamestate/activity/activity.h @@ -2,12 +2,13 @@ #pragma once +#include #include - -#include "gamestate/activity/node.h" +#include namespace openage::gamestate::activity { +class Node; using activity_id = size_t; using activity_label = std::string; diff --git a/libopenage/gamestate/activity/end_node.cpp b/libopenage/gamestate/activity/end_node.cpp index dbb8bbebf4..01ecfebade 100644 --- a/libopenage/gamestate/activity/end_node.cpp +++ b/libopenage/gamestate/activity/end_node.cpp @@ -2,6 +2,9 @@ #include "end_node.h" +#include "error/error.h" +#include "log/message.h" + namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/end_node.h b/libopenage/gamestate/activity/end_node.h index 59500daaaf..c068f39475 100644 --- a/libopenage/gamestate/activity/end_node.h +++ b/libopenage/gamestate/activity/end_node.h @@ -5,6 +5,7 @@ #include #include "gamestate/activity/node.h" +#include "gamestate/activity/types.h" namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/event_node.cpp b/libopenage/gamestate/activity/event_node.cpp index 92f703641e..c4c8b3d44c 100644 --- a/libopenage/gamestate/activity/event_node.cpp +++ b/libopenage/gamestate/activity/event_node.cpp @@ -2,6 +2,8 @@ #include "event_node.h" +#include + namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/event_node.h b/libopenage/gamestate/activity/event_node.h index fedd987568..1ddde632ab 100644 --- a/libopenage/gamestate/activity/event_node.h +++ b/libopenage/gamestate/activity/event_node.h @@ -2,7 +2,16 @@ #pragma once +#include +#include +#include + +#include "error/error.h" +#include "log/message.h" + #include "gamestate/activity/node.h" +#include "gamestate/activity/types.h" +#include "time/time.h" namespace openage { diff --git a/libopenage/gamestate/activity/node.cpp b/libopenage/gamestate/activity/node.cpp index 62e924b8ea..5c7f007d74 100644 --- a/libopenage/gamestate/activity/node.cpp +++ b/libopenage/gamestate/activity/node.cpp @@ -2,6 +2,11 @@ #include "node.h" +#include + +#include "error/error.h" +#include "log/message.h" + namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/node.h b/libopenage/gamestate/activity/node.h index 5e8d173523..9a8edfcf9f 100644 --- a/libopenage/gamestate/activity/node.h +++ b/libopenage/gamestate/activity/node.h @@ -2,10 +2,12 @@ #pragma once +#include #include +#include #include +#include -#include "time/time.h" #include "gamestate/activity/types.h" diff --git a/libopenage/gamestate/activity/start_node.cpp b/libopenage/gamestate/activity/start_node.cpp index adf2ffc912..8686fc2f4e 100644 --- a/libopenage/gamestate/activity/start_node.cpp +++ b/libopenage/gamestate/activity/start_node.cpp @@ -2,6 +2,9 @@ #include "start_node.h" +#include +#include + namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/start_node.h b/libopenage/gamestate/activity/start_node.h index d69616b70d..bec65e6ea7 100644 --- a/libopenage/gamestate/activity/start_node.h +++ b/libopenage/gamestate/activity/start_node.h @@ -5,6 +5,7 @@ #include #include "gamestate/activity/node.h" +#include "gamestate/activity/types.h" namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/task_node.cpp b/libopenage/gamestate/activity/task_node.cpp index 36033cb954..d1a9386812 100644 --- a/libopenage/gamestate/activity/task_node.cpp +++ b/libopenage/gamestate/activity/task_node.cpp @@ -2,6 +2,9 @@ #include "task_node.h" +#include +#include + namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/task_node.h b/libopenage/gamestate/activity/task_node.h index 050770594c..c8329ab051 100644 --- a/libopenage/gamestate/activity/task_node.h +++ b/libopenage/gamestate/activity/task_node.h @@ -5,7 +5,12 @@ #include #include +#include "error/error.h" +#include "log/message.h" + #include "gamestate/activity/node.h" +#include "gamestate/activity/types.h" +#include "time/time.h" namespace openage::gamestate { diff --git a/libopenage/gamestate/activity/task_system_node.cpp b/libopenage/gamestate/activity/task_system_node.cpp index 97c0f461d4..158bcc41b2 100644 --- a/libopenage/gamestate/activity/task_system_node.cpp +++ b/libopenage/gamestate/activity/task_system_node.cpp @@ -2,6 +2,9 @@ #include "task_system_node.h" +#include +#include + namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/task_system_node.h b/libopenage/gamestate/activity/task_system_node.h index 3619a042fa..834ee321f7 100644 --- a/libopenage/gamestate/activity/task_system_node.h +++ b/libopenage/gamestate/activity/task_system_node.h @@ -5,6 +5,7 @@ #include #include "gamestate/activity/node.h" +#include "gamestate/activity/types.h" #include "gamestate/system/types.h" diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp index e93c1d92dc..d19e552030 100644 --- a/libopenage/gamestate/activity/tests.cpp +++ b/libopenage/gamestate/activity/tests.cpp @@ -1,19 +1,28 @@ // Copyright 2023-2023 the openage authors. See copying.md for legal info. -#include "log/log.h" +#include +#include +#include +#include -#include "event/event.h" #include "event/event_loop.h" #include "event/evententity.h" #include "event/eventhandler.h" #include "event/state.h" +#include "error/error.h" +#include "log/log.h" +#include "log/message.h" + #include "gamestate/activity/end_node.h" #include "gamestate/activity/event_node.h" +#include "gamestate/activity/node.h" #include "gamestate/activity/start_node.h" #include "gamestate/activity/task_node.h" #include "gamestate/activity/types.h" #include "gamestate/activity/xor_node.h" +#include "time/time.h" + namespace openage::gamestate::tests { @@ -90,8 +99,8 @@ class TestActivityHandler : public event::OnceEventHandler { } time::time_t predict_invoke_time(const std::shared_ptr & /* target */, - const std::shared_ptr & /* state */, - const time::time_t &at) override { + const std::shared_ptr & /* state */, + const time::time_t &at) override { return at; } }; diff --git a/libopenage/gamestate/activity/xor_node.cpp b/libopenage/gamestate/activity/xor_node.cpp index cd5a744197..ca703c288a 100644 --- a/libopenage/gamestate/activity/xor_node.cpp +++ b/libopenage/gamestate/activity/xor_node.cpp @@ -2,6 +2,8 @@ #include "xor_node.h" +#include + namespace openage::gamestate::activity { diff --git a/libopenage/gamestate/activity/xor_node.h b/libopenage/gamestate/activity/xor_node.h index a4a7b6837c..c8996e606b 100644 --- a/libopenage/gamestate/activity/xor_node.h +++ b/libopenage/gamestate/activity/xor_node.h @@ -2,7 +2,16 @@ #pragma once +#include +#include +#include + +#include "error/error.h" +#include "log/message.h" + #include "gamestate/activity/node.h" +#include "gamestate/activity/types.h" +#include "time/time.h" namespace openage::gamestate { diff --git a/libopenage/gamestate/api/ability.cpp b/libopenage/gamestate/api/ability.cpp index bd56040a2f..49ecfad48f 100644 --- a/libopenage/gamestate/api/ability.cpp +++ b/libopenage/gamestate/api/ability.cpp @@ -2,8 +2,17 @@ #include "ability.h" +#include +#include +#include +#include + +#include + +#include "datastructure/constexpr_map.h" #include "gamestate/api/definitions.h" + namespace openage::gamestate::api { bool APIAbility::is_ability(const nyan::Object &obj) { diff --git a/libopenage/gamestate/api/animation.cpp b/libopenage/gamestate/api/animation.cpp index 2dd79a4ca0..86cbfe6014 100644 --- a/libopenage/gamestate/api/animation.cpp +++ b/libopenage/gamestate/api/animation.cpp @@ -2,8 +2,11 @@ #include "animation.h" +#include + #include "gamestate/api/util.h" + namespace openage::gamestate::api { bool APIAnimation::is_animation(nyan::Object &obj) { diff --git a/libopenage/gamestate/api/animation.h b/libopenage/gamestate/api/animation.h index 0d9c235464..a002749786 100644 --- a/libopenage/gamestate/api/animation.h +++ b/libopenage/gamestate/api/animation.h @@ -2,6 +2,9 @@ #pragma once +#include +#include + #include diff --git a/libopenage/gamestate/api/definitions.h b/libopenage/gamestate/api/definitions.h index c884e32386..d06553c89e 100644 --- a/libopenage/gamestate/api/definitions.h +++ b/libopenage/gamestate/api/definitions.h @@ -2,11 +2,15 @@ #pragma once +#include +#include + #include #include "datastructure/constexpr_map.h" #include "gamestate/api/types.h" + namespace openage::gamestate::api { /** Maps internal ability types to nyan API values **/ diff --git a/libopenage/gamestate/api/patch.cpp b/libopenage/gamestate/api/patch.cpp index 9f4d2a0453..05c2f67d5f 100644 --- a/libopenage/gamestate/api/patch.cpp +++ b/libopenage/gamestate/api/patch.cpp @@ -2,6 +2,14 @@ #include "patch.h" +#include +#include +#include +#include + +#include + +#include "datastructure/constexpr_map.h" #include "gamestate/api/definitions.h" diff --git a/libopenage/gamestate/api/player_setup.cpp b/libopenage/gamestate/api/player_setup.cpp index 956662a374..4d01cf327d 100644 --- a/libopenage/gamestate/api/player_setup.cpp +++ b/libopenage/gamestate/api/player_setup.cpp @@ -2,6 +2,13 @@ #include "player_setup.h" +#include +#include +#include +#include + +#include + namespace openage::gamestate::api { diff --git a/libopenage/gamestate/api/player_setup.h b/libopenage/gamestate/api/player_setup.h index b4bbe4c7dc..f3e23fbc81 100644 --- a/libopenage/gamestate/api/player_setup.h +++ b/libopenage/gamestate/api/player_setup.h @@ -2,6 +2,8 @@ #pragma once +#include + #include diff --git a/libopenage/gamestate/api/property.cpp b/libopenage/gamestate/api/property.cpp index cbd12c4da1..fb4458dd5b 100644 --- a/libopenage/gamestate/api/property.cpp +++ b/libopenage/gamestate/api/property.cpp @@ -2,6 +2,13 @@ #include "property.h" +#include +#include +#include +#include + +#include + namespace openage::gamestate::api { diff --git a/libopenage/gamestate/api/property.h b/libopenage/gamestate/api/property.h index e3346ab635..702abd2088 100644 --- a/libopenage/gamestate/api/property.h +++ b/libopenage/gamestate/api/property.h @@ -2,6 +2,8 @@ #pragma once +#include + #include diff --git a/libopenage/gamestate/api/sound.cpp b/libopenage/gamestate/api/sound.cpp index e978842c93..0af5fafcf4 100644 --- a/libopenage/gamestate/api/sound.cpp +++ b/libopenage/gamestate/api/sound.cpp @@ -2,6 +2,10 @@ #include "sound.h" +#include + +#include + #include "gamestate/api/util.h" diff --git a/libopenage/gamestate/api/sound.h b/libopenage/gamestate/api/sound.h index 6237ac3812..c45fc7c397 100644 --- a/libopenage/gamestate/api/sound.h +++ b/libopenage/gamestate/api/sound.h @@ -2,6 +2,9 @@ #pragma once +#include +#include + #include diff --git a/libopenage/gamestate/api/util.cpp b/libopenage/gamestate/api/util.cpp index 907befcb73..7e59ebf9bd 100644 --- a/libopenage/gamestate/api/util.cpp +++ b/libopenage/gamestate/api/util.cpp @@ -2,9 +2,13 @@ #include "util.h" -#include "error/error.h" - #include +#include + +#include + +#include "error/error.h" +#include "log/message.h" namespace openage::gamestate::api { diff --git a/libopenage/gamestate/api/util.h b/libopenage/gamestate/api/util.h index 297705830a..cad829a205 100644 --- a/libopenage/gamestate/api/util.h +++ b/libopenage/gamestate/api/util.h @@ -2,6 +2,8 @@ #pragma once +#include + #include diff --git a/libopenage/gamestate/component/api/idle.h b/libopenage/gamestate/component/api/idle.h index cf13a1c40c..f9d42e5330 100644 --- a/libopenage/gamestate/component/api/idle.h +++ b/libopenage/gamestate/component/api/idle.h @@ -3,6 +3,7 @@ #pragma once #include "gamestate/component/api_component.h" +#include "gamestate/component/types.h" namespace openage::gamestate::component { diff --git a/libopenage/gamestate/component/api/live.cpp b/libopenage/gamestate/component/api/live.cpp index 9a740b7c00..b9e0d0cd57 100644 --- a/libopenage/gamestate/component/api/live.cpp +++ b/libopenage/gamestate/component/api/live.cpp @@ -2,6 +2,11 @@ #include "live.h" +#include + +#include "curve/discrete.h" +#include "curve/iterator.h" +#include "curve/map_filter_iterator.h" #include "gamestate/component/types.h" diff --git a/libopenage/gamestate/component/api/live.h b/libopenage/gamestate/component/api/live.h index a132e4b045..dd3d8cb0a0 100644 --- a/libopenage/gamestate/component/api/live.h +++ b/libopenage/gamestate/component/api/live.h @@ -2,11 +2,16 @@ #pragma once +#include +#include + #include #include "curve/map.h" -#include "curve/segmented.h" #include "gamestate/component/api_component.h" +#include "gamestate/component/types.h" +#include "time/time.h" + namespace openage::gamestate::component { class Live : public APIComponent { diff --git a/libopenage/gamestate/component/api/move.h b/libopenage/gamestate/component/api/move.h index 77bc538f05..1cc6d65cf7 100644 --- a/libopenage/gamestate/component/api/move.h +++ b/libopenage/gamestate/component/api/move.h @@ -3,6 +3,8 @@ #pragma once #include "gamestate/component/api_component.h" +#include "gamestate/component/types.h" + namespace openage::gamestate::component { diff --git a/libopenage/gamestate/component/api/turn.h b/libopenage/gamestate/component/api/turn.h index ef84bbf20c..881bb05de9 100644 --- a/libopenage/gamestate/component/api/turn.h +++ b/libopenage/gamestate/component/api/turn.h @@ -5,6 +5,8 @@ #include #include "gamestate/component/api_component.h" +#include "gamestate/component/types.h" + namespace openage::gamestate::component { diff --git a/libopenage/gamestate/component/api_component.cpp b/libopenage/gamestate/component/api_component.cpp index f8cb9e44b7..e61ccabfae 100644 --- a/libopenage/gamestate/component/api_component.cpp +++ b/libopenage/gamestate/component/api_component.cpp @@ -2,11 +2,12 @@ #include "api_component.h" + namespace openage::gamestate::component { APIComponent::APIComponent(const std::shared_ptr &loop, nyan::Object &ability, - const time_t &creation_time, + const time::time_t &creation_time, const bool enabled) : ability{ability}, enabled(loop, 0) { diff --git a/libopenage/gamestate/component/api_component.h b/libopenage/gamestate/component/api_component.h index af0681fa24..e994103c5e 100644 --- a/libopenage/gamestate/component/api_component.h +++ b/libopenage/gamestate/component/api_component.h @@ -2,10 +2,14 @@ #pragma once +#include + #include #include "curve/discrete.h" #include "gamestate/component/base_component.h" +#include "time/time.h" + namespace openage { @@ -31,7 +35,7 @@ class APIComponent : public Component { */ APIComponent(const std::shared_ptr &loop, nyan::Object &ability, - const time_t &creation_time, + const time::time_t &creation_time, bool enabled = true); /** diff --git a/libopenage/gamestate/component/internal/activity.cpp b/libopenage/gamestate/component/internal/activity.cpp index 1562d798a2..540ad1e532 100644 --- a/libopenage/gamestate/component/internal/activity.cpp +++ b/libopenage/gamestate/component/internal/activity.cpp @@ -2,7 +2,9 @@ #include "activity.h" +#include "event/event.h" #include "gamestate/activity/activity.h" +#include "gamestate/component/internal/activity.h" namespace openage::gamestate::component { diff --git a/libopenage/gamestate/component/internal/activity.h b/libopenage/gamestate/component/internal/activity.h index 7cd3782e3a..a52a97bd57 100644 --- a/libopenage/gamestate/component/internal/activity.h +++ b/libopenage/gamestate/component/internal/activity.h @@ -7,6 +7,9 @@ #include "curve/discrete.h" #include "gamestate/component/internal_component.h" +#include "gamestate/component/types.h" +#include "time/time.h" + namespace openage { diff --git a/libopenage/gamestate/component/internal/command_queue.cpp b/libopenage/gamestate/component/internal/command_queue.cpp index 354b644ecf..6896f63cdf 100644 --- a/libopenage/gamestate/component/internal/command_queue.cpp +++ b/libopenage/gamestate/component/internal/command_queue.cpp @@ -2,6 +2,8 @@ #include "command_queue.h" +#include + #include "gamestate/component/types.h" diff --git a/libopenage/gamestate/component/internal/command_queue.h b/libopenage/gamestate/component/internal/command_queue.h index 7009781720..6c16d5cbc7 100644 --- a/libopenage/gamestate/component/internal/command_queue.h +++ b/libopenage/gamestate/component/internal/command_queue.h @@ -2,9 +2,14 @@ #pragma once +#include + #include "curve/queue.h" #include "gamestate/component/internal/commands/base_command.h" #include "gamestate/component/internal_component.h" +#include "gamestate/component/types.h" +#include "time/time.h" + namespace openage { diff --git a/libopenage/gamestate/component/internal/commands/custom.h b/libopenage/gamestate/component/internal/commands/custom.h index 5e8764b17a..2cc8a6d910 100644 --- a/libopenage/gamestate/component/internal/commands/custom.h +++ b/libopenage/gamestate/component/internal/commands/custom.h @@ -5,6 +5,7 @@ #include #include "gamestate/component/internal/commands/base_command.h" +#include "gamestate/component/internal/commands/types.h" namespace openage::gamestate::component::command { diff --git a/libopenage/gamestate/component/internal/commands/idle.h b/libopenage/gamestate/component/internal/commands/idle.h index c0fb2d6ca2..9870ad1ce5 100644 --- a/libopenage/gamestate/component/internal/commands/idle.h +++ b/libopenage/gamestate/component/internal/commands/idle.h @@ -3,6 +3,7 @@ #pragma once #include "gamestate/component/internal/commands/base_command.h" +#include "gamestate/component/internal/commands/types.h" namespace openage::gamestate::component::command { diff --git a/libopenage/gamestate/component/internal/commands/move.h b/libopenage/gamestate/component/internal/commands/move.h index 713e1ebe55..0516dddb46 100644 --- a/libopenage/gamestate/component/internal/commands/move.h +++ b/libopenage/gamestate/component/internal/commands/move.h @@ -4,6 +4,7 @@ #include "coord/phys.h" #include "gamestate/component/internal/commands/base_command.h" +#include "gamestate/component/internal/commands/types.h" namespace openage::gamestate::component::command { diff --git a/libopenage/gamestate/component/internal/ownership.h b/libopenage/gamestate/component/internal/ownership.h index e244b69106..b28448a557 100644 --- a/libopenage/gamestate/component/internal/ownership.h +++ b/libopenage/gamestate/component/internal/ownership.h @@ -2,10 +2,21 @@ #pragma once +#include +#include + #include "curve/discrete.h" #include "gamestate/component/internal_component.h" +#include "gamestate/component/types.h" +#include "time/time.h" + + +namespace openage { +namespace event { +class EventLoop; +} -namespace openage::gamestate::component { +namespace gamestate::component { using ownership_id_t = uint64_t; @@ -53,4 +64,5 @@ class Ownership : public InternalComponent { curve::Discrete owner; }; -} // namespace openage::gamestate::component +} // namespace gamestate::component +} // namespace openage diff --git a/libopenage/gamestate/component/internal/position.cpp b/libopenage/gamestate/component/internal/position.cpp index 280880ed39..61611382cf 100644 --- a/libopenage/gamestate/component/internal/position.cpp +++ b/libopenage/gamestate/component/internal/position.cpp @@ -4,6 +4,7 @@ #include "gamestate/component/types.h" #include "gamestate/definitions.h" +#include "util/fixed_point.h" namespace openage::gamestate::component { diff --git a/libopenage/gamestate/component/internal/position.h b/libopenage/gamestate/component/internal/position.h index a2c5e19a4b..af0502fbae 100644 --- a/libopenage/gamestate/component/internal/position.h +++ b/libopenage/gamestate/component/internal/position.h @@ -2,15 +2,23 @@ #pragma once +#include #include #include "coord/phys.h" #include "curve/continuous.h" #include "curve/segmented.h" #include "gamestate/component/internal_component.h" -#include "util/fixed_point.h" +#include "gamestate/component/types.h" +#include "time/time.h" -namespace openage::gamestate::component { + +namespace openage { +namespace event { +class EventLoop; +} + +namespace gamestate::component { class Position : public InternalComponent { public: @@ -85,4 +93,5 @@ class Position : public InternalComponent { curve::Segmented angle; }; -} // namespace openage::gamestate::component +} // namespace gamestate::component +} // namespace openage diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index 5f93af4f5f..b8794e80a3 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -2,7 +2,17 @@ #include "entity_factory.h" -#include "event/state.h" +#include +#include +#include +#include +#include + +#include "error/error.h" + +#include "curve/discrete.h" +#include "curve/queue.h" +#include "event/event_loop.h" #include "gamestate/activity/activity.h" #include "gamestate/activity/end_node.h" #include "gamestate/activity/event_node.h" @@ -17,10 +27,16 @@ #include "gamestate/component/internal/command_queue.h" #include "gamestate/component/internal/ownership.h" #include "gamestate/component/internal/position.h" +#include "gamestate/component/types.h" #include "gamestate/game_entity.h" #include "gamestate/game_state.h" #include "gamestate/manager.h" +#include "gamestate/system/types.h" +#include "log/message.h" #include "renderer/render_factory.h" +#include "time/time.h" +#include "util/fixed_point.h" + namespace openage::gamestate { diff --git a/libopenage/gamestate/entity_factory.h b/libopenage/gamestate/entity_factory.h index 075f275603..3960a08e53 100644 --- a/libopenage/gamestate/entity_factory.h +++ b/libopenage/gamestate/entity_factory.h @@ -7,9 +7,9 @@ #include -#include "coord/phys.h" #include "gamestate/types.h" + namespace openage { namespace event { @@ -69,9 +69,9 @@ class EntityFactory { * @param nyan_entity fqon of the GameEntity data in the nyan database. */ void init_components(const std::shared_ptr &loop, - const std::shared_ptr &state, - const std::shared_ptr &entity, - const nyan::fqon_t &nyan_entity); + const std::shared_ptr &state, + const std::shared_ptr &entity, + const nyan::fqon_t &nyan_entity); /** * Get a unique ID for creating a game entity. diff --git a/libopenage/gamestate/event/process_command.h b/libopenage/gamestate/event/process_command.h index 94ed02ad9f..8b940dd616 100644 --- a/libopenage/gamestate/event/process_command.h +++ b/libopenage/gamestate/event/process_command.h @@ -2,8 +2,11 @@ #pragma once -#include "event/evententity.h" +#include + #include "event/eventhandler.h" +#include "time/time.h" + namespace openage { @@ -34,8 +37,8 @@ class ProcessCommandHandler : public openage::event::OnceEventHandler { const param_map ¶ms) override; time::time_t predict_invoke_time(const std::shared_ptr &target, - const std::shared_ptr &state, - const time::time_t &at) override; + const std::shared_ptr &state, + const time::time_t &at) override; }; diff --git a/libopenage/gamestate/event/send_command.cpp b/libopenage/gamestate/event/send_command.cpp index 5d4fb2977b..8530d6340d 100644 --- a/libopenage/gamestate/event/send_command.cpp +++ b/libopenage/gamestate/event/send_command.cpp @@ -2,13 +2,29 @@ #include "send_command.h" +#include + +#include "coord/phys.h" #include "gamestate/component/internal/command_queue.h" #include "gamestate/component/internal/commands/idle.h" #include "gamestate/component/internal/commands/move.h" +#include "gamestate/component/types.h" #include "gamestate/game_entity.h" #include "gamestate/game_state.h" +#include "gamestate/types.h" + + +namespace openage::gamestate { +namespace component { +class CommandQueue; + +namespace command { +class IdleCommand; +class MoveCommand; +} // namespace command +} // namespace component -namespace openage::gamestate::event { +namespace event { Commander::Commander(const std::shared_ptr &loop) : openage::event::EventEntity{loop} { @@ -65,9 +81,10 @@ void SendCommandHandler::invoke(openage::event::EventLoop & /* loop */, } time::time_t SendCommandHandler::predict_invoke_time(const std::shared_ptr & /* target */, - const std::shared_ptr & /* state */, - const time::time_t &at) { + const std::shared_ptr & /* state */, + const time::time_t &at) { return at; } -} // namespace openage::gamestate::event +} // namespace event +} // namespace openage::gamestate diff --git a/libopenage/gamestate/event/send_command.h b/libopenage/gamestate/event/send_command.h index eafc95a8f7..75d59a8b05 100644 --- a/libopenage/gamestate/event/send_command.h +++ b/libopenage/gamestate/event/send_command.h @@ -2,15 +2,19 @@ #pragma once +#include +#include +#include + #include "event/evententity.h" #include "event/eventhandler.h" + namespace openage { namespace event { class EventLoop; class Event; -class EventEntity; class State; } // namespace event @@ -44,8 +48,8 @@ class SendCommandHandler : public openage::event::OnceEventHandler { const param_map ¶ms) override; time::time_t predict_invoke_time(const std::shared_ptr &target, - const std::shared_ptr &state, - const time::time_t &at) override; + const std::shared_ptr &state, + const time::time_t &at) override; }; } // namespace gamestate::event diff --git a/libopenage/gamestate/event/spawn_entity.cpp b/libopenage/gamestate/event/spawn_entity.cpp index 85848ec188..5389cf775c 100644 --- a/libopenage/gamestate/event/spawn_entity.cpp +++ b/libopenage/gamestate/event/spawn_entity.cpp @@ -2,16 +2,24 @@ #include "spawn_entity.h" +#include +#include +#include + +#include + #include "coord/phys.h" #include "gamestate/component/internal/activity.h" #include "gamestate/component/internal/command_queue.h" #include "gamestate/component/internal/ownership.h" #include "gamestate/component/internal/position.h" +#include "gamestate/component/types.h" #include "gamestate/definitions.h" #include "gamestate/entity_factory.h" #include "gamestate/game_entity.h" #include "gamestate/game_state.h" #include "gamestate/manager.h" +#include "gamestate/types.h" // TODO: Testing #include "assets/mod_manager.h" @@ -135,8 +143,8 @@ void SpawnEntityHandler::invoke(openage::event::EventLoop & /* loop */, } time::time_t SpawnEntityHandler::predict_invoke_time(const std::shared_ptr & /* target */, - const std::shared_ptr & /* state */, - const time::time_t &at) { + const std::shared_ptr & /* state */, + const time::time_t &at) { return at; } diff --git a/libopenage/gamestate/event/spawn_entity.h b/libopenage/gamestate/event/spawn_entity.h index 6d12dd001c..12f99378b3 100644 --- a/libopenage/gamestate/event/spawn_entity.h +++ b/libopenage/gamestate/event/spawn_entity.h @@ -2,15 +2,20 @@ #pragma once +#include +#include +#include + #include "event/evententity.h" #include "event/eventhandler.h" +#include "time/time.h" + namespace openage { namespace event { class EventLoop; class Event; -class EventEntity; class State; } // namespace event @@ -82,8 +87,8 @@ class SpawnEntityHandler : public openage::event::OnceEventHandler { * (if other events have already been calculated before that). */ time::time_t predict_invoke_time(const std::shared_ptr &target, - const std::shared_ptr &state, - const time::time_t &at) override; + const std::shared_ptr &state, + const time::time_t &at) override; private: /** diff --git a/libopenage/gamestate/event/wait.h b/libopenage/gamestate/event/wait.h index 43686d6575..2c0d9732f2 100644 --- a/libopenage/gamestate/event/wait.h +++ b/libopenage/gamestate/event/wait.h @@ -2,8 +2,11 @@ #pragma once -#include "event/evententity.h" +#include + #include "event/eventhandler.h" +#include "time/time.h" + namespace openage { @@ -34,8 +37,8 @@ class WaitHandler : public openage::event::OnceEventHandler { const param_map ¶ms) override; time::time_t predict_invoke_time(const std::shared_ptr &target, - const std::shared_ptr &state, - const time::time_t &at) override; + const std::shared_ptr &state, + const time::time_t &at) override; }; } // namespace gamestate::event } // namespace openage diff --git a/libopenage/gamestate/game.cpp b/libopenage/gamestate/game.cpp index c7c1a22a47..b496ae7e1e 100644 --- a/libopenage/gamestate/game.cpp +++ b/libopenage/gamestate/game.cpp @@ -2,13 +2,19 @@ #include "game.h" -#include -#include +#include + +#include + +#include "log/log.h" +#include "log/message.h" #include "assets/mod_manager.h" +#include "assets/modpack.h" #include "gamestate/game_state.h" #include "gamestate/universe.h" -#include "time/time_loop.h" +#include "util/path.h" +#include "util/strings.h" namespace openage::gamestate { diff --git a/libopenage/gamestate/game.h b/libopenage/gamestate/game.h index b5849e8c8d..9c9eb1def4 100644 --- a/libopenage/gamestate/game.h +++ b/libopenage/gamestate/game.h @@ -3,8 +3,7 @@ #pragma once #include - -#include "util/path.h" +#include namespace nyan { class Database; @@ -24,6 +23,10 @@ namespace renderer { class RenderFactory; } +namespace util { +class Path; +} + namespace gamestate { class GameState; class Universe; diff --git a/libopenage/gamestate/game_entity.h b/libopenage/gamestate/game_entity.h index d345d59d5d..a79f167e7f 100644 --- a/libopenage/gamestate/game_entity.h +++ b/libopenage/gamestate/game_entity.h @@ -3,12 +3,13 @@ #pragma once #include +#include #include -#include "coord/phys.h" -#include "time/time.h" #include "gamestate/component/types.h" #include "gamestate/types.h" +#include "time/time.h" + namespace openage { diff --git a/libopenage/gamestate/game_state.cpp b/libopenage/gamestate/game_state.cpp index b39c8c7ab3..759b638840 100644 --- a/libopenage/gamestate/game_state.cpp +++ b/libopenage/gamestate/game_state.cpp @@ -2,10 +2,14 @@ #include "game_state.h" -#include +#include + +#include "error/error.h" +#include "log/log.h" #include "gamestate/game_entity.h" + namespace openage::gamestate { GameState::GameState(const std::shared_ptr &db, diff --git a/libopenage/gamestate/game_state.h b/libopenage/gamestate/game_state.h index 8ea365a9e3..c091303548 100644 --- a/libopenage/gamestate/game_state.h +++ b/libopenage/gamestate/game_state.h @@ -2,11 +2,13 @@ #pragma once +#include #include #include "event/state.h" #include "gamestate/types.h" + namespace nyan { class Database; class View; @@ -18,6 +20,10 @@ namespace assets { class ModManager; } +namespace event { +class EventLoop; +} + namespace gamestate { class GameEntity; diff --git a/libopenage/gamestate/manager.cpp b/libopenage/gamestate/manager.cpp index e71027d97d..9c8f6c1899 100644 --- a/libopenage/gamestate/manager.cpp +++ b/libopenage/gamestate/manager.cpp @@ -3,9 +3,9 @@ #include "manager.h" #include "log/log.h" +#include "log/message.h" #include "gamestate/component/internal/command_queue.h" -#include "gamestate/component/types.h" #include "gamestate/game_entity.h" #include "gamestate/system/activity.h" diff --git a/libopenage/gamestate/manager.h b/libopenage/gamestate/manager.h index 723297a866..6131c94bd2 100644 --- a/libopenage/gamestate/manager.h +++ b/libopenage/gamestate/manager.h @@ -2,12 +2,20 @@ #pragma once +#include #include +#include #include "event/evententity.h" +#include "time/time.h" -namespace openage::gamestate { +namespace openage { +namespace event { +class EventLoop; +} // namespace event + +namespace gamestate { class GameState; class GameEntity; @@ -31,4 +39,5 @@ class GameEntityManager : public openage::event::EventEntity { std::shared_ptr game_entity; }; -} // namespace openage::gamestate +} // namespace gamestate +} // namespace openage diff --git a/libopenage/gamestate/system/activity.cpp b/libopenage/gamestate/system/activity.cpp index 57b225819d..600a0e4622 100644 --- a/libopenage/gamestate/system/activity.cpp +++ b/libopenage/gamestate/system/activity.cpp @@ -2,19 +2,26 @@ #include "activity.h" -#include "log/log.h" +#include +#include +#include + +#include "error/error.h" +#include "log/message.h" -#include "gamestate/activity/activity.h" #include "gamestate/activity/event_node.h" +#include "gamestate/activity/node.h" #include "gamestate/activity/start_node.h" #include "gamestate/activity/task_node.h" #include "gamestate/activity/task_system_node.h" +#include "gamestate/activity/types.h" #include "gamestate/activity/xor_node.h" #include "gamestate/component/internal/activity.h" +#include "gamestate/component/types.h" #include "gamestate/game_entity.h" - #include "gamestate/system/idle.h" #include "gamestate/system/move.h" +#include "util/fixed_point.h" namespace openage::gamestate::system { @@ -101,8 +108,8 @@ void Activity::advance(const std::shared_ptr &entity, } const time::time_t Activity::handle_subsystem(const std::shared_ptr &entity, - const time::time_t &start_time, - system_id_t system_id) { + const time::time_t &start_time, + system_id_t system_id) { switch (system_id) { case system_id_t::IDLE: return Idle::idle(entity, start_time); diff --git a/libopenage/gamestate/system/idle.cpp b/libopenage/gamestate/system/idle.cpp index 6c6d1137c0..3db9f5cb5f 100644 --- a/libopenage/gamestate/system/idle.cpp +++ b/libopenage/gamestate/system/idle.cpp @@ -2,19 +2,25 @@ #include "idle.h" +#include + +#include "error/error.h" #include "log/log.h" +#include "log/message.h" #include "gamestate/api/ability.h" #include "gamestate/api/animation.h" #include "gamestate/api/property.h" +#include "gamestate/api/types.h" #include "gamestate/component/api/idle.h" +#include "gamestate/component/types.h" #include "gamestate/game_entity.h" namespace openage::gamestate::system { const time::time_t Idle::idle(const std::shared_ptr &entity, - const time::time_t &start_time) { + const time::time_t &start_time) { if (not entity->has_component(component::component_t::IDLE)) [[unlikely]] { throw Error{ERR << "Entity " << entity->get_id() << " has no idle component."}; } diff --git a/libopenage/gamestate/system/move.cpp b/libopenage/gamestate/system/move.cpp index ede7619c8e..3ebd02085b 100644 --- a/libopenage/gamestate/system/move.cpp +++ b/libopenage/gamestate/system/move.cpp @@ -2,22 +2,34 @@ #include "move.h" +#include +#include + +#include + #include "log/log.h" +#include "log/message.h" +#include "coord/phys.h" +#include "curve/continuous.h" +#include "curve/segmented.h" #include "gamestate/api/ability.h" #include "gamestate/api/animation.h" #include "gamestate/api/property.h" +#include "gamestate/api/types.h" #include "gamestate/component/api/move.h" #include "gamestate/component/api/turn.h" #include "gamestate/component/internal/command_queue.h" #include "gamestate/component/internal/commands/move.h" #include "gamestate/component/internal/position.h" +#include "gamestate/component/types.h" #include "gamestate/game_entity.h" +#include "util/fixed_point.h" namespace openage::gamestate::system { const time::time_t Move::move_command(const std::shared_ptr &entity, - const time::time_t &start_time) { + const time::time_t &start_time) { auto command_queue = std::dynamic_pointer_cast( entity->get_component(component::component_t::COMMANDQUEUE)); auto command = std::dynamic_pointer_cast( @@ -33,8 +45,8 @@ const time::time_t Move::move_command(const std::shared_ptr &entity, - const coord::phys3 &destination, - const time::time_t &start_time) { + const coord::phys3 &destination, + const time::time_t &start_time) { if (not entity->has_component(component::component_t::MOVE)) [[unlikely]] { log::log(WARN << "Entity " << entity->get_id() << " has no move component."); return time::time_t::from_int(0); diff --git a/libopenage/gamestate/terrain.cpp b/libopenage/gamestate/terrain.cpp index 2564347eef..247b457251 100644 --- a/libopenage/gamestate/terrain.cpp +++ b/libopenage/gamestate/terrain.cpp @@ -2,6 +2,10 @@ #include "terrain.h" +#include +#include +#include + #include "renderer/stages/terrain/terrain_render_entity.h" namespace openage::gamestate { diff --git a/libopenage/gamestate/terrain.h b/libopenage/gamestate/terrain.h index 690ea3bd03..1e1aef9846 100644 --- a/libopenage/gamestate/terrain.h +++ b/libopenage/gamestate/terrain.h @@ -3,7 +3,7 @@ #pragma once #include -#include +#include #include #include "util/vector.h" diff --git a/libopenage/gamestate/world.cpp b/libopenage/gamestate/world.cpp index 85785e0a67..d47ce1ce0b 100644 --- a/libopenage/gamestate/world.cpp +++ b/libopenage/gamestate/world.cpp @@ -2,9 +2,8 @@ #include "world.h" -#include "log/log.h" - -#include +#include +#include #include "gamestate/game_entity.h" #include "gamestate/game_state.h" diff --git a/libopenage/gamestate/world.h b/libopenage/gamestate/world.h index 2ba27cfd02..2fd825143b 100644 --- a/libopenage/gamestate/world.h +++ b/libopenage/gamestate/world.h @@ -2,7 +2,8 @@ #pragma once -#include "util/path.h" +#include + namespace openage { @@ -11,7 +12,6 @@ class RenderFactory; } namespace gamestate { -class GameEntity; class GameState; /** diff --git a/libopenage/gui/integration/private/gui_log.cpp b/libopenage/gui/integration/private/gui_log.cpp index d4387b1147..481374f9de 100644 --- a/libopenage/gui/integration/private/gui_log.cpp +++ b/libopenage/gui/integration/private/gui_log.cpp @@ -1,10 +1,12 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "../../integration/private/gui_log.h" #include #include "../../../log/log.h" +#include "log/message.h" + namespace openage { namespace gui { @@ -36,9 +38,8 @@ void gui_log(QtMsgType type, const QMessageLogContext &context, const QString &m log::MessageBuilder builder{ context.file != nullptr ? context.file : "", static_cast(context.line), - context.function != nullptr ? context.function : "", - msg_lvl - }; + context.function != nullptr ? context.function : "", + msg_lvl}; // TODO: maybe it's not UTF-8 // TODO: Qt should become a LogSource @@ -48,4 +49,5 @@ void gui_log(QtMsgType type, const QMessageLogContext &context, const QString &m abort(); } -}} // namespace openage::gui +} // namespace gui +} // namespace openage diff --git a/libopenage/input/controller/game/controller.cpp b/libopenage/input/controller/game/controller.cpp index 582c0a045b..d12f7e1135 100644 --- a/libopenage/input/controller/game/controller.cpp +++ b/libopenage/input/controller/game/controller.cpp @@ -2,6 +2,7 @@ #include "controller.h" +#include "event/event_loop.h" #include "event/evententity.h" #include "event/state.h" #include "gamestate/component/internal/commands/types.h" diff --git a/libopenage/log/file_logsink.cpp b/libopenage/log/file_logsink.cpp index 340d50f7f0..53e309f9be 100644 --- a/libopenage/log/file_logsink.cpp +++ b/libopenage/log/file_logsink.cpp @@ -1,18 +1,19 @@ -// Copyright 2015-2020 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "file_logsink.h" -#include #include +#include -#include "message.h" -#include "logsource.h" +#include "log/level.h" +#include "log/logsource.h" +#include "log/message.h" +#include "util/enum.h" namespace openage::log { -FileSink::FileSink(const char *filename, bool append) - : +FileSink::FileSink(const char *filename, bool append) : outfile{filename, std::ios_base::out | (append ? std::ios_base::app : std::ios_base::trunc)} {} diff --git a/libopenage/log/file_logsink.h b/libopenage/log/file_logsink.h index a4cdd4c4d8..ab651d57e6 100644 --- a/libopenage/log/file_logsink.h +++ b/libopenage/log/file_logsink.h @@ -1,14 +1,15 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once -#include #include #include "logsink.h" namespace openage { namespace log { +class LogSource; +struct message; class FileSink : public LogSink { public: @@ -20,4 +21,5 @@ class FileSink : public LogSink { std::ofstream outfile; }; -}} // namespace openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/level.h b/libopenage/log/level.h index 817311083c..ef7322ec63 100644 --- a/libopenage/log/level.h +++ b/libopenage/log/level.h @@ -1,8 +1,9 @@ -// Copyright 2015-2019 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once // pxd: from libopenage.util.enum cimport Enum, EnumValue +#include "../util/compiler.h" #include "../util/enum.h" @@ -48,26 +49,27 @@ struct OAAPI level : util::Enum { // initializes the level to an internal UNDEFINED value. level(); - #ifdef __MINGW32__ - // Do not try to optimize these out even if it seems they are not used. - // Namely MIN that is not used within the library. - #define NOOPTIMIZE __attribute__((__used__)) - #else - #define NOOPTIMIZE - #endif // _win32 +#ifdef __MINGW32__ +// Do not try to optimize these out even if it seems they are not used. +// Namely MIN that is not used within the library. +#define NOOPTIMIZE __attribute__((__used__)) +#else +#define NOOPTIMIZE +#endif // _win32 - static constexpr level_value MIN NOOPTIMIZE {{"min loglevel", -1000}, "5"}; + static constexpr level_value MIN NOOPTIMIZE{{"min loglevel", -1000}, "5"}; - static constexpr level_value spam NOOPTIMIZE {{"SPAM", -100}, ""}; - static constexpr level_value dbg NOOPTIMIZE {{"DBG", -20}, ""}; - static constexpr level_value info NOOPTIMIZE {{"INFO", 0}, ""}; - static constexpr level_value warn NOOPTIMIZE {{"WARN", 100}, "33"}; - static constexpr level_value err NOOPTIMIZE {{"ERR", 200}, "31;1"}; - static constexpr level_value crit NOOPTIMIZE {{"CRIT", 500}, "31;1;47"}; + static constexpr level_value spam NOOPTIMIZE{{"SPAM", -100}, ""}; + static constexpr level_value dbg NOOPTIMIZE{{"DBG", -20}, ""}; + static constexpr level_value info NOOPTIMIZE{{"INFO", 0}, ""}; + static constexpr level_value warn NOOPTIMIZE{{"WARN", 100}, "33"}; + static constexpr level_value err NOOPTIMIZE{{"ERR", 200}, "31;1"}; + static constexpr level_value crit NOOPTIMIZE{{"CRIT", 500}, "31;1;47"}; - static constexpr level_value MAX NOOPTIMIZE {{"max loglevel", 1000}, "5"}; + static constexpr level_value MAX NOOPTIMIZE{{"max loglevel", 1000}, "5"}; - #undef NOOPTIMIZE +#undef NOOPTIMIZE }; -}} // namespace openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/log.cpp b/libopenage/log/log.cpp index 49d610bba3..fa2300d1e3 100644 --- a/libopenage/log/log.cpp +++ b/libopenage/log/log.cpp @@ -1,10 +1,10 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "log.h" -#include "message.h" -#include "named_logsource.h" -#include "stdout_logsink.h" +#include "log/named_logsource.h" +#include "log/stdout_logsink.h" + namespace openage { namespace log { @@ -20,4 +20,5 @@ void set_level(level lvl) { } -}} // namespace openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/log.h b/libopenage/log/log.h index 936a7735ab..4fe103d420 100644 --- a/libopenage/log/log.h +++ b/libopenage/log/log.h @@ -1,10 +1,12 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once // pxd: from libopenage.log.level cimport level -#include "level.h" -#include "message.h" +#include "../util/compiler.h" +#include "./level.h" +#include "./message.h" + namespace openage { namespace log { @@ -27,4 +29,5 @@ void log(const message &msg); OAAPI void set_level(level lvl); -}} // openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/logsink.h b/libopenage/log/logsink.h index 3fef9b4a5f..6edfa7890c 100644 --- a/libopenage/log/logsink.h +++ b/libopenage/log/logsink.h @@ -1,14 +1,16 @@ -// Copyright 2015-2020 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once #include #include -#include "level.h" +#include "../util/compiler.h" +#include "./level.h" -namespace openage::log { +namespace openage::log { +struct message; /** * Abstract base for classes that - in one way or an other - print log messages. @@ -51,9 +53,9 @@ class OAAPI LogSinkList { public: static LogSinkList &instance(); - LogSinkList(LogSinkList const&) = delete; + LogSinkList(LogSinkList const &) = delete; - void operator=(LogSinkList const&) = delete; + void operator=(LogSinkList const &) = delete; void log(const message &msg, class LogSource *source) const; @@ -68,7 +70,7 @@ class OAAPI LogSinkList { private: LogSinkList(); - std::list sinks; + std::list sinks; mutable std::mutex sinks_mutex; diff --git a/libopenage/log/logsource.cpp b/libopenage/log/logsource.cpp index 4c7c9d90d8..e2491e89cd 100644 --- a/libopenage/log/logsource.cpp +++ b/libopenage/log/logsource.cpp @@ -1,18 +1,20 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "logsource.h" -#include "../util/compiler.h" +#include +#include + +#include "log/logsink.h" +#include "log/stdout_logsink.h" +#include "util/compiler.h" -#include "logsink.h" -#include "stdout_logsink.h" namespace openage { namespace log { -LogSource::LogSource() - : +LogSource::LogSource() : logger_id{LogSource::get_unique_logger_id()} {} @@ -33,4 +35,5 @@ size_t LogSource::get_unique_logger_id() { } -}} // namespace openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/logsource.h b/libopenage/log/logsource.h index 21d793bdd0..fa0268ddf5 100644 --- a/libopenage/log/logsource.h +++ b/libopenage/log/logsource.h @@ -1,17 +1,21 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once +#include + // pxd: from libcpp.string cimport string #include // pxd: from libopenage.log.message cimport message #include "message.h" +#include "../util/compiler.h" + namespace openage { namespace log { - +struct message; /** * Any class that wants to provide .log() shall inherit from this. @@ -52,4 +56,5 @@ class OAAPI LogSource { }; -}} // namespace openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/message.cpp b/libopenage/log/message.cpp index fd7e8616cb..f3be4c4068 100644 --- a/libopenage/log/message.cpp +++ b/libopenage/log/message.cpp @@ -1,14 +1,19 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "message.h" +#include #include +#include #include +#include + +#include "log/level.h" +#include "util/enum.h" +#include "util/stringformatter.h" +#include "util/thread_id.h" +#include "util/timing.h" -#include "../util/timing.h" -#include "../util/compiler.h" -#include "../util/thread_id.h" -#include "../util/strings.h" namespace openage { namespace log { @@ -35,20 +40,18 @@ void message::init_with_metadata_copy(const std::string &filename, const std::st MessageBuilder::MessageBuilder(const char *filename, unsigned lineno, const char *functionname, - level lvl) - : + level lvl) : StringFormatter{msg.text} { + this->msg.filename = filename; + this->msg.lineno = lineno; + this->msg.functionname = functionname; + this->msg.lvl = lvl; - this->msg.filename = filename; - this->msg.lineno = lineno; - this->msg.functionname = functionname; - this->msg.lvl = lvl; - - this->msg.init(); - } + this->msg.init(); +} -std::ostream &operator <<(std::ostream &os, const message &msg) { +std::ostream &operator<<(std::ostream &os, const message &msg) { os << "\x1b[" << msg.lvl->colorcode << "m" << std::setw(4) << msg.lvl->name << "\x1b[m "; os << msg.filename << ":" << msg.lineno << " "; os << "(" << msg.functionname; @@ -59,4 +62,5 @@ std::ostream &operator <<(std::ostream &os, const message &msg) { } -}} // namespace openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/message.h b/libopenage/log/message.h index 194320355a..09b888a9ac 100644 --- a/libopenage/log/message.h +++ b/libopenage/log/message.h @@ -1,20 +1,25 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once +#include +#include + // pxd: from libc.stdint cimport int64_t #include // pxd: from libcpp.string cimport string #include -#include "config.h" +#include "../util/compiler.h" #include "../util/constexpr.h" #include "../util/stringformatter.h" +#include "config.h" #include "logsink.h" // pxd: from libopenage.log.level cimport level #include "level.h" + #if defined(__GNUC__) #define OPENAGE_FUNC_NAME __PRETTY_FUNCTION__ #elif defined(_MSC_VER) @@ -27,7 +32,9 @@ namespace openage { // forward-declaration for use in 'friend' declaration below. -namespace error { class Error; } +namespace error { +class Error; +} namespace log { @@ -107,7 +114,7 @@ struct OAAPI message { /** * prints message to a stream (with color codes and everything!) */ -std::ostream &operator <<(std::ostream &os, const message &msg); +std::ostream &operator<<(std::ostream &os, const message &msg); /** * Wrapper around a log message that allows appending to the message with operator <<. @@ -125,8 +132,7 @@ class OAAPI MessageBuilder : public util::StringFormatter { * @param functionname (fully qualified) function name (OPENAGE_FUNC_NAME). * @param lvl loglevel of the message. Also required for exception messages. */ - MessageBuilder(const char *filename, unsigned lineno, const char *functionname, - level lvl=level::info); + MessageBuilder(const char *filename, unsigned lineno, const char *functionname, level lvl = level::info); // auto-convert to message inline operator const message &() const { @@ -159,16 +165,16 @@ class OAAPI MessageBuilder : public util::StringFormatter { // for use with existing log::level objects #define MSG_LVLOBJ(LVLOBJ) \ ::openage::log::MessageBuilder( \ - ::openage::util::constexpr_::strip_prefix( \ - __FILE__, \ - ::openage::config::buildsystem_sourcefile_dir), \ - __LINE__, \ - OPENAGE_FUNC_NAME, \ - LVLOBJ) + ::openage::util::constexpr_::strip_prefix( \ + __FILE__, \ + ::openage::config::buildsystem_sourcefile_dir), \ + __LINE__, \ + OPENAGE_FUNC_NAME, \ + LVLOBJ) // for use with log::level literals (auto-prefixes full qualification) -#define MSG(LVL) MSG_LVLOBJ(::openage::log::level:: LVL) +#define MSG(LVL) MSG_LVLOBJ(::openage::log::level::LVL) // some convenience shorteners for MSG(...). @@ -180,4 +186,5 @@ class OAAPI MessageBuilder : public util::StringFormatter { #define CRIT MSG(crit) -}} // openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/named_logsource.h b/libopenage/log/named_logsource.h index fe5e980123..c1839bd0cc 100644 --- a/libopenage/log/named_logsource.h +++ b/libopenage/log/named_logsource.h @@ -1,4 +1,4 @@ -// Copyright 2015-2019 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once @@ -8,6 +8,8 @@ // pxd: from libopenage.log.logsource cimport LogSource #include "logsource.h" +#include "../util/compiler.h" + namespace openage { namespace log { @@ -37,4 +39,5 @@ class OAAPI NamedLogSource : public LogSource { NamedLogSource &general_source(); -}} // namespace openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/stdout_logsink.cpp b/libopenage/log/stdout_logsink.cpp index 978bae1579..647af51c49 100644 --- a/libopenage/log/stdout_logsink.cpp +++ b/libopenage/log/stdout_logsink.cpp @@ -1,16 +1,22 @@ -// Copyright 2015-2019 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "stdout_logsink.h" -#include #include +#include +#include #ifdef _MSC_VER #define WIN32_LEAN_AND_MEAN #include #endif -#include "named_logsource.h" +#include "log/level.h" +#include "log/logsource.h" +#include "log/message.h" +#include "log/named_logsource.h" +#include "util/enum.h" + namespace openage::log { @@ -36,7 +42,7 @@ void enable_ansi_color_codes() { } -} +} // namespace StdOutSink::StdOutSink() { @@ -46,14 +52,19 @@ StdOutSink::StdOutSink() { void StdOutSink::output_log_message(const message &msg, LogSource *source) { // print log level (width 4) - std::cout << "\x1b[" << msg.lvl->colorcode << "m" << std::setw(4) << msg.lvl->name << "\x1b[m" " "; + std::cout << "\x1b[" << msg.lvl->colorcode << "m" << std::setw(4) << msg.lvl->name << "\x1b[m" + " "; if (msg.thread_id != 0) { - std::cout << "\x1b[32m" "[T" << msg.thread_id << "]\x1b[m "; + std::cout << "\x1b[32m" + "[T" + << msg.thread_id << "]\x1b[m "; } if (source != &general_source()) { - std::cout << "\x1b[36m" "[" << source->logsource_name() << "]\x1b[m "; + std::cout << "\x1b[36m" + "[" + << source->logsource_name() << "]\x1b[m "; } std::cout << msg.text << std::endl; diff --git a/libopenage/log/stdout_logsink.h b/libopenage/log/stdout_logsink.h index cdf8c22637..b27a76175f 100644 --- a/libopenage/log/stdout_logsink.h +++ b/libopenage/log/stdout_logsink.h @@ -1,4 +1,4 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once @@ -6,7 +6,8 @@ namespace openage { namespace log { - +class LogSource; +struct message; /** * Simple logsink that prints messages to stdout (via std::cout). @@ -14,6 +15,7 @@ namespace log { class StdOutSink : public LogSink { public: StdOutSink(); + private: void output_log_message(const message &msg, LogSource *source) override; }; @@ -26,4 +28,5 @@ class StdOutSink : public LogSink { StdOutSink &global_stdoutsink(); -}} // namespace openage::log +} // namespace log +} // namespace openage diff --git a/libopenage/log/test.cpp b/libopenage/log/test.cpp index ae28caf7a2..1d7be48d0b 100644 --- a/libopenage/log/test.cpp +++ b/libopenage/log/test.cpp @@ -1,15 +1,15 @@ -// Copyright 2014-2019 the openage authors. See copying.md for legal info. +// Copyright 2014-2023 the openage authors. See copying.md for legal info. #include -#include +#include #include -#include -#include "log.h" -#include "logsource.h" -#include "logsink.h" +#include "log/logsink.h" +#include "log/logsource.h" +#include "log/message.h" +#include "util/stringformatter.h" +#include "util/strings.h" -#include "../util/strings.h" namespace openage::log::tests { @@ -24,9 +24,9 @@ class TestLogSource : public LogSource { class TestLogSink : public LogSink { public: - explicit TestLogSink(std::ostream &os) - : + explicit TestLogSink(std::ostream &os) : os{os} {} + private: std::ostream &os; @@ -62,4 +62,4 @@ void demo() { t1.join(); } -} // openage::log::tests +} // namespace openage::log::tests diff --git a/libopenage/main.cpp b/libopenage/main.cpp index 1d17727ea0..7c80689a25 100644 --- a/libopenage/main.cpp +++ b/libopenage/main.cpp @@ -2,11 +2,12 @@ #include "main.h" +#include + #include "cvar/cvar.h" #include "engine/engine.h" #include "util/timer.h" - namespace openage { /* diff --git a/libopenage/main.h b/libopenage/main.h index c45052fd0a..7eb3317711 100644 --- a/libopenage/main.h +++ b/libopenage/main.h @@ -12,6 +12,9 @@ // pxd: from libopenage.util.path cimport Path #include "util/path.h" +#include + +#include "util/compiler.h" namespace openage { diff --git a/libopenage/renderer/animation.h b/libopenage/renderer/animation.h index 860f245a21..aa5e27f8cf 100644 --- a/libopenage/renderer/animation.h +++ b/libopenage/renderer/animation.h @@ -2,8 +2,6 @@ #pragma once -#include - #include "renderer/resources/animation/animation_info.h" namespace openage::renderer { diff --git a/libopenage/renderer/camera/camera.cpp b/libopenage/renderer/camera/camera.cpp index aeaf45ffb7..7956e9fa70 100644 --- a/libopenage/renderer/camera/camera.cpp +++ b/libopenage/renderer/camera/camera.cpp @@ -2,11 +2,17 @@ #include "camera.h" +#include #include +#include +#include +#include "coord/pixel.h" +#include "coord/scene.h" #include "renderer/renderer.h" #include "renderer/resources/buffer_info.h" + namespace openage::renderer::camera { Camera::Camera(const std::shared_ptr &renderer, diff --git a/libopenage/renderer/camera/camera.h b/libopenage/renderer/camera/camera.h index c5d75beb5b..23cd649b82 100644 --- a/libopenage/renderer/camera/camera.h +++ b/libopenage/renderer/camera/camera.h @@ -2,6 +2,10 @@ #pragma once +#include +#include +#include + #include #include "coord/pixel.h" diff --git a/libopenage/renderer/demo/tests.cpp b/libopenage/renderer/demo/tests.cpp index 5deb3efd4c..3de072267c 100644 --- a/libopenage/renderer/demo/tests.cpp +++ b/libopenage/renderer/demo/tests.cpp @@ -3,6 +3,8 @@ #include "tests.h" #include "log/log.h" +#include "log/message.h" + #include "renderer/demo/demo_0.h" #include "renderer/demo/demo_1.h" #include "renderer/demo/demo_2.h" @@ -10,6 +12,7 @@ #include "renderer/demo/demo_4.h" #include "renderer/demo/demo_5.h" + namespace openage::renderer::tests { void renderer_demo(int demo_id, const util::Path &path) { diff --git a/libopenage/renderer/demo/tests.h b/libopenage/renderer/demo/tests.h index 0de8596c05..05568ae8fe 100644 --- a/libopenage/renderer/demo/tests.h +++ b/libopenage/renderer/demo/tests.h @@ -4,12 +4,17 @@ #include "../../util/compiler.h" // pxd: from libopenage.util.path cimport Path -#include "../../util/path.h" -namespace openage::renderer::tests { +namespace openage { +namespace util { +class Path; +} // namespace util + +namespace renderer::tests { // pxd: void renderer_demo(int demo_id, Path path) except + OAAPI void renderer_demo(int demo_id, const util::Path &path); -} // namespace openage::renderer::tests +} // namespace renderer::tests +} // namespace openage diff --git a/libopenage/renderer/gui/integration/private/gui_log.cpp b/libopenage/renderer/gui/integration/private/gui_log.cpp index 2cd4cef01c..f2556d214f 100644 --- a/libopenage/renderer/gui/integration/private/gui_log.cpp +++ b/libopenage/renderer/gui/integration/private/gui_log.cpp @@ -1,10 +1,12 @@ -// Copyright 2015-2022 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "../../integration/private/gui_log.h" #include #include "log/log.h" +#include "log/message.h" + namespace openage::renderer::gui { @@ -35,9 +37,8 @@ void gui_log(QtMsgType type, const QMessageLogContext &context, const QString &m log::MessageBuilder builder{ context.file != nullptr ? context.file : "", static_cast(context.line), - context.function != nullptr ? context.function : "", - msg_lvl - }; + context.function != nullptr ? context.function : "", + msg_lvl}; // TODO: maybe it's not UTF-8 // TODO: Qt should become a LogSource diff --git a/libopenage/renderer/opengl/debug.cpp b/libopenage/renderer/opengl/debug.cpp index 93fc41a2da..65bcfb9365 100644 --- a/libopenage/renderer/opengl/debug.cpp +++ b/libopenage/renderer/opengl/debug.cpp @@ -5,6 +5,7 @@ #include #include "log/log.h" +#include "log/message.h" namespace openage::renderer::opengl { diff --git a/libopenage/renderer/render_factory.h b/libopenage/renderer/render_factory.h index 13f7b50304..84f2b6eb39 100644 --- a/libopenage/renderer/render_factory.h +++ b/libopenage/renderer/render_factory.h @@ -5,8 +5,6 @@ #include namespace openage::renderer { -class Renderer; - namespace terrain { class TerrainRenderer; class TerrainRenderEntity; diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 6432d34b0a..8dec3dec2f 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -3,6 +3,8 @@ #pragma once #include +#include +#include #include diff --git a/libopenage/renderer/resources/animation/animation_info.h b/libopenage/renderer/resources/animation/animation_info.h index 3c8b666923..be53acb180 100644 --- a/libopenage/renderer/resources/animation/animation_info.h +++ b/libopenage/renderer/resources/animation/animation_info.h @@ -2,16 +2,16 @@ #pragma once +#include +#include #include #include "renderer/resources/animation/layer_info.h" -#include "renderer/resources/texture_info.h" -namespace openage::renderer { -class Texture2d; - -namespace resources { +namespace openage::renderer::resources { +class Texture2dInfo; +class Texture2dSubInfo; /** * Contains information about a 2D animation. The animation data can be @@ -96,5 +96,4 @@ class Animation2dInfo { std::vector layers; }; -} // namespace resources -} // namespace openage::renderer +} // namespace openage::renderer::resources diff --git a/libopenage/renderer/resources/animation/layer_info.cpp b/libopenage/renderer/resources/animation/layer_info.cpp index 47c23fe990..49c95104a4 100644 --- a/libopenage/renderer/resources/animation/layer_info.cpp +++ b/libopenage/renderer/resources/animation/layer_info.cpp @@ -3,9 +3,12 @@ #include "layer_info.h" #include +#include #include "renderer/resources/animation/angle_info.h" #include "renderer/resources/frame_timing.h" +#include "time/time.h" + namespace openage::renderer::resources { diff --git a/libopenage/renderer/resources/assets/asset_manager.cpp b/libopenage/renderer/resources/assets/asset_manager.cpp index 54952b6cc2..284b2d27fb 100644 --- a/libopenage/renderer/resources/assets/asset_manager.cpp +++ b/libopenage/renderer/resources/assets/asset_manager.cpp @@ -2,9 +2,10 @@ #include "asset_manager.h" +#include "error/error.h" #include "log/log.h" +#include "log/message.h" -#include "renderer/renderer.h" #include "renderer/resources/animation/animation_info.h" #include "renderer/resources/assets/cache.h" #include "renderer/resources/assets/texture_manager.h" @@ -18,9 +19,9 @@ #include "renderer/resources/terrain/blendpattern_info.h" #include "renderer/resources/terrain/blendtable_info.h" #include "renderer/resources/terrain/terrain_info.h" -#include "renderer/resources/texture_data.h" #include "renderer/resources/texture_info.h" + namespace openage::renderer::resources { AssetManager::AssetManager(const std::shared_ptr &renderer, diff --git a/libopenage/renderer/resources/assets/asset_manager.h b/libopenage/renderer/resources/assets/asset_manager.h index 0ce9706cef..0d45e473dd 100644 --- a/libopenage/renderer/resources/assets/asset_manager.h +++ b/libopenage/renderer/resources/assets/asset_manager.h @@ -5,10 +5,11 @@ #include #include #include -#include +#include #include "util/path.h" + namespace openage::renderer { class Renderer; diff --git a/libopenage/renderer/resources/assets/cache.cpp b/libopenage/renderer/resources/assets/cache.cpp index 6ace9b0a00..5ab4db4229 100644 --- a/libopenage/renderer/resources/assets/cache.cpp +++ b/libopenage/renderer/resources/assets/cache.cpp @@ -2,47 +2,44 @@ #include "cache.h" -#include "renderer/resources/animation/animation_info.h" -#include "renderer/resources/palette_info.h" -#include "renderer/resources/terrain/blendpattern_info.h" -#include "renderer/resources/terrain/blendtable_info.h" -#include "renderer/resources/terrain/terrain_info.h" +#include "util/path.h" + namespace openage::renderer::resources { const std::shared_ptr &AssetCache::get_animation(const util::Path &path) { auto flat_path = path.resolve_native_path(); - return this->loaded_animations[flat_path]; + return this->loaded_animations.at(flat_path); } const std::shared_ptr &AssetCache::get_blpattern(const util::Path &path) { auto flat_path = path.resolve_native_path(); - return this->loaded_blpatterns[flat_path]; + return this->loaded_blpatterns.at(flat_path); } const std::shared_ptr &AssetCache::get_bltable(const util::Path &path) { auto flat_path = path.resolve_native_path(); - return this->loaded_bltables[flat_path]; + return this->loaded_bltables.at(flat_path); } const std::shared_ptr &AssetCache::get_palette(const util::Path &path) { auto flat_path = path.resolve_native_path(); - return this->loaded_palettes[flat_path]; + return this->loaded_palettes.at(flat_path); } const std::shared_ptr &AssetCache::get_terrain(const util::Path &path) { auto flat_path = path.resolve_native_path(); - return this->loaded_terrains[flat_path]; + return this->loaded_terrains.at(flat_path); } const std::shared_ptr &AssetCache::get_texture(const util::Path &path) { auto flat_path = path.resolve_native_path(); - return this->loaded_textures[flat_path]; + return this->loaded_textures.at(flat_path); } diff --git a/libopenage/renderer/resources/assets/cache.h b/libopenage/renderer/resources/assets/cache.h index c3cc9a3d66..504a02bfd2 100644 --- a/libopenage/renderer/resources/assets/cache.h +++ b/libopenage/renderer/resources/assets/cache.h @@ -6,11 +6,12 @@ #include #include -#include "util/path.h" -namespace openage::renderer { - -namespace resources { +namespace openage { +namespace util { +class Path; +} +namespace renderer::resources { class Animation2dInfo; class BlendPatternInfo; class BlendTableInfo; @@ -128,5 +129,5 @@ class AssetCache { texture_cache_t loaded_textures; }; -} // namespace resources -} // namespace openage::renderer +} // namespace renderer::resources +} // namespace openage diff --git a/libopenage/renderer/resources/assets/texture_manager.cpp b/libopenage/renderer/resources/assets/texture_manager.cpp index cc22d63e31..5cf3b2db0c 100644 --- a/libopenage/renderer/resources/assets/texture_manager.cpp +++ b/libopenage/renderer/resources/assets/texture_manager.cpp @@ -3,9 +3,9 @@ #include "texture_manager.h" #include "renderer/renderer.h" -#include "renderer/resources/parser/parse_texture.h" #include "renderer/resources/texture_data.h" + namespace openage::renderer::resources { TextureManager::TextureManager(const std::shared_ptr &renderer) : diff --git a/libopenage/renderer/resources/assets/texture_manager.h b/libopenage/renderer/resources/assets/texture_manager.h index 20e30973bb..73c8318c6b 100644 --- a/libopenage/renderer/resources/assets/texture_manager.h +++ b/libopenage/renderer/resources/assets/texture_manager.h @@ -6,9 +6,11 @@ #include #include #include +#include #include "util/path.h" + namespace openage::renderer { class Renderer; class Texture2d; diff --git a/libopenage/renderer/resources/buffer_info.h b/libopenage/renderer/resources/buffer_info.h index 7bdee91752..b654c3090f 100644 --- a/libopenage/renderer/resources/buffer_info.h +++ b/libopenage/renderer/resources/buffer_info.h @@ -2,11 +2,15 @@ #pragma once +#include +#include #include +#include #include #include "datastructure/constexpr_map.h" + namespace openage::renderer::resources { /** diff --git a/libopenage/renderer/resources/frame_timing.cpp b/libopenage/renderer/resources/frame_timing.cpp index d734b49447..c29801827a 100644 --- a/libopenage/renderer/resources/frame_timing.cpp +++ b/libopenage/renderer/resources/frame_timing.cpp @@ -2,6 +2,14 @@ #include "frame_timing.h" +#include +#include + +#include "error/error.h" +#include "log/message.h" + +#include "util/fixed_point.h" + namespace openage::renderer::resources { diff --git a/libopenage/renderer/resources/mesh_data.cpp b/libopenage/renderer/resources/mesh_data.cpp index e7aed09a87..74fb72b629 100644 --- a/libopenage/renderer/resources/mesh_data.cpp +++ b/libopenage/renderer/resources/mesh_data.cpp @@ -4,10 +4,13 @@ #include #include +#include #include -#include "../../error/error.h" -#include "../../datastructure/constexpr_map.h" +#include "error/error.h" +#include "log/message.h" + +#include "datastructure/constexpr_map.h" namespace openage::renderer::resources { @@ -16,15 +19,13 @@ static constexpr auto vin_size = datastructure::create_const_map( std::make_pair(vertex_input_t::F32, 1), std::make_pair(vertex_input_t::V2F32, 2), std::make_pair(vertex_input_t::V3F32, 3), - std::make_pair(vertex_input_t::M3F32, 9) -); + std::make_pair(vertex_input_t::M3F32, 9)); size_t vertex_input_size(vertex_input_t in) { return vin_size.get(in); @@ -34,18 +35,13 @@ size_t vertex_input_count(vertex_input_t in) { return vin_count.get(in); } -VertexInputInfo::VertexInputInfo(std::vector inputs, vertex_layout_t layout, vertex_primitive_t primitive) - : inputs(std::move(inputs)) - , layout(layout) - , primitive(primitive) {} +VertexInputInfo::VertexInputInfo(std::vector inputs, vertex_layout_t layout, vertex_primitive_t primitive) : + inputs(std::move(inputs)), layout(layout), primitive(primitive) {} -VertexInputInfo::VertexInputInfo(std::vector inputs, vertex_layout_t layout, vertex_primitive_t primitive, index_t index_type) - : inputs(std::move(inputs)) - , layout(layout) - , primitive(primitive) - , index_type(index_type) {} +VertexInputInfo::VertexInputInfo(std::vector inputs, vertex_layout_t layout, vertex_primitive_t primitive, index_t index_type) : + inputs(std::move(inputs)), layout(layout), primitive(primitive), index_type(index_type) {} -void VertexInputInfo::add_shader_input_map(std::unordered_map&& in_map) { +void VertexInputInfo::add_shader_input_map(std::unordered_map &&in_map) { for (auto mapping : in_map) { if (mapping.first >= this->inputs.size()) [[unlikely]] { throw Error(MSG(err) << "A shader input mapping is out-of-range, exceeding the available number of attributes."); @@ -67,7 +63,7 @@ const std::vector &VertexInputInfo::get_inputs() const { return this->inputs; } -std::optional> const& VertexInputInfo::get_shader_input_map() const { +std::optional> const &VertexInputInfo::get_shader_input_map() const { return this->shader_input_map; } @@ -83,21 +79,18 @@ std::optional VertexInputInfo::get_index_type() const { return this->index_type; } -MeshData::MeshData(const util::Path &/*path*/) { +MeshData::MeshData(const util::Path & /*path*/) { // TODO implement mesh loaders throw "unimplemented lol"; } -MeshData::MeshData(std::vector&& verts, const VertexInputInfo &info) - : data(std::move(verts)) - , info(info) {} +MeshData::MeshData(std::vector &&verts, const VertexInputInfo &info) : + data(std::move(verts)), info(info) {} -MeshData::MeshData(std::vector &&verts, std::vector &&ids, const VertexInputInfo &info) - : data(std::move(verts)) - , ids(std::move(ids)) - , info(info) {} +MeshData::MeshData(std::vector &&verts, std::vector &&ids, const VertexInputInfo &info) : + data(std::move(verts)), ids(std::move(ids)), info(info) {} -std::vector const& MeshData::get_data() const { +std::vector const &MeshData::get_data() const { return this->data; } @@ -112,24 +105,12 @@ VertexInputInfo MeshData::get_info() const { /// Vertices of a quadrilateral filling the whole screen. /// Format: (pos, tex_coords) = (x, y, u, v) static constexpr const std::array QUAD_DATA_CENTERED = { - { - -1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f - } -}; + {-1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f}}; /// Vertices of a quad from (0, 0) to (1, 1) /// Format: (pos, tex_coords) = (x, y, u, v) static constexpr const std::array QUAD_DATA_UNIT = { - { - 0.0f, 1.0f, 0.0f, 1.0f, - 0.0f, 0.0f, 0.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 0.0f, 1.0f, 0.0f - } -}; + {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f}}; namespace { @@ -142,18 +123,17 @@ MeshData create_float_mesh(const std::array &src) { auto const data_size = size * sizeof(float); std::vector verts(data_size); - std::memcpy(verts.data(), reinterpret_cast(src.data()), data_size); + std::memcpy(verts.data(), reinterpret_cast(src.data()), data_size); - VertexInputInfo info { - { vertex_input_t::V2F32, vertex_input_t::V2F32 }, + VertexInputInfo info{ + {vertex_input_t::V2F32, vertex_input_t::V2F32}, vertex_layout_t::AOS, - vertex_primitive_t::TRIANGLE_STRIP - }; + vertex_primitive_t::TRIANGLE_STRIP}; return MeshData(std::move(verts), info); } -} // anon namespace +} // namespace MeshData MeshData::make_quad(bool centered) { @@ -167,31 +147,18 @@ MeshData MeshData::make_quad(bool centered) { MeshData MeshData::make_quad(float sidelength, bool centered) { - // 8 positions and 8 uv-coords. // store pos and uv as: (x, y, uvx, uvy) std::array positions; if (centered) { - float halfsidelength = sidelength/2; + float halfsidelength = sidelength / 2; positions = { - { - -halfsidelength, halfsidelength, 0.0f, 1.0f, - -halfsidelength, -halfsidelength, 0.0f, 0.0f, - halfsidelength, halfsidelength, 1.0f, 1.0f, - halfsidelength, -halfsidelength, 1.0f, 0.0f - } - }; + {-halfsidelength, halfsidelength, 0.0f, 1.0f, -halfsidelength, -halfsidelength, 0.0f, 0.0f, halfsidelength, halfsidelength, 1.0f, 1.0f, halfsidelength, -halfsidelength, 1.0f, 0.0f}}; } else { positions = { - { - 0.0f, sidelength, 0.0f, 1.0f, - 0.0f, 0.0f, 0.0f, 0.0f, - sidelength, sidelength, 1.0f, 1.0f, - sidelength, 0.0f, 1.0f, 0.0f - } - }; + {0.0f, sidelength, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, sidelength, sidelength, 1.0f, 1.0f, sidelength, 0.0f, 1.0f, 0.0f}}; } return create_float_mesh(positions); @@ -204,26 +171,14 @@ MeshData MeshData::make_quad(float width, float height, bool centered) { std::array positions; if (centered) { - float halfwidth = width/2; - float halfheight = height/2; + float halfwidth = width / 2; + float halfheight = height / 2; positions = { - { - -halfwidth, halfheight, 0.0f, 1.0f, - -halfwidth, -halfheight, 0.0f, 0.0f, - halfwidth, halfheight, 1.0f, 1.0f, - halfwidth, -halfheight, 1.0f, 0.0f - } - }; + {-halfwidth, halfheight, 0.0f, 1.0f, -halfwidth, -halfheight, 0.0f, 0.0f, halfwidth, halfheight, 1.0f, 1.0f, halfwidth, -halfheight, 1.0f, 0.0f}}; } else { positions = { - { - 0.0f, height, 0.0f, 1.0f, - 0.0f, 0.0f, 0.0f, 0.0f, - width, height, 1.0f, 1.0f, - width, 0.0f, 1.0f, 0.0f - } - }; + {0.0f, height, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, width, height, 1.0f, 1.0f, width, 0.0f, 1.0f, 0.0f}}; } return create_float_mesh(positions); diff --git a/libopenage/renderer/resources/mesh_data.h b/libopenage/renderer/resources/mesh_data.h index 5d1ce51f9e..1ad0d7a3f4 100644 --- a/libopenage/renderer/resources/mesh_data.h +++ b/libopenage/renderer/resources/mesh_data.h @@ -1,17 +1,18 @@ -// Copyright 2017-2019 the openage authors. See copying.md for legal info. +// Copyright 2017-2023 the openage authors. See copying.md for legal info. #pragma once -#include -#include #include #include #include - -#include "../../util/path.h" +#include +#include namespace openage { +namespace util { +class Path; +} namespace renderer { namespace resources { @@ -71,13 +72,13 @@ class VertexInputInfo { /// map. The map entries must have the format (index_in_vector, index_in_shader) and will /// overwrite the default mapping. If an entry for a given index in the vector is missing, /// that attribute and its data will be skipped. - void add_shader_input_map(std::unordered_map&&); + void add_shader_input_map(std::unordered_map &&); /// Returns the list of per-vertex inputs. const std::vector &get_inputs() const; /// Returns the shader input map or an empty optional if it's not present. - std::optional> const& get_shader_input_map() const; + std::optional> const &get_shader_input_map() const; /// Returns the layout of vertices in memory. vertex_layout_t get_layout() const; @@ -114,13 +115,13 @@ class VertexInputInfo { class MeshData { public: /// Tries to load the mesh data from the specified file. - explicit MeshData(const util::Path&); + explicit MeshData(const util::Path &); /// Initializes the mesh data to a custom unindexed vertex vector described by the given info. - MeshData(std::vector &&verts, const VertexInputInfo&); + MeshData(std::vector &&verts, const VertexInputInfo &); /// Initializes the mesh data to a custom indexed vertex vector described by the given info. - MeshData(std::vector &&verts, std::vector &&ids, const VertexInputInfo&); + MeshData(std::vector &&verts, std::vector &&ids, const VertexInputInfo &); /// Returns the raw vertex data. std::vector const &get_data() const; @@ -136,13 +137,13 @@ class MeshData { /// (0, 0) of this quad is at the quad center and it stretches from (-1, -1) to (1, 1) /// and uv=(0, 0) at bottom left (-> (-1, -1)) and uv=(1, 1) top right (at (1, 1)). /// When centered = false, the quad stretches from (0, 0) to (1, 1). - static MeshData make_quad(bool centered=true); + static MeshData make_quad(bool centered = true); /// Initialize the mesh data with given sidelength. Optionally place (0, 0) bottom left. - static MeshData make_quad(float sidelength, bool centered=true); + static MeshData make_quad(float sidelength, bool centered = true); /// Initialize the mesh data with given width and height. Optionally place (0, 0) bottom left. - static MeshData make_quad(float width, float height, bool centered=true); + static MeshData make_quad(float width, float height, bool centered = true); private: /// The raw vertex data. The size is an integer multiple of the size of a single vertex. @@ -159,4 +160,6 @@ class MeshData { std::optional info; }; -}}} +} // namespace resources +} // namespace renderer +} // namespace openage diff --git a/libopenage/renderer/resources/palette_info.h b/libopenage/renderer/resources/palette_info.h index ba5d17cf59..d147522feb 100644 --- a/libopenage/renderer/resources/palette_info.h +++ b/libopenage/renderer/resources/palette_info.h @@ -2,10 +2,13 @@ #pragma once +#include +#include #include #include + namespace openage::renderer::resources { /** diff --git a/libopenage/renderer/resources/parser/common.cpp b/libopenage/renderer/resources/parser/common.cpp index 3f30fa2f72..30fa97118c 100644 --- a/libopenage/renderer/resources/parser/common.cpp +++ b/libopenage/renderer/resources/parser/common.cpp @@ -2,6 +2,9 @@ #include "common.h" +#include + + namespace openage::renderer::resources::parser { size_t parse_version(const std::vector &args) { diff --git a/libopenage/renderer/resources/parser/parse_blendmask.cpp b/libopenage/renderer/resources/parser/parse_blendmask.cpp index 29c00b2f53..d7f6e4aa74 100644 --- a/libopenage/renderer/resources/parser/parse_blendmask.cpp +++ b/libopenage/renderer/resources/parser/parse_blendmask.cpp @@ -2,16 +2,27 @@ #include "parse_blendmask.h" +#include +#include #include +#include +#include +#include +#include +#include + +#include "log/message.h" #include "error/error.h" #include "renderer/resources/assets/cache.h" #include "renderer/resources/parser/common.h" #include "renderer/resources/parser/parse_texture.h" -#include "renderer/resources/texture_data.h" -#include "renderer/texture.h" +#include "renderer/resources/texture_info.h" +#include "util/file.h" +#include "util/path.h" #include "util/strings.h" + namespace openage::renderer::resources::parser { /** diff --git a/libopenage/renderer/resources/parser/parse_blendmask.h b/libopenage/renderer/resources/parser/parse_blendmask.h index 7c80cb5545..795401c13a 100644 --- a/libopenage/renderer/resources/parser/parse_blendmask.h +++ b/libopenage/renderer/resources/parser/parse_blendmask.h @@ -2,10 +2,17 @@ #pragma once +#include + #include "renderer/resources/terrain/blendpattern_info.h" -#include "util/path.h" -namespace openage::renderer::resources { + +namespace openage { +namespace util { +class Path; +} + +namespace renderer::resources { class AssetCache; namespace parser { @@ -22,4 +29,5 @@ BlendPatternInfo parse_blendmask_file(const util::Path &file, const std::shared_ptr &cache = nullptr); } // namespace parser -} // namespace openage::renderer::resources +} // namespace renderer::resources +} // namespace openage diff --git a/libopenage/renderer/resources/parser/parse_blendtable.cpp b/libopenage/renderer/resources/parser/parse_blendtable.cpp index e7d033eeaf..c37579ce36 100644 --- a/libopenage/renderer/resources/parser/parse_blendtable.cpp +++ b/libopenage/renderer/resources/parser/parse_blendtable.cpp @@ -3,15 +3,24 @@ #include "parse_blendtable.h" #include +#include +#include +#include +#include #include "error/error.h" +#include "log/message.h" + #include "renderer/resources/assets/cache.h" #include "renderer/resources/parser/common.h" #include "renderer/resources/parser/parse_blendmask.h" #include "renderer/resources/terrain/blendpattern_info.h" #include "renderer/resources/terrain/blendtable_info.h" +#include "util/file.h" +#include "util/path.h" #include "util/strings.h" + namespace openage::renderer::resources::parser { /** diff --git a/libopenage/renderer/resources/parser/parse_blendtable.h b/libopenage/renderer/resources/parser/parse_blendtable.h index fb667101c0..d3e7ea2265 100644 --- a/libopenage/renderer/resources/parser/parse_blendtable.h +++ b/libopenage/renderer/resources/parser/parse_blendtable.h @@ -2,10 +2,18 @@ #pragma once +#include +#include +#include + #include "renderer/resources/terrain/blendtable_info.h" -#include "util/path.h" -namespace openage::renderer::resources { +namespace openage { +namespace util { +class Path; +} + +namespace renderer::resources { class AssetCache; namespace parser { @@ -32,4 +40,5 @@ BlendTableInfo parse_blendtable_file(const util::Path &file, const std::shared_ptr &cache = nullptr); } // namespace parser -} // namespace openage::renderer::resources +} // namespace renderer::resources +} // namespace openage diff --git a/libopenage/renderer/resources/parser/parse_palette.cpp b/libopenage/renderer/resources/parser/parse_palette.cpp index d360a8976f..344466dd86 100644 --- a/libopenage/renderer/resources/parser/parse_palette.cpp +++ b/libopenage/renderer/resources/parser/parse_palette.cpp @@ -2,12 +2,24 @@ #include "parse_palette.h" +#include +#include #include +#include +#include +#include +#include +#include #include "error/error.h" +#include "log/message.h" + #include "renderer/resources/parser/common.h" +#include "util/file.h" +#include "util/path.h" #include "util/strings.h" + namespace openage::renderer::resources::parser { /** diff --git a/libopenage/renderer/resources/parser/parse_palette.h b/libopenage/renderer/resources/parser/parse_palette.h index 17c48c4e88..8b8112371a 100644 --- a/libopenage/renderer/resources/parser/parse_palette.h +++ b/libopenage/renderer/resources/parser/parse_palette.h @@ -3,9 +3,13 @@ #pragma once #include "renderer/resources/palette_info.h" -#include "util/path.h" -namespace openage::renderer::resources::parser { +namespace openage { +namespace util { +class Path; +} + +namespace renderer::resources::parser { /** * Parse an palette definition from a .opal format file. @@ -16,4 +20,5 @@ namespace openage::renderer::resources::parser { */ PaletteInfo parse_palette_file(const util::Path &file); -} // namespace openage::renderer::resources::parser +} // namespace renderer::resources::parser +} // namespace openage diff --git a/libopenage/renderer/resources/parser/parse_sprite.cpp b/libopenage/renderer/resources/parser/parse_sprite.cpp index 48c55f1046..2156cc8422 100644 --- a/libopenage/renderer/resources/parser/parse_sprite.cpp +++ b/libopenage/renderer/resources/parser/parse_sprite.cpp @@ -2,18 +2,29 @@ #include "parse_sprite.h" +#include #include +#include +#include +#include +#include +#include #include "error/error.h" +#include "log/message.h" + #include "renderer/resources/animation/angle_info.h" #include "renderer/resources/animation/frame_info.h" #include "renderer/resources/animation/layer_info.h" #include "renderer/resources/assets/cache.h" #include "renderer/resources/parser/common.h" #include "renderer/resources/parser/parse_texture.h" -#include "renderer/texture.h" +#include "renderer/resources/texture_info.h" +#include "util/file.h" +#include "util/path.h" #include "util/strings.h" + namespace openage::renderer::resources::parser { /** diff --git a/libopenage/renderer/resources/parser/parse_sprite.h b/libopenage/renderer/resources/parser/parse_sprite.h index 091778d134..b2021190b3 100644 --- a/libopenage/renderer/resources/parser/parse_sprite.h +++ b/libopenage/renderer/resources/parser/parse_sprite.h @@ -2,10 +2,19 @@ #pragma once -#include "renderer/animation.h" -#include "util/path.h" +#include +#include -namespace openage::renderer::resources { +#include "renderer/resources/animation/animation_info.h" +#include "renderer/resources/animation/layer_info.h" + + +namespace openage { +namespace util { +class Path; +} + +namespace renderer::resources { class AssetCache; namespace parser { @@ -47,5 +56,6 @@ struct FrameData { Animation2dInfo parse_sprite_file(const util::Path &file, const std::shared_ptr &cache = nullptr); -} // namespace resources::parser -} // namespace openage::renderer +} // namespace parser +} // namespace renderer::resources +} // namespace openage diff --git a/libopenage/renderer/resources/parser/parse_terrain.cpp b/libopenage/renderer/resources/parser/parse_terrain.cpp index 1a0c65194f..cc7b2f4814 100644 --- a/libopenage/renderer/resources/parser/parse_terrain.cpp +++ b/libopenage/renderer/resources/parser/parse_terrain.cpp @@ -2,19 +2,30 @@ #include "parse_terrain.h" +#include +#include #include +#include +#include +#include +#include #include "error/error.h" +#include "log/message.h" + #include "renderer/resources/assets/cache.h" #include "renderer/resources/parser/common.h" #include "renderer/resources/parser/parse_blendtable.h" #include "renderer/resources/parser/parse_texture.h" +#include "renderer/resources/terrain/blendtable_info.h" #include "renderer/resources/terrain/frame_info.h" #include "renderer/resources/terrain/layer_info.h" -#include "renderer/resources/texture_data.h" -#include "renderer/texture.h" +#include "renderer/resources/texture_info.h" +#include "util/file.h" +#include "util/path.h" #include "util/strings.h" + namespace openage::renderer::resources::parser { /** diff --git a/libopenage/renderer/resources/parser/parse_terrain.h b/libopenage/renderer/resources/parser/parse_terrain.h index ab7b39fb5f..6aad27ce6e 100644 --- a/libopenage/renderer/resources/parser/parse_terrain.h +++ b/libopenage/renderer/resources/parser/parse_terrain.h @@ -2,12 +2,21 @@ #pragma once +#include +#include #include +#include +#include "renderer/resources/terrain/layer_info.h" #include "renderer/resources/terrain/terrain_info.h" -#include "util/path.h" -namespace openage::renderer::resources { + +namespace openage { +namespace util { +class Path; +} + +namespace renderer::resources { class AssetCache; namespace parser { @@ -51,4 +60,5 @@ TerrainInfo parse_terrain_file(const util::Path &file, const std::shared_ptr &cache = nullptr); } // namespace parser -} // namespace openage::renderer::resources +} // namespace renderer::resources +} // namespace openage diff --git a/libopenage/renderer/resources/parser/parse_texture.cpp b/libopenage/renderer/resources/parser/parse_texture.cpp index 442691b2f4..7da91211f4 100644 --- a/libopenage/renderer/resources/parser/parse_texture.cpp +++ b/libopenage/renderer/resources/parser/parse_texture.cpp @@ -2,11 +2,25 @@ #include "parse_texture.h" +#include +#include +#include +#include +#include +#include +#include +#include + #include "error/error.h" -#include "renderer/renderer.h" +#include "log/message.h" + #include "renderer/resources/parser/common.h" +#include "renderer/resources/texture_subinfo.h" +#include "util/file.h" +#include "util/path.h" #include "util/strings.h" + namespace openage::renderer::resources::parser { /// Tries to guess the alignment of image rows based on image parameters. Kinda diff --git a/libopenage/renderer/resources/parser/parse_texture.h b/libopenage/renderer/resources/parser/parse_texture.h index 52a6eb3640..3000ce3b47 100644 --- a/libopenage/renderer/resources/parser/parse_texture.h +++ b/libopenage/renderer/resources/parser/parse_texture.h @@ -2,14 +2,17 @@ #pragma once +#include + #include "renderer/resources/texture_info.h" -#include "util/path.h" -namespace openage::renderer { -class Renderer; -class Texture2d; -namespace resources::parser { +namespace openage { +namespace util { +class Path; +} + +namespace renderer::resources::parser { /** * Containers for the raw data. @@ -44,5 +47,5 @@ struct SubtextureData { */ Texture2dInfo parse_texture_file(const util::Path &file); -} // namespace resources::parser -} // namespace openage::renderer +} // namespace renderer::resources::parser +} // namespace openage diff --git a/libopenage/renderer/resources/shader_source.cpp b/libopenage/renderer/resources/shader_source.cpp index 0dcf38a39b..60e6b102e0 100644 --- a/libopenage/renderer/resources/shader_source.cpp +++ b/libopenage/renderer/resources/shader_source.cpp @@ -1,25 +1,24 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #include "shader_source.h" -#include "../../util/file.h" +#include + +#include "util/file.h" +#include "util/path.h" namespace openage { namespace renderer { namespace resources { -ShaderSource::ShaderSource(shader_lang_t lang, shader_stage_t stage, std::string &&code) - : lang(lang) - , stage(stage) - , code(std::move(code)) {} +ShaderSource::ShaderSource(shader_lang_t lang, shader_stage_t stage, std::string &&code) : + lang(lang), stage(stage), code(std::move(code)) {} -ShaderSource::ShaderSource(shader_lang_t lang, shader_stage_t stage, const util::Path& path) - : lang(lang) - , stage(stage) - , code(path.open().read()) {} +ShaderSource::ShaderSource(shader_lang_t lang, shader_stage_t stage, const util::Path &path) : + lang(lang), stage(stage), code(path.open().read()) {} -std::string const& ShaderSource::get_source() const { +std::string const &ShaderSource::get_source() const { return this->code; } @@ -31,4 +30,6 @@ shader_stage_t ShaderSource::get_stage() const { return this->stage; } -}}} // openage::renderer::resources +} // namespace resources +} // namespace renderer +} // namespace openage diff --git a/libopenage/renderer/resources/shader_source.h b/libopenage/renderer/resources/shader_source.h index 1008569d9e..1467a306bf 100644 --- a/libopenage/renderer/resources/shader_source.h +++ b/libopenage/renderer/resources/shader_source.h @@ -1,13 +1,14 @@ -// Copyright 2015-2018 the openage authors. See copying.md for legal info. +// Copyright 2015-2023 the openage authors. See copying.md for legal info. #pragma once #include -#include "../../util/path.h" - namespace openage { +namespace util { +class Path; +} namespace renderer { namespace resources { @@ -37,7 +38,7 @@ class ShaderSource { /// Returns a view of the shader source code. This might be text /// or binary data. - std::string const& get_source() const; + std::string const &get_source() const; /// Returns the language of this shader source. shader_lang_t get_lang() const; @@ -56,4 +57,6 @@ class ShaderSource { std::string code; }; -}}} // openage::renderer::resources +} // namespace resources +} // namespace renderer +} // namespace openage diff --git a/libopenage/renderer/resources/terrain/blendpattern_info.h b/libopenage/renderer/resources/terrain/blendpattern_info.h index a5d061a415..6e79b96483 100644 --- a/libopenage/renderer/resources/terrain/blendpattern_info.h +++ b/libopenage/renderer/resources/terrain/blendpattern_info.h @@ -3,12 +3,14 @@ #pragma once #include +#include #include +#include #include -#include "renderer/resources/texture_info.h" namespace openage::renderer::resources { +class Texture2dInfo; struct blending_mask { char directions; diff --git a/libopenage/renderer/resources/terrain/layer_info.cpp b/libopenage/renderer/resources/terrain/layer_info.cpp index 4e4fb20e0e..a93df7ef44 100644 --- a/libopenage/renderer/resources/terrain/layer_info.cpp +++ b/libopenage/renderer/resources/terrain/layer_info.cpp @@ -2,7 +2,11 @@ #include "layer_info.h" +#include +#include + #include "renderer/resources/frame_timing.h" +#include "time/time.h" namespace openage::renderer::resources { diff --git a/libopenage/renderer/resources/terrain/layer_info.h b/libopenage/renderer/resources/terrain/layer_info.h index a730e63c05..92edbc0249 100644 --- a/libopenage/renderer/resources/terrain/layer_info.h +++ b/libopenage/renderer/resources/terrain/layer_info.h @@ -8,8 +8,6 @@ namespace openage::renderer::resources { - -class BlendTableInfo; class TerrainFrameInfo; class FrameTiming; diff --git a/libopenage/renderer/resources/terrain/terrain_info.cpp b/libopenage/renderer/resources/terrain/terrain_info.cpp index b08521b44d..724fd146db 100644 --- a/libopenage/renderer/resources/terrain/terrain_info.cpp +++ b/libopenage/renderer/resources/terrain/terrain_info.cpp @@ -2,6 +2,13 @@ #include "terrain_info.h" +#include +#include +#include + +#include "renderer/resources/terrain/layer_info.h" + + namespace openage::renderer::resources { TerrainInfo::TerrainInfo(const float scalefactor, diff --git a/libopenage/renderer/resources/terrain/terrain_info.h b/libopenage/renderer/resources/terrain/terrain_info.h index 76bc179d25..dda22c8e6b 100644 --- a/libopenage/renderer/resources/terrain/terrain_info.h +++ b/libopenage/renderer/resources/terrain/terrain_info.h @@ -2,12 +2,17 @@ #pragma once +#include +#include #include #include "renderer/resources/terrain/layer_info.h" -#include "renderer/resources/texture_info.h" + namespace openage::renderer::resources { +class BlendTableInfo; +class Texture2dInfo; + /** * Contains information about a 2D terrain texture. diff --git a/libopenage/renderer/resources/texture_data.cpp b/libopenage/renderer/resources/texture_data.cpp index 15965dc88c..0e765b61d7 100644 --- a/libopenage/renderer/resources/texture_data.cpp +++ b/libopenage/renderer/resources/texture_data.cpp @@ -2,11 +2,18 @@ #include "texture_data.h" +#include +#include +#include +#include + #include -#include "../../error/error.h" -#include "../../log/log.h" -#include "../../util/csv.h" +#include "error/error.h" +#include "log/log.h" +#include "renderer/resources/texture_info.h" +#include "renderer/resources/texture_subinfo.h" +#include "util/path.h" namespace openage::renderer::resources { diff --git a/libopenage/renderer/resources/texture_data.h b/libopenage/renderer/resources/texture_data.h index 0b0f296c2b..0107959d36 100644 --- a/libopenage/renderer/resources/texture_data.h +++ b/libopenage/renderer/resources/texture_data.h @@ -2,15 +2,22 @@ #pragma once +#include #include -#include +#include #include -#include "../../util/path.h" +#include "error/error.h" +#include "log/message.h" + #include "texture_info.h" -namespace openage::renderer::resources { +namespace openage { +namespace util { +class Path; +} +namespace renderer::resources { /// Stores 2D texture data in a CPU-accessible byte buffer. Provides methods for loading from /// and storing onto disk, as well as sending to and receiving from graphics hardware. @@ -70,4 +77,5 @@ class Texture2dData { std::vector data; }; -} // namespace openage::renderer::resources +} // namespace renderer::resources +} // namespace openage diff --git a/libopenage/renderer/resources/texture_info.cpp b/libopenage/renderer/resources/texture_info.cpp index 6a65cf729f..dfe3c573e7 100644 --- a/libopenage/renderer/resources/texture_info.cpp +++ b/libopenage/renderer/resources/texture_info.cpp @@ -2,6 +2,11 @@ #include "texture_info.h" +#include + +#include "error/error.h" +#include "log/message.h" + namespace openage::renderer::resources { diff --git a/libopenage/renderer/resources/texture_info.h b/libopenage/renderer/resources/texture_info.h index 4f706bb517..f51f34ed73 100644 --- a/libopenage/renderer/resources/texture_info.h +++ b/libopenage/renderer/resources/texture_info.h @@ -2,12 +2,13 @@ #pragma once +#include #include #include +#include +#include #include -#include - #include "datastructure/constexpr_map.h" #include "renderer/resources/texture_subinfo.h" #include "util/path.h" diff --git a/libopenage/renderer/shader_program.h b/libopenage/renderer/shader_program.h index 912b736f8d..594f66dbc6 100644 --- a/libopenage/renderer/shader_program.h +++ b/libopenage/renderer/shader_program.h @@ -2,10 +2,10 @@ #pragma once +#include #include #include #include -#include #include @@ -15,7 +15,6 @@ namespace openage { namespace renderer { - class Texture2d; class UniformBuffer; diff --git a/libopenage/renderer/stages/camera/manager.h b/libopenage/renderer/stages/camera/manager.h index cddd8dc1ff..342b4bd8ef 100644 --- a/libopenage/renderer/stages/camera/manager.h +++ b/libopenage/renderer/stages/camera/manager.h @@ -4,10 +4,8 @@ #include -#include namespace openage::renderer { -class Renderer; class UniformBufferInput; namespace camera { diff --git a/libopenage/renderer/stages/terrain/terrain_mesh.cpp b/libopenage/renderer/stages/terrain/terrain_mesh.cpp index 364b12ac39..1085e315d4 100644 --- a/libopenage/renderer/stages/terrain/terrain_mesh.cpp +++ b/libopenage/renderer/stages/terrain/terrain_mesh.cpp @@ -2,9 +2,14 @@ #include "terrain_mesh.h" +#include +#include +#include + #include "renderer/resources/assets/asset_manager.h" #include "renderer/resources/assets/texture_manager.h" #include "renderer/resources/terrain/terrain_info.h" +#include "renderer/resources/texture_info.h" #include "renderer/uniform_input.h" diff --git a/libopenage/renderer/stages/terrain/terrain_mesh.h b/libopenage/renderer/stages/terrain/terrain_mesh.h index 9ad66da2f0..a571007204 100644 --- a/libopenage/renderer/stages/terrain/terrain_mesh.h +++ b/libopenage/renderer/stages/terrain/terrain_mesh.h @@ -3,18 +3,16 @@ #pragma once #include -#include -#include +#include #include #include "curve/discrete.h" #include "renderer/resources/mesh_data.h" +#include "time/time.h" + namespace openage::renderer { -class Geometry; -class Renderer; -class Texture2d; class UniformInput; namespace resources { @@ -23,7 +21,6 @@ class TerrainInfo; } // namespace resources namespace terrain { -class TerrainRenderEntity; /** * Drawable chunk of terrain with a single texture. diff --git a/libopenage/renderer/stages/terrain/terrain_model.cpp b/libopenage/renderer/stages/terrain/terrain_model.cpp index 0dc7e0a641..d663b19dae 100644 --- a/libopenage/renderer/stages/terrain/terrain_model.cpp +++ b/libopenage/renderer/stages/terrain/terrain_model.cpp @@ -2,13 +2,21 @@ #include "terrain_model.h" -#include "renderer/camera/camera.h" +#include +#include +#include +#include + +#include + +#include "coord/scene.h" #include "renderer/resources/assets/asset_manager.h" -#include "renderer/resources/assets/texture_manager.h" -#include "renderer/resources/terrain/terrain_info.h" +#include "renderer/resources/mesh_data.h" #include "renderer/stages/terrain/terrain_mesh.h" #include "renderer/stages/terrain/terrain_render_entity.h" -#include "renderer/uniform_input.h" +#include "util/fixed_point.h" +#include "util/vector.h" + namespace openage::renderer::terrain { diff --git a/libopenage/renderer/stages/terrain/terrain_model.h b/libopenage/renderer/stages/terrain/terrain_model.h index 0bdfa91532..4a1ec15aed 100644 --- a/libopenage/renderer/stages/terrain/terrain_model.h +++ b/libopenage/renderer/stages/terrain/terrain_model.h @@ -3,10 +3,10 @@ #pragma once #include +#include -#include +#include "time/time.h" -#include "curve/discrete.h" namespace openage::renderer { diff --git a/libopenage/renderer/stages/terrain/terrain_render_entity.cpp b/libopenage/renderer/stages/terrain/terrain_render_entity.cpp index a4c915c2b1..a142461cb0 100644 --- a/libopenage/renderer/stages/terrain/terrain_render_entity.cpp +++ b/libopenage/renderer/stages/terrain/terrain_render_entity.cpp @@ -2,9 +2,10 @@ #include "terrain_render_entity.h" -#include "renderer/renderer.h" -#include "renderer/resources/texture_data.h" -#include "renderer/texture.h" +#include +#include +#include + namespace openage::renderer::terrain { diff --git a/libopenage/renderer/stages/terrain/terrain_render_entity.h b/libopenage/renderer/stages/terrain/terrain_render_entity.h index 5def8eeef4..2d074e2590 100644 --- a/libopenage/renderer/stages/terrain/terrain_render_entity.h +++ b/libopenage/renderer/stages/terrain/terrain_render_entity.h @@ -4,12 +4,15 @@ #include #include +#include #include #include "coord/scene.h" #include "curve/discrete.h" +#include "time/time.h" #include "util/vector.h" + namespace openage::renderer { namespace terrain { diff --git a/libopenage/renderer/stages/world/world_object.cpp b/libopenage/renderer/stages/world/world_object.cpp index 57b333d083..918578b4f0 100644 --- a/libopenage/renderer/stages/world/world_object.cpp +++ b/libopenage/renderer/stages/world/world_object.cpp @@ -2,6 +2,12 @@ #include "world_object.h" +#include +#include +#include +#include +#include + #include #include "renderer/camera/camera.h" @@ -9,13 +15,17 @@ #include "renderer/resources/animation/angle_info.h" #include "renderer/resources/animation/animation_info.h" #include "renderer/resources/animation/frame_info.h" +#include "renderer/resources/animation/layer_info.h" #include "renderer/resources/assets/asset_manager.h" #include "renderer/resources/assets/texture_manager.h" #include "renderer/resources/frame_timing.h" #include "renderer/resources/mesh_data.h" -#include "renderer/resources/texture_data.h" +#include "renderer/resources/texture_info.h" +#include "renderer/resources/texture_subinfo.h" #include "renderer/stages/world/world_render_entity.h" #include "renderer/uniform_input.h" +#include "util/fixed_point.h" +#include "util/vector.h" namespace openage::renderer::world { diff --git a/libopenage/renderer/stages/world/world_object.h b/libopenage/renderer/stages/world/world_object.h index dfa7ba20a6..9c08cddad5 100644 --- a/libopenage/renderer/stages/world/world_object.h +++ b/libopenage/renderer/stages/world/world_object.h @@ -2,16 +2,18 @@ #pragma once +#include +#include #include - -#include +#include #include "coord/scene.h" #include "curve/continuous.h" -#include "time/time.h" #include "curve/discrete.h" #include "curve/segmented.h" #include "renderer/resources/mesh_data.h" +#include "time/time.h" + namespace openage::renderer { class UniformInput; diff --git a/libopenage/renderer/stages/world/world_render_entity.cpp b/libopenage/renderer/stages/world/world_render_entity.cpp index 2375c127eb..8cba90ca40 100644 --- a/libopenage/renderer/stages/world/world_render_entity.cpp +++ b/libopenage/renderer/stages/world/world_render_entity.cpp @@ -2,8 +2,12 @@ #include "world_render_entity.h" +#include +#include + #include "renderer/definitions.h" + namespace openage::renderer::world { WorldRenderEntity::WorldRenderEntity() : diff --git a/libopenage/renderer/stages/world/world_render_entity.h b/libopenage/renderer/stages/world/world_render_entity.h index bd50e10b48..ae656da20d 100644 --- a/libopenage/renderer/stages/world/world_render_entity.h +++ b/libopenage/renderer/stages/world/world_render_entity.h @@ -2,16 +2,20 @@ #pragma once -#include +#include +#include #include +#include + +#include #include "coord/phys.h" #include "coord/scene.h" #include "curve/continuous.h" -#include "time/time.h" #include "curve/discrete.h" #include "curve/segmented.h" -#include "util/path.h" +#include "time/time.h" + namespace openage::renderer::world { diff --git a/libopenage/renderer/texture_array.h b/libopenage/renderer/texture_array.h index 491be5f4d5..ccc19df0a9 100644 --- a/libopenage/renderer/texture_array.h +++ b/libopenage/renderer/texture_array.h @@ -1,8 +1,11 @@ -// Copyright 2018-2018 the openage authors. See copying.md for legal info. +// Copyright 2018-2023 the openage authors. See copying.md for legal info. #pragma once -#include "resources/texture_data.h" +#include + +#include "renderer/resources/texture_data.h" +#include "renderer/resources/texture_info.h" namespace openage { @@ -16,19 +19,20 @@ class Texture2dArray { virtual ~Texture2dArray(); /// Returns information about the layer format. - resources::Texture2dInfo const& get_info() const; + resources::Texture2dInfo const &get_info() const; /// Uploads the given texture data into the specified layer. `layer` must /// be strictly less than the size of the array and the data format must /// match the format this array was originally created with. - virtual void upload(size_t layer, resources::Texture2dData const&) = 0; + virtual void upload(size_t layer, resources::Texture2dData const &) = 0; protected: /// Constructs the base class. - Texture2dArray(const resources::Texture2dInfo&); + Texture2dArray(const resources::Texture2dInfo &); /// Information about the size, format, etc. of every layer in this array. resources::Texture2dInfo layer_info; }; -}} // openage::renderer +} // namespace renderer +} // namespace openage diff --git a/libopenage/renderer/uniform_buffer.h b/libopenage/renderer/uniform_buffer.h index dc642b7040..2ba5790127 100644 --- a/libopenage/renderer/uniform_buffer.h +++ b/libopenage/renderer/uniform_buffer.h @@ -2,15 +2,17 @@ #pragma once +#include #include #include #include "renderer/uniform_input.h" -namespace openage::renderer { +namespace openage::renderer { class Texture2d; +class UniformBufferInput; class UniformBuffer : public std::enable_shared_from_this { friend UniformBufferInput; diff --git a/libopenage/renderer/uniform_input.h b/libopenage/renderer/uniform_input.h index 8a52fc3e6e..85b197e4fa 100644 --- a/libopenage/renderer/uniform_input.h +++ b/libopenage/renderer/uniform_input.h @@ -1,17 +1,18 @@ // Copyright 2019-2023 the openage authors. See copying.md for legal info. - #pragma once +#include #include #include -#include "../error/error.h" +#include "error/error.h" +#include "log/message.h" +#include "util/compiler.h" namespace openage::renderer { - class ShaderProgram; class Texture2d; class UniformBuffer; diff --git a/libopenage/renderer/util.cpp b/libopenage/renderer/util.cpp index d9c5fcfe1d..3cb3995790 100644 --- a/libopenage/renderer/util.cpp +++ b/libopenage/renderer/util.cpp @@ -1,22 +1,19 @@ -// Copyright 2018-2019 the openage authors. See copying.md for legal info. - +// Copyright 2018-2023 the openage authors. See copying.md for legal info. #include "util.h" +#include + namespace openage::renderer::util { -Eigen::Matrix4d ortho_matrix_d(double left, double right, - double bottom, double top, - double near, double far) { +Eigen::Matrix4d ortho_matrix_d(double left, double right, double bottom, double top, double near, double far) { return ortho_matrix(left, right, bottom, top, near, far); } -Eigen::Matrix4f ortho_matrix_f(float left, float right, - float bottom, float top, - float near, float far) { +Eigen::Matrix4f ortho_matrix_f(float left, float right, float bottom, float top, float near, float far) { return ortho_matrix(left, right, bottom, top, near, far); } -} // openage::renderer::util +} // namespace openage::renderer::util diff --git a/libopenage/renderer/window_event_handler.cpp b/libopenage/renderer/window_event_handler.cpp index 52cd317aae..8f83ff3eaf 100644 --- a/libopenage/renderer/window_event_handler.cpp +++ b/libopenage/renderer/window_event_handler.cpp @@ -1,4 +1,4 @@ -// Copyright 2022-2022 the openage authors. See copying.md for legal info. +// Copyright 2022-2023 the openage authors. See copying.md for legal info. #include "window_event_handler.h" @@ -10,7 +10,7 @@ namespace openage::renderer { -bool WindowEventHandler::eventFilter(QObject *obj, QEvent *event) { +bool WindowEventHandler::eventFilter(QObject * /* obj */, QEvent *event) { auto add_to_queue = [this, event]() { this->event_queue.push_back(std::shared_ptr(event->clone())); }; From 3064802bea7ea19c50136e5199f03a80b28ea0ff Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 27 Jul 2023 01:57:15 +0200 Subject: [PATCH 07/49] doc: Simulation time. --- doc/code/time.md | 61 ++++++++++++++++++++++--------------- libopenage/time/time_loop.h | 2 +- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/doc/code/time.md b/doc/code/time.md index 068cf56a23..18b8b93846 100644 --- a/doc/code/time.md +++ b/doc/code/time.md @@ -1,26 +1,39 @@ # Simulation Time -- basis for all game simulation and rendering routines - - scheduling and execution of events (event loop) - - data management over time (curves) - - timing animations (renderer) - -![time loop UML]() - -- time type - - curve::time_t - - fixed point value - - time unit: seconds - -- controlled by time loop - - advances time with clock - - runs in its own thread - - accessed by simulation and renderer threads - -- clock manages time - - calculates time using system clock - - can be stopped and resumed - - tracks both real time and simulation time - - real time = cumulative run time of clock - - speed can be adjusted for simulation time - - simulationm time = cumulative run time of clock adjusted by speed +*Simulation time* is the driving force behind pretty much all internal routines of the engine. +Its used to schedule and execute events in the [event system](/doc/code/event_system.md), +managing data of game entities at runtime via [curves](/doc/code/curves.md), and time +animations in the [renderer](/doc/code/renderer/level2.md). + +To keep track of and synchronize time between different engine components, openage implements +a low-level time control interface in the `openage::time` namespace. This system is very simple: +Basically, it consists of a loop in a dedicated thread that constantly updates an internal clock. + +![time loop UML](ASDF) + +`TimeLoop` provides a basic interface to create an internal clock and run the update loop. Once +initialized, the `Clock` instance can be accessed with the `get_clock()` method. For most calculations, +the clock object is sufficient. Access to the time loop is only necessary if you want to +pause/stop the entire simulation. + +openage represents time with the internal type `time::time_t` (which must not be confused with the C standard +type `time_t`). `time::time_t` is a 64 bit signed fixed-point value (scaling factor 16) +which represents the time passed in **seconds**, i.e. a value of `1.25` denotes 1.25 seconds or +1250 milliseconds, respectively. + +`Clock` manages the current simulation time using the system clock. It also allows the configuration +of a simulation speed (which can be negative). Time is advanced in each +loop iteration of `TimeLoop` by calling the clock object's `update_time()` method. This method +first calculates the real time difference between the previous and current update call. The result +is then multiplied by the simulation speed and added to the total simulation time. Additionally, `Clock` +keeps track of how much real time has passed, regardless of simulation speed. Real time is used +in the renderer to time animation frames which should not affected by simulation speed. + +The time resolution used for the system clock is **milliseconds**. When requesting the current time +from a clock via `get_time()` or `get_real_time()`, this gets converted into the openage `time::time_t` +format. + +To prevent the clock updates from advancing too far if the program is stopped by the operating system +or a debugger, `update_time()` sets a maximum limit on how much real time can pass between two update +calls. This limit is currently hardcoded to 50ms. Similarly, there is a minimum limit of 1ms to +prevent the time loop thread from stalling. diff --git a/libopenage/time/time_loop.h b/libopenage/time/time_loop.h index e01d1a2ab1..0611b5b30e 100644 --- a/libopenage/time/time_loop.h +++ b/libopenage/time/time_loop.h @@ -10,7 +10,7 @@ namespace openage::time { class Clock; /** - * Manage event loop and clock. + * Manage and update internal time via a clock. */ class TimeLoop { public: From 6fe3126e365a03f16518a1bc5bd8af12f2947876 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 27 Jul 2023 04:16:00 +0200 Subject: [PATCH 08/49] doc: Add TOCs for old docs. --- doc/code/converter/architecture_overview.md | 8 ++++++++ doc/code/converter/workflow.md | 11 +++++++++++ doc/code/input/README.md | 11 +++++++++++ 3 files changed, 30 insertions(+) diff --git a/doc/code/converter/architecture_overview.md b/doc/code/converter/architecture_overview.md index a1f57917b6..9247952246 100644 --- a/doc/code/converter/architecture_overview.md +++ b/doc/code/converter/architecture_overview.md @@ -2,6 +2,14 @@ This document describes the code architecture of the openage converter module. +1. [Design Principles](#design-principles) + 1. [Value Object](#value-object) + 2. [Entity Object](#entity-object) + 3. [Service](#service) + 4. [Processor](#processor) + 5. [Tool](#tool) + + ## Design Principles Our converter uses hierarchical multi-layered object-oriented code principles similar to those found in diff --git a/doc/code/converter/workflow.md b/doc/code/converter/workflow.md index 427be6052c..5ddd2db621 100644 --- a/doc/code/converter/workflow.md +++ b/doc/code/converter/workflow.md @@ -8,6 +8,17 @@ must test with all supported version when you add a feature. ![workflow structogram](images/workflow.svg) +1. [Game and Version detection](#game-and-version-detection) +2. [Mounting](#mounting) +3. [Reader](#reader) + 1. [Game Data](#game-data) +4. [Processor](#processor) + 1. [Preprocessing](#preprocessing) + 2. [Grouping/Linking](#groupinglinking) + 3. [Mapping](#mapping) +5. [Exporter](#exporter) + + ## Game and Version detection The conversion process starts with determining the game version. Users diff --git a/doc/code/input/README.md b/doc/code/input/README.md index d7c048a882..ab059a668f 100644 --- a/doc/code/input/README.md +++ b/doc/code/input/README.md @@ -4,6 +4,17 @@ Input managment concerns the handling external input sources that are used to in The most common example for such input sources are probably **keyboard and mouse**, but other things like the **GUI**, **networking** and **scripting** also fall under this umbrella. +1. [Motivation](#motivation) +2. [Basic Architecture](#basic-architecture) +3. [Window System](#window-system) + 1. [Workflow](#workflow) + 2. [Class Relationships](#class-relationships) + 3. [Low-level Interface](#low-level-interface) + 4. [High-Level Interface (Controller)](#high-level-interface-controller) +4. [Scripting](#scripting) +5. [Network](#network) +6. [AI](#ai) + ## Motivation From 36ca609300a078f03918cbeef2ae6daa70bdb601 Mon Sep 17 00:00:00 2001 From: heinezen Date: Fri, 28 Jul 2023 00:32:44 +0200 Subject: [PATCH 09/49] doc: Event system. --- doc/code/event_system.md | 138 +++++++++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 40 deletions(-) diff --git a/doc/code/event_system.md b/doc/code/event_system.md index 98b77f223c..afe9940571 100644 --- a/doc/code/event_system.md +++ b/doc/code/event_system.md @@ -1,48 +1,106 @@ # Event System -- event system in openage - - sending and receiving events - - handling events - - integration of events into the simulation +Game simulation in openage is event-driven. The internal event system +manages the creation, scheduling, and execution of these events. 1. [Architecture](#architecture) +2. [Event Handler Setup](#event-handler-setup) +3. [Event Instances](#event-instances) +4. [Triggering/Targeting Events](#triggeringtargeting-events) +5. [Canceling Events](#canceling-events) ## Architecture -![event system UML]() - -- central component: event loop - - main point of interaction to create events - - manage event queue and event store - - manage interdependencies between events - - events are based on simulation time - - time from time loop and its clock - - advance to a point in time - - check for interdendency changes and reorder events if necessary - - execute event handlers in order of execution time - -- event - - represents an event - - always targets an event entity - - executed at specific time which can be modified by event loop based on dependencies - - stores parameters for execution - -- event entity - - inherit from this class - - target OR dependency of event - - as target: - - may be manipulated by event handler on event execution - - as dependency: - - triggers the event execution - -- event handler - - executes if event fires - - can be registered on event loop (if reusable) - - or passed when creating event - - trigger type of handler manages when event is executed - - dependency, once, repeating, etc. - -- state - - object that persists across event executions - - can be anything, usually used for global vars +![event system UML](ASDF) + +The central component of the event system is the *event loop* (`EventLoop`). It is +the main point of interaction between the game simulation logic and the event system. +New events (`Event` instances) are added via the `create_event(..)` method and scheduled based on the +configuration of the associated *[event handler](#event-handler-setup)* (`EventHandler`). +Internally, the event is stored inside an *event queue* (`EventQueue`) instance +sorts the events by invoke time. + +Events are executed by passing the current [simulation time](/doc/code/time.md) to the +event loop's `reach_time(..)` method. This executes all events with invoke time + +``` +t_old < t_e <= t_cur +``` + +where `t_cur` is the current simulation time, `t_old` is the last time `reach_time(..)` was +called, and `t_e` is the event's scheduled invoke time. Events are executed in order of +earliest to latest invoke time. During execution, events may trigger the rescheduling of +other events if there are dependencies between the events. + +Both the creation and execution of events require passing a `State` object that functions +as a persistant global storage for values that are not linked to a specific event executions. +In practice, `State` is primarily used to pass the state of the [game simulation](/doc/code/game_simulation/README.md) +to the event system via an object of the derived `GameState` class. This object allows +access to the indexing structures of the current game that can be used to retrieve +the available game entities, for example. + + +## Event Handler Setup + +The logic for (re-)scheduling and invoking an event is implemented by derivatives of the +`EventHandler` class. Event handlers must implement 3 methods: + +* `invoke(..)`: Called by the event loop when the event is executed. +* `predict_invoke_time(..)`: Calculates the desired invoke time. Called when the event is first created and whenever + the event is due to be rescheduled (e.g. if a dependency event changes). +* `setup_event(..)`: Sets up dependencies during creation of the event. + +Additionally, event handlers require a unique ID (defined as a string) as well as +a *trigger type* which defines under which circumstances an event is executed. +These parameters can be defined at runtime when instantiating the event handler. + +To enable events to use the event handler, an event handler object must first +be initialized and then passed to the event loop. This can either be done explicitly via +the `add_event_handler(..)` method of the `EventLoop` class or during the +creation of an event via an overloaded `create_event(..)` method. Once an event +handler is registered, new events can also be created using the event handler's +unique ID string when calling `create_event(..)`. + + +## Event Instances + +When calling `create_event(..)` on the event loop, an `Event` instance is returned which +allows access to the configuration parameters of the event. Event instances store +references to: + +* **Event payload**: Map of parameters passed to the event handler on invocation. +* **Event target**: Entity targeted by the event. Implemented as a weak reference to let the target expire. +* **Event handler**: Event handler object used for rescheduling and executing the event. +* **Invoke time**: Determines when the event is executed. +* **Time of last change**: Time of the last change to invocation time. Used for rescheduling the event. + +These parameters may be changed after creating the event. However, in most cases it's +more beneficial to create a new event instead and [cancel](#canceling-events) the old +event. + +## Triggering/Targeting Events + +Both sending and receiving events is managed by the same interface, i.e. the `EventEntity` +class. By inheriting from `EventEntity`, a class can be targeted by events, trigger +or change events or both. + +An event target is passed during the creation of an event (i.e. in `create_event(..)`) +and later when executing the event via the event handler's `invoke(..)` method. Events +can only have one target at a time. + +`EventEntity` derivatives may also be used to trigger or reschedule the invocation of events. To do this, +the event has to be added as a dependent for the event entity using the `add_dependent(..)` +method of `EventEntity`. Calling the `changes(..)` method of `EventEntity` will then result +in a rescheduling of the event in the next iteration of the event loop. + + +## Canceling Events + +Events are automatically canceled if their target has expired, i.e. it has been deleted +from memory. To accomplish this target references are implemented using `std::weak_ptr` +which are checked for expiration before event execution. + +Manually canceling events can also be done by calling `cancel(..)` on an `Event` instance. +In the current implementation, this change is effective immediately, even if the provided +reference time is in the future. From c122d0a5d72599ef9d4a5e4a9fa4101015db177c Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 29 Jul 2023 16:46:36 +0200 Subject: [PATCH 10/49] doc: Curves. --- doc/code/curves.md | 274 ++++++++++++++++++++++++++++----------------- 1 file changed, 172 insertions(+), 102 deletions(-) diff --git a/doc/code/curves.md b/doc/code/curves.md index 234255987e..3233bafb42 100644 --- a/doc/code/curves.md +++ b/doc/code/curves.md @@ -1,148 +1,218 @@ # Curves -- data structure for keyframe-based simulation - - not just data but manages data over time +*Curves* are data structures that manage changes to data over time. More precisely, +they do not only store data but time-value pairs (keyframes) that allow the retrieval +of a curve data value for any point in time. +Curves are an integral part of openage's event-based game simulation. 1. [Motivation](#motivation) 2. [Architecture](#architecture) 3. [Curve Types](#curve-types) 1. [Primitive](#primitive) - 1. [Discrete](#discrete) - 2. [Continuous](#continuous) - 3. [Segmented](#segmented) + 1. [Common Operations](#common-operations) + 2. [Discrete](#discrete) + 3. [Continuous](#continuous) + 4. [Segmented](#segmented) 2. [Container](#container) 1. [Queue](#queue) 2. [Unordered Map](#unordered-map) - ## Motivation -- inspired by a similar implementation for Planetary Annihilation - - https://www.forrestthewoods.com/blog/tech_of_planetary_annihilation_chrono_cam/#.lmxbu3vld -- problem: data is hard to keep consistent in sync (synchronous) - - over network - - across threads - - state should be the same for all components -- solution: instead of only storing *current* data, store as keyframes with (data, time) - - storage becomes a function that tracks data values over time -> timeline - - data in between keyframes can be interpolated - - changing data can be asynchronous -> keyframes can be added for future - - reverting to previous simulation time becomes much easier -- downsides: - - requires more storage - - interpolation is more costly than simple ticks - - can be more complex to integrate -- however: - - RTS actions are often predictable and therefore doesn't require many keyframes - - data doesn't have to be accessed only when necessary - - may be easier to understand in implemention +The openage curve structures are inspired by a [similar implementation](https://www.forrestthewoods.com/blog/tech_of_planetary_annihilation_chrono_cam/#.lmxbu3vld) from the game Planetary Annihilation. + +Curves intend to solve synchronicity problems in games. In traditional implementations +like lockstep, data is changed incrementally in regular intervals (ticks). Keeping +these changes and the overall gamestate consistent/in sync over network or across threads +is very important, as one missing change can result in a desync of the entire simulation. +Recovering from a desync can also be very hard. + +In comparison, simulation with curves allows both async operations and an easier recovery +from desync states. Data is not changed incrementally but is instead calculated using +*keyframe interpolation*. In other words, changes to data are treated as keyframes on +a timeline with in-between values being interpolated from adjacent keyframes. + +![Curve keyframes example](ASDF) + +Additionally, curves cannot only access values for the current simulation time but also for +any past or even future times. Keyframes can also be inserted for any point in time without +directly invalidating the state, making curves more reliable in async scenarios (although +resolving dependencies for keyframes in the past can still be challenging). + +The usage of curves has a few downsides though. They are less space efficient due to the +keyframe storage, interpolation are more costly more costly than incremental changes, and +their integration is more complex than the usage of simpler data structures. However, in +situations where operations are predictable, long-lasting, and easy to calculate - which +is the case for most RTS games - these downsides are usually outweight by the positives. ## Architecture -- major components - - BaseCurve: primitive data - - Discrete - - Interpolated - - Continuous - - Segmented - - Queue and UnorderedMap: containers - - Keyframe: time-value pair - - KeyframeContainer: storage for curve keyframes - - curve::time_t: time type -> fixed point that represents simulation time in seconds - -- which curves should be used for - - data that isn't modified or read - - data that must be tracked over time - - ALL game data in game entity components that is required for simulation - -- what curves should not be used for - - data that is modified or read often - - data that doesn't have to be tracked over time - - e.g. temporary variables - - e.g. things that don't affect the simulation like visual settings +![Curve classes hierarchy UML](ASDF) + +openage provides several curve types with different interpolation behaviour that each have +their specific use case. Primitive data types are covered by the `BaseCurve` interface +and its derivates of which the `Discrete`, `Continuous`, and `Segmented` are the practically +usable types. For containers, there are classes for `Queue` and `UnorderedMap` available. +All curve types are templated, so that they can store any type of value (with some constraints). +The usable types are explained in more detail in the [Curve Types](#curve-types) section. + +Keyframes are implemented as time-value pairs by the `Keyframe` class. `KeyframeContainer` +is used by all curves to manage their keyframe storage. It also provides functionality to +efficiently access and insert keyframes as wel as sorting them by time. For the time +type, the [simulation time type](/doc/code/time.md) from the `openage::time` namespace is +used. + +It should be noted that curves are not useful in every situation as keyframe insertion, +interpolation, and searching keyframes based on time create significant overhead. Curves +should be used for variables or members where + +* data values must be tracked over time (e.g. HP of a game entity), or +* data values are not modified/read very often (e.g. not every frame), or +* data values are supposed to be sent over the network. + +This is usually the case for all game data in [game entity components](/doc/code/game_simulation/components.md) +inside the game simulation. + +Curves should generally not be used for variables or members where + +* data values are not tracked over time (e.g. for temporary variables), or +* data values are modified/read very often (e.g. in the rendering loop), or +* data values don't affect the simulation state (e.g. visual settings). + ## Curve Types -- overview of built-in curve types +This section provides an overview over the available curves types. ### Primitive -- curves for storing the change for primitive/single values - - primitive C++ types, e.g. int/float/std::string - - objects - - shared ptr - -- default value is assigned for t = curve::time_t::min - - guarantees that there is always a value to access - - mirrors expected behaviour from declaring primitive values in C++ - - for objects that don't have default constructor, the default must be assigned when initializing curve - -- operations - - read - - get(t): Get the value at time t - - frame(t): Get the previous keyframe (time and value) before t - - next_frame(t): Get the next keyframe (time and value) after t - - modify - - set_insert(t, value): Insert a new keyframe value at t - - set_last(t, value): Insert a new keyframe value at t, delete all keyframes with after t - - set_replace(t, value): Insert a new keyframe value at t, remove all other keyframes with time t - - copy - - sync(Curve, t): copy keyframes from one curve to the other, starting at t +![Primitive curve types UML](ASDF) + +Primitive curves are intended for members with single value types. These include, for example, +the primitive C++ types (e.g. `int`, `float`, `std::string`), simple structs and data classes, +and shared pointers. + +On contruction of a primitive curve object, a keyframe with time `t = time::time_t::min_value()` +and the default value of the value type is inserted. This is done to ensure that for any +requested time `t`, there is always a valid value to be returned. This mirrors the expected +behaviour from declaring primitive values in C++ where members may be auto-initialized without +explicit assignment to a default value. The default value for curves can also be explicitely +assigned in the constructor. For value types that don't have default values, a default value +must always be passed in the constructor. + +`BaseCurve` objects can be targeted by or trigger events from the [event system](/doc/code/event_system.md). +As a consequence, curves are not copy constructable as they require a unique ID for +event management. However, it is possible to copy the keyframes of one curve to +the other using the `sync(..)` method. `sync(..)` also works for curves with different +values types if a converter function from one value type to the other is supplied. + +#### Common Operations + +All primitive curves support the following operations. They may work slightly different +for specific curve types. + +**Read** + +| Method | Description | +| --------------- | ----------------------------------------------------------- | +| `get(t)` | Get (interpolated) value at time `t` | +| `frame(t)` | Get the previous keyframe (time and value) before or at `t` | +| `next_frame(t)` | Get the next keyframe (time and value) after `t` | + +**Modify** + +| Method | Description | +| ----------------------- | --------------------------------------------------------------------------------- | +| `set_insert(t, value)` | Insert a new keyframe value at time `t` | +| `set_last(t, value)` | Insert a new keyframe value at time `t`; delete all keyframes after time `t` | +| `set_replace(t, value)` | Insert a new keyframe value at time `t`; remove all other keyframes with time `t` | + +**Copy** + +| Method | Description | +| ---------------------- | ----------------------------------------------------------------------------------------- | +| `sync(Curve, t)` | copy keyframes from `Curve`, starting at time `t`; delete all keyframes after time `t` | +| `sync_after(Curve, t)` | copy keyframes from `Curve`, starting after time `t`; delete all keyframes after time `t` | + #### Discrete -- value between two keyframes is constant - - returns value of previous keyframe +![Discrete curve function example]() + +Discrete curves implement **constant interpolation** between keyframes. This means +that the value returned by `get(t)` is always equal to the value of the previous +keyframe. + +Discrete curves should be used for values that only change at specific points in time, +e.g. for the health of a game entity. -- used for values that dont change much - - e.g. current health #### Continuous -- value between two keyframes is interpolated - - returns interpolated value from requested time between t1 and t2 - - if t > last keyframe, then behave like discrete curve - - currently only supports linear interpolation +![Continuous curve function example]() + +Continuous curves implement **linear interpolation** between keyframes. This means +that the value returned by `get(t)` is calculated from the value difference +between the keyframes before and after `t`. If there is no keyframe after `t`, +the value of the previous keyframe is used (like on discrete curves). + +Value types on continuous curves need to implement methods for the `operator*(..)` and +`operator-(..)` operations to support linear interpolation. In particular, `operator*(..)` +must support multiplication with `time::time_t` and `operator-(..)` must support +substraction for values of the same type. + +Continuous curves do not allow jumps between keyframe values (hence "continuous"). +Therefore, there cannot be two keyframes inserted for the same time `t`. + +Continuous curves should be used for values that change gradually over time, +e.g. a game entity's position or the construction progress of a building. -- used for values that change gradually - - e.g. entity position, current construction progress of building #### Segmented -- value between two keyframes is interpolated - - behaves like continuous but allows "jumps" between intervals - - e.g. intervals are not connected like on continuous curve - - mix between continuous and discrete +![Segmented curve function example]() + +Segmented curves implement **linear interpolation** between keyframes and additionally +allow jumps between keyframe values. As with continuous curves, the value returned by `get(t)` +is calculated from the value difference between the keyframes before and after `t`. + +Jumps are inserted using the special methods `set_insert_jump(..)` and `set_last_jump(..)`: + +| Method | Description | +| ------------------------------------ | --------------------------------------------------------------------------------- | +| `set_insert_jump(t, value1, value2)` | Insert a two new keyframes at time `t`: `(t, value1)` and `(t, value2)` | +| `set_last_jump(t, value1, value2)` | Insert a two new keyframes at time `t` like above; delete all keyframes after `t` | + +Segmented curves should be used for values that change gradually but are not on +connected intervals. Typically, this are values that wrap around at some point, +e.g. angles between 0 and 360 degrees. -- used for values that change gradually but are not on connected intervals - - e.g. angles (because the value jumps when rolling over from 0 to 360 degrees) ### Container -- curves for storing the changes for collections and containers - - queues - - maps +Container curves are intended for storing changes to collections and containers. +The currently supported containers are `Queue` and `UnorderedMap`. + +The most important distinction between regular C++ containers and curve containers +is that curve containers keep track of when modifications happen and what changes +to an element are made. Deleting elements also does not erase elements from memory. +Instead, they are simply hidden for requests for time `t1` after the deletion time `t2` if +`t1 > t2`. -- most important distinction between normal containers and curve containers - - modify operations do not erase/overwrite previous elements - - elements are always valid at a specific time - - if element e is erased at t, requesting it at t1>=t will return nothing but requesting it at t2 smaller t will return e #### Queue -- stores elements in insertion order and additionally insertion time - - front: for requested time t, return the element that was in front at t - - iterate: iterate over queue beginning at time t - - no empty check because elements are not deleted - - callee must remember the iterator to the last element it accessed! +Queue curve containers store elements in first-in-first-out (FIFO) insertion order +while additionally keeping track of element insertion time. Requests for the front element +at time `t` will return the element that is in front of the queue at that time. +The queue can also be iterated over for a specific time `t` which allows access to +all elements that were in the queue at time `t`. #### Unordered Map -- stores key-value pairs and remembers inserion time - - at: return the element for key k that was present at time t - - insert: add pair k, e at time t - - insert: add pair k, e at time t1 and erase at t2 - - kill: erase element at time t - - iterate over elements between time t1 and t2 +Unordered map curve containers store key-value pairs while additionally keeping +track of element insertion time. Requests for a key `k` at time `t` will return the value +of `k` at that time. The unordered map can also be iterated over for a specific time `t` which +allows access to all key-value pairs that were in the map at time `t`. From 96c4b7158e7f518dc0c64ec71730a81c0080c96c Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 29 Jul 2023 17:07:21 +0200 Subject: [PATCH 11/49] curve: Align name of 'sync' for curve and keyframe container. --- doc/code/curves.md | 7 +++---- libopenage/curve/base_curve.h | 4 ++-- libopenage/curve/keyframe_container.h | 20 ++++++++++---------- libopenage/curve/tests/curve_types.cpp | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/doc/code/curves.md b/doc/code/curves.md index 3233bafb42..d345fdb94f 100644 --- a/doc/code/curves.md +++ b/doc/code/curves.md @@ -131,10 +131,9 @@ for specific curve types. **Copy** -| Method | Description | -| ---------------------- | ----------------------------------------------------------------------------------------- | -| `sync(Curve, t)` | copy keyframes from `Curve`, starting at time `t`; delete all keyframes after time `t` | -| `sync_after(Curve, t)` | copy keyframes from `Curve`, starting after time `t`; delete all keyframes after time `t` | +| Method | Description | +| ---------------- | -------------------------------------------------------------------------------------- | +| `sync(Curve, t)` | copy keyframes from `Curve`, starting at time `t`; delete all keyframes after time `t` | #### Discrete diff --git a/libopenage/curve/base_curve.h b/libopenage/curve/base_curve.h index 77be819139..8c56586637 100644 --- a/libopenage/curve/base_curve.h +++ b/libopenage/curve/base_curve.h @@ -283,7 +283,7 @@ template void BaseCurve::sync(const BaseCurve &other, const time::time_t &start) { // Copy keyframes between containers for t >= start - this->last_element = this->container.sync_after(other.container, start); + this->last_element = this->container.sync(other.container, start); // Check if this->get() returns the same value as other->get() for t = start // If not, insert a new keyframe at start @@ -302,7 +302,7 @@ void BaseCurve::sync(const BaseCurve &other, const std::function &converter, const time::time_t &start) { // Copy keyframes between containers for t >= start - this->last_element = this->container.sync_after(other.get_container(), converter, start); + this->last_element = this->container.sync(other.get_container(), converter, start); // Check if this->get() returns the same value as other->get() for t = start // If not, insert a new keyframe at start diff --git a/libopenage/curve/keyframe_container.h b/libopenage/curve/keyframe_container.h index c61ac5105a..1d7079498b 100644 --- a/libopenage/curve/keyframe_container.h +++ b/libopenage/curve/keyframe_container.h @@ -280,8 +280,8 @@ class KeyframeContainer { * Using the default value replaces ALL keyframes of \p this with * the keyframes of \p other. */ - iterator sync_after(const KeyframeContainer &other, - const time::time_t &start = std::numeric_limits::min()); + iterator sync(const KeyframeContainer &other, + const time::time_t &start = std::numeric_limits::min()); /** * Copy keyframes from another container (with a different element type) to this container. @@ -296,9 +296,9 @@ class KeyframeContainer { * the keyframes of \p other. */ template - iterator sync_after(const KeyframeContainer &other, - const std::function &converter, - const time::time_t &start = std::numeric_limits::min()); + iterator sync(const KeyframeContainer &other, + const std::function &converter, + const time::time_t &start = std::numeric_limits::min()); /** * Debugging method to be used from gdb to understand bugs better. @@ -512,8 +512,8 @@ KeyframeContainer::erase(KeyframeContainer::iterator e) { template typename KeyframeContainer::iterator -KeyframeContainer::sync_after(const KeyframeContainer &other, - const time::time_t &start) { +KeyframeContainer::sync(const KeyframeContainer &other, + const time::time_t &start) { // Delete elements after start time iterator at = this->last_before(start, this->end()); at = this->erase_after(at); @@ -536,9 +536,9 @@ KeyframeContainer::sync_after(const KeyframeContainer &other, template template typename KeyframeContainer::iterator -KeyframeContainer::sync_after(const KeyframeContainer &other, - const std::function &converter, - const time::time_t &start) { +KeyframeContainer::sync(const KeyframeContainer &other, + const std::function &converter, + const time::time_t &start) { // Delete elements after start time iterator at = this->last_before(start, this->end()); at = this->erase_after(at); diff --git a/libopenage/curve/tests/curve_types.cpp b/libopenage/curve/tests/curve_types.cpp index ab75fb5408..cab8473bc3 100644 --- a/libopenage/curve/tests/curve_types.cpp +++ b/libopenage/curve/tests/curve_types.cpp @@ -168,7 +168,7 @@ void curve_types() { // TODO: test c.insert_overwrite and c.insert_after KeyframeContainer c2; - c2.sync_after(c, 1); + c2.sync(c, 1); // now c2 should be [-inf: 0, 1: 15, 2: 20, 3: 25] TESTEQUALS(c2.last(0)->value, 0); TESTEQUALS(c2.last(1)->value, 15); From 2fa666329b31288133d24a047b6dfee0da3945c6 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 30 Jul 2023 17:19:32 +0200 Subject: [PATCH 12/49] doc: Game Entity. --- doc/code/game_simulation/game_entity.md | 195 +++++++++++++----------- doc/nyan/openage-lib.md | 17 ++- 2 files changed, 125 insertions(+), 87 deletions(-) diff --git a/doc/code/game_simulation/game_entity.md b/doc/code/game_simulation/game_entity.md index 1de1cac635..af6ef34391 100644 --- a/doc/code/game_simulation/game_entity.md +++ b/doc/code/game_simulation/game_entity.md @@ -1,116 +1,141 @@ # Game Entity +Game entities represent objects inside the game world. + +1. [Architecture](#architecture) +2. [Game Entity class](#game-entity-class) +3. [Component Data Storage](#component-data-storage) +4. [Control Flow](#control-flow) + 1. [System](#system) + 2. [Activities](#activities) + 3. [Manager](#manager) + + ## Architecture -![game entity UML]() +![Game entity UML](ASDF) + +Game entity mechanics are structured using the concept of *separation of concerns*. +This means that there is not one single `GameEntity` class that implements all data +and game logic. Instead, data and logic are separated into distinct engine components +that each have a dedicated, clearly-defined purpose. + +Data storage is handled by `Component` objects that manage references to the [nyan](/doc/nyan/nyan.md) +database and store runtime data on [curves](/doc/code/curves.md). Game logic is implemented +in *systems*, which are best described as independent functions that perform a specific action +on an entity. Event handling is done by the `GameEntityManager` which initiates and directs +the entity's individual control flow. -- major components - - game entity class - - components - - systems - - manager +It should be noted that while the terminology we use here is very similar to names +in the *Entity-Component-System* (ECS) architecture, you shouldn't think of the openage +game simulation as a traditional ECS-driven architecture. We merely use this terminology +because we can't think of anything better (and because we are too lazy to find better names). -- separation of concerns - - components = only data storage - - systems = only game logic - - manager/entity = handles to access data/initiate game logic ## Game Entity class -- content - - entity ID - - map of components - - manager for event handling - - render entity if should be animated (optional) +![Game entity class UML](ASDF) + +The `GameEntity` class primarily provides references to the game entity's unique ID, assigned +components, and event handling manager. If the game entity is supposed to be animated, +a [render entity](/doc/code/renderer/level2.md#updating-render-stages-from-the-gamestate) +can also be set. This allows requesting animations for the game entity. + +`GameEntity` follows the principle of *composition over inheritance*. Therefore, capabilities +and attributes of a game entity are defined by the assigned components rather than properties +of its class. The `GameEntity` class is merely a thin wrapper with a reference ID for +a set of components storing the actual data (via `Component` subclasses). Components +of the specific entity can be accessed via the `GameEntity` object's `get_component(..)` method. -- game entity should capture every use case for world object - - composition over inheritance - - game entity attributes/capabilities defined by components ## Component Data Storage -- dual role - - signify what game entity can be or do by component type (e.g. move) - - store data for said attribute - - provide common helper functions for interacting with data - - atomic: components never directly depend on another component to be assigned to the entity - -- differentiate between prsistent game data and runtime data - - Persistent game data - - unit stats as defined in the game/modpack files, e.g. movement speed - - usually does not change often during gameplay and changes are predictable - - managed as objects in nyan db - - openage nyan API object engine.ability.Ability - - component holds reference to nyan object ability - - data must always be fetched from nyan - - not all components manage persitant game data - - e.g. Position - - runtime data - - data that changes due to gameplay interactions, e.g. current health - - unpredictable - - managed inside the component on curves - - data structure that tracks values over time - - manipulated by accessing the curve and adding/removing keyframes - - or by helper method +For a description of the available components, check the [component reference](/doc/code/game_simulation/components.md). +![Component class UML](ASDF) -## Control Flow +Components are data storage objects for a game entity that also perform the dual role +of signifying what a game entity can do or is depending on their component type. Component +types are usually associated with a subclass of `Component`, e.g. `Move`. For example, assigning +a `Move` component to a game entity signifies that the game entity can perform move +actions during gameplay. Movement data relevant to the game entity is then also stored in this +component object. + +In general, component classes are designed to be atomic, i.e. they don't depend on other +components to be assigned to the game entity. While some components may have a close relationship, +e.g. `Move` and `Position`, it should never be assumed that all components are assigned +when operating on a game entity. -- doing stuff with the game entity components -- not implemented into game entity class / component - - separation of concerns - - data classes only store data - - control flow is sperate and can be adjusted for different purposes +Persistent game data, i.e. data that is the same in every game like unit stats, is not +managed by the component itself but in the game's [nyan](/doc/nyan/nyan.md) database. The component +only holds a reference to the nyan object associated with the component. Data from nyan +must be fetched via the [openage nyan interface](/doc/nyan/openage-lib.md). +Component types usually have a corresponding nyan object type in the nyan API, e.g. the `Move` +component corresponds to the `engine.ability.type.Move` nyan type. -- major core parts - - system - - activity - - manager +Runtime data, i.e. data that changes due to gameplay interactions, is stored on [curves](/doc/code/curves.md) +managed by the component object. Curves are keyframe-based time-value storages that track +changes to the data over time. Components can provide helper methods to interact with +the curve, e.g. `Position` provides `set_position` to insert a new keyframe on its +`position` data curve. + + +## Control Flow + +The control flow is organized into 3 core mechanisms: Systems, activities, and +managers. This again tries to achieve a *separation of concern* design to +make the game logic maintanable and extensible. ### System -- function that implements game logic - - implemented as static functions of a class with only static methods - - i.e. do not belong to a particular object - - manipulate the components data of one or more entities +For a description of the available systems, check the [system reference](/doc/code/game_simulation/systems.md). -- atomic (like components): focus on one particular action - - don't call other systems (unless its a subroutine) - - no hardcoded transitions from one system to the next, e.g. hardcode attack after move - - managed by activities instead +A *system* in openage is basically a function that operates on game entity +components. They are explicitely separated from game entity and component objects +to allow for more flexible implementation. In practice, systems are implemented as static +functions inside the `gamestate::system` namespace. + +Systems are *stateless*, so they don't remember anything about the game entity +components they operate on. If something about the execution state should be remembered, +it needs to be stored in a component. Additionally, system execution is always +immediate. This means that a system never waits for events or for a certain amount +of time. Waiting is handled by the [game entity manager](#manager) and the +[activity control flow](#activities) instead. + +When adding new systems to the engine, interdependencies to other systems should +be avoided at all cost, i.e. a system should never call another system directly. +Exceptions should only be made for direct subsystems implementing subroutines +or to avoid code redundancies. The reasoning behind this is that dependencies +between systems may quickly become unmanageable. -- ECS vs. event-based gamestate - - in ECS systems are called every tick - - event-based systems are called on events, so they only run when they need to - - heavy use of prediction due to curves logic - - only calculate the necessary keyframes - - e.g. constructing buildings only needs 2 keyframes for start and finish ### Activities -- connect systems together in a node graph to form a control flow for game entity - - models complex behaviour and action chains - - usually, game entity's of the same type use the same node graph - - e.g. all spearman have the same general behaviour - - but can be changed via components +![Activity Example](ASDF) + +*Activities* connect systems together in a node graph to describe the overall control flow +for a game entity. In openage, activity node graphs are used to model the complex behaviour +behaviour and action chains of RTS game entities, while also allowing the behaviour to +be as configurable as possible. One could also think of activities as a behaviour graph +where paths are taken based on the inputs a game entity receives. The architecture +of the activity control flow is described in more detail in the +[activity control flow documentation](/doc/code/game_simulation/activity.md). + +A game entity's current activity state is stored in its `Activity` component. This component +holds a reference to the activity node graph used by the entity as well as the +last visited node. This node describes which action/behavioural state the +game entity currently is in. -- workflow - - nodes in graph denote what systems should be called for the game entity - - or decide where to go next based on conditions - - game entity's "action state" defined by the current node in the graph - - activity system follows along the nodes in the graph to execute game logic on an entity - - register callback events +Advancement to the next node can be initiated in several ways, depending on the +[node type](/doc/code/game_simulation/activity.md#node-types) of the current node. +It can happen automatically or be triggered by an event. In the latter case, +the event is handled by the `GameEntityManager` which calls an activity *system* +that processes the event to choose the next node. -- action state stored in ActivityComponent - - stores reference to full activity graph used by game entity - - current node in graph - - events that are currently waited for - - resume execution from current node when callback is received +![Activity Workflow](ASDF) -- see more activities.md ### Manager -- listens for events for the game entity -- takes appropriate action when called back by events - - re-enter activity system +The game entity listens for events that target the entity and takes the +associated action depending on the event type. diff --git a/doc/nyan/openage-lib.md b/doc/nyan/openage-lib.md index 69f7efda99..0525ac3ba6 100644 --- a/doc/nyan/openage-lib.md +++ b/doc/nyan/openage-lib.md @@ -1,10 +1,19 @@ -openage nyan library -==================== +# openage nyan library The openage engine defines all kinds of `NyanObject`s so the nyan interpreter can verify data integrity. Together, the defined objects provide the API that can be used by the games running on the engine. This interface will be described here. +1. [Design principles](#design-principles) +2. [API objects](#api-objects) + 1. [Entity](#entity) + 2. [GameEntity](#gameentity) + 3. [Ability](#ability) + 4. [Modifier](#modifier) + 5. [Effect](#effect) +3. [C++ Interface](#c-interface) + + ## Design principles In addition to the [nyan design goals](https://github.com/SFTtech/nyan/blob/master/doc/nyan.md#design-goals), the openage mod API follows its own design principles. It is recommended to follow these principles when extending the API. @@ -80,3 +89,7 @@ By default, modifiers apply to the abilities of the game entity that stores them There are two major types of effects: Discrete and continuous. Discrete effects happen at a *specific point in time*, while continuous effects are applied *over time* at a per second rate. Any discrete effect can be combined with every other discrete effect. The same applies for continuous effects. For an effect to apply, the effector's `Effect` needs a corresponding `Resistance` object on the resistor's side. Otherwise the effect is not evaluated. + +## C++ Interface + +ASDF From c4f280a86051b5ec9ec23089aa17a4231dc4e9e8 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 30 Jul 2023 18:20:53 +0200 Subject: [PATCH 13/49] doc: Components reference. --- doc/code/game_simulation/components.md | 155 +++++++++++++++++++------ 1 file changed, 117 insertions(+), 38 deletions(-) diff --git a/doc/code/game_simulation/components.md b/doc/code/game_simulation/components.md index 4783119964..5630426a4c 100644 --- a/doc/code/game_simulation/components.md +++ b/doc/code/game_simulation/components.md @@ -3,49 +3,128 @@ Overview of the built-in game entity components in the game simulation. 1. [Internal](#internal) + 1. [Activity](#activity) + 2. [CommandQueue](#commandqueue) + 3. [Owner](#owner) + 4. [Position](#position) 2. [API](#api) + 1. [Idle](#idle) + 2. [Live](#live) + 3. [Move](#move) + 4. [Turn](#turn) ## Internal -- components used by every game entity -- no corresponding nyan API object - -- Activity - - reference to the top-level activity for the game entity - - current node in activity - - list of scheduled events the entity waits for to continue the control flow -- CommandQueue - - stores commands from the input system for the game entity - - commands are basically signals from the input system to handle conditional branches in the activity control flow - - commands have type - - payload depends on type -- Owner - - stores ID of player who owns the unit - - allow basic access control -- Position - - 3D position of game entity in the world - - angle of game entity relative to camera, clockwise rotation +Internal components do not have a corresponding nyan API object and thus only +store runtime data. + +### Activity + +![Activity Component UML](ASDF) + +The `Activity` component stores a reference to the top-level activity for the +game entity. Essentially, this gives access to the entire activity node graph +used by the entity. + +Additionally, the current activity state is stored on a discrete curve that +contains the last visited node. + +`Activity` also stores the handles of events initiated by the activity system +for advancing to the next node. Once the next node is visited, these events +should be canceled via the `cancel_events(..)` method. + + +### CommandQueue + +![CommandQueue Component UML](ASDF) + +The `CommandQueue` component stores commands for the game entity in a [queue curve container](/doc/code/curves.md#queue). + +Commands in the queue use `Command` class derivatives which specify a command type +and payload for the command. + + +### Owner + +![Owner Component UML](ASDF) + +The `Owner` component stores the ID of the player who owns the game entity. + + +### Position + +![Position Component UML](ASDF) + +The `Position` component stores the location and direction of the game entity +inside the game world. + +The 3D position of the game entity is stored on a continuous curve with value type +`phys3`. + +Directions are stored as angles relative to the camera vector using clock-wise +rotation. Here are some example values for reference to see how that works in +practice: + +| Angle (degrees) | Direction | +| --------------- | --------------------- | +| 0 | look towards camera | +| 90 | look left | +| 180 | look away from camera | +| 270 | look right | + +Angles are stored on a segmented curve. ## API -- components which are assigned depending on whether the game entity defnition has a matching nyan API object (engine.ability.Ability) - -- Idle - - nyan API: engine.ability.type.Idle - - represents the ingame "idle" state of a unit, i.e. where it is not active - - only used to access animations, sounds from nyan db -- Live - - nyan API: engine.ability.type.Live - - stores attributes of entity and their current values, e.g. health -- Move - - nyan API: engine.ability.type.Move - - allows the entity to move - - required for issuing move commands - - stores nothing - - speed, animations, sounds from nyan db -- Turn - - nyan API: engine.ability.type.Turn - - allows the entity to turn, i.e. change its angle - - stores nothing - - speed, animations, sounds from nyan db +API components have a corresponding nyan API object of type `engine.ability.Ability` defined +in the nyan API. This API object can be retrieved using the `get_ability(..)` method of the +component. + +### Idle + +![Idle Component UML](ASDF) + +**nyan API object:** [`engine.ability.type.Idle`](/doc/nyan/api_reference/reference_ability.md#abilitytypeidle) + +The `Idle` component represents the ingame "idle" state of the game entity, i.e. when +it is doing nothing. + +The component stores no runtime data. + + +### Live + +![Live Component UML](ASDF) + +**nyan API object:** [`engine.ability.type.Live`](/doc/nyan/api_reference/reference_ability.md#abilitytypelive) + +The `Live` component represents the game entity's ability to have attributes (e.g. health). + +An attribute's maximum limit is stored in the nyan API object, while +the game entity's current attribute values are stored in the component +on a discrete curve. + + +### Move + +![Move Component UML](ASDF) + +**nyan API object:** [`engine.ability.type.Move`](/doc/nyan/api_reference/reference_ability.md#abilitytypemove) + +The `Move` component represents the game entity's ability to move in the game world. +This also allows moving the game entity with move commands. + +The component stores no runtime data. + + +### Turn + +![Turn Component UML](ASDF) + +**nyan API object:** [`engine.ability.type.Turn`](/doc/nyan/api_reference/reference_ability.md#abilitytypeturn) + +The `Turn` component represents the game entity's ability to change directions in the game world. +Turning is implicitely required for moving but it also works on its own. + +The component stores no runtime data. From b63a62ac6e02758c7e06444c4b311e77651669ab Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 30 Jul 2023 20:51:32 +0200 Subject: [PATCH 14/49] doc: Add missing text. --- doc/code/renderer/level1.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/code/renderer/level1.md b/doc/code/renderer/level1.md index 9d163d3d69..e7ed4ed933 100644 --- a/doc/code/renderer/level1.md +++ b/doc/code/renderer/level1.md @@ -4,19 +4,18 @@ Low-level renderer for communicating with the OpenGL and Vulkan APIs. ## Overview -1. [Level 1 Renderer](#level-1-renderer) - 1. [Overview](#overview) - 2. [Architecture](#architecture) - 3. [Basic Usage](#basic--usage) - 1. [Window/Renderer Creation](#windowrenderer-creation) - 2. [Adding a Shader Program](#adding-a-shader-program) - 3. [Creating a Renderable](#creating-a-renderable) - 4. [Rendering and Displaying the Result](#rendering-and-displaying-the-result) - 4. [Advanced Usage](#advanced-usage) - 1. [Framebuffers / Multiple Render Passes](#framebuffers--multiple-render-passes) - 2. [Complex Geometry](#complex-geometry) - 3. [Uniform Buffers](#uniform-buffers) - 5. [Thread-safety](#thread-safety) +1. [Overview](#overview) +2. [Architecture](#architecture) +3. [Basic Usage](#basic--usage) + 1. [Window/Renderer Creation](#windowrenderer-creation) + 2. [Adding a Shader Program](#adding-a-shader-program) + 3. [Creating a Renderable](#creating-a-renderable) + 4. [Rendering and Displaying the Result](#rendering-and-displaying-the-result) +4. [Advanced Usage](#advanced-usage) + 1. [Framebuffers / Multiple Render Passes](#framebuffers--multiple-render-passes) + 2. [Complex Geometry](#complex-geometry) + 3. [Uniform Buffers](#uniform-buffers) +5. [Thread-safety](#thread-safety) ## Architecture @@ -46,7 +45,8 @@ abstract interface of the renderer renderer to make them usable with graphics ha ## Basic Usage -- see demos in libopenage/renderer/demo +Code examples can be found in the [renderer demos](/libopenage/renderer/demo/). +See the [testing docs](/doc/code/testing.md#python-demos) on how to try them out. ### Window/Renderer Creation From 16237113bb9e25bf48746b18ef7689bac09da670 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 31 Jul 2023 20:16:40 +0200 Subject: [PATCH 15/49] doc: Systems reference. --- doc/code/game_simulation/systems.md | 47 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/doc/code/game_simulation/systems.md b/doc/code/game_simulation/systems.md index 552e8d582b..672d8b8659 100644 --- a/doc/code/game_simulation/systems.md +++ b/doc/code/game_simulation/systems.md @@ -7,30 +7,29 @@ Overview of the built-in systems in the game simulation. ## Move -- handles movement actions for game entities -- move_command - - move the entity using a move command - - uses movement parameters from command to call move_default -- move default - - move the game entity to a new position - - requires Move and Turn components - - workflow - 1. Turn - 1. calculate new angle in move direction - 2. calculate turn time from new angle and turn speed - 3. add keyframe for new angle at t + turn time - 2. Move - 1. calculate move time from path distance and move speed - 2. add keyframe for old position at t and for new position at t + turn time + move time - 3. Update animations if animated - - returns duration of movement (t + turn time + move time) +![Move systems class UML](ASDF) + +Handles movement actions for game entities. + +`move_default(..)` moves a game entity to the new position specified in the function +call. This requires the game entity to have the `Move` and `Turn` components. +Waypoints for the exact path are fetched from the pathfinder. +For every straight path between waypoints, the game entity is turned first, then +moved (same as in *Age of Empires*). If an animation is available for the `Move` +component, this animation is forwarded as the game entity's active animation to the +renderer. The function returns the cumulative time of all turn and movement actions +initiated by this function. + +`move_command(..)` processes the payload from a move *command* to call `move_default(..)` +with the payload parameters. + ## Idle -- handles idle action for game entities -- since idle means doing nothing, this only updates animations and sounds -- idle - - requires Idle component - - workflow - 1. Update animations if animated - - returns duration of action (always 0) +![Idle systems class UML](ASDF) + +Handles idle actions for game entities. + +`idle(..)` updates the animation of the game entity. This requires the game +entity to have the `Idle` component. The function returns a time of 0 since +no actionsconsuming simulation time are taken. From 0d82a281bb17990174e45419a23546a21be4050e Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 1 Aug 2023 00:54:47 +0200 Subject: [PATCH 16/49] doc: Activity reference. --- doc/code/game_simulation/activity.md | 100 +++++++----------- libopenage/gamestate/activity/event_node.cpp | 12 +-- libopenage/gamestate/activity/event_node.h | 18 ++-- libopenage/gamestate/activity/task_node.cpp | 10 +- libopenage/gamestate/activity/task_node.h | 14 +-- .../gamestate/activity/task_system_node.h | 4 +- libopenage/gamestate/activity/tests.cpp | 24 ++--- libopenage/gamestate/activity/types.h | 6 +- libopenage/gamestate/activity/xor_node.cpp | 8 +- libopenage/gamestate/activity/xor_node.h | 14 +-- libopenage/gamestate/entity_factory.cpp | 8 +- libopenage/gamestate/system/activity.cpp | 16 +-- 12 files changed, 106 insertions(+), 128 deletions(-) diff --git a/doc/code/game_simulation/activity.md b/doc/code/game_simulation/activity.md index 451a62c9f9..45c891fd8d 100644 --- a/doc/code/game_simulation/activity.md +++ b/doc/code/game_simulation/activity.md @@ -1,80 +1,58 @@ # Activity Control Flow -- Configurable control flow for game entities -- using a node graph +The *activity control flow* is openage's method to make complex game entity behaviour +configurable. 1. [Motivation](#motivation) 2. [Architecture](#architecture) 3. [Node Types](#node-types) -4. [Activity](#activity) ## Motivation -- Commands in RTS often result in multiple actions that have to be taken - - command does not translate to single atomic action - - action chains for most commands, e.g. attack command results in actions move + attack +Unit behaviour in RTS games can get very complex. In many cases, units are not +just controlled by commands but other automated mechanisms, e.g. attacking enemy +units in the line of sight. Furthermore, commands do not always translate to a single +well-defined action. For example, an attack command usually results in a move action +and a subsequent attack action. Some commands may even execute different actions depending +on context. -- action chains can get complex - - branching paths depending on conditions - - waiting for events or triggers - - parallel actions - - canceling an action requires clean up +All this means that we cannot view the control flow of a unit as a simple mapping of +command to action as is done in other games. Instead, we have to treat unit behaviour +as a complex chain of actions with branching paths, wait states for events, triggers +and feedback loops. Unless we want every command to be a hardcoded action chain, managing +this complexity is key to making unit behaviour configurable. -- control flow of commands and actions should not be hardcoded! ## Architecture -- control flow modeled as a directed node graph - - following along the paths in the graph tells the game entity what to do - - current node == current action - - paths == connections to the next action - - activity == (sub)graph with start and end node - - defines a scope or reusable -![graph example]() +Game entity control flows in openage are modelled as directed node graph, so-called *activities*. +Nodes in the graph correspond to actions that execute for the game entity or conditional queries +and event triggers that indicate which path to take next. By traversing the node graph along +its paths, the game entities actions are determined. The currently visited node in the graph +corresponds to the current action of a unit. -- inpired by BPMN flow graph - - you don't need to know BPMN because we explain everything relevant in here - - but you can use BPMN draw tools to make graphs +Activities are reusable, i.e. they are intended to be shared by many game entities Usually, +all game entities of the same type should share the same behaviour, so they get assigned +the same activity node graph. + +An activity can also be represented visually like this: + +![graph example](ASDF) + +The design is heavily inpired by the [BPMN](https://en.wikipedia.org/wiki/Business_Process_Model_and_Notation) +representation. You don't need to know BPMN to understand the activity control flow because +we explain everything important about the graphs in our documentation. However, +you can use available [BPMN tools](https://bpmn.io/) to draw activity node graphs. ## Node Types -- Start - - 0 inputs / 1 output - - no action - - signifies start of activity -- End - - 1 input / 0 outputs - - no action - - signifies end of activity -- Task System - - 1 input / 1 output - - references built-in system that should be executed when visiting - - signifies an action -- Task - - 1 input / 1 output - - references a custom function that should be exectuted when visiting - - signifies an action - - for scripting or debugging -- Exclusive Event Gateway - - 1 input / 1+ outputs - - register event callback(s) when visiting - - choose 1 output when event callback s triggered - - signifies a branch -- Exclusive Gateway - - 1 input / 1+ outputs - - choose 1 output depending on condition function - - i comparison to event gateway, conditions are checked immediately - - signifies a branch -- activity - - 1 input / 1 output - - start a subactivity - - signifies a nested action - - -## Activity - -- encapsulates a node graph with start and end -- defines a scope of interaction -- allows reuse of common control flows - - e.g. movement + +| Type | Symbol | Inputs | Outputs | Description | +| ---------------- | ------ | ------ | ------- | ------------------------- | +| `START` | [ASDF] | 0 | 1 | Start of activity | +| `END` | [ASDF] | 1 | 0 | End of activity | +| `TASK_SYSTEM` | [ASDF] | 1 | 1 | Run built-in system | +| `TASK_CUSTOM` | [ASDF] | 1 | 1 | Run custom function | +| `XOR_EVENT_GATE` | [ASDF] | 1 | 1+ | Wait for event and branch | +| `XOR_GATE` | [ASDF] | 1 | 1+ | Branch on condition | diff --git a/libopenage/gamestate/activity/event_node.cpp b/libopenage/gamestate/activity/event_node.cpp index c4c8b3d44c..3d28e007a3 100644 --- a/libopenage/gamestate/activity/event_node.cpp +++ b/libopenage/gamestate/activity/event_node.cpp @@ -7,7 +7,7 @@ namespace openage::gamestate::activity { -EventNode::EventNode(node_id id, +XorEventGate::XorEventGate(node_id id, node_label label, const std::vector> &outputs, event_primer_func_t primer_func, @@ -17,23 +17,23 @@ EventNode::EventNode(node_id id, next_func{next_func} { } -void EventNode::add_output(const std::shared_ptr &output) { +void XorEventGate::add_output(const std::shared_ptr &output) { this->outputs.emplace(output->get_id(), output); } -void EventNode::set_primer_func(event_primer_func_t primer_func) { +void XorEventGate::set_primer_func(event_primer_func_t primer_func) { this->primer_func = primer_func; } -void EventNode::set_next_func(event_next_func_t next_func) { +void XorEventGate::set_next_func(event_next_func_t next_func) { this->next_func = next_func; } -event_primer_func_t EventNode::get_primer_func() const { +event_primer_func_t XorEventGate::get_primer_func() const { return this->primer_func; } -event_next_func_t EventNode::get_next_func() const { +event_next_func_t XorEventGate::get_next_func() const { return this->next_func; } diff --git a/libopenage/gamestate/activity/event_node.h b/libopenage/gamestate/activity/event_node.h index 1ddde632ab..a753682b68 100644 --- a/libopenage/gamestate/activity/event_node.h +++ b/libopenage/gamestate/activity/event_node.h @@ -80,10 +80,10 @@ static const event_next_func_t no_next = [](const time::time_t &, /** * Waits for an event to be executed before continuing the control flow. */ -class EventNode : public Node { +class XorEventGate : public Node { public: /** - * Create a new event node. + * Create a new exclusive event gateway. * * @param id Unique identifier for this node. * @param label Human-readable label (optional). @@ -91,15 +91,15 @@ class EventNode : public Node { * @param primer_func Function to create and register the event. * @param next_func Function to decide which node to visit after the event is handled. */ - EventNode(node_id id, - node_label label = "Event", - const std::vector> &outputs = {}, - event_primer_func_t primer_func = no_event, - event_next_func_t next_func = no_next); - virtual ~EventNode() = default; + XorEventGate(node_id id, + node_label label = "Event", + const std::vector> &outputs = {}, + event_primer_func_t primer_func = no_event, + event_next_func_t next_func = no_next); + virtual ~XorEventGate() = default; inline node_t get_type() const override { - return node_t::EVENT_GATEWAY; + return node_t::XOR_EVENT_GATE; } /** diff --git a/libopenage/gamestate/activity/task_node.cpp b/libopenage/gamestate/activity/task_node.cpp index d1a9386812..36b5de6663 100644 --- a/libopenage/gamestate/activity/task_node.cpp +++ b/libopenage/gamestate/activity/task_node.cpp @@ -8,7 +8,7 @@ namespace openage::gamestate::activity { -TaskNode::TaskNode(node_id id, +TaskCustom::TaskCustom(node_id id, node_label label, const std::shared_ptr &output, task_func_t task_func) : @@ -19,20 +19,20 @@ TaskNode::TaskNode(node_id id, } } -void TaskNode::add_output(const std::shared_ptr &output) { +void TaskCustom::add_output(const std::shared_ptr &output) { this->outputs.clear(); this->outputs.emplace(output->get_id(), output); } -void TaskNode::set_task_func(task_func_t task_func) { +void TaskCustom::set_task_func(task_func_t task_func) { this->task_func = task_func; } -task_func_t TaskNode::get_task_func() const { +task_func_t TaskCustom::get_task_func() const { return this->task_func; } -node_id TaskNode::get_next() const { +node_id TaskCustom::get_next() const { return (*this->outputs.begin()).first; } diff --git a/libopenage/gamestate/activity/task_node.h b/libopenage/gamestate/activity/task_node.h index c8329ab051..0efd3f784b 100644 --- a/libopenage/gamestate/activity/task_node.h +++ b/libopenage/gamestate/activity/task_node.h @@ -29,7 +29,7 @@ static const task_func_t no_task = [](const time::time_t &, /** * Executes a function when visited. */ -class TaskNode : public Node { +class TaskCustom : public Node { public: /** * Create a new task node. @@ -39,14 +39,14 @@ class TaskNode : public Node { * @param task_func Action to perform when visiting this node (can be set later). * @param output Next node to visit (optional). */ - TaskNode(node_id id, - node_label label = "Task", - const std::shared_ptr &output = nullptr, - task_func_t task_func = no_task); - virtual ~TaskNode() = default; + TaskCustom(node_id id, + node_label label = "TaskCustom", + const std::shared_ptr &output = nullptr, + task_func_t task_func = no_task); + virtual ~TaskCustom() = default; inline node_t get_type() const override { - return node_t::TASK; + return node_t::TASK_CUSTOM; } /** diff --git a/libopenage/gamestate/activity/task_system_node.h b/libopenage/gamestate/activity/task_system_node.h index 834ee321f7..8f9e6b1163 100644 --- a/libopenage/gamestate/activity/task_system_node.h +++ b/libopenage/gamestate/activity/task_system_node.h @@ -12,13 +12,13 @@ namespace openage::gamestate::activity { /** - * Similar to the tas node, but runs a built-in system function + * Similar to the custom task node, but runs a built-in system function * when visited. */ class TaskSystemNode : public Node { public: /** - * Create a new task system node. + * Create a new custom task system node. * * @param id Unique identifier for this node. * @param label Human-readable label (optional). diff --git a/libopenage/gamestate/activity/tests.cpp b/libopenage/gamestate/activity/tests.cpp index d19e552030..7588e5a9b0 100644 --- a/libopenage/gamestate/activity/tests.cpp +++ b/libopenage/gamestate/activity/tests.cpp @@ -109,8 +109,8 @@ class TestActivityHandler : public event::OnceEventHandler { const std::shared_ptr activity_flow(const std::shared_ptr ¤t_node) { auto current = current_node; - if (current->get_type() == activity::node_t::EVENT_GATEWAY) { - auto node = std::static_pointer_cast(current); + if (current->get_type() == activity::node_t::XOR_EVENT_GATE) { + auto node = std::static_pointer_cast(current); auto event_next = node->get_next_func(); auto next_id = event_next(0, nullptr, nullptr, nullptr); current = node->next(next_id); @@ -129,21 +129,21 @@ const std::shared_ptr activity_flow(const std::shared_ptr(current); + case activity::node_t::TASK_CUSTOM: { + auto node = std::static_pointer_cast(current); auto task = node->get_task_func(); task(0, nullptr); auto next_id = node->get_next(); current = node->next(next_id); } break; - case activity::node_t::XOR_GATEWAY: { - auto node = std::static_pointer_cast(current); + case activity::node_t::XOR_GATE: { + auto node = std::static_pointer_cast(current); auto condition = node->get_condition_func(); auto next_id = condition(0, nullptr); current = node->next(next_id); } break; - case activity::node_t::EVENT_GATEWAY: { - auto node = std::static_pointer_cast(current); + case activity::node_t::XOR_EVENT_GATE: { + auto node = std::static_pointer_cast(current); auto event_primer = node->get_primer_func(); event_primer(0, nullptr, nullptr, nullptr); @@ -181,10 +181,10 @@ void activity_demo() { // create the nodes in the graph // connections are created further below auto start = std::make_shared(0); - auto task1 = std::make_shared(1); - auto xor_node = std::make_shared(2); - auto event_node = std::make_shared(3); - auto task2 = std::make_shared(4); + auto task1 = std::make_shared(1); + auto xor_node = std::make_shared(2); + auto event_node = std::make_shared(3); + auto task2 = std::make_shared(4); auto end = std::make_shared(5); // create an activity manager that controls the flow diff --git a/libopenage/gamestate/activity/types.h b/libopenage/gamestate/activity/types.h index 0315e3d672..ac2189e5ab 100644 --- a/libopenage/gamestate/activity/types.h +++ b/libopenage/gamestate/activity/types.h @@ -11,9 +11,9 @@ namespace openage::gamestate::activity { enum class node_t { START, END, - EVENT_GATEWAY, - XOR_GATEWAY, - TASK, + XOR_EVENT_GATE, + XOR_GATE, + TASK_CUSTOM, TASK_SYSTEM, }; diff --git a/libopenage/gamestate/activity/xor_node.cpp b/libopenage/gamestate/activity/xor_node.cpp index ca703c288a..644362f877 100644 --- a/libopenage/gamestate/activity/xor_node.cpp +++ b/libopenage/gamestate/activity/xor_node.cpp @@ -7,7 +7,7 @@ namespace openage::gamestate::activity { -ConditionNode::ConditionNode(node_id id, +XorGate::XorGate(node_id id, node_label label, const std::vector> &outputs, condition_func_t condition_func) : @@ -15,15 +15,15 @@ ConditionNode::ConditionNode(node_id id, condition_func{condition_func} { } -void ConditionNode::add_output(const std::shared_ptr &output) { +void XorGate::add_output(const std::shared_ptr &output) { this->outputs.emplace(output->get_id(), output); } -void ConditionNode::set_condition_func(condition_func_t condition_func) { +void XorGate::set_condition_func(condition_func_t condition_func) { this->condition_func = condition_func; } -condition_func_t ConditionNode::get_condition_func() const { +condition_func_t XorGate::get_condition_func() const { return this->condition_func; } diff --git a/libopenage/gamestate/activity/xor_node.h b/libopenage/gamestate/activity/xor_node.h index c8996e606b..39efca6209 100644 --- a/libopenage/gamestate/activity/xor_node.h +++ b/libopenage/gamestate/activity/xor_node.h @@ -31,7 +31,7 @@ static const condition_func_t no_condition = [](const time::time_t &, /** * Chooses one of its output nodes based on a condition. */ -class ConditionNode : public Node { +class XorGate : public Node { public: /** * Creates a new condition node. @@ -42,14 +42,14 @@ class ConditionNode : public Node { * @param condition_func Function that determines which output node is chosen (can be set later). * This must be a valid node ID of one of the output nodes. */ - ConditionNode(node_id id, - node_label label = "Condition", - const std::vector> &outputs = {}, - condition_func_t condition_func = no_condition); - virtual ~ConditionNode() = default; + XorGate(node_id id, + node_label label = "ExclusiveGateway", + const std::vector> &outputs = {}, + condition_func_t condition_func = no_condition); + virtual ~XorGate() = default; inline node_t get_type() const override { - return node_t::XOR_GATEWAY; + return node_t::XOR_GATE; } /** diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index b8794e80a3..f6aa7f4fc9 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -65,11 +65,11 @@ namespace openage::gamestate { std::shared_ptr create_test_activity() { auto start = std::make_shared(0); auto idle = std::make_shared(1, "Idle"); - auto condition_moveable = std::make_shared(2); - auto wait_for_command = std::make_shared(3); - auto condition_command = std::make_shared(4); + auto condition_moveable = std::make_shared(2); + auto wait_for_command = std::make_shared(3); + auto condition_command = std::make_shared(4); auto move = std::make_shared(5, "Move"); - auto wait_for_move = std::make_shared(6); + auto wait_for_move = std::make_shared(6); auto end = std::make_shared(7); start->add_output(idle); diff --git a/libopenage/gamestate/system/activity.cpp b/libopenage/gamestate/system/activity.cpp index 600a0e4622..240b5c7c50 100644 --- a/libopenage/gamestate/system/activity.cpp +++ b/libopenage/gamestate/system/activity.cpp @@ -41,10 +41,10 @@ void Activity::advance(const std::shared_ptr &entity, } // TODO: this check should be moved to a more general pre-processing section - if (current_node->get_type() == activity::node_t::EVENT_GATEWAY) { + if (current_node->get_type() == activity::node_t::XOR_EVENT_GATE) { // returning to a event gateway means that the event has been triggered // move to the next node here - auto node = std::static_pointer_cast(current_node); + auto node = std::static_pointer_cast(current_node); auto event_next = node->get_next_func(); auto next_id = event_next(start_time, entity, loop, state); current_node = node->next(next_id); @@ -66,8 +66,8 @@ void Activity::advance(const std::shared_ptr &entity, // TODO: if activities are nested, advance to parent activity stop = true; } break; - case activity::node_t::TASK: { - auto node = std::static_pointer_cast(current_node); + case activity::node_t::TASK_CUSTOM: { + auto node = std::static_pointer_cast(current_node); auto task = node->get_task_func(); task(start_time, entity); auto next_id = node->get_next(); @@ -80,14 +80,14 @@ void Activity::advance(const std::shared_ptr &entity, auto next_id = node->get_next(); current_node = node->next(next_id); } break; - case activity::node_t::XOR_GATEWAY: { - auto node = std::static_pointer_cast(current_node); + case activity::node_t::XOR_GATE: { + auto node = std::static_pointer_cast(current_node); auto condition = node->get_condition_func(); auto next_id = condition(start_time, entity); current_node = node->next(next_id); } break; - case activity::node_t::EVENT_GATEWAY: { - auto node = std::static_pointer_cast(current_node); + case activity::node_t::XOR_EVENT_GATE: { + auto node = std::static_pointer_cast(current_node); auto event_primer = node->get_primer_func(); auto evs = event_primer(start_time + event_wait_time, entity, loop, state); for (auto &ev : evs) { From fd5b376f810155850b0fcf75601dc70feeea1108 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 2 Aug 2023 02:50:30 +0200 Subject: [PATCH 17/49] doc: Game simulation. --- doc/code/game_simulation/README.md | 103 +++++++++++++---------------- 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/doc/code/game_simulation/README.md b/doc/code/game_simulation/README.md index 287f52da64..40ac497962 100644 --- a/doc/code/game_simulation/README.md +++ b/doc/code/game_simulation/README.md @@ -1,66 +1,55 @@ # Game Simulation -- Components for simulating gameplay +*Game simulation* is the engine subsystem that implements game mechanics and data structures +used for gameplay. +The subsystem only contains components that are strictly necessary to simulate a game. Everything else, +e.g. the [input system](/doc/code/input/README.md), [rendering](/doc/code/renderer/README.md), +networking or scripting, are handled as separate subsystems. 1. [Architecture](#architecture) - +2. [Workflow](#workflow) ## Architecture -- Hierarchy - - game simulation - - game - - universe - - terrain - - world - - game entity - - activity manager - - components - - systems - - -- GameSimulation - - initialized by engine - - controls overarching control parameters - - event loop - - time loop - - factory for creating game entity - - mod manager - - game - - runs the main event loop - - advances game by executing events for time t from time loop - -- Game - - manages current game session - - store information about the running game - - settings - - game data (from nyan database) - - current game state - - players - - game entities - - other objects - - universe - -- universe - - manages "physical" objects inside the game - - terrain - - game entities (via World reference) - - may be integrated into gamestate class in later versions - -- world - - manages game entities inside the game - - may be integrated into gamestate class in later versions - -- terrain - - manages the terrain in the game world - - may be integrated into gamestate class in later versions - -- game entity - - object inside the game world (e.g. unit/building/tree/ambience) - - capabilties defined as components - - game data from nyan db - - runtime data - - controlled by events handled by manager class - - altered/controlled by systems +![Game simulation overview UML](ASDF) + +The game simulation is instantiated from the main thread via a `GameSimulation` object. +This object controls the game loop, the current game session, and other simulation +parameters necessary to run the game. + +The `Game` class represents a game session. Its instances store information about the +gamestate via a `GameState` object which includes references to objects inside the game world +(e.g. players or game entities). It also contains gameplay settings for the current session. + +"Physical" objects in the game world are represented by `Terrain` and `GameEntity`. `Terrain` +is used to model the underlying map terrain, while [`GameEntity`](/doc/code/game_simulation/game_entity.md) +is used for every type of world object (e.g. units, buildings, trees, resources, ambience). + + +## Workflow + +![Game simulation loop](ASDF) + +To initiate a game, a `GameSimulation` object must be created. This is usually done by +the `Engine` object in the main thread. On initialization, the game simulation automatically sets up + several subcomponents required for running the game such as: + +* [Event loop](/doc/code/event_system.md) for executing events +* Entity factory for creating game entities +* Mod manager for detecting/loading [openage modpacks](/doc/media/openage/modpacks.md) + +After the `GameSimulation` object is initialized, modpacks that should be loaded in +the game session can be passed via the `set_modpacks(..)` method. + +The game simulation loop is started using the `run()` method of the `GameSimulation` object. +`run()` should be run in its own thread with no other looping subsystems present. Before +the simulation loop is entered, the simulation sets up a new game session (as a `Game` +object). This loads the modpacks specified in the `set_modpacks(..)` method into the +session. + +The logic of the simulation loop is quite simple as most of the execution happens in the +[event system](/doc/code/event_system.md). In every loop iteration, the current simulation time is fetched from the +[time subsystem](/doc/code/time.md). This time value is then passed to the simulation's +event loop which executes all events queued until this time. From a8bfb074aca9f68f642c1b6d0f653b884a301a67 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 2 Aug 2023 15:59:55 +0200 Subject: [PATCH 18/49] doc: Add 'versionstr' field to modpack info format. Helps distinguishing between openage modpack versions and the versions of converted/external mods. --- doc/media/openage/modpack_definition_file.md | 101 +++++++++--------- libopenage/assets/modpack.cpp | 3 + libopenage/assets/modpack.h | 1 + .../export/formats/modpack_info.py | 11 +- .../conversion/aoc/modpack_subprocessor.py | 2 +- .../conversion/de2/modpack_subprocessor.py | 2 +- .../conversion/hd/modpack_subprocessor.py | 2 +- .../conversion/ror/modpack_subprocessor.py | 2 +- .../conversion/swgbcc/modpack_subprocessor.py | 2 +- 9 files changed, 69 insertions(+), 57 deletions(-) diff --git a/doc/media/openage/modpack_definition_file.md b/doc/media/openage/modpack_definition_file.md index c4549f4cc9..a9ff6d6a41 100644 --- a/doc/media/openage/modpack_definition_file.md +++ b/doc/media/openage/modpack_definition_file.md @@ -1,6 +1,6 @@ # Modpack Definition File -**Format Version:** 1 +**Format Version:** 2 The modpack definition file is the header file for [modpacks](modpacks.md). @@ -22,54 +22,55 @@ find this specification document. The following parameters have to be specified. -Parameter | Data Type | Optional | Description ----------------------|-----------|----------|------------ -`file_version` | String | No | Version of the modpack definition file format. +| Parameter | Data Type | Optional | Description | +| -------------- | --------- | -------- | ---------------------------------------------- | +| `file_version` | String | No | Version of the modpack definition file format. | ## [info] Section `[info]` contains general information about the modpack. -Parameter | Data Type | Optional | Description --------------------|---------------|----------|------------ -`packagename` | String | No | Name of the modpack. -`version` | String | No | Version number. -`repo` | String | Yes | Name of the repo where the package is hosted. -`alias` | String | Yes | Alias of the modpack. Aliases can be used for replacing other modpacks. -`title` | String | Yes | Title used in UI. -`description` | String | Yes | Path to a file with a short description (max 500 chars). -`long_description` | String | Yes | Path to a file with a detailed description. -`url` | String | Yes | Link to the modpack's website. -`license` | Array[String] | Yes | License(s) of the modpack. +| Parameter | Data Type | Optional | Description | +| ------------------ | ------------- | -------- | ------------------------------------------------------------------------ | +| `packagename` | String | No | Name of the modpack. | +| `version` | String | No | Internal version number. Must have [semver](https://semver.org/) format. | +| `versionstr` | String | Yes | Human-readable version string. | +| `repo` | String | Yes | Name of the repo where the package is hosted. | +| `alias` | String | Yes | Alias of the modpack. Aliases can be used for replacing other modpacks. | +| `title` | String | Yes | Title used in UI. | +| `description` | String | Yes | Path to a file with a short description (max 500 chars). | +| `long_description` | String | Yes | Path to a file with a detailed description. | +| `url` | String | Yes | Link to the modpack's website. | +| `license` | Array[String] | Yes | License(s) of the modpack. | ## [assets] Section `[assets]` contains paths to assets in the modpack. -Parameter | Data Type | Optional | Description ----------------|----------------|----------|------------ -`include` | Array[String] | No | List of paths to assets that should be mounted on load time. Paths are allowed to contain wildcards. -`exclude` | Array[String] | Yes | List of paths to assets that should be excluded from mounting. Paths are allowed to contain wildcards. +| Parameter | Data Type | Optional | Description | +| --------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------ | +| `include` | Array[String] | No | List of paths to assets that should be mounted on load time. Paths are allowed to contain wildcards. | +| `exclude` | Array[String] | Yes | List of paths to assets that should be excluded from mounting. Paths are allowed to contain wildcards. | ## [dependency] Section `[dependency]` contains a list of other modpacks that the modpack depends on. -Parameter | Data Type | Optional | Description ----------------|----------------|----------|------------ -`modpacks` | Array[String] | Yes | List of modpack aliases or identifiers. +| Parameter | Data Type | Optional | Description | +| ---------- | ------------- | -------- | --------------------------------------- | +| `modpacks` | Array[String] | Yes | List of modpack aliases or identifiers. | ## [conflict] Section `[conflict]` contains a list of other modpacks that the modpack conflicts with. -Parameter | Data Type | Optional | Description ----------------|----------------|----------|------------ -`modpacks` | Array[String] | Yes | List of modpack aliases or identifiers. +| Parameter | Data Type | Optional | Description | +| ---------- | ------------- | -------- | --------------------------------------- | +| `modpacks` | Array[String] | Yes | List of modpack aliases or identifiers. | ## [authors] Section @@ -78,29 +79,29 @@ Parameter | Data Type | Optional | Description Every author must have their own subtable `[authors.{authorname}]`. The subtable can set the following parameters. -Parameter | Data Type | Optional | Description ------------|---------------|----------|------------ -`name` | String | No | Nickname of the author. Must be unique for the modpack. -`fullname` | String | Yes | Full name of the author. -`since` | String | Yes | Version number of the release where the author started to contribute. -`until` | String | Yes | Version number of the release where the author stopped to contribute. -`roles` | Array[String] | Yes | List of roles of the author during the creation of the modpack. -`contact` | Table | Yes | Contact information (see below). +| Parameter | Data Type | Optional | Description | +| ---------- | ------------- | -------- | --------------------------------------------------------------------- | +| `name` | String | No | Nickname of the author. Must be unique for the modpack. | +| `fullname` | String | Yes | Full name of the author. | +| `since` | String | Yes | Version number of the release where the author started to contribute. | +| `until` | String | Yes | Version number of the release where the author stopped to contribute. | +| `roles` | Array[String] | Yes | List of roles of the author during the creation of the modpack. | +| `contact` | Table | Yes | Contact information (see below). | The contact table can use the following parameters. -Parameter | Data Type | Optional | Description ------------|---------------|----------|------------ -`discord` | String | Yes | Discord username. -`email` | String | Yes | Email address. -`github` | String | Yes | GitHub username. -`gitlab` | String | Yes | Gitlab username. -`irc` | String | Yes | IRC username. -`mastodon` | String | Yes | Mastodon username. -`matrix` | String | Yes | Matrix username. -`reddit` | String | Yes | Reddit username. -`twitter` | String | Yes | Twitter username. -`youtube` | String | Yes | YouTube username. +| Parameter | Data Type | Optional | Description | +| ---------- | --------- | -------- | ------------------ | +| `discord` | String | Yes | Discord username. | +| `email` | String | Yes | Email address. | +| `github` | String | Yes | GitHub username. | +| `gitlab` | String | Yes | Gitlab username. | +| `irc` | String | Yes | IRC username. | +| `mastodon` | String | Yes | Mastodon username. | +| `matrix` | String | Yes | Matrix username. | +| `reddit` | String | Yes | Reddit username. | +| `twitter` | String | Yes | Twitter username. | +| `youtube` | String | Yes | YouTube username. | ## [authorgroups] Section @@ -110,8 +111,8 @@ It can be used in addition to `[authors]` to signify that the modpack is a team effort. -Parameter | Data Type | Optional | Description ---------------|---------------|----------|------------ -`name` | String | No | Group or team name. -`authors` | Array[String] | No | List of author identifiers. These must match up with subtable keys in the `[authors]` section, e.g. `"xxbunny123"` references `[authors.xxbunny123]`. -`description` | String | Yes | Path to a file with a description of the team. +| Parameter | Data Type | Optional | Description | +| ------------- | ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | String | No | Group or team name. | +| `authors` | Array[String] | No | List of author identifiers. These must match up with subtable keys in the `[authors]` section, e.g. `"xxbunny123"` references `[authors.xxbunny123]`. | +| `description` | String | Yes | Path to a file with a description of the team. | diff --git a/libopenage/assets/modpack.cpp b/libopenage/assets/modpack.cpp index 8d784ef92e..acc3865696 100644 --- a/libopenage/assets/modpack.cpp +++ b/libopenage/assets/modpack.cpp @@ -33,6 +33,9 @@ ModpackInfo parse_modepack_def(const util::Path &info_file) { } // optionals + if (info.contains("versionstr")) { + def.versionstr = info.at("versionstr").as_string(); + } if (info.contains("repo")) { def.repo = info.at("repo").as_string(); } diff --git a/libopenage/assets/modpack.h b/libopenage/assets/modpack.h index 96ee08fa29..7c858b5ed7 100644 --- a/libopenage/assets/modpack.h +++ b/libopenage/assets/modpack.h @@ -43,6 +43,7 @@ struct ModpackInfo { // see /doc/media/openage/modpack_definition_file.md std::string id; std::string version; + std::string versionstr; std::string repo; std::string alias; std::string title; diff --git a/openage/convert/entity_object/export/formats/modpack_info.py b/openage/convert/entity_object/export/formats/modpack_info.py index 143a261f81..1a6017ce4e 100644 --- a/openage/convert/entity_object/export/formats/modpack_info.py +++ b/openage/convert/entity_object/export/formats/modpack_info.py @@ -10,7 +10,7 @@ from ..data_definition import DataDefinition -FILE_VERSION = "1" +FILE_VERSION = "2" class ModpackInfo(DataDefinition): @@ -25,6 +25,7 @@ def __init__(self, targetdir: str, filename: str): # Info self.packagename: str = None self.version: str = None + self.versionstr: str = None self.extra_info: dict[str, str] = {} # Assets @@ -153,6 +154,7 @@ def set_info( self, packagename: str, version: str, + versionstr: str = None, repo: str = None, alias: str = None, title: str = None, @@ -166,8 +168,10 @@ def set_info( :param packagename: Name of the modpack. :type packagename: str - :param version: Version number. + :param version: Internal version number. Must have semver format. :type version: str + :param versionstr: Human-readable version number. + :type versionstr: str :param repo: Name of the repo where the package is hosted. :type repo: str :param alias: Alias of the modpack. @@ -186,6 +190,9 @@ def set_info( self.packagename = packagename self.version = version + if versionstr: + self.extra_info["versionstr"] = versionstr + if repo: self.extra_info["repo"] = repo diff --git a/openage/convert/processor/conversion/aoc/modpack_subprocessor.py b/openage/convert/processor/conversion/aoc/modpack_subprocessor.py index cd365d1a51..8d54d68fed 100644 --- a/openage/convert/processor/conversion/aoc/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/aoc/modpack_subprocessor.py @@ -44,7 +44,7 @@ def _get_aoe2_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("aoe2_base", "1.0c", repo="openage") + mod_def.set_info("aoe2_base", "0.5", versionstr="1.0c", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/de2/modpack_subprocessor.py b/openage/convert/processor/conversion/de2/modpack_subprocessor.py index 13c20cca53..22d9d220e2 100644 --- a/openage/convert/processor/conversion/de2/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/de2/modpack_subprocessor.py @@ -40,7 +40,7 @@ def _get_aoe2_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("de2_base", "1.0c", repo="openage") + mod_def.set_info("de2_base", "0.5", versionstr="1.0c", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/hd/modpack_subprocessor.py b/openage/convert/processor/conversion/hd/modpack_subprocessor.py index e491660107..50e77ba711 100644 --- a/openage/convert/processor/conversion/hd/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/hd/modpack_subprocessor.py @@ -40,7 +40,7 @@ def _get_aoe2_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("hd_base", "5.8", repo="openage") + mod_def.set_info("hd_base", "0.5", versionstr="5.8", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/ror/modpack_subprocessor.py b/openage/convert/processor/conversion/ror/modpack_subprocessor.py index 0269f7da92..dfe485b003 100644 --- a/openage/convert/processor/conversion/ror/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/ror/modpack_subprocessor.py @@ -41,7 +41,7 @@ def _get_aoe1_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("aoe1_base", "1.0a", repo="openage") + mod_def.set_info("aoe1_base", "0.5", versionstr="1.0a", repo="openage") mod_def.add_include("data/**") diff --git a/openage/convert/processor/conversion/swgbcc/modpack_subprocessor.py b/openage/convert/processor/conversion/swgbcc/modpack_subprocessor.py index bc50d080c7..cef278f9eb 100644 --- a/openage/convert/processor/conversion/swgbcc/modpack_subprocessor.py +++ b/openage/convert/processor/conversion/swgbcc/modpack_subprocessor.py @@ -40,7 +40,7 @@ def _get_swgb_base(cls, full_data_set: GenieObjectContainer) -> Modpack: mod_def = modpack.get_info() - mod_def.set_info("swgb_base", "1.1-gog4", repo="openage") + mod_def.set_info("swgb_base", "0.5", versionstr="1.1-gog4", repo="openage") mod_def.add_include("data/**") From e7aaa40a1bbc231fe854a2d542443749d2dc4234 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 3 Aug 2023 02:30:02 +0200 Subject: [PATCH 19/49] doc: Remove obslete docs. --- doc/ai/interface.md | 93 ----------------------------------- doc/code/gui.md | 12 +++-- doc/code/pathfinding.md | 22 --------- doc/code/terrain.md | 37 -------------- doc/debug.md | 7 ++- doc/ideas/ai.md | 104 +++++++++++++++++++++++++++++++++++++--- doc/milestones.md | 32 ------------- 7 files changed, 110 insertions(+), 197 deletions(-) delete mode 100644 doc/ai/interface.md delete mode 100644 doc/code/pathfinding.md delete mode 100644 doc/code/terrain.md delete mode 100644 doc/milestones.md diff --git a/doc/ai/interface.md b/doc/ai/interface.md deleted file mode 100644 index 3ac52bbdf6..0000000000 --- a/doc/ai/interface.md +++ /dev/null @@ -1,93 +0,0 @@ -AI Interface -============ - -This is a very early draft with some ideas taken -from http://bwmirror.jurenka.sk/javadoc/bwapi/package-summary.html - -This file shall provide information about the AI interface design. - - -General ideas -------------- - - * An AI registers hooks for desired events - * The C++ engine triggers python AI functions when desired events occur - * The AI then can trigger actions on the controllable units - * Periodically (1 Hz?), the AI gets ticked to allow event independent decisions - - -Event types ------------ - -Python interface basic events: - - * on_start(GameInfo info) - * on_frame(Game game) - * on_end(Player winner) - -Some AIs may prefer an event driven approach (rule based AI): - - * on_unit_discover(Unit unit) - * on_unit_complete(Unit unit) - * on_unit_lost(Unit unit) - * many more... - - -Some AIs will need to simulate ticks into the future to help -make decisions for the current tick. (min-max, MCTS, etc.) - - -Relevant structures for the AI ------------------------------- - -Remember: These are just some ideas for possible interfaces, -they are not existent in the game yet. - -```cpp -struct game_info { - int num_players; - int map_size; - int pop_limit; - resources_type; - map_view; - starting_age; - victory_mode; -} -``` - -```cpp -class Game { - Player[] allies(); // all the ally players that have not left or been defeated. - bool canBuildHere(Unit builder, TilePosition position, UnitType type); - bool canMake(Unit builder, UnitType type); - bool canResearch(Unit unit, TechType type); - bool canUpgrade(Unit unit, UpgradeType type); - Player[] enemies(); // all the enemy players that have not left or been defeated. - Unit[] getAllUnits(); // returns all the visible units. - - // many more examples at: http://bwmirror.jurenka.sk/javadoc/bwapi/Game.html -} -``` - -```cpp -class Player { - string getName(); // returns the name of the player. - Race getRace(); // returns the race of the player. - Unit[] getUnits(); // returns the set of units the player own. - bool hasResearched(TechType tech); - int getWood() // Returns the amount of wood the player owns. - // many more... -} -``` - -```cpp -class Unit { - // used to get information about individual units as well as issue orders to units -} -``` - -```cpp -class Calculation { - // used to get computation heavy calculations from the C++ engine -} -``` diff --git a/doc/code/gui.md b/doc/code/gui.md index 17196b4b09..6c548c2b65 100644 --- a/doc/code/gui.md +++ b/doc/code/gui.md @@ -1,5 +1,7 @@ # QtQuick-based Graphical User Interface +**Important Notice**: This document is outdated and subject to change. + ## Using Qt Creator Integrated Development Environment (IDE) See [Qt Creator IDE](/doc/ide/qt_creator.md). @@ -30,7 +32,7 @@ Example: ## Exposing components to the GUI layer Components are adapted by writing QObject counterparts for them. -Look for the examples in the `libopenage/gui` directory. +Look for the examples in the `libopenage/renderer/gui` directory. There is a property-based approach and a model-based extension to it. @@ -40,7 +42,7 @@ Let's suppose we have a class `ResourceAmount` in `libopenage/economy`, and we w In order to do that: -1. A class `ResourceAmountLink` must be created in the `libopenage/gui`. +1. A class `ResourceAmountLink` must be created in the `libopenage/renderer/gui`. It must derive from `GuiItemQObject` and `GuiItem`. It must be registered in the QML type system using a usual Qt approach: ```cpp @@ -85,7 +87,7 @@ this->setv("player_radius", 10); this->set_csv("player_names", std::vector{"name1", "name2"}); ``` -3. A class `GeneratorParametersLink` must be created in the `libopenage/gui`. +3. A class `GeneratorParametersLink` must be created in the `libopenage/renderer/gui`. It must derive from `QObject` and `GuiItemListModel`. It must be registered in the QML type system using a usual Qt approach: ```cpp @@ -166,11 +168,11 @@ void EditorModeLink::on_core_adopted() { ### Including headers -The files that are outside of the `libopenage/gui/` are allowed to include only the headers from the `libopenage/gui/guisys/public` and `libopenage/gui/integration/public`. +The files that are outside of the `libopenage/renderer/gui/` are allowed to include only the headers from the `libopenage/renderer/gui/guisys/public` and `libopenage/renderer/gui/integration/public`. ## Directory structure -The subsystem resides in the `libopenage/gui`. +The subsystem resides in the `libopenage/renderer/gui`. * random files in the directory - bindings for the game components * `guisys/` - non-openage-specific part diff --git a/doc/code/pathfinding.md b/doc/code/pathfinding.md deleted file mode 100644 index 9ed33e129c..0000000000 --- a/doc/code/pathfinding.md +++ /dev/null @@ -1,22 +0,0 @@ -Original algorithm ------------------- - -Short range pathfinder: -Don't check all possible points and vectors. - -Path from (x0, y0) to (x1, y1): - -Draw line from (x0, y0)->(x1, y1). - -Query obstruction manager for collisions: -Obstruction manager stores 2D outline of all relevant objects, -dynamically adjusts outline sizes for the size of the moving unit. -Just stores the area and shapes on the map occupied by everything. - -If obstruction manager detects no collision: unit walks the line. -Else: -find the 0. collision point (=c0), find way on outline around obstruction: -branch path at c0 into 2 new ones in an `A*` like search tree. -The new paths go to the each side of the obstruction, -each one attempts to build a new path from their end to (x1, y1). -For speedup: assemble multiple obstructions to convex hulls. diff --git a/doc/code/terrain.md b/doc/code/terrain.md deleted file mode 100644 index 974296dc68..0000000000 --- a/doc/code/terrain.md +++ /dev/null @@ -1,37 +0,0 @@ -terrain implementation -====================== - - -if you ever asked yourself, -"hey, myself, how is terrain stuff implemented in openage?", -this document may enlighten your mind. - - -ideas ------ - -You might know the infinite terrain epicness of minecraft. That's exactly what we are doing in openage. - -The terrain consists of loads of small tiles, grouped in "chunks". - -One chunk contains an area of (currently) 16×16 tiles. Terrain is blended by the technique described in [doc/media/blendomatic.md](/doc/media/blendomatic.md). - -(short summary: -a neighbor tile is blended onto the current one, -if neighbor.priority > current.priority: - the neighbor tile is drawn onto the current tile, - but before that, parts are alphamasked. -) - - -master terrain --------------- - -this is the container of all chunks. - - - -chunk ------ - -a grouped piece of terrain. diff --git a/doc/debug.md b/doc/debug.md index 01d3c0fac9..e8f838e0b2 100644 --- a/doc/debug.md +++ b/doc/debug.md @@ -1,6 +1,9 @@ # How to debug openage? -## Qt Creator IDE -See [qt_creator.md](/doc/ide/qt_creator.md) + +## Your favorite IDE + +Check the docs in [the `/doc/ide` subfolder](/doc/ide/). + ## GDB GDB can be used to debug C++ code in a terminal. diff --git a/doc/ideas/ai.md b/doc/ideas/ai.md index 991dbcb596..fab47ddc86 100644 --- a/doc/ideas/ai.md +++ b/doc/ideas/ai.md @@ -1,7 +1,101 @@ +# Artificial Intelligence + +## Interface + +This is a very early draft with some ideas taken +from http://bwmirror.jurenka.sk/javadoc/bwapi/package-summary.html + +This file shall provide information about the AI interface design. + + +### General ideas + + * An AI registers hooks for desired events + * The C++ engine triggers python AI functions when desired events occur + * The AI then can trigger actions on the controllable units + * Periodically (1 Hz?), the AI gets ticked to allow event independent decisions + + +### Event types + +Python interface basic events: + + * on_start(GameInfo info) + * on_frame(Game game) + * on_end(Player winner) + +Some AIs may prefer an event driven approach (rule based AI): + + * on_unit_discover(Unit unit) + * on_unit_complete(Unit unit) + * on_unit_lost(Unit unit) + * many more... + + +Some AIs will need to simulate ticks into the future to help +make decisions for the current tick. (min-max, MCTS, etc.) + + +### Relevant structures for the AI + +Remember: These are just some ideas for possible interfaces, +they are not existent in the game yet. + +```cpp +struct game_info { + int num_players; + int map_size; + int pop_limit; + resources_type; + map_view; + starting_age; + victory_mode; +} +``` + +```cpp +class Game { + Player[] allies(); // all the ally players that have not left or been defeated. + bool canBuildHere(Unit builder, TilePosition position, UnitType type); + bool canMake(Unit builder, UnitType type); + bool canResearch(Unit unit, TechType type); + bool canUpgrade(Unit unit, UpgradeType type); + Player[] enemies(); // all the enemy players that have not left or been defeated. + Unit[] getAllUnits(); // returns all the visible units. + + // many more examples at: http://bwmirror.jurenka.sk/javadoc/bwapi/Game.html +} +``` + +```cpp +class Player { + string getName(); // returns the name of the player. + Race getRace(); // returns the race of the player. + Unit[] getUnits(); // returns the set of units the player own. + bool hasResearched(TechType tech); + int getWood() // Returns the amount of wood the player owns. + // many more... +} +``` + +```cpp +class Unit { + // used to get information about individual units as well as issue orders to units +} +``` + +```cpp +class Calculation { + // used to get computation heavy calculations from the C++ engine +} +``` + + +## Gameplay + Improvements for the AI's gameplay -Villagers ---------- +### Villagers - Don't collect resources in hazardous areas / send the military to clear them out - Send military task forces to guard strategic resources / tower them @@ -10,8 +104,7 @@ Villagers - Prefer to build castles and towers near the water/thin places - Create more trade cogs and trade ships -Military --------- +### Military - Avoid fighting in hazardous areas / prefer fighting in range of own castles - Coordinates reaction to attacks on units (during march, ...) (including attacks by towers/castles) @@ -23,7 +116,6 @@ Military - send smaller amount of troops earlier in the game to annoy you - while building up a big army in the back -Ship --------- +### Ship - Transport ship can deliver villagers to another islands diff --git a/doc/milestones.md b/doc/milestones.md deleted file mode 100644 index f0c66f094d..0000000000 --- a/doc/milestones.md +++ /dev/null @@ -1,32 +0,0 @@ -Milestones -========== - - -- [x] 0: decide on the technology and architecture we're using. - * language, libraries, build system, coding standards, ... - * may be incomplete, will be changed later on anyway -- [x] 1: Create a database format for game data ([nyan](https://github.com/SFTtech/nyan)) -- [x] 2: Renderer using OpenGL and Vulkan ([code doc](https://github.com/SFTtech/openage/tree/master/doc/code/renderer)) -- [x] 3: Event system for the game simulation -- [x] 4: Design a modding API for the engine ([reference sheet](https://github.com/SFTtech/openage/tree/master/doc/nyan/api_reference)) -- [X] 5: Convert AoE2 game data to our engine formats ([code doc](https://github.com/SFTtech/openage/tree/master/doc/code/converter)) -- [ ] 6: Gamestate calculation with curves logic ([blogpost](https://blog.openage.dev/t1-curves-logic.html)) -- [ ] 7: Gameplay simulation! Where everything comes together -- [ ] 8: Multiplayer - -things to be done sometime: - -* game recording, playback -* lobby + lobby server -* implement AI Python API (use GlaDOS?) -* messaging and taunts (important!) -* random map generation (RMS scripts?) -* age2 map reader - * scenario reader - * maybe even the singleplayer campaigns - -things to keep in mind: - -* performance (responsiveness, fps, needed resources) -* high-quality code -* having fun! From 36095c43e0166e22496612b8da0f1a259d751816 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 3 Aug 2023 02:34:18 +0200 Subject: [PATCH 20/49] doc: Rework release guide. --- doc/releasing.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/releasing.md b/doc/releasing.md index 23c834aa67..8a3f37f955 100644 --- a/doc/releasing.md +++ b/doc/releasing.md @@ -1,15 +1,15 @@ # Releasing -* Bump the version number in `openage_version`. Given `0.y.z`, both `0.y.(z+1)` and `0.(y+1).0` are valid. The latter will probably be more common, but which of those you choose doesn't really matter. +openage uses [Semver](https://semver.org).. People often seem to overlook the special rules for initial development versions. Please avoid prerelease versions (`-alpha`, `-beta`, `-rc.1`, ...) for now. We might decide to include build metadata like date or commit hash, that's up for discussion. - Read up on [Semver](https://semver.org) for details. People often seem to overlook the special rules for initial development versions. Please avoid prerelease versions (`-alpha`, `-beta`, `-rc.1`, ...) for now. We might decide to include build metadata like date or commit hash, that's up for discussion. -* Create a new changelog named `doc/changelogs/engine/$VERSION.md`. -* Commit, PR, merge, pull as usual. -* OPTIONAL: Set up [commit signing](https://help.github.com/en/articles/managing-commit-signature-verification), signing tags will make a "verified" badge show up on the release later. -* Tag the merge commit, something like `git tag -s v0.5.0`. The prefixed `v` is mandatory. -* Push the tag to GitHub. -* Use the GitHub web interface to publish a release from the tag. Make it look nice, see previous release description. If in doubt, save the draft and have someone else proof read it. Also include the changelog. -* In the future, kevin will automatically attach build artifacts like installers to the release. For now, you will need to do that manually. Have fun setting up a windows build environment or if you feel like being lazy, go bug someone who already has. Actually, taking care of where to build for windows before publishing the release might not be a bad idea. -* OPTIONAL: Brag on social media. /r/openage, /r/aoe2 and AoE2 Discords might be the right place to do so. +Release guide: -If you need help or are not completely sure what you are doing, ask in [`#sfttech:matrix.org`](https://app.element.io/#/room/#sfttech:matrix.org) and highlight `zuntrax` or someone else who has done it before. Better having someone help you than screwing up a release. +1. Create a new changelog named `doc/changelogs/engine/$VERSION.md`. +1. Bump the version number in `openage_version`. Given `0.y.z`, both `0.y.(z+1)` and `0.(y+1).0` are valid. The latter will probably be more common, but which of those you choose doesn't really matter. +1. Commit, PR, merge, pull as usual. + * OPTIONAL: Set up [commit signing](https://help.github.com/en/articles/managing-commit-signature-verification), signing tags will make a "verified" badge show up on the release later. +1. Tag the merge commit, something like `git tag -s v0.5.0`. The prefixed `v` is mandatory. +1. Push the tag to GitHub. +1. Use the GitHub web interface to publish a release from the tag. Make it look nice, see previous release description. If in doubt, save the draft and have someone else proof read it. Also include the changelog. +1. In the future, kevin will automatically attach build artifacts like installers to the release. For now, you will need to do that manually. Have fun setting up a windows build environment or if you feel like being lazy, go bug someone who already has. Actually, taking care of where to build for windows before publishing the release might not be a bad idea. + * OPTIONAL: Brag on social media. /r/openage, /r/aoe2 and AoE2 Discords might be the right place to do so. From 2ad779b37cec042215fcb564668a8c30fd1e603d Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 3 Aug 2023 02:40:05 +0200 Subject: [PATCH 21/49] doc: Update project structure. --- doc/project_structure.md | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/doc/project_structure.md b/doc/project_structure.md index 945ef5c3b9..404a1c2caf 100644 --- a/doc/project_structure.md +++ b/doc/project_structure.md @@ -1,6 +1,4 @@ -Project Structure -================= - +# Project Structure One of the biggest problems for newcomers who want to contribute code to free software projects is that they have no idea where to start. @@ -9,6 +7,21 @@ Reading, understanding and finding the relevant code part is hard. This file explains the modular structure of the project. +1. [Architecture](#architecture) +2. [Languages](#languages) +3. [Folders](#folders) + 1. [assets/](#assets) + 2. [buildsystem/](#buildsystem) + 3. [cfg/](#cfg) + 4. [dist/](#dist) + 5. [doc/](#doc) + 6. [etc/](#etc) + 7. [legal/](#legal) + 8. [libopenage/](#libopenage) + 9. [openage/](#openage) + 10. [packaging/](#packaging) + + ## Architecture @@ -19,11 +32,11 @@ The [overall architecture](/doc/architecture.md) describes the conceptual overvi We use Python, Cython and C++. -Extension | Language | Usage ---------------|-----------|--------- -`.py` | Python | Everything that does not crunch data -`.pyx` `.pxd` | Cython | Fast Python code, glue between C/C++ and Python -`.h` `.cpp` | C++ | Data crunching code: simulation, graphics, sound, ... +| Extension | Language | Usage | +| ------------- | -------- | ----------------------------------------------------- | +| `.py` | Python | Everything that does not crunch data | +| `.pyx` `.pxd` | Cython | Fast Python code, glue between C/C++ and Python | +| `.h` `.cpp` | C++ | Data crunching code: simulation, graphics, sound, ... | ## Folders @@ -37,8 +50,8 @@ of the project. Game assets required at run time are placed in here. This includes everything that is converted from the original assets (see [asset conversion](media_convert.md)) -and other input like shaders etc. The directory is installed to `/usr/share/openage/assets` -or whatever the platform recommends. +and other input like shaders etc. The directory is `assets` for local builds or +installed to `/usr/share/openage/assets` or whatever the platform recommends. ### buildsystem/ @@ -68,6 +81,10 @@ The code itself is commented with `/** doxygen comments */`. See [doc/README.md](/doc/README.md) for documentation guidelines. +### etc/ + +Additional resources for development tools, e.g. pylint and valgrind configs. + ### legal/ From d478458481f12ae7ec649c163a0e8320f82066db Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 3 Aug 2023 02:57:02 +0200 Subject: [PATCH 22/49] doc: Describe openage nyan API layer. --- doc/nyan/openage-lib.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/nyan/openage-lib.md b/doc/nyan/openage-lib.md index 0525ac3ba6..328065c148 100644 --- a/doc/nyan/openage-lib.md +++ b/doc/nyan/openage-lib.md @@ -92,4 +92,5 @@ For an effect to apply, the effector's `Effect` needs a corresponding `Resistanc ## C++ Interface -ASDF +openage provides a thin nyan API layer to make interacing with nyan more accessible. This layer is implemented in `libopenage/gamestate/api` and +covers the most common use caes for retrieving objects and reading values. Using/extending the nyan API layer should be preferred when adding game logic that accesses nyan to the engine. From e666227dc5ffef186ecfd652135b4a8b188592f2 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 3 Aug 2023 03:05:39 +0200 Subject: [PATCH 23/49] doc: Refactor nyan docs. --- doc/{nyan/conversion.md => convert/nyan.md} | 0 doc/nyan/{nyan.md => README.md} | 21 ++++++++++++--------- doc/nyan/examples.md | 9 --------- 3 files changed, 12 insertions(+), 18 deletions(-) rename doc/{nyan/conversion.md => convert/nyan.md} (100%) rename doc/nyan/{nyan.md => README.md} (76%) delete mode 100644 doc/nyan/examples.md diff --git a/doc/nyan/conversion.md b/doc/convert/nyan.md similarity index 100% rename from doc/nyan/conversion.md rename to doc/convert/nyan.md diff --git a/doc/nyan/nyan.md b/doc/nyan/README.md similarity index 76% rename from doc/nyan/nyan.md rename to doc/nyan/README.md index 28fa0a5aaf..090fad8579 100644 --- a/doc/nyan/nyan.md +++ b/doc/nyan/README.md @@ -1,13 +1,11 @@ -nyan -==== +# nyan nyan - yet another notation https://github.com/SFTtech/nyan -Idea ----- +## Idea nyan is the data storage format for openage. @@ -32,18 +30,23 @@ it can actually store unit upgrades, unit abilities, research, civ bonuses and age upgrades. -Technical specification ------------------------ +## Technical specification [nyan specification](https://github.com/SFTtech/nyan/blob/master/doc/nyan.md) -openage specifics ------------------ +## openage specifics nyan is a general purpose data language, the following describes the openage specific usage of it. * [data conversion](conversion.md) * [openage engine nyan interface](openage-lib.md) -* [examples](examples.md) + +## Examples + +For the beginning, you can look at these resources to find examples of the nyan notation: + +* [nyan docs](https://github.com/SFTtech/nyan/blob/master/doc/nyan.md) +* converted game assets from [our converter](/doc/convert/) +* [openage modding guide for nyan](https://github.com/SFTtech/openage-modding/tree/master/tutorials/nyan) diff --git a/doc/nyan/examples.md b/doc/nyan/examples.md deleted file mode 100644 index a4b5d02d76..0000000000 --- a/doc/nyan/examples.md +++ /dev/null @@ -1,9 +0,0 @@ -openage nyan examples -===================== - -openage will make excessive use of the nyan data storage. - -This file will help you understand how you'd create nyan files -to modify or extend the behavior of openage. - -For the beginning, you can look [here](https://github.com/SFTtech/nyan/blob/master/doc/nyan.md) for examples. From f9409aeab528599a937f4f011b4dbcd5863123b4 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 3 Aug 2023 03:30:46 +0200 Subject: [PATCH 24/49] doc: Refactor conversion docs. --- doc/convert/README.md | 98 ++++-------------------------- doc/convert/convert_single_file.md | 22 +++++-- doc/convert/nyan.md | 17 +----- doc/media_convert.md | 7 +-- 4 files changed, 36 insertions(+), 108 deletions(-) diff --git a/doc/convert/README.md b/doc/convert/README.md index 8d97992042..1bf72efe36 100644 --- a/doc/convert/README.md +++ b/doc/convert/README.md @@ -1,5 +1,4 @@ -Convert Script -============== +# Convert Script The convert script transforms media files to openage-compatible formats, and generates code for parsing the generated files. It is written @@ -13,92 +12,21 @@ so we need to convert the files to a format that is less special, and more usable. This is what the convert script does. -The convert script is divided into two parts: -original media dependent and independent exports. +The convert script is divided into two usage modes: *singlefile media conversion* and +*modpack conversion*. -Converting Media ----------------- +## Singlefile Media Conversion -* depends on existing original game installation -* converts images, sounds, units and all other data values - to compatible formats -* creates files for the proprietary data package, - its files will be used if free media files are missing. +This converts graphics and sounds to open/compatible formats. Graphics are +exported to PNG files. If the graphics file is an animation with multiple +sprites, the converter will automatically pack all of them into a spritesheet. +Sounds are exported to OPUS files. -Exporting Structures --------------------- +[See here](convert_single_file.md) for usage examples of the singlefile conversion. -* totally independent of original game installation -* exports original content structures -* prevents redundancy: - * data structures are needed for media export (to convert them) - * data structures are needed to import them in the game - * => structure definition at a single point. -* generation is integrated into make system -* generated files placed in `libopenage/gamedata/` +## Modpack Conversion -Conversion Procedure -==================== - -Media Files ------------ - -A media file is a sound or image file. In the original media files, they are -stored in [drs archives](/doc/media/drs-files.md). - -Each media file has an id and a file extension. -Files can be "redefined" by overlay archives. - -All available archives have to be ordered, so that 'high priority' files -override 'low priority' files. Mods have a higher priority than the base game -to be able to apply their modifications. - -The overlay procedure works by assigning unique media files to the desired -file source or archive. - -A dict has to be filled with values to assign the data source: - - (fileid, extension, destination) => (file archive) - -The resulting dict is a mapping between all available media files and the -archive each should be obtained from. - -The files can then be converted to the appropriate format, the conversion -procedure is selected by their file extension. Graphics are stored in the -[SLP format](/doc/media/slp-files.md) and are converted to *png*. Sounds are -*wav* files which are compressed to *opus*. - - -Original metadata files ------------------------ - -This data describes the configuration and behavior of the original game and its -entities. Namely this defines the available buildings, units, their creation -cost, etc. - -The [gamedata files](/doc/media/original_metadata.md) are parsed and converted to a -huge tree of *csv* files. This is not optimal and will be improved soon(tm). - - -### Data Updates ### - -To modify any data extracted from the original files in a hardcoded manner, -simply update or delete values in the parsed python data structure. These -modifications can be applied like this: - -```python -def fix_data(data): - # make lists for all units building locations - for each unit: - unit.building_loc = [unit.building_loc] - - huskarl_castle.building_loc.append(huskarl_barracks.building_loc) - delete huskarl_barracks - - # value updating is done like this: - get_unit(data, "hussar")["hp"] = 9001 - data.terrain.terrains[6].slp_id = 1337 - - return data -``` +Modpack conversion takes an original game installation and converts its data +and media files into an [openage modpack](/doc/media/openage/modpacks.md). +The converted game can then be run inside openage. diff --git a/doc/convert/convert_single_file.md b/doc/convert/convert_single_file.md index e998b7ad5d..f8c7f16d31 100644 --- a/doc/convert/convert_single_file.md +++ b/doc/convert/convert_single_file.md @@ -1,8 +1,7 @@ -Single file conversion -====================== +# Singlefile conversion -You can use openage to convert a single slp image from some drs archive to a -png image. +You can use openage to convert a single graphics file to PNG or sound file to OPUS. The +asset can also be unpacked from a DRS archive. The invocation could be: @@ -16,6 +15,11 @@ Standalone SLPs (Age of Empires 1: DE and Age of Empires 2: HD): python3 -m openage convert-file --palette-file ~/games/aoe2hd/Data/50500.bina 326.slp /tmp/rofl.png ``` +Standalone SLDs (Age of Empires 2: DE): +``` +python3 -m openage convert-file --player-palette-file ~/games/aoe2de/Data/playercolor_blue.pal --palette-file ~/games/aoe2de/Data/b_west.pal u_elite_eagle.sld /tmp/rofl.png +``` + Standalone SMXs (Age of Empires 2: DE): ``` python3 -m openage convert-file --player-palette-file ~/games/aoe2de/Data/playercolor_blue.pal --palette-file ~/games/aoe2de/Data/b_west.pal u_elite_eagle.smx /tmp/rofl.png @@ -26,6 +30,16 @@ Standalone SMPs (Age of Empires 2: DE): python3 -m openage convert-file --player-palette-file ~/games/aoe2de/Data/playercolor_blue.pal --palette-file ~/games/aoe2de/Data/b_west.pal u_elite_eagle.smp /tmp/rofl.png ``` +WAVs in DRS archives (for older versions of Age of Empires 1, Age of Empires 2 and SWGB): +``` +python3 -m openage convert-file --drs ~/games/aoe2/Data/sounds.drs 123.wav /tmp/rofl.opus +``` + +Standalone WAVs (Age of Empires 2: HD Edition): +``` +python3 -m openage convert-file ~/games/aoe2/resources/123.wav /tmp/rofl.opus +``` + Have a look at `openage/convert/singlefile.py`, this is also a simple API demo for how to interact with the aoe files. diff --git a/doc/convert/nyan.md b/doc/convert/nyan.md index b094c0f1fb..5d28947c6a 100644 --- a/doc/convert/nyan.md +++ b/doc/convert/nyan.md @@ -1,20 +1,9 @@ -nyan data conversion -==================== +# nyan data conversion We depend on the original game. We have a converter which spits out a files in our formats by parsing the original data. -Gamedata conversion -------------------- +## Gamedata conversion -* `openage/convert/gamedata/` specifies the format of `empires_x1_p1.dat` - * this format allows reading the binary file and storing it into tables - * these tables and the original format suck, - information is redundant and we can heavily improve it - * buuut: the file format is given (every original game has it.) - * :arrow_right: data transformation. -* the original file will be read according to the original format (we can't help it) -* a transformation is specified - * mapping: original format -> our awesome format - * redundancy is purged (e.g. no more double huskarl unit (1x castle, 1x barracs)) +`openage/convert/value_object/read/media/datfile/` specifies the format of the `.dat` from various games. diff --git a/doc/media_convert.md b/doc/media_convert.md index 04f72554b1..1e4f07bae6 100644 --- a/doc/media_convert.md +++ b/doc/media_convert.md @@ -8,12 +8,9 @@ Currently we *support* conversion for these games: * **RoR**: Age of Empires 1 (1997) + Rise of Rome * **AoC**: Age of Empires 2 (1999) + The Conqueror's * **SWGB**: Star Wars: Galactic Battlegrounds + Clone Campaigns -* **DE2**: Age of Empires 2: Definitve Edition - -The following versions are currently *unsupported*, but will be added later: - * **HD**: Age of Empires 2 (2013) (formerly: Age of Empires 2: HD Edition) * **DE1**: Age of Empires 1: Definitive Edition +* **DE2**: Age of Empires 2: Definitve Edition On Linux, you may have to use additional measures to download DE2 versions from Steam: @@ -26,7 +23,7 @@ On Linux, you may have to use additional measures to download DE2 versions from `./steamcmd.sh +@sSteamCmdForcePlatformType windows +login USERNAME +force_install_dir ASSET_DIR +app_update GAME_APP_ID validate +quit` The game version vs Game_ID table such as below: - Age of Empires(2013): 221380 + Age of Empires (2013): 221380 Age of Empires: Definitive Edition: 1017900 Age of Empires II (Rise of the Rajas): 488060 Age of Empires II: Definitive Edition: 813780 From 19eaa60170be4ee5711363c73ba36b0ae5f344d9 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 3 Aug 2023 22:45:44 +0200 Subject: [PATCH 25/49] doc: Add UML images. --- doc/code/curves.md | 10 +- doc/code/event_system.md | 2 +- doc/code/game_simulation/README.md | 4 +- doc/code/game_simulation/activity.md | 18 +- doc/code/game_simulation/components.md | 22 +-- doc/code/game_simulation/game_entity.md | 10 +- .../game_simulation/images/activity_graph.svg | 4 + .../images/activity_workflow.png | Bin 0 -> 112675 bytes .../images/component_activity_uml.svg | 82 +++++++++ .../images/component_command_queue_uml.svg | 51 ++++++ .../images/component_idle_uml.svg | 25 +++ .../images/component_live_uml.svg | 33 ++++ .../images/component_move_uml.svg | 25 +++ .../images/component_ownership_uml.svg | 33 ++++ .../images/component_position_uml.svg | 39 ++++ .../images/component_turn_uml.svg | 25 +++ .../game_simulation/images/component_uml.svg | 170 +++++++++++++++++ .../images/game_entity_overview.svg | 73 ++++++++ .../images/game_entity_uml.svg | 94 ++++++++++ .../game_simulation/images/simulation_uml.svg | 134 ++++++++++++++ .../game_simulation/images/system_idle.svg | 28 +++ .../game_simulation/images/system_move.svg | 30 +++ doc/code/game_simulation/systems.md | 29 +-- doc/code/images/curve_classes_uml.svg | 149 +++++++++++++++ doc/code/images/event_classes_uml.svg | 124 +++++++++++++ doc/code/images/primitive_curves_uml.svg | 171 ++++++++++++++++++ doc/code/images/time_classes_uml.svg | 71 ++++++++ doc/code/time.md | 2 +- 28 files changed, 1409 insertions(+), 49 deletions(-) create mode 100644 doc/code/game_simulation/images/activity_graph.svg create mode 100644 doc/code/game_simulation/images/activity_workflow.png create mode 100644 doc/code/game_simulation/images/component_activity_uml.svg create mode 100644 doc/code/game_simulation/images/component_command_queue_uml.svg create mode 100644 doc/code/game_simulation/images/component_idle_uml.svg create mode 100644 doc/code/game_simulation/images/component_live_uml.svg create mode 100644 doc/code/game_simulation/images/component_move_uml.svg create mode 100644 doc/code/game_simulation/images/component_ownership_uml.svg create mode 100644 doc/code/game_simulation/images/component_position_uml.svg create mode 100644 doc/code/game_simulation/images/component_turn_uml.svg create mode 100644 doc/code/game_simulation/images/component_uml.svg create mode 100644 doc/code/game_simulation/images/game_entity_overview.svg create mode 100644 doc/code/game_simulation/images/game_entity_uml.svg create mode 100644 doc/code/game_simulation/images/simulation_uml.svg create mode 100644 doc/code/game_simulation/images/system_idle.svg create mode 100644 doc/code/game_simulation/images/system_move.svg create mode 100644 doc/code/images/curve_classes_uml.svg create mode 100644 doc/code/images/event_classes_uml.svg create mode 100644 doc/code/images/primitive_curves_uml.svg create mode 100644 doc/code/images/time_classes_uml.svg diff --git a/doc/code/curves.md b/doc/code/curves.md index d345fdb94f..a100fd789b 100644 --- a/doc/code/curves.md +++ b/doc/code/curves.md @@ -49,7 +49,7 @@ is the case for most RTS games - these downsides are usually outweight by the po ## Architecture -![Curve classes hierarchy UML](ASDF) +![Curve classes hierarchy UML](images/curve_classes_uml.svg) openage provides several curve types with different interpolation behaviour that each have their specific use case. Primitive data types are covered by the `BaseCurve` interface @@ -88,7 +88,7 @@ This section provides an overview over the available curves types. ### Primitive -![Primitive curve types UML](ASDF) +![Primitive curve types UML](images/primitive_curves_uml.svg) Primitive curves are intended for members with single value types. These include, for example, the primitive C++ types (e.g. `int`, `float`, `std::string`), simple structs and data classes, @@ -138,7 +138,7 @@ for specific curve types. #### Discrete -![Discrete curve function example]() +![Discrete curve function example](ASDF) Discrete curves implement **constant interpolation** between keyframes. This means that the value returned by `get(t)` is always equal to the value of the previous @@ -150,7 +150,7 @@ e.g. for the health of a game entity. #### Continuous -![Continuous curve function example]() +![Continuous curve function example](ASDF) Continuous curves implement **linear interpolation** between keyframes. This means that the value returned by `get(t)` is calculated from the value difference @@ -171,7 +171,7 @@ e.g. a game entity's position or the construction progress of a building. #### Segmented -![Segmented curve function example]() +![Segmented curve function example](ASDF) Segmented curves implement **linear interpolation** between keyframes and additionally allow jumps between keyframe values. As with continuous curves, the value returned by `get(t)` diff --git a/doc/code/event_system.md b/doc/code/event_system.md index afe9940571..9c6b7dae05 100644 --- a/doc/code/event_system.md +++ b/doc/code/event_system.md @@ -12,7 +12,7 @@ manages the creation, scheduling, and execution of these events. ## Architecture -![event system UML](ASDF) +![event system UML](images/event_classes_uml.svg) The central component of the event system is the *event loop* (`EventLoop`). It is the main point of interaction between the game simulation logic and the event system. diff --git a/doc/code/game_simulation/README.md b/doc/code/game_simulation/README.md index 40ac497962..016f005cc8 100644 --- a/doc/code/game_simulation/README.md +++ b/doc/code/game_simulation/README.md @@ -13,7 +13,7 @@ networking or scripting, are handled as separate subsystems. ## Architecture -![Game simulation overview UML](ASDF) +![Game simulation overview UML](images/simulation_uml.svg) The game simulation is instantiated from the main thread via a `GameSimulation` object. This object controls the game loop, the current game session, and other simulation @@ -30,8 +30,6 @@ is used for every type of world object (e.g. units, buildings, trees, resources, ## Workflow -![Game simulation loop](ASDF) - To initiate a game, a `GameSimulation` object must be created. This is usually done by the `Engine` object in the main thread. On initialization, the game simulation automatically sets up several subcomponents required for running the game such as: diff --git a/doc/code/game_simulation/activity.md b/doc/code/game_simulation/activity.md index 45c891fd8d..0b84864fe9 100644 --- a/doc/code/game_simulation/activity.md +++ b/doc/code/game_simulation/activity.md @@ -38,7 +38,7 @@ the same activity node graph. An activity can also be represented visually like this: -![graph example](ASDF) +![graph example](images/activity_graph.svg) The design is heavily inpired by the [BPMN](https://en.wikipedia.org/wiki/Business_Process_Model_and_Notation) representation. You don't need to know BPMN to understand the activity control flow because @@ -48,11 +48,11 @@ you can use available [BPMN tools](https://bpmn.io/) to draw activity node graph ## Node Types -| Type | Symbol | Inputs | Outputs | Description | -| ---------------- | ------ | ------ | ------- | ------------------------- | -| `START` | [ASDF] | 0 | 1 | Start of activity | -| `END` | [ASDF] | 1 | 0 | End of activity | -| `TASK_SYSTEM` | [ASDF] | 1 | 1 | Run built-in system | -| `TASK_CUSTOM` | [ASDF] | 1 | 1 | Run custom function | -| `XOR_EVENT_GATE` | [ASDF] | 1 | 1+ | Wait for event and branch | -| `XOR_GATE` | [ASDF] | 1 | 1+ | Branch on condition | +| Type | Inputs | Outputs | Description | +| ---------------- | ------ | ------- | ------------------------- | +| `START` | 0 | 1 | Start of activity | +| `END` | 1 | 0 | End of activity | +| `TASK_SYSTEM` | 1 | 1 | Run built-in system | +| `TASK_CUSTOM` | 1 | 1 | Run custom function | +| `XOR_EVENT_GATE` | 1 | 1+ | Wait for event and branch | +| `XOR_GATE` | 1 | 1+ | Branch on condition | diff --git a/doc/code/game_simulation/components.md b/doc/code/game_simulation/components.md index 5630426a4c..7bd3cb7113 100644 --- a/doc/code/game_simulation/components.md +++ b/doc/code/game_simulation/components.md @@ -5,7 +5,7 @@ Overview of the built-in game entity components in the game simulation. 1. [Internal](#internal) 1. [Activity](#activity) 2. [CommandQueue](#commandqueue) - 3. [Owner](#owner) + 3. [Ownership](#ownership) 4. [Position](#position) 2. [API](#api) 1. [Idle](#idle) @@ -21,7 +21,7 @@ store runtime data. ### Activity -![Activity Component UML](ASDF) +![Activity Component UML](images/component_activity_uml.svg) The `Activity` component stores a reference to the top-level activity for the game entity. Essentially, this gives access to the entire activity node graph @@ -37,7 +37,7 @@ should be canceled via the `cancel_events(..)` method. ### CommandQueue -![CommandQueue Component UML](ASDF) +![CommandQueue Component UML](images/component_component_activity_uml.svg) The `CommandQueue` component stores commands for the game entity in a [queue curve container](/doc/code/curves.md#queue). @@ -45,16 +45,16 @@ Commands in the queue use `Command` class derivatives which specify a command ty and payload for the command. -### Owner +### Ownership -![Owner Component UML](ASDF) +![Ownership Component UML](images/component_ownership_uml.svg) -The `Owner` component stores the ID of the player who owns the game entity. +The `Ownership` component stores the ID of the player who owns the game entity. ### Position -![Position Component UML](ASDF) +![Position Component UML](images/component_position_uml.svg) The `Position` component stores the location and direction of the game entity inside the game world. @@ -83,7 +83,7 @@ component. ### Idle -![Idle Component UML](ASDF) +![Idle Component UML](images/component_idle_uml.svg) **nyan API object:** [`engine.ability.type.Idle`](/doc/nyan/api_reference/reference_ability.md#abilitytypeidle) @@ -95,7 +95,7 @@ The component stores no runtime data. ### Live -![Live Component UML](ASDF) +![Live Component UML](images/component_live_uml.svg) **nyan API object:** [`engine.ability.type.Live`](/doc/nyan/api_reference/reference_ability.md#abilitytypelive) @@ -108,7 +108,7 @@ on a discrete curve. ### Move -![Move Component UML](ASDF) +![Move Component UML](images/component_move_uml.svg) **nyan API object:** [`engine.ability.type.Move`](/doc/nyan/api_reference/reference_ability.md#abilitytypemove) @@ -120,7 +120,7 @@ The component stores no runtime data. ### Turn -![Turn Component UML](ASDF) +![Turn Component UML](images/component_turn_uml.svg) **nyan API object:** [`engine.ability.type.Turn`](/doc/nyan/api_reference/reference_ability.md#abilitytypeturn) diff --git a/doc/code/game_simulation/game_entity.md b/doc/code/game_simulation/game_entity.md index af6ef34391..9886ac928c 100644 --- a/doc/code/game_simulation/game_entity.md +++ b/doc/code/game_simulation/game_entity.md @@ -13,7 +13,7 @@ Game entities represent objects inside the game world. ## Architecture -![Game entity UML](ASDF) +![Game entity UML](images/game_entity_overview.svg) Game entity mechanics are structured using the concept of *separation of concerns*. This means that there is not one single `GameEntity` class that implements all data @@ -34,7 +34,7 @@ because we can't think of anything better (and because we are too lazy to find b ## Game Entity class -![Game entity class UML](ASDF) +![Game entity class UML](images/game_entity_uml.svg) The `GameEntity` class primarily provides references to the game entity's unique ID, assigned components, and event handling manager. If the game entity is supposed to be animated, @@ -52,7 +52,7 @@ of the specific entity can be accessed via the `GameEntity` object's `get_compon For a description of the available components, check the [component reference](/doc/code/game_simulation/components.md). -![Component class UML](ASDF) +![Component class UML](images/component_uml.svg) Components are data storage objects for a game entity that also perform the dual role of signifying what a game entity can do or is depending on their component type. Component @@ -111,7 +111,7 @@ between systems may quickly become unmanageable. ### Activities -![Activity Example](ASDF) +![Activity Example](images/activity_graph.svg) *Activities* connect systems together in a node graph to describe the overall control flow for a game entity. In openage, activity node graphs are used to model the complex behaviour @@ -132,7 +132,7 @@ It can happen automatically or be triggered by an event. In the latter case, the event is handled by the `GameEntityManager` which calls an activity *system* that processes the event to choose the next node. -![Activity Workflow](ASDF) +![Activity Workflow](images/activity_workflow.png) ### Manager diff --git a/doc/code/game_simulation/images/activity_graph.svg b/doc/code/game_simulation/images/activity_graph.svg new file mode 100644 index 0000000000..bf87f779fa --- /dev/null +++ b/doc/code/game_simulation/images/activity_graph.svg @@ -0,0 +1,4 @@ + + + +IdleMoveStartMoveable?Can MoveCan't MoveWait for commandWait for Move to FinishEnd \ No newline at end of file diff --git a/doc/code/game_simulation/images/activity_workflow.png b/doc/code/game_simulation/images/activity_workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..188d48d3cc659287a98cb2306b597957dd4af7cb GIT binary patch literal 112675 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 + +NodeActivityActivitystart_activity: Activitynode: curve::Discretescheduled_events: vector<Event>get_start_activity(): Activityget_node(time_t): Nodeset_node(time_t, Node): voidinit(time_t): voidadd_event(Event): voidcancel_events(): void diff --git a/doc/code/game_simulation/images/component_command_queue_uml.svg b/doc/code/game_simulation/images/component_command_queue_uml.svg new file mode 100644 index 0000000000..cabb92d98b --- /dev/null +++ b/doc/code/game_simulation/images/component_command_queue_uml.svg @@ -0,0 +1,51 @@ + + +CommandCommandQueuecommand_queue: curve::Queueadd_command(time_t, Command): voidget_queue(): curve::Queue diff --git a/doc/code/game_simulation/images/component_idle_uml.svg b/doc/code/game_simulation/images/component_idle_uml.svg new file mode 100644 index 0000000000..439a7c39af --- /dev/null +++ b/doc/code/game_simulation/images/component_idle_uml.svg @@ -0,0 +1,25 @@ + + +Idle diff --git a/doc/code/game_simulation/images/component_live_uml.svg b/doc/code/game_simulation/images/component_live_uml.svg new file mode 100644 index 0000000000..b7d47a963e --- /dev/null +++ b/doc/code/game_simulation/images/component_live_uml.svg @@ -0,0 +1,33 @@ + + +Liveattribute_values: curve::UnorderedMapadd_attribute(time_t, fqon_t, curve::Discrete)set_attribute(time_t, fqon_t, int64_t): void diff --git a/doc/code/game_simulation/images/component_move_uml.svg b/doc/code/game_simulation/images/component_move_uml.svg new file mode 100644 index 0000000000..6076927c78 --- /dev/null +++ b/doc/code/game_simulation/images/component_move_uml.svg @@ -0,0 +1,25 @@ + + +Move diff --git a/doc/code/game_simulation/images/component_ownership_uml.svg b/doc/code/game_simulation/images/component_ownership_uml.svg new file mode 100644 index 0000000000..141bd5c7ef --- /dev/null +++ b/doc/code/game_simulation/images/component_ownership_uml.svg @@ -0,0 +1,33 @@ + + +Ownershipowner: curve::Discreteset_owner(time_t, ownership_id_t): voidget_owners(): curve::Discrete diff --git a/doc/code/game_simulation/images/component_position_uml.svg b/doc/code/game_simulation/images/component_position_uml.svg new file mode 100644 index 0000000000..6f090fcb9e --- /dev/null +++ b/doc/code/game_simulation/images/component_position_uml.svg @@ -0,0 +1,39 @@ + + +Positionposition: curve::Continuousangle: curve::Segmentedget_positions(): curve::Continuousget_angles(): curve::Segmentedset_position(time_t, coord::phys3): voidset_angle(time_t, phys_angle_t): void diff --git a/doc/code/game_simulation/images/component_turn_uml.svg b/doc/code/game_simulation/images/component_turn_uml.svg new file mode 100644 index 0000000000..9f06b9f662 --- /dev/null +++ b/doc/code/game_simulation/images/component_turn_uml.svg @@ -0,0 +1,25 @@ + + +Turn diff --git a/doc/code/game_simulation/images/component_uml.svg b/doc/code/game_simulation/images/component_uml.svg new file mode 100644 index 0000000000..06c5c3c74e --- /dev/null +++ b/doc/code/game_simulation/images/component_uml.svg @@ -0,0 +1,170 @@ + + +TurnMoveLiveIdlePositionOwnershipCommandQueueActivity«interface»APIComponent«interface»InternalComponent«interface»Component diff --git a/doc/code/game_simulation/images/game_entity_overview.svg b/doc/code/game_simulation/images/game_entity_overview.svg new file mode 100644 index 0000000000..f57806cd39 --- /dev/null +++ b/doc/code/game_simulation/images/game_entity_overview.svg @@ -0,0 +1,73 @@ + + +systems...GameEntityManagerComponentGameEntity diff --git a/doc/code/game_simulation/images/game_entity_uml.svg b/doc/code/game_simulation/images/game_entity_uml.svg new file mode 100644 index 0000000000..d3a24bdfd6 --- /dev/null +++ b/doc/code/game_simulation/images/game_entity_uml.svg @@ -0,0 +1,94 @@ + + +WorldRenderEntityGameEntityManagerComponentGameEntityid: entity_id_tcomponents: unordered_maprender_entity: WorldRenderEntitymanager: GameEntityManagercopy(): GameEntityget_component(): Componentadd_component(Component): voidhas_component(component_t): boolrender_update(time_t, string): void diff --git a/doc/code/game_simulation/images/simulation_uml.svg b/doc/code/game_simulation/images/simulation_uml.svg new file mode 100644 index 0000000000..36b67458cd --- /dev/null +++ b/doc/code/game_simulation/images/simulation_uml.svg @@ -0,0 +1,134 @@ + + +TimeLoopEventLoopTerrainGameEntityGameStateGameSimulationrunning: boolgame: Gameset_modpacks(vector<string>): voidinit_event_handlers(): voidrun(): voidstart(): voidstop(): voidGame diff --git a/doc/code/game_simulation/images/system_idle.svg b/doc/code/game_simulation/images/system_idle.svg new file mode 100644 index 0000000000..3da9289b05 --- /dev/null +++ b/doc/code/game_simulation/images/system_idle.svg @@ -0,0 +1,28 @@ + + +Idleidle(GameEntity, time_t): time_t diff --git a/doc/code/game_simulation/images/system_move.svg b/doc/code/game_simulation/images/system_move.svg new file mode 100644 index 0000000000..46966a4b4f --- /dev/null +++ b/doc/code/game_simulation/images/system_move.svg @@ -0,0 +1,30 @@ + + +Movemove_default(GameEntity, phys3, time_t): time_tmove_command(GameEntity, time_t): time_t diff --git a/doc/code/game_simulation/systems.md b/doc/code/game_simulation/systems.md index 672d8b8659..61ea2fcf34 100644 --- a/doc/code/game_simulation/systems.md +++ b/doc/code/game_simulation/systems.md @@ -2,12 +2,24 @@ Overview of the built-in systems in the game simulation. -1. [Move](#move) -2. [Idle](#idle) +1. [Idle](#idle) +2. [Move](#move) + + +## Idle + +![Idle systems class UML](images/system_idle.svg) + +Handles idle actions for game entities. + +`idle(..)` updates the animation of the game entity. This requires the game +entity to have the `Idle` component. The function returns a time of 0 since +no actionsconsuming simulation time are taken. + ## Move -![Move systems class UML](ASDF) +![Move systems class UML](images/system_move.svg) Handles movement actions for game entities. @@ -22,14 +34,3 @@ initiated by this function. `move_command(..)` processes the payload from a move *command* to call `move_default(..)` with the payload parameters. - - -## Idle - -![Idle systems class UML](ASDF) - -Handles idle actions for game entities. - -`idle(..)` updates the animation of the game entity. This requires the game -entity to have the `Idle` component. The function returns a time of 0 since -no actionsconsuming simulation time are taken. diff --git a/doc/code/images/curve_classes_uml.svg b/doc/code/images/curve_classes_uml.svg new file mode 100644 index 0000000000..95295b7c13 --- /dev/null +++ b/doc/code/images/curve_classes_uml.svg @@ -0,0 +1,149 @@ + + +UnorderedMapQueueKeyframeKeyframeContainerSegmentedContinuous«interface»InterpolatedDiscrete«interface»BaseCurve diff --git a/doc/code/images/event_classes_uml.svg b/doc/code/images/event_classes_uml.svg new file mode 100644 index 0000000000..9db8ffea01 --- /dev/null +++ b/doc/code/images/event_classes_uml.svg @@ -0,0 +1,124 @@ + + +EventEntityStateEventHandlerEventEventQueueEventLoop diff --git a/doc/code/images/primitive_curves_uml.svg b/doc/code/images/primitive_curves_uml.svg new file mode 100644 index 0000000000..1ff0be8d94 --- /dev/null +++ b/doc/code/images/primitive_curves_uml.svg @@ -0,0 +1,171 @@ + + +KeyframeKeyframeContainerSegmentedset_insert_jump(time_t, T, T): voidset_last_jump(time_t, T, T): voidContinuousset_insert(time_t, T): voidset_last(time_t, T): void«interface»Interpolatedget(time_t): TDiscreteget(time_t): Tget_time(time_t): Tget_previous(time_t): T«interface»BaseCurve<T>get(time_t): Tframe(time_t): pair<time_t, T>next_frame(time_t): pair<time_t, T>set_insert(time_t, T): voidset_last(time_t, T): voidset_replace(time_t, T): voidsync(BaseCurve<T>, time_t): void diff --git a/doc/code/images/time_classes_uml.svg b/doc/code/images/time_classes_uml.svg new file mode 100644 index 0000000000..aed04e35b7 --- /dev/null +++ b/doc/code/images/time_classes_uml.svg @@ -0,0 +1,71 @@ + + +Clockupdate_time(): voidget_time(): time_tget_real_time(): time_tget_speed(): speed_tset_speed(speed_t): voidstart(): voidstop(): voidpause(): voidresume(): voidTimeLoopget_clock(): Clockrun(): voidstart(): voidstop(): void diff --git a/doc/code/time.md b/doc/code/time.md index 18b8b93846..e546ae1c4b 100644 --- a/doc/code/time.md +++ b/doc/code/time.md @@ -9,7 +9,7 @@ To keep track of and synchronize time between different engine components, opena a low-level time control interface in the `openage::time` namespace. This system is very simple: Basically, it consists of a loop in a dedicated thread that constantly updates an internal clock. -![time loop UML](ASDF) +![time loop UML](images/time_classes_uml.svg) `TimeLoop` provides a basic interface to create an internal clock and run the update loop. Once initialized, the `Clock` instance can be accessed with the `get_clock()` method. For most calculations, From 839012306b2a289ffdd37867900f00bc6db0ed46 Mon Sep 17 00:00:00 2001 From: heinezen Date: Fri, 4 Aug 2023 17:47:46 +0200 Subject: [PATCH 26/49] doc: Curve type visualization. --- doc/code/curves.md | 8 ++++---- doc/code/images/continuous_curve.png | Bin 0 -> 31124 bytes doc/code/images/discrete_curve.png | Bin 0 -> 21507 bytes doc/code/images/segmented_curve.png | Bin 0 -> 36313 bytes 4 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 doc/code/images/continuous_curve.png create mode 100644 doc/code/images/discrete_curve.png create mode 100644 doc/code/images/segmented_curve.png diff --git a/doc/code/curves.md b/doc/code/curves.md index a100fd789b..d9cc2de6c8 100644 --- a/doc/code/curves.md +++ b/doc/code/curves.md @@ -34,7 +34,7 @@ from desync states. Data is not changed incrementally but is instead calculated *keyframe interpolation*. In other words, changes to data are treated as keyframes on a timeline with in-between values being interpolated from adjacent keyframes. -![Curve keyframes example](ASDF) +![Curve keyframes example](images/continuous_curve.png) Additionally, curves cannot only access values for the current simulation time but also for any past or even future times. Keyframes can also be inserted for any point in time without @@ -138,7 +138,7 @@ for specific curve types. #### Discrete -![Discrete curve function example](ASDF) +![Discrete curve function example](images/discrete_curve.png) Discrete curves implement **constant interpolation** between keyframes. This means that the value returned by `get(t)` is always equal to the value of the previous @@ -150,7 +150,7 @@ e.g. for the health of a game entity. #### Continuous -![Continuous curve function example](ASDF) +![Continuous curve function example](images/continuous_curve.png) Continuous curves implement **linear interpolation** between keyframes. This means that the value returned by `get(t)` is calculated from the value difference @@ -171,7 +171,7 @@ e.g. a game entity's position or the construction progress of a building. #### Segmented -![Segmented curve function example](ASDF) +![Segmented curve function example](images/segmented_curve.png) Segmented curves implement **linear interpolation** between keyframes and additionally allow jumps between keyframe values. As with continuous curves, the value returned by `get(t)` diff --git a/doc/code/images/continuous_curve.png b/doc/code/images/continuous_curve.png new file mode 100644 index 0000000000000000000000000000000000000000..a7bc2371111b8f8f6f507fa33a45d8af9b2b3fc2 GIT binary patch literal 31124 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�f+gI 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 Mj*)iBC5ONM8vufT_y7O^ literal 0 HcmV?d00001 From 163ad2c35c654fc58bb57ab1bb4dfe27ca5e70d0 Mon Sep 17 00:00:00 2001 From: heinezen Date: Fri, 4 Aug 2023 17:48:08 +0200 Subject: [PATCH 27/49] doc: Add image sources. --- doc/code/images/component_classes.uxf | 250 +++++++++++++ doc/code/images/continuous_curve.ggb | Bin 0 -> 4100 bytes doc/code/images/curves_classes.uxf | 418 ++++++++++++++++++++++ doc/code/images/discrete_curve.ggb | Bin 0 -> 3502 bytes doc/code/images/event_classes.uxf | 177 ++++++++++ doc/code/images/game_entity_classes.uxf | 450 ++++++++++++++++++++++++ doc/code/images/segmented_curve.ggb | Bin 0 -> 4527 bytes doc/code/images/simulation.uxf | 162 +++++++++ doc/code/images/system_classes.uxf | 31 ++ doc/code/images/time_classes.uxf | 76 ++++ 10 files changed, 1564 insertions(+) create mode 100644 doc/code/images/component_classes.uxf create mode 100644 doc/code/images/continuous_curve.ggb create mode 100644 doc/code/images/curves_classes.uxf create mode 100644 doc/code/images/discrete_curve.ggb create mode 100644 doc/code/images/event_classes.uxf create mode 100644 doc/code/images/game_entity_classes.uxf create mode 100644 doc/code/images/segmented_curve.ggb create mode 100644 doc/code/images/simulation.uxf create mode 100644 doc/code/images/system_classes.uxf create mode 100644 doc/code/images/time_classes.uxf diff --git a/doc/code/images/component_classes.uxf b/doc/code/images/component_classes.uxf new file mode 100644 index 0000000000..7329e11d96 --- /dev/null +++ b/doc/code/images/component_classes.uxf @@ -0,0 +1,250 @@ + + + // Uncomment the following line to change the fontsize and font: +fontsize=14 +// fontfamily=SansSerif //possible: SansSerif,Serif,Monospaced + + +////////////////////////////////////////////////////////////////////////////////////////////// +// Welcome to UMLet! +// +// Double-click on elements to add them to the diagram, or to copy them +// Edit elements by modifying the text in this panel +// Hold Ctrl to select multiple elements +// Use Ctrl+mouse to select via lasso +// +// Use +/- or Ctrl+mouse wheel to zoom +// Drag a whole relation at its central square icon +// +// Press Ctrl+C to copy the whole diagram to the system clipboard (then just paste it to, eg, Word) +// Edit the files in the "palettes" directory to create your own element palettes +// +// Select "Custom Elements > New..." to create new element types +////////////////////////////////////////////////////////////////////////////////////////////// + + +// This text will be stored with each diagram; use it for notes. + 15 + + UMLClass + + 720 + 435 + 420 + 240 + + **CommandQueue** +-- +command_queue: curve::Queue +-- +add_command(time_t, Command): void +get_queue(): curve::Queue + + + + Text + + 660 + 0 + 540 + 105 + + **Components** +style=wordwrap +fontsize=20 + + + + UMLClass + + 1260 + 435 + 195 + 90 + + +**Command** + + + + Relation + + 1125 + 465 + 165 + 45 + + lt=<. + 90.0;10.0;10.0;10.0 + + + UMLClass + + 720 + 75 + 420 + 300 + + **Activity** +-- +start_activity: Activity +node: curve::Discrete +scheduled_events: vector<Event> +-- +get_start_activity(): Activity +get_node(time_t): Node +set_node(time_t, Node): void +init(time_t): void +add_event(Event): void +cancel_events(): void + + + + + UMLClass + + 1260 + 75 + 195 + 90 + + +**Activity** + + + + Relation + + 1125 + 105 + 165 + 45 + + lt=<. + 90.0;10.0;10.0;10.0 + + + UMLClass + + 1260 + 210 + 195 + 90 + + +**Node** + + + + Relation + + 1125 + 240 + 165 + 45 + + lt=<. + 90.0;10.0;10.0;10.0 + + + Relation + + 1350 + 150 + 45 + 90 + + lt=<. + 10.0;40.0;10.0;10.0 + + + UMLClass + + 720 + 720 + 420 + 240 + + **Ownership** +-- +owner: curve::Discrete +-- +set_owner(time_t, ownership_id_t): void +get_owners(): curve::Discrete + + + + UMLClass + + 720 + 1020 + 420 + 240 + + **Position** +-- +position: curve::Continuous +angle: curve::Segmented +-- +get_positions(): curve::Continuous +get_angles(): curve::Segmented +set_position(time_t, coord::phys3): void +set_angle(time_t, phys_angle_t): void + + + + UMLClass + + 855 + 1305 + 165 + 90 + + +**Idle** + + + + + UMLClass + + 720 + 1455 + 465 + 240 + + **Live** +-- +attribute_values: curve::UnorderedMap +-- +add_attribute(time_t, fqon_t, curve::Discrete) +set_attribute(time_t, fqon_t, int64_t): void + + + + UMLClass + + 855 + 1740 + 165 + 90 + + +**Move** + + + + + UMLClass + + 855 + 1890 + 165 + 90 + + +**Turn** + + + + diff --git a/doc/code/images/continuous_curve.ggb b/doc/code/images/continuous_curve.ggb new file mode 100644 index 0000000000000000000000000000000000000000..e55f6ee9268ea7c45e61dc5048cc98b669c58f23 GIT binary patch literal 4100 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 literal 0 HcmV?d00001 diff --git a/doc/code/images/curves_classes.uxf b/doc/code/images/curves_classes.uxf new file mode 100644 index 0000000000..351ad7255e --- /dev/null +++ b/doc/code/images/curves_classes.uxf @@ -0,0 +1,418 @@ + + + // Uncomment the following line to change the fontsize and font: +fontsize=14 +// fontfamily=SansSerif //possible: SansSerif,Serif,Monospaced + + +////////////////////////////////////////////////////////////////////////////////////////////// +// Welcome to UMLet! +// +// Double-click on elements to add them to the diagram, or to copy them +// Edit elements by modifying the text in this panel +// Hold Ctrl to select multiple elements +// Use Ctrl+mouse to select via lasso +// +// Use +/- or Ctrl+mouse wheel to zoom +// Drag a whole relation at its central square icon +// +// Press Ctrl+C to copy the whole diagram to the system clipboard (then just paste it to, eg, Word) +// Edit the files in the "palettes" directory to create your own element palettes +// +// Select "Custom Elements > New..." to create new element types +////////////////////////////////////////////////////////////////////////////////////////////// + + +// This text will be stored with each diagram; use it for notes. + 15 + + UMLClass + + 510 + 120 + 195 + 90 + + <<interface>> +**BaseCurve** +bg=pink + + + + + UMLClass + + 390 + 510 + 180 + 90 + + +**Discrete** +bg=green + + + + UMLClass + + 720 + 315 + 180 + 90 + + <<interface>> +**Interpolated** +bg=pink + + + + + UMLClass + + 615 + 510 + 180 + 90 + + +**Continuous** +bg=green + + + + + UMLClass + + 840 + 510 + 165 + 90 + + +**Segmented** +bg=green + + + + + Relation + + 510 + 195 + 45 + 345 + + lt=<<- + 10.0;10.0;10.0;210.0 + + + Relation + + 660 + 195 + 90 + 195 + + lt=<<- + 10.0;10.0;10.0;110.0;40.0;110.0 + + + Relation + + 750 + 390 + 45 + 150 + + lt=<<- + 10.0;10.0;10.0;80.0 + + + Relation + + 855 + 390 + 45 + 150 + + lt=<<- + 10.0;10.0;10.0;80.0 + + + UMLClass + + 930 + 120 + 255 + 90 + + +**KeyframeContainer** + + + + + Relation + + 690 + 150 + 270 + 45 + + lt=<. + 160.0;10.0;10.0;10.0 + + + UMLClass + + 1275 + 120 + 150 + 90 + + +**Keyframe** + + + + + Relation + + 1170 + 150 + 135 + 45 + + lt=<. + 70.0;10.0;10.0;10.0 + + + UMLClass + + 1050 + 510 + 165 + 90 + + +**Queue** +bg=green + + + + + UMLClass + + 1260 + 510 + 195 + 90 + + +**UnorderedMap** +bg=green + + + + + Text + + 300 + 0 + 375 + 105 + + **Overview** +style=wordwrap +fontsize=20 + + + + Text + + 315 + 675 + 375 + 105 + + **Primitive Curves** +style=wordwrap +fontsize=20 + + + + UMLClass + + 330 + 765 + 360 + 270 + + <<interface>> +**BaseCurve<T>** +bg=pink +-- +/get(time_t): T/ +frame(time_t): pair<time_t, T> +next_frame(time_t): pair<time_t, T> +set_insert(time_t, T): void +set_last(time_t, T): void +set_replace(time_t, T): void +sync(BaseCurve<T>, time_t): void + + + + UMLClass + + 270 + 1290 + 270 + 135 + + **Discrete** +bg=green +-- +get(time_t): T +get_time(time_t): T +get_previous(time_t): T + + + + UMLClass + + 765 + 1095 + 255 + 120 + + <<interface>> +**Interpolated** +bg=pink +-- +get(time_t): T + + + + UMLClass + + 600 + 1290 + 270 + 135 + + **Continuous** +bg=green +-- +set_insert(time_t, T): void +set_last(time_t, T): void + + + + UMLClass + + 930 + 1290 + 345 + 135 + + **Segmented** +bg=green +-- +set_insert_jump(time_t, T, T): void +set_last_jump(time_t, T, T): void + + + + + Relation + + 420 + 1020 + 45 + 300 + + lt=<<- + 10.0;10.0;10.0;180.0 + + + Relation + + 615 + 1020 + 180 + 150 + + lt=<<- + 10.0;10.0;10.0;80.0;100.0;80.0 + + + Relation + + 780 + 1200 + 45 + 120 + + lt=<<- + 10.0;10.0;10.0;60.0 + + + Relation + + 975 + 1200 + 45 + 120 + + lt=<<- + 10.0;10.0;10.0;60.0 + + + UMLClass + + 915 + 750 + 255 + 90 + + +**KeyframeContainer** + + + + + Relation + + 675 + 780 + 270 + 45 + + lt=<. + 160.0;10.0;10.0;10.0 + + + UMLClass + + 1260 + 750 + 150 + 90 + + +**Keyframe** + + + + + Relation + + 1155 + 780 + 135 + 45 + + lt=<. + 70.0;10.0;10.0;10.0 + + diff --git a/doc/code/images/discrete_curve.ggb b/doc/code/images/discrete_curve.ggb new file mode 100644 index 0000000000000000000000000000000000000000..c49ddd65c6f78a67eb052efef2be98be2a67dd7a GIT binary patch literal 3502 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 TPjhxdK|yu4y3Y3Tzn}jLK!l); literal 0 HcmV?d00001 diff --git a/doc/code/images/event_classes.uxf b/doc/code/images/event_classes.uxf new file mode 100644 index 0000000000..d9be3fd902 --- /dev/null +++ b/doc/code/images/event_classes.uxf @@ -0,0 +1,177 @@ + + + // Uncomment the following line to change the fontsize and font: +fontsize=14 +// fontfamily=SansSerif //possible: SansSerif,Serif,Monospaced + + +////////////////////////////////////////////////////////////////////////////////////////////// +// Welcome to UMLet! +// +// Double-click on elements to add them to the diagram, or to copy them +// Edit elements by modifying the text in this panel +// Hold Ctrl to select multiple elements +// Use Ctrl+mouse to select via lasso +// +// Use +/- or Ctrl+mouse wheel to zoom +// Drag a whole relation at its central square icon +// +// Press Ctrl+C to copy the whole diagram to the system clipboard (then just paste it to, eg, Word) +// Edit the files in the "palettes" directory to create your own element palettes +// +// Select "Custom Elements > New..." to create new element types +////////////////////////////////////////////////////////////////////////////////////////////// + + +// This text will be stored with each diagram; use it for notes. + 15 + + UMLClass + + 750 + 450 + 180 + 90 + + +**EventLoop** + + + + UMLClass + + 1095 + 450 + 180 + 90 + + +**EventQueue** + + + + UMLClass + + 1005 + 645 + 150 + 90 + + +**Event** + + + + UMLClass + + 735 + 780 + 180 + 90 + + +**EventHandler** + + + + UMLClass + + 585 + 645 + 120 + 90 + + +**State** + + + + Relation + + 915 + 480 + 210 + 45 + + lt=<. + 120.0;10.0;10.0;10.0 + + + Relation + + 870 + 525 + 165 + 195 + + lt=<. + 90.0;110.0;10.0;110.0;10.0;10.0 + + + Relation + + 1110 + 525 + 45 + 150 + + lt=<. + 10.0;80.0;10.0;10.0 + + + Relation + + 795 + 525 + 45 + 285 + + lt=<. + 10.0;170.0;10.0;10.0 + + + Relation + + 900 + 720 + 165 + 135 + + lt=<. + 10.0;70.0;90.0;70.0;90.0;10.0 + + + Relation + + 630 + 480 + 150 + 195 + + lt=<. + 10.0;110.0;10.0;10.0;80.0;10.0 + + + UMLClass + + 1185 + 780 + 150 + 90 + + +**EventEntity** + + + + Relation + + 1095 + 720 + 120 + 135 + + lt=<. + 60.0;70.0;10.0;70.0;10.0;10.0 + + diff --git a/doc/code/images/game_entity_classes.uxf b/doc/code/images/game_entity_classes.uxf new file mode 100644 index 0000000000..fedf65862a --- /dev/null +++ b/doc/code/images/game_entity_classes.uxf @@ -0,0 +1,450 @@ + + + 15 + + UMLClass + + 0 + 390 + 390 + 285 + + **GameEntity** +-- +id: entity_id_t +components: unordered_map +render_entity: WorldRenderEntity +manager: GameEntityManager +-- +copy(): GameEntity +get_component(): Component +add_component(Component): void +has_component(component_t): bool +render_update(time_t, string): void + + + + UMLClass + + 510 + 405 + 210 + 90 + + +**Component** + + + + UMLClass + + 510 + 555 + 255 + 90 + + +**GameEntityManager** + + + + UMLClass + + 75 + 765 + 255 + 90 + + +**WorldRenderEntity** + + + + Relation + + 180 + 660 + 45 + 135 + + lt=<. + 10.0;70.0;10.0;10.0 + + + Relation + + 375 + 585 + 165 + 45 + + lt=<.> + 90.0;10.0;10.0;10.0 + + + Relation + + 375 + 435 + 165 + 45 + + lt=<. + 90.0;10.0;10.0;10.0 + + + UMLClass + + 90 + 0 + 210 + 90 + + +**GameEntity** + + + + UMLClass + + 375 + 0 + 210 + 90 + + +**Component** + + + + UMLClass + + 90 + 165 + 255 + 90 + + +**GameEntityManager** + + + + Relation + + 195 + 75 + 45 + 120 + + lt=<.> + 10.0;60.0;10.0;10.0 + + + Relation + + 285 + 30 + 120 + 45 + + lt=<. + 60.0;10.0;10.0;10.0 + + + Relation + + 330 + 195 + 135 + 45 + + lt=<. + 70.0;10.0;10.0;10.0 + + + Text + + 465 + 195 + 150 + 105 + + /systems.../ + + + + UMLClass + + 1290 + 375 + 210 + 90 + + <<interface>> +**Component** +bg=pink + + + + UMLClass + + 1095 + 525 + 270 + 90 + + <<interface>> +**InternalComponent** +bg=pink + + + + UMLClass + + 1425 + 525 + 255 + 90 + + <<interface>> +**APIComponent** +bg=pink + + + + Relation + + 1320 + 450 + 45 + 105 + + lt=<<- + 10.0;10.0;10.0;50.0 + + + Relation + + 1440 + 450 + 45 + 105 + + lt=<<- + 10.0;10.0;10.0;50.0 + + + UMLClass + + 1065 + 675 + 210 + 90 + + +**Activity** +bg=green + + + + UMLClass + + 1065 + 780 + 210 + 90 + + +**CommandQueue** +bg=green + + + + UMLClass + + 1065 + 885 + 210 + 90 + + +**Ownership** +bg=green + + + + UMLClass + + 1065 + 990 + 210 + 90 + + +**Position** +bg=green + + + + UMLClass + + 1515 + 675 + 210 + 90 + + +**Idle** +bg=green + + + + UMLClass + + 1515 + 780 + 210 + 90 + + +**Live** +bg=green + + + + UMLClass + + 1515 + 885 + 210 + 90 + + +**Move** +bg=green + + + + UMLClass + + 1515 + 990 + 210 + 90 + + +**Turn** +bg=green + + + + Relation + + 1305 + 600 + 45 + 465 + + lt=<<- + 10.0;10.0;10.0;290.0 + + + Relation + + 1455 + 600 + 45 + 465 + + lt=<<- + 10.0;10.0;10.0;290.0 + + + Relation + + 1260 + 705 + 90 + 45 + + lt=- + 40.0;10.0;10.0;10.0 + + + Relation + + 1260 + 810 + 90 + 45 + + lt=- + 40.0;10.0;10.0;10.0 + + + Relation + + 1260 + 915 + 90 + 45 + + lt=- + 40.0;10.0;10.0;10.0 + + + Relation + + 1260 + 1020 + 90 + 45 + + lt=- + 40.0;10.0;10.0;10.0 + + + Relation + + 1455 + 1020 + 90 + 45 + + lt=- + 40.0;10.0;10.0;10.0 + + + Relation + + 1455 + 915 + 90 + 45 + + lt=- + 40.0;10.0;10.0;10.0 + + + Relation + + 1455 + 810 + 90 + 45 + + lt=- + 40.0;10.0;10.0;10.0 + + + Relation + + 1455 + 705 + 90 + 45 + + lt=- + 40.0;10.0;10.0;10.0 + + diff --git a/doc/code/images/segmented_curve.ggb b/doc/code/images/segmented_curve.ggb new file mode 100644 index 0000000000000000000000000000000000000000..97beced399ca7062afd04a79231836daad73beba GIT binary patch literal 4527 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 P0wBP*5BPBsa<%#|?a~%8 literal 0 HcmV?d00001 diff --git a/doc/code/images/simulation.uxf b/doc/code/images/simulation.uxf new file mode 100644 index 0000000000..e7fc0893f7 --- /dev/null +++ b/doc/code/images/simulation.uxf @@ -0,0 +1,162 @@ + + + 15 + + UMLClass + + 1170 + 525 + 150 + 90 + + +**Game** + + + + UMLClass + + 705 + 495 + 375 + 285 + + **GameSimulation** +-- +running: bool +game: Game +-- +set_modpacks(vector<string>): void +init_event_handlers(): void +run(): void +start(): void +stop(): void + + + + UMLClass + + 1170 + 690 + 180 + 90 + + +**GameState** + + + + UMLClass + + 1440 + 645 + 195 + 90 + + +**GameEntity** + + + + UMLClass + + 1440 + 750 + 165 + 90 + + +**Terrain** + + + + Relation + + 1335 + 690 + 135 + 45 + + lt=<. + 70.0;10.0;10.0;10.0 + + + Relation + + 1335 + 750 + 135 + 45 + + lt=<. + 70.0;10.0;10.0;10.0 + + + Relation + + 1245 + 600 + 45 + 120 + + lt=<. + 10.0;60.0;10.0;10.0 + + + Relation + + 1065 + 555 + 135 + 45 + + lt=<. + 70.0;10.0;10.0;10.0 + + + UMLClass + + 705 + 870 + 150 + 90 + + +**EventLoop** + + + + UMLClass + + 900 + 870 + 150 + 90 + + +**TimeLoop** + + + + Relation + + 960 + 765 + 45 + 135 + + lt=<. + 10.0;70.0;10.0;10.0 + + + Relation + + 765 + 765 + 45 + 135 + + lt=<. + 10.0;70.0;10.0;10.0 + + diff --git a/doc/code/images/system_classes.uxf b/doc/code/images/system_classes.uxf new file mode 100644 index 0000000000..7143f89e14 --- /dev/null +++ b/doc/code/images/system_classes.uxf @@ -0,0 +1,31 @@ + + + 15 + + UMLClass + + 795 + 405 + 510 + 165 + + **Move** +-- +move_default(GameEntity, phys3, time_t): time_t +move_command(GameEntity, time_t): time_t + + + + UMLClass + + 810 + 630 + 375 + 165 + + **Idle** +-- +idle(GameEntity, time_t): time_t + + + diff --git a/doc/code/images/time_classes.uxf b/doc/code/images/time_classes.uxf new file mode 100644 index 0000000000..55f0e4992c --- /dev/null +++ b/doc/code/images/time_classes.uxf @@ -0,0 +1,76 @@ + + + // Uncomment the following line to change the fontsize and font: +fontsize=14 +// fontfamily=SansSerif //possible: SansSerif,Serif,Monospaced + + +////////////////////////////////////////////////////////////////////////////////////////////// +// Welcome to UMLet! +// +// Double-click on elements to add them to the diagram, or to copy them +// Edit elements by modifying the text in this panel +// Hold Ctrl to select multiple elements +// Use Ctrl+mouse to select via lasso +// +// Use +/- or Ctrl+mouse wheel to zoom +// Drag a whole relation at its central square icon +// +// Press Ctrl+C to copy the whole diagram to the system clipboard (then just paste it to, eg, Word) +// Edit the files in the "palettes" directory to create your own element palettes +// +// Select "Custom Elements > New..." to create new element types +////////////////////////////////////////////////////////////////////////////////////////////// + + +// This text will be stored with each diagram; use it for notes. + 15 + + UMLClass + + 675 + 300 + 315 + 180 + + **TimeLoop** +-- +get_clock(): Clock +run(): void +start(): void +stop(): void + + + + UMLClass + + 1095 + 300 + 315 + 285 + + **Clock** +-- +update_time(): void +get_time(): time_t +get_real_time(): time_t +get_speed(): speed_t +set_speed(speed_t): void +start(): void +stop(): void +pause(): void +resume(): void + + + + Relation + + 975 + 375 + 150 + 45 + + lt=<. + 80.0;10.0;10.0;10.0 + + From d5007e23a9a678935379fac7d28fef7c4ae84b9f Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 5 Aug 2023 14:23:26 +0200 Subject: [PATCH 28/49] doc: Fix unclear instructions and broken links. --- README.md | 84 +++++-------- doc/{ => code}/architecture.md | 32 +++-- doc/code/coordinate-systems.md | 2 +- doc/code/event_system.md | 4 +- doc/code/game_simulation/README.md | 4 +- doc/code/game_simulation/components.md | 2 +- doc/code/game_simulation/game_entity.md | 8 +- doc/code/time.md | 4 +- doc/contributing.md | 8 +- doc/development.md | 9 +- doc/media/openage/modpacks.md | 2 +- doc/media/original-metadata.md | 2 +- doc/media_convert.md | 6 + doc/nyan/README.md | 2 +- doc/nyan/openage-lib.md | 2 +- doc/project_structure.md | 2 +- .../game_mechanics/formations.md | 2 +- .../game_mechanics/swgb/shields.md | 2 +- .../networking/01-general.md | 116 +++++++++--------- 19 files changed, 142 insertions(+), 151 deletions(-) rename doc/{ => code}/architecture.md (68%) diff --git a/README.md b/README.md index b3693ce6c8..1b5b060af1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ [![openage](/assets/logo/banner.svg)](http://openage.dev) ========================================================= -**openage**: a volunteer project to create a free engine clone of the *Genie Engine* used by *Age of Empires*, *Age of Empires II (HD)* and *Star Wars: Galactic Battlegrounds*, comparable to projects like [OpenMW](https://openmw.org/), [OpenRA](http://openra.net/), [OpenSAGE](https://github.com/OpenSAGE/OpenSAGE/), [OpenTTD](https://openttd.org/) and [OpenRCT2](https://openrct2.org/). At the moment we focus our efforts on the integration of *Age of Empires II*, while being primarily aimed at POSIX platforms such as **GNU/Linux**. +**openage**: a volunteer project to create a free engine clone of the *Genie Engine* used by *Age of Empires*, *Age of Empires II (HD)* and *Star Wars: Galactic Battlegrounds*, comparable to projects like [OpenMW](https://openmw.org/), [OpenRA](http://openra.net/), [OpenSAGE](https://github.com/OpenSAGE/OpenSAGE/), [OpenTTD](https://openttd.org/) and [OpenRCT2](https://openrct2.org/). openage uses the original game assets (such as sounds and graphics), but (for obvious reasons) doesn't ship them. -To play, you require *an original AoE II: TC, [AoE II: HD](http://store.steampowered.com/app/221380/)* or [AoE II: DE](https://store.steampowered.com/app/813780) installation (via [Wine](https://www.winehq.org/) or [Steam-Linux](doc/media_convert.md)). +To play, you require *[any of the original games (AoE1, AoE2)](/doc/media_convert.md)* or their *Definitive Edition* releases. [![github stars](https://img.shields.io/github/stars/SFTtech/openage.svg)](https://github.com/SFTtech/openage/stargazers) [![#sfttech on matrix.org](/assets/doc/matrixroom.svg)](https://matrix.to/#/#sfttech:matrix.org) @@ -13,31 +13,30 @@ To play, you require *an original AoE II: TC, [AoE II: HD](http://store.steampow Contact ------- -Contact | Where? ------------------|------- -Issue Tracker | [GitHub SFTtech/openage](https://github.com/SFTtech/openage/issues) -Development Blog | [blog.openage.dev](https://blog.openage.dev) -Subreddit | [![reddit](/assets/doc/reddit.svg) /r/openage](https://www.reddit.com/r/openage/) -Discussions | [GitHub Discussions](https://github.com/SFTtech/openage/discussions) -Matrix Chat | [![matrix](/assets/doc/matrix.svg) `#sfttech:matrix.org`](https://matrix.to/#/#sfttech:matrix.org) -Money Sink | [![money sink](/assets/doc/liberapay.svg)](https://liberapay.com/SFTtech) +| Contact | Where? | +| ---------------- | -------------------------------------------------------------------------------------------------- | +| Issue Tracker | [GitHub SFTtech/openage](https://github.com/SFTtech/openage/issues) | +| Development Blog | [blog.openage.dev](https://blog.openage.dev) | +| Subreddit | [![reddit](/assets/doc/reddit.svg) /r/openage](https://www.reddit.com/r/openage/) | +| Discussions | [GitHub Discussions](https://github.com/SFTtech/openage/discussions) | +| Matrix Chat | [![matrix](/assets/doc/matrix.svg) `#sfttech:matrix.org`](https://matrix.to/#/#sfttech:matrix.org) | +| Money Sink | [![money sink](/assets/doc/liberapay.svg)](https://liberapay.com/SFTtech) | Technical foundation -------------------- -Technology | Component ----------------|---------- -**C++20** | Engine core -**Python3** | Scripting, media conversion, in-game console, code generation -**Qt6** | Graphical user interface -**Cython** | Python/C++ Glue code -**CMake** | Build system -**OpenGL3.3** | Rendering, shaders -**SDL2** | Cross-platform Audio/Input/Window handling -**Opus** | Audio codec -[**nyan**](https://github.com/SFTtech/nyan) | Content Configuration and Modding -**Humans** | Mixing together all of the above +| Technology | Component | +| ------------------------------------------- | ------------------------------------------------------------- | +| **C++20** | Engine core | +| **Python3** | Scripting, media conversion, in-game console, code generation | +| **Cython** | Python/C++ Glue code | +| **Qt6** | Graphical user interface | +| **CMake** | Build system | +| **OpenGL** | Rendering, shaders | +| **Opus** | Audio codec | +| [**nyan**](https://github.com/SFTtech/nyan) | Content Configuration and Modding | +| **Humans** | Mixing together all of the above | Goals @@ -72,26 +71,17 @@ Current State of the Project We're implementing the internal game simulation (how units even do anything) with simplicity and extensibility in mind, so we had to get rid of the temporary (but kind of working) previous version. With these changes we can (finally) actually make use of our converted asset packs and our nyan API! We're working day and night to make gameplay return\*. -If you're interested, we wrote detailed explanations on our blog: [Part 1](https://blog.openage.dev/new-gamestate-2020.html), [Part 2](https://blog.openage.dev/engine-core-modules.html). +If you're interested, we wrote detailed explanations on our blog: [Part 1](https://blog.openage.dev/new-gamestate-2020.html), [Part 2](https://blog.openage.dev/engine-core-modules.html), [Monthly Devlog](https://blog.openage.dev/tag/news.html). *\* may not actually be every day and night* -Operating System | Build status -:------------------:|:--------------: -Debian Sid | [Todo: Kevin #11](https://github.com/SFTtech/kevin/issues/11) -Ubuntu 22.04 LTS | [![Ubuntu 22.04 build status](https://github.com/SFTTech/openage/actions/workflows/ubuntu-22.04.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/ubuntu-22.04.yml) -macOS | [![macOS build status](https://github.com/SFTtech/openage/workflows/macOS-CI/badge.svg)](https://github.com/SFTtech/openage/actions?query=workflow%3AmacOS-CI) -Windows Server 2019 | [![Windows Server 2019 build status](https://github.com/SFTtech/openage/actions/workflows/windows-server-2019.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/windows-server-2019.yml) -Windows Server 2022 | [![Windows Server 2022 build status](https://github.com/SFTtech/openage/actions/workflows/windows-server-2022.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/windows-server-2022.yml) - -* Which **platforms** are supported? - * Linux (primary), Windows & macOS (at least we try :) - -* What **features** are currently implemented? - * See [status page](https://github.com/SFTtech/openage/projects). - -* What's the **plan**? - * See [doc/milestones.md](/doc/milestones.md). We also have [lists of crazy xor good ideas](/doc/ideas) and a [technical overview for requested features](/doc/ideas/fr_technical_overview.md). +| Operating System | Build status | +| :-----------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| Debian Sid | [Todo: Kevin #11](https://github.com/SFTtech/kevin/issues/11) | +| Ubuntu 22.04 LTS | [![Ubuntu 22.04 build status](https://github.com/SFTTech/openage/actions/workflows/ubuntu-22.04.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/ubuntu-22.04.yml) | +| macOS | [![macOS build status](https://github.com/SFTtech/openage/workflows/macOS-CI/badge.svg)](https://github.com/SFTtech/openage/actions?query=workflow%3AmacOS-CI) | +| Windows Server 2019 | [![Windows Server 2019 build status](https://github.com/SFTtech/openage/actions/workflows/windows-server-2019.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/windows-server-2019.yml) | +| Windows Server 2022 | [![Windows Server 2022 build status](https://github.com/SFTtech/openage/actions/workflows/windows-server-2022.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/windows-server-2022.yml) | Installation Packages @@ -141,26 +131,14 @@ or the [bug tracker](https://github.com/SFTtech/openage/issues). Contributing ============ -You might ask yourself now "Yeah, this sounds cool and all, but how do *I* participate +You might ask yourself now "Sounds cool, but how do I participate and ~~get famous~~ contribute useful features?". Fortunately for you, there is a lot to do and we are very grateful for help. ## Where do I start? -* The engine has several [core parts](https://github.com/SFTtech/openage/projects) that need help. - You can look at the project related issues and find something for you, for example: - * **Asset Converter:** Converts whatever proprietary format used by a Age of Empires 2 into - open formats. Written mostly in Python 3. There are a lot of TODOs and beginner issues available - right now, so it's a good place to get your feet wet. - * **Game simulation:** Also known as the gameplay implementation. Written in C++, using the - Entity-Component-System paradigm in addition to an event-driven simulation. - * **Documentation:** We not only document code, but also anything technical about the Genie engine - and its games. If you like documenting [file formats](/doc/media) - or thoroughly investigating [game mechanics](/doc/reverse_engineering), - then this might be the right place to start. -* **Check the issues** [labelled with good first issues](https://github.com/SFTtech/openage/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). These are tasks that you can start right away - and don't require much previous knowledge. +* **Check the issues** [labelled with `good first issue`](https://github.com/SFTtech/openage/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). These are tasks that you can start right away and don't require much previous knowledge. * **Ask us** in the [chat](https://matrix.to/#/#sfttech:matrix.org). Someone there could need help with something. * You can also **take the initiative** and fix a bug you found, create an issue for discussion or diff --git a/doc/architecture.md b/doc/code/architecture.md similarity index 68% rename from doc/architecture.md rename to doc/code/architecture.md index 154a303361..4e7bbec9d1 100644 --- a/doc/architecture.md +++ b/doc/code/architecture.md @@ -2,6 +2,13 @@ openage is separated into many modules ("subsystems"). +1. [Overview](#overview) + 1. [Components](#components) + 2. [Utilities](#utilities) +2. [Information flow](#information-flow) + 1. [Current architecture](#current-architecture) + 2. [Goal architecture](#goal-architecture) + ## Overview @@ -13,28 +20,29 @@ All of them need to be revisited to implement the goal architecture. * Audio system * Configuration system -* [Coordinate system](code/coordinate-systems.md) -* [Converter](code/converter/) -* Input system +* [Coordinate system](coordinate-systems.md) +* [Converter](converter/) +* [Input system](input/) * Networking -* [Game rule database](nyan/) -* [Pathfinding](code/pathfinding.md) -* Rendering -* Simulation -* [User interface](code/gui.md) +* [Game data database](/doc/nyan/) +* [Rendering](renderer/) +* [Simulation](game_simulation/) +* [Time management](time.md) +* [User interface](gui.md) ### Utilities * Datastructures -* Error handling + * [Curves](curves.md) +* [Error handling](exceptions.md) * Filesystem abstraction * Job dispatching * Live reloading -* Logging system -* Python interface +* [Logging system](logger.md) +* [Python interface](pyinterface.md) * Random number generator -* Test and demo infrastructure +* [Test and demo infrastructure](testing.md) ## Information flow diff --git a/doc/code/coordinate-systems.md b/doc/code/coordinate-systems.md index 284575f1db..c537074a1e 100644 --- a/doc/code/coordinate-systems.md +++ b/doc/code/coordinate-systems.md @@ -205,7 +205,7 @@ viewport --> normalized device space (Eigen::Vector2f) The translations make use of current coordinate state (e.g. scroll position, stored in `CoordManager`) and additional infos to solve -degrees of freedom (see [below](#Parameters)). +degrees of freedom (see [below](#parameters)). * `input` -> `viewport`: just flip the y axis diff --git a/doc/code/event_system.md b/doc/code/event_system.md index 9c6b7dae05..6dda8a8064 100644 --- a/doc/code/event_system.md +++ b/doc/code/event_system.md @@ -21,7 +21,7 @@ configuration of the associated *[event handler](#event-handler-setup)* (`EventH Internally, the event is stored inside an *event queue* (`EventQueue`) instance sorts the events by invoke time. -Events are executed by passing the current [simulation time](/doc/code/time.md) to the +Events are executed by passing the current [simulation time](time.md) to the event loop's `reach_time(..)` method. This executes all events with invoke time ``` @@ -35,7 +35,7 @@ other events if there are dependencies between the events. Both the creation and execution of events require passing a `State` object that functions as a persistant global storage for values that are not linked to a specific event executions. -In practice, `State` is primarily used to pass the state of the [game simulation](/doc/code/game_simulation/README.md) +In practice, `State` is primarily used to pass the state of the [game simulation](/doc/code/game_simulation/) to the event system via an object of the derived `GameState` class. This object allows access to the indexing structures of the current game that can be used to retrieve the available game entities, for example. diff --git a/doc/code/game_simulation/README.md b/doc/code/game_simulation/README.md index 016f005cc8..0f8910a36b 100644 --- a/doc/code/game_simulation/README.md +++ b/doc/code/game_simulation/README.md @@ -4,7 +4,7 @@ used for gameplay. The subsystem only contains components that are strictly necessary to simulate a game. Everything else, -e.g. the [input system](/doc/code/input/README.md), [rendering](/doc/code/renderer/README.md), +e.g. the [input system](/doc/code/input/), [rendering](/doc/code/renderer/), networking or scripting, are handled as separate subsystems. 1. [Architecture](#architecture) @@ -24,7 +24,7 @@ gamestate via a `GameState` object which includes references to objects inside t (e.g. players or game entities). It also contains gameplay settings for the current session. "Physical" objects in the game world are represented by `Terrain` and `GameEntity`. `Terrain` -is used to model the underlying map terrain, while [`GameEntity`](/doc/code/game_simulation/game_entity.md) +is used to model the underlying map terrain, while [`GameEntity`](game_entity.md) is used for every type of world object (e.g. units, buildings, trees, resources, ambience). diff --git a/doc/code/game_simulation/components.md b/doc/code/game_simulation/components.md index 7bd3cb7113..dc385a368b 100644 --- a/doc/code/game_simulation/components.md +++ b/doc/code/game_simulation/components.md @@ -37,7 +37,7 @@ should be canceled via the `cancel_events(..)` method. ### CommandQueue -![CommandQueue Component UML](images/component_component_activity_uml.svg) +![CommandQueue Component UML](images/component_activity_uml.svg) The `CommandQueue` component stores commands for the game entity in a [queue curve container](/doc/code/curves.md#queue). diff --git a/doc/code/game_simulation/game_entity.md b/doc/code/game_simulation/game_entity.md index 9886ac928c..b511a626a9 100644 --- a/doc/code/game_simulation/game_entity.md +++ b/doc/code/game_simulation/game_entity.md @@ -50,7 +50,7 @@ of the specific entity can be accessed via the `GameEntity` object's `get_compon ## Component Data Storage -For a description of the available components, check the [component reference](/doc/code/game_simulation/components.md). +For a description of the available components, check the [component reference](components.md). ![Component class UML](images/component_uml.svg) @@ -88,7 +88,7 @@ make the game logic maintanable and extensible. ### System -For a description of the available systems, check the [system reference](/doc/code/game_simulation/systems.md). +For a description of the available systems, check the [system reference](systems.md). A *system* in openage is basically a function that operates on game entity components. They are explicitely separated from game entity and component objects @@ -119,7 +119,7 @@ behaviour and action chains of RTS game entities, while also allowing the behavi be as configurable as possible. One could also think of activities as a behaviour graph where paths are taken based on the inputs a game entity receives. The architecture of the activity control flow is described in more detail in the -[activity control flow documentation](/doc/code/game_simulation/activity.md). +[activity control flow documentation](activity.md). A game entity's current activity state is stored in its `Activity` component. This component holds a reference to the activity node graph used by the entity as well as the @@ -127,7 +127,7 @@ last visited node. This node describes which action/behavioural state the game entity currently is in. Advancement to the next node can be initiated in several ways, depending on the -[node type](/doc/code/game_simulation/activity.md#node-types) of the current node. +[node type](activity.md#node-types) of the current node. It can happen automatically or be triggered by an event. In the latter case, the event is handled by the `GameEntityManager` which calls an activity *system* that processes the event to choose the next node. diff --git a/doc/code/time.md b/doc/code/time.md index e546ae1c4b..fcbdde0b0f 100644 --- a/doc/code/time.md +++ b/doc/code/time.md @@ -1,8 +1,8 @@ # Simulation Time *Simulation time* is the driving force behind pretty much all internal routines of the engine. -Its used to schedule and execute events in the [event system](/doc/code/event_system.md), -managing data of game entities at runtime via [curves](/doc/code/curves.md), and time +Its used to schedule and execute events in the [event system](event_system.md), +managing data of game entities at runtime via [curves](curves.md), and time animations in the [renderer](/doc/code/renderer/level2.md). To keep track of and synchronize time between different engine components, openage implements diff --git a/doc/contributing.md b/doc/contributing.md index cdbbd6245a..6498b10f95 100644 --- a/doc/contributing.md +++ b/doc/contributing.md @@ -28,7 +28,7 @@ What's the concept? We basically do whatever we think is good to do. -The [openage architecture](/doc/architecture.md) is the foundation. +The [openage architecture](/doc/code/architecture.md) is the foundation. For our development concept, see the [development guide](/doc/development.md). If you need some inspiration [what to work on, see above](#what-can-i-do). @@ -81,15 +81,15 @@ For tiny stuff like typo fixes, just create your PR and be done with it. - You can always update to upstream state by [rebasing](#rebasing). - Discuss your ideas and your work: - On the [group chats](/README.md#contact) - - That way, "bad ideas" can be "resolved" beforehand and "better ideas" are found + - That way, "bad" ideas can be resolved beforehand and "better" ideas are found - "Release early and often!" also applies to pull requests! - - Once your branch has some visible work, create `[WIP]` pull request + - Once your branch has some visible work, create a draft/`[WIP]` pull request - Give the pull request a description of what you did or want to do, so we can discuss it - Make sure you are in the `copying.md` file - People will be able to look at your code and give feedback - You'll get free checks from the build bot -- Once your work is done, remove the `[WIP]` so it can be merged +- Once your work is done, mark it as ready for review/remove the `[WIP]` so it can be merged - Do the changes that are requested by the reviewers. - Aaaaaand you're done. diff --git a/doc/development.md b/doc/development.md index 99bc5650e0..05d5f2a26f 100644 --- a/doc/development.md +++ b/doc/development.md @@ -4,12 +4,11 @@ About development Dev team -------- -There is a "core dev team" (*mic_e*, *JJ*, *zuntrax*, but there's more) -who work on the project in their free time, +There is a "core dev team" who work on the project in their free time, but since we all have other life-originating interrupts and fluctuating motivation, our activity will alternate equally over time (just take a look at the commit statistics). We "core devs" have known each other personally for a long time; -thus, communication happens mostly over private channels (XMPP, Mumble, maybe even real life). +thus, communication happens mostly over private channels (Matrix chats, Mumble, maybe even real life). Development workflow @@ -20,7 +19,7 @@ or an issue on the [issue tracker](https://github.com/sfttech/openage/issues). The pull requests are then open for review by everybody. If the pull request author thinks that their goals aren't met yet, -it is tagged with `[WIP] $title`. +they create it as a draft or tag it with `[WIP] $title`. The author removes it once the goals are met. In the big picture, we're working on completing those WIP pull requests, @@ -32,7 +31,7 @@ just convenient to implement, or needed right now. We're releasing early and often: whenever we add/improve a feature or fix a bug and the code still compiles, we push the code to the github master. Once significant changes have accumulated, -we tag that commit with a new minor version. +we tag that commit with a new version. We don't have dedicated "subsystem maintainers" yet. You can track down the person who did things with `git log` or `git blame`. diff --git a/doc/media/openage/modpacks.md b/doc/media/openage/modpacks.md index 4bbf9c580e..f475a6a636 100644 --- a/doc/media/openage/modpacks.md +++ b/doc/media/openage/modpacks.md @@ -13,7 +13,7 @@ anything from minor enhancements to standalone games. Apart from game-related data, modpacks store configuration data for loading them into the engine. Most of this data is located in the -**[modpack definition file](###modpack-definition-file)**. Modpacks can define +**[modpack definition file](#modpack-definition-file)**. Modpacks can define their interaction with other modpacks by specifying **dependencies** and **conflicts**. diff --git a/doc/media/original-metadata.md b/doc/media/original-metadata.md index 318630fa34..5e5540fe44 100644 --- a/doc/media/original-metadata.md +++ b/doc/media/original-metadata.md @@ -5,7 +5,7 @@ All data relevant for the game (e.g. how much costs building the castle? What cultures exist? Can my priest overclock his "Wololo?") are stored in a binary format in the file `empires2_x1_p1.dat`. -The format is described in the [huge struct definition](/openage/convert/gamedata/empiresdat.py). +The format is described in the [huge struct definition](/openage/convert/value_object/read/media/datfile/empiresdat.py). These data files are basically a huge tree structure. diff --git a/doc/media_convert.md b/doc/media_convert.md index 1e4f07bae6..a6526de99e 100644 --- a/doc/media_convert.md +++ b/doc/media_convert.md @@ -41,4 +41,10 @@ The game will ask for your AoE II installation folder; examples include: ~/.steam/steam/SteamApps/common/Age2HD ~/Library/Application Support/Steam/steamapps/common/Age2HD +Alternatively, if your game installation is not found, you can trigger the conversion manually: + +``` +python3 -m openage convert --force --source-dir /path/to/game/install +``` + You will find the converted files in `assets/converted`. diff --git a/doc/nyan/README.md b/doc/nyan/README.md index 090fad8579..dcada2c2f6 100644 --- a/doc/nyan/README.md +++ b/doc/nyan/README.md @@ -32,7 +32,7 @@ civ bonuses and age upgrades. ## Technical specification -[nyan specification](https://github.com/SFTtech/nyan/blob/master/doc/nyan.md) +[nyan specification](https://github.com/SFTtech/nyan/blob/master/doc/README.md) ## openage specifics diff --git a/doc/nyan/openage-lib.md b/doc/nyan/openage-lib.md index 328065c148..751dd7fe97 100644 --- a/doc/nyan/openage-lib.md +++ b/doc/nyan/openage-lib.md @@ -16,7 +16,7 @@ This interface will be described here. ## Design principles -In addition to the [nyan design goals](https://github.com/SFTtech/nyan/blob/master/doc/nyan.md#design-goals), the openage mod API follows its own design principles. It is recommended to follow these principles when extending the API. +In addition to the [nyan design goals](https://github.com/SFTtech/nyan/blob/master/doc/README.md#design-idea), the openage mod API follows its own design principles. It is recommended to follow these principles when extending the API. **Single API tree** diff --git a/doc/project_structure.md b/doc/project_structure.md index 404a1c2caf..4b2498aae8 100644 --- a/doc/project_structure.md +++ b/doc/project_structure.md @@ -25,7 +25,7 @@ This file explains the modular structure of the project. ## Architecture -The [overall architecture](/doc/architecture.md) describes the conceptual overview. +The [overall architecture](/doc/code/architecture.md) describes the conceptual overview. ## Languages diff --git a/doc/reverse_engineering/game_mechanics/formations.md b/doc/reverse_engineering/game_mechanics/formations.md index 86d4fd2443..dd39371ccd 100644 --- a/doc/reverse_engineering/game_mechanics/formations.md +++ b/doc/reverse_engineering/game_mechanics/formations.md @@ -89,7 +89,7 @@ For a start one has to look at the behavior of an individual subformation. Let's ............ ``` -The number of lines (or `row_count`) in this subformation is always determined by the number of its units. Unit width and different unit types have no influence here. The `row_count` can be calculated by an algorithm ([see here](https://gist.github.com/Piruzzolo/e744c9da5ed18898fae85f29a560e90f)). After the value has been calculated the units will be sorted into the lines starting from the top left. +The number of lines (or `row_count`) in this subformation is always determined by the number of its units. Unit width and different unit types have no influence here. The `row_count` can be calculated by an algorithm. After the value has been calculated the units will be sorted into the lines starting from the top left. ``` ............ diff --git a/doc/reverse_engineering/game_mechanics/swgb/shields.md b/doc/reverse_engineering/game_mechanics/swgb/shields.md index 962182271f..2ab7a4d570 100644 --- a/doc/reverse_engineering/game_mechanics/swgb/shields.md +++ b/doc/reverse_engineering/game_mechanics/swgb/shields.md @@ -12,7 +12,7 @@ A shielded unit gradually fills a shield bar (originally yellow and placed under the health bar), that doubles its health points. Aditionally, shielded units have a yellow glow effect, and [the UI](user-interface.md#cap) lights the leftmost indicator in yellow when a shielded unit is selected, as visible -[here]([https://www.youtube.com/watch?v=pwAtMv_eiM4&feature=youtu.be&t=1786]). +[here](https://www.youtube.com/watch?v=pwAtMv_eiM4&t=1786). Shields are depleted first when taking hits, before the regular health bar. Regenerating shields do not affect the health bar. diff --git a/doc/reverse_engineering/networking/01-general.md b/doc/reverse_engineering/networking/01-general.md index 4ac6cc1fa7..e171eb4703 100644 --- a/doc/reverse_engineering/networking/01-general.md +++ b/doc/reverse_engineering/networking/01-general.md @@ -25,59 +25,59 @@ Sync packets can be further categorized into periodic (sent in regular intervals Packets are recognized by a one byte long "command byte" in the header. So far 12 different network commands have been identified. -Command | Purpose ---------|-------------- -0x31 | Sync -0x32 | Sync -0x35 | Sync (Lobby) -0x3e | Player-issued -0x41 | Sync -0x43 | Chat Message -0x44 | Sync -0x4d | Sync -0x51 | De-Sync -0x52 | Readying (Lobby) -0x53 | Sync -0x5a | Lobby +| Command | Purpose | +| ------- | ---------------- | +| 0x31 | Sync | +| 0x32 | Sync | +| 0x35 | Sync (Lobby) | +| 0x3e | Player-issued | +| 0x41 | Sync | +| 0x43 | Chat Message | +| 0x44 | Sync | +| 0x4d | Sync | +| 0x51 | De-Sync | +| 0x52 | Readying (Lobby) | +| 0x53 | Sync | +| 0x5a | Lobby | All packets with command `0x3e` have a second "command byte" after the header that represents the command a player has given ingame. To avoid confusion, we will call all player-issued commands "actions" and reserve the term "commands" for the actual network commands seen above. Furthermore, the identifier for a player's action will be called "action byte". 34 of these can be found in network packets. -Action | Purpose --------|------------ -0x00 | Primary Action (Attacking, Resource gathering, Boarding Transport Ship) -0x01 | Stop -0x02 | Primary Action (AI) -0x03 | Move -0x0a | Move (AI) -0x0b | Resign -0x10 | Set waypoint -0x12 | Stance -0x13 | Guard -0x14 | Follow -0x15 | Patrol -0x17 | Formation -0x1b | Save & Exit -0x1f | Coordinated Move (AI) -0x64 | Train unit (AI) -0x65 | Research -0x66 | Build -0x67 | Diplomacy/Cheats/Change Speed -0x69 | Build wall -0x6a | Delete -0x6b | Attack ground -0x6c | Tribute -0x6e | Repair -0x6f | Unboard/Ungarrison -0x72 | Toggle gate -0x73 | Flare -0x75 | Garrison/Stop building unit -0x77 | Train unit (Human) -0x78 | Rally point -0x7a | Sell -0x7b | Buy -0x7e | Drop relic -0x7f | Toggle townbell -0x80 | Back to work +| Action | Purpose | +| ------ | ----------------------------------------------------------------------- | +| 0x00 | Primary Action (Attacking, Resource gathering, Boarding Transport Ship) | +| 0x01 | Stop | +| 0x02 | Primary Action (AI) | +| 0x03 | Move | +| 0x0a | Move (AI) | +| 0x0b | Resign | +| 0x10 | Set waypoint | +| 0x12 | Stance | +| 0x13 | Guard | +| 0x14 | Follow | +| 0x15 | Patrol | +| 0x17 | Formation | +| 0x1b | Save & Exit | +| 0x1f | Coordinated Move (AI) | +| 0x64 | Train unit (AI) | +| 0x65 | Research | +| 0x66 | Build | +| 0x67 | Diplomacy/Cheats/Change Speed | +| 0x69 | Build wall | +| 0x6a | Delete | +| 0x6b | Attack ground | +| 0x6c | Tribute | +| 0x6e | Repair | +| 0x6f | Unboard/Ungarrison | +| 0x72 | Toggle gate | +| 0x73 | Flare | +| 0x75 | Garrison/Stop building unit | +| 0x77 | Train unit (Human) | +| 0x78 | Rally point | +| 0x7a | Sell | +| 0x7b | Buy | +| 0x7e | Drop relic | +| 0x7f | Toggle townbell | +| 0x80 | Back to work | When the game is recorded, the UDP data stream of a `0x3e` packet (without the header) is written straight into the .mgx files with few changes. Viewing the recording will therefore simulate the exact actions that were done by the players. For more information on this, check the **Further Reading** section below. @@ -89,12 +89,12 @@ Values in the network protocol can have a length of one, two or four byte. Littl The data is described with few data types, which are shown in the table below. -Length | Data Types ----------|------------ -1 byte | int8, (ASCII) char -2 byte | int16 -4 byte | int32, float -other | (1-dimensional) array +| Length | Data Types | +| ------ | --------------------- | +| 1 byte | int8, (ASCII) char | +| 2 byte | int16 | +| 4 byte | int32, float | +| other | (1-dimensional) array | Most of the fields present in the network protocol have a fixed length. The use cases for variable length fields are usually lists of `unit_id`s or waypoints and will be handled as arrays in this documentation. @@ -113,10 +113,10 @@ In this document, we will assume that AoC uses a carthesian coordinate system wi To get a better understanding of the networking design and the underlying principles, it might be beneficial to read these sources. [1] Dave Pottering, *1500 Archers on a 28.8: Network Programming in Age of Empires and Beyond*, 2001 -https://www.gamasutra.com/view/feature/131503/1500_archers_on_a_288_network_.php?page=1 +https://www.gamedeveloper.com/programming/1500-archers-on-a-28-8-network-programming-in-age-of-empires-and-beyond [2] Matthew Pritchard, *How to Hurt the Hackers: The Scoop on Internet Cheating and How You Can Combat It*, 2000 -https://www.gamasutra.com/view/feature/3149/how_to_hurt_the_hackers_the_scoop_.php +https://www.gamedeveloper.com/design/how-to-hurt-the-hackers-the-scoop-on-internet-cheating-and-how-you-can-combat-it [3] Stefan Kolb, *Age of Empires 2: The Conquerors — Savegame File Format Specification*, https://github.com/stefan-kolb/aoc-mgx-format From 77c5b17d60aaa3b9c1d6443c74fddd41e5a944f1 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 5 Aug 2023 16:14:55 +0200 Subject: [PATCH 29/49] doc: Create a doc for the different Python start subsystems. --- README.md | 16 ++++-- doc/code/testing.md | 8 +-- doc/running.md | 121 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 doc/running.md diff --git a/README.md b/README.md index 1b5b060af1..84637d29c2 100644 --- a/README.md +++ b/README.md @@ -106,15 +106,21 @@ If you need help, maybe our [troubleshooting guide](/doc/troubleshooting.md) hel Quickstart ---------- -* How do I get this to run on my box? - * See [doc/building.md](/doc/building.md). - -* I compiled everything. Now how do I run it? +* **How do I get this to run on my box?** + 1. [Fork](https://help.github.com/articles/fork-a-repo/) the repo. + 1. Install dependencies. See [doc/building.md](/doc/building.md#dependency-installation) to get instructions for your favorite platform. + 1. Build the project: + ``` + ./configure --download-nyan + make + ``` + +* **I compiled everything. Now how do I run it?** * Execute `./bin/run`. * [The convert script](/doc/media_convert.md) will transform original assets into openage formats, which are a lot saner and more moddable. * Use your brain and react to the things you'll see. -* Waaaaaah! It +* **Waaaaaah! It...** * segfaults * prints error messages I don't want to read * ate my dog diff --git a/doc/code/testing.md b/doc/code/testing.md index e23f4a366f..b64b51d9bb 100644 --- a/doc/code/testing.md +++ b/doc/code/testing.md @@ -15,9 +15,9 @@ Tests run without user interaction to check for errors automatically. All tests are run automatically by [Kevin](https://github.com/SFTtech/kevin/) for pullrequests. -You can invoke them with `./run test -a` or `make test` +You can invoke them with `bin/run test -a` or `make test` -Have a look at `./run test --help` for further options. +Have a look at `bin/run test --help` for further options. You are encouraged to write tests for all your contributions, as well as other components that currently lack testing. @@ -35,7 +35,7 @@ They usually produce lots of output on stdout or may even be interactive. Python All tests must be registered in `openage/testing/testlist.py` (else the game won't know about them). -Also see `./run test --help`. +Also see `bin/run test --help`. ## Adding new tests @@ -135,7 +135,7 @@ C++ demos don't support `argv`; if you want that, make it a Python demo in a `.p Similar to Python tests, but have one argument, `argv`. Pass arguments in the invocation: - ./run test -d prime_demo 100 + bin/run test -d prime_demo 100 Example demo: diff --git a/doc/running.md b/doc/running.md new file mode 100644 index 0000000000..ffcdf15e76 --- /dev/null +++ b/doc/running.md @@ -0,0 +1,121 @@ +# How to run openage? + +This document explains the different run modes in openage. + +1. [Quickstart](#quickstart) +2. [Modes](#modes) + 1. [`game`](#game) + 2. [`main`](#main) + 3. [`test`](#test) + 4. [`convert`](#convert) + 5. [`convert-file`](#convert-file) + 6. [`convert-export-api`](#convert-export-api) + 7. [`codegen`](#codegen) + + +## Quickstart + +After building the project with the commands + +``` +./configure +make +``` + +you can execute + +``` +bin/run +``` + +from the same subfolder. This automatically selects the [`game` mode](#game) as default +to start a new game instance and also creates all necessary configs. + +If prompts appear, follow the instructions and choose what you think is best. It's +almost idiot-proof! + + +## Modes + +Modes can be selected manually by appending the `bin/run` prompt with the mode name. + +### `game` + +``` +bin/run game +``` + +Start the engine and immediately create a new game instance. This run mode is supposed +to jump straight into a game (or replay recording). + +If no converted modpacks can be found, this mode will start with a prompt asking if +the user wants to convert any before initializing the game. + +It's the default run mode. + + +### `main` + +``` +bin/run main +``` + +This run mode is supposed to start a main menu or launcher which allows configuring a +game. Neither of these are implemented at the moment, so `main` just does the same +thing as `game`. + + +### `test` + +``` +bin/run test +``` + +Used for running [tests and engine demos](code/testing.md). These show off selected +subsystems of the engine. + + +### `convert` + +``` +bin/run convert +``` + +Runs the [asset conversion](media_convert.md) subsystem which creates openage modpacks +from original game installations. + + +### `convert-file` + +``` +bin/run convert-file +``` + +Allows converting single media files using the original game formats to open formats +(PNG for graphics, OPUS for sounds). + + +### `convert-export-api` + +``` +bin/run convert-export-api +``` + +Exports the [openage modding API](nyan/README.md) nyan objects to a modpack that can +be loaded by the engine. + +This is supposed to be temporary solution until the modding API is finalized. Currently, +the modding API nyan objects are hardcoded into the converter. In the future, the +modding API modpack should be part of the engine config which is loaded by both the engine +the converter subsystem as a single source of truth. + + +### `codegen` + +``` +bin/run codegen +``` + +Runs the code generation logic for the build process. In particular, this generates code +for [mako](https://www.makotemplates.org/) templates and creates the test lists for the +[testing](code/testing.md) subsystem. From 5f210e4daf79457d33eb7dd1428c509ef75259a5 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 5 Aug 2023 17:24:25 +0200 Subject: [PATCH 30/49] doc: Engine architecture rewrite. --- doc/code/architecture.md | 59 +- doc/code/images/engine_architecture.svg | 269 ++++ doc/code/images/engine_architecture.uxf | 1092 +++++++++++++++++ .../images/engine_target_architecture.svg | 344 ++++++ 4 files changed, 1745 insertions(+), 19 deletions(-) create mode 100644 doc/code/images/engine_architecture.svg create mode 100644 doc/code/images/engine_architecture.uxf create mode 100644 doc/code/images/engine_target_architecture.svg diff --git a/doc/code/architecture.md b/doc/code/architecture.md index 4e7bbec9d1..e0eda0701d 100644 --- a/doc/code/architecture.md +++ b/doc/code/architecture.md @@ -51,32 +51,53 @@ All of them need to be revisited to implement the goal architecture. The current data flow of openage is just to display the raw simulation data. +![Engine architecture workflow](images/engine_architecture.svg) + +The presenter, simulation, and time subsystems each run in a loop in their own threads. +They are largely decoupled from each other and only communicate via defined interfaces. + +Communication between all subsystems forms a larger *main* loop that encompasses everything +happening in an engine run. It's workflow is roughly like this: + ``` -input -> simulation -> renderer -> output +renderer (window system) -> input -> event system -> simulation -> renderer -> output ``` +openage does not have simulation steps or *ticks* since everything in the simulation is +event-based and scheduled by time. Therefore, updates between threads should work +asynchronously in general. + +Decoupling allows us to treat some subsystems as optional such as the renderer and input +system (basically everything from the presenter). + ### Goal architecture -``` -0 input -> -1 network -> -2 simulation -> -3 network -> -4 prediction/interpolation -> -5 renderer -> -6 output -``` +The goal architecture extends the current workflow by subsystems for networking and +scripting: + +![Goal architecture workflow](images/engine_target_architecture.svg) + +Both of these are supposed to be decoupled and optional. The current architecture can be extended +by adding the missing components in between. -For *singleplayer* we could bypass `1 network` and `3 network` by using direct function calls or talk to a local socket. -For *multiplayer* this means that we will have a single authoritative server that is just running -the simulation. Each client then receives the data visible for it. +Networking forwards the relevant events and simulation parameters during multiplayer. +We will have a single authoritative server that is also running the simulation asynchronously. +Each client then receives the data visible for it. -The current architecture can be extended by adding the missing components in -between. +Scripting extends the event system with external sources targets for events. Most +scripting should be integrated using event logic. -The `prediction/interpolation` reuses the `simulation` code, but is -non-authoritative: The data provided from `2` has higher priority. +The new workflow would then look something like this: -There exists a link of `0 input -> 4 prediction` so that input -is immediately accounted in the prediction, thus displayed. +``` + ---------> scripting + | ^ + | | + v v +renderer (window system) -> input -> event system -> simulation -> renderer -> output + ^ ^ + | | + | v + ---------> network +``` diff --git a/doc/code/images/engine_architecture.svg b/doc/code/images/engine_architecture.svg new file mode 100644 index 0000000000..c24199612c --- /dev/null +++ b/doc/code/images/engine_architecture.svg @@ -0,0 +1,269 @@ + + +Current timeAnimation requestsCurrent timeScheduled eventsCurrent timeEntity updatesInput eventsMouse eventsKey/Mouse eventsClockTimePresenterSimulationEventLoopGUIRendererInput diff --git a/doc/code/images/engine_architecture.uxf b/doc/code/images/engine_architecture.uxf new file mode 100644 index 0000000000..35b2297706 --- /dev/null +++ b/doc/code/images/engine_architecture.uxf @@ -0,0 +1,1092 @@ + + + // Uncomment the following line to change the fontsize and font: +fontsize=14 +// fontfamily=SansSerif //possible: SansSerif,Serif,Monospaced + + +////////////////////////////////////////////////////////////////////////////////////////////// +// Welcome to UMLet! +// +// Double-click on elements to add them to the diagram, or to copy them +// Edit elements by modifying the text in this panel +// Hold Ctrl to select multiple elements +// Use Ctrl+mouse to select via lasso +// +// Use +/- or Ctrl+mouse wheel to zoom +// Drag a whole relation at its central square icon +// +// Press Ctrl+C to copy the whole diagram to the system clipboard (then just paste it to, eg, Word) +// Edit the files in the "palettes" directory to create your own element palettes +// +// Select "Custom Elements > New..." to create new element types +////////////////////////////////////////////////////////////////////////////////////////////// + + +// This text will be stored with each diagram; use it for notes. + 15 + + UMLClass + + 690 + 345 + 195 + 90 + + +**Input** + + + + UMLClass + + 1020 + 180 + 195 + 90 + + +**Renderer** + + + + UMLClass + + 690 + 90 + 195 + 90 + + +**GUI** + + + + UMLClass + + 1035 + 600 + 195 + 90 + + +**EventLoop** + + + + UMLClass + + 1275 + 405 + 195 + 90 + + +**Simulation** + + + + Text + + 525 + 15 + 195 + 60 + + **Presenter** +fontsize=20 + +style=wordwrap + + + + Relation + + 510 + 0 + 780 + 45 + + lt=. + 10.0;10.0;500.0;10.0 + + + Relation + + 510 + 0 + 45 + 600 + + lt=. + 10.0;380.0;10.0;10.0 + + + Relation + + 510 + 555 + 450 + 45 + + lt=. + 280.0;10.0;10.0;10.0 + + + Relation + + 1245 + 0 + 45 + 360 + + lt=. + 10.0;220.0;10.0;10.0 + + + Relation + + 915 + 315 + 375 + 285 + + lt=. + 10.0;170.0;230.0;10.0 + + + Text + + 1620 + 90 + 105 + 60 + + **Time** +fontsize=20 + +style=wordwrap + + + + UMLClass + + 1500 + 180 + 135 + 90 + + +**Clock** + + + + Relation + + 1245 + 75 + 495 + 45 + + lt=. + 10.0;10.0;310.0;10.0 + + + Relation + + 1695 + 75 + 45 + 285 + + lt=. + 10.0;170.0;10.0;10.0 + + + Relation + + 1440 + 315 + 300 + 45 + + lt=. + 180.0;10.0;10.0;10.0 + + + Relation + + 1695 + 315 + 45 + 510 + + lt=. + 10.0;320.0;10.0;10.0 + + + Relation + + 1245 + 315 + 495 + 45 + + lt=. + 10.0;10.0;310.0;10.0 + + + Relation + + 915 + 780 + 825 + 45 + + lt=. + 530.0;10.0;10.0;10.0 + + + Relation + + 915 + 555 + 45 + 270 + + lt=. + 10.0;10.0;10.0;160.0 + + + Relation + + 840 + 225 + 210 + 150 + + lt=<- + 10.0;80.0;10.0;10.0;120.0;10.0 + + + Relation + + 765 + 165 + 45 + 210 + + lt=<- + 10.0;10.0;10.0;120.0 + + + Relation + + 870 + 375 + 255 + 255 + + lt=<- + 150.0;150.0;150.0;10.0;10.0;10.0 + + + Relation + + 1185 + 465 + 120 + 165 + + lt=<- + 60.0;10.0;10.0;10.0;10.0;90.0 + + + Relation + + 1155 + 255 + 150 + 195 + + lt=<- + 10.0;10.0;10.0;110.0;80.0;110.0 + + + Relation + + 1455 + 255 + 150 + 225 + + lt=<- + 10.0;130.0;80.0;130.0;80.0;10.0 + + + Relation + + 1200 + 210 + 330 + 45 + + lt=<- + 10.0;10.0;200.0;10.0 + + + Text + + 840 + 210 + 225 + 45 + + Key/Mouse events +fontsize=12 +style=wordwrap + + + + Text + + 630 + 225 + 150 + 45 + + Mouse events +fontsize=12 +style=wordwrap + + + + Text + + 915 + 390 + 150 + 45 + + Input events +fontsize=12 +style=wordwrap + + + + Text + + 1200 + 510 + 180 + 45 + + Entity updates +fontsize=12 +style=wordwrap + + + + Relation + + 1155 + 450 + 150 + 180 + + lt=<- + 10.0;100.0;10.0;10.0;80.0;10.0 + + + Text + + 1500 + 450 + 180 + 45 + + Current time +fontsize=12 +style=wordwrap + + + + Text + + 1110 + 435 + 180 + 45 + + Scheduled events +fontsize=12 +style=wordwrap + + + + Relation + + 1215 + 435 + 390 + 240 + + lt=<- + 10.0;140.0;240.0;140.0;240.0;10.0 + + + Text + + 1440 + 645 + 180 + 45 + + Current time +fontsize=12 +style=wordwrap + + + + Text + + 990 + 285 + 195 + 45 + + Animation requests +fontsize=12 +style=wordwrap + + + + Text + + 1380 + 195 + 180 + 45 + + Current time +fontsize=12 +style=wordwrap + + + + UMLClass + + 690 + 1215 + 195 + 90 + + +**Input** + + + + UMLClass + + 1020 + 1050 + 195 + 90 + + +**Renderer** + + + + UMLClass + + 690 + 960 + 195 + 90 + + +**GUI** + + + + UMLClass + + 1035 + 1470 + 195 + 90 + + +**EventLoop** + + + + UMLClass + + 1275 + 1275 + 195 + 90 + + +**Simulation** + + + + Text + + 525 + 885 + 195 + 60 + + **Presenter** +fontsize=20 + +style=wordwrap + + + + Relation + + 510 + 870 + 780 + 45 + + lt=. + 10.0;10.0;500.0;10.0 + + + Relation + + 510 + 870 + 45 + 600 + + lt=. + 10.0;380.0;10.0;10.0 + + + Relation + + 510 + 1425 + 450 + 45 + + lt=. + 280.0;10.0;10.0;10.0 + + + Relation + + 1245 + 870 + 45 + 360 + + lt=. + 10.0;220.0;10.0;10.0 + + + Relation + + 915 + 1185 + 375 + 285 + + lt=. + 10.0;170.0;230.0;10.0 + + + Text + + 1620 + 960 + 105 + 60 + + **Time** +fontsize=20 + +style=wordwrap + + + + UMLClass + + 1500 + 1050 + 135 + 90 + + +**Clock** + + + + Relation + + 1245 + 945 + 495 + 45 + + lt=. + 10.0;10.0;310.0;10.0 + + + Relation + + 1695 + 945 + 45 + 285 + + lt=. + 10.0;170.0;10.0;10.0 + + + Relation + + 1440 + 1185 + 300 + 45 + + lt=. + 180.0;10.0;10.0;10.0 + + + Relation + + 1695 + 1185 + 45 + 510 + + lt=. + 10.0;320.0;10.0;10.0 + + + Relation + + 1245 + 1185 + 495 + 45 + + lt=. + 10.0;10.0;310.0;10.0 + + + Relation + + 915 + 1650 + 825 + 45 + + lt=. + 530.0;10.0;10.0;10.0 + + + Relation + + 915 + 1425 + 45 + 270 + + lt=. + 10.0;10.0;10.0;160.0 + + + Relation + + 840 + 1095 + 210 + 150 + + lt=<- + 10.0;80.0;10.0;10.0;120.0;10.0 + + + Relation + + 765 + 1035 + 45 + 210 + + lt=<- + 10.0;10.0;10.0;120.0 + + + Relation + + 870 + 1245 + 255 + 255 + + lt=<- + 150.0;150.0;150.0;10.0;10.0;10.0 + + + Relation + + 1185 + 1335 + 120 + 165 + + lt=<- + 60.0;10.0;10.0;10.0;10.0;90.0 + + + Relation + + 1155 + 1125 + 150 + 195 + + lt=<- + 10.0;10.0;10.0;110.0;80.0;110.0 + + + Relation + + 1455 + 1125 + 150 + 225 + + lt=<- + 10.0;130.0;80.0;130.0;80.0;10.0 + + + Relation + + 1200 + 1080 + 330 + 45 + + lt=<- + 10.0;10.0;200.0;10.0 + + + Text + + 840 + 1080 + 225 + 45 + + Key/Mouse events +fontsize=12 +style=wordwrap + + + + Text + + 630 + 1095 + 150 + 45 + + Mouse events +fontsize=12 +style=wordwrap + + + + Text + + 915 + 1260 + 150 + 45 + + Input events +fontsize=12 +style=wordwrap + + + + Text + + 1200 + 1380 + 180 + 45 + + Entity updates +fontsize=12 +style=wordwrap + + + + Relation + + 1155 + 1320 + 150 + 180 + + lt=<- + 10.0;100.0;10.0;10.0;80.0;10.0 + + + Text + + 1500 + 1320 + 180 + 45 + + Current time +fontsize=12 +style=wordwrap + + + + Text + + 1110 + 1305 + 180 + 45 + + Scheduled events +fontsize=12 +style=wordwrap + + + + Relation + + 1215 + 1305 + 390 + 240 + + lt=<- + 10.0;140.0;240.0;140.0;240.0;10.0 + + + Text + + 1440 + 1515 + 180 + 45 + + Current time +fontsize=12 +style=wordwrap + + + + Text + + 990 + 1155 + 195 + 45 + + Animation requests +fontsize=12 +style=wordwrap + + + + Text + + 1380 + 1065 + 180 + 45 + + Current time +fontsize=12 +style=wordwrap + + + + UMLClass + + 1035 + 1770 + 195 + 90 + + +**Networking** + + + + UMLClass + + 660 + 1470 + 195 + 90 + + +**Scripting** + + + + Relation + + 840 + 1485 + 225 + 45 + + lt=<- + 130.0;10.0;10.0;10.0 + + + Relation + + 840 + 1515 + 225 + 45 + + lt=<- + 10.0;10.0;130.0;10.0 + + + Relation + + 1065 + 1545 + 45 + 255 + + lt=<- + 10.0;150.0;10.0;10.0 + + + Relation + + 1110 + 1545 + 45 + 255 + + lt=<- + 10.0;10.0;10.0;150.0 + + + Relation + + 1215 + 1350 + 225 + 510 + + lt=<- + 130.0;10.0;130.0;320.0;10.0;320.0 + + + Relation + + 1215 + 1350 + 195 + 480 + + lt=<- + 10.0;300.0;110.0;300.0;110.0;10.0 + + + Text + + 1335 + 1830 + 180 + 45 + + Server updates +fontsize=12 +style=wordwrap + + + + Text + + 1275 + 1755 + 180 + 45 + + Client data +fontsize=12 +style=wordwrap + + + + Text + + 1125 + 1665 + 180 + 45 + + Server events +fontsize=12 +style=wordwrap + + + + Text + + 960 + 1665 + 180 + 45 + + Client events +fontsize=12 +style=wordwrap + + + + Text + + 870 + 1530 + 180 + 45 + + Event notifiers +fontsize=12 +style=wordwrap + + + + Text + + 870 + 1470 + 180 + 45 + + Script events +fontsize=12 +style=wordwrap + + + diff --git a/doc/code/images/engine_target_architecture.svg b/doc/code/images/engine_target_architecture.svg new file mode 100644 index 0000000000..fbc026c06a --- /dev/null +++ b/doc/code/images/engine_target_architecture.svg @@ -0,0 +1,344 @@ + + +Script eventsEvent notifiersClient eventsServer eventsClient dataServer updatesScriptingNetworkingCurrent timeAnimation requestsCurrent timeScheduled eventsCurrent timeEntity updatesInput eventsMouse eventsKey/Mouse eventsClockTimePresenterSimulationEventLoopGUIRendererInput From 966a4bc4ed16e630bfbd7be8c2ba62ab76fadf62 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 14:28:37 +0200 Subject: [PATCH 31/49] engine: Fix import stop_token in llvm. --- libopenage/engine/engine.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libopenage/engine/engine.h b/libopenage/engine/engine.h index 40fe9882b9..0d6bc1feee 100644 --- a/libopenage/engine/engine.h +++ b/libopenage/engine/engine.h @@ -3,7 +3,6 @@ #pragma once #include -#include #include #include #include @@ -23,6 +22,8 @@ class jthread : public thread { }; } // namespace std #endif +#else +#include #endif From e15eca948e7aea2a62aeb982df596dd29d66bf9e Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 14:54:03 +0200 Subject: [PATCH 32/49] renderer: Enforce float sqrt calculation. --- libopenage/renderer/camera/camera.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libopenage/renderer/camera/camera.h b/libopenage/renderer/camera/camera.h index 23cd649b82..d82254cf5e 100644 --- a/libopenage/renderer/camera/camera.h +++ b/libopenage/renderer/camera/camera.h @@ -25,9 +25,9 @@ namespace camera { * pitch = -30 degrees */ static const Eigen::Vector3f cam_direction{ - -1 * (sqrt(6) / 4), + -1 * (std::sqrt(6.f) / 4), -0.5f, - -1 * (sqrt(6) / 4), + -1 * (std::sqrt(6.f) / 4), }; /** From 54f936f8410748b1d22bdbfd6d28a3dabd46e1d9 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 15:19:43 +0200 Subject: [PATCH 33/49] coord: Use math constants from engine typedefs. --- libopenage/coord/phys.cpp | 5 +++-- libopenage/coord/scene.cpp | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libopenage/coord/phys.cpp b/libopenage/coord/phys.cpp index 9f6ccacd3c..bb5e89498d 100644 --- a/libopenage/coord/phys.cpp +++ b/libopenage/coord/phys.cpp @@ -8,6 +8,7 @@ #include "coord/tile.h" #include "terrain/terrain.h" #include "util/math.h" +#include "util/math_constants.h" namespace openage::coord { @@ -38,7 +39,7 @@ phys_angle_t phys2_delta::to_angle(const coord::phys2_delta &other) const { auto det = other.ne.to_float() * this->se.to_float() - this->ne.to_float() * other.se.to_float(); auto dot = this->ne.to_float() * other.ne.to_float() + this->se.to_float() * other.se.to_float(); - auto angle = std::atan2(det, dot) * 180 / std::numbers::pi; + auto angle = std::atan2(det, dot) * 180 / math::PI; if (angle < 0) { angle += 360; } @@ -99,7 +100,7 @@ phys_angle_t phys3_delta::to_angle(const coord::phys2_delta &other) const { auto det = other.ne.to_float() * this->se.to_float() - this->ne.to_float() * other.se.to_float(); auto dot = this->ne.to_float() * other.ne.to_float() + this->se.to_float() * other.se.to_float(); - auto angle = std::atan2(det, dot) * 180 / std::numbers::pi; + auto angle = std::atan2(det, dot) * 180 / math::PI; if (angle < 0) { angle += 360; } diff --git a/libopenage/coord/scene.cpp b/libopenage/coord/scene.cpp index 7ea121750d..d34519bfe4 100644 --- a/libopenage/coord/scene.cpp +++ b/libopenage/coord/scene.cpp @@ -8,6 +8,7 @@ #include "coord/pixel.h" #include "coord/tile.h" #include "util/math.h" +#include "util/math_constants.h" namespace openage::coord { @@ -53,7 +54,7 @@ float scene2_delta::to_angle(const coord::scene2_delta &other) const { auto det = other.ne.to_float() * this->se.to_float() - this->ne.to_float() * other.se.to_float(); auto dot = this->ne.to_float() * other.ne.to_float() + this->se.to_float() * other.se.to_float(); - auto angle = std::atan2(det, dot) * 180 / std::numbers::pi; + auto angle = std::atan2(det, dot) * 180 / math::PI; if (angle < 0) { angle += 360; } From 75c9f3755c6d4f71eb1d91105da3952e91a4806d Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 15:39:10 +0200 Subject: [PATCH 34/49] util: Remove unused header. --- libopenage/datastructure/tests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libopenage/datastructure/tests.cpp b/libopenage/datastructure/tests.cpp index d481922a80..95fa02a9da 100644 --- a/libopenage/datastructure/tests.cpp +++ b/libopenage/datastructure/tests.cpp @@ -3,7 +3,6 @@ #include "tests.h" #include -#include #include #include #include From 7884e0758ba7aa085d1b835d978edc0dd04049dd Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 16:12:00 +0200 Subject: [PATCH 35/49] error: Fix Windows complaints about sighandler_t. --- libopenage/error/handlers.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libopenage/error/handlers.cpp b/libopenage/error/handlers.cpp index fe00f2c518..f608b0e78f 100644 --- a/libopenage/error/handlers.cpp +++ b/libopenage/error/handlers.cpp @@ -12,13 +12,9 @@ #include "handlers.h" -#include -#include #include #include -#include #include -#include #ifdef _MSC_VER #include @@ -28,11 +24,10 @@ #include "util/init.h" #include "util/language.h" +#include "util/signal.h" -#include "error/backtrace.h" #include "error/error.h" #include "error/stackanalyzer.h" -#include "util/compiler.h" namespace openage { From 70560a262097d9ffd17eb706c4ba5254aecff0a0 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 16:31:04 +0200 Subject: [PATCH 36/49] engine: Fix llvm problems with jthread. --- libopenage/engine/engine.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libopenage/engine/engine.h b/libopenage/engine/engine.h index 0d6bc1feee..d3afe6554d 100644 --- a/libopenage/engine/engine.h +++ b/libopenage/engine/engine.h @@ -16,6 +16,10 @@ namespace std { class jthread : public thread { public: using thread::thread; // needed constructors + jthread(const jthread &) = delete; + jthread &operator=(const jthread &) = delete; + jthread(jthread &&) = default; + jthread &operator=(jthread &&) = default; ~jthread() { this->join(); } From a63863253b0c9964fc11c6e6768eab74190aeef6 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 16:40:25 +0200 Subject: [PATCH 37/49] gui: Include additional Windows GL headers. --- libopenage/gui/guisys/private/gui_renderer_impl.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libopenage/gui/guisys/private/gui_renderer_impl.h b/libopenage/gui/guisys/private/gui_renderer_impl.h index 8e6f6a17fb..31c1330eea 100644 --- a/libopenage/gui/guisys/private/gui_renderer_impl.h +++ b/libopenage/gui/guisys/private/gui_renderer_impl.h @@ -3,6 +3,10 @@ #pragma once #ifndef __APPLE__ +#ifdef _MSC_VER +#define NOMINMAX +#include +#endif //_MSC_VER #include #else // __APPLE__ #include From 55eb1c5e511a3c9a2d419f6d5c01f0f8f1c92fc9 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 17:03:59 +0200 Subject: [PATCH 38/49] make: Run start commands from build directory. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index cedcf1bd60..da6c6a2ae9 100644 --- a/Makefile +++ b/Makefile @@ -22,14 +22,14 @@ install: $(BUILDDIR) .PHONY: run run: build - $(BUILDDIR)/run game + cd $(BUILDDIR) && ./run game .PHONY: test test: tests checkfast .PHONY: tests tests: build - $(BUILDDIR)/run test -a + cd $(BUILDDIR) && ./run test -a .PHONY: build build: $(BUILDDIR) From 9a311ce45e745992fe3095e69f9800beac97b25e Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 17:21:25 +0200 Subject: [PATCH 39/49] gamestate: Don't inline component type getters. --- libopenage/gamestate/component/api/idle.cpp | 2 +- libopenage/gamestate/component/api/live.cpp | 2 +- libopenage/gamestate/component/api/move.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libopenage/gamestate/component/api/idle.cpp b/libopenage/gamestate/component/api/idle.cpp index 4d80e9ecff..1292fbadaf 100644 --- a/libopenage/gamestate/component/api/idle.cpp +++ b/libopenage/gamestate/component/api/idle.cpp @@ -6,7 +6,7 @@ namespace openage::gamestate::component { -inline component_t Idle::get_type() const { +component_t Idle::get_type() const { return component_t::IDLE; } diff --git a/libopenage/gamestate/component/api/live.cpp b/libopenage/gamestate/component/api/live.cpp index b9e0d0cd57..01fafde5ea 100644 --- a/libopenage/gamestate/component/api/live.cpp +++ b/libopenage/gamestate/component/api/live.cpp @@ -12,7 +12,7 @@ namespace openage::gamestate::component { -inline component_t Live::get_type() const { +component_t Live::get_type() const { return component_t::LIVE; } diff --git a/libopenage/gamestate/component/api/move.cpp b/libopenage/gamestate/component/api/move.cpp index aa9ac73daf..4abd1b5c3e 100644 --- a/libopenage/gamestate/component/api/move.cpp +++ b/libopenage/gamestate/component/api/move.cpp @@ -7,7 +7,7 @@ namespace openage::gamestate::component { -inline component_t Move::get_type() const { +component_t Move::get_type() const { return component_t::MOVE; } From 02c32a8c76a6ded23786cbf5463813b27d1411f7 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 10 Aug 2023 18:08:19 +0200 Subject: [PATCH 40/49] main: Fix Python-C++ glue code on Windows. --- libopenage/main/tests.cpp | 2 ++ libopenage/main/tests.h | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/libopenage/main/tests.cpp b/libopenage/main/tests.cpp index 9ec4d77227..20b7369a52 100644 --- a/libopenage/main/tests.cpp +++ b/libopenage/main/tests.cpp @@ -1,5 +1,7 @@ // Copyright 2018-2023 the openage authors. See copying.md for legal info. +#include "tests.h" + #include "tests/pong.h" #include "tests/presenter.h" diff --git a/libopenage/main/tests.h b/libopenage/main/tests.h index 20e7c6edbd..fa9d8e4aef 100644 --- a/libopenage/main/tests.h +++ b/libopenage/main/tests.h @@ -2,13 +2,19 @@ #pragma once +#include "../util/compiler.h" // pxd: from libopenage.util.path cimport Path -#include "../util/path.h" -namespace openage::main::tests { +namespace openage { +namespace util { +class Path; +} // namespace util + +namespace main::tests { // pxd: void engine_demo(int demo_id, Path path) except + OAAPI void engine_demo(int demo_id, const util::Path &path); -} // namespace openage::main::tests +} // namespace main::tests +} // namespace openage From 6725ab7b5f1b0235d96b9228b1dfc33c2c57e7e5 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 23 Aug 2023 11:27:16 +0200 Subject: [PATCH 41/49] doc: Update README links. --- README.md | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 84637d29c2..f40bd91d3f 100644 --- a/README.md +++ b/README.md @@ -15,29 +15,39 @@ Contact ------- | Contact | Where? | | ---------------- | -------------------------------------------------------------------------------------------------- | -| Issue Tracker | [GitHub SFTtech/openage](https://github.com/SFTtech/openage/issues) | -| Development Blog | [blog.openage.dev](https://blog.openage.dev) | +| Issue Tracker | [GitHub SFTtech/openage] | +| Development Blog | [blog.openage.dev] | | Subreddit | [![reddit](/assets/doc/reddit.svg) /r/openage](https://www.reddit.com/r/openage/) | -| Discussions | [GitHub Discussions](https://github.com/SFTtech/openage/discussions) | +| Discussions | [GitHub Discussions] | | Matrix Chat | [![matrix](/assets/doc/matrix.svg) `#sfttech:matrix.org`](https://matrix.to/#/#sfttech:matrix.org) | | Money Sink | [![money sink](/assets/doc/liberapay.svg)](https://liberapay.com/SFTtech) | +[GitHub SFTtech/openage]: https://github.com/SFTtech/openage/issues +[blog.openage.dev]: https://blog.openage.dev +[GitHub Discussions]: https://github.com/SFTtech/openage/discussions Technical foundation -------------------- -| Technology | Component | -| ------------------------------------------- | ------------------------------------------------------------- | -| **C++20** | Engine core | -| **Python3** | Scripting, media conversion, in-game console, code generation | -| **Cython** | Python/C++ Glue code | -| **Qt6** | Graphical user interface | -| **CMake** | Build system | -| **OpenGL** | Rendering, shaders | -| **Opus** | Audio codec | -| [**nyan**](https://github.com/SFTtech/nyan) | Content Configuration and Modding | -| **Humans** | Mixing together all of the above | - +| Technology | Component | +| ------------ | ------------------------------------------------------------- | +| **C++20** | Engine core | +| **Python3** | Scripting, media conversion, in-game console, code generation | +| [**Cython**] | Python/C++ Glue code | +| [**Qt6**] | Graphical user interface | +| [**CMake**] | Build system | +| [**OpenGL**] | Rendering, shaders | +| [**Opus**] | Audio codec | +| [**nyan**] | Content Configuration and Modding | +| [**Humans**] | Mixing together all of the above | + +[**Cython**]: https://cython.org/ +[**Qt6**]: https://contribute.qt-project.org/ +[**CMake**]: https://cmake.org/ +[**OpenGL**]: https://www.opengl.org/ +[**Opus**]: https://opus-codec.org/ +[**nyan**]: https://github.com/SFTtech/nyan +[**Humans**]: https://www.youtube.com/watch?v=fQGbXmkSArs&t=20s Goals ----- @@ -77,12 +87,14 @@ If you're interested, we wrote detailed explanations on our blog: [Part 1](https | Operating System | Build status | | :-----------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| Debian Sid | [Todo: Kevin #11](https://github.com/SFTtech/kevin/issues/11) | +| Debian Sid | [Todo: Kevin #11] | | Ubuntu 22.04 LTS | [![Ubuntu 22.04 build status](https://github.com/SFTTech/openage/actions/workflows/ubuntu-22.04.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/ubuntu-22.04.yml) | | macOS | [![macOS build status](https://github.com/SFTtech/openage/workflows/macOS-CI/badge.svg)](https://github.com/SFTtech/openage/actions?query=workflow%3AmacOS-CI) | | Windows Server 2019 | [![Windows Server 2019 build status](https://github.com/SFTtech/openage/actions/workflows/windows-server-2019.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/windows-server-2019.yml) | | Windows Server 2022 | [![Windows Server 2022 build status](https://github.com/SFTtech/openage/actions/workflows/windows-server-2022.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/windows-server-2022.yml) | +[Todo: Kevin #11]: https://github.com/SFTtech/kevin/issues/11 + Installation Packages --------------------- @@ -107,9 +119,9 @@ Quickstart ---------- * **How do I get this to run on my box?** - 1. [Fork](https://help.github.com/articles/fork-a-repo/) the repo. - 1. Install dependencies. See [doc/building.md](/doc/building.md#dependency-installation) to get instructions for your favorite platform. - 1. Build the project: + 1. [Clone](https://docs.github.com/repositories/creating-and-managing-repositories/cloning-a-repository) the repo. + 2. Install dependencies. See [doc/building.md](/doc/building.md#dependency-installation) to get instructions for your favorite platform. + 3. Build the project: ``` ./configure --download-nyan make From b81eb2eec52e854fdb12bb074e74fdbd5ec9ec90 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 23 Aug 2023 12:30:25 +0200 Subject: [PATCH 42/49] doc: Rephrase architecture/curve/event documentation. --- doc/code/architecture.md | 4 ++-- doc/code/curves.md | 20 +++++++++++++------- doc/code/event_system.md | 41 ++++++++++++++++++++++------------------ 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/doc/code/architecture.md b/doc/code/architecture.md index e0eda0701d..a45cc73dd9 100644 --- a/doc/code/architecture.md +++ b/doc/code/architecture.md @@ -64,8 +64,8 @@ renderer (window system) -> input -> event system -> simulation -> renderer -> o ``` openage does not have simulation steps or *ticks* since everything in the simulation is -event-based and scheduled by time. Therefore, updates between threads should work -asynchronously in general. +event-based and scheduled by time. Therefore, updates between threads should generally work +asynchronously. Decoupling allows us to treat some subsystems as optional such as the renderer and input system (basically everything from the presenter). diff --git a/doc/code/curves.md b/doc/code/curves.md index d9cc2de6c8..2ead4a8c77 100644 --- a/doc/code/curves.md +++ b/doc/code/curves.md @@ -45,7 +45,7 @@ The usage of curves has a few downsides though. They are less space efficient du keyframe storage, interpolation are more costly more costly than incremental changes, and their integration is more complex than the usage of simpler data structures. However, in situations where operations are predictable, long-lasting, and easy to calculate - which -is the case for most RTS games - these downsides are usually outweight by the positives. +is the case for most RTS games - the positives may outweigh the downsides. ## Architecture @@ -95,12 +95,12 @@ the primitive C++ types (e.g. `int`, `float`, `std::string`), simple structs and and shared pointers. On contruction of a primitive curve object, a keyframe with time `t = time::time_t::min_value()` -and the default value of the value type is inserted. This is done to ensure that for any +and the value's type is instanciated with its default constructor. This is done to ensure that for any requested time `t`, there is always a valid value to be returned. This mirrors the expected behaviour from declaring primitive values in C++ where members may be auto-initialized without explicit assignment to a default value. The default value for curves can also be explicitely -assigned in the constructor. For value types that don't have default values, a default value -must always be passed in the constructor. +assigned in the constructor. Types that don't have a default constructor require that a +default value is passed to the curve constructor. `BaseCurve` objects can be targeted by or trigger events from the [event system](/doc/code/event_system.md). As a consequence, curves are not copy constructable as they require a unique ID for @@ -115,6 +115,8 @@ for specific curve types. **Read** +Read operations retrieve values for a specific point in time. + | Method | Description | | --------------- | ----------------------------------------------------------- | | `get(t)` | Get (interpolated) value at time `t` | @@ -123,6 +125,8 @@ for specific curve types. **Modify** +Modify operations insert values for a specific point in time. + | Method | Description | | ----------------------- | --------------------------------------------------------------------------------- | | `set_insert(t, value)` | Insert a new keyframe value at time `t` | @@ -131,9 +135,11 @@ for specific curve types. **Copy** -| Method | Description | -| ---------------- | -------------------------------------------------------------------------------------- | -| `sync(Curve, t)` | copy keyframes from `Curve`, starting at time `t`; delete all keyframes after time `t` | +Copy operations transfer keyframes from one curve to another. + +| Method | Description | +| ---------------- | ------------------------------------------------------------------------------------------------ | +| `sync(Curve, t)` | Replace all keyframes from self after time `t` with keyframes from source `Curve` after time `t` | #### Discrete diff --git a/doc/code/event_system.md b/doc/code/event_system.md index 6dda8a8064..9dcb327144 100644 --- a/doc/code/event_system.md +++ b/doc/code/event_system.md @@ -22,7 +22,8 @@ Internally, the event is stored inside an *event queue* (`EventQueue`) instance sorts the events by invoke time. Events are executed by passing the current [simulation time](time.md) to the -event loop's `reach_time(..)` method. This executes all events with invoke time +event loop's `reach_time(..)` method. This executes all not-yet-executed events +until the given target time stamp ``` t_old < t_e <= t_cur @@ -43,24 +44,28 @@ the available game entities, for example. ## Event Handler Setup -The logic for (re-)scheduling and invoking an event is implemented by derivatives of the -`EventHandler` class. Event handlers must implement 3 methods: +Event effects on the game simulation are implemented by *event handlers*. Event handlers +are attached to the event on creation and are invoked when the event is executed by the event +loop. They are also used for (re-)scheduling the invocation time of the event. + +Event handlers are implemented as derivatives of the `EventHandler` class. Event handlers +must implement 3 methods: * `invoke(..)`: Called by the event loop when the event is executed. -* `predict_invoke_time(..)`: Calculates the desired invoke time. Called when the event is first created and whenever - the event is due to be rescheduled (e.g. if a dependency event changes). +* `predict_invoke_time(..)`: Called when the event handler shall determine a new invocation time of an event, + i.e. (re)place it on the event queue * `setup_event(..)`: Sets up dependencies during creation of the event. Additionally, event handlers require a unique ID (defined as a string) as well as a *trigger type* which defines under which circumstances an event is executed. These parameters can be defined at runtime when instantiating the event handler. -To enable events to use the event handler, an event handler object must first -be initialized and then passed to the event loop. This can either be done explicitly via -the `add_event_handler(..)` method of the `EventLoop` class or during the -creation of an event via an overloaded `create_event(..)` method. Once an event -handler is registered, new events can also be created using the event handler's -unique ID string when calling `create_event(..)`. +New events require that an event handler object is attached during creation. This +can be done by passing the event handler object to the `create_event(..)` method. +If the event handler should be reusable, it can also be registered on the event loop +via the `add_event_handler(..)` method. When calling `create_event(..)` on the event +loop to create a new event, the event handler can then be referenced by a unique +ID string. ## Event Instances @@ -69,21 +74,21 @@ When calling `create_event(..)` on the event loop, an `Event` instance is return allows access to the configuration parameters of the event. Event instances store references to: -* **Event payload**: Map of parameters passed to the event handler on invocation. * **Event target**: Entity targeted by the event. Implemented as a weak reference to let the target expire. * **Event handler**: Event handler object used for rescheduling and executing the event. * **Invoke time**: Determines when the event is executed. * **Time of last change**: Time of the last change to invocation time. Used for rescheduling the event. +* **Event payload**: Optional map of parameters passed to the event handler on invocation. + +These parameters may be changed after creating the event. Alternatively, it is possible to +[cancel](#canceling-events) the event and create a new event with the updated parameters. -These parameters may be changed after creating the event. However, in most cases it's -more beneficial to create a new event instead and [cancel](#canceling-events) the old -event. ## Triggering/Targeting Events -Both sending and receiving events is managed by the same interface, i.e. the `EventEntity` -class. By inheriting from `EventEntity`, a class can be targeted by events, trigger -or change events or both. +To allow an event to modify an object in its invocation, the object's class must be +a derivative of the `EventEntity` class. By inheriting from `EventEntity`, a class can +be targeted by events, trigger or change events, or both. An event target is passed during the creation of an event (i.e. in `create_event(..)`) and later when executing the event via the event handler's `invoke(..)` method. Events From 4f9f44cb16df733236ec8c0b3d983ebbe29e16c3 Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 30 Aug 2023 02:24:41 +0200 Subject: [PATCH 43/49] doc: Renderer uniform ID usage. --- doc/code/renderer/level1.md | 44 +++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/doc/code/renderer/level1.md b/doc/code/renderer/level1.md index e7ed4ed933..8a7d4e1eb0 100644 --- a/doc/code/renderer/level1.md +++ b/doc/code/renderer/level1.md @@ -12,9 +12,10 @@ Low-level renderer for communicating with the OpenGL and Vulkan APIs. 3. [Creating a Renderable](#creating-a-renderable) 4. [Rendering and Displaying the Result](#rendering-and-displaying-the-result) 4. [Advanced Usage](#advanced-usage) - 1. [Framebuffers / Multiple Render Passes](#framebuffers--multiple-render-passes) - 2. [Complex Geometry](#complex-geometry) - 3. [Uniform Buffers](#uniform-buffers) + 1. [Addressing Uniforms via numeric IDs](#addressing-uniforms-via-numeric-ids) + 2. [Framebuffers / Multiple Render Passes](#framebuffers--multiple-render-passes) + 3. [Complex Geometry](#complex-geometry) + 4. [Uniform Buffers](#uniform-buffers) 5. [Thread-safety](#thread-safety) @@ -142,10 +143,14 @@ std::shared_ptr input = shader_prog->new_uniform_input( ); ``` -Input values are passed to the method in pairs consisting of the uniform variable -name (as a string) and the input value. The definition order doesn't matter and the -method doesn't differentiate between different shader stages, so uniform inputs -for vertex and fragment shaders can be freely mixed. +Note that the definition order doesn't matter and the method doesn't differentiate +between different shader stages, so uniform inputs for vertex and fragment shaders +can be freely mixed. + +Input values are passed to the method in pairs consisting of the uniform ID and the +input value. Uniform IDs can either be the uniform name from the shader source (as +shown above) or a numeric ID that is determined at load time by the shader program. +Numeric ID usage is explained in [this section](#addressing-uniforms-via-numeric-ids). Uniform input values are automatically converted to the correct types expected by the uniform definition, e.g. a `uint8_t` for a uniform with type `uint` will be transformed to the @@ -209,6 +214,31 @@ window->update(); These are some of the more advanced features of the renderer. +### Addressing Uniforms via numeric IDs + +Numeric uniform IDs are unique identifiers for a uniform in a shader program. They are +assigned at load time and can be used to address uniforms instead of their string names. +The type used for numeric IDs is `renderer::uniform_id_t`. The numeric ID of a uniform +can be fetched from the shader program using the uniform name by calling the +`renderer::ShaderProgram::get_uniform_id(..)` method. + +```c++ +uniform_id_t color_id = shader_prog->get_uniform_id("color"); +uniform_id_t time_id = shader_prog->get_uniform_id("time"); +uniform_id_t num_id = shader_prog->get_uniform_id("num"); +std::shared_ptr input = shader_prog->new_uniform_input( + color_id, Eigen::Vector3f{ 0.0f, 1.0f, 0.0f }, + time_id, 0.0f, + num_id, 1337 +); +``` + +Setting uniform values via numeric IDs can be much faster than using strings as +string lookups are avoided. This is especially useful for uniforms which are updated +very frequently, e.g. every frame. However, this requires that the IDs are fetched +at runtime and have to be stored somewhere. + + ### Framebuffers / Multiple Render Passes Sometimes it is useful to render the scene in multiple passes, e.g. for post-processing From 5dacafddf94523f632eb9995d5e7586db61b514c Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 2 Sep 2023 03:11:49 +0200 Subject: [PATCH 44/49] doc: Code optimization. --- doc/code/optimization.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 doc/code/optimization.md diff --git a/doc/code/optimization.md b/doc/code/optimization.md new file mode 100644 index 0000000000..1317cc34ed --- /dev/null +++ b/doc/code/optimization.md @@ -0,0 +1,40 @@ +# Code Optimization + +openage should be efficient in its resource usage and offer high performance. +As we are building a game engine, it is kind of expected that everything runs +smoothly out-of-the-box. + +1. [Basic Rules](#basic-rules) +2. [Checking Performance](#checking-performance) + + +## Basic Rules + +Before anyone dives into the codebase and starts changing things around for better performance, +they should consider these simple rules: + +- **readability comes first**: openage depends on outsiders being able to understand the engine code. If the code is super fast but totally incomprehensible, then nobody is going to maintain it. +- **avoid premature optimization**: optimization without reason wastes time, reduces readability, and can sometimes lead to even worse performance. It's often better to implement a workable solution first and then use profiling to identify bottlenecks. Modern compilers are often smart enough to find small improvements during compilation anyway. +- **prefer architecture improvements over speeding up specific use cases**: openage is a game *engine*, so we don't exactly know what people will use it for. We want the general case to be fast, not the edge cases. + + +## Checking Performance + +Performance bottlenecks are best identified with a *profiler* of your choice. For Python, +the built-in modules [cProfile](https://docs.python.org/3/library/profile.html) (speed) +and [tracemalloc](https://docs.python.org/3/library/tracemalloc.html) (memory) are recommended. +openage also provides convenience functions for them in its `openage.util.profiler` module. +For C++, [valgrind](https://valgrind.org/info/tools.html) with `callgrind` (speed) and +`memcheck` (memory) can be used. Any other profiler will also do the trick as long as +you know how to use it. + +Profiling a full engine run can be very resource intensive to profile. Therefore, it +can be beneficial to profile engine [demos](/doc/code/testing.md#demos) for a specific +subsystem, e.g. the renderer or the event system. This is also useful to +check the performance of subsystems in isolation. However, consider that a full engine +run may have different bottlenecks that are not obvious in the demos. + +Stresstests are demos that are specifically designed to test performance, particularly +in the renderer subsystem. They can also include benchmarks with additional +information, e.g. an FPS counter. When profiling performance critical code, implementing +a stresstest should be considered to complement profiling results. From ada2157018da2d8b763f7bb4bec394a61d0a1777 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 4 Sep 2023 19:08:53 +0200 Subject: [PATCH 45/49] doc: Bring AI ideas up-to-date. --- doc/ideas/ai.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/ideas/ai.md b/doc/ideas/ai.md index fab47ddc86..b7471a5a82 100644 --- a/doc/ideas/ai.md +++ b/doc/ideas/ai.md @@ -14,6 +14,7 @@ This file shall provide information about the AI interface design. * The C++ engine triggers python AI functions when desired events occur * The AI then can trigger actions on the controllable units * Periodically (1 Hz?), the AI gets ticked to allow event independent decisions + * Get the AI its own dedicated thread ### Event types @@ -56,12 +57,12 @@ struct game_info { ```cpp class Game { Player[] allies(); // all the ally players that have not left or been defeated. - bool canBuildHere(Unit builder, TilePosition position, UnitType type); - bool canMake(Unit builder, UnitType type); - bool canResearch(Unit unit, TechType type); - bool canUpgrade(Unit unit, UpgradeType type); + bool can_build_at(Unit builder, TilePosition position, UnitType type); + bool can_create(Unit builder, UnitType type); + bool can_research(Unit unit, TechType type); + bool can_upgrade(Unit unit, UpgradeType type); Player[] enemies(); // all the enemy players that have not left or been defeated. - Unit[] getAllUnits(); // returns all the visible units. + Unit[] get_all_units(); // returns all the visible units. // many more examples at: http://bwmirror.jurenka.sk/javadoc/bwapi/Game.html } @@ -69,11 +70,11 @@ class Game { ```cpp class Player { - string getName(); // returns the name of the player. - Race getRace(); // returns the race of the player. - Unit[] getUnits(); // returns the set of units the player own. - bool hasResearched(TechType tech); - int getWood() // Returns the amount of wood the player owns. + string get_name(); // returns the name of the player. + Faction get_faction(); // returns the faction of the player. + Unit[] get_units(); // returns the set of units the player own. + bool has_researched(TechType tech); + int get_resource() // Returns the amount of resources the player owns. // many more... } ``` From 84bfc245dff66d8de8619a18586c508d46caa22b Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 4 Sep 2023 19:13:22 +0200 Subject: [PATCH 46/49] engine: Add pre-processor error for llvm jthread. --- libopenage/engine/engine.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libopenage/engine/engine.h b/libopenage/engine/engine.h index d3afe6554d..d01fe9c4c2 100644 --- a/libopenage/engine/engine.h +++ b/libopenage/engine/engine.h @@ -25,6 +25,8 @@ class jthread : public thread { } }; } // namespace std +#else +#error "jthread is supported now, remove custom definition" #endif #else #include From 68740258abb2d834b9bfc3e192770f3676f78528 Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 4 Sep 2023 19:28:20 +0200 Subject: [PATCH 47/49] doc: Add a list of alternative profilers. --- doc/code/optimization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/code/optimization.md b/doc/code/optimization.md index 1317cc34ed..5f5f32a072 100644 --- a/doc/code/optimization.md +++ b/doc/code/optimization.md @@ -26,7 +26,7 @@ and [tracemalloc](https://docs.python.org/3/library/tracemalloc.html) (memory) a openage also provides convenience functions for them in its `openage.util.profiler` module. For C++, [valgrind](https://valgrind.org/info/tools.html) with `callgrind` (speed) and `memcheck` (memory) can be used. Any other profiler will also do the trick as long as -you know how to use it. +you know how to use it (e.g. `perf`, `VTune`, `uProf`, etc.). Profiling a full engine run can be very resource intensive to profile. Therefore, it can be beneficial to profile engine [demos](/doc/code/testing.md#demos) for a specific From 90c7fa0e75a19c90a401a30fd9860f74f2dae5af Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 4 Sep 2023 19:37:25 +0200 Subject: [PATCH 48/49] doc: Mention valgrind suppression file. --- doc/code/optimization.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/doc/code/optimization.md b/doc/code/optimization.md index 5f5f32a072..36b3f03a22 100644 --- a/doc/code/optimization.md +++ b/doc/code/optimization.md @@ -20,13 +20,19 @@ they should consider these simple rules: ## Checking Performance -Performance bottlenecks are best identified with a *profiler* of your choice. For Python, -the built-in modules [cProfile](https://docs.python.org/3/library/profile.html) (speed) -and [tracemalloc](https://docs.python.org/3/library/tracemalloc.html) (memory) are recommended. +Performance bottlenecks are best identified with a *profiler* of your choice. + +For **Python**, the built-in modules [cProfile](https://docs.python.org/3/library/profile.html) +(speed) and [tracemalloc](https://docs.python.org/3/library/tracemalloc.html) (memory) are recommended. openage also provides convenience functions for them in its `openage.util.profiler` module. -For C++, [valgrind](https://valgrind.org/info/tools.html) with `callgrind` (speed) and -`memcheck` (memory) can be used. Any other profiler will also do the trick as long as -you know how to use it (e.g. `perf`, `VTune`, `uProf`, etc.). + +For **C++**, [valgrind](https://valgrind.org/info/tools.html) with `callgrind` (speed) and +`memcheck` (memory) can be used. If you use valgrind, you should supply it the suppression +file in our repo: [`etc/valgrind-python.supp`](/etc/valgrind-python.supp). This lets +valgrind handle the Python parts of the engine a bit better. + +Any other profiler will also do the trick as long as you know how to use it (e.g. `perf`, +`VTune`, `uProf`, etc.). Profiling a full engine run can be very resource intensive to profile. Therefore, it can be beneficial to profile engine [demos](/doc/code/testing.md#demos) for a specific From 16f5f9a2145955fcfaf0c4856e18aeb59aed0e3b Mon Sep 17 00:00:00 2001 From: heinezen Date: Mon, 4 Sep 2023 19:51:04 +0200 Subject: [PATCH 49/49] doc: Clarify event targeting. --- doc/code/event_system.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/code/event_system.md b/doc/code/event_system.md index 9dcb327144..762583df3f 100644 --- a/doc/code/event_system.md +++ b/doc/code/event_system.md @@ -74,7 +74,7 @@ When calling `create_event(..)` on the event loop, an `Event` instance is return allows access to the configuration parameters of the event. Event instances store references to: -* **Event target**: Entity targeted by the event. Implemented as a weak reference to let the target expire. +* **Event target**: Entity targeted by the event, i.e. the event entity that the event effects should be applied on. Implemented as a weak reference to let the target expire. * **Event handler**: Event handler object used for rescheduling and executing the event. * **Invoke time**: Determines when the event is executed. * **Time of last change**: Time of the last change to invocation time. Used for rescheduling the event. @@ -102,7 +102,7 @@ in a rescheduling of the event in the next iteration of the event loop. ## Canceling Events -Events are automatically canceled if their target has expired, i.e. it has been deleted +Events are automatically canceled if their targeted event entity has expired, i.e. it has been deleted from memory. To accomplish this target references are implemented using `std::weak_ptr` which are checked for expiration before event execution.