diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..14453e3 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,27 @@ +Checks: >- + -*, + readability-identifier-naming, + readability-redundant-string-cstr, + readability-redundant-string-init, + readability-simplify-boolean-expr, + performance-unnecessary-value-param, + performance-unnecessary-copy-initialization, + performance-for-range-copy, + performance-implicit-conversion-in-loop, +CheckOptions: +- key: readability-identifier-naming.PrivateMemberPrefix + value: 'm_' +- key: readability-identifier-naming.ClassConstantCase + value: aNy_CasE +# an empty *Prefix needs a *Case to work +- key: readability-identifier-naming.ClassConstantPrefix + value: '' +#- key: readability-identifier-naming.PrivateMemberCase +# value: CamelCase +#- key: readability-identifier-naming.FunctionCase +# value: CamelCase +#- key: readability-identifier-naming.EnumCase +# value: camelBack + +WarningsAsErrors: '*' +FormatStyle: 'file' \ No newline at end of file diff --git a/.gitignore b/.gitignore index fb11540..4129f34 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,8 @@ build/ .DS_Store -.vscode \ No newline at end of file +.vscode + +imgui.ini + +test/tdmap.tdmap3 \ No newline at end of file diff --git a/include/td/Maths.h b/include/td/Maths.h new file mode 100644 index 0000000..023ea6d --- /dev/null +++ b/include/td/Maths.h @@ -0,0 +1,188 @@ +#pragma once + +#include + +namespace td { + +static constexpr float PI = 3.141592653f; + +template +struct Vec2 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + constexpr Vec2(T X = T(0), T Y = T(0)) : x(X), y(Y) {} +}; + +template +inline bool operator==(const Vec2& vec2, const Vec2& other) { + return vec2.x == other.x && vec2.y == other.y; +} + +template +struct Vec3 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + union { + T z; + T b; + }; + + constexpr Vec3(T X = 0, T Y = 0, T Z = 0) : x(X), y(Y), z(Z) {} +}; + +template +inline bool operator==(const Vec3& vec3, const Vec3& other) { + return vec3.x == other.x && vec3.y == other.y && vec3.z == other.z; +} + +template +struct Vec4 { + union { + T x; + T r; + }; + + union { + T y; + T g; + }; + + union { + T z; + T b; + }; + + union { + T w; + T a; + }; + + constexpr Vec4(Vec3 vec, T W = 1) : x(vec.x), y(vec.y), z(vec.z), w(W) {} + constexpr Vec4(T X = 0, T Y = 0, T Z = 0, T W = 0) : x(X), y(Y), z(Z), w(W) {} +}; + +template +inline bool operator==(const Vec4& vec4, const Vec4& other) { + return vec4.x == other.x && vec4.y == other.y && vec4.z == other.z && vec4.w = other.w; +} + +using Vec2i = Vec2; +using Vec2u = Vec2; +using Vec2f = Vec2; +using Vec2d = Vec2; + +using Vec3i = Vec3; +using Vec3u = Vec3; +using Vec3f = Vec3; +using Vec3d = Vec3; + +using Vec4i = Vec4; +using Vec4u = Vec4; +using Vec4f = Vec4; +using Vec4d = Vec4; + +using Color = Vec3; + +template +struct Mat4 { + static const std::size_t MATRIX_SIZE = 4; + + T x0, x1, x2, x3; + T y0, y1, y2, y3; + T z0, z1, z2, z3; + T w0, w1, w2, w3; + + T operator[](std::size_t offset) const { + return reinterpret_cast(this)[offset]; + } + + T& operator[](std::size_t offset) { + return reinterpret_cast(this)[offset]; + } + + T* data() { + return reinterpret_cast(this); + } + + const T* data() const { + return reinterpret_cast(this); + } + + T at(std::size_t row, std::size_t column) const { + return operator[](row * MATRIX_SIZE + column); + } + + T& at(std::size_t row, std::size_t column) { + return operator[](row * MATRIX_SIZE + column); + } +}; + +typedef Mat4 Mat4f; +typedef Mat4 Mat4i; +typedef Mat4 Mat4d; + +template +inline bool operator==(const Mat4& mat, const Mat4& other) { + return mat.x0 == other.x0 && mat.y0 == other.y0 && mat.z0 == other.z0 && mat.w0 == other.w0 && mat.x1 == other.x1 && + mat.y1 == other.y1 && mat.z1 == other.z1 && mat.w1 == other.w1 && mat.x2 == other.x2 && mat.y2 == other.y2 && + mat.z2 == other.z2 && mat.w2 == other.w2 && mat.x3 == other.x3 && mat.y3 == other.y3 && mat.z3 == other.z3 && + mat.w3 == other.w3; +} + +namespace maths { + +template +Mat4 Transpose(const Mat4& mat) { + Mat4 result; + + result.x1 = mat.y0; + result.x2 = mat.z0; + result.x3 = mat.w0; + result.y0 = mat.x1; + result.y2 = mat.z1; + result.y3 = mat.w1; + result.z0 = mat.x2; + result.z1 = mat.y2; + result.z3 = mat.w2; + result.w0 = mat.x3; + result.w1 = mat.y3; + result.w2 = mat.z3; + result.x0 = mat.x0; + result.y1 = mat.y1; + result.z2 = mat.z2; + result.w3 = mat.w3; + + return result; +} + +Mat4f Perspective(float fovY, float aspectRatio, float zNear, float zFar); +Mat4f Look(const Vec3f& eye, const Vec3f& center, const Vec3f& up); + +Mat4f Inverse(const Mat4f& mat); + +template +T Lerp(T v0, T v1, T t) { + return (T(1) - t) * v0 + t * v1; +} + +} // namespace maths + + + +} // namespace td diff --git a/include/td/Types.h b/include/td/Types.h index 0bc9948..cafc172 100644 --- a/include/td/Types.h +++ b/include/td/Types.h @@ -2,13 +2,19 @@ #include #include +#include + +namespace sp { +class DataBuffer; +} // namespace sp namespace td { using FpFloat = fpm::fixed_16_16; -enum class Team : std::uint8_t { - Blue = 0, +enum class TeamColor : std::int8_t { + None = -1, + Blue, Red, }; @@ -30,7 +36,7 @@ enum class EntityType : std::uint8_t { Zombie = 0, Spider, Pigman, - Skeleton, + Skelon, Creeper, Silverfish, Blaze, @@ -60,12 +66,22 @@ struct TowerCoords { std::int16_t y; }; -struct EntityCoords { - FpFloat x; - FpFloat y; -}; +using EntityCoords = Vec2; using PeerID = std::uint16_t; using StepsType = std::uint16_t; +enum class Direction : std::uint8_t { + PositiveX = 1 << 0, + NegativeX = 1 << 1, + PositiveY = 1 << 2, + NegativeY = 1 << 3, +}; + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coords); +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float); + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, EntityCoords& a_Coords); +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, FpFloat& a_Float); + } // namespace td diff --git a/include/td/common/Array.h b/include/td/common/Array.h new file mode 100644 index 0000000..87a2b88 --- /dev/null +++ b/include/td/common/Array.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +namespace td { + +/** + * Reflectable std::array + */ +template +class Array { + private: + T* m_Data; + + public: + Array() : m_Data(new T[S]) {} + + Array(const Array& a_Other) : Array() { + *this = a_Other; + } + + Array(Array&& a_Other) { + m_Data = a_Other.m_Data; + a_Other.m_Data = nullptr; + } + + Array(const std::initializer_list& args) { + std::size_t i = 0; + for(const T& element : args) { + m_Data[i] = element; + i++; + } + } + + Array& operator=(const Array& a_Other) { + for (std::size_t i = 0; i < S; i++) { + m_Data[i] = a_Other.m_Data[i]; + } + return *this; + } + + Array& operator=(Array&& a_Other) { + std::swap(m_Data, a_Other.m_Data); + return *this; + } + + T& operator[](std::size_t a_Index) { + return m_Data[a_Index]; + } + + const T& operator[](std::size_t a_Index) const { + return m_Data[a_Index]; + } + + ~Array() { + delete [] m_Data; + } +}; + +} // namespace td diff --git a/include/td/common/DataBuffer.h b/include/td/common/DataBuffer.h deleted file mode 100644 index 2cc9eff..0000000 --- a/include/td/common/DataBuffer.h +++ /dev/null @@ -1,300 +0,0 @@ -#pragma once - -/** - * \file DataBuffer.h - * \brief File containing the td::DataBuffer class - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace td { - -/** - * \class DataBuffer - * \brief Class used to manipulate memory - */ -class DataBuffer { - private: - typedef std::vector Data; - Data m_Buffer; - std::size_t m_ReadOffset; - - public: - typedef Data::iterator iterator; - typedef Data::const_iterator const_iterator; - typedef Data::reference reference; - typedef Data::const_reference const_reference; - typedef Data::difference_type difference_type; - - DataBuffer(); - DataBuffer(const DataBuffer& other); - DataBuffer(const DataBuffer& other, difference_type offset); - DataBuffer(DataBuffer&& other); - DataBuffer(const std::string& str); - - DataBuffer& operator=(const DataBuffer& other); - DataBuffer& operator=(DataBuffer&& other); - - /** - * \brief Append data to the buffer - */ - template - void Append(const T& data) { - std::size_t size = sizeof(data); - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + size); - std::memcpy(&m_Buffer[end_pos], &data, size); - } - - /** - * \brief Append data to the buffer - */ - template - DataBuffer& operator<<(const T& data) { - Append(data); - return *this; - } - - /** - * \brief Append a string to the buffer - * \warning Don't use it for binary data ! - * \param str The string to append - */ - DataBuffer& operator<<(const std::string& str); - - /** - * \brief Append data to the buffer from another const buffer - * \param data The buffer to append - */ - DataBuffer& operator<<(const DataBuffer& data); - - /** - * \brief Append a vector to the buffer by first writing the size - * \param data The vector to append - */ - template - DataBuffer& operator<<(const std::vector& data) { - *this << VarInt{data.size()}; - for (const auto& element : data) { - *this << element; - } - return *this; - } - - /** - * \brief Append an array to the buffer by first writing the size - * \param data The buffer to append - */ - template - DataBuffer& operator<<(const std::array& data) { - for (const auto& element : data) { - *this << element; - } - return *this; - } - - /** - * \brief Read some data from the buffer and assign to desired variable - */ - template - DataBuffer& operator>>(T& data) { - assert(m_ReadOffset + sizeof(T) <= GetSize()); - data = *(reinterpret_cast(&m_Buffer[m_ReadOffset])); - m_ReadOffset += sizeof(T); - return *this; - } - - /** - * \brief Read some data from the buffer and assign to the new buffer - * \param data The buffer to assign - */ - DataBuffer& operator>>(DataBuffer& data); - - /** - * \brief Read a string from the buffer - * \param str The string to assign in the new buffer - * \warning Don't use it for binary data ! - */ - DataBuffer& operator>>(std::string& str); - - /** - * \brief Read a vector (size + data) from the buffer - * \pre The vector is assumed to be empty - */ - template - DataBuffer& operator>>(std::vector& data) { - VarInt arraySize; - *this >> arraySize; - for (std::size_t i = 0; i < arraySize.GetValue(); i++) { - T newElement; - *this >> newElement; - data.push_back(newElement); - } - return *this; - } - - /** - * \brief Read an array from the buffer - */ - template - DataBuffer& operator>>(std::array& data) { - for (std::size_t i = 0; i < Size; i++) { - T newElement; - *this >> newElement; - data[i] = newElement; - } - return *this; - } - - /** - * \brief Write some data to the buffer - * \param buffer The buffer to write - * \param amount The amount of data to write - */ - void WriteSome(const char* buffer, std::size_t amount); - - /** - * \brief Write some data to the buffer - * \param buffer The buffer to write - * \param amount The amount of data to write - */ - void WriteSome(const std::uint8_t* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(char* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(std::uint8_t* buffer, std::size_t amount); - - /** - * \brief Read some data from the buffer - * \param buffer The buffer to Read - * \param amount The amount of data from the buffer - */ - void ReadSome(DataBuffer& buffer, std::size_t amount); - - /** - * \brief Resize the buffer - * \param size The new size of the buffer - */ - void Resize(std::size_t size) { - m_Buffer.resize(size); - } - - /** - * \brief Reserve some space in the buffer - * \param amount The amount of space to reserve - */ - void Reserve(std::size_t amount) { - m_Buffer.reserve(amount); - } - - - /** - * \brief Clear the buffer - */ - void Clear() { - m_Buffer.clear(); - m_ReadOffset = 0; - } - - /** - * \brief When the buffer has been read entirely - */ - bool IsFinished() const { - return m_ReadOffset >= m_Buffer.size(); - } - - /** - * \brief Get the buffer data - */ - std::uint8_t* data() { - return m_Buffer.data(); - } - - /** - * \brief Get the buffer data - */ - const std::uint8_t* data() const { - return m_Buffer.data(); - } - - /** - * \brief Get the read offset - */ - std::size_t GetReadOffset() const { - return m_ReadOffset; - } - - /** - * \brief Set the read offset - * \param pos The new read offset - */ - void SetReadOffset(std::size_t pos); - - /** - * \brief Get the size of the buffer - */ - std::size_t GetSize() const; - - /** - * \brief Get the remaining size of the buffer - */ - std::size_t GetRemaining() const; - - /** - * \brief Read a file into the buffer - * \param fileName The name of the file to read - */ - bool ReadFile(const std::string& fileName); - - /** - * \brief Write a file into the buffer - * \param fileName The name of the file to write to - */ - bool WriteFile(const std::string& fileName) const; - - /** - * \brief Allocate the buffer on the heap - * \warning Don't forget to free the data ! - */ - std::uint8_t* HeapAllocatedData() const { - std::uint8_t* newBuffer = new std::uint8_t[GetSize()]; - std::memcpy(newBuffer, data(), GetSize()); - return newBuffer; - } - - /** - * \brief Operator == to compare two DataBuffer - */ - bool operator==(const DataBuffer& other) const { - return m_Buffer == other.m_Buffer; - } - - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; -}; - -/** - * \brief Operator << to write a DataBuffer to an ostream - */ -std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer); - -} // namespace td diff --git a/include/td/common/VarInt.h b/include/td/common/VarInt.h deleted file mode 100644 index 12ac986..0000000 --- a/include/td/common/VarInt.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -/** - * \file VarInt.h - * \brief File containing the td::VarInt class - */ - -#include -#include - -namespace td { - -class DataBuffer; - -/** - * \class VarInt - * \brief Variable-length format such that smaller numbers use fewer bytes. - */ -class VarInt { - private: - std::uint64_t m_Value; - - public: - VarInt() : m_Value(0) {} - /** - * \brief Construct a variable integer from a value - * \param value The value of the variable integer - */ - VarInt(std::uint64_t value) : m_Value(value) {} - - /** - * \brief Get the value of the variable integer - */ - std::uint64_t GetValue() const { - return m_Value; - } - - /** - * \brief Get the length of the serialized variable integer - */ - std::size_t GetSerializedLength() const; - - /** - * \brief Serialize the variable integer - * \param out The buffer to write the serialized variable integer to - * \param var The variable integer to serialize - */ - friend DataBuffer& operator<<(DataBuffer& out, const VarInt& var); - - /** - * \brief Deserialize the variable integer - * \param in The buffer to read the serialized variable integer from - * \param var The variable integer to deserialize - */ - friend DataBuffer& operator>>(DataBuffer& in, VarInt& var); -}; - -} // namespace td diff --git a/include/td/game/Game.h b/include/td/game/Game.h new file mode 100644 index 0000000..dd5bac9 --- /dev/null +++ b/include/td/game/Game.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace td { +namespace game { + +enum class GameState : std::uint8_t { + Lobby, + Game, + EndGame, + Disconnected, + Closed +}; + +typedef std::map PlayerList; + + +class Game { +protected: + World* m_World; + + GameState m_GameState = GameState::Lobby; + PlayerList m_Players; +public: + Game(World* world); + virtual ~Game(); + + virtual void Tick(std::uint64_t delta); + + GameState GetGameState() const { return m_GameState; } + void SetGameState(GameState gameState) { m_GameState = gameState; }; + + const World* GetWorld() const { return m_World; } + World* GetWorld() { return m_World; } + + const PlayerList& GetPlayers() const { return m_Players; } + PlayerList& GetPlayers() { return m_Players; } + + const Player* GetPlayerById(PlayerID id) const; + Player* GetPlayerById(PlayerID id); + +}; + +} // namespace game +} // namespace td diff --git a/include/td/game/Mobs.h b/include/td/game/Mobs.h new file mode 100644 index 0000000..ace8d0d --- /dev/null +++ b/include/td/game/Mobs.h @@ -0,0 +1,132 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace td { +using Vec2fp = Vec2; + +namespace game { + +enum class EffectType : std::uint8_t { + Slowness = 0, + Stun, + Fire, + Poison, + Heal, +}; + +enum class MobType : std::uint8_t { + Zombie = 0, + Spider, + Skeleton, + Pigman, + Creeper, + Silverfish, + Blaze, + Witch, + Slime, + Giant, + + MOB_COUNT +}; + +typedef std::uint32_t MobID; +typedef std::uint8_t MobLevel; +typedef std::vector TowerImmunities; +typedef std::vector EffectImmunities; + +struct MobStats { + float m_Damage; + float m_Speed; + Vec2f m_Size; + std::uint16_t m_MoneyCost; + std::uint16_t m_ExpCost; + std::uint16_t m_MaxLife; + std::uint16_t m_ExpReward; +}; + +struct EffectDuration { + EffectType type; + float duration; // in seconds + Tower* tower; // the tower that gived the effect +}; + +const MobStats* GetMobStats(MobType type, std::uint8_t level); +const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level); +const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level); + +class MobHandler; + +struct MobData {}; + +class Mob : public sp::MessageBase { + public: + MobID m_ID; + MobLevel m_Level; + PlayerID m_Sender; + float m_Health; + Vec2fp m_Position; + Direction m_Direction; + std::vector m_Effects; + const Tower* m_LastDamage; // the last tower that damaged the mob + float m_HitCooldown; + TeamCastle* m_CastleTarget; + // utils::CooldownTimer m_AttackTimer; + + MobPtr m_Next; + + Mob() {} + + Mob& operator=(const Mob& a_Other) = default; +}; + +template +using ConcreteMob = sp::ConcreteMessage; + +using Zombie = ConcreteMob; +using Spider = ConcreteMob; +using Skeleton = ConcreteMob; +using PigMan = ConcreteMob; +using Creeper = ConcreteMob; +using Silverfish = ConcreteMob; +using Blaze = ConcreteMob; +using Witch = ConcreteMob; +using Slime = ConcreteMob; +using Giant = ConcreteMob; + +using AllMobs = std::tuple; + +class MobHandler : public sp::GenericHandler {}; + +using MobFactory = sp::MessageFactory; + +class MobListener { + public: + virtual void OnMobSpawn(Mob* mob) { + MobHandler h; + } + virtual void OnMobDie(Mob* mob) {} + + virtual void OnMobDamage(Mob* target, float damage, Tower* damager) {} + + virtual void OnMobTouchCastle(Mob* damager, TeamCastle* enemyCastle) {} + virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) {} +}; + +using MobList = std::vector; + +// typedef utils::ObjectNotifier MobNotifier; + +} // namespace game +} // namespace td diff --git a/include/td/game/Team.h b/include/td/game/Team.h new file mode 100644 index 0000000..031b144 --- /dev/null +++ b/include/td/game/Team.h @@ -0,0 +1,102 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +namespace td { +namespace game { + +class Player; + +class Spawn : public utils::shape::Rectangle { +private: + Direction m_Direction; +public: + Spawn() : m_Direction(Direction::PositiveX) { + SetWidth(5); + SetHeight(5); + } + + Direction GetDirection() const { return m_Direction; } + + void SetDirection(Direction direction) { m_Direction = direction; } +}; + +class Team; + +class TeamCastle : public utils::shape::Rectangle { +private: + float m_Life; +public: + static constexpr int CastleMaxLife = 1000; + + TeamCastle() : m_Life(CastleMaxLife) { + SetWidth(5); + SetHeight(5); + } + + float GetLife() const { return m_Life; } + + void SetLife(float life) { m_Life = life; } + void Damage(float damage) { m_Life = std::max(0.0f, m_Life - damage); } + + void SetShape(utils::shape::Rectangle rect) { + SetCenter(rect.GetCenter()); + SetSize(rect.GetSize()); + } +}; + +class Team { +private: + std::vector m_Players; + TeamColor m_Color; + Spawn m_Spawn; + TeamCastle m_TeamCastle; +public: + Team(TeamColor color) : m_Color(color) {} + + void AddPlayer(Player* newPlayer); + void RemovePlayer(const Player* player); + + TeamColor GetColor() const; + + const Spawn& GetSpawn() const { return m_Spawn; } + Spawn& GetSpawn() { return m_Spawn; } + + const TeamCastle& GetCastle() const { return m_TeamCastle; } + TeamCastle& GetCastle() { return m_TeamCastle; } + + std::uint8_t GetPlayerCount() const; +}; + +struct TeamList { + std::array m_Teams; + + TeamList() : m_Teams{Team{TeamColor::Red}, Team{TeamColor::Blue}}{ + + } + + Team& operator[](std::size_t a_Index) { + return m_Teams[a_Index]; + } + + Team& operator[](TeamColor a_Index) { + return m_Teams[static_cast(a_Index)]; + } + + const Team& operator[](std::size_t a_Index) const { + return m_Teams[a_Index]; + } + + const Team& operator[](TeamColor a_Index) const { + return m_Teams[static_cast(a_Index)]; + } +}; + +} // namespace game +} // namespace td diff --git a/include/td/game/Towers.h b/include/td/game/Towers.h new file mode 100644 index 0000000..574c1d2 --- /dev/null +++ b/include/td/game/Towers.h @@ -0,0 +1,147 @@ +#pragma once + +#include +#include + +#include + +#include + +#include +#include +#include + +namespace td { +namespace game { + +class World; +class Mob; + +typedef std::shared_ptr MobPtr; + +enum class TowerSize : std::uint8_t { + Little = 3, // 3x3 + Big = 5, // 5x5 +}; + +enum class TowerPath : std::uint8_t { + Top = 0, + Base, // Base Path + Bottom +}; + +class TowerStats { + private: + float m_Rate; + float m_Damage; + std::uint8_t m_Range; + + public: + TowerStats(float rate, float damage, std::uint8_t range) : m_Rate(rate), m_Damage(damage), m_Range(range) {} + + float GetDamageRate() const { + return m_Rate; + } + float GetDamage() const { + return m_Damage; + } + std::uint8_t GetRange() const { + return m_Range; + } +}; + +class TowerLevel { + private: + // 1, 2, 3, 4 + std::uint8_t m_Level : 3; + // 0 : base path 1 : top path (if there is bottom path) 2 : bottom path (if there is top path) + TowerPath m_Path : 2; + + public: + TowerLevel() : m_Level(1), m_Path(TowerPath::Base) {} + TowerLevel(std::uint8_t level, TowerPath path) : m_Level(level), m_Path(path) {} + + std::uint8_t GetLevel() const { + return m_Level; + } + TowerPath GetPath() const { + return m_Path; + } + + void SetLevel(std::uint8_t level) { + m_Level = level; + } + void SetPath(TowerPath path) { + m_Path = path; + } + + // operator to sort maps + friend bool operator<(const TowerLevel& level, const TowerLevel& other) { + return level.GetLevel() + static_cast(level.GetPath()) * 4 < + other.GetLevel() + static_cast(other.GetPath()) * 4; + } +}; + +using TowerID = std::uint16_t; + +class TowerHandler; + +class Tower : public utils::shape::Circle, public sp::MessageBase { + public: + TowerID m_ID; + TowerLevel m_Level{}; + PlayerID m_Builder; + + public: + Tower() : m_ID(0), m_Level({}), m_Builder(0) {} + + virtual TowerType GetType() const = 0; + virtual TowerSize GetSize() const = 0; + virtual void Tick(std::uint64_t delta, World* world) = 0; +}; + +struct TowerData {}; + +template +class ConcreteTower : public sp::ConcreteMessage { + public: + using HandlerType = typename sp::ConcreteMessage::HandlerType; + + virtual TowerSize GetSize() const override { + return Size; + } + + virtual TowerType GetType() const override { + return Type; + } + + virtual void Tick(std::uint64_t delta, World* world) override {} + + virtual void Dispatch(HandlerType& handler) const override { + handler.Handle(*this); + } +}; + +using TowerPtr = std::shared_ptr; + +using ArcherTower = ConcreteTower; +using ArtilleryTower = ConcreteTower; +using IceTower = ConcreteTower; +using LeachTower = ConcreteTower; +using MageTower = ConcreteTower; +using NecromancerTower = ConcreteTower; +using PoisonTower = ConcreteTower; +using QuakeTower = ConcreteTower; +using SorcererTower = ConcreteTower; +using TurretTower = ConcreteTower; +using ZeusTower = ConcreteTower; + +using AllTowers = std::tuple; + +using TowerFactory = sp::MessageFactory; + +class TowerHandler : public sp::GenericHandler {}; + +} // namespace game +} // namespace td diff --git a/include/td/game/World.h b/include/td/game/World.h index 41e6407..d941932 100644 --- a/include/td/game/World.h +++ b/include/td/game/World.h @@ -1,27 +1,137 @@ #pragma once -#include -#include -#include +#include +#include +#include namespace td { namespace game { class World { - private: - std::vector m_WorldStates; - GameHistory m_GameHistory; + protected: + TowerTileColorPalette m_TowerPlacePalette; + Color m_WalkablePalette; + std::vector m_DecorationPalette; + Color m_Background; - float m_OffsetTime; + ChunkList m_Chunks; + + SpawnColorPalette m_SpawnColorPalette; + + TilePalette m_TilePalette; + + std::shared_ptr m_CurrentState; + std::shared_ptr m_NextState; + + private: + sim::WorldTicker m_Ticker; public: World(); - void Tick(float a_Delta); + bool LoadMap(const protocol::pdata::WorldHeader& worldHeader); + bool LoadMap(const protocol::pdata::WorldData& worldData); + + bool LoadMapFromFile(const std::string& fileName); + bool SaveMap(const std::string& fileName) const; + + void SpawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir); + + TowerPtr PlaceTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder); + TowerPtr RemoveTower(TowerID id); + + TilePtr GetTile(std::int32_t x, std::int32_t y) const; + + const TowerTileColorPalette& GetTowerTileColorPalette() const { + return m_TowerPlacePalette; + } + const Color& GetWalkableTileColor() const { + return m_WalkablePalette; + } + const std::vector& GetDecorationPalette() const { + return m_DecorationPalette; + } + const Color& GetBackgroundColor() const { + return m_Background; + } + + const TilePalette& GetTilePalette() const { + return m_TilePalette; + } + + TilePtr GetTilePtr(TileIndex index) const { + if (index == 0) + return TilePtr(nullptr); + return m_TilePalette.at(index - 1); + } + + bool CanPlaceLittleTower(const Vec2f& worldPos, PlayerID player) const; + bool CanPlaceBigTower(const Vec2f& worldPos, PlayerID player) const; + + TowerPtr GetTower(const Vec2f& position) const; // returns null if no tower is here + + const ChunkList& GetChunks() const { + return m_Chunks; + } + + const Color& GetSpawnColor(TeamColor color) const { + return m_SpawnColorPalette[static_cast(color)]; + } + const SpawnColorPalette& GetSpawnColors() const { + return m_SpawnColorPalette; + } + + const MobList& GetMobList() const { + return m_CurrentState->m_Mobs; + } + MobList& GetMobList() { + return m_CurrentState->m_Mobs; + } + + const Color* GetTileColor(const TilePtr& tile) const; + + Team& GetRedTeam() { + return m_CurrentState->m_Teams[TeamColor::Red]; + } + const Team& GetRedTeam() const { + return m_CurrentState->m_Teams[TeamColor::Red]; + } + + Team& GetBlueTeam() { + return m_CurrentState->m_Teams[TeamColor::Blue]; + } + const Team& GetBlueTeam() const { + return m_CurrentState->m_Teams[TeamColor::Red]; + } + + Team& GetTeam(TeamColor team) { + return m_CurrentState->m_Teams[team]; + } + const Team& GetTeam(TeamColor team) const { + return m_CurrentState->m_Teams[team]; + } + + const TeamList& GetTeams() const { + return m_CurrentState->m_Teams; + } + + const TowerList& GetTowers() const { + return m_CurrentState->m_Towers; + } + + TowerPtr GetTowerById(TowerID tower); + + const Player* GetPlayerById(PlayerID id) const; + + const std::shared_ptr& Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta); + + void ResetSnapshots(std::shared_ptr& a_Current, std::shared_ptr& a_Next); private: - std::uint16_t GetCursorPos(); + void TickMobs(std::uint64_t delta); + void CleanDeadMobs(); + }; } // namespace game -} // namespace td +} // namespace td \ No newline at end of file diff --git a/include/td/game/WorldState.h b/include/td/game/WorldState.h deleted file mode 100644 index 6db7a4a..0000000 --- a/include/td/game/WorldState.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace td { -namespace game { - -class WorldState { - private: - // list of players, mobs, towers, castles - public: - WorldState() {} - - WorldState GetNextState(const protocol::LockStep& a_Step); - - private: - void Tick(float a_Delta); -}; - -} // namespace game -} // namespace td diff --git a/include/td/game/WorldTypes.h b/include/td/game/WorldTypes.h new file mode 100644 index 0000000..60023a6 --- /dev/null +++ b/include/td/game/WorldTypes.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include + +#include + +namespace td { +namespace game { + +using ChunkCoord = Vec2; + +class Game; + +enum class TileType : std::uint8_t { + None = 0, + Tower, + Walk, + Decoration, + /*Heal, + Lava, + Bedrock, + Freeze, + Ice,*/ +}; + +static constexpr Color BLACK{0, 0, 0}; +static constexpr Color WHITE{255, 255, 255}; + +static constexpr Color RED{255, 0, 0}; +static constexpr Color GREEN{0, 255, 0}; +static constexpr Color BLUE{0, 0, 255}; + +class TileHandler; + +using Tile = sp::MessageBase; + +template +using ConcreteTile = sp::ConcreteMessage; + +namespace data { +struct EmptyData {}; + +struct TowerTileData { + std::uint8_t m_ColorPaletteRef; + TeamColor m_TeamOwner; +}; + +struct WalkableTileData { + Direction m_Direction; +}; + +struct DecorationTileData { + std::uint16_t m_ColorPaletteRef; +}; +} // namespace data + +using EmptyTile = ConcreteTile; +using TowerTile = ConcreteTile; +using WalkableTile = ConcreteTile; +using DecorationTile = ConcreteTile; + +using AllTiles = std::tuple; + +using TileFactory = sp::MessageFactory; + +class TileHandler : public sp::GenericHandler {}; + +using TilePtr = sp::SerializableMessage; + +// typedef std::shared_ptr TilePtr; +typedef std::vector ChunkPalette; + +typedef std::shared_ptr WalkableTilePtr; + +typedef std::uint32_t TileIndex; + +// 32 x 32 area +struct Chunk { + enum { ChunkWidth = 32, ChunkHeight = 32, ChunkSize = ChunkWidth * ChunkHeight }; + using ChunkData = std::array; + using ChunkPackedData = std::vector; + + // stores index of tile palette + ChunkPalette m_Palette; + ChunkPackedData m_Data; + + TileIndex GetTileIndex(std::uint16_t tileNumber) const; +}; + +typedef std::shared_ptr ChunkPtr; + +typedef std::array TowerTileColorPalette; + +typedef std::vector TilePalette; + +typedef std::array SpawnColorPalette; + +typedef std::vector TowerList; + +using ChunkList = std::unordered_map; + +} // namespace game + +} // namespace td + + + +namespace std { +template <> +struct hash { + std::size_t operator()(const td::game::ChunkCoord& key) const noexcept { + return std::hash()(key.x << 16 | key.y); + } +}; +} // namespace std diff --git a/include/td/input/Display.h b/include/td/input/Display.h new file mode 100644 index 0000000..7d1bf87 --- /dev/null +++ b/include/td/input/Display.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include +#include +#include + +namespace td { + +class Display { + public: + utils::Signal OnAspectRatioChange; + utils::Signal OnKeyDown; + + Display(int a_Width, int a_Height, const std::string& a_Title); + ~Display(); + + void PollEvents(); + void Update(); + + bool IsCloseRequested() { + return m_ShouldClose; + } + + float GetAspectRatio() { + return m_AspectRatio; + } + + int GetWindowWidth() { + return m_LastWidth; + } + + int GetWindowHeight() { + return m_LastHeight; + } + + private: + SDL_Window* m_Window; + SDL_GLContext m_GLContext; + + int m_LastWidth, m_LastHeight; + float m_AspectRatio; + + bool m_ShouldClose; +}; + +} // namespace td \ No newline at end of file diff --git a/include/td/misc/Shapes.h b/include/td/misc/Shapes.h new file mode 100644 index 0000000..2367efb --- /dev/null +++ b/include/td/misc/Shapes.h @@ -0,0 +1,96 @@ +#pragma once + +#include + +namespace td { +namespace utils { +namespace shape { + +class Point { +private: + float m_X, m_Y; +public: + Point() : m_X(0), m_Y(0) {} + Point(float x, float y) : m_X(x), m_Y(y) {} + + float GetX() const { return m_X; } + float GetY() const { return m_Y; } + + void SetX(float x) { m_X = x; } + void SetY(float y) { m_Y = y; } + + float Distance(const Point& point) const; + float DistanceSquared(const Point& point) const; +}; + +class Circle; + +class Rectangle { +private: + Point m_Center; + float m_Width, m_Height; +public: + Rectangle() : m_Center(), m_Width(0), m_Height(0) {} + + const Point& GetCenter() const { return m_Center; } + float GetCenterX() const { return m_Center.GetX(); } + float GetCenterY() const { return m_Center.GetY(); } + + float GetWidth() const { return m_Width; } + float GetHeight() const { return m_Height; } + + Point GetTopLeft() const { return { m_Center.GetX() - (m_Width / 2.0f), m_Center.GetY() - (m_Height / 2.0f) }; } + Point GetBottomRight() const { return { m_Center.GetX() + (m_Width / 2.0f), m_Center.GetY() + (m_Height / 2.0f) }; } + + void SetCenter(const Point& center) { m_Center = center; } + void SetCenterX(float x) { m_Center.SetX(x); } + void SetCenterY(float y) { m_Center.SetY(y); } + + void SetSize(float width, float height) { SetWidth(width); SetHeight(height); } + void SetSize(Point size) { SetSize(size.GetX(), size.GetY()); } + Point GetSize() { return { m_Width, m_Height }; } + + void SetWidth(float width) { m_Width = width; } + void SetHeight(float height) { m_Height = height; } + + bool CollidesWith(const Point& point) const; + bool CollidesWith(const Rectangle& rect) const; + bool CollidesWith(const Circle& circle) const; + + // distance from the closest side of the rectangle + float Distance(const Circle& circle) const; + float DistanceSquared(const Circle& circle) const; +}; + +class Circle { +private: + Point m_Center; + float m_Radius; +public: + Circle(float x, float y, float radius) : m_Center(x, y), m_Radius(radius) {} + Circle() : m_Radius(0) {} + + const Point& GetCenter() const { return m_Center; } + float GetCenterX() const { return m_Center.GetX(); } + float GetCenterY() const { return m_Center.GetY(); } + + float GetRadius() const { return m_Radius; } + + void SetCenter(const Point& center) { m_Center = center; } + void SetCenterX(float x) { m_Center.SetX(x); } + void SetCenterY(float y) { m_Center.SetY(y); } + + void SetRadius(float radius) { m_Radius = radius; } + + bool CollidesWith(const Point& point) const; + bool CollidesWith(const Rectangle& rect) const; + bool CollidesWith(const Circle& circle) const; + + // distance from the closest side of the rectangle + float Distance(const Rectangle& rect) const; + float DistanceSquared(const Rectangle& rect) const; +}; + +} // namespace shape +} // namespace utils +} // namespace td diff --git a/include/td/misc/Signal.h b/include/td/misc/Signal.h new file mode 100644 index 0000000..b8dbf17 --- /dev/null +++ b/include/td/misc/Signal.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace utils { + +template +class Signal : private NonCopyable { + private: + using CallBack = std::function; + + std::vector m_Callbacks; + + public: + void Connect(CallBack&& a_Callback) { + m_Callbacks.push_back(std::move(a_Callback)); + } + + void operator()(Args... args) const { + for (const CallBack& callback : m_Callbacks) { + callback(args...); + } + } +}; + +} // namespace utils +} // namespace td diff --git a/include/td/protocol/Dispatcher.h b/include/td/protocol/Dispatcher.h deleted file mode 100644 index 037ce03..0000000 --- a/include/td/protocol/Dispatcher.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -/** - * \file PacketDispatcher.h - * \brief File containing the td::protocol::PacketDispatcher class - */ - -#include -#include - -#include - -namespace td { -namespace protocol { - -/** - * \class Dispatcher - * \brief Class used to dispatch things - */ -template -class Dispatcher : private NonCopyable { - private: - std::map> m_Handlers; - - public: - /** - * \brief Constructor - */ - Dispatcher() {} - - /** - * \brief Dispatch a packet - * \param packet The packet to dispatch - */ - void Dispatch(const T& packet); - - /** - * \brief Register a packet handler - * \param type The packet type - * \param handler The packet handler - */ - void RegisterHandler(T_Enum type, T_Handler& handler); - - /** - * \brief Unregister a packet handler - * \param type The packet type - * \param handler The packet handler - */ - void UnregisterHandler(T_Enum type, T_Handler& handler); - - /** - * \brief Unregister a packet handler - * \param handler The packet handler - */ - void UnregisterHandler(T_Handler& handler); -}; - -} // namespace protocol -} // namespace td - -#include "Dispatcher.inl" \ No newline at end of file diff --git a/include/td/protocol/Dispatcher.inl b/include/td/protocol/Dispatcher.inl deleted file mode 100644 index d807f9b..0000000 --- a/include/td/protocol/Dispatcher.inl +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -namespace td { -namespace protocol { - -template -void Dispatcher::Dispatch(const T& packet) { - T_Enum type = packet.GetType(); - for (auto* handler : m_Handlers[type]) - handler->Check(packet); -} - -template -void Dispatcher::RegisterHandler(T_Enum type, T_Handler& handler) { - auto found = std::find(m_Handlers[type].begin(), m_Handlers[type].end(), &handler); - if (found == m_Handlers[type].end()) - m_Handlers[type].push_back(&handler); -} - -template -void Dispatcher::UnregisterHandler(T_Enum type, T_Handler& handler) { - m_Handlers[type].erase(std::remove(m_Handlers[type].begin(), m_Handlers[type].end(), &handler), m_Handlers[type].end()); -} - -template -void Dispatcher::UnregisterHandler(T_Handler& handler) { - for (auto& pair : m_Handlers) { - if (pair.second.empty()) - continue; - - PacketType type = pair.first; - - m_Handlers[type].erase(std::remove(m_Handlers[type].begin(), m_Handlers[type].end(), &handler), m_Handlers[type].end()); - } -} - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/CommandData.h b/include/td/protocol/command/CommandData.h index f4e0aac..bf01602 100644 --- a/include/td/protocol/command/CommandData.h +++ b/include/td/protocol/command/CommandData.h @@ -3,40 +3,40 @@ #include #include #include +#include namespace td { namespace protocol { namespace cdata { - struct PlaceTower { - TowerType m_Type : 4; - PlayerID m_Placer : 4; + sp::BitField m_Type; + sp::BitField m_Placer; TowerCoords m_Position; }; struct UpgradeTower { - TowerID m_Tower : 12; - std::uint8_t m_Upgrade : 4; + sp::BitField m_Tower; + sp::BitField m_Upgrade; }; struct SpawnTroop { - EntityType m_Type : 5; - std::uint8_t m_Level : 3; + sp::BitField m_Type; + sp::BitField m_Level; EntityCoords m_Position; PlayerID m_Sender; }; struct UseItem { - ShopItem m_Item : 4; - PlayerID m_User : 4; + sp::BitField m_Item; + sp::BitField m_User; EntityCoords m_Position; }; struct TeamChange { - PlayerID m_Player : 7; - Team m_NewTeam : 1; + sp::BitField m_Player; + sp::BitField m_NewTeam; }; struct PlayerJoin { diff --git a/include/td/protocol/command/CommandDeclare.h b/include/td/protocol/command/CommandDeclare.h deleted file mode 100644 index 76013d7..0000000 --- a/include/td/protocol/command/CommandDeclare.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - - -namespace td { -namespace protocol { - -/** - * \def DeclareAllPacket - * \brief Avoids repetitive operations on commands - */ -#define DeclareAllCommand() \ - DeclareCommand(End) \ - DeclareCommand(PlaceTower) \ - DeclareCommand(PlayerJoin) \ - DeclareCommand(SpawnTroop) \ - DeclareCommand(TeamChange) \ - DeclareCommand(UpgradeTower) \ - DeclareCommand(UseItem) - - -} // namespace protocol -} // namespace td \ No newline at end of file diff --git a/include/td/protocol/command/CommandDispatcher.h b/include/td/protocol/command/CommandDispatcher.h deleted file mode 100644 index 0dc3ef9..0000000 --- a/include/td/protocol/command/CommandDispatcher.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -/** - * \file CommandDispatcher.h - * \brief File containing the td::protocol::CommandDispatcher class - */ - -#include -#include - -namespace td { -namespace protocol { - -using CommandDispatcher = Dispatcher; - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/CommandFactory.h b/include/td/protocol/command/CommandFactory.h deleted file mode 100644 index 3bae063..0000000 --- a/include/td/protocol/command/CommandFactory.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace protocol { -namespace CommandFactory { - -template ::value>::type> -std::shared_ptr CreateCommand() { - return std::make_shared(); -} - -const std::shared_ptr& CreateReadOnlyCommand(CommandType a_Type); - -} // namespace CommandFactory -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/CommandSerializer.h b/include/td/protocol/command/CommandSerializer.h deleted file mode 100644 index 181fcb3..0000000 --- a/include/td/protocol/command/CommandSerializer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace protocol { - -class Command; - -using CommandPtr = std::shared_ptr; - -namespace CommandSerializer { - -DataBuffer Serialize(const Command& a_Command); - -std::shared_ptr Deserialize(DataBuffer& a_Data); - -} // namespace CommandSerializer -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/CommandVisitor.h b/include/td/protocol/command/CommandVisitor.h deleted file mode 100644 index 987ffd3..0000000 --- a/include/td/protocol/command/CommandVisitor.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/** - * \file CommandVisitor.h - * \brief File containing the td::protocol::CommandVisitor class - */ - -#include - -namespace td { -namespace protocol { - -#define DeclareCommand(CommandName, ...) \ - /** This function is called when the packet processed by CommandVisitor::Check is a CommandName */ \ - virtual void Visit(const commands::CommandName&) {} - -/** - * \class CommandVisitor - * \brief This class uses double-dispatch in order to find the real type of a packet - */ -class CommandVisitor : private NonCopyable { - protected: - CommandVisitor() {} - virtual ~CommandVisitor() {} - - public: - /** - * \brief Calls the right CommandVisitor::Visit method corresponding to the real type of the packet - * \param packet the Command to visit - */ - void Check(const Command& packet); - - DeclareAllCommand() -}; - -#undef DeclareCommand - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/command/Commands.h b/include/td/protocol/command/Commands.h index 4444d21..b9fae92 100644 --- a/include/td/protocol/command/Commands.h +++ b/include/td/protocol/command/Commands.h @@ -5,107 +5,63 @@ * \brief File containing the definitions of the lockstep commands */ +#include +#include +#include +#include +#include #include #include #include -#include -#include + +#include namespace td { namespace protocol { -class CommandVisitor; - /** A Command id is 8 bits wide */ -using CommandID = std::uint8_t; - -#define DeclareCommand(CommandName, ...) /** CommandName */ CommandName, - -/** - * \enum CommandType - * \brief Map a Command to an id - */ -enum class CommandType : CommandID { - - DeclareAllCommand() - - /** The number of Commands */ - COMMAND_COUNT -}; - - -#undef DeclareCommand - - -class Command : private NonCopyable { - public: - /** - * \return The real type of the Command - */ - virtual CommandType GetType() const = 0; - - private: - /** Use a CommandVisitor to make double-dispatch possible */ - virtual void Accept(CommandVisitor& a_Visitor) const = 0; - - friend class CommandVisitor; +enum class CommandID : std::uint8_t { + End = 0, + PlaceTower, + PlayerJoin, + SpawnTroop, + TeamChange, + UpgradeTower, + UseItem, }; +class CommandHandler; +using CommandBase = sp::MessageBase; +template +using CommandMessage = sp::ConcreteMessage; namespace commands { -/** - * \class ConcreteCommand - * \brief A Command associated with an id and holding data - * \tparam PT The Command type - * \tparam Data The structure holding the data of the Command (in td::protocol::data namespace) - */ -template -class ConcreteCommand : public Command { - public: - /** The type of the struct holding the data */ - using CommandDataType = Data; - - /** The structure holding the actual data */ - CommandDataType m_Data; - - /** Construct the Command with data of type CommandDataType */ - ConcreteCommand(const CommandDataType& a_Data = {}); - - constexpr CommandType GetType() const override { - return CT; - }; - - private: - void Accept(CommandVisitor& a_Visitor) const override; -}; - - - - - -// define TD_INSTANCIATE_COMMANDS -// before including this file -// if you want to instantiate templates -#ifdef TD_INSTANCIATE_COMMANDS -#define DeclareCommand(CommandName, ...) \ - using CommandName = ConcreteCommand; \ - template class ConcreteCommand; -#else -#define DeclareCommand(CommandName, ...) /** Defines the CommandName Command */ \ - using CommandName = ConcreteCommand; -#endif - -DeclareAllCommand() - -#undef DeclareCommand +using EndCommand = CommandMessage; +using PlaceTowerCommand = CommandMessage; +using PlayerJoinCommand = CommandMessage; +using SpawnTroopCommand = CommandMessage; +using TeamChangeCommand = CommandMessage; +using UpgradeTowerCommand = CommandMessage; +using UseItemCommand = CommandMessage; } // namespace commands -using LockStep = std::vector>; +using AllCommands = std::tuple; + +class CommandHandler : public sp::GenericHandler {}; + +using CommandDispatcher = sp::MessageDispatcher; + +using CommandFactory = sp::MessageFactory; + +using CommandPtr = sp::SerializableMessage; + +using LockStep = std::vector; } // namespace protocol } // namespace td diff --git a/include/td/protocol/packet/PacketData.h b/include/td/protocol/packet/PacketData.h index fdcfb62..8bdcc39 100644 --- a/include/td/protocol/packet/PacketData.h +++ b/include/td/protocol/packet/PacketData.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include // Make it dynamic ? #define LOCKSTEP_BUFFER_SIZE 10 @@ -41,6 +43,11 @@ struct PlayerLeave { PlayerID m_PlayerId; }; +struct PredictCommand { + CommandPtr m_Command; + std::uint16_t m_FrameNumber; +}; + /** Keep alive */ struct KeepAlive { std::uint64_t m_KeepAliveId; @@ -59,7 +66,6 @@ struct ChatMessage { // TODO: handle players joining in the first second struct BeginGame { - MapData m_Map; std::vector m_BlueTeam; std::vector m_RedTeam; // optional, used for players joining mid game @@ -71,6 +77,34 @@ struct LockSteps { std::array m_LockSteps; }; +struct WorldHeader { + game::TowerTileColorPalette m_TowerPlacePalette; + Color m_WalkablePalette; + std::vector m_DecorationPalette; + Color m_Background; + + game::SpawnColorPalette m_SpawnColorPalette; + + game::TilePalette m_TilePalette; + + game::Spawn m_RedSpawn, m_BlueSpawn; + game::TeamCastle m_RedCastle, m_BlueCastle; +}; + +struct WorldData { + game::ChunkList m_Chunks; +}; + +struct SpawnTroop { + sp::BitField m_Type; + sp::BitField m_Level; +}; + +struct PlaceTower { + TowerType m_Type; + TowerCoords m_Position; +}; + } // namespace pdata } // namespace protocol } // namespace td diff --git a/include/td/protocol/packet/PacketDeclare.h b/include/td/protocol/packet/PacketDeclare.h deleted file mode 100644 index 1354d6d..0000000 --- a/include/td/protocol/packet/PacketDeclare.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -/** - * \file PacketDeclare.h - * \brief Holds the definitions of the packets (but not their content) - */ - -namespace td { -namespace protocol { - -/** - * \enum PacketSender - * \brief Indicate who should send a packet - */ -enum class PacketSenderType { - /** Sent by clients and server */ - Both = 1, - /** Sent by clients to the server */ - Client, - /** Sent by server to the clients */ - Server, -}; - -enum class PacketSendType { - Reliable = 1, - Unreliable, - UnreliableOrdered, -}; - - -/** - * \def DeclareAllPacket - * \brief Avoids repetitive operations on packets - */ -#define DeclareAllPacket() \ - DeclarePacket(ChatMessage, Reliable, Both) \ - DeclarePacket(BeginGame, Reliable, Server) \ - DeclarePacket(Disconnect, Reliable, Both) \ - DeclarePacket(KeepAlive, Reliable, Both) \ - DeclarePacket(LockSteps, Unreliable, Both) \ - DeclarePacket(LoggingSuccess, Reliable, Server) \ - DeclarePacket(PlayerJoin, Reliable, Server) \ - DeclarePacket(PlayerLeave, Reliable, Server) \ - DeclarePacket(PlayerLogin, Reliable, Client) \ - - -} // namespace protocol -} // namespace td \ No newline at end of file diff --git a/include/td/protocol/packet/PacketDispatcher.h b/include/td/protocol/packet/PacketDispatcher.h deleted file mode 100644 index d0a5d21..0000000 --- a/include/td/protocol/packet/PacketDispatcher.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -/** - * \file PacketDispatcher.h - * \brief File containing the td::protocol::PacketDispatcher class - */ - -#include -#include - -namespace td { -namespace protocol { - -using PacketDispatcher = Dispatcher; - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/packet/PacketFactory.h b/include/td/protocol/packet/PacketFactory.h deleted file mode 100644 index 0f346ad..0000000 --- a/include/td/protocol/packet/PacketFactory.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace protocol { -namespace PacketFactory { - -template ::value>::type> -std::unique_ptr CreatePacket() { - return std::make_unique(); -} - -const std::unique_ptr& CreateReadOnlyPacket(PacketType a_Type); - -} // namespace PacketFactory -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/packet/PacketSerialize.h b/include/td/protocol/packet/PacketSerialize.h new file mode 100644 index 0000000..29c2331 --- /dev/null +++ b/include/td/protocol/packet/PacketSerialize.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +namespace td { + +template +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Vec2& a_Vec) { + return a_Buffer << a_Vec.x << a_Vec.y; +} + +template +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Vec2& a_Vec) { + return a_Buffer >> a_Vec.x >> a_Vec.y; +} + +template +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Vec3& a_Vec) { + return a_Buffer << a_Vec.x << a_Vec.y << a_Vec.z; +} + +template +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Vec3& a_Vec) { + return a_Buffer >> a_Vec.x >> a_Vec.y >> a_Vec.z; +} + + +template +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Vec4& a_Vec) { + return a_Buffer << a_Vec.x << a_Vec.y << a_Vec.z << a_Vec.w; +} + +template +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Vec4& a_Vec) { + return a_Buffer >> a_Vec.x >> a_Vec.y >> a_Vec.z >> a_Vec.w; +} + +namespace game { + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const TeamCastle& a_Castle); +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, TeamCastle& a_Castle); + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Spawn& a_Spawn); +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Spawn& a_Spawn); + +} // namespace game +} // namespace td diff --git a/include/td/protocol/packet/PacketSerializer.h b/include/td/protocol/packet/PacketSerializer.h deleted file mode 100644 index fd71ca2..0000000 --- a/include/td/protocol/packet/PacketSerializer.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include - -namespace td { -namespace protocol { - -class Packet; - -using PacketPtr = std::unique_ptr; - -namespace PacketSerializer { - -DataBuffer Serialize(const Packet& a_Packet); - -std::unique_ptr Deserialize(DataBuffer& a_Data); - -} // namespace PacketSerializer -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/packet/PacketVisitor.h b/include/td/protocol/packet/PacketVisitor.h deleted file mode 100644 index d2a6799..0000000 --- a/include/td/protocol/packet/PacketVisitor.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/** - * \file PacketVisitor.h - * \brief File containing the td::protocol::PacketVisitor class - */ - -#include - -namespace td { -namespace protocol { - -#define DeclarePacket(PacketName, ...) \ - /** This function is called when the packet processed by PacketVisitor::Check is a PacketName */ \ - virtual void Visit(const packets::PacketName&) {} - -/** - * \class PacketVisitor - * \brief This class uses double-dispatch in order to find the real type of a packet - */ -class PacketVisitor : private NonCopyable { - protected: - PacketVisitor() {} - virtual ~PacketVisitor() {} - - public: - /** - * \brief Calls the right PacketVisitor::Visit method corresponding to the real type of the packet - * \param packet the Packet to visit - */ - void Check(const Packet& packet); - - DeclareAllPacket() -}; - -#undef DeclarePacket - -} // namespace protocol -} // namespace td diff --git a/include/td/protocol/packet/Packets.h b/include/td/protocol/packet/Packets.h index 24599fe..9b2ebd9 100644 --- a/include/td/protocol/packet/Packets.h +++ b/include/td/protocol/packet/Packets.h @@ -7,107 +7,66 @@ #include #include -#include + +#include +#include +#include +#include +#include namespace td { namespace protocol { -class PacketVisitor; - -/** A Packet id is 8 bits wide */ -using PacketID = std::uint8_t; -using PeerID = std::uint16_t; - -#define DeclarePacket(PacketName, ...) /** PacketName */ PacketName, - -/** - * \enum PacketType - * \brief Map a Packet to an id - */ -enum class PacketType : PacketID { - - DeclareAllPacket() - - /** The number of packets */ - PACKET_COUNT +enum class PacketID : std::uint8_t { + ChatMessage = 0, + BeginGame, + Disconnect, + KeepAlive, + LockSteps, + LoggingSuccess, + PlayerJoin, + PlayerLeave, + PlayerLogin, + PredictCommand, + WorldHeader, + WorldData, }; +class PacketHandler; -#undef DeclarePacket - - -class Packet : private NonCopyable { - public: - /** - * \return The real type of the packet - */ - virtual PacketType GetType() const = 0; - - /** - * \brief The network peer who sent the packet - */ - PeerID m_Sender; - - private: - /** Use a PacketVisitor to make double-dispatch possible */ - virtual void Accept(PacketVisitor& a_Visitor) const = 0; - - friend class PacketVisitor; -}; - +using PacketBase = sp::MessageBase; +template +using PacketMessage = sp::ConcreteMessage; namespace packets { -/** - * \class ConcretePacket - * \brief A Packet associated with an id and holding data - * \tparam PT The packet type - * \tparam Data The structure holding the data of the packet (in td::protocol::data namespace) - */ -template -class ConcretePacket : public Packet { - public: - /** The type of the struct holding the data */ - using PacketDataType = Data; - - /** The structure holding the actual data */ - PacketDataType m_Data; - - /** Construct the packet with data of type PacketDataType */ - ConcretePacket(const PacketDataType& a_Data = {}); - - constexpr PacketType GetType() const override { - return PT; - }; - - private: - void Accept(PacketVisitor& a_Visitor) const override; -}; - - - - - -// define TD_INSTANCIATE_PACKETS -// before including this file -// if you want to instantiate templates -#ifdef TD_INSTANCIATE_PACKETS -#define DeclarePacket(PacketName, ...) \ - using PacketName = ConcretePacket; \ - template class ConcretePacket; -#else -#define DeclarePacket(PacketName, ...) /** Defines the PacketName packet */ \ - using PacketName = ConcretePacket; -#endif - -DeclareAllPacket() - -#undef DeclarePacket +using BeginGamePacket = PacketMessage; +using ChatMessagePacket = PacketMessage; +using DisconnectPacket = PacketMessage; +using KeepAlivePacket = PacketMessage; +using LockStepsPacket = PacketMessage; +using LoggingSuccessPacket = PacketMessage; +using PlayerJoinPacket = PacketMessage; +using PlayerLeavePacket = PacketMessage; +using PlayerLoginPacket = PacketMessage; +using PredictCommandPacket = PacketMessage; +using WorldHeaderPacket = PacketMessage; +using WorldDataPacket = PacketMessage; } // namespace packets +using AllPackets = std::tuple; + +class PacketHandler : public sp::GenericHandler {}; + +using PacketDispatcher = sp::MessageDispatcher; + +using PacketFactory = sp::MessageFactory; + } // namespace protocol } // namespace td diff --git a/include/td/render/Camera.h b/include/td/render/Camera.h new file mode 100644 index 0000000..a0d386b --- /dev/null +++ b/include/td/render/Camera.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace td { +namespace render { + +class Camera { + private: + Mat4f m_ViewMatrix; + Mat4f m_ProjectionMatrix; + Mat4f m_InvViewMatrix; + Mat4f m_InvProjectionMatrix; + + float m_CamDistance = 25.0f; + Vec3f m_CamPos{0, m_CamDistance, 0}; + Vec2f m_CamLook{}; + + float m_Yaw = -PI / 2.0f; + float m_Pitch = -PI / 2.0f + 0.0000001f; + + public: + utils::Signal<> OnPerspectiveChange; + utils::Signal<> OnViewChange; + + const Mat4f& GetViewMatrix() const { + return m_ViewMatrix; + } + + const Mat4f& GetProjectionMatrix() const { + return m_ProjectionMatrix; + } + + void UpdatePerspective(float a_AspectRatio); + + void SetCamPos(const Vec3f& a_NewPos); +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/OpenGL.h b/include/td/render/OpenGL.h new file mode 100644 index 0000000..75fd640 --- /dev/null +++ b/include/td/render/OpenGL.h @@ -0,0 +1,8 @@ +#pragma once + +#ifdef TD_GL_LOADER_GLEW +#include +#else +#include +using namespace gl; +#endif diff --git a/include/td/render/Renderer.h b/include/td/render/Renderer.h new file mode 100644 index 0000000..cb5c23b --- /dev/null +++ b/include/td/render/Renderer.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace render { + +class BasicRenderer { + public: + virtual void Render(float a_Lerp) = 0; + virtual ~BasicRenderer() {} + + void Render(const GL::VertexArray& a_Vao); +}; + +template +class Renderer : public BasicRenderer { + protected: + std::unique_ptr m_Shader; + Camera& m_Camera; + + public: + Renderer(Camera& a_Camera); + virtual ~Renderer() {} + + template + float Lerp(const T& a_Mob, float a_LerpFactor, const std::function& a_MemberGetter) { + return static_cast(maths::Lerp(a_MemberGetter(a_Mob), a_MemberGetter(*a_Mob.m_Next), a_LerpFactor)); + } +}; + +class RenderPipeline { + private: + std::vector> m_Renderers; + + public: + RenderPipeline(); + virtual ~RenderPipeline() {} + + template + void AddRenderer(Args&&... args) { + m_Renderers.push_back(std::make_unique(args...)); + } + + void Clear() { + m_Renderers.clear(); + } + + void Render(float a_Lerp) { + for (auto& renderer : m_Renderers) { + renderer->Render(a_Lerp); + } + } +}; + + + + + +template +Renderer::Renderer(Camera& a_Camera) : m_Shader(std::make_unique()), m_Camera(a_Camera) { + a_Camera.OnPerspectiveChange.Connect([this]() { + m_Shader->Start(); + m_Shader->SetProjectionMatrix(m_Camera.GetProjectionMatrix()); + }); + + a_Camera.OnViewChange.Connect([this]() { + m_Shader->Start(); + m_Shader->SetViewMatrix(m_Camera.GetViewMatrix()); + }); +} + +} // namespace render +} // namespace td diff --git a/include/td/render/loader/GLLoader.h b/include/td/render/loader/GLLoader.h new file mode 100644 index 0000000..2447489 --- /dev/null +++ b/include/td/render/loader/GLLoader.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include + +namespace td { +namespace GL { + +struct VertexAttribPointer { + unsigned int m_Index; + unsigned int m_Size; + unsigned int m_Offset; +}; + +class ElementBuffer : private sp::NonCopyable { + private: + unsigned int m_ID = 0; + std::size_t m_TriangleCount; + + public: + ElementBuffer(ElementBuffer&& other) { + std::swap(m_ID, other.m_ID); + m_TriangleCount = other.m_TriangleCount; + } + + explicit ElementBuffer(const std::vector& indicies); + ~ElementBuffer(); + + void Bind() const; + void Unbind() const; + + std::size_t GetTriangleCount() const { + return m_TriangleCount; + } +}; + +class VertexBuffer : private sp::NonCopyable { + private: + unsigned int m_ID = 0, m_DataStride; + std::vector m_VertexAttribs; + + public: + VertexBuffer(VertexBuffer&& other) { + std::swap(m_ID, other.m_ID); + m_VertexAttribs = std::move(other.m_VertexAttribs); + m_DataStride = other.m_DataStride; + } + + VertexBuffer(const std::vector& data, unsigned int stride); + ~VertexBuffer(); + + void Bind() const; + void Unbind() const; + void AddVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset); + void BindVertexAttribs() const; +}; + +class VertexArray : private sp::NonCopyable { + private: + unsigned int m_ID = 0; + ElementBuffer m_ElementBuffer; + std::vector m_VertexBuffers; // use to destroy vbos when become unused + public: + VertexArray(VertexArray&& other) : m_ElementBuffer(std::move(other.m_ElementBuffer)) { + std::swap(m_ID, other.m_ID); + m_VertexBuffers = std::move(other.m_VertexBuffers); + } + + VertexArray(ElementBuffer&& indicies); + ~VertexArray(); + + std::size_t GetVertexCount() const { + return m_ElementBuffer.GetTriangleCount(); + } + + void BindVertexBuffer(VertexBuffer&& vbo); + void Bind() const; + void Unbind() const; + + private: + void BindElementArrayBuffer(); +}; + +} // namespace GL +} // namespace td diff --git a/include/td/render/loader/WorldLoader.h b/include/td/render/loader/WorldLoader.h new file mode 100644 index 0000000..0a5dc79 --- /dev/null +++ b/include/td/render/loader/WorldLoader.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace td { +namespace render { + +namespace WorldLoader { + +struct RenderData { + std::vector positions; + std::vector colors; +}; + +GL::VertexArray LoadMobModel(); +GL::VertexArray LoadWorldModel(const td::game::World* world); +GL::VertexArray LoadTileSelectModel(); +RenderData LoadTowerModel(const game::TowerPtr& tower); + +} // namespace WorldLoader + + +} // namespace render +} // namespace td diff --git a/include/td/render/renderer/EntityRenderer.h b/include/td/render/renderer/EntityRenderer.h new file mode 100644 index 0000000..eefd91d --- /dev/null +++ b/include/td/render/renderer/EntityRenderer.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace render { + +class EntityRenderer : public Renderer { + private: + const game::World& m_World; + std::unique_ptr m_EntityVao; + + public: + EntityRenderer(Camera& a_Camera, const game::World& a_World); + virtual ~EntityRenderer(); + + virtual void Render(float a_Lerp) override; +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/renderer/TowerRenderer.h b/include/td/render/renderer/TowerRenderer.h new file mode 100644 index 0000000..55f4dc3 --- /dev/null +++ b/include/td/render/renderer/TowerRenderer.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace render { + +class TowerRenderer : public Renderer { + private: + const game::World& m_World; + std::unique_ptr m_EntityVao; + + public: + TowerRenderer(Camera& a_Camera, const game::World& a_World); + virtual ~TowerRenderer(); + + virtual void Render(float a_Lerp) override; +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/renderer/WorldRenderer.h b/include/td/render/renderer/WorldRenderer.h new file mode 100644 index 0000000..ded2b24 --- /dev/null +++ b/include/td/render/renderer/WorldRenderer.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +namespace td { +namespace render { + +class WorldRenderer : public Renderer { + private: + std::unique_ptr m_WorldVao; + + public: + WorldRenderer(Camera& a_Camera, const game::World& a_World); + virtual ~WorldRenderer(); + + virtual void Render(float a_Lerp) override; +}; + +} // namespace render +} // namespace td diff --git a/include/td/render/shader/CameraShaderProgram.h b/include/td/render/shader/CameraShaderProgram.h new file mode 100644 index 0000000..66960ce --- /dev/null +++ b/include/td/render/shader/CameraShaderProgram.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace td { +namespace shader { + +class CameraShaderProgram : public ShaderProgram { + private: + unsigned int m_LocationProjection = 0, m_LocationView = 0; + + public: + CameraShaderProgram() {} + + void SetProjectionMatrix(const Mat4f& proj) const; + void SetViewMatrix(const Mat4f& view) const; + + virtual void GetAllUniformLocation(); +}; + + +} // namespace shader +} // namespace td diff --git a/include/td/render/shader/EntityShader.h b/include/td/render/shader/EntityShader.h new file mode 100644 index 0000000..1c7c948 --- /dev/null +++ b/include/td/render/shader/EntityShader.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace td { +namespace shader { + +class EntityShader : public CameraShaderProgram { + private: + unsigned int m_LocationPosition = 0; + unsigned int m_LocationColorEffect = 0; + + protected: + virtual void GetAllUniformLocation(); + + public: + EntityShader(); + + void SetColorEffect(const Vec3f& color); + void SetModelPos(const Vec3f& pos) const; +}; + +} // namespace shader +} // namespace td diff --git a/include/td/render/shader/ShaderProgram.h b/include/td/render/shader/ShaderProgram.h new file mode 100755 index 0000000..93d1f13 --- /dev/null +++ b/include/td/render/shader/ShaderProgram.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +namespace td { +namespace shader { + +class ShaderProgram { + public: + ShaderProgram(); + virtual ~ShaderProgram(); + + void Start() const; + void Stop() const; + + protected: + void LoadProgramFile(const std::string& vertexFile, const std::string& fragmentFile); + void LoadProgram(const std::string& vertexSource, const std::string& fragmentSource); + + virtual void GetAllUniformLocation() = 0; + int GetUniformLocation(const std::string& uniformName) const; + + void LoadFloat(unsigned int location, float value) const; + void LoadInt(unsigned int location, int value) const; + void LoadVector(unsigned int location, const Vec2f& vector) const; + void LoadVector(unsigned int location, const Vec3f& vector) const; + void LoadBoolean(unsigned int location, bool value) const; + void LoadMat4(unsigned int location, const Mat4f& mat) const; + void CleanUp() const; + + private: + unsigned int m_ProgramID; + unsigned int m_VertexShaderID; + unsigned int m_FragmentShaderID; + + unsigned int LoadShaderFromFile(const std::string& file, GLenum type); + unsigned int LoadShader(const std::string& source, GLenum type); +}; + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/include/td/render/shader/WorldShader.h b/include/td/render/shader/WorldShader.h new file mode 100644 index 0000000..8765708 --- /dev/null +++ b/include/td/render/shader/WorldShader.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace td { +namespace shader { + +class WorldShader : public CameraShaderProgram { +public: + WorldShader(); +}; + +} // namespace shader +} // namespace td diff --git a/include/td/simulation/ClientSimulation.h b/include/td/simulation/ClientSimulation.h new file mode 100644 index 0000000..f58abde --- /dev/null +++ b/include/td/simulation/ClientSimulation.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +namespace td { +namespace sim { + +using GameHistory = std::vector; +using GameBuffer = std::vector>; + +// TODO: OnEnd signal +class ClientSimulation : public protocol::PacketHandler { + private: + std::uint64_t m_StepTime; + game::World& m_World; + GameBuffer m_History; + std::uint64_t m_CurrentTime; + std::uint64_t m_LastTime; + std::size_t m_CurrentStep; + + std::shared_ptr m_LastSnapshot; + std::uint64_t m_LastValidStep; + + static const protocol::LockStep EMPTY_LOCKSTEP; + + public: + /** + * \brief Replay constructor + * \param a_StepTime in ms + */ + ClientSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime); + + /** + * \brief Live update constructor (continuous game updates) + * \param a_StepTime in ms + */ + ClientSimulation(game::World& a_World, std::uint64_t a_StepTime); + + /** + * \return the progress [0-1] between two steps + */ + float Update(); + + void Handle(const protocol::packets::LockStepsPacket& a_LockSteps) override; + void Handle(const protocol::packets::PredictCommandPacket& a_Predict) override; + + private: + void Step(); + + /** + * \brief Ticks a_Count times + */ + void FastForward(std::size_t a_Count); + + /** + * \brief Tries to recompute simulation if needed (for example in late command receival) + */ + void FastReplay(); +}; + +} // namespace sim +} // namespace td diff --git a/include/td/simulation/CommandApply.h b/include/td/simulation/CommandApply.h new file mode 100644 index 0000000..92d57d0 --- /dev/null +++ b/include/td/simulation/CommandApply.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace td { +namespace sim { + +class CommandApply : public protocol::CommandHandler { + private: + const game::World& m_World; + WorldSnapshot& m_Snapshot; + + public: + CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot); + + virtual void Handle(const protocol::commands::EndCommand& a_End) override; + virtual void Handle(const protocol::commands::PlaceTowerCommand& a_PlaceTower) override; + virtual void Handle(const protocol::commands::PlayerJoinCommand& a_PlayerJoin) override; + virtual void Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) override; + virtual void Handle(const protocol::commands::TeamChangeCommand& a_TeamChange) override; + virtual void Handle(const protocol::commands::UpgradeTowerCommand& a_UpgradeTower) override; + virtual void Handle(const protocol::commands::UseItemCommand& a_UseItem) override; +}; + +} // namespace sim +} // namespace td diff --git a/include/td/game/GameHistory.h b/include/td/simulation/GameHistory.h similarity index 88% rename from include/td/game/GameHistory.h rename to include/td/simulation/GameHistory.h index 2c951ed..806286a 100644 --- a/include/td/game/GameHistory.h +++ b/include/td/simulation/GameHistory.h @@ -24,7 +24,7 @@ class GameHistory { void FromPacket(td::protocol::pdata::LockSteps&& a_Steps); - td::protocol::packets::LockSteps ToPacket(HistorySizeType a_StartIndex); + td::protocol::packets::LockStepsPacket ToPacket(HistorySizeType a_StartIndex); }; } // namespace game diff --git a/include/td/simulation/ServerSimulation.h b/include/td/simulation/ServerSimulation.h new file mode 100644 index 0000000..2114fd1 --- /dev/null +++ b/include/td/simulation/ServerSimulation.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace td { +namespace sim { + +// TODO: OnEnd signal +class ServerSimulation { + private: + game::World& m_World; + std::uint64_t m_StepTime; + std::uint64_t m_CurrentTime; + + public: + ServerSimulation(game::World& a_World, std::uint64_t a_StepTime); + + void Update(); +}; + +} // namespace sim +} // namespace td diff --git a/include/td/simulation/WorldSnapshot.h b/include/td/simulation/WorldSnapshot.h new file mode 100644 index 0000000..3f20b64 --- /dev/null +++ b/include/td/simulation/WorldSnapshot.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace td { +namespace sim { + +struct WorldSnapshot { + game::MobList m_Mobs; + + game::TowerList m_Towers; + + game::TeamList m_Teams; +}; + +} // namespace sim +} // namespace td diff --git a/include/td/simulation/WorldTicker.h b/include/td/simulation/WorldTicker.h new file mode 100644 index 0000000..6431717 --- /dev/null +++ b/include/td/simulation/WorldTicker.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace td { +namespace game { +class World; +} + +namespace sim { + +class IWorldSystem { + public: + virtual void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) = 0; + virtual ~IWorldSystem() = default; +}; + +class WorldTicker { + private: + std::vector> m_Systems; + + public: + WorldTicker(); + + WorldSnapshot NextStep( + const game::World& a_World, WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta); + + private: + void ApplySteps(const game::World& a_World, WorldSnapshot& a_State, const protocol::LockStep& a_LockStep); + void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta); + WorldSnapshot CreateNext(WorldSnapshot& a_PreviousState); + + template + void AddSystem() { + m_Systems.push_back(std::make_unique()); + } +}; + +} // namespace sim +} // namespace td diff --git a/include/td/simulation/system/EntityMove.h b/include/td/simulation/system/EntityMove.h new file mode 100644 index 0000000..0535054 --- /dev/null +++ b/include/td/simulation/system/EntityMove.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace td { +namespace sim { + +class EntityMove : public IWorldSystem { + public: + virtual void Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) override; +}; + +} // namespace sim +} // namespace td diff --git a/src/main.cpp b/src/main.cpp index 9a466a1..ea61461 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,21 +1,139 @@ #include -// #include -#include -#include -#include -#include -#include -class Test : public td::protocol::CommandVisitor {}; +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +class WorldApply : public td::protocol::PacketHandler { + private: + td::game::World& m_World; + using td::protocol::PacketHandler::Handle; + + public: + WorldApply(td::game::World& a_World) : m_World(a_World) {} + + void Handle(const td::protocol::packets::WorldHeaderPacket& a_Header) override { + m_World.LoadMap(*a_Header); + } + + void Handle(const td::protocol::packets::WorldDataPacket& a_Data) override { + m_World.LoadMap(*a_Data); + } +}; + +void Save(const td::protocol::PacketBase& header, const td::protocol::PacketBase& data) { + auto comp = std::make_shared(); + + std::ofstream fStream("test/tdmap.tdmap3"); + auto out = std::make_shared(fStream); + + sp::MessageStream stream(std::move(out), std::move(comp)); + + stream.WriteMessage(header, false); + stream.WriteMessage(data, false); +} + +td::game::World GetWorld() { + auto comp = std::make_shared(); + + std::ifstream fStream("test/tdmap.tdmap2"); + auto out = std::make_shared(fStream); + + sp::MessageStream stream(std::move(out), std::move(comp)); + + auto header = stream.ReadMessage(td::protocol::PacketID::WorldHeader); + auto data = stream.ReadMessage(td::protocol::PacketID::WorldData); + + td::game::World w; + auto wa = std::make_shared(w); + + td::protocol::PacketDispatcher d; + d.RegisterHandler(wa); + + d.Dispatch(*header); + d.Dispatch(*data); + + Save(*header, *data); + + return w; +} + +void FastForward(td::game::World& a_World, const td::sim::GameHistory& a_LockSteps) { + const td::FpFloat delta = td::FpFloat(1) / td::FpFloat(75); + for (const auto& lockstep : a_LockSteps) { + a_World.Tick(lockstep, delta); + } +} + +td::sim::GameHistory GetCustomHistory() { + constexpr std::size_t MAX_COUNT = 20 * 60 * 40; + + td::sim::GameHistory gh(MAX_COUNT); + + auto spawn = td::protocol::CommandPtr( + std::make_shared(td::EntityType::Zombie, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0)); + gh[0].push_back(spawn); + + auto tower = td::protocol::CommandPtr( + std::make_shared(td::TowerType::Archer, 0, td::TowerCoords{77, 13})); + gh[0].push_back(tower); + + return gh; +} + int main(int argc, char** argv) { - // Test visitor; - // td::protocol::packets::ChatMessage chat{{"coucou"}}; - // visitor.Check(chat); + td::game::World w = GetWorld(); + // init GL context + td::Display display(1920, 1080, "Tower-Defense 2"); + + td::render::Camera cam; + + display.OnAspectRatioChange.Connect([&cam](float a_AspectRatio) { cam.UpdatePerspective(a_AspectRatio); }); + + td::sim::GameHistory gh = GetCustomHistory(); + + td::render::RenderPipeline renderer; + renderer.AddRenderer(cam, w); + renderer.AddRenderer(cam, w); + renderer.AddRenderer(cam, w); + + cam.SetCamPos({77, 7, 13}); + cam.UpdatePerspective(display.GetAspectRatio()); + + td::sim::ClientSimulation simulation(w, 50); + + display.OnKeyDown.Connect([&simulation](SDL_Keycode key) { + static int counter = 0; + if (key == SDLK_A) { + auto spawn = td::protocol::CommandPtr( + std::make_shared(td::EntityType::Zombie, 0, td::Vec2fp{td::FpFloat(77), td::FpFloat(13)}, 0)); + std::array steps{}; + steps[0].push_back(spawn); + td::protocol::packets::LockStepsPacket packet{counter * LOCKSTEP_BUFFER_SIZE * 3, steps}; + simulation.Handle(packet); + counter++; + } + }); + + while (!display.IsCloseRequested()) { + display.PollEvents(); + float lerp = simulation.Update(); + renderer.Render(lerp); + display.Update(); + } - td::protocol::commands::UpgradeTower com{{1, 2}}; - std::cout << (unsigned)com.GetType() << std::endl; - td::protocol::CommandDispatcher disptacher; return 0; } diff --git a/src/td/Maths.cpp b/src/td/Maths.cpp new file mode 100644 index 0000000..97358af --- /dev/null +++ b/src/td/Maths.cpp @@ -0,0 +1,169 @@ +#include + +#include +#include + +namespace td { +namespace maths { + +template +T Length(const Vec3& vect) { + return std::sqrt(vect.x * vect.x + vect.y * vect.y + vect.z * vect.z); +} + +template +Vec3 Normalize(const Vec3& vect) { + T length = Length(vect); + + return {vect.x / length, vect.y / length, vect.z / length}; +} + +template +Vec4 Normalize(const Vec4& vect) { + T length = std::sqrt(vect.x * vect.x + vect.y * vect.y + vect.z * vect.z + vect.w * vect.w); + + return {vect.x / length, vect.y / length, vect.z / length, vect.w / length}; +} + +template +T Dot(const Vec3& vect, const Vec3& other) { + return vect.x * other.x + vect.y * other.y + vect.z * other.z; +} + +template +Vec3 Cross(const Vec3& vect, const Vec3& other) { + return { + vect.y * other.z - vect.z * other.y, + vect.z * other.x - vect.x * other.z, + vect.x * other.y - vect.y * other.x, + }; +} + +template +T Dot(const Vec4& vect, const Vec4& other) { + return vect.x * other.x + vect.y * other.y + vect.z * other.z + vect.w * other.w; +} + +template +T Distance(const Vec3& vect, const Vec3& other) { + return Length(vect - other); +} + +template +Vec4 Dot(const Mat4& mat, const Vec4& vect) { + return {mat.x0 * vect.x + mat.x1 * vect.y + mat.x2 * vect.z + mat.x3 * vect.w, + mat.y0 * vect.x + mat.y1 * vect.y + mat.y2 * vect.z + mat.y3 * vect.w, + mat.z0 * vect.x + mat.z1 * vect.y + mat.z2 * vect.z + mat.z3 * vect.w, + mat.w0 * vect.x + mat.w1 * vect.y + mat.w2 * vect.z + mat.w3 * vect.w}; +} + +template +Mat4 Dot(const Mat4& mat, const Mat4& other) { + Mat4 result{}; + + for (std::size_t i = 0; i < Mat4::MATRIX_SIZE; i++) { + for (std::size_t j = 0; j < Mat4::MATRIX_SIZE; j++) { + for (std::size_t k = 0; k < Mat4::MATRIX_SIZE; k++) { + result.at(i, j) += mat.at(i, k) * other.at(k, j); + } + } + } + + return result; +} + +template +Mat4 Identity() { + Mat4 result{}; + + result.x0 = static_cast(1); + result.y1 = static_cast(1); + result.z2 = static_cast(1); + result.w3 = static_cast(1); + + return result; +} + +Mat4f Perspective(float fovY, float aspectRatio, float zNear, float zFar) { + const float tanHalfFovy = std::tan(fovY / 2.0f); + + Mat4f result{}; + result.x0 = 1.0f / (aspectRatio * tanHalfFovy); + result.y1 = 1.0f / (tanHalfFovy); + result.z2 = -(zFar + zNear) / (zFar - zNear); + result.z3 = -1.0f; + result.w2 = -(2.0f * zFar * zNear) / (zFar - zNear); + + return result; +} + +Mat4f Look(const Vec3f& eye, const Vec3f& center, const Vec3f& up) { + const Vec3f f = Normalize(center); + const Vec3f s = Normalize(Cross(f, up)); + const Vec3f u = Cross(s, f); + + Mat4f result = Identity(); + result.x0 = s.x; + result.y0 = s.y; + result.z0 = s.z; + result.x1 = u.x; + result.y1 = u.y; + result.z1 = u.z; + result.x2 = -f.x; + result.y2 = -f.y; + result.z2 = -f.z; + result.w0 = -Dot(s, eye); + result.w1 = -Dot(u, eye); + result.w2 = Dot(f, eye); + + return result; +} + +Mat4f Inverse(const Mat4f& mat) { + float s0 = mat.at(0, 0) * mat.at(1, 1) - mat.at(1, 0) * mat.at(0, 1); + float s1 = mat.at(0, 0) * mat.at(1, 2) - mat.at(1, 0) * mat.at(0, 2); + float s2 = mat.at(0, 0) * mat.at(1, 3) - mat.at(1, 0) * mat.at(0, 3); + float s3 = mat.at(0, 1) * mat.at(1, 2) - mat.at(1, 1) * mat.at(0, 2); + float s4 = mat.at(0, 1) * mat.at(1, 3) - mat.at(1, 1) * mat.at(0, 3); + float s5 = mat.at(0, 2) * mat.at(1, 3) - mat.at(1, 2) * mat.at(0, 3); + + float c5 = mat.at(2, 2) * mat.at(3, 3) - mat.at(3, 2) * mat.at(2, 3); + float c4 = mat.at(2, 1) * mat.at(3, 3) - mat.at(3, 1) * mat.at(2, 3); + float c3 = mat.at(2, 1) * mat.at(3, 2) - mat.at(3, 1) * mat.at(2, 2); + float c2 = mat.at(2, 0) * mat.at(3, 3) - mat.at(3, 0) * mat.at(2, 3); + float c1 = mat.at(2, 0) * mat.at(3, 2) - mat.at(3, 0) * mat.at(2, 2); + float c0 = mat.at(2, 0) * mat.at(3, 1) - mat.at(3, 0) * mat.at(2, 1); + + float det = s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0; + + assert(det != 0 && "Determinant equals 0 !"); + + float invdet = 1.0 / det; + + Mat4f result; + + result.at(0, 0) = (mat.at(1, 1) * c5 - mat.at(1, 2) * c4 + mat.at(1, 3) * c3) * invdet; + result.at(0, 1) = (-mat.at(0, 1) * c5 + mat.at(0, 2) * c4 - mat.at(0, 3) * c3) * invdet; + result.at(0, 2) = (mat.at(3, 1) * s5 - mat.at(3, 2) * s4 + mat.at(3, 3) * s3) * invdet; + result.at(0, 3) = (-mat.at(2, 1) * s5 + mat.at(2, 2) * s4 - mat.at(2, 3) * s3) * invdet; + + result.at(1, 0) = (-mat.at(1, 0) * c5 + mat.at(1, 2) * c2 - mat.at(1, 3) * c1) * invdet; + result.at(1, 1) = (mat.at(0, 0) * c5 - mat.at(0, 2) * c2 + mat.at(0, 3) * c1) * invdet; + result.at(1, 2) = (-mat.at(3, 0) * s5 + mat.at(3, 2) * s2 - mat.at(3, 3) * s1) * invdet; + result.at(1, 3) = (mat.at(2, 0) * s5 - mat.at(2, 2) * s2 + mat.at(2, 3) * s1) * invdet; + + result.at(2, 0) = (mat.at(1, 0) * c4 - mat.at(1, 1) * c2 + mat.at(1, 3) * c0) * invdet; + result.at(2, 1) = (-mat.at(0, 0) * c4 + mat.at(0, 1) * c2 - mat.at(0, 3) * c0) * invdet; + result.at(2, 2) = (mat.at(3, 0) * s4 - mat.at(3, 1) * s2 + mat.at(3, 3) * s0) * invdet; + result.at(2, 3) = (-mat.at(2, 0) * s4 + mat.at(2, 1) * s2 - mat.at(2, 3) * s0) * invdet; + + result.at(3, 0) = (-mat.at(1, 0) * c3 + mat.at(1, 1) * c1 - mat.at(1, 2) * c0) * invdet; + result.at(3, 1) = (mat.at(0, 0) * c3 - mat.at(0, 1) * c1 + mat.at(0, 2) * c0) * invdet; + result.at(3, 2) = (-mat.at(3, 0) * s3 + mat.at(3, 1) * s1 - mat.at(3, 2) * s0) * invdet; + result.at(3, 3) = (mat.at(2, 0) * s3 - mat.at(2, 1) * s1 + mat.at(2, 2) * s0) * invdet; + + return result; +} + +} // namespace maths +} // namespace td \ No newline at end of file diff --git a/src/td/Types.cpp b/src/td/Types.cpp new file mode 100644 index 0000000..25ce84f --- /dev/null +++ b/src/td/Types.cpp @@ -0,0 +1,29 @@ +#include + +#include +#include +#include + +namespace td { + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const EntityCoords& a_Coords) { + return a_Buffer << a_Coords.x << a_Coords.y; +} + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const FpFloat& a_Float) { + auto raw = a_Float.raw_value(); + return a_Buffer << raw; +} + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, EntityCoords& a_Coords) { + return a_Buffer >> a_Coords.x >> a_Coords.y; +} + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, FpFloat& a_Float) { + auto raw = a_Float.raw_value(); + a_Buffer >> raw; + a_Float = FpFloat::from_raw_value(raw); + return a_Buffer; +} + +} // namespace td diff --git a/src/td/common/DataBuffer.cpp b/src/td/common/DataBuffer.cpp deleted file mode 100644 index 9ed5062..0000000 --- a/src/td/common/DataBuffer.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include - -#include -#include -#include - -#include -#include - -namespace td { - -DataBuffer::DataBuffer() : m_ReadOffset(0) {} - -DataBuffer::DataBuffer(const DataBuffer& other) : m_Buffer(other.m_Buffer), m_ReadOffset(other.m_ReadOffset) {} - -DataBuffer::DataBuffer(DataBuffer&& other) : m_Buffer(std::move(other.m_Buffer)), m_ReadOffset(std::move(other.m_ReadOffset)) {} - -DataBuffer::DataBuffer(const std::string& str) : m_Buffer(str.begin(), str.end()), m_ReadOffset(0) {} - -DataBuffer::DataBuffer(const DataBuffer& other, Data::difference_type offset) : m_ReadOffset(0) { - m_Buffer.reserve(other.GetSize() - static_cast(offset)); - std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer)); -} - -DataBuffer& DataBuffer::operator<<(const std::string& str) { - std::size_t strlen = str.length() + 1; // including null character - Resize(GetSize() + strlen); - std::memcpy(m_Buffer.data() + GetSize() - strlen, str.data(), strlen); - return *this; -} - -DataBuffer& DataBuffer::operator<<(const DataBuffer& data) { - m_Buffer.insert(m_Buffer.end(), data.begin(), data.end()); - return *this; -} - -DataBuffer& DataBuffer::operator>>(std::string& str) { - std::size_t stringSize = strlen(reinterpret_cast(m_Buffer.data()) + m_ReadOffset) + 1; // including null character - str.resize(stringSize); - std::copy(m_Buffer.begin() + static_cast(m_ReadOffset), - m_Buffer.begin() + static_cast(m_ReadOffset + stringSize), str.begin()); - m_ReadOffset += stringSize; - str.resize(stringSize - 1); - return *this; -} - -DataBuffer& DataBuffer::operator>>(DataBuffer& data) { - data.Resize(GetSize() - m_ReadOffset); - std::copy(m_Buffer.begin() + static_cast(m_ReadOffset), m_Buffer.end(), data.begin()); - m_ReadOffset = m_Buffer.size(); - return *this; -} - -void DataBuffer::WriteSome(const char* buffer, std::size_t amount) { - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + amount); - std::memcpy(m_Buffer.data() + end_pos, buffer, amount); -} - -void DataBuffer::WriteSome(const std::uint8_t* buffer, std::size_t amount) { - std::size_t end_pos = m_Buffer.size(); - m_Buffer.resize(m_Buffer.size() + amount); - std::memcpy(m_Buffer.data() + end_pos, buffer, amount); -} - -void DataBuffer::ReadSome(char* buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer); - m_ReadOffset += amount; -} - - -void DataBuffer::ReadSome(std::uint8_t* buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer); - m_ReadOffset += amount; -} - - -void DataBuffer::ReadSome(DataBuffer& buffer, std::size_t amount) { - assert(m_ReadOffset + amount <= GetSize()); - buffer.Resize(amount); - std::copy_n(m_Buffer.begin() + static_cast(m_ReadOffset), amount, buffer.begin()); - m_ReadOffset += amount; -} - - -DataBuffer& DataBuffer::operator=(const DataBuffer& other) { - m_Buffer = other.m_Buffer; - m_ReadOffset = other.m_ReadOffset; - return *this; -} - -DataBuffer& DataBuffer::operator=(DataBuffer&& other) { - m_Buffer = std::move(other.m_Buffer); - m_ReadOffset = std::move(other.m_ReadOffset); - return *this; -} - -void DataBuffer::SetReadOffset(std::size_t pos) { - assert(pos <= GetSize()); - m_ReadOffset = pos; -} - -std::size_t DataBuffer::GetSize() const { - return m_Buffer.size(); -} - -std::size_t DataBuffer::GetRemaining() const { - return m_Buffer.size() - m_ReadOffset; -} - -DataBuffer::iterator DataBuffer::begin() { - return m_Buffer.begin(); -} - -DataBuffer::iterator DataBuffer::end() { - return m_Buffer.end(); -} - -DataBuffer::const_iterator DataBuffer::begin() const { - return m_Buffer.begin(); -} - -DataBuffer::const_iterator DataBuffer::end() const { - return m_Buffer.end(); -} - -std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer) { - for (unsigned char u : buffer) - os << std::hex << std::setfill('0') << std::setw(2) << static_cast(u) << " "; - os << std::dec; - return os; -} - -bool DataBuffer::ReadFile(const std::string& fileName) { - try { - std::ifstream file(fileName, std::istream::binary); - std::ostringstream ss; - ss << file.rdbuf(); - const std::string& s = ss.str(); - m_Buffer = DataBuffer::Data(s.begin(), s.end()); - m_ReadOffset = 0; - } catch (std::exception& e) { - utils::LOGE(utils::Format("[IO] Failed to read file %s ! reason : %s", fileName.c_str(), e.what())); - return false; - } - return m_Buffer.size() > 0; -} - -bool DataBuffer::WriteFile(const std::string& fileName) const { - try { - std::ofstream file(fileName, std::ostream::binary); - file.write(reinterpret_cast(m_Buffer.data()), static_cast(m_Buffer.size())); - file.flush(); - } catch (std::exception& e) { - utils::LOGE(utils::Format("[IO] Failed to read file %s ! reason : %s", fileName.c_str(), e.what())); - return false; - } - return true; -} - -} // namespace td diff --git a/src/td/common/VarInt.cpp b/src/td/common/VarInt.cpp deleted file mode 100644 index 0136f2f..0000000 --- a/src/td/common/VarInt.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include - -#include -#include - -namespace td { - -static constexpr int SEGMENT_BITS = 0x7F; -static constexpr int CONTINUE_BIT = 0x80; - -std::size_t VarInt::GetSerializedLength() const { - DataBuffer buffer; - buffer << *this; - return buffer.GetSize(); -} - -DataBuffer& operator<<(DataBuffer& out, const VarInt& var) { - std::uint64_t value = var.m_Value; - while (true) { - if ((value & ~static_cast(SEGMENT_BITS)) == 0) { - out << static_cast(value); - return out; - } - - out << static_cast((value & SEGMENT_BITS) | CONTINUE_BIT); - - value >>= 7; - } -} - -DataBuffer& operator>>(DataBuffer& in, VarInt& var) { - var.m_Value = 0; - unsigned int position = 0; - std::uint8_t currentByte; - - while (true) { - in.ReadSome(¤tByte, 1); - var.m_Value |= static_cast(currentByte & SEGMENT_BITS) << position; - - if ((currentByte & CONTINUE_BIT) == 0) - break; - - position += 7; - - if (position >= 8 * sizeof(var.m_Value)) - throw std::runtime_error("VarInt is too big"); - } - - return in; -} - -} // namespace td diff --git a/src/td/game/Game.cpp b/src/td/game/Game.cpp new file mode 100644 index 0000000..c3677ec --- /dev/null +++ b/src/td/game/Game.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/td/game/Mobs.cpp b/src/td/game/Mobs.cpp new file mode 100644 index 0000000..1cae4a7 --- /dev/null +++ b/src/td/game/Mobs.cpp @@ -0,0 +1,24 @@ +#include + +namespace td { +namespace game { + + +const MobStats* GetMobStats(MobType type, std::uint8_t level) { + static MobStats stats; + return &stats; +} + +const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level) { + static TowerImmunities ti; + return ti; +} + +const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level) { + static EffectImmunities ei; + return ei; +} + + +} // namespace game +} // namespace td diff --git a/src/td/game/World.cpp b/src/td/game/World.cpp index 19928b0..ba9be9a 100644 --- a/src/td/game/World.cpp +++ b/src/td/game/World.cpp @@ -1,30 +1,81 @@ #include -#include - -#include +#include namespace td { namespace game { -World::World() : m_WorldStates(std::numeric_limits::max()), m_OffsetTime(0) {} +class ColorTileVisitor : public TileHandler { + private: + const World& m_World; + const Color* m_Result; -void World::Tick(float a_Delta) { - m_OffsetTime += a_Delta; + public: + ColorTileVisitor(const World& a_World) : m_World(a_World), m_Result(nullptr) {} - if (GetCursorPos() > m_WorldStates.size()) { - if (!m_GameHistory.HasLockStep(GetCursorPos())) { - // oupsi - return; - } - WorldState& lastState = m_WorldStates.back(); - WorldState nextState = lastState.GetNextState(m_GameHistory.GetLockStep(GetCursorPos())); - m_WorldStates.push_back(std::move(nextState)); + virtual void Handle(const EmptyTile& a_Tile) override {} + + virtual void Handle(const TowerTile& a_Tile) override { + m_Result = &m_World.GetTowerTileColorPalette()[a_Tile->m_ColorPaletteRef]; } + + virtual void Handle(const WalkableTile& a_Tile) override { + m_Result = &m_World.GetWalkableTileColor(); + } + + virtual void Handle(const DecorationTile& a_Tile) override { + m_Result = &m_World.GetDecorationPalette()[a_Tile->m_ColorPaletteRef]; + } + + const Color* GetResult() { + return m_Result; + } +}; + +World::World() : m_CurrentState(std::make_shared()), m_NextState(m_CurrentState) {} + +const Color* World::GetTileColor(const TilePtr& tile) const { + ColorTileVisitor visitor(*this); + tile->Dispatch(visitor); + return visitor.GetResult(); } -std::uint16_t World::GetCursorPos() { - return (std::uint16_t) m_OffsetTime * TPS; +bool World::LoadMap(const protocol::pdata::WorldHeader& a_WorldHeader) { + m_TowerPlacePalette = a_WorldHeader.m_TowerPlacePalette; + m_WalkablePalette = a_WorldHeader.m_WalkablePalette; + m_DecorationPalette = a_WorldHeader.m_DecorationPalette; + m_Background = a_WorldHeader.m_Background; + + GetRedTeam().GetSpawn() = a_WorldHeader.m_RedSpawn; + GetBlueTeam().GetSpawn() = a_WorldHeader.m_BlueSpawn; + + m_SpawnColorPalette = a_WorldHeader.m_SpawnColorPalette; + + GetRedTeam().GetCastle() = a_WorldHeader.m_RedCastle; + // GetRedTeam().GetCastle().SetTeam(&GetRedTeam()); + + GetBlueTeam().GetCastle() = a_WorldHeader.m_BlueCastle; + // GetBlueTeam().GetCastle().SetTeam(&GetBlueTeam()); + + m_TilePalette = a_WorldHeader.m_TilePalette; + + return true; +} + +bool World::LoadMap(const protocol::pdata::WorldData& a_WorldData) { + m_Chunks = a_WorldData.m_Chunks; + return true; +} + +const std::shared_ptr& World::Tick(const protocol::LockStep& a_LockStep, FpFloat a_Delta) { + m_CurrentState = m_NextState; + m_NextState = std::make_shared(m_Ticker.NextStep(*this, *m_NextState, a_LockStep, a_Delta)); + return m_CurrentState; +} + +void World::ResetSnapshots(std::shared_ptr& a_Current, std::shared_ptr& a_Next) { + m_CurrentState = a_Current; + m_NextState = a_Next; } } // namespace game diff --git a/src/td/game/WorldState.cpp b/src/td/game/WorldState.cpp deleted file mode 100644 index c6a4905..0000000 --- a/src/td/game/WorldState.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include - -#include - -#include - -namespace td { -namespace game { - -class CommandHandler : public protocol::CommandVisitor { - private: - WorldState& m_WorldState; - - public: - CommandHandler(WorldState& a_WorldState) : m_WorldState(a_WorldState) {} - - void Visit(const protocol::commands::End&) override {} - void Visit(const protocol::commands::PlaceTower&) override {} - void Visit(const protocol::commands::PlayerJoin&) override {} - void Visit(const protocol::commands::SpawnTroop&) override {} - void Visit(const protocol::commands::TeamChange&) override {} - void Visit(const protocol::commands::UpgradeTower&) override {} - void Visit(const protocol::commands::UseItem&) override {} -}; - -WorldState WorldState::GetNextState(const protocol::LockStep& a_Step) { - WorldState newState = *this; - CommandHandler handler(*this); - for (auto command : a_Step) { - handler.Check(*command); - } - newState.Tick(STEP_PERIOD); - return newState; -} - -void WorldState::Tick(float a_Delta) { - // move mobs - // process towers - // process damages -} - -} // namespace game -} // namespace td diff --git a/src/td/game/WorldTypes.cpp b/src/td/game/WorldTypes.cpp new file mode 100644 index 0000000..a9c2f96 --- /dev/null +++ b/src/td/game/WorldTypes.cpp @@ -0,0 +1,37 @@ +#include + +namespace td { +namespace game { + +const int BITS_IN_BYTE = 8; +const int BITS_IN_LONG = BITS_IN_BYTE * sizeof(std::uint64_t); + +static unsigned int countBits(unsigned int number) { + // log function in base 2 + // take only integer part + return static_cast(std::log2(number) + 1); +} + +TileIndex Chunk::GetTileIndex(std::uint16_t tileNumber) const { + const std::uint8_t bitsPerTile = countBits(m_Palette.size()); + + const std::size_t startLong = (tileNumber * bitsPerTile) / BITS_IN_LONG; + const std::size_t startOffset = (tileNumber * bitsPerTile) % BITS_IN_LONG; + const std::size_t endLong = ((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG; + + const Chunk::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1); + + td::game::Chunk::ChunkData::value_type value; + if (startLong == endLong) { + value = (m_Data[startLong] >> startOffset); + } else { + int endOffset = BITS_IN_LONG - startOffset; + value = (m_Data[startLong] >> startOffset | m_Data[endLong] << endOffset); + } + value &= individualValueMask; + + return m_Palette.at(value); +} + +} // namespace game +} // namespace td diff --git a/src/td/input/Display.cpp b/src/td/input/Display.cpp new file mode 100644 index 0000000..ec32934 --- /dev/null +++ b/src/td/input/Display.cpp @@ -0,0 +1,184 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace td { + +Display::Display(int a_Width, int a_Height, const std::string& a_Title) : + m_LastWidth(0), m_LastHeight(0), m_AspectRatio(1), m_ShouldClose(false) { + + m_Window = SDL_CreateWindow(a_Title.c_str(), a_Width, a_Height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + + m_LastWidth = a_Width; + m_LastHeight = a_Height; + m_AspectRatio = (float)m_LastWidth / m_LastHeight; + + // Prepare and create context +#ifdef __ANDROID__ + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); +#endif + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 8); + + m_GLContext = SDL_GL_CreateContext(m_Window); + + if (!m_GLContext) { + utils::LOGE(utils::Format("Could not create context! SDL error: %s", SDL_GetError())); + } + + int major, minor, mask; + int r, g, b, a, depth; + int mBuffers, mSamples; + + SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &mask); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); + + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r); + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g); + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b); + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &a); + + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth); + + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &mBuffers); + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &mSamples); + + const char* mask_desc; + + if (mask & SDL_GL_CONTEXT_PROFILE_CORE) { + mask_desc = "core"; + } else if (mask & SDL_GL_CONTEXT_PROFILE_COMPATIBILITY) { + mask_desc = "compatibility"; + } else if (mask & SDL_GL_CONTEXT_PROFILE_ES) { + mask_desc = "es"; + } else { + mask_desc = "?"; + } + + utils::LOG(utils::Format( + "GL Context : %i.%i %s, Color : R:%i G:%i B:%i A:%i, Depth bits : %i", major, minor, mask_desc, r, g, b, a, depth)); + + utils::LOG(utils::Format( + "MultiSamples : Buffers : %i, Samples : %i", mBuffers, mSamples)); + + SDL_GL_MakeCurrent(m_Window, m_GLContext); + + GLenum error = glewInit(); + if (error) { + utils::LOGE(utils::Format("Error initializing glew : %s", glewGetErrorString(error))); + } + + // WindowResizeEvent(WindowWidth, WindowHeight); + + // vsync + SDL_GL_SetSwapInterval(1); + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + // ImGui::StyleColorsLight(); + + // Setup scaling + float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); + // ImGuiStyle& style = ImGui::GetStyle(); + // style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this + // // requires resetting Style + calling this again) + // style.FontSizeBase = 13 * main_scale; // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave + // // both here for documentation purpose) + + + ImFontConfig cfg; + cfg.SizePixels = 13 * main_scale * 2; + io.Fonts->AddFontDefault(&cfg); + + // Setup Platform/Renderer backends + ImGui_ImplSDL3_InitForOpenGL(m_Window, m_GLContext); + ImGui_ImplOpenGL3_Init("#version 330"); +} + + +void Display::PollEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_QUIT: + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { + m_ShouldClose = true; + break; + } + + case SDL_EVENT_WINDOW_RESIZED: { + m_LastWidth = event.window.data1; + m_LastHeight = event.window.data2; + m_AspectRatio = (float)m_LastWidth / m_LastHeight; + OnAspectRatioChange(m_AspectRatio); + break; + } + + case SDL_EVENT_KEY_DOWN: { + if(!event.key.repeat) + OnKeyDown(event.key.key); + break; + } + + default: + break; + } + ImGui_ImplSDL3_ProcessEvent(&event); + } + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL3_NewFrame(); + ImGui::NewFrame(); +} + +void Display::Update() { + ImGui::Render(); + ImGuiIO& io = ImGui::GetIO(); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + // glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + SDL_GL_SwapWindow(m_Window); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + + +Display::~Display() { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL3_Shutdown(); + ImGui::DestroyContext(); + SDL_GL_DestroyContext(m_GLContext); + SDL_DestroyWindow(m_Window); + SDL_Quit(); +} + +} // namespace td diff --git a/src/td/protocol/command/CommandFactory.cpp b/src/td/protocol/command/CommandFactory.cpp deleted file mode 100644 index 0dbdc9f..0000000 --- a/src/td/protocol/command/CommandFactory.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include - -#include -#include -#include - -namespace td { -namespace protocol { -namespace CommandFactory { - -using CommandCreator = std::function()>; - -#define DeclareCommand(CommandName, ...) std::make_shared(), - -static std::array, static_cast(CommandType::COMMAND_COUNT)> Commands = {DeclareAllCommand()}; - -const std::shared_ptr& CreateReadOnlyCommand(CommandType a_Type) { - assert(a_Type < CommandType::COMMAND_COUNT); - return Commands[static_cast(a_Type)]; -} - -} // namespace CommandFactory -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/command/CommandSerializer.cpp b/src/td/protocol/command/CommandSerializer.cpp deleted file mode 100644 index 80b1d3d..0000000 --- a/src/td/protocol/command/CommandSerializer.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include - -#include -#include - -#include -#include - -namespace td { -namespace protocol { -namespace CommandSerializer { - -#define DeclareCommand(CommandName, ...) \ - void Visit(const commands::CommandName& a_Command) override { \ - const auto& commandData = a_Command.m_Data; \ - SerializeCommandData(commandData); \ - } \ - \ - void SerializeCommandData(const commands::CommandName::CommandDataType& a_Command); - - - - -class Serializer : public CommandVisitor { - private: - DataBuffer& m_Buffer; - - public: - Serializer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer) {} - - void Serialize(const Command& a_Command) { - m_Buffer << static_cast(a_Command.GetType()); - Check(a_Command); - } - - DeclareAllCommand() -}; - -#undef DeclareCommand - - - - - -#define DeclareCommand(CommandName, ...) \ - void Visit(const commands::CommandName& a_Command) override { \ - auto commandPtr = CommandFactory::CreateCommand(); \ - auto& commandData = commandPtr->m_Data; \ - \ - DeserializeCommandData(commandData); \ - \ - m_Command = std::move(commandPtr); \ - } \ - \ - void DeserializeCommandData(commands::CommandName::CommandDataType& a_Command); - - - -class Deserializer : public CommandVisitor { - private: - DataBuffer& m_Buffer; - CommandPtr m_Command; - - public: - Deserializer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer) {} - - bool Deserialize(const CommandPtr& a_Command) { - try { - Check(*a_Command.get()); - } catch (std::exception& e) { - utils::LOGE(utils::Format("[CommandSerializer::Deserializer] %s", e.what())); - return false; - } - return true; - } - - CommandPtr& GetCommand() { - return m_Command; - } - - DeclareAllCommand() -}; - - - - - -DataBuffer Serialize(const Command& a_Command) { - DataBuffer buffer; - - Serializer serializer(buffer); - serializer.Serialize(a_Command); - - return buffer; -} - -std::shared_ptr Deserialize(DataBuffer& a_Data) { - CommandID commandId; - a_Data >> commandId; - - if (commandId >= static_cast(CommandType::COMMAND_COUNT)) - return nullptr; - - CommandType commandType = CommandType(commandId); - - // for double-dispatch - const CommandPtr& emptyCommand = CommandFactory::CreateReadOnlyCommand(commandType); - - Deserializer deserializer(a_Data); - - if (deserializer.Deserialize(emptyCommand)) { - CommandPtr command = std::move(deserializer.GetCommand()); - return command; - } - - return nullptr; -} - - - - - -//--------------------------------------------- -// Command serializer implementation -//---------------------------------------------- - - - - - -void Serializer::SerializeCommandData(const cdata::End& a_Command) {} - -void Deserializer::DeserializeCommandData(cdata::End& a_Command) {} - - - - - -void Serializer::SerializeCommandData(const cdata::PlaceTower& a_Command) { - m_Buffer << static_cast((static_cast(a_Command.m_Type) << 4 | a_Command.m_Placer & 0xF)) - << a_Command.m_Position; -} - -void Deserializer::DeserializeCommandData(cdata::PlaceTower& a_Command) { - std::uint8_t union1; - m_Buffer >> union1 >> a_Command.m_Position; - a_Command.m_Type = td::TowerType(union1 >> 4); - a_Command.m_Placer = union1 & 0xF; -} - - - - - -void Serializer::SerializeCommandData(const cdata::PlayerJoin& a_Command) { - m_Buffer << a_Command.m_ID << a_Command.m_Name; -} - -void Deserializer::DeserializeCommandData(cdata::PlayerJoin& a_Command) { - m_Buffer >> a_Command.m_ID >> a_Command.m_Name; -} - - - - -void Serializer::SerializeCommandData(const cdata::SpawnTroop& a_Command) { - m_Buffer << static_cast(static_cast(a_Command.m_Type) << 3 | a_Command.m_Level & 0x7) - << a_Command.m_Position << a_Command.m_Sender; -} - -void Deserializer::DeserializeCommandData(cdata::SpawnTroop& a_Command) { - std::uint8_t union1; - m_Buffer >> union1 >> a_Command.m_Position >> a_Command.m_Sender; - a_Command.m_Type = td::EntityType(union1 >> 3); - a_Command.m_Level = union1 & 0x7; -} - - - - - -void Serializer::SerializeCommandData(const cdata::TeamChange& a_Command) { - m_Buffer << static_cast(a_Command.m_Player << 1 | static_cast(a_Command.m_NewTeam)); -} - -void Deserializer::DeserializeCommandData(cdata::TeamChange& a_Command) { - std::uint8_t union1; - m_Buffer >> union1; - a_Command.m_Player = union1 >> 1; - a_Command.m_NewTeam = td::Team(union1 & 1); -} - - - - - -void Serializer::SerializeCommandData(const cdata::UpgradeTower& a_Command) { - m_Buffer << static_cast(a_Command.m_Tower << 4 | a_Command.m_Upgrade & 0xF); -} - -void Deserializer::DeserializeCommandData(cdata::UpgradeTower& a_Command) { - std::uint16_t union1; - m_Buffer >> union1; - a_Command.m_Tower = union1 >> 4; - a_Command.m_Upgrade = union1 & 0xF; -} - - - - - -void Serializer::SerializeCommandData(const cdata::UseItem& a_Command) { - m_Buffer << static_cast(static_cast(a_Command.m_Item) << 4 | a_Command.m_User & 0xF) - << a_Command.m_Position; -} - -void Deserializer::DeserializeCommandData(cdata::UseItem& a_Command) { - std::uint8_t union1; - m_Buffer >> union1 >> a_Command.m_Position; - a_Command.m_Item = td::ShopItem(union1 >> 4); - a_Command.m_User = union1 & 0xF; -} - - - - -} // namespace CommandSerializer -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/command/CommandVisitor.cpp b/src/td/protocol/command/CommandVisitor.cpp deleted file mode 100644 index a018309..0000000 --- a/src/td/protocol/command/CommandVisitor.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace td { -namespace protocol { - -void CommandVisitor::Check(const Command& a_Command) { - a_Command.Accept(*this); -} - -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/command/Commands.cpp b/src/td/protocol/command/Commands.cpp deleted file mode 100644 index 09f575e..0000000 --- a/src/td/protocol/command/Commands.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#define TD_INSTANCIATE_COMMANDS -#include - -#include - -namespace td { -namespace protocol { - -template -commands::ConcreteCommand::ConcreteCommand(const CommandDataType& a_Data) : m_Data(a_Data) {} - -template -void commands::ConcreteCommand::Accept(CommandVisitor& a_Visitor) const { - a_Visitor.Visit(*this); -} - -} // namespace protocol -} // namespace td \ No newline at end of file diff --git a/src/td/protocol/packet/ChunkSerialize.cpp b/src/td/protocol/packet/ChunkSerialize.cpp new file mode 100644 index 0000000..28d20d7 --- /dev/null +++ b/src/td/protocol/packet/ChunkSerialize.cpp @@ -0,0 +1,9 @@ +#include + +namespace td { +namespace game { + + + +} // namespace game +} // namespace td \ No newline at end of file diff --git a/src/td/protocol/packet/PacketFactory.cpp b/src/td/protocol/packet/PacketFactory.cpp deleted file mode 100644 index d1aa219..0000000 --- a/src/td/protocol/packet/PacketFactory.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include - -#include -#include -#include - -namespace td { -namespace protocol { -namespace PacketFactory { - -using PacketCreator = std::function()>; - -#define DeclarePacket(PacketName, ...) std::make_unique(), - -static std::array, static_cast(PacketType::PACKET_COUNT)> Packets = { - DeclareAllPacket() -}; - -const std::unique_ptr& CreateReadOnlyPacket(PacketType a_Type) { - assert(a_Type < PacketType::PACKET_COUNT); - return Packets[static_cast(a_Type)]; -} - -} // namespace PacketFactory -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/packet/PacketSerialize.cpp b/src/td/protocol/packet/PacketSerialize.cpp new file mode 100644 index 0000000..b119ca6 --- /dev/null +++ b/src/td/protocol/packet/PacketSerialize.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include + +namespace td { +namespace game { + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const TeamCastle& a_Castle) { + return a_Buffer << a_Castle.GetCenterX() << a_Castle.GetCenterY(); +} + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, TeamCastle& a_Castle) { + float x, y; + a_Buffer >> x >> y; + a_Castle.SetCenter({x, y}); + return a_Buffer; +} + +sp::DataBuffer& operator<<(sp::DataBuffer& a_Buffer, const Spawn& a_Spawn) { + return a_Buffer << a_Spawn.GetCenterX() << a_Spawn.GetCenterY(); +} + +sp::DataBuffer& operator>>(sp::DataBuffer& a_Buffer, Spawn& a_Spawn) { + float x, y; + a_Buffer >> x >> y; + a_Spawn.SetCenter({x, y}); + return a_Buffer; +} + + +} // namespace game +} // namespace td diff --git a/src/td/protocol/packet/PacketSerializer.cpp b/src/td/protocol/packet/PacketSerializer.cpp deleted file mode 100644 index 0fc785a..0000000 --- a/src/td/protocol/packet/PacketSerializer.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include - -#include -#include -#include - -#include -#include - -namespace td { -namespace protocol { -namespace PacketSerializer { - -#define DeclarePacket(PacketName, ...) \ - void Visit(const packets::PacketName& a_Packet) override { \ - const auto& packetData = a_Packet.m_Data; \ - SerializePacketData(packetData); \ - } \ - \ - void SerializePacketData(const packets::PacketName::PacketDataType& a_Packet); - - - - -class Serializer : public PacketVisitor { - private: - DataBuffer& m_Buffer; - - public: - Serializer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer) {} - - void Serialize(const Packet& a_Packet) { - m_Buffer << static_cast(a_Packet.GetType()); - Check(a_Packet); - } - - DeclareAllPacket() -}; - -#undef DeclarePacket - - - - - -#define DeclarePacket(PacketName, ...) \ - void Visit(const packets::PacketName& a_Packet) override { \ - auto packetPtr = PacketFactory::CreatePacket(); \ - auto& packetData = packetPtr->m_Data; \ - \ - DeserializePacketData(packetData); \ - \ - m_Packet = std::move(packetPtr); \ - } \ - \ - void DeserializePacketData(packets::PacketName::PacketDataType& a_Packet); - - - -class Deserializer : public PacketVisitor { - private: - DataBuffer& m_Buffer; - PacketPtr m_Packet; - - public: - Deserializer(DataBuffer& a_Buffer) : m_Buffer(a_Buffer) {} - - bool Deserialize(const PacketPtr& a_Packet) { - try { - Check(*a_Packet.get()); - } catch (std::exception& e) { - utils::LOGE(utils::Format("[PacketSerializer::Deserializer] %s", e.what())); - return false; - } - return true; - } - - PacketPtr& GetPacket() { - return m_Packet; - } - - DeclareAllPacket() -}; - - - - - -DataBuffer Serialize(const Packet& a_Packet) { - DataBuffer buffer; - - Serializer serializer(buffer); - serializer.Serialize(a_Packet); - - return buffer; -} - -std::unique_ptr Deserialize(DataBuffer& a_Data) { - PacketID packetId; - a_Data >> packetId; - - if (packetId >= static_cast(PacketType::PACKET_COUNT)) - return nullptr; - - PacketType packetType = PacketType(packetId); - - // for double-dispatch - const PacketPtr& emptyPacket = PacketFactory::CreateReadOnlyPacket(packetType); - - Deserializer deserializer(a_Data); - - if (deserializer.Deserialize(emptyPacket)) { - PacketPtr packet = std::move(deserializer.GetPacket()); - return packet; - } - - return nullptr; -} - - - - - -//--------------------------------------------- -// Packet serializer implementation -//---------------------------------------------- - - - - - -void Serializer::SerializePacketData(const pdata::PlayerLogin& a_Packet) { - m_Buffer << a_Packet.m_PlayerName; -} - -void Deserializer::DeserializePacketData(pdata::PlayerLogin& a_Packet) { - m_Buffer >> a_Packet.m_PlayerName; -} - - - - - -void Serializer::SerializePacketData(const pdata::LoggingSuccess& a_Packet) { - m_Buffer << a_Packet.m_PlayerId; -} - -void Deserializer::DeserializePacketData(pdata::LoggingSuccess& a_Packet) { - m_Buffer >> a_Packet.m_PlayerId; -} - - - - - -void Serializer::SerializePacketData(const pdata::PlayerJoin& a_Packet) { - m_Buffer << a_Packet.m_Player; -} - -void Deserializer::DeserializePacketData(pdata::PlayerJoin& a_Packet) { - m_Buffer >> a_Packet.m_Player; -} - - - - -void Serializer::SerializePacketData(const pdata::PlayerLeave& a_Packet) { - m_Buffer << a_Packet.m_PlayerId; -} - -void Deserializer::DeserializePacketData(pdata::PlayerLeave& a_Packet) { - m_Buffer >> a_Packet.m_PlayerId; -} - - - - - -void Serializer::SerializePacketData(const pdata::KeepAlive& a_Packet) { - m_Buffer << a_Packet.m_KeepAliveId; -} - -void Deserializer::DeserializePacketData(pdata::KeepAlive& a_Packet) { - m_Buffer >> a_Packet.m_KeepAliveId; -} - - - - - -void Serializer::SerializePacketData(const pdata::Disconnect& a_Packet) { - m_Buffer << a_Packet.m_Reason; -} - -void Deserializer::DeserializePacketData(pdata::Disconnect& a_Packet) { - m_Buffer >> a_Packet.m_Reason; -} - - - - - -void Serializer::SerializePacketData(const pdata::ChatMessage& a_Packet) { - m_Buffer << a_Packet.m_Text; -} - -void Deserializer::DeserializePacketData(pdata::ChatMessage& a_Packet) { - m_Buffer >> a_Packet.m_Text; -} - - - - - -void Serializer::SerializePacketData(const pdata::LockSteps& a_Packet) { - m_Buffer << a_Packet.m_FirstFrameNumber; - for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { - auto& lockStep = a_Packet.m_LockSteps[i]; - m_Buffer << VarInt{lockStep.size()}; - - for (int j = 0; j < lockStep.size(); i++) { - auto command = lockStep[j]; - m_Buffer << CommandSerializer::Serialize(*command); - } - } -} - -void Deserializer::DeserializePacketData(pdata::LockSteps& a_Packet) { - m_Buffer >> a_Packet.m_FirstFrameNumber; - for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { - VarInt lockStepSize; - m_Buffer >> lockStepSize; - - for (std::size_t j = 0; j < lockStepSize.GetValue(); i++) { - auto command = CommandSerializer::Deserialize(m_Buffer); - if (command) { - a_Packet.m_LockSteps[i].push_back(command); - } else { - throw std::runtime_error("Failed to deserialise command"); - } - } - } -} - - - - - -void Serializer::SerializePacketData(const pdata::BeginGame& a_Packet) { - m_Buffer << a_Packet.m_Map << a_Packet.m_BlueTeam << a_Packet.m_RedTeam << a_Packet.m_Map; -} - -void Deserializer::DeserializePacketData(pdata::BeginGame& a_Packet) { - m_Buffer >> a_Packet.m_Map >> a_Packet.m_BlueTeam >> a_Packet.m_RedTeam >> a_Packet.m_Map; -} - - - - -} // namespace PacketSerializer -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/packet/PacketVisitor.cpp b/src/td/protocol/packet/PacketVisitor.cpp deleted file mode 100644 index 940046c..0000000 --- a/src/td/protocol/packet/PacketVisitor.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace td { -namespace protocol { - -void PacketVisitor::Check(const Packet& a_Packet) { - a_Packet.Accept(*this); -} - -} // namespace protocol -} // namespace td diff --git a/src/td/protocol/packet/Packets.cpp b/src/td/protocol/packet/Packets.cpp deleted file mode 100644 index 1fbc535..0000000 --- a/src/td/protocol/packet/Packets.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#define TD_INSTANCIATE_PACKETS -#include - -#include - -namespace td { -namespace protocol { - -template -packets::ConcretePacket::ConcretePacket(const PacketDataType& a_Data) : m_Data(a_Data) {} - -template -void packets::ConcretePacket::Accept(PacketVisitor& a_Visitor) const { - a_Visitor.Visit(*this); -} - -#define DeclarePacket(PacketName, packetSendType, packetSenderType) \ - static_assert(static_cast(PacketSendType::packetSendType) && static_cast(PacketSenderType::packetSenderType)); - -DeclareAllPacket() - -} // namespace protocol -} // namespace td diff --git a/src/td/render/Camera.cpp b/src/td/render/Camera.cpp new file mode 100644 index 0000000..260a467 --- /dev/null +++ b/src/td/render/Camera.cpp @@ -0,0 +1,28 @@ +#include + +#include + +namespace td { +namespace render { + +void Camera::UpdatePerspective(float a_AspectRatio) { + m_ProjectionMatrix = maths::Perspective(80.0f / 180.0f * PI, a_AspectRatio, 0.1f, 160.0f); + m_InvProjectionMatrix = maths::Inverse(m_ProjectionMatrix); + OnPerspectiveChange(); +} + +void Camera::SetCamPos(const Vec3f& a_NewPos) { + Vec3f front = { + std::cos(m_Yaw) * std::cos(m_Pitch), + std::sin(m_Pitch), + std::sin(m_Yaw) * std::cos(m_Pitch) + }; + + m_CamPos = a_NewPos; + m_ViewMatrix = maths::Look(m_CamPos, front, { 0, 1, 0 }); + m_InvViewMatrix = maths::Transpose(maths::Inverse(m_ViewMatrix)); // why transpose ? I don't know + OnViewChange(); +} + +} // namespace render +} // namespace td diff --git a/src/td/render/Renderer.cpp b/src/td/render/Renderer.cpp new file mode 100644 index 0000000..cc5a032 --- /dev/null +++ b/src/td/render/Renderer.cpp @@ -0,0 +1,26 @@ +#include + +#include + +namespace td { +namespace render { + +void BasicRenderer::Render(const GL::VertexArray& a_Vao) { + a_Vao.Bind(); + glDrawArrays(GL_TRIANGLES, 0, a_Vao.GetVertexCount()); + // glDrawElements(GL_TRIANGLES, a_Vao.GetVertexCount(), GL_UNSIGNED_INT, nullptr); + a_Vao.Unbind(); +} + +RenderPipeline::RenderPipeline() { + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthFunc(GL_LESS); + glFrontFace(GL_CCW); +} + +} // namespace render +} // namespace td diff --git a/src/td/render/loader/GLLoader.cpp b/src/td/render/loader/GLLoader.cpp new file mode 100644 index 0000000..48b388d --- /dev/null +++ b/src/td/render/loader/GLLoader.cpp @@ -0,0 +1,99 @@ +#include + +#include + +namespace td { +namespace GL { + +VertexArray::~VertexArray() { + if (m_ID != 0) + glDeleteVertexArrays(1, &m_ID); +} + +VertexArray::VertexArray(ElementBuffer&& indicies) : m_ElementBuffer(std::move(indicies)) { + glGenVertexArrays(1, &m_ID); + Bind(); + BindElementArrayBuffer(); + // Unbind(); +} + +void VertexArray::Bind() const { + glBindVertexArray(m_ID); +} + +void VertexArray::Unbind() const { + glBindVertexArray(0); +} + +void VertexArray::BindVertexBuffer(VertexBuffer&& VertexBuffer) { + VertexBuffer.Bind(); + VertexBuffer.BindVertexAttribs(); + m_VertexBuffers.push_back(std::move(VertexBuffer)); +} + +void VertexArray::BindElementArrayBuffer() { + m_ElementBuffer.Bind(); +} + +VertexBuffer::~VertexBuffer() { + if (m_ID != 0) + glDeleteBuffers(1, &m_ID); +} + +VertexBuffer::VertexBuffer(const std::vector& data, unsigned int stride) : m_DataStride(stride) { + glGenBuffers(1, &m_ID); + Bind(); + glBufferData(GL_ARRAY_BUFFER, static_cast(data.size() * sizeof(float)), nullptr, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, static_cast(data.size() * sizeof(float)), data.data()); + Unbind(); +} + +void VertexBuffer::Bind() const { + glBindBuffer(GL_ARRAY_BUFFER, m_ID); +} + +void VertexBuffer::Unbind() const { + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void VertexBuffer::AddVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset) { + VertexAttribPointer pointer { + .m_Index = index, + .m_Size = coordinateSize, + .m_Offset = offset + }; + m_VertexAttribs.push_back(pointer); +} + +void VertexBuffer::BindVertexAttribs() const { + for (const VertexAttribPointer& pointer : m_VertexAttribs) { + glVertexAttribPointer(pointer.m_Index, static_cast(pointer.m_Size), GL_FLOAT, false, m_DataStride * sizeof(float), + reinterpret_cast(static_cast(pointer.m_Offset))); + glEnableVertexAttribArray(pointer.m_Index); + } +} + +ElementBuffer::ElementBuffer(const std::vector& indicies) { + m_TriangleCount = indicies.size(); + glGenBuffers(1, &m_ID); + Bind(); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast(indicies.size() * sizeof(unsigned int)), nullptr, GL_STATIC_DRAW); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, static_cast(indicies.size() * sizeof(unsigned int)), indicies.data()); + Unbind(); +} + +ElementBuffer::~ElementBuffer() { + if (m_ID != 0) + glDeleteBuffers(1, &m_ID); +} + +void ElementBuffer::Bind() const { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ID); +} + +void ElementBuffer::Unbind() const { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +} // namespace GL +} // namespace td \ No newline at end of file diff --git a/src/td/render/loader/WorldLoader.cpp b/src/td/render/loader/WorldLoader.cpp new file mode 100644 index 0000000..fa478ce --- /dev/null +++ b/src/td/render/loader/WorldLoader.cpp @@ -0,0 +1,245 @@ +#include + +#include +#include + +#include + +namespace td { +namespace render { + +namespace WorldLoader { + +const static int POSITION_VERTEX_SIZE = 3; +// const static int TEXTURE_VERTEX_SIZE = 2; + +GL::VertexArray LoadWorldModel(const td::game::World* world) { + std::vector positions; + std::vector colors; + + for (const auto& [coords, chunk] : world->GetChunks()) { + std::int32_t chunkX = coords.x * td::game::Chunk::ChunkWidth; + std::int32_t chunkY = coords.y * td::game::Chunk::ChunkHeight; + + for (int tileY = 0; tileY < td::game::Chunk::ChunkHeight; tileY++) { + for (int tileX = 0; tileX < td::game::Chunk::ChunkWidth; tileX++) { + int tileNumber = tileY * td::game::Chunk::ChunkWidth + tileX; + td::game::TileIndex tileIndex = chunk->GetTileIndex(tileNumber); + td::game::TilePtr tile = world->GetTilePtr(tileIndex); + + if (!tile) + continue; + + positions.insert( + positions.end(), {static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY + 1), + + static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY), + static_cast(chunkX + tileX), 0, static_cast(chunkY + tileY + 1), + static_cast(chunkX + tileX + 1), 0, static_cast(chunkY + tileY + 1)}); + + const td::Color* tileColor = world->GetTileColor(tile); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= tileColor->r << 24; + color |= tileColor->g << 16; + color |= tileColor->b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + } + } + + for (int spawnColor = 0; spawnColor < 2; spawnColor++) { + const game::Spawn& spawn = world->GetTeam(TeamColor(spawnColor)).GetSpawn(); + float fromX = spawn.GetTopLeft().GetX(), toX = spawn.GetBottomRight().GetX(); + float fromY = spawn.GetTopLeft().GetY(), toY = spawn.GetBottomRight().GetY(); + + positions.insert(positions.end(), {fromX, 0, fromY, fromX, 0, toY, toX, 0, fromY, fromX, 0, toY, toX, 0, toY, toX, 0, fromY}); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= world->GetSpawnColor(TeamColor(spawnColor)).r << 24; + color |= world->GetSpawnColor(TeamColor(spawnColor)).g << 16; + color |= world->GetSpawnColor(TeamColor(spawnColor)).b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + + for (int castleColor = 0; castleColor < 2; castleColor++) { + const game::TeamCastle& castle = world->GetTeam(TeamColor(castleColor)).GetCastle(); + float fromX = castle.GetTopLeft().GetX(), toX = castle.GetBottomRight().GetX(); + float fromY = castle.GetTopLeft().GetY(), toY = castle.GetBottomRight().GetY(); + + positions.insert(positions.end(), {fromX, 0, fromY, fromX, 0, toY, toX, 0, fromY, fromX, 0, toY, toX, 0, toY, toX, 0, fromY}); + + for (int i = 0; i < 6; i++) { + int color = 255; + color |= world->GetSpawnColor(TeamColor(castleColor)).r << 24; + color |= world->GetSpawnColor(TeamColor(castleColor)).g << 16; + color |= world->GetSpawnColor(TeamColor(castleColor)).b << 8; + + int newColorIndex = colors.size(); + colors.push_back(0); + + memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int)); + } + } + + GL::VertexBuffer positionVBO(positions, POSITION_VERTEX_SIZE); + positionVBO.AddVertexAttribPointer(0, POSITION_VERTEX_SIZE, 0); + GL::VertexBuffer colorVBO(colors, 1); + colorVBO.AddVertexAttribPointer(1, 1, 0); + + std::vector indexes(positions.size() / 3, 0); + for (size_t i = 0; i < indexes.size(); i++) { + indexes[i] = i + 1; + } + + GL::ElementBuffer indexVBO(indexes); + + GL::VertexArray worldVao(std::move(indexVBO)); // each pos = 3 vertecies + worldVao.Bind(); + worldVao.BindVertexBuffer(std::move(positionVBO)); + worldVao.BindVertexBuffer(std::move(colorVBO)); + worldVao.Unbind(); + return worldVao; +} + +GL::VertexArray LoadTileSelectModel() { + std::vector positions = { + -0.5f, + -0.5f, + -1.0f, + + 0.5f, + -0.5f, + -1.0f, + + 0.0f, + 0.5f, + -1.0f, + + 1, + .01, + 1, + + 0, + .01, + 1, + + 0, + 1, + 1, + }; + + int color = 255 << 24 | 255 << 16 | 255 << 8 | 150; + float colorFloat; + + memcpy(reinterpret_cast(&colorFloat), &color, sizeof(float)); + + std::vector colors(6, colorFloat); + + GL::VertexBuffer positionVBO(positions, POSITION_VERTEX_SIZE); + positionVBO.AddVertexAttribPointer(0, POSITION_VERTEX_SIZE, 0); + GL::VertexBuffer colorVBO(colors, 1); + colorVBO.AddVertexAttribPointer(1, 1, 0); + + std::vector indexes(positions.size() / 3, 0); + // for (size_t i = 0; i < indexes.size(); i++) { + // indexes[i] = i + 1; + // } + GL::ElementBuffer indexVBO(indexes); + + GL::VertexArray tileSelectVao(std::move(indexVBO)); + tileSelectVao.Bind(); + tileSelectVao.BindVertexBuffer(std::move(positionVBO)); + tileSelectVao.BindVertexBuffer(std::move(colorVBO)); + tileSelectVao.Unbind(); + + return tileSelectVao; +} + +RenderData LoadTowerModel(const game::TowerPtr& tower) { + RenderData renderData; + + float towerX, towerDX; + float towerY, towerDY; + + if (tower->GetSize() == game::TowerSize::Little) { + towerX = tower->GetCenterX() - 1.5f; + towerDX = tower->GetCenterX() + 1.5f; + + towerY = tower->GetCenterY() - 1.5f; + towerDY = tower->GetCenterY() + 1.5f; + } else { + towerX = tower->GetCenterX() - 2.5f; + towerDX = tower->GetCenterX() + 2.5f; + + towerY = tower->GetCenterY() - 2.5f; + towerDY = tower->GetCenterY() + 2.5f; + } + std::vector positions = {towerDX, 0.001, towerY, towerX, 0.001, towerY, towerX, 0.001, towerDY, towerDX, 0.001, towerY, + towerX, 0.001, towerDY, towerDX, 0.001, towerDY}; + + renderData.positions = positions; + + std::uint8_t towerType = static_cast(tower->GetType()); + std::uint8_t r = 10 * towerType + 40, g = 5 * towerType + 30, b = 10 * towerType + 20; + + float colorFloat; + int color = r << 24 | g << 16 | b << 8 | 255; + memcpy(&colorFloat, &color, sizeof(int)); + + std::vector colors(6, colorFloat); + renderData.colors = colors; + + return renderData; +} + + + +GL::VertexArray LoadMobModel() { + std::vector positions = { + -0.5, 0, -0.5, + -0.5, 0, 0.5, + 0.5, 0, -0.5, + + 0.5, 0, -0.5, + -0.5, 0, 0.5, + 0.5, 0, 0.5 + }; + + std::vector indexes(positions.size() / 3, 0); + // for (size_t i = 0; i < indexes.size(); i++) { + // indexes[i] = i + 1; + // } + GL::ElementBuffer indexVBO(indexes); + + GL::VertexBuffer positionVBO(positions, POSITION_VERTEX_SIZE); + positionVBO.AddVertexAttribPointer(0, POSITION_VERTEX_SIZE, 0); + + GL::VertexArray mobVao(std::move(indexVBO)); // each pos = 1 color + mobVao.Bind(); + mobVao.BindVertexBuffer(std::move(positionVBO)); + mobVao.Unbind(); + return mobVao; +} + + + +} // namespace WorldLoader + + +} // namespace render +} // namespace td diff --git a/src/td/render/renderer/EntityRenderer.cpp b/src/td/render/renderer/EntityRenderer.cpp new file mode 100644 index 0000000..9b5d189 --- /dev/null +++ b/src/td/render/renderer/EntityRenderer.cpp @@ -0,0 +1,31 @@ +#include + +#include + +namespace td { +namespace render { + +EntityRenderer::EntityRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { + m_EntityVao = std::make_unique(WorldLoader::LoadMobModel()); + m_Shader->Start(); + m_Shader->SetColorEffect({1, 0, 1}); +} + +EntityRenderer::~EntityRenderer() {} + + + +void EntityRenderer::Render(float a_Lerp) { + m_Shader->Start(); + for (const auto& mob : m_World.GetMobList()) { + + float x = Lerp(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast(a_Mob.m_Position.x); }); + float z = Lerp(*mob, a_Lerp, [](const game::Mob& a_Mob) { return static_cast(a_Mob.m_Position.y); }); + + m_Shader->SetModelPos({x, 1, z}); + Renderer::Render(*m_EntityVao); + } +} + +} // namespace render +} // namespace td diff --git a/src/td/render/renderer/TowerRenderer.cpp b/src/td/render/renderer/TowerRenderer.cpp new file mode 100644 index 0000000..b9ad89c --- /dev/null +++ b/src/td/render/renderer/TowerRenderer.cpp @@ -0,0 +1,27 @@ +#include + +#include + +namespace td { +namespace render { + +TowerRenderer::TowerRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera), m_World(a_World) { + m_EntityVao = std::make_unique(WorldLoader::LoadMobModel()); + m_Shader->Start(); + m_Shader->SetColorEffect({0, 0, 1}); +} + +TowerRenderer::~TowerRenderer() {} + + + +void TowerRenderer::Render(float a_Lerp) { + m_Shader->Start(); + for (const auto& tower : m_World.GetTowers()) { + m_Shader->SetModelPos({tower->GetCenterX(), 1, tower->GetCenterY()}); + Renderer::Render(*m_EntityVao); + } +} + +} // namespace render +} // namespace td diff --git a/src/td/render/renderer/WorldRenderer.cpp b/src/td/render/renderer/WorldRenderer.cpp new file mode 100644 index 0000000..078dc5d --- /dev/null +++ b/src/td/render/renderer/WorldRenderer.cpp @@ -0,0 +1,23 @@ +#include + +#include + +#include + +namespace td { +namespace render { + +WorldRenderer::WorldRenderer(Camera& a_Camera, const game::World& a_World) : Renderer(a_Camera) { + m_WorldVao = std::make_unique(WorldLoader::LoadWorldModel(&a_World)); +} + +WorldRenderer::~WorldRenderer() {} + +void WorldRenderer::Render(float a_Lerp) { + m_Shader->Start(); + Renderer::Render(*m_WorldVao); + ImGui::ShowDemoWindow(); +} + +} // namespace render +} // namespace td diff --git a/src/td/render/shader/CameraShaderProgram.cpp b/src/td/render/shader/CameraShaderProgram.cpp new file mode 100644 index 0000000..a54aeed --- /dev/null +++ b/src/td/render/shader/CameraShaderProgram.cpp @@ -0,0 +1,21 @@ +#include + +namespace td { +namespace shader { + +void CameraShaderProgram::SetProjectionMatrix(const Mat4f& proj) const { + LoadMat4(m_LocationProjection, proj); +} + +void CameraShaderProgram::SetViewMatrix(const Mat4f& view) const { + LoadMat4(m_LocationView, view); +} + +void CameraShaderProgram::GetAllUniformLocation() { + m_LocationProjection = static_cast(GetUniformLocation("projectionMatrix")); + m_LocationView = static_cast(GetUniformLocation("viewMatrix")); +} + + +} // namespace shader +} // namespace td diff --git a/src/td/render/shader/EntityShader.cpp b/src/td/render/shader/EntityShader.cpp new file mode 100644 index 0000000..3080487 --- /dev/null +++ b/src/td/render/shader/EntityShader.cpp @@ -0,0 +1,106 @@ +#include + +namespace td { +namespace shader { + +// TODO: update ES shaders + +#ifdef __ANDROID__ +static const char vertexSource[] = +R"(#version 300 es + +precision mediump float; + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; +uniform vec3 modelPosition; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position + modelPosition, 1.0); +} +)"; + +static const char fragmentSource[] = +R"(#version 300 es + +precision mediump float; + +flat in int pass_color; + +out vec4 out_color; + +uniform vec3 ColorEffect; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + vec3 intermediate_color = vec3(r, g, b) * ColorEffect; + out_color = vec4(intermediate_color, a); + +} +)"; +#else +static const char vertexSource[] = R"( +#version 330 + +layout(location = 0) in vec3 position; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; +uniform vec3 modelPosition; + +void main(void){ + gl_Position = projectionMatrix * viewMatrix * vec4(position + modelPosition, 1.0); +} +)"; + +static const char fragmentSource[] = R"( +#version 330 + +out vec4 out_color; + +uniform vec3 ColorEffect; + +void main(void){ + + vec4 color = vec4(ColorEffect, 1.0); + + if (color.a <= 0.1) + discard; + + out_color = color; + +} +)"; +#endif + +EntityShader::EntityShader() : CameraShaderProgram() { + ShaderProgram::LoadProgram(vertexSource, fragmentSource); +} + + +void EntityShader::GetAllUniformLocation() { + CameraShaderProgram::GetAllUniformLocation(); + m_LocationColorEffect = static_cast(GetUniformLocation("ColorEffect")); + m_LocationPosition = static_cast(GetUniformLocation("modelPosition")); +} + +void EntityShader::SetColorEffect(const Vec3f& color) { + LoadVector(m_LocationColorEffect, color); +} + +void EntityShader::SetModelPos(const Vec3f& pos) const { + LoadVector(m_LocationPosition, pos); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/src/td/render/shader/ShaderProgram.cpp b/src/td/render/shader/ShaderProgram.cpp new file mode 100755 index 0000000..9406bb5 --- /dev/null +++ b/src/td/render/shader/ShaderProgram.cpp @@ -0,0 +1,145 @@ +/* + * ShaderProgram.cpp + * + * Created on: 31 janv. 2020 + * Author: simon + */ + +#include +#include +#include + +#include +#include +#include +#include + +namespace td { +namespace shader { + +ShaderProgram::ShaderProgram() : + m_ProgramID(0), m_VertexShaderID(0), m_FragmentShaderID(0) { +} + +ShaderProgram::~ShaderProgram() { + CleanUp(); +} + +void ShaderProgram::Start() const { + glUseProgram(m_ProgramID); +} + +void ShaderProgram::Stop() const { + glUseProgram(0); +} + +int ShaderProgram::GetUniformLocation(const std::string& uniformName) const { + const int location = glGetUniformLocation(m_ProgramID, uniformName.c_str()); + if (location == -1) { + utils::LOGD(utils::Format("Warning ! Uniform variable %s not found !", uniformName.c_str())); + } + return location; +} + +void ShaderProgram::LoadFloat(unsigned int location, float value) const { + glUniform1f(static_cast(location), value); +} + +void ShaderProgram::LoadInt(unsigned int location, int value) const { + glUniform1i(static_cast(location), value); +} + +void ShaderProgram::LoadVector(unsigned int location, + const Vec2f& vector) const { + glUniform2f(static_cast(location), vector.x, vector.y); +} + +void ShaderProgram::LoadVector(unsigned int location, + const Vec3f& vector) const { + glUniform3f(static_cast(location), vector.x, vector.y, vector.z); +} + +void ShaderProgram::LoadBoolean(unsigned int location, bool value) const { + glUniform1i(static_cast(location), value); +} + +void ShaderProgram::LoadMat4(unsigned int location, const Mat4f& mat) const { + glUniformMatrix4fv(static_cast(location), 1, false, reinterpret_cast(&mat)); +} + +void ShaderProgram::CleanUp() const { + Stop(); + glDetachShader(m_ProgramID, m_VertexShaderID); + glDetachShader(m_ProgramID, m_FragmentShaderID); + glDeleteShader(m_VertexShaderID); + glDeleteShader(m_FragmentShaderID); + glDeleteProgram(m_ProgramID); +} + +void ShaderProgram::LoadProgramFile(const std::string& vertexFile, + const std::string& fragmentFile) { + m_VertexShaderID = static_cast(LoadShaderFromFile(vertexFile, GL_VERTEX_SHADER)); + m_FragmentShaderID = static_cast(LoadShaderFromFile(fragmentFile, GL_FRAGMENT_SHADER)); + m_ProgramID = glCreateProgram(); + glAttachShader(m_ProgramID, m_VertexShaderID); + glAttachShader(m_ProgramID, m_FragmentShaderID); + glLinkProgram(m_ProgramID); + glValidateProgram(m_ProgramID); + GetAllUniformLocation(); +} + +void ShaderProgram::LoadProgram(const std::string& vertexSource, + const std::string& fragmentSource) { + m_VertexShaderID = static_cast(LoadShader(vertexSource, GL_VERTEX_SHADER)); + m_FragmentShaderID = static_cast(LoadShader(fragmentSource, GL_FRAGMENT_SHADER)); + m_ProgramID = glCreateProgram(); + glAttachShader(m_ProgramID, m_VertexShaderID); + glAttachShader(m_ProgramID, m_FragmentShaderID); + glLinkProgram(m_ProgramID); + glValidateProgram(m_ProgramID); + GetAllUniformLocation(); +} + +unsigned int ShaderProgram::LoadShader(const std::string& source, GLenum type) { + unsigned int shaderID = glCreateShader(type); + + const char* c_str = source.c_str(); + int* null = 0; + glShaderSource(shaderID, 1, &c_str, null); // @suppress("Function cannot be resolved") + glCompileShader(shaderID); + GLint compilesuccessful; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compilesuccessful); + if (!compilesuccessful) { + GLsizei size; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &size); + std::vector shaderError(static_cast(size)); + glGetShaderInfoLog(shaderID, size, &size, shaderError.data()); + + utils::LOGE("Could not compile shader !"); + + utils::LOGE(shaderError.data()); + + utils::LOGD(utils::Format("\nShader source : \n" + "------------------------------------------------------------------------------------------------------------------------------------\n" + "%s\n" + "------------------------------------------------------------------------------------------------------------------------------------\n" + , source.c_str())); + } + return shaderID; +} + +unsigned int ShaderProgram::LoadShaderFromFile(const std::string& file, GLenum type) { + std::stringstream stream; + std::ifstream fileStream(file); + + if (fileStream) { + stream << fileStream.rdbuf(); + } else { + return 0; + } + + return LoadShader(stream.str(), type); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/src/td/render/shader/WorldShader.cpp b/src/td/render/shader/WorldShader.cpp new file mode 100644 index 0000000..01d9e64 --- /dev/null +++ b/src/td/render/shader/WorldShader.cpp @@ -0,0 +1,89 @@ +#include + +namespace td { +namespace shader { + +// TODO: GLES Shaders + +#ifdef __ANDROID__ +static const char vertexSource[] = +R"(#version 300 es + +precision mediump float; + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); +} +)"; + +static const char fragmentSource[] = +R"(#version 300 es + +precision mediump float; + +flat in int pass_color; + +out vec4 out_color; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + out_color = vec4(r, g, b, a); + +} +)"; +#else +static const char vertexSource[] = R"( +#version 330 + +layout(location = 0) in vec3 position; +layout(location = 1) in int color; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +flat out int pass_color; + +void main(void){ + pass_color = color; + gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); +} +)"; + +static const char fragmentSource[] = R"( +#version 330 + +flat in int pass_color; + +out vec4 out_color; + +void main(void){ + + float r = float(pass_color >> 24 & 0xFF) / 255.0; + float g = float(pass_color >> 16 & 0xFF) / 255.0; + float b = float(pass_color >> 8 & 0xFF) / 255.0; + float a = float(pass_color & 0xFF) / 255.0; + out_color = vec4(r, g, b, a); + +} +)"; +#endif + +WorldShader::WorldShader() : CameraShaderProgram() { + ShaderProgram::LoadProgram(vertexSource, fragmentSource); +} + +} // namespace shader +} // namespace td \ No newline at end of file diff --git a/src/td/simulation/ClientSimulation.cpp b/src/td/simulation/ClientSimulation.cpp new file mode 100644 index 0000000..7d43c36 --- /dev/null +++ b/src/td/simulation/ClientSimulation.cpp @@ -0,0 +1,95 @@ +#include + +#include + +namespace td { +namespace sim { + +const protocol::LockStep ClientSimulation::EMPTY_LOCKSTEP; + +std::uint64_t GetTime() { + return static_cast( + std::chrono::duration_cast(std::chrono::system_clock().now().time_since_epoch()).count()); +} + +ClientSimulation::ClientSimulation(game::World& a_World, GameHistory&& a_History, std::uint64_t a_StepTime) : + m_StepTime(a_StepTime), + m_World(a_World), + m_CurrentTime(0), + m_LastTime(GetTime()), + m_CurrentStep(0), + m_LastSnapshot(std::make_shared()), + m_LastValidStep(0) { + m_History.reserve(a_History.size()); + for (auto&& lockstep : a_History) { + m_History.emplace_back(std::move(lockstep)); + } + Step(); +} + +ClientSimulation::ClientSimulation(game::World& a_World, std::uint64_t a_StepTime) : + m_StepTime(a_StepTime), + m_World(a_World), + m_History(std::numeric_limits::max()), + m_CurrentTime(0), + m_LastTime(GetTime()), + m_CurrentStep(0), + m_LastSnapshot(std::make_shared()), + m_LastValidStep(0) { + Step(); +} + +float ClientSimulation::Update() { + // TODO: handle freezes (m_CurrentTime > 2 * m_StepTime) + m_CurrentTime += GetTime() - m_LastTime; + m_LastTime = GetTime(); + if (m_CurrentTime > m_StepTime) { + Step(); + m_CurrentTime -= m_StepTime; + } + return (float)m_CurrentTime / (float)m_StepTime; +} + +void ClientSimulation::Step() { + const auto& step = m_History[m_CurrentStep]; + if (step.has_value()) { + m_LastSnapshot = m_World.Tick(step.value(), FpFloat(m_StepTime) / FpFloat(1000)); + m_LastValidStep = m_CurrentStep; + } else { + m_World.Tick(EMPTY_LOCKSTEP, FpFloat(m_StepTime) / FpFloat(1000)); + } + m_CurrentStep++; +} + +void ClientSimulation::Handle(const protocol::packets::LockStepsPacket& a_LockSteps) { + const auto& steps = a_LockSteps->m_LockSteps; + for (std::size_t i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { + m_History[a_LockSteps->m_FirstFrameNumber + i] = steps[i]; + } + FastReplay(); +} + +void ClientSimulation::Handle(const protocol::packets::PredictCommandPacket& a_Predict) { + +} + +void ClientSimulation::FastForward(std::size_t a_Count) { + for (std::size_t i = 0; i < a_Count; i++) { + Step(); + } +} + +void ClientSimulation::FastReplay() { + if (m_LastValidStep >= m_CurrentStep) + return; + + m_World.ResetSnapshots(m_LastSnapshot, m_LastSnapshot); + + const std::size_t stepCount = m_CurrentStep - m_LastValidStep; + m_CurrentStep = m_LastValidStep; + + FastForward(stepCount); +} + +} // namespace sim +} // namespace td diff --git a/src/td/simulation/CommandApply.cpp b/src/td/simulation/CommandApply.cpp new file mode 100644 index 0000000..8e17aef --- /dev/null +++ b/src/td/simulation/CommandApply.cpp @@ -0,0 +1,35 @@ +#include + +namespace td { +namespace sim { + +CommandApply::CommandApply(const game::World& a_World, WorldSnapshot& a_Snapshot) : m_World(a_World), m_Snapshot(a_Snapshot) {} + +void CommandApply::Handle(const protocol::commands::EndCommand& a_End) { + (void) m_World; +} + +void CommandApply::Handle(const protocol::commands::PlaceTowerCommand& a_PlaceTower) { + static game::TowerFactory factory; + + auto tower = std::shared_ptr(factory.CreateMessage(*a_PlaceTower->m_Type).release()); + tower->m_Builder = *a_PlaceTower->m_Placer; + tower->SetCenter(utils::shape::Point(a_PlaceTower->m_Position.x, a_PlaceTower->m_Position.y)); + + m_Snapshot.m_Towers.push_back(tower); +} + +void CommandApply::Handle(const protocol::commands::PlayerJoinCommand& a_PlayerJoin) {} + +void CommandApply::Handle(const protocol::commands::SpawnTroopCommand& a_SpawnTroop) { + auto zombie = std::make_shared(); + zombie->m_Position = a_SpawnTroop->m_Position; + m_Snapshot.m_Mobs.push_back(zombie); +} + +void CommandApply::Handle(const protocol::commands::TeamChangeCommand& a_TeamChange) {} +void CommandApply::Handle(const protocol::commands::UpgradeTowerCommand& a_UpgradeTower) {} +void CommandApply::Handle(const protocol::commands::UseItemCommand& a_UseItem) {} + +} // namespace sim +} // namespace td diff --git a/src/td/game/GameHistory.cpp b/src/td/simulation/GameHistory.cpp similarity index 78% rename from src/td/game/GameHistory.cpp rename to src/td/simulation/GameHistory.cpp index b3c30cf..fd36519 100644 --- a/src/td/game/GameHistory.cpp +++ b/src/td/simulation/GameHistory.cpp @@ -1,4 +1,4 @@ -#include +#include namespace td { namespace game { @@ -17,19 +17,19 @@ bool GameHistory::HasLockStep(HistorySizeType a_Index) const { return m_History[a_Index].has_value(); } -void GameHistory::FromPacket(td::protocol::pdata::LockSteps&& a_Steps) { +void GameHistory::FromPacket(protocol::pdata::LockSteps&& a_Steps) { for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { protocol::LockStep& step = a_Steps.m_LockSteps[i]; SetLockStep(i + a_Steps.m_FirstFrameNumber, std::move(step)); } } -td::protocol::packets::LockSteps GameHistory::ToPacket(HistorySizeType a_StartIndex) { +protocol::packets::LockStepsPacket GameHistory::ToPacket(HistorySizeType a_StartIndex) { std::array steps; for (int i = 0; i < LOCKSTEP_BUFFER_SIZE; i++) { steps[i] = GetLockStep(a_StartIndex + i); } - return {{a_StartIndex, std::move(steps)}}; + return {a_StartIndex, std::move(steps)}; } } // namespace game diff --git a/src/td/simulation/WorldTicker.cpp b/src/td/simulation/WorldTicker.cpp new file mode 100644 index 0000000..88f465d --- /dev/null +++ b/src/td/simulation/WorldTicker.cpp @@ -0,0 +1,53 @@ +#include + +#include +#include + +namespace td { +namespace sim { + +WorldTicker::WorldTicker() { + AddSystem(); +} + +WorldSnapshot WorldTicker::NextStep( + const game::World& a_World, WorldSnapshot& a_PreviousState, const protocol::LockStep& a_LockStep, FpFloat a_Delta) { + WorldSnapshot next = CreateNext(a_PreviousState); + Tick(a_World, next, a_Delta); + ApplySteps(a_World, next, a_LockStep); + return next; +} + +void WorldTicker::ApplySteps(const game::World& a_World, WorldSnapshot& a_State, const protocol::LockStep& a_LockStep) { + CommandApply cmdHandler(a_World, a_State); + for (const auto& cmd : a_LockStep) { + cmd->Dispatch(cmdHandler); + } +} + +void WorldTicker::Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) { + for (const auto& system : m_Systems) { + system->Tick(a_World, a_State, a_Delta); + } +} + +WorldSnapshot WorldTicker::CreateNext(WorldSnapshot& a_PreviousState) { + static game::MobFactory mobFactory; + + WorldSnapshot next { + .m_Towers = a_PreviousState.m_Towers, + .m_Teams = a_PreviousState.m_Teams + }; + next.m_Mobs.reserve(a_PreviousState.m_Mobs.size()); + // creating a "linked list" of mobs between steps + for (auto& mob : a_PreviousState.m_Mobs) { + game::MobPtr newMob = std::shared_ptr(mobFactory.CreateMessage(mob->GetId()).release()); + *newMob = *mob; + mob->m_Next = newMob; + next.m_Mobs.push_back(newMob); + } + return next; +} + +} // namespace sim +} // namespace td diff --git a/src/td/simulation/system/EntityMove.cpp b/src/td/simulation/system/EntityMove.cpp new file mode 100644 index 0000000..798a6f8 --- /dev/null +++ b/src/td/simulation/system/EntityMove.cpp @@ -0,0 +1,13 @@ +#include + +namespace td { +namespace sim { + +void EntityMove::Tick(const game::World& a_World, WorldSnapshot& a_State, FpFloat a_Delta) { + for (auto& mob : a_State.m_Mobs) { + mob->m_Position.x += a_Delta; + } +} + +} // namespace sim +} // namespace td diff --git a/test/blitz/protocol/command/CommandFactory_test.cpp b/test/blitz/protocol/command/CommandFactory_test.cpp deleted file mode 100644 index 9ce58cb..0000000 --- a/test/blitz/protocol/command/CommandFactory_test.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include - -static int Test() { - for (std::size_t i = 0; i < static_cast(td::protocol::CommandType::COMMAND_COUNT); i++) { - td::protocol::CommandType commandType = td::protocol::CommandType(i); - - if (td::protocol::CommandFactory::CreateReadOnlyCommand(commandType)->GetType() != commandType) - return TD_TEST_FAILED; - } - return TD_TEST_SUCCESSFUL; -} - -int main() { - return Test(); -} \ No newline at end of file diff --git a/test/blitz/protocol/command/CommandSerializer_test.cpp b/test/blitz/protocol/command/CommandSerializer_test.cpp deleted file mode 100644 index de8f731..0000000 --- a/test/blitz/protocol/command/CommandSerializer_test.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include - -#include -#include - -namespace tp = td::protocol; - -class Comparator { - public: - bool operator()(const tp::cdata::End& left, const tp::cdata::End& right) { - return true; - } - bool operator()(const tp::cdata::PlaceTower& left, const tp::cdata::PlaceTower& right) { - return left.m_Placer == right.m_Placer && left.m_Position.x == right.m_Position.x && left.m_Position.y == right.m_Position.y && - left.m_Type == right.m_Type; - } - bool operator()(const tp::cdata::PlayerJoin& left, const tp::cdata::PlayerJoin& right) { - return left.m_ID == right.m_ID && left.m_Name == right.m_Name; - } - bool operator()(const tp::cdata::SpawnTroop& left, const tp::cdata::SpawnTroop& right) { - return left.m_Level == right.m_Level && left.m_Position.x == right.m_Position.x && left.m_Position.y == right.m_Position.y && - left.m_Sender == right.m_Sender && left.m_Type == right.m_Type; - } - bool operator()(const tp::cdata::TeamChange& left, const tp::cdata::TeamChange& right) { - return left.m_NewTeam == right.m_NewTeam && left.m_Player == right.m_Player; - } - bool operator()(const tp::cdata::UpgradeTower& left, const tp::cdata::UpgradeTower& right) { - return left.m_Tower == right.m_Tower && left.m_Upgrade == right.m_Upgrade; - } - bool operator()(const tp::cdata::UseItem& left, const tp::cdata::UseItem& right) { - return left.m_Item == right.m_Item && left.m_Position.x == right.m_Position.x && left.m_Position.y == right.m_Position.y && - left.m_User == right.m_User; - } -}; - -template -static int TestCommand(const Command_T& command) { - - td::DataBuffer buffer = tp::CommandSerializer::Serialize(command); - - auto abstractCommand = tp::CommandSerializer::Deserialize(buffer); - - td_test_assert(abstractCommand); - - Command_T* command2 = dynamic_cast(abstractCommand.get()); - - td_test_assert(command2); - td_test_assert(command.GetType() == command2->GetType()); - - return Comparator{}(command.m_Data, command2->m_Data); -} - -#define DeclareCommand(Command, ...) td_test_assert(TestCommand({})); - -static int TestAllCommands() { - td_test_assert(TestCommand({})); - td_test_assert(TestCommand({tp::cdata::PlaceTower{ - td::TowerType::Necromancer, - 8, - td::TowerCoords{-50, 69}, - }})); - td_test_assert(TestCommand({tp::cdata::PlayerJoin{ - 4, - "Persson", - }})); - td_test_assert(TestCommand({tp::cdata::SpawnTroop{ - td::EntityType::Blaze, - 4, - td::EntityCoords{td::FpFloat{54}, td::FpFloat{58}}, - 2, - }})); - td_test_assert(TestCommand({tp::cdata::TeamChange{ - 7, - td::Team::Red, - }})); - td_test_assert(TestCommand({tp::cdata::UpgradeTower{ - 69, - 3, - }})); - td_test_assert(TestCommand({tp::cdata::UseItem{ - td::ShopItem::Freeze, - 5, - td::EntityCoords{td::FpFloat{24}, td::FpFloat{-69}}, - }})); - - DeclareAllCommand() - - return TD_TEST_SUCCESSFUL; -} - -int main() { - return TestAllCommands(); -} \ No newline at end of file diff --git a/test/blitz/protocol/packet/PacketFactory_test.cpp b/test/blitz/protocol/packet/PacketFactory_test.cpp deleted file mode 100644 index 4518e33..0000000 --- a/test/blitz/protocol/packet/PacketFactory_test.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include - -static int Test() { - for (std::size_t i = 0; i < static_cast(td::protocol::PacketType::PACKET_COUNT); i++) { - td::protocol::PacketType packetType = td::protocol::PacketType(i); - - if (td::protocol::PacketFactory::CreateReadOnlyPacket(packetType)->GetType() != packetType) - return TD_TEST_FAILED; - } - return TD_TEST_SUCCESSFUL; -} - -int main() { - return Test(); -} \ No newline at end of file diff --git a/test/blitz/protocol/packet/PacketSerializer_test.cpp b/test/blitz/protocol/packet/PacketSerializer_test.cpp deleted file mode 100644 index 38f4c1c..0000000 --- a/test/blitz/protocol/packet/PacketSerializer_test.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include - -#include -#include - -namespace tp = td::protocol; - -template -static int TestPacket() { - Packet_T packet; - - td::DataBuffer buffer = tp::PacketSerializer::Serialize(packet); - - auto abstractPacket = tp::PacketSerializer::Deserialize(buffer); - - td_test_assert(abstractPacket); - - Packet_T* packet2 = dynamic_cast(abstractPacket.get()); - - td_test_assert(packet2); - td_test_assert(packet.GetType() == packet2->GetType()); - - return std::memcmp(&packet.m_Data, &packet2->m_Data, sizeof(Packet_Data_T)); -} - -#define DeclarePacket(Packet, ...) TestPacket(); - -static int TestAllPackets() { - DeclareAllPacket() - - return TD_TEST_SUCCESSFUL; -} - -int main() { - return TestAllPackets(); -} \ No newline at end of file diff --git a/test/tdmap.tdmap b/test/tdmap.tdmap new file mode 100644 index 0000000..798a860 Binary files /dev/null and b/test/tdmap.tdmap differ diff --git a/test/tdmap.tdmap2 b/test/tdmap.tdmap2 new file mode 100644 index 0000000..cc9a223 Binary files /dev/null and b/test/tdmap.tdmap2 differ diff --git a/xmake.lua b/xmake.lua index 7ad71f9..c4cf095 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,13 +1,26 @@ add_rules("mode.debug", "mode.release") -add_requires("fpm") +add_repositories("persson-repo https://git.ale-pri.com/Persson-dev/xmake-repo.git") + +add_requires("imgui 1.92.0", {configs = {sdl3 = true, opengl3 = true}}) +add_requires("splib 2.3.0", "zlib") +add_requires("libsdl3 3.2.16", "glew", "fpm", "enet6") + set_languages("c++17") +set_warnings("all") + +if is_mode("release") then + set_warnings("all", "error") +end + target("Tower-Defense2") add_includedirs("include", {public = true}) - set_kind("static") + set_kind("binary") add_files("src/**.cpp") - add_packages("fpm", {public = true}) + add_packages("libsdl3", "imgui", "glew", "splib", "zlib", "fpm", "enet6", {public = true}) + set_rundir(".") + add_defines("TD_GL_LOADER_GLEW") @@ -25,72 +38,3 @@ for _, file in ipairs(os.files("test/**.cpp")) do add_tests("compile_and_run") end --- --- If you want to known more usage about xmake, please see https://xmake.io --- --- ## FAQ --- --- You can enter the project directory firstly before building project. --- --- $ cd projectdir --- --- 1. How to build project? --- --- $ xmake --- --- 2. How to configure project? --- --- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release] --- --- 3. Where is the build output directory? --- --- The default output directory is `./build` and you can configure the output directory. --- --- $ xmake f -o outputdir --- $ xmake --- --- 4. How to run and debug target after building project? --- --- $ xmake run [targetname] --- $ xmake run -d [targetname] --- --- 5. How to install target to the system directory or other output directory? --- --- $ xmake install --- $ xmake install -o installdir --- --- 6. Add some frequently-used compilation flags in xmake.lua --- --- @code --- -- add debug and release modes --- add_rules("mode.debug", "mode.release") --- --- -- add macro definition --- add_defines("NDEBUG", "_GNU_SOURCE=1") --- --- -- set warning all as error --- set_warnings("all", "error") --- --- -- set language: c99, c++11 --- set_languages("c99", "c++11") --- --- -- set optimization: none, faster, fastest, smallest --- set_optimize("fastest") --- --- -- add include search directories --- add_includedirs("/usr/include", "/usr/local/include") --- --- -- add link libraries and search directories --- add_links("tbox") --- add_linkdirs("/usr/local/lib", "/usr/lib") --- --- -- add system link libraries --- add_syslinks("z", "pthread") --- --- -- add compilation and link flags --- add_cxflags("-stdnolib", "-fno-strict-aliasing") --- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true}) --- --- @endcode --- -