Skip to content

Instantly share code, notes, and snippets.

@nopbxlr
Created November 5, 2025 22:43
Show Gist options
  • Select an option

  • Save nopbxlr/5cc23fb657e3df759b479ad6228f72e4 to your computer and use it in GitHub Desktop.

Select an option

Save nopbxlr/5cc23fb657e3df759b479ad6228f72e4 to your computer and use it in GitHub Desktop.
A simple, lazy EasyAuth (Fabric) to AuthMe (Bukkit) SQLite DB converter
"""
A low effort script to migrate EasyAuth (Fabric) data to AuthMe (Bukkit) format.
Useful if you want to switch server types without forcing all your users to re-register, and maintain their last known positions.
Requires nbtlib: pip install nbtlib
Feed it the server path as the first argument. It'll look for EasyAuth data there and create authme.db in the script's directory.
Check the data! This is a fragile script lacking a lot of checks. I am not responsible for any data loss. Use a staging env.
This only works if EasyAuth data was using bcrypt hashes. It may be possible with a bit more effort to support Argon2 hashes too.
Don't forget to set up AuthMe with bcrypt support.
"""
import os
import sys
import sqlite3
import json
from datetime import datetime
import glob
import nbtlib
def get_player_coords(server_path):
playerdata_dirs = []
for root, dirs, files in os.walk(server_path):
if 'playerdata' in dirs:
pd = os.path.join(root, 'playerdata')
if os.path.isdir(pd):
playerdata_dirs.append(pd)
results = {}
for pd in playerdata_dirs:
world_name = os.path.basename(os.path.dirname(pd))
for nbt_file in glob.glob(os.path.join(pd, '*.dat')):
uuid = os.path.splitext(os.path.basename(nbt_file))[0]
try:
nbt = nbtlib.load(nbt_file)
if 'Pos' in nbt:
pos = nbt['Pos']
elif 'Data' in nbt:
pos = nbt['Data'].get('Pos', [None, None, None])
else:
pos = [None, None, None]
x, y, z = pos if len(pos) == 3 else (None, None, None)
results[uuid] = {
'world': world_name,
'x': float(x) if x is not None else None,
'y': float(y) if y is not None else None,
'z': float(z) if z is not None else None
}
except Exception as e:
print(f'Error reading {nbt_file}: {e}')
return results
# --- Main migration logic ---
def migrate_easyauth_to_authme(server_path, script_path):
# Paths
easyauth_db_path = os.path.join(server_path, 'EasyAuth', 'easyauth.db')
script_dir = os.path.dirname(os.path.abspath(__file__))
authme_db_path = os.path.join(script_dir, 'authme.db')
# Check if authme.db exists and matches expected structure
create_table_sql = '''
CREATE TABLE authme (
id INTEGER,
username VARCHAR(255) NOT NULL UNIQUE,
realname VARCHAR(255) NOT NULL DEFAULT 'Player',
password VARCHAR(255) NOT NULL DEFAULT '',
ip VARCHAR(40),
lastlogin TIMESTAMP,
regip VARCHAR(40),
regdate TIMESTAMP NOT NULL DEFAULT '0',
x DOUBLE NOT NULL DEFAULT '0.0',
y DOUBLE NOT NULL DEFAULT '0.0',
z DOUBLE NOT NULL DEFAULT '0.0',
world VARCHAR(255) NOT NULL DEFAULT 'world',
yaw FLOAT,
pitch FLOAT,
email VARCHAR(255),
isLogged INT NOT NULL DEFAULT '0',
hasSession INT NOT NULL DEFAULT '0',
totp VARCHAR(32),
CONSTRAINT table_const_prim PRIMARY KEY (id)
);
'''
if os.path.exists(authme_db_path):
print(f"Warning: {authme_db_path} already exists.")
response = input("Do you want to overwrite it? (y/N): ").strip().lower()
if response == 'y':
os.remove(authme_db_path)
print(f"Deleted old {authme_db_path}.")
conn = sqlite3.connect(authme_db_path)
cur = conn.cursor()
cur.execute(create_table_sql)
conn.commit()
conn.close()
else:
print("Aborted migration. No changes made.")
return
else:
conn = sqlite3.connect(authme_db_path)
cur = conn.cursor()
cur.execute(create_table_sql)
conn.commit()
conn.close()
# Get player coordinates
coords = get_player_coords(server_path)
# Connect to easyauth.db
ea_conn = sqlite3.connect(easyauth_db_path)
ea_cur = ea_conn.cursor()
ea_cur.execute('SELECT username, username_lower, uuid, data FROM easyauth')
easyauth_rows = ea_cur.fetchall()
# Connect to authme.db
am_conn = sqlite3.connect(authme_db_path)
am_cur = am_conn.cursor()
now = int(datetime.now().timestamp())
next_id = 1
for username, username_lower, uuid, data_json in easyauth_rows:
try:
data = json.loads(data_json)
except Exception as e:
print(f"Error parsing JSON for {username_lower}: {e}")
continue
password = data.get('password', '')
if not password:
continue
last_ip = data.get('last_ip', '')
# Get coords for this uuid
coord = coords.get(uuid, {})
x = coord.get('x', 0.0)
y = coord.get('y', 0.0)
z = coord.get('z', 0.0)
world = coord.get('world', 'world')
# Manually assign id
am_cur.execute('''
INSERT INTO authme (id, username, realname, password, ip, regip, regdate, x, y, z, world, yaw, pitch)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
next_id,
username_lower,
username,
password,
last_ip,
last_ip,
now,
x,
y,
z,
world,
0.0,
0.0
))
next_id += 1
am_conn.commit()
ea_conn.close()
am_conn.close()
print(f"Migration complete. Output DB: {authme_db_path}")
if __name__ == '__main__':
if len(sys.argv) < 2:
print('Usage: python migrate_auth.py <server_path>')
sys.exit(1)
migrate_easyauth_to_authme(sys.argv[1], None)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment