Canvas scale over network

This commit is contained in:
Yousef Khadadeh 2025-09-30 16:36:09 +03:00
parent 094fce776f
commit 2f758cf4e2
13 changed files with 125 additions and 36 deletions

View file

@ -5,6 +5,10 @@
#include "MainProgram.hpp"
#include <algorithm>
void Bookmark::scale_up(const WorldScalar& scaleUpAmount) {
coords.scale_about(WorldVec{0, 0}, scaleUpAmount, true);
}
BookmarkManager::BookmarkManager(World& w):
world(w)
{}
@ -63,5 +67,5 @@ const std::unordered_map<std::string, Bookmark>& BookmarkManager::bookmark_list(
void BookmarkManager::scale_up(const WorldScalar& scaleUpAmount) {
for(auto& [n, b] : bookmarks)
b.coords.scale_about(WorldVec{0, 0}, scaleUpAmount, true);
b.scale_up(scaleUpAmount);
}

View file

@ -10,6 +10,7 @@ struct Bookmark {
template <class Archive> void serialize(Archive& a) {
a(coords, windowSize);
}
void scale_up(const WorldScalar& scaleUpAmount);
};
class World;

View file

@ -3,6 +3,7 @@
#include "Helpers/FixedPoint.hpp"
#include "Helpers/MathExtras.hpp"
#include "MainProgram.hpp"
#include "Server/CommandList.hpp"
#include "World.hpp"
#include "InputManager.hpp"
#include <iostream>
@ -154,8 +155,6 @@ void DrawCamera::update_main(World& w) {
set_viewing_area(w.main.window.size.cast<float>());
if(c.inverseScale < WorldScalar(1)) {
std::cout << "yes!" << std::endl;
w.scale_up(WorldScalar(1000000));
}
if(c.inverseScale < WorldScalar(1))
w.scale_up_step();
}

View file

@ -152,6 +152,7 @@ void DrawingProgram::scale_up(const WorldScalar& scaleUpAmount) {
c->obj->scale_up(scaleUpAmount);
selection.deselect_all();
force_rebuild_cache();
switch_to_tool(drawTool->get_type() == DrawingProgramToolType::GRIDMODIFY ? DrawingProgramToolType::EDIT : drawTool->get_type(), true);
}
void DrawingProgram::check_delayed_update_timers() {

View file

@ -18,6 +18,8 @@ class ClientData {
std::string displayName;
ServerPortionID serverID = 0;
uint64_t canvasScale = 0;
std::queue<ResourceData> pendingResourceData;
std::queue<ServerClientID> pendingResourceID;

View file

@ -20,7 +20,8 @@ enum ServerCommands : MessageCommandType {
SERVER_REMOVE_BOOKMARK,
SERVER_CANVAS_COLOR,
SERVER_SET_GRID,
SERVER_REMOVE_GRID
SERVER_REMOVE_GRID,
SERVER_CANVAS_SCALE
};
enum ClientCommands : MessageCommandType {
@ -42,7 +43,8 @@ enum ClientCommands : MessageCommandType {
CLIENT_REMOVE_BOOKMARK,
CLIENT_CANVAS_COLOR,
CLIENT_SET_GRID,
CLIENT_REMOVE_GRID
CLIENT_REMOVE_GRID,
CLIENT_CANVAS_SCALE
};
// NOTE: Do not change savefile header length
@ -53,3 +55,6 @@ enum ClientCommands : MessageCommandType {
#define SAVEFILE_HEADER_V1 "INFPNT000001"
// Version header
#define VERSION_STRING "0.2.0"
// 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
#define CANVAS_SCALE_UP_STEP WorldScalar("100000000000000000000000000000000000000000000000000")

View file

@ -22,31 +22,6 @@
#include "../DrawComponents/DrawComponent.hpp"
#include "../World.hpp"
WorldVec cursorPos;
void ServerData::save(cereal::PortableBinaryOutputArchive& a) const {
a(canvasBackColor);
a((uint64_t)components.size());
for(uint64_t i = 0; i < components.size(); i++)
a(components[i]->get_type(), components[i]->id, components[i]->coords, *components[i]);
a(bookmarks, grids);
}
void ServerData::load(cereal::PortableBinaryInputArchive& a) {
a(canvasBackColor);
uint64_t compCount;
a(compCount);
for(uint64_t i = 0; i < compCount; i++) {
DrawComponentType t;
a(t);
auto newComp = DrawComponent::allocate_comp_type(t);
a(newComp->id, newComp->coords, *newComp);
components.emplace_back(newComp);
idToComponentMap.emplace(newComp->id, newComp);
}
a(bookmarks, grids);
}
MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
world(initWorld)
{
@ -55,6 +30,7 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
data.components.back()->id = c->obj->id;
data.idToComponentMap.emplace(data.components.back()->id, data.components.back());
}
data.canvasScale = world.canvasScale;
data.bookmarks = world.bMan.bookmark_list();
data.grids = world.gridMan.grids;
data.resources = world.rMan.resource_list();
@ -105,6 +81,11 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
placement = std::min<uint64_t>(placement, data.components.size());
data.components.insert(data.components.begin() + placement, newComp);
data.idToComponentMap.emplace(newComp->id, newComp);
auto& c = clients[client->customID];
if(c.canvasScale < data.canvasScale)
newComp->scale_up(FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, c.canvasScale - data.canvasScale));
newComp->server_send_place(*this, placement);
});
netServer->add_recv_callback(SERVER_PLACE_MANY_COMPONENTS, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
@ -148,6 +129,12 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
}
}
auto& c = clients[client->customID];
if(c.canvasScale < data.canvasScale) {
for(auto& o : objOrderedVector)
o->obj->scale_up(FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, c.canvasScale - data.canvasScale));
}
DrawComponent::server_send_place_many(*this, objOrderedVector);
});
netServer->add_recv_callback(SERVER_ERASE_SINGLE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
@ -177,6 +164,11 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
if(it != data.idToComponentMap.end())
it->second->coords = t.second;
}
auto& c = clients[client->customID];
if(c.canvasScale < data.canvasScale) {
for(auto& [id, coord] : transformsToSend)
coord.scale_about(WorldVec{0, 0}, FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, c.canvasScale - data.canvasScale), true);
}
DrawComponent::server_send_transform_many(*this, transformsToSend);
});
netServer->add_recv_callback(SERVER_UPDATE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
@ -226,6 +218,11 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
message(name);
Bookmark& b = data.bookmarks[name];
message(b);
auto& c = clients[client->customID];
if(c.canvasScale < data.canvasScale)
b.scale_up(FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, c.canvasScale - data.canvasScale));
netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_NEW_BOOKMARK, name, b);
});
netServer->add_recv_callback(SERVER_REMOVE_BOOKMARK, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
@ -239,6 +236,11 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
message(gID);
WorldGrid& g = data.grids[gID];
message(g);
auto& c = clients[client->customID];
if(c.canvasScale < data.canvasScale)
g.scale_up(FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, c.canvasScale - data.canvasScale));
netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_SET_GRID, gID, g);
});
netServer->add_recv_callback(SERVER_REMOVE_GRID, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
@ -251,6 +253,15 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
message(data.canvasBackColor);
netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_CANVAS_COLOR, data.canvasBackColor);
});
netServer->add_recv_callback(SERVER_CANVAS_SCALE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
auto& c = clients[client->customID];
message(c.canvasScale);
if(c.canvasScale > data.canvasScale) {
data.scale_up(FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, c.canvasScale - data.canvasScale));
data.canvasScale = c.canvasScale;
netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_CANVAS_SCALE, data.canvasScale);
}
});
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) {

34
src/Server/ServerData.cpp Normal file
View file

@ -0,0 +1,34 @@
#include "ServerData.hpp"
#include "../DrawComponents/DrawComponent.hpp"
void ServerData::save(cereal::PortableBinaryOutputArchive& a) const {
a(canvasBackColor, canvasScale);
a((uint64_t)components.size());
for(uint64_t i = 0; i < components.size(); i++)
a(components[i]->get_type(), components[i]->id, components[i]->coords, *components[i]);
a(bookmarks, grids);
}
void ServerData::load(cereal::PortableBinaryInputArchive& a) {
a(canvasBackColor, canvasScale);
uint64_t compCount;
a(compCount);
for(uint64_t i = 0; i < compCount; i++) {
DrawComponentType t;
a(t);
auto newComp = DrawComponent::allocate_comp_type(t);
a(newComp->id, newComp->coords, *newComp);
components.emplace_back(newComp);
idToComponentMap.emplace(newComp->id, newComp);
}
a(bookmarks, grids);
}
void ServerData::scale_up(const WorldScalar& scaleUpAmount) {
for(auto& [n, b] : bookmarks)
b.scale_up(scaleUpAmount);
for(auto& [n, g] : grids)
g.scale_up(scaleUpAmount);
for(auto& c : components)
c->scale_up(scaleUpAmount);
}

View file

@ -14,9 +14,10 @@ class ServerData {
std::unordered_map<ServerClientID, ResourceData> resources;
std::unordered_map<std::string, Bookmark> bookmarks;
std::unordered_map<ServerClientID, WorldGrid> grids;
uint64_t canvasScale = 0;
Vector3f canvasBackColor;
void save(cereal::PortableBinaryOutputArchive& a) const;
void write_to_file(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);
void scale_up(const WorldScalar& scaleUpAmount);
};

View file

@ -78,6 +78,7 @@ void World::init_client_callbacks() {
Vector3f newBackColor;
message(newBackColor);
set_canvas_background_color(newBackColor, false);
message(canvasScale);
drawProg.initialize_draw_data(message);
message(bMan, gridMan);
}
@ -118,6 +119,13 @@ void World::init_client_callbacks() {
message(newBackColor);
set_canvas_background_color(newBackColor, false);
});
con.client_add_recv_callback(CLIENT_CANVAS_SCALE, [&](cereal::PortableBinaryInputArchive& message) {
uint64_t newCanvasScale;
message(newCanvasScale);
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_CANVAS_SCALE, newCanvasScale);
scale_up(FixedPoint::pow_int(CANVAS_SCALE_UP_STEP, newCanvasScale - canvasScale));
canvasScale = newCanvasScale;
});
con.client_add_recv_callback(CLIENT_KEEP_ALIVE, [&](cereal::PortableBinaryInputArchive& message) {
});
}
@ -407,13 +415,23 @@ void World::draw_other_player_cursors(SkCanvas* canvas, const DrawData& drawData
}
}
void World::scale_up_step() {
canvasScale++;
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_CANVAS_SCALE, canvasScale);
scale_up(WorldScalar(CANVAS_SCALE_UP_STEP));
}
void World::scale_up(const WorldScalar& scaleUpAmount) {
Logger::get().log("USERINFO", "Canvas scaled up");
drawProg.scale_up(scaleUpAmount);
bMan.scale_up(scaleUpAmount);
gridMan.scale_up(scaleUpAmount);
drawData.cam.scale_up(*this, scaleUpAmount);
undo.clear();
// drawProg will be sending info on committed objects/modified grids. These objects will be scaled up already.
// This means that the scale up message must be send BEFORE the World::scale_up function is called on our end
// if this client is the one responsible for the scale up
drawProg.scale_up(scaleUpAmount);
undo.clear(); // Do last to make sure that any undo generated while scaling up is removed, as it contains outdated scale info
}
void World::draw(SkCanvas* canvas) {

View file

@ -100,6 +100,9 @@ class World {
} canvasTheme;
void scale_up(const WorldScalar& scaleUpAmount);
void scale_up_step();
uint64_t canvasScale = 0;
private:
ClientPortionID get_max_id(ServerPortionID serverID);