Custom Minecraft Server Scaffolding
- 16 Devlogs
- 45 Total hours
Scaffolding for hyper specific Minecraft Servers
Scaffolding for hyper specific Minecraft Servers
I have made it so the server will finally send the SetChunkCacheCenter packet whenever you move, just like the vanilla server. Previously this was only send on login, and seemingly stalled my server for no reason when I tried to send it continuously.
I found out that the issue was in my WriteVarInt function, where negative values lead to an infinite loop. Now that this is fixed I can send that packet anytime now!!
I made some more packets for moving in a vehicle, paddling a boat, and player right click interations. Just 3 more packets towards feature completeness. Once again they’re just for reading them and telling the world about them, it is up to the user to implement them (kinda)
I also made the readme (for the most part) and corrected my writeup on where i got the registries from and fixed my requirements.txt
I have rewrote all of the code that wrote and read the packet data. now reading uses a reader class that can just call readbool, readint, etc etc one after another instead of needing to chop off data after each read.
Simillarly for the writer I made it so that you can just make a writer and call .writeInt, writebool. etc etc w/o needing to do a += on a bytes object.
Now that my code is cleaned up quite a bit, I want to clean it up some more by moving all of the writing functions into their packet class so you can call something like: StatusResponse_ClientBound.write(PARAMS) to make the code much nicer. after that I will add more play packets to get more feature complete
I implemented some more play packets. While they are not 100% functional the server can recognize them and forward it to the world to be handled/implemented later when the times comes.
The client is now a subclass of the entity class, making things a bit easier.
As mentioned above, the client, when receiving a packet response from handling a packet, it could be of a server packet response type, which then the client routes it’s responses directly to the server/world for handling.
Also now on startup we print out the completeness of the server. It just measures how many of the server bound packets we have implemented.
I will work on making a client bound completeness print after finishing (mostly) the server bound ones, but I assume by time i get there many will be implemented already due to the handling of server bound packets
I want to continue implementing more play packets to become more feature complete, also for one of the packets I wrote a write method that took in parameters and returned a fully completed packet, I might need to do that w/ some other packets like the login and player joining ones to make my code smaller on the world.py end and more appropriate on the packets.py end
Chunks now automatically load when you walk by them. its based on the render distance so when i move it tries to send all the chunks in a radius around the player to them.
I also fixed a niche bug, that when there is 1 byte left in a packet after reading it could cause the packet to say it is fine with reading the rest & drop off the last byte, but now it is fixed!
I want to import the definitions for a bunch of the play packets (mostly server bound packets) and then polish up my code and do any todo items and then ship. after shipping ill make a fork and work on my lce minigame server
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.