mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-03 22:21:59 +00:00 
			
		
		
		
	Merge pull request #1715 from DarkflameUniverse/webapiv2
feat: Chat Web API (now with no threading)
This commit is contained in:
		@@ -248,6 +248,8 @@ include_directories(
 | 
			
		||||
	"thirdparty/SQLite"
 | 
			
		||||
	"thirdparty/cpplinq"
 | 
			
		||||
	"thirdparty/MD5"
 | 
			
		||||
	"thirdparty/nlohmann"
 | 
			
		||||
	"thirdparty/mongoose"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
 | 
			
		||||
 
 | 
			
		||||
@@ -354,6 +354,10 @@ Now follow the [build](#build-the-server) section for your system and your serve
 | 
			
		||||
## In-game commands
 | 
			
		||||
* A list of all in-game commands can be found [here](./docs/Commands.md).
 | 
			
		||||
 | 
			
		||||
## Chat Web API
 | 
			
		||||
* The Chat server has an API that can be enabled via `chatconfig.ini`.
 | 
			
		||||
* You can view the OpenAPI doc for the API here [here](./docs/ChatWebAPI.yaml).
 | 
			
		||||
 | 
			
		||||
## Verifying your client files
 | 
			
		||||
 | 
			
		||||
### LEGO® Universe 1.10.64
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@ set(DCHATSERVER_SOURCES
 | 
			
		||||
	"ChatIgnoreList.cpp"
 | 
			
		||||
	"ChatPacketHandler.cpp"
 | 
			
		||||
	"PlayerContainer.cpp"
 | 
			
		||||
	"ChatWebAPI.cpp"
 | 
			
		||||
	"JSONUtils.cpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(ChatServer "ChatServer.cpp")
 | 
			
		||||
@@ -12,5 +14,5 @@ add_library(dChatServer ${DCHATSERVER_SOURCES})
 | 
			
		||||
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
 | 
			
		||||
 | 
			
		||||
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
 | 
			
		||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer)
 | 
			
		||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,8 @@
 | 
			
		||||
#include "RakNetDefines.h"
 | 
			
		||||
#include "MessageIdentifiers.h"
 | 
			
		||||
 | 
			
		||||
#include "ChatWebAPI.h"
 | 
			
		||||
 | 
			
		||||
namespace Game {
 | 
			
		||||
	Logger* logger = nullptr;
 | 
			
		||||
	dServer* server = nullptr;
 | 
			
		||||
@@ -74,7 +76,8 @@ int main(int argc, char** argv) {
 | 
			
		||||
		Game::assetManager = new AssetManager(clientPath);
 | 
			
		||||
	} catch (std::runtime_error& ex) {
 | 
			
		||||
		LOG("Got an error while setting up assets: %s", ex.what());
 | 
			
		||||
 | 
			
		||||
		delete Game::logger;
 | 
			
		||||
		delete Game::config;
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -84,11 +87,23 @@ int main(int argc, char** argv) {
 | 
			
		||||
	} catch (std::exception& ex) {
 | 
			
		||||
		LOG("Got an error while connecting to the database: %s", ex.what());
 | 
			
		||||
		Database::Destroy("ChatServer");
 | 
			
		||||
		delete Game::server;
 | 
			
		||||
		delete Game::logger;
 | 
			
		||||
		delete Game::config;
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// seyup the chat api web server
 | 
			
		||||
	bool web_server_enabled = Game::config->GetValue("web_server_enabled") == "1";
 | 
			
		||||
	ChatWebAPI chatwebapi;
 | 
			
		||||
	if (web_server_enabled && !chatwebapi.Startup()){
 | 
			
		||||
		// if we want the web api and it fails to start, exit
 | 
			
		||||
		LOG("Failed to start web server, shutting down.");
 | 
			
		||||
		Database::Destroy("ChatServer");
 | 
			
		||||
		delete Game::logger;
 | 
			
		||||
		delete Game::config;
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	//Find out the master's IP:
 | 
			
		||||
	std::string masterIP;
 | 
			
		||||
	uint32_t masterPort = 1000;
 | 
			
		||||
@@ -151,6 +166,11 @@ int main(int argc, char** argv) {
 | 
			
		||||
			packet = nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//Check and handle web requests:
 | 
			
		||||
		if (web_server_enabled) {
 | 
			
		||||
			chatwebapi.ReceiveRequests();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//Push our log every 30s:
 | 
			
		||||
		if (framesSinceLastFlush >= logFlushTime) {
 | 
			
		||||
			Game::logger->Flush();
 | 
			
		||||
@@ -288,12 +308,11 @@ void HandlePacket(Packet* packet) {
 | 
			
		||||
	case MessageType::Chat::LOGIN_SESSION_NOTIFY:
 | 
			
		||||
		Game::playerContainer.InsertPlayer(packet);
 | 
			
		||||
		break;
 | 
			
		||||
	case MessageType::Chat::GM_ANNOUNCE: {
 | 
			
		||||
	case MessageType::Chat::GM_ANNOUNCE:
 | 
			
		||||
		// we just forward this packet to every connected server
 | 
			
		||||
		inStream.ResetReadPointer();
 | 
			
		||||
		Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
 | 
			
		||||
	}
 | 
			
		||||
									   break;
 | 
			
		||||
		break;
 | 
			
		||||
	case MessageType::Chat::UNEXPECTED_DISCONNECT:
 | 
			
		||||
		Game::playerContainer.ScheduleRemovePlayer(packet);
 | 
			
		||||
		break;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										196
									
								
								dChatServer/ChatWebAPI.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								dChatServer/ChatWebAPI.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
			
		||||
#include "ChatWebAPI.h"
 | 
			
		||||
 | 
			
		||||
#include "Logger.h"
 | 
			
		||||
#include "Game.h"
 | 
			
		||||
#include "json.hpp"
 | 
			
		||||
#include "dCommonVars.h"
 | 
			
		||||
#include "MessageType/Chat.h"
 | 
			
		||||
#include "dServer.h"
 | 
			
		||||
#include "dConfig.h"
 | 
			
		||||
#include "PlayerContainer.h"
 | 
			
		||||
#include "JSONUtils.h"
 | 
			
		||||
#include "GeneralUtils.h"
 | 
			
		||||
#include "eHTTPMethod.h"
 | 
			
		||||
#include "magic_enum.hpp"
 | 
			
		||||
#include "ChatPackets.h"
 | 
			
		||||
#include "StringifiedEnum.h"
 | 
			
		||||
#include "Database.h"
 | 
			
		||||
 | 
			
		||||
#ifdef DARKFLAME_PLATFORM_WIN32
 | 
			
		||||
#pragma push_macro("DELETE")
 | 
			
		||||
#undef DELETE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
typedef struct mg_connection mg_connection;
 | 
			
		||||
typedef struct mg_http_message mg_http_message;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	const char* json_content_type = "Content-Type: application/json\r\n";
 | 
			
		||||
	std::map<std::pair<eHTTPMethod, std::string>, WebAPIHTTPRoute> Routes {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ValidateAuthentication(const mg_http_message* http_msg) {
 | 
			
		||||
	// TO DO: This is just a placeholder for now
 | 
			
		||||
	// use tokens or something at a later point if we want to implement authentication
 | 
			
		||||
	// bit using the listen bind address to limit external access is good enough to start with
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ValidateJSON(std::optional<json> data, HTTPReply& reply) {
 | 
			
		||||
	if (!data) {
 | 
			
		||||
		reply.status = eHTTPStatusCode::BAD_REQUEST;
 | 
			
		||||
		reply.message = "{\"error\":\"Invalid JSON\"}";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HandlePlayersRequest(HTTPReply& reply, std::string body) {
 | 
			
		||||
	const json data = Game::playerContainer;
 | 
			
		||||
	reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
 | 
			
		||||
	reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HandleTeamsRequest(HTTPReply& reply, std::string body) {
 | 
			
		||||
	const json data = Game::playerContainer.GetTeamContainer();
 | 
			
		||||
	reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
 | 
			
		||||
	reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HandleAnnounceRequest(HTTPReply& reply, std::string body) {
 | 
			
		||||
	auto data = GeneralUtils::TryParse<json>(body);
 | 
			
		||||
	if (!ValidateJSON(data, reply)) return;
 | 
			
		||||
 | 
			
		||||
	const auto& good_data = data.value();
 | 
			
		||||
	auto check = JSONUtils::CheckRequiredData(good_data, { "title", "message" });
 | 
			
		||||
	if (!check.empty()) {
 | 
			
		||||
		reply.status = eHTTPStatusCode::BAD_REQUEST;
 | 
			
		||||
		reply.message = check;
 | 
			
		||||
	} else {
 | 
			
		||||
 | 
			
		||||
		ChatPackets::Announcement announcement;
 | 
			
		||||
		announcement.title = good_data["title"];
 | 
			
		||||
		announcement.message = good_data["message"];
 | 
			
		||||
		announcement.Send();
 | 
			
		||||
 | 
			
		||||
		reply.status = eHTTPStatusCode::OK;
 | 
			
		||||
		reply.message = "{\"status\":\"Announcement Sent\"}";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HandleInvalidRoute(HTTPReply& reply) {
 | 
			
		||||
	reply.status = eHTTPStatusCode::NOT_FOUND;
 | 
			
		||||
	reply.message = "{\"error\":\"Invalid Route\"}";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_msg) {
 | 
			
		||||
	HTTPReply reply;
 | 
			
		||||
	
 | 
			
		||||
	if (!http_msg) {
 | 
			
		||||
		reply.status = eHTTPStatusCode::BAD_REQUEST;
 | 
			
		||||
		reply.message = "{\"error\":\"Invalid Request\"}";
 | 
			
		||||
	} else if (ValidateAuthentication(http_msg)) {
 | 
			
		||||
		
 | 
			
		||||
		// convert method from cstring to std string
 | 
			
		||||
		std::string method_string(http_msg->method.buf, http_msg->method.len);
 | 
			
		||||
		// get mehtod from mg to enum
 | 
			
		||||
		const eHTTPMethod method = magic_enum::enum_cast<eHTTPMethod>(method_string).value_or(eHTTPMethod::INVALID);
 | 
			
		||||
 | 
			
		||||
		// convert uri from cstring to std string
 | 
			
		||||
		std::string uri(http_msg->uri.buf, http_msg->uri.len);
 | 
			
		||||
		std::transform(uri.begin(), uri.end(), uri.begin(), ::tolower);
 | 
			
		||||
 | 
			
		||||
		// convert body from cstring to std string
 | 
			
		||||
		std::string body(http_msg->body.buf, http_msg->body.len);
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		const auto routeItr = Routes.find({method, uri});
 | 
			
		||||
 | 
			
		||||
		if (routeItr != Routes.end()) {
 | 
			
		||||
			const auto& [_, route] = *routeItr;
 | 
			
		||||
			route.handle(reply, body);
 | 
			
		||||
		} else HandleInvalidRoute(reply);
 | 
			
		||||
	} else {
 | 
			
		||||
		reply.status = eHTTPStatusCode::UNAUTHORIZED;
 | 
			
		||||
		reply.message = "{\"error\":\"Unauthorized\"}";
 | 
			
		||||
	}
 | 
			
		||||
	mg_http_reply(connection, static_cast<int>(reply.status), json_content_type, reply.message.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void HandleRequests(mg_connection* connection, int request, void* request_data) {
 | 
			
		||||
	switch (request) {
 | 
			
		||||
		case MG_EV_HTTP_MSG:
 | 
			
		||||
			HandleHTTPMessage(connection, static_cast<mg_http_message*>(request_data));
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatWebAPI::RegisterHTTPRoutes(WebAPIHTTPRoute route) {
 | 
			
		||||
	auto [_, success] = Routes.try_emplace({ route.method, route.path }, route);
 | 
			
		||||
	if (!success) {
 | 
			
		||||
		LOG_DEBUG("Failed to register route %s", route.path.c_str());
 | 
			
		||||
	} else {
 | 
			
		||||
		LOG_DEBUG("Registered route %s", route.path.c_str());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ChatWebAPI::ChatWebAPI() {
 | 
			
		||||
	mg_log_set(MG_LL_NONE);
 | 
			
		||||
	mg_mgr_init(&mgr);  // Initialize event manager
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ChatWebAPI::~ChatWebAPI() {
 | 
			
		||||
	mg_mgr_free(&mgr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ChatWebAPI::Startup() {
 | 
			
		||||
	// Make listen address
 | 
			
		||||
	std::string listen_ip = Game::config->GetValue("web_server_listen_ip");
 | 
			
		||||
	if (listen_ip == "localhost") listen_ip = "127.0.0.1";
 | 
			
		||||
 | 
			
		||||
	const std::string& listen_port = Game::config->GetValue("web_server_listen_port");
 | 
			
		||||
	const std::string& listen_address = "http://" + listen_ip + ":" + listen_port;
 | 
			
		||||
	LOG("Starting web server on %s", listen_address.c_str());
 | 
			
		||||
 | 
			
		||||
	// Create HTTP listener
 | 
			
		||||
	if (!mg_http_listen(&mgr, listen_address.c_str(), HandleRequests, NULL)) {
 | 
			
		||||
		LOG("Failed to create web server listener on %s", listen_port.c_str());
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Register routes
 | 
			
		||||
 | 
			
		||||
	// API v1 routes
 | 
			
		||||
	std::string v1_route = "/api/v1/";
 | 
			
		||||
	RegisterHTTPRoutes({
 | 
			
		||||
		.path = v1_route + "players",
 | 
			
		||||
		.method = eHTTPMethod::GET,
 | 
			
		||||
		.handle = HandlePlayersRequest
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	RegisterHTTPRoutes({
 | 
			
		||||
		.path = v1_route + "teams",
 | 
			
		||||
		.method = eHTTPMethod::GET,
 | 
			
		||||
		.handle = HandleTeamsRequest
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	RegisterHTTPRoutes({
 | 
			
		||||
		.path = v1_route + "announce",
 | 
			
		||||
		.method = eHTTPMethod::POST,
 | 
			
		||||
		.handle = HandleAnnounceRequest
 | 
			
		||||
	});
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatWebAPI::ReceiveRequests() {
 | 
			
		||||
	mg_mgr_poll(&mgr, 15);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef DARKFLAME_PLATFORM_WIN32
 | 
			
		||||
#pragma pop_macro("DELETE")
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										36
									
								
								dChatServer/ChatWebAPI.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								dChatServer/ChatWebAPI.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
#ifndef __CHATWEBAPI_H__
 | 
			
		||||
#define __CHATWEBAPI_H__
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
#include "mongoose.h"
 | 
			
		||||
#include "eHTTPStatusCode.h"
 | 
			
		||||
 | 
			
		||||
enum class eHTTPMethod;
 | 
			
		||||
 | 
			
		||||
typedef struct mg_mgr mg_mgr;
 | 
			
		||||
 | 
			
		||||
struct HTTPReply {
 | 
			
		||||
	eHTTPStatusCode status = eHTTPStatusCode::NOT_FOUND;
 | 
			
		||||
	std::string message = "{\"error\":\"Not Found\"}";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct WebAPIHTTPRoute {
 | 
			
		||||
	std::string path;
 | 
			
		||||
	eHTTPMethod method;
 | 
			
		||||
	std::function<void(HTTPReply&, const std::string&)> handle;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ChatWebAPI {
 | 
			
		||||
public:
 | 
			
		||||
	ChatWebAPI();
 | 
			
		||||
	~ChatWebAPI();
 | 
			
		||||
	void ReceiveRequests();
 | 
			
		||||
	void RegisterHTTPRoutes(WebAPIHTTPRoute route);
 | 
			
		||||
	bool Startup();
 | 
			
		||||
private:
 | 
			
		||||
	mg_mgr mgr;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // __CHATWEBAPI_H__
 | 
			
		||||
							
								
								
									
										62
									
								
								dChatServer/JSONUtils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								dChatServer/JSONUtils.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
#include "JSONUtils.h"
 | 
			
		||||
 | 
			
		||||
#include "json.hpp"
 | 
			
		||||
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
void to_json(json& data, const PlayerData& playerData) {
 | 
			
		||||
	data["id"] = playerData.playerID;
 | 
			
		||||
	data["name"] = playerData.playerName;
 | 
			
		||||
	data["gm_level"] = playerData.gmLevel;
 | 
			
		||||
	data["muted"] = playerData.GetIsMuted();
 | 
			
		||||
 | 
			
		||||
	auto& zoneID = data["zone_id"];
 | 
			
		||||
	zoneID["map_id"] = playerData.zoneID.GetMapID();
 | 
			
		||||
	zoneID["instance_id"] = playerData.zoneID.GetInstanceID();
 | 
			
		||||
	zoneID["clone_id"] = playerData.zoneID.GetCloneID();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void to_json(json& data, const PlayerContainer& playerContainer) {
 | 
			
		||||
	data = json::array();
 | 
			
		||||
	for(auto& playerData : playerContainer.GetAllPlayers()) {
 | 
			
		||||
		if (playerData.first == LWOOBJID_EMPTY) continue;
 | 
			
		||||
		data.push_back(playerData.second);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void to_json(json& data, const TeamContainer& teamContainer) {
 | 
			
		||||
	for (auto& teamData : Game::playerContainer.GetTeams()) {
 | 
			
		||||
		if (!teamData) continue;
 | 
			
		||||
		data.push_back(*teamData);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void to_json(json& data, const TeamData& teamData) {
 | 
			
		||||
	data["id"] = teamData.teamID;
 | 
			
		||||
	data["loot_flag"] = teamData.lootFlag;
 | 
			
		||||
	data["local"] = teamData.local;
 | 
			
		||||
 | 
			
		||||
	auto& leader = Game::playerContainer.GetPlayerData(teamData.leaderID);
 | 
			
		||||
	data["leader"] = leader.playerName;
 | 
			
		||||
 | 
			
		||||
	auto& members = data["members"];
 | 
			
		||||
	for (auto& member : teamData.memberIDs) {
 | 
			
		||||
		auto& playerData = Game::playerContainer.GetPlayerData(member);
 | 
			
		||||
 | 
			
		||||
		if (!playerData) continue;
 | 
			
		||||
		members.push_back(playerData);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string JSONUtils::CheckRequiredData(const json& data, const std::vector<std::string>& requiredData) {
 | 
			
		||||
	json check;
 | 
			
		||||
	check["error"] = json::array();
 | 
			
		||||
	for (const auto& required : requiredData) {
 | 
			
		||||
		if (!data.contains(required)) {
 | 
			
		||||
			check["error"].push_back("Missing Parameter: " + required);
 | 
			
		||||
		} else if (data[required] == "") {
 | 
			
		||||
			check["error"].push_back("Empty Parameter: " + required);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return check["error"].empty() ? "" : check.dump();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								dChatServer/JSONUtils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								dChatServer/JSONUtils.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
#ifndef __JSONUTILS_H__
 | 
			
		||||
#define __JSONUTILS_H__
 | 
			
		||||
 | 
			
		||||
#include "json_fwd.hpp"
 | 
			
		||||
#include "PlayerContainer.h"
 | 
			
		||||
 | 
			
		||||
void to_json(nlohmann::json& data, const PlayerData& playerData);
 | 
			
		||||
void to_json(nlohmann::json& data, const PlayerContainer& playerContainer);
 | 
			
		||||
void to_json(nlohmann::json& data, const TeamContainer& teamData);
 | 
			
		||||
void to_json(nlohmann::json& data, const TeamData& teamData);
 | 
			
		||||
 | 
			
		||||
namespace JSONUtils {
 | 
			
		||||
	// check required data for reqeust
 | 
			
		||||
	std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // __JSONUTILS_H__
 | 
			
		||||
@@ -219,7 +219,7 @@ TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
 | 
			
		||||
	team->leaderID = leader;
 | 
			
		||||
	team->local = local;
 | 
			
		||||
 | 
			
		||||
	mTeams.push_back(team);
 | 
			
		||||
	GetTeamsMut().push_back(team);
 | 
			
		||||
 | 
			
		||||
	AddMember(team, leader);
 | 
			
		||||
 | 
			
		||||
@@ -227,7 +227,7 @@ TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
 | 
			
		||||
	for (auto* team : mTeams) {
 | 
			
		||||
	for (auto* team : GetTeams()) {
 | 
			
		||||
		if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue;
 | 
			
		||||
 | 
			
		||||
		return team;
 | 
			
		||||
@@ -335,9 +335,9 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerContainer::DisbandTeam(TeamData* team) {
 | 
			
		||||
	const auto index = std::find(mTeams.begin(), mTeams.end(), team);
 | 
			
		||||
	const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
 | 
			
		||||
 | 
			
		||||
	if (index == mTeams.end()) return;
 | 
			
		||||
	if (index == GetTeams().end()) return;
 | 
			
		||||
 | 
			
		||||
	for (const auto memberId : team->memberIDs) {
 | 
			
		||||
		const auto& otherMember = GetPlayerData(memberId);
 | 
			
		||||
@@ -352,15 +352,15 @@ void PlayerContainer::DisbandTeam(TeamData* team) {
 | 
			
		||||
 | 
			
		||||
	UpdateTeamsOnWorld(team, true);
 | 
			
		||||
 | 
			
		||||
	mTeams.erase(index);
 | 
			
		||||
	GetTeamsMut().erase(index);
 | 
			
		||||
 | 
			
		||||
	delete team;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerContainer::TeamStatusUpdate(TeamData* team) {
 | 
			
		||||
	const auto index = std::find(mTeams.begin(), mTeams.end(), team);
 | 
			
		||||
	const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
 | 
			
		||||
 | 
			
		||||
	if (index == mTeams.end()) return;
 | 
			
		||||
	if (index == GetTeams().end()) return;
 | 
			
		||||
 | 
			
		||||
	const auto& leader = GetPlayerData(team->leaderID);
 | 
			
		||||
 | 
			
		||||
@@ -447,5 +447,5 @@ void PlayerContainer::Shutdown() {
 | 
			
		||||
		Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
 | 
			
		||||
		m_Players.erase(m_Players.begin());
 | 
			
		||||
	}
 | 
			
		||||
	for (auto* team : mTeams) if (team) delete team;
 | 
			
		||||
	for (auto* team : GetTeams()) if (team) delete team;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,12 @@
 | 
			
		||||
 | 
			
		||||
enum class eGameMasterLevel : uint8_t;
 | 
			
		||||
 | 
			
		||||
struct TeamData;
 | 
			
		||||
 | 
			
		||||
struct TeamContainer {
 | 
			
		||||
	std::vector<TeamData*> mTeams;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct IgnoreData {
 | 
			
		||||
	IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
 | 
			
		||||
	inline bool operator==(const std::string& other) const noexcept {
 | 
			
		||||
@@ -49,6 +55,7 @@ struct PlayerData {
 | 
			
		||||
	bool isLogin = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct TeamData {
 | 
			
		||||
	TeamData();
 | 
			
		||||
	LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
 | 
			
		||||
@@ -76,7 +83,7 @@ public:
 | 
			
		||||
	PlayerData& GetPlayerDataMutable(const std::string& playerName);
 | 
			
		||||
	uint32_t GetPlayerCount() { return m_PlayerCount; };
 | 
			
		||||
	uint32_t GetSimCount() { return m_SimCount; };
 | 
			
		||||
	const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
 | 
			
		||||
	const std::map<LWOOBJID, PlayerData>& GetAllPlayers() const { return m_Players; };
 | 
			
		||||
 | 
			
		||||
	TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
 | 
			
		||||
	TeamData* CreateTeam(LWOOBJID leader, bool local = false);
 | 
			
		||||
@@ -91,6 +98,9 @@ public:
 | 
			
		||||
	LWOOBJID GetId(const std::u16string& playerName);
 | 
			
		||||
	uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
 | 
			
		||||
	uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
 | 
			
		||||
	const TeamContainer& GetTeamContainer() { return m_TeamContainer; }
 | 
			
		||||
	std::vector<TeamData*>& GetTeamsMut() { return m_TeamContainer.mTeams; };
 | 
			
		||||
	const std::vector<TeamData*>& GetTeams() { return GetTeamsMut(); };
 | 
			
		||||
 | 
			
		||||
	void Update(const float deltaTime);
 | 
			
		||||
	bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
 | 
			
		||||
@@ -98,7 +108,7 @@ public:
 | 
			
		||||
private:
 | 
			
		||||
	LWOOBJID m_TeamIDCounter = 0;
 | 
			
		||||
	std::map<LWOOBJID, PlayerData> m_Players;
 | 
			
		||||
	std::vector<TeamData*> mTeams;
 | 
			
		||||
	TeamContainer m_TeamContainer{};
 | 
			
		||||
	std::unordered_map<LWOOBJID, std::u16string> m_Names;
 | 
			
		||||
	std::map<LWOOBJID, float> m_PlayersToRemove;
 | 
			
		||||
	uint32_t m_MaxNumberOfBestFriends = 5;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,10 @@
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include "json.hpp"
 | 
			
		||||
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
 | 
			
		||||
	if (size == SIZE_MAX || size > string.size()) {
 | 
			
		||||
@@ -327,6 +331,17 @@ std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::stri
 | 
			
		||||
	return sortedFiles;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<>
 | 
			
		||||
[[nodiscard]] std::optional<json> GeneralUtils::TryParse(std::string_view str) {
 | 
			
		||||
	try {
 | 
			
		||||
		return json::parse(str);
 | 
			
		||||
	} catch (const std::exception& e) {
 | 
			
		||||
		return std::nullopt;
 | 
			
		||||
	} catch (...) {
 | 
			
		||||
		return std::nullopt;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
 | 
			
		||||
 | 
			
		||||
// MacOS floating-point parse function specializations
 | 
			
		||||
 
 | 
			
		||||
@@ -201,6 +201,10 @@ namespace GeneralUtils {
 | 
			
		||||
		return isParsed ? static_cast<T>(result) : std::optional<T>{};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template<typename T>
 | 
			
		||||
	requires(!Numeric<T>)
 | 
			
		||||
	[[nodiscard]] std::optional<T> TryParse(std::string_view str);
 | 
			
		||||
 | 
			
		||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
 | 
			
		||||
 | 
			
		||||
	// MacOS floating-point parse helper function specializations
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								dCommon/dEnums/eHTTPMethod.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								dCommon/dEnums/eHTTPMethod.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#ifndef __EHTTPMETHODS__H__
 | 
			
		||||
#define __EHTTPMETHODS__H__
 | 
			
		||||
 | 
			
		||||
#ifdef DARKFLAME_PLATFORM_WIN32
 | 
			
		||||
#pragma push_macro("DELETE")
 | 
			
		||||
#undef DELETE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum class eHTTPMethod {
 | 
			
		||||
	GET,
 | 
			
		||||
	POST,
 | 
			
		||||
	PUT,
 | 
			
		||||
	DELETE,
 | 
			
		||||
	HEAD,
 | 
			
		||||
	CONNECT,
 | 
			
		||||
	OPTIONS,
 | 
			
		||||
	TRACE,
 | 
			
		||||
	PATCH,
 | 
			
		||||
	INVALID
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef DARKFLAME_PLATFORM_WIN32
 | 
			
		||||
#pragma pop_macro("DELETE")
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif // __EHTTPMETHODS__H__
 | 
			
		||||
							
								
								
									
										72
									
								
								dCommon/dEnums/eHTTPStatusCode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								dCommon/dEnums/eHTTPStatusCode.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
#ifndef __EHTTPSTATUSCODE__H__
 | 
			
		||||
#define __EHTTPSTATUSCODE__H__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
// verbose list of http codes
 | 
			
		||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
 | 
			
		||||
enum class eHTTPStatusCode : uint32_t {
 | 
			
		||||
	CONTINUE = 100,
 | 
			
		||||
	SWITCHING_PROTOCOLS = 101,
 | 
			
		||||
	PROCESSING = 102,
 | 
			
		||||
	EARLY_HINTS = 103,
 | 
			
		||||
	OK = 200,
 | 
			
		||||
	CREATED = 201,
 | 
			
		||||
	ACCEPTED = 202,
 | 
			
		||||
	NON_AUTHORITATIVE_INFORMATION = 203,
 | 
			
		||||
	NO_CONTENT = 204,
 | 
			
		||||
	RESET_CONTENT = 205,
 | 
			
		||||
	PARTIAL_CONTENT = 206,
 | 
			
		||||
	MULTI_STATUS = 207,
 | 
			
		||||
	ALREADY_REPORTED = 208,
 | 
			
		||||
	IM_USED = 226,
 | 
			
		||||
	MULTIPLE_CHOICES = 300,
 | 
			
		||||
	MOVED_PERMANENTLY = 301,
 | 
			
		||||
	FOUND = 302,
 | 
			
		||||
	SEE_OTHER = 303,
 | 
			
		||||
	NOT_MODIFIED = 304,
 | 
			
		||||
	USE_PROXY = 305,
 | 
			
		||||
	SWITCH_PROXY = 306,
 | 
			
		||||
	TEMPORARY_REDIRECT = 307,
 | 
			
		||||
	PERMANENT_REDIRECT = 308,
 | 
			
		||||
	BAD_REQUEST = 400,
 | 
			
		||||
	UNAUTHORIZED = 401,
 | 
			
		||||
	PAYMENT_REQUIRED = 402,
 | 
			
		||||
	FORBIDDEN = 403,
 | 
			
		||||
	NOT_FOUND = 404,
 | 
			
		||||
	METHOD_NOT_ALLOWED = 405,
 | 
			
		||||
	NOT_ACCEPTABLE = 406,
 | 
			
		||||
	PROXY_AUTHENTICATION_REQUIRED = 407,
 | 
			
		||||
	REQUEST_TIMEOUT = 408,
 | 
			
		||||
	CONFLICT = 409,
 | 
			
		||||
	GONE = 410,
 | 
			
		||||
	LENGTH_REQUIRED = 411,
 | 
			
		||||
	PRECONDITION_FAILED = 412,
 | 
			
		||||
	PAYLOAD_TOO_LARGE = 413,
 | 
			
		||||
	URI_TOO_LONG = 414,
 | 
			
		||||
	UNSUPPORTED_MEDIA_TYPE = 415,
 | 
			
		||||
	RANGE_NOT_SATISFIABLE = 416,
 | 
			
		||||
	EXPECTATION_FAILED = 417,
 | 
			
		||||
	I_AM_A_TEAPOT = 418,
 | 
			
		||||
	MISDIRECTED_REQUEST = 421,
 | 
			
		||||
	UNPROCESSABLE_ENTITY = 422,
 | 
			
		||||
	LOCKED = 423,
 | 
			
		||||
	FAILED_DEPENDENCY = 424,
 | 
			
		||||
	UPGRADE_REQUIRED = 426,
 | 
			
		||||
	PRECONDITION_REQUIRED = 428,
 | 
			
		||||
	TOO_MANY_REQUESTS = 429,
 | 
			
		||||
	REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
 | 
			
		||||
	UNAVAILABLE_FOR_LEGAL_REASONS = 451,
 | 
			
		||||
	INTERNAL_SERVER_ERROR = 500,
 | 
			
		||||
	NOT_IMPLEMENTED = 501,
 | 
			
		||||
	BAD_GATEWAY = 502,
 | 
			
		||||
	SERVICE_UNAVAILABLE = 503,
 | 
			
		||||
	GATEWAY_TIMEOUT = 504,
 | 
			
		||||
	HTTP_VERSION_NOT_SUPPORTED = 505,
 | 
			
		||||
	VARIANT_ALSO_NEGOTIATES = 506,
 | 
			
		||||
	INSUFFICIENT_STORAGE = 507,
 | 
			
		||||
	LOOP_DETECTED = 508,
 | 
			
		||||
	NOT_EXTENDED = 510,
 | 
			
		||||
	NETWORK_AUTHENTICATION_REQUIRED = 511
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // !__EHTTPSTATUSCODE__H__
 | 
			
		||||
@@ -82,7 +82,7 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c
 | 
			
		||||
	if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth);
 | 
			
		||||
	else if (serverType == ServerType::World) bitStream.Write(ServiceId::World);
 | 
			
		||||
	else bitStream.Write(ServiceId::General);
 | 
			
		||||
	bitStream.Write<uint64_t>(219818241584);
 | 
			
		||||
	bitStream.Write<uint64_t>(219818307120);
 | 
			
		||||
 | 
			
		||||
	server->Send(bitStream, sysAddr, false);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -97,3 +97,13 @@ void ChatPackets::SendMessageFail(const SystemAddress& sysAddr) {
 | 
			
		||||
	//docs say there's a wstring here-- no idea what it's for, or if it's even needed so leaving it as is for now.
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPackets::Announcement::Send() {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_ANNOUNCE);
 | 
			
		||||
	bitStream.Write<uint32_t>(title.size());
 | 
			
		||||
	bitStream.Write(title);
 | 
			
		||||
	bitStream.Write<uint32_t>(message.size());
 | 
			
		||||
	bitStream.Write(message);
 | 
			
		||||
	SEND_PACKET_BROADCAST;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,13 @@ struct FindPlayerRequest{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace ChatPackets {
 | 
			
		||||
 | 
			
		||||
	struct Announcement {
 | 
			
		||||
		std::string title;
 | 
			
		||||
		std::string message;
 | 
			
		||||
		void Send();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message);
 | 
			
		||||
	void SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, bool broadcast = false);
 | 
			
		||||
	void SendMessageFail(const SystemAddress& sysAddr);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										151
									
								
								docs/ChatWebAPI.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								docs/ChatWebAPI.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
			
		||||
openapi: 3.0.3
 | 
			
		||||
info:
 | 
			
		||||
  title: DLU Chat Server API
 | 
			
		||||
  description: |-
 | 
			
		||||
    This documents the available api endpoints for the DLU Chat Server Web API
 | 
			
		||||
  contact:
 | 
			
		||||
    name: DarkflameUniverse Github
 | 
			
		||||
    url: https://github.com/DarkflameUniverse/DarkflameServer/issues
 | 
			
		||||
  license:
 | 
			
		||||
    name: GNU AGPL v3.0
 | 
			
		||||
    url: https://github.com/DarkflameUniverse/DarkflameServer/blob/main/LICENSE
 | 
			
		||||
  version: 1.0.0
 | 
			
		||||
 | 
			
		||||
externalDocs:
 | 
			
		||||
  description: Find out more about Swagger
 | 
			
		||||
  url: http://swagger.io
 | 
			
		||||
 | 
			
		||||
servers:
 | 
			
		||||
  - url: http://localhost:2005/api/v1/
 | 
			
		||||
    description: localhost
 | 
			
		||||
 | 
			
		||||
tags:
 | 
			
		||||
  - name: management
 | 
			
		||||
    description: Server Management Utilities
 | 
			
		||||
  - name: user
 | 
			
		||||
    description: User Data Utilities
 | 
			
		||||
 | 
			
		||||
paths:
 | 
			
		||||
  /announce:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - management
 | 
			
		||||
      summary: Send an announcement to the game server
 | 
			
		||||
      requestBody:
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              $ref: "#/components/schemas/Announce"
 | 
			
		||||
        required: true
 | 
			
		||||
      responses:
 | 
			
		||||
        "200":
 | 
			
		||||
          description: Successful operation
 | 
			
		||||
        "400":
 | 
			
		||||
          description: Missing Parameter
 | 
			
		||||
 | 
			
		||||
  /players:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - user
 | 
			
		||||
      summary: Get all online Players
 | 
			
		||||
      responses:
 | 
			
		||||
        "200":
 | 
			
		||||
          description: Successful operation
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                type: array
 | 
			
		||||
                items:
 | 
			
		||||
                  $ref: "#/components/schemas/Player"
 | 
			
		||||
        "204":
 | 
			
		||||
          description: No Data
 | 
			
		||||
 | 
			
		||||
  /teams:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - user
 | 
			
		||||
      summary: Get all active Teams
 | 
			
		||||
      responses:
 | 
			
		||||
        "200":
 | 
			
		||||
          description: Successful operation
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                type: array
 | 
			
		||||
                items:
 | 
			
		||||
                  $ref: "#/components/schemas/Team"
 | 
			
		||||
        "204":
 | 
			
		||||
          description: No Data
 | 
			
		||||
 | 
			
		||||
components:
 | 
			
		||||
  schemas:
 | 
			
		||||
    Player:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        id:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
          example: 1152921508901824000
 | 
			
		||||
        gm_level:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: uint8
 | 
			
		||||
          example: 0
 | 
			
		||||
        name:
 | 
			
		||||
          type: string
 | 
			
		||||
          example: thisisatestname
 | 
			
		||||
        muted:
 | 
			
		||||
          type: boolean
 | 
			
		||||
          example: false
 | 
			
		||||
        zone_id:
 | 
			
		||||
          $ref: "#/components/schemas/ZoneID"
 | 
			
		||||
 | 
			
		||||
    ZoneID:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        map_id:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: uint16
 | 
			
		||||
          example: 1200
 | 
			
		||||
        instance_id:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: uint16
 | 
			
		||||
          example: 2
 | 
			
		||||
        clone_id:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: uint32
 | 
			
		||||
          example: 0
 | 
			
		||||
 | 
			
		||||
    Team:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        id:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
          example: 1152921508901824000
 | 
			
		||||
        loot_flag:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: uint8
 | 
			
		||||
          example: 1
 | 
			
		||||
        local:
 | 
			
		||||
          type: boolean
 | 
			
		||||
          example: false
 | 
			
		||||
        leader:
 | 
			
		||||
          type: string
 | 
			
		||||
          example: thisisatestname
 | 
			
		||||
        members:
 | 
			
		||||
          type: array
 | 
			
		||||
          items:
 | 
			
		||||
            $ref: "#/components/schemas/Player"
 | 
			
		||||
 | 
			
		||||
    Announce:
 | 
			
		||||
      required:
 | 
			
		||||
        - title
 | 
			
		||||
        - message
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        title:
 | 
			
		||||
          type: string
 | 
			
		||||
          example: A Mythran has taken Action against you!
 | 
			
		||||
        message:
 | 
			
		||||
          type: string
 | 
			
		||||
          example: Check your mailbox for details!
 | 
			
		||||
@@ -6,3 +6,8 @@ max_number_of_best_friends=5
 | 
			
		||||
# Change the value below to what you would like this to be (50 is live accurate)
 | 
			
		||||
# going over 50 will be allowed in some secnarios, but proper handling will require client modding
 | 
			
		||||
max_number_of_friends=50
 | 
			
		||||
 | 
			
		||||
web_server_enabled=0
 | 
			
		||||
 | 
			
		||||
web_server_listen_ip=127.0.0.1
 | 
			
		||||
web_server_listen_port=2005
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								thirdparty/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								thirdparty/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@@ -65,3 +65,5 @@ if(UNIX AND NOT APPLE)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_subdirectory(MD5)
 | 
			
		||||
 | 
			
		||||
add_subdirectory(mongoose)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								thirdparty/mongoose/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								thirdparty/mongoose/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
add_library(mongoose mongoose.c)
 | 
			
		||||
							
								
								
									
										20300
									
								
								thirdparty/mongoose/mongoose.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20300
									
								
								thirdparty/mongoose/mongoose.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3287
									
								
								thirdparty/mongoose/mongoose.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3287
									
								
								thirdparty/mongoose/mongoose.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										24765
									
								
								thirdparty/nlohmann/json.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24765
									
								
								thirdparty/nlohmann/json.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										176
									
								
								thirdparty/nlohmann/json_fwd.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								thirdparty/nlohmann/json_fwd.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
			
		||||
//     __ _____ _____ _____
 | 
			
		||||
//  __|  |   __|     |   | |  JSON for Modern C++
 | 
			
		||||
// |  |  |__   |  |  | | | |  version 3.11.3
 | 
			
		||||
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 | 
			
		||||
//
 | 
			
		||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
 | 
			
		||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
 | 
			
		||||
 | 
			
		||||
#include <cstdint> // int64_t, uint64_t
 | 
			
		||||
#include <map> // map
 | 
			
		||||
#include <memory> // allocator
 | 
			
		||||
#include <string> // string
 | 
			
		||||
#include <vector> // vector
 | 
			
		||||
 | 
			
		||||
// #include <nlohmann/detail/abi_macros.hpp>
 | 
			
		||||
//     __ _____ _____ _____
 | 
			
		||||
//  __|  |   __|     |   | |  JSON for Modern C++
 | 
			
		||||
// |  |  |__   |  |  | | | |  version 3.11.3
 | 
			
		||||
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 | 
			
		||||
//
 | 
			
		||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// This file contains all macro definitions affecting or depending on the ABI
 | 
			
		||||
 | 
			
		||||
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
 | 
			
		||||
    #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
 | 
			
		||||
        #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3
 | 
			
		||||
            #warning "Already included a different version of the library!"
 | 
			
		||||
        #endif
 | 
			
		||||
    #endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define NLOHMANN_JSON_VERSION_MAJOR 3   // NOLINT(modernize-macro-to-enum)
 | 
			
		||||
#define NLOHMANN_JSON_VERSION_MINOR 11  // NOLINT(modernize-macro-to-enum)
 | 
			
		||||
#define NLOHMANN_JSON_VERSION_PATCH 3   // NOLINT(modernize-macro-to-enum)
 | 
			
		||||
 | 
			
		||||
#ifndef JSON_DIAGNOSTICS
 | 
			
		||||
    #define JSON_DIAGNOSTICS 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
 | 
			
		||||
    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if JSON_DIAGNOSTICS
 | 
			
		||||
    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
 | 
			
		||||
#else
 | 
			
		||||
    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
 | 
			
		||||
    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
 | 
			
		||||
#else
 | 
			
		||||
    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
 | 
			
		||||
    #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Construct the namespace ABI tags component
 | 
			
		||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
 | 
			
		||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
 | 
			
		||||
    NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
 | 
			
		||||
 | 
			
		||||
#define NLOHMANN_JSON_ABI_TAGS                                       \
 | 
			
		||||
    NLOHMANN_JSON_ABI_TAGS_CONCAT(                                   \
 | 
			
		||||
            NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS,                       \
 | 
			
		||||
            NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
 | 
			
		||||
 | 
			
		||||
// Construct the namespace version component
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
 | 
			
		||||
    _v ## major ## _ ## minor ## _ ## patch
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
 | 
			
		||||
    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
 | 
			
		||||
 | 
			
		||||
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE_VERSION
 | 
			
		||||
#else
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE_VERSION                                 \
 | 
			
		||||
    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
 | 
			
		||||
                                           NLOHMANN_JSON_VERSION_MINOR, \
 | 
			
		||||
                                           NLOHMANN_JSON_VERSION_PATCH)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// Combine namespace components
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
 | 
			
		||||
    NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
 | 
			
		||||
 | 
			
		||||
#ifndef NLOHMANN_JSON_NAMESPACE
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE               \
 | 
			
		||||
    nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
 | 
			
		||||
            NLOHMANN_JSON_ABI_TAGS,           \
 | 
			
		||||
            NLOHMANN_JSON_NAMESPACE_VERSION)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE_BEGIN                \
 | 
			
		||||
    namespace nlohmann                               \
 | 
			
		||||
    {                                                \
 | 
			
		||||
    inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
 | 
			
		||||
                NLOHMANN_JSON_ABI_TAGS,              \
 | 
			
		||||
                NLOHMANN_JSON_NAMESPACE_VERSION)     \
 | 
			
		||||
    {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef NLOHMANN_JSON_NAMESPACE_END
 | 
			
		||||
#define NLOHMANN_JSON_NAMESPACE_END                                     \
 | 
			
		||||
    }  /* namespace (inline namespace) NOLINT(readability/namespace) */ \
 | 
			
		||||
    }  // namespace nlohmann
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
@brief namespace for Niels Lohmann
 | 
			
		||||
@see https://github.com/nlohmann
 | 
			
		||||
@since version 1.0.0
 | 
			
		||||
*/
 | 
			
		||||
NLOHMANN_JSON_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
@brief default JSONSerializer template argument
 | 
			
		||||
 | 
			
		||||
This serializer ignores the template arguments and uses ADL
 | 
			
		||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
 | 
			
		||||
for serialization.
 | 
			
		||||
*/
 | 
			
		||||
template<typename T = void, typename SFINAE = void>
 | 
			
		||||
struct adl_serializer;
 | 
			
		||||
 | 
			
		||||
/// a class to store JSON values
 | 
			
		||||
/// @sa https://json.nlohmann.me/api/basic_json/
 | 
			
		||||
template<template<typename U, typename V, typename... Args> class ObjectType =
 | 
			
		||||
         std::map,
 | 
			
		||||
         template<typename U, typename... Args> class ArrayType = std::vector,
 | 
			
		||||
         class StringType = std::string, class BooleanType = bool,
 | 
			
		||||
         class NumberIntegerType = std::int64_t,
 | 
			
		||||
         class NumberUnsignedType = std::uint64_t,
 | 
			
		||||
         class NumberFloatType = double,
 | 
			
		||||
         template<typename U> class AllocatorType = std::allocator,
 | 
			
		||||
         template<typename T, typename SFINAE = void> class JSONSerializer =
 | 
			
		||||
         adl_serializer,
 | 
			
		||||
         class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
 | 
			
		||||
         class CustomBaseClass = void>
 | 
			
		||||
class basic_json;
 | 
			
		||||
 | 
			
		||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
 | 
			
		||||
/// @sa https://json.nlohmann.me/api/json_pointer/
 | 
			
		||||
template<typename RefStringType>
 | 
			
		||||
class json_pointer;
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
@brief default specialization
 | 
			
		||||
@sa https://json.nlohmann.me/api/json/
 | 
			
		||||
*/
 | 
			
		||||
using json = basic_json<>;
 | 
			
		||||
 | 
			
		||||
/// @brief a minimal map-like container that preserves insertion order
 | 
			
		||||
/// @sa https://json.nlohmann.me/api/ordered_map/
 | 
			
		||||
template<class Key, class T, class IgnoredLess, class Allocator>
 | 
			
		||||
struct ordered_map;
 | 
			
		||||
 | 
			
		||||
/// @brief specialization that maintains the insertion order of object keys
 | 
			
		||||
/// @sa https://json.nlohmann.me/api/ordered_json/
 | 
			
		||||
using ordered_json = basic_json<nlohmann::ordered_map>;
 | 
			
		||||
 | 
			
		||||
NLOHMANN_JSON_NAMESPACE_END
 | 
			
		||||
 | 
			
		||||
#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
3.1 - Shooting Gallery Live accurate, Web API, and many QOL fixes
 | 
			
		||||
3.0 - Single player with minimal setup is fully functional with SQLite database
 | 
			
		||||
2.3 - Dragonmaw functional, new slash command system, vanity system overhaul
 | 
			
		||||
2.2 - Code cleanup and QoL fixes
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user