Canvas theme moved to net objects

This commit is contained in:
Yousef Khadadeh 2025-12-16 23:06:46 +03:00
parent 86858d238e
commit 0b67c9cc46
18 changed files with 164 additions and 112 deletions

View file

@ -38,6 +38,7 @@ set(sources "src/main.cpp"
"src/ConnectionManager.cpp"
"src/InputManager.cpp"
"src/UndoManager.cpp"
"src/CanvasTheme.cpp"
"src/DrawCamera.cpp"
"src/DrawCollision.cpp"
"src/CoordSpaceHelper.cpp"

65
src/CanvasTheme.cpp Normal file
View file

@ -0,0 +1,65 @@
#include "CanvasTheme.hpp"
#include "DrawingProgram/DrawingProgram.hpp"
#include <Helpers/HsvRgb.hpp>
#include "MainProgram.hpp"
#include "Helpers/Networking/NetLibrary.hpp"
#include "World.hpp"
using namespace NetworkingObjects;
CanvasTheme::CanvasTheme(World& w):
world(w)
{}
void CanvasTheme::init() {
if(world.netObjMan.is_server()) {
backColor = world.netObjMan.make_obj_direct<BackColor>(world.main.defaultCanvasBackgroundColor);
set_tool_front_color(world.drawProg);
}
}
SkColor4f CanvasTheme::get_back_color() const {
if(!backColor)
return SkColor4f{DEFAULT_CANVAS_BACKGROUND_COLOR.x(), DEFAULT_CANVAS_BACKGROUND_COLOR.y(), DEFAULT_CANVAS_BACKGROUND_COLOR.z(), 1.0f};
else
return SkColor4f{backColor->c.x(), backColor->c.y(), backColor->c.z(), 1.0f};
}
const SkColor4f& CanvasTheme::get_tool_front_color() const {
return toolFrontColor;
}
void CanvasTheme::set_back_color(const Vector3f& newBackColor) {
if(backColor && newBackColor != backColor->c) {
backColor->c = newBackColor;
world.delayedUpdateObjectManager.send_update_to_all<BackColor>(backColor, true);
set_tool_front_color(world.drawProg);
}
}
void CanvasTheme::set_tool_front_color(DrawingProgram& drawP) {
Vector3f newHSV = rgb_to_hsv<Vector3f>(backColor->c);
SkColor4f oldToolFrontColor = toolFrontColor;
if(newHSV.z() >= 0.6f)
toolFrontColor = SkColor4f{0.0f, 0.0f, 0.0f, 1.0f};
else
toolFrontColor = SkColor4f{1.0f, 1.0f, 1.0f, 1.0f};
if(oldToolFrontColor != toolFrontColor)
drawP.clear_draw_cache();
}
void CanvasTheme::register_class() {
world.delayedUpdateObjectManager.register_class<BackColor>(world.netObjMan, NetworkingObjects::DelayUpdateSerializedClassManager::CustomConstructors<BackColor>{
.postUpdateFunc = [&](BackColor& o) {
set_tool_front_color(world.drawProg);
}
});
}
void CanvasTheme::read_create_message(cereal::PortableBinaryInputArchive& a) {
backColor = world.netObjMan.read_create_message<BackColor>(a, nullptr);
}
void CanvasTheme::write_create_message(cereal::PortableBinaryOutputArchive& a) const {
backColor.write_create_message(a);
}

32
src/CanvasTheme.hpp Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include "cereal/archives/portable_binary.hpp"
#include <include/core/SkColor.h>
#include <Helpers/ConvertVec.hpp>
#include <Helpers/NetworkingObjects/NetObjOwnerPtr.hpp>
#include <Helpers/Serializers.hpp>
class DrawingProgram;
class World;
class CanvasTheme {
public:
CanvasTheme(World& w);
void init();
SkColor4f get_back_color() const;
const SkColor4f& get_tool_front_color() const;
void set_back_color(const Vector3f& newBackColor);
void register_class();
void read_create_message(cereal::PortableBinaryInputArchive& a);
void write_create_message(cereal::PortableBinaryOutputArchive& a) const;
private:
void set_tool_front_color(DrawingProgram& drawP);
struct BackColor {
Vector3f c;
template <typename Archive> void serialize(Archive& a) {
a(c);
}
};
NetworkingObjects::NetObjOwnerPtr<BackColor> backColor;
SkColor4f toolFrontColor = {1.0f, 1.0f, 1.0f, 1.0f};
World& world;
};

View file

@ -183,7 +183,7 @@ bool BrushTool::prevent_undo_or_redo() {
void BrushTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(!drawData.main->toolbar.io->hoverObstructed) {
SkPaint linePaint;
linePaint.setColor4f(drawP.world.canvasTheme.toolFrontColor);
linePaint.setColor4f(drawP.world.canvasTheme.get_tool_front_color());
linePaint.setStroke(true);
linePaint.setStrokeWidth(0.0f);
float width;

View file

@ -385,7 +385,7 @@ SkPaint DrawingProgram::select_tool_line_paint() {
SkPaint selectLinePaint;
selectLinePaint.setStyle(SkPaint::kStroke_Style);
selectLinePaint.setStrokeWidth(3);
selectLinePaint.setColor4f(world.canvasTheme.toolFrontColor);
selectLinePaint.setColor4f(world.canvasTheme.get_tool_front_color());
selectLinePaint.setPathEffect(lassoLineDashEffect);
return selectLinePaint;

View file

@ -84,7 +84,7 @@ bool EraserTool::prevent_undo_or_redo() {
void EraserTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(!drawData.main->toolbar.io->hoverObstructed) {
SkColor4f c = drawP.world.canvasTheme.toolFrontColor;
SkColor4f c = drawP.world.canvasTheme.get_tool_front_color();
c.fA = 0.5f;
SkPaint linePaint;

View file

@ -163,7 +163,7 @@ void GridModifyTool::draw(SkCanvas* canvas, const DrawData& drawData) {
Vector2f bMin = drawP.world.drawData.cam.c.to_space(b.min);
Vector2f bMax = drawP.world.drawData.cam.c.to_space(b.max);
SkPaint rectBoundsPaint;
rectBoundsPaint.setColor4f(drawP.world.canvasTheme.toolFrontColor);
rectBoundsPaint.setColor4f(drawP.world.canvasTheme.get_tool_front_color());
rectBoundsPaint.setStroke(true);
rectBoundsPaint.setStrokeWidth(0.0f);
canvas->drawRect(SkRect::MakeLTRB(bMin.x(), bMin.y(), bMax.x(), bMax.y()), rectBoundsPaint);

View file

@ -308,15 +308,15 @@ class GUIManager {
bool clicked;
CLAY_AUTO_ID({.layout = {.sizing = {.width = CLAY_SIZING_FIXED(BIG_BUTTON_SIZE), .height = CLAY_SIZING_FIXED(BIG_BUTTON_SIZE) } } }) {
clicked = color_button("0", val, isOpen, elemUpdate);
}
if(clicked)
isOpen = true;
if(isOpen) {
top_to_bottom_window_popup_layout(CLAY_SIZING_FIT(300), CLAY_SIZING_FIT(0), [&]() {
isUpdating = color_picker_items("c", val, selectAlpha);
if(io->mouse.leftClick && !Clay_Hovered() && !clicked)
isOpen = false;
});
if(clicked)
isOpen = true;
if(isOpen) {
top_to_bottom_window_popup_layout(CLAY_SIZING_FIT(300), CLAY_SIZING_FIT(0), [&]() {
isUpdating = color_picker_items("c", val, selectAlpha);
if(io->mouse.leftClick && !Clay_Hovered() && !clicked)
isOpen = false;
});
}
}
pop_id();
return isUpdating;

View file

@ -18,7 +18,7 @@ void GridManager::add_default_grid(const std::string& newName) {
if(grids) {
WorldGrid g;
g.name = newName;
g.color = color_mul_alpha(convert_vec4<Vector4f>(world.canvasTheme.toolFrontColor), 0.4f);
g.color = color_mul_alpha(convert_vec4<Vector4f>(world.canvasTheme.get_tool_front_color()), 0.4f);
g.size = world.drawData.cam.c.inverseScale * WorldScalar(WorldGrid::GRID_UNIT_PIXEL_SIZE);
g.offset = world.drawData.cam.c.pos + world.drawData.cam.c.dir_from_space(world.main.window.size.cast<float>() * 0.5f);
grids->emplace_back_direct(grids, g);

View file

@ -282,7 +282,7 @@ float MainProgram::get_scale_and_density_factor_gui() {
}
void MainProgram::draw(SkCanvas* canvas) {
canvas->clear(transparentBackground ? SkColor4f{0.0f, 0.0f, 0.0f, 0.0f} : world->canvasTheme.backColor);
canvas->clear(transparentBackground ? SkColor4f{0.0f, 0.0f, 0.0f, 0.0f} : world->canvasTheme.get_back_color());
world->draw(canvas);
toolbar.draw(canvas);
}

View file

@ -32,7 +32,7 @@ void FileResourceDisplay::draw(SkCanvas* canvas, const DrawData& drawData, const
svgDom = findSVGData->second;
SkPaint blendingPaint;
blendingPaint.setColorFilter(SkColorFilters::Blend(drawData.main->world->canvasTheme.toolFrontColor, SkColorSpace::MakeSRGB(), SkBlendMode::kSrcIn));
blendingPaint.setColorFilter(SkColorFilters::Blend(drawData.main->world->canvasTheme.get_tool_front_color(), SkColorSpace::MakeSRGB(), SkBlendMode::kSrcIn));
canvas->saveLayer(imRect, &blendingPaint);
canvas->translate(imRect.x(), imRect.y());
canvas->scale(imRect.width() / svgDom->containerSize().width(), imRect.height() / svgDom->containerSize().height());

View file

@ -9,14 +9,9 @@ enum ServerCommands : MessageCommandType {
SERVER_UPDATE_NETWORK_OBJECT,
SERVER_MOVE_CAMERA,
SERVER_MOVE_SCREEN_MOUSE,
SERVER_PLACE_SINGLE_COMPONENT,
SERVER_PLACE_MANY_COMPONENTS,
SERVER_TRANSFORM_MANY_COMPONENTS,
SERVER_NEW_RESOURCE_ID,
SERVER_NEW_RESOURCE_DATA,
SERVER_CHAT_MESSAGE,
SERVER_CANVAS_COLOR,
SERVER_CANVAS_SCALE
SERVER_CHAT_MESSAGE
};
enum ClientCommands : MessageCommandType {
@ -26,14 +21,11 @@ enum ClientCommands : MessageCommandType {
CLIENT_UPDATE_NETWORK_OBJECT,
CLIENT_USER_CONNECT,
CLIENT_USER_DISCONNECT,
CLIENT_TRANSFORM_MANY_COMPONENTS,
CLIENT_MOVE_CAMERA,
CLIENT_MOVE_SCREEN_MOUSE,
CLIENT_NEW_RESOURCE_ID,
CLIENT_NEW_RESOURCE_DATA,
CLIENT_CHAT_MESSAGE,
CLIENT_CANVAS_COLOR,
CLIENT_CANVAS_SCALE
CLIENT_CHAT_MESSAGE
};
// 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

@ -22,7 +22,6 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
world(initWorld)
{
data.canvasScale = world.canvasScale;
data.canvasBackColor = convert_vec3<Vector3f>(world.canvasTheme.backColor);
lastKeepAliveSent = std::chrono::steady_clock::now();
@ -66,6 +65,7 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
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()) {
@ -88,24 +88,6 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
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_CHAT_MESSAGE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
std::string chatMessage;
message(chatMessage);
netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_CHAT_MESSAGE, client->customID, chatMessage);
});
netServer->add_recv_callback(SERVER_CANVAS_COLOR, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
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) {

View file

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

View file

@ -9,7 +9,6 @@ class DrawComponent;
class ServerData {
public:
uint64_t canvasScale = 0;
Vector3f canvasBackColor;
void save(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);

View file

@ -1691,9 +1691,9 @@ void Toolbar::options_menu() {
}) {
gui.push_id("canvas settings menu");
gui.obstructing_window();
SkColor4f newColorToSet = main.world->canvasTheme.backColor;
SkColor4f newColorToSet = main.world->canvasTheme.get_back_color();
gui.color_picker_button_field("canvasColor", "Canvas Color", &newColorToSet, false);
main.world->set_canvas_background_color(convert_vec3<Vector3f>(newColorToSet));
main.world->canvasTheme.set_back_color(convert_vec3<Vector3f>(newColorToSet));
if(gui.text_button_wide("done", "Done"))
optionsMenuOpen = false;
gui.pop_id();

View file

@ -38,14 +38,15 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
rMan(*this),
drawProg(*this),
bMan(*this),
gridMan(*this)
gridMan(*this),
canvasTheme(*this)
{
init_net_obj_type_list();
gridMan.init();
bMan.init();
drawProg.init();
canvasTheme.init();
set_canvas_background_color(main.defaultCanvasBackgroundColor);
displayName = main.displayName;
netSource = worldInfo.netSource;
@ -88,6 +89,16 @@ void World::init_net_obj_type_list() {
CanvasComponentAllocator::register_class(*this, netObjMan);
CanvasComponentContainer::register_class(netObjMan);
NetworkingObjects::register_ordered_list_class<CanvasComponentContainer>(netObjMan);
canvasTheme.register_class();
}
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);
});
}
void World::init_client_callbacks() {
@ -105,13 +116,11 @@ void World::init_client_callbacks() {
message(clients);
set_name(fileDisplayName);
drawData.cam.smooth_move_to(*main.world, camCoordsToMoveTo, windowSizeToMoveTo, true);
Vector3f newBackColor;
message(newBackColor);
set_canvas_background_color(newBackColor, false);
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);
}
clientStillConnecting = false;
});
@ -150,25 +159,13 @@ void World::init_client_callbacks() {
message(c.cursorPos);
});
con.client_add_recv_callback(CLIENT_CHAT_MESSAGE, [&](cereal::PortableBinaryInputArchive& message) {
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);
});
con.client_add_recv_callback(CLIENT_CANVAS_COLOR, [&](cereal::PortableBinaryInputArchive& message) {
Vector3f newBackColor;
message(newBackColor);
set_canvas_background_color(newBackColor, false);
});
con.client_add_recv_callback(CLIENT_CANVAS_SCALE, [&](cereal::PortableBinaryInputArchive& message) {
uint64_t newCanvasScale;
message(newCanvasScale);
if(newCanvasScale > canvasScale) {
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;
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);
}
});
con.client_add_recv_callback(CLIENT_KEEP_ALIVE, [&](cereal::PortableBinaryInputArchive& message) {
@ -242,25 +239,12 @@ void World::unfocus_update() {
}
}
void World::set_canvas_background_color(const Vector3f& newBackColor, bool sendChangeOverNetwork) {
if(newBackColor != convert_vec3<Vector3f>(canvasTheme.backColor)) {
canvasTheme.backColor = SkColor4f{newBackColor.x(), newBackColor.y(), newBackColor.z(), 1.0f};
Vector3f newHSV = rgb_to_hsv<Vector3f>(newBackColor);
SkColor4f oldToolFrontColor = canvasTheme.toolFrontColor;
if(newHSV.z() >= 0.6f)
canvasTheme.toolFrontColor = SkColor4f{0.0f, 0.0f, 0.0f, 1.0f};
else
canvasTheme.toolFrontColor = SkColor4f{1.0f, 1.0f, 1.0f, 1.0f};
if(oldToolFrontColor != canvasTheme.toolFrontColor)
drawProg.clear_draw_cache();
if(sendChangeOverNetwork)
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_CANVAS_COLOR, newBackColor);
}
}
void World::send_chat_message(const std::string& message) {
if(!clientStillConnecting) {
con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_CHAT_MESSAGE, message);
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);
}
}
@ -390,7 +374,7 @@ void World::load_from_file(const std::filesystem::path& filePathToLoadFrom, std:
void World::save_file(cereal::PortableBinaryOutputArchive& a) const {
a(drawData.cam.c, main.window.size.cast<float>().eval());
a(convert_vec3<Vector3f>(canvasTheme.backColor));
//a(convert_vec3<Vector3f>(canvasTheme.backColor));
drawProg.save_file(a);
}
@ -399,17 +383,17 @@ void World::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber versi
Vector2f windowSizeToJumpTo;
a(coordsToJumpTo, windowSizeToJumpTo);
if(version < VersionNumber(0, 1, 0))
set_canvas_background_color(DEFAULT_CANVAS_BACKGROUND_COLOR);
else {
Vector3f canvasBackColor;
a(canvasBackColor);
const Vector3f OLD_DEFAULT_CANVAS_BACKGROUND_COLOR{0.12f, 0.12f, 0.12f};
if(version < VersionNumber(0, 3, 0) && canvasBackColor == OLD_DEFAULT_CANVAS_BACKGROUND_COLOR)
set_canvas_background_color(DEFAULT_CANVAS_BACKGROUND_COLOR);
else
set_canvas_background_color(canvasBackColor);
}
//if(version < VersionNumber(0, 1, 0))
// set_canvas_background_color(DEFAULT_CANVAS_BACKGROUND_COLOR);
//else {
// Vector3f canvasBackColor;
// a(canvasBackColor);
// const Vector3f OLD_DEFAULT_CANVAS_BACKGROUND_COLOR{0.12f, 0.12f, 0.12f};
// if(version < VersionNumber(0, 3, 0) && canvasBackColor == OLD_DEFAULT_CANVAS_BACKGROUND_COLOR)
// set_canvas_background_color(DEFAULT_CANVAS_BACKGROUND_COLOR);
// else
// set_canvas_background_color(canvasBackColor);
//}
drawData.cam.smooth_move_to(*this, coordsToJumpTo, windowSizeToJumpTo, true);
drawProg.load_file(a, version);
@ -458,9 +442,9 @@ 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));
//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) {

View file

@ -8,6 +8,7 @@
#include "DrawingProgram/DrawingProgram.hpp"
#include "Toolbar.hpp"
#include "SharedTypes.hpp"
#include "CanvasTheme.hpp"
#include "GridManager.hpp"
#include <Helpers/NetworkingObjects/NetObjManager.hpp>
#include <filesystem>
@ -92,12 +93,7 @@ class World {
std::string name;
std::unordered_map<ServerPortionID, ClientData> clients;
void set_canvas_background_color(const Vector3f& newBackColor, bool sendChangeOverNetwork = true);
struct CanvasTheme {
SkColor4f backColor = {0.0f, 0.0f, 0.0f, 1.0f};
SkColor4f toolFrontColor = {1.0f, 1.0f, 1.0f, 1.0f};
} canvasTheme;
CanvasTheme canvasTheme;
void scale_up(const WorldScalar& scaleUpAmount);
void scale_up_step();
@ -111,6 +107,7 @@ class World {
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);