You are not logged in.
Pages: 1
#1 2026-09-16 20:13:31
- Seth
- Administrator

- Registered: 2002-07-10
- Posts: 136
How I Built a 2015 Growtopia Server From Scratch
A deep dive into reverse engineering, experimentation, and building a playable server from the ground up.
The finished result: the V1.93 client running against a server built from scratch.
In early July 2024, I started an experiment to see if I could get the 2015 Growtopia client running against a server I had written entirely from scratch.
I was doing the entire project alone. There was no existing server implementation for me to simply modify, and many of the details needed to make the client work had to be discovered through reverse engineering, experimentation, debugging, and a lot of trial and error.
Why 2015 Growtopia?
There wasn't some grand plan behind it. I'm pretty sure I was cleaning my computer and saw that I had the V1.93 client installed, started looking at it out of boredom, and eventually decided that I wanted to see if I could make my own server for it.
What started as "let's see if I can make the client connect" turned into several weeks of extremely intensive development.

The Beginning
The project started on July 6th, 2024.
The initial goal was surprisingly simple: get the V1.93 client to connect to a server that I wrote myself.
It turned out to be a bigger problem than it sounds. The client already knew how everything worked, but my server didn't.
I had to figure out what the client expected, what it sent, and what information it needed before it would continue.
I couldn't just start implementing the entire game at once. The development quickly became a loop of making one small thing work, observing the result, and using that result to figure out what to do next.

First Contact
One of the first major milestones was getting the client to recognize and communicate with my server.
Before this point, most of the work was based on assumptions. Once the client was communicating with my server, every response gave me another piece of information.
The first successful client/server interaction.
From there, I could start asking much more specific questions:
What does the client send here?
What does it expect in response?
What happens if something is missing?
Which responses cause the client to continue?
Every successful experiment unlocked another part of the system. The server didn't suddenly appear fully formed; it was assembled one successful interaction at a time.

Trial and Error
A large part of the project was figuring what the client expected from the server.
There wasn't a manual sitting in front of me telling me what every value was supposed to mean. I had to work it out as I went.
My usual approach was pretty simple: make a change, run the client, and see what happened. If the client progressed further than before, I had learned something. If it behaved strangely or crashed, that was usually just as useful.
Sometimes a seemingly insignificant change would suddenly make the client move past a part that I had been stuck on for hours. Other times, I would spend a long time debugging something only to discover that I had misunderstood something much earlier in the process.
Part of the reverse-engineering process.
A lot of the screenshots I took during development aren't particularly impressive by themselves. Some show nothing more than the client getting slightly further than it did five minutes earlier.
Looking back at them as a sequence, however, they show the project slowly coming together.

Recreating items.dat
One of the early problems was the 2015 items.dat file. The client needed item information, of course, but finding an original copy from the exact version I was targeting proved difficult.
After a lot of searching, the oldest copy I could find was from 2018.
Rather than give up, I decided to reconstruct the data I needed. This meant first understanding enough of the format to work with it, then figuring out what the 2015 client expected.
Working out how the 2015 client expected its item data.
I used the 2018 data as a starting point and removed items that were released after the period I was trying to recreate.
I also wrote a basic item editor to make working with the data much easier. Once I had enough information about the differences between the versions, I wrote another tool that converted the modified data into something the 2015 client could use.
Part of the item-data tooling I wrote.
Then came the important test: would the client actually accept it?
The 2015 client successfully reading my reconstructed items.dat.
It looks small in retrospect, but at the time it felt huge. I had gone from not having any correct data at all to generating a file the client could load.

Getting Worlds to Load
After reconstructing the item data, the next step was getting to the world selection screen.
From what I remember, this was achieved fairly quickly. I only needed to get the server to acknowledge the login request and provide what the client expected next.
The world selection menu working for the first time.
The next problem was far more intimidating: actually loading a world.
I spent hours and hours reversing and testing the world data handling, trying to understand how the client represented a world and what information had to be supplied by the server.
My first attempt at entering a world. Needless to say, it didn't go particularly well.
More reversing, more debugging, and eventually:
The first successfully loaded world.
There was no player. No blocks. Nothing. Just a world that existed because the server had created it and the client had loaded it without crashing.
From there, I could finally start making the game playable.
The first gameplay.

Inventory, Worlds, and Blocks
Once the client could load a world and spawn a player, the project stopped feeling like a reverse-engineering experiment and started feeling like a real server.
I added player inventories next. Nothing was persisted yet, everything was still living in memory.
Player inventory working for the first time.
The empty world was getting boring, so I wrote a world generator next.
The first normal-looking generated world. Also the first time I had an item with TileExtra data working correctly.
Then came block punching and placing.
Block punching.
An early, slightly bugged version of block punching.
Block placing working.
With the world itself working, I could spend less time reversing and more time building the game itself: clothing, chat, trading, trees, doors, a store, and a long list of smaller features that are easy to take for granted until you have to build them yourself.

TileExtra
One of the more important pieces was getting the different kinds of TileExtra data working correctly.
A small mistake here could have a surprisingly large effect. If the client received data in a form it wasn't expecting, the rest of the packet would become misaligned, which often meant a crash or strange behavior.
TileExtra data being handled correctly.
More TileExtra blocks working correctly.
Locks came next. They were one of the first items that needed more than just sending basic tile data to the client.
The first World Lock.
One of the first lock-related failures. Apparently my implementation wasn't quite right yet.
After getting the basic lock behavior working, I implemented the access system and then worked through the other lock types.
The lock access system.
The first Big Lock.
The first Small Lock.
More world corruption while testing the lock implementation.

Persistence and Accounts
At some point I realized that I had built a server where basically nothing survived a restart.
Players could exist. Worlds could exist. Items could exist. But shut down the server and all of it disappeared.
That wasn't going to cut it, so I connected the project to a database and started moving all the data there.
One of the first screenshots after connecting the server to a database.
I started working on world saving next. It ended up being a pretty lengthy feature, mostly because it was easy to mess something up -- one wrong byte and the whole save would end up corrupted.
I spent a lot of time proofreading the code and making sure every single byte was accounted for, but still ended up missing a few.
The first successfully saved world.
The first saved dropped items.
TileExtra blocks surviving a save and reload.

Becoming Playable
By this point, the server was no longer just a collection of experiments. I started adding the features needed to make it feel like a real, playable game.
I added wearable clothes, punch effects, and playmods. The first two were fairly straightforward, while playmods took a lot longer because I had to figure out all the names and toggle messages.
One of the first versions that could be considered playable.
Seeds and trees working.
The local player's wrench dialog working.

A Bit of Security
I also started adding some basic server-side security features. One of the first was A* pathfinding.
The first version of A* pathfinding in action.
There are plenty more of course, but I won't list them all here for obvious reasons.
Once the security checks were in place, the server was finally becoming something that could stay online rather than something that only existed for testing.

Going into Beta
By late August 2024, I decided it was time to let other people try the server for themselves.
Some time before that, I asked a friend of mine to fill the role of Hamumu, and also keep an eye on the server.
The game was reasonably playable by this point, although there were still plenty of missing item behaviors and rough edges.
The beta testers managed to find bugs I had somehow never encountered despite spending an absurd number of hours playing and testing the server myself.
Most of the problems were relatively minor: strange edge cases, unexpected sequences of actions, and the occasional server crash. But having other people play completely changed how I approached development. Things that seemed perfectly fine when testing alone could immediately fall apart once several people started using the server in ways I hadn't anticipated.
Basic gameplay, however, worked.
The 2015 client was connecting to my server, loading worlds, interacting with them, saving data, and letting people play.
Unfortunately, I don't have many development screenshots from this period. By then, most of my time was spent monitoring the server, reproducing bugs, and fixing whatever players managed to break.

Filling in the Gaps
The beta wasn't the point where development stopped. If anything, it made the list of things still missing much more obvious.
Over the following months I kept filling in the game piece by piece. Some features were large projects in themselves, while others came together pretty quickly once I understood how they were supposed to work.
We also rebuilt some of the classic worlds from the period, including START, SEEDIARY, NASDAQ, TRADE, and CARNIVAL.
The server ended up with way more features than I could list individually, but here are some of the bigger ones:
Store
Doors
Trees
Trading
Carnival (games, ringmaster, etc)
Legendary quests
Locke the Salesman
Night of the Comet
All seasonal events
Item combining
Surgery
Friends list
Achievements
World rankings (with Growtoken rewards)
Special events
Here's a handful of old images and gifs from around that time:
The first version of the store working.
One of the first successful store purchases.
News banner.
Working doors.
The first version of the trade system.
Working on the first Carnival game.
Combining items.
Surgery added.
Ringmaster added.

Recreating the 2015 Events
One part of the project that ended up taking considerably more time than I originally expected was recreating the seasonal events and updates from 2015.
I couldn't simply look at the modern game and copy how an event worked. I specifically wanted to recreate the older versions, which meant researching how each event really worked at the time.
My main sources were old 2013-2015 videos, the Growtopia Wiki, screenshots, and whatever other surviving information I could find.
Most of the updates weren't necessarily hard to do. Most of my time went into researching and testing, instead of coding.
I would usually start researching and coding an event roughly one or two weeks before it was supposed to run. The goal was to have everything ready beforehand so that when the time came, I could simply enable it.
This was a completely different kind of reverse engineering. Instead of the usual workflow, I was now trying to reconstruct an entire experience from whatever evidence had survived.
What did the event actually contain in 2015?
Any features from earlier years I need to add?
Which worlds existed?
Which items and features were introduced?
Which of them were already handled by the client?
Which parts needed server-side implementation?

St. Patrick's Week
One particularly painful example was the St. Patrick's Week update.
I had to build a total of eight worlds by myself using reference images and old gameplay footage.
Most of the available videos were incredibly low quality. I ended up watching 144p videos of people simply walking around the worlds so I could figure out things like which door led where and what text was written on various signs.
It sounds ridiculous when written down, but those tiny details mattered if the goal was to recreate the experience rather than simply make a collection of vaguely similar worlds.
Rebuilding a BLARNEY world.
And rebuilding the worlds was only one part of it.
I also had to implement the event itself, test all possible edge cases, fix whatever broke, and make sure everything was ready in time.
All of this had to happen within roughly a week.
Some events also introduced new TileExtra blocks. Even though I had reversed all of them already, some of the values used were placeholders, because I hadn't yet implemented the item functionalities.
In practice, every event became its own small research and development project.
That's also one of the reasons the project remained interesting long after the initial server was playable. There was always another piece of the game to research and recreate.

A Growing Community
By November, the project had grown beyond simply recreating gameplay. People were playing, building worlds, and submitting their own work.
World of the Day
I implemented the World of the Day system around November. It gave players another reason to build and submit their worlds, while also giving the server a way to highlight some of the better creations being made by the community.
The submissions were a good reminder that the project had become more than just a technical experiment.
Moderation
As the number of players grew, it also became obvious that we couldn't be the only people watching the server.
The first moderator application was posted in late November, and on December 1st the first two moderators were hired.
Until then, nearly everything had come back to me: development, testing, bug reports, server bills, server problems, and keeping an eye on what was happening in-game.
Having other people I could trust to help with the server made it much easier to keep things running while I continued working on development.
I also want to give a special mention to some of the players who were helpful during the early-ish times (in no particular order): milosiiii, synx, AmbaRulla, KoDePvp, Makaron, car, Novya, and juti. They found bugs, tested unfinished features, broke things in ways I hadn't thought of, and generally helped turn the project into something much more stable than it would have been if I had tested everything alone.

Looking Back
When I started this project, the goal was basically just to see if I could get the 2015 client to connect to something I had written myself.
I didn't have a complete server architecture planned out. I didn't know how most of the protocol worked.
The project grew by solving whatever problem was directly in front of me.
A client crash usually meant I'd misunderstood something. All the unexpected packets, old screenshots, and low-quality videos ended up being documentation, in a way.
That approach obviously wasn't the fastest way to build a server, but for this particular project it seemed to be the only one available. Every solved problem made the next one a little easier.
There is also a lot I haven't covered here. I deliberately left out many of the lower-level implementation details, especially around specific binary formats. There is plenty more that could be said about the project, but documenting every detail would probably require an article of its own.
For me, the project was ultimately a combination of reverse engineering, preservation, and a ridiculous amount of trial and error.
What started as "let's see if I can make the client connect" eventually became a playable server with its own community. That's a much bigger result than I expected when I wrote the first line of code.
Thanks for reading.
Offline
Pages: 1








































