Custom Minecraft Server Scaffolding
- 11 Devlogs
- 32 Total hours
Scaffolding for hyper specific Minecraft Servers
Scaffolding for hyper specific Minecraft Servers
I moved the tag generation into a class inside of Registry.py and it caches. So after everything generates for the first time loading into the world takes less than 5 seconds!!
I’m very proud of how this has turned out so far. I gonna work on some play packets, write a readme, then ship
SyncedRegistry instead of clientObj.registries
So I’ve make a SyncedRegistry class and had it do all the wonderful things client.generateAndSendRegistryData would create for them, I then removed the client.registries and make all references to it use Registry.getSyncedRegistry() and if they needed to get the all the entries and not just one specific one, they can call .getEntries() on that synced register
This change will improve load times & syncability between players. All players will have the same registry data so I can save the chunk packet bytes for that. (it also means i can generate chunk packet bytes w/o needing a reference player and STILL get the biome data correct)
The world on init preloads all the required registries needed and that improves join time.
I need to do the same for the tags data because that is the one that takes up the most time, and caching that would be so great.
Also i want to resolve more PLAY packets so my server can be more feature complete, but rn it looks very good and I might ship it and use it for my minigame server soon
After a chunk is requested into a packet i cache it for later. This system is prone to breaking if the synced registries for each player aren’t the same but im hoping to make sure they are the same. (I also want to precompute these and or cache them)
I experimented with a bunch of precomputing and caching and seemingly for now just saving the packet data after computing it once on load seems to work just fine for me right now
I have managed to decode a bunch of chunks!(pictued is a 3x3 area rendered because of a 5x5 area thats loaded)
I still have set the blocklight and skylight to all 15s and i will probably need to go and fix that in a bit later, but for now it just copies over the palette from the global block state palette, copies over the longs for the biome data + palette.
For the most part it just copies over the nbt data into bytes and packages it up. It also makes up data, like it also says theres 16^3 (the maximum) blocks in each subchunk. This is to prevent it from unrendering when breaking stuff
I want to load a bunch more chunks to make it more seamless
I want to properly give the lighting data, but ill do that later as it works fine for right this second
I want to make sending synchronized registries faster and possibly cache the packets that send them
Okay so i figured out what the heck bit sets were (they’re arrays of bits, or i saved them as an array of bools) and so I was able to make the lighting data needed to render stuff, and that also fixed my player not rendering!
When loading just 1 chunk it doesn’t render, it needs to be surrounded by other chunks so here is me loading a 3x3 chunk area, and as you can see the middle one has rendered.
The chunks right now are all stone and FULL 15 brightness on blocks & skylight everywhere. I soon will import the actual light data and the actual block data, i just need to figure out where the global block state palette is and not the global block palette!
I made the server send a bunch of packets for extra info about the world before loading in. I also make a region file handler to get chunk & subchunk data in nbt format.
Currently idk how to write the level_chunk_with_light packet and it always says 14KB or 22KB of extra data, i need to look into it
Also for some reason the player’s game crashes when they move (but not look around) due to ticking entity or something. I will also need to investigate this
I’mma take a break from this as school has started recently, ive been spending so much time on this, and the chunk roadblock makes me very demotivated
I have now made a main.py that spawns server.py and has it run all the same code as before, connecting clients, and IOing the packets in and out.
But now there is World.py which is a class that runs the world, accept player connections, and will do all of the packet creation and queuing after the client fully connects to the world
I added a bunch more optional packets for extra info, and more will be on the way
After the server sends the login packet it sends a whole bunch of packets to the client to finalize some things to make sure everything is good. It adds player datas, teleport the player somewhere, preps the client for chunk loading, sets the time, etc
After all that finishes then the client will say that it has loaded and then we can exchange packets of interacting with the environment, and loading new chunks, etc
I have finished the connecting/configuring part of the connection. Now when a client tries to connect it goes all the way through the configuring state, which includes the registry & tagged registry packets (which there are a lot of) and then the client accepts all of them then transfers into the playing state!
Now all is left to do is make the actual playing server instead of connection hell
I HATE YOU MOJANG, TAG PACKETS ARE THE BANE OF MY EXISTANCE
For those of you lucky enough to not have needed to make a minecraft server from scratch, this is evil and fucked up.
You have to send a bunch of registries and the tags that they reference. And they can reference other tags, and it is very confusing to make. Also there are built in registries that aren’t exposed to you that you sometimes have to use. So I had to extract those from server.jar
Anyways im till working though that stuff so it isn’t done yet…
I got a TCP Socket server running and receiving bytes from a minecraft client.
I also created a bunch of packets that are a subclass of the Packet class. This allows me to create any packet type I want w/ client or serverbound properties and what id they have. It’ll make it easier to change the IDs when they eventually change in future updates.
The server works by spawning a clientHandler thread when a client connects to the tcp server
It keeps reading bytes until the client ends the connection
Everytime we receive bytes from the client we decode as much as we can, and then we handle each of them in a FIFO style.