107 Commits

Author SHA1 Message Date
28b8659e16 fix linux compiling 2023-06-02 16:26:17 +02:00
0b9fc0520e fix compiling mingw 2023-06-02 16:19:42 +02:00
c54017c7be update presence 2023-06-02 13:34:01 +02:00
bbfe341d23 base discord rpc 2023-06-02 13:18:09 +02:00
14efe2cc39 simplified health bar display 2023-01-02 16:58:31 +01:00
fcda12e321 add hit "animation" 2023-01-02 16:37:18 +01:00
1200a6e087 moved TowerUpgradePopop into its own file 2023-01-02 15:56:20 +01:00
512fb23d0e add server mspt (+procotol format) 2023-01-02 15:07:34 +01:00
5a547b6514 move towers implementations 2023-01-02 14:47:02 +01:00
ed45995645 add mob send cooldown 2023-01-02 14:32:37 +01:00
0b6d826eba remove unused glm package 2023-01-02 13:55:54 +01:00
6d0c6be166 server side safe checks 2023-01-02 13:18:49 +01:00
222b79b40a indent with tabs 2023-01-02 13:05:43 +01:00
8f95b1a750 remove format warning 2023-01-02 12:27:08 +01:00
7d30017742 moved Color to Defines.h 2023-01-02 12:21:27 +01:00
386ea5b6ad forward declare Updater 2023-01-02 12:05:20 +01:00
8949c37891 chore: bump version to alpha-0.3.0 2023-01-01 19:42:49 +01:00
019174128c fix: COMPRESSION 2023-01-01 19:40:23 +01:00
4e8b095e31 xmake.lua show wine console output 2022-12-11 13:04:00 +01:00
0e0368cada add wine xmake.lua 2022-12-11 12:59:57 +01:00
Simon Pribylski
6e0923ac75 remove glm dependency 2022-10-13 12:24:15 +02:00
Simon Pribylski
bba9ef8219 Add glm dependency 2022-09-07 18:54:10 +02:00
fd2288ac3c refactor xmake.lua 2022-09-03 16:55:36 +02:00
660952aed3 add example map 2022-07-18 12:12:53 +02:00
243b153309 remove unsed variable 2022-07-18 11:51:52 +02:00
73fa10d8d6 fixing log 2022-07-18 11:50:30 +02:00
0b8a7d8db7 hmmm 2022-07-14 18:42:46 +02:00
e2ec9d6a3b add player getter 2022-07-14 18:40:52 +02:00
eb5b3c8ce2 explicit cast zlib functions 2022-07-14 18:39:28 +02:00
ddbba7399d use of loge 2022-07-14 18:39:06 +02:00
9ce1f4a1a8 add loge 2022-07-14 18:35:11 +02:00
ea4349af4c fix log android 2022-07-14 18:34:26 +02:00
d6fbb58da8 using log calls 2022-07-14 18:32:32 +02:00
66376eaeda allow custom opengl loader 2022-07-14 18:04:13 +02:00
85eb9bbc32 add exp sync 2022-07-14 13:09:38 +02:00
bbb84c0061 add log 2022-07-14 13:08:20 +02:00
2f1161959b remove unused includes 2022-07-14 13:07:41 +02:00
f506307653 refactor databuffer write 2022-07-04 11:17:30 +02:00
40f0d50991 util format function 2022-07-04 11:13:26 +02:00
663ad60ee3 util player methods 2022-07-04 11:13:11 +02:00
3fbb6acac7 fix initial stats 2022-07-04 10:42:31 +02:00
377e98f583 fix warnings 2022-07-04 10:24:18 +02:00
ef9712629f remove use of loops in protocol 2022-06-29 12:42:21 +02:00
ed481de03e add databuffer helper functions 2022-06-29 12:35:36 +02:00
7d667d9c5e add buy packets 2022-06-29 12:13:58 +02:00
16006adc6e fix more warnings 2022-06-27 13:33:24 +02:00
19062b5c77 fix warnings 2022-06-27 13:12:39 +02:00
d3c6ff3988 fix MobSend size 2022-06-27 13:12:20 +02:00
31a0027bfc add namespace comments 2022-06-27 12:29:22 +02:00
d4b1805998 refactor: forgot this one 2022-04-27 19:46:41 +02:00
b84f1fd302 fix: remove useless warning flag 2022-04-27 19:46:09 +02:00
98f73f467d fix: change ChunkCoords hashing 2022-04-27 19:44:16 +02:00
dc8a4ce947 refactor: alpha sort methods 2022-04-27 19:41:03 +02:00
113a831f39 refactor: removed unused semicolon 2022-04-27 19:35:45 +02:00
f906aa1bf0 refactor: more casts 2022-04-27 19:34:45 +02:00
1bbf607b22 refactor: show override 2022-04-27 19:34:12 +02:00
d5b42caf39 refactor: cpp style casts 2022-04-27 19:25:36 +02:00
d3edc0cb6c refactor: spaces instead of tabs 2022-04-27 18:44:22 +02:00
87e4e65843 refactor: remove useless namespace 2022-04-27 18:38:10 +02:00
7509cc1bf2 feat: add player upgrades 2022-04-27 18:37:07 +02:00
6df59b1487 GIGA REFACTOR 2022-03-02 18:51:42 +01:00
553b2f6aad feat: show demo window on debug only 2022-02-17 19:39:07 +01:00
916fa0e7c0 format 2022-02-17 19:35:09 +01:00
bc271bc01e protocol rework + map packet ids removal 2022-02-17 19:35:02 +01:00
97a33e5517 BIG REFACTOR Part 2 2022-02-16 18:34:49 +01:00
bdebabb79e BIG REFACTOR 2022-02-16 17:54:33 +01:00
387cff36ad style: more space 2022-02-16 16:32:05 +01:00
790237a1b5 refactor: changed to PlayerID type 2022-02-16 16:31:18 +01:00
840d5edbe4 refactor: changed exp and money update 2022-02-16 16:18:25 +01:00
36a9e1921c refactor: use of const reference 2022-01-07 18:30:37 +01:00
784c558840 chore: bump version to "alpha-0.2.0" 2021-12-19 18:40:44 +01:00
5d366b6f6c feat: sync game 2021-12-19 18:40:14 +01:00
f70661694b chore: bump version to "alpha-0.1.2" 2021-12-19 11:58:36 +01:00
36a1ab0572 feat: close server when game finished 2021-12-19 11:57:47 +01:00
409268b604 refactor: use of GuiManager 2021-12-19 11:56:41 +01:00
174d144d26 refactor: add default server tps 2021-12-19 11:38:54 +01:00
360258e4cf feat: add end game disconnect 2021-12-13 20:05:20 +01:00
2148c0050c refactor: add game listener 2021-12-13 19:45:01 +01:00
61166023df feat: don't show tile select when tooltip 2021-12-12 16:59:57 +01:00
e7bf22cea6 feat: add pseudo end game 2021-12-12 16:59:35 +01:00
f09f79198d fix: windows build 2021-12-12 16:41:48 +01:00
8c19d3cc3c refactor: removed unused constant 2021-12-12 16:27:59 +01:00
208892d266 chore: update to alpha-0.1.1 2021-12-12 16:17:53 +01:00
4384806cf0 refactor: removed unused comment 2021-12-12 16:14:24 +01:00
1a091baeaf feat: add map background color 2021-12-12 16:13:05 +01:00
43f21ffd44 chore: bump verssion to alpha-0.1.0 2021-12-12 13:33:12 +01:00
24617c539f feat: add castle tooltip 2021-12-12 13:32:39 +01:00
4611a198c9 fix: add mob spawn border 2021-12-12 13:31:01 +01:00
d40ffe8f6c refactor: removed unused include 2021-12-12 11:36:56 +01:00
384b7ad51e chore: bump version to alpha-0.0.5 2021-12-11 20:07:26 +01:00
fd9b448fa4 feat: mob castle damage 2021-12-11 20:06:52 +01:00
c4a2b08416 feat: add mob spawn notification 2021-12-11 19:56:03 +01:00
a241d7691b refactor: mob die event 2021-12-11 19:51:45 +01:00
0af4cd506c refactor: moved mob functions 2021-12-11 19:30:31 +01:00
6b5c56b37d feat: notify when mob touches castle 2021-12-11 18:46:47 +01:00
1474220a77 refactor: separate mob ans world listener 2021-12-11 18:43:49 +01:00
a802b5cef5 fix: windows compilation 2021-12-08 17:24:29 +01:00
7e91b863da bump version to alpha-0.0.4 2021-12-08 15:39:26 +01:00
129bb47286 feat: debug library 2021-12-08 15:38:20 +01:00
e4a9c5f763 feat: remove tower 2021-12-08 15:34:20 +01:00
78cf2d0f18 Merge branch 'master' of 192.168.0.5:repos/Tower-Defense 2021-12-05 12:01:50 +01:00
00d2b5394f refactor: removed unused include 2021-12-05 12:01:17 +01:00
00414abe61 refactor: comment main 2021-12-04 19:09:26 +01:00
cb847d0edd refactor: removed unused declarations 2021-12-04 13:49:50 +01:00
2da0bd9b79 refactor: tower add event processing 2021-12-04 13:44:34 +01:00
cf9633c061 fix: mob spawning repartition 2021-12-03 18:47:55 +01:00
7a6fdc30b6 add fire arrow and fix world events 2021-12-03 18:30:45 +01:00
185 changed files with 27953 additions and 5246 deletions

63
include/Defines.h Normal file
View File

@@ -0,0 +1,63 @@
#pragma once
namespace td {
template<typename T>
struct Vec2 {
union {
T x;
T r;
};
union {
T y;
T g;
};
constexpr Vec2(T X = 0, T Y = 0) : x(X), y(Y) {}
};
template<typename T>
inline bool operator==(const Vec2<T>& vec2, const Vec2<T>& other) {
return vec2.x == other.x && vec2.y == other.y;
}
template<typename T>
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<typename T>
inline bool operator==(const Vec3<T>& vec3, const Vec3<T>& other) {
return vec3.x == other.x && vec3.y == other.y && vec3.z == other.z;
}
using Vec2i = Vec2<int>;
using Vec2u = Vec2<unsigned int>;
using Vec2f = Vec2<float>;
using Vec2d = Vec2<double>;
using Vec3i = Vec3<int>;
using Vec3u = Vec3<unsigned int>;
using Vec3f = Vec3<float>;
using Vec3d = Vec3<double>;
using Color = Vec3<unsigned char>;
} // namespace td

View File

@@ -8,46 +8,62 @@ namespace td {
namespace game { namespace game {
enum class GameState : std::uint8_t { enum class GameState : std::uint8_t {
Lobby, Lobby,
Game, Game,
EndGame, EndGame,
Disconnected, Disconnected,
Closed
}; };
typedef std::map<std::uint8_t, Player> PlayerList; typedef std::map<std::uint8_t, Player> PlayerList;
class Game { class GameListener {
protected:
World* m_World;
std::array<Team, 2> m_Teams = { Team{TeamColor::Red}, Team{TeamColor::Blue} };
GameState m_GameState = GameState::Lobby;
PlayerList m_Players;
public: public:
Game(World* world); virtual void OnPlayerJoin(PlayerID player) {}
virtual ~Game(); virtual void OnPlayerLeave(PlayerID player) {}
virtual void tick(std::uint64_t delta); virtual void OnGameStateUpdate(GameState newState) {}
virtual void OnGameBegin() {}
virtual void OnGameEnd() {}
virtual void OnGameClose() {}
};
Team& getRedTeam() { return m_Teams[(std::uint8_t)TeamColor::Red]; } typedef utils::ObjectNotifier<GameListener> GameNotifier;
const Team& getRedTeam() const { return m_Teams[(std::uint8_t)TeamColor::Red]; }
Team& getBlueTeam() { return m_Teams[(std::uint8_t)TeamColor::Blue]; } class Game : public GameNotifier {
const Team& getBlueTeam() const { return m_Teams[(std::uint8_t)TeamColor::Blue]; } protected:
World* m_World;
TeamList m_Teams = { Team{TeamColor::Red}, Team{TeamColor::Blue} };
GameState m_GameState = GameState::Lobby;
PlayerList m_Players;
public:
Game(World* world);
virtual ~Game();
Team& getTeam(TeamColor team) { return m_Teams[(std::uint8_t)team]; } virtual void Tick(std::uint64_t delta);
const Team& getTeam(TeamColor team) const { return m_Teams[(std::uint8_t)team]; }
GameState getGameState() const { return m_GameState; } Team& GetRedTeam() { return m_Teams[static_cast<std::uint8_t>(TeamColor::Red)]; }
void setGameState(GameState gameState) { m_GameState = gameState; }; const Team& GetRedTeam() const { return m_Teams[static_cast<std::uint8_t>(TeamColor::Red)]; }
const World* getWorld() const { return m_World; } Team& GetBlueTeam() { return m_Teams[static_cast<std::uint8_t>(TeamColor::Blue)]; }
World* getWorld() { return m_World; } const Team& GetBlueTeam() const { return m_Teams[static_cast<std::uint8_t>(TeamColor::Red)]; }
const PlayerList& getPlayers() const { return m_Players; } Team& GetTeam(TeamColor team) { return m_Teams[static_cast<std::uint8_t>(team)]; }
PlayerList& getPlayers() { return m_Players; } const Team& GetTeam(TeamColor team) const { return m_Teams[static_cast<std::uint8_t>(team)]; }
const Player& getPlayerById(PlayerID id) const { return m_Players.find(id)->second; } GameState GetGameState() const { return m_GameState; }
Player& getPlayerById(PlayerID id) { return m_Players.find(id)->second; } virtual 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);
const TeamList& GetTeams() const { return m_Teams; }
}; };

View File

@@ -10,26 +10,26 @@ namespace protocol {
class Connexion : public protocol::PacketHandler { class Connexion : public protocol::PacketHandler {
protected: protected:
protocol::PacketDispatcher m_Dispatcher; protocol::PacketDispatcher m_Dispatcher;
private: private:
network::TCPSocket m_Socket; network::TCPSocket m_Socket;
public: public:
Connexion(); Connexion();
Connexion(Connexion&& move); Connexion(Connexion&& move);
Connexion(protocol::PacketDispatcher* dispatcher); Connexion(protocol::PacketDispatcher* dispatcher);
Connexion(protocol::PacketDispatcher* dispatcher, network::TCPSocket& socket); Connexion(protocol::PacketDispatcher* dispatcher, network::TCPSocket& socket);
virtual ~Connexion(); virtual ~Connexion();
virtual bool updateSocket(); virtual bool UpdateSocket();
void closeConnection(); void CloseConnection();
bool connect(const std::string& address, std::uint16_t port); bool Connect(const std::string& address, std::uint16_t port);
network::Socket::Status getSocketStatus() const { return m_Socket.GetStatus(); } network::Socket::Status GetSocketStatus() const { return m_Socket.GetStatus(); }
void sendPacket(const protocol::Packet* packet); void SendPacket(const protocol::Packet* packet);
REMOVE_COPY(Connexion); REMOVE_COPY(Connexion);
}; };
} // namespace server } // namespace server

View File

@@ -10,12 +10,12 @@
namespace GameManager { namespace GameManager {
void render(); void Render();
void init(); void Init();
void destroy(); void Destroy();
void tick(); void Tick();
void startServer(); void StartServer();
} }

View File

@@ -1,38 +1,41 @@
#pragma once #pragma once
#include "Defines.h"
#include "Towers.h" #include "Towers.h"
#include "Types.h" #include "Types.h"
#include "Team.h" #include "Team.h"
#include "misc/ObjectNotifier.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <glm/glm.hpp>
namespace td { namespace td {
namespace game { namespace game {
struct WalkableTile;
enum class EffectType : std::uint8_t { enum class EffectType : std::uint8_t {
Slowness = 0, Slowness = 0,
Stun, Stun,
Fire, Fire,
Poison, Poison,
Heal, Heal,
}; };
enum class MobType : std::uint8_t { enum class MobType : std::uint8_t {
Zombie = 0, Zombie = 0,
Spider, Spider,
Skeleton, Skeleton,
Pigman, Pigman,
Creeper, Creeper,
Silverfish, Silverfish,
Blaze, Blaze,
Witch, Witch,
Slime, Slime,
Giant, Giant,
MOB_COUNT MOB_COUNT
}; };
typedef std::uint32_t MobID; typedef std::uint32_t MobID;
@@ -42,182 +45,217 @@ typedef std::vector<EffectType> EffectImmunities;
class MobStats { class MobStats {
private: private:
float m_Damage; float m_Damage;
float m_Speed; float m_Speed;
glm::vec2 m_Size; Vec2f m_Size;
std::uint16_t m_MoneyCost; std::uint16_t m_MoneyCost;
std::uint16_t m_ExpCost; std::uint16_t m_ExpCost;
std::uint16_t m_MaxLife; std::uint16_t m_MaxLife;
std::uint16_t m_ExpReward; std::uint16_t m_ExpReward;
public: public:
MobStats(float damage, float speed, glm::vec2 size, std::uint16_t moneyCost, MobStats(float damage, float speed, Vec2f size, std::uint16_t moneyCost,
std::uint16_t expCost, std::uint16_t expReward, std::uint16_t expCost, std::uint16_t expReward,
std::uint16_t maxLife) : m_Damage(damage), m_Speed(speed), std::uint16_t maxLife) : m_Damage(damage), m_Speed(speed),
m_Size(size), m_MoneyCost(moneyCost), m_ExpCost(expCost), m_Size(size), m_MoneyCost(moneyCost), m_ExpCost(expCost),
m_MaxLife(maxLife), m_ExpReward(expReward) { m_MaxLife(maxLife), m_ExpReward(expReward) {
} }
float getDamage() const { return m_Damage; } float GetDamage() const { return m_Damage; }
float getMovementSpeed() const { return m_Speed; } float GetMovementSpeed() const { return m_Speed; }
const glm::vec2& getSize() const { return m_Size; } const Vec2f& GetSize() const { return m_Size; }
std::uint16_t getMoneyCost() const { return m_MoneyCost; } std::uint16_t GetMoneyCost() const { return m_MoneyCost; }
std::uint16_t getExpCost() const { return m_ExpCost; } std::uint16_t GetExpCost() const { return m_ExpCost; }
std::uint16_t getExpReward() const { return m_ExpReward; } std::uint16_t GetExpReward() const { return m_ExpReward; }
std::uint16_t getMaxLife() const { return m_MaxLife; } std::uint16_t GetMaxLife() const { return m_MaxLife; }
}; };
struct EffectDuration { struct EffectDuration {
EffectType type; EffectType type;
float duration; // in seconds float duration; // in seconds
const Tower* tower; // the tower that gived the effect Tower* tower; // the tower that gived the effect
}; };
const MobStats* getMobStats(MobType type, std::uint8_t level); const MobStats* GetMobStats(MobType type, std::uint8_t level);
const TowerImmunities& getMobTowerImmunities(MobType type, std::uint8_t level); const TowerImmunities& GetMobTowerImmunities(MobType type, std::uint8_t level);
const EffectImmunities& getMobEffectImmunities(MobType type, std::uint8_t level); const EffectImmunities& GetMobEffectImmunities(MobType type, std::uint8_t level);
class Mob : public utils::shape::Rectangle { class Mob : public utils::shape::Rectangle {
protected: protected:
float m_Health; float m_Health;
private: private:
MobID m_ID; MobID m_ID;
PlayerID m_Sender; PlayerID m_Sender;
MobLevel m_Level; MobLevel m_Level;
Direction m_Direction; Direction m_Direction;
std::vector<EffectDuration> m_Effects; std::vector<EffectDuration> m_Effects;
const Tower* m_LastDamage; // the last tower that damaged the mob const Tower* m_LastDamage; // the last tower that damaged the mob
bool m_HasReachedCastle; // if true, no need to walk anymore float m_HitCooldown;
utils::Timer m_EffectFireTimer; utils::Timer m_EffectFireTimer;
utils::Timer m_EffectPoisonTimer; utils::Timer m_EffectPoisonTimer;
utils::Timer m_EffectHealTimer; utils::Timer m_EffectHealTimer;
TeamCastle* m_CastleTarget;
utils::CooldownTimer m_AttackTimer;
public: public:
Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level), Mob(MobID id, MobLevel level, PlayerID sender) : m_Sender(sender), m_Level(level),
m_HasReachedCastle(false), m_EffectFireTimer(1000), m_EffectPoisonTimer(1000), m_HitCooldown(0), m_EffectFireTimer(1000), m_EffectPoisonTimer(1000),
m_EffectHealTimer(1000) { m_EffectHealTimer(1000), m_CastleTarget(nullptr), m_AttackTimer(1000) {
} }
virtual MobType getType() const = 0; virtual MobType GetType() const = 0;
virtual void tick(std::uint64_t delta); virtual void Tick(std::uint64_t delta, World* world);
virtual bool OnDeath(World* world) { return true; } virtual bool OnDeath(World* world) { return true; }
const TowerImmunities& getTowerImmunities() const { return getMobTowerImmunities(getType(), m_Level); } MobID GetMobID() const { return m_ID; }
const EffectImmunities& getEffectImmunities() const { return getMobEffectImmunities(getType(), m_Level); } const TowerImmunities& GetTowerImmunities() const { return GetMobTowerImmunities(GetType(), m_Level); }
PlayerID getSender() const { return m_Sender; } const EffectImmunities& GetEffectImmunities() const { return GetMobEffectImmunities(GetType(), m_Level); }
MobLevel getLevel() const { return m_Level; } PlayerID GetSender() const { return m_Sender; }
const MobStats* getStats() const { return getMobStats(getType(), m_Level); } MobLevel GetLevel() const { return m_Level; }
float getHealth() const { return m_Health; } const MobStats* GetStats() const { return GetMobStats(GetType(), m_Level); }
bool isDead() const { return m_Health <= 0; } void SetHealth(float newHealth) { m_Health = newHealth; }
bool isAlive() const { return m_Health > 0; } float GetHealth() const { return m_Health; }
const Tower* getLastDamageTower() { return m_LastDamage; } bool IsDead() const { return m_Health <= 0; }
bool hasReachedEnemyCastle() { return m_HasReachedCastle; } bool IsAlive() const { return m_Health > 0; }
const Tower* GetLastDamageTower() { return m_LastDamage; }
bool HasReachedEnemyCastle() { return m_CastleTarget != nullptr; }
void damage(float dmg, const Tower* damager) { m_Health = std::max(0.0f, m_Health - dmg); m_LastDamage = damager; } void Damage(float dmg, const Tower* damager) {
void heal(float heal) { m_Health = std::min(static_cast<float>(getStats()->getMaxLife()), m_Health + heal); } m_Health = std::max(0.0f, m_Health - dmg);
void setMobReachedCastle() { m_HasReachedCastle = true; } // used when mob is in front of the castle m_LastDamage = damager;
m_HitCooldown = 0.1;
}
bool isImmuneTo(TowerType type); void Heal(float heal) {
m_Health = std::min(static_cast<float>(GetStats()->GetMaxLife()), m_Health + heal);
}
bool isImmuneTo(EffectType type); void SetMobReachedCastle(TeamCastle* castle) { m_CastleTarget = castle; } // used when mob is in front of the castle
void addEffect(EffectType type, float durationSec, const Tower* tower);
bool hasEffect(EffectType type);
float getTileX() { return getCenterX() - static_cast<float>(static_cast<std::int32_t>(getCenterX())); } // returns a float between 0 and 1 excluded bool IsImmuneTo(TowerType type);
float getTileY() { return getCenterY() - static_cast<float>(static_cast<std::int32_t>(getCenterY())); } // returns a float between 0 and 1 excluded
Direction getDirection() const { return m_Direction; } bool IsImmuneTo(EffectType type);
void setDirection(Direction dir) { m_Direction = dir; } void AddEffect(EffectType type, float durationSec, Tower* tower);
bool HasEffect(EffectType type);
bool HasTakenDamage() { return m_HitCooldown > 0; }
float GetTileX() { return GetCenterX() - static_cast<float>(static_cast<std::int32_t>(GetCenterX())); } // returns a float between 0 and 1 excluded
float GetTileY() { return GetCenterY() - static_cast<float>(static_cast<std::int32_t>(GetCenterY())); } // returns a float between 0 and 1 excluded
Direction GetDirection() const { return m_Direction; }
void SetDirection(Direction dir) { m_Direction = dir; }
protected: protected:
void initMob() { void InitMob() {
m_Health = static_cast<float>(getStats()->getMaxLife()); m_Health = static_cast<float>(GetStats()->GetMaxLife());
setSize(getStats()->getSize().x, getStats()->getSize().y); SetSize(GetStats()->GetSize().x, GetStats()->GetSize().y);
} }
private: private:
void updateEffects(std::uint64_t delta); void UpdateEffects(std::uint64_t delta, World* world);
EffectDuration& getEffect(EffectType type); void AttackCastle(std::uint64_t delta, World* world);
void Move(std::uint64_t delta, World* world);
void Walk(std::uint64_t delta, World* world);
void MoveBack(const TeamCastle& castle, World* world);
void ChangeDirection(const WalkableTile& tile, World* world);
bool IsTouchingCastle(const TeamCastle& castle) const;
EffectDuration& GetEffect(EffectType type);
}; };
typedef std::shared_ptr<Mob> MobPtr; typedef std::shared_ptr<Mob> MobPtr;
class Zombie : public Mob { class Zombie : public Mob {
public: public:
Zombie(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Zombie(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Zombie; } virtual MobType GetType() const { return MobType::Zombie; }
}; };
class Spider : public Mob { class Spider : public Mob {
public: public:
Spider(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Spider(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Spider; } virtual MobType GetType() const { return MobType::Spider; }
}; };
class Skeleton : public Mob { class Skeleton : public Mob {
public: public:
Skeleton(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Skeleton(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Skeleton; } virtual MobType GetType() const { return MobType::Skeleton; }
}; };
class PigMan : public Mob { class PigMan : public Mob {
public: public:
PigMan(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } PigMan(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Pigman; } virtual MobType GetType() const { return MobType::Pigman; }
}; };
class Creeper : public Mob { class Creeper : public Mob {
public: public:
Creeper(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Creeper(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Creeper; } virtual MobType GetType() const { return MobType::Creeper; }
}; };
class Silverfish : public Mob { class Silverfish : public Mob {
public: public:
Silverfish(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Silverfish(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Silverfish; } virtual MobType GetType() const { return MobType::Silverfish; }
}; };
class Blaze : public Mob { class Blaze : public Mob {
public: public:
Blaze(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Blaze(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Blaze; } virtual MobType GetType() const { return MobType::Blaze; }
}; };
class Witch : public Mob { class Witch : public Mob {
public: public:
Witch(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Witch(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Witch; } virtual MobType GetType() const { return MobType::Witch; }
}; };
class Slime : public Mob { class Slime : public Mob {
public: public:
Slime(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Slime(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Slime; } virtual MobType GetType() const { return MobType::Slime; }
}; };
class Giant : public Mob { class Giant : public Mob {
public: public:
Giant(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { initMob(); } Giant(MobID id, std::uint8_t level, PlayerID sender) : Mob(id, level, sender) { InitMob(); }
virtual MobType getType() const { return MobType::Giant; } virtual MobType GetType() const { return MobType::Giant; }
}; };
namespace MobFactory { namespace MobFactory {
MobPtr createMob(MobID id, MobType type, std::uint8_t level, PlayerID sender);
std::string getMobName(MobType type); MobPtr CreateMob(MobID id, MobType type, std::uint8_t level, PlayerID sender);
std::string GetMobName(MobType type);
} }
class MobListener {
public:
virtual void OnMobSpawn(Mob* mob) {}
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) {}
};
typedef utils::ObjectNotifier<MobListener> MobNotifier;
} // namespace game } // namespace game
} // namespace td } // namespace td

View File

@@ -1,56 +1,50 @@
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
#include "game/Team.h" #include "game/Team.h"
#include "game/PlayerUpgrades.h"
namespace td { namespace td {
namespace game { namespace game {
class Player { class Player {
private: private:
game::TeamColor m_TeamColor; TeamColor m_TeamColor;
PlayerUpgrades m_Upgrades;
std::uint32_t m_Gold; std::uint32_t m_Gold;
std::uint32_t m_Exp; std::uint32_t m_Exp;
std::string m_Name; std::string m_Name;
std::uint8_t m_ID; PlayerID m_ID;
std::uint8_t m_GoldPerSecond;
bool m_GoldChanged;
bool m_ExpChanged;
public: public:
Player(std::uint8_t id = 0) : m_TeamColor(game::TeamColor::None), m_Gold(0), m_Exp(0), m_ID(id), m_GoldPerSecond(5),
m_GoldChanged(false), m_ExpChanged(false) {}
const std::string& getName() const { return m_Name; } Player(std::uint8_t id = 0) : m_TeamColor(TeamColor::None), m_Gold(0), m_Exp(0), m_ID(id) {}
void setName(const std::string& name) { m_Name = name; }
game::TeamColor getTeamColor() const { return m_TeamColor; } const std::string& GetName() const { return m_Name; }
void setTeamColor(game::TeamColor teamColor) { m_TeamColor = teamColor; } void SetName(const std::string& name) { m_Name = name; }
std::uint8_t getGoldPerSecond() const { return m_GoldPerSecond; } TeamColor GetTeamColor() const { return m_TeamColor; }
void setGoldPerSecond(std::uint8_t goldPerSecond) { m_GoldPerSecond = goldPerSecond; } void SetTeamColor(TeamColor teamColor) { m_TeamColor = teamColor; }
std::uint32_t getGold() const { return m_Gold; } std::uint32_t GetGold() const { return m_Gold; }
void setGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold = gold; } void SetGold(std::uint32_t gold) { m_Gold = gold; }
void addGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold += gold; } void AddGold(std::uint32_t gold) { m_Gold += gold; }
void removeGold(std::uint32_t gold) { m_GoldChanged = true; m_Gold -= gold; } void RemoveGold(std::uint32_t gold) { m_Gold -= gold; }
std::uint32_t getExp() const { return m_Exp; } std::uint32_t GetExp() const { return m_Exp; }
void setExp(std::uint32_t exp) { m_ExpChanged = true; m_Exp = exp; } void SetExp(std::uint32_t exp) { m_Exp = exp; }
void addExp(std::uint32_t exp) { m_ExpChanged = true; m_Exp += exp; } void AddExp(std::uint32_t exp) { m_Exp += exp; }
void removeExp(std::uint32_t exp) { m_ExpChanged = true; m_Exp -= exp; } void RemoveExp(std::uint32_t exp) { m_Exp -= exp; }
bool hasGoldChanged() const { return m_GoldChanged; } const PlayerUpgrades& getUpgrades() const { return m_Upgrades; }
bool hasExpChanged() const { return m_ExpChanged; } PlayerUpgrades& getUpgrades() { return m_Upgrades; }
void updateGold() { m_GoldChanged = false; } bool HasEnoughGold(std::uint32_t gold) const { return m_Gold >= gold; }
void updateExp() { m_ExpChanged = false; } bool HasEnoughExp(std::uint32_t exp) const { return m_Exp >= exp; }
std::uint8_t getID() const { return m_ID; } PlayerID GetID() const { return m_ID; }
}; };
} // namespace game } // namespace game

View File

@@ -0,0 +1,34 @@
#pragma once
#include "Mobs.h"
namespace td {
namespace game {
class PlayerUpgrades {
private:
std::uint8_t m_ClickerLevel;
std::uint8_t m_GoldPerSecond;
std::array<std::uint8_t, static_cast<std::size_t>(MobType::MOB_COUNT)> m_MobsUpgradeLevel;
public:
static const int MAX_MOB_LEVEL = 5;
static const int MAX_CLICKER_LEVEL = 3;
PlayerUpgrades() : m_ClickerLevel(1), m_GoldPerSecond(5) {}
std::uint8_t GetClickerLevel() const { return m_ClickerLevel; }
std::uint8_t GetMobUpgradeLevel(MobType mob) const { return m_MobsUpgradeLevel.at(static_cast<std::size_t>(mob)); }
std::uint8_t GetGoldPerSecond() const { return m_GoldPerSecond; }
void UpgradeMob(MobType mob) {
std::uint8_t& mobLevel = m_MobsUpgradeLevel.at(static_cast<std::size_t>(mob));
mobLevel = std::min(mobLevel + 1, MAX_MOB_LEVEL);
}
void UpgradeClicker() { m_ClickerLevel = std::min(m_ClickerLevel + 1, MAX_CLICKER_LEVEL); }
void SetGoldPerSecond(std::uint8_t goldPerSecond) { m_GoldPerSecond = goldPerSecond; }
};
} // namespace game
} // namespace td

View File

@@ -14,62 +14,79 @@ namespace game {
class Player; class Player;
enum class TeamColor : std::int8_t { enum class TeamColor : std::int8_t {
None = -1, None = -1,
Red, Red,
Blue Blue
}; };
class Spawn : public utils::shape::Rectangle { class Spawn : public utils::shape::Rectangle {
private: private:
Direction m_Direction; Direction m_Direction;
public: public:
Spawn(){ Spawn() {
setWidth(5); SetWidth(5);
setHeight(5); SetHeight(5);
} }
Direction getDirection() const { return m_Direction; } Direction GetDirection() const { return m_Direction; }
void setDirection(Direction direction) { m_Direction = direction; } void SetDirection(Direction direction) { m_Direction = direction; }
}; };
struct TeamCastle : public utils::shape::Rectangle{ class Team;
class TeamCastle : public utils::shape::Rectangle {
private: private:
float m_Life; const Team* m_Team;
float m_Life;
public: public:
TeamCastle() : m_Life(1000) { static constexpr int CastleMaxLife = 1000;
setWidth(5);
setHeight(5);
}
float getLife() const { return m_Life; } TeamCastle(const Team* team) : m_Team(team), m_Life(CastleMaxLife) {
SetWidth(5);
SetHeight(5);
}
void setLife(float life) { m_Life = life; } TeamCastle() : TeamCastle(nullptr) {}
void damage(float damage) { m_Life = std::max(0.0f, m_Life - damage); }
float GetLife() const { return m_Life; }
const Team* GetTeam() const { return m_Team; }
void SetTeam(const Team* team) { m_Team = team; }
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 { class Team {
private: private:
std::vector<Player*> m_Players; std::vector<Player*> m_Players;
TeamColor m_Color; TeamColor m_Color;
Spawn m_Spawn; Spawn m_Spawn;
TeamCastle m_TeamCastle; TeamCastle m_TeamCastle;
public: public:
Team(TeamColor color); Team(TeamColor color);
void addPlayer(Player* newPlayer); void AddPlayer(Player* newPlayer);
void removePlayer(const Player* player); void RemovePlayer(const Player* player);
TeamColor getColor() const; TeamColor GetColor() const;
const Spawn& getSpawn() const { return m_Spawn; } const Spawn& GetSpawn() const { return m_Spawn; }
Spawn& getSpawn() { return m_Spawn; } Spawn& GetSpawn() { return m_Spawn; }
const TeamCastle& getCastle() const { return m_TeamCastle; } const TeamCastle& GetCastle() const { return m_TeamCastle; }
TeamCastle& getCastle() { return m_TeamCastle; } TeamCastle& GetCastle() { return m_TeamCastle; }
std::uint8_t getPlayerCount() const; std::uint8_t GetPlayerCount() const;
}; };
typedef std::array<Team, 2> TeamList;
} // namespace game } // namespace game
} // namespace td } // namespace td

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
#include <memory> #include <memory>
@@ -18,247 +17,249 @@ class Mob;
typedef std::shared_ptr<Mob> MobPtr; typedef std::shared_ptr<Mob> MobPtr;
enum class TowerType : std::uint8_t { enum class TowerType : std::uint8_t {
Archer = 0, Archer = 0,
Ice, Ice,
Sorcerer, Sorcerer,
Zeus, Zeus,
Mage, Mage,
Artillery, Artillery,
Quake, Quake,
Poison, Poison,
Leach, Leach,
Turret, Turret,
Necromancer, Necromancer,
TowerCount TowerCount
}; };
enum class TowerSize : std::uint8_t { enum class TowerSize : std::uint8_t {
Little = 3, // 3x3 Little = 3, // 3x3
Big = 5, // 5x5 Big = 5, // 5x5
}; };
enum class TowerPath : std::uint8_t { enum class TowerPath : std::uint8_t {
Top = 0, Top = 0,
Base, // Base Path Base, // Base Path
Bottom Bottom
}; };
class TowerStats { class TowerStats {
private: private:
float m_Rate; float m_Rate;
float m_Damage; float m_Damage;
std::uint8_t m_Range; std::uint8_t m_Range;
public: public:
TowerStats(float rate, float damage, std::uint8_t range) : m_Rate(rate), m_Damage(damage), TowerStats(float rate, float damage, std::uint8_t range) : m_Rate(rate), m_Damage(damage),
m_Range(range) { m_Range(range) {
} }
float getDamageRate() const { return m_Rate; } float GetDamageRate() const { return m_Rate; }
float getDamage() const { return m_Damage; } float GetDamage() const { return m_Damage; }
std::uint8_t getRange() const { return m_Range; } std::uint8_t GetRange() const { return m_Range; }
}; };
class TowerLevel { class TowerLevel {
private: private:
// 1, 2, 3, 4 // 1, 2, 3, 4
std::uint8_t m_Level : 3; 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) // 0 : base path 1 : top path (if there is bottom path) 2 : bottom path (if there is top path)
TowerPath m_Path : 2; TowerPath m_Path : 2;
public: public:
TowerLevel() : m_Level(1), m_Path(TowerPath::Base) {} TowerLevel() : m_Level(1), m_Path(TowerPath::Base) {}
TowerLevel(std::uint8_t level, TowerPath path) : m_Level(level), m_Path(path) {} TowerLevel(std::uint8_t level, TowerPath path) : m_Level(level), m_Path(path) {}
std::uint8_t getLevel() const { return m_Level; } std::uint8_t GetLevel() const { return m_Level; }
TowerPath getPath() const { return m_Path; } TowerPath GetPath() const { return m_Path; }
void setLevel(std::uint8_t level) { m_Level = level; } void SetLevel(std::uint8_t level) { m_Level = level; }
void setPath(TowerPath path) { m_Path = path; } void SetPath(TowerPath path) { m_Path = path; }
// operator to sort maps // operator to sort maps
friend bool operator<(const TowerLevel& level, const TowerLevel& other) { friend bool operator<(const TowerLevel& level, const TowerLevel& other) {
return level.getLevel() + static_cast<std::uint8_t>(level.getPath()) * 4 < return level.GetLevel() + static_cast<std::uint8_t>(level.GetPath()) * 4 <
other.getLevel() + static_cast<std::uint8_t>(other.getPath()) * 4; other.GetLevel() + static_cast<std::uint8_t>(other.GetPath()) * 4;
} }
}; };
const TowerStats* getTowerStats(TowerType type, TowerLevel level); const TowerStats* GetTowerStats(TowerType type, TowerLevel level);
typedef std::uint16_t TowerID; typedef std::uint16_t TowerID;
class Tower : public utils::shape::Circle { class Tower : public utils::shape::Circle {
private: private:
TowerID m_ID; TowerID m_ID;
TowerType m_Type; TowerType m_Type;
TowerLevel m_Level{}; TowerLevel m_Level{};
PlayerID m_Builder; PlayerID m_Builder;
protected: protected:
utils::CooldownTimer m_Timer; utils::CooldownTimer m_Timer;
public: public:
Tower(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) : utils::shape::Circle(x + 0.5f, y + 0.5f, 0), m_ID(id), m_Type(type), m_Builder(builder), Tower(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder) : utils::shape::Circle(x + 0.5f, y + 0.5f, 0), m_ID(id), m_Type(type), m_Builder(builder),
m_Timer(getStats()->getDamageRate() * 1000) { // converting seconds to millis m_Timer(GetStats()->GetDamageRate() * 1000) { // converting seconds to millis
setRadius(getStats()->getRange()); SetRadius(GetStats()->GetRange());
} }
virtual TowerType getType() const = 0; virtual TowerType GetType() const = 0;
virtual TowerSize getSize() const = 0; virtual TowerSize GetSize() const = 0;
virtual void tick(std::uint64_t delta, World* world) = 0; virtual void Tick(std::uint64_t delta, World* world) = 0;
void upgrade(std::uint8_t level, TowerPath path) { void Upgrade(std::uint8_t level, TowerPath path) {
m_Level.setLevel(level); m_Level.SetLevel(level);
m_Level.setPath(path); m_Level.SetPath(path);
m_Timer.setCooldown(getStats()->getDamageRate() * 1000); // converting seconds to millis m_Timer.SetCooldown(GetStats()->GetDamageRate() * 1000); // converting seconds to millis
m_Timer.reset(); m_Timer.Reset();
setRadius(getStats()->getRange()); SetRadius(GetStats()->GetRange());
} }
std::uint16_t getID() const { return m_ID; } std::uint16_t GetID() const { return m_ID; }
const TowerLevel& getLevel() const { return m_Level; } const TowerLevel& GetLevel() const { return m_Level; }
const TowerStats* getStats() const { return getTowerStats(m_Type, m_Level); } const TowerStats* GetStats() const { return GetTowerStats(m_Type, m_Level); }
PlayerID getBuilder() const { return m_Builder; } PlayerID GetBuilder() const { return m_Builder; }
bool isMobInRange(MobPtr mob); bool IsMobInRange(MobPtr mob);
}; };
typedef std::shared_ptr<Tower> TowerPtr; typedef std::shared_ptr<Tower> TowerPtr;
namespace TowerFactory { namespace TowerFactory {
TowerPtr createTower(TowerType type, TowerID id, std::int32_t x, std::int32_t y, PlayerID builder); TowerPtr CreateTower(TowerType type, TowerID id, std::int32_t x, std::int32_t y, PlayerID builder);
std::string getTowerName(TowerType type); std::string GetTowerName(TowerType type);
} // namespace TowerFactory } // namespace TowerFactory
class TowerInfo { class TowerInfo {
private: private:
std::string m_Name, m_Description; std::string m_Name, m_Description;
bool m_IsBigTower; bool m_IsBigTower;
public: public:
TowerInfo(std::string&& name, std::string&& description, bool big) : m_Name(std::move(name)), TowerInfo(std::string&& name, std::string&& description, bool big) : m_Name(std::move(name)),
m_Description(std::move(description)), m_IsBigTower(big) {} m_Description(std::move(description)), m_IsBigTower(big) {
}
const std::string& getName() const { return m_Name; } const std::string& GetName() const { return m_Name; }
const std::string& getDescription() const { return m_Description; } const std::string& GetDescription() const { return m_Description; }
bool isBigTower() const { return m_IsBigTower; } bool IsBigTower() const { return m_IsBigTower; }
}; };
const TowerInfo& getTowerInfo(TowerType type); const TowerInfo& GetTowerInfo(TowerType type);
// ---------- Little Towers ---------- // ---------- Little Towers ----------
class LittleTower : public Tower { class LittleTower : public Tower {
public: public:
LittleTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {} LittleTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {}
virtual TowerSize getSize() const { return TowerSize::Little; } virtual TowerSize GetSize() const { return TowerSize::Little; }
virtual TowerType getType() const = 0; virtual TowerType GetType() const = 0;
virtual void tick(std::uint64_t delta, World* world) = 0; virtual void Tick(std::uint64_t delta, World* world) = 0;
}; };
class ArcherTower : public LittleTower { class ArcherTower : public LittleTower {
public: public:
ArcherTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} ArcherTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {}
constexpr static float ExplosionRadius = 1.5f; constexpr static float ExplosionRadius = 1.5f;
constexpr static float FireDurationSec = 10.0f;
virtual TowerType getType() const { return TowerType::Archer; } virtual TowerType GetType() const { return TowerType::Archer; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class IceTower : public LittleTower { class IceTower : public LittleTower {
public: public:
IceTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} IceTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Ice; } virtual TowerType GetType() const { return TowerType::Ice; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class MageTower : public LittleTower { class MageTower : public LittleTower {
public: public:
MageTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} MageTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Mage; } virtual TowerType GetType() const { return TowerType::Mage; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class PoisonTower : public LittleTower { class PoisonTower : public LittleTower {
public: public:
PoisonTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} PoisonTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Poison; } virtual TowerType GetType() const { return TowerType::Poison; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class QuakeTower : public LittleTower { class QuakeTower : public LittleTower {
public: public:
QuakeTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} QuakeTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Quake; } virtual TowerType GetType() const { return TowerType::Quake; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class ArtilleryTower : public LittleTower { class ArtilleryTower : public LittleTower {
public: public:
ArtilleryTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} ArtilleryTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Artillery; } virtual TowerType GetType() const { return TowerType::Artillery; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class SorcererTower : public LittleTower { class SorcererTower : public LittleTower {
public: public:
SorcererTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} SorcererTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Sorcerer; } virtual TowerType GetType() const { return TowerType::Sorcerer; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class ZeusTower : public LittleTower { class ZeusTower : public LittleTower {
public: public:
ZeusTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, getType(), x, y, builder) {} ZeusTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : LittleTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Zeus; } virtual TowerType GetType() const { return TowerType::Zeus; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
// ---------- Big Towers ---------- // ---------- Big Towers ----------
class BigTower : public Tower { class BigTower : public Tower {
public: public:
BigTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {} BigTower(TowerID id, TowerType type, std::uint16_t x, std::uint16_t y, PlayerID builder) : Tower(id, type, x, y, builder) {}
virtual TowerSize getSize() const { return TowerSize::Big; } virtual TowerSize GetSize() const { return TowerSize::Big; }
virtual TowerType getType() const = 0; virtual TowerType GetType() const = 0;
virtual void tick(std::uint64_t delta, World* world) = 0; virtual void Tick(std::uint64_t delta, World* world) = 0;
}; };
class TurretTower : public BigTower { class TurretTower : public BigTower {
public: public:
TurretTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, getType(), x, y, builder) {} TurretTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Turret; } virtual TowerType GetType() const { return TowerType::Turret; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class NecromancerTower : public BigTower { class NecromancerTower : public BigTower {
public: public:
NecromancerTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, getType(), x, y, builder) {} NecromancerTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Necromancer; } virtual TowerType GetType() const { return TowerType::Necromancer; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
class LeachTower : public BigTower { class LeachTower : public BigTower {
public: public:
LeachTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, getType(), x, y, builder) {} LeachTower(TowerID id, std::uint16_t x, std::uint16_t y, PlayerID builder) : BigTower(id, GetType(), x, y, builder) {}
virtual TowerType getType() const { return TowerType::Leach; } virtual TowerType GetType() const { return TowerType::Leach; }
virtual void tick(std::uint64_t delta, World* world); virtual void Tick(std::uint64_t delta, World* world);
}; };
} // namespace game } // namespace game

View File

@@ -2,14 +2,17 @@
#include <cstdint> #include <cstdint>
// include Log for every files
#include "misc/Log.h"
namespace td { namespace td {
namespace game { namespace game {
enum class Direction : std::uint8_t { enum class Direction : std::uint8_t {
PositiveX = 1 << 0, PositiveX = 1 << 0,
NegativeX = 1 << 1, NegativeX = 1 << 1,
PositiveY = 1 << 2, PositiveY = 1 << 2,
NegativeY = 1 << 3, NegativeY = 1 << 3,
}; };
typedef std::uint8_t PlayerID; typedef std::uint8_t PlayerID;

View File

@@ -4,33 +4,37 @@
#include <vector> #include <vector>
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
#include <glm/glm.hpp>
#include <array> #include <array>
#include "Mobs.h" #include "Mobs.h"
#include "Team.h" #include "Team.h"
#include "misc/ObjectNotifier.h"
namespace td { namespace td {
namespace game { namespace game {
typedef std::pair<std::int16_t, std::int16_t> ChunkCoord;
struct ChunkCoord {
std::int16_t x, y;
friend bool operator==(const td::game::ChunkCoord& first, const td::game::ChunkCoord& other) {
return first.x == other.x && first.y == other.y;
}
};
} }
} }
namespace std { namespace std {
template <> template <>
struct hash<td::game::ChunkCoord> { struct hash<td::game::ChunkCoord> {
std::size_t operator()(const td::game::ChunkCoord& key) const { std::size_t operator()(const td::game::ChunkCoord& key) const noexcept {
// Compute individual hash values for first, return std::hash<std::int16_t>()(key.x << 16 | key.y);
// second and third and combine them using XOR }
// and bit shifting:
return ((std::hash<std::int16_t>()(key.first) ^ (std::hash<std::int16_t>()(key.second) << 1)) >> 1);
}
}; };
} }
namespace td { namespace td {
namespace protocol { namespace protocol {
class WorldBeginDataPacket; class WorldBeginDataPacket;
@@ -43,42 +47,45 @@ namespace game {
class Game; class Game;
enum class TileType : std::uint8_t { enum class TileType : std::uint8_t {
None = 0, None = 0,
Tower, Tower,
Walk, Walk,
Decoration, Decoration,
/*Heal, /*Heal,
Lava, Lava,
Bedrock, Bedrock,
Freeze, Freeze,
Ice,*/ Ice,*/
}; };
struct Color { static constexpr Color BLACK{ 0, 0, 0 };
std::uint8_t r, g, b; 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 };
struct Tile { struct Tile {
virtual TileType getType() const = 0; virtual TileType GetType() const = 0;
}; };
struct TowerTile : Tile { struct TowerTile : Tile {
std::uint8_t color_palette_ref; std::uint8_t color_palette_ref;
TeamColor team_owner; TeamColor team_owner;
virtual TileType getType() const { return TileType::Tower; } virtual TileType GetType() const { return TileType::Tower; }
}; };
struct WalkableTile : Tile { struct WalkableTile : Tile {
Direction direction; Direction direction;
virtual TileType getType() const { return TileType::Walk; } virtual TileType GetType() const { return TileType::Walk; }
}; };
struct DecorationTile : Tile { struct DecorationTile : Tile {
std::uint16_t color_palette_ref; std::uint16_t color_palette_ref;
virtual TileType getType() const { return TileType::Decoration; } virtual TileType GetType() const { return TileType::Decoration; }
}; };
typedef std::shared_ptr<Tile> TilePtr; typedef std::shared_ptr<Tile> TilePtr;
@@ -86,22 +93,23 @@ typedef std::vector<std::uint16_t> ChunkPalette;
typedef std::shared_ptr<WalkableTile> WalkableTilePtr; typedef std::shared_ptr<WalkableTile> WalkableTilePtr;
typedef std::array<std::uint16_t, 32 * 32> ChunkData;
typedef std::uint32_t TileIndex; typedef std::uint32_t TileIndex;
//32 x 32 area //32 x 32 area
struct Chunk { struct Chunk {
enum { ChunkWidth = 32, ChunkHeight = 32, ChunkSize = ChunkWidth * ChunkHeight }; enum { ChunkWidth = 32, ChunkHeight = 32, ChunkSize = ChunkWidth * ChunkHeight };
// stores index of tile palette typedef std::array<std::uint16_t, ChunkSize> ChunkData;
ChunkData tiles{ 0 };
ChunkPalette palette;
TileIndex getTileIndex(std::uint16_t tileNumber) const { // stores index of tile palette
TileIndex chunkPaletteIndex = tiles.at(tileNumber); ChunkData tiles{ 0 };
if (chunkPaletteIndex == 0) // index 0 means empty tile index 1 = first tile ChunkPalette palette;
return 0;
return palette.at(chunkPaletteIndex); TileIndex GetTileIndex(std::uint16_t tileNumber) const {
} TileIndex chunkPaletteIndex = tiles.at(tileNumber);
if (chunkPaletteIndex == 0) // index 0 means empty tile index 1 = first tile
return 0;
return palette.at(chunkPaletteIndex);
}
}; };
typedef std::shared_ptr<Chunk> ChunkPtr; typedef std::shared_ptr<Chunk> ChunkPtr;
@@ -118,111 +126,119 @@ typedef std::vector<TowerPtr> TowerList;
class WorldListener { class WorldListener {
public: public:
WorldListener(){} WorldListener() {}
virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter){} virtual void OnTowerAdd(TowerPtr tower) {}
virtual void OnTowerRemove(TowerPtr tower) {}
virtual void OnArrowShot(MobPtr target, Tower* shooter){} virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter) {}
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter){}
virtual void OnMobDamage(MobPtr target, float damage, Tower* damager){} virtual void OnArrowShot(MobPtr target, bool fire, Tower* shooter) {}
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter) {}
virtual void OnMobDead(MobPtr mob){}
}; };
typedef utils::ObjectNotifier<WorldListener> WorldNotifier; typedef utils::ObjectNotifier<WorldListener> WorldNotifier;
class World : public WorldNotifier, public WorldListener{ class World : public WorldListener, public MobListener {
protected: protected:
TowerTileColorPalette m_TowerPlacePalette; TowerTileColorPalette m_TowerPlacePalette;
Color m_WalkablePalette; Color m_WalkablePalette;
std::vector<Color> m_DecorationPalette; std::vector<Color> m_DecorationPalette;
Color m_Background;
std::unordered_map<ChunkCoord, ChunkPtr> m_Chunks; std::unordered_map<ChunkCoord, ChunkPtr> m_Chunks;
SpawnColorPalette m_SpawnColorPalette; SpawnColorPalette m_SpawnColorPalette;
TilePalette m_TilePalette; TilePalette m_TilePalette;
MobList m_Mobs; MobList m_Mobs;
TowerList m_Towers; TowerList m_Towers;
Game* m_Game; Game* m_Game;
WorldNotifier m_WorldNotifier;
MobNotifier m_MobNotifier;
public: public:
World(Game* game); World(Game* game);
static constexpr std::uint8_t CastleWidth = 5, CastleHeight = 5; bool LoadMap(const protocol::WorldBeginDataPacket* worldHeader);
bool LoadMap(const protocol::WorldDataPacket* worldData);
bool loadMap(const protocol::WorldBeginDataPacket* worldHeader); bool LoadMapFromFile(const std::string& fileName);
bool loadMap(const protocol::WorldDataPacket* worldData); bool SaveMap(const std::string& fileName) const;
bool loadMapFromFile(const std::string& fileName); void Tick(std::uint64_t delta);
bool saveMap(const std::string& fileName) const;
void tick(std::uint64_t delta); void SpawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir);
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);
TowerPtr placeTowerAt(TowerID id, TowerType type, std::int32_t x, std::int32_t y, PlayerID builder); TilePtr GetTile(std::int32_t x, std::int32_t y) const;
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<Color>& GetDecorationPalette() const { return m_DecorationPalette; }
const Color& GetBackgroundColor() const { return m_Background; }
const TowerTileColorPalette& getTowerTileColorPalette() const { return m_TowerPlacePalette; } const TilePalette& GetTilePalette() const { return m_TilePalette; }
const Color& getWalkableTileColor() const { return m_WalkablePalette; }
const std::vector<Color>& getDecorationPalette() const { return m_DecorationPalette; }
const TilePalette& getTilePalette() const { return m_TilePalette; } TilePtr GetTilePtr(TileIndex index) const {
if (index == 0)
return nullptr;
return m_TilePalette.at(index - 1);
}
TilePtr getTilePtr(TileIndex index) const { bool CanPlaceLittleTower(const Vec2f& worldPos, PlayerID player) const;
if (index == 0) bool CanPlaceBigTower(const Vec2f& worldPos, PlayerID player) const;
return nullptr;
return m_TilePalette.at(index - 1);
}
bool CanPlaceLittleTower(const glm::vec2& worldPos, PlayerID player) const; TowerPtr GetTower(const Vec2f& position) const; // returns null if no tower is here
bool CanPlaceBigTower(const glm::vec2& worldPos, PlayerID player) const;
TowerPtr getTower(const glm::vec2& position) const; // returns null if no tower is here const std::unordered_map<ChunkCoord, ChunkPtr>& GetChunks() const { return m_Chunks; }
const std::unordered_map<ChunkCoord, ChunkPtr>& getChunks() const { return m_Chunks; } const Color& GetSpawnColor(TeamColor color) const { return m_SpawnColorPalette[static_cast<std::size_t>(color)]; }
const SpawnColorPalette& GetSpawnColors() const { return m_SpawnColorPalette; }
const Color& getSpawnColor(TeamColor color) const { return m_SpawnColorPalette[(std::size_t)color]; } const MobList& GetMobList() const { return m_Mobs; }
const SpawnColorPalette& getSpawnColors() const { return m_SpawnColorPalette; } MobList& GetMobList() { return m_Mobs; }
const MobList& getMobList() const { return m_Mobs; } const Color* GetTileColor(TilePtr tile) const;
MobList& getMobList() { return m_Mobs; }
const Color* getTileColor(TilePtr tile) const; Team& GetRedTeam();
const Team& GetRedTeam() const;
Team& getRedTeam(); Team& GetBlueTeam();
const Team& getRedTeam() const; const Team& GetBlueTeam() const;
Team& getBlueTeam(); Team& GetTeam(TeamColor team);
const Team& getBlueTeam() const; const Team& GetTeam(TeamColor team) const;
Team& getTeam(TeamColor team); const TeamList& GetTeams() const;
const Team& getTeam(TeamColor team) const;
const TowerList& getTowers() const { return m_Towers; }; const TowerList& GetTowers() const { return m_Towers; }
TowerPtr getTowerById(TowerID tower); TowerPtr GetTowerById(TowerID tower);
// WorldListener const Player* GetPlayerById(PlayerID id) const;
virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter); WorldNotifier& GetWorldNotifier() { return m_WorldNotifier; }
MobNotifier& GetMobNotifier() { return m_MobNotifier; }
virtual void OnArrowShot(MobPtr target, Tower* shooter); // WorldListener
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter);
virtual void OnArcherTowerShot(MobPtr target, ArcherTower* shooter) override;
virtual void OnArrowShot(MobPtr target, bool fire, Tower* shooter) override;
virtual void OnExplosion(utils::shape::Circle explosion, float centerDamage, Tower* shooter) override;
// MobListener
virtual void OnMobDamage(Mob* target, float damage, Tower* source) override;
virtual void OnMobCastleDamage(Mob* damager, TeamCastle* enemyCastle, float damage) override;
virtual void OnMobDamage(MobPtr target, float damage, Tower* source);
private: private:
void moveMobs(std::uint64_t delta); void TickMobs(std::uint64_t delta);
void moveMob(MobPtr mob, std::uint64_t delta); void CleanDeadMobs();
void moveBackMob(MobPtr mob, const TeamCastle& castle);
void changeMobDirection(MobPtr mob, WalkableTilePtr tile);
bool isMobTouchingCastle(MobPtr mob, const TeamCastle& castle) const;
void tickMobs(std::uint64_t delta);
void cleanDeadMobs();
}; };
} // namespace game } // namespace game

View File

@@ -17,33 +17,38 @@ namespace client {
class Client { class Client {
private: private:
render::Renderer* m_Renderer; render::Renderer* m_Renderer;
ClientConnexion m_Connexion; ClientConnexion m_Connexion;
ClientGame m_Game; std::unique_ptr<ClientGame> m_Game;
bool m_Connected; bool m_Connected;
public: public:
Client(render::Renderer* renderer) : m_Renderer(renderer), m_Game(this), m_Connected(false) {} Client(render::Renderer* renderer) : m_Renderer(renderer), m_Game(std::make_unique<ClientGame>(this)), m_Connected(false) {}
const ClientGame& getGame() const { return m_Game; } const ClientGame& GetGame() const { return *m_Game; }
const ClientConnexion& getConnexion() const { return m_Connexion; } const ClientConnexion& GetConnexion() const { return m_Connexion; }
render::Renderer* getRenderer() const { return m_Renderer; } render::Renderer* GetRenderer() const { return m_Renderer; }
ClientGame& getGame() { return m_Game; } ClientGame& GetGame() { return *m_Game; }
ClientConnexion& getConnexion() { return m_Connexion; } ClientConnexion& GetConnexion() { return m_Connexion; }
void tick(std::uint64_t delta); const game::Player* GetPlayer() { return m_Game->GetPlayer(); }
void render(); void Tick(std::uint64_t delta);
bool connect(const network::IPAddresses& addresses, std::uint16_t port); void Render();
void closeConnection();
bool isConnected() const { return m_Connexion.getSocketStatus() == network::Socket::Connected; } bool Connect(const network::IPAddresses& addresses, std::uint16_t port);
void CloseConnection();
void selectTeam(game::TeamColor team); bool IsConnected() const { return m_Connexion.GetSocketStatus() == network::Socket::Connected; }
void sendMobs(const std::vector<protocol::MobSend>& mobSends);
void placeTower(game::TowerType type, const glm::vec2& position); void SelectTeam(game::TeamColor team);
void upgradeTower(game::TowerID tower, game::TowerLevel level); void SendMobs(const std::vector<protocol::MobSend>& mobSends);
void PlaceTower(game::TowerType type, const Vec2f& position);
void UpgradeTower(game::TowerID tower, game::TowerLevel level);
void RemoveTower(game::TowerID tower);
private:
void Reset();
}; };
} // namespace client } // namespace client

View File

@@ -9,28 +9,30 @@ namespace client {
class ClientConnexion : public protocol::Connexion { class ClientConnexion : public protocol::Connexion {
private: private:
std::uint8_t m_ConnectionID; std::uint8_t m_ConnectionID;
std::string m_DisconnectReason; std::string m_DisconnectReason;
float m_ServerTPS; float m_ServerTPS;
int m_Ping = 0; float m_ServerMSPT;
int m_Ping = 0;
public: public:
ClientConnexion(); ClientConnexion();
virtual bool updateSocket(); virtual bool UpdateSocket();
virtual void HandlePacket(const protocol::KeepAlivePacket* packet); virtual void HandlePacket(const protocol::KeepAlivePacket* packet) override;
virtual void HandlePacket(const protocol::ConnexionInfoPacket* packet); virtual void HandlePacket(const protocol::ConnexionInfoPacket* packet) override;
virtual void HandlePacket(const protocol::DisconnectPacket* packet); virtual void HandlePacket(const protocol::DisconnectPacket* packet) override;
virtual void HandlePacket(const protocol::ServerTpsPacket* packet); virtual void HandlePacket(const protocol::ServerTpsPacket* packet) override;
const std::string& getDisconnectReason() const { return m_DisconnectReason; } const std::string& GetDisconnectReason() const { return m_DisconnectReason; }
float getServerTPS() const { return m_ServerTPS; } float GetServerTPS() const { return m_ServerTPS; }
int getServerPing() const { return m_Ping; } float GetServerMSPT() const { return m_ServerMSPT; }
int GetServerPing() const { return m_Ping; }
REMOVE_COPY(ClientConnexion); REMOVE_COPY(ClientConnexion);
private: private:
void registerHandlers(); void RegisterHandlers();
void login(); void Login();
}; };
} // namespace client } // namespace client

View File

@@ -16,39 +16,43 @@ class Client;
class ClientGame : public protocol::PacketHandler, public game::Game { class ClientGame : public protocol::PacketHandler, public game::Game {
private: private:
Client* m_Client; Client* m_Client;
std::uint8_t m_ConnexionID; std::uint8_t m_ConnexionID;
std::uint32_t m_LobbyTime = 0; std::uint32_t m_LobbyTime = 0;
game::Player* m_Player = nullptr; game::Player* m_Player = nullptr;
render::Renderer* m_Renderer; render::Renderer* m_Renderer;
client::WorldClient m_WorldClient; client::WorldClient m_WorldClient;
render::WorldRenderer m_WorldRenderer; render::WorldRenderer m_WorldRenderer;
public: public:
ClientGame(Client* client); ClientGame(Client* client);
virtual ~ClientGame(); virtual ~ClientGame();
virtual void tick(std::uint64_t delta); virtual void Tick(std::uint64_t delta);
void renderWorld(); void RenderWorld();
std::uint32_t getLobbyTime() const { return m_LobbyTime; } std::uint32_t GetLobbyTime() const { return m_LobbyTime; }
const game::Player* getPlayer() const { return m_Player; } const game::Player* GetPlayer() const { return m_Player; }
const WorldClient& getWorld() const { return m_WorldClient; } const WorldClient& GetWorld() const { return m_WorldClient; }
Client* getClient() const { return m_Client; } Client* GetClient() const { return m_Client; }
render::Renderer* getRenderer() const { return m_Renderer; } render::Renderer* GetRenderer() const { return m_Renderer; }
WorldClient& GetWorldClient() { return m_WorldClient; }
void SetGameState(game::GameState gameState) override;
virtual void HandlePacket(const protocol::ConnexionInfoPacket* packet) override;
virtual void HandlePacket(const protocol::PlayerJoinPacket* packet) override;
virtual void HandlePacket(const protocol::PlayerLeavePacket* packet) override;
virtual void HandlePacket(const protocol::PlayerListPacket* packet) override;
virtual void HandlePacket(const protocol::UpdatePlayerTeamPacket* packet) override;
virtual void HandlePacket(const protocol::UpdateGameStatePacket* packet) override;
virtual void HandlePacket(const protocol::UpdateLobbyTimePacket* packet) override;
virtual void HandlePacket(const protocol::UpdateMoneyPacket* packet) override;
virtual void HandlePacket(const protocol::UpdateExpPacket* packet) override;
virtual void HandlePacket(const protocol::DisconnectPacket* packet) override;
virtual void HandlePacket(const protocol::WorldDataPacket* packet) override;
virtual void HandlePacket(const protocol::ConnexionInfoPacket* packet);
virtual void HandlePacket(const protocol::PlayerJoinPacket* packet);
virtual void HandlePacket(const protocol::PlayerLeavePacket* packet);
virtual void HandlePacket(const protocol::PlayerListPacket* packet);
virtual void HandlePacket(const protocol::UpdatePlayerTeamPacket* packet);
virtual void HandlePacket(const protocol::UpdateGameStatePacket* packet);
virtual void HandlePacket(const protocol::UpdateLobbyTimePacket* packet);
virtual void HandlePacket(const protocol::UpdateMoneyPacket* packet);
virtual void HandlePacket(const protocol::DisconnectPacket* packet);
virtual void HandlePacket(const protocol::WorldDataPacket* packet);
virtual void HandlePacket(const protocol::WorldAddTowerPacket* packet);
}; };
} // namespace client } // namespace client

View File

@@ -10,16 +10,19 @@ class ClientGame;
class WorldClient : public game::World, public protocol::PacketHandler { class WorldClient : public game::World, public protocol::PacketHandler {
private: private:
ClientGame* m_Game; ClientGame* m_Game;
public: public:
WorldClient(ClientGame* game); WorldClient(ClientGame* game);
virtual void HandlePacket(const protocol::WorldBeginDataPacket* packet) override; virtual void HandlePacket(const protocol::WorldBeginDataPacket* packet) override;
virtual void HandlePacket(const protocol::WorldDataPacket* packet) override; virtual void HandlePacket(const protocol::WorldDataPacket* packet) override;
virtual void HandlePacket(const protocol::SpawnMobPacket* packet) override; virtual void HandlePacket(const protocol::SpawnMobPacket* packet) override;
virtual void HandlePacket(const protocol::UpgradeTowerPacket* packet) override; virtual void HandlePacket(const protocol::UpgradeTowerPacket* packet) override;
virtual void HandlePacket(const protocol::WorldAddTowerPacket* packet) override;
virtual void HandlePacket(const protocol::RemoveTowerPacket* packet) override;
virtual void HandlePacket(const protocol::UpdateMobStatesPacket* packet) override;
virtual void HandlePacket(const protocol::UpdateCastleLifePacket* packet) override;
virtual void OnArrowShot(game::MobPtr target, game::Tower* shooter) override;
}; };
} // namespace client } // namespace client

View File

@@ -11,23 +11,23 @@ class Server;
class Lobby { class Lobby {
private: private:
Server* m_Server; Server* m_Server;
bool m_GameStarted = false; bool m_GameStarted = false;
std::uint64_t m_StartTimerTime = 0; std::uint64_t m_StartTimerTime = 0;
std::vector<std::uint8_t> m_Players; std::vector<std::uint8_t> m_Players;
utils::AutoTimer m_Timer; utils::AutoTimer m_Timer;
public: public:
Lobby(Server* server); Lobby(Server* server);
void OnPlayerJoin(std::uint8_t playerID); void OnPlayerJoin(std::uint8_t playerID);
void OnPlayerLeave(std::uint8_t playerID); void OnPlayerLeave(std::uint8_t playerID);
void sendTimeRemaining(); void SendTimeRemaining();
void tick(); void Tick();
//static constexpr int LobbyWaitingTime = 2 * 60 * 1000; // in millis //static constexpr int LobbyWaitingTime = 2 * 60 * 1000; // in millis
static constexpr int LobbyWaitingTime = 5 * 1000; // in millis static constexpr int LobbyWaitingTime = 5 * 1000; // in millis
}; };
} // namespace server } // namespace server

View File

@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cstdint>
#include <map> #include <map>
#include <thread> #include <thread>
@@ -22,78 +21,84 @@ typedef std::map<std::uint8_t, ServerConnexion> ConnexionMap;
class TickCounter { class TickCounter {
private: private:
float m_TPS; float m_TPS;
std::uint64_t m_LastTPSTime; float m_MSPT;
std::uint8_t m_TickCount; std::uint64_t m_LastTPSTime;
std::uint8_t m_TickCount;
public: public:
TickCounter() {} TickCounter() {}
void reset() { void Reset() {
m_TPS = SERVER_TPS; m_TPS = SERVER_TPS;
m_LastTPSTime = utils::getTime(); m_LastTPSTime = utils::GetTime();
m_TickCount = 0; m_TickCount = 0;
} }
bool update() { // return true when tps is updated bool Update() { // return true when tps is updated
m_TickCount++; m_TickCount++;
if (m_TickCount >= SERVER_TPS) { if (m_TickCount >= SERVER_TPS) {
std::uint64_t timeElapsedSinceLast20Ticks = td::utils::getTime() - m_LastTPSTime; std::uint64_t timeElapsedSinceLast20Ticks = td::utils::GetTime() - m_LastTPSTime;
m_TPS = (float)SERVER_TPS / (float)(timeElapsedSinceLast20Ticks / 1000.0f); m_TPS = static_cast<float>(SERVER_TPS) / static_cast<float>(timeElapsedSinceLast20Ticks / 1000.0f);
m_TickCount = 0; m_TickCount = 0;
m_LastTPSTime = td::utils::getTime(); m_LastTPSTime = td::utils::GetTime();
return true; return true;
} }
return false; return false;
} }
float getTPS() const { return m_TPS; } float GetTPS() const { return m_TPS; }
float GetMSPT() const { return m_MSPT; }
void SetMSPT(float mspt) { m_MSPT = mspt; }
}; };
class Server { class Server {
private: private:
network::TCPListener m_Listener; network::TCPListener m_Listener;
ConnexionMap m_Connections; ConnexionMap m_Connections;
ServerGame m_Game{ this }; ServerGame m_Game{ this };
Lobby m_Lobby{ this }; Lobby m_Lobby{ this };
TickCounter m_TickCounter; TickCounter m_TickCounter;
std::thread m_Thread; std::thread m_Thread;
bool m_ServerRunning; bool m_ServerRunning;
public: public:
Server(const std::string& worldFilePath); Server(const std::string& worldFilePath);
virtual ~Server() {} virtual ~Server();
bool start(std::uint16_t port); bool Start(std::uint16_t port);
void stop(); void Stop(); // force the server to stop
void Close(); // at the end of a game
void lauchGame(); void RemoveConnexion(std::uint8_t connexionID);
void removeConnexion(std::uint8_t connexionID); void BroadcastPacket(const protocol::Packet* packet);
void broadcastPacket(const protocol::Packet* packet); float GetTPS() const { return m_TickCounter.GetTPS(); }
float getTPS() const { return m_TickCounter.getTPS(); } bool IsRunning() { return m_ServerRunning; }
const ServerGame& getGame() const { return m_Game; } const ServerGame& GetGame() const { return m_Game; }
ServerGame& getGame() { return m_Game; } ServerGame& GetGame() { return m_Game; }
const Lobby& getLobby() const { return m_Lobby; } const Lobby& GetLobby() const { return m_Lobby; }
const ConnexionMap& getConnexions() const { return m_Connections; } const ConnexionMap& GetConnexions() const { return m_Connections; }
ConnexionMap& getConnexions() { return m_Connections; } ConnexionMap& GetConnexions() { return m_Connections; }
const game::PlayerList& getPlayers() const { return m_Game.getPlayers(); } const game::PlayerList& GetPlayers() const { return m_Game.GetPlayers(); }
game::PlayerList& getPlayers() { return m_Game.getPlayers(); } game::PlayerList& GetPlayers() { return m_Game.GetPlayers(); }
private: private:
void accept(); void Accept();
void updateSockets(); void UpdateSockets();
void startThread(); void Clean();
void stopThread(); void StartThread();
void tick(std::uint64_t delta); void StopThread();
void Tick(std::uint64_t delta);
void OnPlayerJoin(std::uint8_t id); void OnPlayerJoin(std::uint8_t id);
void OnPlayerLeave(std::uint8_t id); void OnPlayerLeave(std::uint8_t id);
}; };
} // namespace server } // namespace server

View File

@@ -11,48 +11,48 @@ namespace server {
class Server; class Server;
struct KeepAlive struct KeepAlive {
{ std::uint64_t keepAliveID = 0;
std::uint64_t keepAliveID = 0; std::uint64_t sendTime;
std::uint64_t sendTime; bool recievedResponse = false;
bool recievedResponse = false;
}; };
class ServerConnexion : public protocol::Connexion { class ServerConnexion : public protocol::Connexion {
private: private:
Server* m_Server = nullptr; Server* m_Server = nullptr;
std::uint8_t m_ID; std::uint8_t m_ID;
KeepAlive m_KeepAlive; KeepAlive m_KeepAlive;
game::Player* m_Player; game::Player* m_Player;
public: public:
ServerConnexion(); ServerConnexion();
ServerConnexion(network::TCPSocket& socket, std::uint8_t id); ServerConnexion(network::TCPSocket& socket, std::uint8_t id);
ServerConnexion(ServerConnexion&& move); ServerConnexion(ServerConnexion&& move);
virtual ~ServerConnexion(); virtual ~ServerConnexion();
void setServer(Server* server); void SetServer(Server* server);
virtual void HandlePacket(const protocol::PlayerLoginPacket* packet); virtual void HandlePacket(const protocol::PlayerLoginPacket* packet) override;
virtual void HandlePacket(const protocol::KeepAlivePacket* packet); virtual void HandlePacket(const protocol::KeepAlivePacket* packet) override;
virtual void HandlePacket(const protocol::SelectTeamPacket* packet); virtual void HandlePacket(const protocol::SelectTeamPacket* packet) override;
virtual void HandlePacket(const protocol::DisconnectPacket* packet); virtual void HandlePacket(const protocol::DisconnectPacket* packet) override;
virtual void HandlePacket(const protocol::PlaceTowerPacket* packet); virtual void HandlePacket(const protocol::PlaceTowerPacket* packet) override;
virtual void HandlePacket(const protocol::SendMobsPacket* packet); virtual void HandlePacket(const protocol::SendMobsPacket* packet) override;
virtual void HandlePacket(const protocol::UpgradeTowerPacket* packet); virtual void HandlePacket(const protocol::UpgradeTowerPacket* packet) override;
virtual void HandlePacket(const protocol::RemoveTowerPacket* packet) override;
std::uint8_t getID() const { return m_ID; } std::uint8_t GetID() const { return m_ID; }
const game::Player* getPlayer() const { return m_Player; } const game::Player* GetPlayer() const { return m_Player; }
game::Player* getPlayer() { return m_Player; } game::Player* GetPlayer() { return m_Player; }
virtual bool updateSocket(); virtual bool UpdateSocket();
REMOVE_COPY(ServerConnexion); REMOVE_COPY(ServerConnexion);
private: private:
void registerHandlers(); void RegisterHandlers();
void checkKeepAlive(); void CheckKeepAlive();
void sendKeepAlive(); void SendKeepAlive();
void initConnection(); void InitConnection();
}; };
} // namespace server } // namespace server

View File

@@ -9,23 +9,34 @@ namespace server {
class Server; class Server;
class ServerGame : public game::Game { class ServerGame : public game::Game, public game::GameListener {
private: private:
Server* m_Server; Server* m_Server;
ServerWorld m_ServerWorld; ServerWorld m_ServerWorld;
utils::AutoTimer m_GoldMineTimer{ 1000, std::bind(&ServerGame::updateGoldMines, this) }; utils::AutoTimer m_GoldMineTimer;
utils::AutoTimer m_MobStatesTimer;
utils::CooldownTimer m_EndGameCooldown;
public: public:
ServerGame(Server* server); ServerGame(Server* server);
~ServerGame() {} ~ServerGame() {}
ServerWorld* getServerWorld() { return &m_ServerWorld; } ServerWorld* GetServerWorld() { return &m_ServerWorld; }
virtual void tick(std::uint64_t delta); virtual void Tick(std::uint64_t delta);
void startGame(); void StartGame();
// GameListener
virtual void OnGameStateUpdate(game::GameState newState) override;
virtual void OnGameBegin() override;
virtual void OnGameEnd() override;
virtual void OnGameClose() override;
private: private:
void balanceTeams(); void BalanceTeams();
void updateGoldMines(); void InitPlayerStats();
void updatePlayerStats(); void UpdateMobStates();
void UpdateGoldMines();
void UpdatePlayerStats();
}; };
} // namespace game } // namespace game

View File

@@ -10,16 +10,20 @@ class ServerGame;
class ServerWorld : public game::World { class ServerWorld : public game::World {
private: private:
game::MobID m_CurrentMobID; game::MobID m_CurrentMobID;
game::TowerID m_CurrentTowerID; game::TowerID m_CurrentTowerID;
Server* m_Server; Server* m_Server;
public: public:
ServerWorld(Server* server, ServerGame* game); static constexpr float MobSpawnBorder = 0.01f;
void spawnMobs(game::MobType type, std::uint8_t level, game::PlayerID sender, std::uint8_t count); ServerWorld(Server* server, ServerGame* game);
game::TowerPtr placeTowerAt(game::TowerType type, std::int32_t x, std::int32_t y, game::PlayerID builder);
void SpawnMobs(game::MobType type, std::uint8_t level, game::PlayerID sender, std::uint8_t count);
game::TowerPtr PlaceTowerAt(game::TowerType type, std::int32_t x, std::int32_t y, game::PlayerID builder);
virtual void OnMobDie(game::Mob* mob) override;
virtual void OnMobCastleDamage(game::Mob* damager, game::TeamCastle* enemyCastle, float damage) override;
virtual void OnArrowShot(game::MobPtr target, game::Tower* shooter) override;
}; };
} // namespace server } // namespace server

4465
include/misc/Backward.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,9 @@
namespace td { namespace td {
namespace utils { namespace utils {
std::uint64_t Inflate(const std::string& source, std::string& dest);
std::uint64_t Deflate(const std::string& source, std::string& dest);
DataBuffer Compress(const DataBuffer& buffer); DataBuffer Compress(const DataBuffer& buffer);
DataBuffer Decompress(DataBuffer& buffer); DataBuffer Decompress(DataBuffer& buffer);
DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength); DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength);

View File

@@ -1,5 +1,4 @@
#ifndef MCLIB_COMMON_DATA_BUFFER_H_ #pragma once
#define MCLIB_COMMON_DATA_BUFFER_H_
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
@@ -11,157 +10,168 @@ namespace td {
class DataBuffer { class DataBuffer {
private: private:
typedef std::vector<std::uint8_t> Data; typedef std::vector<std::uint8_t> Data;
Data m_Buffer; Data m_Buffer;
std::size_t m_ReadOffset = 0; std::size_t m_ReadOffset;
public: public:
typedef Data::iterator iterator; typedef Data::iterator iterator;
typedef Data::const_iterator const_iterator; typedef Data::const_iterator const_iterator;
typedef Data::reference reference; typedef Data::reference reference;
typedef Data::const_reference const_reference; typedef Data::const_reference const_reference;
DataBuffer(); DataBuffer();
DataBuffer(const DataBuffer& other); DataBuffer(const DataBuffer& other);
DataBuffer(const DataBuffer& other, std::size_t offset); DataBuffer(const DataBuffer& other, Data::difference_type offset);
DataBuffer(DataBuffer&& other); DataBuffer(DataBuffer&& other);
DataBuffer(const std::string& str); DataBuffer(const std::string& str);
DataBuffer& operator=(const DataBuffer& other); DataBuffer& operator=(const DataBuffer& other);
DataBuffer& operator=(DataBuffer&& other); DataBuffer& operator=(DataBuffer&& other);
template <typename T> template <typename T>
void Append(T data) { void Append(T data) {
std::size_t size = sizeof(data); std::size_t size = sizeof(data);
std::size_t end_pos = m_Buffer.size(); std::size_t end_pos = m_Buffer.size();
m_Buffer.resize(m_Buffer.size() + size); m_Buffer.resize(m_Buffer.size() + size);
memcpy(&m_Buffer[end_pos], &data, size); memcpy(&m_Buffer[end_pos], &data, size);
} }
template <typename T> template <typename T>
DataBuffer& operator<<(T data) { DataBuffer& operator<<(T data) {
// Switch to big endian // Switch to big endian
//std::reverse((std::uint8_t*)&data, (std::uint8_t*)&data + sizeof(T)); //std::reverse((std::uint8_t*)&data, (std::uint8_t*)&data + sizeof(T));
Append(data); Append(data);
return *this; return *this;
} }
DataBuffer& operator<<(std::string str) { DataBuffer& operator<<(std::string str) {
m_Buffer.insert(m_Buffer.end(), str.begin(), str.end()); m_Buffer.insert(m_Buffer.end(), str.begin(), str.end());
return *this; return *this;
} }
DataBuffer& operator<<(DataBuffer& data) { DataBuffer& operator<<(DataBuffer& data) {
m_Buffer.insert(m_Buffer.end(), data.begin(), data.end()); m_Buffer.insert(m_Buffer.end(), data.begin(), data.end());
return *this; return *this;
} }
DataBuffer& operator<<(const DataBuffer& data) { DataBuffer& operator<<(const DataBuffer& data) {
m_Buffer.insert(m_Buffer.end(), data.begin(), data.end()); m_Buffer.insert(m_Buffer.end(), data.begin(), data.end());
return *this; return *this;
} }
template <typename T> template <typename T>
DataBuffer& operator>>(T& data) { DataBuffer& operator>>(T& data) {
assert(m_ReadOffset + sizeof(T) <= GetSize()); assert(m_ReadOffset + sizeof(T) <= GetSize());
data = *(T*)&m_Buffer[m_ReadOffset]; data = *(reinterpret_cast<T*>(&m_Buffer[m_ReadOffset]));
//std::reverse((std::uint8_t*)&data, (std::uint8_t*)&data + sizeof(T)); //std::reverse((std::uint8_t*)&data, (std::uint8_t*)&data + sizeof(T));
m_ReadOffset += sizeof(T); m_ReadOffset += sizeof(T);
return *this; return *this;
} }
DataBuffer& operator>>(DataBuffer& data) { DataBuffer& operator>>(DataBuffer& data) {
data.Resize(GetSize() - m_ReadOffset); data.Resize(GetSize() - m_ReadOffset);
std::copy(m_Buffer.begin() + m_ReadOffset, m_Buffer.end(), data.begin()); std::copy(m_Buffer.begin() + static_cast<Data::difference_type>(m_ReadOffset), m_Buffer.end(), data.begin());
m_ReadOffset = m_Buffer.size(); m_ReadOffset = m_Buffer.size();
return *this; return *this;
} }
DataBuffer& operator>>(std::string& str) { DataBuffer& operator>>(std::string& str) {
std::size_t stringSize = strlen((const char*)m_Buffer.data() + m_ReadOffset) + 1; // including null character std::size_t stringSize = strlen(reinterpret_cast<const char*>(m_Buffer.data()) + m_ReadOffset) + 1; // including null character
str.resize(stringSize); str.resize(stringSize);
std::copy(m_Buffer.begin() + m_ReadOffset, m_Buffer.begin() + m_ReadOffset + stringSize, str.begin()); std::copy(m_Buffer.begin() + static_cast<Data::difference_type>(m_ReadOffset),
m_ReadOffset += stringSize; m_Buffer.begin() + static_cast<Data::difference_type>(m_ReadOffset + stringSize), str.begin());
return *this; m_ReadOffset += stringSize;
} return *this;
}
void ReadSome(char* buffer, std::size_t amount) { void WriteSome(const char* buffer, std::size_t amount) {
assert(m_ReadOffset + amount <= GetSize()); std::size_t end_pos = m_Buffer.size();
std::copy_n(m_Buffer.begin() + m_ReadOffset, amount, buffer); m_Buffer.resize(m_Buffer.size() + amount);
m_ReadOffset += amount; memcpy(m_Buffer.data() + end_pos, buffer, amount);
} }
void ReadSome(std::uint8_t* buffer, std::size_t amount) { void WriteSome(const std::uint8_t* buffer, std::size_t amount) {
assert(m_ReadOffset + amount <= GetSize()); std::size_t end_pos = m_Buffer.size();
std::copy_n(m_Buffer.begin() + m_ReadOffset, amount, buffer); m_Buffer.resize(m_Buffer.size() + amount);
m_ReadOffset += amount; memcpy(m_Buffer.data() + end_pos, buffer, amount);
} }
void ReadSome(DataBuffer& buffer, std::size_t amount) { void ReadSome(char* buffer, std::size_t amount) {
assert(m_ReadOffset + amount <= GetSize()); assert(m_ReadOffset + amount <= GetSize());
buffer.Resize(amount); std::copy_n(m_Buffer.begin() + static_cast<Data::difference_type>(m_ReadOffset), amount, buffer);
std::copy_n(m_Buffer.begin() + m_ReadOffset, amount, buffer.begin()); m_ReadOffset += amount;
m_ReadOffset += amount; }
}
void ReadSome(std::string& buffer, std::size_t amount) { void ReadSome(std::uint8_t* buffer, std::size_t amount) {
assert(m_ReadOffset + amount <= GetSize()); assert(m_ReadOffset + amount <= GetSize());
buffer.resize(amount); std::copy_n(m_Buffer.begin() + static_cast<Data::difference_type>(m_ReadOffset), amount, buffer);
std::copy_n(m_Buffer.begin() + m_ReadOffset, amount, buffer.begin()); m_ReadOffset += amount;
m_ReadOffset += amount; }
}
void Resize(std::size_t size) { void ReadSome(DataBuffer& buffer, std::size_t amount) {
m_Buffer.resize(size); assert(m_ReadOffset + amount <= GetSize());
} buffer.Resize(amount);
std::copy_n(m_Buffer.begin() + static_cast<Data::difference_type>(m_ReadOffset), amount, buffer.begin());
m_ReadOffset += amount;
}
void Reserve(std::size_t amount) { void ReadSome(std::string& buffer, std::size_t amount) {
m_Buffer.reserve(amount); assert(m_ReadOffset + amount <= GetSize());
} buffer.resize(amount);
std::copy_n(m_Buffer.begin() + static_cast<Data::difference_type>(m_ReadOffset), amount, buffer.begin());
m_ReadOffset += amount;
}
void erase(iterator it) { void Resize(std::size_t size) {
m_Buffer.erase(it); m_Buffer.resize(size);
} }
void Clear() { void Reserve(std::size_t amount) {
m_Buffer.clear(); m_Buffer.reserve(amount);
m_ReadOffset = 0; }
}
bool IsFinished() const { void erase(iterator it) {
return m_ReadOffset >= m_Buffer.size(); m_Buffer.erase(it);
} }
std::uint8_t* data() { void Clear() {
return m_Buffer.data(); m_Buffer.clear();
} m_ReadOffset = 0;
}
const std::uint8_t* data() const { bool IsFinished() const {
return m_Buffer.data(); return m_ReadOffset >= m_Buffer.size();
} }
std::size_t GetReadOffset() const { return m_ReadOffset; } std::uint8_t* data() {
void SetReadOffset(std::size_t pos); return m_Buffer.data();
}
std::string ToString() const; const std::uint8_t* data() const {
std::size_t GetSize() const; return m_Buffer.data();
bool IsEmpty() const; }
std::size_t GetRemaining() const;
iterator begin(); std::size_t GetReadOffset() const { return m_ReadOffset; }
iterator end(); void SetReadOffset(std::size_t pos);
const_iterator begin() const;
const_iterator end() const;
reference operator[](Data::size_type i) { return m_Buffer[i]; } std::string ToString() const;
const_reference operator[](Data::size_type i) const { return m_Buffer[i]; } std::size_t GetSize() const;
bool IsEmpty() const;
std::size_t GetRemaining() const;
bool ReadFile(const std::string& fileName); iterator begin();
bool WriteFile(const std::string& fileName); iterator end();
const_iterator begin() const;
const_iterator end() const;
reference operator[](Data::size_type i) { return m_Buffer[i]; }
const_reference operator[](Data::size_type i) const { return m_Buffer[i]; }
bool ReadFile(const std::string& fileName);
bool WriteFile(const std::string& fileName);
}; };
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer); std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer);
} // ns td } // ns td
#endif

15
include/misc/DiscordRPC.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include <string>
namespace td {
namespace utils {
void InitDiscordRPC();
void UpdateDiscordRPC();
void ShutdownDiscordRPC();
void UpdateDiscordPresence(const std::string& state, const std::string& details, bool resetTimer = false);
} // namespace utils
} // namespace td

View File

@@ -7,63 +7,63 @@ constexpr float PI = 3.14159274101257324219;
/* Sine functions */ /* Sine functions */
float easeInSine(float x); float EaseInSine(float x);
float easeOutSine(float x); float EaseOutSine(float x);
float easeInOutSine(float x); float EaseInOutSine(float x);
/* Cubic functions */ /* Cubic functions */
float easeInCubic(float x); float EaseInCubic(float x);
float easeOutCubic(float x); float EaseOutCubic(float x);
float easeInOutCubic(float x); float EaseInOutCubic(float x);
/* Quint functions */ /* Quint functions */
float easeInQuint(float x); float EaseInQuint(float x);
float easeOutQuint(float x); float EaseOutQuint(float x);
float easeInOutQuint(float x); float EaseInOutQuint(float x);
/* Circ functions */ /* Circ functions */
float easeInCirc(float x); float EaseInCirc(float x);
float easeOutCirc(float x); float EaseOutCirc(float x);
float easeInOutCirc(float x); float EaseInOutCirc(float x);
/* Elastic functions */ /* Elastic functions */
float easeInElastic(float x); float EaseInElastic(float x);
float easeOutElastic(float x); float EaseOutElastic(float x);
float easeInOutElastic(float x); float EaseInOutElastic(float x);
/* Quad functions */ /* Quad functions */
float easeInQuad(float x); float EaseInQuad(float x);
float easeOutQuad(float x); float EaseOutQuad(float x);
float easeInOutQuad(float x); float EaseInOutQuad(float x);
/* Quart functions */ /* Quart functions */
float easeInQuart(float x); float EaseInQuart(float x);
float easeOutQuart(float x); float EaseOutQuart(float x);
float easeInOutQuart(float x); float EaseInOutQuart(float x);
/* Expo functions */ /* Expo functions */
float easeInExpo(float x); float EaseInExpo(float x);
float easeOutExpo(float x); float EaseOutExpo(float x);
float easeInOutExpo(float x); float EaseInOutExpo(float x);
/* Back functions */ /* Back functions */
float easeInBack(float x); float EaseInBack(float x);
float easeOutBack(float x); float EaseOutBack(float x);
float easeInOutBack(float x); float EaseInOutBack(float x);
/* Bounce functions */ /* Bounce functions */
float easeInBounce(float x); float EaseInBounce(float x);
float easeOutBounce(float x); float EaseOutBounce(float x);
float easeInOutBounce(float x); float EaseInOutBounce(float x);
} // namespace utils } // namespace utils
} // namespace td } // namespace td

23
include/misc/Format.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <string>
#include <memory>
#include <stdexcept>
namespace td {
namespace utils {
template <typename... Args>
std::string format(const std::string& format, Args... args) {
int size = snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
if (size <= 0) {
throw std::runtime_error("Error during formatting.");
}
std::unique_ptr<char[]> buf(new char[size]);
snprintf(buf.get(), static_cast<std::size_t>(size), format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
} // namespace utils
} // namespace td

13
include/misc/Log.h Normal file
View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
namespace td {
namespace utils {
void LOG(const std::string& msg);
void LOGD(const std::string& msg);
void LOGE(const std::string& err);
} // namespace utils
} // namespace td

View File

@@ -10,26 +10,26 @@ namespace utils {
template <typename Listener> template <typename Listener>
class ObjectNotifier { class ObjectNotifier {
protected: protected:
std::vector<Listener*> m_Listeners; std::vector<Listener*> m_Listeners;
public: public:
void bindListener(Listener* listener) { void BindListener(Listener* listener) {
m_Listeners.push_back(listener); m_Listeners.push_back(listener);
} }
void unbindListener(Listener* listener) { void UnbindListener(Listener* listener) {
auto iter = std::find(m_Listeners.begin(), m_Listeners.end(), listener); auto iter = std::find(m_Listeners.begin(), m_Listeners.end(), listener);
if(iter == m_Listeners.end()) return; if (iter == m_Listeners.end()) return;
m_Listeners.erase(iter); m_Listeners.erase(iter);
} }
template <typename Func, typename... Args> template <typename Func, typename... Args>
void notifyListeners(Func function, Args... args) { void NotifyListeners(Func function, Args... args) {
for (Listener* listener : m_Listeners) for (Listener* listener : m_Listeners)
std::bind(function, listener, args...)(); std::bind(function, listener, args...)();
} }
}; };
} // namespace utils } // namespace utils

View File

@@ -4,49 +4,49 @@ namespace td {
namespace utils { namespace utils {
enum class Os { enum class Os {
Windows = 0, Windows = 0,
Linux, Linux,
Android, Android,
Unknown, Unknown,
}; };
enum class Architecture { enum class Architecture {
x86_64 = 0, x86_64 = 0,
x86, x86,
Arm64, Arm64,
Armhf, Armhf,
Unknown, Unknown,
}; };
inline Os getSystemOs() { inline Os GetSystemOs() {
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
return Os::Windows; return Os::Windows;
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
return Os::Android; return Os::Android;
#elif defined(__unix__) #elif defined(__unix__)
return Os::Linux; return Os::Linux;
#else #else
#pragma message ("Target OS unknown or unsupported !") #pragma message ("Target OS unknown or unsupported !")
return Os::Unknown; return Os::Unknown;
#endif #endif
} }
inline Architecture getSystemArchitecture() { inline Architecture GetSystemArchitecture() {
#if defined(_WIN64) #if defined(_WIN64)
return Architecture::x86_64; return Architecture::x86_64;
#elif defined(_WIN32) #elif defined(_WIN32)
return Architecture::x86; return Architecture::x86;
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) #elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
return Architecture::x86_64; return Architecture::x86_64;
#elif defined(_M_IX86) || defined(_X86_) || defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) #elif defined(_M_IX86) || defined(_X86_) || defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
return Architecture::x86; return Architecture::x86;
#elif defined(__aarch64__) #elif defined(__aarch64__)
return Architecture::Arm64; return Architecture::Arm64;
#elif defined(__arm__) || defined(_M_ARM) #elif defined(__arm__) || defined(_M_ARM)
return Architecture::Armhf; return Architecture::Armhf;
#else #else
#pragma message ("Target CPU architecture unknown or unsupported !") #pragma message ("Target CPU architecture unknown or unsupported !")
return Architecture::Unknown; return Architecture::Unknown;
#endif #endif
} }

View File

@@ -6,19 +6,19 @@ namespace td {
namespace utils { namespace utils {
template<typename NumberType> template<typename NumberType>
NumberType getRandomInt(NumberType min, NumberType max) { NumberType GetRandomInt(NumberType min, NumberType max) {
std::random_device randomDevice; std::random_device randomDevice;
std::mt19937 generator(randomDevice()); std::mt19937 generator(randomDevice());
std::uniform_int_distribution<NumberType> distrib(min, max); std::uniform_int_distribution<NumberType> distrib(min, max);
return distrib(generator); return distrib(generator);
} }
template<typename NumberType> template<typename NumberType>
NumberType getRandomReal(NumberType min, NumberType max) { NumberType GetRandomReal(NumberType min, NumberType max) {
std::random_device randomDevice; std::random_device randomDevice;
std::mt19937 generator(randomDevice()); std::mt19937 generator(randomDevice());
std::uniform_real_distribution<NumberType> distrib(min, max); std::uniform_real_distribution<NumberType> distrib(min, max);
return distrib(generator); return distrib(generator);
} }
} // namespace utils } // namespace utils

View File

@@ -8,84 +8,87 @@ namespace shape {
class Point { class Point {
private: private:
float m_X, m_Y; float m_X, m_Y;
public: public:
Point() : m_X(0), m_Y(0) {} Point() : m_X(0), m_Y(0) {}
Point(float x, float y) : m_X(x), m_Y(y) {} Point(float x, float y) : m_X(x), m_Y(y) {}
float getX() const { return m_X; } float GetX() const { return m_X; }
float getY() const { return m_Y; } float GetY() const { return m_Y; }
void setX(float x) { m_X = x; } void SetX(float x) { m_X = x; }
void setY(float y) { m_Y = y; } void SetY(float y) { m_Y = y; }
float distance(const Point& point) const; float Distance(const Point& point) const;
float distanceSquared(const Point& point) const; float DistanceSquared(const Point& point) const;
}; };
class Circle; class Circle;
class Rectangle { class Rectangle {
private: private:
Point m_Center; Point m_Center;
float m_Width, m_Height; float m_Width, m_Height;
public: public:
Rectangle() {} Rectangle() {}
const Point& getCenter() const { return m_Center; } const Point& GetCenter() const { return m_Center; }
float getCenterX() const { return m_Center.getX(); } float GetCenterX() const { return m_Center.GetX(); }
float getCenterY() const { return m_Center.getY(); } float GetCenterY() const { return m_Center.GetY(); }
float getWidth() const { return m_Width; } float GetWidth() const { return m_Width; }
float getHeight() const { return m_Height; } 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 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) }; } 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 SetCenter(const Point& center) { m_Center = center; }
void setCenterX(float x) { m_Center.setX(x); } void SetCenterX(float x) { m_Center.SetX(x); }
void setCenterY(float y) { m_Center.setY(y); } void SetCenterY(float y) { m_Center.SetY(y); }
void setSize(float width, float height) { setWidth(width); setHeight(height); } void SetSize(float width, float height) { SetWidth(width); SetHeight(height); }
void setWidth(float width) { m_Width = width; } void SetSize(Point size) { SetSize(size.GetX(), size.GetY()); }
void setHeight(float height) { m_Height = height; } Point GetSize() { return { m_Width, m_Height }; }
bool collidesWith(const Point& point) const; void SetWidth(float width) { m_Width = width; }
bool collidesWith(const Rectangle& rect) const; void SetHeight(float height) { m_Height = height; }
bool collidesWith(const Circle& circle) const;
// distance from the closest side of the rectangle bool CollidesWith(const Point& point) const;
float distance(const Circle& circle) const; bool CollidesWith(const Rectangle& rect) const;
float distanceSquared(const Circle& circle) 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 { class Circle {
private: private:
Point m_Center; Point m_Center;
float m_Radius; float m_Radius;
public: public:
Circle(float x, float y, float radius) : m_Center(x, y), m_Radius(radius) {} Circle(float x, float y, float radius) : m_Center(x, y), m_Radius(radius) {}
Circle() : m_Radius(0) {} Circle() : m_Radius(0) {}
const Point& getCenter() const { return m_Center; } const Point& GetCenter() const { return m_Center; }
float getCenterX() const { return m_Center.getX(); } float GetCenterX() const { return m_Center.GetX(); }
float getCenterY() const { return m_Center.getY(); } float GetCenterY() const { return m_Center.GetY(); }
float getRadius() const { return m_Radius; } float GetRadius() const { return m_Radius; }
void setCenter(const Point& center) { m_Center = center; } void SetCenter(const Point& center) { m_Center = center; }
void setCenterX(float x) { m_Center.setX(x); } void SetCenterX(float x) { m_Center.SetX(x); }
void setCenterY(float y) { m_Center.setY(y); } void SetCenterY(float y) { m_Center.SetY(y); }
void setRadius(float radius) { m_Radius = radius; } void SetRadius(float radius) { m_Radius = radius; }
bool collidesWith(const Point& point) const; bool CollidesWith(const Point& point) const;
bool collidesWith(const Rectangle& rect) const; bool CollidesWith(const Rectangle& rect) const;
bool collidesWith(const Circle& circle) const; bool CollidesWith(const Circle& circle) const;
// distance from the closest side of the rectangle // distance from the closest side of the rectangle
float distance(const Rectangle& rect) const; float Distance(const Rectangle& rect) const;
float distanceSquared(const Rectangle& rect) const; float DistanceSquared(const Rectangle& rect) const;
}; };
} // namespace shape } // namespace shape

View File

@@ -6,7 +6,7 @@
namespace td { namespace td {
namespace utils { namespace utils {
std::uint64_t getTime(); std::uint64_t GetTime();
typedef std::function<void()> TimerExecFunction; typedef std::function<void()> TimerExecFunction;
@@ -14,62 +14,62 @@ typedef std::function<void()> TimerExecFunction;
// utililty class to call function at regular period of time // utililty class to call function at regular period of time
class AutoTimer { class AutoTimer {
private: private:
std::uint64_t m_Interval; std::uint64_t m_Interval;
TimerExecFunction m_Function; TimerExecFunction m_Function;
std::uint64_t m_LastTime = getTime(); std::uint64_t m_LastTime = GetTime();
std::uint64_t m_InternalTime = 0; std::uint64_t m_InternalTime = 0;
public: public:
AutoTimer() : m_Interval(0), m_Function(nullptr) {} AutoTimer() : m_Interval(0), m_Function(nullptr) {}
AutoTimer(std::uint64_t interval) : m_Interval(interval), m_Function(nullptr) {} AutoTimer(std::uint64_t interval) : m_Interval(interval), m_Function(nullptr) {}
AutoTimer(std::uint64_t interval, TimerExecFunction callback) : m_Interval(interval), m_Function(callback) {} AutoTimer(std::uint64_t interval, TimerExecFunction callback) : m_Interval(interval), m_Function(callback) {}
void update(); void Update();
void update(std::uint64_t delta); void Update(std::uint64_t delta);
void reset(); void Reset();
void setInterval(std::uint64_t newInterval) { m_Interval = newInterval; } void SetInterval(std::uint64_t newInterval) { m_Interval = newInterval; }
std::uint64_t getInterval() const { return m_Interval; } std::uint64_t GetInterval() const { return m_Interval; }
void setCallbackFunction(TimerExecFunction newCallback) { m_Function = newCallback; } void SetCallbackFunction(TimerExecFunction newCallback) { m_Function = newCallback; }
TimerExecFunction getCallbackFunction() const { return m_Function; } TimerExecFunction GetCallbackFunction() const { return m_Function; }
}; };
// utililty class to call function at regular period of time // utililty class to call function at regular period of time
class Timer { class Timer {
private: private:
std::uint64_t m_Interval; // in millis std::uint64_t m_Interval; // in millis
std::uint64_t m_InternalTime = 0; std::uint64_t m_InternalTime = 0;
public: public:
Timer() : m_Interval(0) {} Timer() : m_Interval(0) {}
Timer(std::uint64_t interval) : m_Interval(interval) {} Timer(std::uint64_t interval) : m_Interval(interval) {}
bool update(std::uint64_t delta); bool Update(std::uint64_t delta);
void reset(); void Reset();
void setInterval(std::uint64_t newInterval) { m_Interval = newInterval; } void SetInterval(std::uint64_t newInterval) { m_Interval = newInterval; }
std::uint64_t getInterval() const { return m_Interval; } std::uint64_t GetInterval() const { return m_Interval; }
}; };
// utililty class to call function at regular period of time with a cooldown (used for towers) // utililty class to call function at regular period of time with a cooldown (used for towers and mobs )
class CooldownTimer { class CooldownTimer {
private: private:
std::uint64_t m_Cooldown; // in millis std::uint64_t m_Cooldown; // in millis
std::uint64_t m_CooldownTime; std::uint64_t m_CooldownTime;
public: public:
CooldownTimer() : m_Cooldown(0), m_CooldownTime(0) {} CooldownTimer() : m_Cooldown(0), m_CooldownTime(0) {}
CooldownTimer(std::uint64_t cooldown) : m_Cooldown(0), m_CooldownTime(cooldown) {} CooldownTimer(std::uint64_t cooldown) : m_Cooldown(0), m_CooldownTime(cooldown) {}
bool update(std::uint64_t delta); bool Update(std::uint64_t delta);
void applyCooldown(); void ApplyCooldown();
void reset(); void Reset();
void setCooldown(std::uint64_t newCooldown) { m_CooldownTime = newCooldown; } void SetCooldown(std::uint64_t newCooldown) { m_CooldownTime = newCooldown; }
std::uint64_t getCooldown() const { return m_CooldownTime; } std::uint64_t GetCooldown() const { return m_CooldownTime; }
}; };
} // namespace utils } // namespace utils

View File

@@ -11,38 +11,38 @@ namespace network {
/* IPv4 address */ /* IPv4 address */
class IPAddress { class IPAddress {
private: private:
std::uint32_t m_Address; std::uint32_t m_Address;
bool m_Valid; bool m_Valid;
public: public:
/* Create an invalid address */ /* Create an invalid address */
IPAddress() noexcept; IPAddress() noexcept;
/* Initialize by string IP */ /* Initialize by string IP */
IPAddress(const std::string& str); IPAddress(const std::string& str);
/* Initialize by string IP */ /* Initialize by string IP */
IPAddress(const std::wstring& str); IPAddress(const std::wstring& str);
/* Initialize by octets */ /* Initialize by octets */
IPAddress(std::uint8_t octet1, std::uint8_t octet2, std::uint8_t octet3, std::uint8_t octet4) noexcept; IPAddress(std::uint8_t octet1, std::uint8_t octet2, std::uint8_t octet3, std::uint8_t octet4) noexcept;
/* Get the specific octet. 1-4 */ /* Get the specific octet. 1-4 */
std::uint8_t GetOctet(std::uint8_t num) const; std::uint8_t GetOctet(std::uint8_t num) const;
/* Set the specific octet. 1-4 */ /* Set the specific octet. 1-4 */
void SetOctet(std::uint8_t num, std::uint8_t value); void SetOctet(std::uint8_t num, std::uint8_t value);
/* Make sure the IP is valid. It will be invalid if the host wasn't found. */ /* Make sure the IP is valid. It will be invalid if the host wasn't found. */
bool IsValid() const noexcept { return m_Valid; } bool IsValid() const noexcept { return m_Valid; }
std::string ToString() const; std::string ToString() const;
static IPAddress LocalAddress(); static IPAddress LocalAddress();
bool operator==(const IPAddress& right); bool operator==(const IPAddress& right);
bool operator!=(const IPAddress& right); bool operator!=(const IPAddress& right);
bool operator==(bool b); bool operator==(bool b);
}; };
typedef std::vector<IPAddress> IPAddresses; typedef std::vector<IPAddress> IPAddresses;

View File

@@ -22,7 +22,7 @@ namespace network {
class Dns { class Dns {
public: public:
static IPAddresses Resolve(const std::string& host); static IPAddresses Resolve(const std::string& host);
}; };
} // ns network } // ns network

View File

@@ -40,45 +40,45 @@ typedef int SocketHandle;
class Socket { class Socket {
public: public:
enum Status { Connected, Disconnected, Error }; enum Status { Connected, Disconnected, Error };
enum Type { TCP, UDP }; enum Type { TCP, UDP };
private: private:
bool m_Blocking; bool m_Blocking;
Type m_Type; Type m_Type;
Status m_Status; Status m_Status;
protected: protected:
SocketHandle m_Handle; SocketHandle m_Handle;
Socket(Type type); Socket(Type type);
void SetStatus(Status status); void SetStatus(Status status);
public: public:
virtual ~Socket(); virtual ~Socket();
Socket(Socket&& rhs) = default; Socket(Socket&& rhs) = default;
Socket& operator=(Socket&& rhs) = default; Socket& operator=(Socket&& rhs) = default;
bool SetBlocking(bool block); bool SetBlocking(bool block);
bool IsBlocking() const noexcept; bool IsBlocking() const noexcept;
Type GetType() const noexcept; Type GetType() const noexcept;
Status GetStatus() const noexcept; Status GetStatus() const noexcept;
SocketHandle GetHandle() const noexcept; SocketHandle GetHandle() const noexcept;
bool Connect(const std::string& ip, std::uint16_t port); bool Connect(const std::string& ip, std::uint16_t port);
virtual bool Connect(const IPAddress& address, std::uint16_t port) = 0; virtual bool Connect(const IPAddress& address, std::uint16_t port) = 0;
void Disconnect(); void Disconnect();
std::size_t Send(const std::string& data); std::size_t Send(const std::string& data);
std::size_t Send(DataBuffer& buffer); std::size_t Send(DataBuffer& buffer);
virtual std::size_t Send(const uint8_t* data, std::size_t size) = 0; virtual std::size_t Send(const uint8_t* data, std::size_t size) = 0;
virtual DataBuffer Receive(std::size_t amount) = 0; virtual DataBuffer Receive(std::size_t amount) = 0;
virtual std::size_t Receive(DataBuffer& buffer, std::size_t amount) = 0; virtual std::size_t Receive(DataBuffer& buffer, std::size_t amount) = 0;
}; };
typedef std::shared_ptr<Socket> SocketPtr; typedef std::shared_ptr<Socket> SocketPtr;

View File

@@ -5,29 +5,30 @@ namespace td {
namespace network { namespace network {
class TCPListener { class TCPListener {
int m_Handle; int m_Handle;
std::uint16_t m_Port; std::uint16_t m_Port;
int m_MaxConnections; int m_MaxConnections;
public: public:
TCPListener(); TCPListener();
~TCPListener(); ~TCPListener();
bool listen(std::uint16_t port, int maxConnections); bool Listen(std::uint16_t port, int maxConnections);
bool accept(TCPSocket& newSocket); bool Accept(TCPSocket& newSocket);
void destroy(); void Destroy();
bool close(); bool Close();
bool setBlocking(bool blocking); bool SetBlocking(bool blocking);
std::uint16_t getListeningPort() const { std::uint16_t GetListeningPort() const {
return m_Port; return m_Port;
} }
int getMaximumConnections() const {
return m_MaxConnections;
}
REMOVE_COPY(TCPListener); int GetMaximumConnections() const {
return m_MaxConnections;
}
REMOVE_COPY(TCPListener);
}; };
} // namespace network } // namespace network

View File

@@ -13,22 +13,22 @@ class TCPListener;
class TCPSocket : public Socket { class TCPSocket : public Socket {
private: private:
IPAddress m_RemoteIP; IPAddress m_RemoteIP;
uint16_t m_Port; uint16_t m_Port;
sockaddr_in m_RemoteAddr; sockaddr m_RemoteAddr;
public: public:
TCPSocket(); TCPSocket();
TCPSocket(TCPSocket&& other); TCPSocket(TCPSocket&& other);
virtual bool Connect(const IPAddress& address, std::uint16_t port); virtual bool Connect(const IPAddress& address, std::uint16_t port);
virtual std::size_t Send(const std::uint8_t* data, std::size_t size); virtual std::size_t Send(const std::uint8_t* data, std::size_t size);
virtual DataBuffer Receive(std::size_t amount); virtual DataBuffer Receive(std::size_t amount);
virtual std::size_t Receive(DataBuffer& buffer, std::size_t amount); virtual std::size_t Receive(DataBuffer& buffer, std::size_t amount);
REMOVE_COPY(TCPSocket); REMOVE_COPY(TCPSocket);
friend class TCPListener; friend class TCPListener;
}; };
void SendPacket(const DataBuffer& data, network::TCPSocket& socket); void SendPacket(const DataBuffer& data, network::TCPSocket& socket);

View File

@@ -11,16 +11,18 @@ namespace network {
class UDPSocket : public Socket { class UDPSocket : public Socket {
private: private:
IPAddress m_RemoteIP; IPAddress m_RemoteIP;
uint16_t m_Port; uint16_t m_Port;
sockaddr_in m_RemoteAddr; sockaddr_in m_RemoteAddr;
public: public:
UDPSocket(); UDPSocket();
bool Connect(const IPAddress& address, std::uint16_t port); bool Connect(const IPAddress& address, std::uint16_t port);
std::size_t Send(const std::uint8_t* data, std::size_t size); std::size_t Send(const std::uint8_t* data, std::size_t size);
DataBuffer Receive(std::size_t amount);
virtual DataBuffer Receive(std::size_t amount);
virtual std::size_t Receive(DataBuffer& buffer, std::size_t amount);
}; };
} // ns network } // ns network

View File

@@ -12,21 +12,21 @@ class PacketHandler;
class PacketDispatcher { class PacketDispatcher {
private: private:
std::map<PacketType, std::vector<PacketHandler*>> m_Handlers; std::map<PacketType, std::vector<PacketHandler*>> m_Handlers;
public: public:
PacketDispatcher() = default; PacketDispatcher() = default;
PacketDispatcher(const PacketDispatcher& rhs) = delete; PacketDispatcher(const PacketDispatcher& rhs) = delete;
PacketDispatcher& operator=(const PacketDispatcher& rhs) = delete; PacketDispatcher& operator=(const PacketDispatcher& rhs) = delete;
PacketDispatcher(PacketDispatcher&& rhs) = delete; PacketDispatcher(PacketDispatcher&& rhs) = delete;
PacketDispatcher& operator=(PacketDispatcher&& rhs) = delete; PacketDispatcher& operator=(PacketDispatcher&& rhs) = delete;
void Dispatch(const PacketPtr& packet); void Dispatch(const PacketPtr& packet);
void RegisterHandler(PacketType type, PacketHandler* handler); void RegisterHandler(PacketType type, PacketHandler* handler);
void UnregisterHandler(PacketType type, PacketHandler* handler); void UnregisterHandler(PacketType type, PacketHandler* handler);
void UnregisterHandler(PacketHandler* handler); void UnregisterHandler(PacketHandler* handler);
}; };
} // namespace protocol } // namespace protocol

View File

@@ -6,7 +6,7 @@ namespace td {
namespace protocol { namespace protocol {
namespace PacketFactory { namespace PacketFactory {
PacketPtr createPacket(PacketType type, DataBuffer& buffer); PacketPtr CreatePacket(PacketType type, DataBuffer& buffer);
} }
} // namespace protocol } // namespace protocol

View File

@@ -9,35 +9,39 @@ class PacketDispatcher;
class PacketHandler { class PacketHandler {
private: private:
PacketDispatcher* m_Dispatcher; PacketDispatcher* m_Dispatcher;
public: public:
PacketHandler(PacketDispatcher* dispatcher) : m_Dispatcher(dispatcher) {} PacketHandler(PacketDispatcher* dispatcher) : m_Dispatcher(dispatcher) {}
virtual ~PacketHandler() {} virtual ~PacketHandler() {}
PacketDispatcher* GetDispatcher() { return m_Dispatcher; } PacketDispatcher* GetDispatcher() { return m_Dispatcher; }
virtual void HandlePacket(const PlayerLoginPacket* packet) {} virtual void HandlePacket(const ConnexionInfoPacket* packet) {}
virtual void HandlePacket(const WorldBeginDataPacket* packet) {} virtual void HandlePacket(const DisconnectPacket* packet) {}
virtual void HandlePacket(const WorldDataPacket* packet) {} virtual void HandlePacket(const KeepAlivePacket* packet) {}
virtual void HandlePacket(const KeepAlivePacket* packet) {} virtual void HandlePacket(const PlaceTowerPacket* packet) {}
virtual void HandlePacket(const UpdateMoneyPacket* packet) {} virtual void HandlePacket(const PlayerBuyItemPacket* packet) {}
virtual void HandlePacket(const UpdateExpPacket* packet) {} virtual void HandlePacket(const PlayerBuyMobUpgradePacket* packet) {}
virtual void HandlePacket(const UpdateLobbyTimePacket* packet) {} virtual void HandlePacket(const PlayerJoinPacket* packet) {}
virtual void HandlePacket(const UpdateGameStatePacket* packet) {} virtual void HandlePacket(const PlayerLeavePacket* packet) {}
virtual void HandlePacket(const PlayerListPacket* packet) {} virtual void HandlePacket(const PlayerListPacket* packet) {}
virtual void HandlePacket(const PlayerJoinPacket* packet) {} virtual void HandlePacket(const PlayerLoginPacket* packet) {}
virtual void HandlePacket(const PlayerLeavePacket* packet) {} virtual void HandlePacket(const RemoveTowerPacket* packet) {}
virtual void HandlePacket(const ConnexionInfoPacket* packet) {} virtual void HandlePacket(const SelectTeamPacket* packet) {}
virtual void HandlePacket(const SelectTeamPacket* packet) {} virtual void HandlePacket(const SendMobsPacket* packet) {}
virtual void HandlePacket(const UpdatePlayerTeamPacket* packet) {} virtual void HandlePacket(const ServerTpsPacket* packet) {}
virtual void HandlePacket(const DisconnectPacket* packet) {} virtual void HandlePacket(const SpawnMobPacket* packet) {}
virtual void HandlePacket(const ServerTpsPacket* packet) {} virtual void HandlePacket(const UpdateCastleLifePacket* packet) {}
virtual void HandlePacket(const SpawnMobPacket* packet) {} virtual void HandlePacket(const UpdateExpPacket* packet) {}
virtual void HandlePacket(const PlaceTowerPacket* packet) {} virtual void HandlePacket(const UpdateGameStatePacket* packet) {}
virtual void HandlePacket(const WorldAddTowerPacket* packet) {} virtual void HandlePacket(const UpdateLobbyTimePacket* packet) {}
virtual void HandlePacket(const WorldRemoveTowerPacket* packet) {} virtual void HandlePacket(const UpdateMobStatesPacket* packet) {}
virtual void HandlePacket(const SendMobsPacket* packet) {} virtual void HandlePacket(const UpdateMoneyPacket* packet) {}
virtual void HandlePacket(const UpgradeTowerPacket* packet) {} virtual void HandlePacket(const UpdatePlayerTeamPacket* packet) {}
virtual void HandlePacket(const UpgradeTowerPacket* packet) {}
virtual void HandlePacket(const WorldAddTowerPacket* packet) {}
virtual void HandlePacket(const WorldBeginDataPacket* packet) {}
virtual void HandlePacket(const WorldDataPacket* packet) {}
}; };
} // namespace protocol } // namespace protocol

View File

@@ -12,506 +12,628 @@ namespace protocol {
class PacketHandler; class PacketHandler;
enum class PacketType : std::uint8_t { enum class PacketType : std::uint8_t {
// client --> server // client --> server
PlayerLogin = 0, PlayerLogin = 0,
SelectTeam, SelectTeam,
SpawnMob, SpawnMob,
SendMobs, SendMobs,
PlaceTower, PlaceTower,
// client <-- server // client <-- server
PlayerJoin, PlayerJoin,
PlayerLeave, PlayerLeave,
WorldBeginData, WorldBeginData,
WorldData, WorldData,
UpdateMoney, UpdateMoney,
UpdateEXP, UpdateEXP,
UpdateLobbyTime, UpdateLobbyTime,
UpdateGameState, UpdateGameState,
PlayerList, PlayerList,
ConnectionInfo, ConnectionInfo,
UpdatePlayerTeam, UpdatePlayerTeam,
ServerTps, ServerTps,
WorldAddTower, WorldAddTower,
WorldRemoveTower, UpdateMobStates,
UpdateCastleLife,
// client <--> server // client <--> server
KeepAlive, KeepAlive,
Disconnect, Disconnect,
UpgradeTower, UpgradeTower,
RemoveTower,
PlayerBuyItem,
PlayerBuyMobUpgrade,
PACKET_COUNT
}; };
struct WorldHeader { struct WorldHeader {
game::TowerTileColorPalette m_TowerPlacePalette; game::TowerTileColorPalette m_TowerPlacePalette;
game::Color m_WalkablePalette; Color m_WalkablePalette;
std::vector<game::Color> m_DecorationPalette; std::vector<Color> m_DecorationPalette;
Color m_Background;
game::SpawnColorPalette m_SpawnColorPalette; game::SpawnColorPalette m_SpawnColorPalette;
game::TilePalette m_TilePalette; game::TilePalette m_TilePalette;
game::Spawn m_RedSpawn, m_BlueSpawn; game::Spawn m_RedSpawn, m_BlueSpawn;
game::TeamCastle m_RedCastle, m_BlueCastle; game::TeamCastle m_RedCastle, m_BlueCastle;
const game::World* m_World; const game::World* m_World;
}; };
struct WorldData { struct WorldData {
std::unordered_map<game::ChunkCoord, game::ChunkPtr> m_Chunks; std::unordered_map<game::ChunkCoord, game::ChunkPtr> m_Chunks;
}; };
class Packet { class Packet {
public: public:
Packet() {} Packet() {}
virtual ~Packet() {} virtual ~Packet() {}
virtual DataBuffer Serialize() const = 0; virtual DataBuffer Serialize(bool packetID = true) const = 0;
virtual void Deserialize(DataBuffer& data) = 0; virtual void Deserialize(DataBuffer& data) = 0;
virtual void Dispatch(PacketHandler* handler) const = 0; virtual void Dispatch(PacketHandler* handler) const = 0;
virtual PacketType getType() const = 0; void WritePacketID(DataBuffer& data, bool packetID) const;
std::uint8_t getID() const { return (std::uint8_t)getType(); }
virtual PacketType GetType() const = 0;
std::uint8_t GetID() const { return static_cast<std::uint8_t>(GetType()); }
}; };
typedef std::unique_ptr<Packet> PacketPtr; typedef std::unique_ptr<Packet> PacketPtr;
class KeepAlivePacket : public Packet { class KeepAlivePacket : public Packet {
private: private:
std::uint64_t m_AliveID; std::uint64_t m_AliveID;
public: public:
KeepAlivePacket() {} KeepAlivePacket() {}
KeepAlivePacket(std::uint64_t aliveID) : m_AliveID(aliveID) {} KeepAlivePacket(std::uint64_t aliveID) : m_AliveID(aliveID) {}
virtual ~KeepAlivePacket() {} virtual ~KeepAlivePacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
std::uint64_t getAliveID() const { return m_AliveID; } std::uint64_t GetAliveID() const { return m_AliveID; }
virtual PacketType getType() const { return PacketType::KeepAlive; } virtual PacketType GetType() const { return PacketType::KeepAlive; }
}; };
class PlayerLoginPacket : public Packet { class PlayerLoginPacket : public Packet {
private: private:
std::string m_PlayerName; std::string m_PlayerName;
public: public:
PlayerLoginPacket() {} PlayerLoginPacket() {}
PlayerLoginPacket(std::string playerName) : m_PlayerName(playerName) {} PlayerLoginPacket(std::string playerName) : m_PlayerName(playerName) {}
virtual ~PlayerLoginPacket() {} virtual ~PlayerLoginPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
virtual PacketType getType() const { return PacketType::PlayerLogin; } virtual PacketType GetType() const { return PacketType::PlayerLogin; }
const std::string& getPlayerName() const { return m_PlayerName; } const std::string& GetPlayerName() const { return m_PlayerName; }
}; };
class WorldBeginDataPacket : public Packet { class WorldBeginDataPacket : public Packet {
private: private:
WorldHeader m_Header; WorldHeader m_Header;
public: public:
WorldBeginDataPacket() {} WorldBeginDataPacket() {}
WorldBeginDataPacket(const game::World* world) { WorldBeginDataPacket(const game::World* world) {
m_Header.m_World = world; m_Header.m_World = world;
} }
virtual ~WorldBeginDataPacket() {} virtual ~WorldBeginDataPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
virtual PacketType getType() const { return PacketType::WorldBeginData; } virtual PacketType GetType() const { return PacketType::WorldBeginData; }
const game::TowerTileColorPalette& getTowerTilePalette() const { return m_Header.m_TowerPlacePalette; } const game::TowerTileColorPalette& GetTowerTilePalette() const { return m_Header.m_TowerPlacePalette; }
const game::Color& getWalkableTileColor() const { return m_Header.m_WalkablePalette; } const Color& GetWalkableTileColor() const { return m_Header.m_WalkablePalette; }
const std::vector<game::Color>& getDecorationPalette() const { return m_Header.m_DecorationPalette; } const std::vector<Color>& GetDecorationPalette() const { return m_Header.m_DecorationPalette; }
const Color& GetBackgroundColor() const { return m_Header.m_Background; }
const game::Spawn& getRedSpawn() const { return m_Header.m_RedSpawn; } const game::Spawn& GetRedSpawn() const { return m_Header.m_RedSpawn; }
const game::Spawn& getBlueSpawn() const { return m_Header.m_BlueSpawn; } const game::Spawn& GetBlueSpawn() const { return m_Header.m_BlueSpawn; }
const game::SpawnColorPalette& getSpawnPalette() const { return m_Header.m_SpawnColorPalette; } const game::SpawnColorPalette& GetSpawnPalette() const { return m_Header.m_SpawnColorPalette; }
const game::TeamCastle& getRedCastle() const { return m_Header.m_RedCastle; } const game::TeamCastle& GetRedCastle() const { return m_Header.m_RedCastle; }
const game::TeamCastle& getBlueCastle() const { return m_Header.m_BlueCastle; } const game::TeamCastle& GetBlueCastle() const { return m_Header.m_BlueCastle; }
const game::TilePalette getTilePalette() const { return m_Header.m_TilePalette; } const game::TilePalette GetTilePalette() const { return m_Header.m_TilePalette; }
DataBuffer SerializeCustom() const; // allow serialisation with invalid World member void setWorldHeader(const WorldHeader& header) { m_Header = header; }
void setWorldHeader(const WorldHeader& header) { m_Header = header; }
}; };
class WorldDataPacket : public Packet { class WorldDataPacket : public Packet {
private: private:
WorldData m_WorldData; WorldData m_WorldData;
const game::World* m_World; const game::World* m_World;
public: public:
WorldDataPacket() {} WorldDataPacket() {}
WorldDataPacket(const game::World* world) : m_World(world) {} WorldDataPacket(const game::World* world) : m_World(world) {}
virtual ~WorldDataPacket() {} virtual ~WorldDataPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
virtual PacketType getType() const { return PacketType::WorldData; } virtual PacketType GetType() const { return PacketType::WorldData; }
const std::unordered_map<game::ChunkCoord, game::ChunkPtr>& getChunks() const { return m_WorldData.m_Chunks; } const std::unordered_map<game::ChunkCoord, game::ChunkPtr>& GetChunks() const { return m_WorldData.m_Chunks; }
DataBuffer SerializeCustom() const; // allow serialisation with invalid World member DataBuffer SerializeCustom() const; // allow serialisation with invalid World member
void setWorldData(const WorldData& worldData) { m_WorldData = worldData; } void SetWorldData(const WorldData& worldData) { m_WorldData = worldData; }
}; };
class UpdateMoneyPacket : public Packet { class UpdateMoneyPacket : public Packet {
private: private:
std::uint32_t m_NewAmount; std::uint32_t m_NewAmount;
public: public:
UpdateMoneyPacket() {} UpdateMoneyPacket() {}
UpdateMoneyPacket(std::uint32_t newAmount) : m_NewAmount(newAmount) {} UpdateMoneyPacket(std::uint32_t newAmount) : m_NewAmount(newAmount) {}
virtual ~UpdateMoneyPacket() {} virtual ~UpdateMoneyPacket() {}
std::uint32_t getGold() const { return m_NewAmount; } std::uint32_t GetGold() const { return m_NewAmount; }
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
virtual PacketType getType() const { return PacketType::UpdateMoney; } virtual PacketType GetType() const { return PacketType::UpdateMoney; }
}; };
class UpdateExpPacket : public Packet { class UpdateExpPacket : public Packet {
private: private:
std::uint32_t m_NewAmount; std::uint32_t m_NewAmount;
public: public:
UpdateExpPacket() {} UpdateExpPacket() {}
UpdateExpPacket(std::uint32_t newAmount) : m_NewAmount(newAmount) {} UpdateExpPacket(std::uint32_t newAmount) : m_NewAmount(newAmount) {}
virtual ~UpdateExpPacket() {} virtual ~UpdateExpPacket() {}
virtual DataBuffer Serialize() const; std::uint32_t GetExp() const { return m_NewAmount; }
virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const;
virtual PacketType getType() const { return PacketType::UpdateEXP; } virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const;
virtual PacketType GetType() const { return PacketType::UpdateEXP; }
}; };
class UpdateLobbyTimePacket : public Packet { class UpdateLobbyTimePacket : public Packet {
private: private:
std::uint32_t m_RemainingTime; std::uint32_t m_RemainingTime;
public: public:
UpdateLobbyTimePacket() {} UpdateLobbyTimePacket() {}
UpdateLobbyTimePacket(std::uint32_t remainingTime) : m_RemainingTime(remainingTime) {} UpdateLobbyTimePacket(std::uint32_t remainingTime) : m_RemainingTime(remainingTime) {}
virtual ~UpdateLobbyTimePacket() {} virtual ~UpdateLobbyTimePacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
std::uint32_t getRemainingTime() const { return m_RemainingTime; } std::uint32_t GetRemainingTime() const { return m_RemainingTime; }
virtual PacketType getType() const { return PacketType::UpdateLobbyTime; } virtual PacketType GetType() const { return PacketType::UpdateLobbyTime; }
}; };
class UpdateGameStatePacket : public Packet { class UpdateGameStatePacket : public Packet {
private: private:
game::GameState m_GameState; game::GameState m_GameState;
public: public:
UpdateGameStatePacket() {} UpdateGameStatePacket() {}
UpdateGameStatePacket(game::GameState gameState) : m_GameState(gameState) {} UpdateGameStatePacket(game::GameState gameState) : m_GameState(gameState) {}
virtual ~UpdateGameStatePacket() {} virtual ~UpdateGameStatePacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
game::GameState getGameState() const { return m_GameState; } game::GameState GetGameState() const { return m_GameState; }
virtual PacketType getType() const { return PacketType::UpdateGameState; } virtual PacketType GetType() const { return PacketType::UpdateGameState; }
}; };
struct PlayerInfo { struct PlayerInfo {
std::string name; std::string name;
game::TeamColor team; game::TeamColor team;
}; };
class PlayerListPacket : public Packet { class PlayerListPacket : public Packet {
private: private:
std::map<std::uint8_t, PlayerInfo> m_Players; std::map<std::uint8_t, PlayerInfo> m_Players;
public: public:
PlayerListPacket() {} PlayerListPacket() {}
PlayerListPacket(std::map<std::uint8_t, PlayerInfo> players) : m_Players(players) {} PlayerListPacket(std::map<std::uint8_t, PlayerInfo> players) : m_Players(players) {}
virtual ~PlayerListPacket() {} virtual ~PlayerListPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
const std::map<std::uint8_t, PlayerInfo>& getPlayers() const { return m_Players; } const std::map<std::uint8_t, PlayerInfo>& GetPlayers() const { return m_Players; }
virtual PacketType getType() const { return PacketType::PlayerList; } virtual PacketType GetType() const { return PacketType::PlayerList; }
}; };
class PlayerJoinPacket : public Packet { class PlayerJoinPacket : public Packet {
private: private:
std::uint8_t m_PlayerID; std::uint8_t m_PlayerID;
std::string m_PlayerName; std::string m_PlayerName;
public: public:
PlayerJoinPacket() {} PlayerJoinPacket() {}
PlayerJoinPacket(std::uint8_t playerID, const std::string& playerName) : m_PlayerID(playerID), m_PlayerName(playerName) {} PlayerJoinPacket(std::uint8_t playerID, const std::string& playerName) : m_PlayerID(playerID), m_PlayerName(playerName) {}
virtual ~PlayerJoinPacket() {} virtual ~PlayerJoinPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
std::uint8_t getPlayerID() const { return m_PlayerID; } std::uint8_t GetPlayerID() const { return m_PlayerID; }
const std::string& getPlayerName() const { return m_PlayerName; } const std::string& GetPlayerName() const { return m_PlayerName; }
virtual PacketType getType() const { return PacketType::PlayerJoin; } virtual PacketType GetType() const { return PacketType::PlayerJoin; }
}; };
class PlayerLeavePacket : public Packet { class PlayerLeavePacket : public Packet {
private: private:
std::uint8_t m_PlayerID; std::uint8_t m_PlayerID;
public: public:
PlayerLeavePacket() {} PlayerLeavePacket() {}
PlayerLeavePacket(std::uint8_t playerID) : m_PlayerID(playerID) {} PlayerLeavePacket(std::uint8_t playerID) : m_PlayerID(playerID) {}
virtual ~PlayerLeavePacket() {} virtual ~PlayerLeavePacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
std::uint8_t getPlayerID() const { return m_PlayerID; } std::uint8_t GetPlayerID() const { return m_PlayerID; }
virtual PacketType getType() const { return PacketType::PlayerLeave; } virtual PacketType GetType() const { return PacketType::PlayerLeave; }
}; };
class ConnexionInfoPacket : public Packet { class ConnexionInfoPacket : public Packet {
private: private:
std::uint8_t m_ConnectionID; std::uint8_t m_ConnectionID;
public: public:
ConnexionInfoPacket() {} ConnexionInfoPacket() {}
ConnexionInfoPacket(std::uint8_t connectionID) : m_ConnectionID(connectionID) {} ConnexionInfoPacket(std::uint8_t connectionID) : m_ConnectionID(connectionID) {}
virtual ~ConnexionInfoPacket() {} virtual ~ConnexionInfoPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
std::uint8_t getConnectionID() const { return m_ConnectionID; } std::uint8_t GetConnectionID() const { return m_ConnectionID; }
virtual PacketType getType() const { return PacketType::ConnectionInfo; } virtual PacketType GetType() const { return PacketType::ConnectionInfo; }
}; };
class SelectTeamPacket : public Packet { class SelectTeamPacket : public Packet {
private: private:
game::TeamColor m_SelectedTeam; game::TeamColor m_SelectedTeam;
public: public:
SelectTeamPacket() {} SelectTeamPacket() {}
SelectTeamPacket(game::TeamColor selectedTeam) : m_SelectedTeam(selectedTeam) {} SelectTeamPacket(game::TeamColor selectedTeam) : m_SelectedTeam(selectedTeam) {}
virtual ~SelectTeamPacket() {} virtual ~SelectTeamPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
game::TeamColor getSelectedTeam() const { return m_SelectedTeam; } game::TeamColor GetSelectedTeam() const { return m_SelectedTeam; }
virtual PacketType getType() const { return PacketType::SelectTeam; } virtual PacketType GetType() const { return PacketType::SelectTeam; }
}; };
class UpdatePlayerTeamPacket : public Packet { class UpdatePlayerTeamPacket : public Packet {
private: private:
std::uint8_t m_PlayerID; std::uint8_t m_PlayerID;
game::TeamColor m_SelectedTeam; game::TeamColor m_SelectedTeam;
public: public:
UpdatePlayerTeamPacket() {} UpdatePlayerTeamPacket() {}
UpdatePlayerTeamPacket(std::uint8_t playerID, game::TeamColor selectedTeam) : m_PlayerID(playerID), m_SelectedTeam(selectedTeam) {} UpdatePlayerTeamPacket(std::uint8_t playerID, game::TeamColor selectedTeam) : m_PlayerID(playerID), m_SelectedTeam(selectedTeam) {}
virtual ~UpdatePlayerTeamPacket() {} virtual ~UpdatePlayerTeamPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
game::TeamColor getSelectedTeam() const { return m_SelectedTeam; } game::TeamColor GetSelectedTeam() const { return m_SelectedTeam; }
std::uint8_t getPlayerID() const { return m_PlayerID; } std::uint8_t GetPlayerID() const { return m_PlayerID; }
virtual PacketType getType() const { return PacketType::UpdatePlayerTeam; } virtual PacketType GetType() const { return PacketType::UpdatePlayerTeam; }
}; };
class DisconnectPacket : public Packet { class DisconnectPacket : public Packet {
private: private:
std::string m_Reason; // only when sent from server std::string m_Reason; // only when sent from server
public: public:
DisconnectPacket() {} DisconnectPacket() {}
DisconnectPacket(std::string reason) : m_Reason(reason) {} DisconnectPacket(std::string reason) : m_Reason(reason) {}
virtual ~DisconnectPacket() {} virtual ~DisconnectPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
const std::string& getReason() const { return m_Reason; } const std::string& GetReason() const { return m_Reason; }
virtual PacketType getType() const { return PacketType::Disconnect; } virtual PacketType GetType() const { return PacketType::Disconnect; }
}; };
class ServerTpsPacket : public Packet { class ServerTpsPacket : public Packet {
private: private:
float m_TPS; float m_TPS;
std::uint64_t m_PacketSendTime; // used to calculate ping float m_MSPT;
std::uint64_t m_PacketSendTime; // used to calculate ping
public: public:
ServerTpsPacket() {} ServerTpsPacket() {}
ServerTpsPacket(float tps, std::uint64_t sendTime) : m_TPS(tps), m_PacketSendTime(sendTime) {} ServerTpsPacket(float tps, float mspt, std::uint64_t sendTime) : m_TPS(tps), m_MSPT(mspt), m_PacketSendTime(sendTime) {}
virtual ~ServerTpsPacket() {} virtual ~ServerTpsPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
float getTPS() const { return m_TPS; } float GetTPS() const { return m_TPS; }
std::uint64_t getPacketSendTime() const { return m_PacketSendTime; } float GetMSPT() const { return m_MSPT; }
std::uint64_t GetPacketSendTime() const { return m_PacketSendTime; }
virtual PacketType getType() const { return PacketType::ServerTps; } virtual PacketType GetType() const { return PacketType::ServerTps; }
}; };
struct MobSend { // represents a mob send struct MobSend { // represents a mob send
game::MobType mobType; game::MobType mobType : 4;
game::MobLevel mobLevel; game::MobLevel mobLevel : 4;
std::uint8_t mobCount; // the max is 12 std::uint8_t mobCount; // the max is 12
}; };
class SendMobsPacket : public Packet { class SendMobsPacket : public Packet {
private: private:
std::vector<MobSend> m_MobSends; std::vector<MobSend> m_MobSends;
public: public:
SendMobsPacket() {} SendMobsPacket() {}
SendMobsPacket(const std::vector<MobSend>& mobSends) : m_MobSends(mobSends) {} SendMobsPacket(const std::vector<MobSend>& mobSends) : m_MobSends(mobSends) {}
virtual ~SendMobsPacket() {} virtual ~SendMobsPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
const std::vector<MobSend>& getMobSends() const { return m_MobSends; } const std::vector<MobSend>& GetMobSends() const { return m_MobSends; }
virtual PacketType getType() const { return PacketType::SendMobs; } virtual PacketType GetType() const { return PacketType::SendMobs; }
}; };
class SpawnMobPacket : public Packet { class SpawnMobPacket : public Packet {
private: private:
game::MobID m_MobID; game::MobID m_MobID;
game::MobType m_MobType; game::MobType m_MobType;
game::MobLevel m_MobLevel; game::MobLevel m_MobLevel;
game::Direction m_MobDirection; game::Direction m_MobDirection;
game::PlayerID m_Sender; game::PlayerID m_Sender;
float m_MobX, m_MobY; float m_MobX, m_MobY;
public: public:
SpawnMobPacket() {} SpawnMobPacket() {}
SpawnMobPacket(game::MobID id, game::MobType type, std::uint8_t level, game::PlayerID sender, SpawnMobPacket(game::MobID id, game::MobType type, std::uint8_t level, game::PlayerID sender,
float x, float y, game::Direction dir) : m_MobID(id), m_MobType(type), m_MobLevel(level), float x, float y, game::Direction dir) : m_MobID(id), m_MobType(type), m_MobLevel(level),
m_MobDirection(dir), m_Sender(sender), m_MobX(x), m_MobY(y) {} m_MobDirection(dir), m_Sender(sender), m_MobX(x), m_MobY(y) {
virtual ~SpawnMobPacket() {} }
virtual ~SpawnMobPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
game::MobID getMobID() const { return m_MobID; } game::MobID GetMobID() const { return m_MobID; }
game::MobType getMobType() const { return m_MobType; } game::MobType GetMobType() const { return m_MobType; }
game::MobLevel getMobLevel() const { return m_MobLevel; } game::MobLevel GetMobLevel() const { return m_MobLevel; }
game::Direction getMobDirection() const { return m_MobDirection; } game::Direction GetMobDirection() const { return m_MobDirection; }
game::PlayerID getSender() const { return m_Sender; } game::PlayerID GetSender() const { return m_Sender; }
float getMobX() const { return m_MobX; } float GetMobX() const { return m_MobX; }
float getMobY() const { return m_MobY; } float GetMobY() const { return m_MobY; }
virtual PacketType getType() const { return PacketType::SpawnMob; } virtual PacketType GetType() const { return PacketType::SpawnMob; }
}; };
class PlaceTowerPacket : public Packet { class PlaceTowerPacket : public Packet {
private: private:
std::int32_t m_TowerX, m_TowerY; std::int32_t m_TowerX, m_TowerY;
game::TowerType m_TowerType; game::TowerType m_TowerType;
public: public:
PlaceTowerPacket() {} PlaceTowerPacket() {}
PlaceTowerPacket(std::int32_t x, std::int32_t y, game::TowerType type) : PlaceTowerPacket(std::int32_t x, std::int32_t y, game::TowerType type) :
m_TowerX(x), m_TowerY(y), m_TowerType(type) {} m_TowerX(x), m_TowerY(y), m_TowerType(type) {
virtual ~PlaceTowerPacket() {} }
virtual ~PlaceTowerPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
std::int32_t getTowerX() const { return m_TowerX; } std::int32_t GetTowerX() const { return m_TowerX; }
std::int32_t getTowerY() const { return m_TowerY; } std::int32_t GetTowerY() const { return m_TowerY; }
game::TowerType getTowerType() const { return m_TowerType; } game::TowerType GetTowerType() const { return m_TowerType; }
virtual PacketType getType() const { return PacketType::PlaceTower; } virtual PacketType GetType() const { return PacketType::PlaceTower; }
}; };
class WorldAddTowerPacket : public Packet { class WorldAddTowerPacket : public Packet {
private: private:
game::TowerID m_TowerID; game::TowerID m_TowerID;
std::int32_t m_TowerX, m_TowerY; std::int32_t m_TowerX, m_TowerY;
game::TowerType m_TowerType; game::TowerType m_TowerType;
game::PlayerID m_Builder; game::PlayerID m_Builder;
public: public:
WorldAddTowerPacket() {} WorldAddTowerPacket() {}
WorldAddTowerPacket(game::TowerID id, std::int32_t x, std::int32_t y, game::TowerType type, game::PlayerID player) : WorldAddTowerPacket(game::TowerID id, std::int32_t x, std::int32_t y, game::TowerType type, game::PlayerID player) :
m_TowerID(id), m_TowerX(x), m_TowerY(y), m_TowerType(type), m_Builder(player) {} m_TowerID(id), m_TowerX(x), m_TowerY(y), m_TowerType(type), m_Builder(player) {
virtual ~WorldAddTowerPacket() {} }
virtual ~WorldAddTowerPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
game::TowerID getTowerID() const { return m_TowerID; } game::TowerID GetTowerID() const { return m_TowerID; }
std::int32_t getTowerX() const { return m_TowerX; } std::int32_t GetTowerX() const { return m_TowerX; }
std::int32_t getTowerY() const { return m_TowerY; } std::int32_t GetTowerY() const { return m_TowerY; }
game::TowerType getTowerType() const { return m_TowerType; } game::TowerType GetTowerType() const { return m_TowerType; }
game::PlayerID getBuilder() const { return m_Builder; } game::PlayerID GetBuilder() const { return m_Builder; }
virtual PacketType getType() const { return PacketType::WorldAddTower; } virtual PacketType GetType() const { return PacketType::WorldAddTower; }
}; };
class WorldRemoveTowerPacket : public Packet { class RemoveTowerPacket : public Packet {
private: private:
game::TowerID m_TowerID; game::TowerID m_TowerID;
public: public:
WorldRemoveTowerPacket() {} RemoveTowerPacket() {}
WorldRemoveTowerPacket(game::TowerID id) : m_TowerID(id) {} RemoveTowerPacket(game::TowerID id) : m_TowerID(id) {}
virtual ~WorldRemoveTowerPacket() {} virtual ~RemoveTowerPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
game::TowerID getTowerID() const { return m_TowerID; } game::TowerID GetTowerID() const { return m_TowerID; }
virtual PacketType getType() const { return PacketType::WorldRemoveTower; } virtual PacketType GetType() const { return PacketType::RemoveTower; }
}; };
class UpgradeTowerPacket : public Packet { class UpgradeTowerPacket : public Packet {
private: private:
game::TowerID m_TowerID; game::TowerID m_TowerID;
game::TowerLevel m_TowerLevel; game::TowerLevel m_TowerLevel;
public: public:
UpgradeTowerPacket() {} UpgradeTowerPacket() {}
UpgradeTowerPacket(game::TowerID tower, game::TowerLevel level) : m_TowerID(tower), m_TowerLevel(level) {} UpgradeTowerPacket(game::TowerID tower, game::TowerLevel level) : m_TowerID(tower), m_TowerLevel(level) {}
virtual ~UpgradeTowerPacket() {} virtual ~UpgradeTowerPacket() {}
virtual DataBuffer Serialize() const; virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data); virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const; virtual void Dispatch(PacketHandler* handler) const;
game::TowerID getTowerID() const { return m_TowerID; } game::TowerID GetTowerID() const { return m_TowerID; }
game::TowerLevel getTowerLevel() const { return m_TowerLevel; } game::TowerLevel GetTowerLevel() const { return m_TowerLevel; }
virtual PacketType getType() const { return PacketType::UpgradeTower; } virtual PacketType GetType() const { return PacketType::UpgradeTower; }
}; };
} class MobState {
} using Point = utils::shape::Point;
private:
game::MobID m_MobID;
Point m_MobPosition;
float m_MobLife;
game::Direction m_MobDirection;
public:
MobState() {}
MobState(game::MobID id, const Point& position, float life, game::Direction direction) :
m_MobID(id), m_MobPosition(position), m_MobLife(life), m_MobDirection(direction) {
}
game::MobID GetMobId() const { return m_MobID; }
Point GetMobPosition() const { return m_MobPosition; }
float GetMobLife() const { return m_MobLife; }
game::Direction GetMobDirection() const { return m_MobDirection; }
};
class UpdateMobStatesPacket : public Packet {
private:
std::vector<MobState> m_MobStates;
public:
UpdateMobStatesPacket() {}
virtual ~UpdateMobStatesPacket() {}
virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const;
void addMobState(MobState mobState) { m_MobStates.push_back(mobState); }
const std::vector<MobState>& GetMobStates() const { return m_MobStates; }
virtual PacketType GetType() const { return PacketType::UpdateMobStates; }
};
class UpdateCastleLifePacket : public Packet {
private:
std::uint16_t m_CastleLife;
game::TeamColor m_Team;
public:
UpdateCastleLifePacket() {}
UpdateCastleLifePacket(std::uint16_t life, game::TeamColor team) : m_CastleLife(life), m_Team(team) {}
virtual ~UpdateCastleLifePacket() {}
virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const;
std::uint16_t GetCastleLife() const { return m_CastleLife; }
game::TeamColor GetTeamColor() const { return m_Team; }
virtual PacketType GetType() const { return PacketType::UpdateCastleLife; }
};
enum class ItemType : std::uint8_t {
// Upgrades
ClickerUpgrade,
GoldPerSecUpgrade,
// Items
};
/** Packet used by the client to buy items or upgrades
Packet used by the server to confirm transaction */
class PlayerBuyItemPacket : public Packet {
private:
ItemType m_ItemType;
std::uint8_t m_Count;
public:
PlayerBuyItemPacket() {}
PlayerBuyItemPacket(ItemType itemType, std::uint8_t count) : m_ItemType(itemType), m_Count(count) {}
virtual ~PlayerBuyItemPacket() {}
virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const;
ItemType GetItemType() const { return m_ItemType; }
std::uint8_t GetCount() const { return m_Count; }
virtual PacketType GetType() const { return PacketType::PlayerBuyItem; }
};
/** Packet used by the client to buy mob upgrades
Packet used by the server to confirm transaction */
class PlayerBuyMobUpgradePacket : public Packet {
private:
game::MobType m_MobType;
std::uint8_t m_MobLevel;
public:
PlayerBuyMobUpgradePacket() {}
PlayerBuyMobUpgradePacket(game::MobType mobType, std::uint8_t level) : m_MobType(mobType), m_MobLevel(level) {}
virtual ~PlayerBuyMobUpgradePacket() {}
virtual DataBuffer Serialize(bool packetID = true) const;
virtual void Deserialize(DataBuffer& data);
virtual void Dispatch(PacketHandler* handler) const;
game::MobType GetMobType() const { return m_MobType; }
std::uint8_t GetLevel() const { return m_MobLevel; }
virtual PacketType GetType() const { return PacketType::PlayerBuyMobUpgrade; }
};
} // namespace protocol
} // namespace td

View File

@@ -1,8 +1,62 @@
#pragma once #pragma once
#ifdef __ANDROID__ #if !defined(TD_IMPL_OPENGL_ES2) \
#include <GLES3/gl3.h> && !defined(TD_IMPL_OPENGL_ES3) \
&& !defined(TD_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(TD_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(TD_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(TD_IMPL_OPENGL_LOADER_GLBINDING2) \
&& !defined(TD_IMPL_OPENGL_LOADER_GLBINDING3) \
&& !defined(TD_IMPL_OPENGL_LOADER_CUSTOM) \
&& !defined(__ANDROID__)
#if defined(__has_include)
#if __has_include(<GL/glew.h>)
#define TD_IMPL_OPENGL_LOADER_GLEW
#elif __has_include(<glad/glad.h>)
#define TD_IMPL_OPENGL_LOADER_GLAD
#elif __has_include(<GL/gl3w.h>)
#define TD_IMPL_OPENGL_LOADER_GL3W
#elif __has_include(<glbinding/glbinding.h>)
#define TD_IMPL_OPENGL_LOADER_GLBINDING3
#elif __has_include(<glbinding/Binding.h>)
#define TD_IMPL_OPENGL_LOADER_GLBINDING2
#else #else
#include "glbinding/gl/gl.h" #error "Cannot detect OpenGL loader!"
using namespace gl; #endif
#else
#error "Cannot detect loader with include detection !"
#endif
#endif
// Include correct files
#if defined(__ANDROID__)
#include <GLES3/gl3.h>
#elif defined(TD_IMPL_OPENGL_LOADER_GL3W)
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
#elif defined(TD_IMPL_OPENGL_LOADER_GLEW)
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code.
#elif defined(TD_IMPL_OPENGL_LOADER_GLAD)
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code.
#elif defined(TD_IMPL_OPENGL_LOADER_GLBINDING2)
#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
#endif
#include <glbinding/Binding.h> // Needs to be initialized with glbinding::Binding::initialize() in user's code.
#include <glbinding/gl/gl.h>
using namespace gl;
#elif defined(TD_IMPL_OPENGL_LOADER_GLBINDING3)
#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
#endif
#include <glbinding/glbinding.h>// Needs to be initialized with glbinding::initialize() in user's code.
#include <glbinding/gl/gl.h>
using namespace gl;
#else
#include TD_IMPL_OPENGL_LOADER_CUSTOM
#endif #endif

View File

@@ -1,14 +1,6 @@
/* #pragma once
* Renderer.h
*
* Created on: 4 nov. 2020
* Author: simon
*/
#ifndef RENDER_RENDERER_H_ #include "Defines.h"
#define RENDER_RENDERER_H_
#include <glm/glm.hpp>
#include <memory> #include <memory>
#include "loader/GLLoader.h" #include "loader/GLLoader.h"
#include "render/shaders/WorldShader.h" #include "render/shaders/WorldShader.h"
@@ -19,44 +11,47 @@ namespace render {
class Renderer { class Renderer {
public: public:
static constexpr float m_AnimationSpeed = 2.0f; static constexpr float m_AnimationSpeed = 2.0f;
struct Model { struct Model {
GL::VertexArray* vao; GL::VertexArray* vao;
glm::vec2 positon; Vec2f positon;
}; Vec3f color = { 1, 1, 1 };
};
private: private:
std::unique_ptr<WorldShader> m_WorldShader; std::unique_ptr<shader::WorldShader> m_WorldShader;
std::unique_ptr<EntityShader> m_EntityShader; std::unique_ptr<shader::EntityShader> m_EntityShader;
bool m_IsometricView = true; Vec3f m_BackgroundColor;
float m_IsometricShade = m_IsometricView;
glm::vec2 m_CamPos{}; bool m_IsometricView = true;
float m_IsometricShade = m_IsometricView;
Vec2f m_CamPos{};
public: public:
Renderer(); Renderer();
~Renderer(); ~Renderer();
bool init(); bool Init();
void prepare(); void Prepare();
void resize(const int width, const int height); void Resize(const int width, const int height);
void renderVAO(const GL::VertexArray& vao); void RenderVAO(const GL::VertexArray& vao);
void renderModel(const Model& model); void RenderModel(const Model& model);
void setZoom(float zoom); void SetZoom(float zoom);
void setCamMovement(const glm::vec2& mov); void SetCamMovement(const Vec2f& mov);
void setCamPos(const glm::vec2& newPos); void SetCamPos(const Vec2f& newPos);
void setIsometricView(bool isometric); // false = 2D true = Isometric void SetIsometricView(bool isometric); // false = 2D true = Isometric
glm::vec2 getCursorWorldPos(const glm::vec2& cursorPos, float aspectRatio, float zoom, float windowWidth, float windowHeight); void SetBackgroundColor(const Vec3f& color) { m_BackgroundColor = color; }
Vec2f GetCursorWorldPos(const Vec2f& cursorPos, float aspectRatio, float zoom, float windowWidth, float windowHeight);
private: private:
void updateIsometricView(); void UpdateIsometricView();
void updateIsometricFade(); void UpdateIsometricFade();
void initShader(); void InitShaders();
}; };
} // namespace render } // namespace render
} // namespace td } // namespace td
#endif /* RENDER_RENDERER_H_ */

View File

@@ -11,23 +11,27 @@ namespace render {
class VertexCache { class VertexCache {
typedef std::vector<float> Vector; typedef std::vector<float> Vector;
typedef std::pair<Vector::iterator, Vector::iterator> ElementsIndex;
typedef std::pair<ElementsIndex, ElementsIndex> DataIndex; struct DataIndex {
Vector position;
Vector color;
};
private: private:
Vector m_Positions; std::size_t m_VertexCount;
Vector m_Colors; std::unordered_map<std::uint64_t, DataIndex> m_Indexes;
std::unordered_map<std::uint64_t, DataIndex> m_Indexes; std::unique_ptr<GL::VertexArray> m_VertexArray;
std::unique_ptr<GL::VertexArray> m_VertexArray;
public: public:
void addData(std::uint64_t index, std::vector<float> positions, std::vector<float> colors); VertexCache() : m_VertexCount(0) {}
void removeData(std::uint64_t index);
void clear();
void updateVertexArray();
const GL::VertexArray& getVertexArray() const { return *m_VertexArray; } void AddData(std::uint64_t index, std::vector<float> positions, std::vector<float> colors);
bool isEmpty() const { return m_VertexArray == nullptr; } void RemoveData(std::uint64_t index);
void Clear();
void UpdateVertexArray();
const GL::VertexArray& GetVertexArray() const { return *m_VertexArray; }
bool IsEmpty() const { return m_VertexArray == nullptr; }
}; };
} // namespace render } // namespace render

View File

@@ -6,12 +6,12 @@
#include "render/VertexCache.h" #include "render/VertexCache.h"
#include "render/gui/TowerPlacePopup.h" #include "render/gui/TowerPlacePopup.h"
#include "render/gui/TowerUpgradePopup.h"
#include "render/gui/MobTooltip.h" #include "render/gui/MobTooltip.h"
#include "render/gui/CastleTooltip.h"
#include "render/gui/imgui/imgui.h" #include "render/gui/imgui/imgui.h"
#include <glm/glm.hpp>
namespace td { namespace td {
namespace client { namespace client {
@@ -23,56 +23,63 @@ class ClientGame;
namespace render { namespace render {
class WorldRenderer { class WorldRenderer : public game::WorldListener {
private: private:
client::ClientGame* m_Client; client::ClientGame* m_Client;
Renderer* m_Renderer; Renderer* m_Renderer;
game::World* m_World; game::World* m_World;
std::unique_ptr<GL::VertexArray> m_WorldVao, m_MobVao, m_SelectTileVao; std::unique_ptr<GL::VertexArray> m_WorldVao, m_MobVao, m_SelectTileVao;
glm::vec2 m_CamPos; Vec2f m_CamPos;
glm::vec2 m_CursorPos; Vec2f m_CursorPos;
glm::vec2 m_HoldCursorPos; Vec2f m_HoldCursorPos;
glm::vec2 m_LastClicked; Vec2f m_LastClicked;
float m_Zoom; float m_Zoom;
float m_CamSensibility = 1; float m_CamSensibility = 1;
bool m_PopupOpened = false; bool m_PopupOpened = false;
VertexCache m_TowersCache; VertexCache m_TowersCache;
std::unique_ptr<gui::TowerPlacePopup> m_TowerPlacePopup; std::unique_ptr<gui::TowerPlacePopup> m_TowerPlacePopup;
std::unique_ptr<gui::MobTooltip> m_MobTooltip; std::unique_ptr<gui::TowerUpgradePopup> m_TowerUpgradePopup;
std::unique_ptr<gui::MobTooltip> m_MobTooltip;
std::unique_ptr<gui::CastleTooltip> m_CastleTooltip;
public: public:
WorldRenderer(game::World* world, client::ClientGame* client); WorldRenderer(game::World* world, client::ClientGame* client);
~WorldRenderer(); ~WorldRenderer();
void loadModels(); void LoadModels();
void addTower(game::TowerPtr tower); static ImVec4 GetImGuiTeamColor(game::TeamColor color);
void removeTower(game::TowerPtr tower);
static ImVec4 getImGuiTeamColor(game::TeamColor color); void Update();
void Render();
void update(); void SetCamPos(float camX, float camY);
void render();
void setCamPos(float camX, float camY); void MoveCam(float relativeX, float relativeY, float aspectRatio);
void ChangeZoom(float zoom);
void moveCam(float relativeX, float relativeY, float aspectRatio); // WorldListener
void changeZoom(float zoom);
virtual void OnTowerAdd(game::TowerPtr tower);
virtual void OnTowerRemove(game::TowerPtr tower);
private: private:
void click(); void Click();
void renderWorld() const; void RenderWorld() const;
void renderTowers() const; void RenderTowers() const;
void renderMobs() const; void RenderMobs() const;
void renderTileSelect() const; void RenderTileSelect() const;
void renderPopups(); void RenderPopups();
void renderTowerUpgradePopup(); void RenderMobTooltip() const;
void renderMobTooltip() const; void RenderCastleTooltip() const;
void detectClick(); void DetectClick();
void detectMobHovering() const; void DetectMobHovering() const;
glm::vec2 getCursorWorldPos() const; void DetectCastleHovering() const;
glm::vec2 getClickWorldPos() const; void RenderTooltips() const;
void RemoveTower();
Vec2f GetCursorWorldPos() const;
Vec2f GetClickWorldPos() const;
void updateCursorPos(); void UpdateCursorPos();
}; };
} // namespace render } // namespace render

View File

@@ -0,0 +1,28 @@
#pragma once
#include "GuiWidget.h"
namespace td {
namespace game {
class TeamCastle;
} // namespace game
namespace gui {
class CastleTooltip : public GuiWidget {
private:
const game::TeamCastle* m_Castle;
public:
CastleTooltip(client::Client* client);
virtual void Render();
void SetCastle(const game::TeamCastle* castle) { m_Castle = castle; }
bool IsShown() { return m_Castle != nullptr; }
};
} // namespace gui
} // namespace td

View File

@@ -7,13 +7,13 @@ namespace gui {
class FrameMenu : public GuiWidget { class FrameMenu : public GuiWidget {
private: private:
bool m_VSync; bool m_VSync;
bool m_IsometricView; bool m_IsometricView;
bool m_ShowDemoWindow; bool m_ShowDemoWindow;
public: public:
FrameMenu(client::Client* client); FrameMenu(client::Client* client);
virtual void render(); virtual void Render();
}; };
} // namespace gui } // namespace gui

View File

@@ -7,17 +7,17 @@ namespace gui {
class GameMenu : public GuiWidget { class GameMenu : public GuiWidget {
private: private:
std::unique_ptr<SummonMenu> m_SummonMenu; std::unique_ptr<SummonMenu> m_SummonMenu;
public: public:
GameMenu(client::Client* client); GameMenu(client::Client* client);
virtual void render(); virtual void Render();
private: private:
void showTPS(); void ShowTPS();
void showStats(); void ShowStats();
void showPlayers(); void ShowPlayers();
void showLobbyProgress(); void ShowLobbyProgress();
void showTeamSelection(); void ShowTeamSelection();
}; };
} // namespace gui } // namespace gui

View File

@@ -10,17 +10,19 @@ namespace gui {
class GuiManager { class GuiManager {
private: private:
std::vector<std::shared_ptr<GuiWidget>> m_Widgets; std::vector<std::unique_ptr<GuiWidget>> m_Widgets;
public: public:
void renderWidgets() { GuiManager() {}
for (auto widget : m_Widgets) {
widget->render();
}
}
void addWidgets(const std::shared_ptr<GuiWidget>& widget) { void RenderWidgets() {
m_Widgets.push_back(std::move(widget)); for (auto& widget : m_Widgets) {
} widget->Render();
}
}
void AddWidget(std::unique_ptr<GuiWidget>&& widget) {
m_Widgets.push_back(std::move(widget));
}
}; };
} // namespace gui } // namespace gui

View File

@@ -10,13 +10,13 @@ namespace gui {
class GuiWidget { class GuiWidget {
protected: protected:
client::Client* m_Client; client::Client* m_Client;
public: public:
GuiWidget(client::Client* client) : m_Client(client) {} GuiWidget(client::Client* client) : m_Client(client) {}
client::Client* getClient() { return m_Client; } client::Client* GetClient() { return m_Client; }
virtual void render() = 0; virtual void Render() = 0;
}; };
} // namespace gui } // namespace gui

View File

@@ -0,0 +1,9 @@
#pragma once
namespace td {
namespace gui {
extern void RenderLifeProgress(float progress);
} // namespace gui
} // namespace td

View File

@@ -13,24 +13,24 @@ namespace gui {
class MainMenu : public GuiWidget { class MainMenu : public GuiWidget {
private: private:
bool m_TriedToConnect = false; bool m_TriedToConnect = false;
bool m_TriedToCreate = false; bool m_TriedToCreate = false;
std::string m_ConnectAddress; std::string m_ConnectAddress;
int m_ConnectPort; int m_ConnectPort;
int m_ServerPort = 25565; int m_ServerPort = 25565;
std::string m_WorldFilePath; std::string m_WorldFilePath;
imgui_addons::ImGuiFileBrowser m_FileDialog; imgui_addons::ImGuiFileBrowser m_FileDialog;
std::unique_ptr<server::Server> m_Server; std::unique_ptr<server::Server> m_Server;
public: public:
MainMenu(client::Client* client); MainMenu(client::Client* client);
~MainMenu(); ~MainMenu();
virtual void render(); virtual void Render();
const server::Server* getServer() const { return m_Server.get(); } const server::Server* GetServer() const { return m_Server.get(); }
private: private:
bool startServer(); bool StartServer();
}; };
} // namespace gui } // namespace gui

View File

@@ -14,13 +14,14 @@ namespace gui {
class MobTooltip : public GuiWidget { class MobTooltip : public GuiWidget {
private: private:
const game::Mob* m_Mob; const game::Mob* m_Mob;
public: public:
MobTooltip(client::Client* client); MobTooltip(client::Client* client);
virtual void render(); virtual void Render();
void setMob(const game::Mob* mob) { m_Mob = mob; } void SetMob(const game::Mob* mob) { m_Mob = mob; }
bool IsShown() { return m_Mob != nullptr; }
}; };
} // namespace gui } // namespace gui

View File

@@ -10,16 +10,20 @@ namespace gui {
class SummonMenu : public GuiWidget { class SummonMenu : public GuiWidget {
private: private:
bool m_MenuOpened; bool m_MenuOpened;
int m_ImageWidth = 100; int m_ImageWidth = 100;
static constexpr int m_MobTypeCount = static_cast<std::size_t>(td::game::MobType::MOB_COUNT); float m_Cooldown;
std::array<int, static_cast<std::size_t>(m_MobTypeCount)> m_Values; float m_LastCooldown;
static constexpr int m_MobTypeCount = static_cast<std::size_t>(td::game::MobType::MOB_COUNT);
std::array<int, static_cast<std::size_t>(m_MobTypeCount)> m_Values;
public: public:
SummonMenu(client::Client* client); SummonMenu(client::Client* client);
virtual void render(); void SetCooldown(float cooldown);
virtual void Render();
private: private:
void setSummonMax(int valueIndex); void SetSummonMax(int valueIndex);
}; };
} // namespace gui } // namespace gui

View File

@@ -9,6 +9,8 @@
#include <memory> #include <memory>
#include "render/gui/GuiManager.h"
struct SDL_Window; struct SDL_Window;
typedef void* SDL_GLContext; typedef void* SDL_GLContext;
@@ -20,39 +22,27 @@ class Client;
} // namespace client } // namespace client
namespace gui {
class MainMenu;
class GameMenu;
class FrameMenu;
class UpdateMenu;
} // namespace gui
namespace render { namespace render {
class Renderer; class Renderer;
class TowerGui { class TowerGui {
private: private:
SDL_Window* m_Window; SDL_Window* m_Window;
SDL_GLContext m_GlContext; SDL_GLContext m_GlContext;
td::render::Renderer* m_Renderer; td::render::Renderer* m_Renderer;
std::unique_ptr<td::client::Client> m_Client; td::gui::GuiManager m_GuiManager;
std::unique_ptr<td::gui::MainMenu> m_MainMenu; std::unique_ptr<td::client::Client> m_Client;
std::unique_ptr<td::gui::GameMenu> m_GameMenu;
std::unique_ptr<td::gui::FrameMenu> m_FrameMenu;
std::unique_ptr<td::gui::UpdateMenu> m_UpdateMenu;
public: public:
TowerGui(SDL_Window* wndow, SDL_GLContext glContext, td::render::Renderer* renderer); TowerGui(SDL_Window* wndow, SDL_GLContext glContext, td::render::Renderer* renderer);
~TowerGui(); ~TowerGui();
void render(); void Render();
private: private:
void initWidgets(); void InitWidgets();
void tick(); void Tick();
void beginFrame(); void BeginFrame();
void endFrame(); void EndFrame();
}; };
} // namespace render } // namespace render

View File

@@ -2,26 +2,26 @@
#include "GuiWidget.h" #include "GuiWidget.h"
#include <glm/glm.hpp> #include "Defines.h"
namespace td { namespace td {
namespace gui { namespace gui {
class TowerPlacePopup : public GuiWidget { class TowerPlacePopup : public GuiWidget {
private: private:
glm::vec2 m_ClickWorldPos; Vec2f m_ClickWorldPos;
public: public:
TowerPlacePopup(client::Client* client); TowerPlacePopup(client::Client* client);
virtual void render(); virtual void Render();
void setClickPos(const glm::vec2& worldPos); void SetClickPos(const Vec2f& worldPos);
private: private:
static constexpr float m_TowerPopupTileWidth = 200.0f; static constexpr float m_TowerPopupTileWidth = 200.0f;
static constexpr float m_TowerPopupTileHeight = 200.0f; static constexpr float m_TowerPopupTileHeight = 200.0f;
static constexpr float m_PlaceTowerButtonWidth = 150.0f; static constexpr float m_PlaceTowerButtonWidth = 150.0f;
static constexpr float m_PlaceTowerButtonHeight = 35.0f; static constexpr float m_PlaceTowerButtonHeight = 35.0f;
}; };
} // namespace gui } // namespace gui

View File

@@ -0,0 +1,32 @@
#pragma once
#include "GuiWidget.h"
#include "Defines.h"
namespace td {
namespace gui {
class TowerUpgradePopup : public GuiWidget {
private:
Vec2f m_ClickWorldPos;
bool m_ShouldBeClosed;
bool m_Opened;
public:
TowerUpgradePopup(client::Client* client);
virtual void Render();
void SetClickPos(const Vec2f& worldPos);
bool IsPopupOpened();
private:
static constexpr float m_TowerPopupTileWidth = 200.0f;
static constexpr float m_TowerPopupTileHeight = 200.0f;
static constexpr float m_PlaceTowerButtonWidth = 150.0f;
static constexpr float m_PlaceTowerButtonHeight = 35.0f;
};
} // namespace gui
} // namespace td

View File

@@ -2,27 +2,34 @@
#include "GuiWidget.h" #include "GuiWidget.h"
#include "updater/Updater.h"
#include <future> #include <future>
#include <memory>
namespace td { namespace td {
namespace utils {
class Updater;
} // namespace utils
namespace gui { namespace gui {
class UpdateMenu : public GuiWidget { class UpdateMenu : public GuiWidget {
private: private:
bool m_Opened; bool m_Opened;
std::string m_Error; std::string m_Error;
utils::Updater m_Updater; std::unique_ptr<utils::Updater> m_Updater;
std::shared_future<bool> m_UpdateAvailable; std::shared_future<bool> m_UpdateAvailable;
public: public:
UpdateMenu(client::Client* client); UpdateMenu(client::Client* client);
virtual ~UpdateMenu();
virtual void render(); virtual void Render();
private: private:
void checkUpdates(); void CheckUpdates();
bool isUpdateChecked(); bool IsUpdateChecked();
void renderErrorPopup(); void RenderErrorPopup();
}; };
} // namespace gui } // namespace gui

View File

@@ -107,8 +107,20 @@ namespace ImGui
} }
*/ */
#ifdef __ANDROID__ #include "render/GL.h"
#define IMGUI_IMPL_OPENGL_ES3
#if defined(__ANDROID__)
#define IMGUI_IMPL_OPENGL_LOADER_ES3
#elif defined(TD_IMPL_OPENGL_LOADER_GL3W)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#elif defined(TD_IMPL_OPENGL_LOADER_GLEW)
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#elif defined(TD_IMPL_OPENGL_LOADER_GLAD)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#elif defined(TD_IMPL_OPENGL_LOADER_GLBINDING2)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
#elif defined(TD_IMPL_OPENGL_LOADER_GLBINDING3)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
#else #else
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2 #define IMGUI_IMPL_OPENGL_LOADER_CUSTOM
#endif #endif

View File

@@ -17,8 +17,9 @@
namespace GL { namespace GL {
struct VertexAttribPointer { struct VertexAttribPointer {
unsigned int m_Index, m_Size; unsigned int m_Index;
int m_Offset; unsigned int m_Size;
unsigned int m_Offset;
}; };
class VertexBuffer { class VertexBuffer {
@@ -27,6 +28,7 @@ private:
std::vector<VertexAttribPointer> m_VertexAttribs; std::vector<VertexAttribPointer> m_VertexAttribs;
public: public:
REMOVE_COPY(VertexBuffer); REMOVE_COPY(VertexBuffer);
VertexBuffer(VertexBuffer&& other) { VertexBuffer(VertexBuffer&& other) {
m_VertexAttribs = std::move(other.m_VertexAttribs); m_VertexAttribs = std::move(other.m_VertexAttribs);
m_ID = other.m_ID; m_ID = other.m_ID;
@@ -34,12 +36,14 @@ public:
other.m_ID = 0; other.m_ID = 0;
other.m_DataStride = 0; other.m_DataStride = 0;
} }
VertexBuffer(const std::vector<float>& data, unsigned int stride); VertexBuffer(const std::vector<float>& data, unsigned int stride);
~VertexBuffer(); ~VertexBuffer();
void bind() const;
void unbind() const; void Bind() const;
void addVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset); void Unbind() const;
void bindVertexAttribs() const; void AddVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset);
void BindVertexAttribs() const;
}; };
class VertexArray { class VertexArray {
@@ -48,6 +52,7 @@ private:
std::vector<VertexBuffer> m_VertexBuffers; //use to destroy vbos when become unused std::vector<VertexBuffer> m_VertexBuffers; //use to destroy vbos when become unused
public: public:
REMOVE_COPY(VertexArray); REMOVE_COPY(VertexArray);
VertexArray(VertexArray&& other) { VertexArray(VertexArray&& other) {
m_ID = other.m_ID; m_ID = other.m_ID;
m_VertexCount = other.m_VertexCount; m_VertexCount = other.m_VertexCount;
@@ -55,12 +60,14 @@ public:
other.m_VertexCount = 0; other.m_VertexCount = 0;
other.m_ID = 0; other.m_ID = 0;
} }
VertexArray(unsigned int vertexCount); VertexArray(unsigned int vertexCount);
~VertexArray(); ~VertexArray();
unsigned int getVertexCount() const { return m_VertexCount; }
void bindVertexBuffer(VertexBuffer& vbo); unsigned int GetVertexCount() const { return m_VertexCount; }
void bind() const; void BindVertexBuffer(VertexBuffer& vbo);
void unbind() const; void Bind() const;
void Unbind() const;
}; };
} }

View File

@@ -9,7 +9,9 @@
#define RENDER_LOADER_TEXTURELOADER_H_ #define RENDER_LOADER_TEXTURELOADER_H_
namespace TextureLoader { namespace TextureLoader {
const unsigned int loadGLTexture(const char* fileName);
unsigned int LoadGLTexture(const char* fileName);
} }

View File

@@ -9,14 +9,14 @@ namespace render {
namespace WorldLoader { namespace WorldLoader {
struct RenderData { struct RenderData {
std::vector<float> positions; std::vector<float> positions;
std::vector<float> colors; std::vector<float> colors;
}; };
GL::VertexArray loadMobModel(); GL::VertexArray LoadMobModel();
GL::VertexArray loadWorldModel(const td::game::World* world); GL::VertexArray LoadWorldModel(const td::game::World* world);
GL::VertexArray loadTileSelectModel(); GL::VertexArray LoadTileSelectModel();
RenderData loadTowerModel(game::TowerPtr tower); RenderData LoadTowerModel(game::TowerPtr tower);
} // namespace WorldLoader } // namespace WorldLoader

View File

@@ -2,17 +2,31 @@
#include "ShaderProgram.h" #include "ShaderProgram.h"
namespace td {
namespace shader {
class EntityShader : public ShaderProgram { class EntityShader : public ShaderProgram {
private: private:
unsigned int location_cam = 0, location_zoom = 0, location_aspect_ratio = 0, location_translation = 0, location_viewtype = 0; unsigned int m_LocationCam = 0;
unsigned int m_LocationZoom = 0;
unsigned int m_LocationAspectRatio = 0;
unsigned int m_LocationTranslation = 0;
unsigned int m_LocationViewtype = 0;
unsigned int m_LocationColorEffect = 0;
protected: protected:
void getAllUniformLocation(); virtual void GetAllUniformLocation();
public: public:
EntityShader(); EntityShader();
void loadShader();
void setCamPos(const glm::vec2& camPos); void LoadShader();
void setZoom(float zoom); void SetCamPos(const Vec2f& camPos);
void setAspectRatio(float aspectRatio); void SetZoom(float zoom);
void setModelPos(const glm::vec2& modelPos); void SetAspectRatio(float aspectRatio);
void setIsometricView(float isometric); void SetModelPos(const Vec2f& modelPos);
void SetIsometricView(float isometric);
void SetColorEffect(const Vec3f& color);
}; };
} // namespace shader
} // namespace td

View File

@@ -1,44 +1,42 @@
/* #pragma once
* ShaderProgram.h
*
* Created on: 31 janv. 2020
* Author: simon
*/
#ifndef RENDER_SHADERS_SHADERPROGRAM_H_
#define RENDER_SHADERS_SHADERPROGRAM_H_
#include <string> #include <string>
#include <glm/glm.hpp> #include "Defines.h"
#include "render/GL.h" #include "render/GL.h"
namespace td {
namespace shader {
class ShaderProgram { class ShaderProgram {
public: public:
ShaderProgram(); ShaderProgram();
virtual ~ShaderProgram(); virtual ~ShaderProgram();
void start() const;
void stop() const; void Start() const;
void loadProgramFile(const std::string& vertexFile, const std::string& fragmentFile); void Stop() const;
void loadProgram(const std::string& vertexSource, const std::string& fragmentSource);
void LoadProgramFile(const std::string& vertexFile, const std::string& fragmentFile);
void LoadProgram(const std::string& vertexSource, const std::string& fragmentSource);
protected: protected:
virtual void getAllUniformLocation() = 0; virtual void GetAllUniformLocation() = 0;
int getUniformLocation(const std::string& uniformName)const; int GetUniformLocation(const std::string& uniformName) const;
void loadFloat(const int location, const float value)const;
void loadInt(const int& location, const int& value)const; void LoadFloat(unsigned int location, float value) const;
void loadVector(const int& location, const glm::vec2& vector)const; void LoadInt(unsigned int location, int value) const;
void loadVector(const int& location, const glm::vec3& vector)const; void LoadVector(unsigned int location, const Vec2f& vector) const;
void loadVector(const int& location, const glm::vec4& vector)const; void LoadVector(unsigned int location, const Vec3f& vector) const;
void loadBoolean(const int& location, const bool& value)const; void LoadBoolean(unsigned int location, bool value) const;
void loadMatrix(const int& location, const glm::mat4& matrix); void CleanUp() const;
void cleanUp() const;
private: private:
unsigned int programID; unsigned int m_ProgramID;
unsigned int vertexShaderID; unsigned int m_VertexShaderID;
unsigned int fragmentShaderID; unsigned int m_FragmentShaderID;
int loadShaderFromFile(const std::string& file, GLenum type);
int loadShader(const std::string& source, GLenum type); unsigned int LoadShaderFromFile(const std::string& file, GLenum type);
unsigned int LoadShader(const std::string& source, GLenum type);
}; };
#endif /* RENDER_SHADERS_SHADERPROGRAM_H_ */ } // namespace shader
} // namespace td

View File

@@ -1,27 +1,23 @@
/* #pragma once
* GameShader.h
*
* Created on: 4 nov. 2020
* Author: simon
*/
#ifndef RENDER_SHADERS_GAMESHADER_H_
#define RENDER_SHADERS_GAMESHADER_H_
#include "ShaderProgram.h" #include "ShaderProgram.h"
namespace td {
namespace shader {
class WorldShader : public ShaderProgram { class WorldShader : public ShaderProgram {
private: private:
unsigned int location_cam = 0, location_zoom = 0, location_aspect_ratio = 0, location_viewtype = 0; unsigned int m_LocationCam = 0, m_LocationZoom = 0, m_LocationAspectRatio = 0, m_LocationViewtype = 0;
protected: protected:
void getAllUniformLocation(); void GetAllUniformLocation();
public: public:
WorldShader(); WorldShader();
void loadShader(); void LoadShader();
void setCamPos(const glm::vec2& camPos); void SetCamPos(const Vec2f& camPos);
void setZoom(float zoom); void SetZoom(float zoom);
void setAspectRatio(float aspectRatio); void SetAspectRatio(float aspectRatio);
void setIsometricView(float isometric); void SetIsometricView(float isometric);
}; };
#endif /* RENDER_SHADERS_GAMESHADER_H_ */ } // namespace shader
} // namespace td

View File

@@ -2,44 +2,42 @@
#include "misc/DataBuffer.h" #include "misc/DataBuffer.h"
#include <ctime> #define TD_VERSION "alpha-0.3.0"
#define TD_VERSION "alpha-0.0.3"
namespace td { namespace td {
namespace utils { namespace utils {
class Updater { class Updater {
private: private:
float m_Progress; float m_Progress;
bool m_DownloadComplete; bool m_DownloadComplete;
bool m_FileWrited; bool m_FileWrited;
bool m_CancelDownload; bool m_CancelDownload;
DataBuffer m_FileBuffer; DataBuffer m_FileBuffer;
std::string m_LastVersion; std::string m_LastVersion;
public: public:
Updater() : m_Progress(0), m_DownloadComplete(false), m_FileWrited(false), m_CancelDownload(false) {} Updater() : m_Progress(0), m_DownloadComplete(false), m_FileWrited(false), m_CancelDownload(false) {}
bool checkUpdate(); bool CheckUpdate();
void downloadUpdate(); void DownloadUpdate();
void cancelDownload() { m_CancelDownload = true; m_Progress = 0.0f; m_DownloadComplete = false; } void CancelDownload() { m_CancelDownload = true; m_Progress = 0.0f; m_DownloadComplete = false; }
bool writeFile(); bool WriteFile();
void clearCache() { m_FileBuffer.Clear(); } void ClearCache() { m_FileBuffer.Clear(); }
float getDownloadProgress() { return m_Progress; } float GetDownloadProgress() { return m_Progress; }
bool isDownloadComplete() { return m_DownloadComplete; } bool IsDownloadComplete() { return m_DownloadComplete; }
bool isFileWrited() { return m_FileWrited; } bool IsFileWrited() { return m_FileWrited; }
static std::string getLocalFilePath(); static std::string GetLocalFilePath();
static void removeOldFile(); static void RemoveOldFile();
static std::string getCurrentVersion() { return TD_VERSION; } static std::string GetCurrentVersion() { return TD_VERSION; }
std::string getLastVersion() { return m_LastVersion; } std::string GetLastVersion() { return m_LastVersion; }
bool canUpdate(); bool CanUpdate();
private: private:
std::string getDownloadFileURL(); std::string GetDownloadFileURL();
}; };
} // namespace utils } // namespace utils

View File

@@ -11,19 +11,19 @@
namespace Display { namespace Display {
bool create(); bool Create();
void render(); void Render();
void update(); void Update();
void destroy(); void Destroy();
void pollEvents(); void PollEvents();
bool isCloseRequested(); bool IsCloseRequested();
bool isMouseDown(int button); bool IsMouseDown(int button);
float getAspectRatio(); float GetAspectRatio();
int getWindowWidth(); int GetWindowWidth();
int getWindowHeight(); int GetWindowHeight();
} }

40
libs/discordrpc/backoff.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include <algorithm>
#include <random>
#include <stdint.h>
#include <time.h>
struct Backoff {
int64_t minAmount;
int64_t maxAmount;
int64_t current;
int fails;
std::mt19937_64 randGenerator;
std::uniform_real_distribution<> randDistribution;
double rand01() { return randDistribution(randGenerator); }
Backoff(int64_t min, int64_t max)
: minAmount(min)
, maxAmount(max)
, current(min)
, fails(0)
, randGenerator((uint64_t)time(0))
{
}
void reset()
{
fails = 0;
current = minAmount;
}
int64_t nextDelay()
{
++fails;
int64_t delay = (int64_t)((double)current * 2.0 * rand01());
current = std::min(current + delay, maxAmount);
return current;
}
};

View File

@@ -0,0 +1,19 @@
#pragma once
// This is to wrap the platform specific kinds of connect/read/write.
#include <stdint.h>
#include <stdlib.h>
// not really connectiony, but need per-platform
int GetProcessId();
struct BaseConnection {
static BaseConnection* Create();
static void Destroy(BaseConnection*&);
bool isOpen{false};
bool Open();
bool Close();
bool Write(const void* data, size_t length);
bool Read(void* data, size_t length);
};

View File

@@ -0,0 +1,129 @@
#ifdef __unix__
#include "connection.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
int GetProcessId()
{
return ::getpid();
}
struct BaseConnectionUnix : public BaseConnection {
int sock{-1};
};
static BaseConnectionUnix Connection;
static sockaddr_un PipeAddr{};
#ifdef MSG_NOSIGNAL
static int MsgFlags = MSG_NOSIGNAL;
#else
static int MsgFlags = 0;
#endif
static const char* GetTempPath()
{
const char* temp = getenv("XDG_RUNTIME_DIR");
temp = temp ? temp : getenv("TMPDIR");
temp = temp ? temp : getenv("TMP");
temp = temp ? temp : getenv("TEMP");
temp = temp ? temp : "/tmp";
return temp;
}
/*static*/ BaseConnection* BaseConnection::Create()
{
PipeAddr.sun_family = AF_UNIX;
return &Connection;
}
/*static*/ void BaseConnection::Destroy(BaseConnection*& c)
{
auto self = reinterpret_cast<BaseConnectionUnix*>(c);
self->Close();
c = nullptr;
}
bool BaseConnection::Open()
{
const char* tempPath = GetTempPath();
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
self->sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (self->sock == -1) {
return false;
}
fcntl(self->sock, F_SETFL, O_NONBLOCK);
#ifdef SO_NOSIGPIPE
int optval = 1;
setsockopt(self->sock, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
#endif
for (int pipeNum = 0; pipeNum < 10; ++pipeNum) {
snprintf(
PipeAddr.sun_path, sizeof(PipeAddr.sun_path), "%s/discord-ipc-%d", tempPath, pipeNum);
int err = connect(self->sock, (const sockaddr*)&PipeAddr, sizeof(PipeAddr));
if (err == 0) {
self->isOpen = true;
return true;
}
}
self->Close();
return false;
}
bool BaseConnection::Close()
{
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
if (self->sock == -1) {
return false;
}
close(self->sock);
self->sock = -1;
self->isOpen = false;
return true;
}
bool BaseConnection::Write(const void* data, size_t length)
{
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
if (self->sock == -1) {
return false;
}
ssize_t sentBytes = send(self->sock, data, length, MsgFlags);
if (sentBytes < 0) {
Close();
}
return sentBytes == (ssize_t)length;
}
bool BaseConnection::Read(void* data, size_t length)
{
auto self = reinterpret_cast<BaseConnectionUnix*>(this);
if (self->sock == -1) {
return false;
}
int res = (int)recv(self->sock, data, length, MsgFlags);
if (res < 0) {
if (errno == EAGAIN) {
return false;
}
Close();
}
else if (res == 0) {
Close();
}
return res == (int)length;
}
#endif

View File

@@ -0,0 +1,132 @@
#if defined(_WIN32) || defined(_WIN64)
#include "connection.h"
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOSERVICE
#define NOIME
#include <assert.h>
#include <windows.h>
int GetProcessId()
{
return (int)::GetCurrentProcessId();
}
struct BaseConnectionWin : public BaseConnection {
HANDLE pipe{INVALID_HANDLE_VALUE};
};
static BaseConnectionWin Connection;
/*static*/ BaseConnection* BaseConnection::Create()
{
return &Connection;
}
/*static*/ void BaseConnection::Destroy(BaseConnection*& c)
{
auto self = reinterpret_cast<BaseConnectionWin*>(c);
self->Close();
c = nullptr;
}
bool BaseConnection::Open()
{
wchar_t pipeName[]{L"\\\\?\\pipe\\discord-ipc-0"};
const size_t pipeDigit = sizeof(pipeName) / sizeof(wchar_t) - 2;
pipeName[pipeDigit] = L'0';
auto self = reinterpret_cast<BaseConnectionWin*>(this);
for (;;) {
self->pipe = ::CreateFileW(
pipeName, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (self->pipe != INVALID_HANDLE_VALUE) {
self->isOpen = true;
return true;
}
auto lastError = GetLastError();
if (lastError == ERROR_FILE_NOT_FOUND) {
if (pipeName[pipeDigit] < L'9') {
pipeName[pipeDigit]++;
continue;
}
}
else if (lastError == ERROR_PIPE_BUSY) {
if (!WaitNamedPipeW(pipeName, 10000)) {
return false;
}
continue;
}
return false;
}
}
bool BaseConnection::Close()
{
auto self = reinterpret_cast<BaseConnectionWin*>(this);
::CloseHandle(self->pipe);
self->pipe = INVALID_HANDLE_VALUE;
self->isOpen = false;
return true;
}
bool BaseConnection::Write(const void* data, size_t length)
{
if (length == 0) {
return true;
}
auto self = reinterpret_cast<BaseConnectionWin*>(this);
assert(self);
if (!self) {
return false;
}
if (self->pipe == INVALID_HANDLE_VALUE) {
return false;
}
assert(data);
if (!data) {
return false;
}
const DWORD bytesLength = (DWORD)length;
DWORD bytesWritten = 0;
return ::WriteFile(self->pipe, data, bytesLength, &bytesWritten, nullptr) == TRUE &&
bytesWritten == bytesLength;
}
bool BaseConnection::Read(void* data, size_t length)
{
assert(data);
if (!data) {
return false;
}
auto self = reinterpret_cast<BaseConnectionWin*>(this);
assert(self);
if (!self) {
return false;
}
if (self->pipe == INVALID_HANDLE_VALUE) {
return false;
}
DWORD bytesAvailable = 0;
if (::PeekNamedPipe(self->pipe, nullptr, 0, nullptr, &bytesAvailable, nullptr)) {
if (bytesAvailable >= length) {
DWORD bytesToRead = (DWORD)length;
DWORD bytesRead = 0;
if (::ReadFile(self->pipe, data, bytesToRead, &bytesRead, nullptr) == TRUE) {
assert(bytesToRead == bytesRead);
return true;
}
else {
Close();
}
}
}
else {
Close();
}
return false;
}
#endif

View File

@@ -0,0 +1,26 @@
#pragma once
#if defined(DISCORD_DYNAMIC_LIB)
#if defined(_WIN32)
#if defined(DISCORD_BUILDING_SDK)
#define DISCORD_EXPORT __declspec(dllexport)
#else
#define DISCORD_EXPORT __declspec(dllimport)
#endif
#else
#define DISCORD_EXPORT __attribute__((visibility("default")))
#endif
#else
#define DISCORD_EXPORT
#endif
#ifdef __cplusplus
extern "C" {
#endif
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,106 @@
#ifdef __unix__
#include "discord_rpc.h"
#include "discord_register.h"
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static bool Mkdir(const char* path)
{
int result = mkdir(path, 0755);
if (result == 0) {
return true;
}
if (errno == EEXIST) {
return true;
}
return false;
}
// we want to register games so we can run them from Discord client as discord-<appid>://
extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command)
{
// Add a desktop file and update some mime handlers so that xdg-open does the right thing.
const char* home = getenv("HOME");
if (!home) {
return;
}
char exePath[1024];
if (!command || !command[0]) {
ssize_t size = readlink("/proc/self/exe", exePath, sizeof(exePath));
if (size <= 0 || size >= (ssize_t)sizeof(exePath)) {
return;
}
exePath[size] = '\0';
command = exePath;
}
const char* desktopFileFormat = "[Desktop Entry]\n"
"Name=Game %s\n"
"Exec=%s %%u\n" // note: it really wants that %u in there
"Type=Application\n"
"NoDisplay=true\n"
"Categories=Discord;Games;\n"
"MimeType=x-scheme-handler/discord-%s;\n";
char desktopFile[2048];
int fileLen = snprintf(
desktopFile, sizeof(desktopFile), desktopFileFormat, applicationId, command, applicationId);
if (fileLen <= 0) {
return;
}
char desktopFilename[256];
snprintf(desktopFilename, sizeof(desktopFilename), "/discord-%s.desktop", applicationId);
char desktopFilePath[1024];
snprintf(desktopFilePath, sizeof(desktopFilePath), "%s/.local", home);
if (!Mkdir(desktopFilePath)) {
return;
}
strcat(desktopFilePath, "/share");
if (!Mkdir(desktopFilePath)) {
return;
}
strcat(desktopFilePath, "/applications");
if (!Mkdir(desktopFilePath)) {
return;
}
strcat(desktopFilePath, desktopFilename);
FILE* fp = fopen(desktopFilePath, "w");
if (fp) {
fwrite(desktopFile, 1, fileLen, fp);
fclose(fp);
}
else {
return;
}
char xdgMimeCommand[1024];
snprintf(xdgMimeCommand,
sizeof(xdgMimeCommand),
"xdg-mime default discord-%s.desktop x-scheme-handler/discord-%s",
applicationId,
applicationId);
if (system(xdgMimeCommand) < 0) {
fprintf(stderr, "Failed to register mime handler\n");
}
}
extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId,
const char* steamId)
{
char command[256];
sprintf(command, "xdg-open steam://rungameid/%s", steamId);
Discord_Register(applicationId, command);
}
#endif

View File

@@ -0,0 +1,190 @@
#if defined(_WIN32) || defined(_WIN64)
#include "discord_rpc.h"
#include "discord_register.h"
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOSERVICE
#define NOIME
#include <windows.h>
#include <psapi.h>
#include <cstdio>
/**
* Updated fixes for MinGW and WinXP
* This block is written the way it does not involve changing the rest of the code
* Checked to be compiling
* 1) strsafe.h belongs to Windows SDK and cannot be added to MinGW
* #include guarded, functions redirected to <string.h> substitutes
* 2) RegSetKeyValueW and LSTATUS are not declared in <winreg.h>
* The entire function is rewritten
*/
#ifdef __MINGW32__
#include <wchar.h>
/// strsafe.h fixes
static HRESULT StringCbPrintfW(LPWSTR pszDest, size_t cbDest, LPCWSTR pszFormat, ...)
{
HRESULT ret;
va_list va;
va_start(va, pszFormat);
cbDest /= 2; // Size is divided by 2 to convert from bytes to wide characters - causes segfault
// othervise
ret = vsnwprintf(pszDest, cbDest, pszFormat, va);
pszDest[cbDest - 1] = 0; // Terminate the string in case a buffer overflow; -1 will be returned
va_end(va);
return ret;
}
#else
#include <cwchar>
#include <strsafe.h>
#endif // __MINGW32__
/// winreg.h fixes
#ifndef LSTATUS
#define LSTATUS LONG
#endif
#ifdef RegSetKeyValueW
#undefine RegSetKeyValueW
#endif
#define RegSetKeyValueW regset
static LSTATUS regset(HKEY hkey,
LPCWSTR subkey,
LPCWSTR name,
DWORD type,
const void* data,
DWORD len)
{
HKEY htkey = hkey, hsubkey = nullptr;
LSTATUS ret;
if (subkey && subkey[0]) {
if ((ret = RegCreateKeyExW(hkey, subkey, 0, 0, 0, KEY_ALL_ACCESS, 0, &hsubkey, 0)) !=
ERROR_SUCCESS)
return ret;
htkey = hsubkey;
}
ret = RegSetValueExW(htkey, name, 0, type, (const BYTE*)data, len);
if (hsubkey && hsubkey != hkey)
RegCloseKey(hsubkey);
return ret;
}
static void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* command)
{
// https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
// we want to register games so we can run them as discord-<appid>://
// Update the HKEY_CURRENT_USER, because it doesn't seem to require special permissions.
wchar_t exeFilePath[MAX_PATH];
DWORD exeLen = GetModuleFileNameW(nullptr, exeFilePath, MAX_PATH);
wchar_t openCommand[1024];
if (command && command[0]) {
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", command);
}
else {
// StringCbCopyW(openCommand, sizeof(openCommand), exeFilePath);
StringCbPrintfW(openCommand, sizeof(openCommand), L"%s", exeFilePath);
}
wchar_t protocolName[64];
StringCbPrintfW(protocolName, sizeof(protocolName), L"discord-%s", applicationId);
wchar_t protocolDescription[128];
StringCbPrintfW(
protocolDescription, sizeof(protocolDescription), L"URL:Run game %s protocol", applicationId);
wchar_t urlProtocol = 0;
wchar_t keyName[256];
StringCbPrintfW(keyName, sizeof(keyName), L"Software\\Classes\\%s", protocolName);
HKEY key;
auto status =
RegCreateKeyExW(HKEY_CURRENT_USER, keyName, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr);
if (status != ERROR_SUCCESS) {
fprintf(stderr, "Error creating key\n");
return;
}
DWORD len;
LSTATUS result;
len = (DWORD)lstrlenW(protocolDescription) + 1;
result =
RegSetKeyValueW(key, nullptr, nullptr, REG_SZ, protocolDescription, len * sizeof(wchar_t));
if (FAILED(result)) {
fprintf(stderr, "Error writing description\n");
}
len = (DWORD)lstrlenW(protocolDescription) + 1;
result = RegSetKeyValueW(key, nullptr, L"URL Protocol", REG_SZ, &urlProtocol, sizeof(wchar_t));
if (FAILED(result)) {
fprintf(stderr, "Error writing description\n");
}
result = RegSetKeyValueW(
key, L"DefaultIcon", nullptr, REG_SZ, exeFilePath, (exeLen + 1) * sizeof(wchar_t));
if (FAILED(result)) {
fprintf(stderr, "Error writing icon\n");
}
len = (DWORD)lstrlenW(openCommand) + 1;
result = RegSetKeyValueW(
key, L"shell\\open\\command", nullptr, REG_SZ, openCommand, len * sizeof(wchar_t));
if (FAILED(result)) {
fprintf(stderr, "Error writing command\n");
}
RegCloseKey(key);
}
extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command)
{
wchar_t appId[32];
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
wchar_t openCommand[1024];
const wchar_t* wcommand = nullptr;
if (command && command[0]) {
const auto commandBufferLen = sizeof(openCommand) / sizeof(*openCommand);
MultiByteToWideChar(CP_UTF8, 0, command, -1, openCommand, commandBufferLen);
wcommand = openCommand;
}
Discord_RegisterW(appId, wcommand);
}
extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId,
const char* steamId)
{
wchar_t appId[32];
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
wchar_t wSteamId[32];
MultiByteToWideChar(CP_UTF8, 0, steamId, -1, wSteamId, 32);
HKEY key;
auto status = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_READ, &key);
if (status != ERROR_SUCCESS) {
fprintf(stderr, "Error opening Steam key\n");
return;
}
wchar_t steamPath[MAX_PATH];
DWORD pathBytes = sizeof(steamPath);
status = RegQueryValueExW(key, L"SteamExe", nullptr, nullptr, (BYTE*)steamPath, &pathBytes);
RegCloseKey(key);
if (status != ERROR_SUCCESS || pathBytes < 1) {
fprintf(stderr, "Error reading SteamExe key\n");
return;
}
DWORD pathChars = pathBytes / sizeof(wchar_t);
for (DWORD i = 0; i < pathChars; ++i) {
if (steamPath[i] == L'/') {
steamPath[i] = L'\\';
}
}
wchar_t command[1024];
StringCbPrintfW(command, sizeof(command), L"\"%s\" steam://rungameid/%s", steamPath, wSteamId);
Discord_RegisterW(appId, command);
}
#endif

View File

@@ -0,0 +1,504 @@
#include "discord_rpc.h"
#include "backoff.h"
#include "discord_register.h"
#include "msg_queue.h"
#include "rpc_connection.h"
#include "serialization.h"
#include <atomic>
#include <chrono>
#include <mutex>
#ifndef DISCORD_DISABLE_IO_THREAD
#include <condition_variable>
#include <thread>
#endif
constexpr size_t MaxMessageSize{16 * 1024};
constexpr size_t MessageQueueSize{8};
constexpr size_t JoinQueueSize{8};
struct QueuedMessage {
size_t length;
char buffer[MaxMessageSize];
void Copy(const QueuedMessage& other)
{
length = other.length;
if (length) {
memcpy(buffer, other.buffer, length);
}
}
};
struct User {
// snowflake (64bit int), turned into a ascii decimal string, at most 20 chars +1 null
// terminator = 21
char userId[32];
// 32 unicode glyphs is max name size => 4 bytes per glyph in the worst case, +1 for null
// terminator = 129
char username[344];
// 4 decimal digits + 1 null terminator = 5
char discriminator[8];
// optional 'a_' + md5 hex digest (32 bytes) + null terminator = 35
char avatar[128];
// Rounded way up because I'm paranoid about games breaking from future changes in these sizes
};
static RpcConnection* Connection{nullptr};
static DiscordEventHandlers QueuedHandlers{};
static DiscordEventHandlers Handlers{};
static std::atomic_bool WasJustConnected{false};
static std::atomic_bool WasJustDisconnected{false};
static std::atomic_bool GotErrorMessage{false};
static std::atomic_bool WasJoinGame{false};
static std::atomic_bool WasSpectateGame{false};
static std::atomic_bool UpdatePresence{false};
static char JoinGameSecret[256];
static char SpectateGameSecret[256];
static int LastErrorCode{0};
static char LastErrorMessage[256];
static int LastDisconnectErrorCode{0};
static char LastDisconnectErrorMessage[256];
static std::mutex PresenceMutex;
static std::mutex HandlerMutex;
static QueuedMessage QueuedPresence{};
static MsgQueue<QueuedMessage, MessageQueueSize> SendQueue;
static MsgQueue<User, JoinQueueSize> JoinAskQueue;
static User connectedUser;
// We want to auto connect, and retry on failure, but not as fast as possible. This does expoential
// backoff from 0.5 seconds to 1 minute
static Backoff ReconnectTimeMs(500, 60 * 1000);
static auto NextConnect = std::chrono::system_clock::now();
static int Pid{0};
static int Nonce{1};
#ifndef DISCORD_DISABLE_IO_THREAD
static void Discord_UpdateConnection(void);
class IoThreadHolder {
private:
std::atomic_bool keepRunning{true};
std::mutex waitForIOMutex;
std::condition_variable waitForIOActivity;
std::thread ioThread;
public:
void Start()
{
keepRunning.store(true);
ioThread = std::thread([&]() {
const std::chrono::duration<int64_t, std::milli> maxWait{500LL};
Discord_UpdateConnection();
while (keepRunning.load()) {
std::unique_lock<std::mutex> lock(waitForIOMutex);
waitForIOActivity.wait_for(lock, maxWait);
Discord_UpdateConnection();
}
});
}
void Notify() { waitForIOActivity.notify_all(); }
void Stop()
{
keepRunning.exchange(false);
Notify();
if (ioThread.joinable()) {
ioThread.join();
}
}
~IoThreadHolder() { Stop(); }
};
#else
class IoThreadHolder {
public:
void Start() {}
void Stop() {}
void Notify() {}
};
#endif // DISCORD_DISABLE_IO_THREAD
static IoThreadHolder* IoThread{nullptr};
static void UpdateReconnectTime()
{
NextConnect = std::chrono::system_clock::now() +
std::chrono::duration<int64_t, std::milli>{ReconnectTimeMs.nextDelay()};
}
#ifdef DISCORD_DISABLE_IO_THREAD
extern "C" DISCORD_EXPORT void Discord_UpdateConnection(void)
#else
static void Discord_UpdateConnection(void)
#endif
{
if (!Connection) {
return;
}
if (!Connection->IsOpen()) {
if (std::chrono::system_clock::now() >= NextConnect) {
UpdateReconnectTime();
Connection->Open();
}
}
else {
// reads
for (;;) {
JsonDocument message;
if (!Connection->Read(message)) {
break;
}
const char* evtName = GetStrMember(&message, "evt");
const char* nonce = GetStrMember(&message, "nonce");
if (nonce) {
// in responses only -- should use to match up response when needed.
if (evtName && strcmp(evtName, "ERROR") == 0) {
auto data = GetObjMember(&message, "data");
LastErrorCode = GetIntMember(data, "code");
StringCopy(LastErrorMessage, GetStrMember(data, "message", ""));
GotErrorMessage.store(true);
}
}
else {
// should have evt == name of event, optional data
if (evtName == nullptr) {
continue;
}
auto data = GetObjMember(&message, "data");
if (strcmp(evtName, "ACTIVITY_JOIN") == 0) {
auto secret = GetStrMember(data, "secret");
if (secret) {
StringCopy(JoinGameSecret, secret);
WasJoinGame.store(true);
}
}
else if (strcmp(evtName, "ACTIVITY_SPECTATE") == 0) {
auto secret = GetStrMember(data, "secret");
if (secret) {
StringCopy(SpectateGameSecret, secret);
WasSpectateGame.store(true);
}
}
else if (strcmp(evtName, "ACTIVITY_JOIN_REQUEST") == 0) {
auto user = GetObjMember(data, "user");
auto userId = GetStrMember(user, "id");
auto username = GetStrMember(user, "username");
auto avatar = GetStrMember(user, "avatar");
auto joinReq = JoinAskQueue.GetNextAddMessage();
if (userId && username && joinReq) {
StringCopy(joinReq->userId, userId);
StringCopy(joinReq->username, username);
auto discriminator = GetStrMember(user, "discriminator");
if (discriminator) {
StringCopy(joinReq->discriminator, discriminator);
}
if (avatar) {
StringCopy(joinReq->avatar, avatar);
}
else {
joinReq->avatar[0] = 0;
}
JoinAskQueue.CommitAdd();
}
}
}
}
// writes
if (UpdatePresence.exchange(false) && QueuedPresence.length) {
QueuedMessage local;
{
std::lock_guard<std::mutex> guard(PresenceMutex);
local.Copy(QueuedPresence);
}
if (!Connection->Write(local.buffer, local.length)) {
// if we fail to send, requeue
std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.Copy(local);
UpdatePresence.exchange(true);
}
}
while (SendQueue.HavePendingSends()) {
auto qmessage = SendQueue.GetNextSendMessage();
Connection->Write(qmessage->buffer, qmessage->length);
SendQueue.CommitSend();
}
}
}
static void SignalIOActivity()
{
if (IoThread != nullptr) {
IoThread->Notify();
}
}
static bool RegisterForEvent(const char* evtName)
{
auto qmessage = SendQueue.GetNextAddMessage();
if (qmessage) {
qmessage->length =
JsonWriteSubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
SendQueue.CommitAdd();
SignalIOActivity();
return true;
}
return false;
}
static bool DeregisterForEvent(const char* evtName)
{
auto qmessage = SendQueue.GetNextAddMessage();
if (qmessage) {
qmessage->length =
JsonWriteUnsubscribeCommand(qmessage->buffer, sizeof(qmessage->buffer), Nonce++, evtName);
SendQueue.CommitAdd();
SignalIOActivity();
return true;
}
return false;
}
extern "C" DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId)
{
IoThread = new (std::nothrow) IoThreadHolder();
if (IoThread == nullptr) {
return;
}
if (autoRegister) {
if (optionalSteamId && optionalSteamId[0]) {
Discord_RegisterSteamGame(applicationId, optionalSteamId);
}
else {
Discord_Register(applicationId, nullptr);
}
}
Pid = GetProcessId();
{
std::lock_guard<std::mutex> guard(HandlerMutex);
if (handlers) {
QueuedHandlers = *handlers;
}
else {
QueuedHandlers = {};
}
Handlers = {};
}
if (Connection) {
return;
}
Connection = RpcConnection::Create(applicationId);
Connection->onConnect = [](JsonDocument& readyMessage) {
Discord_UpdateHandlers(&QueuedHandlers);
if (QueuedPresence.length > 0) {
UpdatePresence.exchange(true);
SignalIOActivity();
}
auto data = GetObjMember(&readyMessage, "data");
auto user = GetObjMember(data, "user");
auto userId = GetStrMember(user, "id");
auto username = GetStrMember(user, "username");
auto avatar = GetStrMember(user, "avatar");
if (userId && username) {
StringCopy(connectedUser.userId, userId);
StringCopy(connectedUser.username, username);
auto discriminator = GetStrMember(user, "discriminator");
if (discriminator) {
StringCopy(connectedUser.discriminator, discriminator);
}
if (avatar) {
StringCopy(connectedUser.avatar, avatar);
}
else {
connectedUser.avatar[0] = 0;
}
}
WasJustConnected.exchange(true);
ReconnectTimeMs.reset();
};
Connection->onDisconnect = [](int err, const char* message) {
LastDisconnectErrorCode = err;
StringCopy(LastDisconnectErrorMessage, message);
WasJustDisconnected.exchange(true);
UpdateReconnectTime();
};
IoThread->Start();
}
extern "C" DISCORD_EXPORT void Discord_Shutdown(void)
{
if (!Connection) {
return;
}
Connection->onConnect = nullptr;
Connection->onDisconnect = nullptr;
Handlers = {};
QueuedPresence.length = 0;
UpdatePresence.exchange(false);
if (IoThread != nullptr) {
IoThread->Stop();
delete IoThread;
IoThread = nullptr;
}
RpcConnection::Destroy(Connection);
}
extern "C" DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence)
{
{
std::lock_guard<std::mutex> guard(PresenceMutex);
QueuedPresence.length = JsonWriteRichPresenceObj(
QueuedPresence.buffer, sizeof(QueuedPresence.buffer), Nonce++, Pid, presence);
UpdatePresence.exchange(true);
}
SignalIOActivity();
}
extern "C" DISCORD_EXPORT void Discord_ClearPresence(void)
{
Discord_UpdatePresence(nullptr);
}
extern "C" DISCORD_EXPORT void Discord_Respond(const char* userId, /* DISCORD_REPLY_ */ int reply)
{
// if we are not connected, let's not batch up stale messages for later
if (!Connection || !Connection->IsOpen()) {
return;
}
auto qmessage = SendQueue.GetNextAddMessage();
if (qmessage) {
qmessage->length =
JsonWriteJoinReply(qmessage->buffer, sizeof(qmessage->buffer), userId, reply, Nonce++);
SendQueue.CommitAdd();
SignalIOActivity();
}
}
extern "C" DISCORD_EXPORT void Discord_RunCallbacks(void)
{
// Note on some weirdness: internally we might connect, get other signals, disconnect any number
// of times inbetween calls here. Externally, we want the sequence to seem sane, so any other
// signals are book-ended by calls to ready and disconnect.
if (!Connection) {
return;
}
bool wasDisconnected = WasJustDisconnected.exchange(false);
bool isConnected = Connection->IsOpen();
if (isConnected) {
// if we are connected, disconnect cb first
std::lock_guard<std::mutex> guard(HandlerMutex);
if (wasDisconnected && Handlers.disconnected) {
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
}
}
if (WasJustConnected.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.ready) {
DiscordUser du{connectedUser.userId,
connectedUser.username,
connectedUser.discriminator,
connectedUser.avatar};
Handlers.ready(&du);
}
}
if (GotErrorMessage.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.errored) {
Handlers.errored(LastErrorCode, LastErrorMessage);
}
}
if (WasJoinGame.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.joinGame) {
Handlers.joinGame(JoinGameSecret);
}
}
if (WasSpectateGame.exchange(false)) {
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.spectateGame) {
Handlers.spectateGame(SpectateGameSecret);
}
}
// Right now this batches up any requests and sends them all in a burst; I could imagine a world
// where the implementer would rather sequentially accept/reject each one before the next invite
// is sent. I left it this way because I could also imagine wanting to process these all and
// maybe show them in one common dialog and/or start fetching the avatars in parallel, and if
// not it should be trivial for the implementer to make a queue themselves.
while (JoinAskQueue.HavePendingSends()) {
auto req = JoinAskQueue.GetNextSendMessage();
{
std::lock_guard<std::mutex> guard(HandlerMutex);
if (Handlers.joinRequest) {
DiscordUser du{req->userId, req->username, req->discriminator, req->avatar};
Handlers.joinRequest(&du);
}
}
JoinAskQueue.CommitSend();
}
if (!isConnected) {
// if we are not connected, disconnect message last
std::lock_guard<std::mutex> guard(HandlerMutex);
if (wasDisconnected && Handlers.disconnected) {
Handlers.disconnected(LastDisconnectErrorCode, LastDisconnectErrorMessage);
}
}
}
extern "C" DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* newHandlers)
{
if (newHandlers) {
#define HANDLE_EVENT_REGISTRATION(handler_name, event) \
if (!Handlers.handler_name && newHandlers->handler_name) { \
RegisterForEvent(event); \
} \
else if (Handlers.handler_name && !newHandlers->handler_name) { \
DeregisterForEvent(event); \
}
std::lock_guard<std::mutex> guard(HandlerMutex);
HANDLE_EVENT_REGISTRATION(joinGame, "ACTIVITY_JOIN")
HANDLE_EVENT_REGISTRATION(spectateGame, "ACTIVITY_SPECTATE")
HANDLE_EVENT_REGISTRATION(joinRequest, "ACTIVITY_JOIN_REQUEST")
#undef HANDLE_EVENT_REGISTRATION
Handlers = *newHandlers;
}
else {
std::lock_guard<std::mutex> guard(HandlerMutex);
Handlers = {};
}
return;
}

View File

@@ -0,0 +1,90 @@
#pragma once
#include <stdint.h>
// clang-format off
#if defined(DISCORD_DYNAMIC_LIB)
# if defined(_WIN32)
# if defined(DISCORD_BUILDING_SDK)
# define DISCORD_EXPORT __declspec(dllexport)
# else
# define DISCORD_EXPORT __declspec(dllimport)
# endif
# else
# define DISCORD_EXPORT __attribute__((visibility("default")))
# endif
#else
# define DISCORD_EXPORT
#endif
// clang-format on
#ifdef __cplusplus
extern "C" {
#endif
typedef struct DiscordRichPresence {
const char* state; /* max 128 bytes */
const char* details; /* max 128 bytes */
int64_t startTimestamp;
int64_t endTimestamp;
const char* largeImageKey; /* max 32 bytes */
const char* largeImageText; /* max 128 bytes */
const char* smallImageKey; /* max 32 bytes */
const char* smallImageText; /* max 128 bytes */
const char* partyId; /* max 128 bytes */
int partySize;
int partyMax;
int partyPrivacy;
const char* matchSecret; /* max 128 bytes */
const char* joinSecret; /* max 128 bytes */
const char* spectateSecret; /* max 128 bytes */
int8_t instance;
} DiscordRichPresence;
typedef struct DiscordUser {
const char* userId;
const char* username;
const char* discriminator;
const char* avatar;
} DiscordUser;
typedef struct DiscordEventHandlers {
void (*ready)(const DiscordUser* request);
void (*disconnected)(int errorCode, const char* message);
void (*errored)(int errorCode, const char* message);
void (*joinGame)(const char* joinSecret);
void (*spectateGame)(const char* spectateSecret);
void (*joinRequest)(const DiscordUser* request);
} DiscordEventHandlers;
#define DISCORD_REPLY_NO 0
#define DISCORD_REPLY_YES 1
#define DISCORD_REPLY_IGNORE 2
#define DISCORD_PARTY_PRIVATE 0
#define DISCORD_PARTY_PUBLIC 1
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId);
DISCORD_EXPORT void Discord_Shutdown(void);
/* checks for incoming messages, dispatches callbacks */
DISCORD_EXPORT void Discord_RunCallbacks(void);
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
#ifdef DISCORD_DISABLE_IO_THREAD
DISCORD_EXPORT void Discord_UpdateConnection(void);
#endif
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
DISCORD_EXPORT void Discord_ClearPresence(void);
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@@ -0,0 +1,12 @@
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
// outsmart GCC's missing-declarations warning
BOOL WINAPI DllMain(HMODULE, DWORD, LPVOID);
BOOL WINAPI DllMain(HMODULE, DWORD, LPVOID)
{
return TRUE;
}
#endif

View File

@@ -0,0 +1,36 @@
#pragma once
#include <atomic>
// A simple queue. No locks, but only works with a single thread as producer and a single thread as
// a consumer. Mutex up as needed.
template <typename ElementType, size_t QueueSize>
class MsgQueue {
ElementType queue_[QueueSize];
std::atomic_uint nextAdd_{0};
std::atomic_uint nextSend_{0};
std::atomic_uint pendingSends_{0};
public:
MsgQueue() {}
ElementType* GetNextAddMessage()
{
// if we are falling behind, bail
if (pendingSends_.load() >= QueueSize) {
return nullptr;
}
auto index = (nextAdd_++) % QueueSize;
return &queue_[index];
}
void CommitAdd() { ++pendingSends_; }
bool HavePendingSends() const { return pendingSends_.load() != 0; }
ElementType* GetNextSendMessage()
{
auto index = (nextSend_++) % QueueSize;
return &queue_[index];
}
void CommitSend() { --pendingSends_; }
};

View File

@@ -0,0 +1,271 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ALLOCATORS_H_
#define RAPIDJSON_ALLOCATORS_H_
#include "rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Allocator
/*! \class rapidjson::Allocator
\brief Concept for allocating, resizing and freeing memory block.
Note that Malloc() and Realloc() are non-static but Free() is static.
So if an allocator need to support Free(), it needs to put its pointer in
the header of memory block.
\code
concept Allocator {
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
// Allocate a memory block.
// \param size of the memory block in bytes.
// \returns pointer to the memory block.
void* Malloc(size_t size);
// Resize a memory block.
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
// \param newSize the new size in bytes.
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
// Free a memory block.
// \param pointer to the memory block. Null pointer is permitted.
static void Free(void *ptr);
};
\endcode
*/
///////////////////////////////////////////////////////////////////////////////
// CrtAllocator
//! C-runtime library allocator.
/*! This class is just wrapper for standard C library memory routines.
\note implements Allocator concept
*/
class CrtAllocator {
public:
static const bool kNeedFree = true;
void* Malloc(size_t size) {
if (size) // behavior of malloc(0) is implementation defined.
return std::malloc(size);
else
return NULL; // standardize to returning NULL.
}
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
(void)originalSize;
if (newSize == 0) {
std::free(originalPtr);
return NULL;
}
return std::realloc(originalPtr, newSize);
}
static void Free(void *ptr) { std::free(ptr); }
};
///////////////////////////////////////////////////////////////////////////////
// MemoryPoolAllocator
//! Default memory allocator used by the parser and DOM.
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
It does not free memory blocks. And Realloc() only allocate new memory.
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
User may also supply a buffer as the first chunk.
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
The user-buffer is not deallocated by this allocator.
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
\note implements Allocator concept
*/
template <typename BaseAllocator = CrtAllocator>
class MemoryPoolAllocator {
public:
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
//! Constructor with chunkSize.
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
}
//! Constructor with user-supplied buffer.
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
The user buffer will not be deallocated when this allocator is destructed.
\param buffer User supplied buffer.
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks.
*/
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
RAPIDJSON_ASSERT(buffer != 0);
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
chunkHead_->capacity = size - sizeof(ChunkHeader);
chunkHead_->size = 0;
chunkHead_->next = 0;
}
//! Destructor.
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
*/
~MemoryPoolAllocator() {
Clear();
RAPIDJSON_DELETE(ownBaseAllocator_);
}
//! Deallocates all memory chunks, excluding the user-supplied buffer.
void Clear() {
while (chunkHead_ && chunkHead_ != userBuffer_) {
ChunkHeader* next = chunkHead_->next;
baseAllocator_->Free(chunkHead_);
chunkHead_ = next;
}
if (chunkHead_ && chunkHead_ == userBuffer_)
chunkHead_->size = 0; // Clear user buffer
}
//! Computes the total capacity of allocated memory chunks.
/*! \return total capacity in bytes.
*/
size_t Capacity() const {
size_t capacity = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
capacity += c->capacity;
return capacity;
}
//! Computes the memory blocks allocated.
/*! \return total used bytes.
*/
size_t Size() const {
size_t size = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
size += c->size;
return size;
}
//! Allocates a memory block. (concept Allocator)
void* Malloc(size_t size) {
if (!size)
return NULL;
size = RAPIDJSON_ALIGN(size);
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
return NULL;
void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size;
chunkHead_->size += size;
return buffer;
}
//! Resizes a memory block (concept Allocator)
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
if (originalPtr == 0)
return Malloc(newSize);
if (newSize == 0)
return NULL;
originalSize = RAPIDJSON_ALIGN(originalSize);
newSize = RAPIDJSON_ALIGN(newSize);
// Do not shrink if new size is smaller than original
if (originalSize >= newSize)
return originalPtr;
// Simply expand it if it is the last allocation and there is sufficient space
if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) {
size_t increment = static_cast<size_t>(newSize - originalSize);
if (chunkHead_->size + increment <= chunkHead_->capacity) {
chunkHead_->size += increment;
return originalPtr;
}
}
// Realloc process: allocate and copy memory, do not free original buffer.
if (void* newBuffer = Malloc(newSize)) {
if (originalSize)
std::memcpy(newBuffer, originalPtr, originalSize);
return newBuffer;
}
else
return NULL;
}
//! Frees a memory block (concept Allocator)
static void Free(void *ptr) { (void)ptr; } // Do nothing
private:
//! Copy constructor is not permitted.
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
//! Copy assignment operator is not permitted.
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
//! Creates a new chunk.
/*! \param capacity Capacity of the chunk in bytes.
\return true if success.
*/
bool AddChunk(size_t capacity) {
if (!baseAllocator_)
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) {
chunk->capacity = capacity;
chunk->size = 0;
chunk->next = chunkHead_;
chunkHead_ = chunk;
return true;
}
else
return false;
}
static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
//! Chunk header for perpending to each chunk.
/*! Chunks are stored as a singly linked list.
*/
struct ChunkHeader {
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
size_t size; //!< Current size of allocated memory in bytes.
ChunkHeader *next; //!< Next chunk in the linked list.
};
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
void *userBuffer_; //!< User supplied buffer.
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
};
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ENCODINGS_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,299 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
#define RAPIDJSON_ENCODEDSTREAM_H_
#include "stream.h"
#include "memorystream.h"
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Input byte stream wrapper with a statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
*/
template <typename Encoding, typename InputByteStream>
class EncodedInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public:
typedef typename Encoding::Ch Ch;
EncodedInputStream(InputByteStream& is) : is_(is) {
current_ = Encoding::TakeBOM(is_);
}
Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
size_t Tell() const { return is_.Tell(); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&);
InputByteStream& is_;
Ch current_;
};
//! Specialized for UTF8 MemoryStream.
template <>
class EncodedInputStream<UTF8<>, MemoryStream> {
public:
typedef UTF8<>::Ch Ch;
EncodedInputStream(MemoryStream& is) : is_(is) {
if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
}
Ch Peek() const { return is_.Peek(); }
Ch Take() { return is_.Take(); }
size_t Tell() const { return is_.Tell(); }
// Not implemented
void Put(Ch) {}
void Flush() {}
Ch* PutBegin() { return 0; }
size_t PutEnd(Ch*) { return 0; }
MemoryStream& is_;
private:
EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&);
};
//! Output byte stream wrapper with statically bound encoding.
/*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
*/
template <typename Encoding, typename OutputByteStream>
class EncodedOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public:
typedef typename Encoding::Ch Ch;
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
if (putBOM)
Encoding::PutBOM(os_);
}
void Put(Ch c) { Encoding::Put(os_, c); }
void Flush() { os_.Flush(); }
// Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
EncodedOutputStream(const EncodedOutputStream&);
EncodedOutputStream& operator=(const EncodedOutputStream&);
OutputByteStream& os_;
};
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
/*!
\tparam CharType Type of character for reading.
\tparam InputByteStream type of input byte stream to be wrapped.
*/
template <typename CharType, typename InputByteStream>
class AutoUTFInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public:
typedef CharType Ch;
//! Constructor.
/*!
\param is input stream to be wrapped.
\param type UTF encoding type if it is not detected from the stream.
*/
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
DetectType();
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
takeFunc_ = f[type_];
current_ = takeFunc_(*is_);
}
UTFType GetType() const { return type_; }
bool HasBOM() const { return hasBOM_; }
Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
size_t Tell() const { return is_->Tell(); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
AutoUTFInputStream(const AutoUTFInputStream&);
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
// Detect encoding type with BOM or RFC 4627
void DetectType() {
// BOM (Byte Order Mark):
// 00 00 FE FF UTF-32BE
// FF FE 00 00 UTF-32LE
// FE FF UTF-16BE
// FF FE UTF-16LE
// EF BB BF UTF-8
const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
if (!c)
return;
unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
hasBOM_ = false;
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
// RFC 4627: Section 3
// "Since the first two characters of a JSON text will always be ASCII
// characters [RFC0020], it is possible to determine whether an octet
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
// at the pattern of nulls in the first four octets."
// 00 00 00 xx UTF-32BE
// 00 xx 00 xx UTF-16BE
// xx 00 00 00 UTF-32LE
// xx 00 xx 00 UTF-16LE
// xx xx xx xx UTF-8
if (!hasBOM_) {
unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
switch (pattern) {
case 0x08: type_ = kUTF32BE; break;
case 0x0A: type_ = kUTF16BE; break;
case 0x01: type_ = kUTF32LE; break;
case 0x05: type_ = kUTF16LE; break;
case 0x0F: type_ = kUTF8; break;
default: break; // Use type defined by user.
}
}
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
}
typedef Ch (*TakeFunc)(InputByteStream& is);
InputByteStream* is_;
UTFType type_;
Ch current_;
TakeFunc takeFunc_;
bool hasBOM_;
};
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
/*!
\tparam CharType Type of character for writing.
\tparam OutputByteStream type of output byte stream to be wrapped.
*/
template <typename CharType, typename OutputByteStream>
class AutoUTFOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public:
typedef CharType Ch;
//! Constructor.
/*!
\param os output stream to be wrapped.
\param type UTF encoding type.
\param putBOM Whether to write BOM at the beginning of the stream.
*/
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
putFunc_ = f[type_];
if (putBOM)
PutBOM();
}
UTFType GetType() const { return type_; }
void Put(Ch c) { putFunc_(*os_, c); }
void Flush() { os_->Flush(); }
// Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
AutoUTFOutputStream(const AutoUTFOutputStream&);
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
void PutBOM() {
typedef void (*PutBOMFunc)(OutputByteStream&);
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
f[type_](*os_);
}
typedef void (*PutFunc)(OutputByteStream&, Ch);
OutputByteStream* os_;
UTFType type_;
PutFunc putFunc_;
};
#undef RAPIDJSON_ENCODINGS_FUNC
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

View File

@@ -0,0 +1,716 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ENCODINGS_H_
#define RAPIDJSON_ENCODINGS_H_
#include "rapidjson.h"
#ifdef _MSC_VER
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
RAPIDJSON_DIAG_OFF(4702) // unreachable code
#elif defined(__GNUC__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
RAPIDJSON_DIAG_OFF(overflow)
#endif
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Encoding
/*! \class rapidjson::Encoding
\brief Concept for encoding of Unicode characters.
\code
concept Encoding {
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
enum { supportUnicode = 1 }; // or 0 if not supporting unicode
//! \brief Encode a Unicode codepoint to an output stream.
//! \param os Output stream.
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint);
//! \brief Decode a Unicode codepoint from an input stream.
//! \param is Input stream.
//! \param codepoint Output of the unicode codepoint.
//! \return true if a valid codepoint can be decoded from the stream.
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint);
//! \brief Validate one Unicode codepoint from an encoded stream.
//! \param is Input stream to obtain codepoint.
//! \param os Output for copying one codepoint.
//! \return true if it is valid.
//! \note This function just validating and copying the codepoint without actually decode it.
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os);
// The following functions are deal with byte streams.
//! Take a character from input byte stream, skip BOM if exist.
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is);
//! Take a character from input byte stream.
template <typename InputByteStream>
static Ch Take(InputByteStream& is);
//! Put BOM to output byte stream.
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os);
//! Put a character to output byte stream.
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c);
};
\endcode
*/
///////////////////////////////////////////////////////////////////////////////
// UTF8
//! UTF-8 encoding.
/*! http://en.wikipedia.org/wiki/UTF-8
http://tools.ietf.org/html/rfc3629
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
\note implements Encoding concept
*/
template<typename CharType = char>
struct UTF8 {
typedef CharType Ch;
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
if (codepoint <= 0x7F)
os.Put(static_cast<Ch>(codepoint & 0xFF));
else if (codepoint <= 0x7FF) {
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
}
else if (codepoint <= 0xFFFF) {
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
if (codepoint <= 0x7F)
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
else if (codepoint <= 0x7FF) {
PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
}
else if (codepoint <= 0xFFFF) {
PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
#define TAIL() COPY(); TRANS(0x70)
typename InputStream::Ch c = is.Take();
if (!(c & 0x80)) {
*codepoint = static_cast<unsigned char>(c);
return true;
}
unsigned char type = GetRange(static_cast<unsigned char>(c));
if (type >= 32) {
*codepoint = 0;
} else {
*codepoint = (0xFF >> type) & static_cast<unsigned char>(c);
}
bool result = true;
switch (type) {
case 2: TAIL(); return result;
case 3: TAIL(); TAIL(); return result;
case 4: COPY(); TRANS(0x50); TAIL(); return result;
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
case 6: TAIL(); TAIL(); TAIL(); return result;
case 10: COPY(); TRANS(0x20); TAIL(); return result;
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
default: return false;
}
#undef COPY
#undef TRANS
#undef TAIL
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
#define COPY() os.Put(c = is.Take())
#define TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
#define TAIL() COPY(); TRANS(0x70)
Ch c;
COPY();
if (!(c & 0x80))
return true;
bool result = true;
switch (GetRange(static_cast<unsigned char>(c))) {
case 2: TAIL(); return result;
case 3: TAIL(); TAIL(); return result;
case 4: COPY(); TRANS(0x50); TAIL(); return result;
case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result;
case 6: TAIL(); TAIL(); TAIL(); return result;
case 10: COPY(); TRANS(0x20); TAIL(); return result;
case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result;
default: return false;
}
#undef COPY
#undef TRANS
#undef TAIL
}
static unsigned char GetRange(unsigned char c) {
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
static const unsigned char type[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
};
return type[c];
}
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
typename InputByteStream::Ch c = Take(is);
if (static_cast<unsigned char>(c) != 0xEFu) return c;
c = is.Take();
if (static_cast<unsigned char>(c) != 0xBBu) return c;
c = is.Take();
if (static_cast<unsigned char>(c) != 0xBFu) return c;
c = is.Take();
return c;
}
template <typename InputByteStream>
static Ch Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
return static_cast<Ch>(is.Take());
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c));
}
};
///////////////////////////////////////////////////////////////////////////////
// UTF16
//! UTF-16 encoding.
/*! http://en.wikipedia.org/wiki/UTF-16
http://tools.ietf.org/html/rfc2781
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
\note implements Encoding concept
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
For streaming, use UTF16LE and UTF16BE, which handle endianness.
*/
template<typename CharType = wchar_t>
struct UTF16 {
typedef CharType Ch;
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
if (codepoint <= 0xFFFF) {
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
unsigned v = codepoint - 0x10000;
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
os.Put((v & 0x3FF) | 0xDC00);
}
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
if (codepoint <= 0xFFFF) {
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
}
else {
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
unsigned v = codepoint - 0x10000;
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
PutUnsafe(os, (v & 0x3FF) | 0xDC00);
}
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
typename InputStream::Ch c = is.Take();
if (c < 0xD800 || c > 0xDFFF) {
*codepoint = static_cast<unsigned>(c);
return true;
}
else if (c <= 0xDBFF) {
*codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
c = is.Take();
*codepoint |= (static_cast<unsigned>(c) & 0x3FF);
*codepoint += 0x10000;
return c >= 0xDC00 && c <= 0xDFFF;
}
return false;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
typename InputStream::Ch c;
os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
if (c < 0xD800 || c > 0xDFFF)
return true;
else if (c <= 0xDBFF) {
os.Put(c = is.Take());
return c >= 0xDC00 && c <= 0xDFFF;
}
return false;
}
};
//! UTF-16 little endian encoding.
template<typename CharType = wchar_t>
struct UTF16LE : UTF16<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<uint8_t>(is.Take());
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
}
};
//! UTF-16 big endian encoding.
template<typename CharType = wchar_t>
struct UTF16BE : UTF16<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<uint8_t>(is.Take());
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
}
};
///////////////////////////////////////////////////////////////////////////////
// UTF32
//! UTF-32 encoding.
/*! http://en.wikipedia.org/wiki/UTF-32
\tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
\note implements Encoding concept
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
For streaming, use UTF32LE and UTF32BE, which handle endianness.
*/
template<typename CharType = unsigned>
struct UTF32 {
typedef CharType Ch;
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
enum { supportUnicode = 1 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
os.Put(codepoint);
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
PutUnsafe(os, codepoint);
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
Ch c = is.Take();
*codepoint = c;
return c <= 0x10FFFF;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
Ch c;
os.Put(c = is.Take());
return c <= 0x10FFFF;
}
};
//! UTF-32 little endian enocoding.
template<typename CharType = unsigned>
struct UTF32LE : UTF32<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<uint8_t>(is.Take());
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
}
};
//! UTF-32 big endian encoding.
template<typename CharType = unsigned>
struct UTF32BE : UTF32<CharType> {
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
CharType c = Take(is);
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
}
template <typename InputByteStream>
static CharType Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
return static_cast<CharType>(c);
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, CharType c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
}
};
///////////////////////////////////////////////////////////////////////////////
// ASCII
//! ASCII encoding.
/*! http://en.wikipedia.org/wiki/ASCII
\tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
\note implements Encoding concept
*/
template<typename CharType = char>
struct ASCII {
typedef CharType Ch;
enum { supportUnicode = 0 };
template<typename OutputStream>
static void Encode(OutputStream& os, unsigned codepoint) {
RAPIDJSON_ASSERT(codepoint <= 0x7F);
os.Put(static_cast<Ch>(codepoint & 0xFF));
}
template<typename OutputStream>
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
RAPIDJSON_ASSERT(codepoint <= 0x7F);
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
}
template <typename InputStream>
static bool Decode(InputStream& is, unsigned* codepoint) {
uint8_t c = static_cast<uint8_t>(is.Take());
*codepoint = c;
return c <= 0X7F;
}
template <typename InputStream, typename OutputStream>
static bool Validate(InputStream& is, OutputStream& os) {
uint8_t c = static_cast<uint8_t>(is.Take());
os.Put(static_cast<typename OutputStream::Ch>(c));
return c <= 0x7F;
}
template <typename InputByteStream>
static CharType TakeBOM(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
uint8_t c = static_cast<uint8_t>(Take(is));
return static_cast<Ch>(c);
}
template <typename InputByteStream>
static Ch Take(InputByteStream& is) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
return static_cast<Ch>(is.Take());
}
template <typename OutputByteStream>
static void PutBOM(OutputByteStream& os) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
(void)os;
}
template <typename OutputByteStream>
static void Put(OutputByteStream& os, Ch c) {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
os.Put(static_cast<typename OutputByteStream::Ch>(c));
}
};
///////////////////////////////////////////////////////////////////////////////
// AutoUTF
//! Runtime-specified UTF encoding type of a stream.
enum UTFType {
kUTF8 = 0, //!< UTF-8.
kUTF16LE = 1, //!< UTF-16 little endian.
kUTF16BE = 2, //!< UTF-16 big endian.
kUTF32LE = 3, //!< UTF-32 little endian.
kUTF32BE = 4 //!< UTF-32 big endian.
};
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
*/
template<typename CharType>
struct AutoUTF {
typedef CharType Ch;
enum { supportUnicode = 1 };
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
template<typename OutputStream>
RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) {
typedef void (*EncodeFunc)(OutputStream&, unsigned);
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
(*f[os.GetType()])(os, codepoint);
}
template<typename OutputStream>
RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
typedef void (*EncodeFunc)(OutputStream&, unsigned);
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
(*f[os.GetType()])(os, codepoint);
}
template <typename InputStream>
RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) {
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
return (*f[is.GetType()])(is, codepoint);
}
template <typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
return (*f[is.GetType()])(is, os);
}
#undef RAPIDJSON_ENCODINGS_FUNC
};
///////////////////////////////////////////////////////////////////////////////
// Transcoder
//! Encoding conversion.
template<typename SourceEncoding, typename TargetEncoding>
struct Transcoder {
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
template<typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
unsigned codepoint;
if (!SourceEncoding::Decode(is, &codepoint))
return false;
TargetEncoding::Encode(os, codepoint);
return true;
}
template<typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
unsigned codepoint;
if (!SourceEncoding::Decode(is, &codepoint))
return false;
TargetEncoding::EncodeUnsafe(os, codepoint);
return true;
}
//! Validate one Unicode codepoint from an encoded stream.
template<typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
return Transcode(is, os); // Since source/target encoding is different, must transcode.
}
};
// Forward declaration.
template<typename Stream>
inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
//! Specialization of Transcoder with same source and target encoding.
template<typename Encoding>
struct Transcoder<Encoding, Encoding> {
template<typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) {
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
return true;
}
template<typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
return true;
}
template<typename InputStream, typename OutputStream>
RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) {
return Encoding::Validate(is, os); // source/target encoding are the same
}
};
RAPIDJSON_NAMESPACE_END
#if defined(__GNUC__) || defined(_MSC_VER)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ENCODINGS_H_

View File

@@ -0,0 +1,74 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ERROR_EN_H_
#define RAPIDJSON_ERROR_EN_H_
#include "error.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(switch-enum)
RAPIDJSON_DIAG_OFF(covered-switch-default)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Maps error code of parsing into error message.
/*!
\ingroup RAPIDJSON_ERRORS
\param parseErrorCode Error code obtained in parsing.
\return the error message.
\note User can make a copy of this function for localization.
Using switch-case is safer for future modification of error codes.
*/
inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
switch (parseErrorCode) {
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
}
}
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ERROR_EN_H_

View File

@@ -0,0 +1,155 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ERROR_ERROR_H_
#define RAPIDJSON_ERROR_ERROR_H_
#include "../rapidjson.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
/*! \file error.h */
/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ERROR_CHARTYPE
//! Character type of error messages.
/*! \ingroup RAPIDJSON_ERRORS
The default character type is \c char.
On Windows, user can define this macro as \c TCHAR for supporting both
unicode/non-unicode settings.
*/
#ifndef RAPIDJSON_ERROR_CHARTYPE
#define RAPIDJSON_ERROR_CHARTYPE char
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ERROR_STRING
//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[].
/*! \ingroup RAPIDJSON_ERRORS
By default this conversion macro does nothing.
On Windows, user can define this macro as \c _T(x) for supporting both
unicode/non-unicode settings.
*/
#ifndef RAPIDJSON_ERROR_STRING
#define RAPIDJSON_ERROR_STRING(x) x
#endif
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// ParseErrorCode
//! Error code of parsing.
/*! \ingroup RAPIDJSON_ERRORS
\see GenericReader::Parse, GenericReader::GetParseErrorCode
*/
enum ParseErrorCode {
kParseErrorNone = 0, //!< No error.
kParseErrorDocumentEmpty, //!< The document is empty.
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
kParseErrorValueInvalid, //!< Invalid value.
kParseErrorObjectMissName, //!< Missing a name for object member.
kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
kParseErrorNumberTooBig, //!< Number too big to be stored in double.
kParseErrorNumberMissFraction, //!< Miss fraction part in number.
kParseErrorNumberMissExponent, //!< Miss exponent in number.
kParseErrorTermination, //!< Parsing was terminated.
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
};
//! Result of parsing (wraps ParseErrorCode)
/*!
\ingroup RAPIDJSON_ERRORS
\code
Document doc;
ParseResult ok = doc.Parse("[42]");
if (!ok) {
fprintf(stderr, "JSON parse error: %s (%u)",
GetParseError_En(ok.Code()), ok.Offset());
exit(EXIT_FAILURE);
}
\endcode
\see GenericReader::Parse, GenericDocument::Parse
*/
struct ParseResult {
public:
//! Default constructor, no error.
ParseResult() : code_(kParseErrorNone), offset_(0) {}
//! Constructor to set an error.
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
//! Get the error code.
ParseErrorCode Code() const { return code_; }
//! Get the error offset, if \ref IsError(), 0 otherwise.
size_t Offset() const { return offset_; }
//! Conversion to \c bool, returns \c true, iff !\ref IsError().
operator bool() const { return !IsError(); }
//! Whether the result is an error.
bool IsError() const { return code_ != kParseErrorNone; }
bool operator==(const ParseResult& that) const { return code_ == that.code_; }
bool operator==(ParseErrorCode code) const { return code_ == code; }
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
//! Reset error code.
void Clear() { Set(kParseErrorNone); }
//! Update error code and offset.
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
private:
ParseErrorCode code_;
size_t offset_;
};
//! Function pointer type of GetParseError().
/*! \ingroup RAPIDJSON_ERRORS
This is the prototype for \c GetParseError_X(), where \c X is a locale.
User can dynamically change locale in runtime, e.g.:
\code
GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
\endcode
*/
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_ERROR_ERROR_H_

View File

@@ -0,0 +1,99 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_FILEREADSTREAM_H_
#define RAPIDJSON_FILEREADSTREAM_H_
#include "stream.h"
#include <cstdio>
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
RAPIDJSON_DIAG_OFF(unreachable-code)
RAPIDJSON_DIAG_OFF(missing-noreturn)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! File byte stream for input using fread().
/*!
\note implements Stream concept
*/
class FileReadStream {
public:
typedef char Ch; //!< Character type (byte).
//! Constructor.
/*!
\param fp File pointer opened for read.
\param buffer user-supplied buffer.
\param bufferSize size of buffer in bytes. Must >=4 bytes.
*/
FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
RAPIDJSON_ASSERT(fp_ != 0);
RAPIDJSON_ASSERT(bufferSize >= 4);
Read();
}
Ch Peek() const { return *current_; }
Ch Take() { Ch c = *current_; Read(); return c; }
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
// Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
// For encoding detection only.
const Ch* Peek4() const {
return (current_ + 4 <= bufferLast_) ? current_ : 0;
}
private:
void Read() {
if (current_ < bufferLast_)
++current_;
else if (!eof_) {
count_ += readCount_;
readCount_ = fread(buffer_, 1, bufferSize_, fp_);
bufferLast_ = buffer_ + readCount_ - 1;
current_ = buffer_;
if (readCount_ < bufferSize_) {
buffer_[readCount_] = '\0';
++bufferLast_;
eof_ = true;
}
}
}
std::FILE* fp_;
Ch *buffer_;
size_t bufferSize_;
Ch *bufferLast_;
Ch *current_;
size_t readCount_;
size_t count_; //!< Number of characters read
bool eof_;
};
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

View File

@@ -0,0 +1,104 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
#define RAPIDJSON_FILEWRITESTREAM_H_
#include "stream.h"
#include <cstdio>
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(unreachable-code)
#endif
RAPIDJSON_NAMESPACE_BEGIN
//! Wrapper of C file stream for input using fread().
/*!
\note implements Stream concept
*/
class FileWriteStream {
public:
typedef char Ch; //!< Character type. Only support char.
FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
RAPIDJSON_ASSERT(fp_ != 0);
}
void Put(char c) {
if (current_ >= bufferEnd_)
Flush();
*current_++ = c;
}
void PutN(char c, size_t n) {
size_t avail = static_cast<size_t>(bufferEnd_ - current_);
while (n > avail) {
std::memset(current_, c, avail);
current_ += avail;
Flush();
n -= avail;
avail = static_cast<size_t>(bufferEnd_ - current_);
}
if (n > 0) {
std::memset(current_, c, n);
current_ += n;
}
}
void Flush() {
if (current_ != buffer_) {
size_t result = fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
if (result < static_cast<size_t>(current_ - buffer_)) {
// failure deliberately ignored at this time
// added to avoid warn_unused_result build errors
}
current_ = buffer_;
}
}
// Not implemented
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
char Take() { RAPIDJSON_ASSERT(false); return 0; }
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
private:
// Prohibit copy constructor & assignment operator.
FileWriteStream(const FileWriteStream&);
FileWriteStream& operator=(const FileWriteStream&);
std::FILE* fp_;
char *buffer_;
char *bufferEnd_;
char *current_;
};
//! Implement specialized version of PutN() with memset() for better performance.
template<>
inline void PutN(FileWriteStream& stream, char c, size_t n) {
stream.PutN(c, n);
}
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_FILESTREAM_H_

View File

@@ -0,0 +1,151 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_FWD_H_
#define RAPIDJSON_FWD_H_
#include "rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
// encodings.h
template<typename CharType> struct UTF8;
template<typename CharType> struct UTF16;
template<typename CharType> struct UTF16BE;
template<typename CharType> struct UTF16LE;
template<typename CharType> struct UTF32;
template<typename CharType> struct UTF32BE;
template<typename CharType> struct UTF32LE;
template<typename CharType> struct ASCII;
template<typename CharType> struct AutoUTF;
template<typename SourceEncoding, typename TargetEncoding>
struct Transcoder;
// allocators.h
class CrtAllocator;
template <typename BaseAllocator>
class MemoryPoolAllocator;
// stream.h
template <typename Encoding>
struct GenericStringStream;
typedef GenericStringStream<UTF8<char> > StringStream;
template <typename Encoding>
struct GenericInsituStringStream;
typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
// stringbuffer.h
template <typename Encoding, typename Allocator>
class GenericStringBuffer;
typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
// filereadstream.h
class FileReadStream;
// filewritestream.h
class FileWriteStream;
// memorybuffer.h
template <typename Allocator>
struct GenericMemoryBuffer;
typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
// memorystream.h
struct MemoryStream;
// reader.h
template<typename Encoding, typename Derived>
struct BaseReaderHandler;
template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
class GenericReader;
typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
// writer.h
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
class Writer;
// prettywriter.h
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
class PrettyWriter;
// document.h
template <typename Encoding, typename Allocator>
struct GenericMember;
template <bool Const, typename Encoding, typename Allocator>
class GenericMemberIterator;
template<typename CharType>
struct GenericStringRef;
template <typename Encoding, typename Allocator>
class GenericValue;
typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
template <typename Encoding, typename Allocator, typename StackAllocator>
class GenericDocument;
typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
// pointer.h
template <typename ValueType, typename Allocator>
class GenericPointer;
typedef GenericPointer<Value, CrtAllocator> Pointer;
// schema.h
template <typename SchemaDocumentType>
class IGenericRemoteSchemaDocumentProvider;
template <typename ValueT, typename Allocator>
class GenericSchemaDocument;
typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
template <
typename SchemaDocumentType,
typename OutputHandler,
typename StateAllocator>
class GenericSchemaValidator;
typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_RAPIDJSONFWD_H_

View File

@@ -0,0 +1,290 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_BIGINTEGER_H_
#define RAPIDJSON_BIGINTEGER_H_
#include "../rapidjson.h"
#if defined(_MSC_VER) && defined(_M_AMD64)
#include <intrin.h> // for _umul128
#pragma intrinsic(_umul128)
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
class BigInteger {
public:
typedef uint64_t Type;
BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
}
explicit BigInteger(uint64_t u) : count_(1) {
digits_[0] = u;
}
BigInteger(const char* decimals, size_t length) : count_(1) {
RAPIDJSON_ASSERT(length > 0);
digits_[0] = 0;
size_t i = 0;
const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
while (length >= kMaxDigitPerIteration) {
AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
length -= kMaxDigitPerIteration;
i += kMaxDigitPerIteration;
}
if (length > 0)
AppendDecimal64(decimals + i, decimals + i + length);
}
BigInteger& operator=(const BigInteger &rhs)
{
if (this != &rhs) {
count_ = rhs.count_;
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
}
return *this;
}
BigInteger& operator=(uint64_t u) {
digits_[0] = u;
count_ = 1;
return *this;
}
BigInteger& operator+=(uint64_t u) {
Type backup = digits_[0];
digits_[0] += u;
for (size_t i = 0; i < count_ - 1; i++) {
if (digits_[i] >= backup)
return *this; // no carry
backup = digits_[i + 1];
digits_[i + 1] += 1;
}
// Last carry
if (digits_[count_ - 1] < backup)
PushBack(1);
return *this;
}
BigInteger& operator*=(uint64_t u) {
if (u == 0) return *this = 0;
if (u == 1) return *this;
if (*this == 1) return *this = u;
uint64_t k = 0;
for (size_t i = 0; i < count_; i++) {
uint64_t hi;
digits_[i] = MulAdd64(digits_[i], u, k, &hi);
k = hi;
}
if (k > 0)
PushBack(k);
return *this;
}
BigInteger& operator*=(uint32_t u) {
if (u == 0) return *this = 0;
if (u == 1) return *this;
if (*this == 1) return *this = u;
uint64_t k = 0;
for (size_t i = 0; i < count_; i++) {
const uint64_t c = digits_[i] >> 32;
const uint64_t d = digits_[i] & 0xFFFFFFFF;
const uint64_t uc = u * c;
const uint64_t ud = u * d;
const uint64_t p0 = ud + k;
const uint64_t p1 = uc + (p0 >> 32);
digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
k = p1 >> 32;
}
if (k > 0)
PushBack(k);
return *this;
}
BigInteger& operator<<=(size_t shift) {
if (IsZero() || shift == 0) return *this;
size_t offset = shift / kTypeBit;
size_t interShift = shift % kTypeBit;
RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
if (interShift == 0) {
std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type));
count_ += offset;
}
else {
digits_[count_] = 0;
for (size_t i = count_; i > 0; i--)
digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
digits_[offset] = digits_[0] << interShift;
count_ += offset;
if (digits_[count_])
count_++;
}
std::memset(digits_, 0, offset * sizeof(Type));
return *this;
}
bool operator==(const BigInteger& rhs) const {
return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
}
bool operator==(const Type rhs) const {
return count_ == 1 && digits_[0] == rhs;
}
BigInteger& MultiplyPow5(unsigned exp) {
static const uint32_t kPow5[12] = {
5,
5 * 5,
5 * 5 * 5,
5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
};
if (exp == 0) return *this;
for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
if (exp > 0) *this *= kPow5[exp - 1];
return *this;
}
// Compute absolute difference of this and rhs.
// Assume this != rhs
bool Difference(const BigInteger& rhs, BigInteger* out) const {
int cmp = Compare(rhs);
RAPIDJSON_ASSERT(cmp != 0);
const BigInteger *a, *b; // Makes a > b
bool ret;
if (cmp < 0) { a = &rhs; b = this; ret = true; }
else { a = this; b = &rhs; ret = false; }
Type borrow = 0;
for (size_t i = 0; i < a->count_; i++) {
Type d = a->digits_[i] - borrow;
if (i < b->count_)
d -= b->digits_[i];
borrow = (d > a->digits_[i]) ? 1 : 0;
out->digits_[i] = d;
if (d != 0)
out->count_ = i + 1;
}
return ret;
}
int Compare(const BigInteger& rhs) const {
if (count_ != rhs.count_)
return count_ < rhs.count_ ? -1 : 1;
for (size_t i = count_; i-- > 0;)
if (digits_[i] != rhs.digits_[i])
return digits_[i] < rhs.digits_[i] ? -1 : 1;
return 0;
}
size_t GetCount() const { return count_; }
Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
private:
void AppendDecimal64(const char* begin, const char* end) {
uint64_t u = ParseUint64(begin, end);
if (IsZero())
*this = u;
else {
unsigned exp = static_cast<unsigned>(end - begin);
(MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
}
}
void PushBack(Type digit) {
RAPIDJSON_ASSERT(count_ < kCapacity);
digits_[count_++] = digit;
}
static uint64_t ParseUint64(const char* begin, const char* end) {
uint64_t r = 0;
for (const char* p = begin; p != end; ++p) {
RAPIDJSON_ASSERT(*p >= '0' && *p <= '9');
r = r * 10u + static_cast<unsigned>(*p - '0');
}
return r;
}
// Assume a * b + k < 2^128
static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
#if defined(_MSC_VER) && defined(_M_AMD64)
uint64_t low = _umul128(a, b, outHigh) + k;
if (low < k)
(*outHigh)++;
return low;
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
__extension__ typedef unsigned __int128 uint128;
uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
p += k;
*outHigh = static_cast<uint64_t>(p >> 64);
return static_cast<uint64_t>(p);
#else
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
x1 += (x0 >> 32); // can't give carry
x1 += x2;
if (x1 < x2)
x3 += (static_cast<uint64_t>(1) << 32);
uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
uint64_t hi = x3 + (x1 >> 32);
lo += k;
if (lo < k)
hi++;
*outHigh = hi;
return lo;
#endif
}
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
static const size_t kCapacity = kBitCount / sizeof(Type);
static const size_t kTypeBit = sizeof(Type) * 8;
Type digits_[kCapacity];
size_t count_;
};
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_BIGINTEGER_H_

View File

@@ -0,0 +1,258 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
#ifndef RAPIDJSON_DIYFP_H_
#define RAPIDJSON_DIYFP_H_
#include "../rapidjson.h"
#if defined(_MSC_VER) && defined(_M_AMD64)
#include <intrin.h>
#pragma intrinsic(_BitScanReverse64)
#pragma intrinsic(_umul128)
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
#endif
struct DiyFp {
DiyFp() : f(), e() {}
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
explicit DiyFp(double d) {
union {
double d;
uint64_t u64;
} u = { d };
int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
uint64_t significand = (u.u64 & kDpSignificandMask);
if (biased_e != 0) {
f = significand + kDpHiddenBit;
e = biased_e - kDpExponentBias;
}
else {
f = significand;
e = kDpMinExponent + 1;
}
}
DiyFp operator-(const DiyFp& rhs) const {
return DiyFp(f - rhs.f, e);
}
DiyFp operator*(const DiyFp& rhs) const {
#if defined(_MSC_VER) && defined(_M_AMD64)
uint64_t h;
uint64_t l = _umul128(f, rhs.f, &h);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
__extension__ typedef unsigned __int128 uint128;
uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
uint64_t h = static_cast<uint64_t>(p >> 64);
uint64_t l = static_cast<uint64_t>(p);
if (l & (uint64_t(1) << 63)) // rounding
h++;
return DiyFp(h, e + rhs.e + 64);
#else
const uint64_t M32 = 0xFFFFFFFF;
const uint64_t a = f >> 32;
const uint64_t b = f & M32;
const uint64_t c = rhs.f >> 32;
const uint64_t d = rhs.f & M32;
const uint64_t ac = a * c;
const uint64_t bc = b * c;
const uint64_t ad = a * d;
const uint64_t bd = b * d;
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
tmp += 1U << 31; /// mult_round
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
#endif
}
DiyFp Normalize() const {
#if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index;
_BitScanReverse64(&index, f);
return DiyFp(f << (63 - index), e - (63 - index));
#elif defined(__GNUC__) && __GNUC__ >= 4
int s = __builtin_clzll(f);
return DiyFp(f << s, e - s);
#else
DiyFp res = *this;
while (!(res.f & (static_cast<uint64_t>(1) << 63))) {
res.f <<= 1;
res.e--;
}
return res;
#endif
}
DiyFp NormalizeBoundary() const {
DiyFp res = *this;
while (!(res.f & (kDpHiddenBit << 1))) {
res.f <<= 1;
res.e--;
}
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
return res;
}
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
mi.f <<= mi.e - pl.e;
mi.e = pl.e;
*plus = pl;
*minus = mi;
}
double ToDouble() const {
union {
double d;
uint64_t u64;
}u;
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
static_cast<uint64_t>(e + kDpExponentBias);
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
return u.d;
}
static const int kDiySignificandSize = 64;
static const int kDpSignificandSize = 52;
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
static const int kDpMinExponent = -kDpExponentBias;
static const int kDpDenormalExponent = -kDpExponentBias + 1;
static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
uint64_t f;
int e;
};
inline DiyFp GetCachedPowerByIndex(size_t index) {
// 10^-348, 10^-340, ..., 10^340
static const uint64_t kCachedPowers_F[] = {
RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
};
static const int16_t kCachedPowers_E[] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
907, 933, 960, 986, 1013, 1039, 1066
};
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
}
inline DiyFp GetCachedPower(int e, int* K) {
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
int k = static_cast<int>(dk);
if (dk - k > 0.0)
k++;
unsigned index = static_cast<unsigned>((k >> 3) + 1);
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
return GetCachedPowerByIndex(index);
}
inline DiyFp GetCachedPower10(int exp, int *outExp) {
unsigned index = (static_cast<unsigned>(exp) + 348u) / 8u;
*outExp = -348 + static_cast<int>(index) * 8;
return GetCachedPowerByIndex(index);
}
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#ifdef __clang__
RAPIDJSON_DIAG_POP
RAPIDJSON_DIAG_OFF(padded)
#endif
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_DIYFP_H_

View File

@@ -0,0 +1,245 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
#ifndef RAPIDJSON_DTOA_
#define RAPIDJSON_DTOA_
#include "itoa.h" // GetDigitsLut()
#include "diyfp.h"
#include "ieee754.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
#endif
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w && delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest + ten_kappa - wp_w)) {
buffer[len - 1]--;
rest += ten_kappa;
}
}
inline unsigned CountDecimalDigit32(uint32_t n) {
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
// Will not reach 10 digits in DigitGen()
//if (n < 1000000000) return 9;
//return 10;
return 9;
}
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
uint64_t p2 = Mp.f & (one.f - 1);
unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
*len = 0;
while (kappa > 0) {
uint32_t d = 0;
switch (kappa) {
case 9: d = p1 / 100000000; p1 %= 100000000; break;
case 8: d = p1 / 10000000; p1 %= 10000000; break;
case 7: d = p1 / 1000000; p1 %= 1000000; break;
case 6: d = p1 / 100000; p1 %= 100000; break;
case 5: d = p1 / 10000; p1 %= 10000; break;
case 4: d = p1 / 1000; p1 %= 1000; break;
case 3: d = p1 / 100; p1 %= 100; break;
case 2: d = p1 / 10; p1 %= 10; break;
case 1: d = p1; p1 = 0; break;
default:;
}
if (d || *len)
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
kappa--;
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
if (tmp <= delta) {
*K += kappa;
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
return;
}
}
// kappa = 0
for (;;) {
p2 *= 10;
delta *= 10;
char d = static_cast<char>(p2 >> -one.e);
if (d || *len)
buffer[(*len)++] = static_cast<char>('0' + d);
p2 &= one.f - 1;
kappa--;
if (p2 < delta) {
*K += kappa;
int index = -static_cast<int>(kappa);
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast<int>(kappa)] : 0));
return;
}
}
}
inline void Grisu2(double value, char* buffer, int* length, int* K) {
const DiyFp v(value);
DiyFp w_m, w_p;
v.NormalizedBoundaries(&w_m, &w_p);
const DiyFp c_mk = GetCachedPower(w_p.e, K);
const DiyFp W = v.Normalize() * c_mk;
DiyFp Wp = w_p * c_mk;
DiyFp Wm = w_m * c_mk;
Wm.f++;
Wp.f--;
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
}
inline char* WriteExponent(int K, char* buffer) {
if (K < 0) {
*buffer++ = '-';
K = -K;
}
if (K >= 100) {
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
K %= 100;
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else if (K >= 10) {
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
else
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
return buffer;
}
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
if (0 <= k && kk <= 21) {
// 1234e7 -> 12340000000
for (int i = length; i < kk; i++)
buffer[i] = '0';
buffer[kk] = '.';
buffer[kk + 1] = '0';
return &buffer[kk + 2];
}
else if (0 < kk && kk <= 21) {
// 1234e-2 -> 12.34
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
buffer[kk] = '.';
if (0 > k + maxDecimalPlaces) {
// When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
// Remove extra trailing zeros (at least one) after truncation.
for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
if (buffer[i] != '0')
return &buffer[i + 1];
return &buffer[kk + 2]; // Reserve one zero
}
else
return &buffer[length + 1];
}
else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234
const int offset = 2 - kk;
std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
buffer[0] = '0';
buffer[1] = '.';
for (int i = 2; i < offset; i++)
buffer[i] = '0';
if (length - kk > maxDecimalPlaces) {
// When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
// Remove extra trailing zeros (at least one) after truncation.
for (int i = maxDecimalPlaces + 1; i > 2; i--)
if (buffer[i] != '0')
return &buffer[i + 1];
return &buffer[3]; // Reserve one zero
}
else
return &buffer[length + offset];
}
else if (kk < -maxDecimalPlaces) {
// Truncate to zero
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
return &buffer[3];
}
else if (length == 1) {
// 1e30
buffer[1] = 'e';
return WriteExponent(kk - 1, &buffer[2]);
}
else {
// 1234e30 -> 1.234e33
std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
buffer[1] = '.';
buffer[length + 1] = 'e';
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
}
}
inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
Double d(value);
if (d.IsZero()) {
if (d.Sign())
*buffer++ = '-'; // -0.0, Issue #289
buffer[0] = '0';
buffer[1] = '.';
buffer[2] = '0';
return &buffer[3];
}
else {
if (value < 0) {
*buffer++ = '-';
value = -value;
}
int length, K;
Grisu2(value, buffer, &length, &K);
return Prettify(buffer, length, K, maxDecimalPlaces);
}
}
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_DTOA_

View File

@@ -0,0 +1,78 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_IEEE754_
#define RAPIDJSON_IEEE754_
#include "../rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
class Double {
public:
Double() {}
Double(double d) : d_(d) {}
Double(uint64_t u) : u_(u) {}
double Value() const { return d_; }
uint64_t Uint64Value() const { return u_; }
double NextPositiveDouble() const {
RAPIDJSON_ASSERT(!Sign());
return Double(u_ + 1).Value();
}
bool Sign() const { return (u_ & kSignMask) != 0; }
uint64_t Significand() const { return u_ & kSignificandMask; }
int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
static unsigned EffectiveSignificandSize(int order) {
if (order >= -1021)
return 53;
else if (order <= -1074)
return 0;
else
return static_cast<unsigned>(order) + 1074;
}
private:
static const int kSignificandSize = 52;
static const int kExponentBias = 0x3FF;
static const int kDenormalExponent = 1 - kExponentBias;
static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
union {
double d_;
uint64_t u_;
};
};
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_IEEE754_

View File

@@ -0,0 +1,304 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_ITOA_
#define RAPIDJSON_ITOA_
#include "../rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
inline const char* GetDigitsLut() {
static const char cDigitsLut[200] = {
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
};
return cDigitsLut;
}
inline char* u32toa(uint32_t value, char* buffer) {
const char* cDigitsLut = GetDigitsLut();
if (value < 10000) {
const uint32_t d1 = (value / 100) << 1;
const uint32_t d2 = (value % 100) << 1;
if (value >= 1000)
*buffer++ = cDigitsLut[d1];
if (value >= 100)
*buffer++ = cDigitsLut[d1 + 1];
if (value >= 10)
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
}
else if (value < 100000000) {
// value = bbbbcccc
const uint32_t b = value / 10000;
const uint32_t c = value % 10000;
const uint32_t d1 = (b / 100) << 1;
const uint32_t d2 = (b % 100) << 1;
const uint32_t d3 = (c / 100) << 1;
const uint32_t d4 = (c % 100) << 1;
if (value >= 10000000)
*buffer++ = cDigitsLut[d1];
if (value >= 1000000)
*buffer++ = cDigitsLut[d1 + 1];
if (value >= 100000)
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
*buffer++ = cDigitsLut[d3];
*buffer++ = cDigitsLut[d3 + 1];
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
}
else {
// value = aabbbbcccc in decimal
const uint32_t a = value / 100000000; // 1 to 42
value %= 100000000;
if (a >= 10) {
const unsigned i = a << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
}
else
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
const uint32_t b = value / 10000; // 0 to 9999
const uint32_t c = value % 10000; // 0 to 9999
const uint32_t d1 = (b / 100) << 1;
const uint32_t d2 = (b % 100) << 1;
const uint32_t d3 = (c / 100) << 1;
const uint32_t d4 = (c % 100) << 1;
*buffer++ = cDigitsLut[d1];
*buffer++ = cDigitsLut[d1 + 1];
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
*buffer++ = cDigitsLut[d3];
*buffer++ = cDigitsLut[d3 + 1];
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
}
return buffer;
}
inline char* i32toa(int32_t value, char* buffer) {
uint32_t u = static_cast<uint32_t>(value);
if (value < 0) {
*buffer++ = '-';
u = ~u + 1;
}
return u32toa(u, buffer);
}
inline char* u64toa(uint64_t value, char* buffer) {
const char* cDigitsLut = GetDigitsLut();
const uint64_t kTen8 = 100000000;
const uint64_t kTen9 = kTen8 * 10;
const uint64_t kTen10 = kTen8 * 100;
const uint64_t kTen11 = kTen8 * 1000;
const uint64_t kTen12 = kTen8 * 10000;
const uint64_t kTen13 = kTen8 * 100000;
const uint64_t kTen14 = kTen8 * 1000000;
const uint64_t kTen15 = kTen8 * 10000000;
const uint64_t kTen16 = kTen8 * kTen8;
if (value < kTen8) {
uint32_t v = static_cast<uint32_t>(value);
if (v < 10000) {
const uint32_t d1 = (v / 100) << 1;
const uint32_t d2 = (v % 100) << 1;
if (v >= 1000)
*buffer++ = cDigitsLut[d1];
if (v >= 100)
*buffer++ = cDigitsLut[d1 + 1];
if (v >= 10)
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
}
else {
// value = bbbbcccc
const uint32_t b = v / 10000;
const uint32_t c = v % 10000;
const uint32_t d1 = (b / 100) << 1;
const uint32_t d2 = (b % 100) << 1;
const uint32_t d3 = (c / 100) << 1;
const uint32_t d4 = (c % 100) << 1;
if (value >= 10000000)
*buffer++ = cDigitsLut[d1];
if (value >= 1000000)
*buffer++ = cDigitsLut[d1 + 1];
if (value >= 100000)
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
*buffer++ = cDigitsLut[d3];
*buffer++ = cDigitsLut[d3 + 1];
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
}
}
else if (value < kTen16) {
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
const uint32_t b0 = v0 / 10000;
const uint32_t c0 = v0 % 10000;
const uint32_t d1 = (b0 / 100) << 1;
const uint32_t d2 = (b0 % 100) << 1;
const uint32_t d3 = (c0 / 100) << 1;
const uint32_t d4 = (c0 % 100) << 1;
const uint32_t b1 = v1 / 10000;
const uint32_t c1 = v1 % 10000;
const uint32_t d5 = (b1 / 100) << 1;
const uint32_t d6 = (b1 % 100) << 1;
const uint32_t d7 = (c1 / 100) << 1;
const uint32_t d8 = (c1 % 100) << 1;
if (value >= kTen15)
*buffer++ = cDigitsLut[d1];
if (value >= kTen14)
*buffer++ = cDigitsLut[d1 + 1];
if (value >= kTen13)
*buffer++ = cDigitsLut[d2];
if (value >= kTen12)
*buffer++ = cDigitsLut[d2 + 1];
if (value >= kTen11)
*buffer++ = cDigitsLut[d3];
if (value >= kTen10)
*buffer++ = cDigitsLut[d3 + 1];
if (value >= kTen9)
*buffer++ = cDigitsLut[d4];
if (value >= kTen8)
*buffer++ = cDigitsLut[d4 + 1];
*buffer++ = cDigitsLut[d5];
*buffer++ = cDigitsLut[d5 + 1];
*buffer++ = cDigitsLut[d6];
*buffer++ = cDigitsLut[d6 + 1];
*buffer++ = cDigitsLut[d7];
*buffer++ = cDigitsLut[d7 + 1];
*buffer++ = cDigitsLut[d8];
*buffer++ = cDigitsLut[d8 + 1];
}
else {
const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
value %= kTen16;
if (a < 10)
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
else if (a < 100) {
const uint32_t i = a << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
}
else if (a < 1000) {
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
const uint32_t i = (a % 100) << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
}
else {
const uint32_t i = (a / 100) << 1;
const uint32_t j = (a % 100) << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
*buffer++ = cDigitsLut[j];
*buffer++ = cDigitsLut[j + 1];
}
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
const uint32_t b0 = v0 / 10000;
const uint32_t c0 = v0 % 10000;
const uint32_t d1 = (b0 / 100) << 1;
const uint32_t d2 = (b0 % 100) << 1;
const uint32_t d3 = (c0 / 100) << 1;
const uint32_t d4 = (c0 % 100) << 1;
const uint32_t b1 = v1 / 10000;
const uint32_t c1 = v1 % 10000;
const uint32_t d5 = (b1 / 100) << 1;
const uint32_t d6 = (b1 % 100) << 1;
const uint32_t d7 = (c1 / 100) << 1;
const uint32_t d8 = (c1 % 100) << 1;
*buffer++ = cDigitsLut[d1];
*buffer++ = cDigitsLut[d1 + 1];
*buffer++ = cDigitsLut[d2];
*buffer++ = cDigitsLut[d2 + 1];
*buffer++ = cDigitsLut[d3];
*buffer++ = cDigitsLut[d3 + 1];
*buffer++ = cDigitsLut[d4];
*buffer++ = cDigitsLut[d4 + 1];
*buffer++ = cDigitsLut[d5];
*buffer++ = cDigitsLut[d5 + 1];
*buffer++ = cDigitsLut[d6];
*buffer++ = cDigitsLut[d6 + 1];
*buffer++ = cDigitsLut[d7];
*buffer++ = cDigitsLut[d7 + 1];
*buffer++ = cDigitsLut[d8];
*buffer++ = cDigitsLut[d8 + 1];
}
return buffer;
}
inline char* i64toa(int64_t value, char* buffer) {
uint64_t u = static_cast<uint64_t>(value);
if (value < 0) {
*buffer++ = '-';
u = ~u + 1;
}
return u64toa(u, buffer);
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ITOA_

View File

@@ -0,0 +1,181 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_META_H_
#define RAPIDJSON_INTERNAL_META_H_
#include "../rapidjson.h"
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#if defined(_MSC_VER)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(6334)
#endif
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
#include <type_traits>
#endif
//@cond RAPIDJSON_INTERNAL
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
template <typename T> struct Void { typedef void Type; };
///////////////////////////////////////////////////////////////////////////////
// BoolType, TrueType, FalseType
//
template <bool Cond> struct BoolType {
static const bool Value = Cond;
typedef BoolType Type;
};
typedef BoolType<true> TrueType;
typedef BoolType<false> FalseType;
///////////////////////////////////////////////////////////////////////////////
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
//
template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
template <> struct AndExprCond<true, true> : TrueType {};
template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
template <> struct OrExprCond<false, false> : FalseType {};
template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
///////////////////////////////////////////////////////////////////////////////
// AddConst, MaybeAddConst, RemoveConst
template <typename T> struct AddConst { typedef const T Type; };
template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
template <typename T> struct RemoveConst { typedef T Type; };
template <typename T> struct RemoveConst<const T> { typedef T Type; };
///////////////////////////////////////////////////////////////////////////////
// IsSame, IsConst, IsMoreConst, IsPointer
//
template <typename T, typename U> struct IsSame : FalseType {};
template <typename T> struct IsSame<T, T> : TrueType {};
template <typename T> struct IsConst : FalseType {};
template <typename T> struct IsConst<const T> : TrueType {};
template <typename CT, typename T>
struct IsMoreConst
: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
template <typename T> struct IsPointer : FalseType {};
template <typename T> struct IsPointer<T*> : TrueType {};
///////////////////////////////////////////////////////////////////////////////
// IsBaseOf
//
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
template <typename B, typename D> struct IsBaseOf
: BoolType< ::std::is_base_of<B,D>::value> {};
#else // simplified version adopted from Boost
template<typename B, typename D> struct IsBaseOfImpl {
RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
typedef char (&Yes)[1];
typedef char (&No) [2];
template <typename T>
static Yes Check(const D*, T);
static No Check(const B*, int);
struct Host {
operator const B*() const;
operator const D*();
};
enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
};
template <typename B, typename D> struct IsBaseOf
: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
//////////////////////////////////////////////////////////////////////////
// EnableIf / DisableIf
//
template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
template <typename T> struct EnableIfCond<false, T> { /* empty */ };
template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
template <typename T> struct DisableIfCond<true, T> { /* empty */ };
template <typename Condition, typename T = void>
struct EnableIf : EnableIfCond<Condition::Value, T> {};
template <typename Condition, typename T = void>
struct DisableIf : DisableIfCond<Condition::Value, T> {};
// SFINAE helpers
struct SfinaeTag {};
template <typename T> struct RemoveSfinaeTag;
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
#define RAPIDJSON_REMOVEFPTR_(type) \
typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
< ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
#define RAPIDJSON_ENABLEIF(cond) \
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
#define RAPIDJSON_DISABLEIF(cond) \
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
<RAPIDJSON_REMOVEFPTR_(cond), \
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
<RAPIDJSON_REMOVEFPTR_(cond), \
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
} // namespace internal
RAPIDJSON_NAMESPACE_END
//@endcond
#if defined(__GNUC__) || defined(_MSC_VER)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_INTERNAL_META_H_

View File

@@ -0,0 +1,55 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_POW10_
#define RAPIDJSON_POW10_
#include "../rapidjson.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
//! Computes integer powers of 10 in double (10.0^n).
/*! This function uses lookup table for fast and accurate results.
\param n non-negative exponent. Must <= 308.
\return 10.0^n
*/
inline double Pow10(int n) {
static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
1e+0,
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
};
RAPIDJSON_ASSERT(n >= 0 && n <= 308);
return e[n];
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_POW10_

View File

@@ -0,0 +1,701 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_REGEX_H_
#define RAPIDJSON_INTERNAL_REGEX_H_
#include "../allocators.h"
#include "../stream.h"
#include "stack.h"
#ifdef __clang__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(padded)
RAPIDJSON_DIAG_OFF(switch-enum)
RAPIDJSON_DIAG_OFF(implicit-fallthrough)
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
#ifdef _MSC_VER
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
#endif
#ifndef RAPIDJSON_REGEX_VERBOSE
#define RAPIDJSON_REGEX_VERBOSE 0
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
///////////////////////////////////////////////////////////////////////////////
// GenericRegex
static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
static const SizeType kRegexInvalidRange = ~SizeType(0);
//! Regular expression engine with subset of ECMAscript grammar.
/*!
Supported regular expression syntax:
- \c ab Concatenation
- \c a|b Alternation
- \c a? Zero or one
- \c a* Zero or more
- \c a+ One or more
- \c a{3} Exactly 3 times
- \c a{3,} At least 3 times
- \c a{3,5} 3 to 5 times
- \c (ab) Grouping
- \c ^a At the beginning
- \c a$ At the end
- \c . Any character
- \c [abc] Character classes
- \c [a-c] Character class range
- \c [a-z0-9_] Character class combination
- \c [^abc] Negated character classes
- \c [^a-c] Negated character class range
- \c [\b] Backspace (U+0008)
- \c \\| \\\\ ... Escape characters
- \c \\f Form feed (U+000C)
- \c \\n Line feed (U+000A)
- \c \\r Carriage return (U+000D)
- \c \\t Tab (U+0009)
- \c \\v Vertical tab (U+000B)
\note This is a Thompson NFA engine, implemented with reference to
Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
https://swtch.com/~rsc/regexp/regexp1.html
*/
template <typename Encoding, typename Allocator = CrtAllocator>
class GenericRegex {
public:
typedef typename Encoding::Ch Ch;
GenericRegex(const Ch* source, Allocator* allocator = 0) :
states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_()
{
GenericStringStream<Encoding> ss(source);
DecodedStream<GenericStringStream<Encoding> > ds(ss);
Parse(ds);
}
~GenericRegex() {
Allocator::Free(stateSet_);
}
bool IsValid() const {
return root_ != kRegexInvalidState;
}
template <typename InputStream>
bool Match(InputStream& is) const {
return SearchWithAnchoring(is, true, true);
}
bool Match(const Ch* s) const {
GenericStringStream<Encoding> is(s);
return Match(is);
}
template <typename InputStream>
bool Search(InputStream& is) const {
return SearchWithAnchoring(is, anchorBegin_, anchorEnd_);
}
bool Search(const Ch* s) const {
GenericStringStream<Encoding> is(s);
return Search(is);
}
private:
enum Operator {
kZeroOrOne,
kZeroOrMore,
kOneOrMore,
kConcatenation,
kAlternation,
kLeftParenthesis
};
static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
static const unsigned kRangeNegationFlag = 0x80000000;
struct Range {
unsigned start; //
unsigned end;
SizeType next;
};
struct State {
SizeType out; //!< Equals to kInvalid for matching state
SizeType out1; //!< Equals to non-kInvalid for split
SizeType rangeStart;
unsigned codepoint;
};
struct Frag {
Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
SizeType start;
SizeType out; //!< link-list of all output states
SizeType minIndex;
};
template <typename SourceStream>
class DecodedStream {
public:
DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
unsigned Peek() { return codepoint_; }
unsigned Take() {
unsigned c = codepoint_;
if (c) // No further decoding when '\0'
Decode();
return c;
}
private:
void Decode() {
if (!Encoding::Decode(ss_, &codepoint_))
codepoint_ = 0;
}
SourceStream& ss_;
unsigned codepoint_;
};
State& GetState(SizeType index) {
RAPIDJSON_ASSERT(index < stateCount_);
return states_.template Bottom<State>()[index];
}
const State& GetState(SizeType index) const {
RAPIDJSON_ASSERT(index < stateCount_);
return states_.template Bottom<State>()[index];
}
Range& GetRange(SizeType index) {
RAPIDJSON_ASSERT(index < rangeCount_);
return ranges_.template Bottom<Range>()[index];
}
const Range& GetRange(SizeType index) const {
RAPIDJSON_ASSERT(index < rangeCount_);
return ranges_.template Bottom<Range>()[index];
}
template <typename InputStream>
void Parse(DecodedStream<InputStream>& ds) {
Allocator allocator;
Stack<Allocator> operandStack(&allocator, 256); // Frag
Stack<Allocator> operatorStack(&allocator, 256); // Operator
Stack<Allocator> atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis)
*atomCountStack.template Push<unsigned>() = 0;
unsigned codepoint;
while (ds.Peek() != 0) {
switch (codepoint = ds.Take()) {
case '^':
anchorBegin_ = true;
break;
case '$':
anchorEnd_ = true;
break;
case '|':
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
return;
*operatorStack.template Push<Operator>() = kAlternation;
*atomCountStack.template Top<unsigned>() = 0;
break;
case '(':
*operatorStack.template Push<Operator>() = kLeftParenthesis;
*atomCountStack.template Push<unsigned>() = 0;
break;
case ')':
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
return;
if (operatorStack.Empty())
return;
operatorStack.template Pop<Operator>(1);
atomCountStack.template Pop<unsigned>(1);
ImplicitConcatenation(atomCountStack, operatorStack);
break;
case '?':
if (!Eval(operandStack, kZeroOrOne))
return;
break;
case '*':
if (!Eval(operandStack, kZeroOrMore))
return;
break;
case '+':
if (!Eval(operandStack, kOneOrMore))
return;
break;
case '{':
{
unsigned n, m;
if (!ParseUnsigned(ds, &n))
return;
if (ds.Peek() == ',') {
ds.Take();
if (ds.Peek() == '}')
m = kInfinityQuantifier;
else if (!ParseUnsigned(ds, &m) || m < n)
return;
}
else
m = n;
if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
return;
ds.Take();
}
break;
case '.':
PushOperand(operandStack, kAnyCharacterClass);
ImplicitConcatenation(atomCountStack, operatorStack);
break;
case '[':
{
SizeType range;
if (!ParseRange(ds, &range))
return;
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
GetState(s).rangeStart = range;
*operandStack.template Push<Frag>() = Frag(s, s, s);
}
ImplicitConcatenation(atomCountStack, operatorStack);
break;
case '\\': // Escape character
if (!CharacterEscape(ds, &codepoint))
return; // Unsupported escape character
// fall through to default
default: // Pattern character
PushOperand(operandStack, codepoint);
ImplicitConcatenation(atomCountStack, operatorStack);
}
}
while (!operatorStack.Empty())
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
return;
// Link the operand to matching state.
if (operandStack.GetSize() == sizeof(Frag)) {
Frag* e = operandStack.template Pop<Frag>(1);
Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
root_ = e->start;
#if RAPIDJSON_REGEX_VERBOSE
printf("root: %d\n", root_);
for (SizeType i = 0; i < stateCount_ ; i++) {
State& s = GetState(i);
printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
}
printf("\n");
#endif
}
// Preallocate buffer for SearchWithAnchoring()
RAPIDJSON_ASSERT(stateSet_ == 0);
if (stateCount_ > 0) {
stateSet_ = static_cast<unsigned*>(states_.GetAllocator().Malloc(GetStateSetSize()));
state0_.template Reserve<SizeType>(stateCount_);
state1_.template Reserve<SizeType>(stateCount_);
}
}
SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
State* s = states_.template Push<State>();
s->out = out;
s->out1 = out1;
s->codepoint = codepoint;
s->rangeStart = kRegexInvalidRange;
return stateCount_++;
}
void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
*operandStack.template Push<Frag>() = Frag(s, s, s);
}
void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
if (*atomCountStack.template Top<unsigned>())
*operatorStack.template Push<Operator>() = kConcatenation;
(*atomCountStack.template Top<unsigned>())++;
}
SizeType Append(SizeType l1, SizeType l2) {
SizeType old = l1;
while (GetState(l1).out != kRegexInvalidState)
l1 = GetState(l1).out;
GetState(l1).out = l2;
return old;
}
void Patch(SizeType l, SizeType s) {
for (SizeType next; l != kRegexInvalidState; l = next) {
next = GetState(l).out;
GetState(l).out = s;
}
}
bool Eval(Stack<Allocator>& operandStack, Operator op) {
switch (op) {
case kConcatenation:
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
{
Frag e2 = *operandStack.template Pop<Frag>(1);
Frag e1 = *operandStack.template Pop<Frag>(1);
Patch(e1.out, e2.start);
*operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
}
return true;
case kAlternation:
if (operandStack.GetSize() >= sizeof(Frag) * 2) {
Frag e2 = *operandStack.template Pop<Frag>(1);
Frag e1 = *operandStack.template Pop<Frag>(1);
SizeType s = NewState(e1.start, e2.start, 0);
*operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
return true;
}
return false;
case kZeroOrOne:
if (operandStack.GetSize() >= sizeof(Frag)) {
Frag e = *operandStack.template Pop<Frag>(1);
SizeType s = NewState(kRegexInvalidState, e.start, 0);
*operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
return true;
}
return false;
case kZeroOrMore:
if (operandStack.GetSize() >= sizeof(Frag)) {
Frag e = *operandStack.template Pop<Frag>(1);
SizeType s = NewState(kRegexInvalidState, e.start, 0);
Patch(e.out, s);
*operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
return true;
}
return false;
default:
RAPIDJSON_ASSERT(op == kOneOrMore);
if (operandStack.GetSize() >= sizeof(Frag)) {
Frag e = *operandStack.template Pop<Frag>(1);
SizeType s = NewState(kRegexInvalidState, e.start, 0);
Patch(e.out, s);
*operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
return true;
}
return false;
}
}
bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
RAPIDJSON_ASSERT(n <= m);
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
if (n == 0) {
if (m == 0) // a{0} not support
return false;
else if (m == kInfinityQuantifier)
Eval(operandStack, kZeroOrMore); // a{0,} -> a*
else {
Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
for (unsigned i = 0; i < m - 1; i++)
CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
for (unsigned i = 0; i < m - 1; i++)
Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
}
return true;
}
for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
CloneTopOperand(operandStack);
if (m == kInfinityQuantifier)
Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
else if (m > n) {
CloneTopOperand(operandStack); // a{3,5} -> a a a a
Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
for (unsigned i = n; i < m - 1; i++)
CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
for (unsigned i = n; i < m; i++)
Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
}
for (unsigned i = 0; i < n - 1; i++)
Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
return true;
}
static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
void CloneTopOperand(Stack<Allocator>& operandStack) {
const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
State* s = states_.template Push<State>(count);
memcpy(s, &GetState(src.minIndex), count * sizeof(State));
for (SizeType j = 0; j < count; j++) {
if (s[j].out != kRegexInvalidState)
s[j].out += count;
if (s[j].out1 != kRegexInvalidState)
s[j].out1 += count;
}
*operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
stateCount_ += count;
}
template <typename InputStream>
bool ParseUnsigned(DecodedStream<InputStream>& ds, unsigned* u) {
unsigned r = 0;
if (ds.Peek() < '0' || ds.Peek() > '9')
return false;
while (ds.Peek() >= '0' && ds.Peek() <= '9') {
if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
return false; // overflow
r = r * 10 + (ds.Take() - '0');
}
*u = r;
return true;
}
template <typename InputStream>
bool ParseRange(DecodedStream<InputStream>& ds, SizeType* range) {
bool isBegin = true;
bool negate = false;
int step = 0;
SizeType start = kRegexInvalidRange;
SizeType current = kRegexInvalidRange;
unsigned codepoint;
while ((codepoint = ds.Take()) != 0) {
if (isBegin) {
isBegin = false;
if (codepoint == '^') {
negate = true;
continue;
}
}
switch (codepoint) {
case ']':
if (start == kRegexInvalidRange)
return false; // Error: nothing inside []
if (step == 2) { // Add trailing '-'
SizeType r = NewRange('-');
RAPIDJSON_ASSERT(current != kRegexInvalidRange);
GetRange(current).next = r;
}
if (negate)
GetRange(start).start |= kRangeNegationFlag;
*range = start;
return true;
case '\\':
if (ds.Peek() == 'b') {
ds.Take();
codepoint = 0x0008; // Escape backspace character
}
else if (!CharacterEscape(ds, &codepoint))
return false;
// fall through to default
default:
switch (step) {
case 1:
if (codepoint == '-') {
step++;
break;
}
// fall through to step 0 for other characters
case 0:
{
SizeType r = NewRange(codepoint);
if (current != kRegexInvalidRange)
GetRange(current).next = r;
if (start == kRegexInvalidRange)
start = r;
current = r;
}
step = 1;
break;
default:
RAPIDJSON_ASSERT(step == 2);
GetRange(current).end = codepoint;
step = 0;
}
}
}
return false;
}
SizeType NewRange(unsigned codepoint) {
Range* r = ranges_.template Push<Range>();
r->start = r->end = codepoint;
r->next = kRegexInvalidRange;
return rangeCount_++;
}
template <typename InputStream>
bool CharacterEscape(DecodedStream<InputStream>& ds, unsigned* escapedCodepoint) {
unsigned codepoint;
switch (codepoint = ds.Take()) {
case '^':
case '$':
case '|':
case '(':
case ')':
case '?':
case '*':
case '+':
case '.':
case '[':
case ']':
case '{':
case '}':
case '\\':
*escapedCodepoint = codepoint; return true;
case 'f': *escapedCodepoint = 0x000C; return true;
case 'n': *escapedCodepoint = 0x000A; return true;
case 'r': *escapedCodepoint = 0x000D; return true;
case 't': *escapedCodepoint = 0x0009; return true;
case 'v': *escapedCodepoint = 0x000B; return true;
default:
return false; // Unsupported escape character
}
}
template <typename InputStream>
bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const {
RAPIDJSON_ASSERT(IsValid());
DecodedStream<InputStream> ds(is);
state0_.Clear();
Stack<Allocator> *current = &state0_, *next = &state1_;
const size_t stateSetSize = GetStateSetSize();
std::memset(stateSet_, 0, stateSetSize);
bool matched = AddState(*current, root_);
unsigned codepoint;
while (!current->Empty() && (codepoint = ds.Take()) != 0) {
std::memset(stateSet_, 0, stateSetSize);
next->Clear();
matched = false;
for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
const State& sr = GetState(*s);
if (sr.codepoint == codepoint ||
sr.codepoint == kAnyCharacterClass ||
(sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
{
matched = AddState(*next, sr.out) || matched;
if (!anchorEnd && matched)
return true;
}
if (!anchorBegin)
AddState(*next, root_);
}
internal::Swap(current, next);
}
return matched;
}
size_t GetStateSetSize() const {
return (stateCount_ + 31) / 32 * 4;
}
// Return whether the added states is a match state
bool AddState(Stack<Allocator>& l, SizeType index) const {
RAPIDJSON_ASSERT(index != kRegexInvalidState);
const State& s = GetState(index);
if (s.out1 != kRegexInvalidState) { // Split
bool matched = AddState(l, s.out);
return AddState(l, s.out1) || matched;
}
else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) {
stateSet_[index >> 5] |= (1 << (index & 31));
*l.template PushUnsafe<SizeType>() = index;
}
return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
}
bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0;
while (rangeIndex != kRegexInvalidRange) {
const Range& r = GetRange(rangeIndex);
if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end)
return yes;
rangeIndex = r.next;
}
return !yes;
}
Stack<Allocator> states_;
Stack<Allocator> ranges_;
SizeType root_;
SizeType stateCount_;
SizeType rangeCount_;
static const unsigned kInfinityQuantifier = ~0u;
// For SearchWithAnchoring()
uint32_t* stateSet_; // allocated by states_.GetAllocator()
mutable Stack<Allocator> state0_;
mutable Stack<Allocator> state1_;
bool anchorBegin_;
bool anchorEnd_;
};
typedef GenericRegex<UTF8<> > Regex;
} // namespace internal
RAPIDJSON_NAMESPACE_END
#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
#ifdef _MSC_VER
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_INTERNAL_REGEX_H_

View File

@@ -0,0 +1,230 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_STACK_H_
#define RAPIDJSON_INTERNAL_STACK_H_
#include "../allocators.h"
#include "swap.h"
#if defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(c++98-compat)
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
///////////////////////////////////////////////////////////////////////////////
// Stack
//! A type-unsafe stack for storing different types of data.
/*! \tparam Allocator Allocator for allocating stack memory.
*/
template <typename Allocator>
class Stack {
public:
// Optimization note: Do not allocate memory for stack_ in constructor.
// Do it lazily when first Push() -> Expand() -> Resize().
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
Stack(Stack&& rhs)
: allocator_(rhs.allocator_),
ownAllocator_(rhs.ownAllocator_),
stack_(rhs.stack_),
stackTop_(rhs.stackTop_),
stackEnd_(rhs.stackEnd_),
initialCapacity_(rhs.initialCapacity_)
{
rhs.allocator_ = 0;
rhs.ownAllocator_ = 0;
rhs.stack_ = 0;
rhs.stackTop_ = 0;
rhs.stackEnd_ = 0;
rhs.initialCapacity_ = 0;
}
#endif
~Stack() {
Destroy();
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
Stack& operator=(Stack&& rhs) {
if (&rhs != this)
{
Destroy();
allocator_ = rhs.allocator_;
ownAllocator_ = rhs.ownAllocator_;
stack_ = rhs.stack_;
stackTop_ = rhs.stackTop_;
stackEnd_ = rhs.stackEnd_;
initialCapacity_ = rhs.initialCapacity_;
rhs.allocator_ = 0;
rhs.ownAllocator_ = 0;
rhs.stack_ = 0;
rhs.stackTop_ = 0;
rhs.stackEnd_ = 0;
rhs.initialCapacity_ = 0;
}
return *this;
}
#endif
void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT {
internal::Swap(allocator_, rhs.allocator_);
internal::Swap(ownAllocator_, rhs.ownAllocator_);
internal::Swap(stack_, rhs.stack_);
internal::Swap(stackTop_, rhs.stackTop_);
internal::Swap(stackEnd_, rhs.stackEnd_);
internal::Swap(initialCapacity_, rhs.initialCapacity_);
}
void Clear() { stackTop_ = stack_; }
void ShrinkToFit() {
if (Empty()) {
// If the stack is empty, completely deallocate the memory.
Allocator::Free(stack_);
stack_ = 0;
stackTop_ = 0;
stackEnd_ = 0;
}
else
Resize(GetSize());
}
// Optimization note: try to minimize the size of this function for force inline.
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
template<typename T>
RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
// Expand the stack if needed
if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_))
Expand<T>(count);
}
template<typename T>
RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
Reserve<T>(count);
return PushUnsafe<T>(count);
}
template<typename T>
RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_);
T* ret = reinterpret_cast<T*>(stackTop_);
stackTop_ += sizeof(T) * count;
return ret;
}
template<typename T>
T* Pop(size_t count) {
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
stackTop_ -= count * sizeof(T);
return reinterpret_cast<T*>(stackTop_);
}
template<typename T>
T* Top() {
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
}
template<typename T>
const T* Top() const {
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
}
template<typename T>
T* End() { return reinterpret_cast<T*>(stackTop_); }
template<typename T>
const T* End() const { return reinterpret_cast<T*>(stackTop_); }
template<typename T>
T* Bottom() { return reinterpret_cast<T*>(stack_); }
template<typename T>
const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
bool HasAllocator() const {
return allocator_ != 0;
}
Allocator& GetAllocator() {
RAPIDJSON_ASSERT(allocator_);
return *allocator_;
}
bool Empty() const { return stackTop_ == stack_; }
size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
private:
template<typename T>
void Expand(size_t count) {
// Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
size_t newCapacity;
if (stack_ == 0) {
if (!allocator_)
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
newCapacity = initialCapacity_;
} else {
newCapacity = GetCapacity();
newCapacity += (newCapacity + 1) / 2;
}
size_t newSize = GetSize() + sizeof(T) * count;
if (newCapacity < newSize)
newCapacity = newSize;
Resize(newCapacity);
}
void Resize(size_t newCapacity) {
const size_t size = GetSize(); // Backup the current size
stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
stackTop_ = stack_ + size;
stackEnd_ = stack_ + newCapacity;
}
void Destroy() {
Allocator::Free(stack_);
RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
}
// Prohibit copy constructor & assignment operator.
Stack(const Stack&);
Stack& operator=(const Stack&);
Allocator* allocator_;
Allocator* ownAllocator_;
char *stack_;
char *stackTop_;
char *stackEnd_;
size_t initialCapacity_;
};
} // namespace internal
RAPIDJSON_NAMESPACE_END
#if defined(__clang__)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_STACK_H_

View File

@@ -0,0 +1,55 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
#define RAPIDJSON_INTERNAL_STRFUNC_H_
#include "../stream.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
//! Custom strlen() which works on different character types.
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
\param s Null-terminated input string.
\return Number of characters in the string.
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
*/
template <typename Ch>
inline SizeType StrLen(const Ch* s) {
const Ch* p = s;
while (*p) ++p;
return SizeType(p - s);
}
//! Returns number of code points in a encoded string.
template<typename Encoding>
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
GenericStringStream<Encoding> is(s);
const typename Encoding::Ch* end = s + length;
SizeType count = 0;
while (is.src_ < end) {
unsigned codepoint;
if (!Encoding::Decode(is, &codepoint))
return false;
count++;
}
*outCount = count;
return true;
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_INTERNAL_STRFUNC_H_

View File

@@ -0,0 +1,269 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_STRTOD_
#define RAPIDJSON_STRTOD_
#include "ieee754.h"
#include "biginteger.h"
#include "diyfp.h"
#include "pow10.h"
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
inline double FastPath(double significand, int exp) {
if (exp < -308)
return 0.0;
else if (exp >= 0)
return significand * internal::Pow10(exp);
else
return significand / internal::Pow10(-exp);
}
inline double StrtodNormalPrecision(double d, int p) {
if (p < -308) {
// Prevent expSum < -308, making Pow10(p) = 0
d = FastPath(d, -308);
d = FastPath(d, p + 308);
}
else
d = FastPath(d, p);
return d;
}
template <typename T>
inline T Min3(T a, T b, T c) {
T m = a;
if (m > b) m = b;
if (m > c) m = c;
return m;
}
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
const Double db(b);
const uint64_t bInt = db.IntegerSignificand();
const int bExp = db.IntegerExponent();
const int hExp = bExp - 1;
int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
// Adjust for decimal exponent
if (dExp >= 0) {
dS_Exp2 += dExp;
dS_Exp5 += dExp;
}
else {
bS_Exp2 -= dExp;
bS_Exp5 -= dExp;
hS_Exp2 -= dExp;
hS_Exp5 -= dExp;
}
// Adjust for binary exponent
if (bExp >= 0)
bS_Exp2 += bExp;
else {
dS_Exp2 -= bExp;
hS_Exp2 -= bExp;
}
// Adjust for half ulp exponent
if (hExp >= 0)
hS_Exp2 += hExp;
else {
dS_Exp2 -= hExp;
bS_Exp2 -= hExp;
}
// Remove common power of two factor from all three scaled values
int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
dS_Exp2 -= common_Exp2;
bS_Exp2 -= common_Exp2;
hS_Exp2 -= common_Exp2;
BigInteger dS = d;
dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
BigInteger bS(bInt);
bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
BigInteger hS(1);
hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2);
BigInteger delta(0);
dS.Difference(bS, &delta);
return delta.Compare(hS);
}
inline bool StrtodFast(double d, int p, double* result) {
// Use fast path for string-to-double conversion if possible
// see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
if (p > 22 && p < 22 + 16) {
// Fast Path Cases In Disguise
d *= internal::Pow10(p - 22);
p = 22;
}
if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
*result = FastPath(d, p);
return true;
}
else
return false;
}
// Compute an approximation and see if it is within 1/2 ULP
inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) {
uint64_t significand = 0;
size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
for (; i < length; i++) {
if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
(significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5'))
break;
significand = significand * 10u + static_cast<unsigned>(decimals[i] - '0');
}
if (i < length && decimals[i] >= '5') // Rounding
significand++;
size_t remaining = length - i;
const unsigned kUlpShift = 3;
const unsigned kUlp = 1 << kUlpShift;
int64_t error = (remaining == 0) ? 0 : kUlp / 2;
DiyFp v(significand, 0);
v = v.Normalize();
error <<= -v.e;
const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(i) + exp;
int actualExp;
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
if (actualExp != dExp) {
static const DiyFp kPow10[] = {
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1
DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2
DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3
DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4
DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5
DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7
};
int adjustment = dExp - actualExp - 1;
RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7);
v = v * kPow10[adjustment];
if (length + static_cast<unsigned>(adjustment)> 19u) // has more digits than decimal digits in 64-bit
error += kUlp / 2;
}
v = v * cachedPower;
error += kUlp + (error == 0 ? 0 : 1);
const int oldExp = v.e;
v = v.Normalize();
error <<= oldExp - v.e;
const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
unsigned precisionSize = 64 - effectiveSignificandSize;
if (precisionSize + kUlpShift >= 64) {
unsigned scaleExp = (precisionSize + kUlpShift) - 63;
v.f >>= scaleExp;
v.e += scaleExp;
error = (error >> scaleExp) + 1 + static_cast<int>(kUlp);
precisionSize -= scaleExp;
}
DiyFp rounded(v.f >> precisionSize, v.e + static_cast<int>(precisionSize));
const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
if (precisionBits >= halfWay + static_cast<unsigned>(error)) {
rounded.f++;
if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
rounded.f >>= 1;
rounded.e++;
}
}
*result = rounded.ToDouble();
return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
}
inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) {
const BigInteger dInt(decimals, length);
const int dExp = static_cast<int>(decimalPosition) - static_cast<int>(length) + exp;
Double a(approx);
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
if (cmp < 0)
return a.Value(); // within half ULP
else if (cmp == 0) {
// Round towards even
if (a.Significand() & 1)
return a.NextPositiveDouble();
else
return a.Value();
}
else // adjustment
return a.NextPositiveDouble();
}
inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) {
RAPIDJSON_ASSERT(d >= 0.0);
RAPIDJSON_ASSERT(length >= 1);
double result;
if (StrtodFast(d, p, &result))
return result;
// Trim leading zeros
while (*decimals == '0' && length > 1) {
length--;
decimals++;
decimalPosition--;
}
// Trim trailing zeros
while (decimals[length - 1] == '0' && length > 1) {
length--;
decimalPosition--;
exp++;
}
// Trim right-most digits
const int kMaxDecimalDigit = 780;
if (static_cast<int>(length) > kMaxDecimalDigit) {
int delta = (static_cast<int>(length) - kMaxDecimalDigit);
exp += delta;
decimalPosition -= static_cast<unsigned>(delta);
length = kMaxDecimalDigit;
}
// If too small, underflow to zero
if (int(length) + exp < -324)
return 0.0;
if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result))
return result;
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
return StrtodBigInteger(result, decimals, length, decimalPosition, exp);
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_STRTOD_

View File

@@ -0,0 +1,46 @@
// Tencent is pleased to support the open source community by making RapidJSON available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
//
// Licensed under the MIT License (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef RAPIDJSON_INTERNAL_SWAP_H_
#define RAPIDJSON_INTERNAL_SWAP_H_
#include "../rapidjson.h"
#if defined(__clang__)
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(c++98-compat)
#endif
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
//! Custom swap() to avoid dependency on C++ <algorithm> header
/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only.
\note This has the same semantics as std::swap().
*/
template <typename T>
inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT {
T tmp = a;
a = b;
b = tmp;
}
} // namespace internal
RAPIDJSON_NAMESPACE_END
#if defined(__clang__)
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_INTERNAL_SWAP_H_

Some files were not shown because too many files have changed in this diff Show More