Completely removed MainServer code. All server code is now part of the program

This commit is contained in:
Yousef Khadadeh 2025-12-17 14:46:27 +03:00
parent 0b67c9cc46
commit 26d807ce87
31 changed files with 459 additions and 512 deletions

View file

@ -32,6 +32,7 @@ set(sources "src/main.cpp"
"src/MainProgram.cpp"
"src/DrawData.cpp"
"src/FontData.cpp"
"src/ClientData.cpp"
"src/GridManager.cpp"
"src/WorldGrid.cpp"
"src/TimePoint.cpp"
@ -53,8 +54,6 @@ set(sources "src/main.cpp"
"src/ResourceDisplay/SvgResourceDisplay.cpp"
"src/ResourceDisplay/ImageResourceDisplay.cpp"
"src/ResourceDisplay/FileResourceDisplay.cpp"
"src/Server/MainServer.cpp"
"src/Server/ServerData.cpp"
"src/RichText/TextBox.cpp"
"src/RichText/TextStyleModifier.cpp"
"src/RichText/ParagraphStyleData.cpp"

View file

@ -37,7 +37,7 @@ class NetServer : public std::enable_shared_from_this<NetServer> {
};
std::unordered_map<std::string, std::queue<OutgoingMessage>> messageQueues;
uint64_t customID;
std::array<uint64_t, 2> customID;
void send_queued_messages(NetServer& server);
void parse_received_messages(NetServer& server);

View file

@ -91,7 +91,6 @@ namespace NetworkingObjects {
}
template <typename T> static void send_client_update(const NetObjTemporaryPtr<T>& ptr, const std::string& channel, std::function<void(const NetObjTemporaryPtr<T>&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) {
assert(!isServer);
if(ptr.get_obj_man()->client && !ptr.get_obj_man()->client->is_disconnected()) {
auto ss(std::make_shared<std::stringstream>());
{
@ -104,7 +103,6 @@ namespace NetworkingObjects {
}
template <typename T> static void send_server_update_to_client(const NetObjTemporaryPtr<T>& ptr, const std::shared_ptr<NetServer::ClientData>& clientToSendTo, const std::string& channel, std::function<void(const NetObjTemporaryPtr<T>&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) {
assert(isServer);
if(ptr.get_obj_man()->server && !ptr.get_obj_man()->server->is_disconnected()) {
auto ss(std::make_shared<std::stringstream>());
{
@ -117,7 +115,6 @@ namespace NetworkingObjects {
}
template <typename T> static void send_server_update_to_all_clients_except(const NetObjTemporaryPtr<T>& ptr, const std::shared_ptr<NetServer::ClientData>& clientToNotSendTo, const std::string& channel, std::function<void(const NetObjTemporaryPtr<T>&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) {
assert(isServer);
if(ptr.get_obj_man()->server && !ptr.get_obj_man()->server->is_disconnected()) {
auto ss(std::make_shared<std::stringstream>());
{
@ -130,7 +127,6 @@ namespace NetworkingObjects {
}
template <typename T> static void send_server_update_to_all_clients(const NetObjTemporaryPtr<T>& ptr, const std::string& channel, std::function<void(const NetObjTemporaryPtr<T>&, cereal::PortableBinaryOutputArchive&)> sendUpdateFunc) {
assert(isServer);
if(ptr.get_obj_man()->server && !ptr.get_obj_man()->server->is_disconnected()) {
auto ss(std::make_shared<std::stringstream>());
{

View file

@ -47,7 +47,7 @@ namespace NetworkingObjects {
}
bool contains(const NetObjID& p) const {
return std::find_if(data.begin(), data.end(), [&p](const NetObjOwnerPtr<T>& o){ o.get_net_id() == p; }) != data.end();
return std::find_if(data.begin(), data.end(), [&p](const NetObjOwnerPtr<T>& o){ return o.get_net_id() == p; }) != data.end();
}
const std::vector<NetObjOwnerPtr<T>>& get_data() const {
return data;
@ -68,7 +68,7 @@ namespace NetworkingObjects {
}
bool erase_by_id(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, const NetObjID& id) {
auto it = std::find_if(this->data.begin(), this->data.end(), [&id](const NetObjOwnerPtr<T>& o){ o.get_net_id() == id; });
auto it = std::find_if(this->data.begin(), this->data.end(), [&id](const NetObjOwnerPtr<T>& o){ return o.get_net_id() == id; });
if(it == this->data.end())
return false;
call_erase_callback(*it);
@ -77,14 +77,14 @@ namespace NetworkingObjects {
return true;
}
virtual void insert_network(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, NetObjOwnerPtr<T> newObj) const = 0;
virtual void insert_network(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, const NetObjOwnerPtr<T>& newObj) const = 0;
virtual void erase_network(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, const NetObjID& id) const = 0;
virtual void read_update(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) = 0;
void write_constructor(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, cereal::PortableBinaryOutputArchive& a) {
a(static_cast<uint32_t>(data.size()));
for(size_t i = 0; i < data.size(); i++)
data[i]->obj.write_create_message(a);
data[i].write_create_message(a);
}
void read_constructor(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) {
@ -128,7 +128,7 @@ namespace NetworkingObjects {
public:
NetObjUnorderedSetServer() {}
protected:
virtual void insert_network(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, NetObjOwnerPtr<T> newObj) const override {
virtual void insert_network(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, const std::shared_ptr<NetServer::ClientData>& clientInserting, const NetObjOwnerPtr<T>& newObj) const override {
l.send_server_update_to_all_clients_except(clientInserting, RELIABLE_COMMAND_CHANNEL, [&newObj](const NetObjTemporaryPtr<NetObjUnorderedSet<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrUnorderedSetCommand_StoC::INSERT_SINGLE_CONSTRUCT);
newObj.write_create_message(a);
@ -144,13 +144,13 @@ namespace NetworkingObjects {
a(command);
switch(command) {
case ObjPtrUnorderedSetCommand_CtoS::INSERT_SINGLE: {
insert(l, c, l.get_obj_man()->template read_create_message<T>(a, c));
NetObjUnorderedSet<T>::insert(l, c, l.get_obj_man()->template read_create_message<T>(a, c));
break;
}
case ObjPtrUnorderedSetCommand_CtoS::ERASE_SINGLE: {
NetObjID idToErase;
a(idToErase);
erase_by_id(l, idToErase);
NetObjUnorderedSet<T>::erase_by_id(l, idToErase);
break;
}
}
@ -161,7 +161,7 @@ namespace NetworkingObjects {
public:
NetObjUnorderedSetClient() {}
protected:
virtual void insert_network(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, const std::shared_ptr<NetServer::ClientData>&, NetObjOwnerPtr<T> newObj) const override {
virtual void insert_network(const NetObjTemporaryPtr<NetObjUnorderedSet<T>>& l, const std::shared_ptr<NetServer::ClientData>&, const NetObjOwnerPtr<T>& newObj) const override {
l.send_client_update(RELIABLE_COMMAND_CHANNEL, [&newObj](const NetObjTemporaryPtr<NetObjUnorderedSet<T>>&, cereal::PortableBinaryOutputArchive& a) {
a(ObjPtrUnorderedSetCommand_CtoS::INSERT_SINGLE);
newObj.write_create_message(a);
@ -183,7 +183,7 @@ namespace NetworkingObjects {
case ObjPtrUnorderedSetCommand_StoC::ERASE_SINGLE: {
NetObjID id;
a(id);
auto it = std::find_if(this->data.begin(), this->data.end(), [&id](const NetObjOwnerPtr<T>& o){ o.get_net_id() == id; });
auto it = std::find_if(this->data.begin(), this->data.end(), [&id](const NetObjOwnerPtr<T>& o){ return o.get_net_id() == id; });
if(it != this->data.end())
this->data.erase(it);
break;

View file

@ -2,7 +2,6 @@
#include "Helpers/Networking/NetLibrary.hpp"
#include "Helpers/NetworkingObjects/NetObjOrderedList.hpp"
#include "World.hpp"
#include "Server/CommandList.hpp"
#include "MainProgram.hpp"
#include <algorithm>

182
src/ClientData.cpp Normal file
View file

@ -0,0 +1,182 @@
#include "ClientData.hpp"
#include "World.hpp"
#include <Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp>
#include "MainProgram.hpp"
using namespace NetworkingObjects;
enum class ClientDataCommand : uint8_t {
SET_CURSOR_POS = 0,
SET_WINDOW_SIZE,
SET_CAM_COORDS,
SEND_CHAT_MESSAGE
};
ClientData::ClientData() {
set_from_init_struct(InitStruct());
}
ClientData::ClientData(const InitStruct& initStruct) {
set_from_init_struct(initStruct);
}
void ClientData::set_from_init_struct(const InitStruct& initStruct) {
camCoords = initStruct.camCoords;
windowSize = initStruct.windowSize;
cursorPos = initStruct.cursorPos;
cursorColor = initStruct.cursorColor;
displayName = initStruct.displayName;
}
void ClientData::register_class(World& world) {
world.netObjMan.register_class<ClientData, ClientData, ClientData, ClientData>({
.writeConstructorFuncClient = default_serialize_write_func<ClientData>,
.readConstructorFuncClient = default_serialize_read_constructor_func<ClientData>,
.readUpdateFuncClient = [&world](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>&) {
ClientDataCommand command;
a(command);
switch(command) {
case ClientDataCommand::SET_CURSOR_POS:
a(o->cursorPos);
break;
case ClientDataCommand::SET_WINDOW_SIZE:
a(o->windowSize);
break;
case ClientDataCommand::SET_CAM_COORDS:
a(o->camCoords);
break;
case ClientDataCommand::SEND_CHAT_MESSAGE: {
std::string chatMessage;
a(chatMessage);
world.add_chat_message(o->displayName, chatMessage, Toolbar::ChatMessage::Type::NORMAL);
}
}
},
.writeConstructorFuncServer = default_serialize_write_func<ClientData>,
.readConstructorFuncServer = default_serialize_read_constructor_func<ClientData>,
.readUpdateFuncServer = [&world](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) {
ClientDataCommand command;
a(command);
switch(command) {
case ClientDataCommand::SET_CURSOR_POS:
a(o->cursorPos);
o.send_server_update_to_all_clients_except(c, UNRELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryOutputArchive & a) {
a(ClientDataCommand::SET_CURSOR_POS, o->cursorPos);
});
break;
case ClientDataCommand::SET_WINDOW_SIZE:
a(o->windowSize);
o.send_server_update_to_all_clients_except(c, RELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryOutputArchive & a) {
a(ClientDataCommand::SET_WINDOW_SIZE, o->windowSize);
});
break;
case ClientDataCommand::SET_CAM_COORDS:
a(o->camCoords);
o.send_server_update_to_all_clients_except(c, RELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryOutputArchive & a) {
a(ClientDataCommand::SET_CAM_COORDS, o->camCoords);
});
break;
case ClientDataCommand::SEND_CHAT_MESSAGE: {
std::string chatMessage;
a(chatMessage);
world.add_chat_message(o->displayName, chatMessage, Toolbar::ChatMessage::Type::NORMAL);
o.send_server_update_to_all_clients_except(c, RELIABLE_COMMAND_CHANNEL, [&chatMessage](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryOutputArchive & a) {
a(ClientDataCommand::SEND_CHAT_MESSAGE, chatMessage);
});
break;
}
}
},
});
}
void ClientData::set_cursor_pos(const NetworkingObjects::NetObjTemporaryPtr<ClientData>& o, Vector2f newPos) {
o->cursorPos = newPos;
o.send_update_to_all(UNRELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryOutputArchive & a) {
a(ClientDataCommand::SET_CURSOR_POS, o->cursorPos);
});
}
void ClientData::set_window_size(const NetworkingObjects::NetObjTemporaryPtr<ClientData>& o, Vector2f newWindowSize) {
if(o->windowSize != newWindowSize) {
o->windowSize = newWindowSize;
o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryOutputArchive & a) {
a(ClientDataCommand::SET_WINDOW_SIZE, o->windowSize);
});
}
}
void ClientData::set_camera_coords(const NetworkingObjects::NetObjTemporaryPtr<ClientData>& o, const CoordSpaceHelper& newCoords) {
if(o->camCoords != newCoords) {
o->camCoords = newCoords;
o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, [](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryOutputArchive & a) {
a(ClientDataCommand::SET_CAM_COORDS, o->camCoords);
});
}
}
void ClientData::send_chat_message(const NetworkingObjects::NetObjTemporaryPtr<ClientData>& o, World& world, const std::string& chatMessage) {
world.add_chat_message(o->displayName, chatMessage, Toolbar::ChatMessage::Type::NORMAL);
o.send_update_to_all(RELIABLE_COMMAND_CHANNEL, [&chatMessage](const NetObjTemporaryPtr<ClientData>& o, cereal::PortableBinaryOutputArchive & a) {
a(ClientDataCommand::SEND_CHAT_MESSAGE, chatMessage);
});
}
void ClientData::set_display_name(const std::string& newDisplayName) {
displayName = newDisplayName;
}
void ClientData::set_cursor_color(const Vector3f& newCursorColor) {
cursorColor = newCursorColor;
}
const CoordSpaceHelper& ClientData::get_cam_coords() const {
return camCoords;
}
const Vector2f& ClientData::get_window_size() const {
return windowSize;
}
const Vector2f& ClientData::get_cursor_pos() const {
return cursorPos;
}
const std::string& ClientData::get_display_name() const {
return displayName;
}
const Vector3f& ClientData::get_cursor_color() const {
return cursorColor;
}
void ClientData::draw_cursor(SkCanvas* canvas, const DrawData& drawData) const {
if((drawData.cam.c.inverseScale << 10) > camCoords.inverseScale && drawData.clampDrawMinimum < camCoords.inverseScale) {
canvas->save();
camCoords.transform_sk_canvas(canvas, drawData);
canvas->translate(cursorPos.x(), cursorPos.y());
SkPaint p(SkColor4f{cursorColor.x(), cursorColor.y(), cursorColor.z(), 0.2f});
canvas->drawCircle(0.0f, 0.0f, 4.5f, p);
p.setStroke(true);
p.setStrokeWidth(1.0f);
p.setColor4f({cursorColor.x(), cursorColor.y(), cursorColor.z(), 0.7f});
canvas->drawCircle(0.0f, 0.0f, 5.0f, p);
SkFont f = drawData.main->toolbar.io->get_font(18.0f);
SkFontMetrics metrics;
f.getMetrics(&metrics);
float nextText = f.measureText(displayName.c_str(), displayName.length(), SkTextEncoding::kUTF8, nullptr);
Vector2f bounds{nextText, - metrics.fAscent + metrics.fDescent};
SkPaint p2(SkColor4f{cursorColor.x(), cursorColor.y(), cursorColor.z(), 0.5f});
canvas->drawSimpleText(displayName.c_str(), displayName.length(), SkTextEncoding::kUTF8, 6.0f, -metrics.fDescent, f, p2);
SkPaint p3(color_mul_alpha(drawData.main->toolbar.io->theme->backColor1, 0.5f));
canvas->drawRoundRect(SkRect::MakeXYWH(6.0f, -bounds.y(), bounds.x(), bounds.y()), 3.0f, 3.0f, p3);
canvas->restore();
}
}

48
src/ClientData.hpp Normal file
View file

@ -0,0 +1,48 @@
#pragma once
#include <Helpers/Serializers.hpp>
#include "CoordSpaceHelper.hpp"
#include <Helpers/NetworkingObjects/NetObjTemporaryPtr.hpp>
#include <Helpers/NetworkingObjects/NetObjManager.hpp>
class ClientData {
public:
struct InitStruct {
CoordSpaceHelper camCoords;
Vector2f windowSize = {1000.0f, 1000.0f};
Vector2f cursorPos = {1.0f, 1.0f};
Vector3f cursorColor = {1.0f, 1.0f, 1.0f};
std::string displayName;
};
ClientData();
ClientData(const InitStruct& initStruct);
static void register_class(World& world);
static void set_cursor_pos(const NetworkingObjects::NetObjTemporaryPtr<ClientData>& o, Vector2f newPos);
static void set_window_size(const NetworkingObjects::NetObjTemporaryPtr<ClientData>& o, Vector2f newWindowSize);
static void set_camera_coords(const NetworkingObjects::NetObjTemporaryPtr<ClientData>& o, const CoordSpaceHelper& newCoords);
static void send_chat_message(const NetworkingObjects::NetObjTemporaryPtr<ClientData>& o, World& world, const std::string& chatMessage);
void set_display_name(const std::string& newDisplayName);
void set_cursor_color(const Vector3f& newCursorColor);
const CoordSpaceHelper& get_cam_coords() const;
const Vector2f& get_window_size() const;
const Vector2f& get_cursor_pos() const;
const std::string& get_display_name() const;
const Vector3f& get_cursor_color() const;
void draw_cursor(SkCanvas* canvas, const DrawData& drawData) const;
template <typename Archive> void serialize(Archive& a) {
a(camCoords, windowSize, cursorPos, cursorColor, displayName);
}
private:
void set_from_init_struct(const InitStruct& initStruct);
CoordSpaceHelper camCoords;
Vector2f windowSize;
Vector2f cursorPos;
Vector3f cursorColor;
std::string displayName;
};

View file

@ -7,11 +7,8 @@ enum ServerCommands : MessageCommandType {
SERVER_KEEP_ALIVE,
SERVER_INITIAL_DATA,
SERVER_UPDATE_NETWORK_OBJECT,
SERVER_MOVE_CAMERA,
SERVER_MOVE_SCREEN_MOUSE,
SERVER_NEW_RESOURCE_ID,
SERVER_NEW_RESOURCE_DATA,
SERVER_CHAT_MESSAGE
SERVER_NEW_RESOURCE_DATA
};
enum ClientCommands : MessageCommandType {
@ -19,13 +16,8 @@ enum ClientCommands : MessageCommandType {
CLIENT_KEEP_ALIVE,
CLIENT_INITIAL_DATA,
CLIENT_UPDATE_NETWORK_OBJECT,
CLIENT_USER_CONNECT,
CLIENT_USER_DISCONNECT,
CLIENT_MOVE_CAMERA,
CLIENT_MOVE_SCREEN_MOUSE,
CLIENT_NEW_RESOURCE_ID,
CLIENT_NEW_RESOURCE_DATA,
CLIENT_CHAT_MESSAGE
CLIENT_NEW_RESOURCE_DATA
};
// Arbitrary value. Higher scale up values lead to less scale up events (which can cause stuttering), but leads to larger numbers overall, which can cause a tiny bit of lag and increased memory usage

View file

@ -1,16 +1,10 @@
#include "ConnectionManager.hpp"
#include "Helpers/Networking/NetLibrary.hpp"
#include "Server/CommandList.hpp"
#include "CommandList.hpp"
#include <chrono>
#include "World.hpp"
#include <Helpers/Logger.hpp>
void ConnectionManager::init_local_p2p(World& initWorld, const std::string& serverLocalID) {
localServer = std::make_unique<MainServer>(initWorld, serverLocalID);
client = std::make_shared<NetClient>(*localServer->netServer);
lastKeepAliveTime = std::chrono::steady_clock::now();
}
void ConnectionManager::connect_p2p(World& initWorld, const std::string& serverFullID) {
client = std::make_shared<NetClient>(serverFullID);
lastKeepAliveTime = std::chrono::steady_clock::now();
@ -19,9 +13,6 @@ void ConnectionManager::connect_p2p(World& initWorld, const std::string& serverF
}
void ConnectionManager::update() {
if(localServer) {
localServer->update();
}
if(client) {
client->update();
if(std::chrono::steady_clock::now() - lastKeepAliveTime > std::chrono::seconds(2)) {
@ -40,14 +31,6 @@ bool ConnectionManager::is_client_disconnected() {
return client && client->is_disconnected();
}
bool ConnectionManager::is_host_disconnected() {
return localServer && localServer->netServer->is_disconnected();
}
bool ConnectionManager::host_exists() {
return localServer != nullptr;
}
bool ConnectionManager::client_exists() {
return client != nullptr;
}

View file

@ -1,10 +1,10 @@
#pragma once
#include "Server/MainServer.hpp"
#include <Helpers/Networking/NetClient.hpp>
class World;
class ConnectionManager {
public:
void init_local_p2p(World& initWorld, const std::string& serverLocalID);
void connect_p2p(World& initWorld, const std::string& serverFullID);
void update();
void client_add_recv_callback(uint32_t commandID, const NetClientRecvCallback &callback);
@ -13,11 +13,9 @@ class ConnectionManager {
client->send_items_to_server(channel, items...);
}
bool is_client_disconnected();
bool is_host_disconnected();
bool host_exists();
bool client_exists();
NetLibrary::DownloadProgress client_get_resource_retrieval_progress();
std::unique_ptr<MainServer> localServer;
private:
std::shared_ptr<NetClient> client;
std::chrono::steady_clock::time_point lastKeepAliveTime;

View file

@ -1,12 +1,9 @@
#include "DrawCamera.hpp"
#include "DrawingProgram/DrawingProgramToolBase.hpp"
#include "Helpers/FixedPoint.hpp"
#include "Helpers/MathExtras.hpp"
#include "MainProgram.hpp"
#include "Server/CommandList.hpp"
#include "World.hpp"
#include "InputManager.hpp"
#include <iostream>
DrawCamera::DrawCamera():
c({0, 0}, WorldScalar(1000000), 0.0)

View file

@ -8,7 +8,6 @@
#include "GridModifyTool.hpp"
#include "../InputManager.hpp"
#include "../SharedTypes.hpp"
#include "../Server/CommandList.hpp"
#include <memory>
#include <optional>
#include <Helpers/ConvertVec.hpp>
@ -80,13 +79,6 @@ void DrawingProgram::parallel_loop_all_components(std::function<void(const Canva
parallel_loop_container(components->get_data(), func);
}
std::unordered_set<ServerClientID> DrawingProgram::get_used_resources() const {
std::unordered_set<ServerClientID> toRet;
//for(auto& c : components->get_data())
// c->obj->get_used_resources(toRet);
return toRet;
}
void DrawingProgram::erase_component_set(const std::unordered_set<CanvasComponentContainer::ObjInfoSharedPtr>& compsToErase) {
std::unordered_set<NetworkingObjects::NetObjID> idsToErase;
for(auto& c : compsToErase)

View file

@ -36,7 +36,6 @@ class DrawingProgram {
void add_file_to_canvas_by_path(const std::filesystem::path& filePath, Vector2f dropPos, bool addInSameThread);
void add_file_to_canvas_by_data(const std::string& fileName, std::string_view fileBuffer, Vector2f dropPos);
std::unordered_set<ServerClientID> get_used_resources() const;
void load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version);
void save_file(cereal::PortableBinaryOutputArchive& a) const;
World& world;

View file

@ -1,6 +1,5 @@
#include "EyeDropperTool.hpp"
#include "DrawingProgram.hpp"
#include "../Server/CommandList.hpp"
#include "../MainProgram.hpp"
#include "../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"

View file

@ -1,6 +1,5 @@
#include "ScreenshotTool.hpp"
#include "DrawingProgram.hpp"
#include "../Server/CommandList.hpp"
#include "../MainProgram.hpp"
#include "../DrawData.hpp"
#include "DrawingProgramToolBase.hpp"

View file

@ -1,10 +1,6 @@
#include "GridManager.hpp"
#include "Helpers/Networking/NetLibrary.hpp"
#include "Server/CommandList.hpp"
#include "SharedTypes.hpp"
#include "World.hpp"
#include "MainProgram.hpp"
#include <algorithm>
GridManager::GridManager(World& w):
world(w) {}

View file

@ -5,8 +5,6 @@
#include <optional>
#include "Server/CommandList.hpp"
#ifdef __EMSCRIPTEN__
#include <EmscriptenHelpers/emscripten_browser_clipboard.h>

View file

@ -16,7 +16,6 @@
#include <include/core/SkData.h>
#include <include/core/SkSurfaceProps.h>
#include "InputManager.hpp"
#include "Server/CommandList.hpp"
#include <cereal/types/vector.hpp>
#include <cereal/types/unordered_map.hpp>
#include <Eigen/Core>
@ -137,7 +136,7 @@ void MainProgram::update() {
void MainProgram::update_display_names() {
for(auto& w : worlds) {
if(!w->network_being_used())
w->displayName = displayName;
w->ownClientData->set_display_name(displayName);
}
}

View file

@ -62,13 +62,6 @@ class MainProgram {
SDL_Window* sdlWindow;
} window;
struct Clipboard {
std::vector<std::shared_ptr<DrawComponent>> components;
std::unordered_map<ServerClientID, ResourceData> resources;
WorldVec pos;
WorldScalar inverseScale;
} clipboard;
std::shared_ptr<FontData> fonts;
TimePoint deltaTime;
Toolbar toolbar;

View file

@ -3,7 +3,7 @@
#include <include/core/SkImage.h>
#include <Helpers/NetworkingObjects/NetObjID.hpp>
#include "ResourceDisplay/SvgResourceDisplay.hpp"
#include "Server/CommandList.hpp"
#include "CommandList.hpp"
#include "ResourceDisplay/ImageResourceDisplay.hpp"
#include "ResourceDisplay/FileResourceDisplay.hpp"
#include <cereal/types/string.hpp>
@ -19,16 +19,14 @@ ResourceManager::ResourceManager(World& initWorld):
void ResourceManager::init_client_callbacks() {
world.con.client_add_recv_callback(SERVER_NEW_RESOURCE_ID, [&](cereal::PortableBinaryInputArchive& message) {
if(!world.con.host_exists()) {
NetworkingObjects::NetObjID idBeingRetrieved;
message(idBeingRetrieved);
// There is a scenario where the resource id and data messages can be sent twice for the same resource (a resource can be sent early before CLIENT_INITIAL_DATA is sent, and then resent with CLIENT_INITIAL_DATA), so check if resource already exists, and only keep track of the ID if the resource doesn't exist yet
if(!world.netObjMan.get_obj_temporary_ref_from_id<ResourceData>(idBeingRetrieved))
resourcesBeingRetrieved.emplace(idBeingRetrieved, 0);
}
NetworkingObjects::NetObjID idBeingRetrieved;
message(idBeingRetrieved);
// There is a scenario where the resource id and data messages can be sent twice for the same resource (a resource can be sent early before CLIENT_INITIAL_DATA is sent, and then resent with CLIENT_INITIAL_DATA), so check if resource already exists, and only keep track of the ID if the resource doesn't exist yet
if(!world.netObjMan.get_obj_temporary_ref_from_id<ResourceData>(idBeingRetrieved))
resourcesBeingRetrieved.emplace(idBeingRetrieved, std::weak_ptr<NetServer::ClientData>());
});
world.con.client_add_recv_callback(SERVER_NEW_RESOURCE_DATA, [&](cereal::PortableBinaryInputArchive& message) {
if(!world.con.host_exists() && !resourcesBeingRetrieved.empty()) { // If the resource was sent for the second time, this will be empty, so we can ignore the message
if(!resourcesBeingRetrieved.empty()) { // If the resource was sent for the second time, this will be empty, so we can ignore the message
ResourceData newResource;
message(newResource);
resourceList.emplace_back(world.netObjMan.make_obj_direct_with_specific_id<ResourceData>(resourcesBeingRetrieved.begin()->first, newResource));
@ -38,21 +36,21 @@ void ResourceManager::init_client_callbacks() {
}
void ResourceManager::init_server_callbacks() {
world.con.localServer->netServer->add_recv_callback(CLIENT_NEW_RESOURCE_ID, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
world.netServer->add_recv_callback(CLIENT_NEW_RESOURCE_ID, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
NetworkingObjects::NetObjID idBeingRetrieved;
message(idBeingRetrieved);
resourcesBeingRetrieved.emplace(idBeingRetrieved, client->customID);
resourcesBeingRetrieved.emplace(idBeingRetrieved, client);
});
world.con.localServer->netServer->add_recv_callback(CLIENT_NEW_RESOURCE_DATA, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
world.netServer->add_recv_callback(CLIENT_NEW_RESOURCE_DATA, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
ResourceData newResource;
message(newResource);
auto it = std::find_if(resourcesBeingRetrieved.begin(), resourcesBeingRetrieved.end(), [&client](auto& p) {
return p.second == client->customID;
return (!p.second.expired() && p.second.lock() == client);
});
resourceList.emplace_back(world.netObjMan.make_obj_direct_with_specific_id<ResourceData>(it->first, newResource));
world.con.localServer->netServer->send_items_to_all_clients_except(client, RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_ID, resourceList.back().get_net_id());
world.con.localServer->netServer->send_items_to_all_clients_except(client, RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_DATA, newResource);
resourcesBeingRetrieved.erase(resourceList.back().get_net_id());
world.netServer->send_items_to_all_clients_except(client, RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_ID, resourceList.back().get_net_id());
world.netServer->send_items_to_all_clients_except(client, RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_DATA, newResource);
resourcesBeingRetrieved.erase(it);
});
}
@ -60,18 +58,13 @@ float ResourceManager::get_resource_retrieval_progress(const NetworkingObjects::
auto resourceClientID = resourcesBeingRetrieved.find(id);
if(resourceClientID == resourcesBeingRetrieved.end())
return 0.0f;
if(world.con.localServer) {
uint64_t clientIDToLookFor = resourceClientID->second;
const std::vector<std::shared_ptr<NetServer::ClientData>>& clientListInNetServer = world.con.localServer->netServer->get_client_list();
auto netClientIt = std::find_if(clientListInNetServer.begin(), clientListInNetServer.end(), [&clientIDToLookFor](auto& nC) {
return nC->customID == clientIDToLookFor;
});
if(netClientIt == clientListInNetServer.end()) {
resourcesBeingRetrieved.erase(resourceClientID); // Client disconnected, remove it from the list
if(world.netServer) {
std::weak_ptr<NetServer::ClientData> clientIDToLookFor = resourceClientID->second;
if(clientIDToLookFor.expired()) {
resourcesBeingRetrieved.erase(resourceClientID);
return 0.0f;
}
auto progressBytes = (*netClientIt)->get_progress_into_fragmented_message(RESOURCE_COMMAND_CHANNEL);
auto progressBytes = clientIDToLookFor.lock()->get_progress_into_fragmented_message(RESOURCE_COMMAND_CHANNEL);
if(progressBytes.totalBytes == 0)
return 0.0f;
return static_cast<float>(progressBytes.downloadedBytes) / static_cast<float>(progressBytes.totalBytes);
@ -136,9 +129,9 @@ const NetworkingObjects::NetObjOwnerPtr<ResourceData>& ResourceManager::add_reso
return *it;
}
auto& resourceInsert = resourceList.emplace_back(world.netObjMan.make_obj_direct<ResourceData>(resource));
if(world.con.localServer) {
world.con.localServer->netServer->send_items_to_all_clients(RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_ID, resourceInsert.get_net_id());
world.con.localServer->netServer->send_items_to_all_clients(RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_DATA, resource);
if(world.netServer) {
world.netServer->send_items_to_all_clients(RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_ID, resourceInsert.get_net_id());
world.netServer->send_items_to_all_clients(RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_DATA, resource);
}
else {
world.con.client_send_items_to_server(RESOURCE_COMMAND_CHANNEL, SERVER_NEW_RESOURCE_ID, resourceInsert.get_net_id());

View file

@ -36,7 +36,7 @@ class ResourceManager {
private:
std::unordered_map<NetworkingObjects::NetObjID, std::unique_ptr<ResourceDisplay>> displays;
std::unordered_map<NetworkingObjects::NetObjID, uint64_t> resourcesBeingRetrieved;
std::unordered_map<NetworkingObjects::NetObjID, std::weak_ptr<NetServer::ClientData>> resourcesBeingRetrieved;
std::vector<NetworkingObjects::NetObjOwnerPtr<ResourceData>> resourceList;
static std::array<const SkCodecs::Decoder, 5> decoders;

View file

@ -1,25 +0,0 @@
#pragma once
#include "../SharedTypes.hpp"
#include <Helpers/Serializers.hpp>
#include "../CoordSpaceHelper.hpp"
#include "../ResourceManager.hpp"
#include <Helpers/Networking/NetServer.hpp>
#include <chrono>
class ClientData {
public:
CoordSpaceHelper camCoords;
Vector2f windowSize = {1000.0f, 1000.0f};
Vector2f cursorPos = {1.0f, 1.0f};
Vector3f cursorColor = {1.0f, 1.0f, 1.0f};
std::string displayName;
ServerPortionID serverID = 0;
uint64_t canvasScale = 0;
template <typename Archive> void serialize(Archive& a) {
a(camCoords, windowSize, cursorPos, cursorColor, displayName);
}
};

View file

@ -1,151 +0,0 @@
#include "MainServer.hpp"
#include "CommandList.hpp"
#include "Helpers/HsvRgb.hpp"
#include "Helpers/Networking/NetLibrary.hpp"
#include "ServerData.hpp"
#include "../SharedTypes.hpp"
#include "../CoordSpaceHelper.hpp"
#include <cereal/archives/portable_binary.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/types/unordered_map.hpp>
#include <cereal/types/utility.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/unordered_set.hpp>
#include <Helpers/Serializers.hpp>
#include <Helpers/Random.hpp>
#include <chrono>
#include <limits>
#include <fstream>
#include "../World.hpp"
MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
world(initWorld)
{
data.canvasScale = world.canvasScale;
lastKeepAliveSent = std::chrono::steady_clock::now();
netServer = std::make_shared<NetServer>(serverLocalID);
NetLibrary::register_server(netServer);
world.netObjMan.set_server(netServer, CLIENT_UPDATE_NETWORK_OBJECT);
netServer->add_recv_callback(SERVER_INITIAL_DATA, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
bool isDirectConnect;
ClientData newClient;
Vector3f hsv;
hsv[0] = Random::get().real_range(0.0f, 360.0f);
hsv[1] = Random::get().real_range(0.3f, 0.7f);
hsv[2] = 1.0;
newClient.cursorColor = hsv_to_rgb<Vector3f>(hsv);
newClient.canvasScale = data.canvasScale;
message(newClient.displayName, isDirectConnect);
ensure_display_name_unique(newClient.displayName);
if(isDirectConnect) {
fileDisplayName = newClient.displayName;
message(newClient.camCoords, newClient.windowSize);
}
else {
auto& [randomClientServerID, randomClient] = *clients.begin();
newClient.camCoords = randomClient.camCoords;
newClient.windowSize = randomClient.windowSize;
}
newClient.serverID = Random::get().int_range<ServerPortionID>(1, std::numeric_limits<ServerPortionID>::max());
client->customID = newClient.serverID;
netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_USER_CONNECT, newClient.serverID, newClient.displayName, newClient.cursorColor);
if(isDirectConnect)
netServer->send_items_to_client(client, RELIABLE_COMMAND_CHANNEL, CLIENT_INITIAL_DATA, isDirectConnect, newClient.serverID, newClient.cursorColor);
else {
auto ss(std::make_shared<std::stringstream>());
{
cereal::PortableBinaryOutputArchive a(*ss);
a(CLIENT_INITIAL_DATA, isDirectConnect, newClient.serverID, newClient.cursorColor, newClient.displayName, newClient.camCoords, newClient.windowSize, fileDisplayName, clients, data);
world.bMan.bookmarks.write_create_message(a);
world.gridMan.grids.write_create_message(a);
world.drawProg.write_components_server(a);
world.canvasTheme.write_create_message(a);
}
netServer->send_string_stream_to_client(client, RELIABLE_COMMAND_CHANNEL, ss);
for(auto& r : world.rMan.resource_list()) {
netServer->send_items_to_client(client, RESOURCE_COMMAND_CHANNEL, CLIENT_NEW_RESOURCE_ID, r.get_net_id());
netServer->send_items_to_client(client, RESOURCE_COMMAND_CHANNEL, CLIENT_NEW_RESOURCE_DATA, *r);
}
}
clients.emplace(newClient.serverID, newClient);
});
netServer->add_recv_callback(SERVER_UPDATE_NETWORK_OBJECT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
world.netObjMan.read_update_message(message, client);
});
netServer->add_recv_callback(SERVER_MOVE_CAMERA, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
auto& c = clients[client->customID];
message(c.camCoords, c.windowSize);
netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_MOVE_CAMERA, c.serverID, c.camCoords, c.windowSize);
});
netServer->add_recv_callback(SERVER_MOVE_SCREEN_MOUSE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
auto& c = clients[client->customID];
message(c.cursorPos);
netServer->send_items_to_all_clients_except(client, UNRELIABLE_COMMAND_CHANNEL, CLIENT_MOVE_SCREEN_MOUSE, c.serverID, c.cursorPos);
});
netServer->add_recv_callback(SERVER_KEEP_ALIVE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
});
netServer->add_disconnect_callback([&](std::shared_ptr<NetServer::ClientData> client) {
auto it = clients.find(client->customID);
if(it != clients.end()) {
netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_USER_DISCONNECT, client->customID);
clients.erase(it);
}
});
}
void MainServer::ensure_display_name_unique(std::string& displayName) {
for(;;) {
bool isUnique = true;
for(auto& [id, client] : clients) {
if(client.displayName == displayName) {
size_t leftParenthesisIndex = displayName.find_last_of('(');
size_t rightParenthesisIndex = displayName.find_last_of(')');
bool failToIncrement = true;
if(leftParenthesisIndex != std::string::npos && rightParenthesisIndex != std::string::npos && leftParenthesisIndex < rightParenthesisIndex) {
std::string numStr = displayName.substr(leftParenthesisIndex + 1, rightParenthesisIndex - leftParenthesisIndex - 1);
bool isAllDigits = true;
for(char c : numStr) {
if(!isdigit(c)) {
isAllDigits = false;
break;
}
}
if(isAllDigits) {
try {
int s = std::stoi(numStr);
s++;
displayName = displayName.substr(0, leftParenthesisIndex);
displayName += "(" + std::to_string(s) + ")";
failToIncrement = false;
}
catch(...) {}
}
}
if(failToIncrement)
displayName += " (2)";
isUnique = false;
break;
}
}
if(isUnique)
break;
}
}
void MainServer::update() {
netServer->update();
if(std::chrono::steady_clock::now() - lastKeepAliveSent > std::chrono::seconds(2)) {
netServer->send_items_to_all_clients(UNRELIABLE_COMMAND_CHANNEL, CLIENT_KEEP_ALIVE);
lastKeepAliveSent = std::chrono::steady_clock::now();
}
}
MainServer::~MainServer() {
//netServer->close_server();
}

View file

@ -1,21 +0,0 @@
#pragma once
#include <Helpers/Networking/NetServer.hpp>
#include <chrono>
#include "ClientData.hpp"
#include "Helpers/Networking/NetLibrary.hpp"
#include "ServerData.hpp"
class MainServer {
public:
MainServer(World& initWorld, const std::string& serverLocalID);
void update();
~MainServer();
std::shared_ptr<NetServer> netServer;
std::unordered_map<ServerPortionID, ClientData> clients;
private:
World& world;
void ensure_display_name_unique(std::string& displayName);
ServerData data;
std::string fileDisplayName;
std::chrono::steady_clock::time_point lastKeepAliveSent;
};

View file

@ -1,12 +0,0 @@
#include "ServerData.hpp"
void ServerData::save(cereal::PortableBinaryOutputArchive& a) const {
a(canvasScale);
}
void ServerData::load(cereal::PortableBinaryInputArchive& a) {
a(canvasScale);
}
void ServerData::scale_up(const WorldScalar& scaleUpAmount) {
}

View file

@ -1,16 +0,0 @@
#pragma once
#include "cereal/archives/portable_binary.hpp"
#include "../SharedTypes.hpp"
#include "../ResourceManager.hpp"
#include "../WorldGrid.hpp"
class DrawComponent;
class ServerData {
public:
uint64_t canvasScale = 0;
void save(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);
void scale_up(const WorldScalar& scaleUpAmount);
};

View file

@ -10,26 +10,7 @@ using namespace Eigen;
typedef Vector<WorldScalar, 2> WorldVec;
enum DrawComponentType : uint8_t {
DRAWCOMPONENT_BRUSHSTROKE = 0,
DRAWCOMPONENT_RECTANGLE,
DRAWCOMPONENT_ELLIPSE,
DRAWCOMPONENT_TEXTBOX,
DRAWCOMPONENT_IMAGE
};
typedef uint64_t ClientPortionID;
typedef uint64_t ServerPortionID;
typedef std::pair<ServerPortionID, ClientPortionID> ServerClientID;
template <> struct std::hash<ServerClientID> {
size_t operator()(const ServerClientID& v) const
{
uint64_t h = 0;
hash_combine(h, v.first, v.second);
return h;
}
};
typedef uint64_t NetClientID;
template <typename F, typename S> std::ostream& operator<<(std::ostream& os, const std::pair<F, S>& p) {
os << p.first << ", " << p.second;

View file

@ -1275,33 +1275,37 @@ void Toolbar::player_list() {
gui.obstructing_window();
gui.push_id("client list");
gui.text_label_centered("Player List");
gui.left_to_right_line_layout([&]() {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(20), .height = CLAY_SIZING_FIXED(20)}
},
.backgroundColor = convert_vec4<Clay_Color>(SkColor4f{main.world->userColor.x(), main.world->userColor.y(), main.world->userColor.z(), 1.0f}),
.cornerRadius = CLAY_CORNER_RADIUS(3)
}) {}
gui.text_label(main.world->displayName);
});
size_t num = 0;
for(auto& [id, client] : main.world->clients) {
gui.push_id(num++);
if(!main.world->clientStillConnecting) {
gui.left_to_right_line_layout([&]() {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(20), .height = CLAY_SIZING_FIXED(20)}
},
.backgroundColor = convert_vec4<Clay_Color>(SkColor4f{client.cursorColor.x(), client.cursorColor.y(), client.cursorColor.z(), 1.0f}),
.backgroundColor = convert_vec4<Clay_Color>(SkColor4f{main.world->ownClientData->get_cursor_color().x(), main.world->ownClientData->get_cursor_color().y(), main.world->ownClientData->get_cursor_color().z(), 1.0f}),
.cornerRadius = CLAY_CORNER_RADIUS(3)
}) {}
gui.text_label(client.displayName);
CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}}) {}
if(gui.text_button("teleport button", "Jump To"))
main.world->drawData.cam.smooth_move_to(*main.world, client.camCoords, client.windowSize);
gui.text_label(main.world->ownClientData->get_display_name());
});
gui.pop_id();
size_t num = 0;
for(auto& client : main.world->clients->get_data()) {
if(client != main.world->ownClientData) {
gui.push_id(num++);
gui.left_to_right_line_layout([&]() {
CLAY_AUTO_ID({
.layout = {
.sizing = {.width = CLAY_SIZING_FIXED(20), .height = CLAY_SIZING_FIXED(20)}
},
.backgroundColor = convert_vec4<Clay_Color>(SkColor4f{client->get_cursor_color().x(), client->get_cursor_color().y(), client->get_cursor_color().z(), 1.0f}),
.cornerRadius = CLAY_CORNER_RADIUS(3)
}) {}
gui.text_label(client->get_display_name());
CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}}) {}
if(gui.text_button("teleport button", "Jump To"))
main.world->drawData.cam.smooth_move_to(*main.world, client->get_cam_coords(), client->get_window_size());
});
gui.pop_id();
}
}
}
if(gui.text_button_wide("close list", "Done"))
playerMenuOpen = false;

View file

@ -2,7 +2,6 @@
#include "GUIStuff/GUIManager.hpp"
#include "DrawData.hpp"
#include "Helpers/FileDownloader.hpp"
#include "Server/CommandList.hpp"
#include <filesystem>
#include <modules/skparagraph/include/Paragraph.h>
#include <nlohmann/json.hpp>

View file

@ -8,7 +8,9 @@
#include <Helpers/NetworkingObjects/NetObjGenericSerializedClass.hpp>
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
#include <cereal/types/unordered_map.hpp>
#include "Server/CommandList.hpp"
#include "Helpers/NetworkingObjects/NetObjTemporaryPtr.decl.hpp"
#include "Helpers/NetworkingObjects/NetObjUnorderedSet.hpp"
#include "CommandList.hpp"
#include "MainProgram.hpp"
#include "SharedTypes.hpp"
#include "Toolbar.hpp"
@ -42,13 +44,12 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
canvasTheme(*this)
{
init_net_obj_type_list();
init_client_data_list();
gridMan.init();
bMan.init();
drawProg.init();
canvasTheme.init();
displayName = main.displayName;
netSource = worldInfo.netSource;
if(worldInfo.conType == CONNECTIONTYPE_CLIENT || worldInfo.conType == CONNECTIONTYPE_SERVER)
@ -76,9 +77,36 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
}
}
rMan.init_client_callbacks();
init_client_callbacks();
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_INITIAL_DATA, displayName, false);
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_INITIAL_DATA, main.displayName);
}
void World::init_client_data_list() {
if(netObjMan.is_server()) {
clients = netObjMan.make_obj<NetworkingObjects::NetObjUnorderedSet<ClientData>>();
ownClientData = clients->emplace_direct(clients, ClientData::InitStruct{
.cursorColor = get_random_cursor_color(),
.displayName = main.displayName
});
init_client_data_list_callbacks();
}
}
Vector3f World::get_random_cursor_color() {
Vector3f hsv;
hsv[0] = Random::get().real_range(0.0f, 360.0f);
hsv[1] = Random::get().real_range(0.3f, 0.7f);
hsv[2] = 1.0;
return hsv_to_rgb<Vector3f>(hsv);
}
void World::init_client_data_list_callbacks() {
clients->set_insert_callback([&](const NetworkingObjects::NetObjOwnerPtr<ClientData>& objPtr) {
add_chat_message(objPtr->get_display_name(), "joined", Toolbar::ChatMessage::Type::JOIN);
});
clients->set_erase_callback([&](const NetworkingObjects::NetObjOwnerPtr<ClientData>& objPtr) {
add_chat_message(objPtr->get_display_name(), "left", Toolbar::ChatMessage::Type::JOIN);
});
}
void World::init_net_obj_type_list() {
@ -90,83 +118,74 @@ void World::init_net_obj_type_list() {
CanvasComponentContainer::register_class(netObjMan);
NetworkingObjects::register_ordered_list_class<CanvasComponentContainer>(netObjMan);
canvasTheme.register_class();
ClientData::register_class(*this);
NetworkingObjects::register_unordered_set_class<ClientData>(netObjMan);
}
void World::init_server_callbacks() {
con.localServer->netServer->add_recv_callback(SERVER_CHAT_MESSAGE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
std::string chatMessage;
message(chatMessage);
add_chat_message(clients[client->customID].displayName, chatMessage, Toolbar::ChatMessage::Type::NORMAL);
con.localServer->netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_CHAT_MESSAGE, client->customID, chatMessage);
rMan.init_server_callbacks();
netServer->add_recv_callback(SERVER_INITIAL_DATA, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
std::cout << "Received server init data" << std::endl;
ClientData::InitStruct newClientData;
newClientData.cursorColor = get_random_cursor_color();
message(newClientData.displayName);
ensure_display_name_unique(newClientData.displayName);
newClientData.camCoords = ownClientData->get_cam_coords();
newClientData.windowSize = ownClientData->get_window_size();
NetworkingObjects::NetObjTemporaryPtr<ClientData> clientDataObjPtr = clients->emplace_direct(clients, newClientData);
client->customID = clientDataObjPtr.get_net_id().data;
auto ss(std::make_shared<std::stringstream>());
{
cereal::PortableBinaryOutputArchive a(*ss);
a(CLIENT_INITIAL_DATA, name, clientDataObjPtr.get_net_id());
bMan.bookmarks.write_create_message(a);
gridMan.grids.write_create_message(a);
drawProg.write_components_server(a);
canvasTheme.write_create_message(a);
clients.write_create_message(a);
}
netServer->send_string_stream_to_client(client, RELIABLE_COMMAND_CHANNEL, ss);
for(auto& r : rMan.resource_list()) {
netServer->send_items_to_client(client, RESOURCE_COMMAND_CHANNEL, CLIENT_NEW_RESOURCE_ID, r.get_net_id());
netServer->send_items_to_client(client, RESOURCE_COMMAND_CHANNEL, CLIENT_NEW_RESOURCE_DATA, *r);
}
});
netServer->add_recv_callback(SERVER_UPDATE_NETWORK_OBJECT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
netObjMan.read_update_message(message, client);
});
netServer->add_recv_callback(SERVER_KEEP_ALIVE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
});
netServer->add_disconnect_callback([&](std::shared_ptr<NetServer::ClientData> client) {
NetworkingObjects::NetObjID idToErase;
idToErase.data = client->customID;
clients->erase(clients, idToErase);
});
}
void World::init_client_callbacks() {
rMan.init_client_callbacks();
con.client_add_recv_callback(CLIENT_INITIAL_DATA, [&](cereal::PortableBinaryInputArchive& message) {
bool isDirectConnect;
message(isDirectConnect, ownID, userColor);
if(!isDirectConnect) {
std::string fileDisplayName;
CoordSpaceHelper camCoordsToMoveTo;
Vector2f windowSizeToMoveTo;
message(displayName);
message(camCoordsToMoveTo);
message(windowSizeToMoveTo);
message(fileDisplayName);
message(clients);
set_name(fileDisplayName);
drawData.cam.smooth_move_to(*main.world, camCoordsToMoveTo, windowSizeToMoveTo, true);
message(canvasScale);
bMan.bookmarks = netObjMan.read_create_message<NetworkingObjects::NetObjOrderedList<Bookmark>>(message, nullptr);
gridMan.grids = netObjMan.read_create_message<NetworkingObjects::NetObjOrderedList<WorldGrid>>(message, nullptr);
drawProg.read_components_client(message);
canvasTheme.read_create_message(message);
}
std::cout << "Received client init data" << std::endl;
std::string fileDisplayName;
NetworkingObjects::NetObjID clientDataObjID;
message(fileDisplayName, clientDataObjID);
set_name(fileDisplayName);
bMan.bookmarks = netObjMan.read_create_message<NetworkingObjects::NetObjOrderedList<Bookmark>>(message, nullptr);
gridMan.grids = netObjMan.read_create_message<NetworkingObjects::NetObjOrderedList<WorldGrid>>(message, nullptr);
drawProg.read_components_client(message);
canvasTheme.read_create_message(message);
clients = netObjMan.read_create_message<NetworkingObjects::NetObjUnorderedSet<ClientData>>(message, nullptr);
init_client_data_list_callbacks();
ownClientData = netObjMan.get_obj_from_id<ClientData>(clientDataObjID);
drawData.cam.smooth_move_to(*main.world, ownClientData->get_cam_coords(), ownClientData->get_window_size(), true);
clientStillConnecting = false;
});
con.client_add_recv_callback(CLIENT_USER_CONNECT, [&](cereal::PortableBinaryInputArchive& message) {
ServerPortionID id;
std::string displayName;
Vector3f cursorColor;
message(id, displayName, cursorColor);
clients[id].displayName = displayName;
clients[id].cursorColor = cursorColor;
add_chat_message(clients[id].displayName, "joined", Toolbar::ChatMessage::Type::JOIN);
});
con.client_add_recv_callback(CLIENT_UPDATE_NETWORK_OBJECT, [&](cereal::PortableBinaryInputArchive& message) {
if(!con.host_exists() && !clientStillConnecting) // Dont update a direct connected client (which is just the server), and dont process these until the CLIENT_INITIAL_DATA message is received first
netObjMan.read_update_message(message, nullptr);
});
con.client_add_recv_callback(CLIENT_USER_DISCONNECT, [&](cereal::PortableBinaryInputArchive& message) {
ServerPortionID id;
message(id);
auto it = clients.find(id);
if(it != clients.end()) {
add_chat_message(clients[id].displayName, "left", Toolbar::ChatMessage::Type::JOIN);
clients.erase(id);
}
});
con.client_add_recv_callback(CLIENT_MOVE_CAMERA, [&](cereal::PortableBinaryInputArchive& message) {
ServerPortionID id;
message(id);
auto& c = clients[id];
message(c.camCoords, c.windowSize);
});
con.client_add_recv_callback(CLIENT_MOVE_SCREEN_MOUSE, [&](cereal::PortableBinaryInputArchive& message) {
ServerPortionID id;
message(id);
auto& c = clients[id];
message(c.cursorPos);
});
con.client_add_recv_callback(CLIENT_CHAT_MESSAGE, [&](cereal::PortableBinaryInputArchive& message) {
if(!con.host_exists() && !clientStillConnecting) { // Dont update a direct connected client (which is just the server), and dont process these until the CLIENT_INITIAL_DATA message is received first
ServerPortionID senderID;
std::string chatMessage;
message(senderID, chatMessage);
auto it = clients.find(senderID);
if(it != clients.end())
add_chat_message(clients[senderID].displayName, chatMessage, Toolbar::ChatMessage::Type::NORMAL);
}
netObjMan.read_update_message(message, nullptr);
});
con.client_add_recv_callback(CLIENT_KEEP_ALIVE, [&](cereal::PortableBinaryInputArchive& message) {
});
@ -175,11 +194,10 @@ void World::init_client_callbacks() {
void World::focus_update() {
con.update();
if(con.is_host_disconnected()) {
if(netServer && netServer->is_disconnected()) {
Logger::get().log("USERINFO", "Host connection failed");
clientStillConnecting = false;
clients.clear();
netSource.clear();
netServer = nullptr;
conType = CONNECTIONTYPE_LOCAL;
con = ConnectionManager();
}
@ -196,15 +214,10 @@ void World::focus_update() {
timeToSendCameraData.update_time_since();
if(timeToSendCameraData.get_time_since() > SECONDS_TO_SEND_CAMERA_DATA) {
timeToSendCameraData.update_time_point();
if(!sentCameraData.has_value() || (sentCameraData.value().windowSize != main.window.size.cast<float>().eval() || sentCameraData.value().camTransform != drawData.cam.c)) {
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_MOVE_CAMERA, drawData.cam.c, main.window.size.cast<float>().eval());
sentCameraData = {
.camTransform = drawData.cam.c,
.windowSize = main.window.size.cast<float>()
};
}
ownClientData->set_window_size(ownClientData, main.window.size.cast<float>().eval());
ownClientData->set_camera_coords(ownClientData, drawData.cam.c);
}
con.client_send_items_to_server(UNRELIABLE_COMMAND_CHANNEL, SERVER_MOVE_SCREEN_MOUSE, main.input.mouse.pos);
ownClientData->set_cursor_pos(ownClientData, main.input.mouse.pos);
drawProg.update();
}
@ -237,16 +250,18 @@ void World::unfocus_update() {
Logger::get().log("USERINFO", "Client connection failed");
setToDestroy = true;
}
if(netServer) {
netServer->update();
if(std::chrono::steady_clock::now() - lastKeepAliveSent > std::chrono::seconds(2)) {
netServer->send_items_to_all_clients(UNRELIABLE_COMMAND_CHANNEL, CLIENT_KEEP_ALIVE);
lastKeepAliveSent = std::chrono::steady_clock::now();
}
}
}
void World::send_chat_message(const std::string& message) {
if(!clientStillConnecting) {
if(con.localServer)
con.localServer->netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_CHAT_MESSAGE, ownID, message);
else
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_CHAT_MESSAGE, message);
add_chat_message(displayName, message, Toolbar::ChatMessage::Type::NORMAL);
}
if(!clientStillConnecting)
ownClientData->send_chat_message(ownClientData, *this, message);
}
void World::add_chat_message(const std::string& name, const std::string& message, Toolbar::ChatMessage::Type type) {
@ -279,18 +294,55 @@ void World::set_name(const std::string& n) {
name = n;
}
void World::ensure_display_name_unique(std::string& displayName) {
for(;;) {
bool isUnique = true;
for(auto& client : clients->get_data()) {
if(client->get_display_name() == displayName) {
size_t leftParenthesisIndex = displayName.find_last_of('(');
size_t rightParenthesisIndex = displayName.find_last_of(')');
bool failToIncrement = true;
if(leftParenthesisIndex != std::string::npos && rightParenthesisIndex != std::string::npos && leftParenthesisIndex < rightParenthesisIndex) {
std::string numStr = displayName.substr(leftParenthesisIndex + 1, rightParenthesisIndex - leftParenthesisIndex - 1);
bool isAllDigits = true;
for(char c : numStr) {
if(!isdigit(c)) {
isAllDigits = false;
break;
}
}
if(isAllDigits) {
try {
int s = std::stoi(numStr);
s++;
displayName = displayName.substr(0, leftParenthesisIndex);
displayName += "(" + std::to_string(s) + ")";
failToIncrement = false;
}
catch(...) {}
}
}
if(failToIncrement)
displayName += " (2)";
isUnique = false;
break;
}
}
if(isUnique)
break;
}
}
void World::start_hosting(const std::string& initNetSource, const std::string& serverLocalID) {
if(conType != CONNECTIONTYPE_LOCAL)
return;
main.init_net_library();
con.init_local_p2p(*this, serverLocalID);
rMan.init_client_callbacks();
rMan.init_server_callbacks();
init_client_callbacks();
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_INITIAL_DATA, displayName, true, drawData.cam.c, main.window.size.cast<float>().eval());
netServer = std::make_shared<NetServer>(serverLocalID);
NetLibrary::register_server(netServer);
netObjMan.set_server(netServer, CLIENT_UPDATE_NETWORK_OBJECT);
conType = CONNECTIONTYPE_SERVER;
netSource = initNetSource;
clientStillConnecting = true;
init_server_callbacks();
}
void World::save_to_file(const std::filesystem::path& filePathToSaveAt) {
@ -409,34 +461,10 @@ WorldScalar World::calculate_zoom_from_uniform_zoom(WorldScalar uniformZoom, Wor
}
void World::draw_other_player_cursors(SkCanvas* canvas, const DrawData& drawData) {
for(auto& [id, data] : clients) {
if((drawData.cam.c.inverseScale << 10) > data.camCoords.inverseScale && drawData.clampDrawMinimum < data.camCoords.inverseScale) {
canvas->save();
data.camCoords.transform_sk_canvas(canvas, drawData);
canvas->translate(data.cursorPos.x(), data.cursorPos.y());
SkPaint p(SkColor4f{data.cursorColor.x(), data.cursorColor.y(), data.cursorColor.z(), 0.2f});
canvas->drawCircle(0.0f, 0.0f, 4.5f, p);
p.setStroke(true);
p.setStrokeWidth(1.0f);
p.setColor4f({data.cursorColor.x(), data.cursorColor.y(), data.cursorColor.z(), 0.7f});
canvas->drawCircle(0.0f, 0.0f, 5.0f, p);
SkFont f = main.toolbar.io->get_font(18.0f);
SkFontMetrics metrics;
f.getMetrics(&metrics);
float nextText = f.measureText(data.displayName.c_str(), data.displayName.length(), SkTextEncoding::kUTF8, nullptr);
Vector2f bounds{nextText, - metrics.fAscent + metrics.fDescent};
SkPaint p2(SkColor4f{data.cursorColor.x(), data.cursorColor.y(), data.cursorColor.z(), 0.5f});
canvas->drawSimpleText(data.displayName.c_str(), data.displayName.length(), SkTextEncoding::kUTF8, 6.0f, -metrics.fDescent, f, p2);
SkPaint p3(color_mul_alpha(main.toolbar.io->theme->backColor1, 0.5f));
canvas->drawRoundRect(SkRect::MakeXYWH(6.0f, -bounds.y(), bounds.x(), bounds.y()), 3.0f, 3.0f, p3);
canvas->restore();
if(clients) {
for(auto& clientDataPtr : clients->get_data()) {
if(clientDataPtr != ownClientData)
clientDataPtr->draw_cursor(canvas, drawData);
}
}
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
#include <Helpers/NetworkingObjects/NetObjOrderedList.hpp>
#include "Helpers/NetworkingObjects/NetObjUnorderedSet.hpp"
#include "UndoManager.hpp"
#include "BookmarkManager.hpp"
#include "ResourceManager.hpp"
@ -12,6 +13,7 @@
#include "GridManager.hpp"
#include <Helpers/NetworkingObjects/NetObjManager.hpp>
#include <filesystem>
#include "ClientData.hpp"
class MainProgram;
@ -56,8 +58,6 @@ class World {
std::deque<Toolbar::ChatMessage> chatMessages;
std::string netSource;
std::string displayName;
void start_hosting(const std::string& initNetSource, const std::string& serverLocalID);
@ -86,39 +86,37 @@ class World {
void undo_with_checks();
void redo_with_checks();
ServerPortionID ownID = 0;
std::filesystem::path filePath;
Vector3f userColor = {0.0f, 0.0f, 0.0f};
std::string name;
std::unordered_map<ServerPortionID, ClientData> clients;
NetworkingObjects::NetObjTemporaryPtr<ClientData> ownClientData;
NetworkingObjects::NetObjOwnerPtr<NetworkingObjects::NetObjUnorderedSet<ClientData>> clients;
CanvasTheme canvasTheme;
void scale_up(const WorldScalar& scaleUpAmount);
void scale_up_step();
uint64_t canvasScale = 0;
bool setToDestroy = false;
NetworkingObjects::DelayUpdateSerializedClassManager delayedUpdateObjectManager;
private:
void init_net_obj_type_list();
std::shared_ptr<NetServer> netServer;
private:
Vector3f get_random_cursor_color();
void init_client_data_list();
void init_client_data_list_callbacks();
void init_net_obj_type_list();
void init_client_callbacks();
void init_server_callbacks();
void set_name(const std::string& n);
void draw_other_player_cursors(SkCanvas* canvas, const DrawData& drawData);
void ensure_display_name_unique(std::string& displayName);
TimePoint timeToSendCameraData;
struct SentCameraData {
CoordSpaceHelper camTransform;
Vector2f windowSize;
};
std::optional<SentCameraData> sentCameraData;
WorldVec mousePreviousWorldVec = {0, 0};
WorldVec mouseWorldMove = {0, 0};
std::chrono::steady_clock::time_point lastKeepAliveSent;
};