Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: chat web api mvp #1550

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
1 change: 1 addition & 0 deletions dChatServer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
set(DCHATSERVER_SOURCES
"ChatWebApi.cpp"
"ChatIgnoreList.cpp"
"ChatPacketHandler.cpp"
"PlayerContainer.cpp"
Expand Down
10 changes: 9 additions & 1 deletion dChatServer/ChatServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "eWorldMessageType.h"
#include "ChatIgnoreList.h"
#include "StringifiedEnum.h"
#include "ChatWebApi.h"

#include "Game.h"
#include "Server.h"
Expand All @@ -36,7 +37,7 @@ namespace Game {
AssetManager* assetManager = nullptr;
Game::signal_t lastSignal = 0;
std::mt19937 randomEngine;
PlayerContainer playerContainer;
PlayerContainer playerContainer;
}

void HandlePacket(Packet* packet);
Expand Down Expand Up @@ -122,6 +123,9 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0;

// start the web api thread
std::thread webAPIThread(ChatWebApi::Listen, ourPort);

Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) {
//Check if we're still connected to master:
Expand Down Expand Up @@ -174,6 +178,10 @@ int main(int argc, char** argv) {
delete Game::server;
delete Game::logger;
delete Game::config;

// rejoin the web api thread
ChatWebApi::Stop();
webAPIThread.join();

return EXIT_SUCCESS;
}
Expand Down
93 changes: 93 additions & 0 deletions dChatServer/ChatWebApi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "ChatWebApi.h"

#include <cstdint>

#include "dCommonVars.h"
#include "eConnectionType.h"
#include "eChatMessageType.h"
#include "httplib.h"
#include "dServer.h"
#include "PlayerContainer.h"
#include "dConfig.h"
#include "httplib.h"
#include "nlohmann/json.hpp"

using json = nlohmann::json;

namespace {
httplib::Server m_APIServer;
}

void ChatWebApi::Listen(const uint32_t port) {
if (Game::config->GetValue("enable_chat_web_api") != "1") {
LOG("Chat Web API is disabled");
return;
}
LOG("Chat Web API is enabled, starting web server...");

m_APIServer.Post("/announce", [](const httplib::Request& req, httplib::Response& res) {
const json data = json::parse(req.body);
if (!data.contains("title")) {
res.set_content("{\"error\":\"Missing paramater: title\"}", "application/json");
res.status = 400;
return;
}
std::string title = data["title"];
if (!data.contains("message")) {
res.set_content("{\"error\":\"Missing paramater: message\"}", "application/json");
res.status = 400;
return;
}
std::string message = data["message"];
// build and send the packet to all world servers
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_ANNOUNCE);
bitStream.Write<uint32_t>(title.size());
bitStream.Write(title);
bitStream.Write<uint32_t>(message.size());
bitStream.Write(message);
Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
});

m_APIServer.Get("/players", [](const httplib::Request& req, httplib::Response& res) {
auto data = json::array();
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){
if (!playerData) continue;
data.push_back(playerData.to_json());
}
res.set_content(data.dump(), "application/json");
if (data.empty()) res.status = 204;
});

m_APIServer.Get("/teams", [](const httplib::Request& req, httplib::Response& res) {
auto data = json::array();
for (auto& teamData: Game::playerContainer.GetAllTeams()){
if (!teamData) continue;
json toInsert;
toInsert["id"] = teamData->teamID;
toInsert["loot_flag"] = teamData->lootFlag;
toInsert["local"] = teamData->local;

auto& leader = Game::playerContainer.GetPlayerData(teamData->leaderID);
toInsert["leader"] = leader.to_json();

json members;
for (auto& member : teamData->memberIDs){
auto playerData = Game::playerContainer.GetPlayerData(member);
aronwk-aaron marked this conversation as resolved.
Show resolved Hide resolved
if (!playerData) continue;
members.push_back(playerData.to_json());
}
toInsert["members"] = members;
data.push_back(toInsert);
}
res.set_content(data.dump(), "application/json");
if (data.empty()) res.status = 204;
});

m_APIServer.listen(Game::config->GetValue("chat_web_api_listen_address").c_str(), port);
};

void ChatWebApi::Stop(){
LOG("Stopping Chat Web API server...");
m_APIServer.stop();
}
6 changes: 6 additions & 0 deletions dChatServer/ChatWebApi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include <cstdint>

namespace ChatWebApi {
void Listen(const uint32_t port);
void Stop();
};
23 changes: 21 additions & 2 deletions dChatServer/PlayerContainer.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "PlayerContainer.h"
#include "dNetCommon.h"

#include <iostream>
#include <algorithm>

#include "dNetCommon.h"
#include "Game.h"
#include "Logger.h"
#include "ChatPacketHandler.h"
Expand All @@ -11,7 +13,24 @@
#include "eConnectionType.h"
#include "ChatPackets.h"
#include "dConfig.h"
#include "eChatMessageType.h"
#include "eChatMessageType.h"

using json = nlohmann::json;

const json PlayerData::to_json() const {
json data;
data["id"] = this->playerID;
data["name"] = this->playerName;
data["gm_level"] = this->gmLevel;
data["muted"] = this->GetIsMuted();

json zoneID;
zoneID["map_id"] = std::to_string(this->zoneID.GetMapID());
zoneID["instance_id"] = std::to_string(this->zoneID.GetInstanceID());
zoneID["clone_id"] = std::to_string(this->zoneID.GetCloneID());
data["zone_id"] = zoneID;
return data;
}

void PlayerContainer::Initialize() {
m_MaxNumberOfBestFriends =
Expand Down
4 changes: 4 additions & 0 deletions dChatServer/PlayerContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Game.h"
#include "dServer.h"
#include <unordered_map>
#include "nlohmann/json.hpp"

enum class eGameMasterLevel : uint8_t;

Expand Down Expand Up @@ -36,6 +37,8 @@ struct PlayerData {
return muteExpire == 1 || muteExpire > time(NULL);
}

const nlohmann::json to_json() const;

SystemAddress sysAddr{};
LWOZONEID zoneID{};
LWOOBJID playerID = LWOOBJID_EMPTY;
Expand Down Expand Up @@ -88,6 +91,7 @@ class PlayerContainer {
LWOOBJID GetId(const std::u16string& playerName);
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
const std::vector<TeamData*>& GetAllTeams() { return mTeams;};

private:
LWOOBJID m_TeamIDCounter = 0;
Expand Down
151 changes: 151 additions & 0 deletions docs/ChatWebAPI.yaml
Original file line number Diff line number Diff line change
@@ -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/
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:
$ref: "#/components/schemas/Player"
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!
8 changes: 8 additions & 0 deletions resources/chatconfig.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,11 @@ 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

# Enable or disable the chat web API, disabled by default
# It will run on the same port the chat server is running on, defined in shardconfig.ini
enable_chat_web_api=0

# If that chat web api is enabled, it will only listen for connections on this ip address
# 127.0.0.1 is localhost
chat_web_api_listen_address=127.0.0.1
aronwk-aaron marked this conversation as resolved.
Show resolved Hide resolved
Loading