So my main Minecraft world is currently on 1.7.10 right now. I’m a retro guy like that. I’ve set up a Tailscale network along with my friends to
- 1) avoid port forwarding, but also
- 2) essentially create a LAN Minecraft server, on and off at my will.
If you havn’t tried it yet, I highly recommend it. It just works.
However, in these old versions of Minecraft, if you decide to “Open to LAN”, it picks a new randomly chosen port number every time. This means I have to tell everyone the new one each time we want to play together.
There’s also no way of editing the description that is displayed in the Multiplayer menu when a connection is established.
The perfect solution I found is a little mod called Server.Properties for LAN by Jaideep. There were others like EasyLAN, but they did not support colourable Message Of The Day descriptions as I found out later.
The mod does exactly what it says on the tin, it creates a server.properties file for local Minecraft worlds giving server-like LAN configuration options. This allows me to set the port number to a hardcoded, short and easy-to-remember value which my dear friends only have to enter once and never again.
Now every time I click “Open to LAN” the port number will be exactly the same.
[4 ss in a html grid as of the chat showing the same port opening]
Leaving the same port open every time is like having the same password for everything, so this isn’t exactly ideal for network security. It must be stressed that if you do the same, you do so at your own risk. I accept no liability for any loss, damage, or consequences incurred directly or indirectly from following these steps.
The point is this worked. Except, when attempting to join over on a vanilla client, my friend was met with an endless “Logging in…” screen. I tried eliminating the factors by running another instance of Minecraft using his account and doing the hosting and joining from the same machine, but still no change. Then I tried changing the port number, resetting the properties file to default, disabling my DNS resolver, explicitly allowing traffic from that specific port number via Windows' firewall… you get the idea.
The real progress started when I switched to EasyLAN, assuming it was a problem with the mod itself. Strangely, I had the exact same issue.
What I found was that a Forge client was able to join just fine, but not a Vanilla one. This is because the server uses a special handshake developed by Forge in order to verify and synchronise mod lists, among other things. Now this usually wouldn’t be a problem, considering I’m only trying to load a client-side mod. However, after much headache, I found that Vanilla clients could not join Forge servers at all until Forge 1.8+. So apparently that issue I commented on Jaideep’s GitHub page was slightly pointless :/
Upgrade to v1.9.4, change the icon.png to match our discord server and boom: server-like LAN world.
With all this said and done it got me thinking, how can I make this even more perfect? What’s the coolest, most overengineered way to make this even more personal?
The cherry on top is randomised MOTDs. You see, my friends and I carry a little online tradition of quoting everything funny we ever say to each other. I’d like to create a Python script that checks when a specific port is opened, and then appends the “motd=” with a randomly selected quote from a text file. If possible, I also want to ensure duplicates don’t happen. All of this will be open source if anybody reading would like to do the same.
I’ll handle running the script with Windows shell startup, but first: a way to check the port. For this I leveraged this socket code from Chris's twin.sh article which for some reason wasn’t indented; thanks Chris.
import socket
HOST = "localhost"
PORT = 123
# Creates a new socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Try to connect to the given host and port
if sock.connect_ex((HOST, PORT)) == 0:
print("Port " + str(PORT) + " is open") # Connected successfully
else:
print("Port " + str(PORT) + " is closed") # Failed to connect because port is in use (or bad host)
# Close the connection
sock.close()
Running a quick test gives me the expected outputs.
>lan_motd.py
Port 123 is closed
>lan_motd.py
Port 123 is open
Now to make it continuously check this port until it opens. For this I can use a while loop.
import socket # for checking if the world is Open to LAN
HOST = "localhost"
PORT = 123
# Creates a new socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
# Try to connect to the given host and port
if sock.connect_ex((HOST, PORT)) == 0:
print("Port " + str(PORT) + " is open") # Connected successfully
break
else:
print("Port " + str(PORT) + " is closed...") # Failed to connect because port is in use (or bad host)
# Close the connection
sock.close()
Now if I try to run the script and then Open to LAN I get:
>lan_motd.py
Port 123 is closed...
Port 123 is closed...
Port 123 is closed...
Port 123 is open
>
Perfect.
We don’t just want to break though, we want to randomly select a new MOTD. Time for some file I/O.
import socket # for checking if the world is Open to LAN
HOST = "localhost"
PORT = 123
MOTD = "descriptions.txt" # file to be randomly picked from
# Creates a new socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
# Try to connect to the given host and port
if sock.connect_ex((HOST, PORT)) == 0:
print("Port " + str(PORT) + " is open") # Connected successfully
with open(MOTD,"r") as file:
line = file.readline()
print(line) # Prints the first line of the file
break
else:
print("Port " + str(PORT) + " is closed...") # Failed to connect because port is in use (or bad host)
# Close the connection
sock.close()
To create the descriptions I used this "MOTD creator".
Let’s test before implementing the randomisation. This should output the first line in the file:
UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 1896: character maps to <undefined>
The problem here is that Python is struggling to decode the file due to the Minecraft colour codes I used like \u00A73Welcome to \u00A7bsomething \u00A73\u00A7o(im not sure).
To solve this, all I need to do is specify how the file is encoded:
#...
while True:
# Try to connect to the given host and port
if sock.connect_ex((HOST, PORT)) == 0:
print("Port " + str(PORT) + " is open") # Connected successfully
with open(MOTD,"r", encoding="utf-8") as file:
#...
Now we get:
>lan_motd.py
Port 123 is closed...
Port 123 is closed...
Port 123 is closed...
Port 123 is closed...
Port 123 is closed...
Port 123 is open
\u00A73Welcome to \u00A7bsomething \u00A73\u00A7o(im not sure)
>
Great. Next is to randomise it. Instead of outputting the first line, I want to pick one at random. To do this I’ll first count how many lines there are, then generate a number within that range.
The counting can just be done with a simple for loop.
for line in file.readlines():
total_lines+=1
To pick a random line, I can use the total number of lines calculated and generate any number in between. This can be done using the built-in python module random.
random_line = random.randint(1, total_lines)
To output this is also straightforward. Since file.readlines() returns an array of strings, the freshly generated number can be passed to select and output the chosen MOTD.
with open(MOTD,"r", encoding="utf-8") as file: # read file and close when done using "with"
line = file.readlines()
chosen_motd = line[random_line]
print(chosen_motd)
To edit the server.properties file, I will first load all the lines into memory using read mode in Python's file I/O class. Then I can loop through each line using a for loop, writing the randomly selected string if the current line is an MOTD.
with open("server.properties","r", encoding="utf-8") as motd: # open server properties file in read mode
lines = motd.readlines() # load all lines into memory
with open("server.properties","w", encoding="utf-8") as motd:
for line in lines:# loop through each line
if line.startswith("motd="): # if the current line is motd then
motd.write("motd="+chosen_motd) # write a new motd
else: # if the line is not motd then
motd.write(line) # write the line the same as it was before when it was loaded into memory
That’s working nicely. Let’s put it all together now.
import socket # for checking if the world is Open to LAN
import random # for randomly selecting an motd
HOST = "localhost"
PORT = 123
MOTD = "descriptions.txt" # file to be randomly picked from
total_lines = 0 # for counting number of lines within MOTD file
current_line = 0 # track current line when reading MOTD file
modt_line = 0 # track current line when reading server properties file
#calculate number of lines in file
with open(MOTD,"r", encoding="utf-8") as file:
for line in file.readlines():
total_lines+=1
print("Total lines in target file:",total_lines)
random_line = random.randint(0, total_lines)
# Creates a new socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while True:
# Try to connect to the given host and port
if sock.connect_ex((HOST, PORT)) == 0: # if connection successful
print("Port " + str(PORT) + " is open") # Connected successfully
with open(MOTD,"r", encoding="utf-8") as file: # read file and close when done using "with"
line = file.readlines()
chosen_motd = line[random_line]
print("Picked line:",random_line,"which is",chosen_motd)
with open("server.properties","r", encoding="utf-8") as motd: # open server properties file in read mode
lines = motd.readlines() # load all lines into memory
with open("server.properties","w", encoding="utf-8") as motd:
for line in lines:# loop through each line
if line.startswith("motd="): # if the current line is motd then
motd.write("motd="+chosen_motd) # write a new motd
else: # if the line is not motd then
motd.write(line) # write the line the same as it was before when it was loaded into memory
break
else:
print("Port " + str(PORT) + " is closed...") # Failed to connect because port is in use (or bad host)
# Close the connection
sock.close()
>lan_motd.py
Total lines in target file: 71
Port 123 is closed...
Port 123 is closed...
Port 123 is closed...
Port 123 is closed...
Port 123 is open
Picked line: 56
Because it waits for the port to open before writing to the file, the changes won’t be noticeable until the next time you Open to LAN.
To prevent two of the same quotes being picked out back to back, I just kept a log of what the previous one was, and if they’re the same as the new pick, we re-roll.
#create descriptions.log with the previous entry to ensure no dupes
with open(MOTD+".log","r", encoding="utf-8") as log:
if log.read() == chosen_motd:
chooseRand()
with open(MOTD+".log","w", encoding="utf-8") as log:
log.write(chosen_motd)
To automate the script I put a shortcut to it in my startup folder. You can get there yourself by pressing Win + R and typing in shell:startup into the dialogue box. If you were bothered, you could probably set it up as a Windows Service to make it more discrete, but I’m fine with this setup.
All these files are kept in the same folder as my world folder, where the server.properties lies. The final script can be found [here on my CodeBerg] (link) along with a binary for the off-chance you actually want to use it daily.
There, just like the splash texts.