Skip to content

Commit

Permalink
🌄 Added icon loading
Browse files Browse the repository at this point in the history
  • Loading branch information
Lygaen committed Jun 19, 2023
1 parent a2c3563 commit 578ba4e
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 7 deletions.
11 changes: 7 additions & 4 deletions src/net/packets/status/serverlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ void ServerListPacket::write(IStream *stream)
Config::inst()->MOTD.getValue().save(description, alloc);
document.AddMember("description", description, alloc);

// TODO Add favicon in base64 with prepending "data:image/png;base64,"
// rapidjson::Value favicon(rapidjson::kStringType);
// --- load the favicon from config ---
// document.AddMember("favicon", favicon, alloc);
rapidjson::Value favicon(rapidjson::kStringType);

const std::string &data = Config::inst()->ICON_FILE.getValue().getBase64String();
std::string f = std::string("data:image/png;base64,") + data;
favicon.SetString(f.c_str(), f.size());

document.AddMember("favicon", favicon, alloc);

rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
Expand Down
14 changes: 14 additions & 0 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ Server::~Server()
}
}

void Server::checks()
{
const PNGFile &icon = Config::inst()->ICON_FILE.getValue();
if (icon.getHeight() != icon.getWidth() != 64)
{
// Notchian clients only render 64x64 images
logger::warn("Invalid image ! Check it's resolution (must be 64x64) or just if it's there !");
Config::inst()->ICON_FILE.setValue(PNGFile()); // Frees memory and deletes config entry
}
}

void Server::start()
{
std::string addr = Config::inst()->ADDRESS.getValue();
Expand All @@ -39,6 +50,9 @@ void Server::start()
exit(EXIT_FAILURE);
}
sock.start(Config::inst()->BACKLOG.getValue());

checks();

logger::info("Server started on %s:%d !", addr.c_str(), port);

isRunning = true;
Expand Down
9 changes: 9 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ class Server
ServerSocket sock;
bool isRunning{};

/**
* @brief Internal Checks
*
* Internal checks that will not
* prevent the integrity of the
* server.
*/
void checks();

public:
/**
* @brief Construct a new Server object
Expand Down
24 changes: 23 additions & 1 deletion src/utils/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ inline rapidjson::Document::ConstMemberIterator Field<T>::canSafelyRead(const ra
auto loc = document.FindMember(section);
if (loc == document.MemberEnd())
return document.MemberEnd();
return loc->value.IsObject() ? document.MemberEnd() : loc->value.FindMember(key);
return !loc->value.IsObject() ? document.MemberEnd() : loc->value.FindMember(key);
}

template <typename T>
Expand Down Expand Up @@ -117,6 +117,28 @@ void Field<std::string>::save(rapidjson::Document &document)
writeSafely(document, v);
}

template <>
void Field<PNGFile>::load(const rapidjson::Document &document)
{
auto loc = canSafelyRead(document);
if (loc == document.MemberEnd())
return;

if (!loc->value.IsString())
return;

std::string s = std::string(std::string(loc->value.GetString(), loc->value.GetStringLength()));
if (!s.empty())
value = PNGFile(s);
}
template <>
void Field<PNGFile>::save(rapidjson::Document &document)
{
rapidjson::Value v;
v.SetString(value.getPath().c_str(), value.getPath().length(), document.GetAllocator());
writeSafely(document, v);
}

template <>
void Field<bool>::load(const rapidjson::Document &document)
{
Expand Down
13 changes: 11 additions & 2 deletions src/utils/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <memory>
#include <rapidjson/document.h>
#include <net/types/chatmessage.h>
#include <utils/file.h>

/**
* @brief The Field Object for the ::Config
Expand Down Expand Up @@ -193,6 +194,13 @@ class Config
* should use.
*/
Field<std::string> LOGLEVEL = Field("other", "loglevel", std::string("ALL"));
/**
* @brief The Icon File
*
* The icon file that is sent to the server
* while pinging.
*/
Field<PNGFile> ICON_FILE = Field("display", "icon_file", PNGFile());

/**
* @brief List of all the config fields
Expand All @@ -202,7 +210,7 @@ class Config
* on all of the fields by defining the UF(x) macro.
*/
#define CONFIG_FIELDS UF(PORT) UF(MOTD) UF(LOGLEVEL) UF(COMPRESSION_LVL) UF(ONLINE_MODE) UF(ADDRESS) \
UF(BACKLOG) UF(MAX_PLAYERS)
UF(BACKLOG) UF(MAX_PLAYERS) UF(ICON_FILE)

/**
* @brief The Version Number
Expand All @@ -226,7 +234,8 @@ class Config
* the singleton class-like.
* @return ::Config* The instance of the config
*/
static Config *inst()
static Config *
inst()
{
return INSTANCE;
}
Expand Down
98 changes: 98 additions & 0 deletions src/utils/file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include "file.h"
#include <fstream>
#include <net/stream.h>
#include <openssl/evp.h>
#include <utils/logger.h>

File::File() : path("")
{
}

File::File(std::string path) : path(path)
{
load();
}

File::~File()
{
}

bool File::load()
{
std::ifstream file(path, std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);

data.resize(size);
file.read(data.data(), size);

return file.good();
}

void File::setPath(std::string path)
{
this->path = path;
}

const std::string &File::getPath() const
{
return path;
}

const char *File::getPointer() const
{
return data.data();
}

int File::getSize() const
{
return data.size();
}

PNGFile::PNGFile() : File()
{
}

PNGFile::PNGFile(std::string path) : File(path), width(0), height(0)
{
MemoryStream m;
m.write(reinterpret_cast<std::byte *>(const_cast<char *>(getPointer())), 1, 15 + 8);

char buff[3];
m.read(reinterpret_cast<std::byte *>(buff), 0, 3);

if (buff[0] != 'P' || buff[1] != 'N' || buff[2] != 'G')
return;

// Crafty way to just skip 12 bytes
m.readLong();
m.readInt();

int32_t temp = m.readInt();
width = *reinterpret_cast<unsigned int *>(&temp);
temp = m.readInt();
height = *reinterpret_cast<unsigned int *>(&temp);

char encoded[((getSize() + 2) / 3) * 4];
EVP_EncodeBlock(reinterpret_cast<unsigned char *>(encoded), reinterpret_cast<unsigned char *>(const_cast<char *>(getPointer())), getSize());
base64String = std::string(encoded, ((getSize() + 2) / 3) * 4);
}

PNGFile::~PNGFile()
{
}

unsigned int PNGFile::getWidth() const
{
return width;
}

unsigned int PNGFile::getHeight() const
{
return height;
}

const std::string &PNGFile::getBase64String() const
{
return base64String;
}
144 changes: 144 additions & 0 deletions src/utils/file.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* @file file.h
* @author Mathieu Cayeux
* @brief The file containing file (lol) loading logic
* @version 0.1
* @date 2023-06-19
*
* @copyright Copyright (c) 2023
*
*/

#ifndef MINESERVER_FILE_H
#define MINESERVER_FILE_H

#include <string>
#include <vector>

/**
* @brief File Loader Wrapper
*
*/
class File
{
private:
std::vector<char> data;
std::string path;

public:
/**
* @brief Constructs a new File object
*
* Constructs an empty file object, really
* just used for the config or placeholder
* objects.
*/
File();
/**
* @brief Construct a new File object
*
* Internally calls File::load()
* @param path The Path of the file
*/
File(std::string path);
/**
* @brief Destroy the File object
*
*/
~File();

/**
* @brief Loads the data of the file into ram
*
* @return true file was loaded correctly
* @return false file was not loaded correctly
*/
bool load();

/**
* @brief Set the Path of the file
*
* You should call File::load() to load
* the file data afterwards.
* @param path the path of the targeted file
*/
void setPath(std::string path);
/**
* @brief Get the Path object
*
* @warning The path does not assure that the data
* stored in this object contains the data
* of the file at the path.
* @return const std::string& the path of the file
*/
const std::string &getPath() const;

/**
* @brief Get the pointer to the data
*
* @return const char* the pointer to the file data
*/
const char *getPointer() const;
/**
* @brief Get the size of the file stored
*
* @return int the size of the file stored
*/
int getSize() const;
};

/**
* @brief Wrapper around ::File for PNG files
*
*/
class PNGFile : public File
{
private:
unsigned int width, height;
std::string base64String;

public:
/**
* @brief Construct a new PNGFile object
*
* Constructs an empty file object, really
* just used for the config or placeholder
* objects.
*/
PNGFile();
/**
* @brief Construct a new PNGFile object
*
* Internally calls File::load(), then
* does few calculations for loading
* data used in the server.
* @param path The Path of the file
*/
PNGFile(std::string path);
/**
* @brief Destroy the PNGFile object
*
*/
~PNGFile();

/**
* @brief Get the Width of the file
*
* @return unsigned int the width
*/
unsigned int getWidth() const;
/**
* @brief Get the Height of the file
*
* @return unsigned int the height
*/
unsigned int getHeight() const;
/**
* @brief Get the Base64 representation of the file
*
* @return const std::string& the base64 representation
*/
const std::string &getBase64String() const;
};

#endif // MINESERVER_FILE_H

0 comments on commit 578ba4e

Please sign in to comment.