1er commit
This commit is contained in:
51
include/game/BaseGame.h
Normal file
51
include/game/BaseGame.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/Team.h"
|
||||
#include "game/World.h"
|
||||
#include "game/Player.h"
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
enum class GameState : std::uint8_t{
|
||||
Lobby,
|
||||
Game,
|
||||
EndGame
|
||||
};
|
||||
|
||||
typedef std::map<std::uint8_t, Player> PlayerList;
|
||||
|
||||
class Game{
|
||||
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:
|
||||
Game(World* world);
|
||||
virtual ~Game();
|
||||
|
||||
virtual void tick(std::uint64_t delta);
|
||||
|
||||
Team& getRedTeam(){ return m_Teams[(std::uint8_t)TeamColor::Red]; }
|
||||
const Team& getRedTeam() const{ return m_Teams[(std::uint8_t)TeamColor::Red]; }
|
||||
|
||||
Team& getBlueTeam(){ return m_Teams[(std::uint8_t)TeamColor::Blue]; }
|
||||
const Team& getBlueTeam() const{ return m_Teams[(std::uint8_t)TeamColor::Blue]; }
|
||||
|
||||
Team& getTeam(TeamColor team){ return m_Teams[(std::uint8_t)team]; }
|
||||
const Team& getTeam(TeamColor team) const{ return m_Teams[(std::uint8_t)team]; }
|
||||
|
||||
GameState getGameState() const{ return m_GameState; }
|
||||
void setGameState(GameState gameState){ m_GameState = gameState; };
|
||||
|
||||
const World* getWorld() const{ return m_World; }
|
||||
World* getWorld(){ return m_World; }
|
||||
|
||||
const PlayerList& getPlayers() const{ return m_Players; }
|
||||
PlayerList& getPlayers(){ return m_Players; }
|
||||
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
36
include/game/Connexion.h
Normal file
36
include/game/Connexion.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "network/TCPSocket.h"
|
||||
#include "protocol/PacketHandler.h"
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
#include "game/Player.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
class Connexion : public protocol::PacketHandler{
|
||||
protected:
|
||||
protocol::PacketDispatcher m_Dispatcher;
|
||||
private:
|
||||
network::TCPSocket m_Socket;
|
||||
public:
|
||||
Connexion();
|
||||
Connexion(Connexion&& move);
|
||||
Connexion(protocol::PacketDispatcher* dispatcher);
|
||||
Connexion(protocol::PacketDispatcher* dispatcher, network::TCPSocket& socket);
|
||||
virtual ~Connexion();
|
||||
|
||||
virtual bool updateSocket();
|
||||
void closeConnection();
|
||||
|
||||
bool connect(const std::string& address, std::uint16_t port);
|
||||
|
||||
network::Socket::Status getSocketStatus() const{return m_Socket.GetStatus();}
|
||||
|
||||
void sendPacket(protocol::Packet* packet);
|
||||
|
||||
REMOVE_COPY(Connexion);
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
25
include/game/GameManager.h
Normal file
25
include/game/GameManager.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* GameManager.h
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#ifndef GAME_GAMEMANAGER_H_
|
||||
#define GAME_GAMEMANAGER_H_
|
||||
|
||||
namespace GameManager{
|
||||
|
||||
void render();
|
||||
void init();
|
||||
void destroy();
|
||||
void tick();
|
||||
|
||||
void startServer();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* GAME_GAMEMANAGER_H_ */
|
||||
139
include/game/Mobs.h
Normal file
139
include/game/Mobs.h
Normal file
@@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include "Towers.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
enum class Direction : std::uint8_t{
|
||||
PositiveX = 1 << 0,
|
||||
NegativeX = 1 << 1,
|
||||
PositiveY = 1 << 2,
|
||||
NegativeY = 1 << 3,
|
||||
};
|
||||
|
||||
enum class EffectType : std::uint8_t{
|
||||
Slowness = 0,
|
||||
Stun,
|
||||
Fire,
|
||||
Poison,
|
||||
Heal,
|
||||
};
|
||||
|
||||
enum class MobType : std::uint8_t{
|
||||
Zombie = 0,
|
||||
Spider,
|
||||
Skeleton,
|
||||
Pigman,
|
||||
Creeper,
|
||||
Silverfish,
|
||||
Blaze,
|
||||
Witch,
|
||||
Slime,
|
||||
Giant
|
||||
};
|
||||
|
||||
typedef std::uint8_t PlayerID;
|
||||
typedef std::uint32_t MobID;
|
||||
typedef std::uint8_t MobLevel;
|
||||
typedef std::vector<TowerType> TowerImmunities;
|
||||
typedef std::vector<EffectType> EffectImmunities;
|
||||
|
||||
class MobStats{
|
||||
private:
|
||||
float m_Damage;
|
||||
float m_Speed;
|
||||
std::uint16_t m_MoneyCost;
|
||||
std::uint16_t m_ExpCost;
|
||||
std::uint16_t m_MaxLife;
|
||||
std::uint16_t m_ExpReward;
|
||||
public:
|
||||
MobStats(float damage, float speed, std::uint16_t moneyCost,
|
||||
std::uint16_t expCost, std::uint16_t maxLife,
|
||||
std::uint16_t expReward): m_Damage(damage), m_Speed(speed),
|
||||
m_MoneyCost(moneyCost), m_ExpCost(expCost), m_MaxLife(maxLife),
|
||||
m_ExpReward(expReward){
|
||||
}
|
||||
|
||||
float getDamage() const{ return m_Damage; }
|
||||
float getMovementSpeed() const{ return m_Speed; }
|
||||
std::uint16_t getMoneyCost() const{ return m_MoneyCost; }
|
||||
std::uint16_t getExpCost() const{ return m_ExpCost; }
|
||||
std::uint16_t getExpReward() const{ return m_ExpReward; }
|
||||
std::uint16_t getMaxLife() const{ return m_MaxLife; }
|
||||
};
|
||||
|
||||
const MobStats* getMobStats(MobType type, std::uint8_t level);
|
||||
const TowerImmunities& getMobTowerImmunities(MobType type, std::uint8_t level);
|
||||
const EffectImmunities& getMobEffectImmunities(MobType type, std::uint8_t level);
|
||||
|
||||
class Mob{
|
||||
protected:
|
||||
float m_Health;
|
||||
private:
|
||||
MobID m_ID;
|
||||
PlayerID m_Sender;
|
||||
MobLevel m_Level;
|
||||
Direction m_Direction;
|
||||
|
||||
float m_X = 0, m_Y = 0;
|
||||
public:
|
||||
Mob(MobID id, MobLevel level, PlayerID sender): m_Sender(sender), m_Level(level){
|
||||
|
||||
}
|
||||
|
||||
virtual MobType getType() const = 0;
|
||||
|
||||
virtual void tick(std::uint64_t delta){}
|
||||
|
||||
const TowerImmunities& getTowerImmunities() const{ return getMobTowerImmunities(getType(), m_Level); }
|
||||
const EffectImmunities& getEffectImmunities() const{ return getMobEffectImmunities(getType(), m_Level); }
|
||||
PlayerID getSender() const{ return m_Sender; }
|
||||
MobLevel getLevel() const{ return m_Level; }
|
||||
const MobStats* getStats() const{ return getMobStats(getType(), m_Level); }
|
||||
float getHealth() const{ return m_Health; }
|
||||
bool isAlive() const{ return m_Health > 0; }
|
||||
|
||||
void damage(float dmg){ m_Health -= dmg; }
|
||||
void heal(float heal){ m_Health = std::min((float)getStats()->getMaxLife(), m_Health + heal); }
|
||||
|
||||
float getX() const{ return m_X; }
|
||||
float setX(float x){ m_X = x; }
|
||||
|
||||
float getY() const{ return m_Y; }
|
||||
float setY(float y){ m_Y = y; }
|
||||
|
||||
Direction getDirection() const{ return m_Direction; }
|
||||
void setDirection(Direction dir){ m_Direction = dir; }
|
||||
protected:
|
||||
void initHealth(){m_Health = (float)getStats()->getMaxLife();}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Mob> MobPtr;
|
||||
|
||||
class Zombie : public Mob{
|
||||
public:
|
||||
Zombie(MobID id, std::uint8_t level, PlayerID sender): Mob(id, level, sender){initHealth();}
|
||||
|
||||
virtual MobType getType() const{ return MobType::Zombie; }
|
||||
};
|
||||
|
||||
class Spider : public Mob{
|
||||
public:
|
||||
Spider(MobID id, std::uint8_t level, PlayerID sender): Mob(id, level, sender){initHealth();}
|
||||
|
||||
virtual MobType getType() const{ return MobType::Spider; }
|
||||
};
|
||||
|
||||
namespace MobFactory{
|
||||
MobPtr createMob(MobID id, MobType type, std::uint8_t level, PlayerID sender);
|
||||
}
|
||||
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
|
||||
40
include/game/Player.h
Normal file
40
include/game/Player.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "game/Team.h"
|
||||
|
||||
namespace td{
|
||||
namespace game{
|
||||
|
||||
class Player{
|
||||
private:
|
||||
game::TeamColor m_TeamColor = game::TeamColor::None;
|
||||
|
||||
std::uint32_t m_Gold = 0;
|
||||
std::int32_t m_EXP;
|
||||
std::string m_Name;
|
||||
std::uint8_t m_ID;
|
||||
|
||||
std::uint8_t m_GoldPerSecond = 5;
|
||||
public:
|
||||
Player(std::uint8_t id = 0) : m_ID(id){}
|
||||
|
||||
const std::string& getName() const{return m_Name;}
|
||||
void setName(const std::string& name){m_Name = name;}
|
||||
|
||||
game::TeamColor getTeamColor() const{return m_TeamColor;}
|
||||
void setTeamColor(game::TeamColor teamColor){m_TeamColor = teamColor;}
|
||||
|
||||
std::uint8_t getGoldPerSecond() const{ return m_GoldPerSecond;}
|
||||
void setGoldPerSecond(std::uint8_t goldPerSecond){m_GoldPerSecond = goldPerSecond;}
|
||||
|
||||
std::uint32_t getGold() const{ return m_Gold;}
|
||||
void setGold(std::uint32_t gold){m_Gold = gold;}
|
||||
|
||||
std::uint8_t getID() const{return m_ID;}
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
34
include/game/Team.h
Normal file
34
include/game/Team.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
class Player;
|
||||
|
||||
enum class TeamColor : std::int8_t{
|
||||
None = -1,
|
||||
Red,
|
||||
Blue
|
||||
};
|
||||
|
||||
class Team{
|
||||
private:
|
||||
std::vector<Player*> m_Players;
|
||||
TeamColor m_Color;
|
||||
public:
|
||||
Team(TeamColor color);
|
||||
|
||||
void addPlayer(Player* newPlayer);
|
||||
void removePlayer(const Player* player);
|
||||
|
||||
TeamColor getColor() const;
|
||||
|
||||
std::uint8_t getPlayerCount() const;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
212
include/game/Towers.h
Normal file
212
include/game/Towers.h
Normal file
@@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "misc/Time.h"
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
enum class TowerType : std::uint8_t{
|
||||
Archer = 0,
|
||||
Ice,
|
||||
Sorcerer,
|
||||
Zeus,
|
||||
Mage,
|
||||
Artillery,
|
||||
Quake,
|
||||
Poison,
|
||||
|
||||
Leach,
|
||||
Turret,
|
||||
Necromancer
|
||||
};
|
||||
|
||||
enum class TowerSize : bool{
|
||||
Little = 0, // 3x3
|
||||
Big, // 5x5
|
||||
};
|
||||
|
||||
enum class TowerPath : std::uint8_t{
|
||||
Top = 1, // Base path
|
||||
Bottom
|
||||
};
|
||||
|
||||
class TowerStats{
|
||||
private:
|
||||
float m_Rate;
|
||||
float m_Damage;
|
||||
std::uint8_t m_Range;
|
||||
public:
|
||||
TowerStats(float rate, float damage, std::uint8_t range): m_Rate(rate), m_Damage(damage),
|
||||
m_Range(range){
|
||||
}
|
||||
|
||||
float getDamageRate() const{ return m_Rate; }
|
||||
float getDamage() const{ return m_Damage; }
|
||||
std::uint8_t getRange() const{ return m_Range; }
|
||||
};
|
||||
|
||||
class TowerLevel{
|
||||
private:
|
||||
// 1, 2, 3, 4
|
||||
std::uint8_t m_Level : 3;
|
||||
// 0 : base path 1 : top path (if there is bottom path) 2 : bottom path (if there is top path)
|
||||
TowerPath m_Path : 1;
|
||||
public:
|
||||
TowerLevel(): m_Level(1), m_Path(TowerPath::Top){}
|
||||
TowerLevel(std::uint8_t level, TowerPath path): m_Level(level), m_Path(path){}
|
||||
|
||||
std::uint8_t getLevel() const{ return m_Level; }
|
||||
TowerPath getPath() const{ return m_Path; }
|
||||
|
||||
void setLevel(std::uint8_t level){ m_Level = level; }
|
||||
void setPath(TowerPath path){ m_Path = path; }
|
||||
|
||||
// operator to sort maps
|
||||
friend bool operator<(const TowerLevel& level, const TowerLevel& other){
|
||||
return level.getLevel() * (std::uint8_t)level.getPath() <
|
||||
other.getLevel() * (std::uint8_t)other.getPath();
|
||||
}
|
||||
};
|
||||
|
||||
const TowerStats* getTowerStats(TowerType type, TowerLevel level);
|
||||
|
||||
class Tower{
|
||||
private:
|
||||
std::uint16_t m_X, m_Y;
|
||||
TowerLevel m_Level{};
|
||||
protected:
|
||||
utils::Timer m_Timer;
|
||||
public: // converting seconds to millis
|
||||
Tower(std::uint16_t x, std::uint16_t y): m_X(x), m_Y(y), m_Timer(getStats()->getDamageRate() * 1000){}
|
||||
|
||||
virtual TowerType getType() const = 0;
|
||||
virtual TowerSize getSize() const = 0;
|
||||
virtual void tick(std::uint64_t delta) = 0;
|
||||
|
||||
void upgrade(std::uint8_t level, TowerPath path){
|
||||
m_Level.setLevel(level);
|
||||
m_Level.setPath(path);
|
||||
}
|
||||
|
||||
std::uint16_t getX() const{ return m_X; }
|
||||
std::uint16_t getY() const{ return m_Y; }
|
||||
const TowerLevel& getLevel() const{ return m_Level; }
|
||||
const TowerStats* getStats() const{ return getTowerStats(getType(), m_Level); }
|
||||
|
||||
};
|
||||
|
||||
// ---------- Little Towers ----------
|
||||
|
||||
class LittleTower : public Tower{
|
||||
public:
|
||||
LittleTower(std::uint16_t x, std::uint16_t y): Tower(x, y){}
|
||||
|
||||
virtual TowerSize getSize() const{ return TowerSize::Little; }
|
||||
|
||||
virtual TowerType getType() const = 0;
|
||||
virtual void tick(std::uint64_t delta) = 0;
|
||||
};
|
||||
|
||||
class ArcherTower : public LittleTower{
|
||||
public:
|
||||
ArcherTower(std::uint16_t x, std::uint16_t y): LittleTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Archer; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class IceTower : public LittleTower{
|
||||
public:
|
||||
IceTower(std::uint16_t x, std::uint16_t y): LittleTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Ice; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class MageTower : public LittleTower{
|
||||
public:
|
||||
MageTower(std::uint16_t x, std::uint16_t y): LittleTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Mage; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class PoisonTower : public LittleTower{
|
||||
public:
|
||||
PoisonTower(std::uint16_t x, std::uint16_t y): LittleTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Poison; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class QuakeTower : public LittleTower{
|
||||
public:
|
||||
QuakeTower(std::uint16_t x, std::uint16_t y): LittleTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Quake; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class ArtilleryTower : public LittleTower{
|
||||
public:
|
||||
ArtilleryTower(std::uint16_t x, std::uint16_t y): LittleTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Artillery; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class SorcererTower : public LittleTower{
|
||||
public:
|
||||
SorcererTower(std::uint16_t x, std::uint16_t y): LittleTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Sorcerer; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class ZeusTower : public LittleTower{
|
||||
public:
|
||||
ZeusTower(std::uint16_t x, std::uint16_t y): LittleTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Zeus; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
// ---------- Big Towers ----------
|
||||
|
||||
class BigTower : public Tower{
|
||||
public:
|
||||
BigTower(std::uint16_t x, std::uint16_t y): Tower(x, y){}
|
||||
|
||||
virtual TowerSize getSize() const{ return TowerSize::Big; }
|
||||
|
||||
virtual TowerType getType() const = 0;
|
||||
virtual void tick(std::uint64_t delta) = 0;
|
||||
};
|
||||
|
||||
class TurretTower : public BigTower{
|
||||
public:
|
||||
TurretTower(std::uint16_t x, std::uint16_t y): BigTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Turret; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class NecromancerTower : public BigTower{
|
||||
public:
|
||||
NecromancerTower(std::uint16_t x, std::uint16_t y): BigTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Necromancer; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
class LeachTower : public BigTower{
|
||||
public:
|
||||
LeachTower(std::uint16_t x, std::uint16_t y): BigTower(x, y){}
|
||||
|
||||
virtual TowerType getType() const{ return TowerType::Leach; }
|
||||
virtual void tick(std::uint64_t delta);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
190
include/game/World.h
Normal file
190
include/game/World.h
Normal file
@@ -0,0 +1,190 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include "Mobs.h"
|
||||
#include "Team.h"
|
||||
|
||||
namespace td{
|
||||
namespace game{
|
||||
typedef std::pair<std::int16_t, std::int16_t> ChunkCoord;
|
||||
}
|
||||
}
|
||||
namespace std{
|
||||
template <>
|
||||
struct hash<td::game::ChunkCoord>{
|
||||
std::size_t operator()(const td::game::ChunkCoord& key) const{
|
||||
// Compute individual hash values for first,
|
||||
// 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 protocol {
|
||||
|
||||
class WorldBeginDataPacket;
|
||||
class WorldDataPacket;
|
||||
|
||||
}
|
||||
|
||||
namespace game {
|
||||
|
||||
class Game;
|
||||
|
||||
enum class TileType : std::uint8_t{
|
||||
None = 0,
|
||||
Tower,
|
||||
Walk,
|
||||
Decoration,
|
||||
/*Heal,
|
||||
Lava,
|
||||
Bedrock,
|
||||
Freeze,
|
||||
Ice,*/
|
||||
};
|
||||
|
||||
struct Color{
|
||||
std::uint8_t r, g, b;
|
||||
};
|
||||
|
||||
struct Tile{
|
||||
virtual TileType getType() const = 0;
|
||||
};
|
||||
|
||||
struct TowerTile : Tile{
|
||||
std::uint8_t color_palette_ref;
|
||||
TeamColor team_owner;
|
||||
|
||||
virtual TileType getType() const{ return TileType::Tower; }
|
||||
};
|
||||
|
||||
struct WalkableTile : Tile{
|
||||
Direction direction;
|
||||
|
||||
virtual TileType getType() const{ return TileType::Walk; }
|
||||
};
|
||||
|
||||
struct DecorationTile : Tile{
|
||||
std::uint16_t color_palette_ref;
|
||||
|
||||
virtual TileType getType() const{ return TileType::Decoration; }
|
||||
};
|
||||
|
||||
struct Spawn{
|
||||
Direction direction;
|
||||
std::int32_t x, y;
|
||||
};
|
||||
|
||||
struct TeamCastle{
|
||||
std::int32_t x, y;
|
||||
std::uint16_t life = 1000;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Tile> TilePtr;
|
||||
typedef std::vector<std::uint16_t> ChunkPalette;
|
||||
|
||||
typedef std::shared_ptr<WalkableTile> WalkableTilePtr;
|
||||
|
||||
typedef std::array<std::uint16_t, 32 * 32> ChunkData;
|
||||
typedef std::uint32_t TileIndex;
|
||||
|
||||
//32 x 32 area
|
||||
struct Chunk{
|
||||
enum{ ChunkWidth = 32, ChunkHeight = 32, ChunkSize = ChunkWidth * ChunkHeight };
|
||||
// stores index of tile palette
|
||||
ChunkData tiles{0};
|
||||
ChunkPalette palette;
|
||||
|
||||
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::array<Color, 2> TowerTileColorPalette;
|
||||
|
||||
typedef std::vector<TilePtr> TilePalette;
|
||||
|
||||
typedef std::vector<MobPtr> MobList;
|
||||
|
||||
typedef std::array<Color, 2> SpawnColorPalette;
|
||||
|
||||
|
||||
class World{
|
||||
protected:
|
||||
TowerTileColorPalette m_TowerPlacePalette;
|
||||
Color m_WalkablePalette;
|
||||
std::vector<Color> m_DecorationPalette;
|
||||
|
||||
std::unordered_map<ChunkCoord, ChunkPtr> m_Chunks;
|
||||
|
||||
SpawnColorPalette m_SpawnColorPalette;
|
||||
|
||||
Spawn m_Spawns[2];
|
||||
TeamCastle m_Castles[2];
|
||||
|
||||
TilePalette m_TilePalette;
|
||||
|
||||
MobList m_Mobs;
|
||||
|
||||
Game* m_Game;
|
||||
public:
|
||||
World(Game* game);
|
||||
|
||||
bool loadMap(const protocol::WorldBeginDataPacket* worldHeader);
|
||||
bool loadMap(const protocol::WorldDataPacket* worldData);
|
||||
|
||||
bool loadMapFromFile(const std::string& fileName);
|
||||
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);
|
||||
|
||||
TilePtr getTile(std::int32_t x, std::int32_t y);
|
||||
|
||||
const TowerTileColorPalette& getTowerTileColorPalette() const{ return m_TowerPlacePalette; }
|
||||
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);
|
||||
}
|
||||
|
||||
const std::unordered_map<ChunkCoord, ChunkPtr>& getChunks() const{ return m_Chunks; }
|
||||
|
||||
const Spawn& getRedSpawn() const{ return m_Spawns[(std::size_t) TeamColor::Red]; }
|
||||
const Spawn& getBlueSpawn() const{ return m_Spawns[(std::size_t) TeamColor::Blue]; }
|
||||
|
||||
const Spawn& getSpawn(TeamColor color) const{ return m_Spawns[(std::size_t) color]; }
|
||||
|
||||
const Color& getSpawnColor(TeamColor color) const{ return m_SpawnColorPalette[(std::size_t) color]; }
|
||||
const SpawnColorPalette& getSpawnColors() const{ return m_SpawnColorPalette; }
|
||||
|
||||
const TeamCastle& getRedCastle() const{ return m_Castles[(std::size_t) TeamColor::Red]; }
|
||||
const TeamCastle& getBlueCastle() const{ return m_Castles[(std::size_t) TeamColor::Blue]; }
|
||||
|
||||
const MobList& getMobList() const{ return m_Mobs; }
|
||||
MobList& getMobList(){ return m_Mobs; }
|
||||
|
||||
const Color& getTileColor(TilePtr tile) const;
|
||||
private:
|
||||
void moveMobs(std::uint64_t delta);
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
34
include/game/client/Client.h
Normal file
34
include/game/client/Client.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "ClientConnexion.h"
|
||||
#include "ClientGame.h"
|
||||
|
||||
#include "game/Team.h"
|
||||
#include "game/Player.h"
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
class Client{
|
||||
private:
|
||||
ClientConnexion m_Connexion;
|
||||
ClientGame m_Game{m_Connexion.GetDispatcher()};
|
||||
bool m_Connected;
|
||||
public:
|
||||
Client(){}
|
||||
|
||||
const ClientGame& getGame() const{ return m_Game; }
|
||||
const ClientConnexion& getConnexion() const{ return m_Connexion; }
|
||||
|
||||
void tick(std::uint64_t delta);
|
||||
|
||||
void connect(const std::string& address, std::uint16_t port);
|
||||
void closeConnection();
|
||||
|
||||
bool isConnected() const{ return m_Connexion.getSocketStatus() == network::Socket::Connected; }
|
||||
|
||||
void selectTeam(game::TeamColor team);
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
37
include/game/client/ClientConnexion.h
Normal file
37
include/game/client/ClientConnexion.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "protocol/PacketHandler.h"
|
||||
#include "network/TCPSocket.h"
|
||||
#include "game/Connexion.h"
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
class ClientConnexion : public protocol::Connexion{
|
||||
private:
|
||||
std::uint8_t m_ConnectionID;
|
||||
std::string m_DisconnectReason;
|
||||
float m_ServerTPS;
|
||||
int m_Ping = 0;
|
||||
public:
|
||||
ClientConnexion();
|
||||
|
||||
virtual bool updateSocket();
|
||||
|
||||
virtual void HandlePacket(protocol::KeepAlivePacket* packet);
|
||||
virtual void HandlePacket(protocol::ConnexionInfoPacket* packet);
|
||||
virtual void HandlePacket(protocol::DisconnectPacket* packet);
|
||||
virtual void HandlePacket(protocol::ServerTpsPacket* packet);
|
||||
|
||||
const std::string& getDisconnectReason() const{ return m_DisconnectReason; }
|
||||
float getServerTPS() const{ return m_ServerTPS; }
|
||||
int getServerPing() const {return m_Ping;}
|
||||
|
||||
REMOVE_COPY(ClientConnexion);
|
||||
private:
|
||||
void registerHandlers();
|
||||
void login();
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
36
include/game/client/ClientGame.h
Normal file
36
include/game/client/ClientGame.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/BaseGame.h"
|
||||
#include "protocol/PacketHandler.h"
|
||||
#include "WorldClient.h"
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
class ClientGame : public protocol::PacketHandler, public game::Game{
|
||||
private:
|
||||
std::uint8_t m_ConnexionID;
|
||||
std::uint32_t m_LobbyTime = 0;
|
||||
game::Player* m_Player = nullptr;
|
||||
client::WorldClient m_WorldClient{this};
|
||||
public:
|
||||
ClientGame(protocol::PacketDispatcher* dispatcher);
|
||||
virtual ~ClientGame();
|
||||
|
||||
virtual void tick(std::uint64_t delta);
|
||||
|
||||
std::uint32_t getLobbyTime() const{return m_LobbyTime;}
|
||||
const game::Player* getPlayer() const{return m_Player;}
|
||||
|
||||
virtual void HandlePacket(protocol::ConnexionInfoPacket* packet);
|
||||
virtual void HandlePacket(protocol::PlayerJoinPacket* packet);
|
||||
virtual void HandlePacket(protocol::PlayerLeavePacket* packet);
|
||||
virtual void HandlePacket(protocol::PlayerListPacket* packet);
|
||||
virtual void HandlePacket(protocol::UpdatePlayerTeamPacket* packet);
|
||||
virtual void HandlePacket(protocol::UpdateGameStatePacket* packet);
|
||||
virtual void HandlePacket(protocol::UpdateLobbyTimePacket* packet);
|
||||
virtual void HandlePacket(protocol::UpdateMoneyPacket* packet);
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
23
include/game/client/WorldClient.h
Normal file
23
include/game/client/WorldClient.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/World.h"
|
||||
#include "protocol/PacketHandler.h"
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
class ClientGame;
|
||||
|
||||
class WorldClient : public game::World, public protocol::PacketHandler{
|
||||
private:
|
||||
ClientGame* m_Game;
|
||||
public:
|
||||
WorldClient(ClientGame* game);
|
||||
|
||||
virtual void HandlePacket(protocol::WorldBeginDataPacket* packet);
|
||||
virtual void HandlePacket(protocol::WorldDataPacket* packet);
|
||||
virtual void HandlePacket(protocol::SpawnMobPacket* packet);
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
31
include/game/server/Lobby.h
Normal file
31
include/game/server/Lobby.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "misc/Time.h"
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
class Server;
|
||||
|
||||
class Lobby{
|
||||
private:
|
||||
Server* m_Server;
|
||||
bool m_GameStarted = false;
|
||||
std::uint64_t m_StartTimerTime = 0;
|
||||
std::vector<std::uint8_t> m_Players;
|
||||
utils::Timer m_Timer;
|
||||
public:
|
||||
Lobby(Server* server);
|
||||
|
||||
void OnPlayerJoin(std::uint8_t playerID);
|
||||
void OnPlayerLeave(std::uint8_t playerID);
|
||||
|
||||
void sendTimeRemaining();
|
||||
|
||||
void tick();
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
96
include/game/server/Server.h
Normal file
96
include/game/server/Server.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
|
||||
#include "network/TCPListener.h"
|
||||
#include "protocol/Protocol.h"
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
#include "protocol/PacketHandler.h"
|
||||
#include "ServerGame.h"
|
||||
#include "ServerConnexion.h"
|
||||
#include "Lobby.h"
|
||||
|
||||
//#define LOBBY_WAITING_TIME 2 * 60 * 1000
|
||||
#define LOBBY_WAITING_TIME 5 * 1000
|
||||
|
||||
#define SERVER_TPS 20
|
||||
#define SERVER_TICK 1000 / SERVER_TPS
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
typedef std::map<std::uint8_t, ServerConnexion> ConnexionMap;
|
||||
|
||||
class TickCounter{
|
||||
private:
|
||||
float m_TPS;
|
||||
std::uint64_t m_LastTPSTime;
|
||||
std::uint8_t m_TickCount;
|
||||
public:
|
||||
TickCounter(){}
|
||||
|
||||
void reset(){
|
||||
m_TPS = SERVER_TPS;
|
||||
m_LastTPSTime = utils::getTime();
|
||||
m_TickCount = 0;
|
||||
}
|
||||
|
||||
bool update(){ // return true when tps is updated
|
||||
m_TickCount++;
|
||||
if (m_TickCount >= SERVER_TPS){
|
||||
std::uint64_t timeElapsedSinceLast20Ticks = td::utils::getTime() - m_LastTPSTime;
|
||||
m_TPS = (float)SERVER_TPS / (float)(timeElapsedSinceLast20Ticks / 1000.0f);
|
||||
m_TickCount = 0;
|
||||
m_LastTPSTime = td::utils::getTime();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float getTPS() const{ return m_TPS; }
|
||||
};
|
||||
|
||||
class Server{
|
||||
private:
|
||||
network::TCPListener m_Listener;
|
||||
ConnexionMap m_Connections;
|
||||
ServerGame m_Game{this};
|
||||
Lobby m_Lobby{this};
|
||||
TickCounter m_TickCounter;
|
||||
public:
|
||||
Server(const std::string& worldFilePath);
|
||||
virtual ~Server(){}
|
||||
|
||||
bool start(std::uint16_t port);
|
||||
void tick(std::uint64_t delta);
|
||||
void stop();
|
||||
|
||||
void lauchGame();
|
||||
|
||||
void removeConnexion(std::uint8_t connexionID);
|
||||
|
||||
void broadcastPacket(protocol::Packet* packet);
|
||||
|
||||
float getTPS() const{ return m_TickCounter.getTPS(); }
|
||||
|
||||
const ServerGame& getGame() const{ return m_Game; }
|
||||
ServerGame& getGame() { return m_Game; }
|
||||
|
||||
const Lobby& getLobby() const{ return m_Lobby; }
|
||||
const ConnexionMap& getConnexions() const{ return m_Connections; }
|
||||
ConnexionMap& getConnexions(){ return m_Connections; }
|
||||
|
||||
const game::PlayerList& getPlayers() const{ return m_Game.getPlayers(); }
|
||||
game::PlayerList& getPlayers(){ return m_Game.getPlayers(); }
|
||||
|
||||
private:
|
||||
void accept();
|
||||
void updateSockets();
|
||||
|
||||
void OnPlayerJoin(std::uint8_t id);
|
||||
void OnPlayerLeave(std::uint8_t id);
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
56
include/game/server/ServerConnexion.h
Normal file
56
include/game/server/ServerConnexion.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "network/TCPSocket.h"
|
||||
#include "protocol/PacketHandler.h"
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
#include "game/Player.h"
|
||||
#include "game/Connexion.h"
|
||||
|
||||
namespace td{
|
||||
namespace server{
|
||||
|
||||
class Server;
|
||||
|
||||
struct KeepAlive
|
||||
{
|
||||
std::uint64_t keepAliveID = 0;
|
||||
std::uint64_t sendTime;
|
||||
bool recievedResponse = false;
|
||||
};
|
||||
|
||||
|
||||
class ServerConnexion : public protocol::Connexion{
|
||||
private:
|
||||
Server* m_Server = nullptr;
|
||||
std::uint8_t m_ID;
|
||||
KeepAlive m_KeepAlive;
|
||||
game::Player* m_Player;
|
||||
public:
|
||||
ServerConnexion();
|
||||
ServerConnexion(network::TCPSocket& socket, std::uint8_t id);
|
||||
ServerConnexion(ServerConnexion&& move);
|
||||
virtual ~ServerConnexion();
|
||||
|
||||
void setServer(Server* server);
|
||||
|
||||
virtual void HandlePacket(protocol::PlayerLoginPacket* packet);
|
||||
virtual void HandlePacket(protocol::KeepAlivePacket* packet);
|
||||
virtual void HandlePacket(protocol::SelectTeamPacket* packet);
|
||||
virtual void HandlePacket(protocol::DisconnectPacket* packet);
|
||||
|
||||
std::uint8_t getID() const{return m_ID;}
|
||||
const game::Player* getPlayer() const{return m_Player;}
|
||||
game::Player* getPlayer() {return m_Player;}
|
||||
|
||||
virtual bool updateSocket();
|
||||
|
||||
REMOVE_COPY(ServerConnexion);
|
||||
private:
|
||||
void registerHandlers();
|
||||
void checkKeepAlive();
|
||||
void sendKeepAlive();
|
||||
void initConnection();
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
29
include/game/server/ServerGame.h
Normal file
29
include/game/server/ServerGame.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/BaseGame.h"
|
||||
#include "misc/Time.h"
|
||||
#include "ServerWorld.h"
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
class Server;
|
||||
|
||||
class ServerGame : public game::Game{
|
||||
private:
|
||||
Server* m_Server;
|
||||
ServerWorld m_ServerWorld;
|
||||
utils::Timer m_GoldMineTimer{1000, std::bind(&ServerGame::updateGoldMines, this)};
|
||||
public:
|
||||
ServerGame(Server* server);
|
||||
~ServerGame(){}
|
||||
|
||||
virtual void tick(std::uint64_t delta);
|
||||
void startGame();
|
||||
private:
|
||||
void balanceTeams();
|
||||
void updateGoldMines();
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
21
include/game/server/ServerWorld.h
Normal file
21
include/game/server/ServerWorld.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/World.h"
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
class Server;
|
||||
class ServerGame;
|
||||
|
||||
class ServerWorld : public game::World{
|
||||
private:
|
||||
game::MobID m_CurrentMobID = 0;
|
||||
Server* m_Server;
|
||||
public:
|
||||
ServerWorld(Server* server, ServerGame* game);
|
||||
void spawnMobs(game::MobType type, std::uint8_t level, game::PlayerID sender, std::uint8_t count);
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
14
include/misc/Compression.h
Normal file
14
include/misc/Compression.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "network/DataBuffer.h"
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
DataBuffer Compress(const DataBuffer& buffer);
|
||||
DataBuffer Decompress(DataBuffer& buffer);
|
||||
DataBuffer Decompress(DataBuffer& buffer, std::size_t packetLength);
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
12
include/misc/Random.h
Normal file
12
include/misc/Random.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
void initRandomizer();
|
||||
std::uint64_t getRandomNumber(std::size_t max);
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
41
include/misc/Time.h
Normal file
41
include/misc/Time.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
std::uint64_t getTime();
|
||||
|
||||
|
||||
typedef std::function<void()> TimerExecFunction;
|
||||
|
||||
// utililty class to call function at regular period of time
|
||||
class Timer{
|
||||
private:
|
||||
std::uint64_t m_Interval;
|
||||
TimerExecFunction m_Function;
|
||||
|
||||
std::uint64_t m_LastTime = getTime();
|
||||
std::uint64_t m_InternalTime = 0;
|
||||
public:
|
||||
Timer() : m_Interval(0), m_Function(nullptr){}
|
||||
Timer(std::uint64_t interval) : m_Interval(interval), m_Function(nullptr){}
|
||||
Timer(std::uint64_t interval, TimerExecFunction callback) : m_Interval(interval), m_Function(callback){}
|
||||
|
||||
void update();
|
||||
void update(std::uint64_t delta);
|
||||
|
||||
void reset();
|
||||
|
||||
void setInterval(std::uint64_t newInterval){m_Interval = newInterval;}
|
||||
std::uint64_t getInterval() const{return m_Interval;}
|
||||
|
||||
void setCallbackFunction(TimerExecFunction newCallback){m_Function = newCallback;}
|
||||
TimerExecFunction getCallbackFunction() const{return m_Function;}
|
||||
};
|
||||
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
167
include/network/DataBuffer.h
Normal file
167
include/network/DataBuffer.h
Normal file
@@ -0,0 +1,167 @@
|
||||
#ifndef MCLIB_COMMON_DATA_BUFFER_H_
|
||||
#define MCLIB_COMMON_DATA_BUFFER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
namespace td{
|
||||
|
||||
class DataBuffer{
|
||||
private:
|
||||
typedef std::vector<std::uint8_t> Data;
|
||||
Data m_Buffer;
|
||||
std::size_t m_ReadOffset = 0;
|
||||
|
||||
public:
|
||||
typedef Data::iterator iterator;
|
||||
typedef Data::const_iterator const_iterator;
|
||||
typedef Data::reference reference;
|
||||
typedef Data::const_reference const_reference;
|
||||
|
||||
DataBuffer();
|
||||
DataBuffer(const DataBuffer& other);
|
||||
DataBuffer(const DataBuffer& other, std::size_t offset);
|
||||
DataBuffer(DataBuffer&& other);
|
||||
DataBuffer(const std::string& str);
|
||||
|
||||
DataBuffer& operator=(const DataBuffer& other);
|
||||
DataBuffer& operator=(DataBuffer&& other);
|
||||
|
||||
template <typename T>
|
||||
void Append(T data){
|
||||
std::size_t size = sizeof(data);
|
||||
std::size_t end_pos = m_Buffer.size();
|
||||
m_Buffer.resize(m_Buffer.size() + size);
|
||||
memcpy(&m_Buffer[end_pos], &data, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DataBuffer& operator<<(T data){
|
||||
// Switch to big endian
|
||||
//std::reverse((std::uint8_t*)&data, (std::uint8_t*)&data + sizeof(T));
|
||||
Append(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataBuffer& operator<<(std::string str){
|
||||
m_Buffer.insert(m_Buffer.end(), str.begin(), str.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataBuffer& operator<<(DataBuffer& data){
|
||||
m_Buffer.insert(m_Buffer.end(), data.begin(), data.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataBuffer& operator<<(const DataBuffer& data){
|
||||
m_Buffer.insert(m_Buffer.end(), data.begin(), data.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
DataBuffer& operator>>(T& data){
|
||||
assert(m_ReadOffset + sizeof(T) <= GetSize());
|
||||
data = *(T*)&m_Buffer[m_ReadOffset];
|
||||
//std::reverse((std::uint8_t*)&data, (std::uint8_t*)&data + sizeof(T));
|
||||
m_ReadOffset += sizeof(T);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataBuffer& operator>>(DataBuffer& data){
|
||||
data.Resize(GetSize() - m_ReadOffset);
|
||||
std::copy(m_Buffer.begin() + m_ReadOffset, m_Buffer.end(), data.begin());
|
||||
m_ReadOffset = m_Buffer.size();
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataBuffer& operator>>(std::string& str){
|
||||
std::size_t stringSize = strlen((const char*) m_Buffer.data() + m_ReadOffset) + 1; // including null character
|
||||
str.resize(stringSize);
|
||||
std::copy(m_Buffer.begin() + m_ReadOffset, m_Buffer.begin() + m_ReadOffset + stringSize, str.begin());
|
||||
m_ReadOffset += stringSize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ReadSome(char* buffer, std::size_t amount){
|
||||
assert(m_ReadOffset + amount <= GetSize());
|
||||
std::copy_n(m_Buffer.begin() + m_ReadOffset, amount, buffer);
|
||||
m_ReadOffset += amount;
|
||||
}
|
||||
|
||||
void ReadSome(std::uint8_t* buffer, std::size_t amount){
|
||||
assert(m_ReadOffset + amount <= GetSize());
|
||||
std::copy_n(m_Buffer.begin() + m_ReadOffset, amount, buffer);
|
||||
m_ReadOffset += amount;
|
||||
}
|
||||
|
||||
void ReadSome(DataBuffer& buffer, std::size_t amount){
|
||||
assert(m_ReadOffset + amount <= GetSize());
|
||||
buffer.Resize(amount);
|
||||
std::copy_n(m_Buffer.begin() + m_ReadOffset, amount, buffer.begin());
|
||||
m_ReadOffset += amount;
|
||||
}
|
||||
|
||||
void ReadSome(std::string& buffer, std::size_t amount){
|
||||
assert(m_ReadOffset + amount <= GetSize());
|
||||
buffer.resize(amount);
|
||||
std::copy_n(m_Buffer.begin() + m_ReadOffset, amount, buffer.begin());
|
||||
m_ReadOffset += amount;
|
||||
}
|
||||
|
||||
void Resize(std::size_t size){
|
||||
m_Buffer.resize(size);
|
||||
}
|
||||
|
||||
void Reserve(std::size_t amount){
|
||||
m_Buffer.reserve(amount);
|
||||
}
|
||||
|
||||
void erase(iterator it){
|
||||
m_Buffer.erase(it);
|
||||
}
|
||||
|
||||
void Clear(){
|
||||
m_Buffer.clear();
|
||||
m_ReadOffset = 0;
|
||||
}
|
||||
|
||||
bool IsFinished() const{
|
||||
return m_ReadOffset >= m_Buffer.size();
|
||||
}
|
||||
|
||||
std::uint8_t* data(){
|
||||
return m_Buffer.data();
|
||||
}
|
||||
|
||||
const std::uint8_t* data() const{
|
||||
return m_Buffer.data();
|
||||
}
|
||||
|
||||
std::size_t GetReadOffset() const{ return m_ReadOffset; }
|
||||
void SetReadOffset(std::size_t pos);
|
||||
|
||||
std::string ToString() const;
|
||||
std::size_t GetSize() const;
|
||||
bool IsEmpty() const;
|
||||
std::size_t GetRemaining() const;
|
||||
|
||||
iterator begin();
|
||||
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);
|
||||
|
||||
} // ns td
|
||||
|
||||
#endif
|
||||
56
include/network/IPAddress.h
Normal file
56
include/network/IPAddress.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef NETWORK_IPADDRESS_H_
|
||||
#define NETWORK_IPADDRESS_H_
|
||||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
/* IPv4 address */
|
||||
class IPAddress {
|
||||
private:
|
||||
std::uint32_t m_Address;
|
||||
bool m_Valid;
|
||||
|
||||
public:
|
||||
/* Create an invalid address */
|
||||
IPAddress() noexcept;
|
||||
|
||||
/* Initialize by string IP */
|
||||
IPAddress(const std::string& str);
|
||||
|
||||
/* Initialize by string IP */
|
||||
IPAddress(const std::wstring& str);
|
||||
|
||||
/* Initialize by octets */
|
||||
IPAddress(std::uint8_t octet1, std::uint8_t octet2, std::uint8_t octet3, std::uint8_t octet4) noexcept;
|
||||
|
||||
/* Get the specific octet. 1-4 */
|
||||
std::uint8_t GetOctet(std::uint8_t num) const;
|
||||
|
||||
/* Set the specific octet. 1-4 */
|
||||
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. */
|
||||
bool IsValid() const noexcept { return m_Valid; }
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
static IPAddress LocalAddress();
|
||||
|
||||
bool operator==(const IPAddress& right);
|
||||
bool operator!=(const IPAddress& right);
|
||||
bool operator==(bool b);
|
||||
};
|
||||
|
||||
typedef std::vector<IPAddress> IPAddresses;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const IPAddress& addr);
|
||||
std::wostream& operator<<(std::wostream& os, const IPAddress& addr);
|
||||
|
||||
} // ns network
|
||||
} // ns td
|
||||
|
||||
#endif
|
||||
32
include/network/Network.h
Normal file
32
include/network/Network.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef NETWORK_NETWORK_H_
|
||||
#define NETWORK_NETWORK_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "network/Socket.h"
|
||||
#include "network/IPAddress.h"
|
||||
#include "network/UDPSocket.h"
|
||||
#include "network/TCPSocket.h"
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
class Dns {
|
||||
public:
|
||||
static IPAddresses Resolve(const std::string& host);
|
||||
};
|
||||
|
||||
} // ns network
|
||||
} // ns td
|
||||
|
||||
#endif
|
||||
90
include/network/Socket.h
Normal file
90
include/network/Socket.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#ifndef NETWORK_SOCKET_H_
|
||||
#define NETWORK_SOCKET_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "network/DataBuffer.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
|
||||
#define closesocket close
|
||||
#endif
|
||||
|
||||
#ifndef INVALID_SOCKET
|
||||
#define INVALID_SOCKET -1
|
||||
#endif
|
||||
|
||||
#define REMOVE_COPY(className) \
|
||||
className(const className &) = delete;\
|
||||
className& operator=(const className &) = delete
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
class IPAddress;
|
||||
|
||||
typedef int SocketHandle;
|
||||
|
||||
class Socket {
|
||||
public:
|
||||
enum Status { Connected, Disconnected, Error };
|
||||
enum Type { TCP, UDP };
|
||||
|
||||
private:
|
||||
bool m_Blocking;
|
||||
Type m_Type;
|
||||
Status m_Status;
|
||||
|
||||
protected:
|
||||
SocketHandle m_Handle;
|
||||
|
||||
Socket(Type type);
|
||||
void SetStatus(Status status);
|
||||
|
||||
public:
|
||||
virtual ~Socket();
|
||||
|
||||
Socket(Socket&& rhs) = default;
|
||||
Socket& operator=(Socket&& rhs) = default;
|
||||
|
||||
bool SetBlocking(bool block);
|
||||
bool IsBlocking() const noexcept;
|
||||
|
||||
Type GetType() const noexcept;
|
||||
Status GetStatus() const noexcept;
|
||||
SocketHandle GetHandle() const noexcept;
|
||||
|
||||
bool Connect(const std::string& ip, std::uint16_t port);
|
||||
virtual bool Connect(const IPAddress& address, std::uint16_t port) = 0;
|
||||
|
||||
void Disconnect();
|
||||
|
||||
std::size_t Send(const std::string& data);
|
||||
std::size_t Send(DataBuffer& buffer);
|
||||
|
||||
virtual std::size_t Send(const uint8_t* data, std::size_t size) = 0;
|
||||
virtual DataBuffer Receive(std::size_t amount) = 0;
|
||||
|
||||
virtual std::size_t Receive(DataBuffer& buffer, std::size_t amount) = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Socket> SocketPtr;
|
||||
|
||||
} // ns network
|
||||
} // ns td
|
||||
|
||||
#endif
|
||||
|
||||
34
include/network/TCPListener.h
Normal file
34
include/network/TCPListener.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "TCPSocket.h"
|
||||
|
||||
namespace td{
|
||||
namespace network{
|
||||
|
||||
class TCPListener{
|
||||
int m_Handle;
|
||||
std::uint16_t m_Port;
|
||||
int m_MaxConnections;
|
||||
|
||||
public:
|
||||
TCPListener();
|
||||
~TCPListener();
|
||||
|
||||
bool listen(std::uint16_t port, int maxConnections);
|
||||
bool accept(TCPSocket& newSocket);
|
||||
|
||||
void destroy();
|
||||
bool close();
|
||||
bool setBlocking(bool blocking);
|
||||
|
||||
std::uint16_t getListeningPort() const{
|
||||
return m_Port;
|
||||
}
|
||||
int getMaximumConnections() const{
|
||||
return m_MaxConnections;
|
||||
}
|
||||
|
||||
REMOVE_COPY(TCPListener);
|
||||
};
|
||||
|
||||
} // namespace network
|
||||
} // namespace td
|
||||
39
include/network/TCPSocket.h
Normal file
39
include/network/TCPSocket.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef NETWORK_TCP_SOCKET_H_
|
||||
#define NETWORK_TCP_SOCKET_H_
|
||||
|
||||
#include "network/IPAddress.h"
|
||||
#include "network/Socket.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
class TCPListener;
|
||||
|
||||
class TCPSocket : public Socket {
|
||||
private:
|
||||
IPAddress m_RemoteIP;
|
||||
uint16_t m_Port;
|
||||
sockaddr_in m_RemoteAddr;
|
||||
|
||||
public:
|
||||
TCPSocket();
|
||||
TCPSocket(TCPSocket&& other);
|
||||
|
||||
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 DataBuffer Receive(std::size_t amount);
|
||||
virtual std::size_t Receive(DataBuffer& buffer, std::size_t amount);
|
||||
|
||||
REMOVE_COPY(TCPSocket);
|
||||
|
||||
friend class TCPListener;
|
||||
};
|
||||
|
||||
void SendPacket(const DataBuffer& data, network::TCPSocket& socket);
|
||||
|
||||
} // ns network
|
||||
} // ns td
|
||||
|
||||
#endif
|
||||
29
include/network/UDPSocket.h
Normal file
29
include/network/UDPSocket.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef NETWORK_UDP_SOCKET_H_
|
||||
#define NETWORK_UDP_SOCKET_H_
|
||||
|
||||
#include "network/IPAddress.h"
|
||||
#include "network/Socket.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
class UDPSocket : public Socket {
|
||||
private:
|
||||
IPAddress m_RemoteIP;
|
||||
uint16_t m_Port;
|
||||
sockaddr_in m_RemoteAddr;
|
||||
|
||||
public:
|
||||
UDPSocket();
|
||||
|
||||
bool Connect(const IPAddress& address, std::uint16_t port);
|
||||
std::size_t Send(const std::uint8_t* data, std::size_t size);
|
||||
DataBuffer Receive(std::size_t amount);
|
||||
};
|
||||
|
||||
} // ns network
|
||||
} // ns td
|
||||
|
||||
#endif
|
||||
33
include/protocol/PacketDispatcher.h
Normal file
33
include/protocol/PacketDispatcher.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "protocol/Protocol.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
class PacketHandler;
|
||||
|
||||
class PacketDispatcher {
|
||||
private:
|
||||
std::map<PacketType, std::vector<PacketHandler*>> m_Handlers;
|
||||
|
||||
public:
|
||||
PacketDispatcher() = default;
|
||||
|
||||
PacketDispatcher(const PacketDispatcher& rhs) = delete;
|
||||
PacketDispatcher& operator=(const PacketDispatcher& rhs) = delete;
|
||||
PacketDispatcher(PacketDispatcher&& rhs) = delete;
|
||||
PacketDispatcher& operator=(PacketDispatcher&& rhs) = delete;
|
||||
|
||||
void Dispatch(Packet* packet);
|
||||
|
||||
void RegisterHandler(PacketType type, PacketHandler* handler);
|
||||
void UnregisterHandler(PacketType type, PacketHandler* handler);
|
||||
void UnregisterHandler(PacketHandler* handler);
|
||||
};
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
13
include/protocol/PacketFactory.h
Normal file
13
include/protocol/PacketFactory.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "protocol/Protocol.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
namespace PacketFactory {
|
||||
|
||||
Packet* createPacket(PacketType type, DataBuffer& buffer);
|
||||
|
||||
}
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
39
include/protocol/PacketHandler.h
Normal file
39
include/protocol/PacketHandler.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "protocol/Protocol.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
class PacketDispatcher;
|
||||
|
||||
class PacketHandler{
|
||||
private:
|
||||
PacketDispatcher* m_Dispatcher;
|
||||
public:
|
||||
PacketHandler(PacketDispatcher* dispatcher) : m_Dispatcher(dispatcher){}
|
||||
virtual ~PacketHandler(){}
|
||||
|
||||
PacketDispatcher* GetDispatcher(){return m_Dispatcher;}
|
||||
|
||||
virtual void HandlePacket(PlayerLoginPacket* packet){}
|
||||
virtual void HandlePacket(WorldBeginDataPacket* packet){}
|
||||
virtual void HandlePacket(WorldDataPacket* packet){}
|
||||
virtual void HandlePacket(KeepAlivePacket* packet){}
|
||||
virtual void HandlePacket(UpdateMoneyPacket* packet){}
|
||||
virtual void HandlePacket(UpdateExpPacket* packet){}
|
||||
virtual void HandlePacket(UpdateLobbyTimePacket* packet){}
|
||||
virtual void HandlePacket(UpdateGameStatePacket* packet){}
|
||||
virtual void HandlePacket(PlayerListPacket* packet){}
|
||||
virtual void HandlePacket(PlayerJoinPacket* packet){}
|
||||
virtual void HandlePacket(PlayerLeavePacket* packet){}
|
||||
virtual void HandlePacket(ConnexionInfoPacket* packet){}
|
||||
virtual void HandlePacket(SelectTeamPacket* packet){}
|
||||
virtual void HandlePacket(UpdatePlayerTeamPacket* packet){}
|
||||
virtual void HandlePacket(DisconnectPacket* packet){}
|
||||
virtual void HandlePacket(ServerTpsPacket* packet){}
|
||||
virtual void HandlePacket(SpawnMobPacket* packet){}
|
||||
};
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
384
include/protocol/Protocol.h
Normal file
384
include/protocol/Protocol.h
Normal file
@@ -0,0 +1,384 @@
|
||||
#pragma once
|
||||
|
||||
#include "network/DataBuffer.h"
|
||||
#include "game/World.h"
|
||||
#include "game/BaseGame.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
class PacketHandler;
|
||||
|
||||
enum class PacketType : std::uint8_t{
|
||||
PlayerLogin = 0,
|
||||
PlayerJoin,
|
||||
PlayerLeave,
|
||||
WorldBeginData,
|
||||
WorldData,
|
||||
ChunkData,
|
||||
KeepAlive,
|
||||
UpdateMoney,
|
||||
UpdateEXP,
|
||||
UpdateLobbyTime,
|
||||
UpdateGameState,
|
||||
Disconnect,
|
||||
PlayerList,
|
||||
ConnectionInfo,
|
||||
SelectTeam,
|
||||
UpdatePlayerTeam,
|
||||
ServerTps,
|
||||
SpawnMob,
|
||||
};
|
||||
|
||||
class Packet{
|
||||
public:
|
||||
Packet(){}
|
||||
virtual ~Packet(){}
|
||||
|
||||
virtual DataBuffer Serialize() const = 0;
|
||||
virtual void Deserialize(DataBuffer& data) = 0;
|
||||
virtual void Dispatch(PacketHandler* handler) = 0;
|
||||
|
||||
virtual PacketType getType() const = 0;
|
||||
std::uint8_t getID() const{ return (std::uint8_t)getType(); }
|
||||
};
|
||||
|
||||
|
||||
class KeepAlivePacket : public Packet{
|
||||
private:
|
||||
std::uint64_t m_AliveID;
|
||||
public:
|
||||
KeepAlivePacket(){}
|
||||
KeepAlivePacket(std::uint64_t aliveID): m_AliveID(aliveID){}
|
||||
virtual ~KeepAlivePacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
std::uint64_t getAliveID(){ return m_AliveID; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::KeepAlive; }
|
||||
};
|
||||
|
||||
class PlayerLoginPacket : public Packet{
|
||||
private:
|
||||
std::string m_PlayerName;
|
||||
public:
|
||||
PlayerLoginPacket(){}
|
||||
PlayerLoginPacket(std::string playerName): m_PlayerName(playerName){}
|
||||
virtual ~PlayerLoginPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::PlayerLogin; }
|
||||
|
||||
const std::string& getPlayerName(){ return m_PlayerName; }
|
||||
};
|
||||
|
||||
class WorldBeginDataPacket : public Packet{
|
||||
private:
|
||||
game::TowerTileColorPalette m_TowerPlacePalette;
|
||||
game::Color m_WalkablePalette;
|
||||
std::vector<game::Color> m_DecorationPalette;
|
||||
|
||||
game::SpawnColorPalette m_SpawnColorPalette;
|
||||
|
||||
game::TilePalette m_TilePalette;
|
||||
|
||||
game::Spawn m_RedSpawn, m_BlueSpawn;
|
||||
game::TeamCastle m_RedTower, m_BlueTower;
|
||||
|
||||
const game::World* m_World;
|
||||
public:
|
||||
WorldBeginDataPacket(){}
|
||||
WorldBeginDataPacket(const game::World* world): m_World(world){}
|
||||
virtual ~WorldBeginDataPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::WorldBeginData; }
|
||||
|
||||
const game::TowerTileColorPalette& getTowerTilePalette() const{ return m_TowerPlacePalette; }
|
||||
const game::Color& getWalkableTileColor() const{ return m_WalkablePalette; }
|
||||
const std::vector<game::Color>& getDecorationPalette() const{ return m_DecorationPalette; }
|
||||
|
||||
const game::Spawn& getRedSpawn() const{ return m_RedSpawn; }
|
||||
const game::Spawn& getBlueSpawn() const{ return m_BlueSpawn; }
|
||||
|
||||
const game::SpawnColorPalette& getSpawnPalette() const{ return m_SpawnColorPalette; }
|
||||
|
||||
const game::TeamCastle& getRedCastle() const{ return m_RedTower; }
|
||||
const game::TeamCastle& getBlueCastle() const{ return m_BlueTower; }
|
||||
|
||||
const game::TilePalette getTilePalette() const{ return m_TilePalette; }
|
||||
};
|
||||
|
||||
class WorldDataPacket : public Packet{
|
||||
private:
|
||||
std::unordered_map<game::ChunkCoord, game::ChunkPtr> m_Chunks;
|
||||
|
||||
const game::World* m_World;
|
||||
public:
|
||||
WorldDataPacket(){}
|
||||
WorldDataPacket(const game::World* world): m_World(world){}
|
||||
virtual ~WorldDataPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::WorldData; }
|
||||
|
||||
const std::unordered_map<game::ChunkCoord, game::ChunkPtr>& getChunks() const{ return m_Chunks; }
|
||||
};
|
||||
|
||||
class UpdateMoneyPacket : public Packet{
|
||||
private:
|
||||
std::uint32_t m_NewAmount;
|
||||
public:
|
||||
UpdateMoneyPacket(){}
|
||||
UpdateMoneyPacket(std::uint32_t newAmount): m_NewAmount(newAmount){}
|
||||
virtual ~UpdateMoneyPacket(){}
|
||||
|
||||
std::uint32_t getGold() const{ return m_NewAmount; }
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::UpdateMoney; }
|
||||
};
|
||||
|
||||
class UpdateExpPacket : public Packet{
|
||||
private:
|
||||
std::uint32_t m_NewAmount;
|
||||
public:
|
||||
UpdateExpPacket(){}
|
||||
UpdateExpPacket(std::uint32_t newAmount): m_NewAmount(newAmount){}
|
||||
virtual ~UpdateExpPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::UpdateEXP; }
|
||||
};
|
||||
|
||||
class UpdateLobbyTimePacket : public Packet{
|
||||
private:
|
||||
std::uint32_t m_RemainingTime;
|
||||
public:
|
||||
UpdateLobbyTimePacket(){}
|
||||
UpdateLobbyTimePacket(std::uint32_t remainingTime): m_RemainingTime(remainingTime){}
|
||||
virtual ~UpdateLobbyTimePacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
std::uint32_t getRemainingTime() const{ return m_RemainingTime; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::UpdateLobbyTime; }
|
||||
};
|
||||
|
||||
class UpdateGameStatePacket : public Packet{
|
||||
private:
|
||||
game::GameState m_GameState;
|
||||
public:
|
||||
UpdateGameStatePacket(){}
|
||||
UpdateGameStatePacket(game::GameState gameState): m_GameState(gameState){}
|
||||
virtual ~UpdateGameStatePacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
game::GameState getGameState() const{ return m_GameState; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::UpdateGameState; }
|
||||
};
|
||||
|
||||
struct PlayerInfo{
|
||||
std::string name;
|
||||
game::TeamColor team;
|
||||
};
|
||||
|
||||
class PlayerListPacket : public Packet{
|
||||
private:
|
||||
std::map<std::uint8_t, PlayerInfo> m_Players;
|
||||
public:
|
||||
PlayerListPacket(){}
|
||||
PlayerListPacket(std::map<std::uint8_t, PlayerInfo> players): m_Players(players){}
|
||||
virtual ~PlayerListPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
const std::map<std::uint8_t, PlayerInfo>& getPlayers() const{ return m_Players; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::PlayerList; }
|
||||
};
|
||||
|
||||
class PlayerJoinPacket : public Packet{
|
||||
private:
|
||||
std::uint8_t m_PlayerID;
|
||||
std::string m_PlayerName;
|
||||
public:
|
||||
PlayerJoinPacket(){}
|
||||
PlayerJoinPacket(std::uint8_t playerID, const std::string& playerName): m_PlayerID(playerID), m_PlayerName(playerName){}
|
||||
virtual ~PlayerJoinPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
std::uint8_t getPlayerID() const{ return m_PlayerID; }
|
||||
const std::string& getPlayerName() const{ return m_PlayerName; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::PlayerJoin; }
|
||||
};
|
||||
|
||||
class PlayerLeavePacket : public Packet{
|
||||
private:
|
||||
std::uint8_t m_PlayerID;
|
||||
public:
|
||||
PlayerLeavePacket(){}
|
||||
PlayerLeavePacket(std::uint8_t playerID): m_PlayerID(playerID){}
|
||||
virtual ~PlayerLeavePacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
std::uint8_t getPlayerID() const{ return m_PlayerID; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::PlayerLeave; }
|
||||
};
|
||||
|
||||
class ConnexionInfoPacket : public Packet{
|
||||
private:
|
||||
std::uint8_t m_ConnectionID;
|
||||
public:
|
||||
ConnexionInfoPacket(){}
|
||||
ConnexionInfoPacket(std::uint8_t connectionID): m_ConnectionID(connectionID){}
|
||||
virtual ~ConnexionInfoPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
std::uint8_t getConnectionID() const{ return m_ConnectionID; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::ConnectionInfo; }
|
||||
};
|
||||
|
||||
class SelectTeamPacket : public Packet{
|
||||
private:
|
||||
game::TeamColor m_SelectedTeam;
|
||||
public:
|
||||
SelectTeamPacket(){}
|
||||
SelectTeamPacket(game::TeamColor selectedTeam): m_SelectedTeam(selectedTeam){}
|
||||
virtual ~SelectTeamPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
game::TeamColor getSelectedTeam() const{ return m_SelectedTeam; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::SelectTeam; }
|
||||
};
|
||||
|
||||
class UpdatePlayerTeamPacket : public Packet{
|
||||
private:
|
||||
std::uint8_t m_PlayerID;
|
||||
game::TeamColor m_SelectedTeam;
|
||||
public:
|
||||
UpdatePlayerTeamPacket(){}
|
||||
UpdatePlayerTeamPacket(std::uint8_t playerID, game::TeamColor selectedTeam): m_PlayerID(playerID), m_SelectedTeam(selectedTeam){}
|
||||
virtual ~UpdatePlayerTeamPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
game::TeamColor getSelectedTeam() const{ return m_SelectedTeam; }
|
||||
std::uint8_t getPlayerID() const{ return m_PlayerID; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::UpdatePlayerTeam; }
|
||||
};
|
||||
|
||||
class DisconnectPacket : public Packet{
|
||||
private:
|
||||
std::string m_Reason; // only when sent from server
|
||||
public:
|
||||
DisconnectPacket(){}
|
||||
DisconnectPacket(std::string reason): m_Reason(reason){}
|
||||
virtual ~DisconnectPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
const std::string& getReason() const{ return m_Reason; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::Disconnect; }
|
||||
};
|
||||
|
||||
class ServerTpsPacket : public Packet{
|
||||
private:
|
||||
float m_TPS;
|
||||
std::uint64_t m_PacketSendTime; // used to calculate ping
|
||||
public:
|
||||
ServerTpsPacket(){}
|
||||
ServerTpsPacket(float tps, std::uint64_t sendTime): m_TPS(tps), m_PacketSendTime(sendTime){}
|
||||
virtual ~ServerTpsPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
float getTPS() const{ return m_TPS; }
|
||||
std::uint64_t getPacketSendTime() const {return m_PacketSendTime;}
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::ServerTps; }
|
||||
};
|
||||
|
||||
class SpawnMobPacket : public Packet{
|
||||
private:
|
||||
game::MobID m_MobID;
|
||||
game::MobType m_MobType;
|
||||
game::MobLevel m_MobLevel;
|
||||
game::Direction m_MobDirection;
|
||||
game::PlayerID m_Sender;
|
||||
float m_MobX, m_MobY;
|
||||
public:
|
||||
SpawnMobPacket(){}
|
||||
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),
|
||||
m_MobDirection(dir), m_Sender(sender), m_MobX(x), m_MobY(y){}
|
||||
virtual ~SpawnMobPacket(){}
|
||||
|
||||
virtual DataBuffer Serialize() const;
|
||||
virtual void Deserialize(DataBuffer& data);
|
||||
virtual void Dispatch(PacketHandler* handler);
|
||||
|
||||
game::MobID getMobID() const{ return m_MobID; }
|
||||
game::MobType getMobType() const{ return m_MobType; }
|
||||
game::MobLevel getMobLevel() const{ return m_MobLevel; }
|
||||
game::Direction getMobDirection() const{ return m_MobDirection; }
|
||||
game::PlayerID getSender() const{ return m_Sender; }
|
||||
float getMobX() const{ return m_MobX; }
|
||||
float getMobY() const{ return m_MobY; }
|
||||
|
||||
virtual PacketType getType() const{ return PacketType::SpawnMob; }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
44
include/render/Renderer.h
Normal file
44
include/render/Renderer.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Renderer.h
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#ifndef RENDER_RENDERER_H_
|
||||
#define RENDER_RENDERER_H_
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include "loader/GLLoader.h"
|
||||
|
||||
namespace td {
|
||||
namespace render{
|
||||
|
||||
namespace Renderer{
|
||||
|
||||
struct Model{
|
||||
GL::VAO* vao;
|
||||
glm::vec2 positon;
|
||||
};
|
||||
|
||||
bool init();
|
||||
void destroy();
|
||||
|
||||
void prepare();
|
||||
void resize(const int width, const int height);
|
||||
|
||||
void renderVAO(const GL::VAO& vao);
|
||||
void renderModel(const Model& model);
|
||||
|
||||
void setZoom(float zoom);
|
||||
void setCamMovement(const glm::vec2& mov);
|
||||
void setCamPos(const glm::vec2& newPos);
|
||||
void setIsometricView(bool isometric); // false = 2D true = Isometric
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace render
|
||||
} // namespace td
|
||||
|
||||
#endif /* RENDER_RENDERER_H_ */
|
||||
23
include/render/WorldRenderer.h
Normal file
23
include/render/WorldRenderer.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/World.h"
|
||||
|
||||
namespace td {
|
||||
namespace render {
|
||||
|
||||
namespace WorldRenderer{
|
||||
|
||||
void init(const game::World* world);
|
||||
void update();
|
||||
void render();
|
||||
void destroy();
|
||||
|
||||
void moveCam(float relativeX, float relativeY, float aspectRatio);
|
||||
void changeZoom(float zoom);
|
||||
void click(int mouseX, int mouseY);
|
||||
|
||||
} // namespace WorldRenderer
|
||||
|
||||
|
||||
} // namespace render
|
||||
} // namespace td
|
||||
23
include/render/gui/TowerGui.h
Normal file
23
include/render/gui/TowerGui.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* TowerGUI.h
|
||||
*
|
||||
* Created on: 5 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#ifndef RENDER_GUI_TOWERGUI_H_
|
||||
#define RENDER_GUI_TOWERGUI_H_
|
||||
|
||||
struct GLFWwindow;
|
||||
|
||||
namespace TowerGui{
|
||||
|
||||
void init(GLFWwindow* window);
|
||||
void render();
|
||||
void destroy();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* RENDER_GUI_TOWERGUI_H_ */
|
||||
67
include/render/loader/GLLoader.h
Normal file
67
include/render/loader/GLLoader.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* GLLoader.h
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define REMOVE_COPY(className) \
|
||||
className(const className &) = delete;\
|
||||
className& operator=(const className &) = delete
|
||||
|
||||
|
||||
namespace GL{
|
||||
|
||||
struct VertexAttribPointer{
|
||||
unsigned int m_Index, m_Size;
|
||||
int m_Offset;
|
||||
};
|
||||
|
||||
class VBO{
|
||||
private:
|
||||
unsigned int m_ID, m_DataStride;
|
||||
std::vector<VertexAttribPointer> m_VertexAttribs;
|
||||
public:
|
||||
REMOVE_COPY(VBO);
|
||||
VBO(VBO&& other){
|
||||
m_VertexAttribs = std::move(other.m_VertexAttribs);
|
||||
m_ID = other.m_ID;
|
||||
m_DataStride = other.m_DataStride;
|
||||
other.m_ID = 0;
|
||||
other.m_DataStride = 0;
|
||||
}
|
||||
VBO(const std::vector<float>& data, unsigned int stride);
|
||||
~VBO();
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
void addVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset);
|
||||
void bindVertexAttribs() const;
|
||||
};
|
||||
|
||||
class VAO{
|
||||
private:
|
||||
unsigned int m_ID, m_VertexCount;
|
||||
std::vector<VBO> m_Vbos; //use to destroy vbos when become unused
|
||||
public:
|
||||
REMOVE_COPY(VAO);
|
||||
VAO(VAO&& other){
|
||||
m_ID = other.m_ID;
|
||||
m_VertexCount = other.m_VertexCount;
|
||||
m_Vbos = std::move(other.m_Vbos);
|
||||
other.m_VertexCount = 0;
|
||||
other.m_ID = 0;
|
||||
}
|
||||
VAO(unsigned int vertexCount);
|
||||
~VAO();
|
||||
unsigned int getVertexCount() const {return m_VertexCount;}
|
||||
void bindVBO(VBO& vbo);
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
16
include/render/loader/TextureLoader.h
Normal file
16
include/render/loader/TextureLoader.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* TextureLoader.h
|
||||
*
|
||||
* Created on: 15 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#ifndef RENDER_LOADER_TEXTURELOADER_H_
|
||||
#define RENDER_LOADER_TEXTURELOADER_H_
|
||||
|
||||
namespace TextureLoader{
|
||||
const unsigned int loadGLTexture(const char* fileName);
|
||||
}
|
||||
|
||||
|
||||
#endif /* RENDER_LOADER_TEXTURELOADER_H_ */
|
||||
18
include/render/loader/WorldLoader.h
Normal file
18
include/render/loader/WorldLoader.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/World.h"
|
||||
#include "GLLoader.h"
|
||||
|
||||
namespace td {
|
||||
namespace render {
|
||||
|
||||
namespace WorldLoader {
|
||||
|
||||
GL::VAO loadMobModel();
|
||||
GL::VAO loadWorldModel(const td::game::World* world);
|
||||
|
||||
} // namespace WorldLoader
|
||||
|
||||
|
||||
} // namespace render
|
||||
} // namespace td
|
||||
7559
include/render/loader/stb_image.h
Executable file
7559
include/render/loader/stb_image.h
Executable file
File diff suppressed because it is too large
Load Diff
18
include/render/shaders/EntityShader.h
Normal file
18
include/render/shaders/EntityShader.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "ShaderProgram.h"
|
||||
|
||||
class EntityShader : public ShaderProgram{
|
||||
private:
|
||||
unsigned int location_cam = 0, location_zoom = 0, location_aspect_ratio = 0, location_translation = 0, location_viewtype = 0;
|
||||
protected:
|
||||
void getAllUniformLocation();
|
||||
public:
|
||||
EntityShader();
|
||||
void loadShader();
|
||||
void setCamPos(const glm::vec2& camPos);
|
||||
void setZoom(float zoom);
|
||||
void setAspectRatio(float aspectRatio);
|
||||
void setModelPos(const glm::vec2& modelPos);
|
||||
void setIsometricView(float isometric);
|
||||
};
|
||||
47
include/render/shaders/ShaderProgram.h
Executable file
47
include/render/shaders/ShaderProgram.h
Executable file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* ShaderProgram.h
|
||||
*
|
||||
* Created on: 31 janv. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#ifndef RENDER_SHADERS_SHADERPROGRAM_H_
|
||||
#define RENDER_SHADERS_SHADERPROGRAM_H_
|
||||
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace gl{
|
||||
enum class GLenum : unsigned int;
|
||||
}
|
||||
|
||||
class ShaderProgram {
|
||||
public:
|
||||
ShaderProgram();
|
||||
virtual ~ShaderProgram();
|
||||
void start() const;
|
||||
void stop() const;
|
||||
void loadProgramFile(const std::string& vertexFile, const std::string& fragmentFile);
|
||||
void loadProgram(const std::string& vertexSource, const std::string& fragmentSource);
|
||||
|
||||
protected:
|
||||
virtual void getAllUniformLocation() = 0;
|
||||
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 loadVector(const int& location, const glm::vec2& vector)const;
|
||||
void loadVector(const int& location, const glm::vec3& vector)const;
|
||||
void loadVector(const int& location, const glm::vec4& vector)const;
|
||||
void loadBoolean(const int& location, const bool& value)const;
|
||||
void loadMatrix(const int& location, const glm::mat4& matrix);
|
||||
void cleanUp() const;
|
||||
|
||||
private:
|
||||
unsigned int programID;
|
||||
unsigned int vertexShaderID;
|
||||
unsigned int fragmentShaderID;
|
||||
int loadShaderFromFile(const std::string& file, gl::GLenum type);
|
||||
int loadShader(const std::string& source, gl::GLenum type);
|
||||
};
|
||||
|
||||
#endif /* RENDER_SHADERS_SHADERPROGRAM_H_ */
|
||||
27
include/render/shaders/WorldShader.h
Normal file
27
include/render/shaders/WorldShader.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* GameShader.h
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#ifndef RENDER_SHADERS_GAMESHADER_H_
|
||||
#define RENDER_SHADERS_GAMESHADER_H_
|
||||
|
||||
#include "ShaderProgram.h"
|
||||
|
||||
class WorldShader : public ShaderProgram{
|
||||
private:
|
||||
unsigned int location_cam = 0, location_zoom = 0, location_aspect_ratio = 0, location_viewtype = 0;
|
||||
protected:
|
||||
void getAllUniformLocation();
|
||||
public:
|
||||
WorldShader();
|
||||
void loadShader();
|
||||
void setCamPos(const glm::vec2& camPos);
|
||||
void setZoom(float zoom);
|
||||
void setAspectRatio(float aspectRatio);
|
||||
void setIsometricView(float isometric);
|
||||
};
|
||||
|
||||
#endif /* RENDER_SHADERS_GAMESHADER_H_ */
|
||||
31
include/window/Display.h
Normal file
31
include/window/Display.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Display.h
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#ifndef WINDOW_DISPLAY_H_
|
||||
#define WINDOW_DISPLAY_H_
|
||||
|
||||
|
||||
namespace Display{
|
||||
|
||||
void create();
|
||||
void render();
|
||||
void update();
|
||||
void destroy();
|
||||
void pollEvents();
|
||||
|
||||
bool isCloseRequested();
|
||||
|
||||
const bool isMouseDown(int button);
|
||||
|
||||
float getAspectRatio();
|
||||
int getWindowWidth();
|
||||
int getWindowHeight();
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif /* WINDOW_DISPLAY_H_ */
|
||||
22
src/Tower Defense.cpp
Normal file
22
src/Tower Defense.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
//============================================================================
|
||||
// Name : Tower.cpp
|
||||
// Author : Persson
|
||||
// Version :
|
||||
// Copyright : Copyright
|
||||
// Description : Hello World in C++, Ansi-style
|
||||
//============================================================================
|
||||
|
||||
#include "window/Display.h"
|
||||
#include "misc/Random.h"
|
||||
|
||||
int main(int argc, const char* args[]){
|
||||
td::utils::initRandomizer();
|
||||
Display::create();
|
||||
while (!Display::isCloseRequested()){
|
||||
Display::pollEvents();
|
||||
Display::render();
|
||||
Display::update();
|
||||
}
|
||||
Display::destroy();
|
||||
return 0;
|
||||
}
|
||||
21
src/game/BaseGame.cpp
Normal file
21
src/game/BaseGame.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "game/BaseGame.h"
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
Game::Game(World* world) : m_World(world){
|
||||
|
||||
}
|
||||
|
||||
Game::~Game(){
|
||||
|
||||
}
|
||||
|
||||
void Game::tick(std::uint64_t delta){
|
||||
if(m_GameState == GameState::Game){
|
||||
m_World->tick(delta);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
76
src/game/Connexion.cpp
Normal file
76
src/game/Connexion.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "game/Connexion.h"
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
#include "protocol/PacketFactory.h"
|
||||
#include "game/server/Server.h"
|
||||
#include "misc/Compression.h"
|
||||
|
||||
#include "misc/Time.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
Connexion::Connexion() : protocol::PacketHandler(nullptr){
|
||||
|
||||
}
|
||||
|
||||
Connexion::Connexion(Connexion&& move) : m_Socket(std::move(move.m_Socket)), protocol::PacketHandler(&m_Dispatcher){
|
||||
|
||||
}
|
||||
|
||||
Connexion::Connexion(protocol::PacketDispatcher* dispatcher) : protocol::PacketHandler(dispatcher){
|
||||
|
||||
}
|
||||
|
||||
Connexion::Connexion(protocol::PacketDispatcher* dispatcher, network::TCPSocket& socket) : protocol::PacketHandler(dispatcher), m_Socket(std::move(socket)){
|
||||
|
||||
}
|
||||
|
||||
bool Connexion::updateSocket(){
|
||||
if(m_Socket.GetStatus() != network::Socket::Connected)
|
||||
return false;
|
||||
|
||||
DataBuffer buffer;
|
||||
m_Socket.Receive(buffer, sizeof(std::size_t));
|
||||
if (buffer.GetSize() > 0){
|
||||
std::size_t packetLenght;
|
||||
buffer >> packetLenght;
|
||||
|
||||
m_Socket.Receive(buffer, packetLenght);
|
||||
|
||||
DataBuffer decompressed = utils::Decompress(buffer, packetLenght);
|
||||
|
||||
protocol::PacketType packetType;
|
||||
decompressed >> packetType;
|
||||
|
||||
protocol::Packet* packet = protocol::PacketFactory::createPacket(packetType, decompressed);
|
||||
GetDispatcher()->Dispatch(packet);
|
||||
delete packet;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Connexion::connect(const std::string& address, std::uint16_t port){
|
||||
if(!m_Socket.Connect(address, port)){
|
||||
return false;
|
||||
}
|
||||
m_Socket.SetBlocking(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Connexion::sendPacket(protocol::Packet* packet){
|
||||
network::SendPacket(packet->Serialize(), m_Socket);
|
||||
}
|
||||
|
||||
void Connexion::closeConnection(){
|
||||
m_Socket.Disconnect();
|
||||
}
|
||||
|
||||
Connexion::~Connexion(){
|
||||
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
220
src/game/Mobs.cpp
Normal file
220
src/game/Mobs.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
#include "game/Mobs.h"
|
||||
#include <map>
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
typedef std::pair<MobType, std::uint8_t> MobKey;
|
||||
|
||||
const std::map<MobKey, MobStats> MobConstants = {
|
||||
// damage speed money_cost exp_cost exp_reward max_health
|
||||
{{MobType::Zombie, 1},{MobStats{1, 1.6, 15, 0, 7, 40}}},
|
||||
{{MobType::Zombie, 2},{MobStats{1, 1.6, 18, 88, 9, 56}}},
|
||||
{{MobType::Zombie, 3},{MobStats{1, 1.6, 22, 153, 10, 78}}},
|
||||
{{MobType::Zombie, 4},{MobStats{1.5, 1.6, 26, 268, 13, 110}}},
|
||||
{{MobType::Zombie, 5},{MobStats{2, 1.6, 31, 469, 15, 154}}},
|
||||
|
||||
{{MobType::Spider, 1},{MobStats{1, 1.6, 25, 100, 15, 80}}},
|
||||
{{MobType::Spider, 2},{MobStats{1, 2, 30, 175, 16, 112}}},
|
||||
{{MobType::Spider, 3},{MobStats{1.5, 2, 36, 306, 18, 157}}},
|
||||
{{MobType::Spider, 4},{MobStats{2.5, 2, 43, 536, 19, 222}}},
|
||||
{{MobType::Spider, 5},{MobStats{1.5, 2.5, 52, 938, 22, 307}}},
|
||||
|
||||
{{MobType::Skeleton, 1},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Skeleton, 2},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Skeleton, 3},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Skeleton, 4},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Skeleton, 5},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
|
||||
{{MobType::Pigman, 1},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Pigman, 2},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Pigman, 3},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Pigman, 4},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Pigman, 5},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
|
||||
{{MobType::Creeper, 1},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Creeper, 2},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Creeper, 3},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Creeper, 4},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Creeper, 5},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
|
||||
{{MobType::Silverfish, 1},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Silverfish, 2},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Silverfish, 3},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Silverfish, 4},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Silverfish, 5},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
|
||||
{{MobType::Blaze, 1},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Blaze, 2},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Blaze, 3},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Blaze, 4},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Blaze, 5},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
|
||||
{{MobType::Witch, 1},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Witch, 2},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Witch, 3},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Witch, 4},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Witch, 5},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
|
||||
{{MobType::Slime, 1},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Slime, 2},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Slime, 3},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Slime, 4},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Slime, 5},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
|
||||
{{MobType::Giant, 1},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Giant, 2},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Giant, 3},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Giant, 4},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
{{MobType::Giant, 5},{MobStats{0, 0, 0, 0, 0, 0}}},
|
||||
};
|
||||
|
||||
const MobStats* getMobStats(MobType type, std::uint8_t level){
|
||||
return &MobConstants.at(MobKey{type, level});
|
||||
}
|
||||
|
||||
const std::map<MobKey, TowerImmunities> MobsTowerImmunities = {
|
||||
{{MobType::Zombie, 1},{}},
|
||||
{{MobType::Zombie, 2},{}},
|
||||
{{MobType::Zombie, 3},{}},
|
||||
{{MobType::Zombie, 4},{}},
|
||||
{{MobType::Zombie, 5},{}},
|
||||
|
||||
{{MobType::Spider, 1},{}},
|
||||
{{MobType::Spider, 2},{}},
|
||||
{{MobType::Spider, 3},{}},
|
||||
{{MobType::Spider, 4},{TowerType::Archer, TowerType::Artillery, TowerType::Leach, TowerType::Necromancer, TowerType::Poison, TowerType::Quake, TowerType::Sorcerer, TowerType::Turret, TowerType::Zeus}},
|
||||
{{MobType::Spider, 5},{TowerType::Archer, TowerType::Artillery, TowerType::Leach, TowerType::Necromancer, TowerType::Poison, TowerType::Quake, TowerType::Sorcerer, TowerType::Turret, TowerType::Zeus}},
|
||||
|
||||
{{MobType::Skeleton, 1},{}},
|
||||
{{MobType::Skeleton, 2},{}},
|
||||
{{MobType::Skeleton, 3},{}},
|
||||
{{MobType::Skeleton, 4},{}},
|
||||
{{MobType::Skeleton, 5},{}},
|
||||
|
||||
{{MobType::Pigman, 1},{TowerType::Zeus}},
|
||||
{{MobType::Pigman, 2},{TowerType::Zeus}},
|
||||
{{MobType::Pigman, 3},{TowerType::Zeus}},
|
||||
{{MobType::Pigman, 4},{TowerType::Zeus}},
|
||||
{{MobType::Pigman, 5},{TowerType::Zeus}},
|
||||
|
||||
{{MobType::Creeper, 1},{}},
|
||||
{{MobType::Creeper, 2},{}},
|
||||
{{MobType::Creeper, 3},{}},
|
||||
{{MobType::Creeper, 4},{}},
|
||||
{{MobType::Creeper, 5},{}},
|
||||
|
||||
{{MobType::Silverfish, 1},{}},
|
||||
{{MobType::Silverfish, 2},{}},
|
||||
{{MobType::Silverfish, 3},{}},
|
||||
{{MobType::Silverfish, 4},{}},
|
||||
{{MobType::Silverfish, 5},{}},
|
||||
|
||||
{{MobType::Blaze, 1},{}},
|
||||
{{MobType::Blaze, 2},{}},
|
||||
{{MobType::Blaze, 3},{}},
|
||||
{{MobType::Blaze, 4},{}},
|
||||
{{MobType::Blaze, 5},{}},
|
||||
|
||||
{{MobType::Witch, 1},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
|
||||
{{MobType::Witch, 2},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
|
||||
{{MobType::Witch, 3},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
|
||||
{{MobType::Witch, 4},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
|
||||
{{MobType::Witch, 5},{TowerType::Zeus, TowerType::Mage, TowerType::Poison}},
|
||||
|
||||
{{MobType::Slime, 1},{}},
|
||||
{{MobType::Slime, 2},{}},
|
||||
{{MobType::Slime, 3},{}},
|
||||
{{MobType::Slime, 4},{}},
|
||||
{{MobType::Slime, 5},{}},
|
||||
|
||||
{{MobType::Giant, 1},{}},
|
||||
{{MobType::Giant, 2},{}},
|
||||
{{MobType::Giant, 3},{}},
|
||||
{{MobType::Giant, 4},{}},
|
||||
{{MobType::Giant, 5},{}},
|
||||
};
|
||||
|
||||
const TowerImmunities& getMobTowerImmunities(MobType type, std::uint8_t level){
|
||||
return MobsTowerImmunities.at({type, level});
|
||||
}
|
||||
|
||||
const std::map<MobKey, EffectImmunities> MobsEffectImmunities = {
|
||||
{{MobType::Zombie, 1},{}},
|
||||
{{MobType::Zombie, 2},{}},
|
||||
{{MobType::Zombie, 3},{}},
|
||||
{{MobType::Zombie, 4},{}},
|
||||
{{MobType::Zombie, 5},{}},
|
||||
|
||||
{{MobType::Spider, 1},{}},
|
||||
{{MobType::Spider, 2},{}},
|
||||
{{MobType::Spider, 3},{}},
|
||||
{{MobType::Spider, 4},{}},
|
||||
{{MobType::Spider, 5},{}},
|
||||
|
||||
{{MobType::Skeleton, 1},{}},
|
||||
{{MobType::Skeleton, 2},{}},
|
||||
{{MobType::Skeleton, 3},{EffectType::Fire}},
|
||||
{{MobType::Skeleton, 4},{EffectType::Fire}},
|
||||
{{MobType::Skeleton, 5},{EffectType::Fire}},
|
||||
|
||||
{{MobType::Pigman, 1},{EffectType::Fire}},
|
||||
{{MobType::Pigman, 2},{EffectType::Fire}},
|
||||
{{MobType::Pigman, 3},{EffectType::Fire}},
|
||||
{{MobType::Pigman, 4},{EffectType::Fire}},
|
||||
{{MobType::Pigman, 5},{EffectType::Fire}},
|
||||
|
||||
{{MobType::Creeper, 1},{}},
|
||||
{{MobType::Creeper, 2},{}},
|
||||
{{MobType::Creeper, 3},{}},
|
||||
{{MobType::Creeper, 4},{}},
|
||||
{{MobType::Creeper, 5},{}},
|
||||
|
||||
{{MobType::Silverfish, 1},{}},
|
||||
{{MobType::Silverfish, 2},{}},
|
||||
{{MobType::Silverfish, 3},{}},
|
||||
{{MobType::Silverfish, 4},{EffectType::Fire}},
|
||||
{{MobType::Silverfish, 5},{EffectType::Fire}},
|
||||
|
||||
{{MobType::Blaze, 1},{EffectType::Fire}},
|
||||
{{MobType::Blaze, 2},{EffectType::Fire}},
|
||||
{{MobType::Blaze, 3},{EffectType::Fire}},
|
||||
{{MobType::Blaze, 4},{EffectType::Fire}},
|
||||
{{MobType::Blaze, 5},{EffectType::Fire}},
|
||||
|
||||
{{MobType::Witch, 1},{}},
|
||||
{{MobType::Witch, 2},{}},
|
||||
{{MobType::Witch, 3},{}},
|
||||
{{MobType::Witch, 4},{}},
|
||||
{{MobType::Witch, 5},{}},
|
||||
|
||||
{{MobType::Slime, 1},{}},
|
||||
{{MobType::Slime, 2},{}},
|
||||
{{MobType::Slime, 3},{}},
|
||||
{{MobType::Slime, 4},{EffectType::Fire}},
|
||||
{{MobType::Slime, 5},{EffectType::Fire}},
|
||||
|
||||
{{MobType::Giant, 1},{EffectType::Stun}},
|
||||
{{MobType::Giant, 2},{EffectType::Stun}},
|
||||
{{MobType::Giant, 3},{EffectType::Stun}},
|
||||
{{MobType::Giant, 4},{EffectType::Stun}},
|
||||
{{MobType::Giant, 5},{EffectType::Stun}},
|
||||
};
|
||||
|
||||
const EffectImmunities& getMobEffectImmunities(MobType type, std::uint8_t level){
|
||||
return MobsEffectImmunities.at({type, level});
|
||||
}
|
||||
|
||||
MobPtr MobFactory::createMob(MobID id, MobType type, std::uint8_t level, PlayerID sender){
|
||||
using MobCreator = std::function<std::shared_ptr<Mob>(MobID, std::uint8_t, PlayerID)>;
|
||||
|
||||
static std::map<MobType, MobCreator> mobFactory = {
|
||||
{MobType::Zombie, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Zombie>(id, level, sender);} },
|
||||
{MobType::Spider, [](MobID id, std::uint8_t level, PlayerID sender) -> MobPtr {return std::make_shared<Spider>(id, level, sender);} },
|
||||
};
|
||||
|
||||
return mobFactory[type](id, level, sender);
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
27
src/game/Team.cpp
Normal file
27
src/game/Team.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "game/Player.h"
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
Team::Team(TeamColor color): m_Color(color){}
|
||||
|
||||
void Team::addPlayer(Player* newPlayer){
|
||||
m_Players.push_back(newPlayer);
|
||||
newPlayer->setTeamColor(m_Color);
|
||||
}
|
||||
|
||||
void Team::removePlayer(const Player* player){
|
||||
m_Players.erase(std::find(m_Players.begin(), m_Players.end(), player));
|
||||
}
|
||||
|
||||
TeamColor Team::getColor() const{
|
||||
return m_Color;
|
||||
}
|
||||
|
||||
std::uint8_t Team::getPlayerCount() const{
|
||||
return m_Players.size();
|
||||
}
|
||||
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
161
src/game/Towers.cpp
Normal file
161
src/game/Towers.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "game/Towers.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
const std::map<std::pair<TowerType, TowerLevel>, TowerStats> TowerConstants = {
|
||||
// // rate damage range
|
||||
{{TowerType::Archer, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Archer, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Archer, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Archer, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
{{TowerType::Archer, {3, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
{{TowerType::Archer, {4, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Ice, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Ice, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Ice, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Ice, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Sorcerer, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Sorcerer, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Sorcerer, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Sorcerer, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
{{TowerType::Sorcerer, {3, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
{{TowerType::Sorcerer, {4, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Zeus, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Zeus, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Zeus, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Zeus, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
{{TowerType::Zeus, {3, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
{{TowerType::Zeus, {4, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Mage, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Mage, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Mage, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Mage, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Artillery, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Artillery, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Artillery, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Artillery, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
{{TowerType::Artillery, {3, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
{{TowerType::Artillery, {4, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Quake, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Quake, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Quake, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Quake, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Poison, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Poison, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Poison, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Poison, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
{{TowerType::Poison, {3, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
{{TowerType::Poison, {4, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Leach, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Leach, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Leach, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Turret, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Turret, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Turret, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Turret, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
{{TowerType::Turret, {3, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
{{TowerType::Turret, {4, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
{{TowerType::Necromancer, {1, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Necromancer, {2, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Necromancer, {3, TowerPath::Top}}, {0, 0, 0}},
|
||||
{{TowerType::Necromancer, {4, TowerPath::Top}}, {0, 0, 0}},
|
||||
|
||||
{{TowerType::Necromancer, {3, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
{{TowerType::Necromancer, {4, TowerPath::Bottom}}, {0, 0, 0}},
|
||||
};
|
||||
|
||||
const TowerStats* getTowerStats(TowerType type, TowerLevel level){
|
||||
return &TowerConstants.at({type, level});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void ArcherTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void IceTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void MageTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void PoisonTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void QuakeTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void ZeusTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void ArtilleryTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void SorcererTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LeachTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void TurretTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
void NecromancerTower::tick(std::uint64_t delta){
|
||||
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
386
src/game/World.cpp
Normal file
386
src/game/World.cpp
Normal file
@@ -0,0 +1,386 @@
|
||||
#include "game/World.h"
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
#include "protocol/Protocol.h"
|
||||
#include "game/BaseGame.h"
|
||||
#include "misc/Random.h"
|
||||
|
||||
#include <cmath>
|
||||
#include "misc/Compression.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define WRITE_MAP 0
|
||||
|
||||
namespace td {
|
||||
namespace game {
|
||||
|
||||
World::World(Game* game) : m_Game(game){
|
||||
#if WRITE_MAP
|
||||
loadMapFromFile("");
|
||||
#endif
|
||||
}
|
||||
|
||||
TilePtr World::getTile(std::int32_t x, std::int32_t y){
|
||||
std::int16_t chunkX = x / Chunk::ChunkWidth;
|
||||
std::int16_t chunkY = y / Chunk::ChunkHeight;
|
||||
|
||||
std::uint16_t subChunkX = x % Chunk::ChunkWidth;
|
||||
std::uint16_t subChunkY = y % Chunk::ChunkHeight;
|
||||
|
||||
auto chunkIt = m_Chunks.find({chunkX, chunkY});
|
||||
if(chunkIt == m_Chunks.end())
|
||||
return nullptr;
|
||||
|
||||
ChunkPtr chunk = chunkIt->second;
|
||||
|
||||
return getTilePtr(chunk->getTileIndex(subChunkY * Chunk::ChunkWidth + subChunkX));
|
||||
}
|
||||
|
||||
bool World::loadMap(const protocol::WorldBeginDataPacket* worldHeader){
|
||||
m_TowerPlacePalette = worldHeader->getTowerTilePalette();
|
||||
m_WalkablePalette = worldHeader->getWalkableTileColor();
|
||||
m_DecorationPalette = worldHeader->getDecorationPalette();
|
||||
|
||||
m_Spawns[(std::size_t) TeamColor::Red] = worldHeader->getRedSpawn();
|
||||
m_Spawns[(std::size_t) TeamColor::Blue] = worldHeader->getBlueSpawn();
|
||||
|
||||
m_SpawnColorPalette = worldHeader->getSpawnPalette();
|
||||
|
||||
m_Castles[(std::size_t) TeamColor::Red] = worldHeader->getRedCastle();
|
||||
m_Castles[(std::size_t) TeamColor::Blue] = worldHeader->getBlueCastle();
|
||||
|
||||
m_TilePalette = worldHeader->getTilePalette();
|
||||
}
|
||||
|
||||
bool World::loadMap(const protocol::WorldDataPacket* worldData){
|
||||
m_Chunks = worldData->getChunks();
|
||||
}
|
||||
|
||||
bool World::loadMapFromFile(const std::string& fileName){
|
||||
#if !WRITE_MAP
|
||||
DataBuffer buffer;
|
||||
if(!buffer.ReadFile(fileName)){
|
||||
std::cerr << "Failed to load map from file " << fileName << " !\n";
|
||||
}
|
||||
|
||||
std::cout << "File size : " << buffer.GetSize() << std::endl;
|
||||
|
||||
DataBuffer mapHeaderPacketBuffer = utils::Decompress(buffer);
|
||||
DataBuffer mapDataPacketBuffer = utils::Decompress(buffer);
|
||||
|
||||
protocol::PacketType packetType;
|
||||
|
||||
mapHeaderPacketBuffer >> packetType;
|
||||
|
||||
protocol::WorldBeginDataPacket headerPacket;
|
||||
headerPacket.Deserialize(mapHeaderPacketBuffer);
|
||||
|
||||
mapDataPacketBuffer >> packetType;
|
||||
|
||||
protocol::WorldDataPacket dataPacket;
|
||||
dataPacket.Deserialize(mapDataPacketBuffer);
|
||||
|
||||
loadMap(&headerPacket);
|
||||
loadMap(&dataPacket);
|
||||
|
||||
#else
|
||||
m_WalkablePalette = {102, 102, 153};
|
||||
|
||||
m_TowerPlacePalette[0] = {204, 51, 0};
|
||||
m_TowerPlacePalette[1] = {255, 153, 0};
|
||||
|
||||
WalkableTile walkableTileDown; // 1
|
||||
walkableTileDown.direction = Direction::PositiveY;
|
||||
m_TilePalette.push_back(std::make_shared<WalkableTile>(walkableTileDown));
|
||||
|
||||
WalkableTile walkableTileUp; // 2
|
||||
walkableTileUp.direction = Direction::NegativeY;
|
||||
m_TilePalette.push_back(std::make_shared<WalkableTile>(walkableTileUp));
|
||||
|
||||
WalkableTile walkableTileLeft; // 3
|
||||
walkableTileLeft.direction = Direction::NegativeX;
|
||||
m_TilePalette.push_back(std::make_shared<WalkableTile>(walkableTileLeft));
|
||||
|
||||
WalkableTile walkableTileRight; // 4
|
||||
walkableTileRight.direction = Direction::PositiveX;
|
||||
m_TilePalette.push_back(std::make_shared<WalkableTile>(walkableTileRight));
|
||||
|
||||
TowerTile redTile0; // 5
|
||||
redTile0.color_palette_ref = 0;
|
||||
redTile0.team_owner = TeamColor::Red;
|
||||
m_TilePalette.push_back(std::make_shared<TowerTile>(redTile0));
|
||||
|
||||
TowerTile redTile1; // 6
|
||||
redTile1.color_palette_ref = 1;
|
||||
redTile1.team_owner = TeamColor::Red;
|
||||
m_TilePalette.push_back(std::make_shared<TowerTile>(redTile1));
|
||||
|
||||
TowerTile blueTile0; // 7
|
||||
blueTile0.color_palette_ref = 0;
|
||||
blueTile0.team_owner = TeamColor::Blue;
|
||||
m_TilePalette.push_back(std::make_shared<TowerTile>(blueTile0));
|
||||
|
||||
TowerTile blueTile1; // 8
|
||||
blueTile1.color_palette_ref = 1;
|
||||
blueTile1.team_owner = TeamColor::Blue;
|
||||
m_TilePalette.push_back(std::make_shared<TowerTile>(blueTile1));
|
||||
|
||||
m_Castles[(uint) TeamColor::Red].x = 58;
|
||||
m_Castles[(uint) TeamColor::Red].y = 43;
|
||||
|
||||
m_Spawns[(uint) TeamColor::Red].direction = Direction::PositiveY;
|
||||
m_Spawns[(uint) TeamColor::Red].x = 13;
|
||||
m_Spawns[(uint) TeamColor::Red].y = 13;
|
||||
|
||||
m_SpawnColorPalette[(uint) TeamColor::Red] = {255, 0, 0};
|
||||
|
||||
//Chunks
|
||||
|
||||
Chunk chunk0;
|
||||
for (int i = 0; i <= 6; i++){
|
||||
chunk0.palette.push_back(i);
|
||||
}
|
||||
chunk0.tiles = {
|
||||
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,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,
|
||||
0,0,0,0,0,0,6,5,6,5,6,0,0,0,0,0,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
0,0,0,0,0,0,5,6,5,6,5,0,0,0,0,0,5,5,5,2,4,4,4,4,4,4,4,4,4,4,4,4,
|
||||
0,0,0,0,0,0,6,5,6,5,6,0,0,0,0,0,5,5,5,2,2,4,4,4,4,4,4,4,4,4,4,1,
|
||||
0,0,0,0,0,0,5,6,5,6,5,0,0,0,0,0,0,0,0,2,2,2,4,4,4,4,4,4,4,4,1,1,
|
||||
0,0,0,0,0,0,6,5,6,5,6,0,0,0,0,0,0,0,0,2,2,2,2,4,4,4,4,4,4,1,1,1,
|
||||
0,0,0,0,0,6,6,6,5,5,5,0,0,0,0,0,6,6,6,2,2,2,2,2,5,5,5,5,5,1,1,1,
|
||||
0,0,0,0,0,6,6,6,5,5,5,1,1,1,1,1,6,6,6,2,2,2,2,2,5,6,6,6,5,1,1,1,
|
||||
0,0,0,0,0,6,6,6,5,5,5,1,1,1,1,1,6,6,6,2,2,2,2,2,5,6,5,6,5,1,1,1,
|
||||
0,0,0,0,0,5,5,5,6,6,6,1,1,1,1,1,5,5,5,2,2,2,2,2,5,6,6,6,5,1,1,1,
|
||||
0,0,0,0,0,5,5,5,6,6,6,1,1,1,1,1,5,5,5,2,2,2,2,2,5,5,5,5,5,1,1,1,
|
||||
0,0,0,0,0,5,5,5,6,6,6,1,1,1,1,1,5,5,5,2,2,2,2,2,6,6,6,6,6,1,1,1,
|
||||
0,0,0,0,0,6,6,6,5,5,5,1,1,1,1,4,4,4,4,2,2,2,2,2,6,5,5,5,6,1,1,4,
|
||||
0,0,0,0,0,6,6,6,5,5,5,1,1,1,4,4,4,4,4,4,2,2,2,2,6,5,6,5,6,1,4,4,
|
||||
0,0,0,0,0,6,6,6,5,5,5,1,1,4,4,4,4,4,4,4,4,2,2,2,6,5,5,5,6,4,4,4,
|
||||
0,0,0,0,0,5,5,5,6,6,6,1,4,4,4,4,4,4,4,4,4,4,2,2,6,6,6,6,6,0,0,0,
|
||||
0,0,0,0,0,5,5,5,6,6,6,4,4,4,4,4,4,4,4,4,4,4,4,2,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,5,5,5,6,6,6,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,
|
||||
0,0,0,0,0,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,
|
||||
0,0,0,0,0,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,
|
||||
0,0,0,0,0,6,6,6,5,5,5,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,
|
||||
0,0,0,0,0,0,0,0,0,0,0,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,
|
||||
0,0,0,0,0,0,0,0,0,0,0,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,
|
||||
};
|
||||
|
||||
|
||||
Chunk chunk1;
|
||||
for (int i = 0; i <= 6; i++){
|
||||
chunk1.palette.push_back(i);
|
||||
}
|
||||
|
||||
chunk1.tiles = {
|
||||
0,0,0,0,0,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,
|
||||
0,0,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,
|
||||
0,0,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,6,6,6,5,5,5,
|
||||
0,0,6,6,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,1,6,6,6,
|
||||
6,6,5,5,5,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,1,1,6,6,6,
|
||||
6,6,5,5,5,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,1,1,1,6,6,6,
|
||||
6,6,5,5,5,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,1,1,1,1,5,5,5,
|
||||
5,5,6,6,6,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,1,1,1,1,1,5,5,5,
|
||||
5,5,6,6,6,2,2,2,2,2,0,0,5,5,5,6,6,6,5,5,5,6,6,6,1,1,1,1,1,5,5,5,
|
||||
5,5,6,6,6,2,2,2,2,2,0,0,5,5,5,6,6,6,5,5,5,6,6,6,1,1,1,1,1,6,6,6,
|
||||
4,1,5,5,5,2,2,2,2,2,0,0,5,5,5,6,6,6,5,5,5,6,6,6,1,1,1,1,1,6,6,6,
|
||||
1,1,5,5,5,2,2,2,2,2,3,3,3,3,3,3,3,3,3,5,5,5,5,5,1,1,1,1,1,6,6,6,
|
||||
1,1,5,5,5,2,2,2,2,3,3,3,3,3,3,3,3,3,2,5,6,6,6,5,1,1,1,1,1,5,5,5,
|
||||
1,1,6,6,6,2,2,2,3,3,3,3,3,3,3,3,3,2,2,5,6,5,6,5,1,1,1,1,1,5,5,5,
|
||||
1,1,6,6,6,2,2,3,3,3,3,3,3,3,3,3,2,2,2,5,6,6,6,5,1,1,1,1,1,5,5,5,
|
||||
1,1,6,6,6,2,3,3,3,3,3,3,3,3,3,2,2,2,2,5,5,5,5,5,1,1,1,1,1,6,6,6,
|
||||
1,1,5,5,5,6,6,6,5,5,5,6,6,6,2,2,2,2,2,6,6,6,6,6,1,1,1,1,1,6,6,6,
|
||||
1,1,5,5,5,6,6,6,5,5,5,6,6,6,2,2,2,2,2,6,5,5,5,6,1,1,1,1,1,6,6,6,
|
||||
1,1,5,5,5,6,6,6,5,5,5,6,6,6,2,2,2,2,2,6,5,6,5,6,1,1,1,1,1,5,5,5,
|
||||
1,4,4,4,4,4,4,4,4,4,1,5,5,5,2,2,2,2,2,6,5,5,5,6,1,1,1,1,1,5,5,5,
|
||||
4,4,4,4,4,4,4,4,4,1,1,5,5,5,2,2,2,2,2,6,6,6,6,6,1,1,1,1,1,5,5,5,
|
||||
4,4,4,4,4,4,4,4,1,1,1,5,5,5,2,2,2,2,2,5,5,5,5,5,1,1,1,1,1,6,6,6,
|
||||
4,4,4,4,4,4,4,1,1,1,1,6,6,6,2,2,2,2,2,5,6,6,6,5,1,1,1,1,1,6,6,6,
|
||||
4,4,4,4,4,4,1,1,1,1,1,6,6,6,2,2,2,2,2,5,6,5,6,5,1,1,1,1,1,6,6,6,
|
||||
6,6,6,5,5,5,1,1,1,1,1,6,6,6,2,2,2,2,2,5,6,6,6,5,1,1,1,1,1,5,5,5,
|
||||
6,6,6,5,5,5,1,1,1,1,1,5,5,5,2,2,2,2,2,5,5,5,5,5,1,1,1,1,1,5,5,5,
|
||||
6,6,6,5,5,5,1,1,1,1,1,5,5,5,2,2,2,2,2,6,6,6,6,6,1,1,1,1,1,5,5,5,
|
||||
0,6,6,6,6,6,1,1,1,1,1,5,5,5,2,2,2,2,2,6,5,5,5,6,1,1,1,1,1,6,6,6,
|
||||
0,6,5,5,5,6,1,1,1,1,1,6,6,6,2,2,2,2,2,6,5,6,5,6,1,1,1,1,1,6,6,6,
|
||||
0,6,5,6,5,6,1,1,1,1,1,6,6,6,2,2,2,2,2,6,5,5,5,6,1,1,1,1,1,6,6,6,
|
||||
0,6,5,5,5,6,1,1,1,1,1,6,6,6,2,2,2,2,2,6,6,6,6,6,1,1,1,1,1,5,5,5,
|
||||
0,6,6,6,6,6,1,1,1,1,1,5,5,5,2,2,2,2,2,5,5,5,5,5,1,1,1,1,1,5,5,5,
|
||||
};
|
||||
|
||||
|
||||
Chunk chunk2;
|
||||
for (int i = 0; i <= 6; i++){
|
||||
chunk2.palette.push_back(i);
|
||||
}
|
||||
|
||||
chunk2.tiles = {
|
||||
0,5,5,5,5,5,1,1,1,1,1,5,5,5,2,2,2,2,2,5,6,6,6,5,1,1,1,1,1,5,5,5,
|
||||
0,5,6,6,6,5,1,1,1,1,1,5,5,5,2,2,2,2,2,5,6,5,6,5,1,1,1,1,1,6,6,6,
|
||||
0,5,6,5,6,5,1,1,1,1,1,0,0,0,2,2,2,2,2,5,6,6,6,5,1,1,1,1,1,6,6,6,
|
||||
0,5,6,6,6,5,1,1,1,1,1,0,0,0,2,2,2,2,2,5,5,5,5,5,1,1,1,1,1,6,6,6,
|
||||
0,5,5,5,5,5,1,1,1,1,4,4,4,4,2,2,2,2,2,6,6,6,6,6,1,1,1,1,1,5,5,5,
|
||||
0,6,6,6,6,6,1,1,1,4,4,4,4,4,4,2,2,2,2,6,5,5,5,6,1,1,1,1,1,5,5,5,
|
||||
0,6,5,5,5,6,1,1,4,4,4,4,4,4,4,4,2,2,2,6,5,6,5,6,1,1,1,1,1,5,5,5,
|
||||
0,6,5,6,5,6,1,4,4,4,4,4,4,4,4,4,4,2,2,6,5,5,5,6,1,1,1,1,1,6,6,6,
|
||||
0,6,5,5,5,6,4,4,4,4,4,4,4,4,4,4,4,4,2,6,6,6,6,6,1,1,1,1,1,6,6,6,
|
||||
0,6,6,6,6,6,5,5,5,5,5,6,6,6,6,6,5,5,5,5,5,0,0,0,0,0,0,0,0,6,6,6,
|
||||
0,0,0,0,0,0,5,6,6,6,5,6,5,5,5,6,5,6,6,6,5,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,5,6,5,6,5,6,5,6,5,6,5,6,5,6,5,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,5,6,6,6,5,6,5,5,5,6,5,6,6,6,5,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,5,5,5,5,5,6,6,6,6,6,5,5,5,5,5,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,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,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,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,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,
|
||||
};
|
||||
|
||||
m_Chunks.insert({{0, 0}, std::make_shared<Chunk>(chunk0)});
|
||||
m_Chunks.insert({{1, 0}, std::make_shared<Chunk>(chunk1)});
|
||||
m_Chunks.insert({{1, 1}, std::make_shared<Chunk>(chunk2)});
|
||||
|
||||
|
||||
// blue map = same but with offset of 64
|
||||
|
||||
|
||||
m_Castles[(uint) TeamColor::Blue].x = 58 + 64;
|
||||
m_Castles[(uint) TeamColor::Blue].y = 43;
|
||||
|
||||
m_Spawns[(uint) TeamColor::Blue].direction = Direction::PositiveY;
|
||||
m_Spawns[(uint) TeamColor::Blue].x = 13 + 64;
|
||||
m_Spawns[(uint) TeamColor::Blue].y = 13;
|
||||
|
||||
m_SpawnColorPalette[(uint) TeamColor::Blue] = {0, 0, 255};
|
||||
|
||||
Chunk chunk01;
|
||||
chunk01.palette = {0, 1, 2, 3, 4, 7, 8};
|
||||
|
||||
chunk01.tiles = chunk0.tiles; // the tiles indicies are the same, only the palette has changed
|
||||
|
||||
|
||||
Chunk chunk11;
|
||||
chunk11.palette = {0, 1, 2, 3, 4, 7, 8};
|
||||
|
||||
chunk11.tiles = chunk1.tiles; // the tiles indicies are the same, only the palette has changed
|
||||
|
||||
|
||||
Chunk chunk21;
|
||||
chunk21.palette = {0, 1, 2, 3, 4, 7, 8};
|
||||
|
||||
chunk21.tiles = chunk2.tiles; // the tiles indicies are the same, only the palette has changed
|
||||
|
||||
m_Chunks.insert({{2, 0}, std::make_shared<Chunk>(chunk01)});
|
||||
m_Chunks.insert({{3, 0}, std::make_shared<Chunk>(chunk11)});
|
||||
m_Chunks.insert({{3, 1}, std::make_shared<Chunk>(chunk21)});
|
||||
|
||||
saveMap("tdmap.tdmap");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool World::saveMap(const std::string& fileName) const{
|
||||
protocol::WorldBeginDataPacket headerPacket(this);
|
||||
protocol::WorldDataPacket dataPacket(this);
|
||||
|
||||
DataBuffer mapHeaderCompressed = utils::Compress(headerPacket.Serialize());
|
||||
DataBuffer mapDataCompressed = utils::Compress(dataPacket.Serialize());
|
||||
|
||||
std::cout << "Header Packet Size : " << mapHeaderCompressed.GetSize() << std::endl;
|
||||
std::cout << "World Data Packet Size : " << mapDataCompressed.GetSize() << std::endl;
|
||||
|
||||
DataBuffer buffer = mapHeaderCompressed << mapDataCompressed;
|
||||
std::cout << "Total Size : " << buffer.GetSize() << std::endl;
|
||||
buffer.WriteFile(fileName);
|
||||
std::cout << "----- Map Saved ! -----\n";
|
||||
}
|
||||
|
||||
void World::tick(std::uint64_t delta){
|
||||
moveMobs(delta);
|
||||
}
|
||||
|
||||
void World::spawnMobAt(MobID id, MobType type, std::uint8_t level, PlayerID sender, float x, float y, Direction dir){
|
||||
MobPtr mob = MobFactory::createMob(id, type, level, sender);
|
||||
mob->setX(x);
|
||||
mob->setY(y);
|
||||
mob->setDirection(dir);
|
||||
m_Mobs.push_back(mob);
|
||||
}
|
||||
|
||||
void World::moveMobs(std::uint64_t delta){
|
||||
for(MobPtr mob : m_Mobs){
|
||||
TilePtr tile = getTile(mob->getX(), mob->getY());
|
||||
|
||||
Direction tileDir;
|
||||
|
||||
if(tile != nullptr && tile->getType() == TileType::Walk){
|
||||
WalkableTile* walkTile = dynamic_cast<WalkableTile*>(tile.get());
|
||||
mob->setDirection(walkTile->direction);
|
||||
}
|
||||
|
||||
float mobWalkSpeed = mob->getStats()->getMovementSpeed();
|
||||
|
||||
float walkAmount = mobWalkSpeed * ((float) delta / 1000.0f);
|
||||
|
||||
switch(mob->getDirection()){
|
||||
case Direction::NegativeX:{
|
||||
mob->setX(mob->getX() - walkAmount);
|
||||
break;
|
||||
}
|
||||
case Direction::PositiveX:{
|
||||
mob->setX(mob->getX() + walkAmount);
|
||||
break;
|
||||
}
|
||||
case Direction::NegativeY:{
|
||||
mob->setY(mob->getY() - walkAmount);
|
||||
break;
|
||||
}
|
||||
case Direction::PositiveY:{
|
||||
mob->setY(mob->getY() + walkAmount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Color& World::getTileColor(TilePtr tile) const{
|
||||
switch(tile->getType()){
|
||||
case TileType::Tower:{
|
||||
TowerTile* towerTile = (TowerTile*) tile.get();
|
||||
return m_TowerPlacePalette[towerTile->color_palette_ref];
|
||||
}
|
||||
case TileType::Walk:{
|
||||
return m_WalkablePalette;
|
||||
}
|
||||
case TileType::Decoration:{
|
||||
DecorationTile* towerTile = (DecorationTile*) tile.get();
|
||||
return m_DecorationPalette[towerTile->color_palette_ref];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return m_DecorationPalette[0];
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
47
src/game/client/Client.cpp
Normal file
47
src/game/client/Client.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "game/client/Client.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
void Client::connect(const std::string& address, std::uint16_t port){
|
||||
if(!m_Connexion.connect(address, port)){
|
||||
std::cout << "Failed to connect !\n";
|
||||
return;
|
||||
}
|
||||
m_Connected = true;
|
||||
}
|
||||
|
||||
void Client::selectTeam(game::TeamColor team){
|
||||
if(!m_Connected)
|
||||
return;
|
||||
|
||||
protocol::SelectTeamPacket packet(team);
|
||||
m_Connexion.sendPacket(&packet);
|
||||
}
|
||||
|
||||
void Client::closeConnection(){
|
||||
if(!m_Connected)
|
||||
return;
|
||||
|
||||
m_Connected = false;
|
||||
|
||||
protocol::DisconnectPacket packet;
|
||||
m_Connexion.sendPacket(&packet);
|
||||
}
|
||||
|
||||
void Client::tick(std::uint64_t delta){
|
||||
if(!m_Connected)
|
||||
return;
|
||||
m_Connected = m_Connexion.updateSocket();
|
||||
if(!m_Connected){
|
||||
std::cout << "Disconnected ! (Reason : " << m_Connexion.getDisconnectReason() << ")\n";
|
||||
}else{
|
||||
m_Game.tick(delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
49
src/game/client/ClientConnexion.cpp
Normal file
49
src/game/client/ClientConnexion.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "game/client/ClientConnexion.h"
|
||||
#include "render/WorldRenderer.h"
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
ClientConnexion::ClientConnexion(): Connexion(&m_Dispatcher){
|
||||
registerHandlers();
|
||||
}
|
||||
|
||||
void ClientConnexion::registerHandlers(){
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::KeepAlive, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::ConnectionInfo, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::Disconnect, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::ServerTps, this);
|
||||
}
|
||||
|
||||
void ClientConnexion::HandlePacket(protocol::KeepAlivePacket* packet){
|
||||
protocol::KeepAlivePacket keepAlivePacket(packet->getAliveID());
|
||||
sendPacket(&keepAlivePacket);
|
||||
}
|
||||
|
||||
void ClientConnexion::HandlePacket(protocol::ConnexionInfoPacket* packet){
|
||||
m_ConnectionID = packet->getConnectionID();
|
||||
login();
|
||||
}
|
||||
|
||||
void ClientConnexion::HandlePacket(protocol::ServerTpsPacket* packet){
|
||||
m_ServerTPS = packet->getTPS();
|
||||
m_Ping = utils::getTime() - packet->getPacketSendTime();
|
||||
}
|
||||
|
||||
void ClientConnexion::login(){
|
||||
td::protocol::PlayerLoginPacket loginPacket("Persson" + std::to_string(m_ConnectionID));
|
||||
sendPacket(&loginPacket);
|
||||
}
|
||||
|
||||
bool ClientConnexion::updateSocket(){
|
||||
return Connexion::updateSocket();
|
||||
}
|
||||
|
||||
void ClientConnexion::HandlePacket(protocol::DisconnectPacket* packet){
|
||||
m_DisconnectReason = packet->getReason();
|
||||
closeConnection();
|
||||
render::WorldRenderer::destroy();
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
94
src/game/client/ClientGame.cpp
Normal file
94
src/game/client/ClientGame.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "game/client/ClientGame.h"
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
//#include "game/Team.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace td {
|
||||
namespace client {
|
||||
|
||||
ClientGame::ClientGame(protocol::PacketDispatcher* dispatcher): protocol::PacketHandler(dispatcher), game::Game(&m_WorldClient){
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::ConnectionInfo, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerJoin, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerList, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerLeave, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::UpdatePlayerTeam, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateLobbyTime, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateGameState, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::UpdateMoney, this);
|
||||
}
|
||||
|
||||
ClientGame::~ClientGame(){
|
||||
GetDispatcher()->UnregisterHandler(this);
|
||||
}
|
||||
|
||||
void ClientGame::tick(std::uint64_t delta){
|
||||
game::Game::tick(delta);
|
||||
if (m_GameState == game::GameState::Lobby && m_Players.size() >= 2){
|
||||
m_LobbyTime -= delta;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientGame::HandlePacket(protocol::PlayerJoinPacket* packet){
|
||||
game::Player player(packet->getPlayerID());
|
||||
player.setName(packet->getPlayerName());
|
||||
|
||||
m_Players.insert({player.getID(), player});
|
||||
}
|
||||
|
||||
void ClientGame::HandlePacket(protocol::PlayerLeavePacket* packet){
|
||||
game::Player* player = &m_Players[packet->getPlayerID()];
|
||||
if (player->getTeamColor() != game::TeamColor::None){
|
||||
m_Teams[(std::size_t)player->getTeamColor()].removePlayer(player);
|
||||
}
|
||||
m_Players.erase(player->getID());
|
||||
}
|
||||
|
||||
void ClientGame::HandlePacket(protocol::PlayerListPacket* packet){
|
||||
for (auto pair : packet->getPlayers()){
|
||||
std::uint8_t playerID = pair.first;
|
||||
protocol::PlayerInfo playerInfo = pair.second;
|
||||
game::Player player(playerID);
|
||||
player.setName(playerInfo.name);
|
||||
player.setTeamColor(playerInfo.team);
|
||||
m_Players.insert({playerID, player});
|
||||
if (player.getTeamColor() != game::TeamColor::None){
|
||||
m_Teams[(std::size_t)player.getTeamColor()].addPlayer(&m_Players[playerID]);
|
||||
}
|
||||
}
|
||||
m_Player = &m_Players[m_ConnexionID];
|
||||
}
|
||||
|
||||
void ClientGame::HandlePacket(protocol::UpdatePlayerTeamPacket* packet){
|
||||
game::Player* player = &m_Players[packet->getPlayerID()];
|
||||
if (player->getTeamColor() == game::TeamColor::None){ //join a team
|
||||
getTeam(packet->getSelectedTeam()).addPlayer(player);
|
||||
}
|
||||
else if (packet->getSelectedTeam() == game::TeamColor::None){ // leave a team
|
||||
getTeam(player->getTeamColor()).removePlayer(player);
|
||||
player->setTeamColor(game::TeamColor::None);
|
||||
}
|
||||
else{ // change team
|
||||
getTeam(player->getTeamColor()).removePlayer(player);
|
||||
getTeam(packet->getSelectedTeam()).addPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientGame::HandlePacket(protocol::UpdateGameStatePacket* packet){
|
||||
setGameState(packet->getGameState());
|
||||
}
|
||||
|
||||
void ClientGame::HandlePacket(protocol::ConnexionInfoPacket* packet){
|
||||
m_ConnexionID = packet->getConnectionID();
|
||||
}
|
||||
|
||||
void ClientGame::HandlePacket(protocol::UpdateLobbyTimePacket* packet){
|
||||
m_LobbyTime = packet->getRemainingTime();
|
||||
}
|
||||
|
||||
void ClientGame::HandlePacket(protocol::UpdateMoneyPacket* packet){
|
||||
m_Player->setGold(packet->getGold());
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
30
src/game/client/WorldClient.cpp
Normal file
30
src/game/client/WorldClient.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "game/client/WorldClient.h"
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
#include "game/client/ClientGame.h"
|
||||
#include "render/WorldRenderer.h"
|
||||
|
||||
namespace td{
|
||||
namespace client{
|
||||
|
||||
WorldClient::WorldClient(ClientGame* game) : m_Game(game), game::World(game), protocol::PacketHandler(game->GetDispatcher()){
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::WorldBeginData, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::WorldData, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::SpawnMob, this);
|
||||
}
|
||||
|
||||
void WorldClient::HandlePacket(protocol::WorldBeginDataPacket* packet){
|
||||
loadMap(packet);
|
||||
}
|
||||
|
||||
void WorldClient::HandlePacket(protocol::WorldDataPacket* packet){
|
||||
loadMap(packet);
|
||||
render::WorldRenderer::init(this);
|
||||
}
|
||||
|
||||
void WorldClient::HandlePacket(protocol::SpawnMobPacket* packet){
|
||||
spawnMobAt(packet->getMobID(), packet->getMobType(), packet->getMobLevel(), packet->getSender(),
|
||||
packet->getMobX(), packet->getMobY(), packet->getMobDirection());
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace td
|
||||
76
src/game/server/Lobby.cpp
Normal file
76
src/game/server/Lobby.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "game/server/Lobby.h"
|
||||
#include "game/server/Server.h"
|
||||
|
||||
#include "misc/Time.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
/*static constexpr std::uint8_t timeNotifications[] = {
|
||||
2 * 60, // 2 min
|
||||
60 + 30, // 1 min 30 s
|
||||
60, // 1 min
|
||||
30, // 30 s
|
||||
15, // 15 s
|
||||
10, // 10 s
|
||||
5, // 5 s
|
||||
4, // 4 s
|
||||
3, // 3 s
|
||||
2, // 2 s
|
||||
1, // 1 s
|
||||
};*/
|
||||
|
||||
Lobby::Lobby(Server* server) : m_Server(server), m_Timer(1000, std::bind(&Lobby::sendTimeRemaining, this)){
|
||||
|
||||
}
|
||||
|
||||
void Lobby::tick(){
|
||||
if (m_GameStarted || m_StartTimerTime == 0)
|
||||
return;
|
||||
|
||||
if(utils::getTime() - m_StartTimerTime >= LOBBY_WAITING_TIME){
|
||||
protocol::UpdateGameStatePacket packet(game::GameState::Game);
|
||||
m_Server->broadcastPacket(&packet);
|
||||
m_GameStarted = true;
|
||||
m_Server->lauchGame();
|
||||
return;
|
||||
}
|
||||
|
||||
m_Timer.update();
|
||||
}
|
||||
|
||||
void Lobby::sendTimeRemaining(){
|
||||
protocol::UpdateLobbyTimePacket packet(LOBBY_WAITING_TIME - (utils::getTime() - m_StartTimerTime)); // converting second to millis
|
||||
m_Server->broadcastPacket(&packet);
|
||||
}
|
||||
|
||||
void Lobby::OnPlayerJoin(std::uint8_t playerID){
|
||||
if(m_GameStarted)
|
||||
return;
|
||||
std::cout << "(Server) Player Joined Lobby !\n";
|
||||
m_Players.push_back(playerID);
|
||||
if (m_Players.size() == 2){ // start timer if a second player join the match
|
||||
m_StartTimerTime = utils::getTime();
|
||||
m_Timer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Lobby::OnPlayerLeave(std::uint8_t playerID){
|
||||
if(m_GameStarted)
|
||||
return;
|
||||
std::cout << "(Server) Player Leaved Lobby !\n";
|
||||
auto it = std::find(m_Players.begin(), m_Players.end(), playerID);
|
||||
if (it == m_Players.end())
|
||||
return;
|
||||
m_Players.erase(it);
|
||||
if (m_Players.size() == 1){
|
||||
protocol::UpdateLobbyTimePacket packet(0);
|
||||
m_Server->broadcastPacket(&packet);
|
||||
m_StartTimerTime = 0; // reset timer if there is only one player left
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
105
src/game/server/Server.cpp
Normal file
105
src/game/server/Server.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "game/server/Server.h"
|
||||
#include <iostream>
|
||||
#include "protocol/PacketFactory.h"
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
Server::Server(const std::string& worldFilePath){
|
||||
m_Game.getWorld()->loadMapFromFile(worldFilePath);
|
||||
}
|
||||
|
||||
void Server::lauchGame(){
|
||||
m_Game.startGame();
|
||||
}
|
||||
|
||||
bool Server::start(std::uint16_t port){
|
||||
if(!m_Listener.listen(port, 10)){
|
||||
std::cout << "Failed to bind port " << port << " !\n";
|
||||
return false;
|
||||
}
|
||||
if(!m_Listener.setBlocking(false)){
|
||||
std::cout << "Failed to block server socket !\n";
|
||||
return false;
|
||||
}
|
||||
std::cout << "Server started at port " << port << " !\n";
|
||||
m_TickCounter.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Server::stop(){
|
||||
protocol::DisconnectPacket packet("Server closed");
|
||||
broadcastPacket(&packet);
|
||||
|
||||
m_Listener.close();
|
||||
m_Listener.destroy();
|
||||
|
||||
m_Connections.clear();
|
||||
getPlayers().clear();
|
||||
}
|
||||
|
||||
void Server::tick(std::uint64_t delta){
|
||||
accept();
|
||||
updateSockets();
|
||||
m_Lobby.tick();
|
||||
m_Game.tick(delta);
|
||||
if(m_TickCounter.update()){
|
||||
protocol::ServerTpsPacket packet(m_TickCounter.getTPS(), utils::getTime());
|
||||
broadcastPacket(&packet);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::accept(){
|
||||
static std::uint8_t newPlayerID = 0;
|
||||
network::TCPSocket newSocket;
|
||||
if (m_Listener.accept(newSocket)){
|
||||
ServerConnexion con(newSocket, newPlayerID);
|
||||
m_Connections.insert(std::move(ConnexionMap::value_type{newPlayerID, std::move(con)}));
|
||||
OnPlayerJoin(newPlayerID);
|
||||
m_Connections[newPlayerID].setServer(this);
|
||||
newPlayerID++;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::updateSockets(){
|
||||
std::int16_t closedConnexionID = -1;
|
||||
for (auto& connection : m_Connections){
|
||||
ServerConnexion& con = connection.second;
|
||||
if(con.getSocketStatus() != network::Socket::Status::Connected){
|
||||
closedConnexionID = connection.first;
|
||||
}else{
|
||||
con.updateSocket();
|
||||
}
|
||||
}
|
||||
if(closedConnexionID != -1){
|
||||
removeConnexion(closedConnexionID);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::broadcastPacket(protocol::Packet* packet){
|
||||
for (auto& connection : m_Connections){
|
||||
ServerConnexion& con = connection.second;
|
||||
con.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::removeConnexion(std::uint8_t connexionID){
|
||||
getPlayers().erase(getPlayers().find(connexionID));
|
||||
m_Connections.erase(connexionID);
|
||||
m_Lobby.OnPlayerLeave(connexionID);
|
||||
OnPlayerLeave(connexionID);
|
||||
}
|
||||
|
||||
void Server::OnPlayerJoin(std::uint8_t id){
|
||||
m_Lobby.OnPlayerJoin(id);
|
||||
|
||||
getPlayers().insert({id, game::Player{id}});
|
||||
}
|
||||
|
||||
void Server::OnPlayerLeave(std::uint8_t id){
|
||||
protocol::PlayerLeavePacket packet(id);
|
||||
broadcastPacket(&packet);
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
153
src/game/server/ServerConnexion.cpp
Normal file
153
src/game/server/ServerConnexion.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "game/server/ServerConnexion.h"
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
#include "protocol/PacketFactory.h"
|
||||
#include "game/server/Server.h"
|
||||
|
||||
#include "misc/Time.h"
|
||||
#include "misc/Random.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#define KEEP_ALIVE_TIMEOUT 10 * 1000 // 10s
|
||||
|
||||
namespace td{
|
||||
namespace server{
|
||||
|
||||
/*
|
||||
NEVER TRUST USER INPUT
|
||||
*/
|
||||
|
||||
|
||||
ServerConnexion::ServerConnexion(): m_Player(0){
|
||||
|
||||
}
|
||||
|
||||
ServerConnexion::ServerConnexion(network::TCPSocket& socket, std::uint8_t id) : Connexion::Connexion(&m_Dispatcher, socket), m_ID(id), m_Player(0){
|
||||
Connexion::updateSocket();
|
||||
}
|
||||
|
||||
ServerConnexion::ServerConnexion(ServerConnexion&& move) : Connexion::Connexion(std::move(move)), m_Server(move.m_Server),
|
||||
m_ID(move.m_ID), m_KeepAlive(move.m_KeepAlive), m_Player(move.m_Player){
|
||||
|
||||
move.m_Server = nullptr;
|
||||
registerHandlers();
|
||||
}
|
||||
|
||||
void ServerConnexion::registerHandlers(){
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::PlayerLogin, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::KeepAlive, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::SelectTeam, this);
|
||||
GetDispatcher()->RegisterHandler(protocol::PacketType::Disconnect, this);
|
||||
}
|
||||
|
||||
bool ServerConnexion::updateSocket(){
|
||||
checkKeepAlive();
|
||||
return Connexion::updateSocket();
|
||||
}
|
||||
|
||||
void ServerConnexion::checkKeepAlive(){
|
||||
std::uint64_t time = utils::getTime();
|
||||
if (time - m_KeepAlive.sendTime > KEEP_ALIVE_TIMEOUT){
|
||||
if (m_KeepAlive.recievedResponse){
|
||||
sendKeepAlive();
|
||||
}
|
||||
else{
|
||||
protocol::DisconnectPacket packet("Time out");
|
||||
sendPacket(&packet);
|
||||
closeConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConnexion::sendKeepAlive(){
|
||||
m_KeepAlive.keepAliveID = utils::getRandomNumber(UINT64_MAX);
|
||||
m_KeepAlive.recievedResponse = false;
|
||||
|
||||
protocol::KeepAlivePacket keepAlivePacket(m_KeepAlive.keepAliveID);
|
||||
sendPacket(&keepAlivePacket);
|
||||
|
||||
std::uint64_t time = utils::getTime();
|
||||
m_KeepAlive.sendTime = time;
|
||||
}
|
||||
|
||||
void ServerConnexion::HandlePacket(protocol::PlayerLoginPacket* packet){
|
||||
if (m_Player->getName().empty() && !packet->getPlayerName().empty()){
|
||||
m_Player->setName(packet->getPlayerName());
|
||||
|
||||
protocol::PlayerJoinPacket joinPacket(m_ID, m_Player->getName());
|
||||
m_Server->broadcastPacket(&joinPacket);
|
||||
|
||||
std::map<std::uint8_t, protocol::PlayerInfo> playerNames;
|
||||
for (const auto& pair : m_Server->getPlayers()){
|
||||
const game::Player& player = pair.second;
|
||||
if (!player.getName().empty()){
|
||||
protocol::PlayerInfo playerInfo;
|
||||
playerInfo.name = player.getName();
|
||||
playerInfo.team = player.getTeamColor();
|
||||
playerNames.insert({player.getID(), playerInfo});
|
||||
}
|
||||
}
|
||||
|
||||
protocol::PlayerListPacket listPacket(playerNames);
|
||||
sendPacket(&listPacket);
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConnexion::HandlePacket(protocol::SelectTeamPacket* packet){
|
||||
if(m_Server->getGame().getGameState() != game::GameState::Lobby)
|
||||
return;
|
||||
if((std::int8_t) packet->getSelectedTeam() >= -1 || (std::int8_t) packet->getSelectedTeam() <= 1){
|
||||
//m_Player->setTeamColor(packet->getSelectedTeam());
|
||||
if(m_Player->getTeamColor() == game::TeamColor::None){ //join a team
|
||||
m_Server->getGame().getTeam(packet->getSelectedTeam()).addPlayer(m_Player);
|
||||
}else if(packet->getSelectedTeam() == game::TeamColor::None){ // leave a team
|
||||
m_Server->getGame().getTeam(m_Player->getTeamColor()).removePlayer(m_Player);
|
||||
m_Player->setTeamColor(game::TeamColor::None);
|
||||
}else{ // change team
|
||||
m_Server->getGame().getTeam(m_Player->getTeamColor()).removePlayer(m_Player);
|
||||
m_Server->getGame().getTeam(packet->getSelectedTeam()).addPlayer(m_Player);
|
||||
}
|
||||
protocol::UpdatePlayerTeamPacket updateTeamPacket(m_ID, packet->getSelectedTeam());
|
||||
m_Server->broadcastPacket(&updateTeamPacket);
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConnexion::HandlePacket(protocol::KeepAlivePacket* packet){
|
||||
if(packet->getAliveID() == m_KeepAlive.keepAliveID)
|
||||
m_KeepAlive.recievedResponse = true;
|
||||
}
|
||||
|
||||
void ServerConnexion::HandlePacket(protocol::DisconnectPacket* packet){
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
void ServerConnexion::setServer(Server* server){
|
||||
m_Server = server;
|
||||
m_Player = &m_Server->getPlayers().at(m_ID);
|
||||
initConnection();
|
||||
sendKeepAlive();
|
||||
}
|
||||
|
||||
void ServerConnexion::initConnection(){
|
||||
protocol::UpdateGameStatePacket statePacket(m_Server->getGame().getGameState());
|
||||
sendPacket(&statePacket);
|
||||
|
||||
protocol::ConnexionInfoPacket conPacket(m_ID);
|
||||
sendPacket(&conPacket);
|
||||
|
||||
if (m_Server->getGame().getGameState() == game::GameState::Game){
|
||||
protocol::WorldBeginDataPacket headerDataPacket(m_Server->getGame().getWorld());
|
||||
protocol::WorldBeginDataPacket dataPacket(m_Server->getGame().getWorld());
|
||||
sendPacket(&headerDataPacket);
|
||||
sendPacket(&dataPacket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ServerConnexion::~ServerConnexion(){
|
||||
if (GetDispatcher() != nullptr)
|
||||
GetDispatcher()->UnregisterHandler(this);
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
57
src/game/server/ServerGame.cpp
Normal file
57
src/game/server/ServerGame.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include "game/server/ServerGame.h"
|
||||
#include "game/server/Server.h"
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
ServerGame::ServerGame(server::Server* server) : m_Server(server), m_ServerWorld(server, this), game::Game(&m_ServerWorld){
|
||||
|
||||
}
|
||||
|
||||
void ServerGame::tick(std::uint64_t delta){
|
||||
Game::tick(delta);
|
||||
m_GoldMineTimer.update();
|
||||
}
|
||||
|
||||
void ServerGame::startGame(){
|
||||
protocol::WorldBeginDataPacket headerMapData(m_World);
|
||||
m_Server->broadcastPacket(&headerMapData);
|
||||
|
||||
protocol::WorldDataPacket mapData(m_World);
|
||||
m_Server->broadcastPacket(&mapData);
|
||||
|
||||
m_GameState = game::GameState::Game;
|
||||
|
||||
balanceTeams();
|
||||
|
||||
m_ServerWorld.spawnMobs(game::MobType::Zombie, 1, 0, 12);
|
||||
}
|
||||
|
||||
void ServerGame::updateGoldMines(){
|
||||
for(auto& pair : m_Server->getPlayers()){
|
||||
game::Player* player = &pair.second;
|
||||
player->setGold(player->getGold() + player->getGoldPerSecond());
|
||||
protocol::UpdateMoneyPacket packet(player->getGold());
|
||||
m_Server->getConnexions()[player->getID()].sendPacket(&packet);
|
||||
}
|
||||
}
|
||||
|
||||
void ServerGame::balanceTeams(){
|
||||
for(auto playerInfo : m_Players){
|
||||
game::Player& player = playerInfo.second;
|
||||
if(player.getTeamColor() == game::TeamColor::None){
|
||||
game::Team& redTeam = getRedTeam();
|
||||
game::Team& blueTeam = getBlueTeam();
|
||||
if(blueTeam.getPlayerCount() > redTeam.getPlayerCount()){
|
||||
redTeam.addPlayer(&player);
|
||||
}else{
|
||||
blueTeam.addPlayer(&player);
|
||||
}
|
||||
protocol::UpdatePlayerTeamPacket packet(player.getID(), player.getTeamColor());
|
||||
m_Server->broadcastPacket(&packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
} // namespace td
|
||||
50
src/game/server/ServerWorld.cpp
Normal file
50
src/game/server/ServerWorld.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "game/server/ServerWorld.h"
|
||||
#include "game/server/Server.h"
|
||||
#include "misc/Random.h"
|
||||
|
||||
#define MOB_SPAWN_PRECISION 100.0f
|
||||
|
||||
namespace td {
|
||||
namespace server {
|
||||
|
||||
ServerWorld::ServerWorld(Server* server, ServerGame* game) : m_Server(server), game::World(game){
|
||||
|
||||
}
|
||||
|
||||
void ServerWorld::spawnMobs(game::MobType type, std::uint8_t level, game::PlayerID sender, std::uint8_t count){
|
||||
for (int i = 0; i < count; i++){
|
||||
game::TeamColor senderTeam = m_Game->getPlayers().at(sender).getTeamColor();
|
||||
game::Spawn* enemyMobSpawn;
|
||||
|
||||
if(senderTeam == game::TeamColor::Red){
|
||||
enemyMobSpawn = &m_Spawns[(std::size_t) game::TeamColor::Blue];
|
||||
}else{
|
||||
enemyMobSpawn = &m_Spawns[(std::size_t) game::TeamColor::Red];
|
||||
}
|
||||
|
||||
std::int32_t spawnCenterX = enemyMobSpawn->x;
|
||||
std::int32_t spawnCenterY = enemyMobSpawn->y;
|
||||
|
||||
std::int32_t minSpawnY = spawnCenterY - 2;
|
||||
std::int32_t maxSpawnY = spawnCenterY + 2;
|
||||
|
||||
std::int32_t minSpawnX = spawnCenterX - 2;
|
||||
std::int32_t maxSpawnX = spawnCenterX + 2;
|
||||
|
||||
std::uint64_t randomX = utils::getRandomNumber(std::abs(minSpawnX - maxSpawnX) * MOB_SPAWN_PRECISION);
|
||||
float mobX = (float) randomX / MOB_SPAWN_PRECISION + (float) minSpawnX;
|
||||
|
||||
std::uint64_t randomY = utils::getRandomNumber(std::abs(minSpawnY - maxSpawnY) * MOB_SPAWN_PRECISION);
|
||||
float mobY = (float) randomY / MOB_SPAWN_PRECISION + (float) minSpawnY;
|
||||
|
||||
spawnMobAt(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->direction);
|
||||
|
||||
protocol::SpawnMobPacket packet(m_CurrentMobID, type, level, sender, mobX, mobY, enemyMobSpawn->direction);
|
||||
m_Server->broadcastPacket(&packet);
|
||||
|
||||
m_CurrentMobID++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace td
|
||||
89
src/misc/Compression.cpp
Normal file
89
src/misc/Compression.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "misc/Compression.h"
|
||||
|
||||
#include <zlib.h>
|
||||
#include <cassert>
|
||||
|
||||
#define COMPRESSION_THRESHOLD 64
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
unsigned long inflate(const std::string& source, std::string& dest) {
|
||||
unsigned long size = dest.size();
|
||||
uncompress((Bytef*)&dest[0], &size, (const Bytef*)source.c_str(), source.length());
|
||||
return size;
|
||||
}
|
||||
|
||||
unsigned long deflate(const std::string& source, std::string& dest) {
|
||||
unsigned long size = source.length();
|
||||
dest.resize(size);
|
||||
|
||||
compress((Bytef*)&dest[0], &size, (const Bytef*)source.c_str(), source.length());
|
||||
dest.resize(size);
|
||||
return size;
|
||||
}
|
||||
|
||||
DataBuffer Compress(const DataBuffer& buffer) {
|
||||
std::string compressedData;
|
||||
DataBuffer packet;
|
||||
|
||||
if (buffer.GetSize() < COMPRESSION_THRESHOLD) {
|
||||
// Don't compress since it's a small packet
|
||||
std::uint64_t dataLength = 0;
|
||||
std::uint64_t packetLength = buffer.GetSize() + sizeof(dataLength);
|
||||
|
||||
packet << packetLength;
|
||||
packet << dataLength;
|
||||
packet << buffer;
|
||||
return packet;
|
||||
}
|
||||
|
||||
deflate(buffer.ToString(), compressedData);
|
||||
|
||||
std::uint64_t dataLength = buffer.GetSize();
|
||||
std::uint64_t packetLength = compressedData.length() + sizeof(dataLength);
|
||||
|
||||
packet << packetLength;
|
||||
packet << dataLength;
|
||||
packet << compressedData;
|
||||
return packet;
|
||||
}
|
||||
|
||||
DataBuffer Decompress(DataBuffer& buffer, std::size_t packetLength){
|
||||
std::uint64_t uncompressedLength;
|
||||
|
||||
buffer >> uncompressedLength;
|
||||
|
||||
std::size_t compressedLength = packetLength - sizeof(uncompressedLength);
|
||||
|
||||
if (uncompressedLength == 0) {
|
||||
// Uncompressed
|
||||
DataBuffer ret;
|
||||
buffer.ReadSome(ret, compressedLength);
|
||||
return ret;
|
||||
}
|
||||
|
||||
assert(buffer.GetReadOffset() + compressedLength <= buffer.GetSize());
|
||||
|
||||
std::string deflatedData;
|
||||
buffer.ReadSome(deflatedData, compressedLength);
|
||||
|
||||
std::string inflated;
|
||||
inflated.resize(uncompressedLength);
|
||||
|
||||
inflate(deflatedData, inflated);
|
||||
|
||||
assert(inflated.length() == uncompressedLength);
|
||||
return DataBuffer(inflated);
|
||||
}
|
||||
|
||||
DataBuffer Decompress(DataBuffer& buffer) {
|
||||
std::uint64_t packetLength;
|
||||
|
||||
buffer >> packetLength;
|
||||
|
||||
return Decompress(buffer, packetLength);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
17
src/misc/Random.cpp
Normal file
17
src/misc/Random.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "misc/Random.h"
|
||||
#include <random>
|
||||
#include <ctime>
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
void initRandomizer(){
|
||||
srand(time(0));
|
||||
}
|
||||
|
||||
std::uint64_t getRandomNumber(std::size_t max){
|
||||
return rand() % max;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
37
src/misc/Time.cpp
Normal file
37
src/misc/Time.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "misc/Time.h"
|
||||
#include <chrono>
|
||||
|
||||
namespace td {
|
||||
namespace utils {
|
||||
|
||||
void Timer::update(){
|
||||
m_InternalTime += getTime() - m_LastTime;
|
||||
if(m_InternalTime >= m_Interval){
|
||||
if(m_Function != nullptr)
|
||||
m_Function();
|
||||
m_InternalTime %= m_Interval;
|
||||
}
|
||||
m_LastTime = getTime();
|
||||
}
|
||||
|
||||
void Timer::update(std::uint64_t delta){
|
||||
m_InternalTime += delta;
|
||||
if(m_InternalTime >= m_Interval){
|
||||
if(m_Function != nullptr)
|
||||
m_Function();
|
||||
m_InternalTime %= m_Interval;
|
||||
}
|
||||
m_LastTime = getTime();
|
||||
}
|
||||
|
||||
void Timer::reset(){
|
||||
m_InternalTime = 0;
|
||||
m_LastTime = getTime();
|
||||
}
|
||||
|
||||
std::uint64_t getTime(){
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock().now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
} // namespace td
|
||||
93
src/network/DataBuffer.cpp
Normal file
93
src/network/DataBuffer.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
#include "network/DataBuffer.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace td {
|
||||
|
||||
DataBuffer::DataBuffer() { }
|
||||
DataBuffer::DataBuffer(const DataBuffer& other) : m_Buffer(other.m_Buffer), m_ReadOffset(other.m_ReadOffset) { }
|
||||
DataBuffer::DataBuffer(DataBuffer&& other) : m_Buffer(std::move(other.m_Buffer)), m_ReadOffset(std::move(other.m_ReadOffset)) { }
|
||||
DataBuffer::DataBuffer(const std::string& str) : m_Buffer(str.begin(), str.end()) { }
|
||||
DataBuffer::DataBuffer(const DataBuffer& other, std::size_t offset) {
|
||||
m_Buffer.reserve(other.GetSize() - offset);
|
||||
std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer));
|
||||
}
|
||||
|
||||
DataBuffer& DataBuffer::operator=(const DataBuffer& other) {
|
||||
m_Buffer = other.m_Buffer;
|
||||
m_ReadOffset = other.m_ReadOffset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataBuffer& DataBuffer::operator=(DataBuffer&& other) {
|
||||
m_Buffer = std::move(other.m_Buffer);
|
||||
m_ReadOffset = std::move(other.m_ReadOffset);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DataBuffer::SetReadOffset(std::size_t pos) {
|
||||
assert(pos <= GetSize());
|
||||
m_ReadOffset = pos;
|
||||
}
|
||||
|
||||
std::string DataBuffer::ToString() const {
|
||||
return std::string(m_Buffer.begin(), m_Buffer.end());
|
||||
}
|
||||
|
||||
std::size_t DataBuffer::GetSize() const { return m_Buffer.size(); }
|
||||
bool DataBuffer::IsEmpty() const { return m_Buffer.empty(); }
|
||||
std::size_t DataBuffer::GetRemaining() const {
|
||||
return m_Buffer.size() - m_ReadOffset;
|
||||
}
|
||||
|
||||
DataBuffer::iterator DataBuffer::begin() {
|
||||
return m_Buffer.begin();
|
||||
}
|
||||
DataBuffer::iterator DataBuffer::end() {
|
||||
return m_Buffer.end();
|
||||
}
|
||||
DataBuffer::const_iterator DataBuffer::begin() const {
|
||||
return m_Buffer.begin();
|
||||
}
|
||||
DataBuffer::const_iterator DataBuffer::end() const {
|
||||
return m_Buffer.end();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer) {
|
||||
for (unsigned char u : buffer)
|
||||
os << std::hex << std::setfill('0') << std::setw(2) << (int)u << " ";
|
||||
os << std::dec;
|
||||
return os;
|
||||
}
|
||||
|
||||
bool DataBuffer::ReadFile(const std::string& fileName){
|
||||
try{
|
||||
std::ifstream file(fileName);
|
||||
std::ostringstream ss;
|
||||
ss << file.rdbuf();
|
||||
const std::string& s = ss.str();
|
||||
m_Buffer = DataBuffer::Data(s.begin(), s.end());
|
||||
m_ReadOffset = 0;
|
||||
}catch(std::exception& e){
|
||||
std::cerr << "Failed to read file \"" << fileName << "\" reason : " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return m_Buffer.size() > 0;
|
||||
}
|
||||
|
||||
bool DataBuffer::WriteFile(const std::string& fileName){
|
||||
try{
|
||||
std::ofstream file(fileName);
|
||||
file.write((const char*) m_Buffer.data(), m_Buffer.size());
|
||||
file.flush();
|
||||
}catch(std::exception& e){
|
||||
std::cerr << "Failed to write file \"" << fileName << "\" reason : " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // td
|
||||
|
||||
127
src/network/IPAddress.cpp
Normal file
127
src/network/IPAddress.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "network/IPAddress.h"
|
||||
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
|
||||
const std::regex IPRegex(R":(^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$):");
|
||||
const std::wregex IPRegexW(LR":(^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$):");
|
||||
|
||||
} // ns
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
/* Create an invalid address */
|
||||
IPAddress::IPAddress() noexcept
|
||||
: m_Valid(false), m_Address(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* Initialize by string IP */
|
||||
IPAddress::IPAddress(const std::string& ip)
|
||||
: m_Valid(false), m_Address(0)
|
||||
{
|
||||
std::sregex_iterator begin(ip.begin(), ip.end(), IPRegex);
|
||||
std::sregex_iterator end;
|
||||
|
||||
if (begin == end) return; // m_Valid = false
|
||||
|
||||
std::smatch match = *begin;
|
||||
|
||||
int octet1 = atoi(std::string(match[1]).c_str());
|
||||
int octet2 = atoi(std::string(match[2]).c_str());
|
||||
int octet3 = atoi(std::string(match[3]).c_str());
|
||||
int octet4 = atoi(std::string(match[4]).c_str());
|
||||
|
||||
m_Address = (octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4;
|
||||
m_Valid = true;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const std::wstring& ip)
|
||||
: m_Address(0), m_Valid(false)
|
||||
{
|
||||
std::wsregex_iterator begin(ip.begin(), ip.end(), IPRegexW);
|
||||
std::wsregex_iterator end;
|
||||
|
||||
if (begin == end) return; // m_Valid = false
|
||||
|
||||
std::wsmatch match = *begin;
|
||||
|
||||
int octet1 = std::stoi(match[1]);
|
||||
int octet2 = std::stoi(match[2]);
|
||||
int octet3 = std::stoi(match[3]);
|
||||
int octet4 = std::stoi(match[4]);
|
||||
|
||||
m_Address = (octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4;
|
||||
m_Valid = true;
|
||||
}
|
||||
|
||||
/* Initialize by octets */
|
||||
IPAddress::IPAddress(uint8_t octet1, uint8_t octet2, uint8_t octet3, uint8_t octet4) noexcept
|
||||
: m_Valid(true)
|
||||
{
|
||||
m_Address = (octet1 << 24) | (octet2 << 16) | (octet3 << 8) | octet4;
|
||||
}
|
||||
|
||||
/* Get the specific octet */
|
||||
uint8_t IPAddress::GetOctet(uint8_t num) const {
|
||||
if (num == 0 || num > 4) throw std::invalid_argument("Invalid argument in IPAddress:GetOctet.");
|
||||
|
||||
return (m_Address >> (8 * (4 - num))) & 0xFF;
|
||||
}
|
||||
|
||||
/* Set the specific octet. 1-4 */
|
||||
void IPAddress::SetOctet(uint8_t num, uint8_t value) {
|
||||
if (num == 0 || num > 4) throw std::invalid_argument("Invalid argument in IPAddress:GetOctet.");
|
||||
uint8_t octets[4];
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
octets[i] = (m_Address >> ((3 - i) * 8)) & 0xFF;
|
||||
|
||||
octets[num - 1] = value;
|
||||
|
||||
m_Address = (octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3];
|
||||
}
|
||||
|
||||
IPAddress IPAddress::LocalAddress() {
|
||||
return IPAddress(127, 0, 0, 1);
|
||||
}
|
||||
|
||||
std::string IPAddress::ToString() const {
|
||||
std::stringstream ss;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i != 0) ss << ".";
|
||||
ss << static_cast<unsigned int>(GetOctet(i + 1));
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const IPAddress& right) {
|
||||
return m_Address == right.m_Address;
|
||||
}
|
||||
|
||||
bool IPAddress::operator!=(const IPAddress& right) {
|
||||
return !(*this == right);
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(bool b) {
|
||||
return IsValid() == b;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const IPAddress& addr) {
|
||||
return os << addr.ToString();
|
||||
}
|
||||
|
||||
std::wostream& operator<<(std::wostream& os, const IPAddress& addr) {
|
||||
std::string str = addr.ToString();
|
||||
return os << std::wstring(str.begin(), str.end());
|
||||
}
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
67
src/network/Network.cpp
Normal file
67
src/network/Network.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "network/Network.h"
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
class NetworkInitializer {
|
||||
public:
|
||||
NetworkInitializer();
|
||||
~NetworkInitializer();
|
||||
|
||||
NetworkInitializer(const NetworkInitializer& rhs) = delete;
|
||||
NetworkInitializer& operator=(const NetworkInitializer& rhs) = delete;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
NetworkInitializer::NetworkInitializer() {
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
}
|
||||
NetworkInitializer::~NetworkInitializer() {
|
||||
WSACleanup();
|
||||
}
|
||||
#else
|
||||
NetworkInitializer::NetworkInitializer() {
|
||||
|
||||
}
|
||||
NetworkInitializer::~NetworkInitializer() {
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
NetworkInitializer initializer;
|
||||
|
||||
IPAddresses Dns::Resolve(const std::string& host) {
|
||||
IPAddresses list;
|
||||
addrinfo hints = { 0 }, *addresses;
|
||||
|
||||
//hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
getaddrinfo(host.c_str(), NULL, &hints, &addresses);
|
||||
|
||||
for (addrinfo *p = addresses; p != NULL; p = p->ai_next) {
|
||||
#ifdef _WIN32
|
||||
//wchar_t straddr[35];
|
||||
//char straddr[512];
|
||||
//DWORD len;
|
||||
//WSAAddressToStringA(p->ai_addr, p->ai_addrlen, NULL, straddr, &len);
|
||||
|
||||
char* straddr = inet_ntoa(((sockaddr_in*)p->ai_addr)->sin_addr);
|
||||
|
||||
#else
|
||||
char straddr[512];
|
||||
|
||||
inet_ntop(p->ai_family, &((sockaddr_in*)p->ai_addr)->sin_addr, straddr, sizeof(straddr));
|
||||
#endif
|
||||
|
||||
list.push_back(IPAddress(straddr));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
79
src/network/Socket.cpp
Normal file
79
src/network/Socket.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "network/Socket.h"
|
||||
|
||||
#include "network/IPAddress.h"
|
||||
#include "network/Network.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define ioctl ioctlsocket
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
Socket::Socket(Type type)
|
||||
: m_Handle(INVALID_SOCKET),
|
||||
m_Type(type),
|
||||
m_Blocking(false),
|
||||
m_Status(Disconnected)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Socket::~Socket() {
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
bool Socket::SetBlocking(bool block) {
|
||||
unsigned long mode = block ? 0 : 1;
|
||||
|
||||
if(ioctl(m_Handle, FIONBIO, &mode) < 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Blocking = block;
|
||||
}
|
||||
|
||||
bool Socket::IsBlocking() const noexcept {
|
||||
return m_Blocking;
|
||||
}
|
||||
|
||||
Socket::Type Socket::GetType() const noexcept {
|
||||
return m_Type;
|
||||
}
|
||||
|
||||
SocketHandle Socket::GetHandle() const noexcept {
|
||||
return m_Handle;
|
||||
}
|
||||
|
||||
void Socket::SetStatus(Socket::Status status) {
|
||||
m_Status = status;
|
||||
}
|
||||
|
||||
Socket::Status Socket::GetStatus() const noexcept {
|
||||
return m_Status;
|
||||
}
|
||||
|
||||
bool Socket::Connect(const std::string& ip, uint16_t port) {
|
||||
IPAddress addr(ip);
|
||||
return Connect(addr, port);
|
||||
}
|
||||
|
||||
std::size_t Socket::Send(const std::string& data) {
|
||||
return this->Send(reinterpret_cast<const unsigned char*>(data.c_str()), data.length());
|
||||
}
|
||||
|
||||
std::size_t Socket::Send(DataBuffer& buffer) {
|
||||
std::string data = buffer.ToString();
|
||||
return this->Send(reinterpret_cast<const unsigned char*>(data.c_str()), data.length());
|
||||
}
|
||||
|
||||
void Socket::Disconnect() {
|
||||
if (m_Handle != INVALID_SOCKET)
|
||||
closesocket(m_Handle);
|
||||
m_Status = Disconnected;
|
||||
}
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
71
src/network/TCPListener.cpp
Normal file
71
src/network/TCPListener.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "network/TCPListener.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#define closesocket close
|
||||
#define SD_BOTH SHUT_RDWR
|
||||
#define ioctlsocket ioctl
|
||||
#endif
|
||||
|
||||
namespace td{
|
||||
namespace network{
|
||||
|
||||
TCPListener::TCPListener(){}
|
||||
|
||||
TCPListener::~TCPListener(){
|
||||
destroy();
|
||||
}
|
||||
|
||||
bool TCPListener::listen(uint16_t port, int maxConnections){
|
||||
if ((m_Handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
|
||||
return false;
|
||||
|
||||
struct sockaddr_in address;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
address.sin_port = htons(port);
|
||||
|
||||
if (::bind(m_Handle, reinterpret_cast<sockaddr*>(&address), sizeof(address)) < 0)
|
||||
return false;
|
||||
|
||||
if (::listen(m_Handle, maxConnections) < 0)
|
||||
return false;
|
||||
|
||||
m_Port = port;
|
||||
m_MaxConnections = maxConnections;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TCPListener::accept(TCPSocket& newSocket){
|
||||
int addrlen = sizeof(newSocket.m_RemoteAddr);
|
||||
if ((newSocket.m_Handle = ::accept(m_Handle, reinterpret_cast<sockaddr*>(&newSocket.m_RemoteAddr),
|
||||
reinterpret_cast<socklen_t*>(&addrlen))) < 0)
|
||||
return false;
|
||||
newSocket.SetStatus(Socket::Status::Connected);
|
||||
newSocket.SetBlocking(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TCPListener::destroy(){
|
||||
if(m_Handle != INVALID_SOCKET)
|
||||
::closesocket(m_Handle);
|
||||
}
|
||||
|
||||
bool TCPListener::close(){
|
||||
if (::shutdown(m_Handle, SD_BOTH) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TCPListener::setBlocking(bool blocking){
|
||||
unsigned long mode = blocking ? 0 : 1;
|
||||
|
||||
if(ioctlsocket(m_Handle, FIONBIO, &mode) < 0){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace network
|
||||
} // namespace td
|
||||
141
src/network/TCPSocket.cpp
Normal file
141
src/network/TCPSocket.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "network/TCPSocket.h"
|
||||
#include "misc/Compression.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WOULDBLOCK WSAEWOULDBLOCK
|
||||
#define MSG_DONTWAIT 0
|
||||
#else
|
||||
#define WOULDBLOCK EWOULDBLOCK
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
TCPSocket::TCPSocket()
|
||||
: Socket(Socket::TCP), m_Port(0)
|
||||
{
|
||||
m_Handle = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
bool TCPSocket::Connect(const IPAddress& address, unsigned short port) {
|
||||
if (this->GetStatus() == Connected)
|
||||
return true;
|
||||
|
||||
struct addrinfo hints = { 0 }, *result = nullptr;
|
||||
|
||||
if ((m_Handle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
|
||||
return false;
|
||||
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
if (getaddrinfo(address.ToString().c_str(), std::to_string(port).c_str(), &hints, &result) != 0)
|
||||
return false;
|
||||
|
||||
struct addrinfo* ptr = nullptr;
|
||||
for (ptr = result; ptr != nullptr; ptr = ptr->ai_next) {
|
||||
struct sockaddr_in* sockaddr = (struct sockaddr_in*)ptr->ai_addr;
|
||||
if (::connect(m_Handle, (struct sockaddr*)sockaddr, sizeof(struct sockaddr_in)) != 0)
|
||||
continue;
|
||||
m_RemoteAddr = *sockaddr;
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
if (!ptr)
|
||||
return false;
|
||||
|
||||
this->SetStatus(Connected);
|
||||
m_RemoteIP = address;
|
||||
m_Port = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t TCPSocket::Send(const unsigned char* data, size_t size){
|
||||
if (this->GetStatus() != Connected)
|
||||
return 0;
|
||||
|
||||
size_t sent = 0;
|
||||
|
||||
while (sent < size) {
|
||||
int cur = ::send(m_Handle, reinterpret_cast<const char*>(data + sent), size - sent, 0);
|
||||
if (cur <= 0) {
|
||||
Disconnect();
|
||||
return 0;
|
||||
}
|
||||
sent += cur;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
std::size_t TCPSocket::Receive(DataBuffer& buffer, std::size_t amount) {
|
||||
buffer.Resize(amount);
|
||||
buffer.SetReadOffset(0);
|
||||
|
||||
int recvAmount = ::recv(m_Handle, (char*)&buffer[0], amount, 0);
|
||||
if (recvAmount <= 0) {
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
if (err == WOULDBLOCK) {
|
||||
buffer.Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Disconnect();
|
||||
buffer.Clear();
|
||||
return 0;
|
||||
}
|
||||
buffer.Resize(recvAmount);
|
||||
return recvAmount;
|
||||
}
|
||||
|
||||
DataBuffer TCPSocket::Receive(std::size_t amount) {
|
||||
std::unique_ptr<char[]> buf(new char[amount]);
|
||||
|
||||
int received = ::recv(m_Handle, buf.get(), amount, 0);
|
||||
|
||||
if (received <= 0) {
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
if (err == WOULDBLOCK)
|
||||
return DataBuffer();
|
||||
|
||||
Disconnect();
|
||||
return DataBuffer();
|
||||
}
|
||||
|
||||
return DataBuffer(std::string(buf.get(), received));
|
||||
}
|
||||
|
||||
TCPSocket::TCPSocket(TCPSocket&& other) : Socket(TCP){
|
||||
m_Handle = other.m_Handle;
|
||||
m_Port = other.m_Port;
|
||||
m_RemoteAddr = other.m_RemoteAddr;
|
||||
m_RemoteIP = other.m_RemoteIP;
|
||||
SetStatus(other.GetStatus());
|
||||
SetBlocking(other.IsBlocking());
|
||||
other.m_Handle = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void SendPacket(const DataBuffer& data, network::TCPSocket& socket){
|
||||
DataBuffer compressed = utils::Compress(data);
|
||||
/*DataBuffer buffer;
|
||||
buffer.Reserve(sizeof(std::size_t) + data.GetSize());
|
||||
buffer << data.GetSize() << data;*/
|
||||
socket.Send((const std::uint8_t*) compressed.ToString().data(), compressed.GetSize());
|
||||
}
|
||||
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
77
src/network/UDPSocket.cpp
Normal file
77
src/network/UDPSocket.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "network/UDPSocket.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WOULDBLOCK WSAEWOULDBLOCK
|
||||
#else
|
||||
#define WOULDBLOCK EWOULDBLOCK
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
namespace network {
|
||||
|
||||
UDPSocket::UDPSocket()
|
||||
: Socket(Socket::UDP), m_Port(0)
|
||||
{
|
||||
m_Handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
}
|
||||
|
||||
bool UDPSocket::Connect(const IPAddress& address, unsigned short port) {
|
||||
if (this->GetStatus() == Connected)
|
||||
return true;
|
||||
|
||||
m_RemoteAddr.sin_port = htons(port);
|
||||
m_RemoteAddr.sin_family = AF_INET;
|
||||
m_RemoteAddr.sin_addr.s_addr = inet_addr(address.ToString().c_str());
|
||||
|
||||
this->SetStatus(Connected);
|
||||
m_RemoteIP = IPAddress(address);
|
||||
m_Port = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t UDPSocket::Send(const unsigned char* data, size_t size) {
|
||||
size_t sent = 0;
|
||||
|
||||
if (this->GetStatus() != Connected)
|
||||
return 0;
|
||||
|
||||
while (sent < size) {
|
||||
int cur = sendto(m_Handle, reinterpret_cast<const char*>(data + sent), size - sent, 0,
|
||||
reinterpret_cast<sockaddr*>(&m_RemoteAddr), sizeof(sockaddr_in));
|
||||
if (cur <= 0) {
|
||||
Disconnect();
|
||||
return 0;
|
||||
}
|
||||
sent += cur;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
DataBuffer UDPSocket::Receive(std::size_t amount) {
|
||||
std::unique_ptr<char[]> buf(new char[amount]);
|
||||
socklen_t slen = sizeof(sockaddr_in);
|
||||
|
||||
int received = recvfrom(m_Handle, buf.get(), amount, 0,
|
||||
reinterpret_cast<sockaddr*>(&m_RemoteAddr), &slen);
|
||||
|
||||
if (received <= 0) {
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
int err = WSAGetLastError();
|
||||
#else
|
||||
int err = errno;
|
||||
#endif
|
||||
if (err == WOULDBLOCK)
|
||||
return DataBuffer(std::string(buf.get(), received));
|
||||
|
||||
Disconnect();
|
||||
return DataBuffer();
|
||||
}
|
||||
|
||||
return DataBuffer(std::string(buf.get(), received));
|
||||
}
|
||||
|
||||
} // ns network
|
||||
} // ns mc
|
||||
37
src/protocol/PacketDispatcher.cpp
Normal file
37
src/protocol/PacketDispatcher.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "protocol/PacketDispatcher.h"
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
void PacketDispatcher::RegisterHandler(PacketType type, PacketHandler* handler){
|
||||
auto found = std::find(m_Handlers[type].begin(), m_Handlers[type].end(), handler);
|
||||
if (found == m_Handlers[type].end())
|
||||
m_Handlers[type].push_back(handler);
|
||||
}
|
||||
|
||||
void PacketDispatcher::UnregisterHandler(PacketType type, PacketHandler* handler){
|
||||
auto found = std::find(m_Handlers[type].begin(), m_Handlers[type].end(), handler);
|
||||
if (found != m_Handlers[type].end())
|
||||
m_Handlers[type].erase(found);
|
||||
}
|
||||
|
||||
void PacketDispatcher::UnregisterHandler(PacketHandler* handler){
|
||||
for (auto& pair : m_Handlers){
|
||||
if (pair.second.empty()) continue;
|
||||
|
||||
PacketType type = pair.first;
|
||||
|
||||
m_Handlers[type].erase(std::remove(m_Handlers[type].begin(), m_Handlers[type].end(), handler), m_Handlers[type].end());
|
||||
}
|
||||
}
|
||||
|
||||
void PacketDispatcher::Dispatch(Packet* packet){
|
||||
if (!packet) return;
|
||||
|
||||
PacketType type = packet->getType();
|
||||
for (PacketHandler* handler : m_Handlers[type])
|
||||
packet->Dispatch(handler);
|
||||
}
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
39
src/protocol/PacketFactory.cpp
Normal file
39
src/protocol/PacketFactory.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "protocol/PacketFactory.h"
|
||||
#include <map>
|
||||
#include <functional>
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
namespace PacketFactory{
|
||||
|
||||
using PacketCreator = std::function<Packet*()>;
|
||||
|
||||
static std::map<PacketType, PacketCreator> packets = {
|
||||
{PacketType::PlayerLogin, []() -> Packet* {return new PlayerLoginPacket();} },
|
||||
{PacketType::WorldBeginData, []() -> Packet* {return new WorldBeginDataPacket();} },
|
||||
{PacketType::WorldData, []() -> Packet* {return new WorldDataPacket();} },
|
||||
{PacketType::KeepAlive, []() -> Packet* {return new KeepAlivePacket();} },
|
||||
{PacketType::UpdateMoney, []() -> Packet* {return new UpdateMoneyPacket();} },
|
||||
{PacketType::UpdateEXP, []() -> Packet* {return new UpdateExpPacket();} },
|
||||
{PacketType::UpdateLobbyTime, []() -> Packet* {return new UpdateLobbyTimePacket(); } },
|
||||
{PacketType::UpdateGameState, []() -> Packet* {return new UpdateGameStatePacket(); } },
|
||||
{PacketType::PlayerList, []() -> Packet* {return new PlayerListPacket(); } },
|
||||
{PacketType::PlayerJoin, []() -> Packet* {return new PlayerJoinPacket(); } },
|
||||
{PacketType::PlayerLeave, []() -> Packet* {return new PlayerLeavePacket(); } },
|
||||
{PacketType::ConnectionInfo, []() -> Packet* {return new ConnexionInfoPacket(); } },
|
||||
{PacketType::SelectTeam, []() -> Packet* {return new SelectTeamPacket(); } },
|
||||
{PacketType::UpdatePlayerTeam, []() -> Packet* {return new UpdatePlayerTeamPacket(); } },
|
||||
{PacketType::Disconnect, []() -> Packet* {return new DisconnectPacket(); } },
|
||||
{PacketType::ServerTps, []() -> Packet* {return new ServerTpsPacket(); } },
|
||||
{PacketType::SpawnMob, []() -> Packet* {return new SpawnMobPacket(); } },
|
||||
};
|
||||
|
||||
Packet* createPacket(PacketType type, DataBuffer& buffer){
|
||||
Packet* packet = packets[type]();
|
||||
packet->Deserialize(buffer);
|
||||
return packet;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
409
src/protocol/Protocol.cpp
Normal file
409
src/protocol/Protocol.cpp
Normal file
@@ -0,0 +1,409 @@
|
||||
#include "protocol/PacketHandler.h"
|
||||
#include <cmath>
|
||||
|
||||
#define REGISTER_DISPATCH_CLASS(className) void className::Dispatch(PacketHandler* handler){handler->HandlePacket(this);}
|
||||
|
||||
namespace td {
|
||||
namespace protocol {
|
||||
|
||||
const int BITS_IN_BYTE = 8;
|
||||
const int BITS_IN_LONG = BITS_IN_BYTE * sizeof(std::uint64_t);
|
||||
|
||||
unsigned int countBits(unsigned int number){
|
||||
// log function in base 2
|
||||
// take only integer part
|
||||
return (int)std::log2(number) + 1;
|
||||
}
|
||||
|
||||
DataBuffer& operator<<(DataBuffer& buffer, game::TilePtr tile){
|
||||
buffer << tile->getType();
|
||||
switch (tile->getType()){
|
||||
case game::TileType::Tower:{
|
||||
const std::shared_ptr<game::TowerTile>& towerTile = (const std::shared_ptr<game::TowerTile>&) tile;
|
||||
buffer << towerTile->color_palette_ref << towerTile->team_owner;
|
||||
break;
|
||||
}
|
||||
case game::TileType::Walk:{
|
||||
const std::shared_ptr<game::WalkableTile>& walkTile = (const std::shared_ptr<game::WalkableTile>&) tile;
|
||||
buffer << walkTile->direction;
|
||||
break;
|
||||
}
|
||||
case game::TileType::Decoration:{
|
||||
const std::shared_ptr<game::DecorationTile>& decoTile = (const std::shared_ptr<game::DecorationTile>&) tile;
|
||||
buffer << decoTile->color_palette_ref;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
DataBuffer& operator>>(DataBuffer& buffer, game::TilePtr& tile){
|
||||
game::TileType tileType;
|
||||
buffer >> tileType;
|
||||
switch (tileType){
|
||||
case game::TileType::Tower:{
|
||||
std::shared_ptr<game::TowerTile> tilePtr = std::make_shared<game::TowerTile>();
|
||||
buffer >> tilePtr->color_palette_ref >> tilePtr->team_owner;
|
||||
tile = tilePtr;
|
||||
break;
|
||||
}
|
||||
case game::TileType::Walk:{
|
||||
std::shared_ptr<game::WalkableTile> tilePtr = std::make_shared<game::WalkableTile>();
|
||||
buffer >> tilePtr->direction;
|
||||
tile = tilePtr;
|
||||
break;
|
||||
}
|
||||
case game::TileType::Decoration:{
|
||||
std::shared_ptr<game::DecorationTile> tilePtr = std::make_shared<game::DecorationTile>();
|
||||
buffer >> tilePtr->color_palette_ref;
|
||||
tile = tilePtr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
DataBuffer PlayerLoginPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_PlayerName;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerLoginPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_PlayerName;
|
||||
}
|
||||
|
||||
DataBuffer WorldBeginDataPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
const game::TowerTileColorPalette towerTilePalette = m_World->getTowerTileColorPalette();
|
||||
const std::vector<game::Color>& decoTilePalette = m_World->getDecorationPalette();
|
||||
|
||||
data << getID() << towerTilePalette << m_World->getWalkableTileColor()
|
||||
<< (std::uint16_t)decoTilePalette.size();
|
||||
|
||||
// deco color palette
|
||||
std::size_t bufferSize = data.GetSize();
|
||||
data.Resize(bufferSize + decoTilePalette.size() * sizeof(game::Color));
|
||||
|
||||
memcpy((void*)data.data() + bufferSize, decoTilePalette.data(), decoTilePalette.size() * sizeof(game::Color));
|
||||
|
||||
const game::Spawn& redSpawn = m_World->getRedSpawn(), blueSpawn = m_World->getBlueSpawn();
|
||||
const game::TeamCastle& redCastle = m_World->getRedCastle(), blueCastle = m_World->getBlueCastle();
|
||||
|
||||
data << redSpawn << redCastle;
|
||||
data << blueSpawn << blueCastle;
|
||||
|
||||
// tile palette
|
||||
data << m_World->getTilePalette().size();
|
||||
|
||||
for (game::TilePtr tile : m_World->getTilePalette()){
|
||||
data << tile;
|
||||
}
|
||||
|
||||
data << m_World->getSpawnColors();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void WorldBeginDataPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_TowerPlacePalette >> m_WalkablePalette;
|
||||
|
||||
std::uint16_t decoPaletteSize;
|
||||
data >> decoPaletteSize;
|
||||
|
||||
std::size_t decoPalletteSizeByte = decoPaletteSize * sizeof(game::Color);
|
||||
|
||||
m_DecorationPalette.reserve(decoPaletteSize);
|
||||
|
||||
memcpy((void*)m_DecorationPalette.data(), data.data() + data.GetReadOffset(), decoPalletteSizeByte);
|
||||
|
||||
data.SetReadOffset(data.GetReadOffset() + decoPalletteSizeByte);
|
||||
|
||||
data >> m_RedSpawn >> m_RedTower;
|
||||
data >> m_BlueSpawn >> m_BlueTower;
|
||||
|
||||
std::size_t tilePaletteSize;
|
||||
data >> tilePaletteSize;
|
||||
|
||||
m_TilePalette.reserve(tilePaletteSize);
|
||||
|
||||
for (int i = 0; i < tilePaletteSize; i++){
|
||||
game::TilePtr tile;
|
||||
data >> tile;
|
||||
m_TilePalette.push_back(tile);
|
||||
}
|
||||
|
||||
data >> m_SpawnColorPalette;
|
||||
}
|
||||
|
||||
typedef std::vector<uint64_t> ChunkPackedData;
|
||||
|
||||
DataBuffer WorldDataPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_World->getChunks().size();
|
||||
for (const auto& pair : m_World->getChunks()){
|
||||
game::ChunkCoord coords = pair.first;
|
||||
game::ChunkPtr chunk = pair.second;
|
||||
|
||||
data << coords.first << coords.second << chunk->palette.size();
|
||||
|
||||
std::size_t bufferSize = data.GetSize();
|
||||
data.Resize(data.GetSize() + chunk->palette.size() * sizeof(game::ChunkPalette::value_type));
|
||||
memcpy((void*)data.data() + bufferSize, chunk->palette.data(), chunk->palette.size() * sizeof(game::ChunkPalette::value_type));
|
||||
|
||||
std::uint8_t bitsPerTile = countBits(chunk->palette.size());
|
||||
|
||||
game::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1);
|
||||
|
||||
ChunkPackedData chunkData(game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0);
|
||||
|
||||
for (int tileNumber = 0; tileNumber < game::Chunk::ChunkSize; tileNumber++){
|
||||
int startLong = (tileNumber * bitsPerTile) / BITS_IN_LONG;
|
||||
int startOffset = (tileNumber * bitsPerTile) % BITS_IN_LONG;
|
||||
int endLong = ((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG;
|
||||
|
||||
std::uint64_t value = chunk->tiles[tileNumber];
|
||||
|
||||
value &= individualValueMask;
|
||||
|
||||
|
||||
chunkData[startLong] |= (value << startOffset);
|
||||
|
||||
if (startLong != endLong){
|
||||
chunkData[endLong] = (value >> (BITS_IN_LONG - startOffset));
|
||||
}
|
||||
}
|
||||
|
||||
bufferSize = data.GetSize();
|
||||
data.Resize(data.GetSize() + chunkData.size() * sizeof(ChunkPackedData::value_type));
|
||||
memcpy((void*)data.data() + bufferSize, chunkData.data(), chunkData.size() * sizeof(ChunkPackedData::value_type));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void WorldDataPacket::Deserialize(DataBuffer& data){
|
||||
std::size_t chunkCount;
|
||||
data >> chunkCount;
|
||||
|
||||
for (int chunkNumber = 0; chunkNumber < chunkCount; chunkNumber++){
|
||||
game::ChunkPtr chunk = std::make_shared<game::Chunk>();
|
||||
|
||||
game::ChunkCoord::first_type chunkX, chunkY;
|
||||
data >> chunkX >> chunkY;
|
||||
|
||||
std::size_t chunkPaletteSize;
|
||||
data >> chunkPaletteSize;
|
||||
|
||||
game::ChunkPalette chunkPalette(chunkPaletteSize);
|
||||
|
||||
memcpy((void*)chunkPalette.data(), data.data() + data.GetReadOffset(), chunkPaletteSize * sizeof(game::ChunkPalette::value_type));
|
||||
data.SetReadOffset(data.GetReadOffset() + chunkPaletteSize * sizeof(game::ChunkPalette::value_type));
|
||||
|
||||
chunk->palette = chunkPalette;
|
||||
|
||||
std::uint8_t bitsPerTile = countBits(chunkPaletteSize);
|
||||
|
||||
// A bitmask that contains bitsPerTile set bits
|
||||
game::ChunkData::value_type individualValueMask = ((1 << bitsPerTile) - 1);
|
||||
|
||||
ChunkPackedData chunkData(game::Chunk::ChunkSize / (BITS_IN_BYTE * sizeof(ChunkPackedData::value_type) / bitsPerTile), 0);
|
||||
|
||||
memcpy((void*)chunkData.data(), data.data() + data.GetReadOffset(), chunkData.size() * sizeof(ChunkPackedData::value_type));
|
||||
data.SetReadOffset(data.GetReadOffset() + chunkData.size() * sizeof(ChunkPackedData::value_type));
|
||||
|
||||
for (int tileNumber = 0; tileNumber < game::Chunk::ChunkSize; tileNumber++){
|
||||
int startLong = (tileNumber * bitsPerTile) / BITS_IN_LONG;
|
||||
int startOffset = (tileNumber * bitsPerTile) % BITS_IN_LONG;
|
||||
int endLong = ((tileNumber + 1) * bitsPerTile - 1) / BITS_IN_LONG;
|
||||
|
||||
game::ChunkData::value_type value;
|
||||
if (startLong == endLong){
|
||||
value = (chunkData[startLong] >> startOffset);
|
||||
}
|
||||
else{
|
||||
int endOffset = BITS_IN_LONG - startOffset;
|
||||
value = (chunkData[startLong] >> startOffset | chunkData[endLong] << endOffset);
|
||||
}
|
||||
value &= individualValueMask;
|
||||
|
||||
chunk->tiles[tileNumber] = value;
|
||||
}
|
||||
|
||||
|
||||
m_Chunks.insert({{chunkX, chunkY}, chunk});
|
||||
}
|
||||
}
|
||||
|
||||
DataBuffer KeepAlivePacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_AliveID;
|
||||
return data;
|
||||
}
|
||||
|
||||
void KeepAlivePacket::Deserialize(DataBuffer& data){
|
||||
data >> m_AliveID;
|
||||
}
|
||||
|
||||
DataBuffer UpdateMoneyPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_NewAmount;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateMoneyPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_NewAmount;
|
||||
}
|
||||
|
||||
DataBuffer UpdateExpPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_NewAmount;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateExpPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_NewAmount;
|
||||
}
|
||||
|
||||
DataBuffer UpdateLobbyTimePacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_RemainingTime;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateLobbyTimePacket::Deserialize(DataBuffer& data){
|
||||
data >> m_RemainingTime;
|
||||
}
|
||||
|
||||
DataBuffer UpdateGameStatePacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_GameState;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdateGameStatePacket::Deserialize(DataBuffer& data){
|
||||
data >> m_GameState;
|
||||
}
|
||||
|
||||
DataBuffer PlayerListPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << (std::uint8_t)m_Players.size();
|
||||
for (auto pair : m_Players){
|
||||
data << pair.first << pair.second.name << pair.second.team;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerListPacket::Deserialize(DataBuffer& data){
|
||||
std::uint8_t playerCount;
|
||||
data >> playerCount;
|
||||
|
||||
for (int i = 0; i < playerCount; i++){
|
||||
std::uint8_t playerID;
|
||||
PlayerInfo playerInfo;
|
||||
data >> playerID >> playerInfo.name >> playerInfo.team;
|
||||
m_Players.insert({playerID, playerInfo});
|
||||
}
|
||||
}
|
||||
|
||||
DataBuffer PlayerJoinPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_PlayerID << m_PlayerName;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerJoinPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_PlayerID >> m_PlayerName;
|
||||
}
|
||||
|
||||
DataBuffer PlayerLeavePacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_PlayerID;
|
||||
return data;
|
||||
}
|
||||
|
||||
void PlayerLeavePacket::Deserialize(DataBuffer& data){
|
||||
data >> m_PlayerID;
|
||||
}
|
||||
|
||||
DataBuffer ConnexionInfoPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_ConnectionID;
|
||||
return data;
|
||||
}
|
||||
|
||||
void ConnexionInfoPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_ConnectionID;
|
||||
}
|
||||
|
||||
DataBuffer SelectTeamPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_SelectedTeam;
|
||||
return data;
|
||||
}
|
||||
|
||||
void SelectTeamPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_SelectedTeam;
|
||||
}
|
||||
|
||||
DataBuffer UpdatePlayerTeamPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_PlayerID << m_SelectedTeam;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UpdatePlayerTeamPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_PlayerID >> m_SelectedTeam;
|
||||
}
|
||||
|
||||
DataBuffer DisconnectPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_Reason;
|
||||
return data;
|
||||
}
|
||||
|
||||
void DisconnectPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_Reason;
|
||||
}
|
||||
|
||||
DataBuffer ServerTpsPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_TPS << m_PacketSendTime;
|
||||
return data;
|
||||
}
|
||||
|
||||
void ServerTpsPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_TPS >> m_PacketSendTime;
|
||||
}
|
||||
|
||||
DataBuffer SpawnMobPacket::Serialize() const{
|
||||
DataBuffer data;
|
||||
data << getID() << m_MobID << m_MobType << m_MobLevel << m_MobDirection
|
||||
<< m_Sender << m_MobX << m_MobY;
|
||||
return data;
|
||||
}
|
||||
|
||||
void SpawnMobPacket::Deserialize(DataBuffer& data){
|
||||
data >> m_MobID >> m_MobType >> m_MobLevel >> m_MobDirection
|
||||
>> m_Sender >> m_MobX >> m_MobY;
|
||||
}
|
||||
|
||||
REGISTER_DISPATCH_CLASS(PlayerLoginPacket);
|
||||
REGISTER_DISPATCH_CLASS(WorldBeginDataPacket);
|
||||
REGISTER_DISPATCH_CLASS(WorldDataPacket);
|
||||
REGISTER_DISPATCH_CLASS(KeepAlivePacket);
|
||||
REGISTER_DISPATCH_CLASS(UpdateExpPacket);
|
||||
REGISTER_DISPATCH_CLASS(UpdateMoneyPacket);
|
||||
REGISTER_DISPATCH_CLASS(UpdateLobbyTimePacket);
|
||||
REGISTER_DISPATCH_CLASS(UpdateGameStatePacket);
|
||||
REGISTER_DISPATCH_CLASS(PlayerListPacket);
|
||||
REGISTER_DISPATCH_CLASS(PlayerJoinPacket);
|
||||
REGISTER_DISPATCH_CLASS(PlayerLeavePacket);
|
||||
REGISTER_DISPATCH_CLASS(ConnexionInfoPacket);
|
||||
REGISTER_DISPATCH_CLASS(SelectTeamPacket);
|
||||
REGISTER_DISPATCH_CLASS(UpdatePlayerTeamPacket);
|
||||
REGISTER_DISPATCH_CLASS(DisconnectPacket);
|
||||
REGISTER_DISPATCH_CLASS(ServerTpsPacket);
|
||||
REGISTER_DISPATCH_CLASS(SpawnMobPacket);
|
||||
|
||||
} // namespace protocol
|
||||
} // namespace td
|
||||
133
src/render/Renderer.cpp
Normal file
133
src/render/Renderer.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Renderer.cpp
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#include "render/Renderer.h"
|
||||
#include "render/shaders/WorldShader.h"
|
||||
#include "render/shaders/EntityShader.h"
|
||||
#include <glbinding/gl/gl.h>
|
||||
#include <stdio.h>
|
||||
#include <glbinding/Binding.h>
|
||||
#include "misc/Time.h"
|
||||
|
||||
using namespace gl;
|
||||
|
||||
namespace td{
|
||||
namespace render{
|
||||
|
||||
namespace Renderer{
|
||||
|
||||
#define ANIMATION_SPEED 2.0f
|
||||
|
||||
static std::unique_ptr<WorldShader> worldShader;
|
||||
static std::unique_ptr<EntityShader> entityShader;
|
||||
|
||||
static bool isometricView = true;
|
||||
static float isometricShade = isometricView;
|
||||
static glm::vec2 camPos{};
|
||||
|
||||
void updateIsometricView(){
|
||||
worldShader->start();
|
||||
worldShader->setIsometricView(isometricShade);
|
||||
entityShader->start();
|
||||
entityShader->setIsometricView(isometricShade);
|
||||
}
|
||||
|
||||
void initShader(){
|
||||
worldShader = std::make_unique<WorldShader>();
|
||||
worldShader->loadShader();
|
||||
entityShader = std::make_unique<EntityShader>();
|
||||
entityShader->loadShader();
|
||||
setIsometricView(true);
|
||||
updateIsometricView();
|
||||
}
|
||||
|
||||
bool init(){
|
||||
glbinding::Binding::initialize();
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
initShader();
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroy(){
|
||||
worldShader.reset(0);
|
||||
entityShader.reset(0);
|
||||
}
|
||||
|
||||
void renderVAO(const GL::VAO& vao){
|
||||
worldShader->start();
|
||||
vao.bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, vao.getVertexCount());
|
||||
vao.unbind();
|
||||
}
|
||||
|
||||
void renderModel(const Model& model){
|
||||
entityShader->start();
|
||||
entityShader->setModelPos(model.positon);
|
||||
model.vao->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, model.vao->getVertexCount());
|
||||
model.vao->unbind();
|
||||
}
|
||||
|
||||
void updateIsometricFade(){
|
||||
static std::uint64_t lastTime = utils::getTime();
|
||||
if(isometricShade != (float) isometricView){
|
||||
float step = (float) (utils::getTime() - lastTime) / 1000.0f * ANIMATION_SPEED;
|
||||
if(isometricShade < isometricView){
|
||||
isometricShade += step;
|
||||
}else{
|
||||
isometricShade -= step;
|
||||
}
|
||||
isometricShade = std::min(isometricShade, 1.0f);
|
||||
isometricShade = std::max(isometricShade, 0.0f);
|
||||
updateIsometricView();
|
||||
}
|
||||
lastTime = utils::getTime();
|
||||
}
|
||||
|
||||
void prepare(){
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClearColor(0, 0, 0, 0);
|
||||
updateIsometricFade();
|
||||
}
|
||||
|
||||
void resize(int width, int height){
|
||||
worldShader->start();
|
||||
worldShader->setAspectRatio((float)width / height);
|
||||
entityShader->start();
|
||||
entityShader->setAspectRatio((float)width / height);
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
void setZoom(float zoom){
|
||||
worldShader->start();
|
||||
worldShader->setZoom(zoom);
|
||||
entityShader->start();
|
||||
entityShader->setZoom(zoom);
|
||||
}
|
||||
|
||||
void setCamMovement(const glm::vec2& mov){
|
||||
camPos.x += mov.x * (1 - isometricView) + (0.5 * mov.x + mov.y) * isometricView;
|
||||
camPos.y += -mov.y * (1 - isometricView) + (0.5 * mov.x - mov.y) * isometricView;
|
||||
setCamPos(camPos);
|
||||
}
|
||||
|
||||
void setCamPos(const glm::vec2& newPos){
|
||||
camPos = newPos;
|
||||
worldShader->start();
|
||||
worldShader->setCamPos(newPos);
|
||||
entityShader->start();
|
||||
entityShader->setCamPos(newPos);
|
||||
}
|
||||
|
||||
void setIsometricView(bool isometric){
|
||||
isometricView = isometric;
|
||||
}
|
||||
|
||||
} // namespace Renderer
|
||||
|
||||
} // namespace render
|
||||
} // namespace td
|
||||
112
src/render/WorldRenderer.cpp
Normal file
112
src/render/WorldRenderer.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "render/WorldRenderer.h"
|
||||
#include "render/loader/WorldLoader.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "../render/gui/imgui/imgui.h"
|
||||
#include "window/Display.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace td {
|
||||
namespace render {
|
||||
|
||||
namespace WorldRenderer{
|
||||
|
||||
static std::unique_ptr<GL::VAO> worldVAO;
|
||||
static const game::World* worldPtr = nullptr;
|
||||
|
||||
static glm::vec2 camPos{13.5, 13.5};//{60, -10};
|
||||
static float zoom = 1;
|
||||
|
||||
static const float camSensibility = 1;
|
||||
|
||||
static std::unique_ptr<GL::VAO> mobVAO;
|
||||
|
||||
void init(const game::World* world){
|
||||
std::cout << "World Created !\n";
|
||||
worldVAO = std::make_unique<GL::VAO>(std::move(WorldLoader::loadWorldModel(world)));
|
||||
mobVAO = std::make_unique<GL::VAO>(std::move(WorldLoader::loadMobModel()));
|
||||
std::cout << "Vertex Count : " << worldVAO->getVertexCount() << std::endl;
|
||||
Renderer::setCamPos(camPos);
|
||||
worldPtr = world;
|
||||
}
|
||||
|
||||
void update(){
|
||||
if(worldVAO == nullptr)
|
||||
return;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if(io.MouseDown[0] && !ImGui::IsAnyItemActive()){
|
||||
ImVec2 mouseDelta = ImGui::GetIO().MouseDelta;
|
||||
const float relativeX = mouseDelta.x / (float) Display::getWindowWidth() * 2;
|
||||
const float relativeY = mouseDelta.y / (float) Display::getWindowHeight() * 2;
|
||||
moveCam(relativeX, relativeY, Display::getAspectRatio());
|
||||
}
|
||||
if(io.MouseWheel != 0){
|
||||
changeZoom(io.MouseWheel);
|
||||
}
|
||||
}
|
||||
|
||||
void renderWorld(){
|
||||
Renderer::renderVAO(*worldVAO);
|
||||
}
|
||||
|
||||
void renderMobs(){
|
||||
for(game::MobPtr mob : worldPtr->getMobList()){
|
||||
Renderer::Model model;
|
||||
model.vao = mobVAO.get();
|
||||
model.positon = {mob->getX(), mob->getY()};
|
||||
Renderer::renderModel(model);
|
||||
}
|
||||
}
|
||||
|
||||
void renderTowers(){
|
||||
|
||||
}
|
||||
|
||||
void render(){
|
||||
if(worldVAO == nullptr)
|
||||
return;
|
||||
renderWorld();
|
||||
renderMobs();
|
||||
renderTowers();
|
||||
}
|
||||
|
||||
void destroy(){
|
||||
if(worldVAO == nullptr)
|
||||
return;
|
||||
worldVAO.reset();
|
||||
worldPtr = nullptr;
|
||||
}
|
||||
|
||||
void moveCam(float relativeX, float relativeY, float aspectRatio){
|
||||
if(worldVAO == nullptr)
|
||||
return;
|
||||
float movementX = -relativeX / zoom * aspectRatio;
|
||||
float movementY = relativeY / zoom;
|
||||
Renderer::setCamMovement({movementX, movementY});
|
||||
}
|
||||
|
||||
void changeZoom(float zoomStep){
|
||||
if(worldVAO == nullptr)
|
||||
return;
|
||||
static float sensibility = 1.5f;
|
||||
if (zoomStep < 0){
|
||||
zoom /= -zoomStep * sensibility;
|
||||
}
|
||||
else{
|
||||
zoom *= zoomStep * sensibility;
|
||||
}
|
||||
Renderer::setZoom(zoom);
|
||||
Renderer::setCamMovement({});
|
||||
}
|
||||
|
||||
void click(int mouseX, int mouseY){
|
||||
if(worldVAO == nullptr)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
} // namespace WorldRenderer
|
||||
|
||||
|
||||
} // namespace render
|
||||
} // namespace td
|
||||
329
src/render/gui/TowerGui.cpp
Normal file
329
src/render/gui/TowerGui.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* TowerGui.cpp
|
||||
*
|
||||
* Created on: 5 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#include "render/gui/TowerGui.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/imgui_impl_opengl3.h"
|
||||
#include "imgui/imgui_impl_glfw.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <thread>
|
||||
#include "game/client/Client.h"
|
||||
#include "game/server/Server.h"
|
||||
#include "misc/Time.h"
|
||||
#include "imgui/imgui_filebrowser.h"
|
||||
#include "render/Renderer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
namespace TowerGui{
|
||||
|
||||
static GLFWwindow* window;
|
||||
static td::client::Client client;
|
||||
static std::thread* serverThread;
|
||||
|
||||
bool serverShouldStop = false;
|
||||
|
||||
void init(GLFWwindow* glfw_window){
|
||||
window = glfw_window;
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
ImGui_ImplGlfw_InitForOpenGL(glfw_window, true);
|
||||
const char* glslVersion = "#version 130";
|
||||
ImGui_ImplOpenGL3_Init(glslVersion);
|
||||
ImFontConfig c;
|
||||
c.SizePixels = 25;
|
||||
ImGui::GetIO().Fonts->AddFontDefault(&c);
|
||||
}
|
||||
|
||||
void beginFrame(){
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void endFrame(){
|
||||
ImGui::EndFrame();
|
||||
ImGui::Render();
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void renderFPSCounter(){
|
||||
ImGui::Begin("FPS Counter");
|
||||
ImGui::Text("FPS : %i", (int)ImGui::GetIO().Framerate);
|
||||
static bool vsync = true;
|
||||
if (ImGui::Checkbox("V-Sync", &vsync)){
|
||||
glfwSwapInterval(vsync);
|
||||
}
|
||||
static bool isometric = true;
|
||||
if (ImGui::Checkbox("Vue Isometrique ?", &isometric)){
|
||||
td::render::Renderer::setIsometricView(isometric);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
bool startServer(int port, const std::string& worldFilePath){
|
||||
if (worldFilePath.empty())
|
||||
return false;
|
||||
std::shared_ptr<td::server::Server> server = std::make_shared<td::server::Server>(worldFilePath);
|
||||
if (!server->start(port)){
|
||||
return false;
|
||||
}
|
||||
serverThread = new std::thread([server](){
|
||||
while (!serverShouldStop){
|
||||
static std::uint64_t lastTime = td::utils::getTime();
|
||||
std::uint64_t time = td::utils::getTime();
|
||||
|
||||
std::uint64_t delta = time - lastTime;
|
||||
|
||||
if (delta >= SERVER_TICK){
|
||||
server->tick(delta);
|
||||
lastTime = td::utils::getTime();
|
||||
std::uint64_t sleepTime = SERVER_TICK - (delta - SERVER_TICK);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime));
|
||||
}
|
||||
|
||||
}
|
||||
server->stop();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void renderMainMenu(){
|
||||
ImGui::Begin("Main Menu");
|
||||
if (ImGui::Button("Rejoindre une partie##join")){
|
||||
ImGui::OpenPopup("Rejoindre une partie##join_popup");
|
||||
}
|
||||
if (ImGui::Button("Créer une partie")){
|
||||
ImGui::OpenPopup("Créer une partie##create_popup");
|
||||
}
|
||||
if (ImGui::Button("Options")){
|
||||
|
||||
}
|
||||
static bool triedToConnect = false;
|
||||
if (ImGui::BeginPopup("Rejoindre une partie##join_popup")){
|
||||
static char buffer[512] = "localhost";
|
||||
static int port = 25565;
|
||||
ImGui::InputText("Server Adress", buffer, sizeof(buffer));
|
||||
ImGui::InputInt("Port", &port, -1);
|
||||
if (ImGui::Button("Rejoindre")){
|
||||
client.connect(buffer, port);
|
||||
triedToConnect = true;
|
||||
}
|
||||
if (triedToConnect){
|
||||
ImGui::Text("Impossible de se connecter");
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
else{
|
||||
triedToConnect = false;
|
||||
}
|
||||
|
||||
static bool triedToCreate = false;
|
||||
if (ImGui::BeginPopup("Créer une partie##create_popup")){
|
||||
static imgui_addons::ImGuiFileBrowser file_dialog;
|
||||
static int port = 25565;
|
||||
static std::string worldFilePath;
|
||||
|
||||
ImGui::InputInt("Server Port", &port, -1);
|
||||
ImGui::Text(std::string("Fichier de monde sélectionné : " + (worldFilePath.empty() ? std::string("Aucun") : worldFilePath)).c_str());
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Ouvrir un fichier")){
|
||||
ImGui::OpenPopup("WorldFileDialog");
|
||||
}
|
||||
if (file_dialog.showFileDialog("WorldFileDialog", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(600, 300), ".tdmap")){
|
||||
worldFilePath = file_dialog.selected_path;
|
||||
}
|
||||
if (ImGui::Button("Créer")){
|
||||
if (!startServer(port, worldFilePath)){
|
||||
triedToCreate = true;
|
||||
}
|
||||
else{
|
||||
client.connect("localhost", port);
|
||||
}
|
||||
}
|
||||
if (triedToCreate)
|
||||
ImGui::Text("Failed to launch server");
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
else{
|
||||
triedToCreate = false;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImVec4 getImGuiTeamColor(td::game::TeamColor color){
|
||||
switch (color){
|
||||
case td::game::TeamColor::None:
|
||||
return ImVec4(1, 1, 1, 1);
|
||||
case td::game::TeamColor::Red:
|
||||
return ImVec4(1, 0, 0, 1);
|
||||
case td::game::TeamColor::Blue:
|
||||
return ImVec4(0, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void showPlayers(){
|
||||
if (ImGui::TreeNode(std::string("Players (" + std::to_string(client.getGame().getPlayers().size()) + ")##player_list").c_str())){
|
||||
for (auto pair : client.getGame().getPlayers()){
|
||||
const td::game::Player& player = pair.second;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, getImGuiTeamColor(player.getTeamColor()));
|
||||
ImGui::Text(player.getName().c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
void showTeamSelection(){
|
||||
if (client.getGame().getPlayer() == nullptr)
|
||||
return;
|
||||
td::game::TeamColor playerTeam = client.getGame().getPlayer()->getTeamColor();
|
||||
|
||||
if (ImGui::Button(std::string((playerTeam == td::game::TeamColor::Red ? "Leave" : "Join") + std::string(" Red Team")).c_str())){
|
||||
if (playerTeam == td::game::TeamColor::Red)
|
||||
client.selectTeam(td::game::TeamColor::None);
|
||||
else
|
||||
client.selectTeam(td::game::TeamColor::Red);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(std::string((playerTeam == td::game::TeamColor::Blue ? "Leave" : "Join") + std::string(" Blue Team")).c_str())){
|
||||
if (playerTeam == td::game::TeamColor::Blue)
|
||||
client.selectTeam(td::game::TeamColor::None);
|
||||
else
|
||||
client.selectTeam(td::game::TeamColor::Blue);
|
||||
}
|
||||
}
|
||||
|
||||
void showLobbyProgress(){
|
||||
const int timePassed = LOBBY_WAITING_TIME - client.getGame().getLobbyTime();
|
||||
const float progress = (float)timePassed / (float)(LOBBY_WAITING_TIME);
|
||||
if (progress > 0 && progress < 1 && client.getGame().getPlayers().size() >= 2){
|
||||
ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f), std::string(std::to_string(client.getGame().getLobbyTime() / 1000) + "s").c_str());
|
||||
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
|
||||
ImGui::Text("Time Remaining");
|
||||
}
|
||||
else{
|
||||
ImGui::Text("Waiting for players ...\n");
|
||||
}
|
||||
}
|
||||
|
||||
void showTPS(){
|
||||
ImGui::Text("Server TPS : %.1f", client.getConnexion().getServerTPS());
|
||||
ImGui::Text("Server Ping : %i", client.getConnexion().getServerPing());
|
||||
}
|
||||
|
||||
void showStats(){
|
||||
ImGui::Text("Gold : %i", client.getGame().getPlayer()->getGold());
|
||||
}
|
||||
|
||||
void renderSummonMenu(){
|
||||
static bool menu_open = false;
|
||||
if (menu_open){
|
||||
|
||||
ImGui::Begin("Summon", &menu_open);
|
||||
static int width = 100;
|
||||
ImTextureID my_tex_id = ImGui::GetIO().Fonts->TexID;
|
||||
for (int i = 0; i < 8; i++){
|
||||
ImGui::SameLine();
|
||||
ImGui::PushID(i);
|
||||
ImGui::Image(my_tex_id, ImVec2(100, 100));
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::Separator();
|
||||
static int values[16];
|
||||
ImGui::PushItemWidth(width);
|
||||
for (int i = 0; i < 8; i++){
|
||||
ImGui::SameLine();
|
||||
ImGui::PushID(i);
|
||||
ImGui::InputInt("", values + i, 1, 10);
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Separator();
|
||||
for (int i = 0; i < 8; i++){
|
||||
ImGui::SameLine();
|
||||
ImGui::PushID(i);
|
||||
ImGui::Image(my_tex_id, ImVec2(100, 100));
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::PushItemWidth(width);
|
||||
for (int i = 8; i < 16; i++){
|
||||
ImGui::SameLine();
|
||||
ImGui::PushID(i);
|
||||
ImGui::InputInt("", values + i, 1, 10);
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
void renderGame(){
|
||||
if (client.getGame().getGameState() == td::game::GameState::Lobby){
|
||||
ImGui::Begin("Lobby");
|
||||
|
||||
showTPS();
|
||||
showPlayers();
|
||||
showLobbyProgress();
|
||||
showTeamSelection();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
if (client.getGame().getGameState() == td::game::GameState::Game){
|
||||
ImGui::Begin("Game");
|
||||
|
||||
showTPS();
|
||||
showStats();
|
||||
showPlayers();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
void tick(){
|
||||
static std::uint64_t lastTime = td::utils::getTime();
|
||||
std::uint64_t time = td::utils::getTime();
|
||||
|
||||
std::uint64_t delta = time - lastTime;
|
||||
|
||||
client.tick(delta);
|
||||
|
||||
lastTime = td::utils::getTime();
|
||||
}
|
||||
|
||||
void render(){
|
||||
tick();
|
||||
beginFrame();
|
||||
if (client.isConnected())
|
||||
renderGame();
|
||||
else
|
||||
renderMainMenu();
|
||||
static bool demo_open = false;
|
||||
if (demo_open)
|
||||
ImGui::ShowDemoWindow(&demo_open);
|
||||
renderSummonMenu();
|
||||
renderFPSCounter();
|
||||
endFrame();
|
||||
}
|
||||
|
||||
void destroy(){
|
||||
client.closeConnection();
|
||||
serverShouldStop = true;
|
||||
if (serverThread != nullptr){
|
||||
serverThread->join();
|
||||
delete serverThread;
|
||||
}
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplGlfw_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
}
|
||||
110
src/render/gui/imgui/imconfig.h
Executable file
110
src/render/gui/imgui/imconfig.h
Executable file
@@ -0,0 +1,110 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
|
||||
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
|
||||
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
|
||||
//-----------------------------------------------------------------------------
|
||||
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h)
|
||||
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
|
||||
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include
|
||||
// the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
|
||||
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
|
||||
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
//---- Define assertion handler. Defaults to calling assert().
|
||||
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
|
||||
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows.
|
||||
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
|
||||
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
|
||||
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable debug/metrics window: ShowMetricsWindow() will be empty.
|
||||
|
||||
//---- Don't implement some functions to reduce linkage requirements.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support full unicode code points.
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
|
||||
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
|
||||
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
|
||||
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
|
||||
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||
|
||||
//---- Unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined, use the much faster STB sprintf library implementation of vsnprintf instead of the one from the default C library.
|
||||
// Note that stb_sprintf.h is meant to be provided by the user and available in the include path at compile time. Also, the compatibility checks of the arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf.
|
||||
// #define IMGUI_USE_STB_SPRINTF
|
||||
|
||||
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||
/*
|
||||
#define IM_VEC2_CLASS_EXTRA \
|
||||
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
|
||||
operator MyVec2() const { return MyVec2(x,y); }
|
||||
|
||||
#define IM_VEC4_CLASS_EXTRA \
|
||||
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
|
||||
operator MyVec4() const { return MyVec4(x,y,z,w); }
|
||||
*/
|
||||
|
||||
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||
// Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bit indices).
|
||||
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||
//#define ImDrawIdx unsigned int
|
||||
|
||||
//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
|
||||
//struct ImDrawList;
|
||||
//struct ImDrawCmd;
|
||||
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||
//#define ImDrawCallback MyImDrawCallback
|
||||
|
||||
//---- Debug Tools: Macro to break in Debugger
|
||||
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
|
||||
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
|
||||
// This adds a small runtime cost which is why it is not enabled by default.
|
||||
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
|
||||
/*
|
||||
namespace ImGui
|
||||
{
|
||||
void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
*/
|
||||
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
|
||||
11589
src/render/gui/imgui/imgui.cpp
Normal file
11589
src/render/gui/imgui/imgui.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2852
src/render/gui/imgui/imgui.h
Normal file
2852
src/render/gui/imgui/imgui.h
Normal file
File diff suppressed because it is too large
Load Diff
7760
src/render/gui/imgui/imgui_demo.cpp
Normal file
7760
src/render/gui/imgui/imgui_demo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
5851
src/render/gui/imgui/imgui_draw.cpp
Normal file
5851
src/render/gui/imgui/imgui_draw.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1184
src/render/gui/imgui/imgui_filebrowser.cpp
Normal file
1184
src/render/gui/imgui/imgui_filebrowser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
122
src/render/gui/imgui/imgui_filebrowser.h
Normal file
122
src/render/gui/imgui/imgui_filebrowser.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#ifndef IMGUIFILEBROWSER_H
|
||||
#define IMGUIFILEBROWSER_H
|
||||
|
||||
#include "imgui.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace imgui_addons
|
||||
{
|
||||
class ImGuiFileBrowser
|
||||
{
|
||||
public:
|
||||
ImGuiFileBrowser();
|
||||
~ImGuiFileBrowser();
|
||||
|
||||
enum class DialogMode
|
||||
{
|
||||
SELECT, //Select Directory Mode
|
||||
OPEN, //Open File mode
|
||||
SAVE //Save File mode.
|
||||
};
|
||||
|
||||
/* Use this to show an open file dialog. The function takes label for the window,
|
||||
* the size, a DialogMode enum value defining in which mode the dialog should operate and optionally the extensions that are valid for opening.
|
||||
* Note that the select directory mode doesn't need any extensions.
|
||||
*/
|
||||
bool showFileDialog(const std::string& label, const DialogMode mode, const ImVec2& sz_xy = ImVec2(0,0), const std::string& valid_types = "*.*");
|
||||
|
||||
/* Store the opened/saved file name or dir name (incase of selectDirectoryDialog) and the absolute path to the selection
|
||||
* Should only be accessed when above functions return true else may contain garbage.
|
||||
*/
|
||||
std::string selected_fn;
|
||||
std::string selected_path;
|
||||
std::string ext; // Store the saved file extension
|
||||
|
||||
|
||||
private:
|
||||
struct Info
|
||||
{
|
||||
Info(std::string name, bool is_hidden) : name(name), is_hidden(is_hidden)
|
||||
{
|
||||
}
|
||||
std::string name;
|
||||
bool is_hidden;
|
||||
};
|
||||
|
||||
//Enum used as bit flags.
|
||||
enum FilterMode
|
||||
{
|
||||
FilterMode_Files = 0x01,
|
||||
FilterMode_Dirs = 0x02
|
||||
};
|
||||
|
||||
//Helper Functions
|
||||
static std::string wStringToString(const wchar_t* wchar_arr);
|
||||
static bool alphaSortComparator(const Info& a, const Info& b);
|
||||
ImVec2 getButtonSize(std::string button_text);
|
||||
|
||||
/* Helper Functions that render secondary modals
|
||||
* and help in validating file extensions and for filtering, parsing top navigation bar.
|
||||
*/
|
||||
void setValidExtTypes(const std::string& valid_types_string);
|
||||
bool validateFile();
|
||||
void showErrorModal();
|
||||
void showInvalidFileModal();
|
||||
bool showReplaceFileModal();
|
||||
void showHelpMarker(std::string desc);
|
||||
void parsePathTabs(std::string str);
|
||||
void filterFiles(int filter_mode);
|
||||
|
||||
/* Core Functions that render the 4 different regions making up
|
||||
* a simple file dialog
|
||||
*/
|
||||
bool renderNavAndSearchBarRegion();
|
||||
bool renderFileListRegion();
|
||||
bool renderInputTextAndExtRegion();
|
||||
bool renderButtonsAndCheckboxRegion();
|
||||
bool renderInputComboBox();
|
||||
void renderExtBox();
|
||||
|
||||
/* Core Functions that handle navigation and
|
||||
* reading directories/files
|
||||
*/
|
||||
bool readDIR(std::string path);
|
||||
bool onNavigationButtonClick(int idx);
|
||||
bool onDirClick(int idx);
|
||||
|
||||
// Functions that reset state and/or clear file list when reading new directory
|
||||
void clearFileList();
|
||||
void closeDialog();
|
||||
|
||||
#if defined (WIN32) || defined (_WIN32) || defined (__WIN32)
|
||||
bool loadWindowsDrives(); // Helper Function for Windows to load Drive Letters.
|
||||
#endif
|
||||
|
||||
#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
|
||||
void initCurrentPath(); // Helper function for UNIX based system to load Absolute path using realpath
|
||||
#endif
|
||||
|
||||
ImVec2 min_size, max_size, input_combobox_pos, input_combobox_sz;
|
||||
DialogMode dialog_mode;
|
||||
int filter_mode, col_items_limit, selected_idx, selected_ext_idx;
|
||||
float col_width, ext_box_width;
|
||||
bool show_hidden, show_inputbar_combobox, is_dir, is_appearing, filter_dirty, validate_file;
|
||||
char input_fn[256];
|
||||
|
||||
std::vector<std::string> valid_exts;
|
||||
std::vector<std::string> current_dirlist;
|
||||
std::vector<Info> subdirs;
|
||||
std::vector<Info> subfiles;
|
||||
std::string current_path, error_msg, error_title, invfile_modal_id, repfile_modal_id;
|
||||
|
||||
ImGuiTextFilter filter;
|
||||
std::string valid_types;
|
||||
std::vector<const Info*> filtered_dirs; // Note: We don't need to call delete. It's just for storing filtered items from subdirs and subfiles so we don't use PassFilter every frame.
|
||||
std::vector<const Info*> filtered_files;
|
||||
std::vector< std::reference_wrapper<std::string> > inputcb_filter_files;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif // IMGUIFILEBROWSER_H
|
||||
369
src/render/gui/imgui/imgui_impl_glfw.cpp
Normal file
369
src/render/gui/imgui/imgui_impl_glfw.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
// dear imgui: Platform Binding for GLFW
|
||||
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..)
|
||||
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
|
||||
// (Requires: GLFW 3.1+)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
|
||||
// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors.
|
||||
// 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor).
|
||||
// 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown.
|
||||
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
|
||||
// 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter().
|
||||
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
|
||||
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
|
||||
// 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them.
|
||||
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
|
||||
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples.
|
||||
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
|
||||
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()).
|
||||
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
|
||||
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
|
||||
// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set.
|
||||
// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set).
|
||||
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
|
||||
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
|
||||
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
|
||||
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_glfw.h"
|
||||
|
||||
// GLFW
|
||||
#include <GLFW/glfw3.h>
|
||||
#ifdef _WIN32
|
||||
#undef APIENTRY
|
||||
#define GLFW_EXPOSE_NATIVE_WIN32
|
||||
#include <GLFW/glfw3native.h> // for glfwGetWin32Window
|
||||
#endif
|
||||
#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING
|
||||
#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED
|
||||
#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity
|
||||
#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale
|
||||
#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface
|
||||
#ifdef GLFW_RESIZE_NESW_CURSOR // let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released?
|
||||
#define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR
|
||||
#else
|
||||
#define GLFW_HAS_NEW_CURSORS (0)
|
||||
#endif
|
||||
|
||||
// Data
|
||||
enum GlfwClientApi
|
||||
{
|
||||
GlfwClientApi_Unknown,
|
||||
GlfwClientApi_OpenGL,
|
||||
GlfwClientApi_Vulkan
|
||||
};
|
||||
static GLFWwindow* g_Window = NULL; // Main window
|
||||
static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown;
|
||||
static double g_Time = 0.0;
|
||||
static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {};
|
||||
static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {};
|
||||
static bool g_InstalledCallbacks = false;
|
||||
|
||||
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
|
||||
static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL;
|
||||
static GLFWscrollfun g_PrevUserCallbackScroll = NULL;
|
||||
static GLFWkeyfun g_PrevUserCallbackKey = NULL;
|
||||
static GLFWcharfun g_PrevUserCallbackChar = NULL;
|
||||
|
||||
static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data)
|
||||
{
|
||||
return glfwGetClipboardString((GLFWwindow*)user_data);
|
||||
}
|
||||
|
||||
static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text)
|
||||
{
|
||||
glfwSetClipboardString((GLFWwindow*)user_data, text);
|
||||
}
|
||||
|
||||
void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
|
||||
{
|
||||
if (g_PrevUserCallbackMousebutton != NULL)
|
||||
g_PrevUserCallbackMousebutton(window, button, action, mods);
|
||||
|
||||
if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed))
|
||||
g_MouseJustPressed[button] = true;
|
||||
}
|
||||
|
||||
void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
|
||||
{
|
||||
if (g_PrevUserCallbackScroll != NULL)
|
||||
g_PrevUserCallbackScroll(window, xoffset, yoffset);
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.MouseWheelH += (float)xoffset;
|
||||
io.MouseWheel += (float)yoffset;
|
||||
}
|
||||
|
||||
void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
|
||||
{
|
||||
if (g_PrevUserCallbackKey != NULL)
|
||||
g_PrevUserCallbackKey(window, key, scancode, action, mods);
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if (action == GLFW_PRESS)
|
||||
io.KeysDown[key] = true;
|
||||
if (action == GLFW_RELEASE)
|
||||
io.KeysDown[key] = false;
|
||||
|
||||
// Modifiers are not reliable across systems
|
||||
io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL];
|
||||
io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT];
|
||||
io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT];
|
||||
#ifdef _WIN32
|
||||
io.KeySuper = false;
|
||||
#else
|
||||
io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER];
|
||||
#endif
|
||||
}
|
||||
|
||||
void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c)
|
||||
{
|
||||
if (g_PrevUserCallbackChar != NULL)
|
||||
g_PrevUserCallbackChar(window, c);
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddInputCharacter(c);
|
||||
}
|
||||
|
||||
static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api)
|
||||
{
|
||||
g_Window = window;
|
||||
g_Time = 0.0;
|
||||
|
||||
// Setup back-end capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
|
||||
io.BackendPlatformName = "imgui_impl_glfw";
|
||||
|
||||
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
|
||||
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
|
||||
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
|
||||
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
|
||||
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
|
||||
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
|
||||
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
|
||||
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
|
||||
io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
|
||||
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
|
||||
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
|
||||
io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
|
||||
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER;
|
||||
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
|
||||
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
|
||||
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
|
||||
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
|
||||
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
|
||||
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
|
||||
|
||||
io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText;
|
||||
io.ClipboardUserData = g_Window;
|
||||
#if defined(_WIN32)
|
||||
io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window);
|
||||
#endif
|
||||
|
||||
// Create mouse cursors
|
||||
// (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist,
|
||||
// GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting.
|
||||
// Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.)
|
||||
GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL);
|
||||
g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
|
||||
#if GLFW_HAS_NEW_CURSORS
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR);
|
||||
#else
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR);
|
||||
#endif
|
||||
glfwSetErrorCallback(prev_error_callback);
|
||||
|
||||
// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any.
|
||||
g_PrevUserCallbackMousebutton = NULL;
|
||||
g_PrevUserCallbackScroll = NULL;
|
||||
g_PrevUserCallbackKey = NULL;
|
||||
g_PrevUserCallbackChar = NULL;
|
||||
if (install_callbacks)
|
||||
{
|
||||
g_InstalledCallbacks = true;
|
||||
g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback);
|
||||
g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback);
|
||||
g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback);
|
||||
g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
|
||||
}
|
||||
|
||||
g_ClientApi = client_api;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks)
|
||||
{
|
||||
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL);
|
||||
}
|
||||
|
||||
bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks)
|
||||
{
|
||||
return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan);
|
||||
}
|
||||
|
||||
void ImGui_ImplGlfw_Shutdown()
|
||||
{
|
||||
if (g_InstalledCallbacks)
|
||||
{
|
||||
glfwSetMouseButtonCallback(g_Window, g_PrevUserCallbackMousebutton);
|
||||
glfwSetScrollCallback(g_Window, g_PrevUserCallbackScroll);
|
||||
glfwSetKeyCallback(g_Window, g_PrevUserCallbackKey);
|
||||
glfwSetCharCallback(g_Window, g_PrevUserCallbackChar);
|
||||
g_InstalledCallbacks = false;
|
||||
}
|
||||
|
||||
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
|
||||
{
|
||||
glfwDestroyCursor(g_MouseCursors[cursor_n]);
|
||||
g_MouseCursors[cursor_n] = NULL;
|
||||
}
|
||||
g_ClientApi = GlfwClientApi_Unknown;
|
||||
}
|
||||
|
||||
static void ImGui_ImplGlfw_UpdateMousePosAndButtons()
|
||||
{
|
||||
// Update buttons
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
|
||||
{
|
||||
// If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
|
||||
io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0;
|
||||
g_MouseJustPressed[i] = false;
|
||||
}
|
||||
|
||||
// Update mouse position
|
||||
const ImVec2 mouse_pos_backup = io.MousePos;
|
||||
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
||||
#ifdef __EMSCRIPTEN__
|
||||
const bool focused = true; // Emscripten
|
||||
#else
|
||||
const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0;
|
||||
#endif
|
||||
if (focused)
|
||||
{
|
||||
if (io.WantSetMousePos)
|
||||
{
|
||||
glfwSetCursorPos(g_Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
double mouse_x, mouse_y;
|
||||
glfwGetCursorPos(g_Window, &mouse_x, &mouse_y);
|
||||
io.MousePos = ImVec2((float)mouse_x, (float)mouse_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplGlfw_UpdateMouseCursor()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED)
|
||||
return;
|
||||
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor)
|
||||
{
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show OS mouse cursor
|
||||
// FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here.
|
||||
glfwSetCursor(g_Window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]);
|
||||
glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
static void ImGui_ImplGlfw_UpdateGamepads()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
memset(io.NavInputs, 0, sizeof(io.NavInputs));
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
|
||||
return;
|
||||
|
||||
// Update gamepad inputs
|
||||
#define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; }
|
||||
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; }
|
||||
int axes_count = 0, buttons_count = 0;
|
||||
const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count);
|
||||
const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count);
|
||||
MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A
|
||||
MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B
|
||||
MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X
|
||||
MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y
|
||||
MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left
|
||||
MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right
|
||||
MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up
|
||||
MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down
|
||||
MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB
|
||||
MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB
|
||||
MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f);
|
||||
MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f);
|
||||
#undef MAP_BUTTON
|
||||
#undef MAP_ANALOG
|
||||
if (axes_count > 0 && buttons_count > 0)
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
else
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
|
||||
}
|
||||
|
||||
void ImGui_ImplGlfw_NewFrame()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer back-end. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame().");
|
||||
|
||||
// Setup display size (every frame to accommodate for window resizing)
|
||||
int w, h;
|
||||
int display_w, display_h;
|
||||
glfwGetWindowSize(g_Window, &w, &h);
|
||||
glfwGetFramebufferSize(g_Window, &display_w, &display_h);
|
||||
io.DisplaySize = ImVec2((float)w, (float)h);
|
||||
if (w > 0 && h > 0)
|
||||
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
|
||||
|
||||
// Setup time step
|
||||
double current_time = glfwGetTime();
|
||||
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f);
|
||||
g_Time = current_time;
|
||||
|
||||
ImGui_ImplGlfw_UpdateMousePosAndButtons();
|
||||
ImGui_ImplGlfw_UpdateMouseCursor();
|
||||
|
||||
// Update game controllers (if enabled and available)
|
||||
ImGui_ImplGlfw_UpdateGamepads();
|
||||
}
|
||||
35
src/render/gui/imgui/imgui_impl_glfw.h
Normal file
35
src/render/gui/imgui/imgui_impl_glfw.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// dear imgui: Platform Binding for GLFW
|
||||
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..)
|
||||
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Clipboard support.
|
||||
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW.
|
||||
// [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE).
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// About GLSL version:
|
||||
// The 'glsl_version' initialization parameter defaults to "#version 150" if NULL.
|
||||
// Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure!
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
struct GLFWwindow;
|
||||
|
||||
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
|
||||
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
|
||||
|
||||
// GLFW callbacks
|
||||
// - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any.
|
||||
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call those function yourself from your own GLFW callbacks.
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
|
||||
677
src/render/gui/imgui/imgui_impl_opengl3.cpp
Executable file
677
src/render/gui/imgui/imgui_impl_opengl3.cpp
Executable file
@@ -0,0 +1,677 @@
|
||||
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 2.x 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
|
||||
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
|
||||
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
|
||||
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
|
||||
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
|
||||
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
|
||||
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
|
||||
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
|
||||
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
|
||||
// 2019-03-15: OpenGL: Added a dummy GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
|
||||
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
|
||||
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
|
||||
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
|
||||
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
|
||||
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
|
||||
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
|
||||
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
|
||||
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
|
||||
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
|
||||
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
|
||||
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
|
||||
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
|
||||
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
|
||||
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
|
||||
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
|
||||
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
|
||||
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
|
||||
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
|
||||
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
|
||||
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
|
||||
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
|
||||
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
|
||||
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
|
||||
|
||||
//----------------------------------------
|
||||
// OpenGL GLSL GLSL
|
||||
// version version string
|
||||
//----------------------------------------
|
||||
// 2.0 110 "#version 110"
|
||||
// 2.1 120 "#version 120"
|
||||
// 3.0 130 "#version 130"
|
||||
// 3.1 140 "#version 140"
|
||||
// 3.2 150 "#version 150"
|
||||
// 3.3 330 "#version 330 core"
|
||||
// 4.0 400 "#version 400 core"
|
||||
// 4.1 410 "#version 410 core"
|
||||
// 4.2 420 "#version 410 core"
|
||||
// 4.3 430 "#version 430 core"
|
||||
// ES 2.0 100 "#version 100" = WebGL 1.0
|
||||
// ES 3.0 300 "#version 300 es" = WebGL 2.0
|
||||
//----------------------------------------
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
|
||||
#include <stddef.h> // intptr_t
|
||||
#else
|
||||
#include <stdint.h> // intptr_t
|
||||
#endif
|
||||
|
||||
|
||||
// GL includes
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <GLES2/gl2.h>
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
|
||||
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
|
||||
#else
|
||||
#include <GLES3/gl3.h> // Use GL ES 3
|
||||
#endif
|
||||
#else
|
||||
// About Desktop OpenGL function loaders:
|
||||
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
|
||||
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
|
||||
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
|
||||
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
|
||||
#include <GL/gl3w.h> // Needs to be initialized with gl3wInit() in user's code
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
|
||||
#include <GL/glew.h> // Needs to be initialized with glewInit() in user's code.
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
|
||||
#include <glad/glad.h> // Needs to be initialized with gladLoadGL() in user's code.
|
||||
#elif defined(IMGUI_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(IMGUI_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 IMGUI_IMPL_OPENGL_LOADER_CUSTOM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) || !defined(GL_VERSION_3_2)
|
||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 0
|
||||
#else
|
||||
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 1
|
||||
#endif
|
||||
|
||||
// OpenGL Data
|
||||
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
|
||||
static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings.
|
||||
static GLuint g_FontTexture = 0;
|
||||
static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
|
||||
static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
|
||||
static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
|
||||
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
|
||||
|
||||
// Functions
|
||||
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
|
||||
{
|
||||
// Query for GL version (e.g. 320 for GL 3.2)
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
GLint major, minor;
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||
g_GlVersion = major * 100 + minor * 10;
|
||||
#else
|
||||
g_GlVersion = 200; // GLES 2
|
||||
#endif
|
||||
|
||||
// Setup back-end capabilities flags
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendRendererName = "imgui_impl_opengl3";
|
||||
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
if (g_GlVersion >= 320)
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
#endif
|
||||
|
||||
// Store GLSL version string so we can refer to it later in case we recreate shaders.
|
||||
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 100";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_ES3)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 300 es";
|
||||
#elif defined(__APPLE__)
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 150";
|
||||
#else
|
||||
if (glsl_version == NULL)
|
||||
glsl_version = "#version 130";
|
||||
#endif
|
||||
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
|
||||
strcpy(g_GlslVersionString, glsl_version);
|
||||
strcat(g_GlslVersionString, "\n");
|
||||
|
||||
// Dummy construct to make it easily visible in the IDE and debugger which GL loader has been selected.
|
||||
// The code actually never uses the 'gl_loader' variable! It is only here so you can read it!
|
||||
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
|
||||
// you are likely to get a crash below.
|
||||
// You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
|
||||
const char* gl_loader = "Unknown";
|
||||
IM_UNUSED(gl_loader);
|
||||
#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W)
|
||||
gl_loader = "GL3W";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW)
|
||||
gl_loader = "GLEW";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD)
|
||||
gl_loader = "GLAD";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2)
|
||||
gl_loader = "glbinding2";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3)
|
||||
gl_loader = "glbinding3";
|
||||
#elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
gl_loader = "custom";
|
||||
#else
|
||||
gl_loader = "none";
|
||||
#endif
|
||||
|
||||
// Make a dummy GL call (we don't actually need the result)
|
||||
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
|
||||
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
|
||||
GLint current_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_Shutdown()
|
||||
{
|
||||
ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_NewFrame()
|
||||
{
|
||||
if (!g_ShaderHandle)
|
||||
ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
|
||||
{
|
||||
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
#ifdef GL_POLYGON_MODE
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
#endif
|
||||
|
||||
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
|
||||
bool clip_origin_lower_left = true;
|
||||
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
|
||||
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin);
|
||||
if (current_clip_origin == GL_UPPER_LEFT)
|
||||
clip_origin_lower_left = false;
|
||||
#endif
|
||||
|
||||
// Setup viewport, orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
float T = draw_data->DisplayPos.y;
|
||||
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
|
||||
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
|
||||
const float ortho_projection[4][4] =
|
||||
{
|
||||
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
|
||||
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
|
||||
{ 0.0f, 0.0f, -1.0f, 0.0f },
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
|
||||
};
|
||||
glUseProgram(g_ShaderHandle);
|
||||
glUniform1i(g_AttribLocationTex, 0);
|
||||
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
|
||||
#ifdef GL_SAMPLER_BINDING
|
||||
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
|
||||
#endif
|
||||
|
||||
(void)vertex_array_object;
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(vertex_array_object);
|
||||
#endif
|
||||
|
||||
// Bind vertex/index buffers and setup attributes for ImDrawVert
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxPos);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxUV);
|
||||
glEnableVertexAttribArray(g_AttribLocationVtxColor);
|
||||
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
|
||||
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
|
||||
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
|
||||
}
|
||||
|
||||
// OpenGL3 Render function.
|
||||
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
|
||||
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
|
||||
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||
if (fb_width <= 0 || fb_height <= 0)
|
||||
return;
|
||||
|
||||
// Backup GL state
|
||||
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
|
||||
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
#ifdef GL_SAMPLER_BINDING
|
||||
GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
|
||||
#endif
|
||||
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
|
||||
#endif
|
||||
#ifdef GL_POLYGON_MODE
|
||||
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
|
||||
#endif
|
||||
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
|
||||
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
|
||||
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
|
||||
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
|
||||
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
|
||||
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
|
||||
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
|
||||
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
|
||||
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
|
||||
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
|
||||
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
|
||||
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
|
||||
|
||||
// Setup desired GL state
|
||||
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
|
||||
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
|
||||
GLuint vertex_array_object = 0;
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glGenVertexArrays(1, &vertex_array_object);
|
||||
#endif
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
|
||||
// Will project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
|
||||
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
||||
|
||||
// Render command lists
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
|
||||
// Upload vertex/index buffers
|
||||
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != NULL)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
||||
else
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec4 clip_rect;
|
||||
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
|
||||
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
|
||||
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
|
||||
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
|
||||
|
||||
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
|
||||
{
|
||||
// Apply scissor/clipping rectangle
|
||||
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
|
||||
|
||||
// Bind texture, Draw
|
||||
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
|
||||
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
|
||||
if (g_GlVersion >= 320)
|
||||
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
|
||||
else
|
||||
#endif
|
||||
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy the temporary VAO
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glDeleteVertexArrays(1, &vertex_array_object);
|
||||
#endif
|
||||
|
||||
// Restore modified GL state
|
||||
glUseProgram(last_program);
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
#ifdef GL_SAMPLER_BINDING
|
||||
glBindSampler(0, last_sampler);
|
||||
#endif
|
||||
glActiveTexture(last_active_texture);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(last_vertex_array_object);
|
||||
#endif
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
|
||||
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
|
||||
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
|
||||
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
|
||||
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
|
||||
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
|
||||
#ifdef GL_POLYGON_MODE
|
||||
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
|
||||
#endif
|
||||
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
|
||||
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
|
||||
|
||||
// Upload texture to graphics system
|
||||
GLint last_texture;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
glGenTextures(1, &g_FontTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
#ifdef GL_UNPACK_ROW_LENGTH
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
#endif
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
|
||||
|
||||
// Restore state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DestroyFontsTexture()
|
||||
{
|
||||
if (g_FontTexture)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
glDeleteTextures(1, &g_FontTexture);
|
||||
io.Fonts->TexID = 0;
|
||||
g_FontTexture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
|
||||
static bool CheckShader(GLuint handle, const char* desc)
|
||||
{
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
|
||||
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
|
||||
static bool CheckProgram(GLuint handle, const char* desc)
|
||||
{
|
||||
GLint status = 0, log_length = 0;
|
||||
glGetProgramiv(handle, GL_LINK_STATUS, &status);
|
||||
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
|
||||
if ((GLboolean)status == GL_FALSE)
|
||||
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
|
||||
if (log_length > 1)
|
||||
{
|
||||
ImVector<char> buf;
|
||||
buf.resize((int)(log_length + 1));
|
||||
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
|
||||
fprintf(stderr, "%s\n", buf.begin());
|
||||
}
|
||||
return (GLboolean)status == GL_TRUE;
|
||||
}
|
||||
|
||||
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
|
||||
{
|
||||
// Backup GL state
|
||||
GLint last_texture, last_array_buffer;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
GLint last_vertex_array;
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
|
||||
#endif
|
||||
|
||||
// Parse GLSL version string
|
||||
int glsl_version = 130;
|
||||
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
|
||||
|
||||
const GLchar* vertex_shader_glsl_120 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"attribute vec2 Position;\n"
|
||||
"attribute vec2 UV;\n"
|
||||
"attribute vec4 Color;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_130 =
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"in vec2 Position;\n"
|
||||
"in vec2 UV;\n"
|
||||
"in vec4 Color;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_300_es =
|
||||
"precision mediump float;\n"
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* vertex_shader_glsl_410_core =
|
||||
"layout (location = 0) in vec2 Position;\n"
|
||||
"layout (location = 1) in vec2 UV;\n"
|
||||
"layout (location = 2) in vec4 Color;\n"
|
||||
"uniform mat4 ProjMtx;\n"
|
||||
"out vec2 Frag_UV;\n"
|
||||
"out vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Frag_UV = UV;\n"
|
||||
" Frag_Color = Color;\n"
|
||||
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_120 =
|
||||
"#ifdef GL_ES\n"
|
||||
" precision mediump float;\n"
|
||||
"#endif\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"varying vec2 Frag_UV;\n"
|
||||
"varying vec4 Frag_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_130 =
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_300_es =
|
||||
"precision mediump float;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
const GLchar* fragment_shader_glsl_410_core =
|
||||
"in vec2 Frag_UV;\n"
|
||||
"in vec4 Frag_Color;\n"
|
||||
"uniform sampler2D Texture;\n"
|
||||
"layout (location = 0) out vec4 Out_Color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
|
||||
"}\n";
|
||||
|
||||
// Select shaders matching our GLSL versions
|
||||
const GLchar* vertex_shader = NULL;
|
||||
const GLchar* fragment_shader = NULL;
|
||||
if (glsl_version < 130)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_120;
|
||||
fragment_shader = fragment_shader_glsl_120;
|
||||
}
|
||||
else if (glsl_version >= 410)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_410_core;
|
||||
fragment_shader = fragment_shader_glsl_410_core;
|
||||
}
|
||||
else if (glsl_version == 300)
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_300_es;
|
||||
fragment_shader = fragment_shader_glsl_300_es;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertex_shader = vertex_shader_glsl_130;
|
||||
fragment_shader = fragment_shader_glsl_130;
|
||||
}
|
||||
|
||||
// Create shaders
|
||||
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
|
||||
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
|
||||
glCompileShader(g_VertHandle);
|
||||
CheckShader(g_VertHandle, "vertex shader");
|
||||
|
||||
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
|
||||
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
|
||||
glCompileShader(g_FragHandle);
|
||||
CheckShader(g_FragHandle, "fragment shader");
|
||||
|
||||
g_ShaderHandle = glCreateProgram();
|
||||
glAttachShader(g_ShaderHandle, g_VertHandle);
|
||||
glAttachShader(g_ShaderHandle, g_FragHandle);
|
||||
glLinkProgram(g_ShaderHandle);
|
||||
CheckProgram(g_ShaderHandle, "shader program");
|
||||
|
||||
g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
|
||||
g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
|
||||
g_AttribLocationVtxPos = glGetAttribLocation(g_ShaderHandle, "Position");
|
||||
g_AttribLocationVtxUV = glGetAttribLocation(g_ShaderHandle, "UV");
|
||||
g_AttribLocationVtxColor = glGetAttribLocation(g_ShaderHandle, "Color");
|
||||
|
||||
// Create buffers
|
||||
glGenBuffers(1, &g_VboHandle);
|
||||
glGenBuffers(1, &g_ElementsHandle);
|
||||
|
||||
ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
|
||||
// Restore modified GL state
|
||||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
||||
#ifndef IMGUI_IMPL_OPENGL_ES2
|
||||
glBindVertexArray(last_vertex_array);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
|
||||
{
|
||||
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
|
||||
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
|
||||
if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
|
||||
if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); }
|
||||
if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; }
|
||||
if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; }
|
||||
if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; }
|
||||
|
||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
}
|
||||
84
src/render/gui/imgui/imgui_impl_opengl3.h
Executable file
84
src/render/gui/imgui/imgui_impl_opengl3.h
Executable file
@@ -0,0 +1,84 @@
|
||||
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
|
||||
// - Desktop GL: 2.x 3.x 4.x
|
||||
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
|
||||
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
|
||||
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
|
||||
// https://github.com/ocornut/imgui
|
||||
|
||||
// About Desktop OpenGL function loaders:
|
||||
// Modern Desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
|
||||
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
|
||||
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
|
||||
|
||||
// About GLSL version:
|
||||
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
|
||||
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
|
||||
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
|
||||
|
||||
#pragma once
|
||||
#include "imgui.h" // IMGUI_IMPL_API
|
||||
|
||||
// Backend API
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// (Optional) Called by Init/NewFrame/Shutdown
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
||||
|
||||
// Specific OpenGL ES versions
|
||||
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
|
||||
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
|
||||
|
||||
// Attempt to auto-detect the default Desktop GL loader based on available header files.
|
||||
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
|
||||
// you are likely to get a crash in ImGui_ImplOpenGL3_Init().
|
||||
// You can explicitly select a loader by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
|
||||
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_ES3) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \
|
||||
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
|
||||
|
||||
// Try to detect GLES on matching platforms
|
||||
#if defined(__APPLE__)
|
||||
#include "TargetConditionals.h"
|
||||
#endif
|
||||
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
|
||||
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
|
||||
|
||||
// Otherwise try to detect supported Desktop OpenGL loaders..
|
||||
#elif defined(__has_include)
|
||||
#if __has_include(<GL/glew.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
|
||||
#elif __has_include(<glad/glad.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
|
||||
#elif __has_include(<GL/gl3w.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
|
||||
#elif __has_include(<glbinding/glbinding.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
|
||||
#elif __has_include(<glbinding/Binding.h>)
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
|
||||
#else
|
||||
#error "Cannot detect OpenGL loader!"
|
||||
#endif
|
||||
#else
|
||||
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
|
||||
#endif
|
||||
|
||||
#endif
|
||||
2688
src/render/gui/imgui/imgui_internal.h
Normal file
2688
src/render/gui/imgui/imgui_internal.h
Normal file
File diff suppressed because it is too large
Load Diff
4028
src/render/gui/imgui/imgui_tables.cpp
Normal file
4028
src/render/gui/imgui/imgui_tables.cpp
Normal file
File diff suppressed because it is too large
Load Diff
8056
src/render/gui/imgui/imgui_widgets.cpp
Normal file
8056
src/render/gui/imgui/imgui_widgets.cpp
Normal file
File diff suppressed because it is too large
Load Diff
639
src/render/gui/imgui/imstb_rectpack.h
Normal file
639
src/render/gui/imgui/imstb_rectpack.h
Normal file
@@ -0,0 +1,639 @@
|
||||
// [DEAR IMGUI]
|
||||
// This is a slightly modified version of stb_rect_pack.h 1.00.
|
||||
// Those changes would need to be pushed into nothings/stb:
|
||||
// - Added STBRP__CDECL
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
|
||||
// stb_rect_pack.h - v1.00 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
typedef int stbrp_coord;
|
||||
#else
|
||||
typedef unsigned short stbrp_coord;
|
||||
#endif
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
#ifndef STBRP_LARGE_RECTS
|
||||
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
|
||||
#endif
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
context->extra[1].y = (1<<30);
|
||||
#else
|
||||
context->extra[1].y = 65535;
|
||||
#endif
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
STBRP_ASSERT(y <= best_y);
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
// [DEAR IMGUI] Added STBRP__CDECL
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
#ifdef STBRP_LARGE_RECTS
|
||||
#define STBRP__MAXVAL 0xffffffff
|
||||
#else
|
||||
#define STBRP__MAXVAL 0xffff
|
||||
#endif
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
1447
src/render/gui/imgui/imstb_textedit.h
Normal file
1447
src/render/gui/imgui/imstb_textedit.h
Normal file
File diff suppressed because it is too large
Load Diff
4903
src/render/gui/imgui/imstb_truetype.h
Normal file
4903
src/render/gui/imgui/imstb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
74
src/render/loader/GLLoader.cpp
Normal file
74
src/render/loader/GLLoader.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* GLLoader.cpp
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
|
||||
#include "render/loader/GLLoader.h"
|
||||
#include "glbinding/gl/gl.h"
|
||||
|
||||
using namespace gl;
|
||||
|
||||
namespace GL{
|
||||
|
||||
VAO::~VAO(){
|
||||
if(m_ID != 0)
|
||||
glDeleteVertexArrays(1, &m_ID);
|
||||
}
|
||||
|
||||
VAO::VAO(unsigned int vertexCount) : m_VertexCount(vertexCount){
|
||||
glGenVertexArrays(1, &m_ID);
|
||||
}
|
||||
|
||||
void VAO::bind() const{
|
||||
glBindVertexArray(m_ID);
|
||||
}
|
||||
|
||||
void VAO::unbind() const{
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void VAO::bindVBO(VBO& vbo){
|
||||
vbo.bind();
|
||||
vbo.bindVertexAttribs();
|
||||
m_Vbos.push_back(std::move(vbo));
|
||||
}
|
||||
|
||||
VBO::~VBO(){
|
||||
if(m_ID != 0)
|
||||
glDeleteBuffers(1, &m_ID);
|
||||
}
|
||||
|
||||
VBO::VBO(const std::vector<float>& data, unsigned int stride) : m_DataStride(stride){
|
||||
glGenBuffers(1, &m_ID);
|
||||
bind();
|
||||
glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), nullptr, GL_STATIC_DRAW);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, data.size() * sizeof(float), data.data());
|
||||
unbind();
|
||||
}
|
||||
|
||||
void VBO::bind() const{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m_ID);
|
||||
}
|
||||
|
||||
void VBO::unbind() const{
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
void VBO::addVertexAttribPointer(unsigned int index, unsigned int coordinateSize, unsigned int offset){
|
||||
VertexAttribPointer pointer;
|
||||
pointer.m_Index = index;
|
||||
pointer.m_Size = coordinateSize;
|
||||
pointer.m_Offset = offset;
|
||||
m_VertexAttribs.push_back(pointer);
|
||||
}
|
||||
|
||||
void VBO::bindVertexAttribs() const{
|
||||
for(const VertexAttribPointer& pointer : m_VertexAttribs){
|
||||
glEnableVertexAttribArray(pointer.m_Index);
|
||||
glVertexAttribPointer(pointer.m_Index, pointer.m_Size, GL_FLOAT, false, m_DataStride * sizeof(float), (void*) pointer.m_Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/render/loader/TextureLoader.cpp
Normal file
46
src/render/loader/TextureLoader.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* TextureLoader.cpp
|
||||
*
|
||||
* Created on: 15 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#include "render/loader/TextureLoader.h"
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "render/loader/stb_image.h"
|
||||
#include <iostream>
|
||||
#include <glbinding/gl/gl.h>
|
||||
|
||||
using namespace gl;
|
||||
|
||||
namespace TextureLoader {
|
||||
const unsigned int loadGLTexture(const char* fileName) {
|
||||
|
||||
int width, height, comp;
|
||||
|
||||
const unsigned char* image = stbi_load(fileName, &width, &height, &comp, STBI_rgb_alpha);
|
||||
|
||||
if (image == nullptr) {
|
||||
std::cerr << "Erreur lors du chargement de la texture !" << std::endl;
|
||||
throw(std::runtime_error("Failed to load texture"));
|
||||
}
|
||||
|
||||
GLuint textureID;
|
||||
glGenTextures(1, &textureID);
|
||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
if (comp == 3)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB,
|
||||
GL_UNSIGNED_BYTE, image);
|
||||
else if (comp == 4)
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, image);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
stbi_image_free((void*) image);
|
||||
return textureID;
|
||||
}
|
||||
}
|
||||
161
src/render/loader/WorldLoader.cpp
Normal file
161
src/render/loader/WorldLoader.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "render/loader/WorldLoader.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
namespace td {
|
||||
namespace render {
|
||||
|
||||
namespace WorldLoader {
|
||||
|
||||
GL::VAO loadMobModel(){
|
||||
std::vector<float> positions = {
|
||||
-0.5, -0.5,
|
||||
0.5, -0.5,
|
||||
-0.5, 0.5,
|
||||
|
||||
0.5, -0.5,
|
||||
-0.5, 0.5,
|
||||
0.5, 0.5
|
||||
};
|
||||
|
||||
float yellowFloat;
|
||||
int yellow = 255 << 24 | 255 << 16 | 255;
|
||||
memcpy(&yellowFloat, &yellow, sizeof(int));
|
||||
|
||||
std::vector<float> colors = {
|
||||
yellowFloat,
|
||||
yellowFloat,
|
||||
yellowFloat,
|
||||
|
||||
yellowFloat,
|
||||
yellowFloat,
|
||||
yellowFloat
|
||||
};
|
||||
|
||||
GL::VBO positionVBO(positions, 2);
|
||||
positionVBO.addVertexAttribPointer(0, 2, 0);
|
||||
GL::VBO colorVBO(colors, 1);
|
||||
colorVBO.addVertexAttribPointer(1, 1, 0);
|
||||
|
||||
GL::VAO mobVao(colors.size()); // each pos = 1 color
|
||||
mobVao.bind();
|
||||
mobVao.bindVBO(positionVBO);
|
||||
mobVao.bindVBO(colorVBO);
|
||||
mobVao.unbind();
|
||||
return mobVao;
|
||||
}
|
||||
|
||||
GL::VAO loadWorldModel(const td::game::World* world){
|
||||
std::vector<float> positions;
|
||||
std::vector<float> colors;
|
||||
|
||||
for (const auto& chunkInfo : world->getChunks()){
|
||||
const td::game::ChunkCoord& coords = chunkInfo.first;
|
||||
td::game::ChunkPtr chunk = chunkInfo.second;
|
||||
|
||||
std::int32_t chunkX = coords.first * td::game::Chunk::ChunkWidth;
|
||||
std::int32_t chunkY = coords.second * td::game::Chunk::ChunkHeight;
|
||||
|
||||
for (int tileY = 0; tileY < td::game::Chunk::ChunkHeight; tileY++){
|
||||
for (int tileX = 0; tileX < td::game::Chunk::ChunkWidth; tileX++){
|
||||
int tileNumber = tileY * td::game::Chunk::ChunkWidth + tileX;
|
||||
td::game::TileIndex tileIndex = chunk->getTileIndex(tileNumber);
|
||||
td::game::TilePtr tile = world->getTilePtr(tileIndex);
|
||||
|
||||
if (tile == nullptr)
|
||||
continue;
|
||||
|
||||
positions.insert(positions.end(), {
|
||||
(float)chunkX + tileX, (float)chunkY + tileY,
|
||||
(float)chunkX + tileX + 1, (float)chunkY + tileY,
|
||||
(float)chunkX + tileX, (float)chunkY + tileY + 1,
|
||||
|
||||
(float)chunkX + tileX + 1, (float)chunkY + tileY,
|
||||
(float)chunkX + tileX, (float)chunkY + tileY + 1,
|
||||
(float)chunkX + tileX + 1, (float)chunkY + tileY + 1,
|
||||
});
|
||||
|
||||
/*positions.push_back(chunkX + tileX);
|
||||
positions.push_back(chunkY + tileY);
|
||||
|
||||
positions.push_back(chunkX + tileX + 1);
|
||||
positions.push_back(chunkY + tileY);
|
||||
|
||||
positions.push_back(chunkX + tileX);
|
||||
positions.push_back(chunkY + tileY + 1);
|
||||
|
||||
|
||||
|
||||
positions.push_back(chunkX + tileX + 1);
|
||||
positions.push_back(chunkY + tileY);
|
||||
|
||||
positions.push_back(chunkX + tileX);
|
||||
positions.push_back(chunkY + tileY + 1);
|
||||
|
||||
positions.push_back(chunkX + tileX + 1);
|
||||
positions.push_back(chunkY + tileY + 1);*/
|
||||
|
||||
const td::game::Color& tileColor = world->getTileColor(tile);
|
||||
|
||||
for (int i = 0; i < 6; i++){
|
||||
int color = 255;
|
||||
color |= tileColor.r << 24;
|
||||
color |= tileColor.g << 16;
|
||||
color |= tileColor.b << 8;
|
||||
|
||||
int newColorIndex = colors.size();
|
||||
colors.push_back(0);
|
||||
|
||||
memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int spawnColor = 0; spawnColor < 2; spawnColor++){
|
||||
const game::Spawn& spawn = world->getSpawn(game::TeamColor(spawnColor));
|
||||
float fromX = spawn.x - 2, toX = spawn.x + 3;
|
||||
float fromY = spawn.y - 2, toY = spawn.y + 3;
|
||||
|
||||
positions.insert(positions.end(), {
|
||||
fromX, fromY,
|
||||
fromX, toY,
|
||||
toX, fromY,
|
||||
|
||||
toX, toY,
|
||||
fromX, toY,
|
||||
toX, fromY,
|
||||
});
|
||||
|
||||
for (int i = 0; i < 6; i++){
|
||||
int color = 255;
|
||||
color |= world->getSpawnColor(game::TeamColor(spawnColor)).r << 24;
|
||||
color |= world->getSpawnColor(game::TeamColor(spawnColor)).g << 16;
|
||||
color |= world->getSpawnColor(game::TeamColor(spawnColor)).b << 8;
|
||||
|
||||
int newColorIndex = colors.size();
|
||||
colors.push_back(0);
|
||||
|
||||
memcpy(colors.data() + newColorIndex, &color, 1 * sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
GL::VBO positionVBO(positions, 2);
|
||||
positionVBO.addVertexAttribPointer(0, 2, 0);
|
||||
GL::VBO colorVBO(colors, 1);
|
||||
colorVBO.addVertexAttribPointer(1, 1, 0);
|
||||
|
||||
GL::VAO worldVao(positions.size() / 2); // each pos = 2 vertecies
|
||||
worldVao.bind();
|
||||
worldVao.bindVBO(positionVBO);
|
||||
worldVao.bindVBO(colorVBO);
|
||||
worldVao.unbind();
|
||||
return worldVao;
|
||||
}
|
||||
|
||||
} // namespace WorldLoader
|
||||
|
||||
|
||||
} // namespace render
|
||||
} // namespace td
|
||||
80
src/render/shaders/EntityShader.cpp
Normal file
80
src/render/shaders/EntityShader.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* EntityShader.cpp
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#include "render/shaders/EntityShader.h"
|
||||
|
||||
static const char vertexSource[] = R"(
|
||||
#version 330
|
||||
|
||||
in vec2 position;
|
||||
in int color;
|
||||
|
||||
uniform vec2 camPos = vec2(0, 0);
|
||||
uniform float zoom = 1;
|
||||
uniform float aspectRatio = 0;
|
||||
uniform vec2 translation;
|
||||
uniform float isometricView;
|
||||
|
||||
flat out int pass_color;
|
||||
|
||||
void main(void){
|
||||
float modelX = position.x + translation.x;
|
||||
float modelY = position.y + translation.y;
|
||||
float x = (modelX - camPos.x + (modelY - camPos.y) * isometricView) / aspectRatio * zoom;
|
||||
float y = ((-0.5 * (modelX - camPos.x) + 0.5 * (modelY - camPos.y)) * isometricView + (modelY - camPos.y) * (1 - isometricView)) * zoom;
|
||||
pass_color = color;
|
||||
gl_Position = vec4(x, -y, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char fragmentSource[] = R"(
|
||||
#version 330
|
||||
|
||||
flat in int pass_color;
|
||||
|
||||
out vec4 out_color;
|
||||
|
||||
void main(void){
|
||||
|
||||
float r = float(pass_color >> 24 & 0xFF) / 255.0;
|
||||
float g = float(pass_color >> 16 & 0xFF) / 255.0;
|
||||
float b = float(pass_color >> 8 & 0xFF) / 255.0;
|
||||
float a = float(pass_color & 0xFF) / 255.0;
|
||||
out_color = vec4(r, g, b, a);
|
||||
|
||||
}
|
||||
)";
|
||||
|
||||
EntityShader::EntityShader(): ShaderProgram(){}
|
||||
|
||||
void EntityShader::loadShader(){
|
||||
ShaderProgram::loadProgram(vertexSource, fragmentSource);
|
||||
}
|
||||
|
||||
void EntityShader::getAllUniformLocation(){
|
||||
location_aspect_ratio = getUniformLocation("aspectRatio");
|
||||
location_zoom = getUniformLocation("zoom");
|
||||
location_cam = getUniformLocation("camPos");
|
||||
location_translation = getUniformLocation("translation");
|
||||
location_viewtype = getUniformLocation("isometricView");
|
||||
}
|
||||
|
||||
void EntityShader::setCamPos(const glm::vec2& camPos){
|
||||
loadVector(location_cam, camPos);
|
||||
}
|
||||
void EntityShader::setZoom(float zoom){
|
||||
loadFloat(location_zoom, zoom);
|
||||
}
|
||||
void EntityShader::setAspectRatio(float aspectRatio){
|
||||
loadFloat(location_aspect_ratio, aspectRatio);
|
||||
}
|
||||
void EntityShader::setModelPos(const glm::vec2& modelPos){
|
||||
loadVector(location_translation, modelPos);
|
||||
}
|
||||
void EntityShader::setIsometricView(float isometric){
|
||||
loadFloat(location_viewtype, isometric);
|
||||
}
|
||||
161
src/render/shaders/ShaderProgram.cpp
Executable file
161
src/render/shaders/ShaderProgram.cpp
Executable file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* ShaderProgram.cpp
|
||||
*
|
||||
* Created on: 31 janv. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#include "render/shaders/ShaderProgram.h"
|
||||
|
||||
#include <glbinding/gl/gl.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
using namespace gl;
|
||||
|
||||
ShaderProgram::ShaderProgram():
|
||||
programID(0), vertexShaderID(0), fragmentShaderID(0){
|
||||
}
|
||||
|
||||
ShaderProgram::~ShaderProgram(){
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
void ShaderProgram::start() const{
|
||||
glUseProgram(programID);
|
||||
}
|
||||
|
||||
void ShaderProgram::stop() const{
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
int ShaderProgram::getUniformLocation(const std::string& uniformName) const{
|
||||
const int location = glGetUniformLocation(programID, uniformName.c_str());
|
||||
if (location == -1){
|
||||
std::cout << "Warning ! Uniform variable " << uniformName << " not found !\n";
|
||||
const GLenum error = glGetError();
|
||||
switch (error){
|
||||
case GL_INVALID_VALUE:
|
||||
break;
|
||||
|
||||
case GL_INVALID_OPERATION:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
void ShaderProgram::loadFloat(const int location, const float value) const{
|
||||
glUniform1f(location, value);
|
||||
}
|
||||
|
||||
void ShaderProgram::loadInt(const int& location, const int& value) const{
|
||||
glUniform1i(location, value);
|
||||
}
|
||||
|
||||
void ShaderProgram::loadVector(const int& location,
|
||||
const glm::vec2& vector) const{
|
||||
glUniform2f(location, vector.x, vector.y);
|
||||
}
|
||||
|
||||
void ShaderProgram::loadVector(const int& location,
|
||||
const glm::vec3& vector) const{
|
||||
glUniform3f(location, vector.x, vector.y, vector.z);
|
||||
}
|
||||
|
||||
void ShaderProgram::loadVector(const int& location,
|
||||
const glm::vec4& vector) const{
|
||||
glUniform4f(location, vector.x, vector.y, vector.z, vector.w);
|
||||
}
|
||||
|
||||
void ShaderProgram::loadBoolean(const int& location, const bool& value) const{
|
||||
glUniform1i(location, value);
|
||||
}
|
||||
|
||||
void ShaderProgram::cleanUp() const{
|
||||
stop();
|
||||
glDetachShader(programID, vertexShaderID);
|
||||
glDetachShader(programID, fragmentShaderID);
|
||||
glDeleteShader(vertexShaderID);
|
||||
glDeleteShader(fragmentShaderID);
|
||||
glDeleteProgram(programID);
|
||||
}
|
||||
|
||||
void ShaderProgram::loadProgramFile(const std::string& vertexFile,
|
||||
const std::string& fragmentFile){
|
||||
vertexShaderID = loadShaderFromFile(vertexFile, GL_VERTEX_SHADER);
|
||||
fragmentShaderID = loadShaderFromFile(fragmentFile, GL_FRAGMENT_SHADER);
|
||||
programID = glCreateProgram();
|
||||
glAttachShader(programID, vertexShaderID);
|
||||
glAttachShader(programID, fragmentShaderID);
|
||||
glLinkProgram(programID);
|
||||
glValidateProgram(programID);
|
||||
getAllUniformLocation();
|
||||
}
|
||||
|
||||
void ShaderProgram::loadProgram(const std::string& vertexSource,
|
||||
const std::string& fragmentSource){
|
||||
vertexShaderID = loadShader(vertexSource, GL_VERTEX_SHADER);
|
||||
fragmentShaderID = loadShader(fragmentSource, GL_FRAGMENT_SHADER);
|
||||
programID = glCreateProgram();
|
||||
glAttachShader(programID, vertexShaderID);
|
||||
glAttachShader(programID, fragmentShaderID);
|
||||
glLinkProgram(programID);
|
||||
glValidateProgram(programID);
|
||||
getAllUniformLocation();
|
||||
}
|
||||
|
||||
int ShaderProgram::loadShader(const std::string& source, GLenum type){
|
||||
unsigned int shaderID = glCreateShader(type);
|
||||
|
||||
const char* c_str = source.c_str();
|
||||
int* null = 0;
|
||||
glShaderSource(shaderID, 1, &c_str, null); // @suppress("Function cannot be resolved")
|
||||
glCompileShader(shaderID);
|
||||
GLint compilesuccessful;
|
||||
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compilesuccessful);
|
||||
if (compilesuccessful == false){
|
||||
std::cout << "Could not compile shader !\n";
|
||||
GLsizei size;
|
||||
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &size);
|
||||
char error[size];
|
||||
glGetShaderInfoLog(shaderID, size, &size, error);
|
||||
std::cout << error << std::endl;
|
||||
}
|
||||
return shaderID;
|
||||
}
|
||||
|
||||
int ShaderProgram::loadShaderFromFile(const std::string& file, GLenum type){
|
||||
std::string shaderSource = "";
|
||||
std::ifstream fileStream(file);
|
||||
if (fileStream.is_open()){
|
||||
std::string line;
|
||||
while (getline(fileStream, line)){
|
||||
shaderSource += line + "\n";
|
||||
}
|
||||
fileStream.close();
|
||||
}
|
||||
unsigned int shaderID = glCreateShader(type);
|
||||
|
||||
const char* c_str = shaderSource.c_str();
|
||||
int* null = 0;
|
||||
glShaderSource(shaderID, 1, &c_str, null); // @suppress("Function cannot be resolved")
|
||||
glCompileShader(shaderID);
|
||||
GLint compilesuccessful;
|
||||
glGetShaderiv(shaderID, GL_COMPILE_STATUS, &compilesuccessful);
|
||||
if (compilesuccessful == false){
|
||||
std::cout << "Could not compile shader !\n";
|
||||
GLsizei size;
|
||||
glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &size);
|
||||
char error[size];
|
||||
glGetShaderInfoLog(shaderID, size, &size, error);
|
||||
std::cout << error << std::endl;
|
||||
}
|
||||
return shaderID;
|
||||
}
|
||||
|
||||
void ShaderProgram::loadMatrix(const int& location, const glm::mat4& matrix){
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(matrix));
|
||||
}
|
||||
73
src/render/shaders/WorldShader.cpp
Normal file
73
src/render/shaders/WorldShader.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* WorldShader.cpp
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#include "render/shaders/WorldShader.h"
|
||||
|
||||
static const char vertexSource[] = R"(
|
||||
#version 330
|
||||
|
||||
layout(location = 0) in vec2 position;
|
||||
layout(location = 1) in int color;
|
||||
|
||||
uniform vec2 camPos = vec2(0, 0);
|
||||
uniform float zoom = 1;
|
||||
uniform float aspectRatio = 0;
|
||||
uniform float isometricView;
|
||||
|
||||
flat out int pass_color;
|
||||
|
||||
void main(void){
|
||||
float x = (position.x - camPos.x + (position.y - camPos.y) * isometricView) / aspectRatio * zoom;
|
||||
float y = ((-0.5 * (position.x - camPos.x) + 0.5 * (position.y - camPos.y)) * isometricView + (position.y - camPos.y) * (1 - isometricView)) * zoom;
|
||||
pass_color = color;
|
||||
gl_Position = vec4(x, -y, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
static const char fragmentSource[] = R"(
|
||||
#version 330
|
||||
|
||||
flat in int pass_color;
|
||||
|
||||
out vec4 out_color;
|
||||
|
||||
void main(void){
|
||||
|
||||
float r = float(pass_color >> 24 & 0xFF) / 255.0;
|
||||
float g = float(pass_color >> 16 & 0xFF) / 255.0;
|
||||
float b = float(pass_color >> 8 & 0xFF) / 255.0;
|
||||
float a = float(pass_color & 0xFF) / 255.0;
|
||||
out_color = vec4(r, g, b, a);
|
||||
|
||||
}
|
||||
)";
|
||||
|
||||
WorldShader::WorldShader(): ShaderProgram(){}
|
||||
|
||||
void WorldShader::loadShader(){
|
||||
ShaderProgram::loadProgram(vertexSource, fragmentSource);
|
||||
}
|
||||
|
||||
void WorldShader::getAllUniformLocation(){
|
||||
location_aspect_ratio = getUniformLocation("aspectRatio");
|
||||
location_zoom = getUniformLocation("zoom");
|
||||
location_cam = getUniformLocation("camPos");
|
||||
location_viewtype = getUniformLocation("isometricView");
|
||||
}
|
||||
|
||||
void WorldShader::setCamPos(const glm::vec2& camPos){
|
||||
loadVector(location_cam, camPos);
|
||||
}
|
||||
void WorldShader::setZoom(float zoom){
|
||||
loadFloat(location_zoom, zoom);
|
||||
}
|
||||
void WorldShader::setAspectRatio(float aspectRatio){
|
||||
loadFloat(location_aspect_ratio, aspectRatio);
|
||||
}
|
||||
void WorldShader::setIsometricView(float isometric){
|
||||
loadFloat(location_viewtype, isometric);
|
||||
}
|
||||
134
src/window/Display.cpp
Normal file
134
src/window/Display.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Display.cpp
|
||||
*
|
||||
* Created on: 4 nov. 2020
|
||||
* Author: simon
|
||||
*/
|
||||
|
||||
#include "window/Display.h"
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include "game/GameManager.h"
|
||||
#include "render/Renderer.h"
|
||||
#include "render/gui/TowerGui.h"
|
||||
#include "render/WorldRenderer.h"
|
||||
|
||||
#define WINDOW_NAME "Tower Defense"
|
||||
#define WINDOW_WIDTH 800
|
||||
#define WINDOW_HEIGHT 600
|
||||
|
||||
namespace Display {
|
||||
|
||||
static GLFWwindow* window;
|
||||
static bool closeRequested = false;
|
||||
|
||||
static int lastWidth = 0, lastHeight = 0;
|
||||
static float aspectRatio;
|
||||
|
||||
void error_callback(int error, const char* description){
|
||||
fprintf(stderr, "Error: %s\n", description);
|
||||
}
|
||||
|
||||
void keyboard_event(GLFWwindow* window, int key, int scancode, int actions, int mods){
|
||||
std::cout << "Key : " << key << " " << scancode << " " << actions << " " << mods << std::endl;
|
||||
}
|
||||
|
||||
void mouseCursorEvent(GLFWwindow* window, double xPos, double yPos){
|
||||
static double lastX = xPos;
|
||||
static double lastY = yPos;
|
||||
if(isMouseDown(0)){
|
||||
const float relativeX = (float) (xPos - lastX) / (float) lastWidth * 2;
|
||||
const float relativeY = (float) (yPos - lastY) / (float) lastHeight * 2;
|
||||
td::render::WorldRenderer::moveCam(relativeX, relativeY, aspectRatio);
|
||||
}
|
||||
lastX = xPos;
|
||||
lastY = yPos;
|
||||
}
|
||||
|
||||
void windowResizeEvent(GLFWwindow* window, int width, int height){
|
||||
aspectRatio = (float) width / height;
|
||||
td::render::Renderer::resize(width, height);
|
||||
lastWidth = width;
|
||||
lastHeight = height;
|
||||
}
|
||||
|
||||
void mouseScroll(GLFWwindow* window, double x, double y){
|
||||
td::render::WorldRenderer::changeZoom(y);
|
||||
}
|
||||
|
||||
void registerCallbacks(){
|
||||
glfwSetKeyCallback(window, &keyboard_event);
|
||||
glfwSetCursorPosCallback(window, &mouseCursorEvent);
|
||||
glfwSetWindowSizeCallback(window, &windowResizeEvent);
|
||||
glfwSetScrollCallback(window, &mouseScroll);
|
||||
}
|
||||
|
||||
void create() {
|
||||
glfwSetErrorCallback(&error_callback);
|
||||
glfwInit();
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, true);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_NAME, nullptr, nullptr);
|
||||
//glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||
//registerCallbacks();
|
||||
glfwMakeContextCurrent(window);
|
||||
if(!td::render::Renderer::init()){
|
||||
exit(1);
|
||||
}
|
||||
TowerGui::init(window);
|
||||
windowResizeEvent(window, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
}
|
||||
|
||||
void render() {
|
||||
td::render::Renderer::prepare();
|
||||
td::render::WorldRenderer::render();
|
||||
TowerGui::render();
|
||||
}
|
||||
|
||||
void update() {
|
||||
glfwSwapBuffers(window);
|
||||
int windowWidth, windowHeight;
|
||||
glfwGetWindowSize(window, &windowWidth, &windowHeight);
|
||||
if(windowWidth != lastWidth || windowHeight != lastHeight){
|
||||
windowResizeEvent(window, windowWidth, windowHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
td::render::Renderer::destroy();
|
||||
td::render::WorldRenderer::destroy();
|
||||
TowerGui::destroy();
|
||||
glfwDestroyWindow(window);
|
||||
window = NULL;
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
void pollEvents() {
|
||||
glfwPollEvents();
|
||||
td::render::WorldRenderer::update();
|
||||
}
|
||||
|
||||
bool isCloseRequested() {
|
||||
return glfwWindowShouldClose(window);
|
||||
}
|
||||
|
||||
const bool isMouseDown(int button) {
|
||||
return glfwGetMouseButton(window, button);
|
||||
}
|
||||
|
||||
float getAspectRatio(){
|
||||
return aspectRatio;
|
||||
}
|
||||
|
||||
int getWindowWidth(){
|
||||
return lastWidth;
|
||||
}
|
||||
int getWindowHeight(){
|
||||
return lastHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
105
xmake.lua
Normal file
105
xmake.lua
Normal file
@@ -0,0 +1,105 @@
|
||||
add_rules("mode.debug", "mode.release")
|
||||
|
||||
add_requires("glbinding", "glfw", "zlib")--, "glm")
|
||||
|
||||
target("Tower Defense")
|
||||
set_kind("binary")
|
||||
add_includedirs("include")
|
||||
add_files("src/*.cpp", "src/*/*.cpp", "src/*/*/*.cpp", "src/*/*/*/*.cpp")
|
||||
add_cxflags("-pthread")
|
||||
|
||||
set_languages("c++14")
|
||||
|
||||
add_links("glbinding", "z")
|
||||
|
||||
if is_os("linux") then
|
||||
add_links("glfw", "GL", "pthread")
|
||||
end
|
||||
if is_os("windows") then
|
||||
add_links("glfw3dll", "opengl32", "ws2_32", "mingw32")
|
||||
end
|
||||
|
||||
if is_mode("release") then
|
||||
-- mark symbols visibility as hidden
|
||||
set_symbols("hidden")
|
||||
|
||||
-- strip all symbols
|
||||
add_ldflags("-s")
|
||||
|
||||
set_optimize("smallest")
|
||||
|
||||
if is_os("windows") then
|
||||
add_ldflags("-static-libgcc", "-static-libstdc++", "-pthread")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- If you want to known more usage about xmake, please see https://xmake.io
|
||||
--
|
||||
-- ## FAQ
|
||||
--
|
||||
-- You can enter the project directory firstly before building project.
|
||||
--
|
||||
-- $ cd projectdir
|
||||
--
|
||||
-- 1. How to build project?
|
||||
--
|
||||
-- $ xmake
|
||||
--
|
||||
-- 2. How to configure project?
|
||||
--
|
||||
-- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release]
|
||||
--
|
||||
-- 3. Where is the build output directory?
|
||||
--
|
||||
-- The default output directory is `./build` and you can configure the output directory.
|
||||
--
|
||||
-- $ xmake f -o outputdir
|
||||
-- $ xmake
|
||||
--
|
||||
-- 4. How to run and debug target after building project?
|
||||
--
|
||||
-- $ xmake run [targetname]
|
||||
-- $ xmake run -d [targetname]
|
||||
--
|
||||
-- 5. How to install target to the system directory or other output directory?
|
||||
--
|
||||
-- $ xmake install
|
||||
-- $ xmake install -o installdir
|
||||
--
|
||||
-- 6. Add some frequently-used compilation flags in xmake.lua
|
||||
--
|
||||
-- @code
|
||||
-- -- add debug and release modes
|
||||
-- add_rules("mode.debug", "mode.release")
|
||||
--
|
||||
-- -- add macro defination
|
||||
-- add_defines("NDEBUG", "_GNU_SOURCE=1")
|
||||
--
|
||||
-- -- set warning all as error
|
||||
-- set_warnings("all", "error")
|
||||
--
|
||||
-- -- set language: c99, c++11
|
||||
-- set_languages("c99", "c++11")
|
||||
--
|
||||
-- -- set optimization: none, faster, fastest, smallest
|
||||
-- set_optimize("fastest")
|
||||
--
|
||||
-- -- add include search directories
|
||||
-- add_includedirs("/usr/include", "/usr/local/include")
|
||||
--
|
||||
-- -- add link libraries and search directories
|
||||
-- add_links("tbox")
|
||||
-- add_linkdirs("/usr/local/lib", "/usr/lib")
|
||||
--
|
||||
-- -- add system link libraries
|
||||
-- add_syslinks("z", "pthread")
|
||||
--
|
||||
-- -- add compilation and link flags
|
||||
-- add_cxflags("-stdnolib", "-fno-strict-aliasing")
|
||||
-- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true})
|
||||
--
|
||||
-- @endcode
|
||||
--
|
||||
|
||||
Reference in New Issue
Block a user