Help making a drawing game

I want to make a game in which players can join and place 1 pixel every minute on a 1600x900 canvas.
This idea comes from r/place, so if you’re interested in what I’m talking about you can view more examples there.

Here’s the problem:
I’m not sure how to make it sync across all servers. I’m also not sure how to efficiently load data when a new server is created.

If you know how to achieve something like this inside of Roblox, please let me know!
Thanks. :smile:

1 Like

This is a very interesting idea. I have never done this before, but apparently there is a thing called messaging service MessagingService | Documentation - Roblox Creator Hub that seems to do what you want.
You would want to store a master version of the canvas somewhere that every server can access it, probably in a datastore.

The main issue I see is what happens if two people change the same pixel on different servers. It should work fine with one person being overwritten, though I have not tried.

I also found this post also talking about saving data between servers.

Happy coding!

1 Like

OP should probably make it yield and await a response from MessagingService:SubscribeAsync to see if that pixel has already been modified. And it should keep checking (in the background) until the pixel has been modified and the change has been sent.

-- example ig

local topicName = "CheckForOverwrite"
local Connection
local function writePixel(pixel: Vector2, writer: Player)
     --[[ obviously you can change
     the type annotations to UDim2
     or smthn else
     ]]
     Connection = MS:SubscribeAsync(topicName, function()
         return Connection:Disconnect()
     end)
     -- ur code
     MS:PublishAsync(topicName, pixel)
     --[[ or you can have
     sender data like:
     ]]
     MS:PublishAsync(topicName, {
          ["Pixel"] = pixel,
          ["Writer"] = Player.Name
     })
end

And then you could interpret some data like this:

MS:SubscribeAsync(topicName, function()
     -- add data
end))

Here’s the trouble:
How do you load the canvas when a new server is created?

1600x900 canvas is ~22 megabytes. That’s a lot of data to store and load.
Any ideas?

@Doomcolp @xtremsk8rs2

You can try to optimize the data like combining the UDim2 values into a string like this:

local function convertPixelPosition(value: UDim2)
     return `{value.Scale.X}..
     {value.Scale.Y}..{value.Offset.X}..
     {value.Offset.Y}`
end

-- before
local pixelData = UDim2.new(1, 5, 10, 9)

-- after
local pixelData = convertPixelPosition(pixelData)
-- "1_5_10_9"

Or you could send it to an external database (obv you’d have to pay though)

Then when you’re decrypting it, jsut use gsub and separate by the underscores or whatever symbol you wanna use and then tonumber it

Well actually I’m not sure if this actually optimizes the data lol but it seems better for performance than straight up saving multiple values

Still gonna be a huge payload like you said tho

Well, on second thought it would be a lot less than 22 megabytes.

My idea was that each tile has an ID. When a pixel is placed, it simply adds that ID to a datastore.
This would mean that if every pixel was filled in, there would be 1,440,000 ID’s. (1600 * 900)

However, this doesn’t account for also storing colors.

Let me recalculate:
(Assuming all pixels are filled in)

For optimization purposes lets say we have 10 colors – each with an ID associated with it.
0: White
1: Black
2: Blue
3: Red
4: Green
5: Orange
6: Cyan
7: Brown
8: Lime
9: Grey

(This is so that we only have to store one character for the color)
Next, there are 1,440,000 pixels, each with a number ID.

However, we are actually storing significantly more than 1,440,000 characters for the ID’s.
We have to store each character of each number. For example, ID #77300 has 5 characters that it has to store.

If you do the math, we are storing 5,869,896 total characters for IDs.
(Number_of_characters_for_IDs + Number_of_ID’s for colors) = 7,309,896 characters

After further research, this should equal ~7 MB.
In theory, you could save the data to 2 separate datastores.

Loading 2 datastores is significantly more manageable than loading 6.

So,
Here’s my idea based on all the knowledge I’ve gained from this post:

  • We can use Messaging Service to communicate between servers when a pixel is placed in real-time.
  • Then, every time a server closes or every 5 minutes we save & sync the data across all servers (this does not run in to datastore limitations because the limits are based on the number of servers the experience has.)
  • The datastore is divided down the middle of the map. The left side is datastore #1 and the right side is datastore #2. Each side of the map luckily will only be using a maximum of 3.5 MB of data. (The datastore limit is 4 MB per datastore)
  • During a save, players will be restricted from placing any new pixels to prevent potential loss of data.

I believe this will work. Now I just have to find the time to work on the project.
Thank you for your help!

1 Like

I have found an easy way to convert tables to strings is by using http service JSONEncode. JSONDecode converts the tables back into strings. Though JSONDecode and JSONEncode do take a while on larger tables.

This may be an alternative to storing it in two separate datastores. It has worked very well for me and my massive tables.